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