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

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

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

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

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

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

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

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

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

    
502
	return $rangeaddresses;
503
}
504

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
654
	return is_ipaddrv6($ipaddr);
655
}
656

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

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

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

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

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

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

    
707
}
708

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

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

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

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

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

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

    
759

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

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

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

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

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

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

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

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

    
812
	return (is_inrange_v6($sub1_min, $sub2_min, $sub2_max) || is_inrange_v6($sub1_max, $sub2_min, $sub2_max) || is_inrange_v6($sub2_min, $sub1_min, $sub1_max));
813
}
814

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

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

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

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

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

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

    
864
	if (preg_match('/^(?:(?:[a-z_0-9]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9])\.)*(?:[a-z_0-9]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9\.])$/i', $domain)) {
865
		return true;
866
	} else {
867
		return false;
868
	}
869
}
870

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
984
	return $result;
985
}
986

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

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

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

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

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

    
1019
	$iflist = array();
1020

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

    
1068
	return $iflist;
1069
}
1070

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

    
1075
	$alias_list=array();
1076

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

    
1090
	return $alias_list;
1091
}
1092

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

    
1097
	$alias_list=array();
1098

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

    
1110
	return $alias_list;
1111
}
1112

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

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

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

    
1134
	$iflist = array();
1135

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

    
1146
	return $iflist;
1147
}
1148

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

    
1153
	$iflist = array();
1154

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

    
1168
	return $iflist;
1169
}
1170

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

    
1175
	$iflist = array();
1176

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

    
1191
	return $iflist;
1192
}
1193

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

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

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

    
1232
	return $ip_array;
1233
}
1234

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1500
	$aliastable = array();
1501

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

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

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

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

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

    
1529
	return "";
1530
}
1531

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

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

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

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

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

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

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

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

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

    
1595
	return false;
1596
}
1597

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

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

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

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

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

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

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

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

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

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

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

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

    
1646
		sleep(1);
1647
	}
1648

    
1649
	return false;
1650
}
1651

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

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

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

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

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

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

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

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

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

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

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

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

    
1756
	return $values;
1757
}
1758

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

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

    
1774
	return $value[$name];
1775
}
1776

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

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

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

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

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

    
1809
	return $ret;
1810
}
1811

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

    
1822
	$result = set_sysctl(array($name => $value));
1823

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

    
1828
	return true;
1829
}
1830

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

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

    
1860
function unmute_kernel_msgs() {
1861
	global $config;
1862
	// Do not mute serial console.  The kernel gets very very cranky
1863
	// and will start dishing you cannot control tty errors.
1864
	switch (trim(file_get_contents("/etc/platform"))) {
1865
		case "nanobsd":
1866
		case "jail":
1867
			return;
1868
	}
1869
	exec("/sbin/conscontrol mute off");
1870
}
1871

    
1872
function start_devd() {
1873
	global $g;
1874

    
1875
	if ($g['platform'] == 'jail') {
1876
		return;
1877
	}
1878
	/* Use the undocumented -q options of devd to quiet its log spamming */
1879
	$_gb = exec("/sbin/devd -q");
1880
	sleep(1);
1881
	unset($_gb);
1882
}
1883

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

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

    
1895
	return false;
1896
}
1897

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

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

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

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

    
1929
	return $do_assign;
1930
}
1931

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

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

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

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

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

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

    
2034
	return false;
2035
}
2036

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

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

    
2053
	return $data;
2054
}
2055

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

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

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

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

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

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

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

    
2107

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

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

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

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

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

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

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

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

    
2236
/* Define what is preferred, IPv4 or IPv6 */
2237
function prefer_ipv4_or_ipv6() {
2238
	global $config;
2239

    
2240
	if (isset($config['system']['prefer_ipv4'])) {
2241
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2242
	} else {
2243
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2244
	}
2245
}
2246

    
2247
/* Redirect to page passing parameters via POST */
2248
function post_redirect($page, $params) {
2249
	if (!is_array($params)) {
2250
		return;
2251
	}
2252

    
2253
	print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n";
2254
	foreach ($params as $key => $value) {
2255
		print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n";
2256
	}
2257
	print "</form><script type=\"text/javascript\">document.formredir.submit();</script>\n";
2258
	print "</body></html>\n";
2259
}
2260

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

    
2275
?>
(55-55/67)