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