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