proto.m4 revision 90792
138032Speterdivert(-1)
238032Speter#
3120256Sgshapiro# Copyright (c) 1998-2001 Sendmail, 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
16120256SgshapiroVERSIONID(`$Id: proto.m4,v 8.628 2001/12/28 19:02:40 ca Exp $')
1738032Speter
1864562Sgshapiro# level CF_LEVEL config file format
1964562SgshapiroV`'CF_LEVEL/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
11438032SpeterCP.
11538032Speter
11638032Speterifdef(`UUCP_RELAY',
11738032Speter`# UUCP relay host
11838032SpeterDY`'UUCP_RELAY
11938032SpeterCPUUCP
12038032Speter
12138032Speter')dnl
12238032Speterifdef(`BITNET_RELAY',
12338032Speter`#  BITNET relay host
12438032SpeterDB`'BITNET_RELAY
12538032SpeterCPBITNET
12638032Speter
12738032Speter')dnl
12838032Speterifdef(`DECNET_RELAY',
12938032Speter`define(`_USE_DECNET_SYNTAX_', 1)dnl
13038032Speter# DECnet relay host
13138032SpeterDC`'DECNET_RELAY
13238032SpeterCPDECNET
13338032Speter
13438032Speter')dnl
13538032Speterifdef(`FAX_RELAY',
13638032Speter`# FAX relay host
13738032SpeterDF`'FAX_RELAY
13838032SpeterCPFAX
13938032Speter
14038032Speter')dnl
14138032Speter# "Smart" relay host (may be null)
14290792SgshapiroDS`'ifdef(`SMART_HOST', `SMART_HOST')
14338032Speter
14438032Speterifdef(`LUSER_RELAY', `dnl
14538032Speter# place to which unknown users should be forwarded
14638032SpeterKuser user -m -a<>
14738032SpeterDL`'LUSER_RELAY',
14838032Speter`dnl')
14938032Speter
15038032Speter# operators that cannot be in local usernames (i.e., network indicators)
15138032SpeterCO @ % ifdef(`_NO_UUCP_', `', `!')
15238032Speter
15338032Speter# a class with just dot (for identifying canonical names)
15438032SpeterC..
15538032Speter
15638032Speter# a class with just a left bracket (for identifying domain literals)
15738032SpeterC[[
15838032Speter
15964562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
16064562Sgshapiro# access_db acceptance class
16164562SgshapiroC{Accept}OK RELAY
16290792Sgshapiroifdef(`_DELAY_COMPAT_8_10_',`dnl
16364562Sgshapiroifdef(`_BLACKLIST_RCPT_',`dnl
16464562Sgshapiro# possible access_db RHS for spam friends/haters
16564562SgshapiroC{SpamTag}SPAMFRIEND SPAMHATER')')',
16638032Speter`dnl')
16738032Speter
16890792Sgshapirodnl mark for "domain is ok" (resolved or accepted anyway)
16990792Sgshapirodefine(`_RES_OK_', `OKR')dnl
17038032Speterifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',`dnl',`dnl
17138032Speter# Resolve map (to check if a host exists in check_mail)
17290792SgshapiroKresolve host -a<_RES_OK_> -T<TEMP>')
17390792SgshapiroC{ResOk}_RES_OK_
17438032Speter
17580785Sgshapiroifdef(`_NEED_MACRO_MAP_', `dnl
17680785Sgshapiroifdef(`_MACRO_MAP_', `', `# macro storage map
17780785Sgshapirodefine(`_MACRO_MAP_', `1')dnl
17880785SgshapiroKmacro macro')', `dnl')
17966494Sgshapiro
18038032Speterifdef(`confCR_FILE', `dnl
18166494Sgshapiro# Hosts for which relaying is permitted ($=R)
18238032SpeterFR`'confCR_FILE',
18338032Speter`dnl')
18438032Speter
18590792Sgshapirodefine(`TLS_SRV_TAG', `"TLS_Srv"')dnl
18690792Sgshapirodefine(`TLS_CLT_TAG', `"TLS_Clt"')dnl
18790792Sgshapirodefine(`TLS_RCPT_TAG', `"TLS_Rcpt"')dnl
18890792Sgshapirodefine(`TLS_TRY_TAG', `"Try_TLS"')dnl
18990792Sgshapirodefine(`SRV_FEAT_TAG', `"Srv_Features"')dnl
19064562Sgshapirodnl this may be useful in other contexts too
19164562Sgshapiroifdef(`_ARITH_MAP_', `', `# arithmetic map
19264562Sgshapirodefine(`_ARITH_MAP_', `1')dnl
19364562SgshapiroKarith arith')
19464562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
19590792Sgshapiroifdef(`_MACRO_MAP_', `', `# macro storage map
19690792Sgshapirodefine(`_MACRO_MAP_', `1')dnl
19790792SgshapiroKmacro macro')
19890792Sgshapiro# possible values for TLS_connection in access map
19964562SgshapiroC{tls}VERIFY ENCR', `dnl')
20064562Sgshapiroifdef(`_CERT_REGEX_ISSUER_', `dnl
20164562Sgshapiro# extract relevant part from cert issuer
20264562SgshapiroKCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
20364562Sgshapiroifdef(`_CERT_REGEX_SUBJECT_', `dnl
20464562Sgshapiro# extract relevant part from cert subject
20564562SgshapiroKCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
20664562Sgshapiro
20790792Sgshapiroifdef(`LOCAL_RELAY', `dnl
208110647Sgshapiro# who I send unqualified names to (null means deliver locally)
209110560SgshapiroDR`'LOCAL_RELAY')
21090792Sgshapiro
21138032Speterifdef(`MAIL_HUB', `dnl
21290792Sgshapiro# who gets all local email traffic ($R has precedence for unqualified names)
213110560SgshapiroDH`'MAIL_HUB')
214110647Sgshapiro
21590792Sgshapiro# dequoting map
21638032SpeterKdequote dequote`'ifdef(`confDEQUOTE_OPTS', ` confDEQUOTE_OPTS', `')
21738032Speter
21890792Sgshapirodivert(0)dnl	# end of nullclient diversion
21938032Speter# class E: names that should be exposed as from this host, even if we masquerade
22038032Speter# class L: names that should be delivered locally, even if we have a relay
22138032Speter# class M: domains that should be converted to $M
22264562Sgshapiro# class N: domains that should not be converted to $M
22338032Speter#CL root
22464562Sgshapiroundivert(5)dnl
22538032Speterifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl')
22638032Speter
22764562Sgshapiroifdef(`MASQUERADE_NAME', `dnl
22838032Speter# who I masquerade as (null for no masquerading) (see also $=M)
22990792SgshapiroDM`'MASQUERADE_NAME')
23038032Speter
23190792Sgshapiro# my name for error messages
23238032Speterifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
23338032Speter
23438032Speterundivert(6)dnl LOCAL_CONFIG
23538032Speterinclude(_CF_DIR_`m4/version.m4')
23664562Sgshapiro
23738032Speter###############
23838032Speter#   Options   #
23938032Speter###############
24038032Speterifdef(`confAUTO_REBUILD',
24138032Speter`errprint(WARNING: `confAUTO_REBUILD' is no longer valid.
24290792Sgshapiro	There was a potential for a denial of service attack if this is set.
24390792Sgshapiro)')dnl
24490792Sgshapiro
24590792Sgshapiro# strip message body to 7 bits on input?
24638032Speter_OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
24738032Speter
24864562Sgshapiro# 8-bit data handling
24938032Speter_OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `pass8')
25038032Speter
25177349Sgshapiro# wait for alias file rebuild (default units: minutes)
25238032Speter_OPTION(AliasWait, `confALIAS_WAIT', `5m')
25338032Speter
25464562Sgshapiro# location of alias file
25538032Speter_OPTION(AliasFile, `ALIAS_FILE', `MAIL_SETTINGS_DIR`'aliases')
25638032Speter
25764562Sgshapiro# minimum number of free blocks on filesystem
25864562Sgshapiro_OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', `100')
25938032Speter
26064562Sgshapiro# maximum message size
26138032Speter_OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', `1000000')
26238032Speter
26364562Sgshapiro# substitution for space (blank) characters
26438032Speter_OPTION(BlankSub, `confBLANK_SUB', `_')
26538032Speter
26664562Sgshapiro# avoid connecting to "expensive" mailers on initial submission?
26738032Speter_OPTION(HoldExpensive, `confCON_EXPENSIVE', `False')
26838032Speter
26964562Sgshapiro# checkpoint queue runs after every N successful deliveries
27038032Speter_OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10')
27138032Speter
27264562Sgshapiro# default delivery mode
27338032Speter_OPTION(DeliveryMode, `confDELIVERY_MODE', `background')
27438032Speter
27564562Sgshapiro# error message header/file
27638032Speter_OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header')
27738032Speter
27864562Sgshapiro# error mode
27938032Speter_OPTION(ErrorMode, `confERROR_MODE', `print')
28038032Speter
28164562Sgshapiro# save Unix-style "From_" lines at top of header?
28238032Speter_OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False')
28338032Speter
28464562Sgshapiro# queue file mode (qf files)
28538032Speter_OPTION(QueueFileMode, `confQUEUE_FILE_MODE', `0600')
28690792Sgshapiro
28790792Sgshapiro# temporary file mode
28890792Sgshapiro_OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600')
28938032Speter
29064562Sgshapiro# match recipients against GECOS field?
29138032Speter_OPTION(MatchGECOS, `confMATCH_GECOS', `False')
29238032Speter
29364562Sgshapiro# maximum hop count
29438032Speter_OPTION(MaxHopCount, `confMAX_HOP', `25')
29538032Speter
29690792Sgshapiro# location of help file
29738032SpeterO HelpFile=ifdef(`HELP_FILE', HELP_FILE, `MAIL_SETTINGS_DIR`'helpfile')
29838032Speter
29964562Sgshapiro# ignore dots as terminators in incoming messages?
30038032Speter_OPTION(IgnoreDots, `confIGNORE_DOTS', `False')
30138032Speter
30264562Sgshapiro# name resolver options
30338032Speter_OPTION(ResolverOptions, `confBIND_OPTS', `+AAONLY')
30438032Speter
30564562Sgshapiro# deliver MIME-encapsulated error messages?
30638032Speter_OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS', `True')
30738032Speter
30864562Sgshapiro# Forward file search path
30938032Speter_OPTION(ForwardPath, `confFORWARD_PATH', `/var/forward/$u:$z/.forward.$w:$z/.forward')
31038032Speter
31164562Sgshapiro# open connection cache size
31238032Speter_OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', `2')
31338032Speter
31464562Sgshapiro# open connection cache timeout
31538032Speter_OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', `5m')
31638032Speter
31764562Sgshapiro# persistent host status directory
31838032Speter_OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', `.hoststat')
31938032Speter
32064562Sgshapiro# single thread deliveries (requires HostStatusDirectory)?
32138032Speter_OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY', `False')
32238032Speter
32364562Sgshapiro# use Errors-To: header?
32438032Speter_OPTION(UseErrorsTo, `confUSE_ERRORS_TO', `False')
32538032Speter
32664562Sgshapiro# log level
32738032Speter_OPTION(LogLevel, `confLOG_LEVEL', `10')
32838032Speter
32964562Sgshapiro# send to me too, even in an alias expansion?
33038032Speter_OPTION(MeToo, `confME_TOO', `True')
33138032Speter
33264562Sgshapiro# verify RHS in newaliases?
33338032Speter_OPTION(CheckAliases, `confCHECK_ALIASES', `False')
33438032Speter
33564562Sgshapiro# default messages to old style headers if no special punctuation?
33638032Speter_OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS', `False')
33738032Speter
33864562Sgshapiro# SMTP daemon options
33938032Speterifelse(defn(`confDAEMON_OPTIONS'), `', `dnl',
34038032Speter`errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.  See cf/README for more information.
34164562Sgshapiro)'dnl
34294334Sgshapiro`DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
34394334Sgshapiroifelse(defn(`_DPO_'), `',
34464562Sgshapiro`ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-v4, Family=inet
34564562SgshapiroO DaemonPortOptions=Name=MTA-v6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_')
34666494Sgshapiroifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
34790792Sgshapiro
34890792Sgshapiro# SMTP client options
34964562Sgshapiroifelse(defn(`confCLIENT_OPTIONS'), `', `dnl',
35038032Speter`errprint(WARNING: `confCLIENT_OPTIONS' is no longer valid.  See cf/README for more information.
35164562Sgshapiro)'dnl
35290792Sgshapiro`CLIENT_OPTIONS(`confCLIENT_OPTIONS')')
35390792Sgshapiroifelse(defn(`_CPO_'), `',
35490792Sgshapiro`#O ClientPortOptions=Family=inet, Address=0.0.0.0', `_CPO_')
35590792Sgshapiro
35690792Sgshapiro# Modifiers to `define' {daemon_flags} for direct submissions
35790792Sgshapiro_OPTION(DirectSubmissionModifiers, `confDIRECT_SUBMISSION_MODIFIERS', `')
35864562Sgshapiro
35990792Sgshapiro# Use as mail submission program? See sendmail/SECURITY
36090792Sgshapiro_OPTION(UseMSP, `confUSE_MSP', `')
36190792Sgshapiro
36290792Sgshapiro# privacy flags
36390792Sgshapiro_OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings')
36490792Sgshapiro
36538032Speter# who (if anyone) should get extra copies of error messages
36664562Sgshapiro_OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster')
36738032Speter
36838032Speter# slope of queue-only function
36964562Sgshapiro_OPTION(QueueFactor, `confQUEUE_FACTOR', `600000')
37038032Speter
37138032Speter# limit on number of concurrent queue runners
37264562Sgshapiro_OPTION(MaxQueueChildren, `confMAX_QUEUE_CHILDREN', `')
37338032Speter
37490792Sgshapiro# maximum number of queue-runners per queue-grouping with multiple queues
37590792Sgshapiro_OPTION(MaxRunnersPerQueue, `confMAX_RUNNERS_PER_QUEUE', `1')
37690792Sgshapiro
37790792Sgshapiro# priority of queue runners (nice(3))
37890792Sgshapiro_OPTION(NiceQueueRun, `confNICE_QUEUE_RUN', `')
37990792Sgshapiro
38090792Sgshapiro# shall we sort the queue by hostname first?
38190792Sgshapiro_OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority')
38290792Sgshapiro
38390792Sgshapiro# minimum time in queue before retry
38490792Sgshapiro_OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m')
38590792Sgshapiro
38690792Sgshapiro# how many jobs can you process in the queue?
38790792Sgshapiro_OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `10000')
38890792Sgshapiro
38990792Sgshapiro# perform initial split of envelope without checking MX records
39090792Sgshapiro_OPTION(FastSplit, `confFAST_SPLIT', `1')
39190792Sgshapiro
39290792Sgshapiro# queue directory
39390792SgshapiroO QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue')
39490792Sgshapiro
39538032Speter# key for shared memory; 0 to turn off
39664562Sgshapiro_OPTION(SharedMemoryKey, `confSHARED_MEMORY_KEY', `0')
39738032Speter
39890792Sgshapiro# timeouts (many of these)
39990792Sgshapiro_OPTION(Timeout.initial, `confTO_INITIAL', `5m')
40090792Sgshapiro_OPTION(Timeout.connect, `confTO_CONNECT', `5m')
40194334Sgshapiro_OPTION(Timeout.aconnect, `confTO_ACONNECT', `0s')
40294334Sgshapiro_OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m')
40394334Sgshapiro_OPTION(Timeout.helo, `confTO_HELO', `5m')
40494334Sgshapiro_OPTION(Timeout.mail, `confTO_MAIL', `10m')
40538032Speter_OPTION(Timeout.rcpt, `confTO_RCPT', `1h')
40664562Sgshapiro_OPTION(Timeout.datainit, `confTO_DATAINIT', `5m')
40764562Sgshapiro_OPTION(Timeout.datablock, `confTO_DATABLOCK', `1h')
40890792Sgshapiro_OPTION(Timeout.datafinal, `confTO_DATAFINAL', `1h')
40964562Sgshapiro_OPTION(Timeout.rset, `confTO_RSET', `5m')
41064562Sgshapiro_OPTION(Timeout.quit, `confTO_QUIT', `2m')
41164562Sgshapiro_OPTION(Timeout.misc, `confTO_MISC', `2m')
41264562Sgshapiro_OPTION(Timeout.command, `confTO_COMMAND', `1h')
41364562Sgshapiro_OPTION(Timeout.ident, `confTO_IDENT', `5s')
41464562Sgshapiro_OPTION(Timeout.fileopen, `confTO_FILEOPEN', `60s')
41564562Sgshapiro_OPTION(Timeout.control, `confTO_CONTROL', `2m')
41664562Sgshapiro_OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', `5d')
41764562Sgshapiro_OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', `5d')
41864562Sgshapiro_OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', `2d')
41964562Sgshapiro_OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', `7d')
42064562Sgshapiro_OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', `4h')
42164562Sgshapiro_OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', `4h')
42264562Sgshapiro_OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', `1h')
42364562Sgshapiro_OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', `12h')
42464562Sgshapiro_OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', `30m')
42564562Sgshapiro_OPTION(Timeout.resolver.retrans, `confTO_RESOLVER_RETRANS', `5s')
42664562Sgshapiro_OPTION(Timeout.resolver.retrans.first, `confTO_RESOLVER_RETRANS_FIRST', `5s')
427112810Sgshapiro_OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s')
428112810Sgshapiro_OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4')
42964562Sgshapiro_OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4')
43064562Sgshapiro_OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4')
43164562Sgshapiro_OPTION(Timeout.lhlo, `confTO_LHLO', `2m')
43264562Sgshapiro_OPTION(Timeout.auth, `confTO_AUTH', `10m')
433112810Sgshapiro_OPTION(Timeout.starttls, `confTO_STARTTLS', `1h')
434112810Sgshapiro
43564562Sgshapiro# time for DeliverBy; extension disabled if less than 0
43664562Sgshapiro_OPTION(DeliverByMin, `confDELIVER_BY_MIN', `0')
43764562Sgshapiro
43864562Sgshapiro# should we not prune routes in route-addr syntax addresses?
43964562Sgshapiro_OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False')
44064562Sgshapiro
44164562Sgshapiro# queue up everything before forking?
44290792Sgshapiro_OPTION(SuperSafe, `confSAFE_QUEUE', `True')
44390792Sgshapiro
44490792Sgshapiro# status file
44538032SpeterO StatusFile=ifdef(`STATUS_FILE', `STATUS_FILE', `MAIL_SETTINGS_DIR`'statistics')
44690792Sgshapiro
44790792Sgshapiro# time zone handling:
44890792Sgshapiro#  if undefined, use system default
44938032Speter#  if defined but null, use TZ envariable passed in
45064562Sgshapiro#  if defined and non-null, use that info
45138032Speterifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
45238032Speter	confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
45364562Sgshapiro	`O TimeZoneSpec=confTIME_ZONE')
45438032Speter
45538032Speter# default UID (can be username or userid:groupid)
45664562Sgshapiro_OPTION(DefaultUser, `confDEF_USER_ID', `mailnull')
45738032Speter
45838032Speter# list of locations of user database file (null means no lookup)
45938032Speter_OPTION(UserDatabaseSpec, `confUSERDB_SPEC', `MAIL_SETTINGS_DIR`'userdb')
46038032Speter
46138032Speter# fallback MX host
46238032Speter_OPTION(FallbackMXhost, `confFALLBACK_MX', `fall.back.host.net')
46338032Speter
46438032Speter# if we are the best MX host for a site, try it directly instead of config err
46538032Speter_OPTION(TryNullMXList, `confTRY_NULL_MX_LIST', `False')
46638032Speter
46764562Sgshapiro# load average at which we just queue messages
46838032Speter_OPTION(QueueLA, `confQUEUE_LA', `8')
46938032Speter
47064562Sgshapiro# load average at which we refuse connections
47138032Speter_OPTION(RefuseLA, `confREFUSE_LA', `12')
47238032Speter
47364562Sgshapiro# load average at which we delay connections; 0 means no limit
47438032Speter_OPTION(DelayLA, `confDELAY_LA', `0')
47538032Speter
47664562Sgshapiro# maximum number of children we allow at one time
47738032Speter_OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `12')
47838032Speter
47964562Sgshapiro# maximum number of new connections per second
48038032Speter_OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0')
48138032Speter
48264562Sgshapiro# work recipient factor
48338032Speter_OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
48490792Sgshapiro
48590792Sgshapiro# deliver each queued job in a separate process?
48690792Sgshapiro_OPTION(ForkEachJob, `confSEPARATE_PROC', `False')
48738032Speter
48898841Sgshapiro# work class factor
48938032Speter_OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800')
49038032Speter
49171345Sgshapiro# work time factor
49238032Speter_OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000')
49338032Speter
49464562Sgshapiro# default character set
49538032Speter_OPTION(DefaultCharSet, `confDEF_CHAR_SET', `iso-8859-1')
49638032Speter
49764562Sgshapiro# service switch file (name hardwired on Solaris, Ultrix, OSF/1, others)
49838032Speter_OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch')
49938032Speter
50064562Sgshapiro# hosts file (normally /etc/hosts)
50138032Speter_OPTION(HostsFile, `confHOSTS_FILE', `/etc/hosts')
50238032Speter
50364562Sgshapiro# dialup line delay on connection failure
50438032Speter_OPTION(DialDelay, `confDIAL_DELAY', `10s')
50538032Speter
50664562Sgshapiro# action to take if there are no recipients in the message
50738032Speter_OPTION(NoRecipientAction, `confNO_RCPT_ACTION', `add-to-undisclosed')
50890792Sgshapiro
50964562Sgshapiro# chrooted environment for writing to files
51038032Speter_OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `/arch')
51138032Speter
51264562Sgshapiro# are colons OK in addresses?
51338032Speter_OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True')
51438032Speter
51564562Sgshapiro# shall I avoid expanding CNAMEs (violates protocols)?
51638032Speter_OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False')
51738032Speter
51864562Sgshapiro# SMTP initial login message (old $e macro)
51938032Speter_OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG', `$j Sendmail $v ready at $b')
52038032Speter
52164562Sgshapiro# UNIX initial From header format (old $l macro)
52238032Speter_OPTION(UnixFromLine, `confFROM_LINE', `From $g $d')
52338032Speter
52464562Sgshapiro# From: lines that have embedded newlines are unwrapped onto one line
52538032Speter_OPTION(SingleLineFromHeader, `confSINGLE_LINE_FROM_HEADER', `False')
52638032Speter
52764562Sgshapiro# Allow HELO SMTP command that does not `include' a host name
52838032Speter_OPTION(AllowBogusHELO, `confALLOW_BOGUS_HELO', `False')
52938032Speter
53064562Sgshapiro# Characters to be quoted in a full name phrase (@,;:\()[] are automatic)
53138032Speter_OPTION(MustQuoteChars, `confMUST_QUOTE_CHARS', `.')
53238032Speter
53364562Sgshapiro# delimiter (operator) characters (old $o macro)
53438032Speter_OPTION(OperatorChars, `confOPERATORS', `.:@[]')
53538032Speter
53664562Sgshapiro# shall I avoid calling initgroups(3) because of high NIS costs?
53738032Speter_OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False')
53838032Speter
53964562Sgshapiro# are group-writable `:include:' and .forward files (un)trustworthy?
54038032Speter# True (the default) means they are not trustworthy.
54138032Speter_OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True')
54264562Sgshapiroifdef(`confUNSAFE_GROUP_WRITES',
54338032Speter`errprint(`WARNING: confUNSAFE_GROUP_WRITES is deprecated; use confDONT_BLAME_SENDMAIL.
54438032Speter')')
54564562Sgshapiro
54638032Speter# where do errors that occur when sending errors get sent?
54738032Speter_OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster')
54864562Sgshapiro
54938032Speter# where to save bounces if all else fails
55038032Speter_OPTION(DeadLetterDrop, `confDEAD_LETTER_DROP', `/var/tmp/dead.letter')
55190792Sgshapiro
55264562Sgshapiro# what user id do we assume for the majority of the processing?
55390792Sgshapiro_OPTION(RunAsUser, `confRUN_AS_USER', `sendmail')
55490792Sgshapiro
55590792Sgshapiro# maximum number of recipients per SMTP envelope
55638032Speter_OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `100')
55738032Speter
55864562Sgshapiro# limit the rate recipients per SMTP envelope are accepted
55938032Speter# once the threshold number of recipients have been rejected
56064562Sgshapiro_OPTION(BadRcptThrottle, `confBAD_RCPT_THROTTLE', `20')
56164562Sgshapiro
56264562Sgshapiro# shall we get local names from our installed interfaces?
56338032Speter_OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False')
56464562Sgshapiro
56538032Speter# Return-Receipt-To: header implies DSN request
56638032Speter_OPTION(RrtImpliesDsn, `confRRT_IMPLIES_DSN', `False')
56764562Sgshapiro
56838032Speter# override connection address (for testing)
56990792Sgshapiro_OPTION(ConnectOnlyTo, `confCONNECT_ONLY_TO', `0.0.0.0')
57090792Sgshapiro
57190792Sgshapiro# Trusted user for file ownership and starting the daemon
57290792Sgshapiro_OPTION(TrustedUser, `confTRUSTED_USER', `root')
57338032Speter
57464562Sgshapiro# Control socket for daemon management
57538032Speter_OPTION(ControlSocketName, `confCONTROL_SOCKET_NAME', `/var/spool/mqueue/.control')
57664562Sgshapiro
57764562Sgshapiro# Maximum MIME header length to protect MUAs
57864562Sgshapiro_OPTION(MaxMimeHeaderLength, `confMAX_MIME_HEADER_LENGTH', `0/0')
57964562Sgshapiro
58064562Sgshapiro# Maximum length of the sum of all headers
58164562Sgshapiro_OPTION(MaxHeadersLength, `confMAX_HEADERS_LENGTH', `32768')
58264562Sgshapiro
58364562Sgshapiro# Maximum depth of alias recursion
58464562Sgshapiro_OPTION(MaxAliasRecursion, `confMAX_ALIAS_RECURSION', `10')
58564562Sgshapiro
58664562Sgshapiro# location of pid file
58764562Sgshapiro_OPTION(PidFile, `confPID_FILE', `/var/run/sendmail.pid')
58864562Sgshapiro
589112810Sgshapiro# Prefix string for the process title shown on 'ps' listings
59064562Sgshapiro_OPTION(ProcessTitlePrefix, `confPROCESS_TITLE_PREFIX', `prefix')
59164562Sgshapiro
59264562Sgshapiro# Data file (df) memory-buffer file maximum size
59364562Sgshapiro_OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096')
59464562Sgshapiro
59564562Sgshapiro# Transcript file (xf) memory-buffer file maximum size
59664562Sgshapiro_OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096')
59764562Sgshapiro
59864562Sgshapiro# lookup type to find information about local mailboxes
59964562Sgshapiro_OPTION(MailboxDatabase, `confMAILBOX_DATABASE', `pw')
60064562Sgshapiro
60164562Sgshapiro# list of authentication mechanisms
60264562Sgshapiro_OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5')
60364562Sgshapiro
60464562Sgshapiro# default authentication information for outgoing connections
60564562Sgshapiro_OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info')
60664562Sgshapiro
60764562Sgshapiro# SMTP AUTH flags
60864562Sgshapiro_OPTION(AuthOptions, `confAUTH_OPTIONS', `')
60990792Sgshapiro
61090792Sgshapiro# SMTP AUTH maximum encryption strength
61190792Sgshapiro_OPTION(AuthMaxBits, `confAUTH_MAX_BITS', `')
61264562Sgshapiro
61390792Sgshapiro# SMTP STARTTLS server options
61464562Sgshapiro_OPTION(TLSSrvOptions, `confTLS_SRV_OPTIONS', `')
61564562Sgshapiro
61664562Sgshapiro# Input mail filters
61764562Sgshapiro_OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
61864562Sgshapiro
61964562Sgshapiroifdef(`confINPUT_MAIL_FILTERS', `dnl
62064562Sgshapiro# Milter options
62190792Sgshapiro_OPTION(Milter.LogLevel, `confMILTER_LOG_LEVEL', `')
62290792Sgshapiro_OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `')
62390792Sgshapiro_OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `')
62490792Sgshapiro_OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
62590792Sgshapiro_OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')')
62690792Sgshapiro
62764562Sgshapiro# CA directory
62864562Sgshapiro_OPTION(CACERTPath, `confCACERT_PATH', `')
62964562Sgshapiro# CA file
63098841Sgshapiro_OPTION(CACERTFile, `confCACERT', `')
63164562Sgshapiro# Server Cert
63290792Sgshapiro_OPTION(ServerCertFile, `confSERVER_CERT', `')
63364562Sgshapiro# Server private key
63464562Sgshapiro_OPTION(ServerKeyFile, `confSERVER_KEY', `')
63564562Sgshapiro# Client Cert
63664562Sgshapiro_OPTION(ClientCertFile, `confCLIENT_CERT', `')
63764562Sgshapiro# Client private key
63864562Sgshapiro_OPTION(ClientKeyFile, `confCLIENT_KEY', `')
639110560Sgshapiro# DHParameters (only required if DSA/DH is used)
64064562Sgshapiro_OPTION(DHParameters, `confDH_PARAMETERS', `')
641110560Sgshapiro# Random data source (required for systems without /dev/urandom under OpenSSL)
64264562Sgshapiro_OPTION(RandFile, `confRAND_FILE', `')
64364562Sgshapiro
64464562Sgshapiro############################
64564562Sgshapiro`# QUEUE GROUP DEFINITIONS  #'
64664562Sgshapiro############################
64764562Sgshapiro_QUEUE_GROUP_
64864562Sgshapiro
64964562Sgshapiro###########################
65064562Sgshapiro#   Message precedences   #
65164562Sgshapiro###########################
65264562Sgshapiro
65364562SgshapiroPfirst-class=0
65464562SgshapiroPspecial-delivery=100
65590792SgshapiroPlist=-30
65690792SgshapiroPbulk=-60
65790792SgshapiroPjunk=-100
65890792Sgshapiro
65942575Speter#####################
66038032Speter#   Trusted users   #
66138032Speter#####################
66238032Speter
66338032Speter# this is equivalent to setting class "t"
66438032Speterifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `MAIL_SETTINGS_DIR`'trusted-users')
66538032SpeterTroot
66638032SpeterTdaemon
66738032Speterifdef(`_NO_UUCP_', `dnl', `Tuucp')
66838032Speterifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
66938032Speter
67038032Speter#########################
67138032Speter#   Format of headers   #
67238032Speter#########################
67338032Speter
67438032Speterifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
67564562SgshapiroH?P?Return-Path: <$g>
67638032SpeterHReceived: confRECEIVED_HEADER
67738032SpeterH?D?Resent-Date: $a
67838032SpeterH?D?Date: $a
67938032SpeterH?F?Resent-From: confFROM_HEADER
68038032SpeterH?F?From: confFROM_HEADER
68138032SpeterH?x?Full-Name: $x
68238032Speter# HPosted-Date: $a
68338032Speter# H?l?Received-Date: $b
68438032SpeterH?M?Resent-Message-Id: <$t.$i@$j>
68538032SpeterH?M?Message-Id: <$t.$i@$j>
68638032Speter
68738032Speter#
68838032Speter######################################################################
68938032Speter######################################################################
69038032Speter#####
69138032Speter#####			REWRITING RULES
69238032Speter#####
69338032Speter######################################################################
69438032Speter######################################################################
69538032Speter
69638032Speter############################################
69764562Sgshapiro###  Ruleset 3 -- Name Canonicalization  ###
69838032Speter############################################
69938032SpeterScanonify=3
70038032Speter
70138032Speter# handle null input (translate to <@> special case)
70238032SpeterR$@			$@ <@>
70338032Speter
70438032Speter# strip group: syntax (not inside angle brackets!) and trailing semicolon
70538032SpeterR$*			$: $1 <@>			mark addresses
70638032SpeterR$* < $* > $* <@>	$: $1 < $2 > $3			unmark <addr>
70738032SpeterR@ $* <@>		$: @ $1				unmark @host:...
70838032SpeterR$* [ IPv6 : $+ ] <@>	$: $1 [ IPv6 : $2 ]		unmark IPv6 addr
70938032SpeterR$* :: $* <@>		$: $1 :: $2			unmark node::addr
71064562SgshapiroR:`include': $* <@>	$: :`include': $1			unmark :`include':...
71138032SpeterR$* : $* [ $* ]		$: $1 : $2 [ $3 ] <@>		remark if leading colon
71238032SpeterR$* : $* <@>		$: $2				strip colon if marked
71338032SpeterR$* <@>			$: $1				unmark
71438032SpeterR$* ;			   $1				strip trailing semi
71538032SpeterR$* < $+ :; > $*	$@ $2 :; <@>			catch <list:;>
71638032SpeterR$* < $* ; >		   $1 < $2 >			bogus bracketed semi
71738032Speter
71838032Speter# null input now results from list:; syntax
71990792SgshapiroR$@			$@ :; <@>
72038032Speter
72138032Speter# strip angle brackets -- note RFC733 heuristic to get innermost item
72238032SpeterR$*			$: < $1 >			housekeeping <>
72338032SpeterR$+ < $* >		   < $2 >			strip excess on left
72438032SpeterR< $* > $+		   < $1 >			strip excess on right
72538032SpeterR<>			$@ < @ >			MAIL FROM:<> case
72671345SgshapiroR< $+ >			$: $1				remove housekeeping <>
72738032Speter
72838032Speterifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
72938032Speter# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
73038032SpeterR@ $+ , $+		@ $1 : $2			change all "," to ":"
73138032Speter
73238032Speter# localize and dispose of route-based addresses
73338032Speterdnl XXX: IPv6 colon conflict
73438032Speterifdef(`NO_NETINET6', `dnl',
73538032Speter`R@ [$+] : $+		$@ $>Canonify2 < @ [$1] > : $2	handle <route-addr>')
73638032SpeterR@ $+ : $+		$@ $>Canonify2 < @$1 > : $2	handle <route-addr>
73738032Speterdnl',`dnl
73838032Speter# strip route address <@a,@b,@c:user@d> -> <user@d>
73964562SgshapiroR@ $+ , $+		$2
74038032Speterifdef(`NO_NETINET6', `dnl',
74138032Speter`R@ [ $* ] : $+		$2')
74238032SpeterR@ $+ : $+		$2
74338032Speterdnl')
74490792Sgshapiro
74590792Sgshapiro# find focus for list syntax
74690792SgshapiroR $+ : $* ; @ $+	$@ $>Canonify2 $1 : $2 ; < @ $3 >	list syntax
74764562SgshapiroR $+ : $* ;		$@ $1 : $2;			list syntax
74864562Sgshapiro
74964562Sgshapiro# find focus for @ syntax addresses
75064562SgshapiroR$+ @ $+		$: $1 < @ $2 >			focus on domain
75190792SgshapiroR$+ < $+ @ $+ >		$1 $2 < @ $3 >			move gaze right
75290792SgshapiroR$+ < @ $+ >		$@ $>Canonify2 $1 < @ $2 >	already canonical
75364562Sgshapiro
75464562Sgshapirodnl This is flagged as an error in S0; no need to silently fix it here.
75538032Speterdnl # do some sanity checking
75638032Speterdnl R$* < @ $~[ $* : $* > $*	$1 < @ $2 $3 > $4	nix colons in addrs
75764562Sgshapiro
75838032Speterifdef(`_NO_UUCP_', `dnl',
75938032Speter`# convert old-style addresses to a domain-based address
76038032SpeterR$- ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	resolve uucp names
76138032SpeterR$+ . $- ! $+		$@ $>Canonify2 $3 < @ $1 . $2 >		domain uucps
76238032SpeterR$+ ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	uucp subdomains
76364562Sgshapiro')
76438032Speterifdef(`_USE_DECNET_SYNTAX_',
76590792Sgshapiro`# convert node::user addresses into a domain-based address
76690792SgshapiroR$- :: $+		$@ $>Canonify2 $2 < @ $1 .DECNET >	resolve DECnet names
76790792SgshapiroR$- . $- :: $+		$@ $>Canonify2 $3 < @ $1.$2 .DECNET >	numeric DECnet addr
76838032Speter',
76938032Speter	`dnl')
77038032Speter# if we have % signs, take the rightmost one
77164562SgshapiroR$* % $*		$1 @ $2				First make them all @s.
77264562SgshapiroR$* @ $* @ $*		$1 % $2 @ $3			Undo all but the last.
77364562SgshapiroR$* @ $*		$@ $>Canonify2 $1 < @ $2 >	Insert < > and finish
77438032Speter
77538032Speter# else we must be a local name
77638032SpeterR$*			$@ $>Canonify2 $1
77764562Sgshapiro
77864562Sgshapiro
77938032Speter################################################
78038032Speter###  Ruleset 96 -- bottom half of ruleset 3  ###
78138032Speter################################################
78238032Speter
78338032SpeterSCanonify2=96
78464562Sgshapiro
78538032Speter# handle special cases for local names
78638032SpeterR$* < @ localhost > $*		$: $1 < @ $j . > $2		no domain at all
78764562SgshapiroR$* < @ localhost . $m > $*	$: $1 < @ $j . > $2		local domain
78838032Speterifdef(`_NO_UUCP_', `dnl',
78938032Speter`R$* < @ localhost . UUCP > $*	$: $1 < @ $j . > $2		.UUCP domain')
79038032Speter
79138032Speter# check for IPv4/IPv6 domain literal
79238032SpeterR$* < @ [ $+ ] > $*		$: $1 < @@ [ $2 ] > $3		mark [addr]
79338032SpeterR$* < @@ $=w > $*		$: $1 < @ $j . > $3		self-literal
79464562SgshapiroR$* < @@ $+ > $*		$@ $1 < @ $2 > $3		canon IP addr
79538032Speter
79638032Speterifdef(`_DOMAIN_TABLE_', `dnl
79738032Speter# look up domains in the domain table
79838032SpeterR$* < @ $+ > $* 		$: $1 < @ $(domaintable $2 $) > $3', `dnl')
79938032Speter
80038032Speterundivert(2)dnl LOCAL_RULE_3
80164562Sgshapiro
80290792Sgshapiroifdef(`_BITDOMAIN_TABLE_', `dnl
80390792Sgshapiro# handle BITNET mapping
80438032SpeterR$* < @ $+ .BITNET > $*		$: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3', `dnl')
80538032Speter
80638032Speterifdef(`_UUDOMAIN_TABLE_', `dnl
80764562Sgshapiro# handle UUCP mapping
80838032SpeterR$* < @ $+ .UUCP > $*		$: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3', `dnl')
80938032Speter
81038032Speterifdef(`_NO_UUCP_', `dnl',
81164562Sgshapiro`ifdef(`UUCP_RELAY',
81238032Speter`# pass UUCP addresses straight through
81364562SgshapiroR$* < @ $+ . UUCP > $*		$@ $1 < @ $2 . UUCP . > $3',
81438032Speter`# if really UUCP, handle it immediately
81538032Speterifdef(`_CLASS_U_',
81638032Speter`R$* < @ $=U . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
81764562Sgshapiroifdef(`_CLASS_V_',
81838032Speter`R$* < @ $=V . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
81938032Speterifdef(`_CLASS_W_',
82038032Speter`R$* < @ $=W . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
82138032Speterifdef(`_CLASS_X_',
82238032Speter`R$* < @ $=X . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
82338032Speterifdef(`_CLASS_Y_',
82438032Speter`R$* < @ $=Y . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
82538032Speter
82638032Speterifdef(`_NO_CANONIFY_', `dnl', `dnl
82738032Speter# try UUCP traffic as a local address
82838032SpeterR$* < @ $+ . UUCP > $*		$: $1 < @ $[ $2 $] . UUCP . > $3
82938032SpeterR$* < @ $+ . . UUCP . > $*	$@ $1 < @ $2 . > $3')
83038032Speter')')
83138032Speter# hostnames ending in class P are always canonical
83238032SpeterR$* < @ $* $=P > $*		$: $1 < @ $2 $3 . > $4
83338032Speterdnl apply the next rule only for hostnames not in class P
83438032Speterdnl this even works for phrases in class P since . is in class P
83538032Speterdnl which daemon flags are set?
83638032SpeterR$* < @ $* $~P > $*		$: $&{daemon_flags} $| $1 < @ $2 $3 > $4
83738032Speterdnl the other rules in this section only apply if the hostname
83838032Speterdnl does not end in class P hence no further checks are done here
83938032Speterdnl if this ever changes make sure the lookups are "protected" again!
84038032Speterifdef(`_NO_CANONIFY_', `dnl
84138032Speterdnl do not canonify unless:
84264562Sgshapirodnl domain ends in class {Canonify} (this does not work if the intersection
84364562Sgshapirodnl	with class P is non-empty)
84464562Sgshapirodnl or {daemon_flags} has c set
84564562Sgshapiro# pass to name server to make hostname canonical if in class {Canonify}
84664562SgshapiroR$* $| $* < @ $* $={Canonify} > $*	$: $2 < @ $[ $3 $4 $] > $5
84764562Sgshapiro# pass to name server to make hostname canonical if requested
84864562SgshapiroR$* c $* $| $* < @ $* > $*	$: $3 < @ $[ $4 $] > $5
84964562Sgshapirodnl trailing dot? -> do not apply _CANONIFY_HOSTS_
85064562SgshapiroR$* $| $* < @ $+ . > $*		$: $2 < @ $3 . > $4
85164562Sgshapiro# add a trailing dot to qualified hostnames so other rules will work
85264562SgshapiroR$* $| $* < @ $+.$+ > $*	$: $2 < @ $3.$4 . > $5
85364562Sgshapiroifdef(`_CANONIFY_HOSTS_', `dnl
85464562Sgshapirodnl this should only apply to unqualified hostnames
85564562Sgshapirodnl but if a valid character inside an unqualified hostname is an OperatorChar
85664562Sgshapirodnl then $- does not work.
85764562Sgshapiro# lookup unqualified hostnames
85864562SgshapiroR$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4', `dnl')', `dnl
85964562Sgshapirodnl _NO_CANONIFY_ is not set: canonify unless:
86064562Sgshapirodnl {daemon_flags} contains CC (do not canonify)
86164562Sgshapirodnl but add a trailing dot to qualified hostnames so other rules will work
86264562Sgshapirodnl should we do this for every hostname: even unqualified?
86364562SgshapiroR$* CC $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
86464562SgshapiroR$* CC $* $| $*			$: $3
86564562Sgshapiroifdef(`_FFR_NOCANONIFY_HEADERS', `dnl
86664562Sgshapiro# do not canonify header addresses
86764562SgshapiroR$* $| $* < @ $* $~P > $*	$: $&{addr_type} $| $2 < @ $3 $4 > $5
86864562SgshapiroR$* h $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
86990792SgshapiroR$* h $* $| $*			$: $3', `dnl')
87064562Sgshapiro# pass to name server to make hostname canonical
87164562SgshapiroR$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4')
87271345Sgshapirodnl remove {daemon_flags} for other cases
87371345SgshapiroR$* $| $*			$: $2
87471345Sgshapiro
87564562Sgshapiro# local host aliases and pseudo-domains are always canonical
87690792SgshapiroR$* < @ $=w > $*		$: $1 < @ $2 . > $3
87790792Sgshapiroifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
87890792Sgshapiro`R$* < @ $* $=M > $*		$: $1 < @ $2 $3 . > $4',
87990792Sgshapiro`R$* < @ $=M > $*		$: $1 < @ $2 . > $3')
88090792Sgshapiroifdef(`_VIRTUSER_TABLE_', `dnl
88138032Speterdnl virtual hosts are also canonical
88264562Sgshapiroifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
88364562Sgshapiro`R$* < @ $* $={VirtHost} > $* 	$: $1 < @ $2 $3 . > $4',
88464562Sgshapiro`R$* < @ $={VirtHost} > $* 	$: $1 < @ $2 . > $3')',
88538032Speter`dnl')
88638032Speterifdef(`_GENERICS_TABLE_', `dnl
88738032Speterdnl hosts for genericstable are also canonical
88838032Speterifdef(`_GENERICS_ENTIRE_DOMAIN_',
88938032Speter`R$* < @ $* $=G > $* 	$: $1 < @ $2 $3 . > $4',
89038032Speter`R$* < @ $=G > $* 	$: $1 < @ $2 . > $3')',
89164562Sgshapiro`dnl')
89264562Sgshapirodnl remove superfluous dots (maybe repeatedly) which may have been added
89364562Sgshapirodnl by one of the rules before
89464562SgshapiroR$* < @ $* . . > $*		$1 < @ $2 . > $3
89564562Sgshapiro
89664562Sgshapiro
89790792Sgshapiro##################################################
89890792Sgshapiro###  Ruleset 4 -- Final Output Post-rewriting  ###
89990792Sgshapiro##################################################
90090792SgshapiroSfinal=4
90190792Sgshapiro
90290792SgshapiroR$+ :; <@>		$@ $1 :				handle <list:;>
90364562SgshapiroR$* <@>			$@				handle <> and list:;
90464562Sgshapiro
90538032Speter# strip trailing dot off possibly canonical name
90638032SpeterR$* < @ $+ . > $*	$1 < @ $2 > $3
90738032Speter
90838032Speter# eliminate internal code
90938032SpeterR$* < @ *LOCAL* > $*	$1 < @ $j > $2
91038032Speter
91164562Sgshapiro# externalize local domain info
91238032SpeterR$* < $+ > $*		$1 $2 $3			defocus
91371345SgshapiroR@ $+ : @ $+ : $+	@ $1 , @ $2 : $3		<route-addr> canonical
91438032SpeterR@ $*			$@ @ $1				... and exit
91538032Speter
91638032Speterifdef(`_NO_UUCP_', `dnl',
91738032Speter`# UUCP must always be presented in old form
91838032SpeterR$+ @ $- . UUCP		$2!$1				u@h.UUCP => h!u')
91964562Sgshapiro
92038032Speterifdef(`_USE_DECNET_SYNTAX_',
92138032Speter`# put DECnet back in :: form
92238032SpeterR$+ @ $+ . DECNET	$2 :: $1			u@h.DECNET => h::u',
92338032Speter	`dnl')
92438032Speter# delete duplicate local names
92538032SpeterR$+ % $=w @ $=w		$1 @ $2				u%host@host => u@host
92638032Speter
92738032Speter
92838032Speter
92938032Speter##############################################################
93038032Speter###   Ruleset 97 -- recanonicalize and call ruleset zero   ###
93138032Speter###		   (used for recursive calls)		   ###
93238032Speter##############################################################
93338032Speter
93438032SpeterSRecurse=97
93538032SpeterR$*			$: $>canonify $1
93638032SpeterR$*			$@ $>parse $1
93738032Speter
93838032Speter
93938032Speter######################################
94038032Speter###   Ruleset 0 -- Parse Address   ###
94138032Speter######################################
94238032Speter
94338032SpeterSparse=0
94438032Speter
94564562SgshapiroR$*			$: $>Parse0 $1		initial parsing
94664562SgshapiroR<@>			$#_LOCAL_ $: <@>		special case error msgs
94764562SgshapiroR$*			$: $>ParseLocal $1	handle local hacks
94838032SpeterR$*			$: $>Parse1 $1		final parsing
94938032Speter
95038032Speter#
95138032Speter#  Parse0 -- do initial syntax checking and eliminate local addresses.
95238032Speter#	This should either return with the (possibly modified) input
95338032Speter#	or return with a #error mailer.  It should not return with a
95464562Sgshapiro#	#mailer other than the #error mailer.
95538032Speter#
95638032Speter
95738032SpeterSParse0
95864562SgshapiroR<@>			$@ <@>			special case error msgs
95938032SpeterR$* : $* ; <@>		$#error $@ 5.1.3 $: "_CODE553 List:; syntax illegal for recipient addresses"
96038032SpeterR@ <@ $* >		< @ $1 >		catch "@@host" bogosity
96138032SpeterR<@ $+>			$#error $@ 5.1.3 $: "_CODE553 User address required"
96238032SpeterR$+ <@>			$#error $@ 5.1.3 $: "_CODE553 Hostname required"
96338032SpeterR$*			$: <> $1
96438032Speterdnl allow tricks like [host1]:[host2]
96538032SpeterR<> $* < @ [ $* ] : $+ > $*	$1 < @ [ $2 ] : $3 > $4
96638032SpeterR<> $* < @ [ $* ] , $+ > $*	$1 < @ [ $2 ] , $3 > $4
96738032Speterdnl but no a@[b]c
96838032SpeterR<> $* < @ [ $* ] $+ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid address"
96938032SpeterR<> $* < @ [ $+ ] > $*		$1 < @ [ $2 ] > $3
97090792SgshapiroR<> $* <$* : $* > $*	$#error $@ 5.1.3 $: "_CODE553 Colon illegal in host name part"
97164562SgshapiroR<> $*			$1
97290792SgshapiroR$* < @ . $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
97390792SgshapiroR$* < @ $* .. $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
97438032Speterdnl no a@b@
97590792SgshapiroR$* < @ $* @ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid route address"
97690792Sgshapirodnl no a@b@c
97790792SgshapiroR$* @ $* < @ $* > $*	$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
97890792Sgshapirodnl comma only allowed before @; this check is not complete
97990792SgshapiroR$* , $~O $*		$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
98090792Sgshapiro
98190792Sgshapiroifdef(`_STRICT_RFC821_', `# more RFC 821 checks
98238032SpeterR$* . < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not end with a dot"
98390792SgshapiroR. $* < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not begin with a dot"
98490792Sgshapirodnl', `dnl')
98590792Sgshapiro
98690792Sgshapiro# now delete the local info -- note $=O to find characters that cause forwarding
98790792SgshapiroR$* < @ > $*		$@ $>Parse0 $>canonify $1	user@ => user
98890792SgshapiroR< @ $=w . > : $*	$@ $>Parse0 $>canonify $2	@here:... -> ...
98964562SgshapiroR$- < @ $=w . >		$: $(dequote $1 $) < @ $2 . >	dequote "foo"@here
99090792SgshapiroR< @ $+ >		$#error $@ 5.1.3 $: "_CODE553 User address required"
99138032SpeterR$* $=O $* < @ $=w . >	$@ $>Parse0 $>canonify $1 $2 $3	...@here -> ...
99290792SgshapiroR$- 			$: $(dequote $1 $) < @ *LOCAL* >	dequote "foo"
99390792SgshapiroR< @ *LOCAL* >		$#error $@ 5.1.3 $: "_CODE553 User address required"
99490792SgshapiroR$* $=O $* < @ *LOCAL* >
99590792Sgshapiro			$@ $>Parse0 $>canonify $1 $2 $3	...@*LOCAL* -> ...
99690792SgshapiroR$* < @ *LOCAL* >	$: $1
99738032Speter
99864562Sgshapiro#
99964562Sgshapiro#  Parse1 -- the bottom half of ruleset 0.
100038032Speter#
100190792Sgshapiro
100264562SgshapiroSParse1
100338032Speterifdef(`_LDAP_ROUTING_', `dnl
100490792Sgshapiro# handle LDAP routing for hosts in $={LDAPRoute}
100538032SpeterR$+ < @ $={LDAPRoute} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2> <>
100664562SgshapiroR$+ < @ $={LDAPRouteEquiv} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $M> <>',
100738032Speter`dnl')
100838032Speter
100938032Speterifdef(`_MAILER_smtp_',
101038032Speter`# handle numeric address spec
101138032Speterdnl there is no check whether this is really an IP number
101238032SpeterR$* < @ [ $+ ] > $*	$: $>ParseLocal $1 < @ [ $2 ] > $3	numeric internet spec
101338032SpeterR$* < @ [ $+ ] > $*	$1 < @ [ $2 ] : $S > $3		Add smart host to path
101464562SgshapiroR$* < @ [ $+ ] : > $*		$#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3	no smarthost: send
101564562SgshapiroR$* < @ [ $+ ] : $- : $*> $*	$#$3 $@ $4 $: $1 < @ [$2] > $5	smarthost with mailer
101690792SgshapiroR$* < @ [ $+ ] : $+ > $*	$#_SMTP_ $@ $3 $: $1 < @ [$2] > $4	smarthost without mailer',
101790792Sgshapiro	`dnl')
101864562Sgshapiro
101964562Sgshapiroifdef(`_VIRTUSER_TABLE_', `dnl
102038032Speter# handle virtual users
102138032Speterifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
102264562Sgshapirodnl this is not a documented option
102364562Sgshapirodnl it stops looping in virtusertable mapping if input and output
1024112810Sgshapirodnl are identical, i.e., if address A is mapped to A.
102590792Sgshapirodnl it does not deal with multi-level recursion
102664562Sgshapiro# handle full domains in RHS of virtusertable
102764562SgshapiroR$+ < @ $+ >			$: $(macro {RecipientAddress} $) $1 < @ $2 >
102838032SpeterR$+ < @ $+ > 			$: <?> $1 < @ $2 > $| $>final $1 < @ $2 >
102938032SpeterR<?> $+ $| $+			$: $1 $(macro {RecipientAddress} $@ $2 $)
103064562SgshapiroR<?> $+ $| $*			$: $1',
103138032Speter`dnl')
103290792SgshapiroR$+			$: <!> $1		Mark for lookup
103390792Sgshapirodnl input: <!> local<@domain>
103490792Sgshapiroifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
103590792Sgshapiro`R<!> $+ < @ $* $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >',
103690792Sgshapiro`R<!> $+ < @ $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >')
103790792Sgshapirodnl input: <result-of-lookup | @> local<@domain> | <!> local<@domain>
103890792SgshapiroR<!> $+ < @ $=w . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
103990792Sgshapirodnl if <@> local<@domain>: no match but try lookup
104090792Sgshapirodnl user+detail: try user++@domain if detail not empty
104190792SgshapiroR<@> $+ + $+ < @ $* . >
104290792Sgshapiro			$: < $(virtuser $1 + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
104364562Sgshapirodnl user+detail: try user+*@domain
104490792SgshapiroR<@> $+ + $* < @ $* . >
104564562Sgshapiro			$: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
104664562Sgshapirodnl user+detail: try user@domain
104764562SgshapiroR<@> $+ + $* < @ $* . >
104890792Sgshapiro			$: < $(virtuser $1 @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
104964562Sgshapirodnl try default entry: @domain
105090792Sgshapirodnl ++@domain
105190792SgshapiroR<@> $+ + $+ < @ $+ . >	$: < $(virtuser + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
105290792Sgshapirodnl +*@domain
105390792SgshapiroR<@> $+ + $* < @ $+ . >	$: < $(virtuser + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
105490792Sgshapirodnl @domain if +detail exists
105538032SpeterR<@> $+ + $* < @ $+ . >	$: < $(virtuser @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
105690792Sgshapirodnl without +detail (or no match)
105790792SgshapiroR<@> $+ < @ $+ . >	$: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
105838032Speterdnl no match
105990792SgshapiroR<@> $+			$: $1
106064562Sgshapirodnl remove mark
106190792SgshapiroR<!> $+			$: $1
106290792SgshapiroR< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
106364562SgshapiroR< error : $- $+ > $* 	$#error $@ $(dequote $1 $) $: $2
106490792Sgshapiroifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
106564562Sgshapiro# check virtuser input address against output address, if same, skip recursion
106698121SgshapiroR< $+ > $+ < @ $+ >				$: < $1 > $2 < @ $3 > $| $1
106798121Sgshapiro# it is the same: stop now
106898121SgshapiroR< $+ > $+ < @ $+ > $| $&{RecipientAddress}	$: $>ParseLocal $>Parse0 $>canonify $1
106938032SpeterR< $+ > $+ < @ $+ > $| $* 			$: < $1 > $2 < @ $3 >
107090792Sgshapirodnl', `dnl')
107138032Speterdnl this is not a documented option
107290792Sgshapirodnl it performs no looping at all for virtusertable
107364562Sgshapiroifdef(`_NO_VIRTUSER_RECURSION_',
107464562Sgshapiro`R< $+ > $+ < @ $+ >	$: $>ParseLocal $>Parse0 $>canonify $1',
107538032Speter`R< $+ > $+ < @ $+ >	$: $>Recurse $1')
107690792Sgshapirodnl', `dnl')
107790792Sgshapiro
107890792Sgshapiro# short circuit local delivery so forwarded email works
107990792Sgshapiroifdef(`_MAILER_usenet_', `dnl
108090792SgshapiroR$+ . USENET < @ $=w . >	$#usenet $@ usenet $: $1	handle usenet specially', `dnl')
108190792Sgshapiro
108290792Sgshapiro
108380785Sgshapiroifdef(`_STICKY_LOCAL_DOMAIN_',
108480785Sgshapiro`R$+ < @ $=w . >		$: < $H > $1 < @ $2 . >		first try hub
108577349SgshapiroR< $+ > $+ < $+ >	$>MailerToTriple < $1 > $2 < $3 >	yep ....
108677349Sgshapirodnl $H empty (but @$=w.)
108777349SgshapiroR< > $+ + $* < $+ >	$#_LOCAL_ $: $1 + $2		plussed name?
108877349SgshapiroR< > $+ < $+ >		$#_LOCAL_ $: @ $1			nope, local address',
108938032Speter`R$=L < @ $=w . >	$#_LOCAL_ $: @ $1			special local names
109038032SpeterR$+ < @ $=w . >		$#_LOCAL_ $: $1			regular local name')
109138032Speter
109264562Sgshapiroifdef(`_MAILER_TABLE_', `dnl
109366494Sgshapiro# not local -- try mailer table lookup
109466494SgshapiroR$* <@ $+ > $*		$: < $2 > $1 < @ $2 > $3	extract host name
109538032SpeterR< $+ . > $*		$: < $1 > $2			strip trailing dot
109638032SpeterR< $+ > $*		$: < $(mailertable $1 $) > $2	lookup
109764562Sgshapirodnl it is $~[ instead of $- to avoid matches on IPv6 addresses
109864562SgshapiroR< $~[ : $* > $* 	$>MailerToTriple < $1 : $2 > $3		check -- resolved?
109938032SpeterR< $+ > $*		$: $>Mailertable <$1> $2		try domain',
110038032Speter`dnl')
110164562Sgshapiroundivert(4)dnl UUCP rules from `MAILER(uucp)'
110238032Speter
110338032Speterifdef(`_NO_UUCP_', `dnl',
110464562Sgshapiro`# resolve remotely connected UUCP links (if any)
110538032Speterifdef(`_CLASS_V_',
110638032Speter`R$* < @ $=V . UUCP . > $*		$: $>MailerToTriple < $V > $1 <@$2.UUCP.> $3',
110738032Speter	`dnl')
110838032Speterifdef(`_CLASS_W_',
110964562Sgshapiro`R$* < @ $=W . UUCP . > $*		$: $>MailerToTriple < $W > $1 <@$2.UUCP.> $3',
111064562Sgshapiro	`dnl')
111164562Sgshapiroifdef(`_CLASS_X_',
111238032Speter`R$* < @ $=X . UUCP . > $*		$: $>MailerToTriple < $X > $1 <@$2.UUCP.> $3',
111364562Sgshapiro	`dnl')')
111438032Speter
111538032Speter# resolve fake top level domains by forwarding to other hosts
111638032Speterifdef(`BITNET_RELAY',
111738032Speter`R$*<@$+.BITNET.>$*	$: $>MailerToTriple < $B > $1 <@$2.BITNET.> $3	user@host.BITNET',
111864562Sgshapiro	`dnl')
111938032Speterifdef(`DECNET_RELAY',
112038032Speter`R$*<@$+.DECNET.>$*	$: $>MailerToTriple < $C > $1 <@$2.DECNET.> $3	user@host.DECNET',
112164562Sgshapiro	`dnl')
112238032Speterifdef(`_MAILER_pop_',
112338032Speter`R$+ < @ POP. >		$#pop $: $1			user@POP',
112464562Sgshapiro	`dnl')
112538032Speterifdef(`_MAILER_fax_',
112638032Speter`R$+ < @ $+ .FAX. >	$#fax $@ $2 $: $1		user@host.FAX',
112738032Speter`ifdef(`FAX_RELAY',
112838032Speter`R$*<@$+.FAX.>$*		$: $>MailerToTriple < $F > $1 <@$2.FAX.> $3	user@host.FAX',
112964562Sgshapiro	`dnl')')
113038032Speter
113138032Speterifdef(`UUCP_RELAY',
113264562Sgshapiro`# forward non-local UUCP traffic to our UUCP relay
113338032SpeterR$*<@$*.UUCP.>$*		$: $>MailerToTriple < $Y > $1 <@$2.UUCP.> $3	uucp mail',
113438032Speter`ifdef(`_MAILER_uucp_',
113538032Speter`# forward other UUCP traffic straight to UUCP
113638032SpeterR$* < @ $+ .UUCP. > $*		$#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3	user@host.UUCP',
113738032Speter	`dnl')')
113838032Speterifdef(`_MAILER_usenet_', `
113938032Speter# addresses sent to net.group.USENET will get forwarded to a newsgroup
114064562SgshapiroR$+ . USENET		$#usenet $@ usenet $: $1',
114138032Speter	`dnl')
114238032Speter
114338032Speterifdef(`_LOCAL_RULES_',
114438032Speter`# figure out what should stay in our local mail system
114564562Sgshapiroundivert(1)', `dnl')
114638032Speter
114738032Speter# pass names that still have a host to a smarthost (if defined)
114838032SpeterR$* < @ $* > $*		$: $>MailerToTriple < $S > $1 < @ $2 > $3	glue on smarthost name
114938032Speter
115038032Speter# deal with other remote names
115138032Speterifdef(`_MAILER_smtp_',
115264562Sgshapiro`R$* < @$* > $*		$#_SMTP_ $@ $2 $: $1 < @ $2 > $3	user@host.domain',
115338032Speter`R$* < @$* > $*		$#error $@ 5.1.2 $: "_CODE553 Unrecognized host name " $2')
115438032Speter
115538032Speter# handle locally delivered names
115638032SpeterR$=L			$#_LOCAL_ $: @ $1		special local names
115738032SpeterR$+			$#_LOCAL_ $: $1			regular local names
115838032Speter
115938032Speter###########################################################################
116064562Sgshapiro###   Ruleset 5 -- special rewriting after aliases have been expanded   ###
116138032Speter###########################################################################
116238032Speter
116338032SpeterSLocal_localaddr
116464562SgshapiroSlocaladdr=5
116590792SgshapiroR$+			$: $1 $| $>"Local_localaddr" $1
116638032SpeterR$+ $| $#ok		$@ $1			no change
116738032SpeterR$+ $| $#$*		$#$2
116864562SgshapiroR$+ $| $*		$: $1
116938032Speter
117038032Speterifdef(`_PRESERVE_LUSER_HOST_', `dnl
117138032Speter# Preserve rcpt_host in {Host}
117238032SpeterR$+			$: $1 $| $&h $| $&{Host}	check h and {Host}
117338032SpeterR$+ $| $|		$: $(macro {Host} $@ $) $1	no h or {Host}
117438032SpeterR$+ $| $| $+		$: $1			h not set, {Host} set
117564562SgshapiroR$+ $| +$* $| $*	$: $1			h is +detail, {Host} set
117664562SgshapiroR$+ $| $+ $| $*		$: $(macro {Host} $@ @$2 $) $1	set {Host} to h
117764562Sgshapiro')dnl
117890792Sgshapiro
117964562Sgshapiroifdef(`_FFR_5_', `dnl
118064562Sgshapiro# Preserve host in a macro
118138032SpeterR$+			$: $(macro {LocalAddrHost} $) $1
118290792SgshapiroR$+ @ $+		$: $(macro {LocalAddrHost} $@ @ $2 $) $1')
118390792Sgshapiro
118490792Sgshapiroifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `dnl
118590792Sgshapiro# deal with plussed users so aliases work nicely
118690792SgshapiroR$+ + *			$#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
118790792SgshapiroR$+ + $*		$#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
118895154Sgshapiro')
118990792Sgshapiro# prepend an empty "forward host" on the front
119090792SgshapiroR$+			$: <> $1
119190792Sgshapiro
119290792Sgshapiroifdef(`LUSER_RELAY', `dnl
119366494Sgshapiro# send unrecognized local users to a relay host
119466494Sgshapiroifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
119566494SgshapiroR< > $+ + $*		$: < ? $L > <+ $2> $(user $1 $)	look up user+
119666494SgshapiroR< > $+			$: < ? $L > < > $(user $1 $)	look up user
119790792SgshapiroR< ? $* > < $* > $+ <>	$: < > $3 $2			found; strip $L
119838032SpeterR< ? $* > < $* > $+	$: < $1 > $3 $2			not found', `
119966494SgshapiroR< > $+ 		$: < $L > $(user $1 $)		look up user
120066494SgshapiroR< $* > $+ <>		$: < > $2			found; strip $L')
120166494Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
120238032SpeterR< $+ > $+		$: < $1 > $2 $&{Host}')
120338032Speterdnl')
120438032Speter
120538032Speterifdef(`MAIL_HUB', `dnl
120638032SpeterR< > $+			$: < $H > $1			try hub', `dnl')
120790792Sgshapiroifdef(`LOCAL_RELAY', `dnl
120866494SgshapiroR< > $+			$: < $R > $1			try relay', `dnl')
120966494Sgshapiroifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
121066494SgshapiroR< > $+			$@ $1', `dnl
121166494SgshapiroR< > $+			$: < > < $1 <> $&h >		nope, restore +detail
121264562Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
121390792SgshapiroR< > < $+ @ $+ <> + $* >	$: < > < $1 + $3 @ $2 >	check whether +detail')
121490792SgshapiroR< > < $+ <> + $* >	$: < > < $1 + $2 >		check whether +detail
121590792SgshapiroR< > < $+ <> $* >	$: < > < $1 >			else discard
121690792SgshapiroR< > < $+ + $* > $*	   < > < $1 > + $2 $3		find the user part
121738032SpeterR< > < $+ > + $*	$#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')		strip the extra +
121890792SgshapiroR< > < $+ >		$@ $1				no +detail
121990792SgshapiroR$+			$: $1 <> $&h			add +detail back in
122090792Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
122190792SgshapiroR$+ @ $+ <> + $*	$: $1 + $3 @ $2			check whether +detail')
122290792SgshapiroR$+ <> + $*		$: $1 + $2			check whether +detail
122390792SgshapiroR$+ <> $*		$: $1				else discard')
122464562SgshapiroR< local : $* > $*	$: $>MailerToTriple < local : $1 > $2	no host extension
122590792SgshapiroR< error : $* > $*	$: $>MailerToTriple < error : $1 > $2	no host extension
122690792Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
122764562Sgshapirodnl it is $~[ instead of $- to avoid matches on IPv6 addresses
122864562SgshapiroR< $~[ : $+ > $+ @ $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $4 >')
122938032SpeterR< $~[ : $+ > $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $2 >
123066494Sgshapiroifdef(`_PRESERVE_LUSER_HOST_', `dnl
123138032SpeterR< $+ > $+ @ $+		$@ $>MailerToTriple < $1 > $2 < @ $3 >')
123243730SpeterR< $+ > $+		$@ $>MailerToTriple < $1 > $2 < @ $1 >
123390792Sgshapiro
123490792Sgshapiroifdef(`_MAILER_TABLE_', `dnl
123543730Speterifdef(`_LDAP_ROUTING_', `dnl
123666494Sgshapiro###################################################################
123764562Sgshapiro###  Ruleset LDAPMailertable -- mailertable lookup for LDAP     ###
123864562Sgshapirodnl input: <Domain> FullAddress
123990792Sgshapiro###################################################################
124090792Sgshapiro
124190792SgshapiroSLDAPMailertable
124290792SgshapiroR< $+ > $*		$: < $(mailertable $1 $) > $2		lookup
124390792SgshapiroR< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		check resolved?
124490792SgshapiroR< $+ > $*		$: < $1 > $>Mailertable <$1> $2		try domain
124564562SgshapiroR< $+ > $#$*		$#$2					found
124638032SpeterR< $+ > $*		$#_RELAY_ $@ $1 $: $2			not found, direct relay',
124764562Sgshapiro`dnl')
124890792Sgshapiro
124938032Speter###################################################################
125090792Sgshapiro###  Ruleset 90 -- try domain part of mailertable entry 	###
125190792Sgshapirodnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress
125290792Sgshapiro###################################################################
125390792Sgshapiro
125490792SgshapiroSMailertable=90
125590792Sgshapirodnl shift and check
125690792Sgshapirodnl %2 is not documented in cf/README
125790792SgshapiroR$* <$- . $+ > $*	$: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
125890792Sgshapirodnl it is $~[ instead of $- to avoid matches on IPv6 addresses
125990792SgshapiroR$* <$~[ : $* > $*	$>MailerToTriple < $2 : $3 > $4		check -- resolved?
126090792SgshapiroR$* < . $+ > $* 	$@ $>Mailertable $1 . <$2> $3		no -- strip & try again
126190792Sgshapirodnl is $2 always empty?
126290792SgshapiroR$* < $* > $*		$: < $(mailertable . $@ $1$2 $) > $3	try "."
126338032SpeterR< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		"." found?
126464562Sgshapirodnl return full address
126538032SpeterR< $* > $*		$@ $2				no mailertable match',
126638032Speter`dnl')
126764562Sgshapiro
126864562Sgshapiro###################################################################
126964562Sgshapiro###  Ruleset 95 -- canonify mailer:[user@]host syntax to triple	###
127038032Speterdnl input: in general: <[mailer:]host> lp<@domain>rest
127164562Sgshapirodnl	<> address				-> address
127264562Sgshapirodnl	<error:d.s.n:text>			-> error
127364562Sgshapirodnl	<error:text>				-> error
127464562Sgshapirodnl	<mailer:user@host> lp<@domain>rest	-> mailer host user
127538032Speterdnl	<mailer:host> address			-> mailer host address
127664562Sgshapirodnl	<localdomain> address			-> address
127764562Sgshapirodnl	<host> address				-> relay host address
127838032Speter###################################################################
127938032Speter
128038032SpeterSMailerToTriple=95
128138032SpeterR< > $*				$@ $1			strip off null relay
128238032SpeterR< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
128364562SgshapiroR< error : $- $+ > $*		$#error $@ $(dequote $1 $) $: $2
128464562SgshapiroR< local : $* > $*		$>CanonLocal < $1 > $2
128564562Sgshapirodnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1286120256SgshapiroR< $~[ : $+ @ $+ > $*<$*>$*	$# $1 $@ $3 $: $2<@$3>	use literal user
128764562SgshapiroR< $~[ : $+ > $*		$# $1 $@ $2 $: $3	try qualified mailer
128864562SgshapiroR< $=w > $*			$@ $2			delete local host
128964562SgshapiroR< $+ > $*			$#_RELAY_ $@ $1 $: $2	use unqualified mailer
129064562Sgshapiro
129164562Sgshapiro###################################################################
129238032Speter###  Ruleset CanonLocal -- canonify local: syntax		###
129338032Speterdnl input: <user> address
129464562Sgshapirodnl <x> <@host> : rest			-> Recurse rest
129538032Speterdnl <x> p1 $=O p2 <@host>		-> Recurse p1 $=O p2
129664562Sgshapirodnl <> user <@host> rest		-> local user@host user
1297120256Sgshapirodnl <> user				-> local user user
1298120256Sgshapirodnl <user@host> lp <@domain> rest	-> <user> lp <@host> [cont]
129938032Speterdnl <user> lp <@host> rest		-> local lp@host user
130090792Sgshapirodnl <user> lp				-> local lp user
130190792Sgshapiro###################################################################
130290792Sgshapiro
130338032SpeterSCanonLocal
130438032Speter# strip local host from routed addresses
130538032SpeterR< $* > < @ $+ > : $+		$@ $>Recurse $3
130638032SpeterR< $* > $+ $=O $+ < @ $+ >	$@ $>Recurse $2 $3 $4
130738032Speter
130864562Sgshapiro# strip trailing dot from any host name that may appear
130964562SgshapiroR< $* > $* < @ $* . >		$: < $1 > $2 < @ $3 >
131064562Sgshapiro
131164562Sgshapiro# handle local: syntax -- use old user, either with or without host
131264562SgshapiroR< > $* < @ $* > $*		$#_LOCAL_ $@ $1@$2 $: $1
131364562SgshapiroR< > $+				$#_LOCAL_ $@ $1    $: $1
131464562Sgshapiro
131564562Sgshapiro# handle local:user@host syntax -- ignore host part
131638032SpeterR< $+ @ $+ > $* < @ $* >	$: < $1 > $3 < @ $4 >
131738032Speter
131838032Speter# handle local:user syntax
131943730SpeterR< $+ > $* <@ $* > $*		$#_LOCAL_ $@ $2@$3 $: $1
132064562SgshapiroR< $+ > $* 			$#_LOCAL_ $@ $2    $: $1
132164562Sgshapiro
132243730Speter###################################################################
132338032Speter###  Ruleset 93 -- convert header names to masqueraded form	###
132438032Speter###################################################################
132538032Speter
132638032SpeterSMasqHdr=93
132738032Speter
132838032Speterifdef(`_GENERICS_TABLE_', `dnl
132938032Speter# handle generics database
133038032Speterifdef(`_GENERICS_ENTIRE_DOMAIN_',
133138032Speterdnl if generics should be applied add a @ as mark
133238032Speter`R$+ < @ $* $=G . >	$: < $1@$2$3 > $1 < @ $2$3 . > @	mark',
133338032Speter`R$+ < @ $=G . >	$: < $1@$2 > $1 < @ $2 . > @	mark')
133438032SpeterR$+ < @ *LOCAL* >	$: < $1@$j > $1 < @ *LOCAL* > @	mark
133538032Speterdnl workspace: either user<@domain> or <user@domain> user <@domain> @
133638032Speterdnl ignore the first case for now
133738032Speterdnl if it has the mark lookup full address
133838032Speterdnl broken: %1 is full address not just detail
133938032SpeterR< $+ > $+ < $* > @	$: < $(generics $1 $: @ $1 $) > $2 < $3 >
134038032Speterdnl workspace: ... or <match|@user@domain> user <@domain>
134164562Sgshapirodnl no match, try user+detail@domain
134238032SpeterR<@$+ + $* @ $+> $+ < @ $+ >
134364562Sgshapiro		$: < $(generics $1+*@$3 $@ $2 $:@$1 + $2@$3 $) >  $4 < @ $5 >
134438032SpeterR<@$+ + $* @ $+> $+ < @ $+ >
134538032Speter		$: < $(generics $1@$3 $: $) > $4 < @ $5 >
134664562Sgshapirodnl no match, remove mark
134738032SpeterR<@$+ > $+ < @ $+ >	$: < > $2 < @ $3 >
134838032Speterdnl no match, try @domain for exceptions
134938032SpeterR< > $+ < @ $+ . >	$: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . >
135064562Sgshapirodnl workspace: ... or <match> user <@domain>
135164562Sgshapirodnl no match, try local part
135264562SgshapiroR< > $+ < @ $+ > 	$: < $(generics $1 $: $) > $1 < @ $2 >
135390792SgshapiroR< > $+ + $* < @ $+ > 	$: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 >
135464562SgshapiroR< > $+ + $* < @ $+ > 	$: < $(generics $1 $: $) > $1 + $2 < @ $3 >
135564562SgshapiroR< $* @ $* > $* < $* >	$@ $>canonify $1 @ $2		found qualified
135664562SgshapiroR< $+ > $* < $* >	$: $>canonify $1 @ *LOCAL*	found unqualified
135764562SgshapiroR< > $*			$: $1				not found',
135864562Sgshapiro`dnl')
135964562Sgshapiro
136064562Sgshapiro# do not masquerade anything in class N
136164562SgshapiroR$* < @ $* $=N . >	$@ $1 < @ $2 $3 . >
136264562Sgshapiro
136364562Sgshapiroifdef(`MASQUERADE_NAME', `dnl
136464562Sgshapiro# special case the users that should be exposed
136564562SgshapiroR$=E < @ *LOCAL* >	$@ $1 < @ $j . >		leave exposed
136664562Sgshapiroifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
136738032Speter`R$=E < @ $* $=M . >	$@ $1 < @ $2 $3 . >',
136864562Sgshapiro`R$=E < @ $=M . >	$@ $1 < @ $2 . >')
136964562Sgshapiroifdef(`_LIMITED_MASQUERADE_', `dnl',
137064562Sgshapiro`R$=E < @ $=w . >	$@ $1 < @ $2 . >')
137164562Sgshapiro
137238032Speter# handle domain-specific masquerading
137338032Speterifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
137438032Speter`R$* < @ $* $=M . > $*	$: $1 < @ $2 $3 . @ $M > $4	convert masqueraded doms',
137564562Sgshapiro`R$* < @ $=M . > $*	$: $1 < @ $2 . @ $M > $3	convert masqueraded doms')
137664562Sgshapiroifdef(`_LIMITED_MASQUERADE_', `dnl',
137764562Sgshapiro`R$* < @ $=w . > $*	$: $1 < @ $2 . @ $M > $3')
137890792SgshapiroR$* < @ *LOCAL* > $*	$: $1 < @ $j . @ $M > $2
137938032SpeterR$* < @ $+ @ > $*	$: $1 < @ $2 > $3		$M is null
138038032SpeterR$* < @ $+ @ $+ > $*	$: $1 < @ $3 . > $4		$M is not null
138138032Speterdnl', `dnl no masquerading
138238032Speterdnl just fix *LOCAL* leftovers
138338032SpeterR$* < @ *LOCAL* >	$@ $1 < @ $j . >')
138438032Speter
138538032Speter###################################################################
138638032Speter###  Ruleset 94 -- convert envelope names to masqueraded form	###
138738032Speter###################################################################
138838032Speter
138938032SpeterSMasqEnv=94
139038032Speterifdef(`_MASQUERADE_ENVELOPE_',
139138032Speter`R$+			$@ $>MasqHdr $1',
139238032Speter`R$* < @ *LOCAL* > $*	$: $1 < @ $j . > $2')
139338032Speter
139438032Speter###################################################################
139538032Speter###  Ruleset 98 -- local part of ruleset zero (can be null)	###
139690792Sgshapiro###################################################################
139790792Sgshapiro
139890792SgshapiroSParseLocal=98
139938032Speterundivert(3)dnl LOCAL_RULE_0
140038032Speter
140138032Speterifdef(`_LDAP_ROUTING_', `dnl
140238032Speter######################################################################
140338032Speter###  LDAPExpand: Expand address using LDAP routing
140464562Sgshapiro###
140538032Speter###	Parameters:
140664562Sgshapiro###		<$1> -- parsed address (user < @ domain . >) (pass through)
140738032Speter###		<$2> -- RFC822 address (user @ domain) (used for lookup)
140838032Speter###		<$3> -- +detail information
140938032Speter###
141038032Speter###	Returns:
141138032Speter###		Mailer triplet ($#mailer $@ host $: address)
141238032Speter###		Parsed address (user < @ domain . >)
141364562Sgshapiro######################################################################
141464562Sgshapiro
141538032SpeterSLDAPExpand
141664562Sgshapiro# do the LDAP lookups
141790792SgshapiroR<$+><$+><$*>	$: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2> <$3>
141890792Sgshapiro
141990792Sgshapiro# if mailRoutingAddress and local or non-existant mailHost,
142090792Sgshapiro# return the new mailRoutingAddress
142190792Sgshapiroifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
142290792SgshapiroR<$+@$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $6 @ $2
142390792SgshapiroR<$+@$+> <> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $5 @ $2')
142490792SgshapiroR<$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1
142590792SgshapiroR<$+> <> <$+> <$+> <$*>		$@ $>Parse0 $>canonify $1
142690792Sgshapiro
142790792Sgshapiro# if mailRoutingAddress and non-local mailHost,
142890792Sgshapiro# relay to mailHost with new mailRoutingAddress
142990792Sgshapiroifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
143064562Sgshapiroifdef(`_MAILER_TABLE_', `dnl
143164562Sgshapiro# check mailertable for host, relay from there
143290792SgshapiroR<$+@$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$3> $>canonify $1 $6 @ $2',
143364562Sgshapiro`R<$+@$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $3 $: $>canonify $1 $6 @ $2')')
143494334Sgshapiroifdef(`_MAILER_TABLE_', `dnl
1435102528Sgshapiro# check mailertable for host, relay from there
1436102528SgshapiroR<$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$2> $>canonify $1',
143794334Sgshapiro`R<$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $2 $: $>canonify $1')
143864562Sgshapiro
143964562Sgshapiro# if no mailRoutingAddress and local mailHost,
144090792Sgshapiro# return original address
144190792SgshapiroR<> <$=w> <$+> <$+> <$*>	$@ $2
144290792Sgshapiro
144390792Sgshapiro# if no mailRoutingAddress and non-local mailHost,
144490792Sgshapiro# relay to mailHost with original address
144564562Sgshapiroifdef(`_MAILER_TABLE_', `dnl
144698121Sgshapiro# check mailertable for host, relay from there
144764562SgshapiroR<> <$+> <$+> <$+> <$*>		$>LDAPMailertable <$1> $2',
144864562Sgshapiro`R<> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $1 $: $2')
144990792Sgshapiro
145090792Sgshapiroifdef(`_LDAP_ROUTE_DETAIL_',
145190792Sgshapiro`# if no mailRoutingAddress and no mailHost,
145290792Sgshapiro# try without +detail
145390792SgshapiroR<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <$2 @ $4> <+$3>')dnl
145490792Sgshapiro
145590792Sgshapiro# if still no mailRoutingAddress and no mailHost,
145690792Sgshapiro# try @domain
145790792Sgshapiroifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
145864562SgshapiroR<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <@ $4> <+$3>')
145964562SgshapiroR<> <> <$+> <$+ @ $+> <$*>	$@ $>LDAPExpand <$1> <@ $3> <$4>
146064562Sgshapiro
146190792Sgshapiro# if no mailRoutingAddress and no mailHost and this was a domain attempt,
146264562Sgshapiroifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl
146398121Sgshapiro# user does not exist
146464562SgshapiroR<> <> <$+> <@ $+> <$*>		$: <?> < $&{addr_type} > < $1 >
146564562Sgshapiro# only give error for envelope recipient
146690792SgshapiroR<?> <e r> <$+>			$#error $@ nouser $: "550 User unknown"
146790792SgshapiroR<?> <$*> <$+>			$@ $2',
146890792Sgshapiro`dnl
146990792Sgshapiro# return the original address
147064562SgshapiroR<> <> <$+> <@ $+> <$*>		$@ $1')',
147190792Sgshapiro`dnl')
147290792Sgshapiro
147390792Sgshapiroifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode.
147490792Sgshapiro')')
147590792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
147690792Sgshapiro######################################################################
147764562Sgshapiro###  D: LookUpDomain -- search for domain in access database
147890792Sgshapiro###
147990792Sgshapiro###	Parameters:
148090792Sgshapiro###		<$1> -- key (domain name)
148164562Sgshapiro###		<$2> -- default (what to return if not found in db)
148264562Sgshapirodnl			must not be empty
148364562Sgshapiro###		<$3> -- mark (must be <(!|+) single-token>)
148464562Sgshapiro###			! does lookup only with tag
148590792Sgshapiro###			+ does lookup with and without tag
148690792Sgshapiro###		<$4> -- passthru (additional data passed unchanged through)
148790792Sgshapirodnl returns:		<default> <passthru>
148890792Sgshapirodnl 			<result> <passthru>
148964562Sgshapiro######################################################################
149064562Sgshapiro
149190792SgshapiroSD
149264562Sgshapirodnl workspace <key> <default> <passthru> <mark>
149364562Sgshapirodnl lookup with tag (in front, no delimiter here)
149464562Sgshapirodnl    2    3  4    5
149564562SgshapiroR<$*> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
149690792Sgshapirodnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark>
149738032Speterdnl lookup without tag?
149890792Sgshapirodnl   1    2      3    4
149938032SpeterR<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
150038032Speterifdef(`_LOOKUPDOTDOMAIN_', `dnl omit first component: lookup .rest
150138032Speterdnl XXX apply this also to IP addresses?
150238032Speterdnl currently it works the wrong way round for [1.2.3.4]
150364562Sgshapirodnl   1  2    3    4  5    6
150490792SgshapiroR<?> <$+.$+> <$+> <$- $-> <$*>	$: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4 $5> <$6>
150564562Sgshapirodnl   1  2    3      4    5
150664562SgshapiroR<?> <$+.$+> <$+> <+ $-> <$*>	$: < $(access .$2 $: ? $) > <$1.$2> <$3> <+ $4> <$5>', `dnl')
150790792Sgshapiroifdef(`_ACCESS_SKIP_', `dnl
150864562Sgshapirodnl found SKIP: return <default> and <passthru>
150964562Sgshapirodnl      1    2    3  4    5
151038032SpeterR<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
151138032Speterdnl not found: IPv4 net (no check is done whether it is an IP number!)
151290792Sgshapirodnl    1  2     3    4  5    6
151364562SgshapiroR<?> <[$+.$-]> <$+> <$- $-> <$*>	$@ $>D <[$1]> <$3> <$4 $5> <$6>
151464562Sgshapiroifdef(`NO_NETINET6', `dnl',
151590792Sgshapiro`dnl not found: IPv6 net
151690792Sgshapirodnl (could be merged with previous rule if we have a class containing .:)
151764562Sgshapirodnl    1   2     3    4  5    6
151864562SgshapiroR<?> <[$+::$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>
151990792SgshapiroR<?> <[$+:$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>')
152090792Sgshapirodnl not found, but subdomain: try again
152190792Sgshapirodnl   1  2    3    4  5    6
152290792SgshapiroR<?> <$+.$+> <$+> <$- $-> <$*>	$@ $>D <$2> <$3> <$4 $5> <$6>
152390792Sgshapiroifdef(`_FFR_LOOKUPTAG_', `dnl lookup Tag:
152490792Sgshapirodnl   1    2      3    4
152590792SgshapiroR<?> <$+> <$+> <! $-> <$*>	$: < $(access $3`'_TAG_DELIM_ $: ? $) > <$1> <$2> <! $3> <$4>', `dnl')
152690792Sgshapirodnl not found, no subdomain: return <default> and <passthru>
152790792Sgshapirodnl   1    2    3  4    5
152890792SgshapiroR<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
152990792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
153090792Sgshapirodnl            2    3    4  5    6
153190792SgshapiroR<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
153290792Sgshapirodnl return <result of lookup> and <passthru>
153390792Sgshapirodnl    2    3    4  5    6
153490792SgshapiroR<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
153590792Sgshapiro
153690792Sgshapiro######################################################################
153790792Sgshapiro###  A: LookUpAddress -- search for host address in access database
153890792Sgshapiro###
153990792Sgshapiro###	Parameters:
154090792Sgshapiro###		<$1> -- key (dot quadded host address)
154164562Sgshapiro###		<$2> -- default (what to return if not found in db)
154290792Sgshapirodnl			must not be empty
154390792Sgshapiro###		<$3> -- mark (must be <(!|+) single-token>)
154490792Sgshapiro###			! does lookup only with tag
154590792Sgshapiro###			+ does lookup with and without tag
154690792Sgshapiro###		<$4> -- passthru (additional data passed through)
154790792Sgshapirodnl	returns:	<default> <passthru>
154890792Sgshapirodnl			<result> <passthru>
154990792Sgshapiro######################################################################
155090792Sgshapiro
155190792SgshapiroSA
155290792Sgshapirodnl lookup with tag
155390792Sgshapirodnl    2    3  4    5
155490792SgshapiroR<$+> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
155590792Sgshapirodnl lookup without tag
155638032Speterdnl   1    2      3    4
155738032SpeterR<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
155890792Sgshapirodnl workspace <result-of-lookup|?> <key> <default> <mark> <passthru>
155938032Speterifdef(`_ACCESS_SKIP_', `dnl
156038032Speterdnl found SKIP: return <default> and <passthru>
156138032Speterdnl      1    2    3  4    5
156238032SpeterR<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
156364562Sgshapiroifdef(`NO_NETINET6', `dnl',
156490792Sgshapiro`dnl no match; IPv6: remove last part
156564562Sgshapirodnl   1   2    3    4  5    6
156664562SgshapiroR<?> <$+::$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
156790792SgshapiroR<?> <$+:$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>')
156864562Sgshapirodnl no match; IPv4: remove last part
156964562Sgshapirodnl   1  2    3    4  5    6
157038032SpeterR<?> <$+.$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
157138032Speterdnl no match: return default
157290792Sgshapirodnl   1    2    3  4    5
157364562SgshapiroR<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
157490792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
157590792Sgshapirodnl            2    3    4  5    6
157664562SgshapiroR<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
157790792Sgshapirodnl match: return result
157890792Sgshapirodnl    2    3    4  5    6
157990792SgshapiroR<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
158090792Sgshapirodnl endif _ACCESS_TABLE_
158190792Sgshapirodivert(0)
158290792Sgshapiro######################################################################
158390792Sgshapiro###  CanonAddr --	Convert an address into a standard form for
158490792Sgshapiro###			relay checking.  Route address syntax is
158590792Sgshapiro###			crudely converted into a %-hack address.
158690792Sgshapiro###
158790792Sgshapiro###	Parameters:
158890792Sgshapiro###		$1 -- full recipient address
158964562Sgshapiro###
159090792Sgshapiro###	Returns:
159190792Sgshapiro###		parsed address, not in source route form
159264562Sgshapirodnl		user%host%host<@domain>
159390792Sgshapirodnl		host!user<@domain>
159490792Sgshapiro######################################################################
159590792Sgshapiro
159690792SgshapiroSCanonAddr
159790792SgshapiroR$*			$: $>Parse0 $>canonify $1	make domain canonical
159864562Sgshapiroifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
159990792SgshapiroR< @ $+ > : $* @ $*	< @ $1 > : $2 % $3	change @ to % in src route
160090792SgshapiroR$* < @ $+ > : $* : $*	$3 $1 < @ $2 > : $4	change to % hack.
160190792SgshapiroR$* < @ $+ > : $*	$3 $1 < @ $2 >
160290792Sgshapirodnl')
160338032Speter
160442575Speter######################################################################
160542575Speter###  ParseRecipient --	Strip off hosts in $=R as well as possibly
160642575Speter###			$* $=m or the access database.
160742575Speter###			Check user portion for host separators.
160842575Speter###
160942575Speter###	Parameters:
161042575Speter###		$1 -- full recipient address
161142575Speter###
161242575Speter###	Returns:
161364562Sgshapiro###		parsed, non-local-relaying address
161464562Sgshapiro######################################################################
161542575Speter
161642575SpeterSParseRecipient
161742575Speterdnl mark and canonify address
161864562SgshapiroR$*				$: <?> $>CanonAddr $1
161964562Sgshapirodnl workspace: <?> localpart<@domain[.]>
162042575SpeterR<?> $* < @ $* . >		<?> $1 < @ $2 >			strip trailing dots
162142575Speterdnl workspace: <?> localpart<@domain>
162242575SpeterR<?> $- < @ $* >		$: <?> $(dequote $1 $) < @ $2 >	dequote local part
162364562Sgshapiro
162442575Speter# if no $=O character, no host in the user portion, we are done
162542575SpeterR<?> $* $=O $* < @ $* >		$: <NO> $1 $2 $3 < @ $4>
162638032Speterdnl no $=O in localpart: return
162738032SpeterR<?> $*				$@ $1
162838032Speter
162938032Speterdnl workspace: <NO> localpart<@domain>, where localpart contains $=O
163038032Speterdnl mark everything which has an "authorized" domain with <RELAY>
163138032Speterifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
163238032Speter# if we relay, check username portion for user%host so host can be checked also
163338032SpeterR<NO> $* < @ $* $=m >		$: <RELAY> $1 < @ $2 $3 >', `dnl')
163438032Speterdnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O
163538032Speterdnl if mark is <NO> then change it to <RELAY> if domain is "authorized"
163638032Speter
163738032Speterdnl what if access map returns something else than RELAY?
163864562Sgshapirodnl we are only interested in RELAY entries...
163942575Speterdnl other To: entries: blacklist recipient; generic entries?
164064562Sgshapirodnl if it is an error we probably do not want to relay anyway
164142575Speterifdef(`_RELAY_HOSTS_ONLY_',
164264562Sgshapiro`R<NO> $* < @ $=R >		$: <RELAY> $1 < @ $2 >
164342575Speterifdef(`_ACCESS_TABLE_', `dnl
164438032SpeterR<NO> $* < @ $+ >		$: <$(access To:$2 $: NO $)> $1 < @ $2 >
164538032SpeterR<NO> $* < @ $+ >		$: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')',
164642575Speter`R<NO> $* < @ $* $=R >		$: <RELAY> $1 < @ $2 $3 >
164764562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
164842575SpeterR<NO> $* < @ $+ >		$: $>D <$2> <NO> <+ To> <$1 < @ $2 >>
164938032SpeterR<$+> <$+>			$: <$1> $2',`dnl')')
165090792Sgshapiro
165164562Sgshapiro
165238032Speterifdef(`_RELAY_MX_SERVED_', `dnl
165338032Speterdnl do "we" ($=w) act as backup MX server for the destination domain?
165442575SpeterR<NO> $* < @ $+ >		$: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > >
165564562SgshapiroR<MX> < : $* <TEMP> : > $*	$#TEMP $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1
165664562Sgshapirodnl yes: mark it as <RELAY>
165790792SgshapiroR<MX> < $* : $=w. : $* > < $+ >	$: <RELAY> $4
165890792Sgshapirodnl no: put old <NO> mark back
165990792SgshapiroR<MX> < : $* : > < $+ >		$: <NO> $2', `dnl')
166090792Sgshapiro
166190792Sgshapirodnl do we relay to this recipient domain?
166238032SpeterR<RELAY> $* < @ $* >		$@ $>ParseRecipient $1
166342575Speterdnl something else
166464562SgshapiroR<$+> $*			$@ $2
166564562Sgshapiro
166642575Speter
166742575Speter######################################################################
166864562Sgshapiro###  check_relay -- check hostname/address on SMTP startup
166990792Sgshapiro######################################################################
167042575Speter
167138032SpeterSLocal_check_relay
167264562SgshapiroScheck`'_U_`'relay
167390792SgshapiroR$*			$: $1 $| $>"Local_check_relay" $1
167490792SgshapiroR$* $| $* $| $#$*	$#$3
167590792SgshapiroR$* $| $* $| $*		$@ $>"Basic_check_relay" $1 $| $2
167690792Sgshapiro
167790792SgshapiroSBasic_check_relay
167890792Sgshapiro# check for deferred delivery mode
167990792SgshapiroR$*			$: < ${deliveryMode} > $1
168090792SgshapiroR< d > $*		$@ deferred
168190792SgshapiroR< $* > $*		$: $2
168290792Sgshapiro
168342575Speterifdef(`_ACCESS_TABLE_', `dnl
168490792Sgshapirodnl workspace: {client_name} $| {client_addr}
168590792SgshapiroR$+ $| $+		$: $>D < $1 > <?> <+ Connect> < $2 >
168642575Speterdnl workspace: <result-of-lookup> <{client_addr}>
168764562SgshapiroR<?> <$+>		$: $>A < $1 > <?> <+ Connect> <>	no: another lookup
168838032Speterdnl workspace: <result-of-lookup> (<>|<{client_addr}>)
168938032SpeterR<?> <$*>		$: OK				found nothing
169038032Speterdnl workspace: <result-of-lookup> (<>|<{client_addr}>) | OK
169138032SpeterR<$={Accept}> <$*>	$@ $1				return value of lookup
169238032SpeterR<REJECT> <$*>		$#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"')
169364562SgshapiroR<DISCARD> <$*>		$#discard $: discard
169438032Speterifdef(`_FFR_QUARANTINE',
169538032Speter`R<QUARANTINE:$+> <$*>	$#error $@ quarantine $: $1', `dnl')
169638032Speterdnl error tag
169738032SpeterR<ERROR:$-.$-.$-:$+> <$*>	$#error $@ $1.$2.$3 $: $4
169838032SpeterR<ERROR:$+> <$*>		$#error $: $1
169938032Speterifdef(`_ATMPF_', `R<$* _ATMPF_> <$*>		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
170098121Sgshapirodnl generic error from access map
170138032SpeterR<$+> <$*>		$#error $: $1', `dnl')
170238032Speter
170338032Speterifdef(`_RBL_',`dnl
170464562Sgshapiro# DNS based IP address spam list
170566494Sgshapirodnl workspace: ignored...
170690792SgshapiroR$*			$: $&{client_addr}
170766494SgshapiroR$-.$-.$-.$-		$: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
1708110560SgshapiroR<?>OK			$: OKSOFAR
1709110560SgshapiroR<?>$+			$#error $@ 5.7.1 $: "550 Mail from " $&{client_addr} " refused by blackhole site _RBL_"',
1710110560Sgshapiro`dnl')
171190792Sgshapiroundivert(8)
171290792Sgshapiro
171390792Sgshapiro######################################################################
171490792Sgshapiro###  check_mail -- check SMTP ``MAIL FROM:'' command argument
171590792Sgshapiro######################################################################
171690792Sgshapiro
171790792SgshapiroSLocal_check_mail
171890792SgshapiroScheck`'_U_`'mail
171990792SgshapiroR$*			$: $1 $| $>"Local_check_mail" $1
172064562SgshapiroR$* $| $#$*		$#$2
172166494SgshapiroR$* $| $*		$@ $>"Basic_check_mail" $1
172266494Sgshapiro
172390792SgshapiroSBasic_check_mail
172464562Sgshapiro# check for deferred delivery mode
172566494SgshapiroR$*			$: < ${deliveryMode} > $1
172638032SpeterR< d > $*		$@ deferred
172764562SgshapiroR< $* > $*		$: $2
172864562Sgshapiro
172990792Sgshapiro# authenticated?
173038032Speterdnl done first: we can require authentication for every mail transaction
173164562Sgshapirodnl workspace: address as given by MAIL FROM: (sender)
173264562SgshapiroR$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
173398121SgshapiroR$* $| $#$+		$#$2
173438032Speterdnl undo damage: remove result of tls_client call
173564562SgshapiroR$* $| $*		$: $1
173638032Speter
173738032Speterdnl workspace: address as given by MAIL FROM:
173838032SpeterR<>			$@ <OK>			we MUST accept <> (RFC 1123)
173938032Speterifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
174038032Speterdnl do some additional checks
174138032Speterdnl no user@host
174264562Sgshapirodnl no user@localhost (if nonlocal sender)
174338032Speterdnl this is a pretty simple canonification, it will not catch every case
174438032Speterdnl just make sure the address has <> around it (which is required by
174538032Speterdnl the RFC anyway, maybe we should complain if they are missing...)
174638032Speterdnl dirty trick: if it is user@host, just add a dot: user@host. this will
174738032Speterdnl not be modified by host lookups.
174838032SpeterR$+			$: <?> $1
174998121SgshapiroR<?><$+>		$: <@> <$1>
175038032SpeterR<?>$+			$: <@> <$1>
175138032Speterdnl workspace: <@> <address>
175238032Speterdnl prepend daemon_flags
175364562SgshapiroR$*			$: $&{daemon_flags} $| $1
175464562Sgshapirodnl workspace: ${daemon_flags} $| <@> <address>
175564562Sgshapirodnl do not allow these at all or only from local systems?
175664562SgshapiroR$* f $* $| <@> < $* @ $- >	$: < ? $&{client_name} > < $3 @ $4 >
175764562Sgshapirodnl accept unqualified sender: change mark to avoid test
175864562SgshapiroR$* u $* $| <@> < $* >	$: <?> < $3 >
175964562Sgshapirodnl workspace: ${daemon_flags} $| <@> <address>
176038032Speterdnl        or:                    <? ${client_name} > <address>
176164562Sgshapirodnl        or:                    <?> <address>
176264562Sgshapirodnl remove daemon_flags
176338032SpeterR$* $| $*		$: $2
176464562Sgshapiro# handle case of @localhost on address
176564562SgshapiroR<@> < $* @ localhost >	$: < ? $&{client_name} > < $1 @ localhost >
176664562SgshapiroR<@> < $* @ [127.0.0.1] >
176764562Sgshapiro			$: < ? $&{client_name} > < $1 @ [127.0.0.1] >
176864562SgshapiroR<@> < $* @ localhost.$m >
176964562Sgshapiro			$: < ? $&{client_name} > < $1 @ localhost.$m >
177064562Sgshapiroifdef(`_NO_UUCP_', `dnl',
177164562Sgshapiro`R<@> < $* @ localhost.UUCP >
177264562Sgshapiro			$: < ? $&{client_name} > < $1 @ localhost.UUCP >')
177364562Sgshapirodnl workspace: < ? $&{client_name} > <user@localhost|host>
177464562Sgshapirodnl	or:    <@> <address>
177564562Sgshapirodnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
177664562SgshapiroR<@> $*			$: $1			no localhost as domain
177764562Sgshapirodnl workspace: < ? $&{client_name} > <user@localhost|host>
177864562Sgshapirodnl	or:    <address>
177964562Sgshapirodnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
178064562SgshapiroR<? $=w> $*		$: $2			local client: ok
178164562SgshapiroR<? $+> <$+>		$#error $@ 5.5.4 $: "_CODE553 Real domain name required for sender address"
178264562Sgshapirodnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags})
178364562SgshapiroR<?> $*			$: $1')
178464562Sgshapirodnl workspace: address (or <address>)
178564562SgshapiroR$*			$: <?> $>CanonAddr $1		canonify sender address and mark it
178664562Sgshapirodnl workspace: <?> CanonicalAddress (i.e. address in canonical form localpart<@host>)
178764562Sgshapirodnl there is nothing behind the <@host> so no trailing $* needed
178838032SpeterR<?> $* < @ $+ . >	<?> $1 < @ $2 >			strip trailing dots
178964562Sgshapiro# handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
179064562SgshapiroR<?> $* < @ $* $=P >	$: <OK> $1 < @ $2 $3 >
179164562Sgshapirodnl workspace <mark> CanonicalAddress	where mark is ? or OK
179264562Sgshapiroifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',
179364562Sgshapiro`R<?> $* < @ $+ >	$: <_RES_OK_> $1 < @ $2 >		... unresolvable OK',
179438032Speter`R<?> $* < @ $+ >	$: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 >
179564562SgshapiroR<? $* <$->> $* < @ $+ >
179664562Sgshapiro			$: <$2> $3 < @ $4 >')
179764562Sgshapirodnl workspace <mark> CanonicalAddress	where mark is ?, _RES_OK_, PERM, TEMP
179864562Sgshapirodnl mark is ? iff the address is user (wo @domain)
179964562Sgshapiro
180064562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
180164562Sgshapiro# check sender address: user@address, user@, address
180264562Sgshapirodnl should we remove +ext from user?
180364562Sgshapirodnl workspace: <mark> CanonicalAddress where mark is: ?, _RES_OK_, PERM, TEMP
180464562SgshapiroR<$+> $+ < @ $* >	$: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <D:$3>
180590792SgshapiroR<$+> $+		$: @<$1> <$2> $| <U:$2@>
180664562Sgshapirodnl workspace: @<mark> <CanonicalAddress> $| <@type:address> ....
180764562Sgshapirodnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
180864562Sgshapirodnl will only return user<@domain when "reversing" the args
180964562SgshapiroR@ <$+> <$*> $| <$+>	$: <@> <$1> <$2> $| $>SearchList <+ From> $| <$3> <>
181064562Sgshapirodnl workspace: <@><mark> <CanonicalAddress> $| <result>
181164562SgshapiroR<@> <$+> <$*> $| <$*>	$: <$3> <$1> <$2>		reverse result
181264562Sgshapirodnl workspace: <result> <mark> <CanonicalAddress>
181364562Sgshapiro# retransform for further use
1814102528Sgshapirodnl required form:
181564562Sgshapirodnl <ResultOfLookup|mark> CanonicalAddress
181698121SgshapiroR<?> <$+> <$*>		$: <$1> $2	no match
1817102528SgshapiroR<$+> <$+> <$*>		$: <$1> $3	relevant result, keep it', `dnl')
181864562Sgshapirodnl workspace <ResultOfLookup|mark> CanonicalAddress
181990792Sgshapirodnl mark is ? iff the address is user (wo @domain)
182064562Sgshapiro
182164562Sgshapiroifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
182264562Sgshapiro# handle case of no @domain on address
182390792Sgshapirodnl prepend daemon_flags
182464562SgshapiroR<?> $*			$: $&{daemon_flags} $| <?> $1
182538032Speterdnl accept unqualified sender: change mark to avoid test
182664562SgshapiroR$* u $* $| <?> $*	$: <_RES_OK_> $3
182764562Sgshapirodnl remove daemon_flags
182864562SgshapiroR$* $| $*		$: $2
182990792SgshapiroR<?> $*			$: < ? $&{client_name} > $1
183090792SgshapiroR<?> $*			$@ <OK>				...local unqualed ok
183164562SgshapiroR<? $+> $*		$#error $@ 5.5.4 $: "_CODE553 Domain name required for sender address " $&f
183264562Sgshapiro							...remote is not')
183364562Sgshapiro# check results
183464562SgshapiroR<?> $*			$: @ $1		mark address: nothing known about it
183590792SgshapiroR<$={ResOk}> $*		$@ <_RES_OK_>	domain ok: stop
183664562SgshapiroR<TEMP> $*		$#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
183764562SgshapiroR<PERM> $*		$#error $@ 5.1.8 $: "_CODE553 Domain of sender address " $&f " does not exist"
183864562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
183938032SpeterR<$={Accept}> $*	$# $1		accept from access map
184064562SgshapiroR<DISCARD> $*		$#discard $: discard
184164562Sgshapiroifdef(`_FFR_QUARANTINE',
184264562Sgshapiro`R<QUARANTINE:$+> $*	$#error $@ quarantine $: $1', `dnl')
184364562SgshapiroR<REJECT> $*		$#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"')
184464562Sgshapirodnl error tag
184564562SgshapiroR<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
184638032SpeterR<ERROR:$+> $*		$#error $: $1
184738032Speterifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
184838032Speterdnl generic error from access map
184964562SgshapiroR<$+> $*		$#error $: $1		error from access db',
185064562Sgshapiro`dnl')
185164562Sgshapiro
185290792Sgshapiro######################################################################
185364562Sgshapiro###  check_rcpt -- check SMTP ``RCPT TO:'' command argument
185464562Sgshapiro######################################################################
1855110560Sgshapiro
1856102528SgshapiroSLocal_check_rcpt
185790792SgshapiroScheck`'_U_`'rcpt
185838032SpeterR$*			$: $1 $| $>"Local_check_rcpt" $1
185938032SpeterR$* $| $#$*		$#$2
186064562SgshapiroR$* $| $*		$@ $>"Basic_check_rcpt" $1
186190792Sgshapiro
186264562SgshapiroSBasic_check_rcpt
186390792Sgshapiro# empty address?
186464562SgshapiroR<>			$#error $@ nouser $: "553 User address required"
186590792SgshapiroR$@			$#error $@ nouser $: "553 User address required"
186638032Speter# check for deferred delivery mode
186790792SgshapiroR$*			$: < ${deliveryMode} > $1
186890792SgshapiroR< d > $*		$@ deferred
186964562SgshapiroR< $* > $*		$: $2
187064562Sgshapiro
187164562Sgshapiroifdef(`_REQUIRE_QUAL_RCPT_', `dnl
187264562Sgshapirodnl this code checks for user@host where host is not a FQHN.
187390792Sgshapirodnl it is not activated.
187464562Sgshapirodnl notice: code to check for a recipient without a domain name is
187564562Sgshapirodnl available down below; look for the same macro.
187638032Speterdnl this check is done here because the name might be qualified by the
187738032Speterdnl canonicalization.
187838032Speter# require fully qualified domain part?
187938032Speterdnl very simple canonification: make sure the address is in < >
188038032SpeterR$+			$: <?> $1
188138032SpeterR<?> <$+>		$: <@> <$1>
188238032SpeterR<?> $+			$: <@> <$1>
188364562SgshapiroR<@> < postmaster >	$: postmaster
188438032SpeterR<@> < $* @ $+ . $+ >	$: < $3 @ $4 . $5 >
188538032Speterdnl prepend daemon_flags
188638032SpeterR<@> $*			$: $&{daemon_flags} $| <@> $1
188738032Speterdnl workspace: ${daemon_flags} $| <@> <address>
188838032Speterdnl do not allow these at all or only from local systems?
188990792SgshapiroR$* r $* $| <@> < $* @ $* >	$: < ? $&{client_name} > < $3 @ $4 >
189090792SgshapiroR<?> < $* >		$: <$1>
189190792SgshapiroR<? $=w> < $* >		$: <$1>
189238032SpeterR<? $+> <$+>		$#error $@ 5.5.4 $: "553 Fully qualified domain name required"
189398121Sgshapirodnl remove daemon_flags for other cases
189438032SpeterR$* $| <@> $*		$: $2', `dnl')
189538032Speter
189638032Speterdnl ##################################################################
189764562Sgshapirodnl call subroutines for recipient and relay
189890792Sgshapirodnl possible returns from subroutines:
189990792Sgshapirodnl $#TEMP	temporary failure
190090792Sgshapirodnl $#error	permanent failure (or temporary if from access map)
190190792Sgshapirodnl $#other	stop processing
190290792Sgshapirodnl RELAY	RELAYing allowed
190390792Sgshapirodnl other	otherwise
190490792Sgshapiro######################################################################
190590792SgshapiroR$*			$: $1 $| @ $>"Rcpt_ok" $1
190664562Sgshapirodnl temporary failure? remove mark @ and remember
190790792SgshapiroR$* $| @ $#TEMP $+	$: $1 $| T $2
190890792Sgshapirodnl error or ok (stop)
190990792SgshapiroR$* $| @ $#$*		$#$2
1910110560Sgshapiroifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
191164562SgshapiroR$* $| @ RELAY		$@ RELAY
191290792Sgshapirodnl something else: call check sender (relay)
191364562SgshapiroR$* $| @ $*		$: O $| $>"Relay_ok" $1
1914120256Sgshapirodnl temporary failure: call check sender (relay)
1915120256SgshapiroR$* $| T $+		$: T $2 $| $>"Relay_ok" $1
191664562Sgshapirodnl temporary failure? return that
1917120256SgshapiroR$* $| $#TEMP $+	$#error $2
191864562Sgshapirodnl error or ok (stop)
191964562SgshapiroR$* $| $#$*		$#$2
192090792SgshapiroR$* $| RELAY		$@ RELAY
192164562Sgshapirodnl something else: return previous temp failure
192264562SgshapiroR T $+ $| $*		$#error $1
192364562Sgshapiro# anything else is bogus
192490792SgshapiroR$*			$#error $@ 5.7.1 $: confRELAY_MSG
192590792Sgshapirodivert(0)
192690792Sgshapiro
192790792Sgshapiro######################################################################
192890792Sgshapiro### Rcpt_ok: is the recipient ok?
192990792Sgshapirodnl input: recipient address (RCPT TO)
193090792Sgshapirodnl output: see explanation at call
193190792Sgshapiro######################################################################
193290792SgshapiroSRcpt_ok
193390792Sgshapiroifdef(`_LOOSE_RELAY_CHECK_',`dnl
193490792SgshapiroR$*			$: $>CanonAddr $1
193590792SgshapiroR$* < @ $* . >		$1 < @ $2 >			strip trailing dots',
193690792Sgshapiro`R$*			$: $>ParseRecipient $1		strip relayable hosts')
193790792Sgshapiro
193890792Sgshapiroifdef(`_BESTMX_IS_LOCAL_',`dnl
193990792Sgshapiroifelse(_BESTMX_IS_LOCAL_, `', `dnl
194090792Sgshapiro# unlimited bestmx
194190792SgshapiroR$* < @ $* > $*			$: $1 < @ $2 @@ $(bestmx $2 $) > $3',
194290792Sgshapiro`dnl
194390792Sgshapiro# limit bestmx to $=B
194490792SgshapiroR$* < @ $* $=B > $*		$: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4')
194590792SgshapiroR$* $=O $* < @ $* @@ $=w . > $*	$@ $>"Rcpt_ok" $1 $2 $3
194690792SgshapiroR$* < @ $* @@ $=w . > $*	$: $1 < @ $3 > $4
194790792SgshapiroR$* < @ $* @@ $* > $*		$: $1 < @ $2 > $4')
194890792Sgshapiro
194990792Sgshapiroifdef(`_BLACKLIST_RCPT_',`dnl
195090792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
195190792Sgshapiro# blacklist local users or any host from receiving mail
195290792SgshapiroR$*			$: <?> $1
195390792Sgshapirodnl user is now tagged with @ to be consistent with check_mail
195490792Sgshapirodnl and to distinguish users from hosts (com would be host, com@ would be user)
195590792SgshapiroR<?> $+ < @ $=w >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <D:$2>
195690792SgshapiroR<?> $+ < @ $* >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <D:$2>
195790792SgshapiroR<?> $+			$: <> <$1> $| <U:$1@>
195890792Sgshapirodnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
195990792Sgshapirodnl will only return user<@domain when "reversing" the args
196090792SgshapiroR<> <$*> $| <$+>	$: <@> <$1> $| $>SearchList <+ To> $| <$2> <>
196138032SpeterR<@> <$*> $| <$*>	$: <$2> <$1>		reverse result
196242575SpeterR<?> <$*>		$: @ $1		mark address as no match
196338032Speterdnl we may have to filter here because otherwise some RHSs
196438032Speterdnl would be interpreted as generic error messages...
196538032Speterdnl error messages should be "tagged" by prefixing them with error: !
196642575Speterdnl that would make a lot of things easier.
196742575SpeterR<$={Accept}> <$*>	$: @ $2		mark address as no match
196842575Speterifdef(`_ACCESS_SKIP_', `dnl
196942575SpeterR<SKIP> <$*>		$: @ $1		mark address as no match', `dnl')
197042575Speterifdef(`_DELAY_COMPAT_8_10_',`dnl
197142575Speterdnl compatility with 8.11/8.10:
197243730Speterdnl we have to filter these because otherwise they would be interpreted
197390792Sgshapirodnl as generic error message...
197442575Speterdnl error messages should be "tagged" by prefixing them with error: !
197542575Speterdnl that would make a lot of things easier.
197642575Speterdnl maybe we should stop checks already here (if SPAM_xyx)?
197738032SpeterR<$={SpamTag}> <$*>	$: @ $2		mark address as no match')
197864562SgshapiroR<REJECT> $*		$#error $@ 5.2.1 $: confRCPTREJ_MSG
197938032SpeterR<DISCARD> $*		$#discard $: discard
198038032Speterifdef(`_FFR_QUARANTINE',
198164562Sgshapiro`R<QUARANTINE:$+> $*	$#error $@ quarantine $: $1', `dnl')
198264562Sgshapirodnl error tag
198390792SgshapiroR<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
198490792SgshapiroR<ERROR:$+> $*		$#error $: $1
198564562Sgshapiroifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
198664562Sgshapirodnl generic error from access map
198764562SgshapiroR<$+> $*		$#error $: $1		error from access db
198890792SgshapiroR@ $*			$1		remove mark', `dnl')', `dnl')
198964562Sgshapiro
199064562Sgshapiroifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
199190792Sgshapiro# authenticated via TLS?
199290792SgshapiroR$*			$: $1 $| $>RelayTLS	client authenticated?
199390792SgshapiroR$* $| $# $+		$# $2			error/ok?
199490792SgshapiroR$* $| $*		$: $1			no
199564562Sgshapiro
199690792SgshapiroR$*			$: $1 $| $>"Local_Relay_Auth" $&{auth_type}
199790792Sgshapirodnl workspace: localpart<@domain> $| result of Local_Relay_Auth
199890792SgshapiroR$* $| $# $*		$# $2
199990792Sgshapirodnl if Local_Relay_Auth returns NO then do not check $={TrustAuthMech}
200064562SgshapiroR$* $| NO		$: $1
200164562SgshapiroR$* $| $*		$: $1 $| $&{auth_type}
200264562Sgshapirodnl workspace: localpart<@domain> [ $| ${auth_type} ]
200364562Sgshapirodnl empty ${auth_type}?
200464562SgshapiroR$* $|			$: $1
200564562Sgshapirodnl mechanism ${auth_type} accepted?
200690792Sgshapirodnl use $# to override further tests (delay_checks): see check_rcpt below
200764562SgshapiroR$* $| $={TrustAuthMech}	$# RELAY
200890792Sgshapirodnl remove ${auth_type}
200990792SgshapiroR$* $| $*		$: $1
201064562Sgshapirodnl workspace: localpart<@domain> | localpart
201164562Sgshapiroifelse(defn(`_NO_UUCP_'), `r',
201264562Sgshapiro`R$* ! $* < @ $* >	$: <REMOTE> $2 < @ BANG_PATH >
201390792SgshapiroR$* ! $* 		$: <REMOTE> $2 < @ BANG_PATH >', `dnl')
201464562Sgshapiro# anything terminating locally is ok
201564562Sgshapiroifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
201664562SgshapiroR$+ < @ $* $=m >	$@ RELAY', `dnl')
201738032SpeterR$+ < @ $=w >		$@ RELAY
201890792Sgshapiroifdef(`_RELAY_HOSTS_ONLY_',
201990792Sgshapiro`R$+ < @ $=R >		$@ RELAY
202090792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
202190792SgshapiroR$+ < @ $+ >		$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>
202290792Sgshapirodnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
202364562SgshapiroR<?> <$+ < @ $+ >>	$: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
202490792Sgshapiro`R$+ < @ $* $=R >	$@ RELAY
202590792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
202690792SgshapiroR$+ < @ $+ >		$: $>D <$2> <?> <+ To> <$1 < @ $2 >>',`dnl')')
202790792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
202890792Sgshapirodnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
202990792SgshapiroR<RELAY> $*		$@ RELAY
203090792Sgshapiroifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
203164562SgshapiroR<$*> <$*>		$: $2',`dnl')
203264562Sgshapiro
203364562Sgshapiro
203464562Sgshapiroifdef(`_RELAY_MX_SERVED_', `dnl
203590792Sgshapiro# allow relaying for hosts which we MX serve
203690792SgshapiroR$+ < @ $+ >		$: < : $(mxserved $2 $) : > $1 < @ $2 >
203764562Sgshapirodnl this must not necessarily happen if the client is checked first...
203871345SgshapiroR< : $* <TEMP> : > $*	$#TEMP $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1
203964562SgshapiroR<$* : $=w . : $*> $*	$@ RELAY
204071345SgshapiroR< : $* : > $*		$: $2',
204171345Sgshapiro`dnl')
204238032Speter
204338032Speter# check for local user (i.e. unqualified address)
204490792SgshapiroR$*			$: <?> $1
204590792SgshapiroR<?> $* < @ $+ >	$: <REMOTE> $1 < @ $2 >
204638032Speter# local user is ok
204790792Sgshapirodnl is it really? the standard requires user@domain, not just user
204864562Sgshapirodnl but we should accept it anyway (maybe making it an option:
204964562Sgshapirodnl RequireFQDN ?)
205064562Sgshapirodnl postmaster must be accepted without domain (DRUMS)
205164562Sgshapiroifdef(`_REQUIRE_QUAL_RCPT_', `dnl
205290792SgshapiroR<?> postmaster		$@ OK
205364562Sgshapiro# require qualified recipient?
205490792Sgshapirodnl prepend daemon_flags
205564562SgshapiroR<?> $+			$: $&{daemon_flags} $| <?> $1
205664562Sgshapirodnl workspace: ${daemon_flags} $| <?> localpart
205790792Sgshapirodnl do not allow these at all or only from local systems?
205890792Sgshapirodnl r flag? add client_name
205938032SpeterR$* r $* $| <?> $+	$: < ? $&{client_name} > <?> $3
206038032Speterdnl no r flag: relay to local user (only local part)
206164562Sgshapiro# no qualified recipient required
206238032SpeterR$* $| <?> $+		$@ RELAY
206338032Speterdnl client_name is empty
206464562SgshapiroR<?> <?> $+		$@ RELAY
206564562Sgshapirodnl client_name is local
206690792SgshapiroR<? $=w> <?> $+		$@ RELAY
206790792Sgshapirodnl client_name is not local
206842575SpeterR<? $+> $+		$#error $@ 5.5.4 $: "553 Domain name required"', `dnl
206938032Speterdnl no qualified recipient required
207038032SpeterR<?> $+			$@ RELAY')
207138032Speterdnl it is a remote user: remove mark and then check client
207238032SpeterR<$+> $*		$: $2
207342575Speterdnl currently the recipient address is not used below
207438032Speter
207564562Sgshapiro######################################################################
207664562Sgshapiro### Relay_ok: is the relay/sender ok?
207764562Sgshapirodnl input: ignored
207864562Sgshapirodnl output: see explanation at call
207964562Sgshapiro######################################################################
208090792SgshapiroSRelay_ok
208164562Sgshapiro# anything originating locally is ok
208264562Sgshapiro# check IP address
208364562SgshapiroR$*			$: $&{client_addr}
208464562SgshapiroR$@			$@ RELAY		originated locally
208564562SgshapiroR0			$@ RELAY		originated locally
208664562SgshapiroR$=R $*			$@ RELAY		relayable IP address
208764562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
208864562SgshapiroR$*			$: $>A <$1> <?> <+ Connect> <$1>
208964562SgshapiroR<RELAY> $* 		$@ RELAY		relayable IP address
209090792Sgshapiroifdef(`_ATMPF_', `R<_ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
209164562SgshapiroR<$*> <$*>		$: $2', `dnl')
209290792SgshapiroR$*			$: [ $1 ]		put brackets around it...
209364562SgshapiroR$=w			$@ RELAY		... and see if it is local
209490792Sgshapiro
209564562Sgshapiroifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
209664562Sgshapiroifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
209764562Sgshapiroifdef(`_RELAY_MAIL_FROM_', `dnl
209890792Sgshapirodnl input: {client_addr} or something "broken"
209964562Sgshapirodnl just throw the input away; we do not need it.
210038032Speter# check whether FROM is allowed to use system as relay
210164562SgshapiroR$*			$: <?> $>CanonAddr $&f
210238032SpeterR<?> $+ < @ $+ . >	<?> $1 < @ $2 >		remove trailing dot
210390792Sgshapiroifdef(`_RELAY_LOCAL_FROM_', `dnl
210490792Sgshapiro# check whether local FROM is ok
210590792SgshapiroR<?> $+ < @ $=w >	$@ RELAY		FROM local', `dnl')
210690792Sgshapiroifdef(`_RELAY_DB_FROM_', `dnl
210790792SgshapiroR<?> $+ < @ $+ >	$: <@> $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', `<D:$2>') <>
210890792SgshapiroR<@> <RELAY>		$@ RELAY		RELAY FROM sender ok
210938032Speterifdef(`_ATMPF_', `R<@> <_ATMPF_>		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
211064562Sgshapiro', `dnl
211164562Sgshapiroifdef(`_RELAY_DB_FROM_DOMAIN_',
211290792Sgshapiro`errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_
211390792Sgshapiro')',
2114110560Sgshapiro`dnl')
2115110560Sgshapirodnl')', `dnl')
211690792Sgshapirodnl notice: the rulesets above do not leave a unique workspace behind.
211764562Sgshapirodnl it does not matter in this case because the following rule ignores
211890792Sgshapirodnl the input. otherwise these rules must "clean up" the workspace.
211990792Sgshapiro
2120102528Sgshapiro# check client name: first: did it resolve?
2121102528Sgshapirodnl input: ignored
2122102528SgshapiroR$*			$: < $&{client_resolve} >
2123102528SgshapiroR<TEMP>			$#TEMP $@ 4.7.1 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr}
2124102528SgshapiroR<FORGED>		$#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name}
2125102528SgshapiroR<FAIL>			$#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name}
212690792Sgshapirodnl ${client_resolve} should be OK, so go ahead
212764562SgshapiroR$*			$: <@> $&{client_name}
212864562Sgshapirodnl should not be necessary since it has been done for client_addr already
212990792SgshapiroR<@>			$@ RELAY
213064562Sgshapirodnl workspace: <@> ${client_name} (not empty)
213164562Sgshapiro# pass to name server to make hostname canonical
213264562SgshapiroR<@> $* $=P 		$:<?>  $1 $2
213364562SgshapiroR<@> $+			$:<?>  $[ $1 $]
213464562Sgshapirodnl workspace: <?> ${client_name} (canonified)
213564562SgshapiroR$* .			$1			strip trailing dots
213664562Sgshapiroifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
213764562SgshapiroR<?> $* $=m		$@ RELAY', `dnl')
213890792SgshapiroR<?> $=w		$@ RELAY
213964562Sgshapiroifdef(`_RELAY_HOSTS_ONLY_',
214064562Sgshapiro`R<?> $=R		$@ RELAY
214190792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
214264562SgshapiroR<?> $*			$: <$(access Connect:$1 $: ? $)> <$1>
214394334SgshapiroR<?> <$*>		$: <$(access $1 $: ? $)> <$1>',`dnl')',
214490792Sgshapiro`R<?> $* $=R			$@ RELAY
214590792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
214690792SgshapiroR<?> $*			$: $>D <$1> <?> <+ Connect> <$1>',`dnl')')
214790792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
214890792SgshapiroR<RELAY> $*		$@ RELAY
214964562Sgshapiroifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
215064562SgshapiroR<$*> <$*>		$: $2',`dnl')
215164562Sgshapirodnl end of _PROMISCUOUS_RELAY_
215290792Sgshapirodivert(0)
215390792Sgshapiroifdef(`_DELAY_CHECKS_',`dnl
215490792Sgshapiro# turn a canonical address in the form user<@domain>
215564562Sgshapiro# qualify unqual. addresses with $j
215664562Sgshapirodnl it might have been only user (without <@domain>)
215764562SgshapiroSFullAddr
215864562SgshapiroR$* <@ $+ . >		$1 <@ $2 >
215990792SgshapiroR$* <@ $* >		$@ $1 <@ $2 >
216064562SgshapiroR$+			$@ $1 <@ $j >
216164562Sgshapiro
216264562Sgshapiro# call all necessary rulesets
216390792SgshapiroScheck_rcpt
216490792Sgshapirodnl this test should be in the Basic_check_rcpt ruleset
2165110560Sgshapirodnl which is the correct DSN code?
2166110560Sgshapiro# R$@			$#error $@ 5.1.3 $: "553 Recipient address required"
2167110560SgshapiroR$+			$: $1 $| $>checkrcpt $1
2168110560Sgshapirodnl now we can simply stop checks by returning "$# xyz" instead of just "ok"
2169110560SgshapiroR$+ $| $#$*		$#$2
217090792SgshapiroR$+ $| $*		$: <?> $>FullAddr $>CanonAddr $1
217138032Speterifdef(`_SPAM_FH_',
217290792Sgshapiro`dnl lookup user@ and user@address
217390792Sgshapiroifdef(`_ACCESS_TABLE_', `',
217490792Sgshapiro`errprint(`*** ERROR: FEATURE(`delay_checks', `argument') requires FEATURE(`access_db')
217564562Sgshapiro')')dnl
217638032Speterdnl one of the next two rules is supposed to match
217790792Sgshapirodnl this code has been copied from BLACKLIST... etc
217890792Sgshapirodnl and simplified by omitting some < >.
217938032SpeterR<?> $+ < @ $=w >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 > <U: $1@>
218090792SgshapiroR<?> $+ < @ $* >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 >
218164562Sgshapirodnl R<?>		$@ something_is_very_wrong_here
218264562Sgshapiro# lookup the addresses only with Spam tag
218364562SgshapiroR<> $* $| <$+>		$: <@> $1 $| $>SearchList <! Spam> $| <$2> <>
218490792SgshapiroR<@> $* $| $*		$: $2 $1		reverse result
218564562Sgshapirodnl', `dnl')
218690792Sgshapiroifdef(`_SPAM_FRIEND_',
218764562Sgshapiro`# is the recipient a spam friend?
218890792Sgshapiroifdef(`_SPAM_HATER_',
218990792Sgshapiro	`errprint(`*** ERROR: define either SpamHater or SpamFriend
219038032Speter')', `dnl')
219190792SgshapiroR<FRIEND> $+		$@ SPAMFRIEND
219264562SgshapiroR<$*> $+		$: $2',
219364562Sgshapiro`dnl')
219464562Sgshapiroifdef(`_SPAM_HATER_',
219564562Sgshapiro`# is the recipient no spam hater?
219664562SgshapiroR<HATER> $+		$: $1			spam hater: continue checks
219764562SgshapiroR<$*> $+		$@ NOSPAMHATER		everyone else: stop
219864562Sgshapirodnl',`dnl')
219964562Sgshapirodnl run further checks: check_mail
220064562Sgshapirodnl should we "clean up" $&f?
220138032Speterifdef(`_FFR_MAIL_MACRO',
2202120256Sgshapiro`R$*			$: $1 $| $>checkmail $&{mail_from}',
2203110560Sgshapiro`R$*			$: $1 $| $>checkmail <$&f>')
2204110560SgshapiroR$* $| $#$*		$#$2
2205110560Sgshapirodnl run further checks: check_relay
2206110560SgshapiroR$*			$: $1 $| $>checkrelay $&{client_name} $| $&{client_addr}
2207110560SgshapiroR$* $| $#$*		$#$2
2208110560SgshapiroR$* $| $*		$: $1
2209120256Sgshapiro', `dnl')
2210110560Sgshapiro
2211110560Sgshapiroifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
2212120256Sgshapiro######################################################################
2213110560Sgshapiro###  F: LookUpFull -- search for an entry in access database
2214110560Sgshapiro###
2215110560Sgshapiro###	lookup of full key (which should be an address) and
2216110560Sgshapiro###	variations if +detail exists: +* and without +detail
2217110560Sgshapiro###
2218110560Sgshapiro###	Parameters:
2219120256Sgshapiro###		<$1> -- key
2220110560Sgshapiro###		<$2> -- default (what to return if not found in db)
2221110560Sgshapirodnl			must not be empty
222264562Sgshapiro###		<$3> -- mark (must be <(!|+) single-token>)
222364562Sgshapiro###			! does lookup only with tag
222464562Sgshapiro###			+ does lookup with and without tag
222564562Sgshapiro###		<$4> -- passthru (additional data passed unchanged through)
222664562Sgshapirodnl returns:		<default> <passthru>
2227110560Sgshapirodnl 			<result> <passthru>
222864562Sgshapiro######################################################################
222964562Sgshapiro
2230110560SgshapiroSF
2231110560Sgshapirodnl workspace: <key> <def> <o tag> <thru>
2232110560Sgshapirodnl full lookup
2233110560Sgshapirodnl    2    3  4    5
2234120256SgshapiroR<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
223564562Sgshapirodnl no match, try without tag
223664562Sgshapirodnl   1    2      3    4
223764562SgshapiroR<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
223864562Sgshapirodnl no match, +detail: try +*
223964562Sgshapirodnl   1    2    3    4    5  6    7
224064562SgshapiroR<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
224164562Sgshapiro			$: <$(access $6`'_TAG_DELIM_`'$1+*@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
224264562Sgshapirodnl no match, +detail: try +* without tag
224364562Sgshapirodnl   1    2    3    4      5    6
224490792SgshapiroR<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
224590792Sgshapiro			$: <$(access $1+*@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
224664562Sgshapirodnl no match, +detail: try without +detail
224790792Sgshapirodnl   1    2    3    4    5  6    7
224890792SgshapiroR<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
224964562Sgshapiro			$: <$(access $6`'_TAG_DELIM_`'$1@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
225064562Sgshapirodnl no match, +detail: try without +detail and without tag
225164562Sgshapirodnl   1    2    3    4      5    6
225264562SgshapiroR<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
225364562Sgshapiro			$: <$(access $1@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2254110560Sgshapirodnl no match, return <default> <passthru>
225564562Sgshapirodnl   1    2    3  4    5
2256120256SgshapiroR<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
225764562Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
225864562Sgshapirodnl            2    3  4    5
225964562SgshapiroR<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
226064562Sgshapirodnl match, return <match> <passthru>
226190792Sgshapirodnl    2    3  4    5
2262120256SgshapiroR<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
226364562Sgshapiro
226464562Sgshapiro######################################################################
226564562Sgshapiro###  E: LookUpExact -- search for an entry in access database
226690792Sgshapiro###
226790792Sgshapiro###	Parameters:
226890792Sgshapiro###		<$1> -- key
226994334Sgshapiro###		<$2> -- default (what to return if not found in db)
227064562Sgshapirodnl			must not be empty
227164562Sgshapiro###		<$3> -- mark (must be <(!|+) single-token>)
227294334Sgshapiro###			! does lookup only with tag
227364562Sgshapiro###			+ does lookup with and without tag
227438032Speter###		<$4> -- passthru (additional data passed unchanged through)
227538032Speterdnl returns:		<default> <passthru>
227690792Sgshapirodnl 			<result> <passthru>
227790792Sgshapiro######################################################################
227864562Sgshapiro
227990792SgshapiroSE
228090792Sgshapirodnl    2    3  4    5
228190792SgshapiroR<$*> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
228290792Sgshapirodnl no match, try without tag
228390792Sgshapirodnl   1    2      3    4
228490792SgshapiroR<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
228590792Sgshapirodnl no match, return default passthru
228690792Sgshapirodnl   1    2    3  4    5
228790792SgshapiroR<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
228890792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
228990792Sgshapirodnl            2    3  4    5
229090792SgshapiroR<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
229190792Sgshapirodnl match, return <match> <passthru>
229290792Sgshapirodnl    2    3  4    5
229390792SgshapiroR<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
229490792Sgshapiro
229590792Sgshapiro######################################################################
229690792Sgshapiro###  U: LookUpUser -- search for an entry in access database
229790792Sgshapiro###
229890792Sgshapiro###	lookup of key (which should be a local part) and
229990792Sgshapiro###	variations if +detail exists: +* and without +detail
230090792Sgshapiro###
230190792Sgshapiro###	Parameters:
230290792Sgshapiro###		<$1> -- key (user@)
230390792Sgshapiro###		<$2> -- default (what to return if not found in db)
230490792Sgshapirodnl			must not be empty
230590792Sgshapiro###		<$3> -- mark (must be <(!|+) single-token>)
230690792Sgshapiro###			! does lookup only with tag
230790792Sgshapiro###			+ does lookup with and without tag
230890792Sgshapiro###		<$4> -- passthru (additional data passed unchanged through)
230990792Sgshapirodnl returns:		<default> <passthru>
231090792Sgshapirodnl 			<result> <passthru>
231190792Sgshapiro######################################################################
231290792Sgshapiro
231390792SgshapiroSU
231490792Sgshapirodnl user lookups are always with trailing @
231590792Sgshapirodnl    2    3  4    5
231690792SgshapiroR<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
231790792Sgshapirodnl no match, try without tag
231890792Sgshapirodnl   1    2      3    4
231990792SgshapiroR<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
232090792Sgshapirodnl do not remove the @ from the lookup:
232190792Sgshapirodnl it is part of the +detail@ which is omitted for the lookup
232290792Sgshapirodnl no match, +detail: try +*
232390792Sgshapirodnl   1    2      3    4  5    6
232490792SgshapiroR<?> <$+ + $* @> <$*> <$- $-> <$*>
232590792Sgshapiro			$: <$(access $5`'_TAG_DELIM_`'$1+*@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
232690792Sgshapirodnl no match, +detail: try +* without tag
232790792Sgshapirodnl   1    2      3      4    5
232890792SgshapiroR<?> <$+ + $* @> <$*> <+ $-> <$*>
232990792Sgshapiro			$: <$(access $1+*@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
233090792Sgshapirodnl no match, +detail: try without +detail
233190792Sgshapirodnl   1    2      3    4  5    6
233290792SgshapiroR<?> <$+ + $* @> <$*> <$- $-> <$*>
233390792Sgshapiro			$: <$(access $5`'_TAG_DELIM_`'$1@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
233490792Sgshapirodnl no match, +detail: try without +detail and without tag
233590792Sgshapirodnl   1    2      3      4    5
233690792SgshapiroR<?> <$+ + $* @> <$*> <+ $-> <$*>
233790792Sgshapiro			$: <$(access $1@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
233890792Sgshapirodnl no match, return <default> <passthru>
233990792Sgshapirodnl   1    2    3  4    5
234090792SgshapiroR<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
234190792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
234290792Sgshapirodnl            2    3  4    5
234390792SgshapiroR<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
234490792Sgshapirodnl match, return <match> <passthru>
234590792Sgshapirodnl    2    3  4    5
234690792SgshapiroR<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
234790792Sgshapiro
234890792Sgshapiro######################################################################
234990792Sgshapiro###  SearchList: search a list of items in the access map
235090792Sgshapiro###	Parameters:
235190792Sgshapiro###		<exact tag> $| <mark:address> <mark:address> ... <>
235290792Sgshapirodnl	maybe we should have a @ (again) in front of the mark to
235390792Sgshapirodnl	avoid errorneous matches (with error messages?)
235490792Sgshapirodnl	if we can make sure that tag is always a single token
235590792Sgshapirodnl	then we can omit the delimiter $|, otherwise we need it
235690792Sgshapirodnl	to avoid errorneous matchs (first rule: D: if there
235790792Sgshapirodnl	is that mark somewhere in the list, it will be taken).
235890792Sgshapirodnl	moreover, we can do some tricks to enforce lookup with
235990792Sgshapirodnl	the tag only, e.g.:
236090792Sgshapiro###	where "exact" is either "+" or "!":
236190792Sgshapiro###	<+ TAG>	lookup with and w/o tag
236290792Sgshapiro###	<! TAG>	lookup with tag
236390792Sgshapirodnl	Warning: + and ! should be in OperatorChars (otherwise there must be
236490792Sgshapirodnl		a blank between them and the tag.
236590792Sgshapiro###	possible values for "mark" are:
236690792Sgshapiro###		D: recursive host lookup (LookUpDomain)
236790792Sgshapirodnl		A: recursive address lookup (LookUpAddress) [not yet required]
236890792Sgshapiro###		E: exact lookup, no modifications
236990792Sgshapiro###		F: full lookup, try user+ext@domain and user@domain
237090792Sgshapiro###		U: user lookup, try user+ext and user (input must have trailing @)
237190792Sgshapiro###	return: <RHS of lookup> or <?> (not found)
237290792Sgshapiro######################################################################
237390792Sgshapiro
237490792Sgshapiro# class with valid marks for SearchList
237590792Sgshapirodnl if A is activated: add it
237690792SgshapiroC{src}E F D U ifdef(`_FFR_SRCHLIST_A', `A')
237790792SgshapiroSSearchList
237890792Sgshapiro# just call the ruleset with the name of the tag... nice trick...
237990792Sgshapirodnl       2       3    4
238090792SgshapiroR<$+> $| <$={src}:$*> <$*>	$: <$1> $| <$4> $| $>$2 <$3> <?> <$1> <>
238190792Sgshapirodnl workspace: <o tag> $| <rest> $| <result of lookup> <>
238290792Sgshapirodnl no match and nothing left: return
238390792SgshapiroR<$+> $| <> $| <?> <>		$@ <?>
238490792Sgshapirodnl no match but something left: continue
238590792SgshapiroR<$+> $| <$+> $| <?> <>		$@ $>SearchList <$1> $| <$2>
238690792Sgshapirodnl match: return
238790792SgshapiroR<$+> $| <$*> $| <$+> <>	$@ <$3>
238890792Sgshapirodnl return result from recursive invocation
238990792SgshapiroR<$+> $| <$+>			$@ <$2>
239090792Sgshapirodnl endif _ACCESS_TABLE_
239190792Sgshapirodivert(0)
239290792Sgshapiro
239390792Sgshapiro######################################################################
239490792Sgshapiro###  trust_auth: is user trusted to authenticate as someone else?
239590792Sgshapiro###
239690792Sgshapiro###	Parameters:
239790792Sgshapiro###		$1: AUTH= parameter from MAIL command
239890792Sgshapiro######################################################################
239990792Sgshapiro
240090792Sgshapirodnl empty ruleset definition so it can be called
240190792SgshapiroSLocal_trust_auth
240290792SgshapiroStrust_auth
240390792SgshapiroR$*			$: $&{auth_type} $| $1
240490792Sgshapiro# required by RFC 2554 section 4.
240590792SgshapiroR$@ $| $*		$#error $@ 5.7.1 $: "550 not authenticated"
240690792Sgshapirodnl seems to be useful...
240790792SgshapiroR$* $| $&{auth_authen}		$@ identical
240890792SgshapiroR$* $| <$&{auth_authen}>	$@ identical
240990792Sgshapirodnl call user supplied code
241090792SgshapiroR$* $| $*		$: $1 $| $>"Local_trust_auth" $1
241190792SgshapiroR$* $| $#$*		$#$2
241290792Sgshapirodnl default: error
241390792SgshapiroR$*			$#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author}
241490792Sgshapiro
241564562Sgshapiro######################################################################
241664562Sgshapiro###  Relay_Auth: allow relaying based on authentication?
241764562Sgshapiro###
241864562Sgshapiro###	Parameters:
241964562Sgshapiro###		$1: ${auth_type}
242064562Sgshapiro######################################################################
242164562SgshapiroSLocal_Relay_Auth
242290792Sgshapiro
242364562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
242464562Sgshapiro######################################################################
242564562Sgshapiro###  srv_features: which features to offer to a client?
242664562Sgshapiro###	(done in server)
242764562Sgshapiro######################################################################
242864562SgshapiroSsrv_features
242964562Sgshapiroifdef(`_LOCAL_SRV_FEATURES_', `dnl
243064562SgshapiroR$*			$: $1 $| $>"Local_srv_features" $1
243164562SgshapiroR$* $| $#$*		$#$2
243290792SgshapiroR$* $| $*		$: $1', `dnl')
243364562SgshapiroR$*		$: $>D <$&{client_name}> <?> <! SRV_FEAT_TAG> <>
243464562SgshapiroR<?>$*		$: $>A <$&{client_addr}> <?> <! SRV_FEAT_TAG> <>
243564562SgshapiroR<?>$*		$: <$(access SRV_FEAT_TAG`'_TAG_DELIM_ $: ? $)>
243664562SgshapiroR<?>$*		$@ OK
243764562Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
243864562SgshapiroR<$* _ATMPF_>$*	$#temp', `dnl')
243938032SpeterR<$+>$*		$# $1
244064562Sgshapiro
244164562Sgshapiro######################################################################
244290792Sgshapiro###  try_tls: try to use STARTTLS?
244364562Sgshapiro###	(done in client)
244490792Sgshapiro######################################################################
244590792SgshapiroStry_tls
244690792Sgshapiroifdef(`_LOCAL_TRY_TLS_', `dnl
244790792SgshapiroR$*			$: $1 $| $>"Local_try_tls" $1
244890792SgshapiroR$* $| $#$*		$#$2
244990792SgshapiroR$* $| $*		$: $1', `dnl')
245090792SgshapiroR$*		$: $>D <$&{server_name}> <?> <! TLS_TRY_TAG> <>
245190792SgshapiroR<?>$*		$: $>A <$&{server_addr}> <?> <! TLS_TRY_TAG> <>
245290792SgshapiroR<?>$*		$: <$(access TLS_TRY_TAG`'_TAG_DELIM_ $: ? $)>
245390792SgshapiroR<?>$*		$@ OK
245464562Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
245590792SgshapiroR<$* _ATMPF_>$*	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
245690792SgshapiroR<NO>$*		$#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"
245790792Sgshapiro  
245838032Speter######################################################################
245990792Sgshapiro###  tls_rcpt: is connection with server "good" enough?
246090792Sgshapiro###	(done in client, per recipient)
246190792Sgshapirodnl called from deliver() before RCPT command
246290792Sgshapiro###
246390792Sgshapiro###	Parameters:
246490792Sgshapiro###		$1: recipient
246590792Sgshapiro######################################################################
246690792SgshapiroStls_rcpt
246790792Sgshapiroifdef(`_LOCAL_TLS_RCPT_', `dnl
246864562SgshapiroR$*			$: $1 $| $>"Local_tls_rcpt" $1
246964562SgshapiroR$* $| $#$*		$#$2
247064562SgshapiroR$* $| $*		$: $1', `dnl')
247164562Sgshapirodnl store name of other side
247264562SgshapiroR$*			$: $(macro {TLS_Name} $@ $&{server_name} $) $1
247364562Sgshapirodnl canonify recipient address
247464562SgshapiroR$+			$: <?> $>CanonAddr $1
247564562Sgshapirodnl strip trailing dots
2476120256SgshapiroR<?> $+ < @ $+ . >	<?> $1 <@ $2 >
247764562Sgshapirodnl full address?
247864562SgshapiroR<?> $+ < @ $+ >	$: $1 <@ $2 > $| <F:$1@$2> <U:$1@> <D:$2> <E:>
247964562Sgshapirodnl only localpart?
248064562SgshapiroR<?> $+			$: $1 $| <U:$1@> <E:>
248190792Sgshapirodnl look it up
248290792Sgshapirodnl also look up a default value via E:
248390792SgshapiroR$* $| $+	$: $1 $| $>SearchList <! TLS_RCPT_TAG> $| $2 <>
248490792Sgshapirodnl found nothing: stop here
248590792SgshapiroR$* $| <?>	$@ OK
248690792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
248790792SgshapiroR$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
248864562Sgshapirodnl use the generic routine (for now)
248990792SgshapiroR$* $| <$+>	$@ $>"TLS_connection" $&{verify} $| <$2>')
249090792Sgshapiro
249190792Sgshapiro######################################################################
249290792Sgshapiro###  tls_client: is connection with client "good" enough?
249390792Sgshapiro###	(done in server)
249490792Sgshapiro###
249590792Sgshapiro###	Parameters:
249690792Sgshapiro###		${verify} $| (MAIL|STARTTLS)
249790792Sgshapiro######################################################################
249890792Sgshapirodnl MAIL: called from check_mail
249990792Sgshapirodnl STARTTLS: called from smtp() after STARTTLS has been accepted
250090792SgshapiroStls_client
250190792Sgshapiroifdef(`_LOCAL_TLS_CLIENT_', `dnl
250264562SgshapiroR$*			$: $1 $| $>"Local_tls_client" $1
250390792SgshapiroR$* $| $#$*		$#$2
250490792SgshapiroR$* $| $*		$: $1', `dnl')
250590792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
250664562Sgshapirodnl store name of other side
250790792SgshapiroR$*		$: $(macro {TLS_Name} $@ $&{server_name} $) $1
250890792Sgshapirodnl ignore second arg for now
250990792Sgshapirodnl maybe use it to distinguish permanent/temporary error?
251090792Sgshapirodnl if MAIL: permanent (STARTTLS has not been offered)
251164562Sgshapirodnl if STARTTLS: temporary (offered but maybe failed)
251290792SgshapiroR$* $| $*	$: $1 $| $>D <$&{client_name}> <?> <! TLS_CLT_TAG> <>
251390792SgshapiroR$* $| <?>$*	$: $1 $| $>A <$&{client_addr}> <?> <! TLS_CLT_TAG> <>
251490792Sgshapirodnl do a default lookup: just TLS_CLT_TAG
251590792SgshapiroR$* $| <?>$*	$: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)>
251690792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
251790792SgshapiroR$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
251890792SgshapiroR$*		$@ $>"TLS_connection" $1', `dnl
251964562SgshapiroR$* $| $*	$@ $>"TLS_connection" $1')
252090792Sgshapiro
252190792Sgshapiro######################################################################
252271345Sgshapiro###  tls_server: is connection with server "good" enough?
2523102528Sgshapiro###	(done in client)
252490792Sgshapiro###
252590792Sgshapiro###	Parameter:
252690792Sgshapiro###		${verify}
252790792Sgshapiro######################################################################
252890792Sgshapirodnl i.e. has the server been authenticated and is encryption active?
252990792Sgshapirodnl called from deliver() after STARTTLS command
253090792SgshapiroStls_server
253190792Sgshapiroifdef(`_LOCAL_TLS_SERVER_', `dnl
253290792SgshapiroR$*			$: $1 $| $>"Local_tls_server" $1
253390792SgshapiroR$* $| $#$*		$#$2
253490792SgshapiroR$* $| $*		$: $1', `dnl')
253590792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
253690792Sgshapirodnl store name of other side
253790792SgshapiroR$*		$: $(macro {TLS_Name} $@ $&{server_name} $) $1
253890792SgshapiroR$*		$: $1 $| $>D <$&{server_name}> <?> <! TLS_SRV_TAG> <>
253990792SgshapiroR$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! TLS_SRV_TAG> <>
254090792Sgshapirodnl do a default lookup: just TLS_SRV_TAG
254190792SgshapiroR$* $| <?>$*	$: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)>
254290792Sgshapiroifdef(`_ATMPF_', `dnl tempfail?
254390792SgshapiroR$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
254490792SgshapiroR$*		$@ $>"TLS_connection" $1', `dnl
254590792SgshapiroR$*		$@ $>"TLS_connection" $1')
254690792Sgshapiro
254790792Sgshapiro######################################################################
254890792Sgshapiro###  TLS_connection: is TLS connection "good" enough?
254990792Sgshapiro###
255090792Sgshapiro###	Parameters:
255190792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
255290792Sgshapiro###		${verify} $| <Requirement> [<>]', `dnl
255390792Sgshapiro###		${verify}')
255490792Sgshapiro###		Requirement: RHS from access map, may be ? for none.
255590792Sgshapirodnl	syntax for Requirement:
255664562Sgshapirodnl	[(PERM|TEMP)+] (VERIFY[:bits]|ENCR:bits) [+extensions]
255790792Sgshapirodnl	extensions: could be a list of further requirements
255890792Sgshapirodnl		for now: CN:string	{cn_subject} == string
255990792Sgshapiro######################################################################
256090792SgshapiroSTLS_connection
256190792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl', `dnl use default error
256290792Sgshapirodnl deal with TLS handshake failures: abort
256390792SgshapiroRSOFTWARE	$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake."
256464562Sgshapirodivert(-1)')
256564562Sgshapirodnl common ruleset for tls_{client|server}
256664562Sgshapirodnl input: ${verify} $| <ResultOfLookup> [<>]
256790792Sgshapirodnl remove optional <>
256890792SgshapiroR$* $| <$*>$*			$: $1 $| <$2>
256990792Sgshapirodnl workspace: ${verify} $| <ResultOfLookup>
257090792Sgshapiro# create the appropriate error codes
257164562Sgshapirodnl permanent or temporary error?
257290792SgshapiroR$* $| <PERM + $={tls} $*>	$: $1 $| <503:5.7.0> <$2 $3>
257390792SgshapiroR$* $| <TEMP + $={tls} $*>	$: $1 $| <403:4.7.0> <$2 $3>
257464562Sgshapirodnl default case depends on TLS_PERM_ERR
257564562SgshapiroR$* $| <$={tls} $*>		$: $1 $| <ifdef(`TLS_PERM_ERR', `503:5.7.0', `403:4.7.0')> <$2 $3>
257664562Sgshapirodnl workspace: ${verify} $| [<SMTP:ESC>] <ResultOfLookup>
257764562Sgshapiro# deal with TLS handshake failures: abort
257890792SgshapiroRSOFTWARE $| <$-:$+> $* 	$#error $@ $2 $: $1 " TLS handshake failed."
257990792Sgshapirodnl no <reply:dns> i.e. not requirements in the access map
258064562Sgshapirodnl use default error
258164562SgshapiroRSOFTWARE $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed."
258290792SgshapiroR$* $| <$*> <VERIFY>		$: <$2> <VERIFY> <> $1
258390792Sgshapirodnl separate optional requirements
258490792SgshapiroR$* $| <$*> <VERIFY + $+>	$: <$2> <VERIFY> <$3> $1
258590792SgshapiroR$* $| <$*> <$={tls}:$->$*	$: <$2> <$3:$4> <> $1
258664562Sgshapirodnl separate optional requirements
258790792SgshapiroR$* $| <$*> <$={tls}:$- + $+>$*	$: <$2> <$3:$4> <$5> $1
258890792Sgshapirodnl some other value in access map: accept
258990792Sgshapirodnl this also allows to override the default case (if used)
259090792SgshapiroR$* $| $*			$@ OK
259190792Sgshapiro# authentication required: give appropriate error
259290792Sgshapiro# other side did authenticate (via STARTTLS)
259390792Sgshapirodnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> <[extensions]> ${verify}
259464562Sgshapirodnl only verification required and it succeeded
259564562SgshapiroR<$*><VERIFY> <> OK		$@ OK
259664562Sgshapirodnl verification required and it succeeded but extensions are given
259790792Sgshapirodnl change it to <SMTP:ESC> <REQ:0>  <extensions>
259890792SgshapiroR<$*><VERIFY> <$+> OK		$: <$1> <REQ:0> <$2>
259990792Sgshapirodnl verification required + some level of encryption
260090792SgshapiroR<$*><VERIFY:$-> <$*> OK	$: <$1> <REQ:$2> <$3>
260164562Sgshapirodnl just some level of encryption required
260290792SgshapiroR<$*><ENCR:$-> <$*> $*		$: <$1> <REQ:$2> <$3>
260390792Sgshapirodnl workspace:
260490792Sgshapirodnl 1. <SMTP:ESC> <VERIFY [:bits]>  <[extensions]> {verify} (!= OK)
260590792Sgshapirodnl 2. <SMTP:ESC> <REQ:bits>  <[extensions]>
260664562Sgshapirodnl verification required but ${verify} is not set (case 1.)
260764562SgshapiroR<$-:$+><VERIFY $*> <$*>	$#error $@ $2 $: $1 " authentication required"
260890792SgshapiroR<$-:$+><VERIFY $*> <$*> FAIL	$#error $@ $2 $: $1 " authentication failed"
260990792SgshapiroR<$-:$+><VERIFY $*> <$*> NO	$#error $@ $2 $: $1 " not authenticated"
261090792SgshapiroR<$-:$+><VERIFY $*> <$*> NOT	$#error $@ $2 $: $1 " no authentication requested"
261190792SgshapiroR<$-:$+><VERIFY $*> <$*> NONE	$#error $@ $2 $: $1 " other side does not support STARTTLS"
261264562Sgshapirodnl some other value for ${verify}
261390792SgshapiroR<$-:$+><VERIFY $*> <$*> $+	$#error $@ $2 $: $1 " authentication failure " $4
261490792Sgshapirodnl some level of encryption required: get the maximum level (case 2.)
261590792SgshapiroR<$*><REQ:$-> <$*>		$: <$1> <REQ:$2> <$3> $>max $&{cipher_bits} : $&{auth_ssf}
261690792Sgshapirodnl compare required bits with actual bits
261764562SgshapiroR<$*><REQ:$-> <$*> $-		$: <$1> <$2:$4> <$3> $(arith l $@ $4 $@ $2 $)
261890792SgshapiroR<$-:$+><$-:$-> <$*> TRUE	$#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3
261990792Sgshapirodnl strength requirements fulfilled
262090792Sgshapirodnl TLS Additional Requirements Separator
262190792Sgshapirodnl this should be something which does not appear in the extensions itself
262290792Sgshapirodnl @ could be part of a CN, DN, etc...
262390792Sgshapirodnl use < > ? those are encoded in CN, DN, ...
262490792Sgshapirodefine(`_TLS_ARS_', `++')dnl
262590792Sgshapirodnl workspace:
262690792Sgshapirodnl <SMTP:ESC> <REQ:bits> <extensions> result-of-compare
262790792SgshapiroR<$-:$+><$-:$-> <$*> $*		$: <$1:$2 _TLS_ARS_ $5>
262890792Sgshapirodnl workspace: <SMTP:ESC _TLS_ARS_ extensions>
262990792Sgshapirodnl continue: check  extensions
263090792SgshapiroR<$-:$+ _TLS_ARS_ >			$@ OK
263164562Sgshapirodnl split extensions into own list
263290792SgshapiroR<$-:$+ _TLS_ARS_ $+ >			$: <$1:$2> <$3>
263364562SgshapiroR<$-:$+> < $+ _TLS_ARS_ $+ >		<$1:$2> <$3> <$4>
263464562SgshapiroR<$-:$+> $+			$@ $>"TLS_req" $3 $| <$1:$2>
263590792Sgshapiro
263690792Sgshapiro######################################################################
263764562Sgshapiro###  TLS_req: check additional TLS requirements
263864562Sgshapiro###
263964562Sgshapiro###	Parameters: [<list> <of> <req>] $| <$-:$+>
264064562Sgshapiro###		$-: SMTP reply code
264164562Sgshapiro###		$+: Enhanced Status Code
264290792Sgshapirodnl  further requirements for this ruleset:
264390792Sgshapirodnl	name of "other side" is stored is {TLS_name} (client/server_name)
264464562Sgshapirodnl
264564562Sgshapirodnl	currently only CN[:common_name] is implemented
264664562Sgshapirodnl	right now this is only a logical AND
264764562Sgshapirodnl	i.e. all requirements must be true
264890792Sgshapirodnl	how about an OR? CN must be X or CN must be Y or ..
264990792Sgshapirodnl	use a macro to compute this as a trivial sequential
265090792Sgshapirodnl	operations (no precedences etc)?
265190792Sgshapiro######################################################################
265290792SgshapiroSTLS_req
265390792Sgshapirodnl no additional requirements: ok
265464562SgshapiroR $| $+		$@ OK
265564562Sgshapirodnl require CN: but no CN specified: use name of other side
265664562SgshapiroR<CN> $* $| <$+>		$: <CN:$&{TLS_Name}> $1 $| <$2>
265764562Sgshapirodnl match, check rest
265864562SgshapiroR<CN:$&{cn_subject}> $* $| <$+>		$@ $>"TLS_req" $1 $| <$2>
265990792Sgshapirodnl CN does not match
266064562Sgshapirodnl  1   2      3  4
266190792SgshapiroR<CN:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " CN " $&{cn_subject} " does not match " $1
266290792Sgshapirodnl cert subject
266390792SgshapiroR<CS:$&{cert_subject}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
266490792Sgshapirodnl CS does not match
266564562Sgshapirodnl  1   2      3  4
266690792SgshapiroR<CS:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " CERT Subject " $&{cert_subject} " does not match " $1
266764562Sgshapirodnl match, check rest
266890792SgshapiroR<CI:$&{cert_issuer}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
266990792Sgshapirodnl CI does not match
267090792Sgshapirodnl  1   2      3  4
267190792SgshapiroR<CI:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " CERT Issuer " $&{cert_issuer} " does not match " $1
267290792Sgshapirodnl return from recursive call
267390792SgshapiroROK			$@ OK
267490792Sgshapiro
267590792Sgshapiro######################################################################
267690792Sgshapiro###  max: return the maximum of two values separated by :
267790792Sgshapiro###
267864562Sgshapiro###	Parameters: [$-]:[$-]
267990792Sgshapiro######################################################################
268090792SgshapiroSmax
268190792SgshapiroR:		$: 0
268264562SgshapiroR:$-		$: $1
268390792SgshapiroR$-:		$: $1
268490792SgshapiroR$-:$-		$: $(arith l $@ $1 $@ $2 $) : $1 : $2
268590792SgshapiroRTRUE:$-:$-	$: $2
268690792SgshapiroR$-:$-:$-	$: $2
268790792Sgshapirodnl endif _ACCESS_TABLE_
268890792Sgshapirodivert(0)
268990792Sgshapiro
269090792Sgshapiro######################################################################
269190792Sgshapiro###  RelayTLS: allow relaying based on TLS authentication
269290792Sgshapiro###
269390792Sgshapiro###	Parameters:
269490792Sgshapiro###		none
269590792Sgshapiro######################################################################
269690792SgshapiroSRelayTLS
269790792Sgshapiro# authenticated?
269890792Sgshapirodnl we do not allow relaying for anyone who can present a cert
269990792Sgshapirodnl signed by a "trusted" CA. For example, even if we put verisigns
270090792Sgshapirodnl CA in CERTPath so we can authenticate users, we do not allow
270164562Sgshapirodnl them to abuse our server (they might be easier to get hold of,
270290792Sgshapirodnl but anyway).
270390792Sgshapirodnl so here is the trick: if the verification succeeded
270490792Sgshapirodnl we look up the cert issuer in the access map
270590792Sgshapirodnl (maybe after extracting a part with a regular expression)
270690792Sgshapirodnl if this returns RELAY we relay without further questions
270790792Sgshapirodnl if it returns SUBJECT we perform a similar check on the
270890792Sgshapirodnl cert subject.
270990792Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
271090792SgshapiroR$*			$: <?> $&{verify}
271190792SgshapiroR<?> OK			$: OK		authenticated: continue
271290792SgshapiroR<?> $*			$@ NO		not authenticated
271390792Sgshapiroifdef(`_CERT_REGEX_ISSUER_', `dnl
271490792SgshapiroR$*			$: $(CERTIssuer $&{cert_issuer} $)',
271590792Sgshapiro`R$*			$: $&{cert_issuer}')
271690792SgshapiroR$+			$: $(access CERTISSUER`'_TAG_DELIM_`'$1 $)
271790792Sgshapirodnl use $# to stop further checks (delay_check)
271890792SgshapiroRRELAY			$# RELAY
271990792Sgshapiroifdef(`_CERT_REGEX_SUBJECT_', `dnl
272090792SgshapiroRSUBJECT		$: <@> $(CERTSubject $&{cert_subject} $)',
272190792Sgshapiro`RSUBJECT		$: <@> $&{cert_subject}')
272290792SgshapiroR<@> $+			$: <@> $(access CERTSUBJECT`'_TAG_DELIM_`'$1 $)
272390792SgshapiroR<@> RELAY		$# RELAY
272490792SgshapiroR$*			$: NO', `dnl')
272590792Sgshapiro
272690792Sgshapiro######################################################################
272790792Sgshapiro###  authinfo: lookup authinfo in the access map
272890792Sgshapiro###
272990792Sgshapiro###	Parameters:
273090792Sgshapiro###		$1: {server_name}
273190792Sgshapiro###		$2: {server_addr}
2732110560Sgshapirodnl	both are currently ignored
273390792Sgshapirodnl if it should be done via another map, we either need to restrict
273490792Sgshapirodnl functionality (it calls D and A) or copy those rulesets (or add another
273590792Sgshapirodnl parameter which I want to avoid, it's quite complex already)
273690792Sgshapiro######################################################################
2737110560Sgshapirodnl omit this ruleset if neither is defined?
273890792Sgshapirodnl it causes DefaultAuthInfo to be ignored
273990792Sgshapirodnl (which may be considered a good thing).
274090792SgshapiroSauthinfo
274190792Sgshapiroifdef(`_AUTHINFO_TABLE_', `dnl
274290792SgshapiroR$*		$: <$(authinfo AuthInfo:$&{server_name} $: ? $)>
274390792SgshapiroR<?>		$: <$(authinfo AuthInfo:$&{server_addr} $: ? $)>
274490792SgshapiroR<?>		$: <$(authinfo AuthInfo: $: ? $)>
274590792SgshapiroR<?>		$@ no				no authinfo available
274664562SgshapiroR<$*>		$# $1
274764562Sgshapirodnl', `dnl
274864562Sgshapiroifdef(`_ACCESS_TABLE_', `dnl
274964562SgshapiroR$*		$: $1 $| $>D <$&{server_name}> <?> <! AuthInfo> <>
275064562SgshapiroR$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! AuthInfo> <>
275164562SgshapiroR$* $| <?>$*	$: $1 $| <$(access AuthInfo`'_TAG_DELIM_ $: ? $)> <>
275290792SgshapiroR$* $| <?>$*	$@ no				no authinfo available
275390792SgshapiroR$* $| <$*> <>	$# $2
275490792Sgshapirodnl', `dnl')')
275564562Sgshapiro
275690792Sgshapiroundivert(9)dnl LOCAL_RULESETS
275790792Sgshapiro#
275890792Sgshapiro######################################################################
275990792Sgshapiro######################################################################
276090792Sgshapiro#####
276190792Sgshapiro`#####			MAIL FILTER DEFINITIONS'
276290792Sgshapiro#####
276364562Sgshapiro######################################################################
276464562Sgshapiro######################################################################
276564562Sgshapiro_MAIL_FILTERS_
2766110560Sgshapiro#
276764562Sgshapiro######################################################################
276864562Sgshapiro######################################################################
276964562Sgshapiro#####
277064562Sgshapiro`#####			MAILER DEFINITIONS'
277164562Sgshapiro#####
277264562Sgshapiro######################################################################
277364562Sgshapiro######################################################################
277464562Sgshapiroundivert(7)dnl MAILER_DEFINITIONS
277564562Sgshapiro
277690792Sgshapiro