Project

General

Profile

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

    
24
require_once("config.inc");
25
require_once("config.lib.inc");
26

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

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

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

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

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

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

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

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

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

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

    
165
	if (is_array($cssfiles)) {
166
		arsort($cssfiles);
167
		$usrcss = $pfscss = $betacss = array();
168

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

    
183
		$css = array_merge($pfscss, $betacss, $usrcss);
184

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

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

    
204
	$csslist = get_css_files();
205

    
206
	if (!isset($csslist[$value])) {
207
		$value = "pfSense.css";
208
	}
209

    
210
	$section->addInput(new Form_Select(
211
		'webguicss',
212
		'Theme',
213
		$value,
214
		$csslist
215
	))->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>');
216
}
217
function validate_webguicss_field(&$input_errors, $value) {
218
	$csslist = get_css_files();
219
	if (!isset($csslist[$value])) {
220
		$input_errors[] = gettext("The submitted Theme could not be found. Pick a different theme.");
221
	}
222
}
223

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

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

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

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

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

    
285
	if (((int) $value < 1) || ((int) $value > 6)) {
286
		$value = 2;
287
	}
288

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

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

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

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

    
333
	$group = new Form_Group('Associated Panels Show/Hide');
334

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

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

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

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

    
363
	$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.');
364

    
365
	$section->add($group);
366
}
367

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

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

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

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

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

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

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

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

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

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

    
488
	if ($has_created_time || $has_updated_time) {
489
		$section = new Form_Section('Rule Information');
490

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

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

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

    
518
		$form->add($section);
519
	}
520
}
521

    
522
function hardware_offloading_applyflags($iface) {
523
	$flags_on = 0;
524
	$flags_off = 0;
525
	$options = get_interface_addresses($iface);
526

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

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

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

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

    
576
/****f* pfsense-utils/enable_hardware_offloading
577
 * NAME
578
 *   enable_hardware_offloading - Enable a NIC's supported hardware features.
579
 * INPUTS
580
 *   $interface	- string containing the physical interface to work on.
581
 * RESULT
582
 *   null
583
 * NOTES
584
 *   This function only supports the fxp driver's loadable microcode.
585
 ******/
586
function enable_hardware_offloading($interface) {
587
	$int = get_real_interface($interface);
588
	if (empty($int)) {
589
		return;
590
	}
591

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

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

    
613
/****f* pfsense-utils/is_alias_inuse
614
 * NAME
615
 *   checks to see if an alias is currently in use by a rule
616
 * INPUTS
617
 *
618
 * RESULT
619
 *   true or false
620
 * NOTES
621
 *
622
 ******/
623
function is_alias_inuse($alias) {
624
	if ($alias == "") {
625
		return false;
626
	}
627
	/* loop through firewall rules looking for alias in use */
628
	foreach (config_get_path('filter/rule', []) as $rule) {
629
		foreach (['source', 'destination'] as $origin) {
630
			if (array_get_path($rule, "{$origin}/address") == $alias) {
631
				return true;
632
			}
633
		}
634
	}
635
	/* loop through nat rules looking for alias in use */
636
	foreach (config_get_path('nat/rule', []) as $rule) {
637
		foreach (['target', 'source/address', 'destination/address'] as $property) {
638
			if (array_get_path($rule, $property) == $alias) {
639
				return true;
640
			}
641
		}
642
	}
643
	return false;
644
}
645

    
646
/****f* pfsense-utils/is_schedule_inuse
647
 * NAME
648
 *   checks to see if a schedule is currently in use by a rule
649
 * INPUTS
650
 *
651
 * RESULT
652
 *   true or false
653
 * NOTES
654
 *
655
 ******/
656
function is_schedule_inuse($schedule) {
657
	if ($schedule == "") {
658
		return false;
659
	}
660
	/* loop through firewall rules looking for schedule in use */
661
	foreach (config_get_path('filter/rule', []) as $rule) {
662
		if ($rule['sched'] == $schedule) {
663
			return true;
664
		}
665
	}
666
	return false;
667
}
668

    
669
/****f* pfsense-utils/setup_microcode
670
 * NAME
671
 *   enumerates all interfaces and calls enable_hardware_offloading which
672
 *   enables a NIC's supported hardware features.
673
 * INPUTS
674
 *
675
 * RESULT
676
 *   null
677
 * NOTES
678
 *   This function only supports the fxp driver's loadable microcode.
679
 ******/
680
function setup_microcode() {
681

    
682
	/* if list */
683
	$iflist = get_configured_interface_list(true);
684
	foreach (array_keys($iflist) as $if) {
685
		enable_hardware_offloading($if);
686
	}
687
	unset($iflist);
688
}
689

    
690
/****f* pfsense-utils/get_carp_status
691
 * NAME
692
 *   get_carp_status - Return whether CARP is enabled or disabled.
693
 * RESULT
694
 *   boolean	- true if CARP is enabled, false if otherwise.
695
 ******/
696
function get_carp_status() {
697
	/* grab the current status of carp */
698
	$status = get_single_sysctl('net.inet.carp.allow');
699
	return (intval($status) > 0);
700
}
701

    
702
/**
703
 * Enable or disable CARP by toggling net.inet.carp.allow if necessary, enabling
704
 * only if CARP VIPs are configured
705
 */
706
function enable_carp(bool $enable=true) {
707
	$carp_enabled = get_carp_status();
708
	$carplist = get_configured_vip_list('all', VIP_CARP);
709
	if (($enable != $carp_enabled) && (!$enable || count($carplist) > 0)) {
710
		set_single_sysctl("net.inet.carp.allow", $enable?"1":"0");
711
	}
712
}
713
	
714
/*
715
 * convert_ip_to_network_format($ip, $subnet): converts an ip address to network form
716
 */
717
function convert_ip_to_network_format($ip, $subnet) {
718
	$ipsplit = explode('.', $ip);
719
	$string = $ipsplit[0] . "." . $ipsplit[1] . "." . $ipsplit[2] . ".0/" . $subnet;
720
	return $string;
721
}
722

    
723
/*
724
 * get_carp_interface_status($carpid): returns the status of a carp uniqid
725
 */
726
function get_carp_interface_status($carpid) {
727

    
728
	$carpiface = get_configured_vip_interface($carpid);
729
	if ($carpiface == NULL)
730
		return "";
731
	$interface = get_real_interface($carpiface);
732
	if ($interface == NULL)
733
		return "";
734
	$vip = get_configured_vip($carpid);
735
	if ($vip == NULL || !isset($vip['vhid']))
736
		return "";
737

    
738
	$vhid = $vip['vhid'];
739
	$carp_query = [];
740
	exec("/sbin/ifconfig {$interface} | /usr/bin/grep \"carp:.* vhid {$vhid} \"", $carp_query);
741
	foreach ($carp_query as $int) {
742
		if (stripos($int, "MASTER"))
743
			return "MASTER";
744
		elseif (stripos($int, "BACKUP"))
745
			return "BACKUP";
746
		elseif (stripos($int, "INIT"))
747
			return "INIT";
748
	}
749

    
750
	return "";
751
}
752

    
753
function get_carp_bind_status($interface) {
754
	$carpstatus = get_carp_interface_status($interface);
755
	if (!empty($carpstatus)) {
756
		return $carpstatus;
757
	} else {
758
		foreach (config_get_path('virtualip/vip', []) as $vip) {
759
			if ($interface == "_vip{$vip['uniqid']}") { 
760
				return get_carp_interface_status($vip['interface']);
761
			}
762
		}
763
	}
764
}
765

    
766
/*
767
 * Return true if the CARP status of at least one interface of a captive portal zone is in backup mode
768
 * This function return false if CARP is not enabled on any interface of the captive portal zone
769
 */
770
function captiveportal_ha_is_node_in_backup_mode($cpzone) {
771
	$cpinterfaces = explode(",", config_get_path("captiveportal/{$cpzone}/interface", ""));
772

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

    
785
/****f* pfsense-utils/WakeOnLan
786
 * NAME
787
 *   WakeOnLan - Wake a machine up using the wake on lan format/protocol
788
 * RESULT
789
 *   true/false - true if the operation was successful
790
 ******/
791
function WakeOnLan($addr, $mac) {
792
	$addr_byte = explode(':', $mac);
793
	$hw_addr = '';
794

    
795
	for ($a = 0; $a < 6; $a++) {
796
		$hw_addr .= chr(hexdec($addr_byte[$a]));
797
	}
798

    
799
	$msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255);
800

    
801
	for ($a = 1; $a <= 16; $a++) {
802
		$msg .= $hw_addr;
803
	}
804

    
805
	// send it to the broadcast address using UDP
806
	$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
807
	if ($s == false) {
808
		log_error(gettext("Error creating socket!"));
809
		log_error(sprintf(gettext("Error code is '%1\$s' - %2\$s"), socket_last_error(), socket_strerror(socket_last_error())));
810
	} else {
811
		// setting a broadcast option to socket:
812
		$opt_ret = socket_set_option($s, 1, 6, TRUE);
813
		if ($opt_ret < 0) {
814
			log_error(sprintf(gettext("setsockopt() failed, error: %s"),
815
							  socket_strerror(socket_last_error($s))));
816
		}
817
		$e = socket_sendto($s, $msg, strlen($msg), 0, $addr, 2050);
818
		socket_close($s);
819
		log_error(sprintf(gettext('Magic Packet sent (%1$s) to (%2$s) MAC=%3$s'), $e, $addr, $mac));
820
		return true;
821
	}
822

    
823
	return false;
824
}
825

    
826
/*
827
 * reverse_strrchr($haystack, $needle):  Return everything in $haystack up to the *last* instance of $needle.
828
 *					 Useful for finding paths and stripping file extensions.
829
 */
830
function reverse_strrchr($haystack, $needle) {
831
	if (!is_string($haystack)) {
832
		return;
833
	}
834
	return strrpos($haystack, $needle) ? substr($haystack, 0, strrpos($haystack, $needle) +1) : false;
835
}
836

    
837
/*
838
 *  backup_config_section($section): returns as an xml file string of
839
 *                                   the configuration section
840
 */
841
function backup_config_section($section_name) {
842
	$new_section = config_get_path($section_name, []);
843
	/* generate configuration XML */
844
	$xmlconfig = dump_xml_config($new_section, $section_name);
845
	$xmlconfig = str_replace("<?xml version=\"1.0\"?>", "", $xmlconfig);
846
	return $xmlconfig;
847
}
848

    
849
/*
850
 *  restore_config_section($section_name, new_contents): restore a configuration section,
851
 *                                                  and write the configuration out
852
 *                                                  to disk/cf.
853
 */
854
function restore_config_section($section_name, $new_contents) {
855
	global $g;
856
	$fout = fopen("{$g['tmp_path']}/tmpxml", "w");
857
	fwrite($fout, $new_contents);
858
	fclose($fout);
859

    
860
	$xml = parse_xml_config(g_get('tmp_path') . "/tmpxml", null);
861
	if ($xml['pfsense']) {
862
		$xml = $xml['pfsense'];
863
	}
864
	if ($xml[$section_name]) {
865
		$section_xml = $xml[$section_name];
866
	} else {
867
		$section_xml = -1;
868
	}
869

    
870
	@unlink(g_get('tmp_path') . "/tmpxml");
871
	if ($section_xml === -1) {
872
		return false;
873
	}
874

    
875
	/* Save current pkg repo to re-add on new config */
876
	unset($pkg_repo_conf_path);
877
	if ($section_name == "system" &&
878
	    config_get_path('system/pkg_repo_conf_path')) {
879
		$pkg_repo_conf_path = config_get_path('system/pkg_repo_conf_path');
880
	}
881

    
882
	config_set_path($section_name, $section_xml);
883
	if (file_exists("{$g['tmp_path']}/config.cache")) {
884
		unlink("{$g['tmp_path']}/config.cache");
885
	}
886

    
887
	/* Restore previously pkg repo configured */
888
	if ($section_name == "system") {
889
		if (isset($pkg_repo_conf_path)) {
890
			config_set_path('system/pkg_repo_conf_path', $pkg_repo_conf_path);
891
		} elseif (config_get_path('system/pkg_repo_conf_path')) {
892
			config_del_path(('system/pkg_repo_conf_path'));
893
		}
894
	}
895

    
896
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
897
	disable_security_checks();
898
	return true;
899
}
900

    
901
/*
902
 *  merge_config_section($section_name, new_contents):   restore a configuration section,
903
 *                                                  and write the configuration out
904
 *                                                  to disk/cf.  But preserve the prior
905
 * 													structure if needed
906
 */
907
function merge_config_section($section_name, $new_contents) {
908
	$fname = get_tmp_file();
909
	$fout = fopen($fname, "w");
910
	fwrite($fout, $new_contents);
911
	fclose($fout);
912
	$section_xml = parse_xml_config($fname, $section_name);
913
	config_set_path($section_name, $section_xml);
914
	unlink($fname);
915
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
916
	disable_security_checks();
917
	return;
918
}
919

    
920
/*
921
 * rmdir_recursive($path, $follow_links=false)
922
 * Recursively remove a directory tree (rm -rf path)
923
 * This is for directories _only_
924
 */
925
function rmdir_recursive($path, $follow_links=false) {
926
	$to_do = glob($path);
927
	if (!is_array($to_do)) {
928
		$to_do = array($to_do);
929
	}
930
	foreach ($to_do as $workingdir) { // Handle wildcards by foreaching.
931
		if (file_exists($workingdir)) {
932
			if (is_dir($workingdir)) {
933
				$dir = opendir($workingdir);
934
				while ($entry = readdir($dir)) {
935
					if (is_file("$workingdir/$entry") || ((!$follow_links) && is_link("$workingdir/$entry"))) {
936
						unlink("$workingdir/$entry");
937
					} elseif (is_dir("$workingdir/$entry") && $entry != '.' && $entry != '..') {
938
						rmdir_recursive("$workingdir/$entry");
939
					}
940
				}
941
				closedir($dir);
942
				rmdir($workingdir);
943
			} elseif (is_file($workingdir)) {
944
				unlink($workingdir);
945
			}
946
		}
947
	}
948
	return;
949
}
950

    
951
/*
952
 * host_firmware_version(): Return the versions used in this install
953
 */
954
function host_firmware_version() {
955
	global $g;
956

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

    
959
	return array(
960
		"firmware" => array("version" => g_get('product_version')),
961
		"kernel"   => array("version" => $os_version),
962
		"base"     => array("version" => $os_version),
963
		"platform" => g_get('product_label'),
964
		"config_version" => config_get_path('version')
965
	);
966
}
967

    
968
function get_disk_info() {
969
	$diskout = "";
970
	exec("/bin/df -h | /usr/bin/grep -w '/' | /usr/bin/awk '{ print $2, $3, $4, $5 }'", $diskout);
971
	return explode(' ', $diskout[0]);
972
}
973

    
974
/****f* pfsense-utils/strncpy
975
 * NAME
976
 *   strncpy - copy strings
977
 * INPUTS
978
 *   &$dst, $src, $length
979
 * RESULT
980
 *   none
981
 ******/
982
function strncpy(&$dst, $src, $length) {
983
	if (strlen($src) > $length) {
984
		$dst = substr($src, 0, $length);
985
	} else {
986
		$dst = $src;
987
	}
988
}
989

    
990
/****f* pfsense-utils/reload_interfaces_sync
991
 * NAME
992
 *   reload_interfaces - reload all interfaces
993
 * INPUTS
994
 *   none
995
 * RESULT
996
 *   none
997
 ******/
998
function reload_interfaces_sync() {
999
	global $config, $g;
1000

    
1001
	if (g_get('debug')) {
1002
		log_error(gettext("reload_interfaces_sync() is starting."));
1003
	}
1004

    
1005
	/* parse config.xml again */
1006
	$config = parse_config(true);
1007

    
1008
	/* enable routing */
1009
	system_routing_enable();
1010
	if (g_get('debug')) {
1011
		log_error(gettext("Enabling system routing"));
1012
	}
1013

    
1014
	if (g_get('debug')) {
1015
		log_error(gettext("Cleaning up Interfaces"));
1016
	}
1017

    
1018
	/* set up interfaces */
1019
	interfaces_configure();
1020
}
1021

    
1022
/****f* pfsense-utils/reload_all
1023
 * NAME
1024
 *   reload_all - triggers a reload of all settings
1025
 *   * INPUTS
1026
 *   none
1027
 * RESULT
1028
 *   none
1029
 ******/
1030
function reload_all() {
1031
	send_event("service reload all");
1032
}
1033

    
1034
/****f* pfsense-utils/reload_interfaces
1035
 * NAME
1036
 *   reload_interfaces - triggers a reload of all interfaces
1037
 * INPUTS
1038
 *   none
1039
 * RESULT
1040
 *   none
1041
 ******/
1042
function reload_interfaces() {
1043
	send_event("interface all reload");
1044
}
1045

    
1046
/****f* pfsense-utils/reload_all_sync
1047
 * NAME
1048
 *   reload_all - reload all settings
1049
 *   * INPUTS
1050
 *   none
1051
 * RESULT
1052
 *   none
1053
 ******/
1054
function reload_all_sync() {
1055
	global $config;
1056

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

    
1060
	/* set up our timezone */
1061
	system_timezone_configure();
1062

    
1063
	/* set up our hostname */
1064
	system_hostname_configure();
1065

    
1066
	/* make hosts file */
1067
	system_hosts_generate();
1068

    
1069
	/* generate resolv.conf */
1070
	system_resolvconf_generate();
1071

    
1072
	/* enable routing */
1073
	system_routing_enable();
1074

    
1075
	/* set up interfaces */
1076
	interfaces_configure();
1077

    
1078
	/* start dyndns service */
1079
	services_dyndns_configure();
1080

    
1081
	/* configure cron service */
1082
	configure_cron();
1083

    
1084
	/* start the NTP client */
1085
	system_ntp_configure();
1086

    
1087
	/* sync pw database */
1088
	unlink_if_exists("/etc/spwd.db.tmp");
1089
	mwexec("/usr/sbin/pwd_mkdb -d /etc/ /etc/master.passwd");
1090

    
1091
	/* restart sshd */
1092
	send_event("service restart sshd");
1093

    
1094
	/* restart webConfigurator if needed */
1095
	send_event("service restart webgui");
1096
}
1097

    
1098
function load_loader_conf($loader_conf = NULL, $local = false) {
1099

    
1100
	if ($loader_conf == NULL) {
1101
		return (NULL);
1102
	}
1103
	if (file_exists($loader_conf)) {
1104
		$input = file_get_contents($loader_conf);
1105
	} else {
1106
		$input = "";
1107
	}
1108

    
1109
	$input_split = explode("\n", $input);
1110

    
1111
	/*
1112
	 * Loop through and only add lines that are not empty and not
1113
	 * managed by us.
1114
	 */
1115
	$data = array();
1116
	/* These values should be removed from loader.conf and loader.conf.local
1117
	 * As they will be replaced when necessary. */
1118
	$remove = array(
1119
	    "hint.cordbuc.0",
1120
	    "hint.e6000sw.0",
1121
	    "hint.gpioled",
1122
	    "hint.mdio.0.at",
1123
	    "hint-model.",
1124
	    "hw.e6000sw.default_disabled",
1125
	    "hw.hn.vf_transparent",
1126
	    "hw.hn.use_if_start",
1127
	    "hw.usb.no_pf",
1128
	    "net.pf.request_maxcount",
1129
	    "vm.pmap.pti",
1130
	);
1131
	if (!$local) {
1132
		/* These values should only be filtered in loader.conf, not .local */
1133
		$remove = array_merge($remove, array(
1134
		    "autoboot_delay",
1135
		    "boot_multicons",
1136
		    "boot_serial",
1137
		    "comconsole_speed",
1138
		    "comconsole_port",
1139
		    "console",
1140
		    "debug.ddb.capture.bufsize",
1141
		    "hint.uart.0.flags",
1142
		    "hint.uart.1.flags",
1143
		    "net.link.ifqmaxlen"
1144
		));
1145
	}
1146
	foreach ($input_split as $line) {
1147
		if (empty($line)) {
1148
			continue;
1149
		}
1150
		$skip = false;
1151
		$name = explode('=', $line, 2)[0];
1152
		foreach($remove as $rkey) {
1153
			if (strncasecmp(trim($name), $rkey, strlen($rkey)) == 0) {
1154
				$skip = true;
1155
				break;
1156
			}
1157
		}
1158
		if (!$skip) {
1159
			$data[] = $line;
1160
		}
1161
	}
1162

    
1163
	return ($data);
1164
}
1165

    
1166
function setup_loader_settings($path = "", $upgrade = false) {
1167
	global $g;
1168

    
1169
	$boot_config_file = "{$path}/boot.config";
1170
	$loader_conf_file = "{$path}/boot/loader.conf";
1171

    
1172
	$serialspeed = (config_get_path('system/serialspeed', 115200));
1173

    
1174
	$vga_only = false;
1175
	$serial_only = false;
1176
	$specific_platform = system_identify_specific_platform();
1177
	$video_console_type = (get_single_sysctl("machdep.bootmethod") == "UEFI") ? "efi" : "vidconsole";
1178
	if ($specific_platform['name'] == '1540' ||
1179
	    $specific_platform['name'] == '1541') {
1180
		$vga_only = true;
1181
	} elseif ($specific_platform['name'] == 'Turbot Dual-E') {
1182
		$g['primaryconsole_force'] = "video";
1183
	} elseif ($specific_platform['name'] == 'RCC-VE' ||
1184
	    $specific_platform['name'] == 'RCC' ||
1185
	    $specific_platform['name'] == 'SG-2220' ||
1186
	    $specific_platform['name'] == 'apu2') {
1187
		$serial_only = true;
1188
	}
1189

    
1190
	/* Serial console - write out /boot.config */
1191
	if (file_exists($boot_config_file)) {
1192
		$boot_config = file_get_contents($boot_config_file);
1193
	} else {
1194
		$boot_config = "";
1195
	}
1196
	$boot_config_split = explode("\n", $boot_config);
1197
	$data = array();
1198
	foreach ($boot_config_split as $bcs) {
1199
		/* Ignore -S, -D and -h lines now */
1200
		if (!empty($bcs) && !strstr($bcs, "-S") &&
1201
		    !strstr($bcs, "-D") && !strstr($bcs, "-h")) {
1202
			$data[] = $bcs;
1203
		}
1204
	}
1205
	if ($serial_only === true) {
1206
		$data[] = "-S{$serialspeed} -h";
1207
	} elseif (is_serial_enabled()) {
1208
		$data[] = "-S{$serialspeed} -D";
1209
	}
1210

    
1211
	if (empty($data)) {
1212
		@unlink($boot_config_file);
1213
	} else {
1214
		safe_write_file($boot_config_file, $data);
1215
	}
1216
	unset($data, $boot_config, $boot_config_file, $boot_config_split);
1217

    
1218
	/* Serial console - write out /boot/loader.conf */
1219
	if ($upgrade) {
1220
		system("echo \"Reading {$loader_conf_file}...\" >> /conf/upgrade_log.txt");
1221
	}
1222

    
1223
	$data = load_loader_conf($loader_conf_file, false);
1224
	if ($serial_only === true) {
1225
		$data[] = 'boot_serial="YES"';
1226
		$data[] = 'console="comconsole"';
1227
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1228
	} elseif ($vga_only === true) {
1229
		if ($video_console_type == 'efi') {
1230
			$data[] = 'boot_serial="NO"';
1231
		}
1232
		$data[] = "console=\"{$video_console_type}\"";
1233
	} elseif (is_serial_enabled()) {
1234
		$data[] = 'boot_multicons="YES"';
1235
		$primaryconsole = isset($g['primaryconsole_force']) ?
1236
		    g_get('primaryconsole_force') :
1237
		    config_get_path('system/primaryconsole');
1238
		switch ($primaryconsole) {
1239
			case "video":
1240
				if ($video_console_type == 'efi') {
1241
					$data[] = 'boot_serial="NO"';
1242
				}
1243
				$data[] = "console=\"{$video_console_type},comconsole\"";
1244
				break;
1245
			case "serial":
1246
			default:
1247
				$data[] = 'boot_serial="YES"';
1248
				$data[] = "console=\"comconsole,{$video_console_type}\"";
1249
		}
1250
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1251
	} elseif ($video_console_type == 'efi') {
1252
		$data[] = 'boot_serial="NO"';
1253
	}
1254

    
1255
	if ($specific_platform['name'] == 'RCC-VE' ||
1256
	    $specific_platform['name'] == 'RCC' ||
1257
	    $specific_platform['name'] == 'SG-2220') {
1258
		$data[] = 'comconsole_port="0x2F8"';
1259
		$data[] = 'hint.uart.0.flags="0x00"';
1260
		$data[] = 'hint.uart.1.flags="0x10"';
1261
	}
1262
	$data[] = 'autoboot_delay="3"';
1263
	if (config_path_enabled('system','pti_disabled')) {
1264
		$data[] = 'vm.pmap.pti="0"';
1265
	}
1266

    
1267
	/* Enable ALTQ support for hnX NICs. */
1268
	if (config_path_enabled('system','hn_altq_enable')) {
1269
		$data[] = 'hw.hn.vf_transparent="0"';
1270
		$data[] = 'hw.hn.use_if_start="1"';
1271
	}
1272

    
1273
	/* Set maximum send queue length. */
1274
	$data[] = 'net.link.ifqmaxlen="128"';
1275

    
1276
	safe_write_file($loader_conf_file, $data);
1277

    
1278
	/* Filter loader.conf.local to avoid duplicate settings. */
1279
	$loader_conf_file = "{$path}/boot/loader.conf.local";
1280
	$data = load_loader_conf($loader_conf_file, true);
1281
	if (empty($data)) {
1282
		@unlink($loader_conf_file);
1283
	} else {
1284
		safe_write_file($loader_conf_file, $data);
1285
	}
1286

    
1287
}
1288

    
1289
function console_configure($when = "save", $path = "") {
1290
	$ttys_file = "{$path}/etc/ttys";
1291

    
1292
	/* Update the loader settings. */
1293
	setup_loader_settings($path, ($when == "upgrade"));
1294

    
1295
	$ttys = file_get_contents($ttys_file);
1296
	$ttys_split = explode("\n", $ttys);
1297

    
1298
	$data = array();
1299

    
1300
	$on_off = (is_serial_enabled() ? 'onifconsole' : 'off');
1301

    
1302
	if (config_path_enabled('system','disableconsolemenu')) {
1303
		$console_type = 'Pc';
1304
		$serial_type = '3wire';
1305
	} else {
1306
		$console_type = 'al.Pc';
1307
		$serial_type = 'al.3wire';
1308
	}
1309

    
1310
	$console_line = "console\tnone\t\t\t\tunknown\toff\tsecure";
1311
	$virt_line =
1312
	    "\"/usr/libexec/getty {$console_type}\"\txterm\tonifexists secure";
1313
	$ttyu_line =
1314
	    "\"/usr/libexec/getty {$serial_type}\"\tvt100\t{$on_off}\tsecure";
1315

    
1316
	$found = array();
1317

    
1318
	foreach ($ttys_split as $tty) {
1319
		/* Ignore blank lines */
1320
		if (empty($tty)) {
1321
			continue;
1322
		}
1323

    
1324
		if (stristr($tty, "ttyv0")) {
1325
			$found['ttyv0'] = 1;
1326
			$data[] = "ttyv0\t{$virt_line}";
1327
		} elseif (stristr($tty, "xc0")) {
1328
			$found['xc0'] = 1;
1329
			$data[] = "xc0\t{$virt_line}";
1330
		} elseif (stristr($tty, "ttyu")) {
1331
			$ttyn = substr($tty, 0, 5);
1332
			$found[$ttyn] = 1;
1333
			$data[] = "{$ttyn}\t{$ttyu_line}";
1334
		} elseif (substr($tty, 0, 7) == 'console') {
1335
			$found['console'] = 1;
1336
			$data[] = $tty;
1337
		} else {
1338
			$data[] = $tty;
1339
		}
1340
	}
1341
	unset($on_off, $console_type, $serial_type);
1342

    
1343
	/* Detect missing main lines on original file and try to rebuild it */
1344
	$items = array(
1345
		'console',
1346
		'ttyv0',
1347
		'ttyu0',
1348
		'ttyu1',
1349
		'ttyu2',
1350
		'ttyu3',
1351
		'xc0'
1352
	);
1353

    
1354
	foreach ($items as $item) {
1355
		if (isset($found[$item])) {
1356
			continue;
1357
		}
1358

    
1359
		if ($item == 'console') {
1360
			$data[] = $console_line;
1361
		} elseif (($item == 'ttyv0') || ($item == 'xc0')) {
1362
			/* xc0 - Xen console, see https://redmine.pfsense.org/issues/11402 */
1363
			$data[] = "{$item}\t{$virt_line}";
1364
		} else {
1365
			$data[] = "{$item}\t{$ttyu_line}";
1366
		}
1367
	}
1368

    
1369
	safe_write_file($ttys_file, $data);
1370

    
1371
	unset($ttys, $ttys_file, $ttys_split, $data);
1372

    
1373
	if ($when != "upgrade") {
1374
		reload_ttys();
1375
	}
1376

    
1377
	return;
1378
}
1379

    
1380
function is_serial_enabled() {
1381
	global $g;
1382

    
1383
	if (!isset($g['enableserial_force']) &&
1384
	    !config_path_enabled('system','enableserial')) {
1385
		return false;
1386
	}
1387

    
1388
	return true;
1389
}
1390

    
1391
function reload_ttys() {
1392
	// Send a HUP signal to init will make it reload /etc/ttys
1393
	posix_kill(1, SIGHUP);
1394
}
1395

    
1396
function print_value_list($list, $count = 10, $separator = ",") {
1397
	$ret = implode($separator, array_slice($list, 0, $count));
1398
	if (count($list) < $count) {
1399
		$ret .= ".";
1400
	} else {
1401
		$ret .= "...";
1402
	}
1403
	return $ret;
1404
}
1405

    
1406
/* DHCP enabled on any interfaces? */
1407
function is_dhcp_server_enabled() {
1408
	foreach (config_get_path('dhcpd', []) as $dhcpif => $dhcpifconf) {
1409
		if (empty($dhcpifconf)) {
1410
			continue;
1411
		}
1412
		if (isset($dhcpifconf['enable']) &&
1413
			!empty(config_get_path("interfaces/{$dhcpif}"))) {
1414
			return true;
1415
		}
1416
	}
1417

    
1418
	return false;
1419
}
1420

    
1421
/* DHCP enabled on any interfaces? */
1422
function is_dhcpv6_server_enabled() {
1423
	foreach (config_get_path('interfaces', []) as $ifcfg) {
1424
		if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface'])) {
1425
			return true;
1426
		}
1427
	}
1428

    
1429
	foreach (config_get_path('dhcpdv6', []) as $dhcpv6if => $dhcpv6ifconf) {
1430
		if (empty($dhcpv6ifconf)) {
1431
			continue;
1432
		}
1433
		if (isset($dhcpv6ifconf['enable']) &&
1434
			!empty(config_get_path("interfaces/{$dhcpv6if}"))) {
1435
			return true;
1436
		}
1437
	}
1438

    
1439
	return false;
1440
}
1441

    
1442
/* radvd enabled on any interfaces? */
1443
function is_radvd_enabled() {
1444
	init_config_arr(['dhcpdv6']);
1445
	$dhcpdv6cfg = config_get_path('dhcpdv6', []);
1446
	$Iflist = get_configured_interface_list();
1447

    
1448
	/* handle manually configured DHCP6 server settings first */
1449
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1450
		if (empty($dhcpv6ifconf)) {
1451
			continue;
1452
		}
1453
		if (!config_path_enabled("interfaces/{$dhcpv6if}")) {
1454
			continue;
1455
		}
1456

    
1457
		if (!isset($dhcpv6ifconf['ramode'])) {
1458
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
1459
		}
1460

    
1461
		if ($dhcpv6ifconf['ramode'] == "disabled") {
1462
			continue;
1463
		}
1464

    
1465
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1466
		if (!is_ipaddrv6($ifcfgipv6)) {
1467
			continue;
1468
		}
1469

    
1470
		return true;
1471
	}
1472

    
1473
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
1474
	foreach (array_keys($Iflist) as $if) {
1475
		if (!config_path_enabled("interfaces/{$if}", 'track6-interface')) {
1476
			continue;
1477
		}
1478
		if (!config_path_enabled("interfaces/{$if}")) {
1479
			continue;
1480
		}
1481

    
1482
		$ifcfgipv6 = get_interface_ipv6($if);
1483
		if (!is_ipaddrv6($ifcfgipv6)) {
1484
			continue;
1485
		}
1486

    
1487
		$ifcfgsnv6 = get_interface_subnetv6($if);
1488
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1489

    
1490
		if (!is_ipaddrv6($subnetv6)) {
1491
			continue;
1492
		}
1493

    
1494
		return true;
1495
	}
1496

    
1497
	return false;
1498
}
1499

    
1500
/* Any PPPoE servers enabled? */
1501
function is_pppoe_server_enabled() {
1502
	$pppoeenable = false;
1503

    
1504
	foreach (config_get_path('pppoes/pppoe', []) as $pppoes) {
1505
		if ($pppoes['mode'] == 'server') {
1506
			$pppoeenable = true;
1507
		}
1508
	}
1509

    
1510
	return $pppoeenable;
1511
}
1512

    
1513
/* Optional arg forces hh:mm:ss without days */
1514
function convert_seconds_to_dhms($sec, $showhoursonly = false) {
1515
	if (!is_numericint($sec)) {
1516
		return '-';
1517
	}
1518
	// FIXME: When we move to PHP 7 we can use "intdiv($sec % X, Y)" etc
1519
	list($d, $h, $m, $s) = array(	(int)($showhoursonly ? 0 : $sec/86400),
1520
					(int)(($showhoursonly ? $sec : $sec % 86400)/3600),
1521
					(int)(($sec % 3600)/60),
1522
					$sec % 60
1523
				);
1524
	return ($d > 0 ? $d . 'd ' : '') . sprintf('%02d:%02d:%02d', $h, $m, $s);
1525
}
1526

    
1527
/* Compute the total uptime from the ppp uptime log file in the conf directory */
1528

    
1529
function get_ppp_uptime($port) {
1530
	if (file_exists("/conf/{$port}.log")) {
1531
		$saved_time = file_get_contents("/conf/{$port}.log");
1532
		$uptime_data = explode("\n", $saved_time);
1533
		$sec = 0;
1534
		foreach ($uptime_data as $upt) {
1535
			/* Skip blank lines, trim, and cast to int to ensure type matches.
1536
			 * https://redmine.pfsense.org/issues/14117 */
1537
			if (!empty($upt)) {
1538
				$sec += (int) trim(substr($upt, 1 + strpos($upt, " ")));
1539
			}
1540
		}
1541
		return convert_seconds_to_dhms($sec);
1542
	} else {
1543
		$total_time = gettext("No history data found!");
1544
		return $total_time;
1545
	}
1546
}
1547

    
1548
//returns interface information
1549
function get_interface_info($ifdescr) {
1550
	global $g;
1551

    
1552
	$ifinfo = array();
1553
	if (empty(config_get_path("interfaces/{$ifdescr}"))) {
1554
		return;
1555
	}
1556
	$ifinfo['hwif'] = config_get_path("interfaces/{$ifdescr}/if");
1557
	$ifinfo['enable'] = config_path_enabled("interfaces/{$ifdescr}");
1558
	$ifinfo['if'] = get_real_interface($ifdescr);
1559

    
1560
	$chkif = $ifinfo['if'];
1561
	$ifinfotmp = get_interface_addresses($chkif);
1562
	$ifinfo['status'] = $ifinfotmp['status'];
1563
	if (empty($ifinfo['status'])) {
1564
		$ifinfo['status'] = "down";
1565
	}
1566
	$ifinfo['macaddr'] = $ifinfotmp['macaddr'];
1567
	$ifinfo['mtu'] = $ifinfotmp['mtu'];
1568
	$ifinfo['ipaddr'] = $ifinfotmp['ipaddr'];
1569
	$ifinfo['subnet'] = $ifinfotmp['subnet'];
1570
	$ifinfo['linklocal'] = get_interface_linklocal($ifdescr);
1571
	$ifinfo['ipaddrv6'] = get_interface_ipv6($ifdescr);
1572
	$ifinfo['subnetv6'] = get_interface_subnetv6($ifdescr);
1573
	if (isset($ifinfotmp['link0'])) {
1574
		$link0 = "down";
1575
	}
1576
	$ifinfotmp = pfSense_get_interface_stats($chkif);
1577
	// $ifinfo['inpkts'] = $ifinfotmp['inpkts'];
1578
	// $ifinfo['outpkts'] = $ifinfotmp['outpkts'];
1579
	$ifinfo['inerrs'] = $ifinfotmp['inerrs'];
1580
	$ifinfo['outerrs'] = $ifinfotmp['outerrs'];
1581
	$ifinfo['collisions'] = $ifinfotmp['collisions'];
1582

    
1583
	/* Use pfctl for non wrapping 64 bit counters */
1584
	/* Pass */
1585
	exec("/sbin/pfctl -vvsI -i {$chkif}", $pfctlstats);
1586
	$pf_in4_pass = preg_split("/ +/ ", $pfctlstats[3]);
1587
	$pf_out4_pass = preg_split("/ +/", $pfctlstats[5]);
1588
	$pf_in6_pass = preg_split("/ +/ ", $pfctlstats[7]);
1589
	$pf_out6_pass = preg_split("/ +/", $pfctlstats[9]);
1590
	$in4_pass = $pf_in4_pass[5];
1591
	$out4_pass = $pf_out4_pass[5];
1592
	$in4_pass_packets = $pf_in4_pass[3];
1593
	$out4_pass_packets = $pf_out4_pass[3];
1594
	$in6_pass = $pf_in6_pass[5];
1595
	$out6_pass = $pf_out6_pass[5];
1596
	$in6_pass_packets = $pf_in6_pass[3];
1597
	$out6_pass_packets = $pf_out6_pass[3];
1598
	$ifinfo['inbytespass'] = $in4_pass + $in6_pass;
1599
	$ifinfo['outbytespass'] = $out4_pass + $out6_pass;
1600
	$ifinfo['inpktspass'] = $in4_pass_packets + $in6_pass_packets;
1601
	$ifinfo['outpktspass'] = $out4_pass_packets + $out6_pass_packets;
1602

    
1603
	/* Block */
1604
	$pf_in4_block = preg_split("/ +/", $pfctlstats[4]);
1605
	$pf_out4_block = preg_split("/ +/", $pfctlstats[6]);
1606
	$pf_in6_block = preg_split("/ +/", $pfctlstats[8]);
1607
	$pf_out6_block = preg_split("/ +/", $pfctlstats[10]);
1608
	$in4_block = $pf_in4_block[5];
1609
	$out4_block = $pf_out4_block[5];
1610
	$in4_block_packets = $pf_in4_block[3];
1611
	$out4_block_packets = $pf_out4_block[3];
1612
	$in6_block = $pf_in6_block[5];
1613
	$out6_block = $pf_out6_block[5];
1614
	$in6_block_packets = $pf_in6_block[3];
1615
	$out6_block_packets = $pf_out6_block[3];
1616
	$ifinfo['inbytesblock'] = $in4_block + $in6_block;
1617
	$ifinfo['outbytesblock'] = $out4_block + $out6_block;
1618
	$ifinfo['inpktsblock'] = $in4_block_packets + $in6_block_packets;
1619
	$ifinfo['outpktsblock'] = $out4_block_packets + $out6_block_packets;
1620

    
1621
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1622
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1623
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1624
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1625

    
1626
	$link_type = config_get_path("interfaces/{$ifdescr}/ipaddr");
1627
	switch ($link_type) {
1628
		/* DHCP? -> see if dhclient is up */
1629
		case "dhcp":
1630
			/* see if dhclient is up */
1631
			if (find_dhclient_process($ifinfo['if']) != 0) {
1632
				$ifinfo['dhcplink'] = "up";
1633
			} else {
1634
				$ifinfo['dhcplink'] = "down";
1635
			}
1636

    
1637
			break;
1638
		/* PPPoE/PPTP/L2TP interface? -> get status from virtual interface */
1639
		case "pppoe":
1640
		case "pptp":
1641
		case "l2tp":
1642
			if ($ifinfo['status'] == "up" && !isset($link0)) {
1643
				/* get PPPoE link status for dial on demand */
1644
				$ifinfo["{$link_type}link"] = "up";
1645
			} else {
1646
				$ifinfo["{$link_type}link"] = "down";
1647
			}
1648

    
1649
			break;
1650
		/* PPP interface? -> get uptime for this session and cumulative uptime from the persistent log file in conf */
1651
		case "ppp":
1652
			if ($ifinfo['status'] == "up") {
1653
				$ifinfo['ppplink'] = "up";
1654
			} else {
1655
				$ifinfo['ppplink'] = "down" ;
1656
			}
1657

    
1658
			if (empty($ifinfo['status'])) {
1659
				$ifinfo['status'] = "down";
1660
			}
1661

    
1662
			foreach (config_get_path('ppps/ppp', []) as $ppp) {
1663
				if (config_get_path("interfaces/{$ifdescr}/if") == $ppp['if']) {
1664
					break;
1665
				}
1666
			}
1667
			$dev = $ppp['ports'];
1668
			if (config_get_path("interfaces/{$ifdescr}/if") != $ppp['if'] || empty($dev)) {
1669
				break;
1670
			}
1671
			if (!file_exists($dev)) {
1672
				$ifinfo['nodevice'] = 1;
1673
				$ifinfo['pppinfo'] = $dev . " " . gettext("device not present! Is the modem attached to the system?");
1674
			}
1675

    
1676
			$usbmodemoutput = array();
1677
			exec("/usr/sbin/usbconfig", $usbmodemoutput);
1678
			$mondev = "{$g['tmp_path']}/3gstats.{$ifdescr}";
1679
			if (file_exists($mondev)) {
1680
				$cellstats = file($mondev);
1681
				/* skip header */
1682
				$a_cellstats = explode(",", $cellstats[1]);
1683
				if (preg_match("/huawei/i", implode("\n", $usbmodemoutput))) {
1684
					$ifinfo['cell_rssi'] = huawei_rssi_to_string($a_cellstats[1]);
1685
					$ifinfo['cell_mode'] = huawei_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1686
					$ifinfo['cell_simstate'] = huawei_simstate_to_string($a_cellstats[10]);
1687
					$ifinfo['cell_service'] = huawei_service_to_string(trim($a_cellstats[11]));
1688
				}
1689
				if (preg_match("/zte/i", implode("\n", $usbmodemoutput))) {
1690
					$ifinfo['cell_rssi'] = zte_rssi_to_string($a_cellstats[1]);
1691
					$ifinfo['cell_mode'] = zte_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1692
					$ifinfo['cell_simstate'] = zte_simstate_to_string($a_cellstats[10]);
1693
					$ifinfo['cell_service'] = zte_service_to_string(trim($a_cellstats[11]));
1694
				}
1695
				$ifinfo['cell_upstream'] = $a_cellstats[4];
1696
				$ifinfo['cell_downstream'] = trim($a_cellstats[5]);
1697
				$ifinfo['cell_sent'] = $a_cellstats[6];
1698
				$ifinfo['cell_received'] = trim($a_cellstats[7]);
1699
				$ifinfo['cell_bwupstream'] = $a_cellstats[8];
1700
				$ifinfo['cell_bwdownstream'] = trim($a_cellstats[9]);
1701
			}
1702
			// Calculate cumulative uptime for PPP link. Useful for connections that have per minute/hour contracts so you don't go over!
1703
			if (isset($ppp['uptime'])) {
1704
				$ifinfo['ppp_uptime_accumulated'] = "(".get_ppp_uptime($ifinfo['if']).")";
1705
			}
1706
			break;
1707
		default:
1708
			break;
1709
	}
1710

    
1711
	if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) {
1712
		$sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`);
1713
		$ifinfo['ppp_uptime'] = convert_seconds_to_dhms($sec);
1714
	}
1715

    
1716
	if ($ifinfo['status'] == "up") {
1717
		/* try to determine media with ifconfig */
1718
		$ifconfiginfo = [];
1719
		exec("/sbin/ifconfig -v " . $ifinfo['if'], $ifconfiginfo);
1720
		$wifconfiginfo = [];
1721
		if (is_interface_wireless($ifdescr)) {
1722
			exec("/sbin/ifconfig {$ifinfo['if']} list sta", $wifconfiginfo);
1723
			array_shift($wifconfiginfo);
1724
		}
1725
		$matches = "";
1726
		foreach ($ifconfiginfo as $ici) {
1727

    
1728
			/* don't list media/speed for wireless cards, as it always
1729
			   displays 2 Mbps even though clients can connect at 11 Mbps */
1730
			if (preg_match("/media: .*? \((.*?)\)/", $ici, $matches)) {
1731
				$ifinfo['media'] = $matches[1];
1732
			} else if (preg_match("/media: Ethernet (.*)/", $ici, $matches)) {
1733
				$ifinfo['media'] = $matches[1];
1734
			} else if (preg_match("/media: IEEE 802.11 Wireless Ethernet (.*)/", $ici, $matches)) {
1735
				$ifinfo['media'] = $matches[1];
1736
			}
1737

    
1738
			if (preg_match("/status: (.*)$/", $ici, $matches)) {
1739
				if ($matches[1] != "active") {
1740
					$ifinfo['status'] = $matches[1];
1741
				}
1742
				if ($ifinfo['status'] == gettext("running")) {
1743
					$ifinfo['status'] = gettext("up");
1744
				}
1745
			}
1746
			if (preg_match("/channel (\S*)/", $ici, $matches)) {
1747
				$ifinfo['channel'] = $matches[1];
1748
			}
1749
			if (preg_match("/ssid (\".*?\"|\S*)/", $ici, $matches)) {
1750
				if ($matches[1][0] == '"') {
1751
					$ifinfo['ssid'] = substr($matches[1], 1, -1);
1752
				}
1753
				else {
1754
					$ifinfo['ssid'] = $matches[1];
1755
				}
1756
			}
1757
			if (preg_match("/laggproto (.*)$/", $ici, $matches)) {
1758
				$ifinfo['laggproto'] = $matches[1];
1759
			}
1760
			if (preg_match("/laggport: (.*)$/", $ici, $matches)) {
1761
				$ifinfo['laggport'][] = $matches[1];
1762
			}
1763
			if (preg_match("/plugged: (.*)$/", $ici, $matches)) {
1764
				$ifinfo['plugged'] = $matches[1];
1765
			}
1766
			if (preg_match("/vendor: (.*)$/", $ici, $matches)) {
1767
				$ifinfo['vendor'] = $matches[1];
1768
			}
1769
			if (preg_match("/module temperature: (.*) Voltage: (.*)$/", $ici, $matches)) {
1770
				$ifinfo['temperature'] = $matches[1];
1771
				$ifinfo['voltage'] = $matches[2];
1772
			}
1773
			if (preg_match("/RX: (.*) TX: (.*)$/", $ici, $matches)) {
1774
				$ifinfo['rx'] = $matches[1];
1775
				$ifinfo['tx'] = $matches[2];
1776
			}
1777
		}
1778
		foreach ($wifconfiginfo as $ici) {
1779
			$elements = preg_split("/[ ]+/i", $ici);
1780
			if ($elements[0] != "") {
1781
				$ifinfo['bssid'] = $elements[0];
1782
			}
1783
			if ($elements[3] != "") {
1784
				$ifinfo['rate'] = $elements[3];
1785
			}
1786
			if ($elements[4] != "") {
1787
				$ifinfo['rssi'] = $elements[4];
1788
			}
1789
		}
1790
		/* lookup the gateway */
1791
		if (interface_has_gateway($ifdescr)) {
1792
			$ifinfo['gateway'] = get_interface_gateway($ifdescr);
1793
		}
1794
		if (interface_has_gatewayv6($ifdescr)) {
1795
			$ifinfo['gatewayv6'] = get_interface_gateway_v6($ifdescr);
1796
		}
1797
	}
1798

    
1799
	$bridge = "";
1800
	$bridge = link_interface_to_bridge($ifdescr);
1801
	if ($bridge) {
1802
		$bridge_text = `/sbin/ifconfig {$bridge}`;
1803
		if (stristr($bridge_text, "blocking") <> false) {
1804
			$ifinfo['bridge'] = "<b><font color='red'>" . gettext("blocking") . "</font></b> - " . gettext("check for ethernet loops");
1805
			$ifinfo['bridgeint'] = $bridge;
1806
		} else if (stristr($bridge_text, "learning") <> false) {
1807
			$ifinfo['bridge'] = gettext("learning");
1808
			$ifinfo['bridgeint'] = $bridge;
1809
		} else if (stristr($bridge_text, "forwarding") <> false) {
1810
			$ifinfo['bridge'] = gettext("forwarding");
1811
			$ifinfo['bridgeint'] = $bridge;
1812
		}
1813
	}
1814

    
1815
	return $ifinfo;
1816
}
1817

    
1818
//returns cpu speed of processor. Good for determining capabilities of machine
1819
function get_cpu_speed() {
1820
	return get_single_sysctl("hw.clockrate");
1821
}
1822

    
1823
function get_uptime_sec() {
1824
	$boottime = "";
1825
	$matches = "";
1826
	$boottime = get_single_sysctl("kern.boottime");
1827
	preg_match("/sec = (\d+)/", $boottime, $matches);
1828
	$boottime = $matches[1];
1829
	if (intval($boottime) == 0) {
1830
		return 0;
1831
	}
1832

    
1833
	$uptime = time() - $boottime;
1834
	return $uptime;
1835
}
1836

    
1837
function resolve_host_addresses($host, $recordtypes = array(DNS_A, DNS_AAAA, DNS_CNAME), $index = true) {
1838
	$dnsresult = array();
1839
	$resolved = array();
1840
	$errreporting = error_reporting();
1841
	error_reporting($errreporting & ~E_WARNING);// dns_get_record throws a warning if nothing is resolved..
1842
	foreach ($recordtypes as $recordtype) {
1843
		$tmp = dns_get_record($host, $recordtype);
1844
		if (is_array($tmp)) {
1845
			$dnsresult = array_merge($dnsresult, $tmp);
1846
		}
1847
	}
1848
	error_reporting($errreporting);// restore original php warning/error settings.
1849
	foreach ($dnsresult as $item) {
1850
		$newitem = array();
1851
		$newitem['type'] = $item['type'];
1852
		switch ($item['type']) {
1853
			case 'CNAME':
1854
				$newitem['data'] = $item['target'];
1855
				$resolved[] = $newitem;
1856
				break;
1857
			case 'A':
1858
				$newitem['data'] = $item['ip'];
1859
				$resolved[] = $newitem;
1860
				break;
1861
			case 'AAAA':
1862
				$newitem['data'] = $item['ipv6'];
1863
				$resolved[] = $newitem;
1864
				break;
1865
		}
1866
	}
1867
	if ($index == false) {
1868
		foreach ($resolved as $res) {
1869
			$noind[] = $res['data'];
1870
		}
1871
		$resolved = $noind;
1872
	}
1873
	return $resolved;
1874
}
1875

    
1876
function add_hostname_to_watch($hostname) {
1877
	if (!is_dir("/var/db/dnscache")) {
1878
		mkdir("/var/db/dnscache");
1879
	}
1880
	$result = array();
1881
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1882
		$contents = "";
1883
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1884
		if (!empty($domips)) {
1885
			foreach ($domips as $ip) {
1886
				$contents .= "$ip\n";
1887
			}
1888
		}
1889
		file_put_contents("/var/db/dnscache/$hostname", $contents);
1890
		/* Remove empty elements */
1891
		$result = array_filter(explode("\n", $contents), 'strlen');
1892
	}
1893
	return $result;
1894
}
1895

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

    
1900
function pfsense_default_state_size() {
1901
	/* get system memory amount */
1902
	$memory = get_memory();
1903
	$physmem = $memory[0];
1904

    
1905
	if ((int) $physmem > 0) {
1906
		/* Be cautious and only allocate 10% of system memory to the state table */
1907
		$max_states = (int) ($physmem/10)*1000;
1908
	} else {
1909
		/* If the memory check result is invalid, use a low but still
1910
		 * somewhat sane default (Equivalent to ~256MB RAM) */
1911
		$max_states = 25600;
1912
	}
1913

    
1914
	return $max_states;
1915
}
1916

    
1917
function pfsense_default_tables_size() {
1918
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1919
	return $current;
1920
}
1921

    
1922
function pfsense_default_table_entries_size() {
1923
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1924
	return (trim($current));
1925
}
1926

    
1927
/* Compare the current hostname DNS to the DNS cache we made
1928
 * if it has changed we return the old records
1929
 * if no change we return false */
1930
function compare_hostname_to_dnscache($hostname) {
1931
	global $g;
1932
	if (!is_dir("/var/db/dnscache")) {
1933
		mkdir("/var/db/dnscache");
1934
	}
1935
	$hostname = trim($hostname);
1936
	if (is_readable("/var/db/dnscache/{$hostname}")) {
1937
		$oldcontents = file_get_contents("/var/db/dnscache/{$hostname}");
1938
	} else {
1939
		$oldcontents = "";
1940
	}
1941
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1942
		$contents = "";
1943
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1944
		if (!empty($domips)) {
1945
			foreach ($domips as $ip) {
1946
				$contents .= "$ip\n";
1947
			}
1948
		}
1949
	}
1950

    
1951
	if (trim($oldcontents) != trim($contents)) {
1952
		if (g_get('debug')) {
1953
			log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents));
1954
		}
1955
		return ($oldcontents);
1956
	} else {
1957
		return false;
1958
	}
1959
}
1960

    
1961
/*
1962
 * load_crypto() - Load crypto modules if enabled in config.
1963
 */
1964
function load_crypto() {
1965
	$crypto_modules = array('aesni', 'cryptodev');
1966

    
1967
	$enabled_modules = explode('_', config_get_path('system/crypto_hardware'));
1968

    
1969
	foreach ($enabled_modules as $enmod) {
1970
		if (empty($enmod) || !in_array($enmod, $crypto_modules)) {
1971
			continue;
1972
		}
1973
		if (!is_module_loaded($enmod)) {
1974
			log_error(sprintf(gettext("Loading %s cryptographic accelerator module."), $enmod));
1975
			mute_kernel_msgs();
1976
			mwexec("/sbin/kldload " . escapeshellarg($enmod));
1977
			unmute_kernel_msgs();
1978
		}
1979
	}
1980
}
1981

    
1982
/*
1983
 * load_thermal_hardware() - Load temperature monitor kernel module
1984
 */
1985
function load_thermal_hardware() {
1986
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
1987
	$thermal_hardware = config_get_path('system/thermal_hardware');
1988

    
1989
	if (!in_array($thermal_hardware, $thermal_hardware_modules)) {
1990
		return false;
1991
	}
1992

    
1993
	if (!empty($thermal_hardware) && !is_module_loaded($thermal_hardware)) {
1994
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $thermal_hardware));
1995
		mute_kernel_msgs();
1996
		mwexec("/sbin/kldload {$thermal_hardware}");
1997
		unmute_kernel_msgs();
1998
	}
1999
}
2000

    
2001
/****f* pfsense-utils/isvm
2002
 * NAME
2003
 *   isvm
2004
 * INPUTS
2005
 *	none
2006
 * RESULT
2007
 *   returns true if machine is running under a virtual environment
2008
 ******/
2009
function isvm() {
2010
	$virtualenvs = array("vmware", "parallels", "qemu", "bochs", "plex86", "VirtualBox");
2011
	exec('/bin/kenv -q smbios.system.product 2>/dev/null', $output, $rc);
2012

    
2013
	if ($rc != 0 || !isset($output[0])) {
2014
		return false;
2015
	}
2016

    
2017
	foreach ($virtualenvs as $virtualenv) {
2018
		if (stripos($output[0], $virtualenv) !== false) {
2019
			return true;
2020
		}
2021
	}
2022

    
2023
	return false;
2024
}
2025

    
2026
function get_freebsd_version() {
2027
	$version = explode(".", php_uname("r"));
2028
	return $version[0];
2029
}
2030

    
2031
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
2032
	global $g;
2033

    
2034
	$fp = fopen($destination, "wb");
2035

    
2036
	if (!$fp) {
2037
		return false;
2038
	}
2039

    
2040
	$ch = curl_init();
2041
	curl_setopt($ch, CURLOPT_URL, $url);
2042
	if ($verify_ssl) {
2043
		curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
2044
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
2045
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
2046
	} else {
2047
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
2048
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2049
		curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false);
2050
	}
2051
	curl_setopt($ch, CURLOPT_FILE, $fp);
2052
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2053
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2054
	curl_setopt($ch, CURLOPT_HEADER, false);
2055
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2056
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
2057
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version') . ':' . system_get_uniqueid());
2058
	} else {
2059
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version'));
2060
	}
2061

    
2062
	set_curlproxy($ch);
2063

    
2064
	@curl_exec($ch);
2065
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2066
	fclose($fp);
2067
	curl_close($ch);
2068
	if ($http_code == 200) {
2069
		return true;
2070
	} else {
2071
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2072
		unlink_if_exists($destination);
2073
		return false;
2074
	}
2075
}
2076

    
2077
function download_file_with_progress_bar($url, $destination, $verify_ssl = true, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) {
2078
	global $g, $ch, $fout, $file_size, $downloaded, $first_progress_update;
2079
	$file_size = 1;
2080
	$downloaded = 1;
2081
	$first_progress_update = TRUE;
2082
	/* open destination file */
2083
	$fout = fopen($destination, "wb");
2084

    
2085
	if (!$fout) {
2086
		return false;
2087
	}
2088
	/*
2089
	 *      Originally by Author: Keyvan Minoukadeh
2090
	 *      Modified by Scott Ullrich to return Content-Length size
2091
	 */
2092
	$ch = curl_init();
2093
	curl_setopt($ch, CURLOPT_URL, $url);
2094
	if ($verify_ssl) {
2095
		curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
2096
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
2097
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
2098
	} else {
2099
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
2100
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2101
		curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false);
2102
	}
2103
	curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
2104
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2105
	curl_setopt($ch, CURLOPT_WRITEFUNCTION, $readbody);
2106
	curl_setopt($ch, CURLOPT_NOPROGRESS, '1');
2107
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2108
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2109
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
2110
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version') . ':' . system_get_uniqueid());
2111
	} else {
2112
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version'));
2113
	}
2114

    
2115
	set_curlproxy($ch);
2116

    
2117
	@curl_exec($ch);
2118
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2119
	fclose($fout);
2120
	curl_close($ch);
2121
	if ($http_code == 200) {
2122
		return true;
2123
	} else {
2124
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2125
		unlink_if_exists($destination);
2126
		return false;
2127
	}
2128
}
2129

    
2130
function read_header($ch, $string) {
2131
	global $file_size;
2132
	$length = strlen($string);
2133
	$regs = "";
2134
	preg_match("/(Content-Length:) (.*)/", $string, $regs);
2135
	if ($regs[2] <> "") {
2136
		$file_size = intval($regs[2]);
2137
	}
2138
	ob_flush();
2139
	return $length;
2140
}
2141

    
2142
function read_body($ch, $string) {
2143
	global $fout, $file_size, $downloaded, $sendto, $static_status, $static_output, $lastseen, $first_progress_update;
2144
	global $pkg_interface;
2145
	$length = strlen($string);
2146
	$downloaded += intval($length);
2147
	if ($file_size > 0) {
2148
		$downloadProgress = round(100 * (1 - $downloaded / $file_size), 0);
2149
		$downloadProgress = 100 - $downloadProgress;
2150
	} else {
2151
		$downloadProgress = 0;
2152
	}
2153
	if ($lastseen <> $downloadProgress and $downloadProgress < 101) {
2154
		if ($sendto == "status") {
2155
			if ($pkg_interface == "console") {
2156
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2157
					$tostatus = $static_status . $downloadProgress . "%";
2158
					if ($downloadProgress == 100) {
2159
						$tostatus = $tostatus . "\r";
2160
					}
2161
					update_status($tostatus);
2162
				}
2163
			} else {
2164
				$tostatus = $static_status . $downloadProgress . "%";
2165
				update_status($tostatus);
2166
			}
2167
		} else {
2168
			if ($pkg_interface == "console") {
2169
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2170
					$tooutput = $static_output . $downloadProgress . "%";
2171
					if ($downloadProgress == 100) {
2172
						$tooutput = $tooutput . "\r";
2173
					}
2174
					update_output_window($tooutput);
2175
				}
2176
			} else {
2177
				$tooutput = $static_output . $downloadProgress . "%";
2178
				update_output_window($tooutput);
2179
			}
2180
		}
2181
		if (($pkg_interface != "console") || (($downloadProgress % 10) == 0) || ($downloadProgress < 10)) {
2182
			update_progress_bar($downloadProgress, $first_progress_update);
2183
			$first_progress_update = FALSE;
2184
		}
2185
		$lastseen = $downloadProgress;
2186
	}
2187
	if ($fout) {
2188
		fwrite($fout, $string);
2189
	}
2190
	ob_flush();
2191
	return $length;
2192
}
2193

    
2194
/*
2195
 *   update_output_window: update bottom textarea dynamically.
2196
 */
2197
function update_output_window($text) {
2198
	global $pkg_interface;
2199
	$log = preg_replace("/\n/", "\\n", $text);
2200
	if ($pkg_interface != "console") {
2201
?>
2202
<script type="text/javascript">
2203
//<![CDATA[
2204
	document.getElementById("output").textContent="<?=htmlspecialchars($log)?>";
2205
	document.getElementById("output").scrollTop = document.getElementById("output").scrollHeight;
2206
//]]>
2207
</script>
2208
<?php
2209
	}
2210
	/* ensure that contents are written out */
2211
	ob_flush();
2212
}
2213

    
2214
/*
2215
 *   update_status: update top textarea dynamically.
2216
 */
2217
function update_status($status) {
2218
	global $pkg_interface;
2219

    
2220
	if ($pkg_interface == "console") {
2221
		print ("{$status}");
2222
	}
2223

    
2224
	/* ensure that contents are written out */
2225
	ob_flush();
2226
}
2227

    
2228
/*
2229
 * update_progress_bar($percent, $first_time): updates the javascript driven progress bar.
2230
 */
2231
function update_progress_bar($percent, $first_time) {
2232
	global $pkg_interface;
2233
	if ($percent > 100) {
2234
		$percent = 1;
2235
	}
2236
	if ($pkg_interface <> "console") {
2237
		echo '<script type="text/javascript">';
2238
		echo "\n//<![CDATA[\n";
2239
		echo 'document.getElementById("progressbar").style.width="'. $percent.'%"';
2240
		echo "\n//]]>\n";
2241
		echo '</script>';
2242
	} else {
2243
		if (!($first_time)) {
2244
			echo "\x08\x08\x08\x08\x08";
2245
		}
2246
		echo sprintf("%4d%%", $percent);
2247
	}
2248
}
2249

    
2250
function update_alias_name($new_alias_name, $orig_alias_name) {
2251
	if (!$orig_alias_name) {
2252
		return;
2253
	}
2254

    
2255
	// Firewall rules
2256
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2257
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2258
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2259
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2260
	// NAT Rules
2261
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2262
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2263
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2264
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2265
	update_alias_names_upon_change(array('nat', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2266
	update_alias_names_upon_change(array('nat', 'rule'), array('local-port'), $new_alias_name, $orig_alias_name);
2267
	// NAT 1:1 Rules
2268
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('external'), $new_alias_name, $orig_alias_name);
2269
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2270
	update_alias_names_upon_change(array('nat', 'onetoone'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2271
	// NAT Outbound Rules
2272
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('source', 'network'), $new_alias_name, $orig_alias_name);
2273
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('sourceport'), $new_alias_name, $orig_alias_name);
2274
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2275
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('dstport'), $new_alias_name, $orig_alias_name);
2276
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2277
	// Alias in an alias
2278
	update_alias_names_upon_change(array('aliases', 'alias'), array('address'), $new_alias_name, $orig_alias_name);
2279
	// Static routes
2280
	update_alias_names_upon_change(array('staticroutes', 'route'), array('network'), $new_alias_name, $orig_alias_name);
2281
	// OpenVPN
2282
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2283
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2284
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('local_network'), $new_alias_name, $orig_alias_name);
2285
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('local_networkv6'), $new_alias_name, $orig_alias_name);
2286
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('remote_network'), $new_alias_name, $orig_alias_name);
2287
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2288
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2289
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2290
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('remote_network'), $new_alias_name, $orig_alias_name);
2291
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2292
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2293
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2294
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('local_network'), $new_alias_name, $orig_alias_name);
2295
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('local_networkv6'), $new_alias_name, $orig_alias_name);
2296
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('remote_network'), $new_alias_name, $orig_alias_name);
2297
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2298
}
2299

    
2300
function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) {
2301
	global $g, $config, $pconfig, $debug;
2302
	if (!$origname) {
2303
		return;
2304
	}
2305

    
2306
	$sectionref = &$config;
2307
	foreach ($section as $sectionname) {
2308
		if (is_array($sectionref) && isset($sectionref[$sectionname])) {
2309
			$sectionref = &$sectionref[$sectionname];
2310
		} else {
2311
			return;
2312
		}
2313
	}
2314

    
2315
	if ($debug) {
2316
		$fd = fopen("{$g['tmp_path']}/print_r", "a");
2317
		fwrite($fd, print_r($pconfig, true));
2318
	}
2319

    
2320
	if (is_array($sectionref)) {
2321
		foreach (array_keys($sectionref) as $itemkey) {
2322
			if ($debug) {
2323
				fwrite($fd, "$itemkey\n");
2324
			}
2325

    
2326
			$fieldfound = true;
2327
			$fieldref = &$sectionref[$itemkey];
2328
			foreach ($field as $fieldname) {
2329
				if (is_array($fieldref) && isset($fieldref[$fieldname])) {
2330
					$fieldref = &$fieldref[$fieldname];
2331
				} else {
2332
					$fieldfound = false;
2333
					break;
2334
				}
2335
			}
2336
			if ($fieldfound && $fieldref == $origname) {
2337
				if ($debug) {
2338
					fwrite($fd, "Setting old alias value $origname to $new_alias_name\n");
2339
				}
2340
				$fieldref = $new_alias_name;
2341
			}
2342
		}
2343
	}
2344

    
2345
	if ($debug) {
2346
		fclose($fd);
2347
	}
2348

    
2349
}
2350

    
2351
function parse_aliases_file($filename, $type = "url", $max_items = -1, $kflc = false) {
2352
	/*
2353
	 * $filename = file to process for example blocklist like DROP:  http://www.spamhaus.org/drop/drop.txt
2354
	 * $type = if set to 'url' then subnets and ips will be returned,
2355
	 *         if set to 'url_ports' port-ranges and ports will be returned
2356
	 * $max_items = sets the maximum amount of valid items to load, -1 the default defines there is no limit.
2357
	 *
2358
	 * RETURNS an array of ip subnets and ip's or ports and port-ranges, returns NULL upon a error conditions (file not found)
2359
	 */
2360

    
2361
	if (!file_exists($filename)) {
2362
		log_error(sprintf(gettext("Could not process non-existent file from alias: %s"), $filename));
2363
		return null;
2364
	}
2365

    
2366
	if (filesize($filename) == 0) {
2367
		log_error(sprintf(gettext("Could not process empty file from alias: %s"), $filename));
2368
		return null;
2369
	}
2370
	$fd = @fopen($filename, 'r');
2371
	if (!$fd) {
2372
		log_error(sprintf(gettext("Could not process aliases from alias: %s"), $filename));
2373
		return null;
2374
	}
2375
	$items = array();
2376
	$comments = array();
2377
	while (($fc = fgets($fd)) !== FALSE) {
2378
		$fc = strip_tags($fc);
2379
		$tmp = alias_idn_to_ascii(trim($fc, " \t\n\r"));
2380
		if (empty($tmp)) {
2381
			continue;
2382
		}
2383
		if (($kflc) && (strpos($tmp, '#') === 0)) {	// Keep Full Line Comments (lines beginning with #).
2384
			$comments[] = $tmp;
2385
		} else {
2386
			$tmp_str = strstr($tmp, '#', true);
2387
			if (!empty($tmp_str)) {
2388
				$tmp = $tmp_str;
2389
			}
2390
			$tmp_str = strstr($tmp, ' ', true);
2391
			if (!empty($tmp_str)) {
2392
				$tmp = $tmp_str;
2393
			}
2394
			switch ($type) {
2395
				case "url":
2396
				case "urltable":
2397
					if (is_ipaddr($tmp) || is_subnet($tmp)) {
2398
						$items[] = $tmp;
2399
						break;
2400
					}
2401
					if (is_fqdn($tmp)) {
2402
						$results = resolve_host_addresses($tmp, array(DNS_A, DNS_AAAA), false);
2403
						if (!empty($results)) {
2404
							foreach ($results as $ip) {
2405
								if (is_ipaddr($ip)) {
2406
									$items[] = $ip;
2407
								}
2408
							}
2409
						}
2410
					}
2411
					break;
2412
				case "url_ports":
2413
				case "urltable_ports":
2414
					if (is_port_or_range($tmp)) {
2415
						$items[] = $tmp;
2416
					}
2417
					break;
2418
				default:
2419
					/* unknown type */
2420
					break;
2421
				}
2422
			if (count($items) == $max_items) {
2423
				break;
2424
			}
2425
		}
2426
	}
2427
	fclose($fd);
2428
	return array_merge($comments, $items);
2429
}
2430

    
2431
function update_alias_url_data() {
2432
	global $g, $aliastable;
2433

    
2434
	$updated = false;
2435

    
2436
	/* item is a url type */
2437
	$lockkey = lock('aliasurl');
2438
	$aliases = array();
2439
	$aliases_nested = array();
2440

    
2441
	foreach (config_get_path('aliases/alias', []) as $x => $alias) {
2442
		if (empty($alias['aliasurl'])) {
2443
			continue;
2444
		}
2445
		foreach ($alias['aliasurl'] as $alias_url) {
2446
			if (is_alias($alias_url)) {
2447
				// process nested URL aliases after URL-only aliases
2448
				$aliases_nested[] = $x;
2449
				continue 2;
2450
			}
2451
		}
2452
		$aliases[] = $x;
2453
	}
2454

    
2455
	foreach (array_merge($aliases, $aliases_nested) as $x) {
2456

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

    
2472
				/* if the item is tar gzipped then extract */
2473
				if (stripos($alias_url, '.tgz') && !process_alias_tgz($temp_filename)) {
2474
					log_error(sprintf(gettext("Could not unpack tgz from the URL '%s'."), $alias_url));
2475
					rmdir_recursive($temp_filename);
2476
					continue;
2477
				}
2478
				if (file_exists("{$temp_filename}/aliases")) {
2479
					$t_address = parse_aliases_file("{$temp_filename}/aliases", $type, 5000);
2480
					if ($t_address == null) {
2481
						/* nothing was found */
2482
						log_error(sprintf(gettext("Could not fetch usable data from '%s'."), $alias_url));
2483
						//rmdir_recursive($temp_filename);
2484
						continue;
2485
					} else {
2486
						array_push($address, ...$t_address);
2487
					}
2488
					unset($t_address);
2489
				}
2490
				rmdir_recursive($temp_filename);
2491
			} elseif (is_alias($alias_url)) {
2492
				/* nested URL alias, see https://redmine.pfsense.org/issues/11863 */
2493
				if (!$aliastable) {
2494
					alias_make_table();
2495
				}
2496
				$t_address = explode(" ", $aliastable[$alias_url]);
2497
				if ($t_address == null) {
2498
					log_error(sprintf(gettext("Could not get usable data from '%s' URL alias."), $alias_url));
2499
					continue;
2500
				}
2501
				array_push($address, ...$t_address);
2502
			}
2503
			if (!empty($address)) {
2504
				config_set_path("aliases/alias/{$x}/address", implode(" ", $address));
2505
				$updated = true;
2506
			}
2507
		}
2508
	}
2509

    
2510
	unlock($lockkey);
2511

    
2512
	/* Report status to callers as well */
2513
	return $updated;
2514
}
2515

    
2516
function process_alias_tgz($temp_filename) {
2517
	if (!file_exists('/usr/bin/tar')) {
2518
		log_error(gettext("Alias archive is a .tar/tgz file which cannot be decompressed because utility is missing!"));
2519
		return false;
2520
	}
2521
	rename("{$temp_filename}/aliases", "{$temp_filename}/aliases.tgz");
2522
	mwexec("/usr/bin/tar xzf {$temp_filename}/aliases.tgz -C {$temp_filename}/aliases/");
2523
	unlink("{$temp_filename}/aliases.tgz");
2524
	$files_to_process = return_dir_as_array("{$temp_filename}/");
2525
	/* foreach through all extracted files and build up aliases file */
2526
	$fd = @fopen("{$temp_filename}/aliases", "w");
2527
	if (!$fd) {
2528
		log_error(sprintf(gettext("Could not open %s/aliases for writing!"), $temp_filename));
2529
		return false;
2530
	}
2531
	foreach ($files_to_process as $f2p) {
2532
		$tmpfd = @fopen($f2p, 'r');
2533
		if (!$tmpfd) {
2534
			log_error(sprintf(gettext('The following file could not be read %1$s from %2$s'), $f2p, $temp_filename));
2535
			continue;
2536
		}
2537
		while (($tmpbuf = fread($tmpfd, 65536)) !== FALSE) {
2538
			fwrite($fd, $tmpbuf);
2539
		}
2540
		fclose($tmpfd);
2541
		unlink($f2p);
2542
	}
2543
	fclose($fd);
2544
	unset($tmpbuf);
2545

    
2546
	return true;
2547
}
2548

    
2549
function version_compare_dates($a, $b) {
2550
	$a_time = strtotime($a);
2551
	$b_time = strtotime($b);
2552

    
2553
	if ((!$a_time) || (!$b_time)) {
2554
		return FALSE;
2555
	} else {
2556
		if ($a_time < $b_time) {
2557
			return -1;
2558
		} elseif ($a_time == $b_time) {
2559
			return 0;
2560
		} else {
2561
			return 1;
2562
		}
2563
	}
2564
}
2565
function version_get_string_value($a) {
2566
	$strs = array(
2567
		0 => "ALPHA-ALPHA",
2568
		2 => "ALPHA",
2569
		3 => "BETA",
2570
		4 => "B",
2571
		5 => "C",
2572
		6 => "D",
2573
		7 => "RC",
2574
		8 => "RELEASE",
2575
		9 => "*"			// Matches all release levels
2576
	);
2577
	$major = 0;
2578
	$minor = 0;
2579
	foreach ($strs as $num => $str) {
2580
		if (substr($a, 0, strlen($str)) == $str) {
2581
			$major = $num;
2582
			$n = substr($a, strlen($str));
2583
			if (is_numeric($n)) {
2584
				$minor = $n;
2585
			}
2586
			break;
2587
		}
2588
	}
2589
	return "{$major}.{$minor}";
2590
}
2591
function version_compare_string($a, $b) {
2592
	// Only compare string parts if both versions give a specific release
2593
	// (If either version lacks a string part, assume intended to match all release levels)
2594
	if (isset($a) && isset($b)) {
2595
		return version_compare_numeric(version_get_string_value($a), version_get_string_value($b));
2596
	} else {
2597
		return 0;
2598
	}
2599
}
2600
function version_compare_numeric($a, $b) {
2601
	$a_arr = explode('.', rtrim($a, '.'));
2602
	$b_arr = explode('.', rtrim($b, '.'));
2603

    
2604
	foreach ($a_arr as $n => $val) {
2605
		if (array_key_exists($n, $b_arr)) {
2606
			// So far so good, both have values at this minor version level. Compare.
2607
			if ($val > $b_arr[$n]) {
2608
				return 1;
2609
			} elseif ($val < $b_arr[$n]) {
2610
				return -1;
2611
			}
2612
		} else {
2613
			// a is greater, since b doesn't have any minor version here.
2614
			return 1;
2615
		}
2616
	}
2617
	if (count($b_arr) > count($a_arr)) {
2618
		// b is longer than a, so it must be greater.
2619
		return -1;
2620
	} else {
2621
		// Both a and b are of equal length and value.
2622
		return 0;
2623
	}
2624
}
2625
function pfs_version_compare($cur_time, $cur_text, $remote) {
2626
	// First try date compare
2627
	$v = version_compare_dates($cur_time, $remote);
2628
	if ($v === FALSE) {
2629
		// If that fails, try to compare by string
2630
		// Before anything else, simply test if the strings are equal
2631
		if (($cur_text == $remote) || ($cur_time == $remote)) {
2632
			return 0;
2633
		}
2634
		list($cur_num, $cur_str) = explode('-', $cur_text);
2635
		list($rem_num, $rem_str) = explode('-', $remote);
2636

    
2637
		// First try to compare the numeric parts of the version string.
2638
		$v = version_compare_numeric($cur_num, $rem_num);
2639

    
2640
		// If the numeric parts are the same, compare the string parts.
2641
		if ($v == 0) {
2642
			return version_compare_string($cur_str, $rem_str);
2643
		}
2644
	}
2645
	return $v;
2646
}
2647
function process_alias_urltable($name, $type, $url, $freq, $forceupdate=false, $validateonly=false) {
2648
	global $g;
2649

    
2650
	if (!is_validaliasname($name) || !filter_var($url, FILTER_VALIDATE_URL)) {
2651
		return false;
2652
	}
2653

    
2654
	$urltable_prefix = "/var/db/aliastables/";
2655
	$urltable_filename = $urltable_prefix . basename($name) . ".txt";
2656
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2657

    
2658
	// Make the aliases directory if it doesn't exist
2659
	if (!file_exists($urltable_prefix)) {
2660
		mkdir($urltable_prefix);
2661
	} elseif (!is_dir($urltable_prefix)) {
2662
		unlink($urltable_prefix);
2663
		mkdir($urltable_prefix);
2664
	}
2665

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

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

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

    
2680
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2681
			if ($type == "urltable_ports") {
2682
				$parsed_contents = group_ports($parsed_contents, true);
2683
			}
2684
			if (is_array($parsed_contents)) {
2685
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2686
			} else {
2687
				touch($urltable_filename);
2688
			}
2689

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

    
2696
			unlink_if_exists($tmp_urltable_filename);
2697
		} else {
2698
			if (!$validateonly) {
2699
				touch($urltable_filename);
2700
			}
2701
			return false;
2702
		}
2703
		return true;
2704
	} else {
2705
		// File exists, and it doesn't need to be updated.
2706
		return -1;
2707
	}
2708
}
2709

    
2710
function get_include_contents($filename) {
2711
	if (is_file($filename)) {
2712
		ob_start();
2713
		include $filename;
2714
		$contents = ob_get_contents();
2715
		ob_end_clean();
2716
		return $contents;
2717
	}
2718
	return false;
2719
}
2720

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

    
2833
function get_country_name($country_code = "ALL") {
2834
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2835
		return "";
2836
	}
2837

    
2838
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2839
	$country_names_contents = file_get_contents($country_names_xml);
2840
	$country_names = xml2array($country_names_contents);
2841

    
2842
	if ($country_code == "ALL") {
2843
		$country_list = array();
2844
		foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2845
			$country_list[] = array(
2846
				"code" => $country['ISO_3166-1_Alpha-2_Code_element'],
2847
				"name" => ucwords(strtolower($country['ISO_3166-1_Country_name'])));
2848
		}
2849
		return $country_list;
2850
	}
2851

    
2852
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2853
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2854
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2855
		}
2856
	}
2857
	return "";
2858
}
2859

    
2860
/* Return the list of country codes to be used on CAs and certs */
2861
function get_cert_country_codes() {
2862
	$countries = get_country_name();
2863

    
2864
	$country_codes = array();
2865
	foreach ($countries as $country) {
2866
		$country_codes[$country['code']] = $country['code'];
2867
	}
2868
	ksort($country_codes);
2869

    
2870
	/* Preserve historical order: None, US, CA, other countries */
2871
	$first_items[''] = gettext("None");
2872
	$first_items['US'] = $country_codes['US'];
2873
	$first_items['CA'] = $country_codes['CA'];
2874
	unset($country_codes['US']);
2875
	unset($country_codes['CA']);
2876

    
2877
	return array_merge($first_items, $country_codes);
2878
}
2879

    
2880
/* sort by interface only, retain the original order of rules that apply to
2881
   the same interface */
2882
function filter_rules_sort() {
2883
	init_config_arr(array('filter', 'rule'));
2884
	$rules = config_get_path('filter/rule', []);
2885

    
2886
	/* mark each rule with the sequence number (to retain the order while sorting) */
2887
	for ($i = 0; isset($rules[$i]); $i++) {
2888
		$rules[$i]['seq'] =$i;
2889
	}
2890
	usort($rules, "filter_rules_compare");
2891

    
2892
	/* strip the sequence numbers again */
2893
	for ($i = 0; isset($rules[$i]); $i++) {
2894
		unset($rules[$i]['seq']);
2895
	}
2896

    
2897
	/* commit changes */
2898
	config_set_path('filter/rule', $rules);
2899
}
2900
function filter_rules_compare($a, $b) {
2901
	if (isset($a['floating']) && isset($b['floating'])) {
2902
		return $a['seq'] - $b['seq'];
2903
	} else if (isset($a['floating'])) {
2904
		return -1;
2905
	} else if (isset($b['floating'])) {
2906
		return 1;
2907
	} else if ($a['interface'] == $b['interface']) {
2908
		return $a['seq'] - $b['seq'];
2909
	} else {
2910
		return compare_interface_friendly_names($a['interface'], $b['interface']);
2911
	}
2912
}
2913

    
2914
function generate_ipv6_from_mac($mac) {
2915
	$elements = explode(":", $mac);
2916
	if (count($elements) <> 6) {
2917
		return false;
2918
	}
2919

    
2920
	$i = 0;
2921
	$ipv6 = "fe80::";
2922
	foreach ($elements as $byte) {
2923
		if ($i == 0) {
2924
			$hexadecimal = substr($byte, 1, 2);
2925
			$bitmap = base_convert($hexadecimal, 16, 2);
2926
			$bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT);
2927
			$bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3, 4);
2928
			$byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16);
2929
		}
2930
		$ipv6 .= $byte;
2931
		if ($i == 1) {
2932
			$ipv6 .= ":";
2933
		}
2934
		if ($i == 3) {
2935
			$ipv6 .= ":";
2936
		}
2937
		if ($i == 2) {
2938
			$ipv6 .= "ff:fe";
2939
		}
2940

    
2941
		$i++;
2942
	}
2943
	return $ipv6;
2944
}
2945

    
2946
/****f* pfsense-utils/load_mac_manufacturer_table
2947
 * NAME
2948
 *   load_mac_manufacturer_table
2949
 * INPUTS
2950
 *   none
2951
 * RESULT
2952
 *   returns associative array with MAC-Manufacturer pairs
2953
 ******/
2954
function load_mac_manufacturer_table() {
2955
	/* load MAC-Manufacture data from the file */
2956
	$macs = false;
2957
	if (file_exists("/usr/local/share/nmap/nmap-mac-prefixes")) {
2958
		$macs=file("/usr/local/share/nmap/nmap-mac-prefixes");
2959
	}
2960
	if ($macs) {
2961
		foreach ($macs as $line) {
2962
			if (preg_match('/([0-9A-Fa-f]{6}) (.*)$/', $line, $matches)) {
2963
				/* store values like this $mac_man['000C29']='VMware' */
2964
				$mac_man["$matches[1]"] = $matches[2];
2965
			}
2966
		}
2967
		return $mac_man;
2968
	} else {
2969
		return -1;
2970
	}
2971

    
2972
}
2973

    
2974
/****f* pfsense-utils/is_ipaddr_configured
2975
 * NAME
2976
 *   is_ipaddr_configured
2977
 * INPUTS
2978
 *   IP Address to check.
2979
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2980
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2981
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2982
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2983
 *     If check_subnets is true and cidrprefix is specified,
2984
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2985
 * RESULT
2986
 *   returns true if the IP Address is configured and present on this device or overlaps a configured subnet.
2987
*/
2988
function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2989
	if (count(where_is_ipaddr_configured($ipaddr, $ignore_if, $check_localip, $check_subnets, $cidrprefix))) {
2990
		return true;
2991
	}
2992
	return false;
2993
}
2994

    
2995
/****f* pfsense-utils/where_is_ipaddr_configured
2996
 * NAME
2997
 *   where_is_ipaddr_configured
2998
 * INPUTS
2999
 *   IP Address to check.
3000
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
3001
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
3002
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
3003
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
3004
 *     If check_subnets is true and cidrprefix is specified,
3005
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
3006
 * RESULT
3007
 *   Returns an array of the interfaces 'if' plus IP address or subnet 'ip_or_subnet' that match or overlap the IP address to check.
3008
 *   If there are no matches then an empty array is returned.
3009
*/
3010
function where_is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
3011
	$where_configured = array();
3012

    
3013
	$pos = strpos($ignore_if, '_virtualip');
3014
	if ($pos !== false) {
3015
		$ignore_vip_id = substr($ignore_if, $pos+10);
3016
		$ignore_vip_if = substr($ignore_if, 0, $pos);
3017
	} else {
3018
		$ignore_vip_id = -1;
3019
		$ignore_vip_if = $ignore_if;
3020
	}
3021

    
3022
	$isipv6 = is_ipaddrv6($ipaddr);
3023

    
3024
	if ($isipv6) {
3025
		$ipaddr = text_to_compressed_ip6($ipaddr);
3026
	}
3027

    
3028
	if ($check_subnets) {
3029
		$cidrprefix = intval($cidrprefix);
3030
		if ($isipv6) {
3031
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
3032
				$cidrprefix = 128;
3033
			}
3034
		} else {
3035
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
3036
				$cidrprefix = 32;
3037
			}
3038
		}
3039
		$iflist = get_configured_interface_list();
3040
		foreach ($iflist as $if => $ifname) {
3041
			if ($ignore_if == $if) {
3042
				continue;
3043
			}
3044

    
3045
			if ($isipv6) {
3046
				$if_ipv6 = get_interface_ipv6($if);
3047
				$if_snbitsv6 = get_interface_subnetv6($if);
3048
				/* do not check subnet overlapping on 6rd interfaces,
3049
				 * see https://redmine.pfsense.org/issues/12371 */ 
3050
				if ($if_ipv6 && $if_snbitsv6 &&
3051
				    ((config_get_path("interfaces/{$if}/ipaddrv6") != '6rd') || ($cidrprefix > $if_snbitsv6)) &&
3052
				    check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) {
3053
					$where_entry = array();
3054
					$where_entry['if'] = $if;
3055
					$where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if);
3056
					$where_configured[] = $where_entry;
3057
				}
3058
			} else {
3059
				$if_ipv4 = get_interface_ip($if);
3060
				$if_snbitsv4 = get_interface_subnet($if);
3061
				if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) {
3062
					$where_entry = array();
3063
					$where_entry['if'] = $if;
3064
					$where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if);
3065
					$where_configured[] = $where_entry;
3066
				}
3067
			}
3068
		}
3069
	} else {
3070
		if ($isipv6) {
3071
			$interface_list_ips = get_configured_ipv6_addresses();
3072
		} else {
3073
			$interface_list_ips = get_configured_ip_addresses();
3074
		}
3075

    
3076
		foreach ($interface_list_ips as $if => $ilips) {
3077
			if ($ignore_if == $if) {
3078
				continue;
3079
			}
3080
			if (strcasecmp($ipaddr, $ilips) == 0) {
3081
				$where_entry = array();
3082
				$where_entry['if'] = $if;
3083
				$where_entry['ip_or_subnet'] = $ilips;
3084
				$where_configured[] = $where_entry;
3085
			}
3086
		}
3087
	}
3088

    
3089
	if ($check_localip) {
3090
		if (strcasecmp($ipaddr, text_to_compressed_ip6(config_get_path('l2tp/localip', ""))) == 0) {
3091
			$where_entry = array();
3092
			$where_entry['if'] = 'l2tp';
3093
			$where_entry['ip_or_subnet'] = config_get_path('l2tp/localip');
3094
			$where_configured[] = $where_entry;
3095
		}
3096
	}
3097

    
3098
	return $where_configured;
3099
}
3100

    
3101
/****f* pfsense-utils/pfSense_handle_custom_code
3102
 * NAME
3103
 *   pfSense_handle_custom_code
3104
 * INPUTS
3105
 *   directory name to process
3106
 * RESULT
3107
 *   globs the directory and includes the files
3108
 */
3109
function pfSense_handle_custom_code($src_dir) {
3110
	// Allow extending of the nat edit page and include custom input validation
3111
	if (is_dir("$src_dir")) {
3112
		$cf = glob($src_dir . "/*.inc");
3113
		foreach ($cf as $nf) {
3114
			if ($nf == "." || $nf == "..") {
3115
				continue;
3116
			}
3117
			// Include the extra handler
3118
			include_once("$nf");
3119
		}
3120
	}
3121
}
3122

    
3123
function set_language() {
3124
	global $g;
3125

    
3126
	$lang = "";
3127
	if (!empty(config_get_path('system/language'))) {
3128
		$lang = config_get_path('system/language');
3129
	} elseif (!empty(g_get('language'))) {
3130
		$lang = g_get('language');
3131
	}
3132
	$lang .= ".UTF-8";
3133

    
3134
	putenv("LANG={$lang}");
3135
	setlocale(LC_ALL, $lang);
3136
	textdomain("pfSense");
3137
	bindtextdomain("pfSense", "/usr/local/share/locale");
3138
	bind_textdomain_codeset("pfSense", $lang);
3139
}
3140

    
3141
function get_locale_list() {
3142
	$locales = array(
3143
		"bs" => gettext("Bosnian"),
3144
		"zh_CN" => gettext("Chinese"),
3145
		"zh_Hans_CN" => gettext("Chinese (Simplified, China)"),
3146
		"zh_Hans_HK" => gettext("Chinese (Simplified, Hong Kong SAR China)"),
3147
		"zh_Hant_TW" => gettext("Chinese (Traditional, Taiwan)"),
3148
		"nl" => gettext("Dutch"),
3149
		"en_US" => gettext("English"),
3150
		"fr" => gettext("French"),
3151
		"de_DE" => gettext("German (Germany)"),
3152
		"it" => gettext("Italian"),
3153
		"ko" => gettext("Korean"),
3154
		"nb" => gettext("Norwegian Bokmål"),
3155
		"pl" => gettext("Polish"),
3156
		"pt_PT" => gettext("Portuguese"),
3157
		"pt_BR" => gettext("Portuguese (Brazil)"),
3158
		"ru" => gettext("Russian"),
3159
		"es" => gettext("Spanish"),
3160
		"es_AR" => gettext("Spanish (Argentina)"),
3161
	);
3162

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

    
3167
	//asort($locales);
3168

    
3169
	return $locales;
3170
}
3171

    
3172
function return_hex_ipv4($ipv4) {
3173
	if (!is_ipaddrv4($ipv4)) {
3174
		return(false);
3175
	}
3176

    
3177
	/* we need the hex form of the interface IPv4 address */
3178
	$ip4arr = explode(".", $ipv4);
3179
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3180
}
3181

    
3182
function convert_ipv6_to_128bit($ipv6) {
3183
	if (!is_ipaddrv6($ipv6)) {
3184
		return(false);
3185
	}
3186

    
3187
	$ip6arr = array();
3188
	$ip6prefix = Net_IPv6::uncompress($ipv6);
3189
	$ip6arr = explode(":", $ip6prefix);
3190
	/* binary presentation of the prefix for all 128 bits. */
3191
	$ip6prefixbin = "";
3192
	foreach ($ip6arr as $element) {
3193
		$ip6prefixbin .= sprintf("%016b", hexdec($element));
3194
	}
3195
	return($ip6prefixbin);
3196
}
3197

    
3198
function convert_128bit_to_ipv6($ip6bin) {
3199
	if (strlen($ip6bin) <> 128) {
3200
		return(false);
3201
	}
3202

    
3203
	$ip6arr = array();
3204
	$ip6binarr = array();
3205
	$ip6binarr = str_split($ip6bin, 16);
3206
	foreach ($ip6binarr as $binpart) {
3207
		$ip6arr[] = dechex(bindec($binpart));
3208
	}
3209
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3210

    
3211
	return($ip6addr);
3212
}
3213

    
3214

    
3215
/* Returns the calculated bit length of the prefix delegation from the WAN interface */
3216
/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */
3217
/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */
3218
/* 6to4 is 16 bits, e.g. 65535 */
3219
function calculate_ipv6_delegation_length($if) {
3220
	$cfg = config_get_path("interfaces/{$if}");
3221
	if (!is_array($cfg)) {
3222
		return false;
3223
	}
3224

    
3225
	switch ($cfg['ipaddrv6']) {
3226
		case "6to4":
3227
			$pdlen = 16;
3228
			break;
3229
		case "6rd":
3230
			$rd6plen = explode("/", $cfg['prefix-6rd']);
3231
			$pdlen = (64 - ((int) $rd6plen[1] + (32 - (int) $cfg['prefix-6rd-v4plen'])));
3232
			break;
3233
		case "dhcp6":
3234
			$pdlen = $cfg['dhcp6-ia-pd-len'];
3235
			break;
3236
		default:
3237
			$pdlen = 0;
3238
			break;
3239
	}
3240
	return($pdlen);
3241
}
3242

    
3243
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3244
	/* convert zero-value prefix IPv6 addresses with IPv4 mapping to hex
3245
	 * see https://redmine.pfsense.org/issues/12440 */
3246
	$suffix_list = explode(':', $suffix);
3247
	if (is_ipaddrv4($suffix_list[count($suffix_list) - 1])) {
3248
		$hexsuffix = dechex(ip2long($suffix_list[count($suffix_list) - 1]));
3249
		$suffix_list[count($suffix_list) - 1] = substr($hexsuffix, 0, 4);
3250
		$suffix_list[] = substr($hexsuffix, 4, 8);
3251
		$suffix = implode(':', $suffix_list);
3252
	}	
3253
	$prefix = Net_IPv6::uncompress($prefix, true);
3254
	$suffix = Net_IPv6::uncompress($suffix, true);
3255

    
3256
	/*
3257
	 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
3258
	 *                ^^^^ ^
3259
	 *                |||| \-> 64
3260
	 *                |||\---> 63, 62, 61, 60
3261
	 *                ||\----> 56
3262
	 *                |\-----> 52
3263
	 *                \------> 48
3264
	 */
3265

    
3266
	switch ($len) {
3267
	case 48:
3268
		$prefix_len = 15;
3269
		break;
3270
	case 52:
3271
		$prefix_len = 16;
3272
		break;
3273
	case 56:
3274
		$prefix_len = 17;
3275
		break;
3276
	case 59:
3277
	case 60:
3278
		$prefix_len = 18;
3279
		break;
3280
	/*
3281
	 * XXX 63, 62 and 61 should use 18 but PD can change and if
3282
	 * we let user chose this bit it can end up out of PD network
3283
	 *
3284
	 * Leave this with 20 for now until we find a way to let user
3285
	 * chose it. The side-effect is users with PD with one of these
3286
	 * lengths will not be able to setup DHCP server range for full
3287
	 * PD size, only for last /64 network
3288
	 */
3289
	case 63:
3290
	case 62:
3291
	case 61:
3292
	default:
3293
		$prefix_len = 20;
3294
		break;
3295
	}
3296

    
3297
	return text_to_compressed_ip6(substr($prefix, 0, $prefix_len) .
3298
	    substr($suffix, $prefix_len));
3299
}
3300

    
3301
function dhcpv6_pd_str_help($pdlen) {
3302
	$result = '';
3303

    
3304
	switch ($pdlen) {
3305
	case 48:
3306
		$result = '::xxxx:xxxx:xxxx:xxxx:xxxx';
3307
		break;
3308
	case 52:
3309
		$result = '::xxx:xxxx:xxxx:xxxx:xxxx';
3310
		break;
3311
	case 56:
3312
		$result = '::xx:xxxx:xxxx:xxxx:xxxx';
3313
		break;
3314
	case 59:
3315
	case 60:
3316
		$result = '::x:xxxx:xxxx:xxxx:xxxx';
3317
		break;
3318
	/*
3319
	 * XXX 63, 62 and 61 should use same mask as 60 but if
3320
	 * we let the user choose this bit it can end up out of PD network
3321
	 *
3322
	 * Leave this with the same as 64 for now until we find a way to
3323
	 * let the user choose it. The side-effect is users with PD with one
3324
	 * of these lengths will not be able to setup DHCP server ranges
3325
	 * for full PD size, only for last /64 network
3326
	 */
3327
	case 61:
3328
	case 62:
3329
	case 63:
3330
	case 64:
3331
	default:
3332
		$result = '::xxxx:xxxx:xxxx:xxxx';
3333
		break;
3334
	}
3335

    
3336
	return $result;
3337
}
3338

    
3339
function huawei_rssi_to_string($rssi) {
3340
	$dbm = array();
3341
	$i = 0;
3342
	$dbstart = -113;
3343
	while ($i < 32) {
3344
		$dbm[$i] = $dbstart + ($i * 2);
3345
		$i++;
3346
	}
3347
	$percent = round(($rssi / 31) * 100);
3348
	$string = "rssi:{$rssi} level:{$dbm[$rssi]}dBm percent:{$percent}%";
3349
	return $string;
3350
}
3351

    
3352
function huawei_mode_to_string($mode, $submode) {
3353
	$modes[0] = gettext("None");
3354
	$modes[1] = "AMPS";
3355
	$modes[2] = "CDMA";
3356
	$modes[3] = "GSM/GPRS";
3357
	$modes[4] = "HDR";
3358
	$modes[5] = "WCDMA";
3359
	$modes[6] = "GPS";
3360

    
3361
	$submodes[0] = gettext("No Service");
3362
	$submodes[1] = "GSM";
3363
	$submodes[2] = "GPRS";
3364
	$submodes[3] = "EDGE";
3365
	$submodes[4] = "WCDMA";
3366
	$submodes[5] = "HSDPA";
3367
	$submodes[6] = "HSUPA";
3368
	$submodes[7] = "HSDPA+HSUPA";
3369
	$submodes[8] = "TD-SCDMA";
3370
	$submodes[9] = "HSPA+";
3371
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3372
	return $string;
3373
}
3374

    
3375
function huawei_service_to_string($state) {
3376
	$modes[0] = gettext("No Service");
3377
	$modes[1] = gettext("Restricted Service");
3378
	$modes[2] = gettext("Valid Service");
3379
	$modes[3] = gettext("Restricted Regional Service");
3380
	$modes[4] = gettext("Powersaving Service");
3381
	$modes[255] = gettext("Unknown Service");
3382
	$string = $modes[$state];
3383
	return $string;
3384
}
3385

    
3386
function huawei_simstate_to_string($state) {
3387
	$modes[0] = gettext("Invalid SIM/locked State");
3388
	$modes[1] = gettext("Valid SIM State");
3389
	$modes[2] = gettext("Invalid SIM CS State");
3390
	$modes[3] = gettext("Invalid SIM PS State");
3391
	$modes[4] = gettext("Invalid SIM CS/PS State");
3392
	$modes[255] = gettext("Missing SIM State");
3393
	$string = $modes[$state];
3394
	return $string;
3395
}
3396

    
3397
function zte_rssi_to_string($rssi) {
3398
	return huawei_rssi_to_string($rssi);
3399
}
3400

    
3401
function zte_mode_to_string($mode, $submode) {
3402
	$modes[0] = gettext("No Service");
3403
	$modes[1] = gettext("Limited Service");
3404
	$modes[2] = "GPRS";
3405
	$modes[3] = "GSM";
3406
	$modes[4] = "UMTS";
3407
	$modes[5] = "EDGE";
3408
	$modes[6] = "HSDPA";
3409

    
3410
	$submodes[0] = "CS_ONLY";
3411
	$submodes[1] = "PS_ONLY";
3412
	$submodes[2] = "CS_PS";
3413
	$submodes[3] = "CAMPED";
3414
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3415
	return $string;
3416
}
3417

    
3418
function zte_service_to_string($service) {
3419
	$modes[0] = gettext("Initializing Service");
3420
	$modes[1] = gettext("Network Lock error Service");
3421
	$modes[2] = gettext("Network Locked Service");
3422
	$modes[3] = gettext("Unlocked or correct MCC/MNC Service");
3423
	$string = $modes[$service];
3424
	return $string;
3425
}
3426

    
3427
function zte_simstate_to_string($state) {
3428
	$modes[0] = gettext("No action State");
3429
	$modes[1] = gettext("Network lock State");
3430
	$modes[2] = gettext("(U)SIM card lock State");
3431
	$modes[3] = gettext("Network Lock and (U)SIM card Lock State");
3432
	$string = $modes[$state];
3433
	return $string;
3434
}
3435

    
3436
function get_configured_pppoe_server_interfaces() {
3437
	$iflist = array();
3438
	foreach (config_get_path('pppoes/pppoe', []) as $pppoe) {
3439
		if ($pppoe['mode'] == "server") {
3440
			$int = "poes". $pppoe['pppoeid'];
3441
			$iflist[$int] = strtoupper($int);
3442
		}
3443
	}
3444
	return $iflist;
3445
}
3446

    
3447
function get_pppoes_child_interfaces($ifpattern) {
3448
	$if_arr = array();
3449
	if ($ifpattern == "") {
3450
		return;
3451
	}
3452

    
3453
	exec("/sbin/ifconfig", $out, $ret);
3454
	foreach ($out as $line) {
3455
		if (preg_match("/^({$ifpattern}-[0-9]+):/i", $line, $match)) {
3456
			$if_arr[] = $match[1];
3457
		}
3458
	}
3459
	return $if_arr;
3460

    
3461
}
3462

    
3463
/****f* pfsense-utils/pkg_call_plugins
3464
 * NAME
3465
 *   pkg_call_plugins
3466
 * INPUTS
3467
 *   $plugin_type value used to search in package configuration if the plugin is used, also used to create the function name
3468
 *   $plugin_params parameters to pass to the plugin function for passing multiple parameters a array can be used.
3469
 * RESULT
3470
 *   returns associative array results from the plugin calls for each package
3471
 * NOTES
3472
 *   This generic function can be used to notify or retrieve results from functions that are defined in packages.
3473
 ******/
3474
function pkg_call_plugins($plugin_type, $plugin_params) {
3475
	global $g;
3476
	$results = array();
3477
	foreach (config_get_path('installedpackages/package') as $package) {
3478
		foreach (array_get_path($package, 'plugins/item', []) as $plugin) {
3479
			if (!is_array($plugin) || empty($plugin)) {
3480
				continue;
3481
			}
3482
			if ($plugin['type'] == $plugin_type) {
3483
				if (file_exists($package['include_file'])) {
3484
					require_once($package['include_file']);
3485
				} else {
3486
					continue;
3487
				}
3488
				$pkgname = substr(reverse_strrchr($package['configurationfile'], "."), 0, -1);
3489
				$plugin_function = $pkgname . '_'. $plugin_type;
3490
				$results[$pkgname] = call_user_func($plugin_function, $plugin_params);
3491
			}
3492
		}
3493
	}
3494
	return $results;
3495
}
3496

    
3497
// Convert IPv6 addresses to lower case
3498
function addrtolower($ip) {
3499
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3500
		return(strtolower($ip));
3501
	} else {
3502
		return($ip);
3503
	}
3504
}
3505

    
3506
function compare_by_name($a, $b) {
3507
	return strcasecmp($a['name'], $b['name']);
3508
}
3509

    
3510
/****f* pfsense-utils/getarraybyref
3511
 * NAME
3512
 *   getarraybyref
3513
 * INPUTS
3514
 *   $array the array of which a items array needs to be found.
3515
 *   $args.. the sub-items to be retrieved/created
3516
 * RESULT
3517
 *   returns the array that was retrieved from configuration, its created if it does not exist
3518
 * NOTES
3519
 *   Used by haproxy / acme / others.?. .
3520
 *   can be used like this:  $a_certificates = getarraybyref($config, 'installedpackages', 'acme', 'certificates', 'item');
3521
 ******/
3522
function &getarraybyref(&$array) {
3523
	if (!isset($array)) {
3524
		return false;
3525
	}
3526
	if (!is_array($array)) {
3527
		$array = array();
3528
	}
3529
	$item = &$array;
3530
	$arg = func_get_args();
3531
	for($i = 1; $i < count($arg); $i++) {
3532
		$itemindex = $arg[$i];
3533
		if (!is_array($item[$itemindex])) {
3534
			$item[$itemindex] = array();
3535
		}
3536
		$item = &$item[$itemindex];
3537
	}
3538
	return $item;
3539
}
3540

    
3541
/****f* pfsense-utils/send_download_data
3542
 * NAME
3543
 *   send_download_data - Send content to a user's browser as a file to download
3544
 * INPUTS
3545
 *   $type        : The type of download, either 'data' to send the contents of
3546
 *                    a variable or 'file' to send the contents of a file on the
3547
 *                    filesystem.
3548
 *   $content     : For 'data' type, the content to send the user. For 'file'
3549
 *                    type, the full path to the file to send.
3550
 *   $userfilename: The filename presented to the user when downloading. For
3551
 *                    'file' type, this may be omitted and the basename of
3552
 *                    $content will be used instead.
3553
 *   $contenttype : MIME content type of the data. Default "application/octet-stream"
3554
 * RESULT
3555
 *   Sends the data to the browser as a file to download.
3556
 ******/
3557

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

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

    
3565
	/* Cannot determine the filename, so bail. */
3566
	if (empty($name)) {
3567
		exit;
3568
	}
3569

    
3570
	/* Send basic download headers */
3571
	header("Content-Type: {$contenttype}");
3572
	header("Content-Length: {$size}");
3573
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3574

    
3575
	/* Send cache headers */
3576
	if (isset($_SERVER['HTTPS'])) {
3577
		header('Pragma: ');
3578
		header('Cache-Control: ');
3579
	} else {
3580
		header("Pragma: private");
3581
		header("Cache-Control: private, must-revalidate");
3582
	}
3583

    
3584
	/* Ensure output buffering is off so PHP does not consume
3585
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3586
	while (ob_get_level()) {
3587
		@ob_end_clean();
3588
	}
3589

    
3590
	/* Send the data to the user */
3591
	if ($type == 'file') {
3592
		readfile($content);
3593
	} else {
3594
		echo $content;
3595
	}
3596

    
3597
	/* Flush any remaining output buffer */
3598
	@ob_end_flush();
3599
	exit;
3600
}
3601

    
3602
// Test whether the hostname in a URL can be resolved with a very short timeout
3603
function is_url_hostname_resolvable($url) {
3604
	$urlhostname = parse_url($url, PHP_URL_HOST);
3605
	if (empty($urlhostname)) {
3606
		return false;
3607
	}
3608
	putenv("RES_OPTIONS=timeout:3 attempts:1");
3609
	$resolvable = ($urlhostname !== gethostbyname($urlhostname));
3610
	putenv("RES_OPTIONS");
3611
	return $resolvable;
3612
}
3613

    
3614
function get_pf_reserved(?string $name = '', bool $check_ifdescr = true):bool|array {
3615
	global $pf_reserved_keywords, $reserved_table_names, $FilterIflist;
3616

    
3617
	// generate a list of assigned interfaces and interface groups
3618
	$iflist = $FilterIflist;
3619
	if (empty($iflist)) {
3620
		$iflist = config_get_path('interfaces', []);
3621
		foreach (config_get_path('ifgroups/ifgroupentry', []) as $ifgen) {
3622
			$iflist[$ifgen['ifname']] = $ifgen['ifname'];
3623
		}
3624
	}
3625
	$reserved = [];
3626
	foreach ($iflist as $ifconf) {
3627
		if (empty($ifconf['descr'])) {
3628
			continue;
3629
		}
3630
		if ($check_ifdescr) {
3631
			$reserved[] = strtoupper($ifconf['descr']);
3632
		}
3633
		$reserved[] = strtoupper($ifconf['descr'] . '__NETWORK');
3634
	}
3635
	$reserved = array_merge($pf_reserved_keywords, $reserved_table_names, $reserved);
3636

    
3637
	return (empty($name) ? $reserved : in_array(strtoupper($name), $reserved));
3638
}
3639

    
3640
function get_pf_timeouts () {
3641
	$pftimeout = array();
3642
	exec("/sbin/pfctl -st", $pfctlst, $retval);
3643
	if ($retval == 0) {
3644
		foreach ($pfctlst as $pfst) {
3645
			preg_match('/([a-z]+)\.([a-z]+)\s+([0-9]+)/', $pfst, $timeout);
3646
			if ($timeout[1] == "other") {
3647
				$proto = "Other";
3648
			} else {
3649
				$proto = strtoupper($timeout[1]);
3650
			}
3651
			if ($timeout[2] == "finwait") {
3652
				$type = "FIN Wait";
3653
			} else {
3654
				$type = ucfirst($timeout[2]);
3655
			}
3656
			$pftimeout[$proto][$type]['name'] = $proto . " " . $type;
3657
			$pftimeout[$proto][$type]['keyname'] = $timeout[1] . $timeout[2] . "timeout";
3658
			$pftimeout[$proto][$type]['value'] = $timeout[3];
3659
		}
3660
	}
3661
	return $pftimeout;
3662
}
3663

    
3664
function set_curlproxy(&$ch) {
3665
	if (!empty(config_get_path('system/proxyurl'))) {
3666
		curl_setopt($ch, CURLOPT_PROXY, config_get_path('system/proxyurl'));
3667
		if (!empty(config_get_path('system/proxyport'))) {
3668
			curl_setopt($ch, CURLOPT_PROXYPORT, config_get_path('system/proxyport'));
3669
		}
3670
		$proxyuser = config_get_path('system/proxyuser');
3671
		$proxypass = config_get_path('system/proxypass');
3672
		if (!empty($proxyuser) && !empty($proxypass)) {
3673
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
3674
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$proxyuser}:{$proxypass}");
3675
		}
3676
	}
3677
}
3678
?>
(39-39/61)