File: rcpt-policy.tcl

package info (click to toggle)
sauce 0.9.1
  • links: PTS
  • area: main
  • in suites: bullseye
  • size: 476 kB
  • sloc: tcl: 4,363; sh: 186; makefile: 129
file content (227 lines) | stat: -rwxr-xr-x 6,109 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/usr/bin/tclsh
#
# This file is part of SAUCE, a very picky anti-spam receiver-SMTP.
# SAUCE is Copyright (C) 1997-2003 Ian Jackson
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
#
# $Id: rcpt-policy.tcl,v 1.7 2003/09/07 23:34:29 ian Exp $

# userv service.  Input is new user policy file, lines like:
#  <sending-site-pat>  <sending-addr-pat>  <receiving-addr-pat>  <result>
# (and one line `.')  Blank lines are permitted.  Lines starting with #
# are comments.
#
# <sending-site-pat> is one of
#   [<ip-address>]
#   [<ip-address-mask>/<prefix-len>]
#   <domain-name-glob>
#
# <foo-addr-pat> is one of
#   <local-part-glob>@
#   <address-glob>  (not ending in @)
# (To match `<>' when specified as envelope sender, match `@' instead.)
#
# globs may contain no whitespace.  They support [...], ? and *
# and \-escapes.  There is no way to specify patterns including
# whitespace.
#
# <result> is one of
#   450|451|452|550|552|553 <message-string>
#   normal|lax|nodelay|bait|unchecked

proc log {lev msg} {
    puts stderr $msg
}

set @@@readlibs@@@ readlibs.tcl
set sauce_libraries {
    readconf
    library
    sconfig
}
source ${@@@readlibs@@@}

proc readconfig_posthook {args} {}

readconfig

if {[string length $current_bigerr]} {
    log fatal "configuration errors, stopping"
    exit 3
}

cd $var_dir/policies

proc fail {emsg} {
    puts stderr $emsg
    exit 1
}

switch -exact [llength $argv] {
    0 {
	set policy $env(USERV_USER)
	if {[info exists env(USERV_U_SUBPOLICY)]} {
	    set subpolicy $env(USERV_U_SUBPOLICY)
	    if {![string length $subpolicy]} {
	    } elseif {[regexp -nocase {^\:[-+_.%$:0-9a-z?*]{0,188}$} \
		    $subpolicy subpolicy]} {
		append policy $subpolicy
	    } else {
		fail \
 "subpolicy must be : followed by 0-127 alphanumerics or : - + _ . % $ ? *"
	    }
	}
    }
    1 {
	set policy [lindex $argv 0]
	if {[regexp {/} $policy]} { fail "policy name may not contain /" }
	if {[regexp {^\.} $policy]} { fail "policy name may not start with ." }
    }
    default { fail "specify only one policy to set" }
}

set lno 0

set outtxt {}
proc out {s} { global outtxt; append outtxt $s "\n" }

out "proc acuser_proc/$policy {} {"

proc syxerr {emsg} {
    global lno errorInfo
    fail "rcpt-policy: policy line $lno: error: $emsg"
}

proc out_once {text} {
    upvar #0 outonce_done($text) d
    if {[info exists d]} return
    out $text
    set d 1
}

set encc 0

proc encvarn {thing} {
    upvar #0 enc_scope($thing) enc
    if {![info exists enc]} {
	global encc
	regsub -all {[^a-z]+} $thing _ p
	regsub -all {_+$} $p {} p
	regsub {^_} $p {U} p
	set enc "${p}_X[incr encc]"
    }
    return $enc
}

proc scope {thing} {
    set enc [encvarn $thing]
    out_once "    upvar 1 $thing $enc"
    return "\$$enc"
}

proc condkind {kind argl rbody} {
    set body    "    upvar 1 \${condname}pat pat\n"
    append body "    if {\"\$pat\" == \"*\"} return\n"
    append body $rbody
    proc cond_add_$kind [concat condname $argl] $body
}

condkind site {} {
    set len 32
    if {[regexp {\[([0-9][0-9.]+)\]} $pat dummy mask] || \
	    [regexp {\[([0-9][0-9.]*)/([0-9]+)\]} $pat dummy mask len]} {
	out_once "    set ra_v \[ia2value [scope state(ra)] 32\]"
	if {[catch { set re_v [ia2value $mask $len] } emsg]} {
	    syxerr "invalid address: $emsg"
	}
	if {$len > 32} { syxerr "prefix length >32" }
	set ma_v [expr {$len == 0 ? 0 : ((0xffffffff<<(32-$len))&0xffffffff)}]
	set bad [expr {$re_v & ($ma_v ^ 0xffffffff)}]
	if {$bad} { syxerr "mask is non-zero beyond prefix" }
	cond_add "(\$ra_v & [format 0x%08x $ma_v]) == [format 0x%08x $re_v]"
    } elseif {[string match "\[*" $pat]} {
	syxerr "invalid address mask"
    } else {
	cond_add_glob $pat [scope state(rh)]
    }
}

proc cond_add_glob {pat valstring} {
    regexp {(.*)} $pat npat
    if {[catch { string match $npat foobar } emsg]} {
	syxerr "invalid glob pattern: $emsg"
    }
    cond_add "\[string match [list $npat] $valstring\]"
}

proc cond_add {cond} {
    global conds
    lappend conds $cond
}

condkind addr {lpv dmv} {
    if {[regexp {^(.*)\@$} $pat dummy lpat]} {
	cond_add_glob $lpat [scope $lpv]
    } else {
	set enc [encvarn $lpv@$dmv]
	out_once "    set $enc [scope $lpv]@[scope $dmv]"
	cond_add_glob $pat \$$enc
    }
}

set condjoin " &&\n        "
set any 0

while 1 {
    if {[gets stdin line] < 0} { syxerr "missing final line `.'" }
    incr lno
    set line [string trim $line]
    if {![string length $line]} continue
    if {"$line" == "."} break
    if {[string match #* $line]} continue
    if {![regexp {^(\S+)\s+(\S+)\s+(\S+)\s+(\S.*\S)$} $line dummy \
	    sspat sapat rapat result]} { syxerr "syntax error" }
    set conds {}
    cond_add_site ss
    cond_add_addr sa state(mf_lp) state(mf_dm)
    cond_add_addr ra lp dm
    if {[llength $conds]} {
	out "    if {[join $conds $condjoin]} \{"
    }
    if {![regexp \
 {^(?:errok\-)?(45[012]|55[023]) \S.*$|^(?:errok\-)?(unchecked|lax|nodelay|normal|bait)$} \
	    $result]} {
	syxerr "invalid result"
    }
    if {[regexp -nocase {[^ -~]} $result]} { syxerr "invalid char in result" }
    out "        [list return $result]"
    set any 1
    if {[llength $conds]} {
	out "    \}"
    }
}
out "    return {}\n}"

if {$any} {
    set out [open n$policy w]
    puts -nonewline $out $outtxt
    close $out
    file rename -force n$policy p$policy
    puts "ok - new SAUCE policy $policy installed"
} else {
    file delete p$policy
    puts "ok - any SAUCE policy $policy removed"
}