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