138032Speterdivert(-1)
238032Speter#
3261363Sgshapiro# Copyright (c) 1998-2010 Proofpoint, Inc. and its suppliers.
464562Sgshapiro#	All rights reserved.
538032Speter# Copyright (c) 1983, 1995 Eric P. Allman.  All rights reserved.
638032Speter# Copyright (c) 1988, 1993
738032Speter#	The Regents of the University of California.  All rights reserved.
838032Speter#
938032Speter# By using this file, you agree to the terms and conditions set
1038032Speter# forth in the LICENSE file which can be found at the top level of
1138032Speter# the sendmail distribution.
1238032Speter#
1338032Speter#
1438032Speterdivert(0)
1538032Speter
16266692SgshapiroVERSIONID(`$Id: proto.m4,v 8.762 2013-11-22 20:51:13 ca Exp $')
1738032Speter
1864562Sgshapiro# level CF_LEVEL config file format
19223067SgshapiroV`'CF_LEVEL`'ifdef(`NO_VENDOR',`', `/ifdef(`VENDOR_NAME', `VENDOR_NAME', `Berkeley')')
2038032Speterdivert(-1)
2138032Speter
2290792Sgshapirodnl if MAILER(`local') not defined: do it ourself; be nice
2390792Sgshapirodnl maybe we should issue a warning?
2490792Sgshapiroifdef(`_MAILER_local_',`', `MAILER(local)')
2590792Sgshapiro
2638032Speter# do some sanity checking
2738032Speterifdef(`__OSTYPE__',,
2864562Sgshapiro	`errprint(`*** ERROR: No system type defined (use OSTYPE macro)
2964562Sgshapiro')')
3038032Speter
3138032Speter# pick our default mailers
3238032Speterifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `esmtp')')
3338032Speterifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')')
3438032Speterifdef(`confRELAY_MAILER',,
3538032Speter	`define(`confRELAY_MAILER',
3638032Speter		`ifdef(`_MAILER_smtp_', `relay',
3738032Speter			`ifdef(`_MAILER_uucp', `uucp-new', `unknown')')')')
3838032Speterifdef(`confUUCP_MAILER',, `define(`confUUCP_MAILER', `uucp-old')')
3938032Speterdefine(`_SMTP_', `confSMTP_MAILER')dnl		for readability only
4038032Speterdefine(`_LOCAL_', `confLOCAL_MAILER')dnl	for readability only
4138032Speterdefine(`_RELAY_', `confRELAY_MAILER')dnl	for readability only
4238032Speterdefine(`_UUCP_', `confUUCP_MAILER')dnl		for readability only
4338032Speter
4438032Speter# back compatibility with old config files
4538032Speterifdef(`confDEF_GROUP_ID',
4664562Sgshapiro`errprint(`*** confDEF_GROUP_ID is obsolete.
4764562Sgshapiro    Use confDEF_USER_ID with a colon in the value instead.
4864562Sgshapiro')')
4938032Speterifdef(`confREAD_TIMEOUT',
5064562Sgshapiro`errprint(`*** confREAD_TIMEOUT is obsolete.
5164562Sgshapiro    Use individual confTO_<timeout> parameters instead.
5264562Sgshapiro')')
5338032Speterifdef(`confMESSAGE_TIMEOUT',
5438032Speter	`define(`_ARG_', index(confMESSAGE_TIMEOUT, /))
5538032Speter	 ifelse(_ARG_, -1,
5638032Speter		`define(`confTO_QUEUERETURN', confMESSAGE_TIMEOUT)',
5738032Speter		`define(`confTO_QUEUERETURN',
5838032Speter			substr(confMESSAGE_TIMEOUT, 0, _ARG_))
5938032Speter		 define(`confTO_QUEUEWARN',
6038032Speter			substr(confMESSAGE_TIMEOUT, eval(_ARG_+1)))')')
6138032Speterifdef(`confMIN_FREE_BLOCKS', `ifelse(index(confMIN_FREE_BLOCKS, /), -1,,
6264562Sgshapiro`errprint(`*** compound confMIN_FREE_BLOCKS is obsolete.
6364562Sgshapiro    Use confMAX_MESSAGE_SIZE for the second part of the value.
6464562Sgshapiro')')')
6538032Speter
6664562Sgshapiro
6764562Sgshapiro# Sanity check on ldap_routing feature
6864562Sgshapiro# If the user doesn't specify a new map, they better have given as a
6964562Sgshapiro# default LDAP specification which has the LDAP base (and most likely the host)
7064562Sgshapiroifdef(`confLDAP_DEFAULT_SPEC',, `ifdef(`_LDAP_ROUTING_WARN_', `errprint(`
7164562SgshapiroWARNING: Using default FEATURE(ldap_routing) map definition(s)
7264562Sgshapirowithout setting confLDAP_DEFAULT_SPEC option.
7364562Sgshapiro')')')dnl
7464562Sgshapiro
7538032Speter# clean option definitions below....
7664562Sgshapirodefine(`_OPTION', `ifdef(`$2', `O $1`'ifelse(defn(`$2'), `',, `=$2')', `#O $1`'ifelse(`$3', `',,`=$3')')')dnl
7738032Speter
7864562Sgshapirodnl required to "rename" the check_* rulesets...
7964562Sgshapirodefine(`_U_',ifdef(`_DELAY_CHECKS_',`',`_'))
8064562Sgshapirodnl default relaying denied message
8190792Sgshapiroifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG',
8290792Sgshapiroifdef(`_USE_AUTH_', `"550 Relaying denied. Proper authentication required."', `"550 Relaying denied"'))')
8390792Sgshapiroifdef(`confRCPTREJ_MSG', `', `define(`confRCPTREJ_MSG', `"550 Mailbox disabled for this recipient"')')
8490792Sgshapirodefine(`_CODE553', `553')
8538032Speterdivert(0)dnl
8638032Speter
8764562Sgshapiro# override file safeties - setting this option compromises system security,
8864562Sgshapiro# addressing the actual file configuration problem is preferred
8964562Sgshapiro# need to set this before any file actions are encountered in the cf file
9064562Sgshapiro_OPTION(DontBlameSendmail, `confDONT_BLAME_SENDMAIL', `safe')
9138032Speter
9264562Sgshapiro# default LDAP map specification
9364562Sgshapiro# need to set this now before any LDAP maps are defined
9464562Sgshapiro_OPTION(LDAPDefaultSpec, `confLDAP_DEFAULT_SPEC', `-h localhost')
9564562Sgshapiro
9638032Speter##################
9738032Speter#   local info   #
9838032Speter##################
9938032Speter
10090792Sgshapiro# my LDAP cluster
10190792Sgshapiro# need to set this before any LDAP lookups are done (including classes)
10290792Sgshapiroifdef(`confLDAP_CLUSTER', `D{sendmailMTACluster}`'confLDAP_CLUSTER', `#D{sendmailMTACluster}$m')
10390792Sgshapiro
10438032SpeterCwlocalhost
10538032Speterifdef(`USE_CW_FILE',
10638032Speter`# file containing names of hosts for which we receive email
10738032SpeterFw`'confCW_FILE',
10838032Speter	`dnl')
10938032Speter
11038032Speter# my official domain name
11138032Speter# ... `define' this only if sendmail cannot automatically determine your domain
11238032Speterifdef(`confDOMAIN_NAME', `Dj`'confDOMAIN_NAME', `#Dj$w.Foo.COM')
11338032Speter
114125820Sgshapiro# host/domain names ending with a token in class P are canonical
11538032SpeterCP.
11638032Speter
11738032Speterifdef(`UUCP_RELAY',
11838032Speter`# UUCP relay host
11938032SpeterDY`'UUCP_RELAY
12038032SpeterCPUUCP
12138032Speter
12238032Speter')dnl
12338032Speterifdef(`BITNET_RELAY',
12438032Speter`#  BITNET relay host
12538032SpeterDB`'BITNET_RELAY
12638032SpeterCPBITNET
12738032Speter
12838032Speter')dnl
12938032Speterifdef(`DECNET_RELAY',
13038032Speter`define(`_USE_DECNET_SYNTAX_', 1)dnl
13138032Speter# DECnet relay host
13238032SpeterDC`'DECNET_RELAY
13338032SpeterCPDECNET
13438032Speter
13538032Speter')dnl
13638032Speterifdef(`FAX_RELAY',
13738032Speter`# FAX relay host
13838032SpeterDF`'FAX_RELAY
13938032SpeterCPFAX
14038032Speter
14138032Speter')dnl
14238032Speter# "Smart" relay host (may be null)
14390792SgshapiroDS`'ifdef(`SMART_HOST', `SMART_HOST')
14438032Speter
14538032Speterifdef(`LUSER_RELAY', `dnl
14638032Speter# place to which unknown users should be forwarded
14738032SpeterKuser user -m -a<>
14838032SpeterDL`'LUSER_RELAY',
14938032Speter`dnl')
15038032Speter
15138032Speter# operators that cannot be in local usernames (i.e., network indicators)
152285303SgshapiroCO @ ifdef(`_NO_PERCENTHACK_', `', `%') ifdef(`_NO_UUCP_', `', `!')
15338032Speter
15438032Speter# a class with just dot (for identifying canonical names)
15538032SpeterC..
15638032Speter
15738032Speter# a class with just a left bracket (for identifying domain literals)
15838032SpeterC[[
15938032Speter
16064562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
16164562Sgshapiro# access_db acceptance class
16264562SgshapiroC{Accept}OK RELAY
16390792Sgshapiroifdef(`_DELAY_COMPAT_8_10_',`dnl
16464562Sgshapiroifdef(`_BLACKLIST_RCPT_',`dnl
16564562Sgshapiro# possible access_db RHS for spam friends/haters
16664562SgshapiroC{SpamTag}SPAMFRIEND SPAMHATER')')',
16738032Speter`dnl')
16838032Speter
16990792Sgshapirodnl mark for "domain is ok" (resolved or accepted anyway)
17090792Sgshapirodefine(`_RES_OK_', `OKR')dnl
17138032Speterifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',`dnl',`dnl
17238032Speter# Resolve map (to check if a host exists in check_mail)
17390792SgshapiroKresolve host -a<_RES_OK_> -T<TEMP>')
17490792SgshapiroC{ResOk}_RES_OK_
17538032Speter
17680785Sgshapiroifdef(`_NEED_MACRO_MAP_', `dnl
17780785Sgshapiroifdef(`_MACRO_MAP_', `', `# macro storage map
17880785Sgshapirodefine(`_MACRO_MAP_', `1')dnl
17980785SgshapiroKmacro macro')', `dnl')
18066494Sgshapiro
18138032Speterifdef(`confCR_FILE', `dnl
18266494Sgshapiro# Hosts for which relaying is permitted ($=R)
18338032SpeterFR`'confCR_FILE',
18438032Speter`dnl')
18538032Speter
18690792Sgshapirodefine(`TLS_SRV_TAG', `"TLS_Srv"')dnl
18790792Sgshapirodefine(`TLS_CLT_TAG', `"TLS_Clt"')dnl
18890792Sgshapirodefine(`TLS_RCPT_TAG', `"TLS_Rcpt"')dnl
18990792Sgshapirodefine(`TLS_TRY_TAG', `"Try_TLS"')dnl
19090792Sgshapirodefine(`SRV_FEAT_TAG', `"Srv_Features"')dnl
19164562Sgshapirodnl this may be useful in other contexts too
19264562Sgshapiroifdef(`_ARITH_MAP_', `', `# arithmetic map
19364562Sgshapirodefine(`_ARITH_MAP_', `1')dnl
19464562SgshapiroKarith arith')
19564562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
19690792Sgshapiroifdef(`_MACRO_MAP_', `', `# macro storage map
19790792Sgshapirodefine(`_MACRO_MAP_', `1')dnl
19890792SgshapiroKmacro macro')
19990792Sgshapiro# possible values for TLS_connection in access map
200132943SgshapiroC{Tls}VERIFY ENCR', `dnl')
20164562Sgshapiroifdef(`_CERT_REGEX_ISSUER_', `dnl
20264562Sgshapiro# extract relevant part from cert issuer
20364562SgshapiroKCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
20464562Sgshapiroifdef(`_CERT_REGEX_SUBJECT_', `dnl
20564562Sgshapiro# extract relevant part from cert subject
20664562SgshapiroKCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
20764562Sgshapiro
20890792Sgshapiroifdef(`LOCAL_RELAY', `dnl
209110647Sgshapiro# who I send unqualified names to if `FEATURE(stickyhost)' is used
210110560Sgshapiro# (null means deliver locally)
21190792SgshapiroDR`'LOCAL_RELAY')
21238032Speter
21390792Sgshapiroifdef(`MAIL_HUB', `dnl
214110560Sgshapiro# who gets all local email traffic
215110647Sgshapiro# ($R has precedence for unqualified names if `FEATURE(stickyhost)' is used)
21690792SgshapiroDH`'MAIL_HUB')
21738032Speter
21838032Speter# dequoting map
21990792SgshapiroKdequote dequote`'ifdef(`confDEQUOTE_OPTS', ` confDEQUOTE_OPTS', `')
22038032Speter
22138032Speterdivert(0)dnl	# end of nullclient diversion
22238032Speter# class E: names that should be exposed as from this host, even if we masquerade
22364562Sgshapiro# class L: names that should be delivered locally, even if we have a relay
22438032Speter# class M: domains that should be converted to $M
22564562Sgshapiro# class N: domains that should not be converted to $M
22638032Speter#CL root
22738032Speterundivert(5)dnl
22864562Sgshapiroifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl')
22938032Speter
23090792Sgshapiroifdef(`MASQUERADE_NAME', `dnl
23138032Speter# who I masquerade as (null for no masquerading) (see also $=M)
23290792SgshapiroDM`'MASQUERADE_NAME')
23338032Speter
23438032Speter# my name for error messages
23538032Speterifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
23638032Speter
23764562Sgshapiroundivert(6)dnl LOCAL_CONFIG
23838032Speterinclude(_CF_DIR_`m4/version.m4')
23938032Speter
24038032Speter###############
24138032Speter#   Options   #
24238032Speter###############
24390792Sgshapiroifdef(`confAUTO_REBUILD',
24490792Sgshapiro`errprint(WARNING: `confAUTO_REBUILD' is no longer valid.
24590792Sgshapiro	There was a potential for a denial of service attack if this is set.
24690792Sgshapiro)')dnl
24738032Speter
24838032Speter# strip message body to 7 bits on input?
24964562Sgshapiro_OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
25038032Speter
25138032Speter# 8-bit data handling
25277349Sgshapiro_OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `pass8')
25338032Speter
25438032Speter# wait for alias file rebuild (default units: minutes)
25564562Sgshapiro_OPTION(AliasWait, `confALIAS_WAIT', `5m')
25638032Speter
25738032Speter# location of alias file
25864562Sgshapiro_OPTION(AliasFile, `ALIAS_FILE', `MAIL_SETTINGS_DIR`'aliases')
25964562Sgshapiro
26038032Speter# minimum number of free blocks on filesystem
26164562Sgshapiro_OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', `100')
26238032Speter
26338032Speter# maximum message size
264132943Sgshapiro_OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', `0')
26538032Speter
26638032Speter# substitution for space (blank) characters
26764562Sgshapiro_OPTION(BlankSub, `confBLANK_SUB', `_')
26838032Speter
26938032Speter# avoid connecting to "expensive" mailers on initial submission?
27064562Sgshapiro_OPTION(HoldExpensive, `confCON_EXPENSIVE', `False')
27138032Speter
27238032Speter# checkpoint queue runs after every N successful deliveries
27364562Sgshapiro_OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10')
27438032Speter
27538032Speter# default delivery mode
27664562Sgshapiro_OPTION(DeliveryMode, `confDELIVERY_MODE', `background')
27738032Speter
27838032Speter# error message header/file
27964562Sgshapiro_OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header')
28038032Speter
28138032Speter# error mode
28264562Sgshapiro_OPTION(ErrorMode, `confERROR_MODE', `print')
28338032Speter
28438032Speter# save Unix-style "From_" lines at top of header?
28564562Sgshapiro_OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False')
28638032Speter
28790792Sgshapiro# queue file mode (qf files)
28890792Sgshapiro_OPTION(QueueFileMode, `confQUEUE_FILE_MODE', `0600')
28990792Sgshapiro
29038032Speter# temporary file mode
29164562Sgshapiro_OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600')
29238032Speter
29338032Speter# match recipients against GECOS field?
29464562Sgshapiro_OPTION(MatchGECOS, `confMATCH_GECOS', `False')
29538032Speter
29638032Speter# maximum hop count
29790792Sgshapiro_OPTION(MaxHopCount, `confMAX_HOP', `25')
29838032Speter
29938032Speter# location of help file
30064562SgshapiroO HelpFile=ifdef(`HELP_FILE', HELP_FILE, `MAIL_SETTINGS_DIR`'helpfile')
30138032Speter
30238032Speter# ignore dots as terminators in incoming messages?
30364562Sgshapiro_OPTION(IgnoreDots, `confIGNORE_DOTS', `False')
30438032Speter
30538032Speter# name resolver options
30664562Sgshapiro_OPTION(ResolverOptions, `confBIND_OPTS', `+AAONLY')
30738032Speter
30838032Speter# deliver MIME-encapsulated error messages?
30964562Sgshapiro_OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS', `True')
31038032Speter
31138032Speter# Forward file search path
31264562Sgshapiro_OPTION(ForwardPath, `confFORWARD_PATH', `/var/forward/$u:$z/.forward.$w:$z/.forward')
31338032Speter
31438032Speter# open connection cache size
31564562Sgshapiro_OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', `2')
31638032Speter
31738032Speter# open connection cache timeout
31864562Sgshapiro_OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', `5m')
31938032Speter
32038032Speter# persistent host status directory
32164562Sgshapiro_OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', `.hoststat')
32238032Speter
32338032Speter# single thread deliveries (requires HostStatusDirectory)?
32464562Sgshapiro_OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY', `False')
32538032Speter
32638032Speter# use Errors-To: header?
32764562Sgshapiro_OPTION(UseErrorsTo, `confUSE_ERRORS_TO', `False')
32838032Speter
329285303Sgshapiro# use compressed IPv6 address format?
330285303Sgshapiro_OPTION(UseCompressedIPv6Addresses, `confUSE_COMPRESSED_IPV6_ADDRESSES', `')
331285303Sgshapiro
33238032Speter# log level
33364562Sgshapiro_OPTION(LogLevel, `confLOG_LEVEL', `10')
33438032Speter
33538032Speter# send to me too, even in an alias expansion?
33664562Sgshapiro_OPTION(MeToo, `confME_TOO', `True')
33738032Speter
33838032Speter# verify RHS in newaliases?
33964562Sgshapiro_OPTION(CheckAliases, `confCHECK_ALIASES', `False')
34038032Speter
34138032Speter# default messages to old style headers if no special punctuation?
34264562Sgshapiro_OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS', `False')
34338032Speter
34438032Speter# SMTP daemon options
34564562Sgshapiroifelse(defn(`confDAEMON_OPTIONS'), `', `dnl',
34694334Sgshapiro`errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.
34794334Sgshapiro	Use `DAEMON_OPTIONS()'; see cf/README.
34864562Sgshapiro)'dnl
34964562Sgshapiro`DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
35066494Sgshapiroifelse(defn(`_DPO_'), `',
35190792Sgshapiro`ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-v4, Family=inet
35290792SgshapiroO DaemonPortOptions=Name=MTA-v6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_')
35364562Sgshapiroifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
35438032Speter
35564562Sgshapiro# SMTP client options
35690792Sgshapiroifelse(defn(`confCLIENT_OPTIONS'), `', `dnl',
35790792Sgshapiro`errprint(WARNING: `confCLIENT_OPTIONS' is no longer valid.  See cf/README for more information.
35890792Sgshapiro)'dnl
35990792Sgshapiro`CLIENT_OPTIONS(`confCLIENT_OPTIONS')')
36090792Sgshapiroifelse(defn(`_CPO_'), `',
36190792Sgshapiro`#O ClientPortOptions=Family=inet, Address=0.0.0.0', `_CPO_')
36264562Sgshapiro
36390792Sgshapiro# Modifiers to `define' {daemon_flags} for direct submissions
36490792Sgshapiro_OPTION(DirectSubmissionModifiers, `confDIRECT_SUBMISSION_MODIFIERS', `')
36590792Sgshapiro
36690792Sgshapiro# Use as mail submission program? See sendmail/SECURITY
36790792Sgshapiro_OPTION(UseMSP, `confUSE_MSP', `')
36890792Sgshapiro
36938032Speter# privacy flags
37064562Sgshapiro_OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings')
37138032Speter
37238032Speter# who (if anyone) should get extra copies of error messages
37364562Sgshapiro_OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster')
37438032Speter
37538032Speter# slope of queue-only function
37664562Sgshapiro_OPTION(QueueFactor, `confQUEUE_FACTOR', `600000')
37738032Speter
37890792Sgshapiro# limit on number of concurrent queue runners
37990792Sgshapiro_OPTION(MaxQueueChildren, `confMAX_QUEUE_CHILDREN', `')
38090792Sgshapiro
38190792Sgshapiro# maximum number of queue-runners per queue-grouping with multiple queues
38290792Sgshapiro_OPTION(MaxRunnersPerQueue, `confMAX_RUNNERS_PER_QUEUE', `1')
38390792Sgshapiro
38490792Sgshapiro# priority of queue runners (nice(3))
38590792Sgshapiro_OPTION(NiceQueueRun, `confNICE_QUEUE_RUN', `')
38690792Sgshapiro
38790792Sgshapiro# shall we sort the queue by hostname first?
38890792Sgshapiro_OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority')
38990792Sgshapiro
39090792Sgshapiro# minimum time in queue before retry
39190792Sgshapiro_OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m')
39290792Sgshapiro
393285303Sgshapiro# maximum time in queue before retry (if > 0; only for exponential delay)
394285303Sgshapiro_OPTION(MaxQueueAge, `confMAX_QUEUE_AGE', `')
395285303Sgshapiro
39690792Sgshapiro# how many jobs can you process in the queue?
397157001Sgshapiro_OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `0')
39890792Sgshapiro
39990792Sgshapiro# perform initial split of envelope without checking MX records
40090792Sgshapiro_OPTION(FastSplit, `confFAST_SPLIT', `1')
40190792Sgshapiro
40238032Speter# queue directory
40364562SgshapiroO QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue')
40438032Speter
405168515Sgshapiro# key for shared memory; 0 to turn off, -1 to auto-select
40690792Sgshapiro_OPTION(SharedMemoryKey, `confSHARED_MEMORY_KEY', `0')
40790792Sgshapiro
408168515Sgshapiro# file to store auto-selected key for shared memory (SharedMemoryKey = -1)
409168515Sgshapiro_OPTION(SharedMemoryKeyFile, `confSHARED_MEMORY_KEY_FILE', `')
41094334Sgshapiro
41138032Speter# timeouts (many of these)
41264562Sgshapiro_OPTION(Timeout.initial, `confTO_INITIAL', `5m')
41364562Sgshapiro_OPTION(Timeout.connect, `confTO_CONNECT', `5m')
41490792Sgshapiro_OPTION(Timeout.aconnect, `confTO_ACONNECT', `0s')
41564562Sgshapiro_OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m')
41664562Sgshapiro_OPTION(Timeout.helo, `confTO_HELO', `5m')
41764562Sgshapiro_OPTION(Timeout.mail, `confTO_MAIL', `10m')
41864562Sgshapiro_OPTION(Timeout.rcpt, `confTO_RCPT', `1h')
41964562Sgshapiro_OPTION(Timeout.datainit, `confTO_DATAINIT', `5m')
42064562Sgshapiro_OPTION(Timeout.datablock, `confTO_DATABLOCK', `1h')
42164562Sgshapiro_OPTION(Timeout.datafinal, `confTO_DATAFINAL', `1h')
42264562Sgshapiro_OPTION(Timeout.rset, `confTO_RSET', `5m')
42364562Sgshapiro_OPTION(Timeout.quit, `confTO_QUIT', `2m')
42464562Sgshapiro_OPTION(Timeout.misc, `confTO_MISC', `2m')
42564562Sgshapiro_OPTION(Timeout.command, `confTO_COMMAND', `1h')
42664562Sgshapiro_OPTION(Timeout.ident, `confTO_IDENT', `5s')
42764562Sgshapiro_OPTION(Timeout.fileopen, `confTO_FILEOPEN', `60s')
42864562Sgshapiro_OPTION(Timeout.control, `confTO_CONTROL', `2m')
42964562Sgshapiro_OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', `5d')
43064562Sgshapiro_OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', `5d')
43164562Sgshapiro_OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', `2d')
43264562Sgshapiro_OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', `7d')
433132943Sgshapiro_OPTION(Timeout.queuereturn.dsn, `confTO_QUEUERETURN_DSN', `5d')
43464562Sgshapiro_OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', `4h')
43564562Sgshapiro_OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', `4h')
43664562Sgshapiro_OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', `1h')
43764562Sgshapiro_OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', `12h')
438132943Sgshapiro_OPTION(Timeout.queuewarn.dsn, `confTO_QUEUEWARN_DSN', `4h')
43964562Sgshapiro_OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', `30m')
44064562Sgshapiro_OPTION(Timeout.resolver.retrans, `confTO_RESOLVER_RETRANS', `5s')
44164562Sgshapiro_OPTION(Timeout.resolver.retrans.first, `confTO_RESOLVER_RETRANS_FIRST', `5s')
44264562Sgshapiro_OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s')
44364562Sgshapiro_OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4')
44464562Sgshapiro_OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4')
44564562Sgshapiro_OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4')
44690792Sgshapiro_OPTION(Timeout.lhlo, `confTO_LHLO', `2m')
44790792Sgshapiro_OPTION(Timeout.auth, `confTO_AUTH', `10m')
44890792Sgshapiro_OPTION(Timeout.starttls, `confTO_STARTTLS', `1h')
44938032Speter
45090792Sgshapiro# time for DeliverBy; extension disabled if less than 0
45190792Sgshapiro_OPTION(DeliverByMin, `confDELIVER_BY_MIN', `0')
45290792Sgshapiro
45338032Speter# should we not prune routes in route-addr syntax addresses?
45464562Sgshapiro_OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False')
45538032Speter
45638032Speter# queue up everything before forking?
45764562Sgshapiro_OPTION(SuperSafe, `confSAFE_QUEUE', `True')
45838032Speter
45938032Speter# status file
460168515Sgshapiro_OPTION(StatusFile, `STATUS_FILE')
46138032Speter
46238032Speter# time zone handling:
46338032Speter#  if undefined, use system default
46438032Speter#  if defined but null, use TZ envariable passed in
46538032Speter#  if defined and non-null, use that info
46638032Speterifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
46738032Speter	confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
46838032Speter	`O TimeZoneSpec=confTIME_ZONE')
46938032Speter
47038032Speter# default UID (can be username or userid:groupid)
47164562Sgshapiro_OPTION(DefaultUser, `confDEF_USER_ID', `mailnull')
47238032Speter
47338032Speter# list of locations of user database file (null means no lookup)
47464562Sgshapiro_OPTION(UserDatabaseSpec, `confUSERDB_SPEC', `MAIL_SETTINGS_DIR`'userdb')
47538032Speter
47638032Speter# fallback MX host
47764562Sgshapiro_OPTION(FallbackMXhost, `confFALLBACK_MX', `fall.back.host.net')
47838032Speter
479132943Sgshapiro# fallback smart host
480132943Sgshapiro_OPTION(FallbackSmartHost, `confFALLBACK_SMARTHOST', `fall.back.host.net')
481132943Sgshapiro
48238032Speter# if we are the best MX host for a site, try it directly instead of config err
48364562Sgshapiro_OPTION(TryNullMXList, `confTRY_NULL_MX_LIST', `False')
48438032Speter
48538032Speter# load average at which we just queue messages
48664562Sgshapiro_OPTION(QueueLA, `confQUEUE_LA', `8')
48738032Speter
48838032Speter# load average at which we refuse connections
48964562Sgshapiro_OPTION(RefuseLA, `confREFUSE_LA', `12')
49038032Speter
491132943Sgshapiro# log interval when refusing connections for this long
492132943Sgshapiro_OPTION(RejectLogInterval, `confREJECT_LOG_INTERVAL', `3h')
493132943Sgshapiro
49490792Sgshapiro# load average at which we delay connections; 0 means no limit
49590792Sgshapiro_OPTION(DelayLA, `confDELAY_LA', `0')
49690792Sgshapiro
49738032Speter# maximum number of children we allow at one time
49898841Sgshapiro_OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `0')
49938032Speter
50038032Speter# maximum number of new connections per second
50171345Sgshapiro_OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0')
50238032Speter
503132943Sgshapiro# Width of the window 
504132943Sgshapiro_OPTION(ConnectionRateWindowSize, `confCONNECTION_RATE_WINDOW_SIZE', `60s')
505132943Sgshapiro
50638032Speter# work recipient factor
50764562Sgshapiro_OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
50838032Speter
50938032Speter# deliver each queued job in a separate process?
51064562Sgshapiro_OPTION(ForkEachJob, `confSEPARATE_PROC', `False')
51138032Speter
51238032Speter# work class factor
51364562Sgshapiro_OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800')
51438032Speter
51538032Speter# work time factor
51664562Sgshapiro_OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000')
51738032Speter
51838032Speter# default character set
519141858Sgshapiro_OPTION(DefaultCharSet, `confDEF_CHAR_SET', `unknown-8bit')
52038032Speter
52190792Sgshapiro# service switch file (name hardwired on Solaris, Ultrix, OSF/1, others)
52264562Sgshapiro_OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch')
52338032Speter
52438032Speter# hosts file (normally /etc/hosts)
52564562Sgshapiro_OPTION(HostsFile, `confHOSTS_FILE', `/etc/hosts')
52638032Speter
52738032Speter# dialup line delay on connection failure
528157001Sgshapiro_OPTION(DialDelay, `confDIAL_DELAY', `0s')
52938032Speter
53038032Speter# action to take if there are no recipients in the message
531157001Sgshapiro_OPTION(NoRecipientAction, `confNO_RCPT_ACTION', `none')
53238032Speter
53338032Speter# chrooted environment for writing to files
534157001Sgshapiro_OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `')
53538032Speter
53638032Speter# are colons OK in addresses?
53764562Sgshapiro_OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True')
53838032Speter
53938032Speter# shall I avoid expanding CNAMEs (violates protocols)?
54064562Sgshapiro_OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False')
54138032Speter
54238032Speter# SMTP initial login message (old $e macro)
54364562Sgshapiro_OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG', `$j Sendmail $v ready at $b')
54438032Speter
54538032Speter# UNIX initial From header format (old $l macro)
54664562Sgshapiro_OPTION(UnixFromLine, `confFROM_LINE', `From $g $d')
54738032Speter
54838032Speter# From: lines that have embedded newlines are unwrapped onto one line
54964562Sgshapiro_OPTION(SingleLineFromHeader, `confSINGLE_LINE_FROM_HEADER', `False')
55038032Speter
55138032Speter# Allow HELO SMTP command that does not `include' a host name
55264562Sgshapiro_OPTION(AllowBogusHELO, `confALLOW_BOGUS_HELO', `False')
55338032Speter
55438032Speter# Characters to be quoted in a full name phrase (@,;:\()[] are automatic)
55564562Sgshapiro_OPTION(MustQuoteChars, `confMUST_QUOTE_CHARS', `.')
55638032Speter
55738032Speter# delimiter (operator) characters (old $o macro)
55864562Sgshapiro_OPTION(OperatorChars, `confOPERATORS', `.:@[]')
55938032Speter
56038032Speter# shall I avoid calling initgroups(3) because of high NIS costs?
56164562Sgshapiro_OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False')
56238032Speter
56338032Speter# are group-writable `:include:' and .forward files (un)trustworthy?
56490792Sgshapiro# True (the default) means they are not trustworthy.
56564562Sgshapiro_OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True')
56690792Sgshapiroifdef(`confUNSAFE_GROUP_WRITES',
56790792Sgshapiro`errprint(`WARNING: confUNSAFE_GROUP_WRITES is deprecated; use confDONT_BLAME_SENDMAIL.
56890792Sgshapiro')')
56938032Speter
57038032Speter# where do errors that occur when sending errors get sent?
57164562Sgshapiro_OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster')
57238032Speter
573168515Sgshapiro# issue temporary errors (4xy) instead of permanent errors (5xy)?
574168515Sgshapiro_OPTION(SoftBounce, `confSOFT_BOUNCE', `False')
575168515Sgshapiro
57664562Sgshapiro# where to save bounces if all else fails
57764562Sgshapiro_OPTION(DeadLetterDrop, `confDEAD_LETTER_DROP', `/var/tmp/dead.letter')
57864562Sgshapiro
57938032Speter# what user id do we assume for the majority of the processing?
58064562Sgshapiro_OPTION(RunAsUser, `confRUN_AS_USER', `sendmail')
58138032Speter
58238032Speter# maximum number of recipients per SMTP envelope
583132943Sgshapiro_OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `0')
58438032Speter
58590792Sgshapiro# limit the rate recipients per SMTP envelope are accepted
58690792Sgshapiro# once the threshold number of recipients have been rejected
587132943Sgshapiro_OPTION(BadRcptThrottle, `confBAD_RCPT_THROTTLE', `0')
58890792Sgshapiro
589203004Sgshapiro
59038032Speter# shall we get local names from our installed interfaces?
59164562Sgshapiro_OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False')
59238032Speter
59364562Sgshapiro# Return-Receipt-To: header implies DSN request
59464562Sgshapiro_OPTION(RrtImpliesDsn, `confRRT_IMPLIES_DSN', `False')
59564562Sgshapiro
59664562Sgshapiro# override connection address (for testing)
59764562Sgshapiro_OPTION(ConnectOnlyTo, `confCONNECT_ONLY_TO', `0.0.0.0')
59864562Sgshapiro
59964562Sgshapiro# Trusted user for file ownership and starting the daemon
60064562Sgshapiro_OPTION(TrustedUser, `confTRUSTED_USER', `root')
60164562Sgshapiro
60264562Sgshapiro# Control socket for daemon management
60364562Sgshapiro_OPTION(ControlSocketName, `confCONTROL_SOCKET_NAME', `/var/spool/mqueue/.control')
60464562Sgshapiro
60564562Sgshapiro# Maximum MIME header length to protect MUAs
606132943Sgshapiro_OPTION(MaxMimeHeaderLength, `confMAX_MIME_HEADER_LENGTH', `0/0')
60764562Sgshapiro
60864562Sgshapiro# Maximum length of the sum of all headers
60964562Sgshapiro_OPTION(MaxHeadersLength, `confMAX_HEADERS_LENGTH', `32768')
61064562Sgshapiro
61164562Sgshapiro# Maximum depth of alias recursion
61264562Sgshapiro_OPTION(MaxAliasRecursion, `confMAX_ALIAS_RECURSION', `10')
61364562Sgshapiro
61464562Sgshapiro# location of pid file
61564562Sgshapiro_OPTION(PidFile, `confPID_FILE', `/var/run/sendmail.pid')
61664562Sgshapiro
61764562Sgshapiro# Prefix string for the process title shown on 'ps' listings
61864562Sgshapiro_OPTION(ProcessTitlePrefix, `confPROCESS_TITLE_PREFIX', `prefix')
61964562Sgshapiro
62064562Sgshapiro# Data file (df) memory-buffer file maximum size
62164562Sgshapiro_OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096')
62264562Sgshapiro
62364562Sgshapiro# Transcript file (xf) memory-buffer file maximum size
62464562Sgshapiro_OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096')
62564562Sgshapiro
62690792Sgshapiro# lookup type to find information about local mailboxes
62790792Sgshapiro_OPTION(MailboxDatabase, `confMAILBOX_DATABASE', `pw')
62890792Sgshapiro
629132943Sgshapiro# override compile time flag REQUIRES_DIR_FSYNC
630132943Sgshapiro_OPTION(RequiresDirfsync, `confREQUIRES_DIR_FSYNC', `true')
631132943Sgshapiro
63264562Sgshapiro# list of authentication mechanisms
63390792Sgshapiro_OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5')
63464562Sgshapiro
635132943Sgshapiro# Authentication realm
636132943Sgshapiro_OPTION(AuthRealm, `confAUTH_REALM', `')
637132943Sgshapiro
63864562Sgshapiro# default authentication information for outgoing connections
63964562Sgshapiro_OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info')
64064562Sgshapiro
64164562Sgshapiro# SMTP AUTH flags
64264562Sgshapiro_OPTION(AuthOptions, `confAUTH_OPTIONS', `')
64364562Sgshapiro
64490792Sgshapiro# SMTP AUTH maximum encryption strength
64590792Sgshapiro_OPTION(AuthMaxBits, `confAUTH_MAX_BITS', `')
64690792Sgshapiro
64790792Sgshapiro# SMTP STARTTLS server options
64890792Sgshapiro_OPTION(TLSSrvOptions, `confTLS_SRV_OPTIONS', `')
64990792Sgshapiro
650285303Sgshapiro# SSL cipherlist
651285303Sgshapiro_OPTION(CipherList, `confCIPHER_LIST', `')
652285303Sgshapiro# server side SSL options
653285303Sgshapiro_OPTION(ServerSSLOptions, `confSERVER_SSL_OPTIONS', `')
654285303Sgshapiro# client side SSL options
655285303Sgshapiro_OPTION(ClientSSLOptions, `confCLIENT_SSL_OPTIONS', `')
656203004Sgshapiro
65764562Sgshapiro# Input mail filters
65864562Sgshapiro_OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
65964562Sgshapiro
66098841Sgshapiroifelse(len(X`'_MAIL_FILTERS_DEF), `1', `dnl', `dnl
66164562Sgshapiro# Milter options
66290792Sgshapiro_OPTION(Milter.LogLevel, `confMILTER_LOG_LEVEL', `')
66364562Sgshapiro_OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `')
66464562Sgshapiro_OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `')
66564562Sgshapiro_OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
666125820Sgshapiro_OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')
667168515Sgshapiro_OPTION(Milter.macros.eom, `confMILTER_MACROS_EOM', `')
668168515Sgshapiro_OPTION(Milter.macros.eoh, `confMILTER_MACROS_EOH', `')
669168515Sgshapiro_OPTION(Milter.macros.data, `confMILTER_MACROS_DATA', `')')
67064562Sgshapiro
67164562Sgshapiro# CA directory
672110560Sgshapiro_OPTION(CACertPath, `confCACERT_PATH', `')
67364562Sgshapiro# CA file
674110560Sgshapiro_OPTION(CACertFile, `confCACERT', `')
67564562Sgshapiro# Server Cert
67664562Sgshapiro_OPTION(ServerCertFile, `confSERVER_CERT', `')
67764562Sgshapiro# Server private key
67864562Sgshapiro_OPTION(ServerKeyFile, `confSERVER_KEY', `')
67964562Sgshapiro# Client Cert
68064562Sgshapiro_OPTION(ClientCertFile, `confCLIENT_CERT', `')
68164562Sgshapiro# Client private key
68264562Sgshapiro_OPTION(ClientKeyFile, `confCLIENT_KEY', `')
683132943Sgshapiro# File containing certificate revocation lists 
684132943Sgshapiro_OPTION(CRLFile, `confCRL', `')
68564562Sgshapiro# DHParameters (only required if DSA/DH is used)
68664562Sgshapiro_OPTION(DHParameters, `confDH_PARAMETERS', `')
68764562Sgshapiro# Random data source (required for systems without /dev/urandom under OpenSSL)
68864562Sgshapiro_OPTION(RandFile, `confRAND_FILE', `')
689285303Sgshapiro# fingerprint algorithm (digest) to use for the presented cert
690285303Sgshapiro_OPTION(CertFingerprintAlgorithm, `confCERT_FINGERPRINT_ALGORITHM', `')
69164562Sgshapiro
692168515Sgshapiro# Maximum number of "useless" commands before slowing down
693168515Sgshapiro_OPTION(MaxNOOPCommands, `confMAX_NOOP_COMMANDS', `20')
694168515Sgshapiro
695168515Sgshapiro# Name to use for EHLO (defaults to $j)
696168515Sgshapiro_OPTION(HeloName, `confHELO_NAME')
697168515Sgshapiro
698285303Sgshapiroifdef(`_NEED_SMTPOPMODES_', `dnl
699285303Sgshapiro# SMTP operation modes
700285303SgshapiroC{SMTPOpModes} s d D')
701285303Sgshapiro
70290792Sgshapiro############################
70390792Sgshapiro`# QUEUE GROUP DEFINITIONS  #'
70490792Sgshapiro############################
70590792Sgshapiro_QUEUE_GROUP_
70642575Speter
70738032Speter###########################
70838032Speter#   Message precedences   #
70938032Speter###########################
71038032Speter
71138032SpeterPfirst-class=0
71238032SpeterPspecial-delivery=100
71338032SpeterPlist=-30
71438032SpeterPbulk=-60
71538032SpeterPjunk=-100
71638032Speter
71738032Speter#####################
71838032Speter#   Trusted users   #
71938032Speter#####################
72038032Speter
72138032Speter# this is equivalent to setting class "t"
72264562Sgshapiroifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `MAIL_SETTINGS_DIR`'trusted-users')
72338032SpeterTroot
72438032SpeterTdaemon
72538032Speterifdef(`_NO_UUCP_', `dnl', `Tuucp')
72638032Speterifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
72738032Speter
72838032Speter#########################
72938032Speter#   Format of headers   #
73038032Speter#########################
73138032Speter
73238032Speterifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
733132943Sgshapiroifdef(`confMESSAGEID_HEADER',, `define(`confMESSAGEID_HEADER', `<$t.$i@$j>')')dnl
73438032SpeterH?P?Return-Path: <$g>
73538032SpeterHReceived: confRECEIVED_HEADER
73638032SpeterH?D?Resent-Date: $a
73738032SpeterH?D?Date: $a
73838032SpeterH?F?Resent-From: confFROM_HEADER
73938032SpeterH?F?From: confFROM_HEADER
74038032SpeterH?x?Full-Name: $x
74138032Speter# HPosted-Date: $a
74238032Speter# H?l?Received-Date: $b
743132943SgshapiroH?M?Resent-Message-Id: confMESSAGEID_HEADER
744132943SgshapiroH?M?Message-Id: confMESSAGEID_HEADER
74564562Sgshapiro
74638032Speter#
74738032Speter######################################################################
74838032Speter######################################################################
74938032Speter#####
75038032Speter#####			REWRITING RULES
75138032Speter#####
75238032Speter######################################################################
75338032Speter######################################################################
75438032Speter
75538032Speter############################################
75638032Speter###  Ruleset 3 -- Name Canonicalization  ###
75738032Speter############################################
75864562SgshapiroScanonify=3
75938032Speter
76038032Speter# handle null input (translate to <@> special case)
76138032SpeterR$@			$@ <@>
76238032Speter
76338032Speter# strip group: syntax (not inside angle brackets!) and trailing semicolon
76438032SpeterR$*			$: $1 <@>			mark addresses
76538032SpeterR$* < $* > $* <@>	$: $1 < $2 > $3			unmark <addr>
76638032SpeterR@ $* <@>		$: @ $1				unmark @host:...
76790792SgshapiroR$* [ IPv6 : $+ ] <@>	$: $1 [ IPv6 : $2 ]		unmark IPv6 addr
76838032SpeterR$* :: $* <@>		$: $1 :: $2			unmark node::addr
76938032SpeterR:`include': $* <@>	$: :`include': $1			unmark :`include':...
77038032SpeterR$* : $* [ $* ]		$: $1 : $2 [ $3 ] <@>		remark if leading colon
77138032SpeterR$* : $* <@>		$: $2				strip colon if marked
77238032SpeterR$* <@>			$: $1				unmark
77338032SpeterR$* ;			   $1				strip trailing semi
77471345SgshapiroR$* < $+ :; > $*	$@ $2 :; <@>			catch <list:;>
77538032SpeterR$* < $* ; >		   $1 < $2 >			bogus bracketed semi
77638032Speter
77738032Speter# null input now results from list:; syntax
77838032SpeterR$@			$@ :; <@>
77938032Speter
78038032Speter# strip angle brackets -- note RFC733 heuristic to get innermost item
78138032SpeterR$*			$: < $1 >			housekeeping <>
78238032SpeterR$+ < $* >		   < $2 >			strip excess on left
78338032SpeterR< $* > $+		   < $1 >			strip excess on right
78438032SpeterR<>			$@ < @ >			MAIL FROM:<> case
78538032SpeterR< $+ >			$: $1				remove housekeeping <>
78638032Speter
78764562Sgshapiroifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
78838032Speter# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
78938032SpeterR@ $+ , $+		@ $1 : $2			change all "," to ":"
79038032Speter
79138032Speter# localize and dispose of route-based addresses
79290792Sgshapirodnl XXX: IPv6 colon conflict
79390792Sgshapiroifdef(`NO_NETINET6', `dnl',
79490792Sgshapiro`R@ [$+] : $+		$@ $>Canonify2 < @ [$1] > : $2	handle <route-addr>')
79564562SgshapiroR@ $+ : $+		$@ $>Canonify2 < @$1 > : $2	handle <route-addr>
79664562Sgshapirodnl',`dnl
79764562Sgshapiro# strip route address <@a,@b,@c:user@d> -> <user@d>
79864562SgshapiroR@ $+ , $+		$2
79990792Sgshapiroifdef(`NO_NETINET6', `dnl',
80090792Sgshapiro`R@ [ $* ] : $+		$2')
80164562SgshapiroR@ $+ : $+		$2
80264562Sgshapirodnl')
80338032Speter
80438032Speter# find focus for list syntax
80564562SgshapiroR $+ : $* ; @ $+	$@ $>Canonify2 $1 : $2 ; < @ $3 >	list syntax
80638032SpeterR $+ : $* ;		$@ $1 : $2;			list syntax
80738032Speter
80838032Speter# find focus for @ syntax addresses
80938032SpeterR$+ @ $+		$: $1 < @ $2 >			focus on domain
81038032SpeterR$+ < $+ @ $+ >		$1 $2 < @ $3 >			move gaze right
81164562SgshapiroR$+ < @ $+ >		$@ $>Canonify2 $1 < @ $2 >	already canonical
81238032Speter
81390792Sgshapirodnl This is flagged as an error in S0; no need to silently fix it here.
81490792Sgshapirodnl # do some sanity checking
81590792Sgshapirodnl R$* < @ $~[ $* : $* > $*	$1 < @ $2 $3 > $4	nix colons in addrs
81638032Speter
81738032Speterifdef(`_NO_UUCP_', `dnl',
81838032Speter`# convert old-style addresses to a domain-based address
81964562SgshapiroR$- ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	resolve uucp names
82064562SgshapiroR$+ . $- ! $+		$@ $>Canonify2 $3 < @ $1 . $2 >		domain uucps
82164562SgshapiroR$+ ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	uucp subdomains
82238032Speter')
82338032Speterifdef(`_USE_DECNET_SYNTAX_',
82438032Speter`# convert node::user addresses into a domain-based address
82564562SgshapiroR$- :: $+		$@ $>Canonify2 $2 < @ $1 .DECNET >	resolve DECnet names
82664562SgshapiroR$- . $- :: $+		$@ $>Canonify2 $3 < @ $1.$2 .DECNET >	numeric DECnet addr
82738032Speter',
82838032Speter	`dnl')
829285303Sgshapiroifdef(`_NO_PERCENTHACK_', `dnl',
830285303Sgshapiro`# if we have % signs, take the rightmost one
83138032SpeterR$* % $*		$1 @ $2				First make them all @s.
83238032SpeterR$* @ $* @ $*		$1 % $2 @ $3			Undo all but the last.
833285303Sgshapiro')
83464562SgshapiroR$* @ $*		$@ $>Canonify2 $1 < @ $2 >	Insert < > and finish
83538032Speter
83638032Speter# else we must be a local name
83764562SgshapiroR$*			$@ $>Canonify2 $1
83838032Speter
83938032Speter
84038032Speter################################################
84138032Speter###  Ruleset 96 -- bottom half of ruleset 3  ###
84238032Speter################################################
84338032Speter
84464562SgshapiroSCanonify2=96
84538032Speter
84638032Speter# handle special cases for local names
84738032SpeterR$* < @ localhost > $*		$: $1 < @ $j . > $2		no domain at all
84838032SpeterR$* < @ localhost . $m > $*	$: $1 < @ $j . > $2		local domain
84938032Speterifdef(`_NO_UUCP_', `dnl',
85038032Speter`R$* < @ localhost . UUCP > $*	$: $1 < @ $j . > $2		.UUCP domain')
85164562Sgshapiro
85290792Sgshapiro# check for IPv4/IPv6 domain literal
85390792SgshapiroR$* < @ [ $+ ] > $*		$: $1 < @@ [ $2 ] > $3		mark [addr]
85438032SpeterR$* < @@ $=w > $*		$: $1 < @ $j . > $3		self-literal
85538032SpeterR$* < @@ $+ > $*		$@ $1 < @ $2 > $3		canon IP addr
85638032Speter
85764562Sgshapiroifdef(`_DOMAIN_TABLE_', `dnl
85838032Speter# look up domains in the domain table
85938032SpeterR$* < @ $+ > $* 		$: $1 < @ $(domaintable $2 $) > $3', `dnl')
86038032Speter
86164562Sgshapiroundivert(2)dnl LOCAL_RULE_3
86238032Speter
86364562Sgshapiroifdef(`_BITDOMAIN_TABLE_', `dnl
86438032Speter# handle BITNET mapping
86538032SpeterR$* < @ $+ .BITNET > $*		$: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3', `dnl')
86638032Speter
86764562Sgshapiroifdef(`_UUDOMAIN_TABLE_', `dnl
86838032Speter# handle UUCP mapping
86938032SpeterR$* < @ $+ .UUCP > $*		$: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3', `dnl')
87038032Speter
87138032Speterifdef(`_NO_UUCP_', `dnl',
87238032Speter`ifdef(`UUCP_RELAY',
87338032Speter`# pass UUCP addresses straight through
87438032SpeterR$* < @ $+ . UUCP > $*		$@ $1 < @ $2 . UUCP . > $3',
87538032Speter`# if really UUCP, handle it immediately
87638032Speterifdef(`_CLASS_U_',
87738032Speter`R$* < @ $=U . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
87838032Speterifdef(`_CLASS_V_',
87938032Speter`R$* < @ $=V . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
88038032Speterifdef(`_CLASS_W_',
88138032Speter`R$* < @ $=W . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
88238032Speterifdef(`_CLASS_X_',
88338032Speter`R$* < @ $=X . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
88438032Speterifdef(`_CLASS_Y_',
88538032Speter`R$* < @ $=Y . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
88638032Speter
88738032Speterifdef(`_NO_CANONIFY_', `dnl', `dnl
88838032Speter# try UUCP traffic as a local address
88938032SpeterR$* < @ $+ . UUCP > $*		$: $1 < @ $[ $2 $] . UUCP . > $3
89038032SpeterR$* < @ $+ . . UUCP . > $*	$@ $1 < @ $2 . > $3')
89138032Speter')')
89264562Sgshapiro# hostnames ending in class P are always canonical
89364562SgshapiroR$* < @ $* $=P > $*		$: $1 < @ $2 $3 . > $4
89464562Sgshapirodnl apply the next rule only for hostnames not in class P
89564562Sgshapirodnl this even works for phrases in class P since . is in class P
89664562Sgshapirodnl which daemon flags are set?
89764562SgshapiroR$* < @ $* $~P > $*		$: $&{daemon_flags} $| $1 < @ $2 $3 > $4
89864562Sgshapirodnl the other rules in this section only apply if the hostname
89964562Sgshapirodnl does not end in class P hence no further checks are done here
90064562Sgshapirodnl if this ever changes make sure the lookups are "protected" again!
90164562Sgshapiroifdef(`_NO_CANONIFY_', `dnl
90264562Sgshapirodnl do not canonify unless:
90364562Sgshapirodnl domain ends in class {Canonify} (this does not work if the intersection
90464562Sgshapirodnl	with class P is non-empty)
90564562Sgshapirodnl or {daemon_flags} has c set
90664562Sgshapiro# pass to name server to make hostname canonical if in class {Canonify}
90764562SgshapiroR$* $| $* < @ $* $={Canonify} > $*	$: $2 < @ $[ $3 $4 $] > $5
90864562Sgshapiro# pass to name server to make hostname canonical if requested
90964562SgshapiroR$* c $* $| $* < @ $* > $*	$: $3 < @ $[ $4 $] > $5
91064562Sgshapirodnl trailing dot? -> do not apply _CANONIFY_HOSTS_
91164562SgshapiroR$* $| $* < @ $+ . > $*		$: $2 < @ $3 . > $4
91264562Sgshapiro# add a trailing dot to qualified hostnames so other rules will work
91364562SgshapiroR$* $| $* < @ $+.$+ > $*	$: $2 < @ $3.$4 . > $5
91464562Sgshapiroifdef(`_CANONIFY_HOSTS_', `dnl
91564562Sgshapirodnl this should only apply to unqualified hostnames
91664562Sgshapirodnl but if a valid character inside an unqualified hostname is an OperatorChar
91764562Sgshapirodnl then $- does not work.
91864562Sgshapiro# lookup unqualified hostnames
91990792SgshapiroR$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4', `dnl')', `dnl
92064562Sgshapirodnl _NO_CANONIFY_ is not set: canonify unless:
92164562Sgshapirodnl {daemon_flags} contains CC (do not canonify)
92271345Sgshapirodnl but add a trailing dot to qualified hostnames so other rules will work
92371345Sgshapirodnl should we do this for every hostname: even unqualified?
92471345SgshapiroR$* CC $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
92564562SgshapiroR$* CC $* $| $*			$: $3
92690792Sgshapiroifdef(`_FFR_NOCANONIFY_HEADERS', `dnl
92790792Sgshapiro# do not canonify header addresses
92890792SgshapiroR$* $| $* < @ $* $~P > $*	$: $&{addr_type} $| $2 < @ $3 $4 > $5
92990792SgshapiroR$* h $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
93090792SgshapiroR$* h $* $| $*			$: $3', `dnl')
93138032Speter# pass to name server to make hostname canonical
93264562SgshapiroR$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4')
93364562Sgshapirodnl remove {daemon_flags} for other cases
93464562SgshapiroR$* $| $*			$: $2
93538032Speter
93638032Speter# local host aliases and pseudo-domains are always canonical
93738032SpeterR$* < @ $=w > $*		$: $1 < @ $2 . > $3
93838032Speterifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
93938032Speter`R$* < @ $* $=M > $*		$: $1 < @ $2 $3 . > $4',
94038032Speter`R$* < @ $=M > $*		$: $1 < @ $2 . > $3')
94164562Sgshapiroifdef(`_VIRTUSER_TABLE_', `dnl
94264562Sgshapirodnl virtual hosts are also canonical
94364562Sgshapiroifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
94464562Sgshapiro`R$* < @ $* $={VirtHost} > $* 	$: $1 < @ $2 $3 . > $4',
94564562Sgshapiro`R$* < @ $={VirtHost} > $* 	$: $1 < @ $2 . > $3')',
94664562Sgshapiro`dnl')
94790792Sgshapiroifdef(`_GENERICS_TABLE_', `dnl
94890792Sgshapirodnl hosts for genericstable are also canonical
94990792Sgshapiroifdef(`_GENERICS_ENTIRE_DOMAIN_',
95090792Sgshapiro`R$* < @ $* $=G > $* 	$: $1 < @ $2 $3 . > $4',
95190792Sgshapiro`R$* < @ $=G > $* 	$: $1 < @ $2 . > $3')',
95290792Sgshapiro`dnl')
95364562Sgshapirodnl remove superfluous dots (maybe repeatedly) which may have been added
95464562Sgshapirodnl by one of the rules before
95538032SpeterR$* < @ $* . . > $*		$1 < @ $2 . > $3
95638032Speter
95738032Speter
95838032Speter##################################################
95938032Speter###  Ruleset 4 -- Final Output Post-rewriting  ###
96038032Speter##################################################
96164562SgshapiroSfinal=4
96238032Speter
96371345SgshapiroR$+ :; <@>		$@ $1 :				handle <list:;>
96438032SpeterR$* <@>			$@				handle <> and list:;
96538032Speter
96638032Speter# strip trailing dot off possibly canonical name
96738032SpeterR$* < @ $+ . > $*	$1 < @ $2 > $3
96838032Speter
96964562Sgshapiro# eliminate internal code
97038032SpeterR$* < @ *LOCAL* > $*	$1 < @ $j > $2
97138032Speter
97238032Speter# externalize local domain info
97338032SpeterR$* < $+ > $*		$1 $2 $3			defocus
97438032SpeterR@ $+ : @ $+ : $+	@ $1 , @ $2 : $3		<route-addr> canonical
97538032SpeterR@ $*			$@ @ $1				... and exit
97638032Speter
97738032Speterifdef(`_NO_UUCP_', `dnl',
97838032Speter`# UUCP must always be presented in old form
97938032SpeterR$+ @ $- . UUCP		$2!$1				u@h.UUCP => h!u')
98038032Speter
98138032Speterifdef(`_USE_DECNET_SYNTAX_',
98238032Speter`# put DECnet back in :: form
98338032SpeterR$+ @ $+ . DECNET	$2 :: $1			u@h.DECNET => h::u',
98438032Speter	`dnl')
98538032Speter# delete duplicate local names
98638032SpeterR$+ % $=w @ $=w		$1 @ $2				u%host@host => u@host
98738032Speter
98838032Speter
98938032Speter
99038032Speter##############################################################
99138032Speter###   Ruleset 97 -- recanonicalize and call ruleset zero   ###
99238032Speter###		   (used for recursive calls)		   ###
99338032Speter##############################################################
99438032Speter
99564562SgshapiroSRecurse=97
99664562SgshapiroR$*			$: $>canonify $1
99764562SgshapiroR$*			$@ $>parse $1
99838032Speter
99938032Speter
100038032Speter######################################
100138032Speter###   Ruleset 0 -- Parse Address   ###
100238032Speter######################################
100338032Speter
100464562SgshapiroSparse=0
100538032Speter
100638032SpeterR$*			$: $>Parse0 $1		initial parsing
100738032SpeterR<@>			$#_LOCAL_ $: <@>		special case error msgs
100864562SgshapiroR$*			$: $>ParseLocal $1	handle local hacks
100938032SpeterR$*			$: $>Parse1 $1		final parsing
101038032Speter
101138032Speter#
101238032Speter#  Parse0 -- do initial syntax checking and eliminate local addresses.
101338032Speter#	This should either return with the (possibly modified) input
101438032Speter#	or return with a #error mailer.  It should not return with a
101538032Speter#	#mailer other than the #error mailer.
101638032Speter#
101738032Speter
101838032SpeterSParse0
101938032SpeterR<@>			$@ <@>			special case error msgs
102090792SgshapiroR$* : $* ; <@>		$#error $@ 5.1.3 $: "_CODE553 List:; syntax illegal for recipient addresses"
102164562SgshapiroR@ <@ $* >		< @ $1 >		catch "@@host" bogosity
102290792SgshapiroR<@ $+>			$#error $@ 5.1.3 $: "_CODE553 User address required"
102390792SgshapiroR$+ <@>			$#error $@ 5.1.3 $: "_CODE553 Hostname required"
102438032SpeterR$*			$: <> $1
102590792Sgshapirodnl allow tricks like [host1]:[host2]
102690792SgshapiroR<> $* < @ [ $* ] : $+ > $*	$1 < @ [ $2 ] : $3 > $4
102790792SgshapiroR<> $* < @ [ $* ] , $+ > $*	$1 < @ [ $2 ] , $3 > $4
102890792Sgshapirodnl but no a@[b]c
102990792SgshapiroR<> $* < @ [ $* ] $+ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid address"
103090792SgshapiroR<> $* < @ [ $+ ] > $*		$1 < @ [ $2 ] > $3
103190792SgshapiroR<> $* <$* : $* > $*	$#error $@ 5.1.3 $: "_CODE553 Colon illegal in host name part"
103238032SpeterR<> $*			$1
103390792SgshapiroR$* < @ . $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
103490792SgshapiroR$* < @ $* .. $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
103590792Sgshapirodnl no a@b@
103690792SgshapiroR$* < @ $* @ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid route address"
103790792Sgshapirodnl no a@b@c
103890792SgshapiroR$* @ $* < @ $* > $*	$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
103964562Sgshapirodnl comma only allowed before @; this check is not complete
104090792SgshapiroR$* , $~O $*		$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
104138032Speter
104290792Sgshapiroifdef(`_STRICT_RFC821_', `# more RFC 821 checks
104390792SgshapiroR$* . < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not end with a dot"
104490792SgshapiroR. $* < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not begin with a dot"
104590792Sgshapirodnl', `dnl')
104690792Sgshapiro
104738032Speter# now delete the local info -- note $=O to find characters that cause forwarding
104864562SgshapiroR$* < @ > $*		$@ $>Parse0 $>canonify $1	user@ => user
104964562SgshapiroR< @ $=w . > : $*	$@ $>Parse0 $>canonify $2	@here:... -> ...
105038032SpeterR$- < @ $=w . >		$: $(dequote $1 $) < @ $2 . >	dequote "foo"@here
105190792SgshapiroR< @ $+ >		$#error $@ 5.1.3 $: "_CODE553 User address required"
105264562SgshapiroR$* $=O $* < @ $=w . >	$@ $>Parse0 $>canonify $1 $2 $3	...@here -> ...
105338032SpeterR$- 			$: $(dequote $1 $) < @ *LOCAL* >	dequote "foo"
105490792SgshapiroR< @ *LOCAL* >		$#error $@ 5.1.3 $: "_CODE553 User address required"
105538032SpeterR$* $=O $* < @ *LOCAL* >
105664562Sgshapiro			$@ $>Parse0 $>canonify $1 $2 $3	...@*LOCAL* -> ...
105738032SpeterR$* < @ *LOCAL* >	$: $1
105838032Speter
1059285303Sgshapiroifdef(`_ADD_BCC_', `dnl
1060285303SgshapiroR$+			$: $>ParseBcc $1', `dnl')
1061285303Sgshapiroifdef(`_PREFIX_MOD_', `dnl
1062285303Sgshapirodnl do this only for addr_type=e r?
1063285303SgshapiroR _PREFIX_MOD_ $+	$: $1 $(macro {rcpt_flags} $@ _PREFIX_FLAGS_ $)
1064285303Sgshapiro')dnl
1065285303Sgshapiro
106638032Speter#
106738032Speter#  Parse1 -- the bottom half of ruleset 0.
106838032Speter#
106938032Speter
107038032SpeterSParse1
107164562Sgshapiroifdef(`_LDAP_ROUTING_', `dnl
107264562Sgshapiro# handle LDAP routing for hosts in $={LDAPRoute}
107390792SgshapiroR$+ < @ $={LDAPRoute} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2> <>
107490792SgshapiroR$+ < @ $={LDAPRouteEquiv} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $M> <>',
107564562Sgshapiro`dnl')
107664562Sgshapiro
107738032Speterifdef(`_MAILER_smtp_',
107838032Speter`# handle numeric address spec
107964562Sgshapirodnl there is no check whether this is really an IP number
108064562SgshapiroR$* < @ [ $+ ] > $*	$: $>ParseLocal $1 < @ [ $2 ] > $3	numeric internet spec
1081112810SgshapiroR$* < @ [ $+ ] > $*	$: $1 < @ [ $2 ] : $S > $3	Add smart host to path
108290792SgshapiroR$* < @ [ $+ ] : > $*		$#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3	no smarthost: send
108364562SgshapiroR$* < @ [ $+ ] : $- : $*> $*	$#$3 $@ $4 $: $1 < @ [$2] > $5	smarthost with mailer
108464562SgshapiroR$* < @ [ $+ ] : $+ > $*	$#_SMTP_ $@ $3 $: $1 < @ [$2] > $4	smarthost without mailer',
108538032Speter	`dnl')
108638032Speter
108764562Sgshapiroifdef(`_VIRTUSER_TABLE_', `dnl
108838032Speter# handle virtual users
108990792Sgshapiroifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
109090792Sgshapirodnl this is not a documented option
109190792Sgshapirodnl it stops looping in virtusertable mapping if input and output
109290792Sgshapirodnl are identical, i.e., if address A is mapped to A.
109390792Sgshapirodnl it does not deal with multi-level recursion
109490792Sgshapiro# handle full domains in RHS of virtusertable
109590792SgshapiroR$+ < @ $+ >			$: $(macro {RecipientAddress} $) $1 < @ $2 >
109690792SgshapiroR$+ < @ $+ > 			$: <?> $1 < @ $2 > $| $>final $1 < @ $2 >
109790792SgshapiroR<?> $+ $| $+			$: $1 $(macro {RecipientAddress} $@ $2 $)
109890792SgshapiroR<?> $+ $| $*			$: $1',
109990792Sgshapiro`dnl')
110064562SgshapiroR$+			$: <!> $1		Mark for lookup
110190792Sgshapirodnl input: <!> local<@domain>
110264562Sgshapiroifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
110364562Sgshapiro`R<!> $+ < @ $* $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >',
110464562Sgshapiro`R<!> $+ < @ $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >')
110590792Sgshapirodnl input: <result-of-lookup | @> local<@domain> | <!> local<@domain>
110664562SgshapiroR<!> $+ < @ $=w . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
110790792Sgshapirodnl if <@> local<@domain>: no match but try lookup
110890792Sgshapirodnl user+detail: try user++@domain if detail not empty
110990792SgshapiroR<@> $+ + $+ < @ $* . >
111090792Sgshapiro			$: < $(virtuser $1 + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
111190792Sgshapirodnl user+detail: try user+*@domain
111238032SpeterR<@> $+ + $* < @ $* . >
111390792Sgshapiro			$: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
111490792Sgshapirodnl user+detail: try user@domain
111538032SpeterR<@> $+ + $* < @ $* . >
111690792Sgshapiro			$: < $(virtuser $1 @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
111764562Sgshapirodnl try default entry: @domain
111890792Sgshapirodnl ++@domain
111990792SgshapiroR<@> $+ + $+ < @ $+ . >	$: < $(virtuser + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
112064562Sgshapirodnl +*@domain
112190792SgshapiroR<@> $+ + $* < @ $+ . >	$: < $(virtuser + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
112264562Sgshapirodnl @domain if +detail exists
112398121Sgshapirodnl if no match, change marker to prevent a second @domain lookup
112498121SgshapiroR<@> $+ + $* < @ $+ . >	$: < $(virtuser @ $3 $@ $1 $@ $2 $@ +$2 $: ! $) > $1 + $2 < @ $3 . >
112598121Sgshapirodnl without +detail
112638032SpeterR<@> $+ < @ $+ . >	$: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
112790792Sgshapirodnl no match
112838032SpeterR<@> $+			$: $1
112990792Sgshapirodnl remove mark
113064562SgshapiroR<!> $+			$: $1
113164562SgshapiroR< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
113238032SpeterR< error : $- $+ > $* 	$#error $@ $(dequote $1 $) $: $2
113390792Sgshapiroifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
113490792Sgshapiro# check virtuser input address against output address, if same, skip recursion
113590792SgshapiroR< $+ > $+ < @ $+ >				$: < $1 > $2 < @ $3 > $| $1
113690792Sgshapiro# it is the same: stop now
113790792SgshapiroR< $+ > $+ < @ $+ > $| $&{RecipientAddress}	$: $>ParseLocal $>Parse0 $>canonify $1
113890792SgshapiroR< $+ > $+ < @ $+ > $| $* 			$: < $1 > $2 < @ $3 >
113990792Sgshapirodnl', `dnl')
114080785Sgshapirodnl this is not a documented option
114180785Sgshapirodnl it performs no looping at all for virtusertable
114277349Sgshapiroifdef(`_NO_VIRTUSER_RECURSION_',
114377349Sgshapiro`R< $+ > $+ < @ $+ >	$: $>ParseLocal $>Parse0 $>canonify $1',
114477349Sgshapiro`R< $+ > $+ < @ $+ >	$: $>Recurse $1')
114577349Sgshapirodnl', `dnl')
114638032Speter
114738032Speter# short circuit local delivery so forwarded email works
114838032Speterifdef(`_MAILER_usenet_', `dnl
114964562SgshapiroR$+ . USENET < @ $=w . >	$#usenet $@ usenet $: $1	handle usenet specially', `dnl')
115066494Sgshapiro
115166494Sgshapiro
115238032Speterifdef(`_STICKY_LOCAL_DOMAIN_',
115338032Speter`R$+ < @ $=w . >		$: < $H > $1 < @ $2 . >		first try hub
115464562SgshapiroR< $+ > $+ < $+ >	$>MailerToTriple < $1 > $2 < $3 >	yep ....
115564562Sgshapirodnl $H empty (but @$=w.)
115638032SpeterR< > $+ + $* < $+ >	$#_LOCAL_ $: $1 + $2		plussed name?
115738032SpeterR< > $+ < $+ >		$#_LOCAL_ $: @ $1			nope, local address',
115864562Sgshapiro`R$=L < @ $=w . >	$#_LOCAL_ $: @ $1			special local names
115938032SpeterR$+ < @ $=w . >		$#_LOCAL_ $: $1			regular local name')
116038032Speter
116164562Sgshapiroifdef(`_MAILER_TABLE_', `dnl
116238032Speter# not local -- try mailer table lookup
116338032SpeterR$* <@ $+ > $*		$: < $2 > $1 < @ $2 > $3	extract host name
116438032SpeterR< $+ . > $*		$: < $1 > $2			strip trailing dot
116538032SpeterR< $+ > $*		$: < $(mailertable $1 $) > $2	lookup
116664562Sgshapirodnl it is $~[ instead of $- to avoid matches on IPv6 addresses
116764562SgshapiroR< $~[ : $* > $* 	$>MailerToTriple < $1 : $2 > $3		check -- resolved?
116864562SgshapiroR< $+ > $*		$: $>Mailertable <$1> $2		try domain',
116938032Speter`dnl')
117064562Sgshapiroundivert(4)dnl UUCP rules from `MAILER(uucp)'
117138032Speter
117238032Speterifdef(`_NO_UUCP_', `dnl',
117338032Speter`# resolve remotely connected UUCP links (if any)
117438032Speterifdef(`_CLASS_V_',
117564562Sgshapiro`R$* < @ $=V . UUCP . > $*		$: $>MailerToTriple < $V > $1 <@$2.UUCP.> $3',
117638032Speter	`dnl')
117738032Speterifdef(`_CLASS_W_',
117864562Sgshapiro`R$* < @ $=W . UUCP . > $*		$: $>MailerToTriple < $W > $1 <@$2.UUCP.> $3',
117938032Speter	`dnl')
118038032Speterifdef(`_CLASS_X_',
118164562Sgshapiro`R$* < @ $=X . UUCP . > $*		$: $>MailerToTriple < $X > $1 <@$2.UUCP.> $3',
118238032Speter	`dnl')')
118338032Speter
118438032Speter# resolve fake top level domains by forwarding to other hosts
118538032Speterifdef(`BITNET_RELAY',
118664562Sgshapiro`R$*<@$+.BITNET.>$*	$: $>MailerToTriple < $B > $1 <@$2.BITNET.> $3	user@host.BITNET',
118738032Speter	`dnl')
118838032Speterifdef(`DECNET_RELAY',
118964562Sgshapiro`R$*<@$+.DECNET.>$*	$: $>MailerToTriple < $C > $1 <@$2.DECNET.> $3	user@host.DECNET',
119038032Speter	`dnl')
119138032Speterifdef(`_MAILER_pop_',
119238032Speter`R$+ < @ POP. >		$#pop $: $1			user@POP',
119338032Speter	`dnl')
119438032Speterifdef(`_MAILER_fax_',
119538032Speter`R$+ < @ $+ .FAX. >	$#fax $@ $2 $: $1		user@host.FAX',
119638032Speter`ifdef(`FAX_RELAY',
119764562Sgshapiro`R$*<@$+.FAX.>$*		$: $>MailerToTriple < $F > $1 <@$2.FAX.> $3	user@host.FAX',
119838032Speter	`dnl')')
119938032Speter
120038032Speterifdef(`UUCP_RELAY',
120138032Speter`# forward non-local UUCP traffic to our UUCP relay
120264562SgshapiroR$*<@$*.UUCP.>$*		$: $>MailerToTriple < $Y > $1 <@$2.UUCP.> $3	uucp mail',
120338032Speter`ifdef(`_MAILER_uucp_',
120438032Speter`# forward other UUCP traffic straight to UUCP
120538032SpeterR$* < @ $+ .UUCP. > $*		$#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3	user@host.UUCP',
120638032Speter	`dnl')')
120738032Speterifdef(`_MAILER_usenet_', `
120838032Speter# addresses sent to net.group.USENET will get forwarded to a newsgroup
120964562SgshapiroR$+ . USENET		$#usenet $@ usenet $: $1',
121038032Speter	`dnl')
121138032Speter
121238032Speterifdef(`_LOCAL_RULES_',
121338032Speter`# figure out what should stay in our local mail system
121438032Speterundivert(1)', `dnl')
121538032Speter
121638032Speter# pass names that still have a host to a smarthost (if defined)
121764562SgshapiroR$* < @ $* > $*		$: $>MailerToTriple < $S > $1 < @ $2 > $3	glue on smarthost name
121838032Speter
121938032Speter# deal with other remote names
122038032Speterifdef(`_MAILER_smtp_',
122164562Sgshapiro`R$* < @$* > $*		$#_SMTP_ $@ $2 $: $1 < @ $2 > $3	user@host.domain',
122290792Sgshapiro`R$* < @$* > $*		$#error $@ 5.1.2 $: "_CODE553 Unrecognized host name " $2')
122338032Speter
122438032Speter# handle locally delivered names
122564562SgshapiroR$=L			$#_LOCAL_ $: @ $1		special local names
122638032SpeterR$+			$#_LOCAL_ $: $1			regular local names
122738032Speter
1228285303Sgshapiroifdef(`_ADD_BCC_', `dnl
1229285303SgshapiroSParseBcc
1230285303SgshapiroR$+			$: $&{addr_type} $| $&A $| $1
1231285303SgshapiroRe b $| $+ $| $+	$>MailerToTriple < $1 > $2	copy?
1232285303SgshapiroR$* $| $* $| $+		$@ $3				no copy
1233285303Sgshapiro')
1234285303Sgshapiro
123538032Speter###########################################################################
123638032Speter###   Ruleset 5 -- special rewriting after aliases have been expanded   ###
123738032Speter###########################################################################
123838032Speter
123964562SgshapiroSLocal_localaddr
124064562SgshapiroSlocaladdr=5
124164562SgshapiroR$+			$: $1 $| $>"Local_localaddr" $1
124290792SgshapiroR$+ $| $#ok		$@ $1			no change
124364562SgshapiroR$+ $| $#$*		$#$2
124464562SgshapiroR$+ $| $*		$: $1
124538032Speter
124690792Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
124790792Sgshapiro# Preserve rcpt_host in {Host}
124890792SgshapiroR$+			$: $1 $| $&h $| $&{Host}	check h and {Host}
124990792SgshapiroR$+ $| $|		$: $(macro {Host} $@ $) $1	no h or {Host}
125090792SgshapiroR$+ $| $| $+		$: $1			h not set, {Host} set
125190792SgshapiroR$+ $| +$* $| $*	$: $1			h is +detail, {Host} set
125295154SgshapiroR$+ $| $* @ $+ $| $*	$: $(macro {Host} $@ @$3 $) $1	set {Host} to host in h
125390792SgshapiroR$+ $| $+ $| $*		$: $(macro {Host} $@ @$2 $) $1	set {Host} to h
125490792Sgshapiro')dnl
125590792Sgshapiro
125690792Sgshapiroifdef(`_FFR_5_', `dnl
125766494Sgshapiro# Preserve host in a macro
125866494SgshapiroR$+			$: $(macro {LocalAddrHost} $) $1
125966494SgshapiroR$+ @ $+		$: $(macro {LocalAddrHost} $@ @ $2 $) $1')
126066494Sgshapiro
126190792Sgshapiroifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `dnl
126238032Speter# deal with plussed users so aliases work nicely
126366494SgshapiroR$+ + *			$#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
126466494SgshapiroR$+ + $*		$#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
126566494Sgshapiro')
126638032Speter# prepend an empty "forward host" on the front
126738032SpeterR$+			$: <> $1
126838032Speter
126938032Speterifdef(`LUSER_RELAY', `dnl
127038032Speter# send unrecognized local users to a relay host
127190792Sgshapiroifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
127266494SgshapiroR< > $+ + $*		$: < ? $L > <+ $2> $(user $1 $)	look up user+
127366494SgshapiroR< > $+			$: < ? $L > < > $(user $1 $)	look up user
127466494SgshapiroR< ? $* > < $* > $+ <>	$: < > $3 $2			found; strip $L
127566494SgshapiroR< ? $* > < $* > $+	$: < $1 > $3 $2			not found', `
127664562SgshapiroR< > $+ 		$: < $L > $(user $1 $)		look up user
127790792SgshapiroR< $* > $+ <>		$: < > $2			found; strip $L')
127890792Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
127990792SgshapiroR< $+ > $+		$: < $1 > $2 $&{Host}')
128090792Sgshapirodnl')
128138032Speter
128290792Sgshapiroifdef(`MAIL_HUB', `dnl
128390792SgshapiroR< > $+			$: < $H > $1			try hub', `dnl')
128490792Sgshapiroifdef(`LOCAL_RELAY', `dnl
128590792SgshapiroR< > $+			$: < $R > $1			try relay', `dnl')
128690792Sgshapiroifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
128790792SgshapiroR< > $+			$@ $1', `dnl
128864562SgshapiroR< > $+			$: < > < $1 <> $&h >		nope, restore +detail
128990792Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
129090792SgshapiroR< > < $+ @ $+ <> + $* >	$: < > < $1 + $3 @ $2 >	check whether +detail')
129164562SgshapiroR< > < $+ <> + $* >	$: < > < $1 + $2 >		check whether +detail
129264562SgshapiroR< > < $+ <> $* >	$: < > < $1 >			else discard
129338032SpeterR< > < $+ + $* > $*	   < > < $1 > + $2 $3		find the user part
129466494SgshapiroR< > < $+ > + $*	$#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')		strip the extra +
129538032SpeterR< > < $+ >		$@ $1				no +detail
129643730SpeterR$+			$: $1 <> $&h			add +detail back in
129790792Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
129890792SgshapiroR$+ @ $+ <> + $*	$: $1 + $3 @ $2			check whether +detail')
129943730SpeterR$+ <> + $*		$: $1 + $2			check whether +detail
130066494SgshapiroR$+ <> $*		$: $1				else discard')
130164562SgshapiroR< local : $* > $*	$: $>MailerToTriple < local : $1 > $2	no host extension
130264562SgshapiroR< error : $* > $*	$: $>MailerToTriple < error : $1 > $2	no host extension
130390792Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
130490792Sgshapirodnl it is $~[ instead of $- to avoid matches on IPv6 addresses
130590792SgshapiroR< $~[ : $+ > $+ @ $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $4 >')
130690792SgshapiroR< $~[ : $+ > $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $2 >
130790792Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
130890792SgshapiroR< $+ > $+ @ $+		$@ $>MailerToTriple < $1 > $2 < @ $3 >')
130964562SgshapiroR< $+ > $+		$@ $>MailerToTriple < $1 > $2 < @ $1 >
131038032Speter
131164562Sgshapiroifdef(`_MAILER_TABLE_', `dnl
131290792Sgshapiroifdef(`_LDAP_ROUTING_', `dnl
131338032Speter###################################################################
131490792Sgshapiro###  Ruleset LDAPMailertable -- mailertable lookup for LDAP     ###
131590792Sgshapirodnl input: <Domain> FullAddress
131690792Sgshapiro###################################################################
131790792Sgshapiro
131890792SgshapiroSLDAPMailertable
131990792SgshapiroR< $+ > $*		$: < $(mailertable $1 $) > $2		lookup
132090792SgshapiroR< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		check resolved?
132190792SgshapiroR< $+ > $*		$: < $1 > $>Mailertable <$1> $2		try domain
132290792SgshapiroR< $+ > $#$*		$#$2					found
132390792SgshapiroR< $+ > $*		$#_RELAY_ $@ $1 $: $2			not found, direct relay',
132490792Sgshapiro`dnl')
132590792Sgshapiro
132690792Sgshapiro###################################################################
132738032Speter###  Ruleset 90 -- try domain part of mailertable entry 	###
132864562Sgshapirodnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress
132938032Speter###################################################################
133038032Speter
133164562SgshapiroSMailertable=90
133264562Sgshapirodnl shift and check
133364562Sgshapirodnl %2 is not documented in cf/README
133438032SpeterR$* <$- . $+ > $*	$: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
133564562Sgshapirodnl it is $~[ instead of $- to avoid matches on IPv6 addresses
133664562SgshapiroR$* <$~[ : $* > $*	$>MailerToTriple < $2 : $3 > $4		check -- resolved?
133764562SgshapiroR$* < . $+ > $* 	$@ $>Mailertable $1 . <$2> $3		no -- strip & try again
133864562Sgshapirodnl is $2 always empty?
133938032SpeterR$* < $* > $*		$: < $(mailertable . $@ $1$2 $) > $3	try "."
134064562SgshapiroR< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		"." found?
134164562Sgshapirodnl return full address
134238032SpeterR< $* > $*		$@ $2				no mailertable match',
134338032Speter`dnl')
134438032Speter
134538032Speter###################################################################
134638032Speter###  Ruleset 95 -- canonify mailer:[user@]host syntax to triple	###
134764562Sgshapirodnl input: in general: <[mailer:]host> lp<@domain>rest
134864562Sgshapirodnl	<> address				-> address
134964562Sgshapirodnl	<error:d.s.n:text>			-> error
1350120256Sgshapirodnl	<error:keyword:text>			-> error
135164562Sgshapirodnl	<error:text>				-> error
135264562Sgshapirodnl	<mailer:user@host> lp<@domain>rest	-> mailer host user
135364562Sgshapirodnl	<mailer:host> address			-> mailer host address
135464562Sgshapirodnl	<localdomain> address			-> address
135564562Sgshapirodnl	<host> address				-> relay host address
135638032Speter###################################################################
135738032Speter
135864562SgshapiroSMailerToTriple=95
135938032SpeterR< > $*				$@ $1			strip off null relay
136064562SgshapiroR< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
1361120256SgshapiroR< error : $- : $+ > $*		$#error $@ $(dequote $1 $) $: $2
1362120256SgshapiroR< error : $+ > $*		$#error $: $1
136338032SpeterR< local : $* > $*		$>CanonLocal < $1 > $2
136490792Sgshapirodnl it is $~[ instead of $- to avoid matches on IPv6 addresses
136590792SgshapiroR< $~[ : $+ @ $+ > $*<$*>$*	$# $1 $@ $3 $: $2<@$3>	use literal user
136690792SgshapiroR< $~[ : $+ > $*		$# $1 $@ $2 $: $3	try qualified mailer
136738032SpeterR< $=w > $*			$@ $2			delete local host
136838032SpeterR< $+ > $*			$#_RELAY_ $@ $1 $: $2	use unqualified mailer
136938032Speter
137038032Speter###################################################################
137138032Speter###  Ruleset CanonLocal -- canonify local: syntax		###
137264562Sgshapirodnl input: <user> address
137364562Sgshapirodnl <x> <@host> : rest			-> Recurse rest
137464562Sgshapirodnl <x> p1 $=O p2 <@host>		-> Recurse p1 $=O p2
137564562Sgshapirodnl <> user <@host> rest		-> local user@host user
137664562Sgshapirodnl <> user				-> local user user
137764562Sgshapirodnl <user@host> lp <@domain> rest	-> <user> lp <@host> [cont]
137864562Sgshapirodnl <user> lp <@host> rest		-> local lp@host user
137964562Sgshapirodnl <user> lp				-> local lp user
138038032Speter###################################################################
138138032Speter
138238032SpeterSCanonLocal
138343730Speter# strip local host from routed addresses
138464562SgshapiroR< $* > < @ $+ > : $+		$@ $>Recurse $3
138564562SgshapiroR< $* > $+ $=O $+ < @ $+ >	$@ $>Recurse $2 $3 $4
138643730Speter
138738032Speter# strip trailing dot from any host name that may appear
138838032SpeterR< $* > $* < @ $* . >		$: < $1 > $2 < @ $3 >
138938032Speter
139038032Speter# handle local: syntax -- use old user, either with or without host
139138032SpeterR< > $* < @ $* > $*		$#_LOCAL_ $@ $1@$2 $: $1
139238032SpeterR< > $+				$#_LOCAL_ $@ $1    $: $1
139338032Speter
139438032Speter# handle local:user@host syntax -- ignore host part
139538032SpeterR< $+ @ $+ > $* < @ $* >	$: < $1 > $3 < @ $4 >
139638032Speter
139738032Speter# handle local:user syntax
139838032SpeterR< $+ > $* <@ $* > $*		$#_LOCAL_ $@ $2@$3 $: $1
139938032SpeterR< $+ > $* 			$#_LOCAL_ $@ $2    $: $1
140038032Speter
140138032Speter###################################################################
140238032Speter###  Ruleset 93 -- convert header names to masqueraded form	###
140338032Speter###################################################################
140438032Speter
140564562SgshapiroSMasqHdr=93
140638032Speter
140764562Sgshapiroifdef(`_GENERICS_TABLE_', `dnl
140838032Speter# handle generics database
140938032Speterifdef(`_GENERICS_ENTIRE_DOMAIN_',
141064562Sgshapirodnl if generics should be applied add a @ as mark
141138032Speter`R$+ < @ $* $=G . >	$: < $1@$2$3 > $1 < @ $2$3 . > @	mark',
141238032Speter`R$+ < @ $=G . >	$: < $1@$2 > $1 < @ $2 . > @	mark')
141338032SpeterR$+ < @ *LOCAL* >	$: < $1@$j > $1 < @ *LOCAL* > @	mark
141464562Sgshapirodnl workspace: either user<@domain> or <user@domain> user <@domain> @
141564562Sgshapirodnl ignore the first case for now
141664562Sgshapirodnl if it has the mark lookup full address
141790792Sgshapirodnl broken: %1 is full address not just detail
141864562SgshapiroR< $+ > $+ < $* > @	$: < $(generics $1 $: @ $1 $) > $2 < $3 >
141964562Sgshapirodnl workspace: ... or <match|@user@domain> user <@domain>
142064562Sgshapirodnl no match, try user+detail@domain
142164562SgshapiroR<@$+ + $* @ $+> $+ < @ $+ >
142264562Sgshapiro		$: < $(generics $1+*@$3 $@ $2 $:@$1 + $2@$3 $) >  $4 < @ $5 >
142364562SgshapiroR<@$+ + $* @ $+> $+ < @ $+ >
142464562Sgshapiro		$: < $(generics $1@$3 $: $) > $4 < @ $5 >
142564562Sgshapirodnl no match, remove mark
142664562SgshapiroR<@$+ > $+ < @ $+ >	$: < > $2 < @ $3 >
142764562Sgshapirodnl no match, try @domain for exceptions
142864562SgshapiroR< > $+ < @ $+ . >	$: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . >
142964562Sgshapirodnl workspace: ... or <match> user <@domain>
143064562Sgshapirodnl no match, try local part
143138032SpeterR< > $+ < @ $+ > 	$: < $(generics $1 $: $) > $1 < @ $2 >
143264562SgshapiroR< > $+ + $* < @ $+ > 	$: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 >
143364562SgshapiroR< > $+ + $* < @ $+ > 	$: < $(generics $1 $: $) > $1 + $2 < @ $3 >
143464562SgshapiroR< $* @ $* > $* < $* >	$@ $>canonify $1 @ $2		found qualified
143564562SgshapiroR< $+ > $* < $* >	$: $>canonify $1 @ *LOCAL*	found unqualified
143638032SpeterR< > $*			$: $1				not found',
143738032Speter`dnl')
143838032Speter
143964562Sgshapiro# do not masquerade anything in class N
144064562SgshapiroR$* < @ $* $=N . >	$@ $1 < @ $2 $3 . >
144164562Sgshapiro
144290792Sgshapiroifdef(`MASQUERADE_NAME', `dnl
144338032Speter# special case the users that should be exposed
144438032SpeterR$=E < @ *LOCAL* >	$@ $1 < @ $j . >		leave exposed
144538032Speterifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
144638032Speter`R$=E < @ $* $=M . >	$@ $1 < @ $2 $3 . >',
144738032Speter`R$=E < @ $=M . >	$@ $1 < @ $2 . >')
144838032Speterifdef(`_LIMITED_MASQUERADE_', `dnl',
144938032Speter`R$=E < @ $=w . >	$@ $1 < @ $2 . >')
145038032Speter
145138032Speter# handle domain-specific masquerading
145238032Speterifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
145338032Speter`R$* < @ $* $=M . > $*	$: $1 < @ $2 $3 . @ $M > $4	convert masqueraded doms',
145438032Speter`R$* < @ $=M . > $*	$: $1 < @ $2 . @ $M > $3	convert masqueraded doms')
145538032Speterifdef(`_LIMITED_MASQUERADE_', `dnl',
145638032Speter`R$* < @ $=w . > $*	$: $1 < @ $2 . @ $M > $3')
145738032SpeterR$* < @ *LOCAL* > $*	$: $1 < @ $j . @ $M > $2
145838032SpeterR$* < @ $+ @ > $*	$: $1 < @ $2 > $3		$M is null
145938032SpeterR$* < @ $+ @ $+ > $*	$: $1 < @ $3 . > $4		$M is not null
146090792Sgshapirodnl', `dnl no masquerading
146190792Sgshapirodnl just fix *LOCAL* leftovers
146290792SgshapiroR$* < @ *LOCAL* >	$@ $1 < @ $j . >')
146338032Speter
146438032Speter###################################################################
146538032Speter###  Ruleset 94 -- convert envelope names to masqueraded form	###
146638032Speter###################################################################
146738032Speter
146864562SgshapiroSMasqEnv=94
146938032Speterifdef(`_MASQUERADE_ENVELOPE_',
147064562Sgshapiro`R$+			$@ $>MasqHdr $1',
147138032Speter`R$* < @ *LOCAL* > $*	$: $1 < @ $j . > $2')
147238032Speter
147338032Speter###################################################################
147438032Speter###  Ruleset 98 -- local part of ruleset zero (can be null)	###
147538032Speter###################################################################
147638032Speter
147764562SgshapiroSParseLocal=98
147864562Sgshapiroundivert(3)dnl LOCAL_RULE_0
147938032Speter
148064562Sgshapiroifdef(`_LDAP_ROUTING_', `dnl
148190792Sgshapiro######################################################################
148290792Sgshapiro###  LDAPExpand: Expand address using LDAP routing
148390792Sgshapiro###
148490792Sgshapiro###	Parameters:
148590792Sgshapiro###		<$1> -- parsed address (user < @ domain . >) (pass through)
148690792Sgshapiro###		<$2> -- RFC822 address (user @ domain) (used for lookup)
148790792Sgshapiro###		<$3> -- +detail information
148890792Sgshapiro###
148990792Sgshapiro###	Returns:
149090792Sgshapiro###		Mailer triplet ($#mailer $@ host $: address)
149190792Sgshapiro###		Parsed address (user < @ domain . >)
149290792Sgshapiro######################################################################
149390792Sgshapiro
149464562SgshapiroSLDAPExpand
149564562Sgshapiro# do the LDAP lookups
149690792SgshapiroR<$+><$+><$*>	$: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2> <$3>
149764562Sgshapiro
1498132943Sgshapiro# look for temporary failures and...
1499132943SgshapiroR<$* <TMPF>> <$*> <$+> <$+> <$*>	$: $&{opMode} $| TMPF <$&{addr_type}> $| $3
1500132943SgshapiroR<$*> <$* <TMPF>> <$+> <$+> <$*>	$: $&{opMode} $| TMPF <$&{addr_type}> $| $3
1501132943Sgshapiroifelse(_LDAP_ROUTE_MAPTEMP_, `_TEMPFAIL_', `dnl
1502132943Sgshapiro# ... temp fail RCPT SMTP commands
1503132943SgshapiroR$={SMTPOpModes} $| TMPF <e r> $| $+	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."')
1504132943Sgshapiro# ... return original address for MTA to queue up
1505132943SgshapiroR$* $| TMPF <$*> $| $+			$@ $3
150694334Sgshapiro
150764562Sgshapiro# if mailRoutingAddress and local or non-existant mailHost,
150864562Sgshapiro# return the new mailRoutingAddress
150990792Sgshapiroifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
151090792SgshapiroR<$+@$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $6 @ $2
151190792SgshapiroR<$+@$+> <> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $5 @ $2')
151290792SgshapiroR<$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1
151390792SgshapiroR<$+> <> <$+> <$+> <$*>		$@ $>Parse0 $>canonify $1
151464562Sgshapiro
151598121Sgshapiro
151664562Sgshapiro# if mailRoutingAddress and non-local mailHost,
151764562Sgshapiro# relay to mailHost with new mailRoutingAddress
151890792Sgshapiroifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
151990792Sgshapiroifdef(`_MAILER_TABLE_', `dnl
152090792Sgshapiro# check mailertable for host, relay from there
152190792SgshapiroR<$+@$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$3> $>canonify $1 $6 @ $2',
152290792Sgshapiro`R<$+@$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $3 $: $>canonify $1 $6 @ $2')')
152390792Sgshapiroifdef(`_MAILER_TABLE_', `dnl
152490792Sgshapiro# check mailertable for host, relay from there
152590792SgshapiroR<$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$2> $>canonify $1',
152690792Sgshapiro`R<$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $2 $: $>canonify $1')
152764562Sgshapiro
152864562Sgshapiro# if no mailRoutingAddress and local mailHost,
152964562Sgshapiro# return original address
153090792SgshapiroR<> <$=w> <$+> <$+> <$*>	$@ $2
153164562Sgshapiro
153298121Sgshapiro
153364562Sgshapiro# if no mailRoutingAddress and non-local mailHost,
153464562Sgshapiro# relay to mailHost with original address
153590792Sgshapiroifdef(`_MAILER_TABLE_', `dnl
153690792Sgshapiro# check mailertable for host, relay from there
153790792SgshapiroR<> <$+> <$+> <$+> <$*>		$>LDAPMailertable <$1> $2',
153890792Sgshapiro`R<> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $1 $: $2')
153964562Sgshapiro
154090792Sgshapiroifdef(`_LDAP_ROUTE_DETAIL_',
154190792Sgshapiro`# if no mailRoutingAddress and no mailHost,
154290792Sgshapiro# try without +detail
154390792SgshapiroR<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <$2 @ $4> <+$3>')dnl
154490792Sgshapiro
1545203004Sgshapiroifdef(`_LDAP_ROUTE_NODOMAIN_', `
1546203004Sgshapiro# pretend we did the @domain lookup
1547203004SgshapiroR<> <> <$+> <$+ @ $+> <$*>	$: <> <> <$1> <@ $3> <$4>', `
154890792Sgshapiro# if still no mailRoutingAddress and no mailHost,
154964562Sgshapiro# try @domain
155090792Sgshapiroifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
155190792SgshapiroR<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <@ $4> <+$3>')
1552132943SgshapiroR<> <> <$+> <$+ @ $+> <$*>	$@ $>LDAPExpand <$1> <@ $3> <$4>')
155364562Sgshapiro
155464562Sgshapiro# if no mailRoutingAddress and no mailHost and this was a domain attempt,
155564562Sgshapiroifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl
155664562Sgshapiro# user does not exist
155790792SgshapiroR<> <> <$+> <@ $+> <$*>		$: <?> < $&{addr_type} > < $1 >
155890792Sgshapiro# only give error for envelope recipient
155990792SgshapiroR<?> <e r> <$+>			$#error $@ nouser $: "550 User unknown"
1560132943Sgshapiroifdef(`_LDAP_SENDER_MUST_EXIST_', `dnl
1561132943Sgshapiro# and the sender too
1562132943SgshapiroR<?> <e s> <$+>			$#error $@ nouser $: "550 User unknown"')
156390792SgshapiroR<?> <$*> <$+>			$@ $2',
156464562Sgshapiro`dnl
156564562Sgshapiro# return the original address
1566244833SgshapiroR<> <> <$+> <@ $+> <$*>		$@ $1')
1567244833Sgshapiro')
156864562Sgshapiro
1569244833Sgshapiro
157064562Sgshapiroifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode.
157164562Sgshapiro')')
157290792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
157338032Speter######################################################################
157490792Sgshapiro###  D: LookUpDomain -- search for domain in access database
157538032Speter###
157638032Speter###	Parameters:
157738032Speter###		<$1> -- key (domain name)
157838032Speter###		<$2> -- default (what to return if not found in db)
157964562Sgshapirodnl			must not be empty
158090792Sgshapiro###		<$3> -- mark (must be <(!|+) single-token>)
158164562Sgshapiro###			! does lookup only with tag
158264562Sgshapiro###			+ does lookup with and without tag
158390792Sgshapiro###		<$4> -- passthru (additional data passed unchanged through)
158464562Sgshapirodnl returns:		<default> <passthru>
158564562Sgshapirodnl 			<result> <passthru>
158638032Speter######################################################################
158738032Speter
158890792SgshapiroSD
158964562Sgshapirodnl workspace <key> <default> <passthru> <mark>
159064562Sgshapirodnl lookup with tag (in front, no delimiter here)
159190792Sgshapirodnl    2    3  4    5
159290792SgshapiroR<$*> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
159364562Sgshapirodnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark>
159464562Sgshapirodnl lookup without tag?
159590792Sgshapirodnl   1    2      3    4
159690792SgshapiroR<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
159790792Sgshapiroifdef(`_LOOKUPDOTDOMAIN_', `dnl omit first component: lookup .rest
159890792Sgshapirodnl XXX apply this also to IP addresses?
159990792Sgshapirodnl currently it works the wrong way round for [1.2.3.4]
160090792Sgshapirodnl   1  2    3    4  5    6
160190792SgshapiroR<?> <$+.$+> <$+> <$- $-> <$*>	$: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4 $5> <$6>
160290792Sgshapirodnl   1  2    3      4    5
160390792SgshapiroR<?> <$+.$+> <$+> <+ $-> <$*>	$: < $(access .$2 $: ? $) > <$1.$2> <$3> <+ $4> <$5>', `dnl')
160490792Sgshapiroifdef(`_ACCESS_SKIP_', `dnl
160590792Sgshapirodnl found SKIP: return <default> and <passthru>
160690792Sgshapirodnl      1    2    3  4    5
160790792SgshapiroR<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
160890792Sgshapirodnl not found: IPv4 net (no check is done whether it is an IP number!)
160990792Sgshapirodnl    1  2     3    4  5    6
161090792SgshapiroR<?> <[$+.$-]> <$+> <$- $-> <$*>	$@ $>D <[$1]> <$3> <$4 $5> <$6>
161190792Sgshapiroifdef(`NO_NETINET6', `dnl',
161290792Sgshapiro`dnl not found: IPv6 net
161390792Sgshapirodnl (could be merged with previous rule if we have a class containing .:)
161490792Sgshapirodnl    1   2     3    4  5    6
161590792SgshapiroR<?> <[$+::$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>
161690792SgshapiroR<?> <[$+:$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>')
161764562Sgshapirodnl not found, but subdomain: try again
161890792Sgshapirodnl   1  2    3    4  5    6
161990792SgshapiroR<?> <$+.$+> <$+> <$- $-> <$*>	$@ $>D <$2> <$3> <$4 $5> <$6>
162090792Sgshapiroifdef(`_FFR_LOOKUPTAG_', `dnl lookup Tag:
162190792Sgshapirodnl   1    2      3    4
162290792SgshapiroR<?> <$+> <$+> <! $-> <$*>	$: < $(access $3`'_TAG_DELIM_ $: ? $) > <$1> <$2> <! $3> <$4>', `dnl')
162390792Sgshapirodnl not found, no subdomain: return <default> and <passthru>
162490792Sgshapirodnl   1    2    3  4    5
162590792SgshapiroR<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
162690792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
162790792Sgshapirodnl            2    3    4  5    6
162890792SgshapiroR<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
162990792Sgshapirodnl return <result of lookup> and <passthru>
163090792Sgshapirodnl    2    3    4  5    6
163190792SgshapiroR<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
163238032Speter
163338032Speter######################################################################
163490792Sgshapiro###  A: LookUpAddress -- search for host address in access database
163538032Speter###
163638032Speter###	Parameters:
163738032Speter###		<$1> -- key (dot quadded host address)
163838032Speter###		<$2> -- default (what to return if not found in db)
163964562Sgshapirodnl			must not be empty
164090792Sgshapiro###		<$3> -- mark (must be <(!|+) single-token>)
164164562Sgshapiro###			! does lookup only with tag
164264562Sgshapiro###			+ does lookup with and without tag
164390792Sgshapiro###		<$4> -- passthru (additional data passed through)
164464562Sgshapirodnl	returns:	<default> <passthru>
164564562Sgshapirodnl			<result> <passthru>
164638032Speter######################################################################
164738032Speter
164890792SgshapiroSA
164964562Sgshapirodnl lookup with tag
165090792Sgshapirodnl    2    3  4    5
165190792SgshapiroR<$+> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
165264562Sgshapirodnl lookup without tag
165390792Sgshapirodnl   1    2      3    4
165490792SgshapiroR<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
165590792Sgshapirodnl workspace <result-of-lookup|?> <key> <default> <mark> <passthru>
165690792Sgshapiroifdef(`_ACCESS_SKIP_', `dnl
165790792Sgshapirodnl found SKIP: return <default> and <passthru>
165890792Sgshapirodnl      1    2    3  4    5
165990792SgshapiroR<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
166090792Sgshapiroifdef(`NO_NETINET6', `dnl',
166190792Sgshapiro`dnl no match; IPv6: remove last part
166290792Sgshapirodnl   1   2    3    4  5    6
166390792SgshapiroR<?> <$+::$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
166490792SgshapiroR<?> <$+:$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>')
166564562Sgshapirodnl no match; IPv4: remove last part
166690792Sgshapirodnl   1  2    3    4  5    6
166790792SgshapiroR<?> <$+.$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
166864562Sgshapirodnl no match: return default
166990792Sgshapirodnl   1    2    3  4    5
167090792SgshapiroR<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
167190792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
167290792Sgshapirodnl            2    3    4  5    6
167390792SgshapiroR<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
167464562Sgshapirodnl match: return result
167590792Sgshapirodnl    2    3    4  5    6
167690792SgshapiroR<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
167790792Sgshapirodnl endif _ACCESS_TABLE_
167890792Sgshapirodivert(0)
167938032Speter######################################################################
168042575Speter###  CanonAddr --	Convert an address into a standard form for
168142575Speter###			relay checking.  Route address syntax is
168242575Speter###			crudely converted into a %-hack address.
168342575Speter###
168442575Speter###	Parameters:
168542575Speter###		$1 -- full recipient address
168642575Speter###
168742575Speter###	Returns:
168842575Speter###		parsed address, not in source route form
168964562Sgshapirodnl		user%host%host<@domain>
169064562Sgshapirodnl		host!user<@domain>
169142575Speter######################################################################
169242575Speter
169342575SpeterSCanonAddr
169464562SgshapiroR$*			$: $>Parse0 $>canonify $1	make domain canonical
169564562Sgshapiroifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
169642575SpeterR< @ $+ > : $* @ $*	< @ $1 > : $2 % $3	change @ to % in src route
169742575SpeterR$* < @ $+ > : $* : $*	$3 $1 < @ $2 > : $4	change to % hack.
169842575SpeterR$* < @ $+ > : $*	$3 $1 < @ $2 >
169964562Sgshapirodnl')
170042575Speter
170142575Speter######################################################################
170238032Speter###  ParseRecipient --	Strip off hosts in $=R as well as possibly
170338032Speter###			$* $=m or the access database.
170438032Speter###			Check user portion for host separators.
170538032Speter###
170638032Speter###	Parameters:
170738032Speter###		$1 -- full recipient address
170838032Speter###
170938032Speter###	Returns:
171038032Speter###		parsed, non-local-relaying address
171138032Speter######################################################################
171238032Speter
171338032SpeterSParseRecipient
171464562Sgshapirodnl mark and canonify address
171542575SpeterR$*				$: <?> $>CanonAddr $1
171664562Sgshapirodnl workspace: <?> localpart<@domain[.]>
171742575SpeterR<?> $* < @ $* . >		<?> $1 < @ $2 >			strip trailing dots
171864562Sgshapirodnl workspace: <?> localpart<@domain>
171942575SpeterR<?> $- < @ $* >		$: <?> $(dequote $1 $) < @ $2 >	dequote local part
172038032Speter
172138032Speter# if no $=O character, no host in the user portion, we are done
172242575SpeterR<?> $* $=O $* < @ $* >		$: <NO> $1 $2 $3 < @ $4>
172364562Sgshapirodnl no $=O in localpart: return
172442575SpeterR<?> $*				$@ $1
172538032Speter
172690792Sgshapirodnl workspace: <NO> localpart<@domain>, where localpart contains $=O
172764562Sgshapirodnl mark everything which has an "authorized" domain with <RELAY>
172838032Speterifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
172938032Speter# if we relay, check username portion for user%host so host can be checked also
173042575SpeterR<NO> $* < @ $* $=m >		$: <RELAY> $1 < @ $2 $3 >', `dnl')
173164562Sgshapirodnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O
173264562Sgshapirodnl if mark is <NO> then change it to <RELAY> if domain is "authorized"
173390792Sgshapiro
173490792Sgshapirodnl what if access map returns something else than RELAY?
173590792Sgshapirodnl we are only interested in RELAY entries...
173690792Sgshapirodnl other To: entries: blacklist recipient; generic entries?
173790792Sgshapirodnl if it is an error we probably do not want to relay anyway
173838032Speterifdef(`_RELAY_HOSTS_ONLY_',
173942575Speter`R<NO> $* < @ $=R >		$: <RELAY> $1 < @ $2 >
174064562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
174164562SgshapiroR<NO> $* < @ $+ >		$: <$(access To:$2 $: NO $)> $1 < @ $2 >
174242575SpeterR<NO> $* < @ $+ >		$: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')',
174342575Speter`R<NO> $* < @ $* $=R >		$: <RELAY> $1 < @ $2 $3 >
174464562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
174590792SgshapiroR<NO> $* < @ $+ >		$: $>D <$2> <NO> <+ To> <$1 < @ $2 >>
174642575SpeterR<$+> <$+>			$: <$1> $2',`dnl')')
174738032Speter
174864562Sgshapiro
174990792Sgshapiroifdef(`_RELAY_MX_SERVED_', `dnl
175090792Sgshapirodnl do "we" ($=w) act as backup MX server for the destination domain?
175190792SgshapiroR<NO> $* < @ $+ >		$: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > >
1752132943SgshapiroR<MX> < : $* <TEMP> : > $*	$#TEMP $@ 4.4.0 $: "450 Can not check MX records for recipient host " $1
175390792Sgshapirodnl yes: mark it as <RELAY>
175490792SgshapiroR<MX> < $* : $=w. : $* > < $+ >	$: <RELAY> $4
175590792Sgshapirodnl no: put old <NO> mark back
175690792SgshapiroR<MX> < : $* : > < $+ >		$: <NO> $2', `dnl')
175790792Sgshapiro
175890792Sgshapirodnl do we relay to this recipient domain?
175942575SpeterR<RELAY> $* < @ $* >		$@ $>ParseRecipient $1
176090792Sgshapirodnl something else
176190792SgshapiroR<$+> $*			$@ $2
176242575Speter
176364562Sgshapiro
176438032Speter######################################################################
176538032Speter###  check_relay -- check hostname/address on SMTP startup
176638032Speter######################################################################
176738032Speter
1768132943Sgshapiroifdef(`_CONTROL_IMMEDIATE_',`dnl
1769132943SgshapiroScheck_relay
1770132943Sgshapiroifdef(`_RATE_CONTROL_IMMEDIATE_',`dnl
1771132943Sgshapirodnl workspace: ignored...
1772132943SgshapiroR$*		$: $>"RateControl" dummy', `dnl')
1773132943Sgshapiroifdef(`_CONN_CONTROL_IMMEDIATE_',`dnl
1774132943Sgshapirodnl workspace: ignored...
1775132943SgshapiroR$*		$: $>"ConnControl" dummy', `dnl')
1776132943Sgshapirodnl')
1777132943Sgshapiro
177838032SpeterSLocal_check_relay
177964562SgshapiroScheck`'_U_`'relay
1780132943Sgshapiroifdef(`_USE_CLIENT_PTR_',`dnl
1781132943SgshapiroR$* $| $*		$: $&{client_ptr} $| $2', `dnl')
178238032SpeterR$*			$: $1 $| $>"Local_check_relay" $1
178338032SpeterR$* $| $* $| $#$*	$#$3
178438032SpeterR$* $| $* $| $*		$@ $>"Basic_check_relay" $1 $| $2
178538032Speter
178638032SpeterSBasic_check_relay
178738032Speter# check for deferred delivery mode
178898121SgshapiroR$*			$: < $&{deliveryMode} > $1
178938032SpeterR< d > $*		$@ deferred
179038032SpeterR< $* > $*		$: $2
179138032Speter
179264562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
179366494Sgshapirodnl workspace: {client_name} $| {client_addr}
179490792SgshapiroR$+ $| $+		$: $>D < $1 > <?> <+ Connect> < $2 >
179566494Sgshapirodnl workspace: <result-of-lookup> <{client_addr}>
1796110560Sgshapirodnl OR $| $+ if client_name is empty
1797110560SgshapiroR   $| $+		$: $>A < $1 > <?> <+ Connect> <>	empty client_name
1798110560Sgshapirodnl workspace: <result-of-lookup> <{client_addr}>
179990792SgshapiroR<?> <$+>		$: $>A < $1 > <?> <+ Connect> <>	no: another lookup
180090792Sgshapirodnl workspace: <result-of-lookup> (<>|<{client_addr}>)
180190792SgshapiroR<?> <$*>		$: OK				found nothing
180290792Sgshapirodnl workspace: <result-of-lookup> (<>|<{client_addr}>) | OK
180390792SgshapiroR<$={Accept}> <$*>	$@ $1				return value of lookup
1804132943SgshapiroR<REJECT> <$*>		$#error ifdef(`confREJECT_MSG', `$: confREJECT_MSG', `$@ 5.7.1 $: "550 Access denied"')
180590792SgshapiroR<DISCARD> <$*>		$#discard $: discard
1806132943SgshapiroR<QUARANTINE:$+> <$*>	$#error $@ quarantine $: $1
180764562Sgshapirodnl error tag
180866494SgshapiroR<ERROR:$-.$-.$-:$+> <$*>	$#error $@ $1.$2.$3 $: $4
180966494SgshapiroR<ERROR:$+> <$*>		$#error $: $1
181090792Sgshapiroifdef(`_ATMPF_', `R<$* _ATMPF_> <$*>		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
181164562Sgshapirodnl generic error from access map
181266494SgshapiroR<$+> <$*>		$#error $: $1', `dnl')
181338032Speter
181464562Sgshapiroifdef(`_RBL_',`dnl
181564562Sgshapiro# DNS based IP address spam list
181690792Sgshapirodnl workspace: ignored...
181738032SpeterR$*			$: $&{client_addr}
181864562SgshapiroR$-.$-.$-.$-		$: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
181964562SgshapiroR<?>OK			$: OKSOFAR
182098121SgshapiroR<?>$+			$#error $@ 5.7.1 $: "550 Rejected: " $&{client_addr} " listed at _RBL_"',
182138032Speter`dnl')
1822132943Sgshapiroifdef(`_RATE_CONTROL_',`dnl
1823132943Sgshapiroifdef(`_RATE_CONTROL_IMMEDIATE_',`', `dnl
1824132943Sgshapirodnl workspace: ignored...
1825132943SgshapiroR$*		$: $>"RateControl" dummy')', `dnl')
1826132943Sgshapiroifdef(`_CONN_CONTROL_',`dnl
1827132943Sgshapiroifdef(`_CONN_CONTROL_IMMEDIATE_',`',`dnl
1828132943Sgshapirodnl workspace: ignored...
1829132943SgshapiroR$*		$: $>"ConnControl" dummy')', `dnl')
1830223067Sgshapiroundivert(8)dnl LOCAL_DNSBL
1831168515Sgshapiroifdef(`_REQUIRE_RDNS_', `dnl
1832168515SgshapiroR$*			$: $&{client_addr} $| $&{client_resolve}
1833168515SgshapiroR$=R $*			$@ RELAY		We relay for these
1834168515SgshapiroR$* $| OK		$@ OK			Resolves.
1835168515SgshapiroR$* $| FAIL		$#error $@ 5.7.1 $: 550 Fix reverse DNS for $1
1836168515SgshapiroR$* $| TEMP		$#error $@ 4.1.8 $: 451 Client IP address $1 does not resolve
1837168515SgshapiroR$* $| FORGED		$#error $@ 4.1.8 $: 451 Possibly forged hostname for $1
1838168515Sgshapiro', `dnl')
183938032Speter
184038032Speter######################################################################
184138032Speter###  check_mail -- check SMTP ``MAIL FROM:'' command argument
184238032Speter######################################################################
184338032Speter
184438032SpeterSLocal_check_mail
184564562SgshapiroScheck`'_U_`'mail
184638032SpeterR$*			$: $1 $| $>"Local_check_mail" $1
184738032SpeterR$* $| $#$*		$#$2
184838032SpeterR$* $| $*		$@ $>"Basic_check_mail" $1
184938032Speter
185038032SpeterSBasic_check_mail
185138032Speter# check for deferred delivery mode
185298121SgshapiroR$*			$: < $&{deliveryMode} > $1
185338032SpeterR< d > $*		$@ deferred
185438032SpeterR< $* > $*		$: $2
185538032Speter
185664562Sgshapiro# authenticated?
185764562Sgshapirodnl done first: we can require authentication for every mail transaction
185864562Sgshapirodnl workspace: address as given by MAIL FROM: (sender)
185964562SgshapiroR$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
186064562SgshapiroR$* $| $#$+		$#$2
186164562Sgshapirodnl undo damage: remove result of tls_client call
186264562SgshapiroR$* $| $*		$: $1
186338032Speter
186464562Sgshapirodnl workspace: address as given by MAIL FROM:
186564562SgshapiroR<>			$@ <OK>			we MUST accept <> (RFC 1123)
186638032Speterifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
186764562Sgshapirodnl do some additional checks
186864562Sgshapirodnl no user@host
186964562Sgshapirodnl no user@localhost (if nonlocal sender)
187064562Sgshapirodnl this is a pretty simple canonification, it will not catch every case
187164562Sgshapirodnl just make sure the address has <> around it (which is required by
187264562Sgshapirodnl the RFC anyway, maybe we should complain if they are missing...)
187364562Sgshapirodnl dirty trick: if it is user@host, just add a dot: user@host. this will
187464562Sgshapirodnl not be modified by host lookups.
187564562SgshapiroR$+			$: <?> $1
187664562SgshapiroR<?><$+>		$: <@> <$1>
187764562SgshapiroR<?>$+			$: <@> <$1>
187864562Sgshapirodnl workspace: <@> <address>
187964562Sgshapirodnl prepend daemon_flags
188064562SgshapiroR$*			$: $&{daemon_flags} $| $1
188164562Sgshapirodnl workspace: ${daemon_flags} $| <@> <address>
188264562Sgshapirodnl do not allow these at all or only from local systems?
188364562SgshapiroR$* f $* $| <@> < $* @ $- >	$: < ? $&{client_name} > < $3 @ $4 >
188464562Sgshapirodnl accept unqualified sender: change mark to avoid test
188564562SgshapiroR$* u $* $| <@> < $* >	$: <?> < $3 >
188664562Sgshapirodnl workspace: ${daemon_flags} $| <@> <address>
188764562Sgshapirodnl        or:                    <? ${client_name} > <address>
188864562Sgshapirodnl        or:                    <?> <address>
188964562Sgshapirodnl remove daemon_flags
189064562SgshapiroR$* $| $*		$: $2
189138032Speter# handle case of @localhost on address
189264562SgshapiroR<@> < $* @ localhost >	$: < ? $&{client_name} > < $1 @ localhost >
189364562SgshapiroR<@> < $* @ [127.0.0.1] >
189464562Sgshapiro			$: < ? $&{client_name} > < $1 @ [127.0.0.1] >
1895285303SgshapiroR<@> < $* @ [IPv6:0:0:0:0:0:0:0:1] >
1896285303Sgshapiro			$: < ? $&{client_name} > < $1 @ [IPv6:0:0:0:0:0:0:0:1] >
1897285303SgshapiroR<@> < $* @ [IPv6:::1] >
1898285303Sgshapiro			$: < ? $&{client_name} > < $1 @ [IPv6:::1] >
189964562SgshapiroR<@> < $* @ localhost.$m >
190064562Sgshapiro			$: < ? $&{client_name} > < $1 @ localhost.$m >
190138032Speterifdef(`_NO_UUCP_', `dnl',
190264562Sgshapiro`R<@> < $* @ localhost.UUCP >
190364562Sgshapiro			$: < ? $&{client_name} > < $1 @ localhost.UUCP >')
190464562Sgshapirodnl workspace: < ? $&{client_name} > <user@localhost|host>
190564562Sgshapirodnl	or:    <@> <address>
190664562Sgshapirodnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
190764562SgshapiroR<@> $*			$: $1			no localhost as domain
190864562Sgshapirodnl workspace: < ? $&{client_name} > <user@localhost|host>
190964562Sgshapirodnl	or:    <address>
191064562Sgshapirodnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
191164562SgshapiroR<? $=w> $*		$: $2			local client: ok
191290792SgshapiroR<? $+> <$+>		$#error $@ 5.5.4 $: "_CODE553 Real domain name required for sender address"
191364562Sgshapirodnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags})
191464562SgshapiroR<?> $*			$: $1')
191564562Sgshapirodnl workspace: address (or <address>)
191664562SgshapiroR$*			$: <?> $>CanonAddr $1		canonify sender address and mark it
191764562Sgshapirodnl workspace: <?> CanonicalAddress (i.e. address in canonical form localpart<@host>)
191864562Sgshapirodnl there is nothing behind the <@host> so no trailing $* needed
191964562SgshapiroR<?> $* < @ $+ . >	<?> $1 < @ $2 >			strip trailing dots
192064562Sgshapiro# handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
1921102528SgshapiroR<?> $* < @ $* $=P >	$: <_RES_OK_> $1 < @ $2 $3 >
192264562Sgshapirodnl workspace <mark> CanonicalAddress	where mark is ? or OK
192398121Sgshapirodnl A sender address with my local host name ($j) is safe
1924102528SgshapiroR<?> $* < @ $j >	$: <_RES_OK_> $1 < @ $j >
192564562Sgshapiroifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',
192690792Sgshapiro`R<?> $* < @ $+ >	$: <_RES_OK_> $1 < @ $2 >		... unresolvable OK',
192764562Sgshapiro`R<?> $* < @ $+ >	$: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 >
192864562SgshapiroR<? $* <$->> $* < @ $+ >
192964562Sgshapiro			$: <$2> $3 < @ $4 >')
193090792Sgshapirodnl workspace <mark> CanonicalAddress	where mark is ?, _RES_OK_, PERM, TEMP
193164562Sgshapirodnl mark is ? iff the address is user (wo @domain)
193238032Speter
193364562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
193464562Sgshapiro# check sender address: user@address, user@, address
193564562Sgshapirodnl should we remove +ext from user?
193690792Sgshapirodnl workspace: <mark> CanonicalAddress where mark is: ?, _RES_OK_, PERM, TEMP
193790792SgshapiroR<$+> $+ < @ $* >	$: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <D:$3>
193864562SgshapiroR<$+> $+		$: @<$1> <$2> $| <U:$2@>
193964562Sgshapirodnl workspace: @<mark> <CanonicalAddress> $| <@type:address> ....
194064562Sgshapirodnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
194164562Sgshapirodnl will only return user<@domain when "reversing" the args
194290792SgshapiroR@ <$+> <$*> $| <$+>	$: <@> <$1> <$2> $| $>SearchList <+ From> $| <$3> <>
194364562Sgshapirodnl workspace: <@><mark> <CanonicalAddress> $| <result>
194464562SgshapiroR<@> <$+> <$*> $| <$*>	$: <$3> <$1> <$2>		reverse result
194564562Sgshapirodnl workspace: <result> <mark> <CanonicalAddress>
194638032Speter# retransform for further use
194764562Sgshapirodnl required form:
194864562Sgshapirodnl <ResultOfLookup|mark> CanonicalAddress
194964562SgshapiroR<?> <$+> <$*>		$: <$1> $2	no match
195064562SgshapiroR<$+> <$+> <$*>		$: <$1> $3	relevant result, keep it', `dnl')
195164562Sgshapirodnl workspace <ResultOfLookup|mark> CanonicalAddress
195264562Sgshapirodnl mark is ? iff the address is user (wo @domain)
195338032Speter
195438032Speterifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
195538032Speter# handle case of no @domain on address
195664562Sgshapirodnl prepend daemon_flags
195764562SgshapiroR<?> $*			$: $&{daemon_flags} $| <?> $1
195864562Sgshapirodnl accept unqualified sender: change mark to avoid test
195990792SgshapiroR$* u $* $| <?> $*	$: <_RES_OK_> $3
196064562Sgshapirodnl remove daemon_flags
196164562SgshapiroR$* $| $*		$: $2
1962110560SgshapiroR<?> $*			$: < ? $&{client_addr} > $1
1963102528SgshapiroR<?> $*			$@ <_RES_OK_>			...local unqualed ok
196490792SgshapiroR<? $+> $*		$#error $@ 5.5.4 $: "_CODE553 Domain name required for sender address " $&f
196538032Speter							...remote is not')
196638032Speter# check results
196764562SgshapiroR<?> $*			$: @ $1		mark address: nothing known about it
1968168515SgshapiroR<$={ResOk}> $*		$: @ $2		domain ok
196964562SgshapiroR<TEMP> $*		$#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
197090792SgshapiroR<PERM> $*		$#error $@ 5.1.8 $: "_CODE553 Domain of sender address " $&f " does not exist"
197164562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
197290792SgshapiroR<$={Accept}> $*	$# $1		accept from access map
197338032SpeterR<DISCARD> $*		$#discard $: discard
1974132943SgshapiroR<QUARANTINE:$+> $*	$#error $@ quarantine $: $1
1975132943SgshapiroR<REJECT> $*		$#error ifdef(`confREJECT_MSG', `$: confREJECT_MSG', `$@ 5.7.1 $: "550 Access denied"')
197664562Sgshapirodnl error tag
197764562SgshapiroR<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
197864562SgshapiroR<ERROR:$+> $*		$#error $: $1
197990792Sgshapiroifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
198064562Sgshapirodnl generic error from access map
198164562SgshapiroR<$+> $*		$#error $: $1		error from access db',
198238032Speter`dnl')
1983168515Sgshapirodnl workspace: @ CanonicalAddress (i.e. address in canonical form localpart<@host>)
198438032Speter
1985168515Sgshapiroifdef(`_BADMX_CHK_', `dnl
1986168515SgshapiroR@ $*<@$+>$*		$: $1<@$2>$3 $| $>BadMX $2
1987168515SgshapiroR$* $| $#$*		$#$2
1988168515Sgshapiro
1989168515SgshapiroSBadMX
1990168515Sgshapiro# Look up MX records and ferret away a copy of the original address.
1991168515Sgshapiro# input: domain part of address to check
1992168515SgshapiroR$+				$:<MX><$1><:$(mxlist $1$):><:>
1993168515Sgshapiro# workspace: <MX><domain><: mxlist-result $><:>
1994168515SgshapiroR<MX><$+><:$*<TEMP>:><$*>	$#error $@ 4.1.2 $: "450 MX lookup failure for "$1
1995168515Sgshapiro# workspace: <MX> <original destination> <unchecked mxlist> <checked mxlist>
1996168515Sgshapiro# Recursively run badmx check on each mx.
1997168515SgshapiroR<MX><$*><:$+:$*><:$*>		<MX><$1><:$3><: $4 $(badmx $2 $):>
1998168515Sgshapiro# See if any of them fail.
1999182352SgshapiroR<MX><$*><$*><$*<BADMX>:$*>	$#error $@ 5.1.2 $:"550 Illegal MX record for host "$1
2000168515Sgshapiro# Reverse the mxlists so we can use the same argument order again.
2001168515SgshapiroR<MX><$*><$*><$*>		$:<MX><$1><$3><$2>
2002168515SgshapiroR<MX><$*><:$+:$*><:$*>		<MX><$1><:$3><:$4 $(dnsA $2 $) :>
2003168515Sgshapiro
2004168515Sgshapiro# Reverse the lists so we can use the same argument order again.
2005168515SgshapiroR<MX><$*><$*><$*>		$:<MX><$1><$3><$2>
2006168515SgshapiroR<MX><$*><:$+:$*><:$*>		<MX><$1><:$3><:$4 $(BadMXIP $2 $) :>
2007168515Sgshapiro
2008182352SgshapiroR<MX><$*><$*><$*<BADMXIP>:$*>	$#error $@ 5.1.2 $:"550 Invalid MX record for host "$1',
2009168515Sgshapiro`dnl')
2010168515Sgshapiro
2011168515Sgshapiro
201238032Speter######################################################################
201338032Speter###  check_rcpt -- check SMTP ``RCPT TO:'' command argument
201438032Speter######################################################################
201538032Speter
201638032SpeterSLocal_check_rcpt
201764562SgshapiroScheck`'_U_`'rcpt
201838032SpeterR$*			$: $1 $| $>"Local_check_rcpt" $1
201938032SpeterR$* $| $#$*		$#$2
202038032SpeterR$* $| $*		$@ $>"Basic_check_rcpt" $1
202138032Speter
202238032SpeterSBasic_check_rcpt
202390792Sgshapiro# empty address?
202490792SgshapiroR<>			$#error $@ nouser $: "553 User address required"
202590792SgshapiroR$@			$#error $@ nouser $: "553 User address required"
202638032Speter# check for deferred delivery mode
202798121SgshapiroR$*			$: < $&{deliveryMode} > $1
202838032SpeterR< d > $*		$@ deferred
202938032SpeterR< $* > $*		$: $2
203038032Speter
203164562Sgshapiroifdef(`_REQUIRE_QUAL_RCPT_', `dnl
203290792Sgshapirodnl this code checks for user@host where host is not a FQHN.
203390792Sgshapirodnl it is not activated.
203490792Sgshapirodnl notice: code to check for a recipient without a domain name is
203590792Sgshapirodnl available down below; look for the same macro.
203690792Sgshapirodnl this check is done here because the name might be qualified by the
203790792Sgshapirodnl canonicalization.
203890792Sgshapiro# require fully qualified domain part?
203990792Sgshapirodnl very simple canonification: make sure the address is in < >
204064562SgshapiroR$+			$: <?> $1
204190792SgshapiroR<?> <$+>		$: <@> <$1>
204290792SgshapiroR<?> $+			$: <@> <$1>
204390792SgshapiroR<@> < postmaster >	$: postmaster
2044110560SgshapiroR<@> < $* @ $+ . $+ >	$: < $1 @ $2 . $3 >
204564562Sgshapirodnl prepend daemon_flags
204690792SgshapiroR<@> $*			$: $&{daemon_flags} $| <@> $1
204764562Sgshapirodnl workspace: ${daemon_flags} $| <@> <address>
2048159609Sgshapirodnl _r_equire qual.rcpt: ok
2049120256SgshapiroR$* r $* $| <@> < $+ @ $+ >	$: < $3 @ $4 >
205064562Sgshapirodnl do not allow these at all or only from local systems?
2051120256SgshapiroR$* r $* $| <@> < $* >	$: < ? $&{client_name} > < $3 >
205264562SgshapiroR<?> < $* >		$: <$1>
205364562SgshapiroR<? $=w> < $* >		$: <$1>
205490792SgshapiroR<? $+> <$+>		$#error $@ 5.5.4 $: "553 Fully qualified domain name required"
205564562Sgshapirodnl remove daemon_flags for other cases
205664562SgshapiroR$* $| <@> $*		$: $2', `dnl')
205764562Sgshapiro
205890792Sgshapirodnl ##################################################################
205990792Sgshapirodnl call subroutines for recipient and relay
206090792Sgshapirodnl possible returns from subroutines:
206190792Sgshapirodnl $#TEMP	temporary failure
206290792Sgshapirodnl $#error	permanent failure (or temporary if from access map)
206390792Sgshapirodnl $#other	stop processing
206490792Sgshapirodnl RELAY	RELAYing allowed
206590792Sgshapirodnl other	otherwise
206690792Sgshapiro######################################################################
206790792SgshapiroR$*			$: $1 $| @ $>"Rcpt_ok" $1
206890792Sgshapirodnl temporary failure? remove mark @ and remember
206990792SgshapiroR$* $| @ $#TEMP $+	$: $1 $| T $2
207090792Sgshapirodnl error or ok (stop)
207190792SgshapiroR$* $| @ $#$*		$#$2
207290792Sgshapiroifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
207390792SgshapiroR$* $| @ RELAY		$@ RELAY
207490792Sgshapirodnl something else: call check sender (relay)
207590792SgshapiroR$* $| @ $*		$: O $| $>"Relay_ok" $1
207690792Sgshapirodnl temporary failure: call check sender (relay)
207790792SgshapiroR$* $| T $+		$: T $2 $| $>"Relay_ok" $1
207890792Sgshapirodnl temporary failure? return that
207990792SgshapiroR$* $| $#TEMP $+	$#error $2
208090792Sgshapirodnl error or ok (stop)
208190792SgshapiroR$* $| $#$*		$#$2
208290792SgshapiroR$* $| RELAY		$@ RELAY
208390792Sgshapirodnl something else: return previous temp failure
208490792SgshapiroR T $+ $| $*		$#error $1
208590792Sgshapiro# anything else is bogus
208690792SgshapiroR$*			$#error $@ 5.7.1 $: confRELAY_MSG
208790792Sgshapirodivert(0)
208890792Sgshapiro
208990792Sgshapiro######################################################################
209090792Sgshapiro### Rcpt_ok: is the recipient ok?
209190792Sgshapirodnl input: recipient address (RCPT TO)
209290792Sgshapirodnl output: see explanation at call
209390792Sgshapiro######################################################################
209490792SgshapiroSRcpt_ok
209538032Speterifdef(`_LOOSE_RELAY_CHECK_',`dnl
209642575SpeterR$*			$: $>CanonAddr $1
209738032SpeterR$* < @ $* . >		$1 < @ $2 >			strip trailing dots',
209838032Speter`R$*			$: $>ParseRecipient $1		strip relayable hosts')
209938032Speter
210042575Speterifdef(`_BESTMX_IS_LOCAL_',`dnl
210142575Speterifelse(_BESTMX_IS_LOCAL_, `', `dnl
210242575Speter# unlimited bestmx
210342575SpeterR$* < @ $* > $*			$: $1 < @ $2 @@ $(bestmx $2 $) > $3',
210442575Speter`dnl
210542575Speter# limit bestmx to $=B
210643730SpeterR$* < @ $* $=B > $*		$: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4')
210790792SgshapiroR$* $=O $* < @ $* @@ $=w . > $*	$@ $>"Rcpt_ok" $1 $2 $3
210842575SpeterR$* < @ $* @@ $=w . > $*	$: $1 < @ $3 > $4
210942575SpeterR$* < @ $* @@ $* > $*		$: $1 < @ $2 > $4')
211042575Speter
211138032Speterifdef(`_BLACKLIST_RCPT_',`dnl
211264562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
211338032Speter# blacklist local users or any host from receiving mail
211438032SpeterR$*			$: <?> $1
211564562Sgshapirodnl user is now tagged with @ to be consistent with check_mail
211664562Sgshapirodnl and to distinguish users from hosts (com would be host, com@ would be user)
211790792SgshapiroR<?> $+ < @ $=w >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <D:$2>
211890792SgshapiroR<?> $+ < @ $* >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <D:$2>
211964562SgshapiroR<?> $+			$: <> <$1> $| <U:$1@>
212064562Sgshapirodnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
212164562Sgshapirodnl will only return user<@domain when "reversing" the args
212290792SgshapiroR<> <$*> $| <$+>	$: <@> <$1> $| $>SearchList <+ To> $| <$2> <>
212364562SgshapiroR<@> <$*> $| <$*>	$: <$2> <$1>		reverse result
212464562SgshapiroR<?> <$*>		$: @ $1		mark address as no match
212590792Sgshapirodnl we may have to filter here because otherwise some RHSs
212690792Sgshapirodnl would be interpreted as generic error messages...
212790792Sgshapirodnl error messages should be "tagged" by prefixing them with error: !
212890792Sgshapirodnl that would make a lot of things easier.
212964562SgshapiroR<$={Accept}> <$*>	$: @ $2		mark address as no match
213090792Sgshapiroifdef(`_ACCESS_SKIP_', `dnl
213190792SgshapiroR<SKIP> <$*>		$: @ $1		mark address as no match', `dnl')
213290792Sgshapiroifdef(`_DELAY_COMPAT_8_10_',`dnl
213390792Sgshapirodnl compatility with 8.11/8.10:
213464562Sgshapirodnl we have to filter these because otherwise they would be interpreted
213564562Sgshapirodnl as generic error message...
213664562Sgshapirodnl error messages should be "tagged" by prefixing them with error: !
213764562Sgshapirodnl that would make a lot of things easier.
213864562Sgshapirodnl maybe we should stop checks already here (if SPAM_xyx)?
213964562SgshapiroR<$={SpamTag}> <$*>	$: @ $2		mark address as no match')
214090792SgshapiroR<REJECT> $*		$#error $@ 5.2.1 $: confRCPTREJ_MSG
214164562SgshapiroR<DISCARD> $*		$#discard $: discard
2142132943SgshapiroR<QUARANTINE:$+> $*	$#error $@ quarantine $: $1
214364562Sgshapirodnl error tag
214464562SgshapiroR<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
214564562SgshapiroR<ERROR:$+> $*		$#error $: $1
214690792Sgshapiroifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
214764562Sgshapirodnl generic error from access map
214864562SgshapiroR<$+> $*		$#error $: $1		error from access db
214964562SgshapiroR@ $*			$1		remove mark', `dnl')', `dnl')
215038032Speter
215190792Sgshapiroifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
215290792Sgshapiro# authenticated via TLS?
215390792SgshapiroR$*			$: $1 $| $>RelayTLS	client authenticated?
215490792SgshapiroR$* $| $# $+		$# $2			error/ok?
215590792SgshapiroR$* $| $*		$: $1			no
215664562Sgshapiro
215790792SgshapiroR$*			$: $1 $| $>"Local_Relay_Auth" $&{auth_type}
215890792Sgshapirodnl workspace: localpart<@domain> $| result of Local_Relay_Auth
215990792SgshapiroR$* $| $# $*		$# $2
216090792Sgshapirodnl if Local_Relay_Auth returns NO then do not check $={TrustAuthMech}
216190792SgshapiroR$* $| NO		$: $1
216290792SgshapiroR$* $| $*		$: $1 $| $&{auth_type}
216390792Sgshapirodnl workspace: localpart<@domain> [ $| ${auth_type} ]
216464562Sgshapirodnl empty ${auth_type}?
216564562SgshapiroR$* $|			$: $1
216664562Sgshapirodnl mechanism ${auth_type} accepted?
216764562Sgshapirodnl use $# to override further tests (delay_checks): see check_rcpt below
216890792SgshapiroR$* $| $={TrustAuthMech}	$# RELAY
216990792Sgshapirodnl remove ${auth_type}
217064562SgshapiroR$* $| $*		$: $1
217171345Sgshapirodnl workspace: localpart<@domain> | localpart
217264562Sgshapiroifelse(defn(`_NO_UUCP_'), `r',
217371345Sgshapiro`R$* ! $* < @ $* >	$: <REMOTE> $2 < @ BANG_PATH >
217471345SgshapiroR$* ! $* 		$: <REMOTE> $2 < @ BANG_PATH >', `dnl')
2175285303Sgshapiroifelse(defn(`_NO_PERCENTHACK_'), `r',
2176285303Sgshapiro`R$* % $* < @ $* >	$: <REMOTE> $1 < @ PERCENT_HACK >
2177285303SgshapiroR$* % $* 		$: <REMOTE> $1 < @ PERCENT_HACK >', `dnl')
217838032Speter# anything terminating locally is ok
217938032Speterifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
218090792SgshapiroR$+ < @ $* $=m >	$@ RELAY', `dnl')
218190792SgshapiroR$+ < @ $=w >		$@ RELAY
218238032Speterifdef(`_RELAY_HOSTS_ONLY_',
218390792Sgshapiro`R$+ < @ $=R >		$@ RELAY
218464562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
2185203004Sgshapiroifdef(`_RELAY_FULL_ADDR_', `dnl
2186203004SgshapiroR$+ < @ $+ >		$: <$(access To:$1@$2 $: ? $)> <$1 < @ $2 >>
2187203004SgshapiroR<?> <$+ < @ $+ >>	$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>',`
2188203004SgshapiroR$+ < @ $+ >		$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>')
218964562Sgshapirodnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
219064562SgshapiroR<?> <$+ < @ $+ >>	$: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
219190792Sgshapiro`R$+ < @ $* $=R >	$@ RELAY
219264562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
2193132943Sgshapiroifdef(`_RELAY_FULL_ADDR_', `dnl
2194132943SgshapiroR$+ < @ $+ >		$: $1 < @ $2 > $| $>SearchList <+ To> $| <F:$1@$2> <D:$2> <F:$1@> <>
2195132943SgshapiroR$+ < @ $+ > $| <$*>	$: <$3> <$1 <@ $2>>
2196132943SgshapiroR$+ < @ $+ > $| $*	$: <$3> <$1 <@ $2>>',
2197132943Sgshapiro`R$+ < @ $+ >		$: $>D <$2> <?> <+ To> <$1 < @ $2 >>')')')
219864562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
219964562Sgshapirodnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
220090792SgshapiroR<RELAY> $*		$@ RELAY
220190792Sgshapiroifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
220238032SpeterR<$*> <$*>		$: $2',`dnl')
220338032Speter
220464562Sgshapiro
220538032Speterifdef(`_RELAY_MX_SERVED_', `dnl
220638032Speter# allow relaying for hosts which we MX serve
220764562SgshapiroR$+ < @ $+ >		$: < : $(mxserved $2 $) : > $1 < @ $2 >
220864562Sgshapirodnl this must not necessarily happen if the client is checked first...
2209132943SgshapiroR< : $* <TEMP> : > $*	$#TEMP $@ 4.4.0 $: "450 Can not check MX records for recipient host " $1
221090792SgshapiroR<$* : $=w . : $*> $*	$@ RELAY
221142575SpeterR< : $* : > $*		$: $2',
221238032Speter`dnl')
221338032Speter
221438032Speter# check for local user (i.e. unqualified address)
221538032SpeterR$*			$: <?> $1
221642575SpeterR<?> $* < @ $+ >	$: <REMOTE> $1 < @ $2 >
221738032Speter# local user is ok
221864562Sgshapirodnl is it really? the standard requires user@domain, not just user
221964562Sgshapirodnl but we should accept it anyway (maybe making it an option:
222064562Sgshapirodnl RequireFQDN ?)
222164562Sgshapirodnl postmaster must be accepted without domain (DRUMS)
222264562Sgshapiroifdef(`_REQUIRE_QUAL_RCPT_', `dnl
222390792SgshapiroR<?> postmaster		$@ OK
222464562Sgshapiro# require qualified recipient?
222564562Sgshapirodnl prepend daemon_flags
222664562SgshapiroR<?> $+			$: $&{daemon_flags} $| <?> $1
222764562Sgshapirodnl workspace: ${daemon_flags} $| <?> localpart
222864562Sgshapirodnl do not allow these at all or only from local systems?
222964562Sgshapirodnl r flag? add client_name
223064562SgshapiroR$* r $* $| <?> $+	$: < ? $&{client_name} > <?> $3
223164562Sgshapirodnl no r flag: relay to local user (only local part)
223264562Sgshapiro# no qualified recipient required
223390792SgshapiroR$* $| <?> $+		$@ RELAY
223464562Sgshapirodnl client_name is empty
223590792SgshapiroR<?> <?> $+		$@ RELAY
223664562Sgshapirodnl client_name is local
223790792SgshapiroR<? $=w> <?> $+		$@ RELAY
223864562Sgshapirodnl client_name is not local
223964562SgshapiroR<? $+> $+		$#error $@ 5.5.4 $: "553 Domain name required"', `dnl
224064562Sgshapirodnl no qualified recipient required
224190792SgshapiroR<?> $+			$@ RELAY')
224264562Sgshapirodnl it is a remote user: remove mark and then check client
224338032SpeterR<$+> $*		$: $2
224464562Sgshapirodnl currently the recipient address is not used below
224538032Speter
224690792Sgshapiro######################################################################
224790792Sgshapiro### Relay_ok: is the relay/sender ok?
224890792Sgshapirodnl input: ignored
224990792Sgshapirodnl output: see explanation at call
225090792Sgshapiro######################################################################
225190792SgshapiroSRelay_ok
225238032Speter# anything originating locally is ok
225364562Sgshapiro# check IP address
225464562SgshapiroR$*			$: $&{client_addr}
225590792SgshapiroR$@			$@ RELAY		originated locally
225690792SgshapiroR0			$@ RELAY		originated locally
2257110560SgshapiroR127.0.0.1		$@ RELAY		originated locally
2258285303SgshapiroRIPv6:0:0:0:0:0:0:0:1	$@ RELAY		originated locally
2259285303Sgshapirodnl if compiled with IPV6_FULL=0
2260110560SgshapiroRIPv6:::1		$@ RELAY		originated locally
226190792SgshapiroR$=R $*			$@ RELAY		relayable IP address
226264562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
226390792SgshapiroR$*			$: $>A <$1> <?> <+ Connect> <$1>
226490792SgshapiroR<RELAY> $* 		$@ RELAY		relayable IP address
2265102528Sgshapiroifdef(`_FFR_REJECT_IP_IN_CHECK_RCPT_',`dnl
2266102528Sgshapirodnl this will cause rejections in cases like:
2267102528Sgshapirodnl Connect:My.Host.Domain	RELAY
2268102528Sgshapirodnl Connect:My.Net		REJECT
2269102528Sgshapirodnl since in check_relay client_name is checked before client_addr
2270102528SgshapiroR<REJECT> $* 		$@ REJECT		rejected IP address')
227190792Sgshapiroifdef(`_ATMPF_', `R<_ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
227264562SgshapiroR<$*> <$*>		$: $2', `dnl')
227364562SgshapiroR$*			$: [ $1 ]		put brackets around it...
227490792SgshapiroR$=w			$@ RELAY		... and see if it is local
227564562Sgshapiro
227664562Sgshapiroifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
227764562Sgshapiroifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
227864562Sgshapiroifdef(`_RELAY_MAIL_FROM_', `dnl
227964562Sgshapirodnl input: {client_addr} or something "broken"
228064562Sgshapirodnl just throw the input away; we do not need it.
228164562Sgshapiro# check whether FROM is allowed to use system as relay
228264562SgshapiroR$*			$: <?> $>CanonAddr $&f
228390792SgshapiroR<?> $+ < @ $+ . >	<?> $1 < @ $2 >		remove trailing dot
228464562Sgshapiroifdef(`_RELAY_LOCAL_FROM_', `dnl
228564562Sgshapiro# check whether local FROM is ok
228690792SgshapiroR<?> $+ < @ $=w >	$@ RELAY		FROM local', `dnl')
228764562Sgshapiroifdef(`_RELAY_DB_FROM_', `dnl
228894334SgshapiroR<?> $+ < @ $+ >	$: <@> $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', ifdef(`_RELAY_HOSTS_ONLY_', `<E:$2>', `<D:$2>')) <>
228990792SgshapiroR<@> <RELAY>		$@ RELAY		RELAY FROM sender ok
229090792Sgshapiroifdef(`_ATMPF_', `R<@> <_ATMPF_>		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
229190792Sgshapiro', `dnl
229290792Sgshapiroifdef(`_RELAY_DB_FROM_DOMAIN_',
229390792Sgshapiro`errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_
229464562Sgshapiro')',
229564562Sgshapiro`dnl')
229664562Sgshapirodnl')', `dnl')
229790792Sgshapirodnl notice: the rulesets above do not leave a unique workspace behind.
229890792Sgshapirodnl it does not matter in this case because the following rule ignores
229990792Sgshapirodnl the input. otherwise these rules must "clean up" the workspace.
230064562Sgshapiro
230164562Sgshapiro# check client name: first: did it resolve?
230264562Sgshapirodnl input: ignored
230364562SgshapiroR$*			$: < $&{client_resolve} >
2304132943SgshapiroR<TEMP>			$#TEMP $@ 4.4.0 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr}
230564562SgshapiroR<FORGED>		$#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name}
230664562SgshapiroR<FAIL>			$#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name}
230764562Sgshapirodnl ${client_resolve} should be OK, so go ahead
230890792SgshapiroR$*			$: <@> $&{client_name}
230990792Sgshapirodnl should not be necessary since it has been done for client_addr already
2310110560Sgshapirodnl this rule actually may cause a problem if {client_name} resolves to ""
2311110560Sgshapirodnl however, this should not happen since the forward lookup should fail
2312110560Sgshapirodnl and {client_resolve} should be TEMP or FAIL.
2313110560Sgshapirodnl nevertheless, removing the rule doesn't hurt.
2314110560Sgshapirodnl R<@>			$@ RELAY
231590792Sgshapirodnl workspace: <@> ${client_name} (not empty)
231638032Speter# pass to name server to make hostname canonical
231790792SgshapiroR<@> $* $=P 		$:<?>  $1 $2
231890792SgshapiroR<@> $+			$:<?>  $[ $1 $]
231990792Sgshapirodnl workspace: <?> ${client_name} (canonified)
232064562SgshapiroR$* .			$1			strip trailing dots
232138032Speterifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
232290792SgshapiroR<?> $* $=m		$@ RELAY', `dnl')
232390792SgshapiroR<?> $=w		$@ RELAY
232438032Speterifdef(`_RELAY_HOSTS_ONLY_',
232590792Sgshapiro`R<?> $=R		$@ RELAY
232664562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
232764562SgshapiroR<?> $*			$: <$(access Connect:$1 $: ? $)> <$1>
232864562SgshapiroR<?> <$*>		$: <$(access $1 $: ? $)> <$1>',`dnl')',
232990792Sgshapiro`R<?> $* $=R			$@ RELAY
233064562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
233190792SgshapiroR<?> $*			$: $>D <$1> <?> <+ Connect> <$1>',`dnl')')
233264562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
233390792SgshapiroR<RELAY> $*		$@ RELAY
233490792Sgshapiroifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
233538032SpeterR<$*> <$*>		$: $2',`dnl')
233690792Sgshapirodnl end of _PROMISCUOUS_RELAY_
233764562Sgshapirodivert(0)
233864562Sgshapiroifdef(`_DELAY_CHECKS_',`dnl
233964562Sgshapiro# turn a canonical address in the form user<@domain>
234064562Sgshapiro# qualify unqual. addresses with $j
234164562Sgshapirodnl it might have been only user (without <@domain>)
234264562SgshapiroSFullAddr
234364562SgshapiroR$* <@ $+ . >		$1 <@ $2 >
234464562SgshapiroR$* <@ $* >		$@ $1 <@ $2 >
234564562SgshapiroR$+			$@ $1 <@ $j >
234638032Speter
2347120256SgshapiroSDelay_TLS_Clt
2348110560Sgshapiro# authenticated?
2349110560Sgshapirodnl code repeated here from Basic_check_mail
2350110560Sgshapirodnl only called from check_rcpt in delay mode if checkrcpt returns $#
2351110560SgshapiroR$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
2352110560SgshapiroR$* $| $#$+		$#$2
2353110560Sgshapirodnl return result from checkrcpt
2354120256SgshapiroR$* $| $*		$# $1
2355110560SgshapiroR$*			$# $1
2356110560Sgshapiro
2357120256SgshapiroSDelay_TLS_Clt2
2358110560Sgshapiro# authenticated?
2359110560Sgshapirodnl code repeated here from Basic_check_mail
2360110560Sgshapirodnl only called from check_rcpt in delay mode if stopping due to Friend/Hater
2361110560SgshapiroR$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
2362110560SgshapiroR$* $| $#$+		$#$2
2363110560Sgshapirodnl return result from friend/hater check
2364120256SgshapiroR$* $| $*		$@ $1
2365110560SgshapiroR$*			$@ $1
2366110560Sgshapiro
236764562Sgshapiro# call all necessary rulesets
236864562SgshapiroScheck_rcpt
236964562Sgshapirodnl this test should be in the Basic_check_rcpt ruleset
237064562Sgshapirodnl which is the correct DSN code?
237164562Sgshapiro# R$@			$#error $@ 5.1.3 $: "553 Recipient address required"
2372110560Sgshapiro
237364562SgshapiroR$+			$: $1 $| $>checkrcpt $1
237464562Sgshapirodnl now we can simply stop checks by returning "$# xyz" instead of just "ok"
2375110560Sgshapirodnl on error (or discard) stop now
2376110560SgshapiroR$+ $| $#error $*	$#error $2
2377110560SgshapiroR$+ $| $#discard $*	$#discard $2
2378110560Sgshapirodnl otherwise call tls_client; see above
2379120256SgshapiroR$+ $| $#$*		$@ $>"Delay_TLS_Clt" $2
238064562SgshapiroR$+ $| $*		$: <?> $>FullAddr $>CanonAddr $1
238164562Sgshapiroifdef(`_SPAM_FH_',
238264562Sgshapiro`dnl lookup user@ and user@address
238364562Sgshapiroifdef(`_ACCESS_TABLE_', `',
238464562Sgshapiro`errprint(`*** ERROR: FEATURE(`delay_checks', `argument') requires FEATURE(`access_db')
238564562Sgshapiro')')dnl
238664562Sgshapirodnl one of the next two rules is supposed to match
238764562Sgshapirodnl this code has been copied from BLACKLIST... etc
238864562Sgshapirodnl and simplified by omitting some < >.
238990792SgshapiroR<?> $+ < @ $=w >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 > <U: $1@>
239090792SgshapiroR<?> $+ < @ $* >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 >
239164562Sgshapirodnl R<?>		$@ something_is_very_wrong_here
239290792Sgshapiro# lookup the addresses only with Spam tag
239390792SgshapiroR<> $* $| <$+>		$: <@> $1 $| $>SearchList <! Spam> $| <$2> <>
239464562SgshapiroR<@> $* $| $*		$: $2 $1		reverse result
239564562Sgshapirodnl', `dnl')
239664562Sgshapiroifdef(`_SPAM_FRIEND_',
239764562Sgshapiro`# is the recipient a spam friend?
239864562Sgshapiroifdef(`_SPAM_HATER_',
2399110560Sgshapiro	`errprint(`*** ERROR: define either Hater or Friend -- not both.
240064562Sgshapiro')', `dnl')
2401120256SgshapiroR<FRIEND> $+		$@ $>"Delay_TLS_Clt2" SPAMFRIEND
240264562SgshapiroR<$*> $+		$: $2',
240364562Sgshapiro`dnl')
240464562Sgshapiroifdef(`_SPAM_HATER_',
240564562Sgshapiro`# is the recipient no spam hater?
240690792SgshapiroR<HATER> $+		$: $1			spam hater: continue checks
2407120256SgshapiroR<$*> $+		$@ $>"Delay_TLS_Clt2" NOSPAMHATER	everyone else: stop
240864562Sgshapirodnl',`dnl')
2409168515Sgshapiro
241064562Sgshapirodnl run further checks: check_mail
241164562Sgshapirodnl should we "clean up" $&f?
241290792Sgshapiroifdef(`_FFR_MAIL_MACRO',
241390792Sgshapiro`R$*			$: $1 $| $>checkmail $&{mail_from}',
241490792Sgshapiro`R$*			$: $1 $| $>checkmail <$&f>')
241594334Sgshapirodnl recipient (canonical format) $| result of checkmail
241664562SgshapiroR$* $| $#$*		$#$2
241764562Sgshapirodnl run further checks: check_relay
241894334SgshapiroR$* $| $*		$: $1 $| $>checkrelay $&{client_name} $| $&{client_addr}
241964562SgshapiroR$* $| $#$*		$#$2
242038032SpeterR$* $| $*		$: $1
242138032Speter', `dnl')
242290792Sgshapiro
2423168515Sgshapiroifdef(`_BLOCK_BAD_HELO_', `dnl
2424168515SgshapiroR$*			$: $1 $| <$&{auth_authen}>	Get auth info
2425168515Sgshapirodnl Bypass the test for users who have authenticated.
2426168515SgshapiroR$* $| <$+>		$: $1				skip if auth
2427168515SgshapiroR$* $| <$*>		$: $1 $| <$&{client_addr}> [$&s]	Get connection info
2428168515Sgshapirodnl Bypass for local clients -- IP address starts with $=R
2429168515SgshapiroR$* $| <$=R $*> [$*]	$: $1				skip if local client
2430168515Sgshapirodnl Bypass a "sendmail -bs" session, which use 0 for client ip address
2431168515SgshapiroR$* $| <0> [$*]		$: $1				skip if sendmail -bs
2432168515Sgshapirodnl Reject our IP - assumes "[ip]" is in class $=w
2433168515SgshapiroR$* $| <$*> $=w		$#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2434168515Sgshapirodnl Reject our hostname
2435168515SgshapiroR$* $| <$*> [$=w]	$#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2436168515Sgshapirodnl Pass anything else with a "." in the domain parameter
2437168515SgshapiroR$* $| <$*> [$+.$+]	$: $1				qualified domain ok
2438261363Sgshapirodnl Pass IPv6: address literals
2439261363SgshapiroR$* $| <$*> [IPv6:$+]	$: $1				qualified domain ok
2440168515Sgshapirodnl Reject if there was no "." or only an initial or final "."
2441168515SgshapiroR$* $| <$*> [$*]	$#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2442168515Sgshapirodnl Clean up the workspace
2443168515SgshapiroR$* $| $*		$: $1
2444168515Sgshapiro', `dnl')
2445168515Sgshapiro
244690792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
244764562Sgshapiro######################################################################
244890792Sgshapiro###  F: LookUpFull -- search for an entry in access database
244990792Sgshapiro###
245090792Sgshapiro###	lookup of full key (which should be an address) and
245190792Sgshapiro###	variations if +detail exists: +* and without +detail
245290792Sgshapiro###
245390792Sgshapiro###	Parameters:
245490792Sgshapiro###		<$1> -- key
245590792Sgshapiro###		<$2> -- default (what to return if not found in db)
245690792Sgshapirodnl			must not be empty
245790792Sgshapiro###		<$3> -- mark (must be <(!|+) single-token>)
245890792Sgshapiro###			! does lookup only with tag
245990792Sgshapiro###			+ does lookup with and without tag
246090792Sgshapiro###		<$4> -- passthru (additional data passed unchanged through)
246190792Sgshapirodnl returns:		<default> <passthru>
246290792Sgshapirodnl 			<result> <passthru>
246390792Sgshapiro######################################################################
246490792Sgshapiro
246590792SgshapiroSF
246690792Sgshapirodnl workspace: <key> <def> <o tag> <thru>
246790792Sgshapirodnl full lookup
246890792Sgshapirodnl    2    3  4    5
246990792SgshapiroR<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
247090792Sgshapirodnl no match, try without tag
247190792Sgshapirodnl   1    2      3    4
247290792SgshapiroR<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
247390792Sgshapirodnl no match, +detail: try +*
247490792Sgshapirodnl   1    2    3    4    5  6    7
247590792SgshapiroR<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
247690792Sgshapiro			$: <$(access $6`'_TAG_DELIM_`'$1+*@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
247790792Sgshapirodnl no match, +detail: try +* without tag
247890792Sgshapirodnl   1    2    3    4      5    6
247990792SgshapiroR<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
248090792Sgshapiro			$: <$(access $1+*@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
248190792Sgshapirodnl no match, +detail: try without +detail
248290792Sgshapirodnl   1    2    3    4    5  6    7
248390792SgshapiroR<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
248490792Sgshapiro			$: <$(access $6`'_TAG_DELIM_`'$1@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
248590792Sgshapirodnl no match, +detail: try without +detail and without tag
248690792Sgshapirodnl   1    2    3    4      5    6
248790792SgshapiroR<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
248890792Sgshapiro			$: <$(access $1@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
248990792Sgshapirodnl no match, return <default> <passthru>
249090792Sgshapirodnl   1    2    3  4    5
249190792SgshapiroR<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
249290792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
249390792Sgshapirodnl            2    3  4    5
249490792SgshapiroR<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
249590792Sgshapirodnl match, return <match> <passthru>
249690792Sgshapirodnl    2    3  4    5
249790792SgshapiroR<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
249890792Sgshapiro
249990792Sgshapiro######################################################################
250090792Sgshapiro###  E: LookUpExact -- search for an entry in access database
250190792Sgshapiro###
250290792Sgshapiro###	Parameters:
250390792Sgshapiro###		<$1> -- key
250490792Sgshapiro###		<$2> -- default (what to return if not found in db)
250590792Sgshapirodnl			must not be empty
250690792Sgshapiro###		<$3> -- mark (must be <(!|+) single-token>)
250790792Sgshapiro###			! does lookup only with tag
250890792Sgshapiro###			+ does lookup with and without tag
250990792Sgshapiro###		<$4> -- passthru (additional data passed unchanged through)
251090792Sgshapirodnl returns:		<default> <passthru>
251190792Sgshapirodnl 			<result> <passthru>
251290792Sgshapiro######################################################################
251390792Sgshapiro
251490792SgshapiroSE
251590792Sgshapirodnl    2    3  4    5
251690792SgshapiroR<$*> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
251790792Sgshapirodnl no match, try without tag
251890792Sgshapirodnl   1    2      3    4
251990792SgshapiroR<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
252090792Sgshapirodnl no match, return default passthru
252190792Sgshapirodnl   1    2    3  4    5
252290792SgshapiroR<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
252390792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
252490792Sgshapirodnl            2    3  4    5
252590792SgshapiroR<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
252690792Sgshapirodnl match, return <match> <passthru>
252790792Sgshapirodnl    2    3  4    5
252890792SgshapiroR<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
252990792Sgshapiro
253090792Sgshapiro######################################################################
253190792Sgshapiro###  U: LookUpUser -- search for an entry in access database
253290792Sgshapiro###
253390792Sgshapiro###	lookup of key (which should be a local part) and
253490792Sgshapiro###	variations if +detail exists: +* and without +detail
253590792Sgshapiro###
253690792Sgshapiro###	Parameters:
253790792Sgshapiro###		<$1> -- key (user@)
253890792Sgshapiro###		<$2> -- default (what to return if not found in db)
253990792Sgshapirodnl			must not be empty
254090792Sgshapiro###		<$3> -- mark (must be <(!|+) single-token>)
254190792Sgshapiro###			! does lookup only with tag
254290792Sgshapiro###			+ does lookup with and without tag
254390792Sgshapiro###		<$4> -- passthru (additional data passed unchanged through)
254490792Sgshapirodnl returns:		<default> <passthru>
254590792Sgshapirodnl 			<result> <passthru>
254690792Sgshapiro######################################################################
254790792Sgshapiro
254890792SgshapiroSU
254990792Sgshapirodnl user lookups are always with trailing @
255090792Sgshapirodnl    2    3  4    5
255190792SgshapiroR<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
255290792Sgshapirodnl no match, try without tag
255390792Sgshapirodnl   1    2      3    4
255490792SgshapiroR<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
255590792Sgshapirodnl do not remove the @ from the lookup:
255690792Sgshapirodnl it is part of the +detail@ which is omitted for the lookup
255790792Sgshapirodnl no match, +detail: try +*
255890792Sgshapirodnl   1    2      3    4  5    6
255990792SgshapiroR<?> <$+ + $* @> <$*> <$- $-> <$*>
256090792Sgshapiro			$: <$(access $5`'_TAG_DELIM_`'$1+*@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
256190792Sgshapirodnl no match, +detail: try +* without tag
256290792Sgshapirodnl   1    2      3      4    5
256390792SgshapiroR<?> <$+ + $* @> <$*> <+ $-> <$*>
256490792Sgshapiro			$: <$(access $1+*@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
256590792Sgshapirodnl no match, +detail: try without +detail
256690792Sgshapirodnl   1    2      3    4  5    6
256790792SgshapiroR<?> <$+ + $* @> <$*> <$- $-> <$*>
256890792Sgshapiro			$: <$(access $5`'_TAG_DELIM_`'$1@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
256990792Sgshapirodnl no match, +detail: try without +detail and without tag
257090792Sgshapirodnl   1    2      3      4    5
257190792SgshapiroR<?> <$+ + $* @> <$*> <+ $-> <$*>
257290792Sgshapiro			$: <$(access $1@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
257390792Sgshapirodnl no match, return <default> <passthru>
257490792Sgshapirodnl   1    2    3  4    5
257590792SgshapiroR<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
257690792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
257790792Sgshapirodnl            2    3  4    5
257890792SgshapiroR<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
257990792Sgshapirodnl match, return <match> <passthru>
258090792Sgshapirodnl    2    3  4    5
258190792SgshapiroR<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
258290792Sgshapiro
258390792Sgshapiro######################################################################
258464562Sgshapiro###  SearchList: search a list of items in the access map
258564562Sgshapiro###	Parameters:
258664562Sgshapiro###		<exact tag> $| <mark:address> <mark:address> ... <>
258764562Sgshapirodnl	maybe we should have a @ (again) in front of the mark to
258864562Sgshapirodnl	avoid errorneous matches (with error messages?)
258964562Sgshapirodnl	if we can make sure that tag is always a single token
259064562Sgshapirodnl	then we can omit the delimiter $|, otherwise we need it
259190792Sgshapirodnl	to avoid errorneous matchs (first rule: D: if there
259264562Sgshapirodnl	is that mark somewhere in the list, it will be taken).
259364562Sgshapirodnl	moreover, we can do some tricks to enforce lookup with
259464562Sgshapirodnl	the tag only, e.g.:
259564562Sgshapiro###	where "exact" is either "+" or "!":
259664562Sgshapiro###	<+ TAG>	lookup with and w/o tag
259764562Sgshapiro###	<! TAG>	lookup with tag
259864562Sgshapirodnl	Warning: + and ! should be in OperatorChars (otherwise there must be
259964562Sgshapirodnl		a blank between them and the tag.
260064562Sgshapiro###	possible values for "mark" are:
260190792Sgshapiro###		D: recursive host lookup (LookUpDomain)
260264562Sgshapirodnl		A: recursive address lookup (LookUpAddress) [not yet required]
260364562Sgshapiro###		E: exact lookup, no modifications
260464562Sgshapiro###		F: full lookup, try user+ext@domain and user@domain
260564562Sgshapiro###		U: user lookup, try user+ext and user (input must have trailing @)
260664562Sgshapiro###	return: <RHS of lookup> or <?> (not found)
260764562Sgshapiro######################################################################
260838032Speter
260964562Sgshapiro# class with valid marks for SearchList
261064562Sgshapirodnl if A is activated: add it
2611132943SgshapiroC{Src}E F D U ifdef(`_FFR_SRCHLIST_A', `A')
261264562SgshapiroSSearchList
261390792Sgshapiro# just call the ruleset with the name of the tag... nice trick...
261490792Sgshapirodnl       2       3    4
2615132943SgshapiroR<$+> $| <$={Src}:$*> <$*>	$: <$1> $| <$4> $| $>$2 <$3> <?> <$1> <>
261690792Sgshapirodnl workspace: <o tag> $| <rest> $| <result of lookup> <>
261790792Sgshapirodnl no match and nothing left: return
261890792SgshapiroR<$+> $| <> $| <?> <>		$@ <?>
261990792Sgshapirodnl no match but something left: continue
262090792SgshapiroR<$+> $| <$+> $| <?> <>		$@ $>SearchList <$1> $| <$2>
262190792Sgshapirodnl match: return
262290792SgshapiroR<$+> $| <$*> $| <$+> <>	$@ <$3>
262364562Sgshapirodnl return result from recursive invocation
262490792SgshapiroR<$+> $| <$+>			$@ <$2>
262590792Sgshapirodnl endif _ACCESS_TABLE_
262690792Sgshapirodivert(0)
262738032Speter
262890792Sgshapiro######################################################################
262990792Sgshapiro###  trust_auth: is user trusted to authenticate as someone else?
263090792Sgshapiro###
263190792Sgshapiro###	Parameters:
263290792Sgshapiro###		$1: AUTH= parameter from MAIL command
263390792Sgshapiro######################################################################
263490792Sgshapiro
263590792Sgshapirodnl empty ruleset definition so it can be called
263690792SgshapiroSLocal_trust_auth
263764562SgshapiroStrust_auth
263864562SgshapiroR$*			$: $&{auth_type} $| $1
263964562Sgshapiro# required by RFC 2554 section 4.
264064562SgshapiroR$@ $| $*		$#error $@ 5.7.1 $: "550 not authenticated"
264164562Sgshapirodnl seems to be useful...
264264562SgshapiroR$* $| $&{auth_authen}		$@ identical
264364562SgshapiroR$* $| <$&{auth_authen}>	$@ identical
264464562Sgshapirodnl call user supplied code
2645120256SgshapiroR$* $| $*		$: $1 $| $>"Local_trust_auth" $2
264664562SgshapiroR$* $| $#$*		$#$2
264764562Sgshapirodnl default: error
264864562SgshapiroR$*			$#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author}
264964562Sgshapiro
265090792Sgshapiro######################################################################
265190792Sgshapiro###  Relay_Auth: allow relaying based on authentication?
265290792Sgshapiro###
265390792Sgshapiro###	Parameters:
265490792Sgshapiro###		$1: ${auth_type}
265590792Sgshapiro######################################################################
265690792SgshapiroSLocal_Relay_Auth
265764562Sgshapiro
265890792Sgshapiro######################################################################
265990792Sgshapiro###  srv_features: which features to offer to a client?
266090792Sgshapiro###	(done in server)
266190792Sgshapiro######################################################################
266290792SgshapiroSsrv_features
266390792Sgshapiroifdef(`_LOCAL_SRV_FEATURES_', `dnl
266490792SgshapiroR$*			$: $1 $| $>"Local_srv_features" $1
266590792SgshapiroR$* $| $#$*		$#$2
266690792SgshapiroR$* $| $*		$: $1', `dnl')
2667132943Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
266890792SgshapiroR$*		$: $>D <$&{client_name}> <?> <! SRV_FEAT_TAG> <>
266990792SgshapiroR<?>$*		$: $>A <$&{client_addr}> <?> <! SRV_FEAT_TAG> <>
267090792SgshapiroR<?>$*		$: <$(access SRV_FEAT_TAG`'_TAG_DELIM_ $: ? $)>
267164562SgshapiroR<?>$*		$@ OK
267290792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
267390792SgshapiroR<$* _ATMPF_>$*	$#temp', `dnl')
2674132943SgshapiroR<$+>$*		$# $1')
267564562Sgshapiro
267690792Sgshapiro######################################################################
267790792Sgshapiro###  try_tls: try to use STARTTLS?
267890792Sgshapiro###	(done in client)
267990792Sgshapiro######################################################################
268064562SgshapiroStry_tls
268190792Sgshapiroifdef(`_LOCAL_TRY_TLS_', `dnl
268290792SgshapiroR$*			$: $1 $| $>"Local_try_tls" $1
268390792SgshapiroR$* $| $#$*		$#$2
268490792SgshapiroR$* $| $*		$: $1', `dnl')
2685132943Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
268690792SgshapiroR$*		$: $>D <$&{server_name}> <?> <! TLS_TRY_TAG> <>
268790792SgshapiroR<?>$*		$: $>A <$&{server_addr}> <?> <! TLS_TRY_TAG> <>
268890792SgshapiroR<?>$*		$: <$(access TLS_TRY_TAG`'_TAG_DELIM_ $: ? $)>
268964562SgshapiroR<?>$*		$@ OK
269090792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
269190792SgshapiroR<$* _ATMPF_>$*	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2692132943SgshapiroR<NO>$*		$#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"')
2693132943Sgshapiro
269490792Sgshapiro######################################################################
269590792Sgshapiro###  tls_rcpt: is connection with server "good" enough?
269690792Sgshapiro###	(done in client, per recipient)
269790792Sgshapirodnl called from deliver() before RCPT command
269890792Sgshapiro###
269990792Sgshapiro###	Parameters:
270090792Sgshapiro###		$1: recipient
270190792Sgshapiro######################################################################
270290792SgshapiroStls_rcpt
270390792Sgshapiroifdef(`_LOCAL_TLS_RCPT_', `dnl
270490792SgshapiroR$*			$: $1 $| $>"Local_tls_rcpt" $1
270590792SgshapiroR$* $| $#$*		$#$2
270690792SgshapiroR$* $| $*		$: $1', `dnl')
2707132943Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
270890792Sgshapirodnl store name of other side
270990792SgshapiroR$*			$: $(macro {TLS_Name} $@ $&{server_name} $) $1
271090792Sgshapirodnl canonify recipient address
271190792SgshapiroR$+			$: <?> $>CanonAddr $1
271290792Sgshapirodnl strip trailing dots
271390792SgshapiroR<?> $+ < @ $+ . >	<?> $1 <@ $2 >
271490792Sgshapirodnl full address?
271590792SgshapiroR<?> $+ < @ $+ >	$: $1 <@ $2 > $| <F:$1@$2> <U:$1@> <D:$2> <E:>
271690792Sgshapirodnl only localpart?
271790792SgshapiroR<?> $+			$: $1 $| <U:$1@> <E:>
271890792Sgshapirodnl look it up
271990792Sgshapirodnl also look up a default value via E:
272090792SgshapiroR$* $| $+	$: $1 $| $>SearchList <! TLS_RCPT_TAG> $| $2 <>
272190792Sgshapirodnl found nothing: stop here
272290792SgshapiroR$* $| <?>	$@ OK
272390792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
272490792SgshapiroR$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
272590792Sgshapirodnl use the generic routine (for now)
272690792SgshapiroR$* $| <$+>	$@ $>"TLS_connection" $&{verify} $| <$2>')
272764562Sgshapiro
272890792Sgshapiro######################################################################
272990792Sgshapiro###  tls_client: is connection with client "good" enough?
273090792Sgshapiro###	(done in server)
273190792Sgshapiro###
273290792Sgshapiro###	Parameters:
273390792Sgshapiro###		${verify} $| (MAIL|STARTTLS)
273490792Sgshapiro######################################################################
273564562Sgshapirodnl MAIL: called from check_mail
273664562Sgshapirodnl STARTTLS: called from smtp() after STARTTLS has been accepted
273764562SgshapiroStls_client
273890792Sgshapiroifdef(`_LOCAL_TLS_CLIENT_', `dnl
2739182352SgshapiroR$*			$: $1 <?> $>"Local_tls_client" $1
2740182352SgshapiroR$* <?> $#$*		$#$2
2741182352SgshapiroR$* <?> $*		$: $1', `dnl')
274264562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
274390792Sgshapirodnl store name of other side
2744203004SgshapiroR$*		$: $(macro {TLS_Name} $@ $&{client_name} $) $1
274564562Sgshapirodnl ignore second arg for now
274664562Sgshapirodnl maybe use it to distinguish permanent/temporary error?
274764562Sgshapirodnl if MAIL: permanent (STARTTLS has not been offered)
274864562Sgshapirodnl if STARTTLS: temporary (offered but maybe failed)
274990792SgshapiroR$* $| $*	$: $1 $| $>D <$&{client_name}> <?> <! TLS_CLT_TAG> <>
275090792SgshapiroR$* $| <?>$*	$: $1 $| $>A <$&{client_addr}> <?> <! TLS_CLT_TAG> <>
275164562Sgshapirodnl do a default lookup: just TLS_CLT_TAG
275264562SgshapiroR$* $| <?>$*	$: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)>
275390792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
275490792SgshapiroR$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
275590792SgshapiroR$*		$@ $>"TLS_connection" $1', `dnl
275690792SgshapiroR$* $| $*	$@ $>"TLS_connection" $1')
275764562Sgshapiro
275890792Sgshapiro######################################################################
275990792Sgshapiro###  tls_server: is connection with server "good" enough?
276090792Sgshapiro###	(done in client)
276190792Sgshapiro###
276290792Sgshapiro###	Parameter:
276390792Sgshapiro###		${verify}
276490792Sgshapiro######################################################################
276564562Sgshapirodnl i.e. has the server been authenticated and is encryption active?
276664562Sgshapirodnl called from deliver() after STARTTLS command
276764562SgshapiroStls_server
276890792Sgshapiroifdef(`_LOCAL_TLS_SERVER_', `dnl
276990792SgshapiroR$*			$: $1 $| $>"Local_tls_server" $1
277090792SgshapiroR$* $| $#$*		$#$2
277190792SgshapiroR$* $| $*		$: $1', `dnl')
277264562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
277390792Sgshapirodnl store name of other side
277490792SgshapiroR$*		$: $(macro {TLS_Name} $@ $&{server_name} $) $1
277590792SgshapiroR$*		$: $1 $| $>D <$&{server_name}> <?> <! TLS_SRV_TAG> <>
277690792SgshapiroR$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! TLS_SRV_TAG> <>
277764562Sgshapirodnl do a default lookup: just TLS_SRV_TAG
277864562SgshapiroR$* $| <?>$*	$: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)>
277990792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
278090792SgshapiroR$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
278190792SgshapiroR$*		$@ $>"TLS_connection" $1', `dnl
278290792SgshapiroR$*		$@ $>"TLS_connection" $1')
278364562Sgshapiro
278490792Sgshapiro######################################################################
278590792Sgshapiro###  TLS_connection: is TLS connection "good" enough?
278690792Sgshapiro###
278790792Sgshapiro###	Parameters:
278864562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
278990792Sgshapiro###		${verify} $| <Requirement> [<>]', `dnl
279090792Sgshapiro###		${verify}')
279190792Sgshapiro###		Requirement: RHS from access map, may be ? for none.
279290792Sgshapirodnl	syntax for Requirement:
279390792Sgshapirodnl	[(PERM|TEMP)+] (VERIFY[:bits]|ENCR:bits) [+extensions]
279490792Sgshapirodnl	extensions: could be a list of further requirements
279590792Sgshapirodnl		for now: CN:string	{cn_subject} == string
279690792Sgshapiro######################################################################
279790792SgshapiroSTLS_connection
279890792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl', `dnl use default error
279990792Sgshapirodnl deal with TLS handshake failures: abort
280090792SgshapiroRSOFTWARE	$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake."
280190792Sgshapirodivert(-1)')
280264562Sgshapirodnl common ruleset for tls_{client|server}
280390792Sgshapirodnl input: ${verify} $| <ResultOfLookup> [<>]
280464562Sgshapirodnl remove optional <>
280564562SgshapiroR$* $| <$*>$*			$: $1 $| <$2>
280690792Sgshapirodnl workspace: ${verify} $| <ResultOfLookup>
280790792Sgshapiro# create the appropriate error codes
280864562Sgshapirodnl permanent or temporary error?
2809132943SgshapiroR$* $| <PERM + $={Tls} $*>	$: $1 $| <503:5.7.0> <$2 $3>
2810132943SgshapiroR$* $| <TEMP + $={Tls} $*>	$: $1 $| <403:4.7.0> <$2 $3>
281164562Sgshapirodnl default case depends on TLS_PERM_ERR
2812132943SgshapiroR$* $| <$={Tls} $*>		$: $1 $| <ifdef(`TLS_PERM_ERR', `503:5.7.0', `403:4.7.0')> <$2 $3>
281390792Sgshapirodnl workspace: ${verify} $| [<SMTP:ESC>] <ResultOfLookup>
281490792Sgshapiro# deal with TLS handshake failures: abort
281564562SgshapiroRSOFTWARE $| <$-:$+> $* 	$#error $@ $2 $: $1 " TLS handshake failed."
281664562Sgshapirodnl no <reply:dns> i.e. not requirements in the access map
281764562Sgshapirodnl use default error
281864562SgshapiroRSOFTWARE $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed."
2819157001Sgshapiro# deal with TLS protocol errors: abort
2820157001SgshapiroRPROTOCOL $| <$-:$+> $* 	$#error $@ $2 $: $1 " STARTTLS failed."
2821157001Sgshapirodnl no <reply:dns> i.e. not requirements in the access map
2822157001Sgshapirodnl use default error
2823157001SgshapiroRPROTOCOL $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') STARTTLS failed."
282490792SgshapiroR$* $| <$*> <VERIFY>		$: <$2> <VERIFY> <> $1
282590792Sgshapirodnl separate optional requirements
282690792SgshapiroR$* $| <$*> <VERIFY + $+>	$: <$2> <VERIFY> <$3> $1
2827132943SgshapiroR$* $| <$*> <$={Tls}:$->$*	$: <$2> <$3:$4> <> $1
282890792Sgshapirodnl separate optional requirements
2829132943SgshapiroR$* $| <$*> <$={Tls}:$- + $+>$*	$: <$2> <$3:$4> <$5> $1
283064562Sgshapirodnl some other value in access map: accept
283164562Sgshapirodnl this also allows to override the default case (if used)
283264562SgshapiroR$* $| $*			$@ OK
283364562Sgshapiro# authentication required: give appropriate error
283464562Sgshapiro# other side did authenticate (via STARTTLS)
283590792Sgshapirodnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> <[extensions]> ${verify}
283664562Sgshapirodnl only verification required and it succeeded
283790792SgshapiroR<$*><VERIFY> <> OK		$@ OK
283890792Sgshapirodnl verification required and it succeeded but extensions are given
283990792Sgshapirodnl change it to <SMTP:ESC> <REQ:0>  <extensions>
284090792SgshapiroR<$*><VERIFY> <$+> OK		$: <$1> <REQ:0> <$2>
284164562Sgshapirodnl verification required + some level of encryption
284290792SgshapiroR<$*><VERIFY:$-> <$*> OK	$: <$1> <REQ:$2> <$3>
284364562Sgshapirodnl just some level of encryption required
284490792SgshapiroR<$*><ENCR:$-> <$*> $*		$: <$1> <REQ:$2> <$3>
284590792Sgshapirodnl workspace:
284690792Sgshapirodnl 1. <SMTP:ESC> <VERIFY [:bits]>  <[extensions]> {verify} (!= OK)
284790792Sgshapirodnl 2. <SMTP:ESC> <REQ:bits>  <[extensions]>
284890792Sgshapirodnl verification required but ${verify} is not set (case 1.)
284990792SgshapiroR<$-:$+><VERIFY $*> <$*>	$#error $@ $2 $: $1 " authentication required"
285090792SgshapiroR<$-:$+><VERIFY $*> <$*> FAIL	$#error $@ $2 $: $1 " authentication failed"
285190792SgshapiroR<$-:$+><VERIFY $*> <$*> NO	$#error $@ $2 $: $1 " not authenticated"
285290792SgshapiroR<$-:$+><VERIFY $*> <$*> NOT	$#error $@ $2 $: $1 " no authentication requested"
285390792SgshapiroR<$-:$+><VERIFY $*> <$*> NONE	$#error $@ $2 $: $1 " other side does not support STARTTLS"
285464562Sgshapirodnl some other value for ${verify}
285590792SgshapiroR<$-:$+><VERIFY $*> <$*> $+	$#error $@ $2 $: $1 " authentication failure " $4
285690792Sgshapirodnl some level of encryption required: get the maximum level (case 2.)
285790792SgshapiroR<$*><REQ:$-> <$*>		$: <$1> <REQ:$2> <$3> $>max $&{cipher_bits} : $&{auth_ssf}
285864562Sgshapirodnl compare required bits with actual bits
285990792SgshapiroR<$*><REQ:$-> <$*> $-		$: <$1> <$2:$4> <$3> $(arith l $@ $4 $@ $2 $)
286090792SgshapiroR<$-:$+><$-:$-> <$*> TRUE	$#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3
286190792Sgshapirodnl strength requirements fulfilled
286290792Sgshapirodnl TLS Additional Requirements Separator
286390792Sgshapirodnl this should be something which does not appear in the extensions itself
286490792Sgshapirodnl @ could be part of a CN, DN, etc...
286590792Sgshapirodnl use < > ? those are encoded in CN, DN, ...
286690792Sgshapirodefine(`_TLS_ARS_', `++')dnl
286790792Sgshapirodnl workspace:
286890792Sgshapirodnl <SMTP:ESC> <REQ:bits> <extensions> result-of-compare
286990792SgshapiroR<$-:$+><$-:$-> <$*> $*		$: <$1:$2 _TLS_ARS_ $5>
287090792Sgshapirodnl workspace: <SMTP:ESC _TLS_ARS_ extensions>
287190792Sgshapirodnl continue: check  extensions
287290792SgshapiroR<$-:$+ _TLS_ARS_ >			$@ OK
287390792Sgshapirodnl split extensions into own list
287490792SgshapiroR<$-:$+ _TLS_ARS_ $+ >			$: <$1:$2> <$3>
287590792SgshapiroR<$-:$+> < $+ _TLS_ARS_ $+ >		<$1:$2> <$3> <$4>
287690792SgshapiroR<$-:$+> $+			$@ $>"TLS_req" $3 $| <$1:$2>
287764562Sgshapiro
287890792Sgshapiro######################################################################
287990792Sgshapiro###  TLS_req: check additional TLS requirements
288090792Sgshapiro###
288190792Sgshapiro###	Parameters: [<list> <of> <req>] $| <$-:$+>
288290792Sgshapiro###		$-: SMTP reply code
288390792Sgshapiro###		$+: Enhanced Status Code
288490792Sgshapirodnl  further requirements for this ruleset:
288590792Sgshapirodnl	name of "other side" is stored is {TLS_name} (client/server_name)
288690792Sgshapirodnl
288790792Sgshapirodnl	currently only CN[:common_name] is implemented
288890792Sgshapirodnl	right now this is only a logical AND
288990792Sgshapirodnl	i.e. all requirements must be true
289090792Sgshapirodnl	how about an OR? CN must be X or CN must be Y or ..
289190792Sgshapirodnl	use a macro to compute this as a trivial sequential
289290792Sgshapirodnl	operations (no precedences etc)?
289390792Sgshapiro######################################################################
289490792SgshapiroSTLS_req
289590792Sgshapirodnl no additional requirements: ok
289690792SgshapiroR $| $+		$@ OK
289790792Sgshapirodnl require CN: but no CN specified: use name of other side
289890792SgshapiroR<CN> $* $| <$+>		$: <CN:$&{TLS_Name}> $1 $| <$2>
289990792Sgshapirodnl match, check rest
290090792SgshapiroR<CN:$&{cn_subject}> $* $| <$+>		$@ $>"TLS_req" $1 $| <$2>
290190792Sgshapirodnl CN does not match
290290792Sgshapirodnl  1   2      3  4
290390792SgshapiroR<CN:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " CN " $&{cn_subject} " does not match " $1
290490792Sgshapirodnl cert subject
290590792SgshapiroR<CS:$&{cert_subject}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
290690792Sgshapirodnl CS does not match
290790792Sgshapirodnl  1   2      3  4
2908110560SgshapiroR<CS:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " Cert Subject " $&{cert_subject} " does not match " $1
290990792Sgshapirodnl match, check rest
291090792SgshapiroR<CI:$&{cert_issuer}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
291190792Sgshapirodnl CI does not match
291290792Sgshapirodnl  1   2      3  4
2913110560SgshapiroR<CI:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " Cert Issuer " $&{cert_issuer} " does not match " $1
291490792Sgshapirodnl return from recursive call
291590792SgshapiroROK			$@ OK
291690792Sgshapiro
291790792Sgshapiro######################################################################
291890792Sgshapiro###  max: return the maximum of two values separated by :
291990792Sgshapiro###
292090792Sgshapiro###	Parameters: [$-]:[$-]
292190792Sgshapiro######################################################################
292264562SgshapiroSmax
292364562SgshapiroR:		$: 0
292464562SgshapiroR:$-		$: $1
292564562SgshapiroR$-:		$: $1
292664562SgshapiroR$-:$-		$: $(arith l $@ $1 $@ $2 $) : $1 : $2
292764562SgshapiroRTRUE:$-:$-	$: $2
292890792SgshapiroR$-:$-:$-	$: $2
292990792Sgshapirodnl endif _ACCESS_TABLE_
293090792Sgshapirodivert(0)
293164562Sgshapiro
2932285303Sgshapiroifdef(`_TLS_SESSION_FEATURES_', `dnl
2933285303SgshapiroStls_srv_features
2934285303Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
2935285303SgshapiroR$* $| $*		$: $>D <$1> <?> <! TLS_Srv_Features> <$2>
2936285303SgshapiroR<?> <$*> 		$: $>A <$1> <?> <! TLS_Srv_Features> <$1>
2937285303SgshapiroR<?> <$*> 		$@ ""
2938285303SgshapiroR<$+> <$*> 		$@ $1
2939285303Sgshapiro', `dnl
2940285303SgshapiroR$* 		$@ ""')
2941285303Sgshapiro
2942285303SgshapiroStls_clt_features
2943285303Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
2944285303SgshapiroR$* $| $*		$: $>D <$1> <?> <! TLS_Clt_Features> <$2>
2945285303SgshapiroR<?> <$*> 		$: $>A <$1> <?> <! TLS_Clt_Features> <$1>
2946285303SgshapiroR<?> <$*> 		$@ ""
2947285303SgshapiroR<$+> <$*> 		$@ $1
2948285303Sgshapiro', `dnl
2949285303SgshapiroR$* 		$@ ""')
2950285303Sgshapiro')
2951285303Sgshapiro
295290792Sgshapiro######################################################################
295390792Sgshapiro###  RelayTLS: allow relaying based on TLS authentication
295490792Sgshapiro###
295590792Sgshapiro###	Parameters:
295690792Sgshapiro###		none
295790792Sgshapiro######################################################################
295890792SgshapiroSRelayTLS
295964562Sgshapiro# authenticated?
296064562Sgshapirodnl we do not allow relaying for anyone who can present a cert
296164562Sgshapirodnl signed by a "trusted" CA. For example, even if we put verisigns
2962110560Sgshapirodnl CA in CertPath so we can authenticate users, we do not allow
296364562Sgshapirodnl them to abuse our server (they might be easier to get hold of,
296464562Sgshapirodnl but anyway).
296564562Sgshapirodnl so here is the trick: if the verification succeeded
296664562Sgshapirodnl we look up the cert issuer in the access map
296764562Sgshapirodnl (maybe after extracting a part with a regular expression)
296864562Sgshapirodnl if this returns RELAY we relay without further questions
296964562Sgshapirodnl if it returns SUBJECT we perform a similar check on the
297064562Sgshapirodnl cert subject.
297164562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
297290792SgshapiroR$*			$: <?> $&{verify}
297390792SgshapiroR<?> OK			$: OK		authenticated: continue
297490792SgshapiroR<?> $*			$@ NO		not authenticated
297564562Sgshapiroifdef(`_CERT_REGEX_ISSUER_', `dnl
297690792SgshapiroR$*			$: $(CERTIssuer $&{cert_issuer} $)',
297790792Sgshapiro`R$*			$: $&{cert_issuer}')
297890792SgshapiroR$+			$: $(access CERTISSUER`'_TAG_DELIM_`'$1 $)
297964562Sgshapirodnl use $# to stop further checks (delay_check)
298090792SgshapiroRRELAY			$# RELAY
298164562Sgshapiroifdef(`_CERT_REGEX_SUBJECT_', `dnl
298290792SgshapiroRSUBJECT		$: <@> $(CERTSubject $&{cert_subject} $)',
298390792Sgshapiro`RSUBJECT		$: <@> $&{cert_subject}')
298490792SgshapiroR<@> $+			$: <@> $(access CERTSUBJECT`'_TAG_DELIM_`'$1 $)
298590792SgshapiroR<@> RELAY		$# RELAY
298690792SgshapiroR$*			$: NO', `dnl')
298764562Sgshapiro
298890792Sgshapiro######################################################################
298990792Sgshapiro###  authinfo: lookup authinfo in the access map
299090792Sgshapiro###
299190792Sgshapiro###	Parameters:
299290792Sgshapiro###		$1: {server_name}
299390792Sgshapiro###		$2: {server_addr}
299490792Sgshapirodnl	both are currently ignored
299590792Sgshapirodnl if it should be done via another map, we either need to restrict
299690792Sgshapirodnl functionality (it calls D and A) or copy those rulesets (or add another
299790792Sgshapirodnl parameter which I want to avoid, it's quite complex already)
299890792Sgshapiro######################################################################
299990792Sgshapirodnl omit this ruleset if neither is defined?
300090792Sgshapirodnl it causes DefaultAuthInfo to be ignored
300190792Sgshapirodnl (which may be considered a good thing).
300290792SgshapiroSauthinfo
300390792Sgshapiroifdef(`_AUTHINFO_TABLE_', `dnl
300490792SgshapiroR$*		$: <$(authinfo AuthInfo:$&{server_name} $: ? $)>
300590792SgshapiroR<?>		$: <$(authinfo AuthInfo:$&{server_addr} $: ? $)>
300690792SgshapiroR<?>		$: <$(authinfo AuthInfo: $: ? $)>
300790792SgshapiroR<?>		$@ no				no authinfo available
300890792SgshapiroR<$*>		$# $1
300990792Sgshapirodnl', `dnl
301090792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
301190792SgshapiroR$*		$: $1 $| $>D <$&{server_name}> <?> <! AuthInfo> <>
301290792SgshapiroR$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! AuthInfo> <>
301390792SgshapiroR$* $| <?>$*	$: $1 $| <$(access AuthInfo`'_TAG_DELIM_ $: ? $)> <>
301490792SgshapiroR$* $| <?>$*	$@ no				no authinfo available
301590792SgshapiroR$* $| <$*> <>	$# $2
301690792Sgshapirodnl', `dnl')')
301790792Sgshapiro
3018132943Sgshapiroifdef(`_RATE_CONTROL_',`dnl
3019132943Sgshapiro######################################################################
3020132943Sgshapiro###  RateControl: 
3021132943Sgshapiro###	Parameters:	ignored
3022132943Sgshapiro###	return: $#error or OK
3023132943Sgshapiro######################################################################
3024132943SgshapiroSRateControl
3025132943Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
3026132943SgshapiroR$*		$: <A:$&{client_addr}> <E:>
3027132943Sgshapirodnl also look up a default value via E:
3028132943SgshapiroR$+		$: $>SearchList <! ClientRate> $| $1 <>
3029132943Sgshapirodnl found nothing: stop here
3030132943SgshapiroR<?>		$@ OK
3031132943Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
3032132943SgshapiroR<$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
3033132943Sgshapirodnl use the generic routine (for now)
3034132943SgshapiroR<0>		$@ OK		no limit
3035173340SgshapiroR<$+>		$: <$1> $| $(arith l $@ $1 $@ $&{client_rate} $)
3036132943Sgshapirodnl log this? Connection rate $&{client_rate} exceeds limit $1.
3037173340SgshapiroR<$+> $| TRUE	$#error $@ 4.3.2 $: _RATE_CONTROL_REPLY Connection rate limit exceeded.
3038132943Sgshapiro')')
3039132943Sgshapiro
3040132943Sgshapiroifdef(`_CONN_CONTROL_',`dnl
3041132943Sgshapiro######################################################################
3042132943Sgshapiro###  ConnControl: 
3043132943Sgshapiro###	Parameters:	ignored
3044132943Sgshapiro###	return: $#error or OK
3045132943Sgshapiro######################################################################
3046132943SgshapiroSConnControl
3047132943Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
3048132943SgshapiroR$*		$: <A:$&{client_addr}> <E:>
3049132943Sgshapirodnl also look up a default value via E:
3050132943SgshapiroR$+		$: $>SearchList <! ClientConn> $| $1 <>
3051132943Sgshapirodnl found nothing: stop here
3052132943SgshapiroR<?>		$@ OK
3053132943Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
3054132943SgshapiroR<$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
3055132943Sgshapirodnl use the generic routine (for now)
3056132943SgshapiroR<0>		$@ OK		no limit
3057173340SgshapiroR<$+>		$: <$1> $| $(arith l $@ $1 $@ $&{client_connections} $)
3058132943Sgshapirodnl log this: Open connections $&{client_connections} exceeds limit $1.
3059173340SgshapiroR<$+> $| TRUE	$#error $@ 4.3.2 $: _CONN_CONTROL_REPLY Too many open connections.
3060132943Sgshapiro')')
3061132943Sgshapiro
306264562Sgshapiroundivert(9)dnl LOCAL_RULESETS
306338032Speter#
306438032Speter######################################################################
306538032Speter######################################################################
306638032Speter#####
306764562Sgshapiro`#####			MAIL FILTER DEFINITIONS'
306864562Sgshapiro#####
306964562Sgshapiro######################################################################
307064562Sgshapiro######################################################################
307190792Sgshapiro_MAIL_FILTERS_
307264562Sgshapiro#
307364562Sgshapiro######################################################################
307464562Sgshapiro######################################################################
307564562Sgshapiro#####
307638032Speter`#####			MAILER DEFINITIONS'
307738032Speter#####
307838032Speter######################################################################
307938032Speter######################################################################
308064562Sgshapiroundivert(7)dnl MAILER_DEFINITIONS
308166494Sgshapiro
3082