Project

General

Profile

Download (56.5 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
	return 0;
71
}
72

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

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

    
85
function is_subsystem_dirty($subsystem = "") {
86
	global $g;
87

    
88
	if ($subsystem == "")
89
		return false;
90

    
91
	if (file_exists("{$g['varrun_path']}/{$subsystem}.dirty"))
92
		return true;
93

    
94
	return false;
95
}
96

    
97
function mark_subsystem_dirty($subsystem = "") {
98
	global $g;
99

    
100
	if (!file_put_contents("{$g['varrun_path']}/{$subsystem}.dirty", "DIRTY"))
101
		log_error(sprintf(gettext("WARNING: Could not mark subsystem: %s dirty"), $subsystem));
102
}
103

    
104
function clear_subsystem_dirty($subsystem = "") {
105
	global $g;
106

    
107
	@unlink("{$g['varrun_path']}/{$subsystem}.dirty");
108
}
109

    
110
function config_lock() {
111
	return;
112
}
113
function config_unlock() {
114
	return;
115
}
116

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

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

    
155
		return $fp;
156
	}
157

    
158
	return NULL;
159
}
160

    
161
/* unlock configuration file */
162
function unlock($cfglckkey = 0) {
163
	global $g, $cfglckkeyconsumers;
164
	flock($cfglckkey, LOCK_UN);
165
	fclose($cfglckkey);
166
	return;
167
}
168

    
169
/* unlock forcefully configuration file */
170
function unlock_force($lock) {
171
	global $g;
172

    
173
	@unlink("{$g['tmp_path']}/{$lock}.lock");
174
}
175

    
176
function send_event($cmd) {
177
	global $g;
178

    
179
	if(!isset($g['event_address']))
180
		$g['event_address'] = "unix:///var/run/check_reload_status";
181

    
182
	$try = 0;
183
	while ($try < 3) {
184
		$fd = @fsockopen($g['event_address']);
185
		if ($fd) {
186
			fwrite($fd, $cmd);
187
			$resp = fread($fd, 4096);
188
			if ($resp != "OK\n")
189
				log_error("send_event: sent {$cmd} got {$resp}");
190
			fclose($fd);
191
			$try = 3;
192
		} else if (!is_process_running("check_reload_status"))
193
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
194
		$try++;
195
	}
196
}
197

    
198
function send_multiple_events($cmds) {
199
	global $g;
200

    
201
	if(!isset($g['event_address']))
202
		$g['event_address'] = "unix:///var/run/check_reload_status";
203

    
204
	if (!is_array($cmds))
205
		return;
206

    
207
	while ($try < 3) {
208
		$fd = @fsockopen($g['event_address']);
209
		if ($fd) {
210
			foreach ($cmds as $cmd) {
211
				fwrite($fd, $cmd);
212
				$resp = fread($fd, 4096);
213
				if ($resp != "OK\n")
214
					log_error("send_event: sent {$cmd} got {$resp}");
215
			}
216
			fclose($fd);
217
			$try = 3;
218
		} else if (!is_process_running("check_reload_status"))
219
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
220
		$try++;
221
	}
222
}
223

    
224
function refcount_init($reference) {
225
	$shmid = @shmop_open($reference, "c", 0644, 10);
226
	@shmop_write($shmid, str_pad("0", 10, "\x0", STR_PAD_RIGHT), 0);
227
	@shmop_close($shmid);
228
}
229

    
230
function refcount_reference($reference) {
231
	/* Take out a lock across the shared memory read, increment, write sequence to make it atomic. */
232
	$shm_lck = lock("shm{$reference}", LOCK_EX);
233
	try {
234
		/* NOTE: A warning is generated when shared memory does not exist */
235
		$shmid = @shmop_open($reference, "w", 0, 0);
236
		if (!$shmid) {
237
			refcount_init($reference);
238
			$shmid = @shmop_open($reference, "w", 0, 0);
239
			if (!$shmid) {
240
				log_error(gettext("Could not open shared memory {$reference}"));
241
				unlock($shm_lck);
242
				return;
243
			}
244
		}
245
		$shm_data = @shmop_read($shmid, 0, 10);
246
		$shm_data = intval($shm_data) + 1;
247
		@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
248
		@shmop_close($shmid);
249
		unlock($shm_lck);
250
	} catch (Exception $e) {
251
		log_error($e->getMessage());
252
		unlock($shm_lck);
253
	}
254

    
255
	return $shm_data;
256
}
257

    
258
function refcount_unreference($reference) {
259
	/* Take out a lock across the shared memory read, decrement, write sequence to make it atomic. */
260
	$shm_lck = lock("shm{$reference}", LOCK_EX);
261
	try {
262
		$shmid = @shmop_open($reference, "w", 0, 0);
263
		if (!$shmid) {
264
			refcount_init($reference);
265
			log_error(gettext("Could not open shared memory {$reference}"));
266
			unlock($shm_lck);
267
			return;
268
		}
269
		$shm_data = @shmop_read($shmid, 0, 10);
270
		$shm_data = intval($shm_data) - 1;
271
		if ($shm_data < 0) {
272
			//debug_backtrace();
273
			log_error(sprintf(gettext("Reference %s is going negative, not doing unreference."), $reference));
274
		} else
275
			@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
276
		@shmop_close($shmid);
277
		unlock($shm_lck);
278
	} catch (Exception $e) {
279
		log_error($e->getMessage());
280
		unlock($shm_lck);
281
	}
282

    
283
	return $shm_data;
284
}
285

    
286
function refcount_read($reference) {
287
	/* This function just reads the current value of the refcount for information. */
288
	/* There is no need for locking. */
289
	$shmid = @shmop_open($reference, "a", 0, 0);
290
	if (!$shmid) {
291
		log_error(gettext("Could not open shared memory for read {$reference}"));
292
		return -1;
293
	}
294
	$shm_data = @shmop_read($shmid, 0, 10);
295
	@shmop_close($shmid);
296
	return $shm_data;
297
}
298

    
299
function is_module_loaded($module_name) {
300
	$module_name = str_replace(".ko", "", $module_name);
301
	$running = 0;
302
	$_gb = exec("/sbin/kldstat -qn {$module_name} 2>&1", $_gb, $running);
303
	if (intval($running) == 0)
304
		return true;
305
	else
306
		return false;
307
}
308

    
309
/* validate non-negative numeric string, or equivalent numeric variable */
310
function is_numericint($arg) {
311
	return (((is_int($arg) && $arg >= 0) || (is_string($arg) && strlen($arg) > 0 && ctype_digit($arg))) ? true : false);  
312
}
313

    
314
/* Generate the (human readable) ipv4 or ipv6 subnet address (i.e., netmask, or subnet start IP) 
315
   given an (human readable) ipv4 or ipv6 host address and subnet bit count */
316
function gen_subnet($ipaddr, $bits) {
317
	if (($sn = gen_subnetv6($ipaddr, $bits)) == '')
318
		$sn = gen_subnetv4($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
319
	return $sn;
320
}
321

    
322
/* same as gen_subnet() but accepts IPv4 only */
323
function gen_subnetv4($ipaddr, $bits) {
324
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
325
		if ($bits == 0)
326
			return '0.0.0.0';  // avoids <<32
327
		return long2ip(ip2long($ipaddr) & ((0xFFFFFFFF << (32 - $bits)) & 0xFFFFFFFF));
328
	}
329
	return "";
330
}
331

    
332
/* same as gen_subnet() but accepts IPv6 only */
333
function gen_subnetv6($ipaddr, $bits) {
334
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128)
335
		return Net_IPv6::compress(Net_IPv6::getNetmask($ipaddr, $bits));
336
	return "";
337
}
338

    
339
/* Generate the (human readable) ipv4 or ipv6 subnet end address (i.e., highest address, end IP, or IPv4 broadcast address)
340
   given an (human readable) ipv4 or ipv6 host address and subnet bit count. */
341
function gen_subnet_max($ipaddr, $bits) {
342
	if (($sn = gen_subnetv6_max($ipaddr, $bits)) == '')
343
		$sn = gen_subnetv4_max($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
344
	return $sn;
345
}
346

    
347
/* same as gen_subnet_max() but validates IPv4 only */
348
function gen_subnetv4_max($ipaddr, $bits) {
349
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
350
		if ($bits == 32)
351
			return $ipaddr;
352
		return long2ip32(ip2long($ipaddr) | ~gen_subnet_mask_long($bits));
353
	}
354
	return "";
355
}
356

    
357
/* same as gen_subnet_max() but validates IPv6 only */
358
function gen_subnetv6_max($ipaddr, $bits) {
359
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
360
		$endip_bin = substr(Net_IPv6::_ip2Bin($ipaddr), 0, $bits) . str_repeat('1', 128 - $bits);
361
		return Net_IPv6::compress(Net_IPv6::_bin2Ip($endip_bin));
362
	}
363
	return "";
364
}
365

    
366
/* returns a subnet mask (long given a bit count) */
367
function gen_subnet_mask_long($bits) {
368
	$sm = 0;
369
	for ($i = 0; $i < $bits; $i++) {
370
		$sm >>= 1;
371
		$sm |= 0x80000000;
372
	}
373
	return $sm;
374
}
375

    
376
/* same as above but returns a string */
377
function gen_subnet_mask($bits) {
378
	return long2ip(gen_subnet_mask_long($bits));
379
}
380

    
381
/* Convert long int to IP address, truncating to 32-bits. */
382
function long2ip32($ip) {
383
	return long2ip($ip & 0xFFFFFFFF);
384
}
385

    
386
/* Convert IP address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms. */
387
function ip2long32($ip) {
388
	return ( ip2long($ip) & 0xFFFFFFFF );
389
}
390

    
391
/* Convert IP address to unsigned long int. */
392
function ip2ulong($ip) {
393
	return sprintf("%u", ip2long32($ip));
394
}
395

    
396
/* Find out how many IPs are contained within a given IP range
397
 *  e.g. 192.168.0.0 to 192.168.0.255 returns 256
398
 */
399
function ip_range_size_v4($startip, $endip) {
400
	if (is_ipaddrv4($startip) && is_ipaddrv4($endip)) {
401
		// Operate as unsigned long because otherwise it wouldn't work
402
		//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
403
		return abs(ip2ulong($startip) - ip2ulong($endip)) + 1;
404
	}
405
	return -1;
406
}
407

    
408
/* Find the smallest possible subnet mask which can contain a given number of IPs
409
 *  e.g. 512 IPs can fit in a /23, but 513 IPs need a /22
410
 */
411
function find_smallest_cidr_v4($number) {
412
	$smallest = 1;
413
	for ($b=32; $b > 0; $b--) {
414
		$smallest = ($number <= pow(2,$b)) ? $b : $smallest;
415
	}
416
	return (32-$smallest);
417
}
418

    
419
/* Return the previous IP address before the given address */
420
function ip_before($ip) {
421
	return long2ip32(ip2long($ip)-1);
422
}
423

    
424
/* Return the next IP address after the given address */
425
function ip_after($ip) {
426
	return long2ip32(ip2long($ip)+1);
427
}
428

    
429
/* Return true if the first IP is 'before' the second */
430
function ip_less_than($ip1, $ip2) {
431
	// Compare as unsigned long because otherwise it wouldn't work when
432
	//   crossing over from 127.255.255.255 / 128.0.0.0 barrier
433
	return ip2ulong($ip1) < ip2ulong($ip2);
434
}
435

    
436
/* Return true if the first IP is 'after' the second */
437
function ip_greater_than($ip1, $ip2) {
438
	// Compare as unsigned long because otherwise it wouldn't work
439
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
440
	return ip2ulong($ip1) > ip2ulong($ip2);
441
}
442

    
443
/* compare two IP addresses */
444
function ipcmp($a, $b) {
445
	if (ip_less_than($a, $b))
446
		return -1;
447
	else if (ip_greater_than($a, $b))
448
		return 1;
449
	else
450
		return 0;
451
}
452

    
453
/* Convert a range of IPv4 addresses to an array of individual addresses. */
454
/* Note: IPv6 ranges are not yet supported here. */
455
function ip_range_to_address_array($startip, $endip, $max_size = 5000) {
456
	if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) {
457
		return false;
458
	}
459

    
460
	if (ip_greater_than($startip, $endip)) {
461
		// Swap start and end so we can process sensibly.
462
		$temp = $startip;
463
		$startip = $endip;
464
		$endip = $temp;
465
	}
466

    
467
	if (ip_range_size_v4($startip, $endip) > $max_size)
468
		return false;
469
	
470
	// Container for IP addresses within this range.
471
	$rangeaddresses = array();
472
	$end_int = ip2ulong($endip);
473
	for ($ip_int = ip2ulong($startip); $ip_int <= $end_int; $ip_int++) {
474
		$rangeaddresses[] = long2ip($ip_int);
475
	}
476

    
477
	return $rangeaddresses;
478
}
479

    
480
/* Convert a range of IPv4 addresses to an array of subnets which can contain the range. */
481
/* Note: IPv6 ranges are not yet supported here. */
482
function ip_range_to_subnet_array($startip, $endip) {
483
	if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) {
484
		return array();
485
	}
486

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

    
494
	// Container for subnets within this range.
495
	$rangesubnets = array();
496

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

    
500
	// Loop here to reduce subnet size and retest as needed. We need to make sure
501
	//   that the target subnet is wholly contained between $startip and $endip.
502
	for ($cidr; $cidr <= 32; $cidr++) {
503
		// Find the network and broadcast addresses for the subnet being tested.
504
		$targetsub_min = gen_subnet($startip, $cidr);
505
		$targetsub_max = gen_subnet_max($startip, $cidr);
506

    
507
		// Check best case where the range is exactly one subnet.
508
		if (($targetsub_min == $startip) && ($targetsub_max == $endip)) {
509
			// Hooray, the range is exactly this subnet!
510
			return array("{$startip}/{$cidr}");
511
		}
512

    
513
		// These remaining scenarios will find a subnet that uses the largest
514
		//  chunk possible of the range being tested, and leave the rest to be
515
		//  tested recursively after the loop.
516

    
517
		// Check if the subnet begins with $startip and ends before $endip
518
		if (($targetsub_min == $startip) && ip_less_than($targetsub_max, $endip)) {
519
			break;
520
		}
521

    
522
		// Check if the subnet ends at $endip and starts after $startip
523
		if (ip_greater_than($targetsub_min, $startip) && ($targetsub_max == $endip)) {
524
			break;
525
		}
526

    
527
		// Check if the subnet is between $startip and $endip
528
		if (ip_greater_than($targetsub_min, $startip) && ip_less_than($targetsub_max, $endip)) {
529
			break;
530
		}
531
	}
532

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

    
539
	// Add in the subnet we found before, to preserve ordering
540
	$rangesubnets[] = "{$targetsub_min}/{$cidr}";
541

    
542
	// And some more logic that will search after the subnet we found to fill in to the end of the range.
543
	if ($endip != $targetsub_max) {
544
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array(ip_after($targetsub_max), $endip));
545
	}
546
	return $rangesubnets;
547
}
548

    
549
/* returns true if $range is a valid pair of IPv4 or IPv6 addresses separated by a "-"
550
   	false - if not a valid pair
551
   	true (numeric 4 or 6) - if valid, gives type of addresses */
552
function is_iprange($range) {
553
	if (substr_count($range, '-') != 1) {
554
		return false;
555
	}
556
	list($ip1, $ip2) = explode ('-', $range);
557
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2))
558
		return 4;
559
	if (is_ipaddrv6($ip1) && is_ipaddrv6($ip2))
560
		return 6;
561
	return false;
562
}
563

    
564
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6 */
565
function is_ipaddr($ipaddr) {
566
	if(is_ipaddrv4($ipaddr)) {
567
		return true;
568
	}
569
	if(is_ipaddrv6($ipaddr)) {
570
		return true;
571
	}
572
	return false;
573
}
574

    
575
/* returns true if $ipaddr is a valid IPv6 address */
576
function is_ipaddrv6($ipaddr) {
577
	if (!is_string($ipaddr) || empty($ipaddr))
578
		return false;
579
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
580
		$tmpip = explode("%", $ipaddr);
581
		$ipaddr = $tmpip[0];
582
	}
583
	return Net_IPv6::checkIPv6($ipaddr);
584
}
585

    
586
/* returns true if $ipaddr is a valid dotted IPv4 address */
587
function is_ipaddrv4($ipaddr) {
588
	if (!is_string($ipaddr) || empty($ipaddr))
589
		return false;
590

    
591
	$ip_long = ip2long($ipaddr);
592
	$ip_reverse = long2ip32($ip_long);
593

    
594
	if ($ipaddr == $ip_reverse)
595
		return true;
596
	else
597
		return false;
598
}
599

    
600
/* returns true if $ipaddr is a valid linklocal address */
601
function is_linklocal($ipaddr) {
602
	return (strtolower(substr($ipaddr, 0, 5)) == "fe80:");
603
}
604

    
605
/* returns scope of a linklocal address */
606
function get_ll_scope($addr) {
607
	if (!is_linklocal($addr) || !strstr($addr, "%"))
608
		return "";
609
	list ($ll, $scope) = explode("%", $addr);
610
	return $scope;
611
}
612

    
613
/* returns true if $ipaddr is a valid literal IPv6 address */
614
function is_literalipaddrv6($ipaddr) {
615
	if(preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match))
616
		$ipaddr = $match[1];
617
	else
618
		return false;
619

    
620
	return is_ipaddrv6($ipaddr);
621
}
622

    
623
function is_ipaddrwithport($ipport) {
624
	$parts = explode(":", $ipport);
625
	$port = array_pop($parts);
626
	if (count($parts) == 1) {
627
		return is_ipaddrv4($parts[0]) && is_port($port);
628
	} elseif (count($parts) > 1) {
629
		return is_literalipaddrv6(implode(":", $parts)) && is_port($port);
630
	} else {
631
		return false;
632
	}
633
}
634

    
635
function is_hostnamewithport($hostport) {
636
	$parts = explode(":", $hostport);
637
	$port = array_pop($parts);
638
	if (count($parts) == 1) {
639
		return is_hostname($parts[0]) && is_port($port);
640
	} else {
641
		return false;
642
	}
643
}
644

    
645
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
646
function is_ipaddroralias($ipaddr) {
647
	global $config;
648

    
649
	if (is_alias($ipaddr)) {
650
		if (is_array($config['aliases']['alias'])) {
651
			foreach ($config['aliases']['alias'] as $alias) {
652
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type']))
653
					return true;
654
			}
655
		}
656
		return false;
657
	} else
658
		return is_ipaddr($ipaddr);
659

    
660
}
661

    
662
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format
663
   	false - if not a valid subnet
664
   	true (numeric 4 or 6) - if valid, gives type of subnet */
665
function is_subnet($subnet) {
666
	if (is_string($subnet) && preg_match('/^(?:([0-9.]{7,15})|([0-9a-f:]{2,39}))\/(\d{1,3})$/i', $subnet, $parts)) {
667
		if (is_ipaddrv4($parts[1]) && $parts[3] <= 32)
668
			return 4;
669
		if (is_ipaddrv6($parts[2]) && $parts[3] <= 128)
670
			return 6;
671
	}
672
	return false;
673
}
674

    
675
/* same as is_subnet() but accepts IPv4 only */
676
function is_subnetv4($subnet) {
677
	return (is_subnet($subnet) == 4);
678
}
679

    
680
/* same as is_subnet() but accepts IPv6 only */
681
function is_subnetv6($subnet) {
682
	return (is_subnet($subnet) == 6);
683
}
684

    
685
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
686
function is_subnetoralias($subnet) {
687
	global $aliastable;
688

    
689
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet]))
690
		return true;
691
	else
692
		return is_subnet($subnet);
693
}
694

    
695
function subnet_size($subnet) {
696
	if (is_subnetv4($subnet)) {
697
		list ($ip, $bits) = explode("/", $subnet);
698
		return round(exp(log(2) * (32 - $bits)));
699
	}
700
	else if (is_subnetv6($subnet)) {
701
		list ($ip, $bits) = explode("/", $subnet);
702
		return round(exp(log(2) * (128 - $bits)));
703
	}
704
	else {
705
		return 0;
706
	}
707
}
708

    
709

    
710
function subnet_expand($subnet) {
711
	if (is_subnetv4($subnet)) {
712
		return subnetv4_expand($subnet);
713
	} else if (is_subnetv6($subnet)) {
714
		return subnetv6_expand($subnet);
715
	} else {
716
		return $subnet;
717
	}
718
}
719

    
720
function subnetv4_expand($subnet) {
721
	$result = array();
722
	list ($ip, $bits) = explode("/", $subnet);
723
	$net  = ip2long($ip);
724
	$mask = (0xffffffff << (32 - $bits));
725
	$net &= $mask;
726
	$size = round(exp(log(2) * (32 - $bits)));
727
	for ($i = 0; $i < $size; $i += 1) {
728
		$result[] = long2ip($net | $i);
729
	}
730
	return $result;
731
}
732

    
733
/* find out whether two subnets overlap */
734
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
735

    
736
	if (!is_numeric($bits1))
737
		$bits1 = 32;
738
	if (!is_numeric($bits2))
739
		$bits2 = 32;
740

    
741
	if ($bits1 < $bits2)
742
		$relbits = $bits1;
743
	else
744
		$relbits = $bits2;
745

    
746
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
747
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
748

    
749
	return ($sn1 == $sn2);
750
}
751

    
752
/* find out whether two IPv6 subnets overlap */
753
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
754
	$sub1_min = gen_subnetv6($subnet1, $bits1);
755
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
756
	$sub2_min = gen_subnetv6($subnet2, $bits2);
757
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
758

    
759
	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));
760
}
761

    
762
/* return true if $addr is in $subnet, false if not */
763
function ip_in_subnet($addr,$subnet) {
764
	if(is_ipaddrv6($addr) && is_subnetv6($subnet)) {
765
		return (Net_IPv6::isInNetmask($addr, $subnet));
766
	} else if (is_ipaddrv4($addr) && is_subnetv4($subnet)) {
767
		list($ip, $mask) = explode('/', $subnet);
768
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
769
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
770
	}
771
	return false;
772
}
773

    
774
/* returns true if $hostname is just a valid hostname (top part without any of the domain part) */
775
function is_unqualified_hostname($hostname) {
776
	if (!is_string($hostname))
777
		return false;
778

    
779
	if (preg_match('/^(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname))
780
		return true;
781
	else
782
		return false;
783
}
784

    
785
/* returns true if $hostname is a valid hostname, with or without being a fully-qualified domain name. */
786
function is_hostname($hostname) {
787
	if (!is_string($hostname))
788
		return false;
789

    
790
	if (is_domain($hostname))
791
		if ((substr_count($hostname, ".") == 1) && ($hostname[strlen($hostname)-1] == ".")) {
792
			/* Only a single dot at the end like "test." - hosts cannot be directly in the root domain. */
793
			return false;
794
		} else {
795
			return true;
796
		}
797
	else
798
		return false;
799
}
800

    
801
/* returns true if $domain is a valid domain name */
802
function is_domain($domain) {
803
	if (!is_string($domain))
804
		return false;
805

    
806
	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))
807
		return true;
808
	else
809
		return false;
810
}
811

    
812
/* returns true if $macaddr is a valid MAC address */
813
function is_macaddr($macaddr, $partial=false) {
814
	$repeat = ($partial) ? '1,5' : '5';
815
	return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false;
816
}
817

    
818
/* returns true if $name is a valid name for an alias
819
   returns NULL if a reserved word is used
820
   returns FALSE for bad chars in the name - this allows calling code to determine what the problem was.
821
   aliases cannot be:
822
   	bad chars: anything except a-z 0-9 and underscore
823
   	bad names: empty string, pure numeric, pure underscore
824
   	reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and  "pass" */
825

    
826
function is_validaliasname($name) {
827
	/* Array of reserved words */
828
	$reserved = array("port", "pass");
829

    
830
	if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name))
831
		return false;
832
	if (in_array($name, $reserved, true) || getservbyname($name, "tcp") || getservbyname($name, "udp") || getprotobyname($name))
833
		return; /* return NULL */
834
	return true;
835
}
836

    
837
/* returns true if $port is a valid TCP/UDP port */
838
function is_port($port) {
839
	if (ctype_digit($port) && ((intval($port) >= 1) && (intval($port) <= 65535)))
840
		return true;
841
	if (getservbyname($port, "tcp") || getservbyname($port, "udp"))
842
		return true;
843
	return false;
844
}
845

    
846
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
847
function is_portrange($portrange) {
848
	$ports = explode(":", $portrange);
849

    
850
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
851
}
852

    
853
/* returns true if $port is a valid port number or an alias thereof */
854
function is_portoralias($port) {
855
	global $config;
856

    
857
	if (is_alias($port)) {
858
		if (is_array($config['aliases']['alias'])) {
859
			foreach ($config['aliases']['alias'] as $alias) {
860
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type']))
861
					return true;
862
				}
863
			}
864
			return false;
865
	} else
866
		return is_port($port);
867
}
868

    
869
/* create ranges of sequential port numbers (200:215) and remove duplicates */
870
function group_ports($ports) {
871
	if (!is_array($ports) || empty($ports))
872
		return;
873

    
874
	$uniq = array();
875
	foreach ($ports as $port) {
876
		if (is_portrange($port)) {
877
			list($begin, $end) = explode(":", $port);
878
			if ($begin > $end) {
879
				$aux = $begin;
880
				$begin = $end;
881
				$end = $aux;
882
			}
883
			for ($i = $begin; $i <= $end; $i++)
884
				if (!in_array($i, $uniq))
885
					$uniq[] = $i;
886
		} else if (is_port($port)) {
887
			if (!in_array($port, $uniq))
888
				$uniq[] = $port;
889
		}
890
	}
891
	sort($uniq, SORT_NUMERIC);
892

    
893
	$result = array();
894
	foreach ($uniq as $idx => $port) {
895
		if ($idx == 0) {
896
			$result[] = $port;
897
			continue;
898
		}
899

    
900
		$last = end($result);
901
		if (is_portrange($last))
902
			list($begin, $end) = explode(":", $last);
903
		else
904
			$begin = $end = $last;
905

    
906
		if ($port == ($end+1)) {
907
			$end++;
908
			$result[count($result)-1] = "{$begin}:{$end}";
909
		} else {
910
			$result[] = $port;
911
		}
912
	}
913

    
914
	return $result;
915
}
916

    
917
/* returns true if $val is a valid shaper bandwidth value */
918
function is_valid_shaperbw($val) {
919
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
920
}
921

    
922
/* returns true if $test is in the range between $start and $end */
923
function is_inrange_v4($test, $start, $end) {
924
	if ( (ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start)) )
925
		return true;
926
	else
927
		return false;
928
}
929

    
930
/* returns true if $test is in the range between $start and $end */
931
function is_inrange_v6($test, $start, $end) {
932
	if ( (inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start)) )
933
		return true;
934
	else
935
		return false;
936
}
937

    
938
/* returns true if $test is in the range between $start and $end */
939
function is_inrange($test, $start, $end) {
940
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
941
}
942

    
943
/* XXX: return the configured carp interface list */
944
function get_configured_carp_interface_list($carpinterface = '', $family = 'inet', $what = 'ip') {
945
	global $config;
946

    
947
	$iflist = array();
948

    
949
	if(is_array($config['virtualip']['vip'])) {
950
		$viparr = &$config['virtualip']['vip'];
951
		foreach ($viparr as $vip) {
952
			switch ($vip['mode']) {
953
			case "carp":
954
				if (!empty($carpinterface)) {
955
					if ($carpinterface == "{$vip['interface']}_vip{$vip['vhid']}") {
956
						switch ($what) {
957
						case 'subnet':
958
							if ($family == 'inet' && is_ipaddrv4($vip['subnet']))
959
								return "{$vip['subnet']}/{$vip['subnet_bits']}";
960
							else if ($family == 'inet6' && is_ipaddrv6($vip['subnet']))
961
								return "{$vip['subnet']}/{$vip['subnet_bits']}";
962
							break;
963
						case 'iface':
964
							if ($family == 'inet' && is_ipaddrv4($vip['subnet']))
965
								return $vip['interface'];
966
							else if ($family == 'inet6' && is_ipaddrv6($vip['subnet']))
967
								return $vip['interface'];
968
							break;
969
						case 'vip':
970
							if ($family == 'inet' && is_ipaddrv4($vip['subnet']))
971
								return $vip;
972
							else if ($family == 'inet6' && is_ipaddrv6($vip['subnet']))
973
								return $vip;
974
							break;
975
						case 'ip':
976
						default:
977
							if ($family == 'inet' && is_ipaddrv4($vip['subnet']))
978
								return $vip['subnet'];
979
							else if ($family == 'inet6' && is_ipaddrv6($vip['subnet']))
980
								return $vip['subnet'];
981
							break;
982
						}
983
					}
984
				} else {
985
					$iflist["{$vip['interface']}_vip{$vip['vhid']}"] = $vip['subnet'];
986
				}
987
				break;
988
			}
989
		}
990
	}
991

    
992
	return $iflist;
993
}
994

    
995
/* return the configured IP aliases list */
996
function get_configured_ip_aliases_list($returnfullentry = false) {
997
	global $config;
998

    
999
	$alias_list=array();
1000

    
1001
	if(is_array($config['virtualip']['vip'])) {
1002
		$viparr = &$config['virtualip']['vip'];
1003
		foreach ($viparr as $vip) {
1004
			if ($vip['mode']=="ipalias") {
1005
				if ($returnfullentry)
1006
					$alias_list[$vip['subnet']] = $vip;
1007
				else
1008
					$alias_list[$vip['subnet']] = $vip['interface'];
1009
			}
1010
		}
1011
	}
1012

    
1013
	return $alias_list;
1014
}
1015

    
1016
/* return all configured aliases list (IP, carp, proxyarp and other) */
1017
function get_configured_vips_list() {
1018
	global $config;
1019

    
1020
	$alias_list=array();
1021

    
1022
	if(is_array($config['virtualip']['vip'])) {
1023
		$viparr = &$config['virtualip']['vip'];
1024
		foreach ($viparr as $vip) {
1025
			if ($vip['mode'] == "carp")
1026
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => "{$vip['interface']}_vip{$vip['vhid']}");
1027
			else
1028
				$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
1029
		}
1030
	}
1031

    
1032
	return $alias_list;
1033
}
1034

    
1035
/* comparison function for sorting by the order in which interfaces are normally created */
1036
function compare_interface_friendly_names($a, $b) {
1037
	if ($a == $b)
1038
		return 0;
1039
	else if ($a == 'wan')
1040
		return -1;
1041
	else if ($b == 'wan')
1042
		return 1;
1043
	else if ($a == 'lan')
1044
		return -1;
1045
	else if ($b == 'lan')
1046
		return 1;
1047

    
1048
	return strnatcmp($a, $b);
1049
}
1050

    
1051
/* return the configured interfaces list. */
1052
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
1053
	global $config;
1054

    
1055
	$iflist = array();
1056

    
1057
	/* if list */
1058
	foreach($config['interfaces'] as $if => $ifdetail) {
1059
		if ($only_opt && ($if == "wan" || $if == "lan"))
1060
			continue;
1061
		if (isset($ifdetail['enable']) || $withdisabled == true)
1062
			$iflist[$if] = $if;
1063
	}
1064

    
1065
	return $iflist;
1066
}
1067

    
1068
/* return the configured interfaces list. */
1069
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
1070
	global $config;
1071

    
1072
	$iflist = array();
1073

    
1074
	/* if list */
1075
	foreach($config['interfaces'] as $if => $ifdetail) {
1076
		if ($only_opt && ($if == "wan" || $if == "lan"))
1077
			continue;
1078
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1079
			$tmpif = get_real_interface($if);
1080
			if (!empty($tmpif))
1081
				$iflist[$tmpif] = $if;
1082
		}
1083
	}
1084

    
1085
	return $iflist;
1086
}
1087

    
1088
/* return the configured interfaces list with their description. */
1089
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
1090
	global $config;
1091

    
1092
	$iflist = array();
1093

    
1094
	/* if list */
1095
	foreach($config['interfaces'] as $if => $ifdetail) {
1096
		if ($only_opt && ($if == "wan" || $if == "lan"))
1097
			continue;
1098
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1099
			if(empty($ifdetail['descr']))
1100
				$iflist[$if] = strtoupper($if);
1101
			else
1102
				$iflist[$if] = strtoupper($ifdetail['descr']);
1103
		}
1104
	}
1105

    
1106
	return $iflist;
1107
}
1108

    
1109
/*
1110
 *   get_configured_ip_addresses() - Return a list of all configured
1111
 *   interfaces IP Addresses
1112
 *
1113
 */
1114
function get_configured_ip_addresses() {
1115
	global $config;
1116

    
1117
	if (!function_exists('get_interface_ip'))
1118
		require_once("interfaces.inc");
1119
	$ip_array = array();
1120
	$interfaces = get_configured_interface_list();
1121
	if (is_array($interfaces)) {
1122
		foreach($interfaces as $int) {
1123
			$ipaddr = get_interface_ip($int);
1124
			$ip_array[$int] = $ipaddr;
1125
		}
1126
	}
1127
	$interfaces = get_configured_carp_interface_list();
1128
	if (is_array($interfaces))
1129
		foreach($interfaces as $int => $ipaddr)
1130
			$ip_array[$int] = $ipaddr;
1131

    
1132
	/* pppoe server */
1133
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
1134
		foreach($config['pppoes']['pppoe'] as $pppoe) {
1135
			if ($pppoe['mode'] == "server") {
1136
				if(is_ipaddr($pppoe['localip'])) {
1137
					$int = "pppoes". $pppoe['pppoeid'];
1138
					$ip_array[$int] = $pppoe['localip'];
1139
				}
1140
			}
1141
		}
1142
	}
1143

    
1144
	return $ip_array;
1145
}
1146

    
1147
/*
1148
 *   get_configured_ipv6_addresses() - Return a list of all configured
1149
 *   interfaces IPv6 Addresses
1150
 *
1151
 */
1152
function get_configured_ipv6_addresses() {
1153
	require_once("interfaces.inc");
1154
	$ipv6_array = array();
1155
	$interfaces = get_configured_interface_list();
1156
	if(is_array($interfaces)) {
1157
		foreach($interfaces as $int) {
1158
			$ipaddrv6 = get_interface_ipv6($int);
1159
			$ipv6_array[$int] = $ipaddrv6;
1160
		}
1161
	}
1162
	$interfaces = get_configured_carp_interface_list();
1163
	if(is_array($interfaces))
1164
		foreach($interfaces as $int => $ipaddrv6)
1165
			$ipv6_array[$int] = $ipaddrv6;
1166
	return $ipv6_array;
1167
}
1168

    
1169
/*
1170
 *   get_interface_list() - Return a list of all physical interfaces
1171
 *   along with MAC and status.
1172
 *
1173
 *   $mode = "active" - use ifconfig -lu
1174
 *           "media"  - use ifconfig to check physical connection
1175
 *			status (much slower)
1176
 */
1177
function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") {
1178
	global $config;
1179
	$upints = array();
1180
	/* get a list of virtual interface types */
1181
	if(!$vfaces) {
1182
		$vfaces = array (
1183
				'bridge',
1184
				'ppp',
1185
				'pppoe',
1186
				'pptp',
1187
				'l2tp',
1188
				'sl',
1189
				'gif',
1190
				'gre',
1191
				'faith',
1192
				'lo',
1193
				'ng',
1194
				'_vlan',
1195
				'_wlan',
1196
				'pflog',
1197
				'plip',
1198
				'pfsync',
1199
				'enc',
1200
				'tun',
1201
				'carp',
1202
				'lagg',
1203
				'vip',
1204
				'ipfw'
1205
		);
1206
	}
1207
	switch($mode) {
1208
	case "active":
1209
		$upints = pfSense_interface_listget(IFF_UP);
1210
		break;
1211
	case "media":
1212
		$intlist = pfSense_interface_listget();
1213
		$ifconfig = "";
1214
		exec("/sbin/ifconfig -a", $ifconfig);
1215
		$regexp = '/(' . implode('|', $intlist) . '):\s/';
1216
		$ifstatus = preg_grep('/status:/', $ifconfig);
1217
		foreach($ifstatus as $status) {
1218
			$int = array_shift($intlist);
1219
			if(stristr($status, "active")) $upints[] = $int;
1220
		}
1221
		break;
1222
	default:
1223
		$upints = pfSense_interface_listget();
1224
		break;
1225
	}
1226
	/* build interface list with netstat */
1227
	$linkinfo = "";
1228
	exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo);
1229
	array_shift($linkinfo);
1230
	/* build ip address list with netstat */
1231
	$ipinfo = "";
1232
	exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo);
1233
	array_shift($ipinfo);
1234
	foreach($linkinfo as $link) {
1235
		$friendly = "";
1236
		$alink = explode(" ", $link);
1237
		$ifname = rtrim(trim($alink[0]), '*');
1238
		/* trim out all numbers before checking for vfaces */
1239
		if (!in_array(array_shift(preg_split('/\d/', $ifname)), $vfaces) &&
1240
			!stristr($ifname, "_vlan") && !stristr($ifname, "_wlan")) {
1241
			$toput = array(
1242
					"mac" => trim($alink[1]),
1243
					"up" => in_array($ifname, $upints)
1244
				);
1245
			foreach($ipinfo as $ip) {
1246
				$aip = explode(" ", $ip);
1247
				if($aip[0] == $ifname) {
1248
					$toput['ipaddr'] = $aip[1];
1249
				}
1250
			}
1251
			if (is_array($config['interfaces'])) {
1252
				foreach($config['interfaces'] as $name => $int)
1253
					if($int['if'] == $ifname) $friendly = $name;
1254
			}
1255
			switch($keyby) {
1256
			case "physical":
1257
				if($friendly != "") {
1258
					$toput['friendly'] = $friendly;
1259
				}
1260
				$dmesg_arr = array();
1261
				exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr);
1262
				preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg);
1263
				$toput['dmesg'] = $dmesg[1][0];
1264
				$iflist[$ifname] = $toput;
1265
				break;
1266
			case "ppp":
1267

    
1268
			case "friendly":
1269
				if($friendly != "") {
1270
					$toput['if'] = $ifname;
1271
					$iflist[$friendly] = $toput;
1272
				}
1273
				break;
1274
			}
1275
		}
1276
	}
1277
	return $iflist;
1278
}
1279

    
1280
/****f* util/log_error
1281
* NAME
1282
*   log_error  - Sends a string to syslog.
1283
* INPUTS
1284
*   $error     - string containing the syslog message.
1285
* RESULT
1286
*   null
1287
******/
1288
function log_error($error) {
1289
	global $g;
1290
	$page = $_SERVER['SCRIPT_NAME'];
1291
	if (empty($page)) {
1292
		$files = get_included_files();
1293
		$page = basename($files[0]);
1294
	}
1295
	syslog(LOG_ERR, "$page: $error");
1296
	if ($g['debug'])
1297
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1298
	return;
1299
}
1300

    
1301
/****f* util/log_auth
1302
* NAME
1303
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1304
* INPUTS
1305
*   $error     - string containing the syslog message.
1306
* RESULT
1307
*   null
1308
******/
1309
function log_auth($error) {
1310
	global $g;
1311
	$page = $_SERVER['SCRIPT_NAME'];
1312
	syslog(LOG_AUTH, "$page: $error");
1313
	if ($g['debug'])
1314
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1315
	return;
1316
}
1317

    
1318
/****f* util/exec_command
1319
 * NAME
1320
 *   exec_command - Execute a command and return a string of the result.
1321
 * INPUTS
1322
 *   $command   - String of the command to be executed.
1323
 * RESULT
1324
 *   String containing the command's result.
1325
 * NOTES
1326
 *   This function returns the command's stdout and stderr.
1327
 ******/
1328
function exec_command($command) {
1329
	$output = array();
1330
	exec($command . ' 2>&1', $output);
1331
	return(implode("\n", $output));
1332
}
1333

    
1334
/* wrapper for exec() */
1335
function mwexec($command, $mute = false, $clearsigmask = false) {
1336
	global $g;
1337

    
1338
	if ($g['debug']) {
1339
		if (!$_SERVER['REMOTE_ADDR'])
1340
			echo "mwexec(): $command\n";
1341
	}
1342
	$oarr = array();
1343
	$retval = 0;
1344

    
1345
	if ($clearsigmask) {
1346
		$oldset = array();
1347
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1348
	}
1349
	$garbage = exec("$command 2>&1", $oarr, $retval);
1350
	if ($clearsigmask) {
1351
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1352
	}
1353

    
1354
	if(isset($config['system']['developerspew']))
1355
		$mute = false;
1356
	if(($retval <> 0) && ($mute === false)) {
1357
		$output = implode(" ", $oarr);
1358
		log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, $output));
1359
		unset($output);
1360
	}
1361
	unset($oarr);
1362
	return $retval;
1363
}
1364

    
1365
/* wrapper for exec() in background */
1366
function mwexec_bg($command, $clearsigmask = false) {
1367
	global $g;
1368

    
1369
	if ($g['debug']) {
1370
		if (!$_SERVER['REMOTE_ADDR'])
1371
			echo "mwexec(): $command\n";
1372
	}
1373

    
1374
	if ($clearsigmask) {
1375
		$oldset = array();
1376
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1377
	}
1378
	$_gb = exec("/usr/bin/nohup $command > /dev/null 2>&1 &");
1379
	if ($clearsigmask) {
1380
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1381
	}
1382
	unset($_gb);
1383
}
1384

    
1385
/* unlink a file, if it exists */
1386
function unlink_if_exists($fn) {
1387
	$to_do = glob($fn);
1388
	if(is_array($to_do)) {
1389
		foreach($to_do as $filename)
1390
			@unlink($filename);
1391
	} else {
1392
		@unlink($fn);
1393
	}
1394
}
1395
/* make a global alias table (for faster lookups) */
1396
function alias_make_table($config) {
1397
	global $aliastable;
1398

    
1399
	$aliastable = array();
1400

    
1401
	if (is_array($config['aliases']['alias'])) {
1402
		foreach ($config['aliases']['alias'] as $alias) {
1403
			if ($alias['name'])
1404
				$aliastable[$alias['name']] = $alias['address'];
1405
		}
1406
	}
1407
}
1408

    
1409
/* check if an alias exists */
1410
function is_alias($name) {
1411
	global $aliastable;
1412

    
1413
	return isset($aliastable[$name]);
1414
}
1415

    
1416
function alias_get_type($name) {
1417
	global $config;
1418

    
1419
	if (is_array($config['aliases']['alias'])) {
1420
		foreach ($config['aliases']['alias'] as $alias) {
1421
			if ($name == $alias['name'])
1422
				return $alias['type'];
1423
		}
1424
	}
1425

    
1426
	return "";
1427
}
1428

    
1429
/* expand a host or network alias, if necessary */
1430
function alias_expand($name) {
1431
	global $aliastable;
1432

    
1433
	if (isset($aliastable[$name]))
1434
		return "\${$name}";
1435
	else if (is_ipaddr($name) || is_subnet($name) || is_port($name) || is_portrange($name))
1436
		return "{$name}";
1437
	else
1438
		return null;
1439
}
1440

    
1441
function alias_expand_urltable($name) {
1442
	global $config;
1443
	$urltable_prefix = "/var/db/aliastables/";
1444
	$urltable_filename = $urltable_prefix . $name . ".txt";
1445

    
1446
	if (is_array($config['aliases']['alias'])) {
1447
		foreach ($config['aliases']['alias'] as $alias) {
1448
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1449
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename))
1450
					return $urltable_filename;
1451
				else if (process_alias_urltable($name, $alias["url"], 0, true))
1452
					return $urltable_filename;
1453
			}
1454
		}
1455
	}
1456
	return null;
1457
}
1458

    
1459
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1460
function verify_digital_signature($fname) {
1461
	global $g;
1462

    
1463
	if(!file_exists("/usr/local/sbin/gzsig"))
1464
		return 4;
1465

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

    
1469
/* obtain MAC address given an IP address by looking at the ARP table */
1470
function arp_get_mac_by_ip($ip) {
1471
	mwexec("/sbin/ping -c 1 -t 1 " . escapeshellarg($ip), true);
1472
	$arpoutput = "";
1473
	exec("/usr/sbin/arp -n " . escapeshellarg($ip), $arpoutput);
1474

    
1475
	if ($arpoutput[0]) {
1476
		$arpi = explode(" ", $arpoutput[0]);
1477
		$macaddr = $arpi[3];
1478
		if (is_macaddr($macaddr))
1479
			return $macaddr;
1480
		else
1481
			return false;
1482
	}
1483

    
1484
	return false;
1485
}
1486

    
1487
/* return a fieldname that is safe for xml usage */
1488
function xml_safe_fieldname($fieldname) {
1489
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1490
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1491
			 ':', ',', '.', '\'', '\\'
1492
		);
1493
	return strtolower(str_replace($replace, "", $fieldname));
1494
}
1495

    
1496
function mac_format($clientmac) {
1497
	global $config, $cpzone;
1498

    
1499
	$mac = explode(":", $clientmac);
1500
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1501

    
1502
	switch($mac_format) {
1503
	case 'singledash':
1504
		return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1505

    
1506
	case 'ietf':
1507
		return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1508

    
1509
	case 'cisco':
1510
		return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1511

    
1512
	case 'unformatted':
1513
		return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1514

    
1515
	default:
1516
		return $clientmac;
1517
	}
1518
}
1519

    
1520
function resolve_retry($hostname, $retries = 5) {
1521

    
1522
	if (is_ipaddr($hostname))
1523
		return $hostname;
1524

    
1525
	for ($i = 0; $i < $retries; $i++) {
1526
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1527
		$ip = gethostbyname($hostname);
1528

    
1529
		if ($ip && $ip != $hostname) {
1530
			/* success */
1531
			return $ip;
1532
		}
1533

    
1534
		sleep(1);
1535
	}
1536

    
1537
	return false;
1538
}
1539

    
1540
function format_bytes($bytes) {
1541
	if ($bytes >= 1073741824) {
1542
		return sprintf("%.2f GB", $bytes/1073741824);
1543
	} else if ($bytes >= 1048576) {
1544
		return sprintf("%.2f MB", $bytes/1048576);
1545
	} else if ($bytes >= 1024) {
1546
		return sprintf("%.0f KB", $bytes/1024);
1547
	} else {
1548
		return sprintf("%d bytes", $bytes);
1549
	}
1550
}
1551

    
1552
function update_filter_reload_status($text) {
1553
	global $g;
1554

    
1555
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1556
}
1557

    
1558
/****** util/return_dir_as_array
1559
 * NAME
1560
 *   return_dir_as_array - Return a directory's contents as an array.
1561
 * INPUTS
1562
 *   $dir          - string containing the path to the desired directory.
1563
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1564
 * RESULT
1565
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1566
 ******/
1567
function return_dir_as_array($dir, $filter_regex = '') {
1568
	$dir_array = array();
1569
	if (is_dir($dir)) {
1570
		if ($dh = opendir($dir)) {
1571
			while (($file = readdir($dh)) !== false) {
1572
				if (($file == ".") || ($file == ".."))
1573
					continue;
1574

    
1575
				if (empty($filter_regex) || preg_match($filter_regex, $file))
1576
					array_push($dir_array, $file);
1577
			}
1578
			closedir($dh);
1579
		}
1580
	}
1581
	return $dir_array;
1582
}
1583

    
1584
function run_plugins($directory) {
1585
	global $config, $g;
1586

    
1587
	/* process packager manager custom rules */
1588
	$files = return_dir_as_array($directory);
1589
	if (is_array($files)) {
1590
		foreach ($files as $file) {
1591
			if (stristr($file, ".sh") == true)
1592
				mwexec($directory . $file . " start");
1593
			else if (!is_dir($directory . "/" . $file) && stristr($file,".inc"))
1594
				require_once($directory . "/" . $file);
1595
		}
1596
	}
1597
}
1598

    
1599
/*
1600
 *    safe_mkdir($path, $mode = 0755)
1601
 *    create directory if it doesn't already exist and isn't a file!
1602
 */
1603
function safe_mkdir($path, $mode=0755) {
1604
	global $g;
1605

    
1606
	if (!is_file($path) && !is_dir($path)) {
1607
		return @mkdir($path, $mode, true);
1608
	} else {
1609
		return false;
1610
	}
1611
}
1612

    
1613
/*
1614
 * make_dirs($path, $mode = 0755)
1615
 * create directory tree recursively (mkdir -p)
1616
 */
1617
function make_dirs($path, $mode = 0755) {
1618
	$base = '';
1619
	foreach (explode('/', $path) as $dir) {
1620
		$base .= "/$dir";
1621
		if (!is_dir($base)) {
1622
			if (!@mkdir($base, $mode))
1623
				return false;
1624
		}
1625
	}
1626
	return true;
1627
}
1628

    
1629
/*
1630
 * get_sysctl($names)
1631
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1632
 * name) and return an array of key/value pairs set for those that exist
1633
 */
1634
function get_sysctl($names) {
1635
	if (empty($names))
1636
		return array();
1637

    
1638
	if (is_array($names)) {
1639
		$name_list = array();
1640
		foreach ($names as $name) {
1641
			$name_list[] = escapeshellarg($name);
1642
		}
1643
	} else
1644
		$name_list = array(escapeshellarg($names));
1645

    
1646
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1647
	$values = array();
1648
	foreach ($output as $line) {
1649
		$line = explode(": ", $line, 2);
1650
		if (count($line) == 2)
1651
			$values[$line[0]] = $line[1];
1652
	}
1653

    
1654
	return $values;
1655
}
1656

    
1657
/*
1658
 * get_single_sysctl($name)
1659
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
1660
 * return the value for sysctl $name or empty string if it doesn't exist
1661
 */
1662
function get_single_sysctl($name) {
1663
	if (empty($name))
1664
		return "";
1665

    
1666
	$value = get_sysctl($name);
1667
	if (empty($value) || !isset($value[$name]))
1668
		return "";
1669

    
1670
	return $value[$name];
1671
}
1672

    
1673
/*
1674
 * set_sysctl($value_list)
1675
 * Set sysctl OID's listed as key/value pairs and return
1676
 * an array with keys set for those that succeeded
1677
 */
1678
function set_sysctl($values) {
1679
	if (empty($values))
1680
		return array();
1681

    
1682
	$value_list = array();
1683
	foreach ($values as $key => $value) {
1684
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1685
	}
1686

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

    
1689
	/* Retry individually if failed (one or more read-only) */
1690
	if ($success <> 0 && count($value_list) > 1) {
1691
		foreach ($value_list as $value) {
1692
			exec("/sbin/sysctl -i " . $value, $output);
1693
		}
1694
	}
1695

    
1696
	$ret = array();
1697
	foreach ($output as $line) {
1698
		$line = explode(": ", $line, 2);
1699
		if (count($line) == 2)
1700
			$ret[$line[0]] = true;
1701
	}
1702

    
1703
	return $ret;
1704
}
1705

    
1706
/*
1707
 * set_single_sysctl($name, $value)
1708
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
1709
 * returns boolean meaning if it suceed
1710
 */
1711
function set_single_sysctl($name, $value) {
1712
	if (empty($name))
1713
		return false;
1714

    
1715
	$result = set_sysctl(array($name => $value));
1716

    
1717
	if (!isset($result[$name]) || $result[$name] != $value)
1718
		return false;
1719

    
1720
	return true;
1721
}
1722

    
1723
/*
1724
 *     get_memory()
1725
 *     returns an array listing the amount of
1726
 *     memory installed in the hardware
1727
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
1728
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
1729
 */
1730
function get_memory() {
1731
	$physmem = get_single_sysctl("hw.physmem");
1732
	$realmem = get_single_sysctl("hw.realmem");
1733
	/* convert from bytes to megabytes */
1734
	return array(($physmem/1048576),($realmem/1048576));
1735
}
1736

    
1737
function mute_kernel_msgs() {
1738
	global $config;
1739
	// Do not mute serial console.  The kernel gets very very cranky
1740
	// and will start dishing you cannot control tty errors.
1741
	switch (trim(file_get_contents("/etc/platform"))) {
1742
		case "nanobsd":
1743
		case "jail":
1744
			return;
1745
	}
1746
	if($config['system']['enableserial'])
1747
		return;
1748
	exec("/sbin/conscontrol mute on");
1749
}
1750

    
1751
function unmute_kernel_msgs() {
1752
	global $config;
1753
	// Do not mute serial console.  The kernel gets very very cranky
1754
	// and will start dishing you cannot control tty errors.
1755
	switch (trim(file_get_contents("/etc/platform"))) {
1756
		case "nanobsd":
1757
		case "jail":
1758
			return;
1759
	}
1760
	exec("/sbin/conscontrol mute off");
1761
}
1762

    
1763
function start_devd() {
1764
	global $g;
1765

    
1766
	if ($g['platform'] == 'jail')
1767
		return;
1768
	/* Use the undocumented -q options of devd to quite its log spamming */
1769
	$_gb = exec("/sbin/devd -q");
1770
	sleep(1);
1771
	unset($_gb);
1772
}
1773

    
1774
function is_interface_vlan_mismatch() {
1775
	global $config, $g;
1776

    
1777
	if (is_array($config['vlans']['vlan'])) {
1778
		foreach ($config['vlans']['vlan'] as $vlan) {
1779
			if (does_interface_exist($vlan['if']) == false)
1780
				return true;
1781
		}
1782
	}
1783

    
1784
	return false;
1785
}
1786

    
1787
function is_interface_mismatch() {
1788
	global $config, $g;
1789

    
1790
	$do_assign = false;
1791
	$i = 0;
1792
	$missing_interfaces = array();
1793
	if (is_array($config['interfaces'])) {
1794
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1795
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1796
				// Do not check these interfaces.
1797
				$i++;
1798
				continue;
1799
			}
1800
			else if (does_interface_exist($ifcfg['if']) == false) {
1801
				$missing_interfaces[] = $ifcfg['if'];
1802
				$do_assign = true;
1803
			} else
1804
				$i++;
1805
		}
1806
	}
1807

    
1808
	if (file_exists("{$g['tmp_path']}/assign_complete"))
1809
		$do_assign = false;
1810

    
1811
	if (!empty($missing_interfaces) && $do_assign)
1812
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1813
	else
1814
		@unlink("{$g['tmp_path']}/missing_interfaces");
1815

    
1816
	return $do_assign;
1817
}
1818

    
1819
/* sync carp entries to other firewalls */
1820
function carp_sync_client() {
1821
	global $g;
1822
	send_event("filter sync");
1823
}
1824

    
1825
/****f* util/isAjax
1826
 * NAME
1827
 *   isAjax - reports if the request is driven from prototype
1828
 * INPUTS
1829
 *   none
1830
 * RESULT
1831
 *   true/false
1832
 ******/
1833
function isAjax() {
1834
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
1835
}
1836

    
1837
/****f* util/timeout
1838
 * NAME
1839
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
1840
 * INPUTS
1841
 *   optional, seconds to wait before timeout. Default 9 seconds.
1842
 * RESULT
1843
 *   returns 1 char of user input or null if no input.
1844
 ******/
1845
function timeout($timer = 9) {
1846
	while(!isset($key)) {
1847
		if ($timer >= 9) { echo chr(8) . chr(8) . ($timer==9 ? chr(32) : null)  . "{$timer}";  }
1848
		else { echo chr(8). "{$timer}"; }
1849
		`/bin/stty -icanon min 0 time 25`;
1850
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
1851
		`/bin/stty icanon`;
1852
		if ($key == '')
1853
			unset($key);
1854
		$timer--;
1855
		if ($timer == 0)
1856
			break;
1857
	}
1858
	return $key;
1859
}
1860

    
1861
/****f* util/msort
1862
 * NAME
1863
 *   msort - sort array
1864
 * INPUTS
1865
 *   $array to be sorted, field to sort by, direction of sort
1866
 * RESULT
1867
 *   returns newly sorted array
1868
 ******/
1869
function msort($array, $id="id", $sort_ascending=true) {
1870
	$temp_array = array();
1871
	while(count($array)>0) {
1872
		$lowest_id = 0;
1873
		$index=0;
1874
		foreach ($array as $item) {
1875
			if (isset($item[$id])) {
1876
				if ($array[$lowest_id][$id]) {
1877
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
1878
						$lowest_id = $index;
1879
					}
1880
				}
1881
			}
1882
			$index++;
1883
		}
1884
		$temp_array[] = $array[$lowest_id];
1885
		$array = array_merge(array_slice($array, 0,$lowest_id), array_slice($array, $lowest_id+1));
1886
	}
1887
	if ($sort_ascending) {
1888
		return $temp_array;
1889
	} else {
1890
		return array_reverse($temp_array);
1891
	}
1892
}
1893

    
1894
/****f* util/is_URL
1895
 * NAME
1896
 *   is_URL
1897
 * INPUTS
1898
 *   string to check
1899
 * RESULT
1900
 *   Returns true if item is a URL
1901
 ******/
1902
function is_URL($url) {
1903
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
1904
	if($match)
1905
		return true;
1906
	return false;
1907
}
1908

    
1909
function is_file_included($file = "") {
1910
	$files = get_included_files();
1911
	if (in_array($file, $files))
1912
		return true;
1913

    
1914
	return false;
1915
}
1916

    
1917
/*
1918
 * Replace a value on a deep associative array using regex
1919
 */
1920
function array_replace_values_recursive($data, $match, $replace) {
1921
	if (empty($data))
1922
		return $data;
1923

    
1924
	if (is_string($data))
1925
		$data = preg_replace("/{$match}/", $replace, $data);
1926
	else if (is_array($data))
1927
		foreach ($data as $k => $v)
1928
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
1929

    
1930
	return $data;
1931
}
1932

    
1933
/*
1934
	This function was borrowed from a comment on PHP.net at the following URL:
1935
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
1936
 */
1937
function array_merge_recursive_unique($array0, $array1) {
1938

    
1939
	$arrays = func_get_args();
1940
	$remains = $arrays;
1941

    
1942
	// We walk through each arrays and put value in the results (without
1943
	// considering previous value).
1944
	$result = array();
1945

    
1946
	// loop available array
1947
	foreach($arrays as $array) {
1948

    
1949
		// The first remaining array is $array. We are processing it. So
1950
		// we remove it from remaing arrays.
1951
		array_shift($remains);
1952

    
1953
		// We don't care non array param, like array_merge since PHP 5.0.
1954
		if(is_array($array)) {
1955
			// Loop values
1956
			foreach($array as $key => $value) {
1957
				if(is_array($value)) {
1958
					// we gather all remaining arrays that have such key available
1959
					$args = array();
1960
					foreach($remains as $remain) {
1961
						if(array_key_exists($key, $remain)) {
1962
							array_push($args, $remain[$key]);
1963
						}
1964
					}
1965

    
1966
					if(count($args) > 2) {
1967
						// put the recursion
1968
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
1969
					} else {
1970
						foreach($value as $vkey => $vval) {
1971
							$result[$key][$vkey] = $vval;
1972
						}
1973
					}
1974
				} else {
1975
					// simply put the value
1976
					$result[$key] = $value;
1977
				}
1978
			}
1979
		}
1980
	}
1981
	return $result;
1982
}
1983

    
1984

    
1985
/*
1986
 * converts a string like "a,b,c,d"
1987
 * into an array like array("a" => "b", "c" => "d")
1988
 */
1989
function explode_assoc($delimiter, $string) {
1990
	$array = explode($delimiter, $string);
1991
	$result = array();
1992
	$numkeys = floor(count($array) / 2);
1993
	for ($i = 0; $i < $numkeys; $i += 1) {
1994
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
1995
	}
1996
	return $result;
1997
}
1998

    
1999
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
2000
	global $config, $aliastable;
2001

    
2002
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
2003
	if (!is_array($config['staticroutes']['route']))
2004
		return array();
2005

    
2006
	$allstaticroutes = array();
2007
	$allsubnets = array();
2008
	/* Loop through routes and expand aliases as we find them. */
2009
	foreach ($config['staticroutes']['route'] as $route) {
2010
		if (is_alias($route['network'])) {
2011
			if (!isset($aliastable[$route['network']]))
2012
				continue;
2013

    
2014
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
2015
			foreach ($subnets as $net) {
2016
				if (!is_subnet($net)) {
2017
					if (is_ipaddrv4($net))
2018
						$net .= "/32";
2019
					else if (is_ipaddrv6($net))
2020
						$net .= "/128";
2021
					else if ($returnhostnames === false || !is_fqdn($net))
2022
						continue;
2023
				}
2024
				$temproute = $route;
2025
				$temproute['network'] = $net;
2026
				$allstaticroutes[] = $temproute;
2027
				$allsubnets[] = $net;
2028
			}
2029
		} elseif (is_subnet($route['network'])) {
2030
			$allstaticroutes[] = $route;
2031
			$allsubnets[] = $route['network'];
2032
		}
2033
	}
2034
	if ($returnsubnetsonly)
2035
		return $allsubnets;
2036
	else
2037
		return $allstaticroutes;
2038
}
2039

    
2040
/****f* util/get_alias_list
2041
 * NAME
2042
 *   get_alias_list - Provide a list of aliases.
2043
 * INPUTS
2044
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
2045
 * RESULT
2046
 *   Array containing list of aliases.
2047
 *   If $type is unspecified, all aliases are returned.
2048
 *   If $type is a string, all aliases of the type specified in $type are returned.
2049
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
2050
 */
2051
function get_alias_list($type = null) {
2052
	global $config;
2053
	$result = array();
2054
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
2055
		foreach ($config['aliases']['alias'] as $alias) {
2056
			if ($type === null) {
2057
				$result[] = $alias['name'];
2058
			}
2059
			else if (is_array($type)) {
2060
				if (in_array($alias['type'], $type)) {
2061
					$result[] = $alias['name'];
2062
				}
2063
			}
2064
			else if ($type === $alias['type']) {
2065
				$result[] = $alias['name'];
2066
			}
2067
		}
2068
	}
2069
	return $result;
2070
}
2071

    
2072
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
2073
function array_exclude($needle, $haystack) {
2074
	$result = array();
2075
	if (is_array($haystack)) {
2076
		foreach ($haystack as $thing) {
2077
			if ($needle !== $thing) {
2078
				$result[] = $thing;
2079
			}
2080
		}
2081
	}
2082
	return $result;
2083
}
2084

    
2085
function get_current_theme() {
2086
	global $config, $g;
2087
	/*
2088
	 *   if user has selected a custom template, use it.
2089
	 *   otherwise default to pfsense tempalte
2090
	 */
2091
	if (($g["disablethemeselection"] === true) && !empty($g["default_theme"]) && (is_dir($g["www_path"].'/themes/'.$g["default_theme"])))
2092
		$theme = $g["default_theme"];
2093
	elseif($config['theme'] <> "" && (is_dir($g["www_path"].'/themes/'.$config['theme'])))
2094
		$theme = $config['theme'];
2095
	else
2096
		$theme = "pfsense";
2097
	/*
2098
	 *  If this device is an apple ipod/iphone
2099
	 *  switch the theme to one that works with it.
2100
	 */
2101
	$lowres_ua = array("iPhone", "iPod", "iPad", "Android", "BlackBerry", "Opera Mini", "Opera Mobi", "PlayBook", "IEMobile");
2102
	foreach($lowres_ua as $useragent)
2103
		if(strstr($_SERVER['HTTP_USER_AGENT'], $useragent))
2104
			$theme = (empty($g['theme_lowres']) && (is_dir($g["www_path"].'/themes/'.$g['theme_lowres']))) ? "pfsense" : $g['theme_lowres'];
2105
	return $theme;
2106
}
2107

    
2108
/* Define what is preferred, IPv4 or IPv6 */
2109
function prefer_ipv4_or_ipv6() {
2110
	global $config;
2111

    
2112
	if (isset($config['system']['prefer_ipv4']))
2113
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2114
	else
2115
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2116
}
2117

    
2118
/* Redirect to page passing parameters via POST */
2119
function post_redirect($page, $params) {
2120
	if (!is_array($params))
2121
		return;
2122

    
2123
	print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n";
2124
	foreach ($params as $key => $value) {
2125
		print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n";
2126
	}
2127
	print "</form><script type=\"text/javascript\">document.formredir.submit();</script>\n";
2128
	print "</body></html>\n";
2129
}
2130

    
2131
/* Locate disks that can be queried for S.M.A.R.T. data. */
2132
function get_smart_drive_list() {
2133
	$disk_list = explode(" ", get_single_sysctl("kern.disks"));
2134
	foreach ($disk_list as $id => $disk) {
2135
		// We only want certain kinds of disks for S.M.A.R.T.
2136
		// 1 is a match, 0 is no match, False is any problem processing the regex
2137
		if (preg_match("/^(ad|da|ada).*[0-9]{1,2}$/", $disk) !== 1) {
2138
			unset($disk_list[$id]);
2139
		}
2140
	}
2141
	sort($disk_list);
2142
	return $disk_list;
2143
}
2144

    
2145
?>
(56-56/68)