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