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