proto.m4 revision 203004
1205408Srdivackydivert(-1)
2205408Srdivacky#
3205408Srdivacky# Copyright (c) 1998-2009 Sendmail, Inc. and its suppliers.
4205408Srdivacky#	All rights reserved.
5205408Srdivacky# Copyright (c) 1983, 1995 Eric P. Allman.  All rights reserved.
6205408Srdivacky# Copyright (c) 1988, 1993
7205408Srdivacky#	The Regents of the University of California.  All rights reserved.
8205408Srdivacky#
9205408Srdivacky# By using this file, you agree to the terms and conditions set
10205408Srdivacky# forth in the LICENSE file which can be found at the top level of
11205408Srdivacky# the sendmail distribution.
12205408Srdivacky#
13205408Srdivacky#
14205408Srdivackydivert(0)
15205408Srdivacky
16205408SrdivackyVERSIONID(`$Id: proto.m4,v 8.741 2009/12/11 00:04:53 ca Exp $')
17249423Sdim
18218893Sdim# level CF_LEVEL config file format
19205408SrdivackyV`'CF_LEVEL/ifdef(`VENDOR_NAME', `VENDOR_NAME', `Berkeley')
20205408Srdivackydivert(-1)
21205408Srdivacky
22205408Srdivackydnl if MAILER(`local') not defined: do it ourself; be nice
23205408Srdivackydnl maybe we should issue a warning?
24218893Sdimifdef(`_MAILER_local_',`', `MAILER(local)')
25218893Sdim
26218893Sdim# do some sanity checking
27226633Sdimifdef(`__OSTYPE__',,
28243830Sdim	`errprint(`*** ERROR: No system type defined (use OSTYPE macro)
29243830Sdim')')
30218893Sdim
31218893Sdim# pick our default mailers
32243830Sdimifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `esmtp')')
33218893Sdimifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')')
34218893Sdimifdef(`confRELAY_MAILER',,
35218893Sdim	`define(`confRELAY_MAILER',
36218893Sdim		`ifdef(`_MAILER_smtp_', `relay',
37218893Sdim			`ifdef(`_MAILER_uucp', `uucp-new', `unknown')')')')
38226633Sdimifdef(`confUUCP_MAILER',, `define(`confUUCP_MAILER', `uucp-old')')
39218893Sdimdefine(`_SMTP_', `confSMTP_MAILER')dnl		for readability only
40218893Sdimdefine(`_LOCAL_', `confLOCAL_MAILER')dnl	for readability only
41249423Sdimdefine(`_RELAY_', `confRELAY_MAILER')dnl	for readability only
42234353Sdimdefine(`_UUCP_', `confUUCP_MAILER')dnl		for readability only
43249423Sdim
44205408Srdivacky# back compatibility with old config files
45205408Srdivackyifdef(`confDEF_GROUP_ID',
46226633Sdim`errprint(`*** confDEF_GROUP_ID is obsolete.
47239462Sdim    Use confDEF_USER_ID with a colon in the value instead.
48226633Sdim')')
49226633Sdimifdef(`confREAD_TIMEOUT',
50226633Sdim`errprint(`*** confREAD_TIMEOUT is obsolete.
51234353Sdim    Use individual confTO_<timeout> parameters instead.
52234353Sdim')')
53234353Sdimifdef(`confMESSAGE_TIMEOUT',
54234353Sdim	`define(`_ARG_', index(confMESSAGE_TIMEOUT, /))
55234353Sdim	 ifelse(_ARG_, -1,
56234353Sdim		`define(`confTO_QUEUERETURN', confMESSAGE_TIMEOUT)',
57234353Sdim		`define(`confTO_QUEUERETURN',
58243830Sdim			substr(confMESSAGE_TIMEOUT, 0, _ARG_))
59234353Sdim		 define(`confTO_QUEUEWARN',
60234353Sdim			substr(confMESSAGE_TIMEOUT, eval(_ARG_+1)))')')
61234353Sdimifdef(`confMIN_FREE_BLOCKS', `ifelse(index(confMIN_FREE_BLOCKS, /), -1,,
62234353Sdim`errprint(`*** compound confMIN_FREE_BLOCKS is obsolete.
63234353Sdim    Use confMAX_MESSAGE_SIZE for the second part of the value.
64234353Sdim')')')
65234353Sdim
66234353Sdim
67234353Sdim# Sanity check on ldap_routing feature
68234353Sdim# If the user doesn't specify a new map, they better have given as a
69234353Sdim# default LDAP specification which has the LDAP base (and most likely the host)
70234353Sdimifdef(`confLDAP_DEFAULT_SPEC',, `ifdef(`_LDAP_ROUTING_WARN_', `errprint(`
71234353SdimWARNING: Using default FEATURE(ldap_routing) map definition(s)
72234353Sdimwithout setting confLDAP_DEFAULT_SPEC option.
73234353Sdim')')')dnl
74234353Sdim
75234353Sdim# clean option definitions below....
76234353Sdimdefine(`_OPTION', `ifdef(`$2', `O $1`'ifelse(defn(`$2'), `',, `=$2')', `#O $1`'ifelse(`$3', `',,`=$3')')')dnl
77234353Sdim
78234353Sdimdnl required to "rename" the check_* rulesets...
79234353Sdimdefine(`_U_',ifdef(`_DELAY_CHECKS_',`',`_'))
80234353Sdimdnl default relaying denied message
81234353Sdimifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG',
82234353Sdimifdef(`_USE_AUTH_', `"550 Relaying denied. Proper authentication required."', `"550 Relaying denied"'))')
83234353Sdimifdef(`confRCPTREJ_MSG', `', `define(`confRCPTREJ_MSG', `"550 Mailbox disabled for this recipient"')')
84234353Sdimdefine(`_CODE553', `553')
85234353Sdimdivert(0)dnl
86234353Sdim
87239462Sdim# override file safeties - setting this option compromises system security,
88234353Sdim# addressing the actual file configuration problem is preferred
89234353Sdim# need to set this before any file actions are encountered in the cf file
90234353Sdim_OPTION(DontBlameSendmail, `confDONT_BLAME_SENDMAIL', `safe')
91234353Sdim
92234353Sdim# default LDAP map specification
93243830Sdim# need to set this now before any LDAP maps are defined
94243830Sdim_OPTION(LDAPDefaultSpec, `confLDAP_DEFAULT_SPEC', `-h localhost')
95249423Sdim
96249423Sdim##################
97249423Sdim#   local info   #
98249423Sdim##################
99234353Sdim
100243830Sdim# my LDAP cluster
101234353Sdim# need to set this before any LDAP lookups are done (including classes)
102234353Sdimifdef(`confLDAP_CLUSTER', `D{sendmailMTACluster}`'confLDAP_CLUSTER', `#D{sendmailMTACluster}$m')
103234353Sdim
104234353SdimCwlocalhost
105234353Sdimifdef(`USE_CW_FILE',
106249423Sdim`# file containing names of hosts for which we receive email
107249423SdimFw`'confCW_FILE',
108234353Sdim	`dnl')
109234353Sdim
110234353Sdim# my official domain name
111234353Sdim# ... `define' this only if sendmail cannot automatically determine your domain
112234353Sdimifdef(`confDOMAIN_NAME', `Dj`'confDOMAIN_NAME', `#Dj$w.Foo.COM')
113234353Sdim
114234353Sdim# host/domain names ending with a token in class P are canonical
115234353SdimCP.
116234353Sdim
117234353Sdimifdef(`UUCP_RELAY',
118249423Sdim`# UUCP relay host
119249423SdimDY`'UUCP_RELAY
120249423SdimCPUUCP
121249423Sdim
122243830Sdim')dnl
123234353Sdimifdef(`BITNET_RELAY',
124234353Sdim`#  BITNET relay host
125234353SdimDB`'BITNET_RELAY
126234353SdimCPBITNET
127234353Sdim
128243830Sdim')dnl
129234353Sdimifdef(`DECNET_RELAY',
130234353Sdim`define(`_USE_DECNET_SYNTAX_', 1)dnl
131226633Sdim# DECnet relay host
132234353SdimDC`'DECNET_RELAY
133226633SdimCPDECNET
134226633Sdim
135234353Sdim')dnl
136226633Sdimifdef(`FAX_RELAY',
137226633Sdim`# FAX relay host
138234353SdimDF`'FAX_RELAY
139234353SdimCPFAX
140226633Sdim
141226633Sdim')dnl
142234353Sdim# "Smart" relay host (may be null)
143226633SdimDS`'ifdef(`SMART_HOST', `SMART_HOST')
144226633Sdim
145234353Sdimifdef(`LUSER_RELAY', `dnl
146234353Sdim# place to which unknown users should be forwarded
147226633SdimKuser user -m -a<>
148234353SdimDL`'LUSER_RELAY',
149226633Sdim`dnl')
150226633Sdim
151234353Sdim# operators that cannot be in local usernames (i.e., network indicators)
152234353SdimCO @ % ifdef(`_NO_UUCP_', `', `!')
153234353Sdim
154226633Sdim# a class with just dot (for identifying canonical names)
155234353SdimC..
156205408Srdivacky
157205408Srdivacky# a class with just a left bracket (for identifying domain literals)
158226633SdimC[[
159226633Sdim
160226633Sdimifdef(`_ACCESS_TABLE_', `dnl
161226633Sdim# access_db acceptance class
162226633SdimC{Accept}OK RELAY
163226633Sdimifdef(`_DELAY_COMPAT_8_10_',`dnl
164226633Sdimifdef(`_BLACKLIST_RCPT_',`dnl
165226633Sdim# possible access_db RHS for spam friends/haters
166226633SdimC{SpamTag}SPAMFRIEND SPAMHATER')')',
167226633Sdim`dnl')
168205408Srdivacky
169205408Srdivackydnl mark for "domain is ok" (resolved or accepted anyway)
170226633Sdimdefine(`_RES_OK_', `OKR')dnl
171226633Sdimifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',`dnl',`dnl
172226633Sdim# Resolve map (to check if a host exists in check_mail)
173226633SdimKresolve host -a<_RES_OK_> -T<TEMP>')
174226633SdimC{ResOk}_RES_OK_
175226633Sdim
176226633Sdimifdef(`_NEED_MACRO_MAP_', `dnl
177226633Sdimifdef(`_MACRO_MAP_', `', `# macro storage map
178226633Sdimdefine(`_MACRO_MAP_', `1')dnl
179226633SdimKmacro macro')', `dnl')
180226633Sdim
181226633Sdimifdef(`confCR_FILE', `dnl
182226633Sdim# Hosts for which relaying is permitted ($=R)
183226633SdimFR`'confCR_FILE',
184226633Sdim`dnl')
185226633Sdim
186226633Sdimdefine(`TLS_SRV_TAG', `"TLS_Srv"')dnl
187226633Sdimdefine(`TLS_CLT_TAG', `"TLS_Clt"')dnl
188226633Sdimdefine(`TLS_RCPT_TAG', `"TLS_Rcpt"')dnl
189226633Sdimdefine(`TLS_TRY_TAG', `"Try_TLS"')dnl
190226633Sdimdefine(`SRV_FEAT_TAG', `"Srv_Features"')dnl
191226633Sdimdnl this may be useful in other contexts too
192226633Sdimifdef(`_ARITH_MAP_', `', `# arithmetic map
193226633Sdimdefine(`_ARITH_MAP_', `1')dnl
194226633SdimKarith arith')
195226633Sdimifdef(`_ACCESS_TABLE_', `dnl
196226633Sdimifdef(`_MACRO_MAP_', `', `# macro storage map
197226633Sdimdefine(`_MACRO_MAP_', `1')dnl
198226633SdimKmacro macro')
199226633Sdim# possible values for TLS_connection in access map
200205408SrdivackyC{Tls}VERIFY ENCR', `dnl')
201205408Srdivackyifdef(`_CERT_REGEX_ISSUER_', `dnl
202226633Sdim# extract relevant part from cert issuer
203226633SdimKCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
204226633Sdimifdef(`_CERT_REGEX_SUBJECT_', `dnl
205226633Sdim# extract relevant part from cert subject
206226633SdimKCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
207226633Sdim
208226633Sdimifdef(`LOCAL_RELAY', `dnl
209226633Sdim# who I send unqualified names to if `FEATURE(stickyhost)' is used
210226633Sdim# (null means deliver locally)
211226633SdimDR`'LOCAL_RELAY')
212226633Sdim
213226633Sdimifdef(`MAIL_HUB', `dnl
214226633Sdim# who gets all local email traffic
215226633Sdim# ($R has precedence for unqualified names if `FEATURE(stickyhost)' is used)
216226633SdimDH`'MAIL_HUB')
217226633Sdim
218226633Sdim# dequoting map
219226633SdimKdequote dequote`'ifdef(`confDEQUOTE_OPTS', ` confDEQUOTE_OPTS', `')
220226633Sdim
221226633Sdimdivert(0)dnl	# end of nullclient diversion
222226633Sdim# class E: names that should be exposed as from this host, even if we masquerade
223226633Sdim# class L: names that should be delivered locally, even if we have a relay
224226633Sdim# class M: domains that should be converted to $M
225226633Sdim# class N: domains that should not be converted to $M
226226633Sdim#CL root
227226633Sdimundivert(5)dnl
228226633Sdimifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl')
229226633Sdim
230226633Sdimifdef(`MASQUERADE_NAME', `dnl
231205408Srdivacky# who I masquerade as (null for no masquerading) (see also $=M)
232205408SrdivackyDM`'MASQUERADE_NAME')
233226633Sdim
234226633Sdim# my name for error messages
235226633Sdimifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
236226633Sdim
237226633Sdimundivert(6)dnl LOCAL_CONFIG
238226633Sdiminclude(_CF_DIR_`m4/version.m4')
239226633Sdim
240226633Sdim###############
241226633Sdim#   Options   #
242226633Sdim###############
243226633Sdimifdef(`confAUTO_REBUILD',
244205408Srdivacky`errprint(WARNING: `confAUTO_REBUILD' is no longer valid.
245205408Srdivacky	There was a potential for a denial of service attack if this is set.
246234353Sdim)')dnl
247234353Sdim
248226633Sdim# strip message body to 7 bits on input?
249226633Sdim_OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
250234353Sdim
251249423Sdim# 8-bit data handling
252234353Sdim_OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `pass8')
253234353Sdim
254234353Sdim# wait for alias file rebuild (default units: minutes)
255249423Sdim_OPTION(AliasWait, `confALIAS_WAIT', `5m')
256234353Sdim
257234353Sdim# location of alias file
258234353Sdim_OPTION(AliasFile, `ALIAS_FILE', `MAIL_SETTINGS_DIR`'aliases')
259234353Sdim
260226633Sdim# minimum number of free blocks on filesystem
261226633Sdim_OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', `100')
262226633Sdim
263226633Sdim# maximum message size
264226633Sdim_OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', `0')
265234353Sdim
266226633Sdim# substitution for space (blank) characters
267226633Sdim_OPTION(BlankSub, `confBLANK_SUB', `_')
268234353Sdim
269234353Sdim# avoid connecting to "expensive" mailers on initial submission?
270249423Sdim_OPTION(HoldExpensive, `confCON_EXPENSIVE', `False')
271249423Sdim
272249423Sdim# checkpoint queue runs after every N successful deliveries
273249423Sdim_OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10')
274249423Sdim
275249423Sdim# default delivery mode
276249423Sdim_OPTION(DeliveryMode, `confDELIVERY_MODE', `background')
277249423Sdim
278249423Sdim# error message header/file
279234353Sdim_OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header')
280234353Sdim
281234353Sdim# error mode
282234353Sdim_OPTION(ErrorMode, `confERROR_MODE', `print')
283234353Sdim
284234353Sdim# save Unix-style "From_" lines at top of header?
285234353Sdim_OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False')
286234353Sdim
287234353Sdim# queue file mode (qf files)
288234353Sdim_OPTION(QueueFileMode, `confQUEUE_FILE_MODE', `0600')
289226633Sdim
290226633Sdim# temporary file mode
291226633Sdim_OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600')
292234353Sdim
293234353Sdim# match recipients against GECOS field?
294234353Sdim_OPTION(MatchGECOS, `confMATCH_GECOS', `False')
295226633Sdim
296226633Sdim# maximum hop count
297234353Sdim_OPTION(MaxHopCount, `confMAX_HOP', `25')
298234353Sdim
299234353Sdim# location of help file
300234353SdimO HelpFile=ifdef(`HELP_FILE', HELP_FILE, `MAIL_SETTINGS_DIR`'helpfile')
301234353Sdim
302234353Sdim# ignore dots as terminators in incoming messages?
303234353Sdim_OPTION(IgnoreDots, `confIGNORE_DOTS', `False')
304234353Sdim
305234353Sdim# name resolver options
306205408Srdivacky_OPTION(ResolverOptions, `confBIND_OPTS', `+AAONLY')
307205408Srdivacky
308205408Srdivacky# deliver MIME-encapsulated error messages?
309226633Sdim_OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS', `True')
310205408Srdivacky
311205408Srdivacky# Forward file search path
312205408Srdivacky_OPTION(ForwardPath, `confFORWARD_PATH', `/var/forward/$u:$z/.forward.$w:$z/.forward')
313205408Srdivacky
314205408Srdivacky# open connection cache size
315226633Sdim_OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', `2')
316226633Sdim
317226633Sdim# open connection cache timeout
318226633Sdim_OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', `5m')
319226633Sdim
320205408Srdivacky# persistent host status directory
321205408Srdivacky_OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', `.hoststat')
322226633Sdim
323249423Sdim# single thread deliveries (requires HostStatusDirectory)?
324249423Sdim_OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY', `False')
325205408Srdivacky
326205408Srdivacky# use Errors-To: header?
327226633Sdim_OPTION(UseErrorsTo, `confUSE_ERRORS_TO', `False')
328226633Sdim
329243830Sdim# log level
330243830Sdim_OPTION(LogLevel, `confLOG_LEVEL', `10')
331243830Sdim
332226633Sdim# send to me too, even in an alias expansion?
333243830Sdim_OPTION(MeToo, `confME_TOO', `True')
334226633Sdim
335243830Sdim# verify RHS in newaliases?
336243830Sdim_OPTION(CheckAliases, `confCHECK_ALIASES', `False')
337243830Sdim
338243830Sdim# default messages to old style headers if no special punctuation?
339243830Sdim_OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS', `False')
340226633Sdim
341243830Sdim# SMTP daemon options
342226633Sdimifelse(defn(`confDAEMON_OPTIONS'), `', `dnl',
343226633Sdim`errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.
344226633Sdim	Use `DAEMON_OPTIONS()'; see cf/README.
345226633Sdim)'dnl
346226633Sdim`DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
347226633Sdimifelse(defn(`_DPO_'), `',
348226633Sdim`ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-v4, Family=inet
349226633SdimO DaemonPortOptions=Name=MTA-v6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_')
350226633Sdimifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
351226633Sdim
352226633Sdim# SMTP client options
353226633Sdimifelse(defn(`confCLIENT_OPTIONS'), `', `dnl',
354226633Sdim`errprint(WARNING: `confCLIENT_OPTIONS' is no longer valid.  See cf/README for more information.
355226633Sdim)'dnl
356226633Sdim`CLIENT_OPTIONS(`confCLIENT_OPTIONS')')
357226633Sdimifelse(defn(`_CPO_'), `',
358226633Sdim`#O ClientPortOptions=Family=inet, Address=0.0.0.0', `_CPO_')
359226633Sdim
360205408Srdivacky# Modifiers to `define' {daemon_flags} for direct submissions
361249423Sdim_OPTION(DirectSubmissionModifiers, `confDIRECT_SUBMISSION_MODIFIERS', `')
362205408Srdivacky
363205408Srdivacky# Use as mail submission program? See sendmail/SECURITY
364205408Srdivacky_OPTION(UseMSP, `confUSE_MSP', `')
365249423Sdim
366249423Sdim# privacy flags
367205408Srdivacky_OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings')
368205408Srdivacky
369249423Sdim# who (if anyone) should get extra copies of error messages
370249423Sdim_OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster')
371249423Sdim
372234353Sdim# slope of queue-only function
373234353Sdim_OPTION(QueueFactor, `confQUEUE_FACTOR', `600000')
374223017Sdim
375223017Sdim# limit on number of concurrent queue runners
376226633Sdim_OPTION(MaxQueueChildren, `confMAX_QUEUE_CHILDREN', `')
377226633Sdim
378226633Sdim# maximum number of queue-runners per queue-grouping with multiple queues
379226633Sdim_OPTION(MaxRunnersPerQueue, `confMAX_RUNNERS_PER_QUEUE', `1')
380226633Sdim
381226633Sdim# priority of queue runners (nice(3))
382205408Srdivacky_OPTION(NiceQueueRun, `confNICE_QUEUE_RUN', `')
383205408Srdivacky
384249423Sdim# shall we sort the queue by hostname first?
385249423Sdim_OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority')
386249423Sdim
387249423Sdim# minimum time in queue before retry
388249423Sdim_OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m')
389249423Sdim
390249423Sdim# how many jobs can you process in the queue?
391249423Sdim_OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `0')
392249423Sdim
393249423Sdim# perform initial split of envelope without checking MX records
394249423Sdim_OPTION(FastSplit, `confFAST_SPLIT', `1')
395249423Sdim
396249423Sdim# queue directory
397249423SdimO QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue')
398249423Sdim
399249423Sdim# key for shared memory; 0 to turn off, -1 to auto-select
400249423Sdim_OPTION(SharedMemoryKey, `confSHARED_MEMORY_KEY', `0')
401263508Sdim
402263508Sdim# file to store auto-selected key for shared memory (SharedMemoryKey = -1)
403249423Sdim_OPTION(SharedMemoryKeyFile, `confSHARED_MEMORY_KEY_FILE', `')
404249423Sdim
405249423Sdim# timeouts (many of these)
406249423Sdim_OPTION(Timeout.initial, `confTO_INITIAL', `5m')
407249423Sdim_OPTION(Timeout.connect, `confTO_CONNECT', `5m')
408249423Sdim_OPTION(Timeout.aconnect, `confTO_ACONNECT', `0s')
409249423Sdim_OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m')
410251662Sdim_OPTION(Timeout.helo, `confTO_HELO', `5m')
411251662Sdim_OPTION(Timeout.mail, `confTO_MAIL', `10m')
412249423Sdim_OPTION(Timeout.rcpt, `confTO_RCPT', `1h')
413249423Sdim_OPTION(Timeout.datainit, `confTO_DATAINIT', `5m')
414249423Sdim_OPTION(Timeout.datablock, `confTO_DATABLOCK', `1h')
415218893Sdim_OPTION(Timeout.datafinal, `confTO_DATAFINAL', `1h')
416249423Sdim_OPTION(Timeout.rset, `confTO_RSET', `5m')
417249423Sdim_OPTION(Timeout.quit, `confTO_QUIT', `2m')
418205408Srdivacky_OPTION(Timeout.misc, `confTO_MISC', `2m')
419205408Srdivacky_OPTION(Timeout.command, `confTO_COMMAND', `1h')
420226633Sdim_OPTION(Timeout.ident, `confTO_IDENT', `5s')
421249423Sdim_OPTION(Timeout.fileopen, `confTO_FILEOPEN', `60s')
422249423Sdim_OPTION(Timeout.control, `confTO_CONTROL', `2m')
423205408Srdivacky_OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', `5d')
424205408Srdivacky_OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', `5d')
425218893Sdim_OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', `2d')
426249423Sdim_OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', `7d')
427249423Sdim_OPTION(Timeout.queuereturn.dsn, `confTO_QUEUERETURN_DSN', `5d')
428249423Sdim_OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', `4h')
429249423Sdim_OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', `4h')
430205408Srdivacky_OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', `1h')
431205408Srdivacky_OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', `12h')
432221345Sdim_OPTION(Timeout.queuewarn.dsn, `confTO_QUEUEWARN_DSN', `4h')
433221345Sdim_OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', `30m')
434221345Sdim_OPTION(Timeout.resolver.retrans, `confTO_RESOLVER_RETRANS', `5s')
435226633Sdim_OPTION(Timeout.resolver.retrans.first, `confTO_RESOLVER_RETRANS_FIRST', `5s')
436221345Sdim_OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s')
437243830Sdim_OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4')
438221345Sdim_OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4')
439226633Sdim_OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4')
440243830Sdim_OPTION(Timeout.lhlo, `confTO_LHLO', `2m')
441243830Sdim_OPTION(Timeout.auth, `confTO_AUTH', `10m')
442218893Sdim_OPTION(Timeout.starttls, `confTO_STARTTLS', `1h')
443218893Sdim
444218893Sdim# time for DeliverBy; extension disabled if less than 0
445218893Sdim_OPTION(DeliverByMin, `confDELIVER_BY_MIN', `0')
446218893Sdim
447218893Sdim# should we not prune routes in route-addr syntax addresses?
448218893Sdim_OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False')
449218893Sdim
450218893Sdim# queue up everything before forking?
451218893Sdim_OPTION(SuperSafe, `confSAFE_QUEUE', `True')
452218893Sdim
453218893Sdim# status file
454218893Sdim_OPTION(StatusFile, `STATUS_FILE')
455218893Sdim
456218893Sdim# time zone handling:
457218893Sdim#  if undefined, use system default
458218893Sdim#  if defined but null, use TZ envariable passed in
459218893Sdim#  if defined and non-null, use that info
460218893Sdimifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
461218893Sdim	confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
462218893Sdim	`O TimeZoneSpec=confTIME_ZONE')
463218893Sdim
464243830Sdim# default UID (can be username or userid:groupid)
465243830Sdim_OPTION(DefaultUser, `confDEF_USER_ID', `mailnull')
466243830Sdim
467243830Sdim# list of locations of user database file (null means no lookup)
468243830Sdim_OPTION(UserDatabaseSpec, `confUSERDB_SPEC', `MAIL_SETTINGS_DIR`'userdb')
469243830Sdim
470243830Sdim# fallback MX host
471243830Sdim_OPTION(FallbackMXhost, `confFALLBACK_MX', `fall.back.host.net')
472243830Sdim
473243830Sdim# fallback smart host
474218893Sdim_OPTION(FallbackSmartHost, `confFALLBACK_SMARTHOST', `fall.back.host.net')
475243830Sdim
476243830Sdim# if we are the best MX host for a site, try it directly instead of config err
477218893Sdim_OPTION(TryNullMXList, `confTRY_NULL_MX_LIST', `False')
478226633Sdim
479218893Sdim# load average at which we just queue messages
480226633Sdim_OPTION(QueueLA, `confQUEUE_LA', `8')
481226633Sdim
482226633Sdim# load average at which we refuse connections
483226633Sdim_OPTION(RefuseLA, `confREFUSE_LA', `12')
484226633Sdim
485226633Sdim# log interval when refusing connections for this long
486226633Sdim_OPTION(RejectLogInterval, `confREJECT_LOG_INTERVAL', `3h')
487
488# load average at which we delay connections; 0 means no limit
489_OPTION(DelayLA, `confDELAY_LA', `0')
490
491# maximum number of children we allow at one time
492_OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `0')
493
494# maximum number of new connections per second
495_OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0')
496
497# Width of the window 
498_OPTION(ConnectionRateWindowSize, `confCONNECTION_RATE_WINDOW_SIZE', `60s')
499
500# work recipient factor
501_OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
502
503# deliver each queued job in a separate process?
504_OPTION(ForkEachJob, `confSEPARATE_PROC', `False')
505
506# work class factor
507_OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800')
508
509# work time factor
510_OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000')
511
512# default character set
513_OPTION(DefaultCharSet, `confDEF_CHAR_SET', `unknown-8bit')
514
515# service switch file (name hardwired on Solaris, Ultrix, OSF/1, others)
516_OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch')
517
518# hosts file (normally /etc/hosts)
519_OPTION(HostsFile, `confHOSTS_FILE', `/etc/hosts')
520
521# dialup line delay on connection failure
522_OPTION(DialDelay, `confDIAL_DELAY', `0s')
523
524# action to take if there are no recipients in the message
525_OPTION(NoRecipientAction, `confNO_RCPT_ACTION', `none')
526
527# chrooted environment for writing to files
528_OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `')
529
530# are colons OK in addresses?
531_OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True')
532
533# shall I avoid expanding CNAMEs (violates protocols)?
534_OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False')
535
536# SMTP initial login message (old $e macro)
537_OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG', `$j Sendmail $v ready at $b')
538
539# UNIX initial From header format (old $l macro)
540_OPTION(UnixFromLine, `confFROM_LINE', `From $g $d')
541
542# From: lines that have embedded newlines are unwrapped onto one line
543_OPTION(SingleLineFromHeader, `confSINGLE_LINE_FROM_HEADER', `False')
544
545# Allow HELO SMTP command that does not `include' a host name
546_OPTION(AllowBogusHELO, `confALLOW_BOGUS_HELO', `False')
547
548# Characters to be quoted in a full name phrase (@,;:\()[] are automatic)
549_OPTION(MustQuoteChars, `confMUST_QUOTE_CHARS', `.')
550
551# delimiter (operator) characters (old $o macro)
552_OPTION(OperatorChars, `confOPERATORS', `.:@[]')
553
554# shall I avoid calling initgroups(3) because of high NIS costs?
555_OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False')
556
557# are group-writable `:include:' and .forward files (un)trustworthy?
558# True (the default) means they are not trustworthy.
559_OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True')
560ifdef(`confUNSAFE_GROUP_WRITES',
561`errprint(`WARNING: confUNSAFE_GROUP_WRITES is deprecated; use confDONT_BLAME_SENDMAIL.
562')')
563
564# where do errors that occur when sending errors get sent?
565_OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster')
566
567# issue temporary errors (4xy) instead of permanent errors (5xy)?
568_OPTION(SoftBounce, `confSOFT_BOUNCE', `False')
569
570# where to save bounces if all else fails
571_OPTION(DeadLetterDrop, `confDEAD_LETTER_DROP', `/var/tmp/dead.letter')
572
573# what user id do we assume for the majority of the processing?
574_OPTION(RunAsUser, `confRUN_AS_USER', `sendmail')
575
576# maximum number of recipients per SMTP envelope
577_OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `0')
578
579# limit the rate recipients per SMTP envelope are accepted
580# once the threshold number of recipients have been rejected
581_OPTION(BadRcptThrottle, `confBAD_RCPT_THROTTLE', `0')
582
583
584# shall we get local names from our installed interfaces?
585_OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False')
586
587# Return-Receipt-To: header implies DSN request
588_OPTION(RrtImpliesDsn, `confRRT_IMPLIES_DSN', `False')
589
590# override connection address (for testing)
591_OPTION(ConnectOnlyTo, `confCONNECT_ONLY_TO', `0.0.0.0')
592
593# Trusted user for file ownership and starting the daemon
594_OPTION(TrustedUser, `confTRUSTED_USER', `root')
595
596# Control socket for daemon management
597_OPTION(ControlSocketName, `confCONTROL_SOCKET_NAME', `/var/spool/mqueue/.control')
598
599# Maximum MIME header length to protect MUAs
600_OPTION(MaxMimeHeaderLength, `confMAX_MIME_HEADER_LENGTH', `0/0')
601
602# Maximum length of the sum of all headers
603_OPTION(MaxHeadersLength, `confMAX_HEADERS_LENGTH', `32768')
604
605# Maximum depth of alias recursion
606_OPTION(MaxAliasRecursion, `confMAX_ALIAS_RECURSION', `10')
607
608# location of pid file
609_OPTION(PidFile, `confPID_FILE', `/var/run/sendmail.pid')
610
611# Prefix string for the process title shown on 'ps' listings
612_OPTION(ProcessTitlePrefix, `confPROCESS_TITLE_PREFIX', `prefix')
613
614# Data file (df) memory-buffer file maximum size
615_OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096')
616
617# Transcript file (xf) memory-buffer file maximum size
618_OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096')
619
620# lookup type to find information about local mailboxes
621_OPTION(MailboxDatabase, `confMAILBOX_DATABASE', `pw')
622
623# override compile time flag REQUIRES_DIR_FSYNC
624_OPTION(RequiresDirfsync, `confREQUIRES_DIR_FSYNC', `true')
625
626# list of authentication mechanisms
627_OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5')
628
629# Authentication realm
630_OPTION(AuthRealm, `confAUTH_REALM', `')
631
632# default authentication information for outgoing connections
633_OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info')
634
635# SMTP AUTH flags
636_OPTION(AuthOptions, `confAUTH_OPTIONS', `')
637
638# SMTP AUTH maximum encryption strength
639_OPTION(AuthMaxBits, `confAUTH_MAX_BITS', `')
640
641# SMTP STARTTLS server options
642_OPTION(TLSSrvOptions, `confTLS_SRV_OPTIONS', `')
643
644
645# Input mail filters
646_OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
647
648ifelse(len(X`'_MAIL_FILTERS_DEF), `1', `dnl', `dnl
649# Milter options
650_OPTION(Milter.LogLevel, `confMILTER_LOG_LEVEL', `')
651_OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `')
652_OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `')
653_OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
654_OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')
655_OPTION(Milter.macros.eom, `confMILTER_MACROS_EOM', `')
656_OPTION(Milter.macros.eoh, `confMILTER_MACROS_EOH', `')
657_OPTION(Milter.macros.data, `confMILTER_MACROS_DATA', `')')
658
659# CA directory
660_OPTION(CACertPath, `confCACERT_PATH', `')
661# CA file
662_OPTION(CACertFile, `confCACERT', `')
663# Server Cert
664_OPTION(ServerCertFile, `confSERVER_CERT', `')
665# Server private key
666_OPTION(ServerKeyFile, `confSERVER_KEY', `')
667# Client Cert
668_OPTION(ClientCertFile, `confCLIENT_CERT', `')
669# Client private key
670_OPTION(ClientKeyFile, `confCLIENT_KEY', `')
671# File containing certificate revocation lists 
672_OPTION(CRLFile, `confCRL', `')
673# DHParameters (only required if DSA/DH is used)
674_OPTION(DHParameters, `confDH_PARAMETERS', `')
675# Random data source (required for systems without /dev/urandom under OpenSSL)
676_OPTION(RandFile, `confRAND_FILE', `')
677
678# Maximum number of "useless" commands before slowing down
679_OPTION(MaxNOOPCommands, `confMAX_NOOP_COMMANDS', `20')
680
681# Name to use for EHLO (defaults to $j)
682_OPTION(HeloName, `confHELO_NAME')
683
684############################
685`# QUEUE GROUP DEFINITIONS  #'
686############################
687_QUEUE_GROUP_
688
689###########################
690#   Message precedences   #
691###########################
692
693Pfirst-class=0
694Pspecial-delivery=100
695Plist=-30
696Pbulk=-60
697Pjunk=-100
698
699#####################
700#   Trusted users   #
701#####################
702
703# this is equivalent to setting class "t"
704ifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `MAIL_SETTINGS_DIR`'trusted-users')
705Troot
706Tdaemon
707ifdef(`_NO_UUCP_', `dnl', `Tuucp')
708ifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
709
710#########################
711#   Format of headers   #
712#########################
713
714ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
715ifdef(`confMESSAGEID_HEADER',, `define(`confMESSAGEID_HEADER', `<$t.$i@$j>')')dnl
716H?P?Return-Path: <$g>
717HReceived: confRECEIVED_HEADER
718H?D?Resent-Date: $a
719H?D?Date: $a
720H?F?Resent-From: confFROM_HEADER
721H?F?From: confFROM_HEADER
722H?x?Full-Name: $x
723# HPosted-Date: $a
724# H?l?Received-Date: $b
725H?M?Resent-Message-Id: confMESSAGEID_HEADER
726H?M?Message-Id: confMESSAGEID_HEADER
727
728#
729######################################################################
730######################################################################
731#####
732#####			REWRITING RULES
733#####
734######################################################################
735######################################################################
736
737############################################
738###  Ruleset 3 -- Name Canonicalization  ###
739############################################
740Scanonify=3
741
742# handle null input (translate to <@> special case)
743R$@			$@ <@>
744
745# strip group: syntax (not inside angle brackets!) and trailing semicolon
746R$*			$: $1 <@>			mark addresses
747R$* < $* > $* <@>	$: $1 < $2 > $3			unmark <addr>
748R@ $* <@>		$: @ $1				unmark @host:...
749R$* [ IPv6 : $+ ] <@>	$: $1 [ IPv6 : $2 ]		unmark IPv6 addr
750R$* :: $* <@>		$: $1 :: $2			unmark node::addr
751R:`include': $* <@>	$: :`include': $1			unmark :`include':...
752R$* : $* [ $* ]		$: $1 : $2 [ $3 ] <@>		remark if leading colon
753R$* : $* <@>		$: $2				strip colon if marked
754R$* <@>			$: $1				unmark
755R$* ;			   $1				strip trailing semi
756R$* < $+ :; > $*	$@ $2 :; <@>			catch <list:;>
757R$* < $* ; >		   $1 < $2 >			bogus bracketed semi
758
759# null input now results from list:; syntax
760R$@			$@ :; <@>
761
762# strip angle brackets -- note RFC733 heuristic to get innermost item
763R$*			$: < $1 >			housekeeping <>
764R$+ < $* >		   < $2 >			strip excess on left
765R< $* > $+		   < $1 >			strip excess on right
766R<>			$@ < @ >			MAIL FROM:<> case
767R< $+ >			$: $1				remove housekeeping <>
768
769ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
770# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
771R@ $+ , $+		@ $1 : $2			change all "," to ":"
772
773# localize and dispose of route-based addresses
774dnl XXX: IPv6 colon conflict
775ifdef(`NO_NETINET6', `dnl',
776`R@ [$+] : $+		$@ $>Canonify2 < @ [$1] > : $2	handle <route-addr>')
777R@ $+ : $+		$@ $>Canonify2 < @$1 > : $2	handle <route-addr>
778dnl',`dnl
779# strip route address <@a,@b,@c:user@d> -> <user@d>
780R@ $+ , $+		$2
781ifdef(`NO_NETINET6', `dnl',
782`R@ [ $* ] : $+		$2')
783R@ $+ : $+		$2
784dnl')
785
786# find focus for list syntax
787R $+ : $* ; @ $+	$@ $>Canonify2 $1 : $2 ; < @ $3 >	list syntax
788R $+ : $* ;		$@ $1 : $2;			list syntax
789
790# find focus for @ syntax addresses
791R$+ @ $+		$: $1 < @ $2 >			focus on domain
792R$+ < $+ @ $+ >		$1 $2 < @ $3 >			move gaze right
793R$+ < @ $+ >		$@ $>Canonify2 $1 < @ $2 >	already canonical
794
795dnl This is flagged as an error in S0; no need to silently fix it here.
796dnl # do some sanity checking
797dnl R$* < @ $~[ $* : $* > $*	$1 < @ $2 $3 > $4	nix colons in addrs
798
799ifdef(`_NO_UUCP_', `dnl',
800`# convert old-style addresses to a domain-based address
801R$- ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	resolve uucp names
802R$+ . $- ! $+		$@ $>Canonify2 $3 < @ $1 . $2 >		domain uucps
803R$+ ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	uucp subdomains
804')
805ifdef(`_USE_DECNET_SYNTAX_',
806`# convert node::user addresses into a domain-based address
807R$- :: $+		$@ $>Canonify2 $2 < @ $1 .DECNET >	resolve DECnet names
808R$- . $- :: $+		$@ $>Canonify2 $3 < @ $1.$2 .DECNET >	numeric DECnet addr
809',
810	`dnl')
811# if we have % signs, take the rightmost one
812R$* % $*		$1 @ $2				First make them all @s.
813R$* @ $* @ $*		$1 % $2 @ $3			Undo all but the last.
814R$* @ $*		$@ $>Canonify2 $1 < @ $2 >	Insert < > and finish
815
816# else we must be a local name
817R$*			$@ $>Canonify2 $1
818
819
820################################################
821###  Ruleset 96 -- bottom half of ruleset 3  ###
822################################################
823
824SCanonify2=96
825
826# handle special cases for local names
827R$* < @ localhost > $*		$: $1 < @ $j . > $2		no domain at all
828R$* < @ localhost . $m > $*	$: $1 < @ $j . > $2		local domain
829ifdef(`_NO_UUCP_', `dnl',
830`R$* < @ localhost . UUCP > $*	$: $1 < @ $j . > $2		.UUCP domain')
831
832# check for IPv4/IPv6 domain literal
833R$* < @ [ $+ ] > $*		$: $1 < @@ [ $2 ] > $3		mark [addr]
834R$* < @@ $=w > $*		$: $1 < @ $j . > $3		self-literal
835R$* < @@ $+ > $*		$@ $1 < @ $2 > $3		canon IP addr
836
837ifdef(`_DOMAIN_TABLE_', `dnl
838# look up domains in the domain table
839R$* < @ $+ > $* 		$: $1 < @ $(domaintable $2 $) > $3', `dnl')
840
841undivert(2)dnl LOCAL_RULE_3
842
843ifdef(`_BITDOMAIN_TABLE_', `dnl
844# handle BITNET mapping
845R$* < @ $+ .BITNET > $*		$: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3', `dnl')
846
847ifdef(`_UUDOMAIN_TABLE_', `dnl
848# handle UUCP mapping
849R$* < @ $+ .UUCP > $*		$: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3', `dnl')
850
851ifdef(`_NO_UUCP_', `dnl',
852`ifdef(`UUCP_RELAY',
853`# pass UUCP addresses straight through
854R$* < @ $+ . UUCP > $*		$@ $1 < @ $2 . UUCP . > $3',
855`# if really UUCP, handle it immediately
856ifdef(`_CLASS_U_',
857`R$* < @ $=U . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
858ifdef(`_CLASS_V_',
859`R$* < @ $=V . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
860ifdef(`_CLASS_W_',
861`R$* < @ $=W . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
862ifdef(`_CLASS_X_',
863`R$* < @ $=X . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
864ifdef(`_CLASS_Y_',
865`R$* < @ $=Y . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
866
867ifdef(`_NO_CANONIFY_', `dnl', `dnl
868# try UUCP traffic as a local address
869R$* < @ $+ . UUCP > $*		$: $1 < @ $[ $2 $] . UUCP . > $3
870R$* < @ $+ . . UUCP . > $*	$@ $1 < @ $2 . > $3')
871')')
872# hostnames ending in class P are always canonical
873R$* < @ $* $=P > $*		$: $1 < @ $2 $3 . > $4
874dnl apply the next rule only for hostnames not in class P
875dnl this even works for phrases in class P since . is in class P
876dnl which daemon flags are set?
877R$* < @ $* $~P > $*		$: $&{daemon_flags} $| $1 < @ $2 $3 > $4
878dnl the other rules in this section only apply if the hostname
879dnl does not end in class P hence no further checks are done here
880dnl if this ever changes make sure the lookups are "protected" again!
881ifdef(`_NO_CANONIFY_', `dnl
882dnl do not canonify unless:
883dnl domain ends in class {Canonify} (this does not work if the intersection
884dnl	with class P is non-empty)
885dnl or {daemon_flags} has c set
886# pass to name server to make hostname canonical if in class {Canonify}
887R$* $| $* < @ $* $={Canonify} > $*	$: $2 < @ $[ $3 $4 $] > $5
888# pass to name server to make hostname canonical if requested
889R$* c $* $| $* < @ $* > $*	$: $3 < @ $[ $4 $] > $5
890dnl trailing dot? -> do not apply _CANONIFY_HOSTS_
891R$* $| $* < @ $+ . > $*		$: $2 < @ $3 . > $4
892# add a trailing dot to qualified hostnames so other rules will work
893R$* $| $* < @ $+.$+ > $*	$: $2 < @ $3.$4 . > $5
894ifdef(`_CANONIFY_HOSTS_', `dnl
895dnl this should only apply to unqualified hostnames
896dnl but if a valid character inside an unqualified hostname is an OperatorChar
897dnl then $- does not work.
898# lookup unqualified hostnames
899R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4', `dnl')', `dnl
900dnl _NO_CANONIFY_ is not set: canonify unless:
901dnl {daemon_flags} contains CC (do not canonify)
902dnl but add a trailing dot to qualified hostnames so other rules will work
903dnl should we do this for every hostname: even unqualified?
904R$* CC $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
905R$* CC $* $| $*			$: $3
906ifdef(`_FFR_NOCANONIFY_HEADERS', `dnl
907# do not canonify header addresses
908R$* $| $* < @ $* $~P > $*	$: $&{addr_type} $| $2 < @ $3 $4 > $5
909R$* h $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
910R$* h $* $| $*			$: $3', `dnl')
911# pass to name server to make hostname canonical
912R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4')
913dnl remove {daemon_flags} for other cases
914R$* $| $*			$: $2
915
916# local host aliases and pseudo-domains are always canonical
917R$* < @ $=w > $*		$: $1 < @ $2 . > $3
918ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
919`R$* < @ $* $=M > $*		$: $1 < @ $2 $3 . > $4',
920`R$* < @ $=M > $*		$: $1 < @ $2 . > $3')
921ifdef(`_VIRTUSER_TABLE_', `dnl
922dnl virtual hosts are also canonical
923ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
924`R$* < @ $* $={VirtHost} > $* 	$: $1 < @ $2 $3 . > $4',
925`R$* < @ $={VirtHost} > $* 	$: $1 < @ $2 . > $3')',
926`dnl')
927ifdef(`_GENERICS_TABLE_', `dnl
928dnl hosts for genericstable are also canonical
929ifdef(`_GENERICS_ENTIRE_DOMAIN_',
930`R$* < @ $* $=G > $* 	$: $1 < @ $2 $3 . > $4',
931`R$* < @ $=G > $* 	$: $1 < @ $2 . > $3')',
932`dnl')
933dnl remove superfluous dots (maybe repeatedly) which may have been added
934dnl by one of the rules before
935R$* < @ $* . . > $*		$1 < @ $2 . > $3
936
937
938##################################################
939###  Ruleset 4 -- Final Output Post-rewriting  ###
940##################################################
941Sfinal=4
942
943R$+ :; <@>		$@ $1 :				handle <list:;>
944R$* <@>			$@				handle <> and list:;
945
946# strip trailing dot off possibly canonical name
947R$* < @ $+ . > $*	$1 < @ $2 > $3
948
949# eliminate internal code
950R$* < @ *LOCAL* > $*	$1 < @ $j > $2
951
952# externalize local domain info
953R$* < $+ > $*		$1 $2 $3			defocus
954R@ $+ : @ $+ : $+	@ $1 , @ $2 : $3		<route-addr> canonical
955R@ $*			$@ @ $1				... and exit
956
957ifdef(`_NO_UUCP_', `dnl',
958`# UUCP must always be presented in old form
959R$+ @ $- . UUCP		$2!$1				u@h.UUCP => h!u')
960
961ifdef(`_USE_DECNET_SYNTAX_',
962`# put DECnet back in :: form
963R$+ @ $+ . DECNET	$2 :: $1			u@h.DECNET => h::u',
964	`dnl')
965# delete duplicate local names
966R$+ % $=w @ $=w		$1 @ $2				u%host@host => u@host
967
968
969
970##############################################################
971###   Ruleset 97 -- recanonicalize and call ruleset zero   ###
972###		   (used for recursive calls)		   ###
973##############################################################
974
975SRecurse=97
976R$*			$: $>canonify $1
977R$*			$@ $>parse $1
978
979
980######################################
981###   Ruleset 0 -- Parse Address   ###
982######################################
983
984Sparse=0
985
986R$*			$: $>Parse0 $1		initial parsing
987R<@>			$#_LOCAL_ $: <@>		special case error msgs
988R$*			$: $>ParseLocal $1	handle local hacks
989R$*			$: $>Parse1 $1		final parsing
990
991#
992#  Parse0 -- do initial syntax checking and eliminate local addresses.
993#	This should either return with the (possibly modified) input
994#	or return with a #error mailer.  It should not return with a
995#	#mailer other than the #error mailer.
996#
997
998SParse0
999R<@>			$@ <@>			special case error msgs
1000R$* : $* ; <@>		$#error $@ 5.1.3 $: "_CODE553 List:; syntax illegal for recipient addresses"
1001R@ <@ $* >		< @ $1 >		catch "@@host" bogosity
1002R<@ $+>			$#error $@ 5.1.3 $: "_CODE553 User address required"
1003R$+ <@>			$#error $@ 5.1.3 $: "_CODE553 Hostname required"
1004R$*			$: <> $1
1005dnl allow tricks like [host1]:[host2]
1006R<> $* < @ [ $* ] : $+ > $*	$1 < @ [ $2 ] : $3 > $4
1007R<> $* < @ [ $* ] , $+ > $*	$1 < @ [ $2 ] , $3 > $4
1008dnl but no a@[b]c
1009R<> $* < @ [ $* ] $+ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid address"
1010R<> $* < @ [ $+ ] > $*		$1 < @ [ $2 ] > $3
1011R<> $* <$* : $* > $*	$#error $@ 5.1.3 $: "_CODE553 Colon illegal in host name part"
1012R<> $*			$1
1013R$* < @ . $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
1014R$* < @ $* .. $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
1015dnl no a@b@
1016R$* < @ $* @ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid route address"
1017dnl no a@b@c
1018R$* @ $* < @ $* > $*	$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
1019dnl comma only allowed before @; this check is not complete
1020R$* , $~O $*		$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
1021
1022ifdef(`_STRICT_RFC821_', `# more RFC 821 checks
1023R$* . < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not end with a dot"
1024R. $* < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not begin with a dot"
1025dnl', `dnl')
1026
1027# now delete the local info -- note $=O to find characters that cause forwarding
1028R$* < @ > $*		$@ $>Parse0 $>canonify $1	user@ => user
1029R< @ $=w . > : $*	$@ $>Parse0 $>canonify $2	@here:... -> ...
1030R$- < @ $=w . >		$: $(dequote $1 $) < @ $2 . >	dequote "foo"@here
1031R< @ $+ >		$#error $@ 5.1.3 $: "_CODE553 User address required"
1032R$* $=O $* < @ $=w . >	$@ $>Parse0 $>canonify $1 $2 $3	...@here -> ...
1033R$- 			$: $(dequote $1 $) < @ *LOCAL* >	dequote "foo"
1034R< @ *LOCAL* >		$#error $@ 5.1.3 $: "_CODE553 User address required"
1035R$* $=O $* < @ *LOCAL* >
1036			$@ $>Parse0 $>canonify $1 $2 $3	...@*LOCAL* -> ...
1037R$* < @ *LOCAL* >	$: $1
1038
1039#
1040#  Parse1 -- the bottom half of ruleset 0.
1041#
1042
1043SParse1
1044ifdef(`_LDAP_ROUTING_', `dnl
1045# handle LDAP routing for hosts in $={LDAPRoute}
1046R$+ < @ $={LDAPRoute} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2> <>
1047R$+ < @ $={LDAPRouteEquiv} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $M> <>',
1048`dnl')
1049
1050ifdef(`_MAILER_smtp_',
1051`# handle numeric address spec
1052dnl there is no check whether this is really an IP number
1053R$* < @ [ $+ ] > $*	$: $>ParseLocal $1 < @ [ $2 ] > $3	numeric internet spec
1054R$* < @ [ $+ ] > $*	$: $1 < @ [ $2 ] : $S > $3	Add smart host to path
1055R$* < @ [ $+ ] : > $*		$#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3	no smarthost: send
1056R$* < @ [ $+ ] : $- : $*> $*	$#$3 $@ $4 $: $1 < @ [$2] > $5	smarthost with mailer
1057R$* < @ [ $+ ] : $+ > $*	$#_SMTP_ $@ $3 $: $1 < @ [$2] > $4	smarthost without mailer',
1058	`dnl')
1059
1060ifdef(`_VIRTUSER_TABLE_', `dnl
1061# handle virtual users
1062ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
1063dnl this is not a documented option
1064dnl it stops looping in virtusertable mapping if input and output
1065dnl are identical, i.e., if address A is mapped to A.
1066dnl it does not deal with multi-level recursion
1067# handle full domains in RHS of virtusertable
1068R$+ < @ $+ >			$: $(macro {RecipientAddress} $) $1 < @ $2 >
1069R$+ < @ $+ > 			$: <?> $1 < @ $2 > $| $>final $1 < @ $2 >
1070R<?> $+ $| $+			$: $1 $(macro {RecipientAddress} $@ $2 $)
1071R<?> $+ $| $*			$: $1',
1072`dnl')
1073R$+			$: <!> $1		Mark for lookup
1074dnl input: <!> local<@domain>
1075ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
1076`R<!> $+ < @ $* $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >',
1077`R<!> $+ < @ $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >')
1078dnl input: <result-of-lookup | @> local<@domain> | <!> local<@domain>
1079R<!> $+ < @ $=w . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
1080dnl if <@> local<@domain>: no match but try lookup
1081dnl user+detail: try user++@domain if detail not empty
1082R<@> $+ + $+ < @ $* . >
1083			$: < $(virtuser $1 + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1084dnl user+detail: try user+*@domain
1085R<@> $+ + $* < @ $* . >
1086			$: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1087dnl user+detail: try user@domain
1088R<@> $+ + $* < @ $* . >
1089			$: < $(virtuser $1 @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1090dnl try default entry: @domain
1091dnl ++@domain
1092R<@> $+ + $+ < @ $+ . >	$: < $(virtuser + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1093dnl +*@domain
1094R<@> $+ + $* < @ $+ . >	$: < $(virtuser + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1095dnl @domain if +detail exists
1096dnl if no match, change marker to prevent a second @domain lookup
1097R<@> $+ + $* < @ $+ . >	$: < $(virtuser @ $3 $@ $1 $@ $2 $@ +$2 $: ! $) > $1 + $2 < @ $3 . >
1098dnl without +detail
1099R<@> $+ < @ $+ . >	$: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
1100dnl no match
1101R<@> $+			$: $1
1102dnl remove mark
1103R<!> $+			$: $1
1104R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
1105R< error : $- $+ > $* 	$#error $@ $(dequote $1 $) $: $2
1106ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
1107# check virtuser input address against output address, if same, skip recursion
1108R< $+ > $+ < @ $+ >				$: < $1 > $2 < @ $3 > $| $1
1109# it is the same: stop now
1110R< $+ > $+ < @ $+ > $| $&{RecipientAddress}	$: $>ParseLocal $>Parse0 $>canonify $1
1111R< $+ > $+ < @ $+ > $| $* 			$: < $1 > $2 < @ $3 >
1112dnl', `dnl')
1113dnl this is not a documented option
1114dnl it performs no looping at all for virtusertable
1115ifdef(`_NO_VIRTUSER_RECURSION_',
1116`R< $+ > $+ < @ $+ >	$: $>ParseLocal $>Parse0 $>canonify $1',
1117`R< $+ > $+ < @ $+ >	$: $>Recurse $1')
1118dnl', `dnl')
1119
1120# short circuit local delivery so forwarded email works
1121ifdef(`_MAILER_usenet_', `dnl
1122R$+ . USENET < @ $=w . >	$#usenet $@ usenet $: $1	handle usenet specially', `dnl')
1123
1124
1125ifdef(`_STICKY_LOCAL_DOMAIN_',
1126`R$+ < @ $=w . >		$: < $H > $1 < @ $2 . >		first try hub
1127R< $+ > $+ < $+ >	$>MailerToTriple < $1 > $2 < $3 >	yep ....
1128dnl $H empty (but @$=w.)
1129R< > $+ + $* < $+ >	$#_LOCAL_ $: $1 + $2		plussed name?
1130R< > $+ < $+ >		$#_LOCAL_ $: @ $1			nope, local address',
1131`R$=L < @ $=w . >	$#_LOCAL_ $: @ $1			special local names
1132R$+ < @ $=w . >		$#_LOCAL_ $: $1			regular local name')
1133
1134ifdef(`_MAILER_TABLE_', `dnl
1135# not local -- try mailer table lookup
1136R$* <@ $+ > $*		$: < $2 > $1 < @ $2 > $3	extract host name
1137R< $+ . > $*		$: < $1 > $2			strip trailing dot
1138R< $+ > $*		$: < $(mailertable $1 $) > $2	lookup
1139dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1140R< $~[ : $* > $* 	$>MailerToTriple < $1 : $2 > $3		check -- resolved?
1141R< $+ > $*		$: $>Mailertable <$1> $2		try domain',
1142`dnl')
1143undivert(4)dnl UUCP rules from `MAILER(uucp)'
1144
1145ifdef(`_NO_UUCP_', `dnl',
1146`# resolve remotely connected UUCP links (if any)
1147ifdef(`_CLASS_V_',
1148`R$* < @ $=V . UUCP . > $*		$: $>MailerToTriple < $V > $1 <@$2.UUCP.> $3',
1149	`dnl')
1150ifdef(`_CLASS_W_',
1151`R$* < @ $=W . UUCP . > $*		$: $>MailerToTriple < $W > $1 <@$2.UUCP.> $3',
1152	`dnl')
1153ifdef(`_CLASS_X_',
1154`R$* < @ $=X . UUCP . > $*		$: $>MailerToTriple < $X > $1 <@$2.UUCP.> $3',
1155	`dnl')')
1156
1157# resolve fake top level domains by forwarding to other hosts
1158ifdef(`BITNET_RELAY',
1159`R$*<@$+.BITNET.>$*	$: $>MailerToTriple < $B > $1 <@$2.BITNET.> $3	user@host.BITNET',
1160	`dnl')
1161ifdef(`DECNET_RELAY',
1162`R$*<@$+.DECNET.>$*	$: $>MailerToTriple < $C > $1 <@$2.DECNET.> $3	user@host.DECNET',
1163	`dnl')
1164ifdef(`_MAILER_pop_',
1165`R$+ < @ POP. >		$#pop $: $1			user@POP',
1166	`dnl')
1167ifdef(`_MAILER_fax_',
1168`R$+ < @ $+ .FAX. >	$#fax $@ $2 $: $1		user@host.FAX',
1169`ifdef(`FAX_RELAY',
1170`R$*<@$+.FAX.>$*		$: $>MailerToTriple < $F > $1 <@$2.FAX.> $3	user@host.FAX',
1171	`dnl')')
1172
1173ifdef(`UUCP_RELAY',
1174`# forward non-local UUCP traffic to our UUCP relay
1175R$*<@$*.UUCP.>$*		$: $>MailerToTriple < $Y > $1 <@$2.UUCP.> $3	uucp mail',
1176`ifdef(`_MAILER_uucp_',
1177`# forward other UUCP traffic straight to UUCP
1178R$* < @ $+ .UUCP. > $*		$#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3	user@host.UUCP',
1179	`dnl')')
1180ifdef(`_MAILER_usenet_', `
1181# addresses sent to net.group.USENET will get forwarded to a newsgroup
1182R$+ . USENET		$#usenet $@ usenet $: $1',
1183	`dnl')
1184
1185ifdef(`_LOCAL_RULES_',
1186`# figure out what should stay in our local mail system
1187undivert(1)', `dnl')
1188
1189# pass names that still have a host to a smarthost (if defined)
1190R$* < @ $* > $*		$: $>MailerToTriple < $S > $1 < @ $2 > $3	glue on smarthost name
1191
1192# deal with other remote names
1193ifdef(`_MAILER_smtp_',
1194`R$* < @$* > $*		$#_SMTP_ $@ $2 $: $1 < @ $2 > $3	user@host.domain',
1195`R$* < @$* > $*		$#error $@ 5.1.2 $: "_CODE553 Unrecognized host name " $2')
1196
1197# handle locally delivered names
1198R$=L			$#_LOCAL_ $: @ $1		special local names
1199R$+			$#_LOCAL_ $: $1			regular local names
1200
1201###########################################################################
1202###   Ruleset 5 -- special rewriting after aliases have been expanded   ###
1203###########################################################################
1204
1205SLocal_localaddr
1206Slocaladdr=5
1207R$+			$: $1 $| $>"Local_localaddr" $1
1208R$+ $| $#ok		$@ $1			no change
1209R$+ $| $#$*		$#$2
1210R$+ $| $*		$: $1
1211
1212ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1213# Preserve rcpt_host in {Host}
1214R$+			$: $1 $| $&h $| $&{Host}	check h and {Host}
1215R$+ $| $|		$: $(macro {Host} $@ $) $1	no h or {Host}
1216R$+ $| $| $+		$: $1			h not set, {Host} set
1217R$+ $| +$* $| $*	$: $1			h is +detail, {Host} set
1218R$+ $| $* @ $+ $| $*	$: $(macro {Host} $@ @$3 $) $1	set {Host} to host in h
1219R$+ $| $+ $| $*		$: $(macro {Host} $@ @$2 $) $1	set {Host} to h
1220')dnl
1221
1222ifdef(`_FFR_5_', `dnl
1223# Preserve host in a macro
1224R$+			$: $(macro {LocalAddrHost} $) $1
1225R$+ @ $+		$: $(macro {LocalAddrHost} $@ @ $2 $) $1')
1226
1227ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `dnl
1228# deal with plussed users so aliases work nicely
1229R$+ + *			$#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1230R$+ + $*		$#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1231')
1232# prepend an empty "forward host" on the front
1233R$+			$: <> $1
1234
1235ifdef(`LUSER_RELAY', `dnl
1236# send unrecognized local users to a relay host
1237ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
1238R< > $+ + $*		$: < ? $L > <+ $2> $(user $1 $)	look up user+
1239R< > $+			$: < ? $L > < > $(user $1 $)	look up user
1240R< ? $* > < $* > $+ <>	$: < > $3 $2			found; strip $L
1241R< ? $* > < $* > $+	$: < $1 > $3 $2			not found', `
1242R< > $+ 		$: < $L > $(user $1 $)		look up user
1243R< $* > $+ <>		$: < > $2			found; strip $L')
1244ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1245R< $+ > $+		$: < $1 > $2 $&{Host}')
1246dnl')
1247
1248ifdef(`MAIL_HUB', `dnl
1249R< > $+			$: < $H > $1			try hub', `dnl')
1250ifdef(`LOCAL_RELAY', `dnl
1251R< > $+			$: < $R > $1			try relay', `dnl')
1252ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
1253R< > $+			$@ $1', `dnl
1254R< > $+			$: < > < $1 <> $&h >		nope, restore +detail
1255ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1256R< > < $+ @ $+ <> + $* >	$: < > < $1 + $3 @ $2 >	check whether +detail')
1257R< > < $+ <> + $* >	$: < > < $1 + $2 >		check whether +detail
1258R< > < $+ <> $* >	$: < > < $1 >			else discard
1259R< > < $+ + $* > $*	   < > < $1 > + $2 $3		find the user part
1260R< > < $+ > + $*	$#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')		strip the extra +
1261R< > < $+ >		$@ $1				no +detail
1262R$+			$: $1 <> $&h			add +detail back in
1263ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1264R$+ @ $+ <> + $*	$: $1 + $3 @ $2			check whether +detail')
1265R$+ <> + $*		$: $1 + $2			check whether +detail
1266R$+ <> $*		$: $1				else discard')
1267R< local : $* > $*	$: $>MailerToTriple < local : $1 > $2	no host extension
1268R< error : $* > $*	$: $>MailerToTriple < error : $1 > $2	no host extension
1269ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1270dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1271R< $~[ : $+ > $+ @ $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $4 >')
1272R< $~[ : $+ > $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $2 >
1273ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1274R< $+ > $+ @ $+		$@ $>MailerToTriple < $1 > $2 < @ $3 >')
1275R< $+ > $+		$@ $>MailerToTriple < $1 > $2 < @ $1 >
1276
1277ifdef(`_MAILER_TABLE_', `dnl
1278ifdef(`_LDAP_ROUTING_', `dnl
1279###################################################################
1280###  Ruleset LDAPMailertable -- mailertable lookup for LDAP     ###
1281dnl input: <Domain> FullAddress
1282###################################################################
1283
1284SLDAPMailertable
1285R< $+ > $*		$: < $(mailertable $1 $) > $2		lookup
1286R< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		check resolved?
1287R< $+ > $*		$: < $1 > $>Mailertable <$1> $2		try domain
1288R< $+ > $#$*		$#$2					found
1289R< $+ > $*		$#_RELAY_ $@ $1 $: $2			not found, direct relay',
1290`dnl')
1291
1292###################################################################
1293###  Ruleset 90 -- try domain part of mailertable entry 	###
1294dnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress
1295###################################################################
1296
1297SMailertable=90
1298dnl shift and check
1299dnl %2 is not documented in cf/README
1300R$* <$- . $+ > $*	$: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
1301dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1302R$* <$~[ : $* > $*	$>MailerToTriple < $2 : $3 > $4		check -- resolved?
1303R$* < . $+ > $* 	$@ $>Mailertable $1 . <$2> $3		no -- strip & try again
1304dnl is $2 always empty?
1305R$* < $* > $*		$: < $(mailertable . $@ $1$2 $) > $3	try "."
1306R< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		"." found?
1307dnl return full address
1308R< $* > $*		$@ $2				no mailertable match',
1309`dnl')
1310
1311###################################################################
1312###  Ruleset 95 -- canonify mailer:[user@]host syntax to triple	###
1313dnl input: in general: <[mailer:]host> lp<@domain>rest
1314dnl	<> address				-> address
1315dnl	<error:d.s.n:text>			-> error
1316dnl	<error:keyword:text>			-> error
1317dnl	<error:text>				-> error
1318dnl	<mailer:user@host> lp<@domain>rest	-> mailer host user
1319dnl	<mailer:host> address			-> mailer host address
1320dnl	<localdomain> address			-> address
1321dnl	<host> address				-> relay host address
1322###################################################################
1323
1324SMailerToTriple=95
1325R< > $*				$@ $1			strip off null relay
1326R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
1327R< error : $- : $+ > $*		$#error $@ $(dequote $1 $) $: $2
1328R< error : $+ > $*		$#error $: $1
1329R< local : $* > $*		$>CanonLocal < $1 > $2
1330dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1331R< $~[ : $+ @ $+ > $*<$*>$*	$# $1 $@ $3 $: $2<@$3>	use literal user
1332R< $~[ : $+ > $*		$# $1 $@ $2 $: $3	try qualified mailer
1333R< $=w > $*			$@ $2			delete local host
1334R< $+ > $*			$#_RELAY_ $@ $1 $: $2	use unqualified mailer
1335
1336###################################################################
1337###  Ruleset CanonLocal -- canonify local: syntax		###
1338dnl input: <user> address
1339dnl <x> <@host> : rest			-> Recurse rest
1340dnl <x> p1 $=O p2 <@host>		-> Recurse p1 $=O p2
1341dnl <> user <@host> rest		-> local user@host user
1342dnl <> user				-> local user user
1343dnl <user@host> lp <@domain> rest	-> <user> lp <@host> [cont]
1344dnl <user> lp <@host> rest		-> local lp@host user
1345dnl <user> lp				-> local lp user
1346###################################################################
1347
1348SCanonLocal
1349# strip local host from routed addresses
1350R< $* > < @ $+ > : $+		$@ $>Recurse $3
1351R< $* > $+ $=O $+ < @ $+ >	$@ $>Recurse $2 $3 $4
1352
1353# strip trailing dot from any host name that may appear
1354R< $* > $* < @ $* . >		$: < $1 > $2 < @ $3 >
1355
1356# handle local: syntax -- use old user, either with or without host
1357R< > $* < @ $* > $*		$#_LOCAL_ $@ $1@$2 $: $1
1358R< > $+				$#_LOCAL_ $@ $1    $: $1
1359
1360# handle local:user@host syntax -- ignore host part
1361R< $+ @ $+ > $* < @ $* >	$: < $1 > $3 < @ $4 >
1362
1363# handle local:user syntax
1364R< $+ > $* <@ $* > $*		$#_LOCAL_ $@ $2@$3 $: $1
1365R< $+ > $* 			$#_LOCAL_ $@ $2    $: $1
1366
1367###################################################################
1368###  Ruleset 93 -- convert header names to masqueraded form	###
1369###################################################################
1370
1371SMasqHdr=93
1372
1373ifdef(`_GENERICS_TABLE_', `dnl
1374# handle generics database
1375ifdef(`_GENERICS_ENTIRE_DOMAIN_',
1376dnl if generics should be applied add a @ as mark
1377`R$+ < @ $* $=G . >	$: < $1@$2$3 > $1 < @ $2$3 . > @	mark',
1378`R$+ < @ $=G . >	$: < $1@$2 > $1 < @ $2 . > @	mark')
1379R$+ < @ *LOCAL* >	$: < $1@$j > $1 < @ *LOCAL* > @	mark
1380dnl workspace: either user<@domain> or <user@domain> user <@domain> @
1381dnl ignore the first case for now
1382dnl if it has the mark lookup full address
1383dnl broken: %1 is full address not just detail
1384R< $+ > $+ < $* > @	$: < $(generics $1 $: @ $1 $) > $2 < $3 >
1385dnl workspace: ... or <match|@user@domain> user <@domain>
1386dnl no match, try user+detail@domain
1387R<@$+ + $* @ $+> $+ < @ $+ >
1388		$: < $(generics $1+*@$3 $@ $2 $:@$1 + $2@$3 $) >  $4 < @ $5 >
1389R<@$+ + $* @ $+> $+ < @ $+ >
1390		$: < $(generics $1@$3 $: $) > $4 < @ $5 >
1391dnl no match, remove mark
1392R<@$+ > $+ < @ $+ >	$: < > $2 < @ $3 >
1393dnl no match, try @domain for exceptions
1394R< > $+ < @ $+ . >	$: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . >
1395dnl workspace: ... or <match> user <@domain>
1396dnl no match, try local part
1397R< > $+ < @ $+ > 	$: < $(generics $1 $: $) > $1 < @ $2 >
1398R< > $+ + $* < @ $+ > 	$: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 >
1399R< > $+ + $* < @ $+ > 	$: < $(generics $1 $: $) > $1 + $2 < @ $3 >
1400R< $* @ $* > $* < $* >	$@ $>canonify $1 @ $2		found qualified
1401R< $+ > $* < $* >	$: $>canonify $1 @ *LOCAL*	found unqualified
1402R< > $*			$: $1				not found',
1403`dnl')
1404
1405# do not masquerade anything in class N
1406R$* < @ $* $=N . >	$@ $1 < @ $2 $3 . >
1407
1408ifdef(`MASQUERADE_NAME', `dnl
1409# special case the users that should be exposed
1410R$=E < @ *LOCAL* >	$@ $1 < @ $j . >		leave exposed
1411ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1412`R$=E < @ $* $=M . >	$@ $1 < @ $2 $3 . >',
1413`R$=E < @ $=M . >	$@ $1 < @ $2 . >')
1414ifdef(`_LIMITED_MASQUERADE_', `dnl',
1415`R$=E < @ $=w . >	$@ $1 < @ $2 . >')
1416
1417# handle domain-specific masquerading
1418ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1419`R$* < @ $* $=M . > $*	$: $1 < @ $2 $3 . @ $M > $4	convert masqueraded doms',
1420`R$* < @ $=M . > $*	$: $1 < @ $2 . @ $M > $3	convert masqueraded doms')
1421ifdef(`_LIMITED_MASQUERADE_', `dnl',
1422`R$* < @ $=w . > $*	$: $1 < @ $2 . @ $M > $3')
1423R$* < @ *LOCAL* > $*	$: $1 < @ $j . @ $M > $2
1424R$* < @ $+ @ > $*	$: $1 < @ $2 > $3		$M is null
1425R$* < @ $+ @ $+ > $*	$: $1 < @ $3 . > $4		$M is not null
1426dnl', `dnl no masquerading
1427dnl just fix *LOCAL* leftovers
1428R$* < @ *LOCAL* >	$@ $1 < @ $j . >')
1429
1430###################################################################
1431###  Ruleset 94 -- convert envelope names to masqueraded form	###
1432###################################################################
1433
1434SMasqEnv=94
1435ifdef(`_MASQUERADE_ENVELOPE_',
1436`R$+			$@ $>MasqHdr $1',
1437`R$* < @ *LOCAL* > $*	$: $1 < @ $j . > $2')
1438
1439###################################################################
1440###  Ruleset 98 -- local part of ruleset zero (can be null)	###
1441###################################################################
1442
1443SParseLocal=98
1444undivert(3)dnl LOCAL_RULE_0
1445
1446ifdef(`_LDAP_ROUTING_', `dnl
1447######################################################################
1448###  LDAPExpand: Expand address using LDAP routing
1449###
1450###	Parameters:
1451###		<$1> -- parsed address (user < @ domain . >) (pass through)
1452###		<$2> -- RFC822 address (user @ domain) (used for lookup)
1453###		<$3> -- +detail information
1454###
1455###	Returns:
1456###		Mailer triplet ($#mailer $@ host $: address)
1457###		Parsed address (user < @ domain . >)
1458######################################################################
1459
1460# SMTP operation modes
1461C{SMTPOpModes} s d D
1462
1463SLDAPExpand
1464# do the LDAP lookups
1465R<$+><$+><$*>	$: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2> <$3>
1466
1467# look for temporary failures and...
1468R<$* <TMPF>> <$*> <$+> <$+> <$*>	$: $&{opMode} $| TMPF <$&{addr_type}> $| $3
1469R<$*> <$* <TMPF>> <$+> <$+> <$*>	$: $&{opMode} $| TMPF <$&{addr_type}> $| $3
1470ifelse(_LDAP_ROUTE_MAPTEMP_, `_TEMPFAIL_', `dnl
1471# ... temp fail RCPT SMTP commands
1472R$={SMTPOpModes} $| TMPF <e r> $| $+	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."')
1473# ... return original address for MTA to queue up
1474R$* $| TMPF <$*> $| $+			$@ $3
1475
1476# if mailRoutingAddress and local or non-existant mailHost,
1477# return the new mailRoutingAddress
1478ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1479R<$+@$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $6 @ $2
1480R<$+@$+> <> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $5 @ $2')
1481R<$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1
1482R<$+> <> <$+> <$+> <$*>		$@ $>Parse0 $>canonify $1
1483
1484
1485# if mailRoutingAddress and non-local mailHost,
1486# relay to mailHost with new mailRoutingAddress
1487ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1488ifdef(`_MAILER_TABLE_', `dnl
1489# check mailertable for host, relay from there
1490R<$+@$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$3> $>canonify $1 $6 @ $2',
1491`R<$+@$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $3 $: $>canonify $1 $6 @ $2')')
1492ifdef(`_MAILER_TABLE_', `dnl
1493# check mailertable for host, relay from there
1494R<$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$2> $>canonify $1',
1495`R<$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $2 $: $>canonify $1')
1496
1497# if no mailRoutingAddress and local mailHost,
1498# return original address
1499R<> <$=w> <$+> <$+> <$*>	$@ $2
1500
1501
1502# if no mailRoutingAddress and non-local mailHost,
1503# relay to mailHost with original address
1504ifdef(`_MAILER_TABLE_', `dnl
1505# check mailertable for host, relay from there
1506R<> <$+> <$+> <$+> <$*>		$>LDAPMailertable <$1> $2',
1507`R<> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $1 $: $2')
1508
1509ifdef(`_LDAP_ROUTE_DETAIL_',
1510`# if no mailRoutingAddress and no mailHost,
1511# try without +detail
1512R<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <$2 @ $4> <+$3>')dnl
1513
1514ifdef(`_LDAP_ROUTE_NODOMAIN_', `
1515# pretend we did the @domain lookup
1516R<> <> <$+> <$+ @ $+> <$*>	$: <> <> <$1> <@ $3> <$4>', `
1517# if still no mailRoutingAddress and no mailHost,
1518# try @domain
1519ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1520R<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <@ $4> <+$3>')
1521R<> <> <$+> <$+ @ $+> <$*>	$@ $>LDAPExpand <$1> <@ $3> <$4>')
1522
1523# if no mailRoutingAddress and no mailHost and this was a domain attempt,
1524ifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl
1525# user does not exist
1526R<> <> <$+> <@ $+> <$*>		$: <?> < $&{addr_type} > < $1 >
1527# only give error for envelope recipient
1528R<?> <e r> <$+>			$#error $@ nouser $: "550 User unknown"
1529ifdef(`_LDAP_SENDER_MUST_EXIST_', `dnl
1530# and the sender too
1531R<?> <e s> <$+>			$#error $@ nouser $: "550 User unknown"')
1532R<?> <$*> <$+>			$@ $2',
1533`dnl
1534# return the original address
1535R<> <> <$+> <@ $+> <$*>		$@ $1')',
1536`dnl')
1537
1538ifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode.
1539')')
1540ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
1541######################################################################
1542###  D: LookUpDomain -- search for domain in access database
1543###
1544###	Parameters:
1545###		<$1> -- key (domain name)
1546###		<$2> -- default (what to return if not found in db)
1547dnl			must not be empty
1548###		<$3> -- mark (must be <(!|+) single-token>)
1549###			! does lookup only with tag
1550###			+ does lookup with and without tag
1551###		<$4> -- passthru (additional data passed unchanged through)
1552dnl returns:		<default> <passthru>
1553dnl 			<result> <passthru>
1554######################################################################
1555
1556SD
1557dnl workspace <key> <default> <passthru> <mark>
1558dnl lookup with tag (in front, no delimiter here)
1559dnl    2    3  4    5
1560R<$*> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
1561dnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark>
1562dnl lookup without tag?
1563dnl   1    2      3    4
1564R<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
1565ifdef(`_LOOKUPDOTDOMAIN_', `dnl omit first component: lookup .rest
1566dnl XXX apply this also to IP addresses?
1567dnl currently it works the wrong way round for [1.2.3.4]
1568dnl   1  2    3    4  5    6
1569R<?> <$+.$+> <$+> <$- $-> <$*>	$: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4 $5> <$6>
1570dnl   1  2    3      4    5
1571R<?> <$+.$+> <$+> <+ $-> <$*>	$: < $(access .$2 $: ? $) > <$1.$2> <$3> <+ $4> <$5>', `dnl')
1572ifdef(`_ACCESS_SKIP_', `dnl
1573dnl found SKIP: return <default> and <passthru>
1574dnl      1    2    3  4    5
1575R<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
1576dnl not found: IPv4 net (no check is done whether it is an IP number!)
1577dnl    1  2     3    4  5    6
1578R<?> <[$+.$-]> <$+> <$- $-> <$*>	$@ $>D <[$1]> <$3> <$4 $5> <$6>
1579ifdef(`NO_NETINET6', `dnl',
1580`dnl not found: IPv6 net
1581dnl (could be merged with previous rule if we have a class containing .:)
1582dnl    1   2     3    4  5    6
1583R<?> <[$+::$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>
1584R<?> <[$+:$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>')
1585dnl not found, but subdomain: try again
1586dnl   1  2    3    4  5    6
1587R<?> <$+.$+> <$+> <$- $-> <$*>	$@ $>D <$2> <$3> <$4 $5> <$6>
1588ifdef(`_FFR_LOOKUPTAG_', `dnl lookup Tag:
1589dnl   1    2      3    4
1590R<?> <$+> <$+> <! $-> <$*>	$: < $(access $3`'_TAG_DELIM_ $: ? $) > <$1> <$2> <! $3> <$4>', `dnl')
1591dnl not found, no subdomain: return <default> and <passthru>
1592dnl   1    2    3  4    5
1593R<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
1594ifdef(`_ATMPF_', `dnl tempfail?
1595dnl            2    3    4  5    6
1596R<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
1597dnl return <result of lookup> and <passthru>
1598dnl    2    3    4  5    6
1599R<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
1600
1601######################################################################
1602###  A: LookUpAddress -- search for host address in access database
1603###
1604###	Parameters:
1605###		<$1> -- key (dot quadded host address)
1606###		<$2> -- default (what to return if not found in db)
1607dnl			must not be empty
1608###		<$3> -- mark (must be <(!|+) single-token>)
1609###			! does lookup only with tag
1610###			+ does lookup with and without tag
1611###		<$4> -- passthru (additional data passed through)
1612dnl	returns:	<default> <passthru>
1613dnl			<result> <passthru>
1614######################################################################
1615
1616SA
1617dnl lookup with tag
1618dnl    2    3  4    5
1619R<$+> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
1620dnl lookup without tag
1621dnl   1    2      3    4
1622R<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
1623dnl workspace <result-of-lookup|?> <key> <default> <mark> <passthru>
1624ifdef(`_ACCESS_SKIP_', `dnl
1625dnl found SKIP: return <default> and <passthru>
1626dnl      1    2    3  4    5
1627R<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
1628ifdef(`NO_NETINET6', `dnl',
1629`dnl no match; IPv6: remove last part
1630dnl   1   2    3    4  5    6
1631R<?> <$+::$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
1632R<?> <$+:$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>')
1633dnl no match; IPv4: remove last part
1634dnl   1  2    3    4  5    6
1635R<?> <$+.$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
1636dnl no match: return default
1637dnl   1    2    3  4    5
1638R<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
1639ifdef(`_ATMPF_', `dnl tempfail?
1640dnl            2    3    4  5    6
1641R<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
1642dnl match: return result
1643dnl    2    3    4  5    6
1644R<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
1645dnl endif _ACCESS_TABLE_
1646divert(0)
1647######################################################################
1648###  CanonAddr --	Convert an address into a standard form for
1649###			relay checking.  Route address syntax is
1650###			crudely converted into a %-hack address.
1651###
1652###	Parameters:
1653###		$1 -- full recipient address
1654###
1655###	Returns:
1656###		parsed address, not in source route form
1657dnl		user%host%host<@domain>
1658dnl		host!user<@domain>
1659######################################################################
1660
1661SCanonAddr
1662R$*			$: $>Parse0 $>canonify $1	make domain canonical
1663ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
1664R< @ $+ > : $* @ $*	< @ $1 > : $2 % $3	change @ to % in src route
1665R$* < @ $+ > : $* : $*	$3 $1 < @ $2 > : $4	change to % hack.
1666R$* < @ $+ > : $*	$3 $1 < @ $2 >
1667dnl')
1668
1669######################################################################
1670###  ParseRecipient --	Strip off hosts in $=R as well as possibly
1671###			$* $=m or the access database.
1672###			Check user portion for host separators.
1673###
1674###	Parameters:
1675###		$1 -- full recipient address
1676###
1677###	Returns:
1678###		parsed, non-local-relaying address
1679######################################################################
1680
1681SParseRecipient
1682dnl mark and canonify address
1683R$*				$: <?> $>CanonAddr $1
1684dnl workspace: <?> localpart<@domain[.]>
1685R<?> $* < @ $* . >		<?> $1 < @ $2 >			strip trailing dots
1686dnl workspace: <?> localpart<@domain>
1687R<?> $- < @ $* >		$: <?> $(dequote $1 $) < @ $2 >	dequote local part
1688
1689# if no $=O character, no host in the user portion, we are done
1690R<?> $* $=O $* < @ $* >		$: <NO> $1 $2 $3 < @ $4>
1691dnl no $=O in localpart: return
1692R<?> $*				$@ $1
1693
1694dnl workspace: <NO> localpart<@domain>, where localpart contains $=O
1695dnl mark everything which has an "authorized" domain with <RELAY>
1696ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1697# if we relay, check username portion for user%host so host can be checked also
1698R<NO> $* < @ $* $=m >		$: <RELAY> $1 < @ $2 $3 >', `dnl')
1699dnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O
1700dnl if mark is <NO> then change it to <RELAY> if domain is "authorized"
1701
1702dnl what if access map returns something else than RELAY?
1703dnl we are only interested in RELAY entries...
1704dnl other To: entries: blacklist recipient; generic entries?
1705dnl if it is an error we probably do not want to relay anyway
1706ifdef(`_RELAY_HOSTS_ONLY_',
1707`R<NO> $* < @ $=R >		$: <RELAY> $1 < @ $2 >
1708ifdef(`_ACCESS_TABLE_', `dnl
1709R<NO> $* < @ $+ >		$: <$(access To:$2 $: NO $)> $1 < @ $2 >
1710R<NO> $* < @ $+ >		$: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')',
1711`R<NO> $* < @ $* $=R >		$: <RELAY> $1 < @ $2 $3 >
1712ifdef(`_ACCESS_TABLE_', `dnl
1713R<NO> $* < @ $+ >		$: $>D <$2> <NO> <+ To> <$1 < @ $2 >>
1714R<$+> <$+>			$: <$1> $2',`dnl')')
1715
1716
1717ifdef(`_RELAY_MX_SERVED_', `dnl
1718dnl do "we" ($=w) act as backup MX server for the destination domain?
1719R<NO> $* < @ $+ >		$: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > >
1720R<MX> < : $* <TEMP> : > $*	$#TEMP $@ 4.4.0 $: "450 Can not check MX records for recipient host " $1
1721dnl yes: mark it as <RELAY>
1722R<MX> < $* : $=w. : $* > < $+ >	$: <RELAY> $4
1723dnl no: put old <NO> mark back
1724R<MX> < : $* : > < $+ >		$: <NO> $2', `dnl')
1725
1726dnl do we relay to this recipient domain?
1727R<RELAY> $* < @ $* >		$@ $>ParseRecipient $1
1728dnl something else
1729R<$+> $*			$@ $2
1730
1731
1732######################################################################
1733###  check_relay -- check hostname/address on SMTP startup
1734######################################################################
1735
1736ifdef(`_CONTROL_IMMEDIATE_',`dnl
1737Scheck_relay
1738ifdef(`_RATE_CONTROL_IMMEDIATE_',`dnl
1739dnl workspace: ignored...
1740R$*		$: $>"RateControl" dummy', `dnl')
1741ifdef(`_CONN_CONTROL_IMMEDIATE_',`dnl
1742dnl workspace: ignored...
1743R$*		$: $>"ConnControl" dummy', `dnl')
1744dnl')
1745
1746SLocal_check_relay
1747Scheck`'_U_`'relay
1748ifdef(`_USE_CLIENT_PTR_',`dnl
1749R$* $| $*		$: $&{client_ptr} $| $2', `dnl')
1750R$*			$: $1 $| $>"Local_check_relay" $1
1751R$* $| $* $| $#$*	$#$3
1752R$* $| $* $| $*		$@ $>"Basic_check_relay" $1 $| $2
1753
1754SBasic_check_relay
1755# check for deferred delivery mode
1756R$*			$: < $&{deliveryMode} > $1
1757R< d > $*		$@ deferred
1758R< $* > $*		$: $2
1759
1760ifdef(`_ACCESS_TABLE_', `dnl
1761dnl workspace: {client_name} $| {client_addr}
1762R$+ $| $+		$: $>D < $1 > <?> <+ Connect> < $2 >
1763dnl workspace: <result-of-lookup> <{client_addr}>
1764dnl OR $| $+ if client_name is empty
1765R   $| $+		$: $>A < $1 > <?> <+ Connect> <>	empty client_name
1766dnl workspace: <result-of-lookup> <{client_addr}>
1767R<?> <$+>		$: $>A < $1 > <?> <+ Connect> <>	no: another lookup
1768dnl workspace: <result-of-lookup> (<>|<{client_addr}>)
1769R<?> <$*>		$: OK				found nothing
1770dnl workspace: <result-of-lookup> (<>|<{client_addr}>) | OK
1771R<$={Accept}> <$*>	$@ $1				return value of lookup
1772R<REJECT> <$*>		$#error ifdef(`confREJECT_MSG', `$: confREJECT_MSG', `$@ 5.7.1 $: "550 Access denied"')
1773R<DISCARD> <$*>		$#discard $: discard
1774R<QUARANTINE:$+> <$*>	$#error $@ quarantine $: $1
1775dnl error tag
1776R<ERROR:$-.$-.$-:$+> <$*>	$#error $@ $1.$2.$3 $: $4
1777R<ERROR:$+> <$*>		$#error $: $1
1778ifdef(`_ATMPF_', `R<$* _ATMPF_> <$*>		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
1779dnl generic error from access map
1780R<$+> <$*>		$#error $: $1', `dnl')
1781
1782ifdef(`_RBL_',`dnl
1783# DNS based IP address spam list
1784dnl workspace: ignored...
1785R$*			$: $&{client_addr}
1786R$-.$-.$-.$-		$: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
1787R<?>OK			$: OKSOFAR
1788R<?>$+			$#error $@ 5.7.1 $: "550 Rejected: " $&{client_addr} " listed at _RBL_"',
1789`dnl')
1790ifdef(`_RATE_CONTROL_',`dnl
1791ifdef(`_RATE_CONTROL_IMMEDIATE_',`', `dnl
1792dnl workspace: ignored...
1793R$*		$: $>"RateControl" dummy')', `dnl')
1794ifdef(`_CONN_CONTROL_',`dnl
1795ifdef(`_CONN_CONTROL_IMMEDIATE_',`',`dnl
1796dnl workspace: ignored...
1797R$*		$: $>"ConnControl" dummy')', `dnl')
1798undivert(8)
1799ifdef(`_REQUIRE_RDNS_', `dnl
1800R$*			$: $&{client_addr} $| $&{client_resolve}
1801R$=R $*			$@ RELAY		We relay for these
1802R$* $| OK		$@ OK			Resolves.
1803R$* $| FAIL		$#error $@ 5.7.1 $: 550 Fix reverse DNS for $1
1804R$* $| TEMP		$#error $@ 4.1.8 $: 451 Client IP address $1 does not resolve
1805R$* $| FORGED		$#error $@ 4.1.8 $: 451 Possibly forged hostname for $1
1806', `dnl')
1807
1808######################################################################
1809###  check_mail -- check SMTP ``MAIL FROM:'' command argument
1810######################################################################
1811
1812SLocal_check_mail
1813Scheck`'_U_`'mail
1814R$*			$: $1 $| $>"Local_check_mail" $1
1815R$* $| $#$*		$#$2
1816R$* $| $*		$@ $>"Basic_check_mail" $1
1817
1818SBasic_check_mail
1819# check for deferred delivery mode
1820R$*			$: < $&{deliveryMode} > $1
1821R< d > $*		$@ deferred
1822R< $* > $*		$: $2
1823
1824# authenticated?
1825dnl done first: we can require authentication for every mail transaction
1826dnl workspace: address as given by MAIL FROM: (sender)
1827R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
1828R$* $| $#$+		$#$2
1829dnl undo damage: remove result of tls_client call
1830R$* $| $*		$: $1
1831
1832dnl workspace: address as given by MAIL FROM:
1833R<>			$@ <OK>			we MUST accept <> (RFC 1123)
1834ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1835dnl do some additional checks
1836dnl no user@host
1837dnl no user@localhost (if nonlocal sender)
1838dnl this is a pretty simple canonification, it will not catch every case
1839dnl just make sure the address has <> around it (which is required by
1840dnl the RFC anyway, maybe we should complain if they are missing...)
1841dnl dirty trick: if it is user@host, just add a dot: user@host. this will
1842dnl not be modified by host lookups.
1843R$+			$: <?> $1
1844R<?><$+>		$: <@> <$1>
1845R<?>$+			$: <@> <$1>
1846dnl workspace: <@> <address>
1847dnl prepend daemon_flags
1848R$*			$: $&{daemon_flags} $| $1
1849dnl workspace: ${daemon_flags} $| <@> <address>
1850dnl do not allow these at all or only from local systems?
1851R$* f $* $| <@> < $* @ $- >	$: < ? $&{client_name} > < $3 @ $4 >
1852dnl accept unqualified sender: change mark to avoid test
1853R$* u $* $| <@> < $* >	$: <?> < $3 >
1854dnl workspace: ${daemon_flags} $| <@> <address>
1855dnl        or:                    <? ${client_name} > <address>
1856dnl        or:                    <?> <address>
1857dnl remove daemon_flags
1858R$* $| $*		$: $2
1859# handle case of @localhost on address
1860R<@> < $* @ localhost >	$: < ? $&{client_name} > < $1 @ localhost >
1861R<@> < $* @ [127.0.0.1] >
1862			$: < ? $&{client_name} > < $1 @ [127.0.0.1] >
1863R<@> < $* @ localhost.$m >
1864			$: < ? $&{client_name} > < $1 @ localhost.$m >
1865ifdef(`_NO_UUCP_', `dnl',
1866`R<@> < $* @ localhost.UUCP >
1867			$: < ? $&{client_name} > < $1 @ localhost.UUCP >')
1868dnl workspace: < ? $&{client_name} > <user@localhost|host>
1869dnl	or:    <@> <address>
1870dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
1871R<@> $*			$: $1			no localhost as domain
1872dnl workspace: < ? $&{client_name} > <user@localhost|host>
1873dnl	or:    <address>
1874dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
1875R<? $=w> $*		$: $2			local client: ok
1876R<? $+> <$+>		$#error $@ 5.5.4 $: "_CODE553 Real domain name required for sender address"
1877dnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags})
1878R<?> $*			$: $1')
1879dnl workspace: address (or <address>)
1880R$*			$: <?> $>CanonAddr $1		canonify sender address and mark it
1881dnl workspace: <?> CanonicalAddress (i.e. address in canonical form localpart<@host>)
1882dnl there is nothing behind the <@host> so no trailing $* needed
1883R<?> $* < @ $+ . >	<?> $1 < @ $2 >			strip trailing dots
1884# handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
1885R<?> $* < @ $* $=P >	$: <_RES_OK_> $1 < @ $2 $3 >
1886dnl workspace <mark> CanonicalAddress	where mark is ? or OK
1887dnl A sender address with my local host name ($j) is safe
1888R<?> $* < @ $j >	$: <_RES_OK_> $1 < @ $j >
1889ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',
1890`R<?> $* < @ $+ >	$: <_RES_OK_> $1 < @ $2 >		... unresolvable OK',
1891`R<?> $* < @ $+ >	$: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 >
1892R<? $* <$->> $* < @ $+ >
1893			$: <$2> $3 < @ $4 >')
1894dnl workspace <mark> CanonicalAddress	where mark is ?, _RES_OK_, PERM, TEMP
1895dnl mark is ? iff the address is user (wo @domain)
1896
1897ifdef(`_ACCESS_TABLE_', `dnl
1898# check sender address: user@address, user@, address
1899dnl should we remove +ext from user?
1900dnl workspace: <mark> CanonicalAddress where mark is: ?, _RES_OK_, PERM, TEMP
1901R<$+> $+ < @ $* >	$: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <D:$3>
1902R<$+> $+		$: @<$1> <$2> $| <U:$2@>
1903dnl workspace: @<mark> <CanonicalAddress> $| <@type:address> ....
1904dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1905dnl will only return user<@domain when "reversing" the args
1906R@ <$+> <$*> $| <$+>	$: <@> <$1> <$2> $| $>SearchList <+ From> $| <$3> <>
1907dnl workspace: <@><mark> <CanonicalAddress> $| <result>
1908R<@> <$+> <$*> $| <$*>	$: <$3> <$1> <$2>		reverse result
1909dnl workspace: <result> <mark> <CanonicalAddress>
1910# retransform for further use
1911dnl required form:
1912dnl <ResultOfLookup|mark> CanonicalAddress
1913R<?> <$+> <$*>		$: <$1> $2	no match
1914R<$+> <$+> <$*>		$: <$1> $3	relevant result, keep it', `dnl')
1915dnl workspace <ResultOfLookup|mark> CanonicalAddress
1916dnl mark is ? iff the address is user (wo @domain)
1917
1918ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1919# handle case of no @domain on address
1920dnl prepend daemon_flags
1921R<?> $*			$: $&{daemon_flags} $| <?> $1
1922dnl accept unqualified sender: change mark to avoid test
1923R$* u $* $| <?> $*	$: <_RES_OK_> $3
1924dnl remove daemon_flags
1925R$* $| $*		$: $2
1926R<?> $*			$: < ? $&{client_addr} > $1
1927R<?> $*			$@ <_RES_OK_>			...local unqualed ok
1928R<? $+> $*		$#error $@ 5.5.4 $: "_CODE553 Domain name required for sender address " $&f
1929							...remote is not')
1930# check results
1931R<?> $*			$: @ $1		mark address: nothing known about it
1932R<$={ResOk}> $*		$: @ $2		domain ok
1933R<TEMP> $*		$#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
1934R<PERM> $*		$#error $@ 5.1.8 $: "_CODE553 Domain of sender address " $&f " does not exist"
1935ifdef(`_ACCESS_TABLE_', `dnl
1936R<$={Accept}> $*	$# $1		accept from access map
1937R<DISCARD> $*		$#discard $: discard
1938R<QUARANTINE:$+> $*	$#error $@ quarantine $: $1
1939R<REJECT> $*		$#error ifdef(`confREJECT_MSG', `$: confREJECT_MSG', `$@ 5.7.1 $: "550 Access denied"')
1940dnl error tag
1941R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
1942R<ERROR:$+> $*		$#error $: $1
1943ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
1944dnl generic error from access map
1945R<$+> $*		$#error $: $1		error from access db',
1946`dnl')
1947dnl workspace: @ CanonicalAddress (i.e. address in canonical form localpart<@host>)
1948
1949ifdef(`_BADMX_CHK_', `dnl
1950R@ $*<@$+>$*		$: $1<@$2>$3 $| $>BadMX $2
1951R$* $| $#$*		$#$2
1952
1953SBadMX
1954# Look up MX records and ferret away a copy of the original address.
1955# input: domain part of address to check
1956R$+				$:<MX><$1><:$(mxlist $1$):><:>
1957# workspace: <MX><domain><: mxlist-result $><:>
1958R<MX><$+><:$*<TEMP>:><$*>	$#error $@ 4.1.2 $: "450 MX lookup failure for "$1
1959# workspace: <MX> <original destination> <unchecked mxlist> <checked mxlist>
1960# Recursively run badmx check on each mx.
1961R<MX><$*><:$+:$*><:$*>		<MX><$1><:$3><: $4 $(badmx $2 $):>
1962# See if any of them fail.
1963R<MX><$*><$*><$*<BADMX>:$*>	$#error $@ 5.1.2 $:"550 Illegal MX record for host "$1
1964# Reverse the mxlists so we can use the same argument order again.
1965R<MX><$*><$*><$*>		$:<MX><$1><$3><$2>
1966R<MX><$*><:$+:$*><:$*>		<MX><$1><:$3><:$4 $(dnsA $2 $) :>
1967
1968# Reverse the lists so we can use the same argument order again.
1969R<MX><$*><$*><$*>		$:<MX><$1><$3><$2>
1970R<MX><$*><:$+:$*><:$*>		<MX><$1><:$3><:$4 $(BadMXIP $2 $) :>
1971
1972R<MX><$*><$*><$*<BADMXIP>:$*>	$#error $@ 5.1.2 $:"550 Invalid MX record for host "$1',
1973`dnl')
1974
1975
1976######################################################################
1977###  check_rcpt -- check SMTP ``RCPT TO:'' command argument
1978######################################################################
1979
1980SLocal_check_rcpt
1981Scheck`'_U_`'rcpt
1982R$*			$: $1 $| $>"Local_check_rcpt" $1
1983R$* $| $#$*		$#$2
1984R$* $| $*		$@ $>"Basic_check_rcpt" $1
1985
1986SBasic_check_rcpt
1987# empty address?
1988R<>			$#error $@ nouser $: "553 User address required"
1989R$@			$#error $@ nouser $: "553 User address required"
1990# check for deferred delivery mode
1991R$*			$: < $&{deliveryMode} > $1
1992R< d > $*		$@ deferred
1993R< $* > $*		$: $2
1994
1995ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
1996dnl this code checks for user@host where host is not a FQHN.
1997dnl it is not activated.
1998dnl notice: code to check for a recipient without a domain name is
1999dnl available down below; look for the same macro.
2000dnl this check is done here because the name might be qualified by the
2001dnl canonicalization.
2002# require fully qualified domain part?
2003dnl very simple canonification: make sure the address is in < >
2004R$+			$: <?> $1
2005R<?> <$+>		$: <@> <$1>
2006R<?> $+			$: <@> <$1>
2007R<@> < postmaster >	$: postmaster
2008R<@> < $* @ $+ . $+ >	$: < $1 @ $2 . $3 >
2009dnl prepend daemon_flags
2010R<@> $*			$: $&{daemon_flags} $| <@> $1
2011dnl workspace: ${daemon_flags} $| <@> <address>
2012dnl _r_equire qual.rcpt: ok
2013R$* r $* $| <@> < $+ @ $+ >	$: < $3 @ $4 >
2014dnl do not allow these at all or only from local systems?
2015R$* r $* $| <@> < $* >	$: < ? $&{client_name} > < $3 >
2016R<?> < $* >		$: <$1>
2017R<? $=w> < $* >		$: <$1>
2018R<? $+> <$+>		$#error $@ 5.5.4 $: "553 Fully qualified domain name required"
2019dnl remove daemon_flags for other cases
2020R$* $| <@> $*		$: $2', `dnl')
2021
2022dnl ##################################################################
2023dnl call subroutines for recipient and relay
2024dnl possible returns from subroutines:
2025dnl $#TEMP	temporary failure
2026dnl $#error	permanent failure (or temporary if from access map)
2027dnl $#other	stop processing
2028dnl RELAY	RELAYing allowed
2029dnl other	otherwise
2030######################################################################
2031R$*			$: $1 $| @ $>"Rcpt_ok" $1
2032dnl temporary failure? remove mark @ and remember
2033R$* $| @ $#TEMP $+	$: $1 $| T $2
2034dnl error or ok (stop)
2035R$* $| @ $#$*		$#$2
2036ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
2037R$* $| @ RELAY		$@ RELAY
2038dnl something else: call check sender (relay)
2039R$* $| @ $*		$: O $| $>"Relay_ok" $1
2040dnl temporary failure: call check sender (relay)
2041R$* $| T $+		$: T $2 $| $>"Relay_ok" $1
2042dnl temporary failure? return that
2043R$* $| $#TEMP $+	$#error $2
2044dnl error or ok (stop)
2045R$* $| $#$*		$#$2
2046R$* $| RELAY		$@ RELAY
2047dnl something else: return previous temp failure
2048R T $+ $| $*		$#error $1
2049# anything else is bogus
2050R$*			$#error $@ 5.7.1 $: confRELAY_MSG
2051divert(0)
2052
2053######################################################################
2054### Rcpt_ok: is the recipient ok?
2055dnl input: recipient address (RCPT TO)
2056dnl output: see explanation at call
2057######################################################################
2058SRcpt_ok
2059ifdef(`_LOOSE_RELAY_CHECK_',`dnl
2060R$*			$: $>CanonAddr $1
2061R$* < @ $* . >		$1 < @ $2 >			strip trailing dots',
2062`R$*			$: $>ParseRecipient $1		strip relayable hosts')
2063
2064ifdef(`_BESTMX_IS_LOCAL_',`dnl
2065ifelse(_BESTMX_IS_LOCAL_, `', `dnl
2066# unlimited bestmx
2067R$* < @ $* > $*			$: $1 < @ $2 @@ $(bestmx $2 $) > $3',
2068`dnl
2069# limit bestmx to $=B
2070R$* < @ $* $=B > $*		$: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4')
2071R$* $=O $* < @ $* @@ $=w . > $*	$@ $>"Rcpt_ok" $1 $2 $3
2072R$* < @ $* @@ $=w . > $*	$: $1 < @ $3 > $4
2073R$* < @ $* @@ $* > $*		$: $1 < @ $2 > $4')
2074
2075ifdef(`_BLACKLIST_RCPT_',`dnl
2076ifdef(`_ACCESS_TABLE_', `dnl
2077# blacklist local users or any host from receiving mail
2078R$*			$: <?> $1
2079dnl user is now tagged with @ to be consistent with check_mail
2080dnl and to distinguish users from hosts (com would be host, com@ would be user)
2081R<?> $+ < @ $=w >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <D:$2>
2082R<?> $+ < @ $* >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <D:$2>
2083R<?> $+			$: <> <$1> $| <U:$1@>
2084dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
2085dnl will only return user<@domain when "reversing" the args
2086R<> <$*> $| <$+>	$: <@> <$1> $| $>SearchList <+ To> $| <$2> <>
2087R<@> <$*> $| <$*>	$: <$2> <$1>		reverse result
2088R<?> <$*>		$: @ $1		mark address as no match
2089dnl we may have to filter here because otherwise some RHSs
2090dnl would be interpreted as generic error messages...
2091dnl error messages should be "tagged" by prefixing them with error: !
2092dnl that would make a lot of things easier.
2093R<$={Accept}> <$*>	$: @ $2		mark address as no match
2094ifdef(`_ACCESS_SKIP_', `dnl
2095R<SKIP> <$*>		$: @ $1		mark address as no match', `dnl')
2096ifdef(`_DELAY_COMPAT_8_10_',`dnl
2097dnl compatility with 8.11/8.10:
2098dnl we have to filter these because otherwise they would be interpreted
2099dnl as generic error message...
2100dnl error messages should be "tagged" by prefixing them with error: !
2101dnl that would make a lot of things easier.
2102dnl maybe we should stop checks already here (if SPAM_xyx)?
2103R<$={SpamTag}> <$*>	$: @ $2		mark address as no match')
2104R<REJECT> $*		$#error $@ 5.2.1 $: confRCPTREJ_MSG
2105R<DISCARD> $*		$#discard $: discard
2106R<QUARANTINE:$+> $*	$#error $@ quarantine $: $1
2107dnl error tag
2108R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
2109R<ERROR:$+> $*		$#error $: $1
2110ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2111dnl generic error from access map
2112R<$+> $*		$#error $: $1		error from access db
2113R@ $*			$1		remove mark', `dnl')', `dnl')
2114
2115ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
2116# authenticated via TLS?
2117R$*			$: $1 $| $>RelayTLS	client authenticated?
2118R$* $| $# $+		$# $2			error/ok?
2119R$* $| $*		$: $1			no
2120
2121R$*			$: $1 $| $>"Local_Relay_Auth" $&{auth_type}
2122dnl workspace: localpart<@domain> $| result of Local_Relay_Auth
2123R$* $| $# $*		$# $2
2124dnl if Local_Relay_Auth returns NO then do not check $={TrustAuthMech}
2125R$* $| NO		$: $1
2126R$* $| $*		$: $1 $| $&{auth_type}
2127dnl workspace: localpart<@domain> [ $| ${auth_type} ]
2128dnl empty ${auth_type}?
2129R$* $|			$: $1
2130dnl mechanism ${auth_type} accepted?
2131dnl use $# to override further tests (delay_checks): see check_rcpt below
2132R$* $| $={TrustAuthMech}	$# RELAY
2133dnl remove ${auth_type}
2134R$* $| $*		$: $1
2135dnl workspace: localpart<@domain> | localpart
2136ifelse(defn(`_NO_UUCP_'), `r',
2137`R$* ! $* < @ $* >	$: <REMOTE> $2 < @ BANG_PATH >
2138R$* ! $* 		$: <REMOTE> $2 < @ BANG_PATH >', `dnl')
2139# anything terminating locally is ok
2140ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
2141R$+ < @ $* $=m >	$@ RELAY', `dnl')
2142R$+ < @ $=w >		$@ RELAY
2143ifdef(`_RELAY_HOSTS_ONLY_',
2144`R$+ < @ $=R >		$@ RELAY
2145ifdef(`_ACCESS_TABLE_', `dnl
2146ifdef(`_RELAY_FULL_ADDR_', `dnl
2147R$+ < @ $+ >		$: <$(access To:$1@$2 $: ? $)> <$1 < @ $2 >>
2148R<?> <$+ < @ $+ >>	$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>',`
2149R$+ < @ $+ >		$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>')
2150dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
2151R<?> <$+ < @ $+ >>	$: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
2152`R$+ < @ $* $=R >	$@ RELAY
2153ifdef(`_ACCESS_TABLE_', `dnl
2154ifdef(`_RELAY_FULL_ADDR_', `dnl
2155R$+ < @ $+ >		$: $1 < @ $2 > $| $>SearchList <+ To> $| <F:$1@$2> <D:$2> <F:$1@> <>
2156R$+ < @ $+ > $| <$*>	$: <$3> <$1 <@ $2>>
2157R$+ < @ $+ > $| $*	$: <$3> <$1 <@ $2>>',
2158`R$+ < @ $+ >		$: $>D <$2> <?> <+ To> <$1 < @ $2 >>')')')
2159ifdef(`_ACCESS_TABLE_', `dnl
2160dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
2161R<RELAY> $*		$@ RELAY
2162ifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2163R<$*> <$*>		$: $2',`dnl')
2164
2165
2166ifdef(`_RELAY_MX_SERVED_', `dnl
2167# allow relaying for hosts which we MX serve
2168R$+ < @ $+ >		$: < : $(mxserved $2 $) : > $1 < @ $2 >
2169dnl this must not necessarily happen if the client is checked first...
2170R< : $* <TEMP> : > $*	$#TEMP $@ 4.4.0 $: "450 Can not check MX records for recipient host " $1
2171R<$* : $=w . : $*> $*	$@ RELAY
2172R< : $* : > $*		$: $2',
2173`dnl')
2174
2175# check for local user (i.e. unqualified address)
2176R$*			$: <?> $1
2177R<?> $* < @ $+ >	$: <REMOTE> $1 < @ $2 >
2178# local user is ok
2179dnl is it really? the standard requires user@domain, not just user
2180dnl but we should accept it anyway (maybe making it an option:
2181dnl RequireFQDN ?)
2182dnl postmaster must be accepted without domain (DRUMS)
2183ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
2184R<?> postmaster		$@ OK
2185# require qualified recipient?
2186dnl prepend daemon_flags
2187R<?> $+			$: $&{daemon_flags} $| <?> $1
2188dnl workspace: ${daemon_flags} $| <?> localpart
2189dnl do not allow these at all or only from local systems?
2190dnl r flag? add client_name
2191R$* r $* $| <?> $+	$: < ? $&{client_name} > <?> $3
2192dnl no r flag: relay to local user (only local part)
2193# no qualified recipient required
2194R$* $| <?> $+		$@ RELAY
2195dnl client_name is empty
2196R<?> <?> $+		$@ RELAY
2197dnl client_name is local
2198R<? $=w> <?> $+		$@ RELAY
2199dnl client_name is not local
2200R<? $+> $+		$#error $@ 5.5.4 $: "553 Domain name required"', `dnl
2201dnl no qualified recipient required
2202R<?> $+			$@ RELAY')
2203dnl it is a remote user: remove mark and then check client
2204R<$+> $*		$: $2
2205dnl currently the recipient address is not used below
2206
2207######################################################################
2208### Relay_ok: is the relay/sender ok?
2209dnl input: ignored
2210dnl output: see explanation at call
2211######################################################################
2212SRelay_ok
2213# anything originating locally is ok
2214# check IP address
2215R$*			$: $&{client_addr}
2216R$@			$@ RELAY		originated locally
2217R0			$@ RELAY		originated locally
2218R127.0.0.1		$@ RELAY		originated locally
2219RIPv6:::1		$@ RELAY		originated locally
2220R$=R $*			$@ RELAY		relayable IP address
2221ifdef(`_ACCESS_TABLE_', `dnl
2222R$*			$: $>A <$1> <?> <+ Connect> <$1>
2223R<RELAY> $* 		$@ RELAY		relayable IP address
2224ifdef(`_FFR_REJECT_IP_IN_CHECK_RCPT_',`dnl
2225dnl this will cause rejections in cases like:
2226dnl Connect:My.Host.Domain	RELAY
2227dnl Connect:My.Net		REJECT
2228dnl since in check_relay client_name is checked before client_addr
2229R<REJECT> $* 		$@ REJECT		rejected IP address')
2230ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2231R<$*> <$*>		$: $2', `dnl')
2232R$*			$: [ $1 ]		put brackets around it...
2233R$=w			$@ RELAY		... and see if it is local
2234
2235ifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
2236ifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
2237ifdef(`_RELAY_MAIL_FROM_', `dnl
2238dnl input: {client_addr} or something "broken"
2239dnl just throw the input away; we do not need it.
2240# check whether FROM is allowed to use system as relay
2241R$*			$: <?> $>CanonAddr $&f
2242R<?> $+ < @ $+ . >	<?> $1 < @ $2 >		remove trailing dot
2243ifdef(`_RELAY_LOCAL_FROM_', `dnl
2244# check whether local FROM is ok
2245R<?> $+ < @ $=w >	$@ RELAY		FROM local', `dnl')
2246ifdef(`_RELAY_DB_FROM_', `dnl
2247R<?> $+ < @ $+ >	$: <@> $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', ifdef(`_RELAY_HOSTS_ONLY_', `<E:$2>', `<D:$2>')) <>
2248R<@> <RELAY>		$@ RELAY		RELAY FROM sender ok
2249ifdef(`_ATMPF_', `R<@> <_ATMPF_>		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2250', `dnl
2251ifdef(`_RELAY_DB_FROM_DOMAIN_',
2252`errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_
2253')',
2254`dnl')
2255dnl')', `dnl')
2256dnl notice: the rulesets above do not leave a unique workspace behind.
2257dnl it does not matter in this case because the following rule ignores
2258dnl the input. otherwise these rules must "clean up" the workspace.
2259
2260# check client name: first: did it resolve?
2261dnl input: ignored
2262R$*			$: < $&{client_resolve} >
2263R<TEMP>			$#TEMP $@ 4.4.0 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr}
2264R<FORGED>		$#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name}
2265R<FAIL>			$#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name}
2266dnl ${client_resolve} should be OK, so go ahead
2267R$*			$: <@> $&{client_name}
2268dnl should not be necessary since it has been done for client_addr already
2269dnl this rule actually may cause a problem if {client_name} resolves to ""
2270dnl however, this should not happen since the forward lookup should fail
2271dnl and {client_resolve} should be TEMP or FAIL.
2272dnl nevertheless, removing the rule doesn't hurt.
2273dnl R<@>			$@ RELAY
2274dnl workspace: <@> ${client_name} (not empty)
2275# pass to name server to make hostname canonical
2276R<@> $* $=P 		$:<?>  $1 $2
2277R<@> $+			$:<?>  $[ $1 $]
2278dnl workspace: <?> ${client_name} (canonified)
2279R$* .			$1			strip trailing dots
2280ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
2281R<?> $* $=m		$@ RELAY', `dnl')
2282R<?> $=w		$@ RELAY
2283ifdef(`_RELAY_HOSTS_ONLY_',
2284`R<?> $=R		$@ RELAY
2285ifdef(`_ACCESS_TABLE_', `dnl
2286R<?> $*			$: <$(access Connect:$1 $: ? $)> <$1>
2287R<?> <$*>		$: <$(access $1 $: ? $)> <$1>',`dnl')',
2288`R<?> $* $=R			$@ RELAY
2289ifdef(`_ACCESS_TABLE_', `dnl
2290R<?> $*			$: $>D <$1> <?> <+ Connect> <$1>',`dnl')')
2291ifdef(`_ACCESS_TABLE_', `dnl
2292R<RELAY> $*		$@ RELAY
2293ifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2294R<$*> <$*>		$: $2',`dnl')
2295dnl end of _PROMISCUOUS_RELAY_
2296divert(0)
2297ifdef(`_DELAY_CHECKS_',`dnl
2298# turn a canonical address in the form user<@domain>
2299# qualify unqual. addresses with $j
2300dnl it might have been only user (without <@domain>)
2301SFullAddr
2302R$* <@ $+ . >		$1 <@ $2 >
2303R$* <@ $* >		$@ $1 <@ $2 >
2304R$+			$@ $1 <@ $j >
2305
2306SDelay_TLS_Clt
2307# authenticated?
2308dnl code repeated here from Basic_check_mail
2309dnl only called from check_rcpt in delay mode if checkrcpt returns $#
2310R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
2311R$* $| $#$+		$#$2
2312dnl return result from checkrcpt
2313R$* $| $*		$# $1
2314R$*			$# $1
2315
2316SDelay_TLS_Clt2
2317# authenticated?
2318dnl code repeated here from Basic_check_mail
2319dnl only called from check_rcpt in delay mode if stopping due to Friend/Hater
2320R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
2321R$* $| $#$+		$#$2
2322dnl return result from friend/hater check
2323R$* $| $*		$@ $1
2324R$*			$@ $1
2325
2326# call all necessary rulesets
2327Scheck_rcpt
2328dnl this test should be in the Basic_check_rcpt ruleset
2329dnl which is the correct DSN code?
2330# R$@			$#error $@ 5.1.3 $: "553 Recipient address required"
2331
2332R$+			$: $1 $| $>checkrcpt $1
2333dnl now we can simply stop checks by returning "$# xyz" instead of just "ok"
2334dnl on error (or discard) stop now
2335R$+ $| $#error $*	$#error $2
2336R$+ $| $#discard $*	$#discard $2
2337dnl otherwise call tls_client; see above
2338R$+ $| $#$*		$@ $>"Delay_TLS_Clt" $2
2339R$+ $| $*		$: <?> $>FullAddr $>CanonAddr $1
2340ifdef(`_SPAM_FH_',
2341`dnl lookup user@ and user@address
2342ifdef(`_ACCESS_TABLE_', `',
2343`errprint(`*** ERROR: FEATURE(`delay_checks', `argument') requires FEATURE(`access_db')
2344')')dnl
2345dnl one of the next two rules is supposed to match
2346dnl this code has been copied from BLACKLIST... etc
2347dnl and simplified by omitting some < >.
2348R<?> $+ < @ $=w >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 > <U: $1@>
2349R<?> $+ < @ $* >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 >
2350dnl R<?>		$@ something_is_very_wrong_here
2351# lookup the addresses only with Spam tag
2352R<> $* $| <$+>		$: <@> $1 $| $>SearchList <! Spam> $| <$2> <>
2353R<@> $* $| $*		$: $2 $1		reverse result
2354dnl', `dnl')
2355ifdef(`_SPAM_FRIEND_',
2356`# is the recipient a spam friend?
2357ifdef(`_SPAM_HATER_',
2358	`errprint(`*** ERROR: define either Hater or Friend -- not both.
2359')', `dnl')
2360R<FRIEND> $+		$@ $>"Delay_TLS_Clt2" SPAMFRIEND
2361R<$*> $+		$: $2',
2362`dnl')
2363ifdef(`_SPAM_HATER_',
2364`# is the recipient no spam hater?
2365R<HATER> $+		$: $1			spam hater: continue checks
2366R<$*> $+		$@ $>"Delay_TLS_Clt2" NOSPAMHATER	everyone else: stop
2367dnl',`dnl')
2368
2369dnl run further checks: check_mail
2370dnl should we "clean up" $&f?
2371ifdef(`_FFR_MAIL_MACRO',
2372`R$*			$: $1 $| $>checkmail $&{mail_from}',
2373`R$*			$: $1 $| $>checkmail <$&f>')
2374dnl recipient (canonical format) $| result of checkmail
2375R$* $| $#$*		$#$2
2376dnl run further checks: check_relay
2377R$* $| $*		$: $1 $| $>checkrelay $&{client_name} $| $&{client_addr}
2378R$* $| $#$*		$#$2
2379R$* $| $*		$: $1
2380', `dnl')
2381
2382ifdef(`_BLOCK_BAD_HELO_', `dnl
2383R$*			$: $1 $| <$&{auth_authen}>	Get auth info
2384dnl Bypass the test for users who have authenticated.
2385R$* $| <$+>		$: $1				skip if auth
2386R$* $| <$*>		$: $1 $| <$&{client_addr}> [$&s]	Get connection info
2387dnl Bypass for local clients -- IP address starts with $=R
2388R$* $| <$=R $*> [$*]	$: $1				skip if local client
2389dnl Bypass a "sendmail -bs" session, which use 0 for client ip address
2390R$* $| <0> [$*]		$: $1				skip if sendmail -bs
2391dnl Reject our IP - assumes "[ip]" is in class $=w
2392R$* $| <$*> $=w		$#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2393dnl Reject our hostname
2394R$* $| <$*> [$=w]	$#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2395dnl Pass anything else with a "." in the domain parameter
2396R$* $| <$*> [$+.$+]	$: $1				qualified domain ok
2397dnl Reject if there was no "." or only an initial or final "."
2398R$* $| <$*> [$*]	$#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2399dnl Clean up the workspace
2400R$* $| $*		$: $1
2401', `dnl')
2402
2403ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
2404######################################################################
2405###  F: LookUpFull -- search for an entry in access database
2406###
2407###	lookup of full key (which should be an address) and
2408###	variations if +detail exists: +* and without +detail
2409###
2410###	Parameters:
2411###		<$1> -- key
2412###		<$2> -- default (what to return if not found in db)
2413dnl			must not be empty
2414###		<$3> -- mark (must be <(!|+) single-token>)
2415###			! does lookup only with tag
2416###			+ does lookup with and without tag
2417###		<$4> -- passthru (additional data passed unchanged through)
2418dnl returns:		<default> <passthru>
2419dnl 			<result> <passthru>
2420######################################################################
2421
2422SF
2423dnl workspace: <key> <def> <o tag> <thru>
2424dnl full lookup
2425dnl    2    3  4    5
2426R<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2427dnl no match, try without tag
2428dnl   1    2      3    4
2429R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2430dnl no match, +detail: try +*
2431dnl   1    2    3    4    5  6    7
2432R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
2433			$: <$(access $6`'_TAG_DELIM_`'$1+*@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
2434dnl no match, +detail: try +* without tag
2435dnl   1    2    3    4      5    6
2436R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
2437			$: <$(access $1+*@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2438dnl no match, +detail: try without +detail
2439dnl   1    2    3    4    5  6    7
2440R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
2441			$: <$(access $6`'_TAG_DELIM_`'$1@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
2442dnl no match, +detail: try without +detail and without tag
2443dnl   1    2    3    4      5    6
2444R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
2445			$: <$(access $1@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2446dnl no match, return <default> <passthru>
2447dnl   1    2    3  4    5
2448R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
2449ifdef(`_ATMPF_', `dnl tempfail?
2450dnl            2    3  4    5
2451R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
2452dnl match, return <match> <passthru>
2453dnl    2    3  4    5
2454R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
2455
2456######################################################################
2457###  E: LookUpExact -- search for an entry in access database
2458###
2459###	Parameters:
2460###		<$1> -- key
2461###		<$2> -- default (what to return if not found in db)
2462dnl			must not be empty
2463###		<$3> -- mark (must be <(!|+) single-token>)
2464###			! does lookup only with tag
2465###			+ does lookup with and without tag
2466###		<$4> -- passthru (additional data passed unchanged through)
2467dnl returns:		<default> <passthru>
2468dnl 			<result> <passthru>
2469######################################################################
2470
2471SE
2472dnl    2    3  4    5
2473R<$*> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2474dnl no match, try without tag
2475dnl   1    2      3    4
2476R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2477dnl no match, return default passthru
2478dnl   1    2    3  4    5
2479R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
2480ifdef(`_ATMPF_', `dnl tempfail?
2481dnl            2    3  4    5
2482R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
2483dnl match, return <match> <passthru>
2484dnl    2    3  4    5
2485R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
2486
2487######################################################################
2488###  U: LookUpUser -- search for an entry in access database
2489###
2490###	lookup of key (which should be a local part) and
2491###	variations if +detail exists: +* and without +detail
2492###
2493###	Parameters:
2494###		<$1> -- key (user@)
2495###		<$2> -- default (what to return if not found in db)
2496dnl			must not be empty
2497###		<$3> -- mark (must be <(!|+) single-token>)
2498###			! does lookup only with tag
2499###			+ does lookup with and without tag
2500###		<$4> -- passthru (additional data passed unchanged through)
2501dnl returns:		<default> <passthru>
2502dnl 			<result> <passthru>
2503######################################################################
2504
2505SU
2506dnl user lookups are always with trailing @
2507dnl    2    3  4    5
2508R<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2509dnl no match, try without tag
2510dnl   1    2      3    4
2511R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2512dnl do not remove the @ from the lookup:
2513dnl it is part of the +detail@ which is omitted for the lookup
2514dnl no match, +detail: try +*
2515dnl   1    2      3    4  5    6
2516R<?> <$+ + $* @> <$*> <$- $-> <$*>
2517			$: <$(access $5`'_TAG_DELIM_`'$1+*@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
2518dnl no match, +detail: try +* without tag
2519dnl   1    2      3      4    5
2520R<?> <$+ + $* @> <$*> <+ $-> <$*>
2521			$: <$(access $1+*@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
2522dnl no match, +detail: try without +detail
2523dnl   1    2      3    4  5    6
2524R<?> <$+ + $* @> <$*> <$- $-> <$*>
2525			$: <$(access $5`'_TAG_DELIM_`'$1@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
2526dnl no match, +detail: try without +detail and without tag
2527dnl   1    2      3      4    5
2528R<?> <$+ + $* @> <$*> <+ $-> <$*>
2529			$: <$(access $1@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
2530dnl no match, return <default> <passthru>
2531dnl   1    2    3  4    5
2532R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
2533ifdef(`_ATMPF_', `dnl tempfail?
2534dnl            2    3  4    5
2535R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
2536dnl match, return <match> <passthru>
2537dnl    2    3  4    5
2538R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
2539
2540######################################################################
2541###  SearchList: search a list of items in the access map
2542###	Parameters:
2543###		<exact tag> $| <mark:address> <mark:address> ... <>
2544dnl	maybe we should have a @ (again) in front of the mark to
2545dnl	avoid errorneous matches (with error messages?)
2546dnl	if we can make sure that tag is always a single token
2547dnl	then we can omit the delimiter $|, otherwise we need it
2548dnl	to avoid errorneous matchs (first rule: D: if there
2549dnl	is that mark somewhere in the list, it will be taken).
2550dnl	moreover, we can do some tricks to enforce lookup with
2551dnl	the tag only, e.g.:
2552###	where "exact" is either "+" or "!":
2553###	<+ TAG>	lookup with and w/o tag
2554###	<! TAG>	lookup with tag
2555dnl	Warning: + and ! should be in OperatorChars (otherwise there must be
2556dnl		a blank between them and the tag.
2557###	possible values for "mark" are:
2558###		D: recursive host lookup (LookUpDomain)
2559dnl		A: recursive address lookup (LookUpAddress) [not yet required]
2560###		E: exact lookup, no modifications
2561###		F: full lookup, try user+ext@domain and user@domain
2562###		U: user lookup, try user+ext and user (input must have trailing @)
2563###	return: <RHS of lookup> or <?> (not found)
2564######################################################################
2565
2566# class with valid marks for SearchList
2567dnl if A is activated: add it
2568C{Src}E F D U ifdef(`_FFR_SRCHLIST_A', `A')
2569SSearchList
2570# just call the ruleset with the name of the tag... nice trick...
2571dnl       2       3    4
2572R<$+> $| <$={Src}:$*> <$*>	$: <$1> $| <$4> $| $>$2 <$3> <?> <$1> <>
2573dnl workspace: <o tag> $| <rest> $| <result of lookup> <>
2574dnl no match and nothing left: return
2575R<$+> $| <> $| <?> <>		$@ <?>
2576dnl no match but something left: continue
2577R<$+> $| <$+> $| <?> <>		$@ $>SearchList <$1> $| <$2>
2578dnl match: return
2579R<$+> $| <$*> $| <$+> <>	$@ <$3>
2580dnl return result from recursive invocation
2581R<$+> $| <$+>			$@ <$2>
2582dnl endif _ACCESS_TABLE_
2583divert(0)
2584
2585######################################################################
2586###  trust_auth: is user trusted to authenticate as someone else?
2587###
2588###	Parameters:
2589###		$1: AUTH= parameter from MAIL command
2590######################################################################
2591
2592dnl empty ruleset definition so it can be called
2593SLocal_trust_auth
2594Strust_auth
2595R$*			$: $&{auth_type} $| $1
2596# required by RFC 2554 section 4.
2597R$@ $| $*		$#error $@ 5.7.1 $: "550 not authenticated"
2598dnl seems to be useful...
2599R$* $| $&{auth_authen}		$@ identical
2600R$* $| <$&{auth_authen}>	$@ identical
2601dnl call user supplied code
2602R$* $| $*		$: $1 $| $>"Local_trust_auth" $2
2603R$* $| $#$*		$#$2
2604dnl default: error
2605R$*			$#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author}
2606
2607######################################################################
2608###  Relay_Auth: allow relaying based on authentication?
2609###
2610###	Parameters:
2611###		$1: ${auth_type}
2612######################################################################
2613SLocal_Relay_Auth
2614
2615######################################################################
2616###  srv_features: which features to offer to a client?
2617###	(done in server)
2618######################################################################
2619Ssrv_features
2620ifdef(`_LOCAL_SRV_FEATURES_', `dnl
2621R$*			$: $1 $| $>"Local_srv_features" $1
2622R$* $| $#$*		$#$2
2623R$* $| $*		$: $1', `dnl')
2624ifdef(`_ACCESS_TABLE_', `dnl
2625R$*		$: $>D <$&{client_name}> <?> <! SRV_FEAT_TAG> <>
2626R<?>$*		$: $>A <$&{client_addr}> <?> <! SRV_FEAT_TAG> <>
2627R<?>$*		$: <$(access SRV_FEAT_TAG`'_TAG_DELIM_ $: ? $)>
2628R<?>$*		$@ OK
2629ifdef(`_ATMPF_', `dnl tempfail?
2630R<$* _ATMPF_>$*	$#temp', `dnl')
2631R<$+>$*		$# $1')
2632
2633######################################################################
2634###  try_tls: try to use STARTTLS?
2635###	(done in client)
2636######################################################################
2637Stry_tls
2638ifdef(`_LOCAL_TRY_TLS_', `dnl
2639R$*			$: $1 $| $>"Local_try_tls" $1
2640R$* $| $#$*		$#$2
2641R$* $| $*		$: $1', `dnl')
2642ifdef(`_ACCESS_TABLE_', `dnl
2643R$*		$: $>D <$&{server_name}> <?> <! TLS_TRY_TAG> <>
2644R<?>$*		$: $>A <$&{server_addr}> <?> <! TLS_TRY_TAG> <>
2645R<?>$*		$: <$(access TLS_TRY_TAG`'_TAG_DELIM_ $: ? $)>
2646R<?>$*		$@ OK
2647ifdef(`_ATMPF_', `dnl tempfail?
2648R<$* _ATMPF_>$*	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2649R<NO>$*		$#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"')
2650
2651######################################################################
2652###  tls_rcpt: is connection with server "good" enough?
2653###	(done in client, per recipient)
2654dnl called from deliver() before RCPT command
2655###
2656###	Parameters:
2657###		$1: recipient
2658######################################################################
2659Stls_rcpt
2660ifdef(`_LOCAL_TLS_RCPT_', `dnl
2661R$*			$: $1 $| $>"Local_tls_rcpt" $1
2662R$* $| $#$*		$#$2
2663R$* $| $*		$: $1', `dnl')
2664ifdef(`_ACCESS_TABLE_', `dnl
2665dnl store name of other side
2666R$*			$: $(macro {TLS_Name} $@ $&{server_name} $) $1
2667dnl canonify recipient address
2668R$+			$: <?> $>CanonAddr $1
2669dnl strip trailing dots
2670R<?> $+ < @ $+ . >	<?> $1 <@ $2 >
2671dnl full address?
2672R<?> $+ < @ $+ >	$: $1 <@ $2 > $| <F:$1@$2> <U:$1@> <D:$2> <E:>
2673dnl only localpart?
2674R<?> $+			$: $1 $| <U:$1@> <E:>
2675dnl look it up
2676dnl also look up a default value via E:
2677R$* $| $+	$: $1 $| $>SearchList <! TLS_RCPT_TAG> $| $2 <>
2678dnl found nothing: stop here
2679R$* $| <?>	$@ OK
2680ifdef(`_ATMPF_', `dnl tempfail?
2681R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2682dnl use the generic routine (for now)
2683R$* $| <$+>	$@ $>"TLS_connection" $&{verify} $| <$2>')
2684
2685######################################################################
2686###  tls_client: is connection with client "good" enough?
2687###	(done in server)
2688###
2689###	Parameters:
2690###		${verify} $| (MAIL|STARTTLS)
2691######################################################################
2692dnl MAIL: called from check_mail
2693dnl STARTTLS: called from smtp() after STARTTLS has been accepted
2694Stls_client
2695ifdef(`_LOCAL_TLS_CLIENT_', `dnl
2696R$*			$: $1 <?> $>"Local_tls_client" $1
2697R$* <?> $#$*		$#$2
2698R$* <?> $*		$: $1', `dnl')
2699ifdef(`_ACCESS_TABLE_', `dnl
2700dnl store name of other side
2701R$*		$: $(macro {TLS_Name} $@ $&{client_name} $) $1
2702dnl ignore second arg for now
2703dnl maybe use it to distinguish permanent/temporary error?
2704dnl if MAIL: permanent (STARTTLS has not been offered)
2705dnl if STARTTLS: temporary (offered but maybe failed)
2706R$* $| $*	$: $1 $| $>D <$&{client_name}> <?> <! TLS_CLT_TAG> <>
2707R$* $| <?>$*	$: $1 $| $>A <$&{client_addr}> <?> <! TLS_CLT_TAG> <>
2708dnl do a default lookup: just TLS_CLT_TAG
2709R$* $| <?>$*	$: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)>
2710ifdef(`_ATMPF_', `dnl tempfail?
2711R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2712R$*		$@ $>"TLS_connection" $1', `dnl
2713R$* $| $*	$@ $>"TLS_connection" $1')
2714
2715######################################################################
2716###  tls_server: is connection with server "good" enough?
2717###	(done in client)
2718###
2719###	Parameter:
2720###		${verify}
2721######################################################################
2722dnl i.e. has the server been authenticated and is encryption active?
2723dnl called from deliver() after STARTTLS command
2724Stls_server
2725ifdef(`_LOCAL_TLS_SERVER_', `dnl
2726R$*			$: $1 $| $>"Local_tls_server" $1
2727R$* $| $#$*		$#$2
2728R$* $| $*		$: $1', `dnl')
2729ifdef(`_ACCESS_TABLE_', `dnl
2730dnl store name of other side
2731R$*		$: $(macro {TLS_Name} $@ $&{server_name} $) $1
2732R$*		$: $1 $| $>D <$&{server_name}> <?> <! TLS_SRV_TAG> <>
2733R$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! TLS_SRV_TAG> <>
2734dnl do a default lookup: just TLS_SRV_TAG
2735R$* $| <?>$*	$: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)>
2736ifdef(`_ATMPF_', `dnl tempfail?
2737R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2738R$*		$@ $>"TLS_connection" $1', `dnl
2739R$*		$@ $>"TLS_connection" $1')
2740
2741######################################################################
2742###  TLS_connection: is TLS connection "good" enough?
2743###
2744###	Parameters:
2745ifdef(`_ACCESS_TABLE_', `dnl
2746###		${verify} $| <Requirement> [<>]', `dnl
2747###		${verify}')
2748###		Requirement: RHS from access map, may be ? for none.
2749dnl	syntax for Requirement:
2750dnl	[(PERM|TEMP)+] (VERIFY[:bits]|ENCR:bits) [+extensions]
2751dnl	extensions: could be a list of further requirements
2752dnl		for now: CN:string	{cn_subject} == string
2753######################################################################
2754STLS_connection
2755ifdef(`_ACCESS_TABLE_', `dnl', `dnl use default error
2756dnl deal with TLS handshake failures: abort
2757RSOFTWARE	$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake."
2758divert(-1)')
2759dnl common ruleset for tls_{client|server}
2760dnl input: ${verify} $| <ResultOfLookup> [<>]
2761dnl remove optional <>
2762R$* $| <$*>$*			$: $1 $| <$2>
2763dnl workspace: ${verify} $| <ResultOfLookup>
2764# create the appropriate error codes
2765dnl permanent or temporary error?
2766R$* $| <PERM + $={Tls} $*>	$: $1 $| <503:5.7.0> <$2 $3>
2767R$* $| <TEMP + $={Tls} $*>	$: $1 $| <403:4.7.0> <$2 $3>
2768dnl default case depends on TLS_PERM_ERR
2769R$* $| <$={Tls} $*>		$: $1 $| <ifdef(`TLS_PERM_ERR', `503:5.7.0', `403:4.7.0')> <$2 $3>
2770dnl workspace: ${verify} $| [<SMTP:ESC>] <ResultOfLookup>
2771# deal with TLS handshake failures: abort
2772RSOFTWARE $| <$-:$+> $* 	$#error $@ $2 $: $1 " TLS handshake failed."
2773dnl no <reply:dns> i.e. not requirements in the access map
2774dnl use default error
2775RSOFTWARE $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed."
2776# deal with TLS protocol errors: abort
2777RPROTOCOL $| <$-:$+> $* 	$#error $@ $2 $: $1 " STARTTLS failed."
2778dnl no <reply:dns> i.e. not requirements in the access map
2779dnl use default error
2780RPROTOCOL $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') STARTTLS failed."
2781R$* $| <$*> <VERIFY>		$: <$2> <VERIFY> <> $1
2782dnl separate optional requirements
2783R$* $| <$*> <VERIFY + $+>	$: <$2> <VERIFY> <$3> $1
2784R$* $| <$*> <$={Tls}:$->$*	$: <$2> <$3:$4> <> $1
2785dnl separate optional requirements
2786R$* $| <$*> <$={Tls}:$- + $+>$*	$: <$2> <$3:$4> <$5> $1
2787dnl some other value in access map: accept
2788dnl this also allows to override the default case (if used)
2789R$* $| $*			$@ OK
2790# authentication required: give appropriate error
2791# other side did authenticate (via STARTTLS)
2792dnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> <[extensions]> ${verify}
2793dnl only verification required and it succeeded
2794R<$*><VERIFY> <> OK		$@ OK
2795dnl verification required and it succeeded but extensions are given
2796dnl change it to <SMTP:ESC> <REQ:0>  <extensions>
2797R<$*><VERIFY> <$+> OK		$: <$1> <REQ:0> <$2>
2798dnl verification required + some level of encryption
2799R<$*><VERIFY:$-> <$*> OK	$: <$1> <REQ:$2> <$3>
2800dnl just some level of encryption required
2801R<$*><ENCR:$-> <$*> $*		$: <$1> <REQ:$2> <$3>
2802dnl workspace:
2803dnl 1. <SMTP:ESC> <VERIFY [:bits]>  <[extensions]> {verify} (!= OK)
2804dnl 2. <SMTP:ESC> <REQ:bits>  <[extensions]>
2805dnl verification required but ${verify} is not set (case 1.)
2806R<$-:$+><VERIFY $*> <$*>	$#error $@ $2 $: $1 " authentication required"
2807R<$-:$+><VERIFY $*> <$*> FAIL	$#error $@ $2 $: $1 " authentication failed"
2808R<$-:$+><VERIFY $*> <$*> NO	$#error $@ $2 $: $1 " not authenticated"
2809R<$-:$+><VERIFY $*> <$*> NOT	$#error $@ $2 $: $1 " no authentication requested"
2810R<$-:$+><VERIFY $*> <$*> NONE	$#error $@ $2 $: $1 " other side does not support STARTTLS"
2811dnl some other value for ${verify}
2812R<$-:$+><VERIFY $*> <$*> $+	$#error $@ $2 $: $1 " authentication failure " $4
2813dnl some level of encryption required: get the maximum level (case 2.)
2814R<$*><REQ:$-> <$*>		$: <$1> <REQ:$2> <$3> $>max $&{cipher_bits} : $&{auth_ssf}
2815dnl compare required bits with actual bits
2816R<$*><REQ:$-> <$*> $-		$: <$1> <$2:$4> <$3> $(arith l $@ $4 $@ $2 $)
2817R<$-:$+><$-:$-> <$*> TRUE	$#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3
2818dnl strength requirements fulfilled
2819dnl TLS Additional Requirements Separator
2820dnl this should be something which does not appear in the extensions itself
2821dnl @ could be part of a CN, DN, etc...
2822dnl use < > ? those are encoded in CN, DN, ...
2823define(`_TLS_ARS_', `++')dnl
2824dnl workspace:
2825dnl <SMTP:ESC> <REQ:bits> <extensions> result-of-compare
2826R<$-:$+><$-:$-> <$*> $*		$: <$1:$2 _TLS_ARS_ $5>
2827dnl workspace: <SMTP:ESC _TLS_ARS_ extensions>
2828dnl continue: check  extensions
2829R<$-:$+ _TLS_ARS_ >			$@ OK
2830dnl split extensions into own list
2831R<$-:$+ _TLS_ARS_ $+ >			$: <$1:$2> <$3>
2832R<$-:$+> < $+ _TLS_ARS_ $+ >		<$1:$2> <$3> <$4>
2833R<$-:$+> $+			$@ $>"TLS_req" $3 $| <$1:$2>
2834
2835######################################################################
2836###  TLS_req: check additional TLS requirements
2837###
2838###	Parameters: [<list> <of> <req>] $| <$-:$+>
2839###		$-: SMTP reply code
2840###		$+: Enhanced Status Code
2841dnl  further requirements for this ruleset:
2842dnl	name of "other side" is stored is {TLS_name} (client/server_name)
2843dnl
2844dnl	currently only CN[:common_name] is implemented
2845dnl	right now this is only a logical AND
2846dnl	i.e. all requirements must be true
2847dnl	how about an OR? CN must be X or CN must be Y or ..
2848dnl	use a macro to compute this as a trivial sequential
2849dnl	operations (no precedences etc)?
2850######################################################################
2851STLS_req
2852dnl no additional requirements: ok
2853R $| $+		$@ OK
2854dnl require CN: but no CN specified: use name of other side
2855R<CN> $* $| <$+>		$: <CN:$&{TLS_Name}> $1 $| <$2>
2856dnl match, check rest
2857R<CN:$&{cn_subject}> $* $| <$+>		$@ $>"TLS_req" $1 $| <$2>
2858dnl CN does not match
2859dnl  1   2      3  4
2860R<CN:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " CN " $&{cn_subject} " does not match " $1
2861dnl cert subject
2862R<CS:$&{cert_subject}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
2863dnl CS does not match
2864dnl  1   2      3  4
2865R<CS:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " Cert Subject " $&{cert_subject} " does not match " $1
2866dnl match, check rest
2867R<CI:$&{cert_issuer}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
2868dnl CI does not match
2869dnl  1   2      3  4
2870R<CI:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " Cert Issuer " $&{cert_issuer} " does not match " $1
2871dnl return from recursive call
2872ROK			$@ OK
2873
2874######################################################################
2875###  max: return the maximum of two values separated by :
2876###
2877###	Parameters: [$-]:[$-]
2878######################################################################
2879Smax
2880R:		$: 0
2881R:$-		$: $1
2882R$-:		$: $1
2883R$-:$-		$: $(arith l $@ $1 $@ $2 $) : $1 : $2
2884RTRUE:$-:$-	$: $2
2885R$-:$-:$-	$: $2
2886dnl endif _ACCESS_TABLE_
2887divert(0)
2888
2889######################################################################
2890###  RelayTLS: allow relaying based on TLS authentication
2891###
2892###	Parameters:
2893###		none
2894######################################################################
2895SRelayTLS
2896# authenticated?
2897dnl we do not allow relaying for anyone who can present a cert
2898dnl signed by a "trusted" CA. For example, even if we put verisigns
2899dnl CA in CertPath so we can authenticate users, we do not allow
2900dnl them to abuse our server (they might be easier to get hold of,
2901dnl but anyway).
2902dnl so here is the trick: if the verification succeeded
2903dnl we look up the cert issuer in the access map
2904dnl (maybe after extracting a part with a regular expression)
2905dnl if this returns RELAY we relay without further questions
2906dnl if it returns SUBJECT we perform a similar check on the
2907dnl cert subject.
2908ifdef(`_ACCESS_TABLE_', `dnl
2909R$*			$: <?> $&{verify}
2910R<?> OK			$: OK		authenticated: continue
2911R<?> $*			$@ NO		not authenticated
2912ifdef(`_CERT_REGEX_ISSUER_', `dnl
2913R$*			$: $(CERTIssuer $&{cert_issuer} $)',
2914`R$*			$: $&{cert_issuer}')
2915R$+			$: $(access CERTISSUER`'_TAG_DELIM_`'$1 $)
2916dnl use $# to stop further checks (delay_check)
2917RRELAY			$# RELAY
2918ifdef(`_CERT_REGEX_SUBJECT_', `dnl
2919RSUBJECT		$: <@> $(CERTSubject $&{cert_subject} $)',
2920`RSUBJECT		$: <@> $&{cert_subject}')
2921R<@> $+			$: <@> $(access CERTSUBJECT`'_TAG_DELIM_`'$1 $)
2922R<@> RELAY		$# RELAY
2923R$*			$: NO', `dnl')
2924
2925######################################################################
2926###  authinfo: lookup authinfo in the access map
2927###
2928###	Parameters:
2929###		$1: {server_name}
2930###		$2: {server_addr}
2931dnl	both are currently ignored
2932dnl if it should be done via another map, we either need to restrict
2933dnl functionality (it calls D and A) or copy those rulesets (or add another
2934dnl parameter which I want to avoid, it's quite complex already)
2935######################################################################
2936dnl omit this ruleset if neither is defined?
2937dnl it causes DefaultAuthInfo to be ignored
2938dnl (which may be considered a good thing).
2939Sauthinfo
2940ifdef(`_AUTHINFO_TABLE_', `dnl
2941R$*		$: <$(authinfo AuthInfo:$&{server_name} $: ? $)>
2942R<?>		$: <$(authinfo AuthInfo:$&{server_addr} $: ? $)>
2943R<?>		$: <$(authinfo AuthInfo: $: ? $)>
2944R<?>		$@ no				no authinfo available
2945R<$*>		$# $1
2946dnl', `dnl
2947ifdef(`_ACCESS_TABLE_', `dnl
2948R$*		$: $1 $| $>D <$&{server_name}> <?> <! AuthInfo> <>
2949R$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! AuthInfo> <>
2950R$* $| <?>$*	$: $1 $| <$(access AuthInfo`'_TAG_DELIM_ $: ? $)> <>
2951R$* $| <?>$*	$@ no				no authinfo available
2952R$* $| <$*> <>	$# $2
2953dnl', `dnl')')
2954
2955ifdef(`_RATE_CONTROL_',`dnl
2956######################################################################
2957###  RateControl: 
2958###	Parameters:	ignored
2959###	return: $#error or OK
2960######################################################################
2961SRateControl
2962ifdef(`_ACCESS_TABLE_', `dnl
2963R$*		$: <A:$&{client_addr}> <E:>
2964dnl also look up a default value via E:
2965R$+		$: $>SearchList <! ClientRate> $| $1 <>
2966dnl found nothing: stop here
2967R<?>		$@ OK
2968ifdef(`_ATMPF_', `dnl tempfail?
2969R<$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2970dnl use the generic routine (for now)
2971R<0>		$@ OK		no limit
2972R<$+>		$: <$1> $| $(arith l $@ $1 $@ $&{client_rate} $)
2973dnl log this? Connection rate $&{client_rate} exceeds limit $1.
2974R<$+> $| TRUE	$#error $@ 4.3.2 $: _RATE_CONTROL_REPLY Connection rate limit exceeded.
2975')')
2976
2977ifdef(`_CONN_CONTROL_',`dnl
2978######################################################################
2979###  ConnControl: 
2980###	Parameters:	ignored
2981###	return: $#error or OK
2982######################################################################
2983SConnControl
2984ifdef(`_ACCESS_TABLE_', `dnl
2985R$*		$: <A:$&{client_addr}> <E:>
2986dnl also look up a default value via E:
2987R$+		$: $>SearchList <! ClientConn> $| $1 <>
2988dnl found nothing: stop here
2989R<?>		$@ OK
2990ifdef(`_ATMPF_', `dnl tempfail?
2991R<$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2992dnl use the generic routine (for now)
2993R<0>		$@ OK		no limit
2994R<$+>		$: <$1> $| $(arith l $@ $1 $@ $&{client_connections} $)
2995dnl log this: Open connections $&{client_connections} exceeds limit $1.
2996R<$+> $| TRUE	$#error $@ 4.3.2 $: _CONN_CONTROL_REPLY Too many open connections.
2997')')
2998
2999undivert(9)dnl LOCAL_RULESETS
3000#
3001######################################################################
3002######################################################################
3003#####
3004`#####			MAIL FILTER DEFINITIONS'
3005#####
3006######################################################################
3007######################################################################
3008_MAIL_FILTERS_
3009#
3010######################################################################
3011######################################################################
3012#####
3013`#####			MAILER DEFINITIONS'
3014#####
3015######################################################################
3016######################################################################
3017undivert(7)dnl MAILER_DEFINITIONS
3018
3019