Project

General

Profile

Download (58 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	util.inc
4
*/
5
/* ====================================================================
6
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *
8
 *  Some or all of this file is based on the m0n0wall project which is
9
 *  Copyright (c)  2004 Manuel Kasper (BSD 2 clause)
10
 *
11
 *	Redistribution and use in source and binary forms, with or without modification,
12
 *	are permitted provided that the following conditions are met:
13
 *
14
 *	1. Redistributions of source code must retain the above copyright notice,
15
 *		this list of conditions and the following disclaimer.
16
 *
17
 *	2. Redistributions in binary form must reproduce the above copyright
18
 *		notice, this list of conditions and the following disclaimer in
19
 *		the documentation and/or other materials provided with the
20
 *		distribution.
21
 *
22
 *	3. All advertising materials mentioning features or use of this software
23
 *		must display the following acknowledgment:
24
 *		"This product includes software developed by the pfSense Project
25
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
26
 *
27
 *	4. The names "pfSense" and "pfSense Project" must not be used to
28
 *		 endorse or promote products derived from this software without
29
 *		 prior written permission. For written permission, please contact
30
 *		 coreteam@pfsense.org.
31
 *
32
 *	5. Products derived from this software may not be called "pfSense"
33
 *		nor may "pfSense" appear in their names without prior written
34
 *		permission of the Electric Sheep Fencing, LLC.
35
 *
36
 *	6. Redistributions of any form whatsoever must retain the following
37
 *		acknowledgment:
38
 *
39
 *	"This product includes software developed by the pfSense Project
40
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
41
 *
42
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
43
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
45
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
46
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
49
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
51
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
52
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
53
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
54
 *
55
 *	====================================================================
56
 *
57
 */
58
/*
59
	pfSense_BUILDER_BINARIES:	/bin/ps	/bin/kill	/usr/bin/killall	/sbin/ifconfig	/usr/bin/netstat
60
	pfSense_BUILDER_BINARIES:	/usr/bin/awk	/sbin/dmesg		/sbin/ping /usr/local/sbin/gzsig	/usr/sbin/arp
61
	pfSense_BUILDER_BINARIES:	/sbin/conscontrol	/sbin/devd	/bin/ps
62
	pfSense_MODULE:	utils
63
*/
64

    
65
/* kill a process by pid file */
66
function killbypid($pidfile) {
67
	return sigkillbypid($pidfile, "TERM");
68
}
69

    
70
function isvalidpid($pidfile) {
71
	$output = "";
72
	if (file_exists($pidfile)) {
73
		exec("/bin/pgrep -nF {$pidfile}", $output, $retval);
74
		return (intval($retval) == 0);
75
	}
76
	return false;
77
}
78

    
79
function is_process_running($process) {
80
	$output = "";
81
	exec("/bin/pgrep -anx " . escapeshellarg($process), $output, $retval);
82

    
83
	return (intval($retval) == 0);
84
}
85

    
86
function isvalidproc($proc) {
87
	return is_process_running($proc);
88
}
89

    
90
/* sigkill a process by pid file */
91
/* return 1 for success and 0 for a failure */
92
function sigkillbypid($pidfile, $sig) {
93
	if (file_exists($pidfile)) {
94
		return mwexec("/bin/pkill " . escapeshellarg("-{$sig}") . " -F {$pidfile}", true);
95
	}
96

    
97
	return 0;
98
}
99

    
100
/* kill a process by name */
101
function sigkillbyname($procname, $sig) {
102
	if (isvalidproc($procname)) {
103
		return mwexec("/usr/bin/killall " . escapeshellarg("-{$sig}") . " " . escapeshellarg($procname), true);
104
	}
105
}
106

    
107
/* kill a process by name */
108
function killbyname($procname) {
109
	if (isvalidproc($procname)) {
110
		mwexec("/usr/bin/killall " . escapeshellarg($procname));
111
	}
112
}
113

    
114
function is_subsystem_dirty($subsystem = "") {
115
	global $g;
116

    
117
	if ($subsystem == "") {
118
		return false;
119
	}
120

    
121
	if (file_exists("{$g['varrun_path']}/{$subsystem}.dirty")) {
122
		return true;
123
	}
124

    
125
	return false;
126
}
127

    
128
function mark_subsystem_dirty($subsystem = "") {
129
	global $g;
130

    
131
	if (!file_put_contents("{$g['varrun_path']}/{$subsystem}.dirty", "DIRTY")) {
132
		log_error(sprintf(gettext("WARNING: Could not mark subsystem: %s dirty"), $subsystem));
133
	}
134
}
135

    
136
function clear_subsystem_dirty($subsystem = "") {
137
	global $g;
138

    
139
	@unlink("{$g['varrun_path']}/{$subsystem}.dirty");
140
}
141

    
142
function config_lock() {
143
	return;
144
}
145
function config_unlock() {
146
	return;
147
}
148

    
149
/* lock configuration file */
150
function lock($lock, $op = LOCK_SH) {
151
	global $g, $cfglckkeyconsumers;
152
	if (!$lock) {
153
		die(gettext("WARNING: You must give a name as parameter to lock() function."));
154
	}
155
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
156
		@touch("{$g['tmp_path']}/{$lock}.lock");
157
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
158
	}
159
	$cfglckkeyconsumers++;
160
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
161
		if (flock($fp, $op)) {
162
			return $fp;
163
		} else {
164
			fclose($fp);
165
		}
166
	}
167
}
168

    
169
function try_lock($lock, $timeout = 5) {
170
	global $g, $cfglckkeyconsumers;
171
	if (!$lock) {
172
		die(gettext("WARNING: You must give a name as parameter to try_lock() function."));
173
	}
174
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
175
		@touch("{$g['tmp_path']}/{$lock}.lock");
176
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
177
	}
178
	$cfglckkeyconsumers++;
179
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
180
		$trycounter = 0;
181
		while (!flock($fp, LOCK_EX | LOCK_NB)) {
182
			if ($trycounter >= $timeout) {
183
				fclose($fp);
184
				return NULL;
185
			}
186
			sleep(1);
187
			$trycounter++;
188
		}
189

    
190
		return $fp;
191
	}
192

    
193
	return NULL;
194
}
195

    
196
/* unlock configuration file */
197
function unlock($cfglckkey = 0) {
198
	global $g, $cfglckkeyconsumers;
199
	flock($cfglckkey, LOCK_UN);
200
	fclose($cfglckkey);
201
	return;
202
}
203

    
204
/* unlock forcefully configuration file */
205
function unlock_force($lock) {
206
	global $g;
207

    
208
	@unlink("{$g['tmp_path']}/{$lock}.lock");
209
}
210

    
211
function send_event($cmd) {
212
	global $g;
213

    
214
	if (!isset($g['event_address'])) {
215
		$g['event_address'] = "unix:///var/run/check_reload_status";
216
	}
217

    
218
	$try = 0;
219
	while ($try < 3) {
220
		$fd = @fsockopen($g['event_address']);
221
		if ($fd) {
222
			fwrite($fd, $cmd);
223
			$resp = fread($fd, 4096);
224
			if ($resp != "OK\n") {
225
				log_error("send_event: sent {$cmd} got {$resp}");
226
			}
227
			fclose($fd);
228
			$try = 3;
229
		} else if (!is_process_running("check_reload_status")) {
230
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
231
		}
232
		$try++;
233
	}
234
}
235

    
236
function send_multiple_events($cmds) {
237
	global $g;
238

    
239
	if (!isset($g['event_address'])) {
240
		$g['event_address'] = "unix:///var/run/check_reload_status";
241
	}
242

    
243
	if (!is_array($cmds)) {
244
		return;
245
	}
246

    
247
	while ($try < 3) {
248
		$fd = @fsockopen($g['event_address']);
249
		if ($fd) {
250
			foreach ($cmds as $cmd) {
251
				fwrite($fd, $cmd);
252
				$resp = fread($fd, 4096);
253
				if ($resp != "OK\n") {
254
					log_error("send_event: sent {$cmd} got {$resp}");
255
				}
256
			}
257
			fclose($fd);
258
			$try = 3;
259
		} else if (!is_process_running("check_reload_status")) {
260
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
261
		}
262
		$try++;
263
	}
264
}
265

    
266
function refcount_init($reference) {
267
	$shmid = @shmop_open($reference, "c", 0644, 10);
268
	@shmop_write($shmid, str_pad("0", 10, "\x0", STR_PAD_RIGHT), 0);
269
	@shmop_close($shmid);
270
}
271

    
272
function refcount_reference($reference) {
273
	/* Take out a lock across the shared memory read, increment, write sequence to make it atomic. */
274
	$shm_lck = lock("shm{$reference}", LOCK_EX);
275
	try {
276
		/* NOTE: A warning is generated when shared memory does not exist */
277
		$shmid = @shmop_open($reference, "w", 0, 0);
278
		if (!$shmid) {
279
			refcount_init($reference);
280
			$shmid = @shmop_open($reference, "w", 0, 0);
281
			if (!$shmid) {
282
				log_error(gettext("Could not open shared memory {$reference}"));
283
				unlock($shm_lck);
284
				return;
285
			}
286
		}
287
		$shm_data = @shmop_read($shmid, 0, 10);
288
		$shm_data = intval($shm_data) + 1;
289
		@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
290
		@shmop_close($shmid);
291
		unlock($shm_lck);
292
	} catch (Exception $e) {
293
		log_error($e->getMessage());
294
		unlock($shm_lck);
295
	}
296

    
297
	return $shm_data;
298
}
299

    
300
function refcount_unreference($reference) {
301
	/* Take out a lock across the shared memory read, decrement, write sequence to make it atomic. */
302
	$shm_lck = lock("shm{$reference}", LOCK_EX);
303
	try {
304
		$shmid = @shmop_open($reference, "w", 0, 0);
305
		if (!$shmid) {
306
			refcount_init($reference);
307
			log_error(gettext("Could not open shared memory {$reference}"));
308
			unlock($shm_lck);
309
			return;
310
		}
311
		$shm_data = @shmop_read($shmid, 0, 10);
312
		$shm_data = intval($shm_data) - 1;
313
		if ($shm_data < 0) {
314
			//debug_backtrace();
315
			log_error(sprintf(gettext("Reference %s is going negative, not doing unreference."), $reference));
316
		} else {
317
			@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
318
		}
319
		@shmop_close($shmid);
320
		unlock($shm_lck);
321
	} catch (Exception $e) {
322
		log_error($e->getMessage());
323
		unlock($shm_lck);
324
	}
325

    
326
	return $shm_data;
327
}
328

    
329
function refcount_read($reference) {
330
	/* This function just reads the current value of the refcount for information. */
331
	/* There is no need for locking. */
332
	$shmid = @shmop_open($reference, "a", 0, 0);
333
	if (!$shmid) {
334
		log_error(gettext("Could not open shared memory for read {$reference}"));
335
		return -1;
336
	}
337
	$shm_data = @shmop_read($shmid, 0, 10);
338
	@shmop_close($shmid);
339
	return $shm_data;
340
}
341

    
342
function is_module_loaded($module_name) {
343
	$module_name = str_replace(".ko", "", $module_name);
344
	$running = 0;
345
	$_gb = exec("/sbin/kldstat -qn {$module_name} 2>&1", $_gb, $running);
346
	if (intval($running) == 0) {
347
		return true;
348
	} else {
349
		return false;
350
	}
351
}
352

    
353
/* validate non-negative numeric string, or equivalent numeric variable */
354
function is_numericint($arg) {
355
	return (((is_int($arg) && $arg >= 0) || (is_string($arg) && strlen($arg) > 0 && ctype_digit($arg))) ? true : false);
356
}
357

    
358
/* Generate the (human readable) ipv4 or ipv6 subnet address (i.e., netmask, or subnet start IP)
359
   given an (human readable) ipv4 or ipv6 host address and subnet bit count */
360
function gen_subnet($ipaddr, $bits) {
361
	if (($sn = gen_subnetv6($ipaddr, $bits)) == '') {
362
		$sn = gen_subnetv4($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
363
	}
364
	return $sn;
365
}
366

    
367
/* same as gen_subnet() but accepts IPv4 only */
368
function gen_subnetv4($ipaddr, $bits) {
369
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
370
		if ($bits == 0) {
371
			return '0.0.0.0';  // avoids <<32
372
		}
373
		return long2ip(ip2long($ipaddr) & ((0xFFFFFFFF << (32 - $bits)) & 0xFFFFFFFF));
374
	}
375
	return "";
376
}
377

    
378
/* same as gen_subnet() but accepts IPv6 only */
379
function gen_subnetv6($ipaddr, $bits) {
380
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
381
		return Net_IPv6::compress(Net_IPv6::getNetmask($ipaddr, $bits));
382
	}
383
	return "";
384
}
385

    
386
/* Generate the (human readable) ipv4 or ipv6 subnet end address (i.e., highest address, end IP, or IPv4 broadcast address)
387
   given an (human readable) ipv4 or ipv6 host address and subnet bit count. */
388
function gen_subnet_max($ipaddr, $bits) {
389
	if (($sn = gen_subnetv6_max($ipaddr, $bits)) == '') {
390
		$sn = gen_subnetv4_max($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
391
	}
392
	return $sn;
393
}
394

    
395
/* same as gen_subnet_max() but validates IPv4 only */
396
function gen_subnetv4_max($ipaddr, $bits) {
397
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
398
		if ($bits == 32) {
399
			return $ipaddr;
400
		}
401
		return long2ip32(ip2long($ipaddr) | ~gen_subnet_mask_long($bits));
402
	}
403
	return "";
404
}
405

    
406
/* same as gen_subnet_max() but validates IPv6 only */
407
function gen_subnetv6_max($ipaddr, $bits) {
408
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
409
		$endip_bin = substr(Net_IPv6::_ip2Bin($ipaddr), 0, $bits) . str_repeat('1', 128 - $bits);
410
		return Net_IPv6::compress(Net_IPv6::_bin2Ip($endip_bin));
411
	}
412
	return "";
413
}
414

    
415
/* returns a subnet mask (long given a bit count) */
416
function gen_subnet_mask_long($bits) {
417
	$sm = 0;
418
	for ($i = 0; $i < $bits; $i++) {
419
		$sm >>= 1;
420
		$sm |= 0x80000000;
421
	}
422
	return $sm;
423
}
424

    
425
/* same as above but returns a string */
426
function gen_subnet_mask($bits) {
427
	return long2ip(gen_subnet_mask_long($bits));
428
}
429

    
430
/* Convert long int to IP address, truncating to 32-bits. */
431
function long2ip32($ip) {
432
	return long2ip($ip & 0xFFFFFFFF);
433
}
434

    
435
/* Convert IP address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms. */
436
function ip2long32($ip) {
437
	return (ip2long($ip) & 0xFFFFFFFF);
438
}
439

    
440
/* Convert IP address to unsigned long int. */
441
function ip2ulong($ip) {
442
	return sprintf("%u", ip2long32($ip));
443
}
444

    
445
/* Find out how many IPs are contained within a given IP range
446
 *  e.g. 192.168.0.0 to 192.168.0.255 returns 256
447
 */
448
function ip_range_size_v4($startip, $endip) {
449
	if (is_ipaddrv4($startip) && is_ipaddrv4($endip)) {
450
		// Operate as unsigned long because otherwise it wouldn't work
451
		//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
452
		return abs(ip2ulong($startip) - ip2ulong($endip)) + 1;
453
	}
454
	return -1;
455
}
456

    
457
/* Find the smallest possible subnet mask which can contain a given number of IPs
458
 *  e.g. 512 IPs can fit in a /23, but 513 IPs need a /22
459
 */
460
function find_smallest_cidr_v4($number) {
461
	$smallest = 1;
462
	for ($b=32; $b > 0; $b--) {
463
		$smallest = ($number <= pow(2, $b)) ? $b : $smallest;
464
	}
465
	return (32-$smallest);
466
}
467

    
468
/* Return the previous IP address before the given address */
469
function ip_before($ip, $offset = 1) {
470
	return long2ip32(ip2long($ip) - $offset);
471
}
472

    
473
/* Return the next IP address after the given address */
474
function ip_after($ip, $offset = 1) {
475
	return long2ip32(ip2long($ip) + $offset);
476
}
477

    
478
/* Return true if the first IP is 'before' the second */
479
function ip_less_than($ip1, $ip2) {
480
	// Compare as unsigned long because otherwise it wouldn't work when
481
	//   crossing over from 127.255.255.255 / 128.0.0.0 barrier
482
	return ip2ulong($ip1) < ip2ulong($ip2);
483
}
484

    
485
/* Return true if the first IP is 'after' the second */
486
function ip_greater_than($ip1, $ip2) {
487
	// Compare as unsigned long because otherwise it wouldn't work
488
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
489
	return ip2ulong($ip1) > ip2ulong($ip2);
490
}
491

    
492
/* compare two IP addresses */
493
function ipcmp($a, $b) {
494
	if (ip_less_than($a, $b)) {
495
		return -1;
496
	} else if (ip_greater_than($a, $b)) {
497
		return 1;
498
	} else {
499
		return 0;
500
	}
501
}
502

    
503
/* Convert a range of IPv4 addresses to an array of individual addresses. */
504
/* Note: IPv6 ranges are not yet supported here. */
505
function ip_range_to_address_array($startip, $endip, $max_size = 5000) {
506
	if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) {
507
		return false;
508
	}
509

    
510
	if (ip_greater_than($startip, $endip)) {
511
		// Swap start and end so we can process sensibly.
512
		$temp = $startip;
513
		$startip = $endip;
514
		$endip = $temp;
515
	}
516

    
517
	if (ip_range_size_v4($startip, $endip) > $max_size) {
518
		return false;
519
	}
520

    
521
	// Container for IP addresses within this range.
522
	$rangeaddresses = array();
523
	$end_int = ip2ulong($endip);
524
	for ($ip_int = ip2ulong($startip); $ip_int <= $end_int; $ip_int++) {
525
		$rangeaddresses[] = long2ip($ip_int);
526
	}
527

    
528
	return $rangeaddresses;
529
}
530

    
531
/* Convert a range of IPv4 addresses to an array of subnets which can contain the range. */
532
/* Note: IPv6 ranges are not yet supported here. */
533
function ip_range_to_subnet_array($startip, $endip) {
534
	if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) {
535
		return array();
536
	}
537

    
538
	if (ip_greater_than($startip, $endip)) {
539
		// Swap start and end so we can process sensibly.
540
		$temp = $startip;
541
		$startip = $endip;
542
		$endip = $temp;
543
	}
544

    
545
	// Container for subnets within this range.
546
	$rangesubnets = array();
547

    
548
	// Figure out what the smallest subnet is that holds the number of IPs in the given range.
549
	$cidr = find_smallest_cidr_v4(ip_range_size_v4($startip, $endip));
550

    
551
	// Loop here to reduce subnet size and retest as needed. We need to make sure
552
	//   that the target subnet is wholly contained between $startip and $endip.
553
	for ($cidr; $cidr <= 32; $cidr++) {
554
		// Find the network and broadcast addresses for the subnet being tested.
555
		$targetsub_min = gen_subnet($startip, $cidr);
556
		$targetsub_max = gen_subnet_max($startip, $cidr);
557

    
558
		// Check best case where the range is exactly one subnet.
559
		if (($targetsub_min == $startip) && ($targetsub_max == $endip)) {
560
			// Hooray, the range is exactly this subnet!
561
			return array("{$startip}/{$cidr}");
562
		}
563

    
564
		// These remaining scenarios will find a subnet that uses the largest
565
		//  chunk possible of the range being tested, and leave the rest to be
566
		//  tested recursively after the loop.
567

    
568
		// Check if the subnet begins with $startip and ends before $endip
569
		if (($targetsub_min == $startip) && ip_less_than($targetsub_max, $endip)) {
570
			break;
571
		}
572

    
573
		// Check if the subnet ends at $endip and starts after $startip
574
		if (ip_greater_than($targetsub_min, $startip) && ($targetsub_max == $endip)) {
575
			break;
576
		}
577

    
578
		// Check if the subnet is between $startip and $endip
579
		if (ip_greater_than($targetsub_min, $startip) && ip_less_than($targetsub_max, $endip)) {
580
			break;
581
		}
582
	}
583

    
584
	// Some logic that will recursively search from $startip to the first IP before the start of the subnet we just found.
585
	// NOTE: This may never be hit, the way the above algo turned out, but is left for completeness.
586
	if ($startip != $targetsub_min) {
587
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array($startip, ip_before($targetsub_min)));
588
	}
589

    
590
	// Add in the subnet we found before, to preserve ordering
591
	$rangesubnets[] = "{$targetsub_min}/{$cidr}";
592

    
593
	// And some more logic that will search after the subnet we found to fill in to the end of the range.
594
	if ($endip != $targetsub_max) {
595
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array(ip_after($targetsub_max), $endip));
596
	}
597
	return $rangesubnets;
598
}
599

    
600
/* returns true if $range is a valid pair of IPv4 or IPv6 addresses separated by a "-"
601
	false - if not a valid pair
602
	true (numeric 4 or 6) - if valid, gives type of addresses */
603
function is_iprange($range) {
604
	if (substr_count($range, '-') != 1) {
605
		return false;
606
	}
607
	list($ip1, $ip2) = explode ('-', $range);
608
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) {
609
		return 4;
610
	}
611
	if (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) {
612
		return 6;
613
	}
614
	return false;
615
}
616

    
617
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6
618
	false - not valid
619
	true (numeric 4 or 6) - if valid, gives type of address */
620
function is_ipaddr($ipaddr) {
621
	if (is_ipaddrv4($ipaddr)) {
622
		return 4;
623
	}
624
	if (is_ipaddrv6($ipaddr)) {
625
		return 6;
626
	}
627
	return false;
628
}
629

    
630
/* returns true if $ipaddr is a valid IPv6 address */
631
function is_ipaddrv6($ipaddr) {
632
	if (!is_string($ipaddr) || empty($ipaddr)) {
633
		return false;
634
	}
635
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
636
		$tmpip = explode("%", $ipaddr);
637
		$ipaddr = $tmpip[0];
638
	}
639
	return Net_IPv6::checkIPv6($ipaddr);
640
}
641

    
642
/* returns true if $ipaddr is a valid dotted IPv4 address */
643
function is_ipaddrv4($ipaddr) {
644
	if (!is_string($ipaddr) || empty($ipaddr) || ip2long($ipaddr) === FALSE) {
645
		return false;
646
	}
647
	return true;
648
}
649

    
650
/* returns true if $ipaddr is a valid IPv6 linklocal address */
651
function is_linklocal($ipaddr) {
652
	return (strtolower(substr($ipaddr, 0, 5)) == "fe80:");
653
}
654

    
655
/* returns scope of a linklocal address */
656
function get_ll_scope($addr) {
657
	if (!is_linklocal($addr) || !strstr($addr, "%")) {
658
		return "";
659
	}
660
	list ($ll, $scope) = explode("%", $addr);
661
	return $scope;
662
}
663

    
664
/* returns true if $ipaddr is a valid literal IPv6 address */
665
function is_literalipaddrv6($ipaddr) {
666
	if (preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match)) {
667
		$ipaddr = $match[1];
668
	} else {
669
		return false;
670
	}
671

    
672
	return is_ipaddrv6($ipaddr);
673
}
674

    
675
/* returns true if $iport is a valid IPv4/IPv6 address + port
676
	false - not valid
677
	true (numeric 4 or 6) - if valid, gives type of address */
678
function is_ipaddrwithport($ipport) {
679
	$c = strrpos($ipport, ":");
680
	if ($c === false) {
681
		return false;  // can't split at final colon if no colon exists
682
	}
683

    
684
	if (!is_port(substr($ipport, $c + 1))) {
685
		return false;  // no valid port after last colon
686
	}
687

    
688
	$ip = substr($ipport, 0, $c);  // else is text before last colon a valid IP
689
	if (is_literalipaddrv6($ip)) {
690
		return 6;
691
	} elseif (is_ipaddrv4($ip)) {
692
		return 4;
693
	} else {
694
		return false;
695
	}
696
}
697

    
698
function is_hostnamewithport($hostport) {
699
	$parts = explode(":", $hostport);
700
	$port = array_pop($parts);
701
	if (count($parts) == 1) {
702
		return is_hostname($parts[0]) && is_port($port);
703
	} else {
704
		return false;
705
	}
706
}
707

    
708
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
709
function is_ipaddroralias($ipaddr) {
710
	global $config;
711

    
712
	if (is_alias($ipaddr)) {
713
		if (is_array($config['aliases']['alias'])) {
714
			foreach ($config['aliases']['alias'] as $alias) {
715
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type'])) {
716
					return true;
717
				}
718
			}
719
		}
720
		return false;
721
	} else {
722
		return is_ipaddr($ipaddr);
723
	}
724

    
725
}
726

    
727
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format
728
	false - if not a valid subnet
729
	true (numeric 4 or 6) - if valid, gives type of subnet */
730
function is_subnet($subnet) {
731
	if (is_string($subnet) && preg_match('/^(?:([0-9.]{7,15})|([0-9a-f:]{2,39}))\/(\d{1,3})$/i', $subnet, $parts)) {
732
		if (is_ipaddrv4($parts[1]) && $parts[3] <= 32) {
733
			return 4;
734
		}
735
		if (is_ipaddrv6($parts[2]) && $parts[3] <= 128) {
736
			return 6;
737
		}
738
	}
739
	return false;
740
}
741

    
742
/* same as is_subnet() but accepts IPv4 only */
743
function is_subnetv4($subnet) {
744
	return (is_subnet($subnet) == 4);
745
}
746

    
747
/* same as is_subnet() but accepts IPv6 only */
748
function is_subnetv6($subnet) {
749
	return (is_subnet($subnet) == 6);
750
}
751

    
752
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
753
function is_subnetoralias($subnet) {
754
	global $aliastable;
755

    
756
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet])) {
757
		return true;
758
	} else {
759
		return is_subnet($subnet);
760
	}
761
}
762

    
763
function subnet_size($subnet) {
764
	if (is_subnetv4($subnet)) {
765
		list ($ip, $bits) = explode("/", $subnet);
766
		return round(exp(log(2) * (32 - $bits)));
767
	}
768
	else if (is_subnetv6($subnet)) {
769
		list ($ip, $bits) = explode("/", $subnet);
770
		return round(exp(log(2) * (128 - $bits)));
771
	}
772
	else {
773
		return 0;
774
	}
775
}
776

    
777

    
778
function subnet_expand($subnet) {
779
	if (is_subnetv4($subnet)) {
780
		return subnetv4_expand($subnet);
781
	} else if (is_subnetv6($subnet)) {
782
		return subnetv6_expand($subnet);
783
	} else {
784
		return $subnet;
785
	}
786
}
787

    
788
function subnetv4_expand($subnet) {
789
	$result = array();
790
	list ($ip, $bits) = explode("/", $subnet);
791
	$net = ip2long($ip);
792
	$mask = (0xffffffff << (32 - $bits));
793
	$net &= $mask;
794
	$size = round(exp(log(2) * (32 - $bits)));
795
	for ($i = 0; $i < $size; $i += 1) {
796
		$result[] = long2ip($net | $i);
797
	}
798
	return $result;
799
}
800

    
801
/* find out whether two subnets overlap */
802
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
803

    
804
	if (!is_numeric($bits1)) {
805
		$bits1 = 32;
806
	}
807
	if (!is_numeric($bits2)) {
808
		$bits2 = 32;
809
	}
810

    
811
	if ($bits1 < $bits2) {
812
		$relbits = $bits1;
813
	} else {
814
		$relbits = $bits2;
815
	}
816

    
817
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
818
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
819

    
820
	return ($sn1 == $sn2);
821
}
822

    
823
/* find out whether two IPv6 subnets overlap */
824
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
825
	$sub1_min = gen_subnetv6($subnet1, $bits1);
826
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
827
	$sub2_min = gen_subnetv6($subnet2, $bits2);
828
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
829

    
830
	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));
831
}
832

    
833
/* return true if $addr is in $subnet, false if not */
834
function ip_in_subnet($addr, $subnet) {
835
	if (is_ipaddrv6($addr) && is_subnetv6($subnet)) {
836
		return (Net_IPv6::isInNetmask($addr, $subnet));
837
	} else if (is_ipaddrv4($addr) && is_subnetv4($subnet)) {
838
		list($ip, $mask) = explode('/', $subnet);
839
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
840
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
841
	}
842
	return false;
843
}
844

    
845
/* returns true if $hostname is just a valid hostname (top part without any of the domain part) */
846
function is_unqualified_hostname($hostname) {
847
	if (!is_string($hostname)) {
848
		return false;
849
	}
850

    
851
	if (preg_match('/^(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname)) {
852
		return true;
853
	} else {
854
		return false;
855
	}
856
}
857

    
858
/* returns true if $hostname is a valid hostname, with or without being a fully-qualified domain name. */
859
function is_hostname($hostname, $allow_wildcard=false) {
860
	if (!is_string($hostname)) {
861
		return false;
862
	}
863

    
864
	if (is_domain($hostname, $allow_wildcard)) {
865
		if ((substr_count($hostname, ".") == 1) && ($hostname[strlen($hostname)-1] == ".")) {
866
			/* Only a single dot at the end like "test." - hosts cannot be directly in the root domain. */
867
			return false;
868
		} else {
869
			return true;
870
		}
871
	} else {
872
		return false;
873
	}
874
}
875

    
876
/* returns true if $domain is a valid domain name */
877
function is_domain($domain, $allow_wildcard=false) {
878
	if (!is_string($domain)) {
879
		return false;
880
	}
881
	if ($allow_wildcard) {
882
		$domain_regex = '/^(?:(?:[a-z_0-9\*]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9])\.)*(?:[a-z_0-9]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9\.])$/i';
883
	} else {
884
		$domain_regex = '/^(?:(?:[a-z_0-9]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9])\.)*(?:[a-z_0-9]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9\.])$/i';
885
	}
886

    
887
	if (preg_match($domain_regex, $domain)) {
888
		return true;
889
	} else {
890
		return false;
891
	}
892
}
893

    
894
/* returns true if $macaddr is a valid MAC address */
895
function is_macaddr($macaddr, $partial=false) {
896
	$repeat = ($partial) ? '1,5' : '5';
897
	return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false;
898
}
899

    
900
/* returns true if $name is a valid name for an alias
901
   returns NULL if a reserved word is used
902
   returns FALSE for bad chars in the name - this allows calling code to determine what the problem was.
903
   aliases cannot be:
904
	bad chars: anything except a-z 0-9 and underscore
905
	bad names: empty string, pure numeric, pure underscore
906
	reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and  "pass" */
907

    
908
function is_validaliasname($name) {
909
	/* Array of reserved words */
910
	$reserved = array("port", "pass");
911

    
912
	if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) {
913
		return false;
914
	}
915
	if (in_array($name, $reserved, true) || getservbyname($name, "tcp") || getservbyname($name, "udp") || getprotobyname($name)) {
916
		return; /* return NULL */
917
	}
918
	return true;
919
}
920

    
921
/* returns true if $port is a valid TCP/UDP port */
922
function is_port($port) {
923
	if (ctype_digit($port) && ((intval($port) >= 1) && (intval($port) <= 65535))) {
924
		return true;
925
	}
926
	if (getservbyname($port, "tcp") || getservbyname($port, "udp")) {
927
		return true;
928
	}
929
	return false;
930
}
931

    
932
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
933
function is_portrange($portrange) {
934
	$ports = explode(":", $portrange);
935

    
936
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
937
}
938

    
939
/* returns true if $port is a valid port number or an alias thereof */
940
function is_portoralias($port) {
941
	global $config;
942

    
943
	if (is_alias($port)) {
944
		if (is_array($config['aliases']['alias'])) {
945
			foreach ($config['aliases']['alias'] as $alias) {
946
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type'])) {
947
					return true;
948
				}
949
			}
950
		}
951
		return false;
952
	} else {
953
		return is_port($port);
954
	}
955
}
956

    
957
/* create ranges of sequential port numbers (200:215) and remove duplicates */
958
function group_ports($ports) {
959
	if (!is_array($ports) || empty($ports)) {
960
		return;
961
	}
962

    
963
	$uniq = array();
964
	foreach ($ports as $port) {
965
		if (is_portrange($port)) {
966
			list($begin, $end) = explode(":", $port);
967
			if ($begin > $end) {
968
				$aux = $begin;
969
				$begin = $end;
970
				$end = $aux;
971
			}
972
			for ($i = $begin; $i <= $end; $i++) {
973
				if (!in_array($i, $uniq)) {
974
					$uniq[] = $i;
975
				}
976
			}
977
		} else if (is_port($port)) {
978
			if (!in_array($port, $uniq)) {
979
				$uniq[] = $port;
980
			}
981
		}
982
	}
983
	sort($uniq, SORT_NUMERIC);
984

    
985
	$result = array();
986
	foreach ($uniq as $idx => $port) {
987
		if ($idx == 0) {
988
			$result[] = $port;
989
			continue;
990
		}
991

    
992
		$last = end($result);
993
		if (is_portrange($last)) {
994
			list($begin, $end) = explode(":", $last);
995
		} else {
996
			$begin = $end = $last;
997
		}
998

    
999
		if ($port == ($end+1)) {
1000
			$end++;
1001
			$result[count($result)-1] = "{$begin}:{$end}";
1002
		} else {
1003
			$result[] = $port;
1004
		}
1005
	}
1006

    
1007
	return $result;
1008
}
1009

    
1010
/* returns true if $val is a valid shaper bandwidth value */
1011
function is_valid_shaperbw($val) {
1012
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
1013
}
1014

    
1015
/* returns true if $test is in the range between $start and $end */
1016
function is_inrange_v4($test, $start, $end) {
1017
	if ((ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start))) {
1018
		return true;
1019
	} else {
1020
		return false;
1021
	}
1022
}
1023

    
1024
/* returns true if $test is in the range between $start and $end */
1025
function is_inrange_v6($test, $start, $end) {
1026
	if ((inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start))) {
1027
		return true;
1028
	} else {
1029
		return false;
1030
	}
1031
}
1032

    
1033
/* returns true if $test is in the range between $start and $end */
1034
function is_inrange($test, $start, $end) {
1035
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
1036
}
1037

    
1038
/* XXX: return the configured carp interface list */
1039
function get_configured_carp_interface_list($carpinterface = '', $family = 'inet', $what = 'ip') {
1040
	global $config;
1041

    
1042
	$iflist = array();
1043

    
1044
	if (is_array($config['virtualip']['vip'])) {
1045
		$viparr = &$config['virtualip']['vip'];
1046
		foreach ($viparr as $vip) {
1047
			switch ($vip['mode']) {
1048
				case "carp":
1049
					if (!empty($carpinterface)) {
1050
						if ($carpinterface == "_vip{$vip['uniqid']}") {
1051
							switch ($what) {
1052
								case 'subnet':
1053
									if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1054
										return $vip['subnet_bits'];
1055
									} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1056
										return $vip['subnet_bits'];
1057
									}
1058
									break;
1059
								case 'iface':
1060
									if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1061
										return $vip['interface'];
1062
									} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1063
										return $vip['interface'];
1064
									}
1065
									break;
1066
								case 'vip':
1067
									if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1068
										return $vip;
1069
									} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1070
										return $vip;
1071
									}
1072
									break;
1073
								case 'ip':
1074
								default:
1075
									if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1076
										return $vip['subnet'];
1077
									} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1078
										return $vip['subnet'];
1079
									}
1080
									break;
1081
							}
1082
						}
1083
					} else {
1084
						$iflist["_vip{$vip['uniqid']}"] = $vip['subnet'];
1085
					}
1086
					break;
1087
			}
1088
		}
1089
	}
1090

    
1091
	return $iflist;
1092
}
1093

    
1094
/* return the configured IP aliases list */
1095
function get_configured_ip_aliases_list($returnfullentry = false) {
1096
	global $config;
1097

    
1098
	$alias_list = array();
1099

    
1100
	if (is_array($config['virtualip']['vip'])) {
1101
		$viparr = &$config['virtualip']['vip'];
1102
		foreach ($viparr as $vip) {
1103
			if ($vip['mode'] == "ipalias") {
1104
				if ($returnfullentry) {
1105
					$alias_list[$vip['subnet']] = $vip;
1106
				} else {
1107
					$alias_list[$vip['subnet']] = $vip['interface'];
1108
				}
1109
			}
1110
		}
1111
	}
1112

    
1113
	return $alias_list;
1114
}
1115

    
1116
/* return all configured aliases list (IP, carp, proxyarp and other) */
1117
function get_configured_vips_list() {
1118
	global $config;
1119

    
1120
	$alias_list = array();
1121

    
1122
	if (is_array($config['virtualip']['vip'])) {
1123
		$viparr = &$config['virtualip']['vip'];
1124
		foreach ($viparr as $vip) {
1125
			if ($vip['mode'] == "carp") {
1126
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => "{$vip['interface']}_vip{$vip['vhid']}");
1127
			} else {
1128
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
1129
			}
1130
		}
1131
	}
1132

    
1133
	return $alias_list;
1134
}
1135

    
1136
/* comparison function for sorting by the order in which interfaces are normally created */
1137
function compare_interface_friendly_names($a, $b) {
1138
	if ($a == $b) {
1139
		return 0;
1140
	} else if ($a == 'wan') {
1141
		return -1;
1142
	} else if ($b == 'wan') {
1143
		return 1;
1144
	} else if ($a == 'lan') {
1145
		return -1;
1146
	} else if ($b == 'lan') {
1147
		return 1;
1148
	}
1149

    
1150
	return strnatcmp($a, $b);
1151
}
1152

    
1153
/* return the configured interfaces list. */
1154
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
1155
	global $config;
1156

    
1157
	$iflist = array();
1158

    
1159
	/* if list */
1160
	foreach ($config['interfaces'] as $if => $ifdetail) {
1161
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1162
			continue;
1163
		}
1164
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1165
			$iflist[$if] = $if;
1166
		}
1167
	}
1168

    
1169
	return $iflist;
1170
}
1171

    
1172
/* return the configured interfaces list. */
1173
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
1174
	global $config;
1175

    
1176
	$iflist = array();
1177

    
1178
	/* if list */
1179
	foreach ($config['interfaces'] as $if => $ifdetail) {
1180
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1181
			continue;
1182
		}
1183
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1184
			$tmpif = get_real_interface($if);
1185
			if (!empty($tmpif)) {
1186
				$iflist[$tmpif] = $if;
1187
			}
1188
		}
1189
	}
1190

    
1191
	return $iflist;
1192
}
1193

    
1194
/* return the configured interfaces list with their description. */
1195
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
1196
	global $config;
1197

    
1198
	$iflist = array();
1199

    
1200
	/* if list */
1201
	foreach ($config['interfaces'] as $if => $ifdetail) {
1202
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1203
			continue;
1204
		}
1205
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1206
			if (empty($ifdetail['descr'])) {
1207
				$iflist[$if] = strtoupper($if);
1208
			} else {
1209
				$iflist[$if] = strtoupper($ifdetail['descr']);
1210
			}
1211
		}
1212
	}
1213

    
1214
	return $iflist;
1215
}
1216

    
1217
/*
1218
 *   get_configured_ip_addresses() - Return a list of all configured
1219
 *   interfaces IP Addresses
1220
 *
1221
 */
1222
function get_configured_ip_addresses() {
1223
	global $config;
1224

    
1225
	if (!function_exists('get_interface_ip')) {
1226
		require_once("interfaces.inc");
1227
	}
1228
	$ip_array = array();
1229
	$interfaces = get_configured_interface_list();
1230
	if (is_array($interfaces)) {
1231
		foreach ($interfaces as $int) {
1232
			$ipaddr = get_interface_ip($int);
1233
			$ip_array[$int] = $ipaddr;
1234
		}
1235
	}
1236
	$interfaces = get_configured_carp_interface_list();
1237
	if (is_array($interfaces)) {
1238
		foreach ($interfaces as $int => $ipaddr) {
1239
			$ip_array[$int] = $ipaddr;
1240
		}
1241
	}
1242

    
1243
	/* pppoe server */
1244
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
1245
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
1246
			if ($pppoe['mode'] == "server") {
1247
				if (is_ipaddr($pppoe['localip'])) {
1248
					$int = "pppoes". $pppoe['pppoeid'];
1249
					$ip_array[$int] = $pppoe['localip'];
1250
				}
1251
			}
1252
		}
1253
	}
1254

    
1255
	return $ip_array;
1256
}
1257

    
1258
/*
1259
 *   get_configured_ipv6_addresses() - Return a list of all configured
1260
 *   interfaces IPv6 Addresses
1261
 *
1262
 */
1263
function get_configured_ipv6_addresses() {
1264
	require_once("interfaces.inc");
1265
	$ipv6_array = array();
1266
	$interfaces = get_configured_interface_list();
1267
	if (is_array($interfaces)) {
1268
		foreach ($interfaces as $int) {
1269
			$ipaddrv6 = get_interface_ipv6($int);
1270
			$ipv6_array[$int] = $ipaddrv6;
1271
		}
1272
	}
1273
	$interfaces = get_configured_carp_interface_list();
1274
	if (is_array($interfaces)) {
1275
		foreach ($interfaces as $int => $ipaddrv6) {
1276
			$ipv6_array[$int] = $ipaddrv6;
1277
		}
1278
	}
1279
	return $ipv6_array;
1280
}
1281

    
1282
/*
1283
 *   get_interface_list() - Return a list of all physical interfaces
1284
 *   along with MAC and status.
1285
 *
1286
 *   $mode = "active" - use ifconfig -lu
1287
 *           "media"  - use ifconfig to check physical connection
1288
 *			status (much slower)
1289
 */
1290
function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") {
1291
	global $config;
1292
	$upints = array();
1293
	/* get a list of virtual interface types */
1294
	if (!$vfaces) {
1295
		$vfaces = array(
1296
				'bridge',
1297
				'ppp',
1298
				'pppoe',
1299
				'pptp',
1300
				'l2tp',
1301
				'sl',
1302
				'gif',
1303
				'gre',
1304
				'faith',
1305
				'lo',
1306
				'ng',
1307
				'_vlan',
1308
				'_wlan',
1309
				'pflog',
1310
				'plip',
1311
				'pfsync',
1312
				'enc',
1313
				'tun',
1314
				'carp',
1315
				'lagg',
1316
				'vip',
1317
				'ipfw'
1318
		);
1319
	}
1320
	switch ($mode) {
1321
		case "active":
1322
			$upints = pfSense_interface_listget(IFF_UP);
1323
			break;
1324
		case "media":
1325
			$intlist = pfSense_interface_listget();
1326
			$ifconfig = "";
1327
			exec("/sbin/ifconfig -a", $ifconfig);
1328
			$regexp = '/(' . implode('|', $intlist) . '):\s/';
1329
			$ifstatus = preg_grep('/status:/', $ifconfig);
1330
			foreach ($ifstatus as $status) {
1331
				$int = array_shift($intlist);
1332
				if (stristr($status, "active")) {
1333
					$upints[] = $int;
1334
				}
1335
			}
1336
			break;
1337
		default:
1338
			$upints = pfSense_interface_listget();
1339
			break;
1340
	}
1341
	/* build interface list with netstat */
1342
	$linkinfo = "";
1343
	exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo);
1344
	array_shift($linkinfo);
1345
	/* build ip address list with netstat */
1346
	$ipinfo = "";
1347
	exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo);
1348
	array_shift($ipinfo);
1349
	foreach ($linkinfo as $link) {
1350
		$friendly = "";
1351
		$alink = explode(" ", $link);
1352
		$ifname = rtrim(trim($alink[0]), '*');
1353
		/* trim out all numbers before checking for vfaces */
1354
		if (!in_array(array_shift(preg_split('/\d/', $ifname)), $vfaces) &&
1355
		    !stristr($ifname, "_vlan") && !stristr($ifname, "_wlan")) {
1356
			$toput = array(
1357
					"mac" => trim($alink[1]),
1358
					"up" => in_array($ifname, $upints)
1359
				);
1360
			foreach ($ipinfo as $ip) {
1361
				$aip = explode(" ", $ip);
1362
				if ($aip[0] == $ifname) {
1363
					$toput['ipaddr'] = $aip[1];
1364
				}
1365
			}
1366
			if (is_array($config['interfaces'])) {
1367
				foreach ($config['interfaces'] as $name => $int) {
1368
					if ($int['if'] == $ifname) {
1369
						$friendly = $name;
1370
					}
1371
				}
1372
			}
1373
			switch ($keyby) {
1374
			case "physical":
1375
				if ($friendly != "") {
1376
					$toput['friendly'] = $friendly;
1377
				}
1378
				$dmesg_arr = array();
1379
				exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr);
1380
				preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg);
1381
				$toput['dmesg'] = $dmesg[1][0];
1382
				$iflist[$ifname] = $toput;
1383
				break;
1384
			case "ppp":
1385

    
1386
			case "friendly":
1387
				if ($friendly != "") {
1388
					$toput['if'] = $ifname;
1389
					$iflist[$friendly] = $toput;
1390
				}
1391
				break;
1392
			}
1393
		}
1394
	}
1395
	return $iflist;
1396
}
1397

    
1398
/****f* util/log_error
1399
* NAME
1400
*   log_error  - Sends a string to syslog.
1401
* INPUTS
1402
*   $error     - string containing the syslog message.
1403
* RESULT
1404
*   null
1405
******/
1406
function log_error($error) {
1407
	global $g;
1408
	$page = $_SERVER['SCRIPT_NAME'];
1409
	if (empty($page)) {
1410
		$files = get_included_files();
1411
		$page = basename($files[0]);
1412
	}
1413
	syslog(LOG_ERR, "$page: $error");
1414
	if ($g['debug']) {
1415
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1416
	}
1417
	return;
1418
}
1419

    
1420
/****f* util/log_auth
1421
* NAME
1422
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1423
* INPUTS
1424
*   $error     - string containing the syslog message.
1425
* RESULT
1426
*   null
1427
******/
1428
function log_auth($error) {
1429
	global $g;
1430
	$page = $_SERVER['SCRIPT_NAME'];
1431
	syslog(LOG_AUTH, "$page: $error");
1432
	if ($g['debug']) {
1433
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1434
	}
1435
	return;
1436
}
1437

    
1438
/****f* util/exec_command
1439
 * NAME
1440
 *   exec_command - Execute a command and return a string of the result.
1441
 * INPUTS
1442
 *   $command   - String of the command to be executed.
1443
 * RESULT
1444
 *   String containing the command's result.
1445
 * NOTES
1446
 *   This function returns the command's stdout and stderr.
1447
 ******/
1448
function exec_command($command) {
1449
	$output = array();
1450
	exec($command . ' 2>&1', $output);
1451
	return(implode("\n", $output));
1452
}
1453

    
1454
/* wrapper for exec()
1455
   Executes in background or foreground.
1456
   For background execution, returns PID of background process to allow calling code control */
1457
function mwexec($command, $nologentry = false, $clearsigmask = false, $background = false) {
1458
	global $g;
1459
	$retval = 0;
1460

    
1461
	if ($g['debug']) {
1462
		if (!$_SERVER['REMOTE_ADDR']) {
1463
			echo "mwexec(): $command" . ($background ? " [BG]":"") . "\n";
1464
		}
1465
	}
1466
	if ($clearsigmask) {
1467
		$oldset = array();
1468
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1469
	}
1470

    
1471
	if ($background) {
1472
		// start background process and return PID
1473
		$retval = exec("/usr/bin/nohup $command > /dev/null 2>&1 & echo $!");
1474
	} else {
1475
		// run in foreground, and (optionally) log if nonzero return
1476
		$outputarray = array();
1477
		exec("$command 2>&1", $outputarray, $retval);
1478
		if (($retval <> 0) && (!$nologentry || isset($config['system']['developerspew'])))
1479
			log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, implode(" ", $outputarray)));
1480
	}
1481

    
1482
	if ($clearsigmask) {
1483
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1484
	}
1485

    
1486
	return $retval;
1487
}
1488

    
1489
/* wrapper for exec() in background */
1490
function mwexec_bg($command, $clearsigmask = false) {
1491
	return mwexec($command, false, $clearsigmask, true);
1492
}
1493

    
1494
/* unlink a file, if it exists */
1495
function unlink_if_exists($fn) {
1496
	$to_do = glob($fn);
1497
	if (is_array($to_do)) {
1498
		foreach ($to_do as $filename) {
1499
			@unlink($filename);
1500
		}
1501
	} else {
1502
		@unlink($fn);
1503
	}
1504
}
1505
/* make a global alias table (for faster lookups) */
1506
function alias_make_table($config) {
1507
	global $aliastable;
1508

    
1509
	$aliastable = array();
1510

    
1511
	if (is_array($config['aliases']['alias'])) {
1512
		foreach ($config['aliases']['alias'] as $alias) {
1513
			if ($alias['name']) {
1514
				$aliastable[$alias['name']] = $alias['address'];
1515
			}
1516
		}
1517
	}
1518
}
1519

    
1520
/* check if an alias exists */
1521
function is_alias($name) {
1522
	global $aliastable;
1523

    
1524
	return isset($aliastable[$name]);
1525
}
1526

    
1527
function alias_get_type($name) {
1528
	global $config;
1529

    
1530
	if (is_array($config['aliases']['alias'])) {
1531
		foreach ($config['aliases']['alias'] as $alias) {
1532
			if ($name == $alias['name']) {
1533
				return $alias['type'];
1534
			}
1535
		}
1536
	}
1537

    
1538
	return "";
1539
}
1540

    
1541
/* expand a host or network alias, if necessary */
1542
function alias_expand($name) {
1543
	global $aliastable;
1544

    
1545
	if (isset($aliastable[$name])) {
1546
		// alias names cannot be strictly numeric. redmine #4289
1547
		if (is_numericint($name)) {
1548
			return null;
1549
		}
1550
		return "\${$name}";
1551
	} else if (is_ipaddr($name) || is_subnet($name) || is_port($name) || is_portrange($name)) {
1552
		return "{$name}";
1553
	} else {
1554
		return null;
1555
	}
1556
}
1557

    
1558
function alias_expand_urltable($name) {
1559
	global $config;
1560
	$urltable_prefix = "/var/db/aliastables/";
1561
	$urltable_filename = $urltable_prefix . $name . ".txt";
1562

    
1563
	if (is_array($config['aliases']['alias'])) {
1564
		foreach ($config['aliases']['alias'] as $alias) {
1565
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1566
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename)) {
1567
					return $urltable_filename;
1568
				} else {
1569
					send_event("service sync alias {$name}");
1570
					break;
1571
				}
1572
			}
1573
		}
1574
	}
1575
	return null;
1576
}
1577

    
1578
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1579
function verify_digital_signature($fname) {
1580
	global $g;
1581

    
1582
	if (!file_exists("/usr/local/sbin/gzsig")) {
1583
		return 4;
1584
	}
1585

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

    
1589
/* obtain MAC address given an IP address by looking at the ARP table */
1590
function arp_get_mac_by_ip($ip) {
1591
	mwexec("/sbin/ping -c 1 -t 1 " . escapeshellarg($ip), true);
1592
	$arpoutput = "";
1593
	exec("/usr/sbin/arp -n " . escapeshellarg($ip), $arpoutput);
1594

    
1595
	if ($arpoutput[0]) {
1596
		$arpi = explode(" ", $arpoutput[0]);
1597
		$macaddr = $arpi[3];
1598
		if (is_macaddr($macaddr)) {
1599
			return $macaddr;
1600
		} else {
1601
			return false;
1602
		}
1603
	}
1604

    
1605
	return false;
1606
}
1607

    
1608
/* return a fieldname that is safe for xml usage */
1609
function xml_safe_fieldname($fieldname) {
1610
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1611
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1612
			 ':', ',', '.', '\'', '\\'
1613
		);
1614
	return strtolower(str_replace($replace, "", $fieldname));
1615
}
1616

    
1617
function mac_format($clientmac) {
1618
	global $config, $cpzone;
1619

    
1620
	$mac = explode(":", $clientmac);
1621
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1622

    
1623
	switch ($mac_format) {
1624
		case 'singledash':
1625
			return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1626

    
1627
		case 'ietf':
1628
			return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1629

    
1630
		case 'cisco':
1631
			return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1632

    
1633
		case 'unformatted':
1634
			return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1635

    
1636
		default:
1637
			return $clientmac;
1638
	}
1639
}
1640

    
1641
function resolve_retry($hostname, $retries = 5) {
1642

    
1643
	if (is_ipaddr($hostname)) {
1644
		return $hostname;
1645
	}
1646

    
1647
	for ($i = 0; $i < $retries; $i++) {
1648
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1649
		$ip = gethostbyname($hostname);
1650

    
1651
		if ($ip && $ip != $hostname) {
1652
			/* success */
1653
			return $ip;
1654
		}
1655

    
1656
		sleep(1);
1657
	}
1658

    
1659
	return false;
1660
}
1661

    
1662
function format_bytes($bytes) {
1663
	if ($bytes >= 1073741824) {
1664
		return sprintf("%.2f GB", $bytes/1073741824);
1665
	} else if ($bytes >= 1048576) {
1666
		return sprintf("%.2f MB", $bytes/1048576);
1667
	} else if ($bytes >= 1024) {
1668
		return sprintf("%.0f KB", $bytes/1024);
1669
	} else {
1670
		return sprintf("%d bytes", $bytes);
1671
	}
1672
}
1673

    
1674
function update_filter_reload_status($text) {
1675
	global $g;
1676

    
1677
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1678
}
1679

    
1680
/****** util/return_dir_as_array
1681
 * NAME
1682
 *   return_dir_as_array - Return a directory's contents as an array.
1683
 * INPUTS
1684
 *   $dir          - string containing the path to the desired directory.
1685
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1686
 * RESULT
1687
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1688
 ******/
1689
function return_dir_as_array($dir, $filter_regex = '') {
1690
	$dir_array = array();
1691
	if (is_dir($dir)) {
1692
		if ($dh = opendir($dir)) {
1693
			while (($file = readdir($dh)) !== false) {
1694
				if (($file == ".") || ($file == "..")) {
1695
					continue;
1696
				}
1697

    
1698
				if (empty($filter_regex) || preg_match($filter_regex, $file)) {
1699
					array_push($dir_array, $file);
1700
				}
1701
			}
1702
			closedir($dh);
1703
		}
1704
	}
1705
	return $dir_array;
1706
}
1707

    
1708
function run_plugins($directory) {
1709
	global $config, $g;
1710

    
1711
	/* process packager manager custom rules */
1712
	$files = return_dir_as_array($directory);
1713
	if (is_array($files)) {
1714
		foreach ($files as $file) {
1715
			if (stristr($file, ".sh") == true) {
1716
				mwexec($directory . $file . " start");
1717
			} else if (!is_dir($directory . "/" . $file) && stristr($file, ".inc")) {
1718
				require_once($directory . "/" . $file);
1719
			}
1720
		}
1721
	}
1722
}
1723

    
1724
/*
1725
 *    safe_mkdir($path, $mode = 0755)
1726
 *    create directory if it doesn't already exist and isn't a file!
1727
 */
1728
function safe_mkdir($path, $mode = 0755) {
1729
	global $g;
1730

    
1731
	if (!is_file($path) && !is_dir($path)) {
1732
		return @mkdir($path, $mode, true);
1733
	} else {
1734
		return false;
1735
	}
1736
}
1737

    
1738
/*
1739
 * get_sysctl($names)
1740
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1741
 * name) and return an array of key/value pairs set for those that exist
1742
 */
1743
function get_sysctl($names) {
1744
	if (empty($names)) {
1745
		return array();
1746
	}
1747

    
1748
	if (is_array($names)) {
1749
		$name_list = array();
1750
		foreach ($names as $name) {
1751
			$name_list[] = escapeshellarg($name);
1752
		}
1753
	} else {
1754
		$name_list = array(escapeshellarg($names));
1755
	}
1756

    
1757
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1758
	$values = array();
1759
	foreach ($output as $line) {
1760
		$line = explode(": ", $line, 2);
1761
		if (count($line) == 2) {
1762
			$values[$line[0]] = $line[1];
1763
		}
1764
	}
1765

    
1766
	return $values;
1767
}
1768

    
1769
/*
1770
 * get_single_sysctl($name)
1771
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
1772
 * return the value for sysctl $name or empty string if it doesn't exist
1773
 */
1774
function get_single_sysctl($name) {
1775
	if (empty($name)) {
1776
		return "";
1777
	}
1778

    
1779
	$value = get_sysctl($name);
1780
	if (empty($value) || !isset($value[$name])) {
1781
		return "";
1782
	}
1783

    
1784
	return $value[$name];
1785
}
1786

    
1787
/*
1788
 * set_sysctl($value_list)
1789
 * Set sysctl OID's listed as key/value pairs and return
1790
 * an array with keys set for those that succeeded
1791
 */
1792
function set_sysctl($values) {
1793
	if (empty($values)) {
1794
		return array();
1795
	}
1796

    
1797
	$value_list = array();
1798
	foreach ($values as $key => $value) {
1799
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1800
	}
1801

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

    
1804
	/* Retry individually if failed (one or more read-only) */
1805
	if ($success <> 0 && count($value_list) > 1) {
1806
		foreach ($value_list as $value) {
1807
			exec("/sbin/sysctl -i " . $value, $output);
1808
		}
1809
	}
1810

    
1811
	$ret = array();
1812
	foreach ($output as $line) {
1813
		$line = explode(": ", $line, 2);
1814
		if (count($line) == 2) {
1815
			$ret[$line[0]] = true;
1816
		}
1817
	}
1818

    
1819
	return $ret;
1820
}
1821

    
1822
/*
1823
 * set_single_sysctl($name, $value)
1824
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
1825
 * returns boolean meaning if it succeeded
1826
 */
1827
function set_single_sysctl($name, $value) {
1828
	if (empty($name)) {
1829
		return false;
1830
	}
1831

    
1832
	$result = set_sysctl(array($name => $value));
1833

    
1834
	if (!isset($result[$name]) || $result[$name] != $value) {
1835
		return false;
1836
	}
1837

    
1838
	return true;
1839
}
1840

    
1841
/*
1842
 *     get_memory()
1843
 *     returns an array listing the amount of
1844
 *     memory installed in the hardware
1845
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
1846
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
1847
 */
1848
function get_memory() {
1849
	$physmem = get_single_sysctl("hw.physmem");
1850
	$realmem = get_single_sysctl("hw.realmem");
1851
	/* convert from bytes to megabytes */
1852
	return array(($physmem/1048576), ($realmem/1048576));
1853
}
1854

    
1855
function mute_kernel_msgs() {
1856
	global $g, $config;
1857
	// Do not mute serial console.  The kernel gets very very cranky
1858
	// and will start dishing you cannot control tty errors.
1859
	if ($g['platform'] == 'nanobsd') {
1860
		return;
1861
	}
1862
	if ($config['system']['enableserial']) {
1863
		return;
1864
	}
1865
	exec("/sbin/conscontrol mute on");
1866
}
1867

    
1868
function unmute_kernel_msgs() {
1869
	global $g;
1870
	// Do not mute serial console.  The kernel gets very very cranky
1871
	// and will start dishing you cannot control tty errors.
1872
	if ($g['platform'] == 'nanobsd') {
1873
		return;
1874
	}
1875
	exec("/sbin/conscontrol mute off");
1876
}
1877

    
1878
function start_devd() {
1879
	/* Use the undocumented -q options of devd to quiet its log spamming */
1880
	$_gb = exec("/sbin/devd -q");
1881
	sleep(1);
1882
	unset($_gb);
1883
}
1884

    
1885
function is_interface_vlan_mismatch() {
1886
	global $config, $g;
1887

    
1888
	if (is_array($config['vlans']['vlan'])) {
1889
		foreach ($config['vlans']['vlan'] as $vlan) {
1890
			if (does_interface_exist($vlan['if']) == false) {
1891
				return true;
1892
			}
1893
		}
1894
	}
1895

    
1896
	return false;
1897
}
1898

    
1899
function is_interface_mismatch() {
1900
	global $config, $g;
1901

    
1902
	$do_assign = false;
1903
	$i = 0;
1904
	$missing_interfaces = array();
1905
	if (is_array($config['interfaces'])) {
1906
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1907
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1908
				// Do not check these interfaces.
1909
				$i++;
1910
				continue;
1911
			} else if (does_interface_exist($ifcfg['if']) == false) {
1912
				$missing_interfaces[] = $ifcfg['if'];
1913
				$do_assign = true;
1914
			} else {
1915
				$i++;
1916
			}
1917
		}
1918
	}
1919

    
1920
	if (file_exists("{$g['tmp_path']}/assign_complete")) {
1921
		$do_assign = false;
1922
	}
1923

    
1924
	if (!empty($missing_interfaces) && $do_assign) {
1925
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1926
	} else {
1927
		@unlink("{$g['tmp_path']}/missing_interfaces");
1928
	}
1929

    
1930
	return $do_assign;
1931
}
1932

    
1933
/* sync carp entries to other firewalls */
1934
function carp_sync_client() {
1935
	global $g;
1936
	send_event("filter sync");
1937
}
1938

    
1939
/****f* util/isAjax
1940
 * NAME
1941
 *   isAjax - reports if the request is driven from prototype
1942
 * INPUTS
1943
 *   none
1944
 * RESULT
1945
 *   true/false
1946
 ******/
1947
function isAjax() {
1948
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
1949
}
1950

    
1951
/****f* util/timeout
1952
 * NAME
1953
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
1954
 * INPUTS
1955
 *   optional, seconds to wait before timeout. Default 9 seconds.
1956
 * RESULT
1957
 *   returns 1 char of user input or null if no input.
1958
 ******/
1959
function timeout($timer = 9) {
1960
	while (!isset($key)) {
1961
		if ($timer >= 9) {
1962
			echo chr(8) . chr(8) . ($timer == 9 ? chr(32) : null) . "{$timer}";
1963
		} else {
1964
			echo chr(8). "{$timer}";
1965
		}
1966
		`/bin/stty -icanon min 0 time 25`;
1967
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
1968
		`/bin/stty icanon`;
1969
		if ($key == '') {
1970
			unset($key);
1971
		}
1972
		$timer--;
1973
		if ($timer == 0) {
1974
			break;
1975
		}
1976
	}
1977
	return $key;
1978
}
1979

    
1980
/****f* util/msort
1981
 * NAME
1982
 *   msort - sort array
1983
 * INPUTS
1984
 *   $array to be sorted, field to sort by, direction of sort
1985
 * RESULT
1986
 *   returns newly sorted array
1987
 ******/
1988
function msort($array, $id = "id", $sort_ascending = true) {
1989
	$temp_array = array();
1990
	while (count($array)>0) {
1991
		$lowest_id = 0;
1992
		$index = 0;
1993
		foreach ($array as $item) {
1994
			if (isset($item[$id])) {
1995
				if ($array[$lowest_id][$id]) {
1996
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
1997
						$lowest_id = $index;
1998
					}
1999
				}
2000
			}
2001
			$index++;
2002
		}
2003
		$temp_array[] = $array[$lowest_id];
2004
		$array = array_merge(array_slice($array, 0, $lowest_id), array_slice($array, $lowest_id + 1));
2005
	}
2006
	if ($sort_ascending) {
2007
		return $temp_array;
2008
	} else {
2009
		return array_reverse($temp_array);
2010
	}
2011
}
2012

    
2013
/****f* util/is_URL
2014
 * NAME
2015
 *   is_URL
2016
 * INPUTS
2017
 *   string to check
2018
 * RESULT
2019
 *   Returns true if item is a URL
2020
 ******/
2021
function is_URL($url) {
2022
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
2023
	if ($match) {
2024
		return true;
2025
	}
2026
	return false;
2027
}
2028

    
2029
function is_file_included($file = "") {
2030
	$files = get_included_files();
2031
	if (in_array($file, $files)) {
2032
		return true;
2033
	}
2034

    
2035
	return false;
2036
}
2037

    
2038
/*
2039
 * Replace a value on a deep associative array using regex
2040
 */
2041
function array_replace_values_recursive($data, $match, $replace) {
2042
	if (empty($data)) {
2043
		return $data;
2044
	}
2045

    
2046
	if (is_string($data)) {
2047
		$data = preg_replace("/{$match}/", $replace, $data);
2048
	} else if (is_array($data)) {
2049
		foreach ($data as $k => $v) {
2050
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
2051
		}
2052
	}
2053

    
2054
	return $data;
2055
}
2056

    
2057
/*
2058
	This function was borrowed from a comment on PHP.net at the following URL:
2059
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
2060
 */
2061
function array_merge_recursive_unique($array0, $array1) {
2062

    
2063
	$arrays = func_get_args();
2064
	$remains = $arrays;
2065

    
2066
	// We walk through each arrays and put value in the results (without
2067
	// considering previous value).
2068
	$result = array();
2069

    
2070
	// loop available array
2071
	foreach ($arrays as $array) {
2072

    
2073
		// The first remaining array is $array. We are processing it. So
2074
		// we remove it from remaining arrays.
2075
		array_shift($remains);
2076

    
2077
		// We don't care non array param, like array_merge since PHP 5.0.
2078
		if (is_array($array)) {
2079
			// Loop values
2080
			foreach ($array as $key => $value) {
2081
				if (is_array($value)) {
2082
					// we gather all remaining arrays that have such key available
2083
					$args = array();
2084
					foreach ($remains as $remain) {
2085
						if (array_key_exists($key, $remain)) {
2086
							array_push($args, $remain[$key]);
2087
						}
2088
					}
2089

    
2090
					if (count($args) > 2) {
2091
						// put the recursion
2092
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
2093
					} else {
2094
						foreach ($value as $vkey => $vval) {
2095
							$result[$key][$vkey] = $vval;
2096
						}
2097
					}
2098
				} else {
2099
					// simply put the value
2100
					$result[$key] = $value;
2101
				}
2102
			}
2103
		}
2104
	}
2105
	return $result;
2106
}
2107

    
2108

    
2109
/*
2110
 * converts a string like "a,b,c,d"
2111
 * into an array like array("a" => "b", "c" => "d")
2112
 */
2113
function explode_assoc($delimiter, $string) {
2114
	$array = explode($delimiter, $string);
2115
	$result = array();
2116
	$numkeys = floor(count($array) / 2);
2117
	for ($i = 0; $i < $numkeys; $i += 1) {
2118
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
2119
	}
2120
	return $result;
2121
}
2122

    
2123
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
2124
	global $config, $aliastable;
2125

    
2126
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
2127
	if (!is_array($config['staticroutes']['route'])) {
2128
		return array();
2129
	}
2130

    
2131
	$allstaticroutes = array();
2132
	$allsubnets = array();
2133
	/* Loop through routes and expand aliases as we find them. */
2134
	foreach ($config['staticroutes']['route'] as $route) {
2135
		if (is_alias($route['network'])) {
2136
			if (!isset($aliastable[$route['network']])) {
2137
				continue;
2138
			}
2139

    
2140
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
2141
			foreach ($subnets as $net) {
2142
				if (!is_subnet($net)) {
2143
					if (is_ipaddrv4($net)) {
2144
						$net .= "/32";
2145
					} else if (is_ipaddrv6($net)) {
2146
						$net .= "/128";
2147
					} else if ($returnhostnames === false || !is_fqdn($net)) {
2148
						continue;
2149
					}
2150
				}
2151
				$temproute = $route;
2152
				$temproute['network'] = $net;
2153
				$allstaticroutes[] = $temproute;
2154
				$allsubnets[] = $net;
2155
			}
2156
		} elseif (is_subnet($route['network'])) {
2157
			$allstaticroutes[] = $route;
2158
			$allsubnets[] = $route['network'];
2159
		}
2160
	}
2161
	if ($returnsubnetsonly) {
2162
		return $allsubnets;
2163
	} else {
2164
		return $allstaticroutes;
2165
	}
2166
}
2167

    
2168
/****f* util/get_alias_list
2169
 * NAME
2170
 *   get_alias_list - Provide a list of aliases.
2171
 * INPUTS
2172
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
2173
 * RESULT
2174
 *   Array containing list of aliases.
2175
 *   If $type is unspecified, all aliases are returned.
2176
 *   If $type is a string, all aliases of the type specified in $type are returned.
2177
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
2178
 */
2179
function get_alias_list($type = null) {
2180
	global $config;
2181
	$result = array();
2182
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
2183
		foreach ($config['aliases']['alias'] as $alias) {
2184
			if ($type === null) {
2185
				$result[] = $alias['name'];
2186
			} else if (is_array($type)) {
2187
				if (in_array($alias['type'], $type)) {
2188
					$result[] = $alias['name'];
2189
				}
2190
			} else if ($type === $alias['type']) {
2191
				$result[] = $alias['name'];
2192
			}
2193
		}
2194
	}
2195
	return $result;
2196
}
2197

    
2198
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
2199
function array_exclude($needle, $haystack) {
2200
	$result = array();
2201
	if (is_array($haystack)) {
2202
		foreach ($haystack as $thing) {
2203
			if ($needle !== $thing) {
2204
				$result[] = $thing;
2205
			}
2206
		}
2207
	}
2208
	return $result;
2209
}
2210

    
2211
/* Define what is preferred, IPv4 or IPv6 */
2212
function prefer_ipv4_or_ipv6() {
2213
	global $config;
2214

    
2215
	if (isset($config['system']['prefer_ipv4'])) {
2216
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2217
	} else {
2218
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2219
	}
2220
}
2221

    
2222
/* Redirect to page passing parameters via POST */
2223
function post_redirect($page, $params) {
2224
	if (!is_array($params)) {
2225
		return;
2226
	}
2227

    
2228
	print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n";
2229
	foreach ($params as $key => $value) {
2230
		print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n";
2231
	}
2232
	print "</form><script type=\"text/javascript\">document.formredir.submit();</script>\n";
2233
	print "</body></html>\n";
2234
}
2235

    
2236
/* Locate disks that can be queried for S.M.A.R.T. data. */
2237
function get_smart_drive_list() {
2238
	$disk_list = explode(" ", get_single_sysctl("kern.disks"));
2239
	foreach ($disk_list as $id => $disk) {
2240
		// We only want certain kinds of disks for S.M.A.R.T.
2241
		// 1 is a match, 0 is no match, False is any problem processing the regex
2242
		if (preg_match("/^(ad|da|ada).*[0-9]{1,2}$/", $disk) !== 1) {
2243
			unset($disk_list[$id]);
2244
		}
2245
	}
2246
	sort($disk_list);
2247
	return $disk_list;
2248
}
2249

    
2250
?>
(56-56/67)