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