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