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