Projet

Général

Profil

Télécharger (52,4 ko) Statistiques
| Branche: | Tag: | Révision:

univnautes / etc / inc / util.inc @ 9060f420

1
<?php
2
/*
3
	util.inc
4
	part of the pfSense project (https://www.pfsense.org)
5

    
6
	originally part of m0n0wall (http://m0n0.ch/wall)
7
	Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
8
	All rights reserved.
9

    
10
	Redistribution and use in source and binary forms, with or without
11
	modification, are permitted provided that the following conditions are met:
12

    
13
	1. Redistributions of source code must retain the above copyright notice,
14
	   this list of conditions and the following disclaimer.
15

    
16
	2. Redistributions in binary form must reproduce the above copyright
17
	   notice, this list of conditions and the following disclaimer in the
18
	   documentation and/or other materials provided with the distribution.
19

    
20
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
	POSSIBILITY OF SUCH DAMAGE.
30
*/
31

    
32
/*
33
	pfSense_BUILDER_BINARIES:	/bin/ps	/bin/kill	/usr/bin/killall	/sbin/ifconfig	/usr/bin/netstat
34
	pfSense_BUILDER_BINARIES:	/usr/bin/awk	/sbin/dmesg		/sbin/ping /usr/local/sbin/gzsig	/usr/sbin/arp
35
	pfSense_BUILDER_BINARIES:	/sbin/conscontrol	/sbin/devd	/bin/ps
36
	pfSense_MODULE:	utils
37
*/
38

    
39
/* kill a process by pid file */
40
function killbypid($pidfile) {
41
	return sigkillbypid($pidfile, "TERM");
42
}
43

    
44
function isvalidpid($pidfile) {
45
	$output = "";
46
	if (file_exists($pidfile)) {
47
		exec("/bin/pgrep -nF {$pidfile}", $output, $retval);
48
		return (intval($retval) == 0);
49
	}
50
	return false;
51
}
52

    
53
function is_process_running($process) {
54
	$output = "";
55
	exec("/bin/pgrep -anx " . escapeshellarg($process), $output, $retval);
56

    
57
	return (intval($retval) == 0);
58
}
59

    
60
function isvalidproc($proc) {
61
	return is_process_running($proc);
62
}
63

    
64
/* sigkill a process by pid file */
65
/* return 1 for success and 0 for a failure */
66
function sigkillbypid($pidfile, $sig) {
67
	if (file_exists($pidfile))
68
		return mwexec("/bin/pkill " . escapeshellarg("-{$sig}") . " -F {$pidfile}", true);
69

    
70
	return 0;
71
}
72

    
73
/* kill a process by name */
74
function sigkillbyname($procname, $sig) {
75
	if(isvalidproc($procname))
76
		return mwexec("/usr/bin/killall " . escapeshellarg("-{$sig}") . " " . escapeshellarg($procname), true);
77
}
78

    
79
/* kill a process by name */
80
function killbyname($procname) {
81
	if(isvalidproc($procname))
82
		mwexec("/usr/bin/killall " . escapeshellarg($procname));
83
}
84

    
85
function is_subsystem_dirty($subsystem = "") {
86
	global $g;
87

    
88
	if ($subsystem == "")
89
		return false;
90

    
91
	if (file_exists("{$g['varrun_path']}/{$subsystem}.dirty"))
92
		return true;
93

    
94
	return false;
95
}
96

    
97
function mark_subsystem_dirty($subsystem = "") {
98
	global $g;
99

    
100
	if (!file_put_contents("{$g['varrun_path']}/{$subsystem}.dirty", "DIRTY"))
101
		log_error(sprintf(gettext("WARNING: Could not mark subsystem: %s dirty"), $subsystem));
102
}
103

    
104
function clear_subsystem_dirty($subsystem = "") {
105
	global $g;
106

    
107
	@unlink("{$g['varrun_path']}/{$subsystem}.dirty");
108
}
109

    
110
function config_lock() {
111
	return;
112
}
113
function config_unlock() {
114
	return;
115
}
116

    
117
/* lock configuration file */
118
function lock($lock, $op = LOCK_SH) {
119
	global $g, $cfglckkeyconsumers;
120
	if (!$lock)
121
		die(gettext("WARNING: You must give a name as parameter to lock() function."));
122
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
123
		@touch("{$g['tmp_path']}/{$lock}.lock");
124
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
125
	}
126
	$cfglckkeyconsumers++;
127
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
128
		if (flock($fp, $op))
129
			return $fp;
130
		else
131
			fclose($fp);
132
	}
133
}
134

    
135
function try_lock($lock, $timeout = 5) {
136
	global $g, $cfglckkeyconsumers;
137
	if (!$lock)
138
		die(gettext("WARNING: You must give a name as parameter to try_lock() function."));
139
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
140
		@touch("{$g['tmp_path']}/{$lock}.lock");
141
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
142
	}
143
	$cfglckkeyconsumers++;
144
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
145
		$trycounter = 0;
146
		while(!flock($fp, LOCK_EX | LOCK_NB)) {
147
			if ($trycounter >= $timeout) {
148
				fclose($fp);
149
				return NULL;
150
			}
151
			sleep(1);
152
			$trycounter++;
153
		}
154

    
155
		return $fp;
156
	}
157

    
158
	return NULL;
159
}
160

    
161
/* unlock configuration file */
162
function unlock($cfglckkey = 0) {
163
	global $g, $cfglckkeyconsumers;
164
	flock($cfglckkey, LOCK_UN);
165
	fclose($cfglckkey);
166
	return;
167
}
168

    
169
/* unlock forcefully configuration file */
170
function unlock_force($lock) {
171
	global $g;
172

    
173
	@unlink("{$g['tmp_path']}/{$lock}.lock");
174
}
175

    
176
function send_event($cmd) {
177
	global $g;
178

    
179
	if(!isset($g['event_address']))
180
		$g['event_address'] = "unix:///var/run/check_reload_status";
181

    
182
	$try = 0;
183
	while ($try < 3) {
184
		$fd = @fsockopen($g['event_address']);
185
		if ($fd) {
186
			fwrite($fd, $cmd);
187
			$resp = fread($fd, 4096);
188
			if ($resp != "OK\n")
189
				log_error("send_event: sent {$cmd} got {$resp}");
190
			fclose($fd);
191
			$try = 3;
192
		} else if (!is_process_running("check_reload_status"))
193
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
194
		$try++;
195
	}
196
}
197

    
198
function send_multiple_events($cmds) {
199
	global $g;
200

    
201
	if(!isset($g['event_address']))
202
		$g['event_address'] = "unix:///var/run/check_reload_status";
203

    
204
	if (!is_array($cmds))
205
		return;
206

    
207
	while ($try < 3) {
208
		$fd = @fsockopen($g['event_address']);
209
		if ($fd) {
210
			foreach ($cmds as $cmd) {
211
				fwrite($fd, $cmd);
212
				$resp = fread($fd, 4096);
213
				if ($resp != "OK\n")
214
					log_error("send_event: sent {$cmd} got {$resp}");
215
			}
216
			fclose($fd);
217
			$try = 3;
218
		} else if (!is_process_running("check_reload_status"))
219
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
220
		$try++;
221
	}
222
}
223

    
224
function refcount_init($reference) {
225
	$shmid = @shmop_open($reference, "c", 0644, 10);
226
	@shmop_write($shmid, str_pad("0", 10, "\x0", STR_PAD_RIGHT), 0);
227
	@shmop_close($shmid);
228
}
229

    
230
function refcount_reference($reference) {
231
	/* Take out a lock across the shared memory read, increment, write sequence to make it atomic. */
232
	$shm_lck = lock("shm{$reference}", LOCK_EX);
233
	try {
234
		/* NOTE: A warning is generated when shared memory does not exist */
235
		$shmid = @shmop_open($reference, "w", 0, 0);
236
		if (!$shmid) {
237
			refcount_init($reference);
238
			$shmid = @shmop_open($reference, "w", 0, 0);
239
			if (!$shmid) {
240
				log_error(gettext("Could not open shared memory {$reference}"));
241
				unlock($shm_lck);
242
				return;
243
			}
244
		}
245
		$shm_data = @shmop_read($shmid, 0, 10);
246
		$shm_data = intval($shm_data) + 1;
247
		@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
248
		@shmop_close($shmid);
249
		unlock($shm_lck);
250
	} catch (Exception $e) {
251
		log_error($e->getMessage());
252
		unlock($shm_lck);
253
	}
254

    
255
	return $shm_data;
256
}
257

    
258
function refcount_unreference($reference) {
259
	/* Take out a lock across the shared memory read, decrement, write sequence to make it atomic. */
260
	$shm_lck = lock("shm{$reference}", LOCK_EX);
261
	try {
262
		$shmid = @shmop_open($reference, "w", 0, 0);
263
		if (!$shmid) {
264
			refcount_init($reference);
265
			log_error(gettext("Could not open shared memory {$reference}"));
266
			unlock($shm_lck);
267
			return;
268
		}
269
		$shm_data = @shmop_read($shmid, 0, 10);
270
		$shm_data = intval($shm_data) - 1;
271
		if ($shm_data < 0) {
272
			//debug_backtrace();
273
			log_error(sprintf(gettext("Reference %s is going negative, not doing unreference."), $reference));
274
		} else
275
			@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
276
		@shmop_close($shmid);
277
		unlock($shm_lck);
278
	} catch (Exception $e) {
279
		log_error($e->getMessage());
280
		unlock($shm_lck);
281
	}
282

    
283
	return $shm_data;
284
}
285

    
286
function refcount_read($reference) {
287
	/* This function just reads the current value of the refcount for information. */
288
	/* There is no need for locking. */
289
	$shmid = @shmop_open($reference, "a", 0, 0);
290
	if (!$shmid) {
291
		log_error(gettext("Could not open shared memory for read {$reference}"));
292
		return -1;
293
	}
294
	$shm_data = @shmop_read($shmid, 0, 10);
295
	@shmop_close($shmid);
296
	return $shm_data;
297
}
298

    
299
function is_module_loaded($module_name) {
300
	$module_name = str_replace(".ko", "", $module_name);
301
	$running = 0;
302
	$_gb = exec("/sbin/kldstat -qn {$module_name} 2>&1", $_gb, $running);
303
	if (intval($running) == 0)
304
		return true;
305
	else
306
		return false;
307
}
308

    
309
/* validate non-negative numeric string, or equivalent numeric variable */
310
function is_numericint($arg) {
311
	return (((is_int($arg) && $arg >= 0) || (is_string($arg) && strlen($arg) > 0 && ctype_digit($arg))) ? true : false);  
312
}
313

    
314
/* return the subnet address given a host address and a subnet bit count */
315
function gen_subnet($ipaddr, $bits) {
316
	if (!is_ipaddr($ipaddr) || !is_numeric($bits))
317
		return "";
318
	return long2ip(ip2long($ipaddr) & gen_subnet_mask_long($bits));
319
}
320

    
321
/* return the subnet address given a host address and a subnet bit count */
322
function gen_subnetv6($ipaddr, $bits) {
323
	if (!is_ipaddrv6($ipaddr) || !is_numeric($bits))
324
		return "";
325

    
326
	$address = Net_IPv6::getNetmask($ipaddr, $bits);
327
	$address = Net_IPv6::compress($address);
328
	return $address;
329
}
330

    
331
/* return the highest (broadcast) address in the subnet given a host address and a subnet bit count */
332
function gen_subnet_max($ipaddr, $bits) {
333
	if (!is_ipaddr($ipaddr) || !is_numeric($bits))
334
		return "";
335

    
336
	return long2ip32(ip2long($ipaddr) | ~gen_subnet_mask_long($bits));
337
}
338

    
339
/* Generate end number for a given ipv6 subnet mask */
340
function gen_subnetv6_max($ipaddr, $bits) {
341
	if(!is_ipaddrv6($ipaddr))
342
		return false;
343

    
344
	$mask = Net_IPv6::getNetmask('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',$bits);
345

    
346
	$inet_ip = (binary)inet_pton($ipaddr);
347
	$inet_mask = (binary)inet_pton($mask);
348

    
349
	$inet_end = $inet_ip | ~$inet_mask;
350

    
351
	return (inet_ntop($inet_end));
352
}
353

    
354
/* returns a subnet mask (long given a bit count) */
355
function gen_subnet_mask_long($bits) {
356
	$sm = 0;
357
	for ($i = 0; $i < $bits; $i++) {
358
		$sm >>= 1;
359
		$sm |= 0x80000000;
360
	}
361
	return $sm;
362
}
363

    
364
/* same as above but returns a string */
365
function gen_subnet_mask($bits) {
366
	return long2ip(gen_subnet_mask_long($bits));
367
}
368

    
369
/* Convert long int to IP address, truncating to 32-bits. */
370
function long2ip32($ip) {
371
	return long2ip($ip & 0xFFFFFFFF);
372
}
373

    
374
/* Convert IP address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms. */
375
function ip2long32($ip) {
376
	return ( ip2long($ip) & 0xFFFFFFFF );
377
}
378

    
379
/* Convert IP address to unsigned long int. */
380
function ip2ulong($ip) {
381
	return sprintf("%u", ip2long32($ip));
382
}
383

    
384
/* Find out how many IPs are contained within a given IP range
385
 *  e.g. 192.168.0.0 to 192.168.0.255 returns 256
386
 */
387
function ip_range_size($startip, $endip) {
388
	if (is_ipaddr($startip) && is_ipaddr($endip)) {
389
		// Operate as unsigned long because otherwise it wouldn't work
390
		//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
391
		return abs(ip2ulong($startip) - ip2ulong($endip)) + 1;
392
	}
393
	return -1;
394
}
395

    
396
/* Find the smallest possible subnet mask which can contain a given number of IPs
397
 *  e.g. 512 IPs can fit in a /23, but 513 IPs need a /22
398
 */
399
function find_smallest_cidr($number) {
400
	$smallest = 1;
401
	for ($b=32; $b > 0; $b--) {
402
		$smallest = ($number <= pow(2,$b)) ? $b : $smallest;
403
	}
404
	return (32-$smallest);
405
}
406

    
407
/* Return the previous IP address before the given address */
408
function ip_before($ip) {
409
	return long2ip32(ip2long($ip)-1);
410
}
411

    
412
/* Return the next IP address after the given address */
413
function ip_after($ip) {
414
	return long2ip32(ip2long($ip)+1);
415
}
416

    
417
/* Return true if the first IP is 'before' the second */
418
function ip_less_than($ip1, $ip2) {
419
	// Compare as unsigned long because otherwise it wouldn't work when
420
	//   crossing over from 127.255.255.255 / 128.0.0.0 barrier
421
	return ip2ulong($ip1) < ip2ulong($ip2);
422
}
423

    
424
/* Return true if the first IP is 'after' the second */
425
function ip_greater_than($ip1, $ip2) {
426
	// Compare as unsigned long because otherwise it wouldn't work
427
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
428
	return ip2ulong($ip1) > ip2ulong($ip2);
429
}
430

    
431
/* Convert a range of IPs to an array of subnets which can contain the range. */
432
function ip_range_to_subnet_array($startip, $endip) {
433
	if (!is_ipaddr($startip) || !is_ipaddr($endip)) {
434
		return array();
435
	}
436

    
437
	// Container for subnets within this range.
438
	$rangesubnets = array();
439

    
440
	// Figure out what the smallest subnet is that holds the number of IPs in the given range.
441
	$cidr = find_smallest_cidr(ip_range_size($startip, $endip));
442

    
443
	// Loop here to reduce subnet size and retest as needed. We need to make sure
444
	//   that the target subnet is wholly contained between $startip and $endip.
445
	for ($cidr; $cidr <= 32; $cidr++) {
446
		// Find the network and broadcast addresses for the subnet being tested.
447
		$targetsub_min = gen_subnet($startip, $cidr);
448
		$targetsub_max = gen_subnet_max($startip, $cidr);
449

    
450
		// Check best case where the range is exactly one subnet.
451
		if (($targetsub_min == $startip) && ($targetsub_max == $endip)) {
452
			// Hooray, the range is exactly this subnet!
453
			return array("{$startip}/{$cidr}");
454
		}
455

    
456
		// These remaining scenarios will find a subnet that uses the largest
457
		//  chunk possible of the range being tested, and leave the rest to be
458
		//  tested recursively after the loop.
459

    
460
		// Check if the subnet begins with $startip and ends before $endip
461
		if (($targetsub_min == $startip) && ip_less_than($targetsub_max, $endip)) {
462
			break;
463
		}
464

    
465
		// Check if the subnet ends at $endip and starts after $startip
466
		if (ip_greater_than($targetsub_min, $startip) && ($targetsub_max == $endip)) {
467
			break;
468
		}
469

    
470
		// Check if the subnet is between $startip and $endip
471
		if (ip_greater_than($targetsub_min, $startip) && ip_less_than($targetsub_max, $endip)) {
472
			break;
473
		}
474
	}
475

    
476
	// Some logic that will recursivly search from $startip to the first IP before the start of the subnet we just found.
477
	// NOTE: This may never be hit, the way the above algo turned out, but is left for completeness.
478
	if ($startip != $targetsub_min) {
479
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array($startip, ip_before($targetsub_min)));
480
	}
481

    
482
	// Add in the subnet we found before, to preserve ordering
483
	$rangesubnets[] = "{$targetsub_min}/{$cidr}";
484

    
485
	// And some more logic that will search after the subnet we found to fill in to the end of the range.
486
	if ($endip != $targetsub_max) {
487
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array(ip_after($targetsub_max), $endip));
488
	}
489
	return $rangesubnets;
490
}
491

    
492
function is_iprange($range) {
493
	if (substr_count($range, '-') != 1) {
494
		return false;
495
	}
496
	list($ip1, $ip2) = explode ('-', $range);
497
	return (is_ipaddr($ip1) && is_ipaddr($ip2));
498
}
499

    
500
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6 */
501
function is_ipaddr($ipaddr) {
502
	if(is_ipaddrv4($ipaddr)) {
503
		return true;
504
	}
505
	if(is_ipaddrv6($ipaddr)) {
506
		return true;
507
	}
508
	return false;
509
}
510

    
511
/* returns true if $ipaddr is a valid IPv6 address */
512
function is_ipaddrv6($ipaddr) {
513
	if (!is_string($ipaddr) || empty($ipaddr))
514
		return false;
515
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
516
		$tmpip = explode("%", $ipaddr);
517
		$ipaddr = $tmpip[0];
518
	}
519
	return Net_IPv6::checkIPv6($ipaddr);
520
}
521

    
522
/* returns true if $ipaddr is a valid dotted IPv4 address */
523
function is_ipaddrv4($ipaddr) {
524
	if (!is_string($ipaddr) || empty($ipaddr))
525
		return false;
526

    
527
	$ip_long = ip2long($ipaddr);
528
	$ip_reverse = long2ip32($ip_long);
529

    
530
	if ($ipaddr == $ip_reverse)
531
		return true;
532
	else
533
		return false;
534
}
535

    
536
/* returns true if $ipaddr is a valid linklocal address */
537
function is_linklocal($ipaddr) {
538
	return (strtolower(substr($ipaddr, 0, 5)) == "fe80:");
539
}
540

    
541
/* returns scope of a linklocal address */
542
function get_ll_scope($addr) {
543
	if (!is_linklocal($addr) || !strstr($addr, "%"))
544
		return "";
545
	list ($ll, $scope) = explode("%", $addr);
546
	return $scope;
547
}
548

    
549
/* returns true if $ipaddr is a valid literal IPv6 address */
550
function is_literalipaddrv6($ipaddr) {
551
	if(preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match))
552
		$ipaddr = $match[1];
553
	else
554
		return false;
555

    
556
	return is_ipaddrv6($ipaddr);
557
}
558

    
559
function is_ipaddrwithport($ipport) {
560
	$parts = explode(":", $ipport);
561
	$port = array_pop($parts);
562
	if (count($parts) == 1) {
563
		return is_ipaddrv4($parts[0]) && is_port($port);
564
	} elseif (count($parts) > 1) {
565
		return is_literalipaddrv6(implode(":", $parts)) && is_port($port);
566
	} else {
567
		return false;
568
	}
569
}
570

    
571
function is_hostnamewithport($hostport) {
572
	$parts = explode(":", $hostport);
573
	$port = array_pop($parts);
574
	if (count($parts) == 1) {
575
		return is_hostname($parts[0]) && is_port($port);
576
	} else {
577
		return false;
578
	}
579
}
580

    
581
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
582
function is_ipaddroralias($ipaddr) {
583
	global $config;
584

    
585
	if (is_alias($ipaddr)) {
586
		if (is_array($config['aliases']['alias'])) {
587
			foreach ($config['aliases']['alias'] as $alias) {
588
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type']))
589
					return true;
590
			}
591
		}
592
		return false;
593
	} else
594
		return is_ipaddr($ipaddr);
595

    
596
}
597

    
598
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format
599
   	false - if not a valid subnet
600
   	true (numeric 4 or 6) - if valid, gives type of subnet */
601
function is_subnet($subnet) {
602
	if (is_string($subnet) && preg_match('/^(?:([0-9.]{7,15})|([0-9a-f:]{2,39}))\/(\d{1,3})$/i', $subnet, $parts)) {
603
		if (is_ipaddrv4($parts[1]) && $parts[3] <= 32)
604
			return 4;
605
		if (is_ipaddrv6($parts[2]) && $parts[3] <= 128)
606
			return 6;
607
	}
608
	return false;
609
}
610

    
611
/* same as is_subnet() but accepts IPv4 only */
612
function is_subnetv4($subnet) {
613
	return (is_subnet($subnet) == 4);
614
}
615

    
616
/* same as is_subnet() but accepts IPv6 only */
617
function is_subnetv6($subnet) {
618
	return (is_subnet($subnet) == 6);
619
}
620

    
621
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
622
function is_subnetoralias($subnet) {
623
	global $aliastable;
624

    
625
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet]))
626
		return true;
627
	else
628
		return is_subnet($subnet);
629
}
630

    
631
/* returns true if $hostname is a valid hostname */
632
function is_hostname($hostname) {
633
	if (!is_string($hostname))
634
		return false;
635

    
636
	if (preg_match('/^(?:(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])\.)*(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname))
637
		return true;
638
	else
639
		return false;
640
}
641

    
642
/* returns true if $domain is a valid domain name */
643
function is_domain($domain) {
644
	if (!is_string($domain))
645
		return false;
646

    
647
	if (preg_match('/^(?:(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])$/i', $domain))
648
		return true;
649
	else
650
		return false;
651
}
652

    
653
/* returns true if $macaddr is a valid MAC address */
654
function is_macaddr($macaddr, $partial=false) {
655
	$repeat = ($partial) ? '1,5' : '5';
656
	return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false;
657
}
658

    
659
/* returns true if $name is a valid name for an alias
660
   returns NULL if a reserved word is used
661
   returns FALSE for bad chars in the name - this allows calling code to determine what the problem was.
662
   aliases cannot be:
663
   	bad chars: anything except a-z 0-9 and underscore
664
   	bad names: empty string, pure numeric, pure underscore
665
   	reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and  "pass" */
666

    
667
function is_validaliasname($name) {
668
	/* Array of reserved words */
669
	$reserved = array("port", "pass");
670

    
671
	if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name))
672
		return false;
673
	if (in_array($name, $reserved, true) || getservbyname($name, "tcp") || getservbyname($name, "udp") || getprotobyname($name))
674
		return; /* return NULL */
675
	return true;
676
}
677

    
678
/* returns true if $port is a valid TCP/UDP port */
679
function is_port($port) {
680
	if (getservbyname($port, "tcp") || getservbyname($port, "udp"))
681
		return true;
682
	if (!ctype_digit($port))
683
		return false;
684
	else if ((intval($port) < 1) || (intval($port) > 65535))
685
		return false;
686
	return true;
687
}
688

    
689
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
690
function is_portrange($portrange) {
691
	$ports = explode(":", $portrange);
692

    
693
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
694
}
695

    
696
/* returns true if $port is a valid port number or an alias thereof */
697
function is_portoralias($port) {
698
	global $config;
699

    
700
	if (is_alias($port)) {
701
		if (is_array($config['aliases']['alias'])) {
702
			foreach ($config['aliases']['alias'] as $alias) {
703
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type']))
704
					return true;
705
				}
706
			}
707
			return false;
708
	} else
709
		return is_port($port);
710
}
711

    
712
/* create ranges of sequential port numbers (200:215) and remove duplicates */
713
function group_ports($ports) {
714
	if (!is_array($ports) || empty($ports))
715
		return;
716

    
717
	$uniq = array();
718
	foreach ($ports as $port) {
719
		if (is_portrange($port)) {
720
			list($begin, $end) = explode(":", $port);
721
			if ($begin > $end) {
722
				$aux = $begin;
723
				$begin = $end;
724
				$end = $aux;
725
			}
726
			for ($i = $begin; $i <= $end; $i++)
727
				if (!in_array($i, $uniq))
728
					$uniq[] = $i;
729
		} else if (is_port($port)) {
730
			if (!in_array($port, $uniq))
731
				$uniq[] = $port;
732
		}
733
	}
734
	sort($uniq, SORT_NUMERIC);
735

    
736
	$result = array();
737
	foreach ($uniq as $idx => $port) {
738
		if ($idx == 0) {
739
			$result[] = $port;
740
			continue;
741
		}
742

    
743
		$last = end($result);
744
		if (is_portrange($last))
745
			list($begin, $end) = explode(":", $last);
746
		else
747
			$begin = $end = $last;
748

    
749
		if ($port == ($end+1)) {
750
			$end++;
751
			$result[count($result)-1] = "{$begin}:{$end}";
752
		} else {
753
			$result[] = $port;
754
		}
755
	}
756

    
757
	return $result;
758
}
759

    
760
/* returns true if $val is a valid shaper bandwidth value */
761
function is_valid_shaperbw($val) {
762
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
763
}
764

    
765
/* returns true if $test is in the range between $start and $end */
766
function is_inrange_v4($test, $start, $end) {
767
	if ( (ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start)) )
768
		return true;
769
	else
770
		return false;
771
}
772

    
773
/* returns true if $test is in the range between $start and $end */
774
function is_inrange_v6($test, $start, $end) {
775
	if ( (inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start)) )
776
		return true;
777
	else
778
		return false;
779
}
780

    
781
/* returns true if $test is in the range between $start and $end */
782
function is_inrange($test, $start, $end) {
783
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
784
}
785

    
786
/* XXX: return the configured carp interface list */
787
function get_configured_carp_interface_list($carpinterface = "", $family = "inet") {
788
	global $config;
789

    
790
	$iflist = array();
791

    
792
	if(is_array($config['virtualip']['vip'])) {
793
		$viparr = &$config['virtualip']['vip'];
794
		foreach ($viparr as $vip) {
795
			switch ($vip['mode']) {
796
			case "carp":
797
				if (!empty($carpinterface)) {
798
					if ($carpinterface == "{$vip['interface']}_vip{$vip['vhid']}") {
799
						if ($family == "inet" && is_ipaddrv4($vip['subnet']))
800
							return $vip['subnet'];
801
						else if ($family == "inet6" && is_ipaddrv6($vip['subnet']))
802
							return $vip['subnet'];
803
					}
804
				} else {
805
					$iflist["{$vip['interface']}_vip{$vip['vhid']}"] = $vip['subnet'];
806
				}
807
				break;
808
			}
809
		}
810
	}
811

    
812
	return $iflist;
813
}
814

    
815
/* return the configured IP aliases list */
816
function get_configured_ip_aliases_list($returnfullentry = false) {
817
	global $config;
818

    
819
	$alias_list=array();
820

    
821
	if(is_array($config['virtualip']['vip'])) {
822
		$viparr = &$config['virtualip']['vip'];
823
		foreach ($viparr as $vip) {
824
			if ($vip['mode']=="ipalias") {
825
				if ($returnfullentry)
826
					$alias_list[$vip['subnet']] = $vip;
827
				else
828
					$alias_list[$vip['subnet']] = $vip['interface'];
829
			}
830
		}
831
	}
832

    
833
	return $alias_list;
834
}
835

    
836
/* return all configured aliases list (IP, carp, proxyarp and other) */
837
function get_configured_vips_list() {
838
	global $config;
839

    
840
	$alias_list=array();
841

    
842
	if(is_array($config['virtualip']['vip'])) {
843
		$viparr = &$config['virtualip']['vip'];
844
		foreach ($viparr as $vip) {
845
			if ($vip['mode'] == "carp")
846
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => "{$vip['interface']}_vip{$vip['vhid']}");
847
			else
848
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
849
		}
850
	}
851

    
852
	return $alias_list;
853
}
854

    
855
/* comparison function for sorting by the order in which interfaces are normally created */
856
function compare_interface_friendly_names($a, $b) {
857
	if ($a == $b)
858
		return 0;
859
	else if ($a == 'wan')
860
		return -1;
861
	else if ($b == 'wan')
862
		return 1;
863
	else if ($a == 'lan')
864
		return -1;
865
	else if ($b == 'lan')
866
		return 1;
867

    
868
	return strnatcmp($a, $b);
869
}
870

    
871
/* return the configured interfaces list. */
872
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
873
	global $config;
874

    
875
	$iflist = array();
876

    
877
	/* if list */
878
	foreach($config['interfaces'] as $if => $ifdetail) {
879
		if ($only_opt && ($if == "wan" || $if == "lan"))
880
			continue;
881
		if (isset($ifdetail['enable']) || $withdisabled == true)
882
			$iflist[$if] = $if;
883
	}
884

    
885
	return $iflist;
886
}
887

    
888
/* return the configured interfaces list. */
889
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
890
	global $config;
891

    
892
	$iflist = array();
893

    
894
	/* if list */
895
	foreach($config['interfaces'] as $if => $ifdetail) {
896
		if ($only_opt && ($if == "wan" || $if == "lan"))
897
			continue;
898
		if (isset($ifdetail['enable']) || $withdisabled == true) {
899
			$tmpif = get_real_interface($if);
900
			if (!empty($tmpif))
901
				$iflist[$tmpif] = $if;
902
		}
903
	}
904

    
905
	return $iflist;
906
}
907

    
908
/* return the configured interfaces list with their description. */
909
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
910
	global $config;
911

    
912
	$iflist = array();
913

    
914
	/* if list */
915
	foreach($config['interfaces'] as $if => $ifdetail) {
916
		if ($only_opt && ($if == "wan" || $if == "lan"))
917
			continue;
918
		if (isset($ifdetail['enable']) || $withdisabled == true) {
919
			if(empty($ifdetail['descr']))
920
				$iflist[$if] = strtoupper($if);
921
			else
922
				$iflist[$if] = strtoupper($ifdetail['descr']);
923
		}
924
	}
925

    
926
	return $iflist;
927
}
928

    
929
/*
930
 *   get_configured_ip_addresses() - Return a list of all configured
931
 *   interfaces IP Addresses
932
 *
933
 */
934
function get_configured_ip_addresses() {
935
	global $config;
936

    
937
	if (!function_exists('get_interface_ip'))
938
		require_once("interfaces.inc");
939
	$ip_array = array();
940
	$interfaces = get_configured_interface_list();
941
	if (is_array($interfaces)) {
942
		foreach($interfaces as $int) {
943
			$ipaddr = get_interface_ip($int);
944
			$ip_array[$int] = $ipaddr;
945
		}
946
	}
947
	$interfaces = get_configured_carp_interface_list();
948
	if (is_array($interfaces))
949
		foreach($interfaces as $int => $ipaddr)
950
			$ip_array[$int] = $ipaddr;
951

    
952
	/* pppoe server */
953
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
954
		foreach($config['pppoes']['pppoe'] as $pppoe) {
955
			if ($pppoe['mode'] == "server") {
956
				if(is_ipaddr($pppoe['localip'])) {
957
					$int = "pppoes". $pppoe['pppoeid'];
958
					$ip_array[$int] = $pppoe['localip'];
959
				}
960
			}
961
		}
962
	}
963

    
964
	return $ip_array;
965
}
966

    
967
/*
968
 *   get_configured_ipv6_addresses() - Return a list of all configured
969
 *   interfaces IPv6 Addresses
970
 *
971
 */
972
function get_configured_ipv6_addresses() {
973
	require_once("interfaces.inc");
974
	$ipv6_array = array();
975
	$interfaces = get_configured_interface_list();
976
	if(is_array($interfaces)) {
977
		foreach($interfaces as $int) {
978
			$ipaddrv6 = get_interface_ipv6($int);
979
			$ipv6_array[$int] = $ipaddrv6;
980
		}
981
	}
982
	$interfaces = get_configured_carp_interface_list();
983
	if(is_array($interfaces))
984
		foreach($interfaces as $int => $ipaddrv6)
985
			$ipv6_array[$int] = $ipaddrv6;
986
	return $ipv6_array;
987
}
988

    
989
/*
990
 *   get_interface_list() - Return a list of all physical interfaces
991
 *   along with MAC and status.
992
 *
993
 *   $mode = "active" - use ifconfig -lu
994
 *           "media"  - use ifconfig to check physical connection
995
 *			status (much slower)
996
 */
997
function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") {
998
	global $config;
999
	$upints = array();
1000
	/* get a list of virtual interface types */
1001
	if(!$vfaces) {
1002
		$vfaces = array (
1003
				'bridge',
1004
				'ppp',
1005
				'pppoe',
1006
				'pptp',
1007
				'l2tp',
1008
				'sl',
1009
				'gif',
1010
				'gre',
1011
				'faith',
1012
				'lo',
1013
				'ng',
1014
				'_vlan',
1015
				'_wlan',
1016
				'pflog',
1017
				'plip',
1018
				'pfsync',
1019
				'enc',
1020
				'tun',
1021
				'carp',
1022
				'lagg',
1023
				'vip',
1024
				'ipfw'
1025
		);
1026
	}
1027
	switch($mode) {
1028
	case "active":
1029
		$upints = pfSense_interface_listget(IFF_UP);
1030
		break;
1031
	case "media":
1032
		$intlist = pfSense_interface_listget();
1033
		$ifconfig = "";
1034
		exec("/sbin/ifconfig -a", $ifconfig);
1035
		$regexp = '/(' . implode('|', $intlist) . '):\s/';
1036
		$ifstatus = preg_grep('/status:/', $ifconfig);
1037
		foreach($ifstatus as $status) {
1038
			$int = array_shift($intlist);
1039
			if(stristr($status, "active")) $upints[] = $int;
1040
		}
1041
		break;
1042
	default:
1043
		$upints = pfSense_interface_listget();
1044
		break;
1045
	}
1046
	/* build interface list with netstat */
1047
	$linkinfo = "";
1048
	exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo);
1049
	array_shift($linkinfo);
1050
	/* build ip address list with netstat */
1051
	$ipinfo = "";
1052
	exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo);
1053
	array_shift($ipinfo);
1054
	foreach($linkinfo as $link) {
1055
		$friendly = "";
1056
		$alink = explode(" ", $link);
1057
		$ifname = rtrim(trim($alink[0]), '*');
1058
		/* trim out all numbers before checking for vfaces */
1059
		if (!in_array(array_shift(preg_split('/\d/', $ifname)), $vfaces) &&
1060
			!stristr($ifname, "_vlan") && !stristr($ifname, "_wlan")) {
1061
			$toput = array(
1062
					"mac" => trim($alink[1]),
1063
					"up" => in_array($ifname, $upints)
1064
				);
1065
			foreach($ipinfo as $ip) {
1066
				$aip = explode(" ", $ip);
1067
				if($aip[0] == $ifname) {
1068
					$toput['ipaddr'] = $aip[1];
1069
				}
1070
			}
1071
			if (is_array($config['interfaces'])) {
1072
				foreach($config['interfaces'] as $name => $int)
1073
					if($int['if'] == $ifname) $friendly = $name;
1074
			}
1075
			switch($keyby) {
1076
			case "physical":
1077
				if($friendly != "") {
1078
					$toput['friendly'] = $friendly;
1079
				}
1080
				$dmesg_arr = array();
1081
				exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr);
1082
				preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg);
1083
				$toput['dmesg'] = $dmesg[1][0];
1084
				$iflist[$ifname] = $toput;
1085
				break;
1086
			case "ppp":
1087

    
1088
			case "friendly":
1089
				if($friendly != "") {
1090
					$toput['if'] = $ifname;
1091
					$iflist[$friendly] = $toput;
1092
				}
1093
				break;
1094
			}
1095
		}
1096
	}
1097
	return $iflist;
1098
}
1099

    
1100
/****f* util/log_error
1101
* NAME
1102
*   log_error  - Sends a string to syslog.
1103
* INPUTS
1104
*   $error     - string containing the syslog message.
1105
* RESULT
1106
*   null
1107
******/
1108
function log_error($error) {
1109
	global $g;
1110
	$page = $_SERVER['SCRIPT_NAME'];
1111
	if (empty($page)) {
1112
		$files = get_included_files();
1113
		$page = basename($files[0]);
1114
	}
1115
	syslog(LOG_ERR, "$page: $error");
1116
	if ($g['debug'])
1117
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1118
	return;
1119
}
1120

    
1121
/****f* util/log_auth
1122
* NAME
1123
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1124
* INPUTS
1125
*   $error     - string containing the syslog message.
1126
* RESULT
1127
*   null
1128
******/
1129
function log_auth($error) {
1130
	global $g;
1131
	$page = $_SERVER['SCRIPT_NAME'];
1132
	syslog(LOG_AUTH, "$page: $error");
1133
	if ($g['debug'])
1134
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1135
	return;
1136
}
1137

    
1138
/****f* util/exec_command
1139
 * NAME
1140
 *   exec_command - Execute a command and return a string of the result.
1141
 * INPUTS
1142
 *   $command   - String of the command to be executed.
1143
 * RESULT
1144
 *   String containing the command's result.
1145
 * NOTES
1146
 *   This function returns the command's stdout and stderr.
1147
 ******/
1148
function exec_command($command) {
1149
	$output = array();
1150
	exec($command . ' 2>&1', $output);
1151
	return(implode("\n", $output));
1152
}
1153

    
1154
/* wrapper for exec() */
1155
function mwexec($command, $mute = false, $clearsigmask = false) {
1156
	global $g;
1157

    
1158
	if ($g['debug']) {
1159
		if (!$_SERVER['REMOTE_ADDR'])
1160
			echo "mwexec(): $command\n";
1161
	}
1162
	$oarr = array();
1163
	$retval = 0;
1164

    
1165
	if ($clearsigmask) {
1166
		$oldset = array();
1167
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1168
	}
1169
	$garbage = exec("$command 2>&1", $oarr, $retval);
1170
	if ($clearsigmask) {
1171
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1172
	}
1173

    
1174
	if(isset($config['system']['developerspew']))
1175
		$mute = false;
1176
	if(($retval <> 0) && ($mute === false)) {
1177
		$output = implode(" ", $oarr);
1178
		log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, $output));
1179
		unset($output);
1180
	}
1181
	unset($oarr);
1182
	return $retval;
1183
}
1184

    
1185
/* wrapper for exec() in background */
1186
function mwexec_bg($command, $clearsigmask = false) {
1187
	global $g;
1188

    
1189
	if ($g['debug']) {
1190
		if (!$_SERVER['REMOTE_ADDR'])
1191
			echo "mwexec(): $command\n";
1192
	}
1193

    
1194
	if ($clearsigmask) {
1195
		$oldset = array();
1196
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1197
	}
1198
	$_gb = exec("/usr/bin/nohup $command > /dev/null 2>&1 &");
1199
	if ($clearsigmask) {
1200
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1201
	}
1202
	unset($_gb);
1203
}
1204

    
1205
/* unlink a file, if it exists */
1206
function unlink_if_exists($fn) {
1207
	$to_do = glob($fn);
1208
	if(is_array($to_do)) {
1209
		foreach($to_do as $filename)
1210
			@unlink($filename);
1211
	} else {
1212
		@unlink($fn);
1213
	}
1214
}
1215
/* make a global alias table (for faster lookups) */
1216
function alias_make_table($config) {
1217
	global $aliastable;
1218

    
1219
	$aliastable = array();
1220

    
1221
	if (is_array($config['aliases']['alias'])) {
1222
		foreach ($config['aliases']['alias'] as $alias) {
1223
			if ($alias['name'])
1224
				$aliastable[$alias['name']] = $alias['address'];
1225
		}
1226
	}
1227
}
1228

    
1229
/* check if an alias exists */
1230
function is_alias($name) {
1231
	global $aliastable;
1232

    
1233
	return isset($aliastable[$name]);
1234
}
1235

    
1236
function alias_get_type($name) {
1237
	global $config;
1238

    
1239
	if (is_array($config['aliases']['alias'])) {
1240
		foreach ($config['aliases']['alias'] as $alias) {
1241
			if ($name == $alias['name'])
1242
				return $alias['type'];
1243
		}
1244
	}
1245

    
1246
	return "";
1247
}
1248

    
1249
/* expand a host or network alias, if necessary */
1250
function alias_expand($name) {
1251
	global $aliastable;
1252

    
1253
	if (isset($aliastable[$name]))
1254
		return "\${$name}";
1255
	else if (is_ipaddr($name) || is_subnet($name) || is_port($name) || is_portrange($name))
1256
		return "{$name}";
1257
	else
1258
		return null;
1259
}
1260

    
1261
function alias_expand_urltable($name) {
1262
	global $config;
1263
	$urltable_prefix = "/var/db/aliastables/";
1264
	$urltable_filename = $urltable_prefix . $name . ".txt";
1265

    
1266
	if (is_array($config['aliases']['alias'])) {
1267
		foreach ($config['aliases']['alias'] as $alias) {
1268
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1269
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename))
1270
					return $urltable_filename;
1271
				else if (process_alias_urltable($name, $alias["url"], 0, true))
1272
					return $urltable_filename;
1273
			}
1274
		}
1275
	}
1276
	return null;
1277
}
1278

    
1279
function subnet_size($subnet) {
1280
	if (is_subnetv4($subnet)) {
1281
		list ($ip, $bits) = explode("/", $subnet);
1282
		return round(exp(log(2) * (32 - $bits)));
1283
	}
1284
	else if (is_subnetv6($subnet)) {
1285
		list ($ip, $bits) = explode("/", $subnet);
1286
		return round(exp(log(2) * (128 - $bits)));
1287
	}
1288
	else {
1289
		return 0;
1290
	}
1291
}
1292

    
1293
function subnet_expand($subnet) {
1294
	if (is_subnetv4($subnet)) {
1295
		return subnetv4_expand($subnet);
1296
	} else if (is_subnetv6($subnet)) {
1297
		return subnetv6_expand($subnet);
1298
	} else {
1299
		return $subnet;
1300
	}
1301
}
1302

    
1303
function subnetv4_expand($subnet) {
1304
	$result = array();
1305
	list ($ip, $bits) = explode("/", $subnet);
1306
	$net  = ip2long($ip);
1307
	$mask = (0xffffffff << (32 - $bits));
1308
	$net &= $mask;
1309
	$size = round(exp(log(2) * (32 - $bits)));
1310
	for ($i = 0; $i < $size; $i += 1) {
1311
		$result[] = long2ip($net | $i);
1312
	}
1313
	return $result;
1314
}
1315

    
1316
/* find out whether two subnets overlap */
1317
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
1318

    
1319
	if (!is_numeric($bits1))
1320
		$bits1 = 32;
1321
	if (!is_numeric($bits2))
1322
		$bits2 = 32;
1323

    
1324
	if ($bits1 < $bits2)
1325
		$relbits = $bits1;
1326
	else
1327
		$relbits = $bits2;
1328

    
1329
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
1330
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
1331

    
1332
	return ($sn1 == $sn2);
1333
}
1334

    
1335
/* find out whether two IPv6 subnets overlap */
1336
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
1337
	$sub1_min = gen_subnetv6($subnet1, $bits1);
1338
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
1339
	$sub2_min = gen_subnetv6($subnet2, $bits2);
1340
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
1341

    
1342
	return (is_inrange_v6($sub1_min, $sub2_min, $sub2_max) || is_inrange_v6($sub1_max, $sub2_min, $sub2_max) || is_inrange_v6($sub2_min, $sub1_min, $sub1_max));
1343
}
1344

    
1345
/* compare two IP addresses */
1346
function ipcmp($a, $b) {
1347
	if (ip_less_than($a, $b))
1348
		return -1;
1349
	else if (ip_greater_than($a, $b))
1350
		return 1;
1351
	else
1352
		return 0;
1353
}
1354

    
1355
/* return true if $addr is in $subnet, false if not */
1356
function ip_in_subnet($addr,$subnet) {
1357
	if(is_ipaddrv6($addr)) {
1358
		return (Net_IPv6::isInNetmask($addr, $subnet));
1359
	} else { /* XXX: Maybe check for IPv4 */
1360
		list($ip, $mask) = explode('/', $subnet);
1361
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
1362
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
1363
	}
1364
}
1365

    
1366
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1367
function verify_digital_signature($fname) {
1368
	global $g;
1369

    
1370
	if(!file_exists("/usr/local/sbin/gzsig"))
1371
		return 4;
1372

    
1373
	return mwexec("/usr/local/sbin/gzsig verify {$g['etc_path']}/pubkey.pem < " . escapeshellarg($fname));
1374
}
1375

    
1376
/* obtain MAC address given an IP address by looking at the ARP table */
1377
function arp_get_mac_by_ip($ip) {
1378
	mwexec("/sbin/ping -c 1 -t 1 " . escapeshellarg($ip), true);
1379
	$arpoutput = "";
1380
	exec("/usr/sbin/arp -n " . escapeshellarg($ip), $arpoutput);
1381

    
1382
	if ($arpoutput[0]) {
1383
		$arpi = explode(" ", $arpoutput[0]);
1384
		$macaddr = $arpi[3];
1385
		if (is_macaddr($macaddr))
1386
			return $macaddr;
1387
		else
1388
			return false;
1389
	}
1390

    
1391
	return false;
1392
}
1393

    
1394
/* return a fieldname that is safe for xml usage */
1395
function xml_safe_fieldname($fieldname) {
1396
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1397
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1398
			 ':', ',', '.', '\'', '\\'
1399
		);
1400
	return strtolower(str_replace($replace, "", $fieldname));
1401
}
1402

    
1403
function mac_format($clientmac) {
1404
	global $config, $cpzone;
1405

    
1406
	$mac = explode(":", $clientmac);
1407
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1408

    
1409
	switch($mac_format) {
1410
	case 'singledash':
1411
		return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1412

    
1413
	case 'ietf':
1414
		return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1415

    
1416
	case 'cisco':
1417
		return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1418

    
1419
	case 'unformatted':
1420
		return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1421

    
1422
	default:
1423
		return $clientmac;
1424
	}
1425
}
1426

    
1427
function resolve_retry($hostname, $retries = 5) {
1428

    
1429
	if (is_ipaddr($hostname))
1430
		return $hostname;
1431

    
1432
	for ($i = 0; $i < $retries; $i++) {
1433
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1434
		$ip = gethostbyname($hostname);
1435

    
1436
		if ($ip && $ip != $hostname) {
1437
			/* success */
1438
			return $ip;
1439
		}
1440

    
1441
		sleep(1);
1442
	}
1443

    
1444
	return false;
1445
}
1446

    
1447
function format_bytes($bytes) {
1448
	if ($bytes >= 1073741824) {
1449
		return sprintf("%.2f GB", $bytes/1073741824);
1450
	} else if ($bytes >= 1048576) {
1451
		return sprintf("%.2f MB", $bytes/1048576);
1452
	} else if ($bytes >= 1024) {
1453
		return sprintf("%.0f KB", $bytes/1024);
1454
	} else {
1455
		return sprintf("%d bytes", $bytes);
1456
	}
1457
}
1458

    
1459
function update_filter_reload_status($text) {
1460
	global $g;
1461

    
1462
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1463
}
1464

    
1465
/****** util/return_dir_as_array
1466
 * NAME
1467
 *   return_dir_as_array - Return a directory's contents as an array.
1468
 * INPUTS
1469
 *   $dir          - string containing the path to the desired directory.
1470
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1471
 * RESULT
1472
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1473
 ******/
1474
function return_dir_as_array($dir, $filter_regex = '') {
1475
	$dir_array = array();
1476
	if (is_dir($dir)) {
1477
		if ($dh = opendir($dir)) {
1478
			while (($file = readdir($dh)) !== false) {
1479
				if (($file == ".") || ($file == ".."))
1480
					continue;
1481

    
1482
				if (empty($filter_regex) || preg_match($filter_regex, $file))
1483
					array_push($dir_array, $file);
1484
			}
1485
			closedir($dh);
1486
		}
1487
	}
1488
	return $dir_array;
1489
}
1490

    
1491
function run_plugins($directory) {
1492
	global $config, $g;
1493

    
1494
	/* process packager manager custom rules */
1495
	$files = return_dir_as_array($directory);
1496
	if (is_array($files)) {
1497
		foreach ($files as $file) {
1498
			if (stristr($file, ".sh") == true)
1499
				mwexec($directory . $file . " start");
1500
			else if (!is_dir($directory . "/" . $file) && stristr($file,".inc"))
1501
				require_once($directory . "/" . $file);
1502
		}
1503
	}
1504
}
1505

    
1506
/*
1507
 *    safe_mkdir($path, $mode = 0755)
1508
 *    create directory if it doesn't already exist and isn't a file!
1509
 */
1510
function safe_mkdir($path, $mode=0755) {
1511
	global $g;
1512

    
1513
	if (!is_file($path) && !is_dir($path)) {
1514
		return @mkdir($path, $mode, true);
1515
	} else {
1516
		return false;
1517
	}
1518
}
1519

    
1520
/*
1521
 * make_dirs($path, $mode = 0755)
1522
 * create directory tree recursively (mkdir -p)
1523
 */
1524
function make_dirs($path, $mode = 0755) {
1525
	$base = '';
1526
	foreach (explode('/', $path) as $dir) {
1527
		$base .= "/$dir";
1528
		if (!is_dir($base)) {
1529
			if (!@mkdir($base, $mode))
1530
				return false;
1531
		}
1532
	}
1533
	return true;
1534
}
1535

    
1536
/*
1537
 * get_sysctl($names)
1538
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1539
 * name) and return an array of key/value pairs set for those that exist
1540
 */
1541
function get_sysctl($names) {
1542
	if (empty($names))
1543
		return array();
1544

    
1545
	if (is_array($names)) {
1546
		$name_list = array();
1547
		foreach ($names as $name) {
1548
			$name_list[] = escapeshellarg($name);
1549
		}
1550
	} else
1551
		$name_list = array(escapeshellarg($names));
1552

    
1553
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1554
	$values = array();
1555
	foreach ($output as $line) {
1556
		$line = explode(": ", $line, 2);
1557
		if (count($line) == 2)
1558
			$values[$line[0]] = $line[1];
1559
	}
1560

    
1561
	return $values;
1562
}
1563

    
1564
/*
1565
 * get_single_sysctl($name)
1566
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
1567
 * return the value for sysctl $name or empty string if it doesn't exist
1568
 */
1569
function get_single_sysctl($name) {
1570
	if (empty($name))
1571
		return "";
1572

    
1573
	$value = get_sysctl($name);
1574
	if (empty($value) || !isset($value[$name]))
1575
		return "";
1576

    
1577
	return $value[$name];
1578
}
1579

    
1580
/*
1581
 * set_sysctl($value_list)
1582
 * Set sysctl OID's listed as key/value pairs and return
1583
 * an array with keys set for those that succeeded
1584
 */
1585
function set_sysctl($values) {
1586
	if (empty($values))
1587
		return array();
1588

    
1589
	$value_list = array();
1590
	foreach ($values as $key => $value) {
1591
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1592
	}
1593

    
1594
	exec("/sbin/sysctl -i " . implode(" ", $value_list), $output, $success);
1595

    
1596
	/* Retry individually if failed (one or more read-only) */
1597
	if ($success <> 0 && count($value_list) > 1) {
1598
		foreach ($value_list as $value) {
1599
			exec("/sbin/sysctl -i " . $value, $output);
1600
		}
1601
	}
1602

    
1603
	$ret = array();
1604
	foreach ($output as $line) {
1605
		$line = explode(": ", $line, 2);
1606
		if (count($line) == 2)
1607
			$ret[$line[0]] = true;
1608
	}
1609

    
1610
	return $ret;
1611
}
1612

    
1613
/*
1614
 * set_single_sysctl($name, $value)
1615
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
1616
 * returns boolean meaning if it suceed
1617
 */
1618
function set_single_sysctl($name, $value) {
1619
	if (empty($name))
1620
		return false;
1621

    
1622
	$result = set_sysctl(array($name => $value));
1623

    
1624
	if (!isset($result[$name]) || $result[$name] != $value)
1625
		return false;
1626

    
1627
	return true;
1628
}
1629

    
1630
/*
1631
 *     get_memory()
1632
 *     returns an array listing the amount of
1633
 *     memory installed in the hardware
1634
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
1635
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
1636
 */
1637
function get_memory() {
1638
	$physmem = get_single_sysctl("hw.physmem");
1639
	$realmem = get_single_sysctl("hw.realmem");
1640
	/* convert from bytes to megabytes */
1641
	return array(($physmem/1048576),($realmem/1048576));
1642
}
1643

    
1644
function mute_kernel_msgs() {
1645
	global $config;
1646
	// Do not mute serial console.  The kernel gets very very cranky
1647
	// and will start dishing you cannot control tty errors.
1648
	switch (trim(file_get_contents("/etc/platform"))) {
1649
		case "nanobsd":
1650
		case "jail":
1651
			return;
1652
	}
1653
	if($config['system']['enableserial'])
1654
		return;
1655
	exec("/sbin/conscontrol mute on");
1656
}
1657

    
1658
function unmute_kernel_msgs() {
1659
	global $config;
1660
	// Do not mute serial console.  The kernel gets very very cranky
1661
	// and will start dishing you cannot control tty errors.
1662
	switch (trim(file_get_contents("/etc/platform"))) {
1663
		case "nanobsd":
1664
		case "jail":
1665
			return;
1666
	}
1667
	exec("/sbin/conscontrol mute off");
1668
}
1669

    
1670
function start_devd() {
1671
	global $g;
1672

    
1673
	if ($g['platform'] == 'jail')
1674
		return;
1675
	exec("/sbin/devd");
1676
	sleep(1);
1677
}
1678

    
1679
function is_interface_vlan_mismatch() {
1680
	global $config, $g;
1681

    
1682
	if (is_array($config['vlans']['vlan'])) {
1683
		foreach ($config['vlans']['vlan'] as $vlan) {
1684
			if (does_interface_exist($vlan['if']) == false)
1685
				return true;
1686
		}
1687
	}
1688

    
1689
	return false;
1690
}
1691

    
1692
function is_interface_mismatch() {
1693
	global $config, $g;
1694

    
1695
	$do_assign = false;
1696
	$i = 0;
1697
	$missing_interfaces = array();
1698
	if (is_array($config['interfaces'])) {
1699
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1700
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1701
				// Do not check these interfaces.
1702
				$i++;
1703
				continue;
1704
			}
1705
			else if (does_interface_exist($ifcfg['if']) == false) {
1706
				$missing_interfaces[] = $ifcfg['if'];
1707
				$do_assign = true;
1708
			} else
1709
				$i++;
1710
		}
1711
	}
1712

    
1713
	if ($g['minimum_nic_count'] > $i) {
1714
		$do_assign = true;
1715
	} else if (file_exists("{$g['tmp_path']}/assign_complete"))
1716
		$do_assign = false;
1717

    
1718
	if (!empty($missing_interfaces) && $do_assign)
1719
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1720
	else
1721
		@unlink("{$g['tmp_path']}/missing_interfaces");
1722

    
1723
	return $do_assign;
1724
}
1725

    
1726
/* sync carp entries to other firewalls */
1727
function carp_sync_client() {
1728
	global $g;
1729
	send_event("filter sync");
1730
}
1731

    
1732
/****f* util/isAjax
1733
 * NAME
1734
 *   isAjax - reports if the request is driven from prototype
1735
 * INPUTS
1736
 *   none
1737
 * RESULT
1738
 *   true/false
1739
 ******/
1740
function isAjax() {
1741
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
1742
}
1743

    
1744
/****f* util/timeout
1745
 * NAME
1746
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
1747
 * INPUTS
1748
 *   optional, seconds to wait before timeout. Default 9 seconds.
1749
 * RESULT
1750
 *   returns 1 char of user input or null if no input.
1751
 ******/
1752
function timeout($timer = 9) {
1753
	while(!isset($key)) {
1754
		if ($timer >= 9) { echo chr(8) . chr(8) . ($timer==9 ? chr(32) : null)  . "{$timer}";  }
1755
		else { echo chr(8). "{$timer}"; }
1756
		`/bin/stty -icanon min 0 time 25`;
1757
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
1758
		`/bin/stty icanon`;
1759
		if ($key == '')
1760
			unset($key);
1761
		$timer--;
1762
		if ($timer == 0)
1763
			break;
1764
	}
1765
	return $key;
1766
}
1767

    
1768
/****f* util/msort
1769
 * NAME
1770
 *   msort - sort array
1771
 * INPUTS
1772
 *   $array to be sorted, field to sort by, direction of sort
1773
 * RESULT
1774
 *   returns newly sorted array
1775
 ******/
1776
function msort($array, $id="id", $sort_ascending=true) {
1777
	$temp_array = array();
1778
	while(count($array)>0) {
1779
		$lowest_id = 0;
1780
		$index=0;
1781
		foreach ($array as $item) {
1782
			if (isset($item[$id])) {
1783
				if ($array[$lowest_id][$id]) {
1784
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
1785
						$lowest_id = $index;
1786
					}
1787
				}
1788
			}
1789
			$index++;
1790
		}
1791
		$temp_array[] = $array[$lowest_id];
1792
		$array = array_merge(array_slice($array, 0,$lowest_id), array_slice($array, $lowest_id+1));
1793
	}
1794
	if ($sort_ascending) {
1795
		return $temp_array;
1796
	} else {
1797
		return array_reverse($temp_array);
1798
	}
1799
}
1800

    
1801
/****f* util/is_URL
1802
 * NAME
1803
 *   is_URL
1804
 * INPUTS
1805
 *   string to check
1806
 * RESULT
1807
 *   Returns true if item is a URL
1808
 ******/
1809
function is_URL($url) {
1810
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
1811
	if($match)
1812
		return true;
1813
	return false;
1814
}
1815

    
1816
function is_file_included($file = "") {
1817
	$files = get_included_files();
1818
	if (in_array($file, $files))
1819
		return true;
1820

    
1821
	return false;
1822
}
1823

    
1824
/*
1825
 * Replace a value on a deep associative array using regex
1826
 */
1827
function array_replace_values_recursive($data, $match, $replace) {
1828
	if (empty($data))
1829
		return $data;
1830

    
1831
	if (is_string($data))
1832
		$data = preg_replace("/{$match}/", $replace, $data);
1833
	else if (is_array($data))
1834
		foreach ($data as $k => $v)
1835
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
1836

    
1837
	return $data;
1838
}
1839

    
1840
/*
1841
	This function was borrowed from a comment on PHP.net at the following URL:
1842
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
1843
 */
1844
function array_merge_recursive_unique($array0, $array1) {
1845

    
1846
	$arrays = func_get_args();
1847
	$remains = $arrays;
1848

    
1849
	// We walk through each arrays and put value in the results (without
1850
	// considering previous value).
1851
	$result = array();
1852

    
1853
	// loop available array
1854
	foreach($arrays as $array) {
1855

    
1856
		// The first remaining array is $array. We are processing it. So
1857
		// we remove it from remaing arrays.
1858
		array_shift($remains);
1859

    
1860
		// We don't care non array param, like array_merge since PHP 5.0.
1861
		if(is_array($array)) {
1862
			// Loop values
1863
			foreach($array as $key => $value) {
1864
				if(is_array($value)) {
1865
					// we gather all remaining arrays that have such key available
1866
					$args = array();
1867
					foreach($remains as $remain) {
1868
						if(array_key_exists($key, $remain)) {
1869
							array_push($args, $remain[$key]);
1870
						}
1871
					}
1872

    
1873
					if(count($args) > 2) {
1874
						// put the recursion
1875
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
1876
					} else {
1877
						foreach($value as $vkey => $vval) {
1878
							$result[$key][$vkey] = $vval;
1879
						}
1880
					}
1881
				} else {
1882
					// simply put the value
1883
					$result[$key] = $value;
1884
				}
1885
			}
1886
		}
1887
	}
1888
	return $result;
1889
}
1890

    
1891

    
1892
/*
1893
 * converts a string like "a,b,c,d"
1894
 * into an array like array("a" => "b", "c" => "d")
1895
 */
1896
function explode_assoc($delimiter, $string) {
1897
	$array = explode($delimiter, $string);
1898
	$result = array();
1899
	$numkeys = floor(count($array) / 2);
1900
	for ($i = 0; $i < $numkeys; $i += 1) {
1901
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
1902
	}
1903
	return $result;
1904
}
1905

    
1906
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
1907
	global $config, $aliastable;
1908

    
1909
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
1910
	if (!is_array($config['staticroutes']['route']))
1911
		return array();
1912

    
1913
	$allstaticroutes = array();
1914
	$allsubnets = array();
1915
	/* Loop through routes and expand aliases as we find them. */
1916
	foreach ($config['staticroutes']['route'] as $route) {
1917
		if (is_alias($route['network'])) {
1918
			if (!isset($aliastable[$route['network']]))
1919
				continue;
1920

    
1921
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
1922
			foreach ($subnets as $net) {
1923
				if (!is_subnet($net)) {
1924
					if (is_ipaddrv4($net))
1925
						$net .= "/32";
1926
					else if (is_ipaddrv6($net))
1927
						$net .= "/128";
1928
					else if ($returnhostnames === false || !is_fqdn($net))
1929
						continue;
1930
				}
1931
				$temproute = $route;
1932
				$temproute['network'] = $net;
1933
				$allstaticroutes[] = $temproute;
1934
				$allsubnets[] = $net;
1935
			}
1936
		} elseif (is_subnet($route['network'])) {
1937
			$allstaticroutes[] = $route;
1938
			$allsubnets[] = $route['network'];
1939
		}
1940
	}
1941
	if ($returnsubnetsonly)
1942
		return $allsubnets;
1943
	else
1944
		return $allstaticroutes;
1945
}
1946

    
1947
/****f* util/get_alias_list
1948
 * NAME
1949
 *   get_alias_list - Provide a list of aliases.
1950
 * INPUTS
1951
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
1952
 * RESULT
1953
 *   Array containing list of aliases.
1954
 *   If $type is unspecified, all aliases are returned.
1955
 *   If $type is a string, all aliases of the type specified in $type are returned.
1956
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
1957
 */
1958
function get_alias_list($type = null) {
1959
	global $config;
1960
	$result = array();
1961
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
1962
		foreach ($config['aliases']['alias'] as $alias) {
1963
			if ($type === null) {
1964
				$result[] = $alias['name'];
1965
			}
1966
			else if (is_array($type)) {
1967
				if (in_array($alias['type'], $type)) {
1968
					$result[] = $alias['name'];
1969
				}
1970
			}
1971
			else if ($type === $alias['type']) {
1972
				$result[] = $alias['name'];
1973
			}
1974
		}
1975
	}
1976
	return $result;
1977
}
1978

    
1979
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
1980
function array_exclude($needle, $haystack) {
1981
	$result = array();
1982
	if (is_array($haystack)) {
1983
		foreach ($haystack as $thing) {
1984
			if ($needle !== $thing) {
1985
				$result[] = $thing;
1986
			}
1987
		}
1988
	}
1989
	return $result;
1990
}
1991

    
1992
function get_current_theme() {
1993
	global $config, $g;
1994
	/*
1995
	 *   if user has selected a custom template, use it.
1996
	 *   otherwise default to pfsense tempalte
1997
	 */
1998
	if (($g["disablethemeselection"] === true) && !empty($g["default_theme"]) && (is_dir($g["www_path"].'/themes/'.$g["default_theme"])))
1999
		$theme = $g["default_theme"];
2000
	elseif($config['theme'] <> "" && (is_dir($g["www_path"].'/themes/'.$config['theme'])))
2001
		$theme = $config['theme'];
2002
	else
2003
		$theme = "pfsense";
2004
	/*
2005
	 *  If this device is an apple ipod/iphone
2006
	 *  switch the theme to one that works with it.
2007
	 */
2008
	$lowres_ua = array("iPhone", "iPod", "iPad", "Android", "BlackBerry", "Opera Mini", "Opera Mobi", "PlayBook", "IEMobile");
2009
	foreach($lowres_ua as $useragent)
2010
		if(strstr($_SERVER['HTTP_USER_AGENT'], $useragent))
2011
			$theme = (empty($g['theme_lowres']) && (is_dir($g["www_path"].'/themes/'.$g['theme_lowres']))) ? "pfsense" : $g['theme_lowres'];
2012
	return $theme;
2013
}
2014

    
2015
/* Define what is preferred, IPv4 or IPv6 */
2016
function prefer_ipv4_or_ipv6() {
2017
	global $config;
2018

    
2019
	if (isset($config['system']['prefer_ipv4']))
2020
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2021
	else
2022
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2023
}
2024

    
2025
?>
(56-56/68)