Project

General

Profile

Download (70.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * util.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
7
 * All rights reserved.
8
 *
9
 * originally part of m0n0wall (http://m0n0.ch/wall)
10
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
11
 * All rights reserved.
12
 *
13
 * Licensed under the Apache License, Version 2.0 (the "License");
14
 * you may not use this file except in compliance with the License.
15
 * You may obtain a copy of the License at
16
 *
17
 * http://www.apache.org/licenses/LICENSE-2.0
18
 *
19
 * Unless required by applicable law or agreed to in writing, software
20
 * distributed under the License is distributed on an "AS IS" BASIS,
21
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
 * See the License for the specific language governing permissions and
23
 * limitations under the License.
24
 */
25

    
26
define('VIP_ALL', 1);
27
define('VIP_CARP', 2);
28
define('VIP_IPALIAS', 3);
29

    
30
/* kill a process by pid file */
31
function killbypid($pidfile) {
32
	return sigkillbypid($pidfile, "TERM");
33
}
34

    
35
function isvalidpid($pidfile) {
36
	$output = "";
37
	if (file_exists($pidfile)) {
38
		exec("/bin/pgrep -qnF {$pidfile} 2>/dev/null", $output, $retval);
39
		return (intval($retval) == 0);
40
	}
41
	return false;
42
}
43

    
44
function is_process_running($process) {
45
	$output = "";
46
	exec("/bin/pgrep -anx " . escapeshellarg($process), $output, $retval);
47

    
48
	return (intval($retval) == 0);
49
}
50

    
51
function isvalidproc($proc) {
52
	return is_process_running($proc);
53
}
54

    
55
/* sigkill a process by pid file */
56
/* return 1 for success and 0 for a failure */
57
function sigkillbypid($pidfile, $sig) {
58
	if (isvalidpid($pidfile)) {
59
		return mwexec("/bin/pkill " . escapeshellarg("-{$sig}") .
60
		    " -F {$pidfile}", true);
61
	}
62

    
63
	return 0;
64
}
65

    
66
/* kill a process by name */
67
function sigkillbyname($procname, $sig) {
68
	if (isvalidproc($procname)) {
69
		return mwexec("/usr/bin/killall " . escapeshellarg("-{$sig}") . " " . escapeshellarg($procname), true);
70
	}
71
}
72

    
73
/* kill a process by name */
74
function killbyname($procname) {
75
	if (isvalidproc($procname)) {
76
		mwexec("/usr/bin/killall " . escapeshellarg($procname));
77
	}
78
}
79

    
80
function is_subsystem_dirty($subsystem = "") {
81
	global $g;
82

    
83
	if ($subsystem == "") {
84
		return false;
85
	}
86

    
87
	if (file_exists("{$g['varrun_path']}/{$subsystem}.dirty")) {
88
		return true;
89
	}
90

    
91
	return false;
92
}
93

    
94
function mark_subsystem_dirty($subsystem = "") {
95
	global $g;
96

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

    
102
function clear_subsystem_dirty($subsystem = "") {
103
	global $g;
104

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

    
108
function config_lock() {
109
	return;
110
}
111
function config_unlock() {
112
	return;
113
}
114

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

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

    
156
		return $fp;
157
	}
158

    
159
	return NULL;
160
}
161

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

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

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

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

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

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

    
202
function send_multiple_events($cmds) {
203
	global $g;
204

    
205
	if (!isset($g['event_address'])) {
206
		$g['event_address'] = "unix:///var/run/check_reload_status";
207
	}
208

    
209
	if (!is_array($cmds)) {
210
		return;
211
	}
212

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

    
232
function is_module_loaded($module_name) {
233
	$module_name = str_replace(".ko", "", $module_name);
234
	$running = 0;
235
	$_gb = exec("/sbin/kldstat -qn {$module_name} 2>&1", $_gb, $running);
236
	if (intval($running) == 0) {
237
		return true;
238
	} else {
239
		return false;
240
	}
241
}
242

    
243
/* validate non-negative numeric string, or equivalent numeric variable */
244
function is_numericint($arg) {
245
	return (((is_int($arg) && $arg >= 0) || (is_string($arg) && strlen($arg) > 0 && ctype_digit($arg))) ? true : false);
246
}
247

    
248
/* Generate the (human readable) ipv4 or ipv6 subnet address (i.e., netmask, or subnet start IP)
249
   given an (human readable) ipv4 or ipv6 host address and subnet bit count */
250
function gen_subnet($ipaddr, $bits) {
251
	if (($sn = gen_subnetv6($ipaddr, $bits)) == '') {
252
		$sn = gen_subnetv4($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
253
	}
254
	return $sn;
255
}
256

    
257
/* same as gen_subnet() but accepts IPv4 only */
258
function gen_subnetv4($ipaddr, $bits) {
259
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
260
		if ($bits == 0) {
261
			return '0.0.0.0';  // avoids <<32
262
		}
263
		return long2ip(ip2long($ipaddr) & ((0xFFFFFFFF << (32 - $bits)) & 0xFFFFFFFF));
264
	}
265
	return "";
266
}
267

    
268
/* same as gen_subnet() but accepts IPv6 only */
269
function gen_subnetv6($ipaddr, $bits) {
270
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
271
		return Net_IPv6::compress(Net_IPv6::getNetmask($ipaddr, $bits));
272
	}
273
	return "";
274
}
275

    
276
/* Generate the (human readable) ipv4 or ipv6 subnet end address (i.e., highest address, end IP, or IPv4 broadcast address)
277
   given an (human readable) ipv4 or ipv6 host address and subnet bit count. */
278
function gen_subnet_max($ipaddr, $bits) {
279
	if (($sn = gen_subnetv6_max($ipaddr, $bits)) == '') {
280
		$sn = gen_subnetv4_max($ipaddr, $bits);  // try to avoid rechecking IPv4/v6
281
	}
282
	return $sn;
283
}
284

    
285
/* same as gen_subnet_max() but validates IPv4 only */
286
function gen_subnetv4_max($ipaddr, $bits) {
287
	if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) {
288
		if ($bits == 32) {
289
			return $ipaddr;
290
		}
291
		return long2ip32(ip2long($ipaddr) | (~gen_subnet_mask_long($bits) & 0xFFFFFFFF));
292
	}
293
	return "";
294
}
295

    
296
/* same as gen_subnet_max() but validates IPv6 only */
297
function gen_subnetv6_max($ipaddr, $bits) {
298
	if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) {
299
		$endip_bin = substr(ip6_to_bin($ipaddr), 0, $bits) . str_repeat('1', 128 - $bits);
300
		return bin_to_compressed_ip6($endip_bin);
301
	}
302
	return "";
303
}
304

    
305
/* returns a subnet mask (long given a bit count) */
306
function gen_subnet_mask_long($bits) {
307
	$sm = 0;
308
	for ($i = 0; $i < $bits; $i++) {
309
		$sm >>= 1;
310
		$sm |= 0x80000000;
311
	}
312
	return $sm;
313
}
314

    
315
/* same as above but returns a string */
316
function gen_subnet_mask($bits) {
317
	return long2ip(gen_subnet_mask_long($bits));
318
}
319

    
320
/* Convert a prefix length to an IPv6 address-like mask notation. Very rare but at least ntp needs it. See #4463 */
321
function gen_subnet_mask_v6($bits) {
322
	/* Binary representation of the prefix length */
323
	$bin = str_repeat('1', $bits);
324
	/* Pad right with zeroes to reach the full address length */
325
	$bin = str_pad($bin, 128, '0', STR_PAD_RIGHT);
326
	/* Convert back to an IPv6 address style notation */
327
	return bin_to_ip6($bin);
328
}
329

    
330
/* Convert long int to IPv4 address
331
   Returns '' if not valid IPv4 (including if any bits >32 are non-zero) */
332
function long2ip32($ip) {
333
	return long2ip($ip & 0xFFFFFFFF);
334
}
335

    
336
/* Convert IPv4 address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms.
337
   Returns '' if not valid IPv4. */
338
function ip2long32($ip) {
339
	return (ip2long($ip) & 0xFFFFFFFF);
340
}
341

    
342
/* Convert IPv4 address to unsigned long int.
343
   Returns '' if not valid IPv4. */
344
function ip2ulong($ip) {
345
	return sprintf("%u", ip2long32($ip));
346
}
347

    
348
/*
349
 * Convert IPv6 address to binary
350
 *
351
 * Obtained from: pear-Net_IPv6
352
 */
353
function ip6_to_bin($ip) {
354
	$binstr = '';
355

    
356
	$ip = Net_IPv6::removeNetmaskSpec($ip);
357
	$ip = Net_IPv6::Uncompress($ip);
358

    
359
	$parts = explode(':', $ip);
360

    
361
	foreach ( $parts as $v ) {
362

    
363
		$str     = base_convert($v, 16, 2);
364
		$binstr .= str_pad($str, 16, '0', STR_PAD_LEFT);
365

    
366
	}
367

    
368
	return $binstr;
369
}
370

    
371
/*
372
 * Convert IPv6 binary to uncompressed address
373
 *
374
 * Obtained from: pear-Net_IPv6
375
 */
376
function bin_to_ip6($bin) {
377
	$ip = "";
378

    
379
	if (strlen($bin) < 128) {
380
		$bin = str_pad($bin, 128, '0', STR_PAD_LEFT);
381
	}
382

    
383
	$parts = str_split($bin, "16");
384

    
385
	foreach ( $parts as $v ) {
386
		$str = base_convert($v, 2, 16);
387
		$ip .= $str.":";
388
	}
389

    
390
	$ip = substr($ip, 0, -1);
391

    
392
	return $ip;
393
}
394

    
395
/*
396
 * Convert IPv6 binary to compressed address
397
 */
398
function bin_to_compressed_ip6($bin) {
399
	return Net_IPv6::compress(bin_to_ip6($bin));
400
}
401

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

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

    
425
/* Return the previous IP address before the given address */
426
function ip_before($ip, $offset = 1) {
427
	return long2ip32(ip2long($ip) - $offset);
428
}
429

    
430
/* Return the next IP address after the given address */
431
function ip_after($ip, $offset = 1) {
432
	return long2ip32(ip2long($ip) + $offset);
433
}
434

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

    
442
/* Return true if the first IP is 'after' the second */
443
function ip_greater_than($ip1, $ip2) {
444
	// Compare as unsigned long because otherwise it wouldn't work
445
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
446
	return ip2ulong($ip1) > ip2ulong($ip2);
447
}
448

    
449
/* compare two IP addresses */
450
function ipcmp($a, $b) {
451
	if (ip_less_than($a, $b)) {
452
		return -1;
453
	} else if (ip_greater_than($a, $b)) {
454
		return 1;
455
	} else {
456
		return 0;
457
	}
458
}
459

    
460
/* Convert a range of IPv4 addresses to an array of individual addresses. */
461
/* Note: IPv6 ranges are not yet supported here. */
462
function ip_range_to_address_array($startip, $endip, $max_size = 5000) {
463
	if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) {
464
		return false;
465
	}
466

    
467
	if (ip_greater_than($startip, $endip)) {
468
		// Swap start and end so we can process sensibly.
469
		$temp = $startip;
470
		$startip = $endip;
471
		$endip = $temp;
472
	}
473

    
474
	if (ip_range_size_v4($startip, $endip) > $max_size) {
475
		return false;
476
	}
477

    
478
	// Container for IP addresses within this range.
479
	$rangeaddresses = array();
480
	$end_int = ip2ulong($endip);
481
	for ($ip_int = ip2ulong($startip); $ip_int <= $end_int; $ip_int++) {
482
		$rangeaddresses[] = long2ip($ip_int);
483
	}
484

    
485
	return $rangeaddresses;
486
}
487

    
488
/* 	Convert an IPv4 or IPv6 IP range to an array of subnets which can contain the range.
489
	Algorithm and embodying code PD'ed by Stilez - enjoy as you like :-)
490

    
491
	Documented on pfsense dev list 19-20 May 2013. Summary:
492

    
493
	The algorithm looks at patterns of 0's and 1's in the least significant bit(s), whether IPv4 or IPv6.
494
	These are all that needs checking to identify a _guaranteed_ correct, minimal and optimal subnet array.
495

    
496
	As a result, string/binary pattern matching of the binary IP is very efficient. It uses just 2 pattern-matching rules
497
	to chop off increasingly larger subnets at both ends that can't be part of larger subnets, until nothing's left.
498

    
499
	(a) If any range has EITHER low bit 1 (in startip) or 0 (in endip), that end-point is _always guaranteed_ to be optimally
500
	represented by its own 'single IP' CIDR; the remaining range then shrinks by one IP up or down, causing the new end-point's
501
	low bit to change from 1->0 (startip) or 0->1 (endip). Only one edge case needs checking: if a range contains exactly 2
502
	adjacent IPs of this format, then the two IPs themselves are required to span it, and we're done.
503
	Once this rule is applied, the remaining range is _guaranteed_ to end in 0's and 1's so rule (b) can now be used, and its
504
	low bits can now be ignored.
505

    
506
	(b) If any range has BOTH startip and endip ending in some number of 0's and 1's respectively, these low bits can
507
	*always* be ignored and "bit-shifted" for subnet spanning. So provided we remember the bits we've place-shifted, we can
508
	_always_ right-shift and chop off those bits, leaving a smaller range that has EITHER startip ending in 1 or endip ending
509
	in 0 (ie can now apply (a) again) or the entire range has vanished and we're done.
510
	We then loop to redo (a) again on the remaining (place shifted) range until after a few loops, the remaining (place shifted)
511
	range 'vanishes' by meeting the exit criteria of (a) or (b), and we're done.
512
*/
513

    
514
function ip_range_to_subnet_array($ip1, $ip2) {
515

    
516
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) {
517
		$proto = 'ipv4';  // for clarity
518
		$bits = 32;
519
		$ip1bin = decbin(ip2long32($ip1));
520
		$ip2bin = decbin(ip2long32($ip2));
521
	} elseif (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) {
522
		$proto = 'ipv6';
523
		$bits = 128;
524
		$ip1bin = ip6_to_bin($ip1);
525
		$ip2bin = ip6_to_bin($ip2);
526
	} else {
527
		return array();
528
	}
529

    
530
	// it's *crucial* that binary strings are guaranteed the expected length;  do this for certainty even though for IPv6 it's redundant
531
	$ip1bin = str_pad($ip1bin, $bits, '0', STR_PAD_LEFT);
532
	$ip2bin = str_pad($ip2bin, $bits, '0', STR_PAD_LEFT);
533

    
534
	if ($ip1bin == $ip2bin) {
535
		return array($ip1 . '/' . $bits); // exit if ip1=ip2 (trivial case)
536
	}
537

    
538
	if ($ip1bin > $ip2bin) {
539
		list ($ip1bin, $ip2bin) = array($ip2bin, $ip1bin);  // swap if needed (ensures ip1 < ip2)
540
	}
541

    
542
	$rangesubnets = array();
543
	$netsize = 0;
544

    
545
	do {
546
		// at loop start, $ip1 is guaranteed strictly less than $ip2 (important for edge case trapping and preventing accidental binary wrapround)
547
		// which means the assignments $ip1 += 1 and $ip2 -= 1 will always be "binary-wrapround-safe"
548

    
549
		// step #1 if start ip (as shifted) ends in any '1's, then it must have a single cidr to itself (any cidr would include the '0' below it)
550

    
551
		if (substr($ip1bin, -1, 1) == '1') {
552
			// the start ip must be in a separate one-IP cidr range
553
			$new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
554
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
555
			$n = strrpos($ip1bin, '0');  //can't be all 1's
556
			$ip1bin = ($n == 0 ? '' : substr($ip1bin, 0, $n)) . '1' . str_repeat('0', $bits - $n - 1);  // BINARY VERSION OF $ip1 += 1
557
		}
558

    
559
		// step #2, if end ip (as shifted) ends in any zeros then that must have a cidr to itself (as cidr cant span the 1->0 gap)
560

    
561
		if (substr($ip2bin, -1, 1) == '0') {
562
			// the end ip must be in a separate one-IP cidr range
563
			$new_subnet_ip = substr($ip2bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
564
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
565
			$n = strrpos($ip2bin, '1');  //can't be all 0's
566
			$ip2bin = ($n == 0 ? '' : substr($ip2bin, 0, $n)) . '0' . str_repeat('1', $bits - $n - 1);  // BINARY VERSION OF $ip2 -= 1
567
			// already checked for the edge case where end = start+1 and start ends in 0x1, above, so it's safe
568
		}
569

    
570
		// this is the only edge case arising from increment/decrement.
571
		// it happens if the range at start of loop is exactly 2 adjacent ips, that spanned the 1->0 gap. (we will have enumerated both by now)
572

    
573
		if ($ip2bin < $ip1bin) {
574
			continue;
575
		}
576

    
577
		// step #3 the start and end ip MUST now end in '0's and '1's respectively
578
		// so we have a non-trivial range AND the last N bits are no longer important for CIDR purposes.
579

    
580
		$shift = $bits - max(strrpos($ip1bin, '0'), strrpos($ip2bin, '1'));  // num of low bits which are '0' in ip1 and '1' in ip2
581
		$ip1bin = str_repeat('0', $shift) . substr($ip1bin, 0, $bits - $shift);
582
		$ip2bin = str_repeat('0', $shift) . substr($ip2bin, 0, $bits - $shift);
583
		$netsize += $shift;
584
		if ($ip1bin == $ip2bin) {
585
			// we're done.
586
			$new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize);
587
			$rangesubnets[$new_subnet_ip] = $bits - $netsize;
588
			continue;
589
		}
590

    
591
		// at this point there's still a remaining range, and either startip ends with '1', or endip ends with '0'. So repeat cycle.
592
	} while ($ip1bin < $ip2bin);
593

    
594
	// subnets are ordered by bit size. Re sort by IP ("naturally") and convert back to IPv4/IPv6
595

    
596
	ksort($rangesubnets, SORT_STRING);
597
	$out = array();
598

    
599
	foreach ($rangesubnets as $ip => $netmask) {
600
		if ($proto == 'ipv4') {
601
			$i = str_split($ip, 8);
602
			$out[] = implode('.', array(bindec($i[0]), bindec($i[1]), bindec($i[2]), bindec($i[3]))) . '/' . $netmask;
603
		} else {
604
			$out[] = bin_to_compressed_ip6($ip) . '/' . $netmask;
605
		}
606
	}
607

    
608
	return $out;
609
}
610

    
611
/* returns true if $range is a valid pair of IPv4 or IPv6 addresses separated by a "-"
612
	false - if not a valid pair
613
	true (numeric 4 or 6) - if valid, gives type of addresses */
614
function is_iprange($range) {
615
	if (substr_count($range, '-') != 1) {
616
		return false;
617
	}
618
	list($ip1, $ip2) = explode ('-', $range);
619
	if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) {
620
		return 4;
621
	}
622
	if (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) {
623
		return 6;
624
	}
625
	return false;
626
}
627

    
628
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6
629
	false - not valid
630
	true (numeric 4 or 6) - if valid, gives type of address */
631
function is_ipaddr($ipaddr) {
632
	if (is_ipaddrv4($ipaddr)) {
633
		return 4;
634
	}
635
	if (is_ipaddrv6($ipaddr)) {
636
		return 6;
637
	}
638
	return false;
639
}
640

    
641
/* returns true if $ipaddr is a valid IPv6 address */
642
function is_ipaddrv6($ipaddr) {
643
	if (!is_string($ipaddr) || empty($ipaddr)) {
644
		return false;
645
	}
646
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
647
		$tmpip = explode("%", $ipaddr);
648
		$ipaddr = $tmpip[0];
649
	}
650
	return Net_IPv6::checkIPv6($ipaddr);
651
}
652

    
653
/* returns true if $ipaddr is a valid dotted IPv4 address */
654
function is_ipaddrv4($ipaddr) {
655
	if (!is_string($ipaddr) || empty($ipaddr) || ip2long($ipaddr) === FALSE) {
656
		return false;
657
	}
658
	return true;
659
}
660

    
661
/* returns 4 or 6 respectively (== TRUE) if $ipaddr is a valid IPv4 or IPv6 linklocal address
662
   returns '' if not a valid linklocal address */
663
function is_linklocal($ipaddr) {
664
	if (is_ipaddrv4($ipaddr)) {
665
		// input is IPv4
666
		// test if it's 169.254.x.x per rfc3927 2.1
667
		$ip4 = explode(".", $ipaddr);
668
		if ($ip4[0] == '169' && $ip4[1] == '254') {
669
			return 4;
670
		}
671
	} elseif (Net_IPv6::getAddressType($ipaddr) == NET_IPV6_LOCAL_LINK) {
672
		return 6;
673
	}
674
	return '';
675
}
676

    
677
/* returns scope of a linklocal address */
678
function get_ll_scope($addr) {
679
	if (!is_linklocal($addr) || !strstr($addr, "%")) {
680
		return "";
681
	}
682
	list ($ll, $scope) = explode("%", $addr);
683
	return $scope;
684
}
685

    
686
/* returns true if $ipaddr is a valid literal IPv6 address */
687
function is_literalipaddrv6($ipaddr) {
688
	if (substr($ipaddr,0,1) == '[' && substr($ipaddr,-1,1) == ']') {
689
		// if it's data wrapped in "[ ... ]" then test if middle part is valid IPv6
690
		return is_ipaddrv6(substr($ipaddr,1,-1));
691
	}
692
	return false;
693
}
694

    
695
/* returns true if $iport is a valid IPv4:port or [Literal IPv6]:port
696
	false - not valid
697
	true (numeric 4 or 6) - if valid, gives type of address */
698
function is_ipaddrwithport($ipport) {
699
	$c = strrpos($ipport, ":");
700
	if ($c === false) {
701
		return false;  // can't split at final colon if no colon exists
702
	}
703

    
704
	if (!is_port(substr($ipport, $c + 1))) {
705
		return false;  // no valid port after last colon
706
	}
707

    
708
	$ip = substr($ipport, 0, $c);  // else is text before last colon a valid IP
709
	if (is_literalipaddrv6($ip)) {
710
		return 6;
711
	} elseif (is_ipaddrv4($ip)) {
712
		return 4;
713
	} else {
714
		return false;
715
	}
716
}
717

    
718
function is_hostnamewithport($hostport) {
719
	$parts = explode(":", $hostport);
720
	// no need to validate with is_string(); if it's not a string then explode won't return 2 parts anyway
721
	if (count($parts) == 2) {
722
		return is_hostname($parts[0]) && is_port($parts[1]);
723
	}
724
	return false;
725
}
726

    
727
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
728
function is_ipaddroralias($ipaddr) {
729
	global $config;
730

    
731
	if (is_alias($ipaddr)) {
732
		if (is_array($config['aliases']['alias'])) {
733
			foreach ($config['aliases']['alias'] as $alias) {
734
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type'])) {
735
					return true;
736
				}
737
			}
738
		}
739
		return false;
740
	} else {
741
		return is_ipaddr($ipaddr);
742
	}
743

    
744
}
745

    
746
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format
747
	false - if not a valid subnet
748
	true (numeric 4 or 6) - if valid, gives type of subnet */
749
function is_subnet($subnet) {
750
	if (is_string($subnet) && preg_match('/^(?:([0-9.]{7,15})|([0-9a-f:]{2,39}))\/(\d{1,3})$/i', $subnet, $parts)) {
751
		if (is_ipaddrv4($parts[1]) && $parts[3] <= 32) {
752
			return 4;
753
		}
754
		if (is_ipaddrv6($parts[2]) && $parts[3] <= 128) {
755
			return 6;
756
		}
757
	}
758
	return false;
759
}
760

    
761
/* same as is_subnet() but accepts IPv4 only */
762
function is_subnetv4($subnet) {
763
	return (is_subnet($subnet) == 4);
764
}
765

    
766
/* same as is_subnet() but accepts IPv6 only */
767
function is_subnetv6($subnet) {
768
	return (is_subnet($subnet) == 6);
769
}
770

    
771
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
772
function is_subnetoralias($subnet) {
773
	global $aliastable;
774

    
775
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet])) {
776
		return true;
777
	} else {
778
		return is_subnet($subnet);
779
	}
780
}
781

    
782
/* Get number of addresses in an IPv4/IPv6 subnet (represented as a string)
783
   optional $exact=true forces error (0) to be returned if it can't be represented exactly
784
   Exact result not possible above PHP_MAX_INT which is about 2^31 addresses on x32 or 2^63 on x64
785
   Returns 0 for bad data or if cannot represent size as an INT when $exact is set. */
786
function subnet_size($subnet, $exact=false) {
787
	$parts = explode("/", $subnet);
788
	$iptype = is_ipaddr($parts[0]);
789
	if (count($parts) == 2 && $iptype) {
790
		return subnet_size_by_netmask($iptype, $parts[1], $exact);
791
	}
792
	return 0;
793
}
794

    
795
/* Get number of addresses in an IPv4/IPv6 subnet (represented numerically as IP type + bits)
796
   optional $exact=true forces error (0) to be returned if it can't be represented exactly
797
   Hard to think where we might need to count exactly a huge subnet but an overflow detection option is probably sensible
798
   Returns 0 for bad data or if cannot represent size as an INT when $exact is set. */
799
function subnet_size_by_netmask($iptype, $bits, $exact=false) {
800
	if (!is_numericint($bits)) {
801
		return 0;
802
	} elseif ($iptype == 4 && $bits <= 32) {
803
		$snsize = 32 - $bits;
804
	} elseif ($iptype == 6 && $bits <= 128) {
805
		$snsize = 128 - $bits;
806
	} else {
807
		return 0;
808
	}
809

    
810
	// 2**N returns an exact result as an INT if possible, and a float/double if not.
811
	// Detect this switch, rather than comparing $result<=PHP_MAX_INT or $bits >=8*PHP_INT_SIZE as it's (probably) easier to get completely reliable
812
	$result = 2 ** $snsize;
813

    
814
	if ($exact && !is_int($result)) {
815
		//exact required but can't represent result exactly as an INT
816
		return 0;
817
	} else {
818
		// result ok, will be an INT where possible (guaranteed up to 2^31 addresses on x32/x64) and a float for 'huge' subnets
819
		return $result;
820
	}
821
}
822

    
823
/* function used by pfblockerng */
824
function subnetv4_expand($subnet) {
825
	$result = array();
826
	list ($ip, $bits) = explode("/", $subnet);
827
	$net = ip2long($ip);
828
	$mask = (0xffffffff << (32 - $bits));
829
	$net &= $mask;
830
	$size = round(exp(log(2) * (32 - $bits)));
831
	for ($i = 0; $i < $size; $i += 1) {
832
		$result[] = long2ip($net | $i);
833
	}
834
	return $result;
835
}
836

    
837
/* find out whether two IPv4/IPv6 CIDR subnets overlap.
838
   Note: CIDR overlap implies one is identical or included so largest sn will be the same */
839
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
840
	if (is_ipaddrv4($subnet1)) {
841
		return check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2);
842
	} else {
843
		return check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2);
844
	}
845
}
846

    
847
/* find out whether two IPv4 CIDR subnets overlap.
848
   Note: CIDR overlap means sn1/sn2 are identical or one is included in other. So sn using largest $bits will be the same  */
849
function check_subnetsv4_overlap($subnet1, $bits1, $subnet2, $bits2) {
850
	$largest_sn = min($bits1, $bits2);
851
	$subnetv4_start1 = gen_subnetv4($subnet1, $largest_sn);
852
	$subnetv4_start2 = gen_subnetv4($subnet2, $largest_sn);
853

    
854
	if ($subnetv4_start1 == '' || $subnetv4_start2 == '') {
855
		// One or both args is not a valid IPv4 subnet
856
		//FIXME: needs to return "bad data" not true/false if bad. For now return false, best we can do until fixed
857
		return false;
858
	}
859
	return ($subnetv4_start1 == $subnetv4_start2);
860
}
861

    
862
/* find out whether two IPv6 CIDR subnets overlap.
863
   Note: CIDR overlap means sn1/sn2 are identical or one is included in other. So sn using largest $bits will be the same  */
864
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
865
	$largest_sn = min($bits1, $bits2);
866
	$subnetv6_start1 = gen_subnetv6($subnet1, $largest_sn);
867
	$subnetv6_start2 = gen_subnetv6($subnet2, $largest_sn);
868

    
869
	if ($subnetv6_start1 == '' || $subnetv6_start2 == '') {
870
		// One or both args is not a valid IPv6 subnet
871
		//FIXME: needs to return "bad data" not true/false if bad. For now return false, best we can do until fixed
872
		return false;
873
	}
874
	return ($subnetv6_start1 == $subnetv6_start2);
875
}
876

    
877
/* return all PTR zones for a IPv6 network */
878
function get_v6_ptr_zones($subnet, $bits) {
879
	$result = array();
880

    
881
	if (!is_ipaddrv6($subnet)) {
882
		return $result;
883
	}
884

    
885
	if (!is_numericint($bits) || $bits > 128) {
886
		return $result;
887
	}
888

    
889
	/*
890
	 * Find a small nibble boundary subnet mask
891
	 * e.g. a /29 will create 8 /32 PTR zones
892
	 */
893
	$small_sn = $bits;
894
	while ($small_sn % 4 != 0) {
895
		$small_sn++;
896
	}
897

    
898
	/* Get network prefix */
899
	$small_subnet = Net_IPv6::getNetmask($subnet, $bits);
900

    
901
	/*
902
	 * While small network is part of bigger one, increase 4-bit in last
903
	 * digit to get next small network
904
	 */
905
	while (Net_IPv6::isInNetmask($small_subnet, $subnet, $bits)) {
906
		/* Get a pure hex value */
907
		$unpacked = unpack('H*hex', inet_pton($small_subnet));
908
		/* Create PTR record using $small_sn / 4 chars */
909
		$result[] = implode('.', array_reverse(str_split(substr(
910
		    $unpacked['hex'], 0, $small_sn / 4)))).'.ip6.arpa';
911

    
912
		/* Detect what part of IP should be increased */
913
		$change_part = (int) ($small_sn / 16);
914
		if ($small_sn % 16 == 0) {
915
			$change_part--;
916
		}
917

    
918
		/* Increase 1 to desired part */
919
		$parts = explode(":", Net_IPv6::uncompress($small_subnet));
920
		$parts[$change_part]++;
921
		$small_subnet = implode(":", $parts);
922
	}
923

    
924
	return $result;
925
}
926

    
927
/* return true if $addr is in $subnet, false if not */
928
function ip_in_subnet($addr, $subnet) {
929
	if (is_ipaddrv6($addr) && is_subnetv6($subnet)) {
930
		return (Net_IPv6::isInNetmask($addr, $subnet));
931
	} else if (is_ipaddrv4($addr) && is_subnetv4($subnet)) {
932
		list($ip, $mask) = explode('/', $subnet);
933
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
934
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
935
	}
936
	return false;
937
}
938

    
939
/* returns true if $hostname is just a valid hostname (top part without any of the domain part) */
940
function is_unqualified_hostname($hostname) {
941
	if (!is_string($hostname)) {
942
		return false;
943
	}
944

    
945
	if (preg_match('/^(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname)) {
946
		return true;
947
	} else {
948
		return false;
949
	}
950
}
951

    
952
/* returns true if $hostname is a valid hostname, with or without being a fully-qualified domain name. */
953
function is_hostname($hostname, $allow_wildcard=false) {
954
	if (!is_string($hostname)) {
955
		return false;
956
	}
957

    
958
	if (is_domain($hostname, $allow_wildcard)) {
959
		if ((substr_count($hostname, ".") == 1) && ($hostname[strlen($hostname)-1] == ".")) {
960
			/* Only a single dot at the end like "test." - hosts cannot be directly in the root domain. */
961
			return false;
962
		} else {
963
			return true;
964
		}
965
	} else {
966
		return false;
967
	}
968
}
969

    
970
/* returns true if $domain is a valid domain name */
971
function is_domain($domain, $allow_wildcard=false) {
972
	if (!is_string($domain)) {
973
		return false;
974
	}
975
	if ($allow_wildcard) {
976
		$domain_regex = '/^(?:(?:[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';
977
	} else {
978
		$domain_regex = '/^(?:(?:[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';
979
	}
980

    
981
	if (preg_match($domain_regex, $domain)) {
982
		return true;
983
	} else {
984
		return false;
985
	}
986
}
987

    
988
/* returns true if $macaddr is a valid MAC address */
989
function is_macaddr($macaddr, $partial=false) {
990
	$values = explode(":", $macaddr);
991

    
992
	/* Verify if the MAC address has a proper amount of parts for either a partial or full match. */
993
	if ($partial) {
994
		if ((count($values) < 1) || (count($values) > 6)) {
995
			return false;
996
		}
997
	} elseif (count($values) != 6) {
998
		return false;
999
	}
1000
	for ($i = 0; $i < count($values); $i++) {
1001
		if (ctype_xdigit($values[$i]) == false)
1002
			return false;
1003
		if (hexdec($values[$i]) < 0 || hexdec($values[$i]) > 255)
1004
			return false;
1005
	}
1006

    
1007
	return true;
1008
}
1009

    
1010
/*
1011
	If $return_message is true then
1012
		returns a text message about the reason that the name is invalid.
1013
		the text includes the type of "thing" that is being checked, passed in $object. (e.g. "alias", "gateway group", "schedule")
1014
	else
1015
		returns true if $name is a valid name for an alias
1016
		returns false if $name is not a valid name for an alias
1017

    
1018
	Aliases cannot be:
1019
		bad chars: anything except a-z 0-9 and underscore
1020
		bad names: empty string, pure numeric, pure underscore
1021
		reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and  "pass" */
1022

    
1023
function is_validaliasname($name, $return_message = false, $object = "alias") {
1024
	/* Array of reserved words */
1025
	$reserved = array("port", "pass");
1026

    
1027
	if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) {
1028
		if ($return_message) {
1029
			return sprintf(gettext('The %1$s name must be less than 32 characters long, may not consist of only numbers, may not consist of only underscores, and may only contain the following characters: %2$s'), $object, 'a-z, A-Z, 0-9, _');
1030
		} else {
1031
			return false;
1032
		}
1033
	}
1034
	if (in_array($name, $reserved, true)) {
1035
		if ($return_message) {
1036
			return sprintf(gettext('The %1$s name must not be either of the reserved words %2$s or %3$s.'), $object, "'port'", "'pass'");
1037
		} else {
1038
			return false;
1039
		}
1040
	}
1041
	if (getprotobyname($name)) {
1042
		if ($return_message) {
1043
			return sprintf(gettext('The %1$s name must not be a well-known IP protocol name such as TCP, UDP, ICMP etc.'), $object);
1044
		} else {
1045
			return false;
1046
		}
1047
	}
1048
	if (getservbyname($name, "tcp") || getservbyname($name, "udp")) {
1049
		if ($return_message) {
1050
			return sprintf(gettext('The %1$s name must not be a well-known TCP or UDP port name such as ssh, smtp, pop3, tftp, http, openvpn etc.'), $object);
1051
		} else {
1052
			return false;
1053
		}
1054
	}
1055
	if ($return_message) {
1056
		return sprintf(gettext("The %1$s name is valid."), $object);
1057
	} else {
1058
		return true;
1059
	}
1060
}
1061

    
1062
/* returns a text message indicating if the alias name is valid, or the reason it is not valid. */
1063
function invalidaliasnamemsg($name, $object = "alias") {
1064
	return is_validaliasname($name, true, $object);
1065
}
1066

    
1067
/* returns true if $port is a valid TCP/UDP port */
1068
function is_port($port) {
1069
	if (ctype_digit($port) && ((intval($port) >= 1) && (intval($port) <= 65535))) {
1070
		return true;
1071
	}
1072
	if (getservbyname($port, "tcp") || getservbyname($port, "udp")) {
1073
		return true;
1074
	}
1075
	return false;
1076
}
1077

    
1078
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
1079
function is_portrange($portrange) {
1080
	$ports = explode(":", $portrange);
1081

    
1082
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
1083
}
1084

    
1085
/* returns true if $port is a valid port number or an alias thereof */
1086
function is_portoralias($port) {
1087
	global $config;
1088

    
1089
	if (is_alias($port)) {
1090
		if (is_array($config['aliases']['alias'])) {
1091
			foreach ($config['aliases']['alias'] as $alias) {
1092
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type'])) {
1093
					return true;
1094
				}
1095
			}
1096
		}
1097
		return false;
1098
	} else {
1099
		return is_port($port);
1100
	}
1101
}
1102

    
1103
/* create ranges of sequential port numbers (200:215) and remove duplicates */
1104
function group_ports($ports, $kflc = false) {
1105
	if (!is_array($ports) || empty($ports)) {
1106
		return;
1107
	}
1108

    
1109
	$uniq = array();
1110
	$comments = array();
1111
	foreach ($ports as $port) {
1112
		if (($kflc) && (strpos($port, '#') === 0)) {	// Keep Full Line Comments (lines beginning with #).
1113
			$comments[] = $port;
1114
		} else if (is_portrange($port)) {
1115
			list($begin, $end) = explode(":", $port);
1116
			if ($begin > $end) {
1117
				$aux = $begin;
1118
				$begin = $end;
1119
				$end = $aux;
1120
			}
1121
			for ($i = $begin; $i <= $end; $i++) {
1122
				if (!in_array($i, $uniq)) {
1123
					$uniq[] = $i;
1124
				}
1125
			}
1126
		} else if (is_port($port)) {
1127
			if (!in_array($port, $uniq)) {
1128
				$uniq[] = $port;
1129
			}
1130
		}
1131
	}
1132
	sort($uniq, SORT_NUMERIC);
1133

    
1134
	$result = array();
1135
	foreach ($uniq as $idx => $port) {
1136
		if ($idx == 0) {
1137
			$result[] = $port;
1138
			continue;
1139
		}
1140

    
1141
		$last = end($result);
1142
		if (is_portrange($last)) {
1143
			list($begin, $end) = explode(":", $last);
1144
		} else {
1145
			$begin = $end = $last;
1146
		}
1147

    
1148
		if ($port == ($end+1)) {
1149
			$end++;
1150
			$result[count($result)-1] = "{$begin}:{$end}";
1151
		} else {
1152
			$result[] = $port;
1153
		}
1154
	}
1155

    
1156
	return array_merge($comments, $result);
1157
}
1158

    
1159
/* returns true if $val is a valid shaper bandwidth value */
1160
function is_valid_shaperbw($val) {
1161
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
1162
}
1163

    
1164
/* returns true if $test is in the range between $start and $end */
1165
function is_inrange_v4($test, $start, $end) {
1166
	if (!is_ipaddrv4($test) || !is_ipaddrv4($start) || !is_ipaddrv4($end)) {
1167
		return false;
1168
	}
1169

    
1170
	if (ip2ulong($test) <= ip2ulong($end) &&
1171
	    ip2ulong($test) >= ip2ulong($start)) {
1172
		return true;
1173
	}
1174

    
1175
	return false;
1176
}
1177

    
1178
/* returns true if $test is in the range between $start and $end */
1179
function is_inrange_v6($test, $start, $end) {
1180
	if (!is_ipaddrv6($test) || !is_ipaddrv6($start) || !is_ipaddrv6($end)) {
1181
		return false;
1182
	}
1183

    
1184
	if (inet_pton($test) <= inet_pton($end) &&
1185
	    inet_pton($test) >= inet_pton($start)) {
1186
		return true;
1187
	}
1188

    
1189
	return false;
1190
}
1191

    
1192
/* returns true if $test is in the range between $start and $end */
1193
function is_inrange($test, $start, $end) {
1194
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
1195
}
1196

    
1197
function get_configured_vip_list($family = 'all', $type = VIP_ALL) {
1198
	global $config;
1199

    
1200
	$list = array();
1201
	if (!is_array($config['virtualip']['vip']) || empty($config['virtualip']['vip'])) {
1202
		return ($list);
1203
	}
1204

    
1205
	$viparr = &$config['virtualip']['vip'];
1206
	foreach ($viparr as $vip) {
1207

    
1208
		if ($type == VIP_CARP) {
1209
			if ($vip['mode'] != "carp")
1210
				continue;
1211
		} elseif ($type == VIP_IPALIAS) {
1212
			if ($vip['mode'] != "ipalias")
1213
				continue;
1214
		} else {
1215
			if ($vip['mode'] != "carp" && $vip['mode'] != "ipalias")
1216
				continue;
1217
		}
1218

    
1219
		if ($family == 'all' ||
1220
		    ($family == 'inet' && is_ipaddrv4($vip['subnet'])) ||
1221
		    ($family == 'inet6' && is_ipaddrv6($vip['subnet']))) {
1222
			$list["_vip{$vip['uniqid']}"] = $vip['subnet'];
1223
		}
1224
	}
1225
	return ($list);
1226
}
1227

    
1228
function get_configured_vip($vipinterface = '') {
1229

    
1230
	return (get_configured_vip_detail($vipinterface, 'all', 'vip'));
1231
}
1232

    
1233
function get_configured_vip_interface($vipinterface = '') {
1234

    
1235
	return (get_configured_vip_detail($vipinterface, 'all', 'iface'));
1236
}
1237

    
1238
function get_configured_vip_ipv4($vipinterface = '') {
1239

    
1240
	return (get_configured_vip_detail($vipinterface, 'inet', 'ip'));
1241
}
1242

    
1243
function get_configured_vip_ipv6($vipinterface = '') {
1244

    
1245
	return (get_configured_vip_detail($vipinterface, 'inet6', 'ip'));
1246
}
1247

    
1248
function get_configured_vip_subnetv4($vipinterface = '') {
1249

    
1250
	return (get_configured_vip_detail($vipinterface, 'inet', 'subnet'));
1251
}
1252

    
1253
function get_configured_vip_subnetv6($vipinterface = '') {
1254

    
1255
	return (get_configured_vip_detail($vipinterface, 'inet6', 'subnet'));
1256
}
1257

    
1258
function get_configured_vip_detail($vipinterface = '', $family = 'inet', $what = 'ip') {
1259
	global $config;
1260

    
1261
	if (empty($vipinterface) || !is_array($config['virtualip']['vip']) ||
1262
	    empty($config['virtualip']['vip'])) {
1263
		return (NULL);
1264
	}
1265

    
1266
	$viparr = &$config['virtualip']['vip'];
1267
	foreach ($viparr as $vip) {
1268
		if ($vip['mode'] != "carp" && $vip['mode'] != "ipalias") {
1269
			continue;
1270
		}
1271

    
1272
		if ($vipinterface != "_vip{$vip['uniqid']}") {
1273
			continue;
1274
		}
1275

    
1276
		switch ($what) {
1277
			case 'subnet':
1278
				if ($family == 'inet' && is_ipaddrv4($vip['subnet']))
1279
					return ($vip['subnet_bits']);
1280
				else if ($family == 'inet6' && is_ipaddrv6($vip['subnet']))
1281
					return ($vip['subnet_bits']);
1282
				break;
1283
			case 'iface':
1284
				return ($vip['interface']);
1285
				break;
1286
			case 'vip':
1287
				return ($vip);
1288
				break;
1289
			case 'ip':
1290
			default:
1291
				if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) {
1292
					return ($vip['subnet']);
1293
				} else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) {
1294
					return ($vip['subnet']);
1295
				}
1296
				break;
1297
		}
1298
		break;
1299
	}
1300

    
1301
	return (NULL);
1302
}
1303

    
1304
/* comparison function for sorting by the order in which interfaces are normally created */
1305
function compare_interface_friendly_names($a, $b) {
1306
	if ($a == $b) {
1307
		return 0;
1308
	} else if ($a == 'wan') {
1309
		return -1;
1310
	} else if ($b == 'wan') {
1311
		return 1;
1312
	} else if ($a == 'lan') {
1313
		return -1;
1314
	} else if ($b == 'lan') {
1315
		return 1;
1316
	}
1317

    
1318
	return strnatcmp($a, $b);
1319
}
1320

    
1321
/* return the configured interfaces list. */
1322
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
1323
	global $config;
1324

    
1325
	$iflist = array();
1326

    
1327
	/* if list */
1328
	foreach ($config['interfaces'] as $if => $ifdetail) {
1329
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1330
			continue;
1331
		}
1332
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1333
			$iflist[$if] = $if;
1334
		}
1335
	}
1336

    
1337
	return $iflist;
1338
}
1339

    
1340
/* return the configured interfaces list. */
1341
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
1342
	global $config;
1343

    
1344
	$iflist = array();
1345

    
1346
	/* if list */
1347
	foreach ($config['interfaces'] as $if => $ifdetail) {
1348
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1349
			continue;
1350
		}
1351
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1352
			$tmpif = get_real_interface($if);
1353
			if (!empty($tmpif)) {
1354
				$iflist[$tmpif] = $if;
1355
			}
1356
		}
1357
	}
1358

    
1359
	return $iflist;
1360
}
1361

    
1362
/* return the configured interfaces list with their description. */
1363
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
1364
	global $config;
1365

    
1366
	$iflist = array();
1367

    
1368
	/* if list */
1369
	foreach ($config['interfaces'] as $if => $ifdetail) {
1370
		if ($only_opt && ($if == "wan" || $if == "lan")) {
1371
			continue;
1372
		}
1373
		if (isset($ifdetail['enable']) || $withdisabled == true) {
1374
			if (empty($ifdetail['descr'])) {
1375
				$iflist[$if] = strtoupper($if);
1376
			} else {
1377
				$iflist[$if] = strtoupper($ifdetail['descr']);
1378
			}
1379
		}
1380
	}
1381

    
1382
	return $iflist;
1383
}
1384

    
1385
/*
1386
 *   get_configured_ip_addresses() - Return a list of all configured
1387
 *   IPv4 addresses.
1388
 *
1389
 */
1390
function get_configured_ip_addresses() {
1391
	global $config;
1392

    
1393
	if (!function_exists('get_interface_ip')) {
1394
		require_once("interfaces.inc");
1395
	}
1396
	$ip_array = array();
1397
	$interfaces = get_configured_interface_list();
1398
	if (is_array($interfaces)) {
1399
		foreach ($interfaces as $int) {
1400
			$ipaddr = get_interface_ip($int);
1401
			$ip_array[$int] = $ipaddr;
1402
		}
1403
	}
1404
	$interfaces = get_configured_vip_list('inet');
1405
	if (is_array($interfaces)) {
1406
		foreach ($interfaces as $int => $ipaddr) {
1407
			$ip_array[$int] = $ipaddr;
1408
		}
1409
	}
1410

    
1411
	/* pppoe server */
1412
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
1413
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
1414
			if ($pppoe['mode'] == "server") {
1415
				if (is_ipaddr($pppoe['localip'])) {
1416
					$int = "pppoes". $pppoe['pppoeid'];
1417
					$ip_array[$int] = $pppoe['localip'];
1418
				}
1419
			}
1420
		}
1421
	}
1422

    
1423
	return $ip_array;
1424
}
1425

    
1426
/*
1427
 *   get_configured_ipv6_addresses() - Return a list of all configured
1428
 *   IPv6 addresses.
1429
 *
1430
 */
1431
function get_configured_ipv6_addresses($linklocal_fallback = false) {
1432
	require_once("interfaces.inc");
1433
	$ipv6_array = array();
1434
	$interfaces = get_configured_interface_list();
1435
	if (is_array($interfaces)) {
1436
		foreach ($interfaces as $int) {
1437
			$ipaddrv6 = get_interface_ipv6($int, false, $linklocal_fallback);
1438
			$ipv6_array[$int] = $ipaddrv6;
1439
		}
1440
	}
1441
	$interfaces = get_configured_vip_list('inet6');
1442
	if (is_array($interfaces)) {
1443
		foreach ($interfaces as $int => $ipaddrv6) {
1444
			$ipv6_array[$int] = $ipaddrv6;
1445
		}
1446
	}
1447
	return $ipv6_array;
1448
}
1449

    
1450
/*
1451
 *   get_interface_list() - Return a list of all physical interfaces
1452
 *   along with MAC and status.
1453
 *
1454
 *   $mode = "active" - use ifconfig -lu
1455
 *           "media"  - use ifconfig to check physical connection
1456
 *			status (much slower)
1457
 */
1458
function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") {
1459
	global $config;
1460
	$upints = array();
1461
	/* get a list of virtual interface types */
1462
	if (!$vfaces) {
1463
		$vfaces = array(
1464
				'bridge',
1465
				'ppp',
1466
				'pppoe',
1467
				'pptp',
1468
				'l2tp',
1469
				'sl',
1470
				'gif',
1471
				'gre',
1472
				'faith',
1473
				'lo',
1474
				'ng',
1475
				'_vlan',
1476
				'_wlan',
1477
				'pflog',
1478
				'plip',
1479
				'pfsync',
1480
				'enc',
1481
				'tun',
1482
				'lagg',
1483
				'vip',
1484
				'ipfw'
1485
		);
1486
	}
1487
	switch ($mode) {
1488
		case "active":
1489
			$upints = pfSense_interface_listget(IFF_UP);
1490
			break;
1491
		case "media":
1492
			$intlist = pfSense_interface_listget();
1493
			$ifconfig = "";
1494
			exec("/sbin/ifconfig -a", $ifconfig);
1495
			$regexp = '/(' . implode('|', $intlist) . '):\s/';
1496
			$ifstatus = preg_grep('/status:/', $ifconfig);
1497
			foreach ($ifstatus as $status) {
1498
				$int = array_shift($intlist);
1499
				if (stristr($status, "active")) {
1500
					$upints[] = $int;
1501
				}
1502
			}
1503
			break;
1504
		default:
1505
			$upints = pfSense_interface_listget();
1506
			break;
1507
	}
1508
	/* build interface list with netstat */
1509
	$linkinfo = "";
1510
	exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo);
1511
	array_shift($linkinfo);
1512
	/* build ip address list with netstat */
1513
	$ipinfo = "";
1514
	exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo);
1515
	array_shift($ipinfo);
1516
	foreach ($linkinfo as $link) {
1517
		$friendly = "";
1518
		$alink = explode(" ", $link);
1519
		$ifname = rtrim(trim($alink[0]), '*');
1520
		/* trim out all numbers before checking for vfaces */
1521
		if (!in_array(array_shift(preg_split('/\d/', $ifname)), $vfaces) &&
1522
		    !stristr($ifname, "_vlan") && !stristr($ifname, "_wlan")) {
1523
			$toput = array(
1524
					"mac" => trim($alink[1]),
1525
					"up" => in_array($ifname, $upints)
1526
				);
1527
			foreach ($ipinfo as $ip) {
1528
				$aip = explode(" ", $ip);
1529
				if ($aip[0] == $ifname) {
1530
					$toput['ipaddr'] = $aip[1];
1531
				}
1532
			}
1533
			if (is_array($config['interfaces'])) {
1534
				foreach ($config['interfaces'] as $name => $int) {
1535
					if ($int['if'] == $ifname) {
1536
						$friendly = $name;
1537
					}
1538
				}
1539
			}
1540
			switch ($keyby) {
1541
			case "physical":
1542
				if ($friendly != "") {
1543
					$toput['friendly'] = $friendly;
1544
				}
1545
				$dmesg_arr = array();
1546
				exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr);
1547
				preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg);
1548
				$toput['dmesg'] = $dmesg[1][0];
1549
				$iflist[$ifname] = $toput;
1550
				break;
1551
			case "ppp":
1552

    
1553
			case "friendly":
1554
				if ($friendly != "") {
1555
					$toput['if'] = $ifname;
1556
					$iflist[$friendly] = $toput;
1557
				}
1558
				break;
1559
			}
1560
		}
1561
	}
1562
	return $iflist;
1563
}
1564

    
1565
/****f* util/log_error
1566
* NAME
1567
*   log_error  - Sends a string to syslog.
1568
* INPUTS
1569
*   $error     - string containing the syslog message.
1570
* RESULT
1571
*   null
1572
******/
1573
function log_error($error) {
1574
	global $g;
1575
	$page = $_SERVER['SCRIPT_NAME'];
1576
	if (empty($page)) {
1577
		$files = get_included_files();
1578
		$page = basename($files[0]);
1579
	}
1580
	syslog(LOG_ERR, "$page: $error");
1581
	if ($g['debug']) {
1582
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1583
	}
1584
	return;
1585
}
1586

    
1587
/****f* util/log_auth
1588
* NAME
1589
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1590
* INPUTS
1591
*   $error     - string containing the syslog message.
1592
* RESULT
1593
*   null
1594
******/
1595
function log_auth($error) {
1596
	global $g;
1597
	$page = $_SERVER['SCRIPT_NAME'];
1598
	syslog(LOG_AUTH, "$page: $error");
1599
	if ($g['debug']) {
1600
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1601
	}
1602
	return;
1603
}
1604

    
1605
/****f* util/exec_command
1606
 * NAME
1607
 *   exec_command - Execute a command and return a string of the result.
1608
 * INPUTS
1609
 *   $command   - String of the command to be executed.
1610
 * RESULT
1611
 *   String containing the command's result.
1612
 * NOTES
1613
 *   This function returns the command's stdout and stderr.
1614
 ******/
1615
function exec_command($command) {
1616
	$output = array();
1617
	exec($command . ' 2>&1', $output);
1618
	return(implode("\n", $output));
1619
}
1620

    
1621
/* wrapper for exec()
1622
   Executes in background or foreground.
1623
   For background execution, returns PID of background process to allow calling code control */
1624
function mwexec($command, $nologentry = false, $clearsigmask = false, $background = false) {
1625
	global $g;
1626
	$retval = 0;
1627

    
1628
	if ($g['debug']) {
1629
		if (!$_SERVER['REMOTE_ADDR']) {
1630
			echo "mwexec(): $command" . ($background ? " [BG]":"") . "\n";
1631
		}
1632
	}
1633
	if ($clearsigmask) {
1634
		$oldset = array();
1635
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1636
	}
1637

    
1638
	if ($background) {
1639
		// start background process and return PID
1640
		$retval = exec("/usr/bin/nohup $command > /dev/null 2>&1 & echo $!");
1641
	} else {
1642
		// run in foreground, and (optionally) log if nonzero return
1643
		$outputarray = array();
1644
		exec("$command 2>&1", $outputarray, $retval);
1645
		if (($retval <> 0) && (!$nologentry || isset($config['system']['developerspew']))) {
1646
			log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, implode(" ", $outputarray)));
1647
		}
1648
	}
1649

    
1650
	if ($clearsigmask) {
1651
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1652
	}
1653

    
1654
	return $retval;
1655
}
1656

    
1657
/* wrapper for exec() in background */
1658
function mwexec_bg($command, $clearsigmask = false) {
1659
	return mwexec($command, false, $clearsigmask, true);
1660
}
1661

    
1662
/*	unlink a file, or pattern-match of a file, if it exists
1663
	if the file/path contains glob() compatible wildcards, all matching files will be unlinked
1664
	any warning/errors are suppressed (e.g. no matching files to delete)
1665
	If there are matching file(s) and they were all unlinked OK, then return true.
1666
	Otherwise return false (the requested file(s) did not exist, or could not be deleted)
1667
	This allows the caller to know if they were the one to successfully delete the file(s).
1668
*/
1669
function unlink_if_exists($fn) {
1670
	$to_do = glob($fn);
1671
	if (is_array($to_do) && count($to_do) > 0) {
1672
		// Returns an array of true/false indicating if each unlink worked
1673
		$results = @array_map("unlink", $to_do);
1674
		// If there is no false in the array, then all went well
1675
		$result = !in_array(false, $results, true);
1676
	} else {
1677
		$result = @unlink($fn);
1678
	}
1679
	return $result;
1680
}
1681
/* make a global alias table (for faster lookups) */
1682
function alias_make_table($config) {
1683
	global $aliastable;
1684

    
1685
	$aliastable = array();
1686

    
1687
	if (is_array($config['aliases']['alias'])) {
1688
		foreach ($config['aliases']['alias'] as $alias) {
1689
			if ($alias['name']) {
1690
				$aliastable[$alias['name']] = $alias['address'];
1691
			}
1692
		}
1693
	}
1694
}
1695

    
1696
/* check if an alias exists */
1697
function is_alias($name) {
1698
	global $aliastable;
1699

    
1700
	return isset($aliastable[$name]);
1701
}
1702

    
1703
function alias_get_type($name) {
1704
	global $config;
1705

    
1706
	if (is_array($config['aliases']['alias'])) {
1707
		foreach ($config['aliases']['alias'] as $alias) {
1708
			if ($name == $alias['name']) {
1709
				return $alias['type'];
1710
			}
1711
		}
1712
	}
1713

    
1714
	return "";
1715
}
1716

    
1717
/* expand a host or network alias, if necessary */
1718
function alias_expand($name) {
1719
	global $config, $aliastable;
1720
	$urltable_prefix = "/var/db/aliastables/";
1721
	$urltable_filename = $urltable_prefix . $name . ".txt";
1722

    
1723
	if (isset($aliastable[$name])) {
1724
		// alias names cannot be strictly numeric. redmine #4289
1725
		if (is_numericint($name)) {
1726
			return null;
1727
		}
1728
		// make sure if it's a ports alias, it actually exists. redmine #5845
1729
		foreach ($config['aliases']['alias'] as $alias) {
1730
			if ($alias['name'] == $name) {
1731
				if ($alias['type'] == "urltable_ports") {
1732
					if (is_URL($alias['url']) && file_exists($urltable_filename) && filesize($urltable_filename)) {
1733
						return "\${$name}";
1734
					} else {
1735
						return null;
1736
					}
1737
				}
1738
			}
1739
		}
1740
		return "\${$name}";
1741
	} else if (is_ipaddr($name) || is_subnet($name) || is_port($name) || is_portrange($name)) {
1742
		return "{$name}";
1743
	} else {
1744
		return null;
1745
	}
1746
}
1747

    
1748
function alias_expand_urltable($name) {
1749
	global $config;
1750
	$urltable_prefix = "/var/db/aliastables/";
1751
	$urltable_filename = $urltable_prefix . $name . ".txt";
1752

    
1753
	if (is_array($config['aliases']['alias'])) {
1754
		foreach ($config['aliases']['alias'] as $alias) {
1755
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1756
				if (is_URL($alias["url"]) && file_exists($urltable_filename)) {
1757
					if (!filesize($urltable_filename)) {
1758
						// file exists, but is empty, try to sync
1759
						send_event("service sync alias {$name}");
1760
					}
1761
					return $urltable_filename;
1762
				} else {
1763
					send_event("service sync alias {$name}");
1764
					break;
1765
				}
1766
			}
1767
		}
1768
	}
1769
	return null;
1770
}
1771

    
1772
/* obtain MAC address given an IP address by looking at the ARP table */
1773
function arp_get_mac_by_ip($ip) {
1774
	mwexec("/sbin/ping -c 1 -t 1 " . escapeshellarg($ip), true);
1775
	$arpoutput = "";
1776
	exec("/usr/sbin/arp -n " . escapeshellarg($ip), $arpoutput);
1777

    
1778
	if ($arpoutput[0]) {
1779
		$arpi = explode(" ", $arpoutput[0]);
1780
		$macaddr = $arpi[3];
1781
		if (is_macaddr($macaddr)) {
1782
			return $macaddr;
1783
		} else {
1784
			return false;
1785
		}
1786
	}
1787

    
1788
	return false;
1789
}
1790

    
1791
/* return a fieldname that is safe for xml usage */
1792
function xml_safe_fieldname($fieldname) {
1793
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1794
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1795
			 ':', ',', '.', '\'', '\\'
1796
		);
1797
	return strtolower(str_replace($replace, "", $fieldname));
1798
}
1799

    
1800
function mac_format($clientmac) {
1801
	global $config, $cpzone;
1802

    
1803
	$mac = explode(":", $clientmac);
1804
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1805

    
1806
	switch ($mac_format) {
1807
		case 'singledash':
1808
			return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1809

    
1810
		case 'ietf':
1811
			return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1812

    
1813
		case 'cisco':
1814
			return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1815

    
1816
		case 'unformatted':
1817
			return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1818

    
1819
		default:
1820
			return $clientmac;
1821
	}
1822
}
1823

    
1824
function resolve_retry($hostname, $retries = 5) {
1825

    
1826
	if (is_ipaddr($hostname)) {
1827
		return $hostname;
1828
	}
1829

    
1830
	for ($i = 0; $i < $retries; $i++) {
1831
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1832
		$ip = gethostbyname($hostname);
1833

    
1834
		if ($ip && $ip != $hostname) {
1835
			/* success */
1836
			return $ip;
1837
		}
1838

    
1839
		sleep(1);
1840
	}
1841

    
1842
	return false;
1843
}
1844

    
1845
function format_bytes($bytes) {
1846
	if ($bytes >= 1099511627776) {
1847
		return sprintf("%.2f TiB", $bytes/1099511627776);
1848
	} else if ($bytes >= 1073741824) {
1849
		return sprintf("%.2f GiB", $bytes/1073741824);
1850
	} else if ($bytes >= 1048576) {
1851
		return sprintf("%.2f MiB", $bytes/1048576);
1852
	} else if ($bytes >= 1024) {
1853
		return sprintf("%.0f KiB", $bytes/1024);
1854
	} else {
1855
		return sprintf("%d B", $bytes);
1856
	}
1857
}
1858

    
1859
function format_number($num, $precision = 3) {
1860
	$units = array('', 'K', 'M', 'G', 'T');
1861

    
1862
	$i = 0;
1863
	while ($num > 1000 && $i < count($units)) {
1864
		$num /= 1000;
1865
		$i++;
1866
	}
1867
	$num = round($num, $precision);
1868

    
1869
	return ("$num {$units[$i]}");
1870
}
1871

    
1872
function update_filter_reload_status($text, $new=false) {
1873
	global $g;
1874

    
1875
	if ($new) {
1876
		file_put_contents("{$g['varrun_path']}/filter_reload_status", $text  . PHP_EOL);
1877
	} else {
1878
		file_put_contents("{$g['varrun_path']}/filter_reload_status", $text  . PHP_EOL, FILE_APPEND);
1879
	}
1880
}
1881

    
1882
/****** util/return_dir_as_array
1883
 * NAME
1884
 *   return_dir_as_array - Return a directory's contents as an array.
1885
 * INPUTS
1886
 *   $dir          - string containing the path to the desired directory.
1887
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1888
 * RESULT
1889
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1890
 ******/
1891
function return_dir_as_array($dir, $filter_regex = '') {
1892
	$dir_array = array();
1893
	if (is_dir($dir)) {
1894
		if ($dh = opendir($dir)) {
1895
			while (($file = readdir($dh)) !== false) {
1896
				if (($file == ".") || ($file == "..")) {
1897
					continue;
1898
				}
1899

    
1900
				if (empty($filter_regex) || preg_match($filter_regex, $file)) {
1901
					array_push($dir_array, $file);
1902
				}
1903
			}
1904
			closedir($dh);
1905
		}
1906
	}
1907
	return $dir_array;
1908
}
1909

    
1910
function run_plugins($directory) {
1911
	global $config, $g;
1912

    
1913
	/* process packager manager custom rules */
1914
	$files = return_dir_as_array($directory);
1915
	if (is_array($files)) {
1916
		foreach ($files as $file) {
1917
			if (stristr($file, ".sh") == true) {
1918
				mwexec($directory . $file . " start");
1919
			} else if (!is_dir($directory . "/" . $file) && stristr($file, ".inc")) {
1920
				require_once($directory . "/" . $file);
1921
			}
1922
		}
1923
	}
1924
}
1925

    
1926
/*
1927
 *    safe_mkdir($path, $mode = 0755)
1928
 *    create directory if it doesn't already exist and isn't a file!
1929
 */
1930
function safe_mkdir($path, $mode = 0755) {
1931
	global $g;
1932

    
1933
	if (!is_file($path) && !is_dir($path)) {
1934
		return @mkdir($path, $mode, true);
1935
	} else {
1936
		return false;
1937
	}
1938
}
1939

    
1940
/*
1941
 * get_sysctl($names)
1942
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1943
 * name) and return an array of key/value pairs set for those that exist
1944
 */
1945
function get_sysctl($names) {
1946
	if (empty($names)) {
1947
		return array();
1948
	}
1949

    
1950
	if (is_array($names)) {
1951
		$name_list = array();
1952
		foreach ($names as $name) {
1953
			$name_list[] = escapeshellarg($name);
1954
		}
1955
	} else {
1956
		$name_list = array(escapeshellarg($names));
1957
	}
1958

    
1959
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1960
	$values = array();
1961
	foreach ($output as $line) {
1962
		$line = explode(": ", $line, 2);
1963
		if (count($line) == 2) {
1964
			$values[$line[0]] = $line[1];
1965
		}
1966
	}
1967

    
1968
	return $values;
1969
}
1970

    
1971
/*
1972
 * get_single_sysctl($name)
1973
 * Wrapper for get_sysctl() to simplify read of a single sysctl value
1974
 * return the value for sysctl $name or empty string if it doesn't exist
1975
 */
1976
function get_single_sysctl($name) {
1977
	if (empty($name)) {
1978
		return "";
1979
	}
1980

    
1981
	$value = get_sysctl($name);
1982
	if (empty($value) || !isset($value[$name])) {
1983
		return "";
1984
	}
1985

    
1986
	return $value[$name];
1987
}
1988

    
1989
/*
1990
 * set_sysctl($value_list)
1991
 * Set sysctl OID's listed as key/value pairs and return
1992
 * an array with keys set for those that succeeded
1993
 */
1994
function set_sysctl($values) {
1995
	if (empty($values)) {
1996
		return array();
1997
	}
1998

    
1999
	$value_list = array();
2000
	foreach ($values as $key => $value) {
2001
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
2002
	}
2003

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

    
2006
	/* Retry individually if failed (one or more read-only) */
2007
	if ($success <> 0 && count($value_list) > 1) {
2008
		foreach ($value_list as $value) {
2009
			exec("/sbin/sysctl -i " . $value, $output);
2010
		}
2011
	}
2012

    
2013
	$ret = array();
2014
	foreach ($output as $line) {
2015
		$line = explode(": ", $line, 2);
2016
		if (count($line) == 2) {
2017
			$ret[$line[0]] = true;
2018
		}
2019
	}
2020

    
2021
	return $ret;
2022
}
2023

    
2024
/*
2025
 * set_single_sysctl($name, $value)
2026
 * Wrapper to set_sysctl() to make it simple to set only one sysctl
2027
 * returns boolean meaning if it succeeded
2028
 */
2029
function set_single_sysctl($name, $value) {
2030
	if (empty($name)) {
2031
		return false;
2032
	}
2033

    
2034
	$result = set_sysctl(array($name => $value));
2035

    
2036
	if (!isset($result[$name]) || $result[$name] != $value) {
2037
		return false;
2038
	}
2039

    
2040
	return true;
2041
}
2042

    
2043
/*
2044
 *     get_memory()
2045
 *     returns an array listing the amount of
2046
 *     memory installed in the hardware
2047
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
2048
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
2049
 */
2050
function get_memory() {
2051
	$physmem = get_single_sysctl("hw.physmem");
2052
	$realmem = get_single_sysctl("hw.realmem");
2053
	/* convert from bytes to megabytes */
2054
	return array(($physmem/1048576), ($realmem/1048576));
2055
}
2056

    
2057
function mute_kernel_msgs() {
2058
	global $g, $config;
2059

    
2060
	if ($config['system']['enableserial']) {
2061
		return;
2062
	}
2063
	exec("/sbin/conscontrol mute on");
2064
}
2065

    
2066
function unmute_kernel_msgs() {
2067
	global $g;
2068

    
2069
	exec("/sbin/conscontrol mute off");
2070
}
2071

    
2072
function start_devd() {
2073
	global $g;
2074

    
2075
	/* Use the undocumented -q options of devd to quiet its log spamming */
2076
	$_gb = exec("/sbin/devd -q -f /etc/{$g['product_name']}-devd.conf");
2077
	sleep(1);
2078
	unset($_gb);
2079
}
2080

    
2081
function is_interface_vlan_mismatch() {
2082
	global $config, $g;
2083

    
2084
	if (is_array($config['vlans']['vlan'])) {
2085
		foreach ($config['vlans']['vlan'] as $vlan) {
2086
			if (substr($vlan['if'], 0, 4) == "lagg") {
2087
				return false;
2088
			}
2089
			if (does_interface_exist($vlan['if']) == false) {
2090
				return true;
2091
			}
2092
		}
2093
	}
2094

    
2095
	return false;
2096
}
2097

    
2098
function is_interface_mismatch() {
2099
	global $config, $g;
2100

    
2101
	$do_assign = false;
2102
	$i = 0;
2103
	$missing_interfaces = array();
2104
	if (is_array($config['interfaces'])) {
2105
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
2106
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
2107
				// Do not check these interfaces.
2108
				$i++;
2109
				continue;
2110
			} else if (does_interface_exist($ifcfg['if']) == false) {
2111
				$missing_interfaces[] = $ifcfg['if'];
2112
				$do_assign = true;
2113
			} else {
2114
				$i++;
2115
			}
2116
		}
2117
	}
2118

    
2119
	if (file_exists("{$g['tmp_path']}/assign_complete")) {
2120
		$do_assign = false;
2121
	}
2122

    
2123
	if (!empty($missing_interfaces) && $do_assign) {
2124
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
2125
	} else {
2126
		@unlink("{$g['tmp_path']}/missing_interfaces");
2127
	}
2128

    
2129
	return $do_assign;
2130
}
2131

    
2132
/* sync carp entries to other firewalls */
2133
function carp_sync_client() {
2134
	global $g;
2135
	send_event("filter sync");
2136
}
2137

    
2138
/****f* util/isAjax
2139
 * NAME
2140
 *   isAjax - reports if the request is driven from prototype
2141
 * INPUTS
2142
 *   none
2143
 * RESULT
2144
 *   true/false
2145
 ******/
2146
function isAjax() {
2147
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
2148
}
2149

    
2150
/****f* util/timeout
2151
 * NAME
2152
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
2153
 * INPUTS
2154
 *   optional, seconds to wait before timeout. Default 9 seconds.
2155
 * RESULT
2156
 *   returns 1 char of user input or null if no input.
2157
 ******/
2158
function timeout($timer = 9) {
2159
	while (!isset($key)) {
2160
		if ($timer >= 9) {
2161
			echo chr(8) . chr(8) . ($timer == 9 ? chr(32) : null) . "{$timer}";
2162
		} else {
2163
			echo chr(8). "{$timer}";
2164
		}
2165
		`/bin/stty -icanon min 0 time 25`;
2166
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
2167
		`/bin/stty icanon`;
2168
		if ($key == '') {
2169
			unset($key);
2170
		}
2171
		$timer--;
2172
		if ($timer == 0) {
2173
			break;
2174
		}
2175
	}
2176
	return $key;
2177
}
2178

    
2179
/****f* util/msort
2180
 * NAME
2181
 *   msort - sort array
2182
 * INPUTS
2183
 *   $array to be sorted, field to sort by, direction of sort
2184
 * RESULT
2185
 *   returns newly sorted array
2186
 ******/
2187
function msort($array, $id = "id", $sort_ascending = true) {
2188
	$temp_array = array();
2189
	while (count($array)>0) {
2190
		$lowest_id = 0;
2191
		$index = 0;
2192
		foreach ($array as $item) {
2193
			if (isset($item[$id])) {
2194
				if ($array[$lowest_id][$id]) {
2195
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
2196
						$lowest_id = $index;
2197
					}
2198
				}
2199
			}
2200
			$index++;
2201
		}
2202
		$temp_array[] = $array[$lowest_id];
2203
		$array = array_merge(array_slice($array, 0, $lowest_id), array_slice($array, $lowest_id + 1));
2204
	}
2205
	if ($sort_ascending) {
2206
		return $temp_array;
2207
	} else {
2208
		return array_reverse($temp_array);
2209
	}
2210
}
2211

    
2212
/****f* util/is_URL
2213
 * NAME
2214
 *   is_URL
2215
 * INPUTS
2216
 *   string to check
2217
 * RESULT
2218
 *   Returns true if item is a URL
2219
 ******/
2220
function is_URL($url) {
2221
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
2222
	if ($match) {
2223
		return true;
2224
	}
2225
	return false;
2226
}
2227

    
2228
function is_file_included($file = "") {
2229
	$files = get_included_files();
2230
	if (in_array($file, $files)) {
2231
		return true;
2232
	}
2233

    
2234
	return false;
2235
}
2236

    
2237
/*
2238
 * Replace a value on a deep associative array using regex
2239
 */
2240
function array_replace_values_recursive($data, $match, $replace) {
2241
	if (empty($data)) {
2242
		return $data;
2243
	}
2244

    
2245
	if (is_string($data)) {
2246
		$data = preg_replace("/{$match}/", $replace, $data);
2247
	} else if (is_array($data)) {
2248
		foreach ($data as $k => $v) {
2249
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
2250
		}
2251
	}
2252

    
2253
	return $data;
2254
}
2255

    
2256
/*
2257
	This function was borrowed from a comment on PHP.net at the following URL:
2258
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
2259
 */
2260
function array_merge_recursive_unique($array0, $array1) {
2261

    
2262
	$arrays = func_get_args();
2263
	$remains = $arrays;
2264

    
2265
	// We walk through each arrays and put value in the results (without
2266
	// considering previous value).
2267
	$result = array();
2268

    
2269
	// loop available array
2270
	foreach ($arrays as $array) {
2271

    
2272
		// The first remaining array is $array. We are processing it. So
2273
		// we remove it from remaining arrays.
2274
		array_shift($remains);
2275

    
2276
		// We don't care non array param, like array_merge since PHP 5.0.
2277
		if (is_array($array)) {
2278
			// Loop values
2279
			foreach ($array as $key => $value) {
2280
				if (is_array($value)) {
2281
					// we gather all remaining arrays that have such key available
2282
					$args = array();
2283
					foreach ($remains as $remain) {
2284
						if (array_key_exists($key, $remain)) {
2285
							array_push($args, $remain[$key]);
2286
						}
2287
					}
2288

    
2289
					if (count($args) > 2) {
2290
						// put the recursion
2291
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
2292
					} else {
2293
						foreach ($value as $vkey => $vval) {
2294
							$result[$key][$vkey] = $vval;
2295
						}
2296
					}
2297
				} else {
2298
					// simply put the value
2299
					$result[$key] = $value;
2300
				}
2301
			}
2302
		}
2303
	}
2304
	return $result;
2305
}
2306

    
2307

    
2308
/*
2309
 * converts a string like "a,b,c,d"
2310
 * into an array like array("a" => "b", "c" => "d")
2311
 */
2312
function explode_assoc($delimiter, $string) {
2313
	$array = explode($delimiter, $string);
2314
	$result = array();
2315
	$numkeys = floor(count($array) / 2);
2316
	for ($i = 0; $i < $numkeys; $i += 1) {
2317
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
2318
	}
2319
	return $result;
2320
}
2321

    
2322
/* Try to change a static route, if it doesn't exist, add it */
2323
function route_add_or_change($args) {
2324
	global $config;
2325

    
2326
	if (empty($args)) {
2327
		return false;
2328
	}
2329

    
2330
	/* First, try to add it */
2331
	$_gb = exec(escapeshellcmd("/sbin/route add " . $args), $output, $rc);
2332

    
2333
	if (isset($config['system']['route-debug'])) {
2334
		$mt = microtime();
2335
		log_error("ROUTING debug: $mt - ADD RC={$rc} - $args");
2336
	}
2337

    
2338
	if ($rc != 0) {
2339
		/* If it fails, try to change it */
2340
		$_gb = exec(escapeshellcmd("/sbin/route change " . $args),
2341
		    $output, $rc);
2342

    
2343
		if (isset($config['system']['route-debug'])) {
2344
			$mt = microtime();
2345
			log_error("ROUTING debug: $mt - CHG RC={$rc} - $args");
2346
		}
2347
	}
2348

    
2349
	return ($rc == 0);
2350
}
2351

    
2352
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false, $returnenabledroutesonly = false) {
2353
	global $config, $aliastable;
2354

    
2355
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
2356
	if (!is_array($config['staticroutes']['route'])) {
2357
		return array();
2358
	}
2359

    
2360
	$allstaticroutes = array();
2361
	$allsubnets = array();
2362
	/* Loop through routes and expand aliases as we find them. */
2363
	foreach ($config['staticroutes']['route'] as $route) {
2364
		if ($returnenabledroutesonly && isset($route['disabled'])) {
2365
			continue;
2366
		}
2367

    
2368
		if (is_alias($route['network'])) {
2369
			if (!isset($aliastable[$route['network']])) {
2370
				continue;
2371
			}
2372

    
2373
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
2374
			foreach ($subnets as $net) {
2375
				if (!is_subnet($net)) {
2376
					if (is_ipaddrv4($net)) {
2377
						$net .= "/32";
2378
					} else if (is_ipaddrv6($net)) {
2379
						$net .= "/128";
2380
					} else if ($returnhostnames === false || !is_fqdn($net)) {
2381
						continue;
2382
					}
2383
				}
2384
				$temproute = $route;
2385
				$temproute['network'] = $net;
2386
				$allstaticroutes[] = $temproute;
2387
				$allsubnets[] = $net;
2388
			}
2389
		} elseif (is_subnet($route['network'])) {
2390
			$allstaticroutes[] = $route;
2391
			$allsubnets[] = $route['network'];
2392
		}
2393
	}
2394
	if ($returnsubnetsonly) {
2395
		return $allsubnets;
2396
	} else {
2397
		return $allstaticroutes;
2398
	}
2399
}
2400

    
2401
/****f* util/get_alias_list
2402
 * NAME
2403
 *   get_alias_list - Provide a list of aliases.
2404
 * INPUTS
2405
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
2406
 * RESULT
2407
 *   Array containing list of aliases.
2408
 *   If $type is unspecified, all aliases are returned.
2409
 *   If $type is a string, all aliases of the type specified in $type are returned.
2410
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
2411
 */
2412
function get_alias_list($type = null) {
2413
	global $config;
2414
	$result = array();
2415
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
2416
		foreach ($config['aliases']['alias'] as $alias) {
2417
			if ($type === null) {
2418
				$result[] = $alias['name'];
2419
			} else if (is_array($type)) {
2420
				if (in_array($alias['type'], $type)) {
2421
					$result[] = $alias['name'];
2422
				}
2423
			} else if ($type === $alias['type']) {
2424
				$result[] = $alias['name'];
2425
			}
2426
		}
2427
	}
2428
	return $result;
2429
}
2430

    
2431
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
2432
function array_exclude($needle, $haystack) {
2433
	$result = array();
2434
	if (is_array($haystack)) {
2435
		foreach ($haystack as $thing) {
2436
			if ($needle !== $thing) {
2437
				$result[] = $thing;
2438
			}
2439
		}
2440
	}
2441
	return $result;
2442
}
2443

    
2444
/* Define what is preferred, IPv4 or IPv6 */
2445
function prefer_ipv4_or_ipv6() {
2446
	global $config;
2447

    
2448
	if (isset($config['system']['prefer_ipv4'])) {
2449
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv4");
2450
	} else {
2451
		mwexec("/etc/rc.d/ip6addrctl prefer_ipv6");
2452
	}
2453
}
2454

    
2455
/* Redirect to page passing parameters via POST */
2456
function post_redirect($page, $params) {
2457
	if (!is_array($params)) {
2458
		return;
2459
	}
2460

    
2461
	print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n";
2462
	foreach ($params as $key => $value) {
2463
		print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n";
2464
	}
2465
	print "</form>\n";
2466
	print "<script type=\"text/javascript\">\n";
2467
	print "//<![CDATA[\n";
2468
	print "document.formredir.submit();\n";
2469
	print "//]]>\n";
2470
	print "</script>\n";
2471
	print "</body></html>\n";
2472
}
2473

    
2474
/* Locate disks that can be queried for S.M.A.R.T. data. */
2475
function get_smart_drive_list() {
2476
	$disk_list = explode(" ", get_single_sysctl("kern.disks"));
2477
	foreach ($disk_list as $id => $disk) {
2478
		// We only want certain kinds of disks for S.M.A.R.T.
2479
		// 1 is a match, 0 is no match, False is any problem processing the regex
2480
		if (preg_match("/^(ad|da|ada).*[0-9]{1,2}$/", $disk) !== 1) {
2481
			unset($disk_list[$id]);
2482
		}
2483
	}
2484
	sort($disk_list);
2485
	return $disk_list;
2486
}
2487

    
2488
// Validate a network address
2489
//	$addr: the address to validate
2490
//	$type: IPV4|IPV6|IPV4V6
2491
//	$label: the label used by the GUI to display this value. Required to compose an error message
2492
//	$err_msg: pointer to the callers error message array so that error messages can be added to it here
2493
//	$alias: are aliases permitted for this address?
2494
// Returns:
2495
//	IPV4 - if $addr is a valid IPv4 address
2496
//	IPV6 - if $addr is a valid IPv6 address
2497
//	ALIAS - if $alias=true and $addr is an alias
2498
//	false - otherwise
2499

    
2500
function validateipaddr(&$addr, $type, $label, &$err_msg, $alias=false) {
2501
	switch ($type) {
2502
		case IPV4:
2503
			if (is_ipaddrv4($addr)) {
2504
				return IPV4;
2505
			} else if ($alias) {
2506
				if (is_alias($addr)) {
2507
					return ALIAS;
2508
				} else {
2509
					$err_msg[] = sprintf(gettext("%s must be a valid IPv4 address or alias."), $label);
2510
					return false;
2511
				}
2512
			} else {
2513
				$err_msg[] = sprintf(gettext("%s must be a valid IPv4 address."), $label);
2514
				return false;
2515
			}
2516
		break;
2517
		case IPV6:
2518
			if (is_ipaddrv6($addr)) {
2519
				$addr = strtolower($addr);
2520
				return IPV6;
2521
			} else if ($alias) {
2522
				if (is_alias($addr)) {
2523
					return ALIAS;
2524
				} else {
2525
					$err_msg[] = sprintf(gettext("%s must be a valid IPv6 address or alias."), $label);
2526
					return false;
2527
				}
2528
			} else {
2529
				$err_msg[] = sprintf(gettext("%s must be a valid IPv6 address."), $label);
2530
				return false;
2531
			}
2532
		break;
2533
		case IPV4V6:
2534
			if (is_ipaddrv6($addr)) {
2535
				$addr = strtolower($addr);
2536
				return IPV6;
2537
			} else if (is_ipaddrv4($addr)) {
2538
				return IPV4;
2539
			} else if ($alias) {
2540
				if (is_alias($addr)) {
2541
					return ALIAS;
2542
				} else {
2543
					$err_msg[] = sprintf(gettext("%s must be a valid IPv4 or IPv6 address or alias."), $label);
2544
					return false;
2545
				}
2546
			} else {
2547
				$err_msg[] = sprintf(gettext("%s must be a valid IPv4 or IPv6 address."), $label);
2548
				return false;
2549
			}
2550
		break;
2551
	}
2552

    
2553
	return false;
2554
}
2555

    
2556
/* format a string to look (more) like the expected DUID format:
2557
 * 1) Replace any "-" with ":"
2558
 * 2) If the user inputs 14 components, then add the expected "0e:00:" to the front.
2559
 *    This is convenience, because the actual DUID (which is reported in logs) is the last 14 components.
2560
 * 3) If any components are input with just a single char (hex digit hopefully), put a "0" in front.
2561
 *
2562
 * The final result should be closer to:
2563
 *
2564
 * "0e:00:00:01:00:01:nn:nn:nn:nn:nn:nn:nn:nn:nn:nn"
2565
 *
2566
 * This function does not validate the input. is_duid() will do validation.
2567
*/
2568
function format_duid($dhcp6duid) {
2569
	$formatted_dhcp6duid = strtolower(str_replace("-", ":", $dhcp6duid));
2570
	$values = explode(":", $formatted_dhcp6duid);
2571
	if (count($values) == 14) {
2572
		$formatted_dhcp6duid = '0e:00:' . $formatted_dhcp6duid;
2573
		$values = explode(":", $formatted_dhcp6duid);
2574
	}
2575
	foreach ($values as $idx => $value) {
2576
		if (strlen($value) == 1) {
2577
			$values[$idx] = "0" . $value;
2578
		}
2579
	}
2580
	$formatted_dhcp6duid = implode(":", $values);
2581
	return $formatted_dhcp6duid;
2582
}
2583

    
2584
/* returns true if $dhcp6duid is a valid duid entry */
2585
function is_duid($dhcp6duid) {
2586
	$values = explode(":", $dhcp6duid);
2587
	if (count($values) != 16 || strlen($dhcp6duid) != 47) {
2588
		return false;
2589
	}
2590
	for ($i = 0; $i < 16; $i++) {
2591
		if (ctype_xdigit($values[$i]) == false)
2592
			return false;
2593
		if (hexdec($values[$i]) < 0 || hexdec($values[$i]) > 255)
2594
			return false;
2595
	}
2596
	return true;
2597
}
2598

    
2599
/* Write the DHCP6 DUID file */
2600
function write_dhcp6_duid($duidstring) {
2601
	// Create the hex array from the dhcp6duid config entry and write to file
2602
	global $g;
2603
 	
2604
 	if(!is_duid($duidstring)) {
2605
		log_error(gettext("Error: attempting to write DUID file - Invalid DUID detected"));
2606
		return false;
2607
	}
2608
	$temp = str_replace(":","",$duidstring);
2609
	$duid_binstring = pack("H*",$temp);
2610
	if ($fd = fopen("{$g['vardb_path']}/dhcp6c_duid", "wb")) {
2611
		fwrite($fd, $duid_binstring);
2612
		fclose($fd);
2613
		return true;
2614
	}
2615
	log_error(gettext("Error: attempting to write DUID file - File write error"));
2616
	return false;
2617
}
2618

    
2619
/* returns duid string from 'vardb_path']}/dhcp6c_duid' */
2620
function get_duid_from_file()
2621
{
2622
	global $g;
2623
	
2624
	$duid_ASCII = "";
2625
	$count = 0;
2626
	
2627
	if ($fd = fopen("{$g['vardb_path']}/dhcp6c_duid", "r")) {
2628
		if(filesize("{$g['vardb_path']}/dhcp6c_duid")==16) {
2629
			$buffer = fread($fd,16);					
2630
			while($count < 16) {
2631
				$duid_ASCII .= bin2hex($buffer[$count]);
2632
				$count++;
2633
				if($count < 16) {
2634
					$duid_ASCII .= ":";
2635
				}
2636
			}
2637
		}
2638
		fclose($fd);
2639
	}
2640
	//if no file or error with read then the string returns blanked DUID string
2641
	if(!is_duid($duid_ASCII)) {
2642
		return "--:--:--:--:--:--:--:--:--:--:--:--:--:--:--:--";
2643
	}
2644
	return($duid_ASCII);	
2645
}
2646
?>
(43-43/51)