Project

General

Profile

Download (51 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
/* return the configured carp interface list */
717
function get_configured_carp_interface_list() {
718
	global $config;
719

    
720
	$iflist = array();
721

    
722
	if(is_array($config['virtualip']['vip'])) {
723
		$viparr = &$config['virtualip']['vip'];
724
		foreach ($viparr as $vip) {
725
			switch ($vip['mode']) {
726
			case "carp":
727
				$vipif = "{$vip['interface']}_vip{$vip['vhid']}";
728
				$iflist[$vipif] = $vip['subnet'];
729
				break;
730
			}
731
		}
732
	}
733

    
734
	return $iflist;
735
}
736

    
737
/* return the configured IP aliases list */
738
function get_configured_ip_aliases_list($returnfullentry = false) {
739
	global $config;
740

    
741
	$alias_list=array();
742

    
743
	if(is_array($config['virtualip']['vip'])) {
744
		$viparr = &$config['virtualip']['vip'];
745
		foreach ($viparr as $vip) {
746
			if ($vip['mode']=="ipalias") {
747
				if ($returnfullentry)
748
					$alias_list[$vip['subnet']] = $vip;
749
				else
750
					$alias_list[$vip['subnet']] = $vip['interface'];
751
			}
752
		}
753
	}
754

    
755
	return $alias_list;
756
}
757

    
758
/* return all configured aliases list (IP, carp, proxyarp and other) */
759
function get_configured_vips_list() {
760
	global $config;
761

    
762
	$alias_list=array();
763

    
764
	if(is_array($config['virtualip']['vip'])) {
765
		$viparr = &$config['virtualip']['vip'];
766
		foreach ($viparr as $vip)
767
			$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
768
	}
769

    
770
	return $alias_list;
771
}
772

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

    
786
	return strnatcmp($a, $b);
787
}
788

    
789
/* return the configured interfaces list. */
790
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
791
	global $config;
792

    
793
	$iflist = array();
794

    
795
	/* if list */
796
	foreach($config['interfaces'] as $if => $ifdetail) {
797
		if ($only_opt && ($if == "wan" || $if == "lan"))
798
			continue;
799
		if (isset($ifdetail['enable']) || $withdisabled == true)
800
			$iflist[$if] = $if;
801
	}
802

    
803
	return $iflist;
804
}
805

    
806
/* return the configured interfaces list. */
807
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
808
	global $config;
809

    
810
	$iflist = array();
811

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

    
823
	return $iflist;
824
}
825

    
826
/* return the configured interfaces list with their description. */
827
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
828
	global $config;
829

    
830
	$iflist = array();
831

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

    
844
	return $iflist;
845
}
846

    
847
/*
848
 *   get_configured_ip_addresses() - Return a list of all configured
849
 *   interfaces IP Addresses
850
 *
851
 */
852
function get_configured_ip_addresses() {
853
	global $config;
854

    
855
	if (!function_exists('get_interface_ip'))
856
		require_once("interfaces.inc");
857
	$ip_array = array();
858
	$interfaces = get_configured_interface_list();
859
	if (is_array($interfaces)) {
860
		foreach($interfaces as $int) {
861
			$ipaddr = get_interface_ip($int);
862
			$ip_array[$int] = $ipaddr;
863
		}
864
	}
865
	$interfaces = get_configured_carp_interface_list();
866
	if (is_array($interfaces)) 
867
		foreach($interfaces as $int => $ipaddr) 
868
			$ip_array[$int] = $ipaddr;
869

    
870
	/* pppoe server */
871
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
872
		foreach($config['pppoes']['pppoe'] as $pppoe) {
873
			if ($pppoe['mode'] == "server") {
874
				if(is_ipaddr($pppoe['localip'])) {
875
					$int = "pppoes". $pppoe['pppoeid'];
876
					$ip_array[$int] = $pppoe['localip'];
877
				}
878
			}
879
		}
880
	}
881

    
882
	return $ip_array;
883
}
884

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

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

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

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

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

    
1072
/* wrapper for exec() */
1073
function mwexec($command, $mute = false, $clearsigmask = false) {
1074
	global $g;
1075

    
1076
	if ($g['debug']) {
1077
		if (!$_SERVER['REMOTE_ADDR'])
1078
			echo "mwexec(): $command\n";
1079
	}
1080
	$oarr = array();
1081
	$retval = 0;
1082

    
1083
	if ($clearsigmask) {
1084
		$oldset = array();
1085
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1086
	}
1087
	$garbage = exec("$command 2>&1", $oarr, $retval);
1088
	if ($clearsigmask) {
1089
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1090
	}
1091

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

    
1101
/* wrapper for exec() in background */
1102
function mwexec_bg($command, $clearsigmask = false) {
1103
	global $g;
1104

    
1105
	if ($g['debug']) {
1106
		if (!$_SERVER['REMOTE_ADDR'])
1107
			echo "mwexec(): $command\n";
1108
	}
1109

    
1110
	if ($clearsigmask) {
1111
		$oldset = array();
1112
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1113
	}
1114
	$_gb = exec("/usr/bin/nohup $command > /dev/null 2>&1 &");
1115
	if ($clearsigmask) {
1116
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1117
	}
1118
	unset($_gb);
1119
}
1120

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

    
1135
	$aliastable = array();
1136

    
1137
	if (is_array($config['aliases']['alias'])) {
1138
		foreach ($config['aliases']['alias'] as $alias) {
1139
			if ($alias['name'])
1140
				$aliastable[$alias['name']] = $alias['address'];
1141
		}
1142
	}
1143
}
1144

    
1145
/* check if an alias exists */
1146
function is_alias($name) {
1147
	global $aliastable;
1148

    
1149
	return isset($aliastable[$name]);
1150
}
1151

    
1152
function alias_get_type($name) {
1153
        global $config;
1154
        
1155
	if (is_array($config['aliases']['alias'])) {
1156
		foreach ($config['aliases']['alias'] as $alias) {
1157
			if ($name == $alias['name'])
1158
				return $alias['type'];
1159
		}
1160
	}
1161

    
1162
        return "";
1163
}
1164

    
1165
/* expand a host or network alias, if necessary */
1166
function alias_expand($name) {
1167
	global $aliastable;
1168

    
1169
	if (isset($aliastable[$name]))
1170
		return "\${$name}";
1171
	else if (is_ipaddr($name) || is_subnet($name) || is_port($name))
1172
		return "{$name}";
1173
	else
1174
		return null;
1175
}
1176

    
1177
function alias_expand_urltable($name) {
1178
	global $config;
1179
	$urltable_prefix = "/var/db/aliastables/";
1180
	$urltable_filename = $urltable_prefix . $name . ".txt";
1181

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

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

    
1209
function subnet_expand($subnet) {
1210
	if (is_subnetv4($subnet)) {
1211
		return subnetv4_expand($subnet);
1212
	} else if (is_subnetv6($subnet)) {
1213
		return subnetv6_expand($subnet);
1214
	} else {
1215
		return $subnet;
1216
	}
1217
}
1218

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

    
1232
/* find out whether two subnets overlap */
1233
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
1234

    
1235
	if (!is_numeric($bits1))
1236
		$bits1 = 32;
1237
	if (!is_numeric($bits2))
1238
		$bits2 = 32;
1239

    
1240
	if ($bits1 < $bits2)
1241
		$relbits = $bits1;
1242
	else
1243
		$relbits = $bits2;
1244

    
1245
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
1246
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
1247

    
1248
	return ($sn1 == $sn2);
1249
}
1250

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

    
1261
/* compare two IP addresses */
1262
function ipcmp($a, $b) {
1263
	if (ip_less_than($a, $b))
1264
		return -1;
1265
	else if (ip_greater_than($a, $b))
1266
		return 1;
1267
	else
1268
		return 0;
1269
}
1270

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

    
1282
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1283
function verify_digital_signature($fname) {
1284
	global $g;
1285

    
1286
	if(!file_exists("/usr/local/sbin/gzsig"))
1287
		return 4;
1288

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

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

    
1298
	if ($arpoutput[0]) {
1299
		$arpi = explode(" ", $arpoutput[0]);
1300
		$macaddr = $arpi[3];
1301
		if (is_macaddr($macaddr))
1302
			return $macaddr;
1303
		else
1304
			return false;
1305
	}
1306

    
1307
	return false;
1308
}
1309

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

    
1319
function mac_format($clientmac) {
1320
    global $config, $cpzone;
1321

    
1322
    $mac = explode(":", $clientmac);
1323
    $mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1324

    
1325
    switch($mac_format) {
1326
        case 'singledash':
1327
		return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1328

    
1329
        case 'ietf':
1330
		return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1331

    
1332
        case 'cisco':
1333
		return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1334

    
1335
        case 'unformatted':
1336
		return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1337

    
1338
        default:
1339
		return $clientmac;
1340
    }
1341
}
1342

    
1343
function resolve_retry($hostname, $retries = 5) {
1344

    
1345
	if (is_ipaddr($hostname))
1346
		return $hostname;
1347

    
1348
       for ($i = 0; $i < $retries; $i++) {
1349
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1350
               $ip = gethostbyname($hostname);
1351

    
1352
		if ($ip && $ip != $hostname) {
1353
			/* success */
1354
			return $ip;
1355
		}
1356

    
1357
		sleep(1);
1358
	}
1359

    
1360
	return false;
1361
}
1362

    
1363
function format_bytes($bytes) {
1364
	if ($bytes >= 1073741824) {
1365
		return sprintf("%.2f GB", $bytes/1073741824);
1366
	} else if ($bytes >= 1048576) {
1367
		return sprintf("%.2f MB", $bytes/1048576);
1368
	} else if ($bytes >= 1024) {
1369
		return sprintf("%.0f KB", $bytes/1024);
1370
	} else {
1371
		return sprintf("%d bytes", $bytes);
1372
	}
1373
}
1374

    
1375
function update_filter_reload_status($text) {
1376
	global $g;
1377

    
1378
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1379
}
1380

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

    
1398
				if (empty($filter_regex) || preg_match($filter_regex, $file))
1399
					array_push($dir_array, $file);
1400
			}
1401
			closedir($dh);
1402
		}
1403
	}
1404
	return $dir_array;
1405
}
1406

    
1407
function run_plugins($directory) {
1408
	global $config, $g;
1409

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

    
1422
/*
1423
 *    safe_mkdir($path, $mode = 0755)
1424
 *    create directory if it doesn't already exist and isn't a file!
1425
 */
1426
function safe_mkdir($path, $mode=0755) {
1427
	global $g;
1428

    
1429
	if (!is_file($path) && !is_dir($path)) {
1430
		return @mkdir($path, $mode, true);
1431
	} else {
1432
		return false;
1433
	}
1434
}
1435

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

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

    
1461
	if (is_array($names)) {
1462
		$name_list = array();
1463
		foreach ($names as $name) {
1464
			$name_list[] = escapeshellarg($name);
1465
		}
1466
	} else
1467
		$name_list = array(escapeshellarg($names));
1468

    
1469
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1470
	$values = array();
1471
	foreach ($output as $line) {
1472
		$line = explode(": ", $line, 2);
1473
		if (count($line) == 2)
1474
			$values[$line[0]] = $line[1];
1475
	}
1476

    
1477
	return $values;
1478
}
1479

    
1480
/*
1481
 * set_sysctl($value_list)
1482
 * Set sysctl OID's listed as key/value pairs and return
1483
 * an array with keys set for those that succeeded
1484
 */
1485
function set_sysctl($values) {
1486
	if (empty($values))
1487
		return array();
1488

    
1489
	$value_list = array();
1490
	foreach ($values as $key => $value) {
1491
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1492
	}
1493

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

    
1496
	/* Retry individually if failed (one or more read-only) */
1497
	if ($success <> 0 && count($value_list) > 1) {
1498
		foreach ($value_list as $value) {
1499
			exec("/sbin/sysctl -i " . $value, $output);
1500
		}
1501
	}
1502

    
1503
	$ret = array();
1504
	foreach ($output as $line) {
1505
		$line = explode(": ", $line, 2);
1506
		if (count($line) == 2)
1507
			$ret[$line[0]] = true;
1508
	}
1509

    
1510
	return $ret;
1511
}
1512

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

    
1522
	$physmem = trim(`sysctl -n hw.physmem`, " \n");
1523
	$realmem = trim(`sysctl -n hw.realmem`, " \n");
1524
	/* convert from bytes to megabytes */
1525
	return array(($physmem/1048576),($realmem/1048576));
1526
}
1527

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

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

    
1554
function start_devd() {
1555
	global $g;
1556

    
1557
	if ($g['platform'] == 'jail')
1558
		return;
1559
	exec("/sbin/devd");
1560
	sleep(1);
1561
}
1562

    
1563
function is_interface_vlan_mismatch() {
1564
	global $config, $g;
1565

    
1566
	if (is_array($config['vlans']['vlan'])) {
1567
		foreach ($config['vlans']['vlan'] as $vlan) {
1568
			if (does_interface_exist($vlan['if']) == false)
1569
				return true;
1570
		}
1571
	}
1572

    
1573
	return false;
1574
}
1575

    
1576
function is_interface_mismatch() {
1577
	global $config, $g;
1578

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

    
1597
	if ($g['minimum_nic_count'] > $i) {
1598
		$do_assign = true;
1599
	} else if (file_exists("{$g['tmp_path']}/assign_complete"))
1600
		$do_assign = false;
1601

    
1602
	if (!empty($missing_interfaces) && $do_assign)
1603
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1604
	else
1605
		@unlink("{$g['tmp_path']}/missing_interfaces");
1606

    
1607
	return $do_assign;
1608
}
1609

    
1610
/* sync carp entries to other firewalls */
1611
function carp_sync_client() {
1612
	global $g;
1613
	send_event("filter sync");
1614
}
1615

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

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

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

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

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

    
1773
function is_file_included($file = "") {
1774
	$files = get_included_files();
1775
	if (in_array($file, $files))
1776
		return true;
1777
	
1778
	return false;
1779
}
1780

    
1781
/*
1782
 * Replace a value on a deep associative array using regex
1783
 */
1784
function array_replace_values_recursive($data, $match, $replace) {
1785
	if (empty($data))
1786
		return $data;
1787

    
1788
	if (is_string($data))
1789
		$data = preg_replace("/{$match}/", $replace, $data);
1790
	else if (is_array($data))
1791
		foreach ($data as $k => $v)
1792
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
1793

    
1794
	return $data;
1795
}
1796

    
1797
/*
1798
	This function was borrowed from a comment on PHP.net at the following URL:
1799
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
1800
 */
1801
function array_merge_recursive_unique($array0, $array1) {
1802

    
1803
	$arrays = func_get_args();
1804
	$remains = $arrays;
1805

    
1806
	// We walk through each arrays and put value in the results (without
1807
	// considering previous value).
1808
	$result = array();
1809

    
1810
	// loop available array
1811
	foreach($arrays as $array) {
1812

    
1813
		// The first remaining array is $array. We are processing it. So
1814
		// we remove it from remaing arrays.
1815
        array_shift($remains);
1816

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

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

    
1848

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

    
1863
function get_staticroutes($returnsubnetsonly = false) {
1864
	global $config;
1865

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

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

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

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

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

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

    
1981
?>
(54-54/66)