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