Project

General

Profile

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

    
24
/****f* pfsense-utils/have_natpfruleint_access
25
 * NAME
26
 *   have_natpfruleint_access
27
 * INPUTS
28
 *	none
29
 * RESULT
30
 *   returns true if user has access to edit a specific firewall nat port forward interface
31
 ******/
32
function have_natpfruleint_access($if) {
33
	$security_url = "firewall_nat_edit.php?if=". strtolower($if);
34
	if (isAllowedPage($security_url, $allowed)) {
35
		return true;
36
	}
37
	return false;
38
}
39

    
40
/****f* pfsense-utils/have_ruleint_access
41
 * NAME
42
 *   have_ruleint_access
43
 * INPUTS
44
 *	none
45
 * RESULT
46
 *   returns true if user has access to edit a specific firewall interface
47
 ******/
48
function have_ruleint_access($if) {
49
	$security_url = "firewall_rules.php?if=". strtolower($if);
50
	if (isAllowedPage($security_url)) {
51
		return true;
52
	}
53
	return false;
54
}
55

    
56
/****f* pfsense-utils/does_url_exist
57
 * NAME
58
 *   does_url_exist
59
 * INPUTS
60
 *	none
61
 * RESULT
62
 *   returns true if a url is available
63
 ******/
64
function does_url_exist($url) {
65
	$fd = fopen("$url", "r");
66
	if ($fd) {
67
		fclose($fd);
68
		return true;
69
	} else {
70
		return false;
71
	}
72
}
73

    
74
/****f* pfsense-utils/is_private_ip
75
 * NAME
76
 *   is_private_ip
77
 * INPUTS
78
 *	none
79
 * RESULT
80
 *   returns true if an ip address is in a private range
81
 ******/
82
function is_private_ip($iptocheck) {
83
	$isprivate = false;
84
	$ip_private_list = array(
85
		"10.0.0.0/8",
86
		"100.64.0.0/10",
87
		"172.16.0.0/12",
88
		"192.168.0.0/16",
89
	);
90
	foreach ($ip_private_list as $private) {
91
		if (ip_in_subnet($iptocheck, $private) == true) {
92
			$isprivate = true;
93
		}
94
	}
95
	return $isprivate;
96
}
97

    
98
/****f* pfsense-utils/get_tmp_file
99
 * NAME
100
 *   get_tmp_file
101
 * INPUTS
102
 *	none
103
 * RESULT
104
 *   returns a temporary filename
105
 ******/
106
function get_tmp_file() {
107
	global $g;
108
	return "{$g['tmp_path']}/tmp-" . time();
109
}
110

    
111
/* Stub for deprecated function
112
 * See https://redmine.pfsense.org/issues/10931 */
113
function get_dns_servers() {
114
	return get_dns_nameservers(false, true);
115
}
116

    
117
/****f* pfsense-utils/pfSenseHeader
118
 * NAME
119
 *   pfSenseHeader
120
 * INPUTS
121
 *   none
122
 * RESULT
123
 *   Javascript header change or browser Location:
124
 ******/
125
function pfSenseHeader($text) {
126
	global $_SERVER;
127
	if (isAjax()) {
128
		if ($_SERVER['HTTPS'] == "on") {
129
			$protocol = "https";
130
		} else {
131
			$protocol = "http";
132
		}
133

    
134
		$port = ":{$_SERVER['SERVER_PORT']}";
135
		if ($_SERVER['SERVER_PORT'] == "80" && $protocol == "http") {
136
			$port = "";
137
		}
138
		if ($_SERVER['SERVER_PORT'] == "443" && $protocol == "https") {
139
			$port = "";
140
		}
141
		$complete_url = "{$protocol}://{$_SERVER['HTTP_HOST']}{$port}/{$text}";
142
		echo "\ndocument.location.href = '{$complete_url}';\n";
143
	} else {
144
		header("Location: $text");
145
	}
146
}
147

    
148
/****f* pfsense-utils/get_css_files
149
 * NAME
150
 *   get_css_files - get a list of the available CSS files (themes)
151
 * INPUTS
152
 *   none
153
 * RESULT
154
 *   $csslist - an array of the CSS files
155
 ******/
156
function get_css_files() {
157
	$csslist = array();
158

    
159
	// List pfSense files, then any BETA files followed by any user-contributed files
160
	$cssfiles = glob("/usr/local/www/css/*.css");
161

    
162
	if (is_array($cssfiles)) {
163
		arsort($cssfiles);
164
		$usrcss = $pfscss = $betacss = array();
165

    
166
		foreach ($cssfiles as $css) {
167
			// Don't display any login/logo page related CSS files
168
			if (strpos($css, "login") == 0 &&
169
			    strpos($css, "logo") == 0) {
170
				if (strpos($css, "BETA") != 0) {
171
					array_push($betacss, $css);
172
				} else if (strpos($css, "pfSense") != 0) {
173
					array_push($pfscss, $css);
174
				} else {
175
					array_push($usrcss, $css);
176
				}
177
			}
178
		}
179

    
180
		$css = array_merge($pfscss, $betacss, $usrcss);
181

    
182
		foreach ($css as $file) {
183
			$file = basename($file);
184
			$csslist[$file] = pathinfo($file, PATHINFO_FILENAME);
185
		}
186
	}
187
	return $csslist;
188
}
189

    
190
/****f* pfsense-utils/gen_webguicss_field
191
 * NAME
192
 *   gen_webguicss_field
193
 * INPUTS
194
 *   Pointer to section object
195
 *   Initial value for the field
196
 * RESULT
197
 *   no return value, section object is updated
198
 ******/
199
function gen_webguicss_field(&$section, $value) {
200

    
201
	$csslist = get_css_files();
202

    
203
	if (!isset($csslist[$value])) {
204
		$value = "pfSense.css";
205
	}
206

    
207
	$section->addInput(new Form_Select(
208
		'webguicss',
209
		'Theme',
210
		$value,
211
		$csslist
212
	))->setHelp('Choose an alternative css file (if installed) to change the appearance of the webConfigurator. css files are located in /usr/local/www/css/%s', '<span id="csstxt"></span>');
213
}
214
function validate_webguicss_field(&$input_errors, $value) {
215
	$csslist = get_css_files();
216
	if (!isset($csslist[$value])) {
217
		$input_errors[] = gettext("The submitted Theme could not be found. Pick a different theme.");
218
	}
219
}
220

    
221
/****f* pfsense-utils/gen_webguifixedmenu_field
222
 * NAME
223
 *   gen_webguifixedmenu_field
224
 * INPUTS
225
 *   Pointer to section object
226
 *   Initial value for the field
227
 * RESULT
228
 *   no return value, section object is updated
229
 ******/
230
function gen_webguifixedmenu_field(&$section, $value) {
231

    
232
	$section->addInput(new Form_Select(
233
		'webguifixedmenu',
234
		'Top Navigation',
235
		$value,
236
		["" => gettext("Scrolls with page"), "fixed" => gettext("Fixed (Remains visible at top of page)")]
237
	))->setHelp("The fixed option is intended for large screens only.");
238
}
239
function validate_webguifixedmenu_field(&$input_errors, $value) {
240
	$valid_values = array("", "fixed");
241
	if (!in_array($value, $valid_values)) {
242
		$input_errors[] = gettext("The submitted Top Navigation value is invalid.");
243
	}
244
}
245

    
246
/****f* pfsense-utils/gen_webguihostnamemenu_field
247
 * NAME
248
 *   gen_webguihostnamemenu_field
249
 * INPUTS
250
 *   Pointer to section object
251
 *   Initial value for the field
252
 * RESULT
253
 *   no return value, section object is updated
254
 ******/
255
function gen_webguihostnamemenu_field(&$section, $value) {
256

    
257
	$section->addInput(new Form_Select(
258
		'webguihostnamemenu',
259
		'Hostname in Menu',
260
		$value,
261
		["" => gettext("Default (No hostname)"), "hostonly" => gettext("Hostname only"), "fqdn" => gettext("Fully Qualified Domain Name")]
262
	))->setHelp("Replaces the Help menu title in the Navbar with the system hostname or FQDN.");
263
}
264
function validate_webguihostnamemenu_field(&$input_errors, $value) {
265
	$valid_values = array("", "hostonly", "fqdn");
266
	if (!in_array($value, $valid_values)) {
267
		$input_errors[] = gettext("The submitted Hostname in Menu value is invalid.");
268
	}
269
}
270

    
271
/****f* pfsense-utils/gen_dashboardcolumns_field
272
 * NAME
273
 *   gen_dashboardcolumns_field
274
 * INPUTS
275
 *   Pointer to section object
276
 *   Initial value for the field
277
 * RESULT
278
 *   no return value, section object is updated
279
 ******/
280
function gen_dashboardcolumns_field(&$section, $value) {
281

    
282
	if (((int) $value < 1) || ((int) $value > 6)) {
283
		$value = 2;
284
	}
285

    
286
	$section->addInput(new Form_Input(
287
		'dashboardcolumns',
288
		'Dashboard Columns',
289
		'number',
290
		$value,
291
		['min' => 1, 'max' => 6]
292
	));
293
}
294
function validate_dashboardcolumns_field(&$input_errors, $value) {
295
	if (!is_numericint($value) || ((int) $value < 1) || ((int) $value > 6)) {
296
		$input_errors[] = gettext("The submitted Dashboard Columns value is invalid.");
297
	}
298
}
299

    
300
/****f* pfsense-utils/gen_interfacessort_field
301
 * NAME
302
 *   gen_interfacessort_field
303
 * INPUTS
304
 *   Pointer to section object
305
 *   Initial value for the field
306
 * RESULT
307
 *   no return value, section object is updated
308
 ******/
309
function gen_interfacessort_field(&$section, $value) {
310

    
311
	$section->addInput(new Form_Checkbox(
312
		'interfacessort',
313
		'Interfaces Sort',
314
		'Sort Alphabetically',
315
		$value
316
	))->setHelp('If selected, lists of interfaces will be sorted by description, otherwise they are listed wan,lan,optn...');
317
}
318

    
319
/****f* pfsense-utils/gen_associatedpanels_fields
320
 * NAME
321
 *   gen_associatedpanels_fields
322
 * INPUTS
323
 *   Pointer to section object
324
 *   Initial value for each of the fields
325
 * RESULT
326
 *   no return value, section object is updated
327
 ******/
328
function gen_associatedpanels_fields(&$section, $value1, $value2, $value3, $value4) {
329

    
330
	$group = new Form_Group('Associated Panels Show/Hide');
331

    
332
	$group->add(new Form_Checkbox(
333
		'dashboardavailablewidgetspanel',
334
		null,
335
		'Available Widgets',
336
		$value1
337
		))->setHelp('Show the Available Widgets panel on the Dashboard.');
338

    
339
	$group->add(new Form_Checkbox(
340
		'systemlogsfilterpanel',
341
		null,
342
		'Log Filter',
343
		$value2
344
	))->setHelp('Show the Log Filter panel in System Logs.');
345

    
346
	$group->add(new Form_Checkbox(
347
		'systemlogsmanagelogpanel',
348
		null,
349
		'Manage Log',
350
		$value3
351
	))->setHelp('Show the Manage Log panel in System Logs.');
352

    
353
	$group->add(new Form_Checkbox(
354
		'statusmonitoringsettingspanel',
355
		null,
356
		'Monitoring Settings',
357
		$value4
358
	))->setHelp('Show the Settings panel in Status Monitoring.');
359

    
360
	$group->setHelp('These options allow certain panels to be automatically hidden on page load. A control is provided in the title bar to un-hide the panel.');
361

    
362
	$section->add($group);
363
}
364

    
365
/****f* pfsense-utils/gen_webguileftcolumnhyper_field
366
 * NAME
367
 *   gen_webguileftcolumnhyper_field
368
 * INPUTS
369
 *   Pointer to section object
370
 *   Initial value for the field
371
 * RESULT
372
 *   no return value, section object is updated
373
 ******/
374
function gen_webguileftcolumnhyper_field(&$section, $value) {
375

    
376
	$section->addInput(new Form_Checkbox(
377
		'webguileftcolumnhyper',
378
		'Left Column Labels',
379
		'Active',
380
		$value
381
	))->setHelp('If selected, clicking a label in the left column will select/toggle the first item of the group.');
382
}
383

    
384
/****f* pfsense-utils/gen_disablealiaspopupdetail_field
385
 * NAME
386
 *   gen_disablealiaspopupdetail_field
387
 * INPUTS
388
 *   Pointer to section object
389
 *   Initial value for the field
390
 * RESULT
391
 *   no return value, section object is updated
392
 ******/
393
function gen_disablealiaspopupdetail_field(&$section, $value) {
394

    
395
	$section->addInput(new Form_Checkbox(
396
		'disablealiaspopupdetail',
397
		'Alias Popups',
398
		'Disable details in alias popups',
399
		$value
400
	))->setHelp('If selected, the details in alias popups will not be shown, just the alias description (e.g. in Firewall Rules).');
401
}
402

    
403
/****f* pfsense-utils/gen_pagenamefirst_field
404
 * NAME
405
 *   gen_pagenamefirst_field
406
 * INPUTS
407
 *   Pointer to section object
408
 *   Initial value for the field
409
 * RESULT
410
 *   no return value, section object is updated
411
 ******/
412
function gen_pagenamefirst_field(&$section, $value) {
413

    
414
	$section->addInput(new Form_Checkbox(
415
		'pagenamefirst',
416
		'Browser tab text',
417
		'Display page name first in browser tab',
418
		$value
419
	))->setHelp('When this is unchecked, the browser tab shows the host name followed '.
420
		'by the current page. Check this box to display the current page followed by the '.
421
		'host name.');
422
}
423

    
424
/****f* pfsense-utils/gen_user_settings_fields
425
 * NAME
426
 *   gen_user_settings_fields
427
 * INPUTS
428
 *   Pointer to section object
429
 *   Array of initial values for the fields
430
 * RESULT
431
 *   no return value, section object is updated
432
 ******/
433
function gen_user_settings_fields(&$section, $pconfig) {
434

    
435
	gen_webguicss_field($section, $pconfig['webguicss']);
436
	gen_webguifixedmenu_field($section, $pconfig['webguifixedmenu']);
437
	gen_webguihostnamemenu_field($section, $pconfig['webguihostnamemenu']);
438
	gen_dashboardcolumns_field($section, $pconfig['dashboardcolumns']);
439
	gen_interfacessort_field($section, $pconfig['interfacessort']);
440
	gen_associatedpanels_fields(
441
		$section,
442
		$pconfig['dashboardavailablewidgetspanel'],
443
		$pconfig['systemlogsfilterpanel'],
444
		$pconfig['systemlogsmanagelogpanel'],
445
		$pconfig['statusmonitoringsettingspanel']);
446
	gen_webguileftcolumnhyper_field($section, $pconfig['webguileftcolumnhyper']);
447
	gen_disablealiaspopupdetail_field($section, $pconfig['disablealiaspopupdetail']);
448
	gen_pagenamefirst_field($section, $pconfig['pagenamefirst']);
449
}
450

    
451
/****f* pfsense-utils/gen_requirestatefilter_field
452
 * NAME
453
 *   gen_requirestatefilter_field
454
 * INPUTS
455
 *   Pointer to section object
456
 *   Initial value for the field
457
 * RESULT
458
 *   no return value, section object is updated
459
 ******/
460
function gen_requirestatefilter_field(&$section, $value) {
461
	$section->addInput(new Form_Checkbox(
462
		'requirestatefilter',
463
		'Require State Filter',
464
		'Do not display state table without a filter',
465
		$value
466
	))->setHelp('By default, the entire state table is displayed when entering '.
467
		'Diagnostics > States. This option requires a filter to be entered '.
468
		'before the states are displayed. Useful for systems with large state tables.');
469
}
470

    
471
/****f* pfsense-utils/gen_created_updated_fields
472
 * NAME
473
 *   gen_created_updated_fields
474
 * INPUTS
475
 *   Pointer to form object
476
 *   Array of created time and username
477
 *   Array of updated time and username
478
 * RESULT
479
 *   no return value, section object is added to form if needed
480
 ******/
481
function gen_created_updated_fields(&$form, $created, $updated, $tracker = 0) {
482
	$has_created_time = (isset($created['time']) && isset($created['username']));
483
	$has_updated_time = (isset($updated['time']) && isset($updated['username']));
484

    
485
	if ($has_created_time || $has_updated_time) {
486
		$section = new Form_Section('Rule Information');
487

    
488
		if (!empty($tracker)) {
489
			$section->addInput(new Form_StaticText(
490
				'Tracking ID',
491
				htmlspecialchars($tracker)
492
			));
493
		}
494

    
495
		if ($has_created_time) {
496
			$section->addInput(new Form_StaticText(
497
				'Created',
498
				htmlspecialchars(sprintf(
499
					gettext('%1$s by %2$s'),
500
					date(gettext("n/j/y H:i:s"), $created['time']),
501
					$created['username']))
502
			));
503
		}
504

    
505
		if ($has_updated_time) {
506
			$section->addInput(new Form_StaticText(
507
				'Updated',
508
				htmlspecialchars(sprintf(
509
					gettext('%1$s by %2$s'),
510
					date(gettext("n/j/y H:i:s"), $updated['time']),
511
					$updated['username']))
512
			));
513
		}
514

    
515
		$form->add($section);
516
	}
517
}
518

    
519
function hardware_offloading_applyflags($iface) {
520
	global $config;
521

    
522
	$flags_on = 0;
523
	$flags_off = 0;
524
	$options = pfSense_get_interface_addresses($iface);
525

    
526
	/* disable hardware checksum offloading for VirtIO network drivers,
527
	 * see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059 */
528
	if (isset($config['system']['disablechecksumoffloading']) ||
529
	    stristr($iface, "vtnet") || stristr($iface, "ena")) { 
530
		if (isset($options['encaps']['txcsum'])) {
531
			$flags_off |= IFCAP_TXCSUM;
532
		}
533
		if (isset($options['encaps']['rxcsum'])) {
534
			$flags_off |= IFCAP_RXCSUM;
535
		}
536
		if (isset($options['encaps']['txcsum6'])) {
537
			$flags_off |= IFCAP_TXCSUM_IPV6;
538
		}
539
		if (isset($options['encaps']['rxcsum6'])) {
540
			$flags_off |= IFCAP_RXCSUM_IPV6;
541
		}
542
	} else {
543
		if (isset($options['caps']['txcsum'])) {
544
			$flags_on |= IFCAP_TXCSUM;
545
		}
546
		if (isset($options['caps']['rxcsum'])) {
547
			$flags_on |= IFCAP_RXCSUM;
548
		}
549
		if (isset($options['caps']['txcsum6'])) {
550
			$flags_on |= IFCAP_TXCSUM_IPV6;
551
		}
552
		if (isset($options['caps']['rxcsum6'])) {
553
			$flags_on |= IFCAP_RXCSUM_IPV6;
554
		}
555
	}
556

    
557
	if (isset($config['system']['disablesegmentationoffloading'])) {
558
		$flags_off |= IFCAP_TSO;
559
		$flags_off |= IFCAP_VLAN_HWTSO;
560
	} else if (isset($options['caps']['tso']) || isset($options['caps']['tso4']) || isset($options['caps']['tso6'])) {
561
		$flags_on |= IFCAP_TSO;
562
		$flags_on |= IFCAP_VLAN_HWTSO;
563
	}
564

    
565
	if (isset($config['system']['disablelargereceiveoffloading'])) {
566
		$flags_off |= IFCAP_LRO;
567
	} else if (isset($options['caps']['lro'])) {
568
		$flags_on |= IFCAP_LRO;
569
	}
570

    
571
	pfSense_interface_capabilities($iface, -$flags_off);
572
	pfSense_interface_capabilities($iface, $flags_on);
573
}
574

    
575
/****f* pfsense-utils/enable_hardware_offloading
576
 * NAME
577
 *   enable_hardware_offloading - Enable a NIC's supported hardware features.
578
 * INPUTS
579
 *   $interface	- string containing the physical interface to work on.
580
 * RESULT
581
 *   null
582
 * NOTES
583
 *   This function only supports the fxp driver's loadable microcode.
584
 ******/
585
function enable_hardware_offloading($interface) {
586
	global $g, $config;
587

    
588
	$int = get_real_interface($interface);
589
	if (empty($int)) {
590
		return;
591
	}
592

    
593
	if (!isset($config['system']['do_not_use_nic_microcode'])) {
594
		/* translate wan, lan, opt -> real interface if needed */
595
		$int_family = preg_split("/[0-9]+/", $int);
596
		$supported_ints = array('fxp');
597
		if (in_array($int_family, $supported_ints)) {
598
			if (does_interface_exist($int)) {
599
				pfSense_interface_flags($int, IFF_LINK0);
600
			}
601
		}
602
	}
603

    
604
	/* This is mostly for vlans and ppp types */
605
	$realhwif = get_parent_interface($interface);
606
	if ($realhwif[0] == $int) {
607
		hardware_offloading_applyflags($int);
608
	} else {
609
		hardware_offloading_applyflags($realhwif[0]);
610
		hardware_offloading_applyflags($int);
611
	}
612
}
613

    
614
/****f* pfsense-utils/is_alias_inuse
615
 * NAME
616
 *   checks to see if an alias is currently in use by a rule
617
 * INPUTS
618
 *
619
 * RESULT
620
 *   true or false
621
 * NOTES
622
 *
623
 ******/
624
function is_alias_inuse($alias) {
625
	global $g, $config;
626

    
627
	if ($alias == "") {
628
		return false;
629
	}
630
	/* loop through firewall rules looking for alias in use */
631
	if (is_array($config['filter']['rule'])) {
632
		foreach ($config['filter']['rule'] as $rule) {
633
			if ($rule['source']['address']) {
634
				if ($rule['source']['address'] == $alias) {
635
					return true;
636
				}
637
			}
638
			if ($rule['destination']['address']) {
639
				if ($rule['destination']['address'] == $alias) {
640
					return true;
641
				}
642
			}
643
		}
644
	}
645
	/* loop through nat rules looking for alias in use */
646
	if (is_array($config['nat']['rule'])) {
647
		foreach ($config['nat']['rule'] as $rule) {
648
			if ($rule['target'] && $rule['target'] == $alias) {
649
				return true;
650
			}
651
			if ($rule['source']['address'] && $rule['source']['address'] == $alias) {
652
				return true;
653
			}
654
			if ($rule['destination']['address'] && $rule['destination']['address'] == $alias) {
655
				return true;
656
			}
657
		}
658
	}
659
	return false;
660
}
661

    
662
/****f* pfsense-utils/is_schedule_inuse
663
 * NAME
664
 *   checks to see if a schedule is currently in use by a rule
665
 * INPUTS
666
 *
667
 * RESULT
668
 *   true or false
669
 * NOTES
670
 *
671
 ******/
672
function is_schedule_inuse($schedule) {
673
	global $g, $config;
674

    
675
	if ($schedule == "") {
676
		return false;
677
	}
678
	/* loop through firewall rules looking for schedule in use */
679
	if (is_array($config['filter']['rule'])) {
680
		foreach ($config['filter']['rule'] as $rule) {
681
			if ($rule['sched'] == $schedule) {
682
				return true;
683
			}
684
		}
685
	}
686
	return false;
687
}
688

    
689
/****f* pfsense-utils/setup_microcode
690
 * NAME
691
 *   enumerates all interfaces and calls enable_hardware_offloading which
692
 *   enables a NIC's supported hardware features.
693
 * INPUTS
694
 *
695
 * RESULT
696
 *   null
697
 * NOTES
698
 *   This function only supports the fxp driver's loadable microcode.
699
 ******/
700
function setup_microcode() {
701

    
702
	/* if list */
703
	$iflist = get_configured_interface_list(true);
704
	foreach ($iflist as $if => $ifdescr) {
705
		enable_hardware_offloading($if);
706
	}
707
	unset($iflist);
708
}
709

    
710
/****f* pfsense-utils/get_carp_status
711
 * NAME
712
 *   get_carp_status - Return whether CARP is enabled or disabled.
713
 * RESULT
714
 *   boolean	- true if CARP is enabled, false if otherwise.
715
 ******/
716
function get_carp_status() {
717
	/* grab the current status of carp */
718
	$status = get_single_sysctl('net.inet.carp.allow');
719
	return (intval($status) > 0);
720
}
721

    
722
/*
723
 * convert_ip_to_network_format($ip, $subnet): converts an ip address to network form
724

    
725
 */
726
function convert_ip_to_network_format($ip, $subnet) {
727
	$ipsplit = explode('.', $ip);
728
	$string = $ipsplit[0] . "." . $ipsplit[1] . "." . $ipsplit[2] . ".0/" . $subnet;
729
	return $string;
730
}
731

    
732
/*
733
 * get_carp_interface_status($carpid): returns the status of a carp uniqid
734
 */
735
function get_carp_interface_status($carpid) {
736

    
737
	$carpiface = get_configured_vip_interface($carpid);
738
	if ($carpiface == NULL)
739
		return "";
740
	$interface = get_real_interface($carpiface);
741
	if ($interface == NULL)
742
		return "";
743
	$vip = get_configured_vip($carpid);
744
	if ($vip == NULL || !isset($vip['vhid']))
745
		return "";
746

    
747
	$vhid = $vip['vhid'];
748
	$carp_query = '';
749
	$_gb = exec("/sbin/ifconfig {$interface} | /usr/bin/grep \"carp:.* vhid {$vhid} \"", $carp_query);
750
	foreach ($carp_query as $int) {
751
		if (stripos($int, "MASTER"))
752
			return "MASTER";
753
		elseif (stripos($int, "BACKUP"))
754
			return "BACKUP";
755
		elseif (stripos($int, "INIT"))
756
			return "INIT";
757
	}
758

    
759
	return "";
760
}
761

    
762
/*
763
 * Return true if the CARP status of at least one interface of a captive portal zone is in backup mode
764
 * This function return false if CARP is not enabled on any interface of the captive portal zone
765
 */
766
function captiveportal_ha_is_node_in_backup_mode($cpzone) {
767
	global $config;
768

    
769
	$cpinterfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
770

    
771
	if (is_array($config['virtualip']['vip'])) {
772
		foreach ($cpinterfaces as $interface) {
773
			foreach ($config['virtualip']['vip'] as $vip) {
774
				if (($vip['interface'] == $interface) && ($vip['mode'] == "carp")) {
775
					if (get_carp_interface_status("_vip{$vip['uniqid']}") != "MASTER") {
776
						return true;
777
					}
778
				}
779
			}
780
		}
781
	}
782
	return false;
783
}
784

    
785
/*
786
 * get_pfsync_interface_status($pfsyncinterface): returns the status of a pfsync
787
 */
788
function get_pfsync_interface_status($pfsyncinterface) {
789
	if (!does_interface_exist($pfsyncinterface)) {
790
		return;
791
	}
792

    
793
	return exec_command("/sbin/ifconfig {$pfsyncinterface} | /usr/bin/awk '/pfsync:/ {print \$5}'");
794
}
795

    
796
/*
797
 * add_rule_to_anchor($anchor, $rule): adds the specified rule to an anchor
798
 */
799
function add_rule_to_anchor($anchor, $rule, $label) {
800
	mwexec("echo " . escapeshellarg($rule) . " | /sbin/pfctl -a " . escapeshellarg($anchor) . ":" . escapeshellarg($label) . " -f -");
801
}
802

    
803
/*
804
 * remove_text_from_file
805
 * remove $text from file $file
806
 */
807
function remove_text_from_file($file, $text) {
808
	if (!file_exists($file) && !is_writable($file)) {
809
		return;
810
	}
811
	$filecontents = file_get_contents($file);
812
	$text = str_replace($text, "", $filecontents);
813
	@file_put_contents($file, $text);
814
}
815

    
816
/*
817
 *   after_sync_bump_adv_skew(): create skew values by 1S
818
 */
819
function after_sync_bump_adv_skew() {
820
	global $config, $g;
821
	$processed_skew = 1;
822
	init_config_arr(array('virtualip', 'vip'));
823
	$a_vip = &$config['virtualip']['vip'];
824
	foreach ($a_vip as $vipent) {
825
		if ($vipent['advskew'] <> "") {
826
			$processed_skew = 1;
827
			$vipent['advskew'] = $vipent['advskew']+1;
828
		}
829
	}
830
	if ($processed_skew == 1) {
831
		write_config(gettext("After synch increase advertising skew"));
832
	}
833
}
834

    
835
/*
836
 * get_filename_from_url($url): converts a url to its filename.
837
 */
838
function get_filename_from_url($url) {
839
	return basename($url);
840
}
841

    
842
/*
843
 *   get_dir: return an array of $dir
844
 */
845
function get_dir($dir) {
846
	$dir_array = array();
847
	$d = dir($dir);
848
	if (!is_object($d)) {
849
		return array();
850
	}
851
	while (false !== ($entry = $d->read())) {
852
		array_push($dir_array, $entry);
853
	}
854
	$d->close();
855
	return $dir_array;
856
}
857

    
858
/****f* pfsense-utils/WakeOnLan
859
 * NAME
860
 *   WakeOnLan - Wake a machine up using the wake on lan format/protocol
861
 * RESULT
862
 *   true/false - true if the operation was successful
863
 ******/
864
function WakeOnLan($addr, $mac) {
865
	$addr_byte = explode(':', $mac);
866
	$hw_addr = '';
867

    
868
	for ($a = 0; $a < 6; $a++) {
869
		$hw_addr .= chr(hexdec($addr_byte[$a]));
870
	}
871

    
872
	$msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255);
873

    
874
	for ($a = 1; $a <= 16; $a++) {
875
		$msg .= $hw_addr;
876
	}
877

    
878
	// send it to the broadcast address using UDP
879
	$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
880
	if ($s == false) {
881
		log_error(gettext("Error creating socket!"));
882
		log_error(sprintf(gettext("Error code is '%1\$s' - %2\$s"), socket_last_error($s), socket_strerror(socket_last_error($s))));
883
	} else {
884
		// setting a broadcast option to socket:
885
		$opt_ret = socket_set_option($s, 1, 6, TRUE);
886
		if ($opt_ret < 0) {
887
			log_error(sprintf(gettext("setsockopt() failed, error: %s"), strerror($opt_ret)));
888
		}
889
		$e = socket_sendto($s, $msg, strlen($msg), 0, $addr, 2050);
890
		socket_close($s);
891
		log_error(sprintf(gettext('Magic Packet sent (%1$s) to (%2$s) MAC=%3$s'), $e, $addr, $mac));
892
		return true;
893
	}
894

    
895
	return false;
896
}
897

    
898
/*
899
 * reverse_strrchr($haystack, $needle):  Return everything in $haystack up to the *last* instance of $needle.
900
 *					 Useful for finding paths and stripping file extensions.
901
 */
902
function reverse_strrchr($haystack, $needle) {
903
	if (!is_string($haystack)) {
904
		return;
905
	}
906
	return strrpos($haystack, $needle) ? substr($haystack, 0, strrpos($haystack, $needle) +1) : false;
907
}
908

    
909
/*
910
 *  backup_config_section($section): returns as an xml file string of
911
 *                                   the configuration section
912
 */
913
function backup_config_section($section_name) {
914
	global $config;
915
	$new_section = &$config[$section_name];
916
	/* generate configuration XML */
917
	$xmlconfig = dump_xml_config($new_section, $section_name);
918
	$xmlconfig = str_replace("<?xml version=\"1.0\"?>", "", $xmlconfig);
919
	return $xmlconfig;
920
}
921

    
922
/*
923
 *  restore_config_section($section_name, new_contents): restore a configuration section,
924
 *                                                  and write the configuration out
925
 *                                                  to disk/cf.
926
 */
927
function restore_config_section($section_name, $new_contents) {
928
	global $config, $g;
929
	$fout = fopen("{$g['tmp_path']}/tmpxml", "w");
930
	fwrite($fout, $new_contents);
931
	fclose($fout);
932

    
933
	$xml = parse_xml_config($g['tmp_path'] . "/tmpxml", null);
934
	if ($xml['pfsense']) {
935
		$xml = $xml['pfsense'];
936
	}
937
	else if ($xml['m0n0wall']) {
938
		$xml = $xml['m0n0wall'];
939
	}
940
	if ($xml[$section_name]) {
941
		$section_xml = $xml[$section_name];
942
	} else {
943
		$section_xml = -1;
944
	}
945

    
946
	@unlink($g['tmp_path'] . "/tmpxml");
947
	if ($section_xml === -1) {
948
		return false;
949
	}
950

    
951
	/* Save current pkg repo to re-add on new config */
952
	unset($pkg_repo_conf_path);
953
	if ($section_name == "system" &&
954
	    isset($config['system']['pkg_repo_conf_path'])) {
955
		$pkg_repo_conf_path = $config['system']['pkg_repo_conf_path'];
956
	}
957

    
958
	$config[$section_name] = &$section_xml;
959
	if (file_exists("{$g['tmp_path']}/config.cache")) {
960
		unlink("{$g['tmp_path']}/config.cache");
961
	}
962

    
963
	/* Restore previously pkg repo configured */
964
	if ($section_name == "system") {
965
		if (isset($pkg_repo_conf_path)) {
966
			$config['system']['pkg_repo_conf_path'] =
967
			    $pkg_repo_conf_path;
968
		} elseif (isset($config['system']['pkg_repo_conf_path'])) {
969
			unset($config['system']['pkg_repo_conf_path']);
970
		}
971
	}
972

    
973
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
974
	disable_security_checks();
975
	return true;
976
}
977

    
978
/*
979
 *  merge_config_section($section_name, new_contents):   restore a configuration section,
980
 *                                                  and write the configuration out
981
 *                                                  to disk/cf.  But preserve the prior
982
 * 													structure if needed
983
 */
984
function merge_config_section($section_name, $new_contents) {
985
	global $config;
986
	$fname = get_tmp_filename();
987
	$fout = fopen($fname, "w");
988
	fwrite($fout, $new_contents);
989
	fclose($fout);
990
	$section_xml = parse_xml_config($fname, $section_name);
991
	$config[$section_name] = $section_xml;
992
	unlink($fname);
993
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
994
	disable_security_checks();
995
	return;
996
}
997

    
998
/*
999
 * rmdir_recursive($path, $follow_links=false)
1000
 * Recursively remove a directory tree (rm -rf path)
1001
 * This is for directories _only_
1002
 */
1003
function rmdir_recursive($path, $follow_links=false) {
1004
	$to_do = glob($path);
1005
	if (!is_array($to_do)) {
1006
		$to_do = array($to_do);
1007
	}
1008
	foreach ($to_do as $workingdir) { // Handle wildcards by foreaching.
1009
		if (file_exists($workingdir)) {
1010
			if (is_dir($workingdir)) {
1011
				$dir = opendir($workingdir);
1012
				while ($entry = readdir($dir)) {
1013
					if (is_file("$workingdir/$entry") || ((!$follow_links) && is_link("$workingdir/$entry"))) {
1014
						unlink("$workingdir/$entry");
1015
					} elseif (is_dir("$workingdir/$entry") && $entry != '.' && $entry != '..') {
1016
						rmdir_recursive("$workingdir/$entry");
1017
					}
1018
				}
1019
				closedir($dir);
1020
				rmdir($workingdir);
1021
			} elseif (is_file($workingdir)) {
1022
				unlink($workingdir);
1023
			}
1024
		}
1025
	}
1026
	return;
1027
}
1028

    
1029
/*
1030
 * host_firmware_version(): Return the versions used in this install
1031
 */
1032
function host_firmware_version($tocheck = "") {
1033
	global $g, $config;
1034

    
1035
	$os_version = trim(substr(php_uname("r"), 0, strpos(php_uname("r"), '-')));
1036

    
1037
	return array(
1038
		"firmware" => array("version" => $g['product_version']),
1039
		"kernel"   => array("version" => $os_version),
1040
		"base"     => array("version" => $os_version),
1041
		"platform" => $g['platform'],
1042
		"config_version" => $config['version']
1043
	);
1044
}
1045

    
1046
function get_disk_info() {
1047
	$diskout = "";
1048
	exec("/bin/df -h | /usr/bin/grep -w '/' | /usr/bin/awk '{ print $2, $3, $4, $5 }'", $diskout);
1049
	return explode(' ', $diskout[0]);
1050
}
1051

    
1052
/****f* pfsense-utils/strncpy
1053
 * NAME
1054
 *   strncpy - copy strings
1055
 * INPUTS
1056
 *   &$dst, $src, $length
1057
 * RESULT
1058
 *   none
1059
 ******/
1060
function strncpy(&$dst, $src, $length) {
1061
	if (strlen($src) > $length) {
1062
		$dst = substr($src, 0, $length);
1063
	} else {
1064
		$dst = $src;
1065
	}
1066
}
1067

    
1068
/****f* pfsense-utils/reload_interfaces_sync
1069
 * NAME
1070
 *   reload_interfaces - reload all interfaces
1071
 * INPUTS
1072
 *   none
1073
 * RESULT
1074
 *   none
1075
 ******/
1076
function reload_interfaces_sync() {
1077
	global $config, $g;
1078

    
1079
	if ($g['debug']) {
1080
		log_error(gettext("reload_interfaces_sync() is starting."));
1081
	}
1082

    
1083
	/* parse config.xml again */
1084
	$config = parse_config(true);
1085

    
1086
	/* enable routing */
1087
	system_routing_enable();
1088
	if ($g['debug']) {
1089
		log_error(gettext("Enabling system routing"));
1090
	}
1091

    
1092
	if ($g['debug']) {
1093
		log_error(gettext("Cleaning up Interfaces"));
1094
	}
1095

    
1096
	/* set up interfaces */
1097
	interfaces_configure();
1098
}
1099

    
1100
/****f* pfsense-utils/reload_all
1101
 * NAME
1102
 *   reload_all - triggers a reload of all settings
1103
 *   * INPUTS
1104
 *   none
1105
 * RESULT
1106
 *   none
1107
 ******/
1108
function reload_all() {
1109
	send_event("service reload all");
1110
}
1111

    
1112
/****f* pfsense-utils/reload_interfaces
1113
 * NAME
1114
 *   reload_interfaces - triggers a reload of all interfaces
1115
 * INPUTS
1116
 *   none
1117
 * RESULT
1118
 *   none
1119
 ******/
1120
function reload_interfaces() {
1121
	send_event("interface all reload");
1122
}
1123

    
1124
/****f* pfsense-utils/reload_all_sync
1125
 * NAME
1126
 *   reload_all - reload all settings
1127
 *   * INPUTS
1128
 *   none
1129
 * RESULT
1130
 *   none
1131
 ******/
1132
function reload_all_sync() {
1133
	global $config, $g;
1134

    
1135
	/* parse config.xml again */
1136
	$config = parse_config(true);
1137

    
1138
	/* set up our timezone */
1139
	system_timezone_configure();
1140

    
1141
	/* set up our hostname */
1142
	system_hostname_configure();
1143

    
1144
	/* make hosts file */
1145
	system_hosts_generate();
1146

    
1147
	/* generate resolv.conf */
1148
	system_resolvconf_generate();
1149

    
1150
	/* enable routing */
1151
	system_routing_enable();
1152

    
1153
	/* set up interfaces */
1154
	interfaces_configure();
1155

    
1156
	/* start dyndns service */
1157
	services_dyndns_configure();
1158

    
1159
	/* configure cron service */
1160
	configure_cron();
1161

    
1162
	/* start the NTP client */
1163
	system_ntp_configure();
1164

    
1165
	/* sync pw database */
1166
	unlink_if_exists("/etc/spwd.db.tmp");
1167
	mwexec("/usr/sbin/pwd_mkdb -d /etc/ /etc/master.passwd");
1168

    
1169
	/* restart sshd */
1170
	send_event("service restart sshd");
1171

    
1172
	/* restart webConfigurator if needed */
1173
	send_event("service restart webgui");
1174
}
1175

    
1176
function load_loader_conf($loader_conf = NULL, $local = false) {
1177

    
1178
	if ($loader_conf == NULL) {
1179
		return (NULL);
1180
	}
1181
	if (file_exists($loader_conf)) {
1182
		$input = file_get_contents($loader_conf);
1183
	} else {
1184
		$input = "";
1185
	}
1186

    
1187
	$input_split = explode("\n", $input);
1188

    
1189
	/*
1190
	 * Loop through and only add lines that are not empty and not
1191
	 * managed by us.
1192
	 */
1193
	$data = array();
1194
	/* These values should be removed from loader.conf and loader.conf.local
1195
	 * As they will be replaced when necessary. */
1196
	$remove = array("hw.usb.no_pf", "hint.mdio.0.at", "hint.e6000sw.0",
1197
	    "hw.e6000sw.default_disabled", "vm.pmap.pti",
1198
	    "net.pf.request_maxcount", "hw.hn.vf_transparent",
1199
	    "hw.hn.use_if_start");
1200
	if (!$local) {
1201
		/* These values should only be filtered in loader.conf, not .local */
1202
		$remove = array_merge($remove,
1203
		    array("autoboot_delay", "console", "comconsole_speed",
1204
		    "boot_multicons", "boot_serial", "hint.uart.0.flags",
1205
		    "hint.uart.1.flags"));
1206
	}
1207
	foreach ($input_split as $line) {
1208
		if (empty($line)) {
1209
			continue;
1210
		}
1211
		$skip = false;
1212
		list($name, $value) = explode('=', $line, 2);
1213
		foreach($remove as $rid => $rkey) {
1214
			if (strncasecmp(trim($name), $rkey, strlen($rkey)) == 0) {
1215
				$skip = true;
1216
				break;
1217
			}
1218
		}
1219
		if (!$skip) {
1220
			$data[] = $line;
1221
		}
1222
	}
1223

    
1224
	return ($data);
1225
}
1226

    
1227
function setup_loader_settings($path = "", $upgrade = false) {
1228
	global $g, $config;
1229

    
1230
	$boot_config_file = "{$path}/boot.config";
1231
	$loader_conf_file = "{$path}/boot/loader.conf";
1232

    
1233
	$serialspeed = (is_numeric($config['system']['serialspeed'])) ? $config['system']['serialspeed'] : "115200";
1234

    
1235
	$vga_only = false;
1236
	$serial_only = false;
1237
	$specific_platform = system_identify_specific_platform();
1238
	$video_console_type = (get_single_sysctl("machdep.bootmethod") == "UEFI") ? "efi" : "vidconsole";
1239
	if ($specific_platform['name'] == 'XG-1540') {
1240
		$vga_only = true;
1241
	} elseif ($specific_platform['name'] == 'Turbot Dual-E') {
1242
		$g['primaryconsole_force'] = "video";
1243
	} elseif ($specific_platform['name'] == 'RCC-VE' ||
1244
	    $specific_platform['name'] == 'RCC' ||
1245
	    $specific_platform['name'] == 'SG-2220' ||
1246
	    $specific_platform['name'] == 'apu2') {
1247
		$serial_only = true;
1248
	}
1249

    
1250
	/* Serial console - write out /boot.config */
1251
	if (file_exists($boot_config_file)) {
1252
		$boot_config = file_get_contents($boot_config_file);
1253
	} else {
1254
		$boot_config = "";
1255
	}
1256
	$boot_config_split = explode("\n", $boot_config);
1257
	$data = array();
1258
	foreach ($boot_config_split as $bcs) {
1259
		/* Ignore -D and -h lines now */
1260
		if (!empty($bcs) && !stristr($bcs, "-D") &&
1261
		    !stristr($bcs, "-h")) {
1262
			$data[] = $bcs;
1263
		}
1264
	}
1265
	if ($serial_only === true) {
1266
		$data[] = "-S{$serialspeed} -h";
1267
	} elseif (is_serial_enabled()) {
1268
		$data[] = "-S{$serialspeed} -D";
1269
	}
1270

    
1271
	if (empty($data)) {
1272
		@unlink($boot_conf_file);
1273
	} else {
1274
		safe_write_file($boot_config_file, $data);
1275
	}
1276
	unset($data, $boot_config, $boot_config_file, $boot_config_split);
1277

    
1278
	/* Serial console - write out /boot/loader.conf */
1279
	if ($upgrade) {
1280
		system("echo \"Reading {$loader_conf_file}...\" >> /conf/upgrade_log.txt");
1281
	}
1282

    
1283
	$data = load_loader_conf($loader_conf_file, false);
1284
	if ($serial_only === true) {
1285
		$data[] = 'boot_serial="YES"';
1286
		$data[] = 'console="comconsole"';
1287
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1288
	} elseif ($vga_only === true) {
1289
		$data[] = "console=\"{$video_console_type}\"";
1290
	} elseif (is_serial_enabled()) {
1291
		$data[] = 'boot_multicons="YES"';
1292
		$data[] = 'boot_serial="YES"';
1293
		$primaryconsole = isset($g['primaryconsole_force']) ?
1294
		    $g['primaryconsole_force'] :
1295
		    $config['system']['primaryconsole'];
1296
		switch ($primaryconsole) {
1297
			case "video":
1298
				$data[] = "console=\"{$video_console_type},comconsole\"";
1299
				break;
1300
			case "serial":
1301
			default:
1302
				$data[] = "console=\"comconsole,{$video_console_type}\"";
1303
		}
1304
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1305
	}
1306

    
1307
	if ($specific_platform['name'] == 'RCC-VE' ||
1308
	    $specific_platform['name'] == 'RCC' ||
1309
	    $specific_platform['name'] == 'SG-2220') {
1310
		$data[] = 'comconsole_port="0x2F8"';
1311
		$data[] = 'hint.uart.0.flags="0x00"';
1312
		$data[] = 'hint.uart.1.flags="0x10"';
1313
	}
1314
	$data[] = 'autoboot_delay="3"';
1315
	if (isset($config['system']['pti_disabled'])) {
1316
		$data[] = 'vm.pmap.pti="0"';
1317
	}
1318

    
1319
	/* Enable ALTQ support for hnX NICs. */
1320
	if (isset($config['system']['hn_altq_enable'])) {
1321
		$data[] = 'hw.hn.vf_transparent="0"';
1322
		$data[] = 'hw.hn.use_if_start="1"';
1323
	}
1324

    
1325
	safe_write_file($loader_conf_file, $data);
1326

    
1327
	/* Filter loader.conf.local to avoid duplicate settings. */
1328
	$loader_conf_file = "{$path}/boot/loader.conf.local";
1329
	$data = load_loader_conf($loader_conf_file, true);
1330
	if (empty($data)) {
1331
		@unlink($loader_conf_file);
1332
	} else {
1333
		safe_write_file($loader_conf_file, $data);
1334
	}
1335

    
1336
}
1337

    
1338
function console_configure($when = "save", $path = "") {
1339
	global $config;
1340
	$ttys_file = "{$path}/etc/ttys";
1341

    
1342
	/* Update the loader settings. */
1343
	setup_loader_settings($path, ($when == "upgrade"));
1344

    
1345
	$ttys = file_get_contents($ttys_file);
1346
	$ttys_split = explode("\n", $ttys);
1347

    
1348
	$data = array();
1349

    
1350
	$on_off = (is_serial_enabled() ? 'onifconsole' : 'off');
1351

    
1352
	if (isset($config['system']['disableconsolemenu'])) {
1353
		$console_type = 'Pc';
1354
		$serial_type = '3wire';
1355
	} else {
1356
		$console_type = 'al.Pc';
1357
		$serial_type = 'al.3wire';
1358
	}
1359

    
1360
	$console_line = "console\tnone\t\t\t\tunknown\toff\tsecure";
1361
	$ttyv0_line =
1362
	    "ttyv0\t\"/usr/libexec/getty {$console_type}\"\txterm\tonifexists secure";
1363
	$ttyu_line =
1364
	    "\"/usr/libexec/getty {$serial_type}\"\tvt100\t{$on_off}\tsecure";
1365

    
1366
	$found = array();
1367

    
1368
	foreach ($ttys_split as $tty) {
1369
		/* Ignore blank lines */
1370
		if (empty($tty)) {
1371
			continue;
1372
		}
1373

    
1374
		if (stristr($tty, "ttyv0")) {
1375
			$found['ttyv0'] = 1;
1376
			$data[] = $ttyv0_line;
1377
		} elseif (stristr($tty, "ttyu")) {
1378
			$ttyn = substr($tty, 0, 5);
1379
			$found[$ttyn] = 1;
1380
			$data[] = "{$ttyn}\t{$ttyu_line}";
1381
		} elseif (substr($tty, 0, 7) == 'console') {
1382
			$found['console'] = 1;
1383
			$data[] = $tty;
1384
		} else {
1385
			$data[] = $tty;
1386
		}
1387
	}
1388
	unset($on_off, $console_type, $serial_type);
1389

    
1390
	/* Detect missing main lines on original file and try to rebuild it */
1391
	$items = array(
1392
		'console',
1393
		'ttyv0',
1394
		'ttyu0',
1395
		'ttyu1',
1396
		'ttyu2',
1397
		'ttyu3'
1398
	);
1399

    
1400
	foreach ($items as $item) {
1401
		if (isset($found[$item])) {
1402
			continue;
1403
		}
1404

    
1405
		if ($item == 'console') {
1406
			$data[] = $console_line;
1407
		} elseif ($item == 'ttyv0') {
1408
			$data[] = $ttyv0_line;
1409
		} else {
1410
			$data[] = "{$item}\t{$ttyu_line}";
1411
		}
1412
	}
1413

    
1414
	safe_write_file($ttys_file, $data);
1415

    
1416
	unset($ttys, $ttys_file, $ttys_split, $data);
1417

    
1418
	if ($when != "upgrade") {
1419
		reload_ttys();
1420
	}
1421

    
1422
	return;
1423
}
1424

    
1425
function is_serial_enabled() {
1426
	global $g, $config;
1427

    
1428
	if (!isset($g['enableserial_force']) &&
1429
	    !isset($config['system']['enableserial'])) {
1430
		return false;
1431
	}
1432

    
1433
	return true;
1434
}
1435

    
1436
function reload_ttys() {
1437
	// Send a HUP signal to init will make it reload /etc/ttys
1438
	posix_kill(1, SIGHUP);
1439
}
1440

    
1441
function print_value_list($list, $count = 10, $separator = ",") {
1442
	$list = implode($separator, array_slice($list, 0, $count));
1443
	if (count($list) < $count) {
1444
		$list .= ".";
1445
	} else {
1446
		$list .= "...";
1447
	}
1448
	return $list;
1449
}
1450

    
1451
/* DHCP enabled on any interfaces? */
1452
function is_dhcp_server_enabled() {
1453
	global $config;
1454

    
1455
	if (!is_array($config['dhcpd'])) {
1456
		return false;
1457
	}
1458

    
1459
	foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) {
1460
		if (isset($dhcpifconf['enable']) && !empty($config['interfaces'][$dhcpif])) {
1461
			return true;
1462
		}
1463
	}
1464

    
1465
	return false;
1466
}
1467

    
1468
/* DHCP enabled on any interfaces? */
1469
function is_dhcpv6_server_enabled() {
1470
	global $config;
1471

    
1472
	if (is_array($config['interfaces'])) {
1473
		foreach ($config['interfaces'] as $ifcfg) {
1474
			if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface'])) {
1475
				return true;
1476
			}
1477
		}
1478
	}
1479

    
1480
	if (!is_array($config['dhcpdv6'])) {
1481
		return false;
1482
	}
1483

    
1484
	foreach ($config['dhcpdv6'] as $dhcpv6if => $dhcpv6ifconf) {
1485
		if (isset($dhcpv6ifconf['enable']) && !empty($config['interfaces'][$dhcpv6if])) {
1486
			return true;
1487
		}
1488
	}
1489

    
1490
	return false;
1491
}
1492

    
1493
/* radvd enabled on any interfaces? */
1494
function is_radvd_enabled() {
1495
	global $config;
1496

    
1497
	if (!is_array($config['dhcpdv6'])) {
1498
		$config['dhcpdv6'] = array();
1499
	}
1500

    
1501
	$dhcpdv6cfg = $config['dhcpdv6'];
1502
	$Iflist = get_configured_interface_list();
1503

    
1504
	/* handle manually configured DHCP6 server settings first */
1505
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1506
		if (!isset($config['interfaces'][$dhcpv6if]['enable'])) {
1507
			continue;
1508
		}
1509

    
1510
		if (!isset($dhcpv6ifconf['ramode'])) {
1511
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
1512
		}
1513

    
1514
		if ($dhcpv6ifconf['ramode'] == "disabled") {
1515
			continue;
1516
		}
1517

    
1518
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1519
		if (!is_ipaddrv6($ifcfgipv6)) {
1520
			continue;
1521
		}
1522

    
1523
		return true;
1524
	}
1525

    
1526
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
1527
	foreach ($Iflist as $if => $ifdescr) {
1528
		if (!isset($config['interfaces'][$if]['track6-interface'])) {
1529
			continue;
1530
		}
1531
		if (!isset($config['interfaces'][$if]['enable'])) {
1532
			continue;
1533
		}
1534

    
1535
		$ifcfgipv6 = get_interface_ipv6($if);
1536
		if (!is_ipaddrv6($ifcfgipv6)) {
1537
			continue;
1538
		}
1539

    
1540
		$ifcfgsnv6 = get_interface_subnetv6($if);
1541
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1542

    
1543
		if (!is_ipaddrv6($subnetv6)) {
1544
			continue;
1545
		}
1546

    
1547
		return true;
1548
	}
1549

    
1550
	return false;
1551
}
1552

    
1553
/* Any PPPoE servers enabled? */
1554
function is_pppoe_server_enabled() {
1555
	global $config;
1556

    
1557
	$pppoeenable = false;
1558

    
1559
	if (!is_array($config['pppoes']) || !is_array($config['pppoes']['pppoe'])) {
1560
		return false;
1561
	}
1562

    
1563
	foreach ($config['pppoes']['pppoe'] as $pppoes) {
1564
		if ($pppoes['mode'] == 'server') {
1565
			$pppoeenable = true;
1566
		}
1567
	}
1568

    
1569
	return $pppoeenable;
1570
}
1571

    
1572
/* Optional arg forces hh:mm:ss without days */
1573
function convert_seconds_to_dhms($sec, $showhoursonly = false) {
1574
	if (!is_numericint($sec)) {
1575
		return '-';
1576
	}
1577
	// FIXME: When we move to PHP 7 we can use "intdiv($sec % X, Y)" etc
1578
	list($d, $h, $m, $s) = array(	(int)($showhoursonly ? 0 : $sec/86400),
1579
					(int)(($showhoursonly ? $sec : $sec % 86400)/3600),
1580
					(int)(($sec % 3600)/60),
1581
					$sec % 60
1582
				);
1583
	return ($d > 0 ? $d . 'd ' : '') . sprintf('%02d:%02d:%02d', $h, $m, $s);
1584
}
1585

    
1586
/* Compute the total uptime from the ppp uptime log file in the conf directory */
1587

    
1588
function get_ppp_uptime($port) {
1589
	if (file_exists("/conf/{$port}.log")) {
1590
		$saved_time = file_get_contents("/conf/{$port}.log");
1591
		$uptime_data = explode("\n", $saved_time);
1592
		$sec = 0;
1593
		foreach ($uptime_data as $upt) {
1594
			$sec += substr($upt, 1 + strpos($upt, " "));
1595
		}
1596
		return convert_seconds_to_dhms($sec);
1597
	} else {
1598
		$total_time = gettext("No history data found!");
1599
		return $total_time;
1600
	}
1601
}
1602

    
1603
//returns interface information
1604
function get_interface_info($ifdescr) {
1605
	global $config, $g;
1606

    
1607
	$ifinfo = array();
1608
	if (empty($config['interfaces'][$ifdescr])) {
1609
		return;
1610
	}
1611
	$ifinfo['hwif'] = $config['interfaces'][$ifdescr]['if'];
1612
	$ifinfo['enable'] = isset($config['interfaces'][$ifdescr]['enable']);
1613
	$ifinfo['if'] = get_real_interface($ifdescr);
1614

    
1615
	$chkif = $ifinfo['if'];
1616
	$ifinfotmp = pfSense_get_interface_addresses($chkif);
1617
	$ifinfo['status'] = $ifinfotmp['status'];
1618
	if (empty($ifinfo['status'])) {
1619
		$ifinfo['status'] = "down";
1620
	}
1621
	$ifinfo['macaddr'] = $ifinfotmp['macaddr'];
1622
	$ifinfo['mtu'] = $ifinfotmp['mtu'];
1623
	$ifinfo['ipaddr'] = $ifinfotmp['ipaddr'];
1624
	$ifinfo['subnet'] = $ifinfotmp['subnet'];
1625
	$ifinfo['linklocal'] = get_interface_linklocal($ifdescr);
1626
	$ifinfo['ipaddrv6'] = get_interface_ipv6($ifdescr);
1627
	$ifinfo['subnetv6'] = get_interface_subnetv6($ifdescr);
1628
	if (isset($ifinfotmp['link0'])) {
1629
		$link0 = "down";
1630
	}
1631
	$ifinfotmp = pfSense_get_interface_stats($chkif);
1632
	// $ifinfo['inpkts'] = $ifinfotmp['inpkts'];
1633
	// $ifinfo['outpkts'] = $ifinfotmp['outpkts'];
1634
	$ifinfo['inerrs'] = $ifinfotmp['inerrs'];
1635
	$ifinfo['outerrs'] = $ifinfotmp['outerrs'];
1636
	$ifinfo['collisions'] = $ifinfotmp['collisions'];
1637

    
1638
	/* Use pfctl for non wrapping 64 bit counters */
1639
	/* Pass */
1640
	exec("/sbin/pfctl -vvsI -i {$chkif}", $pfctlstats);
1641
	$pf_in4_pass = preg_split("/ +/ ", $pfctlstats[3]);
1642
	$pf_out4_pass = preg_split("/ +/", $pfctlstats[5]);
1643
	$pf_in6_pass = preg_split("/ +/ ", $pfctlstats[7]);
1644
	$pf_out6_pass = preg_split("/ +/", $pfctlstats[9]);
1645
	$in4_pass = $pf_in4_pass[5];
1646
	$out4_pass = $pf_out4_pass[5];
1647
	$in4_pass_packets = $pf_in4_pass[3];
1648
	$out4_pass_packets = $pf_out4_pass[3];
1649
	$in6_pass = $pf_in6_pass[5];
1650
	$out6_pass = $pf_out6_pass[5];
1651
	$in6_pass_packets = $pf_in6_pass[3];
1652
	$out6_pass_packets = $pf_out6_pass[3];
1653
	$ifinfo['inbytespass'] = $in4_pass + $in6_pass;
1654
	$ifinfo['outbytespass'] = $out4_pass + $out6_pass;
1655
	$ifinfo['inpktspass'] = $in4_pass_packets + $in6_pass_packets;
1656
	$ifinfo['outpktspass'] = $out4_pass_packets + $out6_pass_packets;
1657

    
1658
	/* Block */
1659
	$pf_in4_block = preg_split("/ +/", $pfctlstats[4]);
1660
	$pf_out4_block = preg_split("/ +/", $pfctlstats[6]);
1661
	$pf_in6_block = preg_split("/ +/", $pfctlstats[8]);
1662
	$pf_out6_block = preg_split("/ +/", $pfctlstats[10]);
1663
	$in4_block = $pf_in4_block[5];
1664
	$out4_block = $pf_out4_block[5];
1665
	$in4_block_packets = $pf_in4_block[3];
1666
	$out4_block_packets = $pf_out4_block[3];
1667
	$in6_block = $pf_in6_block[5];
1668
	$out6_block = $pf_out6_block[5];
1669
	$in6_block_packets = $pf_in6_block[3];
1670
	$out6_block_packets = $pf_out6_block[3];
1671
	$ifinfo['inbytesblock'] = $in4_block + $in6_block;
1672
	$ifinfo['outbytesblock'] = $out4_block + $out6_block;
1673
	$ifinfo['inpktsblock'] = $in4_block_packets + $in6_block_packets;
1674
	$ifinfo['outpktsblock'] = $out4_block_packets + $out6_block_packets;
1675

    
1676
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1677
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1678
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1679
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1680

    
1681
	$ifconfiginfo = "";
1682
	$link_type = $config['interfaces'][$ifdescr]['ipaddr'];
1683
	switch ($link_type) {
1684
		/* DHCP? -> see if dhclient is up */
1685
		case "dhcp":
1686
			/* see if dhclient is up */
1687
			if (find_dhclient_process($ifinfo['if']) != 0) {
1688
				$ifinfo['dhcplink'] = "up";
1689
			} else {
1690
				$ifinfo['dhcplink'] = "down";
1691
			}
1692

    
1693
			break;
1694
		/* PPPoE/PPTP/L2TP interface? -> get status from virtual interface */
1695
		case "pppoe":
1696
		case "pptp":
1697
		case "l2tp":
1698
			if ($ifinfo['status'] == "up" && !isset($link0)) {
1699
				/* get PPPoE link status for dial on demand */
1700
				$ifinfo["{$link_type}link"] = "up";
1701
			} else {
1702
				$ifinfo["{$link_type}link"] = "down";
1703
			}
1704

    
1705
			break;
1706
		/* PPP interface? -> get uptime for this session and cumulative uptime from the persistent log file in conf */
1707
		case "ppp":
1708
			if ($ifinfo['status'] == "up") {
1709
				$ifinfo['ppplink'] = "up";
1710
			} else {
1711
				$ifinfo['ppplink'] = "down" ;
1712
			}
1713

    
1714
			if (empty($ifinfo['status'])) {
1715
				$ifinfo['status'] = "down";
1716
			}
1717

    
1718
			if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) {
1719
				foreach ($config['ppps']['ppp'] as $pppid => $ppp) {
1720
					if ($config['interfaces'][$ifdescr]['if'] == $ppp['if']) {
1721
						break;
1722
					}
1723
				}
1724
			}
1725
			$dev = $ppp['ports'];
1726
			if ($config['interfaces'][$ifdescr]['if'] != $ppp['if'] || empty($dev)) {
1727
				break;
1728
			}
1729
			if (!file_exists($dev)) {
1730
				$ifinfo['nodevice'] = 1;
1731
				$ifinfo['pppinfo'] = $dev . " " . gettext("device not present! Is the modem attached to the system?");
1732
			}
1733

    
1734
			$usbmodemoutput = array();
1735
			exec("/usr/sbin/usbconfig", $usbmodemoutput);
1736
			$mondev = "{$g['tmp_path']}/3gstats.{$ifdescr}";
1737
			if (file_exists($mondev)) {
1738
				$cellstats = file($mondev);
1739
				/* skip header */
1740
				$a_cellstats = explode(",", $cellstats[1]);
1741
				if (preg_match("/huawei/i", implode("\n", $usbmodemoutput))) {
1742
					$ifinfo['cell_rssi'] = huawei_rssi_to_string($a_cellstats[1]);
1743
					$ifinfo['cell_mode'] = huawei_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1744
					$ifinfo['cell_simstate'] = huawei_simstate_to_string($a_cellstats[10]);
1745
					$ifinfo['cell_service'] = huawei_service_to_string(trim($a_cellstats[11]));
1746
				}
1747
				if (preg_match("/zte/i", implode("\n", $usbmodemoutput))) {
1748
					$ifinfo['cell_rssi'] = zte_rssi_to_string($a_cellstats[1]);
1749
					$ifinfo['cell_mode'] = zte_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1750
					$ifinfo['cell_simstate'] = zte_simstate_to_string($a_cellstats[10]);
1751
					$ifinfo['cell_service'] = zte_service_to_string(trim($a_cellstats[11]));
1752
				}
1753
				$ifinfo['cell_upstream'] = $a_cellstats[4];
1754
				$ifinfo['cell_downstream'] = trim($a_cellstats[5]);
1755
				$ifinfo['cell_sent'] = $a_cellstats[6];
1756
				$ifinfo['cell_received'] = trim($a_cellstats[7]);
1757
				$ifinfo['cell_bwupstream'] = $a_cellstats[8];
1758
				$ifinfo['cell_bwdownstream'] = trim($a_cellstats[9]);
1759
			}
1760
			// Calculate cumulative uptime for PPP link. Useful for connections that have per minute/hour contracts so you don't go over!
1761
			if (isset($ppp['uptime'])) {
1762
				$ifinfo['ppp_uptime_accumulated'] = "(".get_ppp_uptime($ifinfo['if']).")";
1763
			}
1764
			break;
1765
		default:
1766
			break;
1767
	}
1768

    
1769
	if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) {
1770
		$sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`);
1771
		$ifinfo['ppp_uptime'] = convert_seconds_to_dhms($sec);
1772
	}
1773

    
1774
	if ($ifinfo['status'] == "up") {
1775
		/* try to determine media with ifconfig */
1776
		unset($ifconfiginfo);
1777
		exec("/sbin/ifconfig " . $ifinfo['if'], $ifconfiginfo);
1778
		$wifconfiginfo = array();
1779
		if (is_interface_wireless($ifdescr)) {
1780
			exec("/sbin/ifconfig {$ifinfo['if']} list sta", $wifconfiginfo);
1781
			array_shift($wifconfiginfo);
1782
		}
1783
		$matches = "";
1784
		foreach ($ifconfiginfo as $ici) {
1785

    
1786
			/* don't list media/speed for wireless cards, as it always
1787
			   displays 2 Mbps even though clients can connect at 11 Mbps */
1788
			if (preg_match("/media: .*? \((.*?)\)/", $ici, $matches)) {
1789
				$ifinfo['media'] = $matches[1];
1790
			} else if (preg_match("/media: Ethernet (.*)/", $ici, $matches)) {
1791
				$ifinfo['media'] = $matches[1];
1792
			} else if (preg_match("/media: IEEE 802.11 Wireless Ethernet (.*)/", $ici, $matches)) {
1793
				$ifinfo['media'] = $matches[1];
1794
			}
1795

    
1796
			if (preg_match("/status: (.*)$/", $ici, $matches)) {
1797
				if ($matches[1] != "active") {
1798
					$ifinfo['status'] = $matches[1];
1799
				}
1800
				if ($ifinfo['status'] == gettext("running")) {
1801
					$ifinfo['status'] = gettext("up");
1802
				}
1803
			}
1804
			if (preg_match("/channel (\S*)/", $ici, $matches)) {
1805
				$ifinfo['channel'] = $matches[1];
1806
			}
1807
			if (preg_match("/ssid (\".*?\"|\S*)/", $ici, $matches)) {
1808
				if ($matches[1][0] == '"') {
1809
					$ifinfo['ssid'] = substr($matches[1], 1, -1);
1810
				}
1811
				else {
1812
					$ifinfo['ssid'] = $matches[1];
1813
				}
1814
			}
1815
			if (preg_match("/laggproto (.*)$/", $ici, $matches)) {
1816
				$ifinfo['laggproto'] = $matches[1];
1817
			}
1818
			if (preg_match("/laggport: (.*)$/", $ici, $matches)) {
1819
				$ifinfo['laggport'][] = $matches[1];
1820
			}
1821
		}
1822
		foreach ($wifconfiginfo as $ici) {
1823
			$elements = preg_split("/[ ]+/i", $ici);
1824
			if ($elements[0] != "") {
1825
				$ifinfo['bssid'] = $elements[0];
1826
			}
1827
			if ($elements[3] != "") {
1828
				$ifinfo['rate'] = $elements[3];
1829
			}
1830
			if ($elements[4] != "") {
1831
				$ifinfo['rssi'] = $elements[4];
1832
			}
1833
		}
1834
		/* lookup the gateway */
1835
		if (interface_has_gateway($ifdescr)) {
1836
			$ifinfo['gateway'] = get_interface_gateway($ifdescr);
1837
			$ifinfo['gatewayv6'] = get_interface_gateway_v6($ifdescr);
1838
		}
1839
	}
1840

    
1841
	$bridge = "";
1842
	$bridge = link_interface_to_bridge($ifdescr);
1843
	if ($bridge) {
1844
		$bridge_text = `/sbin/ifconfig {$bridge}`;
1845
		if (stristr($bridge_text, "blocking") <> false) {
1846
			$ifinfo['bridge'] = "<b><font color='red'>" . gettext("blocking") . "</font></b> - " . gettext("check for ethernet loops");
1847
			$ifinfo['bridgeint'] = $bridge;
1848
		} else if (stristr($bridge_text, "learning") <> false) {
1849
			$ifinfo['bridge'] = gettext("learning");
1850
			$ifinfo['bridgeint'] = $bridge;
1851
		} else if (stristr($bridge_text, "forwarding") <> false) {
1852
			$ifinfo['bridge'] = gettext("forwarding");
1853
			$ifinfo['bridgeint'] = $bridge;
1854
		}
1855
	}
1856

    
1857
	return $ifinfo;
1858
}
1859

    
1860
//returns cpu speed of processor. Good for determining capabilities of machine
1861
function get_cpu_speed() {
1862
	return get_single_sysctl("hw.clockrate");
1863
}
1864

    
1865
function get_uptime_sec() {
1866
	$boottime = "";
1867
	$matches = "";
1868
	$boottime = get_single_sysctl("kern.boottime");
1869
	preg_match("/sec = (\d+)/", $boottime, $matches);
1870
	$boottime = $matches[1];
1871
	if (intval($boottime) == 0) {
1872
		return 0;
1873
	}
1874

    
1875
	$uptime = time() - $boottime;
1876
	return $uptime;
1877
}
1878

    
1879
function resolve_host_addresses($host, $recordtypes = array(DNS_A, DNS_AAAA, DNS_CNAME), $index = true) {
1880
	$dnsresult = array();
1881
	$resolved = array();
1882
	$errreporting = error_reporting();
1883
	error_reporting($errreporting & ~E_WARNING);// dns_get_record throws a warning if nothing is resolved..
1884
	foreach ($recordtypes as $recordtype) {
1885
		$tmp = dns_get_record($host, $recordtype);
1886
		if (is_array($tmp)) {
1887
			$dnsresult = array_merge($dnsresult, $tmp);
1888
		}
1889
	}
1890
	error_reporting($errreporting);// restore original php warning/error settings.
1891
	foreach ($dnsresult as $item) {
1892
		$newitem = array();
1893
		$newitem['type'] = $item['type'];
1894
		switch ($item['type']) {
1895
			case 'CNAME':
1896
				$newitem['data'] = $item['target'];
1897
				$resolved[] = $newitem;
1898
				break;
1899
			case 'A':
1900
				$newitem['data'] = $item['ip'];
1901
				$resolved[] = $newitem;
1902
				break;
1903
			case 'AAAA':
1904
				$newitem['data'] = $item['ipv6'];
1905
				$resolved[] = $newitem;
1906
				break;
1907
		}
1908
	}
1909
	if ($index == false) {
1910
		foreach ($resolved as $res) {
1911
			$noind[] = $res['data'];
1912
		}
1913
		$resolved = $noind;
1914
	}
1915
	return $resolved;
1916
}
1917

    
1918
function add_hostname_to_watch($hostname) {
1919
	if (!is_dir("/var/db/dnscache")) {
1920
		mkdir("/var/db/dnscache");
1921
	}
1922
	$result = array();
1923
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1924
		$contents = "";
1925
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1926
		if (!empty($domips)) {
1927
			foreach ($domips as $ip) {
1928
				$contents .= "$ip\n";
1929
			}
1930
		}
1931
		file_put_contents("/var/db/dnscache/$hostname", $contents);
1932
		/* Remove empty elements */
1933
		$result = array_filter(explode("\n", $contents), 'strlen');
1934
	}
1935
	return $result;
1936
}
1937

    
1938
function is_fqdn($fqdn) {
1939
	return (bool) preg_match('/^(?:(?!-)[-_a-z0-9]+(?<!-)\.)*(?!-)[a-z0-9\-]+(?<!-)\.(?:xn--)?[a-z]+[\.]?$/i', $fqdn);
1940
}
1941

    
1942
function pfsense_default_state_size() {
1943
	/* get system memory amount */
1944
	$memory = get_memory();
1945
	$physmem = $memory[0];
1946
	/* Be cautious and only allocate 10% of system memory to the state table */
1947
	$max_states = (int) ($physmem/10)*1000;
1948
	return $max_states;
1949
}
1950

    
1951
function pfsense_default_tables_size() {
1952
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1953
	return $current;
1954
}
1955

    
1956
function pfsense_default_table_entries_size() {
1957
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1958
	return (trim($current));
1959
}
1960

    
1961
/* Compare the current hostname DNS to the DNS cache we made
1962
 * if it has changed we return the old records
1963
 * if no change we return false */
1964
function compare_hostname_to_dnscache($hostname) {
1965
	if (!is_dir("/var/db/dnscache")) {
1966
		mkdir("/var/db/dnscache");
1967
	}
1968
	$hostname = trim($hostname);
1969
	if (is_readable("/var/db/dnscache/{$hostname}")) {
1970
		$oldcontents = file_get_contents("/var/db/dnscache/{$hostname}");
1971
	} else {
1972
		$oldcontents = "";
1973
	}
1974
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1975
		$contents = "";
1976
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1977
		if (!empty($domips)) {
1978
			foreach ($domips as $ip) {
1979
				$contents .= "$ip\n";
1980
			}
1981
		}
1982
	}
1983

    
1984
	if (trim($oldcontents) != trim($contents)) {
1985
		if ($g['debug']) {
1986
			log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents));
1987
		}
1988
		return ($oldcontents);
1989
	} else {
1990
		return false;
1991
	}
1992
}
1993

    
1994
/*
1995
 * load_crypto() - Load crypto modules if enabled in config.
1996
 */
1997
function load_crypto() {
1998
	global $config, $g;
1999
	$crypto_modules = array('aesni', 'cryptodev');
2000

    
2001
	$enabled_modules = explode('_', $config['system']['crypto_hardware']);
2002

    
2003
	foreach ($enabled_modules as $enmod) {
2004
		if (empty($enmod) || !in_array($enmod, $crypto_modules)) {
2005
			continue;
2006
		}
2007
		if (!is_module_loaded($enmod)) {
2008
			log_error(sprintf(gettext("Loading %s cryptographic accelerator module."), $enmod));
2009
			mwexec("/sbin/kldload " . escapeshellarg($enmod));
2010
		}
2011
	}
2012
}
2013

    
2014
/*
2015
 * load_thermal_hardware() - Load temperature monitor kernel module
2016
 */
2017
function load_thermal_hardware() {
2018
	global $config, $g;
2019
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
2020

    
2021
	if (!in_array($config['system']['thermal_hardware'], $thermal_hardware_modules)) {
2022
		return false;
2023
	}
2024

    
2025
	if (!empty($config['system']['thermal_hardware']) && !is_module_loaded($config['system']['thermal_hardware'])) {
2026
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $config['system']['thermal_hardware']));
2027
		mwexec("/sbin/kldload {$config['system']['thermal_hardware']}");
2028
	}
2029
}
2030

    
2031
/****f* pfsense-utils/isvm
2032
 * NAME
2033
 *   isvm
2034
 * INPUTS
2035
 *	none
2036
 * RESULT
2037
 *   returns true if machine is running under a virtual environment
2038
 ******/
2039
function isvm() {
2040
	$virtualenvs = array("vmware", "parallels", "qemu", "bochs", "plex86", "VirtualBox");
2041
	$_gb = exec('/bin/kenv -q smbios.system.product 2>/dev/null', $output, $rc);
2042

    
2043
	if ($rc != 0 || !isset($output[0])) {
2044
		return false;
2045
	}
2046

    
2047
	foreach ($virtualenvs as $virtualenv) {
2048
		if (stripos($output[0], $virtualenv) !== false) {
2049
			return true;
2050
		}
2051
	}
2052

    
2053
	return false;
2054
}
2055

    
2056
function get_freebsd_version() {
2057
	$version = explode(".", php_uname("r"));
2058
	return $version[0];
2059
}
2060

    
2061
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
2062
	global $config, $g;
2063

    
2064
	$fp = fopen($destination, "wb");
2065

    
2066
	if (!$fp) {
2067
		return false;
2068
	}
2069

    
2070
	$ch = curl_init();
2071
	curl_setopt($ch, CURLOPT_URL, $url);
2072
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify_ssl);
2073
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify_ssl);
2074
	curl_setopt($ch, CURLOPT_FILE, $fp);
2075
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2076
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2077
	curl_setopt($ch, CURLOPT_HEADER, false);
2078
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2079
	if (!isset($config['system']['do_not_send_uniqueid'])) {
2080
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_name'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2081
	} else {
2082
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_name'] . '/' . $g['product_version']);
2083
	}
2084

    
2085
	if (!empty($config['system']['proxyurl'])) {
2086
		curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']);
2087
		if (!empty($config['system']['proxyport'])) {
2088
			curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']);
2089
		}
2090
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
2091
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
2092
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}");
2093
		}
2094
	}
2095

    
2096
	@curl_exec($ch);
2097
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2098
	fclose($fp);
2099
	curl_close($ch);
2100
	if ($http_code == 200) {
2101
		return true;
2102
	} else {
2103
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2104
		unlink_if_exists($destination);
2105
		return false;
2106
	}
2107
}
2108

    
2109
function download_file_with_progress_bar($url, $destination, $verify_ssl = true, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) {
2110
	global $config, $g;
2111
	global $ch, $fout, $file_size, $downloaded, $config, $first_progress_update;
2112
	$file_size = 1;
2113
	$downloaded = 1;
2114
	$first_progress_update = TRUE;
2115
	/* open destination file */
2116
	$fout = fopen($destination, "wb");
2117

    
2118
	if (!$fout) {
2119
		return false;
2120
	}
2121
	/*
2122
	 *      Originally by Author: Keyvan Minoukadeh
2123
	 *      Modified by Scott Ullrich to return Content-Length size
2124
	 */
2125
	$ch = curl_init();
2126
	curl_setopt($ch, CURLOPT_URL, $url);
2127
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify_ssl);
2128
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify_ssl);
2129
	curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
2130
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2131
	curl_setopt($ch, CURLOPT_WRITEFUNCTION, $readbody);
2132
	curl_setopt($ch, CURLOPT_NOPROGRESS, '1');
2133
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2134
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2135
	if (!isset($config['system']['do_not_send_uniqueid'])) {
2136
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_name'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2137
	} else {
2138
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_name'] . '/' . $g['product_version']);
2139
	}
2140

    
2141
	if (!empty($config['system']['proxyurl'])) {
2142
		curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']);
2143
		if (!empty($config['system']['proxyport'])) {
2144
			curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']);
2145
		}
2146
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
2147
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
2148
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}");
2149
		}
2150
	}
2151

    
2152
	@curl_exec($ch);
2153
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2154
	fclose($fout);
2155
	curl_close($ch);
2156
	if ($http_code == 200) {
2157
		return true;
2158
	} else {
2159
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2160
		unlink_if_exists($destination);
2161
		return false;
2162
	}
2163
}
2164

    
2165
function read_header($ch, $string) {
2166
	global $file_size, $fout;
2167
	$length = strlen($string);
2168
	$regs = "";
2169
	preg_match("/(Content-Length:) (.*)/", $string, $regs);
2170
	if ($regs[2] <> "") {
2171
		$file_size = intval($regs[2]);
2172
	}
2173
	ob_flush();
2174
	return $length;
2175
}
2176

    
2177
function read_body($ch, $string) {
2178
	global $fout, $file_size, $downloaded, $sendto, $static_status, $static_output, $lastseen, $first_progress_update;
2179
	global $pkg_interface;
2180
	$length = strlen($string);
2181
	$downloaded += intval($length);
2182
	if ($file_size > 0) {
2183
		$downloadProgress = round(100 * (1 - $downloaded / $file_size), 0);
2184
		$downloadProgress = 100 - $downloadProgress;
2185
	} else {
2186
		$downloadProgress = 0;
2187
	}
2188
	if ($lastseen <> $downloadProgress and $downloadProgress < 101) {
2189
		if ($sendto == "status") {
2190
			if ($pkg_interface == "console") {
2191
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2192
					$tostatus = $static_status . $downloadProgress . "%";
2193
					if ($downloadProgress == 100) {
2194
						$tostatus = $tostatus . "\r";
2195
					}
2196
					update_status($tostatus);
2197
				}
2198
			} else {
2199
				$tostatus = $static_status . $downloadProgress . "%";
2200
				update_status($tostatus);
2201
			}
2202
		} else {
2203
			if ($pkg_interface == "console") {
2204
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2205
					$tooutput = $static_output . $downloadProgress . "%";
2206
					if ($downloadProgress == 100) {
2207
						$tooutput = $tooutput . "\r";
2208
					}
2209
					update_output_window($tooutput);
2210
				}
2211
			} else {
2212
				$tooutput = $static_output . $downloadProgress . "%";
2213
				update_output_window($tooutput);
2214
			}
2215
		}
2216
		if (($pkg_interface != "console") || (($downloadProgress % 10) == 0) || ($downloadProgress < 10)) {
2217
			update_progress_bar($downloadProgress, $first_progress_update);
2218
			$first_progress_update = FALSE;
2219
		}
2220
		$lastseen = $downloadProgress;
2221
	}
2222
	if ($fout) {
2223
		fwrite($fout, $string);
2224
	}
2225
	ob_flush();
2226
	return $length;
2227
}
2228

    
2229
/*
2230
 *   update_output_window: update bottom textarea dynamically.
2231
 */
2232
function update_output_window($text) {
2233
	global $pkg_interface;
2234
	$log = preg_replace("/\n/", "\\n", $text);
2235
	if ($pkg_interface != "console") {
2236
?>
2237
<script type="text/javascript">
2238
//<![CDATA[
2239
	document.getElementById("output").textContent="<?=htmlspecialchars($log)?>";
2240
	document.getElementById("output").scrollTop = document.getElementById("output").scrollHeight;
2241
//]]>
2242
</script>
2243
<?php
2244
	}
2245
	/* ensure that contents are written out */
2246
	ob_flush();
2247
}
2248

    
2249
/*
2250
 *   update_status: update top textarea dynamically.
2251
 */
2252
function update_status($status) {
2253
	global $pkg_interface;
2254

    
2255
	if ($pkg_interface == "console") {
2256
		print ("{$status}");
2257
	}
2258

    
2259
	/* ensure that contents are written out */
2260
	ob_flush();
2261
}
2262

    
2263
/*
2264
 * update_progress_bar($percent, $first_time): updates the javascript driven progress bar.
2265
 */
2266
function update_progress_bar($percent, $first_time) {
2267
	global $pkg_interface;
2268
	if ($percent > 100) {
2269
		$percent = 1;
2270
	}
2271
	if ($pkg_interface <> "console") {
2272
		echo '<script type="text/javascript">';
2273
		echo "\n//<![CDATA[\n";
2274
		echo 'document.getElementById("progressbar").style.width="'. $percent.'%"';
2275
		echo "\n//]]>\n";
2276
		echo '</script>';
2277
	} else {
2278
		if (!($first_time)) {
2279
			echo "\x08\x08\x08\x08\x08";
2280
		}
2281
		echo sprintf("%4d%%", $percent);
2282
	}
2283
}
2284

    
2285
function update_alias_name($new_alias_name, $orig_alias_name) {
2286
	if (!$orig_alias_name) {
2287
		return;
2288
	}
2289

    
2290
	// Firewall rules
2291
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2292
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2293
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2294
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2295
	// NAT Rules
2296
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2297
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2298
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2299
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2300
	update_alias_names_upon_change(array('nat', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2301
	update_alias_names_upon_change(array('nat', 'rule'), array('local-port'), $new_alias_name, $orig_alias_name);
2302
	// NAT 1:1 Rules
2303
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('external'), $new_alias_name, $orig_alias_name);
2304
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2305
	update_alias_names_upon_change(array('nat', 'onetoone'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2306
	// NAT Outbound Rules
2307
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('source', 'network'), $new_alias_name, $orig_alias_name);
2308
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('sourceport'), $new_alias_name, $orig_alias_name);
2309
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2310
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('dstport'), $new_alias_name, $orig_alias_name);
2311
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2312
	// Alias in an alias
2313
	update_alias_names_upon_change(array('aliases', 'alias'), array('address'), $new_alias_name, $orig_alias_name);
2314
}
2315

    
2316
function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) {
2317
	global $g, $config, $pconfig, $debug;
2318
	if (!$origname) {
2319
		return;
2320
	}
2321

    
2322
	$sectionref = &$config;
2323
	foreach ($section as $sectionname) {
2324
		if (is_array($sectionref) && isset($sectionref[$sectionname])) {
2325
			$sectionref = &$sectionref[$sectionname];
2326
		} else {
2327
			return;
2328
		}
2329
	}
2330

    
2331
	if ($debug) {
2332
		$fd = fopen("{$g['tmp_path']}/print_r", "a");
2333
		fwrite($fd, print_r($pconfig, true));
2334
	}
2335

    
2336
	if (is_array($sectionref)) {
2337
		foreach ($sectionref as $itemkey => $item) {
2338
			if ($debug) {
2339
				fwrite($fd, "$itemkey\n");
2340
			}
2341

    
2342
			$fieldfound = true;
2343
			$fieldref = &$sectionref[$itemkey];
2344
			foreach ($field as $fieldname) {
2345
				if (is_array($fieldref) && isset($fieldref[$fieldname])) {
2346
					$fieldref = &$fieldref[$fieldname];
2347
				} else {
2348
					$fieldfound = false;
2349
					break;
2350
				}
2351
			}
2352
			if ($fieldfound && $fieldref == $origname) {
2353
				if ($debug) {
2354
					fwrite($fd, "Setting old alias value $origname to $new_alias_name\n");
2355
				}
2356
				$fieldref = $new_alias_name;
2357
			}
2358
		}
2359
	}
2360

    
2361
	if ($debug) {
2362
		fclose($fd);
2363
	}
2364

    
2365
}
2366

    
2367
function parse_aliases_file($filename, $type = "url", $max_items = -1, $kflc = false) {
2368
	/*
2369
	 * $filename = file to process for example blocklist like DROP:  http://www.spamhaus.org/drop/drop.txt
2370
	 * $type = if set to 'url' then subnets and ips will be returned,
2371
	 *         if set to 'url_ports' port-ranges and ports will be returned
2372
	 * $max_items = sets the maximum amount of valid items to load, -1 the default defines there is no limit.
2373
	 *
2374
	 * RETURNS an array of ip subnets and ip's or ports and port-ranges, returns NULL upon a error conditions (file not found)
2375
	 */
2376

    
2377
	if (!file_exists($filename)) {
2378
		log_error(sprintf(gettext("Could not process non-existent file from alias: %s"), $filename));
2379
		return null;
2380
	}
2381

    
2382
	if (filesize($filename) == 0) {
2383
		log_error(sprintf(gettext("Could not process empty file from alias: %s"), $filename));
2384
		return null;
2385
	}
2386
	$fd = @fopen($filename, 'r');
2387
	if (!$fd) {
2388
		log_error(sprintf(gettext("Could not process aliases from alias: %s"), $filename));
2389
		return null;
2390
	}
2391
	$items = array();
2392
	$comments = array();
2393
	/* NOTE: fgetss() is not a typo RTFM before being smart */
2394
	while (($fc = fgetss($fd)) !== FALSE) {
2395
		$tmp = idn_to_ascii(trim($fc, " \t\n\r"));
2396
		if (empty($tmp)) {
2397
			continue;
2398
		}
2399
		if (($kflc) && (strpos($tmp, '#') === 0)) {	// Keep Full Line Comments (lines beginning with #).
2400
			$comments[] = $tmp;
2401
		} else {
2402
			$tmp_str = strstr($tmp, '#', true);
2403
			if (!empty($tmp_str)) {
2404
				$tmp = $tmp_str;
2405
			}
2406
			$tmp_str = strstr($tmp, ' ', true);
2407
			if (!empty($tmp_str)) {
2408
				$tmp = $tmp_str;
2409
			}
2410
			switch ($type) {
2411
				case "url":
2412
				case "urltable":
2413
					if (is_ipaddr($tmp) || is_subnet($tmp)) {
2414
						$items[] = $tmp;
2415
						break;
2416
					}
2417
					if (is_fqdn($tmp)) {
2418
						$results = resolve_host_addresses($tmp, array(DNS_A, DNS_AAAA), false);
2419
						if (!empty($results)) {
2420
							foreach ($results as $ip) {
2421
								if (is_ipaddr($ip)) {
2422
									$items[] = $ip;
2423
								}
2424
							}
2425
						}
2426
					}
2427
					break;
2428
				case "url_ports":
2429
				case "urltable_ports":
2430
					if (is_port_or_range($tmp)) {
2431
						$items[] = $tmp;
2432
					}
2433
					break;
2434
				default:
2435
					/* unknown type */
2436
					break;
2437
				}
2438
			if (count($items) == $max_items) {
2439
				break;
2440
			}
2441
		}
2442
	}
2443
	fclose($fd);
2444
	return array_merge($comments, $items);
2445
}
2446

    
2447
function update_alias_url_data() {
2448
	global $config, $g;
2449

    
2450
	$updated = false;
2451

    
2452
	/* item is a url type */
2453
	$lockkey = lock('aliasurl');
2454
	if (is_array($config['aliases']['alias'])) {
2455
		foreach ($config['aliases']['alias'] as $x => $alias) {
2456
			if (empty($alias['aliasurl'])) {
2457
				continue;
2458
			}
2459

    
2460
			$address = null;
2461
			foreach ($alias['aliasurl'] as $alias_url) {
2462
				/* fetch down and add in */
2463
				$temp_filename = tempnam("{$g['tmp_path']}/", "alias_import");
2464
				unlink($temp_filename);
2465
				$verify_ssl = isset($config['system']['checkaliasesurlcert']);
2466
				mkdir($temp_filename);
2467
				if (!download_file($alias_url, $temp_filename . "/aliases", $verify_ssl)) {
2468
					log_error(sprintf(gettext("Failed to download alias %s"), $alias_url));
2469
					continue;
2470
				}
2471

    
2472
				/* if the item is tar gzipped then extract */
2473
				if (stripos($alias_url, '.tgz')) {
2474
					if (!process_alias_tgz($temp_filename)) {
2475
						continue;
2476
					}
2477
				}
2478
				if (file_exists("{$temp_filename}/aliases")) {
2479
					$address = parse_aliases_file("{$temp_filename}/aliases", $alias['type'], 5000);
2480
					mwexec("/bin/rm -rf {$temp_filename}");
2481
				}
2482
			}
2483
			if ($address != null) {
2484
				$config['aliases']['alias'][$x]['address'] = implode(" ", $address);
2485
				$updated = true;
2486
			}
2487
		}
2488
	}
2489
	unlock($lockkey);
2490

    
2491
	/* Report status to callers as well */
2492
	return $updated;
2493
}
2494

    
2495
function process_alias_tgz($temp_filename) {
2496
	if (!file_exists('/usr/bin/tar')) {
2497
		log_error(gettext("Alias archive is a .tar/tgz file which cannot be decompressed because utility is missing!"));
2498
		return false;
2499
	}
2500
	rename("{$temp_filename}/aliases", "{$temp_filename}/aliases.tgz");
2501
	mwexec("/usr/bin/tar xzf {$temp_filename}/aliases.tgz -C {$temp_filename}/aliases/");
2502
	unlink("{$temp_filename}/aliases.tgz");
2503
	$files_to_process = return_dir_as_array("{$temp_filename}/");
2504
	/* foreach through all extracted files and build up aliases file */
2505
	$fd = @fopen("{$temp_filename}/aliases", "w");
2506
	if (!$fd) {
2507
		log_error(sprintf(gettext("Could not open %s/aliases for writing!"), $temp_filename));
2508
		return false;
2509
	}
2510
	foreach ($files_to_process as $f2p) {
2511
		$tmpfd = @fopen($f2p, 'r');
2512
		if (!$tmpfd) {
2513
			log_error(sprintf(gettext('The following file could not be read %1$s from %2$s'), $f2p, $temp_filename));
2514
			continue;
2515
		}
2516
		while (($tmpbuf = fread($tmpfd, 65536)) !== FALSE) {
2517
			fwrite($fd, $tmpbuf);
2518
		}
2519
		fclose($tmpfd);
2520
		unlink($f2p);
2521
	}
2522
	fclose($fd);
2523
	unset($tmpbuf);
2524

    
2525
	return true;
2526
}
2527

    
2528
function version_compare_dates($a, $b) {
2529
	$a_time = strtotime($a);
2530
	$b_time = strtotime($b);
2531

    
2532
	if ((!$a_time) || (!$b_time)) {
2533
		return FALSE;
2534
	} else {
2535
		if ($a_time < $b_time) {
2536
			return -1;
2537
		} elseif ($a_time == $b_time) {
2538
			return 0;
2539
		} else {
2540
			return 1;
2541
		}
2542
	}
2543
}
2544
function version_get_string_value($a) {
2545
	$strs = array(
2546
		0 => "ALPHA-ALPHA",
2547
		2 => "ALPHA",
2548
		3 => "BETA",
2549
		4 => "B",
2550
		5 => "C",
2551
		6 => "D",
2552
		7 => "RC",
2553
		8 => "RELEASE",
2554
		9 => "*"			// Matches all release levels
2555
	);
2556
	$major = 0;
2557
	$minor = 0;
2558
	foreach ($strs as $num => $str) {
2559
		if (substr($a, 0, strlen($str)) == $str) {
2560
			$major = $num;
2561
			$n = substr($a, strlen($str));
2562
			if (is_numeric($n)) {
2563
				$minor = $n;
2564
			}
2565
			break;
2566
		}
2567
	}
2568
	return "{$major}.{$minor}";
2569
}
2570
function version_compare_string($a, $b) {
2571
	// Only compare string parts if both versions give a specific release
2572
	// (If either version lacks a string part, assume intended to match all release levels)
2573
	if (isset($a) && isset($b)) {
2574
		return version_compare_numeric(version_get_string_value($a), version_get_string_value($b));
2575
	} else {
2576
		return 0;
2577
	}
2578
}
2579
function version_compare_numeric($a, $b) {
2580
	$a_arr = explode('.', rtrim($a, '.'));
2581
	$b_arr = explode('.', rtrim($b, '.'));
2582

    
2583
	foreach ($a_arr as $n => $val) {
2584
		if (array_key_exists($n, $b_arr)) {
2585
			// So far so good, both have values at this minor version level. Compare.
2586
			if ($val > $b_arr[$n]) {
2587
				return 1;
2588
			} elseif ($val < $b_arr[$n]) {
2589
				return -1;
2590
			}
2591
		} else {
2592
			// a is greater, since b doesn't have any minor version here.
2593
			return 1;
2594
		}
2595
	}
2596
	if (count($b_arr) > count($a_arr)) {
2597
		// b is longer than a, so it must be greater.
2598
		return -1;
2599
	} else {
2600
		// Both a and b are of equal length and value.
2601
		return 0;
2602
	}
2603
}
2604
function pfs_version_compare($cur_time, $cur_text, $remote) {
2605
	// First try date compare
2606
	$v = version_compare_dates($cur_time, $remote);
2607
	if ($v === FALSE) {
2608
		// If that fails, try to compare by string
2609
		// Before anything else, simply test if the strings are equal
2610
		if (($cur_text == $remote) || ($cur_time == $remote)) {
2611
			return 0;
2612
		}
2613
		list($cur_num, $cur_str) = explode('-', $cur_text);
2614
		list($rem_num, $rem_str) = explode('-', $remote);
2615

    
2616
		// First try to compare the numeric parts of the version string.
2617
		$v = version_compare_numeric($cur_num, $rem_num);
2618

    
2619
		// If the numeric parts are the same, compare the string parts.
2620
		if ($v == 0) {
2621
			return version_compare_string($cur_str, $rem_str);
2622
		}
2623
	}
2624
	return $v;
2625
}
2626
function process_alias_urltable($name, $type, $url, $freq, $forceupdate=false, $validateonly=false) {
2627
	global $g, $config;
2628

    
2629
	$urltable_prefix = "/var/db/aliastables/";
2630
	$urltable_filename = $urltable_prefix . $name . ".txt";
2631
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2632

    
2633
	// Make the aliases directory if it doesn't exist
2634
	if (!file_exists($urltable_prefix)) {
2635
		mkdir($urltable_prefix);
2636
	} elseif (!is_dir($urltable_prefix)) {
2637
		unlink($urltable_prefix);
2638
		mkdir($urltable_prefix);
2639
	}
2640

    
2641
	// If the file doesn't exist or is older than update_freq days, fetch a new copy.
2642
	if (!file_exists($urltable_filename) || (filesize($urltable_filename) == "0") ||
2643
	    ((time() - filemtime($urltable_filename)) > ($freq * 86400 - 90)) ||
2644
	    $forceupdate) {
2645

    
2646
		// Try to fetch the URL supplied
2647
		unlink_if_exists($tmp_urltable_filename);
2648
		$verify_ssl = isset($config['system']['checkaliasesurlcert']);
2649
		if (download_file($url, $tmp_urltable_filename, $verify_ssl)) {
2650
			// Convert lines that begin with '$' or ';' to comments '#' instead of deleting them.
2651
			mwexec("/usr/bin/sed -i \"\" -E 's/^[[:space:]]*($|#|;)/#/g; /^#/!s/\;.*//g;' ". escapeshellarg($tmp_urltable_filename));
2652

    
2653
			$type = ($type) ? $type : alias_get_type($name);	// If empty type passed, try to get it from config.
2654

    
2655
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2656
			if ($type == "urltable_ports") {
2657
				$parsed_contents = group_ports($parsed_contents, true);
2658
			}
2659
			if (is_array($parsed_contents)) {
2660
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2661
			} else {
2662
				touch($urltable_filename);
2663
			}
2664

    
2665
			/* Remove existing archive and create an up to date archive if RAM disk is enabled. */
2666
			unlink_if_exists("{$g['cf_conf_path']}/RAM_Disk_Store/{$name}.txt.tgz");
2667
			if (isset($config['system']['use_mfs_tmpvar']) && !file_exists("/conf/ram_disks_failed")) {
2668
				mwexec("/usr/bin/tar -czf " . escapeshellarg("{$g['cf_conf_path']}/RAM_Disk_Store/{$name}.txt.tgz") . " -C / " . escapeshellarg($urltable_filename));
2669
			}
2670

    
2671
			unlink_if_exists($tmp_urltable_filename);
2672
		} else {
2673
			if (!$validateonly) {
2674
				touch($urltable_filename);
2675
			}
2676
			return false;
2677
		}
2678
		return true;
2679
	} else {
2680
		// File exists, and it doesn't need to be updated.
2681
		return -1;
2682
	}
2683
}
2684

    
2685
function get_include_contents($filename) {
2686
	if (is_file($filename)) {
2687
		ob_start();
2688
		include $filename;
2689
		$contents = ob_get_contents();
2690
		ob_end_clean();
2691
		return $contents;
2692
	}
2693
	return false;
2694
}
2695

    
2696
/* This xml 2 array function is courtesy of the php.net comment section on xml_parse.
2697
 * it is roughly 4 times faster then our existing pfSense parser but due to the large
2698
 * size of the RRD xml dumps this is required.
2699
 * The reason we do not use it for pfSense is that it does not know about array fields
2700
 * which causes it to fail on array fields with single items. Possible Todo?
2701
 */
2702
function xml2array($contents, $get_attributes = 1, $priority = 'tag') {
2703
	if (!function_exists('xml_parser_create')) {
2704
		return array ();
2705
	}
2706
	$parser = xml_parser_create('');
2707
	xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
2708
	xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2709
	xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
2710
	xml_parse_into_struct($parser, trim($contents), $xml_values);
2711
	xml_parser_free($parser);
2712
	if (!$xml_values) {
2713
		return; //Hmm...
2714
	}
2715
	$xml_array = array ();
2716
	$parents = array ();
2717
	$opened_tags = array ();
2718
	$arr = array ();
2719
	$current = & $xml_array;
2720
	$repeated_tag_index = array ();
2721
	foreach ($xml_values as $data) {
2722
		unset ($attributes, $value);
2723
		extract($data);
2724
		$result = array ();
2725
		$attributes_data = array ();
2726
		if (isset ($value)) {
2727
			if ($priority == 'tag') {
2728
				$result = $value;
2729
			} else {
2730
				$result['value'] = $value;
2731
			}
2732
		}
2733
		if (isset ($attributes) and $get_attributes) {
2734
			foreach ($attributes as $attr => $val) {
2735
				if ($priority == 'tag') {
2736
					$attributes_data[$attr] = $val;
2737
				} else {
2738
					$result['attr'][$attr] = $val; //Set all the attributes in a array called 'attr'
2739
				}
2740
			}
2741
		}
2742
		if ($type == "open") {
2743
			$parent[$level -1] = & $current;
2744
			if (!is_array($current) or (!in_array($tag, array_keys($current)))) {
2745
				$current[$tag] = $result;
2746
				if ($attributes_data) {
2747
					$current[$tag . '_attr'] = $attributes_data;
2748
				}
2749
				$repeated_tag_index[$tag . '_' . $level] = 1;
2750
				$current = &$current[$tag];
2751
			} else {
2752
				if (isset ($current[$tag][0])) {
2753
					$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
2754
					$repeated_tag_index[$tag . '_' . $level]++;
2755
				} else {
2756
					$current[$tag] = array (
2757
						$current[$tag],
2758
						$result
2759
						);
2760
					$repeated_tag_index[$tag . '_' . $level] = 2;
2761
					if (isset ($current[$tag . '_attr'])) {
2762
						$current[$tag]['0_attr'] = $current[$tag . '_attr'];
2763
						unset ($current[$tag . '_attr']);
2764
					}
2765
				}
2766
				$last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
2767
				$current = &$current[$tag][$last_item_index];
2768
			}
2769
		} elseif ($type == "complete") {
2770
			if (!isset ($current[$tag])) {
2771
				$current[$tag] = $result;
2772
				$repeated_tag_index[$tag . '_' . $level] = 1;
2773
				if ($priority == 'tag' and $attributes_data) {
2774
					$current[$tag . '_attr'] = $attributes_data;
2775
				}
2776
			} else {
2777
				if (isset ($current[$tag][0]) and is_array($current[$tag])) {
2778
					$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
2779
					if ($priority == 'tag' and $get_attributes and $attributes_data) {
2780
						$current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
2781
					}
2782
					$repeated_tag_index[$tag . '_' . $level]++;
2783
				} else {
2784
					$current[$tag] = array (
2785
						$current[$tag],
2786
						$result
2787
						);
2788
					$repeated_tag_index[$tag . '_' . $level] = 1;
2789
					if ($priority == 'tag' and $get_attributes) {
2790
						if (isset ($current[$tag . '_attr'])) {
2791
							$current[$tag]['0_attr'] = $current[$tag . '_attr'];
2792
							unset ($current[$tag . '_attr']);
2793
						}
2794
						if ($attributes_data) {
2795
							$current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
2796
						}
2797
					}
2798
					$repeated_tag_index[$tag . '_' . $level]++; //0 and 1 index is already taken
2799
				}
2800
			}
2801
		} elseif ($type == 'close') {
2802
			$current = &$parent[$level -1];
2803
		}
2804
	}
2805
	return ($xml_array);
2806
}
2807

    
2808
function get_country_name($country_code = "ALL") {
2809
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2810
		return "";
2811
	}
2812

    
2813
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2814
	$country_names_contents = file_get_contents($country_names_xml);
2815
	$country_names = xml2array($country_names_contents);
2816

    
2817
	if ($country_code == "ALL") {
2818
		$country_list = array();
2819
		foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2820
			$country_list[] = array(
2821
				"code" => $country['ISO_3166-1_Alpha-2_Code_element'],
2822
				"name" => ucwords(strtolower($country['ISO_3166-1_Country_name'])));
2823
		}
2824
		return $country_list;
2825
	}
2826

    
2827
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2828
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2829
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2830
		}
2831
	}
2832
	return "";
2833
}
2834

    
2835
/* Return the list of country codes to be used on CAs and certs */
2836
function get_cert_country_codes() {
2837
	$countries = get_country_name();
2838

    
2839
	$country_codes = array();
2840
	foreach ($countries as $country) {
2841
		$country_codes[$country['code']] = $country['code'];
2842
	}
2843
	ksort($country_codes);
2844

    
2845
	/* Preserve historical order: None, US, CA, other countries */
2846
	$first_items[''] = gettext("None");
2847
	$first_items['US'] = $country_codes['US'];
2848
	$first_items['CA'] = $country_codes['CA'];
2849
	unset($country_codes['US']);
2850
	unset($country_codes['CA']);
2851

    
2852
	return array_merge($first_items, $country_codes);
2853
}
2854

    
2855
/* sort by interface only, retain the original order of rules that apply to
2856
   the same interface */
2857
function filter_rules_sort() {
2858
	global $config;
2859

    
2860
	init_config_arr(array('filter', 'rule'));
2861
	/* mark each rule with the sequence number (to retain the order while sorting) */
2862
	for ($i = 0; isset($config['filter']['rule'][$i]); $i++) {
2863
		if (!is_array($config['filter']['rule'][$i])) {
2864
			$config['filter']['rule'][$i] = array();
2865
		}
2866
		$config['filter']['rule'][$i]['seq'] = $i;
2867
	}
2868

    
2869
	usort($config['filter']['rule'], "filter_rules_compare");
2870

    
2871
	/* strip the sequence numbers again */
2872
	for ($i = 0; isset($config['filter']['rule'][$i]); $i++) {
2873
		unset($config['filter']['rule'][$i]['seq']);
2874
	}
2875
}
2876
function filter_rules_compare($a, $b) {
2877
	if (isset($a['floating']) && isset($b['floating'])) {
2878
		return $a['seq'] - $b['seq'];
2879
	} else if (isset($a['floating'])) {
2880
		return -1;
2881
	} else if (isset($b['floating'])) {
2882
		return 1;
2883
	} else if ($a['interface'] == $b['interface']) {
2884
		return $a['seq'] - $b['seq'];
2885
	} else {
2886
		return compare_interface_friendly_names($a['interface'], $b['interface']);
2887
	}
2888
}
2889

    
2890
function generate_ipv6_from_mac($mac) {
2891
	$elements = explode(":", $mac);
2892
	if (count($elements) <> 6) {
2893
		return false;
2894
	}
2895

    
2896
	$i = 0;
2897
	$ipv6 = "fe80::";
2898
	foreach ($elements as $byte) {
2899
		if ($i == 0) {
2900
			$hexadecimal = substr($byte, 1, 2);
2901
			$bitmap = base_convert($hexadecimal, 16, 2);
2902
			$bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT);
2903
			$bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3, 4);
2904
			$byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16);
2905
		}
2906
		$ipv6 .= $byte;
2907
		if ($i == 1) {
2908
			$ipv6 .= ":";
2909
		}
2910
		if ($i == 3) {
2911
			$ipv6 .= ":";
2912
		}
2913
		if ($i == 2) {
2914
			$ipv6 .= "ff:fe";
2915
		}
2916

    
2917
		$i++;
2918
	}
2919
	return $ipv6;
2920
}
2921

    
2922
/****f* pfsense-utils/load_mac_manufacturer_table
2923
 * NAME
2924
 *   load_mac_manufacturer_table
2925
 * INPUTS
2926
 *   none
2927
 * RESULT
2928
 *   returns associative array with MAC-Manufacturer pairs
2929
 ******/
2930
function load_mac_manufacturer_table() {
2931
	/* load MAC-Manufacture data from the file */
2932
	$macs = false;
2933
	if (file_exists("/usr/local/share/nmap/nmap-mac-prefixes")) {
2934
		$macs=file("/usr/local/share/nmap/nmap-mac-prefixes");
2935
	}
2936
	if ($macs) {
2937
		foreach ($macs as $line) {
2938
			if (preg_match('/([0-9A-Fa-f]{6}) (.*)$/', $line, $matches)) {
2939
				/* store values like this $mac_man['000C29']='VMware' */
2940
				$mac_man["$matches[1]"] = $matches[2];
2941
			}
2942
		}
2943
		return $mac_man;
2944
	} else {
2945
		return -1;
2946
	}
2947

    
2948
}
2949

    
2950
/****f* pfsense-utils/is_ipaddr_configured
2951
 * NAME
2952
 *   is_ipaddr_configured
2953
 * INPUTS
2954
 *   IP Address to check.
2955
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2956
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2957
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2958
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2959
 *     If check_subnets is true and cidrprefix is specified,
2960
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2961
 * RESULT
2962
 *   returns true if the IP Address is configured and present on this device or overlaps a configured subnet.
2963
*/
2964
function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2965
	if (count(where_is_ipaddr_configured($ipaddr, $ignore_if, $check_localip, $check_subnets, $cidrprefix))) {
2966
		return true;
2967
	}
2968
	return false;
2969
}
2970

    
2971
/****f* pfsense-utils/where_is_ipaddr_configured
2972
 * NAME
2973
 *   where_is_ipaddr_configured
2974
 * INPUTS
2975
 *   IP Address to check.
2976
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2977
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2978
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2979
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2980
 *     If check_subnets is true and cidrprefix is specified,
2981
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2982
 * RESULT
2983
 *   Returns an array of the interfaces 'if' plus IP address or subnet 'ip_or_subnet' that match or overlap the IP address to check.
2984
 *   If there are no matches then an empty array is returned.
2985
*/
2986
function where_is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2987
	global $config;
2988

    
2989
	$where_configured = array();
2990

    
2991
	$pos = strpos($ignore_if, '_virtualip');
2992
	if ($pos !== false) {
2993
		$ignore_vip_id = substr($ignore_if, $pos+10);
2994
		$ignore_vip_if = substr($ignore_if, 0, $pos);
2995
	} else {
2996
		$ignore_vip_id = -1;
2997
		$ignore_vip_if = $ignore_if;
2998
	}
2999

    
3000
	$isipv6 = is_ipaddrv6($ipaddr);
3001

    
3002
	if ($isipv6) {
3003
		$ipaddr = text_to_compressed_ip6($ipaddr);
3004
	}
3005

    
3006
	if ($check_subnets) {
3007
		$cidrprefix = intval($cidrprefix);
3008
		if ($isipv6) {
3009
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
3010
				$cidrprefix = 128;
3011
			}
3012
		} else {
3013
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
3014
				$cidrprefix = 32;
3015
			}
3016
		}
3017
		$iflist = get_configured_interface_list();
3018
		foreach ($iflist as $if => $ifname) {
3019
			if ($ignore_if == $if) {
3020
				continue;
3021
			}
3022

    
3023
			if ($isipv6) {
3024
				$if_ipv6 = get_interface_ipv6($if);
3025
				$if_snbitsv6 = get_interface_subnetv6($if);
3026
				if ($if_ipv6 && $if_snbitsv6 && check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) {
3027
					$where_entry = array();
3028
					$where_entry['if'] = $if;
3029
					$where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if);
3030
					$where_configured[] = $where_entry;
3031
				}
3032
			} else {
3033
				$if_ipv4 = get_interface_ip($if);
3034
				$if_snbitsv4 = get_interface_subnet($if);
3035
				if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) {
3036
					$where_entry = array();
3037
					$where_entry['if'] = $if;
3038
					$where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if);
3039
					$where_configured[] = $where_entry;
3040
				}
3041
			}
3042
		}
3043
	} else {
3044
		if ($isipv6) {
3045
			$interface_list_ips = get_configured_ipv6_addresses();
3046
		} else {
3047
			$interface_list_ips = get_configured_ip_addresses();
3048
		}
3049

    
3050
		foreach ($interface_list_ips as $if => $ilips) {
3051
			if ($ignore_if == $if) {
3052
				continue;
3053
			}
3054
			if (strcasecmp($ipaddr, $ilips) == 0) {
3055
				$where_entry = array();
3056
				$where_entry['if'] = $if;
3057
				$where_entry['ip_or_subnet'] = $ilips;
3058
				$where_configured[] = $where_entry;
3059
			}
3060
		}
3061
	}
3062

    
3063
	if ($check_localip) {
3064
		if (!is_array($config['l2tp']) && !empty($config['l2tp']['localip']) && (strcasecmp($ipaddr, text_to_compressed_ip6($config['l2tp']['localip'])) == 0)) {
3065
			$where_entry = array();
3066
			$where_entry['if'] = 'l2tp';
3067
			$where_entry['ip_or_subnet'] = $config['l2tp']['localip'];
3068
			$where_configured[] = $where_entry;
3069
		}
3070
	}
3071

    
3072
	return $where_configured;
3073
}
3074

    
3075
/****f* pfsense-utils/pfSense_handle_custom_code
3076
 * NAME
3077
 *   pfSense_handle_custom_code
3078
 * INPUTS
3079
 *   directory name to process
3080
 * RESULT
3081
 *   globs the directory and includes the files
3082
 */
3083
function pfSense_handle_custom_code($src_dir) {
3084
	// Allow extending of the nat edit page and include custom input validation
3085
	if (is_dir("$src_dir")) {
3086
		$cf = glob($src_dir . "/*.inc");
3087
		foreach ($cf as $nf) {
3088
			if ($nf == "." || $nf == "..") {
3089
				continue;
3090
			}
3091
			// Include the extra handler
3092
			include_once("$nf");
3093
		}
3094
	}
3095
}
3096

    
3097
function set_language() {
3098
	global $config, $g;
3099

    
3100
	if (!empty($config['system']['language'])) {
3101
		$lang = $config['system']['language'];
3102
	} elseif (!empty($g['language'])) {
3103
		$lang = $g['language'];
3104
	}
3105
	$lang .= ".UTF-8";
3106

    
3107
	putenv("LANG={$lang}");
3108
	setlocale(LC_ALL, $lang);
3109
	textdomain("pfSense");
3110
	bindtextdomain("pfSense", "/usr/local/share/locale");
3111
	bind_textdomain_codeset("pfSense", $lang);
3112
}
3113

    
3114
function get_locale_list() {
3115
	$locales = array(
3116
		"bs" => gettext("Bosnian"),
3117
		"zh_CN" => gettext("Chinese"),
3118
		"zh_Hans_CN" => gettext("Chinese (Simplified, China)"),
3119
		"zh_Hans_HK" => gettext("Chinese (Simplified, Hong Kong SAR China)"),
3120
		"zh_Hant_TW" => gettext("Chinese (Traditional, Taiwan)"),
3121
		"nl" => gettext("Dutch"),
3122
		"en_US" => gettext("English"),
3123
		"fr" => gettext("French"),
3124
		"de_DE" => gettext("German (Germany)"),
3125
		"it" => gettext("Italian"),
3126
		"ko" => gettext("Korean"),
3127
		"nb" => gettext("Norwegian Bokmål"),
3128
		"pl" => gettext("Polish"),
3129
		"pt_PT" => gettext("Portuguese"),
3130
		"pt_BR" => gettext("Portuguese (Brazil)"),
3131
		"ru" => gettext("Russian"),
3132
		"es" => gettext("Spanish"),
3133
		"es_AR" => gettext("Spanish (Argentina)"),
3134
	);
3135

    
3136
	// If the locales are sorted, the order changes depending on the language selected. If the user accidentally
3137
	// selects the wrong language, this makes it very difficult to guess the intended language. NOT sorting
3138
	// allows the user to remember that English (say) is the seventh on the list and to get back to it more easily
3139

    
3140
	//asort($locales);
3141

    
3142
	return $locales;
3143
}
3144

    
3145
function return_hex_ipv4($ipv4) {
3146
	if (!is_ipaddrv4($ipv4)) {
3147
		return(false);
3148
	}
3149

    
3150
	/* we need the hex form of the interface IPv4 address */
3151
	$ip4arr = explode(".", $ipv4);
3152
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3153
}
3154

    
3155
function convert_ipv6_to_128bit($ipv6) {
3156
	if (!is_ipaddrv6($ipv6)) {
3157
		return(false);
3158
	}
3159

    
3160
	$ip6arr = array();
3161
	$ip6prefix = Net_IPv6::uncompress($ipv6);
3162
	$ip6arr = explode(":", $ip6prefix);
3163
	/* binary presentation of the prefix for all 128 bits. */
3164
	$ip6prefixbin = "";
3165
	foreach ($ip6arr as $element) {
3166
		$ip6prefixbin .= sprintf("%016b", hexdec($element));
3167
	}
3168
	return($ip6prefixbin);
3169
}
3170

    
3171
function convert_128bit_to_ipv6($ip6bin) {
3172
	if (strlen($ip6bin) <> 128) {
3173
		return(false);
3174
	}
3175

    
3176
	$ip6arr = array();
3177
	$ip6binarr = array();
3178
	$ip6binarr = str_split($ip6bin, 16);
3179
	foreach ($ip6binarr as $binpart) {
3180
		$ip6arr[] = dechex(bindec($binpart));
3181
	}
3182
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3183

    
3184
	return($ip6addr);
3185
}
3186

    
3187

    
3188
/* Returns the calculated bit length of the prefix delegation from the WAN interface */
3189
/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */
3190
/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */
3191
/* 6to4 is 16 bits, e.g. 65535 */
3192
function calculate_ipv6_delegation_length($if) {
3193
	global $config;
3194

    
3195
	if (!is_array($config['interfaces'][$if])) {
3196
		return false;
3197
	}
3198

    
3199
	switch ($config['interfaces'][$if]['ipaddrv6']) {
3200
		case "6to4":
3201
			$pdlen = 16;
3202
			break;
3203
		case "6rd":
3204
			$rd6cfg = $config['interfaces'][$if];
3205
			$rd6plen = explode("/", $rd6cfg['prefix-6rd']);
3206
			$pdlen = (64 - ((int) $rd6plen[1] + (32 - (int) $rd6cfg['prefix-6rd-v4plen'])));
3207
			break;
3208
		case "dhcp6":
3209
			$dhcp6cfg = $config['interfaces'][$if];
3210
			$pdlen = $dhcp6cfg['dhcp6-ia-pd-len'];
3211
			break;
3212
		default:
3213
			$pdlen = 0;
3214
			break;
3215
	}
3216
	return($pdlen);
3217
}
3218

    
3219
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3220
	$prefix = Net_IPv6::uncompress($prefix, true);
3221
	$suffix = Net_IPv6::uncompress($suffix, true);
3222

    
3223
	/*
3224
	 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
3225
	 *                ^^^^ ^
3226
	 *                |||| \-> 64
3227
	 *                |||\---> 63, 62, 61, 60
3228
	 *                ||\----> 56
3229
	 *                |\-----> 52
3230
	 *                \------> 48
3231
	 */
3232

    
3233
	switch ($len) {
3234
	case 48:
3235
		$prefix_len = 15;
3236
		break;
3237
	case 52:
3238
		$prefix_len = 16;
3239
		break;
3240
	case 56:
3241
		$prefix_len = 17;
3242
		break;
3243
	case 59:
3244
	case 60:
3245
		$prefix_len = 18;
3246
		break;
3247
	/*
3248
	 * XXX 63, 62 and 61 should use 18 but PD can change and if
3249
	 * we let user chose this bit it can end up out of PD network
3250
	 *
3251
	 * Leave this with 20 for now until we find a way to let user
3252
	 * chose it. The side-effect is users with PD with one of these
3253
	 * lengths will not be able to setup DHCP server range for full
3254
	 * PD size, only for last /64 network
3255
	 */
3256
	case 63:
3257
	case 62:
3258
	case 61:
3259
	default:
3260
		$prefix_len = 20;
3261
		break;
3262
	}
3263

    
3264
	return text_to_compressed_ip6(substr($prefix, 0, $prefix_len) .
3265
	    substr($suffix, $prefix_len));
3266
}
3267

    
3268
function dhcpv6_pd_str_help($pdlen) {
3269
	$result = '';
3270

    
3271
	switch ($pdlen) {
3272
	case 48:
3273
		$result = '::xxxx:xxxx:xxxx:xxxx:xxxx';
3274
		break;
3275
	case 52:
3276
		$result = '::xxx:xxxx:xxxx:xxxx:xxxx';
3277
		break;
3278
	case 56:
3279
		$result = '::xx:xxxx:xxxx:xxxx:xxxx';
3280
		break;
3281
	case 59:
3282
	case 60:
3283
		$result = '::x:xxxx:xxxx:xxxx:xxxx';
3284
		break;
3285
	/*
3286
	 * XXX 63, 62 and 61 should use same mask as 60 but if
3287
	 * we let the user choose this bit it can end up out of PD network
3288
	 *
3289
	 * Leave this with the same as 64 for now until we find a way to
3290
	 * let the user choose it. The side-effect is users with PD with one
3291
	 * of these lengths will not be able to setup DHCP server ranges
3292
	 * for full PD size, only for last /64 network
3293
	 */
3294
	case 61:
3295
	case 62:
3296
	case 63:
3297
	case 64:
3298
	default:
3299
		$result = '::xxxx:xxxx:xxxx:xxxx';
3300
		break;
3301
	}
3302

    
3303
	return $result;
3304
}
3305

    
3306
function huawei_rssi_to_string($rssi) {
3307
	$dbm = array();
3308
	$i = 0;
3309
	$dbstart = -113;
3310
	while ($i < 32) {
3311
		$dbm[$i] = $dbstart + ($i * 2);
3312
		$i++;
3313
	}
3314
	$percent = round(($rssi / 31) * 100);
3315
	$string = "rssi:{$rssi} level:{$dbm[$rssi]}dBm percent:{$percent}%";
3316
	return $string;
3317
}
3318

    
3319
function huawei_mode_to_string($mode, $submode) {
3320
	$modes[0] = gettext("None");
3321
	$modes[1] = "AMPS";
3322
	$modes[2] = "CDMA";
3323
	$modes[3] = "GSM/GPRS";
3324
	$modes[4] = "HDR";
3325
	$modes[5] = "WCDMA";
3326
	$modes[6] = "GPS";
3327

    
3328
	$submodes[0] = gettext("No Service");
3329
	$submodes[1] = "GSM";
3330
	$submodes[2] = "GPRS";
3331
	$submodes[3] = "EDGE";
3332
	$submodes[4] = "WCDMA";
3333
	$submodes[5] = "HSDPA";
3334
	$submodes[6] = "HSUPA";
3335
	$submodes[7] = "HSDPA+HSUPA";
3336
	$submodes[8] = "TD-SCDMA";
3337
	$submodes[9] = "HSPA+";
3338
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3339
	return $string;
3340
}
3341

    
3342
function huawei_service_to_string($state) {
3343
	$modes[0] = gettext("No Service");
3344
	$modes[1] = gettext("Restricted Service");
3345
	$modes[2] = gettext("Valid Service");
3346
	$modes[3] = gettext("Restricted Regional Service");
3347
	$modes[4] = gettext("Powersaving Service");
3348
	$modes[255] = gettext("Unknown Service");
3349
	$string = $modes[$state];
3350
	return $string;
3351
}
3352

    
3353
function huawei_simstate_to_string($state) {
3354
	$modes[0] = gettext("Invalid SIM/locked State");
3355
	$modes[1] = gettext("Valid SIM State");
3356
	$modes[2] = gettext("Invalid SIM CS State");
3357
	$modes[3] = gettext("Invalid SIM PS State");
3358
	$modes[4] = gettext("Invalid SIM CS/PS State");
3359
	$modes[255] = gettext("Missing SIM State");
3360
	$string = $modes[$state];
3361
	return $string;
3362
}
3363

    
3364
function zte_rssi_to_string($rssi) {
3365
	return huawei_rssi_to_string($rssi);
3366
}
3367

    
3368
function zte_mode_to_string($mode, $submode) {
3369
	$modes[0] = gettext("No Service");
3370
	$modes[1] = gettext("Limited Service");
3371
	$modes[2] = "GPRS";
3372
	$modes[3] = "GSM";
3373
	$modes[4] = "UMTS";
3374
	$modes[5] = "EDGE";
3375
	$modes[6] = "HSDPA";
3376

    
3377
	$submodes[0] = "CS_ONLY";
3378
	$submodes[1] = "PS_ONLY";
3379
	$submodes[2] = "CS_PS";
3380
	$submodes[3] = "CAMPED";
3381
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3382
	return $string;
3383
}
3384

    
3385
function zte_service_to_string($service) {
3386
	$modes[0] = gettext("Initializing Service");
3387
	$modes[1] = gettext("Network Lock error Service");
3388
	$modes[2] = gettext("Network Locked Service");
3389
	$modes[3] = gettext("Unlocked or correct MCC/MNC Service");
3390
	$string = $modes[$service];
3391
	return $string;
3392
}
3393

    
3394
function zte_simstate_to_string($state) {
3395
	$modes[0] = gettext("No action State");
3396
	$modes[1] = gettext("Network lock State");
3397
	$modes[2] = gettext("(U)SIM card lock State");
3398
	$modes[3] = gettext("Network Lock and (U)SIM card Lock State");
3399
	$string = $modes[$state];
3400
	return $string;
3401
}
3402

    
3403
function get_configured_pppoe_server_interfaces() {
3404
	global $config;
3405
	$iflist = array();
3406
	if (is_array($config['pppoes']['pppoe'])) {
3407
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
3408
			if ($pppoe['mode'] == "server") {
3409
				$int = "poes". $pppoe['pppoeid'];
3410
				$iflist[$int] = strtoupper($int);
3411
			}
3412
		}
3413
	}
3414
	return $iflist;
3415
}
3416

    
3417
function get_pppoes_child_interfaces($ifpattern) {
3418
	$if_arr = array();
3419
	if ($ifpattern == "") {
3420
		return;
3421
	}
3422

    
3423
	exec("/sbin/ifconfig", $out, $ret);
3424
	foreach ($out as $line) {
3425
		if (preg_match("/^({$ifpattern}[0-9]+):/i", $line, $match)) {
3426
			$if_arr[] = $match[1];
3427
		}
3428
	}
3429
	return $if_arr;
3430

    
3431
}
3432

    
3433
/****f* pfsense-utils/pkg_call_plugins
3434
 * NAME
3435
 *   pkg_call_plugins
3436
 * INPUTS
3437
 *   $plugin_type value used to search in package configuration if the plugin is used, also used to create the function name
3438
 *   $plugin_params parameters to pass to the plugin function for passing multiple parameters a array can be used.
3439
 * RESULT
3440
 *   returns associative array results from the plugin calls for each package
3441
 * NOTES
3442
 *   This generic function can be used to notify or retrieve results from functions that are defined in packages.
3443
 ******/
3444
function pkg_call_plugins($plugin_type, $plugin_params) {
3445
	global $g, $config;
3446
	$results = array();
3447
	if (!is_array($config['installedpackages']['package'])) {
3448
		return $results;
3449
	}
3450
	foreach ($config['installedpackages']['package'] as $package) {
3451
		if (is_array($package['plugins']['item'])) {
3452
			foreach ($package['plugins']['item'] as $plugin) {
3453
				if ($plugin['type'] == $plugin_type) {
3454
					if (file_exists($package['include_file'])) {
3455
						require_once($package['include_file']);
3456
					} else {
3457
						continue;
3458
					}
3459
					$pkgname = substr(reverse_strrchr($package['configurationfile'], "."), 0, -1);
3460
					$plugin_function = $pkgname . '_'. $plugin_type;
3461
					$results[$pkgname] = call_user_func($plugin_function, $plugin_params);
3462
				}
3463
			}
3464
		}
3465
	}
3466
	return $results;
3467
}
3468

    
3469
// Convert IPv6 addresses to lower case
3470
function addrtolower($ip) {
3471
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3472
		return(strtolower($ip));
3473
	} else {
3474
		return($ip);
3475
	}
3476
}
3477

    
3478
function compare_by_name($a, $b) {
3479
	return strcasecmp($a['name'], $b['name']);
3480
}
3481

    
3482
/****f* pfsense-utils/getarraybyref
3483
 * NAME
3484
 *   getarraybyref
3485
 * INPUTS
3486
 *   $array the array of which a items array needs to be found.
3487
 *   $args.. the sub-items to be retrieved/created
3488
 * RESULT
3489
 *   returns the array that was retrieved from configuration, its created if it does not exist
3490
 * NOTES
3491
 *   Used by haproxy / acme / others.?. .
3492
 *   can be used like this:  $a_certificates = getarraybyref($config, 'installedpackages', 'acme', 'certificates', 'item');
3493
 ******/
3494
function &getarraybyref(&$array) {
3495
	if (!isset($array)) {
3496
		return false;
3497
	}
3498
	if (!is_array($array)) {
3499
		$array = array();
3500
	}
3501
	$item = &$array;
3502
	$arg = func_get_args();
3503
	for($i = 1; $i < count($arg); $i++) {
3504
		$itemindex = $arg[$i];
3505
		if (!is_array($item[$itemindex])) {
3506
			$item[$itemindex] = array();
3507
		}
3508
		$item = &$item[$itemindex];
3509
	}
3510
	return $item;
3511
}
3512

    
3513
/****f* pfsense-utils/send_download_data
3514
 * NAME
3515
 *   send_download_data - Send content to a user's browser as a file to download
3516
 * INPUTS
3517
 *   $type        : The type of download, either 'data' to send the contents of
3518
 *                    a variable or 'file' to send the contents of a file on the
3519
 *                    filesystem.
3520
 *   $content     : For 'data' type, the content to send the user. For 'file'
3521
 *                    type, the full path to the file to send.
3522
 *   $userfilename: The filename presented to the user when downloading. For
3523
 *                    'file' type, this may be omitted and the basename of
3524
 *                    $content will be used instead.
3525
 *   $contenttype : MIME content type of the data. Default "application/octet-stream"
3526
 * RESULT
3527
 *   Sends the data to the browser as a file to download.
3528
 ******/
3529

    
3530
function send_user_download($type = 'data', $content, $userfilename = "", $contenttype = "application/octet-stream") {
3531
	/* If the type is 'file', then use the file size, otherwise use the size of the data to send */
3532
	$size = ($type == 'file') ? filesize($content) : strlen($content);
3533

    
3534
	/* If the filename to pass to the user is empty, assume it to be the filename being read. */
3535
	$name = basename((($type == 'file') && empty($userfilename)) ? $content : $userfilename);
3536

    
3537
	/* Cannot determine the filename, so bail. */
3538
	if (empty($name)) {
3539
		exit;
3540
	}
3541

    
3542
	/* Send basic download headers */
3543
	header("Content-Type: {$contenttype}");
3544
	header("Content-Length: {$size}");
3545
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3546

    
3547
	/* Send cache headers */
3548
	if (isset($_SERVER['HTTPS'])) {
3549
		header('Pragma: ');
3550
		header('Cache-Control: ');
3551
	} else {
3552
		header("Pragma: private");
3553
		header("Cache-Control: private, must-revalidate");
3554
	}
3555

    
3556
	/* Ensure output buffering is off so PHP does not consume
3557
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3558
	while (ob_get_level()) {
3559
		@ob_end_clean();
3560
	}
3561

    
3562
	/* Send the data to the user */
3563
	if ($type == 'file') {
3564
		readfile($content);
3565
	} else {
3566
		echo $content;
3567
	}
3568

    
3569
	/* Flush any remaining output buffer */
3570
	@ob_end_flush();
3571
	exit;
3572
}
3573

    
3574
// Test whether the hostname in a URL can be resolved with a very short timeout
3575
function is_url_hostname_resolvable($url) {
3576
	$urlhostname = parse_url($url, PHP_URL_HOST);
3577
	if (empty($urlhostname)) {
3578
		return false;
3579
	}
3580
	putenv("RES_OPTIONS=timeout:3 attempts:1");
3581
	$resolvable = ($urlhostname !== gethostbyname($urlhostname));
3582
	putenv("RES_OPTIONS");
3583
	return $resolvable;
3584
}
3585

    
3586
function get_pf_timeouts () {
3587
	$pftimeout = array();
3588
	exec("/sbin/pfctl -st", $pfctlst, $retval);
3589
	if ($retval == 0) {
3590
		foreach ($pfctlst as $pfst) {
3591
			preg_match('/([a-z]+)\.([a-z]+)\s+([0-9]+)/', $pfst, $timeout);
3592
			if ($timeout[1] == "other") {
3593
				$proto = "Other";
3594
			} else {
3595
				$proto = strtoupper($timeout[1]);
3596
			}
3597
			if ($timeout[2] == "finwait") {
3598
				$type = "FIN Wait";
3599
			} else {
3600
				$type = ucfirst($timeout[2]);
3601
			}
3602
			$pftimeout[$proto][$type]['name'] = $proto . " " . $type;
3603
			$pftimeout[$proto][$type]['keyname'] = $timeout[1] . $timeout[2] . "timeout";
3604
			$pftimeout[$proto][$type]['value'] = $timeout[3];
3605
		}
3606
	}
3607
	return $pftimeout;
3608
}
3609

    
3610
?>
(38-38/61)