Project

General

Profile

Download (57.7 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, $offset = 1) {
444
	return long2ip32(ip2long($ip) - $offset);
445
}
446

    
447
/* Return the next IP address after the given address */
448
function ip_after($ip, $offset = 1) {
449
	return long2ip32(ip2long($ip) + $offset);
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) || ip2long($ipaddr) === FALSE) {
619
		return false;
620
	}
621
	return true;
622
}
623

    
624
/* returns true if $ipaddr is a valid IPv6 linklocal address */
625
function is_linklocal($ipaddr) {
626
	return (strtolower(substr($ipaddr, 0, 5)) == "fe80:");
627
}
628

    
629
/* returns scope of a linklocal address */
630
function get_ll_scope($addr) {
631
	if (!is_linklocal($addr) || !strstr($addr, "%")) {
632
		return "";
633
	}
634
	list ($ll, $scope) = explode("%", $addr);
635
	return $scope;
636
}
637

    
638
/* returns true if $ipaddr is a valid literal IPv6 address */
639
function is_literalipaddrv6($ipaddr) {
640
	if (preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match)) {
641
		$ipaddr = $match[1];
642
	} else {
643
		return false;
644
	}
645

    
646
	return is_ipaddrv6($ipaddr);
647
}
648

    
649
/* returns true if $iport is a valid IPv4/IPv6 address + port
650
	false - not valid
651
	true (numeric 4 or 6) - if valid, gives type of address */
652
function is_ipaddrwithport($ipport) {
653
	$c = strrpos($ipport, ":");
654
	if ($c === false) {
655
		return false;  // can't split at final colon if no colon exists
656
	}
657

    
658
	if (!is_port(substr($ipport, $c + 1))) {
659
		return false;  // no valid port after last colon
660
	}
661

    
662
	$ip = substr($ipport, 0, $c);  // else is text before last colon a valid IP
663
	if (is_literalipaddrv6($ip)) {
664
		return 6;
665
	} elseif (is_ipaddrv4($ip)) {
666
		return 4;
667
	} else {
668
		return false;
669
	}
670
}
671

    
672
function is_hostnamewithport($hostport) {
673
	$parts = explode(":", $hostport);
674
	$port = array_pop($parts);
675
	if (count($parts) == 1) {
676
		return is_hostname($parts[0]) && is_port($port);
677
	} else {
678
		return false;
679
	}
680
}
681

    
682
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
683
function is_ipaddroralias($ipaddr) {
684
	global $config;
685

    
686
	if (is_alias($ipaddr)) {
687
		if (is_array($config['aliases']['alias'])) {
688
			foreach ($config['aliases']['alias'] as $alias) {
689
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type'])) {
690
					return true;
691
				}
692
			}
693
		}
694
		return false;
695
	} else {
696
		return is_ipaddr($ipaddr);
697
	}
698

    
699
}
700

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

    
716
/* same as is_subnet() but accepts IPv4 only */
717
function is_subnetv4($subnet) {
718
	return (is_subnet($subnet) == 4);
719
}
720

    
721
/* same as is_subnet() but accepts IPv6 only */
722
function is_subnetv6($subnet) {
723
	return (is_subnet($subnet) == 6);
724
}
725

    
726
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
727
function is_subnetoralias($subnet) {
728
	global $aliastable;
729

    
730
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet])) {
731
		return true;
732
	} else {
733
		return is_subnet($subnet);
734
	}
735
}
736

    
737
function subnet_size($subnet) {
738
	if (is_subnetv4($subnet)) {
739
		list ($ip, $bits) = explode("/", $subnet);
740
		return round(exp(log(2) * (32 - $bits)));
741
	}
742
	else if (is_subnetv6($subnet)) {
743
		list ($ip, $bits) = explode("/", $subnet);
744
		return round(exp(log(2) * (128 - $bits)));
745
	}
746
	else {
747
		return 0;
748
	}
749
}
750

    
751

    
752
function subnet_expand($subnet) {
753
	if (is_subnetv4($subnet)) {
754
		return subnetv4_expand($subnet);
755
	} else if (is_subnetv6($subnet)) {
756
		return subnetv6_expand($subnet);
757
	} else {
758
		return $subnet;
759
	}
760
}
761

    
762
function subnetv4_expand($subnet) {
763
	$result = array();
764
	list ($ip, $bits) = explode("/", $subnet);
765
	$net = ip2long($ip);
766
	$mask = (0xffffffff << (32 - $bits));
767
	$net &= $mask;
768
	$size = round(exp(log(2) * (32 - $bits)));
769
	for ($i = 0; $i < $size; $i += 1) {
770
		$result[] = long2ip($net | $i);
771
	}
772
	return $result;
773
}
774

    
775
/* find out whether two subnets overlap */
776
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
777

    
778
	if (!is_numeric($bits1)) {
779
		$bits1 = 32;
780
	}
781
	if (!is_numeric($bits2)) {
782
		$bits2 = 32;
783
	}
784

    
785
	if ($bits1 < $bits2) {
786
		$relbits = $bits1;
787
	} else {
788
		$relbits = $bits2;
789
	}
790

    
791
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
792
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
793

    
794
	return ($sn1 == $sn2);
795
}
796

    
797
/* find out whether two IPv6 subnets overlap */
798
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
799
	$sub1_min = gen_subnetv6($subnet1, $bits1);
800
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
801
	$sub2_min = gen_subnetv6($subnet2, $bits2);
802
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
803

    
804
	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));
805
}
806

    
807
/* return true if $addr is in $subnet, false if not */
808
function ip_in_subnet($addr, $subnet) {
809
	if (is_ipaddrv6($addr) && is_subnetv6($subnet)) {
810
		return (Net_IPv6::isInNetmask($addr, $subnet));
811
	} else if (is_ipaddrv4($addr) && is_subnetv4($subnet)) {
812
		list($ip, $mask) = explode('/', $subnet);
813
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
814
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
815
	}
816
	return false;
817
}
818

    
819
/* returns true if $hostname is just a valid hostname (top part without any of the domain part) */
820
function is_unqualified_hostname($hostname) {
821
	if (!is_string($hostname)) {
822
		return false;
823
	}
824

    
825
	if (preg_match('/^(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname)) {
826
		return true;
827
	} else {
828
		return false;
829
	}
830
}
831

    
832
/* returns true if $hostname is a valid hostname, with or without being a fully-qualified domain name. */
833
function is_hostname($hostname, $allow_wildcard=false) {
834
	if (!is_string($hostname)) {
835
		return false;
836
	}
837

    
838
	if (is_domain($hostname, $allow_wildcard)) {
839
		if ((substr_count($hostname, ".") == 1) && ($hostname[strlen($hostname)-1] == ".")) {
840
			/* Only a single dot at the end like "test." - hosts cannot be directly in the root domain. */
841
			return false;
842
		} else {
843
			return true;
844
		}
845
	} else {
846
		return false;
847
	}
848
}
849

    
850
/* returns true if $domain is a valid domain name */
851
function is_domain($domain, $allow_wildcard=false) {
852
	if (!is_string($domain)) {
853
		return false;
854
	}
855
	if ($allow_wildcard) {
856
		$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';
857
	} else {
858
		$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';
859
	}
860

    
861
	if (preg_match($domain_regex, $domain)) {
862
		return true;
863
	} else {
864
		return false;
865
	}
866
}
867

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

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

    
882
function is_validaliasname($name) {
883
	/* Array of reserved words */
884
	$reserved = array("port", "pass");
885

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

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

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

    
910
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
911
}
912

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

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

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

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

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

    
966
		$last = end($result);
967
		if (is_portrange($last)) {
968
			list($begin, $end) = explode(":", $last);
969
		} else {
970
			$begin = $end = $last;
971
		}
972

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

    
981
	return $result;
982
}
983

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

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

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

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

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

    
1016
	$iflist = array();
1017

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

    
1065
	return $iflist;
1066
}
1067

    
1068
/* return the configured IP aliases list */
1069
function get_configured_ip_aliases_list($returnfullentry = false) {
1070
	global $config;
1071

    
1072
	$alias_list = array();
1073

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

    
1087
	return $alias_list;
1088
}
1089

    
1090
/* return all configured aliases list (IP, carp, proxyarp and other) */
1091
function get_configured_vips_list() {
1092
	global $config;
1093

    
1094
	$alias_list = array();
1095

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

    
1107
	return $alias_list;
1108
}
1109

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

    
1124
	return strnatcmp($a, $b);
1125
}
1126

    
1127
/* return the configured interfaces list. */
1128
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
1129
	global $config;
1130

    
1131
	$iflist = array();
1132

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

    
1143
	return $iflist;
1144
}
1145

    
1146
/* return the configured interfaces list. */
1147
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
1148
	global $config;
1149

    
1150
	$iflist = array();
1151

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

    
1165
	return $iflist;
1166
}
1167

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

    
1172
	$iflist = array();
1173

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

    
1188
	return $iflist;
1189
}
1190

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

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

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

    
1229
	return $ip_array;
1230
}
1231

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

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

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

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

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

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

    
1428
/* wrapper for exec() */
1429
function mwexec($command, $mute = false, $clearsigmask = false) {
1430
	global $g;
1431

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

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

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

    
1461
/* wrapper for exec() in background */
1462
function mwexec_bg($command, $clearsigmask = false) {
1463
	global $g;
1464

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

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

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

    
1497
	$aliastable = array();
1498

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

    
1508
/* check if an alias exists */
1509
function is_alias($name) {
1510
	global $aliastable;
1511

    
1512
	return isset($aliastable[$name]);
1513
}
1514

    
1515
function alias_get_type($name) {
1516
	global $config;
1517

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

    
1526
	return "";
1527
}
1528

    
1529
/* expand a host or network alias, if necessary */
1530
function alias_expand($name) {
1531
	global $aliastable;
1532

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

    
1546
function alias_expand_urltable($name) {
1547
	global $config;
1548
	$urltable_prefix = "/var/db/aliastables/";
1549
	$urltable_filename = $urltable_prefix . $name . ".txt";
1550

    
1551
	if (is_array($config['aliases']['alias'])) {
1552
		foreach ($config['aliases']['alias'] as $alias) {
1553
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1554
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename)) {
1555
					return $urltable_filename;
1556
				} else {
1557
					send_event("service sync alias {$name}");
1558
					break;
1559
				}
1560
			}
1561
		}
1562
	}
1563
	return null;
1564
}
1565

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

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

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

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

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

    
1593
	return false;
1594
}
1595

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

    
1605
function mac_format($clientmac) {
1606
	global $config, $cpzone;
1607

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

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

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

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

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

    
1624
		default:
1625
			return $clientmac;
1626
	}
1627
}
1628

    
1629
function resolve_retry($hostname, $retries = 5) {
1630

    
1631
	if (is_ipaddr($hostname)) {
1632
		return $hostname;
1633
	}
1634

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

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

    
1644
		sleep(1);
1645
	}
1646

    
1647
	return false;
1648
}
1649

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

    
1662
function update_filter_reload_status($text) {
1663
	global $g;
1664

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

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

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

    
1696
function run_plugins($directory) {
1697
	global $config, $g;
1698

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

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

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

    
1726
/*
1727
 * get_sysctl($names)
1728
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1729
 * name) and return an array of key/value pairs set for those that exist
1730
 */
1731
function get_sysctl($names) {
1732
	if (empty($names)) {
1733
		return array();
1734
	}
1735

    
1736
	if (is_array($names)) {
1737
		$name_list = array();
1738
		foreach ($names as $name) {
1739
			$name_list[] = escapeshellarg($name);
1740
		}
1741
	} else {
1742
		$name_list = array(escapeshellarg($names));
1743
	}
1744

    
1745
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1746
	$values = array();
1747
	foreach ($output as $line) {
1748
		$line = explode(": ", $line, 2);
1749
		if (count($line) == 2) {
1750
			$values[$line[0]] = $line[1];
1751
		}
1752
	}
1753

    
1754
	return $values;
1755
}
1756

    
1757
/*
1758
 * get_single_sysctl($name)
1759
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
1760
 * return the value for sysctl $name or empty string if it doesn't exist
1761
 */
1762
function get_single_sysctl($name) {
1763
	if (empty($name)) {
1764
		return "";
1765
	}
1766

    
1767
	$value = get_sysctl($name);
1768
	if (empty($value) || !isset($value[$name])) {
1769
		return "";
1770
	}
1771

    
1772
	return $value[$name];
1773
}
1774

    
1775
/*
1776
 * set_sysctl($value_list)
1777
 * Set sysctl OID's listed as key/value pairs and return
1778
 * an array with keys set for those that succeeded
1779
 */
1780
function set_sysctl($values) {
1781
	if (empty($values)) {
1782
		return array();
1783
	}
1784

    
1785
	$value_list = array();
1786
	foreach ($values as $key => $value) {
1787
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1788
	}
1789

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

    
1792
	/* Retry individually if failed (one or more read-only) */
1793
	if ($success <> 0 && count($value_list) > 1) {
1794
		foreach ($value_list as $value) {
1795
			exec("/sbin/sysctl -i " . $value, $output);
1796
		}
1797
	}
1798

    
1799
	$ret = array();
1800
	foreach ($output as $line) {
1801
		$line = explode(": ", $line, 2);
1802
		if (count($line) == 2) {
1803
			$ret[$line[0]] = true;
1804
		}
1805
	}
1806

    
1807
	return $ret;
1808
}
1809

    
1810
/*
1811
 * set_single_sysctl($name, $value)
1812
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
1813
 * returns boolean meaning if it succeeded
1814
 */
1815
function set_single_sysctl($name, $value) {
1816
	if (empty($name)) {
1817
		return false;
1818
	}
1819

    
1820
	$result = set_sysctl(array($name => $value));
1821

    
1822
	if (!isset($result[$name]) || $result[$name] != $value) {
1823
		return false;
1824
	}
1825

    
1826
	return true;
1827
}
1828

    
1829
/*
1830
 *     get_memory()
1831
 *     returns an array listing the amount of
1832
 *     memory installed in the hardware
1833
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
1834
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
1835
 */
1836
function get_memory() {
1837
	$physmem = get_single_sysctl("hw.physmem");
1838
	$realmem = get_single_sysctl("hw.realmem");
1839
	/* convert from bytes to megabytes */
1840
	return array(($physmem/1048576), ($realmem/1048576));
1841
}
1842

    
1843
function mute_kernel_msgs() {
1844
	global $g, $config;
1845
	// Do not mute serial console.  The kernel gets very very cranky
1846
	// and will start dishing you cannot control tty errors.
1847
	if ($g['platform'] == 'nanobsd') {
1848
		return;
1849
	}
1850
	if ($config['system']['enableserial']) {
1851
		return;
1852
	}
1853
	exec("/sbin/conscontrol mute on");
1854
}
1855

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

    
1866
function start_devd() {
1867
	/* Use the undocumented -q options of devd to quiet its log spamming */
1868
	$_gb = exec("/sbin/devd -q");
1869
	sleep(1);
1870
	unset($_gb);
1871
}
1872

    
1873
function is_interface_vlan_mismatch() {
1874
	global $config, $g;
1875

    
1876
	if (is_array($config['vlans']['vlan'])) {
1877
		foreach ($config['vlans']['vlan'] as $vlan) {
1878
			if (does_interface_exist($vlan['if']) == false) {
1879
				return true;
1880
			}
1881
		}
1882
	}
1883

    
1884
	return false;
1885
}
1886

    
1887
function is_interface_mismatch() {
1888
	global $config, $g;
1889

    
1890
	$do_assign = false;
1891
	$i = 0;
1892
	$missing_interfaces = array();
1893
	if (is_array($config['interfaces'])) {
1894
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1895
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1896
				// Do not check these interfaces.
1897
				$i++;
1898
				continue;
1899
			} else if (does_interface_exist($ifcfg['if']) == false) {
1900
				$missing_interfaces[] = $ifcfg['if'];
1901
				$do_assign = true;
1902
			} else {
1903
				$i++;
1904
			}
1905
		}
1906
	}
1907

    
1908
	if (file_exists("{$g['tmp_path']}/assign_complete")) {
1909
		$do_assign = false;
1910
	}
1911

    
1912
	if (!empty($missing_interfaces) && $do_assign) {
1913
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1914
	} else {
1915
		@unlink("{$g['tmp_path']}/missing_interfaces");
1916
	}
1917

    
1918
	return $do_assign;
1919
}
1920

    
1921
/* sync carp entries to other firewalls */
1922
function carp_sync_client() {
1923
	global $g;
1924
	send_event("filter sync");
1925
}
1926

    
1927
/****f* util/isAjax
1928
 * NAME
1929
 *   isAjax - reports if the request is driven from prototype
1930
 * INPUTS
1931
 *   none
1932
 * RESULT
1933
 *   true/false
1934
 ******/
1935
function isAjax() {
1936
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
1937
}
1938

    
1939
/****f* util/timeout
1940
 * NAME
1941
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
1942
 * INPUTS
1943
 *   optional, seconds to wait before timeout. Default 9 seconds.
1944
 * RESULT
1945
 *   returns 1 char of user input or null if no input.
1946
 ******/
1947
function timeout($timer = 9) {
1948
	while (!isset($key)) {
1949
		if ($timer >= 9) {
1950
			echo chr(8) . chr(8) . ($timer == 9 ? chr(32) : null) . "{$timer}";
1951
		} else {
1952
			echo chr(8). "{$timer}";
1953
		}
1954
		`/bin/stty -icanon min 0 time 25`;
1955
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
1956
		`/bin/stty icanon`;
1957
		if ($key == '') {
1958
			unset($key);
1959
		}
1960
		$timer--;
1961
		if ($timer == 0) {
1962
			break;
1963
		}
1964
	}
1965
	return $key;
1966
}
1967

    
1968
/****f* util/msort
1969
 * NAME
1970
 *   msort - sort array
1971
 * INPUTS
1972
 *   $array to be sorted, field to sort by, direction of sort
1973
 * RESULT
1974
 *   returns newly sorted array
1975
 ******/
1976
function msort($array, $id = "id", $sort_ascending = true) {
1977
	$temp_array = array();
1978
	while (count($array)>0) {
1979
		$lowest_id = 0;
1980
		$index = 0;
1981
		foreach ($array as $item) {
1982
			if (isset($item[$id])) {
1983
				if ($array[$lowest_id][$id]) {
1984
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
1985
						$lowest_id = $index;
1986
					}
1987
				}
1988
			}
1989
			$index++;
1990
		}
1991
		$temp_array[] = $array[$lowest_id];
1992
		$array = array_merge(array_slice($array, 0, $lowest_id), array_slice($array, $lowest_id + 1));
1993
	}
1994
	if ($sort_ascending) {
1995
		return $temp_array;
1996
	} else {
1997
		return array_reverse($temp_array);
1998
	}
1999
}
2000

    
2001
/****f* util/is_URL
2002
 * NAME
2003
 *   is_URL
2004
 * INPUTS
2005
 *   string to check
2006
 * RESULT
2007
 *   Returns true if item is a URL
2008
 ******/
2009
function is_URL($url) {
2010
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
2011
	if ($match) {
2012
		return true;
2013
	}
2014
	return false;
2015
}
2016

    
2017
function is_file_included($file = "") {
2018
	$files = get_included_files();
2019
	if (in_array($file, $files)) {
2020
		return true;
2021
	}
2022

    
2023
	return false;
2024
}
2025

    
2026
/*
2027
 * Replace a value on a deep associative array using regex
2028
 */
2029
function array_replace_values_recursive($data, $match, $replace) {
2030
	if (empty($data)) {
2031
		return $data;
2032
	}
2033

    
2034
	if (is_string($data)) {
2035
		$data = preg_replace("/{$match}/", $replace, $data);
2036
	} else if (is_array($data)) {
2037
		foreach ($data as $k => $v) {
2038
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
2039
		}
2040
	}
2041

    
2042
	return $data;
2043
}
2044

    
2045
/*
2046
	This function was borrowed from a comment on PHP.net at the following URL:
2047
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
2048
 */
2049
function array_merge_recursive_unique($array0, $array1) {
2050

    
2051
	$arrays = func_get_args();
2052
	$remains = $arrays;
2053

    
2054
	// We walk through each arrays and put value in the results (without
2055
	// considering previous value).
2056
	$result = array();
2057

    
2058
	// loop available array
2059
	foreach ($arrays as $array) {
2060

    
2061
		// The first remaining array is $array. We are processing it. So
2062
		// we remove it from remaining arrays.
2063
		array_shift($remains);
2064

    
2065
		// We don't care non array param, like array_merge since PHP 5.0.
2066
		if (is_array($array)) {
2067
			// Loop values
2068
			foreach ($array as $key => $value) {
2069
				if (is_array($value)) {
2070
					// we gather all remaining arrays that have such key available
2071
					$args = array();
2072
					foreach ($remains as $remain) {
2073
						if (array_key_exists($key, $remain)) {
2074
							array_push($args, $remain[$key]);
2075
						}
2076
					}
2077

    
2078
					if (count($args) > 2) {
2079
						// put the recursion
2080
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
2081
					} else {
2082
						foreach ($value as $vkey => $vval) {
2083
							$result[$key][$vkey] = $vval;
2084
						}
2085
					}
2086
				} else {
2087
					// simply put the value
2088
					$result[$key] = $value;
2089
				}
2090
			}
2091
		}
2092
	}
2093
	return $result;
2094
}
2095

    
2096

    
2097
/*
2098
 * converts a string like "a,b,c,d"
2099
 * into an array like array("a" => "b", "c" => "d")
2100
 */
2101
function explode_assoc($delimiter, $string) {
2102
	$array = explode($delimiter, $string);
2103
	$result = array();
2104
	$numkeys = floor(count($array) / 2);
2105
	for ($i = 0; $i < $numkeys; $i += 1) {
2106
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
2107
	}
2108
	return $result;
2109
}
2110

    
2111
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
2112
	global $config, $aliastable;
2113

    
2114
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
2115
	if (!is_array($config['staticroutes']['route'])) {
2116
		return array();
2117
	}
2118

    
2119
	$allstaticroutes = array();
2120
	$allsubnets = array();
2121
	/* Loop through routes and expand aliases as we find them. */
2122
	foreach ($config['staticroutes']['route'] as $route) {
2123
		if (is_alias($route['network'])) {
2124
			if (!isset($aliastable[$route['network']])) {
2125
				continue;
2126
			}
2127

    
2128
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
2129
			foreach ($subnets as $net) {
2130
				if (!is_subnet($net)) {
2131
					if (is_ipaddrv4($net)) {
2132
						$net .= "/32";
2133
					} else if (is_ipaddrv6($net)) {
2134
						$net .= "/128";
2135
					} else if ($returnhostnames === false || !is_fqdn($net)) {
2136
						continue;
2137
					}
2138
				}
2139
				$temproute = $route;
2140
				$temproute['network'] = $net;
2141
				$allstaticroutes[] = $temproute;
2142
				$allsubnets[] = $net;
2143
			}
2144
		} elseif (is_subnet($route['network'])) {
2145
			$allstaticroutes[] = $route;
2146
			$allsubnets[] = $route['network'];
2147
		}
2148
	}
2149
	if ($returnsubnetsonly) {
2150
		return $allsubnets;
2151
	} else {
2152
		return $allstaticroutes;
2153
	}
2154
}
2155

    
2156
/****f* util/get_alias_list
2157
 * NAME
2158
 *   get_alias_list - Provide a list of aliases.
2159
 * INPUTS
2160
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
2161
 * RESULT
2162
 *   Array containing list of aliases.
2163
 *   If $type is unspecified, all aliases are returned.
2164
 *   If $type is a string, all aliases of the type specified in $type are returned.
2165
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
2166
 */
2167
function get_alias_list($type = null) {
2168
	global $config;
2169
	$result = array();
2170
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
2171
		foreach ($config['aliases']['alias'] as $alias) {
2172
			if ($type === null) {
2173
				$result[] = $alias['name'];
2174
			} else if (is_array($type)) {
2175
				if (in_array($alias['type'], $type)) {
2176
					$result[] = $alias['name'];
2177
				}
2178
			} else if ($type === $alias['type']) {
2179
				$result[] = $alias['name'];
2180
			}
2181
		}
2182
	}
2183
	return $result;
2184
}
2185

    
2186
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
2187
function array_exclude($needle, $haystack) {
2188
	$result = array();
2189
	if (is_array($haystack)) {
2190
		foreach ($haystack as $thing) {
2191
			if ($needle !== $thing) {
2192
				$result[] = $thing;
2193
			}
2194
		}
2195
	}
2196
	return $result;
2197
}
2198

    
2199
function get_current_theme() {
2200
	global $config, $g;
2201
	/*
2202
	 *   if user has selected a custom template, use it.
2203
	 *   otherwise default to pfsense template
2204
	 */
2205
	if (($g["disablethemeselection"] === true) && !empty($g["default_theme"]) && (is_dir($g["www_path"].'/themes/'.$g["default_theme"]))) {
2206
		$theme = $g["default_theme"];
2207
	} elseif ($config['theme'] <> "" && (is_dir($g["www_path"].'/themes/'.$config['theme']))) {
2208
		$theme = $config['theme'];
2209
	} else {
2210
		$theme = "pfsense";
2211
	}
2212
	/*
2213
	 *  If this device is an apple ipod/iphone
2214
	 *  switch the theme to one that works with it.
2215
	 */
2216
	$lowres_ua = array("iPhone", "iPod", "iPad", "Android", "BlackBerry", "Opera Mini", "Opera Mobi", "PlayBook", "IEMobile");
2217
	foreach ($lowres_ua as $useragent) {
2218
		if (strstr($_SERVER['HTTP_USER_AGENT'], $useragent)) {
2219
			$theme = (empty($g['theme_lowres']) && (is_dir($g["www_path"].'/themes/'.$g['theme_lowres']))) ? "pfsense" : $g['theme_lowres'];
2220
		}
2221
	}
2222
	return $theme;
2223
}
2224

    
2225
/* Define what is preferred, IPv4 or IPv6 */
2226
function prefer_ipv4_or_ipv6() {
2227
	global $config;
2228

    
2229
	if (isset($config['system']['prefer_ipv4'])) {
2230
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2231
	} else {
2232
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2233
	}
2234
}
2235

    
2236
/* Redirect to page passing parameters via POST */
2237
function post_redirect($page, $params) {
2238
	if (!is_array($params)) {
2239
		return;
2240
	}
2241

    
2242
	print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n";
2243
	foreach ($params as $key => $value) {
2244
		print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n";
2245
	}
2246
	print "</form><script type=\"text/javascript\">document.formredir.submit();</script>\n";
2247
	print "</body></html>\n";
2248
}
2249

    
2250
/* Locate disks that can be queried for S.M.A.R.T. data. */
2251
function get_smart_drive_list() {
2252
	$disk_list = explode(" ", get_single_sysctl("kern.disks"));
2253
	foreach ($disk_list as $id => $disk) {
2254
		// We only want certain kinds of disks for S.M.A.R.T.
2255
		// 1 is a match, 0 is no match, False is any problem processing the regex
2256
		if (preg_match("/^(ad|da|ada).*[0-9]{1,2}$/", $disk) !== 1) {
2257
			unset($disk_list[$id]);
2258
		}
2259
	}
2260
	sort($disk_list);
2261
	return $disk_list;
2262
}
2263

    
2264
?>
(56-56/68)