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-2024 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 $g;
1051

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

    
1056
	/* parse config.xml */
1057
	config_read_file(true, 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
	/* parse config.xml */
1107
	config_read_file(true, true);
1108

    
1109
	/* set up our timezone */
1110
	system_timezone_configure();
1111

    
1112
	/* set up our hostname */
1113
	system_hostname_configure();
1114

    
1115
	/* make hosts file */
1116
	system_hosts_generate();
1117

    
1118
	/* generate resolv.conf */
1119
	system_resolvconf_generate();
1120

    
1121
	/* enable routing */
1122
	system_routing_enable();
1123

    
1124
	/* set up interfaces */
1125
	interfaces_configure();
1126

    
1127
	/* start dyndns service */
1128
	services_dyndns_configure();
1129

    
1130
	/* configure cron service */
1131
	configure_cron();
1132

    
1133
	/* start the NTP client */
1134
	system_ntp_configure();
1135

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

    
1140
	/* restart sshd */
1141
	send_event("service restart sshd");
1142

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

    
1147
function load_loader_conf($loader_conf = NULL, $local = false) {
1148

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

    
1158
	$input_split = explode("\n", $input);
1159

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

    
1216
	return ($data);
1217
}
1218

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

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

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

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

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

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

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

    
1276
	$data = load_loader_conf($loader_conf_file, false);
1277

    
1278
	$data[] = 'loader_conf_files="/boot/loader.conf.lua"';
1279

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

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

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

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

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

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

    
1343
	safe_write_file($loader_conf_file, $data);
1344

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

    
1354
}
1355

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

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

    
1362
	$ttys = file_get_contents($ttys_file);
1363
	$ttys_split = explode("\n", $ttys);
1364

    
1365
	$data = array();
1366

    
1367
	$on_off = (is_serial_enabled() ? 'onifconsole' : 'off');
1368

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

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

    
1383
	$found = array();
1384

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

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

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

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

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

    
1436
	safe_write_file($ttys_file, $data);
1437

    
1438
	unset($ttys, $ttys_file, $ttys_split, $data);
1439

    
1440
	if ($when != "upgrade") {
1441
		reload_ttys();
1442
	}
1443

    
1444
	return;
1445
}
1446

    
1447
function is_serial_enabled() {
1448
	global $g;
1449

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

    
1455
	return true;
1456
}
1457

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

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

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

    
1485
	return false;
1486
}
1487

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

    
1500
	return false;
1501
}
1502

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

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

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

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

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

    
1531
		return true;
1532
	}
1533

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

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

    
1548
		$ifcfgsnv6 = get_interface_subnetv6($if);
1549
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1550

    
1551
		if (!is_ipaddrv6($subnetv6)) {
1552
			continue;
1553
		}
1554

    
1555
		if (config_get_path("dhcpdv6/{$if}/ramode", "disabled") == "disabled") {
1556
			continue;
1557
		}
1558

    
1559
		return true;
1560
	}
1561

    
1562
	return false;
1563
}
1564

    
1565
/* Any PPPoE servers enabled? */
1566
function is_pppoe_server_enabled() {
1567
	$pppoeenable = false;
1568

    
1569
	foreach (config_get_path('pppoes/pppoe', []) as $pppoes) {
1570
		if ($pppoes['mode'] == 'server') {
1571
			$pppoeenable = true;
1572
		}
1573
	}
1574

    
1575
	return $pppoeenable;
1576
}
1577

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

    
1592
/* Compute the total uptime from the ppp uptime log file in the conf directory */
1593

    
1594
function get_ppp_uptime($port) {
1595
	if (file_exists("/conf/{$port}.log")) {
1596
		$saved_time = file_get_contents("/conf/{$port}.log");
1597
		$uptime_data = explode("\n", $saved_time);
1598
		$sec = 0;
1599
		foreach ($uptime_data as $upt) {
1600
			/* Skip blank lines, trim, and cast to int to ensure type matches.
1601
			 * https://redmine.pfsense.org/issues/14117 */
1602
			if (!empty($upt)) {
1603
				$sec += (int) trim(substr($upt, 1 + strpos($upt, " ")));
1604
			}
1605
		}
1606
		return convert_seconds_to_dhms($sec);
1607
	} else {
1608
		$total_time = gettext("No history data found!");
1609
		return $total_time;
1610
	}
1611
}
1612

    
1613
//returns interface information
1614
function get_interface_info($ifdescr) {
1615
	global $g;
1616

    
1617
	$ifinfo = array();
1618
	if (empty(config_get_path("interfaces/{$ifdescr}"))) {
1619
		return;
1620
	}
1621
	$ifinfo['hwif'] = config_get_path("interfaces/{$ifdescr}/if");
1622
	$ifinfo['enable'] = config_path_enabled("interfaces/{$ifdescr}");
1623
	$ifinfo['if'] = get_real_interface($ifdescr);
1624

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

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

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

    
1686
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1687
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1688
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1689
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1690

    
1691
	$link_type = config_get_path("interfaces/{$ifdescr}/ipaddr");
1692
	switch ($link_type) {
1693
		/* DHCP? -> see if dhclient is up */
1694
		case "dhcp":
1695
			/* see if dhclient is up */
1696
			if (find_dhclient_process($ifinfo['if']) != 0) {
1697
				$ifinfo['dhcplink'] = "up";
1698
			} else {
1699
				$ifinfo['dhcplink'] = "down";
1700
			}
1701

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

    
1714
			break;
1715
		/* PPP interface? -> get uptime for this session and cumulative uptime from the persistent log file in conf */
1716
		case "ppp":
1717
			if ($ifinfo['status'] == "up") {
1718
				$ifinfo['ppplink'] = "up";
1719
			} else {
1720
				$ifinfo['ppplink'] = "down" ;
1721
			}
1722

    
1723
			if (empty($ifinfo['status'])) {
1724
				$ifinfo['status'] = "down";
1725
			}
1726

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

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

    
1776
	if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) {
1777
		$sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`);
1778
		$ifinfo['ppp_uptime'] = convert_seconds_to_dhms($sec);
1779
	}
1780

    
1781
	if ($ifinfo['status'] == "up") {
1782
		/* try to determine media with ifconfig */
1783
		$ifconfiginfo = [];
1784
		exec("/sbin/ifconfig -v " . $ifinfo['if'], $ifconfiginfo);
1785
		$wifconfiginfo = [];
1786
		if (is_interface_wireless($ifdescr)) {
1787
			exec("/sbin/ifconfig {$ifinfo['if']} list sta", $wifconfiginfo);
1788
			array_shift($wifconfiginfo);
1789
		}
1790
		$matches = "";
1791
		foreach ($ifconfiginfo as $ici) {
1792

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

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

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

    
1880
	return $ifinfo;
1881
}
1882

    
1883
//returns cpu speed of processor. Good for determining capabilities of machine
1884
function get_cpu_speed() {
1885
	return get_single_sysctl("hw.clockrate");
1886
}
1887

    
1888
function get_uptime_sec() {
1889
	$boottime = "";
1890
	$matches = "";
1891
	$boottime = get_single_sysctl("kern.boottime");
1892
	preg_match("/sec = (\d+)/", $boottime, $matches);
1893
	$boottime = $matches[1];
1894
	if (intval($boottime) == 0) {
1895
		return 0;
1896
	}
1897

    
1898
	$uptime = time() - $boottime;
1899
	return $uptime;
1900
}
1901

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

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

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

    
1966
function pfsense_default_state_size() {
1967
	/* get system memory amount */
1968
	$memory = get_memory();
1969
	$physmem = $memory[0];
1970

    
1971
	if ((int) $physmem > 0) {
1972
		/* Be cautious and only allocate 10% of system memory to the state table */
1973
		$max_states = (int) ($physmem/10)*1000;
1974
	} else {
1975
		/* If the memory check result is invalid, use a low but still
1976
		 * somewhat sane default (Equivalent to ~256MB RAM) */
1977
		$max_states = 25600;
1978
	}
1979

    
1980
	return $max_states;
1981
}
1982

    
1983
function pfsense_current_tables_size() {
1984
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1985
	return $current;
1986
}
1987

    
1988
function pfsense_current_table_entries_size() {
1989
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1990
	return (trim($current));
1991
}
1992

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

    
2017
	if (trim($oldcontents) != trim($contents)) {
2018
		if (g_get('debug')) {
2019
			log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents));
2020
		}
2021
		return ($oldcontents);
2022
	} else {
2023
		return false;
2024
	}
2025
}
2026

    
2027
/*
2028
 * load_crypto() - Load crypto modules if enabled in config.
2029
 */
2030
function load_crypto() {
2031
	$crypto_modules = array('aesni', 'cryptodev');
2032

    
2033
	$enabled_modules = explode('_', config_get_path('system/crypto_hardware'));
2034

    
2035
	foreach ($enabled_modules as $enmod) {
2036
		if (empty($enmod) || !in_array($enmod, $crypto_modules)) {
2037
			continue;
2038
		}
2039
		if (!is_module_loaded($enmod)) {
2040
			log_error(sprintf(gettext("Loading %s cryptographic accelerator module."), $enmod));
2041
			mute_kernel_msgs();
2042
			mwexec("/sbin/kldload " . escapeshellarg($enmod));
2043
			unmute_kernel_msgs();
2044
		}
2045
	}
2046
}
2047

    
2048
/*
2049
 * load_thermal_hardware() - Load temperature monitor kernel module
2050
 */
2051
function load_thermal_hardware() {
2052
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
2053
	$thermal_hardware = config_get_path('system/thermal_hardware');
2054

    
2055
	if (!in_array($thermal_hardware, $thermal_hardware_modules)) {
2056
		return false;
2057
	}
2058

    
2059
	if (!empty($thermal_hardware) && !is_module_loaded($thermal_hardware)) {
2060
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $thermal_hardware));
2061
		mute_kernel_msgs();
2062
		mwexec("/sbin/kldload {$thermal_hardware}");
2063
		unmute_kernel_msgs();
2064
	}
2065
}
2066

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

    
2079
	if ($rc != 0 || !isset($output[0])) {
2080
		return false;
2081
	}
2082

    
2083
	foreach ($virtualenvs as $virtualenv) {
2084
		if (stripos($output[0], $virtualenv) !== false) {
2085
			return true;
2086
		}
2087
	}
2088

    
2089
	return false;
2090
}
2091

    
2092
function get_freebsd_version() {
2093
	$version = explode(".", php_uname("r"));
2094
	return $version[0];
2095
}
2096

    
2097
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
2098
	global $g;
2099

    
2100
	$fp = fopen($destination, "wb");
2101

    
2102
	if (!$fp) {
2103
		return false;
2104
	}
2105

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

    
2128
	set_curlproxy($ch);
2129

    
2130
	@curl_exec($ch);
2131
	$http_code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
2132
	fclose($fp);
2133
	curl_close($ch);
2134
	if ($http_code == 200) {
2135
		return true;
2136
	} else {
2137
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2138
		unlink_if_exists($destination);
2139
		return false;
2140
	}
2141
}
2142

    
2143
function download_file_with_progress_bar($url, $destination, $verify_ssl = true, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) {
2144
	global $g, $ch, $fout, $file_size, $downloaded, $first_progress_update;
2145
	$file_size = 1;
2146
	$downloaded = 1;
2147
	$first_progress_update = TRUE;
2148
	/* open destination file */
2149
	$fout = fopen($destination, "wb");
2150

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

    
2181
	set_curlproxy($ch);
2182

    
2183
	@curl_exec($ch);
2184
	$http_code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
2185
	fclose($fout);
2186
	curl_close($ch);
2187
	if ($http_code == 200) {
2188
		return true;
2189
	} else {
2190
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2191
		unlink_if_exists($destination);
2192
		return false;
2193
	}
2194
}
2195

    
2196
function read_header($ch, $string) {
2197
	global $file_size;
2198
	$length = strlen($string);
2199
	$regs = "";
2200
	preg_match("/(Content-Length:) (.*)/", $string, $regs);
2201
	if ($regs[2] <> "") {
2202
		$file_size = intval($regs[2]);
2203
	}
2204
	ob_flush();
2205
	return $length;
2206
}
2207

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

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

    
2280
/*
2281
 *   update_status: update top textarea dynamically.
2282
 */
2283
function update_status($status) {
2284
	global $pkg_interface;
2285

    
2286
	if ($pkg_interface == "console") {
2287
		print ("{$status}");
2288
	}
2289

    
2290
	/* ensure that contents are written out */
2291
	ob_flush();
2292
}
2293

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

    
2316
function update_alias_name($new_alias_name, $orig_alias_name) {
2317
	if (!$orig_alias_name) {
2318
		return;
2319
	}
2320

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

    
2366
function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) {
2367
	global $g, $pconfig, $debug;
2368
	if (!$origname) {
2369
		return;
2370
	}
2371

    
2372
	$config = config_get_path('');
2373
	$sectionref = &$config;
2374
	foreach ($section as $sectionname) {
2375
		if (is_array($sectionref) && isset($sectionref[$sectionname])) {
2376
			$sectionref = &$sectionref[$sectionname];
2377
		} else {
2378
			return;
2379
		}
2380
	}
2381

    
2382
	if ($debug) {
2383
		$fd = fopen("{$g['tmp_path']}/print_r", "a");
2384
		fwrite($fd, print_r($pconfig, true));
2385
	}
2386

    
2387
	if (is_array($sectionref)) {
2388
		foreach (array_keys($sectionref) as $itemkey) {
2389
			if ($debug) {
2390
				fwrite($fd, "$itemkey\n");
2391
			}
2392

    
2393
			$fieldfound = true;
2394
			$fieldref = &$sectionref[$itemkey];
2395
			foreach ($field as $fieldname) {
2396
				if (is_array($fieldref) && isset($fieldref[$fieldname])) {
2397
					$fieldref = &$fieldref[$fieldname];
2398
				} else {
2399
					$fieldfound = false;
2400
					break;
2401
				}
2402
			}
2403
			if ($fieldfound && $fieldref == $origname) {
2404
				if ($debug) {
2405
					fwrite($fd, "Setting old alias value $origname to $new_alias_name\n");
2406
				}
2407
				$fieldref = $new_alias_name;
2408
			}
2409
		}
2410
		config_set_path('', $config);
2411
	}
2412

    
2413
	if ($debug) {
2414
		fclose($fd);
2415
	}
2416

    
2417
}
2418

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

    
2429
	if (!file_exists($filename)) {
2430
		log_error(sprintf(gettext("Could not process non-existent file from alias: %s"), $filename));
2431
		return null;
2432
	}
2433

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

    
2499
function update_alias_url_data() {
2500
	global $g, $aliastable;
2501

    
2502
	$updated = false;
2503

    
2504
	/* item is a url type */
2505
	$lockkey = lock('aliasurl');
2506
	$aliases = array();
2507
	$aliases_nested = array();
2508

    
2509
	foreach (config_get_path('aliases/alias', []) as $x => $alias) {
2510
		if (empty($alias['aliasurl'])) {
2511
			continue;
2512
		}
2513
		foreach ($alias['aliasurl'] as $alias_url) {
2514
			if (is_alias($alias_url)) {
2515
				// process nested URL aliases after URL-only aliases
2516
				$aliases_nested[] = $x;
2517
				continue 2;
2518
			}
2519
		}
2520
		$aliases[] = $x;
2521
	}
2522

    
2523
	foreach (array_merge($aliases, $aliases_nested) as $x) {
2524

    
2525
		$address = array();
2526
		$type = config_get_path("aliases/alias/{$x}/type");
2527
		foreach (config_get_path("aliases/alias/{$x}/aliasurl", []) as $alias_url) {
2528
			/* fetch down and add in */
2529
			if (is_URL($alias_url)) {
2530
				$temp_filename = tempnam("{$g['tmp_path']}/", "alias_import");
2531
				rmdir_recursive($temp_filename);
2532
				$verify_ssl = config_path_enabled('system','checkaliasesurlcert');
2533
				mkdir($temp_filename);
2534
				if (!download_file($alias_url, $temp_filename . "/aliases", $verify_ssl)) {
2535
					log_error(sprintf(gettext("Failed to download alias %s"), $alias_url));
2536
					rmdir_recursive($temp_filename);
2537
					continue;
2538
				}
2539

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

    
2578
	unlock($lockkey);
2579

    
2580
	/* Report status to callers as well */
2581
	return $updated;
2582
}
2583

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

    
2614
	return true;
2615
}
2616

    
2617
function version_compare_dates($a, $b) {
2618
	$a_time = strtotime($a);
2619
	$b_time = strtotime($b);
2620

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

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

    
2705
		// First try to compare the numeric parts of the version string.
2706
		$v = version_compare_numeric($cur_num, $rem_num);
2707

    
2708
		// If the numeric parts are the same, compare the string parts.
2709
		if ($v == 0) {
2710
			return version_compare_string($cur_str, $rem_str);
2711
		}
2712
	}
2713
	return $v;
2714
}
2715
function process_alias_urltable($name, $type, $url, $freq, $forceupdate=false, $validateonly=false) {
2716
	global $g;
2717

    
2718
	if (!is_validaliasname($name) || !filter_var($url, FILTER_VALIDATE_URL)) {
2719
		return false;
2720
	}
2721

    
2722
	$urltable_prefix = "/var/db/aliastables/";
2723
	$urltable_filename = $urltable_prefix . basename($name) . ".txt";
2724
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2725

    
2726
	// Make the aliases directory if it doesn't exist
2727
	if (!file_exists($urltable_prefix)) {
2728
		mkdir($urltable_prefix);
2729
	} elseif (!is_dir($urltable_prefix)) {
2730
		unlink($urltable_prefix);
2731
		mkdir($urltable_prefix);
2732
	}
2733

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

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

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

    
2748
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2749
			if ($type == "urltable_ports") {
2750
				$parsed_contents = group_ports($parsed_contents, true);
2751
			}
2752
			if (is_array($parsed_contents)) {
2753
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2754
			} else {
2755
				touch($urltable_filename);
2756
			}
2757

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

    
2764
			unlink_if_exists($tmp_urltable_filename);
2765
		} else {
2766
			if (!$validateonly) {
2767
				touch($urltable_filename);
2768
			}
2769
			return false;
2770
		}
2771
		return true;
2772
	} else {
2773
		// File exists, and it doesn't need to be updated.
2774
		return -1;
2775
	}
2776
}
2777

    
2778
function get_include_contents($filename) {
2779
	if (is_file($filename)) {
2780
		ob_start();
2781
		include $filename;
2782
		$contents = ob_get_contents();
2783
		ob_end_clean();
2784
		return $contents;
2785
	}
2786
	return false;
2787
}
2788

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

    
2901
function get_country_name($country_code = "ALL") {
2902
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2903
		return "";
2904
	}
2905

    
2906
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2907
	$country_names_contents = file_get_contents($country_names_xml);
2908
	$country_names = xml2array($country_names_contents);
2909

    
2910
	if ($country_code == "ALL") {
2911
		$country_list = array();
2912
		foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2913
			$country_list[] = array(
2914
				"code" => $country['ISO_3166-1_Alpha-2_Code_element'],
2915
				"name" => ucwords(strtolower($country['ISO_3166-1_Country_name'])));
2916
		}
2917
		return $country_list;
2918
	}
2919

    
2920
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2921
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2922
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2923
		}
2924
	}
2925
	return "";
2926
}
2927

    
2928
/* Return the list of country codes to be used on CAs and certs */
2929
function get_cert_country_codes() {
2930
	$countries = get_country_name();
2931

    
2932
	$country_codes = array();
2933
	foreach ($countries as $country) {
2934
		$country_codes[$country['code']] = $country['code'];
2935
	}
2936
	ksort($country_codes);
2937

    
2938
	/* Preserve historical order: None, US, CA, other countries */
2939
	$first_items[''] = gettext("None");
2940
	$first_items['US'] = $country_codes['US'];
2941
	$first_items['CA'] = $country_codes['CA'];
2942
	unset($country_codes['US']);
2943
	unset($country_codes['CA']);
2944

    
2945
	return array_merge($first_items, $country_codes);
2946
}
2947

    
2948
/* sort by interface only, retain the original order of rules that apply to
2949
   the same interface */
2950
function filter_rules_sort() {
2951
	config_init_path('filter/rule');
2952
	$rules = config_get_path('filter/rule', []);
2953

    
2954
	/* mark each rule with the sequence number (to retain the order while sorting) */
2955
	for ($i = 0; isset($rules[$i]); $i++) {
2956
		$rules[$i]['seq'] =$i;
2957
	}
2958
	usort($rules, "filter_rules_compare");
2959

    
2960
	/* strip the sequence numbers again */
2961
	for ($i = 0; isset($rules[$i]); $i++) {
2962
		unset($rules[$i]['seq']);
2963
	}
2964

    
2965
	/* commit changes */
2966
	config_set_path('filter/rule', $rules);
2967
}
2968
function filter_rules_compare($a, $b) {
2969
	if (isset($a['floating']) && isset($b['floating'])) {
2970
		return $a['seq'] - $b['seq'];
2971
	} else if (isset($a['floating'])) {
2972
		return -1;
2973
	} else if (isset($b['floating'])) {
2974
		return 1;
2975
	} else if ($a['interface'] == $b['interface']) {
2976
		return $a['seq'] - $b['seq'];
2977
	} else {
2978
		return compare_interface_friendly_names($a['interface'], $b['interface']);
2979
	}
2980
}
2981

    
2982
function generate_ipv6_from_mac($mac) {
2983
	$elements = explode(":", $mac);
2984
	if (count($elements) <> 6) {
2985
		return false;
2986
	}
2987

    
2988
	$i = 0;
2989
	$ipv6 = "fe80::";
2990
	foreach ($elements as $byte) {
2991
		if ($i == 0) {
2992
			$hexadecimal = substr($byte, 1, 2);
2993
			$bitmap = base_convert($hexadecimal, 16, 2);
2994
			$bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT);
2995
			$bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3, 4);
2996
			$byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16);
2997
		}
2998
		$ipv6 .= $byte;
2999
		if ($i == 1) {
3000
			$ipv6 .= ":";
3001
		}
3002
		if ($i == 3) {
3003
			$ipv6 .= ":";
3004
		}
3005
		if ($i == 2) {
3006
			$ipv6 .= "ff:fe";
3007
		}
3008

    
3009
		$i++;
3010
	}
3011
	return $ipv6;
3012
}
3013

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

    
3040
}
3041

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

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

    
3081
	$pos = strpos($ignore_if, '_virtualip');
3082
	if ($pos !== false) {
3083
		$ignore_vip_id = substr($ignore_if, $pos+10);
3084
		$ignore_vip_if = substr($ignore_if, 0, $pos);
3085
	} else {
3086
		$ignore_vip_id = -1;
3087
		$ignore_vip_if = $ignore_if;
3088
	}
3089

    
3090
	$isipv6 = is_ipaddrv6($ipaddr);
3091

    
3092
	if ($isipv6) {
3093
		$ipaddr = text_to_compressed_ip6($ipaddr);
3094
	}
3095

    
3096
	if ($check_subnets) {
3097
		$cidrprefix = intval($cidrprefix);
3098
		if ($isipv6) {
3099
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
3100
				$cidrprefix = 128;
3101
			}
3102
		} else {
3103
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
3104
				$cidrprefix = 32;
3105
			}
3106
		}
3107
		$iflist = get_configured_interface_list();
3108
		foreach ($iflist as $if => $ifname) {
3109
			if ($ignore_if == $if) {
3110
				continue;
3111
			}
3112

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

    
3144
		foreach ($interface_list_ips as $if => $ilips) {
3145
			if ($ignore_if == $if) {
3146
				continue;
3147
			}
3148
			if (strcasecmp($ipaddr, $ilips) == 0) {
3149
				$where_entry = array();
3150
				$where_entry['if'] = $if;
3151
				$where_entry['ip_or_subnet'] = $ilips;
3152
				$where_configured[] = $where_entry;
3153
			}
3154
		}
3155
	}
3156

    
3157
	if ($check_localip) {
3158
		if (strcasecmp($ipaddr, text_to_compressed_ip6(config_get_path('l2tp/localip', ""))) == 0) {
3159
			$where_entry = array();
3160
			$where_entry['if'] = 'l2tp';
3161
			$where_entry['ip_or_subnet'] = config_get_path('l2tp/localip');
3162
			$where_configured[] = $where_entry;
3163
		}
3164
	}
3165

    
3166
	return $where_configured;
3167
}
3168

    
3169
/****f* pfsense-utils/pfSense_handle_custom_code
3170
 * NAME
3171
 *   pfSense_handle_custom_code
3172
 * INPUTS
3173
 *   directory name to process
3174
 * RESULT
3175
 *   globs the directory and includes the files
3176
 */
3177
function pfSense_handle_custom_code($src_dir) {
3178
	// Allow extending of the nat edit page and include custom input validation
3179
	if (is_dir("$src_dir")) {
3180
		$cf = glob($src_dir . "/*.inc");
3181
		foreach ($cf as $nf) {
3182
			if ($nf == "." || $nf == "..") {
3183
				continue;
3184
			}
3185
			// Include the extra handler
3186
			include_once("$nf");
3187
		}
3188
	}
3189
}
3190

    
3191
function set_language() {
3192
	global $g;
3193

    
3194
	$lang = "";
3195
	if (!empty(config_get_path('system/language'))) {
3196
		$lang = config_get_path('system/language');
3197
	} elseif (!empty(g_get('language'))) {
3198
		$lang = g_get('language');
3199
	}
3200
	$lang .= ".UTF-8";
3201

    
3202
	putenv("LANG={$lang}");
3203
	setlocale(LC_ALL, $lang);
3204
	textdomain("pfSense");
3205
	bindtextdomain("pfSense", "/usr/local/share/locale");
3206
	bind_textdomain_codeset("pfSense", $lang);
3207
}
3208

    
3209
function get_locale_list() {
3210
	$locales = array(
3211
		"bs" => gettext("Bosnian"),
3212
		"zh_CN" => gettext("Chinese"),
3213
		"zh_Hans_CN" => gettext("Chinese (Simplified, China)"),
3214
		"zh_Hans_HK" => gettext("Chinese (Simplified, Hong Kong SAR China)"),
3215
		"zh_Hant_TW" => gettext("Chinese (Traditional, Taiwan)"),
3216
		"nl_NL" => gettext("Dutch"),
3217
		"en_US" => gettext("English"),
3218
		"fr_FR" => gettext("French"),
3219
		"de_DE" => gettext("German (Germany)"),
3220
		"it_IT" => gettext("Italian"),
3221
		"ko_FR" => gettext("Korean"),
3222
		"nb_NO" => gettext("Norwegian Bokmål"),
3223
		"pl_PL" => gettext("Polish"),
3224
		"pt_PT" => gettext("Portuguese"),
3225
		"pt_BR" => gettext("Portuguese (Brazil)"),
3226
		"ru_RU" => gettext("Russian"),
3227
		"es_ES" => gettext("Spanish"),
3228
		"es_AR" => gettext("Spanish (Argentina)"),
3229
	);
3230

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

    
3235
	//asort($locales);
3236

    
3237
	return $locales;
3238
}
3239

    
3240
function return_hex_ipv4($ipv4) {
3241
	if (!is_ipaddrv4($ipv4)) {
3242
		return(false);
3243
	}
3244

    
3245
	/* we need the hex form of the interface IPv4 address */
3246
	$ip4arr = explode(".", $ipv4);
3247
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3248
}
3249

    
3250
function convert_ipv6_to_128bit($ipv6) {
3251
	if (!is_ipaddrv6($ipv6)) {
3252
		return(false);
3253
	}
3254

    
3255
	$ip6arr = array();
3256
	$ip6prefix = Net_IPv6::uncompress($ipv6);
3257
	$ip6arr = explode(":", $ip6prefix);
3258
	/* binary presentation of the prefix for all 128 bits. */
3259
	$ip6prefixbin = "";
3260
	foreach ($ip6arr as $element) {
3261
		$ip6prefixbin .= sprintf("%016b", hexdec($element));
3262
	}
3263
	return($ip6prefixbin);
3264
}
3265

    
3266
function convert_128bit_to_ipv6($ip6bin) {
3267
	if (strlen($ip6bin) <> 128) {
3268
		return(false);
3269
	}
3270

    
3271
	$ip6arr = array();
3272
	$ip6binarr = array();
3273
	$ip6binarr = str_split($ip6bin, 16);
3274
	foreach ($ip6binarr as $binpart) {
3275
		$ip6arr[] = dechex(bindec($binpart));
3276
	}
3277
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3278

    
3279
	return($ip6addr);
3280
}
3281

    
3282

    
3283
/* Returns the calculated bit length of the prefix delegation from the WAN interface */
3284
/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */
3285
/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */
3286
/* 6to4 is 16 bits, e.g. 65535 */
3287
function calculate_ipv6_delegation_length($if) {
3288
	$cfg = config_get_path("interfaces/{$if}");
3289
	if (!is_array($cfg)) {
3290
		return false;
3291
	}
3292

    
3293
	switch ($cfg['ipaddrv6']) {
3294
		case "6to4":
3295
			$pdlen = 16;
3296
			break;
3297
		case "6rd":
3298
			$rd6plen = explode("/", $cfg['prefix-6rd']);
3299
			$pdlen = (64 - ((int) $rd6plen[1] + (32 - (int) $cfg['prefix-6rd-v4plen'])));
3300
			break;
3301
		case "dhcp6":
3302
			$pdlen = $cfg['dhcp6-ia-pd-len'];
3303
			break;
3304
		default:
3305
			$pdlen = 0;
3306
			break;
3307
	}
3308
	return($pdlen);
3309
}
3310

    
3311
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3312
	/* convert zero-value prefix IPv6 addresses with IPv4 mapping to hex
3313
	 * see https://redmine.pfsense.org/issues/12440 */
3314
	$suffix_list = explode(':', $suffix);
3315
	if (is_ipaddrv4($suffix_list[count($suffix_list) - 1])) {
3316
		$hexsuffix = dechex(ip2long($suffix_list[count($suffix_list) - 1]));
3317
		$suffix_list[count($suffix_list) - 1] = substr($hexsuffix, 0, 4);
3318
		$suffix_list[] = substr($hexsuffix, 4, 8);
3319
		$suffix = implode(':', $suffix_list);
3320
	}
3321
	$prefix = Net_IPv6::uncompress($prefix, true);
3322
	$suffix = Net_IPv6::uncompress($suffix, true);
3323

    
3324
	/*
3325
	 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
3326
	 *                ^^^^ ^
3327
	 *                |||| \-> 64
3328
	 *                |||\---> 63, 62, 61, 60
3329
	 *                ||\----> 56
3330
	 *                |\-----> 52
3331
	 *                \------> 48
3332
	 */
3333

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

    
3365
	return text_to_compressed_ip6(substr($prefix, 0, $prefix_len) .
3366
	    substr($suffix, $prefix_len));
3367
}
3368

    
3369
function dhcpv6_pd_str_help($pdlen) {
3370
	$result = '';
3371

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

    
3404
	return $result;
3405
}
3406

    
3407
function huawei_rssi_to_string($rssi) {
3408
	$dbm = array();
3409
	$i = 0;
3410
	$dbstart = -113;
3411
	while ($i < 32) {
3412
		$dbm[$i] = $dbstart + ($i * 2);
3413
		$i++;
3414
	}
3415
	$percent = round(($rssi / 31) * 100);
3416
	$string = "rssi:{$rssi} level:{$dbm[$rssi]}dBm percent:{$percent}%";
3417
	return $string;
3418
}
3419

    
3420
function huawei_mode_to_string($mode, $submode) {
3421
	$modes[0] = gettext("None");
3422
	$modes[1] = "AMPS";
3423
	$modes[2] = "CDMA";
3424
	$modes[3] = "GSM/GPRS";
3425
	$modes[4] = "HDR";
3426
	$modes[5] = "WCDMA";
3427
	$modes[6] = "GPS";
3428

    
3429
	$submodes[0] = gettext("No Service");
3430
	$submodes[1] = "GSM";
3431
	$submodes[2] = "GPRS";
3432
	$submodes[3] = "EDGE";
3433
	$submodes[4] = "WCDMA";
3434
	$submodes[5] = "HSDPA";
3435
	$submodes[6] = "HSUPA";
3436
	$submodes[7] = "HSDPA+HSUPA";
3437
	$submodes[8] = "TD-SCDMA";
3438
	$submodes[9] = "HSPA+";
3439
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3440
	return $string;
3441
}
3442

    
3443
function huawei_service_to_string($state) {
3444
	$modes[0] = gettext("No Service");
3445
	$modes[1] = gettext("Restricted Service");
3446
	$modes[2] = gettext("Valid Service");
3447
	$modes[3] = gettext("Restricted Regional Service");
3448
	$modes[4] = gettext("Powersaving Service");
3449
	$modes[255] = gettext("Unknown Service");
3450
	$string = $modes[$state];
3451
	return $string;
3452
}
3453

    
3454
function huawei_simstate_to_string($state) {
3455
	$modes[0] = gettext("Invalid SIM/locked State");
3456
	$modes[1] = gettext("Valid SIM State");
3457
	$modes[2] = gettext("Invalid SIM CS State");
3458
	$modes[3] = gettext("Invalid SIM PS State");
3459
	$modes[4] = gettext("Invalid SIM CS/PS State");
3460
	$modes[255] = gettext("Missing SIM State");
3461
	$string = $modes[$state];
3462
	return $string;
3463
}
3464

    
3465
function zte_rssi_to_string($rssi) {
3466
	return huawei_rssi_to_string($rssi);
3467
}
3468

    
3469
function zte_mode_to_string($mode, $submode) {
3470
	$modes[0] = gettext("No Service");
3471
	$modes[1] = gettext("Limited Service");
3472
	$modes[2] = "GPRS";
3473
	$modes[3] = "GSM";
3474
	$modes[4] = "UMTS";
3475
	$modes[5] = "EDGE";
3476
	$modes[6] = "HSDPA";
3477

    
3478
	$submodes[0] = "CS_ONLY";
3479
	$submodes[1] = "PS_ONLY";
3480
	$submodes[2] = "CS_PS";
3481
	$submodes[3] = "CAMPED";
3482
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3483
	return $string;
3484
}
3485

    
3486
function zte_service_to_string($service) {
3487
	$modes[0] = gettext("Initializing Service");
3488
	$modes[1] = gettext("Network Lock error Service");
3489
	$modes[2] = gettext("Network Locked Service");
3490
	$modes[3] = gettext("Unlocked or correct MCC/MNC Service");
3491
	$string = $modes[$service];
3492
	return $string;
3493
}
3494

    
3495
function zte_simstate_to_string($state) {
3496
	$modes[0] = gettext("No action State");
3497
	$modes[1] = gettext("Network lock State");
3498
	$modes[2] = gettext("(U)SIM card lock State");
3499
	$modes[3] = gettext("Network Lock and (U)SIM card Lock State");
3500
	$string = $modes[$state];
3501
	return $string;
3502
}
3503

    
3504
function get_configured_pppoe_server_interfaces() {
3505
	$iflist = array();
3506
	foreach (config_get_path('pppoes/pppoe', []) as $pppoe) {
3507
		if ($pppoe['mode'] == "server") {
3508
			$int = "poes". $pppoe['pppoeid'];
3509
			$iflist[$int] = strtoupper($int);
3510
		}
3511
	}
3512
	return $iflist;
3513
}
3514

    
3515
function get_pppoes_child_interfaces($ifpattern) {
3516
	$if_arr = array();
3517
	if ($ifpattern == "") {
3518
		return;
3519
	}
3520

    
3521
	exec("/sbin/ifconfig", $out, $ret);
3522
	foreach ($out as $line) {
3523
		if (preg_match("/^({$ifpattern}-[0-9]+):/i", $line, $match)) {
3524
			$if_arr[] = $match[1];
3525
		}
3526
	}
3527
	return $if_arr;
3528

    
3529
}
3530

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

    
3565
// Convert IPv6 addresses to lower case
3566
function addrtolower($ip) {
3567
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3568
		return(strtolower($ip));
3569
	} else {
3570
		return($ip);
3571
	}
3572
}
3573

    
3574
function compare_by_name($a, $b) {
3575
	return strcasecmp($a['name'], $b['name']);
3576
}
3577

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

    
3609
/****f* pfsense-utils/send_download_data
3610
 * NAME
3611
 *   send_download_data - Send content to a user's browser as a file to download
3612
 * INPUTS
3613
 *   $type        : The type of download, either 'data' to send the contents of
3614
 *                    a variable or 'file' to send the contents of a file on the
3615
 *                    filesystem.
3616
 *   $content     : For 'data' type, the content to send the user. For 'file'
3617
 *                    type, the full path to the file to send.
3618
 *   $userfilename: The filename presented to the user when downloading. For
3619
 *                    'file' type, this may be omitted and the basename of
3620
 *                    $content will be used instead.
3621
 *   $contenttype : MIME content type of the data. Default "application/octet-stream"
3622
 * RESULT
3623
 *   Sends the data to the browser as a file to download.
3624
 ******/
3625

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

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

    
3633
	/* Cannot determine the filename, so bail. */
3634
	if (empty($name)) {
3635
		exit;
3636
	}
3637

    
3638
	/* Send basic download headers */
3639
	header("Content-Type: {$contenttype}");
3640
	header("Content-Length: {$size}");
3641
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3642

    
3643
	/* Send cache headers */
3644
	if (isset($_SERVER['HTTPS'])) {
3645
		header('Pragma: ');
3646
		header('Cache-Control: ');
3647
	} else {
3648
		header("Pragma: private");
3649
		header("Cache-Control: private, must-revalidate");
3650
	}
3651

    
3652
	/* Ensure output buffering is off so PHP does not consume
3653
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3654
	while (ob_get_level()) {
3655
		@ob_end_clean();
3656
	}
3657

    
3658
	/* Send the data to the user */
3659
	if ($type == 'file') {
3660
		readfile($content);
3661
	} else {
3662
		echo $content;
3663
	}
3664

    
3665
	/* Flush any remaining output buffer */
3666
	@ob_end_flush();
3667
	exit;
3668
}
3669

    
3670
function get_pf_reserved(?string $name = '', bool $check_ifname = true):bool|array {
3671
	global $pf_reserved_keywords, $reserved_table_names, $FilterIflist;
3672

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

    
3708
	return (empty($name) ? $reserved : in_array(strtoupper($name), $reserved));
3709
}
3710

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

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

    
3752
/*
3753
 * return the time difference in seconds.microseconds(6 digits) format between 2 DateTime objects
3754
 * @param DateTime $date1
3755
 * @param DateTime $date2
3756
 */
3757

    
3758
function date_mdiff($date1, $date2){
3759
	return number_format(abs((float)$date1->format("U.u") - (float)$date2->format("U.u")), 6);
3760
}
(39-39/61)