proto.m4 revision 71345
1divert(-1)
2#
3# Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
4#	All rights reserved.
5# Copyright (c) 1983, 1995 Eric P. Allman.  All rights reserved.
6# Copyright (c) 1988, 1993
7#	The Regents of the University of California.  All rights reserved.
8#
9# By using this file, you agree to the terms and conditions set
10# forth in the LICENSE file which can be found at the top level of
11# the sendmail distribution.
12#
13#
14divert(0)
15
16VERSIONID(`$Id: proto.m4,v 8.446.2.5.2.38 2000/12/28 03:37:28 ca Exp $')
17
18MAILER(local)dnl
19
20# level CF_LEVEL config file format
21V`'CF_LEVEL/ifdef(`VENDOR_NAME', `VENDOR_NAME', `Berkeley')
22divert(-1)
23
24# do some sanity checking
25ifdef(`__OSTYPE__',,
26	`errprint(`*** ERROR: No system type defined (use OSTYPE macro)
27')')
28
29# pick our default mailers
30ifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `esmtp')')
31ifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')')
32ifdef(`confRELAY_MAILER',,
33	`define(`confRELAY_MAILER',
34		`ifdef(`_MAILER_smtp_', `relay',
35			`ifdef(`_MAILER_uucp', `uucp-new', `unknown')')')')
36ifdef(`confUUCP_MAILER',, `define(`confUUCP_MAILER', `uucp-old')')
37define(`_SMTP_', `confSMTP_MAILER')dnl		for readability only
38define(`_LOCAL_', `confLOCAL_MAILER')dnl	for readability only
39define(`_RELAY_', `confRELAY_MAILER')dnl	for readability only
40define(`_UUCP_', `confUUCP_MAILER')dnl		for readability only
41
42# back compatibility with old config files
43ifdef(`confDEF_GROUP_ID',
44`errprint(`*** confDEF_GROUP_ID is obsolete.
45    Use confDEF_USER_ID with a colon in the value instead.
46')')
47ifdef(`confREAD_TIMEOUT',
48`errprint(`*** confREAD_TIMEOUT is obsolete.
49    Use individual confTO_<timeout> parameters instead.
50')')
51ifdef(`confMESSAGE_TIMEOUT',
52	`define(`_ARG_', index(confMESSAGE_TIMEOUT, /))
53	 ifelse(_ARG_, -1,
54		`define(`confTO_QUEUERETURN', confMESSAGE_TIMEOUT)',
55		`define(`confTO_QUEUERETURN',
56			substr(confMESSAGE_TIMEOUT, 0, _ARG_))
57		 define(`confTO_QUEUEWARN',
58			substr(confMESSAGE_TIMEOUT, eval(_ARG_+1)))')')
59ifdef(`confMIN_FREE_BLOCKS', `ifelse(index(confMIN_FREE_BLOCKS, /), -1,,
60`errprint(`*** compound confMIN_FREE_BLOCKS is obsolete.
61    Use confMAX_MESSAGE_SIZE for the second part of the value.
62')')')
63
64
65# Sanity check on ldap_routing feature
66# If the user doesn't specify a new map, they better have given as a
67# default LDAP specification which has the LDAP base (and most likely the host)
68ifdef(`confLDAP_DEFAULT_SPEC',, `ifdef(`_LDAP_ROUTING_WARN_', `errprint(`
69WARNING: Using default FEATURE(ldap_routing) map definition(s)
70without setting confLDAP_DEFAULT_SPEC option.
71')')')dnl
72
73# clean option definitions below....
74define(`_OPTION', `ifdef(`$2', `O $1`'ifelse(defn(`$2'), `',, `=$2')', `#O $1`'ifelse(`$3', `',,`=$3')')')dnl
75
76dnl required to "rename" the check_* rulesets...
77define(`_U_',ifdef(`_DELAY_CHECKS_',`',`_'))
78dnl default relaying denied message
79ifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG', `"550 Relaying denied"')')
80divert(0)dnl
81
82# override file safeties - setting this option compromises system security,
83# addressing the actual file configuration problem is preferred
84# need to set this before any file actions are encountered in the cf file
85_OPTION(DontBlameSendmail, `confDONT_BLAME_SENDMAIL', `safe')
86
87# default LDAP map specification
88# need to set this now before any LDAP maps are defined
89_OPTION(LDAPDefaultSpec, `confLDAP_DEFAULT_SPEC', `-h localhost')
90
91##################
92#   local info   #
93##################
94
95Cwlocalhost
96ifdef(`USE_CW_FILE',
97`# file containing names of hosts for which we receive email
98Fw`'confCW_FILE',
99	`dnl')
100
101# my official domain name
102# ... `define' this only if sendmail cannot automatically determine your domain
103ifdef(`confDOMAIN_NAME', `Dj`'confDOMAIN_NAME', `#Dj$w.Foo.COM')
104
105CP.
106
107ifdef(`UUCP_RELAY',
108`# UUCP relay host
109DY`'UUCP_RELAY
110CPUUCP
111
112')dnl
113ifdef(`BITNET_RELAY',
114`#  BITNET relay host
115DB`'BITNET_RELAY
116CPBITNET
117
118')dnl
119ifdef(`DECNET_RELAY',
120`define(`_USE_DECNET_SYNTAX_', 1)dnl
121# DECnet relay host
122DC`'DECNET_RELAY
123CPDECNET
124
125')dnl
126ifdef(`FAX_RELAY',
127`# FAX relay host
128DF`'FAX_RELAY
129CPFAX
130
131')dnl
132# "Smart" relay host (may be null)
133DS`'ifdef(`SMART_HOST', SMART_HOST)
134
135ifdef(`LUSER_RELAY', `dnl
136# place to which unknown users should be forwarded
137Kuser user -m -a<>
138DL`'LUSER_RELAY',
139`dnl')
140
141# operators that cannot be in local usernames (i.e., network indicators)
142CO @ % ifdef(`_NO_UUCP_', `', `!')
143
144# a class with just dot (for identifying canonical names)
145C..
146
147# a class with just a left bracket (for identifying domain literals)
148C[[
149
150ifdef(`_ACCESS_TABLE_', `dnl
151# access_db acceptance class
152C{Accept}OK RELAY
153ifdef(`_DELAY_CHECKS_',`dnl
154ifdef(`_BLACKLIST_RCPT_',`dnl
155# possible access_db RHS for spam friends/haters
156C{SpamTag}SPAMFRIEND SPAMHATER')')',
157`dnl')
158
159ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',`dnl',`dnl
160# Resolve map (to check if a host exists in check_mail)
161Kresolve host -a<OK> -T<TEMP>')
162
163ifdef(`_FFR_5_', `# macro storage map
164Kmacro macro')
165
166ifdef(`confCR_FILE', `dnl
167# Hosts for which relaying is permitted ($=R)
168FR`'confCR_FILE',
169`dnl')
170
171define(`TLS_SRV_TAG', `TLS_Srv')dnl
172define(`TLS_CLT_TAG', `TLS_Clt')dnl
173define(`TLS_TRY_TAG', `Try_TLS')dnl
174define(`TLS_OFF_TAG', `Offer_TLS')dnl
175dnl this may be useful in other contexts too
176ifdef(`_ARITH_MAP_', `', `# arithmetic map
177define(`_ARITH_MAP_', `1')dnl
178Karith arith')
179ifdef(`_ACCESS_TABLE_', `dnl
180# possible values for tls_connect in access map
181C{tls}VERIFY ENCR', `dnl')
182ifdef(`_CERT_REGEX_ISSUER_', `dnl
183# extract relevant part from cert issuer
184KCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
185ifdef(`_CERT_REGEX_SUBJECT_', `dnl
186# extract relevant part from cert subject
187KCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
188
189# who I send unqualified names to (null means deliver locally)
190DR`'ifdef(`LOCAL_RELAY', LOCAL_RELAY)
191
192# who gets all local email traffic ($R has precedence for unqualified names)
193DH`'ifdef(`MAIL_HUB', MAIL_HUB)
194
195# dequoting map
196Kdequote dequote
197
198divert(0)dnl	# end of nullclient diversion
199# class E: names that should be exposed as from this host, even if we masquerade
200# class L: names that should be delivered locally, even if we have a relay
201# class M: domains that should be converted to $M
202# class N: domains that should not be converted to $M
203#CL root
204undivert(5)dnl
205ifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl')
206
207# who I masquerade as (null for no masquerading) (see also $=M)
208DM`'ifdef(`MASQUERADE_NAME', MASQUERADE_NAME)
209
210# my name for error messages
211ifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
212
213undivert(6)dnl LOCAL_CONFIG
214include(_CF_DIR_`m4/version.m4')
215
216###############
217#   Options   #
218###############
219
220# strip message body to 7 bits on input?
221_OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
222
223# 8-bit data handling
224_OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `adaptive')
225
226# wait for alias file rebuild (default units: minutes)
227_OPTION(AliasWait, `confALIAS_WAIT', `5m')
228
229# location of alias file
230_OPTION(AliasFile, `ALIAS_FILE', `MAIL_SETTINGS_DIR`'aliases')
231
232# minimum number of free blocks on filesystem
233_OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', `100')
234
235# maximum message size
236_OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', `1000000')
237
238# substitution for space (blank) characters
239_OPTION(BlankSub, `confBLANK_SUB', `_')
240
241# avoid connecting to "expensive" mailers on initial submission?
242_OPTION(HoldExpensive, `confCON_EXPENSIVE', `False')
243
244# checkpoint queue runs after every N successful deliveries
245_OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10')
246
247# default delivery mode
248_OPTION(DeliveryMode, `confDELIVERY_MODE', `background')
249
250# automatically rebuild the alias database?
251# NOTE: There is a potential for a denial of service attack if this is set.
252#       This option is deprecated and will be removed from a future version.
253_OPTION(AutoRebuildAliases, `confAUTO_REBUILD', `False')
254
255# error message header/file
256_OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header')
257
258# error mode
259_OPTION(ErrorMode, `confERROR_MODE', `print')
260
261# save Unix-style "From_" lines at top of header?
262_OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False')
263
264# temporary file mode
265_OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600')
266
267# match recipients against GECOS field?
268_OPTION(MatchGECOS, `confMATCH_GECOS', `False')
269
270# maximum hop count
271_OPTION(MaxHopCount, `confMAX_HOP', `17')
272
273# location of help file
274O HelpFile=ifdef(`HELP_FILE', HELP_FILE, `MAIL_SETTINGS_DIR`'helpfile')
275
276# ignore dots as terminators in incoming messages?
277_OPTION(IgnoreDots, `confIGNORE_DOTS', `False')
278
279# name resolver options
280_OPTION(ResolverOptions, `confBIND_OPTS', `+AAONLY')
281
282# deliver MIME-encapsulated error messages?
283_OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS', `True')
284
285# Forward file search path
286_OPTION(ForwardPath, `confFORWARD_PATH', `/var/forward/$u:$z/.forward.$w:$z/.forward')
287
288# open connection cache size
289_OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', `2')
290
291# open connection cache timeout
292_OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', `5m')
293
294# persistent host status directory
295_OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', `.hoststat')
296
297# single thread deliveries (requires HostStatusDirectory)?
298_OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY', `False')
299
300# use Errors-To: header?
301_OPTION(UseErrorsTo, `confUSE_ERRORS_TO', `False')
302
303# log level
304_OPTION(LogLevel, `confLOG_LEVEL', `10')
305
306# send to me too, even in an alias expansion?
307_OPTION(MeToo, `confME_TOO', `True')
308
309# verify RHS in newaliases?
310_OPTION(CheckAliases, `confCHECK_ALIASES', `False')
311
312# default messages to old style headers if no special punctuation?
313_OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS', `False')
314
315# SMTP daemon options
316ifelse(defn(`confDAEMON_OPTIONS'), `', `dnl',
317`errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.  See cf/README for more information.
318)'dnl
319`DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
320ifelse(defn(`_DPO_'), `',
321`ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-IPv4, Family=inet
322O DaemonPortOptions=Name=MTA-IPv6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_')
323ifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
324
325# SMTP client options
326_OPTION(ClientPortOptions, `confCLIENT_OPTIONS', `Address=0.0.0.0')
327
328# privacy flags
329_OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings')
330
331# who (if anyone) should get extra copies of error messages
332_OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster')
333
334# slope of queue-only function
335_OPTION(QueueFactor, `confQUEUE_FACTOR', `600000')
336
337# queue directory
338O QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue')
339
340# timeouts (many of these)
341_OPTION(Timeout.initial, `confTO_INITIAL', `5m')
342_OPTION(Timeout.connect, `confTO_CONNECT', `5m')
343_OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m')
344_OPTION(Timeout.helo, `confTO_HELO', `5m')
345_OPTION(Timeout.mail, `confTO_MAIL', `10m')
346_OPTION(Timeout.rcpt, `confTO_RCPT', `1h')
347_OPTION(Timeout.datainit, `confTO_DATAINIT', `5m')
348_OPTION(Timeout.datablock, `confTO_DATABLOCK', `1h')
349_OPTION(Timeout.datafinal, `confTO_DATAFINAL', `1h')
350_OPTION(Timeout.rset, `confTO_RSET', `5m')
351_OPTION(Timeout.quit, `confTO_QUIT', `2m')
352_OPTION(Timeout.misc, `confTO_MISC', `2m')
353_OPTION(Timeout.command, `confTO_COMMAND', `1h')
354_OPTION(Timeout.ident, `confTO_IDENT', `5s')
355_OPTION(Timeout.fileopen, `confTO_FILEOPEN', `60s')
356_OPTION(Timeout.control, `confTO_CONTROL', `2m')
357_OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', `5d')
358_OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', `5d')
359_OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', `2d')
360_OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', `7d')
361_OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', `4h')
362_OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', `4h')
363_OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', `1h')
364_OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', `12h')
365_OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', `30m')
366_OPTION(Timeout.resolver.retrans, `confTO_RESOLVER_RETRANS', `5s')
367_OPTION(Timeout.resolver.retrans.first, `confTO_RESOLVER_RETRANS_FIRST', `5s')
368_OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s')
369_OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4')
370_OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4')
371_OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4')
372
373# should we not prune routes in route-addr syntax addresses?
374_OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False')
375
376# queue up everything before forking?
377_OPTION(SuperSafe, `confSAFE_QUEUE', `True')
378
379# status file
380O StatusFile=ifdef(`STATUS_FILE', `STATUS_FILE', `MAIL_SETTINGS_DIR`'statistics')
381
382# time zone handling:
383#  if undefined, use system default
384#  if defined but null, use TZ envariable passed in
385#  if defined and non-null, use that info
386ifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
387	confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
388	`O TimeZoneSpec=confTIME_ZONE')
389
390# default UID (can be username or userid:groupid)
391_OPTION(DefaultUser, `confDEF_USER_ID', `mailnull')
392
393# list of locations of user database file (null means no lookup)
394_OPTION(UserDatabaseSpec, `confUSERDB_SPEC', `MAIL_SETTINGS_DIR`'userdb')
395
396# fallback MX host
397_OPTION(FallbackMXhost, `confFALLBACK_MX', `fall.back.host.net')
398
399# if we are the best MX host for a site, try it directly instead of config err
400_OPTION(TryNullMXList, `confTRY_NULL_MX_LIST', `False')
401
402# load average at which we just queue messages
403_OPTION(QueueLA, `confQUEUE_LA', `8')
404
405# load average at which we refuse connections
406_OPTION(RefuseLA, `confREFUSE_LA', `12')
407
408# maximum number of children we allow at one time
409_OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `12')
410
411# maximum number of new connections per second
412_OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0')
413
414# work recipient factor
415_OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
416
417# deliver each queued job in a separate process?
418_OPTION(ForkEachJob, `confSEPARATE_PROC', `False')
419
420# work class factor
421_OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800')
422
423# work time factor
424_OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000')
425
426# shall we sort the queue by hostname first?
427_OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority')
428
429# minimum time in queue before retry
430_OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m')
431
432# default character set
433_OPTION(DefaultCharSet, `confDEF_CHAR_SET', `iso-8859-1')
434
435# service switch file (ignored on Solaris, Ultrix, OSF/1, others)
436_OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch')
437
438# hosts file (normally /etc/hosts)
439_OPTION(HostsFile, `confHOSTS_FILE', `/etc/hosts')
440
441# dialup line delay on connection failure
442_OPTION(DialDelay, `confDIAL_DELAY', `10s')
443
444# action to take if there are no recipients in the message
445_OPTION(NoRecipientAction, `confNO_RCPT_ACTION', `add-to-undisclosed')
446
447# chrooted environment for writing to files
448_OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `/arch')
449
450# are colons OK in addresses?
451_OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True')
452
453# how many jobs can you process in the queue?
454_OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `10000')
455
456# shall I avoid expanding CNAMEs (violates protocols)?
457_OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False')
458
459# SMTP initial login message (old $e macro)
460_OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG', `$j Sendmail $v ready at $b')
461
462# UNIX initial From header format (old $l macro)
463_OPTION(UnixFromLine, `confFROM_LINE', `From $g $d')
464
465# From: lines that have embedded newlines are unwrapped onto one line
466_OPTION(SingleLineFromHeader, `confSINGLE_LINE_FROM_HEADER', `False')
467
468# Allow HELO SMTP command that does not `include' a host name
469_OPTION(AllowBogusHELO, `confALLOW_BOGUS_HELO', `False')
470
471# Characters to be quoted in a full name phrase (@,;:\()[] are automatic)
472_OPTION(MustQuoteChars, `confMUST_QUOTE_CHARS', `.')
473
474# delimiter (operator) characters (old $o macro)
475_OPTION(OperatorChars, `confOPERATORS', `.:@[]')
476
477# shall I avoid calling initgroups(3) because of high NIS costs?
478_OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False')
479
480# are group-writable `:include:' and .forward files (un)trustworthy?
481_OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True')
482
483# where do errors that occur when sending errors get sent?
484_OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster')
485
486# where to save bounces if all else fails
487_OPTION(DeadLetterDrop, `confDEAD_LETTER_DROP', `/var/tmp/dead.letter')
488
489# what user id do we assume for the majority of the processing?
490_OPTION(RunAsUser, `confRUN_AS_USER', `sendmail')
491
492# maximum number of recipients per SMTP envelope
493_OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `100')
494
495# shall we get local names from our installed interfaces?
496_OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False')
497
498# Return-Receipt-To: header implies DSN request
499_OPTION(RrtImpliesDsn, `confRRT_IMPLIES_DSN', `False')
500
501# override connection address (for testing)
502_OPTION(ConnectOnlyTo, `confCONNECT_ONLY_TO', `0.0.0.0')
503
504# Trusted user for file ownership and starting the daemon
505_OPTION(TrustedUser, `confTRUSTED_USER', `root')
506
507# Control socket for daemon management
508_OPTION(ControlSocketName, `confCONTROL_SOCKET_NAME', `/var/spool/mqueue/.control')
509
510# Maximum MIME header length to protect MUAs
511_OPTION(MaxMimeHeaderLength, `confMAX_MIME_HEADER_LENGTH', `0/0')
512
513# Maximum length of the sum of all headers
514_OPTION(MaxHeadersLength, `confMAX_HEADERS_LENGTH', `32768')
515
516# Maximum depth of alias recursion
517_OPTION(MaxAliasRecursion, `confMAX_ALIAS_RECURSION', `10')
518
519# location of pid file
520_OPTION(PidFile, `confPID_FILE', `/var/run/sendmail.pid')
521
522# Prefix string for the process title shown on 'ps' listings
523_OPTION(ProcessTitlePrefix, `confPROCESS_TITLE_PREFIX', `prefix')
524
525# Data file (df) memory-buffer file maximum size
526_OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096')
527
528# Transcript file (xf) memory-buffer file maximum size
529_OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096')
530
531# list of authentication mechanisms
532_OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5')
533
534# default authentication information for outgoing connections
535_OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info')
536
537# SMTP AUTH flags
538_OPTION(AuthOptions, `confAUTH_OPTIONS', `')
539
540ifdef(`_FFR_MILTER', `
541# Input mail filters
542_OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
543
544# Milter options
545_OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `')
546_OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `')
547_OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
548_OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')')
549
550# CA directory
551_OPTION(CACERTPath, `confCACERT_PATH', `')
552# CA file
553_OPTION(CACERTFile, `confCACERT', `')
554# Server Cert
555_OPTION(ServerCertFile, `confSERVER_CERT', `')
556# Server private key
557_OPTION(ServerKeyFile, `confSERVER_KEY', `')
558# Client Cert
559_OPTION(ClientCertFile, `confCLIENT_CERT', `')
560# Client private key
561_OPTION(ClientKeyFile, `confCLIENT_KEY', `')
562# DHParameters (only required if DSA/DH is used)
563_OPTION(DHParameters, `confDH_PARAMETERS', `')
564# Random data source (required for systems without /dev/urandom under OpenSSL)
565_OPTION(RandFile, `confRAND_FILE', `')
566
567ifdef(`confQUEUE_FILE_MODE',
568`# queue file mode (qf files)
569O QueueFileMode=confQUEUE_FILE_MODE
570')
571
572###########################
573#   Message precedences   #
574###########################
575
576Pfirst-class=0
577Pspecial-delivery=100
578Plist=-30
579Pbulk=-60
580Pjunk=-100
581
582#####################
583#   Trusted users   #
584#####################
585
586# this is equivalent to setting class "t"
587ifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `MAIL_SETTINGS_DIR`'trusted-users')
588Troot
589Tdaemon
590ifdef(`_NO_UUCP_', `dnl', `Tuucp')
591ifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
592
593#########################
594#   Format of headers   #
595#########################
596
597ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
598H?P?Return-Path: <$g>
599HReceived: confRECEIVED_HEADER
600H?D?Resent-Date: $a
601H?D?Date: $a
602H?F?Resent-From: confFROM_HEADER
603H?F?From: confFROM_HEADER
604H?x?Full-Name: $x
605# HPosted-Date: $a
606# H?l?Received-Date: $b
607H?M?Resent-Message-Id: <$t.$i@$j>
608H?M?Message-Id: <$t.$i@$j>
609
610#
611######################################################################
612######################################################################
613#####
614#####			REWRITING RULES
615#####
616######################################################################
617######################################################################
618
619############################################
620###  Ruleset 3 -- Name Canonicalization  ###
621############################################
622Scanonify=3
623
624# handle null input (translate to <@> special case)
625R$@			$@ <@>
626
627# strip group: syntax (not inside angle brackets!) and trailing semicolon
628R$*			$: $1 <@>			mark addresses
629R$* < $* > $* <@>	$: $1 < $2 > $3			unmark <addr>
630R@ $* <@>		$: @ $1				unmark @host:...
631R$* :: $* <@>		$: $1 :: $2			unmark node::addr
632R:`include': $* <@>	$: :`include': $1			unmark :`include':...
633R$* [ IPv6 $- ] <@>	$: $1 [ IPv6 $2 ]		unmark IPv6 addr
634R$* : $* [ $* ]		$: $1 : $2 [ $3 ] <@>		remark if leading colon
635R$* : $* <@>		$: $2				strip colon if marked
636R$* <@>			$: $1				unmark
637R$* ;			   $1				strip trailing semi
638R$* < $+ :; > $*	$@ $2 :; <@>			catch <list:;>
639R$* < $* ; >		   $1 < $2 >			bogus bracketed semi
640
641# null input now results from list:; syntax
642R$@			$@ :; <@>
643
644# strip angle brackets -- note RFC733 heuristic to get innermost item
645R$*			$: < $1 >			housekeeping <>
646R$+ < $* >		   < $2 >			strip excess on left
647R< $* > $+		   < $1 >			strip excess on right
648R<>			$@ < @ >			MAIL FROM:<> case
649R< $+ >			$: $1				remove housekeeping <>
650
651ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
652# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
653R@ $+ , $+		@ $1 : $2			change all "," to ":"
654
655# localize and dispose of route-based addresses
656R@ $+ : $+		$@ $>Canonify2 < @$1 > : $2	handle <route-addr>
657dnl',`dnl
658# strip route address <@a,@b,@c:user@d> -> <user@d>
659R@ $+ , $+		$2
660R@ $+ : $+		$2
661dnl')
662
663# find focus for list syntax
664R $+ : $* ; @ $+	$@ $>Canonify2 $1 : $2 ; < @ $3 >	list syntax
665R $+ : $* ;		$@ $1 : $2;			list syntax
666
667# find focus for @ syntax addresses
668R$+ @ $+		$: $1 < @ $2 >			focus on domain
669R$+ < $+ @ $+ >		$1 $2 < @ $3 >			move gaze right
670R$+ < @ $+ >		$@ $>Canonify2 $1 < @ $2 >	already canonical
671
672# do some sanity checking
673R$* < @ $* : $* > $*	$1 < @ $2 $3 > $4		nix colons in addrs
674
675ifdef(`_NO_UUCP_', `dnl',
676`# convert old-style addresses to a domain-based address
677R$- ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	resolve uucp names
678R$+ . $- ! $+		$@ $>Canonify2 $3 < @ $1 . $2 >		domain uucps
679R$+ ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	uucp subdomains
680')
681ifdef(`_USE_DECNET_SYNTAX_',
682`# convert node::user addresses into a domain-based address
683R$- :: $+		$@ $>Canonify2 $2 < @ $1 .DECNET >	resolve DECnet names
684R$- . $- :: $+		$@ $>Canonify2 $3 < @ $1.$2 .DECNET >	numeric DECnet addr
685',
686	`dnl')
687# if we have % signs, take the rightmost one
688R$* % $*		$1 @ $2				First make them all @s.
689R$* @ $* @ $*		$1 % $2 @ $3			Undo all but the last.
690R$* @ $*		$@ $>Canonify2 $1 < @ $2 >	Insert < > and finish
691
692# else we must be a local name
693R$*			$@ $>Canonify2 $1
694
695
696################################################
697###  Ruleset 96 -- bottom half of ruleset 3  ###
698################################################
699
700SCanonify2=96
701
702# handle special cases for local names
703R$* < @ localhost > $*		$: $1 < @ $j . > $2		no domain at all
704R$* < @ localhost . $m > $*	$: $1 < @ $j . > $2		local domain
705ifdef(`_NO_UUCP_', `dnl',
706`R$* < @ localhost . UUCP > $*	$: $1 < @ $j . > $2		.UUCP domain')
707
708# check for IPv6 domain literal (save quoted form)
709R$* < @ [ IPv6 $- ] > $*	$: $2 $| $1 < @@ [ $(dequote $2 $) ] > $3	mark IPv6 addr
710R$- $| $* < @@ $=w > $*		$: $2 < @ $j . > $4		self-literal
711R$- $| $* < @@ [ $+ ] > $*	$@ $2 < @ [ IPv6 $1 ] > $4	canon IP addr
712
713# check for IPv4 domain literal
714R$* < @ [ $+ ] > $*		$: $1 < @@ [ $2 ] > $3		mark [a.b.c.d]
715R$* < @@ $=w > $*		$: $1 < @ $j . > $3		self-literal
716R$* < @@ $+ > $*		$@ $1 < @ $2 > $3		canon IP addr
717
718ifdef(`_DOMAIN_TABLE_', `dnl
719# look up domains in the domain table
720R$* < @ $+ > $* 		$: $1 < @ $(domaintable $2 $) > $3', `dnl')
721
722undivert(2)dnl LOCAL_RULE_3
723
724ifdef(`_BITDOMAIN_TABLE_', `dnl
725# handle BITNET mapping
726R$* < @ $+ .BITNET > $*		$: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3', `dnl')
727
728ifdef(`_UUDOMAIN_TABLE_', `dnl
729# handle UUCP mapping
730R$* < @ $+ .UUCP > $*		$: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3', `dnl')
731
732ifdef(`_NO_UUCP_', `dnl',
733`ifdef(`UUCP_RELAY',
734`# pass UUCP addresses straight through
735R$* < @ $+ . UUCP > $*		$@ $1 < @ $2 . UUCP . > $3',
736`# if really UUCP, handle it immediately
737ifdef(`_CLASS_U_',
738`R$* < @ $=U . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
739ifdef(`_CLASS_V_',
740`R$* < @ $=V . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
741ifdef(`_CLASS_W_',
742`R$* < @ $=W . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
743ifdef(`_CLASS_X_',
744`R$* < @ $=X . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
745ifdef(`_CLASS_Y_',
746`R$* < @ $=Y . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
747
748ifdef(`_NO_CANONIFY_', `dnl', `dnl
749# try UUCP traffic as a local address
750R$* < @ $+ . UUCP > $*		$: $1 < @ $[ $2 $] . UUCP . > $3
751R$* < @ $+ . . UUCP . > $*	$@ $1 < @ $2 . > $3')
752')')
753# hostnames ending in class P are always canonical
754R$* < @ $* $=P > $*		$: $1 < @ $2 $3 . > $4
755dnl apply the next rule only for hostnames not in class P
756dnl this even works for phrases in class P since . is in class P
757dnl which daemon flags are set?
758R$* < @ $* $~P > $*		$: $&{daemon_flags} $| $1 < @ $2 $3 > $4
759dnl the other rules in this section only apply if the hostname
760dnl does not end in class P hence no further checks are done here
761dnl if this ever changes make sure the lookups are "protected" again!
762ifdef(`_NO_CANONIFY_', `dnl
763dnl do not canonify unless:
764dnl domain ends in class {Canonify} (this does not work if the intersection
765dnl	with class P is non-empty)
766dnl or {daemon_flags} has c set
767# pass to name server to make hostname canonical if in class {Canonify}
768R$* $| $* < @ $* $={Canonify} > $*	$: $2 < @ $[ $3 $4 $] > $5
769# pass to name server to make hostname canonical if requested
770R$* c $* $| $* < @ $* > $*	$: $3 < @ $[ $4 $] > $5
771dnl trailing dot? -> do not apply _CANONIFY_HOSTS_
772R$* $| $* < @ $+ . > $*		$: $2 < @ $3 . > $4
773# add a trailing dot to qualified hostnames so other rules will work
774R$* $| $* < @ $+.$+ > $*	$: $2 < @ $3.$4 . > $5
775ifdef(`_CANONIFY_HOSTS_', `dnl
776dnl this should only apply to unqualified hostnames
777dnl but if a valid character inside an unqualified hostname is an OperatorChar
778dnl then $- does not work.
779# lookup unqualified hostnames
780R$* $| $* < @ $* > $*	$: $2 < @ $[ $3 $] > $4', `dnl')', `dnl
781dnl _NO_CANONIFY_ is not set: canonify unless:
782dnl {daemon_flags} contains CC (do not canonify)
783dnl but add a trailing dot to qualified hostnames so other rules will work
784dnl should we do this for every hostname: even unqualified?
785R$* CC $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
786R$* CC $* $| $*			$: $3
787# pass to name server to make hostname canonical
788R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4')
789dnl remove {daemon_flags} for other cases
790R$* $| $*			$: $2
791
792# local host aliases and pseudo-domains are always canonical
793R$* < @ $=w > $*		$: $1 < @ $2 . > $3
794ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
795`R$* < @ $* $=M > $*		$: $1 < @ $2 $3 . > $4',
796`R$* < @ $=M > $*		$: $1 < @ $2 . > $3')
797ifdef(`_VIRTUSER_TABLE_', `dnl
798dnl virtual hosts are also canonical
799ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
800`R$* < @ $* $={VirtHost} > $* 	$: $1 < @ $2 $3 . > $4',
801`R$* < @ $={VirtHost} > $* 	$: $1 < @ $2 . > $3')',
802`dnl')
803dnl remove superfluous dots (maybe repeatedly) which may have been added
804dnl by one of the rules before
805R$* < @ $* . . > $*		$1 < @ $2 . > $3
806
807
808##################################################
809###  Ruleset 4 -- Final Output Post-rewriting  ###
810##################################################
811Sfinal=4
812
813R$+ :; <@>		$@ $1 :				handle <list:;>
814R$* <@>			$@				handle <> and list:;
815
816# strip trailing dot off possibly canonical name
817R$* < @ $+ . > $*	$1 < @ $2 > $3
818
819# eliminate internal code
820R$* < @ *LOCAL* > $*	$1 < @ $j > $2
821
822# externalize local domain info
823R$* < $+ > $*		$1 $2 $3			defocus
824R@ $+ : @ $+ : $+	@ $1 , @ $2 : $3		<route-addr> canonical
825R@ $*			$@ @ $1				... and exit
826
827ifdef(`_NO_UUCP_', `dnl',
828`# UUCP must always be presented in old form
829R$+ @ $- . UUCP		$2!$1				u@h.UUCP => h!u')
830
831ifdef(`_USE_DECNET_SYNTAX_',
832`# put DECnet back in :: form
833R$+ @ $+ . DECNET	$2 :: $1			u@h.DECNET => h::u',
834	`dnl')
835# delete duplicate local names
836R$+ % $=w @ $=w		$1 @ $2				u%host@host => u@host
837
838
839
840##############################################################
841###   Ruleset 97 -- recanonicalize and call ruleset zero   ###
842###		   (used for recursive calls)		   ###
843##############################################################
844
845SRecurse=97
846R$*			$: $>canonify $1
847R$*			$@ $>parse $1
848
849
850######################################
851###   Ruleset 0 -- Parse Address   ###
852######################################
853
854Sparse=0
855
856R$*			$: $>Parse0 $1		initial parsing
857R<@>			$#_LOCAL_ $: <@>		special case error msgs
858R$*			$: $>ParseLocal $1	handle local hacks
859R$*			$: $>Parse1 $1		final parsing
860
861#
862#  Parse0 -- do initial syntax checking and eliminate local addresses.
863#	This should either return with the (possibly modified) input
864#	or return with a #error mailer.  It should not return with a
865#	#mailer other than the #error mailer.
866#
867
868SParse0
869R<@>			$@ <@>			special case error msgs
870R$* : $* ; <@>		$#error $@ 5.1.3 $: "501 List:; syntax illegal for recipient addresses"
871R@ <@ $* >		< @ $1 >		catch "@@host" bogosity
872R<@ $+>			$#error $@ 5.1.3 $: "501 User address required"
873R$*			$: <> $1
874R<> $* < @ [ $+ ] > $*	$1 < @ [ $2 ] > $3
875R<> $* <$* : $* > $*	$#error $@ 5.1.3 $: "501 Colon illegal in host name part"
876R<> $*			$1
877R$* < @ . $* > $*	$#error $@ 5.1.2 $: "501 Invalid host name"
878R$* < @ $* .. $* > $*	$#error $@ 5.1.2 $: "501 Invalid host name"
879dnl comma only allowed before @; this check is not complete
880R$* , $~O $*		$#error $@ 5.1.2 $: "501 Invalid route address"
881
882# now delete the local info -- note $=O to find characters that cause forwarding
883R$* < @ > $*		$@ $>Parse0 $>canonify $1	user@ => user
884R< @ $=w . > : $*	$@ $>Parse0 $>canonify $2	@here:... -> ...
885R$- < @ $=w . >		$: $(dequote $1 $) < @ $2 . >	dequote "foo"@here
886R< @ $+ >		$#error $@ 5.1.3 $: "501 User address required"
887R$* $=O $* < @ $=w . >	$@ $>Parse0 $>canonify $1 $2 $3	...@here -> ...
888R$- 			$: $(dequote $1 $) < @ *LOCAL* >	dequote "foo"
889R< @ *LOCAL* >		$#error $@ 5.1.3 $: "501 User address required"
890R$* $=O $* < @ *LOCAL* >
891			$@ $>Parse0 $>canonify $1 $2 $3	...@*LOCAL* -> ...
892R$* < @ *LOCAL* >	$: $1
893
894#
895#  Parse1 -- the bottom half of ruleset 0.
896#
897
898SParse1
899ifdef(`_LDAP_ROUTING_', `dnl
900# handle LDAP routing for hosts in $={LDAPRoute}
901R$+ < @ $={LDAPRoute} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2>',
902`dnl')
903
904ifdef(`_MAILER_smtp_',
905`# handle numeric address spec
906dnl there is no check whether this is really an IP number
907R$* < @ [ $+ ] > $*	$: $>ParseLocal $1 < @ [ $2 ] > $3	numeric internet spec
908R$* < @ [ $+ ] > $*	$1 < @ [ $2 ] : $S > $3		Add smart host to path
909R$* < @ [ IPv6 $- ] : > $*
910		$#_SMTP_ $@ [ $(dequote $2 $) ] $: $1 < @ [IPv6 $2 ] > $3	no smarthost: send
911R$* < @ [ $+ ] : > $*	$#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3	no smarthost: send
912R$* < @ [ $+ ] : $- : $*> $*	$#$3 $@ $4 $: $1 < @ [$2] > $5	smarthost with mailer
913R$* < @ [ $+ ] : $+ > $*	$#_SMTP_ $@ $3 $: $1 < @ [$2] > $4	smarthost without mailer',
914	`dnl')
915
916ifdef(`_VIRTUSER_TABLE_', `dnl
917# handle virtual users
918R$+			$: <!> $1		Mark for lookup
919ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
920`R<!> $+ < @ $* $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >',
921`R<!> $+ < @ $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >')
922R<!> $+ < @ $=w . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
923R<@> $+ + $* < @ $* . >
924			$: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $: @ $) > $1 + $2 < @ $3 . >
925R<@> $+ + $* < @ $* . >
926			$: < $(virtuser $1 @ $3 $@ $1 $: @ $) > $1 + $2 < @ $3 . >
927dnl try default entry: @domain
928dnl +*@domain
929R<@> $+ + $+ < @ $+ . >	$: < $(virtuser + * @ $3 $@ $1 $@ $2 $: @ $) > $1 + $2 < @ $3 . >
930dnl @domain if +detail exists
931R<@> $+ + $* < @ $+ . >	$: < $(virtuser @ $3 $@ $1 $@ $2 $: @ $) > $1 + $2 < @ $3 . >
932dnl without +detail (or no match)
933R<@> $+ < @ $+ . >	$: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
934R<@> $+			$: $1
935R<!> $+			$: $1
936R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
937R< error : $- $+ > $* 	$#error $@ $(dequote $1 $) $: $2
938R< $+ > $+ < @ $+ >	$: $>Recurse $1',
939`dnl')
940
941# short circuit local delivery so forwarded email works
942ifdef(`_MAILER_usenet_', `dnl
943R$+ . USENET < @ $=w . >	$#usenet $@ usenet $: $1	handle usenet specially', `dnl')
944
945
946ifdef(`_STICKY_LOCAL_DOMAIN_',
947`R$+ < @ $=w . >		$: < $H > $1 < @ $2 . >		first try hub
948R< $+ > $+ < $+ >	$>MailerToTriple < $1 > $2 < $3 >	yep ....
949dnl $H empty (but @$=w.)
950R< > $+ + $* < $+ >	$#_LOCAL_ $: $1 + $2		plussed name?
951R< > $+ < $+ >		$#_LOCAL_ $: @ $1			nope, local address',
952`R$=L < @ $=w . >	$#_LOCAL_ $: @ $1			special local names
953R$+ < @ $=w . >		$#_LOCAL_ $: $1			regular local name')
954
955ifdef(`_MAILER_TABLE_', `dnl
956# not local -- try mailer table lookup
957R$* <@ $+ > $*		$: < $2 > $1 < @ $2 > $3	extract host name
958R< $+ . > $*		$: < $1 > $2			strip trailing dot
959R< $+ > $*		$: < $(mailertable $1 $) > $2	lookup
960dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
961R< $~[ : $* > $* 	$>MailerToTriple < $1 : $2 > $3		check -- resolved?
962R< $+ > $*		$: $>Mailertable <$1> $2		try domain',
963`dnl')
964undivert(4)dnl UUCP rules from `MAILER(uucp)'
965
966ifdef(`_NO_UUCP_', `dnl',
967`# resolve remotely connected UUCP links (if any)
968ifdef(`_CLASS_V_',
969`R$* < @ $=V . UUCP . > $*		$: $>MailerToTriple < $V > $1 <@$2.UUCP.> $3',
970	`dnl')
971ifdef(`_CLASS_W_',
972`R$* < @ $=W . UUCP . > $*		$: $>MailerToTriple < $W > $1 <@$2.UUCP.> $3',
973	`dnl')
974ifdef(`_CLASS_X_',
975`R$* < @ $=X . UUCP . > $*		$: $>MailerToTriple < $X > $1 <@$2.UUCP.> $3',
976	`dnl')')
977
978# resolve fake top level domains by forwarding to other hosts
979ifdef(`BITNET_RELAY',
980`R$*<@$+.BITNET.>$*	$: $>MailerToTriple < $B > $1 <@$2.BITNET.> $3	user@host.BITNET',
981	`dnl')
982ifdef(`DECNET_RELAY',
983`R$*<@$+.DECNET.>$*	$: $>MailerToTriple < $C > $1 <@$2.DECNET.> $3	user@host.DECNET',
984	`dnl')
985ifdef(`_MAILER_pop_',
986`R$+ < @ POP. >		$#pop $: $1			user@POP',
987	`dnl')
988ifdef(`_MAILER_fax_',
989`R$+ < @ $+ .FAX. >	$#fax $@ $2 $: $1		user@host.FAX',
990`ifdef(`FAX_RELAY',
991`R$*<@$+.FAX.>$*		$: $>MailerToTriple < $F > $1 <@$2.FAX.> $3	user@host.FAX',
992	`dnl')')
993
994ifdef(`UUCP_RELAY',
995`# forward non-local UUCP traffic to our UUCP relay
996R$*<@$*.UUCP.>$*		$: $>MailerToTriple < $Y > $1 <@$2.UUCP.> $3	uucp mail',
997`ifdef(`_MAILER_uucp_',
998`# forward other UUCP traffic straight to UUCP
999R$* < @ $+ .UUCP. > $*		$#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3	user@host.UUCP',
1000	`dnl')')
1001ifdef(`_MAILER_usenet_', `
1002# addresses sent to net.group.USENET will get forwarded to a newsgroup
1003R$+ . USENET		$#usenet $@ usenet $: $1',
1004	`dnl')
1005
1006ifdef(`_LOCAL_RULES_',
1007`# figure out what should stay in our local mail system
1008undivert(1)', `dnl')
1009
1010# pass names that still have a host to a smarthost (if defined)
1011R$* < @ $* > $*		$: $>MailerToTriple < $S > $1 < @ $2 > $3	glue on smarthost name
1012
1013# deal with other remote names
1014ifdef(`_MAILER_smtp_',
1015`R$* < @$* > $*		$#_SMTP_ $@ $2 $: $1 < @ $2 > $3	user@host.domain',
1016`R$* < @$* > $*		$#error $@ 5.1.2 $: "501 Unrecognized host name " $2')
1017
1018# handle locally delivered names
1019R$=L			$#_LOCAL_ $: @ $1		special local names
1020R$+			$#_LOCAL_ $: $1			regular local names
1021
1022###########################################################################
1023###   Ruleset 5 -- special rewriting after aliases have been expanded   ###
1024###########################################################################
1025
1026SLocal_localaddr
1027Slocaladdr=5
1028R$+			$: $1 $| $>"Local_localaddr" $1
1029R$+ $| $#$*		$#$2
1030R$+ $| $*		$: $1
1031
1032ifdef(`_FFR_5_', `
1033# Preserve host in a macro
1034R$+			$: $(macro {LocalAddrHost} $) $1
1035R$+ @ $+		$: $(macro {LocalAddrHost} $@ @ $2 $) $1')
1036
1037ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `
1038# deal with plussed users so aliases work nicely
1039R$+ + *			$#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1040R$+ + $*		$#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1041')
1042# prepend an empty "forward host" on the front
1043R$+			$: <> $1
1044
1045ifdef(`LUSER_RELAY', `dnl
1046# send unrecognized local users to a relay host
1047ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `
1048R< > $+ + $*		$: < ? $L > <+ $2> $(user $1 $)	look up user+
1049R< > $+			$: < ? $L > < > $(user $1 $)	look up user
1050R< ? $* > < $* > $+ <>	$: < > $3 $2			found; strip $L
1051R< ? $* > < $* > $+	$: < $1 > $3 $2			not found', `
1052R< > $+ 		$: < $L > $(user $1 $)		look up user
1053R< $* > $+ <>		$: < > $2			found; strip $L')',
1054`dnl')
1055
1056# see if we have a relay or a hub
1057R< > $+			$: < $H > $1			try hub
1058R< > $+			$: < $R > $1			try relay
1059ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `
1060R< > $+			$@ $1', `
1061R< > $+			$: < > < $1 <> $&h >		nope, restore +detail
1062R< > < $+ <> + $* >	$: < > < $1 + $2 >		check whether +detail
1063R< > < $+ <> $* >	$: < > < $1 >			else discard
1064R< > < $+ + $* > $*	   < > < $1 > + $2 $3		find the user part
1065R< > < $+ > + $*	$#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')		strip the extra +
1066R< > < $+ >		$@ $1				no +detail
1067R$+			$: $1 <> $&h			add +detail back in
1068R$+ <> + $*		$: $1 + $2			check whether +detail
1069R$+ <> $*		$: $1				else discard')
1070R< local : $* > $*	$: $>MailerToTriple < local : $1 > $2	no host extension
1071R< error : $* > $*	$: $>MailerToTriple < error : $1 > $2	no host extension
1072R< $- : $+ > $+		$: $>MailerToTriple < $1 : $2 > $3 < @ $2 >
1073R< $+ > $+		$@ $>MailerToTriple < $1 > $2 < @ $1 >
1074
1075ifdef(`_MAILER_TABLE_', `dnl
1076###################################################################
1077###  Ruleset 90 -- try domain part of mailertable entry 	###
1078dnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress
1079###################################################################
1080
1081SMailertable=90
1082dnl shift and check
1083dnl %2 is not documented in cf/README
1084R$* <$- . $+ > $*	$: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
1085dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1086R$* <$~[ : $* > $*	$>MailerToTriple < $2 : $3 > $4		check -- resolved?
1087R$* < . $+ > $* 	$@ $>Mailertable $1 . <$2> $3		no -- strip & try again
1088dnl is $2 always empty?
1089R$* < $* > $*		$: < $(mailertable . $@ $1$2 $) > $3	try "."
1090R< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		"." found?
1091dnl return full address
1092R< $* > $*		$@ $2				no mailertable match',
1093`dnl')
1094
1095###################################################################
1096###  Ruleset 95 -- canonify mailer:[user@]host syntax to triple	###
1097dnl input: in general: <[mailer:]host> lp<@domain>rest
1098dnl	<> address				-> address
1099dnl	<error:d.s.n:text>			-> error
1100dnl	<error:text>				-> error
1101dnl	<mailer:user@host> lp<@domain>rest	-> mailer host user
1102dnl	<mailer:host> address			-> mailer host address
1103dnl	<localdomain> address			-> address
1104dnl	<[IPv6 number]> address			-> relay number address
1105dnl	<host> address				-> relay host address
1106###################################################################
1107
1108SMailerToTriple=95
1109R< > $*				$@ $1			strip off null relay
1110R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
1111R< error : $- $+ > $*		$#error $@ $(dequote $1 $) $: $2
1112R< local : $* > $*		$>CanonLocal < $1 > $2
1113R< $- : $+ @ $+ > $*<$*>$*	$# $1 $@ $3 $: $2<@$3>	use literal user
1114R< $- : $+ > $*			$# $1 $@ $2 $: $3	try qualified mailer
1115R< $=w > $*			$@ $2			delete local host
1116R< [ IPv6 $+ ] > $*		$#_RELAY_ $@ $(dequote $1 $) $: $2	use unqualified mailer
1117R< $+ > $*			$#_RELAY_ $@ $1 $: $2	use unqualified mailer
1118
1119###################################################################
1120###  Ruleset CanonLocal -- canonify local: syntax		###
1121dnl input: <user> address
1122dnl <x> <@host> : rest			-> Recurse rest
1123dnl <x> p1 $=O p2 <@host>		-> Recurse p1 $=O p2
1124dnl <> user <@host> rest		-> local user@host user
1125dnl <> user				-> local user user
1126dnl <user@host> lp <@domain> rest	-> <user> lp <@host> [cont]
1127dnl <user> lp <@host> rest		-> local lp@host user
1128dnl <user> lp				-> local lp user
1129###################################################################
1130
1131SCanonLocal
1132# strip local host from routed addresses
1133R< $* > < @ $+ > : $+		$@ $>Recurse $3
1134R< $* > $+ $=O $+ < @ $+ >	$@ $>Recurse $2 $3 $4
1135
1136# strip trailing dot from any host name that may appear
1137R< $* > $* < @ $* . >		$: < $1 > $2 < @ $3 >
1138
1139# handle local: syntax -- use old user, either with or without host
1140R< > $* < @ $* > $*		$#_LOCAL_ $@ $1@$2 $: $1
1141R< > $+				$#_LOCAL_ $@ $1    $: $1
1142
1143# handle local:user@host syntax -- ignore host part
1144R< $+ @ $+ > $* < @ $* >	$: < $1 > $3 < @ $4 >
1145
1146# handle local:user syntax
1147R< $+ > $* <@ $* > $*		$#_LOCAL_ $@ $2@$3 $: $1
1148R< $+ > $* 			$#_LOCAL_ $@ $2    $: $1
1149
1150###################################################################
1151###  Ruleset 93 -- convert header names to masqueraded form	###
1152###################################################################
1153
1154SMasqHdr=93
1155
1156ifdef(`_GENERICS_TABLE_', `dnl
1157# handle generics database
1158ifdef(`_GENERICS_ENTIRE_DOMAIN_',
1159dnl if generics should be applied add a @ as mark
1160`R$+ < @ $* $=G . >	$: < $1@$2$3 > $1 < @ $2$3 . > @	mark',
1161`R$+ < @ $=G . >	$: < $1@$2 > $1 < @ $2 . > @	mark')
1162R$+ < @ *LOCAL* >	$: < $1@$j > $1 < @ *LOCAL* > @	mark
1163dnl workspace: either user<@domain> or <user@domain> user <@domain> @
1164dnl ignore the first case for now
1165dnl if it has the mark lookup full address
1166R< $+ > $+ < $* > @	$: < $(generics $1 $: @ $1 $) > $2 < $3 >
1167dnl workspace: ... or <match|@user@domain> user <@domain>
1168dnl no match, try user+detail@domain
1169R<@$+ + $* @ $+> $+ < @ $+ >
1170		$: < $(generics $1+*@$3 $@ $2 $:@$1 + $2@$3 $) >  $4 < @ $5 >
1171R<@$+ + $* @ $+> $+ < @ $+ >
1172		$: < $(generics $1@$3 $: $) > $4 < @ $5 >
1173dnl no match, remove mark
1174R<@$+ > $+ < @ $+ >	$: < > $2 < @ $3 >
1175dnl no match, try @domain for exceptions
1176R< > $+ < @ $+ . >	$: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . >
1177dnl workspace: ... or <match> user <@domain>
1178dnl no match, try local part
1179R< > $+ < @ $+ > 	$: < $(generics $1 $: $) > $1 < @ $2 >
1180R< > $+ + $* < @ $+ > 	$: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 >
1181R< > $+ + $* < @ $+ > 	$: < $(generics $1 $: $) > $1 + $2 < @ $3 >
1182R< $* @ $* > $* < $* >	$@ $>canonify $1 @ $2		found qualified
1183R< $+ > $* < $* >	$: $>canonify $1 @ *LOCAL*	found unqualified
1184R< > $*			$: $1				not found',
1185`dnl')
1186
1187# do not masquerade anything in class N
1188R$* < @ $* $=N . >	$@ $1 < @ $2 $3 . >
1189
1190# special case the users that should be exposed
1191R$=E < @ *LOCAL* >	$@ $1 < @ $j . >		leave exposed
1192ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1193`R$=E < @ $* $=M . >	$@ $1 < @ $2 $3 . >',
1194`R$=E < @ $=M . >	$@ $1 < @ $2 . >')
1195ifdef(`_LIMITED_MASQUERADE_', `dnl',
1196`R$=E < @ $=w . >	$@ $1 < @ $2 . >')
1197
1198# handle domain-specific masquerading
1199ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1200`R$* < @ $* $=M . > $*	$: $1 < @ $2 $3 . @ $M > $4	convert masqueraded doms',
1201`R$* < @ $=M . > $*	$: $1 < @ $2 . @ $M > $3	convert masqueraded doms')
1202ifdef(`_LIMITED_MASQUERADE_', `dnl',
1203`R$* < @ $=w . > $*	$: $1 < @ $2 . @ $M > $3')
1204R$* < @ *LOCAL* > $*	$: $1 < @ $j . @ $M > $2
1205R$* < @ $+ @ > $*	$: $1 < @ $2 > $3		$M is null
1206R$* < @ $+ @ $+ > $*	$: $1 < @ $3 . > $4		$M is not null
1207
1208###################################################################
1209###  Ruleset 94 -- convert envelope names to masqueraded form	###
1210###################################################################
1211
1212SMasqEnv=94
1213ifdef(`_MASQUERADE_ENVELOPE_',
1214`R$+			$@ $>MasqHdr $1',
1215`R$* < @ *LOCAL* > $*	$: $1 < @ $j . > $2')
1216
1217###################################################################
1218###  Ruleset 98 -- local part of ruleset zero (can be null)	###
1219###################################################################
1220
1221SParseLocal=98
1222undivert(3)dnl LOCAL_RULE_0
1223
1224ifdef(`_LDAP_ROUTING_', `dnl
1225SLDAPExpand
1226# do the LDAP lookups
1227R<$+><$+>		$: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2>
1228
1229# if mailRoutingAddress and local or non-existant mailHost,
1230# return the new mailRoutingAddress
1231R< $+ > < $=w > < $+ > < $+ >	$@ $>Parse0 $>canonify $1
1232R< $+ > <  > < $+ > < $+ >	$@ $>Parse0 $>canonify $1
1233
1234# if mailRoutingAddress and non-local mailHost,
1235# relay to mailHost with new mailRoutingAddress
1236R< $+ > < $+ > < $+ > < $+ >	$#_RELAY_ $@ $2 $: $>canonify $1
1237
1238# if no mailRoutingAddress and local mailHost,
1239# return original address
1240R< > < $=w > <$+> <$+>		$@ $2
1241
1242# if no mailRoutingAddress and non-local mailHost,
1243# relay to mailHost with original address
1244R< > < $+ > <$+> <$+>		$#_RELAY_ $@ $1 $: $2
1245
1246# if no mailRoutingAddress and no mailHost,
1247# try @domain
1248R< > < > <$+> <$+ @ $+>		$@ $>LDAPExpand <$1> <@ $3>
1249
1250# if no mailRoutingAddress and no mailHost and this was a domain attempt,
1251ifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl
1252# user does not exist
1253R< > < > <$+> <@ $+>		$#error $@ nouser $: "550 User unknown"',
1254`dnl
1255# return the original address
1256R< > < > <$+> <@ $+>		$@ $1')',
1257`dnl')
1258
1259ifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode.
1260')')
1261ifdef(`_ACCESS_TABLE_', `dnl
1262######################################################################
1263###  LookUpDomain -- search for domain in access database
1264###
1265###	Parameters:
1266###		<$1> -- key (domain name)
1267###		<$2> -- default (what to return if not found in db)
1268dnl			must not be empty
1269###		<$3> -- passthru (additional data passed unchanged through)
1270###		<$4> -- mark (must be <(!|+) single-token>)
1271###			! does lookup only with tag
1272###			+ does lookup with and without tag
1273dnl returns:		<default> <passthru>
1274dnl 			<result> <passthru>
1275######################################################################
1276
1277SLookUpDomain
1278dnl remove IPv6 mark and dequote address
1279dnl it is a bit ugly because it is checked on each "iteration"
1280R<[IPv6 $-]> <$+> <$*> <$*>	$: <[$(dequote $1 $)]> <$2> <$3> <$4>
1281dnl workspace <key> <default> <passthru> <mark>
1282dnl lookup with tag (in front, no delimiter here)
1283R<$*> <$+> <$*> <$- $->		$: < $(access $5`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3> <$4 $5>
1284dnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark>
1285ifdef(`_FFR_LOOKUPDOTDOMAIN', `dnl omit first component: lookup .rest
1286R<?> <$+.$+> <$+> <$*> <$- $->	$: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4> <$5 $6>', `dnl')
1287dnl lookup without tag?
1288R<?> <$+> <$+> <$*> <+ $*>	$: < $(access $1 $: ? $) > <$1> <$2> <$3> <+ $4>
1289ifdef(`_FFR_LOOKUPDOTDOMAIN', `dnl omit first component: lookup .rest
1290R<?> <$+.$+> <$+> <$*> <+ $*>	$: < $(access .$2 $: ? $) > <$1.$2> <$3> <$4> <+ $5>', `dnl')
1291dnl lookup IP address (no check is done whether it is an IP number!)
1292R<?> <[$+.$-]> <$+> <$*> <$*>	$@ $>LookUpDomain <[$1]> <$3> <$4> <$5>
1293dnl lookup IPv6 address
1294R<?> <[$+::$-]> <$+> <$*> <$*>	$: $>LookUpDomain <[$1]> <$3> <$4> <$5>
1295R<?> <[$+:$-]> <$+> <$*> <$*>	$: $>LookUpDomain <[$1]> <$3> <$4> <$5>
1296dnl not found, but subdomain: try again
1297R<?> <$+.$+> <$+> <$*> <$*>	$@ $>LookUpDomain <$2> <$3> <$4> <$5>
1298dnl not found, no subdomain: return default
1299R<?> <$+> <$+> <$*> <$*>	$@ <$2> <$3>
1300dnl return result of lookup
1301R<$*> <$+> <$+> <$*> <$*>	$@ <$1> <$4>
1302
1303######################################################################
1304###  LookUpAddress -- search for host address in access database
1305###
1306###	Parameters:
1307###		<$1> -- key (dot quadded host address)
1308###		<$2> -- default (what to return if not found in db)
1309dnl			must not be empty
1310###		<$3> -- passthru (additional data passed through)
1311###		<$4> -- mark (must be <(!|+) single-token>)
1312###			! does lookup only with tag
1313###			+ does lookup with and without tag
1314dnl	returns:	<default> <passthru>
1315dnl			<result> <passthru>
1316######################################################################
1317
1318SLookUpAddress
1319dnl lookup with tag
1320R<$+> <$+> <$*> <$- $+>		$: < $(access $5`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3> <$4 $5>
1321dnl lookup without tag
1322R<?> <$+> <$+> <$*> <+ $+>	$: < $(access $1 $: ? $) > <$1> <$2> <$3> <+ $4>
1323dnl no match; IPv6: remove last part
1324R<?> <$+::$-> <$+> <$*> <$*>	$@ $>LookUpAddress <$1> <$3> <$4> <$5>
1325R<?> <$+:$-> <$+> <$*> <$*>	$@ $>LookUpAddress <$1> <$3> <$4> <$5>
1326dnl no match; IPv4: remove last part
1327R<?> <$+.$-> <$+> <$*> <$*>	$@ $>LookUpAddress <$1> <$3> <$4> <$5>
1328dnl no match: return default
1329R<?> <$+> <$+> <$*> <$*>	$@ <$2> <$3>
1330dnl match: return result
1331R<$*> <$+> <$+> <$*> <$*>	$@ <$1> <$4>',
1332`dnl')
1333
1334######################################################################
1335###  CanonAddr --	Convert an address into a standard form for
1336###			relay checking.  Route address syntax is
1337###			crudely converted into a %-hack address.
1338###
1339###	Parameters:
1340###		$1 -- full recipient address
1341###
1342###	Returns:
1343###		parsed address, not in source route form
1344dnl		user%host%host<@domain>
1345dnl		host!user<@domain>
1346######################################################################
1347
1348SCanonAddr
1349R$*			$: $>Parse0 $>canonify $1	make domain canonical
1350ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
1351R< @ $+ > : $* @ $*	< @ $1 > : $2 % $3	change @ to % in src route
1352R$* < @ $+ > : $* : $*	$3 $1 < @ $2 > : $4	change to % hack.
1353R$* < @ $+ > : $*	$3 $1 < @ $2 >
1354dnl')
1355
1356######################################################################
1357###  ParseRecipient --	Strip off hosts in $=R as well as possibly
1358###			$* $=m or the access database.
1359###			Check user portion for host separators.
1360###
1361###	Parameters:
1362###		$1 -- full recipient address
1363###
1364###	Returns:
1365###		parsed, non-local-relaying address
1366######################################################################
1367
1368SParseRecipient
1369dnl mark and canonify address
1370R$*				$: <?> $>CanonAddr $1
1371dnl workspace: <?> localpart<@domain[.]>
1372R<?> $* < @ $* . >		<?> $1 < @ $2 >			strip trailing dots
1373dnl workspace: <?> localpart<@domain>
1374R<?> $- < @ $* >		$: <?> $(dequote $1 $) < @ $2 >	dequote local part
1375
1376# if no $=O character, no host in the user portion, we are done
1377R<?> $* $=O $* < @ $* >		$: <NO> $1 $2 $3 < @ $4>
1378dnl no $=O in localpart: return
1379R<?> $*				$@ $1
1380
1381dnl workspace: <?> localpart<@domain>, where localpart contains $=O
1382dnl mark everything which has an "authorized" domain with <RELAY>
1383ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1384# if we relay, check username portion for user%host so host can be checked also
1385R<NO> $* < @ $* $=m >		$: <RELAY> $1 < @ $2 $3 >', `dnl')
1386
1387ifdef(`_RELAY_MX_SERVED_', `dnl
1388dnl do "we" ($=w) act as backup MX server for the destination domain?
1389R<NO> $* < @ $+ >		$: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > >
1390R<MX> < : $* <TEMP> : > $*	$#error $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1
1391dnl yes: mark it as <RELAY>
1392R<MX> < $* : $=w. : $* > < $+ >	$: <RELAY> $4
1393dnl no: put old <NO> mark back
1394R<MX> < : $* : > < $+ >		$: <NO> $2', `dnl')
1395
1396dnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O
1397dnl if mark is <NO> then change it to <RELAY> if domain is "authorized"
1398ifdef(`_RELAY_HOSTS_ONLY_',
1399`R<NO> $* < @ $=R >		$: <RELAY> $1 < @ $2 >
1400ifdef(`_ACCESS_TABLE_', `dnl
1401R<NO> $* < @ $+ >		$: <$(access To:$2 $: NO $)> $1 < @ $2 >
1402R<NO> $* < @ $+ >		$: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')',
1403`R<NO> $* < @ $* $=R >		$: <RELAY> $1 < @ $2 $3 >
1404ifdef(`_ACCESS_TABLE_', `dnl
1405R<NO> $* < @ $+ >		$: $>LookUpDomain <$2> <NO> <$1 < @ $2 >> <+To>
1406R<$+> <$+>			$: <$1> $2',`dnl')')
1407
1408
1409R<RELAY> $* < @ $* >		$@ $>ParseRecipient $1
1410R<$-> $*			$@ $2
1411
1412
1413######################################################################
1414###  check_relay -- check hostname/address on SMTP startup
1415######################################################################
1416
1417SLocal_check_relay
1418Scheck`'_U_`'relay
1419R$*			$: $1 $| $>"Local_check_relay" $1
1420R$* $| $* $| $#$*	$#$3
1421R$* $| $* $| $*		$@ $>"Basic_check_relay" $1 $| $2
1422
1423SBasic_check_relay
1424# check for deferred delivery mode
1425R$*			$: < ${deliveryMode} > $1
1426R< d > $*		$@ deferred
1427R< $* > $*		$: $2
1428
1429ifdef(`_ACCESS_TABLE_', `dnl
1430dnl workspace: {client_name} $| {client_addr}
1431R$+ $| $+		$: $>LookUpDomain < $1 > <?> < $2 > <+Connect>
1432dnl workspace: <result-of-lookup> <{client_addr}>
1433R<?> <$+>		$: $>LookUpAddress < $1 > <?> < $1 > <+Connect>	no: another lookup
1434dnl workspace: <result-of-lookup> <{client_addr}>
1435R<?> < $+ >		$: $1					found nothing
1436dnl workspace: <result-of-lookup> <{client_addr}>
1437dnl or {client_addr}
1438R<$={Accept}> < $* >	$@ $1				return value of lookup
1439R<REJECT> $*		$#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"')
1440R<DISCARD> $*		$#discard $: discard
1441dnl error tag
1442R<ERROR:$-.$-.$-:$+> <$*>	$#error $@ $1.$2.$3 $: $4
1443R<ERROR:$+> <$*>		$#error $: $1
1444dnl generic error from access map
1445R<$+> <$*>		$#error $: $1', `dnl')
1446
1447ifdef(`_RBL_',`dnl
1448# DNS based IP address spam list
1449R$*			$: $&{client_addr}
1450R::ffff:$-.$-.$-.$-	$: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
1451R$-.$-.$-.$-		$: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
1452R<?>OK			$: OKSOFAR
1453R<?>$+			$#error $@ 5.7.1 $: "550 Mail from " $&{client_addr} " refused by blackhole site _RBL_"',
1454`dnl')
1455undivert(8)
1456
1457######################################################################
1458###  check_mail -- check SMTP ``MAIL FROM:'' command argument
1459######################################################################
1460
1461SLocal_check_mail
1462Scheck`'_U_`'mail
1463R$*			$: $1 $| $>"Local_check_mail" $1
1464R$* $| $#$*		$#$2
1465R$* $| $*		$@ $>"Basic_check_mail" $1
1466
1467SBasic_check_mail
1468# check for deferred delivery mode
1469R$*			$: < ${deliveryMode} > $1
1470R< d > $*		$@ deferred
1471R< $* > $*		$: $2
1472
1473# authenticated?
1474dnl done first: we can require authentication for every mail transaction
1475dnl workspace: address as given by MAIL FROM: (sender)
1476R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
1477R$* $| $#$+		$#$2
1478dnl undo damage: remove result of tls_client call
1479R$* $| $*		$: $1
1480
1481dnl workspace: address as given by MAIL FROM:
1482R<>			$@ <OK>			we MUST accept <> (RFC 1123)
1483ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1484dnl do some additional checks
1485dnl no user@host
1486dnl no user@localhost (if nonlocal sender)
1487dnl this is a pretty simple canonification, it will not catch every case
1488dnl just make sure the address has <> around it (which is required by
1489dnl the RFC anyway, maybe we should complain if they are missing...)
1490dnl dirty trick: if it is user@host, just add a dot: user@host. this will
1491dnl not be modified by host lookups.
1492R$+			$: <?> $1
1493R<?><$+>		$: <@> <$1>
1494R<?>$+			$: <@> <$1>
1495dnl workspace: <@> <address>
1496dnl prepend daemon_flags
1497R$*			$: $&{daemon_flags} $| $1
1498dnl workspace: ${daemon_flags} $| <@> <address>
1499dnl do not allow these at all or only from local systems?
1500R$* f $* $| <@> < $* @ $- >	$: < ? $&{client_name} > < $3 @ $4 >
1501dnl accept unqualified sender: change mark to avoid test
1502R$* u $* $| <@> < $* >	$: <?> < $3 >
1503dnl workspace: ${daemon_flags} $| <@> <address>
1504dnl        or:                    <? ${client_name} > <address>
1505dnl        or:                    <?> <address>
1506dnl remove daemon_flags
1507R$* $| $*		$: $2
1508# handle case of @localhost on address
1509R<@> < $* @ localhost >	$: < ? $&{client_name} > < $1 @ localhost >
1510R<@> < $* @ [127.0.0.1] >
1511			$: < ? $&{client_name} > < $1 @ [127.0.0.1] >
1512R<@> < $* @ localhost.$m >
1513			$: < ? $&{client_name} > < $1 @ localhost.$m >
1514ifdef(`_NO_UUCP_', `dnl',
1515`R<@> < $* @ localhost.UUCP >
1516			$: < ? $&{client_name} > < $1 @ localhost.UUCP >')
1517dnl workspace: < ? $&{client_name} > <user@localhost|host>
1518dnl	or:    <@> <address>
1519dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
1520R<@> $*			$: $1			no localhost as domain
1521dnl workspace: < ? $&{client_name} > <user@localhost|host>
1522dnl	or:    <address>
1523dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
1524R<? $=w> $*		$: $2			local client: ok
1525R<? $+> <$+>		$#error $@ 5.5.4 $: "501 Real domain name required for sender address"
1526dnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags})
1527R<?> $*			$: $1')
1528dnl workspace: address (or <address>)
1529R$*			$: <?> $>CanonAddr $1		canonify sender address and mark it
1530dnl workspace: <?> CanonicalAddress (i.e. address in canonical form localpart<@host>)
1531dnl there is nothing behind the <@host> so no trailing $* needed
1532R<?> $* < @ $+ . >	<?> $1 < @ $2 >			strip trailing dots
1533# handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
1534R<?> $* < @ $* $=P >	$: <OK> $1 < @ $2 $3 >
1535dnl workspace <mark> CanonicalAddress	where mark is ? or OK
1536ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',
1537`R<?> $* < @ $+ >	$: <OK> $1 < @ $2 >		... unresolvable OK',
1538`R<?> $* < @ $+ >	$: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 >
1539R<? $* <$->> $* < @ $+ >
1540			$: <$2> $3 < @ $4 >')
1541dnl workspace <mark> CanonicalAddress	where mark is ?, OK, PERM, TEMP
1542dnl mark is ? iff the address is user (wo @domain)
1543
1544ifdef(`_ACCESS_TABLE_', `dnl
1545# check sender address: user@address, user@, address
1546dnl should we remove +ext from user?
1547dnl workspace: <mark> CanonicalAddress where mark is: ?, OK, PERM, TEMP
1548R<$+> $+ < @ $* >	$: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <H:$3>
1549R<$+> $+		$: @<$1> <$2> $| <U:$2@>
1550dnl workspace: @<mark> <CanonicalAddress> $| <@type:address> ....
1551dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1552dnl will only return user<@domain when "reversing" the args
1553R@ <$+> <$*> $| <$+>	$: <@> <$1> <$2> $| $>SearchList <+From> $| <$3> <>
1554dnl workspace: <@><mark> <CanonicalAddress> $| <result>
1555R<@> <$+> <$*> $| <$*>	$: <$3> <$1> <$2>		reverse result
1556dnl workspace: <result> <mark> <CanonicalAddress>
1557# retransform for further use
1558dnl required form:
1559dnl <ResultOfLookup|mark> CanonicalAddress
1560R<?> <$+> <$*>		$: <$1> $2	no match
1561R<$+> <$+> <$*>		$: <$1> $3	relevant result, keep it', `dnl')
1562dnl workspace <ResultOfLookup|mark> CanonicalAddress
1563dnl mark is ? iff the address is user (wo @domain)
1564
1565ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1566# handle case of no @domain on address
1567dnl prepend daemon_flags
1568R<?> $*			$: $&{daemon_flags} $| <?> $1
1569dnl accept unqualified sender: change mark to avoid test
1570R$* u $* $| <?> $*	$: <OK> $3
1571dnl remove daemon_flags
1572R$* $| $*		$: $2
1573R<?> $*			$: < ? $&{client_name} > $1
1574R<?> $*			$@ <OK>				...local unqualed ok
1575R<? $+> $*		$#error $@ 5.5.4 $: "501 Domain name required for sender address " $&f
1576							...remote is not')
1577# check results
1578R<?> $*			$: @ $1		mark address: nothing known about it
1579R<OK> $*		$@ <OK>
1580R<TEMP> $*		$#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
1581R<PERM> $*		$#error $@ 5.1.8 $: "501 Domain of sender address " $&f " does not exist"
1582ifdef(`_ACCESS_TABLE_', `dnl
1583R<$={Accept}> $*	$# $1
1584R<DISCARD> $*		$#discard $: discard
1585R<REJECT> $*		$#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"')
1586dnl error tag
1587R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
1588R<ERROR:$+> $*		$#error $: $1
1589dnl generic error from access map
1590R<$+> $*		$#error $: $1		error from access db',
1591`dnl')
1592
1593######################################################################
1594###  check_rcpt -- check SMTP ``RCPT TO:'' command argument
1595######################################################################
1596
1597SLocal_check_rcpt
1598Scheck`'_U_`'rcpt
1599R$*			$: $1 $| $>"Local_check_rcpt" $1
1600R$* $| $#$*		$#$2
1601R$* $| $*		$@ $>"Basic_check_rcpt" $1
1602
1603SBasic_check_rcpt
1604# check for deferred delivery mode
1605R$*			$: < ${deliveryMode} > $1
1606R< d > $*		$@ deferred
1607R< $* > $*		$: $2
1608
1609ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
1610# require qualified recipient?
1611R$+			$: <?> $1
1612R<?><$+>		$: <@> <$1>
1613R<?>$+			$: <@> <$1>
1614dnl prepend daemon_flags
1615R$*			$: $&{daemon_flags} $| $1
1616dnl workspace: ${daemon_flags} $| <@> <address>
1617dnl do not allow these at all or only from local systems?
1618R$* r $* $| <@> < $* @ $- >	$: < ? $&{client_name} > < $3 @ $4 >
1619R<?> < $* >		$: <$1>
1620R<? $=w> < $* >		$: <$1>
1621R<? $+> <$+>		$#error $@ 5.5.4 $: "553 Domain name required"
1622dnl remove daemon_flags for other cases
1623R$* $| <@> $*		$: $2', `dnl')
1624
1625ifdef(`_LOOSE_RELAY_CHECK_',`dnl
1626R$*			$: $>CanonAddr $1
1627R$* < @ $* . >		$1 < @ $2 >			strip trailing dots',
1628`R$*			$: $>ParseRecipient $1		strip relayable hosts')
1629
1630ifdef(`_BESTMX_IS_LOCAL_',`dnl
1631ifelse(_BESTMX_IS_LOCAL_, `', `dnl
1632# unlimited bestmx
1633R$* < @ $* > $*			$: $1 < @ $2 @@ $(bestmx $2 $) > $3',
1634`dnl
1635# limit bestmx to $=B
1636R$* < @ $* $=B > $*		$: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4')
1637R$* $=O $* < @ $* @@ $=w . > $*	$@ $>"Basic_check_rcpt" $1 $2 $3
1638R$* < @ $* @@ $=w . > $*	$: $1 < @ $3 > $4
1639R$* < @ $* @@ $* > $*		$: $1 < @ $2 > $4')
1640
1641ifdef(`_BLACKLIST_RCPT_',`dnl
1642ifdef(`_ACCESS_TABLE_', `dnl
1643# blacklist local users or any host from receiving mail
1644R$*			$: <?> $1
1645dnl user is now tagged with @ to be consistent with check_mail
1646dnl and to distinguish users from hosts (com would be host, com@ would be user)
1647R<?> $+ < @ $=w >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <H:$2>
1648R<?> $+ < @ $* >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <H:$2>
1649R<?> $+			$: <> <$1> $| <U:$1@>
1650dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1651dnl will only return user<@domain when "reversing" the args
1652R<> <$*> $| <$+>	$: <@> <$1> $| $>SearchList <+To> $| <$2> <>
1653R<@> <$*> $| <$*>	$: <$2> <$1>		reverse result
1654R<?> <$*>		$: @ $1		mark address as no match
1655R<$={Accept}> <$*>	$: @ $2		mark address as no match
1656ifdef(`_DELAY_CHECKS_',`dnl
1657dnl we have to filter these because otherwise they would be interpreted
1658dnl as generic error message...
1659dnl error messages should be "tagged" by prefixing them with error: !
1660dnl that would make a lot of things easier.
1661dnl maybe we should stop checks already here (if SPAM_xyx)?
1662R<$={SpamTag}> <$*>	$: @ $2		mark address as no match')
1663R<REJECT> $*		$#error $@ 5.2.1 $: "550 Mailbox disabled for this recipient"
1664R<DISCARD> $*		$#discard $: discard
1665dnl error tag
1666R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
1667R<ERROR:$+> $*		$#error $: $1
1668dnl generic error from access map
1669R<$+> $*		$#error $: $1		error from access db
1670R@ $*			$1		remove mark', `dnl')', `dnl')
1671
1672ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)')
1673# authenticated?
1674dnl do this unconditionally? this requires to manage CAs carefully
1675dnl just because someone has a CERT signed by a "trusted" CA
1676dnl does not mean we want to allow relaying for her,
1677dnl either use a subroutine or provide something more sophisticated
1678dnl this could for example check the DN (maybe an access map lookup)
1679R$*		$: $1 $| $>RelayAuth $1 $| $&{verify}	client authenticated?
1680R$* $| $# $+		$# $2				error/ok?
1681R$* $| $*		$: $1				no
1682
1683# authenticated by a trusted mechanism?
1684R$*			$: $1 $| $&{auth_type}
1685dnl empty ${auth_type}?
1686R$* $|			$: $1
1687dnl mechanism ${auth_type} accepted?
1688dnl use $# to override further tests (delay_checks): see check_rcpt below
1689R$* $| $={TrustAuthMech}	$# RELAYAUTH
1690dnl undo addition of ${auth_type}
1691R$* $| $*		$: $1
1692dnl workspace: localpart<@domain> | localpart
1693ifelse(defn(`_NO_UUCP_'), `r',
1694`R$* ! $* < @ $* >	$: <REMOTE> $2 < @ BANG_PATH >
1695R$* ! $* 		$: <REMOTE> $2 < @ BANG_PATH >', `dnl')
1696# anything terminating locally is ok
1697ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1698R$+ < @ $* $=m >	$@ RELAYTO', `dnl')
1699R$+ < @ $=w >		$@ RELAYTO
1700ifdef(`_RELAY_HOSTS_ONLY_',
1701`R$+ < @ $=R >		$@ RELAYTO
1702ifdef(`_ACCESS_TABLE_', `dnl
1703R$+ < @ $+ >		$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>
1704dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
1705R<?> <$+ < @ $+ >>	$: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
1706`R$+ < @ $* $=R >	$@ RELAYTO
1707ifdef(`_ACCESS_TABLE_', `dnl
1708R$+ < @ $+ >		$: $>LookUpDomain <$2> <?> <$1 < @ $2 >> <+To>',`dnl')')
1709ifdef(`_ACCESS_TABLE_', `dnl
1710dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
1711R<RELAY> $*		$@ RELAYTO
1712R<$*> <$*>		$: $2',`dnl')
1713
1714
1715ifdef(`_RELAY_MX_SERVED_', `dnl
1716# allow relaying for hosts which we MX serve
1717R$+ < @ $+ >		$: < : $(mxserved $2 $) : > $1 < @ $2 >
1718dnl this must not necessarily happen if the client is checked first...
1719R< : $* <TEMP> : > $*	$#error $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1
1720R<$* : $=w . : $*> $*	$@ RELAYTO
1721R< : $* : > $*		$: $2',
1722`dnl')
1723
1724# check for local user (i.e. unqualified address)
1725R$*			$: <?> $1
1726R<?> $* < @ $+ >	$: <REMOTE> $1 < @ $2 >
1727# local user is ok
1728dnl is it really? the standard requires user@domain, not just user
1729dnl but we should accept it anyway (maybe making it an option:
1730dnl RequireFQDN ?)
1731dnl postmaster must be accepted without domain (DRUMS)
1732ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
1733R<?> postmaster		$@ TOPOSTMASTER
1734# require qualified recipient?
1735dnl prepend daemon_flags
1736R<?> $+			$: $&{daemon_flags} $| <?> $1
1737dnl workspace: ${daemon_flags} $| <?> localpart
1738dnl do not allow these at all or only from local systems?
1739dnl r flag? add client_name
1740R$* r $* $| <?> $+	$: < ? $&{client_name} > <?> $3
1741dnl no r flag: relay to local user (only local part)
1742# no qualified recipient required
1743R$* $| <?> $+		$@ RELAYTOLOCAL
1744dnl client_name is empty
1745R<?> <?> $+		$@ RELAYTOLOCAL
1746dnl client_name is local
1747R<? $=w> <?> $+		$@ RELAYTOLOCAL
1748dnl client_name is not local
1749R<? $+> $+		$#error $@ 5.5.4 $: "553 Domain name required"', `dnl
1750dnl no qualified recipient required
1751R<?> $+			$@ RELAYTOLOCAL')
1752dnl it is a remote user: remove mark and then check client
1753R<$+> $*		$: $2
1754dnl currently the recipient address is not used below
1755
1756# anything originating locally is ok
1757# check IP address
1758R$*			$: $&{client_addr}
1759R$@			$@ RELAYFROM		originated locally
1760R0			$@ RELAYFROM		originated locally
1761R$=R $*			$@ RELAYFROM		relayable IP address
1762ifdef(`_ACCESS_TABLE_', `dnl
1763R$*			$: $>LookUpAddress <$1> <?> <$1> <+Connect>
1764R<RELAY> $* 		$@ RELAYFROM		relayable IP address
1765R<$*> <$*>		$: $2', `dnl')
1766R$*			$: [ $1 ]		put brackets around it...
1767R$=w			$@ RELAYFROM		... and see if it is local
1768
1769ifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
1770ifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
1771ifdef(`_RELAY_MAIL_FROM_', `dnl
1772dnl input: {client_addr} or something "broken"
1773dnl just throw the input away; we do not need it.
1774# check whether FROM is allowed to use system as relay
1775R$*			$: <?> $>CanonAddr $&f
1776ifdef(`_RELAY_LOCAL_FROM_', `dnl
1777# check whether local FROM is ok
1778R<?> $+ < @ $=w . >	$@ RELAYFROMMAIL	FROM local', `dnl')
1779ifdef(`_RELAY_DB_FROM_', `dnl
1780R<?> $+ < @ $+ . >	<?> $1 < @ $2 >		remove trailing dot
1781R<?> $+ < @ $+ >	$: $1 < @ $2 > $| $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', `<H:$2>') <>
1782R$* <RELAY>		$@ RELAYFROMMAIL	RELAY FROM sender ok', `dnl
1783ifdef(`_RELAY_DB_FROM_DOMAIN_', `errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_
1784')',
1785`dnl')
1786dnl')', `dnl')
1787
1788# check client name: first: did it resolve?
1789dnl input: ignored
1790R$*			$: < $&{client_resolve} >
1791R<TEMP>			$#error $@ 4.7.1 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr}
1792R<FORGED>		$#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name}
1793R<FAIL>			$#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name}
1794dnl ${client_resolve} should be OK, so go ahead
1795R$*			$: <?> $&{client_name}
1796# pass to name server to make hostname canonical
1797R<?> $* $~P 		$:<?>  $[ $1 $2 $]
1798R$* .			$1			strip trailing dots
1799dnl should not be necessary since it has been done for client_addr already
1800R<?>			$@ RELAYFROM
1801ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1802R<?> $* $=m		$@ RELAYFROM', `dnl')
1803R<?> $=w		$@ RELAYFROM
1804ifdef(`_RELAY_HOSTS_ONLY_',
1805`R<?> $=R		$@ RELAYFROM
1806ifdef(`_ACCESS_TABLE_', `dnl
1807R<?> $*			$: <$(access Connect:$1 $: ? $)> <$1>
1808R<?> <$*>		$: <$(access $1 $: ? $)> <$1>',`dnl')',
1809`R<?> $* $=R			$@ RELAYFROM
1810ifdef(`_ACCESS_TABLE_', `dnl
1811R<?> $*			$: $>LookUpDomain <$1> <?> <$1> <+Connect>',`dnl')')
1812ifdef(`_ACCESS_TABLE_', `dnl
1813R<RELAY> $*		$@ RELAYFROM
1814R<$*> <$*>		$: $2',`dnl')
1815
1816# anything else is bogus
1817R$*			$#error $@ 5.7.1 $: confRELAY_MSG
1818divert(0)
1819ifdef(`_DELAY_CHECKS_',`dnl
1820# turn a canonical address in the form user<@domain>
1821# qualify unqual. addresses with $j
1822dnl it might have been only user (without <@domain>)
1823SFullAddr
1824R$* <@ $+ . >		$1 <@ $2 >
1825R$* <@ $* >		$@ $1 <@ $2 >
1826R$+			$@ $1 <@ $j >
1827
1828# call all necessary rulesets
1829Scheck_rcpt
1830dnl this test should be in the Basic_check_rcpt ruleset
1831dnl which is the correct DSN code?
1832# R$@			$#error $@ 5.1.3 $: "553 Recipient address required"
1833R$+			$: $1 $| $>checkrcpt $1
1834dnl now we can simply stop checks by returning "$# xyz" instead of just "ok"
1835R$+ $| $#$*		$#$2
1836R$+ $| $*		$: <?> $>FullAddr $>CanonAddr $1
1837ifdef(`_SPAM_FH_',
1838`dnl lookup user@ and user@address
1839ifdef(`_ACCESS_TABLE_', `',
1840`errprint(`*** ERROR: FEATURE(`delay_checks', `argument') requires FEATURE(`access_db')
1841')')dnl
1842dnl one of the next two rules is supposed to match
1843dnl this code has been copied from BLACKLIST... etc
1844dnl and simplified by omitting some < >.
1845R<?> $+ < @ $=w >	$: <> $1 < @ $2 > $| <F: $1@$2 > <U: $1@>
1846R<?> $+ < @ $* >	$: <> $1 < @ $2 > $| <F: $1@$2 >
1847dnl R<?>		$@ something_is_very_wrong_here
1848# lookup the addresses only with To tag
1849R<> $* $| <$+>		$: <@> $1 $| $>SearchList <!To> $| <$2> <>
1850R<@> $* $| $*		$: $2 $1		reverse result
1851dnl', `dnl')
1852ifdef(`_SPAM_FRIEND_',
1853`# is the recipient a spam friend?
1854ifdef(`_SPAM_HATER_',
1855	`errprint(`*** ERROR: define either SpamHater or SpamFriend
1856')', `dnl')
1857R<SPAMFRIEND> $+	$@ SPAMFRIEND
1858R<$*> $+		$: $2',
1859`dnl')
1860ifdef(`_SPAM_HATER_',
1861`# is the recipient no spam hater?
1862R<SPAMHATER> $+		$: $1			spam hater: continue checks
1863R<$*> $+		$@ NOSPAMHATER		everyone else: stop
1864dnl',`dnl')
1865dnl run further checks: check_mail
1866dnl should we "clean up" $&f?
1867R$*			$: $1 $| $>checkmail <$&f>
1868R$* $| $#$*		$#$2
1869dnl run further checks: check_relay
1870R$*			$: $1 $| $>checkrelay $&{client_name} $| $&{client_addr}
1871R$* $| $#$*		$#$2
1872R$* $| $*		$: $1
1873', `dnl')
1874ifdef(`_ACCESS_TABLE_', `dnl
1875######################################################################
1876###  SearchList: search a list of items in the access map
1877###	Parameters:
1878###		<exact tag> $| <mark:address> <mark:address> ... <>
1879dnl	maybe we should have a @ (again) in front of the mark to
1880dnl	avoid errorneous matches (with error messages?)
1881dnl	if we can make sure that tag is always a single token
1882dnl	then we can omit the delimiter $|, otherwise we need it
1883dnl	to avoid errorneous matchs (first rule: H: if there
1884dnl	is that mark somewhere in the list, it will be taken).
1885dnl	moreover, we can do some tricks to enforce lookup with
1886dnl	the tag only, e.g.:
1887###	where "exact" is either "+" or "!":
1888###	<+ TAG>	lookup with and w/o tag
1889###	<! TAG>	lookup with tag
1890dnl	Warning: + and ! should be in OperatorChars (otherwise there must be
1891dnl		a blank between them and the tag.
1892###	possible values for "mark" are:
1893###		H: recursive host lookup (LookUpDomain)
1894dnl		A: recursive address lookup (LookUpAddress) [not yet required]
1895###		E: exact lookup, no modifications
1896###		F: full lookup, try user+ext@domain and user@domain
1897###		U: user lookup, try user+ext and user (input must have trailing @)
1898###	return: <RHS of lookup> or <?> (not found)
1899######################################################################
1900
1901# class with valid marks for SearchList
1902dnl if A is activated: add it
1903C{src}E F H U
1904SSearchList
1905# mark H: lookup domain
1906R<$+> $| <H:$+> <$*>		$: <$1> $| <@> $>LookUpDomain <$2> <?> <$3> <$1>
1907R<$+> $| <@> <$+> <$*>		$: <$1> $| <$2> <$3>
1908dnl A: NOT YET REQUIRED
1909dnl R<$+> $| <A:$+> <$*>	$: <$1> $| <@> $>LookUpAddress <$2> <?> <$3> <$1>
1910dnl R<$+> $| <@> <$+> <$*>	$: <$1> $| <$2> <$3>
1911dnl lookup of the item with tag
1912dnl this applies to F: U: E:
1913R<$- $-> $| <$={src}:$+> <$*>	$: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$4 $: $3:$4 $)> <$5>
1914dnl no match, try without tag
1915R<+ $-> $| <$={src}:$+> <$*>	$: <+ $1> $| <$(access $3 $: $2:$3 $)> <$4>
1916dnl do we really have to distinguish these cases?
1917dnl probably yes, there might be a + in the domain part (is that allowed?)
1918dnl user+detail lookups: should it be:
1919dnl user+detail, user+*, user; just like aliases?
1920R<$- $-> $| <F:$* + $*@$+> <$*>	$: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$3@$5 $: F:$3 + $4@$5$)> <$6>
1921R<+ $-> $| <F:$* + $*@$+> <$*>	$: <+ $1> $| <$(access $2@$4 $: F:$2 + $3@$4$)> <$5>
1922dnl user lookups are always with trailing @
1923dnl do not remove the @ from the lookup:
1924dnl it is part of the +detail@ which is omitted for the lookup
1925R<$- $-> $| <U:$* + $*> <$*>	$: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$3@ $: U:$3 + $4$)> <$5>
1926dnl no match, try without tag
1927R<+ $-> $| <U:$* + $*> <$*>	$: <+ $1> $| <$(access $2@ $: U:$2 + $3$)> <$4>
1928dnl no match, try rest of list
1929R<$+> $| <$={src}:$+> <$+>	$@ $>SearchList <$1> $| <$4>
1930dnl no match, list empty: return failure
1931R<$+> $| <$={src}:$+> <>	$@ <?>
1932dnl got result, return it
1933R<$+> $| <$+> <$*>		$@ <$2>
1934dnl return result from recursive invocation
1935R<$+> $| <$+>			$@ <$2>', `dnl')
1936
1937# is user trusted to authenticate as someone else?
1938dnl AUTH= parameter from MAIL command
1939Strust_auth
1940R$*			$: $&{auth_type} $| $1
1941# required by RFC 2554 section 4.
1942R$@ $| $*		$#error $@ 5.7.1 $: "550 not authenticated"
1943dnl seems to be useful...
1944R$* $| $&{auth_authen}		$@ identical
1945R$* $| <$&{auth_authen}>	$@ identical
1946dnl call user supplied code
1947R$* $| $*		$: $1 $| $>"Local_trust_auth" $1
1948R$* $| $#$*		$#$2
1949dnl default: error
1950R$*			$#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author}
1951
1952dnl empty ruleset definition so it can be called
1953SLocal_trust_auth
1954
1955ifdef(`_FFR_TLS_O_T', `dnl
1956Soffer_tls
1957R$*		$: $>LookUpDomain <$&{client_name}> <?> <> <! TLS_OFF_TAG>
1958R<?>$*		$: $>LookUpAddress <$&{client_addr}> <?> <> <! TLS_OFF_TAG>
1959R<?>$*		$: <$(access TLS_OFF_TAG: $: ? $)>
1960R<?>$*		$@ OK
1961R<NO> <>	$#error $@ 5.7.1 $: "550 do not offer TLS for " $&{client_name} " ["$&{client_addr}"]"
1962
1963Stry_tls
1964R$*		$: $>LookUpDomain <$&{server_name}> <?> <> <! TLS_TRY_TAG>
1965R<?>$*		$: $>LookUpAddress <$&{server_addr}> <?> <> <! TLS_TRY_TAG>
1966R<?>$*		$: <$(access TLS_TRY_TAG: $: ? $)>
1967R<?>$*		$@ OK
1968R<NO>$*		$#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"
1969')dnl
1970
1971# is connection with client "good" enough? (done in server)
1972# input: ${verify} $| (MAIL|STARTTLS)
1973dnl MAIL: called from check_mail
1974dnl STARTTLS: called from smtp() after STARTTLS has been accepted
1975Stls_client
1976ifdef(`_ACCESS_TABLE_', `dnl
1977dnl ignore second arg for now
1978dnl maybe use it to distinguish permanent/temporary error?
1979dnl if MAIL: permanent (STARTTLS has not been offered)
1980dnl if STARTTLS: temporary (offered but maybe failed)
1981R$* $| $*	$: $1 $| $>LookUpDomain <$&{client_name}> <?> <> <! TLS_CLT_TAG>
1982R$* $| <?>$*	$: $1 $| $>LookUpAddress <$&{client_addr}> <?> <> <! TLS_CLT_TAG>
1983dnl do a default lookup: just TLS_CLT_TAG
1984R$* $| <?>$*	$: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)>
1985R$*		$@ $>"tls_connection" $1', `dnl
1986R$* $| $*	$@ $>"tls_connection" $1')
1987
1988# is connection with server "good" enough? (done in client)
1989dnl i.e. has the server been authenticated and is encryption active?
1990dnl called from deliver() after STARTTLS command
1991# input: ${verify}
1992Stls_server
1993ifdef(`_ACCESS_TABLE_', `dnl
1994R$*		$: $1 $| $>LookUpDomain <$&{server_name}> <?> <> <! TLS_SRV_TAG>
1995R$* $| <?>$*	$: $1 $| $>LookUpAddress <$&{server_addr}> <?> <> <! TLS_SRV_TAG>
1996dnl do a default lookup: just TLS_SRV_TAG
1997R$* $| <?>$*	$: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)>
1998R$*		$@ $>"tls_connection" $1', `dnl
1999R$*		$@ $>"tls_connection" $1')
2000
2001Stls_connection
2002ifdef(`_ACCESS_TABLE_', `dnl
2003dnl common ruleset for tls_{client|server}
2004dnl input: $&{verify} $| <ResultOfLookup> [<>]
2005dnl remove optional <>
2006R$* $| <$*>$*			$: $1 $| <$2>
2007dnl permanent or temporary error?
2008R$* $| <PERM + $={tls} $*>	$: $1 $| <503:5.7.0> <$2 $3>
2009R$* $| <TEMP + $={tls} $*>	$: $1 $| <403:4.7.0> <$2 $3>
2010dnl default case depends on TLS_PERM_ERR
2011R$* $| <$={tls} $*>		$: $1 $| <ifdef(`TLS_PERM_ERR', `503:5.7.0', `403:4.7.0')> <$2 $3>
2012dnl deal with TLS handshake failures: abort
2013RSOFTWARE $| <$-:$+> $* 	$#error $@ $2 $: $1 " TLS handshake failed."
2014dnl no <reply:dns> i.e. not requirements in the access map
2015dnl use default error
2016RSOFTWARE $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed."
2017R$* $| <$*> <VERIFY>		$: <$2> <VERIFY> $1
2018R$* $| <$*> <$={tls}:$->$*	$: <$2> <$3:$4> $1
2019dnl some other value in access map: accept
2020dnl this also allows to override the default case (if used)
2021R$* $| $*			$@ OK
2022# authentication required: give appropriate error
2023# other side did authenticate (via STARTTLS)
2024dnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> ${verify}
2025dnl only verification required and it succeeded
2026R<$*><VERIFY> OK		$@ OK
2027dnl verification required + some level of encryption
2028R<$*><VERIFY:$-> OK		$: <$1> <REQ:$2>
2029dnl just some level of encryption required
2030R<$*><ENCR:$-> $*		$: <$1> <REQ:$2>
2031dnl verification required but ${verify} is not set
2032R<$-:$+><VERIFY $*>		$#error $@ $2 $: $1 " authentication required"
2033R<$-:$+><VERIFY $*> FAIL	$#error $@ $2 $: $1 " authentication failed"
2034R<$-:$+><VERIFY $*> NO		$#error $@ $2 $: $1 " not authenticated"
2035R<$-:$+><VERIFY $*> NONE	$#error $@ $2 $: $1 " other side does not support STARTTLS"
2036dnl some other value for ${verify}
2037R<$-:$+><VERIFY $*> $+		$#error $@ $2 $: $1 " authentication failure " $4
2038dnl some level of encryption required: get the maximum level
2039R<$*><REQ:$->			$: <$1> <REQ:$2> $>max $&{cipher_bits} : $&{auth_ssf}
2040dnl compare required bits with actual bits
2041R<$*><REQ:$-> $-		$: <$1> <$2:$3> $(arith l $@ $3 $@ $2 $)
2042R<$-:$+><$-:$-> TRUE		$#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3
2043
2044Smax
2045dnl compute the max of two values separated by :
2046R:		$: 0
2047R:$-		$: $1
2048R$-:		$: $1
2049R$-:$-		$: $(arith l $@ $1 $@ $2 $) : $1 : $2
2050RTRUE:$-:$-	$: $2
2051R$-:$-:$-	$: $2',
2052`dnl use default error
2053dnl deal with TLS handshake failures: abort
2054RSOFTWARE	$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake."')
2055
2056SRelayAuth
2057# authenticated?
2058dnl we do not allow relaying for anyone who can present a cert
2059dnl signed by a "trusted" CA. For example, even if we put verisigns
2060dnl CA in CERTPath so we can authenticate users, we do not allow
2061dnl them to abuse our server (they might be easier to get hold of,
2062dnl but anyway).
2063dnl so here is the trick: if the verification succeeded
2064dnl we look up the cert issuer in the access map
2065dnl (maybe after extracting a part with a regular expression)
2066dnl if this returns RELAY we relay without further questions
2067dnl if it returns SUBJECT we perform a similar check on the
2068dnl cert subject.
2069R$* $| OK		$: $1
2070R$* $| $*		$@ NO		not authenticated
2071ifdef(`_ACCESS_TABLE_', `dnl
2072ifdef(`_CERT_REGEX_ISSUER_', `dnl
2073R$*			$: $1 $| $(CERTIssuer $&{cert_issuer} $)',
2074`R$*			$: $1 $| $&{cert_issuer}')
2075R$* $| $+		$: $1 $| $(access CERTISSUER:$2 $)
2076dnl use $# to stop further checks (delay_check)
2077R$* $| RELAY		$# RELAYCERTISSUER
2078ifdef(`_CERT_REGEX_SUBJECT_', `dnl
2079R$* $| SUBJECT		$: $1 $| <@> $(CERTSubject $&{cert_subject} $)',
2080`R$* $| SUBJECT		$: $1 $| <@> $&{cert_subject}')
2081R$* $| <@> $+		$: $1 $| <@> $(access CERTSUBJECT:$2 $)
2082R$* $| <@> RELAY	$# RELAYCERTSUBJECT
2083R$* $| $*		$: $1', `dnl')
2084
2085undivert(9)dnl LOCAL_RULESETS
2086ifdef(`_FFR_MILTER', `
2087#
2088######################################################################
2089######################################################################
2090#####
2091`#####			MAIL FILTER DEFINITIONS'
2092#####
2093######################################################################
2094######################################################################
2095_MAIL_FILTERS_')
2096#
2097######################################################################
2098######################################################################
2099#####
2100`#####			MAILER DEFINITIONS'
2101#####
2102######################################################################
2103######################################################################
2104undivert(7)dnl MAILER_DEFINITIONS
2105
2106