Project

General

Profile

Download (57.9 KB) Statistics
| Branch: | Tag: | Revision:
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

    
71
	return 0;
72
}
73

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

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

    
88
function is_subsystem_dirty($subsystem = "") {
89
	global $g;
90

    
91
	if ($subsystem == "") {
92
		return false;
93
	}
94

    
95
	if (file_exists("{$g['varrun_path']}/{$subsystem}.dirty")) {
96
		return true;
97
	}
98

    
99
	return false;
100
}
101

    
102
function mark_subsystem_dirty($subsystem = "") {
103
	global $g;
104

    
105
	if (!file_put_contents("{$g['varrun_path']}/{$subsystem}.dirty", "DIRTY")) {
106
		log_error(sprintf(gettext("WARNING: Could not mark subsystem: %s dirty"), $subsystem));
107
	}
108
}
109

    
110
function clear_subsystem_dirty($subsystem = "") {
111
	global $g;
112

    
113
	@unlink("{$g['varrun_path']}/{$subsystem}.dirty");
114
}
115

    
116
function config_lock() {
117
	return;
118
}
119
function config_unlock() {
120
	return;
121
}
122

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

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

    
164
		return $fp;
165
	}
166

    
167
	return NULL;
168
}
169

    
170
/* unlock configuration file */
171
function unlock($cfglckkey = 0) {
172
	global $g, $cfglckkeyconsumers;
173
	flock($cfglckkey, LOCK_UN);
174
	fclose($cfglckkey);
175
	return;
176
}
177

    
178
/* unlock forcefully configuration file */
179
function unlock_force($lock) {
180
	global $g;
181

    
182
	@unlink("{$g['tmp_path']}/{$lock}.lock");
183
}
184

    
185
function send_event($cmd) {
186
	global $g;
187

    
188
	if (!isset($g['event_address'])) {
189
		$g['event_address'] = "unix:///var/run/check_reload_status";
190
	}
191

    
192
	$try = 0;
193
	while ($try < 3) {
194
		$fd = @fsockopen($g['event_address']);
195
		if ($fd) {
196
			fwrite($fd, $cmd);
197
			$resp = fread($fd, 4096);
198
			if ($resp != "OK\n") {
199
				log_error("send_event: sent {$cmd} got {$resp}");
200
			}
201
			fclose($fd);
202
			$try = 3;
203
		} else if (!is_process_running("check_reload_status")) {
204
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
205
		}
206
		$try++;
207
	}
208
}
209

    
210
function send_multiple_events($cmds) {
211
	global $g;
212

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

    
217
	if (!is_array($cmds)) {
218
		return;
219
	}
220

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

    
240
function refcount_init($reference) {
241
	$shmid = @shmop_open($reference, "c", 0644, 10);
242
	@shmop_write($shmid, str_pad("0", 10, "\x0", STR_PAD_RIGHT), 0);
243
	@shmop_close($shmid);
244
}
245

    
246
function refcount_reference($reference) {
247
	/* Take out a lock across the shared memory read, increment, write sequence to make it atomic. */
248
	$shm_lck = lock("shm{$reference}", LOCK_EX);
249
	try {
250
		/* NOTE: A warning is generated when shared memory does not exist */
251
		$shmid = @shmop_open($reference, "w", 0, 0);
252
		if (!$shmid) {
253
			refcount_init($reference);
254
			$shmid = @shmop_open($reference, "w", 0, 0);
255
			if (!$shmid) {
256
				log_error(gettext("Could not open shared memory {$reference}"));
257
				unlock($shm_lck);
258
				return;
259
			}
260
		}
261
		$shm_data = @shmop_read($shmid, 0, 10);
262
		$shm_data = intval($shm_data) + 1;
263
		@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
264
		@shmop_close($shmid);
265
		unlock($shm_lck);
266
	} catch (Exception $e) {
267
		log_error($e->getMessage());
268
		unlock($shm_lck);
269
	}
270

    
271
	return $shm_data;
272
}
273

    
274
function refcount_unreference($reference) {
275
	/* Take out a lock across the shared memory read, decrement, write sequence to make it atomic. */
276
	$shm_lck = lock("shm{$reference}", LOCK_EX);
277
	try {
278
		$shmid = @shmop_open($reference, "w", 0, 0);
279
		if (!$shmid) {
280
			refcount_init($reference);
281
			log_error(gettext("Could not open shared memory {$reference}"));
282
			unlock($shm_lck);
283
			return;
284
		}
285
		$shm_data = @shmop_read($shmid, 0, 10);
286
		$shm_data = intval($shm_data) - 1;
287
		if ($shm_data < 0) {
288
			//debug_backtrace();
289
			log_error(sprintf(gettext("Reference %s is going negative, not doing unreference."), $reference));
290
		} else {
291
			@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
292
		}
293
		@shmop_close($shmid);
294
		unlock($shm_lck);
295
	} catch (Exception $e) {
296
		log_error($e->getMessage());
297
		unlock($shm_lck);
298
	}
299

    
300
	return $shm_data;
301
}
302

    
303
function refcount_read($reference) {
304
	/* This function just reads the current value of the refcount for information. */
305
	/* There is no need for locking. */
306
	$shmid = @shmop_open($reference, "a", 0, 0);
307
	if (!$shmid) {
308
		log_error(gettext("Could not open shared memory for read {$reference}"));
309
		return -1;
310
	}
311
	$shm_data = @shmop_read($shmid, 0, 10);
312
	@shmop_close($shmid);
313
	return $shm_data;
314
}
315

    
316
function is_module_loaded($module_name) {
317
	$module_name = str_replace(".ko", "", $module_name);
318
	$running = 0;
319
	$_gb = exec("/sbin/kldstat -qn {$module_name} 2>&1", $_gb, $running);
320
	if (intval($running) == 0) {
321
		return true;
322
	} else {
323
		return false;
324
	}
325
}
326

    
327
/* validate non-negative numeric string, or equivalent numeric variable */
328
function is_numericint($arg) {
329
	return (((is_int($arg) && $arg >= 0) || (is_string($arg) && strlen($arg) > 0 && ctype_digit($arg))) ? true : false);
330
}
331

    
332
/* Generate the (human readable) ipv4 or ipv6 subnet address (i.e., netmask, or subnet start IP)
333
   given an (human readable) ipv4 or ipv6 host address and subnet bit count */
334
function gen_subnet($ipaddr, $bits) {
335
	if (($sn = gen_subnetv6($ipaddr, $bits)) == '') {
336
		$sn = gen_subnetv4($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
337
	}
338
	return $sn;
339
}
340

    
341
/* same as gen_subnet() but accepts IPv4 only */
342
function gen_subnetv4($ipaddr, $bits) {
343
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
344
		if ($bits == 0) {
345
			return '0.0.0.0';  // avoids <<32
346
		}
347
		return long2ip(ip2long($ipaddr) & ((0xFFFFFFFF << (32 - $bits)) & 0xFFFFFFFF));
348
	}
349
	return "";
350
}
351

    
352
/* same as gen_subnet() but accepts IPv6 only */
353
function gen_subnetv6($ipaddr, $bits) {
354
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
355
		return Net_IPv6::compress(Net_IPv6::getNetmask($ipaddr, $bits));
356
	}
357
	return "";
358
}
359

    
360
/* Generate the (human readable) ipv4 or ipv6 subnet end address (i.e., highest address, end IP, or IPv4 broadcast address)
361
   given an (human readable) ipv4 or ipv6 host address and subnet bit count. */
362
function gen_subnet_max($ipaddr, $bits) {
363
	if (($sn = gen_subnetv6_max($ipaddr, $bits)) == '') {
364
		$sn = gen_subnetv4_max($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
365
	}
366
	return $sn;
367
}
368

    
369
/* same as gen_subnet_max() but validates IPv4 only */
370
function gen_subnetv4_max($ipaddr, $bits) {
371
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
372
		if ($bits == 32) {
373
			return $ipaddr;
374
		}
375
		return long2ip32(ip2long($ipaddr) | ~gen_subnet_mask_long($bits));
376
	}
377
	return "";
378
}
379

    
380
/* same as gen_subnet_max() but validates IPv6 only */
381
function gen_subnetv6_max($ipaddr, $bits) {
382
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
383
		$endip_bin = substr(Net_IPv6::_ip2Bin($ipaddr), 0, $bits) . str_repeat('1', 128 - $bits);
384
		return Net_IPv6::compress(Net_IPv6::_bin2Ip($endip_bin));
385
	}
386
	return "";
387
}
388

    
389
/* returns a subnet mask (long given a bit count) */
390
function gen_subnet_mask_long($bits) {
391
	$sm = 0;
392
	for ($i = 0; $i < $bits; $i++) {
393
		$sm >>= 1;
394
		$sm |= 0x80000000;
395
	}
396
	return $sm;
397
}
398

    
399
/* same as above but returns a string */
400
function gen_subnet_mask($bits) {
401
	return long2ip(gen_subnet_mask_long($bits));
402
}
403

    
404
/* Convert long int to IP address, truncating to 32-bits. */
405
function long2ip32($ip) {
406
	return long2ip($ip & 0xFFFFFFFF);
407
}
408

    
409
/* Convert IP address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms. */
410
function ip2long32($ip) {
411
	return (ip2long($ip) & 0xFFFFFFFF);
412
}
413

    
414
/* Convert IP address to unsigned long int. */
415
function ip2ulong($ip) {
416
	return sprintf("%u", ip2long32($ip));
417
}
418

    
419
/* Find out how many IPs are contained within a given IP range
420
 *  e.g. 192.168.0.0 to 192.168.0.255 returns 256
421
 */
422
function ip_range_size_v4($startip, $endip) {
423
	if (is_ipaddrv4($startip) && is_ipaddrv4($endip)) {
424
		// Operate as unsigned long because otherwise it wouldn't work
425
		//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
426
		return abs(ip2ulong($startip) - ip2ulong($endip)) + 1;
427
	}
428
	return -1;
429
}
430

    
431
/* Find the smallest possible subnet mask which can contain a given number of IPs
432
 *  e.g. 512 IPs can fit in a /23, but 513 IPs need a /22
433
 */
434
function find_smallest_cidr_v4($number) {
435
	$smallest = 1;
436
	for ($b=32; $b > 0; $b--) {
437
		$smallest = ($number <= pow(2,$b)) ? $b : $smallest;
438
	}
439
	return (32-$smallest);
440
}
441

    
442
/* Return the previous IP address before the given address */
443
function ip_before($ip) {
444
	return long2ip32(ip2long($ip)-1);
445
}
446

    
447
/* Return the next IP address after the given address */
448
function ip_after($ip) {
449
	return long2ip32(ip2long($ip)+1);
450
}
451

    
452
/* Return true if the first IP is 'before' the second */
453
function ip_less_than($ip1, $ip2) {
454
	// Compare as unsigned long because otherwise it wouldn't work when
455
	//   crossing over from 127.255.255.255 / 128.0.0.0 barrier
456
	return ip2ulong($ip1) < ip2ulong($ip2);
457
}
458

    
459
/* Return true if the first IP is 'after' the second */
460
function ip_greater_than($ip1, $ip2) {
461
	// Compare as unsigned long because otherwise it wouldn't work
462
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
463
	return ip2ulong($ip1) > ip2ulong($ip2);
464
}
465

    
466
/* compare two IP addresses */
467
function ipcmp($a, $b) {
468
	if (ip_less_than($a, $b)) {
469
		return -1;
470
	} else if (ip_greater_than($a, $b)) {
471
		return 1;
472
	} else {
473
		return 0;
474
	}
475
}
476

    
477
/* Convert a range of IPv4 addresses to an array of individual addresses. */
478
/* Note: IPv6 ranges are not yet supported here. */
479
function ip_range_to_address_array($startip, $endip, $max_size = 5000) {
480
	if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) {
481
		return false;
482
	}
483

    
484
	if (ip_greater_than($startip, $endip)) {
485
		// Swap start and end so we can process sensibly.
486
		$temp = $startip;
487
		$startip = $endip;
488
		$endip = $temp;
489
	}
490

    
491
	if (ip_range_size_v4($startip, $endip) > $max_size) {
492
		return false;
493
	}
494

    
495
	// Container for IP addresses within this range.
496
	$rangeaddresses = array();
497
	$end_int = ip2ulong($endip);
498
	for ($ip_int = ip2ulong($startip); $ip_int <= $end_int; $ip_int++) {
499
		$rangeaddresses[] = long2ip($ip_int);
500
	}
501

    
502
	return $rangeaddresses;
503
}
504

    
505
/* Convert a range of IPv4 addresses to an array of subnets which can contain the range. */
506
/* Note: IPv6 ranges are not yet supported here. */
507
function ip_range_to_subnet_array($startip, $endip) {
508
	if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) {
509
		return array();
510
	}
511

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

    
519
	// Container for subnets within this range.
520
	$rangesubnets = array();
521

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

    
525
	// Loop here to reduce subnet size and retest as needed. We need to make sure
526
	//   that the target subnet is wholly contained between $startip and $endip.
527
	for ($cidr; $cidr <= 32; $cidr++) {
528
		// Find the network and broadcast addresses for the subnet being tested.
529
		$targetsub_min = gen_subnet($startip, $cidr);
530
		$targetsub_max = gen_subnet_max($startip, $cidr);
531

    
532
		// Check best case where the range is exactly one subnet.
533
		if (($targetsub_min == $startip) && ($targetsub_max == $endip)) {
534
			// Hooray, the range is exactly this subnet!
535
			return array("{$startip}/{$cidr}");
536
		}
537

    
538
		// These remaining scenarios will find a subnet that uses the largest
539
		//  chunk possible of the range being tested, and leave the rest to be
540
		//  tested recursively after the loop.
541

    
542
		// Check if the subnet begins with $startip and ends before $endip
543
		if (($targetsub_min == $startip) && ip_less_than($targetsub_max, $endip)) {
544
			break;
545
		}
546

    
547
		// Check if the subnet ends at $endip and starts after $startip
548
		if (ip_greater_than($targetsub_min, $startip) && ($targetsub_max == $endip)) {
549
			break;
550
		}
551

    
552
		// Check if the subnet is between $startip and $endip
553
		if (ip_greater_than($targetsub_min, $startip) && ip_less_than($targetsub_max, $endip)) {
554
			break;
555
		}
556
	}
557

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

    
564
	// Add in the subnet we found before, to preserve ordering
565
	$rangesubnets[] = "{$targetsub_min}/{$cidr}";
566

    
567
	// And some more logic that will search after the subnet we found to fill in to the end of the range.
568
	if ($endip != $targetsub_max) {
569
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array(ip_after($targetsub_max), $endip));
570
	}
571
	return $rangesubnets;
572
}
573

    
574
/* returns true if $range is a valid pair of IPv4 or IPv6 addresses separated by a "-"
575
	false - if not a valid pair
576
	true (numeric 4 or 6) - if valid, gives type of addresses */
577
function is_iprange($range) {
578
	if (substr_count($range, '-') != 1) {
579
		return false;
580
	}
581
	list($ip1, $ip2) = explode ('-', $range);
582
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) {
583
		return 4;
584
	}
585
	if (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) {
586
		return 6;
587
	}
588
	return false;
589
}
590

    
591
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6
592
	false - not valid
593
	true (numeric 4 or 6) - if valid, gives type of address */
594
function is_ipaddr($ipaddr) {
595
	if (is_ipaddrv4($ipaddr)) {
596
		return 4;
597
	}
598
	if (is_ipaddrv6($ipaddr)) {
599
		return 6;
600
	}
601
	return false;
602
}
603

    
604
/* returns true if $ipaddr is a valid IPv6 address */
605
function is_ipaddrv6($ipaddr) {
606
	if (!is_string($ipaddr) || empty($ipaddr)) {
607
		return false;
608
	}
609
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
610
		$tmpip = explode("%", $ipaddr);
611
		$ipaddr = $tmpip[0];
612
	}
613
	return Net_IPv6::checkIPv6($ipaddr);
614
}
615

    
616
/* returns true if $ipaddr is a valid dotted IPv4 address */
617
function is_ipaddrv4($ipaddr) {
618
	if (!is_string($ipaddr) || empty($ipaddr)) {
619
		return false;
620
	}
621

    
622
	$ip_long = ip2long($ipaddr);
623
	$ip_reverse = long2ip32($ip_long);
624

    
625
	if ($ipaddr == $ip_reverse) {
626
		return true;
627
	} else {
628
		return false;
629
	}
630
}
631

    
632
/* returns true if $ipaddr is a valid IPv6 linklocal address */
633
function is_linklocal($ipaddr) {
634
	return (strtolower(substr($ipaddr, 0, 5)) == "fe80:");
635
}
636

    
637
/* returns scope of a linklocal address */
638
function get_ll_scope($addr) {
639
	if (!is_linklocal($addr) || !strstr($addr, "%")) {
640
		return "";
641
	}
642
	list ($ll, $scope) = explode("%", $addr);
643
	return $scope;
644
}
645

    
646
/* returns true if $ipaddr is a valid literal IPv6 address */
647
function is_literalipaddrv6($ipaddr) {
648
	if (preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match)) {
649
		$ipaddr = $match[1];
650
	} else {
651
		return false;
652
	}
653

    
654
	return is_ipaddrv6($ipaddr);
655
}
656

    
657
/* returns true if $iport is a valid IPv4/IPv6 address + port
658
	false - not valid
659
	true (numeric 4 or 6) - if valid, gives type of address */
660
function is_ipaddrwithport($ipport) {
661
	$c = strrpos($ipport, ":");
662
	if ($c === false) {
663
		return false;  // can't split at final colon if no colon exists
664
	}
665

    
666
	if (!is_port(substr($ipport, $c + 1))) {
667
		return false;  // no valid port after last colon
668
	}
669

    
670
	$ip = substr($ipport, 0, $c);  // else is text before last colon a valid IP
671
	if (is_literalipaddrv6($ip)) {
672
		return 6;
673
	} elseif (is_ipaddrv4($ip)) {
674
		return 4;
675
	} else {
676
		return false;
677
	}
678
}
679

    
680
function is_hostnamewithport($hostport) {
681
	$parts = explode(":", $hostport);
682
	$port = array_pop($parts);
683
	if (count($parts) == 1) {
684
		return is_hostname($parts[0]) && is_port($port);
685
	} else {
686
		return false;
687
	}
688
}
689

    
690
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
691
function is_ipaddroralias($ipaddr) {
692
	global $config;
693

    
694
	if (is_alias($ipaddr)) {
695
		if (is_array($config['aliases']['alias'])) {
696
			foreach ($config['aliases']['alias'] as $alias) {
697
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type'])) {
698
					return true;
699
				}
700
			}
701
		}
702
		return false;
703
	} else {
704
		return is_ipaddr($ipaddr);
705
	}
706

    
707
}
708

    
709
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format
710
	false - if not a valid subnet
711
	true (numeric 4 or 6) - if valid, gives type of subnet */
712
function is_subnet($subnet) {
713
	if (is_string($subnet) && preg_match('/^(?:([0-9.]{7,15})|([0-9a-f:]{2,39}))\/(\d{1,3})$/i', $subnet, $parts)) {
714
		if (is_ipaddrv4($parts[1]) && $parts[3] <= 32) {
715
			return 4;
716
		}
717
		if (is_ipaddrv6($parts[2]) && $parts[3] <= 128) {
718
			return 6;
719
		}
720
	}
721
	return false;
722
}
723

    
724
/* same as is_subnet() but accepts IPv4 only */
725
function is_subnetv4($subnet) {
726
	return (is_subnet($subnet) == 4);
727
}
728

    
729
/* same as is_subnet() but accepts IPv6 only */
730
function is_subnetv6($subnet) {
731
	return (is_subnet($subnet) == 6);
732
}
733

    
734
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
735
function is_subnetoralias($subnet) {
736
	global $aliastable;
737

    
738
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet])) {
739
		return true;
740
	} else {
741
		return is_subnet($subnet);
742
	}
743
}
744

    
745
function subnet_size($subnet) {
746
	if (is_subnetv4($subnet)) {
747
		list ($ip, $bits) = explode("/", $subnet);
748
		return round(exp(log(2) * (32 - $bits)));
749
	}
750
	else if (is_subnetv6($subnet)) {
751
		list ($ip, $bits) = explode("/", $subnet);
752
		return round(exp(log(2) * (128 - $bits)));
753
	}
754
	else {
755
		return 0;
756
	}
757
}
758

    
759

    
760
function subnet_expand($subnet) {
761
	if (is_subnetv4($subnet)) {
762
		return subnetv4_expand($subnet);
763
	} else if (is_subnetv6($subnet)) {
764
		return subnetv6_expand($subnet);
765
	} else {
766
		return $subnet;
767
	}
768
}
769

    
770
function subnetv4_expand($subnet) {
771
	$result = array();
772
	list ($ip, $bits) = explode("/", $subnet);
773
	$net  = ip2long($ip);
774
	$mask = (0xffffffff << (32 - $bits));
775
	$net &= $mask;
776
	$size = round(exp(log(2) * (32 - $bits)));
777
	for ($i = 0; $i < $size; $i += 1) {
778
		$result[] = long2ip($net | $i);
779
	}
780
	return $result;
781
}
782

    
783
/* find out whether two subnets overlap */
784
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
785

    
786
	if (!is_numeric($bits1)) {
787
		$bits1 = 32;
788
	}
789
	if (!is_numeric($bits2)) {
790
		$bits2 = 32;
791
	}
792

    
793
	if ($bits1 < $bits2) {
794
		$relbits = $bits1;
795
	} else {
796
		$relbits = $bits2;
797
	}
798

    
799
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
800
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
801

    
802
	return ($sn1 == $sn2);
803
}
804

    
805
/* find out whether two IPv6 subnets overlap */
806
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
807
	$sub1_min = gen_subnetv6($subnet1, $bits1);
808
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
809
	$sub2_min = gen_subnetv6($subnet2, $bits2);
810
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
811

    
812
	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));
813
}
814

    
815
/* return true if $addr is in $subnet, false if not */
816
function ip_in_subnet($addr,$subnet) {
817
	if (is_ipaddrv6($addr) && is_subnetv6($subnet)) {
818
		return (Net_IPv6::isInNetmask($addr, $subnet));
819
	} else if (is_ipaddrv4($addr) && is_subnetv4($subnet)) {
820
		list($ip, $mask) = explode('/', $subnet);
821
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
822
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
823
	}
824
	return false;
825
}
826

    
827
/* returns true if $hostname is just a valid hostname (top part without any of the domain part) */
828
function is_unqualified_hostname($hostname) {
829
	if (!is_string($hostname)) {
830
		return false;
831
	}
832

    
833
	if (preg_match('/^(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname)) {
834
		return true;
835
	} else {
836
		return false;
837
	}
838
}
839

    
840
/* returns true if $hostname is a valid hostname, with or without being a fully-qualified domain name. */
841
function is_hostname($hostname) {
842
	if (!is_string($hostname)) {
843
		return false;
844
	}
845

    
846
	if (is_domain($hostname)) {
847
		if ((substr_count($hostname, ".") == 1) && ($hostname[strlen($hostname)-1] == ".")) {
848
			/* Only a single dot at the end like "test." - hosts cannot be directly in the root domain. */
849
			return false;
850
		} else {
851
			return true;
852
		}
853
	} else {
854
		return false;
855
	}
856
}
857

    
858
/* returns true if $domain is a valid domain name */
859
function is_domain($domain) {
860
	if (!is_string($domain)) {
861
		return false;
862
	}
863

    
864
	if (preg_match('/^(?:(?:[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', $domain)) {
865
		return true;
866
	} else {
867
		return false;
868
	}
869
}
870

    
871
/* returns true if $macaddr is a valid MAC address */
872
function is_macaddr($macaddr, $partial=false) {
873
	$repeat = ($partial) ? '1,5' : '5';
874
	return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false;
875
}
876

    
877
/* returns true if $name is a valid name for an alias
878
   returns NULL if a reserved word is used
879
   returns FALSE for bad chars in the name - this allows calling code to determine what the problem was.
880
   aliases cannot be:
881
	bad chars: anything except a-z 0-9 and underscore
882
	bad names: empty string, pure numeric, pure underscore
883
	reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and  "pass" */
884

    
885
function is_validaliasname($name) {
886
	/* Array of reserved words */
887
	$reserved = array("port", "pass");
888

    
889
	if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) {
890
		return false;
891
	}
892
	if (in_array($name, $reserved, true) || getservbyname($name, "tcp") || getservbyname($name, "udp") || getprotobyname($name)) {
893
		return; /* return NULL */
894
	}
895
	return true;
896
}
897

    
898
/* returns true if $port is a valid TCP/UDP port */
899
function is_port($port) {
900
	if (ctype_digit($port) && ((intval($port) >= 1) && (intval($port) <= 65535))) {
901
		return true;
902
	}
903
	if (getservbyname($port, "tcp") || getservbyname($port, "udp")) {
904
		return true;
905
	}
906
	return false;
907
}
908

    
909
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
910
function is_portrange($portrange) {
911
	$ports = explode(":", $portrange);
912

    
913
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
914
}
915

    
916
/* returns true if $port is a valid port number or an alias thereof */
917
function is_portoralias($port) {
918
	global $config;
919

    
920
	if (is_alias($port)) {
921
		if (is_array($config['aliases']['alias'])) {
922
			foreach ($config['aliases']['alias'] as $alias) {
923
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type'])) {
924
					return true;
925
				}
926
			}
927
		}
928
		return false;
929
	} else {
930
		return is_port($port);
931
	}
932
}
933

    
934
/* create ranges of sequential port numbers (200:215) and remove duplicates */
935
function group_ports($ports) {
936
	if (!is_array($ports) || empty($ports)) {
937
		return;
938
	}
939

    
940
	$uniq = array();
941
	foreach ($ports as $port) {
942
		if (is_portrange($port)) {
943
			list($begin, $end) = explode(":", $port);
944
			if ($begin > $end) {
945
				$aux = $begin;
946
				$begin = $end;
947
				$end = $aux;
948
			}
949
			for ($i = $begin; $i <= $end; $i++) {
950
				if (!in_array($i, $uniq)) {
951
					$uniq[] = $i;
952
				}
953
			}
954
		} else if (is_port($port)) {
955
			if (!in_array($port, $uniq)) {
956
				$uniq[] = $port;
957
			}
958
		}
959
	}
960
	sort($uniq, SORT_NUMERIC);
961

    
962
	$result = array();
963
	foreach ($uniq as $idx => $port) {
964
		if ($idx == 0) {
965
			$result[] = $port;
966
			continue;
967
		}
968

    
969
		$last = end($result);
970
		if (is_portrange($last)) {
971
			list($begin, $end) = explode(":", $last);
972
		} else {
973
			$begin = $end = $last;
974
		}
975

    
976
		if ($port == ($end+1)) {
977
			$end++;
978
			$result[count($result)-1] = "{$begin}:{$end}";
979
		} else {
980
			$result[] = $port;
981
		}
982
	}
983

    
984
	return $result;
985
}
986

    
987
/* returns true if $val is a valid shaper bandwidth value */
988
function is_valid_shaperbw($val) {
989
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
990
}
991

    
992
/* returns true if $test is in the range between $start and $end */
993
function is_inrange_v4($test, $start, $end) {
994
	if ((ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start))) {
995
		return true;
996
	} else {
997
		return false;
998
	}
999
}
1000

    
1001
/* returns true if $test is in the range between $start and $end */
1002
function is_inrange_v6($test, $start, $end) {
1003
	if ((inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start))) {
1004
		return true;
1005
	} else {
1006
		return false;
1007
	}
1008
}
1009

    
1010
/* returns true if $test is in the range between $start and $end */
1011
function is_inrange($test, $start, $end) {
1012
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
1013
}
1014

    
1015
/* XXX: return the configured carp interface list */
1016
function get_configured_carp_interface_list($carpinterface = '', $family = 'inet', $what = 'ip') {
1017
	global $config;
1018

    
1019
	$iflist = array();
1020

    
1021
	if (is_array($config['virtualip']['vip'])) {
1022
		$viparr = &$config['virtualip']['vip'];
1023
		foreach ($viparr as $vip) {
1024
			switch ($vip['mode']) {
1025
				case "carp":
1026
					if (!empty($carpinterface)) {
1027
						if ($carpinterface == "_vip{$vip['uniqid']}") {
1028
							switch ($what) {
1029
								case 'subnet':
1030
									if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1031
										return $vip['subnet_bits'];
1032
									} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1033
										return $vip['subnet_bits'];
1034
									}
1035
									break;
1036
								case 'iface':
1037
									if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1038
										return $vip['interface'];
1039
									} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1040
										return $vip['interface'];
1041
									}
1042
									break;
1043
								case 'vip':
1044
									if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1045
										return $vip;
1046
									} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1047
										return $vip;
1048
									}
1049
									break;
1050
								case 'ip':
1051
								default:
1052
									if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1053
										return $vip['subnet'];
1054
									} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1055
										return $vip['subnet'];
1056
									}
1057
									break;
1058
							}
1059
						}
1060
					} else {
1061
						$iflist["_vip{$vip['uniqid']}"] = $vip['subnet'];
1062
					}
1063
					break;
1064
			}
1065
		}
1066
	}
1067

    
1068
	return $iflist;
1069
}
1070

    
1071
/* return the configured IP aliases list */
1072
function get_configured_ip_aliases_list($returnfullentry = false) {
1073
	global $config;
1074

    
1075
	$alias_list=array();
1076

    
1077
	if (is_array($config['virtualip']['vip'])) {
1078
		$viparr = &$config['virtualip']['vip'];
1079
		foreach ($viparr as $vip) {
1080
			if ($vip['mode']=="ipalias") {
1081
				if ($returnfullentry) {
1082
					$alias_list[$vip['subnet']] = $vip;
1083
				} else {
1084
					$alias_list[$vip['subnet']] = $vip['interface'];
1085
				}
1086
			}
1087
		}
1088
	}
1089

    
1090
	return $alias_list;
1091
}
1092

    
1093
/* return all configured aliases list (IP, carp, proxyarp and other) */
1094
function get_configured_vips_list() {
1095
	global $config;
1096

    
1097
	$alias_list=array();
1098

    
1099
	if (is_array($config['virtualip']['vip'])) {
1100
		$viparr = &$config['virtualip']['vip'];
1101
		foreach ($viparr as $vip) {
1102
			if ($vip['mode'] == "carp") {
1103
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => "{$vip['interface']}_vip{$vip['vhid']}");
1104
			} else {
1105
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
1106
			}
1107
		}
1108
	}
1109

    
1110
	return $alias_list;
1111
}
1112

    
1113
/* comparison function for sorting by the order in which interfaces are normally created */
1114
function compare_interface_friendly_names($a, $b) {
1115
	if ($a == $b) {
1116
		return 0;
1117
	} else if ($a == 'wan') {
1118
		return -1;
1119
	} else if ($b == 'wan') {
1120
		return 1;
1121
	} else if ($a == 'lan') {
1122
		return -1;
1123
	} else if ($b == 'lan') {
1124
		return 1;
1125
	}
1126

    
1127
	return strnatcmp($a, $b);
1128
}
1129

    
1130
/* return the configured interfaces list. */
1131
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
1132
	global $config;
1133

    
1134
	$iflist = array();
1135

    
1136
	/* if list */
1137
	foreach ($config['interfaces'] as $if => $ifdetail) {
1138
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1139
			continue;
1140
		}
1141
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1142
			$iflist[$if] = $if;
1143
		}
1144
	}
1145

    
1146
	return $iflist;
1147
}
1148

    
1149
/* return the configured interfaces list. */
1150
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
1151
	global $config;
1152

    
1153
	$iflist = array();
1154

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

    
1168
	return $iflist;
1169
}
1170

    
1171
/* return the configured interfaces list with their description. */
1172
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
1173
	global $config;
1174

    
1175
	$iflist = array();
1176

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

    
1191
	return $iflist;
1192
}
1193

    
1194
/*
1195
 *   get_configured_ip_addresses() - Return a list of all configured
1196
 *   interfaces IP Addresses
1197
 *
1198
 */
1199
function get_configured_ip_addresses() {
1200
	global $config;
1201

    
1202
	if (!function_exists('get_interface_ip')) {
1203
		require_once("interfaces.inc");
1204
	}
1205
	$ip_array = array();
1206
	$interfaces = get_configured_interface_list();
1207
	if (is_array($interfaces)) {
1208
		foreach ($interfaces as $int) {
1209
			$ipaddr = get_interface_ip($int);
1210
			$ip_array[$int] = $ipaddr;
1211
		}
1212
	}
1213
	$interfaces = get_configured_carp_interface_list();
1214
	if (is_array($interfaces)) {
1215
		foreach ($interfaces as $int => $ipaddr) {
1216
			$ip_array[$int] = $ipaddr;
1217
		}
1218
	}
1219

    
1220
	/* pppoe server */
1221
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
1222
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
1223
			if ($pppoe['mode'] == "server") {
1224
				if (is_ipaddr($pppoe['localip'])) {
1225
					$int = "pppoes". $pppoe['pppoeid'];
1226
					$ip_array[$int] = $pppoe['localip'];
1227
				}
1228
			}
1229
		}
1230
	}
1231

    
1232
	return $ip_array;
1233
}
1234

    
1235
/*
1236
 *   get_configured_ipv6_addresses() - Return a list of all configured
1237
 *   interfaces IPv6 Addresses
1238
 *
1239
 */
1240
function get_configured_ipv6_addresses() {
1241
	require_once("interfaces.inc");
1242
	$ipv6_array = array();
1243
	$interfaces = get_configured_interface_list();
1244
	if (is_array($interfaces)) {
1245
		foreach ($interfaces as $int) {
1246
			$ipaddrv6 = get_interface_ipv6($int);
1247
			$ipv6_array[$int] = $ipaddrv6;
1248
		}
1249
	}
1250
	$interfaces = get_configured_carp_interface_list();
1251
	if (is_array($interfaces)) {
1252
		foreach ($interfaces as $int => $ipaddrv6) {
1253
			$ipv6_array[$int] = $ipaddrv6;
1254
		}
1255
	}
1256
	return $ipv6_array;
1257
}
1258

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

    
1363
			case "friendly":
1364
				if ($friendly != "") {
1365
					$toput['if'] = $ifname;
1366
					$iflist[$friendly] = $toput;
1367
				}
1368
				break;
1369
			}
1370
		}
1371
	}
1372
	return $iflist;
1373
}
1374

    
1375
/****f* util/log_error
1376
* NAME
1377
*   log_error  - Sends a string to syslog.
1378
* INPUTS
1379
*   $error     - string containing the syslog message.
1380
* RESULT
1381
*   null
1382
******/
1383
function log_error($error) {
1384
	global $g;
1385
	$page = $_SERVER['SCRIPT_NAME'];
1386
	if (empty($page)) {
1387
		$files = get_included_files();
1388
		$page = basename($files[0]);
1389
	}
1390
	syslog(LOG_ERR, "$page: $error");
1391
	if ($g['debug']) {
1392
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1393
	}
1394
	return;
1395
}
1396

    
1397
/****f* util/log_auth
1398
* NAME
1399
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1400
* INPUTS
1401
*   $error     - string containing the syslog message.
1402
* RESULT
1403
*   null
1404
******/
1405
function log_auth($error) {
1406
	global $g;
1407
	$page = $_SERVER['SCRIPT_NAME'];
1408
	syslog(LOG_AUTH, "$page: $error");
1409
	if ($g['debug']) {
1410
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1411
	}
1412
	return;
1413
}
1414

    
1415
/****f* util/exec_command
1416
 * NAME
1417
 *   exec_command - Execute a command and return a string of the result.
1418
 * INPUTS
1419
 *   $command   - String of the command to be executed.
1420
 * RESULT
1421
 *   String containing the command's result.
1422
 * NOTES
1423
 *   This function returns the command's stdout and stderr.
1424
 ******/
1425
function exec_command($command) {
1426
	$output = array();
1427
	exec($command . ' 2>&1', $output);
1428
	return(implode("\n", $output));
1429
}
1430

    
1431
/* wrapper for exec() */
1432
function mwexec($command, $mute = false, $clearsigmask = false) {
1433
	global $g;
1434

    
1435
	if ($g['debug']) {
1436
		if (!$_SERVER['REMOTE_ADDR']) {
1437
			echo "mwexec(): $command\n";
1438
		}
1439
	}
1440
	$oarr = array();
1441
	$retval = 0;
1442

    
1443
	if ($clearsigmask) {
1444
		$oldset = array();
1445
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1446
	}
1447
	$garbage = exec("$command 2>&1", $oarr, $retval);
1448
	if ($clearsigmask) {
1449
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1450
	}
1451

    
1452
	if (isset($config['system']['developerspew'])) {
1453
		$mute = false;
1454
	}
1455
	if (($retval <> 0) && ($mute === false)) {
1456
		$output = implode(" ", $oarr);
1457
		log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, $output));
1458
		unset($output);
1459
	}
1460
	unset($oarr);
1461
	return $retval;
1462
}
1463

    
1464
/* wrapper for exec() in background */
1465
function mwexec_bg($command, $clearsigmask = false) {
1466
	global $g;
1467

    
1468
	if ($g['debug']) {
1469
		if (!$_SERVER['REMOTE_ADDR']) {
1470
			echo "mwexec(): $command\n";
1471
		}
1472
	}
1473

    
1474
	if ($clearsigmask) {
1475
		$oldset = array();
1476
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1477
	}
1478
	$_gb = exec("/usr/bin/nohup $command > /dev/null 2>&1 &");
1479
	if ($clearsigmask) {
1480
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1481
	}
1482
	unset($_gb);
1483
}
1484

    
1485
/* unlink a file, if it exists */
1486
function unlink_if_exists($fn) {
1487
	$to_do = glob($fn);
1488
	if (is_array($to_do)) {
1489
		foreach ($to_do as $filename) {
1490
			@unlink($filename);
1491
		}
1492
	} else {
1493
		@unlink($fn);
1494
	}
1495
}
1496
/* make a global alias table (for faster lookups) */
1497
function alias_make_table($config) {
1498
	global $aliastable;
1499

    
1500
	$aliastable = array();
1501

    
1502
	if (is_array($config['aliases']['alias'])) {
1503
		foreach ($config['aliases']['alias'] as $alias) {
1504
			if ($alias['name']) {
1505
				$aliastable[$alias['name']] = $alias['address'];
1506
			}
1507
		}
1508
	}
1509
}
1510

    
1511
/* check if an alias exists */
1512
function is_alias($name) {
1513
	global $aliastable;
1514

    
1515
	return isset($aliastable[$name]);
1516
}
1517

    
1518
function alias_get_type($name) {
1519
	global $config;
1520

    
1521
	if (is_array($config['aliases']['alias'])) {
1522
		foreach ($config['aliases']['alias'] as $alias) {
1523
			if ($name == $alias['name']) {
1524
				return $alias['type'];
1525
			}
1526
		}
1527
	}
1528

    
1529
	return "";
1530
}
1531

    
1532
/* expand a host or network alias, if necessary */
1533
function alias_expand($name) {
1534
	global $aliastable;
1535

    
1536
	if (isset($aliastable[$name])) {
1537
		// alias names cannot be strictly numeric. redmine #4289
1538
		if (is_numericint($name)) {
1539
			return null;
1540
		}
1541
		return "\${$name}";
1542
	} else if (is_ipaddr($name) || is_subnet($name) || is_port($name) || is_portrange($name)) {
1543
		return "{$name}";
1544
	} else {
1545
		return null;
1546
	}
1547
}
1548

    
1549
function alias_expand_urltable($name) {
1550
	global $config;
1551
	$urltable_prefix = "/var/db/aliastables/";
1552
	$urltable_filename = $urltable_prefix . $name . ".txt";
1553

    
1554
	if (is_array($config['aliases']['alias'])) {
1555
		foreach ($config['aliases']['alias'] as $alias) {
1556
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1557
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename)) {
1558
					return $urltable_filename;
1559
				} else if (process_alias_urltable($name, $alias["url"], 0, true)) {
1560
					return $urltable_filename;
1561
				}
1562
			}
1563
		}
1564
	}
1565
	return null;
1566
}
1567

    
1568
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1569
function verify_digital_signature($fname) {
1570
	global $g;
1571

    
1572
	if (!file_exists("/usr/local/sbin/gzsig")) {
1573
		return 4;
1574
	}
1575

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

    
1579
/* obtain MAC address given an IP address by looking at the ARP table */
1580
function arp_get_mac_by_ip($ip) {
1581
	mwexec("/sbin/ping -c 1 -t 1 " . escapeshellarg($ip), true);
1582
	$arpoutput = "";
1583
	exec("/usr/sbin/arp -n " . escapeshellarg($ip), $arpoutput);
1584

    
1585
	if ($arpoutput[0]) {
1586
		$arpi = explode(" ", $arpoutput[0]);
1587
		$macaddr = $arpi[3];
1588
		if (is_macaddr($macaddr)) {
1589
			return $macaddr;
1590
		} else {
1591
			return false;
1592
		}
1593
	}
1594

    
1595
	return false;
1596
}
1597

    
1598
/* return a fieldname that is safe for xml usage */
1599
function xml_safe_fieldname($fieldname) {
1600
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1601
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1602
			 ':', ',', '.', '\'', '\\'
1603
		);
1604
	return strtolower(str_replace($replace, "", $fieldname));
1605
}
1606

    
1607
function mac_format($clientmac) {
1608
	global $config, $cpzone;
1609

    
1610
	$mac = explode(":", $clientmac);
1611
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1612

    
1613
	switch ($mac_format) {
1614
		case 'singledash':
1615
			return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1616

    
1617
		case 'ietf':
1618
			return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1619

    
1620
		case 'cisco':
1621
			return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1622

    
1623
		case 'unformatted':
1624
			return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1625

    
1626
		default:
1627
			return $clientmac;
1628
	}
1629
}
1630

    
1631
function resolve_retry($hostname, $retries = 5) {
1632

    
1633
	if (is_ipaddr($hostname)) {
1634
		return $hostname;
1635
	}
1636

    
1637
	for ($i = 0; $i < $retries; $i++) {
1638
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1639
		$ip = gethostbyname($hostname);
1640

    
1641
		if ($ip && $ip != $hostname) {
1642
			/* success */
1643
			return $ip;
1644
		}
1645

    
1646
		sleep(1);
1647
	}
1648

    
1649
	return false;
1650
}
1651

    
1652
function format_bytes($bytes) {
1653
	if ($bytes >= 1073741824) {
1654
		return sprintf("%.2f GB", $bytes/1073741824);
1655
	} else if ($bytes >= 1048576) {
1656
		return sprintf("%.2f MB", $bytes/1048576);
1657
	} else if ($bytes >= 1024) {
1658
		return sprintf("%.0f KB", $bytes/1024);
1659
	} else {
1660
		return sprintf("%d bytes", $bytes);
1661
	}
1662
}
1663

    
1664
function update_filter_reload_status($text) {
1665
	global $g;
1666

    
1667
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1668
}
1669

    
1670
/****** util/return_dir_as_array
1671
 * NAME
1672
 *   return_dir_as_array - Return a directory's contents as an array.
1673
 * INPUTS
1674
 *   $dir          - string containing the path to the desired directory.
1675
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1676
 * RESULT
1677
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1678
 ******/
1679
function return_dir_as_array($dir, $filter_regex = '') {
1680
	$dir_array = array();
1681
	if (is_dir($dir)) {
1682
		if ($dh = opendir($dir)) {
1683
			while (($file = readdir($dh)) !== false) {
1684
				if (($file == ".") || ($file == "..")) {
1685
					continue;
1686
				}
1687

    
1688
				if (empty($filter_regex) || preg_match($filter_regex, $file)) {
1689
					array_push($dir_array, $file);
1690
				}
1691
			}
1692
			closedir($dh);
1693
		}
1694
	}
1695
	return $dir_array;
1696
}
1697

    
1698
function run_plugins($directory) {
1699
	global $config, $g;
1700

    
1701
	/* process packager manager custom rules */
1702
	$files = return_dir_as_array($directory);
1703
	if (is_array($files)) {
1704
		foreach ($files as $file) {
1705
			if (stristr($file, ".sh") == true) {
1706
				mwexec($directory . $file . " start");
1707
			} else if (!is_dir($directory . "/" . $file) && stristr($file,".inc")) {
1708
				require_once($directory . "/" . $file);
1709
			}
1710
		}
1711
	}
1712
}
1713

    
1714
/*
1715
 *    safe_mkdir($path, $mode = 0755)
1716
 *    create directory if it doesn't already exist and isn't a file!
1717
 */
1718
function safe_mkdir($path, $mode=0755) {
1719
	global $g;
1720

    
1721
	if (!is_file($path) && !is_dir($path)) {
1722
		return @mkdir($path, $mode, true);
1723
	} else {
1724
		return false;
1725
	}
1726
}
1727

    
1728
/*
1729
 * make_dirs($path, $mode = 0755)
1730
 * create directory tree recursively (mkdir -p)
1731
 */
1732
function make_dirs($path, $mode = 0755) {
1733
	$base = '';
1734
	foreach (explode('/', $path) as $dir) {
1735
		$base .= "/$dir";
1736
		if (!is_dir($base)) {
1737
			if (!@mkdir($base, $mode)) {
1738
				return false;
1739
			}
1740
		}
1741
	}
1742
	return true;
1743
}
1744

    
1745
/*
1746
 * get_sysctl($names)
1747
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1748
 * name) and return an array of key/value pairs set for those that exist
1749
 */
1750
function get_sysctl($names) {
1751
	if (empty($names)) {
1752
		return array();
1753
	}
1754

    
1755
	if (is_array($names)) {
1756
		$name_list = array();
1757
		foreach ($names as $name) {
1758
			$name_list[] = escapeshellarg($name);
1759
		}
1760
	} else {
1761
		$name_list = array(escapeshellarg($names));
1762
	}
1763

    
1764
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1765
	$values = array();
1766
	foreach ($output as $line) {
1767
		$line = explode(": ", $line, 2);
1768
		if (count($line) == 2) {
1769
			$values[$line[0]] = $line[1];
1770
		}
1771
	}
1772

    
1773
	return $values;
1774
}
1775

    
1776
/*
1777
 * get_single_sysctl($name)
1778
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
1779
 * return the value for sysctl $name or empty string if it doesn't exist
1780
 */
1781
function get_single_sysctl($name) {
1782
	if (empty($name)) {
1783
		return "";
1784
	}
1785

    
1786
	$value = get_sysctl($name);
1787
	if (empty($value) || !isset($value[$name])) {
1788
		return "";
1789
	}
1790

    
1791
	return $value[$name];
1792
}
1793

    
1794
/*
1795
 * set_sysctl($value_list)
1796
 * Set sysctl OID's listed as key/value pairs and return
1797
 * an array with keys set for those that succeeded
1798
 */
1799
function set_sysctl($values) {
1800
	if (empty($values)) {
1801
		return array();
1802
	}
1803

    
1804
	$value_list = array();
1805
	foreach ($values as $key => $value) {
1806
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1807
	}
1808

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

    
1811
	/* Retry individually if failed (one or more read-only) */
1812
	if ($success <> 0 && count($value_list) > 1) {
1813
		foreach ($value_list as $value) {
1814
			exec("/sbin/sysctl -i " . $value, $output);
1815
		}
1816
	}
1817

    
1818
	$ret = array();
1819
	foreach ($output as $line) {
1820
		$line = explode(": ", $line, 2);
1821
		if (count($line) == 2) {
1822
			$ret[$line[0]] = true;
1823
		}
1824
	}
1825

    
1826
	return $ret;
1827
}
1828

    
1829
/*
1830
 * set_single_sysctl($name, $value)
1831
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
1832
 * returns boolean meaning if it succeeded
1833
 */
1834
function set_single_sysctl($name, $value) {
1835
	if (empty($name)) {
1836
		return false;
1837
	}
1838

    
1839
	$result = set_sysctl(array($name => $value));
1840

    
1841
	if (!isset($result[$name]) || $result[$name] != $value) {
1842
		return false;
1843
	}
1844

    
1845
	return true;
1846
}
1847

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

    
1862
function mute_kernel_msgs() {
1863
	global $config;
1864
	// Do not mute serial console.  The kernel gets very very cranky
1865
	// and will start dishing you cannot control tty errors.
1866
	switch (trim(file_get_contents("/etc/platform"))) {
1867
		case "nanobsd":
1868
		case "jail":
1869
			return;
1870
	}
1871
	if ($config['system']['enableserial']) {
1872
		return;
1873
	}
1874
	exec("/sbin/conscontrol mute on");
1875
}
1876

    
1877
function unmute_kernel_msgs() {
1878
	global $config;
1879
	// Do not mute serial console.  The kernel gets very very cranky
1880
	// and will start dishing you cannot control tty errors.
1881
	switch (trim(file_get_contents("/etc/platform"))) {
1882
		case "nanobsd":
1883
		case "jail":
1884
			return;
1885
	}
1886
	exec("/sbin/conscontrol mute off");
1887
}
1888

    
1889
function start_devd() {
1890
	global $g;
1891

    
1892
	if ($g['platform'] == 'jail') {
1893
		return;
1894
	}
1895
	/* Use the undocumented -q options of devd to quiet its log spamming */
1896
	$_gb = exec("/sbin/devd -q");
1897
	sleep(1);
1898
	unset($_gb);
1899
}
1900

    
1901
function is_interface_vlan_mismatch() {
1902
	global $config, $g;
1903

    
1904
	if (is_array($config['vlans']['vlan'])) {
1905
		foreach ($config['vlans']['vlan'] as $vlan) {
1906
			if (does_interface_exist($vlan['if']) == false) {
1907
				return true;
1908
			}
1909
		}
1910
	}
1911

    
1912
	return false;
1913
}
1914

    
1915
function is_interface_mismatch() {
1916
	global $config, $g;
1917

    
1918
	$do_assign = false;
1919
	$i = 0;
1920
	$missing_interfaces = array();
1921
	if (is_array($config['interfaces'])) {
1922
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1923
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1924
				// Do not check these interfaces.
1925
				$i++;
1926
				continue;
1927
			} else if (does_interface_exist($ifcfg['if']) == false) {
1928
				$missing_interfaces[] = $ifcfg['if'];
1929
				$do_assign = true;
1930
			} else {
1931
				$i++;
1932
			}
1933
		}
1934
	}
1935

    
1936
	if (file_exists("{$g['tmp_path']}/assign_complete")) {
1937
		$do_assign = false;
1938
	}
1939

    
1940
	if (!empty($missing_interfaces) && $do_assign) {
1941
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1942
	} else {
1943
		@unlink("{$g['tmp_path']}/missing_interfaces");
1944
	}
1945

    
1946
	return $do_assign;
1947
}
1948

    
1949
/* sync carp entries to other firewalls */
1950
function carp_sync_client() {
1951
	global $g;
1952
	send_event("filter sync");
1953
}
1954

    
1955
/****f* util/isAjax
1956
 * NAME
1957
 *   isAjax - reports if the request is driven from prototype
1958
 * INPUTS
1959
 *   none
1960
 * RESULT
1961
 *   true/false
1962
 ******/
1963
function isAjax() {
1964
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
1965
}
1966

    
1967
/****f* util/timeout
1968
 * NAME
1969
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
1970
 * INPUTS
1971
 *   optional, seconds to wait before timeout. Default 9 seconds.
1972
 * RESULT
1973
 *   returns 1 char of user input or null if no input.
1974
 ******/
1975
function timeout($timer = 9) {
1976
	while (!isset($key)) {
1977
		if ($timer >= 9) {
1978
			echo chr(8) . chr(8) . ($timer==9 ? chr(32) : null)  . "{$timer}";
1979
		} else {
1980
			echo chr(8). "{$timer}";
1981
		}
1982
		`/bin/stty -icanon min 0 time 25`;
1983
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
1984
		`/bin/stty icanon`;
1985
		if ($key == '') {
1986
			unset($key);
1987
		}
1988
		$timer--;
1989
		if ($timer == 0) {
1990
			break;
1991
		}
1992
	}
1993
	return $key;
1994
}
1995

    
1996
/****f* util/msort
1997
 * NAME
1998
 *   msort - sort array
1999
 * INPUTS
2000
 *   $array to be sorted, field to sort by, direction of sort
2001
 * RESULT
2002
 *   returns newly sorted array
2003
 ******/
2004
function msort($array, $id="id", $sort_ascending=true) {
2005
	$temp_array = array();
2006
	while (count($array)>0) {
2007
		$lowest_id = 0;
2008
		$index=0;
2009
		foreach ($array as $item) {
2010
			if (isset($item[$id])) {
2011
				if ($array[$lowest_id][$id]) {
2012
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
2013
						$lowest_id = $index;
2014
					}
2015
				}
2016
			}
2017
			$index++;
2018
		}
2019
		$temp_array[] = $array[$lowest_id];
2020
		$array = array_merge(array_slice($array, 0,$lowest_id), array_slice($array, $lowest_id+1));
2021
	}
2022
	if ($sort_ascending) {
2023
		return $temp_array;
2024
	} else {
2025
		return array_reverse($temp_array);
2026
	}
2027
}
2028

    
2029
/****f* util/is_URL
2030
 * NAME
2031
 *   is_URL
2032
 * INPUTS
2033
 *   string to check
2034
 * RESULT
2035
 *   Returns true if item is a URL
2036
 ******/
2037
function is_URL($url) {
2038
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
2039
	if ($match) {
2040
		return true;
2041
	}
2042
	return false;
2043
}
2044

    
2045
function is_file_included($file = "") {
2046
	$files = get_included_files();
2047
	if (in_array($file, $files)) {
2048
		return true;
2049
	}
2050

    
2051
	return false;
2052
}
2053

    
2054
/*
2055
 * Replace a value on a deep associative array using regex
2056
 */
2057
function array_replace_values_recursive($data, $match, $replace) {
2058
	if (empty($data)) {
2059
		return $data;
2060
	}
2061

    
2062
	if (is_string($data)) {
2063
		$data = preg_replace("/{$match}/", $replace, $data);
2064
	} else if (is_array($data)) {
2065
		foreach ($data as $k => $v) {
2066
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
2067
		}
2068
	}
2069

    
2070
	return $data;
2071
}
2072

    
2073
/*
2074
	This function was borrowed from a comment on PHP.net at the following URL:
2075
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
2076
 */
2077
function array_merge_recursive_unique($array0, $array1) {
2078

    
2079
	$arrays = func_get_args();
2080
	$remains = $arrays;
2081

    
2082
	// We walk through each arrays and put value in the results (without
2083
	// considering previous value).
2084
	$result = array();
2085

    
2086
	// loop available array
2087
	foreach ($arrays as $array) {
2088

    
2089
		// The first remaining array is $array. We are processing it. So
2090
		// we remove it from remaining arrays.
2091
		array_shift($remains);
2092

    
2093
		// We don't care non array param, like array_merge since PHP 5.0.
2094
		if (is_array($array)) {
2095
			// Loop values
2096
			foreach ($array as $key => $value) {
2097
				if (is_array($value)) {
2098
					// we gather all remaining arrays that have such key available
2099
					$args = array();
2100
					foreach ($remains as $remain) {
2101
						if (array_key_exists($key, $remain)) {
2102
							array_push($args, $remain[$key]);
2103
						}
2104
					}
2105

    
2106
					if (count($args) > 2) {
2107
						// put the recursion
2108
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
2109
					} else {
2110
						foreach ($value as $vkey => $vval) {
2111
							$result[$key][$vkey] = $vval;
2112
						}
2113
					}
2114
				} else {
2115
					// simply put the value
2116
					$result[$key] = $value;
2117
				}
2118
			}
2119
		}
2120
	}
2121
	return $result;
2122
}
2123

    
2124

    
2125
/*
2126
 * converts a string like "a,b,c,d"
2127
 * into an array like array("a" => "b", "c" => "d")
2128
 */
2129
function explode_assoc($delimiter, $string) {
2130
	$array = explode($delimiter, $string);
2131
	$result = array();
2132
	$numkeys = floor(count($array) / 2);
2133
	for ($i = 0; $i < $numkeys; $i += 1) {
2134
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
2135
	}
2136
	return $result;
2137
}
2138

    
2139
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
2140
	global $config, $aliastable;
2141

    
2142
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
2143
	if (!is_array($config['staticroutes']['route'])) {
2144
		return array();
2145
	}
2146

    
2147
	$allstaticroutes = array();
2148
	$allsubnets = array();
2149
	/* Loop through routes and expand aliases as we find them. */
2150
	foreach ($config['staticroutes']['route'] as $route) {
2151
		if (is_alias($route['network'])) {
2152
			if (!isset($aliastable[$route['network']])) {
2153
				continue;
2154
			}
2155

    
2156
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
2157
			foreach ($subnets as $net) {
2158
				if (!is_subnet($net)) {
2159
					if (is_ipaddrv4($net)) {
2160
						$net .= "/32";
2161
					} else if (is_ipaddrv6($net)) {
2162
						$net .= "/128";
2163
					} else if ($returnhostnames === false || !is_fqdn($net)) {
2164
						continue;
2165
					}
2166
				}
2167
				$temproute = $route;
2168
				$temproute['network'] = $net;
2169
				$allstaticroutes[] = $temproute;
2170
				$allsubnets[] = $net;
2171
			}
2172
		} elseif (is_subnet($route['network'])) {
2173
			$allstaticroutes[] = $route;
2174
			$allsubnets[] = $route['network'];
2175
		}
2176
	}
2177
	if ($returnsubnetsonly) {
2178
		return $allsubnets;
2179
	} else {
2180
		return $allstaticroutes;
2181
	}
2182
}
2183

    
2184
/****f* util/get_alias_list
2185
 * NAME
2186
 *   get_alias_list - Provide a list of aliases.
2187
 * INPUTS
2188
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
2189
 * RESULT
2190
 *   Array containing list of aliases.
2191
 *   If $type is unspecified, all aliases are returned.
2192
 *   If $type is a string, all aliases of the type specified in $type are returned.
2193
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
2194
 */
2195
function get_alias_list($type = null) {
2196
	global $config;
2197
	$result = array();
2198
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
2199
		foreach ($config['aliases']['alias'] as $alias) {
2200
			if ($type === null) {
2201
				$result[] = $alias['name'];
2202
			} else if (is_array($type)) {
2203
				if (in_array($alias['type'], $type)) {
2204
					$result[] = $alias['name'];
2205
				}
2206
			} else if ($type === $alias['type']) {
2207
				$result[] = $alias['name'];
2208
			}
2209
		}
2210
	}
2211
	return $result;
2212
}
2213

    
2214
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
2215
function array_exclude($needle, $haystack) {
2216
	$result = array();
2217
	if (is_array($haystack)) {
2218
		foreach ($haystack as $thing) {
2219
			if ($needle !== $thing) {
2220
				$result[] = $thing;
2221
			}
2222
		}
2223
	}
2224
	return $result;
2225
}
2226

    
2227
function get_current_theme() {
2228
	global $config, $g;
2229
	/*
2230
	 *   if user has selected a custom template, use it.
2231
	 *   otherwise default to pfsense template
2232
	 */
2233
	if (($g["disablethemeselection"] === true) && !empty($g["default_theme"]) && (is_dir($g["www_path"].'/themes/'.$g["default_theme"]))) {
2234
		$theme = $g["default_theme"];
2235
	} elseif ($config['theme'] <> "" && (is_dir($g["www_path"].'/themes/'.$config['theme']))) {
2236
		$theme = $config['theme'];
2237
	} else {
2238
		$theme = "pfsense";
2239
	}
2240
	/*
2241
	 *  If this device is an apple ipod/iphone
2242
	 *  switch the theme to one that works with it.
2243
	 */
2244
	$lowres_ua = array("iPhone", "iPod", "iPad", "Android", "BlackBerry", "Opera Mini", "Opera Mobi", "PlayBook", "IEMobile");
2245
	foreach ($lowres_ua as $useragent) {
2246
		if (strstr($_SERVER['HTTP_USER_AGENT'], $useragent)) {
2247
			$theme = (empty($g['theme_lowres']) && (is_dir($g["www_path"].'/themes/'.$g['theme_lowres']))) ? "pfsense" : $g['theme_lowres'];
2248
		}
2249
	}
2250
	return $theme;
2251
}
2252

    
2253
/* Define what is preferred, IPv4 or IPv6 */
2254
function prefer_ipv4_or_ipv6() {
2255
	global $config;
2256

    
2257
	if (isset($config['system']['prefer_ipv4'])) {
2258
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2259
	} else {
2260
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2261
	}
2262
}
2263

    
2264
/* Redirect to page passing parameters via POST */
2265
function post_redirect($page, $params) {
2266
	if (!is_array($params)) {
2267
		return;
2268
	}
2269

    
2270
	print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n";
2271
	foreach ($params as $key => $value) {
2272
		print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n";
2273
	}
2274
	print "</form><script type=\"text/javascript\">document.formredir.submit();</script>\n";
2275
	print "</body></html>\n";
2276
}
2277

    
2278
/* Locate disks that can be queried for S.M.A.R.T. data. */
2279
function get_smart_drive_list() {
2280
	$disk_list = explode(" ", get_single_sysctl("kern.disks"));
2281
	foreach ($disk_list as $id => $disk) {
2282
		// We only want certain kinds of disks for S.M.A.R.T.
2283
		// 1 is a match, 0 is no match, False is any problem processing the regex
2284
		if (preg_match("/^(ad|da|ada).*[0-9]{1,2}$/", $disk) !== 1) {
2285
			unset($disk_list[$id]);
2286
		}
2287
	}
2288
	sort($disk_list);
2289
	return $disk_list;
2290
}
2291

    
2292
?>
(56-56/68)