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 long2ip(ip2long($ipaddr) | (0xFFFFFFFF >> $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)) {
765
		return (Net_IPv6::isInNetmask($addr, $subnet));
766
	} else { /* XXX: Maybe check for IPv4 */
767
		list($ip, $mask) = explode('/', $subnet);
768
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
769
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
770
	}
771
}
772

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
915
	return $result;
916
}
917

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

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

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

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

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

    
948
	$iflist = array();
949

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

    
993
	return $iflist;
994
}
995

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

    
1000
	$alias_list=array();
1001

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

    
1014
	return $alias_list;
1015
}
1016

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

    
1021
	$alias_list=array();
1022

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

    
1033
	return $alias_list;
1034
}
1035

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

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

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

    
1056
	$iflist = array();
1057

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

    
1066
	return $iflist;
1067
}
1068

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

    
1073
	$iflist = array();
1074

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

    
1086
	return $iflist;
1087
}
1088

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

    
1093
	$iflist = array();
1094

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

    
1107
	return $iflist;
1108
}
1109

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

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

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

    
1145
	return $ip_array;
1146
}
1147

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1400
	$aliastable = array();
1401

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

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

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

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

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

    
1427
	return "";
1428
}
1429

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

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

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

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

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

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

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

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

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

    
1485
	return false;
1486
}
1487

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

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

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

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

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

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

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

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

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

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

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

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

    
1535
		sleep(1);
1536
	}
1537

    
1538
	return false;
1539
}
1540

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1655
	return $values;
1656
}
1657

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

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

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

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

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

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

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

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

    
1704
	return $ret;
1705
}
1706

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

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

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

    
1721
	return true;
1722
}
1723

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

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

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

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

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

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

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

    
1785
	return false;
1786
}
1787

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

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

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

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

    
1817
	return $do_assign;
1818
}
1819

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

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

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

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

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

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

    
1915
	return false;
1916
}
1917

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

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

    
1931
	return $data;
1932
}
1933

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

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

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

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

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

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

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

    
1985

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2146
?>
(56-56/68)