Project

General

Profile

Download (108 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-2023 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
require_once("config.inc");
25
require_once("config.lib.inc");
26

    
27
function dhcp_is_backend($dhcpbackend)
28
{
29
	return (dhcp_get_backend() === $dhcpbackend);
30
}
31

    
32
function dhcp_get_backend()
33
{
34
	$dhcpbackend = config_get_path('dhcpbackend');
35
	return (in_array($dhcpbackend, ['isc', 'kea']) ? $dhcpbackend : 'isc');
36
}
37

    
38
function display_isc_warning()
39
{
40
	if (!dhcp_is_backend('isc') ||
41
	    config_path_enabled('system', 'ignoreiscwarning')) {
42
		return;
43
	}
44

    
45
	print_info_box(sprintf(gettext('ISC DHCP has reached end-of-life and will be removed in a future version of %s. Visit %s to switch DHCP backend.'), g_get('product_label'), sprintf('<a href="%s">%s</a>', 'system_advanced_network.php', 'System > Advanced > Networking')), 'warning', false);
46
}
47

    
48
/****f* pfsense-utils/have_natpfruleint_access
49
 * NAME
50
 *   have_natpfruleint_access
51
 * INPUTS
52
 *	none
53
 * RESULT
54
 *   returns true if user has access to edit a specific firewall nat port forward interface
55
 ******/
56
function have_natpfruleint_access($if) {
57
	$security_url = "firewall_nat_edit.php?if=". strtolower($if);
58
	if (isAllowedPage($security_url)) {
59
		return true;
60
	}
61
	return false;
62
}
63

    
64
/****f* pfsense-utils/have_ruleint_access
65
 * NAME
66
 *   have_ruleint_access
67
 * INPUTS
68
 *	none
69
 * RESULT
70
 *   returns true if user has access to edit a specific firewall interface
71
 ******/
72
function have_ruleint_access($if) {
73
	$security_url = "firewall_rules.php?if=". strtolower($if);
74
	if (isAllowedPage($security_url)) {
75
		return true;
76
	}
77
	return false;
78
}
79

    
80
/****f* pfsense-utils/does_url_exist
81
 * NAME
82
 *   does_url_exist
83
 * INPUTS
84
 *	none
85
 * RESULT
86
 *   returns true if a url is available
87
 ******/
88
function does_url_exist($url) {
89
	$fd = fopen("$url", "r");
90
	if ($fd) {
91
		fclose($fd);
92
		return true;
93
	} else {
94
		return false;
95
	}
96
}
97

    
98
/****f* pfsense-utils/is_private_ip
99
 * NAME
100
 *   is_private_ip
101
 * INPUTS
102
 *	none
103
 * RESULT
104
 *   returns true if an ip address is in a private range
105
 ******/
106
function is_private_ip($iptocheck) {
107
	$isprivate = false;
108
	$ip_private_list = array(
109
		"10.0.0.0/8",
110
		"100.64.0.0/10",
111
		"172.16.0.0/12",
112
		"192.168.0.0/16",
113
	);
114
	foreach ($ip_private_list as $private) {
115
		if (ip_in_subnet($iptocheck, $private) == true) {
116
			$isprivate = true;
117
		}
118
	}
119
	return $isprivate;
120
}
121

    
122
/****f* pfsense-utils/get_tmp_file
123
 * NAME
124
 *   get_tmp_file
125
 * INPUTS
126
 *	none
127
 * RESULT
128
 *   returns a temporary filename
129
 ******/
130
function get_tmp_file() {
131
	global $g;
132
	return "{$g['tmp_path']}/tmp-" . time();
133
}
134

    
135
/* Stub for deprecated function
136
 * See https://redmine.pfsense.org/issues/10931 */
137
function get_dns_servers() {
138
	return get_dns_nameservers(false, true);
139
}
140

    
141
/****f* pfsense-utils/pfSenseHeader
142
 * NAME
143
 *   pfSenseHeader
144
 * INPUTS
145
 *   none
146
 * RESULT
147
 *   JavaScript header change or browser Location:
148
 ******/
149
function pfSenseHeader($text) {
150
	global $_SERVER;
151
	if (isAjax()) {
152
		if ($_SERVER['HTTPS'] == "on") {
153
			$protocol = "https";
154
		} else {
155
			$protocol = "http";
156
		}
157

    
158
		$port = ":{$_SERVER['SERVER_PORT']}";
159
		if ($_SERVER['SERVER_PORT'] == "80" && $protocol == "http") {
160
			$port = "";
161
		}
162
		if ($_SERVER['SERVER_PORT'] == "443" && $protocol == "https") {
163
			$port = "";
164
		}
165
		$complete_url = "{$protocol}://{$_SERVER['HTTP_HOST']}{$port}/{$text}";
166
		echo "\ndocument.location.href = '{$complete_url}';\n";
167
	} else {
168
		header("Location: $text");
169
	}
170
}
171

    
172
/****f* pfsense-utils/get_css_files
173
 * NAME
174
 *   get_css_files - get a list of the available CSS files (themes)
175
 * INPUTS
176
 *   none
177
 * RESULT
178
 *   $csslist - an array of the CSS files
179
 ******/
180
function get_css_files() {
181
	$csslist = array();
182

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

    
186
	if (is_array($cssfiles)) {
187
		arsort($cssfiles);
188
		$usrcss = $pfscss = $betacss = array();
189

    
190
		foreach ($cssfiles as $css) {
191
			// Don't display any login/logo page related CSS files
192
			if (strpos($css, "login") == 0 &&
193
			    strpos($css, "logo") == 0) {
194
				if (strpos($css, "BETA") != 0) {
195
					array_push($betacss, $css);
196
				} else if (strpos($css, "pfSense") != 0) {
197
					array_push($pfscss, $css);
198
				} else {
199
					array_push($usrcss, $css);
200
				}
201
			}
202
		}
203

    
204
		$css = array_merge($pfscss, $betacss, $usrcss);
205

    
206
		foreach ($css as $file) {
207
			$file = basename($file);
208
			$csslist[$file] = pathinfo($file, PATHINFO_FILENAME);
209
		}
210
	}
211
	return $csslist;
212
}
213

    
214
/****f* pfsense-utils/gen_webguicss_field
215
 * NAME
216
 *   gen_webguicss_field
217
 * INPUTS
218
 *   Pointer to section object
219
 *   Initial value for the field
220
 * RESULT
221
 *   no return value, section object is updated
222
 ******/
223
function gen_webguicss_field(&$section, $value) {
224

    
225
	$csslist = get_css_files();
226

    
227
	if (!isset($csslist[$value])) {
228
		$value = "pfSense.css";
229
	}
230

    
231
	$section->addInput(new Form_Select(
232
		'webguicss',
233
		'Theme',
234
		$value,
235
		$csslist
236
	))->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>');
237
}
238
function validate_webguicss_field(&$input_errors, $value) {
239
	$csslist = get_css_files();
240
	if (!isset($csslist[$value])) {
241
		$input_errors[] = gettext("The submitted Theme could not be found. Pick a different theme.");
242
	}
243
}
244

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

    
256
	$section->addInput(new Form_Select(
257
		'webguifixedmenu',
258
		'Top Navigation',
259
		$value,
260
		["" => gettext("Scrolls with page"), "fixed" => gettext("Fixed (Remains visible at top of page)")]
261
	))->setHelp("The fixed option is intended for large screens only.");
262
}
263
function validate_webguifixedmenu_field(&$input_errors, $value) {
264
	$valid_values = array("", "fixed");
265
	if (!in_array($value, $valid_values)) {
266
		$input_errors[] = gettext("The submitted Top Navigation value is invalid.");
267
	}
268
}
269

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

    
281
	$section->addInput(new Form_Select(
282
		'webguihostnamemenu',
283
		'Hostname in Menu',
284
		$value,
285
		["" => gettext("Default (No hostname)"), "hostonly" => gettext("Hostname only"), "fqdn" => gettext("Fully Qualified Domain Name")]
286
	))->setHelp("Replaces the Help menu title in the Navbar with the system hostname or FQDN.");
287
}
288
function validate_webguihostnamemenu_field(&$input_errors, $value) {
289
	$valid_values = array("", "hostonly", "fqdn");
290
	if (!in_array($value, $valid_values)) {
291
		$input_errors[] = gettext("The submitted Hostname in Menu value is invalid.");
292
	}
293
}
294

    
295
/****f* pfsense-utils/gen_dashboardcolumns_field
296
 * NAME
297
 *   gen_dashboardcolumns_field
298
 * INPUTS
299
 *   Pointer to section object
300
 *   Initial value for the field
301
 * RESULT
302
 *   no return value, section object is updated
303
 ******/
304
function gen_dashboardcolumns_field(&$section, $value) {
305

    
306
	if (((int) $value < 1) || ((int) $value > 6)) {
307
		$value = 2;
308
	}
309

    
310
	$section->addInput(new Form_Input(
311
		'dashboardcolumns',
312
		'Dashboard Columns',
313
		'number',
314
		$value,
315
		['min' => 1, 'max' => 6]
316
	));
317
}
318
function validate_dashboardcolumns_field(&$input_errors, $value) {
319
	if (!is_numericint($value) || ((int) $value < 1) || ((int) $value > 6)) {
320
		$input_errors[] = gettext("The submitted Dashboard Columns value is invalid.");
321
	}
322
}
323

    
324
/****f* pfsense-utils/gen_interfacessort_field
325
 * NAME
326
 *   gen_interfacessort_field
327
 * INPUTS
328
 *   Pointer to section object
329
 *   Initial value for the field
330
 * RESULT
331
 *   no return value, section object is updated
332
 ******/
333
function gen_interfacessort_field(&$section, $value) {
334

    
335
	$section->addInput(new Form_Checkbox(
336
		'interfacessort',
337
		'Interfaces Sort',
338
		'Sort Alphabetically',
339
		$value
340
	))->setHelp('If selected, lists of interfaces will be sorted by description, otherwise they are listed wan,lan,optn...');
341
}
342

    
343
/****f* pfsense-utils/gen_associatedpanels_fields
344
 * NAME
345
 *   gen_associatedpanels_fields
346
 * INPUTS
347
 *   Pointer to section object
348
 *   Initial value for each of the fields
349
 * RESULT
350
 *   no return value, section object is updated
351
 ******/
352
function gen_associatedpanels_fields(&$section, $value1, $value2, $value3, $value4) {
353

    
354
	$group = new Form_Group('Associated Panels Show/Hide');
355

    
356
	$group->add(new Form_Checkbox(
357
		'dashboardavailablewidgetspanel',
358
		null,
359
		'Available Widgets',
360
		$value1
361
		))->setHelp('Show the Available Widgets panel on the Dashboard.');
362

    
363
	$group->add(new Form_Checkbox(
364
		'systemlogsfilterpanel',
365
		null,
366
		'Log Filter',
367
		$value2
368
	))->setHelp('Show the Log Filter panel in System Logs.');
369

    
370
	$group->add(new Form_Checkbox(
371
		'systemlogsmanagelogpanel',
372
		null,
373
		'Manage Log',
374
		$value3
375
	))->setHelp('Show the Manage Log panel in System Logs.');
376

    
377
	$group->add(new Form_Checkbox(
378
		'statusmonitoringsettingspanel',
379
		null,
380
		'Monitoring Settings',
381
		$value4
382
	))->setHelp('Show the Settings panel in Status Monitoring.');
383

    
384
	$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.');
385

    
386
	$section->add($group);
387
}
388

    
389
/****f* pfsense-utils/gen_webguileftcolumnhyper_field
390
 * NAME
391
 *   gen_webguileftcolumnhyper_field
392
 * INPUTS
393
 *   Pointer to section object
394
 *   Initial value for the field
395
 * RESULT
396
 *   no return value, section object is updated
397
 ******/
398
function gen_webguileftcolumnhyper_field(&$section, $value) {
399

    
400
	$section->addInput(new Form_Checkbox(
401
		'webguileftcolumnhyper',
402
		'Left Column Labels',
403
		'Active',
404
		$value
405
	))->setHelp('If selected, clicking a label in the left column will select/toggle the first item of the group.');
406
}
407

    
408
/****f* pfsense-utils/gen_disablealiaspopupdetail_field
409
 * NAME
410
 *   gen_disablealiaspopupdetail_field
411
 * INPUTS
412
 *   Pointer to section object
413
 *   Initial value for the field
414
 * RESULT
415
 *   no return value, section object is updated
416
 ******/
417
function gen_disablealiaspopupdetail_field(&$section, $value) {
418

    
419
	$section->addInput(new Form_Checkbox(
420
		'disablealiaspopupdetail',
421
		'Alias Popups',
422
		'Disable details in alias popups',
423
		$value
424
	))->setHelp('If selected, the details in alias popups will not be shown, just the alias description (e.g. in Firewall Rules).');
425
}
426

    
427
/****f* pfsense-utils/gen_pagenamefirst_field
428
 * NAME
429
 *   gen_pagenamefirst_field
430
 * INPUTS
431
 *   Pointer to section object
432
 *   Initial value for the field
433
 * RESULT
434
 *   no return value, section object is updated
435
 ******/
436
function gen_pagenamefirst_field(&$section, $value) {
437

    
438
	$section->addInput(new Form_Checkbox(
439
		'pagenamefirst',
440
		'Browser tab text',
441
		'Display page name first in browser tab',
442
		$value
443
	))->setHelp('When this is unchecked, the browser tab shows the host name followed '.
444
		'by the current page. Check this box to display the current page followed by the '.
445
		'host name.');
446
}
447

    
448
/****f* pfsense-utils/gen_user_settings_fields
449
 * NAME
450
 *   gen_user_settings_fields
451
 * INPUTS
452
 *   Pointer to section object
453
 *   Array of initial values for the fields
454
 * RESULT
455
 *   no return value, section object is updated
456
 ******/
457
function gen_user_settings_fields(&$section, $pconfig) {
458

    
459
	gen_webguicss_field($section, $pconfig['webguicss']);
460
	gen_webguifixedmenu_field($section, $pconfig['webguifixedmenu']);
461
	gen_webguihostnamemenu_field($section, $pconfig['webguihostnamemenu']);
462
	gen_dashboardcolumns_field($section, $pconfig['dashboardcolumns']);
463
	gen_interfacessort_field($section, $pconfig['interfacessort']);
464
	gen_associatedpanels_fields(
465
		$section,
466
		$pconfig['dashboardavailablewidgetspanel'],
467
		$pconfig['systemlogsfilterpanel'],
468
		$pconfig['systemlogsmanagelogpanel'],
469
		$pconfig['statusmonitoringsettingspanel']);
470
	gen_webguileftcolumnhyper_field($section, $pconfig['webguileftcolumnhyper']);
471
	gen_disablealiaspopupdetail_field($section, $pconfig['disablealiaspopupdetail']);
472
	gen_pagenamefirst_field($section, $pconfig['pagenamefirst']);
473
}
474

    
475
/****f* pfsense-utils/gen_requirestatefilter_field
476
 * NAME
477
 *   gen_requirestatefilter_field
478
 * INPUTS
479
 *   Pointer to section object
480
 *   Initial value for the field
481
 * RESULT
482
 *   no return value, section object is updated
483
 ******/
484
function gen_requirestatefilter_field(&$section, $value) {
485
	$section->addInput(new Form_Checkbox(
486
		'requirestatefilter',
487
		'Require State Filter',
488
		'Do not display state table without a filter',
489
		$value
490
	))->setHelp('By default, the entire state table is displayed when entering '.
491
		'Diagnostics > States. This option requires a filter to be entered '.
492
		'before the states are displayed. Useful for systems with large state tables.');
493
}
494

    
495
/****f* pfsense-utils/gen_requirefirewallinterface_field
496
 * NAME
497
 *   gen_requirefirewallinterface_field
498
 * INPUTS
499
 *   Pointer to section object
500
 *   Initial value for the field
501
 * RESULT
502
 *   no return value, section object is updated
503
 ******/
504
function gen_requirefirewallinterface_field(&$section, $value) {
505
	$section->addInput(new Form_Checkbox(
506
		'requirefirewallinterface',
507
		'Require Firewall Interface',
508
		'Do not display firewall rules list without first selecting an interface',
509
		$value
510
	))->setHelp('By default, the GUI displays firewall rules for the first enabled interface '.
511
		'(e.g. WAN) when navigating to Firewall > Rules. This option requires the user to '.
512
		'explicitly select an interface before the GUI displays any rules. This is useful '.
513
		'for systems with a large number of rules on the first enabled interface, '.
514
		'causing slowness when loading that page.');
515
}
516

    
517
/****f* pfsense-utils/gen_created_updated_fields
518
 * NAME
519
 *   gen_created_updated_fields
520
 * INPUTS
521
 *   Pointer to form object
522
 *   Array of created time and username
523
 *   Array of updated time and username
524
 * RESULT
525
 *   no return value, section object is added to form if needed
526
 ******/
527
function gen_created_updated_fields(&$form, $created, $updated, $tracker = 0) {
528
	$has_created_time = (isset($created['time']) && isset($created['username']));
529
	$has_updated_time = (isset($updated['time']) && isset($updated['username']));
530

    
531
	if ($has_created_time || $has_updated_time) {
532
		$section = new Form_Section('Rule Information');
533

    
534
		if (!empty($tracker)) {
535
			$section->addInput(new Form_StaticText(
536
				'Tracking ID',
537
				htmlspecialchars($tracker)
538
			));
539
		}
540

    
541
		if ($has_created_time) {
542
			$section->addInput(new Form_StaticText(
543
				'Created',
544
				htmlspecialchars(sprintf(
545
					gettext('%1$s by %2$s'),
546
					date(gettext("n/j/y H:i:s"), $created['time']),
547
					$created['username']))
548
			));
549
		}
550

    
551
		if ($has_updated_time) {
552
			$section->addInput(new Form_StaticText(
553
				'Updated',
554
				htmlspecialchars(sprintf(
555
					gettext('%1$s by %2$s'),
556
					date(gettext("n/j/y H:i:s"), $updated['time']),
557
					$updated['username']))
558
			));
559
		}
560

    
561
		$form->add($section);
562
	}
563
}
564

    
565
function hardware_offloading_applyflags($iface) {
566
	$flags_on = 0;
567
	$flags_off = 0;
568
	$options = get_interface_addresses($iface);
569

    
570
	/* disable hardware checksum offloading for VirtIO network drivers,
571
	 * see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059 */
572
	if (config_path_enabled('system','disablechecksumoffloading') ||
573
	    stristr($iface, "vtnet") || stristr($iface, "ena")) {
574
		if (isset($options['encaps']['txcsum'])) {
575
			$flags_off |= IFCAP_TXCSUM;
576
		}
577
		if (isset($options['encaps']['rxcsum'])) {
578
			$flags_off |= IFCAP_RXCSUM;
579
		}
580
		if (isset($options['encaps']['txcsum6'])) {
581
			$flags_off |= IFCAP_TXCSUM_IPV6;
582
		}
583
		if (isset($options['encaps']['rxcsum6'])) {
584
			$flags_off |= IFCAP_RXCSUM_IPV6;
585
		}
586
	} else {
587
		if (isset($options['caps']['txcsum'])) {
588
			$flags_on |= IFCAP_TXCSUM;
589
		}
590
		if (isset($options['caps']['rxcsum'])) {
591
			$flags_on |= IFCAP_RXCSUM;
592
		}
593
		if (isset($options['caps']['txcsum6'])) {
594
			$flags_on |= IFCAP_TXCSUM_IPV6;
595
		}
596
		if (isset($options['caps']['rxcsum6'])) {
597
			$flags_on |= IFCAP_RXCSUM_IPV6;
598
		}
599
	}
600

    
601
	if (config_path_enabled('system','disablesegmentationoffloading')) {
602
		$flags_off |= IFCAP_TSO;
603
		$flags_off |= IFCAP_VLAN_HWTSO;
604
	} else if (isset($options['caps']['tso']) || isset($options['caps']['tso4']) || isset($options['caps']['tso6'])) {
605
		$flags_on |= IFCAP_TSO;
606
		$flags_on |= IFCAP_VLAN_HWTSO;
607
	}
608

    
609
	if (config_path_enabled('system','disablelargereceiveoffloading')) {
610
		$flags_off |= IFCAP_LRO;
611
	} else if (isset($options['caps']['lro'])) {
612
		$flags_on |= IFCAP_LRO;
613
	}
614

    
615
	pfSense_interface_capabilities($iface, -$flags_off);
616
	pfSense_interface_capabilities($iface, $flags_on);
617
}
618

    
619
/****f* pfsense-utils/enable_hardware_offloading
620
 * NAME
621
 *   enable_hardware_offloading - Enable a NIC's supported hardware features.
622
 * INPUTS
623
 *   $interface	- string containing the physical interface to work on.
624
 * RESULT
625
 *   null
626
 * NOTES
627
 *   This function only supports the fxp driver's loadable microcode.
628
 ******/
629
function enable_hardware_offloading($interface) {
630
	$int = get_real_interface($interface);
631
	if (empty($int)) {
632
		return;
633
	}
634

    
635
	if (!config_path_enabled('system','do_not_use_nic_microcode')) {
636
		/* translate wan, lan, opt -> real interface if needed */
637
		$int_family = preg_split("/[0-9]+/", $int);
638
		$supported_ints = array('fxp');
639
		if (in_array($int_family, $supported_ints)) {
640
			if (does_interface_exist($int)) {
641
				pfSense_interface_flags($int, IFF_LINK0);
642
			}
643
		}
644
	}
645

    
646
	/* This is mostly for vlans and ppp types */
647
	$realhwif = get_parent_interface($interface);
648
	if ($realhwif[0] == $int) {
649
		hardware_offloading_applyflags($int);
650
	} else {
651
		hardware_offloading_applyflags($realhwif[0]);
652
		hardware_offloading_applyflags($int);
653
	}
654
}
655

    
656
/****f* pfsense-utils/is_alias_inuse
657
 * NAME
658
 *   checks to see if an alias is currently in use by a rule
659
 * INPUTS
660
 *
661
 * RESULT
662
 *   true or false
663
 * NOTES
664
 *
665
 ******/
666
function is_alias_inuse($alias) {
667
	if ($alias == "") {
668
		return false;
669
	}
670
	/* loop through firewall rules looking for alias in use */
671
	foreach (config_get_path('filter/rule', []) as $rule) {
672
		foreach (['source', 'destination'] as $origin) {
673
			if (array_get_path($rule, "{$origin}/address") == $alias) {
674
				return true;
675
			}
676
		}
677
	}
678
	/* loop through nat rules looking for alias in use */
679
	foreach (config_get_path('nat/rule', []) as $rule) {
680
		foreach (['target', 'source/address', 'destination/address'] as $property) {
681
			if (array_get_path($rule, $property) == $alias) {
682
				return true;
683
			}
684
		}
685
	}
686
	/* loop through outbound nat rules looking for alias in use */
687
	foreach (config_get_path('nat/outbound/rule', []) as $rule) {
688
		foreach (['target', 'sourceport', 'dstport', 'source/network', 'destination/network'] as $property) {
689
			if (array_get_path($rule, $property) == $alias) {
690
				return true;
691
			}
692
		}
693
	}
694
	return false;
695
}
696

    
697
/****f* pfsense-utils/is_schedule_inuse
698
 * NAME
699
 *   checks to see if a schedule is currently in use by a rule
700
 * INPUTS
701
 *
702
 * RESULT
703
 *   true or false
704
 * NOTES
705
 *
706
 ******/
707
function is_schedule_inuse($schedule) {
708
	if ($schedule == "") {
709
		return false;
710
	}
711
	/* loop through firewall rules looking for schedule in use */
712
	foreach (config_get_path('filter/rule', []) as $rule) {
713
		if ($rule['sched'] == $schedule) {
714
			return true;
715
		}
716
	}
717
	return false;
718
}
719

    
720
/****f* pfsense-utils/setup_microcode
721
 * NAME
722
 *   enumerates all interfaces and calls enable_hardware_offloading which
723
 *   enables a NIC's supported hardware features.
724
 * INPUTS
725
 *
726
 * RESULT
727
 *   null
728
 * NOTES
729
 *   This function only supports the fxp driver's loadable microcode.
730
 ******/
731
function setup_microcode() {
732

    
733
	/* if list */
734
	$iflist = get_configured_interface_list(true);
735
	foreach (array_keys($iflist) as $if) {
736
		enable_hardware_offloading($if);
737
	}
738
	unset($iflist);
739
}
740

    
741
/****f* pfsense-utils/get_carp_status
742
 * NAME
743
 *   get_carp_status - Return whether CARP is enabled or disabled.
744
 * RESULT
745
 *   boolean	- true if CARP is enabled, false if otherwise.
746
 ******/
747
function get_carp_status() {
748
	/* grab the current status of carp */
749
	$status = get_single_sysctl('net.inet.carp.allow');
750
	return (intval($status) > 0);
751
}
752

    
753
/**
754
 * Enable or disable CARP by toggling net.inet.carp.allow if necessary, enabling
755
 * only if CARP VIPs are configured
756
 */
757
function enable_carp(bool $enable=true) {
758
	$carp_enabled = get_carp_status();
759
	$carplist = get_configured_vip_list('all', VIP_CARP);
760
	if (($enable != $carp_enabled) && (!$enable || count($carplist) > 0)) {
761
		set_single_sysctl("net.inet.carp.allow", $enable?"1":"0");
762
	}
763
}
764

    
765
/*
766
 * convert_ip_to_network_format($ip, $subnet): converts an ip address to network form
767
 */
768
function convert_ip_to_network_format($ip, $subnet) {
769
	$ipsplit = explode('.', $ip);
770
	$string = $ipsplit[0] . "." . $ipsplit[1] . "." . $ipsplit[2] . ".0/" . $subnet;
771
	return $string;
772
}
773

    
774
/*
775
 * get_carp_interface_status($carpid): returns the status of a carp uniqid
776
 */
777
function get_carp_interface_status($carpid) {
778

    
779
	$carpiface = get_configured_vip_interface($carpid);
780
	if ($carpiface == NULL)
781
		return "";
782
	$interface = get_real_interface($carpiface);
783
	if ($interface == NULL)
784
		return "";
785
	$vip = get_configured_vip($carpid);
786
	if ($vip == NULL || !isset($vip['vhid']))
787
		return "";
788

    
789
	$vhid = $vip['vhid'];
790
	$carp_query = [];
791
	exec("/sbin/ifconfig {$interface} | /usr/bin/grep \"carp:.* vhid {$vhid} \"", $carp_query);
792
	foreach ($carp_query as $int) {
793
		if (stripos($int, "MASTER"))
794
			return "MASTER";
795
		elseif (stripos($int, "BACKUP"))
796
			return "BACKUP";
797
		elseif (stripos($int, "INIT"))
798
			return "INIT";
799
	}
800

    
801
	return "";
802
}
803

    
804
function get_carp_bind_status($interface) {
805
	$carpstatus = get_carp_interface_status($interface);
806
	if (!empty($carpstatus)) {
807
		return $carpstatus;
808
	} else {
809
		foreach (config_get_path('virtualip/vip', []) as $vip) {
810
			if ($interface == "_vip{$vip['uniqid']}") {
811
				return get_carp_interface_status($vip['interface']);
812
			}
813
		}
814
	}
815
}
816

    
817
/*
818
 * Return true if the CARP status of at least one interface of a captive portal zone is in backup mode
819
 * This function return false if CARP is not enabled on any interface of the captive portal zone
820
 */
821
function captiveportal_ha_is_node_in_backup_mode($cpzone) {
822
	$cpinterfaces = explode(",", config_get_path("captiveportal/{$cpzone}/interface", ""));
823

    
824
	foreach ($cpinterfaces as $interface) {
825
		foreach (config_get_path('virtualip/vip', []) as $vip) {
826
			if (($vip['interface'] == $interface) && ($vip['mode'] == "carp")) {
827
				if (get_carp_interface_status("_vip{$vip['uniqid']}") != "MASTER") {
828
					return true;
829
				}
830
			}
831
		}
832
	}
833
	return false;
834
}
835

    
836
/****f* pfsense-utils/WakeOnLan
837
 * NAME
838
 *   WakeOnLan - Wake a machine up using the wake on lan format/protocol
839
 * RESULT
840
 *   true/false - true if the operation was successful
841
 ******/
842
function WakeOnLan($addr, $mac) {
843
	$addr_byte = explode(':', $mac);
844
	$hw_addr = '';
845

    
846
	for ($a = 0; $a < 6; $a++) {
847
		$hw_addr .= chr(hexdec($addr_byte[$a]));
848
	}
849

    
850
	$msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255);
851

    
852
	for ($a = 1; $a <= 16; $a++) {
853
		$msg .= $hw_addr;
854
	}
855

    
856
	// send it to the broadcast address using UDP
857
	$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
858
	if ($s == false) {
859
		log_error(gettext("Error creating socket!"));
860
		log_error(sprintf(gettext("Error code is '%1\$s' - %2\$s"), socket_last_error(), socket_strerror(socket_last_error())));
861
	} else {
862
		// setting a broadcast option to socket:
863
		$opt_ret = socket_set_option($s, 1, 6, TRUE);
864
		if ($opt_ret < 0) {
865
			log_error(sprintf(gettext("setsockopt() failed, error: %s"),
866
							  socket_strerror(socket_last_error($s))));
867
		}
868
		$e = socket_sendto($s, $msg, strlen($msg), 0, $addr, 2050);
869
		socket_close($s);
870
		log_error(sprintf(gettext('Magic Packet sent (%1$s) to (%2$s) MAC=%3$s'), $e, $addr, $mac));
871
		return true;
872
	}
873

    
874
	return false;
875
}
876

    
877
/*
878
 * reverse_strrchr($haystack, $needle):  Return everything in $haystack up to the *last* instance of $needle.
879
 *					 Useful for finding paths and stripping file extensions.
880
 */
881
function reverse_strrchr($haystack, $needle) {
882
	if (!is_string($haystack)) {
883
		return;
884
	}
885
	return strrpos($haystack, $needle) ? substr($haystack, 0, strrpos($haystack, $needle) +1) : false;
886
}
887

    
888
/*
889
 *  backup_config_section($section): returns as an xml file string of
890
 *                                   the configuration section
891
 */
892
function backup_config_section($section_name) {
893
	$new_section = config_get_path($section_name, []);
894
	/* generate configuration XML */
895
	$xmlconfig = dump_xml_config($new_section, $section_name);
896
	$xmlconfig = str_replace("<?xml version=\"1.0\"?>", "", $xmlconfig);
897
	return $xmlconfig;
898
}
899

    
900
/*
901
 *  restore_config_section($section_name, new_contents): restore a configuration section,
902
 *                                                  and write the configuration out
903
 *                                                  to disk/cf.
904
 */
905
function restore_config_section($section_name, $new_contents) {
906
	global $g;
907
	$fout = fopen("{$g['tmp_path']}/tmpxml", "w");
908
	fwrite($fout, $new_contents);
909
	fclose($fout);
910

    
911
	$xml = parse_xml_config(g_get('tmp_path') . "/tmpxml", null);
912
	if ($xml['pfsense']) {
913
		$xml = $xml['pfsense'];
914
	}
915
	if ($xml[$section_name]) {
916
		$section_xml = $xml[$section_name];
917
	} else {
918
		$section_xml = -1;
919
	}
920

    
921
	@unlink(g_get('tmp_path') . "/tmpxml");
922
	if ($section_xml === -1) {
923
		return false;
924
	}
925

    
926
	/* Save current pkg repo to re-add on new config */
927
	unset($pkg_repo_conf_path);
928
	if ($section_name == "system" &&
929
	    config_get_path('system/pkg_repo_conf_path')) {
930
		$pkg_repo_conf_path = config_get_path('system/pkg_repo_conf_path');
931
	}
932

    
933
	config_set_path($section_name, $section_xml);
934
	if (file_exists("{$g['tmp_path']}/config.cache")) {
935
		unlink("{$g['tmp_path']}/config.cache");
936
	}
937

    
938
	/* Restore previously pkg repo configured */
939
	if ($section_name == "system") {
940
		if (isset($pkg_repo_conf_path)) {
941
			config_set_path('system/pkg_repo_conf_path', $pkg_repo_conf_path);
942
		} elseif (config_get_path('system/pkg_repo_conf_path')) {
943
			config_del_path(('system/pkg_repo_conf_path'));
944
		}
945
	}
946

    
947
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
948
	disable_security_checks();
949
	return true;
950
}
951

    
952
/*
953
 *  merge_config_section($section_name, new_contents):   restore a configuration section,
954
 *                                                  and write the configuration out
955
 *                                                  to disk/cf.  But preserve the prior
956
 * 													structure if needed
957
 */
958
function merge_config_section($section_name, $new_contents) {
959
	$fname = get_tmp_file();
960
	$fout = fopen($fname, "w");
961
	fwrite($fout, $new_contents);
962
	fclose($fout);
963
	$section_xml = parse_xml_config($fname, $section_name);
964
	config_set_path($section_name, $section_xml);
965
	unlink($fname);
966
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
967
	disable_security_checks();
968
	return;
969
}
970

    
971
/*
972
 * rmdir_recursive($path, $follow_links=false)
973
 * Recursively remove a directory tree (rm -rf path)
974
 * This is for directories _only_
975
 */
976
function rmdir_recursive($path, $follow_links=false) {
977
	$to_do = glob($path);
978
	if (!is_array($to_do)) {
979
		$to_do = array($to_do);
980
	}
981
	foreach ($to_do as $workingdir) { // Handle wildcards by foreaching.
982
		if (file_exists($workingdir)) {
983
			if (is_dir($workingdir)) {
984
				$dir = opendir($workingdir);
985
				while ($entry = readdir($dir)) {
986
					if (is_file("$workingdir/$entry") || ((!$follow_links) && is_link("$workingdir/$entry"))) {
987
						unlink("$workingdir/$entry");
988
					} elseif (is_dir("$workingdir/$entry") && $entry != '.' && $entry != '..') {
989
						rmdir_recursive("$workingdir/$entry");
990
					}
991
				}
992
				closedir($dir);
993
				rmdir($workingdir);
994
			} elseif (is_file($workingdir)) {
995
				unlink($workingdir);
996
			}
997
		}
998
	}
999
	return;
1000
}
1001

    
1002
/*
1003
 * host_firmware_version(): Return the versions used in this install
1004
 */
1005
function host_firmware_version() {
1006
	global $g;
1007

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

    
1010
	return array(
1011
		"firmware" => array("version" => g_get('product_version')),
1012
		"kernel"   => array("version" => $os_version),
1013
		"base"     => array("version" => $os_version),
1014
		"platform" => g_get('product_label'),
1015
		"config_version" => config_get_path('version')
1016
	);
1017
}
1018

    
1019
function get_disk_info() {
1020
	$diskout = "";
1021
	exec("/bin/df -h | /usr/bin/grep -w '/' | /usr/bin/awk '{ print $2, $3, $4, $5 }'", $diskout);
1022
	return explode(' ', $diskout[0]);
1023
}
1024

    
1025
/****f* pfsense-utils/strncpy
1026
 * NAME
1027
 *   strncpy - copy strings
1028
 * INPUTS
1029
 *   &$dst, $src, $length
1030
 * RESULT
1031
 *   none
1032
 ******/
1033
function strncpy(&$dst, $src, $length) {
1034
	if (strlen($src) > $length) {
1035
		$dst = substr($src, 0, $length);
1036
	} else {
1037
		$dst = $src;
1038
	}
1039
}
1040

    
1041
/****f* pfsense-utils/reload_interfaces_sync
1042
 * NAME
1043
 *   reload_interfaces - reload all interfaces
1044
 * INPUTS
1045
 *   none
1046
 * RESULT
1047
 *   none
1048
 ******/
1049
function reload_interfaces_sync() {
1050
	global $config, $g;
1051

    
1052
	if (g_get('debug')) {
1053
		log_error(gettext("reload_interfaces_sync() is starting."));
1054
	}
1055

    
1056
	/* parse config.xml again */
1057
	$config = parse_config(true);
1058

    
1059
	/* enable routing */
1060
	system_routing_enable();
1061
	if (g_get('debug')) {
1062
		log_error(gettext("Enabling system routing"));
1063
	}
1064

    
1065
	if (g_get('debug')) {
1066
		log_error(gettext("Cleaning up Interfaces"));
1067
	}
1068

    
1069
	/* set up interfaces */
1070
	interfaces_configure();
1071
}
1072

    
1073
/****f* pfsense-utils/reload_all
1074
 * NAME
1075
 *   reload_all - triggers a reload of all settings
1076
 *   * INPUTS
1077
 *   none
1078
 * RESULT
1079
 *   none
1080
 ******/
1081
function reload_all() {
1082
	send_event("service reload all");
1083
}
1084

    
1085
/****f* pfsense-utils/reload_interfaces
1086
 * NAME
1087
 *   reload_interfaces - triggers a reload of all interfaces
1088
 * INPUTS
1089
 *   none
1090
 * RESULT
1091
 *   none
1092
 ******/
1093
function reload_interfaces() {
1094
	send_event("interface all reload");
1095
}
1096

    
1097
/****f* pfsense-utils/reload_all_sync
1098
 * NAME
1099
 *   reload_all - reload all settings
1100
 *   * INPUTS
1101
 *   none
1102
 * RESULT
1103
 *   none
1104
 ******/
1105
function reload_all_sync() {
1106
	global $config;
1107

    
1108
	/* parse config.xml again */
1109
	$config = parse_config(true);
1110

    
1111
	/* set up our timezone */
1112
	system_timezone_configure();
1113

    
1114
	/* set up our hostname */
1115
	system_hostname_configure();
1116

    
1117
	/* make hosts file */
1118
	system_hosts_generate();
1119

    
1120
	/* generate resolv.conf */
1121
	system_resolvconf_generate();
1122

    
1123
	/* enable routing */
1124
	system_routing_enable();
1125

    
1126
	/* set up interfaces */
1127
	interfaces_configure();
1128

    
1129
	/* start dyndns service */
1130
	services_dyndns_configure();
1131

    
1132
	/* configure cron service */
1133
	configure_cron();
1134

    
1135
	/* start the NTP client */
1136
	system_ntp_configure();
1137

    
1138
	/* sync pw database */
1139
	unlink_if_exists("/etc/spwd.db.tmp");
1140
	mwexec("/usr/sbin/pwd_mkdb -d /etc/ /etc/master.passwd");
1141

    
1142
	/* restart sshd */
1143
	send_event("service restart sshd");
1144

    
1145
	/* restart webConfigurator if needed */
1146
	send_event("service restart webgui");
1147
}
1148

    
1149
function load_loader_conf($loader_conf = NULL, $local = false) {
1150

    
1151
	if ($loader_conf == NULL) {
1152
		return (NULL);
1153
	}
1154
	if (file_exists($loader_conf)) {
1155
		$input = file_get_contents($loader_conf);
1156
	} else {
1157
		$input = "";
1158
	}
1159

    
1160
	$input_split = explode("\n", $input);
1161

    
1162
	/*
1163
	 * Loop through and only add lines that are not empty and not
1164
	 * managed by us.
1165
	 */
1166
	$data = array();
1167
	/* These values should be removed from loader.conf and loader.conf.local
1168
	 * As they will be replaced when necessary. */
1169
	$remove = array(
1170
	    "hint.cordbuc.0",
1171
	    "hint.e6000sw.0",
1172
	    "hint.gpioled",
1173
	    "hint.mdio.0.at",
1174
	    "hint-model.",
1175
	    "hw.e6000sw.default_disabled",
1176
	    "hw.hn.vf_transparent",
1177
	    "hw.hn.use_if_start",
1178
	    "hw.usb.no_pf",
1179
	    "net.pf.request_maxcount",
1180
	    "vm.pmap.pti",
1181
	);
1182
	if (!$local) {
1183
		/* These values should only be filtered in loader.conf, not .local */
1184
		$remove = array_merge($remove, array(
1185
		    "autoboot_delay",
1186
		    "boot_multicons",
1187
		    "boot_serial",
1188
		    "comconsole_speed",
1189
		    "comconsole_port",
1190
		    "console",
1191
		    "debug.ddb.capture.bufsize",
1192
		    "hint.uart.0.flags",
1193
		    "hint.uart.1.flags",
1194
		    "net.link.ifqmaxlen",
1195
		    "hint.hwpstate_intel.0.disabled",
1196
		    "machdep.hwpstate_pkg_ctrl",
1197
		    "net.pf.states_hashsize"
1198
		));
1199
	}
1200
	foreach ($input_split as $line) {
1201
		if (empty($line)) {
1202
			continue;
1203
		}
1204
		$skip = false;
1205
		$name = explode('=', $line, 2)[0];
1206
		foreach($remove as $rkey) {
1207
			if (strncasecmp(trim($name), $rkey, strlen($rkey)) == 0) {
1208
				$skip = true;
1209
				break;
1210
			}
1211
		}
1212
		if (!$skip) {
1213
			$data[] = $line;
1214
		}
1215
	}
1216

    
1217
	return ($data);
1218
}
1219

    
1220
function setup_loader_settings($path = "", $upgrade = false) {
1221
	global $g;
1222

    
1223
	$boot_config_file = "{$path}/boot.config";
1224
	$loader_conf_file = "{$path}/boot/loader.conf";
1225

    
1226
	$serialspeed = (config_get_path('system/serialspeed', 115200));
1227

    
1228
	$vga_only = false;
1229
	$serial_only = false;
1230
	$specific_platform = system_identify_specific_platform();
1231
	$video_console_type = (get_single_sysctl("machdep.bootmethod") == "UEFI") ? "efi" : "vidconsole";
1232
	if ($specific_platform['name'] == '1540' ||
1233
	    $specific_platform['name'] == '1541') {
1234
		$vga_only = true;
1235
	} elseif ($specific_platform['name'] == 'Turbot Dual-E') {
1236
		$g['primaryconsole_force'] = "video";
1237
	} elseif ($specific_platform['name'] == 'RCC-VE' ||
1238
	    $specific_platform['name'] == 'RCC' ||
1239
	    $specific_platform['name'] == 'SG-2220' ||
1240
	    $specific_platform['name'] == 'apu2') {
1241
		$serial_only = true;
1242
	}
1243

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

    
1265
	if (empty($data)) {
1266
		@unlink($boot_config_file);
1267
	} else {
1268
		safe_write_file($boot_config_file, $data);
1269
	}
1270
	unset($data, $boot_config, $boot_config_file, $boot_config_split);
1271

    
1272
	/* Serial console - write out /boot/loader.conf */
1273
	if ($upgrade) {
1274
		system("echo \"Reading {$loader_conf_file}...\" >> /conf/upgrade_log.txt");
1275
	}
1276

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

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

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

    
1327
	/* Set maximum send queue length. */
1328
	$data[] = 'net.link.ifqmaxlen="128"';
1329

    
1330
	/* Speed Shift / hwpstate */
1331
	if (config_get_path('system/hwpstate', 'enabled') == 'disabled') {
1332
		$data[] = 'hint.hwpstate_intel.0.disabled="1"';
1333
	}
1334
	$data[] = 'machdep.hwpstate_pkg_ctrl="' . config_get_path('system/hwpstate_control_level', get_single_sysctl('machdep.hwpstate_pkg_ctrl')) . '"';
1335

    
1336
	// calculate the size of hash tables that store states - should be power of 2
1337
	$pf_maximumstates = intval(config_get_path('system/maximumstates', pfsense_default_state_size()));
1338
	$pf_hashtablesize = pow(2, ceil(log(floor($pf_maximumstates / 2), 2)));
1339
	$data[] = "net.pf.states_hashsize=\"{$pf_hashtablesize}\"";
1340

    
1341
	safe_write_file($loader_conf_file, $data);
1342

    
1343
	/* Filter loader.conf.local to avoid duplicate settings. */
1344
	$loader_conf_file = "{$path}/boot/loader.conf.local";
1345
	$data = load_loader_conf($loader_conf_file, true);
1346
	if (empty($data)) {
1347
		@unlink($loader_conf_file);
1348
	} else {
1349
		safe_write_file($loader_conf_file, $data);
1350
	}
1351

    
1352
}
1353

    
1354
function console_configure($when = "save", $path = "") {
1355
	$ttys_file = "{$path}/etc/ttys";
1356

    
1357
	/* Update the loader settings. */
1358
	setup_loader_settings($path, ($when == "upgrade"));
1359

    
1360
	$ttys = file_get_contents($ttys_file);
1361
	$ttys_split = explode("\n", $ttys);
1362

    
1363
	$data = array();
1364

    
1365
	$on_off = (is_serial_enabled() ? 'onifconsole' : 'off');
1366

    
1367
	if (config_path_enabled('system','disableconsolemenu')) {
1368
		$console_type = 'Pc';
1369
		$serial_type = '3wire';
1370
	} else {
1371
		$console_type = 'al.Pc';
1372
		$serial_type = 'al.3wire';
1373
	}
1374

    
1375
	$console_line = "console\tnone\t\t\t\tunknown\toff\tsecure";
1376
	$virt_line =
1377
	    "\"/usr/libexec/getty {$console_type}\"\txterm\tonifexists secure";
1378
	$ttyu_line =
1379
	    "\"/usr/libexec/getty {$serial_type}\"\tvt100\t{$on_off}\tsecure";
1380

    
1381
	$found = array();
1382

    
1383
	foreach ($ttys_split as $tty) {
1384
		/* Ignore blank lines */
1385
		if (empty($tty)) {
1386
			continue;
1387
		}
1388

    
1389
		if (stristr($tty, "ttyv0")) {
1390
			$found['ttyv0'] = 1;
1391
			$data[] = "ttyv0\t{$virt_line}";
1392
		} elseif (stristr($tty, "xc0")) {
1393
			$found['xc0'] = 1;
1394
			$data[] = "xc0\t{$virt_line}";
1395
		} elseif (stristr($tty, "ttyu")) {
1396
			$ttyn = substr($tty, 0, 5);
1397
			$found[$ttyn] = 1;
1398
			$data[] = "{$ttyn}\t{$ttyu_line}";
1399
		} elseif (substr($tty, 0, 7) == 'console') {
1400
			$found['console'] = 1;
1401
			$data[] = $tty;
1402
		} else {
1403
			$data[] = $tty;
1404
		}
1405
	}
1406
	unset($on_off, $console_type, $serial_type);
1407

    
1408
	/* Detect missing main lines on original file and try to rebuild it */
1409
	$items = array(
1410
		'console',
1411
		'ttyv0',
1412
		'ttyu0',
1413
		'ttyu1',
1414
		'ttyu2',
1415
		'ttyu3',
1416
		'xc0'
1417
	);
1418

    
1419
	foreach ($items as $item) {
1420
		if (isset($found[$item])) {
1421
			continue;
1422
		}
1423

    
1424
		if ($item == 'console') {
1425
			$data[] = $console_line;
1426
		} elseif (($item == 'ttyv0') || ($item == 'xc0')) {
1427
			/* xc0 - Xen console, see https://redmine.pfsense.org/issues/11402 */
1428
			$data[] = "{$item}\t{$virt_line}";
1429
		} else {
1430
			$data[] = "{$item}\t{$ttyu_line}";
1431
		}
1432
	}
1433

    
1434
	safe_write_file($ttys_file, $data);
1435

    
1436
	unset($ttys, $ttys_file, $ttys_split, $data);
1437

    
1438
	if ($when != "upgrade") {
1439
		reload_ttys();
1440
	}
1441

    
1442
	return;
1443
}
1444

    
1445
function is_serial_enabled() {
1446
	global $g;
1447

    
1448
	if (!isset($g['enableserial_force']) &&
1449
	    !config_path_enabled('system','enableserial')) {
1450
		return false;
1451
	}
1452

    
1453
	return true;
1454
}
1455

    
1456
function reload_ttys() {
1457
	// Send a HUP signal to init will make it reload /etc/ttys
1458
	posix_kill(1, SIGHUP);
1459
}
1460

    
1461
function print_value_list($list, $count = 10, $separator = ",") {
1462
	$ret = implode($separator, array_slice($list, 0, $count));
1463
	if (count($list) < $count) {
1464
		$ret .= ".";
1465
	} else {
1466
		$ret .= "...";
1467
	}
1468
	return $ret;
1469
}
1470

    
1471
/* DHCP enabled on any interfaces? */
1472
function is_dhcp_server_enabled() {
1473
	foreach (config_get_path('dhcpd', []) as $dhcpif => $dhcpifconf) {
1474
		if (empty($dhcpifconf)) {
1475
			continue;
1476
		}
1477
		if (isset($dhcpifconf['enable']) &&
1478
			!empty(config_get_path("interfaces/{$dhcpif}"))) {
1479
			return true;
1480
		}
1481
	}
1482

    
1483
	return false;
1484
}
1485

    
1486
/* DHCP enabled on any interfaces? */
1487
function is_dhcpv6_server_enabled() {
1488
	foreach (config_get_path('dhcpdv6', []) as $dhcpv6if => $dhcpv6ifconf) {
1489
		if (empty($dhcpv6ifconf)) {
1490
			continue;
1491
		}
1492
		if (isset($dhcpv6ifconf['enable']) &&
1493
			!empty(config_get_path("interfaces/{$dhcpv6if}"))) {
1494
			return true;
1495
		}
1496
	}
1497

    
1498
	return false;
1499
}
1500

    
1501
/* radvd enabled on any interfaces? */
1502
function is_radvd_enabled() {
1503
	init_config_arr(['dhcpdv6']);
1504
	$dhcpdv6cfg = config_get_path('dhcpdv6', []);
1505
	$Iflist = get_configured_interface_list();
1506

    
1507
	/* handle manually configured DHCP6 server settings first */
1508
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1509
		if (empty($dhcpv6ifconf)) {
1510
			continue;
1511
		}
1512
		if (!config_path_enabled("interfaces/{$dhcpv6if}")) {
1513
			continue;
1514
		}
1515

    
1516
		if (!isset($dhcpv6ifconf['ramode'])) {
1517
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
1518
		}
1519

    
1520
		if ($dhcpv6ifconf['ramode'] == "disabled") {
1521
			continue;
1522
		}
1523

    
1524
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1525
		if (!is_ipaddrv6($ifcfgipv6)) {
1526
			continue;
1527
		}
1528

    
1529
		return true;
1530
	}
1531

    
1532
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
1533
	foreach (array_keys($Iflist) as $if) {
1534
		if (!config_path_enabled("interfaces/{$if}", 'track6-interface')) {
1535
			continue;
1536
		}
1537
		if (!config_path_enabled("interfaces/{$if}")) {
1538
			continue;
1539
		}
1540

    
1541
		$ifcfgipv6 = get_interface_ipv6($if);
1542
		if (!is_ipaddrv6($ifcfgipv6)) {
1543
			continue;
1544
		}
1545

    
1546
		$ifcfgsnv6 = get_interface_subnetv6($if);
1547
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1548

    
1549
		if (!is_ipaddrv6($subnetv6)) {
1550
			continue;
1551
		}
1552

    
1553
		return true;
1554
	}
1555

    
1556
	return false;
1557
}
1558

    
1559
/* Any PPPoE servers enabled? */
1560
function is_pppoe_server_enabled() {
1561
	$pppoeenable = false;
1562

    
1563
	foreach (config_get_path('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
			/* Skip blank lines, trim, and cast to int to ensure type matches.
1595
			 * https://redmine.pfsense.org/issues/14117 */
1596
			if (!empty($upt)) {
1597
				$sec += (int) trim(substr($upt, 1 + strpos($upt, " ")));
1598
			}
1599
		}
1600
		return convert_seconds_to_dhms($sec);
1601
	} else {
1602
		$total_time = gettext("No history data found!");
1603
		return $total_time;
1604
	}
1605
}
1606

    
1607
//returns interface information
1608
function get_interface_info($ifdescr) {
1609
	global $g;
1610

    
1611
	$ifinfo = array();
1612
	if (empty(config_get_path("interfaces/{$ifdescr}"))) {
1613
		return;
1614
	}
1615
	$ifinfo['hwif'] = config_get_path("interfaces/{$ifdescr}/if");
1616
	$ifinfo['enable'] = config_path_enabled("interfaces/{$ifdescr}");
1617
	$ifinfo['if'] = get_real_interface($ifdescr);
1618

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

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

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

    
1680
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1681
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1682
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1683
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1684

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

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

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

    
1717
			if (empty($ifinfo['status'])) {
1718
				$ifinfo['status'] = "down";
1719
			}
1720

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

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

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

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

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

    
1797
			if (preg_match("/status: (.*)$/", $ici, $matches)) {
1798
				if ($matches[1] != "active") {
1799
					$ifinfo['status'] = $matches[1];
1800
				}
1801
				if ($ifinfo['status'] == gettext("running")) {
1802
					$ifinfo['status'] = gettext("up");
1803
				}
1804
			}
1805
			if (preg_match("/channel (\S*)/", $ici, $matches)) {
1806
				$ifinfo['channel'] = $matches[1];
1807
			}
1808
			if (preg_match("/ssid (\".*?\"|\S*)/", $ici, $matches)) {
1809
				if ($matches[1][0] == '"') {
1810
					$ifinfo['ssid'] = substr($matches[1], 1, -1);
1811
				}
1812
				else {
1813
					$ifinfo['ssid'] = $matches[1];
1814
				}
1815
			}
1816
			if (preg_match("/laggproto (.*)$/", $ici, $matches)) {
1817
				$ifinfo['laggproto'] = $matches[1];
1818
			}
1819
			if (preg_match("/laggport: (.*)$/", $ici, $matches)) {
1820
				$ifinfo['laggport'][] = $matches[1];
1821
			}
1822
			if (preg_match("/plugged: (.*)$/", $ici, $matches)) {
1823
				$ifinfo['plugged'] = $matches[1];
1824
			}
1825
			if (preg_match("/vendor: (.*)$/", $ici, $matches)) {
1826
				$ifinfo['vendor'] = $matches[1];
1827
			}
1828
			if (preg_match("/module temperature: (.*) Voltage: (.*)$/", $ici, $matches)) {
1829
				$ifinfo['temperature'] = $matches[1];
1830
				$ifinfo['voltage'] = $matches[2];
1831
			}
1832
			if (preg_match("/RX: (.*) TX: (.*)$/", $ici, $matches)) {
1833
				$ifinfo['rx'] = $matches[1];
1834
				$ifinfo['tx'] = $matches[2];
1835
			}
1836
		}
1837
		foreach ($wifconfiginfo as $ici) {
1838
			$elements = preg_split("/[ ]+/i", $ici);
1839
			if ($elements[0] != "") {
1840
				$ifinfo['bssid'] = $elements[0];
1841
			}
1842
			if ($elements[3] != "") {
1843
				$ifinfo['rate'] = $elements[3];
1844
			}
1845
			if ($elements[4] != "") {
1846
				$ifinfo['rssi'] = $elements[4];
1847
			}
1848
		}
1849
		/* lookup the gateway */
1850
		if (interface_has_gateway($ifdescr)) {
1851
			$ifinfo['gateway'] = get_interface_gateway($ifdescr);
1852
		}
1853
		if (interface_has_gatewayv6($ifdescr)) {
1854
			$ifinfo['gatewayv6'] = get_interface_gateway_v6($ifdescr);
1855
		}
1856
	}
1857

    
1858
	$bridge = "";
1859
	$bridge = link_interface_to_bridge($ifdescr);
1860
	if ($bridge) {
1861
		$bridge_text = `/sbin/ifconfig {$bridge}`;
1862
		if (stristr($bridge_text, "blocking") <> false) {
1863
			$ifinfo['bridge'] = "<b><font color='red'>" . gettext("blocking") . "</font></b> - " . gettext("check for ethernet loops");
1864
			$ifinfo['bridgeint'] = $bridge;
1865
		} else if (stristr($bridge_text, "learning") <> false) {
1866
			$ifinfo['bridge'] = gettext("learning");
1867
			$ifinfo['bridgeint'] = $bridge;
1868
		} else if (stristr($bridge_text, "forwarding") <> false) {
1869
			$ifinfo['bridge'] = gettext("forwarding");
1870
			$ifinfo['bridgeint'] = $bridge;
1871
		}
1872
	}
1873

    
1874
	return $ifinfo;
1875
}
1876

    
1877
//returns cpu speed of processor. Good for determining capabilities of machine
1878
function get_cpu_speed() {
1879
	return get_single_sysctl("hw.clockrate");
1880
}
1881

    
1882
function get_uptime_sec() {
1883
	$boottime = "";
1884
	$matches = "";
1885
	$boottime = get_single_sysctl("kern.boottime");
1886
	preg_match("/sec = (\d+)/", $boottime, $matches);
1887
	$boottime = $matches[1];
1888
	if (intval($boottime) == 0) {
1889
		return 0;
1890
	}
1891

    
1892
	$uptime = time() - $boottime;
1893
	return $uptime;
1894
}
1895

    
1896
function resolve_host_addresses($host, $recordtypes = array(DNS_A, DNS_AAAA, DNS_CNAME), $index = true) {
1897
	$dnsresult = array();
1898
	$resolved = array();
1899
	$errreporting = error_reporting();
1900
	error_reporting($errreporting & ~E_WARNING);// dns_get_record throws a warning if nothing is resolved..
1901
	foreach ($recordtypes as $recordtype) {
1902
		$tmp = dns_get_record($host, $recordtype);
1903
		if (is_array($tmp)) {
1904
			$dnsresult = array_merge($dnsresult, $tmp);
1905
		}
1906
	}
1907
	error_reporting($errreporting);// restore original php warning/error settings.
1908
	foreach ($dnsresult as $item) {
1909
		$newitem = array();
1910
		$newitem['type'] = $item['type'];
1911
		switch ($item['type']) {
1912
			case 'CNAME':
1913
				$newitem['data'] = $item['target'];
1914
				$resolved[] = $newitem;
1915
				break;
1916
			case 'A':
1917
				$newitem['data'] = $item['ip'];
1918
				$resolved[] = $newitem;
1919
				break;
1920
			case 'AAAA':
1921
				$newitem['data'] = $item['ipv6'];
1922
				$resolved[] = $newitem;
1923
				break;
1924
		}
1925
	}
1926
	if ($index == false) {
1927
		foreach ($resolved as $res) {
1928
			$noind[] = $res['data'];
1929
		}
1930
		$resolved = $noind;
1931
	}
1932
	return $resolved;
1933
}
1934

    
1935
function add_hostname_to_watch($hostname) {
1936
	if (!is_dir("/var/db/dnscache")) {
1937
		mkdir("/var/db/dnscache");
1938
	}
1939
	$result = array();
1940
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1941
		$contents = "";
1942
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1943
		if (!empty($domips)) {
1944
			foreach ($domips as $ip) {
1945
				$contents .= "$ip\n";
1946
			}
1947
		}
1948
		file_put_contents("/var/db/dnscache/$hostname", $contents);
1949
		/* Remove empty elements */
1950
		$result = array_filter(explode("\n", $contents), 'strlen');
1951
	}
1952
	return $result;
1953
}
1954

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

    
1959
function pfsense_default_state_size() {
1960
	/* get system memory amount */
1961
	$memory = get_memory();
1962
	$physmem = $memory[0];
1963

    
1964
	if ((int) $physmem > 0) {
1965
		/* Be cautious and only allocate 10% of system memory to the state table */
1966
		$max_states = (int) ($physmem/10)*1000;
1967
	} else {
1968
		/* If the memory check result is invalid, use a low but still
1969
		 * somewhat sane default (Equivalent to ~256MB RAM) */
1970
		$max_states = 25600;
1971
	}
1972

    
1973
	return $max_states;
1974
}
1975

    
1976
function pfsense_current_tables_size() {
1977
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1978
	return $current;
1979
}
1980

    
1981
function pfsense_current_table_entries_size() {
1982
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1983
	return (trim($current));
1984
}
1985

    
1986
/* Compare the current hostname DNS to the DNS cache we made
1987
 * if it has changed we return the old records
1988
 * if no change we return false */
1989
function compare_hostname_to_dnscache($hostname) {
1990
	global $g;
1991
	if (!is_dir("/var/db/dnscache")) {
1992
		mkdir("/var/db/dnscache");
1993
	}
1994
	$hostname = trim($hostname);
1995
	if (is_readable("/var/db/dnscache/{$hostname}")) {
1996
		$oldcontents = file_get_contents("/var/db/dnscache/{$hostname}");
1997
	} else {
1998
		$oldcontents = "";
1999
	}
2000
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
2001
		$contents = "";
2002
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
2003
		if (!empty($domips)) {
2004
			foreach ($domips as $ip) {
2005
				$contents .= "$ip\n";
2006
			}
2007
		}
2008
	}
2009

    
2010
	if (trim($oldcontents) != trim($contents)) {
2011
		if (g_get('debug')) {
2012
			log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents));
2013
		}
2014
		return ($oldcontents);
2015
	} else {
2016
		return false;
2017
	}
2018
}
2019

    
2020
/*
2021
 * load_crypto() - Load crypto modules if enabled in config.
2022
 */
2023
function load_crypto() {
2024
	$crypto_modules = array('aesni', 'cryptodev');
2025

    
2026
	$enabled_modules = explode('_', config_get_path('system/crypto_hardware'));
2027

    
2028
	foreach ($enabled_modules as $enmod) {
2029
		if (empty($enmod) || !in_array($enmod, $crypto_modules)) {
2030
			continue;
2031
		}
2032
		if (!is_module_loaded($enmod)) {
2033
			log_error(sprintf(gettext("Loading %s cryptographic accelerator module."), $enmod));
2034
			mute_kernel_msgs();
2035
			mwexec("/sbin/kldload " . escapeshellarg($enmod));
2036
			unmute_kernel_msgs();
2037
		}
2038
	}
2039
}
2040

    
2041
/*
2042
 * load_thermal_hardware() - Load temperature monitor kernel module
2043
 */
2044
function load_thermal_hardware() {
2045
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
2046
	$thermal_hardware = config_get_path('system/thermal_hardware');
2047

    
2048
	if (!in_array($thermal_hardware, $thermal_hardware_modules)) {
2049
		return false;
2050
	}
2051

    
2052
	if (!empty($thermal_hardware) && !is_module_loaded($thermal_hardware)) {
2053
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $thermal_hardware));
2054
		mute_kernel_msgs();
2055
		mwexec("/sbin/kldload {$thermal_hardware}");
2056
		unmute_kernel_msgs();
2057
	}
2058
}
2059

    
2060
/****f* pfsense-utils/isvm
2061
 * NAME
2062
 *   isvm
2063
 * INPUTS
2064
 *	none
2065
 * RESULT
2066
 *   returns true if machine is running under a virtual environment
2067
 ******/
2068
function isvm() {
2069
	$virtualenvs = array("vmware", "parallels", "qemu", "bochs", "plex86", "VirtualBox");
2070
	exec('/bin/kenv -q smbios.system.product 2>/dev/null', $output, $rc);
2071

    
2072
	if ($rc != 0 || !isset($output[0])) {
2073
		return false;
2074
	}
2075

    
2076
	foreach ($virtualenvs as $virtualenv) {
2077
		if (stripos($output[0], $virtualenv) !== false) {
2078
			return true;
2079
		}
2080
	}
2081

    
2082
	return false;
2083
}
2084

    
2085
function get_freebsd_version() {
2086
	$version = explode(".", php_uname("r"));
2087
	return $version[0];
2088
}
2089

    
2090
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
2091
	global $g;
2092

    
2093
	$fp = fopen($destination, "wb");
2094

    
2095
	if (!$fp) {
2096
		return false;
2097
	}
2098

    
2099
	$ch = curl_init();
2100
	curl_setopt($ch, CURLOPT_URL, $url);
2101
	if ($verify_ssl) {
2102
		curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
2103
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
2104
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
2105
	} else {
2106
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
2107
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2108
		curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false);
2109
	}
2110
	curl_setopt($ch, CURLOPT_FILE, $fp);
2111
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2112
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2113
	curl_setopt($ch, CURLOPT_HEADER, false);
2114
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2115
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
2116
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version') . ':' . system_get_uniqueid());
2117
	} else {
2118
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version'));
2119
	}
2120

    
2121
	set_curlproxy($ch);
2122

    
2123
	@curl_exec($ch);
2124
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2125
	fclose($fp);
2126
	curl_close($ch);
2127
	if ($http_code == 200) {
2128
		return true;
2129
	} else {
2130
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2131
		unlink_if_exists($destination);
2132
		return false;
2133
	}
2134
}
2135

    
2136
function download_file_with_progress_bar($url, $destination, $verify_ssl = true, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) {
2137
	global $g, $ch, $fout, $file_size, $downloaded, $first_progress_update;
2138
	$file_size = 1;
2139
	$downloaded = 1;
2140
	$first_progress_update = TRUE;
2141
	/* open destination file */
2142
	$fout = fopen($destination, "wb");
2143

    
2144
	if (!$fout) {
2145
		return false;
2146
	}
2147
	/*
2148
	 *      Originally by Author: Keyvan Minoukadeh
2149
	 *      Modified by Scott Ullrich to return Content-Length size
2150
	 */
2151
	$ch = curl_init();
2152
	curl_setopt($ch, CURLOPT_URL, $url);
2153
	if ($verify_ssl) {
2154
		curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
2155
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
2156
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
2157
	} else {
2158
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
2159
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2160
		curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false);
2161
	}
2162
	curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
2163
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2164
	curl_setopt($ch, CURLOPT_WRITEFUNCTION, $readbody);
2165
	curl_setopt($ch, CURLOPT_NOPROGRESS, '1');
2166
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2167
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2168
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
2169
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version') . ':' . system_get_uniqueid());
2170
	} else {
2171
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version'));
2172
	}
2173

    
2174
	set_curlproxy($ch);
2175

    
2176
	@curl_exec($ch);
2177
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2178
	fclose($fout);
2179
	curl_close($ch);
2180
	if ($http_code == 200) {
2181
		return true;
2182
	} else {
2183
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2184
		unlink_if_exists($destination);
2185
		return false;
2186
	}
2187
}
2188

    
2189
function read_header($ch, $string) {
2190
	global $file_size;
2191
	$length = strlen($string);
2192
	$regs = "";
2193
	preg_match("/(Content-Length:) (.*)/", $string, $regs);
2194
	if ($regs[2] <> "") {
2195
		$file_size = intval($regs[2]);
2196
	}
2197
	ob_flush();
2198
	return $length;
2199
}
2200

    
2201
function read_body($ch, $string) {
2202
	global $fout, $file_size, $downloaded, $sendto, $static_status, $static_output, $lastseen, $first_progress_update;
2203
	global $pkg_interface;
2204
	$length = strlen($string);
2205
	$downloaded += intval($length);
2206
	if ($file_size > 0) {
2207
		$downloadProgress = round(100 * (1 - $downloaded / $file_size), 0);
2208
		$downloadProgress = 100 - $downloadProgress;
2209
	} else {
2210
		$downloadProgress = 0;
2211
	}
2212
	if ($lastseen <> $downloadProgress and $downloadProgress < 101) {
2213
		if ($sendto == "status") {
2214
			if ($pkg_interface == "console") {
2215
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2216
					$tostatus = $static_status . $downloadProgress . "%";
2217
					if ($downloadProgress == 100) {
2218
						$tostatus = $tostatus . "\r";
2219
					}
2220
					update_status($tostatus);
2221
				}
2222
			} else {
2223
				$tostatus = $static_status . $downloadProgress . "%";
2224
				update_status($tostatus);
2225
			}
2226
		} else {
2227
			if ($pkg_interface == "console") {
2228
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2229
					$tooutput = $static_output . $downloadProgress . "%";
2230
					if ($downloadProgress == 100) {
2231
						$tooutput = $tooutput . "\r";
2232
					}
2233
					update_output_window($tooutput);
2234
				}
2235
			} else {
2236
				$tooutput = $static_output . $downloadProgress . "%";
2237
				update_output_window($tooutput);
2238
			}
2239
		}
2240
		if (($pkg_interface != "console") || (($downloadProgress % 10) == 0) || ($downloadProgress < 10)) {
2241
			update_progress_bar($downloadProgress, $first_progress_update);
2242
			$first_progress_update = FALSE;
2243
		}
2244
		$lastseen = $downloadProgress;
2245
	}
2246
	if ($fout) {
2247
		fwrite($fout, $string);
2248
	}
2249
	ob_flush();
2250
	return $length;
2251
}
2252

    
2253
/*
2254
 *   update_output_window: update bottom textarea dynamically.
2255
 */
2256
function update_output_window($text) {
2257
	global $pkg_interface;
2258
	$log = preg_replace("/\n/", "\\n", $text);
2259
	if ($pkg_interface != "console") {
2260
?>
2261
<script type="text/javascript">
2262
//<![CDATA[
2263
	document.getElementById("output").textContent="<?=htmlspecialchars($log)?>";
2264
	document.getElementById("output").scrollTop = document.getElementById("output").scrollHeight;
2265
//]]>
2266
</script>
2267
<?php
2268
	}
2269
	/* ensure that contents are written out */
2270
	ob_flush();
2271
}
2272

    
2273
/*
2274
 *   update_status: update top textarea dynamically.
2275
 */
2276
function update_status($status) {
2277
	global $pkg_interface;
2278

    
2279
	if ($pkg_interface == "console") {
2280
		print ("{$status}");
2281
	}
2282

    
2283
	/* ensure that contents are written out */
2284
	ob_flush();
2285
}
2286

    
2287
/*
2288
 * update_progress_bar($percent, $first_time): updates the javascript driven progress bar.
2289
 */
2290
function update_progress_bar($percent, $first_time) {
2291
	global $pkg_interface;
2292
	if ($percent > 100) {
2293
		$percent = 1;
2294
	}
2295
	if ($pkg_interface <> "console") {
2296
		echo '<script type="text/javascript">';
2297
		echo "\n//<![CDATA[\n";
2298
		echo 'document.getElementById("progressbar").style.width="'. $percent.'%"';
2299
		echo "\n//]]>\n";
2300
		echo '</script>';
2301
	} else {
2302
		if (!($first_time)) {
2303
			echo "\x08\x08\x08\x08\x08";
2304
		}
2305
		echo sprintf("%4d%%", $percent);
2306
	}
2307
}
2308

    
2309
function update_alias_name($new_alias_name, $orig_alias_name) {
2310
	if (!$orig_alias_name) {
2311
		return;
2312
	}
2313

    
2314
	// Firewall rules
2315
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2316
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2317
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2318
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2319
	// NAT Rules
2320
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2321
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2322
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2323
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2324
	update_alias_names_upon_change(array('nat', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2325
	update_alias_names_upon_change(array('nat', 'rule'), array('local-port'), $new_alias_name, $orig_alias_name);
2326
	// NAT 1:1 Rules
2327
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('external'), $new_alias_name, $orig_alias_name);
2328
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2329
	update_alias_names_upon_change(array('nat', 'onetoone'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2330
	// NAT Outbound Rules
2331
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('source', 'network'), $new_alias_name, $orig_alias_name);
2332
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('sourceport'), $new_alias_name, $orig_alias_name);
2333
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('destination', 'network'), $new_alias_name, $orig_alias_name);
2334
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('dstport'), $new_alias_name, $orig_alias_name);
2335
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2336
	// Alias in an alias
2337
	update_alias_names_upon_change(array('aliases', 'alias'), array('address'), $new_alias_name, $orig_alias_name);
2338
	// Static routes
2339
	update_alias_names_upon_change(array('staticroutes', 'route'), array('network'), $new_alias_name, $orig_alias_name);
2340
	// OpenVPN
2341
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2342
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2343
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('local_network'), $new_alias_name, $orig_alias_name);
2344
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('local_networkv6'), $new_alias_name, $orig_alias_name);
2345
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('remote_network'), $new_alias_name, $orig_alias_name);
2346
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2347
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2348
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2349
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('remote_network'), $new_alias_name, $orig_alias_name);
2350
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2351
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2352
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2353
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('local_network'), $new_alias_name, $orig_alias_name);
2354
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('local_networkv6'), $new_alias_name, $orig_alias_name);
2355
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('remote_network'), $new_alias_name, $orig_alias_name);
2356
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2357
}
2358

    
2359
function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) {
2360
	global $g, $config, $pconfig, $debug;
2361
	if (!$origname) {
2362
		return;
2363
	}
2364

    
2365
	$sectionref = &$config;
2366
	foreach ($section as $sectionname) {
2367
		if (is_array($sectionref) && isset($sectionref[$sectionname])) {
2368
			$sectionref = &$sectionref[$sectionname];
2369
		} else {
2370
			return;
2371
		}
2372
	}
2373

    
2374
	if ($debug) {
2375
		$fd = fopen("{$g['tmp_path']}/print_r", "a");
2376
		fwrite($fd, print_r($pconfig, true));
2377
	}
2378

    
2379
	if (is_array($sectionref)) {
2380
		foreach (array_keys($sectionref) as $itemkey) {
2381
			if ($debug) {
2382
				fwrite($fd, "$itemkey\n");
2383
			}
2384

    
2385
			$fieldfound = true;
2386
			$fieldref = &$sectionref[$itemkey];
2387
			foreach ($field as $fieldname) {
2388
				if (is_array($fieldref) && isset($fieldref[$fieldname])) {
2389
					$fieldref = &$fieldref[$fieldname];
2390
				} else {
2391
					$fieldfound = false;
2392
					break;
2393
				}
2394
			}
2395
			if ($fieldfound && $fieldref == $origname) {
2396
				if ($debug) {
2397
					fwrite($fd, "Setting old alias value $origname to $new_alias_name\n");
2398
				}
2399
				$fieldref = $new_alias_name;
2400
			}
2401
		}
2402
	}
2403

    
2404
	if ($debug) {
2405
		fclose($fd);
2406
	}
2407

    
2408
}
2409

    
2410
function parse_aliases_file($filename, $type = "url", $max_items = -1, $kflc = false) {
2411
	/*
2412
	 * $filename = file to process for example blocklist like DROP:  http://www.spamhaus.org/drop/drop.txt
2413
	 * $type = if set to 'url' then subnets and ips will be returned,
2414
	 *         if set to 'url_ports' port-ranges and ports will be returned
2415
	 * $max_items = sets the maximum amount of valid items to load, -1 the default defines there is no limit.
2416
	 *
2417
	 * RETURNS an array of ip subnets and ip's or ports and port-ranges, returns NULL upon a error conditions (file not found)
2418
	 */
2419

    
2420
	if (!file_exists($filename)) {
2421
		log_error(sprintf(gettext("Could not process non-existent file from alias: %s"), $filename));
2422
		return null;
2423
	}
2424

    
2425
	if (filesize($filename) == 0) {
2426
		log_error(sprintf(gettext("Could not process empty file from alias: %s"), $filename));
2427
		return null;
2428
	}
2429
	$fd = @fopen($filename, 'r');
2430
	if (!$fd) {
2431
		log_error(sprintf(gettext("Could not process aliases from alias: %s"), $filename));
2432
		return null;
2433
	}
2434
	$items = array();
2435
	$comments = array();
2436
	while (($fc = fgets($fd)) !== FALSE) {
2437
		$fc = strip_tags($fc);
2438
		$tmp = alias_idn_to_ascii(trim($fc, " \t\n\r"));
2439
		if (empty($tmp)) {
2440
			continue;
2441
		}
2442
		if (($kflc) && (strpos($tmp, '#') === 0)) {	// Keep Full Line Comments (lines beginning with #).
2443
			$comments[] = $tmp;
2444
		} else {
2445
			$tmp_str = strstr($tmp, '#', true);
2446
			if (!empty($tmp_str)) {
2447
				$tmp = $tmp_str;
2448
			}
2449
			$tmp_str = strstr($tmp, ' ', true);
2450
			if (!empty($tmp_str)) {
2451
				$tmp = $tmp_str;
2452
			}
2453
			switch ($type) {
2454
				case "url":
2455
				case "urltable":
2456
					if (is_ipaddr($tmp) || is_subnet($tmp)) {
2457
						$items[] = $tmp;
2458
						break;
2459
					}
2460
					if (is_fqdn($tmp)) {
2461
						$results = resolve_host_addresses($tmp, array(DNS_A, DNS_AAAA), false);
2462
						if (!empty($results)) {
2463
							foreach ($results as $ip) {
2464
								if (is_ipaddr($ip)) {
2465
									$items[] = $ip;
2466
								}
2467
							}
2468
						}
2469
					}
2470
					break;
2471
				case "url_ports":
2472
				case "urltable_ports":
2473
					if (is_port_or_range($tmp)) {
2474
						$items[] = $tmp;
2475
					}
2476
					break;
2477
				default:
2478
					/* unknown type */
2479
					break;
2480
				}
2481
			if (count($items) == $max_items) {
2482
				break;
2483
			}
2484
		}
2485
	}
2486
	fclose($fd);
2487
	return array_merge($comments, $items);
2488
}
2489

    
2490
function update_alias_url_data() {
2491
	global $g, $aliastable;
2492

    
2493
	$updated = false;
2494

    
2495
	/* item is a url type */
2496
	$lockkey = lock('aliasurl');
2497
	$aliases = array();
2498
	$aliases_nested = array();
2499

    
2500
	foreach (config_get_path('aliases/alias', []) as $x => $alias) {
2501
		if (empty($alias['aliasurl'])) {
2502
			continue;
2503
		}
2504
		foreach ($alias['aliasurl'] as $alias_url) {
2505
			if (is_alias($alias_url)) {
2506
				// process nested URL aliases after URL-only aliases
2507
				$aliases_nested[] = $x;
2508
				continue 2;
2509
			}
2510
		}
2511
		$aliases[] = $x;
2512
	}
2513

    
2514
	foreach (array_merge($aliases, $aliases_nested) as $x) {
2515

    
2516
		$address = array();
2517
		$type = config_get_path("aliases/alias/{$x}/type");
2518
		foreach (config_get_path("aliases/alias/{$x}/aliasurl") as $alias_url) {
2519
			/* fetch down and add in */
2520
			if (is_URL($alias_url)) {
2521
				$temp_filename = tempnam("{$g['tmp_path']}/", "alias_import");
2522
				rmdir_recursive($temp_filename);
2523
				$verify_ssl = config_path_enabled('system','checkaliasesurlcert');
2524
				mkdir($temp_filename);
2525
				if (!download_file($alias_url, $temp_filename . "/aliases", $verify_ssl)) {
2526
					log_error(sprintf(gettext("Failed to download alias %s"), $alias_url));
2527
					rmdir_recursive($temp_filename);
2528
					continue;
2529
				}
2530

    
2531
				/* if the item is tar gzipped then extract */
2532
				if (stripos($alias_url, '.tgz') && !process_alias_tgz($temp_filename)) {
2533
					log_error(sprintf(gettext("Could not unpack tgz from the URL '%s'."), $alias_url));
2534
					rmdir_recursive($temp_filename);
2535
					continue;
2536
				}
2537
				if (file_exists("{$temp_filename}/aliases")) {
2538
					$t_address = parse_aliases_file("{$temp_filename}/aliases", $type, 5000);
2539
					if ($t_address == null) {
2540
						/* nothing was found */
2541
						log_error(sprintf(gettext("Could not fetch usable data from '%s'."), $alias_url));
2542
						//rmdir_recursive($temp_filename);
2543
						continue;
2544
					} else {
2545
						array_push($address, ...$t_address);
2546
					}
2547
					unset($t_address);
2548
				}
2549
				rmdir_recursive($temp_filename);
2550
			} elseif (is_alias($alias_url)) {
2551
				/* nested URL alias, see https://redmine.pfsense.org/issues/11863 */
2552
				if (!$aliastable) {
2553
					alias_make_table();
2554
				}
2555
				$t_address = explode(" ", $aliastable[$alias_url]);
2556
				if ($t_address == null) {
2557
					log_error(sprintf(gettext("Could not get usable data from '%s' URL alias."), $alias_url));
2558
					continue;
2559
				}
2560
				array_push($address, ...$t_address);
2561
			}
2562
			if (!empty($address)) {
2563
				config_set_path("aliases/alias/{$x}/address", implode(" ", $address));
2564
				$updated = true;
2565
			}
2566
		}
2567
	}
2568

    
2569
	unlock($lockkey);
2570

    
2571
	/* Report status to callers as well */
2572
	return $updated;
2573
}
2574

    
2575
function process_alias_tgz($temp_filename) {
2576
	if (!file_exists('/usr/bin/tar')) {
2577
		log_error(gettext("Alias archive is a .tar/tgz file which cannot be decompressed because utility is missing!"));
2578
		return false;
2579
	}
2580
	rename("{$temp_filename}/aliases", "{$temp_filename}/aliases.tgz");
2581
	mwexec("/usr/bin/tar xzf {$temp_filename}/aliases.tgz -C {$temp_filename}/aliases/");
2582
	unlink("{$temp_filename}/aliases.tgz");
2583
	$files_to_process = return_dir_as_array("{$temp_filename}/");
2584
	/* foreach through all extracted files and build up aliases file */
2585
	$fd = @fopen("{$temp_filename}/aliases", "w");
2586
	if (!$fd) {
2587
		log_error(sprintf(gettext("Could not open %s/aliases for writing!"), $temp_filename));
2588
		return false;
2589
	}
2590
	foreach ($files_to_process as $f2p) {
2591
		$tmpfd = @fopen($f2p, 'r');
2592
		if (!$tmpfd) {
2593
			log_error(sprintf(gettext('The following file could not be read %1$s from %2$s'), $f2p, $temp_filename));
2594
			continue;
2595
		}
2596
		while (($tmpbuf = fread($tmpfd, 65536)) !== FALSE) {
2597
			fwrite($fd, $tmpbuf);
2598
		}
2599
		fclose($tmpfd);
2600
		unlink($f2p);
2601
	}
2602
	fclose($fd);
2603
	unset($tmpbuf);
2604

    
2605
	return true;
2606
}
2607

    
2608
function version_compare_dates($a, $b) {
2609
	$a_time = strtotime($a);
2610
	$b_time = strtotime($b);
2611

    
2612
	if ((!$a_time) || (!$b_time)) {
2613
		return FALSE;
2614
	} else {
2615
		if ($a_time < $b_time) {
2616
			return -1;
2617
		} elseif ($a_time == $b_time) {
2618
			return 0;
2619
		} else {
2620
			return 1;
2621
		}
2622
	}
2623
}
2624
function version_get_string_value($a) {
2625
	$strs = array(
2626
		0 => "ALPHA-ALPHA",
2627
		2 => "ALPHA",
2628
		3 => "BETA",
2629
		4 => "B",
2630
		5 => "C",
2631
		6 => "D",
2632
		7 => "RC",
2633
		8 => "RELEASE",
2634
		9 => "*"			// Matches all release levels
2635
	);
2636
	$major = 0;
2637
	$minor = 0;
2638
	foreach ($strs as $num => $str) {
2639
		if (substr($a, 0, strlen($str)) == $str) {
2640
			$major = $num;
2641
			$n = substr($a, strlen($str));
2642
			if (is_numeric($n)) {
2643
				$minor = $n;
2644
			}
2645
			break;
2646
		}
2647
	}
2648
	return "{$major}.{$minor}";
2649
}
2650
function version_compare_string($a, $b) {
2651
	// Only compare string parts if both versions give a specific release
2652
	// (If either version lacks a string part, assume intended to match all release levels)
2653
	if (isset($a) && isset($b)) {
2654
		return version_compare_numeric(version_get_string_value($a), version_get_string_value($b));
2655
	} else {
2656
		return 0;
2657
	}
2658
}
2659
function version_compare_numeric($a, $b) {
2660
	$a_arr = explode('.', rtrim($a, '.'));
2661
	$b_arr = explode('.', rtrim($b, '.'));
2662

    
2663
	foreach ($a_arr as $n => $val) {
2664
		if (array_key_exists($n, $b_arr)) {
2665
			// So far so good, both have values at this minor version level. Compare.
2666
			if ($val > $b_arr[$n]) {
2667
				return 1;
2668
			} elseif ($val < $b_arr[$n]) {
2669
				return -1;
2670
			}
2671
		} else {
2672
			// a is greater, since b doesn't have any minor version here.
2673
			return 1;
2674
		}
2675
	}
2676
	if (count($b_arr) > count($a_arr)) {
2677
		// b is longer than a, so it must be greater.
2678
		return -1;
2679
	} else {
2680
		// Both a and b are of equal length and value.
2681
		return 0;
2682
	}
2683
}
2684
function pfs_version_compare($cur_time, $cur_text, $remote) {
2685
	// First try date compare
2686
	$v = version_compare_dates($cur_time, $remote);
2687
	if ($v === FALSE) {
2688
		// If that fails, try to compare by string
2689
		// Before anything else, simply test if the strings are equal
2690
		if (($cur_text == $remote) || ($cur_time == $remote)) {
2691
			return 0;
2692
		}
2693
		list($cur_num, $cur_str) = explode('-', $cur_text);
2694
		list($rem_num, $rem_str) = explode('-', $remote);
2695

    
2696
		// First try to compare the numeric parts of the version string.
2697
		$v = version_compare_numeric($cur_num, $rem_num);
2698

    
2699
		// If the numeric parts are the same, compare the string parts.
2700
		if ($v == 0) {
2701
			return version_compare_string($cur_str, $rem_str);
2702
		}
2703
	}
2704
	return $v;
2705
}
2706
function process_alias_urltable($name, $type, $url, $freq, $forceupdate=false, $validateonly=false) {
2707
	global $g;
2708

    
2709
	if (!is_validaliasname($name) || !filter_var($url, FILTER_VALIDATE_URL)) {
2710
		return false;
2711
	}
2712

    
2713
	$urltable_prefix = "/var/db/aliastables/";
2714
	$urltable_filename = $urltable_prefix . basename($name) . ".txt";
2715
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2716

    
2717
	// Make the aliases directory if it doesn't exist
2718
	if (!file_exists($urltable_prefix)) {
2719
		mkdir($urltable_prefix);
2720
	} elseif (!is_dir($urltable_prefix)) {
2721
		unlink($urltable_prefix);
2722
		mkdir($urltable_prefix);
2723
	}
2724

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

    
2730
		// Try to fetch the URL supplied
2731
		unlink_if_exists($tmp_urltable_filename);
2732
		$verify_ssl = config_path_enabled('system','checkaliasesurlcert');
2733
		if (download_file($url, $tmp_urltable_filename, $verify_ssl)) {
2734
			// Convert lines that begin with '$' or ';' to comments '#' instead of deleting them.
2735
			mwexec("/usr/bin/sed -i \"\" -E 's/^[[:space:]]*($|#|;)/#/g; /^#/!s/\;.*//g;' ". escapeshellarg($tmp_urltable_filename));
2736

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

    
2739
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2740
			if ($type == "urltable_ports") {
2741
				$parsed_contents = group_ports($parsed_contents, true);
2742
			}
2743
			if (is_array($parsed_contents)) {
2744
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2745
			} else {
2746
				touch($urltable_filename);
2747
			}
2748

    
2749
			/* Remove existing archive and create an up to date archive if RAM disk is enabled. */
2750
			unlink_if_exists("{$g['cf_conf_path']}/RAM_Disk_Store/{$name}.txt.tgz");
2751
			if (config_path_enabled('system','use_mfs_tmpvar') && !file_exists("/conf/ram_disks_failed")) {
2752
				mwexec("/usr/bin/tar -czf " . escapeshellarg("{$g['cf_conf_path']}/RAM_Disk_Store/{$name}.txt.tgz") . " -C / " . escapeshellarg($urltable_filename));
2753
			}
2754

    
2755
			unlink_if_exists($tmp_urltable_filename);
2756
		} else {
2757
			if (!$validateonly) {
2758
				touch($urltable_filename);
2759
			}
2760
			return false;
2761
		}
2762
		return true;
2763
	} else {
2764
		// File exists, and it doesn't need to be updated.
2765
		return -1;
2766
	}
2767
}
2768

    
2769
function get_include_contents($filename) {
2770
	if (is_file($filename)) {
2771
		ob_start();
2772
		include $filename;
2773
		$contents = ob_get_contents();
2774
		ob_end_clean();
2775
		return $contents;
2776
	}
2777
	return false;
2778
}
2779

    
2780
/* This xml 2 array function is courtesy of the php.net comment section on xml_parse.
2781
 * it is roughly 4 times faster then our existing pfSense parser but due to the large
2782
 * size of the RRD xml dumps this is required.
2783
 * The reason we do not use it for pfSense is that it does not know about array fields
2784
 * which causes it to fail on array fields with single items. Possible Todo?
2785
 */
2786
function xml2array($contents, $get_attributes = 1, $priority = 'tag') {
2787
	if (!function_exists('xml_parser_create')) {
2788
		return array ();
2789
	}
2790
	$parser = xml_parser_create('');
2791
	xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
2792
	xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2793
	xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
2794
	xml_parse_into_struct($parser, trim($contents), $xml_values);
2795
	xml_parser_free($parser);
2796
	if (!$xml_values) {
2797
		return; //Hmm...
2798
	}
2799
	$xml_array = array ();
2800
	$parents = array ();
2801
	$opened_tags = array ();
2802
	$arr = array ();
2803
	$current = & $xml_array;
2804
	$repeated_tag_index = array ();
2805
	foreach ($xml_values as $data) {
2806
		unset ($attributes, $value);
2807
		extract($data);
2808
		$result = array ();
2809
		$attributes_data = array ();
2810
		if (isset ($value)) {
2811
			if ($priority == 'tag') {
2812
				$result = $value;
2813
			} else {
2814
				$result['value'] = $value;
2815
			}
2816
		}
2817
		if (isset ($attributes) and $get_attributes) {
2818
			foreach ($attributes as $attr => $val) {
2819
				if ($priority == 'tag') {
2820
					$attributes_data[$attr] = $val;
2821
				} else {
2822
					$result['attr'][$attr] = $val; //Set all the attributes in a array called 'attr'
2823
				}
2824
			}
2825
		}
2826
		if ($type == "open") {
2827
			$parent[$level -1] = & $current;
2828
			if (!is_array($current) or (!in_array($tag, array_keys($current)))) {
2829
				$current[$tag] = $result;
2830
				if ($attributes_data) {
2831
					$current[$tag . '_attr'] = $attributes_data;
2832
				}
2833
				$repeated_tag_index[$tag . '_' . $level] = 1;
2834
				$current = &$current[$tag];
2835
			} else {
2836
				if (isset ($current[$tag][0])) {
2837
					$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
2838
					$repeated_tag_index[$tag . '_' . $level]++;
2839
				} else {
2840
					$current[$tag] = array (
2841
						$current[$tag],
2842
						$result
2843
						);
2844
					$repeated_tag_index[$tag . '_' . $level] = 2;
2845
					if (isset ($current[$tag . '_attr'])) {
2846
						$current[$tag]['0_attr'] = $current[$tag . '_attr'];
2847
						unset ($current[$tag . '_attr']);
2848
					}
2849
				}
2850
				$last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
2851
				$current = &$current[$tag][$last_item_index];
2852
			}
2853
		} elseif ($type == "complete") {
2854
			if (!isset ($current[$tag])) {
2855
				$current[$tag] = $result;
2856
				$repeated_tag_index[$tag . '_' . $level] = 1;
2857
				if ($priority == 'tag' and $attributes_data) {
2858
					$current[$tag . '_attr'] = $attributes_data;
2859
				}
2860
			} else {
2861
				if (isset ($current[$tag][0]) and is_array($current[$tag])) {
2862
					$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
2863
					if ($priority == 'tag' and $get_attributes and $attributes_data) {
2864
						$current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
2865
					}
2866
					$repeated_tag_index[$tag . '_' . $level]++;
2867
				} else {
2868
					$current[$tag] = array (
2869
						$current[$tag],
2870
						$result
2871
						);
2872
					$repeated_tag_index[$tag . '_' . $level] = 1;
2873
					if ($priority == 'tag' and $get_attributes) {
2874
						if (isset ($current[$tag . '_attr'])) {
2875
							$current[$tag]['0_attr'] = $current[$tag . '_attr'];
2876
							unset ($current[$tag . '_attr']);
2877
						}
2878
						if ($attributes_data) {
2879
							$current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
2880
						}
2881
					}
2882
					$repeated_tag_index[$tag . '_' . $level]++; //0 and 1 index is already taken
2883
				}
2884
			}
2885
		} elseif ($type == 'close') {
2886
			$current = &$parent[$level -1];
2887
		}
2888
	}
2889
	return ($xml_array);
2890
}
2891

    
2892
function get_country_name($country_code = "ALL") {
2893
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2894
		return "";
2895
	}
2896

    
2897
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2898
	$country_names_contents = file_get_contents($country_names_xml);
2899
	$country_names = xml2array($country_names_contents);
2900

    
2901
	if ($country_code == "ALL") {
2902
		$country_list = array();
2903
		foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2904
			$country_list[] = array(
2905
				"code" => $country['ISO_3166-1_Alpha-2_Code_element'],
2906
				"name" => ucwords(strtolower($country['ISO_3166-1_Country_name'])));
2907
		}
2908
		return $country_list;
2909
	}
2910

    
2911
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2912
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2913
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2914
		}
2915
	}
2916
	return "";
2917
}
2918

    
2919
/* Return the list of country codes to be used on CAs and certs */
2920
function get_cert_country_codes() {
2921
	$countries = get_country_name();
2922

    
2923
	$country_codes = array();
2924
	foreach ($countries as $country) {
2925
		$country_codes[$country['code']] = $country['code'];
2926
	}
2927
	ksort($country_codes);
2928

    
2929
	/* Preserve historical order: None, US, CA, other countries */
2930
	$first_items[''] = gettext("None");
2931
	$first_items['US'] = $country_codes['US'];
2932
	$first_items['CA'] = $country_codes['CA'];
2933
	unset($country_codes['US']);
2934
	unset($country_codes['CA']);
2935

    
2936
	return array_merge($first_items, $country_codes);
2937
}
2938

    
2939
/* sort by interface only, retain the original order of rules that apply to
2940
   the same interface */
2941
function filter_rules_sort() {
2942
	init_config_arr(array('filter', 'rule'));
2943
	$rules = config_get_path('filter/rule', []);
2944

    
2945
	/* mark each rule with the sequence number (to retain the order while sorting) */
2946
	for ($i = 0; isset($rules[$i]); $i++) {
2947
		$rules[$i]['seq'] =$i;
2948
	}
2949
	usort($rules, "filter_rules_compare");
2950

    
2951
	/* strip the sequence numbers again */
2952
	for ($i = 0; isset($rules[$i]); $i++) {
2953
		unset($rules[$i]['seq']);
2954
	}
2955

    
2956
	/* commit changes */
2957
	config_set_path('filter/rule', $rules);
2958
}
2959
function filter_rules_compare($a, $b) {
2960
	if (isset($a['floating']) && isset($b['floating'])) {
2961
		return $a['seq'] - $b['seq'];
2962
	} else if (isset($a['floating'])) {
2963
		return -1;
2964
	} else if (isset($b['floating'])) {
2965
		return 1;
2966
	} else if ($a['interface'] == $b['interface']) {
2967
		return $a['seq'] - $b['seq'];
2968
	} else {
2969
		return compare_interface_friendly_names($a['interface'], $b['interface']);
2970
	}
2971
}
2972

    
2973
function generate_ipv6_from_mac($mac) {
2974
	$elements = explode(":", $mac);
2975
	if (count($elements) <> 6) {
2976
		return false;
2977
	}
2978

    
2979
	$i = 0;
2980
	$ipv6 = "fe80::";
2981
	foreach ($elements as $byte) {
2982
		if ($i == 0) {
2983
			$hexadecimal = substr($byte, 1, 2);
2984
			$bitmap = base_convert($hexadecimal, 16, 2);
2985
			$bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT);
2986
			$bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3, 4);
2987
			$byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16);
2988
		}
2989
		$ipv6 .= $byte;
2990
		if ($i == 1) {
2991
			$ipv6 .= ":";
2992
		}
2993
		if ($i == 3) {
2994
			$ipv6 .= ":";
2995
		}
2996
		if ($i == 2) {
2997
			$ipv6 .= "ff:fe";
2998
		}
2999

    
3000
		$i++;
3001
	}
3002
	return $ipv6;
3003
}
3004

    
3005
/****f* pfsense-utils/load_mac_manufacturer_table
3006
 * NAME
3007
 *   load_mac_manufacturer_table
3008
 * INPUTS
3009
 *   none
3010
 * RESULT
3011
 *   returns associative array with MAC-Manufacturer pairs
3012
 ******/
3013
function load_mac_manufacturer_table() {
3014
	/* load MAC-Manufacture data from the file */
3015
	$macs = false;
3016
	if (file_exists("/usr/local/share/nmap/nmap-mac-prefixes")) {
3017
		$macs=file("/usr/local/share/nmap/nmap-mac-prefixes");
3018
	}
3019
	if ($macs) {
3020
		foreach ($macs as $line) {
3021
			if (preg_match('/([0-9A-Fa-f]{6}) (.*)$/', $line, $matches)) {
3022
				/* store values like this $mac_man['000C29']='VMware' */
3023
				$mac_man["$matches[1]"] = $matches[2];
3024
			}
3025
		}
3026
		return $mac_man;
3027
	} else {
3028
		return -1;
3029
	}
3030

    
3031
}
3032

    
3033
/****f* pfsense-utils/is_ipaddr_configured
3034
 * NAME
3035
 *   is_ipaddr_configured
3036
 * INPUTS
3037
 *   IP Address to check.
3038
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
3039
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
3040
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
3041
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
3042
 *     If check_subnets is true and cidrprefix is specified,
3043
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
3044
 * RESULT
3045
 *   returns true if the IP Address is configured and present on this device or overlaps a configured subnet.
3046
*/
3047
function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
3048
	if (count(where_is_ipaddr_configured($ipaddr, $ignore_if, $check_localip, $check_subnets, $cidrprefix))) {
3049
		return true;
3050
	}
3051
	return false;
3052
}
3053

    
3054
/****f* pfsense-utils/where_is_ipaddr_configured
3055
 * NAME
3056
 *   where_is_ipaddr_configured
3057
 * INPUTS
3058
 *   IP Address to check.
3059
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
3060
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
3061
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
3062
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
3063
 *     If check_subnets is true and cidrprefix is specified,
3064
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
3065
 * RESULT
3066
 *   Returns an array of the interfaces 'if' plus IP address or subnet 'ip_or_subnet' that match or overlap the IP address to check.
3067
 *   If there are no matches then an empty array is returned.
3068
*/
3069
function where_is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
3070
	$where_configured = array();
3071

    
3072
	$pos = strpos($ignore_if, '_virtualip');
3073
	if ($pos !== false) {
3074
		$ignore_vip_id = substr($ignore_if, $pos+10);
3075
		$ignore_vip_if = substr($ignore_if, 0, $pos);
3076
	} else {
3077
		$ignore_vip_id = -1;
3078
		$ignore_vip_if = $ignore_if;
3079
	}
3080

    
3081
	$isipv6 = is_ipaddrv6($ipaddr);
3082

    
3083
	if ($isipv6) {
3084
		$ipaddr = text_to_compressed_ip6($ipaddr);
3085
	}
3086

    
3087
	if ($check_subnets) {
3088
		$cidrprefix = intval($cidrprefix);
3089
		if ($isipv6) {
3090
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
3091
				$cidrprefix = 128;
3092
			}
3093
		} else {
3094
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
3095
				$cidrprefix = 32;
3096
			}
3097
		}
3098
		$iflist = get_configured_interface_list();
3099
		foreach ($iflist as $if => $ifname) {
3100
			if ($ignore_if == $if) {
3101
				continue;
3102
			}
3103

    
3104
			if ($isipv6) {
3105
				$if_ipv6 = get_interface_ipv6($if);
3106
				$if_snbitsv6 = get_interface_subnetv6($if);
3107
				/* do not check subnet overlapping on 6rd interfaces,
3108
				 * see https://redmine.pfsense.org/issues/12371 */
3109
				if ($if_ipv6 && $if_snbitsv6 &&
3110
				    ((config_get_path("interfaces/{$if}/ipaddrv6") != '6rd') || ($cidrprefix > $if_snbitsv6)) &&
3111
				    check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) {
3112
					$where_entry = array();
3113
					$where_entry['if'] = $if;
3114
					$where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if);
3115
					$where_configured[] = $where_entry;
3116
				}
3117
			} else {
3118
				$if_ipv4 = get_interface_ip($if);
3119
				$if_snbitsv4 = get_interface_subnet($if);
3120
				if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) {
3121
					$where_entry = array();
3122
					$where_entry['if'] = $if;
3123
					$where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if);
3124
					$where_configured[] = $where_entry;
3125
				}
3126
			}
3127
		}
3128
	} else {
3129
		if ($isipv6) {
3130
			$interface_list_ips = get_configured_ipv6_addresses();
3131
		} else {
3132
			$interface_list_ips = get_configured_ip_addresses();
3133
		}
3134

    
3135
		foreach ($interface_list_ips as $if => $ilips) {
3136
			if ($ignore_if == $if) {
3137
				continue;
3138
			}
3139
			if (strcasecmp($ipaddr, $ilips) == 0) {
3140
				$where_entry = array();
3141
				$where_entry['if'] = $if;
3142
				$where_entry['ip_or_subnet'] = $ilips;
3143
				$where_configured[] = $where_entry;
3144
			}
3145
		}
3146
	}
3147

    
3148
	if ($check_localip) {
3149
		if (strcasecmp($ipaddr, text_to_compressed_ip6(config_get_path('l2tp/localip', ""))) == 0) {
3150
			$where_entry = array();
3151
			$where_entry['if'] = 'l2tp';
3152
			$where_entry['ip_or_subnet'] = config_get_path('l2tp/localip');
3153
			$where_configured[] = $where_entry;
3154
		}
3155
	}
3156

    
3157
	return $where_configured;
3158
}
3159

    
3160
/****f* pfsense-utils/pfSense_handle_custom_code
3161
 * NAME
3162
 *   pfSense_handle_custom_code
3163
 * INPUTS
3164
 *   directory name to process
3165
 * RESULT
3166
 *   globs the directory and includes the files
3167
 */
3168
function pfSense_handle_custom_code($src_dir) {
3169
	// Allow extending of the nat edit page and include custom input validation
3170
	if (is_dir("$src_dir")) {
3171
		$cf = glob($src_dir . "/*.inc");
3172
		foreach ($cf as $nf) {
3173
			if ($nf == "." || $nf == "..") {
3174
				continue;
3175
			}
3176
			// Include the extra handler
3177
			include_once("$nf");
3178
		}
3179
	}
3180
}
3181

    
3182
function set_language() {
3183
	global $g;
3184

    
3185
	$lang = "";
3186
	if (!empty(config_get_path('system/language'))) {
3187
		$lang = config_get_path('system/language');
3188
	} elseif (!empty(g_get('language'))) {
3189
		$lang = g_get('language');
3190
	}
3191
	$lang .= ".UTF-8";
3192

    
3193
	putenv("LANG={$lang}");
3194
	setlocale(LC_ALL, $lang);
3195
	textdomain("pfSense");
3196
	bindtextdomain("pfSense", "/usr/local/share/locale");
3197
	bind_textdomain_codeset("pfSense", $lang);
3198
}
3199

    
3200
function get_locale_list() {
3201
	$locales = array(
3202
		"bs" => gettext("Bosnian"),
3203
		"zh_CN" => gettext("Chinese"),
3204
		"zh_Hans_CN" => gettext("Chinese (Simplified, China)"),
3205
		"zh_Hans_HK" => gettext("Chinese (Simplified, Hong Kong SAR China)"),
3206
		"zh_Hant_TW" => gettext("Chinese (Traditional, Taiwan)"),
3207
		"nl_NL" => gettext("Dutch"),
3208
		"en_US" => gettext("English"),
3209
		"fr_FR" => gettext("French"),
3210
		"de_DE" => gettext("German (Germany)"),
3211
		"it_IT" => gettext("Italian"),
3212
		"ko_FR" => gettext("Korean"),
3213
		"nb_NO" => gettext("Norwegian Bokmål"),
3214
		"pl_PL" => gettext("Polish"),
3215
		"pt_PT" => gettext("Portuguese"),
3216
		"pt_BR" => gettext("Portuguese (Brazil)"),
3217
		"ru_RU" => gettext("Russian"),
3218
		"es_ES" => gettext("Spanish"),
3219
		"es_AR" => gettext("Spanish (Argentina)"),
3220
	);
3221

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

    
3226
	//asort($locales);
3227

    
3228
	return $locales;
3229
}
3230

    
3231
function return_hex_ipv4($ipv4) {
3232
	if (!is_ipaddrv4($ipv4)) {
3233
		return(false);
3234
	}
3235

    
3236
	/* we need the hex form of the interface IPv4 address */
3237
	$ip4arr = explode(".", $ipv4);
3238
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3239
}
3240

    
3241
function convert_ipv6_to_128bit($ipv6) {
3242
	if (!is_ipaddrv6($ipv6)) {
3243
		return(false);
3244
	}
3245

    
3246
	$ip6arr = array();
3247
	$ip6prefix = Net_IPv6::uncompress($ipv6);
3248
	$ip6arr = explode(":", $ip6prefix);
3249
	/* binary presentation of the prefix for all 128 bits. */
3250
	$ip6prefixbin = "";
3251
	foreach ($ip6arr as $element) {
3252
		$ip6prefixbin .= sprintf("%016b", hexdec($element));
3253
	}
3254
	return($ip6prefixbin);
3255
}
3256

    
3257
function convert_128bit_to_ipv6($ip6bin) {
3258
	if (strlen($ip6bin) <> 128) {
3259
		return(false);
3260
	}
3261

    
3262
	$ip6arr = array();
3263
	$ip6binarr = array();
3264
	$ip6binarr = str_split($ip6bin, 16);
3265
	foreach ($ip6binarr as $binpart) {
3266
		$ip6arr[] = dechex(bindec($binpart));
3267
	}
3268
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3269

    
3270
	return($ip6addr);
3271
}
3272

    
3273

    
3274
/* Returns the calculated bit length of the prefix delegation from the WAN interface */
3275
/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */
3276
/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */
3277
/* 6to4 is 16 bits, e.g. 65535 */
3278
function calculate_ipv6_delegation_length($if) {
3279
	$cfg = config_get_path("interfaces/{$if}");
3280
	if (!is_array($cfg)) {
3281
		return false;
3282
	}
3283

    
3284
	switch ($cfg['ipaddrv6']) {
3285
		case "6to4":
3286
			$pdlen = 16;
3287
			break;
3288
		case "6rd":
3289
			$rd6plen = explode("/", $cfg['prefix-6rd']);
3290
			$pdlen = (64 - ((int) $rd6plen[1] + (32 - (int) $cfg['prefix-6rd-v4plen'])));
3291
			break;
3292
		case "dhcp6":
3293
			$pdlen = $cfg['dhcp6-ia-pd-len'];
3294
			break;
3295
		default:
3296
			$pdlen = 0;
3297
			break;
3298
	}
3299
	return($pdlen);
3300
}
3301

    
3302
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3303
	/* convert zero-value prefix IPv6 addresses with IPv4 mapping to hex
3304
	 * see https://redmine.pfsense.org/issues/12440 */
3305
	$suffix_list = explode(':', $suffix);
3306
	if (is_ipaddrv4($suffix_list[count($suffix_list) - 1])) {
3307
		$hexsuffix = dechex(ip2long($suffix_list[count($suffix_list) - 1]));
3308
		$suffix_list[count($suffix_list) - 1] = substr($hexsuffix, 0, 4);
3309
		$suffix_list[] = substr($hexsuffix, 4, 8);
3310
		$suffix = implode(':', $suffix_list);
3311
	}
3312
	$prefix = Net_IPv6::uncompress($prefix, true);
3313
	$suffix = Net_IPv6::uncompress($suffix, true);
3314

    
3315
	/*
3316
	 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
3317
	 *                ^^^^ ^
3318
	 *                |||| \-> 64
3319
	 *                |||\---> 63, 62, 61, 60
3320
	 *                ||\----> 56
3321
	 *                |\-----> 52
3322
	 *                \------> 48
3323
	 */
3324

    
3325
	switch ($len) {
3326
	case 48:
3327
		$prefix_len = 15;
3328
		break;
3329
	case 52:
3330
		$prefix_len = 16;
3331
		break;
3332
	case 56:
3333
		$prefix_len = 17;
3334
		break;
3335
	case 59:
3336
	case 60:
3337
		$prefix_len = 18;
3338
		break;
3339
	/*
3340
	 * XXX 63, 62 and 61 should use 18 but PD can change and if
3341
	 * we let user chose this bit it can end up out of PD network
3342
	 *
3343
	 * Leave this with 20 for now until we find a way to let user
3344
	 * chose it. The side-effect is users with PD with one of these
3345
	 * lengths will not be able to setup DHCP server range for full
3346
	 * PD size, only for last /64 network
3347
	 */
3348
	case 63:
3349
	case 62:
3350
	case 61:
3351
	default:
3352
		$prefix_len = 20;
3353
		break;
3354
	}
3355

    
3356
	return text_to_compressed_ip6(substr($prefix, 0, $prefix_len) .
3357
	    substr($suffix, $prefix_len));
3358
}
3359

    
3360
function dhcpv6_pd_str_help($pdlen) {
3361
	$result = '';
3362

    
3363
	switch ($pdlen) {
3364
	case 48:
3365
		$result = '::xxxx:xxxx:xxxx:xxxx:xxxx';
3366
		break;
3367
	case 52:
3368
		$result = '::xxx:xxxx:xxxx:xxxx:xxxx';
3369
		break;
3370
	case 56:
3371
		$result = '::xx:xxxx:xxxx:xxxx:xxxx';
3372
		break;
3373
	case 59:
3374
	case 60:
3375
		$result = '::x:xxxx:xxxx:xxxx:xxxx';
3376
		break;
3377
	/*
3378
	 * XXX 63, 62 and 61 should use same mask as 60 but if
3379
	 * we let the user choose this bit it can end up out of PD network
3380
	 *
3381
	 * Leave this with the same as 64 for now until we find a way to
3382
	 * let the user choose it. The side-effect is users with PD with one
3383
	 * of these lengths will not be able to setup DHCP server ranges
3384
	 * for full PD size, only for last /64 network
3385
	 */
3386
	case 61:
3387
	case 62:
3388
	case 63:
3389
	case 64:
3390
	default:
3391
		$result = '::xxxx:xxxx:xxxx:xxxx';
3392
		break;
3393
	}
3394

    
3395
	return $result;
3396
}
3397

    
3398
function huawei_rssi_to_string($rssi) {
3399
	$dbm = array();
3400
	$i = 0;
3401
	$dbstart = -113;
3402
	while ($i < 32) {
3403
		$dbm[$i] = $dbstart + ($i * 2);
3404
		$i++;
3405
	}
3406
	$percent = round(($rssi / 31) * 100);
3407
	$string = "rssi:{$rssi} level:{$dbm[$rssi]}dBm percent:{$percent}%";
3408
	return $string;
3409
}
3410

    
3411
function huawei_mode_to_string($mode, $submode) {
3412
	$modes[0] = gettext("None");
3413
	$modes[1] = "AMPS";
3414
	$modes[2] = "CDMA";
3415
	$modes[3] = "GSM/GPRS";
3416
	$modes[4] = "HDR";
3417
	$modes[5] = "WCDMA";
3418
	$modes[6] = "GPS";
3419

    
3420
	$submodes[0] = gettext("No Service");
3421
	$submodes[1] = "GSM";
3422
	$submodes[2] = "GPRS";
3423
	$submodes[3] = "EDGE";
3424
	$submodes[4] = "WCDMA";
3425
	$submodes[5] = "HSDPA";
3426
	$submodes[6] = "HSUPA";
3427
	$submodes[7] = "HSDPA+HSUPA";
3428
	$submodes[8] = "TD-SCDMA";
3429
	$submodes[9] = "HSPA+";
3430
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3431
	return $string;
3432
}
3433

    
3434
function huawei_service_to_string($state) {
3435
	$modes[0] = gettext("No Service");
3436
	$modes[1] = gettext("Restricted Service");
3437
	$modes[2] = gettext("Valid Service");
3438
	$modes[3] = gettext("Restricted Regional Service");
3439
	$modes[4] = gettext("Powersaving Service");
3440
	$modes[255] = gettext("Unknown Service");
3441
	$string = $modes[$state];
3442
	return $string;
3443
}
3444

    
3445
function huawei_simstate_to_string($state) {
3446
	$modes[0] = gettext("Invalid SIM/locked State");
3447
	$modes[1] = gettext("Valid SIM State");
3448
	$modes[2] = gettext("Invalid SIM CS State");
3449
	$modes[3] = gettext("Invalid SIM PS State");
3450
	$modes[4] = gettext("Invalid SIM CS/PS State");
3451
	$modes[255] = gettext("Missing SIM State");
3452
	$string = $modes[$state];
3453
	return $string;
3454
}
3455

    
3456
function zte_rssi_to_string($rssi) {
3457
	return huawei_rssi_to_string($rssi);
3458
}
3459

    
3460
function zte_mode_to_string($mode, $submode) {
3461
	$modes[0] = gettext("No Service");
3462
	$modes[1] = gettext("Limited Service");
3463
	$modes[2] = "GPRS";
3464
	$modes[3] = "GSM";
3465
	$modes[4] = "UMTS";
3466
	$modes[5] = "EDGE";
3467
	$modes[6] = "HSDPA";
3468

    
3469
	$submodes[0] = "CS_ONLY";
3470
	$submodes[1] = "PS_ONLY";
3471
	$submodes[2] = "CS_PS";
3472
	$submodes[3] = "CAMPED";
3473
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3474
	return $string;
3475
}
3476

    
3477
function zte_service_to_string($service) {
3478
	$modes[0] = gettext("Initializing Service");
3479
	$modes[1] = gettext("Network Lock error Service");
3480
	$modes[2] = gettext("Network Locked Service");
3481
	$modes[3] = gettext("Unlocked or correct MCC/MNC Service");
3482
	$string = $modes[$service];
3483
	return $string;
3484
}
3485

    
3486
function zte_simstate_to_string($state) {
3487
	$modes[0] = gettext("No action State");
3488
	$modes[1] = gettext("Network lock State");
3489
	$modes[2] = gettext("(U)SIM card lock State");
3490
	$modes[3] = gettext("Network Lock and (U)SIM card Lock State");
3491
	$string = $modes[$state];
3492
	return $string;
3493
}
3494

    
3495
function get_configured_pppoe_server_interfaces() {
3496
	$iflist = array();
3497
	foreach (config_get_path('pppoes/pppoe', []) as $pppoe) {
3498
		if ($pppoe['mode'] == "server") {
3499
			$int = "poes". $pppoe['pppoeid'];
3500
			$iflist[$int] = strtoupper($int);
3501
		}
3502
	}
3503
	return $iflist;
3504
}
3505

    
3506
function get_pppoes_child_interfaces($ifpattern) {
3507
	$if_arr = array();
3508
	if ($ifpattern == "") {
3509
		return;
3510
	}
3511

    
3512
	exec("/sbin/ifconfig", $out, $ret);
3513
	foreach ($out as $line) {
3514
		if (preg_match("/^({$ifpattern}-[0-9]+):/i", $line, $match)) {
3515
			$if_arr[] = $match[1];
3516
		}
3517
	}
3518
	return $if_arr;
3519

    
3520
}
3521

    
3522
/****f* pfsense-utils/pkg_call_plugins
3523
 * NAME
3524
 *   pkg_call_plugins
3525
 * INPUTS
3526
 *   $plugin_type value used to search in package configuration if the plugin is used, also used to create the function name
3527
 *   $plugin_params parameters to pass to the plugin function for passing multiple parameters a array can be used.
3528
 * RESULT
3529
 *   returns associative array results from the plugin calls for each package
3530
 * NOTES
3531
 *   This generic function can be used to notify or retrieve results from functions that are defined in packages.
3532
 ******/
3533
function pkg_call_plugins($plugin_type, $plugin_params) {
3534
	global $g;
3535
	$results = array();
3536
	foreach (config_get_path('installedpackages/package') as $package) {
3537
		foreach (array_get_path($package, 'plugins/item', []) as $plugin) {
3538
			if (!is_array($plugin) || empty($plugin)) {
3539
				continue;
3540
			}
3541
			if ($plugin['type'] == $plugin_type) {
3542
				if (file_exists($package['include_file'])) {
3543
					require_once($package['include_file']);
3544
				} else {
3545
					continue;
3546
				}
3547
				$pkgname = substr(reverse_strrchr($package['configurationfile'], "."), 0, -1);
3548
				$plugin_function = $pkgname . '_'. $plugin_type;
3549
				$results[$pkgname] = call_user_func($plugin_function, $plugin_params);
3550
			}
3551
		}
3552
	}
3553
	return $results;
3554
}
3555

    
3556
// Convert IPv6 addresses to lower case
3557
function addrtolower($ip) {
3558
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3559
		return(strtolower($ip));
3560
	} else {
3561
		return($ip);
3562
	}
3563
}
3564

    
3565
function compare_by_name($a, $b) {
3566
	return strcasecmp($a['name'], $b['name']);
3567
}
3568

    
3569
/****f* pfsense-utils/getarraybyref
3570
 * NAME
3571
 *   getarraybyref
3572
 * INPUTS
3573
 *   $array the array of which a items array needs to be found.
3574
 *   $args.. the sub-items to be retrieved/created
3575
 * RESULT
3576
 *   returns the array that was retrieved from configuration, its created if it does not exist
3577
 * NOTES
3578
 *   Used by haproxy / acme / others.?. .
3579
 *   can be used like this:  $a_certificates = getarraybyref($config, 'installedpackages', 'acme', 'certificates', 'item');
3580
 ******/
3581
function &getarraybyref(&$array) {
3582
	if (!isset($array)) {
3583
		return false;
3584
	}
3585
	if (!is_array($array)) {
3586
		$array = array();
3587
	}
3588
	$item = &$array;
3589
	$arg = func_get_args();
3590
	for($i = 1; $i < count($arg); $i++) {
3591
		$itemindex = $arg[$i];
3592
		if (!is_array($item[$itemindex])) {
3593
			$item[$itemindex] = array();
3594
		}
3595
		$item = &$item[$itemindex];
3596
	}
3597
	return $item;
3598
}
3599

    
3600
/****f* pfsense-utils/send_download_data
3601
 * NAME
3602
 *   send_download_data - Send content to a user's browser as a file to download
3603
 * INPUTS
3604
 *   $type        : The type of download, either 'data' to send the contents of
3605
 *                    a variable or 'file' to send the contents of a file on the
3606
 *                    filesystem.
3607
 *   $content     : For 'data' type, the content to send the user. For 'file'
3608
 *                    type, the full path to the file to send.
3609
 *   $userfilename: The filename presented to the user when downloading. For
3610
 *                    'file' type, this may be omitted and the basename of
3611
 *                    $content will be used instead.
3612
 *   $contenttype : MIME content type of the data. Default "application/octet-stream"
3613
 * RESULT
3614
 *   Sends the data to the browser as a file to download.
3615
 ******/
3616

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

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

    
3624
	/* Cannot determine the filename, so bail. */
3625
	if (empty($name)) {
3626
		exit;
3627
	}
3628

    
3629
	/* Send basic download headers */
3630
	header("Content-Type: {$contenttype}");
3631
	header("Content-Length: {$size}");
3632
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3633

    
3634
	/* Send cache headers */
3635
	if (isset($_SERVER['HTTPS'])) {
3636
		header('Pragma: ');
3637
		header('Cache-Control: ');
3638
	} else {
3639
		header("Pragma: private");
3640
		header("Cache-Control: private, must-revalidate");
3641
	}
3642

    
3643
	/* Ensure output buffering is off so PHP does not consume
3644
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3645
	while (ob_get_level()) {
3646
		@ob_end_clean();
3647
	}
3648

    
3649
	/* Send the data to the user */
3650
	if ($type == 'file') {
3651
		readfile($content);
3652
	} else {
3653
		echo $content;
3654
	}
3655

    
3656
	/* Flush any remaining output buffer */
3657
	@ob_end_flush();
3658
	exit;
3659
}
3660

    
3661
// Test whether the hostname in a URL can be resolved with a very short timeout
3662
function is_url_hostname_resolvable($url) {
3663
	$urlhostname = parse_url($url, PHP_URL_HOST);
3664
	if (empty($urlhostname)) {
3665
		return false;
3666
	}
3667
	putenv("RES_OPTIONS=timeout:3 attempts:1");
3668
	$resolvable = ($urlhostname !== gethostbyname($urlhostname));
3669
	putenv("RES_OPTIONS");
3670
	return $resolvable;
3671
}
3672

    
3673
function get_pf_reserved(?string $name = '', bool $check_ifname = true):bool|array {
3674
	global $pf_reserved_keywords, $reserved_table_names, $FilterIflist;
3675

    
3676
	// get relevant interface names
3677
	$iflist = $FilterIflist;
3678
	if (empty($iflist)) {
3679
		// if list
3680
		$iflist = config_get_path('interfaces', []);
3681
		// l2tp
3682
		if (config_get_path('l2tp/mode') == "server") {
3683
			$iflist['l2tp'] = 'L2TP';
3684
		}
3685
		// pppoe
3686
		foreach (config_get_path('pppoes/pppoe', []) as $ifgen) {
3687
			if (array_key_exists('mode', $ifgen) && $ifgen['mode'] == 'server') {
3688
				$iflist['pppoe'] = 'pppoe';
3689
				break;
3690
			}
3691
		}
3692
		// add interface groups
3693
		foreach (config_get_path('ifgroups/ifgroupentry', []) as $ifgen) {
3694
			$iflist[$ifgen['ifname']] = $ifgen['ifname'];
3695
		}
3696
	}
3697
	/**
3698
	 * pf table names are limited to a max of 31 characters.
3699
	 * Interface group names are limited to a max of 15 characters.
3700
	 * Hence, a combination of the interface name (optX) + the suffix should be safe.
3701
	 */ 
3702
	$reserved = [];
3703
	foreach (array_keys($iflist) as $if) {
3704
		if ($check_ifname) {
3705
			$reserved[] = strtoupper($if);
3706
		}
3707
		$reserved[] = strtoupper("{$if}__NETWORK");
3708
	}
3709
	$reserved = array_merge($pf_reserved_keywords, $reserved_table_names, $reserved);
3710

    
3711
	return (empty($name) ? $reserved : in_array(strtoupper($name), $reserved));
3712
}
3713

    
3714
function get_pf_timeouts () {
3715
	$pftimeout = array();
3716
	exec("/sbin/pfctl -st", $pfctlst, $retval);
3717
	if ($retval == 0) {
3718
		foreach ($pfctlst as $pfst) {
3719
			preg_match('/([a-z]+)\.([a-z]+)\s+([0-9]+)/', $pfst, $timeout);
3720
			if ($timeout[1] == "other") {
3721
				$proto = "Other";
3722
			} else {
3723
				$proto = strtoupper($timeout[1]);
3724
			}
3725
			if ($timeout[2] == "finwait") {
3726
				$type = "FIN Wait";
3727
			} else {
3728
				$type = ucfirst($timeout[2]);
3729
			}
3730
			$pftimeout[$proto][$type]['name'] = $proto . " " . $type;
3731
			$pftimeout[$proto][$type]['keyname'] = $timeout[1] . $timeout[2] . "timeout";
3732
			$pftimeout[$proto][$type]['value'] = $timeout[3];
3733
		}
3734
	}
3735
	return $pftimeout;
3736
}
3737

    
3738
function set_curlproxy(&$ch) {
3739
	if (!empty(config_get_path('system/proxyurl'))) {
3740
		curl_setopt($ch, CURLOPT_PROXY, config_get_path('system/proxyurl'));
3741
		if (!empty(config_get_path('system/proxyport'))) {
3742
			curl_setopt($ch, CURLOPT_PROXYPORT, config_get_path('system/proxyport'));
3743
		}
3744
		$proxyuser = config_get_path('system/proxyuser');
3745
		$proxypass = config_get_path('system/proxypass');
3746
		if (!empty($proxyuser) && !empty($proxypass)) {
3747
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
3748
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$proxyuser}:{$proxypass}");
3749
		}
3750
	}
3751
}
(39-39/61)