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