]>
Commit | Line | Data |
---|---|---|
13b5ce6e MT |
1 | #!/usr/bin/perl |
2 | ############################################################################### | |
3 | # # | |
4 | # IPFire.org - A linux based firewall # | |
5 | # Copyright (C) 2013 IPFire Team <info@ipfire.org> # | |
6 | # # | |
7 | # This program is free software: you can redistribute it and/or modify # | |
8 | # it under the terms of the GNU General Public License as published by # | |
9 | # the Free Software Foundation, either version 3 of the License, or # | |
10 | # (at your option) any later version. # | |
11 | # # | |
12 | # This program is distributed in the hope that it will be useful, # | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
15 | # GNU General Public License for more details. # | |
16 | # # | |
17 | # You should have received a copy of the GNU General Public License # | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
19 | # # | |
20 | ############################################################################### | |
21 | ||
22 | use strict; | |
23 | use Locale::Country; | |
24 | ||
25 | # enable only the following on debugging purpose | |
26 | use warnings; | |
27 | use CGI::Carp 'fatalsToBrowser'; | |
28 | ||
29 | require '/var/ipfire/general-functions.pl'; | |
30 | require "${General::swroot}/lang.pl"; | |
31 | require "${General::swroot}/header.pl"; | |
32 | ||
33 | #workaround to suppress a warning when a variable is used only once | |
34 | my @dummy = ( ${Header::colouryellow} ); | |
35 | undef (@dummy); | |
36 | ||
37 | my @bandwidth_limits = ( | |
38 | 1000 * 1024, # 1G | |
39 | 500 * 1024, | |
40 | 200 * 1024, | |
41 | 100 * 1024, # 100M | |
42 | 64 * 1024, | |
43 | 50 * 1024, | |
44 | 25 * 1024, | |
45 | 20 * 1024, | |
46 | 16 * 1024, | |
47 | 10 * 1024, | |
48 | 8 * 1024, | |
49 | 4 * 1024, | |
50 | 2 * 1024, | |
51 | 1024, # 1M | |
52 | 512, | |
53 | 256, | |
6eb9c49d | 54 | 160 |
13b5ce6e MT |
55 | ); |
56 | my @accounting_periods = ('daily', 'weekly', 'monthly'); | |
57 | ||
58 | my $TOR_CONTROL_PORT = 9051; | |
59 | ||
60 | our %netsettings = (); | |
61 | &General::readhash("${General::swroot}/ethernet/settings", \%netsettings); | |
62 | ||
63 | our %settings = (); | |
64 | ||
65 | $settings{'TOR_ENABLED'} = 'off'; | |
66 | $settings{'TOR_SOCKS_PORT'} = 9050; | |
67 | $settings{'TOR_EXIT_COUNTRY'} = ''; | |
68 | $settings{'TOR_USE_EXIT_NODES'} = ''; | |
69 | $settings{'TOR_ALLOWED_SUBNETS'} = "$netsettings{'GREEN_NETADDRESS'}\/$netsettings{'GREEN_NETMASK'}"; | |
70 | if (&Header::blue_used()) { | |
71 | $settings{'TOR_ALLOWED_SUBNETS'} .= ",$netsettings{'BLUE_NETADDRESS'}\/$netsettings{'BLUE_NETMASK'}"; | |
72 | } | |
73 | ||
74 | $settings{'TOR_RELAY_ENABLED'} = 'off'; | |
75 | $settings{'TOR_RELAY_MODE'} = 'exit'; | |
bd8b0330 | 76 | $settings{'TOR_RELAY_ADDRESS'} = ''; |
13b5ce6e | 77 | $settings{'TOR_RELAY_PORT'} = 9001; |
b0449403 MT |
78 | $settings{'TOR_RELAY_NICKNAME'} = ''; |
79 | $settings{'TOR_RELAY_CONTACT_INFO'} = ''; | |
13b5ce6e MT |
80 | $settings{'TOR_RELAY_NOADVERTISE'} = 'off'; |
81 | $settings{'TOR_RELAY_BANDWIDTH_RATE'} = 0; | |
82 | $settings{'TOR_RELAY_BANDWIDTH_BURST'} = 0; | |
83 | $settings{'TOR_RELAY_ACCOUNTING_LIMIT'} = 0; | |
84 | $settings{'TOR_RELAY_ACCOUNTING_PERIOD'} = 'daily'; | |
85 | ||
86 | $settings{'ACTION'} = ''; | |
87 | ||
88 | my $errormessage = ''; | |
89 | my $warnmessage = ''; | |
90 | ||
91 | &Header::showhttpheaders(); | |
92 | ||
13b5ce6e MT |
93 | # Get GUI values. |
94 | &Header::getcgihash(\%settings); | |
95 | ||
96 | # Create tor command connection. | |
97 | our $torctrl = &TorConnect(); | |
98 | ||
99 | # Toggle enable/disable field. | |
100 | if ($settings{'ACTION'} eq $Lang::tr{'save'}) { | |
b0449403 MT |
101 | if ($settings{'TOR_RELAY_NICKNAME'} !~ /^[a-zA-Z0-9]+$/) { |
102 | $errormessage = "$Lang::tr{'tor errmsg invalid relay name'}: $settings{'TOR_RELAY_NICKNAME'}"; | |
103 | } | |
104 | ||
818f47d0 MT |
105 | if (!&General::validport($settings{'TOR_SOCKS_PORT'})) { |
106 | $errormessage = "$Lang::tr{'tor errmsg invalid socks port'}: $settings{'TOR_SOCKS_PORT'}"; | |
107 | } | |
108 | ||
109 | if (!&General::validport($settings{'TOR_RELAY_PORT'})) { | |
110 | $errormessage = "$Lang::tr{'tor errmsg invalid relay port'}: $settings{'TOR_RELAY_PORT'}"; | |
111 | } | |
112 | ||
bd8b0330 MT |
113 | if ($settings{'TOR_RELAY_ADDRESS'} ne '') { |
114 | if ((!&General::validfqdn($settings{'TOR_RELAY_ADDRESS'})) && (!&General::validip($settings{'TOR_RELAY_ADDRESS'}))) { | |
115 | $errormessage = "$Lang::tr{'tor errmsg invalid relay address'}: $settings{'TOR_RELAY_ADDRESS'}"; | |
116 | } | |
117 | } | |
118 | ||
56bf9f21 MT |
119 | if ($settings{'TOR_RELAY_ACCOUNTING_LIMIT'} !~ /^\d+$/) { |
120 | $errormessage = "$Lang::tr{'tor errmsg invalid accounting limit'}: $settings{'TOR_RELAY_ACCOUNTING_LIMIT'}"; | |
121 | } | |
122 | ||
13b5ce6e MT |
123 | my @temp = split(/[\n,]/,$settings{'TOR_ALLOWED_SUBNETS'}); |
124 | $settings{'TOR_ALLOWED_SUBNETS'} = ""; | |
125 | foreach (@temp) { | |
126 | s/^\s+//g; s/\s+$//g; | |
127 | if ($_) { | |
128 | unless (&General::validipandmask($_)) { | |
129 | $errormessage = "$Lang::tr{'tor errmsg invalid ip or mask'}: $_"; | |
130 | } | |
131 | $settings{'TOR_ALLOWED_SUBNETS'} .= $_.","; | |
132 | } | |
133 | } | |
134 | ||
135 | @temp = split(/[\n,]/,$settings{'TOR_USE_EXIT_NODES'}); | |
136 | $settings{'TOR_USE_EXIT_NODES'} = ""; | |
137 | foreach (@temp) { | |
138 | s/^\s+//g; s/\s+$//g; | |
139 | if ($_) { | |
140 | $settings{'TOR_USE_EXIT_NODES'} .= $_.","; | |
141 | } | |
142 | } | |
143 | ||
3308f8d0 MT |
144 | # Burst bandwidth must be less or equal to bandwidth rate. |
145 | if ($settings{'TOR_RELAY_BANDWIDTH_RATE'} == 0) { | |
146 | $settings{'TOR_RELAY_BANDWIDTH_BURST'} = 0; | |
147 | ||
148 | } elsif ($settings{'TOR_RELAY_BANDWIDTH_BURST'} < $settings{'TOR_RELAY_BANDWIDTH_RATE'}) { | |
149 | $settings{'TOR_RELAY_BANDWIDTH_BURST'} = $settings{'TOR_RELAY_BANDWIDTH_RATE'}; | |
150 | } | |
151 | ||
13b5ce6e MT |
152 | if ($errormessage eq '') { |
153 | # Write configuration settings to file. | |
154 | &General::writehash("${General::swroot}/tor/settings", \%settings); | |
155 | ||
156 | # Update configuration files. | |
157 | &BuildConfiguration(); | |
158 | } | |
b0449403 MT |
159 | } else { |
160 | # Load settings from file. | |
161 | &General::readhash("${General::swroot}/tor/settings", \%settings); | |
13b5ce6e MT |
162 | } |
163 | ||
164 | &showMainBox(); | |
165 | ||
166 | # Close Tor control connection. | |
167 | &TorClose($torctrl); | |
168 | ||
169 | # Functions | |
170 | ||
171 | sub showMainBox() { | |
172 | my %checked = (); | |
173 | my %selected = (); | |
174 | ||
175 | $checked{'TOR_ENABLED'}{'on'} = ''; | |
176 | $checked{'TOR_ENABLED'}{'off'} = ''; | |
177 | $checked{'TOR_ENABLED'}{$settings{'TOR_ENABLED'}} = 'checked'; | |
178 | ||
179 | $checked{'TOR_RELAY_ENABLED'}{'on'} = ''; | |
180 | $checked{'TOR_RELAY_ENABLED'}{'off'} = ''; | |
181 | $checked{'TOR_RELAY_ENABLED'}{$settings{'TOR_RELAY_ENABLED'}} = 'checked'; | |
182 | ||
183 | &Header::openpage($Lang::tr{'tor configuration'}, 1, ''); | |
184 | &Header::openbigbox('100%', 'left', '', $errormessage); | |
185 | ||
186 | if ($errormessage) { | |
187 | &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); | |
188 | print "<font class='base'>$errormessage </font>\n"; | |
189 | &Header::closebox(); | |
190 | } | |
191 | ||
192 | print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>\n"; | |
193 | ||
194 | &Header::openbox('100%', 'left', $Lang::tr{'tor configuration'}); | |
195 | ||
196 | print <<END; | |
197 | <table width='100%'> | |
198 | <tr> | |
199 | <td colspan='4' class='base'><b>$Lang::tr{'tor common settings'}</b></td> | |
200 | </tr> | |
201 | <tr> | |
202 | <td width='25%' class='base'>$Lang::tr{'tor enabled'}:</td> | |
005db206 MT |
203 | <td width='30%'><input type='checkbox' name='TOR_ENABLED' $checked{'TOR_ENABLED'}{'on'} /></td> |
204 | <td width='25%' class='base'>$Lang::tr{'tor socks port'}:</td> | |
205 | <td width='20%'><input type='text' name='TOR_SOCKS_PORT' value='$settings{'TOR_SOCKS_PORT'}' size='5' /></td> | |
13b5ce6e MT |
206 | </tr> |
207 | <tr> | |
208 | <td width='25%' class='base'>$Lang::tr{'tor relay enabled'}:</td> | |
005db206 | 209 | <td width='30%'><input type='checkbox' name='TOR_RELAY_ENABLED' $checked{'TOR_RELAY_ENABLED'}{'on'} /></td> |
13b5ce6e | 210 | <td width='25%' class='base'></td> |
005db206 | 211 | <td width='20%'></td> |
13b5ce6e MT |
212 | </tr> |
213 | </table> | |
214 | END | |
215 | ||
13b5ce6e MT |
216 | if ($settings{'TOR_ENABLED'} eq 'on') { |
217 | my @temp = split(",", $settings{'TOR_ALLOWED_SUBNETS'}); | |
218 | $settings{'TOR_ALLOWED_SUBNETS'} = join("\n", @temp); | |
219 | ||
220 | @temp = split(",", $settings{'TOR_USE_EXIT_NODES'}); | |
221 | $settings{'TOR_USE_EXIT_NODES'} = join("\n", @temp); | |
222 | ||
13b5ce6e | 223 | print <<END; |
005db206 | 224 | <br> |
13b5ce6e | 225 | <hr size='1'> |
005db206 | 226 | <br> |
13b5ce6e MT |
227 | |
228 | <table width='100%'> | |
229 | <tr> | |
230 | <td colspan='4' class='base'><b>$Lang::tr{'tor acls'}</b></td> | |
231 | </tr> | |
232 | <tr> | |
233 | <td colspan='2' class='base' width='55%'> | |
234 | $Lang::tr{'tor allowed subnets'}: | |
235 | </td> | |
236 | <td colspan='2' width='45%'></td> | |
237 | </tr> | |
238 | <tr> | |
239 | <td colspan='2' class='base' width='55%'> | |
240 | <textarea name='TOR_ALLOWED_SUBNETS' cols='32' rows='3' wrap='off'>$settings{'TOR_ALLOWED_SUBNETS'}</textarea> | |
241 | </td> | |
242 | <td colspan='2' width='45%'></td> | |
243 | </tr> | |
244 | </table> | |
245 | ||
005db206 | 246 | <br> |
13b5ce6e | 247 | <hr size='1'> |
005db206 | 248 | <br> |
13b5ce6e MT |
249 | |
250 | <table width='100%'> | |
251 | <tr> | |
252 | <td colspan='4' class='base'><b>$Lang::tr{'tor exit nodes'}</b></td> | |
253 | </tr> | |
254 | <tr> | |
255 | <td colspan='2' class='base' width='55%'></td> | |
256 | <td colspan='2' class='base' width='45%'>$Lang::tr{'tor use exit nodes'}:</td> | |
257 | </tr> | |
258 | <tr> | |
259 | <td width='50%' colspan='2'> | |
260 | <select name='TOR_EXIT_COUNTRY'> | |
261 | <option value=''>- $Lang::tr{'tor exit country any'} -</option> | |
262 | END | |
263 | ||
264 | my @country_names = Locale::Country::all_country_names(); | |
265 | foreach my $country_name (sort @country_names) { | |
266 | my $country_code = Locale::Country::country2code($country_name); | |
267 | $country_code = uc($country_code); | |
268 | print "<option value='$country_code'>$country_name ($country_code)</option>\n"; | |
269 | } | |
270 | ||
271 | print <<END; | |
272 | </select> | |
273 | </td> | |
005db206 | 274 | <td width='50%' colspan='2'> |
13b5ce6e MT |
275 | <textarea name='TOR_USE_EXIT_NODES' cols='32' rows='3' wrap='off'>$settings{'TOR_USE_EXIT_NODES'}</textarea> |
276 | </td> | |
277 | </tr> | |
278 | </table> | |
005db206 | 279 | <br><br> |
13b5ce6e | 280 | END |
13b5ce6e MT |
281 | } |
282 | ||
005db206 MT |
283 | &Header::closebox(); |
284 | ||
13b5ce6e MT |
285 | if ($settings{'TOR_RELAY_ENABLED'} eq 'on') { |
286 | $checked{'TOR_RELAY_NOADVERTISE'}{'on'} = ''; | |
287 | $checked{'TOR_RELAY_NOADVERTISE'}{'off'} = ''; | |
288 | $checked{'TOR_RELAY_NOADVERTISE'}{$settings{'TOR_RELAY_NOADVERTISE'}} = 'checked'; | |
289 | ||
290 | $selected{'TOR_RELAY_MODE'}{'bridge'} = ''; | |
291 | $selected{'TOR_RELAY_MODE'}{'exit'} = ''; | |
292 | $selected{'TOR_RELAY_MODE'}{'private-bridge'} = ''; | |
293 | $selected{'TOR_RELAY_MODE'}{'relay'} = ''; | |
294 | $selected{'TOR_RELAY_MODE'}{$settings{'TOR_RELAY_MODE'}} = 'selected'; | |
295 | ||
296 | $selected{'TOR_RELAY_BANDWIDTH_RATE'}{'0'} = ''; | |
297 | foreach (@bandwidth_limits) { | |
298 | $selected{'TOR_RELAY_BANDWIDTH_RATE'}{$_} = ''; | |
299 | } | |
300 | $selected{'TOR_RELAY_BANDWIDTH_RATE'}{$settings{'TOR_RELAY_BANDWIDTH_RATE'}} = 'selected'; | |
301 | ||
302 | $selected{'TOR_RELAY_BANDWIDTH_BURST'}{'0'} = ''; | |
303 | foreach (@bandwidth_limits) { | |
304 | $selected{'TOR_RELAY_BANDWIDTH_BURST'}{$_} = ''; | |
305 | } | |
306 | $selected{'TOR_RELAY_BANDWIDTH_BURST'}{$settings{'TOR_RELAY_BANDWIDTH_BURST'}} = 'selected'; | |
307 | ||
308 | foreach (@accounting_periods) { | |
309 | $selected{'TOR_RELAY_ACCOUNTING_PERIOD'}{$_} = ''; | |
310 | } | |
311 | $selected{'TOR_RELAY_ACCOUNTING_PERIOD'}{$settings{'TOR_RELAY_ACCOUNTING_PERIOD'}} = 'selected'; | |
312 | ||
313 | &Header::openbox('100%', 'left', $Lang::tr{'tor relay configuration'}); | |
314 | ||
315 | print <<END; | |
316 | <table width='100%'> | |
317 | <tr> | |
318 | <td width='25%' class='base'>$Lang::tr{'tor relay mode'}:</td> | |
319 | <td width='30%'> | |
320 | <select name='TOR_RELAY_MODE'> | |
321 | <option value='exit' $selected{'TOR_RELAY_MODE'}{'exit'}>$Lang::tr{'tor relay mode exit'}</option> | |
322 | <option value='relay' $selected{'TOR_RELAY_MODE'}{'relay'}>$Lang::tr{'tor relay mode relay'}</option> | |
323 | <option value='bridge' $selected{'TOR_RELAY_MODE'}{'bridge'}>$Lang::tr{'tor relay mode bridge'}</option> | |
324 | <option value='private-bridge' $selected{'TOR_RELAY_MODE'}{'private-bridge'}>$Lang::tr{'tor relay mode private bridge'}</option> | |
325 | </select> | |
326 | </td> | |
327 | <td width='25%' class='base'>$Lang::tr{'tor relay port'}:</td> | |
328 | <td width='20%'> | |
329 | <input type='text' name='TOR_RELAY_PORT' value='$settings{'TOR_RELAY_PORT'}' size='5' /> | |
330 | </td> | |
331 | </tr> | |
332 | <tr> | |
333 | <td width='25%' class='base'>$Lang::tr{'tor relay address'}: <img src='/blob.gif' alt='*' /></td> | |
334 | <td width='30%'> | |
335 | <input type='text' name='TOR_RELAY_ADDRESS' value='$settings{'TOR_RELAY_ADDRESS'}' /> | |
336 | </td> | |
337 | <td width='25%' class='base'>$Lang::tr{'tor do not advertise relay'}:</td> | |
338 | <td width='20%'> | |
339 | <input type='checkbox' name='TOR_RELAY_NOADVERTISE' $checked{'TOR_RELAY_NOADVERTISE'}{'on'} /> | |
340 | </td> | |
341 | </tr> | |
342 | <tr> | |
343 | <td width='25%' class='base'>$Lang::tr{'tor relay nickname'}: <img src='/blob.gif' alt='*' /></td> | |
344 | <td width='30%'> | |
345 | <input type='text' name='TOR_RELAY_NICKNAME' value='$settings{'TOR_RELAY_NICKNAME'}' /> | |
346 | </td> | |
347 | <td colspan='2'></td> | |
348 | </tr> | |
349 | <tr> | |
350 | <td width='25%' class='base'>$Lang::tr{'tor contact info'}: <img src='/blob.gif' alt='*' /></td> | |
351 | <td width='75%' colspan='3'> | |
352 | <input type='text' name='TOR_RELAY_CONTACT_INFO' value='$settings{'TOR_RELAY_CONTACT_INFO'}' size='60' /> | |
353 | </td> | |
354 | </tr> | |
355 | </table> | |
356 | ||
357 | <hr size='1'> | |
358 | ||
359 | <table width='100%'> | |
360 | <tr> | |
361 | <td colspan='4' class='base'><b>$Lang::tr{'tor bandwidth settings'}</b></td> | |
362 | </tr> | |
363 | <tr> | |
364 | <td width='25%' class='base'>$Lang::tr{'tor bandwidth rate'}:</td> | |
365 | <td width='30%' class='base'> | |
366 | <select name='TOR_RELAY_BANDWIDTH_RATE'> | |
367 | END | |
368 | ||
369 | foreach (@bandwidth_limits) { | |
370 | if ($_ >= 1024) { | |
371 | print "<option value='$_' $selected{'TOR_RELAY_BANDWIDTH_RATE'}{$_}>". $_ / 1024 ." MBit/s</option>\n"; | |
372 | } else { | |
373 | print "<option value='$_' $selected{'TOR_RELAY_BANDWIDTH_RATE'}{$_}>$_ kBit/s</option>\n"; | |
374 | } | |
375 | } | |
376 | ||
377 | print <<END; | |
378 | <option value='0' $selected{'TOR_RELAY_BANDWIDTH_RATE'}{'0'}>$Lang::tr{'tor bandwidth unlimited'}</option> | |
379 | </select> | |
380 | </td> | |
381 | <td width='25%' class='base'>$Lang::tr{'tor accounting limit'}:</td> | |
382 | <td width='20%'> | |
383 | <input type='text' name='TOR_RELAY_ACCOUNTING_LIMIT' value='$settings{'TOR_RELAY_ACCOUNTING_LIMIT'}' size='12' /> | |
384 | </td> | |
385 | </tr> | |
386 | <tr> | |
387 | <td width='25%' class='base'>$Lang::tr{'tor bandwidth burst'}:</td> | |
388 | <td width='20%' class='base'> | |
389 | <select name='TOR_RELAY_BANDWIDTH_BURST'> | |
390 | END | |
391 | ||
392 | foreach (@bandwidth_limits) { | |
393 | if ($_ >= 1024) { | |
394 | print "<option value='$_' $selected{'TOR_RELAY_BANDWIDTH_BURST'}{$_}>". $_ / 1024 ." MBit/s</option>\n"; | |
395 | } else { | |
396 | print "<option value='$_' $selected{'TOR_RELAY_BANDWIDTH_BURST'}{$_}>$_ kBit/s</option>\n"; | |
397 | } | |
398 | } | |
399 | print <<END; | |
400 | <option value='0' $selected{'TOR_RELAY_BANDWIDTH_BURST'}{'0'}>$Lang::tr{'tor bandwidth unlimited'}</option> | |
401 | </select> | |
402 | </td> | |
403 | <td width='25%' class='base'>$Lang::tr{'tor accounting period'}:</td> | |
404 | <td width='20%'> | |
405 | <select name='TOR_RELAY_ACCOUNTING_PERIOD'> | |
406 | END | |
407 | ||
408 | foreach (@accounting_periods) { | |
409 | print "<option value='$_' $selected{'TOR_RELAY_ACCOUNTING_PERIOD'}{$_}>$Lang::tr{'tor accounting period '.$_}</option>"; | |
410 | } | |
411 | ||
412 | print <<END; | |
413 | </select> | |
414 | </td> | |
415 | </tr> | |
416 | </table> | |
417 | END | |
418 | ||
419 | &Header::closebox(); | |
420 | } | |
421 | ||
422 | print <<END; | |
423 | <table width='100%'> | |
424 | <tr> | |
425 | <td> | |
426 | <img src='/blob.gif' align='top' alt='*' /> <font class='base'>$Lang::tr{'this field may be blank'}</font> | |
427 | </td> | |
428 | <td align='right'> </td> | |
429 | </tr> | |
430 | </table> | |
431 | ||
432 | <hr> | |
433 | ||
434 | <table width='100%'> | |
435 | <tr> | |
436 | <td> </td> | |
437 | <td align='center'><input type='submit' name='ACTION' value='$Lang::tr{'save'}' /></td> | |
438 | <td> </td> | |
439 | </tr> | |
440 | </table> | |
441 | END | |
442 | ||
443 | # If we have a control connection, show the stats. | |
444 | if ($torctrl) { | |
445 | &Header::openbox('100%', 'left', $Lang::tr{'tor stats'}); | |
446 | ||
447 | my @traffic = &TorTrafficStats($torctrl); | |
448 | ||
449 | if (@traffic) { | |
450 | print <<END; | |
451 | <table width='100%'> | |
452 | END | |
453 | ||
454 | if ($settings{'TOR_RELAY_ENABLED'} eq 'on') { | |
455 | my $fingerprint = &TorRelayFingerprint($torctrl); | |
456 | if ($fingerprint) { | |
457 | print <<END; | |
458 | <tr> | |
459 | <td width='40%' class='base'>$Lang::tr{'tor relay fingerprint'}:</td> | |
460 | <td width='60%'> | |
461 | <a href='https://atlas.torproject.org/#details/$fingerprint' target='_blank'>$fingerprint</a> | |
462 | </td> | |
463 | </tr> | |
464 | END | |
465 | } | |
466 | } | |
467 | ||
468 | my $address = TorGetInfo($torctrl, "address"); | |
469 | if ($address) { | |
470 | print <<END; | |
471 | <tr> | |
472 | <td width='40%' class='base'>$Lang::tr{'tor relay external address'}:</td> | |
473 | <td width='60%'>$address</td> | |
474 | </tr> | |
475 | END | |
476 | } | |
477 | ||
478 | print <<END; | |
479 | <tr> | |
480 | <td width='40%'>$Lang::tr{'tor traffic read written'}:</td> | |
481 | END | |
482 | print "<td width='60%'>" . &FormatBytes($traffic[0]) ."/". &FormatBytes($traffic[1]) . "</td>"; | |
483 | print <<END; | |
484 | </tr> | |
485 | </table> | |
486 | END | |
487 | } | |
488 | ||
489 | my $accounting = &TorAccountingStats($torctrl); | |
490 | if ($accounting) { | |
491 | print <<END; | |
492 | <table width='100%'> | |
493 | <tr> | |
494 | <td colspan='2' class='base'><b>$Lang::tr{'tor accounting'}</b></td> | |
495 | </tr> | |
496 | END | |
497 | ||
498 | if ($accounting->{'hibernating'} eq "hard") { | |
499 | print <<END; | |
500 | <tr> | |
501 | <td class='base' colspan='2' bgcolor="$Header::colourred" align='center'> | |
502 | <font color='white'>$Lang::tr{'tor traffic limit hard'}</font> | |
503 | </td> | |
504 | </tr> | |
505 | END | |
506 | } elsif ($accounting->{'hibernating'} eq "soft") { | |
507 | print <<END; | |
508 | <tr> | |
509 | <td class='base' colspan='2' bgcolor="$Header::colourorange" align='center'> | |
510 | <font color='white'>$Lang::tr{'tor traffic limit soft'}</font> | |
511 | </td> | |
512 | </tr> | |
513 | END | |
514 | } | |
515 | ||
516 | print <<END; | |
517 | <tr> | |
518 | <td width='40%' class='base'>$Lang::tr{'tor accounting interval'}</td> | |
519 | <td width='60%'> | |
520 | $accounting->{'interval-start'} - $accounting->{'interval-end'} | |
521 | </td> | |
522 | </tr> | |
523 | <tr> | |
524 | <td width='40%' class='base'>$Lang::tr{'tor accounting bytes'}</td> | |
525 | <td width='60%'> | |
526 | END | |
527 | ||
528 | print &FormatBytes($accounting->{'bytes_read'}) . "/" . &FormatBytes($accounting->{'bytes_written'}); | |
529 | print " (" . &FormatBytes($accounting->{'bytes-left_read'}) . "/" . &FormatBytes($accounting->{'bytes-left_written'}); | |
530 | print " $Lang::tr{'tor accounting bytes left'})"; | |
531 | ||
532 | print <<END; | |
533 | </td> | |
534 | </tr> | |
535 | </table> | |
536 | END | |
537 | } | |
538 | ||
539 | my @nodes = &TorORConnStatus($torctrl); | |
540 | if (@nodes) { | |
f16bcc3e | 541 | my $nodes_length = scalar @nodes; |
13b5ce6e MT |
542 | print <<END; |
543 | <table width='100%'> | |
544 | <tr> | |
f16bcc3e MT |
545 | <td width='40%' class='base'><b>$Lang::tr{'tor connected relays'}</b></td> |
546 | <td width='60%' colspan='2'>($nodes_length)</td> | |
13b5ce6e MT |
547 | </tr> |
548 | END | |
549 | ||
550 | foreach my $node (@nodes) { | |
551 | print <<END; | |
552 | <tr> | |
553 | <td width='40%'> | |
554 | <a href='https://atlas.torproject.org/#details/$node->{'fingerprint'}' target='_blank'> | |
555 | $node->{'name'} | |
556 | </a> | |
557 | </td> | |
558 | <td width='30%'> | |
559 | END | |
560 | ||
561 | if (exists($node->{'country_code'})) { | |
562 | print "<a href='country.cgi#$node->{'country_code'}'><img src='/images/flags/$node->{'country_code'}.png' border='0' align='absmiddle' alt='$node->{'country_code'}'></a>"; | |
563 | } | |
564 | ||
565 | print <<END; | |
566 | <a href='ipinfo.cgi?ip=$node->{'address'}'>$node->{'address'}</a>:$node->{'port'} | |
567 | </td> | |
568 | <td width='30%' align='right'> | |
569 | ~$node->{'bandwidth_string'} | |
570 | </td> | |
571 | </tr> | |
572 | END | |
573 | } | |
574 | print "</table>"; | |
575 | } | |
576 | ||
577 | &Header::closebox(); | |
578 | } | |
579 | ||
580 | print "</form>\n"; | |
581 | ||
582 | &Header::closebigbox(); | |
583 | &Header::closepage(); | |
584 | } | |
585 | ||
586 | sub BuildConfiguration() { | |
587 | my %settings = (); | |
588 | &General::readhash("${General::swroot}/tor/settings", \%settings); | |
589 | ||
590 | my $torrc = "${General::swroot}/tor/torrc"; | |
591 | ||
592 | open(FILE, ">$torrc"); | |
593 | ||
594 | # Global settings. | |
595 | print FILE "ControlPort $TOR_CONTROL_PORT\n"; | |
596 | ||
597 | if ($settings{'TOR_ENABLED'} eq 'on') { | |
598 | my $strict_nodes = 0; | |
599 | ||
600 | print FILE "SocksPort 0.0.0.0:$settings{'TOR_SOCKS_PORT'}\n"; | |
601 | ||
602 | my @subnets = split(",", $settings{'TOR_ALLOWED_SUBNETS'}); | |
603 | foreach (@subnets) { | |
604 | print FILE "SocksPolicy accept $_\n" if (&General::validipandmask($_)); | |
605 | } | |
606 | print FILE "SocksPolicy reject *\n" if (@subnets); | |
607 | ||
608 | if ($settings{'TOR_EXIT_COUNTRY'} ne '') { | |
609 | $strict_nodes = 1; | |
610 | ||
611 | print FILE "ExitNodes {$settings{'TOR_EXIT_COUNTRY'}}\n"; | |
612 | } | |
613 | ||
614 | if ($settings{'TOR_USE_EXIT_NODES'} ne '') { | |
615 | $strict_nodes = 1; | |
616 | ||
617 | my @nodes = split(",", $settings{'TOR_USE_EXIT_NODES'}); | |
618 | foreach (@nodes) { | |
619 | print FILE "ExitNode $_\n"; | |
620 | } | |
621 | } | |
622 | ||
623 | if ($strict_nodes > 0) { | |
624 | print FILE "StrictNodes 1\n"; | |
625 | } | |
626 | } | |
627 | ||
628 | if ($settings{'TOR_RELAY_ENABLED'} eq 'on') { | |
629 | # Reject access to private networks. | |
630 | print FILE "ExitPolicyRejectPrivate 1\n"; | |
631 | ||
632 | print FILE "ORPort $settings{'TOR_RELAY_PORT'}"; | |
633 | if ($settings{'TOR_RELAY_NOADVERTISE'} eq 'on') { | |
634 | print FILE " NoAdvertise"; | |
635 | } | |
636 | print FILE "\n"; | |
637 | ||
638 | if ($settings{'TOR_RELAY_ADDRESS'} ne '') { | |
639 | print FILE "Address $settings{'TOR_RELAY_ADDRESS'}\n"; | |
640 | } | |
641 | ||
642 | if ($settings{'TOR_RELAY_NICKNAME'} ne '') { | |
643 | print FILE "Nickname $settings{'TOR_RELAY_NICKNAME'}\n"; | |
644 | } | |
645 | ||
646 | if ($settings{'TOR_RELAY_CONTACT_INFO'} ne '') { | |
647 | print FILE "ContactInfo $settings{'TOR_RELAY_CONTACT_INFO'}\n"; | |
648 | } | |
649 | ||
650 | # Limit to bridge mode. | |
651 | my $is_bridge = 0; | |
652 | ||
653 | if ($settings{'TOR_RELAY_MODE'} eq 'bridge') { | |
654 | $is_bridge++; | |
655 | ||
656 | # Private bridge. | |
657 | } elsif ($settings{'TOR_RELAY_MODE'} eq 'private-bridge') { | |
658 | $is_bridge++; | |
659 | ||
660 | print FILE "PublishServerDescriptor 0\n"; | |
661 | ||
662 | # Exit node. | |
663 | } elsif ($settings{'TOR_RELAY_MODE'} eq 'exit') { | |
664 | print FILE "ExitPolicy accept *:*\n"; | |
665 | ||
666 | # Relay only. | |
667 | } elsif ($settings{'TOR_RELAY_MODE'} eq 'relay') { | |
668 | print FILE "ExitPolicy reject *:*\n"; | |
669 | } | |
670 | ||
671 | if ($is_bridge > 0) { | |
672 | print FILE "BridgeRelay 1\n"; | |
673 | print FILE "Exitpolicy reject *:*\n"; | |
674 | } | |
675 | ||
676 | if ($settings{'TOR_RELAY_BANDWIDTH_RATE'} > 0) { | |
677 | print FILE "RelayBandwidthRate "; | |
678 | print FILE $settings{'TOR_RELAY_BANDWIDTH_RATE'} / 8; | |
679 | print FILE " KB\n"; | |
680 | ||
681 | if ($settings{'TOR_RELAY_BANDWIDTH_BURST'} > 0) { | |
682 | print FILE "RelayBandwidthBurst "; | |
683 | print FILE $settings{'TOR_RELAY_BANDWIDTH_BURST'} / 8; | |
684 | print FILE " KB\n"; | |
685 | } | |
686 | } | |
687 | ||
688 | if ($settings{'TOR_RELAY_ACCOUNTING_LIMIT'} > 0) { | |
689 | print FILE "AccountingMax ".$settings{'TOR_RELAY_ACCOUNTING_LIMIT'}." MB\n"; | |
690 | ||
691 | if ($settings{'TOR_RELAY_ACCOUNTING_PERIOD'} eq 'daily') { | |
692 | print FILE "AccountingStart day 00:00\n"; | |
693 | } elsif ($settings{'TOR_RELAY_ACCOUNTING_PERIOD'} eq 'weekly') { | |
694 | print FILE "AccountingStart week 1 00:00\n"; | |
695 | } elsif ($settings{'TOR_RELAY_ACCOUNTING_PERIOD'} eq 'monthly') { | |
696 | print FILE "AccountingStart month 1 00:00\n"; | |
697 | } | |
698 | } | |
699 | } | |
700 | ||
701 | close(FILE); | |
702 | ||
703 | # Restart the service. | |
704 | if (($settings{'TOR_ENABLED'} eq 'on') || ($settings{'TOR_RELAY_ENABLED'} eq 'on')) { | |
005db206 | 705 | system("/usr/local/bin/torctrl restart &>/dev/null"); |
13b5ce6e | 706 | } else { |
005db206 | 707 | system("/usr/local/bin/torctrl stop &>/dev/null"); |
13b5ce6e MT |
708 | } |
709 | } | |
710 | ||
711 | sub TorConnect() { | |
712 | my $socket = new IO::Socket::INET( | |
713 | Proto => 'tcp', PeerAddr => '127.0.0.1', PeerPort => $TOR_CONTROL_PORT, | |
714 | ) or return; | |
715 | ||
716 | $socket->autoflush(1); | |
717 | ||
718 | # Authenticate. | |
719 | &TorSendCommand($socket, "AUTHENTICATE"); | |
720 | ||
721 | return $socket; | |
722 | } | |
723 | ||
724 | sub TorSendCommand() { | |
725 | my ($socket, $cmd) = @_; | |
726 | ||
727 | # Replace line ending with \r\n. | |
728 | chomp $cmd; | |
729 | $cmd .= "\r\n"; | |
730 | ||
731 | $socket->send($cmd); | |
732 | ||
733 | my @output = (); | |
734 | while (my $line = <$socket>) { | |
735 | # Skip empty lines. | |
736 | if ($line =~ /^.\r\n$/) { | |
737 | next; | |
738 | } | |
739 | ||
740 | # Command has been successfully executed. | |
741 | if ($line =~ /250 OK/) { | |
742 | last; | |
743 | ||
744 | # Error. | |
745 | } elsif ($line =~ /^5\d+/) { | |
746 | last; | |
747 | ||
748 | } else { | |
749 | # Remove line endings. | |
750 | $line =~ s/\r\n$//; | |
751 | ||
752 | push(@output, $line); | |
753 | } | |
754 | } | |
755 | ||
756 | return @output; | |
757 | } | |
758 | ||
759 | sub TorSendCommandOneLine() { | |
760 | my ($tor, $cmd) = @_; | |
761 | ||
762 | my @output = &TorSendCommand($tor, $cmd); | |
763 | return $output[0]; | |
764 | } | |
765 | ||
766 | sub TorGetInfo() { | |
767 | my ($tor, $cmd) = @_; | |
768 | ||
769 | my $output = &TorSendCommandOneLine($tor, "GETINFO ".$cmd); | |
770 | ||
771 | my ($key, $value) = split("=", $output); | |
772 | return $value; | |
773 | } | |
774 | ||
775 | sub TorClose() { | |
776 | my $socket = shift; | |
777 | ||
778 | if ($socket) { | |
779 | $socket->shutdown(2); | |
780 | } | |
781 | } | |
782 | ||
783 | sub TorTrafficStats() { | |
784 | my $tor = shift; | |
785 | ||
786 | my $output_read = &TorGetInfo($tor, "traffic/read"); | |
787 | my $output_written = &TorGetInfo($tor, "traffic/written"); | |
788 | ||
789 | return ($output_read, $output_written); | |
790 | } | |
791 | ||
792 | sub TorRelayFingerprint() { | |
793 | my $tor = shift; | |
794 | ||
795 | return &TorGetInfo($tor, "fingerprint"); | |
796 | } | |
797 | ||
798 | sub TorORConnStatus() { | |
799 | my $tor = shift; | |
800 | my @nodes = (); | |
801 | ||
802 | my @output = &TorSendCommand($tor, "GETINFO orconn-status"); | |
803 | foreach (@output) { | |
804 | $_ =~ s/^250[\+-]orconn-status=//; | |
805 | next if ($_ eq ""); | |
806 | last if ($_ eq "."); | |
807 | next unless ($_ =~ /^\$/); | |
808 | ||
809 | my @line = split(" ", $_); | |
810 | my @node = split(/[=~]/, $line[0]); | |
811 | ||
812 | my $node = &TorNodeDescription($tor, $node[0]); | |
813 | if ($node) { | |
814 | push(@nodes, $node); | |
815 | } | |
816 | } | |
817 | ||
818 | # Sort by names. | |
819 | @nodes = sort { $a->{'name'} cmp $b->{'name'} } @nodes; | |
820 | ||
821 | return @nodes; | |
822 | } | |
823 | ||
824 | sub TorNodeDescription() { | |
825 | my ($tor, $fingerprint) = @_; | |
826 | $fingerprint =~ s/\$//; | |
827 | ||
828 | my $node = { | |
829 | fingerprint => $fingerprint, | |
830 | exit_node => 0, | |
831 | }; | |
832 | ||
833 | my @output = &TorSendCommand($tor, "GETINFO ns/id/$node->{'fingerprint'}"); | |
834 | ||
835 | foreach (@output) { | |
836 | # Router | |
837 | if ($_ =~ /^r (\w+) (.*) (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) (\d+)/) { | |
838 | $node->{'name'} = $1; | |
839 | $node->{'address'} = $3; | |
840 | $node->{'port'} = $4; | |
841 | ||
842 | my $country_code = &TorGetInfo($tor, "ip-to-country/$node->{'address'}"); | |
843 | $node->{'country_code'} = $country_code; | |
844 | ||
845 | # Flags | |
846 | } elsif ($_ =~ /^s (.*)$/) { | |
847 | $node->{'flags'} = split(" ", $1); | |
848 | ||
849 | foreach my $flag ($node->{'flags'}) { | |
850 | if ($flag eq "Exit") { | |
851 | $node->{'exit_node'}++; | |
852 | } | |
853 | } | |
854 | ||
855 | # Bandwidth | |
856 | } elsif ($_ =~ /^w Bandwidth=(\d+)/) { | |
857 | $node->{'bandwidth'} = $1 * 8; | |
858 | $node->{'bandwidth_string'} = &FormatBitsPerSecond($node->{'bandwidth'}); | |
859 | } | |
860 | } | |
861 | ||
862 | if (exists($node->{'name'})) { | |
863 | return $node; | |
864 | } | |
865 | } | |
866 | ||
867 | sub TorAccountingStats() { | |
868 | my $tor = shift; | |
869 | my $ret = {}; | |
870 | ||
871 | my $enabled = &TorGetInfo($tor, "accounting/enabled"); | |
872 | if ($enabled ne '1') { | |
873 | return; | |
874 | } | |
875 | ||
876 | my @cmds = ("hibernating", "interval-start", "interval-end"); | |
877 | foreach (@cmds) { | |
878 | $ret->{$_} = &TorGetInfo($tor, "accounting/$_"); | |
879 | } | |
880 | ||
881 | my @cmds = ("bytes", "bytes-left"); | |
882 | foreach (@cmds) { | |
883 | my $output = &TorGetInfo($tor, "accounting/$_"); | |
884 | my @bytes = split(" ", $output); | |
885 | ||
886 | $ret->{$_."_read"} = $bytes[0]; | |
887 | $ret->{$_."_written"} = $bytes[1]; | |
888 | } | |
889 | ||
890 | return $ret; | |
891 | } | |
892 | ||
893 | sub FormatBytes() { | |
894 | my $bytes = shift; | |
895 | ||
896 | my @units = ("B", "KB", "MB", "GB", "TB"); | |
897 | my $units_index = 0; | |
898 | ||
899 | while (($units_index <= $#units) && ($bytes >= 1024)) { | |
900 | $units_index++; | |
901 | $bytes /= 1024; | |
902 | } | |
903 | ||
904 | return sprintf("%.2f %s", $bytes, $units[$units_index]); | |
905 | } | |
906 | ||
907 | sub FormatBitsPerSecond() { | |
908 | my $bits = shift; | |
909 | ||
910 | my @units = ("Bit/s", "KBit/s", "MBit/s", "GBit/s", "TBit/s"); | |
911 | my $units_index = 0; | |
912 | ||
913 | while (($units_index <= $#units) && ($bits >= 1024)) { | |
914 | $units_index++; | |
915 | $bits /= 1024; | |
916 | } | |
917 | ||
918 | return sprintf("%.2f %s", $bits, $units[$units_index]); | |
919 | } |