Project

General

Profile

Download (57.6 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) {
834
	if (!is_string($hostname)) {
835
		return false;
836
	}
837

    
838
	if (is_domain($hostname)) {
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) {
852
	if (!is_string($domain)) {
853
		return false;
854
	}
855

    
856
	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)) {
857
		return true;
858
	} else {
859
		return false;
860
	}
861
}
862

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

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

    
877
function is_validaliasname($name) {
878
	/* Array of reserved words */
879
	$reserved = array("port", "pass");
880

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

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

    
901
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
902
function is_portrange($portrange) {
903
	$ports = explode(":", $portrange);
904

    
905
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
906
}
907

    
908
/* returns true if $port is a valid port number or an alias thereof */
909
function is_portoralias($port) {
910
	global $config;
911

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

    
926
/* create ranges of sequential port numbers (200:215) and remove duplicates */
927
function group_ports($ports) {
928
	if (!is_array($ports) || empty($ports)) {
929
		return;
930
	}
931

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

    
954
	$result = array();
955
	foreach ($uniq as $idx => $port) {
956
		if ($idx == 0) {
957
			$result[] = $port;
958
			continue;
959
		}
960

    
961
		$last = end($result);
962
		if (is_portrange($last)) {
963
			list($begin, $end) = explode(":", $last);
964
		} else {
965
			$begin = $end = $last;
966
		}
967

    
968
		if ($port == ($end+1)) {
969
			$end++;
970
			$result[count($result)-1] = "{$begin}:{$end}";
971
		} else {
972
			$result[] = $port;
973
		}
974
	}
975

    
976
	return $result;
977
}
978

    
979
/* returns true if $val is a valid shaper bandwidth value */
980
function is_valid_shaperbw($val) {
981
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
982
}
983

    
984
/* returns true if $test is in the range between $start and $end */
985
function is_inrange_v4($test, $start, $end) {
986
	if ((ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start))) {
987
		return true;
988
	} else {
989
		return false;
990
	}
991
}
992

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

    
1002
/* returns true if $test is in the range between $start and $end */
1003
function is_inrange($test, $start, $end) {
1004
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
1005
}
1006

    
1007
/* XXX: return the configured carp interface list */
1008
function get_configured_carp_interface_list($carpinterface = '', $family = 'inet', $what = 'ip') {
1009
	global $config;
1010

    
1011
	$iflist = array();
1012

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

    
1060
	return $iflist;
1061
}
1062

    
1063
/* return the configured IP aliases list */
1064
function get_configured_ip_aliases_list($returnfullentry = false) {
1065
	global $config;
1066

    
1067
	$alias_list = array();
1068

    
1069
	if (is_array($config['virtualip']['vip'])) {
1070
		$viparr = &$config['virtualip']['vip'];
1071
		foreach ($viparr as $vip) {
1072
			if ($vip['mode'] == "ipalias") {
1073
				if ($returnfullentry) {
1074
					$alias_list[$vip['subnet']] = $vip;
1075
				} else {
1076
					$alias_list[$vip['subnet']] = $vip['interface'];
1077
				}
1078
			}
1079
		}
1080
	}
1081

    
1082
	return $alias_list;
1083
}
1084

    
1085
/* return all configured aliases list (IP, carp, proxyarp and other) */
1086
function get_configured_vips_list() {
1087
	global $config;
1088

    
1089
	$alias_list = array();
1090

    
1091
	if (is_array($config['virtualip']['vip'])) {
1092
		$viparr = &$config['virtualip']['vip'];
1093
		foreach ($viparr as $vip) {
1094
			if ($vip['mode'] == "carp") {
1095
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => "{$vip['interface']}_vip{$vip['vhid']}");
1096
			} else {
1097
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
1098
			}
1099
		}
1100
	}
1101

    
1102
	return $alias_list;
1103
}
1104

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

    
1119
	return strnatcmp($a, $b);
1120
}
1121

    
1122
/* return the configured interfaces list. */
1123
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
1124
	global $config;
1125

    
1126
	$iflist = array();
1127

    
1128
	/* if list */
1129
	foreach ($config['interfaces'] as $if => $ifdetail) {
1130
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1131
			continue;
1132
		}
1133
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1134
			$iflist[$if] = $if;
1135
		}
1136
	}
1137

    
1138
	return $iflist;
1139
}
1140

    
1141
/* return the configured interfaces list. */
1142
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
1143
	global $config;
1144

    
1145
	$iflist = array();
1146

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

    
1160
	return $iflist;
1161
}
1162

    
1163
/* return the configured interfaces list with their description. */
1164
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
1165
	global $config;
1166

    
1167
	$iflist = array();
1168

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

    
1183
	return $iflist;
1184
}
1185

    
1186
/*
1187
 *   get_configured_ip_addresses() - Return a list of all configured
1188
 *   interfaces IP Addresses
1189
 *
1190
 */
1191
function get_configured_ip_addresses() {
1192
	global $config;
1193

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

    
1212
	/* pppoe server */
1213
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
1214
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
1215
			if ($pppoe['mode'] == "server") {
1216
				if (is_ipaddr($pppoe['localip'])) {
1217
					$int = "pppoes". $pppoe['pppoeid'];
1218
					$ip_array[$int] = $pppoe['localip'];
1219
				}
1220
			}
1221
		}
1222
	}
1223

    
1224
	return $ip_array;
1225
}
1226

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

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

    
1355
			case "friendly":
1356
				if ($friendly != "") {
1357
					$toput['if'] = $ifname;
1358
					$iflist[$friendly] = $toput;
1359
				}
1360
				break;
1361
			}
1362
		}
1363
	}
1364
	return $iflist;
1365
}
1366

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

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

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

    
1423
/* wrapper for exec() */
1424
function mwexec($command, $mute = false, $clearsigmask = false) {
1425
	global $g;
1426

    
1427
	if ($g['debug']) {
1428
		if (!$_SERVER['REMOTE_ADDR']) {
1429
			echo "mwexec(): $command\n";
1430
		}
1431
	}
1432
	$oarr = array();
1433
	$retval = 0;
1434

    
1435
	if ($clearsigmask) {
1436
		$oldset = array();
1437
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1438
	}
1439
	$garbage = exec("$command 2>&1", $oarr, $retval);
1440
	if ($clearsigmask) {
1441
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1442
	}
1443

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

    
1456
/* wrapper for exec() in background */
1457
function mwexec_bg($command, $clearsigmask = false) {
1458
	global $g;
1459

    
1460
	if ($g['debug']) {
1461
		if (!$_SERVER['REMOTE_ADDR']) {
1462
			echo "mwexec(): $command\n";
1463
		}
1464
	}
1465

    
1466
	if ($clearsigmask) {
1467
		$oldset = array();
1468
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1469
	}
1470
	$_gb = exec("/usr/bin/nohup $command > /dev/null 2>&1 &");
1471
	if ($clearsigmask) {
1472
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1473
	}
1474
	unset($_gb);
1475
}
1476

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

    
1492
	$aliastable = array();
1493

    
1494
	if (is_array($config['aliases']['alias'])) {
1495
		foreach ($config['aliases']['alias'] as $alias) {
1496
			if ($alias['name']) {
1497
				$aliastable[$alias['name']] = $alias['address'];
1498
			}
1499
		}
1500
	}
1501
}
1502

    
1503
/* check if an alias exists */
1504
function is_alias($name) {
1505
	global $aliastable;
1506

    
1507
	return isset($aliastable[$name]);
1508
}
1509

    
1510
function alias_get_type($name) {
1511
	global $config;
1512

    
1513
	if (is_array($config['aliases']['alias'])) {
1514
		foreach ($config['aliases']['alias'] as $alias) {
1515
			if ($name == $alias['name']) {
1516
				return $alias['type'];
1517
			}
1518
		}
1519
	}
1520

    
1521
	return "";
1522
}
1523

    
1524
/* expand a host or network alias, if necessary */
1525
function alias_expand($name) {
1526
	global $aliastable;
1527

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

    
1541
function alias_expand_urltable($name) {
1542
	global $config;
1543
	$urltable_prefix = "/var/db/aliastables/";
1544
	$urltable_filename = $urltable_prefix . $name . ".txt";
1545

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

    
1561
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1562
function verify_digital_signature($fname) {
1563
	global $g;
1564

    
1565
	if (!file_exists("/usr/local/sbin/gzsig")) {
1566
		return 4;
1567
	}
1568

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

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

    
1578
	if ($arpoutput[0]) {
1579
		$arpi = explode(" ", $arpoutput[0]);
1580
		$macaddr = $arpi[3];
1581
		if (is_macaddr($macaddr)) {
1582
			return $macaddr;
1583
		} else {
1584
			return false;
1585
		}
1586
	}
1587

    
1588
	return false;
1589
}
1590

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

    
1600
function mac_format($clientmac) {
1601
	global $config, $cpzone;
1602

    
1603
	$mac = explode(":", $clientmac);
1604
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1605

    
1606
	switch ($mac_format) {
1607
		case 'singledash':
1608
			return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1609

    
1610
		case 'ietf':
1611
			return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1612

    
1613
		case 'cisco':
1614
			return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1615

    
1616
		case 'unformatted':
1617
			return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1618

    
1619
		default:
1620
			return $clientmac;
1621
	}
1622
}
1623

    
1624
function resolve_retry($hostname, $retries = 5) {
1625

    
1626
	if (is_ipaddr($hostname)) {
1627
		return $hostname;
1628
	}
1629

    
1630
	for ($i = 0; $i < $retries; $i++) {
1631
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1632
		$ip = gethostbyname($hostname);
1633

    
1634
		if ($ip && $ip != $hostname) {
1635
			/* success */
1636
			return $ip;
1637
		}
1638

    
1639
		sleep(1);
1640
	}
1641

    
1642
	return false;
1643
}
1644

    
1645
function format_bytes($bytes) {
1646
	if ($bytes >= 1073741824) {
1647
		return sprintf("%.2f GB", $bytes/1073741824);
1648
	} else if ($bytes >= 1048576) {
1649
		return sprintf("%.2f MB", $bytes/1048576);
1650
	} else if ($bytes >= 1024) {
1651
		return sprintf("%.0f KB", $bytes/1024);
1652
	} else {
1653
		return sprintf("%d bytes", $bytes);
1654
	}
1655
}
1656

    
1657
function update_filter_reload_status($text) {
1658
	global $g;
1659

    
1660
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1661
}
1662

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

    
1681
				if (empty($filter_regex) || preg_match($filter_regex, $file)) {
1682
					array_push($dir_array, $file);
1683
				}
1684
			}
1685
			closedir($dh);
1686
		}
1687
	}
1688
	return $dir_array;
1689
}
1690

    
1691
function run_plugins($directory) {
1692
	global $config, $g;
1693

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

    
1707
/*
1708
 *    safe_mkdir($path, $mode = 0755)
1709
 *    create directory if it doesn't already exist and isn't a file!
1710
 */
1711
function safe_mkdir($path, $mode = 0755) {
1712
	global $g;
1713

    
1714
	if (!is_file($path) && !is_dir($path)) {
1715
		return @mkdir($path, $mode, true);
1716
	} else {
1717
		return false;
1718
	}
1719
}
1720

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

    
1731
	if (is_array($names)) {
1732
		$name_list = array();
1733
		foreach ($names as $name) {
1734
			$name_list[] = escapeshellarg($name);
1735
		}
1736
	} else {
1737
		$name_list = array(escapeshellarg($names));
1738
	}
1739

    
1740
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1741
	$values = array();
1742
	foreach ($output as $line) {
1743
		$line = explode(": ", $line, 2);
1744
		if (count($line) == 2) {
1745
			$values[$line[0]] = $line[1];
1746
		}
1747
	}
1748

    
1749
	return $values;
1750
}
1751

    
1752
/*
1753
 * get_single_sysctl($name)
1754
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
1755
 * return the value for sysctl $name or empty string if it doesn't exist
1756
 */
1757
function get_single_sysctl($name) {
1758
	if (empty($name)) {
1759
		return "";
1760
	}
1761

    
1762
	$value = get_sysctl($name);
1763
	if (empty($value) || !isset($value[$name])) {
1764
		return "";
1765
	}
1766

    
1767
	return $value[$name];
1768
}
1769

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

    
1780
	$value_list = array();
1781
	foreach ($values as $key => $value) {
1782
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1783
	}
1784

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

    
1787
	/* Retry individually if failed (one or more read-only) */
1788
	if ($success <> 0 && count($value_list) > 1) {
1789
		foreach ($value_list as $value) {
1790
			exec("/sbin/sysctl -i " . $value, $output);
1791
		}
1792
	}
1793

    
1794
	$ret = array();
1795
	foreach ($output as $line) {
1796
		$line = explode(": ", $line, 2);
1797
		if (count($line) == 2) {
1798
			$ret[$line[0]] = true;
1799
		}
1800
	}
1801

    
1802
	return $ret;
1803
}
1804

    
1805
/*
1806
 * set_single_sysctl($name, $value)
1807
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
1808
 * returns boolean meaning if it succeeded
1809
 */
1810
function set_single_sysctl($name, $value) {
1811
	if (empty($name)) {
1812
		return false;
1813
	}
1814

    
1815
	$result = set_sysctl(array($name => $value));
1816

    
1817
	if (!isset($result[$name]) || $result[$name] != $value) {
1818
		return false;
1819
	}
1820

    
1821
	return true;
1822
}
1823

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

    
1838
function mute_kernel_msgs() {
1839
	global $config;
1840
	// Do not mute serial console.  The kernel gets very very cranky
1841
	// and will start dishing you cannot control tty errors.
1842
	switch (trim(file_get_contents("/etc/platform"))) {
1843
		case "nanobsd":
1844
		case "jail":
1845
			return;
1846
	}
1847
	if ($config['system']['enableserial']) {
1848
		return;
1849
	}
1850
	exec("/sbin/conscontrol mute on");
1851
}
1852

    
1853
function unmute_kernel_msgs() {
1854
	global $config;
1855
	// Do not mute serial console.  The kernel gets very very cranky
1856
	// and will start dishing you cannot control tty errors.
1857
	switch (trim(file_get_contents("/etc/platform"))) {
1858
		case "nanobsd":
1859
		case "jail":
1860
			return;
1861
	}
1862
	exec("/sbin/conscontrol mute off");
1863
}
1864

    
1865
function start_devd() {
1866
	global $g;
1867

    
1868
	if ($g['platform'] == 'jail') {
1869
		return;
1870
	}
1871
	/* Use the undocumented -q options of devd to quiet its log spamming */
1872
	$_gb = exec("/sbin/devd -q");
1873
	sleep(1);
1874
	unset($_gb);
1875
}
1876

    
1877
function is_interface_vlan_mismatch() {
1878
	global $config, $g;
1879

    
1880
	if (is_array($config['vlans']['vlan'])) {
1881
		foreach ($config['vlans']['vlan'] as $vlan) {
1882
			if (does_interface_exist($vlan['if']) == false) {
1883
				return true;
1884
			}
1885
		}
1886
	}
1887

    
1888
	return false;
1889
}
1890

    
1891
function is_interface_mismatch() {
1892
	global $config, $g;
1893

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

    
1912
	if (file_exists("{$g['tmp_path']}/assign_complete")) {
1913
		$do_assign = false;
1914
	}
1915

    
1916
	if (!empty($missing_interfaces) && $do_assign) {
1917
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1918
	} else {
1919
		@unlink("{$g['tmp_path']}/missing_interfaces");
1920
	}
1921

    
1922
	return $do_assign;
1923
}
1924

    
1925
/* sync carp entries to other firewalls */
1926
function carp_sync_client() {
1927
	global $g;
1928
	send_event("filter sync");
1929
}
1930

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

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

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

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

    
2021
function is_file_included($file = "") {
2022
	$files = get_included_files();
2023
	if (in_array($file, $files)) {
2024
		return true;
2025
	}
2026

    
2027
	return false;
2028
}
2029

    
2030
/*
2031
 * Replace a value on a deep associative array using regex
2032
 */
2033
function array_replace_values_recursive($data, $match, $replace) {
2034
	if (empty($data)) {
2035
		return $data;
2036
	}
2037

    
2038
	if (is_string($data)) {
2039
		$data = preg_replace("/{$match}/", $replace, $data);
2040
	} else if (is_array($data)) {
2041
		foreach ($data as $k => $v) {
2042
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
2043
		}
2044
	}
2045

    
2046
	return $data;
2047
}
2048

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

    
2055
	$arrays = func_get_args();
2056
	$remains = $arrays;
2057

    
2058
	// We walk through each arrays and put value in the results (without
2059
	// considering previous value).
2060
	$result = array();
2061

    
2062
	// loop available array
2063
	foreach ($arrays as $array) {
2064

    
2065
		// The first remaining array is $array. We are processing it. So
2066
		// we remove it from remaining arrays.
2067
		array_shift($remains);
2068

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

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

    
2100

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

    
2115
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
2116
	global $config, $aliastable;
2117

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

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

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

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

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

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

    
2229
/* Define what is preferred, IPv4 or IPv6 */
2230
function prefer_ipv4_or_ipv6() {
2231
	global $config;
2232

    
2233
	if (isset($config['system']['prefer_ipv4'])) {
2234
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2235
	} else {
2236
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2237
	}
2238
}
2239

    
2240
/* Redirect to page passing parameters via POST */
2241
function post_redirect($page, $params) {
2242
	if (!is_array($params)) {
2243
		return;
2244
	}
2245

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

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

    
2268
?>
(56-56/68)