Filename | /usr/local/lib/perl5/site_perl/mach/5.24/Razor2/Client/Core.pm |
Statements | Executed 31 statements in 23.5ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 372µs | 550µs | BEGIN@18 | Razor2::Client::Core::
1 | 1 | 1 | 48µs | 60µs | BEGIN@13 | Razor2::Client::Core::
1 | 1 | 1 | 31µs | 1.25ms | BEGIN@24 | Razor2::Client::Core::
1 | 1 | 1 | 29µs | 18.2ms | BEGIN@23 | Razor2::Client::Core::
1 | 1 | 1 | 26µs | 26µs | CORE:match (opcode) | Razor2::Client::Core::
1 | 1 | 1 | 25µs | 3.62ms | BEGIN@14 | Razor2::Client::Core::
1 | 1 | 1 | 25µs | 86µs | BEGIN@15 | Razor2::Client::Core::
1 | 1 | 1 | 25µs | 30.0ms | BEGIN@22 | Razor2::Client::Core::
1 | 1 | 1 | 23µs | 228µs | BEGIN@21 | Razor2::Client::Core::
1 | 1 | 1 | 22µs | 356µs | BEGIN@26 | Razor2::Client::Core::
1 | 1 | 1 | 22µs | 110µs | BEGIN@19 | Razor2::Client::Core::
1 | 1 | 1 | 20µs | 1.97ms | BEGIN@16 | Razor2::Client::Core::
1 | 1 | 1 | 18µs | 109µs | BEGIN@20 | Razor2::Client::Core::
1 | 1 | 1 | 17µs | 17µs | BEGIN@25 | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | DESTROY | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | _read | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | _send | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | authenticate | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | bootstrap_discovery | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | check | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | check_logic | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | check_resp | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | compute_server_conf | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | compute_sigs | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | compute_supported_engines | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | connect | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | debug | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | disconnect | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | discover | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | load_at_runtime | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | logobj | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | make_query | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | new | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | nextserver | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | obj2queries | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | parse_greeting | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | prepare_objects | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | prepare_parts | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | queries2obj | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | rcheck_resp | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | register | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | report | Razor2::Client::Core::
0 | 0 | 0 | 0s | 0s | zonename | Razor2::Client::Core::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | #!/usr/local/bin/perl -sw | ||||
2 | ## | ||||
3 | ## Razor2::Client::Core - Vipul's Razor Client API | ||||
4 | ## | ||||
5 | ## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved. | ||||
6 | ## This code is free software; you can redistribute it and/or modify | ||||
7 | ## it under the same terms as Perl itself. | ||||
8 | ## | ||||
9 | ## $Id: Core.pm,v 1.92 2006/05/27 00:00:53 rsoderberg Exp $ | ||||
10 | |||||
11 | package Razor2::Client::Core; | ||||
12 | |||||
13 | 2 | 65µs | 2 | 72µs | # spent 60µs (48+12) within Razor2::Client::Core::BEGIN@13 which was called:
# once (48µs+12µs) by base::import at line 13 # spent 60µs making 1 call to Razor2::Client::Core::BEGIN@13
# spent 12µs making 1 call to strict::import |
14 | 2 | 72µs | 2 | 7.21ms | # spent 3.62ms (25µs+3.59) within Razor2::Client::Core::BEGIN@14 which was called:
# once (25µs+3.59ms) by base::import at line 14 # spent 3.62ms making 1 call to Razor2::Client::Core::BEGIN@14
# spent 3.59ms making 1 call to IO::Socket::import |
15 | 2 | 62µs | 2 | 148µs | # spent 86µs (25+62) within Razor2::Client::Core::BEGIN@15 which was called:
# once (25µs+62µs) by base::import at line 15 # spent 86µs making 1 call to Razor2::Client::Core::BEGIN@15
# spent 62µs making 1 call to Exporter::import |
16 | 2 | 65µs | 2 | 3.91ms | # spent 1.97ms (20µs+1.95) within Razor2::Client::Core::BEGIN@16 which was called:
# once (20µs+1.95ms) by base::import at line 16 # spent 1.97ms making 1 call to Razor2::Client::Core::BEGIN@16
# spent 1.95ms making 1 call to Exporter::import |
17 | |||||
18 | 2 | 322µs | 1 | 550µs | # spent 550µs (372+177) within Razor2::Client::Core::BEGIN@18 which was called:
# once (372µs+177µs) by base::import at line 18 # spent 550µs making 1 call to Razor2::Client::Core::BEGIN@18 |
19 | 2 | 60µs | 2 | 197µs | # spent 110µs (22+88) within Razor2::Client::Core::BEGIN@19 which was called:
# once (22µs+88µs) by base::import at line 19 # spent 110µs making 1 call to Razor2::Client::Core::BEGIN@19
# spent 88µs making 1 call to Exporter::import |
20 | 2 | 63µs | 2 | 199µs | # spent 109µs (18+90) within Razor2::Client::Core::BEGIN@20 which was called:
# once (18µs+90µs) by base::import at line 20 # spent 109µs making 1 call to Razor2::Client::Core::BEGIN@20
# spent 90µs making 1 call to vars::import |
21 | 2 | 63µs | 2 | 228µs | # spent 228µs (23+205) within Razor2::Client::Core::BEGIN@21 which was called:
# once (23µs+205µs) by base::import at line 21 # spent 228µs making 1 call to Razor2::Client::Core::BEGIN@21
# spent 205µs making 1 call to base::import, recursion: max depth 1, sum of overlapping time 205µs |
22 | 2 | 88µs | 2 | 30.0ms | # spent 30.0ms (25µs+29.9) within Razor2::Client::Core::BEGIN@22 which was called:
# once (25µs+29.9ms) by base::import at line 22 # spent 30.0ms making 1 call to Razor2::Client::Core::BEGIN@22
# spent 29.9ms making 1 call to base::import, recursion: max depth 1, sum of overlapping time 29.9ms |
23 | 2 | 87µs | 2 | 18.2ms | # spent 18.2ms (29µs+18.2) within Razor2::Client::Core::BEGIN@23 which was called:
# once (29µs+18.2ms) by base::import at line 23 # spent 18.2ms making 1 call to Razor2::Client::Core::BEGIN@23
# spent 18.2ms making 1 call to base::import, recursion: max depth 1, sum of overlapping time 18.2ms |
24 | 2 | 69µs | 2 | 1.25ms | # spent 1.25ms (31µs+1.22) within Razor2::Client::Core::BEGIN@24 which was called:
# once (31µs+1.22ms) by base::import at line 24 # spent 1.25ms making 1 call to Razor2::Client::Core::BEGIN@24
# spent 1.22ms making 1 call to base::import, recursion: max depth 1, sum of overlapping time 1.22ms |
25 | 2 | 81µs | 1 | 17µs | # spent 17µs within Razor2::Client::Core::BEGIN@25 which was called:
# once (17µs+0s) by base::import at line 25 # spent 17µs making 1 call to Razor2::Client::Core::BEGIN@25 |
26 | 1 | 2µs | # spent 356µs (22+334) within Razor2::Client::Core::BEGIN@26 which was called:
# once (22µs+334µs) by base::import at line 29 | ||
27 | prep_mail debugobj to_batched_query | ||||
28 | from_batched_query hexbits2hash | ||||
29 | 1 | 22.3ms | 2 | 690µs | fisher_yates_shuffle); # spent 356µs making 1 call to Razor2::Client::Core::BEGIN@26
# spent 334µs making 1 call to Exporter::import |
30 | |||||
31 | |||||
32 | 3 | 85µs | 1 | 26µs | ($VERSION) = do { my @r = (q$Revision: 1.92 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # spent 26µs making 1 call to Razor2::Client::Core::CORE:match |
33 | 1 | 2µs | $PROTOCOL = $Razor2::Client::Version::PROTOCOL; | ||
34 | |||||
35 | |||||
36 | sub new { | ||||
37 | my ($class, $conf, %params) = @_; | ||||
38 | my $self = {}; | ||||
39 | bless $self, $class; | ||||
40 | $self->debug ("Razor Agents $VERSION, protocol version $PROTOCOL."); | ||||
41 | return $self; | ||||
42 | } | ||||
43 | |||||
- - | |||||
46 | # | ||||
47 | # We store server-specific config info for each server we know about. | ||||
48 | # All info about razor servers is stored in $self->{s}. | ||||
49 | # | ||||
50 | # Basically we get the server name/ip from {list}, | ||||
51 | # load that server's specific info from {allconfs} into {conf}, | ||||
52 | # and do stuff. If server is no good, we get nextserver from {list} | ||||
53 | # | ||||
54 | # $self->{s}->{list} ptr to {nomination} if report,revoke; or {catalogue} if check | ||||
55 | # or the cmd-line server (-rs server) | ||||
56 | # $self->{s}->{new_list} set to 1 when discover gets new lists | ||||
57 | # $self->{s}->{catalogue} array ref containing catalogue servers | ||||
58 | # $self->{s}->{nomination}array ref containing nomination servers | ||||
59 | # $self->{s}->{discovery} array ref containing discovery servers | ||||
60 | # | ||||
61 | # $self->{s}->{modified} array ref containing servers whose .conf needs updating | ||||
62 | # $self->{s}->{modified_lst} array ref containing which .lst files need updating | ||||
63 | # | ||||
64 | # $self->{s}->{ip} string containing ip (or dns name) of current server from {list}) | ||||
65 | # $self->{s}->{port} string containing port, taken from server:port from {list} | ||||
66 | # $self->{s}->{engines} engines supported, derived from {conf}->{se} | ||||
67 | # $self->{s}->{conf} hash ref containing current server's config params | ||||
68 | # read from $razorhome/server.$ip.conf | ||||
69 | # | ||||
70 | # $self->{s}->{allconfs} hash ref of all servers' configs. key={ip}, val={conf} | ||||
71 | # as read from server.*.conf file | ||||
72 | # | ||||
73 | # $self->{s}->{listfile} string containing path/file of server.lst, either | ||||
74 | # nomination or catalogue depending $self->{breed} | ||||
75 | # $self->{conf}->{listfile_discovery} string containing path/file of discovery server | ||||
76 | # | ||||
77 | # NOTE: if we are razor-check, server is Catalogue Server | ||||
78 | # otherwise server is Nomination server. | ||||
79 | # | ||||
80 | # everytime we update our server list, $self->{s}->{list}; | ||||
81 | # we want to write that to disk - $self->{s}->{listfile} | ||||
82 | # | ||||
83 | sub nextserver { | ||||
84 | my ($self) = @_; | ||||
85 | $self->log (16,"entered nextserver"); | ||||
86 | |||||
87 | # see if we need to discover (.lst files might be too old) | ||||
88 | $self->discover() or return $self->errprefix ("nextserver"); | ||||
89 | |||||
90 | # first time we don't remove from list | ||||
91 | # or if we've rediscovered. | ||||
92 | shift @{$self->{s}->{list}} unless ($self->{s}->{new_list} || !$self->{s}->{ip}); | ||||
93 | $self->{s}->{new_list} = 0; | ||||
94 | |||||
95 | my $next = ${$self->{s}->{list}}[0]; | ||||
96 | |||||
97 | # do we ever want to put current back on the end of list? | ||||
98 | # push @{$self->{s}->{list}}, $self->{s}->{ip}; | ||||
99 | |||||
100 | if ($next) { | ||||
101 | ($self->{s}->{port}) = $next =~ /:(.*)$/; | ||||
102 | $next =~ s/:.*$//; # optional | ||||
103 | $self->{s}->{ip} = $next; # ip can be IP or DNS name | ||||
104 | $self->{s}->{port} ||= $self->{conf}->{port} || 2703; | ||||
105 | $self->{s}->{conf} = $self->{s}->{allconfs}->{$next}; | ||||
106 | |||||
107 | my $svrport = "$self->{s}->{ip}:$self->{s}->{port}"; | ||||
108 | |||||
109 | # get rid of server specific stuff | ||||
110 | delete $self->{s}->{greeting}; | ||||
111 | |||||
112 | unless (ref($self->{s}->{conf})) { | ||||
113 | # never used this server before, no cached info. go get it! | ||||
114 | $self->{s}->{conf} = {}; | ||||
115 | $self->connect; # calls parse_greeting which calls compute_server_conf | ||||
116 | } else { | ||||
117 | $self->compute_server_conf(1); # computes supported engines, logs info | ||||
118 | } | ||||
119 | $self->writeservers(); | ||||
120 | |||||
121 | my $srl = defined($self->{s}->{conf}->{srl}) ? $self->{s}->{conf}->{srl} : "<unknown>"; | ||||
122 | $self->log(8, "Using next closest server $svrport, cached info srl $srl"); | ||||
123 | #$self->logobj(11, "Using next closest server $svrport, cached info", $self->{s}->{conf}); | ||||
124 | return 1; | ||||
125 | |||||
126 | } else { | ||||
127 | return $self->error ("Razor server $self->{opt}->{server} not available at this time") | ||||
128 | if $self->{opt}->{server}; | ||||
129 | $self->{force_discovery} = 1; | ||||
130 | if ($self->{done_discovery} && !($self->discover)) { | ||||
131 | return $self->errprefix("No Razor servers available at this time"); | ||||
132 | } | ||||
133 | return $self->nextserver; | ||||
134 | } | ||||
135 | } | ||||
136 | |||||
137 | sub load_at_runtime { | ||||
138 | my ($self,$class,$sub,$args) = @_; | ||||
139 | |||||
140 | $sub = 'new' unless defined $sub; | ||||
141 | $args = "" unless defined $args; | ||||
142 | |||||
143 | eval "use $class"; | ||||
144 | if ($@) { | ||||
145 | $self->log(2,"$class not found, please to fix."); | ||||
146 | return $self->error("\n\n$@"); | ||||
147 | } | ||||
148 | my $evalstr; | ||||
149 | if ($sub && $sub ne "new") { | ||||
150 | $evalstr = $class ."::$sub($args);"; | ||||
151 | } else { | ||||
152 | $evalstr = $class . "->new($args)"; | ||||
153 | } | ||||
154 | if (my $dude = eval $evalstr) { | ||||
155 | $self->log(12,"Found and evaled $evalstr ==> $dude"); | ||||
156 | return $dude; | ||||
157 | } else { | ||||
158 | $self->log(5,"Found but problem (bad args?) with $evalstr"); | ||||
159 | return $self->error("Problem with $evalstr"); | ||||
160 | } | ||||
161 | } | ||||
162 | |||||
163 | # | ||||
164 | # uses DNS to find Discovery servers | ||||
165 | # puts discovery servers in $self->{s}->{discovery} | ||||
166 | # | ||||
167 | sub bootstrap_discovery { | ||||
168 | my ($self) = @_; | ||||
169 | $self->log (16,"entered bootstrap_discovery"); | ||||
170 | |||||
171 | if ($self->{conf}->{server}) { | ||||
172 | $self->log(8,"no bootstap_discovery when cmd-line server specified"); | ||||
173 | return 1; | ||||
174 | } | ||||
175 | unless ($self->{force_bootstrap_discovery}) { | ||||
176 | if (ref($self->{s}->{discovery}) && scalar(@{$self->{s}->{discovery}})) { | ||||
177 | $self->log(8,"already have ". scalar(@{$self->{s}->{discovery}}) | ||||
178 | ." discovery servers"); | ||||
179 | return 1; | ||||
180 | } elsif ($self->{done_bootstrap}) { | ||||
181 | # if we've done it before {s}->{discovery} should be set | ||||
182 | $self->log(8,"already have done bootstrap_discovery"); | ||||
183 | return 1; | ||||
184 | } | ||||
185 | } | ||||
186 | unless (defined $self->{conf}->{listfile_discovery}) { | ||||
187 | $self->log(6,"discovery listfile not defined!"); | ||||
188 | } elsif (-s $self->{conf}->{listfile_discovery}) { | ||||
189 | my $wait = $self->{conf}->{rediscovery_wait_dns} || 604800; # 604800 secs == 7 days | ||||
190 | my $randomize = int(rand($wait/7)); | ||||
191 | my $timeleft = ((stat ($self->{conf}->{listfile_discovery}))[9] + $wait - $randomize) - time; | ||||
192 | if ($timeleft > 0) { | ||||
193 | $self->log (7,"$timeleft seconds before soonest DNS discovery"); | ||||
194 | return 1 unless $self->{force_bootstrap_discovery}; | ||||
195 | $self->log (5,"forcing DNS discovery"); | ||||
196 | } else { | ||||
197 | $self->log (5,"DNS discovery overdue by ". (0-$timeleft) ." seconds"); | ||||
198 | } | ||||
199 | } else { | ||||
200 | if (-e $self->{conf}->{listfile_discovery}) { | ||||
201 | $self->log (6,"empty discovery listfile: $self->{conf}->{listfile_discovery}"); | ||||
202 | } else { | ||||
203 | $self->log (6,"no discovery listfile: $self->{conf}->{listfile_discovery}"); | ||||
204 | } | ||||
205 | } | ||||
206 | |||||
207 | $self->{s}->{discovery} = [ $self->{conf}->{razordiscovery} ]; | ||||
208 | push @{$self->{s}->{modified_lst}}, "discovery"; | ||||
209 | return 1; | ||||
210 | } | ||||
211 | |||||
- - | |||||
214 | # | ||||
215 | # uses Discovery Servers to find closest Nomination/Catalogue Servers. | ||||
216 | # called every day or so of if .lst file is empty | ||||
217 | # | ||||
218 | # puts servers in $self->{s}->{list} | ||||
219 | # | ||||
220 | sub discover { | ||||
221 | my ($self) = @_; | ||||
222 | $self->log (16,"entered discover"); | ||||
223 | |||||
224 | # | ||||
225 | # do we need to discover? | ||||
226 | # | ||||
227 | |||||
228 | # no discover if cmd-line server | ||||
229 | return 1 if $self->{opt}->{server}; | ||||
230 | |||||
231 | # | ||||
232 | # don't discover if conf says turn_off_discovery (unless force_discovery) | ||||
233 | # | ||||
234 | return 1 if $self->{conf}->{turn_off_discovery} && (!($self->{force_discovery})); | ||||
235 | return $self->error ("No Razor servers available at this time") | ||||
236 | if $self->{done_discovery}; | ||||
237 | |||||
238 | # so if user has their own servers, and they are temporarily down, force_discovery. | ||||
239 | # good: shit will work | ||||
240 | # bad: it will erase their custom server*.lst file | ||||
241 | # | ||||
242 | unless (defined $self->{s}->{listfile}) { | ||||
243 | $self->debug ("listfile not defined!"); | ||||
244 | } elsif (-s $self->{s}->{listfile}) { | ||||
245 | my $randomize = int(rand($self->{conf}->{rediscovery_wait}/7)); | ||||
246 | my $timeleft = ((stat ($self->{s}->{listfile}))[9] + $self->{conf}->{rediscovery_wait} - $randomize) - time; | ||||
247 | if ($timeleft > 0) { | ||||
248 | $self->debug ("$timeleft seconds before closest server discovery"); | ||||
249 | return 1 unless $self->{force_discovery}; | ||||
250 | $self->debug ("forcing discovery"); | ||||
251 | } else { | ||||
252 | $self->debug ("server discovery overdue by ". (0-$timeleft) ." seconds"); | ||||
253 | } | ||||
254 | } else { | ||||
255 | if (-e $self->{s}->{listfile}) { | ||||
256 | $self->debug ("empty listfile: $self->{s}->{listfile}"); | ||||
257 | } else { | ||||
258 | $self->debug ("no listfile: $self->{s}->{listfile}"); | ||||
259 | } | ||||
260 | } | ||||
261 | |||||
262 | # | ||||
263 | # we need to discover. | ||||
264 | # | ||||
265 | |||||
266 | |||||
267 | return $self->errprefix("discover0") unless $self->bootstrap_discovery(); | ||||
268 | |||||
269 | |||||
270 | # | ||||
271 | # Go ahead and do discovery for both csl and nsl. | ||||
272 | # | ||||
273 | my %stype = ( csl => 'catalogue', nsl => 'nomination' ); | ||||
274 | |||||
275 | my $srvs = {csl => {}, nsl => {} }; | ||||
276 | |||||
277 | my $list_orig = $self->{s}->{list}; | ||||
278 | $self->{s}->{list} = $self->{s}->{discovery}; | ||||
279 | |||||
280 | foreach (@{$self->{s}->{discovery}}) { | ||||
281 | unless (defined $_) { | ||||
282 | $self->log (5,"Razor Discovery Server not defined!"); | ||||
283 | next; | ||||
284 | } | ||||
285 | |||||
286 | $self->log (8,"Checking with Razor Discovery Server $_"); | ||||
287 | |||||
288 | unless ($self->connect( server => $_, discovery_server => 1 ) ) { | ||||
289 | $self->log (5,"Razor Discovery Server $_ is unreachable"); | ||||
290 | next; | ||||
291 | } | ||||
292 | |||||
293 | foreach my $querytype (qw(csl nsl)) { | ||||
294 | my $query = "a=g&pm=$querytype\r\n"; | ||||
295 | my $resp = $self->_send([$query]); | ||||
296 | |||||
297 | unless ($resp) { | ||||
298 | $self->{s}->{list} = $list_orig; | ||||
299 | return $self->errprefix("discover1"); | ||||
300 | } | ||||
301 | |||||
302 | # from_batched_query wants "-" in beginning, but not ".\r\n" at end | ||||
303 | $resp->[0] =~ s/\.\r\n$//sg; | ||||
304 | my $h = from_batched_query($resp->[0], {}); | ||||
305 | |||||
306 | foreach my $href (@$h) { | ||||
307 | next unless $href->{$querytype}; | ||||
308 | $self->log (8,"Discovery Server $_ replying with $querytype=$href->{$querytype}"); | ||||
309 | $srvs->{$querytype}->{$href->{$querytype}} = 1; | ||||
310 | } | ||||
311 | unless (keys %{$srvs->{$querytype}}) { | ||||
312 | $self->log (5,"Razor Discovery Server $_ had no valid $querytype servers"); | ||||
313 | next; | ||||
314 | } | ||||
315 | } | ||||
316 | } | ||||
317 | |||||
318 | $self->{s}->{list} = $list_orig; | ||||
319 | |||||
320 | foreach my $querytype (qw(csl nsl)) { | ||||
321 | |||||
322 | my @list = keys %{$srvs->{$querytype}}; | ||||
323 | |||||
324 | #return $self->error("Could not get valid info from Discovery Servers") | ||||
325 | # unless @list; | ||||
326 | unless (@list) { | ||||
327 | if ($self->{force_bootstrap_discovery}) { | ||||
328 | return $self->error("Bootstrap discovery failed. Giving up."); | ||||
329 | } | ||||
330 | $self->log(5, "Couldn't talk to discovery servers. Will force a bootstrap..."); | ||||
331 | $self->{force_bootstrap_discovery} = 1; | ||||
332 | return $self->error("Bootstrap discovery failed. Giving up.") unless $self->bootstrap_discovery(); | ||||
333 | return $self->discover(); | ||||
334 | } | ||||
335 | |||||
336 | fisher_yates_shuffle(\@list) if @list > 1; | ||||
337 | |||||
338 | $self->{s}->{$stype{$querytype}} = \@list; | ||||
339 | push @{$self->{s}->{modified_lst}}, $stype{$querytype}; | ||||
340 | |||||
341 | } | ||||
342 | |||||
343 | $self->disconnect(); | ||||
344 | |||||
345 | unless ($self->{opt}->{server}) { | ||||
346 | if ($self->{breed} =~ /^check/) { | ||||
347 | $self->{s}->{list} = $self->{s}->{catalogue}; | ||||
348 | $self->{s}->{listfile} = $self->{conf}->{listfile_catalogue}; # for discovery() | ||||
349 | } else { | ||||
350 | $self->{s}->{list} = $self->{s}->{nomination}; | ||||
351 | $self->{s}->{listfile} = $self->{conf}->{listfile_nomination}; # for discovery() | ||||
352 | } | ||||
353 | } | ||||
354 | |||||
355 | $self->{s}->{new_list} = 1; | ||||
356 | |||||
357 | $self->{done_discovery} = 1; | ||||
358 | $self->writeservers(); | ||||
359 | |||||
360 | return $self; | ||||
361 | } | ||||
362 | |||||
363 | |||||
364 | # only for debugging and errorchecking | ||||
365 | # | ||||
366 | sub logobj { | ||||
367 | my ($self, $loglevel, $prefix, @objs) = @_; | ||||
368 | |||||
369 | return unless $self->logll($loglevel); | ||||
370 | |||||
371 | foreach my $obj (@objs) { | ||||
372 | my $line = debugobj($obj); | ||||
373 | $self->log($loglevel, "$prefix:\n $line"); | ||||
374 | } | ||||
375 | } | ||||
376 | |||||
377 | |||||
378 | # | ||||
379 | # Mail Object | ||||
380 | # | ||||
381 | # Main data type used by check and report is the Mail Object. | ||||
382 | # an array of hash ref's, where array order matches mails in mbox (or stdin). | ||||
383 | # | ||||
384 | # key = value (not all defined) | ||||
385 | # | ||||
386 | # id = integer NOTE: only key guaranteed to exist | ||||
387 | # orig_mail = ref to string containing orig email (headers+body) | ||||
388 | # headers = headers of orig_email | ||||
389 | # spam = 0, not spam, >1 spam | ||||
390 | # skipme = 0|1 (not checked against server, usually whitelisted mail) | ||||
391 | # p = array ref to mimeparts. see below | ||||
392 | # e1 = similar to p, but special for engine 1 | ||||
393 | # | ||||
394 | # e1: each mail obj contains a special part for engine 1 | ||||
395 | # | ||||
396 | # skipme = 0|1 (ex: 1 if cleaned body goes to 0 len) | ||||
397 | # spam = 0, not spam, >1 spam | ||||
398 | # body = body of orig_mail | ||||
399 | # cleaned = body sent thru razor 1 preproc | ||||
400 | # e1 = hash using engine 1 | ||||
401 | # sent = hash ref sent to server | ||||
402 | # resp = hash ref of server response | ||||
403 | # | ||||
404 | # p: each mail obj contains 1 or more mimeparts, which can contain: | ||||
405 | # | ||||
406 | # id = string - mailid.part | ||||
407 | # skipme = 0|1 (ex: 1 if cleaned body goes to 0 len) | ||||
408 | # spam = 0, not spam, >1 spam | ||||
409 | # body = bodyparts (mimeparts) of orig_email, has X-Razor & Content-* headers | ||||
410 | # cleaned = body sent through preprocessors (deHtml, deQP, etc..), debugging use only | ||||
411 | # e2 = hash using engine 2 | ||||
412 | # e3 = hash using engine 2 | ||||
413 | # e4 = hash using engine 2 | ||||
414 | # sent = array ref of hash ref's sent to server | ||||
415 | # resp = array ref of hash ref's, where hash is parsed sis of server response | ||||
416 | # | ||||
417 | # | ||||
418 | sub prepare_objects { | ||||
419 | my ($self, $objs) = @_; | ||||
420 | my @objects; | ||||
421 | |||||
422 | unless ($self->{s}->{engines} || | ||||
423 | ($self->{s}->{engines} = $self->compute_supported_engines() ) ) { | ||||
424 | $self->log(1, "ALLBAD. supported engines not defined"); | ||||
425 | } | ||||
426 | |||||
427 | my $i = 1; | ||||
428 | if (ref($objs->[0]) eq 'HASH') { # checking cmd-line signatures | ||||
429 | foreach my $o (@$objs) { | ||||
430 | my $obj = { id => $i++ }; | ||||
431 | $obj->{p}->[0]->{id} = "$obj->{id}.0"; | ||||
432 | $obj->{p}->[0]->{"e$o->{eng}"} = $o->{sig}; | ||||
433 | $obj->{ep4} = $o->{ep4} if $o->{ep4}; | ||||
434 | push @objects, $obj; | ||||
435 | } | ||||
436 | |||||
437 | } elsif (ref($objs->[0]) eq 'SCALAR') { # checking/reporting mail | ||||
438 | foreach my $o (@$objs) { | ||||
439 | my $obj = { id => $i++ }; | ||||
440 | $obj->{orig_mail} = $o; | ||||
441 | $self->log2file( 16, $o, "$obj->{id}.orig_mail" ); # includes headers and all | ||||
442 | push @objects, $obj; | ||||
443 | } | ||||
444 | $self->prepare_parts(\@objects); | ||||
445 | } | ||||
446 | $self->logobj(14,"prepared objs", \@objects); | ||||
447 | |||||
448 | return \@objects; | ||||
449 | } | ||||
450 | |||||
451 | sub prepare_parts { | ||||
452 | my ($self, $objs) = @_; | ||||
453 | |||||
454 | my $prep_mail_debug = 0; # debug print, 0=none, 1=split_mime stuff, 2=more verbose | ||||
455 | $prep_mail_debug++ if $self->{conf}->{debuglevel} > 15; | ||||
456 | $prep_mail_debug++ if $self->{conf}->{debuglevel} > 16; | ||||
457 | |||||
458 | foreach my $obj (@$objs) { | ||||
459 | next if ($obj->{skipme} || !$obj->{orig_mail}); | ||||
460 | |||||
461 | # | ||||
462 | # now split up mime parts from orig mail | ||||
463 | # | ||||
464 | my ($headers, @bodyparts) = prep_mail ( | ||||
465 | $obj->{orig_mail}, | ||||
466 | $self->{conf}->{report_headers}, | ||||
467 | 4 * 1024, | ||||
468 | 60 * 1024, | ||||
469 | 15 * 1024, | ||||
470 | $self->{name_version}, | ||||
471 | $prep_mail_debug, # $debug, | ||||
472 | ); | ||||
473 | |||||
474 | my $lines = " prep_mail done: mail $obj->{id} headers=". length($$headers); | ||||
475 | foreach (0..$#bodyparts) { $lines .= ", mime$_=". length(${$bodyparts[$_]}); } | ||||
476 | $self->log(8,$lines); | ||||
477 | |||||
478 | unless (@bodyparts) { | ||||
479 | $self->log(2,"empty body in mail $obj->{id}, skipping"); | ||||
480 | next; | ||||
481 | } | ||||
482 | |||||
483 | $$headers =~ s/\r\n/\n/gs; | ||||
484 | $obj->{headers} = $headers; | ||||
485 | |||||
486 | # $obj->{e1} = { | ||||
487 | # id => "$obj->{id}.e1", | ||||
488 | # body => $obj->{orig_mail}, | ||||
489 | # }; | ||||
490 | |||||
491 | $obj->{p} = []; | ||||
492 | foreach (0..$#bodyparts) { | ||||
493 | $bodyparts[$_] =~ s/\r\n/\n/gs; | ||||
494 | $obj->{p}->[$_] = { | ||||
495 | id => "$obj->{id}.$_", | ||||
496 | body => $bodyparts[$_], | ||||
497 | }; | ||||
498 | } | ||||
499 | } | ||||
500 | return 1; | ||||
501 | } | ||||
502 | |||||
503 | |||||
504 | # given mail objects, fills out | ||||
505 | # | ||||
506 | # - e1 | ||||
507 | # | ||||
508 | # and for each body part of mail object, fills out | ||||
509 | # | ||||
510 | # - cleaned | ||||
511 | # - e2 | ||||
512 | # - e3 | ||||
513 | # - e4 | ||||
514 | # | ||||
515 | # also returns array ref of sigs suitable for printing | ||||
516 | # | ||||
517 | sub compute_sigs { | ||||
518 | my ($self, $objects) = @_; | ||||
519 | my @printable_sigs; | ||||
520 | |||||
521 | foreach my $obj (@$objects) { | ||||
522 | next if ($obj->{skipme} || !$obj->{orig_mail}); | ||||
523 | |||||
524 | if (${$obj->{orig_mail}} =~ /\n(Subject: [^\n]+)\n/) { | ||||
525 | my $subj = substr $1, 0, 70; | ||||
526 | $self->log(8,"mail ". $obj->{id} ." $subj"); | ||||
527 | } else { | ||||
528 | $self->log(8,"mail ". $obj->{id} ." has no subject"); | ||||
529 | } | ||||
530 | |||||
531 | # | ||||
532 | # clean each bodypart, removing if new length is 0 | ||||
533 | # | ||||
534 | next unless $obj->{p}; | ||||
535 | foreach my $objp (@{$obj->{p}}) { | ||||
536 | |||||
537 | next if $objp->{skipme}; | ||||
538 | my $olen = length(${$objp->{body}}); | ||||
539 | my $clnpart = ${$objp->{body}}; | ||||
540 | |||||
541 | # We'll do a VR8 preproc to determine emptiness | ||||
542 | # of email, and store it so VR8 can use it. | ||||
543 | |||||
544 | my $clnpart_vr8 = $clnpart; | ||||
545 | $self->{preproc_vr8}->preproc( \$clnpart_vr8 ); # in da future: $self->{s}->{conf}->{dre} | ||||
546 | $objp->{cleaned_vr8} = \$clnpart_vr8; | ||||
547 | |||||
548 | # This for VR4 (the only other signature scheme | ||||
549 | # supported at this time. | ||||
550 | |||||
551 | $self->{preproc}->preproc( \$clnpart ); | ||||
552 | $objp->{cleaned} = \$clnpart; | ||||
553 | |||||
554 | my $clen = length($clnpart_vr8); | ||||
555 | |||||
556 | $self->log2file( 15, $objp->{body}, "$objp->{id}.before_preproc.as_reported"); | ||||
557 | $self->log2file( 15, $objp->{cleaned}, "$objp->{id}.after_preproc"); | ||||
558 | |||||
559 | if ($clen eq 0) { | ||||
560 | $self->log(6,"preproc: mail $objp->{id} went from $olen bytes to 0, erasing"); | ||||
561 | $objp->{skipme} = 1; | ||||
562 | next; | ||||
563 | } elsif (($clen < 128) and ($clnpart =~ /^(Content\S*:[^\n]*\n\r?)+(Content\S*:[^\n]*)?\s*$/s)) { | ||||
564 | $self->log(6,"preproc: mail $objp->{id} seems empty, erasing"); | ||||
565 | $objp->{skipme} = 1; | ||||
566 | next; | ||||
567 | } elsif ($clnpart_vr8 !~ /\S/) { | ||||
568 | $self->log(6,"preproc: mail $objp->{id} went to all whitespace, erasing"); | ||||
569 | $objp->{skipme} = 1; | ||||
570 | next; | ||||
571 | } elsif ($clen eq $olen) { | ||||
572 | $self->log(6,"preproc: mail $objp->{id} unchanged, bytes=$olen"); | ||||
573 | } else { | ||||
574 | $self->log(6,"preproc: mail $objp->{id} went from $olen bytes to $clen "); | ||||
575 | } | ||||
576 | } | ||||
577 | |||||
578 | |||||
579 | # | ||||
580 | # compute sig for bodyparts that are cleaned. | ||||
581 | # | ||||
582 | if ($self->{s}->{conf}->{ep4}) { | ||||
583 | $obj->{ep4} = $self->{s}->{conf}->{ep4}; | ||||
584 | } else { | ||||
585 | $obj->{ep4} = '7542-10'; | ||||
586 | $self->log(8,"warning: no ep4 for server $self->{s}->{ip}, using $obj->{ep4}"); | ||||
587 | } | ||||
588 | |||||
589 | foreach my $objp (@{$obj->{p}}) { | ||||
590 | |||||
591 | next if $objp->{skipme}; | ||||
592 | |||||
593 | $self->log(15, "mail part is [${$objp->{cleaned}}]"); | ||||
594 | |||||
595 | if (${$objp->{cleaned}} =~ /^\s+$/) { | ||||
596 | $self->log(6, "mail $objp->{id} is whitespace only; skipping!"); | ||||
597 | } | ||||
598 | |||||
599 | $self->log(6,"computing sigs for mail $objp->{id}, len ". length(${$objp->{cleaned}})); | ||||
600 | |||||
601 | foreach (sort keys %{$self->{s}->{engines}}) { | ||||
602 | |||||
603 | my $engine_no = $_; | ||||
604 | my $sig; | ||||
605 | |||||
606 | if ($engine_no == 4) { | ||||
607 | |||||
608 | $sig = $self->compute_engine( | ||||
609 | $engine_no, | ||||
610 | $objp->{cleaned}, | ||||
611 | $obj->{ep4} | ||||
612 | ); | ||||
613 | |||||
614 | } elsif ($engine_no == 8) { | ||||
615 | |||||
616 | $sig = $self->compute_engine( | ||||
617 | $engine_no, | ||||
618 | $objp->{cleaned_vr8} | ||||
619 | ); | ||||
620 | |||||
621 | } else { | ||||
622 | |||||
623 | # Unsupported signature type, don't calculate. | ||||
624 | next; # handled above | ||||
625 | |||||
626 | } | ||||
627 | |||||
628 | if ($sig) { | ||||
629 | |||||
630 | $objp->{"e$engine_no"} = $sig; | ||||
631 | |||||
632 | my @sigs; | ||||
633 | if (ref $sig eq 'ARRAY') { | ||||
634 | @sigs = @$sig; | ||||
635 | } else { | ||||
636 | push @sigs, $sig; | ||||
637 | } | ||||
638 | |||||
639 | for (@sigs) { | ||||
640 | |||||
641 | my $line = "$objp->{id} e$engine_no: $_"; | ||||
642 | $line .= ", ep4: $obj->{ep4}" if ($engine_no eq '4'); | ||||
643 | push @printable_sigs, $line; | ||||
644 | |||||
645 | } | ||||
646 | |||||
647 | } else { | ||||
648 | |||||
649 | $self->log(6,"Engine ($engine_no) didn't produce a signature for mail $objp->{id}"); | ||||
650 | |||||
651 | } | ||||
652 | |||||
653 | } | ||||
654 | |||||
655 | } | ||||
656 | |||||
657 | $self->logobj(14,"computed sigs for obj", $obj); | ||||
658 | |||||
659 | } | ||||
660 | |||||
661 | return \@printable_sigs; | ||||
662 | |||||
663 | } | ||||
664 | |||||
665 | |||||
666 | # | ||||
667 | # this function is the only one that has to be aware | ||||
668 | # of razor protocol syntax. (not including random logging) | ||||
669 | # the hashes generated here are eventually sent to to_batched_query. | ||||
670 | # | ||||
671 | sub make_query { | ||||
672 | my ($self, $params) = @_; | ||||
673 | |||||
674 | |||||
675 | if ($params->{action} =~ /^check/) { | ||||
676 | |||||
677 | if (ref $params->{sig} eq 'ARRAY') { # Multiple signature per part, VR8 | ||||
678 | |||||
679 | my $sigs = $params->{sig}; | ||||
680 | my @queries; | ||||
681 | for (@$sigs) { | ||||
682 | my %query = ( a => 'c', e => $params->{eng}, s => $_ ); | ||||
683 | push @queries, \%query; | ||||
684 | } | ||||
685 | return \@queries; | ||||
686 | |||||
687 | } else { | ||||
688 | |||||
689 | my %query = ( a => 'c', e => $params->{eng}, s => $params->{sig} ); | ||||
690 | $query{ep4} = $params->{ep4} if $query{e} eq '4'; | ||||
691 | return \%query; | ||||
692 | |||||
693 | } | ||||
694 | |||||
695 | } elsif ($params->{action} =~ /^rcheck/) { | ||||
696 | |||||
697 | my %query = ( a => 'r', | ||||
698 | e => $params->{eng}, | ||||
699 | s => $params->{sig}, | ||||
700 | ); | ||||
701 | $query{ep4} = $params->{ep4} if $query{e} eq '4'; | ||||
702 | return \%query; | ||||
703 | |||||
704 | } elsif ($params->{action} =~ /(report)/) { | ||||
705 | |||||
706 | # prep_mail already truncated headers and body parts > 64K | ||||
707 | my @dudes; | ||||
708 | my $n = 0; | ||||
709 | while ($params->{obj}->{p}->[$n]) { | ||||
710 | my $line = ${$params->{obj}->{headers}}; | ||||
711 | while (1) { | ||||
712 | my $body = $params->{obj}->{p}->[$n]->{body}; | ||||
713 | last unless ( (length($$body) + length($line) | ||||
714 | < $self->{s}->{conf}->{bqs} * 1024)); | ||||
715 | |||||
716 | $self->log(11, "bqs=". ($self->{s}->{conf}->{bqs} * 1024) . | ||||
717 | " adding to line [len=". length($line) ."] mail $params->{obj}->{p}->[$n]->{id}" | ||||
718 | ." [len=". length($$body) ."], total len=". (length($$body) + length($line)) ); | ||||
719 | |||||
720 | $line .= "\r\n". $$body; | ||||
721 | $n++; | ||||
722 | last unless $params->{obj}->{p}->[$n]; | ||||
723 | } | ||||
724 | push @dudes, $line; | ||||
725 | } | ||||
726 | |||||
727 | my @queries; | ||||
728 | foreach (@dudes) { | ||||
729 | push @queries, { a => $params->{action} eq 'report' ? 'r' : 'revoke', | ||||
730 | message => $_, | ||||
731 | }; | ||||
732 | } | ||||
733 | return @queries; | ||||
734 | |||||
735 | } elsif ($params->{action} =~ /revoke/) { | ||||
736 | |||||
737 | # Never send messages on revoke. Revoke all signature | ||||
738 | # that we were able to compute. | ||||
739 | |||||
740 | my $n = 0; | ||||
741 | my @queries; | ||||
742 | while ($params->{obj}->{p}->[$n]) { | ||||
743 | for my $engine (keys %{$self->{s}->{engines}}) { | ||||
744 | my $sigs; | ||||
745 | if ($sigs = $params->{obj}->{p}->[$n]->{"e$engine"}) { | ||||
746 | if (ref $sigs eq 'ARRAY') { | ||||
747 | for my $sig (@$sigs) { | ||||
748 | push @queries, {a => 'revoke', e => $engine, s => $sig}; | ||||
749 | } | ||||
750 | } else { | ||||
751 | push @queries, {a => 'revoke', e => $engine, s => $sigs}; | ||||
752 | } | ||||
753 | } | ||||
754 | } | ||||
755 | $n++; | ||||
756 | } | ||||
757 | |||||
758 | return @queries; | ||||
759 | |||||
760 | } | ||||
761 | |||||
762 | } | ||||
763 | |||||
764 | # | ||||
765 | # prepare queries in correct syntax for sending over network | ||||
766 | # | ||||
767 | sub obj2queries { | ||||
768 | my ($self, $objects, $action) = @_; | ||||
769 | my @queries = (); | ||||
770 | |||||
771 | foreach my $obj (@$objects) { | ||||
772 | next if $obj->{skipme}; | ||||
773 | push @queries, $obj->{e1}->{sent} if $obj->{e1}->{sent}; | ||||
774 | foreach my $objp (@{$obj->{p}}) { | ||||
775 | next if $objp->{skipme}; | ||||
776 | #$self->log(8,"not skipping mail part $objp->{id}, sent: ". scalar(@{$objp->{sent}})); | ||||
777 | push @queries, @{$objp->{sent}} if $objp->{sent}; | ||||
778 | } | ||||
779 | } | ||||
780 | |||||
781 | if (scalar(@queries)) { | ||||
782 | $self->log(8,"preparing ". scalar(@queries) ." queries"); | ||||
783 | } else { | ||||
784 | $self->log(8,"objects yielded no valid queries"); | ||||
785 | return []; | ||||
786 | } | ||||
787 | my $qbatched = to_batched_query( \@queries, | ||||
788 | $self->{s}->{conf}->{bql}, | ||||
789 | $self->{s}->{conf}->{bqs}, | ||||
790 | 1); | ||||
791 | $self->log(8,"sending ". scalar(@$qbatched) ." batches"); | ||||
792 | |||||
793 | return $qbatched; | ||||
794 | } | ||||
795 | |||||
796 | |||||
797 | # | ||||
798 | # Parse response syntax, add info to appropriate object | ||||
799 | # | ||||
800 | sub queries2obj { | ||||
801 | my ($self, $objs, $responses, $action) = @_; | ||||
802 | |||||
803 | my @resp; | ||||
804 | foreach (@$responses) { | ||||
805 | # from_batched_query wants "-" in beginning, but not ".\r\n" at end | ||||
806 | s/\.\r\n$//sg; | ||||
807 | my $arrayref = from_batched_query($_); | ||||
808 | push @resp, @$arrayref; | ||||
809 | } | ||||
810 | |||||
811 | $self->log(12,"processing ". scalar(@resp) ." responses"); | ||||
812 | $self->logobj(14,"from_batched_query", \@resp); | ||||
813 | |||||
814 | my $j = 0; | ||||
815 | while (@resp) { | ||||
816 | my $obj = $objs->[$j++]; | ||||
817 | return $self->error("more responses than mail objs!") unless $obj; | ||||
818 | next if $obj->{skipme}; | ||||
819 | |||||
820 | if ($obj->{e1}->{sent} && !$obj->{e1}->{skipme}) { | ||||
821 | $obj->{e1}->{resp} = shift @resp; | ||||
822 | $self->log(12,"adding a resp to mail $obj->{e1}->{id}") ; | ||||
823 | } | ||||
824 | foreach my $objp (@{$obj->{p}}) { | ||||
825 | next unless $objp->{sent}; | ||||
826 | # for each part, shift out as many responses as there were queries | ||||
827 | foreach (@{$objp->{sent}}) { | ||||
828 | push @{$objp->{resp}}, shift @resp; | ||||
829 | $self->log(12,"adding a resp to mail $objp->{id}"); | ||||
830 | } | ||||
831 | } | ||||
832 | #$self->logobj(13,"end of queries2obj",$obj); | ||||
833 | } | ||||
834 | return 1; | ||||
835 | } | ||||
836 | |||||
837 | |||||
838 | sub check_resp { | ||||
839 | my ($self, $me, $sent, $resp, $objp) = @_; | ||||
840 | |||||
841 | # default is no contention | ||||
842 | $objp->{ct} = 0; | ||||
843 | $objp->{ct} = $resp->{ct} if exists $resp->{ct}; | ||||
844 | |||||
845 | if (exists $resp->{err}) { | ||||
846 | $self->logobj(4,"$me: got err $resp->{err} for query", $sent); | ||||
847 | return 0; | ||||
848 | } | ||||
849 | if ($resp->{p} eq '1') { | ||||
850 | if (exists $resp->{cf}) { | ||||
851 | if ($resp->{cf} < $self->{s}->{min_cf}) { | ||||
852 | $self->log (6,"$me: Not spam: cf $resp->{cf} < min_cf $self->{s}->{min_cf}"); | ||||
853 | return 0; | ||||
854 | } else { | ||||
855 | $self->log (6,"$me: Is spam: cf $resp->{cf} >= min_cf $self->{s}->{min_cf}"); | ||||
856 | return 1; | ||||
857 | } | ||||
858 | } | ||||
859 | $self->log (6,"$me: sig found, no cf, ok."); | ||||
860 | return 1; | ||||
861 | } | ||||
862 | if ($resp->{p} eq '0') { | ||||
863 | $self->log (6,"$me: sig not found."); | ||||
864 | return 0; | ||||
865 | } | ||||
866 | # should never get here | ||||
867 | $self->logobj(2,"$me: got bad response from server - sent obj, resp obj", | ||||
868 | [$sent, $resp] ); | ||||
869 | return 0; | ||||
870 | } | ||||
871 | |||||
872 | |||||
873 | sub rcheck_resp { | ||||
874 | my ($self, $me, $sent, $resp) = @_; | ||||
875 | |||||
876 | $self->log(8,"$me: invalid $sent") unless ref($sent); | ||||
877 | $self->log(8,"$me: invalid $resp") unless ref($resp); | ||||
878 | |||||
879 | if (exists $resp->{err}) { | ||||
880 | if ($resp->{err} eq '230') { | ||||
881 | $self->log(8,"$me: err 230 - server wants mail"); | ||||
882 | return 1; | ||||
883 | } | ||||
884 | $self->logobj(4,"$me: got err $resp->{err} for query", $sent); | ||||
885 | return 0; | ||||
886 | } | ||||
887 | if ($resp->{res} eq '1') { | ||||
888 | $self->log (5,"$me: Server accepted report."); | ||||
889 | return 0; | ||||
890 | } | ||||
891 | if ($resp->{res} eq '0') { | ||||
892 | $self->log (1,"$me: Server did not accept report. Shame on the server."); | ||||
893 | return 0; | ||||
894 | } | ||||
895 | # should never get here | ||||
896 | $self->logobj(2,"$me: got bad response from server - sent obj, resp obj", | ||||
897 | [$sent, $resp] ); | ||||
898 | return 0; | ||||
899 | } | ||||
900 | |||||
901 | |||||
902 | sub check { | ||||
903 | my ($self, $objects) = @_; | ||||
904 | |||||
905 | my $valid = 0; | ||||
906 | foreach my $obj (@$objects) { | ||||
907 | next if $obj->{skipme}; | ||||
908 | |||||
909 | # | ||||
910 | # Logic used in ordering of check queries | ||||
911 | # | ||||
912 | # queries should go like this: (e=engine, p=part) | ||||
913 | # e1, p0e2, p0e3, p0e4, p1e2, p1e3, p1e4, etc.. | ||||
914 | # unless cmd-line sigs are passed. | ||||
915 | # | ||||
916 | |||||
917 | # engine 1 is for entire mail, not parts | ||||
918 | if ($obj->{e1} # cmd-line sig checks don't have this | ||||
919 | && $self->{s}->{engines}->{1}) { | ||||
920 | $obj->{e1}->{sent} = $self->make_query( { | ||||
921 | action => 'check', | ||||
922 | sig => $obj->{e1}->{e1}, | ||||
923 | eng => 1 } ); | ||||
924 | } | ||||
925 | # rest of engines and mime parts | ||||
926 | foreach my $objp (@{$obj->{p}}) { | ||||
927 | |||||
928 | if ($objp->{skipme}) { | ||||
929 | $self->log(8,"mail $objp->{id} skipped in check"); | ||||
930 | next; | ||||
931 | } | ||||
932 | $objp->{sent} = []; | ||||
933 | foreach (sort keys %{$self->{s}->{engines} }) { | ||||
934 | |||||
935 | my $engine_save = $_; | ||||
936 | next if $_ eq 1; # engine 1 done above | ||||
937 | my $sig = $objp->{"e$_"}; | ||||
938 | unless ($sig) { | ||||
939 | $self->log(5,"mail $objp->{id} e$_ got no sig"); | ||||
940 | next; | ||||
941 | } | ||||
942 | |||||
943 | unless ($self->{s}->{engines}->{$_}) { | ||||
944 | # warn if cmd-lig sig check is not supported | ||||
945 | $self->log(5,"mail $objp->{id} engine $_ is not supported, sig check skipped") | ||||
946 | if ($sig && !$obj->{orig_mail}); | ||||
947 | next; | ||||
948 | } | ||||
949 | |||||
950 | if (ref $sig) { | ||||
951 | for (@$sig) { | ||||
952 | $self->log(8,"mail $objp->{id} e$engine_save sig: $_"); | ||||
953 | } | ||||
954 | } else { | ||||
955 | $self->log(8,"mail $objp->{id} e$engine_save sig: $sig"); | ||||
956 | } | ||||
957 | |||||
958 | my $query = $self->make_query( { | ||||
959 | action => 'check', | ||||
960 | sig => $sig, | ||||
961 | ep4 => $obj->{ep4}, | ||||
962 | eng => $_ } ); | ||||
963 | |||||
964 | $valid++ if $query; | ||||
965 | if (ref $query eq 'ARRAY') { | ||||
966 | push @{$objp->{sent}}, @$query; | ||||
967 | } else { | ||||
968 | push @{$objp->{sent}}, $query; | ||||
969 | } | ||||
970 | } | ||||
971 | } | ||||
972 | } | ||||
973 | unless ($valid) { | ||||
974 | $self->log (5,"No queries, no spam"); | ||||
975 | return 1; | ||||
976 | } | ||||
977 | |||||
978 | $self->{s}->{list} = $self->{s}->{catalogue}; | ||||
979 | $self->connect; | ||||
980 | |||||
981 | # Build query text strings | ||||
982 | # | ||||
983 | my $queries = $self->obj2queries($objects, 'check') or return $self->errprefix("check 1"); | ||||
984 | |||||
985 | # send to server and store answers in mail obj | ||||
986 | # | ||||
987 | my $response = $self->_send($queries) or return $self->errprefix("check 2"); | ||||
988 | $self->queries2obj($objects, $response, 'check') or return $self->errprefix("check 3"); | ||||
989 | |||||
990 | |||||
991 | foreach my $obj (@$objects) { | ||||
992 | |||||
993 | # check_logic will parse response for each object, decide if its spam | ||||
994 | # | ||||
995 | $self->check_logic($obj); | ||||
996 | |||||
997 | $self->log (3,"mail $obj->{id} is ". ($obj->{spam} ? '' : 'not ') ."known spam."); | ||||
998 | } | ||||
999 | return 1; | ||||
1000 | } | ||||
1001 | |||||
- - | |||||
1004 | sub check_logic { | ||||
1005 | my ($self, $obj) = @_; | ||||
1006 | |||||
1007 | # default is not spam | ||||
1008 | $obj->{spam} = 0; | ||||
1009 | if ($obj->{skipme}) { | ||||
1010 | next; | ||||
1011 | } | ||||
1012 | |||||
1013 | # | ||||
1014 | # Logic for Spam | ||||
1015 | # | ||||
1016 | # | ||||
1017 | my $logic_method = $self->{conf}->{logic_method} || 4; | ||||
1018 | my $logic_engines = $self->{conf}->{logic_engines} || 'any'; | ||||
1019 | |||||
1020 | # cmd-line sig checks default to logic_method 1 | ||||
1021 | $logic_method = 1 unless $obj->{orig_mail}; | ||||
1022 | |||||
1023 | my $leng; | ||||
1024 | if ($logic_engines eq 'any') { | ||||
1025 | $leng = ""; # not a hash ref, implies 'any' logic_engine | ||||
1026 | } elsif ($logic_engines eq 'all') { | ||||
1027 | $leng = $self->{s}->{engines}; | ||||
1028 | } elsif ($logic_engines =~ /^(\d\,)+$/) { | ||||
1029 | $leng = {}; | ||||
1030 | foreach (split /,/,$logic_engines) { | ||||
1031 | unless ($self->{s}->{engines}->{$_}) { | ||||
1032 | $self->log(3, "logic_engine $_ not supported, skipping"); | ||||
1033 | next; | ||||
1034 | } | ||||
1035 | $leng->{$_} = 1; | ||||
1036 | } | ||||
1037 | } else { | ||||
1038 | $self->log(3, "invalid logic_engines: $logic_engines, defaulting to 'any'"); | ||||
1039 | $leng = ""; # not a hash ref, implies 'any' logic_engine | ||||
1040 | } | ||||
1041 | |||||
1042 | |||||
1043 | # iterate through sent queries and responses, | ||||
1044 | # perform engine analysis (logic_engines). | ||||
1045 | # | ||||
1046 | # engine 1 case | ||||
1047 | my $sent = $obj->{e1}->{sent}; | ||||
1048 | my $resp = $obj->{e1}->{resp}; | ||||
1049 | if ($resp && $sent) { | ||||
1050 | # if skipme, there would be no resp | ||||
1051 | my $logmsg = "mail $obj->{id} e=1 sig=$sent->{s}"; | ||||
1052 | $obj->{e1}->{spam} = $self->check_resp($logmsg, $sent, $resp, $obj->{e1}); | ||||
1053 | } | ||||
1054 | # all other engines for all parts | ||||
1055 | foreach my $objp (@{$obj->{p}}) { | ||||
1056 | $objp->{spam} = 0; | ||||
1057 | if ($objp->{skipme}) { | ||||
1058 | $self->log(8,"doh. $objp->{id} is skipped, yet has sent") if $objp->{sent}; | ||||
1059 | next; | ||||
1060 | } | ||||
1061 | next unless $objp->{sent}; | ||||
1062 | my $not_spam = 0; | ||||
1063 | foreach (0..(scalar(@{$objp->{sent}}) - 1)) { | ||||
1064 | $sent = $objp->{sent}->[$_]; | ||||
1065 | $resp = $objp->{resp}->[$_]; | ||||
1066 | unless ($resp) { | ||||
1067 | $self->log(5,"doh. more sent queries than responses"); | ||||
1068 | next; | ||||
1069 | } | ||||
1070 | my $logmsg = "mail $objp->{id} e=$sent->{e} sig=$sent->{s}"; | ||||
1071 | my $is_spam = $self->check_resp($logmsg, $sent, $resp, $objp); | ||||
1072 | |||||
1073 | if (ref($leng)) { | ||||
1074 | if ($leng->{$sent->{e}} && $is_spam) { | ||||
1075 | $self->log(8,"logic_engines requires $sent->{e}, and it is. cool."); | ||||
1076 | $objp->{spam} = 1; | ||||
1077 | } elsif ($leng->{$sent->{e}} && !$is_spam) { | ||||
1078 | $self->log(8,"logic_engines requires $sent->{e}, and it is not, part not spam"); | ||||
1079 | $not_spam = 1; | ||||
1080 | } else { | ||||
1081 | $self->log(8,"logic_engines doesn't care about $sent->{e}, skipping"); | ||||
1082 | } | ||||
1083 | } else { | ||||
1084 | # not a hash ref, implies 'any' logic_engine | ||||
1085 | $objp->{spam} += $is_spam; | ||||
1086 | } | ||||
1087 | $objp->{spam} = 0 if $not_spam; | ||||
1088 | } | ||||
1089 | } | ||||
1090 | |||||
1091 | # mime part analysis (logic_methods) | ||||
1092 | # | ||||
1093 | if ($logic_method == 1) { | ||||
1094 | |||||
1095 | $obj->{spam} = 0; | ||||
1096 | if ($obj->{e1}) { | ||||
1097 | $obj->{spam} += $obj->{e1}->{spam} if $obj->{e1}->{spam}; | ||||
1098 | } | ||||
1099 | foreach my $objp (@{$obj->{p}}) { | ||||
1100 | $obj->{spam} += $objp->{spam} if $objp->{spam}; | ||||
1101 | } | ||||
1102 | |||||
1103 | |||||
1104 | } elsif ($logic_method =~ /^(2|3)$/) { | ||||
1105 | # logic_methods > 1 | ||||
1106 | |||||
1107 | foreach my $objp (@{$obj->{p}}) { | ||||
1108 | next if $objp->{skipme}; | ||||
1109 | next unless $objp->{body}; | ||||
1110 | my ($hdrs, $body) = split /\n\n/, ${$objp->{body}}, 2; | ||||
1111 | $hdrs .= "\n"; | ||||
1112 | |||||
1113 | #$self->log(8,"$objp->{id} hdrs:\n$hdrs"); | ||||
1114 | my $type = "<type unknown>"; | ||||
1115 | $objp->{is_text} = 0; | ||||
1116 | $objp->{is_inline} = 0; | ||||
1117 | $objp->{is_inline} = 1 if $hdrs =~ /Content-Disposition: inline/i; | ||||
1118 | #$type = $1 if $hdrs =~ /Content-Type:\s([^\;\n]+)/i; | ||||
1119 | $type = $1 if $hdrs =~ /Content-Type:\s([^\n]+)/i; | ||||
1120 | $objp->{is_text} = 1 if $type =~ /text\//i; | ||||
1121 | $objp->{is_text} = 1 if $type =~ /type unknown/; # assume text ? | ||||
1122 | |||||
1123 | $self->log(8,"mail $objp->{id} Type $objp->{is_text},$objp->{is_inline} $type"); | ||||
1124 | } | ||||
1125 | } | ||||
1126 | |||||
1127 | if ($logic_method == 2) { | ||||
1128 | |||||
1129 | # in this method, only 1 dude decides if mail is spam. decider. | ||||
1130 | |||||
1131 | # the first part is the default decider. can be overwritten, tho. | ||||
1132 | my $decider = $obj->{p}->[0]; | ||||
1133 | |||||
1134 | # basically the first inline text/* becomes the decider. | ||||
1135 | # however, if no inline, the first text/* is used | ||||
1136 | my $found = 0; | ||||
1137 | foreach my $objp (@{$obj->{p}}) { | ||||
1138 | next if $objp->{skipme}; | ||||
1139 | |||||
1140 | if ($objp->{is_inline} && $objp->{is_text}) { | ||||
1141 | $decider = $objp; | ||||
1142 | last; | ||||
1143 | } | ||||
1144 | if (!$found && $objp->{is_text}) { | ||||
1145 | $decider = $objp; | ||||
1146 | $found = 1; | ||||
1147 | } | ||||
1148 | } | ||||
1149 | $self->log (7,"method 2: $decider->{id} is the spam decider"); | ||||
1150 | $obj->{spam} = $decider->{spam}; | ||||
1151 | |||||
1152 | } elsif ($logic_method == 3) { | ||||
1153 | |||||
1154 | # in this method, all text/* parts must be spam for obj to be spam | ||||
1155 | # non-text parts are ignored | ||||
1156 | |||||
1157 | my $found = 0; | ||||
1158 | foreach my $objp (@{$obj->{p}}) { | ||||
1159 | next if $objp->{skipme}; | ||||
1160 | next unless $objp->{is_text}; | ||||
1161 | $found = 1; | ||||
1162 | $obj->{spam} = $objp->{spam}; | ||||
1163 | unless ($objp->{spam}) { | ||||
1164 | $self->log (7,"method 3: $objp->{id} is_text but not spam, mail not spam"); | ||||
1165 | last; | ||||
1166 | } | ||||
1167 | } | ||||
1168 | $self->log (7,"method 3: mail $obj->{id}: all is_text parts spam, mail spam") if $obj->{spam}; | ||||
1169 | |||||
1170 | # if no parts where text, use the first part as spam indicator | ||||
1171 | unless ($found) { | ||||
1172 | $self->log (6,"method 3: mail $obj->{id}: no is_text, using part 1"); | ||||
1173 | $obj->{spam} = 1 if $obj->{p}->[0]->{spam}; | ||||
1174 | } | ||||
1175 | |||||
1176 | } elsif ($logic_method == 4) { | ||||
1177 | |||||
1178 | # in this method, if any non-contention parts is spam, mail obj is spam | ||||
1179 | # contention parts are ignored. | ||||
1180 | |||||
1181 | $obj->{spam} = 0; | ||||
1182 | foreach my $objp (@{$obj->{p}}) { | ||||
1183 | next if $objp->{skipme}; | ||||
1184 | if ($objp->{ct}) { | ||||
1185 | $self->log (7,"method 4: mail $objp->{id}: contention part, skipping"); | ||||
1186 | } else { | ||||
1187 | $self->log (7,"method 4: mail $objp->{id}: no-contention part, spam=$objp->{spam}"); | ||||
1188 | $obj->{spam} = 1 if $objp->{spam}; | ||||
1189 | } | ||||
1190 | } | ||||
1191 | if ($obj->{spam}) { | ||||
1192 | $self->log (7,"method 4: mail $obj->{id}: a non-contention part was spam, mail spam"); | ||||
1193 | } else { | ||||
1194 | $self->log (7,"method 4: mail $obj->{id}: all non-contention parts not spam, mail not spam"); | ||||
1195 | } | ||||
1196 | |||||
1197 | } elsif ($logic_method == 5) { | ||||
1198 | |||||
1199 | # in this method, all non-contention parts must be spam for obj to be spam | ||||
1200 | # contention parts are ignored. | ||||
1201 | |||||
1202 | my $not_spam = 0; | ||||
1203 | my $is_spam = 0; | ||||
1204 | foreach my $objp (@{$obj->{p}}) { | ||||
1205 | next if $objp->{skipme}; | ||||
1206 | if ($objp->{ct}) { | ||||
1207 | $self->log (7,"method 5: mail $objp->{id}: contention part, skipping"); | ||||
1208 | next; | ||||
1209 | } else { | ||||
1210 | $self->log (7,"method 5: mail $objp->{id}: no-contention part, spam=$objp->{spam}"); | ||||
1211 | } | ||||
1212 | if ($objp->{spam}) { | ||||
1213 | $is_spam = 1; | ||||
1214 | } else { | ||||
1215 | $not_spam = 1; | ||||
1216 | } | ||||
1217 | } | ||||
1218 | if ($is_spam && !$not_spam) { | ||||
1219 | $obj->{spam} = 1; | ||||
1220 | $self->log (7,"method 5: mail $obj->{id}: all non-contention parts spam, mail spam"); | ||||
1221 | } else { | ||||
1222 | $self->log (7,"method 5: mail $obj->{id}: a non-contention part not spam, mail not spam"); | ||||
1223 | $obj->{spam} = 0; | ||||
1224 | } | ||||
1225 | |||||
1226 | } | ||||
1227 | return 1; | ||||
1228 | } | ||||
1229 | |||||
- - | |||||
1232 | # returns hash ref if successfully registered | ||||
1233 | # returns 0 if not | ||||
1234 | sub register { | ||||
1235 | my ($self, $p,) = @_; | ||||
1236 | my @queries; | ||||
1237 | |||||
1238 | my $registrar = $self->{name_version}; | ||||
1239 | my %qr = ( a => 'reg', registrar => $registrar ); | ||||
1240 | $qr{user} = $p->{user} if $p->{user}; | ||||
1241 | $qr{pass} = $p->{pass} if $p->{pass}; | ||||
1242 | $queries[0] = makesis(%qr); | ||||
1243 | |||||
1244 | $self->{s}->{list} = $self->{s}->{nomination}; | ||||
1245 | $self->connect; | ||||
1246 | |||||
1247 | my $response = $self->_send(\@queries) or return $self->errprefix("register"); | ||||
1248 | |||||
1249 | my %resp = parsesis($$response[0]); | ||||
1250 | |||||
1251 | if ($resp{err} && $resp{err} eq '210') { | ||||
1252 | if ($qr{user} && $qr{pass}) { | ||||
1253 | my($creds) = { user => $qr{user}, pass => $qr{pass} }; | ||||
1254 | if ($self->authenticate($creds)) { | ||||
1255 | $self->log(6, "Successfully registered provided credentials.\n"); | ||||
1256 | return $creds; | ||||
1257 | } | ||||
1258 | } | ||||
1259 | return $self->error("Error $resp{err}: User exists. Try another name. aborting.\n") | ||||
1260 | } | ||||
1261 | return $self->error("Error $resp{err} while performing register, aborting.\n") | ||||
1262 | if ($resp{err}); | ||||
1263 | |||||
1264 | return $self->error("No success (res=$resp{res}) while performing register, aborting.\n") | ||||
1265 | if ($resp{res} ne '1'); | ||||
1266 | |||||
1267 | $self->log(6,"Successfully registered with $self->{s}->{ip} identity: $resp{user}"); | ||||
1268 | |||||
1269 | # otherwise return hash containing 'user' and 'pass' | ||||
1270 | delete $resp{res}; | ||||
1271 | return \%resp; | ||||
1272 | } | ||||
1273 | |||||
1274 | |||||
1275 | sub authenticate { | ||||
1276 | my ($self, $options) = @_; | ||||
1277 | my @queries; | ||||
1278 | |||||
1279 | unless (($options->{user} =~ /\S/) && ($options->{pass} =~ /\S/)) { | ||||
1280 | return $self->error("authenticate did not get valid user + pass"); | ||||
1281 | } | ||||
1282 | |||||
1283 | my %qr = ( a => 'ai', user => $options->{user}, cn => 'razor-agents', cv => $Razor2::Client::Version::VERSION ); | ||||
1284 | $queries[0] = makesis(%qr); | ||||
1285 | |||||
1286 | $self->{s}->{list} = $self->{s}->{nomination}; | ||||
1287 | $self->connect; | ||||
1288 | |||||
1289 | my $response = $self->_send(\@queries) or return $self->errprefix("authenticate 1"); | ||||
1290 | |||||
1291 | my %resp = parsesis($$response[0]); | ||||
1292 | if ($resp{err}) { | ||||
1293 | if (($resp{err} eq '213') && !defined($self->{reregistered})) { | ||||
1294 | # 213 = unknown user. | ||||
1295 | # Try to register with current user+pass and continue with authenticate | ||||
1296 | $self->log (8,"unknown user, attempting to re-register"); | ||||
1297 | |||||
1298 | my $id = $self->register($options); | ||||
1299 | $self->{reregistered} = 1; | ||||
1300 | if (($id->{user} eq $options->{user}) && | ||||
1301 | ($id->{pass} eq $options->{pass})) { | ||||
1302 | $self->log (5,"re-registered user $id->{user} with $self->{s}->{ip}"); | ||||
1303 | return $self->authenticate($options); | ||||
1304 | } else { | ||||
1305 | return $self->error("Error 213 while authenticating, aborting.\n") | ||||
1306 | } | ||||
1307 | } else { | ||||
1308 | return $self->error("Error $resp{err} while authenticating, aborting.\n") | ||||
1309 | } | ||||
1310 | } | ||||
1311 | my ($iv1, $iv2) = xor_key($options->{pass}); | ||||
1312 | my ($my_digest) = hmac_sha1($resp{achal}, $iv1, $iv2); | ||||
1313 | |||||
1314 | %qr = ( a => 'auth', aresp => $my_digest ); | ||||
1315 | $queries[0] = makesis(%qr); | ||||
1316 | |||||
1317 | $response = $self->_send(\@queries) or return $self->errprefix("authenticate 2"); | ||||
1318 | |||||
1319 | %resp = parsesis($$response[0]); | ||||
1320 | return $self->error("Error $resp{err} while authenticating, aborting.\n") if ($resp{err}); | ||||
1321 | return $self->error("Authentication failed for user=$options->{user}") | ||||
1322 | if ($resp{res} ne '1'); | ||||
1323 | |||||
1324 | $self->log (5,"Authenticated user=$options->{user}"); | ||||
1325 | $self->{authenticated} = 1; | ||||
1326 | return 1; | ||||
1327 | } | ||||
1328 | |||||
1329 | |||||
1330 | # | ||||
1331 | # handles report and revoke | ||||
1332 | # | ||||
1333 | sub report { | ||||
1334 | |||||
1335 | my ($self, $objs) = @_; | ||||
1336 | return $self->error("report: Not Authenticated") unless $self->{authenticated}; | ||||
1337 | |||||
1338 | return $self->error("report/revoke for engine 1 not supported") | ||||
1339 | if ($self->{s}->{conf}->{dre} == 1); | ||||
1340 | |||||
1341 | $self->{s}->{list} = $self->{s}->{nomination}; | ||||
1342 | $self->connect; | ||||
1343 | |||||
1344 | my @robjs; | ||||
1345 | my $valid = 0; | ||||
1346 | if ($self->{breed} eq 'report') { | ||||
1347 | |||||
1348 | # | ||||
1349 | # Before reporting entire email, check to see if server already has it | ||||
1350 | # | ||||
1351 | |||||
1352 | unless ($self->{s}->{conf}->{dre}) { | ||||
1353 | $self->logobj(8,"server has no default dre, using 4", $self->{s}->{conf}); | ||||
1354 | $self->{s}->{conf}->{dre} = 4; | ||||
1355 | } | ||||
1356 | |||||
1357 | foreach my $obj (@$objs) { | ||||
1358 | next if $obj->{skipme}; | ||||
1359 | |||||
1360 | # handle special case for engine 1 | ||||
1361 | # note: razor 1 does not store emails in its db, just sigs. | ||||
1362 | # so we should never get a res=230 for e=1 a=r sig=xxx | ||||
1363 | # | ||||
1364 | #$obj->{e1}->{sent} = $self->make_query( { | ||||
1365 | # action => 'rcheck', | ||||
1366 | # sig => $obj->{e1}->{e1}, | ||||
1367 | # eng => 1, } ); | ||||
1368 | #$valid++ if $obj->{e1}->{sent}; | ||||
1369 | |||||
1370 | # rest of engines and mime parts | ||||
1371 | foreach my $objp (@{$obj->{p}}) { | ||||
1372 | |||||
1373 | if ($objp->{skipme}) { | ||||
1374 | $self->log(13,"mail $objp->{id} skipped in report"); | ||||
1375 | next; | ||||
1376 | } | ||||
1377 | my $q = $self->make_query( { | ||||
1378 | action => 'rcheck', | ||||
1379 | sig => $objp->{"e$self->{s}->{conf}->{dre}"}, | ||||
1380 | ep4 => $obj->{ep4}, | ||||
1381 | eng => $self->{s}->{conf}->{dre}, } ); | ||||
1382 | $objp->{sent} = [$q]; | ||||
1383 | $valid++; | ||||
1384 | } | ||||
1385 | } | ||||
1386 | unless ($valid) { | ||||
1387 | $self->log (5,"No report check queries, no spam"); | ||||
1388 | return 1; | ||||
1389 | } | ||||
1390 | $valid = 0; | ||||
1391 | |||||
1392 | |||||
1393 | # Build query text strings - signatures computed already (see reportit) | ||||
1394 | my $queries = $self->obj2queries($objs,'rcheck') or return $self->errprefix("report1"); | ||||
1395 | |||||
1396 | # send to server and store answers in mail obj | ||||
1397 | my $response = $self->_send($queries) or return $self->errprefix("report2"); | ||||
1398 | $self->queries2obj($objs, $response, 'rcheck') or return $self->errprefix("report3"); | ||||
1399 | |||||
1400 | |||||
1401 | # | ||||
1402 | # If server wants email or certain body parts, | ||||
1403 | # create new {sent} and add obj to @robjs | ||||
1404 | # | ||||
1405 | foreach my $obj (@$objs) { | ||||
1406 | |||||
1407 | next if $obj->{skipme}; | ||||
1408 | |||||
1409 | #$self->log(12,"mail $obj->{id} read ". scalar(@{$obj->{resp}}) ." queries"); | ||||
1410 | # handle engine 1 special case | ||||
1411 | #if ( !$obj->{e1}->{skipme} && $self->rcheck_resp( | ||||
1412 | # "mail ". $obj->{id} .", orig_email, special case eng 1", | ||||
1413 | # $obj->{e1}->{sent}, | ||||
1414 | # $obj->{e1}->{resp} | ||||
1415 | # ) ) { | ||||
1416 | # $self->log(5,"doh. Server should not send res=230 for eng=1 report"); | ||||
1417 | #} | ||||
1418 | #delete $obj->{e1}->{sent}; | ||||
1419 | |||||
1420 | my $wants_orig_mail = 0; | ||||
1421 | foreach my $objp (@{$obj->{p}}) { | ||||
1422 | next if $objp->{skipme}; | ||||
1423 | |||||
1424 | $self->logobj(14,"checking response for $objp->{id}", $objp); | ||||
1425 | unless ( $self->rcheck_resp( | ||||
1426 | "mail $objp->{id}, eng $self->{s}->{conf}->{dre}", | ||||
1427 | $objp->{sent}->[0], $objp->{resp}->[0] )) { | ||||
1428 | $objp->{skipme} = 1; | ||||
1429 | } else { | ||||
1430 | $wants_orig_mail++; | ||||
1431 | } | ||||
1432 | $objp->{resp} = []; # clear responses from rcheck | ||||
1433 | $objp->{sent} = []; | ||||
1434 | } | ||||
1435 | if ($wants_orig_mail) { | ||||
1436 | # reports are special, all parts need to be together, so use part 0's sent | ||||
1437 | my $objp = $obj->{p}->[0]; | ||||
1438 | $objp->{skipme} = 0 if $objp->{skipme}; | ||||
1439 | push @{$objp->{sent}}, $self->make_query( { | ||||
1440 | action => 'report', | ||||
1441 | obj => $obj, | ||||
1442 | } ); | ||||
1443 | push @robjs, $obj; | ||||
1444 | } | ||||
1445 | $valid += $wants_orig_mail; | ||||
1446 | } | ||||
1447 | |||||
1448 | } else { # revoke | ||||
1449 | |||||
1450 | foreach my $obj (@$objs) { | ||||
1451 | # don't revoke eng 1 | ||||
1452 | |||||
1453 | # engines > 1 we send all the body parts, use part 0 to store sent | ||||
1454 | my $objp = $obj->{p}->[0]; | ||||
1455 | $objp->{sent} = []; | ||||
1456 | |||||
1457 | push @{$objp->{sent}}, $self->make_query( { | ||||
1458 | action => 'revoke', | ||||
1459 | obj => $obj, | ||||
1460 | } ); | ||||
1461 | $valid++ if scalar(@{$objp->{sent}}); | ||||
1462 | $self->log (9,"revoke sent:". scalar(@{$objp->{sent}})); | ||||
1463 | push @robjs, $obj; | ||||
1464 | } | ||||
1465 | |||||
1466 | } | ||||
1467 | |||||
1468 | unless ($valid && scalar(@robjs)) { | ||||
1469 | $self->log (3,"Finished $self->{breed}."); | ||||
1470 | return 1; | ||||
1471 | } | ||||
1472 | |||||
1473 | #$self->logobj(14,"report objs", \@robjs); | ||||
1474 | |||||
1475 | # | ||||
1476 | # send server mails/body parts either | ||||
1477 | # revoked, or requested if reporting | ||||
1478 | # | ||||
1479 | my $queries = $self->obj2queries( \@robjs,$self->{breed}) or return $self->errprefix("report4"); | ||||
1480 | my $response = $self->_send( $queries ) or return $self->errprefix("report5"); | ||||
1481 | $self->queries2obj( \@robjs, $response,$self->{breed}) or return $self->errprefix("report6"); | ||||
1482 | |||||
1483 | # we just do this to log server's response | ||||
1484 | # | ||||
1485 | foreach my $obj (@robjs) { | ||||
1486 | my $objp = $obj->{p}->[0]; | ||||
1487 | my $cur = -1; | ||||
1488 | while ($objp->{sent}->[++$cur]) { | ||||
1489 | $self->rcheck_resp( | ||||
1490 | "$self->{breed}: mail $obj->{id}, $cur", | ||||
1491 | $objp->{sent}->[$cur], | ||||
1492 | $objp->{resp}->[$cur] ) unless ($objp->{skipme}); | ||||
1493 | } | ||||
1494 | } | ||||
1495 | $self->logobj(14,"report objs", \@robjs); | ||||
1496 | $self->log (3,"Sent $self->{breed}."); | ||||
1497 | return 1; | ||||
1498 | |||||
1499 | } | ||||
1500 | |||||
- - | |||||
1503 | sub _send { | ||||
1504 | my ($self, $msg, $closesock, $skipread) = @_; | ||||
1505 | $self->log (16,"entered _send"); | ||||
1506 | |||||
1507 | unless ($self->{connected_to}) { | ||||
1508 | $self->connect() or return $self->errprefix("_send"); | ||||
1509 | } | ||||
1510 | |||||
1511 | my @response; | ||||
1512 | my $select = $self->{select}; | ||||
1513 | my $sock = ($select->handles)[0]; | ||||
1514 | $self->{sent_cnt} = 0 unless $self->{sent_cnt}; | ||||
1515 | foreach my $i (0 .. ((scalar @$msg) -1) ) { | ||||
1516 | my @handles = $select->can_write (15); | ||||
1517 | if ($handles[0]) { | ||||
1518 | $self->log (4,"$self->{connected_to} << ". length($$msg[$i]) ); | ||||
1519 | if ($$msg[$i] =~ /message/) { | ||||
1520 | my $line = debugobj($$msg[$i]); | ||||
1521 | $self->log (6, $line ); | ||||
1522 | $self->log2file(16, \$$msg[$i], "sent_to.". $self->{sent_cnt}); | ||||
1523 | } else { | ||||
1524 | $self->log (6, $$msg[$i] ); | ||||
1525 | } | ||||
1526 | local $\; | ||||
1527 | undef $\; | ||||
1528 | print $sock $$msg[$i]; | ||||
1529 | $self->{sent_cnt}++; | ||||
1530 | } else { | ||||
1531 | return $self->error("Timed out (15 sec) while writing to $self->{s}->{ip}"); | ||||
1532 | } | ||||
1533 | next if $skipread; | ||||
1534 | @handles = $select->can_read (15); | ||||
1535 | if ($sock=$handles[0]) { | ||||
1536 | local $/; | ||||
1537 | undef $/; | ||||
1538 | $response[$i] = $self->_read($sock) or return $self->error("Error reading socket"); | ||||
1539 | $self->log (4,"$self->{connected_to} >> ". length($response[$i]) ); | ||||
1540 | $self->log (6,"response to sent.$self->{sent_cnt}\n". $response[$i]); | ||||
1541 | } else { | ||||
1542 | return $self->error("Timed out (15 sec) while reading from $self->{s}->{ip}"); | ||||
1543 | } | ||||
1544 | } | ||||
1545 | if ($closesock) { | ||||
1546 | $select->remove($sock); | ||||
1547 | close $sock; | ||||
1548 | } | ||||
1549 | return \@response; | ||||
1550 | } | ||||
1551 | |||||
1552 | |||||
1553 | sub _read { | ||||
1554 | my ($self, $socket) = @_; | ||||
1555 | my ($query, $read); | ||||
1556 | |||||
1557 | # fixme - need to trim this down (copied from server) | ||||
1558 | # | ||||
1559 | |||||
1560 | unless ($read = sysread($socket, $query, 1024)) { | ||||
1561 | |||||
1562 | # There was an error on sysread(), could be a real error or a | ||||
1563 | # blocking error. | ||||
1564 | |||||
1565 | if ($! == EWOULDBLOCK) { | ||||
1566 | # write would block, so we try again later | ||||
1567 | $self->debug ("_read: EWOULDBLOCK"); | ||||
1568 | return; | ||||
1569 | } elsif ($! == EINTR or $! == EIO) { | ||||
1570 | # sysread() got interupted by a signal. | ||||
1571 | # we will process this socket on next wheelwalk. | ||||
1572 | $self->debug ("_read: EINTR"); | ||||
1573 | return; | ||||
1574 | } elsif ($! == EPIPE or $! == EISDIR or $! == EBADF or $! == EINVAL or $! == EFAULT) { | ||||
1575 | $self->debug ("_read: EPIPE"); | ||||
1576 | return; | ||||
1577 | } else { | ||||
1578 | # This happens when client breaks the connection. | ||||
1579 | # Find out why we don't get an EPIPE instead. FIX! | ||||
1580 | $self->debug ("_read: connection_closed"); | ||||
1581 | return; | ||||
1582 | } | ||||
1583 | } | ||||
1584 | |||||
1585 | if ($read > 0) { | ||||
1586 | |||||
1587 | # Now we are absolutely sure there is data on the socket. | ||||
1588 | |||||
1589 | return $query; | ||||
1590 | |||||
1591 | } else { | ||||
1592 | |||||
1593 | # Otherwise we got an EOF, expire the socket | ||||
1594 | |||||
1595 | $self->debug ("_read: EOF, connection_closed"); | ||||
1596 | return; | ||||
1597 | |||||
1598 | } | ||||
1599 | } | ||||
1600 | |||||
- - | |||||
1603 | sub connect { | ||||
1604 | my ($self, %params) = @_; | ||||
1605 | my $sock; | ||||
1606 | $self->log (16,"entered connect"); | ||||
1607 | |||||
1608 | if ($self->{simulate}) { | ||||
1609 | return $self->error ("Razor Error 4: This is a simulation. Won't connect to $self->{s}->{ip}."); | ||||
1610 | } | ||||
1611 | |||||
1612 | my $server = $params{server} || $self->{s}->{ip}; | ||||
1613 | |||||
1614 | unless ($self->{s}->{ip}) { | ||||
1615 | $self->{s}->{ip} = $server; | ||||
1616 | } | ||||
1617 | |||||
1618 | if ($self->{sock} && $self->{connected_to}) { | ||||
1619 | unless ($server) { | ||||
1620 | $self->log (13,"no server specified, using already connected server $self->{connected_to}"); | ||||
1621 | return 1; | ||||
1622 | } | ||||
1623 | if ($server eq $self->{connected_to}) { | ||||
1624 | $self->log (15,"already connected to server $self->{connected_to}"); | ||||
1625 | return 1; | ||||
1626 | } | ||||
1627 | return 1 if $self->{disconnecting}; | ||||
1628 | $self->log(6,"losing old server connection, $self->{connected_to}, for new server, $server"); | ||||
1629 | $self->disconnect; | ||||
1630 | } | ||||
1631 | unless ($server) { | ||||
1632 | $self->log (6,"no server specified, not connecting"); | ||||
1633 | return; | ||||
1634 | } | ||||
1635 | |||||
1636 | my $port = $params{port} || $self->{s}->{port}; | ||||
1637 | unless (defined($port) && $port =~ /^\d+$/) { | ||||
1638 | my $portlog = defined($port) ? " ($port)" : ""; | ||||
1639 | $self->log (6, "No port specified$portlog, using 2703"); # bootstrap_discovery will come here | ||||
1640 | $port = 2703; | ||||
1641 | } | ||||
1642 | $self->log (5,"Connecting to $server ..."); | ||||
1643 | if (my $proxy = $self->{conf}->{proxy}) { | ||||
1644 | # | ||||
1645 | # Proxy stuff never been tested | ||||
1646 | # | ||||
1647 | $proxy =~ s!^http://!!; | ||||
1648 | $proxy =~ s!:(\d+)/?$!!; | ||||
1649 | my $pport = $1 || 80; | ||||
1650 | $self->debug ("HTTP tunneling through $proxy:$pport."); | ||||
1651 | $sock = IO::Socket::INET->new( | ||||
1652 | PeerAddr => $proxy, | ||||
1653 | PeerPort => $pport, | ||||
1654 | Proto => 'tcp', | ||||
1655 | Timeout => 20, | ||||
1656 | ); | ||||
1657 | unless ( $sock ) { | ||||
1658 | $self->debug ("Unable to connect to proxy $proxy:$pport; Reason: $!."); | ||||
1659 | } else { | ||||
1660 | $sock->printf( "CONNECT %s:%d HTTP/1.0\r\n\r\n", $server, $port ); | ||||
1661 | if( $sock->getline =~ m!^HTTP/1\.\d+ 200 ! ){ | ||||
1662 | # Skip through remaining part of MIME header. | ||||
1663 | while( $sock->getline !~ m!^\r! ){ ; } | ||||
1664 | } else { | ||||
1665 | $self->log (4, "HTTP tunneling is disabled at $proxy."); | ||||
1666 | $sock = undef; | ||||
1667 | } | ||||
1668 | } | ||||
1669 | } | ||||
1670 | |||||
1671 | # if proxy, we already might have a $sock. | ||||
1672 | # if proxy failed to connect, try without proxy. | ||||
1673 | # | ||||
1674 | |||||
1675 | if ($self->{conf}->{socks_server}) { | ||||
1676 | |||||
1677 | my $socks_module = "Net::SOCKS"; | ||||
1678 | eval "require $socks_module"; | ||||
1679 | |||||
1680 | $self->log(6, "Will try to connect through the SOCKS server on $$self{conf}{socks_server}..."); | ||||
1681 | |||||
1682 | my $socks_sock = Net::SOCKS->new ( | ||||
1683 | socks_addr => $$self{conf}{socks_server}, | ||||
1684 | socks_port => 1080, | ||||
1685 | protocol_version => 4 | ||||
1686 | ); | ||||
1687 | |||||
1688 | if ($socks_sock) { | ||||
1689 | |||||
1690 | $sock = $socks_sock->connect(peer_addr => $server, peer_port => $port); | ||||
1691 | if ($sock) { | ||||
1692 | $self->log(6, "Connected to $server via SOCKS server $$self{conf}{socks_server}."); | ||||
1693 | } | ||||
1694 | |||||
1695 | } | ||||
1696 | |||||
1697 | } | ||||
1698 | |||||
1699 | |||||
1700 | unless ($sock) { | ||||
1701 | $sock = IO::Socket::INET->new( | ||||
1702 | PeerAddr => $server, | ||||
1703 | PeerPort => $port, | ||||
1704 | Proto => 'tcp', | ||||
1705 | Timeout => 20, | ||||
1706 | ); | ||||
1707 | unless ( $sock ) { | ||||
1708 | $self->log (3,"Unable to connect to $server:$port; Reason: $!."); | ||||
1709 | return if $params{discovery_server}; | ||||
1710 | $self->nextserver or do { return $self->errprefix("connect1"); }; | ||||
1711 | return $self->connect; | ||||
1712 | } | ||||
1713 | } | ||||
1714 | |||||
1715 | my $select = new IO::Select ($sock); | ||||
1716 | my @handles = $select->can_read (15); | ||||
1717 | if ($handles[0]) { | ||||
1718 | $self->log (8,"Connection established"); | ||||
1719 | my $greeting = <$sock>; | ||||
1720 | # $sock->autoflush; # this is on by default as of IO::Socket 1.18 | ||||
1721 | $self->{sock} = $sock; | ||||
1722 | $self->{connected_to} = $server; | ||||
1723 | $self->{select} = $select; | ||||
1724 | $self->log(4,"$server >> ". length($greeting) ." server greeting: $greeting"); | ||||
1725 | |||||
1726 | return 1 if $params{discovery_server}; | ||||
1727 | unless ($self->parse_greeting($greeting) ) { | ||||
1728 | $self->nextserver or return $self->errprefix("connect2"); | ||||
1729 | return $self->connect; | ||||
1730 | } | ||||
1731 | return 1; | ||||
1732 | } else { | ||||
1733 | $self->log (3, "Timed out (15 sec) while reading from $self->{s}->{ip}."); | ||||
1734 | $select->remove($sock); | ||||
1735 | $sock->close(); | ||||
1736 | return $self->errprefix("connect3") if $params{skip_greeting}; | ||||
1737 | $self->nextserver or return $self->errprefix("connect4"); | ||||
1738 | return $self->connect; | ||||
1739 | } | ||||
1740 | } | ||||
1741 | |||||
1742 | sub disconnect { | ||||
1743 | my $self = shift; | ||||
1744 | |||||
1745 | unless ($self->{sock}) { | ||||
1746 | $self->log (5,"already disconnected from server ". $self->{connected_to}); | ||||
1747 | return 1; | ||||
1748 | } | ||||
1749 | |||||
1750 | $self->log (5,"disconnecting from server ". $self->{connected_to}); | ||||
1751 | |||||
1752 | $self->{disconnecting} = 1; | ||||
1753 | $self->_send(["a=q\r\n"], 0, 1); | ||||
1754 | delete $self->{disconnecting}; | ||||
1755 | |||||
1756 | delete $self->{sock}; # _send closes socket | ||||
1757 | |||||
1758 | |||||
1759 | return 1; | ||||
1760 | } | ||||
1761 | |||||
1762 | |||||
1763 | sub parse_greeting { | ||||
1764 | my ($self, $greeting) = @_; | ||||
1765 | $self->log (16,"entered parse_greeting($greeting)"); | ||||
1766 | |||||
1767 | my %server_greeting = parsesis($greeting); | ||||
1768 | $self->{s}->{greeting} = \%server_greeting; | ||||
1769 | |||||
1770 | unless ($self->{s}->{greeting} && $self->{s}->{greeting}->{sn}) { | ||||
1771 | $self->log(1,"Couldn't parse server greeting\n"); | ||||
1772 | return; | ||||
1773 | } | ||||
1774 | |||||
1775 | # server greeting must contain: sn, srl | ||||
1776 | # server greeting may contain: ep4, redirect, a, | ||||
1777 | |||||
1778 | # | ||||
1779 | # fixme - add support for redirect, etc. | ||||
1780 | # | ||||
1781 | |||||
1782 | # | ||||
1783 | # current server config info is stored in $self->{s}->{conf} | ||||
1784 | # see nextserver for more info | ||||
1785 | # | ||||
1786 | # If server greeting says there are new values | ||||
1787 | # (which we know if greeting's srl > conf's srl) | ||||
1788 | # we ask server for new values, update conf, then | ||||
1789 | # put that server on modified list so it gets recorded to disk | ||||
1790 | # | ||||
1791 | # fixme - in the future, we could have a key with no value | ||||
1792 | # in .conf file - forcing client to ask server 'a=g&pm=key' | ||||
1793 | # | ||||
1794 | |||||
1795 | if ($self->{s}->{greeting}->{a} eq 'cg') { | ||||
1796 | my $version = $Razor2::Client::Version::VERSION; | ||||
1797 | my @cg = ("cn=razor-agents&cv=$version\r\n"); | ||||
1798 | $self->_send(\@cg, 0, 1); | ||||
1799 | } | ||||
1800 | |||||
1801 | if (defined($self->{s}->{greeting}->{srl}) && | ||||
1802 | defined($self->{s}->{conf}->{srl}) && | ||||
1803 | $self->{s}->{greeting}->{srl} <= $self->{s}->{conf}->{srl}) { | ||||
1804 | |||||
1805 | $self->compute_server_conf; | ||||
1806 | return 1 ; | ||||
1807 | } | ||||
1808 | |||||
1809 | # srl > our cached srl, request update (a=g&pm=state) | ||||
1810 | # and rediscover | ||||
1811 | # | ||||
1812 | |||||
1813 | my @queries = ("a=g&pm=state\r\n"); | ||||
1814 | |||||
1815 | my $response = $self->_send(\@queries) or return $self->errprefix("parse_greeting"); | ||||
1816 | |||||
1817 | # should be just one response | ||||
1818 | # from_batched_query wants "-" in beginning, but not ".\r\n" at end | ||||
1819 | $response->[0] =~ s/\.\r\n$//sg; | ||||
1820 | my $h = from_batched_query($response->[0], {}); | ||||
1821 | |||||
1822 | foreach my $href (@$h) { | ||||
1823 | foreach (sort keys %$href) { | ||||
1824 | $self->{s}->{conf}->{$_} = $href->{$_}; | ||||
1825 | #$self->log(8,"updated: $_=$href->{$_}"); | ||||
1826 | } | ||||
1827 | } | ||||
1828 | $self->log(1,"Bad info while trying to get server state (a=g&pm=state)") | ||||
1829 | unless scalar(@$h); | ||||
1830 | |||||
1831 | $self->{s}->{conf}->{srl} = $self->{s}->{greeting}->{srl}; | ||||
1832 | push @{$self->{s}->{modified}}, $self->{s}->{ip}; | ||||
1833 | $self->{s}->{allconfs}->{$self->{s}->{ip}} = $self->{s}->{conf}; # in case new server | ||||
1834 | |||||
1835 | # now we're up to date | ||||
1836 | $self->log(5,"Updated to new server state srl ". $self->{s}->{conf}->{srl} | ||||
1837 | ." for server ". $self->{s}->{ip}); | ||||
1838 | |||||
1839 | $self->compute_server_conf(); | ||||
1840 | $self->writeservers; # writes to disk servers listed in $self->{s}->{modified} | ||||
1841 | |||||
1842 | $self->log(5,"srl was updated, forcing discovery ..."); | ||||
1843 | $self->{done_discovery} = 0; | ||||
1844 | $self->{force_discovery} = 1; | ||||
1845 | $self->discover(); | ||||
1846 | |||||
1847 | return 1; | ||||
1848 | } | ||||
1849 | |||||
1850 | |||||
1851 | # Returns engines supported | ||||
1852 | # | ||||
1853 | # can be called with no paramaters or | ||||
1854 | # with hash of server supported engines | ||||
1855 | sub compute_supported_engines { | ||||
1856 | my ($self, $orig) = @_; | ||||
1857 | |||||
1858 | my %all; | ||||
1859 | my $se = $self->supported_engines(); # local supported engines | ||||
1860 | foreach (@{$self->{conf}->{use_engines}}) { | ||||
1861 | if ($orig) { | ||||
1862 | $all{$_} = 1 if (exists $se->{$_}) && (exists $orig->{$_}); | ||||
1863 | } else { | ||||
1864 | $all{$_} = 1 if exists $se->{$_}; | ||||
1865 | } | ||||
1866 | } | ||||
1867 | if ($orig) { | ||||
1868 | $self->log(8, "Computed supported_engines: ". join(' ', sort(keys %all)) ); | ||||
1869 | } else { | ||||
1870 | $self->log(8, "Client supported_engines: ". join(' ', sort(keys %all)) ); | ||||
1871 | } | ||||
1872 | return \%all; | ||||
1873 | } | ||||
1874 | |||||
1875 | |||||
1876 | # called when we need to parse server conf | ||||
1877 | # - after initial parse_greeting | ||||
1878 | # - if state (srl) changes | ||||
1879 | # - when we switch to cached server conf info in nextserver | ||||
1880 | # | ||||
1881 | sub compute_server_conf { | ||||
1882 | my ($self, $cached) = @_; | ||||
1883 | |||||
1884 | # | ||||
1885 | # compute a confindence (cf) from razor-agent.conf's 'min_cf' | ||||
1886 | # and server's average confidence (ac) | ||||
1887 | # | ||||
1888 | # min_cf can be 'n', 'ac', 'ac + n', or 'ac - n' | ||||
1889 | # where 'n' can be 1..100 | ||||
1890 | # | ||||
1891 | my $cf = $self->{s}->{conf}->{ac}; # default is server's ac | ||||
1892 | my $min_cf = $self->{conf}->{min_cf}; | ||||
1893 | $min_cf =~ s/\s//g; | ||||
1894 | |||||
1895 | if ($min_cf =~ /^ac\+(\d+)$/) { | ||||
1896 | $cf = $self->{s}->{conf}->{ac} + $1; | ||||
1897 | |||||
1898 | } elsif ($min_cf =~ /^ac-(\d+)$/) { | ||||
1899 | $cf = $self->{s}->{conf}->{ac} - $1; | ||||
1900 | |||||
1901 | } elsif ($min_cf =~ /^ac$/) { | ||||
1902 | $cf = $self->{s}->{conf}->{ac}; | ||||
1903 | |||||
1904 | } elsif ($min_cf =~ /^(\d+)$/) { | ||||
1905 | $cf = $min_cf; | ||||
1906 | } else { | ||||
1907 | $self->log(5,"Invalid min_cf $self->{conf}->{min_cf}"); | ||||
1908 | } | ||||
1909 | $cf = 100 if $cf > 100; | ||||
1910 | $cf = 0 if $cf < 0; | ||||
1911 | $self->{s}->{min_cf} = $cf; | ||||
1912 | |||||
1913 | # | ||||
1914 | # ep4 - special for vr4 | ||||
1915 | # | ||||
1916 | $self->{s}->{conf}->{ep4} = $self->{s}->{greeting}->{ep4} | ||||
1917 | if $self->{s}->{greeting}->{ep4}; | ||||
1918 | |||||
1919 | my $info = $cached ? $self->{s}->{conf} : $self->{s}->{greeting}; | ||||
1920 | my $name = "Unknown-Type: "; | ||||
1921 | if ($info->{sn}) { | ||||
1922 | $name .= $info->{sn}; | ||||
1923 | $name = "Nomination" if $info->{sn} =~ /N/; | ||||
1924 | $name = "Catalogue" if $info->{sn} =~ /C/; | ||||
1925 | $name = "Catalogue" if $info->{sn} =~ /S/; | ||||
1926 | $name = "Discovery" if $info->{sn} =~ /D/; | ||||
1927 | } | ||||
1928 | $self->log (6, $self->{s}->{ip} ." is a $name Server srl ". | ||||
1929 | $self->{s}->{conf}->{srl} ."; computed min_cf=$cf, Server se: $self->{s}->{conf}->{se}"); | ||||
1930 | |||||
1931 | # | ||||
1932 | # Supported Engines - greeting contains hex of bits | ||||
1933 | # we turn into a hash so we can just quickly do | ||||
1934 | # do_eng3_stuff if $self->{s}->{engines}->{3}; | ||||
1935 | # | ||||
1936 | |||||
1937 | # if we're just computing hashes locally, ignore what engines server currently supports | ||||
1938 | # fixme - this prolly should be done somewhere else | ||||
1939 | if ($self->{opt}->{printhash}) { | ||||
1940 | $self->log (6, "Ignore what engines server supports for -H"); | ||||
1941 | $self->{s}->{engines} = $self->compute_supported_engines(); | ||||
1942 | } else { | ||||
1943 | my $se = hexbits2hash($self->{s}->{conf}->{se}); | ||||
1944 | $self->{s}->{engines} = $self->compute_supported_engines($se); | ||||
1945 | } | ||||
1946 | } | ||||
1947 | |||||
1948 | |||||
1949 | # sub log2file moved to Agent.pm | ||||
1950 | |||||
1951 | |||||
1952 | sub debug { | ||||
1953 | my ($self, $message) = @_; | ||||
1954 | $self->log(5,$message); | ||||
1955 | } | ||||
1956 | |||||
1957 | |||||
1958 | sub DESTROY { | ||||
1959 | my $self = shift; | ||||
1960 | #$self->debug ("Agent terminated"); | ||||
1961 | } | ||||
1962 | |||||
1963 | |||||
1964 | sub zonename { | ||||
1965 | my ($zone, $type) = @_; | ||||
1966 | my ($sub, $dom) = split /\./, $zone, 2; | ||||
1967 | return "$sub-$type.$dom"; | ||||
1968 | } | ||||
1969 | |||||
1970 | |||||
1971 | 1 | 21µs | 1; | ||
1972 | |||||
1973 | |||||
# spent 26µs within Razor2::Client::Core::CORE:match which was called:
# once (26µs+0s) by base::import at line 32 |