Project

General

Profile

Download (56.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
	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_bits'];
960
							else if ($family == 'inet6' && is_ipaddrv6($vip['subnet']))
961
								return $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
	// alias names cannot be strictly numeric. redmine #4289
1434
	if (is_numericint($name))
1435
		return null;
1436
	
1437
	if (isset($aliastable[$name]))
1438
		return "\${$name}";
1439
	else if (is_ipaddr($name) || is_subnet($name) || is_port($name) || is_portrange($name))
1440
		return "{$name}";
1441
	else
1442
		return null;
1443
}
1444

    
1445
function alias_expand_urltable($name) {
1446
	global $config;
1447
	$urltable_prefix = "/var/db/aliastables/";
1448
	$urltable_filename = $urltable_prefix . $name . ".txt";
1449

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

    
1463
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1464
function verify_digital_signature($fname) {
1465
	global $g;
1466

    
1467
	if(!file_exists("/usr/local/sbin/gzsig"))
1468
		return 4;
1469

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

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

    
1479
	if ($arpoutput[0]) {
1480
		$arpi = explode(" ", $arpoutput[0]);
1481
		$macaddr = $arpi[3];
1482
		if (is_macaddr($macaddr))
1483
			return $macaddr;
1484
		else
1485
			return false;
1486
	}
1487

    
1488
	return false;
1489
}
1490

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

    
1500
function mac_format($clientmac) {
1501
	global $config, $cpzone;
1502

    
1503
	$mac = explode(":", $clientmac);
1504
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1505

    
1506
	switch($mac_format) {
1507
	case 'singledash':
1508
		return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1509

    
1510
	case 'ietf':
1511
		return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1512

    
1513
	case 'cisco':
1514
		return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1515

    
1516
	case 'unformatted':
1517
		return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1518

    
1519
	default:
1520
		return $clientmac;
1521
	}
1522
}
1523

    
1524
function resolve_retry($hostname, $retries = 5) {
1525

    
1526
	if (is_ipaddr($hostname))
1527
		return $hostname;
1528

    
1529
	for ($i = 0; $i < $retries; $i++) {
1530
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1531
		$ip = gethostbyname($hostname);
1532

    
1533
		if ($ip && $ip != $hostname) {
1534
			/* success */
1535
			return $ip;
1536
		}
1537

    
1538
		sleep(1);
1539
	}
1540

    
1541
	return false;
1542
}
1543

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

    
1556
function update_filter_reload_status($text) {
1557
	global $g;
1558

    
1559
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1560
}
1561

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

    
1579
				if (empty($filter_regex) || preg_match($filter_regex, $file))
1580
					array_push($dir_array, $file);
1581
			}
1582
			closedir($dh);
1583
		}
1584
	}
1585
	return $dir_array;
1586
}
1587

    
1588
function run_plugins($directory) {
1589
	global $config, $g;
1590

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

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

    
1610
	if (!is_file($path) && !is_dir($path)) {
1611
		return @mkdir($path, $mode, true);
1612
	} else {
1613
		return false;
1614
	}
1615
}
1616

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

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

    
1642
	if (is_array($names)) {
1643
		$name_list = array();
1644
		foreach ($names as $name) {
1645
			$name_list[] = escapeshellarg($name);
1646
		}
1647
	} else
1648
		$name_list = array(escapeshellarg($names));
1649

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

    
1658
	return $values;
1659
}
1660

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

    
1670
	$value = get_sysctl($name);
1671
	if (empty($value) || !isset($value[$name]))
1672
		return "";
1673

    
1674
	return $value[$name];
1675
}
1676

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

    
1686
	$value_list = array();
1687
	foreach ($values as $key => $value) {
1688
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1689
	}
1690

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

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

    
1700
	$ret = array();
1701
	foreach ($output as $line) {
1702
		$line = explode(": ", $line, 2);
1703
		if (count($line) == 2)
1704
			$ret[$line[0]] = true;
1705
	}
1706

    
1707
	return $ret;
1708
}
1709

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

    
1719
	$result = set_sysctl(array($name => $value));
1720

    
1721
	if (!isset($result[$name]) || $result[$name] != $value)
1722
		return false;
1723

    
1724
	return true;
1725
}
1726

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

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

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

    
1767
function start_devd() {
1768
	global $g;
1769

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

    
1778
function is_interface_vlan_mismatch() {
1779
	global $config, $g;
1780

    
1781
	if (is_array($config['vlans']['vlan'])) {
1782
		foreach ($config['vlans']['vlan'] as $vlan) {
1783
			if (does_interface_exist($vlan['if']) == false)
1784
				return true;
1785
		}
1786
	}
1787

    
1788
	return false;
1789
}
1790

    
1791
function is_interface_mismatch() {
1792
	global $config, $g;
1793

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

    
1812
	if (file_exists("{$g['tmp_path']}/assign_complete"))
1813
		$do_assign = false;
1814

    
1815
	if (!empty($missing_interfaces) && $do_assign)
1816
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1817
	else
1818
		@unlink("{$g['tmp_path']}/missing_interfaces");
1819

    
1820
	return $do_assign;
1821
}
1822

    
1823
/* sync carp entries to other firewalls */
1824
function carp_sync_client() {
1825
	global $g;
1826
	send_event("filter sync");
1827
}
1828

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

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

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

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

    
1913
function is_file_included($file = "") {
1914
	$files = get_included_files();
1915
	if (in_array($file, $files))
1916
		return true;
1917

    
1918
	return false;
1919
}
1920

    
1921
/*
1922
 * Replace a value on a deep associative array using regex
1923
 */
1924
function array_replace_values_recursive($data, $match, $replace) {
1925
	if (empty($data))
1926
		return $data;
1927

    
1928
	if (is_string($data))
1929
		$data = preg_replace("/{$match}/", $replace, $data);
1930
	else if (is_array($data))
1931
		foreach ($data as $k => $v)
1932
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
1933

    
1934
	return $data;
1935
}
1936

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

    
1943
	$arrays = func_get_args();
1944
	$remains = $arrays;
1945

    
1946
	// We walk through each arrays and put value in the results (without
1947
	// considering previous value).
1948
	$result = array();
1949

    
1950
	// loop available array
1951
	foreach($arrays as $array) {
1952

    
1953
		// The first remaining array is $array. We are processing it. So
1954
		// we remove it from remaing arrays.
1955
		array_shift($remains);
1956

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

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

    
1988

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

    
2003
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
2004
	global $config, $aliastable;
2005

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

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

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

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

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

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

    
2112
/* Define what is preferred, IPv4 or IPv6 */
2113
function prefer_ipv4_or_ipv6() {
2114
	global $config;
2115

    
2116
	if (isset($config['system']['prefer_ipv4']))
2117
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2118
	else
2119
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2120
}
2121

    
2122
/* Redirect to page passing parameters via POST */
2123
function post_redirect($page, $params) {
2124
	if (!is_array($params))
2125
		return;
2126

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

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

    
2149
?>
(56-56/68)