]> git.ipfire.org Git - ipfire-2.x.git/blob - config/cfgroot/general-functions.pl
Merge branch 'next' of ssh://git.ipfire.org/pub/git/ipfire-2.x into next
[ipfire-2.x.git] / config / cfgroot / general-functions.pl
1 # SmoothWall CGIs
2 #
3 # This code is distributed under the terms of the GPL
4 #
5 # (c) The SmoothWall Team
6 # Copyright (C) 2002 Alex Hudson - getcgihash() rewrite
7 # Copyright (C) 2002 Bob Grant <bob@cache.ucr.edu> - validmac()
8 # Copyright (c) 2002/04/13 Steve Bootes - add alias section, helper functions
9 # Copyright (c) 2002/08/23 Mark Wormgoor <mark@wormgoor.com> validfqdn()
10 # Copyright (c) 2003/09/11 Darren Critchley <darrenc@telus.net> srtarray()
11 #
12 # $Id: general-functions.pl,v 1.1.2.26 2006/01/04 16:33:55 franck78 Exp $
13 #
14
15 package General;
16
17 use strict;
18 use Socket;
19 use IO::Socket;
20 use Locale::Codes::Country;
21 use Net::SSLeay;
22 use Net::IPv4Addr qw(:all);
23 $|=1; # line buffering
24
25 $General::version = 'VERSION';
26 $General::swroot = 'CONFIG_ROOT';
27 $General::noipprefix = 'noipg-';
28 $General::adminmanualurl = 'http://wiki.ipfire.org';
29
30 require "${General::swroot}/network-functions.pl";
31
32 #
33 # log ("message") use default 'ipcop' tag
34 # log ("tag","message") use your tag
35 #
36 sub log
37 {
38 my $tag='ipfire';
39 $tag = shift if (@_>1);
40 my $logmessage = $_[0];
41 $logmessage =~ /([\w\W]*)/;
42 $logmessage = $1;
43 system('logger', '-t', $tag, $logmessage);
44 }
45 sub setup_default_networks
46 {
47 my %netsettings=();
48 my $defaultNetworks = shift;
49
50 &readhash("/var/ipfire/ethernet/settings", \%netsettings);
51
52 # Get current defined networks (Red, Green, Blue, Orange)
53 $defaultNetworks->{$Lang::tr{'fwhost any'}}{'IPT'} = "0.0.0.0/0.0.0.0";
54 $defaultNetworks->{$Lang::tr{'fwhost any'}}{'NAME'} = "ALL";
55
56 $defaultNetworks->{$Lang::tr{'green'}}{'IPT'} = "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}";
57 $defaultNetworks->{$Lang::tr{'green'}}{'NET'} = "$netsettings{'GREEN_ADDRESS'}";
58 $defaultNetworks->{$Lang::tr{'green'}}{'NAME'} = "GREEN";
59
60 if ($netsettings{'RED_DEV'} ne ''){
61 $defaultNetworks->{$Lang::tr{'fwdfw red'}}{'IPT'} = "$netsettings{'RED_NETADDRESS'}/$netsettings{'RED_NETMASK'}";
62 $defaultNetworks->{$Lang::tr{'fwdfw red'}}{'NET'} = "$netsettings{'RED_ADDRESS'}";
63 $defaultNetworks->{$Lang::tr{'fwdfw red'}}{'NAME'} = "RED";
64 }
65 if ($netsettings{'ORANGE_DEV'} ne ''){
66 $defaultNetworks->{$Lang::tr{'orange'}}{'IPT'} = "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}";
67 $defaultNetworks->{$Lang::tr{'orange'}}{'NET'} = "$netsettings{'ORANGE_ADDRESS'}";
68 $defaultNetworks->{$Lang::tr{'orange'}}{'NAME'} = "ORANGE";
69 }
70
71 if ($netsettings{'BLUE_DEV'} ne ''){
72 $defaultNetworks->{$Lang::tr{'blue'}}{'IPT'} = "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}";
73 $defaultNetworks->{$Lang::tr{'blue'}}{'NET'} = "$netsettings{'BLUE_ADDRESS'}";
74 $defaultNetworks->{$Lang::tr{'blue'}}{'NAME'} = "BLUE";
75 }
76
77 #IPFire himself
78 $defaultNetworks->{'IPFire'}{'NAME'} = "IPFire";
79
80 # OpenVPN
81 if(-e "${General::swroot}/ovpn/settings")
82 {
83 my %ovpnSettings = ();
84 &readhash("${General::swroot}/ovpn/settings", \%ovpnSettings);
85
86 # OpenVPN on Red?
87 if(defined($ovpnSettings{'DOVPN_SUBNET'}))
88 {
89 my ($ip,$sub) = split(/\//,$ovpnSettings{'DOVPN_SUBNET'});
90 $sub=&General::iporsubtocidr($sub);
91 my @tempovpnsubnet = split("\/", $ovpnSettings{'DOVPN_SUBNET'});
92 $defaultNetworks->{'OpenVPN ' ."($ip/$sub)"}{'ADR'} = $tempovpnsubnet[0];
93 $defaultNetworks->{'OpenVPN ' ."($ip/$sub)"}{'NAME'} = "OpenVPN-Dyn";
94 }
95 } # end OpenVPN
96 # IPsec RW NET
97 if(-e "${General::swroot}/vpn/settings")
98 {
99 my %ipsecsettings = ();
100 &readhash("${General::swroot}/vpn/settings", \%ipsecsettings);
101 if($ipsecsettings{'RW_NET'} ne '')
102 {
103 my ($ip,$sub) = split(/\//,$ipsecsettings{'RW_NET'});
104 $sub=&General::iporsubtocidr($sub);
105 my @tempipsecsubnet = split("\/", $ipsecsettings{'RW_NET'});
106 $defaultNetworks->{'IPsec RW (' .$ip."/".$sub.")"}{'ADR'} = $tempipsecsubnet[0];
107 $defaultNetworks->{'IPsec RW (' .$ip."/".$sub.")"}{'NAME'} = "IPsec RW";
108 $defaultNetworks->{'IPsec RW (' .$ip."/".$sub.")"}{'NET'} = &getnextip($ip);
109 }
110 }
111 }
112 sub get_aliases
113 {
114
115 my $defaultNetworks = shift;
116 open(FILE, "${General::swroot}/ethernet/aliases") or die 'Unable to open aliases file.';
117 my @current = <FILE>;
118 close(FILE);
119 my $ctr = 0;
120 foreach my $line (@current)
121 {
122 if ($line ne ''){
123 chomp($line);
124 my @temp = split(/\,/,$line);
125 if ($temp[2] eq '') {
126 $temp[2] = "Alias $ctr : $temp[0]";
127 }
128 $defaultNetworks->{$temp[2]}{'IPT'} = "$temp[0]";
129 $defaultNetworks->{$temp[2]}{'NET'} = "$temp[0]";
130
131 $ctr++;
132 }
133 }
134 }
135
136 sub readhash
137 {
138 my $filename = $_[0];
139 my $hash = $_[1];
140 my ($var, $val);
141
142
143 # Some ipcop code expects that readhash 'complete' the hash if new entries
144 # are presents. Not clear it !!!
145 #%$hash = ();
146
147 open(FILE, $filename) or die "Unable to read file $filename";
148
149 while (<FILE>)
150 {
151 chop;
152 ($var, $val) = split /=/, $_, 2;
153 if ($var)
154 {
155 $val =~ s/^\'//g;
156 $val =~ s/\'$//g;
157
158 # Untaint variables read from hash
159 # trim space from begin and end
160 $var =~ s/^\s+//;
161 $var =~ s/\s+$//;
162 $var =~ /([A-Za-z0-9_-]*)/;
163 $var = $1;
164 $val =~ /([\w\W]*)/;
165 $val = $1;
166 $hash->{$var} = $val;
167 }
168 }
169 close FILE;
170 }
171
172
173 sub writehash
174 {
175 my $filename = $_[0];
176 my $hash = $_[1];
177 my ($var, $val);
178
179 # write cgi vars to the file.
180 open(FILE, ">${filename}") or die "Unable to write file $filename";
181 flock FILE, 2;
182 foreach $var (keys %$hash)
183 {
184 if ( $var eq "__CGI__"){next;}
185 $val = $hash->{$var};
186 # Darren Critchley Jan 17, 2003 added the following because when submitting with a graphic, the x and y
187 # location of the mouse are submitted as well, this was being written to the settings file causing
188 # some serious grief! This skips the variable.x and variable.y
189 if (!($var =~ /(.x|.y)$/)) {
190 if ($val =~ / /) {
191 $val = "\'$val\'"; }
192 if (!($var =~ /^ACTION/)) {
193 print FILE "${var}=${val}\n"; }
194 }
195 }
196 close FILE;
197 }
198
199 sub writehashpart
200 {
201 # This function replaces the given hash in the original hash by keeping the old
202 # content and just replacing the new content
203
204 my $filename = $_[0];
205 my $newhash = $_[1];
206 my %oldhash;
207 my ($var, $val);
208
209 readhash("${filename}", \%oldhash);
210
211 foreach $var (keys %$newhash){
212 $oldhash{$var}=$newhash->{$var};
213 }
214
215 # write cgi vars to the file.
216 open(FILE, ">${filename}") or die "Unable to write file $filename";
217 flock FILE, 2;
218 foreach $var (keys %oldhash)
219 {
220 if ( $var eq "__CGI__"){next;}
221 $val = $oldhash{$var};
222 # Darren Critchley Jan 17, 2003 added the following because when submitting with a graphic, the x and y
223 # location of the mouse are submitted as well, this was being written to the settings file causing
224 # some serious grief! This skips the variable.x and variable.y
225 if (!($var =~ /(.x|.y)$/)) {
226 if ($val =~ / /) {
227 $val = "\'$val\'"; }
228 if (!($var =~ /^ACTION/)) {
229 print FILE "${var}=${val}\n"; }
230 }
231 }
232 close FILE;
233 }
234
235 sub age {
236 my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
237 $atime, $mtime, $ctime, $blksize, $blocks) = stat $_[0];
238 my $totalsecs = time() - $mtime;
239 my @s = ();
240
241 my $secs = $totalsecs % 60;
242 $totalsecs /= 60;
243 if ($secs > 0) {
244 push(@s, "${secs}s");
245 }
246
247 my $min = $totalsecs % 60;
248 $totalsecs /= 60;
249 if ($min > 0) {
250 push(@s, "${min}m");
251 }
252
253 my $hrs = $totalsecs % 24;
254 $totalsecs /= 24;
255 if ($hrs > 0) {
256 push(@s, "${hrs}h");
257 }
258
259 my $days = int($totalsecs);
260 if ($days > 0) {
261 push(@s, "${days}d");
262 }
263
264 return join(" ", reverse(@s));
265 }
266
267 sub validip
268 {
269 my $ip = $_[0];
270
271 if (!($ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)) {
272 return 0; }
273 else
274 {
275 my @octets = ($1, $2, $3, $4);
276 foreach $_ (@octets)
277 {
278 if (/^0./) {
279 return 0; }
280 if ($_ < 0 || $_ > 255) {
281 return 0; }
282 }
283 return 1;
284 }
285 }
286
287 sub validmask {
288 my $mask = shift;
289
290 return &Network::check_netmask($mask) || &Network::check_prefix($mask);
291 }
292
293 sub validipormask
294 {
295 my $ipormask = $_[0];
296
297 # see if it is a IP only.
298 if (&validip($ipormask)) {
299 return 1; }
300 # split it into number and mask.
301 if (!($ipormask =~ /^(.*?)\/(.*?)$/)) {
302 return 0; }
303 my $ip = $1;
304 my $mask = $2;
305 # first part not a ip?
306 if (!(&validip($ip))) {
307 return 0; }
308 return &validmask($mask);
309 }
310
311 sub subtocidr {
312 return &Network::convert_netmask2prefix(shift);
313 }
314
315 sub cidrtosub {
316 return &Network::convert_prefix2netmask(shift);
317 }
318
319 sub iporsubtodec
320 {
321 #Gets: Ip address or subnetmask in decimal oder CIDR
322 #Gives: What it gets only in CIDR format
323 my $subnet=$_[0];
324 my $net;
325 my $mask;
326 my $full=0;
327 if ($subnet =~ /^(.*?)\/(.*?)$/) {
328 ($net,$mask) = split (/\//,$subnet);
329 $full=1;
330 return "$subnet";
331 }else{
332 $mask=$subnet;
333 }
334 #Subnet already in decimal and valid?
335 if ($mask=~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ &&(($1<=255 && $2<=$1 && $3<=$2 && $4<=$3 ))) {
336 for (my $i=0;$i<=32;$i++){
337 if (&General::cidrtosub($i) eq $mask){
338 if ($full == 0){return $mask;}else{
339 return $net."/".$mask;
340 }
341 }
342 }
343 }
344 #Subnet in binary format?
345 if ($mask=~/^(\d{1,2})$/ && (($1<=32 && $1>=0))){
346 if($full == 0){ return &General::cidrtosub($mask);}else{
347 return $net."/".&General::cidrtosub($mask);
348 }
349 }else{
350 return 3;
351 }
352 return 3;
353 }
354
355
356 sub iporsubtocidr
357 {
358 #gets: Ip Address or subnetmask in decimal oder CIDR
359 #Gives: What it gets only in CIDR format
360 my $subnet=$_[0];
361 my $net;
362 my $mask;
363 my $full=0;
364 if ($subnet =~ /^(.*?)\/(.*?)$/) {
365 ($net,$mask) = split (/\//,$subnet);
366 $full=1;
367 }else{
368 $mask=$subnet;
369 }
370 #Subnet in decimal and valid?
371 if ($mask=~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ &&(($1<=255 && $2<=$1 && $3<=$2 && $4<=$3 ))) {
372 for (my $i=0;$i<=32;$i++){
373 if (&General::cidrtosub($i) eq $mask){
374 if ($full == 0){return &General::subtocidr($mask);}else{
375 return $net."/".&General::subtocidr($mask);
376 }
377 }
378 }
379 }
380 #Subnet already in binary format?
381 if ($mask=~/^(\d{1,2})$/ && (($1<=32 && $1>=0))){
382 if($full == 0){ return $mask;}else{
383 return $net."/".$mask;
384 }
385 }else{
386 return 3;
387 }
388 return 3;
389 }
390
391 sub getnetworkip {
392 my $arg = join("/", @_);
393
394 return &Network::get_netaddress($arg);
395 }
396
397 sub getccdbc
398 {
399 #Gets: IP in Form ("192.168.0.0/24")
400 #Gives: Broadcastaddress of network
401 my $ccdnet=$_;
402 my ($ccdip,$ccdsubnet) = split "/",$ccdnet;
403 my $ip_address_binary = inet_aton( $ccdip );
404 my $netmask_binary = ~pack("N", (2**(32-$ccdsubnet))-1);
405 my $broadcast_address = inet_ntoa( $ip_address_binary | ~$netmask_binary );
406 return $broadcast_address;
407 }
408
409 sub ip2dec {
410 return &Network::ip2bin(shift);
411 }
412
413 sub dec2ip {
414 return &Network::bin2ip(shift);
415 }
416
417 sub getnextip {
418 return &Network::find_next_ip_address(shift, 4);
419 }
420
421 sub getlastip {
422 return &Network::find_next_ip_address(shift, -1);
423 }
424
425 sub validipandmask
426 {
427 #Gets: Ip address in 192.168.0.0/24 or 192.168.0.0/255.255.255.0 and checks if subnet valid
428 #Gives: True bzw 0 if success or false
429 my $ccdnet=$_[0];
430 my $subcidr;
431
432 if (!($ccdnet =~ /^(.*?)\/(.*?)$/)) {
433 return 0;
434 }
435 my ($ccdip,$ccdsubnet)=split (/\//, $ccdnet);
436 #IP valid?
437 if ($ccdip=~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ &&(($1>0 && $1<=255 && $2>=0 && $2<=255 && $3>=0 && $3<=255 && $4<=255 ))) {
438 #Subnet in decimal and valid?
439 if ($ccdsubnet=~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ &&(($1<=255 && $2<=$1 && $3<=$2 && $4<=$3 ))) {
440 for (my $i=0;$i<=32;$i++){
441 if (&General::cidrtosub($i) eq $ccdsubnet){
442 return 1;
443 }
444 }
445 #Subnet already in binary format?
446 }elsif ($ccdsubnet=~/^(\d{1,2})$/ && (($1<=32 && $1>=0))){
447 return 1;
448 }else{
449 return 0;
450 }
451
452 }
453 return 0;
454 }
455
456 sub checksubnets
457 {
458 my %ccdconfhash=();
459 my %ovpnconfhash=();
460 my %vpnconf=();
461 my %ipsecconf=();
462 my %ownnet=();
463 my %ovpnconf=();
464 my @ccdconf=();
465 my $ccdname=$_[0];
466 my $ccdnet=$_[1];
467 my $ownnet=$_[2];
468 my $errormessage;
469 my ($ip,$cidr)=split(/\//,$ccdnet);
470 $cidr=&iporsubtocidr($cidr);
471
472 #get OVPN-Subnet (dynamic range)
473 &readhash("${General::swroot}/ovpn/settings", \%ovpnconf);
474 my ($ovpnip,$ovpncidr)= split (/\//,$ovpnconf{'DOVPN_SUBNET'});
475 $ovpncidr=&iporsubtocidr($ovpncidr);
476
477 #check if we try to use same network as ovpn server
478 if ("$ip/$cidr" eq "$ovpnip/$ovpncidr") {
479 $errormessage=$errormessage.$Lang::tr{'ccd err isovpnnet'}."<br>";
480 return $errormessage;
481 }
482
483 #check if we try to use same network as another ovpn N2N
484 if($ownnet ne 'ovpn'){
485 &readhasharray("${General::swroot}/ovpn/ovpnconfig", \%ovpnconfhash);
486 foreach my $key (keys %ovpnconfhash) {
487 if ($ovpnconfhash{$key}[3] eq 'net'){
488 my @ovpnnet=split (/\//,$ovpnconfhash{$key}[11]);
489 if (&IpInSubnet($ip,$ovpnnet[0],&iporsubtodec($ovpnnet[1]))){
490 $errormessage=$errormessage.$Lang::tr{'ccd err isovpnn2n'}." $ovpnconfhash{$key}[1] <br>";
491 return $errormessage;
492 }
493 }
494 }
495 }
496
497 #check if we use a network-name/subnet (static-ovpn) that already exists
498 &readhasharray("${General::swroot}/ovpn/ccd.conf", \%ccdconfhash);
499 foreach my $key (keys %ccdconfhash) {
500 @ccdconf=split(/\//,$ccdconfhash{$key}[1]);
501 if ($ccdname eq $ccdconfhash{$key}[0])
502 {
503 $errormessage=$errormessage.$Lang::tr{'ccd err nameexist'}."<br>";
504 return $errormessage;
505 }
506 my ($newip,$newsub) = split(/\//,$ccdnet);
507 if (&IpInSubnet($newip,$ccdconf[0],&iporsubtodec($ccdconf[1])))
508 {
509 $errormessage=$errormessage.$Lang::tr{'ccd err issubnet'}." $ccdconfhash{$key}[0]<br>";
510 return $errormessage;
511 }
512 }
513
514 #check if we use a ipsec right network which is already defined
515 if($ownnet ne 'ipsec'){
516 &General::readhasharray("${General::swroot}/vpn/config", \%ipsecconf);
517 foreach my $key (keys %ipsecconf){
518 if ($ipsecconf{$key}[11] ne ''){
519 foreach my $ipsecsubitem (split(/\|/, $ipsecconf{$key}[11])) {
520 my ($ipsecip,$ipsecsub) = split (/\//, $ipsecconf{$key}[11]);
521 $ipsecsub=&iporsubtodec($ipsecsub);
522 if($ipsecconf{$key}[1] ne $ccdname){
523 if ( &IpInSubnet ($ip,$ipsecip,$ipsecsub) ){
524 $errormessage=$Lang::tr{'ccd err isipsecnet'}." Name: $ipsecconf{$key}[1]";
525 return $errormessage;
526 }
527 }
528 }
529 }
530 }
531 }
532
533 #check if we use the ipsec RW Network (if defined)
534 &readhash("${General::swroot}/vpn/settings", \%vpnconf);
535 if ($vpnconf{'RW_NET'} ne ''){
536 my ($ipsecrwnet,$ipsecrwsub)=split (/\//, $vpnconf{'RW_NET'});
537 if (&IpInSubnet($ip,$ipsecrwnet,&iporsubtodec($ipsecrwsub)))
538 {
539 $errormessage=$errormessage.$Lang::tr{'ccd err isipsecrw'}."<br>";
540 return $errormessage;
541 }
542 }
543
544 #check if we use one of ipfire's networks (green,orange,blue)
545 &readhash("${General::swroot}/ethernet/settings", \%ownnet);
546 if (($ownnet{'GREEN_NETADDRESS'} ne '' && $ownnet{'GREEN_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'GREEN_NETADDRESS'},&iporsubtodec($ownnet{'GREEN_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err green'};return $errormessage;}
547 if (($ownnet{'ORANGE_NETADDRESS'} ne '' && $ownnet{'ORANGE_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'ORANGE_NETADDRESS'},&iporsubtodec($ownnet{'ORANGE_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err orange'};return $errormessage;}
548 if (($ownnet{'BLUE_NETADDRESS'} ne '' && $ownnet{'BLUE_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'BLUE_NETADDRESS'},&iporsubtodec($ownnet{'BLUE_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err blue'};return $errormessage;}
549 if (($ownnet{'RED_NETADDRESS'} ne '' && $ownnet{'RED_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'RED_NETADDRESS'},&iporsubtodec($ownnet{'RED_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err red'};return $errormessage;}
550 }
551
552 sub check_net_internal{
553 my $network=shift;
554 my ($ip,$cidr)=split(/\//,$network);
555 my %ownnet=();
556 my $errormessage;
557 $cidr=&iporsubtocidr($cidr);
558 #check if we use one of ipfire's networks (green,orange,blue)
559 &readhash("${General::swroot}/ethernet/settings", \%ownnet);
560 if (($ownnet{'GREEN_NETADDRESS'} ne '' && $ownnet{'GREEN_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'GREEN_NETADDRESS'},&iporsubtodec($ownnet{'GREEN_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err green'};return $errormessage;}
561 if (($ownnet{'ORANGE_NETADDRESS'} ne '' && $ownnet{'ORANGE_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'ORANGE_NETADDRESS'},&iporsubtodec($ownnet{'ORANGE_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err orange'};return $errormessage;}
562 if (($ownnet{'BLUE_NETADDRESS'} ne '' && $ownnet{'BLUE_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'BLUE_NETADDRESS'},&iporsubtodec($ownnet{'BLUE_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err blue'};return $errormessage;}
563 if (($ownnet{'RED_NETADDRESS'} ne '' && $ownnet{'RED_NETADDRESS'} ne '0.0.0.0') && &IpInSubnet($ip,$ownnet{'RED_NETADDRESS'},&iporsubtodec($ownnet{'RED_NETMASK'}))){ $errormessage=$Lang::tr{'ccd err red'};return $errormessage;}
564 }
565
566 sub validport
567 {
568 $_ = $_[0];
569
570 if (!/^\d+$/) {
571 return 0; }
572 if (/^0./) {
573 return 0; }
574 if ($_ >= 1 && $_ <= 65535) {
575 return 1; }
576 return 0;
577 }
578
579 sub validproxyport
580 {
581 $_ = $_[0];
582
583 if (!/^\d+$/) {
584 return 0; }
585 if (/^0./) {
586 return 0; }
587 if ($_ == 53 || $_ == 222 || $_ == 444 || $_ == 81 ) {
588 return 0; }
589 elsif ($_ >= 1 && $_ <= 65535) {
590 return 1; }
591 return 0;
592 }
593
594 sub validmac
595 {
596 my $checkmac = $_[0];
597 my $ot = '[0-9a-f]{2}'; # 2 Hex digits (one octet)
598 if ($checkmac !~ /^$ot:$ot:$ot:$ot:$ot:$ot$/i)
599 {
600 return 0;
601 }
602 return 1;
603 }
604
605 sub validhostname
606 {
607 # Checks a hostname against RFC1035
608 my $hostname = $_[0];
609
610 # Each part should be at least two characters in length
611 # but no more than 63 characters
612 if (length ($hostname) < 1 || length ($hostname) > 63) {
613 return 0;}
614 # Only valid characters are a-z, A-Z, 0-9 and -
615 if ($hostname !~ /^[a-zA-Z0-9-\s]*$/) {
616 return 0;}
617 # First character can only be a letter or a digit
618 if (substr ($hostname, 0, 1) !~ /^[a-zA-Z0-9]*$/) {
619 return 0;}
620 # Last character can only be a letter or a digit
621 if (substr ($hostname, -1, 1) !~ /^[a-zA-Z0-9]*$/) {
622 return 0;}
623 return 1;
624 }
625
626 sub validdomainname
627 {
628 my $part;
629
630 # Checks a domain name against RFC1035
631 my $domainname = $_[0];
632 my @parts = split (/\./, $domainname); # Split hostname at the '.'
633
634 foreach $part (@parts) {
635 # Each part should be no more than 63 characters in length
636 if (length ($part) < 1 || length ($part) > 63) {
637 return 0;}
638 # Only valid characters are a-z, A-Z, 0-9, _ and -
639 if ($part !~ /^[a-zA-Z0-9_-]*$/) {
640 return 0;
641 }
642 }
643 return 1;
644 }
645
646 sub validfqdn
647 {
648 my $part;
649
650 # Checks a fully qualified domain name against RFC1035
651 my $fqdn = $_[0];
652 my @parts = split (/\./, $fqdn); # Split hostname at the '.'
653 if (scalar(@parts) < 2) { # At least two parts should
654 return 0;} # exist in a FQDN
655 # (i.e.hostname.domain)
656 foreach $part (@parts) {
657 # Each part should be at least one character in length
658 # but no more than 63 characters
659 if (length ($part) < 1 || length ($part) > 63) {
660 return 0;}
661 # Only valid characters are a-z, A-Z, 0-9 and -
662 if ($part !~ /^[a-zA-Z0-9-]*$/) {
663 return 0;}
664 # First character can only be a letter or a digit
665 if (substr ($part, 0, 1) !~ /^[a-zA-Z0-9]*$/) {
666 return 0;}
667 # Last character can only be a letter or a digit
668 if (substr ($part, -1, 1) !~ /^[a-zA-Z0-9]*$/) {
669 return 0;}
670 }
671 return 1;
672 }
673
674 sub validportrange # used to check a port range
675 {
676 my $port = $_[0]; # port values
677 $port =~ tr/-/:/; # replace all - with colons just in case someone used -
678 my $srcdst = $_[1]; # is it a source or destination port
679
680 if (!($port =~ /^(\d+)\:(\d+)$/)) {
681
682 if (!(&validport($port))) {
683 if ($srcdst eq 'src'){
684 return $Lang::tr{'source port numbers'};
685 } else {
686 return $Lang::tr{'destination port numbers'};
687 }
688 }
689 }
690 else
691 {
692 my @ports = ($1, $2);
693 if ($1 >= $2){
694 if ($srcdst eq 'src'){
695 return $Lang::tr{'bad source range'};
696 } else {
697 return $Lang::tr{'bad destination range'};
698 }
699 }
700 foreach $_ (@ports)
701 {
702 if (!(&validport($_))) {
703 if ($srcdst eq 'src'){
704 return $Lang::tr{'source port numbers'};
705 } else {
706 return $Lang::tr{'destination port numbers'};
707 }
708 }
709 }
710 return;
711 }
712 }
713
714 sub IpInSubnet {
715 my $addr = shift;
716 my $network = shift;
717 my $netmask = shift;
718
719 return &Network::ip_address_in_network($addr, "$network/$netmask");
720 }
721
722 #
723 # Return the following IP (IP+1) in dotted notation.
724 # Call: NextIP ('1.1.1.1');
725 # Return: '1.1.1.2'
726 #
727 sub NextIP {
728 return &Network::find_next_ip_address(shift, 1);
729 }
730
731 sub NextIP2 {
732 return &Network::find_next_ip_address(shift, 4);
733 }
734
735 sub ipcidr {
736 my ($ip,$cidr) = &Net::IPv4Addr::ipv4_parse(shift);
737 return "$ip\/$cidr";
738 }
739
740 sub ipcidr2msk {
741 my ($ip,$cidr) = &Net::IPv4Addr::ipv4_parse(shift);
742 my $netmask = &Net::IPv4Addr::ipv4_cidr2msk($cidr);
743 return "$ip\/$netmask";
744 }
745
746 sub validemail {
747 my $address = shift;
748 my @parts = split( /\@/, $address );
749 my $count=@parts;
750
751 #check if we have one part before and after '@'
752 return 0 if ( $count != 2 );
753
754 #check if one of the parts starts or ends with a dot
755 return 0 if ( substr($parts[0],0,1) eq '.' );
756 return 0 if ( substr($parts[0],-1,1) eq '.' );
757 return 0 if ( substr($parts[1],0,1) eq '.' );
758 return 0 if ( substr($parts[1],-1,1) eq '.' );
759
760 #check first addresspart (before '@' sign)
761 return 0 if ( $parts[0] !~ m/^[a-zA-Z0-9\.!\-\+#]+$/ );
762
763 #check second addresspart (after '@' sign)
764 return 0 if ( $parts[1] !~ m/^[a-zA-Z0-9\.\-]+$/ );
765
766 return 1;
767 }
768
769 #
770 # Currently only vpnmain use this three procs (readhasharray, writehasharray, findhasharray)
771 # The 'key' used is numeric but is perfectly unneeded! This will to be removed so don't use
772 # this code. Vpnmain will be splitted in parts: x509/pki, connection ipsec, connection other,... .
773 #
774 sub readhasharray {
775 my ($filename, $hash) = @_;
776 %$hash = ();
777
778 open(FILE, $filename) or die "Unable to read file $filename";
779
780 while (<FILE>) {
781 my ($key, $rest, @temp);
782 chomp;
783 ($key, $rest) = split (/,/, $_, 2);
784 if ($key =~ /^[0-9]+$/) {
785 @temp = split (/,/, $rest);
786 $hash->{$key} = \@temp;
787 }
788 }
789 close FILE;
790 return;
791 }
792
793 sub writehasharray {
794 my ($filename, $hash) = @_;
795 my ($key, @temp, $i);
796
797 open(FILE, ">$filename") or die "Unable to write to file $filename";
798
799 foreach $key (keys %$hash) {
800 if ($key =~ /^[0-9]+$/) {
801 print FILE "$key";
802 foreach $i (0 .. $#{$hash->{$key}}) {
803 print FILE ",$hash->{$key}[$i]";
804 }
805 print FILE "\n";
806 }
807 }
808 close FILE;
809 return;
810 }
811
812 sub findhasharraykey {
813 foreach my $i (1 .. 1000000) {
814 if ( ! exists $_[0]{$i}) {
815 return $i;
816 }
817 }
818 }
819
820 sub srtarray
821 # Darren Critchley - darrenc@telus.net - (c) 2003
822 # &srtarray(SortOrder, AlphaNumeric, SortDirection, ArrayToBeSorted)
823 # This subroutine will take the following parameters:
824 # ColumnNumber = the column which you want to sort on, starts at 1
825 # AlphaNumberic = a or n (lowercase) defines whether the sort should be alpha or numberic
826 # SortDirection = asc or dsc (lowercase) Ascending or Descending sort
827 # ArrayToBeSorted = the array that wants sorting
828 #
829 # Returns an array that is sorted to your specs
830 #
831 # If SortOrder is greater than the elements in array, then it defaults to the first element
832 #
833 {
834 my ($colno, $alpnum, $srtdir, @tobesorted) = @_;
835 my @tmparray;
836 my @srtedarray;
837 my $line;
838 my $newline;
839 my $ctr;
840 my $ttlitems = scalar @tobesorted; # want to know the number of rows in the passed array
841 if ($ttlitems < 1){ # if no items, don't waste our time lets leave
842 return (@tobesorted);
843 }
844 my @tmp = split(/\,/,$tobesorted[0]);
845 $ttlitems = scalar @tmp; # this should be the number of elements in each row of the passed in array
846
847 # Darren Critchley - validate parameters
848 if ($colno > $ttlitems){$colno = '1';}
849 $colno--; # remove one from colno to deal with arrays starting at 0
850 if($colno < 0){$colno = '0';}
851 if ($alpnum ne '') { $alpnum = lc($alpnum); } else { $alpnum = 'a'; }
852 if ($srtdir ne '') { $srtdir = lc($srtdir); } else { $srtdir = 'src'; }
853
854 foreach $line (@tobesorted)
855 {
856 chomp($line);
857 if ($line ne '') {
858 my @temp = split(/\,/,$line);
859 # Darren Critchley - juggle the fields so that the one we want to sort on is first
860 my $tmpholder = $temp[0];
861 $temp[0] = $temp[$colno];
862 $temp[$colno] = $tmpholder;
863 $newline = "";
864 for ($ctr=0; $ctr < $ttlitems ; $ctr++) {
865 $newline=$newline . $temp[$ctr] . ",";
866 }
867 chop($newline);
868 push(@tmparray,$newline);
869 }
870 }
871 if ($alpnum eq 'n') {
872 @tmparray = sort {$a <=> $b} @tmparray;
873 } else {
874 @tmparray = (sort @tmparray);
875 }
876 foreach $line (@tmparray)
877 {
878 chomp($line);
879 if ($line ne '') {
880 my @temp = split(/\,/,$line);
881 my $tmpholder = $temp[0];
882 $temp[0] = $temp[$colno];
883 $temp[$colno] = $tmpholder;
884 $newline = "";
885 for ($ctr=0; $ctr < $ttlitems ; $ctr++){
886 $newline=$newline . $temp[$ctr] . ",";
887 }
888 chop($newline);
889 push(@srtedarray,$newline);
890 }
891 }
892
893 if ($srtdir eq 'dsc') {
894 @tmparray = reverse(@srtedarray);
895 return (@tmparray);
896 } else {
897 return (@srtedarray);
898 }
899 }
900
901 sub FetchPublicIp {
902 my %proxysettings;
903 &General::readhash("${General::swroot}/proxy/settings", \%proxysettings);
904 if ($_=$proxysettings{'UPSTREAM_PROXY'}) {
905 my ($peer, $peerport) = (/^(?:[a-zA-Z ]+\:\/\/)?(?:[A-Za-z0-9\_\.\-]*?(?:\:[A-Za-z0-9\_\.\-]*?)?\@)?([a-zA-Z0-9\.\_\-]*?)(?:\:([0-9]{1,5}))?(?:\/.*?)?$/);
906 Net::SSLeay::set_proxy($peer,$peerport,$proxysettings{'UPSTREAM_USER'},$proxysettings{'UPSTREAM_PASSWORD'} );
907 }
908 my $user_agent = &MakeUserAgent();
909 my ($out, $response) = Net::SSLeay::get_http( 'checkip4.dns.lightningwirelabs.com',
910 80,
911 "/",
912 Net::SSLeay::make_headers('User-Agent' => $user_agent )
913 );
914 if ($response =~ m%HTTP/1\.. 200 OK%) {
915 $out =~ /Your IP address is: (\d+.\d+.\d+.\d+)/;
916 return $1;
917 }
918 return '';
919 }
920
921 #
922 # Check if hostname.domain provided have IP provided
923 # use gethostbyname to verify that
924 # Params:
925 # IP
926 # hostname
927 # domain
928 # Output
929 # 1 IP matches host.domain
930 # 0 not in sync
931 #
932 sub DyndnsServiceSync ($;$;$) {
933
934 my ($ip,$hostName,$domain) = @_;
935 my @addresses;
936
937 #fix me no ip GROUP, what is the name ?
938 $hostName =~ s/$General::noipprefix//;
939 if ($hostName) { #may be empty
940 $hostName = "$hostName.$domain";
941 @addresses = gethostbyname($hostName);
942 }
943
944 if ($addresses[0] eq '') { # nothing returned ?
945 $hostName = $domain; # try resolving with domain only
946 @addresses = gethostbyname($hostName);
947 }
948
949 if ($addresses[0] ne '') { # got something ?
950 #&General::log("name:$addresses[0], alias:$addresses[1]");
951 # Build clear text list of IP
952 @addresses = map ( &Socket::inet_ntoa($_), @addresses[4..$#addresses]);
953 if (grep (/$ip/, @addresses)) {
954 return 1;
955 }
956 }
957 return 0;
958 }
959 #
960 # This sub returns the red IP used to compare in DyndnsServiceSync
961 #
962 sub GetDyndnsRedIP {
963 my %settings;
964 &General::readhash("${General::swroot}/ddns/settings", \%settings);
965
966 open(IP, "${General::swroot}/red/local-ipaddress") or return 'unavailable';
967 my $ip = <IP>;
968 close(IP);
969 chomp $ip;
970
971 # 100.64.0.0/10 is reserved for dual-stack lite (http://tools.ietf.org/html/rfc6598).
972 if (&General::IpInSubnet ($ip,'10.0.0.0','255.0.0.0') ||
973 &General::IpInSubnet ($ip,'172.16.0.0.','255.240.0.0') ||
974 &General::IpInSubnet ($ip,'192.168.0.0','255.255.0.0') ||
975 &General::IpInSubnet ($ip,'100.64.0.0', '255.192.0.0'))
976 {
977 if ($settings{'BEHINDROUTER'} eq 'FETCH_IP') {
978 my $RealIP = &General::FetchPublicIp;
979 $ip = (&General::validip ($RealIP) ? $RealIP : 'unavailable');
980 }
981 }
982 return $ip;
983 }
984
985 # Translate ICMP code to text
986 # ref: http://www.iana.org/assignments/icmp-parameters
987 sub GetIcmpDescription ($) {
988 my $index = shift;
989 my @icmp_description = (
990 'Echo Reply', #0
991 'Unassigned',
992 'Unassigned',
993 'Destination Unreachable',
994 'Source Quench',
995 'Redirect',
996 'Alternate Host Address',
997 'Unassigned',
998 'Echo',
999 'Router Advertisement',
1000 'Router Solicitation', #10
1001 'Time Exceeded',
1002 'Parameter Problem',
1003 'Timestamp',
1004 'Timestamp Reply',
1005 'Information Request',
1006 'Information Reply',
1007 'Address Mask Request',
1008 'Address Mask Reply',
1009 'Reserved (for Security)',
1010 'Reserved (for Robustness Experiment)', #20
1011 'Reserved',
1012 'Reserved',
1013 'Reserved',
1014 'Reserved',
1015 'Reserved',
1016 'Reserved',
1017 'Reserved',
1018 'Reserved',
1019 'Reserved',
1020 'Traceroute', #30
1021 'Datagram Conversion Error',
1022 'Mobile Host Redirect',
1023 'IPv6 Where-Are-You',
1024 'IPv6 I-Am-Here',
1025 'Mobile Registration Request',
1026 'Mobile Registration Reply',
1027 'Domain Name Request',
1028 'Domain Name Reply',
1029 'SKIP',
1030 'Photur', #40
1031 'Experimental');
1032 if ($index>41) {return 'unknown'} else {return $icmp_description[$index]};
1033 }
1034
1035 sub GetCoreUpdateVersion() {
1036 my $core_update;
1037
1038 open(FILE, "/opt/pakfire/db/core/mine");
1039 while (<FILE>) {
1040 $core_update = $_;
1041 last;
1042 }
1043 close(FILE);
1044
1045 return $core_update;
1046 }
1047
1048 sub MakeUserAgent() {
1049 my $user_agent = "IPFire/$General::version";
1050
1051 my $core_update = &GetCoreUpdateVersion();
1052 if ($core_update ne "") {
1053 $user_agent .= "/$core_update";
1054 }
1055
1056 return $user_agent;
1057 }
1058
1059 sub RedIsWireless() {
1060 # This function checks if a network device is a wireless device.
1061
1062 my %settings = ();
1063 &readhash("${General::swroot}/ethernet/settings", \%settings);
1064
1065 # Find the name of the network device.
1066 my $device = $settings{'RED_DEV'};
1067
1068 # Exit, if no device is configured.
1069 return 0 if ($device eq "");
1070
1071 # Return 1 if the device is a wireless one.
1072 my $path = "/sys/class/net/$device/wireless";
1073 if (-d $path) {
1074 return 1;
1075 }
1076
1077 # Otherwise return zero.
1078 return 0;
1079 }
1080
1081 # Function to read a file with UTF-8 charset.
1082 sub read_file_utf8 ($) {
1083 my ($file) = @_;
1084
1085 open my $in, '<:encoding(UTF-8)', $file or die "Could not open '$file' for reading $!";
1086 local $/ = undef;
1087 my $all = <$in>;
1088 close $in;
1089
1090 return $all;
1091 }
1092
1093 # Function to write a file with UTF-8 charset.
1094 sub write_file_utf8 ($) {
1095 my ($file, $content) = @_;
1096
1097 open my $out, '>:encoding(UTF-8)', $file or die "Could not open '$file' for writing $!";;
1098 print $out $content;
1099 close $out;
1100
1101 return;
1102 }
1103
1104 my $FIREWALL_RELOAD_INDICATOR = "${General::swroot}/firewall/reread";
1105
1106 sub firewall_config_changed() {
1107 open FILE, ">$FIREWALL_RELOAD_INDICATOR" or die "Could not open $FIREWALL_RELOAD_INDICATOR";
1108 close FILE;
1109 }
1110
1111 sub firewall_needs_reload() {
1112 if (-e "$FIREWALL_RELOAD_INDICATOR") {
1113 return 1;
1114 }
1115
1116 return 0;
1117 }
1118
1119 sub firewall_reload() {
1120 system("/usr/local/bin/firewallctrl");
1121 }
1122
1123 # Function which will return the used interface for the red network zone (red0, ppp0, etc).
1124 sub get_red_interface() {
1125
1126 open(IFACE, "${General::swroot}/red/iface") or die "Could not open /var/ipfire/red/iface";
1127
1128 my $interface = <IFACE>;
1129 close(IFACE);
1130 chomp $interface;
1131
1132 return $interface;
1133 }
1134
1135 1;