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