Project

General

Profile

Download (103 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-2022 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
 * convert_ip_to_network_format($ip, $subnet): converts an ip address to network form
704

    
705
 */
706
function convert_ip_to_network_format($ip, $subnet) {
707
	$ipsplit = explode('.', $ip);
708
	$string = $ipsplit[0] . "." . $ipsplit[1] . "." . $ipsplit[2] . ".0/" . $subnet;
709
	return $string;
710
}
711

    
712
/*
713
 * get_carp_interface_status($carpid): returns the status of a carp uniqid
714
 */
715
function get_carp_interface_status($carpid) {
716

    
717
	$carpiface = get_configured_vip_interface($carpid);
718
	if ($carpiface == NULL)
719
		return "";
720
	$interface = get_real_interface($carpiface);
721
	if ($interface == NULL)
722
		return "";
723
	$vip = get_configured_vip($carpid);
724
	if ($vip == NULL || !isset($vip['vhid']))
725
		return "";
726

    
727
	$vhid = $vip['vhid'];
728
	$carp_query = [];
729
	exec("/sbin/ifconfig {$interface} | /usr/bin/grep \"carp:.* vhid {$vhid} \"", $carp_query);
730
	foreach ($carp_query as $int) {
731
		if (stripos($int, "MASTER"))
732
			return "MASTER";
733
		elseif (stripos($int, "BACKUP"))
734
			return "BACKUP";
735
		elseif (stripos($int, "INIT"))
736
			return "INIT";
737
	}
738

    
739
	return "";
740
}
741

    
742
function get_carp_bind_status($interface) {
743
	$carpstatus = get_carp_interface_status($interface);
744
	if (!empty($carpstatus)) {
745
		return $carpstatus;
746
	} else {
747
		foreach (config_get_path('virtualip/vip', []) as $vip) {
748
			if ($interface == "_vip{$vip['uniqid']}") { 
749
				return get_carp_interface_status($vip['interface']);
750
			}
751
		}
752
	}
753
}
754

    
755
/*
756
 * Return true if the CARP status of at least one interface of a captive portal zone is in backup mode
757
 * This function return false if CARP is not enabled on any interface of the captive portal zone
758
 */
759
function captiveportal_ha_is_node_in_backup_mode($cpzone) {
760
	$cpinterfaces = explode(",", config_get_path("captiveportal/{$cpzone}/interface", ""));
761

    
762
	foreach ($cpinterfaces as $interface) {
763
		foreach (config_get_path('virtualip/vip', []) as $vip) {
764
			if (($vip['interface'] == $interface) && ($vip['mode'] == "carp")) {
765
				if (get_carp_interface_status("_vip{$vip['uniqid']}") != "MASTER") {
766
					return true;
767
				}
768
			}
769
		}
770
	}
771
	return false;
772
}
773

    
774
/****f* pfsense-utils/WakeOnLan
775
 * NAME
776
 *   WakeOnLan - Wake a machine up using the wake on lan format/protocol
777
 * RESULT
778
 *   true/false - true if the operation was successful
779
 ******/
780
function WakeOnLan($addr, $mac) {
781
	$addr_byte = explode(':', $mac);
782
	$hw_addr = '';
783

    
784
	for ($a = 0; $a < 6; $a++) {
785
		$hw_addr .= chr(hexdec($addr_byte[$a]));
786
	}
787

    
788
	$msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255);
789

    
790
	for ($a = 1; $a <= 16; $a++) {
791
		$msg .= $hw_addr;
792
	}
793

    
794
	// send it to the broadcast address using UDP
795
	$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
796
	if ($s == false) {
797
		log_error(gettext("Error creating socket!"));
798
		log_error(sprintf(gettext("Error code is '%1\$s' - %2\$s"), socket_last_error(), socket_strerror(socket_last_error())));
799
	} else {
800
		// setting a broadcast option to socket:
801
		$opt_ret = socket_set_option($s, 1, 6, TRUE);
802
		if ($opt_ret < 0) {
803
			log_error(sprintf(gettext("setsockopt() failed, error: %s"),
804
							  socket_strerror(socket_last_error($s))));
805
		}
806
		$e = socket_sendto($s, $msg, strlen($msg), 0, $addr, 2050);
807
		socket_close($s);
808
		log_error(sprintf(gettext('Magic Packet sent (%1$s) to (%2$s) MAC=%3$s'), $e, $addr, $mac));
809
		return true;
810
	}
811

    
812
	return false;
813
}
814

    
815
/*
816
 * reverse_strrchr($haystack, $needle):  Return everything in $haystack up to the *last* instance of $needle.
817
 *					 Useful for finding paths and stripping file extensions.
818
 */
819
function reverse_strrchr($haystack, $needle) {
820
	if (!is_string($haystack)) {
821
		return;
822
	}
823
	return strrpos($haystack, $needle) ? substr($haystack, 0, strrpos($haystack, $needle) +1) : false;
824
}
825

    
826
/*
827
 *  backup_config_section($section): returns as an xml file string of
828
 *                                   the configuration section
829
 */
830
function backup_config_section($section_name) {
831
	$new_section = config_get_path($section_name, []);
832
	/* generate configuration XML */
833
	$xmlconfig = dump_xml_config($new_section, $section_name);
834
	$xmlconfig = str_replace("<?xml version=\"1.0\"?>", "", $xmlconfig);
835
	return $xmlconfig;
836
}
837

    
838
/*
839
 *  restore_config_section($section_name, new_contents): restore a configuration section,
840
 *                                                  and write the configuration out
841
 *                                                  to disk/cf.
842
 */
843
function restore_config_section($section_name, $new_contents) {
844
	global $g;
845
	$fout = fopen("{$g['tmp_path']}/tmpxml", "w");
846
	fwrite($fout, $new_contents);
847
	fclose($fout);
848

    
849
	$xml = parse_xml_config($g['tmp_path'] . "/tmpxml", null);
850
	if ($xml['pfsense']) {
851
		$xml = $xml['pfsense'];
852
	}
853
	if ($xml[$section_name]) {
854
		$section_xml = $xml[$section_name];
855
	} else {
856
		$section_xml = -1;
857
	}
858

    
859
	@unlink($g['tmp_path'] . "/tmpxml");
860
	if ($section_xml === -1) {
861
		return false;
862
	}
863

    
864
	/* Save current pkg repo to re-add on new config */
865
	unset($pkg_repo_conf_path);
866
	if ($section_name == "system" &&
867
	    config_get_path('system/pkg_repo_conf_path')) {
868
		$pkg_repo_conf_path = config_get_path('system/pkg_repo_conf_path');
869
	}
870

    
871
	config_set_path($section_name, $section_xml);
872
	if (file_exists("{$g['tmp_path']}/config.cache")) {
873
		unlink("{$g['tmp_path']}/config.cache");
874
	}
875

    
876
	/* Restore previously pkg repo configured */
877
	if ($section_name == "system") {
878
		if (isset($pkg_repo_conf_path)) {
879
			config_set_path('system/pkg_repo_conf_path', $pkg_repo_conf_path);
880
		} elseif (config_get_path('system/pkg_repo_conf_path')) {
881
			config_del_path(('system/pkg_repo_conf_path'));
882
		}
883
	}
884

    
885
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
886
	disable_security_checks();
887
	return true;
888
}
889

    
890
/*
891
 *  merge_config_section($section_name, new_contents):   restore a configuration section,
892
 *                                                  and write the configuration out
893
 *                                                  to disk/cf.  But preserve the prior
894
 * 													structure if needed
895
 */
896
function merge_config_section($section_name, $new_contents) {
897
	$fname = get_tmp_file();
898
	$fout = fopen($fname, "w");
899
	fwrite($fout, $new_contents);
900
	fclose($fout);
901
	$section_xml = parse_xml_config($fname, $section_name);
902
	config_set_path($section_name, $section_xml);
903
	unlink($fname);
904
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
905
	disable_security_checks();
906
	return;
907
}
908

    
909
/*
910
 * rmdir_recursive($path, $follow_links=false)
911
 * Recursively remove a directory tree (rm -rf path)
912
 * This is for directories _only_
913
 */
914
function rmdir_recursive($path, $follow_links=false) {
915
	$to_do = glob($path);
916
	if (!is_array($to_do)) {
917
		$to_do = array($to_do);
918
	}
919
	foreach ($to_do as $workingdir) { // Handle wildcards by foreaching.
920
		if (file_exists($workingdir)) {
921
			if (is_dir($workingdir)) {
922
				$dir = opendir($workingdir);
923
				while ($entry = readdir($dir)) {
924
					if (is_file("$workingdir/$entry") || ((!$follow_links) && is_link("$workingdir/$entry"))) {
925
						unlink("$workingdir/$entry");
926
					} elseif (is_dir("$workingdir/$entry") && $entry != '.' && $entry != '..') {
927
						rmdir_recursive("$workingdir/$entry");
928
					}
929
				}
930
				closedir($dir);
931
				rmdir($workingdir);
932
			} elseif (is_file($workingdir)) {
933
				unlink($workingdir);
934
			}
935
		}
936
	}
937
	return;
938
}
939

    
940
/*
941
 * host_firmware_version(): Return the versions used in this install
942
 */
943
function host_firmware_version() {
944
	global $g;
945

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

    
948
	return array(
949
		"firmware" => array("version" => $g['product_version']),
950
		"kernel"   => array("version" => $os_version),
951
		"base"     => array("version" => $os_version),
952
		"platform" => $g['product_label'],
953
		"config_version" => config_get_path('version')
954
	);
955
}
956

    
957
function get_disk_info() {
958
	$diskout = "";
959
	exec("/bin/df -h | /usr/bin/grep -w '/' | /usr/bin/awk '{ print $2, $3, $4, $5 }'", $diskout);
960
	return explode(' ', $diskout[0]);
961
}
962

    
963
/****f* pfsense-utils/strncpy
964
 * NAME
965
 *   strncpy - copy strings
966
 * INPUTS
967
 *   &$dst, $src, $length
968
 * RESULT
969
 *   none
970
 ******/
971
function strncpy(&$dst, $src, $length) {
972
	if (strlen($src) > $length) {
973
		$dst = substr($src, 0, $length);
974
	} else {
975
		$dst = $src;
976
	}
977
}
978

    
979
/****f* pfsense-utils/reload_interfaces_sync
980
 * NAME
981
 *   reload_interfaces - reload all interfaces
982
 * INPUTS
983
 *   none
984
 * RESULT
985
 *   none
986
 ******/
987
function reload_interfaces_sync() {
988
	global $config, $g;
989

    
990
	if ($g['debug']) {
991
		log_error(gettext("reload_interfaces_sync() is starting."));
992
	}
993

    
994
	/* parse config.xml again */
995
	$config = parse_config(true);
996

    
997
	/* enable routing */
998
	system_routing_enable();
999
	if ($g['debug']) {
1000
		log_error(gettext("Enabling system routing"));
1001
	}
1002

    
1003
	if ($g['debug']) {
1004
		log_error(gettext("Cleaning up Interfaces"));
1005
	}
1006

    
1007
	/* set up interfaces */
1008
	interfaces_configure();
1009
}
1010

    
1011
/****f* pfsense-utils/reload_all
1012
 * NAME
1013
 *   reload_all - triggers a reload of all settings
1014
 *   * INPUTS
1015
 *   none
1016
 * RESULT
1017
 *   none
1018
 ******/
1019
function reload_all() {
1020
	send_event("service reload all");
1021
}
1022

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

    
1035
/****f* pfsense-utils/reload_all_sync
1036
 * NAME
1037
 *   reload_all - reload all settings
1038
 *   * INPUTS
1039
 *   none
1040
 * RESULT
1041
 *   none
1042
 ******/
1043
function reload_all_sync() {
1044
	global $config;
1045

    
1046
	/* parse config.xml again */
1047
	$config = parse_config(true);
1048

    
1049
	/* set up our timezone */
1050
	system_timezone_configure();
1051

    
1052
	/* set up our hostname */
1053
	system_hostname_configure();
1054

    
1055
	/* make hosts file */
1056
	system_hosts_generate();
1057

    
1058
	/* generate resolv.conf */
1059
	system_resolvconf_generate();
1060

    
1061
	/* enable routing */
1062
	system_routing_enable();
1063

    
1064
	/* set up interfaces */
1065
	interfaces_configure();
1066

    
1067
	/* start dyndns service */
1068
	services_dyndns_configure();
1069

    
1070
	/* configure cron service */
1071
	configure_cron();
1072

    
1073
	/* start the NTP client */
1074
	system_ntp_configure();
1075

    
1076
	/* sync pw database */
1077
	unlink_if_exists("/etc/spwd.db.tmp");
1078
	mwexec("/usr/sbin/pwd_mkdb -d /etc/ /etc/master.passwd");
1079

    
1080
	/* restart sshd */
1081
	send_event("service restart sshd");
1082

    
1083
	/* restart webConfigurator if needed */
1084
	send_event("service restart webgui");
1085
}
1086

    
1087
function load_loader_conf($loader_conf = NULL, $local = false) {
1088

    
1089
	if ($loader_conf == NULL) {
1090
		return (NULL);
1091
	}
1092
	if (file_exists($loader_conf)) {
1093
		$input = file_get_contents($loader_conf);
1094
	} else {
1095
		$input = "";
1096
	}
1097

    
1098
	$input_split = explode("\n", $input);
1099

    
1100
	/*
1101
	 * Loop through and only add lines that are not empty and not
1102
	 * managed by us.
1103
	 */
1104
	$data = array();
1105
	/* These values should be removed from loader.conf and loader.conf.local
1106
	 * As they will be replaced when necessary. */
1107
	$remove = array("hw.usb.no_pf", "hint.mdio.0.at", "hint.e6000sw.0",
1108
	    "hw.e6000sw.default_disabled", "vm.pmap.pti",
1109
	    "net.pf.request_maxcount", "hw.hn.vf_transparent",
1110
	    "hw.hn.use_if_start");
1111
	if (!$local) {
1112
		/* These values should only be filtered in loader.conf, not .local */
1113
		$remove = array_merge($remove,
1114
		    array("autoboot_delay", "console", "comconsole_speed", "comconsole_port",
1115
		    "boot_multicons", "boot_serial", "hint.uart.0.flags",
1116
		    "hint.uart.1.flags", "net.link.ifqmaxlen"));
1117
	}
1118
	foreach ($input_split as $line) {
1119
		if (empty($line)) {
1120
			continue;
1121
		}
1122
		$skip = false;
1123
		$name = explode('=', $line, 2)[0];
1124
		foreach($remove as $rkey) {
1125
			if (strncasecmp(trim($name), $rkey, strlen($rkey)) == 0) {
1126
				$skip = true;
1127
				break;
1128
			}
1129
		}
1130
		if (!$skip) {
1131
			$data[] = $line;
1132
		}
1133
	}
1134

    
1135
	return ($data);
1136
}
1137

    
1138
function setup_loader_settings($path = "", $upgrade = false) {
1139
	global $g;
1140

    
1141
	$boot_config_file = "{$path}/boot.config";
1142
	$loader_conf_file = "{$path}/boot/loader.conf";
1143

    
1144
	$serialspeed = (config_get_path('system/serialspeed', 115200));
1145

    
1146
	$vga_only = false;
1147
	$serial_only = false;
1148
	$specific_platform = system_identify_specific_platform();
1149
	$video_console_type = (get_single_sysctl("machdep.bootmethod") == "UEFI") ? "efi" : "vidconsole";
1150
	if ($specific_platform['name'] == '1540') {
1151
		$vga_only = true;
1152
	} elseif ($specific_platform['name'] == 'Turbot Dual-E') {
1153
		$g['primaryconsole_force'] = "video";
1154
	} elseif ($specific_platform['name'] == 'RCC-VE' ||
1155
	    $specific_platform['name'] == 'RCC' ||
1156
	    $specific_platform['name'] == 'SG-2220' ||
1157
	    $specific_platform['name'] == 'apu2') {
1158
		$serial_only = true;
1159
	}
1160

    
1161
	/* Serial console - write out /boot.config */
1162
	if (file_exists($boot_config_file)) {
1163
		$boot_config = file_get_contents($boot_config_file);
1164
	} else {
1165
		$boot_config = "";
1166
	}
1167
	$boot_config_split = explode("\n", $boot_config);
1168
	$data = array();
1169
	foreach ($boot_config_split as $bcs) {
1170
		/* Ignore -D and -h lines now */
1171
		if (!empty($bcs) && !stristr($bcs, "-D") &&
1172
		    !stristr($bcs, "-h")) {
1173
			$data[] = $bcs;
1174
		}
1175
	}
1176
	if ($serial_only === true) {
1177
		$data[] = "-S{$serialspeed} -h";
1178
	} elseif (is_serial_enabled()) {
1179
		$data[] = "-S{$serialspeed} -D";
1180
	}
1181

    
1182
	if (empty($data)) {
1183
		@unlink($boot_config_file);
1184
	} else {
1185
		safe_write_file($boot_config_file, $data);
1186
	}
1187
	unset($data, $boot_config, $boot_config_file, $boot_config_split);
1188

    
1189
	/* Serial console - write out /boot/loader.conf */
1190
	if ($upgrade) {
1191
		system("echo \"Reading {$loader_conf_file}...\" >> /conf/upgrade_log.txt");
1192
	}
1193

    
1194
	$data = load_loader_conf($loader_conf_file, false);
1195
	if ($serial_only === true) {
1196
		$data[] = 'boot_serial="YES"';
1197
		$data[] = 'console="comconsole"';
1198
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1199
	} elseif ($vga_only === true) {
1200
		$data[] = "console=\"{$video_console_type}\"";
1201
	} elseif (is_serial_enabled()) {
1202
		$data[] = 'boot_multicons="YES"';
1203
		$data[] = 'boot_serial="YES"';
1204
		$primaryconsole = isset($g['primaryconsole_force']) ?
1205
		    $g['primaryconsole_force'] :
1206
		    config_get_path('system/primaryconsole');
1207
		switch ($primaryconsole) {
1208
			case "video":
1209
				$data[] = "console=\"{$video_console_type},comconsole\"";
1210
				break;
1211
			case "serial":
1212
			default:
1213
				$data[] = "console=\"comconsole,{$video_console_type}\"";
1214
		}
1215
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1216
	}
1217

    
1218
	if ($specific_platform['name'] == 'RCC-VE' ||
1219
	    $specific_platform['name'] == 'RCC' ||
1220
	    $specific_platform['name'] == 'SG-2220') {
1221
		$data[] = 'comconsole_port="0x2F8"';
1222
		$data[] = 'hint.uart.0.flags="0x00"';
1223
		$data[] = 'hint.uart.1.flags="0x10"';
1224
	}
1225
	$data[] = 'autoboot_delay="3"';
1226
	if (config_path_enabled('system','pti_disabled')) {
1227
		$data[] = 'vm.pmap.pti="0"';
1228
	}
1229

    
1230
	/* Enable ALTQ support for hnX NICs. */
1231
	if (config_path_enabled('system','hn_altq_enable')) {
1232
		$data[] = 'hw.hn.vf_transparent="0"';
1233
		$data[] = 'hw.hn.use_if_start="1"';
1234
	}
1235

    
1236
	/* Set maximum send queue length. */
1237
	$data[] = 'net.link.ifqmaxlen="128"';
1238

    
1239
	safe_write_file($loader_conf_file, $data);
1240

    
1241
	/* Filter loader.conf.local to avoid duplicate settings. */
1242
	$loader_conf_file = "{$path}/boot/loader.conf.local";
1243
	$data = load_loader_conf($loader_conf_file, true);
1244
	if (empty($data)) {
1245
		@unlink($loader_conf_file);
1246
	} else {
1247
		safe_write_file($loader_conf_file, $data);
1248
	}
1249

    
1250
}
1251

    
1252
function console_configure($when = "save", $path = "") {
1253
	$ttys_file = "{$path}/etc/ttys";
1254

    
1255
	/* Update the loader settings. */
1256
	setup_loader_settings($path, ($when == "upgrade"));
1257

    
1258
	$ttys = file_get_contents($ttys_file);
1259
	$ttys_split = explode("\n", $ttys);
1260

    
1261
	$data = array();
1262

    
1263
	$on_off = (is_serial_enabled() ? 'onifconsole' : 'off');
1264

    
1265
	if (config_path_enabled('system','disableconsolemenu')) {
1266
		$console_type = 'Pc';
1267
		$serial_type = '3wire';
1268
	} else {
1269
		$console_type = 'al.Pc';
1270
		$serial_type = 'al.3wire';
1271
	}
1272

    
1273
	$console_line = "console\tnone\t\t\t\tunknown\toff\tsecure";
1274
	$virt_line =
1275
	    "\"/usr/libexec/getty {$console_type}\"\txterm\tonifexists secure";
1276
	$ttyu_line =
1277
	    "\"/usr/libexec/getty {$serial_type}\"\tvt100\t{$on_off}\tsecure";
1278

    
1279
	$found = array();
1280

    
1281
	foreach ($ttys_split as $tty) {
1282
		/* Ignore blank lines */
1283
		if (empty($tty)) {
1284
			continue;
1285
		}
1286

    
1287
		if (stristr($tty, "ttyv0")) {
1288
			$found['ttyv0'] = 1;
1289
			$data[] = "ttyv0\t{$virt_line}";
1290
		} elseif (stristr($tty, "xc0")) {
1291
			$found['xc0'] = 1;
1292
			$data[] = "xc0\t{$virt_line}";
1293
		} elseif (stristr($tty, "ttyu")) {
1294
			$ttyn = substr($tty, 0, 5);
1295
			$found[$ttyn] = 1;
1296
			$data[] = "{$ttyn}\t{$ttyu_line}";
1297
		} elseif (substr($tty, 0, 7) == 'console') {
1298
			$found['console'] = 1;
1299
			$data[] = $tty;
1300
		} else {
1301
			$data[] = $tty;
1302
		}
1303
	}
1304
	unset($on_off, $console_type, $serial_type);
1305

    
1306
	/* Detect missing main lines on original file and try to rebuild it */
1307
	$items = array(
1308
		'console',
1309
		'ttyv0',
1310
		'ttyu0',
1311
		'ttyu1',
1312
		'ttyu2',
1313
		'ttyu3',
1314
		'xc0'
1315
	);
1316

    
1317
	foreach ($items as $item) {
1318
		if (isset($found[$item])) {
1319
			continue;
1320
		}
1321

    
1322
		if ($item == 'console') {
1323
			$data[] = $console_line;
1324
		} elseif (($item == 'ttyv0') || ($item == 'xc0')) {
1325
			/* xc0 - Xen console, see https://redmine.pfsense.org/issues/11402 */
1326
			$data[] = "{$item}\t{$virt_line}";
1327
		} else {
1328
			$data[] = "{$item}\t{$ttyu_line}";
1329
		}
1330
	}
1331

    
1332
	safe_write_file($ttys_file, $data);
1333

    
1334
	unset($ttys, $ttys_file, $ttys_split, $data);
1335

    
1336
	if ($when != "upgrade") {
1337
		reload_ttys();
1338
	}
1339

    
1340
	return;
1341
}
1342

    
1343
function is_serial_enabled() {
1344
	global $g;
1345

    
1346
	if (!isset($g['enableserial_force']) &&
1347
	    !config_path_enabled('system','enableserial')) {
1348
		return false;
1349
	}
1350

    
1351
	return true;
1352
}
1353

    
1354
function reload_ttys() {
1355
	// Send a HUP signal to init will make it reload /etc/ttys
1356
	posix_kill(1, SIGHUP);
1357
}
1358

    
1359
function print_value_list($list, $count = 10, $separator = ",") {
1360
	$ret = implode($separator, array_slice($list, 0, $count));
1361
	if (count($list) < $count) {
1362
		$ret .= ".";
1363
	} else {
1364
		$ret .= "...";
1365
	}
1366
	return $ret;
1367
}
1368

    
1369
/* DHCP enabled on any interfaces? */
1370
function is_dhcp_server_enabled() {
1371
	foreach (config_get_path('dhcpd', []) as $dhcpif => $dhcpifconf) {
1372
		if (isset($dhcpifconf['enable']) &&
1373
			!empty(config_get_path("interfaces/{$dhcpif}"))) {
1374
			return true;
1375
		}
1376
	}
1377

    
1378
	return false;
1379
}
1380

    
1381
/* DHCP enabled on any interfaces? */
1382
function is_dhcpv6_server_enabled() {
1383
	foreach (config_get_path('interfaces', []) as $ifcfg) {
1384
		if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface'])) {
1385
			return true;
1386
		}
1387
	}
1388

    
1389
	foreach (config_get_path('dhcpdv6', []) as $dhcpv6if => $dhcpv6ifconf) {
1390
		if (isset($dhcpv6ifconf['enable']) &&
1391
			!empty(config_get_path("interfaces/{$dhcpv6if}"))) {
1392
			return true;
1393
		}
1394
	}
1395

    
1396
	return false;
1397
}
1398

    
1399
/* radvd enabled on any interfaces? */
1400
function is_radvd_enabled() {
1401
	init_config_arr(['dhcpdv6']);
1402
	$dhcpdv6cfg = config_get_path('dhcpdv6', []);
1403
	$Iflist = get_configured_interface_list();
1404

    
1405
	/* handle manually configured DHCP6 server settings first */
1406
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1407
		if (config_path_enabled("interfaces/{$dhcpv6if}")) {
1408
			continue;
1409
		}
1410

    
1411
		if (!isset($dhcpv6ifconf['ramode'])) {
1412
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
1413
		}
1414

    
1415
		if ($dhcpv6ifconf['ramode'] == "disabled") {
1416
			continue;
1417
		}
1418

    
1419
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1420
		if (!is_ipaddrv6($ifcfgipv6)) {
1421
			continue;
1422
		}
1423

    
1424
		return true;
1425
	}
1426

    
1427
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
1428
	foreach (array_keys($Iflist) as $if) {
1429
		if (!config_path_enabled("interfaces/{$if}", 'track6-interface')) {
1430
			continue;
1431
		}
1432
		if (!config_path_enabled("interfaces/{$if}")) {
1433
			continue;
1434
		}
1435

    
1436
		$ifcfgipv6 = get_interface_ipv6($if);
1437
		if (!is_ipaddrv6($ifcfgipv6)) {
1438
			continue;
1439
		}
1440

    
1441
		$ifcfgsnv6 = get_interface_subnetv6($if);
1442
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1443

    
1444
		if (!is_ipaddrv6($subnetv6)) {
1445
			continue;
1446
		}
1447

    
1448
		return true;
1449
	}
1450

    
1451
	return false;
1452
}
1453

    
1454
/* Any PPPoE servers enabled? */
1455
function is_pppoe_server_enabled() {
1456
	$pppoeenable = false;
1457

    
1458
	foreach (config_get_path('pppoes/pppoe', []) as $pppoes) {
1459
		if ($pppoes['mode'] == 'server') {
1460
			$pppoeenable = true;
1461
		}
1462
	}
1463

    
1464
	return $pppoeenable;
1465
}
1466

    
1467
/* Optional arg forces hh:mm:ss without days */
1468
function convert_seconds_to_dhms($sec, $showhoursonly = false) {
1469
	if (!is_numericint($sec)) {
1470
		return '-';
1471
	}
1472
	// FIXME: When we move to PHP 7 we can use "intdiv($sec % X, Y)" etc
1473
	list($d, $h, $m, $s) = array(	(int)($showhoursonly ? 0 : $sec/86400),
1474
					(int)(($showhoursonly ? $sec : $sec % 86400)/3600),
1475
					(int)(($sec % 3600)/60),
1476
					$sec % 60
1477
				);
1478
	return ($d > 0 ? $d . 'd ' : '') . sprintf('%02d:%02d:%02d', $h, $m, $s);
1479
}
1480

    
1481
/* Compute the total uptime from the ppp uptime log file in the conf directory */
1482

    
1483
function get_ppp_uptime($port) {
1484
	if (file_exists("/conf/{$port}.log")) {
1485
		$saved_time = file_get_contents("/conf/{$port}.log");
1486
		$uptime_data = explode("\n", $saved_time);
1487
		$sec = 0;
1488
		foreach ($uptime_data as $upt) {
1489
			$sec += substr($upt, 1 + strpos($upt, " "));
1490
		}
1491
		return convert_seconds_to_dhms($sec);
1492
	} else {
1493
		$total_time = gettext("No history data found!");
1494
		return $total_time;
1495
	}
1496
}
1497

    
1498
//returns interface information
1499
function get_interface_info($ifdescr) {
1500
	global $g;
1501

    
1502
	$ifinfo = array();
1503
	if (empty(config_get_path("interfaces/{$ifdescr}"))) {
1504
		return;
1505
	}
1506
	$ifinfo['hwif'] = config_get_path("interfaces/{$ifdescr}/if");
1507
	$ifinfo['enable'] = config_path_enabled("interfaces/{$ifdescr}");
1508
	$ifinfo['if'] = get_real_interface($ifdescr);
1509

    
1510
	$chkif = $ifinfo['if'];
1511
	$ifinfotmp = get_interface_addresses($chkif);
1512
	$ifinfo['status'] = $ifinfotmp['status'];
1513
	if (empty($ifinfo['status'])) {
1514
		$ifinfo['status'] = "down";
1515
	}
1516
	$ifinfo['macaddr'] = $ifinfotmp['macaddr'];
1517
	$ifinfo['mtu'] = $ifinfotmp['mtu'];
1518
	$ifinfo['ipaddr'] = $ifinfotmp['ipaddr'];
1519
	$ifinfo['subnet'] = $ifinfotmp['subnet'];
1520
	$ifinfo['linklocal'] = get_interface_linklocal($ifdescr);
1521
	$ifinfo['ipaddrv6'] = get_interface_ipv6($ifdescr);
1522
	$ifinfo['subnetv6'] = get_interface_subnetv6($ifdescr);
1523
	if (isset($ifinfotmp['link0'])) {
1524
		$link0 = "down";
1525
	}
1526
	$ifinfotmp = pfSense_get_interface_stats($chkif);
1527
	// $ifinfo['inpkts'] = $ifinfotmp['inpkts'];
1528
	// $ifinfo['outpkts'] = $ifinfotmp['outpkts'];
1529
	$ifinfo['inerrs'] = $ifinfotmp['inerrs'];
1530
	$ifinfo['outerrs'] = $ifinfotmp['outerrs'];
1531
	$ifinfo['collisions'] = $ifinfotmp['collisions'];
1532

    
1533
	/* Use pfctl for non wrapping 64 bit counters */
1534
	/* Pass */
1535
	exec("/sbin/pfctl -vvsI -i {$chkif}", $pfctlstats);
1536
	$pf_in4_pass = preg_split("/ +/ ", $pfctlstats[3]);
1537
	$pf_out4_pass = preg_split("/ +/", $pfctlstats[5]);
1538
	$pf_in6_pass = preg_split("/ +/ ", $pfctlstats[7]);
1539
	$pf_out6_pass = preg_split("/ +/", $pfctlstats[9]);
1540
	$in4_pass = $pf_in4_pass[5];
1541
	$out4_pass = $pf_out4_pass[5];
1542
	$in4_pass_packets = $pf_in4_pass[3];
1543
	$out4_pass_packets = $pf_out4_pass[3];
1544
	$in6_pass = $pf_in6_pass[5];
1545
	$out6_pass = $pf_out6_pass[5];
1546
	$in6_pass_packets = $pf_in6_pass[3];
1547
	$out6_pass_packets = $pf_out6_pass[3];
1548
	$ifinfo['inbytespass'] = $in4_pass + $in6_pass;
1549
	$ifinfo['outbytespass'] = $out4_pass + $out6_pass;
1550
	$ifinfo['inpktspass'] = $in4_pass_packets + $in6_pass_packets;
1551
	$ifinfo['outpktspass'] = $out4_pass_packets + $out6_pass_packets;
1552

    
1553
	/* Block */
1554
	$pf_in4_block = preg_split("/ +/", $pfctlstats[4]);
1555
	$pf_out4_block = preg_split("/ +/", $pfctlstats[6]);
1556
	$pf_in6_block = preg_split("/ +/", $pfctlstats[8]);
1557
	$pf_out6_block = preg_split("/ +/", $pfctlstats[10]);
1558
	$in4_block = $pf_in4_block[5];
1559
	$out4_block = $pf_out4_block[5];
1560
	$in4_block_packets = $pf_in4_block[3];
1561
	$out4_block_packets = $pf_out4_block[3];
1562
	$in6_block = $pf_in6_block[5];
1563
	$out6_block = $pf_out6_block[5];
1564
	$in6_block_packets = $pf_in6_block[3];
1565
	$out6_block_packets = $pf_out6_block[3];
1566
	$ifinfo['inbytesblock'] = $in4_block + $in6_block;
1567
	$ifinfo['outbytesblock'] = $out4_block + $out6_block;
1568
	$ifinfo['inpktsblock'] = $in4_block_packets + $in6_block_packets;
1569
	$ifinfo['outpktsblock'] = $out4_block_packets + $out6_block_packets;
1570

    
1571
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1572
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1573
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1574
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1575

    
1576
	$link_type = config_get_path("interfaces/{$ifdescr}/ipaddr");
1577
	switch ($link_type) {
1578
		/* DHCP? -> see if dhclient is up */
1579
		case "dhcp":
1580
			/* see if dhclient is up */
1581
			if (find_dhclient_process($ifinfo['if']) != 0) {
1582
				$ifinfo['dhcplink'] = "up";
1583
			} else {
1584
				$ifinfo['dhcplink'] = "down";
1585
			}
1586

    
1587
			break;
1588
		/* PPPoE/PPTP/L2TP interface? -> get status from virtual interface */
1589
		case "pppoe":
1590
		case "pptp":
1591
		case "l2tp":
1592
			if ($ifinfo['status'] == "up" && !isset($link0)) {
1593
				/* get PPPoE link status for dial on demand */
1594
				$ifinfo["{$link_type}link"] = "up";
1595
			} else {
1596
				$ifinfo["{$link_type}link"] = "down";
1597
			}
1598

    
1599
			break;
1600
		/* PPP interface? -> get uptime for this session and cumulative uptime from the persistent log file in conf */
1601
		case "ppp":
1602
			if ($ifinfo['status'] == "up") {
1603
				$ifinfo['ppplink'] = "up";
1604
			} else {
1605
				$ifinfo['ppplink'] = "down" ;
1606
			}
1607

    
1608
			if (empty($ifinfo['status'])) {
1609
				$ifinfo['status'] = "down";
1610
			}
1611

    
1612
			foreach (config_get_path('ppps/ppp', []) as $ppp) {
1613
				if (config_get_path("interfaces/{$ifdescr}/if") == $ppp['if']) {
1614
					break;
1615
				}
1616
			}
1617
			$dev = $ppp['ports'];
1618
			if (config_get_path("interfaces/{$ifdescr}/if") != $ppp['if'] || empty($dev)) {
1619
				break;
1620
			}
1621
			if (!file_exists($dev)) {
1622
				$ifinfo['nodevice'] = 1;
1623
				$ifinfo['pppinfo'] = $dev . " " . gettext("device not present! Is the modem attached to the system?");
1624
			}
1625

    
1626
			$usbmodemoutput = array();
1627
			exec("/usr/sbin/usbconfig", $usbmodemoutput);
1628
			$mondev = "{$g['tmp_path']}/3gstats.{$ifdescr}";
1629
			if (file_exists($mondev)) {
1630
				$cellstats = file($mondev);
1631
				/* skip header */
1632
				$a_cellstats = explode(",", $cellstats[1]);
1633
				if (preg_match("/huawei/i", implode("\n", $usbmodemoutput))) {
1634
					$ifinfo['cell_rssi'] = huawei_rssi_to_string($a_cellstats[1]);
1635
					$ifinfo['cell_mode'] = huawei_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1636
					$ifinfo['cell_simstate'] = huawei_simstate_to_string($a_cellstats[10]);
1637
					$ifinfo['cell_service'] = huawei_service_to_string(trim($a_cellstats[11]));
1638
				}
1639
				if (preg_match("/zte/i", implode("\n", $usbmodemoutput))) {
1640
					$ifinfo['cell_rssi'] = zte_rssi_to_string($a_cellstats[1]);
1641
					$ifinfo['cell_mode'] = zte_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1642
					$ifinfo['cell_simstate'] = zte_simstate_to_string($a_cellstats[10]);
1643
					$ifinfo['cell_service'] = zte_service_to_string(trim($a_cellstats[11]));
1644
				}
1645
				$ifinfo['cell_upstream'] = $a_cellstats[4];
1646
				$ifinfo['cell_downstream'] = trim($a_cellstats[5]);
1647
				$ifinfo['cell_sent'] = $a_cellstats[6];
1648
				$ifinfo['cell_received'] = trim($a_cellstats[7]);
1649
				$ifinfo['cell_bwupstream'] = $a_cellstats[8];
1650
				$ifinfo['cell_bwdownstream'] = trim($a_cellstats[9]);
1651
			}
1652
			// Calculate cumulative uptime for PPP link. Useful for connections that have per minute/hour contracts so you don't go over!
1653
			if (isset($ppp['uptime'])) {
1654
				$ifinfo['ppp_uptime_accumulated'] = "(".get_ppp_uptime($ifinfo['if']).")";
1655
			}
1656
			break;
1657
		default:
1658
			break;
1659
	}
1660

    
1661
	if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) {
1662
		$sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`);
1663
		$ifinfo['ppp_uptime'] = convert_seconds_to_dhms($sec);
1664
	}
1665

    
1666
	if ($ifinfo['status'] == "up") {
1667
		/* try to determine media with ifconfig */
1668
		$ifconfiginfo = [];
1669
		exec("/sbin/ifconfig -v " . $ifinfo['if'], $ifconfiginfo);
1670
		$wifconfiginfo = [];
1671
		if (is_interface_wireless($ifdescr)) {
1672
			exec("/sbin/ifconfig {$ifinfo['if']} list sta", $wifconfiginfo);
1673
			array_shift($wifconfiginfo);
1674
		}
1675
		$matches = "";
1676
		foreach ($ifconfiginfo as $ici) {
1677

    
1678
			/* don't list media/speed for wireless cards, as it always
1679
			   displays 2 Mbps even though clients can connect at 11 Mbps */
1680
			if (preg_match("/media: .*? \((.*?)\)/", $ici, $matches)) {
1681
				$ifinfo['media'] = $matches[1];
1682
			} else if (preg_match("/media: Ethernet (.*)/", $ici, $matches)) {
1683
				$ifinfo['media'] = $matches[1];
1684
			} else if (preg_match("/media: IEEE 802.11 Wireless Ethernet (.*)/", $ici, $matches)) {
1685
				$ifinfo['media'] = $matches[1];
1686
			}
1687

    
1688
			if (preg_match("/status: (.*)$/", $ici, $matches)) {
1689
				if ($matches[1] != "active") {
1690
					$ifinfo['status'] = $matches[1];
1691
				}
1692
				if ($ifinfo['status'] == gettext("running")) {
1693
					$ifinfo['status'] = gettext("up");
1694
				}
1695
			}
1696
			if (preg_match("/channel (\S*)/", $ici, $matches)) {
1697
				$ifinfo['channel'] = $matches[1];
1698
			}
1699
			if (preg_match("/ssid (\".*?\"|\S*)/", $ici, $matches)) {
1700
				if ($matches[1][0] == '"') {
1701
					$ifinfo['ssid'] = substr($matches[1], 1, -1);
1702
				}
1703
				else {
1704
					$ifinfo['ssid'] = $matches[1];
1705
				}
1706
			}
1707
			if (preg_match("/laggproto (.*)$/", $ici, $matches)) {
1708
				$ifinfo['laggproto'] = $matches[1];
1709
			}
1710
			if (preg_match("/laggport: (.*)$/", $ici, $matches)) {
1711
				$ifinfo['laggport'][] = $matches[1];
1712
			}
1713
			if (preg_match("/plugged: (.*)$/", $ici, $matches)) {
1714
				$ifinfo['plugged'] = $matches[1];
1715
			}
1716
			if (preg_match("/vendor: (.*)$/", $ici, $matches)) {
1717
				$ifinfo['vendor'] = $matches[1];
1718
			}
1719
			if (preg_match("/module temperature: (.*) Voltage: (.*)$/", $ici, $matches)) {
1720
				$ifinfo['temperature'] = $matches[1];
1721
				$ifinfo['voltage'] = $matches[2];
1722
			}
1723
			if (preg_match("/RX: (.*) TX: (.*)$/", $ici, $matches)) {
1724
				$ifinfo['rx'] = $matches[1];
1725
				$ifinfo['tx'] = $matches[2];
1726
			}
1727
		}
1728
		foreach ($wifconfiginfo as $ici) {
1729
			$elements = preg_split("/[ ]+/i", $ici);
1730
			if ($elements[0] != "") {
1731
				$ifinfo['bssid'] = $elements[0];
1732
			}
1733
			if ($elements[3] != "") {
1734
				$ifinfo['rate'] = $elements[3];
1735
			}
1736
			if ($elements[4] != "") {
1737
				$ifinfo['rssi'] = $elements[4];
1738
			}
1739
		}
1740
		/* lookup the gateway */
1741
		if (interface_has_gateway($ifdescr)) {
1742
			$ifinfo['gateway'] = get_interface_gateway($ifdescr);
1743
		}
1744
		if (interface_has_gatewayv6($ifdescr)) {
1745
			$ifinfo['gatewayv6'] = get_interface_gateway_v6($ifdescr);
1746
		}
1747
	}
1748

    
1749
	$bridge = "";
1750
	$bridge = link_interface_to_bridge($ifdescr);
1751
	if ($bridge) {
1752
		$bridge_text = `/sbin/ifconfig {$bridge}`;
1753
		if (stristr($bridge_text, "blocking") <> false) {
1754
			$ifinfo['bridge'] = "<b><font color='red'>" . gettext("blocking") . "</font></b> - " . gettext("check for ethernet loops");
1755
			$ifinfo['bridgeint'] = $bridge;
1756
		} else if (stristr($bridge_text, "learning") <> false) {
1757
			$ifinfo['bridge'] = gettext("learning");
1758
			$ifinfo['bridgeint'] = $bridge;
1759
		} else if (stristr($bridge_text, "forwarding") <> false) {
1760
			$ifinfo['bridge'] = gettext("forwarding");
1761
			$ifinfo['bridgeint'] = $bridge;
1762
		}
1763
	}
1764

    
1765
	return $ifinfo;
1766
}
1767

    
1768
//returns cpu speed of processor. Good for determining capabilities of machine
1769
function get_cpu_speed() {
1770
	return get_single_sysctl("hw.clockrate");
1771
}
1772

    
1773
function get_uptime_sec() {
1774
	$boottime = "";
1775
	$matches = "";
1776
	$boottime = get_single_sysctl("kern.boottime");
1777
	preg_match("/sec = (\d+)/", $boottime, $matches);
1778
	$boottime = $matches[1];
1779
	if (intval($boottime) == 0) {
1780
		return 0;
1781
	}
1782

    
1783
	$uptime = time() - $boottime;
1784
	return $uptime;
1785
}
1786

    
1787
function resolve_host_addresses($host, $recordtypes = array(DNS_A, DNS_AAAA, DNS_CNAME), $index = true) {
1788
	$dnsresult = array();
1789
	$resolved = array();
1790
	$errreporting = error_reporting();
1791
	error_reporting($errreporting & ~E_WARNING);// dns_get_record throws a warning if nothing is resolved..
1792
	foreach ($recordtypes as $recordtype) {
1793
		$tmp = dns_get_record($host, $recordtype);
1794
		if (is_array($tmp)) {
1795
			$dnsresult = array_merge($dnsresult, $tmp);
1796
		}
1797
	}
1798
	error_reporting($errreporting);// restore original php warning/error settings.
1799
	foreach ($dnsresult as $item) {
1800
		$newitem = array();
1801
		$newitem['type'] = $item['type'];
1802
		switch ($item['type']) {
1803
			case 'CNAME':
1804
				$newitem['data'] = $item['target'];
1805
				$resolved[] = $newitem;
1806
				break;
1807
			case 'A':
1808
				$newitem['data'] = $item['ip'];
1809
				$resolved[] = $newitem;
1810
				break;
1811
			case 'AAAA':
1812
				$newitem['data'] = $item['ipv6'];
1813
				$resolved[] = $newitem;
1814
				break;
1815
		}
1816
	}
1817
	if ($index == false) {
1818
		foreach ($resolved as $res) {
1819
			$noind[] = $res['data'];
1820
		}
1821
		$resolved = $noind;
1822
	}
1823
	return $resolved;
1824
}
1825

    
1826
function add_hostname_to_watch($hostname) {
1827
	if (!is_dir("/var/db/dnscache")) {
1828
		mkdir("/var/db/dnscache");
1829
	}
1830
	$result = array();
1831
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1832
		$contents = "";
1833
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1834
		if (!empty($domips)) {
1835
			foreach ($domips as $ip) {
1836
				$contents .= "$ip\n";
1837
			}
1838
		}
1839
		file_put_contents("/var/db/dnscache/$hostname", $contents);
1840
		/* Remove empty elements */
1841
		$result = array_filter(explode("\n", $contents), 'strlen');
1842
	}
1843
	return $result;
1844
}
1845

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

    
1850
function pfsense_default_state_size() {
1851
	/* get system memory amount */
1852
	$memory = get_memory();
1853
	$physmem = $memory[0];
1854
	/* Be cautious and only allocate 10% of system memory to the state table */
1855
	$max_states = (int) ($physmem/10)*1000;
1856
	return $max_states;
1857
}
1858

    
1859
function pfsense_default_tables_size() {
1860
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1861
	return $current;
1862
}
1863

    
1864
function pfsense_default_table_entries_size() {
1865
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1866
	return (trim($current));
1867
}
1868

    
1869
/* Compare the current hostname DNS to the DNS cache we made
1870
 * if it has changed we return the old records
1871
 * if no change we return false */
1872
function compare_hostname_to_dnscache($hostname) {
1873
	global $g;
1874
	if (!is_dir("/var/db/dnscache")) {
1875
		mkdir("/var/db/dnscache");
1876
	}
1877
	$hostname = trim($hostname);
1878
	if (is_readable("/var/db/dnscache/{$hostname}")) {
1879
		$oldcontents = file_get_contents("/var/db/dnscache/{$hostname}");
1880
	} else {
1881
		$oldcontents = "";
1882
	}
1883
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1884
		$contents = "";
1885
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1886
		if (!empty($domips)) {
1887
			foreach ($domips as $ip) {
1888
				$contents .= "$ip\n";
1889
			}
1890
		}
1891
	}
1892

    
1893
	if (trim($oldcontents) != trim($contents)) {
1894
		if ($g['debug']) {
1895
			log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents));
1896
		}
1897
		return ($oldcontents);
1898
	} else {
1899
		return false;
1900
	}
1901
}
1902

    
1903
/*
1904
 * load_crypto() - Load crypto modules if enabled in config.
1905
 */
1906
function load_crypto() {
1907
	$crypto_modules = array('aesni', 'cryptodev');
1908

    
1909
	$enabled_modules = explode('_', config_get_path('system/crypto_hardware'));
1910

    
1911
	foreach ($enabled_modules as $enmod) {
1912
		if (empty($enmod) || !in_array($enmod, $crypto_modules)) {
1913
			continue;
1914
		}
1915
		if (!is_module_loaded($enmod)) {
1916
			log_error(sprintf(gettext("Loading %s cryptographic accelerator module."), $enmod));
1917
			mute_kernel_msgs();
1918
			mwexec("/sbin/kldload " . escapeshellarg($enmod));
1919
			unmute_kernel_msgs();
1920
		}
1921
	}
1922
}
1923

    
1924
/*
1925
 * load_thermal_hardware() - Load temperature monitor kernel module
1926
 */
1927
function load_thermal_hardware() {
1928
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
1929
	$thermal_hardware = config_get_path('system/thermal_hardware');
1930

    
1931
	if (!in_array($thermal_hardware, $thermal_hardware_modules)) {
1932
		return false;
1933
	}
1934

    
1935
	if (!empty($thermal_hardware) && !is_module_loaded($thermal_hardware)) {
1936
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $thermal_hardware));
1937
		mute_kernel_msgs();
1938
		mwexec("/sbin/kldload {$thermal_hardware}");
1939
		unmute_kernel_msgs();
1940
	}
1941
}
1942

    
1943
/****f* pfsense-utils/isvm
1944
 * NAME
1945
 *   isvm
1946
 * INPUTS
1947
 *	none
1948
 * RESULT
1949
 *   returns true if machine is running under a virtual environment
1950
 ******/
1951
function isvm() {
1952
	$virtualenvs = array("vmware", "parallels", "qemu", "bochs", "plex86", "VirtualBox");
1953
	exec('/bin/kenv -q smbios.system.product 2>/dev/null', $output, $rc);
1954

    
1955
	if ($rc != 0 || !isset($output[0])) {
1956
		return false;
1957
	}
1958

    
1959
	foreach ($virtualenvs as $virtualenv) {
1960
		if (stripos($output[0], $virtualenv) !== false) {
1961
			return true;
1962
		}
1963
	}
1964

    
1965
	return false;
1966
}
1967

    
1968
function get_freebsd_version() {
1969
	$version = explode(".", php_uname("r"));
1970
	return $version[0];
1971
}
1972

    
1973
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
1974
	global $g;
1975

    
1976
	$fp = fopen($destination, "wb");
1977

    
1978
	if (!$fp) {
1979
		return false;
1980
	}
1981

    
1982
	$ch = curl_init();
1983
	curl_setopt($ch, CURLOPT_URL, $url);
1984
	if ($verify_ssl) {
1985
		curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
1986
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
1987
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
1988
	} else {
1989
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
1990
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
1991
		curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false);
1992
	}
1993
	curl_setopt($ch, CURLOPT_FILE, $fp);
1994
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
1995
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1996
	curl_setopt($ch, CURLOPT_HEADER, false);
1997
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
1998
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
1999
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2000
	} else {
2001
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
2002
	}
2003

    
2004
	set_curlproxy($ch);
2005

    
2006
	@curl_exec($ch);
2007
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2008
	fclose($fp);
2009
	curl_close($ch);
2010
	if ($http_code == 200) {
2011
		return true;
2012
	} else {
2013
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2014
		unlink_if_exists($destination);
2015
		return false;
2016
	}
2017
}
2018

    
2019
function download_file_with_progress_bar($url, $destination, $verify_ssl = true, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) {
2020
	global $g, $ch, $fout, $file_size, $downloaded, $first_progress_update;
2021
	$file_size = 1;
2022
	$downloaded = 1;
2023
	$first_progress_update = TRUE;
2024
	/* open destination file */
2025
	$fout = fopen($destination, "wb");
2026

    
2027
	if (!$fout) {
2028
		return false;
2029
	}
2030
	/*
2031
	 *      Originally by Author: Keyvan Minoukadeh
2032
	 *      Modified by Scott Ullrich to return Content-Length size
2033
	 */
2034
	$ch = curl_init();
2035
	curl_setopt($ch, CURLOPT_URL, $url);
2036
	if ($verify_ssl) {
2037
		curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
2038
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
2039
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
2040
	} else {
2041
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
2042
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2043
		curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false);
2044
	}
2045
	curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
2046
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2047
	curl_setopt($ch, CURLOPT_WRITEFUNCTION, $readbody);
2048
	curl_setopt($ch, CURLOPT_NOPROGRESS, '1');
2049
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2050
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2051
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
2052
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2053
	} else {
2054
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
2055
	}
2056

    
2057
	set_curlproxy($ch);
2058

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

    
2072
function read_header($ch, $string) {
2073
	global $file_size;
2074
	$length = strlen($string);
2075
	$regs = "";
2076
	preg_match("/(Content-Length:) (.*)/", $string, $regs);
2077
	if ($regs[2] <> "") {
2078
		$file_size = intval($regs[2]);
2079
	}
2080
	ob_flush();
2081
	return $length;
2082
}
2083

    
2084
function read_body($ch, $string) {
2085
	global $fout, $file_size, $downloaded, $sendto, $static_status, $static_output, $lastseen, $first_progress_update;
2086
	global $pkg_interface;
2087
	$length = strlen($string);
2088
	$downloaded += intval($length);
2089
	if ($file_size > 0) {
2090
		$downloadProgress = round(100 * (1 - $downloaded / $file_size), 0);
2091
		$downloadProgress = 100 - $downloadProgress;
2092
	} else {
2093
		$downloadProgress = 0;
2094
	}
2095
	if ($lastseen <> $downloadProgress and $downloadProgress < 101) {
2096
		if ($sendto == "status") {
2097
			if ($pkg_interface == "console") {
2098
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2099
					$tostatus = $static_status . $downloadProgress . "%";
2100
					if ($downloadProgress == 100) {
2101
						$tostatus = $tostatus . "\r";
2102
					}
2103
					update_status($tostatus);
2104
				}
2105
			} else {
2106
				$tostatus = $static_status . $downloadProgress . "%";
2107
				update_status($tostatus);
2108
			}
2109
		} else {
2110
			if ($pkg_interface == "console") {
2111
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2112
					$tooutput = $static_output . $downloadProgress . "%";
2113
					if ($downloadProgress == 100) {
2114
						$tooutput = $tooutput . "\r";
2115
					}
2116
					update_output_window($tooutput);
2117
				}
2118
			} else {
2119
				$tooutput = $static_output . $downloadProgress . "%";
2120
				update_output_window($tooutput);
2121
			}
2122
		}
2123
		if (($pkg_interface != "console") || (($downloadProgress % 10) == 0) || ($downloadProgress < 10)) {
2124
			update_progress_bar($downloadProgress, $first_progress_update);
2125
			$first_progress_update = FALSE;
2126
		}
2127
		$lastseen = $downloadProgress;
2128
	}
2129
	if ($fout) {
2130
		fwrite($fout, $string);
2131
	}
2132
	ob_flush();
2133
	return $length;
2134
}
2135

    
2136
/*
2137
 *   update_output_window: update bottom textarea dynamically.
2138
 */
2139
function update_output_window($text) {
2140
	global $pkg_interface;
2141
	$log = preg_replace("/\n/", "\\n", $text);
2142
	if ($pkg_interface != "console") {
2143
?>
2144
<script type="text/javascript">
2145
//<![CDATA[
2146
	document.getElementById("output").textContent="<?=htmlspecialchars($log)?>";
2147
	document.getElementById("output").scrollTop = document.getElementById("output").scrollHeight;
2148
//]]>
2149
</script>
2150
<?php
2151
	}
2152
	/* ensure that contents are written out */
2153
	ob_flush();
2154
}
2155

    
2156
/*
2157
 *   update_status: update top textarea dynamically.
2158
 */
2159
function update_status($status) {
2160
	global $pkg_interface;
2161

    
2162
	if ($pkg_interface == "console") {
2163
		print ("{$status}");
2164
	}
2165

    
2166
	/* ensure that contents are written out */
2167
	ob_flush();
2168
}
2169

    
2170
/*
2171
 * update_progress_bar($percent, $first_time): updates the javascript driven progress bar.
2172
 */
2173
function update_progress_bar($percent, $first_time) {
2174
	global $pkg_interface;
2175
	if ($percent > 100) {
2176
		$percent = 1;
2177
	}
2178
	if ($pkg_interface <> "console") {
2179
		echo '<script type="text/javascript">';
2180
		echo "\n//<![CDATA[\n";
2181
		echo 'document.getElementById("progressbar").style.width="'. $percent.'%"';
2182
		echo "\n//]]>\n";
2183
		echo '</script>';
2184
	} else {
2185
		if (!($first_time)) {
2186
			echo "\x08\x08\x08\x08\x08";
2187
		}
2188
		echo sprintf("%4d%%", $percent);
2189
	}
2190
}
2191

    
2192
function update_alias_name($new_alias_name, $orig_alias_name) {
2193
	if (!$orig_alias_name) {
2194
		return;
2195
	}
2196

    
2197
	// Firewall rules
2198
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2199
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2200
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2201
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2202
	// NAT Rules
2203
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2204
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2205
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2206
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2207
	update_alias_names_upon_change(array('nat', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2208
	update_alias_names_upon_change(array('nat', 'rule'), array('local-port'), $new_alias_name, $orig_alias_name);
2209
	// NAT 1:1 Rules
2210
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('external'), $new_alias_name, $orig_alias_name);
2211
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2212
	update_alias_names_upon_change(array('nat', 'onetoone'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2213
	// NAT Outbound Rules
2214
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('source', 'network'), $new_alias_name, $orig_alias_name);
2215
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('sourceport'), $new_alias_name, $orig_alias_name);
2216
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2217
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('dstport'), $new_alias_name, $orig_alias_name);
2218
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2219
	// Alias in an alias
2220
	update_alias_names_upon_change(array('aliases', 'alias'), array('address'), $new_alias_name, $orig_alias_name);
2221
	// Static routes
2222
	update_alias_names_upon_change(array('staticroutes', 'route'), array('network'), $new_alias_name, $orig_alias_name);
2223
	// OpenVPN
2224
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2225
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2226
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('local_network'), $new_alias_name, $orig_alias_name);
2227
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('local_networkv6'), $new_alias_name, $orig_alias_name);
2228
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('remote_network'), $new_alias_name, $orig_alias_name);
2229
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2230
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2231
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2232
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('remote_network'), $new_alias_name, $orig_alias_name);
2233
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2234
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2235
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2236
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('local_network'), $new_alias_name, $orig_alias_name);
2237
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('local_networkv6'), $new_alias_name, $orig_alias_name);
2238
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('remote_network'), $new_alias_name, $orig_alias_name);
2239
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2240
}
2241

    
2242
function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) {
2243
	global $g, $config, $pconfig, $debug;
2244
	if (!$origname) {
2245
		return;
2246
	}
2247

    
2248
	$sectionref = &$config;
2249
	foreach ($section as $sectionname) {
2250
		if (is_array($sectionref) && isset($sectionref[$sectionname])) {
2251
			$sectionref = &$sectionref[$sectionname];
2252
		} else {
2253
			return;
2254
		}
2255
	}
2256

    
2257
	if ($debug) {
2258
		$fd = fopen("{$g['tmp_path']}/print_r", "a");
2259
		fwrite($fd, print_r($pconfig, true));
2260
	}
2261

    
2262
	if (is_array($sectionref)) {
2263
		foreach (array_keys($sectionref) as $itemkey) {
2264
			if ($debug) {
2265
				fwrite($fd, "$itemkey\n");
2266
			}
2267

    
2268
			$fieldfound = true;
2269
			$fieldref = &$sectionref[$itemkey];
2270
			foreach ($field as $fieldname) {
2271
				if (is_array($fieldref) && isset($fieldref[$fieldname])) {
2272
					$fieldref = &$fieldref[$fieldname];
2273
				} else {
2274
					$fieldfound = false;
2275
					break;
2276
				}
2277
			}
2278
			if ($fieldfound && $fieldref == $origname) {
2279
				if ($debug) {
2280
					fwrite($fd, "Setting old alias value $origname to $new_alias_name\n");
2281
				}
2282
				$fieldref = $new_alias_name;
2283
			}
2284
		}
2285
	}
2286

    
2287
	if ($debug) {
2288
		fclose($fd);
2289
	}
2290

    
2291
}
2292

    
2293
function parse_aliases_file($filename, $type = "url", $max_items = -1, $kflc = false) {
2294
	/*
2295
	 * $filename = file to process for example blocklist like DROP:  http://www.spamhaus.org/drop/drop.txt
2296
	 * $type = if set to 'url' then subnets and ips will be returned,
2297
	 *         if set to 'url_ports' port-ranges and ports will be returned
2298
	 * $max_items = sets the maximum amount of valid items to load, -1 the default defines there is no limit.
2299
	 *
2300
	 * RETURNS an array of ip subnets and ip's or ports and port-ranges, returns NULL upon a error conditions (file not found)
2301
	 */
2302

    
2303
	if (!file_exists($filename)) {
2304
		log_error(sprintf(gettext("Could not process non-existent file from alias: %s"), $filename));
2305
		return null;
2306
	}
2307

    
2308
	if (filesize($filename) == 0) {
2309
		log_error(sprintf(gettext("Could not process empty file from alias: %s"), $filename));
2310
		return null;
2311
	}
2312
	$fd = @fopen($filename, 'r');
2313
	if (!$fd) {
2314
		log_error(sprintf(gettext("Could not process aliases from alias: %s"), $filename));
2315
		return null;
2316
	}
2317
	$items = array();
2318
	$comments = array();
2319
	while (($fc = strip_tags(fgets($fd))) !== FALSE) {
2320
		$tmp = alias_idn_to_ascii(trim($fc, " \t\n\r"));
2321
		if (empty($tmp)) {
2322
			continue;
2323
		}
2324
		if (($kflc) && (strpos($tmp, '#') === 0)) {	// Keep Full Line Comments (lines beginning with #).
2325
			$comments[] = $tmp;
2326
		} else {
2327
			$tmp_str = strstr($tmp, '#', true);
2328
			if (!empty($tmp_str)) {
2329
				$tmp = $tmp_str;
2330
			}
2331
			$tmp_str = strstr($tmp, ' ', true);
2332
			if (!empty($tmp_str)) {
2333
				$tmp = $tmp_str;
2334
			}
2335
			switch ($type) {
2336
				case "url":
2337
				case "urltable":
2338
					if (is_ipaddr($tmp) || is_subnet($tmp)) {
2339
						$items[] = $tmp;
2340
						break;
2341
					}
2342
					if (is_fqdn($tmp)) {
2343
						$results = resolve_host_addresses($tmp, array(DNS_A, DNS_AAAA), false);
2344
						if (!empty($results)) {
2345
							foreach ($results as $ip) {
2346
								if (is_ipaddr($ip)) {
2347
									$items[] = $ip;
2348
								}
2349
							}
2350
						}
2351
					}
2352
					break;
2353
				case "url_ports":
2354
				case "urltable_ports":
2355
					if (is_port_or_range($tmp)) {
2356
						$items[] = $tmp;
2357
					}
2358
					break;
2359
				default:
2360
					/* unknown type */
2361
					break;
2362
				}
2363
			if (count($items) == $max_items) {
2364
				break;
2365
			}
2366
		}
2367
	}
2368
	fclose($fd);
2369
	return array_merge($comments, $items);
2370
}
2371

    
2372
function update_alias_url_data() {
2373
	global $g, $aliastable;
2374

    
2375
	$updated = false;
2376

    
2377
	/* item is a url type */
2378
	$lockkey = lock('aliasurl');
2379
	$aliases = array();
2380
	$aliases_nested = array();
2381

    
2382
	foreach (config_get_path('aliases/alias', []) as $x => $alias) {
2383
		if (empty($alias['aliasurl'])) {
2384
			continue;
2385
		}
2386
		foreach ($alias['aliasurl'] as $alias_url) {
2387
			if (is_alias($alias_url)) {
2388
				// process nested URL aliases after URL-only aliases
2389
				$aliases_nested[] = $x;
2390
				continue 2;
2391
			}
2392
		}
2393
		$aliases[] = $x;
2394
	}
2395

    
2396
	foreach (array_merge($aliases, $aliases_nested) as $x) {
2397

    
2398
		$address = array();
2399
		$type = config_get_path("aliases/alias/{$x}/type");
2400
		foreach (config_get_path("aliases/alias/{$x}/aliasurl") as $alias_url) {
2401
			/* fetch down and add in */
2402
			if (is_URL($alias_url)) {
2403
				$temp_filename = tempnam("{$g['tmp_path']}/", "alias_import");
2404
				rmdir_recursive($temp_filename);
2405
				$verify_ssl = config_path_enabled('system','checkaliasesurlcert');
2406
				mkdir($temp_filename);
2407
				if (!download_file($alias_url, $temp_filename . "/aliases", $verify_ssl)) {
2408
					log_error(sprintf(gettext("Failed to download alias %s"), $alias_url));
2409
					rmdir_recursive($temp_filename);
2410
					continue;
2411
				}
2412

    
2413
				/* if the item is tar gzipped then extract */
2414
				if (stripos($alias_url, '.tgz') && !process_alias_tgz($temp_filename)) {
2415
					log_error(sprintf(gettext("Could not unpack tgz from the URL '%s'."), $alias_url));
2416
					rmdir_recursive($temp_filename);
2417
					continue;
2418
				}
2419
				if (file_exists("{$temp_filename}/aliases")) {
2420
					$t_address = parse_aliases_file("{$temp_filename}/aliases", $type, 5000);
2421
					if ($t_address == null) {
2422
						/* nothing was found */
2423
						log_error(sprintf(gettext("Could not fetch usable data from '%s'."), $alias_url));
2424
						//rmdir_recursive($temp_filename);
2425
						continue;
2426
					} else {
2427
						array_push($address, ...$t_address);
2428
					}
2429
					unset($t_address);
2430
				}
2431
				rmdir_recursive($temp_filename);
2432
			} elseif (is_alias($alias_url)) {
2433
				/* nested URL alias, see https://redmine.pfsense.org/issues/11863 */
2434
				if (!$aliastable) {
2435
					alias_make_table();
2436
				}
2437
				$t_address = explode(" ", $aliastable[$alias_url]);
2438
				if ($t_address == null) {
2439
					log_error(sprintf(gettext("Could not get usable data from '%s' URL alias."), $alias_url));
2440
					continue;
2441
				}
2442
				array_push($address, ...$t_address);
2443
			}
2444
			if (!empty($address)) {
2445
				config_set_path("aliases/alias/{$x}/address", implode(" ", $address));
2446
				$updated = true;
2447
			}
2448
		}
2449
	}
2450

    
2451
	unlock($lockkey);
2452

    
2453
	/* Report status to callers as well */
2454
	return $updated;
2455
}
2456

    
2457
function process_alias_tgz($temp_filename) {
2458
	if (!file_exists('/usr/bin/tar')) {
2459
		log_error(gettext("Alias archive is a .tar/tgz file which cannot be decompressed because utility is missing!"));
2460
		return false;
2461
	}
2462
	rename("{$temp_filename}/aliases", "{$temp_filename}/aliases.tgz");
2463
	mwexec("/usr/bin/tar xzf {$temp_filename}/aliases.tgz -C {$temp_filename}/aliases/");
2464
	unlink("{$temp_filename}/aliases.tgz");
2465
	$files_to_process = return_dir_as_array("{$temp_filename}/");
2466
	/* foreach through all extracted files and build up aliases file */
2467
	$fd = @fopen("{$temp_filename}/aliases", "w");
2468
	if (!$fd) {
2469
		log_error(sprintf(gettext("Could not open %s/aliases for writing!"), $temp_filename));
2470
		return false;
2471
	}
2472
	foreach ($files_to_process as $f2p) {
2473
		$tmpfd = @fopen($f2p, 'r');
2474
		if (!$tmpfd) {
2475
			log_error(sprintf(gettext('The following file could not be read %1$s from %2$s'), $f2p, $temp_filename));
2476
			continue;
2477
		}
2478
		while (($tmpbuf = fread($tmpfd, 65536)) !== FALSE) {
2479
			fwrite($fd, $tmpbuf);
2480
		}
2481
		fclose($tmpfd);
2482
		unlink($f2p);
2483
	}
2484
	fclose($fd);
2485
	unset($tmpbuf);
2486

    
2487
	return true;
2488
}
2489

    
2490
function version_compare_dates($a, $b) {
2491
	$a_time = strtotime($a);
2492
	$b_time = strtotime($b);
2493

    
2494
	if ((!$a_time) || (!$b_time)) {
2495
		return FALSE;
2496
	} else {
2497
		if ($a_time < $b_time) {
2498
			return -1;
2499
		} elseif ($a_time == $b_time) {
2500
			return 0;
2501
		} else {
2502
			return 1;
2503
		}
2504
	}
2505
}
2506
function version_get_string_value($a) {
2507
	$strs = array(
2508
		0 => "ALPHA-ALPHA",
2509
		2 => "ALPHA",
2510
		3 => "BETA",
2511
		4 => "B",
2512
		5 => "C",
2513
		6 => "D",
2514
		7 => "RC",
2515
		8 => "RELEASE",
2516
		9 => "*"			// Matches all release levels
2517
	);
2518
	$major = 0;
2519
	$minor = 0;
2520
	foreach ($strs as $num => $str) {
2521
		if (substr($a, 0, strlen($str)) == $str) {
2522
			$major = $num;
2523
			$n = substr($a, strlen($str));
2524
			if (is_numeric($n)) {
2525
				$minor = $n;
2526
			}
2527
			break;
2528
		}
2529
	}
2530
	return "{$major}.{$minor}";
2531
}
2532
function version_compare_string($a, $b) {
2533
	// Only compare string parts if both versions give a specific release
2534
	// (If either version lacks a string part, assume intended to match all release levels)
2535
	if (isset($a) && isset($b)) {
2536
		return version_compare_numeric(version_get_string_value($a), version_get_string_value($b));
2537
	} else {
2538
		return 0;
2539
	}
2540
}
2541
function version_compare_numeric($a, $b) {
2542
	$a_arr = explode('.', rtrim($a, '.'));
2543
	$b_arr = explode('.', rtrim($b, '.'));
2544

    
2545
	foreach ($a_arr as $n => $val) {
2546
		if (array_key_exists($n, $b_arr)) {
2547
			// So far so good, both have values at this minor version level. Compare.
2548
			if ($val > $b_arr[$n]) {
2549
				return 1;
2550
			} elseif ($val < $b_arr[$n]) {
2551
				return -1;
2552
			}
2553
		} else {
2554
			// a is greater, since b doesn't have any minor version here.
2555
			return 1;
2556
		}
2557
	}
2558
	if (count($b_arr) > count($a_arr)) {
2559
		// b is longer than a, so it must be greater.
2560
		return -1;
2561
	} else {
2562
		// Both a and b are of equal length and value.
2563
		return 0;
2564
	}
2565
}
2566
function pfs_version_compare($cur_time, $cur_text, $remote) {
2567
	// First try date compare
2568
	$v = version_compare_dates($cur_time, $remote);
2569
	if ($v === FALSE) {
2570
		// If that fails, try to compare by string
2571
		// Before anything else, simply test if the strings are equal
2572
		if (($cur_text == $remote) || ($cur_time == $remote)) {
2573
			return 0;
2574
		}
2575
		list($cur_num, $cur_str) = explode('-', $cur_text);
2576
		list($rem_num, $rem_str) = explode('-', $remote);
2577

    
2578
		// First try to compare the numeric parts of the version string.
2579
		$v = version_compare_numeric($cur_num, $rem_num);
2580

    
2581
		// If the numeric parts are the same, compare the string parts.
2582
		if ($v == 0) {
2583
			return version_compare_string($cur_str, $rem_str);
2584
		}
2585
	}
2586
	return $v;
2587
}
2588
function process_alias_urltable($name, $type, $url, $freq, $forceupdate=false, $validateonly=false) {
2589
	global $g;
2590

    
2591
	if (!is_validaliasname($name) || !filter_var($url, FILTER_VALIDATE_URL)) {
2592
		return false;
2593
	}
2594

    
2595
	$urltable_prefix = "/var/db/aliastables/";
2596
	$urltable_filename = $urltable_prefix . basename($name) . ".txt";
2597
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2598

    
2599
	// Make the aliases directory if it doesn't exist
2600
	if (!file_exists($urltable_prefix)) {
2601
		mkdir($urltable_prefix);
2602
	} elseif (!is_dir($urltable_prefix)) {
2603
		unlink($urltable_prefix);
2604
		mkdir($urltable_prefix);
2605
	}
2606

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

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

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

    
2621
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2622
			if ($type == "urltable_ports") {
2623
				$parsed_contents = group_ports($parsed_contents, true);
2624
			}
2625
			if (is_array($parsed_contents)) {
2626
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2627
			} else {
2628
				touch($urltable_filename);
2629
			}
2630

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

    
2637
			unlink_if_exists($tmp_urltable_filename);
2638
		} else {
2639
			if (!$validateonly) {
2640
				touch($urltable_filename);
2641
			}
2642
			return false;
2643
		}
2644
		return true;
2645
	} else {
2646
		// File exists, and it doesn't need to be updated.
2647
		return -1;
2648
	}
2649
}
2650

    
2651
function get_include_contents($filename) {
2652
	if (is_file($filename)) {
2653
		ob_start();
2654
		include $filename;
2655
		$contents = ob_get_contents();
2656
		ob_end_clean();
2657
		return $contents;
2658
	}
2659
	return false;
2660
}
2661

    
2662
/* This xml 2 array function is courtesy of the php.net comment section on xml_parse.
2663
 * it is roughly 4 times faster then our existing pfSense parser but due to the large
2664
 * size of the RRD xml dumps this is required.
2665
 * The reason we do not use it for pfSense is that it does not know about array fields
2666
 * which causes it to fail on array fields with single items. Possible Todo?
2667
 */
2668
function xml2array($contents, $get_attributes = 1, $priority = 'tag') {
2669
	if (!function_exists('xml_parser_create')) {
2670
		return array ();
2671
	}
2672
	$parser = xml_parser_create('');
2673
	xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
2674
	xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2675
	xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
2676
	xml_parse_into_struct($parser, trim($contents), $xml_values);
2677
	xml_parser_free($parser);
2678
	if (!$xml_values) {
2679
		return; //Hmm...
2680
	}
2681
	$xml_array = array ();
2682
	$parents = array ();
2683
	$opened_tags = array ();
2684
	$arr = array ();
2685
	$current = & $xml_array;
2686
	$repeated_tag_index = array ();
2687
	foreach ($xml_values as $data) {
2688
		unset ($attributes, $value);
2689
		extract($data);
2690
		$result = array ();
2691
		$attributes_data = array ();
2692
		if (isset ($value)) {
2693
			if ($priority == 'tag') {
2694
				$result = $value;
2695
			} else {
2696
				$result['value'] = $value;
2697
			}
2698
		}
2699
		if (isset ($attributes) and $get_attributes) {
2700
			foreach ($attributes as $attr => $val) {
2701
				if ($priority == 'tag') {
2702
					$attributes_data[$attr] = $val;
2703
				} else {
2704
					$result['attr'][$attr] = $val; //Set all the attributes in a array called 'attr'
2705
				}
2706
			}
2707
		}
2708
		if ($type == "open") {
2709
			$parent[$level -1] = & $current;
2710
			if (!is_array($current) or (!in_array($tag, array_keys($current)))) {
2711
				$current[$tag] = $result;
2712
				if ($attributes_data) {
2713
					$current[$tag . '_attr'] = $attributes_data;
2714
				}
2715
				$repeated_tag_index[$tag . '_' . $level] = 1;
2716
				$current = &$current[$tag];
2717
			} else {
2718
				if (isset ($current[$tag][0])) {
2719
					$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
2720
					$repeated_tag_index[$tag . '_' . $level]++;
2721
				} else {
2722
					$current[$tag] = array (
2723
						$current[$tag],
2724
						$result
2725
						);
2726
					$repeated_tag_index[$tag . '_' . $level] = 2;
2727
					if (isset ($current[$tag . '_attr'])) {
2728
						$current[$tag]['0_attr'] = $current[$tag . '_attr'];
2729
						unset ($current[$tag . '_attr']);
2730
					}
2731
				}
2732
				$last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
2733
				$current = &$current[$tag][$last_item_index];
2734
			}
2735
		} elseif ($type == "complete") {
2736
			if (!isset ($current[$tag])) {
2737
				$current[$tag] = $result;
2738
				$repeated_tag_index[$tag . '_' . $level] = 1;
2739
				if ($priority == 'tag' and $attributes_data) {
2740
					$current[$tag . '_attr'] = $attributes_data;
2741
				}
2742
			} else {
2743
				if (isset ($current[$tag][0]) and is_array($current[$tag])) {
2744
					$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
2745
					if ($priority == 'tag' and $get_attributes and $attributes_data) {
2746
						$current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
2747
					}
2748
					$repeated_tag_index[$tag . '_' . $level]++;
2749
				} else {
2750
					$current[$tag] = array (
2751
						$current[$tag],
2752
						$result
2753
						);
2754
					$repeated_tag_index[$tag . '_' . $level] = 1;
2755
					if ($priority == 'tag' and $get_attributes) {
2756
						if (isset ($current[$tag . '_attr'])) {
2757
							$current[$tag]['0_attr'] = $current[$tag . '_attr'];
2758
							unset ($current[$tag . '_attr']);
2759
						}
2760
						if ($attributes_data) {
2761
							$current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
2762
						}
2763
					}
2764
					$repeated_tag_index[$tag . '_' . $level]++; //0 and 1 index is already taken
2765
				}
2766
			}
2767
		} elseif ($type == 'close') {
2768
			$current = &$parent[$level -1];
2769
		}
2770
	}
2771
	return ($xml_array);
2772
}
2773

    
2774
function get_country_name($country_code = "ALL") {
2775
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2776
		return "";
2777
	}
2778

    
2779
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2780
	$country_names_contents = file_get_contents($country_names_xml);
2781
	$country_names = xml2array($country_names_contents);
2782

    
2783
	if ($country_code == "ALL") {
2784
		$country_list = array();
2785
		foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2786
			$country_list[] = array(
2787
				"code" => $country['ISO_3166-1_Alpha-2_Code_element'],
2788
				"name" => ucwords(strtolower($country['ISO_3166-1_Country_name'])));
2789
		}
2790
		return $country_list;
2791
	}
2792

    
2793
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2794
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2795
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2796
		}
2797
	}
2798
	return "";
2799
}
2800

    
2801
/* Return the list of country codes to be used on CAs and certs */
2802
function get_cert_country_codes() {
2803
	$countries = get_country_name();
2804

    
2805
	$country_codes = array();
2806
	foreach ($countries as $country) {
2807
		$country_codes[$country['code']] = $country['code'];
2808
	}
2809
	ksort($country_codes);
2810

    
2811
	/* Preserve historical order: None, US, CA, other countries */
2812
	$first_items[''] = gettext("None");
2813
	$first_items['US'] = $country_codes['US'];
2814
	$first_items['CA'] = $country_codes['CA'];
2815
	unset($country_codes['US']);
2816
	unset($country_codes['CA']);
2817

    
2818
	return array_merge($first_items, $country_codes);
2819
}
2820

    
2821
/* sort by interface only, retain the original order of rules that apply to
2822
   the same interface */
2823
function filter_rules_sort() {
2824
	init_config_arr(array('filter', 'rule'));
2825
	$rules = config_get_path('filter/rule', []);
2826

    
2827
	/* mark each rule with the sequence number (to retain the order while sorting) */
2828
	for ($i = 0; isset($rules[$i]); $i++) {
2829
		$rules[$i]['seq'] =$i;
2830
	}
2831
	usort($rules, "filter_rules_compare");
2832

    
2833
	/* strip the sequence numbers again */
2834
	for ($i = 0; isset($rules[$i]); $i++) {
2835
		unset($rules[$i]['seq']);
2836
	}
2837

    
2838
	/* commit changes */
2839
	config_set_path('filter/rule', $rules);
2840
}
2841
function filter_rules_compare($a, $b) {
2842
	if (isset($a['floating']) && isset($b['floating'])) {
2843
		return $a['seq'] - $b['seq'];
2844
	} else if (isset($a['floating'])) {
2845
		return -1;
2846
	} else if (isset($b['floating'])) {
2847
		return 1;
2848
	} else if ($a['interface'] == $b['interface']) {
2849
		return $a['seq'] - $b['seq'];
2850
	} else {
2851
		return compare_interface_friendly_names($a['interface'], $b['interface']);
2852
	}
2853
}
2854

    
2855
function generate_ipv6_from_mac($mac) {
2856
	$elements = explode(":", $mac);
2857
	if (count($elements) <> 6) {
2858
		return false;
2859
	}
2860

    
2861
	$i = 0;
2862
	$ipv6 = "fe80::";
2863
	foreach ($elements as $byte) {
2864
		if ($i == 0) {
2865
			$hexadecimal = substr($byte, 1, 2);
2866
			$bitmap = base_convert($hexadecimal, 16, 2);
2867
			$bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT);
2868
			$bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3, 4);
2869
			$byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16);
2870
		}
2871
		$ipv6 .= $byte;
2872
		if ($i == 1) {
2873
			$ipv6 .= ":";
2874
		}
2875
		if ($i == 3) {
2876
			$ipv6 .= ":";
2877
		}
2878
		if ($i == 2) {
2879
			$ipv6 .= "ff:fe";
2880
		}
2881

    
2882
		$i++;
2883
	}
2884
	return $ipv6;
2885
}
2886

    
2887
/****f* pfsense-utils/load_mac_manufacturer_table
2888
 * NAME
2889
 *   load_mac_manufacturer_table
2890
 * INPUTS
2891
 *   none
2892
 * RESULT
2893
 *   returns associative array with MAC-Manufacturer pairs
2894
 ******/
2895
function load_mac_manufacturer_table() {
2896
	/* load MAC-Manufacture data from the file */
2897
	$macs = false;
2898
	if (file_exists("/usr/local/share/nmap/nmap-mac-prefixes")) {
2899
		$macs=file("/usr/local/share/nmap/nmap-mac-prefixes");
2900
	}
2901
	if ($macs) {
2902
		foreach ($macs as $line) {
2903
			if (preg_match('/([0-9A-Fa-f]{6}) (.*)$/', $line, $matches)) {
2904
				/* store values like this $mac_man['000C29']='VMware' */
2905
				$mac_man["$matches[1]"] = $matches[2];
2906
			}
2907
		}
2908
		return $mac_man;
2909
	} else {
2910
		return -1;
2911
	}
2912

    
2913
}
2914

    
2915
/****f* pfsense-utils/is_ipaddr_configured
2916
 * NAME
2917
 *   is_ipaddr_configured
2918
 * INPUTS
2919
 *   IP Address to check.
2920
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2921
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2922
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2923
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2924
 *     If check_subnets is true and cidrprefix is specified,
2925
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2926
 * RESULT
2927
 *   returns true if the IP Address is configured and present on this device or overlaps a configured subnet.
2928
*/
2929
function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2930
	if (count(where_is_ipaddr_configured($ipaddr, $ignore_if, $check_localip, $check_subnets, $cidrprefix))) {
2931
		return true;
2932
	}
2933
	return false;
2934
}
2935

    
2936
/****f* pfsense-utils/where_is_ipaddr_configured
2937
 * NAME
2938
 *   where_is_ipaddr_configured
2939
 * INPUTS
2940
 *   IP Address to check.
2941
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2942
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2943
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2944
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2945
 *     If check_subnets is true and cidrprefix is specified,
2946
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2947
 * RESULT
2948
 *   Returns an array of the interfaces 'if' plus IP address or subnet 'ip_or_subnet' that match or overlap the IP address to check.
2949
 *   If there are no matches then an empty array is returned.
2950
*/
2951
function where_is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2952
	$where_configured = array();
2953

    
2954
	$pos = strpos($ignore_if, '_virtualip');
2955
	if ($pos !== false) {
2956
		$ignore_vip_id = substr($ignore_if, $pos+10);
2957
		$ignore_vip_if = substr($ignore_if, 0, $pos);
2958
	} else {
2959
		$ignore_vip_id = -1;
2960
		$ignore_vip_if = $ignore_if;
2961
	}
2962

    
2963
	$isipv6 = is_ipaddrv6($ipaddr);
2964

    
2965
	if ($isipv6) {
2966
		$ipaddr = text_to_compressed_ip6($ipaddr);
2967
	}
2968

    
2969
	if ($check_subnets) {
2970
		$cidrprefix = intval($cidrprefix);
2971
		if ($isipv6) {
2972
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
2973
				$cidrprefix = 128;
2974
			}
2975
		} else {
2976
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
2977
				$cidrprefix = 32;
2978
			}
2979
		}
2980
		$iflist = get_configured_interface_list();
2981
		foreach ($iflist as $if => $ifname) {
2982
			if ($ignore_if == $if) {
2983
				continue;
2984
			}
2985

    
2986
			if ($isipv6) {
2987
				$if_ipv6 = get_interface_ipv6($if);
2988
				$if_snbitsv6 = get_interface_subnetv6($if);
2989
				/* do not check subnet overlapping on 6rd interfaces,
2990
				 * see https://redmine.pfsense.org/issues/12371 */ 
2991
				if ($if_ipv6 && $if_snbitsv6 &&
2992
				    ((config_get_path("interfaces/{$if}/ipaddrv6") != '6rd') || ($cidrprefix > $if_snbitsv6)) &&
2993
				    check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) {
2994
					$where_entry = array();
2995
					$where_entry['if'] = $if;
2996
					$where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if);
2997
					$where_configured[] = $where_entry;
2998
				}
2999
			} else {
3000
				$if_ipv4 = get_interface_ip($if);
3001
				$if_snbitsv4 = get_interface_subnet($if);
3002
				if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) {
3003
					$where_entry = array();
3004
					$where_entry['if'] = $if;
3005
					$where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if);
3006
					$where_configured[] = $where_entry;
3007
				}
3008
			}
3009
		}
3010
	} else {
3011
		if ($isipv6) {
3012
			$interface_list_ips = get_configured_ipv6_addresses();
3013
		} else {
3014
			$interface_list_ips = get_configured_ip_addresses();
3015
		}
3016

    
3017
		foreach ($interface_list_ips as $if => $ilips) {
3018
			if ($ignore_if == $if) {
3019
				continue;
3020
			}
3021
			if (strcasecmp($ipaddr, $ilips) == 0) {
3022
				$where_entry = array();
3023
				$where_entry['if'] = $if;
3024
				$where_entry['ip_or_subnet'] = $ilips;
3025
				$where_configured[] = $where_entry;
3026
			}
3027
		}
3028
	}
3029

    
3030
	if ($check_localip) {
3031
		if (strcasecmp($ipaddr, text_to_compressed_ip6(config_get_path('l2tp/localip', ""))) == 0) {
3032
			$where_entry = array();
3033
			$where_entry['if'] = 'l2tp';
3034
			$where_entry['ip_or_subnet'] = config_get_path('l2tp/localip');
3035
			$where_configured[] = $where_entry;
3036
		}
3037
	}
3038

    
3039
	return $where_configured;
3040
}
3041

    
3042
/****f* pfsense-utils/pfSense_handle_custom_code
3043
 * NAME
3044
 *   pfSense_handle_custom_code
3045
 * INPUTS
3046
 *   directory name to process
3047
 * RESULT
3048
 *   globs the directory and includes the files
3049
 */
3050
function pfSense_handle_custom_code($src_dir) {
3051
	// Allow extending of the nat edit page and include custom input validation
3052
	if (is_dir("$src_dir")) {
3053
		$cf = glob($src_dir . "/*.inc");
3054
		foreach ($cf as $nf) {
3055
			if ($nf == "." || $nf == "..") {
3056
				continue;
3057
			}
3058
			// Include the extra handler
3059
			include_once("$nf");
3060
		}
3061
	}
3062
}
3063

    
3064
function set_language() {
3065
	global $g;
3066

    
3067
	$lang = "";
3068
	if (!empty(config_get_path('system/language'))) {
3069
		$lang = config_get_path('system/language');
3070
	} elseif (!empty($g['language'])) {
3071
		$lang = $g['language'];
3072
	}
3073
	$lang .= ".UTF-8";
3074

    
3075
	putenv("LANG={$lang}");
3076
	setlocale(LC_ALL, $lang);
3077
	textdomain("pfSense");
3078
	bindtextdomain("pfSense", "/usr/local/share/locale");
3079
	bind_textdomain_codeset("pfSense", $lang);
3080
}
3081

    
3082
function get_locale_list() {
3083
	$locales = array(
3084
		"bs" => gettext("Bosnian"),
3085
		"zh_CN" => gettext("Chinese"),
3086
		"zh_Hans_CN" => gettext("Chinese (Simplified, China)"),
3087
		"zh_Hans_HK" => gettext("Chinese (Simplified, Hong Kong SAR China)"),
3088
		"zh_Hant_TW" => gettext("Chinese (Traditional, Taiwan)"),
3089
		"nl" => gettext("Dutch"),
3090
		"en_US" => gettext("English"),
3091
		"fr" => gettext("French"),
3092
		"de_DE" => gettext("German (Germany)"),
3093
		"it" => gettext("Italian"),
3094
		"ko" => gettext("Korean"),
3095
		"nb" => gettext("Norwegian Bokmål"),
3096
		"pl" => gettext("Polish"),
3097
		"pt_PT" => gettext("Portuguese"),
3098
		"pt_BR" => gettext("Portuguese (Brazil)"),
3099
		"ru" => gettext("Russian"),
3100
		"es" => gettext("Spanish"),
3101
		"es_AR" => gettext("Spanish (Argentina)"),
3102
	);
3103

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

    
3108
	//asort($locales);
3109

    
3110
	return $locales;
3111
}
3112

    
3113
function return_hex_ipv4($ipv4) {
3114
	if (!is_ipaddrv4($ipv4)) {
3115
		return(false);
3116
	}
3117

    
3118
	/* we need the hex form of the interface IPv4 address */
3119
	$ip4arr = explode(".", $ipv4);
3120
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3121
}
3122

    
3123
function convert_ipv6_to_128bit($ipv6) {
3124
	if (!is_ipaddrv6($ipv6)) {
3125
		return(false);
3126
	}
3127

    
3128
	$ip6arr = array();
3129
	$ip6prefix = Net_IPv6::uncompress($ipv6);
3130
	$ip6arr = explode(":", $ip6prefix);
3131
	/* binary presentation of the prefix for all 128 bits. */
3132
	$ip6prefixbin = "";
3133
	foreach ($ip6arr as $element) {
3134
		$ip6prefixbin .= sprintf("%016b", hexdec($element));
3135
	}
3136
	return($ip6prefixbin);
3137
}
3138

    
3139
function convert_128bit_to_ipv6($ip6bin) {
3140
	if (strlen($ip6bin) <> 128) {
3141
		return(false);
3142
	}
3143

    
3144
	$ip6arr = array();
3145
	$ip6binarr = array();
3146
	$ip6binarr = str_split($ip6bin, 16);
3147
	foreach ($ip6binarr as $binpart) {
3148
		$ip6arr[] = dechex(bindec($binpart));
3149
	}
3150
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3151

    
3152
	return($ip6addr);
3153
}
3154

    
3155

    
3156
/* Returns the calculated bit length of the prefix delegation from the WAN interface */
3157
/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */
3158
/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */
3159
/* 6to4 is 16 bits, e.g. 65535 */
3160
function calculate_ipv6_delegation_length($if) {
3161
	$cfg = config_get_path("interfaces/{$if}");
3162
	if (!is_array($cfg)) {
3163
		return false;
3164
	}
3165

    
3166
	switch ($cfg['ipaddrv6']) {
3167
		case "6to4":
3168
			$pdlen = 16;
3169
			break;
3170
		case "6rd":
3171
			$rd6plen = explode("/", $cfg['prefix-6rd']);
3172
			$pdlen = (64 - ((int) $rd6plen[1] + (32 - (int) $cfg['prefix-6rd-v4plen'])));
3173
			break;
3174
		case "dhcp6":
3175
			$pdlen = $cfg['dhcp6-ia-pd-len'];
3176
			break;
3177
		default:
3178
			$pdlen = 0;
3179
			break;
3180
	}
3181
	return($pdlen);
3182
}
3183

    
3184
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3185
	/* convert zero-value prefix IPv6 addresses with IPv4 mapping to hex
3186
	 * see https://redmine.pfsense.org/issues/12440 */
3187
	$suffix_list = explode(':', $suffix);
3188
	if (is_ipaddrv4($suffix_list[count($suffix_list) - 1])) {
3189
		$hexsuffix = dechex(ip2long($suffix_list[count($suffix_list) - 1]));
3190
		$suffix_list[count($suffix_list) - 1] = substr($hexsuffix, 0, 4);
3191
		$suffix_list[] = substr($hexsuffix, 4, 8);
3192
		$suffix = implode(':', $suffix_list);
3193
	}	
3194
	$prefix = Net_IPv6::uncompress($prefix, true);
3195
	$suffix = Net_IPv6::uncompress($suffix, true);
3196

    
3197
	/*
3198
	 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
3199
	 *                ^^^^ ^
3200
	 *                |||| \-> 64
3201
	 *                |||\---> 63, 62, 61, 60
3202
	 *                ||\----> 56
3203
	 *                |\-----> 52
3204
	 *                \------> 48
3205
	 */
3206

    
3207
	switch ($len) {
3208
	case 48:
3209
		$prefix_len = 15;
3210
		break;
3211
	case 52:
3212
		$prefix_len = 16;
3213
		break;
3214
	case 56:
3215
		$prefix_len = 17;
3216
		break;
3217
	case 59:
3218
	case 60:
3219
		$prefix_len = 18;
3220
		break;
3221
	/*
3222
	 * XXX 63, 62 and 61 should use 18 but PD can change and if
3223
	 * we let user chose this bit it can end up out of PD network
3224
	 *
3225
	 * Leave this with 20 for now until we find a way to let user
3226
	 * chose it. The side-effect is users with PD with one of these
3227
	 * lengths will not be able to setup DHCP server range for full
3228
	 * PD size, only for last /64 network
3229
	 */
3230
	case 63:
3231
	case 62:
3232
	case 61:
3233
	default:
3234
		$prefix_len = 20;
3235
		break;
3236
	}
3237

    
3238
	return text_to_compressed_ip6(substr($prefix, 0, $prefix_len) .
3239
	    substr($suffix, $prefix_len));
3240
}
3241

    
3242
function dhcpv6_pd_str_help($pdlen) {
3243
	$result = '';
3244

    
3245
	switch ($pdlen) {
3246
	case 48:
3247
		$result = '::xxxx:xxxx:xxxx:xxxx:xxxx';
3248
		break;
3249
	case 52:
3250
		$result = '::xxx:xxxx:xxxx:xxxx:xxxx';
3251
		break;
3252
	case 56:
3253
		$result = '::xx:xxxx:xxxx:xxxx:xxxx';
3254
		break;
3255
	case 59:
3256
	case 60:
3257
		$result = '::x:xxxx:xxxx:xxxx:xxxx';
3258
		break;
3259
	/*
3260
	 * XXX 63, 62 and 61 should use same mask as 60 but if
3261
	 * we let the user choose this bit it can end up out of PD network
3262
	 *
3263
	 * Leave this with the same as 64 for now until we find a way to
3264
	 * let the user choose it. The side-effect is users with PD with one
3265
	 * of these lengths will not be able to setup DHCP server ranges
3266
	 * for full PD size, only for last /64 network
3267
	 */
3268
	case 61:
3269
	case 62:
3270
	case 63:
3271
	case 64:
3272
	default:
3273
		$result = '::xxxx:xxxx:xxxx:xxxx';
3274
		break;
3275
	}
3276

    
3277
	return $result;
3278
}
3279

    
3280
function huawei_rssi_to_string($rssi) {
3281
	$dbm = array();
3282
	$i = 0;
3283
	$dbstart = -113;
3284
	while ($i < 32) {
3285
		$dbm[$i] = $dbstart + ($i * 2);
3286
		$i++;
3287
	}
3288
	$percent = round(($rssi / 31) * 100);
3289
	$string = "rssi:{$rssi} level:{$dbm[$rssi]}dBm percent:{$percent}%";
3290
	return $string;
3291
}
3292

    
3293
function huawei_mode_to_string($mode, $submode) {
3294
	$modes[0] = gettext("None");
3295
	$modes[1] = "AMPS";
3296
	$modes[2] = "CDMA";
3297
	$modes[3] = "GSM/GPRS";
3298
	$modes[4] = "HDR";
3299
	$modes[5] = "WCDMA";
3300
	$modes[6] = "GPS";
3301

    
3302
	$submodes[0] = gettext("No Service");
3303
	$submodes[1] = "GSM";
3304
	$submodes[2] = "GPRS";
3305
	$submodes[3] = "EDGE";
3306
	$submodes[4] = "WCDMA";
3307
	$submodes[5] = "HSDPA";
3308
	$submodes[6] = "HSUPA";
3309
	$submodes[7] = "HSDPA+HSUPA";
3310
	$submodes[8] = "TD-SCDMA";
3311
	$submodes[9] = "HSPA+";
3312
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3313
	return $string;
3314
}
3315

    
3316
function huawei_service_to_string($state) {
3317
	$modes[0] = gettext("No Service");
3318
	$modes[1] = gettext("Restricted Service");
3319
	$modes[2] = gettext("Valid Service");
3320
	$modes[3] = gettext("Restricted Regional Service");
3321
	$modes[4] = gettext("Powersaving Service");
3322
	$modes[255] = gettext("Unknown Service");
3323
	$string = $modes[$state];
3324
	return $string;
3325
}
3326

    
3327
function huawei_simstate_to_string($state) {
3328
	$modes[0] = gettext("Invalid SIM/locked State");
3329
	$modes[1] = gettext("Valid SIM State");
3330
	$modes[2] = gettext("Invalid SIM CS State");
3331
	$modes[3] = gettext("Invalid SIM PS State");
3332
	$modes[4] = gettext("Invalid SIM CS/PS State");
3333
	$modes[255] = gettext("Missing SIM State");
3334
	$string = $modes[$state];
3335
	return $string;
3336
}
3337

    
3338
function zte_rssi_to_string($rssi) {
3339
	return huawei_rssi_to_string($rssi);
3340
}
3341

    
3342
function zte_mode_to_string($mode, $submode) {
3343
	$modes[0] = gettext("No Service");
3344
	$modes[1] = gettext("Limited Service");
3345
	$modes[2] = "GPRS";
3346
	$modes[3] = "GSM";
3347
	$modes[4] = "UMTS";
3348
	$modes[5] = "EDGE";
3349
	$modes[6] = "HSDPA";
3350

    
3351
	$submodes[0] = "CS_ONLY";
3352
	$submodes[1] = "PS_ONLY";
3353
	$submodes[2] = "CS_PS";
3354
	$submodes[3] = "CAMPED";
3355
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3356
	return $string;
3357
}
3358

    
3359
function zte_service_to_string($service) {
3360
	$modes[0] = gettext("Initializing Service");
3361
	$modes[1] = gettext("Network Lock error Service");
3362
	$modes[2] = gettext("Network Locked Service");
3363
	$modes[3] = gettext("Unlocked or correct MCC/MNC Service");
3364
	$string = $modes[$service];
3365
	return $string;
3366
}
3367

    
3368
function zte_simstate_to_string($state) {
3369
	$modes[0] = gettext("No action State");
3370
	$modes[1] = gettext("Network lock State");
3371
	$modes[2] = gettext("(U)SIM card lock State");
3372
	$modes[3] = gettext("Network Lock and (U)SIM card Lock State");
3373
	$string = $modes[$state];
3374
	return $string;
3375
}
3376

    
3377
function get_configured_pppoe_server_interfaces() {
3378
	$iflist = array();
3379
	foreach (config_get_path('pppoes/pppoe', []) as $pppoe) {
3380
		if ($pppoe['mode'] == "server") {
3381
			$int = "poes". $pppoe['pppoeid'];
3382
			$iflist[$int] = strtoupper($int);
3383
		}
3384
	}
3385
	return $iflist;
3386
}
3387

    
3388
function get_pppoes_child_interfaces($ifpattern) {
3389
	$if_arr = array();
3390
	if ($ifpattern == "") {
3391
		return;
3392
	}
3393

    
3394
	exec("/sbin/ifconfig", $out, $ret);
3395
	foreach ($out as $line) {
3396
		if (preg_match("/^({$ifpattern}-[0-9]+):/i", $line, $match)) {
3397
			$if_arr[] = $match[1];
3398
		}
3399
	}
3400
	return $if_arr;
3401

    
3402
}
3403

    
3404
/****f* pfsense-utils/pkg_call_plugins
3405
 * NAME
3406
 *   pkg_call_plugins
3407
 * INPUTS
3408
 *   $plugin_type value used to search in package configuration if the plugin is used, also used to create the function name
3409
 *   $plugin_params parameters to pass to the plugin function for passing multiple parameters a array can be used.
3410
 * RESULT
3411
 *   returns associative array results from the plugin calls for each package
3412
 * NOTES
3413
 *   This generic function can be used to notify or retrieve results from functions that are defined in packages.
3414
 ******/
3415
function pkg_call_plugins($plugin_type, $plugin_params) {
3416
	global $g;
3417
	$results = array();
3418
	foreach (config_get_path('installedpackages/package') as $package) {
3419
		if (is_array($package['plugins']['item'])) {
3420
			foreach ($package['plugins']['item'] as $plugin) {
3421
				if ($plugin['type'] == $plugin_type) {
3422
					if (file_exists($package['include_file'])) {
3423
						require_once($package['include_file']);
3424
					} else {
3425
						continue;
3426
					}
3427
					$pkgname = substr(reverse_strrchr($package['configurationfile'], "."), 0, -1);
3428
					$plugin_function = $pkgname . '_'. $plugin_type;
3429
					$results[$pkgname] = call_user_func($plugin_function, $plugin_params);
3430
				}
3431
			}
3432
		}
3433
	}
3434
	return $results;
3435
}
3436

    
3437
// Convert IPv6 addresses to lower case
3438
function addrtolower($ip) {
3439
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3440
		return(strtolower($ip));
3441
	} else {
3442
		return($ip);
3443
	}
3444
}
3445

    
3446
function compare_by_name($a, $b) {
3447
	return strcasecmp($a['name'], $b['name']);
3448
}
3449

    
3450
/****f* pfsense-utils/getarraybyref
3451
 * NAME
3452
 *   getarraybyref
3453
 * INPUTS
3454
 *   $array the array of which a items array needs to be found.
3455
 *   $args.. the sub-items to be retrieved/created
3456
 * RESULT
3457
 *   returns the array that was retrieved from configuration, its created if it does not exist
3458
 * NOTES
3459
 *   Used by haproxy / acme / others.?. .
3460
 *   can be used like this:  $a_certificates = getarraybyref($config, 'installedpackages', 'acme', 'certificates', 'item');
3461
 ******/
3462
function &getarraybyref(&$array) {
3463
	if (!isset($array)) {
3464
		return false;
3465
	}
3466
	if (!is_array($array)) {
3467
		$array = array();
3468
	}
3469
	$item = &$array;
3470
	$arg = func_get_args();
3471
	for($i = 1; $i < count($arg); $i++) {
3472
		$itemindex = $arg[$i];
3473
		if (!is_array($item[$itemindex])) {
3474
			$item[$itemindex] = array();
3475
		}
3476
		$item = &$item[$itemindex];
3477
	}
3478
	return $item;
3479
}
3480

    
3481
/****f* pfsense-utils/send_download_data
3482
 * NAME
3483
 *   send_download_data - Send content to a user's browser as a file to download
3484
 * INPUTS
3485
 *   $type        : The type of download, either 'data' to send the contents of
3486
 *                    a variable or 'file' to send the contents of a file on the
3487
 *                    filesystem.
3488
 *   $content     : For 'data' type, the content to send the user. For 'file'
3489
 *                    type, the full path to the file to send.
3490
 *   $userfilename: The filename presented to the user when downloading. For
3491
 *                    'file' type, this may be omitted and the basename of
3492
 *                    $content will be used instead.
3493
 *   $contenttype : MIME content type of the data. Default "application/octet-stream"
3494
 * RESULT
3495
 *   Sends the data to the browser as a file to download.
3496
 ******/
3497

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

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

    
3505
	/* Cannot determine the filename, so bail. */
3506
	if (empty($name)) {
3507
		exit;
3508
	}
3509

    
3510
	/* Send basic download headers */
3511
	header("Content-Type: {$contenttype}");
3512
	header("Content-Length: {$size}");
3513
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3514

    
3515
	/* Send cache headers */
3516
	if (isset($_SERVER['HTTPS'])) {
3517
		header('Pragma: ');
3518
		header('Cache-Control: ');
3519
	} else {
3520
		header("Pragma: private");
3521
		header("Cache-Control: private, must-revalidate");
3522
	}
3523

    
3524
	/* Ensure output buffering is off so PHP does not consume
3525
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3526
	while (ob_get_level()) {
3527
		@ob_end_clean();
3528
	}
3529

    
3530
	/* Send the data to the user */
3531
	if ($type == 'file') {
3532
		readfile($content);
3533
	} else {
3534
		echo $content;
3535
	}
3536

    
3537
	/* Flush any remaining output buffer */
3538
	@ob_end_flush();
3539
	exit;
3540
}
3541

    
3542
// Test whether the hostname in a URL can be resolved with a very short timeout
3543
function is_url_hostname_resolvable($url) {
3544
	$urlhostname = parse_url($url, PHP_URL_HOST);
3545
	if (empty($urlhostname)) {
3546
		return false;
3547
	}
3548
	putenv("RES_OPTIONS=timeout:3 attempts:1");
3549
	$resolvable = ($urlhostname !== gethostbyname($urlhostname));
3550
	putenv("RES_OPTIONS");
3551
	return $resolvable;
3552
}
3553

    
3554
function get_pf_timeouts () {
3555
	$pftimeout = array();
3556
	exec("/sbin/pfctl -st", $pfctlst, $retval);
3557
	if ($retval == 0) {
3558
		foreach ($pfctlst as $pfst) {
3559
			preg_match('/([a-z]+)\.([a-z]+)\s+([0-9]+)/', $pfst, $timeout);
3560
			if ($timeout[1] == "other") {
3561
				$proto = "Other";
3562
			} else {
3563
				$proto = strtoupper($timeout[1]);
3564
			}
3565
			if ($timeout[2] == "finwait") {
3566
				$type = "FIN Wait";
3567
			} else {
3568
				$type = ucfirst($timeout[2]);
3569
			}
3570
			$pftimeout[$proto][$type]['name'] = $proto . " " . $type;
3571
			$pftimeout[$proto][$type]['keyname'] = $timeout[1] . $timeout[2] . "timeout";
3572
			$pftimeout[$proto][$type]['value'] = $timeout[3];
3573
		}
3574
	}
3575
	return $pftimeout;
3576
}
3577

    
3578
function set_curlproxy(&$ch) {
3579
	if (!empty(config_get_path('system/proxyurl'))) {
3580
		curl_setopt($ch, CURLOPT_PROXY, config_get_path('system/proxyurl'));
3581
		if (!empty(config_get_path('system/proxyport'))) {
3582
			curl_setopt($ch, CURLOPT_PROXYPORT, config_get_path('system/proxyport'));
3583
		}
3584
		$proxyuser = config_get_path('system/proxyuser');
3585
		$proxypass = config_get_path('system/proxypass');
3586
		if (!empty($proxyuser) && !empty($proxypass)) {
3587
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
3588
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$proxyuser}:{$proxypass}");
3589
		}
3590
	}
3591
}
3592
?>
(39-39/62)