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