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