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