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