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