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

Last change on this file was a051af0, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

updated to 1.06_rob20201204

  • Property mode set to 100644
File size: 936.8 KB
Line 
1diff -ruN ../netqmail-1.06-original/CHANNELS netqmail-1.06/CHANNELS
2--- ../netqmail-1.06-original/CHANNELS  1970-01-01 01:00:00.000000000 +0100
3+++ netqmail-1.06/CHANNELS      2019-06-26 16:39:31.572826981 +0200
4@@ -0,0 +1,100 @@
5+CHANNELS by Reed Sandberg
6+Copyright (c) 2007-2008 The SMB Exchange, INC
7+
8+This patch is free software; you can redistribute it and/or modify
9+it under the Artistic License.
10+
11+This patch for (net)qmail comes with NO WARRANTY.
12+
13+RELEASE: November 15, 2008
14+
15+
16+qmail manages two different queues
17+with different configurable concurrency settings (rates) based on a set
18+of domains - those delivered locally (control files: locals,
19+virtualdomains, concurrencylocal) and those delivered remotely (domains
20+not listed in  above control files and concurrencyremote). Luckily,
21+qmail's author (DJB) spent some time abstracting the implementation of
22+these channels and this patch advances the abstraction to add an
23+arbitrary number of channels - each with a distinct set of domains and
24+throttling capabilities.
25+
26+BIG PICTURE
27+With ext_todo patch. Adapted from:
28+EXTTODO by Claudio Jeker <jeker@n-r-g.com> and
29+Andre Oppermann <opi@nrg4u.com>
30+(c) 1998,1999,2000,2001,2002 Internet Business Solutions Ltd.
31+
32+
33+               +-------+   +-------+       +-------+
34+               | clean |   | clean |       | logger|
35+               +--0-1--+   +--0-1--+       +---0---+           +-----------+
36+         trigger  ^ |         ^ |              |             +->0,1 lspawn |
37+            |     | v         | v              v            /  +-----------+
38+ +-------+  v  +--2-3--+   +--5-6--------------0-------+   /
39+ |       |  |  |       0<--7                         1,2<-+
40+ | queue |--+--| todo  |   | send                      |
41+ |       |  |  |       1-->8                         3,4<-+
42+ +-------+     +-------+   +--11,12---...-------X,Y----+   \
43+                                |                |          \  +-----------+
44+                                v                v           +->0,1 rspwan |
45+                            +--0,1-+         +--0,1-+          +-----------+
46+                            |rspawn|  ...    |rspawn|   
47+                            +------+         +------+       
48+
49+Communication between qmail-send and qmail-todo
50+
51+todo -> send:
52+   D[01]{n}<mesgid>\0
53+          Start delivery for a new message with id <mesgid>.
54+          the character '0' or '1' indicates whether this message
55+          will go through the corresponding channel (false/true)
56+          by position where n is the number of channels. E.g. D1011<msgid>\0:
57+          means there are four channels, the first 2 are always
58+          the local and default remote channels, and the rest are
59+          an optional number of supplemental channels (defined
60+          at compile-time by conf-channels). So this message
61+          has a local recipient, and a recipient on the first and
62+          second supplemental channels.
63+   L<string>\0
64+          Dump string to the logger without adding additional \n or similar.
65+send -> todo:
66+   H      Got a SIGHUP, reread ~/control/locals, ~/control/virtualdomains,
67+          ~/control/concurrencyremote, ~/control/concurrencylocal,
68+          ~/control/concurrencysupplX, ~/control/supplsX
69+   X      Quit ASAP.
70+
71+qmail-todo sends "\0" terminated messages whereas qmail-send just send one
72+character to qmail-todo.
73+
74+
75+CAVEATS
76+qmail-qread ignores all supplemental channels - contributions are welcome!
77+
78+Supplemental channels use qmail-rspawn for remote recipients only.
79+
80+Dynamic throttling and resource limits
81+File descriptor limits are imposed on a per-process basis (FD_SET), on a
82+per-account basis (ulimit -n, /etc/security/limits.conf on Linux, pam limits, etc.)
83+and then on a system-wide basis by the OS (/proc/sys/fs/file-max on Linux, etc).
84+concurrencyremote, concurrencysupplX, etc are each subject to the hard limit in
85+conf-spawn, which in turn is bounded by per-process limits. Note that this limit
86+applies separately to each queue, not to all queues in total. The sum of all
87+concurrency limits for each queue in total is bounded on a per-account basis
88+(ulimit -n). These limits can easily be approached if you are running many
89+supplemental channels.
90+
91+qmail double checks the concurrency limits on startup for each channel (using FD_SET)
92+and silently curbs them if needed because bad things happen if this limit is breached.
93+If you're sending qmail-send a HUP signal after editing concurrency limits (dynamic
94+throttling) be aware that qmail's builtin checks can be circumvented, here's what
95+qmail's author has to say on the subject (from chkspawn.c):
96+This means that the qmail daemons could crash if you set the run-time concurrency higher
97+than [the per-process limit].
98+
99+Even if the per-process limits are in check, per-account and system-wide file descriptor
100+limits may still cause bad things to happen if you're not careful (you've been warned!).
101+
102+Enjoy!
103+Reed Sandberg
104+
105diff -ruN ../netqmail-1.06-original/CHKUSER.automatic_patching netqmail-1.06/CHKUSER.automatic_patching
106--- ../netqmail-1.06-original/CHKUSER.automatic_patching        1970-01-01 01:00:00.000000000 +0100
107+++ netqmail-1.06/CHKUSER.automatic_patching    2019-02-27 20:57:13.376025224 +0100
108@@ -0,0 +1,94 @@
109+Chkuser 2.0.9 automatic patching
110+
111+When to use automatic patching
112+==============================
113+
114+The release.tar package contains some .patch files, ready for installation,
115+trying to semplify the most frequent situations.
116+
117+You may use one of these patches if you have these sources:
118+
119+       - a clean qmail 1.03 or netqmail 1.05
120+
121+You may also consider using one of these patches if you have additional compatible
122+patches installed. This means that these additional patches should not have changed
123+the same sources and lines which are going to be used by chkuser.
124+
125+If you have any doubt, backup your sources and try the automatic installation,
126+otherwise execute the manual installation (that's very easy).
127+
128+Backup
129+======
130+
131+Save you qmail working sources before making any change.
132+
133+Basic installation
134+==================
135+
136+Download the newest release.tar package and untar it. It will create a directory
137+containing all release chkuser files and patches.
138+
139+Chose the most appropriate .patch file to be applied, according to your qmail
140+installation: .patch files names are self-describing.
141+
142+Position in the qmail/netqmail source directory:
143+
144+       $ cd /usr/.../netqmail-1.05
145+
146+Apply selected patch:
147+
148+       $ patch < /path_to_chkuser_release_dir/netqmail-1.05_chkuser-2.x.x.patch
149+
150+No errors should be displayed. If you see any error, better you restore your
151+sources and go to manual editing.
152+
153+editing vpopmail home path
154+
155+       If your production home path for vpopmail (or whatever you call him) user
156+       is NOT /home/vpopmail, you must perform the following additional actions.
157+
158+       Edit Makefile, changing the line referring to vpopmail's home path and
159+       putting the right home path:
160+
161+               VPOPMAIL_HOME=/home/vpopmail
162+
163+       Edit conf-cc, changing the string referring to vpopmail's home path and
164+       putting the right home path:
165+
166+               cc -O2 -I/home/vpopmail/include
167+
168+chkuser settings
169+================
170+
171+Edit chkuser_settings.h, uncommenting the options you prefer, and commenting the
172+ones you don't want. Default settings should cover the most of situations.
173+
174+See the related settings pages for more informations.
175+
176+Make
177+====
178+Now, make (or gmake on *BSD) as your usual. No errors (just warnings) should
179+come out. If you see any error, better you restore your sources
180+and go to manual editing.
181+
182+Checking
183+========
184+Select a domain, contained in your rcpthosts, for which bouncing is enabled, and run:
185+
186+       $ ./qmail-smtpd
187+       mail from <wrong_sender>
188+       mail from <right_sender>
189+       rcpt to: <fake_user@your_domain>
190+       rcpt to: <real_user@your_domain>
191+
192+You should see error and ok messages, depending on the addresses you typed.
193+
194+Install
195+=======
196+Copy the new executable in the /var/qmail/bin directory (or make install).
197+
198+Running
199+=======
200+This patched qmail-smtpd must be executed in a different way than the normal one.
201+See the running pages for detailed instructions.
202+
203diff -ruN ../netqmail-1.06-original/CHKUSER.changelog netqmail-1.06/CHKUSER.changelog
204--- ../netqmail-1.06-original/CHKUSER.changelog 1970-01-01 01:00:00.000000000 +0100
205+++ netqmail-1.06/CHKUSER.changelog     2019-02-27 20:57:13.376025224 +0100
206@@ -0,0 +1,183 @@
207+
208+CHKUSER 2.0 change log
209+
210+2.0.9 - 21 march 2007
211+   New features
212+        - New variable for accepting only authorized senders:
213+                #define CHKUSER_EXTRA_MUSTAUTH_VARIABLE "CHKUSER_MUSTAUTH"
214+                if the variable exists, then messages are accepted only if RELAYCLIENT is set
215+                If defined, it works always despite CHKUSER being ON/OFF
216+                This feature can be used for a "submission port" (see RFC 2476)
217+        - Improved checking of MySQL server availability (if MySQL is used as vpopmail user's DB)
218+        - Introduction of a new variable for disabling chkuser on the fly: CHKUSER_DISABLE_VARIABLE
219+                (can be used for single IP or defined as "RELAYCLIENT" for all authorized e-mail clients)
220+        - Improved starting variables checking sequence
221+                CHKUSER_ALWAYS_ON and CHKUSER_STARTING_VARIABLE cannot be defined together
222+                        and in such a case a fatal error is displayed; (in previous versions
223+                        CHKUSER_ALWAYS_ON would automatically disable CHKUSER_STARTING_VARIABLE definition).
224+                CHKUSER_DISABLE_VARIABLE is always evaluated after CHKUSER_ALWAYS_ON is set or
225+                        CHKUSER_STARTING_VARIABLE is evaluated, so CHKUSER_ALWAYS_ON or
226+                        CHKUSER_STARTING_VARIABLE can set the general behaviour, while
227+                        CHKUSER_DISABLE_VARIABLE should be invoked to handle exceptions.
228+        - New variable for accepting qmail doublebounces: CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE
229+                Sender "#@[]" will be accepted. It is used by qmail for doublebounces, and should be enabled for
230+                selected IP senders.
231+       - define CHKUSER_ENABLE_VAUTH_OPEN has been substituted by CHKUSER_VAUTH_OPEN_CALL: this new
232+               define must contain the name of the call used to open the auth DB
233+
234+   Updated features
235+        - checking for ezmlm mailing list is now done looking for file "editor"
236+                within mailing-list directory
237+        - defines for allowed character within sender and rcpt addresses increased
238+                up to CHKUSER_ALLOW_SENDER_CHAR_10 and CHKUSER_ALLOW_RCPT_CHAR_10
239+        - updated SMTP error strings; more exact and detailed error codes
240+                (thanks to Olivier Dony and Dane Thorsen)
241+        - logging of valid rcpt. If CHKUSER_LOG_VALID_RCPT is defined then all valid
242+                recipients are logged, even if domain does not want bouncing or chkuser
243+                is disabled
244+
245+   Bugs corrected
246+        - negative checking of backend DB connection did not report
247+                DB unavailability in some situations
248+                (thanks to Matt Brookings of Inter7)
249+        - in check_rcpt_address_format format checking was done using defines
250+                reserved for senders
251+
252+V 2.0.8 - 7 december 2004
253+   Features
254+       Freeze of new features of 2.0.7, except null senders behaviour.
255+       CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST is no more available
256+       CHKUSER_ENABLE_NULL_SENDER is no more available
257+       NULL SENDERS are now always accepted. No option is available to disable
258+               this behaviour. Previous chkuser versions broke RFC compatibility on
259+               null senders, and complicated real life e-mailing.
260+       Logging of null senders <> is now available.
261+       
262+   Bugs corrected
263+       Sender controls were not executed if CHKUSER_STARTING_VARIABLE was defined
264+               (thanks to Charles Sprickman)
265+       Domains not in control/virtualdomains are now explicitely excluded from
266+               following cascade checks; in previous versions following cascade
267+               checks were done using fake domains paths.
268+       vget_assign is now handled correctly (a domain in rcpthosts but not
269+               in virtualdomains could have an incorrect path in previous versions
270+               (this bug is also in all chkusr versions)
271+
272+       Defaults changed
273+       CHKUSER_RCPT_FORMAT is now undefined as default
274+       CHKUSER_RCPT_MX is now undefined as default.
275+       CHKUSER_SENDER_FORMAT is now undefined as default
276+       CHKUSER_SENDER_MX is now undefined as default.
277+       CHKUSER_ERROR_DELAY_INCREASE new default is 300 milliseconds
278+       
279+V 2.0.7 - 25 october 2004
280+   Features
281+       added vclose() of DB auth connection, overriding
282+               qmail-smtpd _exit call
283+       improved MX checking; now SOFT failure is handled as
284+               temporary error.
285+       added #define CHKUSER_RCPTMX_TMP_STRING
286+       added #define CHKUSER_SENDERMX_TMP_STRING
287+       added handling of mailman mailing lists
288+               (and related #define CHKUSER_ENABLE_MAILMAN_LISTS)
289+       changed order of checking for recipients:
290+               1 - valias
291+               2 - alias
292+               3 - alias extensions
293+               4 - users
294+               5 - users extensions
295+               6 - lists
296+       added #define CHKUSER_ACCEPT_NULL_SENDER (default defined)
297+       added #define CHKUSER_ENABLE_ALIAS_DEFAULT (default not defined)
298+               enables checking of .qmail-alias-default
299+       added #define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY"
300+               in order to allow a easy identification of remote IP
301+               (substitutes RELAYCLIENT in chkuser logging)
302+       added #define CHKUSER_ALLOW_RCPT_SRS
303+               enable usage of "#" and "+" characters within rcpt address
304+       added #define CHKUSER_ALLOW_RCPT_CHAR_1 '$'
305+       added #define CHKUSER_ALLOW_RCPT_CHAR_2 '%'
306+       added #define CHKUSER_ALLOW_RCPT_CHAR_3 '£'
307+       added #define CHKUSER_ALLOW_RCPT_CHAR_4 '?'
308+       added #define CHKUSER_ALLOW_RCPT_CHAR_5 '*'
309+       #define CHKUSER_ENABLE_USERS_EXTENSIONS
310+               substitutes #define CHKUSER_ENABLE_EXTENSIONS
311+       #define CHKUSER_ENABLE_EZMLM_LISTS
312+               substitutes #define CHKUSER_ENABLE_LISTS
313+       #define CHKUSER_USERS_DASH
314+               substitutes #define CHKUSER_EXTENSION_DASH
315+
316+   Bugs corrected
317+       sender address "name@" could cause a crash. Corrected
318+               (Thanks to Dmitry Petukhov)
319+       Corrected Makefile: now qmail-smtpd.c recompiles if chkuser.h
320+               changes
321+       Corrected a bug in #endif sequence related to
322+               #define CHKUSER_RCPT_FORMAT (thanks to Alex Plainer)
323+       Corrected a bug in chkuser_sender; now is not executed when
324+               chkuser is disabled
325+       Corrected check of format for domains:
326+               "xn--" admitted as leading string
327+       Deleted correction over usage of RELAYCLIENT variable
328+               Previous correction could affect a special
329+               feature of RELAYCLIENT (thanks to Alex Pleiner)
330+
331+   Defaults changed
332+       #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST (default undefined)
333+
334+
335+V 2.0.6 - 25 september 2004
336+       No bugs, just doc updates and an empty patch file corrected
337+
338+       #define CHKUSER_ENABLE_VGET_REAL_DOMAIN was existing and working in code,
339+               but not reported both in docs and inside chkuser_settings.h
340+                       (default is commented, but this #define is important)   
341+       patch for toaster-0.6-1 was empty. Now the correct one is provided
342+
343+V 2.0.5 - 23 september 2004
344+       This is the first public release.
345+
346+       added #define CHKUSER_ALLOW_SENDER_CHAR_1 (default not defined)
347+       added #define CHKUSER_ALLOW_SENDER_CHAR_2 (default not defined)
348+       added #define CHKUSER_ALLOW_SENDER_CHAR_3 (default not defined)
349+       added #define CHKUSER_ALLOW_SENDER_CHAR_4 (default not defined)
350+       added #define CHKUSER_ALLOW_SENDER_CHAR_5 (default not defined)
351+       added #define CHKUSER_MIN_DOMAIN_LEN (default defined 4) -
352+               Previously it was hard coded as 5. Juergen Kendzorra
353+               showed me some existing names long only 4 chars.
354+       added #define CHKUSER_LOG_VALID_SENDER (default defined)
355+
356+V 2.0.4 - 15 september 2004
357+
358+       added #define CHKUSER_SENDER_NOCHECK_VARIABLE (default not defined)
359+       added #define CHKUSER_DEBUG_STDERR (default not defined)
360+       added #define CHKUSER_ALLOW_SENDER_SRS (default not defined)
361+       cleaned some typos in code and documentation (thanks to Juergen
362+               Kendzorra - http://www.kendzorra.de)
363+
364+
365+V 2.0.3 - 8 september 2004
366+       This is the first version released outside, for wider testing.
367+
368+       Tested Makefile for netqmail 1.05
369+       Added Makefiles for applying over other patches
370+
371+V 2.0.0 - july 2004
372+       chkuser 2.0.0 starts here, and is a private internal release.
373+       Version 2.0 is much more modular than previous one (named chkusr),
374+       and has been designed with the goal of enabling more features and
375+       semplifying installations and upgrades of the patch himself.
376+
377+       chkusr changes his name, to reflect a deep change of the patch.
378+
379+       Chkusr 1.0 received a lot of feedbacks and suggestions.
380+       The most of these suggestions are now inside version 2.0.
381+
382+               - Marcelo Coelho (marcelo at tpn.com.br), segnaled me some
383+               unseen minor bugs of chkusr 1.0 (minor but very annoying to
384+               my pride) and suggested some very interesting features
385+               (some of them are now in chkuser 2.0).
386+               - Iulian Margarintescu (http:://www.erata.net) suggested a
387+               workable way of introducing quota check on recipients
388+               (now in chkuser 2.0).
389+
390diff -ruN ../netqmail-1.06-original/CHKUSER.copyright netqmail-1.06/CHKUSER.copyright
391--- ../netqmail-1.06-original/CHKUSER.copyright 1970-01-01 01:00:00.000000000 +0100
392+++ netqmail-1.06/CHKUSER.copyright     2019-02-27 20:57:13.376025224 +0100
393@@ -0,0 +1,15 @@
394+
395+chkuser for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
396+
397+Author: Antonio Nati tonix@interazioni.it
398+
399+All rights on this software and
400+the identifying words chkusr and chkuser kept by the author
401+
402+This software may be freely used, modified and distributed,
403+but this lines must be kept in every original or derived version.
404+
405+Original author "Antonio Nati" and the web URL
406+"http://www.interazioni.it/opensource"
407+must be indicated in every related work or web page
408+
409diff -ruN ../netqmail-1.06-original/CHKUSER.log_format netqmail-1.06/CHKUSER.log_format
410--- ../netqmail-1.06-original/CHKUSER.log_format        1970-01-01 01:00:00.000000000 +0100
411+++ netqmail-1.06/CHKUSER.log_format    2019-02-27 20:57:13.376025224 +0100
412@@ -0,0 +1,69 @@
413+
414+chkuser 2.0.9 logging format
415+
416+When #defines for logging are enabled, chkuser patch emits log informations
417+on the same qmail-smtpd log destination
418+
419+This is the log format:
420+
421+    CHKUSER "brief message": \
422+           from <sender:remoteinfo:relayclient> \
423+           remote <helo:remotehostname:remotehostip> \
424+           rcpt <recipient> : "extended message"
425+
426+where
427+       brief message   
428+                   * accepted rcpt
429+                   * relaying rcpt
430+                   * rejected relaying
431+                   * rejected rcpt
432+                   * no auth resource
433+                   * mbx overquota
434+                   * rejected intrusion
435+                   * intrusion threshold
436+                   * accepted sender
437+                   * rejected sender
438+                   * must auth
439+
440+       sender  sender declared within "mail from"
441+
442+       remoteinfo      the value of "TCPREMOTEINFO" or the autenticated user
443+
444+       relayclient     the value of CHKUSER_IDENTIFY env variable (this name
445+                        is defined by #define CHKUSER_IDENTIFY_REMOTE_VARIABLE)
446+
447+       helo            helo declared from remote system
448+
449+       hostname        the value of "TCPREMOTEHOST"
450+
451+       remotehostip    the value of "TCPREMOTEIP"
452+
453+       recipient       recipient address
454+
455+       extended message        this field has more wide description for
456+                               some generic "brief message":
457+                       accepted rcpt   found existing recipient
458+                       accepted rcpt   accepted any recipient for any rcpt doman (from 2.0.9)
459+                       accepted rcpt   accepted any recipient for this domain (from 2.0.9)
460+                       relaying rcpt   client allowed to relay
461+                       rejected relaying       client not allowed to relay
462+                       rejected rcpt   not existing recipient
463+                       rejected rcpt   max number of recipients
464+                       rejected rcpt   max number of invalid recipients
465+                       rejected rcpt   invalid rcpt address format
466+                       rejected rcpt   invalid rcpt MX domain
467+                       rejected rcpt   temporary DNS problem (from 2.0.9)
468+                       intrusion threshold     max number of allowed rcpt
469+                       intrusion threshold     max number of allowed invalid rcpt
470+                       rejected intrusion      rcpt ignored, session over intrusion threshold
471+                       no auth resource        no auth resource available
472+                       must auth       sender not authenticated/authorized (from 2.0.9)
473+                       mbx overquota   rcpt mailbox is overquota
474+                       accepted sender sender accepted (from 2.0.9)
475+                       accepted sender accepted any sender always (from 2.0.9)
476+                       accepted sender accepted null sender always (from 2.0.9)
477+                       accepted doublebounce   accepted qmail doublebounce #@[] (from 2.0.9)
478+                       rejected sender         invalid sender address format
479+                       rejected sender         invalid sender MX domain
480+                       rejected sender         temporary DNS problem (from 2.0.9)
481+       
482diff -ruN ../netqmail-1.06-original/CHKUSER.manual_patching netqmail-1.06/CHKUSER.manual_patching
483--- ../netqmail-1.06-original/CHKUSER.manual_patching   1970-01-01 01:00:00.000000000 +0100
484+++ netqmail-1.06/CHKUSER.manual_patching       2019-02-27 20:57:13.377025213 +0100
485@@ -0,0 +1,182 @@
486+Chkuser 2.0 manual editing
487+
488+Manual editing is a very simple operation.
489+
490+Watching the patch design, shown in the patch design page, you may see that
491+only some simple changes must be done to qmail-smtpd.c and Makefile.
492+
493+Backup
494+======
495+
496+Save you qmail working sources before making any change.
497+
498+Basic installation
499+==================
500+
501+Download the newest release.tar package and untar it. It will create a directory
502+containing all chkuser files and patches.
503+
504+Position in the qmail/netqmail source directory:
505+
506+       $ cd /usr/.../netqmail-1.05
507+
508+Copy all the chkuser sources:
509+
510+       $ cp /path_to_release_tar/chkuser* .
511+
512+edit qmail-smtpd.c
513+       within qmail-smtpd.c, change the following lines:
514+
515+       At the end of initial #include declarations, add the following (+) lines:
516+
517+       #include "timeoutwrite.h"
518+       #include "commands.h"
519+       
520++      /* start chkuser code */
521++      #include "chkuser.h"
522++      /* end chkuser code */
523+
524+       #define MAXHOPS 100
525+
526+Within smtp_mail routine, add the following (+) lines
527+
528+       void smtp_mail(arg) char *arg;
529+       {
530+       if (!addrparse(arg)) { err_syntax(); return; }
531++      /* start chkuser code */
532++      if (chkuser_sender (&addr) != CHKUSER_OK) { return; }
533++      /* end chkuser code */
534+       flagbarf = bmfcheck();
535+
536+       Within smtp_rcpt routine, delete the following (-) lines and substitute
537+       them with the (+) ones:
538+
539+-        if (relayclient) {
540+-          --addr.len;
541+-          if (!stralloc_cats(&addr,relayclient)) die_nomem();
542+-        }
543+-        else
544+-          if (!addrallowed()) { err_nogateway(); return; }
545+
546++      /* start chkuser code */
547++        switch (chkuser_realrcpt (&mailfrom, &addr)) {
548++          case CHKUSER_KO:
549++            return;
550++            break;
551++          case CHKUSER_RELAYING:
552++            --addr.len;
553++            if (!stralloc_cats(&addr,relayclient)) die_nomem();
554++            if (!stralloc_0(&addr)) die_nomem();
555++            break;
556++      }
557++      /* end chkuser code */
558+
559+       if (!stralloc_cats(&rcptto,"T")) die_nomem();
560+       if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
561+       if (!stralloc_0(&rcptto)) die_nomem();
562+
563+edit Makefile
564+       Within Makefile, change or add the following lines.
565+
566+       At the begininng of the file:
567+
568+       # Don't edit Makefile! Use conf-* for configuration.
569+
570++      VPOPMAIL_HOME=/home/vpopmail
571++      SMTPD_CHKUSER_OBJ=chkuser.o dns.o
572++      VPOPMAIL_LIBS=`head -1 $(VPOPMAIL_HOME)/etc/lib_deps` `cat dns.lib`
573+
574+       SHELL=/bin/sh
575+
576+       Be carefule to use the right path, if your vpopmail production home
577+       path is NOT "/home/vpopmail".
578+
579+       dns.lib is added to qmail-smtpd building instructions, so, if you
580+       have previously patched qmail-smtpd in order to include dns.lib, take
581+       care to delete the duplication from the previous lines.
582+
583+       Before "clean:" insert the chkuser.o definition:
584+
585+       exit.h auto_spawn.h
586+               ./compile chkspawn.c
587++       chkuser.o: \
588++       compile chkuser.c chkuser.h chkuser_settings.h
589++              ./compile chkuser.c
590+
591+       clean: \
592+
593+       Beware: the "./compile chkuser.c" line has an heading TAB.
594+
595+       Change the qmail-smtpd compiling and linking instructions,
596+       deleting the (-) lines and adding the (+) ones.
597+
598+
599+       qmail-smtpd: \
600+       load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
601+       timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
602+       date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
603+       open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
604+-      fs.a auto_qmail.o socket.lib
605++      fs.a auto_qmail.o socket.lib $(SMTPD_CHKUSER_OBJ)
606+-            ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
607++            ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) rcpthosts.o commands.o timeoutread.o \
608+             timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
609+             received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
610+             datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
611+-            alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \
612+-            socket.lib`
613++            alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
614++            $(VPOPMAIL_LIBS) \
615++            `cat socket.lib`
616+
617+       Beware: all the lines starting from and following "./load" have an heading TAB.
618+
619+edit TARGETS
620+       Append the following blue line at the end of TARGETS file:
621+
622+       man
623+       setup
624+       check
625++      chkuser.o
626+
627+edit conf-cc
628+       Edit conf-cc, adding the include path of production vpopmail:
629+
630+       cc -O2 -I/home/vpopmail/include
631+
632+       Be carefule to use the right path, if your vpopmail production home path
633+       is NOT "/home/vpopmail".
634+
635+chkuser settings
636+================
637+Edit chkuser_settings.h, uncommenting the options you prefer, and commenting the
638+ones you don't want. Default settings should cover the most of situations.
639+
640+See the related settings pages for more informations.
641+
642+Make
643+====
644+Now, make (or gmake on *BSD) as your usual. No errors (just warnings)
645+should come out. If you see any error, check carefully edited lines.
646+
647+Checking
648+========
649+Select a domain, contained in your rcpthosts, for which bouncing is enabled, and run:
650+
651+       $ ./qmail-smtpd
652+       mail from <wrong_sender>
653+       mail from <right_sender>
654+       rcpt to: <fake_user@your_domain>
655+       rcpt to: <real_user@your_domain>
656+
657+You should see error and ok messages, depending on the addresses you typed.
658+
659+Install
660+=======
661+Copy the new executable in the /var/qmail/bin directory (or make install).
662+
663+Running
664+=======
665+This patched qmail-smtpd must be executed in a different way than the normal one.
666+See the running pages for detailed instructions.
667+
668diff -ruN ../netqmail-1.06-original/CHKUSER.readme netqmail-1.06/CHKUSER.readme
669--- ../netqmail-1.06-original/CHKUSER.readme    1970-01-01 01:00:00.000000000 +0100
670+++ netqmail-1.06/CHKUSER.readme        2019-02-27 20:57:13.377025213 +0100
671@@ -0,0 +1,54 @@
672+chkuser 2.0 - README
673+
674+Description
675+===========
676+The original qmail-smtpd accepts by default all messages, checking later for
677+the existence of the recipient. So, if the message is delivered to not existing
678+recipients a lot of additional system work and network traffic are generated,
679+with multiple expensive bouncing if the sender is a fake one.
680+
681+chkuser has been developed with the goal to improve the acceptance SMTP phase
682+of qmail-smtpd. qmail-smtpd patched with chkuser may check the existance of
683+e-mail recipients immediately in the SMTP acceptance phase of a message and
684+rejects istantly all messages not directed to existing users, avoiding
685+additional traffic, work and messages bounced more times.
686+
687+These goals are achieved enquirying the existing vpopmail archives (each
688+format is supported: cdb, MySQL, LDAP, etc.) by using standard vpopmail calls,
689+or using customized chkuser routines.
690+
691+Version 2.0 - From chkusr to chkuser
692+====================================
693+Version 2.0 is a lot different from previous versions, so it deserves a more
694+evident change in the name.
695+
696+Version 2.0 has been designed with the goal to be modular, and to make more easy
697+both adding new features to chkuser code and semplifing code update.
698+
699+Patching over original qmail files is done over a few points, while the most of
700+chkuser code remains ouside, in dedicated chkuser's files.
701+
702+Same for settings, that are inside a dedicated chkuser_settings.h file.
703+
704+The intention is to semplify upgrading: for future chkuser releases, upgrading
705+will require only to update chkuser specific files, leaving all the rest
706+untouched, and changing chkuser_settings.h only if new features must be enabled.
707+
708+Logging and SPAM
709+================
710+chkuser 2.0 has detailed logging of accepted and refused recipients and senders,
711+allowing a deep analysis of "who's sending to who". This can lead to more
712+sophisticated future enhancements of anti-SPAM features.
713+
714+Intrusion rejection
715+===================
716+chkuser 2.0 can be tuned to reject sessions exceeding some recipients limits
717+(limits can be set for max recipients and max not existing recipients).
718+
719+URL Location
720+============
721+For any new release, support, FAQ, mailing lists, or other information, see:
722+
723+       http://www.interazioni.it/opensource
724+
725+
726diff -ruN ../netqmail-1.06-original/CHKUSER.running netqmail-1.06/CHKUSER.running
727--- ../netqmail-1.06-original/CHKUSER.running   1970-01-01 01:00:00.000000000 +0100
728+++ netqmail-1.06/CHKUSER.running       2019-02-27 20:57:13.377025213 +0100
729@@ -0,0 +1,103 @@
730+
731+CHKUSER 2.0.9 - Running instructions
732+
733+Chkuser may run using the most of security, following very strictly the sacurity
734+model used By Dan Berstein. To achieve this goal, chkuser may switch between
735+differents UID/GID, for differente purposes.
736+
737+However this is incompatible with TLS patches (like toaster-0.6-1), as these patches
738+want to run under a unique UID/GID. Luckily, qmail is enought robust to let us
739+run this way.
740+
741+To achieve both these goals, chkuser uses a #define (CHKUSER_ENABLE_UIDGID)
742+that indicates if UID/GID switching is wanted, and running instructions must
743+adapt to this way.
744+
745+Instead, when this define is not used, another way of running must be used.
746+(Just for precision, even if the CHKUSER_ENABLE_UIDGID define is used, chkuser
747+may be run without switching UID/GID).
748+
749+Running with UID/GID switch
750+===========================
751+
752+If you want the most security when using chkuser, and you have enabled
753+CHKUSER_ENABLE_UIDGID within chkuser_settings.h (it's enabled by default), use
754+these instructions.
755+
756+Description.
757+       qmail-smtpd-chkusr must be installed (by default in /var/qmail/bin) with
758+       setuid (user qmaild) and setgid (group qnofiles), and executed by tcpserver
759+       with -u vpopmail-user and -g vchkpw-group  parameters.
760+
761+       qmail-smtpd-chkusr starts running with the original qmail-smtpd uid and gid,
762+       switching to needed uid and gid only for vpopmail checks on user existance,
763+       turning back to the starting uid and gid.
764+
765+Instructions.
766+       You have to set SUID (set-user-ID-on-execution) and SGID
767+       (set-group-ID-on-execution) bits on qmail-smtpd-chkusr:
768+               chown qmaild qmail-smtpd
769+               chgrp nofiles qmail-smtpd
770+               chmod 6555 qmail-smtpd
771+
772+       and the result you see should be like (different size and date, of course):
773+               -r-sr-sr-x 1 qmaild nofiles 57056 Feb 14 18:18 qmail-smtpd-chkusr
774+
775+       Integrate qmail-smtpd in your start files:
776+
777+       As example, a real start command for qmail-smtpd-chkusr may be
778+
779+       #!/bin/sh -e
780+       #
781+       # Using splogger to send the log through syslog.
782+
783+       exec env - PATH="/var/qmail/bin:/usr/local/bin" \
784+       tcpserver -t 5 -v -p -x <your.tcp.smtp.cdb> \
785+       -u <vpopmail-user> -g <vchkpw-group> -l <your-host.domain> 0 smtp \
786+       qmail-smtpd-chkusr splogger smtpd &
787+
788+       where
789+               <vpopmail-user> = vpopmail uid
790+               <vchkpw-group> = vchkpw gid
791+               <your-host.domain> = your host.domain (!)
792+               <your.tcp.smtp.cdb> = your tcp.permission.to.relay cdb
793+
794+       NOTE: if you are using more system users for your domains, the execution
795+       uid (which I indicated as vpopmail) should be set to root.
796+
797+
798+Running with fixed UID/GID
799+==========================
800+You may use these instructions if you've not defined CHKUSER_ENABLE_UIDGID, or if
801+you want to run qmail-smtpd as unique user, despite of CHKUSER_ENABLE_UIDGID define.
802+qmail-smtpd is well safe and robust, and there is no risk running it directly as
803+vpopmail user, unless you use untrusted software layered down.
804+
805+Description.
806+       qmail-smtpd must be installed normally (-r-xr-xr-x) and executed by tcpserver
807+       with -u vpopmail-user and -g vchkpw-group parameters.
808+
809+Instructions.
810+       Integrate qmail-smtpd-chkusr in your start files:
811+
812+       As example, a real start command for qmail-smtpd-chkusr may be
813+
814+       #!/bin/sh -e
815+       #
816+       # Using splogger to send the log through syslog.
817+
818+       exec env - PATH="/var/qmail/bin:/usr/local/bin" \
819+       tcpserver -t 5 -v -p -x <your.tcp.smtp.cdb> \
820+       -u <vpopmail-user> -g <vchkpw-group> -l <your-host.domain> 0 smtp \
821+       qmail-smtpd-chkusr splogger smtpd &
822+
823+       where
824+               <vpopmail-user> = vpopmail uid
825+               <vchkpw-group> = vchkpw gid
826+               <your-host.domain> = your host.domain (!)
827+               <your.tcp.smtp.cdb> = your tcp.permission.to.relay cdb
828+
829+       NOTE: if you are using more system users for your domains, the execution user
830+       (which I indicated as vpopmail) should be set to root.
831+
832+
833diff -ruN ../netqmail-1.06-original/FILES netqmail-1.06/FILES
834--- ../netqmail-1.06-original/FILES     2007-11-30 21:22:54.000000000 +0100
835+++ netqmail-1.06/FILES 2019-06-26 16:39:31.573826970 +0200
836@@ -136,6 +136,8 @@
837 dnsip.c
838 dnsmxip.c
839 dnsptr.c
840+dnstxt.c
841+spfquery.c
842 hostname.c
843 ipmeprint.c
844 tcp-env.c
845@@ -336,13 +338,16 @@
846 byte.h
847 byte_chr.c
848 byte_copy.c
849+byte_cspn.c
850 byte_cr.c
851 byte_diff.c
852 byte_rchr.c
853+byte_rcspn.c
854 byte_zero.c
855 str.h
856 str_chr.c
857 str_cpy.c
858+str_cpyb.c
859 str_diff.c
860 str_diffn.c
861 str_len.c
862@@ -402,6 +407,8 @@
863 date822fmt.c
864 dns.h
865 dns.c
866+spf.h
867+spf.c
868 trylsock.c
869 tryrsolv.c
870 ip.h
871@@ -432,3 +439,7 @@
872 tcp-environ.5
873 constmap.h
874 constmap.c
875+qmail-todo.c
876+channels.g
877+conf-channels
878+CHANNELS
879diff -ruN ../netqmail-1.06-original/LICENSE.authentication netqmail-1.06/LICENSE.authentication
880--- ../netqmail-1.06-original/LICENSE.authentication    1970-01-01 01:00:00.000000000 +0100
881+++ netqmail-1.06/LICENSE.authentication        2019-02-27 20:57:13.377025213 +0100
882@@ -0,0 +1,43 @@
883+AUTHOR
884+======
885+
886+Author:
887+       Dr. Erwin Hoffmann - FEHCom Germany
888+Web-Site:     
889+       http://www.fehcom.de/qmail.html
890+E-Mail:       
891+       feh@fehcom.de
892+
893+
894+LICENSE
895+=======
896+
897+qmail AUTHENTICATION is free software.
898+This includes:
899+       You can download and use qmail AUTHENTICATION (and parts of it) as you like.
900+       You can modify the source code without notification to or permission by the author.
901+Please check:
902+       http://www.cr.yp.to/softwarelaw.html
903+
904+
905+DEPENDENCIES
906+============
907+
908+qmail AUTHENTICATION patches (modifies) parts of the qmail-1.03 source files.
909+It should only be applied against the source as supplied by D.J. Bernstein.
910+
911+
912+FITNESS
913+=======
914+
915+The Author does not guarantee a specific fitness of qmail AUTHENTICATION.
916+If you use qmail AUTHENTICATION, it's on your own risk.
917+
918+
919+DISTRIBUTION
920+============
921+
922+qmail AUTHENTICATION may be included in ports and packages under the following conditions:
923+       The port/package has to show the current version number of qmail AUTHENTICATION.
924+       All files (namely this) have to be included.
925+
926diff -ruN ../netqmail-1.06-original/MakeArgs.c netqmail-1.06/MakeArgs.c
927--- ../netqmail-1.06-original/MakeArgs.c        1970-01-01 01:00:00.000000000 +0100
928+++ netqmail-1.06/MakeArgs.c    2019-02-27 20:57:13.377025213 +0100
929@@ -0,0 +1,144 @@
930+/*
931+ * $Log: MakeArgs.c,v $
932+ * Revision 2.8  2007-12-21 14:35:42+05:30  Cprogrammer
933+ * included env.h to prevent compiler warning
934+ *
935+ * Revision 2.7  2007-12-20 12:45:28+05:30  Cprogrammer
936+ * expand environment variables with '$' sign
937+ *
938+ * Revision 2.6  2005-08-23 17:31:28+05:30  Cprogrammer
939+ * removed sccsid variable
940+ *
941+ * Revision 2.5  2005-04-02 19:06:02+05:30  Cprogrammer
942+ * djb version
943+ *
944+ * Revision 2.4  2005-03-30 22:52:47+05:30  Cprogrammer
945+ * BUG - Incorrect free
946+ *
947+ * Revision 2.3  2004-07-12 22:47:58+05:30  Cprogrammer
948+ * bug fix. Free all allocated members
949+ *
950+ * Revision 2.2  2002-12-21 18:21:09+05:30  Cprogrammer
951+ * added functionality of escaping text via quotes
952+ *
953+ * Revision 2.1  2002-08-13 20:35:44+05:30  Cprogrammer
954+ * addition spaces were not getting skipped
955+ *
956+ * Revision 1.2  2002-03-03 17:23:05+05:30  Cprogrammer
957+ * replaced strcpy with scopy
958+ *
959+ * Revision 1.1  2001-12-13 01:46:09+05:30  Cprogrammer
960+ * Initial revision
961+ *
962+ */
963+#include "alloc.h"
964+#include "str.h"
965+#include "stralloc.h"
966+#include "env.h"
967+#include <ctype.h>
968+
969+#define isEscape(ch) ((ch) == '"' || (ch) == '\'')
970+
971+/*
972+ * function to expand a string into command line
973+ * arguments. To free memory allocated by this
974+ * function the following should be done
975+ *
976+ * free(argv);
977+ *
978+ */
979+char          **
980+MakeArgs(char *cmmd)
981+{
982+       char           *ptr, *marker;
983+       char          **argv;
984+       int             argc, idx;
985+       static stralloc sptr = { 0 };
986+
987+       for (ptr = cmmd;*ptr && isspace((int) *ptr);ptr++);
988+       idx = str_len(ptr);
989+       if (!stralloc_copys(&sptr, ptr))
990+               return((char **) 0);
991+       if (!stralloc_0(&sptr))
992+               return((char **) 0);
993+       /*-
994+        * Get the number of arguments by counting
995+        * white spaces. Allow escape via the double
996+        * quotes character at the first word
997+        */
998+       for (argc = 0, ptr = sptr.s;*ptr;)
999+       {
1000+               for (;*ptr && isspace((int) *ptr);ptr++);
1001+               if (!*ptr)
1002+                       break;
1003+               argc++;
1004+               marker = ptr;
1005+               /*- Move till you hit the next white space */
1006+               for (;*ptr && !isspace((int) *ptr);ptr++)
1007+               {
1008+                       /*-
1009+                        * 1. If escape char is encounted skip till you
1010+                        *    hit the terminating escape char
1011+                        * 2. If terminating escape char is missing, come
1012+                        *    back to the start escape char
1013+                        */
1014+                       if (ptr == marker && isEscape(*ptr))
1015+                       {
1016+                               for (ptr++;*ptr && !isEscape(*ptr);ptr++);
1017+                               if (!*ptr)
1018+                                       ptr = marker;
1019+                       }
1020+               } /*- for(;*ptr && !isspace((int) *ptr);ptr++) */
1021+       } /*- for (argc = 0, ptr = sptr.s;*ptr;) */
1022+       /*
1023+        * Allocate memory to store the arguments
1024+        * Do not bother extra bytes occupied by
1025+        * white space characters.
1026+        */
1027+       if (!(argv = (char **) alloc((argc + 1) * sizeof(char *))))
1028+               return ((char **) 0);
1029+       for (idx = 0, ptr = sptr.s;*ptr;)
1030+       {
1031+               for (;*ptr && isspace((int) *ptr);ptr++)
1032+                       *ptr = 0;
1033+               if (!*ptr)
1034+                       break;
1035+               if (*ptr == '$')
1036+                       argv[idx++] = env_get(ptr + 1);
1037+               else
1038+                       argv[idx++] = ptr;
1039+               marker = ptr;
1040+               for (;*ptr && !isspace((int) *ptr);ptr++)
1041+               {
1042+                       if (ptr == marker && isEscape(*ptr))
1043+                       {
1044+                               for (ptr++;*ptr && !isEscape(*ptr);ptr++);
1045+                               if (!*ptr)
1046+                                       ptr = marker;
1047+                               else /*- Remove the quotes */
1048+                               {
1049+                                       argv[idx - 1] += 1;
1050+                                       *ptr = 0;
1051+                               }
1052+                       }
1053+               }
1054+       } /*- for (idx = 0, ptr = sptr.s;*ptr;) */
1055+       argv[idx++] = (char *) 0;
1056+       return (argv);
1057+}
1058+
1059+void
1060+FreeMakeArgs(char **argv)
1061+{
1062+       alloc_free(argv);
1063+       return;
1064+}
1065+
1066+void
1067+getversion_MakeArgs__c()
1068+{
1069+       static char     *x = "$Id: MakeArgs.c,v 2.8 2007-12-21 14:35:42+05:30 Cprogrammer Stab mbhangui $";
1070+       x++;
1071+       x--;
1072+       return;
1073+}
1074diff -ruN ../netqmail-1.06-original/Makefile netqmail-1.06/Makefile
1075--- ../netqmail-1.06-original/Makefile  2007-11-30 21:22:54.000000000 +0100
1076+++ netqmail-1.06/Makefile      2019-06-26 16:48:00.745225709 +0200
1077@@ -1,5 +1,14 @@
1078 # Don't edit Makefile! Use conf-* for configuration.
1079 
1080+VPOPMAIL_HOME=/home/vpopmail
1081+VPOPMAIL_LIBS=`head -1 $(VPOPMAIL_HOME)/etc/lib_deps` `cat dns.lib`
1082+
1083+SMTPD_CHKUSER_OBJ=chkuser.o dns.o
1084+
1085+LIBDOMAINKEYS=/usr/lib
1086+
1087+DEFINES=-DEXTERNAL_TODO # use to enable external todo
1088+
1089 SHELL=/bin/sh
1090 
1091 default: it
1092@@ -136,6 +145,18 @@
1093 compile auto_usera.c
1094        ./compile auto_usera.c
1095 
1096+base64.o: \
1097+compile base64.c base64.h stralloc.h substdio.h str.h
1098+       ./compile base64.c
1099+
1100+md5c.o : \
1101+compile md5c.c md5.h
1102+       ./compile md5c.c
1103+
1104+hmac_md5.o : \
1105+compile hmac_md5.c hmac_md5.h global.h
1106+       ./compile hmac_md5.c
1107+
1108 binm1: \
1109 binm1.sh conf-qmail
1110        cat binm1.sh \
1111@@ -203,6 +224,10 @@
1112 compile byte_cr.c byte.h
1113        ./compile byte_cr.c
1114 
1115+byte_cspn.o: \
1116+compile byte_cspn.c byte.h
1117+       ./compile byte_cspn.c
1118+
1119 byte_diff.o: \
1120 compile byte_diff.c byte.h
1121        ./compile byte_diff.c
1122@@ -211,6 +236,10 @@
1123 compile byte_rchr.c byte.h
1124        ./compile byte_rchr.c
1125 
1126+byte_rcspn.o: \
1127+compile byte_rcspn.c byte.h
1128+       ./compile byte_rcspn.c
1129+
1130 byte_zero.o: \
1131 compile byte_zero.c byte.h
1132        ./compile byte_zero.c
1133@@ -300,6 +329,10 @@
1134 exit.h auto_spawn.h
1135        ./compile chkspawn.c
1136 
1137+chkuser.o: \
1138+compile chkuser.c chkuser.h chkuser_settings.h
1139+       ./compile chkuser.c
1140+
1141 clean: \
1142 TARGETS
1143        rm -f `cat TARGETS`
1144@@ -320,10 +353,13 @@
1145        chmod 755 compile
1146 
1147 condredirect: \
1148-load condredirect.o qmail.o strerr.a fd.a sig.a wait.a seek.a env.a \
1149-substdio.a error.a str.a fs.a auto_qmail.o
1150-       ./load condredirect qmail.o strerr.a fd.a sig.a wait.a \
1151-       seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o
1152+load condredirect.o srs.o rcpthosts.o cdb.a control.o constmap.o case.a \
1153+       getln.a stralloc.a alloc.a open.a qmail.o strerr.a fd.a sig.a \
1154+       wait.a seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o
1155+       ./load condredirect srs.o rcpthosts.o cdb.a control.o constmap.o \
1156+       case.a getln.a stralloc.a alloc.a open.a qmail.o strerr.a fd.a sig.a \
1157+       wait.a seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o \
1158+       -I/usr/local/include -L/usr/local/lib -lsrs2
1159 
1160 condredirect.0: \
1161 condredirect.1
1162@@ -331,7 +367,7 @@
1163 
1164 condredirect.o: \
1165 compile condredirect.c sig.h readwrite.h exit.h env.h error.h fork.h \
1166-wait.h seek.h qmail.h substdio.h strerr.h substdio.h fmt.h
1167+wait.h seek.h qmail.h substdio.h strerr.h substdio.h fmt.h stralloc.h srs.h
1168        ./compile condredirect.c
1169 
1170 config: \
1171@@ -393,84 +429,96 @@
1172        rm -f trydrent.o
1173 
1174 dns.lib: \
1175-tryrsolv.c compile load socket.lib dns.o ipalloc.o ip.o stralloc.a \
1176-alloc.a error.a fs.a str.a
1177+tryrsolv.c compile load socket.lib dns.o ipalloc.o strsalloc.o ip.o \
1178+stralloc.a alloc.a error.a fs.a str.a
1179        ( ( ./compile tryrsolv.c && ./load tryrsolv dns.o \
1180-       ipalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \
1181+       ipalloc.o strsalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \
1182        -lresolv `cat socket.lib` ) >/dev/null 2>&1 \
1183        && echo -lresolv || exit 0 ) > dns.lib
1184        rm -f tryrsolv.o tryrsolv
1185 
1186 dns.o: \
1187-compile dns.c ip.h ipalloc.h ip.h gen_alloc.h fmt.h alloc.h str.h \
1188-stralloc.h gen_alloc.h dns.h case.h
1189+compile dns.c ip.h ipalloc.h strsalloc.h gen_alloc.h fmt.h alloc.h \
1190+str.h stralloc.h dns.h case.h
1191        ./compile dns.c
1192 
1193 dnscname: \
1194-load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
1195+load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
1196 substdio.a error.a str.a fs.a dns.lib socket.lib
1197-       ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
1198+       ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
1199        alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
1200        socket.lib`
1201 
1202 dnscname.o: \
1203-compile dnscname.c substdio.h subfd.h substdio.h stralloc.h \
1204+compile dnscname.c substdio.h subfd.h stralloc.h \
1205 gen_alloc.h dns.h dnsdoe.h readwrite.h exit.h
1206        ./compile dnscname.c
1207 
1208 dnsdoe.o: \
1209-compile dnsdoe.c substdio.h subfd.h substdio.h exit.h dns.h dnsdoe.h
1210+compile dnsdoe.c substdio.h subfd.h exit.h dns.h dnsdoe.h
1211        ./compile dnsdoe.c
1212 
1213 dnsfq: \
1214-load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
1215+load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
1216 substdio.a error.a str.a fs.a dns.lib socket.lib
1217-       ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
1218+       ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
1219        alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
1220        socket.lib`
1221 
1222 dnsfq.o: \
1223-compile dnsfq.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
1224-dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h
1225+compile dnsfq.c substdio.h subfd.h stralloc.h gen_alloc.h \
1226+dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h
1227        ./compile dnsfq.c
1228 
1229 dnsip: \
1230-load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
1231+load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
1232 substdio.a error.a str.a fs.a dns.lib socket.lib
1233-       ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
1234+       ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
1235        alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
1236        socket.lib`
1237 
1238 dnsip.o: \
1239-compile dnsip.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
1240-dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h
1241+compile dnsip.c substdio.h subfd.h stralloc.h gen_alloc.h \
1242+dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h
1243        ./compile dnsip.c
1244 
1245 dnsmxip: \
1246-load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o now.o stralloc.a alloc.a \
1247-substdio.a error.a str.a fs.a dns.lib socket.lib
1248-       ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o now.o \
1249+load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o stralloc.a \
1250+alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
1251+       ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o \
1252        stralloc.a alloc.a substdio.a error.a str.a fs.a  `cat \
1253        dns.lib` `cat socket.lib`
1254 
1255 dnsmxip.o: \
1256-compile dnsmxip.c substdio.h subfd.h substdio.h stralloc.h \
1257-gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h \
1258+compile dnsmxip.c substdio.h subfd.h stralloc.h \
1259+gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h \
1260 now.h datetime.h exit.h
1261        ./compile dnsmxip.c
1262 
1263 dnsptr: \
1264-load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
1265+load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
1266 substdio.a error.a str.a fs.a dns.lib socket.lib
1267-       ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
1268+       ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
1269        alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
1270        socket.lib`
1271 
1272 dnsptr.o: \
1273-compile dnsptr.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
1274+compile dnsptr.c substdio.h subfd.h stralloc.h gen_alloc.h \
1275 str.h scan.h dns.h dnsdoe.h ip.h exit.h
1276        ./compile dnsptr.c
1277 
1278+dnstxt: \
1279+load dnstxt.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
1280+substdio.a error.a str.a fs.a dns.lib socket.lib
1281+       ./load dnstxt dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
1282+       alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
1283+       socket.lib`
1284+
1285+dnstxt.o: \
1286+compile dnstxt.c substdio.h subfd.h stralloc.h gen_alloc.h \
1287+str.h scan.h dns.h dnsdoe.h ip.h exit.h
1288+       ./compile dnstxt.c
1289+
1290 dot-qmail.0: \
1291 dot-qmail.5
1292        nroff -man dot-qmail.5 > dot-qmail.0
1293@@ -593,10 +641,13 @@
1294        rm -f tryvfork.o tryvfork
1295 
1296 forward: \
1297-load forward.o qmail.o strerr.a alloc.a fd.a wait.a sig.a env.a \
1298-substdio.a error.a str.a fs.a auto_qmail.o
1299-       ./load forward qmail.o strerr.a alloc.a fd.a wait.a sig.a \
1300-       env.a substdio.a error.a str.a fs.a auto_qmail.o
1301+load forward.o srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \
1302+       cdb.a case.a open.a stralloc.a alloc.a getln.a \
1303+       fd.a wait.a sig.a env.a substdio.a error.a str.a fs.a auto_qmail.o
1304+       ./load forward srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \
1305+       cdb.a case.a open.a stralloc.a alloc.a getln.a \
1306+       fd.a wait.a sig.a env.a substdio.a error.a str.a fs.a auto_qmail.o \
1307+       -I/usr/local/include -L/usr/local/lib -lsrs2
1308 
1309 forward.0: \
1310 forward.1
1311@@ -604,7 +655,7 @@
1312 
1313 forward.o: \
1314 compile forward.c sig.h readwrite.h exit.h env.h qmail.h substdio.h \
1315-strerr.h substdio.h fmt.h
1316+strerr.h substdio.h fmt.h stralloc.h srs.h
1317        ./compile forward.c
1318 
1319 fs.a: \
1320@@ -702,8 +753,15 @@
1321        ./compile hfield.c
1322 
1323 hier.o: \
1324-compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h
1325-       ./compile hier.c
1326+compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h channels.h
1327+       ./compile $(DEFINES) hier.c
1328+
1329+channels.h: \
1330+conf-channels channels.g
1331+       cat channels.g \
1332+       | sed s}NUMCHANNELS}"`head -1 conf-channels`"}g \
1333+       > channels.h
1334+       chmod 644 channels.h
1335 
1336 home: \
1337 home.sh conf-qmail
1338@@ -754,8 +812,8 @@
1339 
1340 install-big.o: \
1341 compile install-big.c auto_qmail.h auto_split.h auto_uids.h fmt.h \
1342-fifo.h
1343-       ./compile install-big.c
1344+fifo.h channels.h
1345+       ./compile $(DEFINES) install-big.c
1346 
1347 install.o: \
1348 compile install.c substdio.h strerr.h error.h open.h readwrite.h \
1349@@ -777,38 +835,52 @@
1350        ./compile ip.c
1351 
1352 ipalloc.o: \
1353-compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h ip.h \
1354+compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h \
1355 gen_alloc.h
1356        ./compile ipalloc.c
1357 
1358 ipme.o: \
1359-compile ipme.c hassalen.h byte.h ip.h ipalloc.h ip.h gen_alloc.h \
1360-stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h
1361+compile ipme.c hassalen.h byte.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h \
1362+stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h readwrite.h
1363        ./compile ipme.c
1364 
1365 ipmeprint: \
1366-load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \
1367-error.a str.a fs.a socket.lib
1368-       ./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \
1369-       substdio.a error.a str.a fs.a  `cat socket.lib`
1370+load ipmeprint.o ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a \
1371+strsalloc.o stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib
1372+       ./load ipmeprint ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a \
1373+       strsalloc.o stralloc.a alloc.a substdio.a error.a str.a fs.a \
1374+       `cat socket.lib`
1375 
1376 ipmeprint.o: \
1377 compile ipmeprint.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \
1378-ipalloc.h ip.h gen_alloc.h exit.h
1379+ipalloc.h strsalloc.h ip.h gen_alloc.h exit.h auto_qmail.h
1380        ./compile ipmeprint.c
1381 
1382+ipmetest: \
1383+load ipmetest.o ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a stralloc.a alloc.a substdio.a \
1384+error.a str.a fs.a env.a socket.lib
1385+       ./load ipmetest ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a stralloc.a alloc.a \
1386+       substdio.a error.a env.a str.a fs.a `cat socket.lib`
1387+
1388+ipmetest.o: \
1389+compile ipmetest.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \
1390+ipalloc.h ip.h gen_alloc.h exit.h auto_qmail.h
1391+       ./compile ipmetest.c
1392+
1393 it: \
1394 qmail-local qmail-lspawn qmail-getpw qmail-remote qmail-rspawn \
1395+qmail-newmvrt \
1396 qmail-clean qmail-send qmail-start splogger qmail-queue qmail-inject \
1397 predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \
1398 qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
1399 qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
1400 qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
1401-dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
1402+dnsptr dnsip dnsmxip dnsfq dnstxt hostname ipmeprint ipmetest qreceipt qreceipt qsmhook qbiff \
1403 forward preline condredirect bouncesaying except maildirmake \
1404 maildir2mbox maildirwatch qail elq pinq idedit install-big install \
1405+dktest qmail-dk qmail-dkim dkim spawn-filter dk-filter surblfilter \
1406 instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \
1407-binm3 binm3+df
1408+binm3 binm3+df srsfilter surblqueue dknewkey qmail-todo spfquery update_tmprsadh
1409 
1410 load: \
1411 make-load warn-auto.sh systype
1412@@ -890,6 +962,38 @@
1413 readwrite.h open.h headerbody.h maildir.h strerr.h
1414        ./compile maildirwatch.c
1415 
1416+maildirgetquota.o: \
1417+compile maildirgetquota.c maildirgetquota.h maildirmisc.h
1418+       ./compile maildirgetquota.c
1419+
1420+maildirflags.o: \
1421+compile maildirflags.c
1422+       ./compile maildirflags.c
1423+
1424+maildiropen.o: \
1425+compile maildiropen.c maildirmisc.h
1426+       ./compile maildiropen.c
1427+
1428+maildirparsequota.o: \
1429+compile maildirparsequota.c
1430+       ./compile maildirparsequota.c
1431+
1432+maildirquota.o: \
1433+compile maildirquota.c maildirquota.h maildirmisc.h numlib.h
1434+       ./compile maildirquota.c
1435+
1436+overmaildirquota.o: \
1437+compile overmaildirquota.c
1438+       ./compile overmaildirquota.c
1439+
1440+strtimet.o: \
1441+compile strtimet.c
1442+       ./compile strtimet.c
1443+
1444+strpidt.o: \
1445+compile strpidt.c
1446+       ./compile strpidt.c
1447+
1448 mailsubj: \
1449 warn-auto.sh mailsubj.sh conf-qmail conf-break conf-split
1450        cat warn-auto.sh mailsubj.sh \
1451@@ -934,8 +1038,9 @@
1452 preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \
1453 maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \
1454 qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \
1455-qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \
1456-envelopes.0 forgeries.0
1457+qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 dkim.8 dktest.8 \
1458+envelopes.0 forgeries.0 qmail-dk.0 qmail-dkim.0 dk-filter.0 spawn-filter.0 \
1459+surblfilter.0
1460 
1461 mbox.0: \
1462 mbox.5
1463@@ -1107,11 +1212,80 @@
1464        | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1465        > qmail-control.5
1466 
1467+qmail-dk: \
1468+load qmail-dk.o triggerpull.o fmtqfn.o now.o date822fmt.o mess822_ok.o \
1469+subgetopt.o MakeArgs.o datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \
1470+str.a case.a fs.a auto_qmail.o auto_split.o auto_uids.o fd.a wait.a \
1471+$(LIBDOMAINKEYS)/libdomainkeys.a env.a getln.a control.o stralloc.a dns.lib
1472+       ./load qmail-dk triggerpull.o fmtqfn.o now.o mess822_ok.o \
1473+       date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \
1474+       subgetopt.o MakeArgs.o substdio.a error.a fs.a auto_qmail.o \
1475+       auto_split.o auto_uids.o \
1476+       fd.a wait.a $(LIBDOMAINKEYS)/libdomainkeys.a -lcrypto env.a control.o open.a getln.a \
1477+       stralloc.a alloc.a substdio.a str.a case.a `cat dns.lib`
1478+
1479+qmail-dk.0: \
1480+qmail-dk.8
1481+       nroff -man qmail-dk.8 > qmail-dk.0
1482+qmail-dk.8: qmail-dk.9
1483+       cat qmail-dk.9 \
1484+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1485+       > qmail-dk.8
1486+
1487+qmail-dk.o: \
1488+compile qmail-dk.c readwrite.h sig.h exit.h open.h seek.h fmt.h \
1489+qmail.h alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \
1490+env.h wait.h fd.h fork.h str.h uint64.h \
1491+auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
1492+       ./compile qmail-dk.c
1493+
1494+dktest: load dktest.o scan_ulong.o dktrace.o \
1495+dns.o strsalloc.o ip.o error.o ipalloc.o fmt_ulong.o \
1496+scan_xlong.o socket_v4mappedprefix.o socket_v6any.o \
1497+case_diffs.o case_diffb.o fmt_str.o stralloc.a alloc.a str.a \
1498+$(LIBDOMAINKEYS)/libdomainkeys.a dns.lib
1499+       ./load dktest scan_ulong.o dktrace.o \
1500+       dns.o strsalloc.o ip.o error.o ipalloc.o fmt_ulong.o \
1501+       scan_xlong.o socket_v4mappedprefix.o socket_v6any.o \
1502+       case_diffs.o case_diffb.o fmt_str.o stralloc.a alloc.a str.a \
1503+       $(LIBDOMAINKEYS)/libdomainkeys.a -lcrypto `cat dns.lib`
1504+
1505+dktest.o: compile dktest.c domainkeys.h conf-domainkeys
1506+       ./compile `grep -h -v "^#" conf-domainkeys` dktest.c
1507+
1508+dktest.8: dktest.9
1509+       cat dktest.9 \
1510+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1511+       > dktest.8
1512+
1513+dktrace.o: compile dktrace.c dktrace.h str.h case.h conf-domainkeys
1514+       ./compile `grep -h -v "^#" conf-domainkeys` dktrace.c
1515+
1516+qmail-dkim: \
1517+load qmail-dkim.o triggerpull.o fmtqfn.o now.o date822fmt.o \
1518+subgetopt.o MakeArgs.o dkimdns.o datetime.a seek.a ndelay.a \
1519+open.a sig.a alloc.a substdio.a error.a \
1520+str.a case.a fs.a auto_qmail.o auto_split.o auto_uids.o fd.a wait.a \
1521+$(LIBDOMAINKEYS)/libdomainkeys.a env.a getln.a control.o stralloc.a dns.lib libdkim.a
1522+       g++ -o qmail-dkim qmail-dkim.o triggerpull.o dkimdns.o fmtqfn.o now.o \
1523+       subgetopt.o MakeArgs.o date822fmt.o datetime.a seek.a ndelay.a \
1524+       open.a sig.a substdio.a error.a fs.a auto_qmail.o \
1525+       auto_split.o auto_uids.o fd.a wait.a \
1526+       $(LIBDOMAINKEYS)/libdomainkeys.a -lcrypto env.a control.o open.a getln.a \
1527+       stralloc.a alloc.a substdio.a str.a case.a libdkim.a `cat dns.lib`
1528+
1529+qmail-dkim.o: \
1530+compile qmail-dkim.c readwrite.h sig.h exit.h open.h seek.h fmt.h \
1531+qmail.h alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \
1532+sgetopt.h env.h wait.h fd.h fork.h str.h dkim.h \
1533+auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
1534+       ./compile -DHAVE_CONFIG_H qmail-dkim.c
1535+
1536 qmail-getpw: \
1537 load qmail-getpw.o case.a substdio.a error.a str.a fs.a auto_break.o \
1538 auto_usera.o
1539        ./load qmail-getpw case.a substdio.a error.a str.a fs.a \
1540-       auto_break.o auto_usera.o
1541+       auto_break.o auto_usera.o
1542 
1543 qmail-getpw.0: \
1544 qmail-getpw.8
1545@@ -1125,6 +1299,28 @@
1546        | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1547        > qmail-getpw.8
1548 
1549+qmail-dkim.0: qmail-dkim.8
1550+       nroff -man qmail-dkim.8 > qmail-dkim.0
1551+qmail-dkim.8: qmail-dkim.9
1552+       cat qmail-dkim.9 \
1553+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1554+       > qmail-dkim.8
1555+
1556+dkim.8: dkim.9
1557+       cat dkim.9 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1558+       > dkim.8
1559+
1560+dk-filter.0: dk-filter.8
1561+       nroff -man dk-filter.8 > dk-filter.0
1562+dk-filter.8: dk-filter.9
1563+       cat dk-filter.9 \
1564+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1565+       > dk-filter.8
1566+
1567+dknewkey: dknewkey.sh warn-auto.sh
1568+       rm -f dknewkey
1569+       cat warn-auto.sh dknewkey.sh > dknewkey
1570+
1571 qmail-getpw.o: \
1572 compile qmail-getpw.c readwrite.h substdio.h subfd.h substdio.h \
1573 error.h exit.h byte.h str.h case.h fmt.h auto_usera.h auto_break.h \
1574@@ -1136,15 +1332,16 @@
1575        nroff -man qmail-header.5 > qmail-header.0
1576 
1577 qmail-inject: \
1578-load qmail-inject.o headerbody.o hfield.o newfield.o quote.o now.o \
1579+load qmail-inject.o rcpthosts.o cdb.a srs.o headerbody.o hfield.o newfield.o quote.o now.o \
1580 control.o date822fmt.o constmap.o qmail.o case.a fd.a wait.a open.a \
1581 getln.a sig.a getopt.a datetime.a token822.o env.a stralloc.a alloc.a \
1582 substdio.a error.a str.a fs.a auto_qmail.o
1583-       ./load qmail-inject headerbody.o hfield.o newfield.o \
1584+       ./load qmail-inject rcpthosts.o cdb.a srs.o headerbody.o hfield.o newfield.o \
1585        quote.o now.o control.o date822fmt.o constmap.o qmail.o \
1586        case.a fd.a wait.a open.a getln.a sig.a getopt.a datetime.a \
1587        token822.o env.a stralloc.a alloc.a substdio.a error.a \
1588-       str.a fs.a auto_qmail.o
1589+       str.a fs.a auto_qmail.o \
1590+       -I/usr/local/include -L/usr/local/lib -lsrs2
1591 
1592 qmail-inject.0: \
1593 qmail-inject.8
1594@@ -1171,15 +1368,20 @@
1595        > qmail-limits.7
1596 
1597 qmail-local: \
1598-load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \
1599+load qmail-local.o constmap.o control.o rcpthosts.o srs.o cdb.a qmail.o quote.o now.o gfrom.o myctime.o \
1600 slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \
1601 wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \
1602-fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib
1603-       ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \
1604+fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib maildirquota.o \
1605+maildirgetquota.o maildiropen.o maildirparsequota.o overmaildirquota.o \
1606+strtimet.o strpidt.o
1607+       ./load qmail-local constmap.o control.o rcpthosts.o srs.o cdb.a \
1608+       qmail.o quote.o now.o gfrom.o myctime.o \
1609        slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \
1610        lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \
1611        substdio.a error.a str.a fs.a datetime.a auto_qmail.o \
1612-       auto_patrn.o  `cat socket.lib`
1613+       auto_patrn.o  `cat socket.lib` maildirquota.o maildirgetquota.o \
1614+       maildiropen.o maildirparsequota.o overmaildirquota.o strtimet.o strpidt.o \
1615+       -I/usr/local/include -L/usr/local/lib -lsrs2
1616 
1617 qmail-local.0: \
1618 qmail-local.8
1619@@ -1200,11 +1402,11 @@
1620 qmail-lspawn: \
1621 load qmail-lspawn.o spawn.o prot.o slurpclose.o coe.o sig.a wait.a \
1622 case.a cdb.a fd.a open.a stralloc.a alloc.a substdio.a error.a str.a \
1623-fs.a auto_qmail.o auto_uids.o auto_spawn.o
1624+fs.a auto_qmail.o auto_uids.o auto_spawn.o envread.o str_diffn.o
1625        ./load qmail-lspawn spawn.o prot.o slurpclose.o coe.o \
1626        sig.a wait.a case.a cdb.a fd.a open.a stralloc.a alloc.a \
1627        substdio.a error.a str.a fs.a auto_qmail.o auto_uids.o \
1628-       auto_spawn.o
1629+       auto_spawn.o envread.o str_diffn.o
1630 
1631 qmail-lspawn.0: \
1632 qmail-lspawn.8
1633@@ -1213,9 +1415,22 @@
1634 qmail-lspawn.o: \
1635 compile qmail-lspawn.c fd.h wait.h prot.h substdio.h stralloc.h \
1636 gen_alloc.h scan.h exit.h fork.h error.h cdb.h uint32.h case.h \
1637-slurpclose.h auto_qmail.h auto_uids.h qlx.h
1638+slurpclose.h auto_qmail.h auto_uids.h qlx.h env.h
1639        ./compile qmail-lspawn.c
1640 
1641+qmail-newmvrt: \
1642+load qmail-newmvrt.o cdbmss.o getln.a open.a cdbmake.a seek.a case.a \
1643+stralloc.a alloc.a strerr.a substdio.a error.a str.a auto_qmail.o
1644+       ./load qmail-newmvrt cdbmss.o getln.a open.a cdbmake.a \
1645+       seek.a case.a stralloc.a alloc.a strerr.a substdio.a \
1646+       error.a str.a auto_qmail.o
1647+
1648+qmail-newmvrt.o: \
1649+compile qmail-newmvrt.c strerr.h stralloc.h gen_alloc.h substdio.h \
1650+getln.h exit.h readwrite.h open.h auto_qmail.h cdbmss.h cdbmake.h \
1651+uint32.h substdio.h
1652+       ./compile qmail-newmvrt.c
1653+
1654 qmail-newmrh: \
1655 load qmail-newmrh.o cdbmss.o getln.a open.a cdbmake.a seek.a case.a \
1656 stralloc.a alloc.a strerr.a substdio.a error.a str.a auto_qmail.o
1657@@ -1269,11 +1484,13 @@
1658 qmail-pop3d: \
1659 load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \
1660 maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \
1661-stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib
1662+stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib maildirquota.o \
1663+maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o
1664        ./load qmail-pop3d commands.o case.a timeoutread.o \
1665        timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \
1666        open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \
1667-       fs.a  `cat socket.lib`
1668+       fs.a  `cat socket.lib` maildirquota.o maildirgetquota.o \
1669+    maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o
1670 
1671 qmail-pop3d.0: \
1672 qmail-pop3d.8
1673@@ -1419,13 +1636,13 @@
1674        nroff -man qmail-qstat.8 > qmail-qstat.0
1675 
1676 qmail-queue: \
1677-load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \
1678-datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \
1679-str.a fs.a auto_qmail.o auto_split.o auto_uids.o
1680+load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o wildmat.o qregex.o \
1681+datetime.a seek.a case.a ndelay.a open.a sig.a getln.a stralloc.a alloc.a substdio.a error.a \
1682+env.a control.o constmap.o str.a fs.a auto_qmail.o auto_split.o auto_uids.o
1683        ./load qmail-queue triggerpull.o fmtqfn.o now.o \
1684-       date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \
1685-       alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
1686-       auto_split.o auto_uids.o
1687+        date822fmt.o wildmat.o qregex.o env.a control.o constmap.o datetime.a case.a seek.a ndelay.a open.a sig.a \
1688+        getln.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o auto_uids.o
1689+
1690 
1691 qmail-queue.0: \
1692 qmail-queue.8
1693@@ -1439,14 +1656,18 @@
1694 
1695 qmail-remote: \
1696 load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \
1697-timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \
1698+timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o strsalloc.o ipme.o quote.o \
1699 ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \
1700-substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib
1701+substdio.a error.a str.a fs.a auto_qmail.o \
1702+base64.o md5c.o hmac_md5.o \
1703+dns.lib socket.lib
1704        ./load qmail-remote control.o constmap.o timeoutread.o \
1705        timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
1706-       ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
1707+        tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \
1708+       ipalloc.o strsalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
1709        lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
1710-       str.a fs.a auto_qmail.o  `cat dns.lib` `cat socket.lib`
1711+        base64.o md5c.o hmac_md5.o \
1712+        str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib`
1713 
1714 qmail-remote.0: \
1715 qmail-remote.8
1716@@ -1455,7 +1676,7 @@
1717 qmail-remote.o: \
1718 compile qmail-remote.c sig.h stralloc.h gen_alloc.h substdio.h \
1719 subfd.h substdio.h scan.h case.h error.h auto_qmail.h control.h dns.h \
1720-alloc.h quote.h ip.h ipalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h \
1721+alloc.h quote.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h \
1722 gen_alloc.h gen_allocdefs.h str.h now.h datetime.h exit.h constmap.h \
1723 tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h
1724        ./compile qmail-remote.c
1725@@ -1463,11 +1684,11 @@
1726 qmail-rspawn: \
1727 load qmail-rspawn.o spawn.o tcpto_clean.o now.o coe.o sig.a open.a \
1728 seek.a lock.a wait.a fd.a stralloc.a alloc.a substdio.a error.a str.a \
1729-auto_qmail.o auto_uids.o auto_spawn.o
1730+auto_qmail.o auto_uids.o auto_spawn.o envread.o str_diffn.o
1731        ./load qmail-rspawn spawn.o tcpto_clean.o now.o coe.o \
1732        sig.a open.a seek.a lock.a wait.a fd.a stralloc.a alloc.a \
1733        substdio.a error.a str.a auto_qmail.o auto_uids.o \
1734-       auto_spawn.o
1735+       auto_spawn.o  envread.o str_diffn.o
1736 
1737 qmail-rspawn.0: \
1738 qmail-rspawn.8
1739@@ -1475,31 +1696,33 @@
1740 
1741 qmail-rspawn.o: \
1742 compile qmail-rspawn.c fd.h wait.h substdio.h exit.h fork.h error.h \
1743-tcpto.h
1744+tcpto.h env.h
1745        ./compile qmail-rspawn.c
1746 
1747 qmail-send: \
1748-load qmail-send.o qsutil.o control.o constmap.o newfield.o prioq.o \
1749+load qmail-send.o rcpthosts.o cdb.a srs.o qsutil.o control.o constmap.o newfield.o prioq.o \
1750 trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \
1751 datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \
1752 lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
1753-auto_split.o env.a
1754-       ./load qmail-send qsutil.o control.o constmap.o newfield.o \
1755+auto_split.o env.a auto_spawn.o
1756+       ./load qmail-send rcpthosts.o cdb.a srs.o qsutil.o control.o constmap.o newfield.o \
1757        prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \
1758        qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \
1759        wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \
1760-       substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a
1761+       substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a auto_spawn.o \
1762+       -I/usr/local/include -L/usr/local/lib -lsrs2
1763 
1764 qmail-send.0: \
1765 qmail-send.8
1766        nroff -man qmail-send.8 > qmail-send.0
1767 
1768 qmail-send.8: \
1769-qmail-send.9 conf-break conf-spawn
1770+qmail-send.9 conf-break conf-spawn conf-channels
1771        cat qmail-send.9 \
1772        | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1773        | sed s}BREAK}"`head -1 conf-break`"}g \
1774        | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1775+       | sed s}CHANNELS}"`head -1 conf-channels`"}g \
1776        > qmail-send.8
1777 
1778 qmail-send.o: \
1779@@ -1508,8 +1731,8 @@
1780 substdio.h alloc.h error.h stralloc.h gen_alloc.h str.h byte.h fmt.h \
1781 scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \
1782 qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \
1783-fmtqfn.h readsubdir.h direntry.h
1784-       ./compile qmail-send.c
1785+fmtqfn.h readsubdir.h direntry.h channels.h auto_qmail.h
1786+       ./compile $(DEFINES) qmail-send.c
1787 
1788 qmail-showctl: \
1789 load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \
1790@@ -1528,21 +1751,26 @@
1791 compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \
1792 str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \
1793 auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \
1794-auto_split.h
1795+auto_split.h spf.h
1796        ./compile qmail-showctl.c
1797 
1798 qmail-smtpd: \
1799 load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
1800-timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
1801-date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
1802-open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
1803-fs.a auto_qmail.o socket.lib
1804-       ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
1805-       timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
1806-       received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
1807-       datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
1808-       alloc.a substdio.a error.a str.a fs.a auto_qmail.o  `cat \
1809-       socket.lib`
1810+strerr.a wildmat.o qregex.o \
1811+timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o constmap.o \
1812+received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a fd.a wait.a \
1813+datetime.a getln.a open.a sig.a case.a env.a stralloc.a alloc.a substdio.a \
1814+error.a str.a fs.a auto_qmail.o base64.o socket.lib dns.lib lock.a policy.o \
1815+$(SMTPD_CHKUSER_OBJ)
1816+       ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) rcpthosts.o commands.o timeoutread.o \
1817+       strerr.a wildmat.o qregex.o \
1818+       timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o \
1819+       tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \
1820+       constmap.o received.o date822fmt.o now.o qmail.o spf.o cdb.a \
1821+       fd.a wait.a datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
1822+       alloc.a substdio.a error.a strerr.a str.a fs.a auto_qmail.o base64.o policy.o \
1823+       $(VPOPMAIL_LIBS) \
1824+       `cat socket.lib`
1825 
1826 qmail-smtpd.0: \
1827 qmail-smtpd.8
1828@@ -1551,9 +1779,10 @@
1829 qmail-smtpd.o: \
1830 compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \
1831 substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
1832-error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
1833-substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
1834-exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
1835+error.h ipme.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ip.h qmail.h \
1836+substdio.h strerr.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
1837+exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h spf.h dns.h base64.h \
1838+cdb.h chkuser.h
1839        ./compile qmail-smtpd.c
1840 
1841 qmail-start: \
1842@@ -1573,8 +1802,8 @@
1843        > qmail-start.8
1844 
1845 qmail-start.o: \
1846-compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h
1847-       ./compile qmail-start.c
1848+compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h channels.h
1849+       ./compile $(DEFINES) qmail-start.c
1850 
1851 qmail-tcpok: \
1852 load qmail-tcpok.o open.a lock.a strerr.a substdio.a error.a str.a \
1853@@ -1606,6 +1835,20 @@
1854 fmt.h ip.h lock.h error.h exit.h datetime.h now.h datetime.h
1855        ./compile qmail-tcpto.c
1856 
1857+qmail-todo: \
1858+load qmail-todo.o control.o constmap.o trigger.o fmtqfn.o now.o \
1859+readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a alloc.a \
1860+substdio.a error.a str.a fs.a auto_qmail.o auto_split.o
1861+       ./load qmail-todo control.o constmap.o trigger.o fmtqfn.o now.o \
1862+       readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a \
1863+       alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o
1864+
1865+qmail-todo.o: \
1866+compile alloc.h auto_qmail.h byte.h constmap.h control.h direntry.h error.h \
1867+exit.h fmt.h fmtqfn.h getln.h open.h ndelay.h now.h readsubdir.h readwrite.h \
1868+scan.h select.h str.h stralloc.h substdio.h trigger.h channels.h
1869+       ./compile $(DEFINES) qmail-todo.c
1870+
1871 qmail-upq: \
1872 warn-auto.sh qmail-upq.sh conf-qmail conf-break conf-split
1873        cat warn-auto.sh qmail-upq.sh \
1874@@ -1639,10 +1882,10 @@
1875 qreceipt: \
1876 load qreceipt.o headerbody.o hfield.o quote.o token822.o qmail.o \
1877 getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a substdio.a error.a \
1878-str.a auto_qmail.o
1879+str.a auto_qmail.o scan_ulong.o
1880        ./load qreceipt headerbody.o hfield.o quote.o token822.o \
1881        qmail.o getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a \
1882-       substdio.a error.a str.a auto_qmail.o
1883+       substdio.a error.a str.a auto_qmail.o scan_ulong.o
1884 
1885 qreceipt.0: \
1886 qreceipt.1
1887@@ -1779,7 +2022,7 @@
1888 qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
1889 qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
1890 qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \
1891-dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \
1892+dnsip.c dnsmxip.c dnsptr.c dnstxt.c hostname.c ipmeprint.c ipmetest.c tcp-env.c \
1893 sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \
1894 except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \
1895 maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \
1896@@ -1813,8 +2056,9 @@
1897 trywaitp.c sig.h sig_alarm.c sig_block.c sig_catch.c sig_pause.c \
1898 sig_pipe.c sig_child.c sig_term.c sig_hup.c sig_misc.c sig_bug.c \
1899 trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \
1900-byte_copy.c byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h \
1901-str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \
1902+byte_copy.c byte_cr.c byte_cspn.c byte_diff.c byte_rchr.c byte_rcspn.c \
1903+byte_zero.c str.h spf.c spf.h spfquery.c \
1904+str_chr.c str_cpy.c str_cpyb.c str_diff.c str_diffn.c str_len.c str_rchr.c \
1905 str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \
1906 getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \
1907 subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \
1908@@ -1824,10 +2068,11 @@
1909 headerbody.h headerbody.c token822.h token822.c control.h control.c \
1910 datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \
1911 date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \
1912-ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
1913+ipalloc.h strsalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
1914 ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \
1915 prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \
1916-maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c
1917+maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c \
1918+update_tmprsadh
1919        shar -m `cat FILES` > shar
1920        chmod 400 shar
1921 
1922@@ -1897,6 +2142,23 @@
1923        ./chkspawn
1924        ./compile spawn.c
1925 
1926+spf.o: \
1927+compile spf.c stralloc.h gen_alloc.h alloc.h ipme.h ip.h ipalloc.h \
1928+strsalloc.h str.h fmt.h scan.h byte.h now.h case.h
1929+       ./compile spf.c
1930+
1931+spfquery: \
1932+load spfquery.o spf.o ip.o getln.o getln2.o open_read.o ipme.o ipalloc.o strsalloc.o \
1933+now.o dns.o datetime.a stralloc.a alloc.a str.a substdio.a error.a fs.a case.a dns.lib
1934+       ./load spfquery spf.o ip.o getln.o getln2.o open_read.o ipme.o ipalloc.o strsalloc.o \
1935+       now.o dns.o datetime.a stralloc.a alloc.a str.a substdio.a \
1936+       case.a error.a fs.a `cat dns.lib` `cat socket.lib`
1937+
1938+spfquery.o: \
1939+compile spfquery.c substdio.h subfd.h stralloc.h gen_alloc.h alloc.h \
1940+spf.h exit.h
1941+       ./compile spfquery.c
1942+
1943 splogger: \
1944 load splogger.o substdio.a error.a str.a fs.a syslog.lib socket.lib
1945        ./load splogger substdio.a error.a str.a fs.a  `cat \
1946@@ -1911,13 +2173,33 @@
1947 scan.h fmt.h
1948        ./compile splogger.c
1949 
1950+srs.o: \
1951+compile srs.c srs.h auto_qmail.h stralloc.h
1952+       ./compile srs.c
1953+
1954+
1955+srsfilter: \
1956+load srsfilter.o srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \
1957+       cdb.a case.a open.a stralloc.a alloc.a getln.a fd.a wait.a sig.a \
1958+       env.a substdio.a error.a str.a fs.a auto_qmail.o
1959+       ./load srsfilter srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \
1960+       cdb.a case.a open.a stralloc.a alloc.a getln.a fd.a wait.a sig.a \
1961+       env.a substdio.a error.a str.a fs.a auto_qmail.o \
1962+       -I/usr/local/include -L/usr/local/lib -lsrs2
1963+
1964+srsfilter.o: \
1965+compile srsfilter.c sig.h readwrite.h exit.h env.h qmail.h substdio.h strerr.h \
1966+substdio.h fmt.h stralloc.h srs.h
1967+       ./compile srsfilter.c
1968+
1969+
1970 str.a: \
1971-makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \
1972-str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \
1973-byte_cr.o byte_zero.o
1974-       ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \
1975-       str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \
1976-       byte_diff.o byte_copy.o byte_cr.o byte_zero.o
1977+makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o str_chr.o \
1978+str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o byte_rcspn.o \
1979+byte_diff.o byte_copy.o byte_cr.o byte_zero.o
1980+       ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o \
1981+       str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o \
1982+       byte_rcspn.o byte_diff.o byte_copy.o byte_cr.o byte_zero.o
1983 
1984 str_chr.o: \
1985 compile str_chr.c str.h
1986@@ -1927,6 +2209,10 @@
1987 compile str_cpy.c str.h
1988        ./compile str_cpy.c
1989 
1990+str_cpyb.o: \
1991+compile str_cpyb.c str.h
1992+       ./compile str_cpyb.c
1993+
1994 str_diff.o: \
1995 compile str_diff.c str.h
1996        ./compile str_diff.c
1997@@ -2006,6 +2292,11 @@
1998 compile strerr_sys.c error.h strerr.h
1999        ./compile strerr_sys.c
2000 
2001+strsalloc.o: \
2002+compile strsalloc.c alloc.h gen_allocdefs.h stralloc.h strsalloc.h \
2003+gen_alloc.h
2004+       ./compile strsalloc.c
2005+
2006 subfderr.o: \
2007 compile subfderr.c readwrite.h substdio.h subfd.h substdio.h
2008        ./compile subfderr.c
2009@@ -2066,11 +2357,11 @@
2010 
2011 tcp-env: \
2012 load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \
2013-timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \
2014-stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
2015+timeoutconn.o ip.o ipalloc.o strsalloc.o case.a ndelay.a sig.a env.a \
2016+getopt.a stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
2017        ./load tcp-env dns.o remoteinfo.o timeoutread.o \
2018-       timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \
2019-       sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
2020+       timeoutwrite.o timeoutconn.o ip.o ipalloc.o strsalloc.o case.a \
2021+       ndelay.a sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
2022        str.a fs.a  `cat dns.lib` `cat socket.lib`
2023 
2024 tcp-env.0: \
2025@@ -2108,6 +2399,19 @@
2026 compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h
2027        ./compile timeoutwrite.c
2028 
2029+qmail-smtpd: tls.o ssl_timeoutio.o ndelay.a
2030+qmail-remote: tls.o ssl_timeoutio.o
2031+qmail-smtpd.o: tls.h ssl_timeoutio.h
2032+qmail-remote.o: tls.h ssl_timeoutio.h
2033+
2034+tls.o: \
2035+compile tls.c exit.h error.h
2036+       ./compile tls.c
2037+
2038+ssl_timeoutio.o: \
2039+compile ssl_timeoutio.c ssl_timeoutio.h select.h error.h ndelay.h
2040+       ./compile ssl_timeoutio.c
2041+
2042 token822.o: \
2043 compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \
2044 gen_alloc.h gen_allocdefs.h
2045@@ -2139,3 +2443,173 @@
2046 wait_pid.o: \
2047 compile wait_pid.c error.h haswaitp.h
2048        ./compile wait_pid.c
2049+MakeArgs.o: compile MakeArgs.c alloc.h str.h alloc.h stralloc.h
2050+       ./compile MakeArgs.c
2051+
2052+spawn-filter: \
2053+load spawn-filter.o auto_qmail.o \
2054+fmt_ulong.o scan_ulong.o control.o open_read.o wildmat.o qregex.o MakeArgs.o \
2055+case_lowerb.o constmap.o byte_chr.o byte_cr.o case_diffb.o \
2056+error.a env.a stralloc.a wait.a strerr.a str.a getln.a substdio.a alloc.a
2057+       ./load spawn-filter \
2058+       fmt_ulong.o scan_ulong.o control.o open_read.o wildmat.o qregex.o MakeArgs.o \
2059+       case_lowerb.o constmap.o byte_chr.o byte_cr.o case_diffb.o auto_qmail.o \
2060+       error.a env.a stralloc.a wait.a strerr.a str.a getln.a substdio.a alloc.a
2061+
2062+spawn-filter.o: \
2063+compile spawn-filter.c fmt.h str.h strerr.h env.h substdio.h stralloc.h error.h \
2064+wait.h qregex.h
2065+       ./compile spawn-filter.c
2066+
2067+qregex.o: \
2068+compile qregex.c qregex.h case.h stralloc.h constmap.h substdio.h byte.h env.h
2069+       ./compile qregex.c
2070+
2071+wildmat.o: \
2072+compile wildmat.c
2073+       ./compile wildmat.c
2074+
2075+spawn-filter.0: \
2076+spawn-filter.8
2077+       nroff -man spawn-filter.8 > spawn-filter.0
2078+
2079+spawn-filter.8: \
2080+spawn-filter.9
2081+       cat spawn-filter.9 \
2082+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
2083+       > spawn-filter.8
2084+
2085+str_cspn.o: \
2086+compile str.h str_cspn.c
2087+       ./compile str_cspn.c
2088+
2089+mess822_ok.o: \
2090+compile mess822_ok.c uint64.h
2091+
2092+surblfilter.0: surblfilter.8
2093+       nroff -man surblfilter.8 > surblfilter.0
2094+
2095+surblfilter.8: surblfilter.9
2096+       cat surblfilter.9 \
2097+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
2098+       > surblfilter.8
2099+
2100+surblfilter: \
2101+load surblfilter.o envread.o strerr_die.o strerr_sys.o \
2102+control.o alloc.o alloc_re.o error.o \
2103+error_str.o auto_qmail.o \
2104+case_startb.o byte_diff.o str_cspn.o \
2105+byte_copy.o byte_chr.o byte_rchr.o byte_cr.o \
2106+getln.o getln2.o open_read.o str_len.o str_diffn.o \
2107+str_cpy.o str_chr.o scan_xlong.o \
2108+now.o scan_ulong.o mess822_ok.o constmap.o \
2109+ip.o strsalloc.o dns.o ipalloc.o fmt_str.o fmt_ulong.o \
2110+socket_v6any.o socket_v4mappedprefix.o \
2111+sgetopt.o subgetopt.o base64sub.o \
2112+case_diffb.o stralloc.a substdio.a
2113+       ./load surblfilter envread.o strerr_die.o strerr_sys.o \
2114+       control.o alloc.o alloc_re.o error.o \
2115+       error_str.o auto_qmail.o \
2116+       case_startb.o byte_diff.o str_cspn.o \
2117+       byte_copy.o byte_chr.o byte_rchr.o byte_cr.o \
2118+       getln.o getln2.o open_read.o str_len.o str_diffn.o \
2119+       str_cpy.o str_chr.o scan_xlong.o \
2120+       now.o scan_ulong.o mess822_ok.o constmap.o \
2121+       ip.o strsalloc.o dns.o ipalloc.o fmt_str.o fmt_ulong.o \
2122+       socket_v6any.o socket_v4mappedprefix.o \
2123+       sgetopt.o subgetopt.o base64sub.o \
2124+       case_diffb.o stralloc.a substdio.a -lresolv
2125+
2126+surblfilter.o: \
2127+compile surblfilter.c alloc.h error.h str.h case.h \
2128+constmap.h auto_qmail.h stralloc.h env.h control.h \
2129+strerr.h substdio.h getln.h byte.h dns.h ip.h \
2130+ipalloc.h mess822.h scan.h subgetopt.h uint64.h \
2131+base64.h
2132+       ./compile surblfilter.c
2133+
2134+surblqueue: \
2135+surblqueue.sh conf-qmail
2136+       cat surblqueue.sh \
2137+       | sed s}QMAIL}"`head -1 conf-qmail`"}g \
2138+       > surblqueue
2139+       chmod 755 surblqueue
2140+
2141+hastai.h: \
2142+trytai.c compile load
2143+       (( ./compile trytai.c && ./load trytai -ltai) >/dev/null \
2144+       2>&1 \
2145+       && echo \#define HASTAI 1 || exit 0 ) > hastai.h
2146+       rm -f trytai.o trytai
2147+
2148+uint64.h: \
2149+tryulong64.c compile load uint64.h1 uint64.h2
2150+       ( ( ./compile tryulong64.c && ./load tryulong64 && \
2151+       ./tryulong64 ) >/dev/null 2>&1 \
2152+       && cat uint64.h1 || cat uint64.h2 ) > uint64.h
2153+       rm -f tryulong64.o tryulong64
2154+
2155+base64sub.o: \
2156+compile base64sub.c base64.h stralloc.h substdio.h str.h
2157+       ./compile base64sub.c
2158+
2159+dk-filter: \
2160+warn-auto.sh dk-filter.sh conf-qmail
2161+       cat warn-auto.sh dk-filter.sh \
2162+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
2163+       > dk-filter
2164+
2165+DKIMHDRS = dkim.h dkimdns.h dkimbase.h dkimsign.h dkimverify.h time_t_size.h
2166+DKIMSRCS = dkimfuncs.cpp dkimbase.cpp
2167+DKIMOBJS = $(DKIMSRCS:.cpp=.o)
2168+dkim: libdkim.a dkim.o dkimdns.o
2169+       g++ -o dkim $(LFLAGS) -L. dkim.o dkimdns.o libdkim.a `cat dns.lib` -lcrypto
2170+
2171+time_t_size.h: time_t_size.c compile load
2172+       (./compile time_t_size.c && ./load time_t_size && \
2173+               ./time_t_size) > time_t_size.h
2174+       rm -f time_t_size.o time_t_size
2175+
2176+dkimfuncs.o: dkimfuncs.cpp time_t_size.h
2177+       g++ -DHAVE_EVP_SHA256 -c dkimfuncs.cpp
2178+
2179+dkimverify.o: dkim.h dkimdns.h dkimverify.h dkimverify.cpp time_t_size.h
2180+       g++ -DHAVE_EVP_SHA256 -c dkimverify.cpp
2181+
2182+dkimsign.o: dkim.h dkimsign.h dkimsign.cpp
2183+       g++ -DHAVE_EVP_SHA256 -c dkimsign.cpp
2184+
2185+dkim.o: dkim.c $(DKIMHDRS)
2186+       g++ -DHAVE_EVP_SHA256 -I. -DHAVE_EVP_SHA256 -c dkim.c
2187+
2188+libdkim.a: $(DKIMOBJS) dkimverify.o dkimsign.o makelib time_t_size.h
2189+       rm -f libdkim.a
2190+       ./makelib libdkim.a $(DKIMOBJS) dkimsign.o dkimverify.o
2191+.cpp.o:
2192+       g++ -I. -DHAVE_EVP_SHA256 $(CFLAGS) $(INCL) -c $<
2193+
2194+cert cert-req: \
2195+Makefile-cert
2196+       @$(MAKE) -sf $< $@
2197+
2198+Makefile-cert: \
2199+conf-qmail conf-users conf-groups Makefile-cert.mk
2200+       @cat Makefile-cert.mk \
2201+       | sed s}QMAIL}"`head -1 conf-qmail`"}g \
2202+       > $@
2203+
2204+update_tmprsadh: \
2205+conf-qmail conf-users conf-groups update_tmprsadh.sh
2206+       @cat update_tmprsadh.sh\
2207+       | sed s}UGQMAILD}"`head -2 conf-users|tail -1`:`head -1 conf-groups`"}g \
2208+       | sed s}QMAIL}"`head -1 conf-qmail`"}g \
2209+       > $@
2210+       chmod 755 update_tmprsadh
2211+
2212+tmprsadh: \
2213+update_tmprsadh
2214+       echo "Creating new temporary RSA and DH parameters"
2215+       ./update_tmprsadh
2216+
2217+policy.o: policy.c policy.h conf-policy
2218+       ./compile policy.c `head -1 conf-policy`
2219diff -ruN ../netqmail-1.06-original/Makefile-cert.mk netqmail-1.06/Makefile-cert.mk
2220--- ../netqmail-1.06-original/Makefile-cert.mk  1970-01-01 01:00:00.000000000 +0100
2221+++ netqmail-1.06/Makefile-cert.mk      2019-02-27 20:57:13.379025191 +0100
2222@@ -0,0 +1,21 @@
2223+cert-req: req.pem
2224+cert cert-req: QMAIL/control/clientcert.pem
2225+       @:
2226+
2227+QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem
2228+       ln -s $< $@
2229+
2230+QMAIL/control/servercert.pem:
2231+       PATH=$$PATH:/usr/local/ssl/bin \
2232+               openssl req -new -x509 -nodes -days 366 -out $@ -keyout $@
2233+       chmod 640 $@
2234+       chown `head -2 conf-users | tail -1`:`head -1 conf-groups` $@
2235+
2236+req.pem:
2237+       PATH=$$PATH:/usr/local/ssl/bin openssl req \
2238+               -new -nodes -out $@ -keyout QMAIL/control/servercert.pem
2239+       chmod 640 QMAIL/control/servercert.pem
2240+       chown `head -2 conf-users | tail -1`:`head -1 conf-groups` QMAIL/control/servercert.pem
2241+       @echo
2242+       @echo "Send req.pem to your CA to obtain signed_req.pem, and do:"
2243+       @echo "cat signed_req.pem >> QMAIL/control/servercert.pem"
2244diff -ruN ../netqmail-1.06-original/README.PATCH netqmail-1.06/README.PATCH
2245--- ../netqmail-1.06-original/README.PATCH      1970-01-01 01:00:00.000000000 +0100
2246+++ netqmail-1.06/README.PATCH  2020-07-29 21:22:44.084295087 +0200
2247@@ -0,0 +1,547 @@
2248+v. 2020.07.29
2249+Combined patch for netqmail-1.06 by Roberto Puzzanghera [roberto dot puzzanghera at sagredo dot eu]
2250+More info at https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html
2251+==========================================================================================================
2252+
2253+= This patch puts together
2254+* Erwin Hoffmann's qmail-authentication patch v. 0.8.3, which updates the patches provided by
2255+  Krysztof Dabrowski and Bjoern Kalkbrenner.
2256+  It provides cram-md5, login, plain authentication support for qmail-smtpd and qmail-remote.
2257+  http://www.fehcom.de/qmail/smtpauth.html##PATCHES
2258+* Frederik Vermeulen's qmail-tls patch v. 20200107
2259+  implements SSL or TLS encrypted and authenticated SMTP.
2260+  http://inoa.net/qmail-tls/
2261+  The file update_tmprsadh was modified to chown all .pem files to vpopmail.
2262+* Marcel Telka's force-tls patch v. 2016.05.15
2263+  optionally gets qmail to require TLS before authentication to improve security.
2264+  You have to declare FORCETLS=0 if you want to allow the auth without TLS
2265+  https://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/roberto-netqmail-1.06_force-tls.patch-2012.10.28
2266+* Antonio Nati's chkuser patch v. 2.0.9
2267+  performs, among the other things, a check for the existence of recipients during the SMTP conversation,
2268+  bouncing emails of fake senders.
2269+  http://www.interazioni.it/opensource/chkuser/
2270+* Flavio Curti's qmail-queue-custom-error patch
2271+  enables simscan and qmail-dkim to return the appropriate message for each e-mail it refuses to deliver.
2272+  https://no-way.org/uploads/qmail-error/
2273+* Christophe Saout's qmail-SPF rc5 patch
2274+  Modified by Manvendra Bhangui to make it IPv4-mapped IPv6 addresses compliant.
2275+  checks incoming mails inside the SMTP daemon, add Received-SPF lines and optionally block undesired transfers.
2276+  http://www.saout.de/misc/spf/
2277+* Marcelo Coelho's qmail-SRS patch
2278+  implements Sender Rewriting Scheme fixing SPF break upon email forwarding.
2279+  http://www.mco2.com.br/opensource/qmail/srs/
2280+* Christopher K. Davis' oversize dns patch
2281+  enables qmail to handle large DNS packets.
2282+  http://www.ckdhr.com/ckd/qmail-103.patch
2283+* Jul's reread-concurrency v.2 patch
2284+  rereads control/concurrencylocal and control/concurrencyremote files when qmail-send receives a HUP signal.
2285+  http://js.hu/package/qmail/index.html
2286+* Johannes Erdfelt's Big Concurrency patch
2287+  sets the spawn limit above 255
2288+  http://qmail.org/big-concurrency.patch
2289+* Mihai Secasiu's Big Concurrency fix v.1.0 patch
2290+  fixes a compiler error if you set concurrency higher than 509 in conf-spawn.
2291+  http://patchlog.com/linux/qmail-big-concurrency/
2292+* Bill Shupp's netqmail-maildir++.patch
2293+  adds maildirquota support to qmail-pop3d and qmail-local.
2294+  Fixed a bug where the filesize part of the S=<filesize> component of the Maildir++ compatible filename
2295+  is wrong (tx MG). More info here:
2296+  https://notes.sagredo.eu/en/qmail-notes-185/installing-dovecot-and-sieve-on-a-vpopmail-qmail-server-28.html#comment995
2297+  https://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/netqmail-maildir.patch
2298+* Kyle B. Wheeler's "Better qmail-smtpd Logging" v.5 patch
2299+  facilitates diagnostics of qmail-smtpd logging its actions and decisions (search for a line with qmail-smtp:)
2300+  http://www.memoryhole.net/qmail/#logging
2301+* John Simpson's (?) Greeting delay patch
2302+  adds a user-definable delay after SMTP clients have initiated SMTP sessions, prior to qmail-smtpd responding
2303+  with "220 ESMTP". It can reject connections from clients which tried to send commands before greeting.
2304+  https://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmail-greetdelay.patch
2305+* Manvendra Bhangui's DKIM and SURBL filter v.1.28 patch
2306+  adds DKIM signing & verification and SURBL filtering support to qmail.
2307+  qmail-dk is based on Russ Nelson's patch: http//:www.qmail.org/qmail-1.03-dk-0.54.patch
2308+  qmail-dkim uses hacked libdkim libraries from libdkim project at http://libdkim.sourceforge.net/
2309+  surbfilter is built on djb functions and some functions have been ruthlessly borrowed from qmail surbl
2310+  interface by Pieter Droogendijk and the surblhost program at http://surblhost.sourceforge.net/
2311+  (file hier.c modified to chown /var/qmail/control/cache and subdirs to vpopmail)
2312+  http://sourceforge.net/projects/indimail/files/netqmail-addons/qmail-dkim-1.0/
2313+  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/ANNOUNCE.surblfilter
2314+* Claudio Jeker and Andre Oppermann's EXTTODO patch (release 5. Jan. 2003)
2315+  addresses a problem known as the silly qmail (queue)  problem
2316+  http://www.nrg4u.com/qmail/ext_todo-20030105.patch
2317+* Russell Nelson's big-todo patch
2318+  makes qmail use a hashing mechanism in the todo folder similar to that used in the rest of the queue
2319+  http://www.qmail.org/big-todo.103.patch
2320+* Stephane Cottin's qmail-inject-null-sender patch (let's call it in this way)
2321+  prevents qmail-inject from rewriting the null sender, fixing an issue with sieve vacation/reject messages.
2322+  More info here: http://www.dovecot.org/list/dovecot/2009-June/040811.html
2323+  https://notes.sagredo.eu/files/qmail/patches/qmail-inject-null-sender.patch
2324+* Russell Nelson's (modified by Charles Cazabon) doublebounce-trim patch, which updates the original
2325+  version by Russel Nelson
2326+  prevents double bounces from hitting your queue a second time provided that you delete the first line
2327+  from /var/qmail/control/doublebounceto
2328+  http://qmail.org/doublebounce-trim.patch
2329+* Will Harris' esmtp-size patch
2330+  enables qmail-smtpd to reject messages if they're larger than the maximum number of bytes allowed
2331+  according to the /var/qmail/control/databytes control file.
2332+  http://will.harris.ch/qmail-smtpd.c.diff
2333+* Inter7's qmail-taps-extended patch
2334+  http://notes.sagredo.eu/files/qmail/patches/qmail-tap.diff
2335+  Extended by Michai Secasiu (http://patchlog.com/patches/qmail-taps-extended/)
2336+  Provides the ability to archive each email that flows through the system.
2337+  Archiving only messages from or to certain email addresses is possible as well.
2338+* Rolf Eike Beer's qmail-remote CRLF patch
2339+  enables qmail-remote to handle CR properly, always sending the line breaks as CRLF and avoiding to
2340+  double the CR (like qmail-remote normally does)
2341+  http://opensource.sf-tec.de/qmail/
2342+* Andy Repton's outgoingip patch (adjusted by Sergio Gelato)
2343+  by default all outgoing emails are sent through the first IP address on the interface. In case of a multiple
2344+  IP server this patch makes qmail send outgoing emails with the IP eventually stored in control/outgoingip.
2345+  The ehlo domain is NOT modified by this patch.
2346+  http://www.qmail.org/outgoingip.patch
2347+  Robbie Walker provided a patch to correct qmail-qmqpc.c's call to timeoutconn(), because the function
2348+  signature was modified by the original outgoingip patch
2349+  https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html#comment373
2350+* Iain Patterson's qmail-smtpd pid, qp log patch
2351+  makes qmail-smtpd log a line similar to the following:
2352+  @4000000039b89c95026a89b4 mail recv: pid 8155 from <name@domain.xy> qp 8157
2353+  The pid allows you to match the message up with a given tcpserver process and the qp lets you find a
2354+  particular delivery.
2355+  http://iain.cx/qmail/patches.html#smtpd_pidqp
2356+* Jonathan de Boyne Pollard's any-to-cname patch
2357+  avoids qmail getting large amounts of DNS data we have no interest in and that may overflow our response
2358+  buffer.
2359+  http://www.memoryhole.net/qmail/#any-to-cname
2360+* Matthias Andree's qmail-rfc2821 patch
2361+  makes qmail rfc2821 compliant
2362+  http://www-dt.e-technik.uni-dortmund.de/~ma/qmail/patch-qmail-1.03-rfc2821.diff
2363+* Jonathan de Boyne Pollard's smtpd-502-to-500 patch
2364+  makes qmail rfc2821 compliant
2365+  https://notes.sagredo.eu/files/qmail/patches/smtpd-502-to-500.patch
2366+* Fabio Busatto's qmail-dnsbl patch
2367+  allows you to reject spam and virus looking at the sender's ip address.
2368+  Modified by Luca Franceschini to add support for whitelists, TXT and A queries, configurable return codes
2369+  451 or 553 with custom messages
2370+  http://qmail-dnsbl.sourceforge.net/
2371+* Scott Gifford's qmail-moreipme patch v. 0.6
2372+  prevents a problem caused by an MX or other mail routing directive instructing qmail to connect to
2373+  itself without realizing it's connecting to itself, saving CPU time.
2374+  http://www.suspectclass.com/sgifford/qmail/qmail-1.03-moreipme.README
2375+  http://www.suspectclass.com/sgifford/qmail/qmail-1.03-moreipme-0.6.patch
2376+* Alex Nee's qmail-hide-ip-headers patch
2377+  It will hide your Private or Public IP in the email Headers when you are sending Mail as a Relay Client.
2378+  https://notes.sagredo.eu/files/qmail/patches/qmail-hide-ip-headers.patch
2379+* John Saunders' qmail-date-localtime patch
2380+  causes the various qmail programs to generate date stamps in the local timezone.
2381+  https://notes.sagredo.eu/files/qmail/patches/qmail-date-localtime.patch
2382+* Dean Gaudet's qmail-liberal-lf patch v. 0.95
2383+  allow qmail-smtpd to accept messages that are terminated with a single \n instead of the required \r\n
2384+  sequence.
2385+  http://www.arctic.org/~dean/patches/qmail-0.95-liberal-lf.patch
2386+* Michael Samuel's maxrcpt patch
2387+  allows you to set a limit on how many recipients are specified for any one email message by setting
2388+  control/maxrcpt. RFC 2821 section 4.5.3.1 says that an MTA MUST allow at least 100 recipients for each
2389+  message, since this is one of the favourite tricks of the spammer.
2390+  http://copilotco.com/mail-archives/qmail.1997/msg03066.html
2391+* Inter7's qmail-eMPF patch
2392+  More info: http://www.qmailwiki.org/EMPF
2393+  eMPF follows a set of administrator-defined rules describing who can message whom.  With this,
2394+  companies can segregate various parts of their organizations email activities, as well as provide a
2395+  variety of security-enhancing services.
2396+* qregex (by  Andrew St. Jean http://www.arda.homeunix.net/downloads-qmail/, contributors: Jeremy Kitchen,
2397+  Alex Pleiner,
2398+  Thanos Massias. Original patch by Evan Borgstrom)
2399+  adds the ability to match address evelopes via Regular Expressions (REs) in the qmail-smtpd process.
2400+  Added new control file 'badhelonorelay', control/badmailto renamed control/badrcptto (Tx Luca Franceschini).
2401+* brtlimit
2402+  Luca Franceschini derived this patch from http://netdevice.com/qmail/patch/goodrcptto-12.patch
2403+  added control/brtlimit and BRTLIMIT variable to limit max invalid recipient errors before closing
2404+  the connection (man qmail-control)
2405+* validrcptto
2406+  https://notes.sagredo.eu/files/qmail/patches/validrcptto.README
2407+  Luca Franceschini grabbed the code from several patches with additional features:
2408+  http://qmail.jms1.net/patches/validrcptto cdb.shtml,
2409+  http://netdevice.com/qmail/patch/goodrcptto-ms-12.patch, http://patch.be/qmail/badrcptto.html
2410+  It works in conjunction with chkuser with both cdb and mysql accounts.
2411+* reject-relay-test by Russell Nelson
2412+  http://qmail.org/qmail-smtpd-relay-reject
2413+  It gets qmail to reject relay probes generated by so-called anti-spammers. These relay probes have
2414+  '!', '%' and '@' in the local (username) part of the address.
2415+* Luca Franceschini
2416+  added DISABLETLS environment variable, useful if you want to disable TLS on a desired port
2417+  added FORCEAUTHMAILFROM environment variable to REQUIRE that authenticated user and 'mail from' are identical
2418+  added SMTPAUTHMETHOD, SMTPAUTHUSER and SMTP_AUTH_USER env variables for external plugins (see
2419+  http://qmail-spp.sourceforge.net/doc/)
2420+* fixed little bug in 'mail from' address handling
2421+  patch by Andre Opperman at http://qmail.cr.yp.narkive.com/kBry6GJl/bug-in-qmail-smtpd-c-addrparse-function
2422+* Luca Franceschini's qlog patch
2423+  smtpd logging with fixed format. An entry 'qlogenvelope' is generated after accepting or rejecting
2424+  every recipients in the envelope phase.
2425+* Luca Franceschini's reject null senders patch
2426+  useful in special cases if you temporarily need to reject the null sender (although breaks RFC compatibility).
2427+  You just need to put 1 (actually any number different from 0) in your control/rejectnullsenders to reject
2428+  the null sender with 421 error message.
2429+* dnscname patch
2430+  Removes CNAME check in order to avoid getting large amounts of data of no interest in and that may
2431+  overflow the response buffer.
2432+  https://lists.gt.net/qmail/users/138190
2433+* Luca Franceschini's rcptcheck patch
2434+  (based on original patch from Jay Soffian (http://www.soffian.org/downloads/qmail/qmail-smtpd-doc.html)
2435+  Originally designed for the purpose of receipt validation, it can also be used to limit the numbr of
2436+  email a given IP and/or auth-user and/or domain can send in a given time interval. It has to be used
2437+  in conjuction with the rcptcheck-overlimit.sh LF's script
2438+  https://notes.sagredo.eu/files/qmail/rcptcheck-overlimit.sh
2439+  https://notes.sagredo.eu/files/qmail/patches/rcptcheck.patch
2440+* Reed Sandberg's qmail-channels patch
2441+  Allows you to add an arbitrary number of supplemental remote queues, each distinguished by a list of
2442+  recipient domains and separate throttling (concurrency) capabilities. This patch also allows dynamic
2443+  throttling of the concurrency control files so you can just send qmail-send a HUP signal instead of
2444+  restarting the service every time.
2445+  This patch is useful when some email providers complain of too many emails receveid at the same time
2446+  (in case of news letters for instance). Look here for more info
2447+  https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html#comment1328
2448+  Edit conf-channels before compiling: Total number of channels (queues) available for delivery. Must be at
2449+  least 2, and anything above 2 are considered supplemental channels.
2450+  http://www.thesmbexchange.com/eng/qmail-channels_patch.html
2451+* Endersys R&D team's qmail-remote-logging patch
2452+  gets qmail-remote to log sender, recipient and IP adddress all together in the "Delivery success/failure" line
2453+  https://web.archive.org/web/20120530051612/http://blog.endersys.com/2009/12/qmail-canonicalised-recipient-logging-and-more-patch/
2454+* notqmail.org's cve-2005-1513 patch
2455+  addresses a vulnerability issue spotted by Georgi Guninski in 2005
2456+  https://www.qualys.com/2020/05/19/cve-2005-1513/remote-code-execution-qmail.txt
2457+
2458+= Disclaimer
2459+This patch comes with the usual warranty: it works for me, it may not work for you,
2460+use at your own risk etc. etc. :)
2461+Comments, suggestions, criticisms are always welcome!
2462+
2463+= Usage
2464+
2465+* Install libdomainkeys
2466+wget https://notes.sagredo.eu/files/qmail/tar/libdomainkeys-0.69.tar.gz
2467+tar xzf libdomainkeys-0.69.tar.gz
2468+wget https://notes.sagredo.eu/files/qmail/patches/libdomainkeys/libdomainkeys-openssl-1.1.patch
2469+wget https://notes.sagredo.eu/files/qmail/patches/libdomainkeys-0.69.diff
2470+cd libdomainkeys-0.69
2471+chown -R root.root .
2472+patch -p1 < ../libdomainkeys-openssl-1.1.patch
2473+patch < ../libdomainkeys-0.69.diff
2474+make
2475+cp libdomainkeys.a /usr/lib
2476+
2477+* Install libsrs2
2478+wget https://notes.sagredo.eu/files/qmail/tar/libsrs2-1.0.18.tar.gz
2479+tar xzf libsrs2-1.0.18.tar.gz
2480+cd libsrs2-1.0.18
2481+./configure
2482+make
2483+make install
2484+ldconfig
2485+cd ../
2486+
2487+* Apply the patch and compile
2488+wget https://notes.sagredo.eu/files/qmail/patches/roberto-netqmail-1.06.patch-latest.gz
2489+wget http://qmail.org/netqmail-1.06.tar.gz
2490+tar xzf netqmail-1.06.tar.gz
2491+cd netqmail-1.06
2492+chown -R root.root .
2493+gunzip -c ../roberto-netqmail-1.06.patch-latest.gz | patch
2494+make
2495+make setup check
2496+
2497+* You have to export SMTPAUTH in your run file if you want to do the auth
2498+
2499+* You have to export SURBL=1 in your run file if you want to enable SURBL
2500+
2501+* /var/qmail/control/cache must be owned by the user who runs qmail-smtpd, vpopmail.vchkpwd in my case.
2502+  Change the permissions according to your qmail configuration.
2503+
2504+=================================================================================================================
2505+
2506+= Changelog
2507+
2508+2020.07.29
2509+-dk-filter: corrected a bug where dk-filter was using DKIMDOMAIN unconditionally. Now it uses DKIMDOMAIN
2510+ only if _SENDER is null (tx Manvendra Bhangui).
2511+
2512+2020.07.27
2513+-added cve-2005-1513 patch
2514+
2515+2020.04.25
2516+-qmail-smtpd.c: added rcptcount = 0; in smtp_rset function to prevent the maxrcpto error if control/maxrcpt limit
2517+ has been exceeded in multiple messages sent sequentially rather than in a single mail (tx Alexandre Fonceca).
2518+ More info here: https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html#comment1594
2519+
2520+2020.04.16
2521+-qmail-remote-logging patch added
2522+
2523+2020.04.10
2524+-DKIM patch updated to v. 1.28
2525+ * outgoing messages from null sender ("<>") will be signed as well with the domain in env variable DKIMDOMAIN
2526+ * declaring NODK env variable disables old domainkeys signature, while defining NODKIM disables DKIM.
2527+
2528+2020.01.11
2529+-qmail-tls patch updated to v. 20200107
2530+ * working client cert authentication with TLSv1.3 (Rolf Eike Beer)
2531+
2532+2019.12.08
2533+-BUG qmail-smtpd.c: now TLS is defined before chkuser.h call, to avoid errors on closing the db connection
2534+ (tx ChangHo.Na https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html#comment1469)
2535+
2536+2019.07.12
2537+-qmail-channels patch added
2538+ more info here http://www.thesmbexchange.com/eng/qmail-channels_patch.html
2539+-improved verbosity of die_read function in qmail-smtpd.c (qmail-smtpd: read failure)
2540+ more info here https://notes.sagredo.eu/files/qmail/patches/roberto-netqmail-1.06/die_read.patch
2541+
2542+2019.06.19
2543+-DKIM patch updated to v. 1.26
2544+ * BUG - honor body length tag in verification
2545+
2546+2019.05.24
2547+-qmail-tls updated to v. 20190517
2548+ * bug: qmail-smtpd ssl_free before tls_out error string (K. Wheeler)
2549+
2550+2019.05.23
2551+-DKIM patch updated to v. 1.25
2552+ * SIGSEGV - when the txt data for domainkeys is very large exposed a bug in the way realloc() was used incorrectly.
2553+ * On 32 bit systems, variable defined as time_t overflows. Now qmail-dkim will skip expiry check in such conditions.
2554+
2555+2019.04.25
2556+-bug fixed on qmail-smtpd.c: it was selecting the wrong openssl version on line 2331 (tx ChangHo.Na)
2557+
2558+2019.04.09
2559+-qmail-tls updated to v. 20190408
2560+ * make compatible with openssl 1.1.0 (Rolf Eike Beer, Dirk Engling, Alexander Hof)
2561+ * compiler warnings on char * casts (Kai Peter)
2562+
2563+2019.03.22
2564+-fixed a bug causing crashes with qmail-remote when using openssl-1.1 (tx Luca Franceschini)
2565+(https://notes.sagredo.eu/files/qmail//patches//roberto-netqmail-1.06/2019.03.22-fix.patch)
2566+
2567+2019.02.13
2568+-Port to openssl-1.1
2569+-DKIM patch updated to v. 1.24
2570+ * bug fix: restored signaturedomains/nosignaturedomains functionalities.
2571+
2572+2018.08.25
2573+-DKIM patch updated to v. 1.23
2574+ * fixed a bug where including round brackets in the From: field ouside the double quotes, i.e.
2575+   From: "Name Surname (My Company)" <name.surname@company.com>, results in a DKIMContext structure invalid
2576+   error (tx Mirko Buffoni).
2577+ * qmail-dkim and dkim were issuing a failure for emails which had multiple signature with at least one good
2578+   signature. Now qmail-dkim and dkim will issue a success if at least one good signature is found.
2579+
2580+2018.08.23
2581+-logging patch
2582+ * fixed a bug in logit and logit2 functions where after a RSET command and a subsequent brutal quit
2583+   of the smtp conversation '^]' by the client cause a segfault (tx Mirko Buffoni, more info here
2584+   https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html#comment1132)
2585+-patch info moved to 'README.PATCH' file
2586+
2587+2018.04.03
2588+-DKIM patch updated to v. 1.22
2589+ * openssl 1.1.0 port
2590+ * various improvements, bug fixes
2591+
2592+2018.01.10
2593+-maildir++
2594+ * fixed a bug where the filesize part of the S=<filesize> component of the Maildir++ compatible filename
2595+   is wrong (tx MG). More info here: http://notes.sagredo.eu/en/qmail-notes-185/installing-dovecot-and-sieve-on-a-
2596+vpopmail-qmail-server-28.html#comment995
2597+-qmail-queue-extra
2598+ * removed, because it was causing more problems than advantages, as the domain of the log@yourdomain.tld
2599+   had to match the system domain inside control/me and shouldn't be a virtual domain as well.
2600+
2601+2017.10.11 (tx Luca Franceschini)
2602+-qlogfix
2603+ * log strings should terminate with \n to avoid trailing ^M using splogger
2604+ * bug reporting custom errors from qmail-queue in qlog
2605+-added dnscname patch
2606+-added rcptcheck patch
2607+
2608+2017.08.18
2609+-qmail-smtpd now retains authentication upon rset
2610+ (tx to Andreas http://notes.sagredo.eu/qmail-notes-185/smtp-auth-qmail-tls-forcetls-patch-for-qmail-84.html#comme
2611+nt750)
2612+
2613+2017-05-14
2614+-DKIM patch updated to v. 1.20
2615+ It now manages long TXT records, avoiding the rejection of some hotmail.com messages.
2616+
2617+2016-12-19
2618+-Several new patches and improvements added (thanks to Luca Franceschini)
2619+More info here http://notes.sagredo.eu/node/178
2620+ -qregex patch
2621+ -brtlimit patch
2622+ -validrcptto patch
2623+ -rbl patch (updates qmail-dnsbl patch)
2624+ -reject-relay-test patch
2625+ -added DISABLETLS environment variable, useful if you want to disable TLS on a desired port
2626+ -added FORCEAUTHMAILFROM environment variable to REQUIRE that authenticated user and 'mail from' are identical
2627+ -fixed little bug in 'mail from' address handling (patch by Andre Opperman at http://qmail.cr.yp.narkive.com/kBry
2628+  6GJl/bug-in-qmail-smtpd-c-addrparse-function)
2629+ -added SMTPAUTHMETHOD, SMTPAUTHUSER and SMTP_AUTH_USER env variables for external plugins
2630+ -qlog patch
2631+ -reject null senders patch
2632+ -bouncecontrolmime patch
2633+ -qmail-taps-extended (updates qmail-tap)
2634+
2635+2016-12-02
2636+-fixed BUG in qmail-remote.c: in case of remote server who doesn't allow EHLO the response for an alternative
2637+ HELO was checked twice, making the connection to die. (Thanks to Luca Franceschini)
2638+ Patch applied: http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/fix_sagredo_remotehelo.patch
2639+
2640+2016-09-19
2641+-qmail-tls patch updated to v. 20160918
2642+  * bug: qmail-remote accepting any dNSName, without checking that is matches (E. Surovegin)
2643+  * bug: documentation regarding RSA and DH keys (K. Peter, G. A. Bofill)
2644+
2645+2016-05-15
2646+-force-tls patch improved (a big thanks to Marcel Telka). Now qmail-smtpd avoids to write the auth verb if the
2647+ the STARTTLS command was not sent by the client
2648+
2649+2016-03-09
2650+-DKIM patch upgraded to v. 1.19
2651+ * verification will not fail when a dkim signature does not include the subject provided that the
2652+   UNSIGNED_SUBJECT environment variable is declared.
2653+
2654+2015-12-26
2655+-qmail-tls patch updated to v. 20151215
2656+ * typo in #if OPENSSL_VERSION_NUMBER for 2015-12-08 patch release (V. Smith)
2657+ * add ECDH to qmail-smtpd
2658+ * increase size of RSA and DH pregenerated keys to 2048 bits
2659+ * qmail-smtpd sets RELAYCLIENT if relaying allowed by cert
2660+ more info at http://inoa.net/qmail-tls/
2661+
2662+2015-12-15
2663+-DKIM patch by Manvendra Bhangui updated to v. 1.18
2664+
2665+2015-10-03
2666+-qmail-authentication: updated to v. 0.8.3
2667+
2668+2015-08-08
2669+-fixed a bug on qmail-remote.c that was causing the sending of an additionale ehlo greeting (thanks to Cristoph Gr
2670+over)
2671+
2672+2015-04-11
2673+-qmail-authentication: updated to v. 0.8.2
2674+-qmail-tls: upgraded to v. 20141216 (POODLE vulnerability fixed)
2675+
2676+2015-03-28
2677+-added qmail-eMPF patch
2678+
2679+2014-11-19
2680+-security fix: the SSLv3 connection is now switched off
2681+
2682+2014-11-15
2683+-modified the QUEUE_EXTRA variable in extra.h to improve the qmail-send's log
2684+
2685+2014-04-14
2686+-added maxrcpt patch
2687+
2688+2014-03-10
2689+-added qmail-0.95-liberal-lf patch
2690+
2691+2013-12-30
2692+-added qmail-srs
2693+-the character "=" is now considered valid in the sender address by chkuser in order to accept SRS
2694+
2695+2013-12-18
2696+-added qmail-date-localtime patch
2697+
2698+2013-12-14
2699+-added qmail-hide-ip patch
2700+
2701+2013-12-10
2702+-the original greetdelay by e.h. has been replaced with the improved patch by John Simpson. Now
2703+ communications trying to send commands before the greeting will be closed. Premature disconnections will be
2704+ logged as well.
2705+-CHKUSER_SENDER_FORMAT enabled to reject fake senders without any domain declared (like <foo>)
2706+-chkuser logging: I slightly modified the log line adding the variables' name just to facilitate its interpretation
2707+-added qmail-moreipme patch
2708+
2709+2013-12-07
2710+-added qmail-dnsbl patch
2711+
2712+2013-12-05
2713+-added two patches to make qmail rfc2821 compliant
2714+
2715+2013-11-23
2716+-added any-to-cname patch
2717+
2718+2013-09-27
2719+-DKIM patch upgraded to v. 1.17. Defined -DHAVE_SHA_256 while compiling dkimverify.cpp in the Makefile.
2720+ This solved an issue while verifying signatures using sha256.
2721+
2722+2013-09-16
2723+-Minor fixes to the DKIM patch.
2724+
2725+2013-09-13
2726+-DKIM patch upgraded to v. 1.16. The signing at qmail-remote level has been revised by its author.
2727+
2728+2013-08-25
2729+-qmail-qmqpc.c call to timeoutconn() needed a correction because the function signature was modified by the
2730+ outgoingip patch. Thanks to Robbie Walker (diff here http://notes.sagredo.eu/node/82#comment-373)
2731+
2732+2013-08-21
2733+-fixed a bug in hier.c which caused the installation not to build properly the queue/todo dir structure (thanks to
2734+ Scott Ramshaw)
2735+
2736+2013-08-18
2737+-DKIM-SURBL patch by Manvendra Bhangui updated to v. 1.14
2738+
2739+2013-08-12
2740+-DKIM patch upgraded to v. 1.12. The new patch adds surblfilter functionality.
2741+-added qmail-smtpd pid, qp log patch
2742+
2743+2013-08-08
2744+-qmail-SPF modified by Manvendra Bhangui to make it IPv6-mapped IPv4 addresses compliant. In order to have it
2745+ working with such addresses you have to patch tcpserver.c accordingly. You can use a patch fot ucspi-tcp6-0.98
2746+ by Manvendra Bhangui at http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/tcpserver-ipv6mapped_ip
2747+ v4.patch or wait for v. 0.99 relase of ucspi-tcp6
2748+-added outgoingip patch
2749+-added qmail-bounce patch
2750+
2751+2013-03-31
2752+qmail-auth updated to latest v. 0.8.1 Added authentication by recipient domain for qmail-remote.
2753+Look at README.auth for further details
2754+
2755+2013-02-11
2756+some code adjustments in qmail-smtpd.c smtpd_ehlo() to restore total compatibility with esmtp-size patch
2757+
2758+2013-02-08
2759+qmail-auth updated to latest v. 0.7.6. Look at README.auth for further details
2760+
2761+2013-01-28
2762+fixed an issue on qmail-pop3d which was causing a double +OK after the pass command (thanks to Rakesh, Orbit
2763+and Simplex for helping in testing and troubleshooting)
2764+
2765+2013-01-06
2766+environment variable GREETDELAY renamed to SMTPD_GREETDELAY
2767+
2768+2012-10-31
2769+qmail-auth updated to latest v. 0.7.5. Look at README.auth for further details
2770+The qmail-forcetls patch was simplyfied accordingly.
2771+You MUST export SMTPAUTH="" in your run file now.
2772+
2773+2012-04-25
2774+-added qmail-remote CRLF (thanks to Pierre Lauriente for the help on testing and troubleshooting)
2775+The qmail-remote CRLF patch solved a problem of broken headers after sieve forwarding that was
2776+caused by a bad handling of the CR (carriage return) by qmail-remote.
2777+The issue is also reported here http://www.dt.e-technik.uni-dortmund.de/~ma/qmail-bugs.html
2778+
2779+2012.04.16
2780+-added qmail-tap
2781+
2782+2012.02.08
2783+-added smtp-size patch
2784+
2785+2012.01.29
2786+-added doublebounce-trim patch
2787+
2788+2011.12.12
2789+-file update_tmprsadh modified to chown the .pem files to vpopmail to avoid hang-ups during the smtp
2790+conversation on port 587 caused by permission problems.
2791+
2792+2011.10.06
2793+-qmail-remote.c: fixed. It was not going into tls on authentication (thanks to Krzysztof Gajdemski)
2794+-force-tls now quits if the starttls command is not provided when required (thanks to Jacekalex)
2795diff -ruN ../netqmail-1.06-original/README.auth netqmail-1.06/README.auth
2796--- ../netqmail-1.06-original/README.auth       1970-01-01 01:00:00.000000000 +0100
2797+++ netqmail-1.06/README.auth   2019-02-27 20:57:13.379025191 +0100
2798@@ -0,0 +1,154 @@
2799+README qmail SMTP Authentication
2800+================================
2801+
2802+Scope:
2803+------
2804+
2805+This patch supports RFC 2554 "SMTP Service Extension for Authentication" and
2806+RFC 4409 "Message Submission for Mail" for
2807+
2808+* qmail-smtpd and
2809+* qmail-remote
2810+
2811+and supports commonly the AUTH methods
2812+
2813+- CRAM-MD5
2814+- LOGIN (unsecure)
2815+- PLAIN (unsecure)
2816+
2817+Additionally, RFC 1870 is honoured ("SMTP Service Extension for Message Size Declaration").
2818+For more technical details see: http://www.fehcom.de/qmail/docu/smtpauth.html.
2819+
2820+
2821+Installation:
2822+-------------
2823+
2824+* Untar the source in the qmail-1.03 home direcotry.
2825+* Run ./install_auth.
2826+* Re-make qmail.
2827+
2828+
2829+Setup for qmail-smtpd:
2830+----------------------
2831+
2832+1. Prereqs:
2833+
2834+In order to use SMTP Authentication you have to use a 'Pluggable Authentication Module'
2835+PAM to be called by qmail-smtpd; typically
2836+
2837+       /var/qmail/bin/qmail-smtpd /bin/checkpassword true 2>&1
2838+
2839+Since qmail-smtpd does not run as root, checkpassword has to be made sticky.
2840+There is no need to include additionally the hostname in the call.
2841+In order to compute the CRAM-MD5 challenge, qmail-smtpd uses the 'tcplocalhost' information.
2842+
2843+2. Invocation:
2844+
2845+In order activate SMTP authentication, you need to provide the environment
2846+variable 'SMTPAUTH' to qmail-smtpd.
2847+
2848+Possible choices:
2849+       
2850+       a) SMTPAUTH="";         qmail-smtpd supports auth of type PLAIN and/or LOGIN.
2851+       b) SMTPAUTH="+cram";    qmail-smtpd will additionally annonce CRAM-MD5,
2852+                               this requires a CRAM-MD5 supporting PAM.
2853+       c) SMTPAUTH="cram";     qmail-smtpd will only annonce CRAM-MD5.
2854+       d) SMTPAUTH="!";        this instructs qmail-smtpd to require (any type) authentication for this connection.
2855+                               This behavior is equivalent to the Submission feaure.
2856+       e) SMTPAUTH="!cram";    same as d) but now CRAM-MD5 is the only method instead.
2857+
2858+
2859+Setup for qmail-remote:
2860+-----------------------
2861+
2862+SMTP Authentication with qmail-remote is faclitated by two means:
2863+
2864+a) SMTP Authentication by sender/sending domain as provided in the 'Mail From:'
2865+
2866+The control file 'authsenders' which works similar to 'control/smtproutes'
2867+but with additional authentication information (username + password):
2868+
2869+    @example.com:relay.example.com|user|passwd
2870+    info@example.com:relay.example.com:26|infouser|infopasswd
2871+    :mailrelay.example.com:587|e=mc2|testpass
2872+
2873+Note: The choice of the AUTH method depends on the capabilities of the server.
2874+
2875+b) SMTP Authentication by recipient domain:
2876+
2877+The control file 'smtproutes' is enhanced with the authentication information:
2878+
2879+    authdomain.com:mx.authdomain.com:125|myuserid|mypassword
2880+    :mailrelay.example.com:587|e=mc2|testpass
2881+
2882+
2883+Historical Notes:
2884+-----------------
2885+
2886+SMTP authentication for qmail-smtpd was initially provided by Krysztof Dabrowski (version 0.31):
2887+
2888+
2889+Changes wrt. Krysztof Dabrowski's patch:
2890+
2891+* Avoid the 'hostname' in the call of the PAM.
2892+* Confirm to Dan Bernstein's checkpassword interface even for CRAM-MD5.
2893+* Doesn't close FD 2; thus not inhibiting logging to STDERR.
2894+* Fixed bugs in base64.c.
2895+* Modified unconditional close of FD 3 in order to sustain reading of 'control/morecpthosts.cdb'.
2896+* Evaluation of the (informational) Mail From: < > Auth=username.
2897+* Additional support for the advertised "Size" via 'Mail From: <return-path> SIZE=123456780' (RFC 1870).
2898+* RFC 3848 conformance for Received header in case of SMTP Auth (keyword ESMTPA).
2899+* Added SMTPAUTH environment variable.
2900+* The particular Submission feature has been removed; obsolete.
2901+
2902+
2903+SMTP authentication for qmail-remote is taken from Bjoern Kalkbrenner.
2904+
2905+Changes wrt. Bjoern Kalkbrenner's patch (version 0.0.1 / 20020715):
2906+
2907+* Uniform, modular support for LOGIN and PLAIN.
2908+* Added 'Mail From: < > Auth=username' information in provisionally XTEXT format.
2909+* Added CRAM-MD5 support.
2910+* Added authentication by recipient domain.
2911+
2912+
2913+Release Notes:
2914+--------------
2915+
2916+Version:       Notes:                                  Date:
2917+-------------------------------------------------------------------
2918+0.5.9          qmail-smtpd AUTH (only)                 25.3.2008
2919+0.6.9          qmail-authentication                    1.2.2010
2920+0.7.0          Based on qmail-authentication 0.69
2921+               including now CRAM-MD5 support
2922+               for qmail-remote                        31.7.2010
2923+0.7.1          cosmetics for qmail-remote              5.8.2010
2924+0.7.2          authorization-id = authentication-id
2925+               for qmail-remote;
2926+               added SMTPAUTH environment variable
2927+               for qmail-smtpd; backport from SC 2.7   29.4.2012
2928+0.7.3          Fixed missing AUTH for qmai-smtpd announcement.
2929+               Improved SUBMISSION port handling.      2.5.2012
2930+0.7.4          Fixed missing 250 statements for AUTH.
2931+               Changed SMTPAUTH settings for cram-md5  18.5.2012
2932+0.7.5          Fixed bug in qmail-remote not respecting
2933+               announced AUTH types. Tx. Callum Gibson.
2934+               Added '432' server code evaluation for
2935+               AUTH password expired in qmail-remote.  23.10.2012
2936+0.7.6          Fixed order of SMTP commands (tx roberto).
2937+                                                       02.02.2013
2938+0.8.0          Added authentication by recipient domain
2939+               for qmail-remote.
2940+               Removed SUBMISSION port feature for
2941+               qmail-smtpd.                            22.03.2013
2942+0.8.1          Added SMTPAUTH="!+crom" feature.        24.03.2013
2943+0.8.2          Bug fix: qmail-smtpd ACCEPTS auth commands
2944+               even if SMTPAUTH="-". (tx chris).       21.01.2015
2945+0.8.3          Fixed bug in MD5 calculation for AMD64.
2946+               Fixed 'die_nomem' bug in qmail-smtpd.   23.08.2015
2947+
2948+
2949+
2950+Erwin Hoffmann - Hoehn 2015-08-23 (www.fehcom.de)
2951+
2952+
2953diff -ruN ../netqmail-1.06-original/README.dnsbl netqmail-1.06/README.dnsbl
2954--- ../netqmail-1.06-original/README.dnsbl      1970-01-01 01:00:00.000000000 +0100
2955+++ netqmail-1.06/README.dnsbl  2019-02-27 20:57:13.379025191 +0100
2956@@ -0,0 +1,18 @@
2957+Code and logic from rblsmtpd and qmail-dnsbl patch http://qmail-dnsbl.sourceforge.net/
2958+
2959+-added support for whitelists, TXT and A queries, configurable return codes 451 or 553 with custom messages
2960+
2961+  # - default file control/dnsbllist can be overridden with env variable DNSBLLIST
2962+  # - if DNSBLSKIP env variable is set, qmail-smtpd skips the rbl check
2963+  # - 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)
2964+  # - support for environment variable RBLSMTPD (check rblsmtpd man page for more details)
2965+  # - dnsbllist can contain empty lines and comments with '#' at start or end of lines; leading and trailing spaces are automatically removed
2966+
2967+CONTROL FILES
2968+
2969+dnsbllist
2970+            A list of dnsbl providers that qmail-smtpd checks to identify blacklisted ip addresses.
2971+
2972+            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
2973+            some other authentication method succedeed and authorized the client to relay (smtp-auth or tls client certificate), or if RELAYCLIENT enviromnent variable  is
2974+            set.
2975diff -ruN ../netqmail-1.06-original/README.empf netqmail-1.06/README.empf
2976--- ../netqmail-1.06-original/README.empf       1970-01-01 01:00:00.000000000 +0100
2977+++ netqmail-1.06/README.empf   2019-02-27 20:57:13.380025180 +0100
2978@@ -0,0 +1,106 @@
2979+config file is: /var/qmail/control/policy
2980+more info: http://www.qmailwiki.org/EMPF
2981+
2982+eMail Messaging Policy Framework (eMPF)
2983+
2984+What it does
2985+------------
2986+
2987+   eMPF follows a set of administrator-defined rules describing
2988+who can message whom.  With this, companies can segregate various
2989+parts of their organizations email activities, as well as provide
2990+a variety of security-enhancing services.
2991+
2992+How it does it
2993+--------------
2994+
2995+   During an SMTP session, when a sender identifies themselves, either
2996+via SMTP_AUTH, or via the message envelope, as well as a recipient,
2997+eMPF loads applicable message policies to determine if the sender is
2998+allowed to message the sender, and if the recipient is allowed to
2999+receive mail from the sender.
3000+
3001+What it doesn't do
3002+------------------
3003+
3004+   Because mail from outside your mail server cannot be authenticated,
3005+the policy framework cannot be entirely sure about the identities of
3006+senders messaging local users.  However, if SMTP_AUTH is enabled, and
3007+required by your local users, it can prevent local users from transmitting
3008+information to parties who should not view it.  It is HIGHLY recommended
3009+that a mail server implementing eMPF also require SMTP_AUTH by it's local
3010+users as well as use the POLICY_ENFORCE_AUTHENTICATION configuration so that
3011+senders from the outside cannot masquerade as local users.
3012+
3013+Policy configuration
3014+--------------------
3015+
3016+   A large, complicated policy may be rather intimidating at first, however,
3017+if the rules are documented well, and a basic knowledge of the format of a
3018+policy is known, they are rather simple to set up.
3019+
3020+   comment:
3021+         # text
3022+         ; text
3023+
3024+   policy:
3025+         <domain>:<delivery policy>,[<user policy>,][<etc>,]
3026+
3027+   user policy:
3028+         <username>:<delivery policy>[<delivery policy>]
3029+
3030+   delivery policy:
3031+         <delivery type>[(<address>[,<address>])]
3032+
3033+   delivery types:
3034+         L - Local
3035+         R - Remote
3036+         E - External
3037+         I - Internal
3038+
3039+Delivery types specify what types of messaging can take place.  An uppercase
3040+delivery type allows a type of delivery, and a lowercase delivery type,
3041+disallows a type of delivery.  Delivery types may take a list of addresses.
3042+When a list of addresses is provided after a delivery type, those addresses
3043+are the only addresses covered by that delivery type.
3044+
3045+Certian delivery types apply only to either the sender of the recipient.
3046+Here is a more clear definition of each of the delivery types.
3047+
3048+   Local    - (Sender only) When sending a message to a user on the same domain
3049+   Internal - (Recipient only) When recieving a messages from a user of the
3050+              same domain
3051+   Remote   - (Sender only) When sending a message to a user on another
3052+              domain (even if that domain resides on the same mail server).
3053+   External - (Recipient only) When receiving a message from a user on
3054+              another domain (even if that domain resides on the same server).
3055+
3056+Samples
3057+-------
3058+
3059+In this example, example.com allows all messaging.  In this case,
3060+simply not defining a policy would be more efficient.
3061+
3062+   example.com:LREI,
3063+
3064+Now, example.com wishes all mail to stay internal.  As stated above,
3065+there are particular cases in which eMPF cannot authenticate a sender.
3066+This only occurs when a remote mail server is transmitting mail to a local
3067+user on your system.  In this case, a remote user could pretend to be a local
3068+user, and succesfully deliver mail to another local user.  However, the
3069+recipient would be unable to message back.
3070+
3071+   example.com:LIre,
3072+
3073+As in the above example, example.com wants all mail to stay internal, however,
3074+a few of their users are allowed to communicate with the outside world.  Sales
3075+can communicate with everybody, and Tasks can send messages only to their
3076+sister-site, example.org.
3077+
3078+   example.com:LIre,sales:RE,tasks:R(*@example.org)E(*@example.org),
3079+
3080+Something to keep in mind in this scenario, is that if example.org is hosted
3081+on the same system, and has similar policies to example.com, a policy must be
3082+established for example.org which allows messages from example.com.
3083+
3084+   example.org:LIre,sales:RE,tasks:E(*@example.com)R(*@example.com),
3085diff -ruN ../netqmail-1.06-original/README.exttodo netqmail-1.06/README.exttodo
3086--- ../netqmail-1.06-original/README.exttodo    1970-01-01 01:00:00.000000000 +0100
3087+++ netqmail-1.06/README.exttodo        2019-02-27 20:57:13.380025180 +0100
3088@@ -0,0 +1,114 @@
3089+EXTTODO by Claudio Jeker <jeker@n-r-g.com> and
3090+Andre Oppermann <opi@nrg4u.com>
3091+(c) 1998,1999,2000,2001,2002 Internet Business Solutions Ltd.
3092+
3093+The EXTTODO patch is a part of the qmail-ldap patch.
3094+This patches for qmail come with NO WARRANTY.
3095+
3096+These patches are under the BSD license.
3097+
3098+RELEASE: 5. Jan. 2003
3099+
3100+EXTTODO:
3101+======================
3102+
3103+TOC:
3104+ WHAT DOES IT DO
3105+ INSTALL
3106+ CONFIG FILES
3107+ SETUP
3108+ BIG PICTURE
3109+
3110+NEWS:
3111+
3112+ This is the first release of the EXTTODO patch.
3113+
3114+================================================================================
3115+
3116+WHAT DOES IT DO
3117+
3118+ The exttodo patch addresses a problem known as the silly qmail (queue)
3119+ problem. This problem is found only on system with high injection rates.
3120+
3121+ qmail with a big local and remote concurrency could deliver a tremendous
3122+ amount of messages but normally this can not be achieved because qmail-send
3123+ becomes a bottleneck on those high volumes servers.
3124+ qmail-send preprocesses all new messages before distributing them for local
3125+ or remote delivering. In one run qmail-send does one todo run but has the
3126+ ability to close multiple jobs. Because of this layout qmail-send can not
3127+ feed all the new available (local/remote) delivery slots and therefor it is
3128+ not possible to achieve the maximum throughput.
3129+ This would be a minor problem if one qmail-send run could be done in extreme
3130+ short time but because of many file system calls (fsync and (un)link) a todo
3131+ run is expensive and throttles the throughput.
3132+
3133+ The exttodo patch tries to solve the problem by moving the todo routine into
3134+ an external program. This reduces the run time in qmail-send.
3135+
3136+ exttodo adds a new program to qmail called qmail-todo. qmail-todo prepares
3137+ incoming messages for local and remote delivering (by creating info/<messid>
3138+ local/<messid> and remote/<messid> and removing todo/<messid>). See also
3139+ INTERNALS. As next qmail-todo transmits the <messid> to qmail-send which will
3140+ add this message into the priority queue which schedules the message for
3141+ delivery.
3142+
3143+INSTALL
3144+
3145+ To enable the exttodo patch you need to define EXTERNAL_TODO while compiling
3146+ qmail(-ldap) this can be done with the -D flag of cc (e.g. cc -DEXTERNAL_TODO).
3147+
3148+ NOTE: the exttodo patch can also be used on qmail systems without the
3149+ qmail-ldap patch.
3150+
3151+================================================================================
3152+
3153+CONFIG FILES
3154+
3155+ No additional control files are used or needed.
3156+
3157+================================================================================
3158+
3159+SETUP
3160+
3161+ qmail-todo will be started by qmail-start and therefor no additional setup
3162+ is needed.
3163+
3164+ To verify that exttodo is running just check if qmail-todo is running.
3165+
3166+================================================================================
3167+
3168+BIG PICTURE
3169+
3170+               +-------+   +-------+
3171+               | clean |   | clean |
3172+               +--0-1--+   +--0-1--+       +-----------+
3173+         trigger  ^ |         ^ |        +->0,1 lspawn |
3174+            |     | v         | v       /  +-----------+
3175+ +-------+  v  +--2-3--+   +--5-6--+   /
3176+ |       |  |  |       0<--7     1,2<-+
3177+ | queue |--+--| todo  |   | send  |
3178+ |       |  |  |       1-->8     3,4<-+
3179+ +-------+     +-------+   +---0---+   \
3180+                               |        \  +-----------+
3181+                               v         +->0,1 rspwan |
3182+                           +---0---+       +-----------+
3183+                           | logger|
3184+                           +-------+
3185+
3186+Communication between qmail-send and qmail-todo
3187+
3188+todo -> send:
3189+   D[LRB]<mesgid>\0
3190+          Start delivery for new message with id <messid>.
3191+          the character L, R or B defines the type
3192+          of delivery, local, remote or both respectively.
3193+   L<string>\0
3194+          Dump string to the logger without adding additional \n or similar.
3195+send -> todo:
3196+   H      Got a SIGHUP reread ~/control/locals and ~/control/virtualdomains
3197+   X      Quit ASAP.
3198+
3199+qmail-todo sends "\0" terminated messages whereas qmail-send just send one
3200+character to qmail-todo.
3201+
3202+
3203diff -ruN ../netqmail-1.06-original/README.liberal-lf netqmail-1.06/README.liberal-lf
3204--- ../netqmail-1.06-original/README.liberal-lf 1970-01-01 01:00:00.000000000 +0100
3205+++ netqmail-1.06/README.liberal-lf     2019-02-27 20:57:13.380025180 +0100
3206@@ -0,0 +1,39 @@
3207+From dgaudet-list-qmail@arctic.org Fri Jan 17 17:50:19 1997
3208+Date: Fri, 17 Jan 1997 17:38:33 -0800 (PST)
3209+From: Dean Gaudet <dgaudet-list-qmail@arctic.org>
3210+To: Julie Baumler <a4jb@psulib.cc.pdx.edu>
3211+Cc: qmail list <djb-qmail@koobera.math.uic.edu>
3212+Subject: [PATCH] qmail and \r\n (was Re: host unable to deliver to host using qmail (summary))
3213+
3214+On Thu, 16 Jan 1997, Julie Baumler wrote:
3215+>
3216+> The admin on the machine in question added "E=\r\n" this morning and low
3217+> and behold, suddenly they can send us mail.
3218+>
3219+> >From a quick look at the source code, it appears that the remote machine
3220+> is notified of the problem, but sendmail in its infinite wisdom apparently
3221+> doesn't capture the error message.  (SIGH)
3222+
3223+I don't know what the sendmail config had wrong with it, but I know that
3224+systems I've set up bounce the error just fine.  Check also in the mail
3225+logs.
3226+
3227+qmail by default violates the "be liberal in what you accept" rule when
3228+receiving mail via smtp.  It enforces strict \r\n behaviour... which is
3229+exactly what the standard dictates.  But I'm not interested in my mailer
3230+telling other admins to fix their setup, because I know they won't.  (The
3231+straynewline() function tells people that if they're under Solaris they
3232+need E=\r\n in their Mether mailer.)
3233+
3234+There is also bug in sendmail pre 8.8 that in some instances will emit
3235+\rx\n for some printable x.  The case where it occured to me, the message
3236+had been generated by a lame PC mail program.  In any event, this causes
3237+qmail to indefinitely defer the incoming message.
3238+
3239+So instead I looked at the sendmail-8.8.4 code and tried to duplicate its
3240+behaviour w.r.t. seeing \n when it wasn't expecting it.  That is what this
3241+patch implements.
3242+
3243+Go ahead, shoot me.
3244+
3245+Dean
3246diff -ruN ../netqmail-1.06-original/README.maxrcpt netqmail-1.06/README.maxrcpt
3247--- ../netqmail-1.06-original/README.maxrcpt    1970-01-01 01:00:00.000000000 +0100
3248+++ netqmail-1.06/README.maxrcpt        2019-02-27 20:57:13.380025180 +0100
3249@@ -0,0 +1,60 @@
3250+maxrcpt patch for qmail-smtpd
3251+
3252+    To: djb-qmail@xxxxxxxxxxxxxxxxxxxx
3253+    Subject: maxrcpt patch for qmail-smtpd
3254+    From: Michael Samuel <michael@xxxxxxxxxxxxxxxxxx>
3255+    Date: Wed, 12 Nov 1997 15:18:38 +1100 (EST)
3256+    Mailing-list: contact djb-qmail-help@xxxxxxxxxxxxxxxxxxxx; run by ezmlm
3257+
3258+-----BEGIN PGP SIGNED MESSAGE-----
3259+
3260+Here is a patch for qmail-smtpd to read a control file to limit the number
3261+of rcpt to commands.
3262+
3263+In the patch I have a thank-you to Sam, this is referring to
3264+mrsam@xxxxxxxxxxxxx who gave me the ideas on how to start off.
3265+
3266+Anyway, it is a "Do what you like with it, but don't blame me" license,
3267+but if it doesn't work, tell me what went wrong if you like and I will
3268+try to figure it out.
3269+
3270+
3271+
3272+Michael Samuel,
3273+
3274+Surf-Net City - Internet Cafe and Internet Service Providers
3275+
3276+                                Phone: +61 3 9593-9977
3277+                                E-Mail: michael@xxxxxxxxxxxxxxxxxx
3278+
3279+-----BEGIN PGP SIGNATURE-----
3280+Version: 2.6.3ia
3281+Charset: noconv
3282+
3283+iQCVAwUBNGkuIUqgdYLWa7qBAQHEiwP+JqNDMZDLwLY7CUdmkuY0OUHwSaFCJJiS
3284+T853fUkupG2kQz6WU8m0RXWd4Rhr+BT8+hqjDDPQYfWzK6QcEf563D0Mp7nA0ZuQ
3285+s+XHKflwb8PAZBp+lpzkMsgDg/B8mlw9dnJ4pGeP1keWR/5cgBFM78XsthW2rLXd
3286+EIXiZJ7AEhc=
3287+=5RMp
3288+-----END PGP SIGNATURE-----
3289+
3290+Here is a patch I rigged up limit the number of RCPT TO: commands per E-Mail
3291+messages.
3292+
3293+It reads the file control/maxrcpt relative to your qmail directory stucture
3294+(usually /var/qmail/control/maxrcpt).
3295+
3296+In that file you should have a integer, which represents the maximum number
3297+of recipients per E-Mail Messages.
3298+
3299+Apparently one of the SMTP rfcs recommends a minimum of 100 recipients per
3300+message be allowed.  Just something to keep in mind anyway.
3301+
3302+If /var/qmail/control/maxrcpt doesn't exist, it does not impose a limit and
3303+skips the rcpt part of the code, so unless I missed something in the source,
3304+you could even have more than MAXINT.
3305+
3306+I would like to thank Sam from the qmail list for giving me a good start to
3307+this patch, and anyone else who offered me suggestions from the qmail list.
3308+(When I refer to qmail list, I'm referring to djb-qmail@xxxxxxxxxxxxxxxxxxxx)
3309+
3310diff -ruN ../netqmail-1.06-original/README.moreipme netqmail-1.06/README.moreipme
3311--- ../netqmail-1.06-original/README.moreipme   1970-01-01 01:00:00.000000000 +0100
3312+++ netqmail-1.06/README.moreipme       2019-02-27 20:57:13.380025180 +0100
3313@@ -0,0 +1,154 @@
3314+###########
3315+### WHO ###
3316+###########
3317+
3318+This patch was written by Scott Gifford <sgifford@suspectclass.com>.
3319+The design and much of the code for supporting "notipme" was
3320+contributed by Charles Cazabon <charlesc@discworld.dyndns.org>.
3321+
3322+
3323+############
3324+### WHAT ###
3325+############
3326+
3327+This patch may be necessary in some configurations that involve network
3328+address translation or port forwarding.  It prevents a problem caused
3329+by an MX or other mail routing directive instructing qmail to connect to
3330+itself without realizing it's connecting to itself.  When this happens,
3331+it accepts the message, finds out where to deliver it to (itself), and
3332+promptly reconnects to itself to deliver the message.  Eventually, when
3333+it has done this 20 or 30 times, it will give up and bounce the message,
3334+but not before sucking up all of your CPU while it's happening.
3335+
3336+It may also be useful in some configurations that have multiple qmail
3337+servers configured on different interfaces of the same system.  qmail
3338+will normally refuse to deliver mail by SMTP to the machine it's
3339+running on, but with multiple copies of qmail, you may want to prevent
3340+this behavior.
3341+
3342+Normally, qmail can detect what IP addresses refer to itself by getting
3343+a list of all network interfaces with IP addresses from the operating
3344+system.  It uses this list to determine whether connecting to an address
3345+will cause it to connect to itself, and avoid the situation (it calls
3346+the perm_ambigmx() function, which prints the message:
3347+
3348+   Sorry. Although I'm listed as a best-preference MX or A for that host,
3349+   it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)
3350+
3351+But in situations where the OS is not aware of all IP addresses that
3352+connect back to itself, this detection fails, causing the CPU-sucking
3353+phenomenon described above.  This can happen if there is a network
3354+address translation device in front of the qmail server, such as a
3355+load-balancer or a router which allows you to share one IP address among
3356+several machines; if there is a port forwarder forwarding connections
3357+from some other machine to the SMTP server on the qmail server; or in
3358+configurations where a "dumb" mailserver is configured to use your qmail
3359+server as a "smarthost", delivering all mail to it without inspection.
3360+
3361+To solve this, other IP addresses which will ultimately connect back to
3362+your machine can be added to the file "control/moreipme", one per line.
3363+qmail will treat all addresses in this file exactly as if they were
3364+local, and if it finds an MX record or other mail routing information
3365+which would cause it to connect to any of these addresses, it will call
3366+perm_ambigmx(), and print the above error message.
3367+
3368+Additionally, IP addresses which the system detects but which should
3369+*not* be treated as local can be removed from qmail's ipme list by
3370+adding them to the file "control/notipme".
3371+
3372+IP addresses can be specified as individual addresses in the usual
3373+dotted-quad format, or as entire networks using a slash followed by
3374+the full dotted-quad netmask:
3375+
3376+    127.0.0.1
3377+    127.0.0.1/255.255.255.255
3378+    127.0.0.0/255.0.0.0
3379+    10.0.0.0/255.255.255.0
3380+
3381+An individual address is treated exactly like a network with a mask of
3382+255.255.255.255.  Addresses of interfaces found on the system are
3383+added with their individual addresses.  In addition, these addresses
3384+are implicitly added:
3385+
3386+    0.0.0.0
3387+    127.0.0.0/255.0.0.0
3388+
3389+So the list of system addresses (the "ipme" list) is, in order,
3390+127.0.0.0/255.0.0.0, 0.0.0.0, then all actual interfaces on the system
3391+in the order they are reported, then the contents of the "moreipme"
3392+file.  The list of excluded addresses (the "notipme" list) is just the
3393+contents of the "notipme" file.
3394+
3395+If an address appears in both the ipme list and the notipme list, the
3396+entry with the longest netmask wins.  If the netmask lengths are the
3397+same, notipme wins.
3398+
3399+For example, if the ipme list has 127.0.0.0/255.0.0.0 and notipme has
3400+127.0.0.2, then 127.0.0.2 will not be considered me because the entry
3401+in notipme has a 32-bit mask.  If the notipme list has
3402+127.0.0.0/255.0.0.0, all of 127.* will not be considered me.
3403+
3404+You can run the program "ipmeprint" from the source directory to see
3405+what interfaces qmail is detecting or finds in moreipme.
3406+
3407+You can run the program "ipmetest" from the source directory to test
3408+your configuration.  It takes as its first and only parameter an IP
3409+address to test, and prints either "me" or "not me".
3410+
3411+###########
3412+### HOW ###
3413+###########
3414+
3415+To apply the patch, download and save it somewhere, then cd into your
3416+qmail source directory.
3417+
3418+For stock qmail, download qmail-1.03-moreipme-0.6.patch then run:
3419+
3420+    cd qmail-1.03
3421+    patch -p1 </path/to/qmail-1.03-moreipme-0.6.patch
3422+
3423+For netqmail, first download netqmail-1.05, and run the included
3424+collate.sh script.  Then download netqmail-1.05-moreipme-0.6.patch and
3425+apply it to the netqmail base directory, after runn:
3426+
3427+    cd netqmail-1.05
3428+    patch -p1 </path/to/netqmail-1.05-moreipme-0.6.patch
3429+
3430+
3431+###################
3432+### OTHER NOTES ###
3433+###################
3434+
3435+This patch also incorporates the "0.0.0.0" patch, which causes qmail
3436+to recognize the IP address 0.0.0.0 as a local address.  See:
3437+
3438+    http://www.suspectclass.com/~sgifford/qmail/qmail-0.0.0.0.README
3439+
3440+for more information, and
3441+
3442+    http://www.suspectclass.com/~sgifford/qmail/qmail-0.0.0.0.patch
3443+
3444+for a copy of the patch.
3445+
3446+
3447+###############
3448+### HISTORY ###
3449+###############
3450+
3451+2004 May 22 - Patch version 0.6 released.  Fix from Richard Dawe where
3452+              masks weren't handled properly, removed some dead code,
3453+              updated comments and docs.  Produce a copy of the patch
3454+              for netqmail-1.05.
3455+
3456+2003 Apr 29 - Patch version 0.5 released.  Added support for netmasks
3457+              in moreipme and notipme, ipmetest utility, 127/8 as
3458+              implicit ipme.
3459+
3460+2002 Apr 26 - Patch version 0.4 released.  Includes support for "notipme" file.
3461+              Many other small fixes and cleanups.  Fixes ipmeprint to
3462+              chdir(/var/qmail) before running.
3463+
3464+2001 Oct  8 - Original release of patch.
3465+
3466+2001 Jan 22 - (roughly) Original release of 0.0.0.0 patch.
3467+
3468diff -ruN ../netqmail-1.06-original/README.qregex netqmail-1.06/README.qregex
3469--- ../netqmail-1.06-original/README.qregex     1970-01-01 01:00:00.000000000 +0100
3470+++ netqmail-1.06/README.qregex 2019-02-27 20:57:13.380025180 +0100
3471@@ -0,0 +1,203 @@
3472+QREGEX (v2) 20060423 - README April 23, 2006
3473+A Regular Expression matching patch for qmail 1.03 and netqmail
3474+
3475+
3476+OVERVIEW:
3477+
3478+qregex adds the ability to match address evelopes via Regular Expressions (REs)
3479+in the qmail-smtpd process. It has the abiltiy to match `helo/ehlo` (host name),
3480+`mail from` (envelope sender), and `rcpt to` (envelope recipient) commands.
3481+It follows all the base rules that are set out with qmail (ie using control
3482+files) so it makes for easy integretion into an existing setup (see the
3483+install instructions for more info). The v2 is specified because qregex was
3484+re-written to better conform to the security guarantee set forth by the author
3485+of qmail. The original version used stdio.h and stdlib.h for reading the
3486+control files whereas v2 now uses all stralloc functions which are much more
3487+regulated against buffer overruns and the like.
3488+See: http://cr.yp.to/qmail/guarantee.html
3489+
3490+
3491+FEATURES:
3492+
3493+Features of qregex include:
3494+
3495+1. Performs pattern matching on envelope senders and envelope
3496+   recipients against REs in the badmailfrom and badmailto control
3497+   files. Two additional control files, badmailfromnorelay and
3498+   badmailtonorelay, are used for pattern matching when the
3499+   RELAYCLIENT environment variable is not set.
3500+
3501+2. Performs pattern matching on the helo/ehlo host name. Setting the
3502+   NOBADHELO environment variable prevents the host name from being
3503+   compared to the patterns in the badhelo control file.
3504+
3505+3. Matches to patterns are logged. Setting the LOGREGEX environment
3506+   variable causes the matched regex pattern to be included in the log.
3507+
3508+4. Matching is case insensitive.
3509+
3510+5. qregex ignores empty envelope senders. An empty envelope sender is not
3511+   compared to the patterns in the badmailfrom and badmailfromnorelay
3512+   control files and is always accepted.
3513+
3514+
3515+PLATFORMS:
3516+
3517+qregex has been built and tested on the following platforms. I'm sure it won't
3518+have any problems on any platform that qmail will run on (providing they have
3519+a regex interface) but if you run into problems let me know.
3520+
3521+       - OpenBSD 3.x
3522+       - FreeBSD 4.x, 5.x
3523+       - Mandrake Linux 9.x
3524+       - SuSE Linux 8.x
3525+
3526+
3527+
3528+INSTALLATION INSTRUCTIONS:
3529+
3530+Installation is very simple, there is only one requirement. You need to use the
3531+GNU version of the patch utility (http://www.gnu.org/software/patch/patch.html).
3532+(For Solaris 8 users it is installed as 'gpatch')
3533+
3534+- If this is a new setup.
3535+Unpack the qmail archive, cd into the qmail-1.03 directory and run
3536+"patch < /path/to/qregex-<version>.patch". Follow the instructions as per the
3537+included qmail INSTALL file. Once you are done come back to this file and read
3538+the section on the control files.
3539+
3540+If you are using netqmail, then unpack the netqmail archive. Run the collate.sh
3541+script and cd into the resulting netqmail-<version> directory. From there, run
3542+"patch < /path/to/qregex-<version>.patch". Complete the netqmail installation
3543+normally. Once you are done, come back to this file and read the section on the
3544+control files.
3545+
3546+- If this is an existing setup.
3547+FIRST: create your control files (see below).
3548+cd into your existing qmail or netqmail source directory. Run
3549+"patch < /path/to/qregex-<version>.patch" then "make qmail-smtpd". Now run
3550+./qmail-smtpd and test your new rules to make sure they work as expected.
3551+
3552+Install the new binary by cd'ing to /var/qmail/bin and as root (in one command)
3553+copy the existing binary to 'qmail-smtpd.old' and copy the new binary from the
3554+source directory to 'qmail-smtpd'.
3555+(ex. cp qmail-smtpd qmail-smtpd.old && cp ~/qmail-1.03/qmail-smtpd qmail-smtpd)
3556+
3557+You can also optionally just run "make setup check" as it will install the
3558+updated documentation and man pages provided with this patch. Stopping qmail
3559+before doing the "make setup check" is always a good idea.
3560+
3561+
3562+LOGGING:
3563+
3564+qregex will log matches to the patterns in the various control files. Log
3565+messages will take these three forms depending on which control file was
3566+matched:
3567+
3568+badhelo
3569+qmail-smtpd: badhelo: <host> at <remote IP>
3570+
3571+badmailfrom and badmailfromnorelay
3572+qmail-smtpd: badmailfrom: <sender address> at <remote IP>
3573+
3574+badmailto and badmailtonorelay
3575+qmail-smtpd: badmailto: <rcpt address> at <remote IP>
3576+
3577+When the LOGREGEX environment variable is set, the matched pattern will
3578+be included in the log. Log messages will have the regex pattern appended
3579+to them. For example, a badhelo log message will look like this:
3580+
3581+qmail-smtpd: badhelo: <host> at <remote IP> matches pattern: <regex>
3582+
3583+
3584+CONTROL FILES:
3585+
3586+qregex provides you with five control files. None of these control files
3587+is mandatory and you can use them in any combination you choose in your setup.
3588+
3589+The "control/badmailfrom" and "control/badmailto" files contain your REs for
3590+matching against the 'mail from' (envelope sender) and 'rcpt to' (envelope
3591+recipient) smtp commands respectively.
3592+The "control/badmailfromnorelay" and "control/badmailtonorelay" match against
3593+the same commands but are read only when the RELAYCLIENT environment variable
3594+is not set.
3595+The "control/badhelo" file matches against the 'helo/ehlo' smtp command.
3596+
3597+If you prefer you can symlink the badmailfrom and badmailto control files
3598+(ln -s badmailfrom badmailto) and maintain fewer sets of rules. Beware
3599+this might cause problems in certain setups.
3600+       
3601+       Here's an example "badhelo" file.
3602+       -----------------------------------
3603+       # block host strings with no dot (not a FQDN)
3604+       !\.
3605+       -----------------------------------
3606+       
3607+       An example "badmailfrom" file.
3608+       -----------------------------------
3609+       # this will drop everything containing the string
3610+       # bad.domain.com or Bad.Domain.Com or BAD.domain.COM
3611+       bad\.domain\.com
3612+       # force users to fully qualify themselves
3613+       # (i.e. deny "user", accept "user@domain")
3614+       !@
3615+       -----------------------------------
3616+
3617+       And "badmailto" (a little more interesting)
3618+       -----------------------------------
3619+       # must not contain invalid characters, brakets or multiple @'s
3620+       [!%#:*^(){}]
3621+       @.*@
3622+       -----------------------------------
3623+
3624+You can use the non-RE character '!' to start an RE as a signal to qregex to
3625+negate the action. As used above in the badmailfrom file, by negating the '@'
3626+symbol qregex will signal qmail-smtpd to deny the 'mail from' command whenever
3627+the address doesn't contain an @ symbol. When used inside a bracket expression,
3628+the '!' character looses this special meaning. This is shown in the badmailto
3629+example.
3630+
3631+The norelay control files follow the same rules as the other control files but
3632+are intended to address two specific scenarios.
3633+The badmailfromnorelay file can be used to block mail trying to spoof a domain
3634+hosted on your mail server. It prevents a mail client that is not allowed to
3635+relay email through your server from using one of your hosted domains as its
3636+envelope sender.
3637+The badmailtonorelay file can be used to create email addresses that cannot
3638+receive mail from any source not allowed to relay email through your server.
3639+This is handy for creating email addresses for use only within your own
3640+domain(s) that can't receive spam from the world at large.
3641+
3642+
3643+INTERNALS:
3644+
3645+qregex (or regexmatch as the function is called) will be called during the
3646+`helo/ehlo`, `rcpt to` and `mail from` handling routines in "qmail-smtpd.c".
3647+When called, it will read the proper control file then one by one compile and
3648+execute the regex on the string passed into qmail-smtpd. If the regex matches
3649+it returns TRUE (1) and the qmail-smtpd process will deny the user the ability
3650+to continue. If you change anything and think it betters this patch please
3651+send me a new diff file so I can take a peek.
3652+
3653+
3654+CONTACT:
3655+qregex is maintained by:
3656+       Andrew St. Jean
3657+       andrew@arda.homeunix.net
3658+       www.arda.homeunix.net/store/qmail/
3659+
3660+Contributers to qregex:
3661+       Jeremy Kitchen 
3662+       kitchen at scriptkitchen dot com
3663+       http://www.scriptkitchen.com/qmail
3664+
3665+       Alex Pleiner
3666+       alex@zeitform.de
3667+       zeitform Internet Dienste
3668+       http://www.zeitform.de/
3669+
3670+       Thanos Massias
3671+
3672+Original qregex patch written by:
3673+       Evan Borgstrom
3674+       evan at unixpimps dot org
3675diff -ruN ../netqmail-1.06-original/README.rfc2821 netqmail-1.06/README.rfc2821
3676--- ../netqmail-1.06-original/README.rfc2821    1970-01-01 01:00:00.000000000 +0100
3677+++ netqmail-1.06/README.rfc2821        2019-02-27 20:57:13.380025180 +0100
3678@@ -0,0 +1,39 @@
3679+This patch is Copyright (C) 2002 - 2003 by Matthias Andree. License below.
3680+
3681+(this is the 2nd edition of this patch and the 4th edition of the
3682+introductory text)
3683+
3684+This patch changes qmail-remote to skip over MX servers that greet with
3685+codes 400 to 499 and to bounce mail when any MX server that qmail tries
3686+greets with a code 500 to 599.
3687+
3688+If you want qmail-remote to skip over hosts greeting with 5XX-codes and
3689+try the next MX for real RFC-2821 compliance (Sendmail and Postfix do
3690+that), change the
3691+
3692+  if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed");
3693+
3694+to
3695+
3696+  if (code >= 500 && code < 600) return;
3697+
3698+License:
3699+
3700+Permission is hereby granted, free of charge, to any person obtaining a
3701+copy of this software and associated documentation files (the
3702+"Software"), to deal in the Software without restriction, including
3703+without limitation the rights to use, copy, modify, merge, publish,
3704+distribute, sublicense, and/or sell copies of the Software, and to
3705+permit persons to whom the Software is furnished to do so, subject to
3706+the following conditions:
3707+
3708+The above copyright notice and this permission notice shall be included
3709+in all copies or substantial portions of the Software.
3710+
3711+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
3712+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
3713+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
3714+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
3715+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
3716+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
3717+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3718diff -ruN ../netqmail-1.06-original/README.srs netqmail-1.06/README.srs
3719--- ../netqmail-1.06-original/README.srs        1970-01-01 01:00:00.000000000 +0100
3720+++ netqmail-1.06/README.srs    2019-02-27 20:57:13.381025169 +0100
3721@@ -0,0 +1,93 @@
3722+qmail SRS patch
3723+
3724+This is a SRS (Sender Rewriting Scheme) implementation for qmail using libsrs2.
3725+
3726+Current version: qmail-srs-0.8.patch
3727+Changes
3728+
3729+    2011-03-30 (0.8):
3730+        Fixed bug reading configuration files.
3731+    2007-06-05 (0.7):
3732+        New QMAILINJECT_FORCE_SRS and QMAILINJECT_SKIP_SRS environment variables can force or skip envelope rewriting in qmail-inject.
3733+    2007-05-31 (0.6):
3734+        qmail-inject only will rewrite envelope if EXT and HOST variables are set.
3735+        Fixed bug in qmail-send handling chdir() calls (Special Thanks to Werner Fleck).
3736+    2007-01-11 (0.5):
3737+        Added parameters srs_separator and srs_alwaysrewrite from libsrs2.
3738+    2007-01-10 (0.4):
3739+        If srs_domain is empty or not set, SRS is disabled.
3740+    2006-12-18 (0.3):
3741+        forward and condredirect: modified to work with SRS.
3742+    2006-12-17 (0.2):
3743+        srsfilter: now rewrites To header with the SRS decoded address.
3744+        srsfilter: only accepts messages from null-sender, Return-Path: <>.
3745+        srsfilter: modified to reject messages without body.
3746+        qmail-inject: error message detailed.
3747+        If optional parameters are not set, will use libsrs2 defaults.
3748+        Install instructions revised.
3749+    2006-12-15 (0.1):
3750+        First release.
3751+
3752+Install instructions
3753+
3754+    Download and install libsrs2 from http://www.libsrs2.org/download.html.
3755+    Download qmail-srs-0.8.patch.
3756+    Apply this patch:
3757+
3758+    tar -xzf /path/to/qmail-1.03.tar.gz
3759+    cd qmail-1.03
3760+    patch -p1 < qmail-srs-0.8.patch
3761+    And follow your qmail instalation (config, make, make setup check, ...)
3762+    Configure some parameters in /var/qmail/control.
3763+
3764+    Required parameters:
3765+
3766+    echo srs.YOURDOMAIN > /var/qmail/control/srs_domain
3767+    echo SECRET > /var/qmail/control/srs_secrets
3768+
3769+    YOURDOMAIN: Replace with your domain name, e.g. srs.foo-bar.com
3770+    SECRET: Replace with a random string
3771+
3772+    Important! You MUST create a MX record for srs.YOURDOMAIN pointing to your server.
3773+
3774+    Optional parameters:
3775+
3776+    echo 7 > /var/qmail/control/srs_maxage
3777+    echo 4 > /var/qmail/control/srs_hashlength
3778+    echo 4 > /var/qmail/control/srs_hashmin
3779+    echo = > /var/qmail/control/srs_separator
3780+    echo 0 > /var/qmail/control/srs_alwaysrewrite
3781+    Configure your SRS domain.
3782+
3783+    echo srs.YOURDOMAIN >> /var/qmail/control/rcpthosts
3784+    echo srs.YOURDOMAIN:srs >> /var/qmail/control/virtualdomains
3785+    echo "| /var/qmail/bin/srsfilter" > /var/qmail/alias/.qmail-srs-default
3786+
3787+    YOURDOMAIN: Replace with your domain name, e.g. srs.foo-bar.com.
3788+
3789+Configuration Parameters
3790+
3791+Parameters in bold are required.
3792+Parameter      Description     Example
3793+srs_domain     A domain to use in rewritten addresses. If not set, SRS is disabled.    srs.foo-bar.com
3794+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
3795+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
3796+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
3797+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
3798+srs_separator  The separator to appear immediately after SRS[01] in rewritten addresses. This must be -, + or =. Default value is =.   =
3799+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
3800+Environment Variables (qmail-inject only)
3801+
3802+By default, this patch modifies qmail-inject to rewrite the envelope sender only if EXT and HOST variables are set.
3803+
3804+You can change this behavior using the following environment variables:
3805+
3806+    QMAILINJECT_FORCE_SRS: qmail-inject will call srsforward() even if EXT and HOST are not set.
3807+    QMAILINJECT_SKIP_SRS: qmail-inject will not call srsforward() even if EXT and HOST are set.
3808+
3809+More about SRS
3810+
3811+    http://www.openspf.org/SRS
3812+    http://www.libsrs2.org/
3813+    http://wooledge.org/~greg/qmail-srs.html
3814+
3815diff -ruN ../netqmail-1.06-original/README.surbl netqmail-1.06/README.surbl
3816--- ../netqmail-1.06-original/README.surbl      1970-01-01 01:00:00.000000000 +0100
3817+++ netqmail-1.06/README.surbl  2019-02-27 20:57:13.381025169 +0100
3818@@ -0,0 +1,34 @@
3819+SURBL filter for netqmail
3820+=========================
3821+surblfilter reads an rfc822 email on stdin and prints it back on stdout.
3822+It extracts URL and checks them against SURBL. surblfilter can be used
3823+as a filter using qmail-qfilter(1) or as a qmail-queue(8) frontend by
3824+setting QMAILQUEUE environment variable to a wrapper
3825+/var/qmail/bin/surblqueue
3826+
3827+surbfilter caches the result in /var/qmail/control/cache. qmail-smtpd
3828+needs to have write permission for this directory.
3829+
3830+surbfilter does base64 decoding for base64 encoded text/html, text/plain
3831+to extract urls.
3832+
3833+surblfilter requires two control files level2-tlds and level3-tlds in
3834+/var/qmail/control. The same can be obtained from surbl.org website
3835+
3836+http://www.surbl.org/tld/three-level-tlds
3837+http://www.surbl.org/tld/two-level-tlds
3838+
3839+surbfilter is a standone program and does not need you to patch qmail-smtpd
3840+or existing programs. It exits 88 in case it finds a SURBL listed URL. It
3841+exits 111 for temporary errors. It exits 0 if everyting is fine.
3842+
3843+surblfilter has been integrated with my qmail-dkim patch and is avaiable at
3844+
3845+https://sourceforge.net/projects/indimail/files/netqmail-addons/qmail-dkim-1.0/
3846+
3847+It has been integrated in dkim-netqmail-1.06.patch-1.9.gz. The older patches
3848+will not have surblfilter
3849+
3850+surbfilter is built on djb functions and some functions have been ruthlessly
3851+borrowed from qmail surbl interface by Pieter Droogendijk and the surblhost
3852+program at http://surblhost.sourceforge.net/
3853diff -ruN ../netqmail-1.06-original/README.tap netqmail-1.06/README.tap
3854--- ../netqmail-1.06-original/README.tap        1970-01-01 01:00:00.000000000 +0100
3855+++ netqmail-1.06/README.tap    2019-02-27 20:57:13.381025169 +0100
3856@@ -0,0 +1,34 @@
3857+qmail provides the ability to make a copy of each email that flows through the system.
3858+This is done using the QUEUE_EXTRA code. See qmail FAQ #8.2
3859+
3860+The qmail tap patch adds additional functionality:
3861+1) Specify which email addresses to tap using a regex style control file. With the
3862+   regex function, you can specify full domains or individual email addresses.
3863+
3864+2) Specify which email address to send the emails to.
3865+
3866+3) Qmail does not need to be restated to when the taps control file is changed.
3867+
3868+The regex match is applied to both the to and from email addresses. So email
3869+sent to or from the addresses will be copied. Matching is case insensitive.
3870+If there are multiple matches, the first match is used.
3871+
3872+The queue tap patch adds a new control file:
3873+
3874+/var/qmail/control/taps
3875+Contains a regex style list of addresses to tap and the email
3876+address of where you want the copy sent to.
3877+
3878+Examples:
3879+a) To tap a whole domain add a line like:
3880+A:.*@domain.com:joe@example.com
3881+
3882+
3883+b) To tap an individual email address add a line like:
3884+A:user@domain.com:other@example.com
3885+
3886+c) To tap messages going to a domain add a line like:
3887+T:.*@domain.com:joe@example.com
3888+
3889+d) To tap messages comming from a domain add a line like:
3890+F:.*@domain.com:joe@example.com
3891diff -ruN ../netqmail-1.06-original/README.tls netqmail-1.06/README.tls
3892--- ../netqmail-1.06-original/README.tls        1970-01-01 01:00:00.000000000 +0100
3893+++ netqmail-1.06/README.tls    2020-01-10 21:46:22.909448586 +0100
3894@@ -0,0 +1,98 @@
3895+Frederik Vermeulen <qmail-tls akrul inoa.net> 20200107
3896+http://inoa.net/qmail-tls/
3897+
3898+This patch implements RFC 3207 in qmail.
3899+This means you can get SSL or TLS encrypted and
3900+authenticated SMTP between the MTAs and from MUA to MTA.
3901+The code is considered experimental (but has worked for
3902+many since its first release on 1999-03-21).
3903+
3904+Usage: - install OpenSSL-1.1.0 http://www.openssl.org/ or later
3905+         (any version since 0.9.8 is presumed to work)
3906+       - apply patch to netqmail-1.06 http://www.usenix.org.uk/mirrors/qmail/netqmail
3907+         The patches to qmail-remote.c and qmail-smtpd.c can be applied
3908+         separately.
3909+       - provide a server certificate in /var/qmail/control/servercert.pem.
3910+         "make cert" makes a self-signed certificate.
3911+         "make cert-req" makes a certificate request.
3912+         Note: you can add the CA certificate and intermediate
3913+         certs to the end of servercert.pem.
3914+       - replace qmail-smtpd and/or qmail-remote binary
3915+       - verify operation (header information should show
3916+         something like
3917+         "Received [..] with (DHE-RSA-AES256-SHA encrypted) SMTP;")
3918+
3919+Optional: - when DEBUG is defined, some extra TLS info will be logged
3920+          - qmail-remote will authenticate with the certificate in
3921+            /var/qmail/control/clientcert.pem. By preference this is
3922+            the same as servercert.pem, where nsCertType should be
3923+            == server,client or be a generic certificate (no usage specified).
3924+          - when a 2048 bit RSA key is provided in /var/qmail/control/rsa2048.pem,
3925+            this key will be used instead of (slow) on-the-fly generation by
3926+            qmail-smtpd. Idem for 2048 DH param in control/dh2048.pem.
3927+            `make tmprsadh` does this.
3928+            Periodical replacement can be done by crontab:
3929+            01 01 * * * /var/qmail/bin/update_tmprsadh > /dev/null 2>&1
3930+          - server authentication:
3931+            qmail-remote requires authentication from servers for which
3932+            /var/qmail/control/tlshosts/host.dom.ain.pem exists.
3933+            The .pem file contains the validating CA certificates.
3934+            One of the dNSName or the CommonName attributes have to match.
3935+            WARNING: this option may cause mail to be delayed, bounced,
3936+            doublebounced, and lost.
3937+            If /var/qmail/control/tlshosts/exhaustivelist is present,
3938+            the lists of hosts in /var/qmail/control/tlshosts is
3939+            an exhaustive list of hosts TLS is tried on.
3940+            If /var/qmail/control/notlshosts/host.dom.ain is present,
3941+            no TLS is tried on this host.
3942+          - client authentication:
3943+            when relay rules would reject an incoming mail,
3944+            qmail-smtpd can allow the mail based on a presented cert.
3945+            Certs are verified against a CA list in
3946+            /var/qmail/control/clientca.pem (eg. from
3947+            http://curl.haxx.se/ca/cacert.pem)
3948+            and the cert email-address has to match a line in
3949+            /var/qmail/control/tlsclients. This email-address is logged
3950+            in the headers. CRLs can be provided through
3951+            /var/qmail/control/clientcrl.pem.
3952+          - cipher selection:
3953+            qmail-remote:
3954+              openssl cipher string (`man ciphers`) read from
3955+              /var/qmail/control/tlsclientciphers
3956+            qmail-smtpd:
3957+              openssl cipher string read from TLSCIPHERS environment variable
3958+              (can vary based on client IP address e.g.)
3959+              or if that is not available /var/qmail/control/tlsserverciphers
3960+          - smtps (deprecated SMTP over TLS via port 465):
3961+            qmail-remote: when connecting to port 465
3962+            qmail-smtpd: when SMTPS environment variable is not empty
3963+
3964+Caveats: - do a `make clean` after patching
3965+         - binaries dynamically linked with current openssl versions need
3966+           recompilation when the shared openssl libs are upgraded.
3967+         - this patch could conflict with other patches (notably those
3968+           replacing \n with \r\n, which is a bad idea on encrypted links).
3969+         - needs working /dev/urandom (or EGD for openssl versions >0.9.7)
3970+           for seeding random number generator.
3971+         - packagers should make sure that installing without a valid
3972+           servercert is impossible
3973+         - when applied in combination with AUTH patch, AUTH patch
3974+           should be applied first and first part of this patch
3975+           will fail. This error can be ignored. Packagers should
3976+           cut the first 12 lines of this patch to make a happy
3977+           patch
3978+         - `make tmprsadh` is recommended (or should I say required),
3979+           otherwise DH generation can be unpredictably slow
3980+         - some need "-I/usr/kerberos/include" to be added in conf-cc
3981+
3982+Copyright: GPL
3983+           Links with OpenSSL
3984+           Inspiration and code from examples in SSLeay (E. Young
3985+           <eay@cryptsoft.com> and T. Hudson <tjh@cryptsoft.com>),
3986+           stunnel (M. Trojnara <mtrojnar@ddc.daewoo.com.pl>),
3987+           Postfix/TLS (L. Jaenicke <Lutz.Jaenicke@aet.tu-cottbus.de>),
3988+           modssl (R. Engelschall <rse@engelschall.com>),
3989+           openssl examples of E. Rescorla <ekr@rtfm.com>.
3990+
3991+Bug reports: mailto:<qmail-tls akrul inoa.net>
3992+
3993diff -ruN ../netqmail-1.06-original/TARGETS netqmail-1.06/TARGETS
3994--- ../netqmail-1.06-original/TARGETS   1998-06-15 12:53:16.000000000 +0200
3995+++ netqmail-1.06/TARGETS       2019-06-26 16:45:45.017718421 +0200
3996@@ -1,3 +1,4 @@
3997+dktest
3998 auto-ccld.sh
3999 make-load
4000 find-systype
4001@@ -10,11 +11,20 @@
4002 qmail.o
4003 quote.o
4004 now.o
4005+base64.o
4006 gfrom.o
4007 myctime.o
4008 slurpclose.o
4009 make-makelib
4010 makelib
4011+maildirflags.o
4012+maildirparsequota.o
4013+maildiropen.o
4014+maildirgetquota.o
4015+maildirquota.o
4016+overmaildirquota.o
4017+strtimet.o
4018+strpidt.o
4019 case_diffb.o
4020 case_diffs.o
4021 case_lowerb.o
4022@@ -100,11 +110,14 @@
4023 str_diff.o
4024 str_diffn.o
4025 str_cpy.o
4026+str_cpyb.o
4027 str_chr.o
4028 str_rchr.o
4029 str_start.o
4030 byte_chr.o
4031 byte_rchr.o
4032+byte_cspn.o
4033+byte_rcspn.o
4034 byte_diff.o
4035 byte_copy.o
4036 byte_cr.o
4037@@ -158,6 +171,8 @@
4038 auto_uids.o
4039 qmail-lspawn
4040 qmail-getpw.o
4041+qmail-newmvrt.o
4042+qmail-newmvrt
4043 auto_break.c
4044 auto_break.o
4045 auto_usera.c
4046@@ -168,11 +183,19 @@
4047 constmap.o
4048 timeoutread.o
4049 timeoutwrite.o
4050+tls.o
4051+ssl_timeoutio.o
4052 timeoutconn.o
4053 tcpto.o
4054 dns.o
4055+srsfilter
4056+srsfilter.o
4057+srs
4058+srs.o
4059+spf.o
4060 ip.o
4061 ipalloc.o
4062+strsalloc.o
4063 hassalen.h
4064 ipme.o
4065 ndelay.o
4066@@ -182,6 +205,8 @@
4067 qmail-remote
4068 qmail-rspawn.o
4069 tcpto_clean.o
4070+md5c.o
4071+hmac_md5.o
4072 qmail-rspawn
4073 direntry.h
4074 qmail-clean.o
4075@@ -212,6 +237,9 @@
4076 headerbody.o
4077 hfield.o
4078 token822.o
4079+spf.o
4080+spfquery.o
4081+spfquery
4082 qmail-inject
4083 predate.o
4084 predate
4085@@ -270,12 +298,16 @@
4086 dnsip
4087 dnsmxip.o
4088 dnsmxip
4089+dnstxt.o
4090+dnstxt
4091 dnsfq.o
4092 dnsfq
4093 hostname.o
4094 hostname
4095 ipmeprint.o
4096 ipmeprint
4097+ipmetest.o
4098+ipmetest
4099 qreceipt.o
4100 qreceipt
4101 qsmhook.o
4102@@ -320,6 +352,7 @@
4103 binm2+df
4104 binm3
4105 binm3+df
4106+Makefile-cert
4107 it
4108 qmail-local.0
4109 qmail-lspawn.0
4110@@ -382,6 +415,54 @@
4111 addresses.0
4112 envelopes.0
4113 forgeries.0
4114+policy.o
4115 man
4116 setup
4117 check
4118+qmail-todo.o
4119+qmail-todo
4120+update_tmprsadh
4121+chkuser.o
4122+qmail-dk
4123+qmail-dkim
4124+qmail-dkim.o
4125+qmail-dk.o
4126+libdkim.a
4127+dkimbase.o
4128+dkimdns.o
4129+dkim.o
4130+dkimsign.o
4131+dkimverify.o
4132+dkim
4133+dkim.o
4134+dkim.8
4135+qmail-dkim.8
4136+qmail-dkim.0
4137+str_cpyb.o
4138+dkimfuncs.o
4139+MakeArgs.o
4140+spawn-filter spawn-filter.o qregex.o wildmat.o
4141+spawn-filter.8
4142+qmail-dk.0 spawn-filter.0
4143+dk-filter echo.o echo dk-filter.0 dk-filter.8
4144+case_startb.o
4145+mess822_ok.o
4146+scan_xlong.o
4147+socket_v4mappedprefix.o
4148+socket_v6any.o
4149+str_cspn.o
4150+surblfilter.o
4151+surblfilter
4152+surblfilter.8
4153+uint64.h
4154+surblqueue
4155+surblfilter.0
4156+base64sub.o
4157+qmail-dk.8
4158+dktest.o
4159+dktrace.o
4160+dknewkey
4161+dktest.8
4162+time_t_size.h
4163+channels.h
4164+
4165diff -ruN ../netqmail-1.06-original/alloc.c netqmail-1.06/alloc.c
4166--- ../netqmail-1.06-original/alloc.c   1998-06-15 12:53:16.000000000 +0200
4167+++ netqmail-1.06/alloc.c       2020-07-27 13:06:47.995674192 +0200
4168@@ -1,6 +1,7 @@
4169+#include <limits.h>
4170 #include "alloc.h"
4171 #include "error.h"
4172-extern char *malloc();
4173+extern void *malloc();
4174 extern void free();
4175 
4176 #define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */
4177@@ -15,6 +16,10 @@
4178 unsigned int n;
4179 {
4180   char *x;
4181+  if (n >= (INT_MAX >> 3)) {
4182+    errno = error_nomem;
4183+    return 0;
4184+  }
4185   n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */
4186   if (n <= avail) { avail -= n; return space + avail; }
4187   x = malloc(n);
4188diff -ruN ../netqmail-1.06-original/base64.c netqmail-1.06/base64.c
4189--- ../netqmail-1.06-original/base64.c  1970-01-01 01:00:00.000000000 +0100
4190+++ netqmail-1.06/base64.c      2019-02-27 20:57:13.381025169 +0100
4191@@ -0,0 +1,124 @@
4192+#include "base64.h"
4193+#include "stralloc.h"
4194+#include "substdio.h"
4195+#include "str.h"
4196+
4197+static char *b64alpha =
4198+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4199+#define B64PAD '='
4200+
4201+/* returns 0 ok, 1 illegal, -1 problem */
4202+
4203+int b64decode(in,l,out)
4204+const unsigned char *in;
4205+int l;
4206+stralloc *out; /* not null terminated */
4207+{
4208+  int p = 0;
4209+  int n;
4210+  unsigned int x;
4211+  int i, j;
4212+  char *s;
4213+  unsigned char b[3];
4214+
4215+  if (l == 0)
4216+  {
4217+    if (!stralloc_copys(out,"")) return -1;
4218+    return 0;
4219+  }
4220+
4221+  while(in[l-1] == B64PAD) {
4222+    p ++;
4223+    l--;
4224+  }
4225+
4226+  n = (l + p) / 4;
4227+  i = (n * 3) - p;
4228+  if (!stralloc_ready(out,i)) return -1;
4229+  out->len = i;
4230+  s = out->s;
4231+
4232+  for(i = 0; i < n - 1 ; i++) {
4233+    x = 0;
4234+    for(j = 0; j < 4; j++) {
4235+      if(in[j] >= 'A' && in[j] <= 'Z')
4236+        x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
4237+      else if(in[j] >= 'a' && in[j] <= 'z')
4238+        x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
4239+      else if(in[j] >= '0' && in[j] <= '9')
4240+        x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
4241+      else if(in[j] == '+')
4242+        x = (x << 6) + 62;
4243+      else if(in[j] == '/')
4244+        x = (x << 6) + 63;
4245+      else if(in[j] == '=')
4246+        x = (x << 6);
4247+    }
4248+
4249+    s[2] = (unsigned char)(x & 255); x >>= 8;
4250+    s[1] = (unsigned char)(x & 255); x >>= 8;
4251+    s[0] = (unsigned char)(x & 255); x >>= 8;
4252+    s += 3; in += 4;
4253+  }
4254+
4255+  x = 0;
4256+  for(j = 0; j < 4; j++) {
4257+    if(in[j] >= 'A' && in[j] <= 'Z')
4258+      x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
4259+    else if(in[j] >= 'a' && in[j] <= 'z')
4260+      x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
4261+    else if(in[j] >= '0' && in[j] <= '9')
4262+      x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
4263+    else if(in[j] == '+')
4264+      x = (x << 6) + 62;
4265+    else if(in[j] == '/')
4266+      x = (x << 6) + 63;
4267+    else if(in[j] == '=')
4268+      x = (x << 6);
4269+  }
4270+
4271+  b[2] = (unsigned char)(x & 255); x >>= 8;
4272+  b[1] = (unsigned char)(x & 255); x >>= 8;
4273+  b[0] = (unsigned char)(x & 255); x >>= 8;
4274+
4275+  for(i = 0; i < 3 - p; i++)
4276+    s[i] = b[i];
4277+
4278+  return 0;
4279+}
4280+
4281+int b64encode(in,out)
4282+stralloc *in;
4283+stralloc *out; /* not null terminated */
4284+{
4285+  unsigned char a, b, c;
4286+  int i;
4287+  char *s;
4288+
4289+  if (in->len == 0)
4290+  {
4291+    if (!stralloc_copys(out,"")) return -1;
4292+    return 0;
4293+  }
4294+
4295+  i = in->len / 3 * 4 + 4;   
4296+  if (!stralloc_ready(out,i)) return -1;
4297+  s = out->s;
4298+
4299+  for (i = 0;i < in->len;i += 3) {
4300+    a = in->s[i];
4301+    b = i + 1 < in->len ? in->s[i + 1] : 0;
4302+    c = i + 2 < in->len ? in->s[i + 2] : 0;
4303+
4304+    *s++ = b64alpha[a >> 2];
4305+    *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)];
4306+
4307+    if (i + 1 >= in->len) *s++ = B64PAD;
4308+    else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
4309+
4310+    if (i + 2 >= in->len) *s++ = B64PAD;
4311+    else *s++ = b64alpha[c & 63];
4312+  }
4313+  out->len = s - out->s;
4314+  return 0;
4315+}
4316diff -ruN ../netqmail-1.06-original/base64.h netqmail-1.06/base64.h
4317--- ../netqmail-1.06-original/base64.h  1970-01-01 01:00:00.000000000 +0100
4318+++ netqmail-1.06/base64.h      2019-02-27 20:57:13.381025169 +0100
4319@@ -0,0 +1,13 @@
4320+#ifndef BASE64_H
4321+#define BASE64_H
4322+
4323+/* DKIM-1.10 */
4324+#include "stralloc.h"
4325+extern int b64decode(const unsigned char *, int, stralloc *);
4326+extern int b64encode(stralloc *, stralloc *);
4327+/* end DKIM-1.10 */
4328+
4329+extern int b64decode();
4330+extern int b64encode();
4331+
4332+#endif
4333diff -ruN ../netqmail-1.06-original/base64sub.c netqmail-1.06/base64sub.c
4334--- ../netqmail-1.06-original/base64sub.c       1970-01-01 01:00:00.000000000 +0100
4335+++ netqmail-1.06/base64sub.c   2019-02-27 20:57:13.382025158 +0100
4336@@ -0,0 +1,170 @@
4337+/*
4338+ * $Log: base64sub.c,v $
4339+ * Revision 1.6  2010-03-03 09:33:16+05:30  Cprogrammer
4340+ * renamed base64 to base64sub
4341+ *
4342+ * Revision 1.5  2004-10-22 20:18:37+05:30  Cprogrammer
4343+ * added RCS id
4344+ *
4345+ * Revision 1.4  2004-09-19 14:36:11+05:30  Cprogrammer
4346+ * corrected number of bytes in stralloc variable 'out'
4347+ *
4348+ * Revision 1.3  2004-07-30 17:36:47+05:30  Cprogrammer
4349+ * fixed bugs in b64decode()
4350+ *
4351+ * Revision 1.2  2004-07-17 21:16:27+05:30  Cprogrammer
4352+ * added RCS log
4353+ *
4354+ */
4355+#include "base64.h"
4356+#include "stralloc.h"
4357+#include "substdio.h"
4358+#include "str.h"
4359+
4360+static char    *b64alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4361+#define B64PAD '='
4362+
4363+/*
4364+ * returns 0 ok, 1 illegal, -1 problem
4365+ */
4366+
4367+int
4368+b64decode(in, l, out)
4369+       const unsigned char *in;
4370+       int             l;
4371+       stralloc       *out;            /*- not null terminated */
4372+{
4373+       int             p = 0;
4374+       int             n;
4375+       unsigned int    x;
4376+       int             i, j;
4377+       char           *s;
4378+       unsigned char   b[3];
4379+
4380+       if (l == 0)
4381+       {
4382+               if (!stralloc_copys(out, ""))
4383+                       return -1;
4384+               return 0;
4385+       }
4386+       while (in[l - 1] == B64PAD)
4387+       {
4388+               p++;
4389+               l--;
4390+       }
4391+       n = (l + p) / 4;
4392+       out->len = (n * 3) - p;
4393+       if (!stralloc_ready(out, out->len))
4394+               return -1;
4395+       s = out->s;
4396+       for (i = 0; i < n - 1; i++)
4397+       {
4398+               x = 0;
4399+               for (j = 0; j < 4; j++)
4400+               {
4401+                       if (in[j] >= 'A' && in[j] <= 'Z')
4402+                               x = (x << 6) + (unsigned int) (in[j] - 'A' + 0);
4403+                       else
4404+                       if (in[j] >= 'a' && in[j] <= 'z')
4405+                               x = (x << 6) + (unsigned int) (in[j] - 'a' + 26);
4406+                       else
4407+                       if (in[j] >= '0' && in[j] <= '9')
4408+                               x = (x << 6) + (unsigned int) (in[j] - '0' + 52);
4409+                       else
4410+                       if (in[j] == '+')
4411+                               x = (x << 6) + 62;
4412+                       else
4413+                       if (in[j] == '/')
4414+                               x = (x << 6) + 63;
4415+                       else
4416+                       if (in[j] == '=')
4417+                               x = (x << 6);
4418+               }
4419+               s[2] = (unsigned char) (x & 255);
4420+               x >>= 8;
4421+               s[1] = (unsigned char) (x & 255);
4422+               x >>= 8;
4423+               s[0] = (unsigned char) (x & 255);
4424+               x >>= 8;
4425+               s += 3;
4426+               in += 4;
4427+       }
4428+       x = 0;
4429+       for (j = 0; j < 4; j++)
4430+       {
4431+               if (in[j] >= 'A' && in[j] <= 'Z')
4432+                       x = (x << 6) + (unsigned int) (in[j] - 'A' + 0);
4433+               else
4434+               if (in[j] >= 'a' && in[j] <= 'z')
4435+                       x = (x << 6) + (unsigned int) (in[j] - 'a' + 26);
4436+               else
4437+               if (in[j] >= '0' && in[j] <= '9')
4438+                       x = (x << 6) + (unsigned int) (in[j] - '0' + 52);
4439+               else
4440+               if (in[j] == '+')
4441+                       x = (x << 6) + 62;
4442+               else
4443+               if (in[j] == '/')
4444+                       x = (x << 6) + 63;
4445+               else
4446+               if (in[j] == '=')
4447+                       x = (x << 6);
4448+       }
4449+       b[2] = (unsigned char) (x & 255);
4450+       x >>= 8;
4451+       b[1] = (unsigned char) (x & 255);
4452+       x >>= 8;
4453+       b[0] = (unsigned char) (x & 255);
4454+       x >>= 8;
4455+       for (i = 0; i < 3 - p; i++)
4456+               s[i] = b[i];
4457+       out->len = (n * 3) - p;
4458+       return 0;
4459+}
4460+
4461+int
4462+b64encode(in, out)
4463+       stralloc       *in;
4464+       stralloc       *out;            /*- not null terminated */
4465+{
4466+       unsigned char   a, b, c;
4467+       int             i;
4468+       char           *s;
4469+
4470+       if (in->len == 0)
4471+       {
4472+               if (!stralloc_copys(out, ""))
4473+                       return -1;
4474+               return 0;
4475+       }
4476+       if (!stralloc_ready(out, in->len / 3 * 4 + 4))
4477+               return -1;
4478+       s = out->s;
4479+       for (i = 0; i < in->len; i += 3)
4480+       {
4481+               a = in->s[i];
4482+               b = i + 1 < in->len ? in->s[i + 1] : 0;
4483+               c = i + 2 < in->len ? in->s[i + 2] : 0;
4484+               *s++ = b64alpha[a >> 2];
4485+               *s++ = b64alpha[((a & 3) << 4) | (b >> 4)];
4486+               if (i + 1 >= in->len)
4487+                       *s++ = B64PAD;
4488+               else
4489+                       *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
4490+
4491+               if (i + 2 >= in->len)
4492+                       *s++ = B64PAD;
4493+               else
4494+                       *s++ = b64alpha[c & 63];
4495+       }
4496+       out->len = s - out->s;
4497+       return 0;
4498+}
4499+
4500+void
4501+getversion_base64sub_c()
4502+{
4503+       static char    *x = "$Id: base64sub.c,v 1.6 2010-03-03 09:33:16+05:30 Cprogrammer Stab mbhangui $";
4504+
4505+       x++;
4506+}
4507diff -ruN ../netqmail-1.06-original/byte.h netqmail-1.06/byte.h
4508--- ../netqmail-1.06-original/byte.h    1998-06-15 12:53:16.000000000 +0200
4509+++ netqmail-1.06/byte.h        2019-02-27 20:57:13.382025158 +0100
4510@@ -3,6 +3,8 @@
4511 
4512 extern unsigned int byte_chr();
4513 extern unsigned int byte_rchr();
4514+extern unsigned int byte_cspn();
4515+extern unsigned int byte_rcspn();
4516 extern void byte_copy();
4517 extern void byte_copyr();
4518 extern int byte_diff();
4519diff -ruN ../netqmail-1.06-original/byte_cspn.c netqmail-1.06/byte_cspn.c
4520--- ../netqmail-1.06-original/byte_cspn.c       1970-01-01 01:00:00.000000000 +0100
4521+++ netqmail-1.06/byte_cspn.c   2019-02-27 20:57:13.382025158 +0100
4522@@ -0,0 +1,11 @@
4523+#include "byte.h"
4524+
4525+unsigned int byte_cspn(s,n,c)
4526+register char *s;
4527+register unsigned int n;
4528+register char *c;
4529+{
4530+  while(*c)
4531+    n = byte_chr(s,n,*c++);
4532+  return n;
4533+}
4534diff -ruN ../netqmail-1.06-original/byte_rcspn.c netqmail-1.06/byte_rcspn.c
4535--- ../netqmail-1.06-original/byte_rcspn.c      1970-01-01 01:00:00.000000000 +0100
4536+++ netqmail-1.06/byte_rcspn.c  2019-02-27 20:57:13.382025158 +0100
4537@@ -0,0 +1,17 @@
4538+#include "byte.h"
4539+
4540+unsigned int byte_rcspn(s,n,c)
4541+register char *s;
4542+register unsigned int n;
4543+register char *c;
4544+{
4545+  unsigned int ret,pos,i;
4546+
4547+  for(ret = n,pos = 0;*c;++c) {
4548+    i = byte_rchr(s + pos,n - pos,*c) + pos;
4549+    if (i < n) ret = pos = i;
4550+  }
4551+
4552+  return ret;
4553+}
4554+
4555diff -ruN ../netqmail-1.06-original/caldate.h netqmail-1.06/caldate.h
4556--- ../netqmail-1.06-original/caldate.h 1970-01-01 01:00:00.000000000 +0100
4557+++ netqmail-1.06/caldate.h     2019-02-27 20:57:13.382025158 +0100
4558@@ -0,0 +1,24 @@
4559+/*
4560+ * $Log: caldate.h,v $
4561+ * Revision 1.3  2004-10-09 23:20:20+05:30  Cprogrammer
4562+ * added function prototypes
4563+ *
4564+ * Revision 1.2  2004-06-18 22:57:44+05:30  Cprogrammer
4565+ * added RCS log
4566+ *
4567+ */
4568+#ifndef CALDATE_H
4569+#define CALDATE_H
4570+
4571+struct caldate
4572+{
4573+       long            year;
4574+       int             month;
4575+       int             day;
4576+};
4577+
4578+void            caldate_frommjd(struct caldate *, long, int *, int *);
4579+long            caldate_mjd(struct caldate *);
4580+unsigned int    caldate_fmt(char *, struct caldate *);
4581+
4582+#endif
4583diff -ruN ../netqmail-1.06-original/caltime.h netqmail-1.06/caltime.h
4584--- ../netqmail-1.06-original/caltime.h 1970-01-01 01:00:00.000000000 +0100
4585+++ netqmail-1.06/caltime.h     2019-02-27 20:57:13.382025158 +0100
4586@@ -0,0 +1,30 @@
4587+/*
4588+ * $Log: caltime.h,v $
4589+ * Revision 1.3  2004-10-09 23:20:26+05:30  Cprogrammer
4590+ * added function prototypes
4591+ *
4592+ * Revision 1.2  2004-06-18 22:57:47+05:30  Cprogrammer
4593+ * added RCS log
4594+ *
4595+ */
4596+#ifndef CALTIME_H
4597+#define CALTIME_H
4598+
4599+#include "caldate.h"
4600+#include "tai.h"
4601+
4602+struct caltime
4603+{
4604+       struct caldate  date;
4605+       int             hour;
4606+       int             minute;
4607+       int             second;
4608+       long            offset;
4609+};
4610+
4611+void            caltime_tai(struct caltime *, struct tai *);
4612+void            caltime_utc(struct caltime *, struct tai *, int *, int *);
4613+
4614+unsigned int    caltime_fmt(char *, struct caltime *);
4615+
4616+#endif
4617diff -ruN ../netqmail-1.06-original/case_startb.c netqmail-1.06/case_startb.c
4618--- ../netqmail-1.06-original/case_startb.c     1970-01-01 01:00:00.000000000 +0100
4619+++ netqmail-1.06/case_startb.c 2019-02-27 20:57:13.382025158 +0100
4620@@ -0,0 +1,31 @@
4621+#include "case.h"
4622+
4623+int case_startb(s,len,t)
4624+register char *s;
4625+unsigned int len;
4626+register char *t;
4627+{
4628+  register unsigned char x;
4629+  register unsigned char y;
4630+
4631+  for (;;) {
4632+    y = *t++ - 'A';
4633+    if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
4634+    if (!y) return 1;
4635+    if (!len) return 0;
4636+    --len;
4637+    x = *s++ - 'A';
4638+    if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
4639+    if (x != y) return 0;
4640+  }
4641+}
4642+
4643+/* DKIM 1.10 */
4644+void
4645+getversion_case_startb_c()
4646+{
4647+        static char    *x = "$Id: case_startb.c,v 1.3 2004-10-22 20:23:18+05:30 Cprogrammer Stab mbhangui $";
4648+
4649+        x++;
4650+}
4651+/* end DKIM 1.10 */
4652diff -ruN ../netqmail-1.06-original/channels.g netqmail-1.06/channels.g
4653--- ../netqmail-1.06-original/channels.g        1970-01-01 01:00:00.000000000 +0100
4654+++ netqmail-1.06/channels.g    2019-06-26 16:39:31.572826981 +0200
4655@@ -0,0 +1,18 @@
4656+#ifndef CHANNELS_H
4657+#define CHANNELS_H
4658+
4659+/* total number of channels including canonical "local" and "remote" channels */
4660+#define CHANNELS NUMCHANNELS
4661+
4662+/* supplemental channels are all channels less the canonical "local" and "remote" channels */
4663+#define SUPPL_CHANNELS (CHANNELS - 2)
4664+
4665+/* Not longer than 80 bytes, must also change qmail-upq.sh */
4666+#define QDIR_BASENAME "suppl"
4667+
4668+/* start supplemental channel fd numbers here */
4669+#define CHANNEL_FD_OFFSET 10
4670+
4671+
4672+#endif
4673+
4674diff -ruN ../netqmail-1.06-original/chkspawn.c netqmail-1.06/chkspawn.c
4675--- ../netqmail-1.06-original/chkspawn.c        1998-06-15 12:53:16.000000000 +0200
4676+++ netqmail-1.06/chkspawn.c    2019-02-27 20:57:13.382025158 +0100
4677@@ -22,8 +22,8 @@
4678     _exit(1);
4679   }
4680 
4681-  if (auto_spawn > 255) {
4682-    substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n");
4683+  if (auto_spawn > 65000) {
4684+    substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 65000.\n");
4685     substdio_flush(subfderr);
4686     _exit(1);
4687   }
4688diff -ruN ../netqmail-1.06-original/chkuser.c netqmail-1.06/chkuser.c
4689--- ../netqmail-1.06-original/chkuser.c 1970-01-01 01:00:00.000000000 +0100
4690+++ netqmail-1.06/chkuser.c     2019-08-05 19:11:16.583873852 +0200
4691@@ -0,0 +1,1266 @@
4692+
4693+/*
4694+ *
4695+ * 'chkuser.c' v.2.0.9
4696+ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
4697+ *
4698+ * Author: Antonio Nati tonix@interazioni.it
4699+ * All rights on this software and
4700+ * the identifying words chkusr and chkuser reserved by the author
4701+ *
4702+ * This software may be freely used, modified and distributed,
4703+ * but this lines must be kept in every original or derived version.
4704+ * Original author "Antonio Nati" and the web URL
4705+ * "http://www.interazioni.it/opensource"
4706+ * must be indicated in every related work or web page
4707+ *
4708+ */
4709+
4710+#include <pwd.h>
4711+
4712+/* required by vpopmail */
4713+#include <stdio.h>
4714+
4715+#include <stdlib.h>
4716+#include <string.h>
4717+#include <unistd.h>
4718+
4719+#include "dns.h"
4720+#include "env.h"
4721+#include "ipme.h"
4722+#include "now.h"
4723+#include "str.h"
4724+#include "open.h"
4725+#include "subfd.h"
4726+#include "substdio.h"
4727+#include "stralloc.h"
4728+
4729+#include "vpopmail.h"
4730+#include "vauth.h"
4731+#include "vpopmail_config.h"
4732+
4733+#include "chkuser.h"
4734+#include "chkuser_settings.h"
4735+
4736+#if defined _exit
4737+#undef _exit
4738+#endif
4739+
4740+extern void flush();
4741+extern void out (char *s);
4742+
4743+extern char *remotehost;
4744+extern char *remoteip;
4745+extern char *remoteinfo;
4746+extern char *relayclient;
4747+extern char *fakehelo;
4748+
4749+extern void die_nomem();
4750+
4751+#define DIE_NOMEM() die_nomem()
4752+
4753+#if defined CHKUSER_DEBUG
4754+
4755+#if defined CHKUSER_DEBUG_STDERR
4756+
4757+#define CHKUSER_DBG(a) write (STDERR_FILENO, a, strlen (a))
4758+#define CHKUSER_DBG_INT(a) { int x; char str[30]; sprintf (str, "%d", a); write (STDERR_FILENO, str, strlen (str));}
4759+
4760+#else
4761+
4762+#define CHKUSER_DBG(a) write (STDOUT_FILENO, a, strlen (a))
4763+#define CHKUSER_DBG_INT(a) { int x; char str[30]; sprintf (str, "%d", a); write (STDOUT_FILENO, str, strlen (str));}
4764+
4765+#endif
4766+#else
4767+
4768+#define CHKUSER_DBG(a) /* DBG dummy */
4769+#define CHKUSER_DBG_INT(a) /* DBG dummy */
4770+
4771+#endif
4772+
4773+static int intrusion_threshold_reached = 0;
4774+static int first_time_init_flag = 1;
4775+
4776+static int recipients = 0;
4777+static int wrong_recipients = 0;
4778+
4779+static stralloc user = {0};
4780+static stralloc domain = {0};
4781+static stralloc domain_path = {0};
4782+static stralloc tmp_path = {0};
4783+static stralloc alias_path = {0};
4784+
4785+#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE
4786+ static char *identify_remote;
4787+#endif
4788+
4789+#if defined CHKUSER_ENABLE_EXTENSIONS
4790+#define CHKUSER_ENABLE_USERS_EXTENSIONS
4791+#endif
4792+
4793+#if defined CHKUSER_ENABLE_LISTS
4794+#define CHKUSER_ENABLE_EZMLM_LISTS
4795+#endif
4796+
4797+#if defined CHKUSER_EXTENSION_DASH
4798+#define CHKUSER_USERS_DASH CHKUSER_EXTENSION_DASH
4799+#endif
4800+
4801+
4802+#if defined CHKUSER_ENABLE_VALIAS
4803+#error  "chkuser setting error: CHKUSER_ENABLE_VALIAS has been substituted by VALIAS (within vpopmail includes); you don't need anymore this define"
4804+#endif
4805+
4806+#if defined CHKUSER_ENABLE_VAUTH_OPEN
4807+#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"
4808+#endif
4809+
4810+#if defined CHKUSER_ENABLE_VAUTH_OPEN_CALL
4811+ static int db_already_open = 0;
4812+#endif
4813+
4814+#if defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE
4815+#error "chkuser setting error: CHKUSER_ALWAYS_ON and CHKUSER_STARTING_VARIABLE are mutually esclusive. Edit your chkuser_settings.h and disable one of them"
4816+#endif
4817+
4818+  static int starting_value = 0;
4819+
4820+#if defined CHKUSER_STARTING_VARIABLE
4821+  static char *starting_string = 0;
4822+#endif
4823+
4824+#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE
4825+  static int mustauth_value = 0;
4826+#endif
4827+
4828+
4829+#if defined CHKUSER_RCPT_LIMIT_VARIABLE
4830+  static char *maxrcpt_string = 0;
4831+  static int maxrcpt_limit = 0;
4832+  static int maxrcpt_limit_reached = 0;
4833+#endif
4834+
4835+#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE
4836+  static char *maxwrongrcpt_string = 0;
4837+  static int maxwrongrcpt_limit = 0;
4838+  static int maxwrongrcpt_limit_reached = 0;
4839+#endif
4840+
4841+#if defined CHKUSER_MBXQUOTA_VARIABLE
4842+  static char *maxmbxquota_string = 0;
4843+  static int maxmbxquota_limit = 0;
4844+#endif
4845+
4846+  static unsigned int sender_nocheck = 0;
4847+
4848+#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX
4849+static stralloc sender_user = {0};
4850+static stralloc sender_domain = {0};
4851+#endif
4852+
4853+#if defined CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE
4854+static unsigned int enable_doublebounce = 0;
4855+#endif
4856+
4857+#if defined CHKUSER_ERROR_DELAY
4858+
4859+  static int chkuser_delay_interval = CHKUSER_ERROR_DELAY * 1000;
4860+
4861+#define CHKUSER_DELAY()        chkuser_delay()
4862+
4863+void chkuser_delay (void) {
4864+
4865+        usleep (chkuser_delay_interval);
4866+
4867+#if defined CHKUSER_ERROR_DELAY_INCREASE
4868+        chkuser_delay_interval += CHKUSER_ERROR_DELAY_INCREASE * 1000;
4869+#endif
4870+}
4871+
4872+#if defined CHKUSER_RCPT_DELAY_ANYERROR
4873+#define CHKUSER_RCPT_DELAY_ANY() chkuser_delay()
4874+#else
4875+#define CHKUSER_RCPT_DELAY_ANY() /* no delay for any error */
4876+#endif
4877+
4878+#if defined CHKUSER_SENDER_DELAY_ANYERROR
4879+#define CHKUSER_SENDER_DELAY_ANY() chkuser_delay()
4880+#else
4881+#define CHKUSER_SENDER_DELAY_ANY() /* no delay for any error */
4882+#endif
4883+
4884+
4885+#else
4886+#define CHKUSER_DELAY() /* no delay */
4887+#define CHKUSER_RCPT_DELAY_ANY() /* no delay */
4888+#define CHKUSER_SENDER_DELAY_ANY() /* no delay */
4889+#endif
4890+
4891+#if defined CHKUSER_ENABLE_LOGGING
4892+
4893+static stralloc logstr = { 0 };
4894+
4895+static void chkuser_commonlog (char *sender, char *rcpt, char *title, char *description) {
4896+
4897+  substdio_puts (subfderr, "CHKUSER ");
4898+  substdio_puts (subfderr, title);
4899+  substdio_puts (subfderr, ": from <");
4900+  substdio_puts (subfderr, sender);
4901+  substdio_puts (subfderr, "|remoteinfo/auth:" );
4902+  if (remoteinfo) {
4903+       substdio_puts (subfderr, remoteinfo);
4904+  }
4905+  substdio_puts (subfderr, "|chkuser-identify:" );
4906+#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE
4907+  if (identify_remote) substdio_puts (subfderr, identify_remote);
4908+#endif
4909+  substdio_puts (subfderr, "> remote <helo:");
4910+  if (fakehelo) substdio_puts (subfderr, fakehelo);
4911+  substdio_puts (subfderr, "|remotehostname:" );
4912+  if (remotehost) substdio_puts (subfderr, remotehost);
4913+  substdio_puts (subfderr, "|remotehostip:" );
4914+  if (remoteip) substdio_puts (subfderr, remoteip);
4915+  substdio_puts (subfderr, "> rcpt <");
4916+  substdio_puts (subfderr, rcpt);
4917+  substdio_puts (subfderr, "> : ");
4918+  substdio_puts (subfderr, description);
4919+  substdio_puts (subfderr, "\n");
4920+  substdio_flush (subfderr);
4921+}
4922+
4923+#else
4924+#define chkuser_commonlog(a,b,c,d) /* no log */
4925+#endif
4926+
4927+#if defined CHKUSER_SENDER_FORMAT
4928+
4929+static int check_sender_address_format (stralloc *user, stralloc *domain) {
4930+
4931+        int x;
4932+
4933+        for (x = 0; x < (user->len -1); ++x) {
4934+                if ((!isalnum (user->s[x]))
4935+
4936+#if defined CHKUSER_ALLOW_SENDER_SRS
4937+               && (user->s[x] != '#')
4938+               && (user->s[x] != '+')
4939+#endif
4940+#if defined CHKUSER_ALLOW_SENDER_CHAR_1
4941+               && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_1)
4942+#endif
4943+#if defined CHKUSER_ALLOW_SENDER_CHAR_2
4944+               && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_2)
4945+#endif
4946+#if defined CHKUSER_ALLOW_SENDER_CHAR_3
4947+               && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_3)
4948+#endif
4949+#if defined CHKUSER_ALLOW_SENDER_CHAR_4
4950+               && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_4)
4951+#endif
4952+#if defined CHKUSER_ALLOW_SENDER_CHAR_5
4953+               && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_5)
4954+#endif
4955+#if defined CHKUSER_ALLOW_SENDER_CHAR_6
4956+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_6)
4957+#endif
4958+#if defined CHKUSER_ALLOW_SENDER_CHAR_7
4959+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_7)
4960+#endif
4961+#if defined CHKUSER_ALLOW_SENDER_CHAR_8
4962+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_8)
4963+#endif
4964+#if defined CHKUSER_ALLOW_SENDER_CHAR_9
4965+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_9)
4966+#endif
4967+#if defined CHKUSER_ALLOW_SENDER_CHAR_10
4968+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_10)
4969+#endif
4970+               && (user->s[x] != '_') && (user->s[x] != '-') && (user->s[x] != '.') && (user->s[x] != '=')) {
4971+                        return 0;
4972+                }
4973+        }
4974+
4975+/*
4976+ * Be careful, this is a base check
4977+ *      Minimum is x.xx + ending \0
4978+ *      Minimum characters needed are 5
4979+ */
4980+#if defined CHKUSER_MIN_DOMAIN_LEN
4981+        if (domain->len < (CHKUSER_MIN_DOMAIN_LEN +1)) {
4982+                return 0;
4983+        }
4984+#endif
4985+
4986+/*
4987+ *      This is a safety check
4988+ */
4989+#if defined CHKUSER_MIN_DOMAIN_LEN
4990+        if (domain->len < 2) {
4991+                return 0;
4992+        }
4993+#endif
4994+
4995+        for (x = 0; x < (domain->len -1); ++x) {
4996+                if ((!isalnum (domain->s[x])) && (domain->s[x] != '-') && (domain->s[x] != '.')) {
4997+                        return 0;
4998+                }
4999+        }
5000+
5001+        if ((domain->s[0] == '-') || (domain->s[domain->len -2] == '-') || (domain->s[0] == '.') || (domain->s[domain->len -2] == '.')) {
5002+                return 0;
5003+        }
5004+        if (strstr (domain->s, "..") != NULL) {
5005+                return 0;
5006+        }
5007+       if (strncmp (domain->s, "xn--", 4) == 0) {
5008+               if (strstr (&domain->s[4], "--") != NULL)
5009+                       return 0;
5010+       } else {
5011+               if (strstr (domain->s, "--") != NULL)
5012+                       return 0;
5013+       }
5014+        if (strstr (domain->s, ".-") != NULL) {
5015+                return 0;
5016+        }
5017+        if (strstr (domain->s, "-.") != NULL) {
5018+                return 0;
5019+        }
5020+        if (strchr (domain->s, '.') == NULL) {
5021+                return 0;
5022+        }
5023+
5024+        return 1;
5025+}
5026+
5027+#endif
5028+
5029+#if defined CHKUSER_RCPT_FORMAT
5030+
5031+static int check_rcpt_address_format (stralloc *user, stralloc *domain) {
5032+
5033+        int x;
5034+
5035+        for (x = 0; x < (user->len -1); ++x) {
5036+                if ((!isalnum (user->s[x]))
5037+#if defined CHKUSER_ALLOW_RCPT_SRS
5038+                && (user->s[x] != '#')
5039+                && (user->s[x] != '+')
5040+#endif
5041+#if defined CHKUSER_ALLOW_RCPT_CHAR_1
5042+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_1)
5043+#endif
5044+#if defined CHKUSER_ALLOW_RCPT_CHAR_2
5045+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_2)
5046+#endif
5047+#if defined CHKUSER_ALLOW_RCPT_CHAR_3
5048+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_3)
5049+#endif
5050+#if defined CHKUSER_ALLOW_RCPT_CHAR_4
5051+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_4)
5052+#endif
5053+#if defined CHKUSER_ALLOW_RCPT_CHAR_5
5054+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_5)
5055+#endif
5056+#if defined CHKUSER_ALLOW_RCPT_CHAR_6
5057+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_6)
5058+#endif
5059+#if defined CHKUSER_ALLOW_RCPT_CHAR_7
5060+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_7)
5061+#endif
5062+#if defined CHKUSER_ALLOW_RCPT_CHAR_8
5063+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_8)
5064+#endif
5065+#if defined CHKUSER_ALLOW_RCPT_CHAR_9
5066+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_9)
5067+#endif
5068+#if defined CHKUSER_ALLOW_RCPT_CHAR_10
5069+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_10)
5070+#endif
5071+               && (user->s[x] != '_') && (user->s[x] != '-') && (user->s[x] != '.') && (user->s[x] != '=')) {
5072+                        return 0;
5073+                }
5074+        }
5075+
5076+/*
5077+ * Be careful, this is a base check
5078+ *      Minimum is x.xx + ending \0
5079+ *      Minimum characters needed are 5
5080+ */
5081+#if defined CHKUSER_MIN_DOMAIN_LEN
5082+        if (domain->len < (CHKUSER_MIN_DOMAIN_LEN +1)) {
5083+                return 0;
5084+        }
5085+#endif
5086+
5087+/*
5088+ *      This is a safety check
5089+ */
5090+#if defined CHKUSER_MIN_DOMAIN_LEN
5091+        if (domain->len < 2) {
5092+                return 0;
5093+        }
5094+#endif
5095+        for (x = 0; x < (domain->len -1); ++x) {
5096+                if ((!isalnum (domain->s[x])) && (domain->s[x] != '-') && (domain->s[x] != '.')) {
5097+                        return 0;
5098+                }
5099+        }
5100+
5101+        if ((domain->s[0] == '-') || (domain->s[domain->len -2] == '-') || (domain->s[0] == '.') || (domain->s[domain->len -2] == '.')) {
5102+                return 0;
5103+        }
5104+        if (strstr (domain->s, "..") != NULL) {
5105+                return 0;
5106+        }
5107+       if (strncmp (domain->s, "xn--", 4) == 0) {
5108+               if (strstr (&domain->s[4], "--") != NULL)
5109+                       return 0;
5110+       } else {
5111+               if (strstr (domain->s, "--") != NULL)
5112+                       return 0;
5113+       }
5114+        if (strstr (domain->s, ".-") != NULL) {
5115+                return 0;
5116+        }
5117+        if (strstr (domain->s, "-.") != NULL) {
5118+                return 0;
5119+        }
5120+        if (strchr (domain->s, '.') == NULL) {
5121+                return 0;
5122+        }
5123+
5124+        return 1;
5125+}
5126+
5127+#endif
5128+
5129+#if defined CHKUSER_SENDER_MX || defined CHKUSER_RCPT_MX
5130+
5131+static   unsigned long mx_random;
5132+static  ipalloc mx_ip = {0};
5133+
5134+static int chkuser_mx_lookup (stralloc *domain) {
5135+
5136+  int status;
5137+
5138+       mx_random = now() + getpid();
5139+       dns_init(0);
5140+       status = dns_mxip (&mx_ip, domain, mx_random);
5141+
5142+       if (status == DNS_MEM) DIE_NOMEM();
5143+
5144+       return status;
5145+}
5146+
5147+#endif
5148+
5149+
5150+void chkuser_cleanup (int exit_value) {
5151+
5152+#if defined CHKUSER_DB_CLEANUP
5153+       vclose ();
5154+#endif
5155+       _exit (exit_value);
5156+}
5157+
5158+static void first_time_init (void) {
5159+
5160+  starting_value = 0;
5161+
5162+#if defined CHKUSER_ALWAYS_ON
5163+       starting_value = 1;
5164+#endif
5165+
5166+#if defined CHKUSER_STARTING_VARIABLE
5167+        starting_string = env_get (CHKUSER_STARTING_VARIABLE);
5168+        if (starting_string) {
5169+                if (strcasecmp(starting_string, "ALWAYS") == 0) {
5170+                        starting_value = 1;
5171+                } else if (strcasecmp(starting_string, "DOMAIN") == 0) {
5172+                        starting_value = 0;
5173+/*
5174+  Edit by Roberto Puzzanghera
5175+  It seems like any other definition of starting_string ends up as "DOMAIN".
5176+  Instead, if starting_string is otherwise defined, we want to turn off chkuser,
5177+  just like if the starting_string is "NONE".
5178+ */
5179+                } else {
5180+                       starting_value = -1;
5181+               }
5182+        } else {
5183+                starting_string = "";
5184+               starting_value = -1;
5185+        }
5186+#endif
5187+
5188+#if defined CHKUSER_DISABLE_VARIABLE
5189+       if (env_get (CHKUSER_DISABLE_VARIABLE)) {
5190+               starting_value = -1;
5191+       }
5192+#endif
5193+
5194+#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE
5195+        if (env_get (CHKUSER_EXTRA_MUSTAUTH_VARIABLE)) {
5196+               if (relayclient) {
5197+                       mustauth_value = 0;
5198+               } else {
5199+                       mustauth_value = 1;
5200+               }
5201+        }
5202+#endif
5203+
5204+
5205+#if defined CHKUSER_RCPT_LIMIT_VARIABLE
5206+        maxrcpt_string = env_get (CHKUSER_RCPT_LIMIT_VARIABLE);
5207+        if (maxrcpt_string) {
5208+                maxrcpt_limit = atoi (maxrcpt_string);
5209+                if (maxrcpt_limit < 1) {
5210+                        maxrcpt_limit = 0;
5211+                }
5212+        } else {
5213+                maxrcpt_string = "";;
5214+        }
5215+#endif
5216+
5217+#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE
5218+        maxwrongrcpt_string = env_get (CHKUSER_WRONGRCPT_LIMIT_VARIABLE);
5219+        if (maxwrongrcpt_string) {
5220+                maxwrongrcpt_limit = atoi (maxwrongrcpt_string);
5221+                if (maxwrongrcpt_limit < 1) {
5222+                        maxwrongrcpt_limit = 0;
5223+                }
5224+        } else {
5225+                maxwrongrcpt_string = "";
5226+        }
5227+#endif
5228+
5229+#if defined CHKUSER_MBXQUOTA_VARIABLE
5230+        maxmbxquota_string = env_get (CHKUSER_MBXQUOTA_VARIABLE);
5231+        if (maxmbxquota_string) {
5232+                maxmbxquota_limit = atoi (maxmbxquota_string);
5233+                if (maxmbxquota_limit < 1) {
5234+                       maxmbxquota_limit = 0;
5235+                }
5236+       } else {
5237+                       maxmbxquota_string = "";
5238+       }
5239+#endif
5240+
5241+#if defined CHKUSER_SENDER_NOCHECK_VARIABLE
5242+
5243+        if (env_get (CHKUSER_SENDER_NOCHECK_VARIABLE)) {
5244+               sender_nocheck = 1;
5245+        } else {
5246+               sender_nocheck = 0;
5247+        }
5248+#endif
5249+
5250+#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE
5251+
5252+        identify_remote = env_get (CHKUSER_IDENTIFY_REMOTE_VARIABLE);
5253+#endif
5254+
5255+
5256+#if defined CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE
5257+
5258+        if (env_get (CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE)) {
5259+                enable_doublebounce = 1;
5260+        } else {
5261+                enable_doublebounce = 0;
5262+        }
5263+#endif
5264+
5265+        if (!stralloc_ready (&user, 300)) DIE_NOMEM();
5266+        if (!stralloc_ready (&domain, 500)) DIE_NOMEM();
5267+        if (!stralloc_ready (&domain_path, 1000)) DIE_NOMEM();
5268+        if (!stralloc_ready (&tmp_path, 1000)) DIE_NOMEM();
5269+        if (!stralloc_ready (&alias_path, 1000)) DIE_NOMEM();
5270+
5271+       first_time_init_flag = 0;
5272+
5273+}
5274+
5275+/*
5276+ * realrcpt ()
5277+ *
5278+ * Returns:
5279+ *
5280+ *     CHKUSER_OK = 1 = Ok, recipients does exists
5281+ *
5282+ *     0 = Not in rcpthosts
5283+ *
5284+ *     < 0 various errors
5285+ *
5286+ *
5287+ * Parameters:
5288+ *     stralloc *sender = sender address
5289+ *     stralloc *rcpt = rcpt address to check
5290+ *
5291+ *
5292+*/
5293+
5294+static int realrcpt (stralloc *sender, stralloc *rcpt)
5295+{
5296+  int count;
5297+  int retstat = CHKUSER_KO;
5298+  struct vqpasswd *user_passwd = NULL;
5299+  int fd_file = -1;
5300+  int read_char;
5301+  int offset;
5302+  char read_buf[1024];
5303+
5304+#if defined CHKUSER_ENABLE_UIDGID
5305+  uid_t eff_uid;
5306+  gid_t eff_gid;
5307+#endif
5308+
5309+#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE
5310+  if (mustauth_value == 1) {
5311+       return CHKUSER_ERR_MUSTAUTH;
5312+  }
5313+#endif
5314+
5315+
5316+  if (starting_value == -1) {
5317+       if (addrallowed()) {
5318+               return CHKUSER_OK_NOCHECKALL;
5319+       } else {
5320+               if (relayclient) {
5321+                       return CHKUSER_RELAYING;
5322+               }
5323+               return CHKUSER_NORCPTHOSTS;
5324+       }
5325+  }
5326+
5327+  if (intrusion_threshold_reached == 1) {
5328+       return CHKUSER_ERR_INTRUSION_THRESHOLD;
5329+  }
5330+
5331+#if defined CHKUSER_RCPT_LIMIT_VARIABLE
5332+
5333+  ++recipients;
5334+  if ((maxrcpt_limit > 0) && (recipients >= maxrcpt_limit)) {
5335+       chkuser_commonlog (sender->s, rcpt->s, "intrusion threshold", "max number of allowed rcpt");
5336+       intrusion_threshold_reached = 1;
5337+        return CHKUSER_ERR_MAXRCPT;
5338+  }
5339+#endif
5340+
5341+/* Search the '@' character */
5342+  count = byte_rchr(rcpt->s,rcpt->len,'@');
5343+
5344+  if (count < rcpt->len) {
5345+    if (!stralloc_copyb (&user, rcpt->s, count)) DIE_NOMEM();
5346+    if (!stralloc_copys (&domain, rcpt->s + count + 1)) DIE_NOMEM();
5347+  }
5348+  else {
5349+    if (!stralloc_copys (&user, rcpt->s)) DIE_NOMEM();
5350+    domain.len = 0;
5351+  }
5352+  if (!stralloc_0 (&user)) DIE_NOMEM();
5353+  if (!stralloc_0 (&domain)) DIE_NOMEM();
5354+
5355+#if defined CHKUSER_ENABLE_UIDGID
5356+
5357+/* qmail-smtpd is running now as (effective) qmaild:nofiles */
5358+/* Save the effective UID & GID (qmaild:nofiles) */
5359+  eff_uid = geteuid ();
5360+  eff_gid = getegid ();
5361+
5362+/* Now set new effective UID & GID, getting it from real UID & GID (vpopmail:vchkpw) */
5363+  setegid (getgid());
5364+  seteuid (getuid());
5365+
5366+/* qmail-smtpd is running now as effective vpopmail:vchkpw */
5367+#endif
5368+
5369+
5370+/*
5371+ *
5372+ * Now let's start the test/setting suite
5373+ *
5374+ **/
5375+
5376+       switch (0) {
5377+
5378+       case 0:
5379+/* These are some preliminary settings */
5380+               case_lowers (user.s);
5381+               case_lowers (domain.s);
5382+
5383+       case 1:
5384+
5385+                if (domain.len == 1) {
5386+#if defined CHKUSER_DOMAIN_WANTED
5387+                        retstat = CHKUSER_ERR_DOMAIN_MISSING;
5388+                       break;
5389+#else
5390+                        if (!stralloc_copys (&domain, DEFAULT_DOMAIN)) DIE_NOMEM();
5391+                       if (!stralloc_0 (&domain)) DIE_NOMEM();
5392+#endif
5393+                }
5394+
5395+       case 2:
5396+
5397+#if defined CHKUSER_RCPT_FORMAT
5398+                if (check_rcpt_address_format (&user, &domain) == 0) {
5399+                        retstat = CHKUSER_ERR_RCPT_FORMAT;
5400+                        break;
5401+                }
5402+#endif
5403+
5404+       case 3:
5405+
5406+                if (!addrallowed()) {
5407+
5408+#if defined CHKUSER_RCPT_MX
5409+                       switch (chkuser_mx_lookup(&domain)) {
5410+
5411+                               case DNS_HARD:
5412+                                       retstat = CHKUSER_ERR_RCPT_MX;
5413+                                       break;
5414+
5415+                               case DNS_SOFT:
5416+                                       retstat = CHKUSER_ERR_RCPT_MX_TMP;
5417+                                       break;
5418+                       }
5419+
5420+                       if (retstat != CHKUSER_KO) {
5421+                               break;
5422+                       }
5423+#endif
5424+                       if (relayclient) {
5425+                               retstat = CHKUSER_RELAYING;
5426+                               break;
5427+                       }
5428+
5429+                        retstat = CHKUSER_NORCPTHOSTS;
5430+                        break;
5431+                }
5432+
5433+       case 4:
5434+
5435+#if defined CHKUSER_ENABLE_VGET_REAL_DOMAIN
5436+/* Check if domain is a real domain */
5437+
5438+                vget_real_domain(domain.s, domain.a);
5439+
5440+                domain.len = strlen (domain.s) +1;
5441+                if (domain.len > (domain.a - 1)) DIE_NOMEM();
5442+#endif
5443+
5444+/* Let's get domain's real path */
5445+                if (vget_assign(domain.s, domain_path.s, domain_path.a -1, NULL, NULL) == NULL) {
5446+                       retstat = CHKUSER_OK;
5447+                       break;
5448+               }
5449+       
5450+               domain_path.len = strlen (domain_path.s);
5451+
5452+       case 5:
5453+
5454+/* Check if domain has bouncing enabled */
5455+
5456+               if (starting_value == 0) {
5457+
5458+                       if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM();
5459+
5460+#if defined CHKUSER_SPECIFIC_BOUNCING
5461+                       if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM();
5462+                       if (!stralloc_cats (&tmp_path, CHKUSER_SPECIFIC_BOUNCING)) DIE_NOMEM();
5463+                       if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5464+                       fd_file = open_read (tmp_path.s);       
5465+                       if (fd_file != -1) {
5466+                               close (fd_file);
5467+                       } else {
5468+                               retstat = CHKUSER_OK_NOCHECKDOMAIN;
5469+                               break;
5470+                       }
5471+#else
5472+                       if (!stralloc_cats (&tmp_path, "/.qmail-default")) DIE_NOMEM();
5473+                       if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5474+
5475+                       read_char = 0;
5476+                       fd_file = open_read (tmp_path.s);       
5477+                       if (fd_file != -1) {
5478+                               read_char = read (fd_file, read_buf, sizeof(read_buf) - 1);
5479+                               close (fd_file);
5480+                               if (read_char < 0) read_char = 0;
5481+                               }
5482+                       read_buf[read_char] = 0;
5483+
5484+                       if ( strstr(read_buf, CHKUSER_BOUNCE_STRING) == NULL ) {
5485+                               retstat = CHKUSER_OK_NOCHECKDOMAIN;
5486+                               break;
5487+                       }
5488+#endif
5489+               }
5490+
5491+
5492+        case 6:
5493+
5494+#if defined CHKUSER_ENABLE_VAUTH_OPEN_CALL
5495+                if (db_already_open != 1) {
5496+                        if (CHKUSER_VAUTH_OPEN_CALL () == 0) {
5497+                                db_already_open == 1;
5498+                        } else {
5499+                                retstat = CHKUSER_ERR_AUTH_RESOURCE;
5500+                               break;
5501+                        }
5502+                }
5503+#endif
5504+
5505+
5506+       case 7:
5507+#if defined VALIAS
5508+/* Check for aliases/forwards - valias*/
5509+
5510+               if (valias_select (user.s, domain.s) != NULL) {
5511+                       retstat = CHKUSER_OK;
5512+                       break;
5513+               }
5514+#endif
5515+
5516+       case 8:
5517+#if defined CHKUSER_ENABLE_ALIAS
5518+/* Check for aliases/forwards - .qmail.x files */
5519+
5520+               if (!stralloc_copy (&tmp_path, &user)) DIE_NOMEM();
5521+                /* Change all '.' in ':' before continuing on aliases */
5522+                for (count = 0; count < tmp_path.len; ++count)
5523+                       if (*(tmp_path.s + count) == '.') *(tmp_path.s + count) = ':';
5524+
5525+                if (!stralloc_copy (&alias_path, &domain_path)) DIE_NOMEM();
5526+                if (!stralloc_cats (&alias_path, "/.qmail-")) DIE_NOMEM();
5527+                if (!stralloc_cats (&alias_path, tmp_path.s)) DIE_NOMEM();
5528+                if (!stralloc_0 (&alias_path)) DIE_NOMEM();
5529+
5530+               fd_file = open_read (alias_path.s);
5531+               if (fd_file != -1) {
5532+                       close (fd_file);
5533+                       retstat = CHKUSER_OK;
5534+                       break;
5535+               }
5536+#endif
5537+
5538+       case 9:
5539+
5540+#if defined CHKUSER_ENABLE_ALIAS_DEFAULT
5541+
5542+               if (!stralloc_copy (&tmp_path, &user)) DIE_NOMEM();
5543+                /* Change all '.' in ':' before continuing on aliases */
5544+                for (count = 0; count < tmp_path.len; ++count)
5545+                       if (*(tmp_path.s + count) == '.') *(tmp_path.s + count) = ':';
5546+
5547+                /* Search for the outer '-' character */
5548+                for (offset = user.len - 1; offset > 0; --offset) {
5549+                        if (*(user.s + offset) == CHKUSER_USERS_DASH)  {
5550+                                if (!stralloc_copy (&alias_path, &domain_path)) die_nomem();
5551+                                if (!stralloc_cats (&alias_path, "/.qmail-")) die_nomem();
5552+                                if (!stralloc_catb (&alias_path, user.s, offset)) die_nomem();
5553+                                if (!stralloc_cats (&alias_path, "-default")) die_nomem();
5554+                                if (!stralloc_0 (&alias_path)) die_nomem();
5555+
5556+                                fd_file = open_read (alias_path.s);
5557+                                if (fd_file != -1) {
5558+                                        close (fd_file);
5559+                                        retstat = CHKUSER_OK;
5560+                                        break;
5561+                                }
5562+                        }
5563+               }
5564+               if (retstat != CHKUSER_KO) {
5565+                       break;
5566+                }
5567+
5568+#endif
5569+
5570+        case 10:
5571+#if defined CHKUSER_ENABLE_USERS
5572+/* User control: check the existance of a real user */
5573+
5574+                user_passwd = vauth_getpw (user.s, domain.s);
5575+
5576+#if defined CHKUSER_ENABLE_USERS_EXTENSIONS
5577+                if (user_passwd == NULL) {
5578+                       count = 0;
5579+                       while ((count < (user.len -1)) && (user_passwd == NULL)) {
5580+                               count += byte_chr(&user.s[count], user.len - count, CHKUSER_USERS_DASH);
5581+                               if (count < user.len) {
5582+                                       if (!stralloc_copyb (&tmp_path, user.s, count)) DIE_NOMEM();
5583+                                       if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5584+                                       user_passwd = vauth_getpw (tmp_path.s, domain.s);
5585+                                         ++count;
5586+                               }
5587+                        }
5588+                }
5589+
5590+#endif
5591+                if (user_passwd != NULL) {
5592+
5593+                /* If user exists check if he has BOUNCE_MAIL flag set */
5594+
5595+                        if (user_passwd->pw_gid & BOUNCE_MAIL)
5596+                                retstat = CHKUSER_KO;
5597+                        else {
5598+                                retstat = CHKUSER_OK;
5599+#if defined CHKUSER_MBXQUOTA_VARIABLE
5600+                                if ((maxmbxquota_limit > 0) && (strcasecmp(user_passwd->pw_shell, "NOQUOTA") != 0)) {
5601+                                        if (!stralloc_copys (&tmp_path, user_passwd->pw_dir)) DIE_NOMEM();
5602+                                        if (!stralloc_cats (&tmp_path, "/Maildir")) DIE_NOMEM();
5603+                                        if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5604+
5605+                                        if (vmaildir_readquota(tmp_path.s,format_maildirquota(user_passwd->pw_shell))
5606+                                                >= maxmbxquota_limit) {
5607+                                                retstat = CHKUSER_ERR_MBXFULL;
5608+                                        }
5609+                                }
5610+#endif
5611+                        }
5612+                        break;
5613+                }
5614+#endif
5615+
5616+       case 11:
5617+#if defined CHKUSER_ENABLE_EZMLM_LISTS
5618+/* Let's check for mailing lists */
5619+
5620+               /* Search for the outer CHKUSER_EZMLM_DASH character */
5621+               for (offset = user.len - 2; offset > 0; --offset) {
5622+                       if (*(user.s + offset) == CHKUSER_EZMLM_DASH)  {
5623+                               if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM();
5624+                               if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM();
5625+                               if (!stralloc_catb (&tmp_path, user.s, offset)) DIE_NOMEM();
5626+                               if (!stralloc_cats (&tmp_path, "/editor")) DIE_NOMEM();
5627+                               if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5628+                               fd_file = open_read (tmp_path.s);
5629+                               if (fd_file != -1) {
5630+                                       close (fd_file);
5631+                                       retstat = CHKUSER_OK;
5632+                                       break;
5633+                               }
5634+                       }
5635+               }
5636+               if (retstat != CHKUSER_KO) {
5637+                       break;
5638+               }
5639+#endif
5640+
5641+        case 12:
5642+#if defined CHKUSER_ENABLE_MAILMAN_LISTS
5643+/* Let's check for mailing lists */
5644+
5645+                /* Search for the outer CHKUSER_MAILMAN_DASH character */
5646+                for (offset = user.len - 2; offset > 0; --offset) {
5647+                        if (*(user.s + offset) == CHKUSER_MAILMAN_DASH)  {
5648+                                if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM();
5649+                                if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM();
5650+                               if (!stralloc_cats (&alias_path, "/.qmail-")) DIE_NOMEM();
5651+                                if (!stralloc_catb (&tmp_path, user.s, offset)) DIE_NOMEM();
5652+                                if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5653+                                fd_file = open_read (tmp_path.s);
5654+                               read_char = 0;
5655+                               if (fd_file != -1) {
5656+                                       read_char = read (fd_file, read_buf, sizeof(read_buf) - 1);
5657+                                       close (fd_file);
5658+                                       if (read_char < 0) read_char = 0;
5659+                                }
5660+                               read_buf[read_char] = 0;
5661+
5662+                               if ( strstr(read_buf, CHKUSER_MAILMAN_STRING) == NULL ) {
5663+                                       retstat = CHKUSER_OK;
5664+                                       break;
5665+                               }
5666+
5667+                        }
5668+                }
5669+                if (retstat != CHKUSER_KO) {
5670+                        break;
5671+                }
5672+#endif
5673+
5674+/*
5675+ * Add this code if another case is following
5676+       case xx:
5677+               code ....
5678+               code ....
5679+               code ....
5680+               code ....
5681+
5682+               if (xxxxxxxx) {
5683+                       retstat != CHKUSER_KO)
5684+                       break;
5685+               }
5686+*/
5687+           
5688+        default:
5689+                retstat = CHKUSER_KO;
5690+
5691+       } /* end switch */
5692+
5693+#if defined CHKUSER_ENABLE_UIDGID
5694+/* Now switch back effective to saved UID & GID (qmaild:nofiles) */
5695+
5696+  setegid (eff_gid);
5697+  seteuid (eff_uid);
5698+
5699+/* qmail-smtpd is running again as (effective) qmaild:nofiles */
5700+#endif
5701+
5702+  return retstat;
5703+
5704+}
5705+
5706+
5707+
5708+/*
5709+ * chkuser_realrcpt ()
5710+ *
5711+ * Returns a simple status:
5712+ *
5713+ *      CHKUSER_OK = 1 = Ok, recipients does exists
5714+ *
5715+ *      CHKUSER_NORCPTHOSTS = Not in rcpthosts
5716+ *
5717+ *      CHKUSER_KO = ERROR
5718+ *
5719+ *
5720+ * Parameters:
5721+ *      stralloc *sender = sender address
5722+ *      stralloc *rcpt = rcpt address to check
5723+ *
5724+ *
5725+*/
5726+
5727+int chkuser_realrcpt (stralloc *sender, stralloc *rcpt) {
5728+
5729+int retstat;
5730+
5731+  if (first_time_init_flag) {
5732+        first_time_init ();
5733+  }
5734+
5735+
5736+  retstat = realrcpt (sender, rcpt);
5737+
5738+       switch (retstat) {
5739+
5740+               case CHKUSER_OK:
5741+#if defined CHKUSER_LOG_VALID_RCPT
5742+                       chkuser_commonlog (sender->s, rcpt->s, "accepted rcpt", "found existing recipient");
5743+#endif
5744+                       return CHKUSER_OK;
5745+                       break;
5746+
5747+               case CHKUSER_OK_NOCHECKALL:
5748+#if defined CHKUSER_LOG_VALID_RCPT
5749+                        chkuser_commonlog (sender->s, rcpt->s, "accepted any rcpt", "accepted any recipient for any rcpt domain");
5750+#endif
5751+                        return CHKUSER_OK;
5752+                        break;
5753+
5754+                case CHKUSER_OK_NOCHECKDOMAIN:
5755+#if defined CHKUSER_LOG_VALID_RCPT
5756+                        chkuser_commonlog (sender->s, rcpt->s, "accepted any rcpt", "accepted any recipient for this domain");
5757+#endif
5758+                        return CHKUSER_OK;
5759+                        break;
5760+
5761+                case CHKUSER_RELAYING:
5762+#if defined CHKUSER_LOG_VALID_RCPT
5763+                        chkuser_commonlog (sender->s, rcpt->s, "relaying rcpt", "client allowed to relay");
5764+#endif
5765+                        return CHKUSER_RELAYING;
5766+                        break;
5767+
5768+               case CHKUSER_NORCPTHOSTS:
5769+                        chkuser_commonlog (sender->s, rcpt->s, "rejected relaying", "client not allowed to relay");
5770+                       CHKUSER_RCPT_DELAY_ANY();
5771+                       out(CHKUSER_NORELAY_STRING);
5772+                       break;
5773+
5774+               case CHKUSER_KO:
5775+                       chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "not existing recipient");
5776+                       CHKUSER_DELAY();
5777+                       out(CHKUSER_NORCPT_STRING);
5778+                       break;
5779+
5780+               case CHKUSER_ERR_AUTH_RESOURCE:
5781+                       chkuser_commonlog (sender->s, rcpt->s, "no auth resource", "no auth resource available");
5782+                       CHKUSER_RCPT_DELAY_ANY();
5783+                       out(CHKUSER_RESOURCE_STRING);
5784+                       break;
5785+
5786+                case CHKUSER_ERR_MUSTAUTH:
5787+                        chkuser_commonlog (sender->s, rcpt->s, "must auth", "sender not authenticated/authorized");
5788+                        CHKUSER_RCPT_DELAY_ANY();
5789+                        out(CHKUSER_MUSTAUTH_STRING);
5790+                        break;
5791+
5792+               case CHKUSER_ERR_MBXFULL:
5793+                       chkuser_commonlog (sender->s, rcpt->s, "mbx overquota", "rcpt mailbox is overquota");
5794+                       CHKUSER_RCPT_DELAY_ANY();
5795+                       out(CHKUSER_MBXFULL_STRING);
5796+                       break;
5797+
5798+               case CHKUSER_ERR_MAXRCPT:
5799+                       chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "max number of recipients");
5800+                       CHKUSER_DELAY ();
5801+                       out(CHKUSER_MAXRCPT_STRING);
5802+                       break;
5803+
5804+               case CHKUSER_ERR_MAXWRONGRCPT:
5805+                       chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "max number of invalid recipients");
5806+                       CHKUSER_DELAY ();
5807+                       out(CHKUSER_MAXWRONGRCPT_STRING);
5808+                       break;
5809+
5810+               case CHKUSER_ERR_INTRUSION_THRESHOLD:
5811+                       chkuser_commonlog (sender->s, rcpt->s, "rejected intrusion", "rcpt ignored, session over intrusion threshold");
5812+                       CHKUSER_DELAY ();
5813+                       out(CHKUSER_INTRUSIONTHRESHOLD_STRING);
5814+                       break;
5815+
5816+               case CHKUSER_ERR_DOMAIN_MISSING:
5817+                       CHKUSER_DELAY ();
5818+                       out(CHKUSER_DOMAINMISSING_STRING);
5819+                       break;
5820+
5821+                case CHKUSER_ERR_RCPT_FORMAT:
5822+                        chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "invalid rcpt address format");
5823+                       CHKUSER_RCPT_DELAY_ANY();
5824+                       out(CHKUSER_RCPTFORMAT_STRING);
5825+                        break;
5826+
5827+                case CHKUSER_ERR_RCPT_MX:
5828+                       chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "invalid rcpt MX domain");
5829+                       CHKUSER_RCPT_DELAY_ANY();
5830+                       out(CHKUSER_RCPTMX_STRING);
5831+                        break;
5832+
5833+                case CHKUSER_ERR_RCPT_MX_TMP:
5834+                        chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "temporary DNS problem");
5835+                        CHKUSER_RCPT_DELAY_ANY();
5836+                        out(CHKUSER_RCPTMX_TMP_STRING);
5837+                        break;
5838+       }
5839+
5840+
5841+
5842+#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE
5843+       if ((retstat == CHKUSER_KO) || (retstat == CHKUSER_ERR_DOMAIN_MISSING)) {
5844+               ++wrong_recipients;
5845+               if ((intrusion_threshold_reached == 0) && (maxwrongrcpt_limit > 0) && (wrong_recipients >= maxwrongrcpt_limit)) {
5846+                       chkuser_commonlog (sender->s, rcpt->s, "intrusion threshold", "max number of allowed invalid rcpt");
5847+                       intrusion_threshold_reached = 1;
5848+               }
5849+       }
5850+#endif
5851+
5852+       return retstat;
5853+}
5854+
5855+
5856+/*
5857+ *
5858+ * This routine checks for sender format and MX
5859+ *
5860+ */
5861+
5862+
5863+int chkuser_sender (stralloc *sender) {
5864+
5865+int count;
5866+
5867+       if (first_time_init_flag) {
5868+               first_time_init ();
5869+       }
5870+
5871+#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE
5872+       if (mustauth_value == 1) {
5873+               out(CHKUSER_MUSTAUTH_STRING);
5874+#if defined CHKUSER_LOG_VALID_SENDER
5875+                        chkuser_commonlog (sender->s, "", "must auth", "sender not authenticated/authorized");
5876+                        CHKUSER_SENDER_DELAY_ANY();
5877+#endif
5878+               return CHKUSER_ERR_MUSTAUTH;
5879+       }
5880+#endif
5881+
5882+        if (sender->len <= 1) {
5883+#if defined CHKUSER_LOG_VALID_SENDER
5884+                chkuser_commonlog (sender->s, "", "accepted sender", "accepted null sender always");
5885+#endif
5886+                return CHKUSER_OK;
5887+        }
5888+
5889+       if ((starting_value == -1) || (sender_nocheck == 1)) {
5890+#if defined CHKUSER_LOG_VALID_SENDER
5891+                        chkuser_commonlog (sender->s, "", "accepted sender", "accepted any sender always");
5892+#endif
5893+               return CHKUSER_OK;
5894+       }
5895+
5896+#if defined CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE
5897+       if ((enable_doublebounce) && str_equal(sender->s,"#@[]")) {
5898+#if defined CHKUSER_LOG_VALID_SENDER
5899+                chkuser_commonlog (sender->s, "", "accepted doublebounce", "accepted qmail doublebounce #@[]");
5900+#endif
5901+                return CHKUSER_OK;
5902+       }
5903+#endif
5904+
5905+#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX
5906+        count = byte_rchr(sender->s,sender->len,'@');
5907+        if (count < sender->len) {
5908+                if (!stralloc_copyb (&sender_user, sender->s, count)) DIE_NOMEM();
5909+                if (!stralloc_copys (&sender_domain, sender->s + count + 1)) DIE_NOMEM();
5910+        } else {
5911+                if (!stralloc_copys (&sender_user, sender->s)) DIE_NOMEM();
5912+                sender_domain.len = 0;
5913+        }
5914+        if (!stralloc_0 (&sender_user)) DIE_NOMEM();
5915+        if (!stralloc_0 (&sender_domain)) DIE_NOMEM();
5916+
5917+#if defined CHKUSER_SENDER_FORMAT
5918+        if (check_sender_address_format (&sender_user, &sender_domain) == 0) {
5919+                chkuser_commonlog (sender->s, "", "rejected sender", "invalid sender address format");
5920+               CHKUSER_SENDER_DELAY_ANY();
5921+               out(CHKUSER_SENDERFORMAT_STRING);
5922+               return CHKUSER_ERR_SENDER_FORMAT;
5923+        }
5924+
5925+#endif
5926+
5927+#if defined CHKUSER_SENDER_MX
5928+
5929+       switch (chkuser_mx_lookup(&sender_domain)) {
5930+
5931+               case DNS_HARD:
5932+                       CHKUSER_SENDER_DELAY_ANY();
5933+                       out(CHKUSER_SENDERMX_STRING);
5934+                       chkuser_commonlog (sender->s, "", "rejected sender", "invalid sender MX domain");
5935+                       return CHKUSER_ERR_SENDER_MX;
5936+                       break;
5937+
5938+               case DNS_SOFT:
5939+                       CHKUSER_SENDER_DELAY_ANY();
5940+                       out(CHKUSER_SENDERMX_TMP_STRING);
5941+                       chkuser_commonlog (sender->s, "", "rejected sender", "temporary DNS problem");
5942+                       return CHKUSER_ERR_SENDER_MX_TMP;
5943+                       break;
5944+       }
5945+
5946+#endif
5947+#endif
5948+
5949+#if defined CHKUSER_LOG_VALID_SENDER
5950+                        chkuser_commonlog (sender->s, "", "accepted sender", "sender accepted");
5951+#endif
5952+
5953+       return CHKUSER_OK;
5954+
5955+}
5956+
5957+
5958diff -ruN ../netqmail-1.06-original/chkuser.h netqmail-1.06/chkuser.h
5959--- ../netqmail-1.06-original/chkuser.h 1970-01-01 01:00:00.000000000 +0100
5960+++ netqmail-1.06/chkuser.h     2019-02-27 20:57:13.383025147 +0100
5961@@ -0,0 +1,55 @@
5962+
5963+/*
5964+ *
5965+ * 'chkuser.h' v.2.0.9
5966+ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
5967+ *
5968+ * Author: Antonio Nati tonix@interazioni.it
5969+ * All rights on this software and
5970+ * the identifying words chkusr and chkuser reserved by the author
5971+ *
5972+ * This software may be freely used, modified and distributed,
5973+ * but this lines must be kept in every original or derived version.
5974+ * Original author "Antonio Nati" and the web URL
5975+ * "http://www.interazioni.it/opensource"
5976+ * must be indicated in every related work or web page
5977+ *
5978+ */
5979+
5980+#define CHKUSER
5981+#define CHKUSER_VERSION                "2.0.9"
5982+#define CHKUSER_VERSION_RL     2
5983+#define CHKUSER_VERSION_MJ     0
5984+#define CHKUSER_VERSION_MN     9
5985+
5986+#define CHKUSER_OK_NOCHECKALL          11
5987+#define CHKUSER_OK_NOCHECKDOMAIN       10
5988+#define CHKUSER_OK                     1
5989+#define CHKUSER_RELAYING               0
5990+#define CHKUSER_KO                     -1
5991+#define CHKUSER_NORCPTHOSTS            -10
5992+#define CHKUSER_ERR_MUSTAUTH           -15
5993+#define CHKUSER_ERR_AUTH_RESOURCE      -20
5994+#define CHKUSER_ERR_MBXFULL            -30
5995+#define CHKUSER_ERR_MAXRCPT            -40
5996+#define CHKUSER_ERR_MAXWRONGRCPT       -50
5997+#define CHKUSER_ERR_DOMAIN_MISSING     -60
5998+#define CHKUSER_ERR_RCPT_FORMAT                -70
5999+#define CHKUSER_ERR_RCPT_MX            -75
6000+#define CHKUSER_ERR_RCPT_MX_TMP                -76
6001+#define CHKUSER_ERR_SENDER_FORMAT      -80
6002+#define CHKUSER_ERR_SENDER_MX          -85
6003+#define CHKUSER_ERR_SENDER_MX_TMP      -86
6004+#define CHKUSER_ERR_INTRUSION_THRESHOLD        -90
6005+
6006+
6007+void chkuser_cleanup (int exit_value);
6008+int chkuser_realrcpt (stralloc *sender, stralloc *rcpt);
6009+int chkuser_sender (stralloc *sender);
6010+
6011+#ifdef TLS_H
6012+#undef _exit
6013+#define _exit(value) { if (ssl) ssl_free(ssl); chkuser_cleanup(value); }
6014+#else
6015+#define _exit(value) chkuser_cleanup(value);
6016+#endif
6017diff -ruN ../netqmail-1.06-original/chkuser_settings.h netqmail-1.06/chkuser_settings.h
6018--- ../netqmail-1.06-original/chkuser_settings.h        1970-01-01 01:00:00.000000000 +0100
6019+++ netqmail-1.06/chkuser_settings.h    2020-06-16 22:27:56.782600304 +0200
6020@@ -0,0 +1,468 @@
6021+/*
6022+ *
6023+ * 'chkuser_settings.h' v.2.0.9
6024+ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
6025+ *
6026+ * Author: Antonio Nati tonix@interazioni.it
6027+ * All rights on this software and
6028+ * the identifying words chkusr and chkuser reserved by the author
6029+ *
6030+ * This software may be freely used, modified and distributed,
6031+ * but this lines must be kept in every original or derived version.
6032+ * Original author "Antonio Nati" and the web URL
6033+ * "http://www.interazioni.it/opensource"
6034+ * must be indicated in every related work or web page
6035+ *
6036+ */
6037+
6038+/*
6039+ * the following line enables debugging of chkuser
6040+ */
6041+#define CHKUSER_DEBUG
6042+
6043+/*
6044+ * The following line moves DEBUG output from STDOUT (default) to STDERR
6045+ * Example of usage within sh: ./qmail-smtpd 2> /var/log/smtpd-debug.log
6046+ */
6047+#define CHKUSER_DEBUG_STDERR
6048+
6049+/*
6050+ * Uncomment the following define if you want chkuser ALWAYS enabled.
6051+ * If uncommented, it will check for rcpt existance despite any .qmail-default
6052+ * setting.
6053+ * So, unsomments this if you are aware that ALL rcpt in all domains will be
6054+ * ALWAYS checked.
6055+ */
6056+/* #define CHKUSER_ALWAYS_ON */
6057+
6058+/*
6059+ * The following defines which virtual manager is used.
6060+ * Up to know, only vpopmail, but versions with pure qmail are in the mind.
6061+ */
6062+#define CHKUSER_VPOPMAIL
6063+
6064+/*
6065+ * Uncomment the following line if you want chkuser to work depending on a VARIABLE setting
6066+ * VALUE HERE DEFINED is the name of the variable
6067+ * Values admitted inside the variable: NONE | ALWAYS | DOMAIN
6068+ *             NONE    = chkuser will not work
6069+ *             ALWAYS  = chkuser will work always
6070+ *             DOMAIN  = chkuser will work depending by single domain settings
6071+ * CHKUSER_STARTING_VARIABLE cannot be defined together with CHKUSER_ALWAYS_ON
6072+ * if CHKUSER_STARTING_VARIABLE is defined, and no variable or no value is set, then chkuser is disabled
6073+ */
6074+#define CHKUSER_STARTING_VARIABLE "CHKUSER_START"
6075+
6076+/*
6077+ * Uncomment this to enable uid/gid changing
6078+ * (switching UID/GID is NOT compatible with TLS; you may keep this commented if you have TLS)
6079+ */
6080+/* #define CHKUSER_ENABLE_UIDGID */
6081+
6082+/*
6083+ * Uncomment this to check if a domain is ALWAYS specified in rcpt addresses
6084+ */
6085+#define CHKUSER_DOMAIN_WANTED
6086+
6087+/*
6088+ * Uncomment this to check for vpopmail users
6089+ */
6090+#define CHKUSER_ENABLE_USERS
6091+
6092+/*
6093+ * Uncomment this to check for alias
6094+ */
6095+#define CHKUSER_ENABLE_ALIAS
6096+
6097+/*
6098+ * The following #define set the character used for lists extensions
6099+ * be careful: this is a  single char '-' definition, not a "string"
6100+ */
6101+#define CHKUSER_EZMLM_DASH '-'
6102+
6103+/*
6104+ * Uncomment this to set an alternative way to check for bouncing enabling;
6105+ * with this option enabled, the file here defined
6106+ * will be searched, inside the domain dir, in order to check if bouncing is enabled
6107+ * The content of this file is not important, just it's existence is enough
6108+ */
6109+/* #define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing" */
6110+
6111+/*
6112+ * This is the string to look for inside .qmail-default
6113+ * Be careful, chkuser looks within the first 1023 characters of .qmail-default for
6114+ * this string (despite the line containing the string is working or commented).
6115+ */
6116+#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox"
6117+
6118+
6119+/*
6120+ * Uncomment to enable logging of rejected recipients and variuos limits reached
6121+ */
6122+#define CHKUSER_ENABLE_LOGGING
6123+
6124+/*
6125+ * Uncomment to enable logging of "good" rcpts
6126+ * valid only if CHKUSER_ENABLE_LOGGING is defined
6127+ */
6128+#define CHKUSER_LOG_VALID_RCPT
6129+
6130+/*
6131+ * Uncomment to enable usage of a variable escluding any check on the sender.
6132+ * The variable should be set in tcp.smtp for clients, with static IP, whose mailer
6133+ * is composing bad sender addresses
6134+ * Defining it as "RELAYCLIENT" will avoid sender checking for authenticated/authorized users.
6135+ *     Senders will be logged anyway if CHKUSER_LOG_VALID_SENDER is defined.
6136+ */
6137+#define CHKUSER_SENDER_NOCHECK_VARIABLE "RELAYCLIENT"
6138+
6139+/*
6140+ * Uncomment to enable usage of "#" and "+" characters within sender address
6141+ * This is used by SRS (Sender Rewriting Scheme) products
6142+ */
6143+#define CHKUSER_ALLOW_SENDER_SRS
6144+
6145+/*
6146+ * The following #define sets the minimum length of a domain:
6147+ * as far as I know, "k.st" is the shortest domain, so 4 characters is the
6148+ * minimum length.
6149+ * This value is used to check formally a domain name validity.
6150+ * if CHKUSER_SENDER_FORMAT is undefined, no check on length is done.
6151+ * If you comment this define, no check on length is done.
6152+ */
6153+#define CHKUSER_MIN_DOMAIN_LEN 4
6154+
6155+/*
6156+ * Uncomment to enable logging of "good" senders
6157+ * valid only if CHKUSER_ENABLE_LOGGING is defined
6158+ */
6159+#define CHKUSER_LOG_VALID_SENDER
6160+
6161+/*
6162+ * Uncomment to define a variable which contains the max recipients number
6163+ * this will return always error if total recipients exceed this limit.
6164+ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE,
6165+ * makes chkuser rejecting everything else
6166+ */
6167+#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT"
6168+
6169+/*
6170+ * Uncomment to define a variable which contains the max unknown recipients number
6171+ * this will return always error if not existing recipients exceed this limit.
6172+ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE,
6173+ * makes chkuser rejecting everything else
6174+ */
6175+#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT"
6176+
6177+/*
6178+ * Uncomment to define the variable containing the percent to check for.
6179+ * Remember to define externally (i.e. in tcp.smtp) the environment variable containing
6180+ * the limit percent.
6181+ * If the variable is not defined, or it is <= 0, quota checking is not performed.
6182+ */
6183+#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA"
6184+
6185+/*
6186+ * Delay to wait for each not existing recipient
6187+ * value is expressed in milliseconds
6188+ */
6189+#define CHKUSER_ERROR_DELAY 1000
6190+
6191+/*
6192+ * Uncomment to consider rcpt errors on address format and MX as intrusive
6193+ *
6194+ */
6195+#define CHKUSER_RCPT_DELAY_ANYERROR
6196+
6197+/*
6198+ * Uncomment to consider sender errors on address format and MX as intrusive
6199+ *
6200+ */
6201+#define CHKUSER_SENDER_DELAY_ANYERROR
6202+
6203+
6204+/***************************************************
6205+ *
6206+ *      new/modified defines in/from 2.0.6
6207+ *
6208+ **************************************************/
6209+
6210+/*
6211+ * Before version 5.3.25, vpopmail used the function vget_real_domain()
6212+ * to get the real name of a domain (useful if rcpt domain is aliasing
6213+ * another domain).
6214+ * From version 5.3.25, this call is not available and has been
6215+ * substituted by other calls.
6216+ *
6217+ *        must be enabled if vpopmail version< 5.3.5
6218+ *        must be disabled  if vpopmail version => 5.3.5 *
6219+ */
6220+/* #define CHKUSER_ENABLE_VGET_REAL_DOMAIN */
6221+
6222+/***************************************************
6223+ *
6224+ *      new/modified defines in/from 2.0.7
6225+ *
6226+ **************************************************/
6227+
6228+/*
6229+ * Uncomment next define to accept recipients for
6230+ * aliases that have a -default extension
6231+ */
6232+#define CHKUSER_ENABLE_ALIAS_DEFAULT
6233+
6234+
6235+/*
6236+ * Uncomment to enable usage of "#" and "+" characters within rcpt address
6237+ * This is used by SRS (Sender Rewriting Scheme) products
6238+ */
6239+#define CHKUSER_ALLOW_RCPT_SRS
6240+
6241+/*
6242+ * This define has been eliminated and its usage will generate an error.
6243+ * Turning it ON or OFF has no effect, as we consider the existence
6244+ * of #define VALIAS inside ~vpopmail/include/vpopmail_config.h
6245+ */
6246+/* #define CHKUSER_ENABLE_VALIAS */
6247+
6248+/*
6249+ * Uncomment this to enable user extension on names (i.e. TMDA)
6250+ * (for mailing lists this is done without checking this define)
6251+ * This define substitutes #define CHKUSER_ENABLE_EXTENSIONS
6252+ */
6253+/* #define CHKUSER_ENABLE_USERS_EXTENSIONS */
6254+
6255+/*
6256+ * Enables checking for EZMLM lists
6257+ * this define substitutes #define CHKUSER_ENABLE_LISTS
6258+ *
6259+ */
6260+#define CHKUSER_ENABLE_EZMLM_LISTS
6261+
6262+/*
6263+ * Help identifying remote authorized IPs giving them a descriptive name
6264+ * Can be put in tcp.smtp, and will be displayed inside chkuser log
6265+ */
6266+#define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY"
6267+
6268+/*
6269+ * The following #define set the character used for users extensions
6270+ * be careful: this is a  single char '-' definition, not a "string"
6271+ * this define substitutes #define CHKUSER_EXTENSION_DASH
6272+ * MUST be defined if CHKUSER_ENABLE_USERS_EXTENSIONS is defined
6273+ */
6274+#define CHKUSER_USERS_DASH '-'
6275+
6276+/*
6277+ * Enables checking for mailman lists
6278+ *
6279+ */
6280+/* #define CHKUSER_ENABLE_MAILMAN_LISTS */
6281+
6282+/*
6283+ * Identifies the pattern string to be searched within mailman aliases
6284+ *
6285+ */
6286+#define CHKUSER_MAILMAN_STRING "mailman"
6287+
6288+/*
6289+ * The following #define set the character used for mailman lists extensions
6290+ * be careful: this is a  single char '-' definition, not a "string"
6291+ */
6292+#define CHKUSER_MAILMAN_DASH '-'
6293+
6294+
6295+/*
6296+ * Enables final clean-up routine of chkuser
6297+ * This routine cleans open DB connections used for checking users and valiases
6298+ */
6299+#define CHKUSER_DB_CLEANUP
6300+
6301+/***************************************************
6302+ *
6303+ *      new/modified defines in/from 2.0.8
6304+ *
6305+ **************************************************/
6306+
6307+/*
6308+ * The following defines are NO MORE used. NULL SENDER rejecting breaks RFC
6309+ * compatibility, and makes harder to handle e-mail receipts.
6310+ * Please comment or delete them from your chkuser_settings.h.
6311+ */
6312+/* #define CHKUSER_ACCEPT_NULL_SENDER */
6313+/* #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST */
6314+
6315+/*
6316+ * Uncomment to enable checking of user and domain format for rcpt addresses
6317+ *      user    =       [a-z0-9_-]
6318+ *      domain  =       [a-z0-9-.] with not consecutive "-.", not leading or ending "-."
6319+ */
6320+#define CHKUSER_RCPT_FORMAT
6321+
6322+/*
6323+ * Uncomment to enable checking of domain MX for rcpt addresses
6324+ * It works on any rcpt address domain that is not inside rcpthosts
6325+ */
6326+#define CHKUSER_RCPT_MX
6327+
6328+/*
6329+ * Uncomment to enable checking of user and domain format for sender address
6330+ *      user    =       [a-z0-9_-]
6331+ *      domain  =       [a-z0-9-.] with not consecutive "-.", not leading or ending "-."
6332+ */
6333+#define CHKUSER_SENDER_FORMAT
6334+
6335+/*
6336+ * Uncomment to enable checking of domain MX for sender address
6337+ * it works on the first rcpt address, despite of any domain setting on chkuser
6338+ */
6339+#define CHKUSER_SENDER_MX
6340+
6341+/*
6342+ * Delay to add, for each not existing recipient, to the initial CHKUSER_ERROR_DELAY value
6343+ * value is expressed in milliseconds
6344+ */
6345+#define CHKUSER_ERROR_DELAY_INCREASE 300
6346+
6347+/***************************************************
6348+ *
6349+ *      new/modified defines in/from 2.0.9
6350+ *
6351+ **************************************************/
6352+
6353+/*
6354+ * A new class of defines is introduced
6355+ *     CHKUSER_EXTRA_xxxxx
6356+ *
6357+ *     These defines will be used for features/behaviours that may work despite of other CHKUSER enable/disable settings
6358+ *
6359+ */
6360+
6361+/*
6362+ * If you want to accept only authenticated/authorized users you MUST enable this define and set the related variable.
6363+ *
6364+ * if this define is uncommented and the variable is set (to whatever value) then RELAYCLIENT must be set
6365+ *      otherwise any message will be rejected giving "not authorized" error.
6366+ *
6367+ */
6368+/* #define CHKUSER_EXTRA_MUSTAUTH_VARIABLE "CHKUSER_MUSTAUTH" */
6369+
6370+
6371+/*
6372+ * This is to check DB availability
6373+ * It avoids bouncing messages with wrong codes if MySQL/LDAP/PostGRES/etc are down or not reachable
6374+ *
6375+ * If you are using MySQL in normal installation use #define CHKUSER_VAUTH_OPEN_CALL vauth_open_update
6376+ * If you are using MySQL with separate servers for read and write use #define CHKUSER_VAUTH_OPEN_CALL vauth_open
6377+ * If you are using other DB, check the most appropriate function for your DB within dedicated vpopmail module
6378+ *
6379+ * This define substitutes CHKUSER_ENABLE_VAUTH_OPEN
6380+ */
6381+
6382+/* #define CHKUSER_VAUTH_OPEN_CALL vauth_open   */
6383+#define CHKUSER_VAUTH_OPEN_CALL vauth_open_update
6384+
6385+/*
6386+ * Variable to be set in order to disable chkuser
6387+ * You may set it to any value you like. If it exists chkuser will be disabled.
6388+ *     Setting it to RELAYCLIENT helps disabling chkuser when sender is a known/authenticated mail client
6389+ *     This is useful because Outlook/Eudora and other clients are not able to handle a KO when multiple recipients
6390+ *             are present in the message. They should always relay to a SMTP service accepting all.
6391+ *
6392+ *     Recipients will be logged anyway if CHKUSER_LOG_VALID_RCPT is defined.
6393+ *
6394+ * Important changes from 2.0.9
6395+ *     CHKUSER_ALWAYS_ON and CHKUSER_STARTING_VARIABLE cannot be defined together and in such a case a fatal error is displayed
6396+ *     (in the previous versions CHKUSER_ALWAYS_ON would automatically disable CHKUSER_STARTING_VARIABLE definition)
6397+ *
6398+ *     CHKUSER_DISABLE_VARIABLE is always evaluated after CHKUSER_ALWAYS_ON is set or CHKUSER_STARTING_VARIABLE is evaluated, so
6399+ *             CHKUSER_ALWAYS_ON or CHKUSER_STARTING_VARIABLE can set the general behaviour, while CHKUSER_DISABLE_VARIABLE
6400+ *             should be invoked to handle exceptions.
6401+ *
6402+ */
6403+#define CHKUSER_DISABLE_VARIABLE "RELAYCLIENT"
6404+
6405+
6406+/*
6407+ * Error strings (SMTP error answers)
6408+ * If you don't like these definitions you can change them here
6409+ *
6410+ */
6411+#define CHKUSER_NORCPT_STRING "550 5.1.1 sorry, no mailbox here by that name (chkuser)\r\n"
6412+#define CHKUSER_RESOURCE_STRING "451 4.3.0 system temporary unavailable, try again later (chkuser)\r\n"
6413+#define CHKUSER_MBXFULL_STRING "552 5.2.2 sorry, recipient mailbox is full (chkuser)\r\n"
6414+#define CHKUSER_MAXRCPT_STRING "550 5.5.3 sorry, reached maximum number of recipients allowed in one session (chkuser)\r\n"
6415+#define CHKUSER_MAXWRONGRCPT_STRING "550 5.5.3 sorry, you are violating our security policies (chkuser)\r\n"
6416+#define CHKUSER_DOMAINMISSING_STRING "550 5.1.2 sorry, you must specify a domain (chkuser)\r\n"
6417+#define CHKUSER_RCPTFORMAT_STRING "553 5.1.3 sorry, mailbox syntax not allowed (chkuser)\r\n"
6418+#define CHKUSER_RCPTMX_STRING "550 5.1.2 sorry, can't find a valid MX for rcpt domain (chkuser)\r\n"
6419+#define CHKUSER_SENDERFORMAT_STRING "553 5.1.7 sorry, mailbox syntax not allowed (chkuser)\r\n"
6420+#define CHKUSER_SENDERMX_STRING "550 5.1.8 sorry, can't find a valid MX for sender domain (chkuser)\r\n"
6421+#define CHKUSER_INTRUSIONTHRESHOLD_STRING "550 5.7.1 sorry, you are violating our security policies (chkuser)\r\n"
6422+#define CHKUSER_NORELAY_STRING "553 5.7.1 sorry, that domain isn't in my list of allowed rcpthosts (chkuser)\r\n"
6423+
6424+#define CHKUSER_RCPTMX_TMP_STRING "451 4.4.0 DNS temporary failure (chkuser)\r\n"
6425+#define CHKUSER_SENDERMX_TMP_STRING "451 4.4.0 DNS temporary failure (chkuser)\r\n"
6426+
6427+#define CHKUSER_MUSTAUTH_STRING "530 5.7.0 Authentication required (chkuser)\r\n"
6428+
6429+/*
6430+ * No more used defines
6431+ *     Following defines are eliminated since 2.0.9
6432+ *     They will make compilation errors and must be deleted/commented
6433+ *
6434+ *                     #define CHKUSER_ENABLE_VAUTH_OPEN -> Substituted by CHKUSER_VAUTH_OPEN_CALL
6435+ */
6436+
6437+
6438+/*
6439+ * If you need more additional characters to be accepted within sender address
6440+ * uncomment one of the following #define and edit the character value.
6441+ * Be careful to use '*' (single hiphen) and NOT "*" (double hiphen) around the
6442+ * wanted char.
6443+ *
6444+ * Remember: '#' and '+' are accepted by CHKUSER_ALLOW_SENDER_SRS
6445+ *
6446+ */
6447+#define CHKUSER_ALLOW_SENDER_CHAR_1 "'"
6448+#define CHKUSER_ALLOW_SENDER_CHAR_2 '='
6449+/* #define CHKUSER_ALLOW_SENDER_CHAR_2 '%' */
6450+/* #define CHKUSER_ALLOW_SENDER_CHAR_3 '£' */
6451+/* #define CHKUSER_ALLOW_SENDER_CHAR_4 '?' */
6452+/* #define CHKUSER_ALLOW_SENDER_CHAR_5 '*' */
6453+/* #define CHKUSER_ALLOW_SENDER_CHAR_6 '^' */
6454+/* #define CHKUSER_ALLOW_SENDER_CHAR_7 '~' */
6455+/* #define CHKUSER_ALLOW_SENDER_CHAR_8 '&' */  /* available for other characters */
6456+/* #define CHKUSER_ALLOW_SENDER_CHAR_9 '#' */  /* available for other characters */
6457+/* #define CHKUSER_ALLOW_SENDER_CHAR_10 '=' */         /* available for other characters */
6458+
6459+
6460+/*
6461+ * If you need more additional characters to be accepted within rcpt address
6462+ * uncomment one of the following #define and edit the character value.
6463+ * Be careful to use '*' (single hiphen) and NOT "*" (double hiphen) around the
6464+ * wanted char.
6465+ *
6466+ * Remember: '#' and '+' are accepted by CHKUSER_ALLOW_RCPT_SRS
6467+ *
6468+ */
6469+/* #define CHKUSER_ALLOW_RCPT_CHAR_1 '$' */
6470+/* #define CHKUSER_ALLOW_RCPT_CHAR_2 '%' */
6471+/* #define CHKUSER_ALLOW_RCPT_CHAR_3 '£' */
6472+/* #define CHKUSER_ALLOW_RCPT_CHAR_4 '?' */
6473+/* #define CHKUSER_ALLOW_RCPT_CHAR_5 '*' */
6474+/* #define CHKUSER_ALLOW_RCPT_CHAR_6 '^' */
6475+/* #define CHKUSER_ALLOW_RCPT_CHAR_7 '~' */
6476+/* #define CHKUSER_ALLOW_RCPT_CHAR_8 '&' */    /* available for other characters */
6477+/* #define CHKUSER_ALLOW_RCPT_CHAR_9 '#' */    /* available for other characters */
6478+/* #define CHKUSER_ALLOW_RCPT_CHAR_10 '=' */   /* available for other characters */
6479+
6480+
6481+/*
6482+ * This define tells chkuser which variable must be set to accept a <#@[]> sender
6483+ * This kind of sender is usually generated from qmail when there is a doublebounce
6484+ * and all the job is done within the same system.
6485+ * You may need to accept double bounces from outside when you are migrating servers and
6486+ * doublebounces are forwarded between systems
6487+ */
6488+#define CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE "CHKUSER_DOUBLEBOUNCE"
6489diff -ruN ../netqmail-1.06-original/condredirect.c netqmail-1.06/condredirect.c
6490--- ../netqmail-1.06-original/condredirect.c    1998-06-15 12:53:16.000000000 +0200
6491+++ netqmail-1.06/condredirect.c        2019-02-27 20:57:13.384025136 +0100
6492@@ -10,6 +10,8 @@
6493 #include "strerr.h"
6494 #include "substdio.h"
6495 #include "fmt.h"
6496+#include "stralloc.h"
6497+#include "srs.h"
6498 
6499 #define FATAL "condredirect: fatal: "
6500 
6501@@ -68,6 +70,16 @@
6502   dtline = env_get("DTLINE");
6503   if (!dtline) strerr_die2x(100,FATAL,"DTLINE not set");
6504 
6505+  if (str_len(sender)) {
6506+    switch(srsforward(sender)) {
6507+      case -3: strerr_die2x(100,FATAL,srs_error.s); break;
6508+      case -2: strerr_die2x(111,FATAL,"out of memory"); break;
6509+      case -1: strerr_die2x(111,FATAL,"unable to read controls"); break;
6510+      case 0: break; // nothing
6511+      case 1: sender = srs_result.s; break;
6512+    }
6513+  }
6514+
6515   if (qmail_open(&qqt) == -1)
6516     strerr_die2sys(111,FATAL,"unable to fork: ");
6517   qmail_puts(&qqt,dtline);
6518diff -ruN ../netqmail-1.06-original/conf-cc netqmail-1.06/conf-cc
6519--- ../netqmail-1.06-original/conf-cc   1998-06-15 12:53:16.000000000 +0200
6520+++ netqmail-1.06/conf-cc       2020-01-10 21:52:13.080721081 +0100
6521@@ -1,3 +1,3 @@
6522-cc -O2
6523+cc -O2 -g -DEXTERNAL_TODO -DTLS=20200107 -I/usr/local/ssl/include -I/home/vpopmail/include
6524 
6525 This will be used to compile .c files.
6526diff -ruN ../netqmail-1.06-original/conf-channels netqmail-1.06/conf-channels
6527--- ../netqmail-1.06-original/conf-channels     1970-01-01 01:00:00.000000000 +0100
6528+++ netqmail-1.06/conf-channels 2019-06-26 19:12:46.033685227 +0200
6529@@ -0,0 +1,4 @@
6530+2
6531+
6532+Total number of channels (queues) available for delivery. Must be at
6533+least 2, and anything above 2 are considered supplemental channels.
6534diff -ruN ../netqmail-1.06-original/conf-domainkeys netqmail-1.06/conf-domainkeys
6535--- ../netqmail-1.06-original/conf-domainkeys   1970-01-01 01:00:00.000000000 +0100
6536+++ netqmail-1.06/conf-domainkeys       2019-02-27 20:57:13.384025136 +0100
6537@@ -0,0 +1 @@
6538+-DDOMAIN_KEYS
6539diff -ruN ../netqmail-1.06-original/conf-ld netqmail-1.06/conf-ld
6540--- ../netqmail-1.06-original/conf-ld   1998-06-15 12:53:16.000000000 +0200
6541+++ netqmail-1.06/conf-ld       2019-02-27 20:57:13.384025136 +0100
6542@@ -1,3 +1,3 @@
6543-cc -s
6544+cc -g
6545 
6546 This will be used to link .o files into an executable.
6547diff -ruN ../netqmail-1.06-original/conf-policy netqmail-1.06/conf-policy
6548--- ../netqmail-1.06-original/conf-policy       1970-01-01 01:00:00.000000000 +0100
6549+++ netqmail-1.06/conf-policy   2019-02-27 20:57:13.384025136 +0100
6550@@ -0,0 +1,17 @@
6551+-DPOLICY_FILENAME="/var/qmail/control/policy" -DPOLICY_DEALLOCATE -DPOLICY_ENFORCE_AUTHENTICATION
6552+
6553+POLICY_FILENAME
6554+   Sets where the policy file is located
6555+
6556+POLICY_ENFORCE_AUTHENTICATION
6557+   Require that senders who use a local name for envelope
6558+   authenticate.  This is the recommended setting.
6559+
6560+POLICY_DEALLOCATE
6561+   For every MAIL FROM, RCPT TO combination, re-read all
6562+   policy information.  This makes policies more dynamic,
6563+   and stops remote users from causing lots of memory usage,
6564+   however, it also increases disk i/o, and slows down
6565+   policy enforcement.  If this is not defined, domain policies
6566+   will stay in memory until the SMTP session is ended.
6567+
6568diff -ruN ../netqmail-1.06-original/conf-spawn netqmail-1.06/conf-spawn
6569--- ../netqmail-1.06-original/conf-spawn        1998-06-15 12:53:16.000000000 +0200
6570+++ netqmail-1.06/conf-spawn    2019-02-27 20:57:13.384025136 +0100
6571@@ -1,4 +1,4 @@
6572-120
6573+1000
6574 
6575 This is a silent concurrency limit. You can't set it above 255. On some
6576 systems you can't set it above 125. qmail will refuse to compile if the
6577diff -ruN ../netqmail-1.06-original/config.h netqmail-1.06/config.h
6578--- ../netqmail-1.06-original/config.h  1970-01-01 01:00:00.000000000 +0100
6579+++ netqmail-1.06/config.h      2019-02-27 20:57:13.384025136 +0100
6580@@ -0,0 +1,10 @@
6581+/* config.h.  Generated from config.h.in by configure.  */
6582+/* config.h.in.  Generated from configure.in by autoheader.  */
6583+
6584+/* Define to 1 if you have the <arpa/nameser.h> header file. */
6585+
6586+/* Define to 1 if you have the <dlfcn.h> header file. */
6587+
6588+/* HAVE_EVP_SHA256 */
6589+#define HAVE_EVP_SHA256 1
6590+
6591diff -ruN ../netqmail-1.06-original/control.c netqmail-1.06/control.c
6592--- ../netqmail-1.06-original/control.c 1998-06-15 12:53:16.000000000 +0200
6593+++ netqmail-1.06/control.c     2019-02-27 20:57:13.384025136 +0100
6594@@ -85,6 +85,82 @@
6595  return 1;
6596 }
6597 
6598+int
6599+control_readulong(i, fn)
6600+       unsigned long  *i;
6601+       char           *fn;
6602+{
6603+       unsigned long   u;
6604+
6605+       switch (control_readline(&line, fn))
6606+       {
6607+       case 0:
6608+               return 0;
6609+       case -1:
6610+               return -1;
6611+       }
6612+       if (!stralloc_0(&line))
6613+               return -1;
6614+       if (!scan_ulong(line.s, &u))
6615+               return 0;
6616+       *i = u;
6617+       return 1;
6618+}
6619+
6620+/*
6621+ * read entire file in variable sa
6622+ * without any interpretation (e.g. comments)
6623+ * To be used in case a file contains '#' character
6624+ * in the first column (which control_readfile() will
6625+ * skip
6626+ */
6627+int
6628+control_readnativefile(sa, fn, mode)
6629+      stralloc       *sa;
6630+      char           *fn;
6631+      int             mode;
6632+{
6633+      substdio        ss;
6634+      int             fd, match;
6635+
6636+      if (!stralloc_copys(sa, ""))
6637+              return -1;
6638+      if ((fd = open_read(fn)) == -1)
6639+      {
6640+              if (errno == error_noent)
6641+                      return(0);
6642+              return -1;
6643+      }
6644+      substdio_fdbuf(&ss, read, fd, inbuf, sizeof(inbuf));
6645+      for (;;)
6646+      {
6647+              if (getln(&ss, &line, &match, '\n') == -1)
6648+                      break;
6649+              if (!match && !line.len)
6650+              {
6651+                      close(fd);
6652+                      return 1;
6653+              }
6654+              if (mode) /* for qmail-dk */
6655+              {
6656+                      striptrailingwhitespace(&line);
6657+                      if (!stralloc_0(&line))
6658+                              break;
6659+                      if (line.s[0] && !stralloc_cat(sa, &line))
6660+                              break;
6661+              } else
6662+              if (!stralloc_cat(sa, &line))
6663+                      break;
6664+              if (!match)
6665+              {
6666+                      close(fd);
6667+                      return 1;
6668+              }
6669+      }
6670+      close(fd);
6671+      return -1;
6672+}
6673+
6674 int control_readfile(sa,fn,flagme)
6675 stralloc *sa;
6676 char *fn;
6677diff -ruN ../netqmail-1.06-original/control.h netqmail-1.06/control.h
6678--- ../netqmail-1.06-original/control.h 1998-06-15 12:53:16.000000000 +0200
6679+++ netqmail-1.06/control.h     2019-02-27 20:57:13.384025136 +0100
6680@@ -3,8 +3,10 @@
6681 
6682 extern int control_init();
6683 extern int control_readline();
6684+extern int control_readulong();
6685 extern int control_rldef();
6686 extern int control_readint();
6687+extern int control_readnativefile();
6688 extern int control_readfile();
6689 
6690 #endif
6691diff -ruN ../netqmail-1.06-original/date822fmt.c netqmail-1.06/date822fmt.c
6692--- ../netqmail-1.06-original/date822fmt.c      1998-06-15 12:53:16.000000000 +0200
6693+++ netqmail-1.06/date822fmt.c  2019-02-27 20:57:13.385025125 +0100
6694@@ -1,3 +1,4 @@
6695+#include <time.h>
6696 #include "datetime.h"
6697 #include "fmt.h"
6698 #include "date822fmt.h"
6699@@ -12,18 +13,51 @@
6700 {
6701   unsigned int i;
6702   unsigned int len;
6703+  time_t now;
6704+  datetime_sec utc;
6705+  datetime_sec local;
6706+  struct tm *tm;
6707+  struct datetime new_dt;
6708+  int minutes;
6709+
6710+  utc = datetime_untai(dt);
6711+  now = (time_t)utc;
6712+  tm = localtime(&now);
6713+  new_dt.year = tm->tm_year;
6714+  new_dt.mon = tm->tm_mon;
6715+  new_dt.mday = tm->tm_mday;
6716+  new_dt.hour = tm->tm_hour;
6717+  new_dt.min = tm->tm_min;
6718+  new_dt.sec = tm->tm_sec;
6719+  local = datetime_untai(&new_dt);
6720+
6721   len = 0;
6722-  i = fmt_uint(s,dt->mday); len += i; if (s) s += i;
6723+  i = fmt_uint(s,new_dt.mday); len += i; if (s) s += i;
6724   i = fmt_str(s," "); len += i; if (s) s += i;
6725-  i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i;
6726+  i = fmt_str(s,montab[new_dt.mon]); len += i; if (s) s += i;
6727   i = fmt_str(s," "); len += i; if (s) s += i;
6728-  i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i;
6729+  i = fmt_uint(s,new_dt.year + 1900); len += i; if (s) s += i;
6730   i = fmt_str(s," "); len += i; if (s) s += i;
6731-  i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i;
6732+  i = fmt_uint0(s,new_dt.hour,2); len += i; if (s) s += i;
6733   i = fmt_str(s,":"); len += i; if (s) s += i;
6734-  i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i;
6735+  i = fmt_uint0(s,new_dt.min,2); len += i; if (s) s += i;
6736   i = fmt_str(s,":"); len += i; if (s) s += i;
6737-  i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i;
6738-  i = fmt_str(s," -0000\n"); len += i; if (s) s += i;
6739+  i = fmt_uint0(s,new_dt.sec,2); len += i; if (s) s += i;
6740+
6741+  if (local < utc) {
6742+    minutes = (utc - local + 30) / 60;
6743+    i = fmt_str(s," -"); len += i; if (s) s += i;
6744+    i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i;
6745+    i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i;
6746+  }
6747+  else {
6748+    minutes = (local - utc + 30) / 60;
6749+    i = fmt_str(s," +"); len += i; if (s) s += i;
6750+    i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i;
6751+    i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i;
6752+  }
6753+
6754+  i = fmt_str(s,"\n"); len += i; if (s) s += i;
6755+
6756   return len;
6757 }
6758diff -ruN ../netqmail-1.06-original/dk-filter.9 netqmail-1.06/dk-filter.9
6759--- ../netqmail-1.06-original/dk-filter.9       1970-01-01 01:00:00.000000000 +0100
6760+++ netqmail-1.06/dk-filter.9   2020-04-10 11:08:43.313802048 +0200
6761@@ -0,0 +1,98 @@
6762+.TH dk-filter 8
6763+.SH NAME
6764+dk-filter \- sign/verify using DK/DKIM (SSP/ADSP optionally) and deliver a mail message for delivery
6765+.SH SYNOPSIS
6766+.B FILTERARGS=QMAILHOME/bin/dk-filter
6767+.SH DESCRIPTION
6768+.B dk-filter
6769+is a qfilter which can be set as a filter for
6770+.BR spawn-filter(8) .
6771+The filter can be set either as
6772+.B FILTERARGS
6773+or in the control file
6774+.BR filterargs .
6775+
6776+.B dk-filter
6777+supports DK/DKIM signing and verification and can optionally use
6778+.B Sender Signing Practice (SSP)
6779+or
6780+.B Author Domain Signing Practice.
6781+It uses the libdkim and OpenSSL libraries.  To sign a message, set the
6782+.B DKIMSIGN
6783+environment variable (for DKIM signing) or
6784+.B DKSIGN
6785+environment variable (for DK signing) to the pathname of the private key that will be
6786+used to sign the message. If there is a % character in the environment
6787+variable, it is removed and replaced by the domain name in the From: header.
6788+If, after substituting the %, that file does not exist, QMAILHOME/control/domainkeys/%/default
6789+will be used as the key. If again, after substituting the % sign, the file does not exist,
6790+QMAILHOME/control/domainkeys/default will be used as the key.
6791+After all substitutions, if the key file does not exist, the message will not be signed.
6792+If there is no % and the file does not exist, the message will be rejected with error 35.
6793+The default private key QMAILHOME/control/domainkeys/default can be overriden by the
6794+\fBDKIM_DEFAULT_KEY\fR environment variable.
6795+
6796+The selector (s=) will be taken from the basename of the file.
6797+The private key should be created by
6798+.BR dknewkey(8) .
6799+
6800+You can set various DK options in getopt style, by setting the environment variable DKSIGNOPTIONS
6801+ b <advice_length>    Length of Advice
6802+ c <canonicalization> simple, nofws
6803+ s <privkeyfile>
6804+ h                    show headers included
6805+ r                    Skip Duplicate Headers
6806+.EX
6807+ DKSIGNOPTIONS="-h -r -c nofws"
6808+ sets the h= tag, skips duplicate headers and sets nofws canonicalization
6809+.EE
6810+
6811+You can set various DKIM options in getopt style, by setting the environment variable DKIMSIGNOPTIONS
6812+
6813+ b <standard>         1 - allman, 2 - ietf or 3 - both
6814+ c <canonicalization> r for relaxed [DEFAULT], s - simple,
6815+                      t relaxed/simple, u - simple/relaxed
6816+ l                    include body length tag
6817+ q                    include query method tag;
6818+ t                    include a timestamp tag
6819+ h
6820+ i <identity>         the identity, if not provided it will not be included
6821+ x <expire_time>      the expire time in seconds since epoch
6822+                      ( DEFAULT = current time + 604800)
6823+                      if set to - then it will not be included
6824+ z <hash>             1 for sha1, 2 for sha256, 3 for both
6825+ s <privkeyfile>
6826+ y <selector>
6827+
6828+.EX
6829+ DKIMSIGNOPTIONS="-b 1 -c r -q"
6830+ sets allman standard, with relaxed canonicalization and include query method tag
6831+.EE
6832+
6833+when signing \fBdk-filter\fR uses the domain found in the Return-Path, Sender, From headers to set
6834+the domain tag. If not it uses the value of \fBDKIMDOMAIN\fR environment
6835+variable. \fBDKIMDOMAIN\fR can be set to an email address or a domain (without the at sign).
6836+
6837+To verify a message, set the \fBDKIMVERIFY\fR or \fBDKVERIFY\fR environment variables.
6838+\fBdk-filter\fR always inserts the \fBDKIM-Status\fR or \fBDomainKey-Status\fR header, so
6839+that messages can be rejected later at delivery time, or in the mail reader. In that case
6840+you may set \fBDKIMVERIFY\fR or \fBDKVERIFY\fR to an empty string. The exit code of \fBdk-filter\fR
6841+can be fine tuned by setting \fBDKIMVERIFY\fR environment variable. See \fBdkim(8)\fR for a detailed
6842+description on setting the \fBDKIMVERIFY\fR environment variable.
6843+
6844+\fBdk-filter\fR does not use any signing practice by default. You can override this by setting
6845+the \fBSIGN_PRACTICE\fR to ssp or adsp (lowercase).
6846+
6847+If neither of these environment variables (\fBDKIMSIGN\fR, \fBDKSIGN\fR, \fBDKIMVERIFY\fR, \fBDKVERIFY\fR) are defined, \fBdk-filter\fR
6848+will do signing by default.
6849+
6850+You can set environment variable \fBNODK\fR to disable domainkeys and \fBNODKIM\fR to disable \fBDKIM\fR.
6851+
6852+.SH "EXIT CODES"
6853+0 for success, non-zero failure
6854+
6855+.SH "SEE ALSO"
6856+dknewkey(8),
6857+dktest(8),
6858+dkim(8),
6859+spawn-filter(8)
6860diff -ruN ../netqmail-1.06-original/dk-filter.sh netqmail-1.06/dk-filter.sh
6861--- ../netqmail-1.06-original/dk-filter.sh      1970-01-01 01:00:00.000000000 +0100
6862+++ netqmail-1.06/dk-filter.sh  2020-07-29 15:59:13.351023490 +0200
6863@@ -0,0 +1,367 @@
6864+#
6865+# $Log: dk-filter.sh,v $
6866+# Revision 1.22  2019-06-24 23:19:57+05:30  Cprogrammer
6867+# added code for -d option in DKIMSIGNOPTIONS
6868+#
6869+# Revision 1.21  2019-01-14 00:10:00+05:30  Cprogrammer
6870+# added -S, -f option to verify signatures with unsigned subject, unsigned from
6871+#
6872+# Revision 1.20  2017-03-09 16:38:15+05:30  Cprogrammer
6873+# FHS changes
6874+#
6875+# Revision 1.19  2016-05-17 23:11:42+05:30  Cprogrammer
6876+# fix for configurable control directory
6877+#
6878+# Revision 1.18  2014-03-12 08:50:48+05:30  Cprogrammer
6879+# bug - fixed signing when env variables DKSIGN or DKIMSIGN were set
6880+#
6881+# Revision 1.17  2013-09-03 23:04:30+05:30  Cprogrammer
6882+# set signing as default if both DKSIGN and DKIMSIGN are not defined
6883+#
6884+# Revision 1.16  2013-08-17 15:59:21+05:30  Cprogrammer
6885+# do not treat duplicate DomainKey-Signature as an error
6886+#
6887+# Revision 1.15  2013-08-17 15:02:06+05:30  Cprogrammer
6888+# fixed syntax errors and private key lookup
6889+#
6890+# Revision 1.14  2011-02-10 22:47:01+05:30  Cprogrammer
6891+# fixed exit code of dk-filter when doing verification
6892+#
6893+# Revision 1.13  2011-02-08 22:02:29+05:30  Cprogrammer
6894+# use sender domain when replacing '%' in private key
6895+#
6896+# Revision 1.12  2010-05-04 08:37:42+05:30  Cprogrammer
6897+# do DK signing before DKIM signing to prevent DK_SYNTAX error
6898+#
6899+# Revision 1.11  2009-12-10 19:25:13+05:30  Cprogrammer
6900+# added RCS id
6901+#
6902+# Revision 1.10  2009-12-10 16:41:14+05:30  Cprogrammer
6903+# continue of message gives DK_SYNTAX_ERR
6904+#
6905+# Revision 1.9  2009-05-04 10:30:32+05:30  Cprogrammer
6906+# fixed argument expected error
6907+#
6908+# Revision 1.8  2009-04-21 20:42:44+05:30  Cprogrammer
6909+# added check for dktest, dkim executables
6910+#
6911+# Revision 1.7  2009-04-20 10:06:58+05:30  Cprogrammer
6912+# added DKSIGNOPTS
6913+#
6914+# Revision 1.6  2009-04-19 13:38:24+05:30  Cprogrammer
6915+# added full set of dkim options
6916+# replaced indimail/bin/echo with echo 1>&2
6917+#
6918+# Revision 1.5  2009-04-06 16:37:50+05:30  Cprogrammer
6919+# added SIGN_PRACTICE
6920+# use ietf standard insted of allman so that Yahoo verification does not fail
6921+#
6922+# Revision 1.4  2009-04-03 14:39:00+05:30  Cprogrammer
6923+# added return status
6924+#
6925+# Revision 1.3  2009-04-03 08:55:29+05:30  Cprogrammer
6926+# print error messages to stderr
6927+#
6928+# Revision 1.2  2009-04-02 20:36:25+05:30  Cprogrammer
6929+# added -h option to dktest
6930+# added -x - option to dkim
6931+#
6932+# Revision 1.1  2009-04-02 14:52:27+05:30  Cprogrammer
6933+# Initial revision
6934+#
6935+# $Id: dk-filter.sh,v 1.22 2019-06-24 23:19:57+05:30 Cprogrammer Exp mbhangui $
6936+#
6937+if [ -z "$QMAILREMOTE" -a -z "$QMAILLOCAL" ]; then
6938+       echo "dk-filter should be run by spawn-filter" 1>&2
6939+       exit 1
6940+fi
6941+dksign=0
6942+dkimsign=0
6943+dkverify=0
6944+dkimverify=0
6945+if [ -z "$DEFAULT_DKIM_KEY" ] ; then
6946+       default_key=QMAILHOME/control/domainkeys/default
6947+else
6948+       default_key=$DEFAULT_DKIM_KEY
6949+fi
6950+if [ -z "$NODK" -a -x QMAILHOME/bin/dktest -a -z "$DKVERIFY" ] ; then
6951+       if [ -z "$DKSIGN" ] ; then
6952+               DKSIGN=QMAILHOME/control/domainkeys/%/default
6953+               dksign=2
6954+       elif [ " $DKSIGN" = " QMAILHOME/control/domainkeys/%/default" ] ; then
6955+               dksign=2
6956+       fi
6957+fi
6958+if [ -z "$NODKIM" -a -x QMAILHOME/bin/dkim -a -z "$DKIMVERIFY" ] ; then
6959+       if [ -z "$DKIMSIGN" ] ; then
6960+               DKIMSIGN=QMAILHOME/control/domainkeys/%/default
6961+               dkimsign=2
6962+       elif [ " $DKIMSIGN" = " QMAILHOME/control/domainkeys/%/default" ] ; then
6963+               dkimsign=2
6964+       fi
6965+fi
6966+if [ -z "$NODK" -a -n "$DKSIGN" ] ; then
6967+       if [ ! -f QMAILHOME/bin/dktest ] ; then
6968+               echo "QMAILHOME/bin/dktest: No such file or directory" 1>&2
6969+               exit 1
6970+       fi
6971+       percent_found=0
6972+       echo $DKSIGN|grep "%" >/dev/null 2>&1
6973+       if [ $? -eq 0 ] ; then
6974+               percent_found=1
6975+       fi
6976+       if [ -n "$DKIMDOMAIN" ] && [ -z "$_SENDER" ] ; then
6977+               dkkeyfn=`echo $DKSIGN | sed s{%{$DKIMDOMAIN{g`
6978+       elif [ ! " $_SENDER" = " " ] ; then
6979+               # replace '%' in filename with domain
6980+               domain=`echo $_SENDER | cut -d@ -f2`
6981+               dkkeyfn=`echo $DKSIGN | sed s{%{$domain{g`
6982+       else
6983+               dkkeyfn=$DKSIGN
6984+       fi
6985+       if [ $dksign -eq 2 -a ! -f $dkkeyfn ] ; then
6986+               dkkeyfn=$default_key
6987+       fi
6988+       if [ -f $dkkeyfn ] ; then
6989+               dksign=1
6990+       else
6991+               dksign=0
6992+       fi
6993+       if [ $dksign -eq 0 -a $percent_found -ne 1 ] ; then
6994+               exit 32
6995+       fi
6996+       dkselector=`basename $dkkeyfn`
6997+fi
6998+if [ -z "$NODKIM" -a -n "$DKIMSIGN" ] ; then
6999+       if [ ! -f QMAILHOME/bin/dkim ] ; then
7000+               echo "QMAILHOME/bin/dkim: No such file or directory" 1>&2
7001+               exit 1
7002+       fi
7003+       percent_found=0
7004+       echo $DKIMSIGN|grep "%" >/dev/null 2>&1
7005+       if [ $? -eq 0 ] ; then
7006+               percent_found=1
7007+       fi
7008+       if [ -n "$DKIMDOMAIN" ] && [ -z "$_SENDER" ] ; then
7009+               dkimkeyfn=`echo $DKIMSIGN | sed s{%{$DKIMDOMAIN{g`
7010+       elif [ ! " $_SENDER" = " " ] ; then
7011+               # replace '%' in filename with domain
7012+               domain=`echo $_SENDER | cut -d@ -f2`
7013+               dkimkeyfn=`echo $DKIMSIGN | sed s{%{$domain{g`
7014+       else
7015+               dkimkeyfn=$DKIMSIGN
7016+       fi
7017+       if [ $dkimsign -eq 2 -a ! -f $dkimkeyfn ] ; then
7018+               dkimkeyfn=$default_key
7019+       fi
7020+       if [ -f $dkimkeyfn ] ; then
7021+               dkimsign=1
7022+       else
7023+               dkimsign=0
7024+       fi
7025+       if [ $dkimsign -eq 0 -a $percent_found -ne 1 ] ; then
7026+               exit 32 # private key does not exist
7027+       fi
7028+       dkimselector=`basename $dkimkeyfn`
7029+fi
7030+if [ -z "$NODK" -a -n "$DKVERIFY" ] ; then
7031+       if [ ! -f QMAILHOME/bin/dktest ] ; then
7032+               echo "QMAILHOME/bin/dktest: No such file or directory" 1>&2
7033+               exit 1
7034+       fi
7035+       dkverify=1
7036+fi
7037+if [ -z "$NODKIM" -a -n "$DKIMVERIFY" ] ; then
7038+       if [ ! -f QMAILHOME/bin/dkim ] ; then
7039+               echo "QMAILHOME/bin/dkim: No such file or directory" 1>&2
7040+               exit 1
7041+       fi
7042+       dkimverify=1
7043+fi
7044+cat > /tmp/dk.$$
7045+if [ $dkimsign -eq 1 ] ; then
7046+       # DKIMSIGNOPTIONS="-z 1 -b 2 -x - -y $dkimselector -s $dkimkeyfn"
7047+       set -- `getopt lqthb:c:d:i:x:z:y:s: $DKIMSIGNOPTIONS`
7048+       bopt=0
7049+       xopt=0
7050+       zopt=0
7051+       yopt=0
7052+       sopt=0
7053+       dkimopts="QMAILHOME/bin/dkim"
7054+       while [ $1 != -- ]
7055+       do
7056+               case $1 in
7057+               -l)
7058+               dkimopts="$dkimopts -l"
7059+               ;;
7060+               -q)
7061+               dkimopts="$dkimopts -q"
7062+               ;;
7063+               -t)
7064+               dkimopts="$dkimopts -t"
7065+               ;;
7066+               -h)
7067+               dkimopts="$dkimopts -h"
7068+               ;;
7069+
7070+               -b)
7071+               bopt=1
7072+               dkimopts="$dkimopts -b $2"
7073+               shift
7074+               ;;
7075+
7076+               -c)
7077+               dkimopts="$dkimopts -c $2"
7078+               shift
7079+               ;;
7080+
7081+               -d)
7082+               dkimopts="$dkimopts -d $2"
7083+               shift
7084+               ;;
7085+
7086+               -i)
7087+               dkimopts="$dkimopts -i $2"
7088+               shift
7089+               ;;
7090+
7091+               -x)
7092+               xopt=1
7093+               dkimopts="$dkimopts -x $2"
7094+               shift
7095+               ;;
7096+
7097+               -z)
7098+               zopt=1
7099+               dkimopts="$dkimopts -z $2"
7100+               shift
7101+               ;;
7102+
7103+               -y)
7104+               yopt=1
7105+               dkimopts="$dkimopts -y $2"
7106+               shift
7107+               ;;
7108+
7109+               -s)
7110+               sopt=1
7111+               dkimopts="$dkimopts -s $2"
7112+               shift
7113+               ;;
7114+               esac
7115+               shift   # next flag
7116+       done
7117+       if [ $zopt -eq 0 ] ; then
7118+               dkimopts="$dkimopts -z 1"
7119+       fi
7120+       if [ $bopt -eq 0 ] ; then
7121+               dkimopts="$dkimopts -b 2"
7122+       fi
7123+       if [ $xopt -eq 0 ] ; then
7124+               dkimopts="$dkimopts -x -"
7125+       fi
7126+       if [ $yopt -eq 0 ] ; then
7127+               dkimopts="$dkimopts -y $dkimselector"
7128+       fi
7129+       if [ $sopt -eq 0 ] ; then
7130+               dkimopts="$dkimopts -s $dkimkeyfn"
7131+       fi
7132+       exec 0</tmp/dk.$$
7133+       eval $dkimopts
7134+       if [ $? -ne 0 ] ; then
7135+               /bin/rm -f /tmp/dk.$$
7136+               exit 1
7137+       fi
7138+fi
7139+if [ $dksign -eq 1 ] ; then
7140+       #dktest: [-f] [-b advice_length] [-c nofws|simple] [-v|-s selector] [-h] [-t#] [-r] [-T][-d dnsrecord]
7141+       # DKSIGNOPTIONS="-z 1 -b 2 -x - -y $dkimselector -s $dkimkeyfn"
7142+       set -- `getopt hrb:c:s: $DKSIGNOPTIONS`
7143+       dkopts="QMAILHOME/bin/dktest"
7144+       sopt=0
7145+       while [ $1 != -- ]
7146+       do
7147+               case $1 in
7148+               -h)
7149+               dkopts="$dkopts -h"
7150+               ;;
7151+               -r)
7152+               dkopts="$dkopts -r"
7153+               ;;
7154+
7155+               -b)
7156+               dkopts="$dkopts -b $2"
7157+               shift
7158+               ;;
7159+
7160+               -c)
7161+               dkopts="$dkopts -c $2"
7162+               shift
7163+               ;;
7164+
7165+               -s)
7166+               sopt=1
7167+               dkopts="$dkopts -s $2"
7168+               shift
7169+               ;;
7170+               esac
7171+               shift   # next flag
7172+       done
7173+       if [ $sopt -eq 0 ] ; then
7174+               dkopts="$dkopts -s $dkkeyfn"
7175+       fi
7176+       exec 0</tmp/dk.$$
7177+       eval $dkopts
7178+       exit_val=$?
7179+       # allow error due to duplicate DomainKey-Header
7180+       if [ $exit_val -ne 0 -a $exit_val -ne 12 ] ; then
7181+               /bin/rm -f /tmp/dk.$$
7182+               exit $exit_val
7183+       fi
7184+fi
7185+if [ $dkimverify -eq 1 ] ; then
7186+       practice=$SIGN_PRACTICE
7187+       if [ " $practice" = " " ] ; then
7188+               practice=0
7189+       elif [ " $practice" = " ssp" ] ; then
7190+               practice=1
7191+       elif [ " $practice" = " adsp" ] ; then
7192+               practice=2
7193+       fi
7194+       exec 0</tmp/dk.$$
7195+       dkimvargs="-p $practice"
7196+       if [ -n "$UNSIGNED_SUBJECT" ] ; then
7197+               dkimvargs="$dkimvargs -S"
7198+       fi
7199+       if [ -n "$UNSIGNED_FROM" ] ; then
7200+               dkimvargs="$dkimvargs -f"
7201+       fi
7202+       QMAILHOME/bin/dkim $dkimvargs -v
7203+       ret=$?
7204+       case $ret in
7205+               14)
7206+               /bin/rm -f /tmp/dk.$$
7207+               exit 100
7208+               ;;
7209+               88)
7210+               /bin/rm -f /tmp/dk.$$
7211+               exit 111
7212+               ;;
7213+       esac
7214+       if [ $ret -lt 0 ] ; then
7215+               /bin/rm -f /tmp/dk.$$
7216+               exit 1
7217+       fi
7218+fi
7219+if [ $dkverify -eq 1 ] ; then
7220+       exec 0</tmp/dk.$$
7221+       QMAILHOME/bin/dktest -v
7222+       if [ $? -ne 0 ] ; then
7223+               /bin/rm -f /tmp/dk.$$
7224+               exit 1
7225+       fi
7226+fi
7227+exec 0</tmp/dk.$$
7228+/bin/rm -f /tmp/dk.$$
7229+cat
7230+exit $?
7231diff -ruN ../netqmail-1.06-original/dkim.9 netqmail-1.06/dkim.9
7232--- ../netqmail-1.06-original/dkim.9    1970-01-01 01:00:00.000000000 +0100
7233+++ netqmail-1.06/dkim.9        2020-04-09 19:43:56.494473495 +0200
7234@@ -0,0 +1,108 @@
7235+.TH dkim 8
7236+.SH NAME
7237+dkim \- exercise the domainkeys library
7238+.SH SYNOPSIS
7239+.B dkim
7240+.I opts
7241+
7242+.I opts
7243+is a series of getopt-style options.
7244+
7245+.SH DESCRIPTION
7246+.B dkim
7247+exercises the dkim library. Both signing and verification merely print out the DKIM header.
7248+They do not keep a copy of the input file. You will need to do something like this:
7249+
7250+.EX
7251+ (./dkim -s INDIMAIL/control/domainkeys/dog </tmp/testmsg; cat /tmp/testmsg)\
7252+ | ./dkim -v
7253+.EE
7254+
7255+.SH OPTIONS
7256+.TP
7257+-s \fIkey\fR
7258+.I key
7259+is a path to a file containing a PEM-format private key. The base name of
7260+the file is  used  as  the  selector. Reads the email message on stdin. Prints the
7261+.B DKIM-Signature
7262+header.
7263+
7264+.TP
7265+-v
7266+Verifies the email on stdin. Exits with a non-zero exit code and a message to
7267+stderr if there was a problem with the signature. Always prints a
7268+.B DKIM-Status:
7269+header to stdout. This option requires the \fBs\fR._domainkey.\fBd\fR txt record in
7270+dns (public key). Here \fBs\fR is the selector and \fBd\fR is the domain
7271+
7272+.EX
7273+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
7274+d=gmail.com; s=gamma; h=DomainKey-Status:
7275+To:Subject:Message-Id:Date:From; bh=IarZI4AMTl/vy1jTbPphDcOl4YNS
7276+POk7Vn0tDdgkbV4=; b=VpIS6XNOLU2WWWlsYGeLB3wMbyFQwkg7F0hr7blu1W8f
7277+0RRtuyw9igFwY7q7FNaPVlfZ0cfLPh0mRrlExu4V7uQaTP8nnnHO2cAokYbncGS5
7278+ADU9NbAcpDh+E2YQwzCX
7279+.EE
7280+
7281+.TP
7282+-l
7283+include body length tag when signing. Honor body length tag when verifying
7284+.TP
7285+-q
7286+include query method tag
7287+.TP
7288+-t
7289+include a timestamp tag
7290+.TP
7291+-f
7292+issue error if not all message's From headers are in signature
7293+.TP
7294+-S
7295+Allow unsigned subject in signature
7296+.TP
7297+-h
7298+include Copied Headers
7299+.TP
7300+-p \fIssp\fR | \fIadsp\fR
7301+0 - disable practice (default), 1- SSP, or 2 - ADSP verification
7302+.TP
7303+-b \fIstandard\fR
7304+1 - allman, 2 - ietf or 3 - both
7305+.TP
7306+-c \fIcanonicalization\fR
7307+r for relaxed [DEFAULT], s - simple, t relaxed/simple, u - simple/relaxed
7308+
7309+.TP
7310+-d \fIdomain\fR
7311+the domain tag, if not provided, determined from the return-path/sender/from header
7312+.TP
7313+-i \fIidentity\fR
7314+the identity, if not provided it will not be included
7315+.TP
7316+-x \fIexpire_time\fR
7317+the expire time in seconds since epoch ( DEFAULT = current time + 604800). If set to - then it will not be included
7318+.TP
7319+-z \fIhash\fR
7320+1 for sha1, 2 for sha256, 3 for both
7321+.TP
7322+-y \fIselector\fR
7323+the selector tag DEFAULT=private
7324+.TP
7325+-s \fIprivkeyfile\fR
7326+sign the message using the private key in privkeyfile
7327+.TP
7328+-H
7329+this help
7330+
7331+.SH Return Value
7332+When signing, \fBdkim\fR returns 0 on success and non-zero on any failure. For verification, you can set the
7333+environment varable \fBDKIMVERIFY\fR. Refer to qmail-dkim(8) for a full description of  the \fBDKIMVERIFY\fR
7334+environment variable
7335+
7336+.SH "SEE ALSO"
7337+dktest(8),
7338+qmail-dk(8),
7339+qmail-dkim(8),
7340+dknewkey(8),
7341+rfc-4870(5),
7342+rfc-4871(5)
7343diff -ruN ../netqmail-1.06-original/dkim.c netqmail-1.06/dkim.c
7344--- ../netqmail-1.06-original/dkim.c    1970-01-01 01:00:00.000000000 +0100
7345+++ netqmail-1.06/dkim.c        2019-06-19 09:46:59.689809564 +0200
7346@@ -0,0 +1,871 @@
7347+/*
7348+ * $Log: dkim.c,v $
7349+ * Revision 1.23  2019-06-14 21:24:59+05:30  Cprogrammer
7350+ * BUG - honor body length tag in verification
7351+ *
7352+ * Revision 1.22  2019-01-13 10:10:27+05:30  Cprogrammer
7353+ * added missing usage string for allowing unsigned subject.
7354+ *
7355+ * Revision 1.21  2018-08-08 23:57:02+05:30  Cprogrammer
7356+ * issue success if at lease one one good signature is found
7357+ *
7358+ * Revision 1.20  2018-05-22 10:03:26+05:30  Cprogrammer
7359+ * changed return type of writeHeader() to void
7360+ *
7361+ * Revision 1.19  2016-03-01 16:23:38+05:30  Cprogrammer
7362+ * added -S option to allow email with unsigned subject
7363+ *
7364+ * Revision 1.18  2016-02-01 10:53:32+05:30  Cprogrammer
7365+ * use basename of private key as the selector in absense of -y option
7366+ *
7367+ * Revision 1.17  2015-12-15 15:36:01+05:30  Cprogrammer
7368+ * added case 3 for 3rd party signature without SSP and ADSP
7369+ * increased buffer size for Apple mail with X-BrightMail-Tracker header issue
7370+ *
7371+ * Revision 1.16  2012-08-16 08:01:19+05:30  Cprogrammer
7372+ * do not skip X-Mailer headers
7373+ *
7374+ * Revision 1.15  2011-06-04 13:55:50+05:30  Cprogrammer
7375+ * set AllowUnsignedFromHeaders
7376+ *
7377+ * Revision 1.14  2011-06-04 09:36:36+05:30  Cprogrammer
7378+ * added AllowUnsignedFromHeaders option
7379+ *
7380+ * Revision 1.13  2011-02-07 22:05:23+05:30  Cprogrammer
7381+ * added case DKIM_3PS_SIGNATURE
7382+ *
7383+ * Revision 1.12  2010-05-04 14:00:13+05:30  Cprogrammer
7384+ * make option '-z' work on systems without SHA_256
7385+ *
7386+ * Revision 1.11  2009-04-20 08:35:45+05:30  Cprogrammer
7387+ * corrected usage()
7388+ *
7389+ * Revision 1.10  2009-04-15 21:30:32+05:30  Cprogrammer
7390+ * added DKIM-Signature to list of excluded headers
7391+ *
7392+ * Revision 1.9  2009-04-15 20:45:04+05:30  Cprogrammer
7393+ * corrected usage
7394+ *
7395+ * Revision 1.8  2009-04-05 19:04:44+05:30  Cprogrammer
7396+ * improved formating of usage
7397+ *
7398+ * Revision 1.7  2009-04-03 12:05:25+05:30  Cprogrammer
7399+ * minor changes on usage display
7400+ *
7401+ * Revision 1.6  2009-03-28 20:15:23+05:30  Cprogrammer
7402+ * invoke DKIMVerifyGetDetails()
7403+ *
7404+ * Revision 1.5  2009-03-27 20:43:48+05:30  Cprogrammer
7405+ * added HAVE_OPENSSL_EVP_H conditional
7406+ *
7407+ * Revision 1.4  2009-03-27 20:19:28+05:30  Cprogrammer
7408+ * added ADSP
7409+ *
7410+ * Revision 1.3  2009-03-26 15:10:53+05:30  Cprogrammer
7411+ * added ADSP
7412+ *
7413+ * Revision 1.2  2009-03-25 08:37:45+05:30  Cprogrammer
7414+ * added dkim_error
7415+ *
7416+ * Revision 1.1  2009-03-21 08:24:47+05:30  Cprogrammer
7417+ * Initial revision
7418+ *
7419+ *
7420+ * This code incorporates intellectual property owned by Yahoo! and licensed
7421+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
7422+ *
7423+ * Unless required by applicable law or agreed to in writing, software
7424+ * distributed under the License is distributed on an "AS IS" BASIS,
7425+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7426+ * See the License for the specific language governing permissions and
7427+ * limitations under the License.
7428+ */
7429+/*
7430+ * (cat /tmp/test.msg|./dkimtest -z 2 -b 1 -y private \
7431+ * -s /var/indimail/control/domainkeys/private ;cat /tmp/test.msg )|./dkimtest -v
7432+ */
7433+#ifndef __cplusplus
7434+#error A C++ compiler is required!
7435+#endif
7436+#ifdef HAVE_CONFIG_H
7437+#include "config.h"
7438+#endif
7439+
7440+#include <stdio.h>
7441+#include <fcntl.h>
7442+#include <errno.h>
7443+#include <unistd.h>
7444+#include <string.h>
7445+#include <time.h>
7446+#include <stdlib.h>
7447+#include <sys/types.h>
7448+#include <sys/stat.h>
7449+#include "dkim.h"
7450+#include "dkimdns.h"
7451+
7452+#ifdef HAVE_OPENSSL_EVP_H
7453+#include <openssl/evp.h>
7454+#define DKIM_MALLOC(s)     OPENSSL_malloc(s)
7455+#define DKIM_MFREE(s)      OPENSSL_free(s); s = NULL;
7456+#else
7457+#define DKIM_MALLOC(s)     malloc(s)
7458+#define DKIM_MFREE(s)      free(s); s = NULL;
7459+#endif
7460+
7461+int DKIM_CALL
7462+SignThisHeader(const char *szHeader)
7463+{
7464+       if ((!strncasecmp(szHeader, "X-", 2) && strncasecmp(szHeader, "X-Mailer", 8))
7465+               || !strncasecmp(szHeader, "Received:", 9)
7466+               || !strncasecmp(szHeader, "DKIM-Signature:", 15)
7467+               || !strncasecmp(szHeader, "Authentication-Results:", 23)
7468+               || !strncasecmp(szHeader, "DomainKey-Signature", 19)
7469+               || !strncasecmp(szHeader, "Return-Path:", 12))
7470+       {
7471+               return 0;
7472+       }
7473+       return 1;
7474+}
7475+
7476+char           *program;
7477+
7478+void
7479+usage()
7480+{
7481+#ifdef HAVE_EVP_SHA256
7482+       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);
7483+#else
7484+       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);
7485+#endif
7486+       fprintf(stderr, "l                    include body length tag\n");
7487+       fprintf(stderr, "q                    include query method tag\n");
7488+       fprintf(stderr, "t                    include a timestamp tag\n");
7489+       fprintf(stderr, "h                    include Copied Headers\n");
7490+       fprintf(stderr, "f                    allow Unsigned From (default is to reject if From field is not signed)\n");
7491+       fprintf(stderr, "S                    allow Unsigned Subject (default is to reject if Subject field is not signed)\n");
7492+       fprintf(stderr, "v                    verify the message\n");
7493+       fprintf(stderr, "p <ssp|adsp>         0 - disable practice (default), 1- SSP, or 2 - ADSP verification\n");
7494+       fprintf(stderr, "b <standard>         1 - allman, 2 - ietf or 3 - both\n");
7495+       fprintf(stderr, "c <canonicalization> r for relaxed [DEFAULT], s - simple, t relaxed/simple, u - simple/relaxed\n");
7496+       fprintf(stderr, "d <domain>           the domain tag, if not provided, determined from the sender/from header\n");
7497+       fprintf(stderr, "i <identity>         the identity, if not provided it will not be included\n");
7498+       fprintf(stderr, "x <expire_time>      the expire time in seconds since epoch ( DEFAULT = current time + 604800)\n");
7499+       fprintf(stderr, "                     if set to - then it will not be included\n");
7500+#ifdef HAVE_EVP_SHA256
7501+       fprintf(stderr, "z <hash>             1 for sha1, 2 for sha256, 3 for both\n");
7502+#endif
7503+       fprintf(stderr, "y <selector>         the selector tag DEFAULT=private\n");
7504+       fprintf(stderr, "s <privkeyfile>      sign the message using the private key in privkeyfile\n");
7505+       fprintf(stderr, "V                    set verbose mode\n");
7506+       fprintf(stderr, "H                    this help\n");
7507+       exit(1);
7508+}
7509+
7510+unsigned int str_chr(char *s, int c)
7511+{
7512+       register char   ch;
7513+       register char  *t;
7514+
7515+       ch = c;
7516+       t = s;
7517+       for (;;) {
7518+               if (!*t)
7519+                       break;
7520+               if (*t == ch)
7521+                       break;
7522+               ++t;
7523+               if (!*t)
7524+                       break;
7525+               if (*t == ch)
7526+                       break;
7527+               ++t;
7528+               if (!*t)
7529+                       break;
7530+               if (*t == ch)
7531+                       break;
7532+               ++t;
7533+               if (!*t)
7534+                       break;
7535+               if (*t == ch)
7536+                       break;
7537+               ++t;
7538+       }
7539+       return t - s;
7540+}
7541+
7542+void
7543+dkim_error(int e)
7544+{
7545+       switch (e)
7546+       {
7547+       case DKIM_OUT_OF_MEMORY:
7548+               fprintf(stderr, "memory allocation failed\n");
7549+               break;
7550+       case DKIM_INVALID_CONTEXT:
7551+               fprintf(stderr, "DKIMContext structure invalid for this operation\n");
7552+               break;
7553+       case DKIM_NO_SENDER:
7554+               fprintf(stderr, "Could not find From: or Sender: header in message\n");
7555+               break;
7556+       case DKIM_BAD_PRIVATE_KEY:
7557+               fprintf(stderr, "Could not parse private key\n");
7558+               break;
7559+       case DKIM_BUFFER_TOO_SMALL:
7560+               fprintf(stderr, "Buffer passed in is not large enough");
7561+               break;
7562+       }
7563+}
7564+
7565+/*
7566+ * Allows you to add the headers contain the results and DKIM ADSP
7567+ */
7568+void writeHeader(int ret, int resDKIMSSP, int resDKIMADSP, int useSSP, int useADSP )
7569+{
7570+       char           *dkimStatus, *sspStatus, *adspStatus;
7571+
7572+       dkimStatus = sspStatus = adspStatus = (char *) "";
7573+       switch (ret)
7574+       {
7575+       case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */
7576+               dkimStatus = (char *) "signature result: signature verified but it did not include all of the body";
7577+               break;
7578+       case DKIM_NEUTRAL:                      /*- 3 verify result: no signatures verified but message is not suspicious */
7579+               dkimStatus = (char *) "verify result: no signatures verified but message is not suspicious";
7580+               break;
7581+       case DKIM_PARTIAL_SUCCESS:      /*- 2 verify result: at least one but not all signatures verified */
7582+               dkimStatus = (char *) "verify result: at least none but not all signatures verified";
7583+               break;
7584+       case DKIM_FINISHED_BODY:        /*- 1 process result: no more message body is needed */
7585+               dkimStatus = (char *) "process result: no more message body is needed";
7586+               break;
7587+       case DKIM_SUCCESS:
7588+               dkimStatus = (char *) "good        ";
7589+               break;
7590+       case DKIM_FAIL:
7591+               dkimStatus = (char *) "failed      ";
7592+               break;
7593+       case DKIM_BAD_SYNTAX:
7594+               dkimStatus = (char *) "signature error: DKIM-Signature could not parse or has bad tags/values";
7595+               break;
7596+       case DKIM_SIGNATURE_BAD:
7597+               dkimStatus = (char *) "signature error: RSA verify failed";
7598+               break;
7599+       case DKIM_SIGNATURE_BAD_BUT_TESTING:
7600+               dkimStatus = (char *) "signature error: RSA verify failed but testing";
7601+               break;
7602+       case DKIM_SIGNATURE_EXPIRED:
7603+               dkimStatus = (char *) "signature error: x= is old";
7604+               break;
7605+       case DKIM_SELECTOR_INVALID:
7606+               dkimStatus = (char *) "signature error: selector doesn't parse or contains invalid values";
7607+               break;
7608+       case DKIM_SELECTOR_GRANULARITY_MISMATCH:
7609+               dkimStatus = (char *) "signature error: selector g= doesn't match i=";
7610+               break;
7611+       case DKIM_SELECTOR_KEY_REVOKED:
7612+               dkimStatus = (char *) "signature error: selector p= empty";
7613+               break;
7614+       case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG:
7615+               dkimStatus = (char *) "signature error: selector domain name too long to request";
7616+               break;
7617+       case DKIM_SELECTOR_DNS_TEMP_FAILURE:
7618+               dkimStatus = (char *) "signature error: temporary dns failure requesting selector";
7619+               break;
7620+       case DKIM_SELECTOR_DNS_PERM_FAILURE:
7621+               dkimStatus = (char *) "signature error: permanent dns failure requesting selector";
7622+               break;
7623+       case DKIM_SELECTOR_PUBLIC_KEY_INVALID:
7624+               dkimStatus = (char *) "signature error: selector p= value invalid or wrong format";
7625+               break;
7626+       case DKIM_NO_SIGNATURES:
7627+               dkimStatus = (char *) "process error, no sigs";
7628+               break;
7629+       case DKIM_NO_VALID_SIGNATURES:
7630+               dkimStatus = (char *) "process error, no valid sigs";
7631+               break;
7632+       case DKIM_BODY_HASH_MISMATCH:
7633+               dkimStatus = (char *) "sigature verify error: message body does not hash to bh value";
7634+               break;
7635+       case DKIM_SELECTOR_ALGORITHM_MISMATCH:
7636+               dkimStatus = (char *) "signature error: selector h= doesn't match signature a=";
7637+               break;
7638+       case DKIM_STAT_INCOMPAT:
7639+               dkimStatus = (char *) "signature error: incompatible v=";
7640+               break;
7641+       default:
7642+               dkimStatus = (char *) "error";
7643+               break;
7644+       }
7645+       if (useSSP && resDKIMSSP != -1)
7646+       {
7647+               switch(resDKIMSSP)
7648+               {
7649+                       case DKIM_SSP_ALL:
7650+                               sspStatus = (char *) "all;";
7651+                               break;
7652+                       case DKIM_SSP_STRICT:
7653+                               sspStatus = (char *) "strict;";
7654+                               break;
7655+                       case DKIM_SSP_SCOPE:
7656+                               sspStatus = (char *) "out of scope;";
7657+                               break;
7658+                       case DKIM_SSP_TEMPFAIL:
7659+                               sspStatus = (char *) "temporary failure;";
7660+                               break;
7661+                       case DKIM_SSP_UNKNOWN:
7662+                       default:
7663+                               sspStatus = (char *) "unknown;";
7664+                               break;
7665+               }
7666+       }
7667+       if (useADSP && resDKIMADSP != -1) {
7668+               switch(resDKIMADSP)
7669+               {
7670+                       case DKIM_ADSP_ALL:
7671+                               adspStatus = (char *) "all;";
7672+                               break;
7673+                       case DKIM_ADSP_DISCARDABLE:
7674+                               adspStatus = (char *) "discardable;";
7675+                               break;
7676+                       case DKIM_ADSP_SCOPE:
7677+                               adspStatus = (char *) "out of scope;";
7678+                               break;
7679+                       case DKIM_ADSP_TEMPFAIL:
7680+                               adspStatus = (char *) "temporary failure;";
7681+                               break;
7682+                       case DKIM_ADSP_UNKNOWN:
7683+                       default:
7684+                               adspStatus = (char *) "unknown ;";
7685+                               break;
7686+               }
7687+       }
7688+       printf("DKIM-Status: %s\n", dkimStatus);
7689+       if (useSSP && *sspStatus)
7690+               printf("X-DKIM-SSP: %s\n", sspStatus);
7691+       if (useADSP && *adspStatus)
7692+               printf("X-DKIM-ADSP: %s\n", adspStatus);
7693+}
7694+
7695+int
7696+ParseTagValues(char *list, char *letters[], char *values[])
7697+{
7698+       char           *tmp, *ptr, *key;
7699+       int             i;
7700+
7701+       /*- start with all args unset */
7702+       for (i = 0; letters[i]; i++)
7703+               values[i] = 0;
7704+       key = 0;
7705+       for(ptr = list;*ptr;) {
7706+               if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) /*- FWS */
7707+                       *ptr++ = 0;
7708+               if (!key)
7709+                       key = ptr;
7710+               if (*ptr == '=') {
7711+                       *ptr = 0;
7712+                       for (i = 0;letters[i];i++) {
7713+                               if (!strcmp(letters[i], key)) {
7714+                                       ptr++;
7715+                                       for (;*ptr;) {
7716+                                               if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) {
7717+                                                       ptr++;
7718+                                                       continue;
7719+                                               }
7720+                                               break;
7721+                                       }
7722+                                       values[i] = ptr;
7723+                                       for(;*ptr && *ptr != ';';ptr++);
7724+                                       tmp = ptr;
7725+                                       if (*ptr)
7726+                                               *ptr++ = 0;
7727+                                       for(;tmp != values[i];tmp--) /*- RFC 4871 3.2 */ {
7728+                                               if ((*tmp == ' ') || (*tmp == '\t') || (*tmp == '\r') || (*tmp == '\n')) {
7729+                                                       *tmp = 0;
7730+                                                       continue;
7731+                                               }
7732+                                               break;
7733+                                       }
7734+                                       key = 0;
7735+                                       break;
7736+                               }
7737+                       }
7738+               } else
7739+                       ptr++;
7740+       }
7741+       return (0);
7742+}
7743+
7744+int
7745+GetSSP(char *domain, int *bTesting)
7746+{
7747+       char           *query, *results;
7748+       char           *tags[] = { (char *) "dkim", (char *) "t", (char *) 0};
7749+       char           *values[2];
7750+       int             bIsParentSSP = 0, iSSP = DKIM_SSP_UNKNOWN;
7751+
7752+       *bTesting = 0;
7753+       if (!(query = (char *) DKIM_MALLOC(strlen("_ssp._domainkey.") + strlen(domain) + 1))) {
7754+               fprintf(stderr, "malloc: %d: %s\n", strlen("_ssp._domainkey.") + strlen(domain) + 1,
7755+                       strerror(errno));
7756+               exit(1);
7757+       }
7758+       sprintf(query, "_ssp._domainkey.%s", domain);
7759+       results = dns_text(query);
7760+       DKIM_MFREE(query);
7761+       if (!strcmp(results, "e=temp;")) {
7762+               DKIM_MFREE(results);
7763+               return DKIM_SSP_TEMPFAIL;
7764+       } else
7765+       if (!strcmp(results, "e=perm;")) {
7766+               DKIM_MFREE(results);
7767+               results = dns_text(domain);
7768+               if (!strcmp(results, "e=temp;")) {
7769+                       DKIM_MFREE(results);
7770+                       return DKIM_SSP_TEMPFAIL;
7771+               } else
7772+               if (!strcmp(results, "e=perm;")) {
7773+                       DKIM_MFREE(results);
7774+                       return DKIM_SSP_SCOPE;
7775+               }
7776+               bIsParentSSP = 1;
7777+       }
7778+       if (!ParseTagValues(results, tags, values)) {
7779+               DKIM_MFREE(results);
7780+               return DKIM_SSP_UNKNOWN;
7781+       }
7782+       DKIM_MFREE(results);
7783+       if (values[0] != NULL) {
7784+               if (strcasecmp(values[0], "all") == 0)
7785+                       iSSP = DKIM_SSP_ALL;
7786+               else
7787+               if (strcasecmp(values[0], "strict") == 0)
7788+                       iSSP = DKIM_SSP_STRICT;
7789+       }
7790+       // flags
7791+       if (values[1] != NULL) {
7792+               char           *s, *p;
7793+               for (p = values[1], s = values[1]; *p; p++) {
7794+                       if (*p == '|')
7795+                               *p = 0;
7796+                       else
7797+                               continue;
7798+                       if (!strcmp(s, "y"))
7799+                               *bTesting = 1;
7800+                       else
7801+                       if (!strcmp(s, "s")) {
7802+                               if (bIsParentSSP) {
7803+                                       /*
7804+                                        * this is a parent's SSP record that should not apply to subdomains
7805+                                        * the message is non-suspicious
7806+                                        */
7807+                                       *bTesting = 0;
7808+                                       return (DKIM_SSP_UNKNOWN);
7809+                               }
7810+                       }
7811+                       s = p + 1;
7812+               }
7813+       }
7814+       return iSSP; /*- No ADSP Record */
7815+}
7816+
7817+int
7818+GetADSP(char *domain)
7819+{
7820+       char           *query, *results;
7821+       char           *tags[] = {(char *) "dkim", (char *) 0};
7822+       char           *values[1];
7823+
7824+       results = dns_text(domain);
7825+       if (!strcmp(results, "e=perm;")) {
7826+               DKIM_MFREE(results);
7827+               return DKIM_ADSP_SCOPE;
7828+       } else
7829+       if (!strcmp(results, "e=temp;")) {
7830+               DKIM_MFREE(results);
7831+               return DKIM_ADSP_TEMPFAIL;
7832+       }
7833+       if (!(query = (char *) DKIM_MALLOC(strlen((char *) "_adsp._domainkey.") + strlen(domain) + 1))) {
7834+               fprintf(stderr, "malloc: %d: %s\n", strlen("_adsp._domainkey.") + strlen(domain) + 1,
7835+                       strerror(errno));
7836+               exit(1);
7837+       }
7838+       sprintf(query, "_adsp._domainkey.%s", domain);
7839+       results = dns_text(query);
7840+       DKIM_MFREE(query);
7841+       if (!strcmp(results, "e=perm;")) {
7842+               DKIM_MFREE(results);
7843+               return DKIM_ADSP_SCOPE;
7844+       } else
7845+       if (!strcmp(results, "e=temp;")) {
7846+               DKIM_MFREE(results);
7847+               return DKIM_ADSP_TEMPFAIL;
7848+       }
7849+       if (!ParseTagValues(results, tags, values)) {
7850+               DKIM_MFREE(results);
7851+               return DKIM_ADSP_UNKNOWN;
7852+       }
7853+       DKIM_MFREE(results);
7854+       if (values[0] != NULL) {
7855+               if (strcasecmp(values[0], "all") == 0)
7856+                       return (DKIM_ADSP_ALL);
7857+               else
7858+               if (strcasecmp(values[0], "discardable") == 0)
7859+                       return (DKIM_ADSP_DISCARDABLE);
7860+       }
7861+       return DKIM_ADSP_UNKNOWN; /*- No ADSP Record */
7862+}
7863+
7864+int
7865+main(int argc, char **argv)
7866+{
7867+       char           *PrivKey, *PrivKeyFile = NULL, *pSig = NULL, *dkimverify;
7868+       int             i, ret, ch, nPrivKeyLen, PrivKeyFD, verbose = 0;
7869+       int             bSign = 1, nSigCount = 0, useSSP = 0, useADSP = 0, accept3ps = 0;
7870+       int             sCount = 0, sSize = 0, resDKIMSSP = -1, resDKIMADSP = -1;
7871+       int             nAllowUnsignedFromHeaders = 0;
7872+       int             nAllowUnsignedSubject = 1;
7873+       char            Buffer[4096], szPolicy[512];
7874+       time_t          t;
7875+       struct stat     statbuf;
7876+       DKIMContext     ctxt;
7877+       DKIMSignOptions opts = { 0 };
7878+       DKIMVerifyDetails *pDetails;
7879+       DKIMVerifyOptions vopts = { 0 };
7880+
7881+       if (!(program = strrchr(argv[0], '/')))
7882+               program = argv[0];
7883+       else
7884+               program++;
7885+       t = time(0);
7886+#ifdef HAVE_EVP_SHA256
7887+       opts.nHash = DKIM_HASH_SHA1_AND_256;
7888+#else
7889+       opts.nHash = DKIM_HASH_SHA1;
7890+#endif
7891+       opts.nCanon = DKIM_SIGN_RELAXED;
7892+       opts.nIncludeBodyLengthTag = 0;
7893+       opts.nIncludeQueryMethod = 0;
7894+       opts.nIncludeTimeStamp = 0;
7895+       opts.expireTime = t + 604800;   // expires in 1 week
7896+       opts.nIncludeCopiedHeaders = 0;
7897+       opts.nIncludeBodyHash = DKIM_BODYHASH_BOTH;
7898+       strcpy(opts.szRequiredHeaders, "NonExistent");
7899+       opts.pfnHeaderCallback = SignThisHeader;
7900+       while (1) {
7901+               if ((ch = getopt(argc, argv, "lqtfhHSvVp:b:c:d:i:s:x:y:z:")) == -1)
7902+                       break;
7903+               switch (ch)
7904+               {
7905+               case 'l': /*- body length tag */
7906+                       vopts.nHonorBodyLengthTag = 1;
7907+                       opts.nIncludeBodyLengthTag = 1;
7908+                       break;
7909+               case 'q': /*- query method tag */
7910+                       opts.nIncludeQueryMethod = 1;
7911+                       break;
7912+               case 'S':
7913+                       nAllowUnsignedSubject = 0;
7914+                       break;
7915+               case 'f':
7916+                       nAllowUnsignedFromHeaders = 1;
7917+               case 't': /*- timestamp tag */
7918+                       opts.nIncludeTimeStamp = 1;
7919+                       break;
7920+               case 'h':
7921+                       opts.nIncludeCopiedHeaders = 1;
7922+                       break;
7923+               case 'H':
7924+                       usage();
7925+                       break;
7926+               case 'v': /*- verify */
7927+                       bSign = 0;
7928+                       break;
7929+               case 'V': /*- verbose */
7930+                       verbose = 1;
7931+                       break;
7932+               case 'p':
7933+                       switch(*optarg)
7934+                       {
7935+                       case '1':
7936+                               accept3ps = 1;
7937+                               useSSP = 1;
7938+                               useADSP = 0;
7939+                               break;
7940+                       case '2':
7941+                               accept3ps = 1;
7942+                               useSSP = 0;
7943+                               useADSP = 1;
7944+                               break;
7945+                       case '3':
7946+                               accept3ps = 1;
7947+                               useSSP = 0;
7948+                               useADSP = 0;
7949+                               break;
7950+                       case '0':
7951+                               accept3ps = 0;
7952+                               useSSP = 0;
7953+                               useADSP = 0;
7954+                               break;
7955+                       default:
7956+                               fprintf(stderr, "%s: unrecognized practice %c.\n", program, *optarg);
7957+                               return (1);
7958+                       }
7959+                       break;
7960+               case 'b': /*- allman or ietf draft 1 or both */
7961+                       switch (*optarg)
7962+                       {
7963+                       case '1':
7964+                               opts.nIncludeBodyHash = DKIM_BODYHASH_ALLMAN_1;
7965+                               break;
7966+                       case '2':
7967+                               opts.nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
7968+                               break;
7969+                       case '3':
7970+                               opts.nIncludeBodyHash = DKIM_BODYHASH_BOTH;
7971+                               break;
7972+                       default:
7973+                               fprintf(stderr, "%s: unrecognized standard %c.\n", program, *optarg);
7974+                               return (1);
7975+                       }
7976+                       break;
7977+               case 'c':
7978+                       switch (*optarg)
7979+                       {
7980+                       case 'r':
7981+                               opts.nCanon = DKIM_SIGN_RELAXED;
7982+                               break;
7983+                       case 's':
7984+                               opts.nCanon = DKIM_SIGN_SIMPLE;
7985+                               break;
7986+                       case 't':
7987+                               opts.nCanon = DKIM_SIGN_RELAXED_SIMPLE;
7988+                               break;
7989+                       case 'u':
7990+                               opts.nCanon = DKIM_SIGN_SIMPLE_RELAXED;
7991+                               break;
7992+                       default:
7993+                               fprintf(stderr, "%s: unrecognized canonicalization.\n", program);
7994+                               return (1);
7995+                       }
7996+                       break;
7997+               case 'd':
7998+                       strncpy(opts.szDomain, optarg, sizeof (opts.szDomain) - 1);
7999+                       break;
8000+               case 'i':       /*- identity */
8001+                       if (*optarg == '-')
8002+                               opts.szIdentity[0] = '\0';
8003+                       else
8004+                               strncpy(opts.szIdentity, optarg, sizeof (opts.szIdentity) - 1);
8005+                       break;
8006+               case 's': /*- sign */
8007+                       bSign = 1;
8008+                       PrivKeyFile = optarg;
8009+                       break;
8010+               case 'x': /*- expire time */
8011+                       if (*optarg == '-')
8012+                               opts.expireTime = 0;
8013+                       else
8014+                               opts.expireTime = t + atoi(optarg);
8015+                       break;
8016+               case 'y':
8017+                       strncpy(opts.szSelector, optarg, sizeof (opts.szSelector) - 1);
8018+                       break;
8019+               case 'z': /*- sign w/ sha1, sha256 or both */
8020+#ifdef HAVE_EVP_SHA256
8021+                       switch (*optarg)
8022+                       {
8023+                       case '1':
8024+                               opts.nHash = DKIM_HASH_SHA1;
8025+                               break;
8026+                       case '2':
8027+                               opts.nHash = DKIM_HASH_SHA256;
8028+                               break;
8029+                       case '3':
8030+                               opts.nHash = DKIM_HASH_SHA1_AND_256;
8031+                               break;
8032+                       default:
8033+                               fprintf(stderr, "%s: unrecognized hash.\n", program);
8034+                               return (1);
8035+                       }
8036+#else
8037+                       opts.nHash = DKIM_HASH_SHA1;
8038+#endif
8039+                       break;
8040+               } /*- switch (ch) */
8041+       }
8042+       if (bSign) { /*- sign */
8043+               if (!PrivKeyFile) {
8044+                       fprintf(stderr, "Private Key not provided\n");
8045+                       usage();
8046+                       return (1);
8047+               }
8048+               if (!opts.szSelector[0]) {
8049+                       if ((pSig = strrchr(PrivKeyFile, '/'))) {
8050+                               pSig++;
8051+                               strcpy(opts.szSelector, pSig);
8052+                       } else
8053+                               strcpy(opts.szSelector, "private");
8054+               }
8055+               if ((PrivKeyFD = open(PrivKeyFile, O_RDONLY)) == -1) {
8056+                       fprintf(stderr, "%s: %s\n", PrivKeyFile, strerror(errno));
8057+                       return (1);
8058+               }
8059+               if (fstat(PrivKeyFD, &statbuf) == -1) {
8060+                       fprintf(stderr, "fstat: %s: %s\n", PrivKeyFile, strerror(errno));
8061+                       return (1);
8062+               }
8063+               if (!(PrivKey = (char *) DKIM_MALLOC(sizeof(char) * ((nPrivKeyLen = statbuf.st_size) + 1)))) {
8064+                       fprintf(stderr, "malloc: %ld bytes: %s\n", statbuf.st_size + 1, strerror(errno));
8065+                       return (1);
8066+               }
8067+               if (read(PrivKeyFD, PrivKey, nPrivKeyLen) != nPrivKeyLen) {
8068+                       fprintf(stderr, "%s: read: %s\n", strerror(errno), program);
8069+                       return (1);
8070+               }
8071+               close(PrivKeyFD);
8072+               PrivKey[nPrivKeyLen] = '\0';
8073+               if (DKIMSignInit(&ctxt, &opts) != DKIM_SUCCESS) {
8074+                       fprintf(stderr, "DKIMSignInit: failed to initialize signature %s\n", PrivKeyFile);
8075+                       return (1);
8076+               }
8077+               for (;;) {
8078+                       if ((ret = read(0, Buffer, sizeof(Buffer) - 1)) == -1) {
8079+                               fprintf(stderr, "read: %s\n", strerror(errno));
8080+                               DKIMSignFree(&ctxt);
8081+                               return (1);
8082+                       } else
8083+                       if (!ret)
8084+                               break;
8085+                       if (DKIMSignProcess(&ctxt, Buffer, ret) == DKIM_INVALID_CONTEXT) {
8086+                               fprintf(stderr, "DKIMSignProcess: DKIMContext structure invalid for this operation\n");
8087+                               DKIMSignFree(&ctxt);
8088+                               return (1);
8089+                       }
8090+               }
8091+               if (DKIMSignGetSig2(&ctxt, PrivKey, &pSig) == DKIM_INVALID_CONTEXT) {
8092+                       fprintf(stderr, "DKIMSignProcess: DKIMContext structure invalid for this operation\n");
8093+                       DKIMSignFree(&ctxt);
8094+                       return (1);
8095+               }
8096+               if (pSig) {
8097+                       fwrite(pSig, 1, strlen(pSig), stdout);
8098+                       fwrite("\n", 1, 1, stdout);
8099+               }
8100+               DKIMSignFree(&ctxt);
8101+               return 0;
8102+       } else { /*- verify */
8103+               if (useADSP)
8104+                       vopts.nCheckPractices = useADSP;
8105+               else
8106+               if (useSSP)
8107+                       vopts.nCheckPractices = useSSP;
8108+               else
8109+                       vopts.nCheckPractices = 0;
8110+               vopts.nAccept3ps = accept3ps;
8111+               vopts.pfnSelectorCallback = NULL;       /*- SelectorCallback; */
8112+               vopts.nAllowUnsignedFromHeaders = nAllowUnsignedFromHeaders;
8113+               vopts.nSubjectRequired = nAllowUnsignedSubject;
8114+               DKIMVerifyInit(&ctxt, &vopts);          /*- this is always successful */
8115+               for (;;) {
8116+                       if ((i = read(0, Buffer, sizeof(Buffer) - 1)) == -1) {
8117+                               fprintf(stderr, "read: %s\n", strerror(errno));
8118+                               DKIMVerifyFree(&ctxt);
8119+                               return (1);
8120+                       } else
8121+                       if (!i)
8122+                               break;
8123+                       ret = DKIMVerifyProcess(&ctxt, Buffer, i);
8124+                       dkim_error(ret);
8125+                       if (ret > 0 && ret < DKIM_FINISHED_BODY)
8126+                               ret = DKIM_FAIL;
8127+                       if (ret)
8128+                               break;
8129+               }
8130+               if (!ret) {
8131+                       ret = DKIMVerifyResults(&ctxt, &sCount, &sSize);
8132+                       if (ret != DKIM_SUCCESS && ret != DKIM_3PS_SIGNATURE && ret != DKIM_NEUTRAL)
8133+                               dkim_error(ret);
8134+                       if ((ret = DKIMVerifyGetDetails(&ctxt, &nSigCount, &pDetails, szPolicy)) != DKIM_SUCCESS)
8135+                               dkim_error(ret);
8136+                       else {
8137+                               for (ret = DKIM_FAIL, i = 0; i < nSigCount; i++) {
8138+                                       if (verbose)
8139+                                               printf("Signature # %02d: ", i + 1);
8140+                                       if (pDetails[i].nResult >= 0) {
8141+                                               ret = 0;
8142+                                               if (verbose)
8143+                                                       printf("Success\n");
8144+                                               continue;
8145+                                       } else {
8146+                                               if (ret == DKIM_FAIL)
8147+                                                       ret = pDetails[i].nResult;
8148+                                               if (verbose)
8149+                                                       printf("Failure %d\n", pDetails[i].nResult);
8150+                                       }
8151+                               }
8152+                               if (!nSigCount)
8153+                                       ret = DKIM_NO_SIGNATURES;
8154+                       }
8155+               }
8156+               if (ret < 0 || ret == DKIM_3PS_SIGNATURE) {
8157+                       if (useADSP) {
8158+                               char           *domain;
8159+       
8160+                               if ((domain = DKIMVerifyGetDomain(&ctxt)))
8161+                                       resDKIMADSP = GetADSP(domain);
8162+                               if (sCount > 0) {
8163+                                       if (resDKIMADSP == DKIM_ADSP_UNKNOWN || resDKIMADSP == DKIM_ADSP_ALL)
8164+                                               ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
8165+                               }
8166+                               /* if the message should be signed, return fail */
8167+                               if (resDKIMADSP == DKIM_ADSP_DISCARDABLE)
8168+                                       ret = DKIM_FAIL;
8169+                               ret = DKIM_NEUTRAL;
8170+                       } else
8171+                       if (useSSP) {
8172+                               int             bTestingPractices = 0;
8173+                               char           *domain;
8174+
8175+                               if ((domain = DKIMVerifyGetDomain(&ctxt)))
8176+                                       resDKIMSSP = GetSSP(domain, &bTestingPractices);
8177+                               if (sCount > 0) {
8178+                                       if ((resDKIMSSP == DKIM_SSP_UNKNOWN || resDKIMSSP == DKIM_SSP_ALL))
8179+                                               ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
8180+                               }
8181+                               // if the SSP is testing, return neutral
8182+                               if (bTestingPractices)
8183+                                       return(DKIM_NEUTRAL);
8184+                               /* if the message should be signed, return fail */
8185+                               if (resDKIMSSP == DKIM_SSP_ALL || resDKIMSSP == DKIM_SSP_STRICT)
8186+                                       return(DKIM_FAIL);
8187+                               ret = DKIM_NEUTRAL;
8188+                       }
8189+               }
8190+               DKIMVerifyFree(&ctxt);
8191+               writeHeader(ret, resDKIMSSP, resDKIMADSP, useSSP, useADSP);
8192+               if ((dkimverify = getenv("DKIMVERIFY"))) {
8193+                       if (ret < 0) {
8194+                               if (dkimverify[str_chr(dkimverify, 'F' - ret)])
8195+                                       ret = 14; /*- return permanent error */
8196+                               if (dkimverify[str_chr(dkimverify, 'f' - ret)])
8197+                                       ret = 88; /*- return temporary error */
8198+                       } else {
8199+                               if (dkimverify[str_chr(dkimverify, 'A' + ret)])
8200+                                       ret = 14; /*- return permanent error */
8201+                               if (dkimverify[str_chr(dkimverify, 'a' + ret)])
8202+                                       ret = 88; /*- return temporary error */
8203+                       }
8204+               }
8205+               return (ret);
8206+       }
8207+       /*- Not Reached */
8208+       _exit(0);
8209+}
8210+
8211+void
8212+getversion_dkim_c()
8213+{
8214+       static char    *x = (char *) "$Id: dkim.c,v 1.23 2019-06-14 21:24:59+05:30 Cprogrammer Exp mbhangui $";
8215+
8216+       x++;
8217+}
8218diff -ruN ../netqmail-1.06-original/dkim.h netqmail-1.06/dkim.h
8219--- ../netqmail-1.06-original/dkim.h    1970-01-01 01:00:00.000000000 +0100
8220+++ netqmail-1.06/dkim.h        2019-02-27 20:57:13.386025114 +0100
8221@@ -0,0 +1,193 @@
8222+/*
8223+ * $Log: dkim.h,v $
8224+ * Revision 1.8  2015-12-15 16:03:18+05:30  Cprogrammer
8225+ * use time_t for expireTime
8226+ *
8227+ * Revision 1.7  2011-06-04 13:56:06+05:30  Cprogrammer
8228+ * corrected return codes
8229+ *
8230+ * Revision 1.6  2011-06-04 10:04:00+05:30  Cprogrammer
8231+ * unified error code for signing & verifcation
8232+ * added signature and identity domain information to
8233+ *     DKIMVerifyDetails structure
8234+ *
8235+ * Revision 1.5  2009-03-27 20:19:05+05:30  Cprogrammer
8236+ * major changes made for incorporating ADSP
8237+ *
8238+ * Revision 1.4  2009-03-26 19:28:15+05:30  Cprogrammer
8239+ * removed DKIM_3PS_PARTIAL_SUCCESS
8240+ *
8241+ * Revision 1.3  2009-03-26 15:11:33+05:30  Cprogrammer
8242+ * added ADSP
8243+ *
8244+ * Revision 1.2  2009-03-25 08:37:58+05:30  Cprogrammer
8245+ * changed definitions of constants to avoid clash between error and success
8246+ *
8247+ * Revision 1.1  2009-03-21 08:50:19+05:30  Cprogrammer
8248+ * Initial revision
8249+ *
8250+ *
8251+ *  Copyright 2005 Alt-N Technologies, Ltd.
8252+ *
8253+ *  Licensed under the Apache License, Version 2.0 (the "License");
8254+ *  you may not use this file except in compliance with the License.
8255+ *  You may obtain a copy of the License at
8256+ *
8257+ *      http://www.apache.org/licenses/LICENSE-2.0
8258+ *
8259+ *  This code incorporates intellectual property owned by Yahoo! and licensed
8260+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
8261+ *
8262+ *  Unless required by applicable law or agreed to in writing, software
8263+ *  distributed under the License is distributed on an "AS IS" BASIS,
8264+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8265+ *  See the License for the specific language governing permissions and
8266+ *  limitations under the License.
8267+ *
8268+ */
8269+
8270+#define DKIM_CALL
8271+#define MAKELONG(a,b) ((long)(((unsigned)(a) & 0xffff) | (((unsigned)(b) & 0xffff) << 16)))
8272+
8273+#ifdef __cplusplus
8274+extern          "C" {
8275+#endif
8276+
8277+#include <time.h>
8278+// DKIM Body hash versions
8279+#define DKIM_BODYHASH_ALLMAN_1 1
8280+#define DKIM_BODYHASH_IETF_1   2
8281+#define DKIM_BODYHASH_BOTH             DKIM_BODYHASH_ALLMAN_1 | DKIM_BODYHASH_IETF_1
8282+
8283+// DKIM hash algorithms
8284+#define DKIM_HASH_SHA1                 1
8285+#define DKIM_HASH_SHA256               2
8286+#define DKIM_HASH_SHA1_AND_256  DKIM_HASH_SHA1 | DKIM_HASH_SHA256
8287+
8288+// DKIM canonicalization methods
8289+#define DKIM_CANON_SIMPLE              1
8290+#define DKIM_CANON_NOWSP               2
8291+#define DKIM_CANON_RELAXED             3
8292+
8293+#define DKIM_SIGN_SIMPLE                       MAKELONG(DKIM_CANON_SIMPLE,DKIM_CANON_SIMPLE)
8294+#define DKIM_SIGN_SIMPLE_RELAXED       MAKELONG(DKIM_CANON_RELAXED,DKIM_CANON_SIMPLE)
8295+#define DKIM_SIGN_RELAXED                      MAKELONG(DKIM_CANON_RELAXED,DKIM_CANON_RELAXED)
8296+#define DKIM_SIGN_RELAXED_SIMPLE       MAKELONG(DKIM_CANON_SIMPLE,DKIM_CANON_RELAXED)
8297+
8298+// DKIM_SUCCESS                                 // verify result: all signatures verified
8299+// signature result: signature verified
8300+#define DKIM_SUCCESS                                            0      // operation successful
8301+#define DKIM_FINISHED_BODY                                      1      // process result: no more message body is needed
8302+#define DKIM_PARTIAL_SUCCESS                            2      // verify result: at least one but not all signatures verified
8303+#define DKIM_NEUTRAL                                            3      // verify result: no signatures verified but message is not suspicous
8304+#define DKIM_SUCCESS_BUT_EXTRA                          4      // signature result: signature verified but it did not include all of the body
8305+#define DKIM_3PS_SIGNATURE                                      5      // 3rd-party signature
8306+
8307+// DKIM Error codes
8308+#define DKIM_FAIL                                                      -1      // verify error: message is suspicious
8309+#define DKIM_BAD_SYNTAX                                                -2      // signature error: DKIM-Signature could not parse or has bad tags/values
8310+#define DKIM_SIGNATURE_BAD                                     -3      // signature error: RSA verify failed
8311+#define DKIM_SIGNATURE_BAD_BUT_TESTING         -4      // signature error: RSA verify failed but testing
8312+#define DKIM_SIGNATURE_EXPIRED                         -5      // signature error: x= is old
8313+#define DKIM_SELECTOR_INVALID                          -6      // signature error: selector doesn't parse or contains invalid values
8314+#define DKIM_SELECTOR_GRANULARITY_MISMATCH     -7      // signature error: selector g= doesn't match i=
8315+#define DKIM_SELECTOR_KEY_REVOKED                      -8      // signature error: selector p= empty
8316+#define DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG     -9      // signature error: selector domain name too long to request
8317+#define DKIM_SELECTOR_DNS_TEMP_FAILURE         -10     // signature error: temporary dns failure requesting selector
8318+#define DKIM_SELECTOR_DNS_PERM_FAILURE         -11     // signature error: permanent dns failure requesting selector
8319+#define DKIM_SELECTOR_PUBLIC_KEY_INVALID       -12     // signature error: selector p= value invalid or wrong format
8320+#define DKIM_NO_SIGNATURES                                     -13     // process error, no sigs
8321+#define DKIM_NO_VALID_SIGNATURES                       -14     // process error, no valid sigs
8322+#define DKIM_BODY_HASH_MISMATCH                                -15     // sigature verify error: message body does not hash to bh value
8323+#define DKIM_SELECTOR_ALGORITHM_MISMATCH       -16     // signature error: selector h= doesn't match signature a=
8324+#define DKIM_STAT_INCOMPAT                                     -17     // signature error: incompatible v=
8325+#define DKIM_UNSIGNED_FROM                  -18 // signature error: not all message's From headers in signature
8326+#define DKIM_OUT_OF_MEMORY                  -19 // memory allocation failed
8327+#define DKIM_INVALID_CONTEXT                -20 // DKIMContext structure invalid for this operation
8328+#define DKIM_NO_SENDER                      -21 // signing error: Could not find From: or Sender: header in message
8329+#define DKIM_BAD_PRIVATE_KEY                -22 // signing error: Could not parse private key
8330+#define DKIM_BUFFER_TOO_SMALL               -23 // signing error: Buffer passed in is not large enough
8331+#define DKIM_MAX_ERROR                      -24 // set this to 1 greater than the highest error code (but negative)
8332+
8333+#define DKIM_SSP_UNKNOWN                        1 /*- some messages may be signed */
8334+#define DKIM_SSP_ALL                            2 /*- all messages are signed, 3rd party allowed */
8335+#define DKIM_SSP_STRICT                                 3 /*- all messages are signed, no 3rd party allowed */
8336+#define DKIM_SSP_SCOPE                          4 /*- the domain should be considered invalid */
8337+#define DKIM_SSP_TEMPFAIL                       5 /*- Temporary Error */
8338+
8339+#define DKIM_ADSP_UNKNOWN                       1 /*- some messages may be signed */
8340+#define DKIM_ADSP_ALL                           2 /*- All message are signed with author signature */
8341+#define DKIM_ADSP_DISCARDABLE           3 /*- messages which fail verification are Discardable */
8342+#define DKIM_ADSP_SCOPE                                 4 /*- domain is out of scope */
8343+#define DKIM_ADSP_TEMPFAIL                      5 /*- Temporary Error */
8344+
8345+
8346+// This function is called once for each header in the message
8347+// return 1 to include this header in the signature and 0 to exclude.
8348+typedef int     (DKIM_CALL * DKIMHEADERCALLBACK) (const char *szHeader);
8349+
8350+// This function is called to retrieve a TXT record from DNS
8351+typedef int     (DKIM_CALL * DKIMDNSCALLBACK) (const char *szFQDN, char *szBuffer, int nBufLen);
8352+
8353+typedef struct DKIMContext_t {
8354+       unsigned int    reserved1;
8355+       unsigned int    reserved2;
8356+       void           *reserved3;
8357+} DKIMContext;
8358+
8359+typedef struct DKIMSignOptions_t {
8360+       int             nCanon; // canonization
8361+       int             nIncludeBodyLengthTag;  // 0 = don't include l= tag, 1 = include l= tag
8362+       int             nIncludeTimeStamp;      // 0 = don't include t= tag, 1 = include t= tag
8363+       int             nIncludeQueryMethod;    // 0 = don't include q= tag, 1 = include q= tag
8364+       char            szSelector[80]; // selector - required
8365+       char            szDomain[256];  // domain - optional - if empty, domain is computed from sender
8366+       char            szIdentity[256];        // for i= tag, if empty tag will not be included in sig
8367+       time_t          expireTime;     // for x= tag, if 0 tag will not be included in sig
8368+       DKIMHEADERCALLBACK pfnHeaderCallback;   // header callback
8369+       char            szRequiredHeaders[256]; // colon-separated list of headers that must be signed
8370+       int             nHash;  // use one of the DKIM_HASH_xx constants here
8371+       // even if not present in the message
8372+       int             nIncludeCopiedHeaders;  // 0 = don't include z= tag, 1 = include z= tag
8373+       int             nIncludeBodyHash;       // use one of the DKIM_BODYHASH_xx constants here
8374+} DKIMSignOptions;
8375+
8376+typedef struct DKIMVerifyOptions_t {
8377+       DKIMDNSCALLBACK pfnSelectorCallback;    // selector record callback
8378+       DKIMDNSCALLBACK pfnPracticesCallback;   // SSP record callback
8379+       int             nHonorBodyLengthTag;    // 0 = ignore l= tag, 1 = use l= tag to limit the amount of body verified
8380+       int             nCheckPractices;                // 0 = use default (unknown) practices, 1 = request and use sender's signing practices
8381+       int             nSubjectRequired;               // 0 = subject is required to be signed, 1 = not required
8382+       int             nSaveCanonicalizedData; // 0 = canonicalized data is not saved, 1 = canonicalized data is saved
8383+       int             nAllowUnsignedFromHeaders;      // 0 = From headers not included in the signature are not allowed, 1 = allowed
8384+       int             nAccept3ps;                             // 0 = don't check 3rd party signature(s), 1 = check 3rd party signature(s)
8385+} DKIMVerifyOptions;
8386+
8387+typedef struct DKIMVerifyDetails_t {
8388+       char           *szSignature;
8389+       char           *DNS;
8390+       char           *szSignatureDomain;
8391+       char           *szIdentityDomain;
8392+       char           *szCanonicalizedData;
8393+       int             nResult;
8394+} DKIMVerifyDetails;
8395+
8396+int DKIM_CALL   DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions * pOptions);
8397+int DKIM_CALL   DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength);
8398+int DKIM_CALL   DKIMSignGetSig(DKIMContext *pSignContext, char *szPrivKey, char *szSignature, int nSigLength);
8399+int DKIM_CALL   DKIMSignGetSig2(DKIMContext *pSignContext, char *szPrivKey, char **pszSignature);
8400+void DKIM_CALL  DKIMSignFree(DKIMContext *pSignContext);
8401+char           *DKIM_CALL DKIMSignGetDomain(DKIMContext *pSignContext);
8402+
8403+int DKIM_CALL   DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions * pOptions);
8404+int DKIM_CALL   DKIMVerifyProcess(DKIMContext *pVerifyContext, char *szBuffer, int nBufLength);
8405+int DKIM_CALL   DKIMVerifyResults(DKIMContext *pVerifyContext , int *sCount, int *sSize);
8406+int DKIM_CALL   DKIMVerifyGetDetails(DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices);
8407+char           *DKIM_CALL DKIMVerifyGetDomain(DKIMContext *pVerifyContext);
8408+void DKIM_CALL  DKIMVerifyFree(DKIMContext *pVerifyContext);
8409+char           *DKIM_CALL DKIMVersion();
8410+char           *DKIM_CALL DKIMGetErrorString(int ErrorCode);
8411+#include "macros.h"
8412+#ifdef __cplusplus
8413+}
8414+#endif
8415diff -ruN ../netqmail-1.06-original/dkimbase.cpp netqmail-1.06/dkimbase.cpp
8416--- ../netqmail-1.06-original/dkimbase.cpp      1970-01-01 01:00:00.000000000 +0100
8417+++ netqmail-1.06/dkimbase.cpp  2019-06-19 09:46:20.131250010 +0200
8418@@ -0,0 +1,339 @@
8419+/*
8420+ * $Log: dkimbase.cpp,v $
8421+ * Revision 1.5  2019-06-14 21:24:03+05:30  Cprogrammer
8422+ * BUG - honor body length tag in verification
8423+ *
8424+ * Revision 1.4  2017-09-05 10:58:26+05:30  Cprogrammer
8425+ * removed compiler warnings
8426+ *
8427+ * Revision 1.3  2009-03-26 15:10:32+05:30  Cprogrammer
8428+ * fixed indentation
8429+ *
8430+ * Revision 1.2  2009-03-25 08:37:27+05:30  Cprogrammer
8431+ * fixed indentation
8432+ *
8433+ * Revision 1.1  2009-03-21 08:43:08+05:30  Cprogrammer
8434+ * Initial revision
8435+ *
8436+ *
8437+ *  Copyright 2005 Alt-N Technologies, Ltd.
8438+ *
8439+ *  Licensed under the Apache License, Version 2.0 (the "License");
8440+ *  you may not use this file except in compliance with the License.
8441+ *  You may obtain a copy of the License at
8442+ *
8443+ *      http://www.apache.org/licenses/LICENSE-2.0
8444+ *
8445+ *  This code incorporates intellectual property owned by Yahoo! and licensed
8446+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
8447+ *
8448+ *  Unless required by applicable law or agreed to in writing, software
8449+ *  distributed under the License is distributed on an "AS IS" BASIS,
8450+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8451+ *  See the License for the specific language governing permissions and
8452+ *  limitations under the License.
8453+ *
8454+ */
8455+
8456+#include "dkim.h"
8457+#include "dkimbase.h"
8458+#include <string.h>
8459+#include <algorithm>
8460+
8461+CDKIMBase::CDKIMBase()
8462+{
8463+       m_From = NULL;
8464+       m_Sender = NULL;
8465+       m_hTag = NULL;
8466+       m_hTagSize = 0;
8467+       m_hTagPos = 0;
8468+       m_Line = NULL;
8469+       m_LineSize = 0;
8470+       m_LinePos = 0;
8471+       m_InHeaders = true;
8472+}
8473+
8474+CDKIMBase::~CDKIMBase()
8475+{
8476+       Free(m_Line);
8477+       Free(m_From);
8478+       Free(m_Sender);
8479+       Free(m_hTag);
8480+}
8481+
8482+int
8483+CDKIMBase::Init(void)
8484+{
8485+       return DKIM_SUCCESS;
8486+}
8487+
8488+////////////////////////////////////////////////////////////////////////////////
8489+//
8490+// Alloc - allocate buffer
8491+//
8492+////////////////////////////////////////////////////////////////////////////////
8493+int CDKIMBase::Alloc(char *&szBuffer, int nRequiredSize)
8494+{
8495+       szBuffer = new char[nRequiredSize];
8496+
8497+       return (szBuffer == NULL) ? DKIM_OUT_OF_MEMORY : DKIM_SUCCESS;
8498+}
8499+
8500+
8501+////////////////////////////////////////////////////////////////////////////////
8502+//
8503+// ReAlloc - extend buffer if necessary, leaving room for future expansion
8504+//
8505+////////////////////////////////////////////////////////////////////////////////
8506+int CDKIMBase::ReAlloc(char *&szBuffer, int &nBufferSize, int nRequiredSize)
8507+{
8508+       if (nRequiredSize > nBufferSize) {
8509+               char           *
8510+                       newp;
8511+               int
8512+                       nNewSize = nRequiredSize + BUFFER_ALLOC_INCREMENT;
8513+
8514+               if (Alloc(newp, nNewSize) == DKIM_SUCCESS) {
8515+                       if (szBuffer != NULL && nBufferSize > 0) {
8516+                               memcpy(newp, szBuffer, nBufferSize);
8517+                               delete[]szBuffer;
8518+                       }
8519+                       szBuffer = newp;
8520+                       nBufferSize = nNewSize;
8521+               } else {
8522+                       return DKIM_OUT_OF_MEMORY;      // memory alloc error!
8523+               }
8524+       }
8525+       return DKIM_SUCCESS;
8526+}
8527+
8528+////////////////////////////////////////////////////////////////////////////////
8529+//
8530+// Process - split buffers into lines without any CRs or LFs at the end.
8531+//
8532+////////////////////////////////////////////////////////////////////////////////
8533+void CDKIMBase::Free(char *szBuffer)
8534+{
8535+       if (szBuffer)
8536+               delete[]szBuffer;
8537+}
8538+
8539+////////////////////////////////////////////////////////////////////////////////
8540+//
8541+// Process - split buffers into lines without any CRs or LFs at the end.
8542+//
8543+////////////////////////////////////////////////////////////////////////////////
8544+int CDKIMBase::Process(char *szBuffer, int nBufLength, bool bEOF)
8545+{
8546+       char           *p = szBuffer;
8547+       char           *e = szBuffer + nBufLength;
8548+
8549+       while (p < e) {
8550+               if (*p != '\n' && *p != '\r') {
8551+                       if (m_LinePos >= m_LineSize) {
8552+                               int nRet = ReAlloc(m_Line, m_LineSize, m_LinePos + 1);
8553+                               if (nRet != DKIM_SUCCESS)
8554+                                                       /*
8555+                                                        * How to distinguish between
8556+                                                        * DKIM_FINISHED_BODY & DKIM_OUT_OF_MEMORY
8557+                                                        */
8558+                                       return nRet;
8559+                       }
8560+                       m_Line[m_LinePos++] = *p;
8561+               } else {
8562+                       if (*p == '\r' && p + 1 < e && *(p + 1) == '\n')
8563+                               p++;
8564+                       if (m_InHeaders) {
8565+                       // process header line
8566+                               if (m_LinePos == 0) {
8567+                                       m_InHeaders = false;
8568+                                       int Result = ProcessHeaders();
8569+                                       if (Result != DKIM_SUCCESS)
8570+                                               return Result;
8571+                               } else {
8572+                               // append the header to the headers list
8573+                                       if (m_Line[0] != ' ' && m_Line[0] != '\t') {
8574+                                               HeaderList.push_back(string(m_Line, m_LinePos));
8575+                                       } else {
8576+                                               if (!HeaderList.empty()) {
8577+                                                       HeaderList.back().append("\r\n", 2).append(m_Line, m_LinePos);
8578+                                               } else {
8579+                                               // no header to append to...
8580+                                               }
8581+                                       }
8582+                               }
8583+                       } else {
8584+                               // process body line
8585+                               int Result = ProcessBody(m_Line, m_LinePos, bEOF);
8586+                               if (Result != DKIM_SUCCESS && Result != DKIM_FINISHED_BODY) {
8587+                                       m_LinePos = 0;
8588+                                       return Result;
8589+                               }
8590+                       }
8591+                       m_LinePos = 0;
8592+               }
8593+               p++;
8594+       }
8595+       return DKIM_SUCCESS;
8596+}
8597+
8598+
8599+////////////////////////////////////////////////////////////////////////////////
8600+//
8601+// ProcessFinal - process leftovers if stopping before the body or mid-line
8602+//
8603+////////////////////////////////////////////////////////////////////////////////
8604+int CDKIMBase::ProcessFinal(void)
8605+{
8606+       if (m_LinePos > 0) {
8607+               Process((char *) "\r\n", 2, true);
8608+       }
8609+
8610+       if (m_InHeaders) {
8611+               m_InHeaders = false;
8612+               ProcessHeaders();
8613+               ProcessBody((char *) "", 0, true);
8614+       }
8615+
8616+       return DKIM_SUCCESS;
8617+}
8618+
8619+
8620+////////////////////////////////////////////////////////////////////////////////
8621+//
8622+// ProcessHeaders - process the headers (to be implemented by derived class)
8623+//
8624+////////////////////////////////////////////////////////////////////////////////
8625+int CDKIMBase::ProcessHeaders()
8626+{
8627+       return DKIM_SUCCESS;
8628+}
8629+
8630+
8631+////////////////////////////////////////////////////////////////////////////////
8632+//
8633+// ProcessBody - process body line (to be implemented by derived class)
8634+//
8635+////////////////////////////////////////////////////////////////////////////////
8636+int CDKIMBase::ProcessBody(char *szBuffer, int nBufLength, bool bEOF)
8637+{
8638+       return DKIM_SUCCESS;
8639+}
8640+
8641+
8642+////////////////////////////////////////////////////////////////////////////////
8643+//
8644+// RemoveSWSP - remove streaming white space from buffer/string inline
8645+//
8646+////////////////////////////////////////////////////////////////////////////////
8647+
8648+struct isswsp {
8649+       bool
8650+       operator() (char ch) {
8651+               return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
8652+       }
8653+};
8654+
8655+void CDKIMBase::RemoveSWSP(char *szBuffer)
8656+{
8657+       *remove_if(szBuffer, szBuffer + strlen(szBuffer), isswsp()) = '\0';
8658+}
8659+
8660+void CDKIMBase::RemoveSWSP(char *pBuffer, int &nBufLength)
8661+{
8662+       nBufLength = remove_if(pBuffer, pBuffer + nBufLength, isswsp()) - pBuffer;
8663+}
8664+
8665+void CDKIMBase::RemoveSWSP(string &sBuffer)
8666+{
8667+       sBuffer.erase(remove_if(sBuffer.begin(), sBuffer.end(), isswsp()), sBuffer.end());
8668+}
8669+
8670+
8671+//////////////////////////////////////////////////////////////////////////////////////////
8672+//
8673+// CompressSWSP - compress streaming white space into single spaces from buffer/string inline
8674+//
8675+//////////////////////////////////////////////////////////////////////////////////////////
8676+
8677+void CDKIMBase::CompressSWSP(char *pBuffer, int &nBufLength)
8678+{
8679+       char           *pSrc = pBuffer;
8680+       char           *pDst = pBuffer;
8681+       char           *pEnd = pBuffer + nBufLength;
8682+
8683+       while (pSrc != pEnd) {
8684+               if (isswsp()(*pSrc)) {
8685+                       do {
8686+                               ++pSrc;
8687+                       } while (pSrc != pEnd && isswsp()(*pSrc));
8688+                       if (pSrc == pEnd)
8689+                               break;
8690+                       *pDst++ = ' ';
8691+               }
8692+               *pDst++ = *pSrc++;
8693+       }
8694+       nBufLength = pDst - pBuffer;
8695+}
8696+
8697+void CDKIMBase::CompressSWSP(string &sBuffer)
8698+{
8699+       string::iterator iSrc = sBuffer.begin();
8700+       string::iterator iDst = sBuffer.begin();
8701+       string::iterator iEnd = sBuffer.end();
8702+
8703+       while (iSrc != iEnd) {
8704+               if (isswsp()(*iSrc)) {
8705+                       do {
8706+                               ++iSrc;
8707+                       } while (iSrc != iEnd && isswsp()(*iSrc));
8708+
8709+                       if (iSrc == iEnd)
8710+                               break;
8711+                       *iDst++ = ' ';
8712+               }
8713+               *iDst++ = *iSrc++;
8714+       }
8715+       sBuffer.erase(iDst, iEnd);
8716+}
8717+
8718+//////////////////////////////////////////////////////////////////////////////////////////
8719+//
8720+// RelaxHeader - relax a header field (lower case the name, remove swsp before and after :)
8721+//
8722+// modified 4/21/06 STB to remove white space before colon
8723+//
8724+//////////////////////////////////////////////////////////////////////////////////////////
8725+
8726+string CDKIMBase::RelaxHeader(const string &sHeader)
8727+{
8728+       string sTemp = sHeader;
8729+
8730+       CompressSWSP(sTemp);
8731+
8732+       int cpos = sTemp.find(':');
8733+       if (cpos == -1) {
8734+       // no colon?!
8735+       } else {
8736+       // lower case the header field name
8737+               for (int i = 0; i < cpos; i++) {
8738+                       if (sTemp[i] >= 'A' && sTemp[i] <= 'Z')
8739+                               sTemp[i] += 'a' - 'A';
8740+               }
8741+       // remove the space after the :
8742+               if ((unsigned int) (cpos + 1) < sTemp.length() && sTemp[cpos + 1] == ' ')
8743+                       sTemp.erase(cpos + 1, 1);
8744+       // remove the space before the :
8745+               if (cpos > 0 && sTemp[cpos - 1] == ' ')
8746+                       sTemp.erase(cpos - 1, 1);
8747+       }
8748+       return sTemp;
8749+}
8750+
8751+void
8752+getversion_dkimbase_cpp()
8753+{
8754+       static char    *x = (char *) "$Id: dkimbase.cpp,v 1.5 2019-06-14 21:24:03+05:30 Cprogrammer Exp mbhangui $";
8755+
8756+       x++;
8757+}
8758diff -ruN ../netqmail-1.06-original/dkimbase.h netqmail-1.06/dkimbase.h
8759--- ../netqmail-1.06-original/dkimbase.h        1970-01-01 01:00:00.000000000 +0100
8760+++ netqmail-1.06/dkimbase.h    2019-02-27 20:57:13.386025114 +0100
8761@@ -0,0 +1,72 @@
8762+/*
8763+ * $Log: dkimbase.h,v $
8764+ * Revision 1.1  2009-03-21 08:50:18+05:30  Cprogrammer
8765+ * Initial revision
8766+ *
8767+ *
8768+ *  Copyright 2005 Alt-N Technologies, Ltd.
8769+ *
8770+ *  Licensed under the Apache License, Version 2.0 (the "License");
8771+ *  you may not use this file except in compliance with the License.
8772+ *  You may obtain a copy of the License at
8773+ *
8774+ *      http://www.apache.org/licenses/LICENSE-2.0
8775+ *
8776+ *  This code incorporates intellectual property owned by Yahoo! and licensed
8777+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
8778+ *
8779+ *  Unless required by applicable law or agreed to in writing, software
8780+ *  distributed under the License is distributed on an "AS IS" BASIS,
8781+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8782+ *  See the License for the specific language governing permissions and
8783+ *  limitations under the License.
8784+ *
8785+ */
8786+
8787+#ifndef DKIMBASE_H
8788+#define DKIMBASE_H
8789+
8790+#include <openssl/evp.h>
8791+#include <openssl/pem.h>
8792+#include <openssl/err.h>
8793+
8794+#define BUFFER_ALLOC_INCREMENT 256
8795+
8796+#include <string>
8797+#include <list>
8798+
8799+using namespace std;
8800+class           CDKIMBase {
8801+public:
8802+
8803+       CDKIMBase();
8804+       ~CDKIMBase();
8805+
8806+       int             Init(void);
8807+       int             Process(char *szBuffer, int nBufLength, bool bEOF);
8808+       int             ProcessFinal(void);
8809+       int             Alloc(char *&szBuffer, int nRequiredSize);
8810+       int             ReAlloc(char *&szBuffer, int &nBufferLength, int nRequiredSize);
8811+       void            Free(char *szBuffer);
8812+       static void     RemoveSWSP(char *szBuffer);
8813+       static void     RemoveSWSP(char *pBuffer, int &nBufLength);
8814+       static void     RemoveSWSP(string & sBuffer);
8815+       static void     CompressSWSP(char *pBuffer, int &nBufLength);
8816+       static void     CompressSWSP(string & sBuffer);
8817+       static string   RelaxHeader(const string & sHeader);
8818+       virtual int     ProcessHeaders(void);
8819+       virtual int     ProcessBody(char *szBuffer, int nBufLength, bool bEOF);
8820+
8821+protected:
8822+       char           *m_From;
8823+       char           *m_Sender;
8824+       char           *m_hTag;
8825+       int             m_hTagSize;
8826+       int             m_hTagPos;
8827+       char           *m_Line;
8828+       int             m_LineSize;
8829+       int             m_LinePos;
8830+       bool            m_InHeaders;
8831+                       list < string > HeaderList;
8832+};
8833+#endif /*- DKIMBASE_H */
8834diff -ruN ../netqmail-1.06-original/dkimdns.cpp netqmail-1.06/dkimdns.cpp
8835--- ../netqmail-1.06-original/dkimdns.cpp       1970-01-01 01:00:00.000000000 +0100
8836+++ netqmail-1.06/dkimdns.cpp   2019-05-23 15:12:30.128092884 +0200
8837@@ -0,0 +1,329 @@
8838+/*
8839+ * $Log: dns.cpp,v $
8840+ * Revision 1.10  2017-09-05 11:01:06+05:30  Cprogrammer
8841+ * removed unused variables
8842+ *
8843+ * Revision 1.9  2017-09-01 12:46:27+05:30  Cprogrammer
8844+ * fixed double free() of dnresult
8845+ *
8846+ * Revision 1.8  2017-08-09 22:06:33+05:30  Cprogrammer
8847+ * fixed resolve() function
8848+ *
8849+ * Revision 1.7  2017-05-16 12:40:23+05:30  Cprogrammer
8850+ * refactored dns_text() function
8851+ *
8852+ * Revision 1.6  2017-05-10 14:58:06+05:30  Cprogrammer
8853+ * increase responselen to 1024 for long text records
8854+ *
8855+ * Revision 1.5  2017-05-10 12:27:49+05:30  Cprogrammer
8856+ * use packetsize > 512 to avoid dkim failures for sites having long txt records (hotmail.com)
8857+ *
8858+ * Revision 1.4  2009-06-11 13:58:39+05:30  Cprogrammer
8859+ * port for DARWIN
8860+ *
8861+ * Revision 1.3  2009-03-27 19:22:45+05:30  Cprogrammer
8862+ * dns functions
8863+ *
8864+ */
8865+#include <netdb.h>
8866+#include <stdlib.h>
8867+#include <errno.h>
8868+#include <sys/types.h>
8869+#include <netinet/in.h>
8870+#ifdef DARWIN
8871+#include <nameser8_compat.h>
8872+#endif
8873+#include <arpa/nameser.h>
8874+#include <resolv.h>
8875+#include <string.h>
8876+#include "dkimdns.h"
8877+
8878+static unsigned short
8879+getshort(unsigned char *cp)
8880+{
8881+       return (cp[0] << 8) | cp[1];
8882+}
8883+
8884+static struct
8885+{
8886+       unsigned char  *buf;
8887+} response;
8888+static int      responsebuflen = 0;
8889+static int      responselen;
8890+static unsigned char *responseend;
8891+static unsigned char *responsepos;
8892+static u_long   saveresoptions;
8893+static int      numanswers;
8894+static char     name[MAXDNAME];
8895+
8896+static int
8897+resolve(char *domain, int type)
8898+{
8899+       int             n, i;
8900+       unsigned char  *ptr;
8901+
8902+       errno = 0;
8903+       if (!responsebuflen) {
8904+               if ((response.buf = (unsigned char *) malloc(PACKETSZ + 1)))
8905+                       responsebuflen = PACKETSZ + 1;
8906+               else
8907+                       return DNS_MEM;
8908+       }
8909+       responselen = res_query(domain, C_IN, type, response.buf, responsebuflen);
8910+       if ((responselen >= responsebuflen) || (responselen > 0 && (((HEADER *) response.buf)->tc))) {
8911+               if (responsebuflen < 65536) {
8912+                       if ((ptr = (unsigned char *) realloc((void *) response.buf, 65536))) {
8913+                               response.buf = ptr;
8914+                               responsebuflen = 65536;
8915+                       } else {
8916+                               free(response.buf);
8917+                               responsebuflen = 0;
8918+                               return DNS_MEM;
8919+                       }
8920+               }
8921+               saveresoptions = _res.options;
8922+               _res.options |= RES_USEVC;
8923+               responselen = res_query(domain, C_IN, type, response.buf, responsebuflen);
8924+               _res.options = saveresoptions;
8925+       }
8926+       if (responselen <= 0) {
8927+               if (errno == ECONNREFUSED)
8928+                       return DNS_SOFT;
8929+               if (h_errno == TRY_AGAIN)
8930+                       return DNS_SOFT;
8931+               return DNS_HARD;
8932+       }
8933+       responseend = response.buf + responselen;
8934+       responsepos = response.buf + sizeof(HEADER);
8935+       n = ntohs(((HEADER *) response.buf)->qdcount);
8936+       while (n-- > 0) {
8937+               if ((i = dn_expand(response.buf, responseend, responsepos, name, MAXDNAME)) < 0)
8938+                       return DNS_SOFT;
8939+               responsepos += i;
8940+               i = responseend - responsepos;
8941+               if (i < QFIXEDSZ)
8942+                       return DNS_SOFT;
8943+               responsepos += QFIXEDSZ;
8944+       }
8945+       numanswers = ntohs(((HEADER *) response.buf)->ancount);
8946+       return 0;
8947+}
8948+
8949+void
8950+byte_copy(register char *to, register unsigned int n, register char *from)
8951+{
8952+       for (;;) {
8953+               if (!n)
8954+                       return;
8955+               *to++ = *from++;
8956+               --n;
8957+               if (!n)
8958+                       return;
8959+               *to++ = *from++;
8960+               --n;
8961+               if (!n)
8962+                       return;
8963+               *to++ = *from++;
8964+               --n;
8965+               if (!n)
8966+                       return;
8967+               *to++ = *from++;
8968+               --n;
8969+       }
8970+}
8971+
8972+static char     *txt;
8973+static int      txtlen;
8974+
8975+static int
8976+findtxt(int wanttype, int *txt_strlen)
8977+{
8978+       unsigned short  rrtype, rrdlen;
8979+       char           *ptr;
8980+       int             i;
8981+
8982+       if (numanswers <= 0)
8983+               return 2;
8984+       --numanswers;
8985+       if (responsepos == responseend)
8986+               return DNS_SOFT;
8987+
8988+       if ((i = dn_expand(response.buf, responseend, responsepos, name, MAXDNAME)) < 0)
8989+               return DNS_SOFT;
8990+       responsepos += i;
8991+
8992+       if ((i = responseend - responsepos) < 4 + 3 * 3)
8993+               return DNS_SOFT;
8994+
8995+       rrtype = getshort(responsepos);
8996+       rrdlen = getshort(responsepos + 8);
8997+       responsepos += 10;
8998+
8999+       *txt_strlen = 0;
9000+       if (!txtlen) {
9001+               if (!(txt = (char *) malloc(PACKETSZ * 2 * sizeof(char))))
9002+                       return DNS_MEM;
9003+               txtlen = PACKETSZ * 2;
9004+       }
9005+       if (rrtype == wanttype) {
9006+               unsigned short  txtpos;
9007+               unsigned char   n;
9008+
9009+               *txt = 0;
9010+               for (txtpos = 0; txtpos < rrdlen; txtpos += n) {
9011+                       n = responsepos[txtpos++];
9012+                       if (n > rrdlen - txtpos)
9013+                               n = rrdlen - txtpos;
9014+                       if ((*txt_strlen + n + 1) > txtlen) {
9015+                               if (!(ptr = (char *) realloc(txt, (*txt_strlen + n) * 2))) {
9016+                                       free(txt);
9017+                                       txtlen = 0;
9018+                                       *txt_strlen = 0;
9019+                                       return DNS_MEM;
9020+                               }
9021+                               txt = ptr;
9022+                               txtlen = (*txt_strlen + n) * 2;
9023+                       }
9024+                       byte_copy(txt + *txt_strlen, n, (char *) &responsepos[txtpos]);
9025+                       *txt_strlen += n;
9026+               }
9027+               responsepos += rrdlen;
9028+               txt[*txt_strlen] = 0;
9029+               return 1;
9030+       }
9031+       responsepos += rrdlen;
9032+       return 0;
9033+}
9034+
9035+static char    *dnresult;
9036+static int      dnresultlen;
9037+
9038+static int
9039+dns_txtplus(char *domain)
9040+{
9041+       int             r, len, total;
9042+       char           *ptr;
9043+
9044+       switch (resolve(domain, T_TXT))
9045+       {
9046+       case DNS_MEM:
9047+               return DNS_MEM;
9048+       case DNS_SOFT:
9049+               return DNS_SOFT;
9050+       case DNS_HARD:
9051+               return DNS_HARD;
9052+       }
9053+       total = 0;
9054+       if (!dnresultlen) {
9055+               if (!(dnresult = (char *) malloc((2 * PACKETSZ) * sizeof(char))))
9056+                       return DNS_MEM;
9057+               dnresultlen = 2 * PACKETSZ;
9058+       }
9059+       while ((r = findtxt(T_TXT, &len)) != 2) {
9060+               if (r == DNS_SOFT) {
9061+                       if (txtlen) {
9062+                               txtlen = 0;
9063+                               free(txt);
9064+                       }
9065+                       return DNS_SOFT;
9066+               }
9067+               if (r == 1) {
9068+                       if ((total + len + 1) >= dnresultlen) {
9069+                               if (!(ptr = (char *) realloc(dnresult, (total + len) * 2))) {
9070+                                       free(dnresult);
9071+                                       dnresultlen = 0;
9072+                                       if (txtlen) {
9073+                                               txtlen = 0;
9074+                                               free(txt);
9075+                                       }
9076+                                       return DNS_MEM;
9077+                               }
9078+                               dnresult = ptr;
9079+                               dnresultlen = (total + len) * 2;
9080+                       }
9081+                       byte_copy(dnresult + total, len, txt);
9082+                       total += len;
9083+               }
9084+       }
9085+       if (txtlen) {
9086+               txtlen = 0;
9087+               free(txt);
9088+       }
9089+       if (total) {
9090+               dnresult[total] = 0;
9091+               return (0);
9092+       }
9093+       return DNS_HARD;
9094+}
9095+
9096+/*
9097+ * we always return a null-terminated string which has been malloc'ed.  The string
9098+ * is always in the tag=value form.  If a temporary or permanent error occurs,
9099+ * the string will be exactly "e=perm;" or "e=temp;".
9100+ * Note that it never returns NULL.
9101+ */
9102+char           *
9103+dns_text(char *dn)
9104+{
9105+       int             r;
9106+       
9107+       switch (r = dns_txtplus(dn))
9108+       {
9109+       case DNS_MEM:
9110+       case DNS_SOFT:
9111+               if (responsebuflen) {
9112+                       free(response.buf);
9113+                       responsebuflen = 0;
9114+               }
9115+               return strdup("e=temp;");
9116+       case DNS_HARD:
9117+               if (responsebuflen) {
9118+                       free(response.buf);
9119+                       responsebuflen = 0;
9120+               }
9121+               return strdup("e=perm;");
9122+       }
9123+       if (responsebuflen) {
9124+               free(response.buf);
9125+               responsebuflen = 0;
9126+       }
9127+       return dnresult;
9128+}
9129+
9130+
9131+int
9132+DNSGetTXT(const char *domain, char *buffer, int maxlen)
9133+{
9134+       char           *results;
9135+       int             len;
9136+
9137+       results = dns_text((char *) domain);
9138+       if (!strcmp(results, "e=perm;")) {
9139+               free(results);
9140+               dnresultlen = 0;
9141+               return DNSRESP_PERM_FAIL;
9142+       } else
9143+       if (!strcmp(results, "e=temp;")) {
9144+               free(results);
9145+               dnresultlen = 0;
9146+               return DNSRESP_TEMP_FAIL;
9147+       }
9148+       if ((len = strlen(results)) > maxlen - 1) {
9149+               free(results);
9150+               dnresultlen = 0;
9151+               return DNSRESP_DOMAIN_NAME_TOO_LONG;
9152+       }
9153+       byte_copy(buffer, len, results);
9154+       buffer[len] = 0;
9155+       free(results);
9156+       dnresultlen = 0;
9157+       return DNSRESP_SUCCESS;
9158+}
9159+
9160+void
9161+getversion_dkimdns_cpp()
9162+{
9163+       static char    *x = (char *) "$Id: dns.cpp,v 1.10 2017-09-05 11:01:06+05:30 Cprogrammer Exp mbhangui $";
9164+
9165+       x++;
9166+}
9167diff -ruN ../netqmail-1.06-original/dkimdns.h netqmail-1.06/dkimdns.h
9168--- ../netqmail-1.06-original/dkimdns.h 1970-01-01 01:00:00.000000000 +0100
9169+++ netqmail-1.06/dkimdns.h     2019-02-27 20:57:13.387025103 +0100
9170@@ -0,0 +1,54 @@
9171+/*
9172+ * $Log: dns.h,v $
9173+ * Revision 1.4  2017-05-16 12:40:39+05:30  Cprogrammer
9174+ * define DNS_SOFT, DNS_HARD and DNS_MEM
9175+ *
9176+ * Revision 1.3  2009-03-27 20:40:16+05:30  Cprogrammer
9177+ * removed windows definitions
9178+ *
9179+ * Revision 1.2  2009-03-27 19:23:01+05:30  Cprogrammer
9180+ * added dns_text()
9181+ *
9182+ * Revision 1.1  2009-03-21 08:50:24+05:30  Cprogrammer
9183+ * Initial revision
9184+ *
9185+ *  Copyright 2005 Alt-N Technologies, Ltd.
9186+ *
9187+ *  Licensed under the Apache License, Version 2.0 (the "License");
9188+ *  you may not use this file except in compliance with the License.
9189+ *  You may obtain a copy of the License at
9190+ *
9191+ *      http://www.apache.org/licenses/LICENSE-2.0
9192+ *
9193+ *  This code incorporates intellectual property owned by Yahoo! and licensed
9194+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
9195+ *
9196+ *  Unless required by applicable law or agreed to in writing, software
9197+ *  distributed under the License is distributed on an "AS IS" BASIS,
9198+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9199+ *  See the License for the specific language governing permissions and
9200+ *  limitations under the License.
9201+ *
9202+ */
9203+
9204+// These DNS resolution routines are encapsulated by the API below
9205+
9206+// return values for DNS functions:
9207+
9208+
9209+#define MAX_DOMAIN                     254
9210+
9211+#define DNSRESP_SUCCESS                                        0       // DNS lookup returned sought after records
9212+#define DNSRESP_TEMP_FAIL                              1       // No response from DNS server
9213+#define DNSRESP_PERM_FAIL                              2       // DNS server returned error or no records
9214+#define DNSRESP_DOMAIN_NAME_TOO_LONG   3       // Domain name too long
9215+#define DNSRESP_NXDOMAIN                               4       // DNS server returned Name Error
9216+#define DNSRESP_EMPTY                                  5       // DNS server returned successful response but no records
9217+
9218+#define DNS_SOFT -1
9219+#define DNS_HARD -2
9220+#define DNS_MEM  -3
9221+
9222+// Pass in the FQDN to get the TXT record
9223+int             DNSGetTXT(const char *szFQDN, char *Buffer, int nBufLen);
9224+char           *dns_text(char *szFQDN);
9225diff -ruN ../netqmail-1.06-original/dkimfuncs.cpp netqmail-1.06/dkimfuncs.cpp
9226--- ../netqmail-1.06-original/dkimfuncs.cpp     1970-01-01 01:00:00.000000000 +0100
9227+++ netqmail-1.06/dkimfuncs.cpp 2019-02-27 20:57:13.387025103 +0100
9228@@ -0,0 +1,236 @@
9229+/*
9230+ * $Log: dkimfuncs.cpp,v $
9231+ * Revision 1.4  2011-06-04 10:06:33+05:30  Cprogrammer
9232+ * unified error strings for signing & verification
9233+ *
9234+ * Revision 1.3  2009-04-15 20:45:29+05:30  Cprogrammer
9235+ * code beautified
9236+ *
9237+ * Revision 1.2  2009-03-26 15:11:12+05:30  Cprogrammer
9238+ * added GetDomain(), ADSP
9239+ *
9240+ * Revision 1.1  2009-03-21 08:43:10+05:30  Cprogrammer
9241+ * Initial revision
9242+ *
9243+ *
9244+ *  Copyright 2005 Alt-N Technologies, Ltd.
9245+ *
9246+ *  Licensed under the Apache License, Version 2.0 (the "License");
9247+ *  you may not use this file except in compliance with the License.
9248+ *  You may obtain a copy of the License at
9249+ *
9250+ *      http://www.apache.org/licenses/LICENSE-2.0
9251+ *
9252+ *  This code incorporates intellectual property owned by Yahoo! and licensed
9253+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
9254+ *
9255+ *  Unless required by applicable law or agreed to in writing, software
9256+ *  distributed under the License is distributed on an "AS IS" BASIS,
9257+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9258+ *  See the License for the specific language governing permissions and
9259+ *  limitations under the License.
9260+ *
9261+ */
9262+#ifdef HAVE_CONFIG_H
9263+#include "config.h"
9264+#endif
9265+#include "dkim.h"
9266+#include "dkimsign.h"
9267+#include "dkimverify.h"
9268+#include <string.h>
9269+
9270+#define DKIMID ('D' | 'K'<<8 | 'I'<<16 | 'M'<<24)
9271+
9272+static void
9273+InitContext(DKIMContext *pContext, bool bSign, void *pObject)
9274+{
9275+       pContext->reserved1 = DKIMID;
9276+       pContext->reserved2 = bSign ? 1 : 0;
9277+       pContext->reserved3 = pObject;
9278+}
9279+
9280+static void    *
9281+ValidateContext(DKIMContext *pContext, bool bSign)
9282+{
9283+       if (pContext->reserved1 != DKIMID)
9284+               return NULL;
9285+       if (pContext->reserved2 != (unsigned int) (bSign ? 1 : 0))
9286+               return NULL;
9287+       return pContext->reserved3;
9288+}
9289+
9290+int             DKIM_CALL
9291+DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions *pOptions)
9292+{
9293+       int             nRet = DKIM_OUT_OF_MEMORY;
9294+       CDKIMSign      *pSign = new CDKIMSign;
9295+
9296+       if (pSign) {
9297+               nRet = pSign->Init(pOptions);
9298+               if (nRet != DKIM_SUCCESS)
9299+                       delete          pSign;
9300+       }
9301+       if (nRet == DKIM_SUCCESS)
9302+               InitContext(pSignContext, true, pSign);
9303+       return nRet;
9304+}
9305+
9306+int             DKIM_CALL
9307+DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength)
9308+{
9309+       CDKIMSign      *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
9310+       if (pSign)
9311+               return pSign->Process(szBuffer, nBufLength, false);
9312+       return DKIM_INVALID_CONTEXT;
9313+}
9314+
9315+int             DKIM_CALL
9316+DKIMSignGetSig(DKIMContext *pSignContext, char *szPrivKey, char *szSignature, int nSigLength)
9317+{
9318+       CDKIMSign      *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
9319+       if (pSign)
9320+               return pSign->GetSig(szPrivKey, szSignature, nSigLength);
9321+       return DKIM_INVALID_CONTEXT;
9322+}
9323+
9324+int             DKIM_CALL
9325+DKIMSignGetSig2(DKIMContext *pSignContext, char *szPrivKey, char **pszSignature)
9326+{
9327+       CDKIMSign      *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
9328+       if (pSign)
9329+               return pSign->GetSig2(szPrivKey, pszSignature);
9330+       return DKIM_INVALID_CONTEXT;
9331+}
9332+
9333+char           *DKIM_CALL
9334+DKIMSignGetDomain(DKIMContext *pSignContext)
9335+{
9336+       CDKIMSign      *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
9337+       if (pSign)
9338+               return pSign->GetDomain();
9339+       return ((char *) 0);
9340+}
9341+
9342+void            DKIM_CALL
9343+DKIMSignFree(DKIMContext *pSignContext)
9344+{
9345+       CDKIMSign      *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
9346+       if (pSign) {
9347+               delete          pSign;
9348+               pSignContext->reserved3 = NULL;
9349+       }
9350+}
9351+
9352+int             DKIM_CALL
9353+DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions *pOptions)
9354+{
9355+       int             nRet = DKIM_OUT_OF_MEMORY;
9356+       CDKIMVerify    *pVerify = new CDKIMVerify;
9357+       if (pVerify) {
9358+               nRet = pVerify->Init(pOptions);
9359+               if (nRet != DKIM_SUCCESS)
9360+                       delete          pVerify;
9361+       }
9362+       if (nRet == DKIM_SUCCESS)
9363+               InitContext(pVerifyContext, false, pVerify);
9364+       return nRet;
9365+}
9366+
9367+int             DKIM_CALL
9368+DKIMVerifyProcess(DKIMContext *pVerifyContext, char *szBuffer, int nBufLength)
9369+{
9370+       CDKIMVerify    *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
9371+       if (pVerify)
9372+               return pVerify->Process(szBuffer, nBufLength, false);
9373+       return DKIM_INVALID_CONTEXT;
9374+}
9375+
9376+int             DKIM_CALL
9377+DKIMVerifyResults( DKIMContext *pVerifyContext , int *sCount, int *sSize)
9378+{
9379+       CDKIMVerify    *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
9380+       if (pVerify)
9381+               return pVerify->GetResults(sCount, sSize);
9382+       return DKIM_INVALID_CONTEXT;
9383+}
9384+
9385+int             DKIM_CALL
9386+DKIMVerifyGetDetails(DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices)
9387+{
9388+       szPractices[0] = '\0';
9389+       CDKIMVerify    *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
9390+       if (pVerify) {
9391+               strcpy(szPractices, pVerify->GetPractices());
9392+               return pVerify->GetDetails(nSigCount, pDetails);
9393+       }
9394+       return DKIM_INVALID_CONTEXT;
9395+}
9396+
9397+void            DKIM_CALL
9398+DKIMVerifyFree(DKIMContext *pVerifyContext)
9399+{
9400+       CDKIMVerify    *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
9401+       if (pVerify) {
9402+               delete          pVerify;
9403+               pVerifyContext->reserved3 = NULL;
9404+       }
9405+}
9406+
9407+char           *DKIM_CALL
9408+DKIMVerifyGetDomain(DKIMContext *pVerifyContext)
9409+{
9410+       CDKIMVerify    *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
9411+       if (pVerify)
9412+               return pVerify->GetDomain();
9413+       return ((char *) 0);
9414+}
9415+
9416+char           *DKIM_CALL
9417+DKIMVersion()
9418+{
9419+       return (char *) "1.5";
9420+}
9421+
9422+static char    *DKIMErrorStrings[-1 - DKIM_MAX_ERROR] = {
9423+       (char *) "DKIM_FAIL",
9424+       (char *) "DKIM_BAD_SYNTAX",
9425+       (char *) "DKIM_SIGNATURE_BAD",
9426+       (char *) "DKIM_SIGNATURE_BAD_BUT_TESTING",
9427+       (char *) "DKIM_SIGNATURE_EXPIRED",
9428+       (char *) "DKIM_SELECTOR_INVALID",
9429+       (char *) "DKIM_SELECTOR_GRANULARITY_MISMATCH",
9430+       (char *) "DKIM_SELECTOR_KEY_REVOKED",
9431+       (char *) "DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG",
9432+       (char *) "DKIM_SELECTOR_DNS_TEMP_FAILURE",
9433+       (char *) "DKIM_SELECTOR_DNS_PERM_FAILURE",
9434+       (char *) "DKIM_SELECTOR_PUBLIC_KEY_INVALID",
9435+       (char *) "DKIM_NO_SIGNATURES",
9436+       (char *) "DKIM_NO_VALID_SIGNATURES",
9437+       (char *) "DKIM_BODY_HASH_MISMATCH",
9438+       (char *) "DKIM_SELECTOR_ALGORITHM_MISMATCH",
9439+       (char *) "DKIM_STAT_INCOMPAT",
9440+       (char *) "DKIM_UNSIGNED_FROM",
9441+       (char *) "DKIM_OUT_OF_MEMORY",
9442+       (char *) "DKIM_INVALID_CONTEXT",
9443+       (char *) "DKIM_NO_SENDER",
9444+       (char *) "DKIM_BAD_PRIVATE_KEY",
9445+       (char *) "DKIM_BUFFER_TOO_SMALL"
9446+};
9447+
9448+char           *DKIM_CALL
9449+DKIMGetErrorString(int ErrorCode)
9450+{
9451+       if (ErrorCode >= 0 || ErrorCode <= DKIM_MAX_ERROR)
9452+               return (char *) "Unknown";
9453+
9454+       else
9455+               return DKIMErrorStrings[-1 - ErrorCode];
9456+}
9457+
9458+void
9459+getversion_dkimfuncs_cpp()
9460+{
9461+       static char    *x = (char *) "$Id: dkimfuncs.cpp,v 1.4 2011-06-04 10:06:33+05:30 Cprogrammer Exp mbhangui $";
9462+
9463+       x++;
9464+}
9465diff -ruN ../netqmail-1.06-original/dkimsign.cpp netqmail-1.06/dkimsign.cpp
9466--- ../netqmail-1.06-original/dkimsign.cpp      1970-01-01 01:00:00.000000000 +0100
9467+++ netqmail-1.06/dkimsign.cpp  2020-04-10 18:20:19.279077900 +0200
9468@@ -0,0 +1,1029 @@
9469+/*
9470+ * $Log: dkimsign.cpp,v $
9471+ * Revision 1.17  2020-04-10 21:36:20+05:30  Cprogrammer
9472+ * fixed BUG with domain assignment
9473+ *
9474+ * Revision 1.16  2020-04-09 21:21:04+05:30  Cprogrammer
9475+ * check for null domain after DKIMDOMAIN replacement
9476+ *
9477+ * Revision 1.15  2019-06-26 19:08:18+05:30  Cprogrammer
9478+ * added sBouncedAddr variable for X-Bounced-Address header added by qmail-send for bounces
9479+ *
9480+ * Revision 1.14  2019-06-24 22:22:15+05:30  Cprogrammer
9481+ * use DKIMDOMAIN only if Return-Path, From, Sender header are empty
9482+ *
9483+ * Revision 1.13  2018-08-25 18:01:59+05:30  Cprogrammer
9484+ * fixed dkim signing for From address containing company name
9485+ *
9486+ * Revision 1.12  2018-05-23 13:07:58+05:30  Cprogrammer
9487+ * fixed compiler warnings
9488+ *
9489+ * Revision 1.11  2017-09-05 10:59:03+05:30  Cprogrammer
9490+ * removed compiler warnings
9491+ *
9492+ * Revision 1.10  2017-08-09 22:02:13+05:30  Cprogrammer
9493+ * replaced EVP_MD_CTX_free() with EVP_MD_CTX_reset()
9494+ *
9495+ * Revision 1.9  2017-08-08 23:50:19+05:30  Cprogrammer
9496+ * openssl 1.1.0 port
9497+ *
9498+ * Revision 1.8  2013-07-16 20:18:03+05:30  Cprogrammer
9499+ * replace '%' with domain name in selector
9500+ *
9501+ * Revision 1.7  2013-06-11 00:02:39+05:30  Cprogrammer
9502+ * removed header iostream
9503+ *
9504+ * Revision 1.6  2013-06-09 16:41:28+05:30  Cprogrammer
9505+ * parse address properly from From and Sender header
9506+ *
9507+ * Revision 1.5  2009-04-16 10:32:38+05:30  Cprogrammer
9508+ * added DKIMDOMAIN env variable
9509+ *
9510+ * Revision 1.4  2009-04-15 21:32:12+05:30  Cprogrammer
9511+ * added DKIM-Signature, Received to list of excluded headers
9512+ *
9513+ * Revision 1.3  2009-03-26 15:11:46+05:30  Cprogrammer
9514+ * added GetDomain
9515+ *
9516+ * Revision 1.2  2009-03-21 11:57:19+05:30  Cprogrammer
9517+ * fixed indentation
9518+ *
9519+ * Revision 1.1  2009-03-21 08:43:11+05:30  Cprogrammer
9520+ * Initial revision
9521+ *
9522+ *
9523+ *  Copyright 2005 Alt-N Technologies, Ltd.
9524+ *
9525+ *  Licensed under the Apache License, Version 2.0 (the "License");
9526+ *  you may not use this file except in compliance with the License.
9527+ *  You may obtain a copy of the License at
9528+ *
9529+ *      http://www.apache.org/licenses/LICENSE-2.0
9530+ *
9531+ *  This code incorporates intellectual property owned by Yahoo! and licensed
9532+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
9533+ *
9534+ *  Unless required by applicable law or agreed to in writing, software
9535+ *  distributed under the License is distributed on an "AS IS" BASIS,
9536+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9537+ *  See the License for the specific language governing permissions and
9538+ *  limitations under the License.
9539+ *
9540+ */
9541+
9542+#ifdef HAVE_CONFIG_H
9543+#include "config.h"
9544+#endif
9545+#define _strnicmp strncasecmp
9546+#define _stricmp strcasecmp
9547+#define LOWORD(l) ((unsigned)(l) & 0xffff)
9548+#define HIWORD(l) ((unsigned)(l) >> 16)
9549+
9550+#include <string.h>
9551+#include <map>
9552+#include "dkim.h"
9553+#include "dkimsign.h"
9554+
9555+CDKIMSign::CDKIMSign()
9556+{
9557+       m_EmptyLineCount = 0;
9558+       m_pfnHdrCallback = NULL;
9559+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
9560+       if (!m_allman_sha1ctx)
9561+               m_allman_sha1ctx = EVP_MD_CTX_new();
9562+       EVP_SignInit(m_allman_sha1ctx, EVP_sha1());
9563+       if (!m_Hdr_ietf_sha1ctx)
9564+               m_Hdr_ietf_sha1ctx = EVP_MD_CTX_new();
9565+       EVP_SignInit(m_Hdr_ietf_sha1ctx, EVP_sha1());
9566+       if (!m_Bdy_ietf_sha1ctx)
9567+               m_Bdy_ietf_sha1ctx = EVP_MD_CTX_new();
9568+       EVP_DigestInit(m_Bdy_ietf_sha1ctx, EVP_sha1());
9569+#ifdef HAVE_EVP_SHA256
9570+       if (!m_Hdr_ietf_sha256ctx)
9571+               m_Hdr_ietf_sha256ctx = EVP_MD_CTX_new();
9572+       EVP_SignInit(m_Hdr_ietf_sha256ctx, EVP_sha256());
9573+       if (!m_Bdy_ietf_sha256ctx)
9574+               m_Bdy_ietf_sha256ctx = EVP_MD_CTX_new();
9575+       EVP_DigestInit(m_Bdy_ietf_sha256ctx, EVP_sha256());
9576+#endif
9577+#else
9578+       EVP_SignInit(&m_allman_sha1ctx, EVP_sha1());
9579+       EVP_SignInit(&m_Hdr_ietf_sha1ctx, EVP_sha1());
9580+       EVP_DigestInit(&m_Bdy_ietf_sha1ctx, EVP_sha1());
9581+#ifdef HAVE_EVP_SHA256
9582+       EVP_SignInit(&m_Hdr_ietf_sha256ctx, EVP_sha256());
9583+       EVP_DigestInit(&m_Bdy_ietf_sha256ctx, EVP_sha256());
9584+#endif
9585+#endif
9586+}
9587+
9588+CDKIMSign::~CDKIMSign()
9589+{
9590+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
9591+       EVP_MD_CTX_reset(m_allman_sha1ctx);
9592+       EVP_MD_CTX_reset(m_Hdr_ietf_sha1ctx);
9593+       EVP_MD_CTX_reset(m_Bdy_ietf_sha1ctx);
9594+#ifdef HAVE_EVP_SHA256
9595+       EVP_MD_CTX_reset(m_Hdr_ietf_sha256ctx);
9596+       EVP_MD_CTX_reset(m_Bdy_ietf_sha256ctx);
9597+#endif
9598+#else
9599+       EVP_MD_CTX_cleanup(&m_allman_sha1ctx);
9600+       EVP_MD_CTX_cleanup(&m_Hdr_ietf_sha1ctx);
9601+       EVP_MD_CTX_cleanup(&m_Bdy_ietf_sha1ctx);
9602+#ifdef HAVE_EVP_SHA256
9603+       EVP_MD_CTX_cleanup(&m_Hdr_ietf_sha256ctx);
9604+       EVP_MD_CTX_cleanup(&m_Bdy_ietf_sha256ctx);
9605+#endif
9606+#endif
9607+}
9608+
9609+
9610+////////////////////////////////////////////////////////////////////////////////
9611+//
9612+// Init - save the options
9613+//
9614+////////////////////////////////////////////////////////////////////////////////
9615+int
9616+CDKIMSign::Init(DKIMSignOptions * pOptions)
9617+{
9618+       int             nRet = CDKIMBase::Init();
9619+       m_Canon = pOptions->nCanon;
9620+
9621+// as of draft 01, these are the only allowed signing types:
9622+       if ((m_Canon != DKIM_SIGN_SIMPLE_RELAXED) && (m_Canon != DKIM_SIGN_RELAXED) && (m_Canon != DKIM_SIGN_RELAXED_SIMPLE)) {
9623+               m_Canon = DKIM_SIGN_SIMPLE;
9624+       }
9625+       sSelector.assign(pOptions->szSelector);
9626+       m_pfnHdrCallback = pOptions->pfnHeaderCallback;
9627+       sDomain.assign(pOptions->szDomain);
9628+       m_IncludeBodyLengthTag = (pOptions->nIncludeBodyLengthTag != 0);
9629+       m_nBodyLength = 0;
9630+       m_ExpireTime = pOptions->expireTime;
9631+       sIdentity.assign(pOptions->szIdentity);
9632+       m_nIncludeTimeStamp = pOptions->nIncludeTimeStamp;
9633+       m_nIncludeQueryMethod = pOptions->nIncludeQueryMethod;
9634+       m_nIncludeCopiedHeaders = pOptions->nIncludeCopiedHeaders;
9635+       m_nIncludeBodyHash = pOptions->nIncludeBodyHash;
9636+
9637+// NOTE: the following line is not backwards compatible with MD 8.0.3
9638+// because the szRequiredHeaders member was added after the release
9639+//sRequiredHeaders.assign( pOptions->szRequiredHeaders );
9640+
9641+//make sure there is a colon after the last header in the list
9642+       if ((sRequiredHeaders.size() > 0) && sRequiredHeaders.at(sRequiredHeaders.size() - 1) != ':')
9643+               sRequiredHeaders.append(":");
9644+       m_nHash = pOptions->nHash;
9645+       m_bReturnedSigAssembled = false;
9646+       m_sCopiedHeaders.erase();
9647+       return nRet;
9648+}
9649+
9650+
9651+// Hash - update the hash
9652+void
9653+CDKIMSign::Hash(const char *szBuffer, int nBufLength, bool bHdr, bool bAllmanOnly)
9654+{
9655+       EVP_MD_CTX     *p1, *p2, *p3, *p4, *p5;
9656+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
9657+       p1 = m_allman_sha1ctx;
9658+       p2 = m_Hdr_ietf_sha1ctx;
9659+       p3 = m_Hdr_ietf_sha256ctx;
9660+       p4 = m_Bdy_ietf_sha1ctx;
9661+       p5 = m_Bdy_ietf_sha256ctx;
9662+#else
9663+       p1 = &m_allman_sha1ctx;
9664+       p2 = &m_Hdr_ietf_sha1ctx;
9665+       p3 = &m_Hdr_ietf_sha256ctx;
9666+       p4 = &m_Bdy_ietf_sha1ctx;
9667+       p5 = &m_Bdy_ietf_sha256ctx;
9668+#endif
9669+       if (bAllmanOnly) {
9670+               if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1)
9671+                       EVP_SignUpdate(p1, szBuffer, nBufLength);
9672+       } else {
9673+               if (m_nIncludeBodyHash < DKIM_BODYHASH_IETF_1)
9674+                       EVP_SignUpdate(p1, szBuffer, nBufLength);
9675+               else
9676+               if (m_nIncludeBodyHash & DKIM_BODYHASH_IETF_1) {
9677+                       if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1)
9678+                               EVP_SignUpdate(p1, szBuffer, nBufLength);
9679+#ifdef HAVE_EVP_SHA256
9680+                       if (m_nHash & DKIM_HASH_SHA256) {
9681+                               if (bHdr)
9682+                                       EVP_SignUpdate(p3, szBuffer, nBufLength);
9683+                               else
9684+                                       EVP_DigestUpdate(p5, szBuffer, nBufLength);
9685+                       }
9686+                       if (m_nHash != DKIM_HASH_SHA256) {
9687+                               if (bHdr)
9688+                                       EVP_SignUpdate(p2, szBuffer, nBufLength);
9689+                               else
9690+                                       EVP_DigestUpdate(p4, szBuffer, nBufLength);
9691+                       }
9692+#else
9693+                       if (bHdr)
9694+                               EVP_SignUpdate(p2, szBuffer, nBufLength);
9695+                       else
9696+                               EVP_DigestUpdate(p4, szBuffer, nBufLength);
9697+#endif
9698+               }
9699+       }
9700+}
9701+
9702+
9703+////////////////////////////////////////////////////////////////////////////////
9704+//
9705+// SignThisTag - return boolean whether or not to sign this tag
9706+//
9707+////////////////////////////////////////////////////////////////////////////////
9708+bool CDKIMSign::SignThisTag(const string &sTag)
9709+{
9710+       bool            bRet = true;
9711+
9712+       if (_strnicmp(sTag.c_str(), "X-", 2) == 0
9713+               || _stricmp(sTag.c_str(), "Authentication-Results:") == 0
9714+               || _stricmp(sTag.c_str(), "DKIM-Signature:") == 0
9715+               || _stricmp(sTag.c_str(), "Received:") == 0
9716+               || _stricmp(sTag.c_str(), "Return-Path:") == 0)
9717+       {
9718+               bRet = false;
9719+       }
9720+       return bRet;
9721+}
9722+
9723+bool
9724+ConvertHeaderToQuotedPrintable(const char *source, char *dest)
9725+{
9726+       bool            bConvert = false;
9727+
9728+// do quoted printable
9729+       static unsigned char hexchars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
9730+       unsigned char  *d = (unsigned char *) dest;
9731+       for (const unsigned char *s = (const unsigned char *)source; *s != '\0'; s++) {
9732+               if (*s >= 33 && *s <= 126 && *s != '=' && *s != ':' && *s != ';' && *s != '|') {
9733+                       *d++ = *s;
9734+               }
9735+
9736+               else {
9737+                       bConvert = true;
9738+                       *d++ = '=';
9739+                       *d++ = hexchars[*s >> 4];
9740+                       *d++ = hexchars[*s & 15];
9741+               }
9742+       }
9743+       *d = '\0';
9744+       return bConvert;
9745+}
9746+
9747+
9748+////////////////////////////////////////////////////////////////////////////////
9749+//
9750+// GetHeaderParams - Extract any needed header parameters
9751+//
9752+////////////////////////////////////////////////////////////////////////////////
9753+void
9754+CDKIMSign::GetHeaderParams(const string & sHdr)
9755+{
9756+       if (_strnicmp(sHdr.c_str(), "X-Bounced-Address:", 18) == 0)
9757+               sBouncedAddr.assign(sHdr.c_str() + 21);
9758+       else
9759+       if (_strnicmp(sHdr.c_str(), "X", 1) == 0)
9760+               return;
9761+       if (_strnicmp(sHdr.c_str(), "From:", 5) == 0)
9762+               sFrom.assign(sHdr.c_str() + 5);
9763+       if (_strnicmp(sHdr.c_str(), "Sender:", 7) == 0)
9764+               sSender.assign(sHdr.c_str() + 7);
9765+       if (_strnicmp(sHdr.c_str(), "Return-Path:", 12) == 0)
9766+               sReturnPath.assign(sHdr.c_str() + 12);
9767+       if (m_nIncludeCopiedHeaders) {
9768+               string::size_type pos = sHdr.find(':');
9769+               if (pos != string::npos) {
9770+                       string          sTag, sValue;
9771+                       char           *workBuffer = new char[sHdr.size() * 3 + 1];
9772+                       sTag.assign(sHdr.substr(0, pos));
9773+                       sValue.assign(sHdr.substr(pos + 1, string::npos));
9774+                       ConvertHeaderToQuotedPrintable(sTag.c_str(), workBuffer);
9775+                       if (!m_sCopiedHeaders.empty()) {
9776+                               m_sCopiedHeaders.append("|");
9777+                       }
9778+                       m_sCopiedHeaders.append(workBuffer);
9779+                       m_sCopiedHeaders.append(":");
9780+                       ConvertHeaderToQuotedPrintable(sValue.c_str(), workBuffer);
9781+                       m_sCopiedHeaders.append(workBuffer);
9782+                       delete[]workBuffer;
9783+               }
9784+       }
9785+}
9786+
9787+// ProcessHeaders - sign headers and save needed parameters
9788+int
9789+CDKIMSign::ProcessHeaders(void)
9790+{
9791+       map <string, list <string>::reverse_iterator> IterMap;
9792+       map <string, list <string>::reverse_iterator>::iterator IterMapIter;
9793+       list <string>::reverse_iterator riter;
9794+       list <string>::iterator iter;
9795+       string          sTag;
9796+       bool            bFromHeaderFound = false;
9797+
9798+       // walk the header list
9799+       for (iter = HeaderList.begin(); iter != HeaderList.end(); iter++) {
9800+               sTag.assign(*iter);
9801+               // look for a colon
9802+               string::size_type pos = sTag.find(':');
9803+               if (pos != string::npos) {
9804+                       int             nSignThisTag = 1;
9805+                       // hack off anything past the colon
9806+                       sTag.erase(pos + 1, string::npos);
9807+                       // is this the From: header?
9808+                       if (_stricmp(sTag.c_str(), "From:") == 0) {
9809+                               bFromHeaderFound = true;
9810+                               nSignThisTag = 1;
9811+                               IsRequiredHeader(sTag); // remove from required header list
9812+                       }
9813+                       // is this in the list of headers that must be signed?
9814+                       else
9815+                       if (IsRequiredHeader(sTag))
9816+                               nSignThisTag = 1;
9817+                       else {
9818+                               if (m_pfnHdrCallback)
9819+                                       nSignThisTag = m_pfnHdrCallback(iter->c_str());
9820+                               else
9821+                                       nSignThisTag = SignThisTag(sTag) ? 1 : 0;
9822+                       }
9823+                       // save header parameters
9824+                       GetHeaderParams(*iter);
9825+                       if (nSignThisTag > 0) {
9826+                               // add this tag to h=
9827+                               hParam.append(sTag);
9828+                               IterMapIter = IterMap.find(sTag);
9829+                               riter = (IterMapIter == IterMap.end())? HeaderList.rbegin() : IterMapIter->second;
9830+                               // walk the list in reverse looking for the last instance of this header
9831+                               while (riter != HeaderList.rend()) {
9832+                                       if (_strnicmp(riter->c_str(), sTag.c_str(), sTag.size()) == 0) {
9833+                                               ProcessHeader(*riter);
9834+                                               // save the reverse iterator position for this tag
9835+                                               riter++;
9836+                                               IterMap[sTag] = riter;
9837+                                               break;
9838+                                       }
9839+                                       riter++;
9840+                               }
9841+                       }
9842+               }
9843+       }
9844+       Hash("\r\n", 2, true, true);    // only for Allman sig
9845+       if (!bFromHeaderFound) {
9846+               string sFrom("From:");
9847+               hParam.append(sFrom);
9848+               IsRequiredHeader(sFrom);        // remove from required header list
9849+       }
9850+       hParam.append(sRequiredHeaders);
9851+       if (hParam.at(hParam.size() - 1) == ':')
9852+               hParam.erase(hParam.size() - 1, string::npos);
9853+       return DKIM_SUCCESS;
9854+}
9855+
9856+char           *DKIM_CALL
9857+CDKIMSign::GetDomain(void)
9858+{
9859+       if (ParseFromAddress() == false)
9860+               return ((char *) 0);
9861+       return ((char *) sDomain.c_str());
9862+}
9863+
9864+void
9865+CDKIMSign::ProcessHeader(const string & sHdr)
9866+{
9867+       switch (HIWORD(m_Canon)) {
9868+       case DKIM_CANON_SIMPLE:
9869+               Hash(sHdr.c_str(), sHdr.size(), true);
9870+               Hash("\r\n", 2, true);
9871+               break;
9872+       case DKIM_CANON_NOWSP:
9873+               {
9874+               string sTemp = sHdr;
9875+               RemoveSWSP(sTemp);
9876+               // convert characters before ':' to lower case
9877+               for (char *s = (char *)sTemp.c_str(); *s != '\0' && *s != ':'; s++) {
9878+                       if (*s >= 'A' && *s <= 'Z')
9879+                               *s += 'a' - 'A';
9880+               }
9881+               Hash(sTemp.c_str(), sTemp.size(), true);
9882+               Hash("\r\n", 2, true);
9883+               }
9884+               break;
9885+       case DKIM_CANON_RELAXED:
9886+               {
9887+               string sTemp = RelaxHeader(sHdr);
9888+               Hash(sTemp.c_str(), sTemp.length(), true);
9889+               Hash("\r\n", 2, true);
9890+               }
9891+               break;
9892+       }
9893+}
9894+
9895+int CDKIMSign::ProcessBody(char *szBuffer, int nBufLength, bool bEOF)
9896+{
9897+       switch (LOWORD(m_Canon)) {
9898+       case DKIM_CANON_SIMPLE:
9899+               if (nBufLength > 0) {
9900+                       while (m_EmptyLineCount > 0) {
9901+                               Hash("\r\n", 2, false);
9902+                               m_nBodyLength += 2;
9903+                               m_EmptyLineCount--;
9904+                       }
9905+                       Hash(szBuffer, nBufLength, false);
9906+                       Hash("\r\n", 2, false);
9907+                       m_nBodyLength += nBufLength + 2;
9908+               } else {
9909+                       m_EmptyLineCount++;
9910+                       if (bEOF) {
9911+                               Hash("\r\n", 2, false);
9912+                               m_nBodyLength += 2;
9913+                       }
9914+               }
9915+               break;
9916+       case DKIM_CANON_NOWSP:
9917+               RemoveSWSP(szBuffer, nBufLength);
9918+               if (nBufLength > 0) {
9919+                       Hash(szBuffer, nBufLength, false);
9920+                       m_nBodyLength += nBufLength;
9921+               }
9922+               break;
9923+       case DKIM_CANON_RELAXED:
9924+               CompressSWSP(szBuffer, nBufLength);
9925+               if (nBufLength > 0) {
9926+                       while (m_EmptyLineCount > 0) {
9927+                               Hash("\r\n", 2, false);
9928+                               m_nBodyLength += 2;
9929+                               m_EmptyLineCount--;
9930+                       }
9931+                       Hash(szBuffer, nBufLength, false);
9932+                       m_nBodyLength += nBufLength;
9933+                       if (!bEOF) {
9934+                               Hash("\r\n", 2, false);
9935+                               m_nBodyLength += 2;
9936+                       }
9937+               } else
9938+                       m_EmptyLineCount++;
9939+               break;
9940+       }
9941+       return DKIM_SUCCESS;
9942+}
9943+
9944+bool CDKIMSign::ParseFromAddress(void)
9945+{
9946+       string::size_type pos;
9947+       string          sAddress;
9948+       char           *p, *at;
9949+
9950+       /* thanks to fred */
9951+       if (!sReturnPath.empty())
9952+               sAddress.assign(sReturnPath);
9953+       else
9954+       if (!sSender.empty())
9955+               sAddress.assign(sSender);
9956+       else
9957+       if (!sFrom.empty())
9958+               sAddress.assign(sFrom);
9959+       else /* use indimail's X-Bounced-Address header to find the domain that injected the bounce */
9960+       if (!sBouncedAddr.empty())
9961+               sAddress.assign(sBouncedAddr);
9962+       else
9963+               return false;
9964+       /*-
9965+        * simple for now, beef it up later
9966+        * remove '<' and anything before it
9967+        */
9968+       pos = sAddress.find('<');
9969+       if (pos != string::npos)
9970+               sAddress.erase(0, pos + 1);
9971+
9972+       /* remove '>' and anything after it */
9973+       pos = sAddress.find('>');
9974+       if (pos != string::npos)
9975+               sAddress.erase(pos, string::npos);
9976+       /* look for '@' symbol */
9977+       if (sDomain.empty()) {
9978+               pos = sAddress.find('@');
9979+               if (pos != string::npos)
9980+                       sDomain.assign(sAddress.c_str() + pos + 1);
9981+               if (sDomain.empty()) {
9982+                       p = getenv("DKIMDOMAIN");
9983+                       if (p && *p) {
9984+                               if (!(at = strchr(p, '@')))
9985+                                       at = p;
9986+                               else
9987+                                       at++;
9988+                               sDomain.assign(at);
9989+                       }
9990+               }
9991+       }
9992+       RemoveSWSP(sDomain);
9993+       return true;
9994+}
9995+
9996+
9997+////////////////////////////////////////////////////////////////////////////////
9998+//
9999+// InitSig - initialize signature folding algorithm
10000+//
10001+////////////////////////////////////////////////////////////////////////////////
10002+void
10003+CDKIMSign::InitSig(void)
10004+{
10005+       m_sSig.reserve(1024);
10006+       m_sSig.assign("DKIM-Signature:");
10007+       m_nSigPos = m_sSig.size();
10008+}
10009+
10010+////////////////////////////////////////////////////////////////////////////////
10011+//
10012+// AddTagToSig - add tag and value to signature folding if necessary
10013+//               if bFold, fold at cbrk char
10014+//
10015+////////////////////////////////////////////////////////////////////////////////
10016+void CDKIMSign::AddTagToSig(char *Tag, const string & sValue, char cbrk, bool bFold)
10017+{
10018+       int
10019+                       nTagLen = strlen(Tag);
10020+       AddInterTagSpace((!bFold) ? sValue.size() + nTagLen + 2 : nTagLen + 2);
10021+       m_sSig.append(Tag);
10022+       m_sSig.append("=");
10023+       m_nSigPos += 1 + nTagLen;
10024+       if (!bFold) {
10025+               m_sSig.append(sValue);
10026+               m_nSigPos += sValue.size();
10027+       }
10028+
10029+       else {
10030+               AddFoldedValueToSig(sValue, cbrk);
10031+       }
10032+       m_sSig.append(";");
10033+       m_nSigPos++;
10034+}
10035+
10036+
10037+////////////////////////////////////////////////////////////////////////////////
10038+//
10039+// AddTagToSig - add tag and numeric value to signature folding if necessary
10040+//
10041+////////////////////////////////////////////////////////////////////////////////
10042+void CDKIMSign::AddTagToSig(char *Tag, unsigned long nValue)
10043+{
10044+       char            szValue[64];
10045+       sprintf(szValue, "%lu", nValue);
10046+       AddTagToSig(Tag, szValue, 0, false);
10047+}
10048+
10049+////////////////////////////////////////////////////////////////////////////////
10050+//
10051+// AddInterTagSpace - add space or fold here
10052+//
10053+////////////////////////////////////////////////////////////////////////////////
10054+void CDKIMSign::AddInterTagSpace(int nSizeOfNextTag)
10055+{
10056+       if (m_nSigPos + nSizeOfNextTag + 1 > OptimalHeaderLineLength) {
10057+               m_sSig.append("\n\t");
10058+               m_nSigPos = 1;
10059+       }
10060+
10061+       else {
10062+               m_sSig.append(" ");
10063+               m_nSigPos++;
10064+       }
10065+}
10066+
10067+
10068+////////////////////////////////////////////////////////////////////////////////
10069+//
10070+// AddTagToSig - add value to signature folding if necessary
10071+//               if cbrk == 0 fold anywhere, otherwise fold only at cbrk
10072+//
10073+////////////////////////////////////////////////////////////////////////////////
10074+void CDKIMSign::AddFoldedValueToSig(const string & sValue, char cbrk)
10075+{
10076+       string::size_type pos = 0;
10077+       if (cbrk == 0) {
10078+
10079+       // fold anywhere
10080+               while (pos < sValue.size()) {
10081+                       string::size_type len = OptimalHeaderLineLength - m_nSigPos;
10082+                       if (len > sValue.size() - pos)
10083+                               len = sValue.size() - pos;
10084+                       m_sSig.append(sValue.substr(pos, len));
10085+                       m_nSigPos += len;
10086+                       pos += len;
10087+                       if (pos < sValue.size()) {
10088+                               m_sSig.append("\n\t");
10089+                               m_nSigPos = 1;
10090+                       }
10091+               }
10092+       }
10093+
10094+       else {
10095+
10096+       // fold only at cbrk
10097+               while (pos < sValue.size()) {
10098+                       string::size_type len = OptimalHeaderLineLength - m_nSigPos;
10099+                       string::size_type brkpos;
10100+                       if (sValue.size() - pos < len) {
10101+                               brkpos = sValue.size() - 1;
10102+                       }
10103+
10104+                       else {
10105+                               brkpos = sValue.rfind(cbrk, pos + len);
10106+                       }
10107+                       if (brkpos == string::npos || brkpos < pos) {
10108+                               brkpos = sValue.find(cbrk, pos);
10109+                               if (brkpos == string::npos) {
10110+                                       brkpos = sValue.size();
10111+                               }
10112+                       }
10113+                       len = brkpos - pos + 1;
10114+                       m_sSig.append(sValue.substr(pos, len));
10115+                       m_nSigPos += len;
10116+                       pos += len;
10117+                       if (pos < sValue.size()) {
10118+
10119+                       //m_sSig.append( "\r\n\t" );
10120+                               m_sSig.append("\n\t");
10121+                               m_nSigPos = 1;
10122+                       }
10123+               }
10124+       }
10125+}
10126+
10127+
10128+////////////////////////////////////////////////////////////////////////////////
10129+//
10130+// GetSig - compute hash and return signature header in szSignature
10131+//
10132+////////////////////////////////////////////////////////////////////////////////
10133+int CDKIMSign::GetSig(char *szPrivKey, char *szSignature, unsigned int nSigLength)
10134+{
10135+       if (szPrivKey == NULL) {
10136+               return DKIM_BAD_PRIVATE_KEY;
10137+       }
10138+       if (szSignature == NULL) {
10139+               return DKIM_BUFFER_TOO_SMALL;
10140+       }
10141+       int             nRet = AssembleReturnedSig(szPrivKey);
10142+       if (nRet != DKIM_SUCCESS)
10143+               return nRet;
10144+       if (m_sReturnedSig.size() + 1 < nSigLength)
10145+               strcpy(szSignature, m_sReturnedSig.c_str());
10146+       else
10147+               return DKIM_BUFFER_TOO_SMALL;
10148+       return DKIM_SUCCESS;
10149+}
10150+
10151+
10152+////////////////////////////////////////////////////////////////////////////////
10153+//
10154+// GetSig - compute hash and return signature header in szSignature
10155+//
10156+////////////////////////////////////////////////////////////////////////////////
10157+int CDKIMSign::GetSig2(char *szPrivKey, char **pszSignature)
10158+{
10159+       if (szPrivKey == NULL) {
10160+               return DKIM_BAD_PRIVATE_KEY;
10161+       }
10162+       if (pszSignature == NULL) {
10163+               return DKIM_BUFFER_TOO_SMALL;
10164+       }
10165+       int             nRet = AssembleReturnedSig(szPrivKey);
10166+       if (nRet != DKIM_SUCCESS)
10167+               return nRet;
10168+       *pszSignature = (char *) m_sReturnedSig.c_str();
10169+       return DKIM_SUCCESS;
10170+}
10171+
10172+
10173+////////////////////////////////////////////////////////////////////////////////
10174+//
10175+// IsRequiredHeader - Check if header in required list. If so, delete
10176+//                    header from list.
10177+//
10178+////////////////////////////////////////////////////////////////////////////////
10179+bool CDKIMSign::IsRequiredHeader(const string & sTag)
10180+{
10181+       string::size_type start = 0;
10182+       string::size_type end = sRequiredHeaders.find(':');
10183+       while (end != string::npos) {
10184+
10185+       // check for a zero-length header
10186+               if (start == end) {
10187+                       sRequiredHeaders.erase(start, 1);
10188+               }
10189+
10190+               else {
10191+                       if (_stricmp(sTag.c_str(), sRequiredHeaders.substr(start, end - start + 1).c_str()) == 0) {
10192+                               sRequiredHeaders.erase(start, end - start + 1);
10193+                               return true;
10194+                       }
10195+
10196+                       else {
10197+                               start = end + 1;
10198+                       }
10199+               }
10200+               end = sRequiredHeaders.find(':', start);
10201+       }
10202+       return false;
10203+}
10204+
10205+int CDKIMSign::ConstructSignature(char *szPrivKey, bool bUseIetfBodyHash, bool bUseSha256)
10206+{
10207+       string          sSignedSig;
10208+       unsigned char  *sig;
10209+       EVP_PKEY       *pkey;
10210+       BIO            *bio, *b64;
10211+       unsigned int    siglen;
10212+       int             size, len;
10213+       char           *buf, *cptr;
10214+       const char     *ptr, *dptr, *sptr;
10215+       EVP_MD_CTX     *p1, *p2, *p3, *p4, *p5;
10216+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10217+       p1 = m_allman_sha1ctx;
10218+       p2 = m_Hdr_ietf_sha1ctx;
10219+       p3 = m_Hdr_ietf_sha256ctx;
10220+       p4 = m_Bdy_ietf_sha1ctx;
10221+       p5 = m_Bdy_ietf_sha256ctx;
10222+#else
10223+       p1 = &m_allman_sha1ctx;
10224+       p2 = &m_Hdr_ietf_sha1ctx;
10225+       p3 = &m_Hdr_ietf_sha256ctx;
10226+       p4 = &m_Bdy_ietf_sha1ctx;
10227+       p5 = &m_Bdy_ietf_sha256ctx;
10228+#endif
10229+
10230+// construct the DKIM-Signature: header and add to hash
10231+       InitSig();
10232+       if (bUseIetfBodyHash) {
10233+               AddTagToSig((char *) "v", (char *) "1", 0, false);
10234+       }
10235+#ifdef HAVE_EVP_SHA256
10236+       AddTagToSig((char *) "a", bUseSha256 ? "rsa-sha256" : "rsa-sha1", 0, false);
10237+#else
10238+       AddTagToSig((char *) "a", "rsa-sha1", 0, false);
10239+#endif
10240+       switch (m_Canon) {
10241+       case DKIM_SIGN_SIMPLE:
10242+               AddTagToSig((char *) "c", "simple", 0, false);
10243+               break;
10244+       case DKIM_SIGN_SIMPLE_RELAXED:
10245+               AddTagToSig((char *) "c", "simple/relaxed", 0, false);
10246+               break;
10247+       case DKIM_SIGN_RELAXED:
10248+               AddTagToSig((char *) "c", "relaxed/relaxed", 0, false);
10249+               break;
10250+       case DKIM_SIGN_RELAXED_SIMPLE:
10251+               AddTagToSig((char *) "c", "relaxed", 0, false);
10252+               break;
10253+       }
10254+       AddTagToSig((char *) "d", sDomain, 0, false);
10255+       /*- replace % with domain name */
10256+       ptr = sSelector.c_str();
10257+       if ((sptr = strchr(ptr, '%'))) {
10258+               dptr = sDomain.c_str();
10259+               for (sptr = ptr, len = 0;*sptr;sptr++) {
10260+                       if (*sptr == '%')
10261+                               len += (int) strlen(dptr) + 1;
10262+                       else
10263+                               len++;
10264+               }
10265+               if (!(buf = new char[len]))
10266+                       return DKIM_OUT_OF_MEMORY;
10267+               for (cptr = buf, sptr = ptr; *sptr; sptr++) {
10268+                       if (*sptr == '%') {
10269+                               memcpy(cptr, dptr, (len = strlen(dptr)));
10270+                               cptr += len;
10271+                       } else
10272+                               *cptr++ = *sptr;
10273+               }
10274+               *cptr = 0;
10275+               sSelector.assign(buf);
10276+               delete[]buf;
10277+       }
10278+       AddTagToSig((char *) "s", sSelector, 0, false);
10279+       if (m_IncludeBodyLengthTag) {
10280+               AddTagToSig((char *) "l", m_nBodyLength);
10281+       }
10282+       if (m_nIncludeTimeStamp != 0) {
10283+               time_t t;
10284+               time(&t);
10285+               AddTagToSig((char *) "t", t);
10286+       }
10287+       if (m_ExpireTime != 0) {
10288+               AddTagToSig((char *) "x", m_ExpireTime);
10289+       }
10290+       if (!sIdentity.empty()) {
10291+               AddTagToSig((char *) "i", sIdentity, 0, false);
10292+       }
10293+       if (m_nIncludeQueryMethod) {
10294+               AddTagToSig((char *) "q", bUseIetfBodyHash ? "dns/txt" : "dns", 0, false);
10295+       }
10296+       AddTagToSig((char *) "h", hParam, ':', true);
10297+       if (m_nIncludeCopiedHeaders) {
10298+               AddTagToSig((char *) "z", m_sCopiedHeaders, 0, true);
10299+       }
10300+       if (bUseIetfBodyHash) {
10301+               unsigned char Hash[EVP_MAX_MD_SIZE];
10302+               unsigned int nHashLen = 0;
10303+#ifdef HAVE_EVP_SHA256
10304+               EVP_DigestFinal(bUseSha256 ? p5 : p4, Hash, &nHashLen);
10305+#else
10306+               EVP_DigestFinal(p4, Hash, &nHashLen);
10307+#endif
10308+               bio = BIO_new(BIO_s_mem());
10309+               if (!bio) {
10310+                       return DKIM_OUT_OF_MEMORY;
10311+               }
10312+               b64 = BIO_new(BIO_f_base64());
10313+               if (!b64) {
10314+                       BIO_free(bio);
10315+                       return DKIM_OUT_OF_MEMORY;
10316+               }
10317+               BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
10318+               BIO_push(b64, bio);
10319+               if (BIO_write(b64, Hash, nHashLen) < (int) nHashLen) {
10320+                       BIO_free_all(b64);
10321+                       return DKIM_OUT_OF_MEMORY;
10322+               }
10323+               BIO_flush(b64);
10324+               len = nHashLen * 2;
10325+               buf = new char[len];
10326+               if (buf == NULL) {
10327+                       BIO_free_all(b64);
10328+                       return DKIM_OUT_OF_MEMORY;
10329+               }
10330+               size = BIO_read(bio, buf, len);
10331+               BIO_free_all(b64);
10332+               // this should never happen
10333+               if (size >= len) {
10334+                       delete[]buf;
10335+                       return DKIM_OUT_OF_MEMORY;
10336+               }
10337+               buf[size] = '\0';
10338+               AddTagToSig((char *) "bh", buf, 0, true);
10339+               delete[]buf;
10340+       }
10341+       AddInterTagSpace(3);
10342+       m_sSig.append("b=");
10343+       m_nSigPos += 2;
10344+       // Force a full copy - no reference copies please
10345+       sSignedSig.assign(m_sSig.c_str());
10346+       // note that since we're not calling hash here, need to dump this
10347+       // to the debug file if you want the full canonical form
10348+       string          sTemp;
10349+       if (HIWORD(m_Canon) == DKIM_CANON_RELAXED)
10350+               sTemp = RelaxHeader(sSignedSig);
10351+       else
10352+               sTemp = sSignedSig.c_str();
10353+       if (bUseIetfBodyHash) {
10354+#ifdef HAVE_EVP_SHA256
10355+               EVP_SignUpdate(bUseSha256 ? p3 : p2, sTemp.c_str(), sTemp.size());
10356+#else
10357+               EVP_SignUpdate(p2, sTemp.c_str(), sTemp.size());
10358+#endif
10359+       } else
10360+               EVP_SignUpdate(p1, sTemp.c_str(), sTemp.size());
10361+       if (!(bio = BIO_new_mem_buf(szPrivKey, -1)))
10362+               return DKIM_OUT_OF_MEMORY;
10363+       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
10364+       BIO_free(bio);
10365+       if (!pkey) {
10366+               return DKIM_BAD_PRIVATE_KEY;
10367+       }
10368+       siglen = EVP_PKEY_size(pkey);
10369+       int             nSignRet;
10370+       sig = (unsigned char *) OPENSSL_malloc(siglen);
10371+       if (sig == NULL) {
10372+               EVP_PKEY_free(pkey);
10373+               return DKIM_OUT_OF_MEMORY;
10374+       }
10375+       if (bUseIetfBodyHash) {
10376+#ifdef HAVE_EVP_SHA256
10377+               nSignRet = EVP_SignFinal(bUseSha256 ? p3 : p2, sig, &siglen, pkey);
10378+#else
10379+               nSignRet = EVP_SignFinal(p2, sig, &siglen, pkey);
10380+#endif
10381+       } else
10382+               nSignRet = EVP_SignFinal(p1, sig, &siglen, pkey);
10383+       EVP_PKEY_free(pkey);
10384+       if (!nSignRet) {
10385+               OPENSSL_free(sig);
10386+               return DKIM_BAD_PRIVATE_KEY;    // key too small
10387+       }
10388+       bio = BIO_new(BIO_s_mem());
10389+       if (!bio) {
10390+               return DKIM_OUT_OF_MEMORY;
10391+       }
10392+       b64 = BIO_new(BIO_f_base64());
10393+       if (!b64) {
10394+               BIO_free(bio);
10395+               return DKIM_OUT_OF_MEMORY;
10396+       }
10397+       BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
10398+       BIO_push(b64, bio);
10399+       if (BIO_write(b64, sig, siglen) < (int) siglen) {
10400+               OPENSSL_free(sig);
10401+               BIO_free_all(b64);
10402+               return DKIM_OUT_OF_MEMORY;
10403+       }
10404+       BIO_flush(b64);
10405+       OPENSSL_free(sig);
10406+       len = siglen * 2;
10407+       buf = new char[len];
10408+       if (buf == NULL) {
10409+               BIO_free_all(b64);
10410+               return DKIM_OUT_OF_MEMORY;
10411+       }
10412+       size = BIO_read(bio, buf, len);
10413+       BIO_free_all(b64);
10414+       // this should never happen
10415+       if (size >= len) {
10416+               delete[]buf;
10417+               return DKIM_OUT_OF_MEMORY;
10418+       }
10419+       buf[size] = '\0';
10420+       AddFoldedValueToSig(buf, 0);
10421+       delete[]buf;
10422+       return DKIM_SUCCESS;
10423+}
10424+
10425+int CDKIMSign::AssembleReturnedSig(char *szPrivKey)
10426+{
10427+       int             nRet;
10428+       if (m_bReturnedSigAssembled)
10429+               return DKIM_SUCCESS;
10430+       ProcessFinal();
10431+       if (ParseFromAddress() == false) {
10432+               //return DKIM_NO_SENDER;
10433+       }
10434+       Hash("\r\n", 2, true, true);    // only for Allman sig
10435+       string allmansha1sig,
10436+#ifdef HAVE_EVP_SHA256
10437+               ietfsha256Sig,
10438+#endif
10439+               ietfsha1Sig;
10440+       if (m_nIncludeBodyHash < DKIM_BODYHASH_IETF_1) {
10441+               nRet = ConstructSignature(szPrivKey, false, false);
10442+               if (nRet == DKIM_SUCCESS)
10443+                       allmansha1sig.assign(m_sSig);
10444+               else
10445+                       return nRet;
10446+       } else
10447+       if (m_nIncludeBodyHash & DKIM_BODYHASH_IETF_1) {
10448+               if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1) {
10449+                       if ((nRet = ConstructSignature(szPrivKey, false, false)) == DKIM_SUCCESS)
10450+                               allmansha1sig.assign(m_sSig);
10451+                       else
10452+                               return nRet;
10453+               }
10454+#ifdef HAVE_EVP_SHA256
10455+               if (m_nHash & DKIM_HASH_SHA256) {
10456+                       if ((nRet = ConstructSignature(szPrivKey, true, true)) == DKIM_SUCCESS)
10457+                               ietfsha256Sig.assign(m_sSig);
10458+                       else
10459+                               return nRet;
10460+               }
10461+               if (m_nHash != DKIM_HASH_SHA256) {
10462+                       if ((nRet = ConstructSignature(szPrivKey, true, false)) == DKIM_SUCCESS)
10463+                               ietfsha1Sig.assign(m_sSig);
10464+                       else
10465+                               return nRet;
10466+               }
10467+#else
10468+               if ((nRet = ConstructSignature(szPrivKey, true, false)) == DKIM_SUCCESS)
10469+                       ietfsha1Sig.assign(m_sSig);
10470+               else
10471+                       return nRet;
10472+#endif
10473+       }
10474+       m_sReturnedSig.assign(allmansha1sig);
10475+       if (!ietfsha1Sig.empty()) {
10476+               if (!m_sReturnedSig.empty())
10477+                       m_sReturnedSig.append("\n");
10478+               m_sReturnedSig.append(ietfsha1Sig);
10479+       }
10480+#ifdef HAVE_EVP_SHA256
10481+       if (!ietfsha256Sig.empty()) {
10482+               if (!m_sReturnedSig.empty())
10483+                       m_sReturnedSig.append("\n");
10484+               m_sReturnedSig.append(ietfsha256Sig);
10485+       }
10486+#endif
10487+       m_bReturnedSigAssembled = true;
10488+       return DKIM_SUCCESS;
10489+}
10490+
10491+void
10492+getversion_dkimsign_cpp()
10493+{
10494+       static char    *x = (char *) "$Id: dkimsign.cpp,v 1.17 2020-04-10 21:36:20+05:30 Cprogrammer Exp mbhangui $";
10495+
10496+       x++;
10497+}
10498diff -ruN ../netqmail-1.06-original/dkimsign.h netqmail-1.06/dkimsign.h
10499--- ../netqmail-1.06-original/dkimsign.h        1970-01-01 01:00:00.000000000 +0100
10500+++ netqmail-1.06/dkimsign.h    2020-04-10 18:39:39.483427628 +0200
10501@@ -0,0 +1,115 @@
10502+/*
10503+ * $Log: dkimsign.h,v $
10504+ * Revision 1.5  2019-06-26 19:09:07+05:30  Cprogrammer
10505+ * added sBouncedAddr variable for X-Bounced-Address header added by qmail-send for bounces
10506+ *
10507+ * Revision 1.4  2017-09-05 10:59:20+05:30  Cprogrammer
10508+ * removed compiler warnings
10509+ *
10510+ * Revision 1.3  2017-08-09 22:03:09+05:30  Cprogrammer
10511+ * initialized EVP_MD_CTX variables
10512+ *
10513+ * Revision 1.2  2017-08-08 23:50:33+05:30  Cprogrammer
10514+ * openssl 1.1.0 port
10515+ *
10516+ * Revision 1.1  2009-04-16 10:34:02+05:30  Cprogrammer
10517+ * Initial revision
10518+ *
10519+ *
10520+ *  Copyright 2005 Alt-N Technologies, Ltd.
10521+ *
10522+ *  Licensed under the Apache License, Version 2.0 (the "License");
10523+ *  you may not use this file except in compliance with the License.
10524+ *  You may obtain a copy of the License at
10525+ *
10526+ *      http://www.apache.org/licenses/LICENSE-2.0
10527+ *
10528+ *  This code incorporates intellectual property owned by Yahoo! and licensed
10529+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
10530+ *
10531+ *  Unless required by applicable law or agreed to in writing, software
10532+ *  distributed under the License is distributed on an "AS IS" BASIS,
10533+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10534+ *  See the License for the specific language governing permissions and
10535+ *  limitations under the License.
10536+ *
10537+ */
10538+
10539+#ifndef DKIMSIGN_H
10540+#define DKIMSIGN_H
10541+
10542+#include "dkimbase.h"
10543+
10544+class           CDKIMSign:public CDKIMBase {
10545+public:
10546+
10547+       CDKIMSign();
10548+       ~CDKIMSign();
10549+       int             Init(DKIMSignOptions * pOptions);
10550+       int             GetSig(char *szPrivKey, char *szSignature, unsigned int nSigLength);
10551+       int             GetSig2(char *szPrivKey, char **pszSignature);
10552+       virtual int     ProcessHeaders(void);
10553+       virtual int     ProcessBody(char *szBuffer, int nBufLength, bool bEOF);
10554+       enum CKDKIMConstants { OptimalHeaderLineLength = 65 };
10555+       char           *DKIM_CALL GetDomain(void);
10556+
10557+protected:
10558+       void            Hash(const char *szBuffer, int nBufLength, bool bHdr, bool bAllmanOnly = false);
10559+       bool            SignThisTag(const string & sTag);
10560+       void            GetHeaderParams(const string & sHdr);
10561+       void            ProcessHeader(const string & sHdr);
10562+       bool            ParseFromAddress(void);
10563+       void            InitSig(void);
10564+       void            AddTagToSig(char *Tag, const string & sValue, char cbrk, bool bFold);
10565+       void            AddTagToSig(char *Tag, unsigned long nValue);
10566+       void            AddInterTagSpace(int nSizeOfNextTag);
10567+       void            AddFoldedValueToSig(const string & sValue, char cbrk);
10568+       bool            IsRequiredHeader(const string & sTag);
10569+       int             ConstructSignature(char *szPrivKey, bool bUseIetfBodyHash, bool bUseSha256);
10570+       int             AssembleReturnedSig(char *szPrivKey);
10571+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10572+       EVP_MD_CTX     *m_Hdr_ietf_sha1ctx = NULL;      /* the header hash for ietf sha1  */
10573+       EVP_MD_CTX     *m_Bdy_ietf_sha1ctx = NULL;      /* the body hash for ietf sha1  */
10574+#ifdef HAVE_EVP_SHA256
10575+       EVP_MD_CTX     *m_Hdr_ietf_sha256ctx = NULL;    /* the header hash for ietf sha256 */
10576+       EVP_MD_CTX     *m_Bdy_ietf_sha256ctx = NULL;    /* the body hash for ietf sha256 */
10577+#endif
10578+       EVP_MD_CTX     *m_allman_sha1ctx = NULL;        /* the hash for allman sha1  */
10579+#else
10580+       EVP_MD_CTX      m_Hdr_ietf_sha1ctx;     /* the header hash for ietf sha1  */
10581+       EVP_MD_CTX      m_Bdy_ietf_sha1ctx;     /* the body hash for ietf sha1  */
10582+#ifdef HAVE_EVP_SHA256
10583+       EVP_MD_CTX      m_Hdr_ietf_sha256ctx;   /* the header hash for ietf sha256 */
10584+       EVP_MD_CTX      m_Bdy_ietf_sha256ctx;   /* the body hash for ietf sha256 */
10585+#endif
10586+       EVP_MD_CTX      m_allman_sha1ctx;       /* the hash for allman sha1  */
10587+#endif
10588+       int             m_Canon;        // canonization method
10589+       int             m_EmptyLineCount;
10590+       string          hParam;
10591+       string          sFrom;
10592+       string          sSender;
10593+       string          sSelector;
10594+       string          sReturnPath;
10595+       string          sBouncedAddr; /*- used for bounces */
10596+       string          sDomain;
10597+       string          sIdentity;      // for i= tag, if empty tag will not be included in sig
10598+       string          sRequiredHeaders;
10599+       bool            m_IncludeBodyLengthTag;
10600+       int             m_nBodyLength;
10601+       time_t          m_ExpireTime;
10602+       int             m_nIncludeTimeStamp;    // 0 = don't include t= tag, 1 = include t= tag
10603+       int             m_nIncludeQueryMethod;  // 0 = don't include q= tag, 1 = include q= tag
10604+       int             m_nHash;        // use one of the DKIM_HASH_xx constants here
10605+       int             m_nIncludeCopiedHeaders;        // 0 = don't include z= tag, 1 = include z= tag
10606+       int             m_nIncludeBodyHash;     // 0 = calculate sig using draft 0, 1 = include bh= tag and
10607+       // use new signature computation algorithm
10608+       DKIMHEADERCALLBACK m_pfnHdrCallback;
10609+       string          m_sSig;
10610+       int             m_nSigPos;
10611+       string          m_sReturnedSig;
10612+       bool            m_bReturnedSigAssembled;
10613+       string          m_sCopiedHeaders;
10614+};
10615+
10616+#endif /*- DKIMSIGN_H */
10617diff -ruN ../netqmail-1.06-original/dkimverify.cpp netqmail-1.06/dkimverify.cpp
10618--- ../netqmail-1.06-original/dkimverify.cpp    1970-01-01 01:00:00.000000000 +0100
10619+++ netqmail-1.06/dkimverify.cpp        2019-06-19 09:47:20.017583230 +0200
10620@@ -0,0 +1,1303 @@
10621+/*
10622+ * $Log: dkimverify.cpp,v $
10623+ * Revision 1.23  2019-05-22 11:29:09+05:30  Cprogrammer
10624+ * fix for 32 bit systems where time_t is 4 bytes & encounters year 2038 issue
10625+ *
10626+ * Revision 1.22  2019-05-21 22:27:17+05:30  Cprogrammer
10627+ * increased buffer size
10628+ *
10629+ * Revision 1.21  2019-02-17 11:32:05+05:30  Cprogrammer
10630+ * made scope of sFromDomain static
10631+ *
10632+ * Revision 1.20  2018-12-14 11:05:20+05:30  Cprogrammer
10633+ * fixed 'conversion from 'int' to 'char' inside { }” for cross compiling on arm
10634+ *
10635+ * Revision 1.19  2018-08-08 23:56:27+05:30  Cprogrammer
10636+ * changed comment style
10637+ *
10638+ * Revision 1.18  2017-09-05 11:00:33+05:30  Cprogrammer
10639+ * removed extra whitespace
10640+ *
10641+ * Revision 1.17  2017-09-03 14:02:04+05:30  Cprogrammer
10642+ * call EVP_MD_CTX_init() only once
10643+ *
10644+ * Revision 1.16  2017-09-01 12:46:05+05:30  Cprogrammer
10645+ * removed dkimd2i_PUBKEY function
10646+ *
10647+ * Revision 1.15  2017-08-31 17:04:34+05:30  Cprogrammer
10648+ * replaced d2i_PUBKEY() with dkimd2i_PUBKEY() to avoid SIGSEGV on X509_PUBKEY_free()
10649+ *
10650+ * Revision 1.14  2017-08-09 21:59:39+05:30  Cprogrammer
10651+ * fixed segmentation fault. Use EVP_MD_CTX_reset() instead of EVP_MD_CTX_free()
10652+ *
10653+ * Revision 1.13  2017-08-08 23:50:41+05:30  Cprogrammer
10654+ * openssl 1.1.0 port
10655+ *
10656+ * Revision 1.12  2017-05-23 09:23:45+05:30  Cprogrammer
10657+ * use strtok_r instead of strtok() for thread safe operation
10658+ *
10659+ * Revision 1.11  2016-03-01 16:24:00+05:30  Cprogrammer
10660+ * reverse value of m_SubjectIsRequired
10661+ *
10662+ * Revision 1.10  2015-12-15 16:05:00+05:30  Cprogrammer
10663+ * fixed issue with time comparision. Use time_t for time variables
10664+ *
10665+ * Revision 1.9  2011-06-04 10:05:01+05:30  Cprogrammer
10666+ * added signature and identity domain information to
10667+ *     DKIMVerifyDetails structure
10668+ *
10669+ * Revision 1.8  2011-06-04 09:37:13+05:30  Cprogrammer
10670+ * added AllowUnsignedFromHeaders
10671+ *
10672+ * Revision 1.7  2009-06-11 13:58:34+05:30  Cprogrammer
10673+ * port for DARWIN
10674+ *
10675+ * Revision 1.6  2009-05-31 21:09:29+05:30  Cprogrammer
10676+ * changed cast
10677+ *
10678+ * Revision 1.5  2009-03-27 20:19:58+05:30  Cprogrammer
10679+ * added ADSP code
10680+ *
10681+ * Revision 1.4  2009-03-26 15:12:05+05:30  Cprogrammer
10682+ * added ADSP code
10683+ *
10684+ * Revision 1.3  2009-03-25 08:38:20+05:30  Cprogrammer
10685+ * fixed indentation
10686+ *
10687+ * Revision 1.2  2009-03-21 11:57:40+05:30  Cprogrammer
10688+ * fixed indentation
10689+ *
10690+ * Revision 1.1  2009-03-21 08:43:13+05:30  Cprogrammer
10691+ * Initial revision
10692+ *
10693+ *
10694+ *  Copyright 2005 Alt-N Technologies, Ltd.
10695+ *
10696+ *  Licensed under the Apache License, Version 2.0 (the "License");
10697+ *  you may not use this file except in compliance with the License.
10698+ *  You may obtain a copy of the License at
10699+ *
10700+ *      http://www.apache.org/licenses/LICENSE-2.0
10701+ *
10702+ *  This code incorporates intellectual property owned by Yahoo! and licensed
10703+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
10704+ *
10705+ *  Unless required by applicable law or agreed to in writing, software
10706+ *  distributed under the License is distributed on an "AS IS" BASIS,
10707+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10708+ *  See the License for the specific language governing permissions and
10709+ *  limitations under the License.
10710+ *
10711+ */
10712+#ifdef HAVE_CONFIG_H
10713+#include "config.h"
10714+#endif
10715+#include "time_t_size.h"
10716+#define _strnicmp strncasecmp
10717+#define _stricmp strcasecmp
10718+#include <string.h>
10719+#include <ctype.h>
10720+#include <assert.h>
10721+#include <vector>
10722+#include <algorithm>
10723+#include "dkim.h"
10724+#include "dkimverify.h"
10725+#include "dkimdns.h"
10726+
10727+#define MAX_SIGNATURES 10              /*- maximum number of DKIM signatures to process in a message */
10728+
10729+SignatureInfo::SignatureInfo(bool s)
10730+{
10731+       VerifiedBodyCount = 0;
10732+       UnverifiedBodyCount = 0;
10733+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10734+       if (m_Hdr_ctx)
10735+               EVP_MD_CTX_init(m_Hdr_ctx);
10736+       else
10737+               m_Hdr_ctx = EVP_MD_CTX_new();
10738+       if (m_Bdy_ctx)
10739+               EVP_MD_CTX_init(m_Bdy_ctx);
10740+       else
10741+               m_Bdy_ctx = EVP_MD_CTX_new();
10742+#else
10743+       EVP_MD_CTX_init(&m_Hdr_ctx);
10744+       EVP_MD_CTX_init(&m_Bdy_ctx);
10745+#endif
10746+       m_pSelector = NULL;
10747+       Status = DKIM_SUCCESS;
10748+       m_nHash = 0;
10749+       EmptyLineCount = 0;
10750+       m_SaveCanonicalizedData = s;
10751+}
10752+
10753+SignatureInfo::~SignatureInfo()
10754+{
10755+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10756+       if (m_Hdr_ctx)
10757+               EVP_MD_CTX_reset(m_Hdr_ctx);
10758+       if (m_Bdy_ctx)
10759+               EVP_MD_CTX_reset(m_Bdy_ctx);
10760+#else
10761+       EVP_MD_CTX_cleanup(&m_Hdr_ctx);
10762+       EVP_MD_CTX_cleanup(&m_Bdy_ctx);
10763+#endif
10764+}
10765+
10766+inline          bool
10767+isswsp(char ch)
10768+{
10769+       return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
10770+}
10771+
10772+
10773+////////////////////////////////////////////////////////////////////////////////
10774+//
10775+// Parse a DKIM tag-list.  Returns true for success
10776+//
10777+////////////////////////////////////////////////////////////////////////////////
10778+bool
10779+ParseTagValueList(char *tagvaluelist, const char *wanted[], char *values[])
10780+{
10781+       char           *s = tagvaluelist;
10782+
10783+       for (;;) {
10784+               /* skip whitespace */
10785+               while (isswsp(*s))
10786+                       s++;
10787+               /* if at the end of the string, return success.  note: this allows a list with no entries */
10788+               if (*s == '\0')
10789+                       return true;
10790+               /*- get tag name -*/
10791+               if (!isalpha(*s))
10792+                       return false;
10793+               char           *tag = s;
10794+               do {
10795+                       s++;
10796+               } while (isalnum(*s) || *s == '-');
10797+               char           *endtag = s;
10798+               /*- skip whitespace before equals -*/
10799+               while (isswsp(*s))
10800+                       s++;
10801+               /*- next character must be equals -*/
10802+               if (*s != '=')
10803+                       return false;
10804+               s++;
10805+               /*- null-terminate tag name -*/
10806+               *endtag = '\0';
10807+               /*- skip whitespace after equals -*/
10808+               while (isswsp(*s))
10809+                       s++;
10810+               /*- get tag value -*/
10811+               char           *value = s;
10812+               while (*s != ';' && ((*s == '\t' || *s == '\r' || *s == '\n') || (*s >= ' ' && *s <= '~')))
10813+                       s++;
10814+               char           *e = s;
10815+               /*- make sure the next character is the null terminator (which means we're done) or a semicolon (not done) -*/
10816+               bool            done = false;
10817+               if (*s == '\0')
10818+                       done = true;
10819+               else {
10820+                       if (*s != ';')
10821+                               return false;
10822+                       s++;
10823+               }
10824+               /*- skip backwards past any trailing whitespace -*/
10825+               while (e > value && isswsp(e[-1]))
10826+                       e--;
10827+               /*- null-terminate tag value -*/
10828+               *e = '\0';
10829+               /*- check to see if we want this tag -*/
10830+               for (unsigned i = 0; wanted[i] != NULL; i++) {
10831+                       if (strcmp(wanted[i], tag) == 0) {
10832+                               /*- return failure if we already have a value for this tag (duplicates not allowed) -*/
10833+                               if (values[i] != NULL)
10834+                                       return false;
10835+                               values[i] = value;
10836+                               break;
10837+                       }
10838+               }
10839+               if (done)
10840+                       return true;
10841+       }
10842+}
10843+
10844+/*- Convert hex char to value (0-15) -*/
10845+char
10846+tohex(char ch)
10847+{
10848+       if (ch >= '0' && ch <= '9')
10849+               return (ch - '0');
10850+       else
10851+       if (ch >= 'A' && ch <= 'F')
10852+               return (ch - 'A' + 10);
10853+       else
10854+       if (ch >= 'a' && ch <= 'f')
10855+               return (ch - 'a' + 10);
10856+       else {
10857+               assert(0);
10858+               return 0;
10859+       }
10860+}
10861+
10862+
10863+////////////////////////////////////////////////////////////////////////////////
10864+//
10865+// Decode quoted printable string in-place
10866+//
10867+////////////////////////////////////////////////////////////////////////////////
10868+void
10869+DecodeQuotedPrintable(char *ptr)
10870+{
10871+       char           *s = ptr;
10872+       while (*s != '\0' && *s != '=')
10873+               s++;
10874+       if (*s == '\0')
10875+               return;
10876+       char           *d = s;
10877+       do {
10878+               if (*s == '=' && isxdigit(s[1]) && isxdigit(s[2])) {
10879+                       *d++ = (tohex(s[1]) << 4) | tohex(s[2]);
10880+                       s += 3;
10881+               } else {
10882+                       *d++ = *s++;
10883+               }
10884+       } while (*s != '\0');
10885+       *d = '\0';
10886+}
10887+
10888+
10889+////////////////////////////////////////////////////////////////////////////////
10890+//
10891+// Decode base64 string in-place, returns number of bytes output
10892+//
10893+////////////////////////////////////////////////////////////////////////////////
10894+unsigned
10895+DecodeBase64(char *ptr)
10896+{
10897+       static const signed char base64_table[256] =
10898+               { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
10899+               -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,
10900+               -1, -1,
10901+               -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,
10902+               -1, -1,
10903+               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,
10904+               -1, -1,
10905+               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
10906+               -1, -1,
10907+               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
10908+               -1, -1,
10909+               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
10910+               -1, -1,
10911+               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
10912+       };
10913+       unsigned char  *s = (unsigned char *) ptr;
10914+       unsigned char  *d = (unsigned char *) ptr;
10915+       unsigned        b64accum = 0;
10916+       unsigned char   b64shift = 0;
10917+       while (*s != '\0') {
10918+               unsigned char value = base64_table[*s++];
10919+               if ((signed char) value >= 0) {
10920+                       b64accum = (b64accum << 6) | value;
10921+                       b64shift += 6;
10922+                       if (b64shift >= 8) {
10923+                               b64shift -= 8;
10924+                               *d++ = (b64accum >> b64shift);
10925+                       }
10926+               }
10927+       }
10928+       return (char *) d - ptr;
10929+}
10930+
10931+////////////////////////////////////////////////////////////////////////////////
10932+//
10933+// Match a string with a pattern (used for g= value)
10934+// Supports a single, optional "*" wildcard character.
10935+//
10936+////////////////////////////////////////////////////////////////////////////////
10937+bool
10938+WildcardMatch(const char *p, const char *s)
10939+{
10940+       /*- special case: An empty "g=" value never matches any addresses -*/
10941+       if (*p == '\0')
10942+               return false;
10943+       const char     *wildcard = strchr(p, '*');
10944+       if (wildcard == NULL)
10945+               return strcmp(s, p) == 0;
10946+       else {
10947+               unsigned beforewildcardlen = wildcard - p;
10948+               unsigned afterwildcardlen = strlen(wildcard + 1);
10949+               unsigned slen = strlen(s);
10950+               return (slen >= beforewildcardlen + afterwildcardlen) && (strncmp(s, p, beforewildcardlen) == 0)
10951+                       && strcmp(s + slen - afterwildcardlen, wildcard + 1) == 0;
10952+       }
10953+}
10954+
10955+
10956+////////////////////////////////////////////////////////////////////////////////
10957+//
10958+// Parse addresses from a string.  Returns true if at least one address found
10959+//
10960+////////////////////////////////////////////////////////////////////////////////
10961+bool
10962+ParseAddresses(string str, vector < string > &Addresses)
10963+{
10964+       char           *s = (char *) str.c_str();
10965+       while (*s != '\0') {
10966+               char           *start = s;
10967+               char           *from = s;
10968+               char           *to = s;
10969+               char           *lt = NULL;      /*- pointer to less than character (<) which starts the address if found */
10970+               while (*from != '\0') {
10971+                       if (*from == '(') {
10972+                               /*- skip over comment -*/
10973+                               from++;
10974+                               for (int depth = 1; depth != 0; from++) {
10975+                                       if (*from == '\0')
10976+                                               break;
10977+                                       else
10978+                                       if (*from == '(')
10979+                                               depth++;
10980+                                       else
10981+                                       if (*from == ')')
10982+                                               depth--;
10983+                                       else
10984+                                       if (*from == '\\' && from[1] != '\0')
10985+                                               from++;
10986+                               }
10987+                       }
10988+                       else
10989+                       if (*from == ')') /*- ignore closing parenthesis outside of comment -*/
10990+                               from++;
10991+                       else
10992+                       if (*from == ',' || *from == ';') {
10993+                               /*- comma/selicolon ends the address -*/
10994+                               from++;
10995+                               break;
10996+                       }
10997+                       else
10998+                       if (*from == ' ' || *from == '\t' || *from == '\r' || *from == '\n') /*- ignore whitespace -*/
10999+                               from++;
11000+                       else
11001+                       if (*from == '"') {
11002+                               /*- copy the contents of a quoted string -*/
11003+                               from++;
11004+                               while (*from != '\0') {
11005+                                       if (*from == '"') {
11006+                                               from++;
11007+                                               break;
11008+                                       }
11009+                                       else
11010+                                       if (*from == '\\' && from[1] != '\0')
11011+                                               *to++ = *from++;
11012+                                       *to++ = *from++;
11013+                               }
11014+                       }
11015+                       else
11016+                       if (*from == '\\' && from[1] != '\0') {
11017+                               /*- copy quoted-pair -*/
11018+                               *to++ = *from++;
11019+                               *to++ = *from++;
11020+                       } else {
11021+                               /*- copy any other char -*/
11022+                               *to = *from++;
11023+                               // save pointer to '<' for later...
11024+                               if (*to == '<')
11025+                                       lt = to;
11026+                               to++;
11027+                       }
11028+               }
11029+               *to = '\0';
11030+               /*- if there's < > get what's inside -*/
11031+               if (lt != NULL) {
11032+                       start = lt + 1;
11033+                       char           *gt = strchr(start, '>');
11034+                       if (gt != NULL)
11035+                               *gt = '\0';
11036+               } else {
11037+                       /*- look for and strip group name -*/
11038+                       char           *colon = strchr(start, ':');
11039+                       if (colon != NULL) {
11040+                               char           *at = strchr(start, '@');
11041+                               if (at == NULL || colon < at)
11042+                                       start = colon + 1;
11043+                       }
11044+               }
11045+               if (*start != '\0' && strchr(start, '@') != NULL)
11046+                       Addresses.push_back(start);
11047+               s = from;
11048+       }
11049+       return !Addresses.empty();
11050+}
11051+
11052+CDKIMVerify::CDKIMVerify()
11053+{
11054+       m_pfnSelectorCallback = NULL;
11055+       m_pfnPracticesCallback = NULL;
11056+       m_HonorBodyLengthTag = false;
11057+       m_CheckPractices = false;
11058+       m_Accept3ps = false;
11059+       m_SubjectIsRequired = true;
11060+       m_SaveCanonicalizedData = false;
11061+       m_AllowUnsignedFromHeaders = false;
11062+}
11063+
11064+CDKIMVerify::~CDKIMVerify()
11065+{
11066+}
11067+
11068+/*- Init - save the options -*/
11069+int
11070+CDKIMVerify::Init(DKIMVerifyOptions *pOptions)
11071+{
11072+       int             nRet = CDKIMBase::Init();
11073+       m_pfnSelectorCallback = pOptions->pfnSelectorCallback;
11074+       m_pfnPracticesCallback = pOptions->pfnPracticesCallback;
11075+
11076+       m_HonorBodyLengthTag = pOptions->nHonorBodyLengthTag != 0;
11077+       m_CheckPractices = pOptions->nCheckPractices != 0;
11078+       m_SubjectIsRequired = pOptions->nSubjectRequired != 0;
11079+       m_Accept3ps = pOptions->nAccept3ps != 0;                //TBS(Luc)
11080+       m_SaveCanonicalizedData = pOptions->nSaveCanonicalizedData != 0;
11081+       m_AllowUnsignedFromHeaders = pOptions->nAllowUnsignedFromHeaders != 0;
11082+       return nRet;
11083+}
11084+
11085+/*- GetResults - return the pass/fail/neutral verification result -*/
11086+int
11087+CDKIMVerify::GetResults(int *sCount, int *sSize)
11088+{
11089+       ProcessFinal();
11090+       unsigned int    SuccessCount = 0;
11091+       int             TestingFailures = 0;
11092+       int             RealFailures = 0;
11093+       list <string>   SuccessfulDomains;      /* can contain duplicates */
11094+       string          sFromDomain; /*- get the From address's domain if we might need it -*/
11095+
11096+       for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) {
11097+               if (i->Status == DKIM_SUCCESS) {
11098+                       if (!i->BodyHashData.empty()) { /*- check the body hash -*/
11099+                               unsigned char   md[EVP_MAX_MD_SIZE];
11100+                               unsigned        len = 0;
11101+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
11102+                               int             res = EVP_DigestFinal(i->m_Bdy_ctx, md, &len);
11103+#else
11104+                               int             res = EVP_DigestFinal(&i->m_Bdy_ctx, md, &len);
11105+#endif
11106+                               if (!res || len != i->BodyHashData.length() || memcmp(i->BodyHashData.data(), md, len) != 0) {
11107+                                       /* body hash mismatch */
11108+                                       if (i->m_pSelector->Testing) { /* if the selector is in testing mode... */
11109+                                               i->Status = DKIM_SIGNATURE_BAD_BUT_TESTING;     /* todo: make a new error code for this? */
11110+                                               TestingFailures++;
11111+                                       } else {
11112+                                               i->Status = DKIM_BODY_HASH_MISMATCH;
11113+                                               RealFailures++;
11114+                                       }
11115+                                       continue;
11116+                               }
11117+                       } else {
11118+                               /* hash CRLF separating the body from the signature */
11119+                               i->Hash("\r\n", 2);
11120+                       }
11121+                       /*- check the header hash -*/
11122+                       string          sSignedSig = i->Header;
11123+                       string          sSigValue = sSignedSig.substr(sSignedSig.find(':') + 1);
11124+                       static const char *tags[] = { "b", NULL };
11125+                       char           *values[sizeof (tags) / sizeof (tags[0])] = { NULL };
11126+                       char           *pSigValue = (char *) sSigValue.c_str();
11127+                       if (ParseTagValueList(pSigValue, tags, values) && values[0] != NULL) {
11128+                               sSignedSig.erase(15 + values[0] - pSigValue, strlen(values[0]));
11129+                       }
11130+                       if (i->HeaderCanonicalization == DKIM_CANON_RELAXED) {
11131+                               sSignedSig = RelaxHeader(sSignedSig);
11132+                       } else
11133+                       if (i->HeaderCanonicalization == DKIM_CANON_NOWSP) {
11134+                               RemoveSWSP(sSignedSig);
11135+                               /* convert "DKIM-Signature" to lower case */
11136+                               sSignedSig.replace(0, 14, "dkim-signature", 14);
11137+                       }
11138+                       i->Hash(sSignedSig.c_str(), sSignedSig.length());
11139+                       assert(i->m_pSelector != NULL);
11140+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
11141+                       int             res = EVP_VerifyFinal(i->m_Hdr_ctx, (unsigned char *) i->SignatureData.data(),
11142+                                                       i->SignatureData.length(), i->m_pSelector->PublicKey);
11143+#else
11144+                       int             res = EVP_VerifyFinal(&i->m_Hdr_ctx, (unsigned char *) i->SignatureData.data(),
11145+                                                       i->SignatureData.length(), i->m_pSelector->PublicKey);
11146+#endif
11147+                       if (res == 1) {
11148+                               if (i->UnverifiedBodyCount == 0)
11149+                                       i->Status = DKIM_SUCCESS;
11150+                               else
11151+                                       i->Status = DKIM_SUCCESS_BUT_EXTRA;
11152+                               SuccessCount++;
11153+                               SuccessfulDomains.push_back(i->Domain);
11154+                       } else {
11155+                               /* if the selector is in testing mode... */
11156+                               if (i->m_pSelector->Testing) {
11157+                                       i->Status = DKIM_SIGNATURE_BAD_BUT_TESTING;
11158+                                       TestingFailures++;
11159+                               } else {
11160+                                       i->Status = DKIM_SIGNATURE_BAD;
11161+                                       RealFailures++;
11162+                               }
11163+                       }
11164+               } else
11165+               if (i->Status == DKIM_SELECTOR_GRANULARITY_MISMATCH
11166+                       || i->Status == DKIM_SELECTOR_ALGORITHM_MISMATCH
11167+                       || i->Status == DKIM_SELECTOR_KEY_REVOKED) {
11168+                       /*- treat these as failures -*/
11169+                       /*- todo: maybe see if the selector is in testing mode? -*/
11170+                       RealFailures++;
11171+               }
11172+       }
11173+       if (SuccessCount > 0 || m_CheckPractices) {
11174+               for (list < string >::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) {
11175+                       if (_strnicmp(i->c_str(), "From", 4) == 0) {
11176+                               /*- skip over whitespace between the header name and : -*/
11177+                               const char     *s = i->c_str() + 4;
11178+                               while (*s == ' ' || *s == '\t')
11179+                                       s++;
11180+                               if (*s == ':') {
11181+                                       vector <string> Addresses;
11182+                                       if (ParseAddresses(s + 1, Addresses)) {
11183+                                               unsigned atpos = Addresses[0].find('@');
11184+                                               sFromDomain = Addresses[0].substr(atpos + 1);
11185+                                               break;
11186+                                       }
11187+                               }
11188+                       }
11189+               }
11190+       }
11191+       /*-
11192+        * if a signature from the From domain verified successfully,
11193+        * return success now without checking the sender signing practices
11194+        */
11195+       if (SuccessCount > 0 && !sFromDomain.empty()) {
11196+               for (list < string >::iterator i = SuccessfulDomains.begin(); i != SuccessfulDomains.end(); ++i) {
11197+                       /* see if the successful domain is the same as or a parent of the From domain */
11198+                       if (i->length() > sFromDomain.length())
11199+                               continue;
11200+                       if (_stricmp(i->c_str(), sFromDomain.c_str() + sFromDomain.length() - i->length()) != 0)
11201+                               continue;
11202+                       if (i->length() == sFromDomain.length() || sFromDomain.c_str()[sFromDomain.length() - i->length() - 1] == '.')
11203+                               return ((SuccessCount == Signatures.size()) ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
11204+               }
11205+       }
11206+       if (!m_Accept3ps)
11207+               return DKIM_NEUTRAL;
11208+       *sCount = SuccessCount;
11209+       *sSize = Signatures.size();
11210+       return DKIM_3PS_SIGNATURE;
11211+}
11212+
11213+////////////////////////////////////////////////////////////////////////////////
11214+//
11215+// Hash - update the hash
11216+//
11217+////////////////////////////////////////////////////////////////////////////////
11218+void
11219+SignatureInfo::Hash(const char *szBuffer, unsigned nBufLength, bool IsBody)
11220+{
11221+
11222+       if (IsBody && BodyLength != -1) {
11223+               VerifiedBodyCount += nBufLength;
11224+               if (VerifiedBodyCount > BodyLength) {
11225+                       nBufLength = BodyLength - (VerifiedBodyCount - nBufLength);
11226+                       UnverifiedBodyCount += VerifiedBodyCount - BodyLength;
11227+                       VerifiedBodyCount = BodyLength;
11228+                       if (nBufLength == 0)
11229+                               return;
11230+               }
11231+       }
11232+       if (IsBody && !BodyHashData.empty()) {
11233+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
11234+               EVP_DigestUpdate(m_Bdy_ctx, szBuffer, nBufLength);
11235+#else
11236+               EVP_DigestUpdate(&m_Bdy_ctx, szBuffer, nBufLength);
11237+#endif
11238+       } else {
11239+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
11240+               EVP_VerifyUpdate(m_Hdr_ctx, szBuffer, nBufLength);
11241+#else
11242+               EVP_VerifyUpdate(&m_Hdr_ctx, szBuffer, nBufLength);
11243+#endif
11244+       }
11245+       if (m_SaveCanonicalizedData) {
11246+               CanonicalizedData.append(szBuffer, nBufLength);
11247+       }
11248+}
11249+
11250+
11251+/*- ProcessHeaders - Look for DKIM-Signatures and start processing them -*/
11252+int
11253+CDKIMVerify::ProcessHeaders(void)
11254+{
11255+
11256+       /*- look for DKIM-Signature header(s) -*/
11257+       for (list < string >::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) {
11258+               if (_strnicmp(i->c_str(), "DKIM-Signature", 14) == 0) {
11259+                       /*- skip over whitespace between the header name and : -*/
11260+                       const char     *s = i->c_str() + 14;
11261+                       while (*s == ' ' || *s == '\t')
11262+                               s++;
11263+                       if (*s == ':') {
11264+                               // found
11265+                               SignatureInfo   sig(m_SaveCanonicalizedData);
11266+                               sig.Status = ParseDKIMSignature(*i, sig);
11267+                               Signatures.push_back(sig);
11268+                               if (Signatures.size() >= MAX_SIGNATURES)
11269+                                       break;
11270+                       }
11271+               }
11272+       }
11273+       if (Signatures.empty())
11274+               return DKIM_NO_SIGNATURES;
11275+       bool            ValidSigFound = false;
11276+       for (list < SignatureInfo >::iterator s = Signatures.begin(); s != Signatures.end(); ++s) {
11277+               SignatureInfo &sig = *s;
11278+               if (sig.Status != DKIM_SUCCESS)
11279+                       continue;
11280+               SelectorInfo &sel = GetSelector(sig.Selector, sig.Domain);
11281+               sig.m_pSelector = &sel;
11282+               if (sel.Status != DKIM_SUCCESS) {
11283+                       sig.Status = sel.Status;
11284+                       return (sig.Status);
11285+               } else {
11286+                       /*- check the granularity -*/
11287+                       if (!WildcardMatch(sel.Granularity.c_str(), sig.IdentityLocalPart.c_str()))
11288+                               sig.Status = DKIM_SELECTOR_GRANULARITY_MISMATCH;        /* this error causes the signature to fail */
11289+                       /*- check the hash algorithm -*/
11290+#ifdef HAVE_EVP_SHA256
11291+                       if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1) || (sig.m_nHash == DKIM_HASH_SHA256 && !sel.AllowSHA256))
11292+#else
11293+                       if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1))
11294+#endif
11295+                               sig.Status = DKIM_SELECTOR_ALGORITHM_MISMATCH;  /* causes signature to fail */
11296+                       /*- check for same domain -*/
11297+                       if (sel.SameDomain && _stricmp(sig.Domain.c_str(), sig.IdentityDomain.c_str()) != 0)
11298+                               sig.Status = DKIM_BAD_SYNTAX;
11299+               }
11300+               if (sig.Status != DKIM_SUCCESS)
11301+                       continue;
11302+               /*- initialize the hashes -*/
11303+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
11304+#ifdef HAVE_EVP_SHA256
11305+               if (sig.m_nHash == DKIM_HASH_SHA256) {
11306+                       EVP_VerifyInit(sig.m_Hdr_ctx, EVP_sha256());
11307+                       EVP_DigestInit(sig.m_Bdy_ctx, EVP_sha256());
11308+               } else {
11309+                       EVP_VerifyInit(sig.m_Hdr_ctx, EVP_sha1());
11310+                       EVP_DigestInit(sig.m_Bdy_ctx, EVP_sha1());
11311+               }
11312+#else
11313+               EVP_VerifyInit(sig.m_Hdr_ctx, EVP_sha1());
11314+               EVP_DigestInit(sig.m_Bdy_ctx, EVP_sha1());
11315+#endif
11316+#else
11317+#ifdef HAVE_EVP_SHA256
11318+               if (sig.m_nHash == DKIM_HASH_SHA256) {
11319+                       EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha256());
11320+                       EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha256());
11321+               } else {
11322+                       EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha1());
11323+                       EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha1());
11324+               }
11325+#else
11326+               EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha1());
11327+               EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha1());
11328+#endif
11329+#endif
11330+               /*- compute the hash of the header -*/
11331+               vector < list < string >::reverse_iterator > used;
11332+               for (vector < string >::iterator x = sig.SignedHeaders.begin(); x != sig.SignedHeaders.end(); ++x) {
11333+                       list < string >::reverse_iterator i;
11334+                       for (i = HeaderList.rbegin(); i != HeaderList.rend(); ++i) {
11335+                               if (_strnicmp(i->c_str(), x->c_str(), x->length()) == 0) {
11336+                                       /*- skip over whitespace between the header name and : -*/
11337+                                       const char     *s = i->c_str() + x->length();
11338+                                       while (*s == ' ' || *s == '\t')
11339+                                               s++;
11340+                                       if (*s == ':' && find(used.begin(), used.end(), i) == used.end())
11341+                                               break;
11342+                               }
11343+                       }
11344+                       if (i != HeaderList.rend()) {
11345+                               used.push_back(i);
11346+                               /*- hash this header -*/
11347+                               if (sig.HeaderCanonicalization == DKIM_CANON_SIMPLE)
11348+                                       sig.Hash(i->c_str(), i->length());
11349+                               else
11350+                               if (sig.HeaderCanonicalization == DKIM_CANON_RELAXED) {
11351+                                       string          sTemp = RelaxHeader(*i);
11352+                                       sig.Hash(sTemp.c_str(), sTemp.length());
11353+                               } else
11354+                               if (sig.HeaderCanonicalization == DKIM_CANON_NOWSP) {
11355+                                       string          sTemp = *i;
11356+                                       RemoveSWSP(sTemp);
11357+                                       /*- convert characters before ':' to lower case -*/
11358+                                       for (char *s = (char *)sTemp.c_str(); *s != '\0' && *s != ':'; s++) {
11359+                                               if (*s >= 'A' && *s <= 'Z')
11360+                                                       *s += 'a' - 'A';
11361+                                       }
11362+                                       sig.Hash(sTemp.c_str(), sTemp.length());
11363+                               }
11364+                               sig.Hash("\r\n", 2);
11365+                       }
11366+               }
11367+               if (sig.BodyHashData.empty()) /*- hash CRLF separating headers from body -*/
11368+                       sig.Hash("\r\n", 2);
11369+               if (!m_AllowUnsignedFromHeaders) {
11370+                       /*- make sure the message has no unsigned From headers -*/
11371+                       list<string>::reverse_iterator i;
11372+                       for( i = HeaderList.rbegin(); i != HeaderList.rend(); ++i ) {
11373+                               if( _strnicmp(i->c_str(), "From", 4 ) == 0 ) {
11374+                                       /*- skip over whitespace between the header name and : -*/
11375+                                       const char *s = i->c_str()+4;
11376+                                       while (*s == ' ' || *s == '\t')
11377+                                               s++;
11378+                                       if (*s == ':') {
11379+                                               if (find(used.begin(), used.end(), i) == used.end()) {
11380+                                                       /*- this From header was not signed -*/
11381+                                                       break;
11382+                                               }
11383+                                       }
11384+                               }
11385+                       }
11386+                       if (i != HeaderList.rend()) {
11387+                               /*- treat signature as invalid -*/
11388+                               sig.Status = DKIM_UNSIGNED_FROM;
11389+                               continue;
11390+                       }
11391+               }
11392+               ValidSigFound = true;
11393+       } /*- for (list < SignatureInfo >::iterator s = Signatures.begin(); s != Signatures.end(); ++s) { */
11394+       if (!ValidSigFound)
11395+               return DKIM_NO_VALID_SIGNATURES;
11396+       return DKIM_SUCCESS;
11397+}
11398+
11399+
11400+////////////////////////////////////////////////////////////////////////////////
11401+//
11402+// Strictly parse an unsigned integer.  Don't allow spaces, negative sign,
11403+// 0x prefix, etc.  Values greater than 2^32-1 are capped at 2^32-1
11404+//
11405+////////////////////////////////////////////////////////////////////////////////
11406+bool
11407+ParseUnsigned(const char *s, unsigned long *result)
11408+{
11409+       unsigned        temp = 0, last = 0;
11410+       bool            overflowed = false;
11411+
11412+       do {
11413+               if (*s < '0' || *s > '9')
11414+                       return false;   /*- returns false for an initial '\0' */
11415+               temp = temp * 10 + (*s - '0');
11416+               if (temp < last)
11417+                       overflowed = true;
11418+               last = temp;
11419+               s++;
11420+       } while (*s != '\0');
11421+       if (overflowed)
11422+               *result = -1;
11423+       else
11424+               *result = temp;
11425+       return true;
11426+}
11427+
11428+
11429+/*- ParseDKIMSignature - Parse a DKIM-Signature header field -*/
11430+int
11431+CDKIMVerify::ParseDKIMSignature(const string &sHeader, SignatureInfo &sig)
11432+{
11433+
11434+       /*- save header for later -*/
11435+       sig.Header = sHeader;
11436+       string          sValue = sHeader.substr(sHeader.find(':') + 1);
11437+       static const char *tags[] = { "v", "a", "b", "d", "h", "s", "c", "i", "l", "q", "t", "x", "bh", NULL };
11438+       char           *values[sizeof (tags) / sizeof (tags[0])] = { NULL };
11439+       char           *saveptr;
11440+
11441+       if (!ParseTagValueList((char *) sValue.c_str(), tags, values))
11442+               return DKIM_BAD_SYNTAX;
11443+       /*- check signature version -*/
11444+       if (values[0] != NULL) {
11445+               if (strcmp(values[0], "1") == 0 || strcmp(values[0], "0.5") == 0 || strcmp(values[0], "0.4") == 0
11446+                       || strcmp(values[0], "0.3") == 0 || strcmp(values[0], "0.2") == 0) {
11447+                       sig.Version = DKIM_SIG_VERSION_02_PLUS;
11448+               } else /*- unknown version -*/
11449+                       return DKIM_STAT_INCOMPAT;
11450+       } else {
11451+               /*-
11452+                * Note:  DKIM Interop 1 pointed out that v= is now required, but we do
11453+                * not enforce that in order to verify signatures made by older drafts.
11454+                * prior to 0.2, there MUST NOT have been a v=
11455+                * (optionally) support these signatures, for backwards compatibility
11456+                */
11457+               if (true) {
11458+                       sig.Version = DKIM_SIG_VERSION_PRE_02;
11459+               } else {
11460+                       return DKIM_BAD_SYNTAX;
11461+               }
11462+       }
11463+       /*- signature MUST have a=, b=, d=, h=, s= -*/
11464+       if (values[1] == NULL || values[2] == NULL || values[3] == NULL || values[4] == NULL || values[5] == NULL)
11465+               return DKIM_BAD_SYNTAX;
11466+       /*- algorithm can be "rsa-sha1" or "rsa-sha256" -*/
11467+       if (strcmp(values[1], "rsa-sha1") == 0) {
11468+               sig.m_nHash = DKIM_HASH_SHA1;
11469+       }
11470+#ifdef HAVE_EVP_SHA256
11471+       else
11472+       if (strcmp(values[1], "rsa-sha256") == 0)
11473+               sig.m_nHash = DKIM_HASH_SHA256;
11474+#endif
11475+       else
11476+               return DKIM_BAD_SYNTAX; /* todo: maybe create a new error code for unknown algorithm */
11477+       /*- make sure the signature data is not empty -*/
11478+       unsigned SigDataLen = DecodeBase64(values[2]);
11479+       if (SigDataLen == 0)
11480+               return DKIM_BAD_SYNTAX;
11481+       sig.SignatureData.assign(values[2], SigDataLen);
11482+       /*- check for body hash -*/
11483+       if (values[12] == NULL) {
11484+               /*- use the old single hash way for backwards compatibility -*/
11485+               if (sig.Version != DKIM_SIG_VERSION_PRE_02)
11486+                       return DKIM_BAD_SYNTAX;
11487+       } else {
11488+               unsigned BodyHashLen = DecodeBase64(values[12]);
11489+               if (BodyHashLen == 0)
11490+                       return DKIM_BAD_SYNTAX;
11491+               sig.BodyHashData.assign(values[12], BodyHashLen);
11492+       }
11493+       /*- domain must not be empty -*/
11494+       if (*values[3] == '\0')
11495+               return DKIM_BAD_SYNTAX;
11496+       sig.Domain = values[3];
11497+       /*- signed headers must not be empty (more verification is done later) -*/
11498+       if (*values[4] == '\0')
11499+               return DKIM_BAD_SYNTAX;
11500+       /*- selector must not be empty -*/
11501+       if (*values[5] == '\0')
11502+               return DKIM_BAD_SYNTAX;
11503+       sig.Selector = values[5];
11504+       /*- canonicalization -*/
11505+       if (values[6] == NULL) {
11506+               sig.HeaderCanonicalization = sig.BodyCanonicalization = DKIM_CANON_SIMPLE;
11507+       }
11508+       else
11509+       if (sig.Version == DKIM_SIG_VERSION_PRE_02 && strcmp(values[6], "nowsp") == 0) /*- for backwards compatibility -*/
11510+               sig.HeaderCanonicalization = sig.BodyCanonicalization = DKIM_CANON_NOWSP;
11511+       else {
11512+               char           *slash = strchr(values[6], '/');
11513+               if (slash != NULL)
11514+                       *slash = '\0';
11515+               if (strcmp(values[6], "simple") == 0)
11516+                       sig.HeaderCanonicalization = DKIM_CANON_SIMPLE;
11517+               else
11518+               if (strcmp(values[6], "relaxed") == 0)
11519+                       sig.HeaderCanonicalization = DKIM_CANON_RELAXED;
11520+               else
11521+                       return DKIM_BAD_SYNTAX;
11522+               if (slash == NULL || strcmp(slash + 1, "simple") == 0)
11523+                       sig.BodyCanonicalization = DKIM_CANON_SIMPLE;
11524+               else
11525+               if (strcmp(slash + 1, "relaxed") == 0)
11526+                       sig.BodyCanonicalization = DKIM_CANON_RELAXED;
11527+               else
11528+                       return DKIM_BAD_SYNTAX;
11529+       }
11530+       /*- identity -*/
11531+       if (values[7] == NULL) {
11532+               sig.IdentityLocalPart.erase();
11533+               sig.IdentityDomain = sig.Domain;
11534+       } else {
11535+               /*- quoted-printable decode the value -*/
11536+               DecodeQuotedPrintable(values[7]);
11537+               /*- must have a '@' separating the local part from the domain -*/
11538+               char           *at = strchr(values[7], '@');
11539+               if (at == NULL)
11540+                       return DKIM_BAD_SYNTAX;
11541+               *at = '\0';
11542+               char           *ilocalpart = values[7];
11543+               char           *idomain = at + 1;
11544+               /*- i= domain must be the same as or a subdomain of the d= domain -*/
11545+               int idomainlen = strlen(idomain);
11546+               int ddomainlen = strlen(values[3]);
11547+
11548+               /*- todo: maybe create a new error code for invalid identity domain -*/
11549+               if (idomainlen < ddomainlen)
11550+                       return DKIM_BAD_SYNTAX;
11551+               if (_stricmp(idomain + idomainlen - ddomainlen, values[3]) != 0)
11552+                       return DKIM_BAD_SYNTAX;
11553+               if (idomainlen > ddomainlen && idomain[idomainlen - ddomainlen - 1] != '.')
11554+                       return DKIM_BAD_SYNTAX;
11555+               sig.IdentityLocalPart = ilocalpart;
11556+               sig.IdentityDomain = idomain;
11557+       }
11558+       /*- body count -*/
11559+       if (values[8] == NULL || !m_HonorBodyLengthTag) {
11560+               sig.BodyLength = -1;
11561+       } else {
11562+               if (!ParseUnsigned(values[8], (unsigned long *) &sig.BodyLength))
11563+                       return DKIM_BAD_SYNTAX;
11564+       }
11565+       /*- query methods -*/
11566+       if (values[9] != NULL) {
11567+
11568+               /*- make sure "dns" is in the list -*/
11569+               bool            HasDNS = false;
11570+               char           *s = strtok_r(values[9], ":", &saveptr);
11571+               while (s != NULL) {
11572+                       if (strncmp(s, "dns", 3) == 0 && (s[3] == '\0' || s[3] == '/')) {
11573+                               HasDNS = true;
11574+                               break;
11575+                       }
11576+                       s = strtok_r(NULL, ": \t", &saveptr);
11577+               }
11578+               if (!HasDNS)
11579+                       return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown query method
11580+       }
11581+#if SIZEOF_TIME_T  == 8
11582+       /*- signature time -*/
11583+       time_t          SignedTime = -1;
11584+#else
11585+       long long       SignedTime = -1;
11586+#endif
11587+       if (values[10] != NULL) {
11588+               if (!ParseUnsigned(values[10], (unsigned long *) &SignedTime))
11589+                       return DKIM_BAD_SYNTAX;
11590+       }
11591+       /*- expiration time -*/
11592+       if (values[11] == NULL) {
11593+               sig.ExpireTime = -1;
11594+       } else {
11595+               if (!ParseUnsigned(values[11], (unsigned long *) &sig.ExpireTime))
11596+                       return DKIM_BAD_SYNTAX;
11597+               if (sig.ExpireTime != -1) {
11598+                       /*- the value of x= MUST be greater than the value of t= if both are present -*/
11599+#if SIZEOF_TIME_T  == 8
11600+                       if (SignedTime != -1 && sig.ExpireTime <= SignedTime)
11601+                               return DKIM_BAD_SYNTAX;
11602+#else
11603+                       if (SignedTime != -1 && (long long) sig.ExpireTime <= SignedTime)
11604+                               return DKIM_BAD_SYNTAX;
11605+#endif
11606+                       /*- todo: if possible, use the received date/time instead of the current time -*/
11607+                       time_t curtime = time(NULL);
11608+#if SIZEOF_TIME_T  == 8
11609+                       if (curtime > sig.ExpireTime)
11610+                               return DKIM_SIGNATURE_EXPIRED;
11611+#else /*- handle year 2038 the best we can, beyond which one has to upgrade to a 64 bit os */
11612+                       if (curtime < 2147483648 && curtime > sig.ExpireTime)
11613+                               return DKIM_SIGNATURE_EXPIRED;
11614+#endif
11615+               }
11616+       }
11617+       /*- parse the signed headers list -*/
11618+       bool            HasFrom = false, HasSubject = false;
11619+       RemoveSWSP(values[4]);          /*- header names shouldn't have spaces in them so this should be ok... */
11620+       char           *s = strtok_r(values[4], ":", &saveptr);
11621+       while (s != NULL) {
11622+               if (_stricmp(s, "From") == 0)
11623+                       HasFrom = true;
11624+               else
11625+               if (_stricmp(s, "Subject") == 0)
11626+                       HasSubject = true;
11627+               sig.SignedHeaders.push_back(s);
11628+               s = strtok_r(NULL, ":", &saveptr);
11629+       }
11630+       if (!HasFrom)
11631+               return DKIM_BAD_SYNTAX; /*- todo: maybe create a new error code for h= missing From */
11632+       if (m_SubjectIsRequired && !HasSubject)
11633+               return DKIM_BAD_SYNTAX; /*- todo: maybe create a new error code for h= missing Subject */
11634+       return DKIM_SUCCESS;
11635+}
11636+
11637+
11638+/*- ProcessBody - Process message body data -*/
11639+int
11640+CDKIMVerify::ProcessBody(char *szBuffer, int nBufLength, bool bEOF)
11641+{
11642+       bool            MoreBodyNeeded = false;
11643+
11644+       for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) {
11645+               if (i->Status == DKIM_SUCCESS) {
11646+                       if (i->BodyCanonicalization == DKIM_CANON_SIMPLE) {
11647+                               if (nBufLength > 0) {
11648+                                       while (i->EmptyLineCount > 0) {
11649+                                               i->Hash("\r\n", 2, true);
11650+                                               i->EmptyLineCount--;
11651+                                       }
11652+                                       i->Hash(szBuffer, nBufLength, true);
11653+                                       i->Hash("\r\n", 2, true);
11654+                               } else {
11655+                                       i->EmptyLineCount++;
11656+                                       if (bEOF)
11657+                                               i->Hash("\r\n", 2, true);
11658+                               }
11659+                       } else
11660+                       if (i->BodyCanonicalization == DKIM_CANON_RELAXED) {
11661+                               CompressSWSP(szBuffer, nBufLength);
11662+                               if (nBufLength > 0) {
11663+                                       while (i->EmptyLineCount > 0) {
11664+                                               i->Hash("\r\n", 2, true);
11665+                                               i->EmptyLineCount--;
11666+                                       }
11667+                                       i->Hash(szBuffer, nBufLength, true);
11668+                                       if (!bEOF)
11669+                                               i->Hash("\r\n", 2, true);
11670+                               } else
11671+                                       i->EmptyLineCount++;
11672+                       } else
11673+                       if (i->BodyCanonicalization == DKIM_CANON_NOWSP) {
11674+                               RemoveSWSP(szBuffer, nBufLength);
11675+                               i->Hash(szBuffer, nBufLength, true);
11676+                       }
11677+                       if (i->UnverifiedBodyCount == 0)
11678+                               MoreBodyNeeded = true;
11679+               }
11680+       }
11681+       if (!MoreBodyNeeded)
11682+               return DKIM_FINISHED_BODY;
11683+       return DKIM_SUCCESS;
11684+}
11685+
11686+SelectorInfo::SelectorInfo(const string &sSelector, const string &sDomain):Selector(sSelector), Domain(sDomain)
11687+{
11688+       AllowSHA1 = true;
11689+#ifdef HAVE_EVP_SHA256
11690+       AllowSHA256 = true;
11691+#else
11692+       AllowSHA256 = false;
11693+#endif
11694+       PublicKey = NULL;
11695+       Testing = false;
11696+       SameDomain = false;
11697+       Status = DKIM_SUCCESS;
11698+} SelectorInfo::~SelectorInfo()
11699+{
11700+       if (PublicKey != NULL) {
11701+               EVP_PKEY_free(PublicKey);
11702+       }
11703+}
11704+
11705+////////////////////////////////////////////////////////////////////////////////
11706+//
11707+// Parse - Parse a DKIM selector
11708+//
11709+////////////////////////////////////////////////////////////////////////////////
11710+int
11711+SelectorInfo::Parse(char *Buffer)
11712+{
11713+       static const char *tags[] = { "v", "g", "h", "k", "p", "s", "t", "n", NULL };
11714+       char           *values[sizeof (tags) / sizeof (tags[0])] = { NULL };
11715+       char           *saveptr;
11716+
11717+       if (!ParseTagValueList(Buffer, tags, values))
11718+               return DKIM_SELECTOR_INVALID;
11719+       if (values[0] != NULL) {
11720+               /*- make sure the version is "DKIM1" -*/
11721+               if (strcmp(values[0], "DKIM1") != 0)
11722+                       return DKIM_SELECTOR_INVALID;   /*- todo: maybe create a new error code for unsupported selector version */
11723+               /*- make sure v= is the first tag in the response  */
11724+               /*- todo: maybe don't enforce this, it seems unnecessary */
11725+               for (unsigned int j = 1; j < sizeof (values) / sizeof (values[0]); j++) {
11726+                       if (values[j] != NULL && values[j] < values[0]) {
11727+                               return DKIM_SELECTOR_INVALID;
11728+                       }
11729+               }
11730+       }
11731+       /*- selector MUST have p= tag -*/
11732+       if (values[4] == NULL)
11733+               return DKIM_SELECTOR_INVALID;
11734+       /*- granularity -*/
11735+       if (values[1] == NULL)
11736+               Granularity = "*";
11737+
11738+       else
11739+               Granularity = values[1];
11740+       /*- hash algorithm -*/
11741+       if (values[2] == NULL) {
11742+               AllowSHA1 = true;
11743+#ifdef HAVE_EVP_SHA256
11744+               AllowSHA256 = true;
11745+#else
11746+               AllowSHA256 = false;
11747+#endif
11748+       } else {
11749+               /*- MUST include "sha1" or "sha256" -*/
11750+               char           *s = strtok_r(values[2], ":", &saveptr);
11751+               while (s != NULL) {
11752+                       if (strcmp(s, "sha1") == 0)
11753+                               AllowSHA1 = true;
11754+#ifdef HAVE_EVP_SHA256
11755+                       else if (strcmp(s, "sha256") == 0)
11756+                               AllowSHA256 = true;
11757+#endif
11758+                       s = strtok_r(NULL, ":", &saveptr);
11759+               }
11760+#ifdef HAVE_EVP_SHA256
11761+               if (!(AllowSHA1 || AllowSHA256))
11762+#else
11763+               if (!AllowSHA1)
11764+#endif
11765+                       return DKIM_SELECTOR_INVALID;   /*- todo: maybe create a new error code for unsupported hash algorithm */
11766+       }
11767+       /*- key type -*/
11768+       if (values[3] != NULL) {
11769+               /*- key type MUST be "rsa" -*/
11770+               if (strcmp(values[3], "rsa") != 0)
11771+                       return DKIM_SELECTOR_INVALID;
11772+       }
11773+       /*- service type -*/
11774+       if (values[5] != NULL) {
11775+               /*- make sure "*" or "email" is in the list -*/
11776+               bool            ServiceTypeMatch = false;
11777+               char           *s = strtok_r(values[5], ":", &saveptr);
11778+               while (s != NULL) {
11779+                       if (strcmp(s, "*") == 0 || strcmp(s, "email") == 0) {
11780+                               ServiceTypeMatch = true;
11781+                               break;
11782+                       }
11783+                       s = strtok_r(NULL, ":", &saveptr);
11784+               }
11785+               if (!ServiceTypeMatch)
11786+                       return DKIM_SELECTOR_INVALID;
11787+       }
11788+       /*- flags -*/
11789+       if (values[6] != NULL) {
11790+               char           *s = strtok_r(values[6], ":", &saveptr);
11791+               while (s != NULL) {
11792+                       if (strcmp(s, "y") == 0) {
11793+                               Testing = true;
11794+                       } else
11795+                       if (strcmp(s, "s") == 0) {
11796+                               SameDomain = true;
11797+                       }
11798+                       s = strtok_r(NULL, ":", &saveptr);
11799+               }
11800+       }
11801+#define M_ToConstUCharPtr(p)       reinterpret_cast<const unsigned char*>(p) /* Cast to unsigned char* */
11802+       /*- public key data */
11803+       unsigned        PublicKeyLen = DecodeBase64(values[4]);
11804+       if (PublicKeyLen == 0)
11805+               return DKIM_SELECTOR_KEY_REVOKED; /*- this error causes the signature to fail */
11806+       else {
11807+               EVP_PKEY       *pkey;
11808+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
11809+               int             rtype;
11810+#endif
11811+               const unsigned char  *qq; /*- public key data */
11812+
11813+               qq = M_ToConstUCharPtr(values[4]);
11814+#ifdef DARWIN
11815+               pkey = d2i_PUBKEY(NULL, (unsigned char **) &qq, PublicKeyLen);
11816+#else
11817+               pkey = d2i_PUBKEY(NULL, &qq, PublicKeyLen);
11818+#endif
11819+               if (!pkey)
11820+                       return DKIM_SELECTOR_PUBLIC_KEY_INVALID;
11821+               /*- make sure public key is the correct type (we only support rsa) */
11822+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
11823+               rtype = EVP_PKEY_base_id(pkey);
11824+               if (rtype == EVP_PKEY_RSA || rtype == EVP_PKEY_RSA2)
11825+#else
11826+               if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2)
11827+#endif
11828+                       PublicKey = pkey;
11829+               else {
11830+                       EVP_PKEY_free(pkey);
11831+                       return DKIM_SELECTOR_PUBLIC_KEY_INVALID;
11832+               }
11833+       }
11834+       return DKIM_SUCCESS;
11835+}
11836+
11837+/*- GetSelector - Get a DKIM selector for a domain -*/
11838+SelectorInfo &CDKIMVerify::GetSelector(const string &sSelector, const string &sDomain)
11839+{
11840+       /*- see if we already have this selector -*/
11841+       for (list < SelectorInfo >::iterator i = Selectors.begin(); i != Selectors.end(); ++i) {
11842+               if (_stricmp(i->Selector.c_str(), sSelector.c_str()) == 0 && _stricmp(i->Domain.c_str(), sDomain.c_str()) == 0) {
11843+                       return *i;
11844+               }
11845+       }
11846+       Selectors.push_back(SelectorInfo(sSelector, sDomain));
11847+       SelectorInfo &sel = Selectors.back();
11848+       string sFQDN = sSelector;
11849+       sFQDN += "._domainkey.";
11850+       sFQDN += sDomain;
11851+       char            Buffer[4096];
11852+       int             DNSResult;
11853+
11854+       if (m_pfnSelectorCallback)
11855+               DNSResult = m_pfnSelectorCallback(sFQDN.c_str(), Buffer, sizeof(Buffer));
11856+       else
11857+               DNSResult = DNSGetTXT(sFQDN.c_str(), Buffer, sizeof(Buffer));
11858+       switch (DNSResult) {
11859+       case DNSRESP_SUCCESS:
11860+               sel.Status = sel.Parse(Buffer);
11861+               break;
11862+       case DNSRESP_TEMP_FAIL:
11863+               sel.Status = DKIM_SELECTOR_DNS_TEMP_FAILURE;
11864+               break;
11865+       case DNSRESP_PERM_FAIL:
11866+       default:
11867+               sel.Status = DKIM_SELECTOR_DNS_PERM_FAILURE;
11868+               break;
11869+       case DNSRESP_DOMAIN_NAME_TOO_LONG:
11870+               sel.Status = DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG;
11871+               break;
11872+       }
11873+       return sel;
11874+}
11875+
11876+/*- GetDetails - Get DKIM verification details (per signature) -*/
11877+int
11878+CDKIMVerify::GetDetails(int *nSigCount, DKIMVerifyDetails ** pDetails)
11879+{
11880+       Details.clear();
11881+       for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) {
11882+               DKIMVerifyDetails d;
11883+               d.szSignature = (char *) i->Header.c_str();
11884+               d.szSignatureDomain = (char*)i->Domain.c_str();
11885+               d.szIdentityDomain = (char*)i->IdentityDomain.c_str();
11886+               d.nResult = i->Status;
11887+               d.szCanonicalizedData = (char *) i->CanonicalizedData.c_str();
11888+               Details.push_back(d);
11889+       } *nSigCount = Details.size();
11890+       *pDetails = (*nSigCount != 0) ? &Details[0] : NULL;
11891+       return DKIM_SUCCESS;
11892+}
11893+
11894+char           *DKIM_CALL
11895+CDKIMVerify::GetDomain(void)
11896+{
11897+       static string   sFromDomain;
11898+       for (list <string>::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) {
11899+               if (_strnicmp(i->c_str(), "From", 4) == 0) {
11900+                       /*- skip over whitespace between the header name and : -*/
11901+                       const char     *s = i->c_str() + 4;
11902+                       while (*s == ' ' || *s == '\t')
11903+                               s++;
11904+                       if (*s == ':') {
11905+                               vector <string> Addresses;
11906+                               if (ParseAddresses(s + 1, Addresses)) {
11907+                                       unsigned atpos = Addresses[0].find('@');
11908+                                       sFromDomain = Addresses[0].substr(atpos + 1);
11909+                                       break;
11910+                               }
11911+                       }
11912+               }
11913+       }
11914+       return ((char *) sFromDomain.c_str());
11915+}
11916+
11917+void
11918+getversion_dkimverify_cpp()
11919+{
11920+       static char    *x = (char *) "$Id: dkimverify.cpp,v 1.23 2019-05-22 11:29:09+05:30 Cprogrammer Exp mbhangui $";
11921+
11922+       x++;
11923+}
11924diff -ruN ../netqmail-1.06-original/dkimverify.h netqmail-1.06/dkimverify.h
11925--- ../netqmail-1.06-original/dkimverify.h      1970-01-01 01:00:00.000000000 +0100
11926+++ netqmail-1.06/dkimverify.h  2019-06-19 09:47:30.665464671 +0200
11927@@ -0,0 +1,150 @@
11928+/*
11929+ * $Log: dkimverify.h,v $
11930+ * Revision 1.9  2019-06-14 21:25:11+05:30  Cprogrammer
11931+ * BUG - honor body length tag in verification. Changed data type for BodyLength
11932+ *
11933+ * Revision 1.8  2019-05-22 11:30:06+05:30  Cprogrammer
11934+ * fix for 32 bit systems where time_t is 4 bytes & encounters year 2038 issue
11935+ *
11936+ * Revision 1.7  2017-08-31 17:07:45+05:30  Cprogrammer
11937+ * fixed g++ compiler warning
11938+ *
11939+ * Revision 1.6  2017-08-09 22:03:46+05:30  Cprogrammer
11940+ * initialized EVP_MD_CTX variables
11941+ *
11942+ * Revision 1.5  2017-08-08 23:50:47+05:30  Cprogrammer
11943+ * openssl 1.1.0 port
11944+ *
11945+ * Revision 1.4  2015-12-15 16:03:09+05:30  Cprogrammer
11946+ * use time_t for ExpireTime
11947+ *
11948+ * Revision 1.3  2011-06-04 09:37:25+05:30  Cprogrammer
11949+ * added AllowUnsignedFromHeaders
11950+ *
11951+ * Revision 1.2  2009-03-26 15:12:15+05:30  Cprogrammer
11952+ * changes for ADSP
11953+ *
11954+ * Revision 1.1  2009-03-21 08:50:22+05:30  Cprogrammer
11955+ * Initial revision
11956+ *
11957+ *
11958+ *  Copyright 2005 Alt-N Technologies, Ltd.
11959+ *
11960+ *  Licensed under the Apache License, Version 2.0 (the "License");
11961+ *  you may not use this file except in compliance with the License.
11962+ *  You may obtain a copy of the License at
11963+ *
11964+ *      http://www.apache.org/licenses/LICENSE-2.0
11965+ *
11966+ *  This code incorporates intellectual property owned by Yahoo! and licensed
11967+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
11968+ *
11969+ *  Unless required by applicable law or agreed to in writing, software
11970+ *  distributed under the License is distributed on an "AS IS" BASIS,
11971+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11972+ *  See the License for the specific language governing permissions and
11973+ *  limitations under the License.
11974+ *
11975+ */
11976+
11977+#ifndef DKIMVERIFY_H
11978+#define DKIMVERIFY_H
11979+
11980+#include "dkimbase.h"
11981+#include "time_t_size.h"
11982+#include <vector>
11983+
11984+#define DKIM_SIG_VERSION_PRE_02                        0
11985+#define DKIM_SIG_VERSION_02_PLUS               1
11986+
11987+class           SelectorInfo {
11988+ public:
11989+       SelectorInfo(const string & sSelector, const string & sDomain);
11990+       ~SelectorInfo();
11991+
11992+       string          Domain;
11993+       string          Selector;
11994+       string          Granularity;
11995+       bool            AllowSHA1;
11996+       bool            AllowSHA256;
11997+       EVP_PKEY       *PublicKey;      /* the public key */
11998+       bool            Testing;
11999+       bool            SameDomain;
12000+       int             Status;
12001+       int             Parse(char *Buffer);
12002+};
12003+
12004+class           SignatureInfo {
12005+public:
12006+       SignatureInfo(bool SaveCanonicalizedData);
12007+       ~SignatureInfo();
12008+
12009+       void            Hash(const char *szBuffer, unsigned nBufLength, bool IsBody = false);
12010+       string          Header;
12011+       unsigned        Version;
12012+       string          Domain;
12013+       string          Selector;
12014+       string          SignatureData;
12015+       string          BodyHashData;
12016+       string          IdentityLocalPart;
12017+       string          IdentityDomain;
12018+       string          CanonicalizedData;
12019+       vector <string> SignedHeaders;
12020+       long            BodyLength;
12021+       unsigned        HeaderCanonicalization;
12022+       unsigned        BodyCanonicalization;
12023+#if SIZEOF_TIME_T  == 8
12024+       time_t          ExpireTime;
12025+#else
12026+       long long       ExpireTime;
12027+#endif
12028+       long            VerifiedBodyCount;
12029+       long            UnverifiedBodyCount;
12030+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
12031+       EVP_MD_CTX     *m_Hdr_ctx = NULL;
12032+       EVP_MD_CTX     *m_Bdy_ctx = NULL;
12033+#else
12034+       EVP_MD_CTX      m_Hdr_ctx;
12035+       EVP_MD_CTX      m_Bdy_ctx;
12036+#endif
12037+       SelectorInfo   *m_pSelector;
12038+       int             Status;
12039+       int             m_nHash;        // use one of the DKIM_HASH_xxx constants here
12040+       unsigned        EmptyLineCount;
12041+       bool            m_SaveCanonicalizedData;
12042+};
12043+
12044+class           CDKIMVerify:public CDKIMBase {
12045+public:
12046+
12047+       CDKIMVerify();
12048+       ~CDKIMVerify();
12049+
12050+       int             Init(DKIMVerifyOptions * pOptions);
12051+       int             GetResults(int *sCount, int *sSize);
12052+       int             GetDetails(int *nSigCount, DKIMVerifyDetails ** pDetails);
12053+       virtual int     ProcessHeaders(void);
12054+       virtual int     ProcessBody(char *szBuffer, int nBufLength, bool bEOF);
12055+       const char     *GetPractices() {return Practices.c_str();}
12056+       char           *DKIM_CALL GetDomain(void);
12057+
12058+protected:
12059+       int             ParseDKIMSignature(const string & sHeader, SignatureInfo & sig);
12060+       SelectorInfo   &GetSelector(const string & sSelector, const string & sDomain);
12061+       int             GetADSP(const string &sDomain, int &iADSP);
12062+       int             GetSSP(const string &sDomain, int &iSSP, bool & bTesting);
12063+       list <SignatureInfo> Signatures;
12064+       list <SelectorInfo> Selectors;
12065+       DKIMDNSCALLBACK m_pfnSelectorCallback;  // selector record callback
12066+       DKIMDNSCALLBACK m_pfnPracticesCallback; // SSP record callback
12067+       bool            m_HonorBodyLengthTag;
12068+       bool            m_CheckPractices;
12069+       bool            m_Accept3ps;            //TBS(Luc) : accept 3rd party signature(s)
12070+       bool            m_SubjectIsRequired;
12071+       bool            m_SaveCanonicalizedData;
12072+       bool            m_AllowUnsignedFromHeaders;
12073+       vector <DKIMVerifyDetails> Details;
12074+       string          Practices;
12075+};
12076+
12077+#endif /*- DKIMVERIFY_H */
12078diff -ruN ../netqmail-1.06-original/dknewkey.sh netqmail-1.06/dknewkey.sh
12079--- ../netqmail-1.06-original/dknewkey.sh       1970-01-01 01:00:00.000000000 +0100
12080+++ netqmail-1.06/dknewkey.sh   2019-02-27 20:57:13.389025081 +0100
12081@@ -0,0 +1,27 @@
12082+#
12083+# $Log: dknewkey.sh,v $
12084+# Revision 1.4  2010-05-16 19:59:48+05:30  Cprogrammer
12085+# fix for Mac OS X
12086+#
12087+# Revision 1.3  2004-11-02 20:48:31+05:30  Cprogrammer
12088+# fixed error when dknewkey was called without arguments
12089+#
12090+# Revision 1.2  2004-10-21 21:54:25+05:30  Cprogrammer
12091+# create public key file
12092+#
12093+# Revision 1.1  2004-10-20 20:40:56+05:30  Cprogrammer
12094+# Initial revision
12095+#
12096+#
12097+if [ $# -lt 1 ] ; then
12098+       echo "USAGE: dknewkey keyfile [bits]"
12099+       exit 1
12100+fi
12101+BITS=384
12102+if test -n "$2"; then BITS=$2; fi
12103+
12104+openssl genrsa -out $1 $BITS 2>/dev/null
12105+openssl rsa -in $1 -out /tmp/dknewkey.$$ -pubout -outform PEM 2>/dev/null
12106+printf "%s._domainkey\tIN\tTXT\t\"k=rsa; p=%s\"\n" `basename $1` `grep -v ^-- /tmp/dknewkey.$$ | tr -d '\n'` > $1.pub
12107+/bin/cat $1.pub
12108+/bin/rm -f /tmp/dknewkey.$$
12109diff -ruN ../netqmail-1.06-original/dktest.9 netqmail-1.06/dktest.9
12110--- ../netqmail-1.06-original/dktest.9  1970-01-01 01:00:00.000000000 +0100
12111+++ netqmail-1.06/dktest.9      2019-02-27 20:57:13.389025081 +0100
12112@@ -0,0 +1,86 @@
12113+.TH dktest 8
12114+.SH NAME
12115+dktest \- exercise the domainkeys library
12116+.SH SYNOPSIS
12117+.B dktest
12118+.I opts
12119+
12120+.I opts
12121+is a series of getopt-style options.
12122+
12123+.SH DESCRIPTION
12124+.B dktest
12125+exercises the domainkeys library. Both signing and verification merely print out the DK header.
12126+They do not keep a copy of the input file. You will need to do something like this:
12127+
12128+.EX
12129+ (./dktest -s QMAILHOME/control/domainkeys/dog </tmp/testmsg; cat /tmp/testmsg)\
12130+ | ./dktest -v
12131+.EE
12132+
12133+Here are the options:
12134+.IP \[bu] 2
12135+-s
12136+\fIkey\fR: Sign.
12137+.I key
12138+is a path to a file containing a PEM-format private key. The base name of
12139+the file is  used  as  the  selector. Reads the email message on stdin. Prints the
12140+.B DomainKey-Signature
12141+header.
12142+
12143+.IP \[bu]
12144+-v: Verify. Verifies the email on stdin. Exits with a non-zero exit code and a message to
12145+stderr if there was a problem with the signature. Always prints a
12146+.B DomainKey-Status:
12147+header to stdout. This option requires the \fBs\fR._domainkey.\fBd\fR txt record in
12148+dns (public key). Here \fBs\fR is the selector and \fBd\fR is the domain
12149+.EX
12150+
12151+ Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys
12152+ DomainKey-Signature: a=rsa-sha1; q=dns; c=simple;
12153+  s=dog; d=indimail.org;
12154+  b=CndyNmOBqXD+d6qLGVjVua/oDJetLzAKAh3JoId93GmWRg1Y40DUdvZZhw8tTIoZ;
12155+.EE
12156+
12157+.IP \[bu]
12158+-c \fI[simple|nofws]\fR Canonicalization.
12159+Defaults to simple.
12160+
12161+.IP \[bu]
12162+-t \fIn\fR: Test.
12163+.I n
12164+is a number indicating which internal test is being performed. The meanings of the numbers
12165+are documented in the source code.
12166+
12167+.IP \[bu]
12168+-d \fIdns\fR Test dns record
12169+
12170+.IP \[bu]
12171+-b \fIn\fR: Buffer size.
12172+.I n
12173+is a number which forces the buffer size. Only needed for testing.
12174+
12175+.IP \[bu]
12176+-D \fIdkdomain\fR: set d= tag.
12177+.I dkdomain
12178+is set as the value for d= tag
12179+
12180+.IP \[bu]
12181+-h:
12182+include h= tag when signing
12183+
12184+.IP \[bu]
12185+-r:
12186+only include unique headers in the signature generation, implies -h
12187+
12188+.IP \[bu]
12189+-T:
12190+Generate DomainKey-Trace: header, shows Diff header if Verifying.
12191+
12192+.SH "SEE ALSO"
12193+dkim(8),
12194+qmail-dk(8),
12195+qmail-dkim(8),
12196+dknewkey(8),
12197+rfc-4870(5),
12198+rfc-4871(5)
12199diff -ruN ../netqmail-1.06-original/dktest.c netqmail-1.06/dktest.c
12200--- ../netqmail-1.06-original/dktest.c  1970-01-01 01:00:00.000000000 +0100
12201+++ netqmail-1.06/dktest.c      2020-04-09 19:44:51.053935329 +0200
12202@@ -0,0 +1,434 @@
12203+/*
12204+ * $Log: dktest.c,v $
12205+ * Revision 1.16  2013-08-17 16:01:08+05:30  Cprogrammer
12206+ * added case for duplicate DomainKey-Signature header
12207+ *
12208+ * Revision 1.15  2011-07-29 09:28:09+05:30  Cprogrammer
12209+ * fixed gcc 4.6 warnings
12210+ *
12211+ * Revision 1.14  2011-07-22 14:39:27+05:30  Cprogrammer
12212+ * added -D option to specify d= tag
12213+ *
12214+ * Revision 1.13  2009-12-10 15:04:09+05:30  Cprogrammer
12215+ * exit with DK_STAT
12216+ *
12217+ * Revision 1.12  2009-04-20 22:20:55+05:30  Cprogrammer
12218+ * fixed compilation warning
12219+ *
12220+ * Revision 1.11  2009-04-16 22:39:33+05:30  Cprogrammer
12221+ * assign from = dk_from()
12222+ *
12223+ * Revision 1.10  2009-04-05 12:51:22+05:30  Cprogrammer
12224+ * added preprocessor warning
12225+ *
12226+ * Revision 1.9  2009-03-14 16:28:22+05:30  Cprogrammer
12227+ * Fixed dktest.c to check for DK_STAT_GRANULARITY
12228+ *
12229+ * Revision 1.8  2009-03-14 08:50:41+05:30  Cprogrammer
12230+ * Added -h option to dktest to add h= tag when signing
12231+ * Added -r option to dktest to enable ignoring duplicate headers when signing (implies -h)
12232+ * Added -T option to dktest to enable generation of trace headers
12233+ * Added -d option to fetch dns text record for domainkey
12234+ *
12235+ * Revision 1.7  2005-08-23 17:15:02+05:30  Cprogrammer
12236+ * gcc 4 compliance
12237+ *
12238+ * Revision 1.6  2005-04-25 22:52:45+05:30  Cprogrammer
12239+ * removed printing of comments
12240+ *
12241+ * Revision 1.5  2005-04-01 19:54:27+05:30  Cprogrammer
12242+ * libdomainkeys-0.64
12243+ *
12244+ * Revision 1.4  2004-10-25 14:54:02+05:30  Cprogrammer
12245+ * libdomainkeys-0.63
12246+ *
12247+ * Revision 1.3  2004-10-22 20:24:18+05:30  Cprogrammer
12248+ * added RCS id
12249+ *
12250+ * Revision 1.2  2004-10-20 20:01:58+05:30  Cprogrammer
12251+ * upgrade to libdomainkeys-0.62
12252+ *
12253+ * Revision 1.1  2004-09-22 23:30:32+05:30  Cprogrammer
12254+ * Initial revision
12255+ *
12256+ */
12257+#include <stdio.h>
12258+#include <string.h>
12259+#include <unistd.h>
12260+#include <stdlib.h>
12261+#include "domainkeys.h"
12262+
12263+#ifdef DOMAIN_KEYS
12264+int             optf = 0;
12265+
12266+void
12267+errorout(DK *dk, DK_STAT st)
12268+{
12269+       if (optf && dk)
12270+               fprintf(stderr, "%s(%d):", dk_errfile(dk), dk_errline(dk));
12271+       fprintf(stderr, "dktest: %s\n", DK_STAT_to_string(st));
12272+       exit(st);
12273+}
12274+
12275+int
12276+main(int argc, char *argv[])
12277+{
12278+       char            inbuf[1024];
12279+       char            advice[2048];
12280+       char            trace_count[BUFSIZ];
12281+       size_t          inlen;
12282+       size_t          advicelen = sizeof(advice);
12283+       DK             *dk;
12284+       DK_LIB         *dklib;
12285+       DK_STAT         st, dkt_st;
12286+       signed char     ch;
12287+       int             opts = 0, optv = 0, optt = 0, opth = 0, optr = 0, optT = 0,
12288+                                       optc = DK_CANON_SIMPLE;
12289+       char           *canon = "simple";
12290+       char           *keyfn  = 0, *selector  = 0;
12291+       char           *txtrec, *cp, *from, *dkdomain;
12292+       char            privkey[2048];
12293+       FILE           *privkeyf = 0;
12294+       size_t          privkeylen;
12295+       DK_FLAGS        dkf = 0;
12296+       int             i;
12297+       DK_TRACE_TYPE   dk_trace_tag[4] = {
12298+               DKT_RAW_HEADER,
12299+               DKT_CANON_HEADER,
12300+               DKT_RAW_BODY,
12301+               DKT_CANON_BODY
12302+       };
12303+
12304+       for (dkdomain = (char *) 0;;)
12305+       {
12306+               ch = getopt(argc, argv, "s:vt:fb:c:hrTd:D:");
12307+               if (ch == -1)
12308+                       break;
12309+               switch (ch)
12310+               {
12311+               case 'D':
12312+                       dkdomain = optarg;
12313+                       break;
12314+               case 'd': /*- optD */
12315+                       txtrec = dns_text(optarg);
12316+                       cp = txtrec;
12317+                       printf("%ld\n", (long) strlen(cp));
12318+                       while (*cp) {
12319+                               printf("%02x ", *cp++);
12320+                               if ((cp - txtrec) % 16 == 0)
12321+                                       printf("\n");
12322+                       }
12323+                       printf("\n");
12324+                       if (!strcmp(txtrec, "e=perm;"))
12325+                               exit(0);
12326+                       if (!strcmp(txtrec, "e=temp;"))
12327+                               exit(0);
12328+                       free(txtrec);
12329+                       return (0);
12330+                       break;
12331+               case 'T':
12332+                       optT = 1;
12333+                       break;
12334+               case 'v':
12335+                       optv = 1;
12336+                       break;
12337+               case 'f':
12338+                       optf = 1;
12339+                       break;
12340+               case 'r':
12341+                       optr = 1;
12342+                       opth = 1;
12343+                       break;
12344+               case 's':
12345+                       opts = 1;
12346+                       keyfn = optarg;
12347+                       selector = optarg;
12348+                       while (*optarg)
12349+                       {
12350+                               if (*optarg == '/')
12351+                                       selector = optarg + 1;
12352+                               optarg++;
12353+                       }
12354+                       break;
12355+               case 't':
12356+                       optt = atoi(optarg);
12357+                       break;
12358+               case 'h':
12359+                       opth = 1;
12360+                       break;
12361+               case 'b':
12362+                       advicelen = atoi(optarg);
12363+                       if (advicelen > sizeof(advice))
12364+                               advicelen = sizeof(advice);
12365+                       break;
12366+               case 'c':
12367+                       if (!strcmp(optarg, "simple"))
12368+                               optc = DK_CANON_SIMPLE, canon = "simple";
12369+                       else
12370+                       if (!strcmp(optarg, "nofws"))
12371+                               optc = DK_CANON_NOFWS, canon = "nofws";
12372+                       else
12373+                       {
12374+                               fprintf(stderr, "dktest: unrecognized canonicalization.\n");
12375+                               exit(1);
12376+                       }
12377+               }
12378+       }
12379+       if (opts)
12380+       {
12381+               if (!(privkeyf = fopen(keyfn, "r"))) /*- TC10 */
12382+               {
12383+                       fprintf(stderr, "dktest: can't open private key file %s\n", keyfn);
12384+                       exit(1);
12385+               }
12386+               if ((privkeylen = fread(privkey, 1, sizeof(privkey), privkeyf)) == sizeof(privkey))
12387+                                       /*- TC9 */
12388+               {                                       
12389+                       fprintf(stderr, "dktest: private key buffer isn't big enough, use a smaller private key or recompile.\n");
12390+                       exit(1);
12391+               }
12392+               privkey[privkeylen] = '\0';
12393+               fclose(privkeyf);
12394+       }
12395+       if (optt == 1)
12396+               errorout(NULL, 0);              /*- TC2 */
12397+       if (optt == 2)
12398+               errorout(NULL, 32767);  /*- TC3 */
12399+       dklib = dk_init(&st);
12400+       if (st != DK_STAT_OK)
12401+               errorout(NULL, st);
12402+       if (!dklib)
12403+               errorout(NULL, 200);
12404+       if (optv)
12405+       {
12406+               dk = dk_verify(dklib, &st);
12407+               if (st != DK_STAT_OK)
12408+                       errorout(dk, st);
12409+       } else
12410+       if (opts)
12411+       {
12412+               dk = dk_sign(dklib, &st, optc);
12413+               if (st != DK_STAT_OK)
12414+                       errorout(dk, st);
12415+               if (optr)
12416+                       st = dk_setopts(dk, DKOPT_RDUPE);
12417+               if (st != DK_STAT_OK)
12418+                       errorout(dk, st);
12419+       } else
12420+       {
12421+               fprintf(stderr, "dktest: [-f] [-b#] [-c nofws|simple] [-v|-s selector] [-h] [-t#] [-r] [-T][-d dnsrecord]\n"); /* TC1 */
12422+               exit(1);
12423+       }
12424+       if (optT) /*- trace */
12425+       {
12426+               st = dk_setopts(dk, (DKOPT_TRACE_h|DKOPT_TRACE_H|DKOPT_TRACE_b|DKOPT_TRACE_B));
12427+               if (st != DK_STAT_OK)
12428+                       errorout(dk, st);
12429+       }
12430+       if (optt == 3)
12431+               errorout(dk, dk_message(NULL, (const unsigned char *) "", 1));  /*- TC4 */
12432+       if (optt == 4)
12433+               errorout(dk, dk_message(dk, (const unsigned char *) NULL, 1));  /*- TC5 */
12434+       if (optt == 5)
12435+               errorout(dk, dk_message(dk, (const unsigned char *) "", 0));    /*- TC6 */
12436+       if (optt >= 100 && optt <= 140)
12437+               errorout(dk, optt - 100);       /*- TC53 */
12438+       st = DK_STAT_OK;
12439+       /*
12440+        * This should work with DOS or UNIX text files -Tim
12441+        * Reduced calls to dk_message, in lib dkhash called for EVERY char
12442+        * DOS formatted input (CRLF line terminated) will have fewer calls
12443+        * to dk_message() than UNIX (LF line terminated) input.
12444+        */
12445+       while (1)
12446+       {
12447+               char           *inp;
12448+
12449+               inlen = fread(inbuf, 1, sizeof(inbuf), stdin);
12450+               inp = inbuf;
12451+               while (inlen--)
12452+               {
12453+                       if (*inp == '\n')
12454+                               st = dk_message(dk, (const unsigned char *) "\r\n", 2);
12455+                       else
12456+                               st = dk_message(dk, (const unsigned char *) inp, 1);
12457+                       if (st != DK_STAT_OK)
12458+                               break;  //stop looping if there was an error
12459+                       inp++;
12460+               }
12461+               if ((inp-inbuf < sizeof(inbuf)) || (st != DK_STAT_OK))
12462+                       break; /*- if we read in the entire message or encountered an error */
12463+       }
12464+       if (st == DK_STAT_OK)
12465+       {
12466+               if (optt == 10)
12467+                       st = dk_end(dk, &dkf);
12468+               else
12469+                       st = dk_eom(dk, &dkf);
12470+       }
12471+       if (optT)
12472+       {
12473+               printf("DomainKey-Trace: U=http://domainkeys.sourceforge.net; V=TESTING;\n");
12474+               for (i = 0; i < 4; i++)
12475+               {
12476+                       if (dk_get_trace(dk, dk_trace_tag[i], trace_count, sizeof (trace_count)) != DK_STAT_OK)
12477+                       {
12478+                               fprintf(stderr, "dktest: Not enough resources for trace buffer output\n");
12479+                               break;
12480+                       } else
12481+                               printf("  %s\n", trace_count);
12482+               }
12483+               if (optv)
12484+               {
12485+                       printf("DomainKey-Trace-Diff:\n");
12486+                       for (i = 0; i < 4; i++) {
12487+                               dkt_st = dk_compare_trace(dk,dk_trace_tag[i],trace_count,sizeof(trace_count));
12488+                               if (dkt_st == DK_STAT_NOSIG)
12489+                               {
12490+                                       printf("  No DK-Trace: header found\n");
12491+                                       break;
12492+                               } else
12493+                               if (dkt_st != DK_STAT_OK)
12494+                               {
12495+                                       fprintf(stderr,"dktest: Not enough resources for trace buffer output\n");
12496+                                       break;
12497+                               } else
12498+                                       printf("  %s\n",trace_count);
12499+                       }
12500+               }
12501+       }
12502+       if ((optt == 6 || optt == 10) && optv)
12503+       {
12504+               printf("flags: ");
12505+               if (dkf & DK_FLAG_SET)
12506+                       printf("+");
12507+               if (dkf & DK_FLAG_TESTING)
12508+                       printf("t");
12509+               if (dkf & DK_FLAG_SIGNSALL)
12510+                       printf("s"); /*- wont be set if dk_end() is sucessful */
12511+               if (dkf & DK_FLAG_G)
12512+                       printf("g");
12513+               printf("\n");
12514+       } else
12515+       if (optt == 6 && opts)
12516+               errorout(dk, dk_getsig(dk, NULL, NULL, advicelen));     /*- TC14 */
12517+       else
12518+       if (optt == 7)
12519+       {
12520+               from = dk_from(dk);
12521+               if (!from)
12522+                       from = "";
12523+               printf("%s\n", from);   /*- TC14-1, TC14-2 */
12524+       } else
12525+       if (optt == 11)
12526+       {
12527+               from = dk_address(dk);
12528+               printf("%s\n", from);   /*- TC14-3, TC14-4 */
12529+       } else
12530+       if (optt == 9)
12531+       {
12532+               char           *s;
12533+
12534+               s = malloc(dk_headers(dk, NULL));
12535+               dk_headers(dk, s);
12536+               printf("%s\n", s);
12537+               free(s);
12538+       } else
12539+       if (optt == 8 && opts)
12540+       {
12541+               dk_getsig(dk, privkey, (unsigned char *) advice, advicelen);
12542+               if (st != DK_STAT_OK)
12543+                       errorout(dk, st);
12544+               printf("%d %d\n", (int) dk_siglen(privkey), (int) strlen(advice)); /*- TC39 */
12545+       } else
12546+       if (opts)
12547+       {
12548+               if (st != DK_STAT_OK)
12549+                       errorout(dk, st);
12550+               st = dk_getsig(dk, privkey, (unsigned char *) advice, advicelen);
12551+               if (st != DK_STAT_OK)
12552+                       errorout(dk, st);
12553+#if 0
12554+               printf("Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n");
12555+#endif
12556+               from = dk_from(dk);
12557+               printf("DomainKey-Signature: a=rsa-sha1; q=dns; c=%s;\n"
12558+                       "  s=%s; d=%s;\n" "  b=%s;\n", canon, selector, dkdomain ? dkdomain : from, advice);
12559+               if (opth == 1)
12560+               {
12561+                       if (dk_headers(dk, NULL) < sizeof(inbuf))
12562+                       {
12563+                               dk_headers(dk, inbuf);
12564+                               printf("  h=%s;\n", inbuf);
12565+                       }
12566+               }
12567+       } else
12568+       if (optv)
12569+       {
12570+               char           *status = 0;
12571+
12572+               switch (st)
12573+               {
12574+               case DK_STAT_OK:
12575+                       status = "good";
12576+                       break;
12577+               case DK_STAT_BADSIG:
12578+                       status = "bad";
12579+                       break;
12580+               case DK_STAT_NOSIG:
12581+                       status = "no signature";
12582+                       break;
12583+               case DK_STAT_NOKEY:
12584+               case DK_STAT_CANTVRFY:
12585+                       status = "no key";
12586+                       break;
12587+               case DK_STAT_BADKEY:
12588+                       status = "bad key";
12589+                       break;
12590+               case DK_STAT_INTERNAL:
12591+               case DK_STAT_ARGS:
12592+               case DK_STAT_SYNTAX:
12593+                       status = "bad format";
12594+                       break;
12595+               case DK_STAT_NORESOURCE:
12596+                       status = "no resources";
12597+                       break;
12598+               case DK_STAT_REVOKED:
12599+                       status = "revoked";
12600+                       break;
12601+               case DK_STAT_GRANULARITY:
12602+                       status = "bad sender (g=)";
12603+                       break;
12604+               case DK_STAT_DUPLICATE:
12605+                       status = "duplicate signature";
12606+                       break;
12607+               }
12608+#if 0
12609+               printf("Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n");
12610+#endif
12611+               printf("DomainKey-Status: %s\n", status);
12612+               rewind(stdin);
12613+       }
12614+       if (st != DK_STAT_OK)
12615+               errorout(dk, st);
12616+       dk_free(dk, 1);//cleanup properly (not really necessary for single run process)
12617+       dk_shutdown(dklib);
12618+       return(0);
12619+}
12620+#else
12621+#warning "not compiled with -DDOMAIN_KEYS"
12622+int
12623+main(int argc, char *argv[])
12624+{
12625+       fprintf(stderr, "not compiled with -DDOMAIN_KEYS\n");
12626+       return(1);
12627+}
12628+#endif
12629+
12630+void
12631+getversion_dktest_c()
12632+{
12633+       static char    *x = "$Id: dktest.c,v 1.16 2013-08-17 16:01:08+05:30 Cprogrammer Exp mbhangui $";
12634+
12635+       x++;
12636+}
12637diff -ruN ../netqmail-1.06-original/dktrace.c netqmail-1.06/dktrace.c
12638--- ../netqmail-1.06-original/dktrace.c 1970-01-01 01:00:00.000000000 +0100
12639+++ netqmail-1.06/dktrace.c     2019-02-27 20:57:13.389025081 +0100
12640@@ -0,0 +1,277 @@
12641+/*
12642+ * $Log: dktrace.c,v $
12643+ * Revision 1.1  2009-03-14 09:00:52+05:30  Cprogrammer
12644+ * Initial revision
12645+ *
12646+ */
12647+/*
12648+ * $Id: dktrace.c,v 1.1 2009-03-14 09:00:52+05:30 Cprogrammer Stab mbhangui $
12649+ */
12650+
12651+#ifdef DOMAIN_KEYS
12652+#include <stdio.h>
12653+#include <stdlib.h>
12654+#include <string.h>
12655+#include "str.h"
12656+#include "case.h"
12657+#include "dktrace.h"
12658+
12659+#define strncasecmp(x,y,z) case_diffb((x), (z), (y))
12660+
12661+static int     *
12662+getPointer(DK_TRACE * dkp, DK_TRACE_TYPE type)
12663+{
12664+       switch (type) {
12665+       case DKT_RAW_HEADER:
12666+               return dkp->ccounts_h;
12667+       case DKT_CANON_HEADER:
12668+               return dkp->ccounts_H;
12669+       case DKT_RAW_BODY:
12670+               return dkp->ccounts_b;
12671+       case DKT_CANON_BODY:
12672+               return dkp->ccounts_B;
12673+       default:
12674+               return 0;
12675+       }
12676+}
12677+
12678+//Modified version of dkparselist()
12679+static int
12680+dkt_parselist(char *list, char *letters, char *values[])
12681+{
12682+       char            key;
12683+       int             i;
12684+       char           *value;
12685+
12686+       /*- start with all args unset */
12687+       for (i = 0; letters[i]; i++) {
12688+               values[i] = NULL;
12689+       }
12690+       key = 0;
12691+       while (*list) {
12692+               if ((*list == ' ') || (*list == '\t') || (*list == '\r') || (*list == '\n')) {
12693+                       list++;
12694+               } else if (*list == '=') {
12695+                       char           *ws;
12696+
12697+                       ++list;
12698+                       value = list;
12699+                       ws = list;
12700+                       while (1) {
12701+                       /*
12702+                        * copy up to null or semicolon, deleting whitespace as we go
12703+                        */
12704+                               *ws = *list;
12705+                               if ((*list == ' ') || (*list == '\t') || (*list == '\r') || (*list == '\n')) {
12706+                               /*
12707+                                * ignore
12708+                                */
12709+                               } else if (!*list) {
12710+                                       break;
12711+                               } else if (*list == ';') {
12712+                                       *ws = '\0';
12713+                                       list++;
12714+                                       break;
12715+                               } else {
12716+                                       ws++;
12717+                               }
12718+                               list++;
12719+                       }
12720+                       if (!key) {
12721+                               return 0;               //No key
12722+                       }
12723+               /*
12724+                * if we find a matching letter, remember the value
12725+                */
12726+                       for (i = 0; letters[i]; i++) {
12727+                               if (key == letters[i]) {
12728+                                       if (values[i]) {
12729+                                               return 0;       /* no duplicate keys. TC23 */
12730+                                       }
12731+                                       values[i] = value;
12732+                               }
12733+                       }
12734+                       key = 0;
12735+               } else {
12736+                       if (key) {
12737+                               return 0;               /* they already gave us a key. TC24 */
12738+                       }
12739+                       key = *list++;
12740+               }
12741+       }
12742+       return 1;
12743+}
12744+
12745+extern void
12746+dkt_add(DK_TRACE * dkp, DK_TRACE_TYPE type, const unsigned char *data, int dataLength)
12747+{
12748+       int            *ip;
12749+       ip = getPointer(dkp, type);
12750+       if (!ip)
12751+               return;
12752+
12753+       while (dataLength-- > 0)
12754+               ip[*data++]++;
12755+}
12756+
12757+//useful for building table directly
12758+extern void
12759+dkt_quickadd(DK_TRACE * dkp, DK_TRACE_TYPE type, int index, int count)
12760+{
12761+       int            *ip;
12762+       ip = getPointer(dkp, type);
12763+       if (!ip)
12764+               return;
12765+       if ((index < 256) && (index >= 0))
12766+               ip[index] = ip[index] + count;
12767+}
12768+
12769+//reverse of dkt_quickadd, reads data from table and returns the int count
12770+extern int
12771+dkt_getcount(DK_TRACE * dkp, DK_TRACE_TYPE type, int index, int count)
12772+{
12773+       int            *ip;
12774+       ip = getPointer(dkp, type);
12775+       if (!ip)
12776+               return 0;
12777+       if ((index < 256) && (index >= 0))
12778+               return ip[index];
12779+       return 0;
12780+}
12781+
12782+
12783+/*
12784+ * Fills in DK_TRACE *diff_table with the differences between
12785+ * *dka (before) and *dkb (after), (after - before = diff)
12786+ */
12787+extern int
12788+dkt_diff(DK_TRACE * dka, DK_TRACE * dkb, DK_TRACE_TYPE type, DK_TRACE * diff_table)
12789+{
12790+       int            *inputa, *inputb, *output;
12791+       int             i;
12792+       inputa = getPointer(dka, type);
12793+       if (!inputa)
12794+               return 0;
12795+       inputb = getPointer(dkb, type);
12796+       if (!inputb)
12797+               return 0;
12798+       output = getPointer(diff_table, type);
12799+       if (!output)
12800+               return 0;
12801+
12802+       for (i = 0; i < 256; i++) {
12803+               output[i] = (inputb[i] - inputa[i]);
12804+       }
12805+       return 1;
12806+}
12807+
12808+/*
12809+ * Generate the tag=value; data for a particular trace type
12810+ * returns length of generated C string including ending '\0'
12811+ */
12812+
12813+extern int
12814+dkt_generate(DK_TRACE * dkp, DK_TRACE_TYPE type, char *buffer, int maxBufferSize)
12815+{
12816+       int            *ip;
12817+       char           *cp;
12818+       int             ix;
12819+       int             len;
12820+       int             highest;
12821+
12822+       if (maxBufferSize < 20)
12823+               return 0;                               /* Getting too close, you lose */
12824+       cp = buffer;
12825+       ip = getPointer(dkp, type);
12826+       if (!ip)
12827+               return 0;
12828+       *buffer++ = (char) type;
12829+       --maxBufferSize;
12830+       *buffer++ = '=';
12831+       --maxBufferSize;
12832+
12833+/*
12834+ * Only produce as many entries as needed, rather than the full 256
12835+ */
12836+
12837+       for (ix = 0, highest = 0; ix < 256; ++ix) {
12838+               if (ip[ix] != 0)
12839+                       highest = ix;
12840+       }
12841+
12842+       for (ix = 0; ix <= highest; ++ix) {
12843+               if (ip[ix] != 0) {
12844+                       len = snprintf(buffer, maxBufferSize, "%d", ip[ix]);
12845+                       buffer += len;
12846+                       maxBufferSize -= len;
12847+               }
12848+               if (maxBufferSize < 10)
12849+                       return 0;                       /* Getting too close, you lose */
12850+               *buffer++ = ':';
12851+               --maxBufferSize;
12852+       }
12853+       /*
12854+        * Finish up the tag with a semi-colon and turn it into a C string
12855+        */
12856+       --buffer;
12857+       *buffer++ = ';'; //replace last ':'
12858+       *buffer++ = '\0';
12859+       --maxBufferSize;
12860+       return buffer - cp;
12861+}
12862+
12863+//converts a header to to a DK_TRACE table
12864+extern int
12865+dkt_hdrtotrace(char *ptr, DK_TRACE * store)
12866+{
12867+       char           *values[4];      // hHbB
12868+       int             idx;
12869+       int             delim_count;
12870+       char           *sptr, *eptr;
12871+       DK_TRACE_TYPE   dk_trace_tag[4] = {
12872+               DKT_RAW_HEADER,
12873+               DKT_CANON_HEADER,
12874+               DKT_RAW_BODY,
12875+               DKT_CANON_BODY
12876+       };
12877+       int            *ip;
12878+
12879+       if ((strncasecmp(ptr, "DomainKey-Trace:", 16)) || !store || (!dkt_parselist(ptr + 16, "hHbB", values))) {
12880+               return 0;
12881+       }
12882+       for (idx = 0; idx < 4; idx++) {
12883+               if (!values[idx])
12884+                       continue;
12885+               ip = getPointer(store, dk_trace_tag[idx]);
12886+               if (!ip)
12887+                       return 0;
12888+               sptr = values[idx];
12889+               for (delim_count = 0; ((delim_count < 256) && (*sptr != '\0')); sptr++) {
12890+                       if (*sptr == ':') {
12891+                               delim_count++;
12892+                               continue;
12893+                       }
12894+               //find the end of the int
12895+                       for (eptr = sptr + 1; ((*eptr != ':') && (*eptr != '\0')); eptr++);
12896+                       if (*eptr == '\0') //if end of values for key finish up
12897+                       {
12898+                               ip[delim_count] = atoi(sptr);
12899+                               break;
12900+                       }
12901+                       *eptr = '\0';
12902+                       ip[delim_count] = atoi(sptr);
12903+                       delim_count++;
12904+                       sptr = eptr;
12905+               }
12906+       }
12907+       return 1;
12908+}
12909+#endif
12910+
12911+void
12912+getversion_dktrace_c()
12913+{
12914+       static char    *x = "$Id: dktrace.c,v 1.1 2009-03-14 09:00:52+05:30 Cprogrammer Stab mbhangui $";
12915+
12916+       x++;
12917+}
12918diff -ruN ../netqmail-1.06-original/dktrace.h netqmail-1.06/dktrace.h
12919--- ../netqmail-1.06-original/dktrace.h 1970-01-01 01:00:00.000000000 +0100
12920+++ netqmail-1.06/dktrace.h     2019-02-27 20:57:13.389025081 +0100
12921@@ -0,0 +1,26 @@
12922+/* $Id: dktrace.h,v 1.3 2005/06/27 18:47:57 ted46045 Exp $ */
12923+
12924+#ifndef _DK_TRACE_H
12925+#define _DK_TRACE_H
12926+
12927+typedef struct {
12928+  int ccounts_h[256];
12929+  int ccounts_H[256];
12930+  int ccounts_b[256];
12931+  int ccounts_B[256];
12932+} DK_TRACE;
12933+
12934+typedef enum { DKT_RAW_HEADER='h', DKT_CANON_HEADER='H',
12935+         DKT_RAW_BODY='b', DKT_CANON_BODY='B' } DK_TRACE_TYPE;
12936+
12937+#define dkt_init(s) memset(s,0,sizeof(DK_TRACE))
12938+
12939+//extern void   dkt_init(DK_TRACE *dkp);
12940+extern void   dkt_add(DK_TRACE *dkp, DK_TRACE_TYPE type, const unsigned char *data, int dataLength);
12941+extern int    dkt_diff(DK_TRACE *dka, DK_TRACE *dkb, DK_TRACE_TYPE type, DK_TRACE *table);
12942+extern void   dkt_quickadd(DK_TRACE *dkp, DK_TRACE_TYPE type, int index, int count);
12943+extern int    dkt_getcount(DK_TRACE *dkp, DK_TRACE_TYPE type, int index, int count);
12944+extern int    dkt_generate(DK_TRACE *dkp, DK_TRACE_TYPE type, char *buffer, int maxBufferSize);
12945+extern int    dkt_hdrtotrace(char *ptr, DK_TRACE *store);
12946+
12947+#endif
12948diff -ruN ../netqmail-1.06-original/dns.c netqmail-1.06/dns.c
12949--- ../netqmail-1.06-original/dns.c     2007-11-30 21:22:54.000000000 +0100
12950+++ netqmail-1.06/dns.c 2019-04-09 20:53:51.981528338 +0200
12951@@ -1,4 +1,3 @@
12952-#include <stdio.h>
12953 #include <netdb.h>
12954 #include <sys/types.h>
12955 #include <netinet/in.h>
12956@@ -9,6 +8,7 @@
12957 extern int res_search();
12958 #include "ip.h"
12959 #include "ipalloc.h"
12960+#include "strsalloc.h"
12961 #include "fmt.h"
12962 #include "alloc.h"
12963 #include "str.h"
12964@@ -19,14 +19,17 @@
12965 static unsigned short getshort(c) unsigned char *c;
12966 { unsigned short u; u = c[0]; return (u << 8) + c[1]; }
12967 
12968-static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response;
12969+static struct { unsigned char *buf; } response;
12970+static int responsebuflen = 0;
12971 static int responselen;
12972 static unsigned char *responseend;
12973 static unsigned char *responsepos;
12974+static u_long saveresoptions;
12975 
12976 static int numanswers;
12977 static char name[MAXDNAME];
12978 static struct ip_address ip;
12979+static stralloc txt = {0};
12980 unsigned short pref;
12981 
12982 static stralloc glue = {0};
12983@@ -43,18 +46,33 @@
12984  errno = 0;
12985  if (!stralloc_copy(&glue,domain)) return DNS_MEM;
12986  if (!stralloc_0(&glue)) return DNS_MEM;
12987- responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response));
12988+ if (!responsebuflen)
12989+  if (response.buf = (unsigned char *)alloc(PACKETSZ+1))
12990+   responsebuflen = PACKETSZ+1;
12991+   else return DNS_MEM;
12992+
12993+ responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
12994+ if ((responselen >= responsebuflen) ||
12995+     (responselen > 0 && (((HEADER *)response.buf)->tc)))
12996+  {
12997+   if (responsebuflen < 65536)
12998+    if (alloc_re(&response.buf, responsebuflen, 65536))
12999+     responsebuflen = 65536;
13000+    else return DNS_MEM;
13001+    saveresoptions = _res.options;
13002+    _res.options |= RES_USEVC;
13003+    responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
13004+    _res.options = saveresoptions;
13005+  }
13006  if (responselen <= 0)
13007   {
13008    if (errno == ECONNREFUSED) return DNS_SOFT;
13009    if (h_errno == TRY_AGAIN) return DNS_SOFT;
13010    return DNS_HARD;
13011   }
13012- if (responselen >= sizeof(response))
13013-   responselen = sizeof(response);
13014  responseend = response.buf + responselen;
13015  responsepos = response.buf + sizeof(HEADER);
13016- n = ntohs(response.hdr.qdcount);
13017+ n = ntohs(((HEADER *)response.buf)->qdcount);
13018  while (n-- > 0)
13019   {
13020    i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
13021@@ -64,7 +82,7 @@
13022    if (i < QFIXEDSZ) return DNS_SOFT;
13023    responsepos += QFIXEDSZ;
13024   }
13025- numanswers = ntohs(response.hdr.ancount);
13026+ numanswers = ntohs(((HEADER *)response.buf)->ancount);
13027  return 0;
13028 }
13029 
13030@@ -177,6 +195,49 @@
13031  return 0;
13032 }
13033 
13034+static int findtxt(wanttype)
13035+int wanttype;
13036+{
13037+ unsigned short rrtype;
13038+ unsigned short rrdlen;
13039+ int i;
13040+
13041+ if (numanswers <= 0) return 2;
13042+ --numanswers;
13043+ if (responsepos == responseend) return DNS_SOFT;
13044+
13045+ i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
13046+ if (i < 0) return DNS_SOFT;
13047+ responsepos += i;
13048+
13049+ i = responseend - responsepos;
13050+ if (i < 4 + 3 * 2) return DNS_SOFT;
13051+   
13052+ rrtype = getshort(responsepos);
13053+ rrdlen = getshort(responsepos + 8);
13054+ responsepos += 10;
13055+
13056+ if (rrtype == wanttype)
13057+  {
13058+   unsigned short txtpos;
13059+   unsigned char txtlen;
13060+
13061+   txt.len = 0;
13062+   for (txtpos = 0;txtpos < rrdlen;txtpos += txtlen)
13063+    {
13064+     txtlen = responsepos[txtpos++];
13065+     if (txtlen > rrdlen-txtpos) txtlen = rrdlen-txtpos;
13066+     if (!stralloc_catb(&txt,&responsepos[txtpos],txtlen)) return DNS_MEM;
13067+    }
13068+
13069+   responsepos += rrdlen;
13070+   return 1;
13071+ }
13072+
13073+ responsepos += rrdlen;
13074+ return 0;
13075+}
13076+
13077 void dns_init(flagsearch)
13078 int flagsearch;
13079 {
13080@@ -194,7 +255,7 @@
13081    if (!sa->len) return loop;
13082    if (sa->s[sa->len - 1] == ']') return loop;
13083    if (sa->s[sa->len - 1] == '.') { --sa->len; continue; }
13084-   switch(resolve(sa,T_ANY))
13085+   switch(resolve(sa,T_CNAME))
13086     {
13087      case DNS_MEM: return DNS_MEM;
13088      case DNS_SOFT: return DNS_SOFT;
13089@@ -235,15 +296,18 @@
13090  return len;
13091 }
13092 
13093-int dns_ptr(sa,ip)
13094-stralloc *sa;
13095+static int dns_ptrplus(ssa,ip)
13096+strsalloc *ssa;
13097 struct ip_address *ip;
13098 {
13099+ stralloc sa = {0};
13100  int r;
13101 
13102- if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM;
13103- sa->len = iaafmt(sa->s,ip);
13104- switch(resolve(sa,T_PTR))
13105+ if (!stralloc_ready(&sa,iaafmt((char *) 0,ip))) return DNS_MEM;
13106+ sa.len = iaafmt(sa.s,ip);
13107+ r = resolve(&sa,T_PTR);
13108+ alloc_free(sa.s);
13109+ switch(r)
13110   {
13111    case DNS_MEM: return DNS_MEM;
13112    case DNS_SOFT: return DNS_SOFT;
13113@@ -254,25 +318,49 @@
13114    if (r == DNS_SOFT) return DNS_SOFT;
13115    if (r == 1)
13116     {
13117-     if (!stralloc_copys(sa,name)) return DNS_MEM;
13118-     return 0;
13119+     stralloc sa2 = {0};
13120+     if (!stralloc_copys(&sa2,name)) return DNS_MEM;
13121+     if (!strsalloc_append(ssa,&sa2)) return DNS_MEM;
13122     }
13123   }
13124+ if (ssa->len) return 0;
13125  return DNS_HARD;
13126 }
13127 
13128+int dns_ptr(ssa,ip)
13129+strsalloc *ssa;
13130+struct ip_address *ip;
13131+{
13132+ int r;
13133+ int j;
13134+
13135+ if (!strsalloc_readyplus(ssa,0)) return DNS_MEM;
13136+ ssa->len = 0;
13137+ r = dns_ptrplus(ssa,ip);
13138+ if (r < 0)
13139+  {
13140+   for (j = 0;j < ssa->len;++j)
13141+    alloc_free(ssa->sa[j].s);
13142+   ssa->len = 0;
13143+  }
13144+ return r;
13145+}
13146+
13147+
13148 static int dns_ipplus(ia,sa,pref)
13149 ipalloc *ia;
13150 stralloc *sa;
13151 int pref;
13152 {
13153  int r;
13154- struct ip_mx ix;
13155+ struct ip_mx ix = {0};
13156 
13157  if (!stralloc_copy(&glue,sa)) return DNS_MEM;
13158  if (!stralloc_0(&glue)) return DNS_MEM;
13159  if (glue.s[0]) {
13160+#ifndef IX_FQDN
13161    ix.pref = 0;
13162+#endif
13163    if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
13164     {
13165      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
13166@@ -291,9 +379,16 @@
13167    ix.ip = ip;
13168    ix.pref = pref;
13169    if (r == DNS_SOFT) return DNS_SOFT;
13170-   if (r == 1)
13171+   if (r == 1) {
13172+#ifdef IX_FQDN
13173+     ix.fqdn = glue.s;
13174+#endif
13175      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
13176   }
13177+  }
13178+#ifdef IX_FQDN
13179+ glue.s = 0;
13180+#endif
13181  return 0;
13182 }
13183 
13184@@ -313,7 +408,7 @@
13185 {
13186  int r;
13187  struct mx { stralloc sa; unsigned short p; } *mx;
13188- struct ip_mx ix;
13189+ struct ip_mx ix = {0};
13190  int nummx;
13191  int i;
13192  int j;
13193@@ -325,7 +420,9 @@
13194  if (!stralloc_copy(&glue,sa)) return DNS_MEM;
13195  if (!stralloc_0(&glue)) return DNS_MEM;
13196  if (glue.s[0]) {
13197+#ifndef IX_FQDN
13198    ix.pref = 0;
13199+#endif
13200    if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
13201     {
13202      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
13203@@ -396,3 +493,49 @@
13204  alloc_free(mx);
13205  return flagsoft;
13206 }
13207+
13208+
13209+static int dns_txtplus(ssa,sa)
13210+strsalloc *ssa;
13211+stralloc *sa;
13212+{
13213+ int r;
13214+
13215+ switch(resolve(sa,T_TXT))
13216+  {
13217+   case DNS_MEM: return DNS_MEM;
13218+   case DNS_SOFT: return DNS_SOFT;
13219+   case DNS_HARD: return DNS_HARD;
13220+  }
13221+ while ((r = findtxt(T_TXT)) != 2)
13222+  {
13223+   if (r == DNS_SOFT) return DNS_SOFT;
13224+   if (r == 1)
13225+    {
13226+     stralloc sa = {0};
13227+     if (!stralloc_copy(&sa,&txt)) return DNS_MEM;
13228+     if (!strsalloc_append(ssa,&sa)) return DNS_MEM;
13229+    }
13230+  }
13231+ if (ssa->len) return 0;
13232+ return DNS_HARD;
13233+}
13234+
13235+int dns_txt(ssa,sa)
13236+strsalloc *ssa;
13237+stralloc *sa;
13238+{
13239+ int r;
13240+ int j;
13241+
13242+ if (!strsalloc_readyplus(ssa,0)) return DNS_MEM;
13243+ ssa->len = 0;
13244+ r = dns_txtplus(ssa,sa);
13245+ if (r < 0)
13246+  {
13247+   for (j = 0;j < ssa->len;++j)
13248+    alloc_free(ssa->sa[j].s);
13249+   ssa->len = 0;
13250+  }
13251+ return r;
13252+}
13253diff -ruN ../netqmail-1.06-original/dns.h netqmail-1.06/dns.h
13254--- ../netqmail-1.06-original/dns.h     1998-06-15 12:53:16.000000000 +0200
13255+++ netqmail-1.06/dns.h 2019-02-27 20:57:13.390025070 +0100
13256@@ -10,5 +10,6 @@
13257 int dns_mxip();
13258 int dns_ip();
13259 int dns_ptr();
13260+int dns_txt();
13261 
13262 #endif
13263diff -ruN ../netqmail-1.06-original/dnsfq.c netqmail-1.06/dnsfq.c
13264--- ../netqmail-1.06-original/dnsfq.c   1998-06-15 12:53:16.000000000 +0200
13265+++ netqmail-1.06/dnsfq.c       2019-02-27 20:57:13.390025070 +0100
13266@@ -5,15 +5,19 @@
13267 #include "dnsdoe.h"
13268 #include "ip.h"
13269 #include "ipalloc.h"
13270+#include "strsalloc.h"
13271 #include "exit.h"
13272 
13273 stralloc sa = {0};
13274+strsalloc ssa = {0};
13275 ipalloc ia = {0};
13276 
13277 void main(argc,argv)
13278 int argc;
13279 char **argv;
13280 {
13281+ int j;
13282+
13283  if (!argv[1]) _exit(100);
13284 
13285  if (!stralloc_copys(&sa,argv[1]))
13286@@ -25,8 +29,11 @@
13287   {
13288    substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100);
13289   }
13290- dnsdoe(dns_ptr(&sa,&ia.ix[0].ip));
13291- substdio_putflush(subfdout,sa.s,sa.len);
13292- substdio_putsflush(subfdout,"\n");
13293+ dnsdoe(dns_ptr(&ssa,&ia.ix[0].ip));
13294+ for(j = 0;j < ssa.len;++j)
13295+  {
13296+   substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len);
13297+   substdio_putsflush(subfdout,"\n");
13298+  }
13299  _exit(0);
13300 }
13301diff -ruN ../netqmail-1.06-original/dnsptr.c netqmail-1.06/dnsptr.c
13302--- ../netqmail-1.06-original/dnsptr.c  1998-06-15 12:53:16.000000000 +0200
13303+++ netqmail-1.06/dnsptr.c      2019-02-27 20:57:13.390025070 +0100
13304@@ -6,22 +6,28 @@
13305 #include "dns.h"
13306 #include "dnsdoe.h"
13307 #include "ip.h"
13308+#include "strsalloc.h"
13309 #include "exit.h"
13310 
13311-stralloc sa = {0};
13312+strsalloc ssa = {0};
13313 struct ip_address ip;
13314 
13315 void main(argc,argv)
13316 int argc;
13317 char **argv;
13318 {
13319+ int j;
13320+
13321  if (!argv[1]) _exit(100);
13322 
13323  ip_scan(argv[1],&ip);
13324 
13325  dns_init(0);
13326- dnsdoe(dns_ptr(&sa,&ip));
13327- substdio_putflush(subfdout,sa.s,sa.len);
13328- substdio_putsflush(subfdout,"\n");
13329+ dnsdoe(dns_ptr(&ssa,&ip));
13330+ for(j = 0;j < ssa.len;++j)
13331+  {
13332+   substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len);
13333+   substdio_putsflush(subfdout,"\n");
13334+  }
13335  _exit(0);
13336 }
13337diff -ruN ../netqmail-1.06-original/dnstxt.c netqmail-1.06/dnstxt.c
13338--- ../netqmail-1.06-original/dnstxt.c  1970-01-01 01:00:00.000000000 +0100
13339+++ netqmail-1.06/dnstxt.c      2019-02-27 20:57:13.390025070 +0100
13340@@ -0,0 +1,32 @@
13341+#include "substdio.h"
13342+#include "subfd.h"
13343+#include "stralloc.h"
13344+#include "str.h"
13345+#include "scan.h"
13346+#include "dns.h"
13347+#include "dnsdoe.h"
13348+#include "strsalloc.h"
13349+#include "exit.h"
13350+
13351+strsalloc ssa = {0};
13352+stralloc sa = {0};
13353+
13354+void main(argc,argv)
13355+int argc;
13356+char **argv;
13357+{
13358+ int j;
13359+
13360+ if (!argv[1]) _exit(100);
13361+
13362+ if (!stralloc_copys(&sa, argv[1]))
13363+  { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
13364+ dns_init(0);
13365+ dnsdoe(dns_txt(&ssa,&sa));
13366+ for (j = 0;j < ssa.len;++j)
13367+  {
13368+   substdio_put(subfdout,ssa.sa[j].s,ssa.sa[j].len);
13369+   substdio_putsflush(subfdout,"\n");
13370+  }
13371+ _exit(0);
13372+}
13373diff -ruN ../netqmail-1.06-original/domainkeys.h netqmail-1.06/domainkeys.h
13374--- ../netqmail-1.06-original/domainkeys.h      1970-01-01 01:00:00.000000000 +0100
13375+++ netqmail-1.06/domainkeys.h  2020-04-09 19:45:10.589742433 +0200
13376@@ -0,0 +1,379 @@
13377+/* This file is automatically created from the corresponding .c file */
13378+/* Do not change this file; change the .c file instead. */
13379+/* This is libdomainkeys.  It's Copyright (c) 2004 Yahoo, Inc.
13380+ * This code incorporates intellectual property owned by
13381+ * Yahoo! and licensed pursuant to the Yahoo! DomainKeys Public License
13382+ * Agreement: http://domainkeys.sourceforge.net/license/softwarelicense1-0.html
13383+ */
13384+#include <openssl/evp.h>
13385+#include <openssl/pem.h>
13386+#include <openssl/err.h>
13387+
13388+#ifdef SWIG
13389+%module domainkeys
13390+%{
13391+#include "domainkeys.h"
13392+%}
13393+#endif
13394+
13395+#include "dktrace.h"
13396+
13397+/* Performance/Debug options.
13398+ * Uncomment below or use -D switch in gcc
13399+ * DK_DEBUG Dumps whatever dkhash() hashes in to stderr and turns on
13400+ *  some debug warnings that should never happen
13401+ * DK_HASH_BUFF Enables code that uses a buffer when processing the
13402+ *  canocalized message, reducing calls to the crypto library (from dkhash()),
13403+ *  but can use up slightly more memory
13404+*/
13405+//#define DK_DEBUG 1
13406+#define DK_HASH_BUFF 1
13407+
13408+
13409+#define DKMARK ('D' | 'K'<<8 | 'E'<<16 | 'Y'<<24)
13410+#define DK_SIGNING_SIGN 0
13411+#define DK_SIGNING_VERIFY 1
13412+#define DK_SIGNING_NOSIGN 2
13413+#define DK_SIGNING_NOVERIFY 3
13414+#define DK_MALLOC(s)  OPENSSL_malloc(s)
13415+#define DK_MFREE(s)   OPENSSL_free(s); s = NULL;
13416+#define DKERR(x) ((dk->errline=__LINE__),(dk->errfile=__FILE__),(x))
13417+#define DK_BLOCK 1024 //default size of malloc'd block
13418+
13419+/*
13420+ * Option Flags for dk_setopts
13421+ * OR together or run dk_setopts several times
13422+ * All option flags are OFF by default
13423+*/
13424+#define DKOPT_TRACE_h 0x01 //enables tracking character count in pre-canon header
13425+#define DKOPT_TRACE_H 0x02 //enables tracking character count in post-canon header
13426+#define DKOPT_TRACE_b 0x04 //enables tracking character count in pre-canon body
13427+#define DKOPT_TRACE_B 0x08 //enables tracking character count in post-canon header
13428+#define DKOPT_RDUPE 0x10 //enables skipping duplicate headers when generateing a signature
13429+
13430+typedef enum
13431+{
13432+  DK_STAT_OK, /* Function completed successfully */
13433+  DK_STAT_BADSIG, /* Signature was available but failed to verify against domain specified key */
13434+  DK_STAT_NOSIG, /* No signature available in message */
13435+  DK_STAT_NOKEY, /* No public key available (permanent failure) */
13436+  DK_STAT_BADKEY, /* Unusable key, public if verifying, private if signing */
13437+  DK_STAT_CANTVRFY, /* Cannot get domain key to verify signature (temporary failure) */
13438+  DK_STAT_SYNTAX, /* Message is not valid syntax. Signature could not be created/checked */
13439+  DK_STAT_NORESOURCE, /* Could not get critical resource (temporary failure) */
13440+  DK_STAT_ARGS, /* Arguments are not usable. */
13441+  DK_STAT_REVOKED,    /* Key has been revoked. */
13442+  DK_STAT_INTERNAL, /* cannot call this routine in this context.  Internal error. */
13443+  DK_STAT_GRANULARITY, /* Granularity mismatch: sender doesn't match g= option. */
13444+  DK_STAT_DUPLICATE, /* Duplicate Domainkey-Header */
13445+} DK_STAT;
13446+
13447+typedef enum
13448+{
13449+  DK_FLAG_TESTING = 1,    /* set when in testing mode. */
13450+  DK_FLAG_SIGNSALL = 2,   /* domain signs all outgoing email. */
13451+  DK_FLAG_SET = 4,    /* flags set from a successful DNS query */
13452+  DK_FLAG_G = 8,    /* g tag was present in the selector. */
13453+} DK_FLAGS;
13454+typedef enum
13455+{
13456+  DK_TXT_KEY = 0,
13457+  DK_TXT_POLICY
13458+} DK_TXT;
13459+
13460+typedef enum
13461+{
13462+  DK_CANON_SIMPLE = 0,
13463+  DK_CANON_NOFWS = 1,
13464+} DK_CANON;
13465+/* STARTSTRUCT */
13466+typedef struct
13467+{
13468+
13469+} DK_LIB;
13470+/* STOPSTRUCT */
13471+
13472+//UnixWare Fix -Tim
13473+/* STARTSTRUCT */
13474+typedef struct
13475+{
13476+} DK;
13477+/* STOPSTRUCT */
13478+
13479+
13480+/* returns the source file from which an error was returned. */
13481+char * dk_errfile(DK *dk)
13482+;
13483+
13484+
13485+/* returns the source line number from which an error was returned. */
13486+int dk_errline(DK *dk)
13487+;
13488+
13489+
13490+/* Per-process, one-time initialization
13491+ * Returns library structure for subsequent dk_sign or dk_verify calls.
13492+ * Consult statp before using.
13493+ *
13494+ * When terminating the PROCESS its a good idea to call dk_shutdown()
13495+ * When terminating a THREAD it's a good idea to call ERR_remove_state(0); defined in <openssl/err.h>
13496+ * NOTE: DK_LIB pointers are safe to use over multiple threads
13497+ *       DK pointers are NOT safe to use over multiple threads
13498+ */
13499+DK_LIB *dk_init(DK_STAT *statp)
13500+;
13501+
13502+
13503+/* Per-process, one-time cleanup
13504+ * Should be called just before the application ends.
13505+ * the dklib pointer is not valid anymore after this call
13506+ * This function should be called even if dk_init failed.
13507+ * It's safe to call dk_shutdown with a NULL pointer
13508+ */
13509+void dk_shutdown(DK_LIB * dklib)
13510+;
13511+
13512+
13513+/* Set dk options, use instead of dk_remdupe and dk_enable_trace
13514+ * Can be called multiple times.
13515+ * use after dk_sign()/dk_verify()
13516+ *
13517+ * the bits field can be an OR of any of the following
13518+ *DKOPT_TRACE_h Trace pre-canon header
13519+ *DKOPT_TRACE_H Trace post-canon header
13520+ *DKOPT_TRACE_b Trace pre-canon body
13521+ *DKOPT_TRACE_B Trace post-canon body
13522+ *DKOPT_RDUPE   Exclude duplicate headers from hash (Signing only)
13523+ */
13524+DK_STAT dk_setopts(DK *dk, int bits)
13525+;
13526+
13527+
13528+/* returns the int holding the options set
13529+ * See dk_setopts for bit flags
13530+ */
13531+int dk_getopts(DK *dk)
13532+;
13533+
13534+
13535+/* DEPRECATED in favor of calling dk_setopts().
13536+ * Enables character trace tracking
13537+ *
13538+ * use after dk_sign()/dk_verify()
13539+ */
13540+DK_STAT dk_enable_trace(DK *dk)
13541+;
13542+
13543+
13544+/* Prints trace table to *store variable (char string)
13545+ * *dk is the container for the table
13546+ * *store is a pointer to a character array to output to
13547+ * store_size is the size of the character array *store
13548+ *
13549+ */
13550+DK_STAT dk_get_trace(DK *dk, DK_TRACE_TYPE type, char *store, int store_size)
13551+;
13552+
13553+
13554+/* Prints difference trace table to *store variable (char string)
13555+ * *dk is the container for the table
13556+ * *store is a pointer to a character array to output to
13557+ * store_size is the size of the character array *store
13558+ * return DK_STAT_NOSIG if no DK-Trace header was found
13559+ */
13560+DK_STAT dk_compare_trace(DK *dk, DK_TRACE_TYPE type, char *store, int store_size)
13561+;
13562+
13563+
13564+/* Sets the DNS key/policy record manually (no DNS lookup)
13565+ * txtrecord needs to be set to "e=perm;" to force a permanent DNS failure
13566+ * txtrecord needs to be set to "e=temp;" to force a temporary DNS failure
13567+ * Valid DK_TXT types are:
13568+ * DK_TXT_KEY (normal selector record; for <selctor>._domainkey.<domain>)
13569+ * DK_TXT_POLICY (domain policy record; for _domainkey.<domain>)
13570+ */
13571+DK_STAT dk_settxt(DK *dk, DK_TXT recordtype, const char *txtrecord)
13572+;
13573+
13574+
13575+/* Per-message, may be threaded.
13576+ * canon is one of DK_CANON_*.
13577+ * Returns state structure for operation.  Consult statp before using.
13578+ */
13579+DK *dk_sign(DK_LIB *dklib, DK_STAT *statp, int canon)
13580+;
13581+
13582+
13583+/* Per-message, may be threaded.
13584+ * Returns state structure for operation.  Consult statp before using.
13585+ */
13586+DK *dk_verify(DK_LIB *dklib, DK_STAT *statp)
13587+;
13588+
13589+
13590+/* DEPRECATED in favor of calling dk_setopts()
13591+ * set option to remove dupe headers
13592+ * should be called after dk_sign();
13593+ * any int NOT 0 turns dupe removal on
13594+ */
13595+DK_STAT dk_remdupe(DK *dk,int i)
13596+;
13597+
13598+
13599+/* Returns the policy flags belonging to the signing domain.
13600+ * Sender: overrides From:, and the d= entry in the DK-Sig overrides both.
13601+ * If the policy flags were not successfully fetched, DK_FLAG_SET will not
13602+ * be set.
13603+ */
13604+DK_FLAGS dk_policy(DK *dk)
13605+;
13606+
13607+
13608+/* Copies the header names that were signed into the pointer.
13609+ * Returns the number of bytes copied.
13610+ * ptr may be NULL, in which case the bytes are just counted, not copied.
13611+ * Feel free to call this twice; once to get the length, and again to
13612+ * copy the data.
13613+ * NOTE: If the return value is 0 then an error occured.
13614+ *     It's a good idea to check for this
13615+ */
13616+int dk_headers(DK *dk, char *ptr)
13617+;
13618+
13619+
13620+/* Must NOT include dots inserted for SMTP encapsulation.
13621+ * Must NOT include CRLF.CRLF which terminates the message.
13622+ * Otherwise must be exactly that which is sent or received over the SMTP session.
13623+ * May be called multiple times (not necessary to read an entire message into memory).
13624+ */
13625+DK_STAT dk_message(DK *dk, const unsigned char *ptr, size_t len)
13626+;
13627+
13628+
13629+/* DEPRECATED in favor of calling dk_address().
13630+ * Returns a pointer to a null-terminated domain name portion of an RFC 2822 address.
13631+ * If a Sender: was encountered, it returns that domain.  Otherwise,
13632+ * if a From: was encountered, it returns that domain.  Otherwise,
13633+ * return NULL.
13634+ * return NULL if no domain name found in the address.
13635+ * return NULL if the dk is unusable for any reason.
13636+ * return NULL if the address is unusable for any reason.
13637+ */
13638+char *dk_from(DK *dk)
13639+;
13640+
13641+
13642+/* Returns a pointer to the selector name used or NULL if there isn't one
13643+ * Added by rjp
13644+ */
13645+const char *dk_selector(DK *dk)
13646+;
13647+
13648+
13649+/* Returns a pointer to the domain name used or NULL if there isn't one
13650+ */
13651+const char *dk_domain(DK *dk)
13652+;
13653+
13654+
13655+/*
13656+ * Returns a pointer to a string which begins with "N", "S", or "F",
13657+ * corresponding to None, Sender: and From:, respectively.
13658+ * This single character is followed by a null-terminated RFC 2822 address.
13659+ * The first character is "N" if no valid address has been seen yet,
13660+ * "S" if the address came from the Sender: field, and "F" if the
13661+ * address came from the From: field.
13662+ */
13663+char *dk_address(DK *dk)
13664+;
13665+
13666+
13667+/*
13668+ * Returns a pointer to a null-terminated string containing the granularity
13669+ * value found in the selector DNS record, if any, but only after dk_end
13670+ * has been called. Otherwise returns NULL.
13671+ */
13672+char *dk_granularity(DK *dk)
13673+;
13674+
13675+
13676+/*
13677+ * Called at end-of-message (before response to DATA-dot, if synchronous with SMTP session).
13678+ * If verifying, returns signature validity.
13679+ * This does not calculate the signature.  Call dk_getsig() for that.
13680+ * Flags are returned indirectly through dkf.
13681+ * If you pass in NULL for dkf, the flags will not be fetched.
13682+ * If there is a DK-Sig line, the d= entry will be used to fetch the flags.
13683+ * Otherwise the Sender: domain will be used to fetch the flags.
13684+ * Otherwise the From: domain will be used to fetch the flags.
13685+ *
13686+ * NOTE: If for some reason dk_end() returns an error (!DK_STAT_OK) dk_policy() should be called
13687+ * to get the domain signing policy (o=) and handle accordingly.
13688+ * dkf (selector flags) wont be set if dk_end() returns
13689+ * DK_STAT_NOSIG
13690+ * DK_STAT_NOKEY
13691+ * DK_STAT_SYNTAX
13692+ * DK_STAT_NORESOURCE
13693+ * DK_STAT_BADKEY
13694+ * DK_STAT_CANTVERIFY
13695+ */
13696+DK_STAT dk_end(DK *dk, DK_FLAGS *dkf)
13697+;
13698+
13699+
13700+/*
13701+ * DEPRECATED in favor of calling dk_end and dk_policy() directly.
13702+ * If you pass in NULL for dkf, the policy flags will not be fetched.
13703+ * If the message verified okay, the policy flags will not be fetched.
13704+ */
13705+DK_STAT dk_eom(DK *dk, DK_FLAGS *dkf)
13706+;
13707+
13708+
13709+/*
13710+ *
13711+ * privatekey is the private key used to create the signature; It should contain
13712+ * the entire contents of a PEM-format private key file, thusly it will begin with
13713+ * -----BEGIN RSA PRIVATE KEY-----.  It should be null-terminated.
13714+ */
13715+size_t dk_siglen(void *privatekey)
13716+;
13717+
13718+
13719+/*
13720+ * Sets buf to a null-terminated string.
13721+ * If the message is being signed, signature is stored in the buffer.
13722+ * If the message is being verified, returns DK_STAT_INTERNAL.
13723+ * privatekey is the private key used to create the signature; It should contain
13724+ * the entire contents of a PEM-format private key file, thus it will begin with
13725+ * -----BEGIN RSA PRIVATE KEY-----.  It should be null-terminated.
13726+ * If you pass in NULL for buf, you'll get back DK_STAT_NORESOURCE.
13727+ * If len is not big enough, you'll get back DK_STAT_NORESOURCE.
13728+ */
13729+DK_STAT dk_getsig(DK *dk, void *privatekey, unsigned char buf[], size_t len)
13730+;
13731+
13732+
13733+/*
13734+ * Free all resources associated with this message.
13735+ * dk is no longer usable.
13736+ * if doClearErrState != 0, the OpenSSL ErrorState is freed.
13737+ * Set clearErrState=0 if you use other openssl functions and
13738+ * want to call openssl's ERR_remove_state(0) by yourself
13739+ * ERR_remove_state(0) is declared in <openssl/err.h>
13740+ */
13741+DK_STAT dk_free(DK *dk, int doClearErrState)
13742+;
13743+
13744+
13745+/*
13746+ * return a pointer to a string which describes st.
13747+ * The string is structured.  All the characters up to the first colon
13748+ * contain the name of the DK_STAT constant.  From there to the end of
13749+ * string is a human-readable description of the error.
13750+ */
13751+const char *DK_STAT_to_string(DK_STAT st)
13752+;
13753+
13754+
13755+char           *dns_text(char *);
13756diff -ruN ../netqmail-1.06-original/forward.c netqmail-1.06/forward.c
13757--- ../netqmail-1.06-original/forward.c 1998-06-15 12:53:16.000000000 +0200
13758+++ netqmail-1.06/forward.c     2019-02-27 20:57:13.391025058 +0100
13759@@ -6,11 +6,11 @@
13760 #include "strerr.h"
13761 #include "substdio.h"
13762 #include "fmt.h"
13763+#include "stralloc.h"
13764+#include "srs.h"
13765 
13766 #define FATAL "forward: fatal: "
13767 
13768-void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
13769-
13770 struct qmail qqt;
13771 
13772 int mywrite(fd,buf,len) int fd; char *buf; int len;
13773@@ -42,6 +42,16 @@
13774   dtline = env_get("DTLINE");
13775   if (!dtline)
13776     strerr_die2x(100,FATAL,"DTLINE not set");
13777+
13778+  if (str_len(sender)) {
13779+    switch(srsforward(sender)) {
13780+      case -3: strerr_die2x(100,FATAL,srs_error.s); break;
13781+      case -2: strerr_die2x(111,FATAL,"out of memory"); break;
13782+      case -1: strerr_die2x(111,FATAL,"unable to read controls"); break;
13783+      case 0: break; // nothing
13784+      case 1: sender = srs_result.s; break;
13785+    }
13786+  }
13787 
13788   if (qmail_open(&qqt) == -1)
13789     strerr_die2sys(111,FATAL,"unable to fork: ");
13790diff -ruN ../netqmail-1.06-original/global.h netqmail-1.06/global.h
13791--- ../netqmail-1.06-original/global.h  1970-01-01 01:00:00.000000000 +0100
13792+++ netqmail-1.06/global.h      2019-02-27 20:57:13.391025058 +0100
13793@@ -0,0 +1,51 @@
13794+/* GLOBAL.H - RSAREF types and constants */
13795+
13796+#include <string.h>
13797+#include "uint32.h"
13798+
13799+/* Copyright (C) RSA Laboratories, a division of RSA Data Security,
13800+     Inc., created 1991. All rights reserved.
13801+ */
13802+
13803+#ifndef _GLOBAL_H_
13804+#define _GLOBAL_H_ 1
13805+
13806+/* PROTOTYPES should be set to one if and only if the compiler supports
13807+     function argument prototyping.
13808+   The following makes PROTOTYPES default to 1 if it has not already been
13809+     defined as 0 with C compiler flags.
13810+ */
13811+#ifndef PROTOTYPES
13812+#define PROTOTYPES 1
13813+#endif
13814+
13815+/* POINTER defines a generic pointer type */
13816+typedef unsigned char *POINTER;
13817+
13818+/* UINT2 defines a two byte word */
13819+typedef unsigned short int UINT2;
13820+
13821+/* UINT4 defines a four byte word */
13822+#ifdef UINT32_H
13823+#define UINT4 uint32
13824+#endif
13825+
13826+#ifndef NULL_PTR
13827+#define NULL_PTR ((POINTER)0)
13828+#endif
13829+
13830+#ifndef UNUSED_ARG
13831+#define UNUSED_ARG(x) x = *(&x);
13832+#endif
13833+
13834+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
13835+   If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
13836+     returns an empty list. 
13837+ */
13838+#if PROTOTYPES
13839+#define PROTO_LIST(list) list
13840+#else
13841+#define PROTO_LIST(list) ()
13842+#endif
13843+
13844+#endif /* end _GLOBAL_H_ */
13845diff -ruN ../netqmail-1.06-original/hier.c netqmail-1.06/hier.c
13846--- ../netqmail-1.06-original/hier.c    1998-06-15 12:53:16.000000000 +0200
13847+++ netqmail-1.06/hier.c        2019-06-26 16:39:31.573826970 +0200
13848@@ -4,6 +4,9 @@
13849 #include "fmt.h"
13850 #include "fifo.h"
13851 
13852+#include <stdio.h>
13853+#include "channels.h"
13854+
13855 char buf[100 + FMT_ULONG];
13856 
13857 void dsplit(base,uid,mode)
13858@@ -29,9 +32,12 @@
13859 
13860 void hier()
13861 {
13862+  int cc;
13863+
13864   h(auto_qmail,auto_uido,auto_gidq,0755);
13865 
13866   d(auto_qmail,"control",auto_uido,auto_gidq,0755);
13867+  d(auto_qmail,"control/cache",89,89,0755);
13868   d(auto_qmail,"users",auto_uido,auto_gidq,0755);
13869   d(auto_qmail,"bin",auto_uido,auto_gidq,0755);
13870   d(auto_qmail,"boot",auto_uido,auto_gidq,0755);
13871@@ -55,10 +61,20 @@
13872   d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700);
13873 
13874   dsplit("queue/mess",auto_uidq,0750);
13875+  dsplit("queue/todo",auto_uidq,0750);
13876+  dsplit("queue/intd",auto_uidq,0700);
13877   dsplit("queue/info",auto_uids,0700);
13878   dsplit("queue/local",auto_uids,0700);
13879   dsplit("queue/remote",auto_uids,0700);
13880 
13881+  for (cc = 0;cc < SUPPL_CHANNELS;++cc)
13882+  {
13883+      char adbuf[100];
13884+
13885+      sprintf(adbuf,"queue/" QDIR_BASENAME "%d", cc);
13886+      dsplit(adbuf,auto_uids,0700);
13887+  }
13888+
13889   d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750);
13890   z(auto_qmail,"queue/lock/tcpto",1024,auto_uidr,auto_gidq,0644);
13891   z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600);
13892@@ -89,6 +105,7 @@
13893   c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644);
13894   c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644);
13895   c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644);
13896+  c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644);
13897   c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644);
13898   c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644);
13899   c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644);
13900@@ -104,13 +121,26 @@
13901   c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700);
13902   c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711);
13903   c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711);
13904+  c(auto_qmail,"bin","spawn-filter",auto_uido,auto_gidq,0711);
13905+  c(auto_qmail,"bin","surblfilter",auto_uido,auto_gidq,0711);
13906+  c(auto_qmail,"bin","dk-filter",auto_uido,auto_gidq,0555);
13907+  c(auto_qmail,"bin","dknewkey", auto_uido, auto_gidq, 0711);
13908+  c(auto_qmail,"bin","dktest",auto_uido,auto_gidq,0711);
13909+  c(auto_qmail,"bin","surblqueue",auto_uido,auto_gidq,0555);
13910   c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711);
13911   c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
13912   c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
13913   c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
13914+#ifdef EXTERNAL_TODO
13915+  c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711);
13916+#endif
13917+  c(auto_qmail,"bin","qmail-dk",auto_uidq,auto_gidq,0711);
13918+  c(auto_qmail,"bin","qmail-dkim",auto_uidq,auto_gidq,0711);
13919+  c(auto_qmail,"bin","dkim",auto_uidq,auto_gidq,0711);
13920   c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
13921   c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
13922   c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
13923+  c(auto_qmail,"bin","qmail-newmvrt",auto_uido,auto_gidq,0700);
13924   c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711);
13925   c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755);
13926   c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755);
13927@@ -127,6 +157,7 @@
13928   c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
13929   c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
13930   c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
13931+  c(auto_qmail,"bin","srsfilter",auto_uido,auto_gidq,0755);
13932   c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
13933   c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
13934   c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
13935@@ -143,6 +174,9 @@
13936   c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755);
13937   c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755);
13938   c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755);
13939+#ifdef TLS
13940+  c(auto_qmail,"bin","update_tmprsadh",auto_uido,auto_gidq,0755);
13941+#endif
13942 
13943   c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644);
13944   c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644);
13945@@ -198,6 +232,18 @@
13946   c(auto_qmail,"man/cat1","tcp-env.0",auto_uido,auto_gidq,0644);
13947 
13948   c(auto_qmail,"man/man8","qmail-local.8",auto_uido,auto_gidq,0644);
13949+  c(auto_qmail,"man/man8","qmail-dk.8",auto_uido,auto_gidq,0644);
13950+  c(auto_qmail,"man/cat8","qmail-dk.0",auto_uido,auto_gidq,0644);
13951+  c(auto_qmail,"man/man8","dkim.8",auto_uido,auto_gidq,0644);
13952+  c(auto_qmail,"man/man8","qmail-dkim.8",auto_uido,auto_gidq,0644);
13953+  c(auto_qmail,"man/cat8","qmail-dkim.0",auto_uido,auto_gidq,0644);
13954+  c(auto_qmail,"man/man8","dk-filter.8",auto_uido,auto_gidq,0644);
13955+  c(auto_qmail,"man/cat8","dk-filter.0",auto_uido,auto_gidq,0644);
13956+  c(auto_qmail,"man/man8","dktest.8",auto_uido,auto_gidq,0644);
13957+  c(auto_qmail,"man/man8","surblfilter.8",auto_uido,auto_gidq,0644);
13958+  c(auto_qmail,"man/cat8","surblfilter.0",auto_uido,auto_gidq,0644);
13959+  c(auto_qmail,"man/man8","spawn-filter.8",auto_uido,auto_gidq,0644);
13960+  c(auto_qmail,"man/cat8","spawn-filter.0",auto_uido,auto_gidq,0644);
13961   c(auto_qmail,"man/cat8","qmail-local.0",auto_uido,auto_gidq,0644);
13962   c(auto_qmail,"man/man8","qmail-lspawn.8",auto_uido,auto_gidq,0644);
13963   c(auto_qmail,"man/cat8","qmail-lspawn.0",auto_uido,auto_gidq,0644);
13964diff -ruN ../netqmail-1.06-original/hmac_md5.c netqmail-1.06/hmac_md5.c
13965--- ../netqmail-1.06-original/hmac_md5.c        1970-01-01 01:00:00.000000000 +0100
13966+++ netqmail-1.06/hmac_md5.c    2019-02-27 20:57:13.391025058 +0100
13967@@ -0,0 +1,76 @@
13968+#include "global.h"
13969+#include "md5.h"
13970+
13971+/*
13972+** Function: hmac_md5
13973+*/
13974+
13975+void hmac_md5(text, text_len, key, key_len, digest)
13976+unsigned char*  text;                /* pointer to data stream */
13977+int             text_len;            /* length of data stream */
13978+unsigned char*  key;                 /* pointer to authentication key */
13979+int             key_len;             /* length of authentication key */
13980+unsigned char   *digest;              /* caller digest to be filled in */
13981+
13982+{
13983+        MD5_CTX context;
13984+        unsigned char k_ipad[65];    /* inner padding -
13985+                                      * key XORd with ipad
13986+                                      */
13987+        unsigned char k_opad[65];    /* outer padding -
13988+                                      * key XORd with opad
13989+                                      */
13990+        unsigned char tk[16];
13991+        int i;
13992+        /* if key is longer than 64 bytes reset it to key=MD5(key) */
13993+        if (key_len > 64) {
13994+
13995+                MD5_CTX      tctx;
13996+
13997+                MD5Init(&tctx);
13998+                MD5Update(&tctx, key, key_len);
13999+                MD5Final(tk, &tctx);
14000+
14001+                key = tk;
14002+                key_len = 16;
14003+        }
14004+
14005+        /*
14006+         * the HMAC_MD5 transform looks like:
14007+         *
14008+         * MD5(K XOR opad, MD5(K XOR ipad, text))
14009+         *
14010+         * where K is an n byte key
14011+         * ipad is the byte 0x36 repeated 64 times
14012+         * opad is the byte 0x5c repeated 64 times
14013+         * and text is the data being protected
14014+         */
14015+
14016+        /* start out by storing key in pads */
14017+        bzero( k_ipad, sizeof k_ipad);
14018+        bzero( k_opad, sizeof k_opad);
14019+        bcopy( key, k_ipad, key_len);
14020+        bcopy( key, k_opad, key_len);
14021+
14022+        /* XOR key with ipad and opad values */
14023+        for (i=0; i<64; i++) {
14024+                k_ipad[i] ^= 0x36;
14025+                k_opad[i] ^= 0x5c;
14026+        }
14027+        /*
14028+         * perform inner MD5
14029+         */
14030+        MD5Init(&context);                   /* init context for 1st pass */
14031+        MD5Update(&context, k_ipad, 64);      /* start with inner pad */
14032+        MD5Update(&context, text, text_len); /* then text of datagram */
14033+        MD5Final(digest, &context);          /* finish up 1st pass */
14034+        /*
14035+         * perform outer MD5
14036+         */
14037+        MD5Init(&context);                   /* init context for 2nd
14038+                                              * pass */
14039+        MD5Update(&context, k_opad, 64);     /* start with outer pad */
14040+        MD5Update(&context, digest, 16);     /* then results of 1st
14041+                                              * hash */
14042+        MD5Final(digest, &context);          /* finish up 2nd pass */
14043+}
14044diff -ruN ../netqmail-1.06-original/hmac_md5.h netqmail-1.06/hmac_md5.h
14045--- ../netqmail-1.06-original/hmac_md5.h        1970-01-01 01:00:00.000000000 +0100
14046+++ netqmail-1.06/hmac_md5.h    2019-02-27 20:57:13.391025058 +0100
14047@@ -0,0 +1,11 @@
14048+
14049+/* prototypes */
14050+
14051+void hmac_md5( unsigned char* text, int text_len, unsigned char* key, int key_len, unsigned char* digest);
14052+
14053+/* pointer to data stream */
14054+/* length of data stream */
14055+/* pointer to authentication key */
14056+/* length of authentication key */
14057+/* caller digest to be filled in */
14058+
14059diff -ruN ../netqmail-1.06-original/install-big.c netqmail-1.06/install-big.c
14060--- ../netqmail-1.06-original/install-big.c     1998-06-15 12:53:16.000000000 +0200
14061+++ netqmail-1.06/install-big.c 2019-06-26 16:39:31.574826959 +0200
14062@@ -4,6 +4,9 @@
14063 #include "fmt.h"
14064 #include "fifo.h"
14065 
14066+#include <stdio.h>
14067+#include "channels.h"
14068+
14069 char buf[100 + FMT_ULONG];
14070 
14071 void dsplit(base,uid,mode)
14072@@ -29,6 +32,8 @@
14073 
14074 void hier()
14075 {
14076+  int cc;
14077+
14078   h(auto_qmail,auto_uido,auto_gidq,0755);
14079 
14080   d(auto_qmail,"control",auto_uido,auto_gidq,0755);
14081@@ -59,6 +64,14 @@
14082   dsplit("queue/local",auto_uids,0700);
14083   dsplit("queue/remote",auto_uids,0700);
14084 
14085+  for (cc = 0;cc < SUPPL_CHANNELS;++cc)
14086+  {
14087+      char adbuf[100];
14088+
14089+      sprintf(adbuf,"queue/" QDIR_BASENAME "%d", cc);
14090+      dsplit(adbuf,auto_uids,0700);
14091+  }
14092+
14093   d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750);
14094   z(auto_qmail,"queue/lock/tcpto",1024,auto_uidr,auto_gidq,0644);
14095   z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600);
14096@@ -89,6 +102,7 @@
14097   c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644);
14098   c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644);
14099   c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644);
14100+  c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644);
14101   c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644);
14102   c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644);
14103   c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644);
14104@@ -108,9 +122,13 @@
14105   c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
14106   c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
14107   c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
14108+#ifdef EXTERNAL_TODO
14109+  c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711);
14110+#endif
14111   c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
14112   c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
14113   c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
14114+  c(auto_qmail,"bin","qmail-newmvrt",auto_uido,auto_gidq,0700);
14115   c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711);
14116   c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755);
14117   c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755);
14118@@ -133,6 +151,7 @@
14119   c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755);
14120   c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755);
14121   c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755);
14122+  c(auto_qmail,"bin","srsfilter",auto_uido,auto_gidq,0755);
14123   c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755);
14124   c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755);
14125   c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755);
14126diff -ruN ../netqmail-1.06-original/ip.h netqmail-1.06/ip.h
14127--- ../netqmail-1.06-original/ip.h      1998-06-15 12:53:16.000000000 +0200
14128+++ netqmail-1.06/ip.h  2019-02-27 20:57:13.392025048 +0100
14129@@ -2,6 +2,7 @@
14130 #define IP_H
14131 
14132 struct ip_address { unsigned char d[4]; } ;
14133+typedef struct  ip_address ip_addr;
14134 
14135 extern unsigned int ip_fmt();
14136 #define IPFMT 19
14137diff -ruN ../netqmail-1.06-original/ipalloc.h netqmail-1.06/ipalloc.h
14138--- ../netqmail-1.06-original/ipalloc.h 1998-06-15 12:53:16.000000000 +0200
14139+++ netqmail-1.06/ipalloc.h     2019-02-27 20:57:13.392025048 +0100
14140@@ -3,7 +3,15 @@
14141 
14142 #include "ip.h"
14143 
14144+#ifdef TLS
14145+# define IX_FQDN 1
14146+#endif
14147+
14148+#ifdef IX_FQDN
14149+struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ;
14150+#else
14151 struct ip_mx { struct ip_address ip; int pref; } ;
14152+#endif
14153 
14154 #include "gen_alloc.h"
14155 
14156diff -ruN ../netqmail-1.06-original/ipme.c netqmail-1.06/ipme.c
14157--- ../netqmail-1.06-original/ipme.c    2007-11-30 21:22:54.000000000 +0100
14158+++ netqmail-1.06/ipme.c        2019-02-27 20:57:13.392025048 +0100
14159@@ -14,23 +14,65 @@
14160 #include "ipalloc.h"
14161 #include "stralloc.h"
14162 #include "ipme.h"
14163+#include "substdio.h"
14164+#include "readwrite.h"
14165 
14166 static int ipmeok = 0;
14167 ipalloc ipme = {0};
14168+ipalloc ipme_mask = {0};
14169+ipalloc notipme = {0};
14170+ipalloc notipme_mask = {0};
14171 
14172 int ipme_is(ip)
14173 struct ip_address *ip;
14174 {
14175-  int i;
14176   if (ipme_init() != 1) return -1;
14177-  for (i = 0;i < ipme.len;++i)
14178-    if (byte_equal(&ipme.ix[i].ip,4,ip))
14179-      return 1;
14180-  return 0;
14181+  return ipme_match(&ipme,&ipme_mask,ip) > ipme_match(&notipme,&notipme_mask,ip);
14182 }
14183 
14184+int ipme_match(ipa, ipa_mask, ip)
14185+struct ipalloc *ipa, *ipa_mask;
14186+struct ip_address *ip;
14187+{
14188+  int i,j;
14189+  struct ip_address masked;
14190+  int masklen, longest_masklen=-1;
14191+
14192+  for(i=0;i < ipa->len;++i)
14193+  {
14194+    masklen = 0;
14195+    for(j=0;j<4;++j)
14196+    {
14197+      switch(ipa_mask->ix[i].ip.d[j])
14198+      {
14199+        case 255:  masklen += 8; break;
14200+        case 254:  masklen += 7; break;
14201+        case 252:  masklen += 6; break;
14202+        case 248:  masklen += 5; break;
14203+        case 240:  masklen += 4; break;
14204+        case 224:  masklen += 3; break;
14205+        case 192:  masklen += 2; break;
14206+        case 128:  masklen += 1; break;
14207+        default:   masklen += 0; break;
14208+      }
14209+      if (ipa->ix[i].ip.d[j] != (ip->d[j] & ipa_mask->ix[i].ip.d[j]))
14210+        break;
14211+    }
14212+    if ( (j == 4) && (masklen > longest_masklen) )
14213+    {
14214+      longest_masklen = masklen;
14215+    }
14216+  }
14217+  return longest_masklen;
14218+}
14219 static stralloc buf = {0};
14220 
14221+#define ipme_init_retclean(ret) { \
14222+  if (moreipme.ix) alloc_free(moreipme.ix); \
14223+  if (moreipme_mask.ix) alloc_free(moreipme_mask.ix); \
14224+  if (buf.s) alloc_free(buf.s); \
14225+  return ret; }
14226+   
14227 int ipme_init()
14228 {
14229   struct ifconf ifc;
14230@@ -39,23 +81,45 @@
14231   struct sockaddr_in *sin;
14232   int len;
14233   int s;
14234-  struct ip_mx ix;
14235-
14236+  struct ip_mx ix, ix_mask;
14237+  ipalloc moreipme = {0};
14238+  ipalloc moreipme_mask = {0};
14239+  int i;
14240+
14241   if (ipmeok) return 1;
14242-  if (!ipalloc_readyplus(&ipme,0)) return 0;
14243+  if (!ipalloc_readyplus(&ipme,0)) ipme_init_retclean(0);
14244+  if (!ipalloc_readyplus(&ipme_mask,0)) ipme_init_retclean(0);
14245+  if (!ipalloc_readyplus(&notipme,0)) ipme_init_retclean(0);
14246+  if (!ipalloc_readyplus(&notipme_mask,0)) ipme_init_retclean(0);
14247+  if (!ipalloc_readyplus(&moreipme,0)) ipme_init_retclean(0);
14248+  if (!ipalloc_readyplus(&moreipme_mask,0)) ipme_init_retclean(0);
14249+
14250   ipme.len = 0;
14251-  ix.pref = 0;
14252-
14253-  /* 0.0.0.0 is a special address which always refers to
14254-   * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a.
14255+  ix.pref = ix_mask.pref = 0;
14256+
14257+  if (!ipme_readipfile(&notipme, &notipme_mask, "control/notipme")) ipme_init_retclean(0);
14258+
14259+  /* 127.0.0.0/255.0.0.0 is the localhost network.  Linux will treat
14260+     every address in this range as a local interface, even if it
14261+     isn't explicitly configured.
14262   */
14263+  byte_copy(&ix.ip,4,"\x7f\0\0\0");
14264+  byte_copy(&ix_mask.ip,4,"\xff\0\0\0");
14265+  if (!ipalloc_append(&ipme,&ix)) ipme_init_retclean(0);
14266+  if (!ipalloc_append(&ipme_mask,&ix_mask)) ipme_init_retclean(0);
14267+
14268+  /* 0.0.0.0 is a special address which always refers to
14269+   * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a.  */
14270   byte_copy(&ix.ip,4,"\0\0\0\0");
14271-  if (!ipalloc_append(&ipme,&ix)) { return 0; }
14272-  if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1;
14273-
14274+  byte_copy(&ix_mask.ip,4,"\xff\xff\xff\xff");
14275+  if (!ipalloc_append(&ipme,&ix)) ipme_init_retclean(0);
14276+  if (!ipalloc_append(&ipme_mask,&ix_mask)) ipme_init_retclean(0);
14277+
14278+  if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) ipme_init_retclean(-1);
14279+
14280   len = 256;
14281   for (;;) {
14282-    if (!stralloc_ready(&buf,len)) { close(s); return 0; }
14283+    if (!stralloc_ready(&buf,len)) { close(s); ipme_init_retclean(0); }
14284     buf.len = 0;
14285     ifc.ifc_buf = buf.s;
14286     ifc.ifc_len = len;
14287@@ -64,7 +128,7 @@
14288         buf.len = ifc.ifc_len;
14289         break;
14290       }
14291-    if (len > 200000) { close(s); return -1; }
14292+    if (len > 200000) { close(s);  ipme_init_retclean(-1); }
14293     len += 100 + (len >> 2);
14294   }
14295   x = buf.s;
14296@@ -79,7 +143,10 @@
14297       byte_copy(&ix.ip,4,&sin->sin_addr);
14298       if (ioctl(s,SIOCGIFFLAGS,x) == 0)
14299         if (ifr->ifr_flags & IFF_UP)
14300-          if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; }
14301+        {
14302+          if (!ipalloc_append(&ipme,&ix)) { close(s);  ipme_init_retclean(0); }
14303+          if (!ipalloc_append(&ipme_mask,&ix_mask)) { close(s);  ipme_init_retclean(0); }
14304+        }
14305     }
14306 #else
14307     len = sizeof(*ifr);
14308@@ -89,12 +156,60 @@
14309          if (ifr->ifr_addr.sa_family == AF_INET) {
14310            sin = (struct sockaddr_in *) &ifr->ifr_addr;
14311            byte_copy(&ix.ip,4,&sin->sin_addr);
14312-           if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; }
14313+            if (!ipalloc_append(&ipme,&ix)) { close(s);  ipme_init_retclean(0); }
14314+            if (!ipalloc_append(&ipme_mask,&ix_mask)) { close(s);  ipme_init_retclean(0); }
14315          }
14316 #endif
14317     x += len;
14318   }
14319   close(s);
14320+
14321+  if (!ipme_readipfile(&moreipme, &moreipme_mask, "control/moreipme"))  ipme_init_retclean(0);
14322+  for(i = 0;i < moreipme.len;++i)
14323+  {
14324+    if (!ipalloc_append(&ipme,&moreipme.ix[i])) ipme_init_retclean(0);
14325+    if (!ipalloc_append(&ipme_mask,&moreipme_mask.ix[i])) ipme_init_retclean(0);
14326+  }
14327   ipmeok = 1;
14328-  return 1;
14329+  ipme_init_retclean(1);
14330 }
14331+
14332+
14333+int ipme_readipfile(ipa, ipa_mask, fn)
14334+  ipalloc *ipa, *ipa_mask;
14335+  char *fn;
14336+{
14337+  int fd = -1;
14338+  char inbuf[1024];
14339+  substdio ss;
14340+  stralloc l = {0};
14341+  int match;
14342+  struct ip_mx ix, ix_mask;
14343+  int ret = 1;
14344+  int slash = 0;
14345+
14346+  if ( (fd = open_read(fn)) != -1) {
14347+    substdio_fdbuf(&ss, read, fd, inbuf, sizeof(inbuf));
14348+    while ( (getln(&ss,&l,&match,'\n') != -1) && (match || l.len) ) {
14349+      l.len--;
14350+      if (!stralloc_0(&l)) { ret = 0; break; }
14351+      if (l.s[slash=str_chr(l.s,'/')]!='\0')
14352+      {
14353+        l.s[slash]='\0';
14354+        if (!ip_scan(l.s+slash+1,&ix_mask.ip))
14355+          continue;
14356+      }
14357+      else
14358+        if (!ip_scan("255.255.255.255",&ix_mask.ip)) { ret = 0; break; }
14359+
14360+      if (!ip_scan(l.s, &ix.ip)) continue;
14361+      if (!ipalloc_append(ipa,&ix)) { ret = 0; break; }
14362+      if (!ipalloc_append(ipa_mask,&ix_mask.ip)) { ret = 0; break; }
14363+    }
14364+    if (l.s) alloc_free(l.s);
14365+    if ( (fd >= 0) && (close(fd) == -1) )
14366+      ret = 0;
14367+  }
14368+  return ret;
14369+}
14370+
14371diff -ruN ../netqmail-1.06-original/ipme.h netqmail-1.06/ipme.h
14372--- ../netqmail-1.06-original/ipme.h    1998-06-15 12:53:16.000000000 +0200
14373+++ netqmail-1.06/ipme.h        2019-02-27 20:57:13.392025048 +0100
14374@@ -4,7 +4,7 @@
14375 #include "ip.h"
14376 #include "ipalloc.h"
14377 
14378-extern ipalloc ipme;
14379+extern ipalloc ipme, ipme_mask, notipme, notipme_mask;
14380 
14381 extern int ipme_init();
14382 extern int ipme_is();
14383diff -ruN ../netqmail-1.06-original/ipmeprint.c netqmail-1.06/ipmeprint.c
14384--- ../netqmail-1.06-original/ipmeprint.c       1998-06-15 12:53:16.000000000 +0200
14385+++ netqmail-1.06/ipmeprint.c   2019-02-27 20:57:13.392025048 +0100
14386@@ -3,12 +3,15 @@
14387 #include "ip.h"
14388 #include "ipme.h"
14389 #include "exit.h"
14390+#include "auto_qmail.h"
14391 
14392 char temp[IPFMT];
14393 
14394 void main()
14395 {
14396- int j;
14397+ int j,k;
14398+
14399+ chdir(auto_qmail);
14400  switch(ipme_init())
14401   {
14402    case 0: substdio_putsflush(subfderr,"out of memory\n"); _exit(111);
14403@@ -17,8 +20,18 @@
14404  for (j = 0;j < ipme.len;++j)
14405   {
14406    substdio_put(subfdout,temp,ip_fmt(temp,&ipme.ix[j].ip));
14407-   substdio_puts(subfdout,"\n");
14408+   substdio_puts(subfdout,"/");
14409+   substdio_put(subfdout,temp,ip_fmt(temp,&ipme_mask.ix[j].ip));
14410+   substdio_puts(subfdout," is me\n");
14411+  }
14412+ for (j = 0;j < notipme.len;++j)
14413+  {
14414+   substdio_put(subfdout,temp,ip_fmt(temp,&notipme.ix[j].ip));
14415+   substdio_puts(subfdout,"/");
14416+   substdio_put(subfdout,temp,ip_fmt(temp,&notipme_mask.ix[j].ip));
14417+   substdio_puts(subfdout," is not me\n");
14418   }
14419+
14420  substdio_flush(subfdout);
14421  _exit(0);
14422 }
14423diff -ruN ../netqmail-1.06-original/ipmetest.c netqmail-1.06/ipmetest.c
14424--- ../netqmail-1.06-original/ipmetest.c        1970-01-01 01:00:00.000000000 +0100
14425+++ netqmail-1.06/ipmetest.c    2019-02-27 20:57:13.392025048 +0100
14426@@ -0,0 +1,38 @@
14427+#include "subfd.h"
14428+#include "substdio.h"
14429+#include "ip.h"
14430+#include "ipme.h"
14431+#include "exit.h"
14432+#include "auto_qmail.h"
14433+#include "env.h"
14434+
14435+void main(int argc, char *argv[])
14436+{
14437+  struct ip_address ip;
14438+
14439+  if (!env_get("IPMETEST_HERE"))
14440+    chdir(auto_qmail);
14441+
14442+  if (argc < 2)
14443+  {
14444+    substdio_puts(subfdout,"invalid usage\n");
14445+    substdio_flush(subfdout);
14446+    exit(1);
14447+  }
14448+  if (!ip_scan(argv[1],&ip))
14449+  {
14450+    substdio_puts(subfdout,"invalid IP address\n");
14451+    substdio_flush(subfdout);
14452+    exit(1);
14453+  }
14454+  if (ipme_is(&ip))
14455+  {
14456+    substdio_puts(subfdout,"me\n");
14457+  }
14458+  else
14459+  {
14460+    substdio_puts(subfdout,"not me\n");
14461+  }
14462+  substdio_flush(subfdout);
14463+  exit(0);
14464+}
14465diff -ruN ../netqmail-1.06-original/macros.h netqmail-1.06/macros.h
14466--- ../netqmail-1.06-original/macros.h  1970-01-01 01:00:00.000000000 +0100
14467+++ netqmail-1.06/macros.h      2019-02-27 20:57:13.392025048 +0100
14468@@ -0,0 +1,25 @@
14469+/*
14470+ * $Log: macros.h,v $
14471+ * Revision 1.1  2009-03-21 08:50:25+05:30  Cprogrammer
14472+ * Initial revision
14473+ *
14474+ *
14475+ * macros.h:  Useful macros
14476+ *
14477+ * Author:
14478+ *  Dick Porter (dick@ximian.com)
14479+ *
14480+ * (C) 2002 Ximian, Inc.
14481+ */
14482+
14483+#ifndef _WAPI_MACROS_H_
14484+#define _WAPI_MACROS_H_
14485+
14486+#include <sys/types.h>
14487+
14488+#define MAKEWORD(low, high) ((__uint16_t)(((__uint8_t)(low)) | \
14489+                                      ((__uint16_t)((__uint8_t)(high))) << 8))
14490+#define LOBYTE(i16) ((__uint8_t)((i16) & 0xFF))
14491+#define HIBYTE(i16) ((__uint8_t)(((__uint16_t)(i16) >> 8) & 0xFF))
14492+
14493+#endif                                                 /* _WAPI_MACROS_H_ */
14494diff -ruN ../netqmail-1.06-original/maildirflags.c netqmail-1.06/maildirflags.c
14495--- ../netqmail-1.06-original/maildirflags.c    1970-01-01 01:00:00.000000000 +0100
14496+++ netqmail-1.06/maildirflags.c        2019-02-27 20:57:13.392025048 +0100
14497@@ -0,0 +1,23 @@
14498+/*
14499+** Copyright 2000 Double Precision, Inc.
14500+** See COPYING for distribution information.
14501+*/
14502+
14503+#include       <sys/types.h>
14504+#include       <string.h>
14505+
14506+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
14507+
14508+int maildir_hasflag(const char *filename, char flag)
14509+{
14510+       const char *p=strrchr(filename, '/');
14511+
14512+       if (p)
14513+               filename=p+1;
14514+
14515+       p=strrchr(p, ':');
14516+       if (p && strncmp(p, ":2,", 3) == 0 &&
14517+           strchr(p+3, flag))
14518+               return (1);
14519+       return (0);
14520+}
14521diff -ruN ../netqmail-1.06-original/maildirgetquota.c netqmail-1.06/maildirgetquota.c
14522--- ../netqmail-1.06-original/maildirgetquota.c 1970-01-01 01:00:00.000000000 +0100
14523+++ netqmail-1.06/maildirgetquota.c     2019-02-27 20:57:13.393025036 +0100
14524@@ -0,0 +1,50 @@
14525+/*
14526+** Copyright 1998 - 2000 Double Precision, Inc.
14527+** See COPYING for distribution information.
14528+*/
14529+
14530+#include       "maildirgetquota.h"
14531+#include       "maildirmisc.h"
14532+#if    HAVE_UNISTD_H
14533+#include       <unistd.h>
14534+#endif
14535+#include       <stdlib.h>
14536+#include       <string.h>
14537+#include       <fcntl.h>
14538+#include       <sys/types.h>
14539+#include       <sys/stat.h>
14540+
14541+int    maildir_getquota(const char *dir, char buf[QUOTABUFSIZE])
14542+{
14543+char   *p;
14544+struct stat    stat_buf;
14545+int    n;
14546+int    l;
14547+
14548+       p=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
14549+       if (!p) return (-1);
14550+
14551+       strcat(strcpy(p, dir), "/maildirfolder");
14552+       if (stat(p, &stat_buf) == 0)
14553+       {
14554+               strcat(strcpy(p, dir), "/..");
14555+               n=maildir_getquota(p, buf);
14556+               free(p);
14557+               return (n);
14558+       }
14559+
14560+       strcat(strcpy(p, dir), "/maildirsize");
14561+       n=maildir_safeopen(p, O_RDONLY, 0);
14562+       free(p);
14563+       if (n < 0)      return (n);
14564+       if ((l=read(n, buf, QUOTABUFSIZE-1)) < 0)
14565+       {
14566+               close(n);
14567+               return (-1);
14568+       }
14569+       close(n);
14570+       for (n=0; n<l; n++)
14571+               if (buf[n] == '\n')     break;
14572+       buf[n]=0;
14573+       return (0);
14574+}
14575diff -ruN ../netqmail-1.06-original/maildirgetquota.h netqmail-1.06/maildirgetquota.h
14576--- ../netqmail-1.06-original/maildirgetquota.h 1970-01-01 01:00:00.000000000 +0100
14577+++ netqmail-1.06/maildirgetquota.h     2019-02-27 20:57:13.393025036 +0100
14578@@ -0,0 +1,30 @@
14579+#ifndef        maildirgetquota_h
14580+#define        maildirgetquota_h
14581+
14582+/*
14583+** Copyright 1998 - 1999 Double Precision, Inc.
14584+** See COPYING for distribution information.
14585+*/
14586+
14587+#if    HAVE_CONFIG_H
14588+#include       "config.h"
14589+#endif
14590+
14591+#include       <sys/types.h>
14592+#include       <stdio.h>
14593+
14594+#ifdef  __cplusplus
14595+extern "C" {
14596+#endif
14597+
14598+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 $";
14599+
14600+#define        QUOTABUFSIZE    256
14601+
14602+int maildir_getquota(const char *, char [QUOTABUFSIZE]);
14603+
14604+#ifdef  __cplusplus
14605+}
14606+#endif
14607+
14608+#endif
14609diff -ruN ../netqmail-1.06-original/maildirmisc.h netqmail-1.06/maildirmisc.h
14610--- ../netqmail-1.06-original/maildirmisc.h     1970-01-01 01:00:00.000000000 +0100
14611+++ netqmail-1.06/maildirmisc.h 2019-02-27 20:57:13.393025036 +0100
14612@@ -0,0 +1,145 @@
14613+#ifndef        maildirmisc_h
14614+#define        maildirmisc_h
14615+
14616+/*
14617+** Copyright 2000 Double Precision, Inc.
14618+** See COPYING for distribution information.
14619+*/
14620+
14621+#if    HAVE_CONFIG_H
14622+#include       "config.h"
14623+#endif
14624+
14625+#ifdef  __cplusplus
14626+extern "C" {
14627+#endif
14628+
14629+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 $";
14630+
14631+/*
14632+**
14633+** Miscellaneous maildir-related code
14634+**
14635+*/
14636+
14637+/* Some special folders */
14638+
14639+#define        INBOX   "INBOX"
14640+#define        DRAFTS  "Drafts"
14641+#define        SENT    "Sent"
14642+#define        TRASH   "Trash"
14643+
14644+#define        SHAREDSUBDIR    "shared-folders"
14645+
14646+char *maildir_folderdir(const char *,          /* maildir */
14647+       const char *);                          /* folder name */
14648+       /* Returns the directory corresponding to foldername (foldername is
14649+       ** checked to make sure that it's a valid name, else we set errno
14650+       ** to EINVAL, and return (0).
14651+       */
14652+
14653+char *maildir_filename(const char *,           /* maildir */
14654+       const char *,                           /* folder */
14655+       const char *);                          /* filename */
14656+       /*
14657+       ** Builds the filename to this message, suitable for opening.
14658+       ** If the file doesn't appear to be there, search the maildir to
14659+       ** see if someone changed the flags, and return the current filename.
14660+       */
14661+
14662+int maildir_safeopen(const char *,             /* filename */
14663+       int,                            /* mode */
14664+       int);                           /* perm */
14665+
14666+/*
14667+**     Same arguments as open().  When we're accessing a shared maildir,
14668+**     prevent someone from playing cute and dumping a bunch of symlinks
14669+**     in there.  This function will open the indicate file only if the
14670+**     last component is not a symlink.
14671+**     This is implemented by opening the file with O_NONBLOCK (to prevent
14672+**     a DOS attack of someone pointing the symlink to a pipe, causing
14673+**     the open to hang), clearing O_NONBLOCK, then stat-int the file
14674+**     descriptor, lstating the filename, and making sure that dev/ino
14675+**     match.
14676+*/
14677+
14678+int maildir_semisafeopen(const char *, /* filename */
14679+       int,                            /* mode */
14680+       int);                           /* perm */
14681+
14682+/*
14683+** Same thing, except that we allow ONE level of soft link indirection,
14684+** because we're reading from our own maildir, which points to the
14685+** message in the sharable maildir.
14686+*/
14687+
14688+int maildir_mkdir(const char *);       /* directory */
14689+/*
14690+** Create maildir including all subdirectories in the path (like mkdir -p)
14691+*/
14692+
14693+void maildir_purgetmp(const char *);           /* maildir */
14694+       /* purges old stuff out of tmp */
14695+
14696+void maildir_purge(const char *,               /* directory */
14697+       unsigned);                              /* time_t to purge */
14698+
14699+void maildir_getnew(const char *,              /* maildir */
14700+       const char *);                          /* folder */
14701+       /* move messages from new to cur */
14702+
14703+int maildir_deletefolder(const char *,         /* maildir */
14704+       const char *);                          /* folder */
14705+       /* deletes a folder */
14706+
14707+int maildir_mddelete(const char *);    /* delete a maildir folder by path */
14708+
14709+void maildir_list_sharable(const char *,       /* maildir */
14710+       void (*)(const char *, void *),         /* callback function */
14711+       void *);                                /* 2nd arg to callback func */
14712+       /* list sharable folders */
14713+
14714+int maildir_shared_subscribe(const char *,     /* maildir */
14715+               const char *);                  /* folder */
14716+       /* subscribe to a shared folder */
14717+
14718+void maildir_list_shared(const char *,         /* maildir */
14719+       void (*)(const char *, void *),         /* callback function */
14720+       void *);                        /* 2nd arg to the callback func */
14721+       /* list subscribed folders */
14722+
14723+int maildir_shared_unsubscribe(const char *,   /* maildir */
14724+               const char *);                  /* folder */
14725+       /* unsubscribe from a shared folder */
14726+
14727+char *maildir_shareddir(const char *,          /* maildir */
14728+       const char *);                          /* folder */
14729+       /*
14730+       ** Validate and return a path to a shared folder.  folderdir must be
14731+       ** a name of a valid shared folder.
14732+       */
14733+
14734+void maildir_shared_sync(const char *);                /* maildir */
14735+       /* "sync" the shared folder */
14736+
14737+int maildir_sharedisro(const char *);          /* maildir */
14738+       /* maildir is a shared read-only folder */
14739+
14740+int maildir_unlinksharedmsg(const char *);     /* filename */
14741+       /* Remove a message from a shared folder */
14742+
14743+/* Internal function that reads a symlink */
14744+
14745+char *maildir_getlink(const char *);
14746+
14747+       /* Determine whether the maildir filename has a certain flag */
14748+
14749+int maildir_hasflag(const char *filename, char);
14750+
14751+#define        MAILDIR_DELETED(f)      maildir_hasflag((f), 'T')
14752+
14753+#ifdef  __cplusplus
14754+}
14755+#endif
14756+
14757+#endif
14758diff -ruN ../netqmail-1.06-original/maildiropen.c netqmail-1.06/maildiropen.c
14759--- ../netqmail-1.06-original/maildiropen.c     1970-01-01 01:00:00.000000000 +0100
14760+++ netqmail-1.06/maildiropen.c 2019-02-27 20:57:13.393025036 +0100
14761@@ -0,0 +1,133 @@
14762+/*
14763+** Copyright 2000 Double Precision, Inc.
14764+** See COPYING for distribution information.
14765+*/
14766+
14767+#if HAVE_CONFIG_H
14768+#include "config.h"
14769+#endif
14770+
14771+#include       <sys/types.h>
14772+#include       <sys/stat.h>
14773+#include       <string.h>
14774+#include       <stdlib.h>
14775+#include       <time.h>
14776+#if    HAVE_UNISTD_H
14777+#include       <unistd.h>
14778+#endif
14779+#include       <stdio.h>
14780+#include       <ctype.h>
14781+#include       <errno.h>
14782+#include       <fcntl.h>
14783+
14784+#include       "maildirmisc.h"
14785+
14786+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
14787+
14788+char *maildir_getlink(const char *filename)
14789+{
14790+#if     HAVE_READLINK
14791+size_t bufsiz;
14792+char   *buf;
14793+
14794+       bufsiz=0;
14795+       buf=0;
14796+
14797+       for (;;)
14798+       {
14799+       int     n;
14800+
14801+               if (buf)        free(buf);
14802+               bufsiz += 256;
14803+               if ((buf=malloc(bufsiz)) == 0)
14804+               {
14805+                       perror("malloc");
14806+                       return (0);
14807+               }
14808+               if ((n=readlink(filename, buf, bufsiz)) < 0)
14809+               {
14810+                       free(buf);
14811+                       return (0);
14812+               }
14813+               if (n < bufsiz)
14814+               {
14815+                       buf[n]=0;
14816+                       break;
14817+               }
14818+       }
14819+       return (buf);
14820+#else
14821+       return (0);
14822+#endif
14823+}
14824+
14825+int maildir_semisafeopen(const char *path, int mode, int perm)
14826+{
14827+
14828+#if    HAVE_READLINK
14829+
14830+char   *l=maildir_getlink(path);
14831+
14832+       if (l)
14833+       {
14834+       int     f;
14835+
14836+               if (*l != '/')
14837+               {
14838+               char    *q=malloc(strlen(path)+strlen(l)+2);
14839+               char    *s;
14840+
14841+                       if (!q)
14842+                       {
14843+                               free(l);
14844+                               return (-1);
14845+                       }
14846+
14847+                       strcpy(q, path);
14848+                       if ((s=strchr(q, '/')) != 0)
14849+                               s[1]=0;
14850+                       else    *q=0;
14851+                       strcat(q, l);
14852+                       free(l);
14853+                       l=q;
14854+               }
14855+
14856+               f=maildir_safeopen(l, mode, perm);
14857+
14858+               free(l);
14859+               return (f);
14860+       }
14861+#endif
14862+
14863+       return (maildir_safeopen(path, mode, perm));
14864+}
14865+               
14866+int maildir_safeopen(const char *path, int mode, int perm)
14867+{
14868+struct stat    stat1, stat2;
14869+
14870+int    fd=open(path, mode
14871+#ifdef O_NONBLOCK
14872+                       | O_NONBLOCK
14873+#else
14874+                       | O_NDELAY
14875+#endif
14876+                               , perm);
14877+
14878+       if (fd < 0)     return (fd);
14879+       if (fcntl(fd, F_SETFL, (mode & O_APPEND)) || fstat(fd, &stat1)
14880+           || lstat(path, &stat2))
14881+       {
14882+               close(fd);
14883+               return (-1);
14884+       }
14885+
14886+       if (stat1.st_dev != stat2.st_dev || stat1.st_ino != stat2.st_ino)
14887+       {
14888+               close(fd);
14889+               errno=ENOENT;
14890+               return (-1);
14891+       }
14892+
14893+       return (fd);
14894+}
14895diff -ruN ../netqmail-1.06-original/maildirparsequota.c netqmail-1.06/maildirparsequota.c
14896--- ../netqmail-1.06-original/maildirparsequota.c       1970-01-01 01:00:00.000000000 +0100
14897+++ netqmail-1.06/maildirparsequota.c   2019-02-27 20:57:13.393025036 +0100
14898@@ -0,0 +1,44 @@
14899+/*
14900+** Copyright 1998 - 1999 Double Precision, Inc.
14901+** See COPYING for distribution information.
14902+*/
14903+
14904+#if HAVE_CONFIG_H
14905+#include "config.h"
14906+#endif
14907+#include       "maildirquota.h"
14908+#include       <stdlib.h>
14909+#include       <string.h>
14910+
14911+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
14912+
14913+int maildir_parsequota(const char *n, unsigned long *s)
14914+{
14915+const char *o;
14916+int    yes;
14917+
14918+       if ((o=strrchr(n, '/')) == 0)   o=n;
14919+
14920+       for (; *o; o++)
14921+               if (*o == ':')  break;
14922+       yes=0;
14923+       for ( ; o >= n; --o)
14924+       {
14925+               if (*o == '/')  break;
14926+
14927+               if (*o == ',' && o[1] == 'S' && o[2] == '=')
14928+               {
14929+                       yes=1;
14930+                       o += 3;
14931+                       break;
14932+               }
14933+       }
14934+       if (yes)
14935+       {
14936+               *s=0;
14937+               while (*o >= '0' && *o <= '9')
14938+                       *s= *s*10 + (*o++ - '0');
14939+               return (0);
14940+       }
14941+       return (-1);
14942+}
14943diff -ruN ../netqmail-1.06-original/maildirquota.c netqmail-1.06/maildirquota.c
14944--- ../netqmail-1.06-original/maildirquota.c    1970-01-01 01:00:00.000000000 +0100
14945+++ netqmail-1.06/maildirquota.c        2019-02-27 20:57:13.393025036 +0100
14946@@ -0,0 +1,685 @@
14947+/*
14948+** Copyright 1998 - 2002 Double Precision, Inc.
14949+** See COPYING for distribution information.
14950+*/
14951+
14952+#if HAVE_CONFIG_H
14953+#include "config.h"
14954+#endif
14955+
14956+#include <sys/types.h>
14957+/* #if HAVE_DIRENT_H */
14958+#include <dirent.h>
14959+#define NAMLEN(dirent) strlen((dirent)->d_name)
14960+/* #else
14961+#define dirent direct
14962+#define NAMLEN(dirent) (dirent)->d_namlen
14963+#if HAVE_SYS_NDIR_H
14964+#include <sys/ndir.h>
14965+#endif
14966+#if HAVE_SYS_DIR_H
14967+#include <sys/dir.h>
14968+#endif
14969+#if HAVE_NDIR_H
14970+#include <ndir.h>
14971+#endif
14972+#endif */
14973+#include       <sys/types.h>
14974+/* #if HAVE_SYS_STAT_H */
14975+#include       <sys/stat.h>
14976+/* #endif */
14977+#include       <sys/uio.h>
14978+
14979+#include       "maildirquota.h"
14980+#include       "maildirmisc.h"
14981+#include       <stdio.h>
14982+#include       <stdlib.h>
14983+#include       <string.h>
14984+#include       <errno.h>
14985+/* #if HAVE_FCNTL_H */
14986+#include       <fcntl.h>
14987+/* #endif */
14988+#if    HAVE_UNISTD_H
14989+#include       <unistd.h>
14990+#endif
14991+#include       <time.h>
14992+#include       "numlib.h"
14993+
14994+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
14995+
14996+/* Read the maildirsize file */
14997+
14998+int maildirsize_read(const char *filename,     /* The filename */
14999+       int *fdptr,     /* Keep the file descriptor open */
15000+       off_t *sizeptr, /* Grand total of maildir size */
15001+       unsigned *cntptr, /* Grand total of message count */
15002+       unsigned *nlines, /* # of lines in maildirsize */
15003+       struct stat *statptr)   /* The stats on maildirsize */
15004+{
15005+char buf[5120];
15006+int f;
15007+char *p;
15008+unsigned l;
15009+int n;
15010+int first;
15011+
15012+       if ((f=maildir_safeopen(filename, O_RDWR|O_APPEND, 0)) < 0)
15013+               return (-1);
15014+       p=buf;
15015+       l=sizeof(buf);
15016+
15017+       while (l)
15018+       {
15019+               n=read(f, p, l);
15020+               if (n < 0)
15021+               {
15022+                       close(f);
15023+                       return (-1);
15024+               }
15025+               if (n == 0)     break;
15026+               p += n;
15027+               l -= n;
15028+       }
15029+       if (l == 0 || fstat(f, statptr))        /* maildir too big */
15030+       {
15031+               close(f);
15032+               return (-1);
15033+       }
15034+
15035+       *sizeptr=0;
15036+       *cntptr=0;
15037+       *nlines=0;
15038+       *p=0;
15039+       p=buf;
15040+       first=1;
15041+       while (*p)
15042+       {
15043+       long n=0;
15044+       int c=0;
15045+       char    *q=p;
15046+
15047+               while (*p)
15048+                       if (*p++ == '\n')
15049+                       {
15050+                               p[-1]=0;
15051+                               break;
15052+                       }
15053+
15054+               if (first)
15055+               {
15056+                       first=0;
15057+                       continue;
15058+               }
15059+               sscanf(q, "%ld %d", &n, &c);
15060+               *sizeptr += n;
15061+               *cntptr += c;
15062+               ++ *nlines;
15063+       }
15064+       *fdptr=f;
15065+       return (0);
15066+}
15067+
15068+static char *makenewmaildirsizename(const char *, int *);
15069+static int countcurnew(const char *, time_t *, off_t *, unsigned *);
15070+static int countsubdir(const char *, const char *,
15071+               time_t *, off_t *, unsigned *);
15072+static int statcurnew(const char *, time_t *);
15073+static int statsubdir(const char *, const char *, time_t *);
15074+
15075+#define        MDQUOTA_SIZE    'S'     /* Total size of all messages in maildir */
15076+#define        MDQUOTA_BLOCKS  'B'     /* Total # of blocks for all messages in
15077+                               maildir -- NOT IMPLEMENTED */
15078+#define        MDQUOTA_COUNT   'C'     /* Total number of messages in maildir */
15079+
15080+static int qcalc(off_t s, unsigned n, const char *quota, int *percentage)
15081+{
15082+off_t i;
15083+int    spercentage=0;
15084+int    npercentage=0;
15085+
15086+       errno=ENOSPC;
15087+       while (quota && *quota)
15088+       {
15089+               int x=1;
15090+
15091+               if (*quota < '0' || *quota > '9')
15092+               {
15093+                       ++quota;
15094+                       continue;
15095+               }
15096+               i=0;
15097+               while (*quota >= '0' && *quota <= '9')
15098+                       i=i*10 + (*quota++ - '0');
15099+               switch (*quota) {
15100+               default:
15101+                       if (i < s)
15102+                       {
15103+                               *percentage=100;
15104+                               return (-1);
15105+                       }
15106+
15107+                       /*
15108+                       ** For huge quotas, over 20mb,
15109+                       ** divide numerator & denominator by 1024 to prevent
15110+                       ** an overflow when multiplying by 100
15111+                       */
15112+
15113+                       x=1;
15114+                       if (i > 20000000) x=1024;
15115+
15116+                       spercentage = i ? (s/x) * 100 / (i/x):100;
15117+                       break;
15118+               case 'C':
15119+
15120+                       if (i < n)
15121+                       {
15122+                               *percentage=100;
15123+                               return (-1);
15124+                       }
15125+
15126+                       /* Ditto */
15127+
15128+                       x=1;
15129+                       if (i > 20000000) x=1024;
15130+
15131+                       npercentage = i ? ((off_t)n/x) * 100 / (i/x):100;
15132+                       break;
15133+               }
15134+       }
15135+       *percentage = spercentage > npercentage ? spercentage:npercentage;
15136+       return (0);
15137+}
15138+
15139+static int     doaddquota(const char *, int, const char *, long, int, int);
15140+
15141+static int docheckquota(const char *dir,
15142+       int *maildirsize_fdptr,
15143+       const char *quota_type,
15144+       long xtra_size,
15145+       int xtra_cnt, int *percentage);
15146+
15147+
15148+int maildir_checkquota(const char *dir,
15149+       int *maildirsize_fdptr,
15150+       const char *quota_type,
15151+       long xtra_size,
15152+       int xtra_cnt)
15153+{
15154+int    dummy;
15155+
15156+       return (docheckquota(dir, maildirsize_fdptr, quota_type,
15157+               xtra_size, xtra_cnt, &dummy));
15158+}
15159+
15160+int maildir_readquota(const char *dir, const char *quota_type)
15161+{
15162+int    percentage=0;
15163+int    fd=-1;
15164+
15165+       (void)docheckquota(dir, &fd, quota_type, 0, 0, &percentage);
15166+       if (fd >= 0)
15167+               close(fd);
15168+       return (percentage);
15169+}
15170+
15171+static int docheckquota(const char *dir,
15172+       int *maildirsize_fdptr,
15173+       const char *quota_type,
15174+       long xtra_size,
15175+       int xtra_cnt,
15176+       int *percentage)
15177+{
15178+char   *checkfolder=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
15179+char   *newmaildirsizename;
15180+struct stat stat_buf;
15181+int    maildirsize_fd;
15182+off_t  maildirsize_size;
15183+unsigned maildirsize_cnt;
15184+unsigned maildirsize_nlines;
15185+int    n;
15186+time_t tm;
15187+time_t maxtime;
15188+DIR    *dirp;
15189+struct dirent *de;
15190+
15191+       if (checkfolder == 0)   return (-1);
15192+       *maildirsize_fdptr= -1;
15193+       strcat(strcpy(checkfolder, dir), "/maildirfolder");
15194+       if (stat(checkfolder, &stat_buf) == 0)  /* Go to parent */
15195+       {
15196+               strcat(strcpy(checkfolder, dir), "/..");
15197+               n=docheckquota(checkfolder, maildirsize_fdptr,
15198+                       quota_type, xtra_size, xtra_cnt, percentage);
15199+               free(checkfolder);
15200+               return (n);
15201+       }
15202+       if (!quota_type || !*quota_type)        return (0);
15203+
15204+       strcat(strcpy(checkfolder, dir), "/maildirsize");
15205+       time(&tm);
15206+       if (maildirsize_read(checkfolder, &maildirsize_fd,
15207+               &maildirsize_size, &maildirsize_cnt,
15208+               &maildirsize_nlines, &stat_buf) == 0)
15209+       {
15210+               n=qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt,
15211+                       quota_type, percentage);
15212+
15213+               if (n == 0)
15214+               {
15215+                       free(checkfolder);
15216+                       *maildirsize_fdptr=maildirsize_fd;
15217+                       return (0);
15218+               }
15219+               close(maildirsize_fd);
15220+
15221+               if (maildirsize_nlines == 1 && tm < stat_buf.st_mtime + 15*60)
15222+                       return (n);
15223+       }
15224+
15225+       maxtime=0;
15226+       maildirsize_size=0;
15227+       maildirsize_cnt=0;
15228+
15229+       if (countcurnew(dir, &maxtime, &maildirsize_size, &maildirsize_cnt))
15230+       {
15231+               free(checkfolder);
15232+               return (-1);
15233+       }
15234+
15235+       dirp=opendir(dir);
15236+       while (dirp && (de=readdir(dirp)) != 0)
15237+       {
15238+               if (countsubdir(dir, de->d_name, &maxtime, &maildirsize_size,
15239+                       &maildirsize_cnt))
15240+               {
15241+                       free(checkfolder);
15242+                       closedir(dirp);
15243+                       return (-1);
15244+               }
15245+       }
15246+       if (dirp)
15247+       {
15248+#if    CLOSEDIR_VOID
15249+               closedir(dirp);
15250+#else
15251+               if (closedir(dirp))
15252+               {
15253+                       free(checkfolder);
15254+                       return (-1);
15255+               }
15256+#endif
15257+       }
15258+
15259+       newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd);
15260+       if (!newmaildirsizename)
15261+       {
15262+               free(checkfolder);
15263+               return (-1);
15264+       }
15265+
15266+       *maildirsize_fdptr=maildirsize_fd;
15267+
15268+       if (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size,
15269+               maildirsize_cnt, 1))
15270+       {
15271+               unlink(newmaildirsizename);
15272+               free(newmaildirsizename);
15273+               close(maildirsize_fd);
15274+               *maildirsize_fdptr= -1;
15275+               free(checkfolder);
15276+               return (-1);
15277+       }
15278+
15279+       strcat(strcpy(checkfolder, dir), "/maildirsize");
15280+
15281+       if (rename(newmaildirsizename, checkfolder))
15282+       {
15283+               /* free(checkfolder); */
15284+               unlink(newmaildirsizename);
15285+               close(maildirsize_fd);
15286+               *maildirsize_fdptr= -1;
15287+       }
15288+       free(checkfolder);
15289+       free(newmaildirsizename);
15290+
15291+       tm=0;
15292+
15293+       if (statcurnew(dir, &tm))
15294+       {
15295+               close(maildirsize_fd);
15296+               *maildirsize_fdptr= -1;
15297+               return (-1);
15298+       }
15299+
15300+       dirp=opendir(dir);
15301+       while (dirp && (de=readdir(dirp)) != 0)
15302+       {
15303+               if (statsubdir(dir, de->d_name, &tm))
15304+               {
15305+                       close(maildirsize_fd);
15306+                       *maildirsize_fdptr= -1;
15307+                       closedir(dirp);
15308+                       return (-1);
15309+               }
15310+       }
15311+       if (dirp)
15312+       {
15313+#if    CLOSEDIR_VOID
15314+               closedir(dirp);
15315+#else
15316+               if (closedir(dirp))
15317+               {
15318+                       close(maildirsize_fd);
15319+                       *maildirsize_fdptr= -1;
15320+                       return (-1);
15321+               }
15322+#endif
15323+       }
15324+
15325+       if (tm != maxtime)      /* Race condition, someone changed something */
15326+       {
15327+               errno=EAGAIN;
15328+               return (-1);
15329+       }
15330+
15331+       return (qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt,
15332+               quota_type, percentage));
15333+}
15334+
15335+int    maildir_addquota(const char *dir, int maildirsize_fd,
15336+       const char *quota_type, long maildirsize_size, int maildirsize_cnt)
15337+{
15338+       if (!quota_type || !*quota_type)        return (0);
15339+       return (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size,
15340+                       maildirsize_cnt, 0));
15341+}
15342+
15343+static int doaddquota(const char *dir, int maildirsize_fd,
15344+       const char *quota_type, long maildirsize_size, int maildirsize_cnt,
15345+       int isnew)
15346+{
15347+union  {
15348+       char    buf[100];
15349+       struct stat stat_buf;
15350+       } u;                            /* Scrooge */
15351+char   *newname2=0;
15352+char   *newmaildirsizename=0;
15353+struct iovec   iov[3];
15354+int    niov;
15355+struct iovec   *p;
15356+int    n;
15357+
15358+       niov=0;
15359+       if ( maildirsize_fd < 0)
15360+       {
15361+               newname2=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
15362+               if (!newname2)  return (-1);
15363+               strcat(strcpy(newname2, dir), "/maildirfolder");
15364+               if (stat(newname2, &u.stat_buf) == 0)
15365+               {
15366+                       strcat(strcpy(newname2, dir), "/..");
15367+                       n=doaddquota(newname2, maildirsize_fd, quota_type,
15368+                                       maildirsize_size, maildirsize_cnt,
15369+                                       isnew);
15370+                       free(newname2);
15371+                       return (n);
15372+               }
15373+
15374+               strcat(strcpy(newname2, dir), "/maildirsize");
15375+
15376+               if ((maildirsize_fd=maildir_safeopen(newname2,
15377+                       O_RDWR|O_APPEND, 0644)) < 0)
15378+               {
15379+                       newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd);
15380+                       if (!newmaildirsizename)
15381+                       {
15382+                               free(newname2);
15383+                               return (-1);
15384+                       }
15385+
15386+                       maildirsize_fd=maildir_safeopen(newmaildirsizename,
15387+                               O_CREAT|O_RDWR|O_APPEND, 0644);
15388+
15389+                       if (maildirsize_fd < 0)
15390+                       {
15391+                               free(newname2);
15392+                               return (-1);
15393+                       }
15394+                       isnew=1;
15395+               }
15396+       }
15397+
15398+       if (isnew)
15399+       {
15400+               iov[0].iov_base=(caddr_t)quota_type;
15401+               iov[0].iov_len=strlen(quota_type);
15402+               iov[1].iov_base=(caddr_t)"\n";
15403+               iov[1].iov_len=1;
15404+               niov=2;
15405+       }
15406+
15407+
15408+       sprintf(u.buf, "%ld %d\n", maildirsize_size, maildirsize_cnt);
15409+       iov[niov].iov_base=(caddr_t)u.buf;
15410+       iov[niov].iov_len=strlen(u.buf);
15411+
15412+       p=iov;
15413+       ++niov;
15414+       n=0;
15415+       while (niov)
15416+       {
15417+               if (n)
15418+               {
15419+                       if (n < p->iov_len)
15420+                       {
15421+                               p->iov_base=
15422+                                       (caddr_t)((char *)p->iov_base + n);
15423+                               p->iov_len -= n;
15424+                       }
15425+                       else
15426+                       {
15427+                               n -= p->iov_len;
15428+                               ++p;
15429+                               --niov;
15430+                               continue;
15431+                       }
15432+               }
15433+
15434+               n=writev( maildirsize_fd, p, niov);
15435+
15436+               if (n <= 0)
15437+               {
15438+                       if (newname2)
15439+                       {
15440+                               close(maildirsize_fd);
15441+                               free(newname2);
15442+                       }
15443+                       return (-1);
15444+               }
15445+       }
15446+       if (newname2)
15447+       {
15448+               close(maildirsize_fd);
15449+
15450+               if (newmaildirsizename)
15451+               {
15452+                       rename(newmaildirsizename, newname2);
15453+                       free(newmaildirsizename);
15454+               }
15455+               free(newname2);
15456+       }
15457+       return (0);
15458+}
15459+
15460+/* New maildirsize is built in the tmp subdirectory */
15461+
15462+static char *makenewmaildirsizename(const char *dir, int *fd)
15463+{
15464+char   hostname[256];
15465+struct stat stat_buf;
15466+time_t t;
15467+char   *p;
15468+
15469+       hostname[0]=0;
15470+       hostname[sizeof(hostname)-1]=0;
15471+       gethostname(hostname, sizeof(hostname)-1);
15472+       p=(char *)malloc(strlen(dir)+strlen(hostname)+130);
15473+       if (!p) return (0);
15474+
15475+       for (;;)
15476+       {
15477+       char    tbuf[NUMBUFSIZE];
15478+       char    pbuf[NUMBUFSIZE];
15479+
15480+               time(&t);
15481+               strcat(strcpy(p, dir), "/tmp/");
15482+               sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s",
15483+                       str_time_t(t, tbuf),
15484+                       str_pid_t(getpid(), pbuf), hostname);
15485+
15486+               if (stat( (const char *)p, &stat_buf) < 0 &&
15487+                       (*fd=maildir_safeopen(p,
15488+                               O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0)
15489+                       break;
15490+               sleep(3);
15491+       }
15492+       return (p);
15493+}
15494+
15495+static int statcurnew(const char *dir, time_t *maxtimestamp)
15496+{
15497+char   *p=(char *)malloc(strlen(dir)+5);
15498+struct stat    stat_buf;
15499+
15500+       if (!p) return (-1);
15501+       strcat(strcpy(p, dir), "/cur");
15502+       if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
15503+               *maxtimestamp=stat_buf.st_mtime;
15504+       strcat(strcpy(p, dir), "/new");
15505+       if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
15506+               *maxtimestamp=stat_buf.st_mtime;
15507+       free(p);
15508+       return (0);
15509+}
15510+
15511+static int statsubdir(const char *dir, const char *subdir, time_t *maxtime)
15512+{
15513+char   *p;
15514+int    n;
15515+
15516+       if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
15517+               strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
15518+               return (0);
15519+
15520+       p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
15521+       if (!p) return (-1);
15522+       strcat(strcat(strcpy(p, dir), "/"), subdir);
15523+       n=statcurnew(p, maxtime);
15524+       free(p);
15525+       return (n);
15526+}
15527+
15528+static int docount(const char *, time_t *, off_t *, unsigned *);
15529+
15530+static int countcurnew(const char *dir, time_t *maxtime,
15531+       off_t *sizep, unsigned *cntp)
15532+{
15533+char   *p=(char *)malloc(strlen(dir)+5);
15534+int    n;
15535+
15536+       if (!p) return (-1);
15537+       strcat(strcpy(p, dir), "/new");
15538+       n=docount(p, maxtime, sizep, cntp);
15539+       if (n == 0)
15540+       {
15541+               strcat(strcpy(p, dir), "/cur");
15542+               n=docount(p, maxtime, sizep, cntp);
15543+       }
15544+       free(p);
15545+       return (n);
15546+}
15547+
15548+static int countsubdir(const char *dir, const char *subdir, time_t *maxtime,
15549+       off_t *sizep, unsigned *cntp)
15550+{
15551+char   *p;
15552+int    n;
15553+
15554+       if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
15555+               strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
15556+               return (0);
15557+
15558+       p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
15559+       if (!p) return (2);
15560+       strcat(strcat(strcpy(p, dir), "/"), subdir);
15561+       n=countcurnew(p, maxtime, sizep, cntp);
15562+       free(p);
15563+       return (n);
15564+}
15565+
15566+static int docount(const char *dir, time_t *dirstamp,
15567+       off_t *sizep, unsigned *cntp)
15568+{
15569+struct stat    stat_buf;
15570+char   *p;
15571+DIR    *dirp;
15572+struct dirent *de;
15573+unsigned long  s;
15574+
15575+       if (stat(dir, &stat_buf))       return (0);     /* Ignore */
15576+       if (stat_buf.st_mtime > *dirstamp)      *dirstamp=stat_buf.st_mtime;
15577+       if ((dirp=opendir(dir)) == 0)   return (0);
15578+       while ((de=readdir(dirp)) != 0)
15579+       {
15580+       const char *n=de->d_name;
15581+
15582+               if (*n == '.')  continue;
15583+
15584+               /* PATCH - do not count msgs marked as deleted */
15585+
15586+               for ( ; *n; n++)
15587+               {
15588+                       if (n[0] != ':' || n[1] != '2' ||
15589+                               n[2] != ',')    continue;
15590+                       n += 3;
15591+                       while (*n >= 'A' && *n <= 'Z')
15592+                       {
15593+                               if (*n == 'T')  break;
15594+                               ++n;
15595+                       }
15596+                       break;
15597+               }
15598+               if (*n == 'T')  continue;
15599+               n=de->d_name;
15600+
15601+
15602+               if (maildir_parsequota(n, &s) == 0)
15603+                       stat_buf.st_size=s;
15604+               else
15605+               {
15606+                       p=(char *)malloc(strlen(dir)+strlen(n)+2);
15607+                       if (!p)
15608+                       {
15609+                               closedir(dirp);
15610+                               return (-1);
15611+                       }
15612+                       strcat(strcat(strcpy(p, dir), "/"), n);
15613+                       if (stat(p, &stat_buf))
15614+                       {
15615+                               free(p);
15616+                               continue;
15617+                       }
15618+                       free(p);
15619+               }
15620+               *sizep += stat_buf.st_size;
15621+               ++*cntp;
15622+       }
15623+
15624+#if    CLOSEDIR_VOID
15625+       closedir(dirp);
15626+#else
15627+       if (closedir(dirp))
15628+               return (-1);
15629+#endif
15630+       return (0);
15631+}
15632diff -ruN ../netqmail-1.06-original/maildirquota.h netqmail-1.06/maildirquota.h
15633--- ../netqmail-1.06-original/maildirquota.h    1970-01-01 01:00:00.000000000 +0100
15634+++ netqmail-1.06/maildirquota.h        2019-02-27 20:57:13.393025036 +0100
15635@@ -0,0 +1,45 @@
15636+#ifndef        maildirquota_h
15637+#define        maildirquota_h
15638+
15639+/*
15640+** Copyright 1998 - 1999 Double Precision, Inc.
15641+** See COPYING for distribution information.
15642+*/
15643+
15644+#if    HAVE_CONFIG_H
15645+#include       "config.h"
15646+#endif
15647+
15648+#include       <sys/types.h>
15649+#include       <stdio.h>
15650+
15651+#ifdef  __cplusplus
15652+extern "C" {
15653+#endif
15654+
15655+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 $";
15656+
15657+int maildir_checkquota(const char *,   /* Pointer to directory */
15658+       int *,  /* Initialized to -1, or opened descriptor for maildirsize */
15659+       const char *,   /* The quota */
15660+       long,           /* Extra bytes planning to add/remove from maildir */
15661+       int);           /* Extra messages planning to add/remove from maildir */
15662+
15663+int maildir_addquota(const char *,     /* Pointer to the maildir */
15664+       int,    /* Must be the int pointed to by 2nd arg to checkquota */
15665+       const char *,   /* The quota */
15666+       long,   /* +/- bytes */
15667+       int);   /* +/- files */
15668+
15669+int maildir_readquota(const char *,    /* Directory */
15670+       const char *);                  /* Quota, from getquota */
15671+
15672+int maildir_parsequota(const char *, unsigned long *);
15673+       /* Attempt to parse file size encoded in filename.  Returns 0 if
15674+       ** parsed, non-zero if we didn't parse. */
15675+
15676+#ifdef  __cplusplus
15677+}
15678+#endif
15679+
15680+#endif
15681diff -ruN ../netqmail-1.06-original/md5.h netqmail-1.06/md5.h
15682--- ../netqmail-1.06-original/md5.h     1970-01-01 01:00:00.000000000 +0100
15683+++ netqmail-1.06/md5.h 2019-02-27 20:57:13.394025025 +0100
15684@@ -0,0 +1,49 @@
15685+/* MD5.H - header file for MD5C.C
15686+ */
15687+
15688+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
15689+   rights reserved.
15690+
15691+   License to copy and use this software is granted provided that it
15692+   is identified as the "RSA Data Security, Inc. MD5 Message-Digest
15693+   Algorithm" in all material mentioning or referencing this software
15694+   or this function.
15695+
15696+   License is also granted to make and use derivative works provided
15697+   that such works are identified as "derived from the RSA Data
15698+   Security, Inc. MD5 Message-Digest Algorithm" in all material
15699+   mentioning or referencing the derived work. 
15700+                                                                   
15701+   RSA Data Security, Inc. makes no representations concerning either
15702+   the merchantability of this software or the suitability of this
15703+   software for any particular purpose. It is provided "as is"
15704+   without express or implied warranty of any kind. 
15705+                                                                   
15706+   These notices must be retained in any copies of any part of this
15707+   documentation and/or software. 
15708+ */
15709+
15710+#ifndef _MD5_H_
15711+#define _MD5_H_ 1
15712+
15713+#ifdef __cplusplus
15714+extern "C" {
15715+#endif
15716+
15717+/* MD5 context. */
15718+typedef struct {
15719+  UINT4 state[4];                                   /* state (ABCD) */
15720+  UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */
15721+  unsigned char buffer[64];                         /* input buffer */
15722+} MD5_CTX;
15723+
15724+void MD5Init PROTO_LIST ((MD5_CTX *));
15725+void MD5Update PROTO_LIST
15726+  ((MD5_CTX *, unsigned char *, unsigned int));
15727+void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
15728+
15729+#ifdef __cplusplus
15730+}
15731+#endif
15732+
15733+#endif
15734diff -ruN ../netqmail-1.06-original/md5c.c netqmail-1.06/md5c.c
15735--- ../netqmail-1.06-original/md5c.c    1970-01-01 01:00:00.000000000 +0100
15736+++ netqmail-1.06/md5c.c        2019-02-27 20:57:13.394025025 +0100
15737@@ -0,0 +1,334 @@
15738+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
15739+ */
15740+
15741+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
15742+   rights reserved.
15743+
15744+   License to copy and use this software is granted provided that it
15745+   is identified as the "RSA Data Security, Inc. MD5 Message-Digest
15746+   Algorithm" in all material mentioning or referencing this software
15747+   or this function.
15748+
15749+   License is also granted to make and use derivative works provided
15750+   that such works are identified as "derived from the RSA Data
15751+   Security, Inc. MD5 Message-Digest Algorithm" in all material
15752+   mentioning or referencing the derived work. 
15753+                                                                   
15754+   RSA Data Security, Inc. makes no representations concerning either
15755+   the merchantability of this software or the suitability of this
15756+   software for any particular purpose. It is provided "as is"
15757+   without express or implied warranty of any kind. 
15758+                                                                   
15759+   These notices must be retained in any copies of any part of this
15760+   documentation and/or software. 
15761+ */
15762+
15763+#include "global.h"
15764+#include "md5.h"
15765+
15766+/* Constants for MD5Transform routine.
15767+ */
15768+#define S11 7
15769+#define S12 12
15770+#define S13 17
15771+#define S14 22
15772+#define S21 5
15773+#define S22 9
15774+#define S23 14
15775+#define S24 20
15776+#define S31 4
15777+#define S32 11
15778+#define S33 16
15779+#define S34 23
15780+#define S41 6
15781+#define S42 10
15782+#define S43 15
15783+#define S44 21
15784+
15785+static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
15786+static void Encode PROTO_LIST
15787+  ((unsigned char *, UINT4 *, unsigned int));
15788+static void Decode PROTO_LIST
15789+  ((UINT4 *, unsigned char *, unsigned int));
15790+static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
15791+static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
15792+
15793+static unsigned char PADDING[64] = {
15794+  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15795+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15796+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
15797+};
15798+
15799+/* F, G, H and I are basic MD5 functions.
15800+ */
15801+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
15802+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
15803+#define H(x, y, z) ((x) ^ (y) ^ (z))
15804+#define I(x, y, z) ((y) ^ ((x) | (~z)))
15805+
15806+/* ROTATE_LEFT rotates x left n bits.
15807+ */
15808+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
15809+
15810+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
15811+   Rotation is separate from addition to prevent recomputation.
15812+ */
15813+#define FF(a, b, c, d, x, s, ac) { \
15814+    (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
15815+    (a) = ROTATE_LEFT ((a), (s)); \
15816+    (a) += (b); \
15817+  }
15818+#define GG(a, b, c, d, x, s, ac) { \
15819+    (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
15820+    (a) = ROTATE_LEFT ((a), (s)); \
15821+    (a) += (b); \
15822+  }
15823+#define HH(a, b, c, d, x, s, ac) { \
15824+    (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
15825+    (a) = ROTATE_LEFT ((a), (s)); \
15826+    (a) += (b); \
15827+  }
15828+#define II(a, b, c, d, x, s, ac) { \
15829+    (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
15830+    (a) = ROTATE_LEFT ((a), (s)); \
15831+    (a) += (b); \
15832+  }
15833+
15834+/* MD5 initialization. Begins an MD5 operation, writing a new context.
15835+ */
15836+void MD5Init (context)
15837+MD5_CTX *context;                                        /* context */
15838+{
15839+  context->count[0] = context->count[1] = 0;
15840+
15841+  /* Load magic initialization constants.
15842+   */
15843+  context->state[0] = 0x67452301;
15844+  context->state[1] = 0xefcdab89;
15845+  context->state[2] = 0x98badcfe;
15846+  context->state[3] = 0x10325476;
15847+}
15848+
15849+/* MD5 block update operation. Continues an MD5 message-digest
15850+     operation, processing another message block, and updating the
15851+     context.
15852+ */
15853+void MD5Update (context, input, inputLen)
15854+MD5_CTX *context;                                        /* context */
15855+unsigned char *input;                                /* input block */
15856+unsigned int inputLen;                     /* length of input block */
15857+{
15858+  unsigned int i, index, partLen;
15859+
15860+  /* Compute number of bytes mod 64 */
15861+  index = (unsigned int)((context->count[0] >> 3) & 0x3F);
15862+
15863+  /* Update number of bits */
15864+  if ((context->count[0] += ((UINT4)inputLen << 3))
15865+      < ((UINT4)inputLen << 3))
15866+    context->count[1]++;
15867+  context->count[1] += ((UINT4)inputLen >> 29);
15868
15869+  partLen = 64 - index;
15870
15871+  /* Transform as many times as possible.
15872+   */
15873+  if (inputLen >= partLen) {
15874+    MD5_memcpy
15875+      ((POINTER)&context->buffer[index], (POINTER)input, partLen);
15876+    MD5Transform (context->state, context->buffer);
15877
15878+    for (i = partLen; i + 63 < inputLen; i += 64)
15879+      MD5Transform (context->state, &input[i]);
15880+   
15881+    index = 0;
15882+  }
15883+  else
15884+    i = 0;
15885
15886+  /* Buffer remaining input */
15887+  MD5_memcpy
15888+    ((POINTER)&context->buffer[index], (POINTER)&input[i],
15889+     inputLen-i);
15890+}
15891+
15892+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
15893+     the message digest and zeroizing the context.
15894+ */
15895+void MD5Final (digest, context)
15896+unsigned char digest[16];                         /* message digest */
15897+MD5_CTX *context;                                       /* context */
15898+{
15899+  unsigned char bits[8];
15900+  unsigned int index, padLen;
15901+
15902+  /* Save number of bits */
15903+  Encode (bits, context->count, 8);
15904+
15905+  /* Pad out to 56 mod 64.
15906+   */
15907+  index = (unsigned int)((context->count[0] >> 3) & 0x3f);
15908+  padLen = (index < 56) ? (56 - index) : (120 - index);
15909+  MD5Update (context, PADDING, padLen);
15910
15911+  /* Append length (before padding) */
15912+  MD5Update (context, bits, 8);
15913+
15914+  /* Store state in digest */
15915+  Encode (digest, context->state, 16);
15916
15917+  /* Zeroize sensitive information.
15918+   */
15919+  MD5_memset ((POINTER)context, 0, sizeof (*context));
15920+}
15921+
15922+/* MD5 basic transformation. Transforms state based on block.
15923+ */
15924+static void MD5Transform (state, block)
15925+UINT4 state[4];
15926+unsigned char block[64];
15927+{
15928+  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
15929
15930+  Decode (x, block, 64);
15931+
15932+  /* Round 1 */
15933+  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
15934+  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
15935+  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
15936+  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
15937+  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
15938+  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
15939+  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
15940+  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
15941+  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
15942+  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
15943+  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
15944+  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
15945+  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
15946+  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
15947+  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
15948+  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
15949+
15950+  /* Round 2 */
15951+  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
15952+  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
15953+  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
15954+  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
15955+  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
15956+  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
15957+  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
15958+  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
15959+  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
15960+  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
15961+  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
15962+  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
15963+  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
15964+  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
15965+  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
15966+  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
15967+
15968+  /* Round 3 */
15969+  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
15970+  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
15971+  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
15972+  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
15973+  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
15974+  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
15975+  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
15976+  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
15977+  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
15978+  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
15979+  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
15980+  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
15981+  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
15982+  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
15983+  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
15984+  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
15985+
15986+  /* Round 4 */
15987+  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
15988+  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
15989+  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
15990+  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
15991+  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
15992+  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
15993+  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
15994+  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
15995+  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
15996+  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
15997+  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
15998+  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
15999+  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
16000+  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
16001+  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
16002+  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
16003+
16004+  state[0] += a;
16005+  state[1] += b;
16006+  state[2] += c;
16007+  state[3] += d;
16008
16009+  /* Zeroize sensitive information.
16010+   */
16011+  MD5_memset ((POINTER)x, 0, sizeof (x));
16012+}
16013+
16014+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
16015+     a multiple of 4.
16016+ */
16017+static void Encode (output, input, len)
16018+unsigned char *output;
16019+UINT4 *input;
16020+unsigned int len;
16021+{
16022+  unsigned int i, j;
16023+
16024+  for (i = 0, j = 0; j < len; i++, j += 4) {
16025+    output[j] = (unsigned char)(input[i] & 0xff);
16026+    output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
16027+    output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
16028+    output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
16029+  }
16030+}
16031+
16032+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
16033+     a multiple of 4.
16034+ */
16035+static void Decode (output, input, len)
16036+UINT4 *output;
16037+unsigned char *input;
16038+unsigned int len;
16039+{
16040+  unsigned int i, j;
16041+
16042+  for (i = 0, j = 0; j < len; i++, j += 4)
16043+    output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
16044+      (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
16045+}
16046+
16047+/* Note: Replace "for loop" with standard memcpy if possible.
16048+ */
16049+static void MD5_memcpy (output, input, len)
16050+POINTER output;
16051+POINTER input;
16052+unsigned int len;
16053+{
16054+  unsigned int i;
16055
16056+  for (i = 0; i < len; i++)
16057+    output[i] = input[i];
16058+}
16059+
16060+/* Note: Replace "for loop" with standard memset if possible.
16061+ */
16062+static void MD5_memset (output, value, len)
16063+POINTER output;
16064+int value;
16065+unsigned int len;
16066+{
16067+  unsigned int i;
16068
16069+  for (i = 0; i < len; i++)
16070+    ((char *)output)[i] = (char)value;
16071+}
16072diff -ruN ../netqmail-1.06-original/mess822.h netqmail-1.06/mess822.h
16073--- ../netqmail-1.06-original/mess822.h 1970-01-01 01:00:00.000000000 +0100
16074+++ netqmail-1.06/mess822.h     2019-02-27 20:57:13.394025025 +0100
16075@@ -0,0 +1,55 @@
16076+/*
16077+ * $Log: mess822.h,v $
16078+ * Revision 1.3  2004-10-11 13:55:55+05:30  Cprogrammer
16079+ * added function prototypes
16080+ *
16081+ * Revision 1.2  2004-06-18 23:00:58+05:30  Cprogrammer
16082+ * added RCS log
16083+ *
16084+ */
16085+#ifndef MESS822_H
16086+#define MESS822_H
16087+#include "stralloc.h"
16088+#include "caltime.h"
16089+
16090+#define MESS822_HEADER { {0} }
16091+
16092+typedef struct
16093+{
16094+       struct caltime  ct;
16095+       int             known; /*- 0 for ct uninitialized; 1 if ok; 2 if ok and right zone */
16096+}
16097+mess822_time;
16098+
16099+typedef struct
16100+{
16101+       char           *name; /*- 0 means all names */
16102+       int            *flag;
16103+       stralloc       *copy;
16104+       stralloc       *value;
16105+       stralloc       *addr;
16106+       mess822_time   *when;
16107+}
16108+mess822_action;
16109+
16110+typedef struct
16111+{
16112+       stralloc        inprogress;
16113+       mess822_action *action;
16114+}
16115+mess822_header;
16116+
16117+int             mess822_quoteplus(stralloc *, char *, char *);
16118+int             mess822_quote(stralloc *, char *, char *);
16119+int             mess822_quotelist(stralloc *, stralloc *);
16120+int             mess822_fold(stralloc *, stralloc *, char *, int);
16121+int             mess822_date(stralloc *, mess822_time *);
16122+int             mess822_token(stralloc *, char *);
16123+int             mess822_addrlist(stralloc *, char *);
16124+int             mess822_when(mess822_time *, char *);
16125+int             mess822_begin(mess822_header *, mess822_action *);
16126+int             mess822_line(mess822_header *, stralloc *);
16127+int             mess822_end(mess822_header *);
16128+int             mess822_ok(stralloc *);
16129+
16130+#endif
16131diff -ruN ../netqmail-1.06-original/mess822_ok.c netqmail-1.06/mess822_ok.c
16132--- ../netqmail-1.06-original/mess822_ok.c      1970-01-01 01:00:00.000000000 +0100
16133+++ netqmail-1.06/mess822_ok.c  2019-02-27 20:57:13.394025025 +0100
16134@@ -0,0 +1,55 @@
16135+/*
16136+ * $Log: mess822_ok.c,v $
16137+ * Revision 1.2  2004-10-22 20:27:30+05:30  Cprogrammer
16138+ * added RCS id
16139+ *
16140+ * Revision 1.1  2004-01-04 23:17:51+05:30  Cprogrammer
16141+ * Initial revision
16142+ *
16143+ */
16144+#include "mess822.h"
16145+#include "byte.h"
16146+
16147+int
16148+mess822_ok(sa)
16149+       stralloc       *sa;
16150+{
16151+       int             i;
16152+       int             len;
16153+       int             colon;
16154+
16155+       len = sa->len;
16156+       if (len && (sa->s[len - 1] == '\n'))
16157+               --len;
16158+       if (!len)
16159+               return 0;
16160+       /*
16161+        * if input message is 822-compliant, will return 1 after this
16162+        */
16163+       if (sa->s[0] == ' ')
16164+               return 1;
16165+       if (sa->s[0] == '\t')
16166+               return 1;
16167+       colon = byte_chr(sa->s, sa->len, ':');
16168+       if (colon >= sa->len)
16169+               return 0;
16170+       while (colon && ((sa->s[colon - 1] == ' ') || (sa->s[colon - 1] == '\t')))
16171+               --colon;
16172+       if (!colon)
16173+               return 0;
16174+       for (i = 0; i < colon; ++i)
16175+               if (sa->s[i] < 33)
16176+                       return 0;
16177+       for (i = 0; i < colon; ++i)
16178+               if (sa->s[i] > 126)
16179+                       return 0;
16180+       return 1;
16181+}
16182+
16183+void
16184+getversion_mess822_ok_c()
16185+{
16186+       static char    *x = "$Id: mess822_ok.c,v 1.2 2004-10-22 20:27:30+05:30 Cprogrammer Stab mbhangui $";
16187+
16188+       x++;
16189+}
16190diff -ruN ../netqmail-1.06-original/numlib.h netqmail-1.06/numlib.h
16191--- ../netqmail-1.06-original/numlib.h  1970-01-01 01:00:00.000000000 +0100
16192+++ netqmail-1.06/numlib.h      2019-02-27 20:57:13.394025025 +0100
16193@@ -0,0 +1,45 @@
16194+#ifndef        numlib_h
16195+#define        numlib_h
16196+
16197+/*
16198+** Copyright 1998 - 1999 Double Precision, Inc.
16199+** See COPYING for distribution information.
16200+*/
16201+
16202+#ifdef __cplusplus
16203+extern "C" {
16204+#endif
16205+
16206+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 $";
16207+
16208+#if    HAVE_CONFIG_H
16209+#include       "config.h"
16210+#endif
16211+
16212+#include       <sys/types.h>
16213+#include       <time.h>
16214+
16215+#define        NUMBUFSIZE      60
16216+
16217+/* Convert various system types to decimal */
16218+
16219+char   *str_time_t(time_t, char *);
16220+char   *str_off_t(off_t, char *);
16221+char   *str_pid_t(pid_t, char *);
16222+char   *str_ino_t(ino_t, char *);
16223+char   *str_uid_t(uid_t, char *);
16224+char   *str_gid_t(gid_t, char *);
16225+char   *str_size_t(size_t, char *);
16226+
16227+char   *str_sizekb(unsigned long, char *);     /* X Kb or X Mb */
16228+
16229+/* Convert selected system types to hex */
16230+
16231+char   *strh_time_t(time_t, char *);
16232+char   *strh_pid_t(pid_t, char *);
16233+char   *strh_ino_t(ino_t, char *);
16234+
16235+#ifdef __cplusplus
16236+}
16237+#endif
16238+#endif
16239diff -ruN ../netqmail-1.06-original/overmaildirquota.c netqmail-1.06/overmaildirquota.c
16240--- ../netqmail-1.06-original/overmaildirquota.c        1970-01-01 01:00:00.000000000 +0100
16241+++ netqmail-1.06/overmaildirquota.c    2019-02-27 20:57:13.394025025 +0100
16242@@ -0,0 +1,41 @@
16243+/*
16244+** Copyright 1998 - 1999 Double Precision, Inc.
16245+** See COPYING for distribution information.
16246+*/
16247+
16248+#if HAVE_CONFIG_H
16249+#include "config.h"
16250+#endif
16251+#include        "maildirquota.h"
16252+#include        <stdlib.h>
16253+#include        <string.h>
16254+#include        <errno.h>
16255+#include        <sys/stat.h>
16256+
16257+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
16258+
16259+int user_over_maildirquota( const char *dir, const char *q)
16260+{
16261+struct  stat    stat_buf;
16262+int     quotafd;
16263+int     ret_value;
16264+
16265+        if (fstat(0, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) &&
16266+                stat_buf.st_size > 0 && *q)
16267+        {
16268+                if (maildir_checkquota(dir, &quotafd, q, stat_buf.st_size, 1)
16269+                        && errno != EAGAIN)
16270+                {
16271+                        if (quotafd >= 0)       close(quotafd);
16272+                        ret_value = 1;
16273+                } else {
16274+                        maildir_addquota(dir, quotafd, q, stat_buf.st_size, 1);
16275+                        if (quotafd >= 0)       close(quotafd);
16276+                        ret_value = 0;
16277+                }
16278+        } else {
16279+                ret_value = 0;
16280+        }
16281+
16282+        return(ret_value);
16283+}
16284diff -ruN ../netqmail-1.06-original/policy.c netqmail-1.06/policy.c
16285--- ../netqmail-1.06-original/policy.c  1970-01-01 01:00:00.000000000 +0100
16286+++ netqmail-1.06/policy.c      2019-02-27 20:57:13.395025014 +0100
16287@@ -0,0 +1,1210 @@
16288+/*
16289+ * Copyright (C) 2005 Inter7 Internet Technologies, Inc.
16290+ *
16291+ * This program is free software; you can redistribute it and/or modify
16292+ * it under the terms of the GNU General Public License as published by
16293+ * the Free Software Foundation; either version 2 of the License, or
16294+ * (at your option) any later version.
16295+ *
16296+ * This program is distributed in the hope that it will be useful,
16297+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16298+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16299+ * GNU General Public License for more details.
16300+ *
16301+ * You should have received a copy of the GNU General Public License
16302+ * along with this program; if not, write to the Free Software
16303+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16304+ *
16305+ * <matt@inter7.com>
16306+ * eMail Messaging Policy Framework
16307+ * http://www.inter7.com/?page=empf
16308+ *
16309+ */
16310+
16311+#include <stdio.h>
16312+#include <string.h>
16313+#include <errno.h>
16314+#ifndef _GNU_SOURCE
16315+   #define _GNU_SOURCE
16316+#endif
16317+#ifndef FNM_CASEFOLD
16318+   #include <ctype.h>
16319+#endif
16320+#include <fnmatch.h>
16321+#include "stralloc.h"
16322+#include "rcpthosts.h"
16323+#include "policy.h"
16324+
16325+/*
16326+   Policy delivery flags
16327+*/
16328+
16329+#define POLICY_F_NONE 0
16330+#define POLICY_F_ALLOW 1               /* Allow this delivery type   */
16331+#define POLICY_F_DISALLOW 2            /* Do not allow this delivery */
16332+#define POLICY_F_LOCAL 4               /* Local -> local delivery    */
16333+#define POLICY_F_REMOTE 8              /* Local -> remote delivery   */
16334+#define POLICY_F_EXTERNAL 16   /* Remote -> local delivery   */
16335+#define POLICY_F_INTERNAL 32    /* Local -> local delivery    */
16336+
16337+/*
16338+   Policy
16339+*/
16340+
16341+typedef struct __policy_ {
16342+   int flags,                          /* Allow or disallow */
16343+          numargs;                             /* Arguments read    */
16344+   char **args;                                /* Argument array    */
16345+
16346+   struct __policy_ *next;
16347+} policy_t;
16348+
16349+/*
16350+   Local delivery name
16351+*/
16352+
16353+typedef struct __local_ {
16354+   char *local;                                                /* Local name */
16355+   policy_t *policy;                           /* Policy         */
16356+
16357+   struct __local_ *next;
16358+} local_t;
16359+
16360+/*
16361+   Domain name
16362+*/
16363+
16364+typedef struct __domain_ {
16365+   char *domain;                                       /* Domain name */
16366+   policy_t *policy;                           /* Policy          */
16367+   local_t *locals;                                    /* Locals      */
16368+
16369+   struct __domain_ *next;
16370+} domain_t;
16371+
16372+extern char *remoteinfo;
16373+extern stralloc mailfrom;
16374+extern stralloc addr;
16375+extern void out(char *);
16376+
16377+static int policy_load(const char *);
16378+static int policy_parse(const char *, char *);
16379+static policy_t *policy_construct(char *);
16380+static int policy_construct_parse_arguments(char **, const int, char *);
16381+static int policy_match(const char *, const char *);
16382+static int policy_applies_to(const policy_t *, const char *);
16383+static int policy_flags(const char);
16384+static policy_t *policy_find(const policy_t *, const int);
16385+static int policy_forbids(void);
16386+static domain_t *domain_find(const char *);
16387+static local_t *local_find(const domain_t *, const char *);
16388+static void policy_free(policy_t *);
16389+static void local_free(local_t *);
16390+static void domains_free(void);
16391+
16392+static domain_t *domains = NULL, *s_domain = NULL, *r_domain = NULL;
16393+
16394+/*
16395+   Check policy for delivery restrictions
16396+*/
16397+
16398+int policy_check(void)
16399+{
16400+   int ret = 0;
16401+   const char *p = NULL;
16402+   
16403+   s_domain = r_domain = NULL;
16404+
16405+#ifdef POLICY_DEALLOCATE
16406+   domains_free();
16407+#endif
16408+
16409+   if (remoteinfo)
16410+         p = remoteinfo;
16411+   else
16412+         p = mailfrom.s;
16413+
16414+   fprintf(stderr, "policy_check: %s %s -> %s %s (%s)\n",
16415+                rcpthosts(p, strlen(p)) ? "local" : "remote", p,
16416+                rcpthosts(addr.s, strlen(addr.s)) ? "local" : "remote", addr.s,
16417+                remoteinfo ? "AUTHENTICATED SENDER" : "UNAUTHENTICATED SENDER");
16418+
16419+   /*
16420+         Load sender-related policy
16421+   */
16422+
16423+   for (; ((p) && (*p)); p++) {
16424+         if (*p == '@')
16425+                break;
16426+   }
16427+
16428+   if (*p) {
16429+         if (rcpthosts(remoteinfo ? remoteinfo : mailfrom.s,
16430+                          remoteinfo ? strlen(remoteinfo) : strlen(mailfrom.s))) {
16431+                ret = policy_load(p + 1);
16432+                if (!ret) {
16433+                       fprintf(stderr, "policy_check: policy_load failed\n");
16434+                       return -1;
16435+                }
16436+
16437+                s_domain = domain_find(p + 1);
16438+
16439+#ifdef POLICY_ENFORCE_AUTHENTICATION
16440+                /*
16441+                   This check is done here in the event that there is
16442+                       no policy for a domain.  In that event, we do not
16443+                       wish to enforce policy rules
16444+                */
16445+
16446+                if ((s_domain) && (remoteinfo == NULL)) {
16447+                       fprintf(stderr, "policy_check: sender not authenticated\n");
16448+                       return 0;
16449+                }
16450+#endif
16451+         }
16452+
16453+#ifdef DEBUG
16454+         else
16455+                fprintf(stderr, "policy_check: %s is not local\n", p + 1);
16456+#endif
16457+   }
16458+
16459+   /*
16460+         Load recipient-related policy
16461+   */
16462+
16463+   for (p = addr.s; ((p) && (*p)); p++) {
16464+         if (*p == '@')
16465+                break;
16466+   }
16467+
16468+   if (*p) {
16469+         if (rcpthosts(addr.s, strlen(addr.s))) {
16470+                ret = policy_load(p + 1);
16471+                if (!ret) {
16472+                       fprintf(stderr, "policy_check: policy_load failed\n");
16473+                       return -1;
16474+                }
16475+
16476+                r_domain = domain_find(p + 1);
16477+         }
16478+
16479+#ifdef DEBUG
16480+         else
16481+                fprintf(stderr, "policy_check: %s is not local\n", p + 1);
16482+#endif
16483+   }
16484+
16485+   /*
16486+         Policy enforcement
16487+   */
16488+
16489+   ret = policy_forbids();
16490+   if (ret == 1) {
16491+         fprintf(stderr, "policy_check: policy forbids transmission\n");
16492+         return 0;
16493+   }
16494+
16495+   else if (ret == 0) {
16496+         fprintf(stderr, "policy_check: policy allows transmission\n");
16497+         return 1;
16498+   }
16499+
16500+   fprintf(stderr, "policy_check: policy_forbids failed\n");
16501+   return -1;
16502+}
16503+
16504+/*
16505+   Load policy from policy file
16506+*/
16507+
16508+static int policy_load(const char *domain)
16509+{
16510+   size_t line = 0;
16511+   FILE *stream = NULL;
16512+   char b[4096] = { 0 };
16513+   int locald = 0, ret = 0;
16514+
16515+   if (domain == NULL)
16516+         return 1;
16517+
16518+   /*
16519+         See if we've already loaded this policy
16520+   */
16521+
16522+   if (domain_find(domain)) {
16523+#ifdef DEBUG
16524+         fprintf(stderr, "policy_load(%s): already loaded\n", domain);
16525+#endif
16526+         return 1;
16527+   }
16528+
16529+   stream = fopen(POLICY_FILENAME, "r");
16530+
16531+   /*
16532+         If policy file doesnt exist, allow all messaging
16533+         Otherwise trigger error
16534+   */
16535+
16536+   if (stream == NULL) {
16537+         if (errno != ENOENT) {
16538+                fprintf(stderr, "policy_load(%s): cannot read policy\n", domain);
16539+                return 0;
16540+         }
16541+
16542+#ifdef DEBUG
16543+         fprintf(stderr, "policy_load(%s): no policy\n", domain);
16544+#endif
16545+         return 1;
16546+   }
16547+
16548+   /*
16549+         Run through policy line-by-line
16550+   */
16551+
16552+   line = 1;
16553+
16554+   while(!(feof(stream))) {
16555+         memset(b, 0, sizeof(b));
16556+         fgets(b, sizeof(b), stream);
16557+
16558+         if ((*b == '#') || (*b == ';') || (*b == '\r') || (*b == '\n') || (*b == '\0'))
16559+                continue;
16560+
16561+         ret = policy_parse(domain, b);
16562+         if (ret == -1) {
16563+                fprintf(stderr, "policy_load(%s): policy_parse failed (line %d)\n", domain, line);
16564+                fclose(stream);
16565+                return 0;
16566+         }
16567+
16568+         else if (ret == 1)
16569+                break;
16570+
16571+         line++;
16572+   }
16573+
16574+   fclose(stream);
16575+
16576+#ifdef DEBUG
16577+   fprintf(stderr, "policy_load(%s): loaded\n", domain);
16578+#endif
16579+
16580+   return 1;
16581+}
16582+
16583+/*
16584+   Parse policy data
16585+*/
16586+
16587+static int policy_parse(const char *domain, char *data)
16588+{
16589+   int ia = 0;
16590+   domain_t *d = NULL;
16591+   policy_t *p = NULL, *lp = NULL;
16592+   local_t *l_list = NULL, *l = NULL;
16593+   char *h = NULL, *t = NULL, *pp = NULL;
16594+
16595+   if ((domain == NULL) || (data == NULL))
16596+         return -1;
16597+
16598+   /*
16599+         Seperate out domain part
16600+   */
16601+
16602+   for (h = t = data; *h; h++) {
16603+         if (*h == ':')
16604+                break;
16605+   }
16606+
16607+   if (*h != ':') {
16608+         fprintf(stderr, "policy_parse: syntax error: no domain seperator\n");
16609+         return -1;
16610+   }
16611+
16612+   *h = '\0';
16613+
16614+   /*
16615+         Determine if this is the correct policy
16616+   */
16617+
16618+   if (strcasecmp(domain, t))
16619+         return 0;
16620+
16621+   /*
16622+         Seperate domain policy
16623+   */
16624+
16625+   for (ia = 0, t = ++h; *h; h++) {
16626+         if ((*h == '(') && (ia == 0))
16627+                ia = 1;
16628+
16629+         else if ((*h == ')') && (ia == 1))
16630+                ia = 0;
16631+
16632+         else if ((*h == ',') && (ia == 0))
16633+                break;
16634+   }
16635+
16636+   if (*h != ',') {
16637+         fprintf(stderr, "policy_parse: syntax error: no domain policy seperator\n");
16638+         return -1;
16639+   }
16640+
16641+   *h = '\0';
16642+
16643+   p = policy_construct(t);
16644+   if (p == NULL) {
16645+         fprintf(stderr, "policy_parse: policy_construct failed\n");
16646+         return -1;
16647+   }
16648+
16649+   /*
16650+         Parse locals
16651+   */
16652+
16653+   h++;
16654+   l_list = l = NULL;
16655+
16656+   while(*h) {
16657+         if ((*h == '\n') || (*h == '\r'))
16658+                break;
16659+
16660+         for (ia = 0, t = h; *h; h++) {
16661+                if ((*h == '(') && (ia == 0))
16662+                       ia = 1;
16663+
16664+                else if ((*h == ')') && (ia == 1))
16665+                       ia = 0;
16666+
16667+                else if ((*h == ',') && (ia == 0))
16668+                       break;
16669+         }
16670+
16671+         if (*h != ',') {
16672+                policy_free(p);
16673+                fprintf(stderr, "policy_parse: syntax error: no local policy seperator\n");
16674+                return -1;
16675+         }
16676+
16677+         *h = '\0';
16678+
16679+         /*
16680+            Seperate local name from local policy
16681+         */
16682+
16683+         for (pp = t; *pp; pp++) {
16684+                if (*pp == ':')
16685+                       break;
16686+         }
16687+
16688+         if (*pp != ':') {
16689+                policy_free(p);
16690+                fprintf(stderr, "policy_parse: syntax error: no local name, policy seperator\n");
16691+                return -1;
16692+         }
16693+
16694+         *pp++ = '\0';
16695+
16696+         if (!(*t)) {
16697+                policy_free(p);
16698+                fprintf(stderr, "policy_parse: syntax error: empty local name\n");
16699+                return -1;
16700+         }
16701+
16702+         if ((!(*pp)) || (*pp == ',')) {
16703+                policy_free(p);
16704+                fprintf(stderr, "policy_parse: syntax error: empty local policy\n");
16705+                return -1;
16706+         }
16707+
16708+         /*
16709+            Load local policy
16710+         */
16711+
16712+         lp = policy_construct(pp);
16713+         if (lp == NULL) {
16714+                policy_free(p);
16715+                fprintf(stderr, "policy_parse: policy_construct failed\n");
16716+                return -1;
16717+         }
16718+
16719+         l = (local_t *)malloc(sizeof(local_t));
16720+         if (l == NULL) {
16721+                policy_free(p);
16722+                policy_free(lp);
16723+                fprintf(stderr, "policy_parse: malloc failed\n");
16724+                return -1;
16725+         }
16726+
16727+         memset(l, 0, sizeof(local_t));
16728+
16729+         ia = strlen(t);
16730+
16731+         l->local = (char *)malloc(ia + 1 + strlen(domain) + 1);
16732+         if (l->local == NULL) {
16733+                policy_free(p);
16734+                policy_free(lp);
16735+                free(l);
16736+                fprintf(stderr, "policy_parse: malloc failed\n");
16737+                return -1;
16738+         }
16739+
16740+         memset(l->local, 0, ia + 1 + strlen(domain) + 1);
16741+         memcpy(l->local, t, ia);
16742+         memcpy(l->local + ia, "@", 1);
16743+         memcpy(l->local + ia + 1, domain, strlen(domain));
16744+
16745+         l->policy = lp;
16746+         l->next = NULL;
16747+
16748+         l->next = l_list;
16749+         l_list = l;
16750+
16751+         t = ++h;
16752+   }
16753+
16754+   /*
16755+         Allocate and fill domain structure
16756+   */
16757+
16758+   d = (domain_t *)malloc(sizeof(domain_t));
16759+   if (d == NULL) {
16760+         policy_free(p);
16761+         local_free(l_list);
16762+         fprintf(stderr, "policy_parse: malloc failed\n");
16763+         return -1;
16764+   }
16765+
16766+   memset(d, 0, sizeof(domain_t));
16767+
16768+   ia = strlen(domain);
16769+
16770+   d->domain = (char *)malloc(ia + 1);
16771+   if (d->domain == NULL) {
16772+         policy_free(p);
16773+         local_free(l_list);
16774+         free(d);
16775+         fprintf(stderr, "policy_parse: malloc failed\n");
16776+         return -1;
16777+   }
16778+
16779+   memset(d->domain, 0, ia + 1);
16780+   memcpy(d->domain, domain, ia);
16781+
16782+   d->locals = l_list;
16783+   d->policy = p;
16784+
16785+   d->next = domains;
16786+   domains = d;
16787+
16788+   return 1;
16789+}
16790+
16791+/*
16792+   Parse policy data into a policy structure
16793+*/
16794+
16795+static policy_t *policy_construct(char *data)
16796+{
16797+   char pc = 0, **args = NULL;
16798+   policy_t *p_list = NULL, *p = NULL;
16799+   char *h = NULL, *t = NULL, *hp = NULL;
16800+   int flags = POLICY_F_NONE, numargs = 0, i = 0, ret = 0;
16801+
16802+   if (data == NULL)
16803+         return 0;
16804+
16805+   pc = 0;
16806+
16807+   for (h = data; *h; h++) {
16808+         pc = *h;
16809+
16810+         flags = policy_flags(*h);
16811+         if (flags == POLICY_F_NONE) {
16812+                fprintf(stderr, "policy_construct: unknown identifier\n");
16813+                return NULL;
16814+         }
16815+
16816+         args = NULL;
16817+
16818+         /*
16819+            Count, parse and allocate addresses
16820+         */
16821+
16822+         if (*(h + 1) == '(') {
16823+                t = (h + 2);
16824+
16825+                for (h += 2; *h; h++) {
16826+                       if (*h == ')')
16827+                          break;
16828+                }
16829+
16830+                if (*h != ')') {
16831+                       fprintf(stderr, "policy_construct: no terminating ')'\n");
16832+                       return 0;
16833+                }
16834+
16835+                numargs = 1;
16836+
16837+                for (hp = t; hp < h; hp++) {
16838+                       if (*hp == ',')
16839+                          numargs++;
16840+                }
16841+
16842+                /*
16843+                   No arguments
16844+                */
16845+
16846+                if (hp == t) {
16847+                       fprintf(stderr, "policy_construct: empty argument\n");
16848+                       return NULL;
16849+                }
16850+
16851+                args = (char **)malloc(sizeof(char *) * numargs);
16852+                if (args == NULL) {
16853+                       fprintf(stderr, "policy_construct: malloc failed\n");
16854+                       return NULL;
16855+                }
16856+
16857+                for (i = 0; i < numargs; i++)
16858+                       args[i] = NULL;
16859+
16860+                *h = '\0';
16861+
16862+                ret = policy_construct_parse_arguments(args, numargs, t);
16863+                if (!ret) {
16864+                       fprintf(stderr, "policy_construct: policy_construct_parse_arguments failed\n");
16865+                       free(args);
16866+                       return NULL;
16867+                }
16868+         }
16869+
16870+         /*
16871+            Allocate policy structure,
16872+                add to linked list
16873+         */
16874+
16875+         p = (policy_t *)malloc(sizeof(policy_t));
16876+         if (p == NULL) {
16877+                for (i = 0; i < numargs; i++)
16878+                       free(args[i]);
16879+
16880+                free(args);
16881+
16882+                fprintf(stderr, "policy_construct: malloc failed\n");
16883+                return NULL;
16884+         }
16885+
16886+         memset(p, 0, sizeof(policy_t));
16887+
16888+         p->numargs = numargs;
16889+         p->args = args;
16890+         p->flags = flags;
16891+         p->next = NULL;
16892+
16893+         p->next = p_list;
16894+         p_list = p;
16895+   }
16896+
16897+   return p_list;
16898+}
16899+
16900+/*
16901+   Parse policy arguments,
16902+   fill array
16903+*/
16904+
16905+static int policy_construct_parse_arguments(char **args, const int numargs, char *data)
16906+{
16907+   int len = 0, i = 0;
16908+   char *h = NULL, *t = NULL;
16909+
16910+   i = 0;
16911+
16912+   for (i = 0, h = t = data;;h++) {
16913+        if ((*h == ',') || (*h == '\0')) {
16914+               len = (h - t);
16915+
16916+                if (*h == '\0')
16917+                       h = NULL;
16918+                else
16919+                       *h = '\0';
16920+
16921+                if (!(*t)) {
16922+                       for (; i >= 0; i--)
16923+                          free(args[i]);
16924+
16925+                       fprintf(stderr, "policy_construct_parse_arguments: empty argument value\n");
16926+                       return 0;
16927+                }
16928+
16929+                if (i >= numargs) {
16930+                       for (; i >= 0; i--)
16931+                          free(args[i]);
16932+
16933+                       fprintf(stderr, "policy_construct_parse_arguments: too many arguments\n");
16934+                       return 0;
16935+                }
16936+
16937+                args[i] = (char *)malloc(len + 1);
16938+                if (args[i] == NULL) {
16939+                       for (; i >= 0; i--)
16940+                          free(args[i]);
16941+
16942+                       fprintf(stderr, "policy_construct_parse_arguments: malloc failed\n");
16943+                       return 0;
16944+                }
16945+
16946+                memset(args[i], 0, len + 1);
16947+                memcpy(args[i], t, len);
16948+
16949+                i++;
16950+
16951+                if (!h)
16952+                       break;
16953+
16954+                t = (h + 1);
16955+         }
16956+   }
16957+
16958+   if (i != numargs) {
16959+         fprintf(stderr, "policy_construct_parse_arguments: post argument count failed (%d/%d)\n",
16960+                       i, numargs);
16961+
16962+         for (; i >= 0; i--)
16963+                free(args[i]);
16964+
16965+         return 0;
16966+   }
16967+
16968+   return 1;
16969+}
16970+
16971+/*
16972+   Match a filter against an address string
16973+*/
16974+
16975+static int policy_match(const char *filter, const char *address)
16976+{
16977+   int ret = 0;
16978+
16979+   if ((filter == NULL) || (address == NULL))
16980+         return 0;
16981+
16982+#ifndef FNM_CASEFOLD
16983+   int len = 0, flags = 0;
16984+   char filt[POLICY_MAX_FILTER] = { 0 }, addr[POLICY_MAX_ADDRESS] = { 0 },
16985+               *p = NULL;
16986+
16987+   memset(filt, 0, sizeof(filt));
16988+
16989+   len = strlen(filter);
16990+   if (len >= POLICY_MAX_FILTER)
16991+         len = (POLICY_MAX_FILTER - 1);
16992+
16993+   memcpy(filt, filter, len);
16994+   
16995+   for (p = filt; *p; p++)
16996+         *p = tolower(*p);
16997+   
16998+   memset(addr, 0, sizeof(addr));
16999+
17000+   len = strlen(address);
17001+   if (len >= POLICY_MAX_ADDRESS)
17002+         len = (POLICY_MAX_ADDRESS - 1);
17003+
17004+   memcpy(addr, address, len);
17005+   
17006+   for (p = addr; *p; p++)
17007+         *p = tolower(*p);
17008+
17009+   filter = filt;
17010+   address = addr;
17011+#else
17012+   int flags = FNM_CASEFOLD;
17013+#endif
17014+   
17015+   ret = fnmatch(filter, address, flags);
17016+   if (ret == 0)
17017+         return 1;
17018+
17019+   return 0;
17020+}
17021+
17022+/*
17023+   See if a given policy applies to a particular
17024+   address
17025+*/
17026+
17027+static int policy_applies_to(const policy_t *p, const char *addr)
17028+{
17029+   int i = 0;
17030+
17031+   if ((p == NULL) || (addr == NULL))
17032+         return 0;
17033+
17034+   if (p->numargs == 0) {
17035+#ifdef DEBUG
17036+         fprintf(stderr, "policy_applies_to: no arguments (yes)\n");
17037+#endif
17038+         return 1;
17039+   }
17040+
17041+   if (p->args == NULL) {
17042+#ifdef DEBUG
17043+         fprintf(stderr, "policy_applies_to: broken arguments (no)\n");
17044+#endif
17045+         return 0;
17046+   }
17047+
17048+   for (i = 0; i < p->numargs; i++) {
17049+         if (policy_match(p->args[i], addr)) {
17050+#ifdef DEBUG
17051+                fprintf(stderr, "policy_applies_to: match (yes)\n");
17052+#endif
17053+                return 1;
17054+         }
17055+   }
17056+
17057+#ifdef DEBUG
17058+   fprintf(stderr, "policy_applies_to: no match (no)\n");
17059+#endif
17060+   return 0;
17061+}
17062+
17063+/*
17064+   Return flags for policy identifier
17065+*/
17066+
17067+static int policy_flags(const char c)
17068+{
17069+   int flags = POLICY_F_NONE;
17070+
17071+   switch(c) {
17072+         case 'l':
17073+                flags = (POLICY_F_LOCAL|POLICY_F_DISALLOW);
17074+                break;
17075+
17076+         case 'r':
17077+                flags = (POLICY_F_REMOTE|POLICY_F_DISALLOW);
17078+                break;
17079+
17080+         case 'e':
17081+                flags = (POLICY_F_EXTERNAL|POLICY_F_DISALLOW);
17082+                break;
17083+
17084+         case 'i':
17085+                flags = (POLICY_F_INTERNAL|POLICY_F_DISALLOW);
17086+                break;
17087+
17088+         case 'L':
17089+                flags = (POLICY_F_LOCAL|POLICY_F_ALLOW);
17090+                break;
17091+
17092+         case 'R':
17093+                flags = (POLICY_F_REMOTE|POLICY_F_ALLOW);
17094+                break;
17095+
17096+         case 'E':
17097+                flags = (POLICY_F_EXTERNAL|POLICY_F_ALLOW);
17098+                break;
17099+
17100+         case 'I':
17101+                flags = (POLICY_F_INTERNAL|POLICY_F_ALLOW);
17102+                break;
17103+   
17104+         default:
17105+                break;
17106+   };
17107+
17108+   return flags;
17109+}
17110+
17111+/*
17112+   Find a policy definition
17113+*/
17114+
17115+static policy_t *policy_find(const policy_t *sp, const int flag)
17116+{
17117+   for (; sp; sp = sp->next) {
17118+         if (sp->flags & flag)
17119+                return (policy_t *)sp;
17120+   }
17121+
17122+   return NULL;
17123+}
17124+
17125+/*
17126+   Compare policies and determine
17127+   if messaging is forbidden
17128+*/
17129+
17130+static int policy_forbids(void)
17131+{
17132+   policy_t *pl = NULL, *p = NULL;
17133+   local_t *s_local = NULL, *r_local = NULL;
17134+   int dtype = 0, s_forbid = 0, r_forbid = 0;
17135+
17136+   /*
17137+         Find local policy if any
17138+   */
17139+
17140+   if (s_domain)
17141+         s_local = local_find(s_domain, remoteinfo ? remoteinfo : mailfrom.s);
17142+
17143+   if (r_domain)
17144+         r_local = local_find(r_domain, addr.s);
17145+
17146+   /*
17147+         Determine type of delivery
17148+         (local, remote, external)
17149+   */
17150+
17151+   if ((s_domain) && (r_domain) && (s_domain == r_domain))
17152+         dtype = POLICY_F_LOCAL;
17153+
17154+   else if ((s_domain == NULL) && (r_domain))
17155+         dtype = POLICY_F_EXTERNAL;
17156+
17157+   else if ((s_domain) && (r_domain == NULL))
17158+         dtype = POLICY_F_REMOTE;
17159+
17160+   else if ((s_domain) && (r_domain) && (s_domain != r_domain))
17161+         dtype = POLICY_F_REMOTE;
17162+
17163+   else if ((s_domain == NULL) && (r_domain == NULL)) {
17164+#ifdef DEBUG
17165+         fprintf(stderr, "policy_forbids: no policies for this delivery\n");
17166+#endif
17167+         return 0;
17168+   }
17169+
17170+   else {
17171+         fprintf(stderr, "policy_forbids: unknown delivery type\n");
17172+         return -1;
17173+   }
17174+
17175+   p = NULL;
17176+   s_forbid = r_forbid = 0;
17177+
17178+   /*
17179+         If there is a local rule for sender, use that to
17180+         determine if able to send.  If not, check domain
17181+   */
17182+
17183+   if (s_local) {
17184+         p = policy_find(s_local->policy, dtype);
17185+         if (p) {
17186+                /*
17187+                   See if policy matches
17188+                */
17189+
17190+                if (p->flags & POLICY_F_DISALLOW) {
17191+                       if (policy_applies_to(p, addr.s)) {
17192+#ifdef DEBUG
17193+                          fprintf(stderr, "*** sender local policy disallows\n");
17194+#endif
17195+                          s_forbid = 1;
17196+                       }
17197+
17198+#ifdef DEBUG
17199+                       else
17200+                          fprintf(stderr, "*** sender local policy allows\n");
17201+#endif
17202+                }
17203+
17204+                else {
17205+#ifdef DEBUG
17206+                       if (policy_applies_to(p, addr.s))
17207+                          fprintf(stderr, "*** sender local policy allows\n");
17208+
17209+                       else {
17210+                          fprintf(stderr, "*** sender local policy denies\n");
17211+                          s_forbid = 1;
17212+                       }
17213+#else
17214+                       if (!(policy_applies_to(p, addr.s)))
17215+                                 s_forbid = 1;
17216+#endif
17217+                }
17218+         }
17219+
17220+#ifdef DEBUG
17221+         else
17222+                fprintf(stderr, "*** no sender local policy\n");
17223+#endif
17224+   }
17225+
17226+   if ((p == NULL) && (s_domain)) {
17227+         p = policy_find(s_domain->policy, dtype);
17228+         if (p) {
17229+                if (p->flags & POLICY_F_DISALLOW) {
17230+                       s_forbid = 1;
17231+#ifdef DEBUG
17232+                       fprintf(stderr, "--- sender domain policy disallows\n");
17233+#endif
17234+                }
17235+         }
17236+
17237+#ifdef DEBUG
17238+         else
17239+                fprintf(stderr, "--- no sender domain policy\n");
17240+#endif
17241+   }
17242+
17243+   /*
17244+         Deny messaging
17245+   */
17246+
17247+   if (s_forbid) {
17248+#ifdef DEBUG
17249+         fprintf(stderr, "policy_forbids: sender policy denies messaging\n");
17250+#endif
17251+         return 1;
17252+   }
17253+
17254+   /*
17255+         Reverse delivery type, and check same above
17256+         for recipient unless the recipient is of the
17257+         same domain
17258+   */
17259+
17260+   /*
17261+         A local user to local user delivery is an 'internal'
17262+         delivery for the recipient user.
17263+   */
17264+
17265+   if (dtype == POLICY_F_LOCAL)
17266+         dtype = POLICY_F_INTERNAL;
17267+
17268+   /*
17269+         Sender on same system, different domain.
17270+         This is considered an 'external' delivery
17271+         to the recipient
17272+   */
17273+
17274+   else if (dtype == POLICY_F_REMOTE)
17275+         dtype = POLICY_F_EXTERNAL;
17276+
17277+   /*
17278+         Sender from off-system remains
17279+         as external
17280+   */
17281+
17282+   else if (dtype != POLICY_F_EXTERNAL) {
17283+         fprintf(stderr, "policy_forbids: unknown recipient delivery type\n");
17284+         return -1;
17285+   }
17286+
17287+   p = NULL;
17288+   r_forbid = 0;
17289+
17290+   if (r_local) {
17291+         p = policy_find(r_local->policy, dtype);
17292+         if (p) {
17293+                /*
17294+                   See if policy matches
17295+                */
17296+
17297+                if (p->flags & POLICY_F_DISALLOW) {
17298+                       if (policy_applies_to(p, remoteinfo ? remoteinfo : mailfrom.s)) {
17299+#ifdef DEBUG
17300+                          fprintf(stderr, "*** recipient local policy disallows\n");
17301+#endif
17302+                          r_forbid = 1;
17303+                       }
17304+
17305+#ifdef DEBUG
17306+                       else
17307+                          fprintf(stderr, "*** recipient local policy allows\n");
17308+#endif
17309+                }
17310+
17311+                else {
17312+#ifdef DEBUG
17313+                       if (policy_applies_to(p, remoteinfo ? remoteinfo : mailfrom.s))
17314+                          fprintf(stderr, "*** recipient local policy allows\n");
17315+
17316+                       else {
17317+                          fprintf(stderr, "*** recipient local policy denies\n");
17318+                          r_forbid = 1;
17319+                       }
17320+#else
17321+                       if (!(policy_applies_to(p, remoteinfo ? remoteinfo : mailfrom.s)))
17322+                                 r_forbid = 1;
17323+#endif
17324+                }
17325+         }
17326+
17327+#ifdef DEBUG
17328+         else
17329+                fprintf(stderr, "*** no recipient local policy\n");
17330+#endif
17331+   }
17332+
17333+   if ((p == NULL) && (r_domain)) {
17334+         p = policy_find(r_domain->policy, dtype);
17335+         if (p) {
17336+                if (p->flags & POLICY_F_DISALLOW) {
17337+                       r_forbid = 1;
17338+#ifdef DEBUG
17339+                       fprintf(stderr, "--- recipient domain policy disallows\n");
17340+#endif
17341+                }
17342+         }
17343+
17344+#ifdef DEBUG
17345+         else
17346+                fprintf(stderr, "--- no recipient domain policy\n");
17347+#endif
17348+
17349+   }
17350+
17351+   /*
17352+         Deny messaging
17353+   */
17354+
17355+   if (r_forbid) {
17356+#ifdef DEBUG
17357+         fprintf(stderr, "policy_forbids: recipient policy denies messaging\n");
17358+#endif
17359+         return 1;
17360+   }
17361+
17362+   /*
17363+         Accept message
17364+   */
17365+
17366+   return 0;
17367+}
17368+
17369+/*
17370+   Search for a domain in the linked list of domains
17371+*/
17372+
17373+static domain_t *domain_find(const char *domain)
17374+{
17375+   domain_t *d = NULL;
17376+
17377+   if (domain == NULL)
17378+         return NULL;
17379+
17380+   for (d = domains; d; d = d->next) {
17381+         if (!(strcasecmp(d->domain, domain)))
17382+                return d;
17383+   }
17384+
17385+   return NULL;
17386+}
17387+
17388+/*
17389+   Search for a local under a domain in the locals linked list
17390+*/
17391+
17392+static local_t *local_find(const domain_t *d, const char *local)
17393+{
17394+   local_t *l = NULL;
17395+
17396+   if ((d == NULL) || (local == NULL)) {
17397+         fprintf(stderr, "local_find: null argument\n");
17398+         return NULL;
17399+   }
17400+
17401+   for (l = d->locals; l; l = l->next) {
17402+         if (policy_match(l->local, local))
17403+                return l;
17404+   }
17405+
17406+   return NULL;
17407+}
17408+
17409+/*
17410+   Deallocate a policy
17411+*/
17412+
17413+static void policy_free(policy_t *policy)
17414+{
17415+   int i = 0;
17416+   policy_t *p = NULL, *op = NULL;
17417+
17418+   if (policy == NULL)
17419+         return;
17420+
17421+   p = policy;
17422+
17423+   while(p) {
17424+         op = p;
17425+         p = p->next;
17426+
17427+         if (op->args) {
17428+                if (op->numargs) {
17429+                       for (i = 0; i < op->numargs; i++)
17430+                          free(op->args[i]);
17431+
17432+                       free(op->args);
17433+                }
17434+
17435+                else
17436+                       fprintf(stderr, "policy_free: no argument count\n");
17437+         }
17438+
17439+         free(op);
17440+   }
17441+}
17442+
17443+/*
17444+   Deallocate a local
17445+*/
17446+
17447+static void local_free(local_t *local)
17448+{
17449+   local_t *l = NULL, *ol = NULL;
17450+
17451+   if (local == NULL)
17452+         return;
17453+
17454+   l = local;
17455+
17456+   while(l) {
17457+         ol = l;
17458+         l = l->next;
17459+
17460+         if (ol->policy)
17461+                policy_free(ol->policy);
17462+
17463+         if (ol->local)
17464+                free(ol->local);
17465+
17466+         free(ol);
17467+   }
17468+}
17469+
17470+/*
17471+   Deallocate all domains
17472+*/
17473+
17474+static void domains_free(void)
17475+{
17476+   domain_t *d = NULL, *od = NULL;
17477+
17478+   d = domains;
17479+
17480+   while(d) {
17481+         od = d;
17482+         d = d->next;
17483+
17484+         if (od->policy)
17485+                policy_free(od->policy);
17486+
17487+         if (od->locals)
17488+                local_free(od->locals);
17489+
17490+         if (od->domain)
17491+                free(od->domain);
17492+
17493+         free(od);
17494+   }
17495+
17496+   domains = NULL;
17497+}
17498diff -ruN ../netqmail-1.06-original/policy.h netqmail-1.06/policy.h
17499--- ../netqmail-1.06-original/policy.h  1970-01-01 01:00:00.000000000 +0100
17500+++ netqmail-1.06/policy.h      2019-02-27 20:57:13.395025014 +0100
17501@@ -0,0 +1,6 @@
17502+#ifndef __POLICY_H_
17503+   #define __POLICY_H_
17504+
17505+int policy_check(void);
17506+
17507+#endif
17508diff -ruN ../netqmail-1.06-original/qmail-clean.c netqmail-1.06/qmail-clean.c
17509--- ../netqmail-1.06-original/qmail-clean.c     1998-06-15 12:53:16.000000000 +0200
17510+++ netqmail-1.06/qmail-clean.c 2019-02-27 20:57:13.395025014 +0100
17511@@ -73,22 +73,26 @@
17512    if (line.len < 7) { respond("x"); continue; }
17513    if (line.len > 100) { respond("x"); continue; }
17514    if (line.s[line.len - 1]) { respond("x"); continue; } /* impossible */
17515-   for (i = 5;i < line.len - 1;++i)
17516+   for (i = line.len - 2;i > 4;--i)
17517+    {
17518+     if (line.s[i] == '/') break;
17519      if ((unsigned char) (line.s[i] - '0') > 9)
17520       { respond("x"); continue; }
17521-   if (!scan_ulong(line.s + 5,&id)) { respond("x"); continue; }
17522+    }
17523+   if (line.s[i] == '/')
17524+     if (!scan_ulong(line.s + i + 1,&id)) { respond("x"); continue; }
17525    if (byte_equal(line.s,5,"foop/"))
17526     {
17527 #define U(prefix,flag) fmtqfn(fnbuf,prefix,id,flag); \
17528 if (unlink(fnbuf) == -1) if (errno != error_noent) { respond("!"); continue; }
17529-     U("intd/",0)
17530+     U("intd/",1)
17531      U("mess/",1)
17532      respond("+");
17533     }
17534    else if (byte_equal(line.s,4,"todo/"))
17535     {
17536-     U("intd/",0)
17537-     U("todo/",0)
17538+     U("intd/",1)
17539+     U("todo/",1)
17540      respond("+");
17541     }
17542    else
17543diff -ruN ../netqmail-1.06-original/qmail-control.9 netqmail-1.06/qmail-control.9
17544--- ../netqmail-1.06-original/qmail-control.9   1998-06-15 12:53:16.000000000 +0200
17545+++ netqmail-1.06/qmail-control.9       2019-02-27 20:57:13.395025014 +0100
17546@@ -20,7 +20,11 @@
17547 
17548 Comments are allowed
17549 in
17550+.IR badhelo ,
17551 .IR badmailfrom ,
17552+.IR badmailfromnorelay ,
17553+.IR badmailto ,
17554+.IR badmailtonorelay ,
17555 .IR locals ,
17556 .IR percenthack ,
17557 .IR qmqpservers ,
17558@@ -40,14 +44,25 @@
17559 .ta 5c 10c
17560 control        default used by
17561 
17562+.I authsender  \fR(none)       \fRqmail-remote
17563+.I badhelo     \fR(none)       \fRqmail-smtpd
17564+.I badhelonorelay     \fR(none)       \fRqmail-smtpd
17565 .I badmailfrom \fR(none)       \fRqmail-smtpd
17566+.I badmailfromnorelay  \fR(none)       \fRqmail-smtpd
17567+.I badmailto   \fR(none)       \fRqmail-smtpd
17568+.I badmailtonorelay    \fR(none)       \fRqmail-smtpd
17569 .I bouncefrom  \fRMAILER-DAEMON        \fRqmail-send
17570 .I bouncehost  \fIme   \fRqmail-send
17571+.I brtlimit    \fR0    \fRqmail-smtpd
17572+.I clientca.pem        \fR(none)       \fRqmail-smtpd
17573+.I clientcert.pem      \fR(none)       \fRqmail-remote
17574 .I concurrencylocal    \fR10   \fRqmail-send
17575 .I concurrencyremote   \fR20   \fRqmail-send
17576 .I defaultdomain       \fIme   \fRqmail-inject
17577 .I defaulthost \fIme   \fRqmail-inject
17578 .I databytes   \fR0    \fRqmail-smtpd
17579+.I dnsbllist   \fR(none)       \fRqmail-smtpd
17580+.I dh2048.pem  \fR(none)       \fRqmail-smtpd
17581 .I doublebouncehost    \fIme   \fRqmail-send
17582 .I doublebounceto      \fRpostmaster   \fRqmail-send
17583 .I envnoathost \fIme   \fRqmail-send
17584@@ -56,22 +71,35 @@
17585 .I localiphost \fIme   \fRqmail-smtpd
17586 .I locals      \fIme   \fRqmail-send
17587 .I morercpthosts       \fR(none)       \fRqmail-smtpd
17588+.I outgoingip  \fR0.0.0.0      \fRqmail-remote
17589 .I percenthack \fR(none)       \fRqmail-send
17590 .I plusdomain  \fIme   \fRqmail-inject
17591 .I qmqpservers \fR(none)       \fRqmail-qmqpc
17592 .I queuelifetime       \fR604800       \fRqmail-send
17593 .I rcpthosts   \fR(none)       \fRqmail-smtpd
17594+.I rsa2048.pem \fR(none)       \fRqmail-smtpd
17595+.I servercert.pem      \fR(none)       \fRqmail-smtpd
17596 .I smtpgreeting        \fIme   \fRqmail-smtpd
17597 .I smtproutes  \fR(none)       \fRqmail-remote
17598+.I spfbehavior \fR0    \fRqmail-smtpd
17599+.I spfexp      \fR(default)    \fRqmail-smtpd
17600+.I spfguess    \fR(none)       \fRqmail-smtpd
17601+.I spfrules    \fR(none)       \fRqmail-smtpd
17602+.I taps        \fR(none)       \fRqmail-queue
17603 .I timeoutconnect      \fR60   \fRqmail-remote
17604 .I timeoutremote       \fR1200 \fRqmail-remote
17605 .I timeoutsmtpd        \fR1200 \fRqmail-smtpd
17606+.I tlsclients  \fR(none)       \fRqmail-smtpd
17607+.I tlsclientciphers    \fR(none)       \fRqmail-remote
17608+.I tlshosts/FQDN.pem   \fR(none)       \fRqmail-remote
17609+.I tlsserverciphers    \fR(none)       \fRqmail-smtpd
17610 .I virtualdomains      \fR(none)       \fRqmail-send
17611 .fi
17612 .RE
17613 .SH "SEE ALSO"
17614 qmail-inject(8),
17615 qmail-qmqpc(8),
17616+qmail-queue(8),
17617 qmail-remote(8),
17618 qmail-send(8),
17619 qmail-showctl(8),
17620diff -ruN ../netqmail-1.06-original/qmail-dk.9 netqmail-1.06/qmail-dk.9
17621--- ../netqmail-1.06-original/qmail-dk.9        1970-01-01 01:00:00.000000000 +0100
17622+++ netqmail-1.06/qmail-dk.9    2019-02-27 20:57:13.395025014 +0100
17623@@ -0,0 +1,164 @@
17624+.TH qmail-dk 8
17625+.SH NAME
17626+qmail-dk \- sign/verify and queue a mail message for delivery
17627+.SH SYNOPSIS
17628+.B qmail-dk
17629+.SH DESCRIPTION
17630+.B qmail-dk
17631+has the same interface as
17632+.B qmail-queue
17633+except that it inserts an appropriate DomainKeys header before it
17634+queues the message.  To invoke
17635+.BR qmail-dk ,
17636+set QMAILQUEUE to point to qmail-dk in the environment when
17637+you send or receive email. qmail-dk will call
17638+.BR qmail-multi .
17639+To invoke an executable other than
17640+.B qmail-multi
17641+set DKQUEUE=bin/qmail-queue for example.
17642+
17643+.B qmail-dk
17644+supports DomainKey signing and verification.  It uses the libdomainkey
17645+and OpenSSL libraries.  To sign a message, set the
17646+.B DKSIGN
17647+environment variable to the pathname to the private key that will be
17648+used to sign the message.  If there is a % character in the environment
17649+variable, it is removed and replaced by the domain name in the From: header.
17650+If, after substituting the %, that file does not exist, the % will be removed.
17651+If the private key file does not exist and does not have a % character, the message will
17652+be rejected with error 32. The selector will be taken from the basename of the file.
17653+The private key should be created by
17654+.BR dknewkey(8) .
17655+
17656+In the absense of DKSIGN and DKVERIFY environment variable, qmail-dk will sign the
17657+message if RELAYCLIENT environment variable is set and verify the message if RELAYCLIENT
17658+environment variable is not set. If DKVERIFY is set, you can disable dk verification, if
17659+RELAYCLIENT is set by setting RELAYCLIENT_NODKVERIFY environment variable.
17660+
17661+You can set various DK options in getopt style, by setting the environment variable DKSIGNOPTIONS
17662+ b <advice_length>    Length of Advice
17663+ c <canonicalization> simple, nofws
17664+ s <privkeyfile>
17665+ h                    include h= tag
17666+ r                    Skip Duplicate Headers
17667+.EX
17668+ DKSIGNOPTIONS="-h -r -c nofws"
17669+ sets the h= tag, skips duplicate headers and sets nofws canonicalization
17670+.EE
17671+
17672+To verify a message, set the
17673+.B DKVERIFY
17674+environment variable to a desired set of letters.  Precisely, if you
17675+want a libdomainkey return status to generate an error, include that
17676+letter, where A is the first return status (DK_STAT_OK), B is the
17677+second (DK_STAT_BADSIG), etc.  The letter should be uppercase if you
17678+want a permanent error to be returned and lowercase if you want a temporary error
17679+to be returned.
17680+
17681+The complete set of letters with the corresponding return status is given below
17682+
17683+ A - DK_STAT_OK           - Function completed successfully
17684+ B - DK_STAT_BADSIG       - Signature was available but failed to verify against
17685+                            domain specified key
17686+ C - DK_STAT_NOSIG        - No signature available in message
17687+ D - DK_STAT_NOKEY        - No public key available (permanent failure)
17688+ E - DK_STAT_BADKEY       - Unusable key, public if verifying, private if signing
17689+ F - DK_STAT_CANTVRFY     - Cannot get domain key to verify signature
17690+                            (temporary failure)
17691+ G - DK_STAT_SYNTAX       - Message is not valid syntax. Signature could not be
17692+                            created/checked
17693+ H - DK_STAT_NORESOURCE   - Could not get critical resource (temporary failure)
17694+ I - DK_STAT_ARGS         - Arguments are not usable.
17695+ J - DK_STAT_REVOKED      - Key has been revoked.
17696+ K - DK_STAT_INTERNAL     - cannot call this routine in this context. Internal error.
17697+ L - DK_STAT_GRANULARITY  - Granularity mismatch: sender doesn't match g= option.
17698+ M - DK_STAT_DUPLICATE    - duplicate Domainkey-Signature in message.
17699+
17700+For example, if you want to permanently reject messages that have a
17701+signature that has been revoked, include the letter 'K' in the
17702+.B DKVERIFY
17703+environment variable.  A conservative set of letters is
17704+.BR DEGIJKfh .
17705+Reject permanently BADSIG, NOKEY, BADKEY, SYNTAX, ARGS, REVOKED, and
17706+INTERNAL errors, and temporarily CANTVRFY and NORESOURCE. Add in
17707+.B B
17708+if you want to reject messages that have a signature that doesn't
17709+verify (presumably because the message is a forgery or has been
17710+damaged in transit.  Note that
17711+.B qmail-dk
17712+always inserts the
17713+.B DomainKey-Status
17714+header, so that messages can be
17715+rejected at delivery time, or in the mail reader.
17716+
17717+Typically, you would sign messages generated on-host by setting
17718+.B DKSIGN
17719+in the environment before running an email program.  DKSIGN will be carried
17720+through qmail's sendmail emulation through
17721+.B qmail-inject
17722+to
17723+.BR qmail-dk .
17724+You would also set it for
17725+.B qmail-smtpd
17726+at the same time
17727+.B RELAYCLIENT
17728+is set, most often in the tcpserver cdb file.  If a host is authorized
17729+to relay, you probably want to sign messages sent by that host.
17730+.B DKVERIFY
17731+should be set for all other hosts.
17732+
17733+If neither
17734+.B DKSIGN
17735+nor
17736+.B DKVERIFY
17737+are set, then
17738+.B DKSIGN
17739+will be set to QMAILHOME/control/domainkeys/%/default. The % will be replaced by the domain in the
17740+From: header. If such a file does not exist, then it will be set to
17741+QMAILHOME/control/domainkeys/default. If such a private key exists, it will be used to sign the
17742+domain. You can also set \fBDKKEY\fR to chose a key different from
17743+QMAILHOME/control/domainkeys/%/default. \fBDKKEY\fR can also have % character that will be
17744+replaced by the domain in the From: header.
17745+
17746+By default
17747+.B qmail-dk
17748+will use all of the headers when signing a message.
17749+The environment variable
17750+.B DKEXCLUDEHEADERS
17751+may be set to a colon-separated list of headers that are to be excluded from signing.
17752+
17753+.B qmail-dk
17754+will ordinarily spawn
17755+.BR qmail-multi ,
17756+but if DKQUEUE is set in the environment,
17757+the program that it points to will be executed instead.
17758+
17759+.SH "EXIT CODES"
17760+.B qmail-dk
17761+returns the same exit codes as qmail-queue with these additions:
17762+.TP 5
17763+.B 35
17764+The private key file does not exist.
17765+.TP 5
17766+.B 57
17767+Trouble waiting for qmail-queue to exit.
17768+.TP 5
17769+.B 58
17770+Unable to vfork.
17771+.TP 5
17772+.B 59
17773+Unable to create a pipe to qmail-queue.
17774+.SH "SEE ALSO"
17775+addresses(5),
17776+envelopes(5),
17777+qmail-header(5),
17778+dknewkey(8),
17779+dktest(8),
17780+qmail-inject(8),
17781+qmail-qmqpc(8),
17782+qmail-queue(8),
17783+qmail-send(8),
17784+qmail-smtpd(8),
17785+qmail-dkim(8),
17786+domain-keys(5)
17787+
17788diff -ruN ../netqmail-1.06-original/qmail-dk.c netqmail-1.06/qmail-dk.c
17789--- ../netqmail-1.06-original/qmail-dk.c        1970-01-01 01:00:00.000000000 +0100
17790+++ netqmail-1.06/qmail-dk.c    2019-02-27 20:57:13.396025003 +0100
17791@@ -0,0 +1,866 @@
17792+/*
17793+ * $Log: qmail-dk.c,v $
17794+ * Revision 1.37  2013-01-24 22:42:07+05:30  Cprogrammer
17795+ * alternate code for chosing DKSIGN selector filename
17796+ *
17797+ * Revision 1.36  2011-11-10 14:31:42+05:30  Cprogrammer
17798+ * BUG ssout to be assigned only after pidopen()
17799+ *
17800+ * Revision 1.35  2011-11-07 09:35:25+05:30  Cprogrammer
17801+ * set ssout, sserr, ssin before executing other functions
17802+ *
17803+ * Revision 1.34  2011-07-29 09:28:48+05:30  Cprogrammer
17804+ * fixed key file name
17805+ *
17806+ * Revision 1.33  2011-07-28 19:34:45+05:30  Cprogrammer
17807+ * BUG - fixed opening of private key with absolute path
17808+ *
17809+ * Revision 1.32  2011-07-22 19:29:06+05:30  Cprogrammer
17810+ * fixed compilation error
17811+ *
17812+ * Revision 1.31  2011-07-22 14:39:53+05:30  Cprogrammer
17813+ * added DKDOMAIN feature to set d= tag
17814+ *
17815+ * Revision 1.30  2011-06-04 17:44:16+05:30  Cprogrammer
17816+ * remove % sign from private key if file not found
17817+ *
17818+ * Revision 1.29  2010-07-21 08:59:14+05:30  Cprogrammer
17819+ * use CONTROLDIR environment variable instead of a hardcoded control directory
17820+ *
17821+ * Revision 1.28  2009-08-13 18:36:39+05:30  Cprogrammer
17822+ * for verification continue in case of DK_SYNTAX errors
17823+ *
17824+ * Revision 1.27  2009-04-22 13:42:47+05:30  Cprogrammer
17825+ * made fd for custom error configurable through env variable ERROR_FD
17826+ *
17827+ * Revision 1.26  2009-04-21 09:05:14+05:30  Cprogrammer
17828+ * return relevant error message for reading private key
17829+ *
17830+ * Revision 1.25  2009-04-21 08:56:03+05:30  Cprogrammer
17831+ * return temp errors for temporary failures
17832+ *
17833+ * Revision 1.24  2009-04-21 08:15:39+05:30  Cprogrammer
17834+ * moved stralloc variable outside block
17835+ *
17836+ * Revision 1.23  2009-04-20 22:20:40+05:30  Cprogrammer
17837+ * added DKSIGNOPTIONS
17838+ *
17839+ * Revision 1.22  2009-04-07 11:38:02+05:30  Cprogrammer
17840+ * use TMPDIR env variable for tmp directory
17841+ *
17842+ * Revision 1.21  2009-04-05 12:52:10+05:30  Cprogrammer
17843+ * added preprocessor warning
17844+ *
17845+ * Revision 1.20  2009-03-31 08:21:12+05:30  Cprogrammer
17846+ * set dksign when RELAYCLIENT is defined when both dksign, dkverify are undefined
17847+ *
17848+ * Revision 1.19  2009-03-28 22:26:35+05:30  Cprogrammer
17849+ * use DKSIGN,DKVERIFY env variables if RELAYCLIENT is not set
17850+ *
17851+ * Revision 1.18  2009-03-28 22:02:32+05:30  Cprogrammer
17852+ * removed extra white space
17853+ *
17854+ * Revision 1.17  2009-03-28 11:34:51+05:30  Cprogrammer
17855+ * BUG fix. corrected setting of dksign, dkverify variables
17856+ *
17857+ * Revision 1.16  2009-03-22 16:58:13+05:30  Cprogrammer
17858+ * report custom errors to qmail-queue through custom error interface
17859+ *
17860+ * Revision 1.15  2009-03-21 15:15:56+05:30  Cprogrammer
17861+ * improved logic
17862+ *
17863+ * Revision 1.14  2009-03-20 22:35:24+05:30  Cprogrammer
17864+ * fix for multi-line headers
17865+ *
17866+ * Revision 1.13  2009-03-19 08:28:12+05:30  Cprogrammer
17867+ * added EXCLUDEHEADERS
17868+ *
17869+ * Revision 1.12  2009-03-14 17:11:32+05:30  Cprogrammer
17870+ * added DK_STAT_GRANULARITY
17871+ *
17872+ * Revision 1.11  2009-03-14 08:52:54+05:30  Cprogrammer
17873+ * look for domainkey in control/domainkeys
17874+ *
17875+ * Revision 1.10  2005-08-23 17:33:37+05:30  Cprogrammer
17876+ * gcc 4 compliance
17877+ *
17878+ * Revision 1.9  2005-04-01 21:42:04+05:30  Cprogrammer
17879+ * added DK_STAT_SYNTAX
17880+ * changed error codes
17881+ *
17882+ * Revision 1.8  2004-11-02 09:15:53+05:30  Cprogrammer
17883+ * commented out writing of Comments: header
17884+ *
17885+ * Revision 1.7  2004-10-24 21:32:00+05:30  Cprogrammer
17886+ * removed unecessary header files
17887+ *
17888+ * Revision 1.6  2004-10-22 20:28:18+05:30  Cprogrammer
17889+ * added RCS id
17890+ *
17891+ * Revision 1.5  2004-10-22 15:36:45+05:30  Cprogrammer
17892+ * removed readwrite.h
17893+ *
17894+ * Revision 1.4  2004-10-22 14:44:26+05:30  Cprogrammer
17895+ * use control_readnativefile to avoid skipping signure with '#' char
17896+ *
17897+ * Revision 1.3  2004-10-20 20:08:53+05:30  Cprogrammer
17898+ * libdomainkeys-0.62
17899+ *
17900+ * Revision 1.2  2004-09-23 22:55:32+05:30  Cprogrammer
17901+ * removed uneccessary header files
17902+ *
17903+ * Revision 1.1  2004-08-28 01:02:16+05:30  Cprogrammer
17904+ * Initial revision
17905+ *
17906+ */
17907+#include <unistd.h>
17908+#include <stdlib.h>
17909+#include <sys/types.h>
17910+#include <sys/stat.h>
17911+#include "sgetopt.h"
17912+#include "substdio.h"
17913+#include "open.h"
17914+#include "qmail.h"
17915+#include "sig.h"
17916+#include "fmt.h"
17917+#include "fd.h"
17918+#include "alloc.h"
17919+#include "str.h"
17920+#include "getln.h"
17921+#include "case.h"
17922+#include "stralloc.h"
17923+#include "datetime.h"
17924+#include "now.h"
17925+#include "wait.h"
17926+#include "auto_qmail.h"
17927+#include "env.h"
17928+#include "scan.h"
17929+#include "mess822.h"
17930+#include "control.h"
17931+#include "domainkeys.h"
17932+
17933+#define DEATH 86400    /*- 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
17934+#define ADDR 1003
17935+#define ADVICE_BUF 2048
17936+
17937+char            inbuf[2048];
17938+struct substdio ssin;
17939+char            outbuf[256];
17940+struct substdio ssout;
17941+struct substdio sserr;
17942+char            errbuf[256];
17943+
17944+datetime_sec    starttime;
17945+struct datetime dt;
17946+unsigned long   mypid;
17947+unsigned long   uid;
17948+char           *pidfn;
17949+int             messfd;
17950+int             readfd;
17951+char           *dksign = 0;
17952+char           *dkverify = 0;
17953+
17954+char          **MakeArgs(char *);
17955+void            FreeMakeArgs(char **);
17956+
17957+void
17958+die(e)
17959+       int             e;
17960+{
17961+       _exit(e);
17962+}
17963+
17964+void
17965+die_write()
17966+{
17967+       die(53);
17968+}
17969+
17970+void
17971+die_read()
17972+{
17973+       die(54);
17974+}
17975+
17976+void
17977+sigalrm()
17978+{
17979+       /*- thou shalt not clean up here */
17980+       die(52);
17981+}
17982+
17983+void
17984+sigbug()
17985+{
17986+       die(81);
17987+}
17988+
17989+void
17990+custom_error(char *flag, char *status, char *code)
17991+{
17992+       char           *c;
17993+
17994+       if (substdio_put(&sserr, flag, 1) == -1)
17995+               die_write();
17996+       if (substdio_put(&sserr, "qmail-dk: ", 10) == -1)
17997+               die_write();
17998+       if (substdio_puts(&sserr, status) == -1)
17999+               die_write();
18000+       if (code)
18001+       {
18002+               if (substdio_put(&sserr, " (#", 3) == -1)
18003+                       die_write();
18004+               c = (*flag == 'Z') ? "4" : "5";
18005+               if (substdio_put(&sserr, c, 1) == -1)
18006+                       die_write();
18007+               if (substdio_put(&sserr, code + 1, 4) == -1)
18008+                       die_write();
18009+               if (substdio_put(&sserr, ")", 1) == -1)
18010+                       die_write();
18011+       }
18012+       if (substdio_flush(&sserr) == -1)
18013+               die_write();
18014+       return;
18015+}
18016+
18017+void
18018+maybe_die_dk(e)
18019+       DK_STAT         e;
18020+{
18021+       switch (e)
18022+       {
18023+       case DK_STAT_NORESOURCE:
18024+               _exit(51);
18025+       case DK_STAT_INTERNAL:
18026+               _exit(81);
18027+       case DK_STAT_ARGS:
18028+               custom_error("Z", "Arguments are not usable. (#4.3.5)", 0);
18029+               _exit(88);
18030+       case DK_STAT_SYNTAX:
18031+               if (!dksign && dkverify)
18032+                       return;
18033+               custom_error("Z", "Message is not valid syntax. Signature could not be created/checked (#4.6.0)", 0);
18034+               _exit(88);
18035+       case DK_STAT_CANTVRFY:
18036+               custom_error("Z", "Cannot get domainkeys to verify signature (#5.7.5)", 0);
18037+               _exit(88);
18038+       case DK_STAT_BADKEY:
18039+               if (env_get("DKVERIFY"))
18040+                       custom_error("Z", "Unusable public key for verifying (#5.7.5)", 0);
18041+               else
18042+                       custom_error("Z", "Unusable private key for signing (#5.7.15", 0);
18043+               _exit(88);
18044+       default:
18045+               return;
18046+       }
18047+}
18048+
18049+unsigned int
18050+pidfmt(s, seq)
18051+       char           *s;
18052+       unsigned long   seq;
18053+{
18054+       unsigned int    i;
18055+       unsigned int    len;
18056+       char           *tmpdir;
18057+
18058+       if (!(tmpdir = env_get("TMPDIR")))
18059+               tmpdir = "/tmp";
18060+       len = 0;
18061+       i = fmt_str(s, tmpdir);
18062+       len += i;
18063+       if (s)
18064+               s += i;
18065+       i = fmt_str(s, "/qmail-dk.");
18066+       len += i;
18067+       if (s)
18068+               s += i;
18069+       i = fmt_ulong(s, mypid);
18070+       len += i;
18071+       if (s)
18072+               s += i;
18073+       i = fmt_str(s, ".");
18074+       len += i;
18075+       if (s)
18076+               s += i;
18077+       i = fmt_ulong(s, starttime);
18078+       len += i;
18079+       if (s)
18080+               s += i;
18081+       i = fmt_str(s, ".");
18082+       len += i;
18083+       if (s)
18084+               s += i;
18085+       i = fmt_ulong(s, seq);
18086+       len += i;
18087+       if (s)
18088+               s += i;
18089+       ++len;
18090+       if (s)
18091+               *s++ = 0;
18092+
18093+       return len;
18094+}
18095+
18096+void
18097+pidopen()
18098+{
18099+       unsigned int    len;
18100+       unsigned long   seq;
18101+
18102+       seq = 1;
18103+       len = pidfmt((char *) 0, seq);
18104+       if (!(pidfn = alloc(len)))
18105+               die(51);
18106+       for (seq = 1; seq < 10; ++seq)
18107+       {
18108+               if (pidfmt((char *) 0, seq) > len)
18109+                       die(81); /*- paranoia */
18110+               pidfmt(pidfn, seq);
18111+               if ((messfd = open_excl(pidfn)) != -1)
18112+                       return;
18113+       }
18114+       die(63);
18115+}
18116+
18117+char            tmp[FMT_ULONG];
18118+DK_LIB         *dklib;
18119+DK             *dk;
18120+DK_STAT         st;
18121+stralloc        dkoutput = { 0 };    /*- Domainkey-Signature */
18122+stralloc        dksignature = { 0 }; /*- content of private signature */
18123+stralloc        dkopts = { 0 };
18124+char           *dkqueue = 0;
18125+char           *dkexcludeheaders;
18126+
18127+static void
18128+write_signature(DK *dk, char *dk_selector, char *keyfn,
18129+       int advicelen, int opth, char *canon)
18130+{
18131+       unsigned char   advice[ADVICE_BUF];
18132+       char           *selector, *from, *tmp;
18133+       static stralloc keyfnfrom = { 0 };
18134+       int             i;
18135+
18136+       from = dk_from(dk);
18137+       if (keyfn[0] != '/')
18138+       {
18139+               if (!stralloc_copys(&keyfnfrom, "control/"))
18140+                       die(51, 1);
18141+       }
18142+       i = str_chr(keyfn, '%');
18143+       if (keyfn[i])
18144+       {
18145+               if (keyfn[0] == '/')
18146+               {
18147+                       if (!stralloc_copyb(&keyfnfrom, keyfn, i))
18148+                               die(51, 1);
18149+               } else
18150+               if (!stralloc_catb(&keyfnfrom, keyfn, i))
18151+                       die(51, 1);
18152+               if (!stralloc_cats(&keyfnfrom, from))
18153+                       die(51);
18154+               if (!stralloc_cats(&keyfnfrom, keyfn + i + 1))
18155+                       die(51);
18156+               if (!stralloc_0(&keyfnfrom))
18157+                       die(51);
18158+               if (access(keyfnfrom.s, F_OK))
18159+               {
18160+                       /*- since file is not found remove '%' sign */
18161+                       keyfnfrom.len = 8;
18162+                       if (keyfn[0] == '/')
18163+                       {
18164+                               if (!stralloc_copyb(&keyfnfrom, keyfn, i))
18165+                                       die(51, 1);
18166+                       } else
18167+                       if (!stralloc_catb(&keyfnfrom, keyfn, i))
18168+                               die(51, 1);
18169+                       if ((i - 1) > 0 && keyfn[i - 1] == '/' && keyfn[i + 1] == '/')
18170+                               i++;
18171+                       if (!stralloc_cats(&keyfnfrom, keyfn + i + 1))
18172+                               die(51);
18173+                       if (!stralloc_0(&keyfnfrom))
18174+                               die(51);
18175+               }
18176+       } else
18177+       {
18178+               if (keyfn[0] == '/')
18179+               {
18180+                       if (!stralloc_copys(&keyfnfrom, keyfn))
18181+                               die(51, 1);
18182+               } else
18183+               if (!stralloc_cats(&keyfnfrom, keyfn))
18184+                       die(51, 1);
18185+               if (!stralloc_0(&keyfnfrom))
18186+                       die(51);
18187+       }
18188+       switch (control_readnativefile(&dksignature, keyfnfrom.s, 1))
18189+       {
18190+       case 0: /*- file not present */
18191+               /*
18192+                * You may have multiple domains, but may chose to sign
18193+                * only for few domains which have the key present. Do not
18194+                * treat domains with missing key as an error.
18195+                */
18196+               if (keyfn[i])
18197+                       return;
18198+               die(32);
18199+       case 1:
18200+               break;
18201+       default:
18202+               custom_error("Z", "Unable to read private key. (#4.3.0)", 0);
18203+               _exit(88);
18204+       }
18205+       for (i = 0; i < dksignature.len; i++)
18206+       {
18207+               if (dksignature.s[i] == '\0')
18208+                       dksignature.s[i] = '\n';
18209+       }
18210+       if (!stralloc_0(&dksignature))
18211+               die(51);
18212+       st = dk_getsig(dk, dksignature.s, advice, advicelen);
18213+       maybe_die_dk(st);
18214+       if (!dk_selector)
18215+       {
18216+               selector = keyfn;
18217+               while (*keyfn)
18218+               {
18219+                       if (*keyfn == '/')
18220+                               selector = keyfn + 1;
18221+                       keyfn++;
18222+               }
18223+       } else
18224+               selector = dk_selector;
18225+       if (!stralloc_cats(&dkoutput,
18226+#if 0
18227+               "Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n"
18228+#endif
18229+               "DomainKey-Signature: a=rsa-sha1; q=dns; c="))
18230+               die(51);
18231+       if (!stralloc_cats(&dkoutput, canon))
18232+               die(51);
18233+       if (!stralloc_cats(&dkoutput, ";\n"))
18234+               die(51);
18235+       if (!stralloc_cats(&dkoutput, "    s="))
18236+               die(51);
18237+       if (!stralloc_cats(&dkoutput, selector))
18238+               die(51);
18239+       if (!stralloc_cats(&dkoutput, "; d="))
18240+               die(51);
18241+       tmp = env_get("DKDOMAIN");
18242+       if (from || tmp)
18243+       {
18244+               if (!stralloc_cats(&dkoutput, tmp ? tmp : from))
18245+                       die(51);
18246+       } else
18247+       if (!stralloc_cats(&dkoutput, "unknown"))
18248+               die(51);
18249+       if (!stralloc_cats(&dkoutput, ";\n"))
18250+               die(51);
18251+       if (!stralloc_cats(&dkoutput, "    b="))
18252+               die(51);
18253+       if (!stralloc_cats(&dkoutput, (char *) advice))
18254+               die(51);
18255+       if (dkexcludeheaders || opth)
18256+       {
18257+               if ((i = dk_headers(dk, NULL)) > 0)
18258+               {
18259+                       if (!(tmp = alloc(i)))
18260+                               die(51);
18261+                       if (!dk_headers(dk, tmp))
18262+                               die(51);
18263+                       if (!stralloc_cats(&dkoutput, ";\n    h="))
18264+                               die(51);
18265+                       if (!stralloc_cats(&dkoutput, tmp))
18266+                               die(51);
18267+                       alloc_free(tmp);
18268+               }
18269+       }
18270+       if (!stralloc_cats(&dkoutput, ";\n"))
18271+               die(51);
18272+}
18273+
18274+int
18275+find_header(stralloc *line)
18276+{
18277+       static stralloc headers = { 0 };
18278+       int             n, i, j;
18279+
18280+       for (n = 0; n < line->len; ++n)
18281+       {
18282+               if (line->s[n] == ':')
18283+                       break;
18284+       }
18285+       if (n == line->len)
18286+               return -1;
18287+       if (!headers.len)
18288+       {
18289+               if (!stralloc_copys(&headers, ""))
18290+                       die(51);
18291+               if (dkexcludeheaders)
18292+               {
18293+                       if (!stralloc_cats(&headers, dkexcludeheaders))
18294+                               die(51);
18295+                       if (!stralloc_append(&headers, ":"))
18296+                               die(51);
18297+               }
18298+       }
18299+       if (!headers.len)
18300+               return 0;
18301+       for (i = j = 0; i < headers.len; ++i)
18302+       {
18303+               if (headers.s[i] != ':')
18304+                       continue;
18305+               if (i - j == n && !case_diffb(headers.s + j, n, line->s))
18306+                       return 1;
18307+               j = i + 1;
18308+       }
18309+       return 0;
18310+}
18311+
18312+int
18313+dk_setoptions(char **selector, int *advicelen, int *opth, int *optr, int *optc,
18314+       char **canon, char *signOptions)
18315+{
18316+       char          **argv;
18317+       int             ch, argc;
18318+
18319+       *opth = 0;
18320+       *optr = 0;
18321+       *optc = DK_CANON_NOFWS;
18322+       *canon = "nofws";
18323+       *selector = 0;
18324+       if (!signOptions)
18325+               return (0);
18326+       if (!stralloc_copys(&dkopts, "qmail-dk "))
18327+               die(51);
18328+       if (!stralloc_cats(&dkopts, signOptions))
18329+               die(51);
18330+       if (!stralloc_0(&dkopts))
18331+               die(51);
18332+       if (!(argv = MakeArgs(dkopts.s)))
18333+               die(51);
18334+       for (argc = 0;argv[argc];argc++);
18335+       while ((ch = sgopt(argc, argv, "hrb:c:s:")) != sgoptdone)
18336+       {
18337+               switch (ch)
18338+               {
18339+               case 'h':
18340+                       *opth = 1;
18341+                       break;
18342+               case 'r':
18343+                       *optr = 1;
18344+                       *opth = 1;
18345+                       break;
18346+               case 'b':
18347+                       *advicelen = atoi(optarg);
18348+                       if (*advicelen > ADVICE_BUF);
18349+                               *advicelen = ADVICE_BUF;
18350+                       break;
18351+               case 'c':
18352+                       if (!str_diffn("simple\0", optarg, 7))
18353+                       {
18354+                               *optc = DK_CANON_SIMPLE;
18355+                               *canon = "simple";
18356+                       }
18357+                       break;
18358+               case 's':
18359+                       *selector = optarg;
18360+                       break;
18361+               default:
18362+                       FreeMakeArgs(argv);
18363+                       return (1);
18364+               }
18365+       } /*- while ((ch = sgopt(argc, argv, "hrb:c:s:")) != sgoptdone) */
18366+       FreeMakeArgs(argv);
18367+       return (0);
18368+}
18369+
18370+static char    *binqqargs[2] = { "bin/qmail-queue", 0 };
18371+
18372+int
18373+main(int argc, char *argv[])
18374+{
18375+       int             errfd, pim[2];
18376+       int             wstat, match, opth = 0, optr = 0, optc = DK_CANON_NOFWS,
18377+                                       advicelen = ADVICE_BUF;
18378+       char           *x, *relayclient, *canon = "nofws", *selector = 0;
18379+       stralloc        line = {0}, dkfn = {0};
18380+       unsigned long   pid;
18381+
18382+       sig_blocknone();
18383+       umask(033);
18384+       if (!(x = env_get("ERROR_FD")))
18385+               errfd = CUSTOM_ERR_FD;
18386+       else
18387+               scan_int(x, &errfd);
18388+       substdio_fdbuf(&sserr, write, errfd, errbuf, sizeof(errbuf));
18389+       if (chdir(auto_qmail) == -1)
18390+               die(61);
18391+       dkqueue = env_get("DKQUEUE");
18392+       if (dkqueue && *dkqueue)
18393+               binqqargs[0] = dkqueue;
18394+       dksign = env_get("DKSIGN");
18395+       dkverify = env_get("DKVERIFY");
18396+       relayclient = env_get("RELAYCLIENT");
18397+       if (dkverify && relayclient && env_get("RELAYCLIENT_NODKVERIFY")) {
18398+               execv(*binqqargs, binqqargs);
18399+               die(120, 0);
18400+       }
18401+       if (!dksign && !dkverify && relayclient)
18402+       {
18403+               if (!(dksign = env_get("DKKEY")))
18404+               {
18405+                       if (!stralloc_copys(&dkfn, "domainkeys/%/default"))
18406+                               die(51);
18407+                       if (!stralloc_0(&dkfn))
18408+                               die(51);
18409+                       dksign = dkfn.s;
18410+               }
18411+       }
18412+       if (dksign || dkverify)
18413+       {
18414+               if (!(dklib = dk_init(&st)))
18415+               {
18416+                       maybe_die_dk(st);
18417+                       custom_error("Z", "dk initialization failed (#4.3.0)", 0);
18418+                       _exit(88);
18419+               }
18420+       }
18421+       /*- Initialization */
18422+       if (dksign)
18423+       {
18424+               if (dk_setoptions(&selector, &advicelen, &opth, &optr, &optc, &canon, env_get("DKSIGNOPTIONS")))
18425+               {
18426+                       custom_error("Z", "Invalid DKSIGNOPTIONS (#4.3.0)", 0);
18427+                       _exit(88);
18428+               }
18429+               if (!(dk = dk_sign(dklib, &st, optc)))
18430+               {
18431+                       maybe_die_dk(st);
18432+                       custom_error("Z", "dk_sign failed (#4.3.0)", 0);
18433+                       _exit(88);
18434+               }
18435+               if (optr && dk_setopts(dk, DKOPT_RDUPE) != DK_STAT_OK)
18436+               {
18437+                       custom_error("Z", "DKOPT_RDUPE failed (#4.3.0)", 0);
18438+                       _exit(88);
18439+               }
18440+       } else
18441+       if (dkverify)
18442+       {
18443+               if (!(dk = dk_verify(dklib, &st)))
18444+               {
18445+                       maybe_die_dk(st);
18446+                       custom_error("Z", "dk_verify failed (#4.3.0)", 0);
18447+                       _exit(88);
18448+               }
18449+       }
18450+       mypid = getpid();
18451+       uid = getuid();
18452+       starttime = now();
18453+       datetime_tai(&dt, starttime);
18454+       sig_pipeignore();
18455+       sig_miscignore();
18456+       sig_alarmcatch(sigalrm);
18457+       sig_bugcatch(sigbug);
18458+       alarm(DEATH);
18459+       pidopen();
18460+       if ((readfd = open_read(pidfn)) == -1)
18461+               die(63);
18462+       if (unlink(pidfn) == -1)
18463+               die(63);
18464+       substdio_fdbuf(&ssin, read, 0, inbuf, sizeof(inbuf));
18465+       substdio_fdbuf(&ssout, write, messfd, outbuf, sizeof(outbuf));
18466+       dkexcludeheaders = env_get("DKEXCLUDEHEADERS");
18467+       if (dkexcludeheaders)
18468+       {
18469+               int             hdr_continue, in_header = 1;
18470+
18471+               hdr_continue = 0;
18472+               for (;;)
18473+               {
18474+       
18475+                       if (getln(&ssin, &line, &match, '\n') == -1)
18476+                               die_read();
18477+                       if (!match && line.len == 0)
18478+                               break;
18479+                       if (substdio_put(&ssout, line.s, line.len) == -1)
18480+                               die_write();
18481+                       if (!dksign && !dkverify)
18482+                               continue;
18483+                       if (in_header && !mess822_ok(&line))
18484+                               in_header = 0;
18485+                       if (in_header)
18486+                       {
18487+                               if (line.s[0] == ' ' || line.s[0] == '\t')
18488+                               {
18489+                                       if (hdr_continue)
18490+                                               continue;
18491+                               } else
18492+                               if (find_header(&line) == 1) {
18493+                                       hdr_continue = 1;
18494+                                       continue;
18495+                               } else
18496+                                       hdr_continue = 0;
18497+                       }
18498+                       if (match)
18499+                       {
18500+                               st = dk_message(dk, (unsigned char *) line.s, line.len - 1);
18501+                               maybe_die_dk(st);
18502+                               st = dk_message(dk, (unsigned char *) "\r\n", 2);
18503+                       } else
18504+                               st = dk_message(dk, (unsigned char *) line.s, line.len);
18505+                       maybe_die_dk(st);
18506+               }
18507+       } else
18508+       for (;;)
18509+       {
18510+               register int    n;
18511+               register char  *x;
18512+               int             i;
18513+
18514+               if ((n = substdio_feed(&ssin)) < 0)
18515+                       die_read();
18516+               if (!n)
18517+                       break;
18518+               x = substdio_PEEK(&ssin);
18519+               if (dksign || dkverify)
18520+               {
18521+                       for (i = 0; i < n; i++)
18522+                       {
18523+                               if (x[i] == '\n')
18524+                                       st = dk_message(dk, (unsigned char *) "\r\n", 2);
18525+                               else
18526+                                       st = dk_message(dk, (unsigned char *) x + i, 1);
18527+                               maybe_die_dk(st);
18528+                       }
18529+               }
18530+               if (substdio_put(&ssout, x, n) == -1)
18531+                       die_write();
18532+               substdio_SEEK(&ssin, n);
18533+       }
18534+       if (substdio_flush(&ssout) == -1)
18535+               die_write();
18536+       if (dksign || dkverify)
18537+       {
18538+               st = dk_eom(dk, (void *) 0);
18539+               maybe_die_dk(st);
18540+               if (dksign)
18541+                       write_signature(dk, selector, dksign, advicelen, opth, canon);
18542+               else
18543+               if (dkverify)
18544+               {
18545+                       char           *status = 0, *code = 0;
18546+
18547+                       if (!stralloc_copys(&dkoutput, "DomainKey-Status: "))
18548+                               die(51);
18549+                       switch (st)
18550+                       {
18551+                       case DK_STAT_OK:
18552+                               status = "good        ";
18553+                               break;
18554+                       case DK_STAT_BADSIG:
18555+                               status = "bad         ";
18556+                               code = "X.7.5";
18557+                               break;
18558+                       case DK_STAT_NOSIG:
18559+                               status = "no signature";
18560+                               code = "X.7.5";
18561+                               break;
18562+                       case DK_STAT_NOKEY:
18563+                       case DK_STAT_CANTVRFY:
18564+                               status = "no key      ";
18565+                               code = "X.7.0";
18566+                               break;
18567+                       case DK_STAT_BADKEY:
18568+                               status = "bad key     ";
18569+                               code = "X.7.5";
18570+                               break;
18571+                       case DK_STAT_INTERNAL:
18572+                               status = "bad format  ";
18573+                               code = "X.3.0";
18574+                               break;
18575+                       case DK_STAT_ARGS:
18576+                               status = "bad format  ";
18577+                               code = "X.3.5";
18578+                               break;
18579+                       case DK_STAT_SYNTAX:
18580+                               status = "bad format  ";
18581+                               code = "X.6.0";
18582+                               break;
18583+                       case DK_STAT_NORESOURCE:
18584+                               status = "no resources";
18585+                               code = "X.3.0";
18586+                               break;
18587+                       case DK_STAT_REVOKED:
18588+                               status = "revoked     ";
18589+                               code = "X.7.5";
18590+                               break;
18591+                       case DK_STAT_GRANULARITY:
18592+                               status = "bad sender  ";
18593+                               code = "X.7.5";
18594+                               break;
18595+                       }
18596+                       if (!stralloc_cats(&dkoutput, status))
18597+                               die(51);
18598+                       if (!stralloc_cats(&dkoutput, "\n"))
18599+                               die(51);
18600+                       if (dkverify[str_chr(dkverify, 'A' + st)])
18601+                       {
18602+                               custom_error("D", status, code); /*- return permanent error */
18603+                               die(88);
18604+                       }
18605+                       if (dkverify[str_chr(dkverify, 'a' + st)])
18606+                       {
18607+                               custom_error("Z", status, code); /*- return temporary error */
18608+                               die(88);
18609+                       }
18610+               }
18611+       }
18612+       if (pipe(pim) == -1)
18613+               die(59);
18614+       switch (pid = vfork())
18615+       {
18616+       case -1:
18617+               close(pim[0]);
18618+               close(pim[1]);
18619+               die(58);
18620+       case 0:
18621+               close(pim[1]);
18622+               if (fd_move(0, pim[0]) == -1)
18623+                       die(120);
18624+               execv(*binqqargs, binqqargs);
18625+               die(120);
18626+       }
18627+       close(pim[0]);
18628+       substdio_fdbuf(&ssin, read, readfd, inbuf, sizeof(inbuf));
18629+       substdio_fdbuf(&ssout, write, pim[1], outbuf, sizeof(outbuf));
18630+       if (substdio_bput(&ssout, dkoutput.s, dkoutput.len) == -1) /*- write DK signature */
18631+               die_write();
18632+       switch (substdio_copy(&ssout, &ssin))
18633+       {
18634+       case -2:
18635+               die_read();
18636+       case -3:
18637+               die_write();
18638+       }
18639+       if (substdio_flush(&ssout) == -1)
18640+               die_write();
18641+       close(pim[1]);
18642+       if (wait_pid(&wstat, pid) != pid)
18643+               die(57);
18644+       if (wait_crashed(wstat))
18645+               die(57);
18646+       die(wait_exitcode(wstat));
18647+       /*- Not Reached */
18648+       exit(0);
18649+}
18650+
18651+void
18652+getversion_qmail_dk_c()
18653+{
18654+       static char    *x = "$Id: qmail-dk.c,v 1.37 2013-01-24 22:42:07+05:30 Cprogrammer Exp mbhangui $";
18655+
18656+       x++;
18657+}
18658diff -ruN ../netqmail-1.06-original/qmail-dkim.9 netqmail-1.06/qmail-dkim.9
18659--- ../netqmail-1.06-original/qmail-dkim.9      1970-01-01 01:00:00.000000000 +0100
18660+++ netqmail-1.06/qmail-dkim.9  2020-04-09 19:45:28.421566373 +0200
18661@@ -0,0 +1,322 @@
18662+.TH qmail-dkim 8
18663+.SH NAME
18664+qmail-dkim \- sign/verify using DKIM (SSP/ADSP optionally) and queue a mail message for delivery
18665+.SH SYNOPSIS
18666+.B qmail-dkim
18667+.SH DESCRIPTION
18668+.B qmail-dkim
18669+has the same interface as
18670+.B qmail-queue
18671+except that it inserts an appropriate DKIM header (rfc4871) before it
18672+queues the message.  To invoke
18673+.BR qmail-dkim ,
18674+set QMAILQUEUE to point to qmail-dkim in the environment when
18675+you send or receive email. qmail-dkim will call
18676+.BR qmail-queue .
18677+To invoke an executable other than
18678+.B qmail-queue
18679+set DKIMQUEUE=bin/qmail-dk for example.
18680+
18681+.B qmail-dkim
18682+supports DKIM signing and verification and can optionally use
18683+.B Sender Signing Practice (SSP)
18684+or
18685+.B Author Domain Signing Practice.
18686+It uses the libdkim and OpenSSL libraries.  To sign a message, set the
18687+.B DKIMSIGN
18688+environment variable to the pathname of the private key that will be
18689+used to sign the message. If there is a % character in the environment
18690+variable, it is removed and replaced by the domain name in the From: header.
18691+If, after substituting the %, that file does not exist, the % character will be
18692+removed. If a private key file does not exist and does not have a % character,
18693+the message will be rejected with error 35. The selector (s=) will be taken from
18694+the basename of the file. The private key should be created by
18695+.BR dknewkey(8) .
18696+
18697+In the absense of DKSIGN and DKVERIFY environment variable, qmail-dkim will sign the
18698+message if RELAYCLIENT or AUTHINFO environment variable is set. It will verify the message
18699+if RELAYCLIENT or AUTHINFO environment variable is not set. Even if DKVERIFY is set, you can disable dkim
18700+verification, if RELAYCLIENT or AUTHINFO is set, by setting RELAYCLIENT_NODKVERIFY environment variable.
18701+
18702+You can set various DKIM options in getopt style, by setting the environment variable
18703+.B DKIMSIGNOPTIONS
18704+
18705+ b <standard>         1 - allman, 2 - ietf or 3 - both
18706+ c <canonicalization> r for relaxed [DEFAULT], s - simple,
18707+                      t relaxed/simple, u - simple/relaxed
18708+ l                    include body length tag
18709+ q                    include query method tag;
18710+ t                    include a timestamp tag
18711+ h                    include copied headers
18712+ i <identity>         the identity, if not provided it will not be included
18713+ x <expire_time>      the expire time in seconds since epoch
18714+                      ( DEFAULT = current time + 604800)
18715+                      if set to - then it will not be included
18716+ z <hash>             1 for sha1, 2 for sha256, 3 for both
18717+
18718+.EX
18719+ DKIMSIGNOPTIONS="-b 1 -c r -q"
18720+ sets allman standard, with relaxed canonicalization and include query method tag
18721+.EE
18722+
18723+Apart from setting
18724+.BR DKIMSIGNOPTIONS ,
18725+you can set the identity and the expire time by setting
18726+.B DKIMIDENTITY
18727+and
18728+.B DKIMEXPIRE
18729+respectively.
18730+.B DKIMIDENTITY
18731+takes precedence over -i option specified in
18732+.BR DKIMSIGNOPTIONS.
18733+Similarly,
18734+.B DKIMEXPIRE
18735+takes precedence over -x option specifed in
18736+.BR DKIMSIGNOPTIONS.
18737+.B qmail-dkim
18738+uses the domain found in the Return-Path, Sender, From headers to set the domain tag.
18739+If not it uses the \fBDKIMDOMAIN\fR environment variable. \fBDKIMDOMAIN\fR
18740+can be set to an email address or a domain (without the at sign).
18741+
18742+To verify a message, set the
18743+.B DKIMVERIFY
18744+environment variable to a desired set of letters.  Precisely, if you
18745+want a libdkim return status to generate an error, include that
18746+letter, where A is the first return status (DKIM_SUCCESS), B is the
18747+second (DKIM_FINISHED_BODY), etc.  The letter should be uppercase if you
18748+want a permanent error to be returned, and lowercase if you want a temporary
18749+error to be returned (exit code 88). If you omit the letter, qmail-dkim will
18750+not issue any error inspite of DKIM verification failure. It will return
18751+success and the email will get delivered.
18752+
18753+The complete set of letters with the corresponding return status is given below
18754+
18755+ A - DKIM_SUCCESS                        - Function executed successfully
18756+ B - DKIM_FINISHED_BODY                  - process result: no more message
18757+                                           body is needed
18758+ C - DKIM_PARTIAL_SUCCESS                - verify result: at least one
18759+                                           but not all signatures verified
18760+ D - DKIM_NEUTRAL                        - verify result: no signatures
18761+                                           verified but message is
18762+                                           not suspicious
18763+ E - DKIM_SUCCESS_BUT_EXTRA              - signature result: signature
18764+                                           verified but it did not
18765+                                           include all of the body
18766+ F - DKIM_3PS_SIGNATURE                  - 3rd-party signature
18767+ G - DKIM_FAIL                           - Function failed to execute
18768+ H - DKIM_BAD_SYNTAX                     - signature error: DKIM-Signature
18769+                                           could not parse or has bad
18770+                                           tags/values
18771+ I - DKIM_SIGNATURE_BAD                  - signature error: RSA verify
18772+                                           failed
18773+ J - DKIM_SIGNATURE_BAD_BUT_TESTING      - signature error: RSA verify
18774+                                           failed but testing
18775+ K - DKIM_SIGNATURE_EXPIRED              - signature error: x= is old
18776+ L - DKIM_SELECTOR_INVALID               - signature error: selector doesn't
18777+                                           parse or contains invalid values
18778+ M - DKIM_SELECTOR_GRANULARITY_MISMATCH  - signature error: selector
18779+                                           g= doesn't match i=
18780+ N - DKIM_SELECTOR_KEY_REVOKED           - signature error: selector
18781+                                           p= empty
18782+ O - DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG  - signature error: selector domain
18783+                                           name too long to request
18784+ P - DKIM_SELECTOR_DNS_TEMP_FAILURE      - signature error: temporary dns
18785+                                           failure requesting selector
18786+ Q - DKIM_SELECTOR_DNS_PERM_FAILURE      - signature error: permanent dns
18787+                                           failure requesting selector
18788+ R - DKIM_SELECTOR_PUBLIC_KEY_INVALID    - signature error: selector
18789+                                           p= value invalid or wrong format
18790+ S - DKIM_NO_SIGNATURES                  - no signatures
18791+ T - DKIM_NO_VALID_SIGNATURES            - no valid signatures
18792+ U - DKIM_BODY_HASH_MISMATCH             - sigature verify error: message
18793+                                           body does not hash to bh value
18794+ V - DKIM_SELECTOR_ALGORITHM_MISMATCH    - signature error: selector
18795+                                           h= doesn't match signature a=
18796+ W - DKIM_STAT_INCOMPAT                  - signature error: incompatible v=
18797+ X - DKIM_UNSIGNED_FROM                  - signature error: not all message's
18798+                                           From headers in signature
18799+
18800+For example, if you want to permanently reject messages that have a
18801+signature that is expired, include the letter 'K' in the
18802+.B DKIMVERIFY
18803+environment variable.  A conservative set of letters is
18804+.BR FGHIKLMNOQRTUVWjp .
18805+Reject permanently 3PS, FAILURE, SYNTAX, SIGNATURE_BAD, SIGNATURE_EXPIRED, SELECTOR_INVALID,
18806+GRANULARITY_MISMATCH, SELECTOR_KEY_REVOKED, DOMAIN_NAME_TOO_LONG, SELECTOR_PUBLIC_KEY_INVALID,
18807+NO_VALID_SIGNATURES and BODY_HASH_MISMATCH errors, and temporarily SIGNATURE_BAD_BUT_TESTING and DNS_TEMP_FAILURE .
18808+Add in
18809+.B S
18810+if you want to reject messages that do not have a DKIM signature. You can use the control files
18811+signaturedomains and nosignature domains (See Below) to further fine tune the action to be
18812+taken when a mail arrives with no DKIM signature.  Note that
18813+.B qmail-dkim
18814+always inserts the
18815+.B DKIM-Status
18816+header, so that messages can be
18817+rejected later at delivery time, or in the mail reader. In that case you may set
18818+.B DKIMVERIFY
18819+to an empty string. If you want to check all message's From header in signature set the
18820+\fBUNSIGNED_FROM\fR environment variable to an empty string. If you want to check messages
18821+without signed subject header, set \fBUNSIGNED_SUBJECT\fR environment variable. If you want
18822+to honor body lengh tag (l=), set \fBHONOR_BODYLENGTHTAG\fR environment variable.
18823+
18824+qmail-dkim supports signing practice which can be additonall checked when a signature
18825+verifcation fails -
18826+
18827+.BR "SSP - Sender Signing Practice"
18828+
18829+and
18830+
18831+.BR "ADSP - Author Domain Signing Practice" .
18832+
18833+When a signature fails to verify for a message, you can use SSP/ADSP to determine if the message is suspicious or not.
18834+To verify a message against SSP/ADSP, set the
18835+.B DKIMPRACTICE
18836+environment variable to the desired set of letters allowed for DKIMVERIFY environment variable.
18837+SSP/ADSP should be used only when signature verification fails. SSP/ADSP should be invoked only when libdkim returns the
18838+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
18839+against SSP/ADSP only for DKIM_NO_SIGNATURE and DKIM_NO_VALID_SIGNATURE
18840+set the environment variable
18841+.BR DKIMPRACTICE="ST" .
18842+If you want automatic behaviour, set DKIMPRACTICE to an empty string. In this case ADSP/SSP will be used when return code
18843+matches "FGHIJKLMNPQRSTUVWX".
18844+.B qmail-dkim
18845+uses ADSP as the default signing practice. You can override this by setting the SIGN_PRACTICE to ssp, adsp, local (lowercase).
18846+if you set SIGN_PRACTICE to \fIlocal\fB,
18847+.B qmail-dkim
18848+will check the domain against the control file
18849+.I signaturedomains
18850+(See Below). If the domain is found listed in
18851+.I signaturedomains
18852+.B qmail-dkim
18853+will bypass ADSP/SSP and return DKIM_FAIL if signature fails to verify. Setting SIGN_PRACTICE
18854+to anything else will cause
18855+.B qmail-dkim
18856+to disable Signing Practice.
18857+
18858+If ADSP or SSP is checked,
18859+.B qmail-dkim
18860+will insert the
18861+.B X-DKIM-ADSP
18862+or
18863+.B X-DKIM-SSP
18864+header as given below
18865+
18866+ A - DKIM_SUCCESS             - Message passes ADSP test
18867+ B - DKIM_ADSP_UNKNOWN        - some messages may be signed
18868+ C - DKIM_ADSP_ALL            - All message are signed with author signature
18869+ D - DKIM_ADSP_DISCARDABLE    - messages which fail verification are Discardable
18870+ E - DKIM_ADSP_SCOPE          - domain is out of scope
18871+ F - DKIM_ADSP_TEMPFAIL       - Temporary Error
18872+
18873+ or
18874+
18875+ A - DKIM_SUCCESS            - Message passes ADSP test
18876+ B - DKIM_SSP_UNKNOWN        - some messages may be signed
18877+ C - DKIM_SSP_ALL            - All message are signed with author signature
18878+ D - DKIM_SSP_STRICT         - messages which fail verification are Discardable
18879+ E - DKIM_SSP_SCOPE          - domain is out of scope
18880+ F - DKIM_SSP_TEMPFAIL       - Temporary Error
18881+
18882+You can have a control file
18883+.I signaturedomains
18884+containing a list of domains which you know are sure to sign messages using DKIM. If a message comes
18885+from a domain listed in
18886+.IR signaturedomains ,
18887+and the signature fails verification (any of DKIM failure status),
18888+.B qmail-dkim
18889+will bypass ADSP/SSP checks and return DKIM_FAIL. The name of this control file can be
18890+overriden by the environment variable
18891+.BR SIGNATUREDOMAINS .
18892+
18893+You can have a control file
18894+.I nosignaturedomains
18895+containing a list of domains which you know are sure not to sign messages using DKIM.
18896+If a message comes from a domain listed in
18897+.IR nosignaturedomains ,
18898+and does not have a DKIM-Signature header,
18899+.B qmail-dkim
18900+will bypass ADSP/SSP checks and return DKIM_NEUTRAL. The wildcard entry '*' in this file, will
18901+result in all mails which do not have a signature to pass DKIM test (unless the domain is listed
18902+in the control file
18903+.BR signaturedomains ).
18904+The name of this control file can be overriden by the environment variable
18905+.BR NOSIGNATUREDOMAINS .
18906+
18907+Typically, you would sign messages generated on-host by setting
18908+.B DKIMSIGN
18909+in the environment before running an \fBqmail-smtpd\fR(8) or \fBsendmail(1)\fR / \fBqmail-inject\fR(8).  DKIMSIGN will be carried
18910+through qmail-smtpd or through qmail's sendmail emulation through
18911+.B qmail-inject
18912+to
18913+.BR qmail-dkim .
18914+You would also set it for
18915+.B qmail-smtpd
18916+at the same time
18917+.B RELAYCLIENT
18918+is set, most often in the tcpserver cdb file.  If a host is authorized
18919+to relay, you probably want to sign messages sent by that host.
18920+.B DKIMVERIFY
18921+should be set for all other hosts.
18922+
18923+If neither
18924+.B DKIMSIGN
18925+nor
18926+.B DKIMVERIFY
18927+are set, then
18928+.B DKIMSIGN
18929+will be set to QMAILHOME/control/domainkeys/%/default. The % will be replaced by the domain in the
18930+From: header. If such a file does not exist, then it will be set to
18931+QMAILHOME/control/domainkeys/default. If such a private key exists, it will be used to sign the
18932+domain. You can also set \fBDKIMKEY\fR to chose a key different from
18933+QMAILHOME/control/domainkeys/%/default. \fBDKIMKEY\fR can also have % character that will be
18934+replaced by the domain in the From: header. If the private key does not exist, qmail-dkim
18935+will exit with return code 32.
18936+
18937+By default
18938+.B qmail-dkim
18939+will use all of the headers when signing a message.
18940+
18941+.B qmail-dkim
18942+will ordinarily spawn
18943+.BR qmail-queue ,
18944+but if DKIMQUEUE is set in the environment,
18945+the program that it points to will be executed instead.
18946+
18947+.SH "EXIT CODES"
18948+.B qmail-dkim
18949+returns the same exit codes as qmail-queue with these additions:
18950+.TP 5
18951+.B 32
18952+The private key file does not exist.
18953+.TP 5
18954+.B 57
18955+Trouble waiting for qmail-queue to exit.
18956+.TP 5
18957+.B 58
18958+Unable to vfork.
18959+.TP 5
18960+.B 59
18961+Unable to create a pipe to qmail-queue.
18962+.SH "SEE ALSO"
18963+addresses(5),
18964+envelopes(5),
18965+qmail-header(5),
18966+dknewkey(8),
18967+dkim(8),
18968+dktest(8),
18969+qmail-inject(8),
18970+qmail-qmqpc(8),
18971+qmail-queue(8),
18972+qmail-send(8),
18973+qmail-smtpd(8),
18974+qmail-dk(8),
18975+domain-keys(5)
18976+
18977+.SH "AUTHORS"
18978+
18979+Manvendra Bhangui.
18980+.SH PROBLEMS
18981+Problems with
18982+.B qmail-dkim
18983+should be forwarded to "Manvendra Bhangui" <mbhangui@gmail.com>
18984diff -ruN ../netqmail-1.06-original/qmail-dkim.c netqmail-1.06/qmail-dkim.c
18985--- ../netqmail-1.06-original/qmail-dkim.c      1970-01-01 01:00:00.000000000 +0100
18986+++ netqmail-1.06/qmail-dkim.c  2019-06-19 09:48:44.632641078 +0200
18987@@ -0,0 +1,1417 @@
18988+/*
18989+ * $Log: qmail-dkim.c,v $
18990+ * Revision 1.53  2019-06-14 21:26:37+05:30  Cprogrammer
18991+ * added env variable HONOR_BODYLENGTHTAG to honor body length tag during verification
18992+ *
18993+ * Revision 1.52  2019-02-18 22:18:12+05:30  Cprogrammer
18994+ * allow DKIMVERIFY env variable in place of DKIMPRACTICE when SIGN_PRACTICE="local"
18995+ *
18996+ * Revision 1.51  2019-02-17 11:38:51+05:30  Cprogrammer
18997+ * set original DKIM error for SIGN_PRACTICE=local
18998+ *
18999+ * Revision 1.50  2019-02-15 21:25:04+05:30  Cprogrammer
19000+ * skip nosignaturedomains if domain is present in signaturedomains
19001+ *
19002+ * Revision 1.49  2018-08-08 23:58:01+05:30  Cprogrammer
19003+ * issue success if at lease one one good signature is found
19004+ *
19005+ * Revision 1.48  2017-09-05 12:37:16+05:30  Cprogrammer
19006+ * added missing DKIM_MFREE()
19007+ *
19008+ * Revision 1.47  2016-06-03 09:57:59+05:30  Cprogrammer
19009+ * moved qmail-multi to sbin
19010+ *
19011+ * Revision 1.46  2016-05-17 19:44:58+05:30  Cprogrammer
19012+ * use auto_control, set by conf-control to set control directory
19013+ *
19014+ * Revision 1.45  2016-03-01 18:48:02+05:30  Cprogrammer
19015+ * added env variable UNSIGNED_SUBJECT to verify dkim without subject field
19016+ *
19017+ * Revision 1.44  2015-12-15 16:05:58+05:30  Cprogrammer
19018+ * increased buffer size for long header issue
19019+ *
19020+ * Revision 1.43  2014-01-22 22:45:01+05:30  Cprogrammer
19021+ * treat AUTHINFO environment like RELAYCLIENT environment variable
19022+ *
19023+ * Revision 1.42  2013-10-01 17:11:24+05:30  Cprogrammer
19024+ * fixed QMAILQUEUE recursion
19025+ *
19026+ * Revision 1.41  2013-09-16 22:16:35+05:30  Cprogrammer
19027+ * corrected logic for RELAYCLIENT_NODKIMVERIFY
19028+ *
19029+ * Revision 1.40  2013-09-13 16:34:35+05:30  Cprogrammer
19030+ * turn off verification if RELAYCLIENT, DKIMVERIFY and RELAYCLIENT_NODKIMVERIFY is set
19031+ *
19032+ * Revision 1.39  2013-08-18 15:53:30+05:30  Cprogrammer
19033+ * revert back to default verification mode if both dksign, dkverify are not set
19034+ *
19035+ * Revision 1.38  2013-08-17 15:00:33+05:30  Cprogrammer
19036+ * BUG - corrected location of private key when % sign is removed
19037+ *
19038+ * Revision 1.37  2013-01-24 22:37:22+05:30  Cprogrammer
19039+ * BUG (fix by Piotr Gronek) - DKIM_FREE(results) called before call to ParseTagValues()
19040+ * alternate code for DKIMSIGN selector file name
19041+ *
19042+ * Revision 1.36  2012-08-16 08:01:46+05:30  Cprogrammer
19043+ * do not skip X-Mailer headers
19044+ *
19045+ * Revision 1.35  2011-11-10 14:32:08+05:30  Cprogrammer
19046+ * BUG ssout to be assigned only after pidopen()
19047+ *
19048+ * Revision 1.34  2011-11-07 09:35:59+05:30  Cprogrammer
19049+ * set ssout, sserr, ssin before executing other functions
19050+ *
19051+ * Revision 1.33  2011-07-29 09:29:17+05:30  Cprogrammer
19052+ * fixed key file name
19053+ *
19054+ * Revision 1.32  2011-07-28 19:36:36+05:30  Cprogrammer
19055+ * BUG - fixed opening of private key with absolute path
19056+ *
19057+ * Revision 1.31  2011-07-22 14:40:05+05:30  Cprogrammer
19058+ * fixed checking of private key file
19059+ *
19060+ * Revision 1.30  2011-06-04 14:49:48+05:30  Cprogrammer
19061+ * remove '%' sign from private key if key not found
19062+ *
19063+ * Revision 1.29  2011-06-04 14:22:29+05:30  Cprogrammer
19064+ * added DKIM_UNSIGNED_FROM error code for dkimadspverify
19065+ *
19066+ * Revision 1.28  2011-06-04 14:07:41+05:30  Cprogrammer
19067+ * added DKIM_UNSIGNED_FROM
19068+ *
19069+ * Revision 1.27  2011-02-10 23:39:59+05:30  Cprogrammer
19070+ * use DKIMKEY to override defult control/domainkeys/%/default
19071+ *
19072+ * Revision 1.26  2011-02-06 10:13:50+05:30  Cprogrammer
19073+ * BUG - signature was wrongly freed before being accessed.
19074+ *
19075+ * Revision 1.25  2011-02-05 09:47:47+05:30  Cprogrammer
19076+ * fixed SIGSEGV occuring for messages without body
19077+ *
19078+ * Revision 1.24  2010-11-02 18:45:14+05:30  Cprogrammer
19079+ * Improve DKIM signing/verification speed
19080+ *
19081+ * Revision 1.23  2010-07-21 08:59:57+05:30  Cprogrammer
19082+ * use CONTROLDIR environment variable instead of a hardcoded control directory
19083+ *
19084+ * Revision 1.22  2009-04-22 13:42:51+05:30  Cprogrammer
19085+ * made fd for custom error configurable through env variable ERROR_FD
19086+ *
19087+ * Revision 1.21  2009-04-21 09:05:48+05:30  Cprogrammer
19088+ * return relevant error message for reading private key
19089+ *
19090+ * Revision 1.20  2009-04-21 08:55:41+05:30  Cprogrammer
19091+ * return temporary error for temp failures
19092+ *
19093+ * Revision 1.19  2009-04-20 22:19:01+05:30  Cprogrammer
19094+ * made dkimopts global
19095+ *
19096+ * Revision 1.18  2009-04-16 13:48:32+05:30  Cprogrammer
19097+ * added dkim_setoptions() to set all DKIM options
19098+ *
19099+ * Revision 1.17  2009-04-07 11:36:56+05:30  Cprogrammer
19100+ * use TMPDIR env variable for tmp directory
19101+ *
19102+ * Revision 1.16  2009-04-05 12:52:17+05:30  Cprogrammer
19103+ * added preprocessor warning
19104+ *
19105+ * Revision 1.15  2009-04-04 00:33:44+05:30  Cprogrammer
19106+ * removed dk_strdup()
19107+ *
19108+ * Revision 1.14  2009-03-31 08:21:58+05:30  Cprogrammer
19109+ * set dkimsign when RELAYCLIENT is defined when both dkimsign and dkimverify are undefined
19110+ *
19111+ * Revision 1.13  2009-03-30 22:25:54+05:30  Cprogrammer
19112+ * made DKIM messages friendlier
19113+ *
19114+ * Revision 1.12  2009-03-30 14:47:59+05:30  Cprogrammer
19115+ * added descriptive text for original dkim error
19116+ *
19117+ * Revision 1.11  2009-03-29 19:20:43+05:30  Cprogrammer
19118+ * added nosignaturedomains
19119+ *
19120+ * Revision 1.10  2009-03-28 22:27:02+05:30  Cprogrammer
19121+ * use DKIMSIGN, DKIMVERIFY if RELAYCLIENT is not set
19122+ *
19123+ * Revision 1.9  2009-03-28 22:03:05+05:30  Cprogrammer
19124+ * fixed DKIM return codes
19125+ *
19126+ * Revision 1.8  2009-03-28 13:37:37+05:30  Cprogrammer
19127+ * call DKIMVerifyGetDetails() always
19128+ *
19129+ * Revision 1.7  2009-03-28 11:39:23+05:30  Cprogrammer
19130+ * set automatic setting of dkimsign, dkimverify variables based on RELAYCLIENT
19131+ *
19132+ * Revision 1.6  2009-03-28 11:35:58+05:30  Cprogrammer
19133+ * added ADSP/SSP
19134+ *
19135+ * Revision 1.5  2009-03-22 17:39:38+05:30  Cprogrammer
19136+ * set identity using basename of signature or environment variable DKIMIDENTITY
19137+ *
19138+ * Revision 1.4  2009-03-22 16:58:38+05:30  Cprogrammer
19139+ * fixed bug with verification
19140+ * report custom errors to qmail-queue through custom error interface
19141+ *
19142+ * Revision 1.3  2009-03-21 12:34:38+05:30  Cprogrammer
19143+ * use hasdkim.h for conditional compilation of dkim
19144+ *
19145+ * Revision 1.2  2009-03-20 22:35:57+05:30  Cprogrammer
19146+ * set error to DKIM_NO_SIGNATURE when DKIM-Signature is not present
19147+ *
19148+ * Revision 1.1  2009-03-18 13:54:49+05:30  Cprogrammer
19149+ * Initial revision
19150+ *
19151+ */
19152+#include <unistd.h>
19153+#include <stdlib.h>
19154+#include <ctype.h>
19155+#include <sys/types.h>
19156+#include <sys/stat.h>
19157+#include "sgetopt.h"
19158+#include "substdio.h"
19159+#include "open.h"
19160+#include "qmail.h"
19161+#include "sig.h"
19162+#include "scan.h"
19163+#include "case.h"
19164+#include "fmt.h"
19165+#include "fd.h"
19166+#include "alloc.h"
19167+#include "str.h"
19168+#include "stralloc.h"
19169+#include "datetime.h"
19170+#include "now.h"
19171+#include "wait.h"
19172+#include "auto_qmail.h"
19173+#include "env.h"
19174+#include "control.h"
19175+#ifdef HAVE_CONFIG_H
19176+#include "config.h"
19177+#endif
19178+#include "dkim.h"
19179+
19180+#define DEATH 86400    /*- 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
19181+#define ADDR 1003
19182+#define HAVE_EVP_SHA256
19183+#define strncasecmp(x,y,z) case_diffb((x), (z), (y))
19184+#define strcasecmp(x,y)    case_diffs((x), (y))
19185+
19186+char            inbuf[4096];
19187+char            outbuf[256];
19188+char            errbuf[256];
19189+struct substdio ssin;
19190+struct substdio ssout;
19191+struct substdio sserr;
19192+
19193+datetime_sec    starttime;
19194+struct datetime dt;
19195+unsigned long   mypid;
19196+unsigned long   uid;
19197+char           *pidfn;
19198+int             messfd;
19199+int             readfd;
19200+DKIMContext     ctxt;
19201+
19202+char          **MakeArgs(char *);
19203+void            FreeMakeArgs(char **);
19204+
19205+
19206+void
19207+die(int e, int what)
19208+{
19209+       if (!what)
19210+               _exit(e);
19211+       (what == 1 ? DKIMSignFree : DKIMVerifyFree) (&ctxt);
19212+       _exit(e);
19213+}
19214+
19215+void
19216+die_write()
19217+{
19218+       die(53, 0);
19219+}
19220+
19221+void
19222+die_read()
19223+{
19224+       die(54, 0);
19225+}
19226+
19227+void
19228+sigalrm()
19229+{
19230+       /*- thou shalt not clean up here */
19231+       die(52, 0);
19232+}
19233+
19234+void
19235+sigbug()
19236+{
19237+       die(81, 0);
19238+}
19239+
19240+void
19241+custom_error(char *flag, char *status, char *code)
19242+{
19243+       char           *c;
19244+
19245+       if (substdio_put(&sserr, flag, 1) == -1)
19246+               die_write();
19247+       if (substdio_put(&sserr, "qmail-dkim: ", 12) == -1)
19248+               die_write();
19249+       if (substdio_puts(&sserr, status) == -1)
19250+               die_write();
19251+       if (code) {
19252+               if (substdio_put(&sserr, " (#", 3) == -1)
19253+                       die_write();
19254+               c = (*flag == 'Z') ? "4" : "5";
19255+               if (substdio_put(&sserr, c, 1) == -1)
19256+                       die_write();
19257+               if (substdio_put(&sserr, code + 1, 4) == -1)
19258+                       die_write();
19259+               if (substdio_put(&sserr, ")", 1) == -1)
19260+                       die_write();
19261+       }
19262+       if (substdio_flush(&sserr) == -1)
19263+               die_write();
19264+       return;
19265+}
19266+
19267+int DKIM_CALL
19268+SignThisHeader(const char *szHeader)
19269+{
19270+       if ((!strncasecmp((char *) szHeader, "X-", 2)
19271+                       && strncasecmp((char *) szHeader, "X-Mailer", 8))
19272+               || strncasecmp((char *) szHeader, "Received:", 9) == 0
19273+               || strncasecmp((char *) szHeader, "Authentication-Results:", 23) == 0
19274+               || strncasecmp((char *) szHeader, "Return-Path:", 12) == 0) {
19275+               return 0;
19276+       }
19277+       return 1;
19278+}
19279+
19280+void
19281+maybe_die_dkim(e)
19282+       int             e;
19283+{
19284+       switch (e)
19285+       {
19286+       case DKIM_OUT_OF_MEMORY:
19287+       case DKIM_BUFFER_TOO_SMALL:
19288+               _exit (51);
19289+       case DKIM_INVALID_CONTEXT:
19290+               custom_error("Z", "DKIMContext structure invalid for this operation (#4.3.0)", 0);
19291+               _exit(88);
19292+       case DKIM_NO_SENDER:
19293+               custom_error("Z", "Could not find From: or Sender: header in message (#5.1.7)", 0);
19294+               _exit(88);
19295+       case DKIM_BAD_PRIVATE_KEY:
19296+               custom_error("D", "Could not parse private key (#5.7.5)", 0);
19297+               _exit(88);
19298+       default:
19299+               return;
19300+       }
19301+}
19302+
19303+unsigned int
19304+pidfmt(s, seq)
19305+       char           *s;
19306+       unsigned long   seq;
19307+{
19308+       unsigned int    i;
19309+       unsigned int    len;
19310+       char           *tmpdir;
19311+
19312+       if (!(tmpdir = env_get("TMPDIR")))
19313+               tmpdir = "/tmp";
19314+       len = 0;
19315+       i = fmt_str(s, tmpdir);
19316+       len += i;
19317+       if (s)
19318+               s += i;
19319+       i = fmt_str(s, "/qmail-dkim.");
19320+       len += i;
19321+       if (s)
19322+               s += i;
19323+       i = fmt_ulong(s, mypid);
19324+       len += i;
19325+       if (s)
19326+               s += i;
19327+       i = fmt_str(s, ".");
19328+       len += i;
19329+       if (s)
19330+               s += i;
19331+       i = fmt_ulong(s, starttime);
19332+       len += i;
19333+       if (s)
19334+               s += i;
19335+       i = fmt_str(s, ".");
19336+       len += i;
19337+       if (s)
19338+               s += i;
19339+       i = fmt_ulong(s, seq);
19340+       len += i;
19341+       if (s)
19342+               s += i;
19343+       ++len;
19344+       if (s)
19345+               *s++ = 0;
19346+
19347+       return len;
19348+}
19349+
19350+void
19351+pidopen()
19352+{
19353+       unsigned int    len;
19354+       unsigned long   seq;
19355+
19356+       seq = 1;
19357+       len = pidfmt((char *) 0, seq);
19358+       if (!(pidfn = alloc(len)))
19359+               die(51, 0);
19360+       for (seq = 1; seq < 10; ++seq) {
19361+               if (pidfmt((char *) 0, seq) > len)
19362+                       die(81, 0); /*- paranoia */
19363+               pidfmt(pidfn, seq);
19364+               if ((messfd = open_excl(pidfn)) != -1)
19365+                       return;
19366+       }
19367+       die(63, 0);
19368+}
19369+
19370+char            tmp[FMT_ULONG];
19371+char           *dkimsign = 0;
19372+char           *dkimverify = 0;
19373+char           *dkimadspverify = 0, *dkimpractice =  "FGHIJKLMNPQRSTUVWX";
19374+stralloc        dkimoutput = { 0 };  /*- DKIM-Signature */
19375+stralloc        dksignature = { 0 }; /*- content of private signature */
19376+stralloc        sigdomains = { 0 };  /*- domains which must have signatures */
19377+stralloc        nsigdomains = { 0 }; /*- domains which do not have signatures */
19378+stralloc        dkimopts = { 0 };
19379+char           *dkimqueue = 0;
19380+
19381+static void
19382+write_signature(char *domain, char *keyfn)
19383+{
19384+       char           *pSig;
19385+       int             i;
19386+       static stralloc keyfnfrom = { 0 };
19387+
19388+       if (keyfn[0] != '/') {
19389+               if (!stralloc_copys(&keyfnfrom, "control/"))
19390+                       die(51, 1);
19391+       }
19392+       i = str_chr(keyfn, '%');
19393+       if (keyfn[i]) {
19394+               if (keyfn[0] == '/') {
19395+                       if (!stralloc_copyb(&keyfnfrom, keyfn, i))
19396+                               die(51, 1);
19397+               } else
19398+               if (!stralloc_catb(&keyfnfrom, keyfn, i))
19399+                       die(51, 1);
19400+               if (!stralloc_cats(&keyfnfrom, domain))
19401+                       die(51, 1);
19402+               if (!stralloc_cats(&keyfnfrom, keyfn + i + 1))
19403+                       die(51, 1);
19404+               if (!stralloc_0(&keyfnfrom))
19405+                       die(51, 1);
19406+               if (access(keyfnfrom.s, F_OK)) {
19407+                       /*- since file does not exists remove '%' sign */
19408+                       keyfnfrom.len = 8;
19409+                       if (keyfn[0] == '/') {
19410+                               if (!stralloc_copyb(&keyfnfrom, keyfn, i))
19411+                                       die(51, 1);
19412+                       } else
19413+                       if (!stralloc_catb(&keyfnfrom, keyfn, i))
19414+                               die(51, 1);
19415+                       if ((i - 1) > 0 && keyfn[i - 1] == '/' && keyfn[i + 1] == '/')
19416+                               i++;
19417+                       if (!stralloc_cats(&keyfnfrom, keyfn + i + 1))
19418+                               die(51, 1);
19419+                       if (!stralloc_0(&keyfnfrom))
19420+                               die(51, 1);
19421+               }
19422+       } else {
19423+               if (keyfn[0] == '/') {
19424+                       if (!stralloc_copys(&keyfnfrom, keyfn))
19425+                               die(51, 1);
19426+               } else
19427+               if (!stralloc_cats(&keyfnfrom, keyfn))
19428+                       die(51, 1);
19429+               if (!stralloc_0(&keyfnfrom))
19430+                       die(51, 1);
19431+       }
19432+       switch (control_readnativefile(&dksignature, keyfnfrom.s, 1))
19433+       {
19434+       case 0: /*- missing signature file */
19435+               DKIMSignFree(&ctxt);
19436+               /*
19437+                * You may have multiple domains, but may chose to sign
19438+                * only for few domains which have the key present. Do not
19439+                * treat domains with missing key as an error.
19440+                */
19441+               if (keyfn[i])
19442+                       return;
19443+               die(32, 0);
19444+       case 1:
19445+               break;
19446+       default:
19447+               custom_error("Z", "Unable to read private key. (#4.3.0)", 0);
19448+               DKIMSignFree(&ctxt);
19449+               _exit(88);
19450+       }
19451+       for (i = 0; i < dksignature.len; i++) {
19452+               if (dksignature.s[i] == '\0')
19453+                       dksignature.s[i] = '\n';
19454+       }
19455+       if (!stralloc_0(&dksignature))
19456+               die(51, 1);
19457+       i = DKIMSignGetSig2(&ctxt, dksignature.s, &pSig);
19458+       maybe_die_dkim(i);
19459+       if (pSig) {
19460+               if (!stralloc_catb(&dkimoutput, pSig, str_len(pSig)))
19461+                       die(51, 1);
19462+               if (!stralloc_cats(&dkimoutput, "\n"))
19463+                       die(51, 1);
19464+       }
19465+       DKIMSignFree(&ctxt);
19466+}
19467+
19468+#include <openssl/evp.h>
19469+#define DKIM_MALLOC(n)     OPENSSL_malloc(n)
19470+#define DKIM_MFREE(s)      OPENSSL_free(s); s = NULL;
19471+char           *dns_text(char *);
19472+
19473+int
19474+ParseTagValues(char *list, char *letters[], char *values[])
19475+{
19476+       char           *tmp, *ptr, *key;
19477+       int             i;
19478+
19479+       /*- start with all args unset */
19480+       for (i = 0; letters[i]; i++)
19481+               values[i] = 0;
19482+       key = 0;
19483+       for(ptr = list;*ptr;) {
19484+               if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) /*- FWS */
19485+                       *ptr++ = 0;
19486+               if (!key)
19487+                       key = ptr;
19488+               if (*ptr == '=') {
19489+                       *ptr = 0;
19490+                       for (i = 0;letters[i];i++) {
19491+                               if (!str_diff(letters[i], key)) {
19492+                                       ptr++;
19493+                                       for (;*ptr;) {
19494+                                               if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) {
19495+                                                       ptr++;
19496+                                                       continue;
19497+                                               }
19498+                                               break;
19499+                                       }
19500+                                       values[i] = ptr;
19501+                                       for(;*ptr && *ptr != ';';ptr++);
19502+                                       tmp = ptr;
19503+                                       if (*ptr)
19504+                                               *ptr++ = 0;
19505+                                       for(;tmp != values[i];tmp--) { /*- RFC 4871 3.2 */
19506+                                               if ((*tmp == ' ') || (*tmp == '\t') || (*tmp == '\r') || (*tmp == '\n')) {
19507+                                                       *tmp = 0;
19508+                                                       continue;
19509+                                               }
19510+                                               break;
19511+                                       }
19512+                                       key = 0;
19513+                                       break;
19514+                               }
19515+                       }
19516+               } else
19517+                       ptr++;
19518+       }
19519+       return (0);
19520+}
19521+
19522+int
19523+checkSSP(char *domain, int *bTesting)
19524+{
19525+       char           *query, *results;
19526+       char           *tags[] = { "dkim", "t", 0};
19527+       char           *values[2];
19528+       int             bIsParentSSP = 0, iSSP = DKIM_SSP_UNKNOWN;
19529+
19530+       *bTesting = 0;
19531+       if (!(query = DKIM_MALLOC(str_len("_ssp._domainkey.") + str_len(domain) + 1)))
19532+               die(51, 0);
19533+       sprintf(query, "_ssp._domainkey.%s", domain);
19534+       results = dns_text(query);
19535+       DKIM_MFREE(query);
19536+       if (!str_diff(results, "e=temp;")) {
19537+               DKIM_MFREE(results);
19538+               return DKIM_SSP_TEMPFAIL;
19539+       } else
19540+       if (!str_diff(results, "e=perm;")) {
19541+               results = dns_text(domain);
19542+               if (!str_diff(results, "e=temp;")) {
19543+                       DKIM_MFREE(results);
19544+                       return DKIM_SSP_TEMPFAIL;
19545+               } else
19546+               if (!str_diff(results, "e=perm;")) {
19547+                       DKIM_MFREE(results);
19548+                       return DKIM_SSP_SCOPE;
19549+               }
19550+               bIsParentSSP = 1;
19551+       }
19552+       /*
19553+        * PG.1 2013-01-03
19554+        * Bug fix by Piotr Gronek, Faculy of Physics & Applied Computer Science, Poland 2013-01-03
19555+        * Deallocating storage for 'results' here is premature - moved beyond last reference to it.
19556+        *
19557+        */
19558+       if (!ParseTagValues(results, tags, values)) {
19559+               DKIM_MFREE(results);
19560+               return DKIM_SSP_UNKNOWN;
19561+       }
19562+       DKIM_MFREE(results);
19563+       if (values[0] != NULL) {
19564+               if (strcasecmp(values[0], "all") == 0)
19565+                       iSSP = DKIM_SSP_ALL;
19566+               else
19567+               if (strcasecmp(values[0], "strict") == 0)
19568+                       iSSP = DKIM_SSP_STRICT;
19569+       }
19570+       // flags
19571+       if (values[1] != NULL) {
19572+               char           *s, *p;
19573+               for (p = values[1], s = values[1]; *p; p++) {
19574+                       if (*p == '|')
19575+                               *p = 0;
19576+                       else
19577+                               continue;
19578+                       if (!str_diff(s, "y"))
19579+                               *bTesting = 1;
19580+                       else
19581+                       if (!str_diff(s, "s")) {
19582+                               if (bIsParentSSP) {
19583+                                       /*
19584+                                        * this is a parent's SSP record that should not apply to subdomains
19585+                                        * the message is non-suspicious
19586+                                        */
19587+                                       *bTesting = 0;
19588+                                       return (DKIM_SSP_UNKNOWN);
19589+                               }
19590+                       }
19591+                       s = p + 1;
19592+               }
19593+       }
19594+       return iSSP;
19595+}
19596+
19597+int
19598+checkADSP(char *domain)
19599+{
19600+       char           *query, *results;
19601+       char           *tags[] = { "dkim", 0};
19602+       char           *values[1];
19603+
19604+       results = dns_text(domain);
19605+       if (!str_diff(results, "e=perm;")) {
19606+               DKIM_MFREE(results);
19607+               return DKIM_ADSP_SCOPE;
19608+       } else
19609+       if (!str_diff(results, "e=temp;")) {
19610+               DKIM_MFREE(results);
19611+               return DKIM_ADSP_TEMPFAIL;
19612+       }
19613+       if (!(query = DKIM_MALLOC(str_len("_adsp._domainkey.") + str_len(domain) + 1))) {
19614+               DKIM_MFREE(results);
19615+               die(51, 0);
19616+       }
19617+       sprintf(query, "_adsp._domainkey.%s", domain);
19618+       results = dns_text(query);
19619+       DKIM_MFREE(query);
19620+       if (!str_diff(results, "e=perm;")) {
19621+               DKIM_MFREE(results);
19622+               return DKIM_ADSP_SCOPE;
19623+       } else
19624+       if (!str_diff(results, "e=temp;")) {
19625+               DKIM_MFREE(results);
19626+               return DKIM_ADSP_TEMPFAIL;
19627+       }
19628+       /*
19629+        * PG.1 2013-01-03
19630+        * Bug fix by Piotr Gronek, Faculy of Physics & Applied Computer Science, Poland 2013-01-03
19631+        *
19632+        * Deallocating storage for 'results' here is premature - moved beyond last reference to it.
19633+        *
19634+        */
19635+       if (!ParseTagValues(results, tags, values)) {
19636+               DKIM_MFREE(results);
19637+               return DKIM_SSP_UNKNOWN;
19638+       }
19639+       DKIM_MFREE(results);
19640+       if (values[0] != NULL) {
19641+               if (strcasecmp(values[0], "all") == 0)
19642+                       return (DKIM_ADSP_ALL);
19643+               else
19644+               if (strcasecmp(values[0], "discardable") == 0)
19645+                       return (DKIM_ADSP_DISCARDABLE);
19646+       }
19647+       return DKIM_ADSP_UNKNOWN; /*- No ADSP Record */
19648+}
19649+
19650+void
19651+dkimverify_exit(int dkimRet, char *status, char *code)
19652+{
19653+       if (dkimRet < 0) {
19654+               if (dkimverify[str_chr(dkimverify, 'F' - dkimRet)]) {
19655+                       custom_error("D", status, code);
19656+                       die(88, 0);
19657+               }
19658+               if (dkimverify[str_chr(dkimverify, 'f' - dkimRet)]) {
19659+                       custom_error("Z", status, code);
19660+                       die(88, 0);
19661+               }
19662+       } else {
19663+               if (dkimverify[str_chr(dkimverify, 'A' + dkimRet)]) {
19664+                       custom_error("D", status, code);
19665+                       die(88, 0);
19666+               }
19667+               if (dkimverify[str_chr(dkimverify, 'a' + dkimRet)]) {
19668+                       custom_error("Z", status, code);
19669+                       die(88, 0);
19670+               }
19671+       }
19672+}
19673+
19674+void
19675+writeHeaderNexit(int ret, int origRet, int resDKIMSSP, int resDKIMADSP, int useSSP, int useADSP)
19676+{
19677+       char           *dkimStatus = 0, *sspStatus = 0, *adspStatus = 0, *code = 0, *orig = 0;
19678+       char            strnum[FMT_ULONG];
19679+
19680+       switch (ret)
19681+       {
19682+       case DKIM_SUCCESS:                      /*- 0 */ /*- A */
19683+               dkimStatus = "good        ";
19684+               code = "X.7.0";
19685+               break;
19686+       case DKIM_FINISHED_BODY:        /*- 1 process result: no more message body is needed */
19687+               dkimStatus = "process result: no more message body is needed";
19688+               code = "X.7.0";
19689+               break;
19690+       case DKIM_PARTIAL_SUCCESS:      /*- 2 verify result: at least one but not all signatures verified */
19691+               dkimStatus = "verify result: at least none but not all signatures verified";
19692+               code = "X.7.0";
19693+               break;
19694+       case DKIM_NEUTRAL:                      /*- 3 verify result: no signatures verified but message is not suspicious */
19695+               dkimStatus = "verify result: no signatures verified but message is not suspicious";
19696+               code = "X.7.0";
19697+               break;
19698+       case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */
19699+               dkimStatus = "signature result: signature verified but it did not include all of the body";
19700+               code = "X.7.0";
19701+               break;
19702+       case DKIM_FAIL:                         /*- -1 */ /*- F */
19703+               dkimStatus = "DKIM Signature verification failed";
19704+               code = "X.7.0";
19705+               break;
19706+       case DKIM_BAD_SYNTAX:           /*- -2 */ /*- G */
19707+               dkimStatus = "signature error: DKIM-Signature could not parse or has bad tags/values";
19708+               code = "X.7.5";
19709+               break;
19710+       case DKIM_SIGNATURE_BAD:        /*- -3 */
19711+               dkimStatus = "signature error: RSA verify failed";
19712+               code = "X.7.5";
19713+               break;
19714+       case DKIM_SIGNATURE_BAD_BUT_TESTING:
19715+               dkimStatus = "signature error: RSA verify failed but testing";
19716+               code = "X.7.5";
19717+               break;
19718+       case DKIM_SIGNATURE_EXPIRED:
19719+               dkimStatus = "signature error: x= is old";
19720+               code = "X.7.5";
19721+               break;
19722+       case DKIM_SELECTOR_INVALID:
19723+               dkimStatus = "signature error: selector doesn't parse or contains invalid values";
19724+               code = "X.7.5";
19725+               break;
19726+       case DKIM_SELECTOR_GRANULARITY_MISMATCH:
19727+               dkimStatus = "signature error: selector g= doesn't match i=";
19728+               code = "X.7.5";
19729+               break;
19730+       case DKIM_SELECTOR_KEY_REVOKED:
19731+               dkimStatus = "signature error: selector p= empty";
19732+               code = "X.7.5";
19733+               break;
19734+       case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG:
19735+               dkimStatus = "signature error: selector domain name too long to request";
19736+               code = "X.7.0";
19737+               break;
19738+       case DKIM_SELECTOR_DNS_TEMP_FAILURE:
19739+               dkimStatus = "signature error: temporary dns failure requesting selector";
19740+               code = "X.7.0";
19741+               break;
19742+       case DKIM_SELECTOR_DNS_PERM_FAILURE:
19743+               dkimStatus = "signature error: permanent dns failure requesting selector";
19744+               code = "X.7.0";
19745+               break;
19746+       case DKIM_SELECTOR_PUBLIC_KEY_INVALID:
19747+               dkimStatus = "signature error: selector p= value invalid or wrong format";
19748+               code = "X.7.5";
19749+               break;
19750+       case DKIM_NO_SIGNATURES:
19751+               dkimStatus = "no signatures";
19752+               code = "X.7.5";
19753+               break;
19754+       case DKIM_NO_VALID_SIGNATURES:
19755+               dkimStatus = "no valid signatures";
19756+               code = "X.7.5";
19757+               break;
19758+       case DKIM_BODY_HASH_MISMATCH:
19759+               dkimStatus = "signature verify error: message body does not hash to bh value";
19760+               code = "X.7.7";
19761+               break;
19762+       case DKIM_SELECTOR_ALGORITHM_MISMATCH:
19763+               dkimStatus = "signature error: selector h= doesn't match signature a=";
19764+               code = "X.7.7";
19765+               break;
19766+       case DKIM_STAT_INCOMPAT:
19767+               dkimStatus = "signature error: incompatible v=";
19768+               code = "X.7.6";
19769+               break;
19770+       case DKIM_UNSIGNED_FROM:
19771+               dkimStatus = "signature error: not all message's From headers in signature";
19772+               code = "X.7.7";
19773+               break;
19774+       default:
19775+               dkimStatus = "error";
19776+               code = "X.3.0";
19777+               break;
19778+       }
19779+       if (useSSP && resDKIMSSP != -1) {
19780+               switch(resDKIMSSP)
19781+               {
19782+                       case DKIM_SSP_ALL:
19783+                               sspStatus = (char *) "all;";
19784+                               break;
19785+                       case DKIM_SSP_STRICT:
19786+                               sspStatus = (char *) "strict;";
19787+                               break;
19788+                       case DKIM_SSP_SCOPE:
19789+                               sspStatus = (char *) "out of scope;";
19790+                               break;
19791+                       case DKIM_SSP_TEMPFAIL:
19792+                               sspStatus = (char *) "temporary failure;";
19793+                               break;
19794+                       case DKIM_SSP_UNKNOWN:
19795+                       default:
19796+                               sspStatus = (char *) "unknown;";
19797+                               break;
19798+               }
19799+       }
19800+       if (useADSP && resDKIMADSP != -1) {
19801+               switch(resDKIMADSP)
19802+               {
19803+                       case DKIM_ADSP_ALL:
19804+                               adspStatus = (char *) "all;";
19805+                               break;
19806+                       case DKIM_ADSP_DISCARDABLE:
19807+                               adspStatus = (char *) "discardable;";
19808+                               break;
19809+                       case DKIM_ADSP_SCOPE:
19810+                               adspStatus = (char *) "out of scope;";
19811+                               break;
19812+                       case DKIM_ADSP_TEMPFAIL:
19813+                               adspStatus = (char *) "temporary failure;";
19814+                               break;
19815+                       case DKIM_ADSP_UNKNOWN:
19816+                       default:
19817+                               adspStatus = (char *) "unknown ;";
19818+                               break;
19819+               }
19820+       }
19821+       if (!stralloc_copys(&dkimoutput, "DKIM-Status: "))
19822+               die(51, 0);
19823+       if (!stralloc_cats(&dkimoutput, dkimStatus))
19824+               die(51, 0);
19825+       if (origRet != DKIM_MAX_ERROR && ret != origRet) {
19826+               if (!stralloc_cats(&dkimoutput, "\n\t(old="))
19827+                       die(51, 0);
19828+               switch (origRet)
19829+               {
19830+               case DKIM_SUCCESS:                      /*- 0 */ /*- A */
19831+                       orig = "SUCCESS";
19832+                       break;
19833+               case DKIM_FINISHED_BODY:        /*- 1 process result: no more message body is needed */
19834+                       orig = "FINISHED BODY";
19835+                       break;
19836+               case DKIM_PARTIAL_SUCCESS:      /*- 2 verify result: at least one but not all signatures verified */
19837+                       orig = "PARTIAL SUCCESS";
19838+                       break;
19839+               case DKIM_NEUTRAL:                      /*- 3 verify result: no signatures verified but message is not suspicious */
19840+                       orig = "NEUTRAL";
19841+                       break;
19842+               case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */
19843+                       orig = "SUCCESS(BUT EXTRA)";
19844+                       break;
19845+               case DKIM_FAIL:                         /*- -1 */ /*- F */
19846+                       orig = "FAIL";
19847+                       break;
19848+               case DKIM_BAD_SYNTAX:           /*- -2 */ /*- G */
19849+                       orig = "BAD SYNTAX";
19850+                       break;
19851+               case DKIM_SIGNATURE_BAD:        /*- -3 */
19852+                       orig = "SIGNATURE BAD";
19853+                       break;
19854+               case DKIM_SIGNATURE_BAD_BUT_TESTING:
19855+                       orig = "SIGNATURE BAD (TESTING)";
19856+                       break;
19857+               case DKIM_SIGNATURE_EXPIRED:
19858+                       orig = "SIGNATURE EXPIRED";
19859+                       break;
19860+               case DKIM_SELECTOR_INVALID:
19861+                       orig = "SELECTOR INVALID";
19862+                       break;
19863+               case DKIM_SELECTOR_GRANULARITY_MISMATCH:
19864+                       orig = "SELECTOR GRANULARITY MISMATCH";
19865+                       break;
19866+               case DKIM_SELECTOR_KEY_REVOKED:
19867+                       orig = "SELECTOR KEY REVOKED";
19868+                       break;
19869+               case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG:
19870+                       orig = "DOMAIN NAME TOO LONG";
19871+                       break;
19872+               case DKIM_SELECTOR_DNS_TEMP_FAILURE:
19873+                       orig = "DNS TEMP FAILURE";
19874+                       break;
19875+               case DKIM_SELECTOR_DNS_PERM_FAILURE:
19876+                       orig = "DNS PERM FAILURE";
19877+                       break;
19878+               case DKIM_SELECTOR_PUBLIC_KEY_INVALID:
19879+                       orig = "PUBLIC KEY INVALID";
19880+                       break;
19881+               case DKIM_NO_SIGNATURES:
19882+                       orig = "NO SIGNATURES";
19883+                       break;
19884+               case DKIM_NO_VALID_SIGNATURES:
19885+                       orig = "NO VALID SIGNATURES";
19886+                       break;
19887+               case DKIM_BODY_HASH_MISMATCH:
19888+                       orig = "BODY HASH MISMATCH";
19889+                       break;
19890+               case DKIM_SELECTOR_ALGORITHM_MISMATCH:
19891+                       orig = "ALGORITHM MISMATCH";
19892+                       break;
19893+               case DKIM_STAT_INCOMPAT:
19894+                       orig = "STAT INCOMPAT";
19895+                       break;
19896+               case DKIM_UNSIGNED_FROM:
19897+                       orig = "UNSIGNED FROM";
19898+                       break;
19899+               default:
19900+                       orig = "Unkown error";
19901+                       break;
19902+               }
19903+               if (!stralloc_cats(&dkimoutput, orig))
19904+                       die(51, 0);
19905+               if (!stralloc_cats(&dkimoutput, ":"))
19906+                       die(51, 0);
19907+               if (origRet < 0) {
19908+                       if (!stralloc_cats(&dkimoutput, "-"))
19909+                               die(51, 0);
19910+                       strnum[fmt_ulong(strnum, 0 - origRet)] = 0;
19911+               } else
19912+                       strnum[fmt_ulong(strnum, origRet)] = 0;
19913+               if (!stralloc_cats(&dkimoutput, strnum))
19914+                       die(51, 0);
19915+               if (!stralloc_cats(&dkimoutput, ")"))
19916+                       die(51, 0);
19917+       }
19918+       if (!stralloc_cats(&dkimoutput, "\n"))
19919+               die(51, 0);
19920+       if (useSSP && sspStatus) {
19921+               if (!stralloc_cats(&dkimoutput, "X-DKIM-SSP: "))
19922+                       die(51, 0);
19923+               if (!stralloc_cats(&dkimoutput, sspStatus))
19924+                       die(51, 0);
19925+               if (!stralloc_cats(&dkimoutput, "\n"))
19926+                       die(51, 0);
19927+       }
19928+       if (useADSP && adspStatus) {
19929+               if (!stralloc_cats(&dkimoutput, "X-DKIM-ADSP: "))
19930+                       die(51, 0);
19931+               if (!stralloc_cats(&dkimoutput, adspStatus))
19932+                       die(51, 0);
19933+               if (!stralloc_cats(&dkimoutput, "\n"))
19934+                       die(51, 0);
19935+       }
19936+       dkimverify_exit(ret, dkimStatus, code);
19937+       return;
19938+}
19939+
19940+int
19941+checkPractice(int dkimRet, int useADSP, int useSSP)
19942+{
19943+       char           *ptr;
19944+
19945+       if (!(ptr = env_get("DKIMPRACTICE"))) {
19946+               /*- if SIGN_PRACTICE="local" then you can use DKIMVERIFY env variable too */
19947+               if (!useADSP && !useSSP)
19948+                       dkimpractice = dkimverify; /*- DKIMVERIFY env variable */
19949+               else
19950+                       return (0);
19951+       } else
19952+               dkimpractice = ptr;
19953+       if (!*dkimpractice) {
19954+               if (dkimRet < 0 || dkimRet == DKIM_3PS_SIGNATURE)
19955+                       return (1);
19956+               return (0);
19957+       }
19958+       if (dkimRet < 0) {
19959+               if (dkimpractice[str_chr(dkimpractice, 'F' - dkimRet)])
19960+                       return (1);
19961+               if (dkimpractice[str_chr(dkimpractice, 'f' - dkimRet)])
19962+                       return (1);
19963+       } else {
19964+               if (dkimpractice[str_chr(dkimpractice, 'A' + dkimRet)])
19965+                       return (1);
19966+               if (dkimpractice[str_chr(dkimpractice, 'a' + dkimRet)])
19967+                       return (1);
19968+       }
19969+       return (0);
19970+}
19971+
19972+static char    *binqqargs[2] = { "bin/qmail-queue", 0 };
19973+
19974+int
19975+dkim_setoptions(DKIMSignOptions *opts, char *signOptions)
19976+{
19977+       int             ch, argc;
19978+       char          **argv;
19979+
19980+       opts->nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
19981+       opts->nCanon = DKIM_SIGN_RELAXED;                                       /*- c */
19982+       opts->nIncludeBodyLengthTag = 0;                                        /*- l */
19983+       opts->nIncludeQueryMethod = 0;                                          /*- q */
19984+       opts->nIncludeTimeStamp = 0;                                            /*- t */
19985+       opts->nIncludeCopiedHeaders = 0;                                        /*- h */
19986+       opts->szIdentity[0] = '\0';
19987+       opts->expireTime = starttime + 604800;  // expires in 1 week
19988+       opts->nHash = DKIM_HASH_SHA1;
19989+       str_copy(opts->szRequiredHeaders, "NonExistent");
19990+       if (!signOptions)
19991+               return (0);
19992+       if (!stralloc_copys(&dkimopts, "dkim "))
19993+               die(51, 0);
19994+       if (!stralloc_cats(&dkimopts, signOptions))
19995+               die(51, 0);
19996+       if (!stralloc_0(&dkimopts))
19997+               die(51, 0);
19998+       if (!(argv = MakeArgs(dkimopts.s)))
19999+               die(51, 0);
20000+       for (argc = 0;argv[argc];argc++);
20001+#ifdef HAVE_EVP_SHA256
20002+       while ((ch = sgopt(argc, argv, "b:c:li:qthx:z:")) != sgoptdone) {
20003+#else
20004+       while ((ch = sgopt(argc, argv, "b:c:li:qthx:")) != sgoptdone) {
20005+#endif
20006+               switch (ch)
20007+               {
20008+               case 'b':
20009+                       switch (*optarg)
20010+                       {
20011+                       case '1':
20012+                               opts->nIncludeBodyHash = DKIM_BODYHASH_ALLMAN_1;
20013+                               break;
20014+                       case '2':
20015+                               opts->nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
20016+                               break;
20017+                       case '3':
20018+                               opts->nIncludeBodyHash = DKIM_BODYHASH_BOTH;
20019+                               break;
20020+                       default:
20021+                               FreeMakeArgs(argv);
20022+                               return (1);
20023+                       }
20024+                       break;
20025+               case 'c':
20026+                       switch (*optarg)
20027+                       {
20028+                       case 'r':
20029+                               opts->nCanon = DKIM_SIGN_RELAXED;
20030+                               break;
20031+                       case 's':
20032+                               opts->nCanon = DKIM_SIGN_SIMPLE;
20033+                               break;
20034+                       case 't':
20035+                               opts->nCanon = DKIM_SIGN_RELAXED_SIMPLE;
20036+                               break;
20037+                       case 'u':
20038+                               opts->nCanon = DKIM_SIGN_SIMPLE_RELAXED;
20039+                               break;
20040+                       default:
20041+                               FreeMakeArgs(argv);
20042+                               return (1);
20043+                       }
20044+                       break;
20045+               case 'l': /*- body length tag */
20046+                       opts->nIncludeBodyLengthTag = 1;
20047+                       break;
20048+               case 'q': /*- query method tag */
20049+                       opts->nIncludeQueryMethod = 1;
20050+                       break;
20051+               case 't': /*- timestamp tag */
20052+                       opts->nIncludeTimeStamp = 1;
20053+                       break;
20054+               case 'h':
20055+                       opts->nIncludeCopiedHeaders = 1;
20056+                       break;
20057+               case 'i':       /*- identity */
20058+                       if (*optarg == '-') /* do not use i= tag */
20059+                               opts->szIdentity[0] = '\0';
20060+                       else
20061+                               str_copyb(opts->szIdentity, optarg, sizeof(opts->szIdentity) - 1);
20062+                       break;
20063+               case 'x': /*- expire time */
20064+                       if (*optarg == '-')
20065+                               opts->expireTime = 0;
20066+                       else
20067+                               opts->expireTime = starttime + atoi(optarg);
20068+                       break;
20069+#ifdef HAVE_EVP_SHA256
20070+               case 'z': /*- sign w/ sha1, sha256 or both */
20071+                       switch (*optarg)
20072+                       {
20073+                       case '1':
20074+                               opts->nHash = DKIM_HASH_SHA1;
20075+                               break;
20076+                       case '2':
20077+                               opts->nHash = DKIM_HASH_SHA256;
20078+                               break;
20079+                       case '3':
20080+                               opts->nHash = DKIM_HASH_SHA1_AND_256;
20081+                               break;
20082+                       default:
20083+                               FreeMakeArgs(argv);
20084+                               return (1);
20085+                       }
20086+                       break;
20087+#endif
20088+               default:
20089+                       FreeMakeArgs(argv);
20090+                       return (1);
20091+               } /*- switch (ch) */
20092+       } /*- while (1) */
20093+       FreeMakeArgs(argv);
20094+       return (0);
20095+}
20096+
20097+int
20098+main(int argc, char *argv[])
20099+{
20100+       int             errfd, pim[2];
20101+       int             wstat;
20102+       int             resDKIMSSP = -1, resDKIMADSP = -1, useSSP = 0, useADSP = 0, accept3ps = 0;
20103+       int             sCount = 0, sSize = 0;
20104+       int             ret = 0, origRet = DKIM_MAX_ERROR, i, nSigCount = 0, len, token_len;
20105+       unsigned long   pid;
20106+       char           *selector, *p;
20107+       stralloc        dkimfn = {0};
20108+       DKIMSignOptions opts = { 0 };
20109+       DKIMVerifyDetails *pDetails;
20110+       DKIMVerifyOptions vopts = { 0 };
20111+
20112+       starttime = now();
20113+       sig_blocknone();
20114+       umask(033);
20115+       if (!(p = env_get("ERROR_FD")))
20116+               errfd = CUSTOM_ERR_FD;
20117+       else
20118+               scan_int(p, &errfd);
20119+       substdio_fdbuf(&sserr, write, errfd, errbuf, sizeof(errbuf));
20120+       if (chdir(auto_qmail) == -1)
20121+               die(61, 0);
20122+       dkimqueue = env_get("DKIMQUEUE");
20123+       if (dkimqueue && *dkimqueue)
20124+               binqqargs[0] = dkimqueue;
20125+       dkimsign = env_get("DKIMSIGN");
20126+       dkimverify = env_get("DKIMVERIFY");
20127+       p = (env_get("RELAYCLIENT") || env_get("AUTHINFO")) ? "" : 0;
20128+       if (dkimverify && p && env_get("RELAYCLIENT_NODKIMVERIFY")) {
20129+               execv(*binqqargs, binqqargs);
20130+               die(120, 0);
20131+       }
20132+       if (!dkimsign && !dkimverify && p) {
20133+               if (!(dkimsign = env_get("DKIMKEY"))) {
20134+                       if (!stralloc_copys(&dkimfn, "domainkeys/%/default"))
20135+                               die(51, 0);
20136+                       if (!stralloc_0(&dkimfn))
20137+                               die(51, 0);
20138+                       dkimsign = dkimfn.s;
20139+               }
20140+       }
20141+       if (dkimsign) {
20142+               /* selector */
20143+               p = dkimsign;
20144+               selector = p;
20145+               while (*p) {
20146+                       if (*p == '/' && *(p + 1))
20147+                               selector = p + 1;
20148+                       p++;
20149+               }
20150+               str_copyb(opts.szSelector, selector, sizeof(opts.szSelector) - 1);
20151+
20152+               if (dkim_setoptions(&opts, env_get("DKIMSIGNOPTIONS"))) {
20153+                       custom_error("Z", "Invalid DKIMSIGNOPTIONS (#4.3.0)", 0);
20154+                       _exit(88);
20155+               }
20156+               p = env_get("DKIMIDENTITY");
20157+               if (p && *p)
20158+                       str_copyb(opts.szIdentity, p, sizeof(opts.szIdentity) - 1);
20159+               p = env_get("DKIMEXPIRE");
20160+               if (p && *p)
20161+                       opts.expireTime = starttime + atol(p);
20162+               else
20163+               if (p)
20164+                       opts.expireTime = 0;
20165+               opts.pfnHeaderCallback = SignThisHeader;
20166+               if (DKIMSignInit(&ctxt, &opts) != DKIM_SUCCESS) { /*- failed to initialize signature */
20167+                       custom_error("Z", "dkim initialization failed (#4.3.0)", 0);
20168+                       _exit(88);
20169+               }
20170+       } else {
20171+               char           *x;
20172+
20173+               if (!dkimverify)
20174+                       dkimverify = "";
20175+               if (!(x = env_get("SIGN_PRACTICE")))
20176+                       x = "adsp";
20177+               if (!str_diffn("adsp", x, 4)) {
20178+                       useADSP = 1;
20179+                       accept3ps = 1;
20180+               } else
20181+               if (!str_diffn("ssp", x, 3)) {
20182+                       useSSP = 1;
20183+                       accept3ps = 1;
20184+               } else
20185+               if (!str_diffn("local", x, 5)) {
20186+                       useSSP = 0;
20187+                       useADSP = 0;
20188+                       accept3ps = 1;
20189+               }
20190+               if (useADSP)
20191+                       vopts.nCheckPractices = useADSP;
20192+               else
20193+               if (useSSP)
20194+                       vopts.nCheckPractices = useSSP;
20195+               else
20196+                       vopts.nCheckPractices = 0;
20197+               vopts.nAccept3ps = accept3ps;
20198+               vopts.pfnSelectorCallback = NULL;       /*- SelectorCallback; */
20199+               if (env_get("UNSIGNED_FROM"))
20200+                       vopts.nAllowUnsignedFromHeaders = 1;
20201+               vopts.nSubjectRequired = env_get("UNSIGNED_SUBJECT") ? 0 : 1;
20202+               vopts.nHonorBodyLengthTag = env_get("HONOR_BODYLENGTHTAG") ? 0 : 1;
20203+               DKIMVerifyInit(&ctxt, &vopts);          /*- this is always successful */
20204+       }
20205+       /*- Initialization */
20206+       mypid = getpid();
20207+       uid = getuid();
20208+       datetime_tai(&dt, starttime);
20209+       sig_pipeignore();
20210+       sig_miscignore();
20211+       sig_alarmcatch(sigalrm);
20212+       sig_bugcatch(sigbug);
20213+       alarm(DEATH);
20214+       pidopen(); /*- fd = messfd */
20215+       if ((readfd = open_read(pidfn)) == -1)
20216+               die(63, dkimsign ? 1 : 2);
20217+       if (unlink(pidfn) == -1)
20218+               die(63, dkimsign ? 1 : 2);
20219+       substdio_fdbuf(&ssout, write, messfd, outbuf, sizeof(outbuf));
20220+       substdio_fdbuf(&ssin, read, 0, inbuf, sizeof(inbuf));
20221+       for (ret = 0;;) {
20222+               register int    n;
20223+               register char  *x;
20224+
20225+               if ((n = substdio_feed(&ssin)) < 0) {
20226+                       (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt);
20227+                       die_read();
20228+               }
20229+               if (!n)
20230+                       break;
20231+               x = substdio_PEEK(&ssin);
20232+               if (!ret) {
20233+                       if ((ret = (dkimsign ? DKIMSignProcess : DKIMVerifyProcess) (&ctxt, x, n)) == DKIM_INVALID_CONTEXT)
20234+                               (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt);
20235+                       maybe_die_dkim(ret);
20236+               }
20237+               if (substdio_put(&ssout, x, n) == -1) {
20238+                       (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt);
20239+                       die_write();
20240+               }
20241+               substdio_SEEK(&ssin, n);
20242+       }
20243+       if (substdio_flush(&ssout) == -1) {
20244+               (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt);
20245+               die_write();
20246+       }
20247+       if (dkimsign || dkimverify) {
20248+               if (dkimsign) {
20249+                       char           *p;
20250+
20251+                       if (!(p = DKIMSignGetDomain(&ctxt))) {
20252+                               DKIMSignFree(&ctxt);
20253+                               maybe_die_dkim(DKIM_INVALID_CONTEXT);
20254+                       }
20255+                       write_signature(p, dkimsign); /*- calls DKIMSignFree(&ctxt) */
20256+               } else
20257+               if (dkimverify) {
20258+                       char            szPolicy[512];
20259+
20260+                       if (!ret) {
20261+                               if ((ret = DKIMVerifyResults(&ctxt, &sCount, &sSize)) != DKIM_SUCCESS)
20262+                                       maybe_die_dkim(ret);
20263+                               if ((ret = DKIMVerifyGetDetails(&ctxt, &nSigCount, &pDetails, szPolicy)) != DKIM_SUCCESS)
20264+                                       maybe_die_dkim(ret);
20265+                               else
20266+                               for (ret = DKIM_FAIL,i = 0; i < nSigCount; i++) {
20267+                                       if (pDetails[i].nResult >= 0) {
20268+                                               ret = 0;
20269+                                       } else {
20270+                                               if (ret == DKIM_FAIL)
20271+                                                       ret = pDetails[i].nResult;
20272+                                       }
20273+                               }
20274+                               if (!nSigCount)
20275+                                       ret = DKIM_NO_SIGNATURES;
20276+                       }
20277+                       /*- what to do if DKIM Verification fails */
20278+                       if (checkPractice(ret, useADSP, useSSP)) {
20279+                               char           *domain;
20280+                               int             skip_nosignature_domain = 0;
20281+
20282+                               origRet = ret;
20283+                               if ((domain = DKIMVerifyGetDomain(&ctxt))) {
20284+                                       if (!(p = env_get("SIGNATUREDOMAINS"))) {
20285+                                               if (control_readfile(&sigdomains, "signaturedomains", 0) == -1)
20286+                                                       die(55, 2);
20287+                                       } else
20288+                                       if (!stralloc_copys(&sigdomains, p))
20289+                                               die(51, 2);
20290+                                       for (len = 0, p = sigdomains.s;len < sigdomains.len;) {
20291+                                               len += ((token_len = str_len(p)) + 1); /*- next domain */
20292+                                               if (!case_diffb(p, token_len, domain)) {
20293+                                                       ret = origRet;
20294+                                                       skip_nosignature_domain = 1;
20295+                                                       useADSP = 0;
20296+                                                       useSSP = 0;
20297+                                                       break;
20298+                                               }
20299+                                               p = sigdomains.s + len;
20300+                                       }
20301+                                       if (!skip_nosignature_domain) {
20302+                                               if (!(p = env_get("NOSIGNATUREDOMAINS"))) {
20303+                                                       if (control_readfile(&nsigdomains, "nosignaturedomains", 0) == -1)
20304+                                                               die(55, 2);
20305+                                               } else
20306+                                               if (!stralloc_copys(&nsigdomains, p))
20307+                                                       die(51, 2);
20308+                                               for (len = 0, p = nsigdomains.s;len < nsigdomains.len;) {
20309+                                                       len += ((token_len = str_len(p)) + 1); /*- next domain */
20310+                                                       if (*p == '*' || !case_diffb(p, token_len, domain)) {
20311+                                                               ret = DKIM_NEUTRAL;
20312+                                                               useADSP = 0;
20313+                                                               useSSP = 0;
20314+                                                               break;
20315+                                                       }
20316+                                                       p = nsigdomains.s + len;
20317+                                               }
20318+                                       }
20319+                               }
20320+                               if (!domain || !*domain)
20321+                                       ; /*- do nothing ? */
20322+                               else
20323+                               if (useADSP) {
20324+                                       resDKIMADSP = checkADSP(domain);
20325+                                       if (sCount > 0) {
20326+                                               if (resDKIMADSP == DKIM_ADSP_UNKNOWN || resDKIMADSP == DKIM_ADSP_ALL)
20327+                                                       ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
20328+                                       }
20329+                                       /* if the message should be signed, return fail */
20330+                                       if (resDKIMADSP == DKIM_ADSP_DISCARDABLE)
20331+                                               ret = DKIM_FAIL;
20332+                                       else
20333+                                               ret = DKIM_NEUTRAL;
20334+                               } else
20335+                               if (useSSP) {
20336+                                       int             bTestingPractices = 0;
20337+                                       char           *domain;
20338+
20339+                                       if ((domain = DKIMVerifyGetDomain(&ctxt)))
20340+                                               resDKIMSSP = checkSSP(domain, &bTestingPractices);
20341+                                       if (sCount > 0) {
20342+                                               if ((resDKIMSSP == DKIM_SSP_UNKNOWN || resDKIMSSP == DKIM_SSP_ALL))
20343+                                                       ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
20344+                                       }
20345+                                       // if the SSP is testing, return neutral
20346+                                       if (bTestingPractices)
20347+                                               ret = DKIM_NEUTRAL;
20348+                                       /* if the message should be signed, return fail */
20349+                                       if (resDKIMSSP == DKIM_SSP_ALL || resDKIMSSP == DKIM_SSP_STRICT)
20350+                                               ret = DKIM_FAIL;
20351+                                       else
20352+                                               ret = DKIM_NEUTRAL;
20353+                               }
20354+                       }
20355+                       DKIMVerifyFree(&ctxt);
20356+                       writeHeaderNexit(ret, origRet, resDKIMSSP, resDKIMADSP, useSSP, useADSP);
20357+               } /*- if (dkimverify) */
20358+       }
20359+       if (pipe(pim) == -1)
20360+               die(59, 0);
20361+       switch (pid = vfork())
20362+       {
20363+       case -1:
20364+               close(pim[0]);
20365+               close(pim[1]);
20366+               die(58, 0);
20367+       case 0:
20368+               close(pim[1]);
20369+               if (fd_move(0, pim[0]) == -1)
20370+                       die(120, 0);
20371+               execv(*binqqargs, binqqargs);
20372+               die(120, 0);
20373+       }
20374+       close(pim[0]);
20375+       substdio_fdbuf(&ssin, read, readfd, inbuf, sizeof(inbuf));
20376+       substdio_fdbuf(&ssout, write, pim[1], outbuf, sizeof(outbuf));
20377+       if (substdio_bput(&ssout, dkimoutput.s, dkimoutput.len) == -1) /*- write DKIM signature */
20378+               die_write();
20379+       switch (substdio_copy(&ssout, &ssin))
20380+       {
20381+       case -2:
20382+               die_read();
20383+       case -3:
20384+               die_write();
20385+       }
20386+       if (substdio_flush(&ssout) == -1)
20387+               die_write();
20388+       close(pim[1]);
20389+       if (wait_pid(&wstat, pid) != pid)
20390+               die(57, 0);
20391+       if (wait_crashed(wstat))
20392+               die(57, 0);
20393+       die(wait_exitcode(wstat), 0);
20394+       /*- Not Reached */
20395+       exit(0);
20396+}
20397+
20398+void
20399+getversion_qmail_dkim_c()
20400+{
20401+       static char    *x = "$Id: qmail-dkim.c,v 1.49 2018-08-08 23:58:01+05:30 Cprogrammer Exp mbhangui $";
20402+
20403+       x++;
20404+}
20405diff -ruN ../netqmail-1.06-original/qmail-inject.c netqmail-1.06/qmail-inject.c
20406--- ../netqmail-1.06-original/qmail-inject.c    1998-06-15 12:53:16.000000000 +0200
20407+++ netqmail-1.06/qmail-inject.c        2019-02-27 20:57:13.397024992 +0100
20408@@ -22,6 +22,7 @@
20409 #include "auto_qmail.h"
20410 #include "newfield.h"
20411 #include "constmap.h"
20412+#include "srs.h"
20413 
20414 #define LINELEN 80
20415 
20416@@ -61,6 +62,11 @@
20417 void temp() { _exit(111); }
20418 void die_nomem() {
20419  substdio_putsflush(subfderr,"qmail-inject: fatal: out of memory\n"); temp(); }
20420+void die_srs() {
20421+ substdio_puts("qmail-inject: fatal: ");
20422+ substdio_puts(subfderr,srs_error.s);
20423+ substdio_putsflush(subfderr,"\n");
20424+ perm(); }
20425 void die_invalid(sa) stralloc *sa; {
20426  substdio_putsflush(subfderr,"qmail-inject: fatal: invalid header field: ");
20427  substdio_putflush(subfderr,sa->s,sa->len); perm(); }
20428@@ -99,6 +105,17 @@
20429    int i;
20430 
20431    if (!stralloc_0(&sender)) die_nomem();
20432+   
20433+   if (!env_get("QMAILINJECT_SKIP_SRS") && (env_get("QMAILINJECT_FORCE_SRS") || (env_get("EXT") && env_get("HOST")))) {
20434+     switch(srsforward(sender.s)) {
20435+       case -3: die_srs(); break;
20436+       case -2: die_nomem(); break;
20437+       case -1: die_read(); break;
20438+       case 0: break;
20439+       case 1: if (!stralloc_copy(&sender,&srs_result)) die_nomem(); break;
20440+     }
20441+   }
20442+   
20443    qmail_from(&qqt,sender.s);
20444 
20445    for (i = 0;i < reciplist.len;++i)
20446@@ -269,6 +286,10 @@
20447 token822_alloc *addr;
20448 {
20449  if (!addr->len) return; /* don't rewrite <> */
20450+ if (addr->len == 1 && str_equal(addr->t[0].s,"<>")) {
20451+ addr->len = 0;
20452+ return;
20453+ }
20454  if (addr->len >= 2)
20455    if (addr->t[1].type == TOKEN822_AT)
20456      if (addr->t[0].type == TOKEN822_LITERAL)
20457diff -ruN ../netqmail-1.06-original/qmail-local.c netqmail-1.06/qmail-local.c
20458--- ../netqmail-1.06-original/qmail-local.c     2007-11-30 21:22:54.000000000 +0100
20459+++ netqmail-1.06/qmail-local.c 2019-02-27 20:57:13.398024981 +0100
20460@@ -28,6 +28,7 @@
20461 #include "myctime.h"
20462 #include "gfrom.h"
20463 #include "auto_patrn.h"
20464+#include "srs.h"
20465 
20466 void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); }
20467 
20468@@ -66,6 +67,15 @@
20469 
20470 char buf[1024];
20471 char outbuf[1024];
20472+#define QUOTABUFSIZE    256
20473+
20474+void die_control() { strerr_die1x(111,"Unable to read controls (#4.3.0)"); }
20475+void die_srs() {
20476+  if (!stralloc_copys(&foo,srs_error.s)) temp_nomem();
20477+  if (!stralloc_cats(&foo," (#4.3.0)")) temp_nomem();
20478+  if (!stralloc_0(&foo)) temp_nomem();
20479+  strerr_die1x(111,foo.s);
20480+}
20481 
20482 /* child process */
20483 
20484@@ -86,9 +96,15 @@
20485  int fd;
20486  substdio ss;
20487  substdio ssout;
20488+ char quotabuf[QUOTABUFSIZE];
20489 
20490  sig_alarmcatch(sigalrm);
20491  if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
20492+ if (maildir_getquota(dir, quotabuf) == 0) {
20493+  if (user_over_maildirquota(dir,quotabuf)==1) {
20494+   _exit(1);
20495+  }
20496+ }
20497  pid = getpid();
20498  host[0] = 0;
20499  gethostname(host,sizeof(host));
20500@@ -99,7 +115,10 @@
20501    s += fmt_str(s,"tmp/");
20502    s += fmt_ulong(s,time); *s++ = '.';
20503    s += fmt_ulong(s,pid); *s++ = '.';
20504-   s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
20505+   s += fmt_strn(s,host,sizeof(host));
20506+   s += fmt_strn(s,",S=",sizeof(",S="));
20507+   if (fstat(0,&st) == -1) if (errno == error_noent) break;
20508+   s += fmt_ulong(s,st.st_size+rpline.len+dtline.len); *s++ = 0;
20509    if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
20510    /* really should never get to this point */
20511    if (loop == 2) _exit(1);
20512@@ -159,6 +178,7 @@
20513  switch(wait_exitcode(wstat))
20514   {
20515    case 0: break;
20516+   case 1: strerr_die1x(1, "User over quota. (#5.1.1)");
20517    case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
20518    case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
20519    case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
20520@@ -282,6 +302,15 @@
20521    qmail_put(&qqt,messline.s,messline.len);
20522   }
20523  while (match);
20524+
20525+ switch(srsforward(ueo.s)) {
20526+   case -3: die_srs(); break;
20527+   case -2: temp_nomem(); break;
20528+   case -1: die_control(); break;
20529+   case 0: break;
20530+   case 1: if (!stralloc_copy(&ueo,&srs_result)) temp_nomem(); break;
20531+ }
20532+
20533  qmail_from(&qqt,ueo.s);
20534  while (*recips) qmail_to(&qqt,*recips++);
20535  qqx = qmail_close(&qqt);
20536diff -ruN ../netqmail-1.06-original/qmail-lspawn.c netqmail-1.06/qmail-lspawn.c
20537--- ../netqmail-1.06-original/qmail-lspawn.c    1998-06-15 12:53:16.000000000 +0200
20538+++ netqmail-1.06/qmail-lspawn.c        2019-02-27 20:57:13.398024981 +0100
20539@@ -1,4 +1,5 @@
20540 #include "fd.h"
20541+#include "env.h"
20542 #include "wait.h"
20543 #include "prot.h"
20544 #include "substdio.h"
20545@@ -170,6 +171,7 @@
20546 char *s; char *r; int at;
20547 {
20548  int f;
20549+ char *ptr;
20550 
20551  if (!(f = fork()))
20552   {
20553@@ -226,7 +228,10 @@
20554    if (prot_uid(uid) == -1) _exit(QLX_USAGE);
20555    if (!getuid()) _exit(QLX_ROOT);
20556 
20557-   execv(*args,args);
20558+       if(!(ptr = env_get("QMAILLOCAL")))
20559+               execv(*args, args);
20560+       else
20561+               execv(ptr, args);
20562    if (error_temp(errno)) _exit(QLX_EXECSOFT);
20563    _exit(QLX_EXECHARD);
20564   }
20565diff -ruN ../netqmail-1.06-original/qmail-newmvrt.c netqmail-1.06/qmail-newmvrt.c
20566--- ../netqmail-1.06-original/qmail-newmvrt.c   1970-01-01 01:00:00.000000000 +0100
20567+++ netqmail-1.06/qmail-newmvrt.c       2019-02-27 20:57:13.398024981 +0100
20568@@ -0,0 +1,70 @@
20569+#include "strerr.h"
20570+#include "stralloc.h"
20571+#include "substdio.h"
20572+#include "getln.h"
20573+#include "exit.h"
20574+#include "readwrite.h"
20575+#include "open.h"
20576+#include "auto_qmail.h"
20577+#include "cdbmss.h"
20578+
20579+#define FATAL "qmail-newmvrt: fatal: "
20580+
20581+void die_read()
20582+{
20583+  strerr_die2sys(111,FATAL,"unable to read control/morevalidrcptto: ");
20584+}
20585+void die_write()
20586+{
20587+  strerr_die2sys(111,FATAL,"unable to write to control/morevalidrcptto.tmp: ");
20588+}
20589+
20590+char inbuf[1024];
20591+substdio ssin;
20592+
20593+int fd;
20594+int fdtemp;
20595+
20596+struct cdbmss cdbmss;
20597+stralloc line = {0};
20598+int match;
20599+
20600+void main()
20601+{
20602+  umask(033);
20603+  if (chdir(auto_qmail) == -1)
20604+    strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": ");
20605+
20606+  fd = open_read("control/morevalidrcptto");
20607+  if (fd == -1) die_read();
20608+
20609+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf);
20610+
20611+  fdtemp = open_trunc("control/morevalidrcptto.tmp");
20612+  if (fdtemp == -1) die_write();
20613+
20614+  if (cdbmss_start(&cdbmss,fdtemp) == -1) die_write();
20615+
20616+  for (;;) {
20617+    if (getln(&ssin,&line,&match,'\n') != 0) die_read();
20618+    case_lowerb(line.s,line.len);
20619+    while (line.len) {
20620+      if (line.s[line.len - 1] == ' ') { --line.len; continue; }
20621+      if (line.s[line.len - 1] == '\n') { --line.len; continue; }
20622+      if (line.s[line.len - 1] == '\t') { --line.len; continue; }
20623+      if (line.s[0] != '#')
20624+       if (cdbmss_add(&cdbmss,line.s,line.len,"",0) == -1)
20625+         die_write();
20626+      break;
20627+    }
20628+    if (!match) break;
20629+  }
20630+
20631+  if (cdbmss_finish(&cdbmss) == -1) die_write();
20632+  if (fsync(fdtemp) == -1) die_write();
20633+  if (close(fdtemp) == -1) die_write(); /* NFS stupidity */
20634+  if (rename("control/morevalidrcptto.tmp","control/morevalidrcptto.cdb") == -1)
20635+    strerr_die2sys(111,FATAL,"unable to move control/morevalidrcptto.tmp to control/morevalidrcptto.cdb");
20636+
20637+  _exit(0);
20638+}
20639diff -ruN ../netqmail-1.06-original/qmail-pop3d.c netqmail-1.06/qmail-pop3d.c
20640--- ../netqmail-1.06-original/qmail-pop3d.c     2007-11-30 21:22:54.000000000 +0100
20641+++ netqmail-1.06/qmail-pop3d.c 2019-02-27 20:57:13.398024981 +0100
20642@@ -16,6 +16,11 @@
20643 #include "readwrite.h"
20644 #include "timeoutread.h"
20645 #include "timeoutwrite.h"
20646+#include <errno.h>
20647+#include "maildirquota.h"
20648+#include "maildirmisc.h"
20649+
20650+#define QUOTABUFSIZE 256
20651 
20652 void die() { _exit(0); }
20653 
20654@@ -45,19 +50,15 @@
20655 {
20656   substdio_put(&ssout,buf,len);
20657 }
20658-void puts(s) char *s;
20659-{
20660-  substdio_puts(&ssout,s);
20661-}
20662 void flush()
20663 {
20664   substdio_flush(&ssout);
20665 }
20666 void err(s) char *s;
20667 {
20668-  puts("-ERR ");
20669-  puts(s);
20670-  puts("\r\n");
20671+  substdio_puts(&ssout,"-ERR ");
20672+  substdio_puts(&ssout,s);
20673+  substdio_puts(&ssout,"\r\n");
20674   flush();
20675 }
20676 
20677@@ -73,7 +74,7 @@
20678 void err_nosuch() { err("unable to open that message"); }
20679 void err_nounlink() { err("unable to unlink all deleted messages"); }
20680 
20681-void okay(arg) char *arg; { puts("+OK \r\n"); flush(); }
20682+void okay() { substdio_puts(&ssout,"+OK \r\n"); flush(); }
20683 
20684 void printfn(fn) char *fn;
20685 {
20686@@ -153,11 +154,11 @@
20687 
20688   total = 0;
20689   for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
20690-  puts("+OK ");
20691+  substdio_puts(&ssout,"+OK ");
20692   put(strnum,fmt_uint(strnum,numm));
20693-  puts(" ");
20694+  substdio_puts(&ssout," ");
20695   put(strnum,fmt_ulong(strnum,total));
20696-  puts("\r\n");
20697+  substdio_puts(&ssout,"\r\n");
20698   flush();
20699 }
20700 
20701@@ -171,18 +172,41 @@
20702 
20703 void pop3_last(arg) char *arg;
20704 {
20705-  puts("+OK ");
20706+  substdio_puts(&ssout,"+OK ");
20707   put(strnum,fmt_uint(strnum,last));
20708-  puts("\r\n");
20709+  substdio_puts(&ssout,"\r\n");
20710   flush();
20711 }
20712 
20713 void pop3_quit(arg) char *arg;
20714 {
20715   int i;
20716+  char quotabuf[QUOTABUFSIZE];
20717+  int has_quota=maildir_getquota(".", quotabuf);
20718+
20719+  long deleted_bytes=0;
20720+  long deleted_messages=0;
20721+
20722   for (i = 0;i < numm;++i)
20723     if (m[i].flagdeleted) {
20724-      if (unlink(m[i].fn) == -1) err_nounlink();
20725+      unsigned long un=0;
20726+      const char *filename=m[i].fn;
20727+      if (has_quota == 0 && !MAILDIR_DELETED(filename)) {
20728+          if (maildir_parsequota(filename, &un)) {
20729+              struct stat stat_buf;
20730+
20731+              if (stat(filename, &stat_buf) == 0)
20732+                  un=stat_buf.st_size;
20733+          }
20734+      }
20735+      if (unlink(m[i].fn) == -1) {
20736+          err_nounlink();
20737+          un=0;
20738+      }
20739+      if (un) {
20740+          deleted_bytes -= un;
20741+          deleted_messages -= 1;
20742+      }
20743     }
20744     else
20745       if (str_start(m[i].fn,"new/")) {
20746@@ -192,6 +216,21 @@
20747        if (!stralloc_0(&line)) die_nomem();
20748        rename(m[i].fn,line.s); /* if it fails, bummer */
20749       }
20750+
20751+    if (deleted_messages < 0) {
20752+        int quotafd;
20753+
20754+        if (maildir_checkquota(".", &quotafd, quotabuf, deleted_bytes,
20755+                               deleted_messages) && errno != EAGAIN &&
20756+                               deleted_bytes >= 0)
20757+            {
20758+                if (quotafd >= 0) close (quotafd);
20759+            } else {
20760+                 maildir_addquota(".", quotafd, quotabuf,
20761+                                 deleted_bytes, deleted_messages);
20762+                 if (quotafd >= 0) close(quotafd);
20763+            }
20764+        }
20765   okay(0);
20766   die();
20767 }
20768@@ -222,10 +261,10 @@
20769 int flaguidl;
20770 {
20771   put(strnum,fmt_uint(strnum,i + 1));
20772-  puts(" ");
20773+  substdio_puts(&ssout," ");
20774   if (flaguidl) printfn(m[i].fn);
20775   else put(strnum,fmt_ulong(strnum,m[i].size));
20776-  puts("\r\n");
20777+  substdio_puts(&ssout,"\r\n");
20778 }
20779 
20780 void dolisting(arg,flaguidl) char *arg; int flaguidl;
20781@@ -234,7 +273,7 @@
20782   if (*arg) {
20783     i = msgno(arg);
20784     if (i == -1) return;
20785-    puts("+OK ");
20786+    substdio_puts(&ssout,"+OK ");
20787     list(i,flaguidl);
20788   }
20789   else {
20790@@ -242,7 +281,7 @@
20791     for (i = 0;i < numm;++i)
20792       if (!m[i].flagdeleted)
20793        list(i,flaguidl);
20794-    puts(".\r\n");
20795+    substdio_puts(&ssout,".\r\n");
20796   }
20797   flush();
20798 }
20799diff -ruN ../netqmail-1.06-original/qmail-pw2u.c netqmail-1.06/qmail-pw2u.c
20800--- ../netqmail-1.06-original/qmail-pw2u.c      1998-06-15 12:53:16.000000000 +0200
20801+++ netqmail-1.06/qmail-pw2u.c  2019-02-27 20:57:13.398024981 +0100
20802@@ -1,3 +1,4 @@
20803+#include <unistd.h>
20804 #include <sys/types.h>
20805 #include <sys/stat.h>
20806 #include "substdio.h"
20807diff -ruN ../netqmail-1.06-original/qmail-qmqpc.c netqmail-1.06/qmail-qmqpc.c
20808--- ../netqmail-1.06-original/qmail-qmqpc.c     1998-06-15 12:53:16.000000000 +0200
20809+++ netqmail-1.06/qmail-qmqpc.c 2019-02-27 20:57:13.398024981 +0100
20810@@ -102,6 +102,8 @@
20811 char *server;
20812 {
20813   struct ip_address ip;
20814+  struct ip_address outip;
20815+  outip.d[0]=outip.d[1]=outip.d[2]=outip.d[3]=(unsigned char) 0;
20816   char ch;
20817 
20818   if (!ip_scan(server,&ip)) return;
20819@@ -109,7 +111,7 @@
20820   qmqpfd = socket(AF_INET,SOCK_STREAM,0);
20821   if (qmqpfd == -1) die_socket();
20822 
20823-  if (timeoutconn(qmqpfd,&ip,PORT_QMQP,10) != 0) {
20824+  if (timeoutconn(qmqpfd,&ip,&outip,PORT_QMQP,10) != 0) {
20825     lasterror = 73;
20826     if (errno == error_timeout) lasterror = 72;
20827     close(qmqpfd);
20828diff -ruN ../netqmail-1.06-original/qmail-qmtpd.c netqmail-1.06/qmail-qmtpd.c
20829--- ../netqmail-1.06-original/qmail-qmtpd.c     1998-06-15 12:53:16.000000000 +0200
20830+++ netqmail-1.06/qmail-qmtpd.c 2019-02-27 20:57:13.399024970 +0100
20831@@ -1,3 +1,5 @@
20832+#include <unistd.h>
20833+#include <unistd.h>
20834 #include "stralloc.h"
20835 #include "substdio.h"
20836 #include "qmail.h"
20837diff -ruN ../netqmail-1.06-original/qmail-qstat.sh netqmail-1.06/qmail-qstat.sh
20838--- ../netqmail-1.06-original/qmail-qstat.sh    1998-06-15 12:53:16.000000000 +0200
20839+++ netqmail-1.06/qmail-qstat.sh        2019-02-27 20:57:13.399024970 +0100
20840@@ -1,7 +1,7 @@
20841 cd QMAIL
20842 messdirs=`echo queue/mess/* | wc -w`
20843 messfiles=`find queue/mess/* -print | wc -w`
20844-tododirs=`echo queue/todo | wc -w`
20845-todofiles=`find queue/todo -print | wc -w`
20846+tododirs=`echo queue/todo/* | wc -w`
20847+todofiles=`find queue/todo/* -print | wc -w`
20848 echo messages in queue: `expr $messfiles - $messdirs`
20849 echo messages in queue but not yet preprocessed: `expr $todofiles - $tododirs`
20850diff -ruN ../netqmail-1.06-original/qmail-queue.8 netqmail-1.06/qmail-queue.8
20851--- ../netqmail-1.06-original/qmail-queue.8     2007-11-30 21:22:54.000000000 +0100
20852+++ netqmail-1.06/qmail-queue.8 2019-02-27 20:57:13.399024970 +0100
20853@@ -46,6 +46,13 @@
20854 will invoke the contents of
20855 .B $QMAILQUEUE
20856 instead, if that environment variable is set.
20857+.SH "CONTROL FILES"
20858+.TP 5
20859+.I taps
20860+Should contain source address (T for To,F for From,
20861+A for Any), regex syntax of email addresses to tap
20862+and the associated email address to send the copy to.
20863+The fields should be separated by colon.
20864 .SH "FILESYSTEM RESTRICTIONS"
20865 .B qmail-queue
20866 imposes two constraints on the queue structure:
20867diff -ruN ../netqmail-1.06-original/qmail-queue.c netqmail-1.06/qmail-queue.c
20868--- ../netqmail-1.06-original/qmail-queue.c     1998-06-15 12:53:16.000000000 +0200
20869+++ netqmail-1.06/qmail-queue.c 2019-02-27 20:57:13.399024970 +0100
20870@@ -16,6 +16,8 @@
20871 #include "auto_uids.h"
20872 #include "date822fmt.h"
20873 #include "fmtqfn.h"
20874+#include "stralloc.h"
20875+#include "constmap.h"
20876 
20877 #define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
20878 #define ADDR 1003
20879@@ -25,6 +27,14 @@
20880 char outbuf[256];
20881 struct substdio ssout;
20882 
20883+int tapok = 0;
20884+stralloc tap = {0};
20885+struct constmap maptap;
20886+stralloc chkaddr = {0};
20887+int tapped;
20888+stralloc tapaddr = {0};
20889+stralloc controlfile = {0};
20890+
20891 datetime_sec starttime;
20892 struct datetime dt;
20893 unsigned long mypid;
20894@@ -175,13 +185,20 @@
20895 
20896  alarm(DEATH);
20897 
20898+ stralloc_copys( &controlfile, auto_qmail);
20899+ stralloc_cats( &controlfile, "/control/taps");
20900+ stralloc_0( &controlfile);
20901+ tapok = control_readfile(&tap,controlfile.s,0);
20902+ if (tapok == -1) die(65);
20903+ if (!constmap_init(&maptap,tap.s,tap.len,0)) die(65);
20904+
20905  pidopen();
20906  if (fstat(messfd,&pidst) == -1) die(63);
20907 
20908  messnum = pidst.st_ino;
20909  messfn = fnnum("mess/",1);
20910- todofn = fnnum("todo/",0);
20911- intdfn = fnnum("intd/",0);
20912+ todofn = fnnum("todo/",1);
20913+ intdfn = fnnum("intd/",1);
20914 
20915  if (link(pidfn,messfn) == -1) die(64);
20916  if (unlink(pidfn) == -1) die(63);
20917@@ -219,14 +236,28 @@
20918  if (substdio_get(&ssin,&ch,1) < 1) die_read();
20919  if (ch != 'F') die(91);
20920  if (substdio_bput(&ssout,&ch,1) == -1) die_write();
20921+ stralloc_0(&chkaddr);
20922  for (len = 0;len < ADDR;++len)
20923   {
20924+   if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1);
20925+   else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1);
20926    if (substdio_get(&ssin,&ch,1) < 1) die_read();
20927    if (substdio_put(&ssout,&ch,1) == -1) die_write();
20928    if (!ch) break;
20929   }
20930  if (len >= ADDR) die(11);
20931 
20932+ /* check the from address */
20933+ stralloc_0(&chkaddr);
20934+ if (tapped == 0 && tapcheck('F')==1 ) {
20935+   tapped = 1;
20936+   if ( tapaddr.len > 0 ) {
20937+     if (substdio_bput(&ssout,"T",1) == -1) die_write();
20938+     if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write();
20939+     if (substdio_bput(&ssout,"",1) == -1) die_write();
20940+   }
20941+ }
20942+
20943  if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write();
20944 
20945  for (;;)
20946@@ -237,10 +268,24 @@
20947    if (substdio_bput(&ssout,&ch,1) == -1) die_write();
20948    for (len = 0;len < ADDR;++len)
20949     {
20950+     if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1);
20951+     else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1);
20952      if (substdio_get(&ssin,&ch,1) < 1) die_read();
20953      if (substdio_bput(&ssout,&ch,1) == -1) die_write();
20954      if (!ch) break;
20955     }
20956+
20957+    /* check the to address */
20958+    stralloc_0(&chkaddr);
20959+    if (tapped == 0 && tapcheck('T')==1 ) {
20960+      tapped = 1;
20961+      if ( tapaddr.len > 0 ) {
20962+        if (substdio_bput(&ssout,"T",1) == -1) die_write();
20963+        if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write();
20964+        if (substdio_bput(&ssout,"",1) == -1) die_write();
20965+       }
20966+     }
20967+
20968    if (len >= ADDR) die(11);
20969   }
20970 
20971@@ -252,3 +297,47 @@
20972  triggerpull();
20973  die(0);
20974 }
20975+
20976+int tapcheck(t)
20977+char t;
20978+{
20979+  int i = 0;
20980+  int j = 0;
20981+  int x = 0;
20982+  int negate = 0;
20983+  stralloc curregex = {0};
20984+  char tmpbuf[200];
20985+
20986+  while (j < tap.len) {
20987+    i = j;
20988+    if ( tap.s[i]==t || tap.s[i]=='A'){
20989+            while ((tap.s[i] != ':') && (i < tap.len)) i++;
20990+            i++;
20991+            j=i;
20992+            while ((tap.s[i] != ':') && (i < tap.len)) i++;
20993+            if (tap.s[j] == '!') {
20994+              negate = 1;
20995+              j++;
20996+            }
20997+            stralloc_copys(&tapaddr, &tap.s[i+1]);
20998+
20999+            stralloc_copyb(&curregex,tap.s + j,(i - j));
21000+            stralloc_0(&curregex);
21001+            x = matchregex(chkaddr.s, curregex.s, tmpbuf);
21002+
21003+
21004+            if ((negate) && (x == 0)) {
21005+              return 1;
21006+            }
21007+            if (!(negate) && (x > 0)) {
21008+              return 1;
21009+            }
21010+    }
21011+    while ((tap.s[i] != '\0') && (i < tap.len)) i++;
21012+    j = i + 1;
21013+    negate = 0;
21014+
21015+
21016+  }
21017+  return 0;
21018+}
21019diff -ruN ../netqmail-1.06-original/qmail-remote.8 netqmail-1.06/qmail-remote.8
21020--- ../netqmail-1.06-original/qmail-remote.8    1998-06-15 12:53:16.000000000 +0200
21021+++ netqmail-1.06/qmail-remote.8        2019-02-27 20:57:13.399024970 +0100
21022@@ -100,6 +100,73 @@
21023 After this letter comes a human-readable description of
21024 what happened.
21025 
21026+.B qmail-remote
21027+may use SMTP Authenticaton of type CRAM-MD5, PLAIN, or LOGIN
21028+(in this order) to connect to remote hosts.
21029+The following reports are provided:
21030+.TP 5
21031+K
21032+no supported AUTH method found, continuing without authentication.
21033+.TP 5
21034+Z
21035+Connected to
21036+.I host
21037+but password expired.
21038+.TP 5
21039+Z
21040+Connected to
21041+.I host
21042+but authentication was rejected (AUTH PLAIN).
21043+.TP 5
21044+Z
21045+Connected to
21046+.I host
21047+but unable to base64encode (plain).
21048+.TP 5
21049+Z
21050+Connected to
21051+.I host
21052+but authentication was rejected (plain)."
21053+.TP 5
21054+Z
21055+Connected to
21056+.I host
21057+but authentication was rejected (AUTH LOGIN).
21058+.TP 5
21059+Z
21060+Connected to
21061+.I host
21062+but unable to base64encode user.
21063+.TP 5
21064+Z
21065+Connected to
21066+.I host
21067+but authentication was rejected (username).
21068+.TP 5
21069+Z
21070+Connected to
21071+.I host
21072+but unable to base64encode pass.
21073+.TP 5
21074+Z
21075+Connected to
21076+.I host
21077+but authentication was rejected (AUTH CRAM-MD5).
21078+Z
21079+Connected to
21080+.I host
21081+but unable to base64decode challenge.
21082+.TP 5
21083+Z
21084+Connected to
21085+.I host
21086+but unable to base64encode username+digest.
21087+.TP 5
21088+Z
21089+Connected to
21090+.I host
21091+but authentication was rejected (username+digest).
21092+.PP
21093 The recipient reports will always be printed in the same order as
21094 .BR qmail-remote 's
21095 .I recip
21096@@ -114,6 +181,55 @@
21097 always exits zero.
21098 .SH "CONTROL FILES"
21099 .TP 5
21100+.I authsenders
21101+Authenticated sender.
21102+For each
21103+.I sender
21104+included in
21105+.IR authsenders :
21106+.I sender\fB:\fIrelay\fB:\fIport\fB|\fIuser\fB|\fIpassword
21107+.B qmail-remote
21108+will try SMTP Authentication
21109+of type CRAM-MD5, LOGIN, or PLAIN
21110+with the provided user name
21111+.I user
21112+and password
21113+.I password
21114+(the authentication information)
21115+and eventually relay the
21116+mail through
21117+.I relay
21118+on port
21119+.IR port .
21120+The use of
21121+.I relay
21122+and
21123+.I port
21124+follows the same rules as for
21125+.IR smtproutes
21126+Note: In case
21127+.I sender
21128+is empty,
21129+.B qmail-remote
21130+will try to deliver each outgoing mail
21131+SMTP authenticated. If the authentication
21132+information is missing, the mail is
21133+delivered none-authenticated.
21134+.I authsenders
21135+can be constructed as follows:
21136+
21137+.EX
21138+   @example.com|generic|passwd
21139+   .subdomain.example.com|other|otherpw
21140+   mail@example.com|test|testpass
21141+   info@example.com:smtp.example.com:26|other|otherpw
21142+   :mailrelay.example.com:587|e=mc2|testpass
21143+.EE
21144+.TP 5
21145+.I clientcert.pem
21146+SSL certificate that is used to authenticate with the remote server
21147+during a TLS session.
21148+.TP 5
21149 .I helohost
21150 Current host name,
21151 for use solely in saying hello to the remote SMTP server.
21152@@ -123,12 +239,31 @@
21153 otherwise
21154 .B qmail-remote
21155 refuses to run.
21156+
21157+.TP 5
21158+.I notlshosts/<FQDN>
21159+.B qmail-remote
21160+will not try TLS on servers for which this file exists
21161+.RB ( <FQDN>
21162+is the fully-qualified domain name of the server).
21163+.IR (tlshosts/<FQDN>.pem
21164+takes precedence over this file however).
21165+
21166+.TP 5
21167+.I outgoingip
21168+IP address to be used on outgoing connections.
21169+Default: system-defined.
21170+The value
21171+.IR 0.0.0.0
21172+is equivalent to the system default.
21173 .TP 5
21174 .I smtproutes
21175 Artificial SMTP routes.
21176 Each route has the form
21177 .IR domain\fB:\fIrelay ,
21178-without any extra spaces.
21179+or
21180+.IR domain\fB:\fIrelay\fB|\fIuser\fB|\fIpassword
21181+in case of authenticated routes without any extra spaces.
21182 If
21183 .I domain
21184 matches
21185@@ -149,6 +284,7 @@
21186 
21187 .EX
21188    inside.af.mil:firewall.af.mil:26
21189+  :submission.myrelay.com:587|myuserid|mypasswd
21190 .EE
21191 
21192 .I relay
21193@@ -156,6 +292,8 @@
21194 this tells
21195 .B qmail-remote
21196 to look up MX records as usual.
21197+.I port
21198+value of 465 (deprecated smtps port) causes TLS session to be started.
21199 .I smtproutes
21200 may include wildcards:
21201 
21202@@ -182,6 +320,10 @@
21203 you are always safe using
21204 .I smtproutes
21205 if you do not accept mail from the network.
21206+Note:
21207+.I authsender
21208+routes have precedence over
21209+.IR smtproutes .
21210 .TP 5
21211 .I timeoutconnect
21212 Number of seconds
21213@@ -195,6 +337,88 @@
21214 .B qmail-remote
21215 will wait for each response from the remote SMTP server.
21216 Default: 1200.
21217+
21218+.TP 5
21219+.I tlsclientciphers
21220+A set of OpenSSL client cipher strings. Multiple ciphers
21221+contained in a string should be separated by a colon.
21222+
21223+.TP 5
21224+.I tlshosts/<FQDN>.pem
21225+.B qmail-remote
21226+requires TLS authentication from servers for which this file exists
21227+.RB ( <FQDN>
21228+is the fully-qualified domain name of the server). One of the
21229+.I dNSName
21230+or the
21231+.I CommonName
21232+attributes have to match. The file contains the trusted CA certificates.
21233+
21234+.B WARNING:
21235+this option may cause mail to be delayed, bounced, doublebounced, or lost.
21236+
21237+.TP 5
21238+.I tlshosts/exhaustivelist
21239+if this file exists
21240+no TLS will be tried on hosts other than those for which a file
21241+.B tlshosts/<FQDN>.pem
21242+exists.
21243+
21244+.SH "ENVIRONMENT VARIABLES READ"
21245+Environment variables may be defined globally in the
21246+.B qmail-smtpd
21247+startup script and/or individually as part of the
21248+.B tcpserver's
21249+cdb database.
21250+The environment variables may be quoted ("variable", or 'variable') and
21251+in case of global use, have to be exported.
21252+.B qmail-smtpd
21253+supports the following legacy environment variables, typically
21254+provided by
21255+.B tcpserver
21256+or
21257+.B sslserver
21258+or
21259+.BR tcp-env :
21260+.IR TCPREMOTEIP ,
21261+.IR TCPREMOTEHOST
21262+.IR TCPREMOTEINFO
21263+and
21264+.IR TCPLOCALPORT
21265+as well as
21266+.IR RELAYCLIENT .
21267+
21268+.B qmail-smtpd
21269+may use the following environment variables for SMTP authentication:
21270+.TP 5
21271+.IR SMTPAUTH
21272+is used to enable SMTP Authentication for the AUTH types
21273+LOGIN and PLAIN.
21274+In case
21275+.TP 5
21276+.IR SMTPAUTH='+cram'
21277+is defined,
21278+.B qmail-smtpd
21279+honors LOGIN, PLAIN, and additionally CRAM-MD5 authentication.
21280+Simply
21281+.TP 5
21282+.IR SMTPAUTH='cram'
21283+restricts authentication just to CRAM-MD5.
21284+If however
21285+.TP 5
21286+.IR SMTPAUTH='!'
21287+starts with an exclamation mark, AUTH is required. In particular,
21288+.TP 5
21289+.IR SMTPAUTH='!cram'
21290+may be useful.
21291+In opposite, if
21292+.TP 5
21293+.IR SMTPAUTH='-'
21294+starts with a dash, AUTH is disabled for particular
21295+connections.
21296+
21297+Note: The use of 'cram' requires a CRAM-MD5 enabled PAM.
21298+
21299 .SH "SEE ALSO"
21300 addresses(5),
21301 envelopes(5),
21302diff -ruN ../netqmail-1.06-original/qmail-remote.c netqmail-1.06/qmail-remote.c
21303--- ../netqmail-1.06-original/qmail-remote.c    1998-06-15 12:53:16.000000000 +0200
21304+++ netqmail-1.06/qmail-remote.c        2020-04-16 11:23:31.333050380 +0200
21305@@ -28,6 +28,7 @@
21306 #include "timeoutconn.h"
21307 #include "timeoutread.h"
21308 #include "timeoutwrite.h"
21309+#include "base64.h"
21310 
21311 #define HUGESMTPTEXT 5000
21312 
21313@@ -39,14 +40,37 @@
21314 static stralloc sauninit = {0};
21315 
21316 stralloc helohost = {0};
21317+stralloc outgoingip = {0};
21318 stralloc routes = {0};
21319 struct constmap maproutes;
21320 stralloc host = {0};
21321 stralloc sender = {0};
21322 
21323+stralloc authsenders = {0};
21324+struct constmap mapauthsenders;
21325+stralloc user = {0};
21326+stralloc pass = {0};
21327+stralloc auth = {0};
21328+stralloc plain = {0};
21329+stralloc chal  = {0};
21330+stralloc slop  = {0};
21331+char *authsender;
21332+
21333 saa reciplist = {0};
21334 
21335 struct ip_address partner;
21336+struct ip_address outip;
21337+
21338+#ifdef TLS
21339+# include <sys/stat.h>
21340+# include "tls.h"
21341+# include "ssl_timeoutio.h"
21342+# include <openssl/x509v3.h>
21343+# define EHLO 1
21344+
21345+int tls_init();
21346+const char *ssl_err_str = 0;
21347+#endif
21348 
21349 void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); }
21350 void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); }
21351@@ -56,6 +80,7 @@
21352 ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?';
21353 if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0); } }
21354 
21355+void temp_noip() { out("Zinvalid ipaddr in control/outgoingip (#4.3.0)\n"); zerodie(); }
21356 void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); }
21357 void temp_oserr() { out("Z\
21358 System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); }
21359@@ -86,6 +111,12 @@
21360 it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
21361 zerodie(); }
21362 
21363+void err_authprot() {
21364+  out("Kno supported AUTH method found, continuing without authentication.\n");
21365+  zero();
21366+  substdio_flush(subfdoutsmall);
21367+}
21368+
21369 void outhost()
21370 {
21371   char x[IPFMT];
21372@@ -99,6 +130,9 @@
21373   outhost();
21374   out(" but connection died. ");
21375   if (flagcritical) out("Possible duplicate! ");
21376+#ifdef TLS
21377+  if (ssl_err_str) { out((char *)ssl_err_str); out(" "); }
21378+#endif
21379   out("(#4.4.2)\n");
21380   zerodie();
21381 }
21382@@ -110,6 +144,12 @@
21383 int saferead(fd,buf,len) int fd; char *buf; int len;
21384 {
21385   int r;
21386+#ifdef TLS
21387+  if (ssl) {
21388+    r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len);
21389+    if (r < 0) ssl_err_str = ssl_error_str();
21390+  } else
21391+#endif
21392   r = timeoutread(timeout,smtpfd,buf,len);
21393   if (r <= 0) dropped();
21394   return r;
21395@@ -117,6 +157,12 @@
21396 int safewrite(fd,buf,len) int fd; char *buf; int len;
21397 {
21398   int r;
21399+#ifdef TLS
21400+  if (ssl) {
21401+    r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len);
21402+    if (r < 0) ssl_err_str = ssl_error_str();
21403+  } else
21404+#endif
21405   r = timeoutwrite(timeout,smtpfd,buf,len);
21406   if (r <= 0) dropped();
21407   return r;
21408@@ -163,6 +209,65 @@
21409   return code;
21410 }
21411 
21412+#ifdef EHLO
21413+saa ehlokw = {0}; /* list of EHLO keywords and parameters */
21414+int maxehlokwlen = 0;
21415+
21416+unsigned long ehlo()
21417+{
21418+  stralloc *sa;
21419+  char *s, *e, *p;
21420+  unsigned long code;
21421+
21422+  if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len;
21423+  ehlokw.len = 0;
21424+
21425+# ifdef MXPS
21426+  if (type == 's') return 0;
21427+# endif
21428+
21429+  substdio_puts(&smtpto, "EHLO ");
21430+  substdio_put(&smtpto, helohost.s, helohost.len);
21431+  substdio_puts(&smtpto, "\r\n");
21432+  substdio_flush(&smtpto);
21433+
21434+  code = smtpcode();
21435+  if (code != 250) return code;
21436+
21437+  s = smtptext.s;
21438+  while (*s++ != '\n') ; /* skip the first line: contains the domain */
21439+
21440+  e = smtptext.s + smtptext.len - 6; /* 250-?\n */
21441+  while (s <= e)
21442+  {
21443+    int wasspace = 0;
21444+
21445+    if (!saa_readyplus(&ehlokw, 1)) temp_nomem();
21446+    sa = ehlokw.sa + ehlokw.len++;
21447+    if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0;
21448+
21449+     /* smtptext is known to end in a '\n' */
21450+     for (p = (s += 4); ; ++p)
21451+       if (*p == '\n' || *p == ' ' || *p == '\t') {
21452+         if (!wasspace)
21453+           if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem();
21454+         if (*p == '\n') break;
21455+         wasspace = 1;
21456+       } else if (wasspace == 1) {
21457+         wasspace = 0;
21458+         s = p;
21459+       }
21460+    s = ++p;
21461+
21462+    /* keyword should consist of alpha-num and '-'
21463+     * broken AUTH might use '=' instead of space */
21464+    for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; }
21465+  }
21466+
21467+  return 250;
21468+}
21469+#endif
21470+
21471 void outsmtptext()
21472 {
21473   int i;
21474@@ -179,6 +284,16 @@
21475 char *prepend;
21476 char *append;
21477 {
21478+#ifdef TLS
21479+  /* shouldn't talk to the client unless in an appropriate state */
21480+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
21481+  OSSL_HANDSHAKE_STATE state = ssl ? SSL_get_state(ssl) : TLS_ST_BEFORE;
21482+  if (state & TLS_ST_OK || (!smtps && state & TLS_ST_BEFORE))
21483+#else
21484+  int state = ssl ? ssl->state : SSL_ST_BEFORE;
21485+  if (state & SSL_ST_OK || (!smtps && state & SSL_ST_BEFORE))
21486+#endif
21487+#endif
21488   substdio_putsflush(&smtpto,"QUIT\r\n");
21489   /* waiting for remote side is just too ridiculous */
21490   out(prepend);
21491@@ -186,6 +301,30 @@
21492   out(append);
21493   out(".\n");
21494   outsmtptext();
21495+
21496+#if defined(TLS) && defined(DEBUG)
21497+  if (ssl) {
21498+    X509 *peercert;
21499+
21500+    out("STARTTLS proto="); out(SSL_get_version(ssl));
21501+    out("; cipher="); out(SSL_get_cipher(ssl));
21502+
21503+    /* we want certificate details */
21504+    if (peercert = SSL_get_peer_certificate(ssl)) {
21505+      char *str;
21506+
21507+      str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0);
21508+      out("; subject="); out(str); OPENSSL_free(str);
21509+
21510+      str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0);
21511+      out("; issuer="); out(str); OPENSSL_free(str);
21512+
21513+      X509_free(peercert);
21514+    }
21515+    out(";\n");
21516+  }
21517+#endif
21518+
21519   zerodie();
21520 }
21521 
21522@@ -201,6 +340,16 @@
21523     if (ch == '.')
21524       substdio_put(&smtpto,".",1);
21525     while (ch != '\n') {
21526+      if (ch == '\r') {
21527+       r = substdio_get(&ssin, &ch, 1);
21528+       if (r == 0)
21529+               break;
21530+       if (r == -1) temp_read();
21531+       if (ch != '\n') {
21532+               substdio_put(&smtpto, "\r\n", 2);
21533+       } else
21534+               break;
21535+      }
21536       substdio_put(&smtpto,&ch,1);
21537       r = substdio_get(&ssin,&ch,1);
21538       if (r == 0) perm_partialline();
21539@@ -214,30 +363,436 @@
21540   substdio_flush(&smtpto);
21541 }
21542 
21543-stralloc recip = {0};
21544+#ifdef TLS
21545+char *partner_fqdn = 0;
21546 
21547-void smtp()
21548+# define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "")
21549+void tls_quit(const char *s1, const char *s2)
21550+{
21551+  out((char *)s1); if (s2) { out(": "); out((char *)s2); } TLS_QUIT;
21552+}
21553+# define tls_quit_error(s) tls_quit(s, ssl_error())
21554+
21555+int match_partner(const char *s, int len)
21556+{
21557+  if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1;
21558+  /* we also match if the name is *.domainname */
21559+  if (*s == '*') {
21560+    const char *domain = partner_fqdn + str_chr(partner_fqdn, '.');
21561+    if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1;
21562+  }
21563+  return 0;
21564+}
21565+
21566+/* don't want to fail handshake if certificate can't be verified */
21567+int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
21568+
21569+int tls_init()
21570 {
21571-  unsigned long code;
21572-  int flagbother;
21573   int i;
21574+  SSL *myssl;
21575+  SSL_CTX *ctx;
21576+  stralloc saciphers = {0};
21577+  const char *ciphers, *servercert = 0;
21578+
21579+  if (partner_fqdn) {
21580+    struct stat st;
21581+    stralloc tmp = {0};
21582+    if (!stralloc_copys(&tmp, "control/tlshosts/")
21583+      || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn))
21584+      || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem();
21585+    if (stat(tmp.s, &st) == 0)
21586+      servercert = tmp.s;
21587+    else {
21588+      if (!stralloc_copys(&tmp, "control/notlshosts/")
21589+        || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1))
21590+        temp_nomem();
21591+      if ((stat("control/tlshosts/exhaustivelist", &st) == 0) ||
21592+         (stat(tmp.s, &st) == 0)) {
21593+         alloc_free(tmp.s);
21594+         return 0;
21595+      }
21596+      alloc_free(tmp.s);
21597+    }
21598+  }
21599 
21600-  if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
21601-
21602-  substdio_puts(&smtpto,"HELO ");
21603-  substdio_put(&smtpto,helohost.s,helohost.len);
21604-  substdio_puts(&smtpto,"\r\n");
21605-  substdio_flush(&smtpto);
21606-  if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
21607-
21608+  if (!smtps) {
21609+    stralloc *sa = ehlokw.sa;
21610+    unsigned int len = ehlokw.len;
21611+    /* look for STARTTLS among EHLO keywords */
21612+    for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ;
21613+    if (!len) {
21614+      if (!servercert) return 0;
21615+      out("ZNo TLS achieved while "); out((char *)servercert);
21616+      out(" exists"); smtptext.len = 0; TLS_QUIT;
21617+    }
21618+  }
21619+
21620+  SSL_library_init();
21621+  ctx = SSL_CTX_new(SSLv23_client_method());
21622+  if (!ctx) {
21623+    if (!smtps && !servercert) return 0;
21624+    smtptext.len = 0;
21625+    tls_quit_error("ZTLS error initializing ctx");
21626+  }
21627+
21628+  /* POODLE vulnerability */
21629+  SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
21630+
21631+  if (servercert) {
21632+    if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) {
21633+      SSL_CTX_free(ctx);
21634+      smtptext.len = 0;
21635+      out("ZTLS unable to load "); tls_quit_error(servercert);
21636+    }
21637+    /* set the callback here; SSL_set_verify didn't work before 0.9.6c */
21638+    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb);
21639+  }
21640+
21641+  /* let the other side complain if it needs a cert and we don't have one */
21642+# define CLIENTCERT "control/clientcert.pem"
21643+  if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT))
21644+    SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM);
21645+# undef CLIENTCERT
21646+
21647+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
21648+  SSL_CTX_set_post_handshake_auth(ctx, 1);
21649+#endif
21650+
21651+  myssl = SSL_new(ctx);
21652+  SSL_CTX_free(ctx);
21653+  if (!myssl) {
21654+    if (!smtps && !servercert) return 0;
21655+    smtptext.len = 0;
21656+    tls_quit_error("ZTLS error initializing ssl");
21657+  }
21658+
21659+  if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n");
21660+
21661+  /* while the server is preparing a responce, do something else */
21662+  if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1)
21663+    { SSL_free(myssl); temp_control(); }
21664+  if (saciphers.len) {
21665+    for (i = 0; i < saciphers.len - 1; ++i)
21666+      if (!saciphers.s[i]) saciphers.s[i] = ':';
21667+    ciphers = saciphers.s;
21668+  }
21669+  else ciphers = "DEFAULT";
21670+  SSL_set_cipher_list(myssl, ciphers);
21671+  alloc_free(saciphers.s);
21672+
21673+  SSL_set_fd(myssl, smtpfd);
21674+
21675+  /* read the response to STARTTLS */
21676+  if (!smtps) {
21677+    if (smtpcode() != 220) {
21678+      SSL_free(myssl);
21679+      if (!servercert) return 0;
21680+      out("ZSTARTTLS rejected while ");
21681+      out((char *)servercert); out(" exists"); TLS_QUIT;
21682+    }
21683+    smtptext.len = 0;
21684+  }
21685+
21686+  ssl = myssl;
21687+  if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0)
21688+    tls_quit("ZTLS connect failed", ssl_error_str());
21689+
21690+  if (servercert) {
21691+    X509 *peercert;
21692+    STACK_OF(GENERAL_NAME) *gens;
21693+    int found_gen_dns = 0;
21694+    int matched_gen_dns = 0;
21695+
21696+    int r = SSL_get_verify_result(ssl);
21697+    if (r != X509_V_OK) {
21698+      out("ZTLS unable to verify server with ");
21699+      tls_quit(servercert, X509_verify_cert_error_string(r));
21700+    }
21701+    alloc_free(servercert);
21702+
21703+    peercert = SSL_get_peer_certificate(ssl);
21704+    if (!peercert) {
21705+      out("ZTLS unable to verify server ");
21706+      tls_quit(partner_fqdn, "no certificate provided");
21707+    }
21708+
21709+    /* RFC 2595 section 2.4: find a matching name
21710+     * first find a match among alternative names */
21711+    gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
21712+    if (gens) {
21713+      for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i)
21714+      {
21715+        const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
21716+        if (gn->type == GEN_DNS){
21717+          found_gen_dns = 1;
21718+          if (match_partner(gn->d.ia5->data, gn->d.ia5->length)){
21719+            matched_gen_dns = 1;
21720+            break;
21721+          }
21722+        }
21723+      }
21724+      sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
21725+    }
21726+
21727+    /* no SubjectAltName of type DNS found, look up commonName */
21728+    if (!found_gen_dns) {
21729+      stralloc peer = {0};
21730+      X509_NAME *subj = X509_get_subject_name(peercert);
21731+      i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1);
21732+      if (i >= 0) {
21733+        const ASN1_STRING *s = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, i));
21734+        if (s) { peer.len = s->length; peer.s = s->data; }
21735+      }
21736+      if (peer.len <= 0) {
21737+        out("ZTLS unable to verify server ");
21738+        tls_quit(partner_fqdn, "certificate contains no valid commonName");
21739+      }
21740+      if (!match_partner(peer.s, peer.len)) {
21741+        out("ZTLS unable to verify server "); out(partner_fqdn);
21742+        out(": received certificate for "); outsafe(&peer); TLS_QUIT;
21743+      }
21744+    } else if (!matched_gen_dns) {
21745+      out("ZTLS unable to verify server ");
21746+      tls_quit(partner_fqdn, "certificate contains no matching dNSNnames");
21747+    }
21748+
21749+    X509_free(peercert);
21750+  }
21751+
21752+  if (smtps) if (smtpcode() != 220)
21753+    quit("ZTLS Connected to "," but greeting failed");
21754+
21755+  return 1;
21756+}
21757+#endif
21758+
21759+stralloc recip = {0};
21760+
21761+void mailfrom()
21762+{
21763   substdio_puts(&smtpto,"MAIL FROM:<");
21764   substdio_put(&smtpto,sender.s,sender.len);
21765   substdio_puts(&smtpto,">\r\n");
21766   substdio_flush(&smtpto);
21767+}
21768+
21769+stralloc xuser = {0};
21770+
21771+int xtext(sa,s,len)
21772+stralloc *sa;
21773+char *s;
21774+int len;
21775+{
21776+  int i;
21777+
21778+  if(!stralloc_copys(sa,"")) temp_nomem();
21779
21780+  for (i = 0; i < len; i++) {
21781+    if (s[i] == '=') {
21782+      if (!stralloc_cats(sa,"+3D")) temp_nomem();
21783+    } else if (s[i] == '+') { 
21784+        if (!stralloc_cats(sa,"+2B")) temp_nomem();
21785+    } else if ((int) s[i] < 33 || (int) s[i] > 126) {
21786+        if (!stralloc_cats(sa,"+3F")) temp_nomem(); /* ok. not correct */
21787+    } else if (!stralloc_catb(sa,s+i,1)) {
21788+        temp_nomem();
21789+    }
21790+  }
21791+
21792+  return sa->len;
21793+}
21794+
21795+void mailfrom_xtext()
21796+{
21797+  if (!xtext(&xuser,user.s,user.len)) temp_nomem();
21798+  substdio_puts(&smtpto,"MAIL FROM:<");
21799+  substdio_put(&smtpto,sender.s,sender.len);
21800+  substdio_puts(&smtpto,"> AUTH=");
21801+  substdio_put(&smtpto,xuser.s,xuser.len);
21802+  substdio_puts(&smtpto,"\r\n");
21803+  substdio_flush(&smtpto);
21804+}
21805+
21806+int mailfrom_plain()
21807+{
21808+  substdio_puts(&smtpto,"AUTH PLAIN\r\n");
21809+  substdio_flush(&smtpto);
21810+  if (smtpcode() != 334) { quit("ZConnected to "," but authentication was rejected (AUTH PLAIN)."); return -1; }
21811+  if (!stralloc_cat(&plain,&user)) temp_nomem(); /* <authorization-id> */
21812+  if (!stralloc_0(&plain)) temp_nomem();
21813+  if (!stralloc_cat(&plain,&user)) temp_nomem(); /* <authentication-id> */
21814+  if (!stralloc_0(&plain)) temp_nomem();
21815+  if (!stralloc_cat(&plain,&pass)) temp_nomem(); /* password */
21816+  if (b64encode(&plain,&auth)) quit("ZConnected to "," but unable to base64encode (plain).");
21817+  substdio_put(&smtpto,auth.s,auth.len);
21818+  substdio_puts(&smtpto,"\r\n");
21819+  substdio_flush(&smtpto);
21820+  if (smtpcode() == 235) { mailfrom_xtext(); return 0; }
21821+  else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; }
21822+  else { quit("ZConnected to "," but authentication was rejected (plain)."); return 1; }
21823+
21824+  return 0;
21825+}
21826+
21827+int mailfrom_login()
21828+{
21829+  substdio_puts(&smtpto,"AUTH LOGIN\r\n");
21830+  substdio_flush(&smtpto);
21831+  if (smtpcode() != 334) { quit("ZConnected to "," but authentication was rejected (AUTH LOGIN)."); return -1; }
21832+
21833+  if (!stralloc_copys(&auth,"")) temp_nomem();
21834+  if (b64encode(&user,&auth)) quit("ZConnected to "," but unable to base64encode user.");
21835+  substdio_put(&smtpto,auth.s,auth.len);
21836+  substdio_puts(&smtpto,"\r\n");
21837+  substdio_flush(&smtpto);
21838+  if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (username).");
21839+
21840+  if (!stralloc_copys(&auth,"")) temp_nomem();
21841+  if (b64encode(&pass,&auth)) quit("ZConnected to "," but unable to base64encode pass.");
21842+  substdio_put(&smtpto,auth.s,auth.len);
21843+  substdio_puts(&smtpto,"\r\n");
21844+  substdio_flush(&smtpto);
21845+  if (smtpcode() == 235) { mailfrom_xtext(); return 0; }
21846+  else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; }
21847+  else { quit("ZConnected to "," but authentication was rejected (password)."); return 1; }
21848+}
21849+
21850+int mailfrom_cram()
21851+{
21852+  int j;
21853+  unsigned char h;
21854+  unsigned char digest[16];
21855+  unsigned char digascii[33];
21856+  static char hextab[]="0123456789abcdef";
21857+
21858+  substdio_puts(&smtpto,"AUTH CRAM-MD5\r\n");
21859+  substdio_flush(&smtpto);
21860+  if (smtpcode() != 334) { quit("ZConnected to "," but authentication was rejected (AUTH CRAM-MD5)."); return -1; }
21861+
21862+  if (str_chr(smtptext.s+4,' ')) {                     /* Challenge */
21863+    if(!stralloc_copys(&slop,"")) temp_nomem();
21864+    if (!stralloc_copyb(&slop,smtptext.s+4,smtptext.len-5)) temp_nomem();
21865+    if (b64decode(slop.s,slop.len,&chal)) quit("ZConnected to "," but unable to base64decode challenge.");
21866+  }
21867+   
21868+  hmac_md5(chal.s,chal.len,pass.s,pass.len,digest);
21869+
21870+  for (j = 0;j < 16;j++)                               /* HEX => ASCII */
21871+  {
21872+    digascii[2*j] = hextab[digest[j] >> 4]; 
21873+    digascii[2*j+1] = hextab[digest[j] & 0xf];
21874+  }
21875+  digascii[32]=0;
21876+
21877+  slop.len = 0;
21878+  if (!stralloc_copys(&slop,"")) temp_nomem();
21879+  if (!stralloc_cat(&slop,&user)) temp_nomem();                 /* user-id */
21880+  if (!stralloc_cats(&slop," ")) temp_nomem();
21881+  if (!stralloc_catb(&slop,digascii,32)) temp_nomem();   /* digest */
21882+
21883+  if (!stralloc_copys(&auth,"")) temp_nomem();
21884+  if (b64encode(&slop,&auth)) quit("ZConnected to "," but unable to base64encode username+digest.");
21885+  substdio_put(&smtpto,auth.s,auth.len);
21886+  substdio_puts(&smtpto,"\r\n");
21887+  substdio_flush(&smtpto);
21888+  if (smtpcode() == 235) { mailfrom_xtext(); return 0; }
21889+  else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; }
21890+  else { quit("ZConnected to "," but authentication was rejected (username+digest)."); return 1; }
21891+}
21892+
21893+void smtp_auth()
21894+{
21895+  int i, j;
21896+
21897+  for (i = 0; i + 8 < smtptext.len; i += str_chr(smtptext.s+i,'\n')+1)
21898+    if (!str_diffn(smtptext.s+i+4,"AUTH",4)) { 
21899+      if (j = str_chr(smtptext.s+i+8,'C') > 0)
21900+        if (case_starts(smtptext.s+i+8+j,"CRAM"))
21901+          if (mailfrom_cram() >= 0) return;
21902+
21903+      if (j = str_chr(smtptext.s+i+8,'P') > 0)
21904+        if (case_starts(smtptext.s+i+8+j,"PLAIN"))
21905+          if (mailfrom_plain() >= 0) return;
21906+
21907+      if (j = str_chr(smtptext.s+i+8,'L') > 0)
21908+        if (case_starts(smtptext.s+i+8+j,"LOGIN"))
21909+          if (mailfrom_login() >= 0) return;
21910+
21911+      err_authprot();
21912+      mailfrom();
21913+    }
21914+}
21915+
21916+void smtp()
21917+{
21918+  unsigned long code;
21919+  int flagbother;
21920+  int i;
21921+
21922+  #ifndef PORT_SMTP
21923+    /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */
21924+  # define port smtp_port
21925+  #endif
21926+
21927+  #ifdef TLS
21928+  # ifdef MXPS
21929+    if (type == 'S') smtps = 1;
21930+    else if (type != 's')
21931+  # endif
21932+      if (port == 465) smtps = 1;
21933+    if (!smtps)
21934+  #endif
21935+
21936+  code = smtpcode();
21937+  if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed");
21938+  if (code >= 400 && code < 500) return; /* try next MX, see RFC-2821 */
21939+  if (code != 220) quit("ZConnected to "," but greeting failed");
21940+
21941+#ifdef EHLO
21942+# ifdef TLS
21943+  if (!smtps)
21944+# endif
21945+  code = ehlo();
21946+
21947+# ifdef TLS
21948+  if (tls_init())
21949+    /* RFC2487 says we should issue EHLO (even if we might not need
21950+     * extensions); at the same time, it does not prohibit a server
21951+     * to reject the EHLO and make us fallback to HELO */
21952+    code = ehlo();
21953+# endif
21954+
21955+  if (code == 250) {
21956+    /* add EHLO response checks here */
21957+
21958+    /* and if EHLO failed, use HELO */
21959+  } else {
21960+#endif
21961+
21962+/*  if (smtpcode() != 250) { */
21963+    substdio_puts(&smtpto,"HELO ");
21964+    substdio_put(&smtpto,helohost.s,helohost.len);
21965+    substdio_puts(&smtpto,"\r\n");
21966+    substdio_flush(&smtpto);
21967+    code = smtpcode();
21968+    if (code >= 500) quit("DConnected to "," but my name was rejected");
21969+    if (code != 250) quit("ZConnected to "," but my name was rejected");
21970+/*  } */
21971+
21972+#ifdef EHLO
21973+  }
21974+#endif
21975+
21976+  if (user.len && pass.len)
21977+    smtp_auth();
21978+  else
21979+    mailfrom();
21980+
21981   code = smtpcode();
21982   if (code >= 500) quit("DConnected to "," but sender was rejected");
21983   if (code >= 400) quit("ZConnected to "," but sender was rejected");
21984-
21985+
21986   flagbother = 0;
21987   for (i = 0;i < reciplist.len;++i) {
21988     substdio_puts(&smtpto,"RCPT TO:<");
21989@@ -246,15 +801,23 @@
21990     substdio_flush(&smtpto);
21991     code = smtpcode();
21992     if (code >= 500) {
21993-      out("h"); outhost(); out(" does not like recipient.\n");
21994+      /* added by Endersys R&D Team */
21995+      out("h<From:"); outsafe(&sender); out(" To:"); outsafe(&reciplist.sa[i]); out("> ");  outhost(); out(" does not like recipient.\n");
21996       outsmtptext(); zero();
21997     }
21998     else if (code >= 400) {
21999-      out("s"); outhost(); out(" does not like recipient.\n");
22000+      /* added by Endersys R&D Team */
22001+      out("s<From:"); outsafe(&sender); out(" To:"); outsafe(&reciplist.sa[i]);  out("> ");  outhost(); out(" does not like recipient.\n");
22002       outsmtptext(); zero();
22003     }
22004     else {
22005-      out("r"); zero();
22006+       /*
22007+       * James Raftery <james@now.ie>
22008+       * Log _real_ envelope recipient, post canonicalisation.
22009+       * and modified by Endersys R&D Team
22010+       */
22011+
22012+      out("r<From:"); outsafe(&sender); out(" To:"); outsafe(&reciplist.sa[i]); out("> "); zero();
22013       flagbother = 1;
22014     }
22015   }
22016@@ -297,19 +860,14 @@
22017   if (!stralloc_cats(saout,"@")) temp_nomem();
22018 
22019   if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem();
22020-  if (flagcname)
22021-    switch(dns_cname(&canonhost)) {
22022-      case 0: *flagalias = 0; break;
22023-      case DNS_MEM: temp_nomem();
22024-      case DNS_SOFT: temp_dnscanon();
22025-      case DNS_HARD: ; /* alias loop, not our problem */
22026-    }
22027+  if (flagcname) *flagalias = 0;
22028 
22029   if (!stralloc_cat(saout,&canonhost)) temp_nomem();
22030 }
22031 
22032 void getcontrols()
22033 {
22034+  int r;
22035   if (control_init() == -1) temp_control();
22036   if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control();
22037   if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1)
22038@@ -324,48 +882,108 @@
22039     case 1:
22040       if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
22041   }
22042
22043+  switch(control_readfile(&authsenders,"control/authsenders",0)) {
22044+    case -1:
22045+       temp_control();
22046+    case 0:
22047+      if (!constmap_init(&mapauthsenders,"",0,1)) temp_nomem(); break;
22048+    case 1:
22049+      if (!constmap_init(&mapauthsenders,authsenders.s,authsenders.len,1)) temp_nomem(); break;
22050+  }
22051+ r = control_readline(&outgoingip,"control/outgoingip");
22052+ if (-1 == r) { if (errno == error_nomem) temp_nomem(); temp_control(); }
22053+ if (0 == r && !stralloc_copys(&outgoingip, "0.0.0.0")) temp_nomem();
22054+ if (str_equal(outgoingip.s, "0.0.0.0"))
22055+   { outip.d[0]=outip.d[1]=outip.d[2]=outip.d[3]=(unsigned long) 0; }
22056+ else if (!ip_scan(outgoingip.s, &outip)) temp_noip();
22057 }
22058 
22059-void main(argc,argv)
22060+int main(argc,argv)
22061 int argc;
22062 char **argv;
22063 {
22064   static ipalloc ip = {0};
22065-  int i;
22066+  int i, j;
22067   unsigned long random;
22068   char **recips;
22069   unsigned long prefme;
22070   int flagallaliases;
22071   int flagalias;
22072   char *relayhost;
22073-
22074+   
22075   sig_pipeignore();
22076   if (argc < 4) perm_usage();
22077   if (chdir(auto_qmail) == -1) temp_chdir();
22078   getcontrols();
22079 
22080-
22081   if (!stralloc_copys(&host,argv[1])) temp_nomem();
22082-
22083+
22084+  authsender = 0;
22085   relayhost = 0;
22086-  for (i = 0;i <= host.len;++i)
22087-    if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
22088-      if (relayhost = constmap(&maproutes,host.s + i,host.len - i))
22089+
22090+  addrmangle(&sender,argv[2],&flagalias,0);
22091+
22092+  for (i = 0;i <= sender.len;++i)
22093+    if ((i == 0) || (i == sender.len) || (sender.s[i] == '.') || (sender.s[i] == '@'))
22094+      if (authsender = constmap(&mapauthsenders,sender.s + i,sender.len - i))
22095         break;
22096-  if (relayhost && !*relayhost) relayhost = 0;
22097-
22098-  if (relayhost) {
22099-    i = str_chr(relayhost,':');
22100-    if (relayhost[i]) {
22101-      scan_ulong(relayhost + i + 1,&port);
22102-      relayhost[i] = 0;
22103+
22104+  if (authsender && !*authsender) authsender = 0;
22105+
22106+  if (authsender) {
22107+    i = str_chr(authsender,'|');
22108+    if (authsender[i]) {
22109+      j = str_chr(authsender + i + 1,'|');
22110+      if (authsender[j]) {
22111+        authsender[i] = 0;
22112+        authsender[i + j + 1] = 0;
22113+        if (!stralloc_copys(&user,"")) temp_nomem();
22114+        if (!stralloc_copys(&user,authsender + i + 1)) temp_nomem();
22115+        if (!stralloc_copys(&pass,"")) temp_nomem();
22116+        if (!stralloc_copys(&pass,authsender + i + j + 2)) temp_nomem();
22117+      }
22118+    }
22119+    i = str_chr(authsender,':');
22120+    if (authsender[i]) {
22121+      scan_ulong(authsender + i + 1,&port);
22122+      authsender[i] = 0;
22123     }
22124-    if (!stralloc_copys(&host,relayhost)) temp_nomem();
22125-  }
22126 
22127+    if (!stralloc_copys(&relayhost,authsender)) temp_nomem();
22128+    if (!stralloc_copys(&host,authsender)) temp_nomem();
22129+
22130+  }
22131+  else {                                       /* default smtproutes -- authenticated */
22132+    for (i = 0;i <= host.len;++i)
22133+      if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
22134+        if (relayhost = constmap(&maproutes,host.s + i,host.len - i))
22135+          break;
22136+
22137+    if (relayhost && !*relayhost) relayhost = 0;
22138+
22139+    if (relayhost) {
22140+      i = str_chr(relayhost,'|');
22141+      if (relayhost[i]) {
22142+        j = str_chr(relayhost + i + 1,'|');
22143+        if (relayhost[j]) {
22144+          relayhost[i] = 0;
22145+          relayhost[i + j + 1] = 0;
22146+          if (!stralloc_copys(&user,"")) temp_nomem();
22147+          if (!stralloc_copys(&user,relayhost + i + 1)) temp_nomem();
22148+          if (!stralloc_copys(&pass,"")) temp_nomem();
22149+          if (!stralloc_copys(&pass,relayhost + i + j + 2)) temp_nomem();
22150+        }
22151+      }
22152+      i = str_chr(relayhost,':');
22153+      if (relayhost[i]) {
22154+        scan_ulong(relayhost + i + 1,&port);
22155+        relayhost[i] = 0;
22156+      }
22157+      if (!stralloc_copys(&host,relayhost)) temp_nomem();
22158+    }
22159+  }
22160 
22161-  addrmangle(&sender,argv[2],&flagalias,0);
22162-
22163   if (!saa_readyplus(&reciplist,0)) temp_nomem();
22164   if (ipme_init() != 1) temp_oserr();
22165 
22166@@ -414,10 +1032,13 @@
22167     smtpfd = socket(AF_INET,SOCK_STREAM,0);
22168     if (smtpfd == -1) temp_oserr();
22169 
22170-    if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
22171+    if (timeoutconn(smtpfd,&ip.ix[i].ip,&outip,(unsigned int) port,timeoutconnect) == 0) {
22172       tcpto_err(&ip.ix[i].ip,0);
22173       partner = ip.ix[i].ip;
22174-      smtp(); /* does not return */
22175+#ifdef TLS
22176+      partner_fqdn = ip.ix[i].fqdn;
22177+#endif
22178+      smtp(); /* only returns when the next MX is to be tried */
22179     }
22180     tcpto_err(&ip.ix[i].ip,errno == error_timeout);
22181     close(smtpfd);
22182diff -ruN ../netqmail-1.06-original/qmail-rspawn.c netqmail-1.06/qmail-rspawn.c
22183--- ../netqmail-1.06-original/qmail-rspawn.c    1998-06-15 12:53:16.000000000 +0200
22184+++ netqmail-1.06/qmail-rspawn.c        2019-02-27 20:57:13.400024959 +0100
22185@@ -1,3 +1,4 @@
22186+#include "env.h"
22187 #include "fd.h"
22188 #include "wait.h"
22189 #include "substdio.h"
22190@@ -82,7 +83,7 @@
22191 char *s; char *r; int at;
22192 {
22193  int f;
22194- char *(args[5]);
22195+ char *ptr, *(args[5]);
22196 
22197  args[0] = "qmail-remote";
22198  args[1] = r + at + 1;
22199@@ -95,7 +96,10 @@
22200    if (fd_move(0,fdmess) == -1) _exit(111);
22201    if (fd_move(1,fdout) == -1) _exit(111);
22202    if (fd_copy(2,1) == -1) _exit(111);
22203-   execvp(*args,args);
22204+   if(!(ptr = env_get("QMAILREMOTE")))
22205+      execvp(*args, args);
22206+   else
22207+      execvp(ptr, args);
22208    if (error_temp(errno)) _exit(111);
22209    _exit(100);
22210   }
22211diff -ruN ../netqmail-1.06-original/qmail-send.9 netqmail-1.06/qmail-send.9
22212--- ../netqmail-1.06-original/qmail-send.9      1998-06-15 12:53:16.000000000 +0200
22213+++ netqmail-1.06/qmail-send.9  2019-06-26 16:45:05.514152928 +0200
22214@@ -16,6 +16,15 @@
22215 .B qmail-send
22216 leaves it in the queue and tries the addresses again later.
22217 
22218+.B Supplemental queues
22219+allow more than one queue for remote recipients. (CHANNELS - 2) supplemental queues total, because one queue is always
22220+designated for local deliveries and a second queue is always available for remote deliveries that
22221+don't match any of the domains listed in the supplemental queue control files.
22222+This makes it possible to divide remote deliveries into distinct queues at different concurrency
22223+levels and can be used as a throttling mechanism based on domain.
22224+Supplemental queues are managed by the supplsX and concurrencysupplX control files, where X is an integer from
22225+0 to (CHANNELS - 3).
22226+
22227 .B qmail-send
22228 prints a readable record of its activities to descriptor 0.
22229 It writes commands to
22230@@ -51,7 +60,13 @@
22231 .B qmail-send
22232 receives a HUP signal,
22233 it will reread
22234-.I locals
22235+.IR concurrencylocal ,
22236+.IR concurrencyremote ,
22237+.IR locals,
22238+.IR supplsX,
22239+.IR concurrencylocal,
22240+.IR concurrencyremote,
22241+.IR concurrencysupplX
22242 and
22243 .IR virtualdomains .
22244 .TP 5
22245@@ -93,6 +108,15 @@
22246 is limited at compile time to
22247 SPAWN.
22248 .TP 5
22249+.I concurrencysupplX
22250+Maximum number of simultaneous delivery attempts via supplemental
22251+channel X, where X is an integer starting at 0.
22252+Default: 20.
22253+If 0, deliveries via channel X will be put on hold.
22254+.I concurrencysupplX
22255+is limited at compile time to
22256+SPAWN.
22257+.TP 5
22258 .I doublebouncehost
22259 Double-bounce host.
22260 Default:
22261@@ -115,6 +139,10 @@
22262 (If that bounces,
22263 .B qmail-send
22264 gives up.)
22265+As a special case, if the first line of
22266+.IR doublebounceto
22267+is blank (contains a single linefeed), qmail-send will not queue
22268+the double-bounce at all.
22269 .TP 5
22270 .I envnoathost
22271 Presumed domain name for addresses without @ signs.
22272@@ -147,6 +175,12 @@
22273 is listed in
22274 .IR locals .
22275 .TP 5
22276+.I supplsX
22277+List of domain names that the current host
22278+will deliver on supplemental channel X where X is an integer starting at 0,
22279+one per line.
22280+No default.
22281+.TP 5
22282 .I percenthack
22283 List of domain names where the percent hack is applied.
22284 If
22285@@ -164,7 +198,9 @@
22286 handles
22287 .I percenthack
22288 before
22289-.IR locals .
22290+.I locals
22291+and
22292+.IR supplsX.
22293 .TP 5
22294 .I queuelifetime
22295 Number of seconds
22296diff -ruN ../netqmail-1.06-original/qmail-send.c netqmail-1.06/qmail-send.c
22297--- ../netqmail-1.06-original/qmail-send.c      1998-06-15 12:53:16.000000000 +0200
22298+++ netqmail-1.06/qmail-send.c  2019-06-26 16:42:40.004753618 +0200
22299@@ -31,6 +31,11 @@
22300 #include "constmap.h"
22301 #include "fmtqfn.h"
22302 #include "readsubdir.h"
22303+#include "srs.h"
22304+
22305+#include "auto_spawn.h"
22306+
22307+#include "channels.h"
22308 
22309 /* critical timing feature #1: if not triggered, do not busy-loop */
22310 /* critical timing feature #2: if triggered, respond within fixed time */
22311@@ -44,6 +49,8 @@
22312 
22313 int lifetime = 604800;
22314 
22315+int bouncemaxbytes = 50000;
22316+
22317 stralloc percenthack = {0};
22318 struct constmap mappercenthack;
22319 stralloc locals = {0};
22320@@ -55,17 +62,20 @@
22321 stralloc bouncehost = {0};
22322 stralloc doublebounceto = {0};
22323 stralloc doublebouncehost = {0};
22324+stralloc srs_domain = {0};
22325 
22326 char strnum2[FMT_ULONG];
22327 char strnum3[FMT_ULONG];
22328 
22329-#define CHANNELS 2
22330-char *chanaddr[CHANNELS] = { "local/", "remote/" };
22331-char *chanstatusmsg[CHANNELS] = { " local ", " remote " };
22332-char *tochan[CHANNELS] = { " to local ", " to remote " };
22333-int chanfdout[CHANNELS] = { 1, 3 };
22334-int chanfdin[CHANNELS] = { 2, 4 };
22335-int chanskip[CHANNELS] = { 10, 20 };
22336+char *chanaddr[CHANNELS];
22337+char *chanstatusmsg[CHANNELS];
22338+char *tochan[CHANNELS];
22339+int chanfdout[CHANNELS];
22340+int chanfdin[CHANNELS];
22341+int chanskip[CHANNELS];
22342+struct constmap mapsuppl[SUPPL_CHANNELS];
22343+stralloc suppls[SUPPL_CHANNELS];
22344+stralloc newsuppls[SUPPL_CHANNELS];
22345 
22346 int flagexitasap = 0; void sigterm() { flagexitasap = 1; }
22347 int flagrunasap = 0; void sigalrm() { flagrunasap = 1; }
22348@@ -82,12 +92,10 @@
22349 
22350 datetime_sec recent;
22351 
22352-
22353-/* this file is too long ----------------------------------------- FILENAMES */
22354-
22355 stralloc fn = {0};
22356 stralloc fn2 = {0};
22357 char fnmake_strnum[FMT_ULONG];
22358+stralloc fname = {0};
22359 
22360 void fnmake_init()
22361 {
22362@@ -96,7 +104,7 @@
22363 }
22364 
22365 void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); }
22366-void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); }
22367+void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,1); }
22368 void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); }
22369 void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); }
22370 void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); }
22371@@ -117,6 +125,7 @@
22372 {
22373   int i;
22374   int j;
22375+  int c;
22376   char *x;
22377   static stralloc addr = {0};
22378   int at;
22379@@ -159,6 +168,13 @@
22380 
22381   if (!stralloc_cat(&rwline,&addr)) return 0;
22382   if (!stralloc_0(&rwline)) return 0;
22383+
22384+  for (c = 0;c < SUPPL_CHANNELS;++c)
22385+  {
22386+      if (constmap(&mapsuppl[c],addr.s + at + 1,addr.len - at - 1))
22387+          return c + 3;
22388+  }
22389+
22390   return 2;
22391 }
22392 
22393@@ -228,7 +244,8 @@
22394 
22395 substdio sstoqc; char sstoqcbuf[1024];
22396 substdio ssfromqc; char ssfromqcbuf[1024];
22397-stralloc comm_buf[CHANNELS] = { {0}, {0} };
22398+
22399+stralloc comm_buf[CHANNELS];
22400 int comm_pos[CHANNELS];
22401 
22402 void comm_init()
22403@@ -262,6 +279,8 @@
22404  while (!stralloc_copys(&comm_buf[c],"")) nomem();
22405  ch = delnum;
22406  while (!stralloc_append(&comm_buf[c],&ch)) nomem();
22407+ ch = delnum >> 8;
22408+ while (!stralloc_append(&comm_buf[c],&ch)) nomem();
22409  fnmake_split(id);
22410  while (!stralloc_cats(&comm_buf[c],fn.s)) nomem();
22411  while (!stralloc_0(&comm_buf[c])) nomem();
22412@@ -382,7 +401,7 @@
22413 /* this file is too long ----------------------------------- PRIORITY QUEUES */
22414 
22415 prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */
22416-prioq pqchan[CHANNELS] = { {0}, {0} };
22417+prioq pqchan[CHANNELS];
22418 /* pqchan 0: -todo +info +local ?remote */
22419 /* pqchan 1: -todo +info ?local +remote */
22420 prioq pqfail = {0}; /* stat() failure; has to be pqadded again */
22421@@ -683,15 +702,39 @@
22422   }
22423  if (str_equal(sender.s,"#@[]"))
22424    log3("triple bounce: discarding ",fn2.s,"\n");
22425+ else if (!*sender.s && *doublebounceto.s == '@')
22426+   log3("double bounce: discarding ",fn2.s,"\n");
22427  else
22428   {
22429    if (qmail_open(&qqt) == -1)
22430     { log1("warning: unable to start qmail-queue, will try later\n"); return 0; }
22431    qp = qmail_qp(&qqt);
22432 
22433-   if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; }
22434-   else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; }
22435-
22436+   if (*sender.s) {
22437+     if (srs_domain.len) {
22438+       int j = 0;
22439+       j = byte_rchr(sender.s, sender.len, '@');
22440+       if (j < sender.len) {
22441+         if (srs_domain.len == sender.len - j - 1 && stralloc_starts(&srs_domain, sender.s + j + 1)) {
22442+           switch(srsreverse(sender.s)) {
22443+             case -3: log1(srs_error.s); log1("\n"); _exit(111); break;
22444+             case -2: nomem(); break;
22445+             case -1: log1("alert: unable to read controls\n"); _exit(111); break;
22446+             case 0: break;
22447+             case 1: if (!stralloc_copy(&sender,&srs_result)) nomem(); break;
22448+           }
22449+           if (chdir(auto_qmail) == -1) { log1("alert: unable to switch to home directory\n"); _exit(111); }
22450+           if (chdir("queue") == -1) { log1("alert: unable to switch to queue directory\n"); _exit(111); }
22451+         }
22452+       }
22453+     }
22454+     bouncesender = "";
22455+     bouncerecip = sender.s;
22456+   } else {
22457+     bouncesender = "#@[]";
22458+     bouncerecip = doublebounceto.s;
22459+   }
22460+   
22461    while (!newfield_datemake(now())) nomem();
22462    qmail_put(&qqt,newfield_date.s,newfield_date.len);
22463    qmail_puts(&qqt,"From: ");
22464@@ -740,9 +783,17 @@
22465      qmail_fail(&qqt);
22466    else
22467     {
22468+     int bytestogo = bouncemaxbytes;
22469+     int bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf;
22470      substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
22471-     while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
22472+     while (bytestoget > 0 && (r = substdio_get(&ssread,buf,bytestoget)) > 0) {
22473        qmail_put(&qqt,buf,r);
22474+       bytestogo -= bytestoget;
22475+       bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf;
22476+     }
22477+     if (r > 0) {
22478+       qmail_puts(&qqt,"\n\n--- End of message stripped.\n");
22479+     }
22480      close(fd);
22481      if (r == -1)
22482        qmail_fail(&qqt);
22483@@ -780,8 +831,8 @@
22484 ;
22485 
22486 unsigned long masterdelid = 1;
22487-unsigned int concurrency[CHANNELS] = { 10, 20 };
22488-unsigned int concurrencyused[CHANNELS] = { 0, 0 };
22489+unsigned int concurrency[CHANNELS];
22490+unsigned int concurrencyused[CHANNELS];
22491 struct del *d[CHANNELS];
22492 stralloc dline[CHANNELS];
22493 char delbuf[2048];
22494@@ -808,9 +859,9 @@
22495  for (c = 0;c < CHANNELS;++c)
22496   {
22497    flagspawnalive[c] = 1;
22498-   while (!(d[c] = (struct del *) alloc(concurrency[c] * sizeof(struct del))))
22499+   while (!(d[c] = (struct del *) alloc(auto_spawn * sizeof(struct del))))
22500      nomem();
22501-   for (i = 0;i < concurrency[c];++i)
22502+   for (i = 0;i < auto_spawn;++i)
22503     { d[c][i].used = 0; d[c][i].recip.s = 0; }
22504    dline[c].s = 0;
22505    while (!stralloc_copys(&dline[c],"")) nomem();
22506@@ -906,41 +957,42 @@
22507      dline[c].len = REPORTMAX;
22508      /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */
22509      /* but from a security point of view, we don't trust rspawn */
22510-   if (!ch && (dline[c].len > 1))
22511+   if (!ch && (dline[c].len > 2))
22512     {
22513      delnum = (unsigned int) (unsigned char) dline[c].s[0];
22514-     if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used)
22515+     delnum += (unsigned int) ((unsigned int) dline[c].s[1]) << 8;
22516+     if ((delnum < 0) || (delnum >= auto_spawn) || !d[c][delnum].used)
22517        log1("warning: internal error: delivery report out of range\n");
22518      else
22519       {
22520        strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0;
22521-       if (dline[c].s[1] == 'Z')
22522+       if (dline[c].s[2] == 'Z')
22523         if (jo[d[c][delnum].j].flagdying)
22524          {
22525-          dline[c].s[1] = 'D';
22526+          dline[c].s[2] = 'D';
22527           --dline[c].len;
22528           while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
22529           while (!stralloc_0(&dline[c])) nomem();
22530          }
22531-       switch(dline[c].s[1])
22532+       switch(dline[c].s[2])
22533        {
22534         case 'K':
22535           log3("delivery ",strnum3,": success: ");
22536-          logsafe(dline[c].s + 2);
22537+          logsafe(dline[c].s + 3);
22538           log1("\n");
22539           markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
22540           --jo[d[c][delnum].j].numtodo;
22541           break;
22542         case 'Z':
22543           log3("delivery ",strnum3,": deferral: ");
22544-          logsafe(dline[c].s + 2);
22545+          logsafe(dline[c].s + 3);
22546           log1("\n");
22547           break;
22548         case 'D':
22549           log3("delivery ",strnum3,": failure: ");
22550-          logsafe(dline[c].s + 2);
22551+          logsafe(dline[c].s + 3);
22552           log1("\n");
22553-          addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2);
22554+          addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 3);
22555           markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
22556           --jo[d[c][delnum].j].numtodo;
22557           break;
22558@@ -1215,8 +1267,10 @@
22559 
22560 /* this file is too long ---------------------------------------------- TODO */
22561 
22562+#ifndef EXTERNAL_TODO
22563 datetime_sec nexttodorun;
22564-DIR *tododir; /* if 0, have to opendir again */
22565+int flagtododir = 0; /* if 0, have to readsubdir_init again */
22566+readsubdir todosubdir;
22567 stralloc todoline = {0};
22568 char todobuf[SUBSTDIO_INSIZE];
22569 char todobufinfo[512];
22570@@ -1224,7 +1278,7 @@
22571 
22572 void todo_init()
22573 {
22574- tododir = 0;
22575+ flagtododir = 0;
22576  nexttodorun = now();
22577  trigger_set();
22578 }
22579@@ -1236,7 +1290,7 @@
22580 {
22581  if (flagexitasap) return;
22582  trigger_selprep(nfds,rfds);
22583- if (tododir) *wakeup = 0;
22584+ if (flagtododir) *wakeup = 0;
22585  if (*wakeup > nexttodorun) *wakeup = nexttodorun;
22586 }
22587 
22588@@ -1253,8 +1307,7 @@
22589  char ch;
22590  int match;
22591  unsigned long id;
22592- unsigned int len;
22593- direntry *d;
22594+ int z;
22595  int c;
22596  unsigned long uid;
22597  unsigned long pid;
22598@@ -1265,32 +1318,26 @@
22599 
22600  if (flagexitasap) return;
22601 
22602- if (!tododir)
22603+ if (!flagtododir)
22604   {
22605    if (!trigger_pulled(rfds))
22606      if (recent < nexttodorun)
22607        return;
22608    trigger_set();
22609-   tododir = opendir("todo");
22610-   if (!tododir)
22611-    {
22612-     pausedir("todo");
22613-     return;
22614-    }
22615+   readsubdir_init(&todosubdir, "todo", pausedir);
22616+   flagtododir = 1;
22617    nexttodorun = recent + SLEEP_TODO;
22618   }
22619 
22620- d = readdir(tododir);
22621- if (!d)
22622+ switch(readsubdir_next(&todosubdir, &id))
22623   {
22624-   closedir(tododir);
22625-   tododir = 0;
22626+    case 1:
22627+      break;
22628+    case 0:
22629+      flagtododir = 0;
22630+    default:
22631    return;
22632   }
22633- if (str_equal(d->d_name,".")) return;
22634- if (str_equal(d->d_name,"..")) return;
22635- len = scan_ulong(d->d_name,&id);
22636- if (!len || d->d_name[len]) return;
22637 
22638  fnmake_todo(id);
22639 
22640@@ -1363,12 +1410,9 @@
22641        log1("\n");
22642        break;
22643      case 'T':
22644-       switch(rewrite(todoline.s + 1))
22645-       {
22646-        case 0: nomem(); goto fail;
22647-        case 2: c = 1; break;
22648-        default: c = 0; break;
22649-        }
22650+       c = rewrite(todoline.s + 1);
22651+       if (c == 0) { nomem(); goto fail; }
22652+       c--;
22653        if (fdchan[c] == -1)
22654        {
22655         fnmake_chanaddr(id,c);
22656@@ -1438,17 +1482,175 @@
22657    if (fdchan[c] != -1) close(fdchan[c]);
22658 }
22659 
22660+#endif
22661+
22662+/* this file is too long ------------------------------------- EXTERNAL TODO */
22663+
22664+#ifdef EXTERNAL_TODO
22665+stralloc todoline = {0};
22666+char todobuf[2048];
22667+int todofdin;
22668+int todofdout;
22669+int flagtodoalive;
22670+
22671+void tododied() { log1("alert: oh no! lost qmail-todo connection! dying...\n");
22672+ flagexitasap = 1; flagtodoalive = 0; }
22673+
22674+void todo_init()
22675+{
22676+  todofdout = 7;
22677+  todofdin = 8;
22678+  flagtodoalive = 1;
22679+  /* sync with external todo */
22680+  if (write(todofdout, "S", 1) != 1) tododied();
22681
22682+  return;
22683+}
22684+
22685+void todo_selprep(nfds,rfds,wakeup)
22686+int *nfds;
22687+fd_set *rfds;
22688+datetime_sec *wakeup;
22689+{
22690+  if (flagexitasap) {
22691+    if (flagtodoalive) {
22692+      write(todofdout, "X", 1);
22693+    }
22694+  }
22695+  if (flagtodoalive) {
22696+    FD_SET(todofdin,rfds);
22697+    if (*nfds <= todofdin)
22698+      *nfds = todofdin + 1;
22699+  }
22700+}
22701+
22702+void todo_del(char* s)
22703+{
22704+ int flagchan[CHANNELS];
22705+ struct prioq_elt pe;
22706+ unsigned long id;
22707+ unsigned int len;
22708+ int c;
22709+
22710+ for (c = 0;c < CHANNELS;++c) flagchan[c] = 0;
22711+
22712+ for (c = 0;c < CHANNELS;++c)
22713+ {
22714+  if (!*s)
22715+  {
22716+     log1("warning: qmail-send unable to understand qmail-todo\n");
22717+     return;
22718+  }
22719+
22720+  switch(*s++) {
22721+   case '0':
22722+     flagchan[c] = 0;
22723+     break;
22724+   case '1':
22725+     flagchan[c] = 1;
22726+     break;
22727+   default:
22728+     log1("warning: qmail-send unable to understand qmail-todo\n");
22729+     return;
22730+  }
22731+ }
22732+
22733+ len = scan_ulong(s,&id);
22734+ if (!len || s[len]) {
22735+  log1("warning: qmail-send unable to understand qmail-todo\n");
22736+  return;
22737+ }
22738+
22739+ pe.id = id; pe.dt = now();
22740+ for (c = 0;c < CHANNELS;++c)
22741+   if (flagchan[c])
22742+     while (!prioq_insert(&pqchan[c],&pe)) nomem();
22743+
22744+ for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
22745+ if (c == CHANNELS)
22746+   while (!prioq_insert(&pqdone,&pe)) nomem();
22747+
22748+ return;
22749+}
22750+
22751+void todo_do(rfds)
22752+fd_set *rfds;
22753+{
22754+  int r;
22755+  char ch;
22756+  int i;
22757
22758+  if (!flagtodoalive) return;
22759+  if (!FD_ISSET(todofdin,rfds)) return;
22760+
22761+  r = read(todofdin,todobuf,sizeof(todobuf));
22762+  if (r == -1) return;
22763+  if (r == 0) {
22764+    if (flagexitasap)
22765+      flagtodoalive = 0;
22766+    else
22767+      tododied();
22768+    return;
22769+  }
22770+  for (i = 0;i < r;++i) {
22771+    ch = todobuf[i];
22772+    while (!stralloc_append(&todoline,&ch)) nomem();
22773+    if (todoline.len > REPORTMAX)
22774+      todoline.len = REPORTMAX;
22775+      /* qmail-todo is responsible for keeping it short */
22776+    if (!ch && (todoline.len > 1)) {
22777+      switch (todoline.s[0]) {
22778+       case 'D':
22779+         if (flagexitasap) break;
22780+         todo_del(todoline.s + 1);
22781+         break;
22782+       case 'L':
22783+         log1(todoline.s + 1);
22784+         break;
22785+       case 'X':
22786+         if (flagexitasap)
22787+           flagtodoalive = 0;
22788+         else
22789+           tododied();
22790+         break;
22791+       default:
22792+         log1("warning: qmail-send unable to understand qmail-todo: report mangled\n");
22793+         break;
22794+      }
22795+      todoline.len = 0;
22796+    }
22797+  }
22798+}
22799+
22800+#endif
22801 
22802 /* this file is too long ---------------------------------------------- MAIN */
22803 
22804-int getcontrols() { if (control_init() == -1) return 0;
22805+int getcontrols() {
22806+ int c;
22807+ int ck = 0;
22808+
22809+ if (control_init() == -1) return 0;
22810+ if (control_readint(&bouncemaxbytes,"control/bouncemaxbytes") == -1) return 0;
22811  if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0;
22812  if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0;
22813  if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0;
22814+
22815+ for (c = 2,ck = 0;c < CHANNELS;++c)
22816+ {
22817+     strnum2[fmt_uint(strnum2,ck++)] = 0;
22818+     if (!stralloc_copys(&fname,"control/concurrencysuppl")) return 0;
22819+     if (!stralloc_cats(&fname,strnum2)) return 0;
22820+     if (!stralloc_0(&fname)) return 0;
22821+     if (control_readint(&concurrency[c],fname.s) == -1) return 0;
22822+ }
22823+
22824  if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0;
22825  if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0;
22826  if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0;
22827  if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0;
22828+ if (control_readline(&srs_domain,"control/srs_domain") == -1) return 0;
22829+ if (srs_domain.len && !stralloc_0(&srs_domain)) return 0;
22830  if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0;
22831  if (!stralloc_cats(&doublebounceto,"@")) return 0;
22832  if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0;
22833@@ -1467,6 +1669,21 @@
22834    case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break;
22835    case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break;
22836   }
22837+
22838+ for (c = 0;c < SUPPL_CHANNELS;++c)
22839+ {
22840+     strnum2[fmt_uint(strnum2,c)] = 0;
22841+     if (!stralloc_copys(&fname,"control/suppls")) return 0;
22842+     if (!stralloc_cats(&fname,strnum2)) return 0;
22843+     if (!stralloc_0(&fname)) return 0;
22844+     switch (control_readfile(&suppls[c],fname.s,0))
22845+     {
22846+         case -1: return 0;
22847+         case 0: if (!constmap_init(&mapsuppl[c],"",0,0)) return 0; break;
22848+         case 1: if (!constmap_init(&mapsuppl[c],suppls[c].s,suppls[c].len,0)) return 0; break;
22849+     }
22850+ }
22851+
22852  return 1; }
22853 
22854 stralloc newlocals = {0};
22855@@ -1475,9 +1692,33 @@
22856 void regetcontrols()
22857 {
22858  int r;
22859+ int c;
22860+ int ck = 0;
22861+
22862+ if (control_readint(&concurrency[0],"control/concurrencylocal") == -1)
22863+  { log1("alert: unable to reread control/concurrencylocal\n"); return; }
22864+ if (control_readint(&concurrency[1],"control/concurrencyremote") == -1)
22865+  { log1("alert: unable to reread control/concurrencyremote\n"); return; }
22866+
22867+ for (c = 2,ck = 0;c < CHANNELS;++c)
22868+ {
22869+     strnum2[fmt_uint(strnum2,ck++)] = 0;
22870+     if (!stralloc_copys(&fname,"control/concurrencysuppl"))
22871+         { log3("alert: unable to reread ",fname.s,"\n"); return; }
22872+     if (!stralloc_cats(&fname,strnum2))
22873+         { log3("alert: unable to reread ",fname.s,"\n"); return; }
22874+     if (!stralloc_0(&fname))
22875+         { log3("alert: unable to reread ",fname.s,"\n"); return; }
22876+     if (control_readint(&concurrency[c],fname.s) == -1)
22877+         { log3("alert: unable to reread ",fname.s,"\n"); return; }
22878+ }
22879 
22880  if (control_readfile(&newlocals,"control/locals",1) != 1)
22881   { log1("alert: unable to reread control/locals\n"); return; }
22882+ if (control_readint(&concurrency[0],"control/concurrencylocal") == -1)
22883+  { log1("alert: unable to reread control/concurrencylocal\n",0); return; }
22884+ if (control_readint(&concurrency[1],"control/concurrencyremote") == -1)
22885+  { log1("alert: unable to reread control/concurrencyremote\n",0); return; }
22886  r = control_readfile(&newvdoms,"control/virtualdomains",0);
22887  if (r == -1)
22888   { log1("alert: unable to reread control/virtualdomains\n"); return; }
22889@@ -1495,6 +1736,28 @@
22890   }
22891  else
22892    while (!constmap_init(&mapvdoms,"",0,1)) nomem();
22893+
22894+ for (c = 0;c < SUPPL_CHANNELS;++c)
22895+ {
22896+     strnum2[fmt_uint(strnum2,c)] = 0;
22897+     if (!stralloc_copys(&fname,"control/suppls")) nomem();
22898+     if (!stralloc_cats(&fname,strnum2)) nomem();
22899+     if (!stralloc_0(&fname)) nomem();
22900+     r = control_readfile(&newsuppls[c],fname.s,0);
22901+     if (r == -1)
22902+      { log3("alert: qmail-todo: unable to reread ", fname.s, "\n"); return; }
22903+
22904+     constmap_free(&mapsuppl[c]);
22905+
22906+     if (r)
22907+      {
22908+       while (!stralloc_copy(&suppls[c],&newsuppls[c])) nomem();
22909+       while (!constmap_init(&mapsuppl[c],suppls[c].s,suppls[c].len,0)) nomem();
22910+      }
22911+     else
22912+       while (!constmap_init(&mapsuppl[c],"",0,0)) nomem();
22913+ }
22914+
22915 }
22916 
22917 void reread()
22918@@ -1504,6 +1767,9 @@
22919    log1("alert: unable to reread controls: unable to switch to home directory\n");
22920    return;
22921   }
22922+#ifdef EXTERNAL_TODO
22923+ write(todofdout, "H", 1);
22924+#endif
22925  regetcontrols();
22926  while (chdir("queue") == -1)
22927   {
22928@@ -1512,6 +1778,104 @@
22929   }
22930 }
22931 
22932+
22933+static int static_i = 0;
22934+static int static_j = 0;
22935+static void channels_init(void)
22936+{
22937+    chanaddr[0] = "local/";
22938+    chanaddr[1] = "remote/";
22939+    for (static_i=2,static_j=0;static_i<CHANNELS;static_i++,static_j++)
22940+    {
22941+        stralloc fnc = {0};
22942+        strnum2[fmt_uint(strnum2,static_j)] = 0;
22943+        if (!stralloc_copys(&fname,QDIR_BASENAME)) nomem();
22944+        if (!stralloc_cats(&fname,strnum2)) nomem();
22945+        if (!stralloc_cats(&fname,"/")) nomem();
22946+        if (!stralloc_0(&fname)) nomem();
22947+        if (!stralloc_copy(&fnc,&fname)) nomem();
22948+        chanaddr[static_i] = fnc.s;
22949+    }
22950+
22951+    chanstatusmsg[0] = " local ";
22952+    chanstatusmsg[1] = " remote ";
22953+    for (static_i=2,static_j=0;static_i<CHANNELS;static_i++,static_j++)
22954+    {
22955+        stralloc fnc = {0};
22956+        strnum2[fmt_uint(strnum2,static_j)] = 0;
22957+        if (!stralloc_copys(&fname," " QDIR_BASENAME)) nomem();
22958+        if (!stralloc_cats(&fname,strnum2)) nomem();
22959+        if (!stralloc_cats(&fname," ")) nomem();
22960+        if (!stralloc_0(&fname)) nomem();
22961+        if (!stralloc_copy(&fnc,&fname)) nomem();
22962+        chanstatusmsg[static_i] = fnc.s;
22963+    }
22964+
22965+    tochan[0] = " to local ";
22966+    tochan[1] = " to remote ";
22967+    static_j = 0;
22968+    for (static_i=2;static_i<CHANNELS;static_i++)
22969+    {
22970+        stralloc fnc = {0};
22971+        strnum2[fmt_uint(strnum2,static_j++)] = 0;
22972+        if (!stralloc_copys(&fname," to " QDIR_BASENAME)) nomem();
22973+        if (!stralloc_cats(&fname,strnum2)) nomem();
22974+        if (!stralloc_cats(&fname," ")) nomem();
22975+        if (!stralloc_0(&fname)) nomem();
22976+        if (!stralloc_copy(&fnc,&fname)) nomem();
22977+        tochan[static_i] = fnc.s;
22978+    }
22979+
22980+    chanfdout[0] = 1;
22981+    chanfdout[1] = 3;
22982+    static_j = 1+CHANNEL_FD_OFFSET;
22983+    for (static_i=2;static_i<CHANNELS;static_i++)
22984+    {
22985+        chanfdout[static_i] = static_j;
22986+        static_j+=2;
22987+    }
22988+
22989+    chanfdin[0] = 2;
22990+    chanfdin[1] = 4;
22991+    static_j = 2+CHANNEL_FD_OFFSET;
22992+    for (static_i=2;static_i<CHANNELS;static_i++)
22993+    {
22994+        chanfdin[static_i] = static_j;
22995+        static_j+=2;
22996+    }
22997+
22998+    chanskip[0] = 10;
22999+    chanskip[1] = 20;
23000+    static_j = 20;
23001+    for (static_i=2;static_i<CHANNELS;static_i++)
23002+    {
23003+        chanskip[static_i] = static_j;
23004+    }
23005+
23006+    for (static_i=0;static_i<CHANNELS;static_i++)
23007+        comm_buf[static_i].s = 0;
23008+
23009+    for (static_i=0;static_i<CHANNELS;static_i++)
23010+        pqchan[static_i].p = 0;
23011+
23012+    concurrency[0] = 10;
23013+    concurrency[1] = 20;
23014+    for (static_i=2;static_i<CHANNELS;static_i++)
23015+    {
23016+        concurrency[static_i] = 20;
23017+    }
23018+
23019+    for (static_i=0;static_i<CHANNELS;static_i++)
23020+        concurrencyused[static_i] = 0;
23021+
23022+    for (static_i=0;static_i<SUPPL_CHANNELS;static_i++)
23023+        suppls[static_i].s = 0;
23024+
23025+    for (static_i=0;static_i<SUPPL_CHANNELS;static_i++)
23026+        newsuppls[static_i].s = 0;
23027+
23028+}
23029+
23030 void main()
23031 {
23032  int fd;
23033@@ -1522,6 +1886,8 @@
23034  struct timeval tv;
23035  int c;
23036 
23037+ channels_init();
23038+
23039  if (chdir(auto_qmail) == -1)
23040   { log1("alert: cannot start: unable to switch to home directory\n"); _exit(111); }
23041  if (!getcontrols())
23042@@ -1544,7 +1910,7 @@
23043  numjobs = 0;
23044  for (c = 0;c < CHANNELS;++c)
23045   {
23046-   char ch;
23047+   char ch, ch1;
23048    int u;
23049    int r;
23050    do
23051@@ -1552,7 +1918,13 @@
23052    while ((r == -1) && (errno == error_intr));
23053    if (r < 1)
23054     { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
23055+   do
23056+     r = read(chanfdin[c],&ch1,1);
23057+   while ((r == -1) && (errno == error_intr));
23058+   if (r < 1)
23059+    { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
23060    u = (unsigned int) (unsigned char) ch;
23061+   u += (unsigned int) ((unsigned char) ch1) << 8;
23062    if (concurrency[c] > u) concurrency[c] = u;
23063    numjobs += concurrency[c];
23064   }
23065@@ -1568,7 +1940,11 @@
23066  todo_init();
23067  cleanup_init();
23068 
23069+#ifdef EXTERNAL_TODO
23070+ while (!flagexitasap || !del_canexit() || flagtodoalive)
23071+#else
23072  while (!flagexitasap || !del_canexit())
23073+#endif
23074   {
23075    recent = now();
23076 
23077diff -ruN ../netqmail-1.06-original/qmail-showctl.c netqmail-1.06/qmail-showctl.c
23078--- ../netqmail-1.06-original/qmail-showctl.c   1998-06-15 12:53:16.000000000 +0200
23079+++ netqmail-1.06/qmail-showctl.c       2019-02-27 20:57:13.401024948 +0100
23080@@ -15,6 +15,7 @@
23081 #include "auto_patrn.h"
23082 #include "auto_spawn.h"
23083 #include "auto_split.h"
23084+#include "spf.h"
23085 
23086 stralloc me = {0};
23087 int meok;
23088@@ -112,7 +113,7 @@
23089   substdio_puts(subfdout,"\n");
23090   substdio_puts(subfdout,fn);
23091   substdio_puts(subfdout,": ");
23092-  switch(control_readfile(&line,fn)) {
23093+  switch(control_readfile(&line,fn, 0)) {
23094     case 0:
23095       substdio_puts(subfdout,"(Default.) ");
23096       substdio_puts(subfdout,def);
23097@@ -214,14 +215,22 @@
23098     _exit(111);
23099   }
23100 
23101-  do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM.");
23102+  do_lst("authsenders","No authenticated SMTP sender.","Authenicated SMTP sender: ","");
23103+  do_lst("badhelo","Any HELO host name is allowed.",""," HELO host name denied if it matches this pattern.");
23104+  do_lst("badhelonorelay","Any HELO host name is allowed.",""," HELO host name denied if it matches this pattern and RELAYCLIENT is not set.");
23105+  do_lst("badmailfrom","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern.");
23106+  do_lst("badmailfromnorelay","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern and RELAYCLIENT is not set.");
23107+  do_lst("badmailto","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern.");
23108+  do_lst("badmailtonorelay","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern and RELAYCLIENT is not set.");
23109   do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is ");
23110   do_str("bouncehost",1,"bouncehost","Bounce host name is ");
23111+  do_int("brtlimit","0","The brtlimit is ","");
23112   do_int("concurrencylocal","10","Local concurrency is ","");
23113   do_int("concurrencyremote","20","Remote concurrency is ","");
23114   do_int("databytes","0","SMTP DATA limit is "," bytes");
23115   do_str("defaultdomain",1,"defaultdomain","Default domain name is ");
23116   do_str("defaulthost",1,"defaulthost","Default host name is ");
23117+  do_lst("dnsbllist","No dnsbl list configured.","List at "," configured for dnsbl check.");
23118   do_str("doublebouncehost",1,"doublebouncehost","2B recipient host: ");
23119   do_str("doublebounceto",0,"postmaster","2B recipient user: ");
23120   do_str("envnoathost",1,"envnoathost","Presumed domain name is ");
23121@@ -230,6 +239,9 @@
23122   do_str("localiphost",1,"localiphost","Local IP address becomes ");
23123   do_lst("locals","Messages for me are delivered locally.","Messages for "," are delivered locally.");
23124   do_str("me",0,"undefined! Uh-oh","My name is ");
23125+  do_lst("moreipme","No additional IP addresses are me.","IP address "," is me.");
23126+  do_lst("notipme","All of my IP addresses are me.","IP address "," is not me.");
23127+  do_str("outgoingip",0,"0.0.0.0","Outgoing IP address is ");
23128   do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@",".");
23129   do_str("plusdomain",1,"plusdomain","Plus domain name is ");
23130   do_lst("qmqpservers","No QMQP servers.","QMQP server: ",".");
23131@@ -257,6 +269,15 @@
23132 
23133   do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 ");
23134   do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ","");
23135+  do_int("spfbehavior","0","The SPF behavior is ","");
23136+  do_str("spfexp",0,SPF_DEFEXP,"The SPF default explanation is: 550 ");
23137+  do_str("spfguess",0,"","The guess SPF rules are: ");
23138+  do_str("spfrules",0,"","The local SPF rules are: ");
23139+  do_str("srs_domain",0,"","SRS domain name is ");
23140+  do_lst("srs_secrets","No secrets","","");
23141+  do_int("srs_maxage","21","SRS maxage is ","");
23142+  do_int("srs_hashlength","4","SRS hashlength is ","");
23143+  do_int("srs_hashmin","4","SRS hashmin is ","");
23144   do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds");
23145   do_int("timeoutremote","1200","SMTP client data timeout is "," seconds");
23146   do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds");
23147@@ -265,9 +286,12 @@
23148   while (d = readdir(dir)) {
23149     if (str_equal(d->d_name,".")) continue;
23150     if (str_equal(d->d_name,"..")) continue;
23151-    if (str_equal(d->d_name,"bouncefrom")) continue;
23152-    if (str_equal(d->d_name,"bouncehost")) continue;
23153+    if (str_equal(d->d_name,"authsenders")) continue;
23154     if (str_equal(d->d_name,"badmailfrom")) continue;
23155+    if (str_equal(d->d_name,"badhelo")) continue;
23156+    if (str_equal(d->d_name,"badmailfromnorelay")) continue;
23157+    if (str_equal(d->d_name,"badmailto")) continue;
23158+    if (str_equal(d->d_name,"badmailtonorelay")) continue;
23159     if (str_equal(d->d_name,"bouncefrom")) continue;
23160     if (str_equal(d->d_name,"bouncehost")) continue;
23161     if (str_equal(d->d_name,"concurrencylocal")) continue;
23162@@ -275,6 +299,7 @@
23163     if (str_equal(d->d_name,"databytes")) continue;
23164     if (str_equal(d->d_name,"defaultdomain")) continue;
23165     if (str_equal(d->d_name,"defaulthost")) continue;
23166+    if (str_equal(d->d_name,"dnsbllist")) continue;
23167     if (str_equal(d->d_name,"doublebouncehost")) continue;
23168     if (str_equal(d->d_name,"doublebounceto")) continue;
23169     if (str_equal(d->d_name,"envnoathost")) continue;
23170@@ -283,8 +308,10 @@
23171     if (str_equal(d->d_name,"localiphost")) continue;
23172     if (str_equal(d->d_name,"locals")) continue;
23173     if (str_equal(d->d_name,"me")) continue;
23174+    if (str_equal(d->d_name,"moreipme")) continue;
23175     if (str_equal(d->d_name,"morercpthosts")) continue;
23176     if (str_equal(d->d_name,"morercpthosts.cdb")) continue;
23177+    if (str_equal(d->d_name,"notipme")) continue;
23178     if (str_equal(d->d_name,"percenthack")) continue;
23179     if (str_equal(d->d_name,"plusdomain")) continue;
23180     if (str_equal(d->d_name,"qmqpservers")) continue;
23181@@ -292,6 +319,15 @@
23182     if (str_equal(d->d_name,"rcpthosts")) continue;
23183     if (str_equal(d->d_name,"smtpgreeting")) continue;
23184     if (str_equal(d->d_name,"smtproutes")) continue;
23185+    if (str_equal(d->d_name,"spfbehavior")) continue;
23186+    if (str_equal(d->d_name,"spfexp")) continue;
23187+    if (str_equal(d->d_name,"spfguess")) continue;
23188+    if (str_equal(d->d_name,"spfrules")) continue;
23189+    if (str_equal(d->d_name,"srs_domain")) continue;
23190+    if (str_equal(d->d_name,"srs_secrets")) continue;
23191+    if (str_equal(d->d_name,"srs_maxage")) continue;
23192+    if (str_equal(d->d_name,"srs_hashlength")) continue;
23193+    if (str_equal(d->d_name,"srs_hashmin")) continue;
23194     if (str_equal(d->d_name,"timeoutconnect")) continue;
23195     if (str_equal(d->d_name,"timeoutremote")) continue;
23196     if (str_equal(d->d_name,"timeoutsmtpd")) continue;
23197diff -ruN ../netqmail-1.06-original/qmail-smtpd.8 netqmail-1.06/qmail-smtpd.8
23198--- ../netqmail-1.06-original/qmail-smtpd.8     1998-06-15 12:53:16.000000000 +0200
23199+++ netqmail-1.06/qmail-smtpd.8 2019-02-27 20:57:13.402024937 +0100
23200@@ -14,6 +14,15 @@
23201 see
23202 .BR tcp-environ(5) .
23203 
23204+If the environment variable
23205+.B SMTPS
23206+is non-empty,
23207+.B qmail-smtpd
23208+starts a TLS session (to support the deprecated SMTPS protocol,
23209+normally on port 465). Otherwise,
23210+.B qmail-smtpd
23211+offers the STARTTLS extension to ESMTP.
23212+
23213 .B qmail-smtpd
23214 is responsible for counting hops.
23215 It rejects any message with 100 or more
23216@@ -23,7 +32,30 @@
23217 header fields.
23218 
23219 .B qmail-smtpd
23220-supports ESMTP, including the 8BITMIME and PIPELINING options.
23221+supports ESMTP, including the 8BITMIME, DATA, PIPELINING, SIZE, and AUTH options.
23222+.B qmail-smtpd
23223+includes a \'MAIL FROM:\' parameter parser and obeys \'Auth\' and \'Size\' advertisements.
23224+.B qmail-smtpd
23225+can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types. It invokes
23226+.IR checkprogram ,
23227+which reads on file descriptor 3 the username, a 0 byte, the password
23228+or CRAM-MD5 digest/response derived from the SMTP client,
23229+another 0 byte, a CRAM-MD5 challenge (if applicable to the AUTH type),
23230+and a final 0 byte.
23231+.I checkprogram
23232+invokes
23233+.I subprogram
23234+upon successful authentication, which should in turn return 0 to
23235+.BR qmail-smtpd ,
23236+effectively setting the environment variables $RELAYCLIENT and $TCPREMOTEINFO
23237+(any supplied value replaced with the authenticated username).
23238+.B qmail-smtpd
23239+will reject the authentication attempt if it receives a nonzero return
23240+value from
23241+.I checkprogram
23242+or
23243+.IR subprogram .
23244+
23245 .SH TRANSPARENCY
23246 .B qmail-smtpd
23247 converts the SMTP newline convention into the UNIX newline convention
23248@@ -37,11 +69,34 @@
23249 even though such messages violate the SMTP protocol.
23250 .SH "CONTROL FILES"
23251 .TP 5
23252+.I badhelo
23253+Unacceptable HELO/EHLO host names.
23254+.B qmail-smtpd
23255+will reject every recipient address for a message if
23256+the host name is listed in,
23257+or matches a POSIX regular expression pattern listed in,
23258+.IR badhelo .
23259+If the
23260+.B NOBADHELO
23261+environment variable is set, then the contents of
23262+.IR badhelo
23263+will be ignored.
23264+For more information, please have a look at doc/README.qregex.
23265+.TP 5
23266+.I badhelonorelay
23267+Functions the same as the
23268+.IR badhelo
23269+control file but is read only if the
23270+.B RELAYCLIENT
23271+environment variable is not set.
23272+For more information, please have a look at doc/README.qregex.
23273+.TP 5
23274 .I badmailfrom
23275 Unacceptable envelope sender addresses.
23276 .B qmail-smtpd
23277 will reject every recipient address for a message
23278-if the envelope sender address is listed in
23279+if the envelope sender address is listed in, , or matches a POSIX regular expression
23280+pattern listed in,
23281 .IR badmailfrom .
23282 A line in
23283 .I badmailfrom
23284@@ -49,6 +104,61 @@
23285 .BR @\fIhost ,
23286 meaning every address at
23287 .IR host .
23288+For more information, please have a look at doc/README.qregex.
23289+.TP 5
23290+.I badmailfromnorelay
23291+Functions the same as the
23292+.IR badmailfrom
23293+control file but is read only if the
23294+.B RELAYCLIENT
23295+environment variable is not set.
23296+For more information, please have a look at doc/README.qregex.
23297+.TP 5
23298+.I badmailtonorelay
23299+Functions the same as the
23300+.IR badmailto
23301+control file but is read only if the
23302+.B RELAYCLIENT
23303+environment variable is not set.
23304+For more information, please have a look at doc/README.qregex.
23305+.TP 5
23306+.I badrcptto
23307+Unacceptable envelope recipient addresses.
23308+.B qmail-smtpd
23309+will reject every recipient address for a message if the recipient address
23310+is listed in,
23311+or matches a POSIX regular expression pattern listed in,
23312+.IR badrcptto .
23313+For more information, please have a look at doc/README.qregex.
23314+
23315+.TP 5
23316+.I brtlimit
23317+Number of bad recipients before closing the transmission channel.
23318+.B qmail-smtpd
23319+will close the transmission channel after
23320+reaching the number of bad recipients in
23321+.IR brtlimit .
23322+Both badrcptto, chkuser and validrcptto failures are counted.
23323+
23324+If the environment variable
23325+.B BRTLIMIT
23326+is set, it overrides
23327+.IR brtlimit .
23328+
23329+Default and minimum: 0.
23330+
23331+.TP 5
23332+.I clientca.pem
23333+A list of Certifying Authority (CA) certificates that are used to verify
23334+the client-presented certificates during a TLS-encrypted session.
23335+
23336+.TP 5
23337+.I clientcrl.pem
23338+A list of Certificate Revocation Lists (CRLs). If present it
23339+should contain the CRLs of the CAs in
23340+.I clientca.pem
23341+and client certs will be checked for revocation.
23342+
23343 .TP 5
23344 .I databytes
23345 Maximum number of bytes allowed in a message,
23346@@ -76,6 +186,34 @@
23347 .B DATABYTES
23348 is set, it overrides
23349 .IR databytes .
23350+
23351+.TP 5
23352+.I dh2048.pem
23353+If these 2048 bit DH parameters are provided,
23354+.B qmail-smtpd
23355+will use them for TLS sessions instead of generating one on-the-fly
23356+(which is very timeconsuming).
23357+
23358+.TP 5
23359+.I dnsbllist
23360+A list of dnsbl providers that
23361+.B qmail-smtpd
23362+checks to identify blacklisted ip addresses.
23363+
23364+Exception:
23365+If the environment variable
23366+.B DNSBLSKIP
23367+is set,
23368+.B qmail-smtpd
23369+ignores
23370+.IR dnsbllist ,
23371+and the dnsbl check is not performed.
23372+The check is skipped even if some other authentication method succedeed
23373+and authorized the client to relay (smtp-auth or tls client certificate),
23374+or if
23375+.B RELAYCLIENT
23376+enviromnent variable is set.
23377+
23378 .TP 5
23379 .I localiphost
23380 Replacement host name for local IP addresses.
23381@@ -151,6 +289,19 @@
23382 
23383 Envelope recipient addresses without @ signs are
23384 always allowed through.
23385+
23386+.TP 5
23387+.I rsa2048.pem
23388+If this 2048 bit RSA key is provided,
23389+.B qmail-smtpd
23390+will use it for TLS sessions instead of generating one on-the-fly.
23391+
23392+.TP 5
23393+.I servercert.pem
23394+SSL certificate to be presented to clients in TLS-encrypted sessions.
23395+Should contain both the certificate and the private key. Certifying Authority
23396+(CA) and intermediate certificates can be added at the end of the file.
23397+
23398 .TP 5
23399 .I smtpgreeting
23400 SMTP greeting message.
23401@@ -169,6 +320,119 @@
23402 .B qmail-smtpd
23403 will wait for each new buffer of data from the remote SMTP client.
23404 Default: 1200.
23405+
23406+.SH "ENVIRONMENT VARIABLES READ"
23407+Environment variables may be defined globally in the
23408+.B qmail-smtpd
23409+startup script and/or individually as part of the
23410+.B tcpserver's
23411+cdb database.
23412+The environment variables may be quoted ("variable", or 'variable') and
23413+in case of global use, have to be exported.
23414+.B qmail-smtpd
23415+supports the following legacy environment variables, typically
23416+provided by
23417+.B tcpserver
23418+or
23419+.B sslserver
23420+or
23421+.BR tcp-env :
23422+.IR TCPREMOTEIP ,
23423+.IR TCPREMOTEHOST
23424+.IR TCPREMOTEINFO
23425+and
23426+.IR TCPLOCALPORT
23427+as well as
23428+.IR RELAYCLIENT .
23429+
23430+.B qmail-smtpd
23431+may use the following environment variables for SMTP authentication:
23432+.TP 5
23433+.IR SMTPAUTH
23434+is used to enable SMTP Authentication for the AUTH types
23435+LOGIN and PLAIN.
23436+In case
23437+.TP 5
23438+.IR SMTPAUTH='+cram'
23439+is defined,
23440+.B qmail-smtpd
23441+honors LOGIN, PLAIN, and additionally CRAM-MD5 authentication.
23442+Simply
23443+.TP 5
23444+.IR SMTPAUTH='cram'
23445+restricts authentication just to CRAM-MD5.
23446+If however
23447+.TP 5
23448+.IR SMTPAUTH='!'
23449+starts with an exclamation mark, AUTH is required.
23450+You can enforce 'Submission' using this option
23451+and binding
23452+.B qmail-smtpd
23453+to the SUBMISSION port \'587'\.
23454+In particular,
23455+.TP 5
23456+.IR SMTPAUTH='!cram'
23457+may be useful.
23458+In opposite, if
23459+.TP 5
23460+.IR SMTPAUTH='-'
23461+starts with a dash, AUTH is disabled for particular
23462+connections.
23463+
23464+Note: The use of 'cram' requires a CRAM-MD5 enabled PAM.
23465+
23466+.TP 5
23467+.I tlsclients
23468+A list of email addresses. When relay rules would reject an incoming message,
23469+.B qmail-smtpd
23470+can allow it if the client presents a certificate that can be verified against
23471+the CA list in
23472+.I clientca.pem
23473+and the certificate email address is in
23474+.IR tlsclients .
23475+
23476+.TP 5
23477+.I tlsserverciphers
23478+A set of OpenSSL cipher strings. Multiple ciphers contained in a
23479+string should be separated by a colon. If the environment variable
23480+.B TLSCIPHERS
23481+is set to such a string, it takes precedence.
23482+
23483+.TP 5
23484+.I spfbehavior
23485+Set to a value between 1 and 6 to enable SPF checks; 0 to disable.
23486+1 selects 'annotate-only' mode, where
23487+.B qmail-smtpd
23488+will annotate incoming email with
23489+.B Received-SPF
23490+fields, but will not reject any messages.  2 will produce temporary
23491+failures on DNS lookup problems so you can make sure you always have
23492+meaningful Received-SPF headers.  3 selects 'reject' mode,
23493+where incoming mail will be rejected if the SPF record says 'fail'.  4
23494+selects a more stricter rejection mode, which is like 'reject' mode,
23495+except that incoming mail will also be rejected when the SPF record
23496+says 'softfail'.  5 will also reject when the SPF record says 'neutral',
23497+and 6 if no SPF records are available at all (or a syntax error was
23498+encountered). The contents of this file are overridden by the value of
23499+the
23500+.B SPFBEHAVIOR
23501+environment variable, if set.
23502+Default: 0.
23503+.TP 5
23504+.I spfexp
23505+You can add a line with a an SPF explanation that will be shown to the
23506+sender in case of a reject. It will override the default one. You can
23507+use SPF macro expansion.
23508+.TP 5
23509+.I spfguess
23510+You can add a line with SPF rules that will be checked if a sender
23511+domain doesn't have a SPF record. The local rules will also be used
23512+in this case.
23513+.TP 5
23514+.I spfrules
23515+You can add a line with SPF rules that will be checked before other SPF
23516+rules would fail.  This can be used to always allow certain machines to
23517+send certain mails.
23518 .SH "SEE ALSO"
23519 tcp-env(1),
23520 tcp-environ(5),
23521diff -ruN ../netqmail-1.06-original/qmail-smtpd.c netqmail-1.06/qmail-smtpd.c
23522--- ../netqmail-1.06-original/qmail-smtpd.c     2007-11-30 21:22:54.000000000 +0100
23523+++ netqmail-1.06/qmail-smtpd.c 2020-04-25 15:15:28.957053097 +0200
23524@@ -12,6 +12,9 @@
23525 #include "ip.h"
23526 #include "qmail.h"
23527 #include "str.h"
23528+#include "strerr.h"
23529+#include "qregex.h"
23530+#include "cdb.h"
23531 #include "fmt.h"
23532 #include "scan.h"
23533 #include "byte.h"
23534@@ -23,44 +26,180 @@
23535 #include "timeoutread.h"
23536 #include "timeoutwrite.h"
23537 #include "commands.h"
23538+#include "dns.h"
23539+#include "wait.h"
23540+
23541+/* chkuser.h will check if TLS_H is defined, so this has to come before chkuser.h */
23542+#ifdef TLS
23543+#include <sys/stat.h>
23544+#include "tls.h"
23545+#include "ssl_timeoutio.h"
23546+void tls_init();
23547+int tls_verify();
23548+void tls_nogateway();
23549+int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */
23550+int forcetls = 1;
23551+#endif
23552+
23553+/* start chkuser code */
23554+#include "chkuser.h"
23555+/* end chkuser code */
23556+#include "spf.h"
23557+/* rbl: start */
23558+#include "strsalloc.h"
23559+/* rbl: end */
23560+
23561+#define AUTHSLEEP 5
23562 
23563 #define MAXHOPS 100
23564+
23565+#define BMCHECK_BMF 0
23566+#define BMCHECK_BMFNR 1
23567+#define BMCHECK_BMT 2
23568+#define BMCHECK_BMTNR 3
23569+#define BMCHECK_BHELO 4
23570+#define BMCHECK_BHELONR 5
23571+
23572+static char strnum[FMT_ULONG];
23573 unsigned int databytes = 0;
23574+unsigned int greetdelay = 0;
23575+unsigned int drop_pre_greet = 0;
23576 int timeout = 1200;
23577+int maxrcpt = -1;
23578+unsigned int spfbehavior = 0;
23579+
23580+/* rejectrelaytest: start */
23581+unsigned int rejectrelaytest = 0;
23582+/* rejecrelayttest: end */
23583+/* rejectnullsenders: start */
23584+unsigned int rejnsmf = 0;
23585+/* rejectnullsenders: end */
23586+
23587+static const char *protocol = "SMTP";
23588+
23589+/* spf ipv6 fix */
23590+char *remoteip4;
23591+/* end spf ipv6 fix */
23592 
23593 int safewrite(fd,buf,len) int fd; char *buf; int len;
23594 {
23595   int r;
23596+#ifdef TLS
23597+  if (ssl && fd == ssl_wfd)
23598+    r = ssl_timeoutwrite(timeout, ssl_rfd, ssl_wfd, ssl, buf, len);
23599+  else
23600+#endif
23601   r = timeoutwrite(timeout,fd,buf,len);
23602   if (r <= 0) _exit(1);
23603   return r;
23604 }
23605 
23606 char ssoutbuf[512];
23607+char sslogbuf[512];
23608 substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
23609+substdio sslog = SUBSTDIO_FDBUF(safewrite,2,sslogbuf,sizeof sslogbuf);
23610+char sserrbuf[512];
23611+substdio sserr = SUBSTDIO_FDBUF(safewrite,2,sserrbuf,sizeof sserrbuf);
23612+
23613+int addrinrcpthosts = 0;
23614+int envelopepos = 0; // 1: ehlo/helo, 2: mailfrom, 3: rcptto: 4: data
23615+void qsmtpdlog(const char *head, const char *result, const char *reason, const char *detail, const char *statuscode);
23616+void qlogenvelope(char *result, char *reason, char *detail, char *statuscode) { qsmtpdlog("qlogenvelope",result,reason,detail,statuscode); }
23617+void qlogreceived(char *result, char *reason, char *detail, char *statuscode) { qsmtpdlog("qlogreceived",result,reason,detail,statuscode); }
23618 
23619+void logit(const char* message);
23620+void logit2(const char* message, const char* reason);
23621 void flush() { substdio_flush(&ssout); }
23622 void out(s) char *s; { substdio_puts(&ssout,s); }
23623 
23624-void die_read() { _exit(1); }
23625-void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
23626-void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
23627-void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
23628-void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
23629-void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
23630+void die_read(char *reason) { logit2("read failed", reason); flush(); _exit(1); }
23631+void die_alarm() { qlogenvelope("rejected","alarmtimeout","","451"); logit("timeout"); out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
23632+void die_nomem() { qlogenvelope("rejected","outofmemory","","421"); out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
23633+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); }
23634+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); }
23635+/* rbl: start */
23636+/*
23637+void die_dnsbl(arg)
23638+char *arg;
23639+{
23640+  out("421 your ip is currently blacklisted, try to auth first ("); out(arg); out(")\r\n");
23641+  logit2("message rejected (qmail-dnsbl)", arg);
23642+  flush();
23643+  _exit(1);
23644+}
23645+*/
23646+/* rbl: end */
23647+void err_maxrcpt()
23648+{
23649+  out("553 max rcpt limit exceeded (#5.7.1)\r\n");
23650+  logit("max rcpt limit exceeded (qmail-maxrcpt)");
23651+  qlogenvelope("rejected","maxrcpt","","553");
23652+  flush();
23653+}
23654+void straynewline() { qlogenvelope("rejected","badnewlines","","451"); logit("bad newlines"); out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
23655+void die_pre_greet() { qlogenvelope("rejected","pregreet","","554"); out("554 SMTP protocol violation\r\n"); flush(); _exit(1); }
23656 
23657-void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
23658-void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
23659+void err_size() { qlogreceived("rejected","size","","552"); out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); }
23660+#ifndef TLS
23661+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"); }
23662+#else
23663+void err_nogateway()
23664+{
23665+  qlogenvelope("rejected","notinrcpthosts","","553"); out("553 sorry, that domain isn't in my list of allowed rcpthosts");
23666+  tls_nogateway();
23667+  out(" (#5.7.1)\r\n");
23668+}
23669+#endif
23670 void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); }
23671+void err_unrecog() { out("500 unrecognised (#5.5.2)\r\n"); }
23672 void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
23673 void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
23674 void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
23675 void err_noop(arg) char *arg; { out("250 ok\r\n"); }
23676 void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); }
23677-void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
23678+void err_qqt() { qlogenvelope("rejected","qqtfailure","","451"); out("451 qqt failure (#4.3.0)\r\n"); }
23679 
23680+int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; }
23681+int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; }
23682+int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; }
23683+int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; }
23684+void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); }
23685+void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); }
23686+int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; }
23687+int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; }
23688+int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }
23689+void err_authfail() { qlogenvelope("rejected","authfailed","","535"); out("535 authentication failed (#5.7.1)\r\n"); }
23690+void err_submission() { qlogenvelope("rejected","authrequired","","530"); out("530 Authorization required (#5.7.1) \r\n"); }
23691+void err_vrt() { qlogenvelope("rejected","validrcptto","","553"); out("553 sorry, this recipient is not in my validrcptto list (#5.7.1)\r\n"); }
23692+void die_brtlimit() { qlogenvelope("rejected","brtlimit","","421"); out("421 too many invalid addresses, goodbye (#4.3.0)\r\n"); flush(); _exit(1); }
23693+void err_rcpt() { qlogenvelope("rejected","nomailbox","","550"); out("550 sorry, no mailbox here by that name (#5.1.1)\r\n"); }
23694+/* rcptcheck: start */
23695+void die_fork() { qlogenvelope("rejected","rcptcheck","cannotfork","421"); out("421 unable to fork (#4.3.0)\r\n"); flush(); _exit(1); }
23696+void die_rcpt() { qlogenvelope("rejected","rcptcheck","cannotverify","421"); out("421 unable to verify recipient (#4.3.0)\r\n"); flush(); _exit(1); }
23697+void die_rcpt2() { qlogenvelope("rejected","rcptcheck","cannotexecute","421"); out("421 unable to execute recipient check (#4.3.0)\r\n"); flush(); _exit(1); }
23698+/* rcptcheck: end */
23699+/* qregex: start */
23700+/*
23701+void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
23702+*/
23703+void err_bmf() { out("553 sorry, your envelope sender has been denied (#5.7.1)\r\n"); }
23704+void err_bmt() { out("553 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); }
23705+void err_bhelo() { out("553 sorry, your HELO host name has been denied (#5.7.1)\r\n"); }
23706+/* qregex: end */
23707+/* rejectnullsenders: start */
23708+void die_nullsender() { qlogenvelope("rejected","nullsenderdenied","","421"); out("421 null senders temporarily denied (#4.3.0)\r\n"); flush(); _exit(1); }
23709+/* rejectnullsenders: end */
23710+/* rejectrelaytest: start */
23711+void err_relay() { qlogenvelope("rejected","dontrelay","","553"); out("553 we don't relay (#5.7.1)\r\n"); }
23712+/* rejectrelaytest: end */
23713+/* authtlsvariables: start */
23714+void err_authmismatch() { qlogenvelope("rejected","authnotmailfrom","","503"); out("503 from and auth not the same (#5.5.1)\r\n"); }
23715+/* authtlsvariables: end */
23716 
23717 stralloc greeting = {0};
23718+stralloc spflocal = {0};
23719+stralloc spfguess = {0};
23720+stralloc spfexp = {0};
23721 
23722 void smtp_greet(code) char *code;
23723 {
23724@@ -76,11 +215,33 @@
23725   smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
23726 }
23727 
23728+/* char *protocol; */
23729 char *remoteip;
23730 char *remotehost;
23731 char *remoteinfo;
23732 char *local;
23733+char *localport;
23734+char *submission;
23735 char *relayclient;
23736+char *dnsblskip;
23737+char *auth;
23738+/* authtlsvariables: start */
23739+int flagtls = 0;
23740+int forceauthmailfrom = 0;
23741+int disabletls = 0;
23742+/* authtlsvariables: end */
23743+
23744+char unique[FMT_ULONG + FMT_ULONG + 3];
23745+static stralloc authin = {0};   /* input from SMTP client */
23746+static stralloc user = {0};     /* authorization user-id */
23747+static stralloc pass = {0};     /* plain passwd or digest */
23748+static stralloc resp = {0};     /* b64 response */
23749+static stralloc chal = {0};     /* plain challenge */
23750+static stralloc slop = {0};     /* b64 challenge */
23751+
23752+char **childargs;
23753+char ssauthbuf[512];
23754+substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf));
23755 
23756 stralloc helohost = {0};
23757 char *fakehelo; /* pointer into helohost, or 0 */
23758@@ -91,11 +252,101 @@
23759   fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
23760 }
23761 
23762+int smtpauth = 0;
23763 int liphostok = 0;
23764 stralloc liphost = {0};
23765 int bmfok = 0;
23766 stralloc bmf = {0};
23767-struct constmap mapbmf;
23768+
23769+/* rbl: start */
23770+int flagrbldns = 0;
23771+int flagrbldelay = 0;
23772+int flagrblfailclosed = 0;
23773+int flagmustnotbounce = 0;
23774+static stralloc rbldnslist = {0};
23775+static stralloc rblhost = {0};
23776+int rblhosterror = 0;
23777+int rbllistok = 0;
23778+int rblok = 0;
23779+char *ip_env;
23780+static stralloc ip_reverse;
23781+int rbldecision = 0; /* 0 undecided, 1 accept, 2 reject (451), 3 bounce (553) */
23782+static stralloc rbltext = {0}; /* defined if rbldecision is 2 or 3 */
23783+static stralloc rblmessage = {0};
23784+static stralloc rblserver = {0};
23785+
23786+void err_rblreject() {
23787+  if (env_get("RBLSMTPD")) {
23788+    qlogenvelope("rejected","rblreject","rblsmtpd","553");
23789+  }
23790+  else {
23791+    if (rblserver.len) qlogenvelope("rejected","rblreject",rblserver.s,"553");
23792+    else qlogenvelope("rejected","rblreject","","553");
23793+  }
23794+  substdio_put(&ssout,rblmessage.s,rblmessage.len);
23795+  flush();
23796+}
23797+
23798+void die_rbldelay() {
23799+  if (env_get("RBLSMTPD")) {
23800+    qlogenvelope("rejected","rbldelay","rblsmtpd","451");
23801+  }
23802+  else {
23803+    if (rblserver.len) qlogenvelope("rejected","rbldelay",rblserver.s,"451");
23804+    else qlogenvelope("rejected","rbldelay","","451");
23805+  }
23806+  substdio_put(&ssout,rblmessage.s,rblmessage.len); flush();
23807+  _exit(1);
23808+}
23809+/* rbl: end */
23810+
23811+/* qregex: start */
23812+/*
23813+ struct constmap mapbmf;
23814+*/
23815+int bmfnrok = 0;
23816+stralloc bmfnr = {0};
23817+
23818+int bmtok = 0;
23819+stralloc bmt = {0};
23820+
23821+int bmtnrok = 0;
23822+stralloc bmtnr = {0};
23823+
23824+int bhelook = 0;
23825+stralloc bhelo = {0};
23826+
23827+int bhelonrok = 0;
23828+stralloc bhelonr = {0};
23829+
23830+int logregex = 0;
23831+stralloc matchedregex = {0};
23832+/* qregex: end */
23833+
23834+/* validrcptto.cdb: start */
23835+int vrtok = 0;
23836+stralloc vrt = {0};
23837+struct constmap mapvrt;
23838+
23839+int vrtfd = -1;
23840+int vrtcount = 0;
23841+int vrtlog_do = 0;
23842+
23843+stralloc title = {0};
23844+char pid_buf[FMT_ULONG];
23845+/* validrcptto.cdb: end */
23846+
23847+/* realbadrcpt: start */
23848+int brtlimit = 0;
23849+static char strnumpid[FMT_ULONG];
23850+static char strnumqp[FMT_ULONG];
23851+/* realbadrcpt: end */
23852+
23853+/* rcptcheck: start */
23854+static char *rcptcheck[2] = { 0, 0 };
23855+char rcptcheck_err[1024];
23856+int rcptcheckrelayclient = 0;
23857+/* rcptcheck: end */
23858 
23859 void setup()
23860 {
23861@@ -109,21 +360,110 @@
23862   if (liphostok == -1) die_control();
23863   if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
23864   if (timeout <= 0) timeout = 1;
23865+  if (control_readint(&maxrcpt,"control/maxrcpt") == -1) die_control();
23866 
23867   if (rcpthosts_init() == -1) die_control();
23868 
23869   bmfok = control_readfile(&bmf,"control/badmailfrom",0);
23870   if (bmfok == -1) die_control();
23871+/* qregex: start */
23872+/*
23873   if (bmfok)
23874     if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
23875+*/
23876+  strnumpid[fmt_uint(strnumpid,(unsigned int) getpid())] = 0;
23877+
23878+  bmfnrok = control_readfile(&bmfnr,"control/badmailfromnorelay",0);
23879+  if (bmfnrok == -1) die_control();
23880+
23881+  bmtok = control_readfile(&bmt,"control/badrcptto",0);
23882+  if (bmtok == -1) die_control();
23883+
23884+  bmtnrok = control_readfile(&bmtnr,"control/badrcpttonorelay",0);
23885+  if (bmtnrok == -1) die_control();
23886+
23887+  bhelook = control_readfile(&bhelo, "control/badhelo",0);
23888+  if (bhelook == -1) die_control();
23889+
23890+  bhelonrok = control_readfile(&bhelonr, "control/badhelonorelay",0);
23891+  if (bhelonrok == -1) die_control();
23892+
23893+  if (env_get("LOGREGEX")) logregex = 1;
23894+/* qregex: end */
23895+
23896+/* validrcptto.cdb: start */
23897+  x = env_get("VALIDRCPTTO");
23898+  if (x) { vrtok = control_readfile(&vrt,x,0); }
23899+  else vrtok = control_readfile(&vrt,"control/validrcptto",0);
23900+  if (vrtok == -1) die_control();
23901+  if (vrtok)
23902+  if (!constmap_init(&mapvrt,vrt.s,vrt.len,0)) die_nomem();
23903+
23904+  x = env_get("MOREVALIDRCPTTO_CDB");
23905+  if (x) { vrtfd = open_read(x); }
23906+  else vrtfd = open_read("control/morevalidrcptto.cdb");
23907+  if (-1 == vrtfd) if (errno != error_noent) die_control();
23908+
23909+  x = env_get("VALIDRCPTTO_LOG");
23910+  if(x) { scan_ulong(x,&u); vrtlog_do = (int) u; }
23911+/* validrcptto.cdb: end */
23912+
23913+/* realbadrcpt: start */
23914+  if (control_readint(&brtlimit,"control/brtlimit") == -1) die_control();
23915+  x = env_get("BRTLIMIT");
23916+  if (x) { scan_ulong(x,&u); brtlimit = u; };
23917+  /* Disable limits check, defaults to 0 */
23918+/*  if (brtlimit <= 1) brtlimit = 2; */
23919+/* realbadrcpt: end */
23920+
23921+/* rcptcheck: start */
23922+  rcptcheck[0] = env_get("RCPTCHECK");
23923+
23924+  x = env_get("RCPTCHECKRELAYCLIENT");
23925+  if (x) { scan_ulong(x,&u); rcptcheckrelayclient = u; };
23926+/* rcptcheck: end */
23927+
23928+/* rejectrelaytest: start */
23929+  if (control_readint(&rejectrelaytest,"control/rejectrelaytest") == -1) die_control();
23930+/* rejectrelaytest: end */
23931+
23932+/* rejectnullsenders: start */
23933+  if (control_readint(&rejnsmf,"control/rejectnullsenders") == -1) die_control();
23934+/* rejectnullsenders: end */
23935 
23936   if (control_readint(&databytes,"control/databytes") == -1) die_control();
23937   x = env_get("DATABYTES");
23938   if (x) { scan_ulong(x,&u); databytes = u; }
23939   if (!(databytes + 1)) --databytes;
23940-
23941+
23942+  x = env_get("SMTPD_GREETDELAY");
23943+  if (x) { scan_ulong(x, &u); greetdelay = u; }
23944+  x = env_get("DROP_PRE_GREET");
23945+  if (x) { scan_ulong(x, &u); drop_pre_greet = u; }
23946+
23947+  protocol = "SMTP";
23948+
23949+  if (control_readint(&spfbehavior,"control/spfbehavior") == -1)
23950+    die_control();
23951+  x = env_get("SPFBEHAVIOR");
23952+  if (x) { scan_ulong(x,&u); spfbehavior = u; }
23953+
23954+  if (control_readline(&spflocal,"control/spfrules") == -1) die_control();
23955+  if (spflocal.len && !stralloc_0(&spflocal)) die_nomem();
23956+  if (control_readline(&spfguess,"control/spfguess") == -1) die_control();
23957+  if (spfguess.len && !stralloc_0(&spfguess)) die_nomem();
23958+  if (control_rldef(&spfexp,"control/spfexp",0,SPF_DEFEXP) == -1)
23959+    die_control();
23960+  if (!stralloc_0(&spfexp)) die_nomem();
23961+
23962+  /* spf ipv6 fix */
23963+  if (!(remoteip4 = env_get("TCPREMOTEIP")))
23964+      remoteip4 = "unknown";
23965+  /* end spf ipv6 fix */
23966   remoteip = env_get("TCPREMOTEIP");
23967   if (!remoteip) remoteip = "unknown";
23968+  localport = env_get("TCPLOCALPORT");
23969+  if (!localport) localport = "0";
23970   local = env_get("TCPLOCALHOST");
23971   if (!local) local = env_get("TCPLOCALIP");
23972   if (!local) local = "unknown";
23973@@ -131,10 +471,67 @@
23974   if (!remotehost) remotehost = "unknown";
23975   remoteinfo = env_get("TCPREMOTEINFO");
23976   relayclient = env_get("RELAYCLIENT");
23977+  dnsblskip = env_get("DNSBLSKIP");
23978+/* rbl: start */
23979+  x = env_get("DNSBLLIST");
23980+  if (x) {
23981+    rbllistok = control_readfile(&rbldnslist,x,0);
23982+    if (rbllistok == -1) die_control();
23983+    if (rbllistok) rblok = 1;
23984+  }
23985+  else {
23986+    rbllistok = control_readfile(&rbldnslist,"control/dnsbllist",0);
23987+    if (rbllistok == -1) die_control();
23988+    if (rbllistok) rblok = 1;
23989+  }
23990+
23991+  /* from rblsmtpd.c, if RBLSMTPD is defined and empty then accept mail, if defined and string begins with '-' then
23992+     block mail using error code 553 + string without hyphen, else (if defined, not null and not beginning with '-')
23993+     reject mail using error code 451 + string  */
23994+  x = env_get("RBLSMTPD");
23995+  if (x) {
23996+    if (!*x)
23997+      rbldecision = 1;
23998+    else if (*x == '-') {
23999+      if (!stralloc_copys(&rbltext,x + 1)) die_nomem();
24000+      rbldecision = 3;
24001+    }
24002+    else {
24003+      if (!stralloc_copys(&rbltext,x)) die_nomem();
24004+      rbldecision = 2;
24005+    }
24006+    rblok = 1;
24007+  }
24008
24009+  if (control_readint(&flagrblfailclosed,"control/dnsblfailclosed") == -1) die_control();
24010+  x = env_get("DNSBLFAILCLOSED");
24011+  if (x) { scan_ulong(x,&u); flagrblfailclosed = u; }
24012+/* rbl: end */
24013+  auth = env_get("SMTPAUTH");
24014+  if (auth) {
24015+    smtpauth = 1;
24016+    case_lowers(auth);
24017+    if (!case_diffs(auth,"-") || !case_diffs(auth,"0")) smtpauth = 0;
24018+    if (!case_diffs(auth,"!")) smtpauth = 11;
24019+    if (case_starts(auth,"cram")) smtpauth = 2;
24020+    if (case_starts(auth,"+cram")) smtpauth = 3;
24021+    if (case_starts(auth,"!cram")) smtpauth = 12;
24022+    if (case_starts(auth,"!+cram")) smtpauth = 13;
24023+  }
24024+/* authtlsvariables: start */
24025+  x = env_get("FORCEAUTHMAILFROM"); if (x) if (!str_diff(x,"1")) { forceauthmailfrom = 1; }
24026+  #ifdef TLS
24027+  x = env_get("DISABLETLS"); if (x) if (!str_diff(x,"1")) { disabletls = 1; }
24028+  #endif
24029+/* authtlsvariables: end */
24030+  #ifdef TLS
24031+  x = env_get("FORCETLS"); if (x) if (!str_diff(x, "0")) forcetls = 0;
24032+  if (env_get("SMTPS")) { smtps = 1; tls_init(); }
24033+  else
24034+  #endif
24035   dohelo(remotehost);
24036 }
24037 
24038-
24039 stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
24040 
24041 int addrparse(arg)
24042@@ -155,6 +552,7 @@
24043     terminator = ' ';
24044     arg += str_chr(arg,':');
24045     if (*arg == ':') ++arg;
24046+    if (*arg == '\0') return 0;
24047     while (*arg == ' ') ++arg;
24048   }
24049 
24050@@ -197,6 +595,8 @@
24051   return 1;
24052 }
24053 
24054+/* qregex: start */
24055+/*
24056 int bmfcheck()
24057 {
24058   int j;
24059@@ -207,76 +607,1108 @@
24060     if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
24061   return 0;
24062 }
24063+*/
24064+int bmcheck(which) int which;
24065+{
24066+  int i = 0;
24067+  int j = 0;
24068+  int x = 0;
24069+  int negate = 0;
24070+  static stralloc bmb = {0};
24071+  static stralloc curregex = {0};
24072+
24073+  if (which == BMCHECK_BMF) {
24074+    if (!stralloc_copy(&bmb,&bmf)) die_nomem();
24075+  } else if (which == BMCHECK_BMFNR) {
24076+    if (!stralloc_copy(&bmb,&bmfnr)) die_nomem();
24077+  } else if (which == BMCHECK_BMT) {
24078+    if (!stralloc_copy(&bmb,&bmt)) die_nomem();
24079+  } else if (which == BMCHECK_BMTNR) {
24080+    if (!stralloc_copy(&bmb,&bmtnr)) die_nomem();
24081+  } else if (which == BMCHECK_BHELO) {
24082+    if (!stralloc_copy(&bmb,&bhelo)) die_nomem();
24083+  } else if (which == BMCHECK_BHELONR) {
24084+    if (!stralloc_copy(&bmb,&bhelonr)) die_nomem();
24085+  } else {
24086+    die_control();
24087+  }
24088+
24089+  while (j < bmb.len) {
24090+    i = j;
24091+    while ((bmb.s[i] != '\0') && (i < bmb.len)) i++;
24092+    if (bmb.s[j] == '!') {
24093+      negate = 1;
24094+      j++;
24095+    }
24096+    if (!stralloc_copyb(&curregex,bmb.s + j,(i - j))) die_nomem();
24097+    if (!stralloc_0(&curregex)) die_nomem();
24098+    if (which == BMCHECK_BHELO) {
24099+      x = matchregex(helohost.s, curregex.s);
24100+    } else {
24101+      x = matchregex(addr.s, curregex.s);
24102+    }
24103+    if ((negate) && (x == 0)) {
24104+      if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem();
24105+      if (!stralloc_0(&matchedregex)) die_nomem();
24106+      return 1;
24107+    }
24108+    if (!(negate) && (x > 0)) {
24109+      if (!stralloc_copyb(&matchedregex,bmb.s + j,(i - j))) die_nomem();
24110+      if (!stralloc_0(&matchedregex)) die_nomem();
24111+      return 1;
24112+    }
24113+    j = i + 1;
24114+    negate = 0;
24115+  }
24116+  return 0;
24117+}
24118+/* qregex: end */
24119+
24120+/* validrcptto.cdb: start */
24121+void vrtlog(l,a,b)
24122+int l;
24123+const char *a;
24124+const char *b;
24125+{
24126+/*  if (l <= vrtlog_do)
24127+    strerr_warn6(title.s,"validrcptto [",remoteip,"] ",a,b,0);*/
24128+}
24129+
24130+int vrtcheck()
24131+{
24132+  static char *rcptto = "RCPT TO: ";
24133+  static char *trying = "trying: ";
24134+  static char *found  = "found: ";
24135+  static char *reject = "reject: ";
24136+  char *f = 0;
24137+  int j,k,r;
24138+  uint32 dlen;
24139+  stralloc laddr = {0};
24140+
24141+  stralloc luser = {0};
24142+  stralloc adom = {0};
24143+  stralloc utry = {0};
24144+  stralloc stnoaddr = {0};
24145+  stralloc stnodom = {0};
24146+
24147+  int atfound, okaddr, okdom, noaddr, nodom;
24148+
24149+  /* if both validrcptto and morevalidrcptto.cdb are missing, consider valid the recipient */
24150+  if (!((vrtok) || (vrtfd != -1))) return 1;
24151+
24152+  okaddr = 0; okdom = 0; noaddr = 0; nodom = 0; atfound = 0;
24153+
24154+  /* lowercase whatever we were sent */
24155+  if (!stralloc_copy(&laddr,&addr)) die_nomem() ;
24156+  case_lowerb(laddr.s,laddr.len);
24157+
24158+  /* split user/domain parts, create negated stralloc */
24159+  j = byte_rchr(laddr.s,laddr.len,'@');
24160+  if (j < laddr.len) {
24161+    atfound = 1;
24162+    if (!stralloc_copyb(&luser,laddr.s,j)) die_nomem();
24163+    if (!stralloc_copyb(&adom,laddr.s+j,laddr.len-j-1)) die_nomem();
24164+
24165+    if (!stralloc_copys(&stnodom,"-")) die_nomem();
24166+    if (!stralloc_cat(&stnodom,&adom)) die_nomem();
24167+    if (!stralloc_0(&stnodom)) die_nomem();
24168+
24169+    if (!stralloc_copys(&stnoaddr,"-")) die_nomem();
24170+    if (!stralloc_cat(&stnoaddr,&luser)) die_nomem();
24171+    if (!stralloc_cat(&stnoaddr,&adom)) die_nomem();
24172+    if (!stralloc_0(&stnoaddr)) die_nomem();
24173+  }
24174+  /* validrcptto */
24175+  if (vrtok) {
24176+    vrtlog(rcptto,laddr.s,0);
24177+    if (constmap(&mapvrt,laddr.s,laddr.len - 1)) { okaddr = 1; vrtlog(found,laddr.s,0); }
24178+    if (atfound) {
24179+      if (constmap(&mapvrt,stnoaddr.s,stnoaddr.len-1)) { noaddr= 1; vrtlog(reject,stnoaddr.s,0); }
24180+      if (constmap(&mapvrt,laddr.s+j,laddr.len-j-1)) { okdom = 1; vrtlog(found,laddr.s+j,0); }
24181+      if (constmap(&mapvrt,stnodom.s,stnodom.len-1)) { nodom = 1; vrtlog(reject,stnodom.s,0); }
24182+    }
24183+  }
24184
24185+  /* morevalidrcptto.cdb */
24186+  if ((vrtfd != -1)) {
24187+    vrtlog(rcptto,laddr.s,0);
24188+
24189+    if (cdb_seek(vrtfd,laddr.s,laddr.len-1,&dlen) > 0) { okaddr = 1; vrtlog(found,laddr.s,0); }
24190+    if (atfound) {
24191+      if (cdb_seek(vrtfd,stnoaddr.s,stnoaddr.len-1,&dlen) > 0) { noaddr = 1; vrtlog(reject,stnoaddr.s,0); }
24192+      if (cdb_seek(vrtfd,laddr.s+j,laddr.len-j-1,&dlen) > 0) { okdom = 1; vrtlog(found,laddr.s+j,0); }
24193+      if (cdb_seek(vrtfd,stnodom.s,stnodom.len-1,&dlen) > 0) { nodom = 1; vrtlog(reject,stnodom.s,0); }
24194+    }
24195+  }
24196+
24197+  if (okaddr) return 1;
24198+  else if (noaddr) return -1;
24199+  else if (okdom) return 1;
24200+  else if (nodom) return -1;
24201+  else return 0;
24202+}
24203+/* validrcptto.cdb: end */
24204+
24205+/* rbl: start */
24206+void rbl(char *base)
24207+{
24208+  int i,j;
24209+  int whitelisted = 0;
24210+  int altmustbounce = 0;
24211+  char *altreply = 0;
24212+  strsalloc ssa = {0};
24213+
24214+  if (!str_len(base)) return;
24215+  if (!stralloc_copys(&rbltext,"")) die_nomem();
24216+  if (!stralloc_copys(&rblhost,"")) die_nomem();
24217+  if (!stralloc_copys(&rblserver,"")) die_nomem();
24218+
24219+  if (!stralloc_copy(&rblhost,&ip_reverse)) die_nomem();
24220+  i = str_chr(base, ':');
24221+  if (base[i]) {
24222+    if (base[i+1] == '-') { /* if reply begins with '-', message must bounce (check rblsmtpd man page) */
24223+      altreply = base+i+2;
24224+      altmustbounce = 1;
24225+    }
24226+    else altreply = base+i+1;
24227+  }
24228+
24229+  if (base[0] == '+') { /* entries beginning with '+' are for whitelistedlists */
24230+    whitelisted = 1;
24231+    if (!stralloc_catb(&rblhost,base+1,i-1)) die_nomem();
24232+    if (!stralloc_catb(&rblserver,base+1,i-1)) die_nomem();
24233+  }
24234+  else if (base[0] == '-') { /* force bounce (553 error message), instead of default reject (451) */
24235+    whitelisted = 0;
24236+    if (!stralloc_catb(&rblhost,base+1,i-1)) die_nomem();
24237+    if (!stralloc_catb(&rblserver,base+1,i-1)) die_nomem();
24238+    altmustbounce = 1;
24239+  }
24240+  else {
24241+    if (!stralloc_catb(&rblhost,base,i)) die_nomem();
24242+    if (!stralloc_catb(&rblserver,base,i)) die_nomem();
24243+  }
24244+  if (!stralloc_0(&rblhost)) die_nomem();
24245+  if (!stralloc_0(&rblserver)) die_nomem();
24246+
24247+  rblhosterror = 0; /* set in case of dns errors */
24248+
24249+  if (altreply) { /* if text response is defined in control file, query A records */
24250+    if (dns_ip(&rbltext,&rblhost) == -1) {
24251+      flagmustnotbounce = 1;
24252+      rblhosterror = 1;
24253+      if (flagrblfailclosed) {
24254+        if (!stralloc_copys(&rbltext,"temporary RBL lookup error")) die_nomem();
24255+        if (whitelisted) rbldecision = 1; else rbldecision = 2;
24256+      }
24257+      return;
24258+    }
24259+    if (rbltext.len) {
24260+      if(!stralloc_copys(&rbltext, "")) die_nomem();
24261+      while(*altreply) {
24262+        i = str_chr(altreply, '%');
24263+        if(!stralloc_catb(&rbltext, altreply, i)) die_nomem();
24264+        if(altreply[i] &&
24265+           altreply[i+1]=='I' &&
24266+           altreply[i+2]=='P' &&
24267+           altreply[i+3]=='%') {
24268+          if(!stralloc_catb(&rbltext, ip_env, str_len(ip_env))) die_nomem();
24269+          altreply+=i+4;
24270+        } else if(altreply[i]) {
24271+          if(!stralloc_cats(&rbltext, "%")) die_nomem();
24272+          altreply+=i+1;
24273+        } else {
24274+          altreply+=i;
24275+        }
24276+      }
24277+    }
24278+  } else { /* normal rbl query looks for TXT record */
24279+    if (dns_txt(&ssa,&rblhost) == -1) { /* DNS_SOFT = -1, DNS_HARD = -2, DNS_MEM = -3 */
24280+      flagmustnotbounce = 1;
24281+      rblhosterror = 1;
24282+      if (flagrblfailclosed) {
24283+        if (!stralloc_copys(&rbltext,"temporary RBL lookup error")) die_nomem();
24284+        if (whitelisted) rbldecision = 1; else rbldecision = 2;
24285+      }
24286+      return;
24287+    }
24288+    else {
24289+      /* in case of multiple records, take only the first */
24290+      if (ssa.len > 0)
24291+        if (!stralloc_cat(&rbltext,&ssa.sa[0])) die_nomem();
24292+      /* in case of multiple records, append results to rbltext */
24293+      /*for (j = 0;j < ssa.len;++j) if (!stralloc_cat(&rbltext,&ssa.sa[j])) die_nomem();*/
24294+    }
24295+  }
24296+  if (rbltext.len) {
24297+    if (whitelisted) {
24298+      rbldecision = 1;
24299+    }
24300+    else {
24301+      if (altmustbounce)
24302+        rbldecision = 3;
24303+      else
24304+        rbldecision = 2;
24305+    }
24306+  }
24307+  else rbldecision = 0;
24308+}
24309+
24310+int rblcheck()
24311+{
24312+  char *ch;
24313+  unsigned int i;
24314+  unsigned int j;
24315+  stralloc sar = {0};
24316+
24317+  if (rbldecision) return rbldecision; /* rbldecision already set in case of RBLSMTPD or if rblcheck was executed previously */
24318+  if (!rbllistok) return 0;
24319+
24320+  ip_env = env_get("TCPREMOTEIP");
24321+  if (!ip_env) ip_env = "";
24322+  if (!stralloc_copys(&ip_reverse,"")) die_nomem();
24323+
24324+  i = str_len(ip_env);
24325+  while (i) {
24326+    for (j = i;j > 0;--j) if (ip_env[j - 1] == '.') break;
24327+    if (!stralloc_catb(&ip_reverse,ip_env + j,i - j)) die_nomem();
24328+    if (!stralloc_cats(&ip_reverse,".")) die_nomem();
24329+    if (!j) break;
24330+    i = j - 1;
24331+  }
24332
24333+  ch = rbldnslist.s;
24334+  while (ch < (rbldnslist.s + rbldnslist.len)) {
24335+    rbl(ch);
24336+    /* debug log */
24337+    if (!stralloc_copys(&sar,title.s)) die_nomem();
24338+    if (!stralloc_cats(&sar,"rbl: ip=")) die_nomem();
24339+    if (!stralloc_cats(&sar,ip_env)) die_nomem();
24340+    if (!stralloc_cats(&sar," query=")) die_nomem();
24341+    if (!stralloc_cats(&sar,rblhost.s)) die_nomem();
24342+    if (rblhosterror) {
24343+      if (!stralloc_cats(&sar," result=dnserr")) die_nomem();
24344+    }
24345+    else {
24346+      if (!stralloc_cats(&sar," result=")) die_nomem();
24347+      switch (rbldecision) {
24348+        case 0: if (!stralloc_cats(&sar,"ignore")) die_nomem(); break;
24349+        case 1: if (!stralloc_cats(&sar,"accept")) die_nomem(); break;
24350+        case 2: if (!stralloc_cats(&sar,"delay")) die_nomem(); break;
24351+        case 3: if (!stralloc_cats(&sar,"reject")) die_nomem(); break;
24352+      }
24353+    }
24354+    if (!stralloc_cats(&sar," message='")) die_nomem();
24355+    if (!stralloc_catb(&sar,rbltext.s,rbltext.len)) die_nomem();
24356+    if (!stralloc_cats(&sar,"'")) die_nomem();
24357+    if (!stralloc_0(&sar)) die_nomem();
24358+    strerr_warn1(sar.s,0);
24359+    /* end debug log */
24360+    if (rbldecision) break;
24361+    while (*ch++);
24362+  }
24363+  return rbldecision;
24364+}
24365+/* rbl: end */
24366+
24367+int sizelimit(arg)
24368+char *arg;
24369+{
24370+  int i;
24371+  long r;
24372+  unsigned long sizebytes = 0;
24373+
24374+  i = str_chr(arg,'<');
24375+  if (arg[i])
24376+    arg += i + 1;
24377+  else {
24378+    arg += str_chr(arg,':');
24379+    if (*arg == ':') ++arg;
24380+    while (*arg == ' ') ++arg;
24381+  }
24382+
24383+  arg += str_chr(arg,' ');
24384+  if (*arg == ' ') while (*arg == ' ') ++arg;
24385+  else return 1;
24386+
24387+  i = str_chr(arg,'=');
24388+  arg[i] = 0;
24389+  if (case_equals(arg,"SIZE")) {
24390+    arg += i;
24391+    while (*++arg && *arg > 47 && *arg < 58) {
24392+      sizebytes *= 10;
24393+      sizebytes += *arg - 48;
24394+    }
24395+    r = databytes - sizebytes;
24396+    if (r < 0) return 0;
24397+  }
24398+  return 1;
24399+}
24400 
24401 int addrallowed()
24402 {
24403   int r;
24404   r = rcpthosts(addr.s,str_len(addr.s));
24405   if (r == -1) die_control();
24406+#ifdef TLS
24407+  if (r == 0) if (tls_verify()) r = -2;
24408+#endif
24409   return r;
24410 }
24411 
24412+/* rejectrelaytest: start */
24413+int addrrelay()
24414+{
24415+  if (!rejectrelaytest) { return 0; }
24416+  else
24417+  {
24418+    int j;
24419+    j = addr.len;
24420+    while(--j >= 0)
24421+      if (addr.s[j] == '@') break;
24422+    if (j < 0) j = addr.len;
24423+    while(--j >= 0) {
24424+      if (addr.s[j] == '@') return 1;
24425+      if (addr.s[j] == '%') return 1;
24426+      if (addr.s[j] == '!') return 1;
24427+    }
24428+    return 0;
24429+  }
24430+}
24431+/* rejectrelaytest: end */
24432 
24433+int seenauth = 0;
24434 int seenmail = 0;
24435-int flagbarf; /* defined if seenmail */
24436+int rcptcount = 0;
24437+
24438+/* qregex: start */
24439+/*
24440+int flagbarf;
24441+*/
24442+int flagbarfbmf; /* defined if seenmail */
24443+int flagbarfbmt;
24444+int flagbarfbhelo;
24445+/* qregex: end */
24446+
24447+int flagsize;
24448+int flagbarfspf;
24449+stralloc spfbarfmsg = {0};
24450 stralloc mailfrom = {0};
24451 stralloc rcptto = {0};
24452+stralloc fuser = {0};
24453+stralloc mfparms = {0};
24454+stralloc log_buf = {0};
24455+
24456+/* realbadrcpt: start */
24457+int flagvrt; /* defined if valid rcpt */
24458+int brtcount = 0; /* for brtlimit count */
24459+/* realbadrcpt: end */
24460+
24461+/* rcptcheck: start */
24462+int addrvalid()
24463+{
24464+  int pid;
24465+  int wstat;
24466+  int pierr[2] ;
24467+  substdio ss;
24468+  char ssbuf[sizeof(rcptcheck_err)];
24469+  int len = 0 ;
24470+  char ch;
24471+
24472+  if (!rcptcheck[0]) return 1;
24473+  if (pipe(pierr) == -1) die_rcpt2();
24474+
24475+  switch(pid = fork()) {
24476+    case -1:
24477+      close(pierr[0]);
24478+      close(pierr[1]);
24479+      die_fork();
24480+    case 0:
24481+      if (!env_put2("SENDER",mailfrom.s)) die_nomem();
24482+      if (!env_put2("RECIPIENT",addr.s)) die_nomem();
24483+      if (!env_put2("HELO",helohost.s)) die_nomem();
24484+      if (!env_put2("USE_FD4","1")) die_nomem();
24485+      close(1);
24486+      dup2(2,1);
24487+      close(pierr[0]);
24488+      if (fd_move(4,pierr[1]) == -1) die_rcpt2();
24489+      execv(*rcptcheck,rcptcheck);
24490+      _exit(120);
24491+  }
24492+
24493+  close(pierr[1]);
24494+  if (wait_pid(&wstat,pid) == -1) die_rcpt2();
24495+  if (wait_crashed(wstat)) die_rcpt2();
24496+
24497+  substdio_fdbuf(&ss,read,pierr[0],ssbuf,sizeof(ssbuf));
24498+  while ( substdio_bget(&ss,&ch,1) && len < (sizeof(ssbuf)-3) )
24499+    rcptcheck_err[len++] = ch;
24500+  close(pierr[0]);
24501+
24502+  while (len&&((rcptcheck_err[len-1]=='\n')||(rcptcheck_err[len-1]=='\r')))
24503+    len -- ;
24504+  if (len) {
24505+    rcptcheck_err[len] = '\0';
24506+    strerr_warn3(title.s,"RCPTCHECK error: ",rcptcheck_err,0);
24507+    rcptcheck_err[len++] = '\r';
24508+    rcptcheck_err[len++] = '\n';
24509+  }
24510+  rcptcheck_err[len] = '\0';
24511+
24512+  switch(wait_exitcode(wstat)) {
24513+    case 100:
24514+      return 0;
24515+    case 111:
24516+      die_rcpt();
24517+    case 112:
24518+      return 2; // ignore
24519+    case 113:
24520+      return 3; // overlimit
24521+    case 120:
24522+      die_rcpt2();
24523+  }
24524+  return 1;
24525+}
24526+/* rcptcheck: end */
24527+
24528+int checkrcptcount() {
24529+  if (maxrcpt == -1) {return 0;}
24530+  else if (rcptcount > maxrcpt) {return 1;}
24531+  else {return 0;}
24532+}
24533+
24534+/* logging patch */
24535+
24536+void safeloglen(const char* string, const int len) {
24537+    if (string && len) {
24538+        if (!stralloc_catb(&log_buf, string, len-1)) die_nomem();
24539+    } else {
24540+        if (!stralloc_catb(&log_buf, "(null)", 6)) die_nomem();
24541+    }
24542+}
24543+
24544+void safelog(const char* string) {
24545+    if (string) {
24546+        if (!stralloc_cats(&log_buf, string)) die_nomem();
24547+    } else {
24548+        if (!stralloc_catb(&log_buf, "(null)", 6)) die_nomem();
24549+    }
24550+}
24551+
24552+void logit(const char* message) {
24553+    logit2(message, (const char*)0);
24554+}
24555+
24556+void logit2(const char* message, const char* reason)
24557+{
24558+  if (!stralloc_copys(&log_buf, "qmail-smtpd: ")) die_nomem();
24559+  safelog(message);
24560+  if (reason) {
24561+      if (!stralloc_cats(&log_buf, " (")) die_nomem();
24562+      if (!stralloc_cats(&log_buf, reason)) die_nomem();
24563+      if (!stralloc_cats(&log_buf, ")")) die_nomem();
24564+  }
24565+  if (!stralloc_catb(&log_buf, ": ", 2)) die_nomem();
24566+  safeloglen(mailfrom.s, mailfrom.len);
24567+  if (!stralloc_catb(&log_buf, " from ", 6)) die_nomem();
24568+  safelog(remoteip);
24569+  if (!stralloc_catb(&log_buf, " to ", 4)) die_nomem();
24570+  safeloglen(addr.s, addr.len);
24571+  if (!stralloc_catb(&log_buf, " helo ", 6)) die_nomem();
24572+  safeloglen(helohost.s, helohost.len);
24573+  if (!stralloc_catb(&log_buf, "\n", 1)) die_nomem();
24574+  substdio_putflush(&sserr, log_buf);
24575+}
24576+
24577+/* end logging patch */
24578+
24579+int mailfrom_size(arg) char *arg;
24580+{
24581+  long r;
24582+  unsigned long sizebytes = 0;
24583+
24584+  scan_ulong(arg,&r);
24585+  sizebytes = r;
24586+  if (databytes) if (sizebytes > databytes) return 1;
24587+  return 0;
24588+}
24589+
24590+void mailfrom_auth(arg,len)
24591+char *arg;
24592+int len;
24593+{
24594+  if (!stralloc_copys(&fuser,"")) die_nomem();
24595+  if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); }
24596+  else
24597+    while (len) {
24598+      if (*arg == '+') {
24599+        if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); }
24600+        if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); }
24601+      }
24602+      else
24603+        if (!stralloc_catb(&fuser,arg,1)) die_nomem();
24604+      arg++; len--;
24605+    }
24606+  if(!stralloc_0(&fuser)) die_nomem();
24607+  if (!remoteinfo) {
24608+    remoteinfo = fuser.s;
24609+    if (!env_unset("TCPREMOTEINFO")) die_read("TCPREMOTEINFO");
24610+    if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
24611+  }
24612+}
24613+
24614+void mailfrom_parms(arg) char *arg;
24615+{
24616+  int i;
24617+  int len;
24618+
24619+    len = str_len(arg);
24620+    if (!stralloc_copys(&mfparms,"")) die_nomem();
24621+    i = byte_chr(arg,len,'>');
24622+    if (i > 4 && i < len) {
24623+      while (len) {
24624+        arg++; len--;
24625+        if (*arg == ' ' || *arg == '\0' ) {
24626+           if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; }
24627+           if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5);
24628+           if (!stralloc_copys(&mfparms,"")) die_nomem();
24629+        }
24630+        else
24631+           if (!stralloc_catb(&mfparms,arg,1)) die_nomem();
24632+      }
24633+    }
24634+}
24635 
24636 void smtp_helo(arg) char *arg;
24637 {
24638+  envelopepos = 1;
24639   smtp_greet("250 "); out("\r\n");
24640   seenmail = 0; dohelo(arg);
24641+  if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO);
24642+  if ((!flagbarfbhelo) && (bhelonrok) && (!relayclient)) flagbarfbhelo = bmcheck(BMCHECK_BHELONR);
24643 }
24644+char size_buf[FMT_ULONG];
24645+void smtp_size()
24646+{
24647+  size_buf[fmt_ulong(size_buf,(unsigned long) databytes)] = 0;
24648+  out("250 SIZE "); out(size_buf); out("\r\n");
24649+}
24650+
24651+/* ESMTP extensions are published here */
24652 void smtp_ehlo(arg) char *arg;
24653 {
24654-  smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
24655+  char size[FMT_ULONG];
24656+#ifdef TLS
24657+  struct stat st;
24658+#endif
24659+  size[fmt_ulong(size,(unsigned int) databytes)] = 0;
24660+  envelopepos = 1;
24661+  smtp_greet("250-");
24662+  #ifdef TLS
24663+  if (!disabletls && !ssl && (stat("control/servercert.pem",&st) == 0))
24664+  out("\r\n250-STARTTLS");
24665+  #endif
24666+  out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n");
24667+#ifdef TLS
24668+  if (!forcetls || ssl) {
24669+#endif
24670+  if (smtpauth == 1 || smtpauth == 11) out("250-AUTH LOGIN PLAIN\r\n");
24671+  if (smtpauth == 3 || smtpauth == 13) out("250-AUTH LOGIN PLAIN CRAM-MD5\r\n");
24672+  if (smtpauth == 2 || smtpauth == 12) out("250-AUTH CRAM-MD5\r\n");
24673+#ifdef TLS
24674+  }
24675+#endif
24676+  smtp_size();
24677   seenmail = 0; dohelo(arg);
24678+  if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO);
24679+  if ((!flagbarfbhelo) && (bhelonrok) && (!relayclient)) flagbarfbhelo = bmcheck(BMCHECK_BHELONR);
24680 }
24681 void smtp_rset(arg) char *arg;
24682 {
24683-  seenmail = 0;
24684+  seenmail = 0; /* seenauth = 0; RFC 5321: retain authentication */
24685+  mailfrom.len = 0; rcptto.len = 0;
24686+  /* prevents the maxrcpto error if control/maxrcpt limit has been exceeded in the same email, but not in multiple messages sequentially */
24687+  rcptcount = 0;
24688+  envelopepos = 1;
24689+  /* end rcptcount adjustment */
24690   out("250 flushed\r\n");
24691 }
24692+
24693 void smtp_mail(arg) char *arg;
24694 {
24695+  int r;
24696+
24697+  envelopepos = 2;
24698+  if (smtpauth)
24699+    if (smtpauth > 10 && !seenauth) { err_submission(); return; }
24700   if (!addrparse(arg)) { err_syntax(); return; }
24701+  if (databytes && !sizelimit(arg)) { err_size(); return; }
24702+/* start chkuser code */
24703+  switch (chkuser_sender (&addr)) {
24704+    case CHKUSER_OK:
24705+       break;
24706+    case CHKUSER_ERR_MUSTAUTH:
24707+       qlogenvelope("rejected","chkusersender","mustauth","530");
24708+       return;
24709+       break;
24710+    case CHKUSER_ERR_SENDER_FORMAT:
24711+       qlogenvelope("rejected","chkusersender","senderformat","553");
24712+       return;
24713+       break;
24714+    case CHKUSER_ERR_SENDER_MX:
24715+       qlogenvelope("rejected","chkusersender","sendermxinvalid","550");
24716+       return;
24717+       break;
24718+    case CHKUSER_ERR_SENDER_MX_TMP:
24719+       qlogenvelope("rejected","chkusersender","sendermxdnstmpfail","451");
24720+       return;
24721+       break;
24722+    default:
24723+       qlogenvelope("rejected","chkusersender","invalid","550");
24724+       return;
24725+       break;
24726+   }
24727+/* end chkuser code */
24728+/* authtlsvariables: start */
24729+    /* if it is authenticated but MAIL FROM and AUTH USER are different */
24730+    if (smtpauth && seenauth && forceauthmailfrom) {
24731+      if (strcmp(addr.s,user.s)) { err_authmismatch(); return; }
24732+    }
24733+/* authtlsvariables: end */
24734+/* rejectnullsenders: start */
24735+  if ((rejnsmf) && (addr.len <= 1)) { die_nullsender(); return; }
24736+/* rejectnullsenders: end */
24737+/* qregex: start */
24738+  /*
24739   flagbarf = bmfcheck();
24740+  */
24741+  flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */
24742+  if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF);
24743+  if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) {
24744+    flagbarfbmf = bmcheck(BMCHECK_BMFNR);
24745+  }
24746+/* qregex: end */
24747+  flagsize = 0;
24748+  mailfrom_parms(arg);
24749+  if (flagsize) { err_size(); return; }
24750+
24751+/* qregex: start */
24752+  /*
24753+  flagbarf = bmfcheck();
24754+  */
24755+  flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */
24756+  if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF);
24757+  if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) {
24758+    flagbarfbmf = bmcheck(BMCHECK_BMFNR);
24759+  }
24760+/* qregex: end */
24761+
24762+  flagbarfspf = 0;
24763+  if (spfbehavior && !relayclient)
24764+   {
24765+    switch(r = spfcheck(remoteip4)) {
24766+    case SPF_OK: env_put2("SPFRESULT","pass"); break;
24767+    case SPF_NONE: env_put2("SPFRESULT","none"); break;
24768+    case SPF_UNKNOWN: env_put2("SPFRESULT","unknown"); break;
24769+    case SPF_NEUTRAL: env_put2("SPFRESULT","neutral"); break;
24770+    case SPF_SOFTFAIL: env_put2("SPFRESULT","softfail"); break;
24771+    case SPF_FAIL: env_put2("SPFRESULT","fail"); break;
24772+    case SPF_ERROR: env_put2("SPFRESULT","error"); break;
24773+    }
24774+    switch (r) {
24775+    case SPF_NOMEM:
24776+      die_nomem();
24777+    case SPF_ERROR:
24778+      if (spfbehavior < 2) break;
24779+      qlogenvelope("rejected","spf","lookupfailure","451");
24780+      out("451 SPF lookup failure (#4.3.0)\r\n");
24781+      return;
24782+    case SPF_NONE:
24783+    case SPF_UNKNOWN:
24784+      if (spfbehavior < 6) break;
24785+    case SPF_NEUTRAL:
24786+      if (spfbehavior < 5) break;
24787+    case SPF_SOFTFAIL:
24788+      if (spfbehavior < 4) break;
24789+    case SPF_FAIL:
24790+      if (spfbehavior < 3) break;
24791+      if (!spfexplanation(&spfbarfmsg)) die_nomem();
24792+      if (!stralloc_0(&spfbarfmsg)) die_nomem();
24793+      flagbarfspf = 1;
24794+    }
24795+   }
24796+  else
24797+   env_unset("SPFRESULT");
24798   seenmail = 1;
24799   if (!stralloc_copys(&rcptto,"")) die_nomem();
24800   if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
24801   if (!stralloc_0(&mailfrom)) die_nomem();
24802   out("250 ok\r\n");
24803 }
24804+
24805+void err_spf() {
24806+  int i,j;
24807+
24808+  for(i = 0; i < spfbarfmsg.len; i = j + 1) {
24809+    j = byte_chr(spfbarfmsg.s + i, spfbarfmsg.len - i, '\n') + i;
24810+    if (j < spfbarfmsg.len) {
24811+      out("550-");
24812+      spfbarfmsg.s[j] = 0;
24813+      out(spfbarfmsg.s);
24814+      spfbarfmsg.s[j] = '\n';
24815+      out("\r\n");
24816+    } else {
24817+      out("550 ");
24818+      out(spfbarfmsg.s);
24819+      out(" (#5.7.1)\r\n");
24820+    }
24821+  }
24822+}
24823+
24824+int flagdnsbl = 0;
24825+stralloc dnsblhost = {0};
24826+
24827 void smtp_rcpt(arg) char *arg; {
24828+  int flagrcptmatch = 0; /* 0 undefined, 1 validrcptto, 2 chkuser, 3 chkuserrelay, 4 rcptcheck */
24829+/* added by empf patch */
24830+  int ret = 0;
24831+/* end of empf pacth  */
24832+  envelopepos = 3;
24833   if (!seenmail) { err_wantmail(); return; }
24834   if (!addrparse(arg)) { err_syntax(); return; }
24835+/* rejectrelaytest: start */
24836+  if (addrrelay()) { err_relay(); return; }
24837+/* rejectrelaytest: end */
24838+  if (addr.len) addrinrcpthosts = addrallowed();
24839+  else addrinrcpthosts = 0;
24840+/* qregex: start */
24841+  /*
24842   if (flagbarf) { err_bmf(); return; }
24843-  if (relayclient) {
24844+  */
24845+  if (flagbarfbhelo) {
24846+    if (logregex) {
24847+      strerr_warn5(title.s,"badhelo: <",helohost.s,"> matches pattern: ",matchedregex.s,0);
24848+    } else {
24849+      strerr_warn5(title.s,"badhelo: <",helohost.s,"> at ",remoteip,0);
24850+    }
24851+    qlogenvelope("rejected","qregexbhelo",matchedregex.s,"553");
24852+    err_bhelo();
24853+    return;
24854+  }
24855+
24856+  if (flagbarfbmf) {
24857+    if (logregex) {
24858+      strerr_warn5(title.s,"badmailfrom: <",mailfrom.s,"> matches pattern: ",matchedregex.s,0);
24859+    } else {
24860+      strerr_warn5(title.s,"badmailfrom: <",mailfrom.s,"> at ",remoteip,0);
24861+    }
24862+    qlogenvelope("rejected","qregexbmf",matchedregex.s,"553");
24863+    err_bmf();
24864+    return;
24865+  }
24866+/* qregex: end */
24867+
24868+  if (flagbarfspf) { qlogenvelope("rejected","spf",env_get("SPFRESULT"),"550"); err_spf(); return; }
24869+
24870+/* dnsbl: start */
24871+/*
24872+  if (!(relayclient || dnsblskip || flagdnsbl))
24873+    if (dnsblcheck()) die_dnsbl(dnsblhost.s);
24874+*/
24875+/* dnsbl: end */
24876+/* start chkuser code */
24877+/*  if (relayclient) {
24878     --addr.len;
24879     if (!stralloc_cats(&addr,relayclient)) die_nomem();
24880     if (!stralloc_0(&addr)) die_nomem();
24881   }
24882   else
24883     if (!addrallowed()) { err_nogateway(); return; }
24884+*/
24885+
24886+/* qregex: start */
24887+    if (brtlimit && (brtcount >= brtlimit)) {
24888+      strerr_warn3(title.s,"badrcptto: excessive rcptto violations hanging up on ",remoteip,0);
24889+      die_brtlimit();
24890+    }
24891+
24892+    if (bmtok) flagbarfbmt = bmcheck(BMCHECK_BMT);
24893+    if ((!flagbarfbmt) && (bmtnrok) && (!relayclient)) {
24894+      flagbarfbmt = bmcheck(BMCHECK_BMTNR);
24895+    }
24896+    if (flagbarfbmt) {
24897+      if (logregex) {
24898+        strerr_warn5(title.s,"badrcptto: <",addr.s,"> matches pattern: ",matchedregex.s,0);
24899+      } else {
24900+        strerr_warn5(title.s,"badrcptto: <",addr.s,"> at ",remoteip,0);
24901+      }
24902+      qlogenvelope("rejected","qregexbmt",matchedregex.s,"553");
24903+      ++brtcount;
24904+      err_bmt();
24905+      return;
24906+    }
24907+/* qregex: end */
24908+
24909+/* realbadrcpt: start */
24910+  if (!relayclient) {  /* if relayclient is defined, skip valid recipient checking */
24911+    /* validrcptto */
24912+    flagvrt = 0;
24913+    int vrtres = 0;
24914+    if ((vrtok) || (vrtfd != -1)) {  /* run check only if validrcptto or morevalidrcptto.cdb exist */
24915+      vrtres = vrtcheck();
24916+      if (vrtres > 0) {
24917+        flagvrt = 1;
24918+       flagrcptmatch = 1;
24919+        strerr_warn5(title.s,"validrcptto: accepted address <",addr.s,"> at ",remoteip,0);
24920+      }
24921+      else if (vrtres < 0) {
24922+        strerr_warn5(title.s,"validrcptto: drop address <",addr.s,"> at ",remoteip,0);
24923+        ++brtcount;
24924+        err_vrt();
24925+        /*err_rcpt();*/
24926+        return;
24927+      }
24928+    }
24929+
24930+    if (!flagvrt) {
24931+      switch (chkuser_realrcpt (&mailfrom, &addr)) {
24932+         case CHKUSER_OK:
24933+               flagrcptmatch = 2;
24934+                break;
24935+         case CHKUSER_RELAYING:
24936+                --addr.len;
24937+                if (!stralloc_cats(&addr,relayclient)) die_nomem();
24938+                if (!stralloc_0(&addr)) die_nomem();
24939+               flagrcptmatch = 3;
24940+                break;
24941+         case CHKUSER_NORCPTHOSTS:
24942+                qlogenvelope("rejected","chkuser","notinrcpthosts","553");
24943+                ++brtcount;
24944+                return;
24945+                break;
24946+         case CHKUSER_KO:
24947+                qlogenvelope("rejected","chkuser","nomailbox","550");
24948+               ++brtcount;
24949+                return;
24950+                break;
24951+         case CHKUSER_ERR_AUTH_RESOURCE:
24952+                qlogenvelope("rejected","chkuser","noauthresource","451");
24953+                return;
24954+                break;
24955+         case CHKUSER_ERR_MUSTAUTH:
24956+                qlogenvelope("rejected","chkuser","mustauth","530");
24957+                return;
24958+                break;
24959+         case CHKUSER_ERR_MBXFULL:
24960+                qlogenvelope("rejected","chkuser","mailboxfull","552");
24961+                return;
24962+                break;
24963+         case CHKUSER_ERR_MAXRCPT:
24964+                qlogenvelope("rejected","chkuser","maxrcpt","550");
24965+                return;
24966+                break;
24967+         case CHKUSER_ERR_MAXWRONGRCPT:
24968+                qlogenvelope("rejected","chkuser","maxwrongrcpt","550");
24969+                return;
24970+                break;
24971+         case CHKUSER_ERR_INTRUSION_THRESHOLD:
24972+                qlogenvelope("rejected","chkuser","instrusionthreshold","550");
24973+                ++brtcount;
24974+                return;
24975+                break;
24976+         case CHKUSER_ERR_DOMAIN_MISSING:
24977+                qlogenvelope("rejected","chkuser","domainmissing","550");
24978+               ++brtcount;
24979+                return;
24980+                break;
24981+         case CHKUSER_ERR_RCPT_FORMAT:
24982+                qlogenvelope("rejected","chkuser","rcptformat","553");
24983+                ++brtcount;
24984+                return;
24985+                break;
24986+         case CHKUSER_ERR_RCPT_MX:
24987+                qlogenvelope("rejected","chkuser","rcptmxinvalid","550");
24988+               ++brtcount;
24989+                return;
24990+                break;
24991+         case CHKUSER_ERR_RCPT_MX_TMP:
24992+                qlogenvelope("rejected","chkuser","rcptmxdnstmpfail","451");
24993+                return;
24994+                break;
24995+         default:
24996+                qlogenvelope("rejected","chkuser","invalid","550");
24997+                return;
24998+                break;
24999+      }
25000+    }
25001+  } // if (!relayclient)
25002+
25003+
25004+  /* rcptcheck */
25005+  if ( (rcptcheck[0]) && (!relayclient || rcptcheckrelayclient) ) { // if RCPTCHECK is not defined, addrvalid returns 1 (rcpt ok),check before calling
25006+    strerr_warn5(title.s,"rcptcheck: checking <",addr.s,"> at ",remoteip,0);
25007+    if (flagrcptmatch) {
25008+      if (!env_put2("RCPTFOUND","1")) die_nomem();
25009+    }
25010+    else {
25011+      if (!env_unset("RCPTFOUND")) die_nomem();
25012+    }
25013+    if (addrinrcpthosts) {
25014+      if (!env_put2("RCPTHOSTS","1")) die_nomem();
25015+    }
25016+    else {
25017+      if (!env_unset("RCPTHOSTS")) die_nomem();
25018+    }
25019+
25020+    int rcres = 0;
25021+    rcres = addrvalid();
25022+
25023+    char smtperrcode[4];
25024+    char *smtperrstrptr;
25025+    long smtperrcodenum = 0;
25026+    int len = 0;
25027+    int closesession = 0;
25028+
25029+    if ((rcptcheck_err[0]) && (sizeof(rcptcheck_err) > 3)) {
25030+      strncpy(smtperrcode,rcptcheck_err,3);
25031+      smtperrcode[3] = '\0';
25032+      smtperrcodenum = strtoul(smtperrcode, &smtperrstrptr, 10);
25033+      if ((smtperrcodenum >= 400) && (smtperrcodenum <=599)) {
25034+        if (smtperrcodenum == 421) closesession = 1;
25035+      }
25036+      else {
25037+        len = str_copy(rcptcheck_err,"451 temporary problem (#4.4.2)\r\n");
25038+        rcptcheck_err[len] = '\0' ;
25039+      }
25040+      qlogenvelope("rejected","rcptcheck","custom",smtperrcode);
25041+    }
25042+    else {
25043+      switch (rcres) {
25044+        case 0:
25045+          strerr_warn5(title.s,"rcptcheck: drop address <",addr.s,"> at ",remoteip,0);
25046+          qlogenvelope("rejected","rcptcheck","nomailbox","550");
25047+          len = str_copy(rcptcheck_err,"550 sorry, no mailbox here by that name. (#5.1.1)\r\n");
25048+          rcptcheck_err[len] = '\0';
25049+          break;
25050+        case 1:
25051+          strerr_warn5(title.s,"rcptcheck: accepted address <",addr.s,"> at ",remoteip,0);
25052+          flagrcptmatch = 4;
25053+          break;
25054+        case 2:
25055+          strerr_warn5(title.s,"rcptcheck: ignore address <",addr.s,"> at ",remoteip,0);
25056+          break;
25057+        case 3:
25058+          strerr_warn5(title.s,"rcptcheck: overlimit sender <",addr.s,"> at ",remoteip,0);
25059+          qlogenvelope("rejected","rcptcheck","overlimit","421");
25060+          len = str_copy(rcptcheck_err,"421 you have exceeded your messaging limits (#4.3.0)\r\n");
25061+          rcptcheck_err[len] = '\0';
25062+          closesession = 1;
25063+          break;
25064+      }
25065+    }
25066+
25067+    if ( (rcres == 0) || (rcres == 3) ) {
25068+      out(rcptcheck_err); flush();
25069+      if (closesession) {
25070+        _exit(1);
25071+      }
25072+      return;
25073+    }
25074+  } // if rcptcheck[0]
25075+/* realbadrcpt: end */
25076+
25077+/* end chkuser code */
25078+/* rbl: start */
25079+  if ((rblok) && !(relayclient || seenauth || dnsblskip || flagrbldns)) {
25080+    flagrbldns = 1;
25081+    rblcheck();
25082+  }
25083+  if (rbldecision >= 2) {
25084+    if (!stralloc_ready(&rblmessage,0)) die_nomem();
25085+    if (flagmustnotbounce || (rbldecision == 2)) {
25086+      if (!stralloc_copys(&rblmessage,"451 ")) die_nomem();
25087+    }
25088+    else
25089+      if (!stralloc_copys(&rblmessage,"553 ")) die_nomem();
25090+        if (rbltext.len > 500) rbltext.len = 500;
25091+    if (!stralloc_cat(&rblmessage,&rbltext)) die_nomem();
25092+    int i;
25093+    for (i = 0;i < rblmessage.len;++i)
25094+      if ((rblmessage.s[i] < 32) || (rblmessage.s[i] > 126))
25095+              rblmessage.s[i] = '?';
25096+        if (!stralloc_cats(&rblmessage,"\r\n")) die_nomem();
25097+    if (flagmustnotbounce || (rbldecision == 2)) die_rbldelay();
25098+    else err_rblreject();
25099+    return;
25100+  }
25101+/* rbl: end */
25102+
25103+/* start empf code */
25104+  ret = policy_check();
25105+
25106+  if (ret == 1) {
25107+    if (!stralloc_cats(&rcptto,"T")) die_nomem();
25108+    if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
25109+    if (!stralloc_0(&rcptto)) die_nomem();
25110+    rcptcount++;
25111+    if (checkrcptcount() == 1) { err_maxrcpt(); return; }
25112+    if (flagrcptmatch == 1) { qlogenvelope("accepted","rcptto","validrcptto","250"); }
25113+    else if (flagrcptmatch == 2) { qlogenvelope("accepted","rcptto","chkuser","250"); }
25114+    else if (flagrcptmatch == 3) { qlogenvelope("accepted","rcptto","chkuserrelay","250"); }
25115+    else if (flagrcptmatch == 4) { qlogenvelope("accepted","rcptto","rcptcheck","250"); }
25116+    else {
25117+      if (relayclient) { qlogenvelope("accepted","relayclient","","250"); }
25118+      else { qlogenvelope("accepted","rcpthosts","","250"); }
25119+    }
25120+    out("250 ok\r\n");
25121+  }
25122+
25123+  else if (ret == 0) {
25124+    qlogenvelope("rejected","empf","","550");
25125+    out("550 cannot message ");
25126+    out(addr.s);
25127+    out(" (#5.0.0 denied by policy)\r\n");
25128+  }
25129+
25130+  else {
25131+    qlogenvelope("rejected","empf","","454");
25132+    out("454 cannot message ");
25133+    out(addr.s);
25134+    out(" (#4.3.0 broken policy)\r\n");
25135+ }
25136+/* end of empf code */
25137+
25138+/*
25139+ * code substituted by empf code
25140   if (!stralloc_cats(&rcptto,"T")) die_nomem();
25141   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
25142   if (!stralloc_0(&rcptto)) die_nomem();
25143+  rcptcount++;
25144+  if (checkrcptcount() == 1) { err_maxrcpt(); return; }
25145   out("250 ok\r\n");
25146+ */
25147 }
25148 
25149-
25150 int saferead(fd,buf,len) int fd; char *buf; int len;
25151 {
25152   int r;
25153   flush();
25154+#ifdef TLS
25155+  if (ssl && fd == ssl_rfd)
25156+    r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len);
25157+  else
25158+#endif
25159   r = timeoutread(timeout,fd,buf,len);
25160   if (r == -1) if (errno == error_timeout) die_alarm();
25161-  if (r <= 0) die_read();
25162+  if (r <= 0) die_read("hang up before quit cmd");
25163   return r;
25164 }
25165 
25166 char ssinbuf[1024];
25167 substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
25168+#ifdef TLS
25169+void flush_io() { ssin.p = 0; flush(); }
25170+#endif
25171 
25172 struct qmail qqt;
25173 unsigned int bytestooverflow = 0;
25174@@ -300,7 +1732,7 @@
25175   int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
25176   int flagmaybey; /* 1 if this line might match \r\n, if fih */
25177   int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
25178-
25179+
25180   state = 1;
25181   *hops = 0;
25182   flaginheader = 1;
25183@@ -322,17 +1754,16 @@
25184     }
25185     switch(state) {
25186       case 0:
25187-        if (ch == '\n') straynewline();
25188+        if (ch == '\n') { state = 1; break; }
25189         if (ch == '\r') { state = 4; continue; }
25190         break;
25191       case 1: /* \r\n */
25192-        if (ch == '\n') straynewline();
25193         if (ch == '.') { state = 2; continue; }
25194         if (ch == '\r') { state = 4; continue; }
25195-        state = 0;
25196+        if (ch != '\n') state = 0;
25197         break;
25198       case 2: /* \r\n + . */
25199-        if (ch == '\n') straynewline();
25200+        if (ch == '\n') return;        /* this is what sendmail-8.8.4 does -djg */
25201         if (ch == '\r') { state = 3; continue; }
25202         state = 0;
25203         break;
25204@@ -351,10 +1782,73 @@
25205   }
25206 }
25207 
25208+void spfreceived()
25209+{
25210+  stralloc sa = {0};
25211+  stralloc rcvd_spf = {0};
25212+
25213+  if (!spfbehavior || relayclient) return;
25214+
25215+  if (!stralloc_copys(&rcvd_spf, "Received-SPF: ")) die_nomem();
25216+  if (!spfinfo(&sa)) die_nomem();
25217+  if (!stralloc_cat(&rcvd_spf, &sa)) die_nomem();
25218+  if (!stralloc_append(&rcvd_spf, "\n")) die_nomem();
25219+  if (bytestooverflow) {
25220+    bytestooverflow -= rcvd_spf.len;
25221+    if (bytestooverflow <= 0) qmail_fail(&qqt);
25222+  }
25223+  qmail_put(&qqt,rcvd_spf.s,rcvd_spf.len);
25224+}
25225+
25226+/* rbl: start */
25227+/*
25228+int dnsblcheck()
25229+{
25230+  char *ch;
25231+  static stralloc dnsblbyte = {0};
25232+  static stralloc dnsblrev = {0};
25233+  static ipalloc dnsblip = {0};
25234+  static stralloc dnsbllist = {0};
25235+
25236+  ch = remoteip;
25237+  if(control_readfile(&dnsbllist,"control/dnsbllist",0) != 1) return 0;
25238+
25239+  if (!stralloc_copys(&dnsblrev,"")) return 0;
25240+  for (;;) {
25241+    if (!stralloc_copys(&dnsblbyte,"")) return 0;
25242+    while (ch[0] && (ch[0] != '.')) {
25243+      if (!stralloc_append(&dnsblbyte,ch)) return 0;
25244+      ch++;
25245+    }
25246+    if (!stralloc_append(&dnsblbyte,".")) return 0;
25247+    if (!stralloc_cat(&dnsblbyte,&dnsblrev)) return 0;
25248+    if (!stralloc_copy(&dnsblrev,&dnsblbyte)) return 0;
25249+
25250+    if (!ch[0]) break;
25251+    ch++;
25252+  }
25253+
25254+  flagdnsbl = 1;
25255+  ch = dnsbllist.s;
25256+  while (ch < (dnsbllist.s + dnsbllist.len)) {
25257+    if (!stralloc_copy(&dnsblhost,&dnsblrev)) return 0;
25258+    if (!stralloc_cats(&dnsblhost,ch)) return 0;
25259+    if (!stralloc_0(&dnsblhost)) return 0;
25260+
25261+    if (!dns_ip(&dnsblip,&dnsblhost)) return 1;
25262+    while (*ch++);
25263+  }
25264+
25265+  return 0;
25266+}
25267+*/
25268+/* rbl:end */
25269+
25270 char accept_buf[FMT_ULONG];
25271 void acceptmessage(qp) unsigned long qp;
25272 {
25273   datetime_sec when;
25274+  strnum[fmt_uint(strnum,(unsigned int) getpid())] = 0;
25275   when = now();
25276   out("250 ok ");
25277   accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0;
25278@@ -363,22 +1857,32 @@
25279   accept_buf[fmt_ulong(accept_buf,qp)] = 0;
25280   out(accept_buf);
25281   out("\r\n");
25282+  substdio_puts(&sslog, "mail recv: pid ");
25283+  substdio_puts(&sslog, strnum);
25284+  substdio_puts(&sslog, " from <");
25285+  substdio_puts(&sslog, mailfrom.s);
25286+  substdio_puts(&sslog, "> qp ");
25287+  substdio_puts(&sslog, accept_buf);
25288+  substdio_putsflush(&sslog, "\n");
25289 }
25290 
25291 void smtp_data(arg) char *arg; {
25292   int hops;
25293   unsigned long qp;
25294   char *qqx;
25295-
25296+
25297   if (!seenmail) { err_wantmail(); return; }
25298   if (!rcptto.len) { err_wantrcpt(); return; }
25299+  envelopepos = 4;
25300   seenmail = 0;
25301   if (databytes) bytestooverflow = databytes + 1;
25302   if (qmail_open(&qqt) == -1) { err_qqt(); return; }
25303   qp = qmail_qp(&qqt);
25304+  strnumqp[fmt_ulong(strnumqp,qp)] = 0; /* qp for qlog */
25305   out("354 go ahead\r\n");
25306 
25307-  received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
25308+  received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo);
25309+  spfreceived();
25310   blast(&hops);
25311   hops = (hops >= MAXHOPS);
25312   if (hops) qmail_fail(&qqt);
25313@@ -386,36 +1890,662 @@
25314   qmail_put(&qqt,rcptto.s,rcptto.len);
25315 
25316   qqx = qmail_close(&qqt);
25317-  if (!*qqx) { acceptmessage(qp); return; }
25318-  if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
25319-  if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }
25320-  if (*qqx == 'D') out("554 "); else out("451 ");
25321+  if (!*qqx) { acceptmessage(qp); logit("message accepted"); qlogreceived("accepted","queueaccept","","250"); return; }
25322+  if (hops) {
25323+    out("554 too many hops, this message is looping (#5.4.6)\r\n");
25324+    logit("message looping");
25325+    qlogreceived("rejected","mailloop","","554");
25326+    return;
25327+  }
25328+  if (databytes) if (!bytestooverflow) {
25329+    err_size();
25330+    logit("message too big");
25331+    return;
25332+  }
25333+  if (*qqx == 'D') {
25334+    out("554 ");
25335+    qlogreceived("rejected","queuereject",qqx + 1,"554");
25336+    logit2("message rejected", qqx + 1);
25337+  } else {
25338+    out("451 ");
25339+    qlogreceived("rejected","queuedelay",qqx + 1,"451");
25340+    logit2("message delayed", qqx + 1);
25341+  }
25342   out(qqx + 1);
25343   out("\r\n");
25344 }
25345 
25346+
25347+int authgetl(void) {
25348+  int i;
25349+
25350+  if (!stralloc_copys(&authin,"")) die_nomem();
25351+  for (;;) {
25352+    if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */
25353+    i = substdio_get(&ssin,authin.s + authin.len,1);
25354+    if (i != 1) die_read("authgetl");
25355+    if (authin.s[authin.len] == '\n') break;
25356+    ++authin.len;
25357+  }
25358+
25359+  if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
25360+  authin.s[authin.len] = 0;
25361+  if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); }
25362+  if (authin.len == 0) { return err_input(); }
25363+  return authin.len;
25364+}
25365+
25366+int authenticate(void)
25367+{
25368+  int child;
25369+  int wstat;
25370+  int pi[2];
25371+
25372+  if (!stralloc_0(&user)) die_nomem();
25373+  if (!stralloc_0(&pass)) die_nomem();
25374+  if (!stralloc_0(&chal)) die_nomem();
25375+
25376+  if (pipe(pi) == -1) return err_pipe();
25377+  switch(child = fork()) {
25378+    case -1:
25379+      return err_fork();
25380+    case 0:
25381+      close(pi[1]);
25382+      if(fd_copy(3,pi[0]) == -1) return err_pipe();
25383+      sig_pipedefault();
25384+        execvp(*childargs, childargs);
25385+      _exit(1);
25386+  }
25387+  close(pi[0]);
25388+
25389+  substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf);
25390+  if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write();
25391+  if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write();
25392+  if (smtpauth == 2 || smtpauth == 3 || smtpauth == 12 || smtpauth == 13)
25393+    if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write();
25394+  if (substdio_flush(&ssauth) == -1) return err_write();
25395+
25396+  close(pi[1]);
25397+  if (!stralloc_copys(&chal,"")) die_nomem();
25398+  if (!stralloc_copys(&slop,"")) die_nomem();
25399+  byte_zero(ssauthbuf,sizeof ssauthbuf);
25400+  if (wait_pid(&wstat,child) == -1) return err_child();
25401+  if (wait_crashed(wstat)) return err_child();
25402+  if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */
25403+  return 0; /* yes */
25404+}
25405+
25406+int auth_login(arg) char *arg;
25407+{
25408+  int r;
25409+
25410+  if (*arg) {
25411+    if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input();
25412+  }
25413+  else {
25414+    out("334 VXNlcm5hbWU6\r\n"); flush();       /* Username: */
25415+    if (authgetl() < 0) return -1;
25416+    if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input();
25417+  }
25418+  if (r == -1) die_nomem();
25419+
25420+  out("334 UGFzc3dvcmQ6\r\n"); flush();         /* Password: */
25421+
25422+  if (authgetl() < 0) return -1;
25423+  if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input();
25424+  if (r == -1) die_nomem();
25425+
25426+  if (!user.len || !pass.len) return err_input();
25427+  return authenticate();
25428+}
25429+
25430+int auth_plain(arg) char *arg;
25431+{
25432+  int r, id = 0;
25433+
25434+  if (*arg) {
25435+    if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input();
25436+  }
25437+  else {
25438+    out("334 \r\n"); flush();
25439+    if (authgetl() < 0) return -1;
25440+    if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
25441+  }
25442+  if (r == -1 || !stralloc_0(&resp)) die_nomem();
25443+  while (resp.s[id]) id++;                       /* "authorize-id\0userid\0passwd\0" */
25444+
25445+  if (resp.len > id + 1)
25446+    if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem();
25447+  if (resp.len > id + user.len + 2)
25448+    if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem();
25449+
25450+  if (!user.len || !pass.len) return err_input();
25451+  return authenticate();
25452+}
25453+
25454+int auth_cram()
25455+{
25456+  int i, r;
25457+  char *s;
25458+
25459+  s = unique;                                           /* generate challenge */
25460+  s += fmt_uint(s,getpid());
25461+  *s++ = '.';
25462+  s += fmt_ulong(s,(unsigned long) now());
25463+  *s++ = '@';
25464+  *s++ = 0;
25465+  if (!stralloc_copys(&chal,"<")) die_nomem();
25466+  if (!stralloc_cats(&chal,unique)) die_nomem();
25467+  if (!stralloc_cats(&chal,local)) die_nomem();
25468+  if (!stralloc_cats(&chal,">")) die_nomem();
25469+  if (b64encode(&chal,&slop) < 0) die_nomem();
25470+  if (!stralloc_0(&slop)) die_nomem();
25471+
25472+  out("334 ");                                          /* "334 base64_challenge \r\n" */
25473+  out(slop.s);
25474+  out("\r\n");
25475+  flush();
25476+
25477+  if (authgetl() < 0) return -1;                        /* got response */
25478+  if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
25479+  if (r == -1 || !stralloc_0(&resp)) die_nomem();
25480+
25481+  i = str_rchr(resp.s,' ');
25482+  s = resp.s + i;
25483+  while (*s == ' ') ++s;
25484+  resp.s[i] = 0;
25485+  if (!stralloc_copys(&user,resp.s)) die_nomem();       /* userid */
25486+  if (!stralloc_copys(&pass,s)) die_nomem();            /* digest */
25487+
25488+  if (!user.len || !pass.len) return err_input();
25489+  return authenticate();
25490+}
25491+
25492+struct authcmd {
25493+  char *text;
25494+  int (*fun)();
25495+} authcmds[] = {
25496+  { "login",auth_login }
25497+, { "plain",auth_plain }
25498+, { "cram-md5",auth_cram }
25499+, { 0,err_noauth }
25500+};
25501+
25502+void smtp_auth(arg)
25503+char *arg;
25504+{
25505+  int i;
25506+  char *cmd = arg;
25507+  if (!smtpauth || !*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; }
25508+  if (seenauth) { err_authd(); return; }
25509+  if (seenmail) { err_authmail(); return; }
25510+#ifdef TLS
25511+  if (forcetls && !ssl) { out("538 auth not available without TLS (#5.3.3)\r\n"); return; }
25512+#endif
25513+
25514+  if (!stralloc_copys(&user,"")) die_nomem();
25515+  if (!stralloc_copys(&pass,"")) die_nomem();
25516+  if (!stralloc_copys(&resp,"")) die_nomem();
25517+  if (!stralloc_copys(&chal,"")) die_nomem();
25518+
25519+  i = str_chr(cmd,' ');
25520+  arg = cmd + i;
25521+  while (*arg == ' ') ++arg;
25522+  cmd[i] = 0;
25523+
25524+  for (i = 0;authcmds[i].text;++i)
25525+    if (case_equals(authcmds[i].text,cmd)) break;
25526+
25527+  switch (authcmds[i].fun(arg)) {
25528+    case 0:
25529+      seenauth = 1;
25530+      protocol = "ESMTPA";
25531+      relayclient = "";
25532+      remoteinfo = user.s;
25533+      if (!env_unset("TCPREMOTEINFO")) die_read("TCPREMOTEINFO");
25534+      if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
25535+      if (!env_put2("RELAYCLIENT",relayclient)) die_nomem();
25536+
25537+      if (!env_unset("SMTPAUTHMETHOD")) die_read("SMTPAUTHMETHOD");
25538+      if (!env_put2("SMTPAUTHMETHOD", authcmds[i].text)) die_nomem();
25539+      if (!env_unset("SMTPAUTHUSER")) die_read("SMTPAUTHUSER");
25540+      if (!env_put2("SMTPAUTHUSER",user.s)) die_nomem();
25541+      if (!env_unset("SMTP_AUTH_USER")) die_read("SMTP_AUTH_USER");
25542+      if (!env_put2("SMTP_AUTH_USER",user.s)) die_nomem();
25543+
25544+      strerr_warn4(title.s,"auth: auth-success type=login user=<",user.s,">",0);
25545+      out("235 ok, go ahead (#2.0.0)\r\n");
25546+      break;
25547+    case 1:
25548+      strerr_warn4(title.s,"auth: auth-failed type=login user=<",user.s,">",0);
25549+      err_authfail(user.s,authcmds[i].text);
25550+  }
25551+}
25552+
25553+#ifdef TLS
25554+stralloc proto = {0};
25555+int ssl_verified = 0;
25556+const char *ssl_verify_err = 0;
25557+
25558+void smtp_tls(char *arg)
25559+{
25560+  if (ssl || disabletls) err_unimpl();
25561+  else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n");
25562+  else tls_init();
25563+}
25564+
25565+RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen)
25566+{
25567+  RSA *rsa;
25568+
25569+  if (!export) keylen = 2048;
25570+  if (keylen == 2048) {
25571+    FILE *in = fopen("control/rsa2048.pem", "r");
25572+    if (in) {
25573+      rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL);
25574+      fclose(in);
25575+      if (rsa) return rsa;
25576+    }
25577+  }
25578+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
25579+  BIGNUM *e; /*exponent */
25580+  e = BN_new();
25581+  BN_set_word(e, RSA_F4);
25582+  if (RSA_generate_key_ex(rsa, keylen, e, NULL) == 1)
25583+    return rsa;
25584+  return NULL;
25585+#else
25586+  return RSA_generate_key(keylen, RSA_F4, NULL, NULL);
25587+#endif
25588+}
25589+
25590+DH *tmp_dh_cb(SSL *ssl, int export, int keylen)
25591+{
25592+  DH *dh;
25593+
25594+  if (!export) keylen = 2048;
25595+  if (keylen == 2048) {
25596+    FILE *in = fopen("control/dh2048.pem", "r");
25597+    if (in) {
25598+      dh = PEM_read_DHparams(in, NULL, NULL, NULL);
25599+      fclose(in);
25600+      if (dh) return dh;
25601+    }
25602+  }
25603+  if (keylen == 1024) {
25604+    FILE *in = fopen("control/dh1024.pem", "r");
25605+    if (in) {
25606+      DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL);
25607+      fclose(in);
25608+      if (dh) return dh;
25609+    }
25610+  }
25611+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
25612+  if((dh = DH_new()) && (DH_generate_parameters_ex(dh, keylen, DH_GENERATOR_2, NULL) == 1))
25613+    return dh;
25614+  return NULL;
25615+#else
25616+  return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL);
25617+#endif
25618+}
25619+
25620+/* don't want to fail handshake if cert isn't verifiable */
25621+int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { return 1; }
25622+
25623+void tls_nogateway()
25624+{
25625+  /* there may be cases when relayclient is set */
25626+  if (!ssl || relayclient) return;
25627+  out("; no valid cert for gatewaying");
25628+  if (ssl_verify_err) { out(": "); out(ssl_verify_err); }
25629+}
25630+void tls_out(const char *s1, const char *s2)
25631+{
25632+  out("454 TLS "); out(s1);
25633+  if (s2) { out(": "); out(s2); }
25634+  out(" (#4.3.0)\r\n"); flush();
25635+}
25636+void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read("tls_err"); }
25637+
25638+# define CLIENTCA "control/clientca.pem"
25639+# define CLIENTCRL "control/clientcrl.pem"
25640+# define SERVERCERT "control/servercert.pem"
25641+
25642+int tls_verify()
25643+{
25644+  stralloc clients = {0};
25645+  struct constmap mapclients;
25646+
25647+  if (!ssl || relayclient || ssl_verified) return 0;
25648+  ssl_verified = 1; /* don't do this twice */
25649+
25650+  /* request client cert to see if it can be verified by one of our CAs
25651+   * and the associated email address matches an entry in tlsclients */
25652+  switch (control_readfile(&clients, "control/tlsclients", 0))
25653+  {
25654+  case 1:
25655+    if (constmap_init(&mapclients, clients.s, clients.len, 0)) {
25656+      /* if CLIENTCA contains all the standard root certificates, a
25657+       * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE;
25658+       * it is probably due to 0.9.6b supporting only 8k key exchange
25659+       * data while the 0.9.6c release increases that limit to 100k */
25660+      STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA);
25661+      if (sk) {
25662+        SSL_set_client_CA_list(ssl, sk);
25663+        SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb);
25664+        break;
25665+      }
25666+      constmap_free(&mapclients);
25667+    }
25668+  case 0: alloc_free(clients.s); return 0;
25669+  case -1: die_control();
25670+  }
25671+
25672+  if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) {
25673+    const char *err = ssl_error_str();
25674+    tls_out("rehandshake failed", err); die_read("rehandshake failed");
25675+  }
25676+
25677+  do { /* one iteration */
25678+    X509 *peercert;
25679+    X509_NAME *subj;
25680+    stralloc email = {0};
25681+
25682+    int n = SSL_get_verify_result(ssl);
25683+    if (n != X509_V_OK)
25684+      { ssl_verify_err = X509_verify_cert_error_string(n); break; }
25685+    peercert = SSL_get_peer_certificate(ssl);
25686+    if (!peercert) break;
25687+
25688+    subj = X509_get_subject_name(peercert);
25689+    n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1);
25690+    if (n >= 0) {
25691+      const ASN1_STRING *s = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, n));
25692+      if (s) { email.len = s->length; email.s = s->data; }
25693+    }
25694+
25695+    if (email.len <= 0)
25696+      ssl_verify_err = "contains no email address";
25697+    else if (!constmap(&mapclients, email.s, email.len))
25698+      ssl_verify_err = "email address not in my list of tlsclients";
25699+    else {
25700+      /* add the cert email to the proto if it helped allow relaying */
25701+      --proto.len;
25702+      if (!stralloc_cats(&proto, "\n  (cert ") /* continuation line */
25703+        || !stralloc_catb(&proto, email.s, email.len)
25704+        || !stralloc_cats(&proto, ")")
25705+        || !stralloc_0(&proto)) die_nomem();
25706+      relayclient = "";
25707+      /* also inform qmail-queue */
25708+      if (!env_put("RELAYCLIENT=")) die_nomem();
25709+      protocol = proto.s;
25710+    }
25711+
25712+    X509_free(peercert);
25713+  } while (0);
25714+  constmap_free(&mapclients); alloc_free(clients.s);
25715+
25716+  /* we are not going to need this anymore: free the memory */
25717+  SSL_set_client_CA_list(ssl, NULL);
25718+  SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
25719+
25720+  return relayclient ? 1 : 0;
25721+}
25722+
25723+void tls_init()
25724+{
25725+  SSL *myssl;
25726+  SSL_CTX *ctx;
25727+  const char *ciphers;
25728+  stralloc saciphers = {0};
25729+  X509_STORE *store;
25730+  X509_LOOKUP *lookup;
25731+  int session_id_context = 1; /* anything will do */
25732+
25733+  SSL_library_init();
25734+
25735+  /* a new SSL context with the bare minimum of options */
25736+  ctx = SSL_CTX_new(SSLv23_server_method());
25737+  if (!ctx) { tls_err("unable to initialize ctx"); return; }
25738+
25739+  /* POODLE vulnerability */
25740+  SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
25741+
25742+  /* renegotiation should include certificate request */
25743+  SSL_CTX_set_options(ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
25744+
25745+  /* never bother the application with retries if the transport is blocking */
25746+  SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
25747+
25748+  /* relevant in renegotiation */
25749+  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
25750+  if (!SSL_CTX_set_session_id_context(ctx, (void *)&session_id_context,
25751+                                        sizeof(session_id_context)))
25752+    { SSL_CTX_free(ctx); tls_err("failed to set session_id_context"); return; }
25753+
25754+  if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT))
25755+    { SSL_CTX_free(ctx); tls_err("missing certificate"); return; }
25756+  SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL);
25757+
25758+  /* crl checking */
25759+  store = SSL_CTX_get_cert_store(ctx);
25760+  if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) &&
25761+      (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1))
25762+    X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
25763+                                X509_V_FLAG_CRL_CHECK_ALL);
25764+
25765+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
25766+  /* support ECDH */
25767+  SSL_CTX_set_ecdh_auto(ctx,1);
25768+#endif
25769+
25770+  SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
25771+
25772+  /* a new SSL object, with the rest added to it directly to avoid copying */
25773+  myssl = SSL_new(ctx);
25774+  SSL_CTX_free(ctx);
25775+  if (!myssl) { tls_err("unable to initialize ssl"); return; }
25776+
25777+  /* this will also check whether public and private keys match */
25778+  if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM))
25779+    { SSL_free(myssl); tls_err("no valid RSA private key"); return; }
25780+
25781+  ciphers = env_get("TLSCIPHERS");
25782+  if (!ciphers) {
25783+    if (control_readfile(&saciphers, "control/tlsserverciphers", 0) == -1)
25784+      { SSL_free(myssl); die_control(); }
25785+    if (saciphers.len) { /* convert all '\0's except the last one to ':' */
25786+      int i;
25787+      for (i = 0; i < saciphers.len - 1; ++i)
25788+        if (!saciphers.s[i]) saciphers.s[i] = ':';
25789+      ciphers = saciphers.s;
25790+    }
25791+  }
25792+  if (!ciphers || !*ciphers) ciphers = "DEFAULT";
25793+  SSL_set_cipher_list(myssl, ciphers);
25794+  alloc_free(saciphers.s);
25795+
25796+  SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb);
25797+  SSL_set_tmp_dh_callback(myssl, tmp_dh_cb);
25798+  SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin));
25799+  SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout));
25800+
25801+  if (!smtps) { flagtls = 1; out("220 ready for tls\r\n"); flush(); }
25802+
25803+  if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) {
25804+    /* neither cleartext nor any other response here is part of a standard */
25805+    const char *err = ssl_error_str();
25806+    tls_out("connection failed", err); ssl_free(myssl); die_read("tls connection failed");
25807+  }
25808+  ssl = myssl;
25809+
25810+  /* populate the protocol string, used in Received */
25811+  if (!stralloc_copys(&proto, "ESMTPS (")
25812+    || !stralloc_cats(&proto, SSL_get_cipher(ssl))
25813+    || !stralloc_cats(&proto, " encrypted)")) die_nomem();
25814+  if (!stralloc_0(&proto)) die_nomem();
25815+  protocol = proto.s;
25816+
25817+  /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */
25818+  dohelo(remotehost);
25819+}
25820+
25821+# undef SERVERCERT
25822+# undef CLIENTCA
25823+
25824+#endif
25825+
25826 struct commands smtpcommands[] = {
25827   { "rcpt", smtp_rcpt, 0 }
25828 , { "mail", smtp_mail, 0 }
25829 , { "data", smtp_data, flush }
25830+, { "auth", smtp_auth, flush }
25831 , { "quit", smtp_quit, flush }
25832 , { "helo", smtp_helo, flush }
25833 , { "ehlo", smtp_ehlo, flush }
25834 , { "rset", smtp_rset, 0 }
25835 , { "help", smtp_help, flush }
25836+#ifdef TLS
25837+, { "starttls", smtp_tls, flush_io }
25838+#endif
25839 , { "noop", err_noop, flush }
25840 , { "vrfy", err_vrfy, flush }
25841-, { 0, err_unimpl, flush }
25842+, { 0, err_unrecog, flush }
25843 } ;
25844 
25845-void main()
25846+/* qsmtpdlog: start */
25847+void qsmtpdlog(const char *head, const char *result, const char *reason, const char *detail, const char *statuscode) {
25848+  char *x;
25849+  char *ch;
25850+  int i, r;
25851+  stralloc lst = {0};
25852+  int isenvelope = 0;
25853
25854+  void outqlog(char *s, unsigned int n) {
25855+    while (n > 0) {
25856+      substdio_put(&sslog,((*s > 32) && (*s <= 126)) ? s : "_",1);
25857+      --n;
25858+      ++s;
25859+    }
25860+  }
25861+  void outsqlog(s) char *s; { outqlog(s,str_len(s)); }
25862+
25863+  stralloc_copys(&lst,head);
25864+  if (stralloc_starts(&lst,"qlogenvelope")) isenvelope = 1;
25865+  substdio_puts(&sslog, head);
25866+  substdio_puts(&sslog, ":");
25867+
25868+  substdio_puts(&sslog, " result="); if (result) outsqlog(result);
25869+  substdio_puts(&sslog, " code="); if (detail) outsqlog(statuscode);
25870+  substdio_puts(&sslog, " reason="); if (reason) outsqlog(reason);
25871+  substdio_puts(&sslog, " detail="); if (detail) outsqlog(detail);
25872+  substdio_puts(&sslog, " helo="); if (helohost.len) outsqlog(helohost.s);
25873+  substdio_puts(&sslog, " mailfrom=");
25874+  if (mailfrom.len) outsqlog(mailfrom.s);
25875+  else if ( (envelopepos==2) && (addr.len) ) outsqlog(addr.s); // qlog called in smtp_mail() doesn't have mailfrom.s defined yet
25876+
25877+  substdio_puts(&sslog, " rcptto=");
25878+  if ((rcptto.len) && (!isenvelope)) {
25879+    ch = rcptto.s;
25880+    outsqlog(ch+1);
25881+    while (*ch++);
25882+    while (ch < (rcptto.s + rcptto.len)) {
25883+      outsqlog(",");
25884+      outsqlog(ch+1);
25885+      while (*ch++);
25886+    }
25887+  }
25888+  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
25889+
25890+  substdio_puts(&sslog, " relay="); if (relayclient) outsqlog("yes"); else outsqlog("no");
25891+
25892+  // only log rcpthosts value in smtp_rcpt(), that is for a single recipient, this field is meaningless for multiple recipients
25893+  substdio_puts(&sslog, " rcpthosts="); if (isenvelope && addr.len && (envelopepos==3)) { if (addrinrcpthosts) outsqlog("yes"); else outsqlog("no"); }
25894+
25895+  substdio_puts(&sslog, " size=");
25896+  if (bytestooverflow) {
25897+    char *p,text[20];
25898+    if ((databytes - bytestooverflow) >= 0)
25899+      sprintf(text,"%d",databytes - bytestooverflow);
25900+    else
25901+      sprintf(text,"");
25902+    p = text;
25903+    outsqlog(p);
25904+  }
25905+
25906+  substdio_puts(&sslog, " authuser="); if (user.len) outsqlog(user.s);
25907+  substdio_puts(&sslog, " authtype="); x = env_get("SMTPAUTHMETHOD"); if (x) outsqlog(x);
25908+  substdio_puts(&sslog, " encrypted="); if (smtps) outsqlog("ssl"); else if (flagtls) outsqlog("tls");
25909+
25910+  substdio_puts(&sslog, " sslverified=");
25911+#ifdef TLS
25912+  if (ssl_verified) outsqlog("yes"); else outsqlog("no");
25913+#endif
25914+/*
25915+  substdio_puts(&sslog, " sslproto=");
25916+#ifdef TLS
25917+  if (proto.len) outsqlog(proto.s);
25918+#endif
25919+*/
25920+  substdio_puts(&sslog, " localip="); x = env_get("TCPLOCALIP"); if (x) outsqlog(x);
25921+  substdio_puts(&sslog, " localport="); x = env_get("TCPLOCALPORT"); if (x) outsqlog(x);
25922+  substdio_puts(&sslog, " remoteip="); x = env_get("TCPREMOTEIP"); if (x) outsqlog(x);
25923+  substdio_puts(&sslog, " remoteport="); x = env_get("TCPREMOTEPORT"); if (x) outsqlog(x);
25924+  substdio_puts(&sslog, " remotehost="); x = env_get("TCPREMOTEHOST"); if (x) outsqlog(x);
25925+  substdio_puts(&sslog, " qp="); if (strnumqp) outsqlog(strnumqp);
25926+  substdio_puts(&sslog, " pid="); if (strnumpid) outsqlog(strnumpid);
25927+  substdio_putsflush(&sslog, "\n");
25928+}
25929+/* qsmtpdlog: end */
25930+
25931+void main(argc,argv)
25932+int argc;
25933+char **argv;
25934 {
25935+  int n, m;
25936+  childargs = argv + 1;
25937   sig_pipeignore();
25938   if (chdir(auto_qmail) == -1) die_control();
25939+
25940+  pid_buf[fmt_ulong(pid_buf,getpid())]=0;
25941+  if (!stralloc_copys(&title,"qmail-smtpd[")) die_nomem();
25942+  if (!stralloc_cats(&title,pid_buf)) die_nomem();
25943+  if (!stralloc_cats(&title,"]: ")) die_nomem();
25944+  if (!stralloc_0(&title)) die_nomem();
25945+
25946   setup();
25947   if (ipme_init() != 1) die_ipme();
25948+  if (!relayclient && greetdelay) {
25949+    if (drop_pre_greet) {
25950+      n = timeoutread(greetdelay ? greetdelay : 1, 0, ssinbuf, sizeof(ssinbuf));
25951+      if(n == -1) {
25952+        if (errno != error_timeout)
25953+          strerr_die3sys(1, "GREETDELAY from ", remoteip, ": ");
25954+      } else if (n == 0) {
25955+        strerr_die3x(1, "GREETDELAY from ", remoteip, ": client disconnected");
25956+      } else {
25957+        strerr_warn3("GREETDELAY from ", remoteip, ": client sent data before greeting", 0);
25958+        die_pre_greet();
25959+      }
25960+    }
25961+    else {
25962+      strerr_warn3("GREETDELAY: ", greetdelay, "s", 0);
25963+      sleep(greetdelay);
25964+      m = 0;
25965+      for (;;) {
25966+        n = timeoutread(0, 0, ssinbuf, sizeof(ssinbuf));
25967+        if (n <= 0)
25968+          break;
25969+        if (n > 0 && m == 0) {
25970+          strerr_warn3("GREETDELAY from ", remoteip, ": client sent data before greeting. ignoring", 0);
25971+          m = 1;
25972+        }
25973+      }
25974+    }
25975+  }
25976+
25977   smtp_greet("220 ");
25978   out(" ESMTP\r\n");
25979-  if (commands(&ssin,&smtpcommands) == 0) die_read();
25980+  if (commands(&ssin,&smtpcommands) == 0) die_read("commands");
25981   die_nomem();
25982 }
25983diff -ruN ../netqmail-1.06-original/qmail-start.c netqmail-1.06/qmail-start.c
25984--- ../netqmail-1.06-original/qmail-start.c     1998-06-15 12:53:16.000000000 +0200
25985+++ netqmail-1.06/qmail-start.c 2019-06-26 16:39:31.578826915 +0200
25986@@ -4,10 +4,15 @@
25987 #include "fork.h"
25988 #include "auto_uids.h"
25989 
25990+#include "channels.h"
25991+
25992 char *(qsargs[]) = { "qmail-send", 0 };
25993 char *(qcargs[]) = { "qmail-clean", 0 };
25994 char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 };
25995 char *(qrargs[]) = { "qmail-rspawn", 0 };
25996+#ifdef EXTERNAL_TODO
25997+char *(qtargs[]) = { "qmail-todo", 0};
25998+#endif
25999 
26000 void die() { _exit(111); }
26001 
26002@@ -18,19 +23,51 @@
26003 int pi4[2];
26004 int pi5[2];
26005 int pi6[2];
26006-
26007-void close23456() { close(2); close(3); close(4); close(5); close(6); }
26008+#ifdef EXTERNAL_TODO
26009+int pi7[2];
26010+int pi8[2];
26011+int pi9[2];
26012+int pi10[2];
26013+#endif
26014+
26015+int suppl_pi[SUPPL_CHANNELS*2][2];
26016+
26017+void close23456() {
26018+  int c;
26019+  close(2); close(3); close(4); close(5); close(6);
26020+#ifdef EXTERNAL_TODO
26021+  close(7); close(8);
26022+#endif
26023+  for (c=1+CHANNEL_FD_OFFSET;c<=SUPPL_CHANNELS*2+CHANNEL_FD_OFFSET;c++)
26024+  {
26025+      close(c);
26026+  }
26027+}
26028 
26029 void closepipes() {
26030+  int c;
26031+
26032   close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]);
26033   close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]);
26034   close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]);
26035+#ifdef EXTERNAL_TODO
26036+  close(pi7[0]); close(pi7[1]); close(pi8[0]); close(pi8[1]);
26037+       close(pi9[0]); close(pi9[1]); close(pi10[0]); close(pi10[1]);
26038+#endif
26039+
26040+  for (c=0;c<SUPPL_CHANNELS*2;c++)
26041+  {
26042+      close(suppl_pi[c][0]);
26043+      close(suppl_pi[c][1]);
26044+  }
26045 }
26046 
26047 void main(argc,argv)
26048 int argc;
26049 char **argv;
26050 {
26051+  int c, cc;
26052+
26053   if (chdir("/") == -1) die();
26054   umask(077);
26055   if (prot_gid(auto_gidq) == -1) die();
26056@@ -40,6 +77,14 @@
26057   if (fd_copy(4,0) == -1) die();
26058   if (fd_copy(5,0) == -1) die();
26059   if (fd_copy(6,0) == -1) die();
26060+#ifdef EXTERNAL_TODO
26061+  if (fd_copy(7,0) == -1) die();
26062+  if (fd_copy(8,0) == -1) die();
26063+#endif
26064+  for (c=1+CHANNEL_FD_OFFSET;c<=SUPPL_CHANNELS*2+CHANNEL_FD_OFFSET;c++)
26065+  {
26066+      if (fd_copy(c,0) == -1) die();
26067+  }
26068 
26069   if (argv[1]) {
26070     qlargs[1] = argv[1];
26071@@ -70,6 +115,16 @@
26072   if (pipe(pi4) == -1) die();
26073   if (pipe(pi5) == -1) die();
26074   if (pipe(pi6) == -1) die();
26075+#ifdef EXTERNAL_TODO
26076+  if (pipe(pi7) == -1) die();
26077+  if (pipe(pi8) == -1) die();
26078+  if (pipe(pi9) == -1) die();
26079+  if (pipe(pi10) == -1) die();
26080+#endif
26081+  for (c=0;c<SUPPL_CHANNELS*2;c++)
26082+  {
26083+      if (pipe(suppl_pi[c]) == -1) die();
26084+  }
26085 
26086   switch(fork()) {
26087     case -1: die();
26088@@ -106,6 +161,57 @@
26089       die();
26090   }
26091 
26092+#ifdef EXTERNAL_TODO
26093+  switch(fork()) {
26094+    case -1: die();
26095+    case 0:
26096+      if (prot_uid(auto_uids) == -1) die();
26097+      if (fd_copy(0,pi7[0]) == -1) die();
26098+      if (fd_copy(1,pi8[1]) == -1) die();
26099+      close23456();
26100+      if (fd_copy(2,pi9[1]) == -1) die();
26101+      if (fd_copy(3,pi10[0]) == -1) die();
26102+      closepipes();
26103+      execvp(*qtargs,qtargs);
26104+      die();
26105+  }
26106+
26107+  switch(fork()) {
26108+    case -1: die();
26109+    case 0:
26110+      if (prot_uid(auto_uidq) == -1) die();
26111+      if (fd_copy(0,pi9[0]) == -1) die();
26112+      if (fd_copy(1,pi10[1]) == -1) die();
26113+      close23456();
26114+      closepipes();
26115+      execvp(*qcargs,qcargs);
26116+      die();
26117+  }
26118+#endif
26119+
26120+  for (c=0,cc=0;c<SUPPL_CHANNELS;++c,cc+=2)
26121+  {
26122+      switch(fork()) {
26123+        case -1: die();
26124+        case 0:
26125+          if (prot_uid(auto_uidr) == -1) die();
26126+          /* Does not increment cc in parent process */
26127+          if (fd_copy(0,suppl_pi[cc++][0]) == -1) die();
26128+          if (fd_copy(1,suppl_pi[cc][1]) == -1) die();
26129+          close23456();
26130+          closepipes();
26131+          /*if (str_equal(channel_types[c],"remote"))
26132+          {*/
26133+              execvp(*qrargs,qrargs);
26134+          /*}
26135+           else
26136+          {
26137+             execvp(*qlargs,qlargs);
26138+          }*/
26139+          die();
26140+      }
26141+  }
26142+
26143   if (prot_uid(auto_uids) == -1) die();
26144   if (fd_copy(0,1) == -1) die();
26145   if (fd_copy(1,pi1[1]) == -1) die();
26146@@ -114,6 +220,17 @@
26147   if (fd_copy(4,pi4[0]) == -1) die();
26148   if (fd_copy(5,pi5[1]) == -1) die();
26149   if (fd_copy(6,pi6[0]) == -1) die();
26150+#ifdef EXTERNAL_TODO
26151+  if (fd_copy(7,pi7[1]) == -1) die();
26152+  if (fd_copy(8,pi8[0]) == -1) die();
26153+#endif
26154+
26155+  for (cc=0,c=1+CHANNEL_FD_OFFSET;c<=SUPPL_CHANNELS*2+CHANNEL_FD_OFFSET;c++)
26156+  {
26157+      if (fd_copy(c++,suppl_pi[cc++][1]) == -1) die();
26158+      if (fd_copy(c,suppl_pi[cc++][0]) == -1) die();
26159+  }
26160+
26161   closepipes();
26162   execvp(*qsargs,qsargs);
26163   die();
26164diff -ruN ../netqmail-1.06-original/qmail-todo.c netqmail-1.06/qmail-todo.c
26165--- ../netqmail-1.06-original/qmail-todo.c      1970-01-01 01:00:00.000000000 +0100
26166+++ netqmail-1.06/qmail-todo.c  2019-06-26 16:39:31.579826904 +0200
26167@@ -0,0 +1,791 @@
26168+#include <sys/types.h>
26169+#include <sys/stat.h>
26170+#include "alloc.h"
26171+#include "auto_qmail.h"
26172+#include "byte.h"
26173+#include "constmap.h"
26174+#include "control.h"
26175+#include "direntry.h"
26176+#include "error.h"
26177+#include "exit.h"
26178+#include "fmt.h"
26179+#include "fmtqfn.h"
26180+#include "getln.h"
26181+#include "open.h"
26182+#include "ndelay.h"
26183+#include "now.h"
26184+#include "readsubdir.h"
26185+#include "readwrite.h"
26186+#include "scan.h"
26187+#include "select.h"
26188+#include "str.h"
26189+#include "stralloc.h"
26190+#include "substdio.h"
26191+#include "trigger.h"
26192+
26193+#include "channels.h"
26194+
26195+/* critical timing feature #1: if not triggered, do not busy-loop */
26196+/* critical timing feature #2: if triggered, respond within fixed time */
26197+/* important timing feature: when triggered, respond instantly */
26198+#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */
26199+#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */
26200+#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */
26201+#define SLEEP_SYSFAIL 123
26202+
26203+
26204+stralloc percenthack = {0};
26205+struct constmap mappercenthack;
26206+stralloc locals = {0};
26207+struct constmap maplocals;
26208+stralloc vdoms = {0};
26209+struct constmap mapvdoms;
26210+stralloc envnoathost = {0};
26211+stralloc fname = {0};
26212+
26213+char strnum[FMT_ULONG];
26214+
26215+struct constmap mapsuppl[SUPPL_CHANNELS];
26216+stralloc suppls[SUPPL_CHANNELS];
26217+stralloc newsuppls[SUPPL_CHANNELS];
26218+char *chanaddr[CHANNELS];
26219+
26220+datetime_sec recent;
26221+
26222+void log1(char *x);
26223+void log3(char* x, char* y, char* z);
26224+
26225+int flagstopasap = 0;
26226+void sigterm(void)
26227+{
26228+  if (flagstopasap == 0)
26229+    log1("status: qmail-todo stop processing asap\n");
26230+  flagstopasap = 1;
26231+}
26232+
26233+int flagreadasap = 0; void sighup(void) { flagreadasap = 1; }
26234+int flagsendalive = 1; void senddied(void) { flagsendalive = 0; }
26235+
26236+void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); }
26237+void pausedir(dir) char *dir;
26238+{ log3("alert: unable to opendir ",dir,", sleeping...\n"); sleep(10); }
26239+
26240+void cleandied()
26241+{
26242+  log1("alert: qmail-todo: oh no! lost qmail-clean connection! dying...\n");
26243+  flagstopasap = 1;
26244+}
26245+
26246+
26247+/* this file is not so long ------------------------------------- FILENAMES */
26248+
26249+stralloc fn = {0};
26250+
26251+void fnmake_init(void)
26252+{
26253+ while (!stralloc_ready(&fn,FMTQFN)) nomem();
26254+}
26255+
26256+void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); }
26257+void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,1); }
26258+void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); }
26259+void fnmake_chanaddr(unsigned long id, int c)
26260+{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); }
26261+
26262+
26263+/* this file is not so long ------------------------------------- REWRITING */
26264+
26265+stralloc rwline = {0};
26266+
26267+/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */
26268+/* may trash recip. must set up rwline, between a T and a \0. */
26269+int rewrite(char *recip)
26270+{
26271+  int i;
26272+  int j;
26273+  int c;
26274+  char *x;
26275+  static stralloc addr = {0};
26276+  int at;
26277+
26278+  if (!stralloc_copys(&rwline,"T")) return 0;
26279+  if (!stralloc_copys(&addr,recip)) return 0;
26280+
26281+  i = byte_rchr(addr.s,addr.len,'@');
26282+  if (i == addr.len) {
26283+    if (!stralloc_cats(&addr,"@")) return 0;
26284+    if (!stralloc_cat(&addr,&envnoathost)) return 0;
26285+  }
26286+
26287+  while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) {
26288+    j = byte_rchr(addr.s,i,'%');
26289+    if (j == i) break;
26290+    addr.len = i;
26291+    i = j;
26292+    addr.s[i] = '@';
26293+  }
26294+
26295+  at = byte_rchr(addr.s,addr.len,'@');
26296+
26297+  if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) {
26298+    if (!stralloc_cat(&rwline,&addr)) return 0;
26299+    if (!stralloc_0(&rwline)) return 0;
26300+    return 1;
26301+  }
26302+
26303+  for (i = 0;i <= addr.len;++i)
26304+    if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.')))
26305+      if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) {
26306+        if (!*x) break;
26307+        if (!stralloc_cats(&rwline,x)) return 0;
26308+        if (!stralloc_cats(&rwline,"-")) return 0;
26309+        if (!stralloc_cat(&rwline,&addr)) return 0;
26310+        if (!stralloc_0(&rwline)) return 0;
26311+        return 1;
26312+      }
26313+
26314+  if (!stralloc_cat(&rwline,&addr)) return 0;
26315+  if (!stralloc_0(&rwline)) return 0;
26316+
26317+  for (c = 0;c < SUPPL_CHANNELS;++c)
26318+  {
26319+      if (constmap(&mapsuppl[c],addr.s + at + 1,addr.len - at - 1))
26320+          return c + 3;
26321+  }
26322+
26323+  return 2;
26324+}
26325+
26326+/* this file is not so long --------------------------------- COMMUNICATION */
26327+
26328+substdio sstoqc; char sstoqcbuf[1024];
26329+substdio ssfromqc; char ssfromqcbuf[1024];
26330+stralloc comm_buf = {0};
26331+int comm_pos;
26332+int fdout = -1;
26333+int fdin = -1;
26334+
26335+void comm_init(void)
26336+{
26337+ substdio_fdbuf(&sstoqc,write,2,sstoqcbuf,sizeof(sstoqcbuf));
26338+ substdio_fdbuf(&ssfromqc,read,3,ssfromqcbuf,sizeof(ssfromqcbuf));
26339+
26340+ fdout = 1; /* stdout */
26341+ fdin = 0;  /* stdin */
26342+ if (ndelay_on(fdout) == -1)
26343+ /* this is so stupid: NDELAY semantics should be default on write */
26344+   senddied(); /* drastic, but better than risking deadlock */
26345+
26346+ while (!stralloc_ready(&comm_buf,1024)) nomem();
26347+}
26348+
26349+int comm_canwrite(void)
26350+{
26351+ /* XXX: could allow a bigger buffer; say 10 recipients */
26352+ /* XXX: returns true if there is something in the buffer */
26353+ if (!flagsendalive) return 0;
26354+ if (comm_buf.s && comm_buf.len) return 1;
26355+ return 0;
26356+}
26357+
26358+void log1(char* x)
26359+{
26360+  int pos;
26361
26362+  pos = comm_buf.len;
26363+  if (!stralloc_cats(&comm_buf,"L")) goto fail;
26364+  if (!stralloc_cats(&comm_buf,x)) goto fail;
26365+  if (!stralloc_0(&comm_buf)) goto fail;
26366+  return;
26367
26368+fail:
26369+  /* either all or nothing */
26370+  comm_buf.len = pos;
26371+}
26372+
26373+void log3(char* x, char *y, char *z)
26374+{
26375+  int pos;
26376
26377+  pos = comm_buf.len;
26378+  if (!stralloc_cats(&comm_buf,"L")) goto fail;
26379+  if (!stralloc_cats(&comm_buf,x)) goto fail;
26380+  if (!stralloc_cats(&comm_buf,y)) goto fail;
26381+  if (!stralloc_cats(&comm_buf,z)) goto fail;
26382+  if (!stralloc_0(&comm_buf)) goto fail;
26383+  return;
26384
26385+fail:
26386+  /* either all or nothing */
26387+  comm_buf.len = pos;
26388+}
26389+
26390+void comm_write(unsigned long id, int flagchan[])
26391+{
26392+  int pos;
26393+  char s[CHANNELS+1];
26394+  int c;
26395
26396+  for (c = 0;c < CHANNELS;++c)
26397+  {
26398+      if (flagchan[c])
26399+      {
26400+          s[c] = '1';
26401+      }
26402+      else
26403+      {
26404+          s[c] = '0';
26405+      }
26406+  }
26407+  s[c] = 0;
26408
26409+  pos = comm_buf.len;
26410+  strnum[fmt_ulong(strnum,id)] = 0;
26411+  if (!stralloc_cats(&comm_buf,"D")) goto fail;
26412+  if (!stralloc_cats(&comm_buf,s)) goto fail;
26413+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
26414+  if (!stralloc_0(&comm_buf)) goto fail;
26415+  return;
26416
26417+fail:
26418+  /* either all or nothing */
26419+  comm_buf.len = pos;
26420+}
26421+
26422+static int issafe(char ch)
26423+{
26424+ if (ch == '%') return 0; /* general principle: allman's code is crap */
26425+ if (ch < 33) return 0;
26426+ if (ch > 126) return 0;
26427+ return 1;
26428+}
26429+
26430+void comm_info(unsigned long id, unsigned long size, char* from, unsigned long pid, unsigned long uid)
26431+{
26432+  int pos;
26433+  int i;
26434
26435+  pos = comm_buf.len;
26436+  if (!stralloc_cats(&comm_buf,"Linfo msg ")) goto fail;
26437+  strnum[fmt_ulong(strnum,id)] = 0;
26438+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
26439+  if (!stralloc_cats(&comm_buf,": bytes ")) goto fail;
26440+  strnum[fmt_ulong(strnum,size)] = 0;
26441+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
26442+  if (!stralloc_cats(&comm_buf," from <")) goto fail;
26443+  i = comm_buf.len;
26444+  if (!stralloc_cats(&comm_buf,from)) goto fail;
26445+  for (;i < comm_buf.len;++i)
26446+    if (comm_buf.s[i] == '\n')
26447+      comm_buf.s[i] = '/';
26448+    else
26449+      if (!issafe(comm_buf.s[i]))
26450+       comm_buf.s[i] = '_';
26451+  if (!stralloc_cats(&comm_buf,"> qp ")) goto fail;
26452+  strnum[fmt_ulong(strnum,pid)] = 0;
26453+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
26454+  if (!stralloc_cats(&comm_buf," uid ")) goto fail;
26455+  strnum[fmt_ulong(strnum,uid)] = 0;
26456+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
26457+  if (!stralloc_cats(&comm_buf,"\n")) goto fail;
26458+  if (!stralloc_0(&comm_buf)) goto fail;
26459+  return;
26460
26461+fail:
26462+  /* either all or nothing */
26463+  comm_buf.len = pos;
26464+}
26465+
26466+void comm_exit(void)
26467+{
26468+  int w;
26469
26470+  /* if it fails exit, we have already stoped */
26471+  if (!stralloc_cats(&comm_buf,"X")) _exit(1);
26472+  if (!stralloc_0(&comm_buf)) _exit(1);
26473+}
26474+
26475+void comm_selprep(int *nfds, fd_set *wfds, fd_set *rfds)
26476+{
26477+  if (flagsendalive) {
26478+    if (flagstopasap && comm_canwrite() == 0)
26479+      comm_exit();
26480+    if (comm_canwrite()) {
26481+      FD_SET(fdout,wfds);
26482+      if (*nfds <= fdout)
26483+       *nfds = fdout + 1;
26484+    }
26485+    FD_SET(fdin,rfds);
26486+    if (*nfds <= fdin)
26487+      *nfds = fdin + 1;
26488+  }
26489+}
26490+
26491+void comm_do(fd_set *wfds, fd_set *rfds)
26492+{
26493+  /* first write then read */
26494+  if (flagsendalive)
26495+    if (comm_canwrite())
26496+      if (FD_ISSET(fdout,wfds)) {
26497+       int w;
26498+       int len;
26499+       len = comm_buf.len;
26500+       w = write(fdout,comm_buf.s + comm_pos,len - comm_pos);
26501+       if (w <= 0) {
26502+         if ((w == -1) && (errno == error_pipe))
26503+           senddied();
26504+       } else {
26505+         comm_pos += w;
26506+         if (comm_pos == len) {
26507+           comm_buf.len = 0;
26508+           comm_pos = 0;
26509+         }
26510+       }
26511+      }
26512+  if (flagsendalive)
26513+    if (FD_ISSET(fdin,rfds)) {
26514+      /* there are only two messages 'H' and 'X' */
26515+      char c;
26516+      int r;
26517+      r = read(fdin, &c, 1);
26518+      if (r <= 0) {
26519+       if ((r == -1) && (errno != error_intr))
26520+         senddied();
26521+      } else {
26522+       switch(c) {
26523+         case 'H':
26524+           sighup();
26525+           break;
26526+         case 'X':
26527+           sigterm();
26528+           break;
26529+         default:
26530+           log1("warning: qmail-todo: qmail-send speaks an obscure dialect\n");
26531+           break;
26532+       }
26533+      }
26534+    }
26535+}
26536+
26537+/* this file is not so long ------------------------------------------ TODO */
26538+
26539+datetime_sec nexttodorun;
26540+/* DIR *tododir;  if 0, have to opendir again */
26541+int flagtododir = 0; /* if 0, have to readsubdir_init again */
26542+readsubdir todosubdir;
26543+stralloc todoline = {0};
26544+char todobuf[SUBSTDIO_INSIZE];
26545+char todobufinfo[512];
26546+char todobufchan[CHANNELS][1024];
26547+
26548+void todo_init(void)
26549+{
26550+/*  tododir = 0; */
26551+ flagtododir = 0;
26552+ nexttodorun = now();
26553+ trigger_set();
26554+}
26555+
26556+void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup)
26557+{
26558+ if (flagstopasap) return;
26559+ trigger_selprep(nfds,rfds);
26560+/*  if (tododir) *wakeup = 0; */
26561+ if (flagtododir) *wakeup = 0;
26562+ if (*wakeup > nexttodorun) *wakeup = nexttodorun;
26563+}
26564+
26565+void todo_do(fd_set *rfds)
26566+{
26567+ struct stat st;
26568+ substdio ss; int fd;
26569+ substdio ssinfo; int fdinfo;
26570+ substdio sschan[CHANNELS];
26571+ int fdchan[CHANNELS];
26572+ int flagchan[CHANNELS];
26573+ char ch;
26574+ int match;
26575+ unsigned long id;
26576+/* unsigned int len;
26577+ direntry *d; */
26578+ int z;
26579+ int c;
26580+ unsigned long uid;
26581+ unsigned long pid;
26582+
26583+ fd = -1;
26584+ fdinfo = -1;
26585+ for (c = 0;c < CHANNELS;++c) fdchan[c] = -1;
26586+
26587+ if (flagstopasap) return;
26588+
26589+/* if (!tododir) */
26590+ if (!flagtododir)
26591+  {
26592+   if (!trigger_pulled(rfds))
26593+     if (recent < nexttodorun)
26594+       return;
26595+   trigger_set();
26596+/*   tododir = opendir("todo");
26597+   if (!tododir)
26598+    {
26599+     pausedir("todo");
26600+     return;
26601+    } */
26602+   readsubdir_init(&todosubdir, "todo", pausedir);
26603+   flagtododir = 1;
26604+   nexttodorun = recent + SLEEP_TODO;
26605+  }
26606+
26607+/* d = readdir(tododir);
26608+ if (!d) */
26609+ switch(readsubdir_next(&todosubdir, &id))
26610+  {
26611+/*   closedir(tododir);
26612+   tododir = 0;
26613+   return; */
26614+       case 1:
26615+        break;
26616+       case 0:
26617+       flagtododir = 0;
26618+       default:
26619+        return;
26620+  }
26621+/* if (str_equal(d->d_name,".")) return;
26622+ if (str_equal(d->d_name,"..")) return;
26623+ len = scan_ulong(d->d_name,&id);
26624+ if (!len || d->d_name[len]) return;
26625+*/
26626+ fnmake_todo(id);
26627+
26628+ fd = open_read(fn.s);
26629+ if (fd == -1) { log3("warning: qmail-todo: unable to open ",fn.s,"\n"); return; }
26630+
26631+ fnmake_mess(id);
26632+ /* just for the statistics */
26633+ if (stat(fn.s,&st) == -1)
26634+  { log3("warning: qmail-todo: unable to stat ",fn.s,"\n"); goto fail; }
26635+
26636+ for (c = 0;c < CHANNELS;++c)
26637+  {
26638+   fnmake_chanaddr(id,c);
26639+   if (unlink(fn.s) == -1) if (errno != error_noent)
26640+    { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; }
26641+  }
26642+
26643+ fnmake_info(id);
26644+ if (unlink(fn.s) == -1) if (errno != error_noent)
26645+  { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; }
26646+
26647+ fdinfo = open_excl(fn.s);
26648+ if (fdinfo == -1)
26649+  { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; }
26650+
26651+ strnum[fmt_ulong(strnum,id)] = 0;
26652+ log3("new msg ",strnum,"\n");
26653+
26654+ for (c = 0;c < CHANNELS;++c) flagchan[c] = 0;
26655+
26656+ substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf));
26657+ substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo));
26658+
26659+ uid = 0;
26660+ pid = 0;
26661+
26662+ for (;;)
26663+  {
26664+   if (getln(&ss,&todoline,&match,'\0') == -1)
26665+    {
26666+     /* perhaps we're out of memory, perhaps an I/O error */
26667+     fnmake_todo(id);
26668+     log3("warning: qmail-todo: trouble reading ",fn.s,"\n"); goto fail;
26669+    }
26670+   if (!match) break;
26671+
26672+   switch(todoline.s[0])
26673+    {
26674+     case 'u':
26675+       scan_ulong(todoline.s + 1,&uid);
26676+       break;
26677+     case 'p':
26678+       scan_ulong(todoline.s + 1,&pid);
26679+       break;
26680+     case 'F':
26681+       if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1)
26682+       {
26683+        fnmake_info(id);
26684+         log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail;
26685+       }
26686+       comm_info(id, (unsigned long) st.st_size, todoline.s + 1, pid, uid);
26687+       break;
26688+     case 'T':
26689+       c = rewrite(todoline.s + 1);
26690+       if (c == 0) { nomem(); goto fail; }
26691+       c--;
26692+       if (fdchan[c] == -1)
26693+       {
26694+        fnmake_chanaddr(id,c);
26695+        fdchan[c] = open_excl(fn.s);
26696+        if (fdchan[c] == -1)
26697+          { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; }
26698+        substdio_fdbuf(&sschan[c]
26699+          ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c]));
26700+        flagchan[c] = 1;
26701+       }
26702+       if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1)
26703+        {
26704+        fnmake_chanaddr(id,c);
26705+         log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail;
26706+        }
26707+       break;
26708+     default:
26709+       fnmake_todo(id);
26710+       log3("warning: qmail-todo: unknown record type in ",fn.s,"\n"); goto fail;
26711+    }
26712+  }
26713+
26714+ close(fd); fd = -1;
26715+
26716+ fnmake_info(id);
26717+ if (substdio_flush(&ssinfo) == -1)
26718+  { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; }
26719+ if (fsync(fdinfo) == -1)
26720+  { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; }
26721+ close(fdinfo); fdinfo = -1;
26722+
26723+ for (c = 0;c < CHANNELS;++c)
26724+   if (fdchan[c] != -1)
26725+    {
26726+     fnmake_chanaddr(id,c);
26727+     if (substdio_flush(&sschan[c]) == -1)
26728+      { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; }
26729+     if (fsync(fdchan[c]) == -1)
26730+      { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; }
26731+     close(fdchan[c]); fdchan[c] = -1;
26732+    }
26733+
26734+ fnmake_todo(id);
26735+ if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
26736+ if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
26737+ if (ch != '+')
26738+  {
26739+   log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
26740+   return;
26741+  }
26742+
26743+ comm_write(id, flagchan);
26744+
26745+ return;
26746+
26747+ fail:
26748+ if (fd != -1) close(fd);
26749+ if (fdinfo != -1) close(fdinfo);
26750+ for (c = 0;c < CHANNELS;++c)
26751+   if (fdchan[c] != -1) close(fdchan[c]);
26752+}
26753+
26754+/* this file is too long ---------------------------------------------- MAIN */
26755+
26756+int getcontrols(void)
26757+{
26758+ int c;
26759+
26760+ if (control_init() == -1) return 0;
26761+ if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0;
26762+ if (control_readfile(&locals,"control/locals",1) != 1) return 0;
26763+ if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0;
26764+ switch(control_readfile(&percenthack,"control/percenthack",0))
26765+  {
26766+   case -1: return 0;
26767+   case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break;
26768+   case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break;
26769+  }
26770+ switch(control_readfile(&vdoms,"control/virtualdomains",0))
26771+  {
26772+   case -1: return 0;
26773+   case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break;
26774+   case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break;
26775+  }
26776+
26777+ for (c = 0;c < SUPPL_CHANNELS;++c)
26778+ {
26779+     strnum[fmt_uint(strnum,c)] = 0;
26780+     if (!stralloc_copys(&fname,"control/suppls")) return 0;
26781+     if (!stralloc_cats(&fname,strnum)) return 0;
26782+     if (!stralloc_0(&fname)) return 0;
26783+     switch (control_readfile(&suppls[c],fname.s,0))
26784+     {
26785+         case -1: return 0;
26786+         case 0: if (!constmap_init(&mapsuppl[c],"",0,0)) return 0; break;
26787+         case 1: if (!constmap_init(&mapsuppl[c],suppls[c].s,suppls[c].len,0)) return 0; break;
26788+     }
26789+ }
26790+
26791+ return 1;
26792+}
26793+
26794+stralloc newlocals = {0};
26795+stralloc newvdoms = {0};
26796+
26797+void regetcontrols(void)
26798+{
26799+ int r;
26800+ int c;
26801+
26802+ if (control_readfile(&newlocals,"control/locals",1) != 1)
26803+  { log1("alert: qmail-todo: unable to reread control/locals\n"); return; }
26804+ r = control_readfile(&newvdoms,"control/virtualdomains",0);
26805+ if (r == -1)
26806+  { log1("alert: qmail-todo: unable to reread control/virtualdomains\n"); return; }
26807+
26808+ constmap_free(&maplocals);
26809+ constmap_free(&mapvdoms);
26810+
26811+ while (!stralloc_copy(&locals,&newlocals)) nomem();
26812+ while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem();
26813+
26814+ if (r)
26815+  {
26816+   while (!stralloc_copy(&vdoms,&newvdoms)) nomem();
26817+   while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem();
26818+  }
26819+ else
26820+   while (!constmap_init(&mapvdoms,"",0,1)) nomem();
26821+
26822+ for (c = 0;c < SUPPL_CHANNELS;++c)
26823+ {
26824+     strnum[fmt_uint(strnum,c)] = 0;
26825+     if (!stralloc_copys(&fname,"control/suppls")) nomem();
26826+     if (!stralloc_cats(&fname,strnum)) nomem();
26827+     if (!stralloc_0(&fname)) nomem();
26828+     r = control_readfile(&newsuppls[c],fname.s,0);
26829+     if (r == -1)
26830+      { log3("alert: qmail-todo: unable to reread ", fname.s, "\n"); return; }
26831+
26832+     constmap_free(&mapsuppl[c]);
26833+
26834+     if (r)
26835+      {
26836+       while (!stralloc_copy(&suppls[c],&newsuppls[c])) nomem();
26837+       while (!constmap_init(&mapsuppl[c],suppls[c].s,suppls[c].len,0)) nomem();
26838+      }
26839+     else
26840+       while (!constmap_init(&mapsuppl[c],"",0,0)) nomem();
26841+ }
26842+
26843+}
26844+
26845+void reread(void)
26846+{
26847+ if (chdir(auto_qmail) == -1)
26848+  {
26849+   log1("alert: qmail-todo: unable to reread controls: unable to switch to home directory\n");
26850+   return;
26851+  }
26852+ regetcontrols();
26853+ while (chdir("queue") == -1)
26854+  {
26855+   log1("alert: qmail-todo: unable to switch back to queue directory; HELP! sleeping...\n");
26856+   sleep(10);
26857+  }
26858+}
26859+
26860+static int static_i = 0;
26861+static int static_j = 0;
26862+void channels_init(void)
26863+{
26864+    for (static_i=0;static_i<SUPPL_CHANNELS;static_i++)
26865+        suppls[static_i].s = 0;
26866+
26867+    for (static_i=0;static_i<SUPPL_CHANNELS;static_i++)
26868+        newsuppls[static_i].s = 0;
26869+
26870+    chanaddr[0] = "local/";
26871+    chanaddr[1] = "remote/";
26872+    static_j = 0;
26873+    for (static_i=2;static_i<CHANNELS;static_i++)
26874+    {
26875+        stralloc fnc = {0};
26876+        strnum[fmt_uint(strnum,static_j++)] = 0;
26877+        if (!stralloc_copys(&fname,QDIR_BASENAME)) nomem();
26878+        if (!stralloc_cats(&fname,strnum)) nomem();
26879+        if (!stralloc_cats(&fname,"/")) nomem();
26880+        if (!stralloc_0(&fname)) nomem();
26881+        if (!stralloc_copy(&fnc,&fname)) nomem();
26882+        chanaddr[static_i] = fnc.s;
26883+    }
26884+}
26885+
26886+
26887+void main()
26888+{
26889+ datetime_sec wakeup;
26890+ fd_set rfds;
26891+ fd_set wfds;
26892+ int nfds;
26893+ struct timeval tv;
26894+ int r;
26895+ char c;
26896+
26897+ channels_init();
26898+
26899+ if (chdir(auto_qmail) == -1)
26900+  { log1("alert: qmail-todo: cannot start: unable to switch to home directory\n"); _exit(111); }
26901+ if (!getcontrols())
26902+  { log1("alert: qmail-todo: cannot start: unable to read controls\n"); _exit(111); }
26903+ if (chdir("queue") == -1)
26904+  { log1("alert: qmail-todo: cannot start: unable to switch to queue directory\n"); _exit(111); }
26905+ sig_pipeignore();
26906+ umask(077);
26907+
26908+ fnmake_init();
26909+
26910+ todo_init();
26911+ comm_init();
26912+
26913+ do {
26914+   r = read(fdin, &c, 1);
26915+   if ((r == -1) && (errno != error_intr))
26916+     _exit(100); /* read failed probably qmail-send died */
26917+ } while (r =! 1); /* we assume it is a 'S' */
26918+
26919+ for (;;)
26920+  {
26921+   recent = now();
26922+
26923+   if (flagreadasap) { flagreadasap = 0; reread(); }
26924+   if (!flagsendalive) {
26925+     /* qmail-send finaly exited, so do the same. */
26926+     if (flagstopasap) _exit(0);
26927+     /* qmail-send died. We can not log and we can not work therefor _exit(1). */
26928+     _exit(1);
26929+   }
26930+
26931+   wakeup = recent + SLEEP_FOREVER;
26932+   FD_ZERO(&rfds);
26933+   FD_ZERO(&wfds);
26934+   nfds = 1;
26935+
26936+   todo_selprep(&nfds,&rfds,&wakeup);
26937+   comm_selprep(&nfds,&wfds,&rfds);
26938+
26939+   if (wakeup <= recent) tv.tv_sec = 0;
26940+   else tv.tv_sec = wakeup - recent + SLEEP_FUZZ;
26941+   tv.tv_usec = 0;
26942+
26943+   if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1)
26944+     if (errno == error_intr)
26945+       ;
26946+     else
26947+       log1("warning: qmail-todo: trouble in select\n");
26948+   else
26949+    {
26950+     recent = now();
26951+
26952+     todo_do(&rfds);
26953+     comm_do(&wfds, &rfds);
26954+    }
26955+  }
26956+  /* NOTREACHED */
26957+}
26958+
26959diff -ruN ../netqmail-1.06-original/qmail-upq.sh netqmail-1.06/qmail-upq.sh
26960--- ../netqmail-1.06-original/qmail-upq.sh      1998-06-15 12:53:16.000000000 +0200
26961+++ netqmail-1.06/qmail-upq.sh  2019-06-26 16:39:31.579826904 +0200
26962@@ -1,6 +1,6 @@
26963 cd QMAIL
26964 cd queue
26965-for dir in mess info local remote
26966+for dir in mess info local remote suppl*
26967 do
26968   ( cd $dir; find . -type f -print ) | (
26969     cd $dir
26970diff -ruN ../netqmail-1.06-original/qmail.c netqmail-1.06/qmail.c
26971--- ../netqmail-1.06-original/qmail.c   2007-11-30 21:22:54.000000000 +0100
26972+++ netqmail-1.06/qmail.c       2019-02-27 20:57:13.404024915 +0100
26973@@ -23,22 +23,32 @@
26974 {
26975   int pim[2];
26976   int pie[2];
26977+  int pic[2], errfd;
26978+  char *x;
26979 
26980   setup_qqargs();
26981 
26982   if (pipe(pim) == -1) return -1;
26983   if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
26984-
26985+  if (pipe(pic) == -1) { close(pim[0]); close(pim[1]); close(pie[0]); close(pie[1]); return -1; }
26986+
26987   switch(qq->pid = vfork()) {
26988     case -1:
26989       close(pim[0]); close(pim[1]);
26990       close(pie[0]); close(pie[1]);
26991-      return -1;
26992+      close(pic[0]); close(pic[1]);
26993+    return -1;
26994     case 0:
26995       close(pim[1]);
26996       close(pie[1]);
26997+      close(pic[0]); /*- we want to receive data */
26998       if (fd_move(0,pim[0]) == -1) _exit(120);
26999       if (fd_move(1,pie[0]) == -1) _exit(120);
27000+      if (!(x = env_get("ERROR_FD")))
27001+        errfd = CUSTOM_ERR_FD;
27002+      else
27003+        scan_int(x, &errfd);
27004+      if (fd_move(errfd, pic[1]) == -1) _exit(120);
27005       if (chdir(auto_qmail) == -1) _exit(61);
27006       execv(*binqqargs,binqqargs);
27007       _exit(120);
27008@@ -46,6 +56,7 @@
27009 
27010   qq->fdm = pim[1]; close(pim[0]);
27011   qq->fde = pie[1]; close(pie[0]);
27012+  qq->fdc = pic[0]; close(pic[1]);
27013   substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf));
27014   qq->flagerr = 0;
27015   return 0;
27016@@ -93,11 +104,27 @@
27017 {
27018   int wstat;
27019   int exitcode;
27020+  char ch;
27021+  static char errstr[256];
27022+  int len = 0;
27023 
27024   qmail_put(qq,"",1);
27025   if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
27026   close(qq->fde);
27027 
27028+  substdio_fdbuf(&qq->ss, read, qq->fdc, qq->buf, sizeof(qq->buf));
27029+
27030+  while (substdio_bget(&qq->ss, &ch, 1) && len < 255)
27031+  {
27032+    errstr[len]=ch;
27033+    len++;
27034+  }
27035+
27036+  if (len > 0) errstr[len]='\0'; /* add str-term */
27037+ /* dkim +     errstr[len] = 0; */  /* add str-term */
27038+
27039+  close(qq->fdc);
27040+
27041   if (wait_pid(&wstat,qq->pid) != qq->pid)
27042     return "Zqq waitpid surprise (#4.3.0)";
27043   if (wait_crashed(wstat))
27044@@ -108,6 +135,7 @@
27045     case 115: /* compatibility */
27046     case 11: return "Denvelope address too long for qq (#5.1.3)";
27047     case 31: return "Dmail server permanently rejected message (#5.3.0)";
27048+    case 32: return "DPrivate key file does not exist (#5.3.5)";
27049     case 51: return "Zqq out of memory (#4.3.0)";
27050     case 52: return "Zqq timeout (#4.3.0)";
27051     case 53: return "Zqq write error or disk full (#4.3.0)";
27052@@ -127,10 +155,14 @@
27053     case 74: return "Zcommunication with mail server failed (#4.4.2)";
27054     case 91: /* fall through */
27055     case 81: return "Zqq internal bug (#4.3.0)";
27056+    case 82: /*- simscan exits with 82 */
27057+       case 88: /*- custom error */
27058+                if (len > 2)
27059+                       return errstr;
27060     case 120: return "Zunable to exec qq (#4.3.0)";
27061     default:
27062       if ((exitcode >= 11) && (exitcode <= 40))
27063-       return "Dqq permanent problem (#5.3.0)";
27064+        return "Dqq permanent problem (#5.3.0)";
27065       return "Zqq temporary problem (#4.3.0)";
27066   }
27067 }
27068diff -ruN ../netqmail-1.06-original/qmail.h netqmail-1.06/qmail.h
27069--- ../netqmail-1.06-original/qmail.h   1998-06-15 12:53:16.000000000 +0200
27070+++ netqmail-1.06/qmail.h       2019-02-27 20:57:13.404024915 +0100
27071@@ -3,11 +3,13 @@
27072 
27073 #include "substdio.h"
27074 
27075+#define CUSTOM_ERR_FD 4
27076 struct qmail {
27077   int flagerr;
27078   unsigned long pid;
27079   int fdm;
27080   int fde;
27081+  int fdc;
27082   substdio ss;
27083   char buf[1024];
27084 } ;
27085diff -ruN ../netqmail-1.06-original/qregex.c netqmail-1.06/qregex.c
27086--- ../netqmail-1.06-original/qregex.c  1970-01-01 01:00:00.000000000 +0100
27087+++ netqmail-1.06/qregex.c      2019-02-27 20:57:13.405024904 +0100
27088@@ -0,0 +1,239 @@
27089+/*
27090+ * $Log: qregex.c,v $
27091+ * Revision 1.13  2007-12-20 13:51:04+05:30  Cprogrammer
27092+ * removed compiler warning
27093+ *
27094+ * Revision 1.12  2005-08-23 17:41:36+05:30  Cprogrammer
27095+ * regex to be turned on only of QREGEX is defined to non-zero value
27096+ *
27097+ * Revision 1.11  2005-04-02 19:07:25+05:30  Cprogrammer
27098+ * use internal wildmat version
27099+ *
27100+ * Revision 1.10  2005-01-22 00:39:04+05:30  Cprogrammer
27101+ * added missing error handling
27102+ *
27103+ * Revision 1.9  2004-10-22 20:29:45+05:30  Cprogrammer
27104+ * added RCS id
27105+ *
27106+ * Revision 1.8  2004-09-21 23:48:18+05:30  Cprogrammer
27107+ * made matchregex() visible
27108+ * introduced dotChar (configurable dot char)
27109+ *
27110+ * Revision 1.7  2004-02-05 18:48:48+05:30  Cprogrammer
27111+ * changed curregex to static
27112+ *
27113+ * Revision 1.6  2003-12-23 23:22:53+05:30  Cprogrammer
27114+ * implicitly use wildcard if address starts with '@'
27115+ *
27116+ * Revision 1.5  2003-12-22 18:33:12+05:30  Cprogrammer
27117+ * added address_match()
27118+ *
27119+ * Revision 1.4  2003-12-22 13:21:08+05:30  Cprogrammer
27120+ * added text and pattern as part of error message
27121+ *
27122+ * Revision 1.3  2003-12-22 10:04:04+05:30  Cprogrammer
27123+ * conditional compilation of qregex
27124+ *
27125+ * Revision 1.2  2003-12-21 15:32:18+05:30  Cprogrammer
27126+ * added regerror
27127+ *
27128+ * Revision 1.1  2003-12-20 13:17:16+05:30  Cprogrammer
27129+ * Initial revision
27130+ *
27131+ * qregex (v2)
27132+ * $Id: qregex.c,v 1.13 2007-12-20 13:51:04+05:30 Cprogrammer Stab mbhangui $
27133+ *
27134+ * Author  : Evan Borgstrom (evan at unixpimps dot org)
27135+ * Created : 2001/12/14 23:08:16
27136+ * Modified: $Date: 2007-12-20 13:51:04+05:30 $
27137+ * Revision: $Revision: 1.13 $
27138+ *
27139+ * Do POSIX regex matching on addresses for anti-relay / spam control.
27140+ * It logs to the maillog
27141+ * See the qregex-readme file included with this tarball.
27142+ * If you didn't get this file in a tarball please see the following URL:
27143+ *  http://www.unixpimps.org/software/qregex
27144+ *
27145+ * qregex.c is released under a BSD style copyright.
27146+ * See http://www.unixpimps.org/software/qregex/copyright.html
27147+ *
27148+ * Note: this revision follows the coding guidelines set forth by the rest of
27149+ *       the qmail code and that described at the following URL.
27150+ *       http://cr.yp.to/qmail/guarantee.html
27151+ *
27152+ */
27153+#include "case.h"
27154+#include "scan.h"
27155+#include "stralloc.h"
27156+#include "constmap.h"
27157+#include "substdio.h"
27158+#include "byte.h"
27159+#include "env.h"
27160+#include <sys/types.h>
27161+#include <regex.h>
27162+#include <unistd.h>
27163+
27164+static int      wildmat_match(stralloc *, int, struct constmap *, int, stralloc *);
27165+static int      regex_match(stralloc *, int, stralloc *);
27166+int             wildmat_internal(char *, char *);
27167+
27168+static char     sserrbuf[512];
27169+static substdio sserr = SUBSTDIO_FDBUF(write, 2, sserrbuf, sizeof(sserrbuf));
27170+static char     dotChar = '@';
27171+
27172+int
27173+address_match(stralloc *addr, int bhfok, stralloc *bhf,
27174+       struct constmap *mapbhf, int bhpok, stralloc *bhp)
27175+{
27176+       char           *ptr;
27177+       int             x = 0;
27178+
27179+       case_lowerb(addr->s, addr->len); /*- convert into lower case */
27180+       if ((ptr = env_get("QREGEX")))
27181+               scan_int(ptr, &x);
27182+       if (ptr && x)
27183+               return (regex_match(addr, bhfok, bhf));
27184+       else
27185+               return (wildmat_match(addr, bhfok, mapbhf, bhpok, bhp));
27186+}
27187+
27188+int
27189+matchregex(char *text, char *regex)
27190+{
27191+       regex_t         qreg;
27192+       char            errbuf[512];
27193+       int             retval = 0;
27194+
27195+#define REGCOMP(X,Y)    regcomp(&X, Y, REG_EXTENDED|REG_ICASE)
27196+       /*- build the regex */
27197+       if ((retval = REGCOMP(qreg, regex)) != 0)
27198+       {
27199+               regerror(retval, &qreg, errbuf, sizeof(errbuf));
27200+               regfree(&qreg);
27201+               if (substdio_puts(&sserr, text) == -1)
27202+                       return (-retval);
27203+               if (substdio_puts(&sserr, ": ") == -1)
27204+                       return (-retval);
27205+               if (substdio_puts(&sserr, regex) == -1)
27206+                       return (-retval);
27207+               if (substdio_puts(&sserr, ": ") == -1)
27208+                       return (-retval);
27209+               if (substdio_puts(&sserr, errbuf) == -1)
27210+                       return (-retval);
27211+               if (substdio_puts(&sserr, "\n") == -1)
27212+                       return (-retval);
27213+               if (substdio_flush(&sserr) == -1)
27214+                       return (-retval);
27215+               return (-retval);
27216+       }
27217+       /*- execute the regex */
27218+#define REGEXEC(X,Y)    regexec(&X, Y, (size_t) 0, (regmatch_t *) 0, (int) 0)
27219+       retval = REGEXEC(qreg, text);
27220+       regfree(&qreg);
27221+       return (retval == REG_NOMATCH ? 0 : 1);
27222+}
27223+
27224+static int
27225+wildmat_match(stralloc * addr, int mapfile, struct constmap *ptrmap, int patfile, stralloc *wildcard)
27226+{
27227+       int             i = 0;
27228+       int             j = 0;
27229+       int             k = 0;
27230+       char            subvalue;
27231+
27232+       if (mapfile)
27233+       {
27234+               if (constmap(ptrmap, addr->s, addr->len - 1))
27235+                       return 1;
27236+               if ((j = byte_rchr(addr->s, addr->len, dotChar)) < addr->len)
27237+               {
27238+                       if (constmap(ptrmap, addr->s + j, addr->len - j - 1))
27239+                               return 1;
27240+               }
27241+       }
27242+       /*- Include control file control/xxxxpatterns and evaluate with Wildmat check */
27243+       if (patfile && wildcard)
27244+       {
27245+               i = 0;
27246+               for (j = 0; j < wildcard->len; ++j)
27247+               {
27248+                       if (!wildcard->s[j])
27249+                       {
27250+                               subvalue = wildcard->s[i] != '!';
27251+                               if (!subvalue)
27252+                                       i++;
27253+                               if ((k != subvalue) && wildmat_internal(addr->s, wildcard->s + i))
27254+                                       k = subvalue;
27255+                               i = j + 1;
27256+                       }
27257+               }
27258+               return k;
27259+       }
27260+       return (0);
27261+}
27262+
27263+static int
27264+regex_match(stralloc * addr, int mapfile, stralloc *map)
27265+{
27266+       int             i = 0;
27267+       int             j = 0;
27268+       int             k = 0;
27269+       int             negate = 0, match;
27270+       static stralloc curregex = { 0 };
27271+
27272+       match = 0;
27273+       if (mapfile)
27274+       {
27275+               while (j < map->len)
27276+               {
27277+                       i = j;
27278+                       while ((map->s[i] != '\0') && (i < map->len))
27279+                               i++;
27280+                       if (map->s[j] == '!')
27281+                       {
27282+                               negate = 1;
27283+                               j++;
27284+                       }
27285+                       if (*(map->s + j) == dotChar)
27286+                       {
27287+                               if (!stralloc_copys(&curregex, ".*"))
27288+                                       return(-1);
27289+                               if (!stralloc_catb(&curregex, map->s + j, (i - j)))
27290+                                       return(-1);
27291+                       } else
27292+                       if (!stralloc_copyb(&curregex, map->s + j, (i - j)))
27293+                               return(-1);
27294+                       if (!stralloc_0(&curregex))
27295+                               return(-1);
27296+                       if((k = matchregex(addr->s, curregex.s)) == 1)
27297+                       {
27298+                               if (negate)
27299+                                       return(0);
27300+                               match = 1;
27301+                       }
27302+                       j = i + 1;
27303+                       negate = 0;
27304+               }
27305+       }
27306+       return (match);
27307+}
27308+
27309+void
27310+setdotChar(c)
27311+       char            c;
27312+{
27313+       dotChar = c;
27314+       return;
27315+}
27316+
27317+void
27318+getversion_qregex_c()
27319+{
27320+       static char    *x = "$Id: qregex.c,v 1.13 2007-12-20 13:51:04+05:30 Cprogrammer Stab mbhangui $";
27321+
27322+#ifdef INDIMAIL
27323+       x = sccsidh;
27324+#else
27325+       x++;
27326+#endif
27327+}
27328diff -ruN ../netqmail-1.06-original/qregex.h netqmail-1.06/qregex.h
27329--- ../netqmail-1.06-original/qregex.h  1970-01-01 01:00:00.000000000 +0100
27330+++ netqmail-1.06/qregex.h      2019-02-27 20:57:13.405024904 +0100
27331@@ -0,0 +1,24 @@
27332+/*
27333+ * $Log: qregex.h,v $
27334+ * Revision 1.3  2004-09-21 23:49:02+05:30  Cprogrammer
27335+ * added matchregex() and setdotChar()
27336+ *
27337+ * Revision 1.2  2003-12-22 18:35:26+05:30  Cprogrammer
27338+ * added address_match() function
27339+ *
27340+ * Revision 1.1  2003-12-20 13:17:45+05:30  Cprogrammer
27341+ * Initial revision
27342+ *
27343+ */
27344+/*
27345+ * simple header file for the matchregex prototype
27346+ */
27347+#ifndef _QREGEX_H_
27348+#define _QREGEX_H_
27349+#include "constmap.h"
27350+#include "stralloc.h"
27351+
27352+int             address_match(stralloc *, int, stralloc *, struct constmap *, int, stralloc *);
27353+int             matchregex(char *, char *);
27354+void            setdotChar(char);
27355+#endif
27356diff -ruN ../netqmail-1.06-original/readwrite.h netqmail-1.06/readwrite.h
27357--- ../netqmail-1.06-original/readwrite.h       1998-06-15 12:53:16.000000000 +0200
27358+++ netqmail-1.06/readwrite.h   2019-02-27 20:57:13.405024904 +0100
27359@@ -1,7 +1,6 @@
27360 #ifndef READWRITE_H
27361 #define READWRITE_H
27362 
27363-extern int read();
27364-extern int write();
27365+#include <unistd.h>
27366 
27367 #endif
27368diff -ruN ../netqmail-1.06-original/received.c netqmail-1.06/received.c
27369--- ../netqmail-1.06-original/received.c        1998-06-15 12:53:16.000000000 +0200
27370+++ netqmail-1.06/received.c    2019-02-27 20:57:13.405024904 +0100
27371@@ -21,6 +21,9 @@
27372   return 0;
27373 }
27374 
27375+char *relayclient;
27376+int relayclientlen;
27377+
27378 void safeput(qqt,s)
27379 struct qmail *qqt;
27380 char *s;
27381@@ -58,9 +61,12 @@
27382   qmail_puts(qqt," (");
27383   if (remoteinfo) {
27384     safeput(qqt,remoteinfo);
27385-    qmail_puts(qqt,"@");
27386   }
27387-  safeput(qqt,remoteip);
27388+  relayclient = env_get("RELAYCLIENT");
27389+  if (!relayclient) {
27390+    if (remoteinfo) { qmail_puts(qqt,"@"); }
27391+     safeput(qqt,remoteip);
27392+  }
27393   qmail_puts(qqt,")\n  by ");
27394   safeput(qqt,local);
27395   qmail_puts(qqt," with ");
27396diff -ruN ../netqmail-1.06-original/remoteinfo.c netqmail-1.06/remoteinfo.c
27397--- ../netqmail-1.06-original/remoteinfo.c      1998-06-15 12:53:16.000000000 +0200
27398+++ netqmail-1.06/remoteinfo.c  2019-02-27 20:57:13.405024904 +0100
27399@@ -44,12 +44,12 @@
27400   s = socket(AF_INET,SOCK_STREAM,0);
27401   if (s == -1) return 0;
27402 
27403-  byte_zero(&sin,sizeof(sin));
27404+/*  byte_zero(&sin,sizeof(sin));
27405   sin.sin_family = AF_INET;
27406   byte_copy(&sin.sin_addr,4,ipl);
27407   sin.sin_port = 0;
27408-  if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; }
27409-  if (timeoutconn(s,ipr,113,timeout) == -1) { close(s); return 0; }
27410+  if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; } */
27411+  if (timeoutconn(s,ipr,ipl,113,timeout) == -1) { close(s); return 0; }
27412   fcntl(s,F_SETFL,fcntl(s,F_GETFL,0) & ~O_NDELAY);
27413 
27414   len = 0;
27415diff -ruN ../netqmail-1.06-original/scan.h netqmail-1.06/scan.h
27416--- ../netqmail-1.06-original/scan.h    1998-06-15 12:53:16.000000000 +0200
27417+++ netqmail-1.06/scan.h        2019-02-27 20:57:13.405024904 +0100
27418@@ -2,6 +2,7 @@
27419 #define SCAN_H
27420 
27421 extern unsigned int scan_uint();
27422+extern unsigned int scan_int();
27423 extern unsigned int scan_xint();
27424 extern unsigned int scan_nbbint();
27425 extern unsigned int scan_ushort();
27426diff -ruN ../netqmail-1.06-original/scan_ulong.c netqmail-1.06/scan_ulong.c
27427--- ../netqmail-1.06-original/scan_ulong.c      1998-06-15 12:53:16.000000000 +0200
27428+++ netqmail-1.06/scan_ulong.c  2019-02-27 20:57:13.405024904 +0100
27429@@ -9,3 +9,43 @@
27430     { result = result * 10 + c; ++pos; }
27431   *u = result; return pos;
27432 }
27433+
27434+unsigned int
27435+scan_int(s, i)
27436+       register char  *s;
27437+       register int   *i;
27438+{
27439+       register unsigned int pos;
27440+       register int result;
27441+       register unsigned char c;
27442+       int             sign;
27443+
27444+       pos = 0;
27445+       result = 0;
27446+       sign = 1;
27447+       /*-
27448+        * determine sign of the number
27449+        */
27450+       switch (s[0])
27451+       {
27452+               case '\0':
27453+                       return 0;
27454+               case '-':
27455+                       ++pos;
27456+                       sign = -1;
27457+                       break;
27458+               case '+':
27459+                       ++pos;
27460+                       sign = 1;
27461+                       break;
27462+               default:
27463+                       break;
27464+       }
27465+       while ((c = (unsigned char)(s[pos] - '0')) < 10)
27466+       {
27467+               result = result * 10 + c;
27468+               ++pos;
27469+       }
27470+       *i = result * sign;
27471+       return pos;
27472+}
27473diff -ruN ../netqmail-1.06-original/scan_xlong.c netqmail-1.06/scan_xlong.c
27474--- ../netqmail-1.06-original/scan_xlong.c      1970-01-01 01:00:00.000000000 +0100
27475+++ netqmail-1.06/scan_xlong.c  2019-02-27 20:57:13.405024904 +0100
27476@@ -0,0 +1,47 @@
27477+/*
27478+ * $Log: scan_xlong.c,v $
27479+ * Revision 1.2  2005-06-15 22:35:48+05:30  Cprogrammer
27480+ * added RCS version information
27481+ *
27482+ * Revision 1.1  2005-06-15 22:11:59+05:30  Cprogrammer
27483+ * Initial revision
27484+ *
27485+ */
27486+#include "scan.h"
27487+
27488+static int
27489+fromhex(unsigned char c)
27490+{
27491+       if (c >= '0' && c <= '9')
27492+               return c - '0';
27493+       else
27494+       if (c >= 'A' && c <= 'F')
27495+               return c - 'A' + 10;
27496+       else
27497+       if (c >= 'a' && c <= 'f')
27498+               return c - 'a' + 10;
27499+       return -1;
27500+}
27501+
27502+unsigned int
27503+scan_xlong(char *src, unsigned long *dest)
27504+{
27505+       register const char *tmp = src;
27506+       register int    l = 0;
27507+       register unsigned char c;
27508+       while ((c = fromhex(*tmp)) < 16)
27509+       {
27510+               l = (l << 4) + c;
27511+               ++tmp;
27512+       }
27513+       *dest = l;
27514+       return tmp - src;
27515+}
27516+
27517+void
27518+getversion_scan_xlong_c()
27519+{
27520+       static char    *x = "$Id: scan_xlong.c,v 1.2 2005-06-15 22:35:48+05:30 Cprogrammer Stab mbhangui $";
27521+
27522+       x++;
27523+}
27524diff -ruN ../netqmail-1.06-original/select.h2 netqmail-1.06/select.h2
27525--- ../netqmail-1.06-original/select.h2 1998-06-15 12:53:16.000000000 +0200
27526+++ netqmail-1.06/select.h2     2019-02-27 20:57:13.405024904 +0100
27527@@ -1,6 +1,12 @@
27528 #ifndef SELECT_H
27529 #define SELECT_H
27530 
27531+#include <features.h>
27532+#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)
27533+# include <bits/types.h>
27534+# undef __FD_SETSIZE
27535+# define __FD_SETSIZE 131077
27536+#endif
27537 #include <sys/types.h>
27538 #include <sys/time.h>
27539 #include <sys/select.h>
27540diff -ruN ../netqmail-1.06-original/socket_v4mappedprefix.c netqmail-1.06/socket_v4mappedprefix.c
27541--- ../netqmail-1.06-original/socket_v4mappedprefix.c   1970-01-01 01:00:00.000000000 +0100
27542+++ netqmail-1.06/socket_v4mappedprefix.c       2019-02-27 20:57:13.406024893 +0100
27543@@ -0,0 +1,9 @@
27544+/*
27545+ * $Log: socket_v4mappedprefix.c,v $
27546+ * Revision 1.1  2005-06-15 22:12:51+05:30  Cprogrammer
27547+ * Initial revision
27548+ *
27549+ */
27550+#ifdef IPV6
27551+unsigned char V4mappedprefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
27552+#endif
27553diff -ruN ../netqmail-1.06-original/socket_v6any.c netqmail-1.06/socket_v6any.c
27554--- ../netqmail-1.06-original/socket_v6any.c    1970-01-01 01:00:00.000000000 +0100
27555+++ netqmail-1.06/socket_v6any.c        2019-02-27 20:57:13.406024893 +0100
27556@@ -0,0 +1,9 @@
27557+/*
27558+ * $Log: socket_v6loopback.c,v $
27559+ * Revision 1.1  2005-06-15 22:13:07+05:30  Cprogrammer
27560+ * Initial revision
27561+ *
27562+ */
27563+#ifdef IPV6
27564+const unsigned char V6any[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
27565+#endif
27566diff -ruN ../netqmail-1.06-original/softwarelicense1-1.html netqmail-1.06/softwarelicense1-1.html
27567--- ../netqmail-1.06-original/softwarelicense1-1.html   1970-01-01 01:00:00.000000000 +0100
27568+++ netqmail-1.06/softwarelicense1-1.html       2019-02-27 20:57:13.406024893 +0100
27569@@ -0,0 +1,59 @@
27570+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
27571+<html>
27572+<head>
27573+<title>Yahoo! DomainKeys Public License Agreement v1.0</title>
27574+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
27575+</head>
27576+
27577+<body>
27578+Yahoo! DomainKeys Public License Agreement v1.1<br>
27579+(this &quot;Agreement&quot;)
27580+<p>Copyright (c) 2004, Yahoo! Inc.<br>
27581+ All rights reserved.</p>
27582+
27583+<br>
27584+<a href=http://domainkeys.sourceforge.net/license/softwarelicense1-1.html>(Available online)</a>
27585+<br>
27586+
27587+
27588+<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>
27589+<p>1.    LICENSE GRANT. </p>
27590+<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>
27591+<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>
27592+<p>2.    DEFINITIONS. </p>
27593+<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>
27594+<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>
27595+<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>
27596+<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>
27597+<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>
27598+<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>
27599+<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>
27600+<p>
27601+ 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>
27602+<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>
27603+<p>3.    TERMS. </p>
27604+<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>
27605+<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>
27606+<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>
27607+<p>(b) preserve the copyright and other proprietary notices and disclaimers of DomainKeys Developers as they appear in the Licensed Code; and  </p>
27608+<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>
27609+<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>
27610+<p>3.4. You may choose to distribute Licensed Code or modifications under this Agreement or a different agreement, provided that:</p>
27611+<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>
27612+<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>
27613+<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>
27614+<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>
27615+<p>3.7. This Agreement and the rights hereunder will terminate: <br>
27616+ (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>
27617+<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>
27618+<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>
27619+<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>
27620+<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>
27621+<p>4.    <b>LEGAL DISCLAIMERS. </b></p>
27622+<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>
27623+<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>
27624+<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>
27625+
27626+</body>
27627+</html>
27628+
27629diff -ruN ../netqmail-1.06-original/spawn-filter.9 netqmail-1.06/spawn-filter.9
27630--- ../netqmail-1.06-original/spawn-filter.9    1970-01-01 01:00:00.000000000 +0100
27631+++ netqmail-1.06/spawn-filter.9        2019-02-27 20:57:13.406024893 +0100
27632@@ -0,0 +1,103 @@
27633+.TH spawn-filter 8
27634+.SH NAME
27635+spawn-filter \- Helper for running filters for qmail-local and qmail-remote
27636+.SH SYNOPSIS
27637+.B spawn-filter args
27638+.SH DESCRIPTION
27639+.B spawn-filter
27640+is a utility to help qmail run any filter during local or remote delivery. It
27641+can run any filter which expects to read mess on fd 0 and writes back the message on fd 1.
27642+The filter can be turned on individually for local and remote mails by defining
27643+.B QMAILLOCAL
27644+and
27645+.B QMAILREMOTE
27646+environment variables respectively in
27647+.B qmail-send
27648+supervise or rc script. If spawn-filter is invoked as qmail-local, it executes the
27649+original
27650+.B qmail-local
27651+after runing the mail through the filter. If spawn-filter is invoked as qmail-remote, it
27652+executes the original
27653+.B qmail-remote
27654+after running the mail through the filter. Hence QMAILLOCAL should be set as QMAILHOME/bin/spawn-filter
27655+for filtering local mails and QMAILREMOTE as QMAILHOME/bin/spawn-filter for filtering
27656+remote mails.
27657+
27658+Filters can be run by setting the environment variable
27659+.B FILTERARGS
27660+or by using a control file
27661+.BR filterargs.
27662+The environment variable overrides the control file.
27663+.B spawn-filter
27664+uses /bin/sh to run the filter (with arguments) specified by the FILTERARGS environment variable or the control file
27665+.BR filterargs .
27666+The environment variable FILTERARGS apply to both local and remote mails. For individual domain level control,
27667+it is best to set using the control file filterargs.
27668+
27669+.TP 5
27670+.I filterargs
27671+The format of this file is of the form
27672+.B domain:args
27673+for both local and remote mails.
27674+.B domain:remote:args
27675+for remote mails and
27676+.B domain:local:args
27677+for local mails.
27678+
27679+.EX
27680+indimail.org:remote:QMAILHOME/bin/dk-filter
27681+.EE
27682+
27683+.TP 0
27684+The sequence in which the filter program is run is given below
27685+
27686+.TP 5
27687+1. create two pipes and fork
27688+.TP 5
27689+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
27690+.TP 5
27691+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.
27692+.TP 5
27693+4. Wait for filter to exit and read read end of second pipe for any error messages.
27694+.TP 5
27695+5. Report success or failure
27696+.TP 0
27697+
27698+This gives the ability for the any filter program to read the mail message from descriptor 0 before
27699+passing it to qmail-local/qmail-remote through the pipe.
27700+
27701+.B spawn-filter
27702+will attempt to make the descriptor 0 seekable if the environment variable MAKE_SEEKABLE
27703+is defined. This may be necessary for certain filter programs which could do lseek().
27704+
27705+.B spawn-filter
27706+sets the environment variable
27707+.B DOMAIN
27708+to the recipient domain. This can be conveniently used in programs/scripts which get invoked by
27709+setting
27710+.B FILTERARGS
27711+environment variable or by rules in the control file
27712+.BR filterargs .
27713+
27714+.SH "EXIT CODES"
27715+.B spawn-filter
27716+exits 111 for any error or if it is not able to exec
27717+QMAILHOME/bin/qmail-local (for local mails) or
27718+QMAILHOME/bin/qmail-remote (for remote mails).
27719+
27720+.SH "SEE ALSO"
27721+qmail-lspawn(8),
27722+qmail-rspawn(8),
27723+qmail-local(8),
27724+qmail-remote(8),
27725+qmail-smtpd(8),
27726+qmail-control(5),
27727+qmail-queue(8)
27728+
27729+.SH "AUTHORS"
27730+
27731+Manvendra Bhangui.
27732+.SH PROBLEMS
27733+Problems with
27734+.B spawn-filter
27735+should be forwarded to "Manvendra Bhangui" <mbhangui@gmail.com>
27736diff -ruN ../netqmail-1.06-original/spawn-filter.c netqmail-1.06/spawn-filter.c
27737--- ../netqmail-1.06-original/spawn-filter.c    1970-01-01 01:00:00.000000000 +0100
27738+++ netqmail-1.06/spawn-filter.c        2020-04-09 19:45:45.390398843 +0200
27739@@ -0,0 +1,506 @@
27740+/*
27741+ * netqmail-version without spam filter
27742+ *
27743+ * $Log: spawn-filter.c,v $
27744+ * Revision 1.41  2009-04-03 11:42:48+05:30  Cprogrammer
27745+ * create pipe for error messages
27746+ *
27747+ * Revision 1.40  2009-04-02 15:17:54+05:30  Cprogrammer
27748+ * unset QMAILLOCAL in qmail-remote and unset QMAILREMOTE in qmail-local
27749+ *
27750+ * Revision 1.39  2008-06-12 08:40:55+05:30  Cprogrammer
27751+ * added rulesfile argument
27752+ *
27753+ * Revision 1.38  2008-05-25 17:16:43+05:30  Cprogrammer
27754+ * made message more readable by adding a blank space
27755+ *
27756+ * Revision 1.37  2007-12-20 13:51:54+05:30  Cprogrammer
27757+ * avoid loops with FILTERARGS, SPAMFILTERARGS
27758+ * removed compiler warning
27759+ *
27760+ * Revision 1.36  2006-06-07 14:11:28+05:30  Cprogrammer
27761+ * added SPAMEXT, SPAMHOST, SPAMSENDER, QQEH environment variable
27762+ * unset FILTERARGS before calling filters
27763+ *
27764+ * Revision 1.35  2006-01-22 10:14:45+05:30  Cprogrammer
27765+ * BUG fix for spam mails wrongly getting blackholed
27766+ *
27767+ * Revision 1.34  2005-08-23 17:36:48+05:30  Cprogrammer
27768+ * gcc 4 compliance
27769+ * delete sender in spam notification
27770+ *
27771+ * Revision 1.33  2005-04-02 19:07:47+05:30  Cprogrammer
27772+ * use internal wildmat version
27773+ *
27774+ * Revision 1.32  2004-11-22 19:50:53+05:30  Cprogrammer
27775+ * include regex.h after sys/types.h to avoid compilation prob on RH 7.3
27776+ *
27777+ * Revision 1.31  2004-10-22 20:30:35+05:30  Cprogrammer
27778+ * added RCS id
27779+ *
27780+ * Revision 1.30  2004-10-21 21:56:21+05:30  Cprogrammer
27781+ * change for two additional arguments to strerr_die()
27782+ *
27783+ * Revision 1.29  2004-10-11 14:06:14+05:30  Cprogrammer
27784+ * use control_readulong instead of control_readint
27785+ *
27786+ * Revision 1.28  2004-09-22 23:14:20+05:30  Cprogrammer
27787+ * replaced atoi() with scan_int()
27788+ *
27789+ * Revision 1.27  2004-09-08 10:54:49+05:30  Cprogrammer
27790+ * incorrect exit code in report() function for remote
27791+ * mails. Caused qmail-rspawn to report "Unable to run qmail-remote"
27792+ *
27793+ * Revision 1.26  2004-07-17 21:23:31+05:30  Cprogrammer
27794+ * change qqeh code in qmail-remote
27795+ *
27796+ * Revision 1.25  2004-07-15 23:40:46+05:30  Cprogrammer
27797+ * fixed compilation warning
27798+ *
27799+ * Revision 1.24  2004-07-02 16:15:25+05:30  Cprogrammer
27800+ * override control files rejectspam, spamredirect by
27801+ * environment variables REJECTSPAM and SPAMREDIRECT
27802+ * allow patterns in domain specification in the control files
27803+ * spamfilterargs, filterargs, rejectspam and spamredirect
27804+ *
27805+ * Revision 1.23  2004-06-03 22:58:34+05:30  Cprogrammer
27806+ * fixed compilation problem without indimail
27807+ *
27808+ * Revision 1.22  2004-05-23 22:18:17+05:30  Cprogrammer
27809+ * added envrules filename as argument
27810+ *
27811+ * Revision 1.21  2004-05-19 23:15:07+05:30  Cprogrammer
27812+ * added comments
27813+ *
27814+ * Revision 1.20  2004-05-12 22:37:47+05:30  Cprogrammer
27815+ * added check DATALIMIT check
27816+ *
27817+ * Revision 1.19  2004-05-03 22:17:36+05:30  Cprogrammer
27818+ * use QUEUE_BASE instead of auto_qmail
27819+ *
27820+ * Revision 1.18  2004-02-13 14:51:24+05:30  Cprogrammer
27821+ * added envrules
27822+ *
27823+ * Revision 1.17  2004-01-20 06:56:56+05:30  Cprogrammer
27824+ * unset FILTERARGS for notifications
27825+ *
27826+ * Revision 1.16  2004-01-20 01:52:08+05:30  Cprogrammer
27827+ * report string length corrected
27828+ *
27829+ * Revision 1.15  2004-01-10 09:44:36+05:30  Cprogrammer
27830+ * added comment for exit codes of bogofilter
27831+ *
27832+ * Revision 1.14  2004-01-08 00:32:49+05:30  Cprogrammer
27833+ * use TMPDIR environment variable for temporary directory
27834+ * send spam reports to central spam logger
27835+ *
27836+ * Revision 1.13  2003-12-30 00:44:42+05:30  Cprogrammer
27837+ * set argv[0] from spamfilterprog
27838+ *
27839+ * Revision 1.12  2003-12-22 18:34:25+05:30  Cprogrammer
27840+ * replaced spfcheck() with address_match()
27841+ *
27842+ * Revision 1.11  2003-12-20 01:35:06+05:30  Cprogrammer
27843+ * added wait_pid to prevent zombies
27844+ *
27845+ * Revision 1.10  2003-12-17 23:33:39+05:30  Cprogrammer
27846+ * improved logic for getting remote/local tokens
27847+ *
27848+ * Revision 1.9  2003-12-16 10:38:24+05:30  Cprogrammer
27849+ * fixed incorrect address being returned if filterargs contained local: or
27850+ * remote: directives
27851+ *
27852+ * Revision 1.8  2003-12-15 20:46:19+05:30  Cprogrammer
27853+ * added case 100 to bounce mail
27854+ *
27855+ * Revision 1.7  2003-12-15 13:51:44+05:30  Cprogrammer
27856+ * code to run additional filters using /bin/sh
27857+ *
27858+ * Revision 1.6  2003-12-14 11:36:18+05:30  Cprogrammer
27859+ * added option to blackhole spammers
27860+ *
27861+ * Revision 1.5  2003-12-13 21:08:46+05:30  Cprogrammer
27862+ * extensive rewrite
27863+ * common report() function for local/remote mails to report errors
27864+ *
27865+ * Revision 1.4  2003-12-12 20:20:55+05:30  Cprogrammer
27866+ * use -a option to prevent using header addresses
27867+ *
27868+ * Revision 1.3  2003-12-09 23:37:16+05:30  Cprogrammer
27869+ * change for spawn-filter to be called as qmail-local or qmail-remote
27870+ *
27871+ * Revision 1.2  2003-12-08 23:48:23+05:30  Cprogrammer
27872+ * new function getDomainToken() to retrieve domain specific values
27873+ * read rejectspam and spamredirect only if SPAMEXITCODE is set
27874+ *
27875+ * Revision 1.1  2003-12-07 13:02:00+05:30  Cprogrammer
27876+ * Initial revision
27877+ *
27878+ */
27879+#include "fmt.h"
27880+#include "str.h"
27881+#include "strerr.h"
27882+#include "env.h"
27883+#include "substdio.h"
27884+#include "subfd.h"
27885+#include "stralloc.h"
27886+#include "error.h"
27887+#include "control.h"
27888+#include "wait.h"
27889+#include "qregex.h"
27890+#include "auto_qmail.h"
27891+#include <regex.h>
27892+#include <unistd.h>
27893+#include <sys/stat.h>
27894+#include <sys/types.h>
27895+#include <fcntl.h>
27896+
27897+#define REGCOMP(X,Y)    regcomp(&X, Y, REG_EXTENDED|REG_ICASE)
27898+#define REGEXEC(X,Y)    regexec(&X, Y, (size_t) 0, (regmatch_t *) 0, (int) 0)
27899+
27900+static int      mkTempFile(int);
27901+static void     report(int, char *, char *, char *, char *, char *, char *);
27902+char           *getDomainToken(char *, stralloc *);
27903+static int      run_mailfilter(char *, char *, char **);
27904+int             wildmat_internal(char *, char *);
27905+
27906+static int      remotE;
27907+stralloc        sender = { 0 };
27908+stralloc        recipient = { 0 };
27909+
27910+static void
27911+report(int errCode, char *s1, char *s2, char *s3, char *s4, char *s5, char *s6)
27912+{
27913+       if (!remotE) /*- strerr_die does not return */
27914+               strerr_die(errCode, s1, s2, s3, s4, s5, s6, 0, 0, (struct strerr *) 0);
27915+       /*- h - hard, s - soft */
27916+       if (substdio_put(subfdoutsmall, errCode == 111 ? "s" : "h", 1) == -1)
27917+               _exit(111);
27918+       if (s1 && substdio_puts(subfdoutsmall, s1) == -1)
27919+               _exit(111);
27920+       if (s2 && substdio_puts(subfdoutsmall, s2) == -1)
27921+               _exit(111);
27922+       if (s3 && substdio_puts(subfdoutsmall, s3) == -1)
27923+               _exit(111);
27924+       if (s4 && substdio_puts(subfdoutsmall, s4) == -1)
27925+               _exit(111);
27926+       if (s5 && substdio_puts(subfdoutsmall, s5) == -1)
27927+               _exit(111);
27928+       if (s6 && substdio_puts(subfdoutsmall, s6) == -1)
27929+               _exit(111);
27930+       if (substdio_put(subfdoutsmall, "\0", 1) == -1)
27931+               _exit(111);
27932+       if (substdio_puts(subfdoutsmall,
27933+               errCode == 111 ?  "Zspawn-filter said: Message deferred" : "DGiving up on spawn-filter\n") == -1)
27934+               _exit(111);
27935+       if (substdio_put(subfdoutsmall, "\0", 1) == -1)
27936+               _exit(111);
27937+       substdio_flush(subfdoutsmall);
27938+       /*- For qmail-rspawn to stop complaining unable to run qmail-remote */
27939+       _exit(0);
27940+}
27941+
27942+void
27943+set_environ(char *host, char *sender, char *recipient)
27944+{
27945+       if (!env_put2("DOMAIN", host))
27946+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27947+       if (!env_put2("_SENDER", sender))
27948+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27949+       if (!env_put2("_RECIPIENT", recipient))
27950+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27951+       return;
27952+}
27953+
27954+static int
27955+run_mailfilter(char *domain, char *mailprog, char **argv)
27956+{
27957+       char            strnum[FMT_ULONG];
27958+       pid_t           filt_pid;
27959+       int             pipefd[2], pipefe[2];
27960+       int             wstat, filt_exitcode, len = 0;
27961+       char           *filterargs;
27962+       static stralloc filterdefs = { 0 };
27963+       static char     errstr[1024];
27964+       char            inbuf[1024];
27965+       char            ch;
27966+       static substdio errbuf;
27967+
27968+       if (!(filterargs = env_get("FILTERARGS")))
27969+       {
27970+               if (control_readfile(&filterdefs, "control/filterargs", 0) == -1)
27971+                       report(111, "spawn-filter: Unable to read filterargs: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27972+               filterargs = getDomainToken(domain, &filterdefs);
27973+       }
27974+       if (!filterargs)
27975+       {
27976+               execv(mailprog, argv);
27977+               report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0);
27978+               _exit(111); /*- To make compiler happy */
27979+       }
27980+       if (pipe(pipefd) == -1)
27981+               report(111, "spawn-filter: Trouble creating pipes: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27982+       if (pipe(pipefe) == -1)
27983+               report(111, "spawn-filter: Trouble creating pipes: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27984+       switch ((filt_pid = fork()))
27985+       {
27986+       case -1:
27987+               report(111, "spawn-filter: Trouble creating child filter: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27988+       case 0: /*- Filter Program */
27989+               set_environ(domain, sender.s, recipient.s);
27990+               /*- Mail content read from fd 0 */
27991+               if (mkTempFile(0))
27992+                       report(111, "spawn-filter: lseek error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27993+               /*- stdout will go here */
27994+               if (dup2(pipefd[1], 1) == -1 || close(pipefd[0]) == -1)
27995+                       report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27996+               if (pipefd[1] != 1)
27997+                       close(pipefd[1]);
27998+               /*- stderr will go here */
27999+               if (dup2(pipefe[1], 2) == -1 || close(pipefe[0]) == -1)
28000+                       report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28001+               if (pipefe[1] != 2)
28002+                       close(pipefe[1]);
28003+               /*- Avoid loop if program(s) defined by FILTERARGS call qmail-inject, etc */
28004+               if (!env_unset("FILTERARGS") || !env_unset("SPAMFILTER"))
28005+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28006+               execl("/bin/sh", "IndiMailfilter", "-c", filterargs, (char *) 0);
28007+               report(111, "spawn-filter: could not exec /bin/sh: ",  filterargs, ": ", error_str(errno), ". (#4.3.0)", 0);
28008+       default:
28009+               close(pipefe[1]);
28010+               close(pipefd[1]);
28011+               if (dup2(pipefd[0], 0))
28012+               {
28013+                       close(pipefd[0]);
28014+                       close(pipefe[0]);
28015+                       wait_pid(&wstat, filt_pid);
28016+                       report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28017+               }
28018+               if (pipefd[0] != 0)
28019+                       close(pipefd[0]);
28020+               if (mkTempFile(0))
28021+               {
28022+                       close(0);
28023+                       close(pipefe[0]);
28024+                       wait_pid(&wstat, filt_pid);
28025+                       report(111, "spawn-filter: lseek error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28026+               }
28027+               break;
28028+       }
28029+       /*- Process message if exit code is 0, bounce if 100 */
28030+       if (wait_pid(&wstat, filt_pid) != filt_pid)
28031+       {
28032+               close(0);
28033+               close(pipefe[0]);
28034+               report(111, "spawn-filter: waitpid surprise: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28035+       }
28036+       if (wait_crashed(wstat))
28037+       {
28038+               close(0);
28039+               close(pipefe[0]);
28040+               report(111, "spawn-filter: filter crashed: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28041+       }
28042+       switch (filt_exitcode = wait_exitcode(wstat))
28043+       {
28044+       case 0:
28045+               execv(mailprog, argv);
28046+               report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0);
28047+       case 100:
28048+               report(100, "Mail Rejected (#5.7.1)", 0, 0, 0, 0, 0);
28049+       default:
28050+               substdio_fdbuf(&errbuf, read, pipefe[0], inbuf, sizeof(inbuf));
28051+               for (len = 0; substdio_bget(&errbuf, &ch, 1) && len < (sizeof(errstr) - 1); len++)
28052+                       errstr[len] = ch;
28053+               errstr[len] = 0;
28054+               strnum[fmt_ulong(strnum, filt_exitcode)] = 0;
28055+               report(111, filterargs, ": (spawn-filter) exit code: ", strnum, *errstr ? ": " : 0, *errstr ? errstr : 0, ". (#4.3.0)");
28056+       }
28057+       /*- Not reached */
28058+       return(111);
28059+}
28060+
28061+char           *
28062+getDomainToken(char *domain, stralloc *sa)
28063+{
28064+       regex_t         qreg;
28065+       int             len, n, retval;
28066+       char           *ptr, *p;
28067+       char            errbuf[512];
28068+
28069+       for (len = 0, ptr = sa->s;len < sa->len;)
28070+       {
28071+               len += ((n = str_len(ptr)) + 1);
28072+               for (p = ptr;*p && *p != ':';p++);
28073+               if (*p == ':')
28074+               {
28075+                       *p = 0;
28076+                       /*- build the regex */
28077+                       if ((retval = str_diff(ptr, domain)))
28078+                       {
28079+                               if (env_get("QREGEX"))
28080+                               {
28081+                                       if ((retval = REGCOMP(qreg, ptr)) != 0)
28082+                                       {
28083+                                               regerror(retval, &qreg, errbuf, sizeof(errbuf));
28084+                                               regfree(&qreg);
28085+                                               report(111, "spawn-filter: ", ptr, ": ", errbuf, ". (#4.3.0)", 0);
28086+                                       }
28087+                                       retval = REGEXEC(qreg, domain);
28088+                                       regfree(&qreg);
28089+                               } else
28090+                                       retval = !wildmat_internal(domain, ptr);
28091+                       }
28092+                       *p = ':';
28093+                       if (!retval) /*- match occurred for domain or wildcard */
28094+                       {
28095+                               /* check for local/remote directives */
28096+                               if (remotE)
28097+                               {
28098+                                       if (!str_diffn(p + 1, "remote:", 7))
28099+                                               return (p + 8);
28100+                                       if (!str_diffn(p + 1, "local:", 6))
28101+                                       {
28102+                                               ptr = sa->s + len;
28103+                                               continue; /*- skip local directives for remote mails */
28104+                                       }
28105+                               } else
28106+                               {
28107+                                       if (!str_diffn(p + 1, "local:", 6))
28108+                                               return (p + 7);
28109+                                       if (!str_diffn(p + 1, "remote:", 7))
28110+                                       {
28111+                                               ptr = sa->s + len;
28112+                                               continue; /*- skip remote directives for remote mails */
28113+                                       }
28114+                               }
28115+                               return (p + 1);
28116+                       }
28117+               }
28118+               ptr = sa->s + len;
28119+       }
28120+       return ((char *) 0);
28121+}
28122+
28123+int
28124+mkTempFile(int seekfd)
28125+{
28126+       char            inbuf[2048], outbuf[2048], strnum[FMT_ULONG];
28127+       char           *tmpdir;
28128+       static stralloc tmpFile = {0};
28129+       struct substdio _ssin;
28130+       struct substdio _ssout;
28131+       int             fd;
28132+
28133+       if (lseek(seekfd, 0, SEEK_SET) == 0)
28134+               return (0);
28135+       if (errno == EBADF)
28136+       {
28137+               strnum[fmt_ulong(strnum, seekfd)] = 0;
28138+               report(111, "spawn-filter: fd ", strnum, ": ", error_str(errno), ". (#4.3.0)", 0);
28139+       }
28140+       if (!(tmpdir = env_get("TMPDIR")))
28141+               tmpdir = "/tmp";
28142+       if (!stralloc_copys(&tmpFile, tmpdir))
28143+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28144+       if (!stralloc_cats(&tmpFile, "/qmailFilterXXX"))
28145+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28146+       if (!stralloc_catb(&tmpFile, strnum, fmt_ulong(strnum, (unsigned long) getpid())))
28147+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28148+       if (!stralloc_0(&tmpFile))
28149+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28150+       if ((fd = open(tmpFile.s, O_RDWR | O_EXCL | O_CREAT, 0600)) == -1)
28151+               report(111, "spawn-filter: ", tmpFile.s, ": ", error_str(errno), ". (#4.3.0)", 0);
28152+       unlink(tmpFile.s);
28153+       substdio_fdbuf(&_ssout, write, fd, outbuf, sizeof(outbuf));
28154+       substdio_fdbuf(&_ssin, read, seekfd, inbuf, sizeof(inbuf));
28155+       switch (substdio_copy(&_ssout, &_ssin))
28156+       {
28157+       case -2: /*- read error */
28158+               report(111, "spawn-filter: read error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28159+       case -3: /*- write error */
28160+               report(111, "spawn-filter: write error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28161+       }
28162+       if (substdio_flush(&_ssout) == -1)
28163+               report(111, "spawn-filter: write error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28164+       if (dup2(fd, seekfd) == -1)
28165+               report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28166+       if (lseek(seekfd, 0, SEEK_SET) != 0)
28167+               report(111, "spawn-filter: lseek: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28168+       return (0);
28169+}
28170+
28171+int
28172+main(int argc, char **argv)
28173+{
28174+       char           *ptr, *mailprog, *domain, *ext;
28175+       int             len;
28176+
28177+       len = str_len(argv[0]);
28178+       for (ptr = argv[0] + len;*ptr != '/' && ptr != argv[0];ptr--);
28179+       if (*ptr && *ptr == '/')
28180+               ptr++;
28181+       ptr += 6;
28182+       if (*ptr == 'l') /*- qmail-local Filter */
28183+       {
28184+               mailprog = "bin/qmail-local";
28185+               ext = argv[6];
28186+               domain = argv[7];
28187+               remotE = 0;
28188+               if (!env_unset("QMAILREMOTE"))
28189+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28190+               /*- sender */
28191+               if (!stralloc_copys(&sender, argv[8]))
28192+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28193+               if (!stralloc_0(&sender))
28194+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28195+               /*- recipient */
28196+               if (*ext) { /*- EXT */
28197+                       if (!stralloc_copys(&recipient, ext))
28198+                               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28199+               } else /*- user */
28200+               if (!stralloc_copys(&recipient, argv[2]))
28201+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28202+               if (!stralloc_cats(&recipient, "@"))
28203+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28204+               if (!stralloc_cats(&recipient, domain))
28205+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28206+               if (!stralloc_0(&recipient))
28207+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28208+       } else
28209+       if (*ptr == 'r') /*- qmail-remote Filter */
28210+       {
28211+               mailprog = "bin/qmail-remote";
28212+               domain = argv[1];
28213+               remotE = 1;
28214+               if (!env_unset("QMAILLOCAL"))
28215+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28216+               /*- sender */
28217+               if (!stralloc_copys(&sender, argv[2]))
28218+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28219+               if (!stralloc_0(&sender))
28220+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28221+               /*- recipient */
28222+               if (!stralloc_copys(&recipient, argv[3]))
28223+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28224+               if (!stralloc_0(&recipient))
28225+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
28226+       } else
28227+       {
28228+               report(111, "spawn-filter: Incorrect usage. ", argv[0], " (#4.3.0)", 0, 0, 0);
28229+               _exit(111);
28230+       }
28231+       if (chdir(auto_qmail) == -1)
28232+               report(111, "spawn-filter: Unable to switch to ", auto_qmail, ": ", error_str(errno), ". (#4.3.0)", 0);
28233+       run_mailfilter(domain, mailprog, argv);
28234+       report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0);
28235+       /*- Not reached */
28236+       return(0);
28237+}
28238+
28239+void
28240+getversion_qmail_spawn_filter_c()
28241+{
28242+       static char    *x = "$Id: spawn-filter.c,v 1.41 2009-04-03 11:42:48+05:30 Cprogrammer Stab mbhangui $";
28243+
28244+       x++;
28245+}
28246diff -ruN ../netqmail-1.06-original/spawn.c netqmail-1.06/spawn.c
28247--- ../netqmail-1.06-original/spawn.c   2007-11-30 21:22:54.000000000 +0100
28248+++ netqmail-1.06/spawn.c       2019-02-27 20:57:13.407024882 +0100
28249@@ -1,4 +1,4 @@
28250-#include <sys/types.h>
28251+#include "select.h"
28252 #include <sys/stat.h>
28253 #include "sig.h"
28254 #include "wait.h"
28255@@ -7,7 +7,6 @@
28256 #include "str.h"
28257 #include "alloc.h"
28258 #include "stralloc.h"
28259-#include "select.h"
28260 #include "exit.h"
28261 #include "coe.h"
28262 #include "open.h"
28263@@ -64,7 +63,7 @@
28264 int flagreading = 1;
28265 char outbuf[1024]; substdio ssout;
28266 
28267-int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */
28268+int stage = 0; /* reading 0:delnum 1:delnum2 2:messid 3:sender 4:recip */
28269 int flagabort = 0; /* if 1, everything except delnum is garbage */
28270 int delnum;
28271 stralloc messid = {0};
28272@@ -74,6 +73,7 @@
28273 void err(s) char *s;
28274 {
28275  char ch; ch = delnum; substdio_put(&ssout,&ch,1);
28276+ ch = delnum >> 8; substdio_put(&ssout,&ch,1);
28277  substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1);
28278 }
28279 
28280@@ -156,16 +156,19 @@
28281     {
28282      case 0:
28283        delnum = (unsigned int) (unsigned char) ch;
28284-       messid.len = 0; stage = 1; break;
28285+       stage = 1; break;
28286      case 1:
28287+       delnum += (unsigned int) ((unsigned int) ch) << 8;
28288+       messid.len = 0; stage = 2; break;
28289+     case 2:
28290        if (!stralloc_append(&messid,&ch)) flagabort = 1;
28291        if (ch) break;
28292-       sender.len = 0; stage = 2; break;
28293-     case 2:
28294+       sender.len = 0; stage = 3; break;
28295+     case 3:
28296        if (!stralloc_append(&sender,&ch)) flagabort = 1;
28297        if (ch) break;
28298-       recip.len = 0; stage = 3; break;
28299-     case 3:
28300+       recip.len = 0; stage = 4; break;
28301+     case 4:
28302        if (!stralloc_append(&recip,&ch)) flagabort = 1;
28303        if (ch) break;
28304        docmd();
28305@@ -202,7 +205,8 @@
28306 
28307  initialize(argc,argv);
28308 
28309- ch = auto_spawn; substdio_putflush(&ssout,&ch,1);
28310+ ch = auto_spawn; substdio_put(&ssout,&ch,1);
28311+ ch = auto_spawn >> 8; substdio_putflush(&ssout,&ch,1);
28312 
28313  for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; }
28314 
28315@@ -237,7 +241,8 @@
28316           continue; /* read error on a readable pipe? be serious */
28317         if (r == 0)
28318          {
28319-           ch = i; substdio_put(&ssout,&ch,1);
28320+           char ch; ch = i; substdio_put(&ssout,&ch,1);
28321+           ch = i >> 8; substdio_put(&ssout,&ch,1);
28322           report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len);
28323           substdio_put(&ssout,"",1);
28324           substdio_flush(&ssout);
28325diff -ruN ../netqmail-1.06-original/spf.c netqmail-1.06/spf.c
28326--- ../netqmail-1.06-original/spf.c     1970-01-01 01:00:00.000000000 +0100
28327+++ netqmail-1.06/spf.c 2019-02-27 20:57:13.407024882 +0100
28328@@ -0,0 +1,877 @@
28329+#include "stralloc.h"
28330+#include "strsalloc.h"
28331+#include "alloc.h"
28332+#include "ip.h"
28333+#include "ipalloc.h"
28334+#include "ipme.h"
28335+#include "str.h"
28336+#include "fmt.h"
28337+#include "scan.h"
28338+#include "byte.h"
28339+#include "now.h"
28340+#include "dns.h"
28341+#include "case.h"
28342+#include "spf.h"
28343+
28344+#define SPF_EXT    -1
28345+#define SPF_SYNTAX -2
28346+
28347+#define WSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n')
28348+#define NXTOK(b, p, a) do { (b) = (p); \
28349+          while((p) < (a)->len && !WSPACE((a)->s[(p)])) ++(p); \
28350+          while((p) < (a)->len && WSPACE((a)->s[(p)])) (a)->s[(p)++] = 0; \
28351+        } while(0)
28352+
28353+/* this table and macro came from wget more or less */
28354+/* and was in turn stolen by me from libspf as is :) */
28355+const static unsigned char urlchr_table[256] =
28356+{
28357+  1,  1,  1,  1,   1,  1,  1,  1,   /* NUL SOH STX ETX  EOT ENQ ACK BEL */
28358+  1,  1,  1,  1,   1,  1,  1,  1,   /* BS  HT  LF  VT   FF  CR  SO  SI  */
28359+  1,  1,  1,  1,   1,  1,  1,  1,   /* DLE DC1 DC2 DC3  DC4 NAK SYN ETB */
28360+  1,  1,  1,  1,   1,  1,  1,  1,   /* CAN EM  SUB ESC  FS  GS  RS  US  */
28361+  1,  0,  1,  1,   0,  1,  1,  0,   /* SP  !   "   #    $   %   &   '   */
28362+  0,  0,  0,  1,   0,  0,  0,  1,   /* (   )   *   +    ,   -   .   /   */
28363+  0,  0,  0,  0,   0,  0,  0,  0,   /* 0   1   2   3    4   5   6   7   */
28364+  0,  0,  1,  1,   1,  1,  1,  1,   /* 8   9   :   ;    <   =   >   ?   */
28365+  1,  0,  0,  0,   0,  0,  0,  0,   /* @   A   B   C    D   E   F   G   */
28366+  0,  0,  0,  0,   0,  0,  0,  0,   /* H   I   J   K    L   M   N   O   */
28367+  0,  0,  0,  0,   0,  0,  0,  0,   /* P   Q   R   S    T   U   V   W   */
28368+  0,  0,  0,  1,   1,  1,  1,  0,   /* X   Y   Z   [    \   ]   ^   _   */
28369+  1,  0,  0,  0,   0,  0,  0,  0,   /* `   a   b   c    d   e   f   g   */
28370+  0,  0,  0,  0,   0,  0,  0,  0,   /* h   i   j   k    l   m   n   o   */
28371+  0,  0,  0,  0,   0,  0,  0,  0,   /* p   q   r   s    t   u   v   w   */
28372+  0,  0,  0,  1,   1,  1,  1,  1,   /* x   y   z   {    |   }   ~   DEL */
28373+
28374+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28375+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28376+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28377+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28378+
28379+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28380+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28381+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28382+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28383+};
28384+
28385+
28386+extern stralloc addr;
28387+extern stralloc helohost;
28388+extern char *local;
28389+
28390+extern stralloc spflocal;
28391+extern stralloc spfguess;
28392+extern stralloc spfexp;
28393+
28394+static stralloc sender_fqdn = {0};
28395+static stralloc explanation = {0};
28396+static stralloc expdomain = {0};
28397+static stralloc errormsg = {0};
28398+static char *received;
28399+
28400+static int recursion;
28401+static struct ip_address ip;
28402+
28403+static void hdr_pass() { received = "pass (%{xr}: %{xs} designates %{i} as permitted sender)"; };
28404+static void hdr_softfail() { received = "softfail (%{xr}: transitioning %{xs} does not designate %{i} as permitted sender)"; };
28405+static void hdr_fail() { received = "fail (%{xr}: %{xs} does not designate %{i} as permitted sender)"; };
28406+static void hdr_unknown() { received = "unknown (%{xr}: domain at %{d} does not designate permitted sender hosts)"; };
28407+static void hdr_neutral() { received = "neutral (%{xr}: %{i} is neither permitted nor denied by %{xs})"; };
28408+static void hdr_none() { received = "none (%{xr}: domain at %{d} does not designate permitted sender hosts)"; };
28409+static void hdr_unknown_msg(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown (%{xr}: %{xe})"; };
28410+static void hdr_ext(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown %{xe} (%{xr}: %{xs} uses mechanism not recognized by this client)"; };
28411+static void hdr_syntax() { received = "unknown (%{xr}: parse error in %{xs})"; };
28412+static void hdr_error(e) char *e; { stralloc_copys(&errormsg, e); received = "error (%{xr}: error in processing during lookup of %{d}: %{xe})"; };
28413+static void hdr_dns() { hdr_error("DNS problem"); }
28414+
28415+
28416+static int matchip(struct ip_address *net, int mask, struct ip_address *ip)
28417+{
28418+       int j;
28419+       int bytemask;
28420+
28421+       for (j = 0; j < 4 && mask > 0; ++j) {
28422+               if (mask > 8) bytemask = 8; else bytemask = mask;
28423+               mask -= bytemask;
28424+
28425+               if ((net->d[j] ^ ip->d[j]) & (0x100 - (1 << (8 - bytemask))))
28426+                       return 0;
28427+       }
28428+       return 1;
28429+}
28430+
28431+static int getipmask(char *mask, int ipv6) {
28432+       unsigned long r;
28433+       int pos;
28434+
28435+       if (!mask) return 32;
28436+
28437+       pos = scan_ulong(mask, &r);
28438+       if (!pos || (mask[pos] && !(mask[pos] == '/' && ipv6))) return -1;
28439+       if (r > 32) return -1;
28440+
28441+       return r;
28442+}
28443+
28444+int spfget(stralloc *spf, stralloc *domain)
28445+{
28446+       strsalloc ssa = {0};
28447+       int j;
28448+       int begin, pos, i;
28449+       int r = SPF_NONE;
28450+
28451+       spf->len = 0;
28452+
28453+       switch(dns_txt(&ssa, domain)) {
28454+               case DNS_MEM: return SPF_NOMEM;
28455+               case DNS_SOFT: hdr_dns(); return SPF_ERROR;
28456+               case DNS_HARD: return SPF_NONE;
28457+       }
28458+
28459+       for (j = 0;j < ssa.len;++j) {
28460+               pos = 0;
28461+
28462+               NXTOK(begin, pos, &ssa.sa[j]);
28463+               if (str_len(ssa.sa[j].s + begin) < 6) continue;
28464+               if (!byte_equal(ssa.sa[j].s + begin,6,"v=spf1")) continue;
28465+               if (ssa.sa[j].s[begin + 6]) {
28466+                       /* check for subversion */
28467+                       if (ssa.sa[j].s[begin + 6] != '.') continue;
28468+                       for(i = begin + 7;;++i)
28469+                               if (!(ssa.sa[j].s[i] >= '0' && ssa.sa[j].s[i] <= '9')) break;
28470+                       if (i == (begin + 7)) continue;
28471+                       if (ssa.sa[j].s[i]) continue;
28472+               }
28473+
28474+               if (spf->len > 0) {
28475+                       spf->len = 0;
28476+                       hdr_unknown_msg("Multiple SPF records returned");
28477+                       r = SPF_UNKNOWN;
28478+                       break;
28479+               }
28480+               if (!stralloc_0(&ssa.sa[j])) return SPF_NOMEM;
28481+               if (!stralloc_copys(spf,ssa.sa[j].s + pos)) return SPF_NOMEM;
28482+               r = SPF_OK;
28483+       }
28484+
28485+       for (j = 0;j < ssa.len;++j)
28486+               alloc_free(ssa.sa[j].s);
28487+       alloc_free(ssa.sa);
28488+       return r;
28489+}
28490+
28491+static int spf_ptr(char *spec, char *mask);
28492+
28493+int spfsubst(stralloc *expand, char *spec, char *domain)
28494+{
28495+       static char hexdigits[] = "0123456789abcdef";
28496+       stralloc sa = {0};
28497+       char ch;
28498+       int digits = -1;
28499+       int urlencode = 0;
28500+       int reverse = 0;
28501+       int start = expand->len;
28502+       int i, pos;
28503+       char *split = ".";
28504+
28505+       if (!stralloc_readyplus(&sa,0)) return 0;
28506+
28507+       if (*spec == 'x') { i = 1; ++spec; } else i = 0;
28508+       ch = *spec++;
28509+       if (!ch) { alloc_free(sa.s); return 1; }
28510+       if (ch >= 'A' && ch <= 'Z') { ch += 32; urlencode = 1; }
28511+       if (i) ch -= 32;
28512+       while(*spec >= '0' && *spec <= '9') {
28513+               if (digits < 0) digits = 0;
28514+               if (digits >= 1000000) { digits = 10000000; continue; }
28515+               digits = (digits * 10) + (*spec - '0');
28516+               spec++;
28517+       }
28518+
28519+       while((*spec >= 'a' && *spec <= 'z') || (*spec >= 'A' && *spec <= 'Z')) {
28520+               if (*spec == 'r') reverse = 1;
28521+               spec++;
28522+       }
28523+
28524+       if (*spec) split = spec;
28525+
28526+       switch(ch) {
28527+               case 'l':
28528+                       pos = byte_rchr(addr.s, addr.len, '@');
28529+                       if (pos < addr.len) {
28530+                               if (!stralloc_copyb(&sa, addr.s, pos)) return 0;
28531+                       } else
28532+                               if (!stralloc_copys(&sa, "postmaster")) return 0;
28533+                       break;
28534+               case 's':
28535+                       if (!stralloc_copys(&sa, addr.s)) return 0;
28536+                       break;
28537+               case 'o':
28538+                       pos = byte_rchr(addr.s, addr.len, '@') + 1;
28539+                       if (pos > addr.len) break;
28540+                       if (!stralloc_copys(&sa, addr.s + pos)) return 0;
28541+                       break;
28542+               case 'd':
28543+                       if (!stralloc_copys(&sa, domain)) return 0;
28544+                       break;
28545+               case 'i':
28546+                       if (!stralloc_ready(&sa, IPFMT)) return 0;
28547+                       sa.len = ip_fmt(sa.s, &ip);
28548+                       break;
28549+               case 't':
28550+                       if (!stralloc_ready(&sa, FMT_ULONG)) return 0;
28551+                       sa.len = fmt_ulong(sa.s, (unsigned long)now());
28552+                       break;
28553+               case 'p':
28554+                       if (!sender_fqdn.len)
28555+                               spf_ptr(domain, 0);
28556+                       if (sender_fqdn.len) {
28557+                               if (!stralloc_copy(&sa, &sender_fqdn)) return 0;
28558+                       } else
28559+                               if (!stralloc_copys(&sa, "unknown")) return 0;
28560+                       break;
28561+               case 'v':
28562+                       if (!stralloc_copys(&sa, "in-addr")) return 0;
28563+                       break;
28564+               case 'h':
28565+                       if (!stralloc_copys(&sa, helohost.s)) return 0; /* FIXME: FQDN? */
28566+                       break;
28567+               case 'E':
28568+                       if (errormsg.len && !stralloc_copy(&sa, &errormsg)) return 0;
28569+                       break;
28570+               case 'R':
28571+                       if (!stralloc_copys(&sa, local)) return 0;
28572+                       break;
28573+               case 'S':
28574+                       if (expdomain.len > 0) {
28575+                               if (!stralloc_copys(&sa, "SPF record at ")) return 0;
28576+                               if (!stralloc_cats(&sa, expdomain.s)) return 0;
28577+                       } else {
28578+                               if (!stralloc_copys(&sa, "local policy")) return 0;
28579+                       }
28580+                       break;
28581+       }
28582+
28583+       if (reverse) {
28584+               for(pos = 0; digits; ++pos) {
28585+                       pos += byte_cspn(sa.s + pos, sa.len - pos, split);
28586+                       if (pos >= sa.len) break;
28587+                       if (!--digits) break;
28588+               }
28589+
28590+               for(; pos > 0; pos = i - 1) {
28591+                       i = byte_rcspn(sa.s, pos, split) + 1;
28592+                       if (i > pos) i = 0;
28593+                       if (!stralloc_catb(expand, sa.s + i, pos - i)) return 0;
28594+                       if (i > 0 && !stralloc_append(expand, ".")) return 0;
28595+               }
28596+       } else {
28597+               for(pos = sa.len; digits; --pos) {
28598+                       i = byte_rcspn(sa.s, pos, split) + 1;
28599+                       if (i > pos) { pos = 0; break; }
28600+                       pos = i;
28601+                       if (!--digits) break;
28602+               }
28603+
28604+               if (!stralloc_catb(expand, sa.s + pos, sa.len - pos)) return 0;
28605+               if (split[0] != '.' || split[1])
28606+                       for(pos = 0; pos < expand->len; pos++) {
28607+                               pos += byte_cspn(expand->s + pos, expand->len - pos, split);
28608+                               if (pos < expand->len)
28609+                                       expand->s[pos] = '.';
28610+                       }
28611+       }
28612+
28613+       if (urlencode) {
28614+               stralloc_copyb(&sa, expand->s + start, expand->len - start);
28615+               expand->len = start;
28616+
28617+               for(pos = 0; pos < sa.len; ++pos) {
28618+                       ch = sa.s[pos];
28619+                       if (urlchr_table[(unsigned char)ch]) {
28620+                               if (!stralloc_readyplus(expand, 3)) return 0;
28621+                               expand->s[expand->len++] = '%';
28622+                               expand->s[expand->len++] = hexdigits[(unsigned char)ch >> 4];
28623+                               expand->s[expand->len++] = hexdigits[(unsigned char)ch & 15];
28624+                       } else
28625+                               if (!stralloc_append(expand, &ch)) return 0;
28626+               }
28627+       }
28628+
28629+       alloc_free(sa.s);
28630+       return 1;
28631+}
28632+
28633+int spfexpand(stralloc *sa, char *spec, char *domain)
28634+{
28635+       char *p;
28636+       char append;
28637+       int pos;
28638+
28639+       if (!stralloc_readyplus(sa, 0)) return 0;
28640+       sa->len = 0;
28641+
28642+       for(p = spec; *p; p++) {
28643+               append = *p;
28644+               if (*p == '%') {
28645+                       p++;
28646+                       switch(*p) {
28647+                               case '%': break;
28648+                               case '_': append = ' '; break;
28649+                               case '-': if (!stralloc_cats(sa, "%20")) return 0; continue;
28650+                               case '{':
28651+                                       pos = str_chr(p, '}');
28652+                                       if (p[pos] != '}') { p--; break; }
28653+                                       p[pos] = 0;
28654+                                       if (!spfsubst(sa, p + 1, domain)) return 0;
28655+                                       p += pos;
28656+                                       continue;
28657+                               default: p--;
28658+                       }
28659+               }
28660+               if (!stralloc_append(sa, &append)) return 0;
28661+       }
28662+
28663+       return 1;
28664+}
28665+
28666+static int spflookup(stralloc *domain);
28667+
28668+static int spf_include(char *spec, char *mask)
28669+{
28670+       stralloc sa = {0};
28671+       int r;
28672+
28673+       if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
28674+       r = spflookup(&sa);
28675+       alloc_free(sa.s);
28676+
28677+       switch(r) {
28678+               case SPF_NONE:
28679+                       hdr_unknown();
28680+                       r = SPF_UNKNOWN;
28681+                       break;
28682+               case SPF_SYNTAX:
28683+                       r = SPF_UNKNOWN;
28684+                       break;
28685+               case SPF_NEUTRAL:
28686+               case SPF_SOFTFAIL:
28687+               case SPF_FAIL:
28688+                       r = SPF_NONE;
28689+                       break;
28690+       }
28691+
28692+       return r;
28693+}
28694+
28695+static int spf_a(char *spec, char *mask)
28696+{
28697+       stralloc sa = {0};
28698+       ipalloc ia = {0};
28699+       int ipmask = getipmask(mask, 1);
28700+       int r;
28701+       int j;
28702+
28703+       if (ipmask < 0) return SPF_SYNTAX;
28704+
28705+       if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
28706+       if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
28707+
28708+       switch(dns_ip(&ia, &sa)) {
28709+               case DNS_MEM: return SPF_NOMEM;
28710+               case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
28711+               case DNS_HARD: r = SPF_NONE; break;
28712+               default:
28713+                       r = SPF_NONE;
28714+                       for(j = 0; j < ia.len; ++j)
28715+                               if (matchip(&ia.ix[j].ip, ipmask, &ip)) {
28716+                                       r = SPF_OK;
28717+                                       break;
28718+                               }
28719+       }
28720+
28721+       alloc_free(sa.s);
28722+       alloc_free(ia.ix);
28723+       return r;
28724+}
28725+
28726+static int spf_mx(char *spec, char *mask)
28727+{
28728+       stralloc sa = {0};
28729+       ipalloc ia = {0};
28730+       int ipmask = getipmask(mask, 1);
28731+       int random = now() + (getpid() << 16);
28732+       int r;
28733+       int j;
28734+
28735+       if (ipmask < 0) return SPF_SYNTAX;
28736+
28737+       if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
28738+       if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
28739+
28740+       switch(dns_mxip(&ia, &sa, random)) {
28741+               case DNS_MEM: return SPF_NOMEM;
28742+               case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
28743+               case DNS_HARD: r = SPF_NONE; break;
28744+               default:
28745+                       r = SPF_NONE;
28746+                       for(j = 0; j < ia.len; ++j)
28747+                               if (matchip(&ia.ix[j].ip, ipmask, &ip)) {
28748+                                       r = SPF_OK;
28749+                                       break;
28750+                               }
28751+       }
28752+
28753+       alloc_free(sa.s);
28754+       alloc_free(ia.ix);
28755+       return r;
28756+}
28757+
28758+static int spf_ptr(char *spec, char *mask)
28759+{
28760+       strsalloc ssa = {0};
28761+       ipalloc ia = {0};
28762+       int len = str_len(spec);
28763+       int r;
28764+       int j, k;
28765+       int pos;
28766+
28767+       /* we didn't find host with the matching ip before */
28768+       if (sender_fqdn.len == 7 && str_equal(sender_fqdn.s, "unknown"))
28769+               return SPF_NONE;
28770+
28771+       /* the hostname found will probably be the same as before */
28772+       while (sender_fqdn.len) {
28773+               pos = sender_fqdn.len - len;
28774+               if (pos < 0) break;
28775+               if (pos > 0 && sender_fqdn.s[pos - 1] != '.') break;
28776+               if (case_diffb(sender_fqdn.s + pos, len, spec)) break;
28777+
28778+               return SPF_OK;
28779+       }
28780+
28781+       /* ok, either it's the first test or it's a very weird setup */
28782+
28783+       if (!stralloc_readyplus(&ssa, 0)) return SPF_NOMEM;
28784+       if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
28785+
28786+       switch(dns_ptr(&ssa, &ip)) {
28787+               case DNS_MEM: return SPF_NOMEM;
28788+               case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
28789+               case DNS_HARD: r = SPF_NONE; break;
28790+               default:
28791+                       r = SPF_NONE;
28792+                       for(j = 0; j < ssa.len; ++j) {
28793+                               switch(dns_ip(&ia, &ssa.sa[j])) {
28794+                                       case DNS_MEM: return SPF_NOMEM;
28795+                                       case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
28796+                                       case DNS_HARD: break;
28797+                                       default:
28798+                                               for(k = 0; k < ia.len; ++k)
28799+                                                       if (matchip(&ia.ix[k].ip, 32, &ip)) {
28800+                                                               if (!sender_fqdn.len)
28801+                                                                       if (!stralloc_copy(&sender_fqdn, &ssa.sa[j])) return SPF_NOMEM;
28802+
28803+                                                               pos = ssa.sa[j].len - len;
28804+                                                               if (pos < 0) continue;
28805+                                                               if (pos > 0 && ssa.sa[j].s[pos - 1] != '.') continue;
28806+                                                               if (case_diffb(ssa.sa[j].s + pos, len, spec)) continue;
28807+
28808+                                                               stralloc_copy(&sender_fqdn, &ssa.sa[j]);
28809+                                                               r = SPF_OK;
28810+                                                               break;
28811+                                                       }
28812+                               }
28813+
28814+                               if (r == SPF_ERROR) break;
28815+                       }
28816+       }
28817+
28818+       for(j = 0;j < ssa.len;++j)
28819+               alloc_free(ssa.sa[j].s);
28820+
28821+       alloc_free(ssa.sa);
28822+       alloc_free(ia.ix);
28823+
28824+       if (!sender_fqdn.len)
28825+               if (!stralloc_copys(&sender_fqdn, "unknown")) return SPF_NOMEM;
28826+
28827+       return r;
28828+}
28829+
28830+static int spf_ip(char *spec, char *mask)
28831+{
28832+       struct ip_address net;
28833+       int ipmask = getipmask(mask, 0);
28834+
28835+       if (ipmask < 0) return SPF_SYNTAX;
28836+       if (!ip_scan(spec, &net)) return SPF_SYNTAX;
28837+
28838+       if (matchip(&net, ipmask, &ip)) return SPF_OK;
28839+
28840+       return SPF_NONE;
28841+}
28842+
28843+static int spf_exists(char *spec, char *mask)
28844+{
28845+       stralloc sa = {0};
28846+       ipalloc ia = {0};
28847+       int r;
28848+
28849+       if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
28850+       if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
28851+
28852+       switch(dns_ip(&ia, &sa)) {
28853+               case DNS_MEM: return SPF_NOMEM;
28854+               case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
28855+               case DNS_HARD: r = SPF_NONE; break;
28856+               default: r = SPF_OK;
28857+       }
28858+
28859+       alloc_free(sa.s);
28860+       alloc_free(ia.ix);
28861+       return r;
28862+}
28863+
28864+static struct mechanisms {
28865+  char *mechanism;
28866+  int (*func)(char *spec, char *mask);
28867+  unsigned int takes_spec  : 1;
28868+  unsigned int takes_mask  : 1;
28869+  unsigned int expands     : 1;
28870+  unsigned int filldomain  : 1;
28871+  int defresult            : 4;
28872+} mechanisms[] = {
28873+  { "all",      0,          0,0,0,0,SPF_OK   }
28874+, { "include",  spf_include,1,0,1,0,0        }
28875+, { "a",        spf_a,      1,1,1,1,0        }
28876+, { "mx",       spf_mx,     1,1,1,1,0        }
28877+, { "ptr",      spf_ptr,    1,0,1,1,0        }
28878+, { "ip4",      spf_ip,     1,1,0,0,0        }
28879+, { "ip6",      0,          1,1,0,0,SPF_NONE }
28880+, { "exists",   spf_exists, 1,0,1,0,0        }
28881+, { "extension",0,          1,1,0,0,SPF_EXT  }
28882+, { 0,          0,          1,1,0,0,SPF_EXT  }
28883+};
28884+
28885+static int spfmech(char *mechanism, char *spec, char *mask, char *domain)
28886+{
28887+       struct mechanisms *mech;
28888+       stralloc sa = {0};
28889+       int r;
28890+       int pos;
28891+
28892+       for(mech = mechanisms; mech->mechanism; mech++)
28893+               if (str_equal(mech->mechanism, mechanism)) break;
28894+
28895+       if (mech->takes_spec && !spec && mech->filldomain) spec = domain;
28896+       if (!mech->takes_spec != !spec) return SPF_SYNTAX;
28897+       if (!mech->takes_mask && mask) return SPF_SYNTAX;
28898+       if (!mech->func) return mech->defresult;
28899+
28900+       if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM;
28901+       if (mech->expands && spec != domain) {
28902+               if (!spfexpand(&sa, spec, domain)) return SPF_NOMEM;
28903+               for (pos = 0; (sa.len - pos) > 255;) {
28904+                       pos += byte_chr(sa.s + pos, sa.len - pos, '.');
28905+                       if (pos < sa.len) pos++;
28906+               }
28907+               sa.len -= pos;
28908+               if (pos > 0) byte_copy(sa.s, sa.len, sa.s + pos);
28909+               stralloc_0(&sa);
28910+               spec = sa.s;
28911+       }
28912+
28913+       r = mech->func(spec, mask);
28914+
28915+       alloc_free(sa.s);
28916+       return r;
28917+}
28918+
28919+static struct default_aliases {
28920+  char *alias;
28921+  int defret;
28922+} default_aliases[] = {
28923+  { "allow",   SPF_OK }
28924+, { "pass",    SPF_OK }
28925+, { "deny",    SPF_FAIL }
28926+, { "softdeny",SPF_SOFTFAIL }
28927+, { "fail",    SPF_FAIL }
28928+, { "softfail",SPF_SOFTFAIL }
28929+, { "unknown", SPF_NEUTRAL }
28930+, { 0,         SPF_UNKNOWN }
28931+};
28932+
28933+static int spflookup(stralloc *domain)
28934+{
28935+       stralloc spf = {0};
28936+       stralloc sa = {0};
28937+       struct default_aliases *da;
28938+       int main = !recursion;
28939+       int local_pos = -1;
28940+       int r, q;
28941+       int begin, pos;
28942+       int i;
28943+       int prefix;
28944+       int done;
28945+       int guessing = 0;
28946+       char *p;
28947+
28948+       if (!stralloc_readyplus(&spf, 0)) return SPF_NOMEM;
28949+       if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM;
28950+
28951+       /* fallthrough result */
28952+       if (main) hdr_none();
28953+
28954+redirect:
28955+       if (++recursion > 20) {
28956+               alloc_free(spf.s);
28957+               alloc_free(sa.s);
28958+               hdr_unknown_msg("Maximum nesting level exceeded, possible loop");
28959+               return SPF_SYNTAX;
28960+       }
28961+
28962+       if (!stralloc_0(domain)) return SPF_NOMEM;
28963+       if (!stralloc_copy(&expdomain, domain)) return SPF_NOMEM;
28964+
28965+       r = spfget(&spf, domain);
28966+       if (r == SPF_NONE) {
28967+               if (!main) { alloc_free(spf.s); return r; }
28968+
28969+               if (spfguess.len) {
28970+                       /* try to guess */
28971+                       guessing = 1;
28972+                       if (!stralloc_copys(&spf, spfguess.s)) return SPF_NOMEM;
28973+                       if (!stralloc_append(&spf, " ")) return SPF_NOMEM;
28974+               } else
28975+                       spf.len = 0;
28976+
28977+               /* append local rulest */
28978+               if (spflocal.len) {
28979+                       local_pos = spf.len;
28980+                       if (!stralloc_cats(&spf, spflocal.s)) return SPF_NOMEM;
28981+               }
28982+               if (!stralloc_0(&spf)) return SPF_NOMEM;
28983+
28984+               expdomain.len = 0;
28985+       } else if (r == SPF_OK) {
28986+               if (!stralloc_0(&spf)) return SPF_NOMEM;
28987+               if (main) hdr_neutral();
28988+               r = SPF_NEUTRAL;
28989+
28990+               /* try to add local rules before fail all mechs */
28991+               if (main && spflocal.len) {
28992+                       pos = 0;
28993+                       p = (char *) 0;
28994+                       while(pos < spf.len) {
28995+                               NXTOK(begin, pos, &spf);
28996+                               if (!spf.s[begin]) continue;
28997+
28998+                               if (p && spf.s[begin] != *p) p = (char *) 0;
28999+                               if (!p && (spf.s[begin] == '-' || spf.s[begin] == '~' ||
29000+                                          spf.s[begin] == '?')) p = &spf.s[begin];
29001+
29002+                               if (p && p > spf.s && str_equal(spf.s + begin + 1, "all")) {
29003+                                       /* ok, we can insert the local rules at p */
29004+                                       local_pos = p - spf.s;
29005+
29006+                                       stralloc_readyplus(&spf, spflocal.len);
29007+                                       p = spf.s + local_pos;
29008+                                       byte_copyr(p + spflocal.len, spf.len - local_pos, p);
29009+                                       byte_copy(p, spflocal.len, spflocal.s);
29010+                                       spf.len += spflocal.len;
29011+
29012+                                       pos += spflocal.len;
29013+                                       break;
29014+                               }
29015+                       }
29016+
29017+                       if (pos >= spf.len) pos = spf.len - 1;
29018+                       for(i = 0; i < pos; i++)
29019+                               if (!spf.s[i]) spf.s[i] = ' ';
29020+               }
29021+       } else {
29022+               alloc_free(spf.s);
29023+               return r;
29024+       }
29025+
29026+       pos = 0;
29027+       done = 0;
29028+       while(pos < spf.len) {
29029+               NXTOK(begin, pos, &spf);
29030+               if (!spf.s[begin]) continue;
29031+
29032+               /* in local ruleset? */
29033+               if (!done && local_pos >= 0 && begin >= local_pos) {
29034+                       if (begin < (local_pos + spflocal.len))
29035+                               expdomain.len = 0;
29036+                       else
29037+                               if (!stralloc_copy(&expdomain, domain))
29038+                                       return SPF_NOMEM;
29039+               }
29040+
29041+               for (p = spf.s + begin;*p;++p)
29042+                       if (*p == ':' || *p == '/' || *p == '=') break;
29043+
29044+               if (*p == '=') {
29045+                       *p++ = 0;
29046+
29047+                       /* modifiers are simply handled here */
29048+                       if (str_equal(spf.s + begin, "redirect")) {
29049+                               if (done) continue;
29050+
29051+                               if (!spfexpand(&sa, p, domain->s)) return SPF_NOMEM;
29052+                               stralloc_copy(domain, &sa);
29053+
29054+                               hdr_unknown();
29055+                               r = SPF_UNKNOWN;
29056+
29057+                               goto redirect;
29058+                       } else if (str_equal(spf.s + begin, "default")) {
29059+                               if (done) continue;
29060+
29061+                               for(da = default_aliases; da->alias; ++da)
29062+                                       if (str_equal(da->alias, p)) break;
29063+
29064+                               r = da->defret;
29065+                       } else if (str_equal(spf.s + begin, "exp")) {
29066+                               strsalloc ssa = {0};
29067+
29068+                               if (!main) continue;
29069+
29070+                               if (!stralloc_copys(&sa, p)) return SPF_NOMEM;
29071+                               switch(dns_txt(&ssa, &sa)) {
29072+                                       case DNS_MEM: return SPF_NOMEM;
29073+                                       case DNS_SOFT: continue; /* FIXME... */
29074+                                       case DNS_HARD: continue;
29075+                               }
29076+
29077+                               explanation.len = 0;
29078+                               for(i = 0; i < ssa.len; i++) {
29079+                                       if (!stralloc_cat(&explanation, &ssa.sa[i])) return SPF_NOMEM;
29080+                                       if (i < (ssa.len - 1))
29081+                                               if (!stralloc_append(&explanation, "\n")) return SPF_NOMEM;
29082+
29083+                                       alloc_free(ssa.sa[i].s);
29084+                               }
29085+                               if (!stralloc_0(&explanation)) return SPF_NOMEM;
29086+                       } /* and unknown modifiers are ignored */
29087+               } else if (!done) {
29088+                       if (!stralloc_copys(&sa, spf.s + begin)) return SPF_NOMEM;
29089+                       if (!stralloc_0(&sa)) return SPF_NOMEM;
29090+
29091+                       switch(spf.s[begin]) {
29092+                               case '-': begin++; prefix = SPF_FAIL; break;
29093+                               case '~': begin++; prefix = SPF_SOFTFAIL; break;
29094+                               case '+': begin++; prefix = SPF_OK; break;
29095+                               case '?': begin++; prefix = SPF_NEUTRAL; break;
29096+                               default: prefix = SPF_OK;
29097+                       }
29098+
29099+                       if (*p == '/') {
29100+                               *p++ = 0;
29101+                               q = spfmech(spf.s + begin, 0, p, domain->s);
29102+                       } else {
29103+                               if (*p) *p++ = 0;
29104+                               i = str_chr(p, '/');
29105+                               if (p[i] == '/') {
29106+                                       p[i++] = 0;
29107+                                       q = spfmech(spf.s + begin, p, p + i, domain->s);
29108+                               } else if (i > 0)
29109+                                       q = spfmech(spf.s + begin, p, 0, domain->s);
29110+                               else
29111+                                       q = spfmech(spf.s + begin, 0, 0, domain->s);
29112+                       }
29113+
29114+                       if (q == SPF_OK) q = prefix;
29115+
29116+                       switch(q) {
29117+                               case SPF_OK: hdr_pass(); break;
29118+                               case SPF_NEUTRAL: hdr_neutral(); break;
29119+                               case SPF_SYNTAX: hdr_syntax(); break;
29120+                               case SPF_SOFTFAIL: hdr_softfail(); break;
29121+                               case SPF_FAIL: hdr_fail(); break;
29122+                               case SPF_EXT: hdr_ext(sa.s); break;
29123+                               case SPF_ERROR:
29124+                                       if (!guessing)
29125+                                               break;
29126+                                       if (local_pos >= 0 && begin >= local_pos)
29127+                                               break;
29128+                                       hdr_none();
29129+                                       q = SPF_NONE;
29130+                                       break;
29131+                               case SPF_NONE: continue;
29132+                       }
29133+
29134+                       r = q;
29135+                       done = 1; /* we're done, no more mechanisms */
29136+               }
29137+       }
29138+
29139+       /* we fell through, no local rule applied */
29140+       if (!done && !stralloc_copy(&expdomain, domain)) return SPF_NOMEM;
29141+
29142+       alloc_free(spf.s);
29143+       alloc_free(sa.s);
29144+       return r;
29145+}
29146+
29147+int spfcheck(char *remoteip)
29148+{
29149+       stralloc domain = {0};
29150+       int pos;
29151+       int r;
29152+
29153+       pos = byte_rchr(addr.s, addr.len, '@') + 1;
29154+       if (pos < addr.len) {
29155+               if (!stralloc_copys(&domain, addr.s + pos)) return SPF_NOMEM;
29156+       } else {
29157+               pos = str_rchr(helohost.s, '@');
29158+               if (helohost.s[pos]) {
29159+                       if (!stralloc_copys(&domain, helohost.s + pos + 1)) return SPF_NOMEM;
29160+               } else
29161+                       if (!stralloc_copys(&domain, helohost.s)) return SPF_NOMEM;
29162+       }
29163+       if (!stralloc_copys(&explanation, spfexp.s)) return SPF_NOMEM;
29164+       if (!stralloc_0(&explanation)) return SPF_NOMEM;
29165+       recursion = 0;
29166+
29167+       if (!remoteip || !ip_scan(remoteip, &ip)) {
29168+               hdr_unknown_msg("No IP address in conversation");
29169+               return SPF_UNKNOWN;
29170+       }
29171+
29172+       if (!stralloc_readyplus(&expdomain, 0)) return SPF_NOMEM;
29173+       if (!stralloc_readyplus(&errormsg, 0)) return SPF_NOMEM;
29174+       expdomain.len = 0;
29175+       errormsg.len = 0;
29176+       sender_fqdn.len = 0;
29177+       received = (char *) 0;
29178+
29179+       if ((ip.d[0] == 127 && ip.d[1] == 0 && ip.d[2] == 0 && ip.d[3] == 1) || ipme_is(&ip))
29180+               { hdr_pass(); r = SPF_OK; }
29181+       else
29182+               r = spflookup(&domain);
29183+
29184+       if (r < 0) r = SPF_UNKNOWN;
29185+
29186+       alloc_free(domain.s);
29187+       return r;
29188+}
29189+
29190+int spfexplanation(sa)
29191+stralloc *sa;
29192+{
29193+       return spfexpand(sa, explanation.s, expdomain.s);
29194+}
29195+
29196+int spfinfo(sa)
29197+stralloc *sa;
29198+{
29199+       stralloc tmp = {0};
29200+       if (!stralloc_copys(&tmp, received)) return 0;
29201+       if (!stralloc_0(&tmp)) return 0;
29202+       if (!spfexpand(sa, tmp.s, expdomain.s)) return 0;
29203+       alloc_free(tmp.s);
29204+       return 1;
29205+}
29206diff -ruN ../netqmail-1.06-original/spf.h netqmail-1.06/spf.h
29207--- ../netqmail-1.06-original/spf.h     1970-01-01 01:00:00.000000000 +0100
29208+++ netqmail-1.06/spf.h 2019-02-27 20:57:13.407024882 +0100
29209@@ -0,0 +1,20 @@
29210+#ifndef SPF_H
29211+#define SPF_H
29212+
29213+#define SPF_OK       0
29214+#define SPF_NONE     1
29215+#define SPF_UNKNOWN  2
29216+#define SPF_NEUTRAL  3
29217+#define SPF_SOFTFAIL 4
29218+#define SPF_FAIL     5
29219+#define SPF_ERROR    6
29220+#define SPF_NOMEM    7
29221+
29222+#define SPF_DEFEXP   "See http://spf.pobox.com/" \
29223+                     "why.html?sender=%{S}&ip=%{I}&receiver=%{xR}"
29224+
29225+extern int spfcheck();
29226+extern int spfexplanation();
29227+extern int spfinfo();
29228+
29229+#endif
29230diff -ruN ../netqmail-1.06-original/spfquery.c netqmail-1.06/spfquery.c
29231--- ../netqmail-1.06-original/spfquery.c        1970-01-01 01:00:00.000000000 +0100
29232+++ netqmail-1.06/spfquery.c    2019-02-27 20:57:13.407024882 +0100
29233@@ -0,0 +1,84 @@
29234+#include "substdio.h"
29235+#include "subfd.h"
29236+#include "stralloc.h"
29237+#include "alloc.h"
29238+#include "spf.h"
29239+#include "exit.h"
29240+
29241+void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); }
29242+void die_usage() { die(100,"fatal: invalid usage\nusage: spfquery <sender-ip> <sender-helo/ehlo> <envelope-from> [<local rules>] [<best guess rules>]\n"); }
29243+void die_nomem() { die(111,"fatal: out of memory\n"); }
29244+
29245+stralloc addr = {0};
29246+stralloc helohost = {0};
29247+char *remoteip;
29248+char *local;
29249+
29250+stralloc spflocal = {0};
29251+stralloc spfguess = {0};
29252+stralloc spfexp = {0};
29253+
29254+void main(argc,argv)
29255+int argc;
29256+char **argv;
29257+{
29258+       stralloc sa = {0};
29259+       int r;
29260+
29261+       if (argc < 4) die_usage();
29262+
29263+       remoteip = (char *)strdup(argv[1]);
29264+       local = "localhost";
29265+
29266+       if (!stralloc_copys(&helohost, argv[2])) die_nomem();
29267+       if (!stralloc_0(&helohost)) die_nomem();
29268+
29269+       if (!stralloc_copys(&addr, argv[3])) die_nomem();
29270+       if (!stralloc_0(&addr)) die_nomem();
29271+
29272+       if (argc > 4) {
29273+               if (!stralloc_copys(&spflocal, argv[4])) die_nomem();
29274+               if (spflocal.len && !stralloc_0(&spflocal)) die_nomem();
29275+       }
29276+
29277+       if (argc > 5) {
29278+               if (!stralloc_copys(&spfguess, argv[5])) die_nomem();
29279+               if (spfguess.len && !stralloc_0(&spfguess)) die_nomem();
29280+       }
29281+
29282+       if (argc > 6) {
29283+               if (!stralloc_copys(&spfexp, argv[6])) die_nomem();
29284+       } else
29285+               if (!stralloc_copys(&spfexp, SPF_DEFEXP)) die_nomem();
29286+       if (spfexp.len && !stralloc_0(&spfexp)) die_nomem();
29287+
29288+       dns_init(0);
29289+       r = spfcheck();
29290+       if (r == SPF_NOMEM) die_nomem();
29291+
29292+       substdio_puts(subfdout,"result=");
29293+       switch(r) {
29294+               case SPF_OK: substdio_puts(subfdout,"pass"); break;
29295+               case SPF_NONE: substdio_puts(subfdout,"none"); break;
29296+               case SPF_UNKNOWN: substdio_puts(subfdout,"unknown"); break;
29297+               case SPF_NEUTRAL: substdio_puts(subfdout,"neutral"); break;
29298+               case SPF_SOFTFAIL: substdio_puts(subfdout,"softfail"); break;
29299+               case SPF_FAIL: substdio_puts(subfdout,"fail"); break;
29300+               case SPF_ERROR: substdio_puts(subfdout,"error"); break;
29301+       }
29302+
29303+       if (r == SPF_FAIL) {
29304+               substdio_puts(subfdout,": ");
29305+               if (!spfexplanation(&sa)) die_nomem();
29306+               substdio_put(subfdout,sa.s,sa.len);
29307+       }
29308+
29309+       substdio_putsflush(subfdout,"\n");
29310+
29311+       substdio_puts(subfdout,"Received-SPF: ");
29312+       if (!spfinfo(&sa)) die_nomem();
29313+       substdio_put(subfdout,sa.s,sa.len);
29314+       substdio_putsflush(subfdout,"\n");
29315+
29316+       _exit(0);
29317+}
29318diff -ruN ../netqmail-1.06-original/srs.c netqmail-1.06/srs.c
29319--- ../netqmail-1.06-original/srs.c     1970-01-01 01:00:00.000000000 +0100
29320+++ netqmail-1.06/srs.c 2019-02-27 20:57:13.408024870 +0100
29321@@ -0,0 +1,166 @@
29322+#include <sys/types.h>
29323+#include <sys/stat.h>
29324+#include </usr/local/include/srs2.h>
29325+#include "auto_qmail.h"
29326+#include "stralloc.h"
29327+#include "srs.h"
29328+
29329+static stralloc srs_domain = {0};
29330+static stralloc srs_secrets = {0};
29331+static unsigned int srs_maxage = 0;
29332+static unsigned int srs_hashlength = 0;
29333+static unsigned int srs_hashmin = 0;
29334+static unsigned int srs_alwaysrewrite = 0;
29335+static stralloc srs_separator = {0};
29336+
29337+stralloc srs_result = {0};
29338+stralloc srs_error = {0};
29339+
29340+static int setup_ok = 0;
29341+static int srs_secrets_ok = 0;
29342+
29343+static int setup(int with_rcpthosts) {
29344+
29345+  if (setup_ok == 1) return 1;
29346+
29347+  if (chdir(auto_qmail) == -1) return -1;
29348+  if (control_init() == -1) return -1;
29349+
29350+  if (control_readline(&srs_domain,"control/srs_domain") == -1) return -1;
29351+  if (srs_domain.len) {
29352+    if (!stralloc_0(&srs_domain)) return -2;
29353+  } else {
29354+    return 0;
29355+  }
29356+
29357+  srs_secrets_ok = control_readfile(&srs_secrets,"control/srs_secrets",0);
29358+  if (srs_secrets_ok == -1) return -1;
29359
29360+  if (control_readint(&srs_maxage,"control/srs_maxage") == -1) return 0;
29361+  if (control_readint(&srs_hashlength,"control/srs_hashlength") == -1) return 0;
29362+  if (control_readint(&srs_hashmin,"control/srs_hashmin") == -1) return 0;
29363+  if (srs_hashmin > srs_hashlength) srs_hashmin = srs_hashlength;
29364
29365+  if (control_readint(&srs_alwaysrewrite,"control/srs_alwaysrewrite") == -1) return 0;
29366
29367+  if (control_readline(&srs_separator,"control/srs_separator") == -1) return -1;
29368+  if (srs_separator.len && !stralloc_0(&srs_separator)) return -2;
29369+  if (srs_separator.len && srs_separator.s[0] != '-' && srs_separator.s[0] != '+' && srs_separator.s[0] != '=') {
29370+    if (!stralloc_copys(&srs_separator,"")) return -2;
29371+  }
29372
29373+  if (!srs_alwaysrewrite) {
29374+    if (with_rcpthosts && rcpthosts_init() == -1) return -1;
29375+  }
29376+
29377+  setup_ok = 1;
29378+  return 1;
29379
29380+}
29381+
29382+static int srs_error_str(int code) {
29383+  if (!stralloc_copys(&srs_error,"SRS: ")) return -2;
29384+  if (!stralloc_cats(&srs_error,srs_strerror(code))) return -2;
29385+  if (!stralloc_0(&srs_error)) return -2;   
29386+  return -3;
29387+}
29388+
29389+
29390+int srsforward(char *address) {
29391+  int x = 0;
29392+
29393+  /* Return if setup was unsucessfull */
29394+  x = setup(1);
29395+  if (x < 1) return(x);
29396
29397+  /* Return zero if null-sender */
29398+  x = str_len(address);
29399+  if (x <= 1) return 0;
29400+
29401+  /* Return zero if local address */
29402+  if (!srs_alwaysrewrite && rcpthosts(address,x) == 1) return 0; 
29403
29404+  /* Now it's time to rewrite the envelope */
29405+  char srsaddress[1000];
29406+
29407+  srs_t *srs;
29408+  srs = srs_new();
29409+  if (srs_maxage > 0) srs->maxage = srs_maxage;
29410+  if (srs_hashlength > 0) srs->hashlength = srs_hashlength;
29411+  if (srs_hashmin > 0) srs->hashmin = srs_hashmin;
29412
29413+  if (srs_alwaysrewrite){ 
29414+    x = srs_set_alwaysrewrite(srs, TRUE);
29415+    if (x != SRS_SUCCESS) return srs_error_str(x);
29416+  }
29417
29418+  if (srs_separator.len) {
29419+    x = srs_set_separator(srs, srs_separator.s[0]);
29420+    if (x != SRS_SUCCESS) return srs_error_str(x);
29421+  }
29422
29423+  int i = 0;
29424+  int j = 0;
29425+  for (j = 0;j < srs_secrets.len;++j)
29426+    if (!srs_secrets.s[j]) {
29427+      x = srs_add_secret(srs, srs_secrets.s + i);
29428+      if (x != SRS_SUCCESS) return srs_error_str(x);
29429+      i = j + 1;
29430+    }
29431
29432+  x = srs_forward(srs, srsaddress, 1000, address, srs_domain.s);
29433+  if (x != SRS_SUCCESS) return srs_error_str(x);
29434
29435+  if (!stralloc_copys(&srs_result,srsaddress)) return -2;
29436+  if (!stralloc_0(&srs_result)) return -2;
29437
29438+  srs_free(srs);
29439
29440+  return 1;
29441+}
29442+
29443+int srsreverse(char *srsaddress) {
29444+  int x = 0;
29445+
29446+  /* Return if setup was unsucessfull */
29447+  x = setup(0);
29448+  if (x < 1) return(x);
29449
29450+  /* Return error if null-sender */
29451+  x = str_len(srsaddress);
29452+  if (x <= 1) return -3;
29453
29454+  /* Now it's time to rewrite the envelope */
29455+  char address[1000];
29456+
29457+  srs_t *srs;
29458+  srs = srs_new();
29459+  if (srs_maxage > 0) srs->maxage = srs_maxage;
29460+  if (srs_hashlength > 0) srs->hashlength = srs_hashlength;
29461+  if (srs_hashmin > 0) srs->hashmin = srs_hashmin;
29462
29463+  if (srs_separator.len) {
29464+    x = srs_set_separator(srs, srs_separator.s[0]);
29465+    if (x != SRS_SUCCESS) return srs_error_str(x);
29466+  }
29467+
29468+  int i = 0;
29469+  int j = 0;
29470+  for (j = 0;j < srs_secrets.len;++j)
29471+    if (!srs_secrets.s[j]) {
29472+      x = srs_add_secret(srs, srs_secrets.s + i);
29473+      if (x != SRS_SUCCESS) return srs_error_str(x);
29474+      i = j + 1;
29475+    }
29476+   
29477+  x = srs_reverse(srs, address, 1000, srsaddress);
29478+  if (x != SRS_SUCCESS) return srs_error_str(x);
29479
29480+  if (!stralloc_copys(&srs_result,address)) return -2;
29481+  if (!stralloc_0(&srs_result)) return -2;
29482
29483+  srs_free(srs);
29484
29485+  return 1;
29486+}
29487+
29488diff -ruN ../netqmail-1.06-original/srs.h netqmail-1.06/srs.h
29489--- ../netqmail-1.06-original/srs.h     1970-01-01 01:00:00.000000000 +0100
29490+++ netqmail-1.06/srs.h 2019-02-27 20:57:13.408024870 +0100
29491@@ -0,0 +1,9 @@
29492+#ifndef SRS_H
29493+#define SRS_H
29494+
29495+extern stralloc srs_result;
29496+extern stralloc srs_error;
29497+extern int srsforward(char *);
29498+extern int srsreverse(char *);
29499+
29500+#endif
29501diff -ruN ../netqmail-1.06-original/srsfilter.c netqmail-1.06/srsfilter.c
29502--- ../netqmail-1.06-original/srsfilter.c       1970-01-01 01:00:00.000000000 +0100
29503+++ netqmail-1.06/srsfilter.c   2019-02-27 20:57:13.408024870 +0100
29504@@ -0,0 +1,137 @@
29505+#include "sig.h"
29506+#include "readwrite.h"
29507+#include "exit.h"
29508+#include "env.h"
29509+#include "qmail.h"
29510+#include "strerr.h"
29511+#include "substdio.h"
29512+#include "fmt.h"
29513+#include "stralloc.h"
29514+#include "srs.h"
29515+
29516+#define FATAL "srsfilter: fatal: "
29517+#define IGNORE "srsfilter: ignore: "
29518+
29519+void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
29520+
29521+struct qmail qqt;
29522+
29523+stralloc line = {0};
29524+int flagbody = 0;
29525+int flagnewline = 0;
29526+int flagto = 0;
29527+int seento = 0;
29528+
29529+void newheader() {
29530+  if (!stralloc_copyb(&line,"To: ",4)) die_nomem();
29531+  if (!stralloc_cat(&line,&srs_result)) die_nomem();
29532+  ++flagto; ++seento;
29533+}
29534+
29535+void skipheader() {
29536+  if (!stralloc_copys(&line,"")) die_nomem();
29537+}
29538+
29539+void printheader() {
29540+  qmail_put(&qqt, line.s, line.len);
29541+  qmail_put(&qqt,"\n",1);
29542+  if (!stralloc_copys(&line,"")) die_nomem();
29543+}
29544+
29545+int mywrite(fd,buf,len) int fd; char *buf; int len;
29546+{
29547+  int i;
29548+  if (flagbody) {
29549+    qmail_put(&qqt,buf,len);
29550+    return len;
29551+  } else {
29552+    i = 0;
29553+    while (buf[i]) {
29554+      if (buf[i] == '\n') {
29555+        if (flagnewline) {
29556+          if (!seento) { newheader(); printheader(); }
29557+          qmail_put(&qqt,"\n",1); i++; flagbody = 1; continue;
29558+        }
29559+        if (flagto && (line.s[0] == ' ' || line.s[0] == '\t')) {
29560+          skipheader(); i++; continue;
29561+        }
29562+        if (line.len > 2 && line.s[2] == ':' && (line.s[1] == 'o' ||
29563+        line.s[1] == 'O') && (line.s[0] == 'T' || line.s[0] == 't')) {
29564+          if (seento) { skipheader(); i++; continue; }
29565+          newheader();
29566+        } else { flagto = 0; }
29567+        printheader();
29568+        flagnewline = 1;
29569+      } else {
29570+        if (!stralloc_append(&line,&buf[i])) die_nomem();
29571+        flagnewline = 0;
29572+      }
29573+      ++i;
29574+    }
29575+    return len;
29576+  }
29577+}
29578+
29579+char inbuf[SUBSTDIO_INSIZE];
29580+char outbuf[1];
29581+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
29582+substdio ssout = SUBSTDIO_FDBUF(mywrite,-1,outbuf,sizeof outbuf);
29583+
29584+char num[FMT_ULONG];
29585+
29586+void main(argc,argv)
29587+int argc;
29588+char **argv;
29589+{
29590+  char *ext2;
29591+  char *host;
29592+  char *sender;
29593+  char *qqx;
29594+
29595+  sig_pipeignore();
29596+
29597+  sender = env_get("SENDER");
29598+  if (!sender)
29599+    strerr_die2x(100,FATAL,"SENDER not set");
29600+  if (str_len(sender)) {
29601+    /* Return zero, the message will not bounce back */
29602+    strerr_die2x(0,IGNORE,"SENDER must be empty");
29603+  }
29604+  ext2 = env_get("EXT2");
29605+  if (!ext2)
29606+    strerr_die2x(100,FATAL,"EXT2 not set");
29607+  host = env_get("HOST");
29608+  if (!host)
29609+    strerr_die2x(100,FATAL,"HOST not set");
29610+   
29611+  switch(srsreverse(ext2)) {
29612+    case -3: strerr_die2x(100,FATAL,srs_error.s); break;
29613+    case -2: die_nomem(); break;
29614+    case -1: strerr_die2x(111,FATAL,"unable to read controls"); break;
29615+    case 0: strerr_die2x(100,FATAL,"unable to rewrite envelope"); break;
29616+  }
29617+
29618+  if (qmail_open(&qqt) == -1)
29619+    strerr_die2x(111,FATAL,"unable to fork");
29620+  if (substdio_copy(&ssout,&ssin) != 0)
29621+    strerr_die2x(111,FATAL,"unable to read message");
29622+  substdio_flush(&ssout);
29623
29624+  if (!flagbody) {
29625+    qmail_fail(&qqt);
29626+    strerr_die2x(100,FATAL,"unable to read message body");
29627+  }
29628+
29629+  num[fmt_ulong(num,qmail_qp(&qqt))] = 0;
29630+
29631+  /* Always from nullsender */
29632+  qmail_from(&qqt,"");
29633
29634+  qmail_to(&qqt,srs_result.s);
29635
29636+  qqx = qmail_close(&qqt);
29637+  if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1);
29638+  strerr_die2x(0,"srsfilter: qp ",num);
29639+
29640+}
29641+
29642diff -ruN ../netqmail-1.06-original/ssl_timeoutio.c netqmail-1.06/ssl_timeoutio.c
29643--- ../netqmail-1.06-original/ssl_timeoutio.c   1970-01-01 01:00:00.000000000 +0100
29644+++ netqmail-1.06/ssl_timeoutio.c       2020-01-10 21:51:32.057157279 +0100
29645@@ -0,0 +1,126 @@
29646+#ifdef TLS
29647+#include "select.h"
29648+#include "error.h"
29649+#include "ndelay.h"
29650+#include "now.h"
29651+#include "ssl_timeoutio.h"
29652+
29653+int ssl_timeoutio(int (*fun)(),
29654+  int t, int rfd, int wfd, SSL *ssl, char *buf, int len)
29655+{
29656+  int n;
29657+  const datetime_sec end = (datetime_sec)t + now();
29658+
29659+  do {
29660+    fd_set fds;
29661+    struct timeval tv;
29662+
29663+    const int r = buf ? fun(ssl, buf, len) : fun(ssl);
29664+    if (r > 0) return r;
29665+
29666+    t = end - now();
29667+    if (t < 0) break;
29668+    tv.tv_sec = (time_t)t; tv.tv_usec = 0;
29669+
29670+    FD_ZERO(&fds);
29671+    switch (SSL_get_error(ssl, r))
29672+    {
29673+    default: return r; /* some other error */
29674+    case SSL_ERROR_WANT_READ:
29675+      FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv);
29676+      break;
29677+    case SSL_ERROR_WANT_WRITE:
29678+      FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv);
29679+      break;
29680+    }
29681+
29682+    /* n is the number of descriptors that changed status */
29683+  } while (n > 0);
29684+
29685+  if (n != -1) errno = error_timeout;
29686+  return -1;
29687+}
29688+
29689+int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl)
29690+{
29691+  int r;
29692+
29693+  /* if connection is established, keep NDELAY */
29694+  if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1;
29695+  r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0);
29696+
29697+  if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); }
29698+  else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
29699+
29700+  return r;
29701+}
29702+
29703+int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl)
29704+{
29705+  int r;
29706+
29707+  /* if connection is established, keep NDELAY */
29708+  if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1;
29709+  r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0);
29710+
29711+  if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); }
29712+  else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
29713+
29714+  return r;
29715+}
29716+
29717+int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl)
29718+{
29719+  int r=0;
29720+
29721+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
29722+  if (SSL_version(ssl) >= TLS1_3_VERSION){
29723+    if(SSL_verify_client_post_handshake(ssl) != 1)
29724+      return -EPROTO;
29725+  } else
29726+#endif
29727+  {
29728+    r =  SSL_renegotiate(ssl);
29729+    if (r<=0) return r;
29730+  }
29731+
29732+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
29733+  char buf[1]; /* dummy read buffer */
29734+  struct timeval tv;
29735+  fd_set fds;
29736+  r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
29737+  if (r <=0) return r;
29738+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
29739+  if (SSL_version(ssl) >= TLS1_3_VERSION) return r;
29740+#endif
29741+
29742+  tv.tv_sec = (time_t)t; tv.tv_usec = 0;
29743+  FD_ZERO(&fds);  FD_SET(rfd, &fds);
29744+  if ((r = select(rfd + 1, &fds, NULL, NULL, &tv)>0) && FD_ISSET(rfd, &fds)){
29745+    r = SSL_read(ssl, buf, 1);
29746+    if (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ) r = 1; /*ignore */
29747+  }
29748+  if (r <=0) return r;
29749+#else
29750+  r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
29751+  if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r;
29752+
29753+  /* this is for the server only */
29754+  ssl->state = SSL_ST_ACCEPT;
29755+#endif
29756+  return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
29757+}
29758+
29759+int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len)
29760+{
29761+  if (!buf) return 0;
29762+  if (SSL_pending(ssl)) return SSL_read(ssl, buf, len);
29763+  return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len);
29764+}
29765+
29766+int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len)
29767+{
29768+  if (!buf) return 0;
29769+  return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len);
29770+}
29771+#endif
29772diff -ruN ../netqmail-1.06-original/ssl_timeoutio.h netqmail-1.06/ssl_timeoutio.h
29773--- ../netqmail-1.06-original/ssl_timeoutio.h   1970-01-01 01:00:00.000000000 +0100
29774+++ netqmail-1.06/ssl_timeoutio.h       2019-04-07 13:05:52.192763950 +0200
29775@@ -0,0 +1,21 @@
29776+#ifndef SSL_TIMEOUTIO_H
29777+#define SSL_TIMEOUTIO_H
29778+
29779+#include <openssl/ssl.h>
29780+
29781+/* the version is like this: 0xMNNFFPPS: major minor fix patch status */
29782+#if OPENSSL_VERSION_NUMBER < 0x00908000L
29783+# error "Need OpenSSL version at least 0.9.8"
29784+#endif
29785+
29786+int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl);
29787+int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl);
29788+int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl);
29789+
29790+int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len);
29791+int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len);
29792+
29793+int ssl_timeoutio(
29794+  int (*fun)(), int t, int rfd, int wfd, SSL *ssl, char *buf, int len);
29795+
29796+#endif
29797diff -ruN ../netqmail-1.06-original/str.h netqmail-1.06/str.h
29798--- ../netqmail-1.06-original/str.h     1998-06-15 12:53:16.000000000 +0200
29799+++ netqmail-1.06/str.h 2019-02-27 20:57:13.408024870 +0100
29800@@ -2,6 +2,11 @@
29801 #define STR_H
29802 
29803 extern unsigned int str_copy();
29804+
29805+/* DKIM 1.10
29806+extern unsigned int str_copyb();
29807+   end DKIM 1.10 */
29808+
29809 extern int str_diff();
29810 extern int str_diffn();
29811 extern unsigned int str_len();
29812@@ -9,6 +14,11 @@
29813 extern unsigned int str_rchr();
29814 extern int str_start();
29815 
29816+/* DKIM 1.10 */
29817+#include <sys/types.h>
29818+extern size_t str_cspn();
29819+/* end DKIM 1.10 */
29820+
29821 #define str_equal(s,t) (!str_diff((s),(t)))
29822 
29823 #endif
29824diff -ruN ../netqmail-1.06-original/str_cpyb.c netqmail-1.06/str_cpyb.c
29825--- ../netqmail-1.06-original/str_cpyb.c        1970-01-01 01:00:00.000000000 +0100
29826+++ netqmail-1.06/str_cpyb.c    2019-02-27 20:57:13.408024870 +0100
29827@@ -0,0 +1,53 @@
29828+/*
29829+ * $Log: str_cpyb.c,v $
29830+ * Revision 1.2  2004-10-22 20:30:54+05:30  Cprogrammer
29831+ * added RCS id
29832+ *
29833+ * Revision 1.1  2004-08-15 19:52:35+05:30  Cprogrammer
29834+ * Initial revision
29835+ *
29836+ */
29837+#include "str.h"
29838+
29839+unsigned int
29840+str_copyb(s, t, max)
29841+      register char  *s;
29842+      register char  *t;
29843+      unsigned int    max;
29844+{
29845+      register int    len;
29846+
29847+      len = 0;
29848+      while (max-- > 0)
29849+      {
29850+              if (!(*s = *t))
29851+                      return len;
29852+              ++s;
29853+              ++t;
29854+              ++len;
29855+              if (!(*s = *t))
29856+                      return len;
29857+              ++s;
29858+              ++t;
29859+              ++len;
29860+              if (!(*s = *t))
29861+                     return len;
29862+              ++s;
29863+              ++t;
29864+              ++len;
29865+              if (!(*s = *t))
29866+                      return len;
29867+              ++s;
29868+              ++t;
29869+              ++len;
29870+      }
29871+      return len;
29872+}
29873+
29874+void
29875+getversion_str_cpyb_c()
29876+{
29877+      static char    *x = "$Id: str_cpyb.c,v 1.2 2004-10-22 20:30:54+05:30 Cprogrammer Stab mbhangui $";
29878+      x++;
29879+}
29880+
29881diff -ruN ../netqmail-1.06-original/str_cspn.c netqmail-1.06/str_cspn.c
29882--- ../netqmail-1.06-original/str_cspn.c        1970-01-01 01:00:00.000000000 +0100
29883+++ netqmail-1.06/str_cspn.c    2019-02-27 20:57:13.408024870 +0100
29884@@ -0,0 +1,40 @@
29885+/*
29886+ * $Log: str_cspn.c,v $
29887+ * Revision 1.1  2011-07-12 20:42:00+05:30  Cprogrammer
29888+ * Initial revision
29889+ *
29890+ */
29891+#include "str.h"
29892+/*
29893+ * Span the complement of string s2.
29894+ */
29895+size_t
29896+str_cspn(s1, s2)
29897+       const char *s1;
29898+       register const char *s2;
29899+{
29900+       register const char *p, *spanp;
29901+       register char c, sc;
29902+
29903+       /*
29904+        * Stop as soon as we find any character from s2.  Note that there
29905+        * must be a NUL in s2; it suffices to stop when we find that, too.
29906+        */
29907+       for (p = s1;;) {
29908+               c = *p++;
29909+               spanp = s2;
29910+               do {
29911+                       if ((sc = *spanp++) == c)
29912+                               return (p - 1 - s1);
29913+               } while (sc != 0);
29914+       }
29915+       /* NOTREACHED */
29916+}
29917+
29918+void
29919+getversion_str_cspn_c()
29920+{
29921+       static char    *x = "$Id: str_cspn.c,v 1.1 2011-07-12 20:42:00+05:30 Cprogrammer Exp mbhangui $";
29922+
29923+       x++;
29924+}
29925diff -ruN ../netqmail-1.06-original/strpidt.c netqmail-1.06/strpidt.c
29926--- ../netqmail-1.06-original/strpidt.c 1970-01-01 01:00:00.000000000 +0100
29927+++ netqmail-1.06/strpidt.c     2019-02-27 20:57:13.408024870 +0100
29928@@ -0,0 +1,26 @@
29929+/*
29930+** Copyright 1998 - 2000 Double Precision, Inc.
29931+** See COPYING for distribution information.
29932+*/
29933+
29934+#if    HAVE_CONFIG_H
29935+#include       "config.h"
29936+#endif
29937+#include       "numlib.h"
29938+#include       <string.h>
29939+
29940+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
29941+
29942+char *str_pid_t(pid_t t, char *arg)
29943+{
29944+char   buf[NUMBUFSIZE];
29945+char   *p=buf+sizeof(buf)-1;
29946+
29947+       *p=0;
29948+       do
29949+       {
29950+               *--p= '0' + (t % 10);
29951+               t=t / 10;
29952+       } while(t);
29953+       return (strcpy(arg, p));
29954+}
29955diff -ruN ../netqmail-1.06-original/strsalloc.c netqmail-1.06/strsalloc.c
29956--- ../netqmail-1.06-original/strsalloc.c       1970-01-01 01:00:00.000000000 +0100
29957+++ netqmail-1.06/strsalloc.c   2019-02-27 20:57:13.409024860 +0100
29958@@ -0,0 +1,7 @@
29959+#include "alloc.h"
29960+#include "gen_allocdefs.h"
29961+#include "stralloc.h"
29962+#include "strsalloc.h"
29963+
29964+GEN_ALLOC_readyplus(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus)
29965+GEN_ALLOC_append(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus,strsalloc_append)
29966diff -ruN ../netqmail-1.06-original/strsalloc.h netqmail-1.06/strsalloc.h
29967--- ../netqmail-1.06-original/strsalloc.h       1970-01-01 01:00:00.000000000 +0100
29968+++ netqmail-1.06/strsalloc.h   2019-02-27 20:57:13.409024860 +0100
29969@@ -0,0 +1,12 @@
29970+#ifndef STRSALLOC_H
29971+#define STRSALLOC_H
29972+
29973+#include "stralloc.h"
29974+
29975+#include "gen_alloc.h"
29976+
29977+GEN_ALLOC_typedef(strsalloc,stralloc,sa,len,a)
29978+extern int strsalloc_readyplus();
29979+extern int strsalloc_append();
29980+
29981+#endif
29982diff -ruN ../netqmail-1.06-original/strtimet.c netqmail-1.06/strtimet.c
29983--- ../netqmail-1.06-original/strtimet.c        1970-01-01 01:00:00.000000000 +0100
29984+++ netqmail-1.06/strtimet.c    2019-02-27 20:57:13.409024860 +0100
29985@@ -0,0 +1,26 @@
29986+/*
29987+** Copyright 1998 - 2000 Double Precision, Inc.
29988+** See COPYING for distribution information.
29989+*/
29990+
29991+#if    HAVE_CONFIG_H
29992+#include       "config.h"
29993+#endif
29994+#include       "numlib.h"
29995+#include       <string.h>
29996+
29997+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
29998+
29999+char *str_time_t(time_t t, char *arg)
30000+{
30001+char   buf[NUMBUFSIZE];
30002+char   *p=buf+sizeof(buf)-1;
30003+
30004+       *p=0;
30005+       do
30006+       {
30007+               *--p= '0' + (t % 10);
30008+               t=t / 10;
30009+       } while(t);
30010+       return (strcpy(arg, p));
30011+}
30012diff -ruN ../netqmail-1.06-original/surblfilter.9 netqmail-1.06/surblfilter.9
30013--- ../netqmail-1.06-original/surblfilter.9     1970-01-01 01:00:00.000000000 +0100
30014+++ netqmail-1.06/surblfilter.9 2019-06-19 09:49:02.553441531 +0200
30015@@ -0,0 +1,82 @@
30016+.TH surblfilter 8
30017+.SH NAME
30018+surblfilter \- qmail SURBL blocklist interface
30019+.SH SYNOPSIS
30020+.B surblfilter
30021+[
30022+.B \-v
30023+]
30024+[
30025+.B \-c
30026+]
30027+[
30028+.B \-t
30029+]
30030+.SH DESCRIPTION
30031+\fBSURBL\fRs are lists of web sites that have appeared in unsolicited messages. Unlike most lists,
30032+SURBLs are not lists of message senders.
30033+
30034+Web sites seen in unsolicited messages tend to be more stable than the rapidly changing botnet
30035+IP addresses used to send the vast majority of them. Sender lists like zen.spamhaus.org can be
30036+used in a first stage filter to help identify 80% to 90% of unsolicited messages. SURBLs can
30037+help find about 75% of the otherwise difficult, remaining unsolicited messages in a second
30038+stage filter. Used together with sender lists, SURBLs have proven to be a highly-effective way
30039+to detect 95% of unsolicited messages.
30040+
30041+\fBsurblfilter\fR reads an rfc822 email on stdin, extracts URL and checks them against SURBL.
30042+\fBsurblfilter\fR can be used as a filter using \fBqmail-qfilter(1)\fR. It can also be used
30043+as a qmail-queue(8) frontend by setting QMAILQUEUE environment variable to a wrapper
30044+QMAILHOME/bin/surblqueue. You can define \fBSURBLQUEUE\fR environment variable to make
30045+\fBsurblqueue\fR execute something other than \fBqmail-queue\fR(8).
30046+
30047+\fBsurblfilter\fR uses \fBQMAILRCPTS\fR environment variable to get the recipient list. You can
30048+whitelist recipients by having the email addresses in \fIsurblrcpt\fR control file. You can
30049+change the name of this control file by setting \fBSURBLRCPT\fR environment variable.
30050+
30051+.PP
30052+\fBsurblfilter\fR uses the control file \fIsurbldomainwhite\fR to whitelist a domain.
30053+
30054+.PP
30055+The default SURBL list that is used is \fBmulti.surbl.org\fR. You can use a different list
30056+by setting the \fIsurbldomain\fR control file.
30057+
30058+.PP
30059+\fBsurblfilter\fR caches results in @controldir@/cache directory. The filename of files in this
30060+directory represents the domain. If a file has permission 0600, it means that the domain was
30061+blacklisted. The entries are cached for a default of 300 seconds. You can change this by
30062+setting \fBCACHELIFETIME\fR environment variable or setting this value in \fBcacheliftime\fR
30063+control file. The cache directory should be owned by the uid of the running qmail-smtpd(8).
30064+
30065+\fBsurblfilter\fR removes all leading host names, subdomains, www., randomized subdomains, etc. In
30066+order to determine the level of domain check, it uses the control files \fBlevel3-tlds\fR and
30067+\fBlevel2-tlds\fR. For any domain found in \fBlevel3-tlds\fR, it checks the domain at level4. For any
30068+domain found in \fBlevel2-tlds\fR, it checks the domain at level3. For all domains not found in
30069+these two control files, it checks the domain at level2. Please look at http://www.surbl.org/guidelines
30070+
30071+for more details.
30072+
30073+.SH OPTIONS
30074+.PP
30075+.TP
30076+\fB-v\fR
30077+Use debug mode
30078+
30079+.TP
30080+\fB-c\fR
30081+Do not cache results
30082+
30083+.TP
30084+\fB-t\fR
30085+Do dns text query to get the reason. This option will slow \fBsurblfilter\fR. Remember that
30086+last octet of the IP address obtained for the domain gives you the reason for the block
30087+
30088+.SH RETURN VALUE
30089+\fBsurbfilter\fR returns 88 if the domain is blocked and prints the reason on standard error.
30090+This allows for qmail-smtpd(8) to print a permanent error during a SMTP session.
30091+It returns 111 for all temporary errors. It returns 0 if the message does not contain any
30092+domain blocked by SURBL
30093+
30094+.SH "SEE ALSO"
30095+qmail-smtpd(8),
30096+qmail-queue(8),
30097+qmail-qfilter(1)
30098diff -ruN ../netqmail-1.06-original/surblfilter.c netqmail-1.06/surblfilter.c
30099--- ../netqmail-1.06-original/surblfilter.c     1970-01-01 01:00:00.000000000 +0100
30100+++ netqmail-1.06/surblfilter.c 2020-04-09 19:46:02.374231173 +0200
30101@@ -0,0 +1,882 @@
30102+/*
30103+ * $Log: surblfilter.c,v $
30104+ * Revision 1.3  2011-07-13 22:11:13+05:30  Cprogrammer
30105+ * skip surblrcpt if QMAILRCPTS is not defined
30106+ *
30107+ * Revision 1.2  2011-07-13 22:02:13+05:30  Cprogrammer
30108+ * added surblrcpt functionality
30109+ *
30110+ * Revision 1.1  2011-07-13 20:56:34+05:30  Cprogrammer
30111+ * Initial revision
30112+ *
30113+ */
30114+#include <unistd.h>
30115+#include <fcntl.h>
30116+#include <errno.h>
30117+#include <ctype.h>
30118+#include <time.h>
30119+#include <sys/stat.h>
30120+
30121+#include <sys/socket.h>
30122+#include <netinet/in.h>
30123+#include <arpa/inet.h>
30124+#ifdef DARWIN
30125+#include <nameser8_compat.h>
30126+#endif
30127+#include <arpa/nameser.h>
30128+#include <resolv.h>
30129+#include <netdb.h>
30130+
30131+#include "alloc.h"
30132+#include "sgetopt.h"
30133+#include "error.h"
30134+#include "scan.h"
30135+#include "str.h"
30136+#include "case.h"
30137+#include "constmap.h"
30138+#include "auto_qmail.h"
30139+#include "stralloc.h"
30140+#include "env.h"
30141+#include "control.h"
30142+#include "strerr.h"
30143+#include "substdio.h"
30144+#include "getln.h"
30145+#include "byte.h"
30146+#include "dns.h"
30147+#include "ip.h"
30148+#include "ipalloc.h"
30149+#include "mess822.h"
30150+#include "base64.h"
30151+
30152+#define FATAL "surblfilter: fatal: "
30153+
30154+char           *dns_text(char *);
30155+
30156+stralloc        line = { 0 };
30157+int             debug = 0, do_text = 0, do_cache = 1;
30158+static int      cachelifetime = 300;
30159+stralloc        whitelist = { 0 };
30160+stralloc        surbldomain = { 0 };
30161+
30162+/*- SURBL: RCPT whitelist. */
30163+stralloc        srw = { 0 };
30164+int             srwok = 0;
30165+struct constmap mapsrw;
30166+
30167+/*- 2 level tld */
30168+stralloc        l2 = { 0 };
30169+int             l2ok = 0;
30170+struct constmap mapl2;
30171+/*- 3 level tld */
30172+stralloc        l3 = { 0 };
30173+int             l3ok = 0;
30174+struct constmap mapl3;
30175+
30176+static char     ssinbuf[1024];
30177+static substdio ssin = SUBSTDIO_FDBUF(read, 0, ssinbuf, sizeof ssinbuf);
30178+static char     ssoutbuf[512];
30179+static substdio ssout = SUBSTDIO_FDBUF(write, 1, ssoutbuf, sizeof ssoutbuf);
30180+static char     sserrbuf[512];
30181+static substdio sserr = SUBSTDIO_FDBUF(write, 2, sserrbuf, sizeof(sserrbuf));
30182+
30183+void
30184+out(char *str)
30185+{
30186+       if (!str || !*str)
30187+               return;
30188+       if (substdio_puts(&ssout, str) == -1)
30189+               strerr_die2sys(111, FATAL, "write: ");
30190+       return;
30191+}
30192+
30193+void
30194+print_debug(char *arg1, char *arg2, char *arg3)
30195+{
30196+       if (!debug)
30197+               return;
30198+       if (arg1 && substdio_puts(&sserr, arg1) == -1)
30199+               _exit(1);
30200+       if (arg2 && substdio_puts(&sserr, arg2) == -1)
30201+               _exit(1);
30202+       if (arg3 && substdio_puts(&sserr, arg3) == -1)
30203+               _exit(1);
30204+       if ((arg1 || arg2 || arg3) && substdio_puts(&sserr, "\n"))
30205+               _exit(1);
30206+       if (substdio_flush(&sserr) == -1)
30207+               _exit(1);
30208+}
30209+
30210+void
30211+die_write()
30212+{
30213+       strerr_die2sys(111, FATAL, "write: ");
30214+       return;
30215+}
30216+
30217+void
30218+flush()
30219+{
30220+       if (substdio_flush(&ssout) == -1)
30221+               strerr_die2sys(111, FATAL, "write: ");
30222+       return;
30223+}
30224+
30225+void
30226+logerr(char *s)
30227+{
30228+       if (substdio_puts(&sserr, s) == -1)
30229+               _exit(1);
30230+}
30231+
30232+void
30233+logerrf(char *s)
30234+{
30235+       if (substdio_puts(&sserr, s) == -1)
30236+               _exit(1);
30237+       if (substdio_flush(&sserr) == -1)
30238+               _exit(1);
30239+}
30240+
30241+void
30242+my_error(char *s1, char *s2, int exit_val)
30243+{
30244+       logerr(s1);
30245+       if (s2) {
30246+               logerr(": ");
30247+               logerr(s2);
30248+       }
30249+       if (exit_val > 0) {
30250+               logerr(": ");
30251+               logerr(error_str(errno));
30252+       }
30253+       logerrf("\n");
30254+       _exit(exit_val > 0 ? exit_val : 0 - exit_val);
30255+}
30256+
30257+void
30258+die_nomem()
30259+{
30260+       substdio_flush(&ssout);
30261+       substdio_puts(&sserr, "surblfilter: out of memory\n");
30262+       substdio_flush(&sserr);
30263+       _exit(1);
30264+}
30265+
30266+void
30267+die_soft()
30268+{
30269+       substdio_flush(&ssout);
30270+       substdio_puts(&sserr, "surblfilter: DNS temporary failure\n");
30271+       substdio_flush(&sserr);
30272+       _exit(1);
30273+}
30274+
30275+void
30276+die_hard()
30277+{
30278+       substdio_flush(&ssout);
30279+       substdio_puts(&sserr, "surblfilter: DNS permanent failure\n");
30280+       substdio_flush(&sserr);
30281+       _exit(1);
30282+}
30283+
30284+void
30285+die_control()
30286+{
30287+       substdio_flush(&ssout);
30288+       substdio_puts(&sserr, "surblfilter: unable to read controls\n");
30289+       substdio_flush(&sserr);
30290+       _exit(1);
30291+}
30292+
30293+static unsigned short
30294+getshort(unsigned char *cp)
30295+{
30296+       return (cp[0] << 8) | cp[1];
30297+}
30298+
30299+static char *
30300+strdup(const char *str)
30301+{
30302+       size_t siz;
30303+       char *copy;
30304+
30305+       siz = str_len((char *) str) + 1;
30306+       if (!(copy = alloc(siz)))
30307+               return((char *) 0);
30308+       byte_copy(copy, siz, (char *) str);
30309+       return(copy);
30310+}
30311+
30312+/*
30313+ * we always return a null-terminated string which has been malloc'ed.  The string
30314+ * is always in the tag=value form.  If a temporary or permanent error occurs,
30315+ * the string will be exactly "e=perm;" or "e=temp;".
30316+ * Note that it never returns NULL.
30317+ */
30318+char           *
30319+dns_text(char *dn)
30320+{
30321+       u_char          response[PACKETSZ + PACKETSZ + 1];      /* response */
30322+       int             responselen;                    /* buffer length */
30323+       int             rc;                                             /* misc variables */
30324+       int             ancount, qdcount;               /* answer count and query count */
30325+       u_short         type, rdlength;                 /* fields of records returned */
30326+       u_char         *eom, *cp;
30327+       u_char          buf[PACKETSZ + PACKETSZ + 1];           /* we're storing a TXT record here, not just a DNAME */
30328+       u_char         *bufptr;
30329+
30330+       for (rc = 0, responselen = PACKETSZ;rc < 2;rc++) {
30331+               if ((responselen = res_query(dn, C_IN, T_TXT, response, responselen)) < 0) {
30332+                       if (h_errno == TRY_AGAIN)
30333+                               return strdup("e=temp;");
30334+                       else
30335+                               return strdup("e=perm;");
30336+               }
30337+               if (responselen <= PACKETSZ)
30338+                       break;
30339+               else
30340+               if (responselen >= (2 * PACKETSZ))
30341+                       return strdup("e=perm;");
30342+       }
30343+       qdcount = getshort(response + 4);       /* http://crynwr.com/rfc1035/rfc1035.html#4.1.1. */
30344+       ancount = getshort(response + 6);
30345+       eom = response + responselen;
30346+       cp = response + HFIXEDSZ;
30347+       while (qdcount-- > 0 && cp < eom) {
30348+               rc = dn_expand(response, eom, cp, (char *) buf, MAXDNAME);
30349+               if (rc < 0)
30350+                       return strdup("e=perm;");
30351+               cp += rc + QFIXEDSZ;
30352+       }
30353+       while (ancount-- > 0 && cp < eom) {
30354+               if ((rc = dn_expand(response, eom, cp, (char *) buf, MAXDNAME)) < 0)
30355+                       return strdup("e=perm;");
30356+               cp += rc;
30357+               if (cp + RRFIXEDSZ >= eom)
30358+                       return strdup("e=perm;");
30359+               type = getshort(cp + 0);        /* http://crynwr.com/rfc1035/rfc1035.html#4.1.3. */
30360+               rdlength = getshort(cp + 8);
30361+               cp += RRFIXEDSZ;
30362+               if (type != T_TXT) {
30363+                       cp += rdlength;
30364+                       continue;
30365+               }
30366+               bufptr = buf;
30367+               while (rdlength && cp < eom) {
30368+                       unsigned int    cnt;
30369+
30370+                       cnt = *cp++;            /* http://crynwr.com/rfc1035/rfc1035.html#3.3.14. */
30371+                       if (bufptr - buf + cnt + 1 >= (2 * PACKETSZ))
30372+                               return strdup("e=perm;");
30373+                       if (cp + cnt > eom)
30374+                               return strdup("e=perm;");
30375+                       byte_copy((char *) bufptr, cnt, (char *) cp);
30376+                       rdlength -= cnt + 1;
30377+                       bufptr += cnt;
30378+                       cp += cnt;
30379+                       *bufptr = '\0';
30380+               }
30381+               return (char *) strdup((char *) buf);
30382+       }
30383+       return strdup("e=perm;");
30384+}
30385+
30386+static char    *
30387+uri_decode(char *str, size_t str_len, char **strend)
30388+{
30389+       size_t          i = 0, j = 0, found;
30390+       int             pasthostname = 0;
30391+       char           *str_bits = "\r\n\t \'\"<>()";
30392+
30393+       for (i = 0; i < str_len; i++, j++) {
30394+               if (str[i] == '%' || (!pasthostname && str[i] == '=')) {
30395+                       if (i + 2 < str_len) {
30396+                               if (isxdigit(str[i + 1]) && isxdigit(str[i + 2])) {
30397+                                       int             c1 = str[i + 1];
30398+                                       int             c2 = str[i + 2];
30399+                                       int             num = ( /* first character */
30400+                                                                                         ((c1 & 0xF)   /* take right half */
30401+                                                                                          +(9 * (c1 >> 6)))    /* add 9 if character is a-f or A-F */
30402+                                                                                         <<4   /* pack into the left half of the byte */
30403+                                               ) | (   /* second character */
30404+                                                               (c2 & 0xF)
30405+                                                               + (9 * (c2 >> 6))
30406+                                               );              /* leave it as the left half */
30407+                                       str[j] = tolower(num);
30408+                                       i += 2;
30409+                                       continue;
30410+                               }
30411+                       }
30412+               }
30413+               if (!pasthostname && (str[i] == '?' || str[i] == '/' || str[i] == '\\'))
30414+                       pasthostname = 1;
30415+               if (i + 1 < str_len) {
30416+                       if (str[i] == '=' && str[i + 1] == '\n') {
30417+                               j -= 1;
30418+                               i += 1;
30419+                               continue;
30420+                       }
30421+               }
30422+               if (i + 2 < str_len) {
30423+                       if (str[i] == '=' && str[i + 1] == '\r' && str[i + 2] == '\n') {
30424+                               j -= 1;
30425+                               i += 2;
30426+                               continue;
30427+                       }
30428+               }
30429+               found = str_chr(str_bits, str[i]);
30430+               if (str_bits[found])
30431+                       break;
30432+               str[j] = tolower(str[i]);
30433+       }
30434+       str[j] = '\0';
30435+       *strend = str + j + 1;
30436+       return str;
30437+}
30438+
30439+/*
30440+ * Returns:
30441+ * -1 on error
30442+ *  0 if domain wasn't cached
30443+ *  1 if domain was cached, and not blacklisted
30444+ *  2 if domain was cached, and blacklisted.
30445+ *
30446+ * text != NULL: host blacklisted, text == reason.
30447+ */
30448+static int
30449+cachefunc(char *uri, size_t urilen, char **text, int flag)
30450+{
30451+       static char     inbuf[2048];
30452+       static stralloc cachefile = { 0 }, reason = { 0 };
30453+       int             fd, i, n, textlen, match;
30454+       struct stat     st;
30455+       substdio        ss;
30456+
30457+       if (!do_cache)
30458+               return (0);
30459+       if (uri[i = str_chr(uri, '/')]) {
30460+               errno = EINVAL;
30461+               return (-1);
30462+       }
30463+       if (!stralloc_copyb(&cachefile, "control/cache", 13))
30464+               die_nomem();
30465+       if (!stralloc_0(&cachefile))
30466+               die_nomem();
30467+       if (access(cachefile.s, F_OK))
30468+               return (0);
30469+       cachefile.len--;
30470+       if (!stralloc_append(&cachefile, "/"))
30471+               die_nomem();
30472+       if (!stralloc_cats(&cachefile, uri))
30473+               die_nomem();
30474+       if (!stralloc_0(&cachefile))
30475+               die_nomem();
30476+       if (flag) { /*- add the cache */
30477+               if (!access(cachefile.s, F_OK))
30478+                       return (0);
30479+               if ((fd = open(cachefile.s, O_CREAT|O_WRONLY, *text ? 0600 : 0644)) == -1)
30480+                       my_error(cachefile.s, 0, 2);
30481+               if (*text) {
30482+                       textlen = str_len(*text);
30483+                       if ((n = write(fd, *text, textlen)) == -1) {
30484+                               close(fd);
30485+                               my_error("write", 0, 1);
30486+                       }
30487+               }
30488+               if (close(fd))
30489+                       my_error(cachefile.s, 0, 1);
30490+       } else {
30491+               if (stat(cachefile.s, &st) == -1) {
30492+                       if (errno == ENOENT)
30493+                               return (0);
30494+                       my_error("stat", 0, 1);
30495+                       return -1;
30496+               }
30497+               if (time(0) > st.st_mtime + cachelifetime) {
30498+                       if (unlink(cachefile.s)) {
30499+                               my_error("unlink", 0, 1);
30500+                               return -1;
30501+                       }
30502+                       return (0);
30503+               }
30504+               if ((fd = open(cachefile.s, O_RDONLY)) == -1)
30505+                       my_error(cachefile.s, 0, 2);
30506+               substdio_fdbuf(&ss, read, fd, inbuf, sizeof(inbuf));
30507+               if (getln(&ss, &reason, &match, '\n') == -1) {
30508+                       close(fd);
30509+                       return -1;
30510+               }
30511+               *text = reason.s;
30512+               close(fd);
30513+               return (((st.st_mode & 07777) == 0600) ? 2 : 1);
30514+       }
30515+       return (0);
30516+}
30517+
30518+static int
30519+getdnsip(stralloc *ip, stralloc *domain, int *code)
30520+{
30521+       char            x[IPFMT];
30522+       ipalloc         ia = { 0 };
30523+       int             len;
30524+
30525+       if (!stralloc_copys(ip, ""))
30526+               die_nomem();
30527+       switch(dns_ip(&ia, domain))
30528+       {
30529+       case DNS_MEM:
30530+               die_nomem();
30531+       case DNS_SOFT:
30532+               die_soft();
30533+       case DNS_HARD:
30534+               return 0;
30535+       case 1:
30536+               if (ia.len <= 0)
30537+                       die_soft();
30538+       }
30539+       if (code)
30540+               *code = *(&ia.ix->ip.d[3]);
30541+       len = ip_fmt(x, &ia.ix->ip);
30542+       if (!stralloc_copyb(ip, x, len))
30543+               die_nomem();
30544+       return 0;
30545+}
30546+
30547+/*- SURBL: Check surbl rcpt whitelist.  */
30548+int
30549+srwcheck(char *arg, int len)
30550+{
30551+       int             j;
30552+
30553+       if (!srwok)
30554+               return 0;
30555+       if (constmap(&mapsrw, arg, len))
30556+               return 1;
30557+       if ((j = byte_rchr(arg, len, '@')) < (len - 1)) {
30558+               if (constmap(&mapsrw, arg + j, len - j))
30559+                       return 1;
30560+       }
30561+       return 0;
30562+}
30563+
30564+int
30565+l2check(char *arg, int len)
30566+{
30567+       if (!l2ok)
30568+               return (0);
30569+       if (constmap(&mapl2, arg, len))
30570+               return 1;
30571+       return (0);
30572+}
30573+
30574+int
30575+l3check(char *arg, int len)
30576+{
30577+       if (!l3ok)
30578+               return (0);
30579+       if (constmap(&mapl3, arg, len))
30580+               return 1;
30581+       return (0);
30582+}
30583+
30584+/*
30585+ * Returns -1 on error.
30586+ * Returns 0 if host does not exist.
30587+ * Returns 1 if host exists.
30588+ */
30589+static int
30590+checkwhitelist(char *hostname, int hostlen)
30591+{
30592+       int             len;
30593+       char           *ptr;
30594+
30595+       for (ptr = whitelist.s, len = 0;len < whitelist.len;) {
30596+               if (!str_diffn(hostname, ptr, hostlen))
30597+                       return (1);
30598+               len += (str_len(ptr) + 1);
30599+               ptr = whitelist.s + len;
30600+       }
30601+       return (0);
30602+}
30603+
30604+static int
30605+getreason(int code, char **text)
30606+{
30607+       static stralloc reason = { 0 };
30608+
30609+       if (!stralloc_copyb(&reason, "blacklisted by ", 15))
30610+               die_nomem();
30611+       if (code & 64 && !stralloc_cats(&reason, debug ? "prolocation/jwspamspy" : "[jp]"))
30612+               die_nomem();
30613+       if (code & 32 && !stralloc_cats(&reason, debug ? "abusebutler " : "[ab]"))
30614+               die_nomem();
30615+       if (code & 16 && !stralloc_cats(&reason, debug ? "outblaze " : "[ob]"))
30616+               die_nomem();
30617+       if (code & 8 && !stralloc_cats(&reason, debug ? "phising " : "[ph]"))
30618+               die_nomem();
30619+       if (code & 2 && !stralloc_cats(&reason, debug ? "spamcop " : "[sc]"))
30620+               die_nomem();
30621+       if (code & 4 && !stralloc_cats(&reason, debug ? "w.stearns " : "[ws]"))
30622+               die_nomem();
30623+       if (!stralloc_0(&reason))
30624+               die_nomem();
30625+       *text = reason.s;
30626+       return (code >= 2);
30627+}
30628+
30629+static int
30630+checksurbl(char *uri, int urilen, char *surbldomain, char **text)
30631+{
30632+       static stralloc ip = { 0 };
30633+       static stralloc host = { 0 };
30634+       int             i, code = 0;
30635+
30636+       if ((i = checkwhitelist(uri, urilen)) == -1)
30637+               return -1;
30638+       else
30639+       if (i)
30640+               return (0);
30641+       if (stralloc_copys(&host, uri) == 0)
30642+               die_nomem();
30643+       if (stralloc_append(&host, ".") == 0)
30644+               die_nomem();
30645+       if (stralloc_cats(&host, surbldomain) == 0)
30646+               die_nomem();
30647+       if (!stralloc_0(&host))
30648+               die_nomem();
30649+       if (getdnsip(&ip, &host, &code) == -1)
30650+               return -1;
30651+       if (do_text && ip.len > 0) {
30652+               if (text) {
30653+                       if ((*text = dns_text(host.s)))
30654+                               return 2;
30655+               }
30656+               return 1;
30657+       }
30658+       if (code > 1)
30659+               return (getreason(code, text) ? 2 : 0);
30660+       return 0;
30661+}
30662+
30663+static int
30664+num_domains(const char *s)
30665+{
30666+       int             r = *s ? 1 : 0;
30667+
30668+       while (*s) {
30669+               if (*s++ == '.')
30670+                       ++r;
30671+       }
30672+       return r;
30673+}
30674+
30675+static char *
30676+remove_subdomains(char *orig, int output_domains)
30677+{
30678+       char           *s = orig + str_len((char *) orig);
30679+       int             dots = 0;
30680+
30681+       while (s > orig) {
30682+               if (*s == '.')
30683+                       ++dots;
30684+               if (dots == output_domains) {
30685+                       ++s;
30686+                       break;
30687+               }
30688+               --s;
30689+       }
30690+       return s;
30691+}
30692+
30693+/*
30694+ * Returns 0 if URI was erronous.
30695+ *         1 if URI was not blacklisted.
30696+ *         2 if URI was blacklisted.
30697+ */
30698+static int
30699+checkuri(char **ouri, char **text, size_t textlen)
30700+{
30701+       char           *uri = *ouri, *uriend, *ptr;
30702+       char            ipuri[IPFMT];
30703+       size_t          urilen = 0;
30704+       ip_addr         ip;
30705+       int             cached, blacklisted, i, level;
30706+
30707+       if (case_diffb(uri, 4, "http"))
30708+               return 0;
30709+       uri += 4;
30710+
30711+       /*- Check and skip http[s]?:[/\\][/\\]?  */
30712+       if (*uri == 's')
30713+               uri++;
30714+       if (*uri == ':' && (uri[1] == '/' || uri[1] == '\\'))
30715+               uri += 2;
30716+       else
30717+               return 0;
30718+       if (*uri == '/' || *uri == '\\')
30719+               uri++;
30720+       if (!isalpha(*uri) && !isdigit(*uri))
30721+               return 0;
30722+       uri_decode(uri, textlen, &uriend);
30723+       *ouri = uriend;
30724+       print_debug("Full    URI: ", uri, 0);
30725+       uri[(urilen = str_cspn(uri, "/\\?"))] = '\0';
30726+       if (uri[i = str_chr(uri, '@')])
30727+               uri += (i + 1);
30728+       uri[i = str_chr(uri, ':')] = 0;
30729+       if (ip_scan(uri, &ip)) {
30730+               ip_fmt(ipuri, &ip);
30731+               uri = ipuri;
30732+               print_debug("Proper IP: ", uri, 0);
30733+       } else {
30734+               urilen = str_len(uri);
30735+               print_debug("Full domain: ", uri, 0);
30736+               level = num_domains(uri);
30737+               if (level > 2) {
30738+                       ptr = remove_subdomains(uri, 3);
30739+                       if (l3check(ptr, str_len(ptr)))
30740+                               uri = remove_subdomains(uri, 4);
30741+                       else {
30742+                               ptr = remove_subdomains(uri, 2);
30743+                               if (l2check(ptr, str_len(ptr)))
30744+                                       uri = remove_subdomains(uri, 3);
30745+                               else
30746+                                       uri = remove_subdomains(uri, 2);
30747+                       }
30748+               } else
30749+               if (level > 1) {
30750+                       ptr = remove_subdomains(uri, 2);
30751+                       if (l2check(ptr, str_len(ptr)))
30752+                               uri = remove_subdomains(uri, 3);
30753+                       else
30754+                               uri = remove_subdomains(uri, 2);
30755+               }
30756+               print_debug("       Part: ", uri, 0);
30757+       }
30758+       urilen = str_len(uri);
30759+       cached = 1;
30760+       blacklisted = 0;
30761+       switch (cachefunc(uri, urilen, text, 0))
30762+       {
30763+       case 0:
30764+               cached = 0;
30765+               break;
30766+       case 1:
30767+               blacklisted = 0;
30768+               break;
30769+       case 2:
30770+               blacklisted = 1;
30771+               break;
30772+       }
30773+       if (cached == 0) {
30774+               switch (checksurbl(uri, urilen, surbldomain.s, text))
30775+               {
30776+               case -1:
30777+                       return -1;
30778+               case 0:
30779+                       blacklisted = 0;
30780+                       *text = (char *) 0;
30781+                       print_debug(uri, ": not blacklisted", 0);
30782+                       break;
30783+               case 1:
30784+                       *text = "No reason given";
30785+                       blacklisted = 1;
30786+                       print_debug(uri, ": blacklisted. reason - ", *text);
30787+                       break;
30788+               case 2:
30789+                       blacklisted = 2;
30790+                       print_debug(uri, ": blacklisted. reason - ", *text);
30791+                       break;
30792+               }
30793+               cachefunc(uri, urilen, text, 1);
30794+       }
30795+       return (blacklisted);
30796+}
30797+
30798+#define DEF_SURBL_DOMAIN "multi.surbl.org"
30799+
30800+static int      do_surbl = 1;
30801+
30802+static void
30803+setup()
30804+{
30805+       char           *x, *y, *rcpt;
30806+       int             i;
30807+
30808+       if ((rcpt = env_get("QMAILRCPTS"))) {
30809+               if ((srwok = control_readfile(&srw, "control/surblrcpt", 0)) == -1)
30810+                       die_control();
30811+               if (srwok && !constmap_init(&mapsrw, srw.s, srw.len, 0))
30812+                       die_nomem();
30813+       }
30814+       for (x = y = rcpt, i = 0;rcpt && *x;x++, i++) {
30815+               if (*x == '\n') {
30816+                       *x = 0;
30817+                       if (srwcheck(y, i)) {
30818+                               do_surbl = 0;
30819+                               return;
30820+                       }
30821+                       y = x + 1;
30822+                       *x = '\n';
30823+                       i = 0;
30824+               }
30825+       }
30826+       if ((l2ok = control_readfile(&l2, "control/level2-tlds", 0)) == -1)
30827+               die_control();
30828+       if (l2ok && !constmap_init(&mapl2, l2.s, l2.len, 0))
30829+               die_nomem();
30830+       if ((l3ok = control_readfile(&l3, "control/level3-tlds", 0)) == -1)
30831+               die_control();
30832+       if (l3ok && !constmap_init(&mapl3, l3.s, l3.len, 0))
30833+               die_nomem();
30834+       switch (control_readline(&surbldomain, "control/surbldomain"))
30835+       {
30836+       case -1:
30837+               die_control();
30838+       case 0:
30839+               if (!stralloc_copys(&surbldomain, DEF_SURBL_DOMAIN))
30840+                       die_nomem();
30841+               /*- flow through */
30842+       case 1:
30843+               if (!stralloc_0(&surbldomain))
30844+                       die_nomem();
30845+       }
30846+       if ((x = env_get("CACHELIFETIME")))
30847+               scan_int(x, &cachelifetime);
30848+       else
30849+       if (control_readint(&cachelifetime, "control/cachelifetime") == -1)
30850+               die_control();
30851+       if (control_readfile(&whitelist, "control/surbldomainwhite", 0) == -1)
30852+               die_control();
30853+       return;
30854+}
30855+
30856+int
30857+main(int argc, char **argv)
30858+{
30859+       stralloc        base64out = { 0 }, boundary = { 0 };
30860+       stralloc       *ptr;
30861+       char           *x, *reason = 0;
30862+       int             opt, in_header = 1, i, total_bl = 0, blacklisted, match, html_plain_text,
30863+                                       base64_decode, found_content_type = 0;
30864+
30865+       if (!(x = env_get("SURBL")))
30866+               do_surbl = 0;
30867+       while ((opt = getopt(argc, argv, "vtc")) != opteof) {
30868+               switch (opt) {
30869+               case 'c':
30870+                       do_cache = 0;
30871+                       break;
30872+               case 'v':
30873+                       debug = 1;
30874+                       break;
30875+               case 't':
30876+                       do_text = 1;
30877+                       break;
30878+               }
30879+       }
30880+       if (chdir(auto_qmail) == -1)
30881+               die_control();
30882+       if (do_surbl)
30883+               setup();
30884+       for (html_plain_text = base64_decode = 0;;) {
30885+               if (getln(&ssin, &line, &match, '\n') == -1)
30886+                       my_error("getln: ", 0, 1);
30887+               if (!match && line.len == 0)
30888+                       break;
30889+               if (substdio_put(&ssout, line.s, line.len))
30890+                       die_write();
30891+               if (!do_surbl)
30892+                       continue;
30893+               if (in_header) {
30894+                       if (!str_diffn(line.s, "Content-Type: ", 14)) {
30895+                               found_content_type = 1;
30896+                       }
30897+                       if (found_content_type) {
30898+                               for (i = 0;i < line.len; i++) {
30899+                                       if (case_startb(line.s + i, line.len - i, "boundary=")) {
30900+                                               if (line.s[i + 9] == '\"' && line.s[line.len -2] == '\"')
30901+                                               {
30902+                                                       if (!stralloc_copyb(&boundary, line.s + i + 10, line.len -i - 12))
30903+                                                               die_nomem();
30904+                                               } else
30905+                                               if (!stralloc_copyb(&boundary, line.s + i + 9, line.len - i - 10))
30906+                                                       die_nomem();
30907+                                               if (!stralloc_0(&boundary))
30908+                                                       die_nomem();
30909+                                               boundary.len--;
30910+                                       }
30911+                               }
30912+                       }
30913+                       if (!mess822_ok(&line))
30914+                               in_header = 0;
30915+               } else {
30916+                       if (!str_diffn(line.s, "Content-Type: ", 14)) {
30917+                               if (!str_diffn(line.s + 14, "message/rfc822", 14) ||
30918+                                       !str_diffn(line.s + 14, "text/html", 9) ||
30919+                                       !str_diffn(line.s + 14, "text/plain", 10))
30920+                                               html_plain_text = 1;
30921+                               else
30922+                                               html_plain_text = 0;
30923+                       }
30924+                       if (html_plain_text && !str_diffn(line.s, "Content-Transfer-Encoding: ", 27)) {
30925+                               if (!str_diffn(line.s + 27, "base64", 6))
30926+                                       base64_decode = 1;
30927+                               else
30928+                                       base64_decode = 0;
30929+                       }
30930+                       if (line.len == 1)
30931+                               continue;
30932+                       if (base64_decode) {
30933+                               if (!str_diffn(line.s, "Content-", 8))
30934+                                       continue;
30935+                               if (!str_diffn(line.s + 2, boundary.s, boundary.len)) {
30936+                                       base64_decode = 0;
30937+                                       continue;
30938+                               }
30939+                               if (b64decode((const unsigned char *) line.s, line.len - 1, &base64out) == -1)
30940+                                       die_nomem();
30941+                               ptr = &base64out;
30942+                       } else
30943+                               ptr = &line;
30944+                       for (blacklisted = -1, i = 0;i < ptr->len; i++) {
30945+                               if (case_startb(line.s + i, ptr->len - i, "http:")) {
30946+                                       x = ptr->s + i;
30947+                                       switch (checkuri(&x, &reason, ptr->len - i))
30948+                                       {
30949+                                       case -1:
30950+                                               my_error("checkuri", 0, 111);
30951+                                       case 0: /*- no valid uri in line */
30952+                                               blacklisted = 0;
30953+                                               break;
30954+                                       case 1:
30955+                                       case 2:
30956+                                               blacklisted = 1;
30957+                                               break;
30958+                                       }
30959+                               }
30960+                               if (blacklisted == 1) {
30961+                                       total_bl++;
30962+                                       break;
30963+                               }
30964+                       }
30965+               }
30966+       } /*- for (html_plain_text = base64_decode = 0;;) { */
30967+       if (substdio_flush(&ssout) == -1)
30968+               die_write();
30969+       if (do_surbl && total_bl) {
30970+               logerrf("Dmessage contains an URL listed in SURBL blocklist");
30971+               _exit (88); /*- custom error */
30972+       }
30973+       return (0);
30974+}
30975+
30976+void
30977+getversion_surblfilter_c()
30978+{
30979+       static char    *x = "$Id: surblfilter.c,v 1.4 2011-07-13 22:28:32+05:30 Cprogrammer Exp mbhangui $";
30980+
30981+       x++;
30982+}
30983+
30984diff -ruN ../netqmail-1.06-original/surblqueue.sh netqmail-1.06/surblqueue.sh
30985--- ../netqmail-1.06-original/surblqueue.sh     1970-01-01 01:00:00.000000000 +0100
30986+++ netqmail-1.06/surblqueue.sh 2019-02-27 20:57:13.409024860 +0100
30987@@ -0,0 +1,33 @@
30988+#!/bin/sh
30989+# I should be called by qmail-smtpd or anything that calls qmail-queue
30990+#
30991+if [ -f /bin/mktemp ] ; then
30992+       MKTEMP=/bin/mktemp
30993+elif [ -f /usr/bin/mktemp ] ; then
30994+       MKTEMP=/usr/bin/mktemp
30995+else
30996+       MKTEMP=mktemp
30997+fi
30998+out=`$MKTEMP -t surblXXXXXXXXXX`
30999+if [ $? -ne 0 ] ; then
31000+       echo "mktemp: unable to create temp files" 1>&2
31001+       exit 111
31002+fi
31003+#
31004+# Redirect standard error to 4 so that qmail_open() will pick up the error
31005+#
31006+QMAIL/bin/surblfilter > $out 2>&4
31007+status=$?
31008+if [ $status -eq 0 ] ; then
31009+       exec 0<$out
31010+       /bin/rm -f $out
31011+       # use SURBLQUEUE to execute queue program (thanks Roberto Puzzanghera)
31012+       if [ "$SURBLQUEUE" != "" -a -x "$SURBLQUEUE" ]; then
31013+               exec $SURBLQUEUE
31014+       else
31015+               exec QMAIL/bin/qmail-queue
31016+       fi
31017+else
31018+       /bin/rm -f $out
31019+       exit $status
31020+fi
31021diff -ruN ../netqmail-1.06-original/tai.h netqmail-1.06/tai.h
31022--- ../netqmail-1.06-original/tai.h     1970-01-01 01:00:00.000000000 +0100
31023+++ netqmail-1.06/tai.h 2019-02-27 20:57:13.409024860 +0100
31024@@ -0,0 +1,34 @@
31025+/*
31026+ * $Log: tai.h,v $
31027+ * Revision 1.3  2004-10-11 14:15:10+05:30  Cprogrammer
31028+ * added function prototypes
31029+ *
31030+ * Revision 1.2  2004-09-19 22:49:23+05:30  Cprogrammer
31031+ * added tai_unix macro
31032+ *
31033+ * Revision 1.1  2004-06-16 01:20:25+05:30  Cprogrammer
31034+ * Initial revision
31035+ *
31036+ */
31037+#ifndef TAI_H
31038+#define TAI_H
31039+
31040+#include "uint64.h"
31041+
31042+struct tai
31043+{
31044+       uint64          x;
31045+};
31046+
31047+#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64) (u)))
31048+#define tai_approx(t) ((double) ((t)->x))
31049+#define tai_less(t,u) ((t)->x < (u)->x)
31050+#define TAI_PACK 8
31051+
31052+void            tai_now(struct tai *);
31053+void            tai_add();
31054+void            tai_sub(struct tai *, struct tai *, struct tai *);
31055+void            tai_pack(char *, struct tai *);
31056+void            tai_unpack(char *, struct tai *);
31057+
31058+#endif
31059diff -ruN ../netqmail-1.06-original/tcp-env.c netqmail-1.06/tcp-env.c
31060--- ../netqmail-1.06-original/tcp-env.c 1998-06-15 12:53:16.000000000 +0200
31061+++ netqmail-1.06/tcp-env.c     2019-02-27 20:57:13.410024849 +0100
31062@@ -10,6 +10,7 @@
31063 #include "scan.h"
31064 #include "subgetopt.h"
31065 #include "ip.h"
31066+#include "strsalloc.h"
31067 #include "dns.h"
31068 #include "byte.h"
31069 #include "remoteinfo.h"
31070@@ -34,6 +35,7 @@
31071 int argc;
31072 char *argv[];
31073 {
31074+ strsalloc ssa = {0};
31075  int dummy;
31076  char *proto;
31077  int opt;
31078@@ -74,12 +76,13 @@
31079    temp[ip_fmt(temp,&iplocal)] = 0;
31080    if (!env_put2("TCPLOCALIP",temp)) die();
31081 
31082-   switch(dns_ptr(&localname,&iplocal))
31083+   switch(dns_ptr(&ssa,&iplocal))
31084     {
31085      case DNS_MEM: die();
31086      case DNS_SOFT:
31087        if (!stralloc_copys(&localname,"softdnserror")) die();
31088      case 0:
31089+       if (!stralloc_copy(&localname,&ssa.sa[0])) die();
31090        if (!stralloc_0(&localname)) die();
31091        case_lowers(localname.s);
31092        if (!env_put2("TCPLOCALHOST",localname.s)) die();
31093@@ -99,12 +102,13 @@
31094    temp[ip_fmt(temp,&ipremote)] = 0;
31095    if (!env_put2("TCPREMOTEIP",temp)) die();
31096 
31097-   switch(dns_ptr(&remotename,&ipremote))
31098+   switch(dns_ptr(&ssa,&ipremote))
31099     {
31100      case DNS_MEM: die();
31101      case DNS_SOFT:
31102        if (!stralloc_copys(&remotename,"softdnserror")) die();
31103      case 0:
31104+       if (!stralloc_copy(&remotename,&ssa.sa[0])) die();
31105        if (!stralloc_0(&remotename)) die();
31106        case_lowers(remotename.s);
31107        if (!env_put2("TCPREMOTEHOST",remotename.s)) die();
31108diff -ruN ../netqmail-1.06-original/time_t_size.c netqmail-1.06/time_t_size.c
31109--- ../netqmail-1.06-original/time_t_size.c     1970-01-01 01:00:00.000000000 +0100
31110+++ netqmail-1.06/time_t_size.c 2019-05-23 15:12:30.140092743 +0200
31111@@ -0,0 +1,8 @@
31112+#include <stdio.h>
31113+#include <time.h>
31114+int
31115+main()
31116+{
31117+       printf("#define SIZEOF_TIME_T %d\n", sizeof(time_t));
31118+       return (0);
31119+}
31120diff -ruN ../netqmail-1.06-original/timeoutconn.c netqmail-1.06/timeoutconn.c
31121--- ../netqmail-1.06-original/timeoutconn.c     1998-06-15 12:53:16.000000000 +0200
31122+++ netqmail-1.06/timeoutconn.c 2019-02-27 20:57:13.410024849 +0100
31123@@ -10,9 +10,10 @@
31124 #include "byte.h"
31125 #include "timeoutconn.h"
31126 
31127-int timeoutconn(s,ip,port,timeout)
31128+int timeoutconn(s,ip,outip,port,timeout)
31129 int s;
31130 struct ip_address *ip;
31131+struct ip_address *outip;
31132 unsigned int port;
31133 int timeout;
31134 {
31135@@ -22,6 +23,13 @@
31136   fd_set wfds;
31137   struct timeval tv;
31138 
31139+  /* bind() an outgoing ipaddr */
31140+  byte_zero(&sin,sizeof(sin));
31141+  byte_copy(&sin.sin_addr.s_addr,4,outip);
31142+  sin.sin_family = AF_INET;
31143+
31144+  if (-1 == bind(s,(struct sockaddr *) &sin,sizeof(sin))) return -1;
31145+
31146   byte_zero(&sin,sizeof(sin));
31147   byte_copy(&sin.sin_addr,4,ip);
31148   x = (char *) &sin.sin_port;
31149diff -ruN ../netqmail-1.06-original/tls.c netqmail-1.06/tls.c
31150--- ../netqmail-1.06-original/tls.c     1970-01-01 01:00:00.000000000 +0100
31151+++ netqmail-1.06/tls.c 2019-04-09 20:56:31.139694929 +0200
31152@@ -0,0 +1,27 @@
31153+#ifdef TLS
31154+#include "exit.h"
31155+#include "error.h"
31156+#include <openssl/ssl.h>
31157+#include <openssl/err.h>
31158+
31159+int smtps = 0;
31160+SSL *ssl = NULL;
31161+
31162+void ssl_free(SSL *myssl) { SSL_shutdown(myssl); SSL_free(myssl); }
31163+void ssl_exit(int status) { if (ssl) ssl_free(ssl); _exit(status); }
31164+
31165+const char *ssl_error()
31166+{
31167+  int r = ERR_get_error();
31168+  if (!r) return NULL;
31169+  SSL_load_error_strings();
31170+  return ERR_error_string(r, NULL);
31171+}
31172+const char *ssl_error_str()
31173+{
31174+  const char *err = ssl_error();
31175+  if (err) return err;
31176+  if (!errno) return 0;
31177+  return (errno == error_timeout) ? "timed out" : error_str(errno);
31178+}
31179+#endif
31180diff -ruN ../netqmail-1.06-original/tls.h netqmail-1.06/tls.h
31181--- ../netqmail-1.06-original/tls.h     1970-01-01 01:00:00.000000000 +0100
31182+++ netqmail-1.06/tls.h 2019-02-27 20:57:13.410024849 +0100
31183@@ -0,0 +1,16 @@
31184+#ifndef TLS_H
31185+#define TLS_H
31186+
31187+#include <openssl/ssl.h>
31188+
31189+extern int smtps;
31190+extern SSL *ssl;
31191+
31192+void ssl_free(SSL *myssl);
31193+void ssl_exit(int status);
31194+# define _exit ssl_exit
31195+
31196+const char *ssl_error();
31197+const char *ssl_error_str();
31198+
31199+#endif
31200diff -ruN ../netqmail-1.06-original/tryulong64.c netqmail-1.06/tryulong64.c
31201--- ../netqmail-1.06-original/tryulong64.c      1970-01-01 01:00:00.000000000 +0100
31202+++ netqmail-1.06/tryulong64.c  2019-02-27 20:57:13.410024849 +0100
31203@@ -0,0 +1,47 @@
31204+/*
31205+ * $Log: tryulong64.c,v $
31206+ * Revision 1.1  2004-05-14 00:45:23+05:30  Cprogrammer
31207+ * Initial revision
31208+ *
31209+ */
31210+void
31211+main()
31212+{
31213+       unsigned long   u;
31214+       u = 1;
31215+       u += u;
31216+       u += u;
31217+       u += u;
31218+       u += u;
31219+       u += u;
31220+       u += u;
31221+       u += u;
31222+       u += u;
31223+       u += u;
31224+       u += u;
31225+       u += u;
31226+       u += u;
31227+       u += u;
31228+       u += u;
31229+       u += u;
31230+       u += u;
31231+       u += u;
31232+       u += u;
31233+       u += u;
31234+       u += u;
31235+       u += u;
31236+       u += u;
31237+       u += u;
31238+       u += u;
31239+       u += u;
31240+       u += u;
31241+       u += u;
31242+       u += u;
31243+       u += u;
31244+       u += u;
31245+       u += u;
31246+       u += u;
31247+       if (!u)
31248+               _exit(1);
31249+       _exit(0);
31250+}
31251diff -ruN ../netqmail-1.06-original/uint64.h1 netqmail-1.06/uint64.h1
31252--- ../netqmail-1.06-original/uint64.h1 1970-01-01 01:00:00.000000000 +0100
31253+++ netqmail-1.06/uint64.h1     2019-02-27 20:57:13.410024849 +0100
31254@@ -0,0 +1,12 @@
31255+/*
31256+ * $Log: uint64.h1,v $
31257+ * Revision 1.1  2004-10-22 15:00:08+05:30  Cprogrammer
31258+ * Initial revision
31259+ *
31260+ */
31261+#ifndef UINT64_H
31262+#define UINT64_H
31263+
31264+typedef unsigned long uint64;
31265+
31266+#endif
31267diff -ruN ../netqmail-1.06-original/uint64.h2 netqmail-1.06/uint64.h2
31268--- ../netqmail-1.06-original/uint64.h2 1970-01-01 01:00:00.000000000 +0100
31269+++ netqmail-1.06/uint64.h2     2019-02-27 20:57:13.410024849 +0100
31270@@ -0,0 +1,12 @@
31271+/*
31272+ * $Log: uint64.h2,v $
31273+ * Revision 1.1  2004-10-22 15:00:36+05:30  Cprogrammer
31274+ * Initial revision
31275+ *
31276+ */
31277+#ifndef UINT64_H
31278+#define UINT64_H
31279+
31280+typedef unsigned long long uint64;
31281+
31282+#endif
31283diff -ruN ../netqmail-1.06-original/update_tmprsadh.sh netqmail-1.06/update_tmprsadh.sh
31284--- ../netqmail-1.06-original/update_tmprsadh.sh        1970-01-01 01:00:00.000000000 +0100
31285+++ netqmail-1.06/update_tmprsadh.sh    2019-02-27 20:57:13.410024849 +0100
31286@@ -0,0 +1,22 @@
31287+#!/bin/sh
31288+
31289+# Update temporary RSA and DH keys
31290+# Frederik Vermeulen 2004-05-31 GPL
31291+#
31292+# Slightly modified by Roberto Puzzanghera
31293+# to chown the .pem files to vpopmail
31294+
31295+umask 0077 || exit 0
31296+
31297+export PATH="$PATH:/usr/local/bin/ssl:/usr/sbin"
31298+
31299+openssl genrsa -out QMAIL/control/rsa2048.new 2048 &&
31300+chmod 600 QMAIL/control/rsa2048.new &&
31301+chown vpopmail:vchkpw QMAIL/control/rsa2048.new &&
31302+mv -f QMAIL/control/rsa2048.new QMAIL/control/rsa2048.pem
31303+
31304+openssl dhparam -2 -out QMAIL/control/dh2048.new 2048 &&
31305+chmod 600 QMAIL/control/dh2048.new &&
31306+chown vpopmail:vchkpw QMAIL/control/dh2048.new &&
31307+mv -f QMAIL/control/dh2048.new QMAIL/control/dh2048.pem
31308+
31309diff -ruN ../netqmail-1.06-original/wildmat.c netqmail-1.06/wildmat.c
31310--- ../netqmail-1.06-original/wildmat.c 1970-01-01 01:00:00.000000000 +0100
31311+++ netqmail-1.06/wildmat.c     2019-02-27 20:57:13.410024849 +0100
31312@@ -0,0 +1,173 @@
31313+/*-** wildmat.c.orig   Wed Dec  3 11:46:31 1997
31314+ * $Revision: 1.6 $
31315+ * Do shell-style pattern matching for ?, \, [], and * characters.
31316+ * Might not be robust in face of malformed patterns; e.g., "foo[a-"
31317+ * could cause a segmentation violation.  It is 8bit clean.
31318+ *
31319+ * Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
31320+ * Rich $alz is now <rsalz@osf.org>.
31321+ * April, 1991:  Replaced mutually-recursive calls with in-line code
31322+ * for the star character.
31323+ *
31324+ * Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code.
31325+ * This can greatly speed up failing wildcard patterns.  For example:
31326+ * pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-*
31327+ * text 1:  -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1
31328+ * text 2:  -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1
31329+ * Text 1 matches with 51 calls, while text 2 fails with 54 calls.  Without
31330+ * the ABORT code, it takes 22310 calls to fail.  Ugh.  The following
31331+ * explanation is from Lars:
31332+ * The precondition that must be fulfilled is that DoMatch will consume
31333+ * at least one character in text.  This is true if *p is neither '*' no
31334+ * '\0'.)  The last return has ABORT instead of FALSE to avoid quadratic
31335+ * behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx".  With
31336+ * FALSE, each star-loop has to run to the end of the text; with ABORT
31337+ * only the last one does.
31338+ *
31339+ * Once the control of one instance of DoMatch enters the star-loop, that
31340+ * instance will return either TRUE or ABORT, and any calling instance
31341+ * will therefore return immediately after (without calling recursively
31342+ * again).  In effect, only one star-loop is ever active.  It would be
31343+ * possible to modify the code to maintain this context explicitly,
31344+ * eliminating all recursive calls at the cost of some complication and
31345+ * loss of clarity (and the ABORT stuff seems to be unclear enough by
31346+ * itself).  I think it would be unwise to try to get this into a
31347+ * released version unless you have a good test data base to try it out
31348+ * on.
31349+ */
31350+#define TRUE                    1
31351+#define FALSE                   0
31352+#define ABORT                  -1
31353+
31354+
31355+/*- What character marks an inverted character class?  */
31356+#define NEGATE_CLASS           '^'
31357+/*- Is "*" a common pattern?  */
31358+#define OPTIMIZE_JUST_STAR
31359+/*- Do tar(1) matching rules, which ignore a trailing slash?  */
31360+#undef MATCH_TAR_PATTERN
31361+
31362+
31363+/*- Match text and p, return TRUE, FALSE, or ABORT.  */
31364+static int
31365+DoMatch(text, p)
31366+       register char  *text;
31367+       register char  *p;
31368+{
31369+       register int    last;
31370+       register int    matched;
31371+       register int    reverse;
31372+
31373+       for (; *p; text++, p++)
31374+       {
31375+               if (*text == '\0' && *p != '*')
31376+                       return ABORT;
31377+               switch (*p)
31378+               {
31379+               case '\\': /*- Literal match with following character.  */
31380+                       p++;
31381+                       /*- FALLTHROUGH */
31382+               default:
31383+                       if (*text != *p)
31384+                               return FALSE;
31385+                       continue;
31386+               case '?': /*- Match anything. */
31387+                       continue;
31388+               case '*':
31389+                       /*- Consecutive stars act just like one.  */
31390+                       while (*++p == '*')
31391+                               continue;
31392+                       /*- Trailing star matches everything.  */
31393+                       if (*p == '\0')
31394+                               return TRUE;
31395+                       while (*text)
31396+                               if ((matched = DoMatch(text++, p)) != FALSE)
31397+                                       return matched;
31398+                       return ABORT;
31399+               case '[':
31400+                       reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
31401+                       /*- Inverted character class.  */
31402+                       if (reverse)
31403+                               p++;
31404+                       matched = FALSE;
31405+                       if (p[1] == ']' || p[1] == '-')
31406+                       {
31407+                               if (*++p == *text)
31408+                                       matched = TRUE;
31409+                       }
31410+                       for (last = *p; *++p && *p != ']'; last = *p)
31411+                       {
31412+                               /*- This next line requires a good C compiler.  */
31413+                               if (*p == '-' && p[1] != ']' ? *text <= *++p && *text >= last : *text == *p)
31414+                                       matched = TRUE;
31415+                       }
31416+                       if (matched == reverse)
31417+                               return FALSE;
31418+                       continue;
31419+               }
31420+       }
31421+
31422+#ifdef MATCH_TAR_PATTERN
31423+       if (*text == '/')
31424+               return TRUE;
31425+#endif /*- MATCH_TAR_ATTERN */
31426+       return *text == '\0';
31427+}
31428+
31429+
31430+/*- User-level routine.  Returns TRUE or FALSE.  */
31431+int
31432+wildmat_internal(text, p)
31433+       char           *text;
31434+       char           *p;
31435+{
31436+#ifdef OPTIMIZE_JUST_STAR
31437+       if (p[0] == '*' && p[1] == '\0')
31438+               return TRUE;
31439+#endif /*- OPTIMIZE_JUST_STAR */
31440+       return DoMatch(text, p) == TRUE;
31441+}
31442+
31443+#if    defined(TEST)
31444+include < stdio.h >
31445+/*- Yes, we use gets not fgets.  Sue me.  */
31446+
31447+int
31448+main()
31449+{
31450+       char            p[80];
31451+       char            text[80];
31452+
31453+       printf("Wildmat tester.  Enter pattern, then strings to test.\n");
31454+       printf("A blank line gets prompts for a new pattern; a blank pattern\n");
31455+       printf("exits the program.\n");
31456+       for (;;)
31457+       {
31458+               printf("\nEnter pattern:  ");
31459+               (void) fflush(stdout);
31460+               if (gets(p) == NULL || p[0] == '\0')
31461+                       break;
31462+               for (;;)
31463+               {
31464+                       printf("Enter text:  ");
31465+                       (void) fflush(stdout);
31466+                       if (gets(text) == NULL)
31467+                               exit(0);
31468+                       /*- Blank line; go back and get a new pattern.  */
31469+                       if (text[0] == '\0')
31470+                               break;
31471+                       printf("      %s\n", wildmat_internal(text, p) ? "YES" : "NO");
31472+               }
31473+       }
31474+       exit(0);
31475+       /*- NOTREACHED */
31476+}
31477+#endif /*- defined(TEST) */
31478+
31479+void
31480+getversion_wildmat_internal_c()
31481+{
31482+       static char    *x = "$Id: wildmat.c,v 1.6 2008-08-03 18:26:33+05:30 Cprogrammer Stab mbhangui $";
31483+       x++;
31484+       x--;
31485+}
Note: See TracBrowser for help on using the repository browser.