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