proto.m4 revision 112810
1divert(-1)
2#
3# Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
4#	All rights reserved.
5# Copyright (c) 1983, 1995 Eric P. Allman.  All rights reserved.
6# Copyright (c) 1988, 1993
7#	The Regents of the University of California.  All rights reserved.
8#
9# By using this file, you agree to the terms and conditions set
10# forth in the LICENSE file which can be found at the top level of
11# the sendmail distribution.
12#
13#
14divert(0)
15
16VERSIONID(`$Id: proto.m4,v 8.649.2.17 2003/03/28 17:20:53 ca Exp $')
17
18# level CF_LEVEL config file format
19V`'CF_LEVEL/ifdef(`VENDOR_NAME', `VENDOR_NAME', `Berkeley')
20divert(-1)
21
22dnl if MAILER(`local') not defined: do it ourself; be nice
23dnl maybe we should issue a warning?
24ifdef(`_MAILER_local_',`', `MAILER(local)')
25
26# do some sanity checking
27ifdef(`__OSTYPE__',,
28	`errprint(`*** ERROR: No system type defined (use OSTYPE macro)
29')')
30
31# pick our default mailers
32ifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `esmtp')')
33ifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')')
34ifdef(`confRELAY_MAILER',,
35	`define(`confRELAY_MAILER',
36		`ifdef(`_MAILER_smtp_', `relay',
37			`ifdef(`_MAILER_uucp', `uucp-new', `unknown')')')')
38ifdef(`confUUCP_MAILER',, `define(`confUUCP_MAILER', `uucp-old')')
39define(`_SMTP_', `confSMTP_MAILER')dnl		for readability only
40define(`_LOCAL_', `confLOCAL_MAILER')dnl	for readability only
41define(`_RELAY_', `confRELAY_MAILER')dnl	for readability only
42define(`_UUCP_', `confUUCP_MAILER')dnl		for readability only
43
44# back compatibility with old config files
45ifdef(`confDEF_GROUP_ID',
46`errprint(`*** confDEF_GROUP_ID is obsolete.
47    Use confDEF_USER_ID with a colon in the value instead.
48')')
49ifdef(`confREAD_TIMEOUT',
50`errprint(`*** confREAD_TIMEOUT is obsolete.
51    Use individual confTO_<timeout> parameters instead.
52')')
53ifdef(`confMESSAGE_TIMEOUT',
54	`define(`_ARG_', index(confMESSAGE_TIMEOUT, /))
55	 ifelse(_ARG_, -1,
56		`define(`confTO_QUEUERETURN', confMESSAGE_TIMEOUT)',
57		`define(`confTO_QUEUERETURN',
58			substr(confMESSAGE_TIMEOUT, 0, _ARG_))
59		 define(`confTO_QUEUEWARN',
60			substr(confMESSAGE_TIMEOUT, eval(_ARG_+1)))')')
61ifdef(`confMIN_FREE_BLOCKS', `ifelse(index(confMIN_FREE_BLOCKS, /), -1,,
62`errprint(`*** compound confMIN_FREE_BLOCKS is obsolete.
63    Use confMAX_MESSAGE_SIZE for the second part of the value.
64')')')
65
66
67# Sanity check on ldap_routing feature
68# If the user doesn't specify a new map, they better have given as a
69# default LDAP specification which has the LDAP base (and most likely the host)
70ifdef(`confLDAP_DEFAULT_SPEC',, `ifdef(`_LDAP_ROUTING_WARN_', `errprint(`
71WARNING: Using default FEATURE(ldap_routing) map definition(s)
72without setting confLDAP_DEFAULT_SPEC option.
73')')')dnl
74
75# clean option definitions below....
76define(`_OPTION', `ifdef(`$2', `O $1`'ifelse(defn(`$2'), `',, `=$2')', `#O $1`'ifelse(`$3', `',,`=$3')')')dnl
77
78dnl required to "rename" the check_* rulesets...
79define(`_U_',ifdef(`_DELAY_CHECKS_',`',`_'))
80dnl default relaying denied message
81ifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG',
82ifdef(`_USE_AUTH_', `"550 Relaying denied. Proper authentication required."', `"550 Relaying denied"'))')
83ifdef(`confRCPTREJ_MSG', `', `define(`confRCPTREJ_MSG', `"550 Mailbox disabled for this recipient"')')
84define(`_CODE553', `553')
85divert(0)dnl
86
87# override file safeties - setting this option compromises system security,
88# addressing the actual file configuration problem is preferred
89# need to set this before any file actions are encountered in the cf file
90_OPTION(DontBlameSendmail, `confDONT_BLAME_SENDMAIL', `safe')
91
92# default LDAP map specification
93# need to set this now before any LDAP maps are defined
94_OPTION(LDAPDefaultSpec, `confLDAP_DEFAULT_SPEC', `-h localhost')
95
96##################
97#   local info   #
98##################
99
100# my LDAP cluster
101# need to set this before any LDAP lookups are done (including classes)
102ifdef(`confLDAP_CLUSTER', `D{sendmailMTACluster}`'confLDAP_CLUSTER', `#D{sendmailMTACluster}$m')
103
104Cwlocalhost
105ifdef(`USE_CW_FILE',
106`# file containing names of hosts for which we receive email
107Fw`'confCW_FILE',
108	`dnl')
109
110# my official domain name
111# ... `define' this only if sendmail cannot automatically determine your domain
112ifdef(`confDOMAIN_NAME', `Dj`'confDOMAIN_NAME', `#Dj$w.Foo.COM')
113
114CP.
115
116ifdef(`UUCP_RELAY',
117`# UUCP relay host
118DY`'UUCP_RELAY
119CPUUCP
120
121')dnl
122ifdef(`BITNET_RELAY',
123`#  BITNET relay host
124DB`'BITNET_RELAY
125CPBITNET
126
127')dnl
128ifdef(`DECNET_RELAY',
129`define(`_USE_DECNET_SYNTAX_', 1)dnl
130# DECnet relay host
131DC`'DECNET_RELAY
132CPDECNET
133
134')dnl
135ifdef(`FAX_RELAY',
136`# FAX relay host
137DF`'FAX_RELAY
138CPFAX
139
140')dnl
141# "Smart" relay host (may be null)
142DS`'ifdef(`SMART_HOST', `SMART_HOST')
143
144ifdef(`LUSER_RELAY', `dnl
145# place to which unknown users should be forwarded
146Kuser user -m -a<>
147DL`'LUSER_RELAY',
148`dnl')
149
150# operators that cannot be in local usernames (i.e., network indicators)
151CO @ % ifdef(`_NO_UUCP_', `', `!')
152
153# a class with just dot (for identifying canonical names)
154C..
155
156# a class with just a left bracket (for identifying domain literals)
157C[[
158
159ifdef(`_ACCESS_TABLE_', `dnl
160# access_db acceptance class
161C{Accept}OK RELAY
162ifdef(`_DELAY_COMPAT_8_10_',`dnl
163ifdef(`_BLACKLIST_RCPT_',`dnl
164# possible access_db RHS for spam friends/haters
165C{SpamTag}SPAMFRIEND SPAMHATER')')',
166`dnl')
167
168dnl mark for "domain is ok" (resolved or accepted anyway)
169define(`_RES_OK_', `OKR')dnl
170ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',`dnl',`dnl
171# Resolve map (to check if a host exists in check_mail)
172Kresolve host -a<_RES_OK_> -T<TEMP>')
173C{ResOk}_RES_OK_
174
175ifdef(`_NEED_MACRO_MAP_', `dnl
176ifdef(`_MACRO_MAP_', `', `# macro storage map
177define(`_MACRO_MAP_', `1')dnl
178Kmacro macro')', `dnl')
179
180ifdef(`confCR_FILE', `dnl
181# Hosts for which relaying is permitted ($=R)
182FR`'confCR_FILE',
183`dnl')
184
185define(`TLS_SRV_TAG', `"TLS_Srv"')dnl
186define(`TLS_CLT_TAG', `"TLS_Clt"')dnl
187define(`TLS_RCPT_TAG', `"TLS_Rcpt"')dnl
188define(`TLS_TRY_TAG', `"Try_TLS"')dnl
189define(`SRV_FEAT_TAG', `"Srv_Features"')dnl
190dnl this may be useful in other contexts too
191ifdef(`_ARITH_MAP_', `', `# arithmetic map
192define(`_ARITH_MAP_', `1')dnl
193Karith arith')
194ifdef(`_ACCESS_TABLE_', `dnl
195ifdef(`_MACRO_MAP_', `', `# macro storage map
196define(`_MACRO_MAP_', `1')dnl
197Kmacro macro')
198# possible values for TLS_connection in access map
199C{tls}VERIFY ENCR', `dnl')
200ifdef(`_CERT_REGEX_ISSUER_', `dnl
201# extract relevant part from cert issuer
202KCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
203ifdef(`_CERT_REGEX_SUBJECT_', `dnl
204# extract relevant part from cert subject
205KCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
206
207ifdef(`LOCAL_RELAY', `dnl
208# who I send unqualified names to if `FEATURE(stickyhost)' is used
209# (null means deliver locally)
210DR`'LOCAL_RELAY')
211
212ifdef(`MAIL_HUB', `dnl
213# who gets all local email traffic
214# ($R has precedence for unqualified names if `FEATURE(stickyhost)' is used)
215DH`'MAIL_HUB')
216
217# dequoting map
218Kdequote dequote`'ifdef(`confDEQUOTE_OPTS', ` confDEQUOTE_OPTS', `')
219
220divert(0)dnl	# end of nullclient diversion
221# class E: names that should be exposed as from this host, even if we masquerade
222# class L: names that should be delivered locally, even if we have a relay
223# class M: domains that should be converted to $M
224# class N: domains that should not be converted to $M
225#CL root
226undivert(5)dnl
227ifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl')
228
229ifdef(`MASQUERADE_NAME', `dnl
230# who I masquerade as (null for no masquerading) (see also $=M)
231DM`'MASQUERADE_NAME')
232
233# my name for error messages
234ifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
235
236undivert(6)dnl LOCAL_CONFIG
237include(_CF_DIR_`m4/version.m4')
238
239###############
240#   Options   #
241###############
242ifdef(`confAUTO_REBUILD',
243`errprint(WARNING: `confAUTO_REBUILD' is no longer valid.
244	There was a potential for a denial of service attack if this is set.
245)')dnl
246
247# strip message body to 7 bits on input?
248_OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
249
250# 8-bit data handling
251_OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `pass8')
252
253# wait for alias file rebuild (default units: minutes)
254_OPTION(AliasWait, `confALIAS_WAIT', `5m')
255
256# location of alias file
257_OPTION(AliasFile, `ALIAS_FILE', `MAIL_SETTINGS_DIR`'aliases')
258
259# minimum number of free blocks on filesystem
260_OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', `100')
261
262# maximum message size
263_OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', `1000000')
264
265# substitution for space (blank) characters
266_OPTION(BlankSub, `confBLANK_SUB', `_')
267
268# avoid connecting to "expensive" mailers on initial submission?
269_OPTION(HoldExpensive, `confCON_EXPENSIVE', `False')
270
271# checkpoint queue runs after every N successful deliveries
272_OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10')
273
274# default delivery mode
275_OPTION(DeliveryMode, `confDELIVERY_MODE', `background')
276
277# error message header/file
278_OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header')
279
280# error mode
281_OPTION(ErrorMode, `confERROR_MODE', `print')
282
283# save Unix-style "From_" lines at top of header?
284_OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False')
285
286# queue file mode (qf files)
287_OPTION(QueueFileMode, `confQUEUE_FILE_MODE', `0600')
288
289# temporary file mode
290_OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600')
291
292# match recipients against GECOS field?
293_OPTION(MatchGECOS, `confMATCH_GECOS', `False')
294
295# maximum hop count
296_OPTION(MaxHopCount, `confMAX_HOP', `25')
297
298# location of help file
299O HelpFile=ifdef(`HELP_FILE', HELP_FILE, `MAIL_SETTINGS_DIR`'helpfile')
300
301# ignore dots as terminators in incoming messages?
302_OPTION(IgnoreDots, `confIGNORE_DOTS', `False')
303
304# name resolver options
305_OPTION(ResolverOptions, `confBIND_OPTS', `+AAONLY')
306
307# deliver MIME-encapsulated error messages?
308_OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS', `True')
309
310# Forward file search path
311_OPTION(ForwardPath, `confFORWARD_PATH', `/var/forward/$u:$z/.forward.$w:$z/.forward')
312
313# open connection cache size
314_OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', `2')
315
316# open connection cache timeout
317_OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', `5m')
318
319# persistent host status directory
320_OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', `.hoststat')
321
322# single thread deliveries (requires HostStatusDirectory)?
323_OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY', `False')
324
325# use Errors-To: header?
326_OPTION(UseErrorsTo, `confUSE_ERRORS_TO', `False')
327
328# log level
329_OPTION(LogLevel, `confLOG_LEVEL', `10')
330
331# send to me too, even in an alias expansion?
332_OPTION(MeToo, `confME_TOO', `True')
333
334# verify RHS in newaliases?
335_OPTION(CheckAliases, `confCHECK_ALIASES', `False')
336
337# default messages to old style headers if no special punctuation?
338_OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS', `False')
339
340# SMTP daemon options
341ifelse(defn(`confDAEMON_OPTIONS'), `', `dnl',
342`errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.
343	Use `DAEMON_OPTIONS()'; see cf/README.
344)'dnl
345`DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
346ifelse(defn(`_DPO_'), `',
347`ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-v4, Family=inet
348O DaemonPortOptions=Name=MTA-v6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_')
349ifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
350
351# SMTP client options
352ifelse(defn(`confCLIENT_OPTIONS'), `', `dnl',
353`errprint(WARNING: `confCLIENT_OPTIONS' is no longer valid.  See cf/README for more information.
354)'dnl
355`CLIENT_OPTIONS(`confCLIENT_OPTIONS')')
356ifelse(defn(`_CPO_'), `',
357`#O ClientPortOptions=Family=inet, Address=0.0.0.0', `_CPO_')
358
359# Modifiers to `define' {daemon_flags} for direct submissions
360_OPTION(DirectSubmissionModifiers, `confDIRECT_SUBMISSION_MODIFIERS', `')
361
362# Use as mail submission program? See sendmail/SECURITY
363_OPTION(UseMSP, `confUSE_MSP', `')
364
365# privacy flags
366_OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings')
367
368# who (if anyone) should get extra copies of error messages
369_OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster')
370
371# slope of queue-only function
372_OPTION(QueueFactor, `confQUEUE_FACTOR', `600000')
373
374# limit on number of concurrent queue runners
375_OPTION(MaxQueueChildren, `confMAX_QUEUE_CHILDREN', `')
376
377# maximum number of queue-runners per queue-grouping with multiple queues
378_OPTION(MaxRunnersPerQueue, `confMAX_RUNNERS_PER_QUEUE', `1')
379
380# priority of queue runners (nice(3))
381_OPTION(NiceQueueRun, `confNICE_QUEUE_RUN', `')
382
383# shall we sort the queue by hostname first?
384_OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority')
385
386# minimum time in queue before retry
387_OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m')
388
389# how many jobs can you process in the queue?
390_OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `10000')
391
392# perform initial split of envelope without checking MX records
393_OPTION(FastSplit, `confFAST_SPLIT', `1')
394
395# queue directory
396O QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue')
397
398# key for shared memory; 0 to turn off
399_OPTION(SharedMemoryKey, `confSHARED_MEMORY_KEY', `0')
400
401ifdef(`confSHARED_MEMORY_KEY_FILE', `dnl
402# file to store key for shared memory (if SharedMemoryKey = -1)
403O SharedMemoryKeyFile=confSHARED_MEMORY_KEY_FILE')
404
405# timeouts (many of these)
406_OPTION(Timeout.initial, `confTO_INITIAL', `5m')
407_OPTION(Timeout.connect, `confTO_CONNECT', `5m')
408_OPTION(Timeout.aconnect, `confTO_ACONNECT', `0s')
409_OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m')
410_OPTION(Timeout.helo, `confTO_HELO', `5m')
411_OPTION(Timeout.mail, `confTO_MAIL', `10m')
412_OPTION(Timeout.rcpt, `confTO_RCPT', `1h')
413_OPTION(Timeout.datainit, `confTO_DATAINIT', `5m')
414_OPTION(Timeout.datablock, `confTO_DATABLOCK', `1h')
415_OPTION(Timeout.datafinal, `confTO_DATAFINAL', `1h')
416_OPTION(Timeout.rset, `confTO_RSET', `5m')
417_OPTION(Timeout.quit, `confTO_QUIT', `2m')
418_OPTION(Timeout.misc, `confTO_MISC', `2m')
419_OPTION(Timeout.command, `confTO_COMMAND', `1h')
420_OPTION(Timeout.ident, `confTO_IDENT', `5s')
421_OPTION(Timeout.fileopen, `confTO_FILEOPEN', `60s')
422_OPTION(Timeout.control, `confTO_CONTROL', `2m')
423_OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', `5d')
424_OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', `5d')
425_OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', `2d')
426_OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', `7d')
427ifdef(`confTO_QUEUERETURN_DSN', `dnl
428O Timeout.queuereturn.dsn=confTO_QUEUERETURN_DSN')
429_OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', `4h')
430_OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', `4h')
431_OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', `1h')
432_OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', `12h')
433ifdef(`confTO_QUEUEWARN_DSN', `dnl
434O Timeout.queuewarn.dsn=confTO_QUEUEWARN_DSN')
435_OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', `30m')
436_OPTION(Timeout.resolver.retrans, `confTO_RESOLVER_RETRANS', `5s')
437_OPTION(Timeout.resolver.retrans.first, `confTO_RESOLVER_RETRANS_FIRST', `5s')
438_OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s')
439_OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4')
440_OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4')
441_OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4')
442_OPTION(Timeout.lhlo, `confTO_LHLO', `2m')
443_OPTION(Timeout.auth, `confTO_AUTH', `10m')
444_OPTION(Timeout.starttls, `confTO_STARTTLS', `1h')
445
446# time for DeliverBy; extension disabled if less than 0
447_OPTION(DeliverByMin, `confDELIVER_BY_MIN', `0')
448
449# should we not prune routes in route-addr syntax addresses?
450_OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False')
451
452# queue up everything before forking?
453_OPTION(SuperSafe, `confSAFE_QUEUE', `True')
454
455# status file
456O StatusFile=ifdef(`STATUS_FILE', `STATUS_FILE', `MAIL_SETTINGS_DIR`'statistics')
457
458# time zone handling:
459#  if undefined, use system default
460#  if defined but null, use TZ envariable passed in
461#  if defined and non-null, use that info
462ifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
463	confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
464	`O TimeZoneSpec=confTIME_ZONE')
465
466# default UID (can be username or userid:groupid)
467_OPTION(DefaultUser, `confDEF_USER_ID', `mailnull')
468
469# list of locations of user database file (null means no lookup)
470_OPTION(UserDatabaseSpec, `confUSERDB_SPEC', `MAIL_SETTINGS_DIR`'userdb')
471
472# fallback MX host
473_OPTION(FallbackMXhost, `confFALLBACK_MX', `fall.back.host.net')
474
475# if we are the best MX host for a site, try it directly instead of config err
476_OPTION(TryNullMXList, `confTRY_NULL_MX_LIST', `False')
477
478# load average at which we just queue messages
479_OPTION(QueueLA, `confQUEUE_LA', `8')
480
481# load average at which we refuse connections
482_OPTION(RefuseLA, `confREFUSE_LA', `12')
483
484# load average at which we delay connections; 0 means no limit
485_OPTION(DelayLA, `confDELAY_LA', `0')
486
487# maximum number of children we allow at one time
488_OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `0')
489
490# maximum number of new connections per second
491_OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0')
492
493# work recipient factor
494_OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
495
496# deliver each queued job in a separate process?
497_OPTION(ForkEachJob, `confSEPARATE_PROC', `False')
498
499# work class factor
500_OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800')
501
502# work time factor
503_OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000')
504
505# default character set
506_OPTION(DefaultCharSet, `confDEF_CHAR_SET', `iso-8859-1')
507
508# service switch file (name hardwired on Solaris, Ultrix, OSF/1, others)
509_OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch')
510
511# hosts file (normally /etc/hosts)
512_OPTION(HostsFile, `confHOSTS_FILE', `/etc/hosts')
513
514# dialup line delay on connection failure
515_OPTION(DialDelay, `confDIAL_DELAY', `10s')
516
517# action to take if there are no recipients in the message
518_OPTION(NoRecipientAction, `confNO_RCPT_ACTION', `add-to-undisclosed')
519
520# chrooted environment for writing to files
521_OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `/arch')
522
523# are colons OK in addresses?
524_OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True')
525
526# shall I avoid expanding CNAMEs (violates protocols)?
527_OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False')
528
529# SMTP initial login message (old $e macro)
530_OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG', `$j Sendmail $v ready at $b')
531
532# UNIX initial From header format (old $l macro)
533_OPTION(UnixFromLine, `confFROM_LINE', `From $g $d')
534
535# From: lines that have embedded newlines are unwrapped onto one line
536_OPTION(SingleLineFromHeader, `confSINGLE_LINE_FROM_HEADER', `False')
537
538# Allow HELO SMTP command that does not `include' a host name
539_OPTION(AllowBogusHELO, `confALLOW_BOGUS_HELO', `False')
540
541# Characters to be quoted in a full name phrase (@,;:\()[] are automatic)
542_OPTION(MustQuoteChars, `confMUST_QUOTE_CHARS', `.')
543
544# delimiter (operator) characters (old $o macro)
545_OPTION(OperatorChars, `confOPERATORS', `.:@[]')
546
547# shall I avoid calling initgroups(3) because of high NIS costs?
548_OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False')
549
550# are group-writable `:include:' and .forward files (un)trustworthy?
551# True (the default) means they are not trustworthy.
552_OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True')
553ifdef(`confUNSAFE_GROUP_WRITES',
554`errprint(`WARNING: confUNSAFE_GROUP_WRITES is deprecated; use confDONT_BLAME_SENDMAIL.
555')')
556
557# where do errors that occur when sending errors get sent?
558_OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster')
559
560# where to save bounces if all else fails
561_OPTION(DeadLetterDrop, `confDEAD_LETTER_DROP', `/var/tmp/dead.letter')
562
563# what user id do we assume for the majority of the processing?
564_OPTION(RunAsUser, `confRUN_AS_USER', `sendmail')
565
566# maximum number of recipients per SMTP envelope
567_OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `100')
568
569# limit the rate recipients per SMTP envelope are accepted
570# once the threshold number of recipients have been rejected
571_OPTION(BadRcptThrottle, `confBAD_RCPT_THROTTLE', `20')
572
573# shall we get local names from our installed interfaces?
574_OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False')
575
576# Return-Receipt-To: header implies DSN request
577_OPTION(RrtImpliesDsn, `confRRT_IMPLIES_DSN', `False')
578
579# override connection address (for testing)
580_OPTION(ConnectOnlyTo, `confCONNECT_ONLY_TO', `0.0.0.0')
581
582# Trusted user for file ownership and starting the daemon
583_OPTION(TrustedUser, `confTRUSTED_USER', `root')
584
585# Control socket for daemon management
586_OPTION(ControlSocketName, `confCONTROL_SOCKET_NAME', `/var/spool/mqueue/.control')
587
588# Maximum MIME header length to protect MUAs
589_OPTION(MaxMimeHeaderLength, `confMAX_MIME_HEADER_LENGTH', `2048/1024')
590
591# Maximum length of the sum of all headers
592_OPTION(MaxHeadersLength, `confMAX_HEADERS_LENGTH', `32768')
593
594# Maximum depth of alias recursion
595_OPTION(MaxAliasRecursion, `confMAX_ALIAS_RECURSION', `10')
596
597# location of pid file
598_OPTION(PidFile, `confPID_FILE', `/var/run/sendmail.pid')
599
600# Prefix string for the process title shown on 'ps' listings
601_OPTION(ProcessTitlePrefix, `confPROCESS_TITLE_PREFIX', `prefix')
602
603# Data file (df) memory-buffer file maximum size
604_OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096')
605
606# Transcript file (xf) memory-buffer file maximum size
607_OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096')
608
609# lookup type to find information about local mailboxes
610_OPTION(MailboxDatabase, `confMAILBOX_DATABASE', `pw')
611
612# list of authentication mechanisms
613_OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5')
614
615# default authentication information for outgoing connections
616_OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info')
617
618# SMTP AUTH flags
619_OPTION(AuthOptions, `confAUTH_OPTIONS', `')
620
621# SMTP AUTH maximum encryption strength
622_OPTION(AuthMaxBits, `confAUTH_MAX_BITS', `')
623
624# SMTP STARTTLS server options
625_OPTION(TLSSrvOptions, `confTLS_SRV_OPTIONS', `')
626
627# Input mail filters
628_OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
629
630ifelse(len(X`'_MAIL_FILTERS_DEF), `1', `dnl', `dnl
631# Milter options
632_OPTION(Milter.LogLevel, `confMILTER_LOG_LEVEL', `')
633_OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `')
634_OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `')
635_OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
636_OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')')
637
638# CA directory
639_OPTION(CACertPath, `confCACERT_PATH', `')
640# CA file
641_OPTION(CACertFile, `confCACERT', `')
642# Server Cert
643_OPTION(ServerCertFile, `confSERVER_CERT', `')
644# Server private key
645_OPTION(ServerKeyFile, `confSERVER_KEY', `')
646# Client Cert
647_OPTION(ClientCertFile, `confCLIENT_CERT', `')
648# Client private key
649_OPTION(ClientKeyFile, `confCLIENT_KEY', `')
650# DHParameters (only required if DSA/DH is used)
651_OPTION(DHParameters, `confDH_PARAMETERS', `')
652# Random data source (required for systems without /dev/urandom under OpenSSL)
653_OPTION(RandFile, `confRAND_FILE', `')
654
655############################
656`# QUEUE GROUP DEFINITIONS  #'
657############################
658_QUEUE_GROUP_
659
660###########################
661#   Message precedences   #
662###########################
663
664Pfirst-class=0
665Pspecial-delivery=100
666Plist=-30
667Pbulk=-60
668Pjunk=-100
669
670#####################
671#   Trusted users   #
672#####################
673
674# this is equivalent to setting class "t"
675ifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `MAIL_SETTINGS_DIR`'trusted-users')
676Troot
677Tdaemon
678ifdef(`_NO_UUCP_', `dnl', `Tuucp')
679ifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
680
681#########################
682#   Format of headers   #
683#########################
684
685ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
686H?P?Return-Path: <$g>
687HReceived: confRECEIVED_HEADER
688H?D?Resent-Date: $a
689H?D?Date: $a
690H?F?Resent-From: confFROM_HEADER
691H?F?From: confFROM_HEADER
692H?x?Full-Name: $x
693# HPosted-Date: $a
694# H?l?Received-Date: $b
695H?M?Resent-Message-Id: <$t.$i@$j>
696H?M?Message-Id: <$t.$i@$j>
697
698#
699######################################################################
700######################################################################
701#####
702#####			REWRITING RULES
703#####
704######################################################################
705######################################################################
706
707############################################
708###  Ruleset 3 -- Name Canonicalization  ###
709############################################
710Scanonify=3
711
712# handle null input (translate to <@> special case)
713R$@			$@ <@>
714
715# strip group: syntax (not inside angle brackets!) and trailing semicolon
716R$*			$: $1 <@>			mark addresses
717R$* < $* > $* <@>	$: $1 < $2 > $3			unmark <addr>
718R@ $* <@>		$: @ $1				unmark @host:...
719R$* [ IPv6 : $+ ] <@>	$: $1 [ IPv6 : $2 ]		unmark IPv6 addr
720R$* :: $* <@>		$: $1 :: $2			unmark node::addr
721R:`include': $* <@>	$: :`include': $1			unmark :`include':...
722R$* : $* [ $* ]		$: $1 : $2 [ $3 ] <@>		remark if leading colon
723R$* : $* <@>		$: $2				strip colon if marked
724R$* <@>			$: $1				unmark
725R$* ;			   $1				strip trailing semi
726R$* < $+ :; > $*	$@ $2 :; <@>			catch <list:;>
727R$* < $* ; >		   $1 < $2 >			bogus bracketed semi
728
729# null input now results from list:; syntax
730R$@			$@ :; <@>
731
732# strip angle brackets -- note RFC733 heuristic to get innermost item
733R$*			$: < $1 >			housekeeping <>
734R$+ < $* >		   < $2 >			strip excess on left
735R< $* > $+		   < $1 >			strip excess on right
736R<>			$@ < @ >			MAIL FROM:<> case
737R< $+ >			$: $1				remove housekeeping <>
738
739ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
740# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
741R@ $+ , $+		@ $1 : $2			change all "," to ":"
742
743# localize and dispose of route-based addresses
744dnl XXX: IPv6 colon conflict
745ifdef(`NO_NETINET6', `dnl',
746`R@ [$+] : $+		$@ $>Canonify2 < @ [$1] > : $2	handle <route-addr>')
747R@ $+ : $+		$@ $>Canonify2 < @$1 > : $2	handle <route-addr>
748dnl',`dnl
749# strip route address <@a,@b,@c:user@d> -> <user@d>
750R@ $+ , $+		$2
751ifdef(`NO_NETINET6', `dnl',
752`R@ [ $* ] : $+		$2')
753R@ $+ : $+		$2
754dnl')
755
756# find focus for list syntax
757R $+ : $* ; @ $+	$@ $>Canonify2 $1 : $2 ; < @ $3 >	list syntax
758R $+ : $* ;		$@ $1 : $2;			list syntax
759
760# find focus for @ syntax addresses
761R$+ @ $+		$: $1 < @ $2 >			focus on domain
762R$+ < $+ @ $+ >		$1 $2 < @ $3 >			move gaze right
763R$+ < @ $+ >		$@ $>Canonify2 $1 < @ $2 >	already canonical
764
765dnl This is flagged as an error in S0; no need to silently fix it here.
766dnl # do some sanity checking
767dnl R$* < @ $~[ $* : $* > $*	$1 < @ $2 $3 > $4	nix colons in addrs
768
769ifdef(`_NO_UUCP_', `dnl',
770`# convert old-style addresses to a domain-based address
771R$- ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	resolve uucp names
772R$+ . $- ! $+		$@ $>Canonify2 $3 < @ $1 . $2 >		domain uucps
773R$+ ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	uucp subdomains
774')
775ifdef(`_USE_DECNET_SYNTAX_',
776`# convert node::user addresses into a domain-based address
777R$- :: $+		$@ $>Canonify2 $2 < @ $1 .DECNET >	resolve DECnet names
778R$- . $- :: $+		$@ $>Canonify2 $3 < @ $1.$2 .DECNET >	numeric DECnet addr
779',
780	`dnl')
781# if we have % signs, take the rightmost one
782R$* % $*		$1 @ $2				First make them all @s.
783R$* @ $* @ $*		$1 % $2 @ $3			Undo all but the last.
784R$* @ $*		$@ $>Canonify2 $1 < @ $2 >	Insert < > and finish
785
786# else we must be a local name
787R$*			$@ $>Canonify2 $1
788
789
790################################################
791###  Ruleset 96 -- bottom half of ruleset 3  ###
792################################################
793
794SCanonify2=96
795
796# handle special cases for local names
797R$* < @ localhost > $*		$: $1 < @ $j . > $2		no domain at all
798R$* < @ localhost . $m > $*	$: $1 < @ $j . > $2		local domain
799ifdef(`_NO_UUCP_', `dnl',
800`R$* < @ localhost . UUCP > $*	$: $1 < @ $j . > $2		.UUCP domain')
801
802# check for IPv4/IPv6 domain literal
803R$* < @ [ $+ ] > $*		$: $1 < @@ [ $2 ] > $3		mark [addr]
804R$* < @@ $=w > $*		$: $1 < @ $j . > $3		self-literal
805R$* < @@ $+ > $*		$@ $1 < @ $2 > $3		canon IP addr
806
807ifdef(`_DOMAIN_TABLE_', `dnl
808# look up domains in the domain table
809R$* < @ $+ > $* 		$: $1 < @ $(domaintable $2 $) > $3', `dnl')
810
811undivert(2)dnl LOCAL_RULE_3
812
813ifdef(`_BITDOMAIN_TABLE_', `dnl
814# handle BITNET mapping
815R$* < @ $+ .BITNET > $*		$: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3', `dnl')
816
817ifdef(`_UUDOMAIN_TABLE_', `dnl
818# handle UUCP mapping
819R$* < @ $+ .UUCP > $*		$: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3', `dnl')
820
821ifdef(`_NO_UUCP_', `dnl',
822`ifdef(`UUCP_RELAY',
823`# pass UUCP addresses straight through
824R$* < @ $+ . UUCP > $*		$@ $1 < @ $2 . UUCP . > $3',
825`# if really UUCP, handle it immediately
826ifdef(`_CLASS_U_',
827`R$* < @ $=U . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
828ifdef(`_CLASS_V_',
829`R$* < @ $=V . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
830ifdef(`_CLASS_W_',
831`R$* < @ $=W . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
832ifdef(`_CLASS_X_',
833`R$* < @ $=X . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
834ifdef(`_CLASS_Y_',
835`R$* < @ $=Y . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
836
837ifdef(`_NO_CANONIFY_', `dnl', `dnl
838# try UUCP traffic as a local address
839R$* < @ $+ . UUCP > $*		$: $1 < @ $[ $2 $] . UUCP . > $3
840R$* < @ $+ . . UUCP . > $*	$@ $1 < @ $2 . > $3')
841')')
842# hostnames ending in class P are always canonical
843R$* < @ $* $=P > $*		$: $1 < @ $2 $3 . > $4
844dnl apply the next rule only for hostnames not in class P
845dnl this even works for phrases in class P since . is in class P
846dnl which daemon flags are set?
847R$* < @ $* $~P > $*		$: $&{daemon_flags} $| $1 < @ $2 $3 > $4
848dnl the other rules in this section only apply if the hostname
849dnl does not end in class P hence no further checks are done here
850dnl if this ever changes make sure the lookups are "protected" again!
851ifdef(`_NO_CANONIFY_', `dnl
852dnl do not canonify unless:
853dnl domain ends in class {Canonify} (this does not work if the intersection
854dnl	with class P is non-empty)
855dnl or {daemon_flags} has c set
856# pass to name server to make hostname canonical if in class {Canonify}
857R$* $| $* < @ $* $={Canonify} > $*	$: $2 < @ $[ $3 $4 $] > $5
858# pass to name server to make hostname canonical if requested
859R$* c $* $| $* < @ $* > $*	$: $3 < @ $[ $4 $] > $5
860dnl trailing dot? -> do not apply _CANONIFY_HOSTS_
861R$* $| $* < @ $+ . > $*		$: $2 < @ $3 . > $4
862# add a trailing dot to qualified hostnames so other rules will work
863R$* $| $* < @ $+.$+ > $*	$: $2 < @ $3.$4 . > $5
864ifdef(`_CANONIFY_HOSTS_', `dnl
865dnl this should only apply to unqualified hostnames
866dnl but if a valid character inside an unqualified hostname is an OperatorChar
867dnl then $- does not work.
868# lookup unqualified hostnames
869R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4', `dnl')', `dnl
870dnl _NO_CANONIFY_ is not set: canonify unless:
871dnl {daemon_flags} contains CC (do not canonify)
872dnl but add a trailing dot to qualified hostnames so other rules will work
873dnl should we do this for every hostname: even unqualified?
874R$* CC $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
875R$* CC $* $| $*			$: $3
876ifdef(`_FFR_NOCANONIFY_HEADERS', `dnl
877# do not canonify header addresses
878R$* $| $* < @ $* $~P > $*	$: $&{addr_type} $| $2 < @ $3 $4 > $5
879R$* h $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
880R$* h $* $| $*			$: $3', `dnl')
881# pass to name server to make hostname canonical
882R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4')
883dnl remove {daemon_flags} for other cases
884R$* $| $*			$: $2
885
886# local host aliases and pseudo-domains are always canonical
887R$* < @ $=w > $*		$: $1 < @ $2 . > $3
888ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
889`R$* < @ $* $=M > $*		$: $1 < @ $2 $3 . > $4',
890`R$* < @ $=M > $*		$: $1 < @ $2 . > $3')
891ifdef(`_VIRTUSER_TABLE_', `dnl
892dnl virtual hosts are also canonical
893ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
894`R$* < @ $* $={VirtHost} > $* 	$: $1 < @ $2 $3 . > $4',
895`R$* < @ $={VirtHost} > $* 	$: $1 < @ $2 . > $3')',
896`dnl')
897ifdef(`_GENERICS_TABLE_', `dnl
898dnl hosts for genericstable are also canonical
899ifdef(`_GENERICS_ENTIRE_DOMAIN_',
900`R$* < @ $* $=G > $* 	$: $1 < @ $2 $3 . > $4',
901`R$* < @ $=G > $* 	$: $1 < @ $2 . > $3')',
902`dnl')
903dnl remove superfluous dots (maybe repeatedly) which may have been added
904dnl by one of the rules before
905R$* < @ $* . . > $*		$1 < @ $2 . > $3
906
907
908##################################################
909###  Ruleset 4 -- Final Output Post-rewriting  ###
910##################################################
911Sfinal=4
912
913R$+ :; <@>		$@ $1 :				handle <list:;>
914R$* <@>			$@				handle <> and list:;
915
916# strip trailing dot off possibly canonical name
917R$* < @ $+ . > $*	$1 < @ $2 > $3
918
919# eliminate internal code
920R$* < @ *LOCAL* > $*	$1 < @ $j > $2
921
922# externalize local domain info
923R$* < $+ > $*		$1 $2 $3			defocus
924R@ $+ : @ $+ : $+	@ $1 , @ $2 : $3		<route-addr> canonical
925R@ $*			$@ @ $1				... and exit
926
927ifdef(`_NO_UUCP_', `dnl',
928`# UUCP must always be presented in old form
929R$+ @ $- . UUCP		$2!$1				u@h.UUCP => h!u')
930
931ifdef(`_USE_DECNET_SYNTAX_',
932`# put DECnet back in :: form
933R$+ @ $+ . DECNET	$2 :: $1			u@h.DECNET => h::u',
934	`dnl')
935# delete duplicate local names
936R$+ % $=w @ $=w		$1 @ $2				u%host@host => u@host
937
938
939
940##############################################################
941###   Ruleset 97 -- recanonicalize and call ruleset zero   ###
942###		   (used for recursive calls)		   ###
943##############################################################
944
945SRecurse=97
946R$*			$: $>canonify $1
947R$*			$@ $>parse $1
948
949
950######################################
951###   Ruleset 0 -- Parse Address   ###
952######################################
953
954Sparse=0
955
956R$*			$: $>Parse0 $1		initial parsing
957R<@>			$#_LOCAL_ $: <@>		special case error msgs
958R$*			$: $>ParseLocal $1	handle local hacks
959R$*			$: $>Parse1 $1		final parsing
960
961#
962#  Parse0 -- do initial syntax checking and eliminate local addresses.
963#	This should either return with the (possibly modified) input
964#	or return with a #error mailer.  It should not return with a
965#	#mailer other than the #error mailer.
966#
967
968SParse0
969R<@>			$@ <@>			special case error msgs
970R$* : $* ; <@>		$#error $@ 5.1.3 $: "_CODE553 List:; syntax illegal for recipient addresses"
971R@ <@ $* >		< @ $1 >		catch "@@host" bogosity
972R<@ $+>			$#error $@ 5.1.3 $: "_CODE553 User address required"
973R$+ <@>			$#error $@ 5.1.3 $: "_CODE553 Hostname required"
974R$*			$: <> $1
975dnl allow tricks like [host1]:[host2]
976R<> $* < @ [ $* ] : $+ > $*	$1 < @ [ $2 ] : $3 > $4
977R<> $* < @ [ $* ] , $+ > $*	$1 < @ [ $2 ] , $3 > $4
978dnl but no a@[b]c
979R<> $* < @ [ $* ] $+ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid address"
980R<> $* < @ [ $+ ] > $*		$1 < @ [ $2 ] > $3
981R<> $* <$* : $* > $*	$#error $@ 5.1.3 $: "_CODE553 Colon illegal in host name part"
982R<> $*			$1
983R$* < @ . $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
984R$* < @ $* .. $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
985dnl no a@b@
986R$* < @ $* @ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid route address"
987dnl no a@b@c
988R$* @ $* < @ $* > $*	$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
989dnl comma only allowed before @; this check is not complete
990R$* , $~O $*		$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
991
992ifdef(`_STRICT_RFC821_', `# more RFC 821 checks
993R$* . < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not end with a dot"
994R. $* < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not begin with a dot"
995dnl', `dnl')
996
997# now delete the local info -- note $=O to find characters that cause forwarding
998R$* < @ > $*		$@ $>Parse0 $>canonify $1	user@ => user
999R< @ $=w . > : $*	$@ $>Parse0 $>canonify $2	@here:... -> ...
1000R$- < @ $=w . >		$: $(dequote $1 $) < @ $2 . >	dequote "foo"@here
1001R< @ $+ >		$#error $@ 5.1.3 $: "_CODE553 User address required"
1002R$* $=O $* < @ $=w . >	$@ $>Parse0 $>canonify $1 $2 $3	...@here -> ...
1003R$- 			$: $(dequote $1 $) < @ *LOCAL* >	dequote "foo"
1004R< @ *LOCAL* >		$#error $@ 5.1.3 $: "_CODE553 User address required"
1005R$* $=O $* < @ *LOCAL* >
1006			$@ $>Parse0 $>canonify $1 $2 $3	...@*LOCAL* -> ...
1007R$* < @ *LOCAL* >	$: $1
1008
1009#
1010#  Parse1 -- the bottom half of ruleset 0.
1011#
1012
1013SParse1
1014ifdef(`_LDAP_ROUTING_', `dnl
1015# handle LDAP routing for hosts in $={LDAPRoute}
1016R$+ < @ $={LDAPRoute} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2> <>
1017R$+ < @ $={LDAPRouteEquiv} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $M> <>',
1018`dnl')
1019
1020ifdef(`_MAILER_smtp_',
1021`# handle numeric address spec
1022dnl there is no check whether this is really an IP number
1023R$* < @ [ $+ ] > $*	$: $>ParseLocal $1 < @ [ $2 ] > $3	numeric internet spec
1024R$* < @ [ $+ ] > $*	$: $1 < @ [ $2 ] : $S > $3	Add smart host to path
1025R$* < @ [ $+ ] : > $*		$#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3	no smarthost: send
1026R$* < @ [ $+ ] : $- : $*> $*	$#$3 $@ $4 $: $1 < @ [$2] > $5	smarthost with mailer
1027R$* < @ [ $+ ] : $+ > $*	$#_SMTP_ $@ $3 $: $1 < @ [$2] > $4	smarthost without mailer',
1028	`dnl')
1029
1030ifdef(`_VIRTUSER_TABLE_', `dnl
1031# handle virtual users
1032ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
1033dnl this is not a documented option
1034dnl it stops looping in virtusertable mapping if input and output
1035dnl are identical, i.e., if address A is mapped to A.
1036dnl it does not deal with multi-level recursion
1037# handle full domains in RHS of virtusertable
1038R$+ < @ $+ >			$: $(macro {RecipientAddress} $) $1 < @ $2 >
1039R$+ < @ $+ > 			$: <?> $1 < @ $2 > $| $>final $1 < @ $2 >
1040R<?> $+ $| $+			$: $1 $(macro {RecipientAddress} $@ $2 $)
1041R<?> $+ $| $*			$: $1',
1042`dnl')
1043R$+			$: <!> $1		Mark for lookup
1044dnl input: <!> local<@domain>
1045ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
1046`R<!> $+ < @ $* $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >',
1047`R<!> $+ < @ $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >')
1048dnl input: <result-of-lookup | @> local<@domain> | <!> local<@domain>
1049R<!> $+ < @ $=w . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
1050dnl if <@> local<@domain>: no match but try lookup
1051dnl user+detail: try user++@domain if detail not empty
1052R<@> $+ + $+ < @ $* . >
1053			$: < $(virtuser $1 + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1054dnl user+detail: try user+*@domain
1055R<@> $+ + $* < @ $* . >
1056			$: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1057dnl user+detail: try user@domain
1058R<@> $+ + $* < @ $* . >
1059			$: < $(virtuser $1 @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1060dnl try default entry: @domain
1061dnl ++@domain
1062R<@> $+ + $+ < @ $+ . >	$: < $(virtuser + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1063dnl +*@domain
1064R<@> $+ + $* < @ $+ . >	$: < $(virtuser + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1065dnl @domain if +detail exists
1066dnl if no match, change marker to prevent a second @domain lookup
1067R<@> $+ + $* < @ $+ . >	$: < $(virtuser @ $3 $@ $1 $@ $2 $@ +$2 $: ! $) > $1 + $2 < @ $3 . >
1068dnl without +detail
1069R<@> $+ < @ $+ . >	$: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
1070dnl no match
1071R<@> $+			$: $1
1072dnl remove mark
1073R<!> $+			$: $1
1074R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
1075R< error : $- $+ > $* 	$#error $@ $(dequote $1 $) $: $2
1076ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
1077# check virtuser input address against output address, if same, skip recursion
1078R< $+ > $+ < @ $+ >				$: < $1 > $2 < @ $3 > $| $1
1079# it is the same: stop now
1080R< $+ > $+ < @ $+ > $| $&{RecipientAddress}	$: $>ParseLocal $>Parse0 $>canonify $1
1081R< $+ > $+ < @ $+ > $| $* 			$: < $1 > $2 < @ $3 >
1082dnl', `dnl')
1083dnl this is not a documented option
1084dnl it performs no looping at all for virtusertable
1085ifdef(`_NO_VIRTUSER_RECURSION_',
1086`R< $+ > $+ < @ $+ >	$: $>ParseLocal $>Parse0 $>canonify $1',
1087`R< $+ > $+ < @ $+ >	$: $>Recurse $1')
1088dnl', `dnl')
1089
1090# short circuit local delivery so forwarded email works
1091ifdef(`_MAILER_usenet_', `dnl
1092R$+ . USENET < @ $=w . >	$#usenet $@ usenet $: $1	handle usenet specially', `dnl')
1093
1094
1095ifdef(`_STICKY_LOCAL_DOMAIN_',
1096`R$+ < @ $=w . >		$: < $H > $1 < @ $2 . >		first try hub
1097R< $+ > $+ < $+ >	$>MailerToTriple < $1 > $2 < $3 >	yep ....
1098dnl $H empty (but @$=w.)
1099R< > $+ + $* < $+ >	$#_LOCAL_ $: $1 + $2		plussed name?
1100R< > $+ < $+ >		$#_LOCAL_ $: @ $1			nope, local address',
1101`R$=L < @ $=w . >	$#_LOCAL_ $: @ $1			special local names
1102R$+ < @ $=w . >		$#_LOCAL_ $: $1			regular local name')
1103
1104ifdef(`_MAILER_TABLE_', `dnl
1105# not local -- try mailer table lookup
1106R$* <@ $+ > $*		$: < $2 > $1 < @ $2 > $3	extract host name
1107R< $+ . > $*		$: < $1 > $2			strip trailing dot
1108R< $+ > $*		$: < $(mailertable $1 $) > $2	lookup
1109dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1110R< $~[ : $* > $* 	$>MailerToTriple < $1 : $2 > $3		check -- resolved?
1111R< $+ > $*		$: $>Mailertable <$1> $2		try domain',
1112`dnl')
1113undivert(4)dnl UUCP rules from `MAILER(uucp)'
1114
1115ifdef(`_NO_UUCP_', `dnl',
1116`# resolve remotely connected UUCP links (if any)
1117ifdef(`_CLASS_V_',
1118`R$* < @ $=V . UUCP . > $*		$: $>MailerToTriple < $V > $1 <@$2.UUCP.> $3',
1119	`dnl')
1120ifdef(`_CLASS_W_',
1121`R$* < @ $=W . UUCP . > $*		$: $>MailerToTriple < $W > $1 <@$2.UUCP.> $3',
1122	`dnl')
1123ifdef(`_CLASS_X_',
1124`R$* < @ $=X . UUCP . > $*		$: $>MailerToTriple < $X > $1 <@$2.UUCP.> $3',
1125	`dnl')')
1126
1127# resolve fake top level domains by forwarding to other hosts
1128ifdef(`BITNET_RELAY',
1129`R$*<@$+.BITNET.>$*	$: $>MailerToTriple < $B > $1 <@$2.BITNET.> $3	user@host.BITNET',
1130	`dnl')
1131ifdef(`DECNET_RELAY',
1132`R$*<@$+.DECNET.>$*	$: $>MailerToTriple < $C > $1 <@$2.DECNET.> $3	user@host.DECNET',
1133	`dnl')
1134ifdef(`_MAILER_pop_',
1135`R$+ < @ POP. >		$#pop $: $1			user@POP',
1136	`dnl')
1137ifdef(`_MAILER_fax_',
1138`R$+ < @ $+ .FAX. >	$#fax $@ $2 $: $1		user@host.FAX',
1139`ifdef(`FAX_RELAY',
1140`R$*<@$+.FAX.>$*		$: $>MailerToTriple < $F > $1 <@$2.FAX.> $3	user@host.FAX',
1141	`dnl')')
1142
1143ifdef(`UUCP_RELAY',
1144`# forward non-local UUCP traffic to our UUCP relay
1145R$*<@$*.UUCP.>$*		$: $>MailerToTriple < $Y > $1 <@$2.UUCP.> $3	uucp mail',
1146`ifdef(`_MAILER_uucp_',
1147`# forward other UUCP traffic straight to UUCP
1148R$* < @ $+ .UUCP. > $*		$#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3	user@host.UUCP',
1149	`dnl')')
1150ifdef(`_MAILER_usenet_', `
1151# addresses sent to net.group.USENET will get forwarded to a newsgroup
1152R$+ . USENET		$#usenet $@ usenet $: $1',
1153	`dnl')
1154
1155ifdef(`_LOCAL_RULES_',
1156`# figure out what should stay in our local mail system
1157undivert(1)', `dnl')
1158
1159# pass names that still have a host to a smarthost (if defined)
1160R$* < @ $* > $*		$: $>MailerToTriple < $S > $1 < @ $2 > $3	glue on smarthost name
1161
1162# deal with other remote names
1163ifdef(`_MAILER_smtp_',
1164`R$* < @$* > $*		$#_SMTP_ $@ $2 $: $1 < @ $2 > $3	user@host.domain',
1165`R$* < @$* > $*		$#error $@ 5.1.2 $: "_CODE553 Unrecognized host name " $2')
1166
1167# handle locally delivered names
1168R$=L			$#_LOCAL_ $: @ $1		special local names
1169R$+			$#_LOCAL_ $: $1			regular local names
1170
1171###########################################################################
1172###   Ruleset 5 -- special rewriting after aliases have been expanded   ###
1173###########################################################################
1174
1175SLocal_localaddr
1176Slocaladdr=5
1177R$+			$: $1 $| $>"Local_localaddr" $1
1178R$+ $| $#ok		$@ $1			no change
1179R$+ $| $#$*		$#$2
1180R$+ $| $*		$: $1
1181
1182ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1183# Preserve rcpt_host in {Host}
1184R$+			$: $1 $| $&h $| $&{Host}	check h and {Host}
1185R$+ $| $|		$: $(macro {Host} $@ $) $1	no h or {Host}
1186R$+ $| $| $+		$: $1			h not set, {Host} set
1187R$+ $| +$* $| $*	$: $1			h is +detail, {Host} set
1188R$+ $| $* @ $+ $| $*	$: $(macro {Host} $@ @$3 $) $1	set {Host} to host in h
1189R$+ $| $+ $| $*		$: $(macro {Host} $@ @$2 $) $1	set {Host} to h
1190')dnl
1191
1192ifdef(`_FFR_5_', `dnl
1193# Preserve host in a macro
1194R$+			$: $(macro {LocalAddrHost} $) $1
1195R$+ @ $+		$: $(macro {LocalAddrHost} $@ @ $2 $) $1')
1196
1197ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `dnl
1198# deal with plussed users so aliases work nicely
1199R$+ + *			$#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1200R$+ + $*		$#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1201')
1202# prepend an empty "forward host" on the front
1203R$+			$: <> $1
1204
1205ifdef(`LUSER_RELAY', `dnl
1206# send unrecognized local users to a relay host
1207ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
1208R< > $+ + $*		$: < ? $L > <+ $2> $(user $1 $)	look up user+
1209R< > $+			$: < ? $L > < > $(user $1 $)	look up user
1210R< ? $* > < $* > $+ <>	$: < > $3 $2			found; strip $L
1211R< ? $* > < $* > $+	$: < $1 > $3 $2			not found', `
1212R< > $+ 		$: < $L > $(user $1 $)		look up user
1213R< $* > $+ <>		$: < > $2			found; strip $L')
1214ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1215R< $+ > $+		$: < $1 > $2 $&{Host}')
1216dnl')
1217
1218ifdef(`MAIL_HUB', `dnl
1219R< > $+			$: < $H > $1			try hub', `dnl')
1220ifdef(`LOCAL_RELAY', `dnl
1221R< > $+			$: < $R > $1			try relay', `dnl')
1222ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
1223R< > $+			$@ $1', `dnl
1224R< > $+			$: < > < $1 <> $&h >		nope, restore +detail
1225ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1226R< > < $+ @ $+ <> + $* >	$: < > < $1 + $3 @ $2 >	check whether +detail')
1227R< > < $+ <> + $* >	$: < > < $1 + $2 >		check whether +detail
1228R< > < $+ <> $* >	$: < > < $1 >			else discard
1229R< > < $+ + $* > $*	   < > < $1 > + $2 $3		find the user part
1230R< > < $+ > + $*	$#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')		strip the extra +
1231R< > < $+ >		$@ $1				no +detail
1232R$+			$: $1 <> $&h			add +detail back in
1233ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1234R$+ @ $+ <> + $*	$: $1 + $3 @ $2			check whether +detail')
1235R$+ <> + $*		$: $1 + $2			check whether +detail
1236R$+ <> $*		$: $1				else discard')
1237R< local : $* > $*	$: $>MailerToTriple < local : $1 > $2	no host extension
1238R< error : $* > $*	$: $>MailerToTriple < error : $1 > $2	no host extension
1239ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1240dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1241R< $~[ : $+ > $+ @ $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $4 >')
1242R< $~[ : $+ > $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $2 >
1243ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1244R< $+ > $+ @ $+		$@ $>MailerToTriple < $1 > $2 < @ $3 >')
1245R< $+ > $+		$@ $>MailerToTriple < $1 > $2 < @ $1 >
1246
1247ifdef(`_MAILER_TABLE_', `dnl
1248ifdef(`_LDAP_ROUTING_', `dnl
1249###################################################################
1250###  Ruleset LDAPMailertable -- mailertable lookup for LDAP     ###
1251dnl input: <Domain> FullAddress
1252###################################################################
1253
1254SLDAPMailertable
1255R< $+ > $*		$: < $(mailertable $1 $) > $2		lookup
1256R< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		check resolved?
1257R< $+ > $*		$: < $1 > $>Mailertable <$1> $2		try domain
1258R< $+ > $#$*		$#$2					found
1259R< $+ > $*		$#_RELAY_ $@ $1 $: $2			not found, direct relay',
1260`dnl')
1261
1262###################################################################
1263###  Ruleset 90 -- try domain part of mailertable entry 	###
1264dnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress
1265###################################################################
1266
1267SMailertable=90
1268dnl shift and check
1269dnl %2 is not documented in cf/README
1270R$* <$- . $+ > $*	$: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
1271dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1272R$* <$~[ : $* > $*	$>MailerToTriple < $2 : $3 > $4		check -- resolved?
1273R$* < . $+ > $* 	$@ $>Mailertable $1 . <$2> $3		no -- strip & try again
1274dnl is $2 always empty?
1275R$* < $* > $*		$: < $(mailertable . $@ $1$2 $) > $3	try "."
1276R< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		"." found?
1277dnl return full address
1278R< $* > $*		$@ $2				no mailertable match',
1279`dnl')
1280
1281###################################################################
1282###  Ruleset 95 -- canonify mailer:[user@]host syntax to triple	###
1283dnl input: in general: <[mailer:]host> lp<@domain>rest
1284dnl	<> address				-> address
1285dnl	<error:d.s.n:text>			-> error
1286dnl	<error:text>				-> error
1287dnl	<mailer:user@host> lp<@domain>rest	-> mailer host user
1288dnl	<mailer:host> address			-> mailer host address
1289dnl	<localdomain> address			-> address
1290dnl	<host> address				-> relay host address
1291###################################################################
1292
1293SMailerToTriple=95
1294R< > $*				$@ $1			strip off null relay
1295R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
1296R< error : $- $+ > $*		$#error $@ $(dequote $1 $) $: $2
1297R< local : $* > $*		$>CanonLocal < $1 > $2
1298dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1299R< $~[ : $+ @ $+ > $*<$*>$*	$# $1 $@ $3 $: $2<@$3>	use literal user
1300R< $~[ : $+ > $*		$# $1 $@ $2 $: $3	try qualified mailer
1301R< $=w > $*			$@ $2			delete local host
1302R< $+ > $*			$#_RELAY_ $@ $1 $: $2	use unqualified mailer
1303
1304###################################################################
1305###  Ruleset CanonLocal -- canonify local: syntax		###
1306dnl input: <user> address
1307dnl <x> <@host> : rest			-> Recurse rest
1308dnl <x> p1 $=O p2 <@host>		-> Recurse p1 $=O p2
1309dnl <> user <@host> rest		-> local user@host user
1310dnl <> user				-> local user user
1311dnl <user@host> lp <@domain> rest	-> <user> lp <@host> [cont]
1312dnl <user> lp <@host> rest		-> local lp@host user
1313dnl <user> lp				-> local lp user
1314###################################################################
1315
1316SCanonLocal
1317# strip local host from routed addresses
1318R< $* > < @ $+ > : $+		$@ $>Recurse $3
1319R< $* > $+ $=O $+ < @ $+ >	$@ $>Recurse $2 $3 $4
1320
1321# strip trailing dot from any host name that may appear
1322R< $* > $* < @ $* . >		$: < $1 > $2 < @ $3 >
1323
1324# handle local: syntax -- use old user, either with or without host
1325R< > $* < @ $* > $*		$#_LOCAL_ $@ $1@$2 $: $1
1326R< > $+				$#_LOCAL_ $@ $1    $: $1
1327
1328# handle local:user@host syntax -- ignore host part
1329R< $+ @ $+ > $* < @ $* >	$: < $1 > $3 < @ $4 >
1330
1331# handle local:user syntax
1332R< $+ > $* <@ $* > $*		$#_LOCAL_ $@ $2@$3 $: $1
1333R< $+ > $* 			$#_LOCAL_ $@ $2    $: $1
1334
1335###################################################################
1336###  Ruleset 93 -- convert header names to masqueraded form	###
1337###################################################################
1338
1339SMasqHdr=93
1340
1341ifdef(`_GENERICS_TABLE_', `dnl
1342# handle generics database
1343ifdef(`_GENERICS_ENTIRE_DOMAIN_',
1344dnl if generics should be applied add a @ as mark
1345`R$+ < @ $* $=G . >	$: < $1@$2$3 > $1 < @ $2$3 . > @	mark',
1346`R$+ < @ $=G . >	$: < $1@$2 > $1 < @ $2 . > @	mark')
1347R$+ < @ *LOCAL* >	$: < $1@$j > $1 < @ *LOCAL* > @	mark
1348dnl workspace: either user<@domain> or <user@domain> user <@domain> @
1349dnl ignore the first case for now
1350dnl if it has the mark lookup full address
1351dnl broken: %1 is full address not just detail
1352R< $+ > $+ < $* > @	$: < $(generics $1 $: @ $1 $) > $2 < $3 >
1353dnl workspace: ... or <match|@user@domain> user <@domain>
1354dnl no match, try user+detail@domain
1355R<@$+ + $* @ $+> $+ < @ $+ >
1356		$: < $(generics $1+*@$3 $@ $2 $:@$1 + $2@$3 $) >  $4 < @ $5 >
1357R<@$+ + $* @ $+> $+ < @ $+ >
1358		$: < $(generics $1@$3 $: $) > $4 < @ $5 >
1359dnl no match, remove mark
1360R<@$+ > $+ < @ $+ >	$: < > $2 < @ $3 >
1361dnl no match, try @domain for exceptions
1362R< > $+ < @ $+ . >	$: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . >
1363dnl workspace: ... or <match> user <@domain>
1364dnl no match, try local part
1365R< > $+ < @ $+ > 	$: < $(generics $1 $: $) > $1 < @ $2 >
1366R< > $+ + $* < @ $+ > 	$: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 >
1367R< > $+ + $* < @ $+ > 	$: < $(generics $1 $: $) > $1 + $2 < @ $3 >
1368R< $* @ $* > $* < $* >	$@ $>canonify $1 @ $2		found qualified
1369R< $+ > $* < $* >	$: $>canonify $1 @ *LOCAL*	found unqualified
1370R< > $*			$: $1				not found',
1371`dnl')
1372
1373# do not masquerade anything in class N
1374R$* < @ $* $=N . >	$@ $1 < @ $2 $3 . >
1375
1376ifdef(`MASQUERADE_NAME', `dnl
1377# special case the users that should be exposed
1378R$=E < @ *LOCAL* >	$@ $1 < @ $j . >		leave exposed
1379ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1380`R$=E < @ $* $=M . >	$@ $1 < @ $2 $3 . >',
1381`R$=E < @ $=M . >	$@ $1 < @ $2 . >')
1382ifdef(`_LIMITED_MASQUERADE_', `dnl',
1383`R$=E < @ $=w . >	$@ $1 < @ $2 . >')
1384
1385# handle domain-specific masquerading
1386ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1387`R$* < @ $* $=M . > $*	$: $1 < @ $2 $3 . @ $M > $4	convert masqueraded doms',
1388`R$* < @ $=M . > $*	$: $1 < @ $2 . @ $M > $3	convert masqueraded doms')
1389ifdef(`_LIMITED_MASQUERADE_', `dnl',
1390`R$* < @ $=w . > $*	$: $1 < @ $2 . @ $M > $3')
1391R$* < @ *LOCAL* > $*	$: $1 < @ $j . @ $M > $2
1392R$* < @ $+ @ > $*	$: $1 < @ $2 > $3		$M is null
1393R$* < @ $+ @ $+ > $*	$: $1 < @ $3 . > $4		$M is not null
1394dnl', `dnl no masquerading
1395dnl just fix *LOCAL* leftovers
1396R$* < @ *LOCAL* >	$@ $1 < @ $j . >')
1397
1398###################################################################
1399###  Ruleset 94 -- convert envelope names to masqueraded form	###
1400###################################################################
1401
1402SMasqEnv=94
1403ifdef(`_MASQUERADE_ENVELOPE_',
1404`R$+			$@ $>MasqHdr $1',
1405`R$* < @ *LOCAL* > $*	$: $1 < @ $j . > $2')
1406
1407###################################################################
1408###  Ruleset 98 -- local part of ruleset zero (can be null)	###
1409###################################################################
1410
1411SParseLocal=98
1412undivert(3)dnl LOCAL_RULE_0
1413
1414ifdef(`_LDAP_ROUTING_', `dnl
1415######################################################################
1416###  LDAPExpand: Expand address using LDAP routing
1417###
1418###	Parameters:
1419###		<$1> -- parsed address (user < @ domain . >) (pass through)
1420###		<$2> -- RFC822 address (user @ domain) (used for lookup)
1421###		<$3> -- +detail information
1422###
1423###	Returns:
1424###		Mailer triplet ($#mailer $@ host $: address)
1425###		Parsed address (user < @ domain . >)
1426######################################################################
1427
1428SLDAPExpand
1429# do the LDAP lookups
1430R<$+><$+><$*>	$: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2> <$3>
1431
1432# look for temporary failures (return original address, MTA will queue up)
1433R<$* <TMPF>> <$*> <$+> <$+> <$*>	$@ $3
1434R<$*> <$* <TMPF>> <$+> <$+> <$*>	$@ $3
1435
1436# if mailRoutingAddress and local or non-existant mailHost,
1437# return the new mailRoutingAddress
1438ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1439R<$+@$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $6 @ $2
1440R<$+@$+> <> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $5 @ $2')
1441R<$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1
1442R<$+> <> <$+> <$+> <$*>		$@ $>Parse0 $>canonify $1
1443
1444
1445# if mailRoutingAddress and non-local mailHost,
1446# relay to mailHost with new mailRoutingAddress
1447ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1448ifdef(`_MAILER_TABLE_', `dnl
1449# check mailertable for host, relay from there
1450R<$+@$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$3> $>canonify $1 $6 @ $2',
1451`R<$+@$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $3 $: $>canonify $1 $6 @ $2')')
1452ifdef(`_MAILER_TABLE_', `dnl
1453# check mailertable for host, relay from there
1454R<$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$2> $>canonify $1',
1455`R<$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $2 $: $>canonify $1')
1456
1457# if no mailRoutingAddress and local mailHost,
1458# return original address
1459R<> <$=w> <$+> <$+> <$*>	$@ $2
1460
1461
1462# if no mailRoutingAddress and non-local mailHost,
1463# relay to mailHost with original address
1464ifdef(`_MAILER_TABLE_', `dnl
1465# check mailertable for host, relay from there
1466R<> <$+> <$+> <$+> <$*>		$>LDAPMailertable <$1> $2',
1467`R<> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $1 $: $2')
1468
1469ifdef(`_LDAP_ROUTE_DETAIL_',
1470`# if no mailRoutingAddress and no mailHost,
1471# try without +detail
1472R<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <$2 @ $4> <+$3>')dnl
1473
1474# if still no mailRoutingAddress and no mailHost,
1475# try @domain
1476ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1477R<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <@ $4> <+$3>')
1478R<> <> <$+> <$+ @ $+> <$*>	$@ $>LDAPExpand <$1> <@ $3> <$4>
1479
1480# if no mailRoutingAddress and no mailHost and this was a domain attempt,
1481ifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl
1482# user does not exist
1483R<> <> <$+> <@ $+> <$*>		$: <?> < $&{addr_type} > < $1 >
1484# only give error for envelope recipient
1485R<?> <e r> <$+>			$#error $@ nouser $: "550 User unknown"
1486R<?> <$*> <$+>			$@ $2',
1487`dnl
1488# return the original address
1489R<> <> <$+> <@ $+> <$*>		$@ $1')',
1490`dnl')
1491
1492ifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode.
1493')')
1494ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
1495######################################################################
1496###  D: LookUpDomain -- search for domain in access database
1497###
1498###	Parameters:
1499###		<$1> -- key (domain name)
1500###		<$2> -- default (what to return if not found in db)
1501dnl			must not be empty
1502###		<$3> -- mark (must be <(!|+) single-token>)
1503###			! does lookup only with tag
1504###			+ does lookup with and without tag
1505###		<$4> -- passthru (additional data passed unchanged through)
1506dnl returns:		<default> <passthru>
1507dnl 			<result> <passthru>
1508######################################################################
1509
1510SD
1511dnl workspace <key> <default> <passthru> <mark>
1512dnl lookup with tag (in front, no delimiter here)
1513dnl    2    3  4    5
1514R<$*> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
1515dnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark>
1516dnl lookup without tag?
1517dnl   1    2      3    4
1518R<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
1519ifdef(`_LOOKUPDOTDOMAIN_', `dnl omit first component: lookup .rest
1520dnl XXX apply this also to IP addresses?
1521dnl currently it works the wrong way round for [1.2.3.4]
1522dnl   1  2    3    4  5    6
1523R<?> <$+.$+> <$+> <$- $-> <$*>	$: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4 $5> <$6>
1524dnl   1  2    3      4    5
1525R<?> <$+.$+> <$+> <+ $-> <$*>	$: < $(access .$2 $: ? $) > <$1.$2> <$3> <+ $4> <$5>', `dnl')
1526ifdef(`_ACCESS_SKIP_', `dnl
1527dnl found SKIP: return <default> and <passthru>
1528dnl      1    2    3  4    5
1529R<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
1530dnl not found: IPv4 net (no check is done whether it is an IP number!)
1531dnl    1  2     3    4  5    6
1532R<?> <[$+.$-]> <$+> <$- $-> <$*>	$@ $>D <[$1]> <$3> <$4 $5> <$6>
1533ifdef(`NO_NETINET6', `dnl',
1534`dnl not found: IPv6 net
1535dnl (could be merged with previous rule if we have a class containing .:)
1536dnl    1   2     3    4  5    6
1537R<?> <[$+::$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>
1538R<?> <[$+:$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>')
1539dnl not found, but subdomain: try again
1540dnl   1  2    3    4  5    6
1541R<?> <$+.$+> <$+> <$- $-> <$*>	$@ $>D <$2> <$3> <$4 $5> <$6>
1542ifdef(`_FFR_LOOKUPTAG_', `dnl lookup Tag:
1543dnl   1    2      3    4
1544R<?> <$+> <$+> <! $-> <$*>	$: < $(access $3`'_TAG_DELIM_ $: ? $) > <$1> <$2> <! $3> <$4>', `dnl')
1545dnl not found, no subdomain: return <default> and <passthru>
1546dnl   1    2    3  4    5
1547R<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
1548ifdef(`_ATMPF_', `dnl tempfail?
1549dnl            2    3    4  5    6
1550R<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
1551dnl return <result of lookup> and <passthru>
1552dnl    2    3    4  5    6
1553R<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
1554
1555######################################################################
1556###  A: LookUpAddress -- search for host address in access database
1557###
1558###	Parameters:
1559###		<$1> -- key (dot quadded host address)
1560###		<$2> -- default (what to return if not found in db)
1561dnl			must not be empty
1562###		<$3> -- mark (must be <(!|+) single-token>)
1563###			! does lookup only with tag
1564###			+ does lookup with and without tag
1565###		<$4> -- passthru (additional data passed through)
1566dnl	returns:	<default> <passthru>
1567dnl			<result> <passthru>
1568######################################################################
1569
1570SA
1571dnl lookup with tag
1572dnl    2    3  4    5
1573R<$+> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
1574dnl lookup without tag
1575dnl   1    2      3    4
1576R<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
1577dnl workspace <result-of-lookup|?> <key> <default> <mark> <passthru>
1578ifdef(`_ACCESS_SKIP_', `dnl
1579dnl found SKIP: return <default> and <passthru>
1580dnl      1    2    3  4    5
1581R<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
1582ifdef(`NO_NETINET6', `dnl',
1583`dnl no match; IPv6: remove last part
1584dnl   1   2    3    4  5    6
1585R<?> <$+::$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
1586R<?> <$+:$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>')
1587dnl no match; IPv4: remove last part
1588dnl   1  2    3    4  5    6
1589R<?> <$+.$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
1590dnl no match: return default
1591dnl   1    2    3  4    5
1592R<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
1593ifdef(`_ATMPF_', `dnl tempfail?
1594dnl            2    3    4  5    6
1595R<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
1596dnl match: return result
1597dnl    2    3    4  5    6
1598R<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
1599dnl endif _ACCESS_TABLE_
1600divert(0)
1601######################################################################
1602###  CanonAddr --	Convert an address into a standard form for
1603###			relay checking.  Route address syntax is
1604###			crudely converted into a %-hack address.
1605###
1606###	Parameters:
1607###		$1 -- full recipient address
1608###
1609###	Returns:
1610###		parsed address, not in source route form
1611dnl		user%host%host<@domain>
1612dnl		host!user<@domain>
1613######################################################################
1614
1615SCanonAddr
1616R$*			$: $>Parse0 $>canonify $1	make domain canonical
1617ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
1618R< @ $+ > : $* @ $*	< @ $1 > : $2 % $3	change @ to % in src route
1619R$* < @ $+ > : $* : $*	$3 $1 < @ $2 > : $4	change to % hack.
1620R$* < @ $+ > : $*	$3 $1 < @ $2 >
1621dnl')
1622
1623######################################################################
1624###  ParseRecipient --	Strip off hosts in $=R as well as possibly
1625###			$* $=m or the access database.
1626###			Check user portion for host separators.
1627###
1628###	Parameters:
1629###		$1 -- full recipient address
1630###
1631###	Returns:
1632###		parsed, non-local-relaying address
1633######################################################################
1634
1635SParseRecipient
1636dnl mark and canonify address
1637R$*				$: <?> $>CanonAddr $1
1638dnl workspace: <?> localpart<@domain[.]>
1639R<?> $* < @ $* . >		<?> $1 < @ $2 >			strip trailing dots
1640dnl workspace: <?> localpart<@domain>
1641R<?> $- < @ $* >		$: <?> $(dequote $1 $) < @ $2 >	dequote local part
1642
1643# if no $=O character, no host in the user portion, we are done
1644R<?> $* $=O $* < @ $* >		$: <NO> $1 $2 $3 < @ $4>
1645dnl no $=O in localpart: return
1646R<?> $*				$@ $1
1647
1648dnl workspace: <NO> localpart<@domain>, where localpart contains $=O
1649dnl mark everything which has an "authorized" domain with <RELAY>
1650ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1651# if we relay, check username portion for user%host so host can be checked also
1652R<NO> $* < @ $* $=m >		$: <RELAY> $1 < @ $2 $3 >', `dnl')
1653dnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O
1654dnl if mark is <NO> then change it to <RELAY> if domain is "authorized"
1655
1656dnl what if access map returns something else than RELAY?
1657dnl we are only interested in RELAY entries...
1658dnl other To: entries: blacklist recipient; generic entries?
1659dnl if it is an error we probably do not want to relay anyway
1660ifdef(`_RELAY_HOSTS_ONLY_',
1661`R<NO> $* < @ $=R >		$: <RELAY> $1 < @ $2 >
1662ifdef(`_ACCESS_TABLE_', `dnl
1663R<NO> $* < @ $+ >		$: <$(access To:$2 $: NO $)> $1 < @ $2 >
1664R<NO> $* < @ $+ >		$: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')',
1665`R<NO> $* < @ $* $=R >		$: <RELAY> $1 < @ $2 $3 >
1666ifdef(`_ACCESS_TABLE_', `dnl
1667R<NO> $* < @ $+ >		$: $>D <$2> <NO> <+ To> <$1 < @ $2 >>
1668R<$+> <$+>			$: <$1> $2',`dnl')')
1669
1670
1671ifdef(`_RELAY_MX_SERVED_', `dnl
1672dnl do "we" ($=w) act as backup MX server for the destination domain?
1673R<NO> $* < @ $+ >		$: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > >
1674R<MX> < : $* <TEMP> : > $*	$#TEMP $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1
1675dnl yes: mark it as <RELAY>
1676R<MX> < $* : $=w. : $* > < $+ >	$: <RELAY> $4
1677dnl no: put old <NO> mark back
1678R<MX> < : $* : > < $+ >		$: <NO> $2', `dnl')
1679
1680dnl do we relay to this recipient domain?
1681R<RELAY> $* < @ $* >		$@ $>ParseRecipient $1
1682dnl something else
1683R<$+> $*			$@ $2
1684
1685
1686######################################################################
1687###  check_relay -- check hostname/address on SMTP startup
1688######################################################################
1689
1690SLocal_check_relay
1691Scheck`'_U_`'relay
1692R$*			$: $1 $| $>"Local_check_relay" $1
1693R$* $| $* $| $#$*	$#$3
1694R$* $| $* $| $*		$@ $>"Basic_check_relay" $1 $| $2
1695
1696SBasic_check_relay
1697# check for deferred delivery mode
1698R$*			$: < $&{deliveryMode} > $1
1699R< d > $*		$@ deferred
1700R< $* > $*		$: $2
1701
1702ifdef(`_ACCESS_TABLE_', `dnl
1703dnl workspace: {client_name} $| {client_addr}
1704R$+ $| $+		$: $>D < $1 > <?> <+ Connect> < $2 >
1705dnl workspace: <result-of-lookup> <{client_addr}>
1706dnl OR $| $+ if client_name is empty
1707R   $| $+		$: $>A < $1 > <?> <+ Connect> <>	empty client_name
1708dnl workspace: <result-of-lookup> <{client_addr}>
1709R<?> <$+>		$: $>A < $1 > <?> <+ Connect> <>	no: another lookup
1710dnl workspace: <result-of-lookup> (<>|<{client_addr}>)
1711R<?> <$*>		$: OK				found nothing
1712dnl workspace: <result-of-lookup> (<>|<{client_addr}>) | OK
1713R<$={Accept}> <$*>	$@ $1				return value of lookup
1714R<REJECT> <$*>		$#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"')
1715R<DISCARD> <$*>		$#discard $: discard
1716ifdef(`_FFR_QUARANTINE',
1717`R<QUARANTINE:$+> <$*>	$#error $@ quarantine $: $1', `dnl')
1718dnl error tag
1719R<ERROR:$-.$-.$-:$+> <$*>	$#error $@ $1.$2.$3 $: $4
1720R<ERROR:$+> <$*>		$#error $: $1
1721ifdef(`_ATMPF_', `R<$* _ATMPF_> <$*>		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
1722dnl generic error from access map
1723R<$+> <$*>		$#error $: $1', `dnl')
1724
1725ifdef(`_RBL_',`dnl
1726# DNS based IP address spam list
1727dnl workspace: ignored...
1728R$*			$: $&{client_addr}
1729R$-.$-.$-.$-		$: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
1730R<?>OK			$: OKSOFAR
1731R<?>$+			$#error $@ 5.7.1 $: "550 Rejected: " $&{client_addr} " listed at _RBL_"',
1732`dnl')
1733undivert(8)
1734
1735######################################################################
1736###  check_mail -- check SMTP ``MAIL FROM:'' command argument
1737######################################################################
1738
1739SLocal_check_mail
1740Scheck`'_U_`'mail
1741R$*			$: $1 $| $>"Local_check_mail" $1
1742R$* $| $#$*		$#$2
1743R$* $| $*		$@ $>"Basic_check_mail" $1
1744
1745SBasic_check_mail
1746# check for deferred delivery mode
1747R$*			$: < $&{deliveryMode} > $1
1748R< d > $*		$@ deferred
1749R< $* > $*		$: $2
1750
1751# authenticated?
1752dnl done first: we can require authentication for every mail transaction
1753dnl workspace: address as given by MAIL FROM: (sender)
1754R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
1755R$* $| $#$+		$#$2
1756dnl undo damage: remove result of tls_client call
1757R$* $| $*		$: $1
1758
1759dnl workspace: address as given by MAIL FROM:
1760R<>			$@ <OK>			we MUST accept <> (RFC 1123)
1761ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1762dnl do some additional checks
1763dnl no user@host
1764dnl no user@localhost (if nonlocal sender)
1765dnl this is a pretty simple canonification, it will not catch every case
1766dnl just make sure the address has <> around it (which is required by
1767dnl the RFC anyway, maybe we should complain if they are missing...)
1768dnl dirty trick: if it is user@host, just add a dot: user@host. this will
1769dnl not be modified by host lookups.
1770R$+			$: <?> $1
1771R<?><$+>		$: <@> <$1>
1772R<?>$+			$: <@> <$1>
1773dnl workspace: <@> <address>
1774dnl prepend daemon_flags
1775R$*			$: $&{daemon_flags} $| $1
1776dnl workspace: ${daemon_flags} $| <@> <address>
1777dnl do not allow these at all or only from local systems?
1778R$* f $* $| <@> < $* @ $- >	$: < ? $&{client_name} > < $3 @ $4 >
1779dnl accept unqualified sender: change mark to avoid test
1780R$* u $* $| <@> < $* >	$: <?> < $3 >
1781dnl workspace: ${daemon_flags} $| <@> <address>
1782dnl        or:                    <? ${client_name} > <address>
1783dnl        or:                    <?> <address>
1784dnl remove daemon_flags
1785R$* $| $*		$: $2
1786# handle case of @localhost on address
1787R<@> < $* @ localhost >	$: < ? $&{client_name} > < $1 @ localhost >
1788R<@> < $* @ [127.0.0.1] >
1789			$: < ? $&{client_name} > < $1 @ [127.0.0.1] >
1790R<@> < $* @ localhost.$m >
1791			$: < ? $&{client_name} > < $1 @ localhost.$m >
1792ifdef(`_NO_UUCP_', `dnl',
1793`R<@> < $* @ localhost.UUCP >
1794			$: < ? $&{client_name} > < $1 @ localhost.UUCP >')
1795dnl workspace: < ? $&{client_name} > <user@localhost|host>
1796dnl	or:    <@> <address>
1797dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
1798R<@> $*			$: $1			no localhost as domain
1799dnl workspace: < ? $&{client_name} > <user@localhost|host>
1800dnl	or:    <address>
1801dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
1802R<? $=w> $*		$: $2			local client: ok
1803R<? $+> <$+>		$#error $@ 5.5.4 $: "_CODE553 Real domain name required for sender address"
1804dnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags})
1805R<?> $*			$: $1')
1806dnl workspace: address (or <address>)
1807R$*			$: <?> $>CanonAddr $1		canonify sender address and mark it
1808dnl workspace: <?> CanonicalAddress (i.e. address in canonical form localpart<@host>)
1809dnl there is nothing behind the <@host> so no trailing $* needed
1810R<?> $* < @ $+ . >	<?> $1 < @ $2 >			strip trailing dots
1811# handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
1812R<?> $* < @ $* $=P >	$: <_RES_OK_> $1 < @ $2 $3 >
1813dnl workspace <mark> CanonicalAddress	where mark is ? or OK
1814dnl A sender address with my local host name ($j) is safe
1815R<?> $* < @ $j >	$: <_RES_OK_> $1 < @ $j >
1816ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',
1817`R<?> $* < @ $+ >	$: <_RES_OK_> $1 < @ $2 >		... unresolvable OK',
1818`R<?> $* < @ $+ >	$: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 >
1819R<? $* <$->> $* < @ $+ >
1820			$: <$2> $3 < @ $4 >')
1821dnl workspace <mark> CanonicalAddress	where mark is ?, _RES_OK_, PERM, TEMP
1822dnl mark is ? iff the address is user (wo @domain)
1823
1824ifdef(`_ACCESS_TABLE_', `dnl
1825# check sender address: user@address, user@, address
1826dnl should we remove +ext from user?
1827dnl workspace: <mark> CanonicalAddress where mark is: ?, _RES_OK_, PERM, TEMP
1828R<$+> $+ < @ $* >	$: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <D:$3>
1829R<$+> $+		$: @<$1> <$2> $| <U:$2@>
1830dnl workspace: @<mark> <CanonicalAddress> $| <@type:address> ....
1831dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1832dnl will only return user<@domain when "reversing" the args
1833R@ <$+> <$*> $| <$+>	$: <@> <$1> <$2> $| $>SearchList <+ From> $| <$3> <>
1834dnl workspace: <@><mark> <CanonicalAddress> $| <result>
1835R<@> <$+> <$*> $| <$*>	$: <$3> <$1> <$2>		reverse result
1836dnl workspace: <result> <mark> <CanonicalAddress>
1837# retransform for further use
1838dnl required form:
1839dnl <ResultOfLookup|mark> CanonicalAddress
1840R<?> <$+> <$*>		$: <$1> $2	no match
1841R<$+> <$+> <$*>		$: <$1> $3	relevant result, keep it', `dnl')
1842dnl workspace <ResultOfLookup|mark> CanonicalAddress
1843dnl mark is ? iff the address is user (wo @domain)
1844
1845ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1846# handle case of no @domain on address
1847dnl prepend daemon_flags
1848R<?> $*			$: $&{daemon_flags} $| <?> $1
1849dnl accept unqualified sender: change mark to avoid test
1850R$* u $* $| <?> $*	$: <_RES_OK_> $3
1851dnl remove daemon_flags
1852R$* $| $*		$: $2
1853R<?> $*			$: < ? $&{client_addr} > $1
1854R<?> $*			$@ <_RES_OK_>			...local unqualed ok
1855R<? $+> $*		$#error $@ 5.5.4 $: "_CODE553 Domain name required for sender address " $&f
1856							...remote is not')
1857# check results
1858R<?> $*			$: @ $1		mark address: nothing known about it
1859R<$={ResOk}> $*		$@ <_RES_OK_>	domain ok: stop
1860R<TEMP> $*		$#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
1861R<PERM> $*		$#error $@ 5.1.8 $: "_CODE553 Domain of sender address " $&f " does not exist"
1862ifdef(`_ACCESS_TABLE_', `dnl
1863R<$={Accept}> $*	$# $1		accept from access map
1864R<DISCARD> $*		$#discard $: discard
1865ifdef(`_FFR_QUARANTINE',
1866`R<QUARANTINE:$+> $*	$#error $@ quarantine $: $1', `dnl')
1867R<REJECT> $*		$#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"')
1868dnl error tag
1869R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
1870R<ERROR:$+> $*		$#error $: $1
1871ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
1872dnl generic error from access map
1873R<$+> $*		$#error $: $1		error from access db',
1874`dnl')
1875
1876######################################################################
1877###  check_rcpt -- check SMTP ``RCPT TO:'' command argument
1878######################################################################
1879
1880SLocal_check_rcpt
1881Scheck`'_U_`'rcpt
1882R$*			$: $1 $| $>"Local_check_rcpt" $1
1883R$* $| $#$*		$#$2
1884R$* $| $*		$@ $>"Basic_check_rcpt" $1
1885
1886SBasic_check_rcpt
1887# empty address?
1888R<>			$#error $@ nouser $: "553 User address required"
1889R$@			$#error $@ nouser $: "553 User address required"
1890# check for deferred delivery mode
1891R$*			$: < $&{deliveryMode} > $1
1892R< d > $*		$@ deferred
1893R< $* > $*		$: $2
1894
1895ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
1896dnl this code checks for user@host where host is not a FQHN.
1897dnl it is not activated.
1898dnl notice: code to check for a recipient without a domain name is
1899dnl available down below; look for the same macro.
1900dnl this check is done here because the name might be qualified by the
1901dnl canonicalization.
1902# require fully qualified domain part?
1903dnl very simple canonification: make sure the address is in < >
1904R$+			$: <?> $1
1905R<?> <$+>		$: <@> <$1>
1906R<?> $+			$: <@> <$1>
1907R<@> < postmaster >	$: postmaster
1908R<@> < $* @ $+ . $+ >	$: < $1 @ $2 . $3 >
1909dnl prepend daemon_flags
1910R<@> $*			$: $&{daemon_flags} $| <@> $1
1911dnl workspace: ${daemon_flags} $| <@> <address>
1912dnl do not allow these at all or only from local systems?
1913R$* r $* $| <@> < $* @ $* >	$: < ? $&{client_name} > < $3 @ $4 >
1914R<?> < $* >		$: <$1>
1915R<? $=w> < $* >		$: <$1>
1916R<? $+> <$+>		$#error $@ 5.5.4 $: "553 Fully qualified domain name required"
1917dnl remove daemon_flags for other cases
1918R$* $| <@> $*		$: $2', `dnl')
1919
1920dnl ##################################################################
1921dnl call subroutines for recipient and relay
1922dnl possible returns from subroutines:
1923dnl $#TEMP	temporary failure
1924dnl $#error	permanent failure (or temporary if from access map)
1925dnl $#other	stop processing
1926dnl RELAY	RELAYing allowed
1927dnl other	otherwise
1928######################################################################
1929R$*			$: $1 $| @ $>"Rcpt_ok" $1
1930dnl temporary failure? remove mark @ and remember
1931R$* $| @ $#TEMP $+	$: $1 $| T $2
1932dnl error or ok (stop)
1933R$* $| @ $#$*		$#$2
1934ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
1935R$* $| @ RELAY		$@ RELAY
1936dnl something else: call check sender (relay)
1937R$* $| @ $*		$: O $| $>"Relay_ok" $1
1938dnl temporary failure: call check sender (relay)
1939R$* $| T $+		$: T $2 $| $>"Relay_ok" $1
1940dnl temporary failure? return that
1941R$* $| $#TEMP $+	$#error $2
1942dnl error or ok (stop)
1943R$* $| $#$*		$#$2
1944R$* $| RELAY		$@ RELAY
1945dnl something else: return previous temp failure
1946R T $+ $| $*		$#error $1
1947# anything else is bogus
1948R$*			$#error $@ 5.7.1 $: confRELAY_MSG
1949divert(0)
1950
1951######################################################################
1952### Rcpt_ok: is the recipient ok?
1953dnl input: recipient address (RCPT TO)
1954dnl output: see explanation at call
1955######################################################################
1956SRcpt_ok
1957ifdef(`_LOOSE_RELAY_CHECK_',`dnl
1958R$*			$: $>CanonAddr $1
1959R$* < @ $* . >		$1 < @ $2 >			strip trailing dots',
1960`R$*			$: $>ParseRecipient $1		strip relayable hosts')
1961
1962ifdef(`_BESTMX_IS_LOCAL_',`dnl
1963ifelse(_BESTMX_IS_LOCAL_, `', `dnl
1964# unlimited bestmx
1965R$* < @ $* > $*			$: $1 < @ $2 @@ $(bestmx $2 $) > $3',
1966`dnl
1967# limit bestmx to $=B
1968R$* < @ $* $=B > $*		$: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4')
1969R$* $=O $* < @ $* @@ $=w . > $*	$@ $>"Rcpt_ok" $1 $2 $3
1970R$* < @ $* @@ $=w . > $*	$: $1 < @ $3 > $4
1971R$* < @ $* @@ $* > $*		$: $1 < @ $2 > $4')
1972
1973ifdef(`_BLACKLIST_RCPT_',`dnl
1974ifdef(`_ACCESS_TABLE_', `dnl
1975# blacklist local users or any host from receiving mail
1976R$*			$: <?> $1
1977dnl user is now tagged with @ to be consistent with check_mail
1978dnl and to distinguish users from hosts (com would be host, com@ would be user)
1979R<?> $+ < @ $=w >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <D:$2>
1980R<?> $+ < @ $* >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <D:$2>
1981R<?> $+			$: <> <$1> $| <U:$1@>
1982dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1983dnl will only return user<@domain when "reversing" the args
1984R<> <$*> $| <$+>	$: <@> <$1> $| $>SearchList <+ To> $| <$2> <>
1985R<@> <$*> $| <$*>	$: <$2> <$1>		reverse result
1986R<?> <$*>		$: @ $1		mark address as no match
1987dnl we may have to filter here because otherwise some RHSs
1988dnl would be interpreted as generic error messages...
1989dnl error messages should be "tagged" by prefixing them with error: !
1990dnl that would make a lot of things easier.
1991R<$={Accept}> <$*>	$: @ $2		mark address as no match
1992ifdef(`_ACCESS_SKIP_', `dnl
1993R<SKIP> <$*>		$: @ $1		mark address as no match', `dnl')
1994ifdef(`_DELAY_COMPAT_8_10_',`dnl
1995dnl compatility with 8.11/8.10:
1996dnl we have to filter these because otherwise they would be interpreted
1997dnl as generic error message...
1998dnl error messages should be "tagged" by prefixing them with error: !
1999dnl that would make a lot of things easier.
2000dnl maybe we should stop checks already here (if SPAM_xyx)?
2001R<$={SpamTag}> <$*>	$: @ $2		mark address as no match')
2002R<REJECT> $*		$#error $@ 5.2.1 $: confRCPTREJ_MSG
2003R<DISCARD> $*		$#discard $: discard
2004ifdef(`_FFR_QUARANTINE',
2005`R<QUARANTINE:$+> $*	$#error $@ quarantine $: $1', `dnl')
2006dnl error tag
2007R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
2008R<ERROR:$+> $*		$#error $: $1
2009ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2010dnl generic error from access map
2011R<$+> $*		$#error $: $1		error from access db
2012R@ $*			$1		remove mark', `dnl')', `dnl')
2013
2014ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
2015# authenticated via TLS?
2016R$*			$: $1 $| $>RelayTLS	client authenticated?
2017R$* $| $# $+		$# $2			error/ok?
2018R$* $| $*		$: $1			no
2019
2020R$*			$: $1 $| $>"Local_Relay_Auth" $&{auth_type}
2021dnl workspace: localpart<@domain> $| result of Local_Relay_Auth
2022R$* $| $# $*		$# $2
2023dnl if Local_Relay_Auth returns NO then do not check $={TrustAuthMech}
2024R$* $| NO		$: $1
2025R$* $| $*		$: $1 $| $&{auth_type}
2026dnl workspace: localpart<@domain> [ $| ${auth_type} ]
2027dnl empty ${auth_type}?
2028R$* $|			$: $1
2029dnl mechanism ${auth_type} accepted?
2030dnl use $# to override further tests (delay_checks): see check_rcpt below
2031R$* $| $={TrustAuthMech}	$# RELAY
2032dnl remove ${auth_type}
2033R$* $| $*		$: $1
2034dnl workspace: localpart<@domain> | localpart
2035ifelse(defn(`_NO_UUCP_'), `r',
2036`R$* ! $* < @ $* >	$: <REMOTE> $2 < @ BANG_PATH >
2037R$* ! $* 		$: <REMOTE> $2 < @ BANG_PATH >', `dnl')
2038# anything terminating locally is ok
2039ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
2040R$+ < @ $* $=m >	$@ RELAY', `dnl')
2041R$+ < @ $=w >		$@ RELAY
2042ifdef(`_RELAY_HOSTS_ONLY_',
2043`R$+ < @ $=R >		$@ RELAY
2044ifdef(`_ACCESS_TABLE_', `dnl
2045R$+ < @ $+ >		$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>
2046dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
2047R<?> <$+ < @ $+ >>	$: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
2048`R$+ < @ $* $=R >	$@ RELAY
2049ifdef(`_ACCESS_TABLE_', `dnl
2050R$+ < @ $+ >		$: $>D <$2> <?> <+ To> <$1 < @ $2 >>',`dnl')')
2051ifdef(`_ACCESS_TABLE_', `dnl
2052dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
2053R<RELAY> $*		$@ RELAY
2054ifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2055R<$*> <$*>		$: $2',`dnl')
2056
2057
2058ifdef(`_RELAY_MX_SERVED_', `dnl
2059# allow relaying for hosts which we MX serve
2060R$+ < @ $+ >		$: < : $(mxserved $2 $) : > $1 < @ $2 >
2061dnl this must not necessarily happen if the client is checked first...
2062R< : $* <TEMP> : > $*	$#TEMP $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1
2063R<$* : $=w . : $*> $*	$@ RELAY
2064R< : $* : > $*		$: $2',
2065`dnl')
2066
2067# check for local user (i.e. unqualified address)
2068R$*			$: <?> $1
2069R<?> $* < @ $+ >	$: <REMOTE> $1 < @ $2 >
2070# local user is ok
2071dnl is it really? the standard requires user@domain, not just user
2072dnl but we should accept it anyway (maybe making it an option:
2073dnl RequireFQDN ?)
2074dnl postmaster must be accepted without domain (DRUMS)
2075ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
2076R<?> postmaster		$@ OK
2077# require qualified recipient?
2078dnl prepend daemon_flags
2079R<?> $+			$: $&{daemon_flags} $| <?> $1
2080dnl workspace: ${daemon_flags} $| <?> localpart
2081dnl do not allow these at all or only from local systems?
2082dnl r flag? add client_name
2083R$* r $* $| <?> $+	$: < ? $&{client_name} > <?> $3
2084dnl no r flag: relay to local user (only local part)
2085# no qualified recipient required
2086R$* $| <?> $+		$@ RELAY
2087dnl client_name is empty
2088R<?> <?> $+		$@ RELAY
2089dnl client_name is local
2090R<? $=w> <?> $+		$@ RELAY
2091dnl client_name is not local
2092R<? $+> $+		$#error $@ 5.5.4 $: "553 Domain name required"', `dnl
2093dnl no qualified recipient required
2094R<?> $+			$@ RELAY')
2095dnl it is a remote user: remove mark and then check client
2096R<$+> $*		$: $2
2097dnl currently the recipient address is not used below
2098
2099######################################################################
2100### Relay_ok: is the relay/sender ok?
2101dnl input: ignored
2102dnl output: see explanation at call
2103######################################################################
2104SRelay_ok
2105# anything originating locally is ok
2106# check IP address
2107R$*			$: $&{client_addr}
2108R$@			$@ RELAY		originated locally
2109R0			$@ RELAY		originated locally
2110R127.0.0.1		$@ RELAY		originated locally
2111RIPv6:::1		$@ RELAY		originated locally
2112R$=R $*			$@ RELAY		relayable IP address
2113ifdef(`_ACCESS_TABLE_', `dnl
2114R$*			$: $>A <$1> <?> <+ Connect> <$1>
2115R<RELAY> $* 		$@ RELAY		relayable IP address
2116ifdef(`_FFR_REJECT_IP_IN_CHECK_RCPT_',`dnl
2117dnl this will cause rejections in cases like:
2118dnl Connect:My.Host.Domain	RELAY
2119dnl Connect:My.Net		REJECT
2120dnl since in check_relay client_name is checked before client_addr
2121R<REJECT> $* 		$@ REJECT		rejected IP address')
2122ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2123R<$*> <$*>		$: $2', `dnl')
2124R$*			$: [ $1 ]		put brackets around it...
2125R$=w			$@ RELAY		... and see if it is local
2126
2127ifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
2128ifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
2129ifdef(`_RELAY_MAIL_FROM_', `dnl
2130dnl input: {client_addr} or something "broken"
2131dnl just throw the input away; we do not need it.
2132# check whether FROM is allowed to use system as relay
2133R$*			$: <?> $>CanonAddr $&f
2134R<?> $+ < @ $+ . >	<?> $1 < @ $2 >		remove trailing dot
2135ifdef(`_RELAY_LOCAL_FROM_', `dnl
2136# check whether local FROM is ok
2137R<?> $+ < @ $=w >	$@ RELAY		FROM local', `dnl')
2138ifdef(`_RELAY_DB_FROM_', `dnl
2139R<?> $+ < @ $+ >	$: <@> $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', ifdef(`_RELAY_HOSTS_ONLY_', `<E:$2>', `<D:$2>')) <>
2140R<@> <RELAY>		$@ RELAY		RELAY FROM sender ok
2141ifdef(`_ATMPF_', `R<@> <_ATMPF_>		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2142', `dnl
2143ifdef(`_RELAY_DB_FROM_DOMAIN_',
2144`errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_
2145')',
2146`dnl')
2147dnl')', `dnl')
2148dnl notice: the rulesets above do not leave a unique workspace behind.
2149dnl it does not matter in this case because the following rule ignores
2150dnl the input. otherwise these rules must "clean up" the workspace.
2151
2152# check client name: first: did it resolve?
2153dnl input: ignored
2154R$*			$: < $&{client_resolve} >
2155R<TEMP>			$#TEMP $@ 4.7.1 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr}
2156R<FORGED>		$#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name}
2157R<FAIL>			$#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name}
2158dnl ${client_resolve} should be OK, so go ahead
2159R$*			$: <@> $&{client_name}
2160dnl should not be necessary since it has been done for client_addr already
2161dnl this rule actually may cause a problem if {client_name} resolves to ""
2162dnl however, this should not happen since the forward lookup should fail
2163dnl and {client_resolve} should be TEMP or FAIL.
2164dnl nevertheless, removing the rule doesn't hurt.
2165dnl R<@>			$@ RELAY
2166dnl workspace: <@> ${client_name} (not empty)
2167# pass to name server to make hostname canonical
2168R<@> $* $=P 		$:<?>  $1 $2
2169R<@> $+			$:<?>  $[ $1 $]
2170dnl workspace: <?> ${client_name} (canonified)
2171R$* .			$1			strip trailing dots
2172ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
2173R<?> $* $=m		$@ RELAY', `dnl')
2174R<?> $=w		$@ RELAY
2175ifdef(`_RELAY_HOSTS_ONLY_',
2176`R<?> $=R		$@ RELAY
2177ifdef(`_ACCESS_TABLE_', `dnl
2178R<?> $*			$: <$(access Connect:$1 $: ? $)> <$1>
2179R<?> <$*>		$: <$(access $1 $: ? $)> <$1>',`dnl')',
2180`R<?> $* $=R			$@ RELAY
2181ifdef(`_ACCESS_TABLE_', `dnl
2182R<?> $*			$: $>D <$1> <?> <+ Connect> <$1>',`dnl')')
2183ifdef(`_ACCESS_TABLE_', `dnl
2184R<RELAY> $*		$@ RELAY
2185ifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2186R<$*> <$*>		$: $2',`dnl')
2187dnl end of _PROMISCUOUS_RELAY_
2188divert(0)
2189ifdef(`_DELAY_CHECKS_',`dnl
2190# turn a canonical address in the form user<@domain>
2191# qualify unqual. addresses with $j
2192dnl it might have been only user (without <@domain>)
2193SFullAddr
2194R$* <@ $+ . >		$1 <@ $2 >
2195R$* <@ $* >		$@ $1 <@ $2 >
2196R$+			$@ $1 <@ $j >
2197
2198SDelay_TLS_Client
2199# authenticated?
2200dnl code repeated here from Basic_check_mail
2201dnl only called from check_rcpt in delay mode if checkrcpt returns $#
2202R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
2203R$* $| $#$+		$#$2
2204dnl return result from checkrcpt
2205R$*			$# $1
2206
2207SDelay_TLS_Client2
2208# authenticated?
2209dnl code repeated here from Basic_check_mail
2210dnl only called from check_rcpt in delay mode if stopping due to Friend/Hater
2211R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
2212R$* $| $#$+		$#$2
2213dnl return result from friend/hater check
2214R$*			$@ $1
2215
2216# call all necessary rulesets
2217Scheck_rcpt
2218dnl this test should be in the Basic_check_rcpt ruleset
2219dnl which is the correct DSN code?
2220# R$@			$#error $@ 5.1.3 $: "553 Recipient address required"
2221
2222R$+			$: $1 $| $>checkrcpt $1
2223dnl now we can simply stop checks by returning "$# xyz" instead of just "ok"
2224dnl on error (or discard) stop now
2225R$+ $| $#error $*	$#error $2
2226R$+ $| $#discard $*	$#discard $2
2227dnl otherwise call tls_client; see above
2228R$+ $| $#$*		$@ $>"Delay_TLS_Client" $2
2229R$+ $| $*		$: <?> $>FullAddr $>CanonAddr $1
2230ifdef(`_SPAM_FH_',
2231`dnl lookup user@ and user@address
2232ifdef(`_ACCESS_TABLE_', `',
2233`errprint(`*** ERROR: FEATURE(`delay_checks', `argument') requires FEATURE(`access_db')
2234')')dnl
2235dnl one of the next two rules is supposed to match
2236dnl this code has been copied from BLACKLIST... etc
2237dnl and simplified by omitting some < >.
2238R<?> $+ < @ $=w >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 > <U: $1@>
2239R<?> $+ < @ $* >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 >
2240dnl R<?>		$@ something_is_very_wrong_here
2241# lookup the addresses only with Spam tag
2242R<> $* $| <$+>		$: <@> $1 $| $>SearchList <! Spam> $| <$2> <>
2243R<@> $* $| $*		$: $2 $1		reverse result
2244dnl', `dnl')
2245ifdef(`_SPAM_FRIEND_',
2246`# is the recipient a spam friend?
2247ifdef(`_SPAM_HATER_',
2248	`errprint(`*** ERROR: define either Hater or Friend -- not both.
2249')', `dnl')
2250R<FRIEND> $+		$@ $>"Delay_TLS_Client2" SPAMFRIEND
2251R<$*> $+		$: $2',
2252`dnl')
2253ifdef(`_SPAM_HATER_',
2254`# is the recipient no spam hater?
2255R<HATER> $+		$: $1			spam hater: continue checks
2256R<$*> $+		$@ $>"Delay_TLS_Client2" NOSPAMHATER	everyone else: stop
2257dnl',`dnl')
2258dnl run further checks: check_mail
2259dnl should we "clean up" $&f?
2260ifdef(`_FFR_MAIL_MACRO',
2261`R$*			$: $1 $| $>checkmail $&{mail_from}',
2262`R$*			$: $1 $| $>checkmail <$&f>')
2263dnl recipient (canonical format) $| result of checkmail
2264R$* $| $#$*		$#$2
2265dnl run further checks: check_relay
2266R$* $| $*		$: $1 $| $>checkrelay $&{client_name} $| $&{client_addr}
2267R$* $| $#$*		$#$2
2268R$* $| $*		$: $1
2269', `dnl')
2270
2271ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
2272######################################################################
2273###  F: LookUpFull -- search for an entry in access database
2274###
2275###	lookup of full key (which should be an address) and
2276###	variations if +detail exists: +* and without +detail
2277###
2278###	Parameters:
2279###		<$1> -- key
2280###		<$2> -- default (what to return if not found in db)
2281dnl			must not be empty
2282###		<$3> -- mark (must be <(!|+) single-token>)
2283###			! does lookup only with tag
2284###			+ does lookup with and without tag
2285###		<$4> -- passthru (additional data passed unchanged through)
2286dnl returns:		<default> <passthru>
2287dnl 			<result> <passthru>
2288######################################################################
2289
2290SF
2291dnl workspace: <key> <def> <o tag> <thru>
2292dnl full lookup
2293dnl    2    3  4    5
2294R<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2295dnl no match, try without tag
2296dnl   1    2      3    4
2297R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2298dnl no match, +detail: try +*
2299dnl   1    2    3    4    5  6    7
2300R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
2301			$: <$(access $6`'_TAG_DELIM_`'$1+*@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
2302dnl no match, +detail: try +* without tag
2303dnl   1    2    3    4      5    6
2304R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
2305			$: <$(access $1+*@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2306dnl no match, +detail: try without +detail
2307dnl   1    2    3    4    5  6    7
2308R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
2309			$: <$(access $6`'_TAG_DELIM_`'$1@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
2310dnl no match, +detail: try without +detail and without tag
2311dnl   1    2    3    4      5    6
2312R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
2313			$: <$(access $1@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2314dnl no match, return <default> <passthru>
2315dnl   1    2    3  4    5
2316R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
2317ifdef(`_ATMPF_', `dnl tempfail?
2318dnl            2    3  4    5
2319R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
2320dnl match, return <match> <passthru>
2321dnl    2    3  4    5
2322R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
2323
2324######################################################################
2325###  E: LookUpExact -- search for an entry in access database
2326###
2327###	Parameters:
2328###		<$1> -- key
2329###		<$2> -- default (what to return if not found in db)
2330dnl			must not be empty
2331###		<$3> -- mark (must be <(!|+) single-token>)
2332###			! does lookup only with tag
2333###			+ does lookup with and without tag
2334###		<$4> -- passthru (additional data passed unchanged through)
2335dnl returns:		<default> <passthru>
2336dnl 			<result> <passthru>
2337######################################################################
2338
2339SE
2340dnl    2    3  4    5
2341R<$*> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2342dnl no match, try without tag
2343dnl   1    2      3    4
2344R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2345dnl no match, return default passthru
2346dnl   1    2    3  4    5
2347R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
2348ifdef(`_ATMPF_', `dnl tempfail?
2349dnl            2    3  4    5
2350R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
2351dnl match, return <match> <passthru>
2352dnl    2    3  4    5
2353R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
2354
2355######################################################################
2356###  U: LookUpUser -- search for an entry in access database
2357###
2358###	lookup of key (which should be a local part) and
2359###	variations if +detail exists: +* and without +detail
2360###
2361###	Parameters:
2362###		<$1> -- key (user@)
2363###		<$2> -- default (what to return if not found in db)
2364dnl			must not be empty
2365###		<$3> -- mark (must be <(!|+) single-token>)
2366###			! does lookup only with tag
2367###			+ does lookup with and without tag
2368###		<$4> -- passthru (additional data passed unchanged through)
2369dnl returns:		<default> <passthru>
2370dnl 			<result> <passthru>
2371######################################################################
2372
2373SU
2374dnl user lookups are always with trailing @
2375dnl    2    3  4    5
2376R<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2377dnl no match, try without tag
2378dnl   1    2      3    4
2379R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2380dnl do not remove the @ from the lookup:
2381dnl it is part of the +detail@ which is omitted for the lookup
2382dnl no match, +detail: try +*
2383dnl   1    2      3    4  5    6
2384R<?> <$+ + $* @> <$*> <$- $-> <$*>
2385			$: <$(access $5`'_TAG_DELIM_`'$1+*@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
2386dnl no match, +detail: try +* without tag
2387dnl   1    2      3      4    5
2388R<?> <$+ + $* @> <$*> <+ $-> <$*>
2389			$: <$(access $1+*@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
2390dnl no match, +detail: try without +detail
2391dnl   1    2      3    4  5    6
2392R<?> <$+ + $* @> <$*> <$- $-> <$*>
2393			$: <$(access $5`'_TAG_DELIM_`'$1@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
2394dnl no match, +detail: try without +detail and without tag
2395dnl   1    2      3      4    5
2396R<?> <$+ + $* @> <$*> <+ $-> <$*>
2397			$: <$(access $1@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
2398dnl no match, return <default> <passthru>
2399dnl   1    2    3  4    5
2400R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
2401ifdef(`_ATMPF_', `dnl tempfail?
2402dnl            2    3  4    5
2403R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
2404dnl match, return <match> <passthru>
2405dnl    2    3  4    5
2406R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
2407
2408######################################################################
2409###  SearchList: search a list of items in the access map
2410###	Parameters:
2411###		<exact tag> $| <mark:address> <mark:address> ... <>
2412dnl	maybe we should have a @ (again) in front of the mark to
2413dnl	avoid errorneous matches (with error messages?)
2414dnl	if we can make sure that tag is always a single token
2415dnl	then we can omit the delimiter $|, otherwise we need it
2416dnl	to avoid errorneous matchs (first rule: D: if there
2417dnl	is that mark somewhere in the list, it will be taken).
2418dnl	moreover, we can do some tricks to enforce lookup with
2419dnl	the tag only, e.g.:
2420###	where "exact" is either "+" or "!":
2421###	<+ TAG>	lookup with and w/o tag
2422###	<! TAG>	lookup with tag
2423dnl	Warning: + and ! should be in OperatorChars (otherwise there must be
2424dnl		a blank between them and the tag.
2425###	possible values for "mark" are:
2426###		D: recursive host lookup (LookUpDomain)
2427dnl		A: recursive address lookup (LookUpAddress) [not yet required]
2428###		E: exact lookup, no modifications
2429###		F: full lookup, try user+ext@domain and user@domain
2430###		U: user lookup, try user+ext and user (input must have trailing @)
2431###	return: <RHS of lookup> or <?> (not found)
2432######################################################################
2433
2434# class with valid marks for SearchList
2435dnl if A is activated: add it
2436C{src}E F D U ifdef(`_FFR_SRCHLIST_A', `A')
2437SSearchList
2438# just call the ruleset with the name of the tag... nice trick...
2439dnl       2       3    4
2440R<$+> $| <$={src}:$*> <$*>	$: <$1> $| <$4> $| $>$2 <$3> <?> <$1> <>
2441dnl workspace: <o tag> $| <rest> $| <result of lookup> <>
2442dnl no match and nothing left: return
2443R<$+> $| <> $| <?> <>		$@ <?>
2444dnl no match but something left: continue
2445R<$+> $| <$+> $| <?> <>		$@ $>SearchList <$1> $| <$2>
2446dnl match: return
2447R<$+> $| <$*> $| <$+> <>	$@ <$3>
2448dnl return result from recursive invocation
2449R<$+> $| <$+>			$@ <$2>
2450dnl endif _ACCESS_TABLE_
2451divert(0)
2452
2453######################################################################
2454###  trust_auth: is user trusted to authenticate as someone else?
2455###
2456###	Parameters:
2457###		$1: AUTH= parameter from MAIL command
2458######################################################################
2459
2460dnl empty ruleset definition so it can be called
2461SLocal_trust_auth
2462Strust_auth
2463R$*			$: $&{auth_type} $| $1
2464# required by RFC 2554 section 4.
2465R$@ $| $*		$#error $@ 5.7.1 $: "550 not authenticated"
2466dnl seems to be useful...
2467R$* $| $&{auth_authen}		$@ identical
2468R$* $| <$&{auth_authen}>	$@ identical
2469dnl call user supplied code
2470R$* $| $*		$: $1 $| $>"Local_trust_auth" $1
2471R$* $| $#$*		$#$2
2472dnl default: error
2473R$*			$#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author}
2474
2475######################################################################
2476###  Relay_Auth: allow relaying based on authentication?
2477###
2478###	Parameters:
2479###		$1: ${auth_type}
2480######################################################################
2481SLocal_Relay_Auth
2482
2483ifdef(`_ACCESS_TABLE_', `dnl
2484######################################################################
2485###  srv_features: which features to offer to a client?
2486###	(done in server)
2487######################################################################
2488Ssrv_features
2489ifdef(`_LOCAL_SRV_FEATURES_', `dnl
2490R$*			$: $1 $| $>"Local_srv_features" $1
2491R$* $| $#$*		$#$2
2492R$* $| $*		$: $1', `dnl')
2493R$*		$: $>D <$&{client_name}> <?> <! SRV_FEAT_TAG> <>
2494R<?>$*		$: $>A <$&{client_addr}> <?> <! SRV_FEAT_TAG> <>
2495R<?>$*		$: <$(access SRV_FEAT_TAG`'_TAG_DELIM_ $: ? $)>
2496R<?>$*		$@ OK
2497ifdef(`_ATMPF_', `dnl tempfail?
2498R<$* _ATMPF_>$*	$#temp', `dnl')
2499R<$+>$*		$# $1
2500
2501######################################################################
2502###  try_tls: try to use STARTTLS?
2503###	(done in client)
2504######################################################################
2505Stry_tls
2506ifdef(`_LOCAL_TRY_TLS_', `dnl
2507R$*			$: $1 $| $>"Local_try_tls" $1
2508R$* $| $#$*		$#$2
2509R$* $| $*		$: $1', `dnl')
2510R$*		$: $>D <$&{server_name}> <?> <! TLS_TRY_TAG> <>
2511R<?>$*		$: $>A <$&{server_addr}> <?> <! TLS_TRY_TAG> <>
2512R<?>$*		$: <$(access TLS_TRY_TAG`'_TAG_DELIM_ $: ? $)>
2513R<?>$*		$@ OK
2514ifdef(`_ATMPF_', `dnl tempfail?
2515R<$* _ATMPF_>$*	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2516R<NO>$*		$#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"
2517 
2518######################################################################
2519###  tls_rcpt: is connection with server "good" enough?
2520###	(done in client, per recipient)
2521dnl called from deliver() before RCPT command
2522###
2523###	Parameters:
2524###		$1: recipient
2525######################################################################
2526Stls_rcpt
2527ifdef(`_LOCAL_TLS_RCPT_', `dnl
2528R$*			$: $1 $| $>"Local_tls_rcpt" $1
2529R$* $| $#$*		$#$2
2530R$* $| $*		$: $1', `dnl')
2531dnl store name of other side
2532R$*			$: $(macro {TLS_Name} $@ $&{server_name} $) $1
2533dnl canonify recipient address
2534R$+			$: <?> $>CanonAddr $1
2535dnl strip trailing dots
2536R<?> $+ < @ $+ . >	<?> $1 <@ $2 >
2537dnl full address?
2538R<?> $+ < @ $+ >	$: $1 <@ $2 > $| <F:$1@$2> <U:$1@> <D:$2> <E:>
2539dnl only localpart?
2540R<?> $+			$: $1 $| <U:$1@> <E:>
2541dnl look it up
2542dnl also look up a default value via E:
2543R$* $| $+	$: $1 $| $>SearchList <! TLS_RCPT_TAG> $| $2 <>
2544dnl found nothing: stop here
2545R$* $| <?>	$@ OK
2546ifdef(`_ATMPF_', `dnl tempfail?
2547R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2548dnl use the generic routine (for now)
2549R$* $| <$+>	$@ $>"TLS_connection" $&{verify} $| <$2>')
2550
2551######################################################################
2552###  tls_client: is connection with client "good" enough?
2553###	(done in server)
2554###
2555###	Parameters:
2556###		${verify} $| (MAIL|STARTTLS)
2557######################################################################
2558dnl MAIL: called from check_mail
2559dnl STARTTLS: called from smtp() after STARTTLS has been accepted
2560Stls_client
2561ifdef(`_LOCAL_TLS_CLIENT_', `dnl
2562R$*			$: $1 $| $>"Local_tls_client" $1
2563R$* $| $#$*		$#$2
2564R$* $| $*		$: $1', `dnl')
2565ifdef(`_ACCESS_TABLE_', `dnl
2566dnl store name of other side
2567R$*		$: $(macro {TLS_Name} $@ $&{server_name} $) $1
2568dnl ignore second arg for now
2569dnl maybe use it to distinguish permanent/temporary error?
2570dnl if MAIL: permanent (STARTTLS has not been offered)
2571dnl if STARTTLS: temporary (offered but maybe failed)
2572R$* $| $*	$: $1 $| $>D <$&{client_name}> <?> <! TLS_CLT_TAG> <>
2573R$* $| <?>$*	$: $1 $| $>A <$&{client_addr}> <?> <! TLS_CLT_TAG> <>
2574dnl do a default lookup: just TLS_CLT_TAG
2575R$* $| <?>$*	$: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)>
2576ifdef(`_ATMPF_', `dnl tempfail?
2577R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2578R$*		$@ $>"TLS_connection" $1', `dnl
2579R$* $| $*	$@ $>"TLS_connection" $1')
2580
2581######################################################################
2582###  tls_server: is connection with server "good" enough?
2583###	(done in client)
2584###
2585###	Parameter:
2586###		${verify}
2587######################################################################
2588dnl i.e. has the server been authenticated and is encryption active?
2589dnl called from deliver() after STARTTLS command
2590Stls_server
2591ifdef(`_LOCAL_TLS_SERVER_', `dnl
2592R$*			$: $1 $| $>"Local_tls_server" $1
2593R$* $| $#$*		$#$2
2594R$* $| $*		$: $1', `dnl')
2595ifdef(`_ACCESS_TABLE_', `dnl
2596dnl store name of other side
2597R$*		$: $(macro {TLS_Name} $@ $&{server_name} $) $1
2598R$*		$: $1 $| $>D <$&{server_name}> <?> <! TLS_SRV_TAG> <>
2599R$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! TLS_SRV_TAG> <>
2600dnl do a default lookup: just TLS_SRV_TAG
2601R$* $| <?>$*	$: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)>
2602ifdef(`_ATMPF_', `dnl tempfail?
2603R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2604R$*		$@ $>"TLS_connection" $1', `dnl
2605R$*		$@ $>"TLS_connection" $1')
2606
2607######################################################################
2608###  TLS_connection: is TLS connection "good" enough?
2609###
2610###	Parameters:
2611ifdef(`_ACCESS_TABLE_', `dnl
2612###		${verify} $| <Requirement> [<>]', `dnl
2613###		${verify}')
2614###		Requirement: RHS from access map, may be ? for none.
2615dnl	syntax for Requirement:
2616dnl	[(PERM|TEMP)+] (VERIFY[:bits]|ENCR:bits) [+extensions]
2617dnl	extensions: could be a list of further requirements
2618dnl		for now: CN:string	{cn_subject} == string
2619######################################################################
2620STLS_connection
2621ifdef(`_ACCESS_TABLE_', `dnl', `dnl use default error
2622dnl deal with TLS handshake failures: abort
2623RSOFTWARE	$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake."
2624divert(-1)')
2625dnl common ruleset for tls_{client|server}
2626dnl input: ${verify} $| <ResultOfLookup> [<>]
2627dnl remove optional <>
2628R$* $| <$*>$*			$: $1 $| <$2>
2629dnl workspace: ${verify} $| <ResultOfLookup>
2630# create the appropriate error codes
2631dnl permanent or temporary error?
2632R$* $| <PERM + $={tls} $*>	$: $1 $| <503:5.7.0> <$2 $3>
2633R$* $| <TEMP + $={tls} $*>	$: $1 $| <403:4.7.0> <$2 $3>
2634dnl default case depends on TLS_PERM_ERR
2635R$* $| <$={tls} $*>		$: $1 $| <ifdef(`TLS_PERM_ERR', `503:5.7.0', `403:4.7.0')> <$2 $3>
2636dnl workspace: ${verify} $| [<SMTP:ESC>] <ResultOfLookup>
2637# deal with TLS handshake failures: abort
2638RSOFTWARE $| <$-:$+> $* 	$#error $@ $2 $: $1 " TLS handshake failed."
2639dnl no <reply:dns> i.e. not requirements in the access map
2640dnl use default error
2641RSOFTWARE $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed."
2642R$* $| <$*> <VERIFY>		$: <$2> <VERIFY> <> $1
2643dnl separate optional requirements
2644R$* $| <$*> <VERIFY + $+>	$: <$2> <VERIFY> <$3> $1
2645R$* $| <$*> <$={tls}:$->$*	$: <$2> <$3:$4> <> $1
2646dnl separate optional requirements
2647R$* $| <$*> <$={tls}:$- + $+>$*	$: <$2> <$3:$4> <$5> $1
2648dnl some other value in access map: accept
2649dnl this also allows to override the default case (if used)
2650R$* $| $*			$@ OK
2651# authentication required: give appropriate error
2652# other side did authenticate (via STARTTLS)
2653dnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> <[extensions]> ${verify}
2654dnl only verification required and it succeeded
2655R<$*><VERIFY> <> OK		$@ OK
2656dnl verification required and it succeeded but extensions are given
2657dnl change it to <SMTP:ESC> <REQ:0>  <extensions>
2658R<$*><VERIFY> <$+> OK		$: <$1> <REQ:0> <$2>
2659dnl verification required + some level of encryption
2660R<$*><VERIFY:$-> <$*> OK	$: <$1> <REQ:$2> <$3>
2661dnl just some level of encryption required
2662R<$*><ENCR:$-> <$*> $*		$: <$1> <REQ:$2> <$3>
2663dnl workspace:
2664dnl 1. <SMTP:ESC> <VERIFY [:bits]>  <[extensions]> {verify} (!= OK)
2665dnl 2. <SMTP:ESC> <REQ:bits>  <[extensions]>
2666dnl verification required but ${verify} is not set (case 1.)
2667R<$-:$+><VERIFY $*> <$*>	$#error $@ $2 $: $1 " authentication required"
2668R<$-:$+><VERIFY $*> <$*> FAIL	$#error $@ $2 $: $1 " authentication failed"
2669R<$-:$+><VERIFY $*> <$*> NO	$#error $@ $2 $: $1 " not authenticated"
2670R<$-:$+><VERIFY $*> <$*> NOT	$#error $@ $2 $: $1 " no authentication requested"
2671R<$-:$+><VERIFY $*> <$*> NONE	$#error $@ $2 $: $1 " other side does not support STARTTLS"
2672dnl some other value for ${verify}
2673R<$-:$+><VERIFY $*> <$*> $+	$#error $@ $2 $: $1 " authentication failure " $4
2674dnl some level of encryption required: get the maximum level (case 2.)
2675R<$*><REQ:$-> <$*>		$: <$1> <REQ:$2> <$3> $>max $&{cipher_bits} : $&{auth_ssf}
2676dnl compare required bits with actual bits
2677R<$*><REQ:$-> <$*> $-		$: <$1> <$2:$4> <$3> $(arith l $@ $4 $@ $2 $)
2678R<$-:$+><$-:$-> <$*> TRUE	$#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3
2679dnl strength requirements fulfilled
2680dnl TLS Additional Requirements Separator
2681dnl this should be something which does not appear in the extensions itself
2682dnl @ could be part of a CN, DN, etc...
2683dnl use < > ? those are encoded in CN, DN, ...
2684define(`_TLS_ARS_', `++')dnl
2685dnl workspace:
2686dnl <SMTP:ESC> <REQ:bits> <extensions> result-of-compare
2687R<$-:$+><$-:$-> <$*> $*		$: <$1:$2 _TLS_ARS_ $5>
2688dnl workspace: <SMTP:ESC _TLS_ARS_ extensions>
2689dnl continue: check  extensions
2690R<$-:$+ _TLS_ARS_ >			$@ OK
2691dnl split extensions into own list
2692R<$-:$+ _TLS_ARS_ $+ >			$: <$1:$2> <$3>
2693R<$-:$+> < $+ _TLS_ARS_ $+ >		<$1:$2> <$3> <$4>
2694R<$-:$+> $+			$@ $>"TLS_req" $3 $| <$1:$2>
2695
2696######################################################################
2697###  TLS_req: check additional TLS requirements
2698###
2699###	Parameters: [<list> <of> <req>] $| <$-:$+>
2700###		$-: SMTP reply code
2701###		$+: Enhanced Status Code
2702dnl  further requirements for this ruleset:
2703dnl	name of "other side" is stored is {TLS_name} (client/server_name)
2704dnl
2705dnl	currently only CN[:common_name] is implemented
2706dnl	right now this is only a logical AND
2707dnl	i.e. all requirements must be true
2708dnl	how about an OR? CN must be X or CN must be Y or ..
2709dnl	use a macro to compute this as a trivial sequential
2710dnl	operations (no precedences etc)?
2711######################################################################
2712STLS_req
2713dnl no additional requirements: ok
2714R $| $+		$@ OK
2715dnl require CN: but no CN specified: use name of other side
2716R<CN> $* $| <$+>		$: <CN:$&{TLS_Name}> $1 $| <$2>
2717dnl match, check rest
2718R<CN:$&{cn_subject}> $* $| <$+>		$@ $>"TLS_req" $1 $| <$2>
2719dnl CN does not match
2720dnl  1   2      3  4
2721R<CN:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " CN " $&{cn_subject} " does not match " $1
2722dnl cert subject
2723R<CS:$&{cert_subject}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
2724dnl CS does not match
2725dnl  1   2      3  4
2726R<CS:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " Cert Subject " $&{cert_subject} " does not match " $1
2727dnl match, check rest
2728R<CI:$&{cert_issuer}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
2729dnl CI does not match
2730dnl  1   2      3  4
2731R<CI:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " Cert Issuer " $&{cert_issuer} " does not match " $1
2732dnl return from recursive call
2733ROK			$@ OK
2734
2735######################################################################
2736###  max: return the maximum of two values separated by :
2737###
2738###	Parameters: [$-]:[$-]
2739######################################################################
2740Smax
2741R:		$: 0
2742R:$-		$: $1
2743R$-:		$: $1
2744R$-:$-		$: $(arith l $@ $1 $@ $2 $) : $1 : $2
2745RTRUE:$-:$-	$: $2
2746R$-:$-:$-	$: $2
2747dnl endif _ACCESS_TABLE_
2748divert(0)
2749
2750######################################################################
2751###  RelayTLS: allow relaying based on TLS authentication
2752###
2753###	Parameters:
2754###		none
2755######################################################################
2756SRelayTLS
2757# authenticated?
2758dnl we do not allow relaying for anyone who can present a cert
2759dnl signed by a "trusted" CA. For example, even if we put verisigns
2760dnl CA in CertPath so we can authenticate users, we do not allow
2761dnl them to abuse our server (they might be easier to get hold of,
2762dnl but anyway).
2763dnl so here is the trick: if the verification succeeded
2764dnl we look up the cert issuer in the access map
2765dnl (maybe after extracting a part with a regular expression)
2766dnl if this returns RELAY we relay without further questions
2767dnl if it returns SUBJECT we perform a similar check on the
2768dnl cert subject.
2769ifdef(`_ACCESS_TABLE_', `dnl
2770R$*			$: <?> $&{verify}
2771R<?> OK			$: OK		authenticated: continue
2772R<?> $*			$@ NO		not authenticated
2773ifdef(`_CERT_REGEX_ISSUER_', `dnl
2774R$*			$: $(CERTIssuer $&{cert_issuer} $)',
2775`R$*			$: $&{cert_issuer}')
2776R$+			$: $(access CERTISSUER`'_TAG_DELIM_`'$1 $)
2777dnl use $# to stop further checks (delay_check)
2778RRELAY			$# RELAY
2779ifdef(`_CERT_REGEX_SUBJECT_', `dnl
2780RSUBJECT		$: <@> $(CERTSubject $&{cert_subject} $)',
2781`RSUBJECT		$: <@> $&{cert_subject}')
2782R<@> $+			$: <@> $(access CERTSUBJECT`'_TAG_DELIM_`'$1 $)
2783R<@> RELAY		$# RELAY
2784R$*			$: NO', `dnl')
2785
2786######################################################################
2787###  authinfo: lookup authinfo in the access map
2788###
2789###	Parameters:
2790###		$1: {server_name}
2791###		$2: {server_addr}
2792dnl	both are currently ignored
2793dnl if it should be done via another map, we either need to restrict
2794dnl functionality (it calls D and A) or copy those rulesets (or add another
2795dnl parameter which I want to avoid, it's quite complex already)
2796######################################################################
2797dnl omit this ruleset if neither is defined?
2798dnl it causes DefaultAuthInfo to be ignored
2799dnl (which may be considered a good thing).
2800Sauthinfo
2801ifdef(`_AUTHINFO_TABLE_', `dnl
2802R$*		$: <$(authinfo AuthInfo:$&{server_name} $: ? $)>
2803R<?>		$: <$(authinfo AuthInfo:$&{server_addr} $: ? $)>
2804R<?>		$: <$(authinfo AuthInfo: $: ? $)>
2805R<?>		$@ no				no authinfo available
2806R<$*>		$# $1
2807dnl', `dnl
2808ifdef(`_ACCESS_TABLE_', `dnl
2809R$*		$: $1 $| $>D <$&{server_name}> <?> <! AuthInfo> <>
2810R$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! AuthInfo> <>
2811R$* $| <?>$*	$: $1 $| <$(access AuthInfo`'_TAG_DELIM_ $: ? $)> <>
2812R$* $| <?>$*	$@ no				no authinfo available
2813R$* $| <$*> <>	$# $2
2814dnl', `dnl')')
2815
2816undivert(9)dnl LOCAL_RULESETS
2817#
2818######################################################################
2819######################################################################
2820#####
2821`#####			MAIL FILTER DEFINITIONS'
2822#####
2823######################################################################
2824######################################################################
2825_MAIL_FILTERS_
2826#
2827######################################################################
2828######################################################################
2829#####
2830`#####			MAILER DEFINITIONS'
2831#####
2832######################################################################
2833######################################################################
2834undivert(7)dnl MAILER_DEFINITIONS
2835
2836