Project

General

Profile

Download (50.9 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
	return Net_IPv6::checkIPv6($ipaddr);
481
}
482

    
483
/* returns true if $ipaddr is a valid dotted IPv4 address */
484
function is_ipaddrv4($ipaddr) {
485
	if (!is_string($ipaddr) || empty($ipaddr))
486
		return false;
487

    
488
	$ip_long = ip2long($ipaddr);
489
	$ip_reverse = long2ip32($ip_long);
490

    
491
	if ($ipaddr == $ip_reverse)
492
		return true;
493
	else
494
		return false;
495
}
496

    
497
/* returns true if $ipaddr is a valid linklocal address */
498
function is_linklocal($ipaddr) {
499
	return preg_match('/^fe80:/i', $ipaddr);
500
}
501

    
502
/* returns true if $ipaddr is a valid literal IPv6 address */
503
function is_literalipaddrv6($ipaddr) {
504
	if(preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match))
505
		$ipaddr = $match[1];
506
	else
507
		return false;
508

    
509
	return is_ipaddrv6($ipaddr);
510
}
511

    
512
function is_ipaddrwithport($ipport) {
513
	$parts = explode(":", $ipport);
514
	$port = array_pop($parts);
515
	if (count($parts) == 1) {
516
		return is_ipaddrv4($parts[0]) && is_port($port);
517
	} elseif (count($parts) > 1) {
518
		return is_literalipaddrv6(implode(":", $parts)) && is_port($port);
519
	} else {
520
		return false;
521
	}
522
}
523

    
524
function is_hostnamewithport($hostport) {
525
	$parts = explode(":", $hostport);
526
	$port = array_pop($parts);
527
	if (count($parts) == 1) {
528
		return is_hostname($parts[0]) && is_port($port);
529
	} else {
530
		return false;
531
	}
532
}
533

    
534
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
535
function is_ipaddroralias($ipaddr) {
536
	global $config;
537

    
538
	if (is_alias($ipaddr)) {
539
		if (is_array($config['aliases']['alias'])) {
540
			foreach ($config['aliases']['alias'] as $alias) {
541
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type']))
542
					return true;
543
			}
544
		}
545
		return false;
546
	} else
547
		return is_ipaddr($ipaddr);
548

    
549
}
550

    
551
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format */
552
function is_subnet($subnet) {
553
	if(is_subnetv4($subnet)) {
554
		return true;
555
	}
556
	if(is_subnetv6($subnet)) {
557
		return true;
558
	}
559
	return false;
560
}
561

    
562
/* returns true if $subnet is a valid IPv4 subnet in CIDR format */
563
function is_subnetv4($subnet) {
564
	if (!is_string($subnet))
565
		return false;
566

    
567
	list($hp,$np) = explode('/', $subnet);
568

    
569
	if (!is_ipaddrv4($hp))
570
		return false;
571

    
572
	if (!is_numeric($np) || ($np < 1) || ($np > 32))
573
		return false;
574

    
575
	return true;
576
}
577

    
578
/* returns true if $subnet is a valid IPv6 subnet in CIDR format */
579
function is_subnetv6($subnet) {
580
	if (!is_string($subnet))
581
		return false;
582

    
583
	list($hp,$np) = explode('/', $subnet);
584

    
585
	if (!is_ipaddrv6($hp))
586
		return false;
587

    
588
	if (!is_numeric($np) || ($np < 1) || ($np > 128))
589
		return false;
590

    
591
	return true;
592
}
593

    
594

    
595
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
596
function is_subnetoralias($subnet) {
597
	global $aliastable;
598

    
599
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet]))
600
		return true;
601
	else
602
		return is_subnet($subnet);
603
}
604

    
605
/* returns true if $hostname is a valid hostname */
606
function is_hostname($hostname) {
607
	if (!is_string($hostname))
608
		return false;
609

    
610
	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))
611
		return true;
612
	else
613
		return false;
614
}
615

    
616
/* returns true if $domain is a valid domain name */
617
function is_domain($domain) {
618
	if (!is_string($domain))
619
		return false;
620

    
621
	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))
622
		return true;
623
	else
624
		return false;
625
}
626

    
627
/* returns true if $macaddr is a valid MAC address */
628
function is_macaddr($macaddr, $partial=false) {
629
	$repeat = ($partial) ? '1,5' : '5';
630
	return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false;
631
}
632

    
633
/* returns true if $name is a valid name for an alias */
634
/* returns NULL if a reserved word is used */
635
function is_validaliasname($name) {
636
	/* Array of reserved words */
637
	$reserved = array("port", "pass");
638
	if (in_array($name, $reserved, true))
639
		return; /* return NULL */
640
	if (!preg_match("/[^a-zA-Z0-9_]/", $name) && (strlen($name) < 32))
641
		return true;
642
	else
643
		return false;
644
}
645

    
646
/* returns true if $port is a valid TCP/UDP port */
647
function is_port($port) {
648
	$tmpports = explode(":", $port);
649
	foreach($tmpports as $tmpport) {
650
		if (getservbyname($tmpport, "tcp") || getservbyname($tmpport, "udp"))
651
			continue;
652
		if (!ctype_digit($tmpport))
653
			return false;
654
		else if ((intval($tmpport) < 1) || (intval($tmpport) > 65535))
655
			return false;
656
	}
657
	return true;
658
}
659

    
660
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
661
function is_portrange($portrange) {
662
	$ports = explode(":", $portrange);
663

    
664
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
665
}
666

    
667
/* returns true if $port is a valid port number or an alias thereof */
668
function is_portoralias($port) {
669
	global $config;
670

    
671
	if (is_alias($port)) {
672
		if (is_array($config['aliases']['alias'])) {
673
			foreach ($config['aliases']['alias'] as $alias) {
674
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type']))
675
					return true;
676
				}
677
			}
678
			return false;
679
	} else
680
		return is_port($port);
681
}
682

    
683
/* create ranges of sequential port numbers (200:215) and remove duplicates */
684
function group_ports($ports) {
685
	if (!is_array($ports) || empty($ports))
686
		return;
687

    
688
	$uniq = array();
689
	foreach ($ports as $port) {
690
		if (is_portrange($port)) {
691
			list($begin, $end) = explode(":", $port);
692
			if ($begin > $end) {
693
				$aux = $begin;
694
				$begin = $end;
695
				$end = $aux;
696
			}
697
			for ($i = $begin; $i <= $end; $i++)
698
				if (!in_array($i, $uniq))
699
					$uniq[] = $i;
700
		} else if (is_port($port)) {
701
			if (!in_array($port, $uniq))
702
				$uniq[] = $port;
703
		}
704
	}
705
	sort($uniq, SORT_NUMERIC);
706

    
707
	$result = array();
708
	foreach ($uniq as $idx => $port) {
709
		if ($idx == 0) {
710
			$result[] = $port;
711
			continue;
712
		}
713

    
714
		$last = end($result);
715
		if (is_portrange($last))
716
			list($begin, $end) = explode(":", $last);
717
		else
718
			$begin = $end = $last;
719

    
720
		if ($port == ($end+1)) {
721
			$end++;
722
			$result[count($result)-1] = "{$begin}:{$end}";
723
		} else {
724
			$result[] = $port;
725
		}
726
	}
727

    
728
	return $result;
729
}
730

    
731
/* returns true if $val is a valid shaper bandwidth value */
732
function is_valid_shaperbw($val) {
733
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
734
}
735

    
736
/* returns true if $test is in the range between $start and $end */
737
function is_inrange_v4($test, $start, $end) {
738
	if ( (ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start)) )
739
		return true;
740
	else
741
		return false;
742
}
743

    
744
/* returns true if $test is in the range between $start and $end */
745
function is_inrange_v6($test, $start, $end) {
746
	if ( (inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start)) )
747
		return true;
748
	else
749
		return false;
750
}
751

    
752
/* return the configured carp interface list */
753
function get_configured_carp_interface_list() {
754
	global $config;
755

    
756
	$iflist = array();
757

    
758
	if(is_array($config['virtualip']['vip'])) {
759
		$viparr = &$config['virtualip']['vip'];
760
		foreach ($viparr as $vip) {
761
			switch ($vip['mode']) {
762
			case "carp":
763
				$vipif = "{$vip['interface']}_vip{$vip['vhid']}";
764
				$iflist[$vipif] = $vip['subnet'];
765
				break;
766
			}
767
		}
768
	}
769

    
770
	return $iflist;
771
}
772

    
773
/* return the configured IP aliases list */
774
function get_configured_ip_aliases_list($returnfullentry = false) {
775
	global $config;
776

    
777
	$alias_list=array();
778

    
779
	if(is_array($config['virtualip']['vip'])) {
780
		$viparr = &$config['virtualip']['vip'];
781
		foreach ($viparr as $vip) {
782
			if ($vip['mode']=="ipalias") {
783
				if ($returnfullentry)
784
					$alias_list[$vip['subnet']] = $vip;
785
				else
786
					$alias_list[$vip['subnet']] = $vip['interface'];
787
			}
788
		}
789
	}
790

    
791
	return $alias_list;
792
}
793

    
794
/* return all configured aliases list (IP, carp, proxyarp and other) */
795
function get_configured_vips_list() {
796
	global $config;
797

    
798
	$alias_list=array();
799

    
800
	if(is_array($config['virtualip']['vip'])) {
801
		$viparr = &$config['virtualip']['vip'];
802
		foreach ($viparr as $vip)
803
			$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
804
	}
805

    
806
	return $alias_list;
807
}
808

    
809
/* comparison function for sorting by the order in which interfaces are normally created */
810
function compare_interface_friendly_names($a, $b) {
811
	if ($a == $b)
812
		return 0;
813
	else if ($a == 'wan')
814
		return -1;
815
	else if ($b == 'wan')
816
		return 1;
817
	else if ($a == 'lan')
818
		return -1;
819
	else if ($b == 'lan')
820
		return 1;
821

    
822
	return strnatcmp($a, $b);
823
}
824

    
825
/* return the configured interfaces list. */
826
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
827
	global $config;
828

    
829
	$iflist = array();
830

    
831
	/* if list */
832
	foreach($config['interfaces'] as $if => $ifdetail) {
833
		if ($only_opt && ($if == "wan" || $if == "lan"))
834
			continue;
835
		if (isset($ifdetail['enable']) || $withdisabled == true)
836
			$iflist[$if] = $if;
837
	}
838

    
839
	return $iflist;
840
}
841

    
842
/* return the configured interfaces list. */
843
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
844
	global $config;
845

    
846
	$iflist = array();
847

    
848
	/* if list */
849
	foreach($config['interfaces'] as $if => $ifdetail) {
850
		if ($only_opt && ($if == "wan" || $if == "lan"))
851
			continue;
852
		if (isset($ifdetail['enable']) || $withdisabled == true) {
853
			$tmpif = get_real_interface($if);
854
			if (!empty($tmpif))
855
				$iflist[$tmpif] = $if;
856
		}
857
	}
858

    
859
	return $iflist;
860
}
861

    
862
/* return the configured interfaces list with their description. */
863
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
864
	global $config;
865

    
866
	$iflist = array();
867

    
868
	/* if list */
869
	foreach($config['interfaces'] as $if => $ifdetail) {
870
		if ($only_opt && ($if == "wan" || $if == "lan"))
871
			continue;
872
		if (isset($ifdetail['enable']) || $withdisabled == true) {
873
			if(empty($ifdetail['descr']))
874
				$iflist[$if] = strtoupper($if);
875
			else
876
				$iflist[$if] = strtoupper($ifdetail['descr']);
877
		}
878
	}
879

    
880
	return $iflist;
881
}
882

    
883
/*
884
 *   get_configured_ip_addresses() - Return a list of all configured
885
 *   interfaces IP Addresses
886
 *
887
 */
888
function get_configured_ip_addresses() {
889
	global $config;
890

    
891
	if (!function_exists('get_interface_ip'))
892
		require_once("interfaces.inc");
893
	$ip_array = array();
894
	$interfaces = get_configured_interface_list();
895
	if (is_array($interfaces)) {
896
		foreach($interfaces as $int) {
897
			$ipaddr = get_interface_ip($int);
898
			$ip_array[$int] = $ipaddr;
899
		}
900
	}
901
	$interfaces = get_configured_carp_interface_list();
902
	if (is_array($interfaces))
903
		foreach($interfaces as $int => $ipaddr)
904
			$ip_array[$int] = $ipaddr;
905

    
906
	/* pppoe server */
907
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
908
		foreach($config['pppoes']['pppoe'] as $pppoe) {
909
			if ($pppoe['mode'] == "server") {
910
				if(is_ipaddr($pppoe['localip'])) {
911
					$int = "pppoes". $pppoe['pppoeid'];
912
					$ip_array[$int] = $pppoe['localip'];
913
				}
914
			}
915
		}
916
	}
917

    
918
	return $ip_array;
919
}
920

    
921
/*
922
 *   get_configured_ipv6_addresses() - Return a list of all configured
923
 *   interfaces IPv6 Addresses
924
 *
925
 */
926
function get_configured_ipv6_addresses() {
927
	require_once("interfaces.inc");
928
	$ipv6_array = array();
929
	$interfaces = get_configured_interface_list();
930
	if(is_array($interfaces)) {
931
		foreach($interfaces as $int) {
932
			$ipaddrv6 = get_interface_ipv6($int);
933
			$ipv6_array[$int] = $ipaddrv6;
934
		}
935
	}
936
	$interfaces = get_configured_carp_interface_list();
937
	if(is_array($interfaces))
938
		foreach($interfaces as $int => $ipaddrv6)
939
			$ipv6_array[$int] = $ipaddrv6;
940
	return $ipv6_array;
941
}
942

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

    
1042
			case "friendly":
1043
				if($friendly != "") {
1044
					$toput['if'] = $ifname;
1045
					$iflist[$friendly] = $toput;
1046
				}
1047
				break;
1048
			}
1049
		}
1050
	}
1051
	return $iflist;
1052
}
1053

    
1054
/****f* util/log_error
1055
* NAME
1056
*   log_error  - Sends a string to syslog.
1057
* INPUTS
1058
*   $error     - string containing the syslog message.
1059
* RESULT
1060
*   null
1061
******/
1062
function log_error($error) {
1063
	global $g;
1064
	$page = $_SERVER['SCRIPT_NAME'];
1065
	if (empty($page)) {
1066
		$files = get_included_files();
1067
		$page = basename($files[0]);
1068
	}
1069
	syslog(LOG_ERR, "$page: $error");
1070
	if ($g['debug'])
1071
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1072
	return;
1073
}
1074

    
1075
/****f* util/log_auth
1076
* NAME
1077
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1078
* INPUTS
1079
*   $error     - string containing the syslog message.
1080
* RESULT
1081
*   null
1082
******/
1083
function log_auth($error) {
1084
	global $g;
1085
	$page = $_SERVER['SCRIPT_NAME'];
1086
	syslog(LOG_AUTH, "$page: $error");
1087
	if ($g['debug'])
1088
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1089
	return;
1090
}
1091

    
1092
/****f* util/exec_command
1093
 * NAME
1094
 *   exec_command - Execute a command and return a string of the result.
1095
 * INPUTS
1096
 *   $command   - String of the command to be executed.
1097
 * RESULT
1098
 *   String containing the command's result.
1099
 * NOTES
1100
 *   This function returns the command's stdout and stderr.
1101
 ******/
1102
function exec_command($command) {
1103
	$output = array();
1104
	exec($command . ' 2>&1 ', $output);
1105
	return(implode("\n", $output));
1106
}
1107

    
1108
/* wrapper for exec() */
1109
function mwexec($command, $mute = false, $clearsigmask = false) {
1110
	global $g;
1111

    
1112
	if ($g['debug']) {
1113
		if (!$_SERVER['REMOTE_ADDR'])
1114
			echo "mwexec(): $command\n";
1115
	}
1116
	$oarr = array();
1117
	$retval = 0;
1118

    
1119
	if ($clearsigmask) {
1120
		$oldset = array();
1121
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1122
	}
1123
	$garbage = exec("$command 2>&1", $oarr, $retval);
1124
	if ($clearsigmask) {
1125
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1126
	}
1127

    
1128
	if(isset($config['system']['developerspew']))
1129
		$mute = false;
1130
	if(($retval <> 0) && ($mute === false)) {
1131
		$output = implode(" ", $oarr);
1132
		log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, $output));
1133
	}
1134
	return $retval;
1135
}
1136

    
1137
/* wrapper for exec() in background */
1138
function mwexec_bg($command, $clearsigmask = false) {
1139
	global $g;
1140

    
1141
	if ($g['debug']) {
1142
		if (!$_SERVER['REMOTE_ADDR'])
1143
			echo "mwexec(): $command\n";
1144
	}
1145

    
1146
	if ($clearsigmask) {
1147
		$oldset = array();
1148
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1149
	}
1150
	$_gb = exec("/usr/bin/nohup $command > /dev/null 2>&1 &");
1151
	if ($clearsigmask) {
1152
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1153
	}
1154
	unset($_gb);
1155
}
1156

    
1157
/* unlink a file, if it exists */
1158
function unlink_if_exists($fn) {
1159
	$to_do = glob($fn);
1160
	if(is_array($to_do)) {
1161
		foreach($to_do as $filename)
1162
			@unlink($filename);
1163
	} else {
1164
		@unlink($fn);
1165
	}
1166
}
1167
/* make a global alias table (for faster lookups) */
1168
function alias_make_table($config) {
1169
	global $aliastable;
1170

    
1171
	$aliastable = array();
1172

    
1173
	if (is_array($config['aliases']['alias'])) {
1174
		foreach ($config['aliases']['alias'] as $alias) {
1175
			if ($alias['name'])
1176
				$aliastable[$alias['name']] = $alias['address'];
1177
		}
1178
	}
1179
}
1180

    
1181
/* check if an alias exists */
1182
function is_alias($name) {
1183
	global $aliastable;
1184

    
1185
	return isset($aliastable[$name]);
1186
}
1187

    
1188
function alias_get_type($name) {
1189
	global $config;
1190

    
1191
	if (is_array($config['aliases']['alias'])) {
1192
		foreach ($config['aliases']['alias'] as $alias) {
1193
			if ($name == $alias['name'])
1194
				return $alias['type'];
1195
		}
1196
	}
1197

    
1198
	return "";
1199
}
1200

    
1201
/* expand a host or network alias, if necessary */
1202
function alias_expand($name) {
1203
	global $aliastable;
1204

    
1205
	if (isset($aliastable[$name]))
1206
		return "\${$name}";
1207
	else if (is_ipaddr($name) || is_subnet($name) || is_port($name))
1208
		return "{$name}";
1209
	else
1210
		return null;
1211
}
1212

    
1213
function alias_expand_urltable($name) {
1214
	global $config;
1215
	$urltable_prefix = "/var/db/aliastables/";
1216
	$urltable_filename = $urltable_prefix . $name . ".txt";
1217

    
1218
	if (is_array($config['aliases']['alias'])) {
1219
		foreach ($config['aliases']['alias'] as $alias) {
1220
			if (($alias['type'] == 'urltable') && ($alias['name'] == $name)) {
1221
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename))
1222
					return $urltable_filename;
1223
				else if (process_alias_urltable($name, $alias["url"], 0, true))
1224
					return $urltable_filename;
1225
			}
1226
		}
1227
	}
1228
	return null;
1229
}
1230

    
1231
function subnet_size($subnet) {
1232
	if (is_subnetv4($subnet)) {
1233
		list ($ip, $bits) = explode("/", $subnet);
1234
		return round(exp(log(2) * (32 - $bits)));
1235
	}
1236
	else if (is_subnetv6($subnet)) {
1237
		list ($ip, $bits) = explode("/", $subnet);
1238
		return round(exp(log(2) * (128 - $bits)));
1239
	}
1240
	else {
1241
		return 0;
1242
	}
1243
}
1244

    
1245
function subnet_expand($subnet) {
1246
	if (is_subnetv4($subnet)) {
1247
		return subnetv4_expand($subnet);
1248
	} else if (is_subnetv6($subnet)) {
1249
		return subnetv6_expand($subnet);
1250
	} else {
1251
		return $subnet;
1252
	}
1253
}
1254

    
1255
function subnetv4_expand($subnet) {
1256
	$result = array();
1257
	list ($ip, $bits) = explode("/", $subnet);
1258
	$net  = ip2long($ip);
1259
	$mask = (0xffffffff << (32 - $bits));
1260
	$net &= $mask;
1261
	$size = round(exp(log(2) * (32 - $bits)));
1262
	for ($i = 0; $i < $size; $i += 1) {
1263
		$result[] = long2ip($net | $i);
1264
	}
1265
	return $result;
1266
}
1267

    
1268
/* find out whether two subnets overlap */
1269
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
1270

    
1271
	if (!is_numeric($bits1))
1272
		$bits1 = 32;
1273
	if (!is_numeric($bits2))
1274
		$bits2 = 32;
1275

    
1276
	if ($bits1 < $bits2)
1277
		$relbits = $bits1;
1278
	else
1279
		$relbits = $bits2;
1280

    
1281
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
1282
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
1283

    
1284
	return ($sn1 == $sn2);
1285
}
1286

    
1287
/* find out whether two IPv6 subnets overlap */
1288
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
1289
	$sub1_min = gen_subnetv6($subnet1, $bits1);
1290
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
1291
	$sub2_min = gen_subnetv6($subnet2, $bits2);
1292
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
1293

    
1294
	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));
1295
}
1296

    
1297
/* compare two IP addresses */
1298
function ipcmp($a, $b) {
1299
	if (ip_less_than($a, $b))
1300
		return -1;
1301
	else if (ip_greater_than($a, $b))
1302
		return 1;
1303
	else
1304
		return 0;
1305
}
1306

    
1307
/* return true if $addr is in $subnet, false if not */
1308
function ip_in_subnet($addr,$subnet) {
1309
	if(is_ipaddrv6($addr)) {
1310
		return (Net_IPv6::isInNetmask($addr, $subnet));
1311
	} else { /* XXX: Maybe check for IPv4 */
1312
		list($ip, $mask) = explode('/', $subnet);
1313
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
1314
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
1315
	}
1316
}
1317

    
1318
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1319
function verify_digital_signature($fname) {
1320
	global $g;
1321

    
1322
	if(!file_exists("/usr/local/sbin/gzsig"))
1323
		return 4;
1324

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

    
1328
/* obtain MAC address given an IP address by looking at the ARP table */
1329
function arp_get_mac_by_ip($ip) {
1330
	mwexec("/sbin/ping -c 1 -t 1 {$ip}", true);
1331
	$arpoutput = "";
1332
	exec("/usr/sbin/arp -n {$ip}", $arpoutput);
1333

    
1334
	if ($arpoutput[0]) {
1335
		$arpi = explode(" ", $arpoutput[0]);
1336
		$macaddr = $arpi[3];
1337
		if (is_macaddr($macaddr))
1338
			return $macaddr;
1339
		else
1340
			return false;
1341
	}
1342

    
1343
	return false;
1344
}
1345

    
1346
/* return a fieldname that is safe for xml usage */
1347
function xml_safe_fieldname($fieldname) {
1348
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1349
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1350
			 ':', ',', '.', '\'', '\\'
1351
		);
1352
	return strtolower(str_replace($replace, "", $fieldname));
1353
}
1354

    
1355
function mac_format($clientmac) {
1356
	global $config, $cpzone;
1357

    
1358
	$mac = explode(":", $clientmac);
1359
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1360

    
1361
	switch($mac_format) {
1362
	case 'singledash':
1363
		return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1364

    
1365
	case 'ietf':
1366
		return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1367

    
1368
	case 'cisco':
1369
		return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1370

    
1371
	case 'unformatted':
1372
		return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1373

    
1374
	default:
1375
		return $clientmac;
1376
	}
1377
}
1378

    
1379
function resolve_retry($hostname, $retries = 5) {
1380

    
1381
	if (is_ipaddr($hostname))
1382
		return $hostname;
1383

    
1384
	for ($i = 0; $i < $retries; $i++) {
1385
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1386
		$ip = gethostbyname($hostname);
1387

    
1388
		if ($ip && $ip != $hostname) {
1389
			/* success */
1390
			return $ip;
1391
		}
1392

    
1393
		sleep(1);
1394
	}
1395

    
1396
	return false;
1397
}
1398

    
1399
function format_bytes($bytes) {
1400
	if ($bytes >= 1073741824) {
1401
		return sprintf("%.2f GB", $bytes/1073741824);
1402
	} else if ($bytes >= 1048576) {
1403
		return sprintf("%.2f MB", $bytes/1048576);
1404
	} else if ($bytes >= 1024) {
1405
		return sprintf("%.0f KB", $bytes/1024);
1406
	} else {
1407
		return sprintf("%d bytes", $bytes);
1408
	}
1409
}
1410

    
1411
function update_filter_reload_status($text) {
1412
	global $g;
1413

    
1414
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1415
}
1416

    
1417
/****** util/return_dir_as_array
1418
 * NAME
1419
 *   return_dir_as_array - Return a directory's contents as an array.
1420
 * INPUTS
1421
 *   $dir          - string containing the path to the desired directory.
1422
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1423
 * RESULT
1424
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1425
 ******/
1426
function return_dir_as_array($dir, $filter_regex = '') {
1427
	$dir_array = array();
1428
	if (is_dir($dir)) {
1429
		if ($dh = opendir($dir)) {
1430
			while (($file = readdir($dh)) !== false) {
1431
				if (($file == ".") || ($file == ".."))
1432
					continue;
1433

    
1434
				if (empty($filter_regex) || preg_match($filter_regex, $file))
1435
					array_push($dir_array, $file);
1436
			}
1437
			closedir($dh);
1438
		}
1439
	}
1440
	return $dir_array;
1441
}
1442

    
1443
function run_plugins($directory) {
1444
	global $config, $g;
1445

    
1446
	/* process packager manager custom rules */
1447
	$files = return_dir_as_array($directory);
1448
	if (is_array($files)) {
1449
		foreach ($files as $file) {
1450
			if (stristr($file, ".sh") == true)
1451
				mwexec($directory . $file . " start");
1452
			else if (!is_dir($directory . "/" . $file) && stristr($file,".inc"))
1453
				require_once($directory . "/" . $file);
1454
		}
1455
	}
1456
}
1457

    
1458
/*
1459
 *    safe_mkdir($path, $mode = 0755)
1460
 *    create directory if it doesn't already exist and isn't a file!
1461
 */
1462
function safe_mkdir($path, $mode=0755) {
1463
	global $g;
1464

    
1465
	if (!is_file($path) && !is_dir($path)) {
1466
		return @mkdir($path, $mode, true);
1467
	} else {
1468
		return false;
1469
	}
1470
}
1471

    
1472
/*
1473
 * make_dirs($path, $mode = 0755)
1474
 * create directory tree recursively (mkdir -p)
1475
 */
1476
function make_dirs($path, $mode = 0755) {
1477
	$base = '';
1478
	foreach (explode('/', $path) as $dir) {
1479
		$base .= "/$dir";
1480
		if (!is_dir($base)) {
1481
			if (!@mkdir($base, $mode))
1482
				return false;
1483
		}
1484
	}
1485
	return true;
1486
}
1487

    
1488
/*
1489
 * get_sysctl($names)
1490
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1491
 * name) and return an array of key/value pairs set for those that exist
1492
 */
1493
function get_sysctl($names) {
1494
	if (empty($names))
1495
		return array();
1496

    
1497
	if (is_array($names)) {
1498
		$name_list = array();
1499
		foreach ($names as $name) {
1500
			$name_list[] = escapeshellarg($name);
1501
		}
1502
	} else
1503
		$name_list = array(escapeshellarg($names));
1504

    
1505
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1506
	$values = array();
1507
	foreach ($output as $line) {
1508
		$line = explode(": ", $line, 2);
1509
		if (count($line) == 2)
1510
			$values[$line[0]] = $line[1];
1511
	}
1512

    
1513
	return $values;
1514
}
1515

    
1516
/*
1517
 * set_sysctl($value_list)
1518
 * Set sysctl OID's listed as key/value pairs and return
1519
 * an array with keys set for those that succeeded
1520
 */
1521
function set_sysctl($values) {
1522
	if (empty($values))
1523
		return array();
1524

    
1525
	$value_list = array();
1526
	foreach ($values as $key => $value) {
1527
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1528
	}
1529

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

    
1532
	/* Retry individually if failed (one or more read-only) */
1533
	if ($success <> 0 && count($value_list) > 1) {
1534
		foreach ($value_list as $value) {
1535
			exec("/sbin/sysctl -i " . $value, $output);
1536
		}
1537
	}
1538

    
1539
	$ret = array();
1540
	foreach ($output as $line) {
1541
		$line = explode(": ", $line, 2);
1542
		if (count($line) == 2)
1543
			$ret[$line[0]] = true;
1544
	}
1545

    
1546
	return $ret;
1547
}
1548

    
1549
/*
1550
 *     get_memory()
1551
 *     returns an array listing the amount of
1552
 *     memory installed in the hardware
1553
 *     [0]real and [1]available
1554
 */
1555
function get_memory() {
1556

    
1557
	$real = trim(`sysctl -n hw.physmem`, " \n");
1558
	$avail = trim(`sysctl -n hw.realmem`, " \n");
1559
	/* convert from bytes to megabytes */
1560
	return array(($real/1048576),($avail/1048576));
1561
}
1562

    
1563
function mute_kernel_msgs() {
1564
	global $config;
1565
	// Do not mute serial console.  The kernel gets very very cranky
1566
	// and will start dishing you cannot control tty errors.
1567
	switch (trim(file_get_contents("/etc/platform"))) {
1568
		case "nanobsd":
1569
		case "jail":
1570
			return;
1571
	}
1572
	if($config['system']['enableserial'])
1573
		return;
1574
	exec("/sbin/conscontrol mute on");
1575
}
1576

    
1577
function unmute_kernel_msgs() {
1578
	global $config;
1579
	// Do not mute serial console.  The kernel gets very very cranky
1580
	// and will start dishing you cannot control tty errors.
1581
	switch (trim(file_get_contents("/etc/platform"))) {
1582
		case "nanobsd":
1583
		case "jail":
1584
			return;
1585
	}
1586
	exec("/sbin/conscontrol mute off");
1587
}
1588

    
1589
function start_devd() {
1590
	global $g;
1591

    
1592
	if ($g['platform'] == 'jail')
1593
		return;
1594
	exec("/sbin/devd");
1595
	sleep(1);
1596
}
1597

    
1598
function is_interface_vlan_mismatch() {
1599
	global $config, $g;
1600

    
1601
	if (is_array($config['vlans']['vlan'])) {
1602
		foreach ($config['vlans']['vlan'] as $vlan) {
1603
			if (does_interface_exist($vlan['if']) == false)
1604
				return true;
1605
		}
1606
	}
1607

    
1608
	return false;
1609
}
1610

    
1611
function is_interface_mismatch() {
1612
	global $config, $g;
1613

    
1614
	$do_assign = false;
1615
	$i = 0;
1616
	$missing_interfaces = array();
1617
	if (is_array($config['interfaces'])) {
1618
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1619
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1620
				// Do not check these interfaces.
1621
				$i++;
1622
				continue;
1623
			}
1624
			else if (does_interface_exist($ifcfg['if']) == false) {
1625
				$missing_interfaces[] = $ifcfg['if'];
1626
				$do_assign = true;
1627
			} else
1628
				$i++;
1629
		}
1630
	}
1631

    
1632
	if ($g['minimum_nic_count'] > $i) {
1633
		$do_assign = true;
1634
	} else if (file_exists("{$g['tmp_path']}/assign_complete"))
1635
		$do_assign = false;
1636

    
1637
	if (!empty($missing_interfaces) && $do_assign)
1638
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1639
	else
1640
		@unlink("{$g['tmp_path']}/missing_interfaces");
1641

    
1642
	return $do_assign;
1643
}
1644

    
1645
/* sync carp entries to other firewalls */
1646
function carp_sync_client() {
1647
	global $g;
1648
	send_event("filter sync");
1649
}
1650

    
1651
/****f* util/isAjax
1652
 * NAME
1653
 *   isAjax - reports if the request is driven from prototype
1654
 * INPUTS
1655
 *   none
1656
 * RESULT
1657
 *   true/false
1658
 ******/
1659
function isAjax() {
1660
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
1661
}
1662

    
1663
/****f* util/timeout
1664
 * NAME
1665
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
1666
 * INPUTS
1667
 *   optional, seconds to wait before timeout. Default 9 seconds.
1668
 * RESULT
1669
 *   returns 1 char of user input or null if no input.
1670
 ******/
1671
function timeout($timer = 9) {
1672
	while(!isset($key)) {
1673
		if ($timer >= 9) { echo chr(8) . chr(8) . ($timer==9 ? chr(32) : null)  . "{$timer}";  }
1674
		else { echo chr(8). "{$timer}"; }
1675
		`/bin/stty -icanon min 0 time 25`;
1676
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
1677
		`/bin/stty icanon`;
1678
		if ($key == '')
1679
			unset($key);
1680
		$timer--;
1681
		if ($timer == 0)
1682
			break;
1683
	}
1684
	return $key;
1685
}
1686

    
1687
/****f* util/msort
1688
 * NAME
1689
 *   msort - sort array
1690
 * INPUTS
1691
 *   $array to be sorted, field to sort by, direction of sort
1692
 * RESULT
1693
 *   returns newly sorted array
1694
 ******/
1695
function msort($array, $id="id", $sort_ascending=true) {
1696
	$temp_array = array();
1697
	while(count($array)>0) {
1698
		$lowest_id = 0;
1699
		$index=0;
1700
		foreach ($array as $item) {
1701
			if (isset($item[$id])) {
1702
				if ($array[$lowest_id][$id]) {
1703
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
1704
						$lowest_id = $index;
1705
					}
1706
				}
1707
			}
1708
			$index++;
1709
		}
1710
		$temp_array[] = $array[$lowest_id];
1711
		$array = array_merge(array_slice($array, 0,$lowest_id), array_slice($array, $lowest_id+1));
1712
	}
1713
	if ($sort_ascending) {
1714
		return $temp_array;
1715
	} else {
1716
		return array_reverse($temp_array);
1717
	}
1718
}
1719

    
1720
/****f* util/color
1721
 * NAME
1722
 *   color - outputs a color code to the ansi terminal if supported
1723
 * INPUTS
1724
 *   color code or color name
1725
 * RESULT
1726
 *   Outputs the ansi color sequence for the color specified.  Default resets terminal.
1727
 ******/
1728
function color($color = "0m") {
1729
	/*
1730
		Color codes available:
1731
		 0m reset; clears all colors and styles (to white on black)
1732
		 1m bold on (see below)
1733
		 3m italics on
1734
		 4m underline on
1735
		 7m inverse on; reverses foreground & background colors
1736
		 9m strikethrough on
1737
		 22m bold off (see below)
1738
		 23m italics off
1739
		 24m underline off
1740
		 27m inverse off
1741
		 29m strikethrough off
1742
		 30m set foreground color to black
1743
		 31m set foreground color to red
1744
		 32m set foreground color to green
1745
		 33m set foreground color to yellow
1746
		 34m set foreground color to blue
1747
		 35m set foreground color to magenta (purple)
1748
		 36m set foreground color to cyan
1749
		 37m set foreground color to white
1750
		 40m  set background color to black
1751
		 41m set background color to red
1752
		 42m set background color to green
1753
		 43m set background color to yellow
1754
		 44m set background color to blue
1755
		 45m set background color to magenta (purple)
1756
		 46m set background color to cyan
1757
		 47m set background color to white
1758
		 49m set background color to default (black)
1759
	*/
1760
	// Allow caching of TERM to
1761
	// speedup subequence requests.
1762
	global $TERM;
1763
	if(!$TERM)
1764
		$TERM=`/usr/bin/env | grep color`;
1765
	if(!$TERM)
1766
		$TERM=`/usr/bin/env | grep cons25`;
1767
	if($TERM) {
1768
		$ESCAPE=chr(27);
1769
		switch ($color) {
1770
			case "black":
1771
				return "{$ESCAPE}[30m";
1772
			case "red":
1773
				return "{$ESCAPE}[31m";
1774
			case "green":
1775
				return "{$ESCAPE}[32m";
1776
			case "yellow":
1777
				return "{$ESCAPE}[33m";
1778
			case "blue":
1779
				return "{$ESCAPE}[34m";
1780
			case "magenta":
1781
				return "{$ESCAPE}[35m";
1782
			case "cyan":
1783
				return "{$ESCAPE}[36m";
1784
			case "white":
1785
				return "{$ESCAPE}[37m";
1786
			case "default":
1787
				return "{$ESCAPE}[39m";
1788
		}
1789
		return "{$ESCAPE}[{$color}";
1790
	}
1791
}
1792

    
1793
/****f* util/is_URL
1794
 * NAME
1795
 *   is_URL
1796
 * INPUTS
1797
 *   string to check
1798
 * RESULT
1799
 *   Returns true if item is a URL
1800
 ******/
1801
function is_URL($url) {
1802
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
1803
	if($match)
1804
		return true;
1805
	return false;
1806
}
1807

    
1808
function is_file_included($file = "") {
1809
	$files = get_included_files();
1810
	if (in_array($file, $files))
1811
		return true;
1812

    
1813
	return false;
1814
}
1815

    
1816
/*
1817
	This function was borrowed from a comment on PHP.net at the following URL:
1818
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
1819
 */
1820
function array_merge_recursive_unique($array0, $array1) {
1821

    
1822
	$arrays = func_get_args();
1823
	$remains = $arrays;
1824

    
1825
	// We walk through each arrays and put value in the results (without
1826
	// considering previous value).
1827
	$result = array();
1828

    
1829
	// loop available array
1830
	foreach($arrays as $array) {
1831

    
1832
		// The first remaining array is $array. We are processing it. So
1833
		// we remove it from remaing arrays.
1834
		array_shift($remains);
1835

    
1836
		// We don't care non array param, like array_merge since PHP 5.0.
1837
		if(is_array($array)) {
1838
			// Loop values
1839
			foreach($array as $key => $value) {
1840
				if(is_array($value)) {
1841
					// we gather all remaining arrays that have such key available
1842
					$args = array();
1843
					foreach($remains as $remain) {
1844
						if(array_key_exists($key, $remain)) {
1845
							array_push($args, $remain[$key]);
1846
						}
1847
					}
1848

    
1849
					if(count($args) > 2) {
1850
						// put the recursion
1851
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
1852
					} else {
1853
						foreach($value as $vkey => $vval) {
1854
							$result[$key][$vkey] = $vval;
1855
						}
1856
					}
1857
				} else {
1858
					// simply put the value
1859
					$result[$key] = $value;
1860
				}
1861
			}
1862
		}
1863
	}
1864
	return $result;
1865
}
1866

    
1867

    
1868
/*
1869
 * converts a string like "a,b,c,d"
1870
 * into an array like array("a" => "b", "c" => "d")
1871
 */
1872
function explode_assoc($delimiter, $string) {
1873
	$array = explode($delimiter, $string);
1874
	$result = array();
1875
	$numkeys = floor(count($array) / 2);
1876
	for ($i = 0; $i < $numkeys; $i += 1) {
1877
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
1878
	}
1879
	return $result;
1880
}
1881

    
1882
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
1883
	global $config, $aliastable;
1884

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

    
1889
	$allstaticroutes = array();
1890
	$allsubnets = array();
1891
	/* Loop through routes and expand aliases as we find them. */
1892
	foreach ($config['staticroutes']['route'] as $route) {
1893
		if (is_alias($route['network'])) {
1894
			if (!isset($aliastable[$route['network']]))
1895
				continue;
1896

    
1897
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
1898
			foreach ($subnets as $net) {
1899
				if (!is_subnet($net)) {
1900
					if (is_ipaddrv4($net))
1901
						$net .= "/32";
1902
					else if (is_ipaddrv6($net))
1903
						$net .= "/128";
1904
					else if ($returnhostnames === false || !is_fqdn($net))
1905
						continue;
1906
				}
1907
				$temproute = $route;
1908
				$temproute['network'] = $net;
1909
				$allstaticroutes[] = $temproute;
1910
				$allsubnets[] = $net;
1911
			}
1912
		} elseif (is_subnet($route['network'])) {
1913
			$allstaticroutes[] = $route;
1914
			$allsubnets[] = $route['network'];
1915
		}
1916
	}
1917
	if ($returnsubnetsonly)
1918
		return $allsubnets;
1919
	else
1920
		return $allstaticroutes;
1921
}
1922

    
1923
/****f* util/get_alias_list
1924
 * NAME
1925
 *   get_alias_list - Provide a list of aliases.
1926
 * INPUTS
1927
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
1928
 * RESULT
1929
 *   Array containing list of aliases.
1930
 *   If $type is unspecified, all aliases are returned.
1931
 *   If $type is a string, all aliases of the type specified in $type are returned.
1932
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
1933
 */
1934
function get_alias_list($type = null) {
1935
	global $config;
1936
	$result = array();
1937
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
1938
		foreach ($config['aliases']['alias'] as $alias) {
1939
			if ($type === null) {
1940
				$result[] = $alias['name'];
1941
			}
1942
			else if (is_array($type)) {
1943
				if (in_array($alias['type'], $type)) {
1944
					$result[] = $alias['name'];
1945
				}
1946
			}
1947
			else if ($type === $alias['type']) {
1948
				$result[] = $alias['name'];
1949
			}
1950
		}
1951
	}
1952
	return $result;
1953
}
1954

    
1955
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
1956
function array_exclude($needle, $haystack) {
1957
	$result = array();
1958
	if (is_array($haystack)) {
1959
		foreach ($haystack as $thing) {
1960
			if ($needle !== $thing) {
1961
				$result[] = $thing;
1962
			}
1963
		}
1964
	}
1965
	return $result;
1966
}
1967

    
1968
function setup_library_paths() {
1969
	$current_library_paths = explode(":", exec("/sbin/ldconfig -r | /usr/bin/grep 'search directories' | /usr/bin/awk '{print $3;}'"));
1970
	$pbi_library_paths = array_merge(glob("/usr/pbi/*/lib", GLOB_ONLYDIR), glob("/usr/pbi/*/lib/*", GLOB_ONLYDIR));
1971
	foreach ($pbi_library_paths as $pbilib) {
1972
		if (!in_array($pbilib, $current_library_paths))
1973
			exec("/sbin/ldconfig -m {$pbilib}");
1974
	}
1975
}
1976

    
1977
function get_current_theme() {
1978
	global $config, $g;
1979
	/*
1980
	 *   if user has selected a custom template, use it.
1981
	 *   otherwise default to pfsense tempalte
1982
	 */
1983
	if (($g["disablethemeselection"] === true) && !empty($g["default_theme"]) && (is_dir($g["www_path"].'/themes/'.$g["default_theme"])))
1984
		$theme = $g["default_theme"];
1985
	elseif($config['theme'] <> "" && (is_dir($g["www_path"].'/themes/'.$config['theme'])))
1986
		$theme = $config['theme'];
1987
	else
1988
		$theme = "pfsense";
1989
	/*
1990
	 *  If this device is an apple ipod/iphone
1991
	 *  switch the theme to one that works with it.
1992
	 */
1993
	$lowres_ua = array("iPhone", "iPod", "iPad", "Android", "BlackBerry", "Opera Mini", "Opera Mobi", "PlayBook", "IEMobile");
1994
	foreach($lowres_ua as $useragent)
1995
		if(strstr($_SERVER['HTTP_USER_AGENT'], $useragent))
1996
			$theme = (empty($g['theme_lowres']) && (is_dir($g["www_path"].'/themes/'.$g['theme_lowres']))) ? "pfsense" : $g['theme_lowres'];
1997
	return $theme;
1998
}
1999

    
2000
?>
(54-54/66)