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