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