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