Project

General

Profile

Download (51.2 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php 
2
/*
3
	util.inc
4
	part of the pfSense project (http://www.pfsense.com)
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 {$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 -{$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 -{$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
/* unlock configuration file */
136
function unlock($cfglckkey = 0) {
137
	global $g, $cfglckkeyconsumers;
138
	flock($cfglckkey, LOCK_UN);
139
	fclose($cfglckkey);
140
	return;
141
}
142

    
143
function send_event($cmd) {
144
	global $g;
145

    
146
	if(!isset($g['event_address']))
147
		$g['event_address'] = "unix:///var/run/check_reload_status";
148
		
149
	$try = 0;
150
	while ($try < 3) {
151
		$fd = @fsockopen($g['event_address']);
152
		if ($fd) {
153
			fwrite($fd, $cmd);
154
			$resp = fread($fd, 4096);
155
			if ($resp != "OK\n")
156
				log_error("send_event: sent {$cmd} got {$resp}");
157
			fclose($fd);
158
			$try = 3;
159
		} else if (!is_process_running("check_reload_status"))
160
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
161
		$try++;
162
	}
163
}
164

    
165
function send_multiple_events($cmds) {
166
	global $g;
167

    
168
	if(!isset($g['event_address']))
169
		$g['event_address'] = "unix:///var/run/check_reload_status";
170
			
171
	if (!is_array($cmds))
172
		return;
173

    
174
	while ($try < 3) {
175
		$fd = @fsockopen($g['event_address']);
176
		if ($fd) {
177
			foreach ($cmds as $cmd) {
178
				fwrite($fd, $cmd);
179
				$resp = fread($fd, 4096);
180
				if ($resp != "OK\n")
181
					log_error("send_event: sent {$cmd} got {$resp}");
182
			}
183
			fclose($fd);
184
			$try = 3;
185
		} else if (!is_process_running("check_reload_status"))
186
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
187
		$try++;
188
	}
189
}
190

    
191
function refcount_init($reference) {
192
	$shmid = @shmop_open($reference, "c", 0644, 10);
193
	@shmop_write($shmid, str_pad("0", 10, "\x0", STR_PAD_RIGHT), 0);
194
	@shmop_close($shmid);
195
}
196

    
197
function refcount_reference($reference) {
198
	/* Take out a lock across the shared memory read, increment, write sequence to make it atomic. */
199
	$shm_lck = lock("shm{$reference}", LOCK_EX);
200
	try {
201
		/* NOTE: A warning is generated when shared memory does not exist */
202
		$shmid = @shmop_open($reference, "w", 0, 0);
203
		if (!$shmid) {
204
			refcount_init($reference);
205
			$shmid = @shmop_open($reference, "w", 0, 0);
206
			if (!$shmid) {
207
				log_error(gettext("Could not open shared memory {$reference}"));
208
				unlock($shm_lck);
209
				return;
210
			}
211
		}
212
		$shm_data = @shmop_read($shmid, 0, 10);
213
		$shm_data = intval($shm_data) + 1;
214
		@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
215
		@shmop_close($shmid);
216
		unlock($shm_lck);
217
	} catch (Exception $e) {
218
		log_error($e->getMessage());
219
		unlock($shm_lck);
220
	}
221

    
222
	return $shm_data;
223
}
224

    
225
function refcount_unreference($reference) {
226
	/* Take out a lock across the shared memory read, decrement, write sequence to make it atomic. */
227
	$shm_lck = lock("shm{$reference}", LOCK_EX);
228
	try {
229
		$shmid = @shmop_open($reference, "w", 0, 0);
230
		if (!$shmid) {
231
			refcount_init($reference);
232
			log_error(gettext("Could not open shared memory {$reference}"));
233
			unlock($shm_lck);
234
			return;
235
		}
236
		$shm_data = @shmop_read($shmid, 0, 10);
237
		$shm_data = intval($shm_data) - 1;
238
		if ($shm_data < 0) {
239
			//debug_backtrace();
240
			log_error(sprintf(gettext("Reference %s is going negative, not doing unreference."), $reference));
241
		} else
242
			@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
243
		@shmop_close($shmid);
244
		unlock($shm_lck);
245
	} catch (Exception $e) {
246
		log_error($e->getMessage());
247
		unlock($shm_lck);
248
	}
249

    
250
	return $shm_data;
251
}
252

    
253
function refcount_read($reference) {
254
	/* This function just reads the current value of the refcount for information. */
255
	/* There is no need for locking. */
256
	$shmid = @shmop_open($reference, "a", 0, 0);
257
	if (!$shmid) {
258
		log_error(gettext("Could not open shared memory for read {$reference}"));
259
		return -1;
260
	}
261
	$shm_data = @shmop_read($shmid, 0, 10);
262
	@shmop_close($shmid);
263
	return $shm_data;
264
}
265

    
266
function is_module_loaded($module_name) {
267
	$running = `/sbin/kldstat | grep {$module_name} | /usr/bin/grep -v grep | /usr/bin/wc -l`;
268
	if (intval($running) >= 1)
269
		return true;
270
	else
271
		return false;
272
}
273

    
274
/* return the subnet address given a host address and a subnet bit count */
275
function gen_subnet($ipaddr, $bits) {
276
	if (!is_ipaddr($ipaddr) || !is_numeric($bits))
277
		return "";
278
	return long2ip(ip2long($ipaddr) & gen_subnet_mask_long($bits));
279
}
280

    
281
/* return the subnet address given a host address and a subnet bit count */
282
function gen_subnetv6($ipaddr, $bits) {
283
	if (!is_ipaddrv6($ipaddr) || !is_numeric($bits))
284
		return "";
285

    
286
	$address = Net_IPv6::getNetmask($ipaddr, $bits);
287
	$address = Net_IPv6::compress($address);
288
	return $address;
289
}
290

    
291
/* return the highest (broadcast) address in the subnet given a host address and a subnet bit count */
292
function gen_subnet_max($ipaddr, $bits) {
293
	if (!is_ipaddr($ipaddr) || !is_numeric($bits))
294
		return "";
295

    
296
	return long2ip32(ip2long($ipaddr) | ~gen_subnet_mask_long($bits));
297
}
298

    
299
/* Generate end number for a given ipv6 subnet mask */
300
function gen_subnetv6_max($ipaddr, $bits) {
301
	if(!is_ipaddrv6($ipaddr))
302
		return false;
303
	
304
	$mask = Net_IPv6::getNetmask('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',$bits);
305
	
306
	$inet_ip = (binary)inet_pton($ipaddr);
307
	$inet_mask = (binary)inet_pton($mask);
308

    
309
	$inet_end = $inet_ip | ~$inet_mask;
310

    
311
	return (inet_ntop($inet_end));
312
}
313

    
314
/* returns a subnet mask (long given a bit count) */
315
function gen_subnet_mask_long($bits) {
316
	$sm = 0;
317
	for ($i = 0; $i < $bits; $i++) {
318
		$sm >>= 1;
319
		$sm |= 0x80000000;
320
	}
321
	return $sm;
322
}
323

    
324
/* same as above but returns a string */
325
function gen_subnet_mask($bits) {
326
	return long2ip(gen_subnet_mask_long($bits));
327
}
328

    
329
/* Convert long int to IP address, truncating to 32-bits. */
330
function long2ip32($ip) {
331
	return long2ip($ip & 0xFFFFFFFF);
332
}
333

    
334
/* Convert IP address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms. */
335
function ip2long32($ip) {
336
	return ( ip2long($ip) & 0xFFFFFFFF );
337
}
338

    
339
/* Convert IP address to unsigned long int. */
340
function ip2ulong($ip) {
341
	return sprintf("%u", ip2long32($ip));
342
}
343

    
344
/* Find out how many IPs are contained within a given IP range
345
 *  e.g. 192.168.0.0 to 192.168.0.255 returns 256
346
 */
347
function ip_range_size($startip, $endip) {
348
	if (is_ipaddr($startip) && is_ipaddr($endip)) {
349
		// Operate as unsigned long because otherwise it wouldn't work
350
		//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
351
		return abs(ip2ulong($startip) - ip2ulong($endip)) + 1;
352
	}
353
	return -1;
354
}
355

    
356
/* Find the smallest possible subnet mask which can contain a given number of IPs
357
 *  e.g. 512 IPs can fit in a /23, but 513 IPs need a /22
358
 */
359
function find_smallest_cidr($number) {
360
	$smallest = 1;
361
	for ($b=32; $b > 0; $b--) {
362
		$smallest = ($number <= pow(2,$b)) ? $b : $smallest;
363
	}
364
	return (32-$smallest);
365
}
366

    
367
/* Return the previous IP address before the given address */
368
function ip_before($ip) {
369
	return long2ip32(ip2long($ip)-1);
370
}
371

    
372
/* Return the next IP address after the given address */
373
function ip_after($ip) {
374
	return long2ip32(ip2long($ip)+1);
375
}
376

    
377
/* Return true if the first IP is 'before' the second */
378
function ip_less_than($ip1, $ip2) {
379
	// Compare as unsigned long because otherwise it wouldn't work when
380
	//   crossing over from 127.255.255.255 / 128.0.0.0 barrier
381
	return ip2ulong($ip1) < ip2ulong($ip2);
382
}
383

    
384
/* Return true if the first IP is 'after' the second */
385
function ip_greater_than($ip1, $ip2) {
386
	// Compare as unsigned long because otherwise it wouldn't work
387
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
388
	return ip2ulong($ip1) > ip2ulong($ip2);
389
}
390

    
391
/* Convert a range of IPs to an array of subnets which can contain the range. */
392
function ip_range_to_subnet_array($startip, $endip) {
393
	if (!is_ipaddr($startip) || !is_ipaddr($endip)) {
394
		return array();
395
	}
396

    
397
	// Container for subnets within this range.
398
	$rangesubnets = array();
399

    
400
	// Figure out what the smallest subnet is that holds the number of IPs in the given range.
401
	$cidr = find_smallest_cidr(ip_range_size($startip, $endip));
402

    
403
	// Loop here to reduce subnet size and retest as needed. We need to make sure
404
	//   that the target subnet is wholly contained between $startip and $endip.
405
	for ($cidr; $cidr <= 32; $cidr++) {
406
		// Find the network and broadcast addresses for the subnet being tested.
407
		$targetsub_min = gen_subnet($startip, $cidr);
408
		$targetsub_max = gen_subnet_max($startip, $cidr);
409

    
410
		// Check best case where the range is exactly one subnet.
411
		if (($targetsub_min == $startip) && ($targetsub_max == $endip)) {
412
			// Hooray, the range is exactly this subnet!
413
			return array("{$startip}/{$cidr}");
414
		}
415

    
416
		// These remaining scenarios will find a subnet that uses the largest
417
		//  chunk possible of the range being tested, and leave the rest to be
418
		//  tested recursively after the loop.
419

    
420
		// Check if the subnet begins with $startip and ends before $endip
421
		if (($targetsub_min == $startip) && ip_less_than($targetsub_max, $endip)) {
422
			break;
423
		}
424

    
425
		// Check if the subnet ends at $endip and starts after $startip
426
		if (ip_greater_than($targetsub_min, $startip) && ($targetsub_max == $endip)) {
427
			break;
428
		}
429

    
430
		// Check if the subnet is between $startip and $endip
431
		if (ip_greater_than($targetsub_min, $startip) && ip_less_than($targetsub_max, $endip)) {
432
			break;
433
		}
434
	}
435

    
436
	// Some logic that will recursivly search from $startip to the first IP before the start of the subnet we just found.
437
	// NOTE: This may never be hit, the way the above algo turned out, but is left for completeness.
438
	if ($startip != $targetsub_min) {
439
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array($startip, ip_before($targetsub_min)));
440
	}
441

    
442
	// Add in the subnet we found before, to preserve ordering
443
	$rangesubnets[] = "{$targetsub_min}/{$cidr}";
444

    
445
	// And some more logic that will search after the subnet we found to fill in to the end of the range.
446
	if ($endip != $targetsub_max) {
447
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array(ip_after($targetsub_max), $endip));
448
	}
449
	return $rangesubnets;
450
}
451

    
452
function is_iprange($range) {
453
	if (substr_count($range, '-') != 1) {
454
		return false;
455
	}
456
	list($ip1, $ip2) = explode ('-', $range);
457
	return (is_ipaddr($ip1) && is_ipaddr($ip2));
458
}
459

    
460
function is_numericint($arg) {
461
	return (preg_match("/[^0-9]/", $arg) ? false : true);
462
}
463

    
464

    
465
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6 */
466
function is_ipaddr($ipaddr) {
467
	if(is_ipaddrv4($ipaddr)) {
468
		return true;
469
	}
470
	if(is_ipaddrv6($ipaddr)) {
471
		return true;
472
	}
473
	return false;
474
}
475

    
476
/* returns true if $ipaddr is a valid IPv6 address */
477
function is_ipaddrv6($ipaddr) {
478
	if (!is_string($ipaddr) || empty($ipaddr))
479
		return false;
480
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
481
		$tmpip = explode("%", $ipaddr);
482
		$ipaddr = $tmpip[0];
483
	}
484
	return Net_IPv6::checkIPv6($ipaddr);
485
}
486

    
487
/* returns true if $ipaddr is a valid dotted IPv4 address */
488
function is_ipaddrv4($ipaddr) {
489
	if (!is_string($ipaddr) || empty($ipaddr))
490
		return false;
491

    
492
	$ip_long = ip2long($ipaddr);
493
	$ip_reverse = long2ip32($ip_long);
494

    
495
	if ($ipaddr == $ip_reverse)
496
		return true;
497
	else
498
		return false;
499
}
500

    
501
/* returns true if $ipaddr is a valid linklocal address */
502
function is_linklocal($ipaddr) {
503
	return (substr($ipaddr, 0, 5) == "fe80:");
504
}
505

    
506
/* returns scope of a linklocal address */
507
function get_ll_scope($addr) {
508
	if (!is_linklocal($addr) || !strstr($addr, "%"))
509
		return "";
510
	list ($ll, $scope) = explode("%", $addr);
511
	return $scope;
512
}
513

    
514
/* returns true if $ipaddr is a valid literal IPv6 address */
515
function is_literalipaddrv6($ipaddr) {
516
	if(preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match))
517
		$ipaddr = $match[1];
518
	else
519
		return false;
520

    
521
	return is_ipaddrv6($ipaddr);
522
}
523

    
524
function is_ipaddrwithport($ipport) {
525
	$parts = explode(":", $ipport);
526
	$port = array_pop($parts);
527
	if (count($parts) == 1) {
528
		return is_ipaddrv4($parts[0]) && is_port($port);
529
	} elseif (count($parts) > 1) {
530
		return is_literalipaddrv6(implode(":", $parts)) && is_port($port);
531
	} else {
532
		return false;
533
	}
534
}
535

    
536
function is_hostnamewithport($hostport) {
537
	$parts = explode(":", $hostport);
538
	$port = array_pop($parts);
539
	if (count($parts) == 1) {
540
		return is_hostname($parts[0]) && is_port($port);
541
	} else {
542
		return false;
543
	}
544
}
545

    
546
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
547
function is_ipaddroralias($ipaddr) {
548
	global $config;
549

    
550
	if (is_alias($ipaddr)) {
551
		if (is_array($config['aliases']['alias'])) {
552
			foreach ($config['aliases']['alias'] as $alias) {
553
				if ($alias['name'] == $ipaddr && $alias['type'] != "port")
554
					return true;
555
			}
556
		}
557
		return false;
558
	} else
559
		return is_ipaddr($ipaddr);
560

    
561
}
562

    
563
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format */
564
function is_subnet($subnet) {
565
	if(is_subnetv4($subnet)) {
566
		return true;
567
	}
568
	if(is_subnetv6($subnet)) {
569
		return true;
570
	}
571
	return false;
572
}
573

    
574
/* returns true if $subnet is a valid IPv4 subnet in CIDR format */
575
function is_subnetv4($subnet) {
576
	if (!is_string($subnet))
577
		return false;
578

    
579
	list($hp,$np) = explode('/', $subnet);
580

    
581
	if (!is_ipaddrv4($hp))
582
		return false;
583

    
584
	if (!is_numeric($np) || ($np < 1) || ($np > 32))
585
		return false;
586

    
587
	return true;
588
}
589

    
590
/* returns true if $subnet is a valid IPv6 subnet in CIDR format */
591
function is_subnetv6($subnet) {
592
	if (!is_string($subnet))
593
		return false;
594

    
595
	list($hp,$np) = explode('/', $subnet);
596

    
597
	if (!is_ipaddrv6($hp))
598
		return false;
599

    
600
	if (!is_numeric($np) || ($np < 1) || ($np > 128))
601
		return false;
602

    
603
	return true;
604
}
605

    
606

    
607
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
608
function is_subnetoralias($subnet) {
609
	global $aliastable;
610

    
611
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet]))
612
		return true;
613
	else
614
		return is_subnet($subnet);
615
}
616

    
617
/* returns true if $hostname is a valid hostname */
618
function is_hostname($hostname) {
619
	if (!is_string($hostname))
620
		return false;
621

    
622
	if (preg_match('/^(?:(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])\.)*(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname))
623
		return true;
624
	else
625
		return false;
626
}
627

    
628
/* returns true if $domain is a valid domain name */
629
function is_domain($domain) {
630
	if (!is_string($domain))
631
		return false;
632

    
633
	if (preg_match('/^(?:(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])$/i', $domain))
634
		return true;
635
	else
636
		return false;
637
}
638

    
639
/* returns true if $macaddr is a valid MAC address */
640
function is_macaddr($macaddr, $partial=false) {
641
	$repeat = ($partial) ? '1,5' : '5';
642
	return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false;
643
}
644

    
645
/* returns true if $name is a valid name for an alias */
646
/* returns NULL if a reserved word is used */
647
function is_validaliasname($name) {
648
	/* Array of reserved words */
649
	$reserved = array("port", "pass");
650
	if (in_array($name, $reserved, true))
651
		return; /* return NULL */
652
	if (!preg_match("/[^a-zA-Z0-9_]/", $name) && (strlen($name) < 32))
653
		return true;
654
	else
655
		return false;
656
}
657

    
658
/* returns true if $port is a valid TCP/UDP port */
659
function is_port($port) {
660
	$tmpports = explode(":", $port);
661
	foreach($tmpports as $tmpport) {
662
		if (getservbyname($tmpport, "tcp") || getservbyname($tmpport, "udp"))
663
			continue;
664
		if (!ctype_digit($tmpport))
665
			return false;
666
		else if ((intval($tmpport) < 1) || (intval($tmpport) > 65535))
667
			return false;
668
	}
669
	return true;
670
}
671

    
672
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
673
function is_portrange($portrange) {
674
	$ports = explode(":", $portrange);
675

    
676
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
677
}
678

    
679
/* returns true if $port is a valid port number or an alias thereof */
680
function is_portoralias($port) {
681
	global $config;
682

    
683
	if (is_alias($port)) {
684
		if (is_array($config['aliases']['alias'])) {
685
			foreach ($config['aliases']['alias'] as $alias) {
686
				if ($alias['name'] == $port && $alias['type'] == "port")
687
					return true;
688
				}
689
			}
690
			return false;
691
	} else
692
		return is_port($port);
693
}
694

    
695
/* returns true if $val is a valid shaper bandwidth value */
696
function is_valid_shaperbw($val) {
697
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
698
}
699

    
700
/* returns true if $test is in the range between $start and $end */
701
function is_inrange_v4($test, $start, $end) {
702
	if ( (ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start)) )
703
		return true;
704
	else
705
		return false;
706
}
707

    
708
/* returns true if $test is in the range between $start and $end */
709
function is_inrange_v6($test, $start, $end) {
710
	if ( (inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start)) )
711
		return true;
712
	else
713
		return false;
714
}
715

    
716
/* returns true if $test is in the range between $start and $end */
717
function is_inrange($test, $start, $end) {
718
	return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end);
719
}
720

    
721
/* return the configured carp interface list */
722
function get_configured_carp_interface_list() {
723
	global $config;
724

    
725
	$iflist = array();
726

    
727
	if(is_array($config['virtualip']['vip'])) {
728
		$viparr = &$config['virtualip']['vip'];
729
		foreach ($viparr as $vip) {
730
			switch ($vip['mode']) {
731
			case "carp":
732
				$vipif = "{$vip['interface']}_vip{$vip['vhid']}";
733
				$iflist[$vipif] = $vip['subnet'];
734
				break;
735
			}
736
		}
737
	}
738

    
739
	return $iflist;
740
}
741

    
742
/* return the configured IP aliases list */
743
function get_configured_ip_aliases_list($returnfullentry = false) {
744
	global $config;
745

    
746
	$alias_list=array();
747

    
748
	if(is_array($config['virtualip']['vip'])) {
749
		$viparr = &$config['virtualip']['vip'];
750
		foreach ($viparr as $vip) {
751
			if ($vip['mode']=="ipalias") {
752
				if ($returnfullentry)
753
					$alias_list[$vip['subnet']] = $vip;
754
				else
755
					$alias_list[$vip['subnet']] = $vip['interface'];
756
			}
757
		}
758
	}
759

    
760
	return $alias_list;
761
}
762

    
763
/* return all configured aliases list (IP, carp, proxyarp and other) */
764
function get_configured_vips_list() {
765
	global $config;
766

    
767
	$alias_list=array();
768

    
769
	if(is_array($config['virtualip']['vip'])) {
770
		$viparr = &$config['virtualip']['vip'];
771
		foreach ($viparr as $vip)
772
			$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
773
	}
774

    
775
	return $alias_list;
776
}
777

    
778
/* comparison function for sorting by the order in which interfaces are normally created */
779
function compare_interface_friendly_names($a, $b) {
780
	if ($a == $b)
781
		return 0;
782
	else if ($a == 'wan')
783
		return -1;
784
	else if ($b == 'wan')
785
		return 1;
786
	else if ($a == 'lan')
787
		return -1;
788
	else if ($b == 'lan')
789
		return 1;
790

    
791
	return strnatcmp($a, $b);
792
}
793

    
794
/* return the configured interfaces list. */
795
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
796
	global $config;
797

    
798
	$iflist = array();
799

    
800
	/* if list */
801
	foreach($config['interfaces'] as $if => $ifdetail) {
802
		if ($only_opt && ($if == "wan" || $if == "lan"))
803
			continue;
804
		if (isset($ifdetail['enable']) || $withdisabled == true)
805
			$iflist[$if] = $if;
806
	}
807

    
808
	return $iflist;
809
}
810

    
811
/* return the configured interfaces list. */
812
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
813
	global $config;
814

    
815
	$iflist = array();
816

    
817
	/* if list */
818
	foreach($config['interfaces'] as $if => $ifdetail) {
819
		if ($only_opt && ($if == "wan" || $if == "lan"))
820
			continue;
821
		if (isset($ifdetail['enable']) || $withdisabled == true) {
822
			$tmpif = get_real_interface($if);
823
			if (!empty($tmpif))
824
				$iflist[$tmpif] = $if;
825
		}
826
	}
827

    
828
	return $iflist;
829
}
830

    
831
/* return the configured interfaces list with their description. */
832
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
833
	global $config;
834

    
835
	$iflist = array();
836

    
837
	/* if list */
838
	foreach($config['interfaces'] as $if => $ifdetail) {
839
		if ($only_opt && ($if == "wan" || $if == "lan"))
840
			continue;
841
		if (isset($ifdetail['enable']) || $withdisabled == true) {
842
			if(empty($ifdetail['descr']))
843
				$iflist[$if] = strtoupper($if);
844
			else
845
				$iflist[$if] = strtoupper($ifdetail['descr']);
846
		}
847
	}
848

    
849
	return $iflist;
850
}
851

    
852
/*
853
 *   get_configured_ip_addresses() - Return a list of all configured
854
 *   interfaces IP Addresses
855
 *
856
 */
857
function get_configured_ip_addresses() {
858
	global $config;
859

    
860
	if (!function_exists('get_interface_ip'))
861
		require_once("interfaces.inc");
862
	$ip_array = array();
863
	$interfaces = get_configured_interface_list();
864
	if (is_array($interfaces)) {
865
		foreach($interfaces as $int) {
866
			$ipaddr = get_interface_ip($int);
867
			$ip_array[$int] = $ipaddr;
868
		}
869
	}
870
	$interfaces = get_configured_carp_interface_list();
871
	if (is_array($interfaces)) 
872
		foreach($interfaces as $int => $ipaddr) 
873
			$ip_array[$int] = $ipaddr;
874

    
875
	/* pppoe server */
876
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
877
		foreach($config['pppoes']['pppoe'] as $pppoe) {
878
			if ($pppoe['mode'] == "server") {
879
				if(is_ipaddr($pppoe['localip'])) {
880
					$int = "pppoes". $pppoe['pppoeid'];
881
					$ip_array[$int] = $pppoe['localip'];
882
				}
883
			}
884
		}
885
	}
886

    
887
	return $ip_array;
888
}
889

    
890
/*
891
 *   get_configured_ipv6_addresses() - Return a list of all configured
892
 *   interfaces IPv6 Addresses
893
 *
894
 */
895
function get_configured_ipv6_addresses() {
896
	require_once("interfaces.inc");
897
	$ipv6_array = array();
898
	$interfaces = get_configured_interface_list();
899
	if(is_array($interfaces)) {
900
		foreach($interfaces as $int) {
901
			$ipaddrv6 = get_interface_ipv6($int);
902
			$ipv6_array[$int] = $ipaddrv6;
903
		}
904
	}
905
	$interfaces = get_configured_carp_interface_list();
906
	if(is_array($interfaces)) 
907
		foreach($interfaces as $int => $ipaddrv6) 
908
			$ipv6_array[$int] = $ipaddrv6;
909
	return $ipv6_array;
910
}
911

    
912
/*
913
 *   get_interface_list() - Return a list of all physical interfaces
914
 *   along with MAC and status.
915
 *
916
 *   $mode = "active" - use ifconfig -lu
917
 *           "media"  - use ifconfig to check physical connection
918
 *			status (much slower)
919
 */
920
function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") {
921
        global $config;
922
	$upints = array();
923
        /* get a list of virtual interface types */
924
        if(!$vfaces) {
925
		$vfaces = array (
926
				'bridge',
927
				'ppp',
928
				'pppoe',
929
				'pptp',
930
				'l2tp',
931
				'sl',
932
				'gif',
933
				'gre',
934
				'faith',
935
				'lo',
936
				'ng',
937
				'_vlan',
938
				'_wlan',
939
				'pflog',
940
				'plip',
941
				'pfsync',
942
				'enc',
943
				'tun',
944
				'carp',
945
				'lagg',
946
				'vip',
947
				'ipfw'
948
		);
949
	}
950
	switch($mode) {
951
	case "active":
952
                $upints = pfSense_interface_listget(IFF_UP);
953
        	break;
954
	case "media":
955
		$intlist = pfSense_interface_listget();
956
                $ifconfig = "";
957
                exec("/sbin/ifconfig -a", $ifconfig);
958
                $regexp = '/(' . implode('|', $intlist) . '):\s/';
959
                $ifstatus = preg_grep('/status:/', $ifconfig);
960
		foreach($ifstatus as $status) {
961
			$int = array_shift($intlist);
962
			if(stristr($status, "active")) $upints[] = $int;
963
		}
964
		break;
965
	default:
966
		$upints = pfSense_interface_listget();
967
		break;
968
	}
969
        /* build interface list with netstat */
970
        $linkinfo = "";
971
        exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo);
972
        array_shift($linkinfo);
973
	/* build ip address list with netstat */
974
	$ipinfo = "";
975
	exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo);
976
	array_shift($ipinfo);
977
	foreach($linkinfo as $link) {
978
		$friendly = "";
979
		$alink = explode(" ", $link);
980
		$ifname = rtrim(trim($alink[0]), '*');
981
		/* trim out all numbers before checking for vfaces */
982
		if (!in_array(array_shift(preg_split('/\d/', $ifname)), $vfaces) &&
983
			!stristr($ifname, "_vlan") && !stristr($ifname, "_wlan")) {
984
			$toput = array(
985
					"mac" => trim($alink[1]),
986
					"up" => in_array($ifname, $upints)
987
				);
988
			foreach($ipinfo as $ip) {
989
				$aip = explode(" ", $ip);
990
				if($aip[0] == $ifname) {
991
					$toput['ipaddr'] = $aip[1];
992
				}
993
			}
994
			if (is_array($config['interfaces'])) {
995
				foreach($config['interfaces'] as $name => $int)
996
					if($int['if'] == $ifname) $friendly = $name;
997
			}
998
			switch($keyby) {
999
			case "physical":
1000
				if($friendly != "") {
1001
					$toput['friendly'] = $friendly;
1002
				}
1003
				$dmesg_arr = array();
1004
				exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr);
1005
				preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg);
1006
				$toput['dmesg'] = $dmesg[1][0];
1007
				$iflist[$ifname] = $toput;
1008
				break;
1009
			case "ppp":
1010
				
1011
			case "friendly":
1012
				if($friendly != "") {
1013
					$toput['if'] = $ifname;
1014
					$iflist[$friendly] = $toput;
1015
				}
1016
				break;
1017
			}
1018
		}
1019
	}
1020
	return $iflist;
1021
}
1022

    
1023
/****f* util/log_error
1024
* NAME
1025
*   log_error  - Sends a string to syslog.
1026
* INPUTS
1027
*   $error     - string containing the syslog message.
1028
* RESULT
1029
*   null
1030
******/
1031
function log_error($error) {
1032
	global $g;
1033
	$page = $_SERVER['SCRIPT_NAME'];
1034
	if (empty($page)) {
1035
		$files = get_included_files();
1036
		$page = basename($files[0]);
1037
	}
1038
	syslog(LOG_ERR, "$page: $error");
1039
	if ($g['debug'])
1040
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1041
	return;
1042
}
1043

    
1044
/****f* util/log_auth
1045
* NAME
1046
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1047
* INPUTS
1048
*   $error     - string containing the syslog message.
1049
* RESULT
1050
*   null
1051
******/
1052
function log_auth($error) {
1053
	global $g;
1054
	$page = $_SERVER['SCRIPT_NAME'];
1055
	syslog(LOG_AUTH, "$page: $error");
1056
	if ($g['debug'])
1057
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1058
	return;
1059
}
1060

    
1061
/****f* util/exec_command
1062
 * NAME
1063
 *   exec_command - Execute a command and return a string of the result.
1064
 * INPUTS
1065
 *   $command   - String of the command to be executed.
1066
 * RESULT
1067
 *   String containing the command's result.
1068
 * NOTES
1069
 *   This function returns the command's stdout and stderr.
1070
 ******/
1071
function exec_command($command) {
1072
	$output = array();
1073
	exec($command . ' 2>&1 ', $output);
1074
	return(implode("\n", $output));
1075
}
1076

    
1077
/* wrapper for exec() */
1078
function mwexec($command, $mute = false, $clearsigmask = false) {
1079
	global $g;
1080

    
1081
	if ($g['debug']) {
1082
		if (!$_SERVER['REMOTE_ADDR'])
1083
			echo "mwexec(): $command\n";
1084
	}
1085
	$oarr = array();
1086
	$retval = 0;
1087

    
1088
	if ($clearsigmask) {
1089
		$oldset = array();
1090
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1091
	}
1092
	$garbage = exec("$command 2>&1", $oarr, $retval);
1093
	if ($clearsigmask) {
1094
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1095
	}
1096

    
1097
	if(isset($config['system']['developerspew']))
1098
		$mute = false;
1099
	if(($retval <> 0) && ($mute === false)) {
1100
		$output = implode(" ", $oarr);
1101
		log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, $output));
1102
	}
1103
	return $retval;
1104
}
1105

    
1106
/* wrapper for exec() in background */
1107
function mwexec_bg($command, $clearsigmask = false) {
1108
	global $g;
1109

    
1110
	if ($g['debug']) {
1111
		if (!$_SERVER['REMOTE_ADDR'])
1112
			echo "mwexec(): $command\n";
1113
	}
1114

    
1115
	if ($clearsigmask) {
1116
		$oldset = array();
1117
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1118
	}
1119
	$_gb = exec("/usr/bin/nohup $command > /dev/null 2>&1 &");
1120
	if ($clearsigmask) {
1121
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1122
	}
1123
	unset($_gb);
1124
}
1125

    
1126
/* unlink a file, if it exists */
1127
function unlink_if_exists($fn) {
1128
	$to_do = glob($fn);
1129
	if(is_array($to_do)) {
1130
		foreach($to_do as $filename)
1131
			@unlink($filename);
1132
	} else {
1133
		@unlink($fn);
1134
	}
1135
}
1136
/* make a global alias table (for faster lookups) */
1137
function alias_make_table($config) {
1138
	global $aliastable;
1139

    
1140
	$aliastable = array();
1141

    
1142
	if (is_array($config['aliases']['alias'])) {
1143
		foreach ($config['aliases']['alias'] as $alias) {
1144
			if ($alias['name'])
1145
				$aliastable[$alias['name']] = $alias['address'];
1146
		}
1147
	}
1148
}
1149

    
1150
/* check if an alias exists */
1151
function is_alias($name) {
1152
	global $aliastable;
1153

    
1154
	return isset($aliastable[$name]);
1155
}
1156

    
1157
function alias_get_type($name) {
1158
        global $config;
1159
        
1160
	if (is_array($config['aliases']['alias'])) {
1161
		foreach ($config['aliases']['alias'] as $alias) {
1162
			if ($name == $alias['name'])
1163
				return $alias['type'];
1164
		}
1165
	}
1166

    
1167
        return "";
1168
}
1169

    
1170
/* expand a host or network alias, if necessary */
1171
function alias_expand($name) {
1172
	global $aliastable;
1173

    
1174
	if (isset($aliastable[$name]))
1175
		return "\${$name}";
1176
	else if (is_ipaddr($name) || is_subnet($name) || is_port($name))
1177
		return "{$name}";
1178
	else
1179
		return null;
1180
}
1181

    
1182
function alias_expand_urltable($name) {
1183
	global $config;
1184
	$urltable_prefix = "/var/db/aliastables/";
1185
	$urltable_filename = $urltable_prefix . $name . ".txt";
1186

    
1187
	if (is_array($config['aliases']['alias'])) {
1188
		foreach ($config['aliases']['alias'] as $alias) {
1189
			if (($alias['type'] == 'urltable') && ($alias['name'] == $name)) {
1190
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename))
1191
					return $urltable_filename;
1192
				else if (process_alias_urltable($name, $alias["url"], 0, true))
1193
					return $urltable_filename;
1194
			}
1195
		}
1196
	}
1197
	return null;
1198
}
1199

    
1200
function subnet_size($subnet) {
1201
	if (is_subnetv4($subnet)) {
1202
		list ($ip, $bits) = explode("/", $subnet);
1203
		return round(exp(log(2) * (32 - $bits)));
1204
	}
1205
	else if (is_subnetv6($subnet)) {
1206
		list ($ip, $bits) = explode("/", $subnet);
1207
		return round(exp(log(2) * (128 - $bits)));
1208
	}
1209
	else {
1210
		return 0;
1211
	}
1212
}
1213

    
1214
function subnet_expand($subnet) {
1215
	if (is_subnetv4($subnet)) {
1216
		return subnetv4_expand($subnet);
1217
	} else if (is_subnetv6($subnet)) {
1218
		return subnetv6_expand($subnet);
1219
	} else {
1220
		return $subnet;
1221
	}
1222
}
1223

    
1224
function subnetv4_expand($subnet) {
1225
	$result = array();
1226
	list ($ip, $bits) = explode("/", $subnet);
1227
	$net  = ip2long($ip);
1228
	$mask = (0xffffffff << (32 - $bits));
1229
	$net &= $mask;
1230
	$size = round(exp(log(2) * (32 - $bits)));
1231
	for ($i = 0; $i < $size; $i += 1) {
1232
		$result[] = long2ip($net | $i);
1233
	}
1234
	return $result;
1235
}
1236

    
1237
/* find out whether two subnets overlap */
1238
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
1239

    
1240
	if (!is_numeric($bits1))
1241
		$bits1 = 32;
1242
	if (!is_numeric($bits2))
1243
		$bits2 = 32;
1244

    
1245
	if ($bits1 < $bits2)
1246
		$relbits = $bits1;
1247
	else
1248
		$relbits = $bits2;
1249

    
1250
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
1251
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
1252

    
1253
	return ($sn1 == $sn2);
1254
}
1255

    
1256
/* find out whether two IPv6 subnets overlap */
1257
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
1258
	$sub1_min = gen_subnetv6($subnet1, $bits1);
1259
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
1260
	$sub2_min = gen_subnetv6($subnet2, $bits2);
1261
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
1262
	
1263
	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));
1264
}
1265

    
1266
/* compare two IP addresses */
1267
function ipcmp($a, $b) {
1268
	if (ip_less_than($a, $b))
1269
		return -1;
1270
	else if (ip_greater_than($a, $b))
1271
		return 1;
1272
	else
1273
		return 0;
1274
}
1275

    
1276
/* return true if $addr is in $subnet, false if not */
1277
function ip_in_subnet($addr,$subnet) {
1278
	if(is_ipaddrv6($addr)) {
1279
		return (Net_IPv6::isInNetmask($addr, $subnet));
1280
	} else { /* XXX: Maybe check for IPv4 */
1281
		list($ip, $mask) = explode('/', $subnet);
1282
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
1283
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
1284
	}
1285
}
1286

    
1287
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1288
function verify_digital_signature($fname) {
1289
	global $g;
1290

    
1291
	if(!file_exists("/usr/local/sbin/gzsig"))
1292
		return 4;
1293

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

    
1297
/* obtain MAC address given an IP address by looking at the ARP table */
1298
function arp_get_mac_by_ip($ip) {
1299
	mwexec("/sbin/ping -c 1 -t 1 {$ip}", true);
1300
	$arpoutput = "";
1301
	exec("/usr/sbin/arp -n {$ip}", $arpoutput);
1302

    
1303
	if ($arpoutput[0]) {
1304
		$arpi = explode(" ", $arpoutput[0]);
1305
		$macaddr = $arpi[3];
1306
		if (is_macaddr($macaddr))
1307
			return $macaddr;
1308
		else
1309
			return false;
1310
	}
1311

    
1312
	return false;
1313
}
1314

    
1315
/* return a fieldname that is safe for xml usage */
1316
function xml_safe_fieldname($fieldname) {
1317
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1318
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1319
			 ':', ',', '.', '\'', '\\'
1320
		);
1321
	return strtolower(str_replace($replace, "", $fieldname));
1322
}
1323

    
1324
function mac_format($clientmac) {
1325
    global $config, $cpzone;
1326

    
1327
    $mac = explode(":", $clientmac);
1328
    $mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1329

    
1330
    switch($mac_format) {
1331
        case 'singledash':
1332
		return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1333

    
1334
        case 'ietf':
1335
		return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1336

    
1337
        case 'cisco':
1338
		return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1339

    
1340
        case 'unformatted':
1341
		return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1342

    
1343
        default:
1344
		return $clientmac;
1345
    }
1346
}
1347

    
1348
function resolve_retry($hostname, $retries = 5) {
1349

    
1350
	if (is_ipaddr($hostname))
1351
		return $hostname;
1352

    
1353
       for ($i = 0; $i < $retries; $i++) {
1354
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1355
               $ip = gethostbyname($hostname);
1356

    
1357
		if ($ip && $ip != $hostname) {
1358
			/* success */
1359
			return $ip;
1360
		}
1361

    
1362
		sleep(1);
1363
	}
1364

    
1365
	return false;
1366
}
1367

    
1368
function format_bytes($bytes) {
1369
	if ($bytes >= 1073741824) {
1370
		return sprintf("%.2f GB", $bytes/1073741824);
1371
	} else if ($bytes >= 1048576) {
1372
		return sprintf("%.2f MB", $bytes/1048576);
1373
	} else if ($bytes >= 1024) {
1374
		return sprintf("%.0f KB", $bytes/1024);
1375
	} else {
1376
		return sprintf("%d bytes", $bytes);
1377
	}
1378
}
1379

    
1380
function update_filter_reload_status($text) {
1381
	global $g;
1382

    
1383
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1384
}
1385

    
1386
/****** util/return_dir_as_array
1387
 * NAME
1388
 *   return_dir_as_array - Return a directory's contents as an array.
1389
 * INPUTS
1390
 *   $dir          - string containing the path to the desired directory.
1391
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1392
 * RESULT
1393
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1394
 ******/
1395
function return_dir_as_array($dir, $filter_regex = '') {
1396
	$dir_array = array();
1397
	if (is_dir($dir)) {
1398
		if ($dh = opendir($dir)) {
1399
			while (($file = readdir($dh)) !== false) {
1400
				if (($file == ".") || ($file == ".."))
1401
					continue;
1402

    
1403
				if (empty($filter_regex) || preg_match($filter_regex, $file))
1404
					array_push($dir_array, $file);
1405
			}
1406
			closedir($dh);
1407
		}
1408
	}
1409
	return $dir_array;
1410
}
1411

    
1412
function run_plugins($directory) {
1413
	global $config, $g;
1414

    
1415
	/* process packager manager custom rules */
1416
	$files = return_dir_as_array($directory);
1417
	if (is_array($files)) {
1418
		foreach ($files as $file) {
1419
			if (stristr($file, ".sh") == true)
1420
				mwexec($directory . $file . " start");
1421
			else if (!is_dir($directory . "/" . $file) && stristr($file,".inc")) 
1422
				require_once($directory . "/" . $file);
1423
		}
1424
	}
1425
}
1426

    
1427
/*
1428
 *    safe_mkdir($path, $mode = 0755)
1429
 *    create directory if it doesn't already exist and isn't a file!
1430
 */
1431
function safe_mkdir($path, $mode=0755) {
1432
	global $g;
1433

    
1434
	if (!is_file($path) && !is_dir($path)) {
1435
		return @mkdir($path, $mode, true);
1436
	} else {
1437
		return false;
1438
	}
1439
}
1440

    
1441
/*
1442
 * make_dirs($path, $mode = 0755)
1443
 * create directory tree recursively (mkdir -p)
1444
 */
1445
function make_dirs($path, $mode = 0755) {
1446
	$base = '';
1447
	foreach (explode('/', $path) as $dir) {
1448
		$base .= "/$dir";
1449
		if (!is_dir($base)) {
1450
			if (!@mkdir($base, $mode))
1451
				return false;
1452
		}
1453
	}
1454
	return true;
1455
}
1456

    
1457
/*
1458
 * get_sysctl($names)
1459
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1460
 * name) and return an array of key/value pairs set for those that exist
1461
 */
1462
function get_sysctl($names) {
1463
	if (empty($names))
1464
		return array();
1465

    
1466
	if (is_array($names)) {
1467
		$name_list = array();
1468
		foreach ($names as $name) {
1469
			$name_list[] = escapeshellarg($name);
1470
		}
1471
	} else
1472
		$name_list = array(escapeshellarg($names));
1473

    
1474
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1475
	$values = array();
1476
	foreach ($output as $line) {
1477
		$line = explode(": ", $line, 2);
1478
		if (count($line) == 2)
1479
			$values[$line[0]] = $line[1];
1480
	}
1481

    
1482
	return $values;
1483
}
1484

    
1485
/*
1486
 * set_sysctl($value_list)
1487
 * Set sysctl OID's listed as key/value pairs and return
1488
 * an array with keys set for those that succeeded
1489
 */
1490
function set_sysctl($values) {
1491
	if (empty($values))
1492
		return array();
1493

    
1494
	$value_list = array();
1495
	foreach ($values as $key => $value) {
1496
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1497
	}
1498

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

    
1501
	/* Retry individually if failed (one or more read-only) */
1502
	if ($success <> 0 && count($value_list) > 1) {
1503
		foreach ($value_list as $value) {
1504
			exec("/sbin/sysctl -i " . $value, $output);
1505
		}
1506
	}
1507

    
1508
	$ret = array();
1509
	foreach ($output as $line) {
1510
		$line = explode(": ", $line, 2);
1511
		if (count($line) == 2)
1512
			$ret[$line[0]] = true;
1513
	}
1514

    
1515
	return $ret;
1516
}
1517

    
1518
/*
1519
 *     get_memory()
1520
 *     returns an array listing the amount of
1521
 *     memory installed in the hardware
1522
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
1523
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
1524
 */
1525
function get_memory() {
1526

    
1527
	$physmem = trim(`sysctl -n hw.physmem`, " \n");
1528
	$realmem = trim(`sysctl -n hw.realmem`, " \n");
1529
	/* convert from bytes to megabytes */
1530
	return array(($physmem/1048576),($realmem/1048576));
1531
}
1532

    
1533
function mute_kernel_msgs() {
1534
	global $config;
1535
	// Do not mute serial console.  The kernel gets very very cranky
1536
	// and will start dishing you cannot control tty errors.
1537
	switch (trim(file_get_contents("/etc/platform"))) {
1538
		case "nanobsd":
1539
		case "jail":
1540
			return;
1541
	}
1542
	if($config['system']['enableserial']) 
1543
		return;			
1544
	exec("/sbin/conscontrol mute on");
1545
}
1546

    
1547
function unmute_kernel_msgs() {
1548
	global $config;
1549
	// Do not mute serial console.  The kernel gets very very cranky
1550
	// and will start dishing you cannot control tty errors.
1551
	switch (trim(file_get_contents("/etc/platform"))) {
1552
		case "nanobsd":
1553
		case "jail":
1554
			return;
1555
	}
1556
	exec("/sbin/conscontrol mute off");
1557
}
1558

    
1559
function start_devd() {
1560
	global $g;
1561

    
1562
	if ($g['platform'] == 'jail')
1563
		return;
1564
	exec("/sbin/devd");
1565
	sleep(1);
1566
}
1567

    
1568
function is_interface_vlan_mismatch() {
1569
	global $config, $g;
1570

    
1571
	if (is_array($config['vlans']['vlan'])) {
1572
		foreach ($config['vlans']['vlan'] as $vlan) {
1573
			if (does_interface_exist($vlan['if']) == false)
1574
				return true;
1575
		}
1576
	}
1577

    
1578
	return false;
1579
}
1580

    
1581
function is_interface_mismatch() {
1582
	global $config, $g;
1583

    
1584
	$do_assign = false;
1585
	$i = 0;
1586
	$missing_interfaces = array();
1587
	if (is_array($config['interfaces'])) {
1588
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1589
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1590
				// Do not check these interfaces.
1591
				$i++;
1592
				continue;
1593
			}
1594
			else if (does_interface_exist($ifcfg['if']) == false) {
1595
				$missing_interfaces[] = $ifcfg['if'];
1596
				$do_assign = true;
1597
			} else
1598
				$i++;
1599
		}
1600
	}
1601

    
1602
	if ($g['minimum_nic_count'] > $i) {
1603
		$do_assign = true;
1604
	} else if (file_exists("{$g['tmp_path']}/assign_complete"))
1605
		$do_assign = false;
1606

    
1607
	if (!empty($missing_interfaces) && $do_assign)
1608
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1609
	else
1610
		@unlink("{$g['tmp_path']}/missing_interfaces");
1611

    
1612
	return $do_assign;
1613
}
1614

    
1615
/* sync carp entries to other firewalls */
1616
function carp_sync_client() {
1617
	global $g;
1618
	send_event("filter sync");
1619
}
1620

    
1621
/****f* util/isAjax
1622
 * NAME
1623
 *   isAjax - reports if the request is driven from prototype
1624
 * INPUTS
1625
 *   none
1626
 * RESULT
1627
 *   true/false
1628
 ******/
1629
function isAjax() {
1630
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
1631
}
1632

    
1633
/****f* util/timeout
1634
 * NAME
1635
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
1636
 * INPUTS
1637
 *   optional, seconds to wait before timeout. Default 9 seconds.
1638
 * RESULT
1639
 *   returns 1 char of user input or null if no input.
1640
 ******/
1641
function timeout($timer = 9) {
1642
	while(!isset($key)) {
1643
		if ($timer >= 9) { echo chr(8) . chr(8) . ($timer==9 ? chr(32) : null)  . "{$timer}";  }
1644
		else { echo chr(8). "{$timer}"; }
1645
		`/bin/stty -icanon min 0 time 25`;
1646
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
1647
		`/bin/stty icanon`;
1648
		if ($key == '')
1649
			unset($key);
1650
		$timer--;
1651
		if ($timer == 0)
1652
			break;
1653
	}
1654
	return $key;	
1655
}
1656

    
1657
/****f* util/msort
1658
 * NAME
1659
 *   msort - sort array
1660
 * INPUTS
1661
 *   $array to be sorted, field to sort by, direction of sort
1662
 * RESULT
1663
 *   returns newly sorted array
1664
 ******/
1665
function msort($array, $id="id", $sort_ascending=true) {
1666
	$temp_array = array();
1667
	while(count($array)>0) {
1668
		$lowest_id = 0;
1669
		$index=0;
1670
		foreach ($array as $item) {
1671
			if (isset($item[$id])) {
1672
				if ($array[$lowest_id][$id]) {
1673
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
1674
						$lowest_id = $index;
1675
					}
1676
				}
1677
			}
1678
			$index++;
1679
		}
1680
		$temp_array[] = $array[$lowest_id];
1681
		$array = array_merge(array_slice($array, 0,$lowest_id), array_slice($array, $lowest_id+1));
1682
	}
1683
	if ($sort_ascending) {
1684
		return $temp_array;
1685
	} else {
1686
    		return array_reverse($temp_array);
1687
	}
1688
}
1689

    
1690
/****f* util/color
1691
 * NAME
1692
 *   color - outputs a color code to the ansi terminal if supported
1693
 * INPUTS
1694
 *   color code or color name
1695
 * RESULT
1696
 *   Outputs the ansi color sequence for the color specified.  Default resets terminal.
1697
 ******/
1698
function color($color = "0m") {
1699
	/*
1700
		Color codes available:
1701
		 0m reset; clears all colors and styles (to white on black)
1702
		 1m bold on (see below)
1703
		 3m italics on
1704
		 4m underline on
1705
		 7m inverse on; reverses foreground & background colors
1706
		 9m strikethrough on
1707
		 22m bold off (see below)
1708
		 23m italics off
1709
		 24m underline off
1710
		 27m inverse off
1711
		 29m strikethrough off
1712
		 30m set foreground color to black
1713
		 31m set foreground color to red
1714
		 32m set foreground color to green
1715
		 33m set foreground color to yellow
1716
		 34m set foreground color to blue
1717
		 35m set foreground color to magenta (purple)
1718
		 36m set foreground color to cyan
1719
		 37m set foreground color to white
1720
		 40m  set background color to black
1721
		 41m set background color to red
1722
		 42m set background color to green
1723
		 43m set background color to yellow
1724
		 44m set background color to blue
1725
		 45m set background color to magenta (purple)
1726
		 46m set background color to cyan
1727
		 47m set background color to white
1728
		 49m set background color to default (black)
1729
	*/	
1730
	// Allow caching of TERM to 
1731
	// speedup subequence requests.
1732
	global $TERM;
1733
	if(!$TERM) 
1734
		$TERM=`/usr/bin/env | grep color`;
1735
	if(!$TERM)
1736
		$TERM=`/usr/bin/env | grep cons25`;
1737
	if($TERM) {
1738
		$ESCAPE=chr(27);
1739
		switch ($color) {
1740
			case "black":
1741
				return "{$ESCAPE}[30m"; 
1742
			case "red":
1743
				return "{$ESCAPE}[31m"; 
1744
			case "green":
1745
				return "{$ESCAPE}[32m"; 
1746
			case "yellow":
1747
				return "{$ESCAPE}[33m"; 
1748
			case "blue":
1749
				return "{$ESCAPE}[34m"; 
1750
			case "magenta":
1751
				return "{$ESCAPE}[35m"; 
1752
			case "cyan":
1753
				return "{$ESCAPE}[36m"; 
1754
			case "white":
1755
				return "{$ESCAPE}[37m"; 
1756
			case "default":
1757
				return "{$ESCAPE}[39m"; 
1758
		}
1759
		return "{$ESCAPE}[{$color}";
1760
	}
1761
}
1762

    
1763
/****f* util/is_URL
1764
 * NAME
1765
 *   is_URL
1766
 * INPUTS
1767
 *   string to check
1768
 * RESULT
1769
 *   Returns true if item is a URL
1770
 ******/
1771
function is_URL($url) {
1772
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
1773
	if($match)
1774
		return true;	
1775
	return false;
1776
}
1777

    
1778
function is_file_included($file = "") {
1779
	$files = get_included_files();
1780
	if (in_array($file, $files))
1781
		return true;
1782
	
1783
	return false;
1784
}
1785

    
1786
/*
1787
 * Replace a value on a deep associative array using regex
1788
 */
1789
function array_replace_values_recursive($data, $match, $replace) {
1790
	if (empty($data))
1791
		return $data;
1792

    
1793
	if (is_string($data))
1794
		$data = preg_replace("/{$match}/", $replace, $data);
1795
	else if (is_array($data))
1796
		foreach ($data as $k => $v)
1797
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
1798

    
1799
	return $data;
1800
}
1801

    
1802
/*
1803
	This function was borrowed from a comment on PHP.net at the following URL:
1804
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
1805
 */
1806
function array_merge_recursive_unique($array0, $array1) {
1807

    
1808
	$arrays = func_get_args();
1809
	$remains = $arrays;
1810

    
1811
	// We walk through each arrays and put value in the results (without
1812
	// considering previous value).
1813
	$result = array();
1814

    
1815
	// loop available array
1816
	foreach($arrays as $array) {
1817

    
1818
		// The first remaining array is $array. We are processing it. So
1819
		// we remove it from remaing arrays.
1820
        array_shift($remains);
1821

    
1822
		// We don't care non array param, like array_merge since PHP 5.0.
1823
		if(is_array($array)) {
1824
			// Loop values
1825
			foreach($array as $key => $value) {
1826
				if(is_array($value)) {
1827
					// we gather all remaining arrays that have such key available
1828
					$args = array();
1829
					foreach($remains as $remain) {
1830
						if(array_key_exists($key, $remain)) {
1831
							array_push($args, $remain[$key]);
1832
						}
1833
					}
1834

    
1835
					if(count($args) > 2) {
1836
						// put the recursion
1837
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
1838
					} else {
1839
						foreach($value as $vkey => $vval) {
1840
							$result[$key][$vkey] = $vval;
1841
						}
1842
					}
1843
				} else {
1844
					// simply put the value
1845
					$result[$key] = $value;
1846
				}
1847
			}
1848
		}
1849
	}
1850
	return $result;
1851
}
1852

    
1853

    
1854
/*
1855
 * converts a string like "a,b,c,d"
1856
 * into an array like array("a" => "b", "c" => "d")
1857
 */
1858
function explode_assoc($delimiter, $string) {
1859
	$array = explode($delimiter, $string);
1860
	$result = array();
1861
	$numkeys = floor(count($array) / 2);
1862
	for ($i = 0; $i < $numkeys; $i += 1) {
1863
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
1864
	}
1865
	return $result;
1866
}
1867

    
1868
function get_staticroutes($returnsubnetsonly = false) {
1869
	global $config;
1870

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

    
1875
	$allstaticroutes = array();
1876
	$allsubnets = array();
1877
	/* Loop through routes and expand aliases as we find them. */
1878
	foreach ($config['staticroutes']['route'] as $route) {
1879
		if (is_alias($route['network'])) {
1880
			if (!function_exists('filter_expand_alias_array'))
1881
				require_once('filter.inc');
1882
			$subnets = filter_expand_alias_array($route['network']);
1883
			foreach ($subnets as $net) {
1884
				if (!is_subnet($net)) {
1885
					if (is_ipaddrv4($net))
1886
						$net .= "/32";
1887
					else if (is_ipaddrv6($net))
1888
						$net .= "/128";
1889
					/* This must be a hostname, we can't use it. */
1890
					else
1891
						continue;
1892
				}
1893
				$temproute = $route;
1894
				$temproute['network'] = $net;
1895
				$allstaticroutes[] = $temproute;
1896
				$allsubnets[] = $net;
1897
			}
1898
		} elseif (is_subnet($route['network'])) {
1899
			$allstaticroutes[] = $route;
1900
			$allsubnets[] = $route['network'];
1901
		}
1902
	}
1903
	if ($returnsubnetsonly)
1904
		return $allsubnets;
1905
	else
1906
		return $allstaticroutes;
1907
}
1908

    
1909
/****f* util/get_alias_list
1910
 * NAME
1911
 *   get_alias_list - Provide a list of aliases.
1912
 * INPUTS
1913
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
1914
 * RESULT
1915
 *   Array containing list of aliases.
1916
 *   If $type is unspecified, all aliases are returned.
1917
 *   If $type is a string, all aliases of the type specified in $type are returned.
1918
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
1919
 */
1920
function get_alias_list($type = null) {
1921
	global $config;
1922
	$result = array();
1923
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
1924
		foreach ($config['aliases']['alias'] as $alias) {
1925
			if ($type === null) {
1926
				$result[] = $alias['name'];
1927
			}
1928
			else if (is_array($type)) {
1929
				if (in_array($alias['type'], $type)) {
1930
					$result[] = $alias['name'];
1931
				}
1932
			}
1933
			else if ($type === $alias['type']) {
1934
				$result[] = $alias['name'];
1935
			}
1936
		}
1937
	}		
1938
	return $result;
1939
}
1940

    
1941
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
1942
function array_exclude($needle, $haystack) {
1943
	$result = array();
1944
	if (is_array($haystack)) {
1945
		foreach ($haystack as $thing) {
1946
			if ($needle !== $thing) {
1947
				$result[] = $thing;
1948
			}
1949
		}
1950
	}
1951
	return $result;
1952
}
1953

    
1954
function setup_library_paths() {
1955
	$current_library_paths = explode(":", exec("/sbin/ldconfig -r | /usr/bin/grep 'search directories' | /usr/bin/awk '{print $3;}'"));
1956
	$pbi_library_paths = array_merge(glob("/usr/pbi/*/lib", GLOB_ONLYDIR), glob("/usr/pbi/*/lib/*", GLOB_ONLYDIR));
1957
	foreach ($pbi_library_paths as $pbilib) {
1958
		if (!in_array($pbilib, $current_library_paths))
1959
			exec("/sbin/ldconfig -m {$pbilib}");
1960
	}
1961
}
1962

    
1963
function get_current_theme() {
1964
	global $config, $g;
1965
	/*
1966
	 *   if user has selected a custom template, use it.
1967
	 *   otherwise default to pfsense tempalte
1968
	 */
1969
	if (($g["disablethemeselection"] === true) && !empty($g["default_theme"]) && (is_dir($g["www_path"].'/themes/'.$g["default_theme"])))
1970
		$theme = $g["default_theme"];
1971
	elseif($config['theme'] <> "" && (is_dir($g["www_path"].'/themes/'.$config['theme'])))
1972
		$theme = $config['theme'];
1973
	else
1974
		$theme = "pfsense";
1975
	/*
1976
	 *  If this device is an apple ipod/iphone
1977
	 *  switch the theme to one that works with it.
1978
	 */
1979
	$lowres_ua = array("iPhone", "iPod", "iPad", "Android", "BlackBerry", "Opera Mini", "Opera Mobi", "PlayBook", "IEMobile");
1980
	foreach($lowres_ua as $useragent)
1981
		if(strstr($_SERVER['HTTP_USER_AGENT'], $useragent))
1982
			$theme = (empty($g['theme_lowres']) && (is_dir($g["www_path"].'/themes/'.$g['theme_lowres']))) ? "pfsense" : $g['theme_lowres'];
1983
	return $theme;
1984
}
1985

    
1986
?>
(54-54/66)