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