Project

General

Profile

Download (104 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(
1108
	    "hint.cordbuc.0",
1109
	    "hint.e6000sw.0",
1110
	    "hint.gpioled",
1111
	    "hint.mdio.0.at",
1112
	    "hint-model.",
1113
	    "hw.e6000sw.default_disabled",
1114
	    "hw.hn.vf_transparent",
1115
	    "hw.hn.use_if_start",
1116
	    "hw.usb.no_pf",
1117
	    "net.pf.request_maxcount",
1118
	    "vm.pmap.pti",
1119
	);
1120
	if (!$local) {
1121
		/* These values should only be filtered in loader.conf, not .local */
1122
		$remove = array_merge($remove, array(
1123
		    "autoboot_delay",
1124
		    "boot_multicons",
1125
		    "boot_serial",
1126
		    "comconsole_speed",
1127
		    "comconsole_port",
1128
		    "console",
1129
		    "debug.ddb.capture.bufsize",
1130
		    "hint.uart.0.flags",
1131
		    "hint.uart.1.flags",
1132
		    "net.link.ifqmaxlen"
1133
		));
1134
	}
1135
	foreach ($input_split as $line) {
1136
		if (empty($line)) {
1137
			continue;
1138
		}
1139
		$skip = false;
1140
		$name = explode('=', $line, 2)[0];
1141
		foreach($remove as $rkey) {
1142
			if (strncasecmp(trim($name), $rkey, strlen($rkey)) == 0) {
1143
				$skip = true;
1144
				break;
1145
			}
1146
		}
1147
		if (!$skip) {
1148
			$data[] = $line;
1149
		}
1150
	}
1151

    
1152
	return ($data);
1153
}
1154

    
1155
function setup_loader_settings($path = "", $upgrade = false) {
1156
	global $g;
1157

    
1158
	$boot_config_file = "{$path}/boot.config";
1159
	$loader_conf_file = "{$path}/boot/loader.conf";
1160

    
1161
	$serialspeed = (config_get_path('system/serialspeed', 115200));
1162

    
1163
	$vga_only = false;
1164
	$serial_only = false;
1165
	$specific_platform = system_identify_specific_platform();
1166
	$video_console_type = (get_single_sysctl("machdep.bootmethod") == "UEFI") ? "efi" : "vidconsole";
1167
	if ($specific_platform['name'] == '1540' ||
1168
	    $specific_platform['name'] == '1541') {
1169
		$vga_only = true;
1170
	} elseif ($specific_platform['name'] == 'Turbot Dual-E') {
1171
		$g['primaryconsole_force'] = "video";
1172
	} elseif ($specific_platform['name'] == 'RCC-VE' ||
1173
	    $specific_platform['name'] == 'RCC' ||
1174
	    $specific_platform['name'] == 'SG-2220' ||
1175
	    $specific_platform['name'] == 'apu2') {
1176
		$serial_only = true;
1177
	}
1178

    
1179
	/* Serial console - write out /boot.config */
1180
	if (file_exists($boot_config_file)) {
1181
		$boot_config = file_get_contents($boot_config_file);
1182
	} else {
1183
		$boot_config = "";
1184
	}
1185
	$boot_config_split = explode("\n", $boot_config);
1186
	$data = array();
1187
	foreach ($boot_config_split as $bcs) {
1188
		/* Ignore -S, -D and -h lines now */
1189
		if (!empty($bcs) && !strstr($bcs, "-S") &&
1190
		    !strstr($bcs, "-D") && !strstr($bcs, "-h")) {
1191
			$data[] = $bcs;
1192
		}
1193
	}
1194
	if ($serial_only === true) {
1195
		$data[] = "-S{$serialspeed} -h";
1196
	} elseif (is_serial_enabled()) {
1197
		$data[] = "-S{$serialspeed} -D";
1198
	}
1199

    
1200
	if (empty($data)) {
1201
		@unlink($boot_config_file);
1202
	} else {
1203
		safe_write_file($boot_config_file, $data);
1204
	}
1205
	unset($data, $boot_config, $boot_config_file, $boot_config_split);
1206

    
1207
	/* Serial console - write out /boot/loader.conf */
1208
	if ($upgrade) {
1209
		system("echo \"Reading {$loader_conf_file}...\" >> /conf/upgrade_log.txt");
1210
	}
1211

    
1212
	$data = load_loader_conf($loader_conf_file, false);
1213
	if ($serial_only === true) {
1214
		$data[] = 'boot_serial="YES"';
1215
		$data[] = 'console="comconsole"';
1216
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1217
	} elseif ($vga_only === true) {
1218
		if ($video_console_type == 'efi') {
1219
			$data[] = 'boot_serial="NO"';
1220
		}
1221
		$data[] = "console=\"{$video_console_type}\"";
1222
	} elseif (is_serial_enabled()) {
1223
		$data[] = 'boot_multicons="YES"';
1224
		$primaryconsole = isset($g['primaryconsole_force']) ?
1225
		    $g['primaryconsole_force'] :
1226
		    config_get_path('system/primaryconsole');
1227
		switch ($primaryconsole) {
1228
			case "video":
1229
				if ($video_console_type == 'efi') {
1230
					$data[] = 'boot_serial="NO"';
1231
				}
1232
				$data[] = "console=\"{$video_console_type},comconsole\"";
1233
				break;
1234
			case "serial":
1235
			default:
1236
				$data[] = 'boot_serial="YES"';
1237
				$data[] = "console=\"comconsole,{$video_console_type}\"";
1238
		}
1239
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1240
	} elseif ($video_console_type == 'efi') {
1241
		$data[] = 'boot_serial="NO"';
1242
	}
1243

    
1244
	if ($specific_platform['name'] == 'RCC-VE' ||
1245
	    $specific_platform['name'] == 'RCC' ||
1246
	    $specific_platform['name'] == 'SG-2220') {
1247
		$data[] = 'comconsole_port="0x2F8"';
1248
		$data[] = 'hint.uart.0.flags="0x00"';
1249
		$data[] = 'hint.uart.1.flags="0x10"';
1250
	}
1251
	$data[] = 'autoboot_delay="3"';
1252
	if (config_path_enabled('system','pti_disabled')) {
1253
		$data[] = 'vm.pmap.pti="0"';
1254
	}
1255

    
1256
	/* Enable ALTQ support for hnX NICs. */
1257
	if (config_path_enabled('system','hn_altq_enable')) {
1258
		$data[] = 'hw.hn.vf_transparent="0"';
1259
		$data[] = 'hw.hn.use_if_start="1"';
1260
	}
1261

    
1262
	/* Set maximum send queue length. */
1263
	$data[] = 'net.link.ifqmaxlen="128"';
1264

    
1265
	safe_write_file($loader_conf_file, $data);
1266

    
1267
	/* Filter loader.conf.local to avoid duplicate settings. */
1268
	$loader_conf_file = "{$path}/boot/loader.conf.local";
1269
	$data = load_loader_conf($loader_conf_file, true);
1270
	if (empty($data)) {
1271
		@unlink($loader_conf_file);
1272
	} else {
1273
		safe_write_file($loader_conf_file, $data);
1274
	}
1275

    
1276
}
1277

    
1278
function console_configure($when = "save", $path = "") {
1279
	$ttys_file = "{$path}/etc/ttys";
1280

    
1281
	/* Update the loader settings. */
1282
	setup_loader_settings($path, ($when == "upgrade"));
1283

    
1284
	$ttys = file_get_contents($ttys_file);
1285
	$ttys_split = explode("\n", $ttys);
1286

    
1287
	$data = array();
1288

    
1289
	$on_off = (is_serial_enabled() ? 'onifconsole' : 'off');
1290

    
1291
	if (config_path_enabled('system','disableconsolemenu')) {
1292
		$console_type = 'Pc';
1293
		$serial_type = '3wire';
1294
	} else {
1295
		$console_type = 'al.Pc';
1296
		$serial_type = 'al.3wire';
1297
	}
1298

    
1299
	$console_line = "console\tnone\t\t\t\tunknown\toff\tsecure";
1300
	$virt_line =
1301
	    "\"/usr/libexec/getty {$console_type}\"\txterm\tonifexists secure";
1302
	$ttyu_line =
1303
	    "\"/usr/libexec/getty {$serial_type}\"\tvt100\t{$on_off}\tsecure";
1304

    
1305
	$found = array();
1306

    
1307
	foreach ($ttys_split as $tty) {
1308
		/* Ignore blank lines */
1309
		if (empty($tty)) {
1310
			continue;
1311
		}
1312

    
1313
		if (stristr($tty, "ttyv0")) {
1314
			$found['ttyv0'] = 1;
1315
			$data[] = "ttyv0\t{$virt_line}";
1316
		} elseif (stristr($tty, "xc0")) {
1317
			$found['xc0'] = 1;
1318
			$data[] = "xc0\t{$virt_line}";
1319
		} elseif (stristr($tty, "ttyu")) {
1320
			$ttyn = substr($tty, 0, 5);
1321
			$found[$ttyn] = 1;
1322
			$data[] = "{$ttyn}\t{$ttyu_line}";
1323
		} elseif (substr($tty, 0, 7) == 'console') {
1324
			$found['console'] = 1;
1325
			$data[] = $tty;
1326
		} else {
1327
			$data[] = $tty;
1328
		}
1329
	}
1330
	unset($on_off, $console_type, $serial_type);
1331

    
1332
	/* Detect missing main lines on original file and try to rebuild it */
1333
	$items = array(
1334
		'console',
1335
		'ttyv0',
1336
		'ttyu0',
1337
		'ttyu1',
1338
		'ttyu2',
1339
		'ttyu3',
1340
		'xc0'
1341
	);
1342

    
1343
	foreach ($items as $item) {
1344
		if (isset($found[$item])) {
1345
			continue;
1346
		}
1347

    
1348
		if ($item == 'console') {
1349
			$data[] = $console_line;
1350
		} elseif (($item == 'ttyv0') || ($item == 'xc0')) {
1351
			/* xc0 - Xen console, see https://redmine.pfsense.org/issues/11402 */
1352
			$data[] = "{$item}\t{$virt_line}";
1353
		} else {
1354
			$data[] = "{$item}\t{$ttyu_line}";
1355
		}
1356
	}
1357

    
1358
	safe_write_file($ttys_file, $data);
1359

    
1360
	unset($ttys, $ttys_file, $ttys_split, $data);
1361

    
1362
	if ($when != "upgrade") {
1363
		reload_ttys();
1364
	}
1365

    
1366
	return;
1367
}
1368

    
1369
function is_serial_enabled() {
1370
	global $g;
1371

    
1372
	if (!isset($g['enableserial_force']) &&
1373
	    !config_path_enabled('system','enableserial')) {
1374
		return false;
1375
	}
1376

    
1377
	return true;
1378
}
1379

    
1380
function reload_ttys() {
1381
	// Send a HUP signal to init will make it reload /etc/ttys
1382
	posix_kill(1, SIGHUP);
1383
}
1384

    
1385
function print_value_list($list, $count = 10, $separator = ",") {
1386
	$ret = implode($separator, array_slice($list, 0, $count));
1387
	if (count($list) < $count) {
1388
		$ret .= ".";
1389
	} else {
1390
		$ret .= "...";
1391
	}
1392
	return $ret;
1393
}
1394

    
1395
/* DHCP enabled on any interfaces? */
1396
function is_dhcp_server_enabled() {
1397
	foreach (config_get_path('dhcpd', []) as $dhcpif => $dhcpifconf) {
1398
		if (isset($dhcpifconf['enable']) &&
1399
			!empty(config_get_path("interfaces/{$dhcpif}"))) {
1400
			return true;
1401
		}
1402
	}
1403

    
1404
	return false;
1405
}
1406

    
1407
/* DHCP enabled on any interfaces? */
1408
function is_dhcpv6_server_enabled() {
1409
	foreach (config_get_path('interfaces', []) as $ifcfg) {
1410
		if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface'])) {
1411
			return true;
1412
		}
1413
	}
1414

    
1415
	foreach (config_get_path('dhcpdv6', []) as $dhcpv6if => $dhcpv6ifconf) {
1416
		if (isset($dhcpv6ifconf['enable']) &&
1417
			!empty(config_get_path("interfaces/{$dhcpv6if}"))) {
1418
			return true;
1419
		}
1420
	}
1421

    
1422
	return false;
1423
}
1424

    
1425
/* radvd enabled on any interfaces? */
1426
function is_radvd_enabled() {
1427
	init_config_arr(['dhcpdv6']);
1428
	$dhcpdv6cfg = config_get_path('dhcpdv6', []);
1429
	$Iflist = get_configured_interface_list();
1430

    
1431
	/* handle manually configured DHCP6 server settings first */
1432
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1433
		if (config_path_enabled("interfaces/{$dhcpv6if}")) {
1434
			continue;
1435
		}
1436

    
1437
		if (!isset($dhcpv6ifconf['ramode'])) {
1438
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
1439
		}
1440

    
1441
		if ($dhcpv6ifconf['ramode'] == "disabled") {
1442
			continue;
1443
		}
1444

    
1445
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1446
		if (!is_ipaddrv6($ifcfgipv6)) {
1447
			continue;
1448
		}
1449

    
1450
		return true;
1451
	}
1452

    
1453
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
1454
	foreach (array_keys($Iflist) as $if) {
1455
		if (!config_path_enabled("interfaces/{$if}", 'track6-interface')) {
1456
			continue;
1457
		}
1458
		if (!config_path_enabled("interfaces/{$if}")) {
1459
			continue;
1460
		}
1461

    
1462
		$ifcfgipv6 = get_interface_ipv6($if);
1463
		if (!is_ipaddrv6($ifcfgipv6)) {
1464
			continue;
1465
		}
1466

    
1467
		$ifcfgsnv6 = get_interface_subnetv6($if);
1468
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1469

    
1470
		if (!is_ipaddrv6($subnetv6)) {
1471
			continue;
1472
		}
1473

    
1474
		return true;
1475
	}
1476

    
1477
	return false;
1478
}
1479

    
1480
/* Any PPPoE servers enabled? */
1481
function is_pppoe_server_enabled() {
1482
	$pppoeenable = false;
1483

    
1484
	foreach (config_get_path('pppoes/pppoe', []) as $pppoes) {
1485
		if ($pppoes['mode'] == 'server') {
1486
			$pppoeenable = true;
1487
		}
1488
	}
1489

    
1490
	return $pppoeenable;
1491
}
1492

    
1493
/* Optional arg forces hh:mm:ss without days */
1494
function convert_seconds_to_dhms($sec, $showhoursonly = false) {
1495
	if (!is_numericint($sec)) {
1496
		return '-';
1497
	}
1498
	// FIXME: When we move to PHP 7 we can use "intdiv($sec % X, Y)" etc
1499
	list($d, $h, $m, $s) = array(	(int)($showhoursonly ? 0 : $sec/86400),
1500
					(int)(($showhoursonly ? $sec : $sec % 86400)/3600),
1501
					(int)(($sec % 3600)/60),
1502
					$sec % 60
1503
				);
1504
	return ($d > 0 ? $d . 'd ' : '') . sprintf('%02d:%02d:%02d', $h, $m, $s);
1505
}
1506

    
1507
/* Compute the total uptime from the ppp uptime log file in the conf directory */
1508

    
1509
function get_ppp_uptime($port) {
1510
	if (file_exists("/conf/{$port}.log")) {
1511
		$saved_time = file_get_contents("/conf/{$port}.log");
1512
		$uptime_data = explode("\n", $saved_time);
1513
		$sec = 0;
1514
		foreach ($uptime_data as $upt) {
1515
			$sec += substr($upt, 1 + strpos($upt, " "));
1516
		}
1517
		return convert_seconds_to_dhms($sec);
1518
	} else {
1519
		$total_time = gettext("No history data found!");
1520
		return $total_time;
1521
	}
1522
}
1523

    
1524
//returns interface information
1525
function get_interface_info($ifdescr) {
1526
	global $g;
1527

    
1528
	$ifinfo = array();
1529
	if (empty(config_get_path("interfaces/{$ifdescr}"))) {
1530
		return;
1531
	}
1532
	$ifinfo['hwif'] = config_get_path("interfaces/{$ifdescr}/if");
1533
	$ifinfo['enable'] = config_path_enabled("interfaces/{$ifdescr}");
1534
	$ifinfo['if'] = get_real_interface($ifdescr);
1535

    
1536
	$chkif = $ifinfo['if'];
1537
	$ifinfotmp = get_interface_addresses($chkif);
1538
	$ifinfo['status'] = $ifinfotmp['status'];
1539
	if (empty($ifinfo['status'])) {
1540
		$ifinfo['status'] = "down";
1541
	}
1542
	$ifinfo['macaddr'] = $ifinfotmp['macaddr'];
1543
	$ifinfo['mtu'] = $ifinfotmp['mtu'];
1544
	$ifinfo['ipaddr'] = $ifinfotmp['ipaddr'];
1545
	$ifinfo['subnet'] = $ifinfotmp['subnet'];
1546
	$ifinfo['linklocal'] = get_interface_linklocal($ifdescr);
1547
	$ifinfo['ipaddrv6'] = get_interface_ipv6($ifdescr);
1548
	$ifinfo['subnetv6'] = get_interface_subnetv6($ifdescr);
1549
	if (isset($ifinfotmp['link0'])) {
1550
		$link0 = "down";
1551
	}
1552
	$ifinfotmp = pfSense_get_interface_stats($chkif);
1553
	// $ifinfo['inpkts'] = $ifinfotmp['inpkts'];
1554
	// $ifinfo['outpkts'] = $ifinfotmp['outpkts'];
1555
	$ifinfo['inerrs'] = $ifinfotmp['inerrs'];
1556
	$ifinfo['outerrs'] = $ifinfotmp['outerrs'];
1557
	$ifinfo['collisions'] = $ifinfotmp['collisions'];
1558

    
1559
	/* Use pfctl for non wrapping 64 bit counters */
1560
	/* Pass */
1561
	exec("/sbin/pfctl -vvsI -i {$chkif}", $pfctlstats);
1562
	$pf_in4_pass = preg_split("/ +/ ", $pfctlstats[3]);
1563
	$pf_out4_pass = preg_split("/ +/", $pfctlstats[5]);
1564
	$pf_in6_pass = preg_split("/ +/ ", $pfctlstats[7]);
1565
	$pf_out6_pass = preg_split("/ +/", $pfctlstats[9]);
1566
	$in4_pass = $pf_in4_pass[5];
1567
	$out4_pass = $pf_out4_pass[5];
1568
	$in4_pass_packets = $pf_in4_pass[3];
1569
	$out4_pass_packets = $pf_out4_pass[3];
1570
	$in6_pass = $pf_in6_pass[5];
1571
	$out6_pass = $pf_out6_pass[5];
1572
	$in6_pass_packets = $pf_in6_pass[3];
1573
	$out6_pass_packets = $pf_out6_pass[3];
1574
	$ifinfo['inbytespass'] = $in4_pass + $in6_pass;
1575
	$ifinfo['outbytespass'] = $out4_pass + $out6_pass;
1576
	$ifinfo['inpktspass'] = $in4_pass_packets + $in6_pass_packets;
1577
	$ifinfo['outpktspass'] = $out4_pass_packets + $out6_pass_packets;
1578

    
1579
	/* Block */
1580
	$pf_in4_block = preg_split("/ +/", $pfctlstats[4]);
1581
	$pf_out4_block = preg_split("/ +/", $pfctlstats[6]);
1582
	$pf_in6_block = preg_split("/ +/", $pfctlstats[8]);
1583
	$pf_out6_block = preg_split("/ +/", $pfctlstats[10]);
1584
	$in4_block = $pf_in4_block[5];
1585
	$out4_block = $pf_out4_block[5];
1586
	$in4_block_packets = $pf_in4_block[3];
1587
	$out4_block_packets = $pf_out4_block[3];
1588
	$in6_block = $pf_in6_block[5];
1589
	$out6_block = $pf_out6_block[5];
1590
	$in6_block_packets = $pf_in6_block[3];
1591
	$out6_block_packets = $pf_out6_block[3];
1592
	$ifinfo['inbytesblock'] = $in4_block + $in6_block;
1593
	$ifinfo['outbytesblock'] = $out4_block + $out6_block;
1594
	$ifinfo['inpktsblock'] = $in4_block_packets + $in6_block_packets;
1595
	$ifinfo['outpktsblock'] = $out4_block_packets + $out6_block_packets;
1596

    
1597
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1598
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1599
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1600
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1601

    
1602
	$link_type = config_get_path("interfaces/{$ifdescr}/ipaddr");
1603
	switch ($link_type) {
1604
		/* DHCP? -> see if dhclient is up */
1605
		case "dhcp":
1606
			/* see if dhclient is up */
1607
			if (find_dhclient_process($ifinfo['if']) != 0) {
1608
				$ifinfo['dhcplink'] = "up";
1609
			} else {
1610
				$ifinfo['dhcplink'] = "down";
1611
			}
1612

    
1613
			break;
1614
		/* PPPoE/PPTP/L2TP interface? -> get status from virtual interface */
1615
		case "pppoe":
1616
		case "pptp":
1617
		case "l2tp":
1618
			if ($ifinfo['status'] == "up" && !isset($link0)) {
1619
				/* get PPPoE link status for dial on demand */
1620
				$ifinfo["{$link_type}link"] = "up";
1621
			} else {
1622
				$ifinfo["{$link_type}link"] = "down";
1623
			}
1624

    
1625
			break;
1626
		/* PPP interface? -> get uptime for this session and cumulative uptime from the persistent log file in conf */
1627
		case "ppp":
1628
			if ($ifinfo['status'] == "up") {
1629
				$ifinfo['ppplink'] = "up";
1630
			} else {
1631
				$ifinfo['ppplink'] = "down" ;
1632
			}
1633

    
1634
			if (empty($ifinfo['status'])) {
1635
				$ifinfo['status'] = "down";
1636
			}
1637

    
1638
			foreach (config_get_path('ppps/ppp', []) as $ppp) {
1639
				if (config_get_path("interfaces/{$ifdescr}/if") == $ppp['if']) {
1640
					break;
1641
				}
1642
			}
1643
			$dev = $ppp['ports'];
1644
			if (config_get_path("interfaces/{$ifdescr}/if") != $ppp['if'] || empty($dev)) {
1645
				break;
1646
			}
1647
			if (!file_exists($dev)) {
1648
				$ifinfo['nodevice'] = 1;
1649
				$ifinfo['pppinfo'] = $dev . " " . gettext("device not present! Is the modem attached to the system?");
1650
			}
1651

    
1652
			$usbmodemoutput = array();
1653
			exec("/usr/sbin/usbconfig", $usbmodemoutput);
1654
			$mondev = "{$g['tmp_path']}/3gstats.{$ifdescr}";
1655
			if (file_exists($mondev)) {
1656
				$cellstats = file($mondev);
1657
				/* skip header */
1658
				$a_cellstats = explode(",", $cellstats[1]);
1659
				if (preg_match("/huawei/i", implode("\n", $usbmodemoutput))) {
1660
					$ifinfo['cell_rssi'] = huawei_rssi_to_string($a_cellstats[1]);
1661
					$ifinfo['cell_mode'] = huawei_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1662
					$ifinfo['cell_simstate'] = huawei_simstate_to_string($a_cellstats[10]);
1663
					$ifinfo['cell_service'] = huawei_service_to_string(trim($a_cellstats[11]));
1664
				}
1665
				if (preg_match("/zte/i", implode("\n", $usbmodemoutput))) {
1666
					$ifinfo['cell_rssi'] = zte_rssi_to_string($a_cellstats[1]);
1667
					$ifinfo['cell_mode'] = zte_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1668
					$ifinfo['cell_simstate'] = zte_simstate_to_string($a_cellstats[10]);
1669
					$ifinfo['cell_service'] = zte_service_to_string(trim($a_cellstats[11]));
1670
				}
1671
				$ifinfo['cell_upstream'] = $a_cellstats[4];
1672
				$ifinfo['cell_downstream'] = trim($a_cellstats[5]);
1673
				$ifinfo['cell_sent'] = $a_cellstats[6];
1674
				$ifinfo['cell_received'] = trim($a_cellstats[7]);
1675
				$ifinfo['cell_bwupstream'] = $a_cellstats[8];
1676
				$ifinfo['cell_bwdownstream'] = trim($a_cellstats[9]);
1677
			}
1678
			// Calculate cumulative uptime for PPP link. Useful for connections that have per minute/hour contracts so you don't go over!
1679
			if (isset($ppp['uptime'])) {
1680
				$ifinfo['ppp_uptime_accumulated'] = "(".get_ppp_uptime($ifinfo['if']).")";
1681
			}
1682
			break;
1683
		default:
1684
			break;
1685
	}
1686

    
1687
	if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) {
1688
		$sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`);
1689
		$ifinfo['ppp_uptime'] = convert_seconds_to_dhms($sec);
1690
	}
1691

    
1692
	if ($ifinfo['status'] == "up") {
1693
		/* try to determine media with ifconfig */
1694
		$ifconfiginfo = [];
1695
		exec("/sbin/ifconfig -v " . $ifinfo['if'], $ifconfiginfo);
1696
		$wifconfiginfo = [];
1697
		if (is_interface_wireless($ifdescr)) {
1698
			exec("/sbin/ifconfig {$ifinfo['if']} list sta", $wifconfiginfo);
1699
			array_shift($wifconfiginfo);
1700
		}
1701
		$matches = "";
1702
		foreach ($ifconfiginfo as $ici) {
1703

    
1704
			/* don't list media/speed for wireless cards, as it always
1705
			   displays 2 Mbps even though clients can connect at 11 Mbps */
1706
			if (preg_match("/media: .*? \((.*?)\)/", $ici, $matches)) {
1707
				$ifinfo['media'] = $matches[1];
1708
			} else if (preg_match("/media: Ethernet (.*)/", $ici, $matches)) {
1709
				$ifinfo['media'] = $matches[1];
1710
			} else if (preg_match("/media: IEEE 802.11 Wireless Ethernet (.*)/", $ici, $matches)) {
1711
				$ifinfo['media'] = $matches[1];
1712
			}
1713

    
1714
			if (preg_match("/status: (.*)$/", $ici, $matches)) {
1715
				if ($matches[1] != "active") {
1716
					$ifinfo['status'] = $matches[1];
1717
				}
1718
				if ($ifinfo['status'] == gettext("running")) {
1719
					$ifinfo['status'] = gettext("up");
1720
				}
1721
			}
1722
			if (preg_match("/channel (\S*)/", $ici, $matches)) {
1723
				$ifinfo['channel'] = $matches[1];
1724
			}
1725
			if (preg_match("/ssid (\".*?\"|\S*)/", $ici, $matches)) {
1726
				if ($matches[1][0] == '"') {
1727
					$ifinfo['ssid'] = substr($matches[1], 1, -1);
1728
				}
1729
				else {
1730
					$ifinfo['ssid'] = $matches[1];
1731
				}
1732
			}
1733
			if (preg_match("/laggproto (.*)$/", $ici, $matches)) {
1734
				$ifinfo['laggproto'] = $matches[1];
1735
			}
1736
			if (preg_match("/laggport: (.*)$/", $ici, $matches)) {
1737
				$ifinfo['laggport'][] = $matches[1];
1738
			}
1739
			if (preg_match("/plugged: (.*)$/", $ici, $matches)) {
1740
				$ifinfo['plugged'] = $matches[1];
1741
			}
1742
			if (preg_match("/vendor: (.*)$/", $ici, $matches)) {
1743
				$ifinfo['vendor'] = $matches[1];
1744
			}
1745
			if (preg_match("/module temperature: (.*) Voltage: (.*)$/", $ici, $matches)) {
1746
				$ifinfo['temperature'] = $matches[1];
1747
				$ifinfo['voltage'] = $matches[2];
1748
			}
1749
			if (preg_match("/RX: (.*) TX: (.*)$/", $ici, $matches)) {
1750
				$ifinfo['rx'] = $matches[1];
1751
				$ifinfo['tx'] = $matches[2];
1752
			}
1753
		}
1754
		foreach ($wifconfiginfo as $ici) {
1755
			$elements = preg_split("/[ ]+/i", $ici);
1756
			if ($elements[0] != "") {
1757
				$ifinfo['bssid'] = $elements[0];
1758
			}
1759
			if ($elements[3] != "") {
1760
				$ifinfo['rate'] = $elements[3];
1761
			}
1762
			if ($elements[4] != "") {
1763
				$ifinfo['rssi'] = $elements[4];
1764
			}
1765
		}
1766
		/* lookup the gateway */
1767
		if (interface_has_gateway($ifdescr)) {
1768
			$ifinfo['gateway'] = get_interface_gateway($ifdescr);
1769
		}
1770
		if (interface_has_gatewayv6($ifdescr)) {
1771
			$ifinfo['gatewayv6'] = get_interface_gateway_v6($ifdescr);
1772
		}
1773
	}
1774

    
1775
	$bridge = "";
1776
	$bridge = link_interface_to_bridge($ifdescr);
1777
	if ($bridge) {
1778
		$bridge_text = `/sbin/ifconfig {$bridge}`;
1779
		if (stristr($bridge_text, "blocking") <> false) {
1780
			$ifinfo['bridge'] = "<b><font color='red'>" . gettext("blocking") . "</font></b> - " . gettext("check for ethernet loops");
1781
			$ifinfo['bridgeint'] = $bridge;
1782
		} else if (stristr($bridge_text, "learning") <> false) {
1783
			$ifinfo['bridge'] = gettext("learning");
1784
			$ifinfo['bridgeint'] = $bridge;
1785
		} else if (stristr($bridge_text, "forwarding") <> false) {
1786
			$ifinfo['bridge'] = gettext("forwarding");
1787
			$ifinfo['bridgeint'] = $bridge;
1788
		}
1789
	}
1790

    
1791
	return $ifinfo;
1792
}
1793

    
1794
//returns cpu speed of processor. Good for determining capabilities of machine
1795
function get_cpu_speed() {
1796
	return get_single_sysctl("hw.clockrate");
1797
}
1798

    
1799
function get_uptime_sec() {
1800
	$boottime = "";
1801
	$matches = "";
1802
	$boottime = get_single_sysctl("kern.boottime");
1803
	preg_match("/sec = (\d+)/", $boottime, $matches);
1804
	$boottime = $matches[1];
1805
	if (intval($boottime) == 0) {
1806
		return 0;
1807
	}
1808

    
1809
	$uptime = time() - $boottime;
1810
	return $uptime;
1811
}
1812

    
1813
function resolve_host_addresses($host, $recordtypes = array(DNS_A, DNS_AAAA, DNS_CNAME), $index = true) {
1814
	$dnsresult = array();
1815
	$resolved = array();
1816
	$errreporting = error_reporting();
1817
	error_reporting($errreporting & ~E_WARNING);// dns_get_record throws a warning if nothing is resolved..
1818
	foreach ($recordtypes as $recordtype) {
1819
		$tmp = dns_get_record($host, $recordtype);
1820
		if (is_array($tmp)) {
1821
			$dnsresult = array_merge($dnsresult, $tmp);
1822
		}
1823
	}
1824
	error_reporting($errreporting);// restore original php warning/error settings.
1825
	foreach ($dnsresult as $item) {
1826
		$newitem = array();
1827
		$newitem['type'] = $item['type'];
1828
		switch ($item['type']) {
1829
			case 'CNAME':
1830
				$newitem['data'] = $item['target'];
1831
				$resolved[] = $newitem;
1832
				break;
1833
			case 'A':
1834
				$newitem['data'] = $item['ip'];
1835
				$resolved[] = $newitem;
1836
				break;
1837
			case 'AAAA':
1838
				$newitem['data'] = $item['ipv6'];
1839
				$resolved[] = $newitem;
1840
				break;
1841
		}
1842
	}
1843
	if ($index == false) {
1844
		foreach ($resolved as $res) {
1845
			$noind[] = $res['data'];
1846
		}
1847
		$resolved = $noind;
1848
	}
1849
	return $resolved;
1850
}
1851

    
1852
function add_hostname_to_watch($hostname) {
1853
	if (!is_dir("/var/db/dnscache")) {
1854
		mkdir("/var/db/dnscache");
1855
	}
1856
	$result = array();
1857
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1858
		$contents = "";
1859
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1860
		if (!empty($domips)) {
1861
			foreach ($domips as $ip) {
1862
				$contents .= "$ip\n";
1863
			}
1864
		}
1865
		file_put_contents("/var/db/dnscache/$hostname", $contents);
1866
		/* Remove empty elements */
1867
		$result = array_filter(explode("\n", $contents), 'strlen');
1868
	}
1869
	return $result;
1870
}
1871

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

    
1876
function pfsense_default_state_size() {
1877
	/* get system memory amount */
1878
	$memory = get_memory();
1879
	$physmem = $memory[0];
1880
	/* Be cautious and only allocate 10% of system memory to the state table */
1881
	$max_states = (int) ($physmem/10)*1000;
1882
	return $max_states;
1883
}
1884

    
1885
function pfsense_default_tables_size() {
1886
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1887
	return $current;
1888
}
1889

    
1890
function pfsense_default_table_entries_size() {
1891
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1892
	return (trim($current));
1893
}
1894

    
1895
/* Compare the current hostname DNS to the DNS cache we made
1896
 * if it has changed we return the old records
1897
 * if no change we return false */
1898
function compare_hostname_to_dnscache($hostname) {
1899
	global $g;
1900
	if (!is_dir("/var/db/dnscache")) {
1901
		mkdir("/var/db/dnscache");
1902
	}
1903
	$hostname = trim($hostname);
1904
	if (is_readable("/var/db/dnscache/{$hostname}")) {
1905
		$oldcontents = file_get_contents("/var/db/dnscache/{$hostname}");
1906
	} else {
1907
		$oldcontents = "";
1908
	}
1909
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1910
		$contents = "";
1911
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1912
		if (!empty($domips)) {
1913
			foreach ($domips as $ip) {
1914
				$contents .= "$ip\n";
1915
			}
1916
		}
1917
	}
1918

    
1919
	if (trim($oldcontents) != trim($contents)) {
1920
		if ($g['debug']) {
1921
			log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents));
1922
		}
1923
		return ($oldcontents);
1924
	} else {
1925
		return false;
1926
	}
1927
}
1928

    
1929
/*
1930
 * load_crypto() - Load crypto modules if enabled in config.
1931
 */
1932
function load_crypto() {
1933
	$crypto_modules = array('aesni', 'cryptodev');
1934

    
1935
	$enabled_modules = explode('_', config_get_path('system/crypto_hardware'));
1936

    
1937
	foreach ($enabled_modules as $enmod) {
1938
		if (empty($enmod) || !in_array($enmod, $crypto_modules)) {
1939
			continue;
1940
		}
1941
		if (!is_module_loaded($enmod)) {
1942
			log_error(sprintf(gettext("Loading %s cryptographic accelerator module."), $enmod));
1943
			mute_kernel_msgs();
1944
			mwexec("/sbin/kldload " . escapeshellarg($enmod));
1945
			unmute_kernel_msgs();
1946
		}
1947
	}
1948
}
1949

    
1950
/*
1951
 * load_thermal_hardware() - Load temperature monitor kernel module
1952
 */
1953
function load_thermal_hardware() {
1954
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
1955
	$thermal_hardware = config_get_path('system/thermal_hardware');
1956

    
1957
	if (!in_array($thermal_hardware, $thermal_hardware_modules)) {
1958
		return false;
1959
	}
1960

    
1961
	if (!empty($thermal_hardware) && !is_module_loaded($thermal_hardware)) {
1962
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $thermal_hardware));
1963
		mute_kernel_msgs();
1964
		mwexec("/sbin/kldload {$thermal_hardware}");
1965
		unmute_kernel_msgs();
1966
	}
1967
}
1968

    
1969
/****f* pfsense-utils/isvm
1970
 * NAME
1971
 *   isvm
1972
 * INPUTS
1973
 *	none
1974
 * RESULT
1975
 *   returns true if machine is running under a virtual environment
1976
 ******/
1977
function isvm() {
1978
	$virtualenvs = array("vmware", "parallels", "qemu", "bochs", "plex86", "VirtualBox");
1979
	exec('/bin/kenv -q smbios.system.product 2>/dev/null', $output, $rc);
1980

    
1981
	if ($rc != 0 || !isset($output[0])) {
1982
		return false;
1983
	}
1984

    
1985
	foreach ($virtualenvs as $virtualenv) {
1986
		if (stripos($output[0], $virtualenv) !== false) {
1987
			return true;
1988
		}
1989
	}
1990

    
1991
	return false;
1992
}
1993

    
1994
function get_freebsd_version() {
1995
	$version = explode(".", php_uname("r"));
1996
	return $version[0];
1997
}
1998

    
1999
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
2000
	global $g;
2001

    
2002
	$fp = fopen($destination, "wb");
2003

    
2004
	if (!$fp) {
2005
		return false;
2006
	}
2007

    
2008
	$ch = curl_init();
2009
	curl_setopt($ch, CURLOPT_URL, $url);
2010
	if ($verify_ssl) {
2011
		curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
2012
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
2013
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
2014
	} else {
2015
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
2016
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2017
		curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false);
2018
	}
2019
	curl_setopt($ch, CURLOPT_FILE, $fp);
2020
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2021
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2022
	curl_setopt($ch, CURLOPT_HEADER, false);
2023
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2024
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
2025
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2026
	} else {
2027
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
2028
	}
2029

    
2030
	set_curlproxy($ch);
2031

    
2032
	@curl_exec($ch);
2033
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2034
	fclose($fp);
2035
	curl_close($ch);
2036
	if ($http_code == 200) {
2037
		return true;
2038
	} else {
2039
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2040
		unlink_if_exists($destination);
2041
		return false;
2042
	}
2043
}
2044

    
2045
function download_file_with_progress_bar($url, $destination, $verify_ssl = true, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) {
2046
	global $g, $ch, $fout, $file_size, $downloaded, $first_progress_update;
2047
	$file_size = 1;
2048
	$downloaded = 1;
2049
	$first_progress_update = TRUE;
2050
	/* open destination file */
2051
	$fout = fopen($destination, "wb");
2052

    
2053
	if (!$fout) {
2054
		return false;
2055
	}
2056
	/*
2057
	 *      Originally by Author: Keyvan Minoukadeh
2058
	 *      Modified by Scott Ullrich to return Content-Length size
2059
	 */
2060
	$ch = curl_init();
2061
	curl_setopt($ch, CURLOPT_URL, $url);
2062
	if ($verify_ssl) {
2063
		curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
2064
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
2065
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
2066
	} else {
2067
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
2068
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2069
		curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false);
2070
	}
2071
	curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
2072
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2073
	curl_setopt($ch, CURLOPT_WRITEFUNCTION, $readbody);
2074
	curl_setopt($ch, CURLOPT_NOPROGRESS, '1');
2075
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2076
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2077
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
2078
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2079
	} else {
2080
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
2081
	}
2082

    
2083
	set_curlproxy($ch);
2084

    
2085
	@curl_exec($ch);
2086
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2087
	fclose($fout);
2088
	curl_close($ch);
2089
	if ($http_code == 200) {
2090
		return true;
2091
	} else {
2092
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2093
		unlink_if_exists($destination);
2094
		return false;
2095
	}
2096
}
2097

    
2098
function read_header($ch, $string) {
2099
	global $file_size;
2100
	$length = strlen($string);
2101
	$regs = "";
2102
	preg_match("/(Content-Length:) (.*)/", $string, $regs);
2103
	if ($regs[2] <> "") {
2104
		$file_size = intval($regs[2]);
2105
	}
2106
	ob_flush();
2107
	return $length;
2108
}
2109

    
2110
function read_body($ch, $string) {
2111
	global $fout, $file_size, $downloaded, $sendto, $static_status, $static_output, $lastseen, $first_progress_update;
2112
	global $pkg_interface;
2113
	$length = strlen($string);
2114
	$downloaded += intval($length);
2115
	if ($file_size > 0) {
2116
		$downloadProgress = round(100 * (1 - $downloaded / $file_size), 0);
2117
		$downloadProgress = 100 - $downloadProgress;
2118
	} else {
2119
		$downloadProgress = 0;
2120
	}
2121
	if ($lastseen <> $downloadProgress and $downloadProgress < 101) {
2122
		if ($sendto == "status") {
2123
			if ($pkg_interface == "console") {
2124
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2125
					$tostatus = $static_status . $downloadProgress . "%";
2126
					if ($downloadProgress == 100) {
2127
						$tostatus = $tostatus . "\r";
2128
					}
2129
					update_status($tostatus);
2130
				}
2131
			} else {
2132
				$tostatus = $static_status . $downloadProgress . "%";
2133
				update_status($tostatus);
2134
			}
2135
		} else {
2136
			if ($pkg_interface == "console") {
2137
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2138
					$tooutput = $static_output . $downloadProgress . "%";
2139
					if ($downloadProgress == 100) {
2140
						$tooutput = $tooutput . "\r";
2141
					}
2142
					update_output_window($tooutput);
2143
				}
2144
			} else {
2145
				$tooutput = $static_output . $downloadProgress . "%";
2146
				update_output_window($tooutput);
2147
			}
2148
		}
2149
		if (($pkg_interface != "console") || (($downloadProgress % 10) == 0) || ($downloadProgress < 10)) {
2150
			update_progress_bar($downloadProgress, $first_progress_update);
2151
			$first_progress_update = FALSE;
2152
		}
2153
		$lastseen = $downloadProgress;
2154
	}
2155
	if ($fout) {
2156
		fwrite($fout, $string);
2157
	}
2158
	ob_flush();
2159
	return $length;
2160
}
2161

    
2162
/*
2163
 *   update_output_window: update bottom textarea dynamically.
2164
 */
2165
function update_output_window($text) {
2166
	global $pkg_interface;
2167
	$log = preg_replace("/\n/", "\\n", $text);
2168
	if ($pkg_interface != "console") {
2169
?>
2170
<script type="text/javascript">
2171
//<![CDATA[
2172
	document.getElementById("output").textContent="<?=htmlspecialchars($log)?>";
2173
	document.getElementById("output").scrollTop = document.getElementById("output").scrollHeight;
2174
//]]>
2175
</script>
2176
<?php
2177
	}
2178
	/* ensure that contents are written out */
2179
	ob_flush();
2180
}
2181

    
2182
/*
2183
 *   update_status: update top textarea dynamically.
2184
 */
2185
function update_status($status) {
2186
	global $pkg_interface;
2187

    
2188
	if ($pkg_interface == "console") {
2189
		print ("{$status}");
2190
	}
2191

    
2192
	/* ensure that contents are written out */
2193
	ob_flush();
2194
}
2195

    
2196
/*
2197
 * update_progress_bar($percent, $first_time): updates the javascript driven progress bar.
2198
 */
2199
function update_progress_bar($percent, $first_time) {
2200
	global $pkg_interface;
2201
	if ($percent > 100) {
2202
		$percent = 1;
2203
	}
2204
	if ($pkg_interface <> "console") {
2205
		echo '<script type="text/javascript">';
2206
		echo "\n//<![CDATA[\n";
2207
		echo 'document.getElementById("progressbar").style.width="'. $percent.'%"';
2208
		echo "\n//]]>\n";
2209
		echo '</script>';
2210
	} else {
2211
		if (!($first_time)) {
2212
			echo "\x08\x08\x08\x08\x08";
2213
		}
2214
		echo sprintf("%4d%%", $percent);
2215
	}
2216
}
2217

    
2218
function update_alias_name($new_alias_name, $orig_alias_name) {
2219
	if (!$orig_alias_name) {
2220
		return;
2221
	}
2222

    
2223
	// Firewall rules
2224
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2225
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2226
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2227
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2228
	// NAT Rules
2229
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2230
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2231
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2232
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2233
	update_alias_names_upon_change(array('nat', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2234
	update_alias_names_upon_change(array('nat', 'rule'), array('local-port'), $new_alias_name, $orig_alias_name);
2235
	// NAT 1:1 Rules
2236
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('external'), $new_alias_name, $orig_alias_name);
2237
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2238
	update_alias_names_upon_change(array('nat', 'onetoone'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2239
	// NAT Outbound Rules
2240
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('source', 'network'), $new_alias_name, $orig_alias_name);
2241
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('sourceport'), $new_alias_name, $orig_alias_name);
2242
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2243
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('dstport'), $new_alias_name, $orig_alias_name);
2244
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2245
	// Alias in an alias
2246
	update_alias_names_upon_change(array('aliases', 'alias'), array('address'), $new_alias_name, $orig_alias_name);
2247
	// Static routes
2248
	update_alias_names_upon_change(array('staticroutes', 'route'), array('network'), $new_alias_name, $orig_alias_name);
2249
	// OpenVPN
2250
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2251
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2252
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('local_network'), $new_alias_name, $orig_alias_name);
2253
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('local_networkv6'), $new_alias_name, $orig_alias_name);
2254
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('remote_network'), $new_alias_name, $orig_alias_name);
2255
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2256
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2257
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2258
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('remote_network'), $new_alias_name, $orig_alias_name);
2259
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2260
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2261
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2262
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('local_network'), $new_alias_name, $orig_alias_name);
2263
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('local_networkv6'), $new_alias_name, $orig_alias_name);
2264
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('remote_network'), $new_alias_name, $orig_alias_name);
2265
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2266
}
2267

    
2268
function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) {
2269
	global $g, $config, $pconfig, $debug;
2270
	if (!$origname) {
2271
		return;
2272
	}
2273

    
2274
	$sectionref = &$config;
2275
	foreach ($section as $sectionname) {
2276
		if (is_array($sectionref) && isset($sectionref[$sectionname])) {
2277
			$sectionref = &$sectionref[$sectionname];
2278
		} else {
2279
			return;
2280
		}
2281
	}
2282

    
2283
	if ($debug) {
2284
		$fd = fopen("{$g['tmp_path']}/print_r", "a");
2285
		fwrite($fd, print_r($pconfig, true));
2286
	}
2287

    
2288
	if (is_array($sectionref)) {
2289
		foreach (array_keys($sectionref) as $itemkey) {
2290
			if ($debug) {
2291
				fwrite($fd, "$itemkey\n");
2292
			}
2293

    
2294
			$fieldfound = true;
2295
			$fieldref = &$sectionref[$itemkey];
2296
			foreach ($field as $fieldname) {
2297
				if (is_array($fieldref) && isset($fieldref[$fieldname])) {
2298
					$fieldref = &$fieldref[$fieldname];
2299
				} else {
2300
					$fieldfound = false;
2301
					break;
2302
				}
2303
			}
2304
			if ($fieldfound && $fieldref == $origname) {
2305
				if ($debug) {
2306
					fwrite($fd, "Setting old alias value $origname to $new_alias_name\n");
2307
				}
2308
				$fieldref = $new_alias_name;
2309
			}
2310
		}
2311
	}
2312

    
2313
	if ($debug) {
2314
		fclose($fd);
2315
	}
2316

    
2317
}
2318

    
2319
function parse_aliases_file($filename, $type = "url", $max_items = -1, $kflc = false) {
2320
	/*
2321
	 * $filename = file to process for example blocklist like DROP:  http://www.spamhaus.org/drop/drop.txt
2322
	 * $type = if set to 'url' then subnets and ips will be returned,
2323
	 *         if set to 'url_ports' port-ranges and ports will be returned
2324
	 * $max_items = sets the maximum amount of valid items to load, -1 the default defines there is no limit.
2325
	 *
2326
	 * RETURNS an array of ip subnets and ip's or ports and port-ranges, returns NULL upon a error conditions (file not found)
2327
	 */
2328

    
2329
	if (!file_exists($filename)) {
2330
		log_error(sprintf(gettext("Could not process non-existent file from alias: %s"), $filename));
2331
		return null;
2332
	}
2333

    
2334
	if (filesize($filename) == 0) {
2335
		log_error(sprintf(gettext("Could not process empty file from alias: %s"), $filename));
2336
		return null;
2337
	}
2338
	$fd = @fopen($filename, 'r');
2339
	if (!$fd) {
2340
		log_error(sprintf(gettext("Could not process aliases from alias: %s"), $filename));
2341
		return null;
2342
	}
2343
	$items = array();
2344
	$comments = array();
2345
	while (($fc = strip_tags(fgets($fd))) !== FALSE) {
2346
		$tmp = alias_idn_to_ascii(trim($fc, " \t\n\r"));
2347
		if (empty($tmp)) {
2348
			continue;
2349
		}
2350
		if (($kflc) && (strpos($tmp, '#') === 0)) {	// Keep Full Line Comments (lines beginning with #).
2351
			$comments[] = $tmp;
2352
		} else {
2353
			$tmp_str = strstr($tmp, '#', true);
2354
			if (!empty($tmp_str)) {
2355
				$tmp = $tmp_str;
2356
			}
2357
			$tmp_str = strstr($tmp, ' ', true);
2358
			if (!empty($tmp_str)) {
2359
				$tmp = $tmp_str;
2360
			}
2361
			switch ($type) {
2362
				case "url":
2363
				case "urltable":
2364
					if (is_ipaddr($tmp) || is_subnet($tmp)) {
2365
						$items[] = $tmp;
2366
						break;
2367
					}
2368
					if (is_fqdn($tmp)) {
2369
						$results = resolve_host_addresses($tmp, array(DNS_A, DNS_AAAA), false);
2370
						if (!empty($results)) {
2371
							foreach ($results as $ip) {
2372
								if (is_ipaddr($ip)) {
2373
									$items[] = $ip;
2374
								}
2375
							}
2376
						}
2377
					}
2378
					break;
2379
				case "url_ports":
2380
				case "urltable_ports":
2381
					if (is_port_or_range($tmp)) {
2382
						$items[] = $tmp;
2383
					}
2384
					break;
2385
				default:
2386
					/* unknown type */
2387
					break;
2388
				}
2389
			if (count($items) == $max_items) {
2390
				break;
2391
			}
2392
		}
2393
	}
2394
	fclose($fd);
2395
	return array_merge($comments, $items);
2396
}
2397

    
2398
function update_alias_url_data() {
2399
	global $g, $aliastable;
2400

    
2401
	$updated = false;
2402

    
2403
	/* item is a url type */
2404
	$lockkey = lock('aliasurl');
2405
	$aliases = array();
2406
	$aliases_nested = array();
2407

    
2408
	foreach (config_get_path('aliases/alias', []) as $x => $alias) {
2409
		if (empty($alias['aliasurl'])) {
2410
			continue;
2411
		}
2412
		foreach ($alias['aliasurl'] as $alias_url) {
2413
			if (is_alias($alias_url)) {
2414
				// process nested URL aliases after URL-only aliases
2415
				$aliases_nested[] = $x;
2416
				continue 2;
2417
			}
2418
		}
2419
		$aliases[] = $x;
2420
	}
2421

    
2422
	foreach (array_merge($aliases, $aliases_nested) as $x) {
2423

    
2424
		$address = array();
2425
		$type = config_get_path("aliases/alias/{$x}/type");
2426
		foreach (config_get_path("aliases/alias/{$x}/aliasurl") as $alias_url) {
2427
			/* fetch down and add in */
2428
			if (is_URL($alias_url)) {
2429
				$temp_filename = tempnam("{$g['tmp_path']}/", "alias_import");
2430
				rmdir_recursive($temp_filename);
2431
				$verify_ssl = config_path_enabled('system','checkaliasesurlcert');
2432
				mkdir($temp_filename);
2433
				if (!download_file($alias_url, $temp_filename . "/aliases", $verify_ssl)) {
2434
					log_error(sprintf(gettext("Failed to download alias %s"), $alias_url));
2435
					rmdir_recursive($temp_filename);
2436
					continue;
2437
				}
2438

    
2439
				/* if the item is tar gzipped then extract */
2440
				if (stripos($alias_url, '.tgz') && !process_alias_tgz($temp_filename)) {
2441
					log_error(sprintf(gettext("Could not unpack tgz from the URL '%s'."), $alias_url));
2442
					rmdir_recursive($temp_filename);
2443
					continue;
2444
				}
2445
				if (file_exists("{$temp_filename}/aliases")) {
2446
					$t_address = parse_aliases_file("{$temp_filename}/aliases", $type, 5000);
2447
					if ($t_address == null) {
2448
						/* nothing was found */
2449
						log_error(sprintf(gettext("Could not fetch usable data from '%s'."), $alias_url));
2450
						//rmdir_recursive($temp_filename);
2451
						continue;
2452
					} else {
2453
						array_push($address, ...$t_address);
2454
					}
2455
					unset($t_address);
2456
				}
2457
				rmdir_recursive($temp_filename);
2458
			} elseif (is_alias($alias_url)) {
2459
				/* nested URL alias, see https://redmine.pfsense.org/issues/11863 */
2460
				if (!$aliastable) {
2461
					alias_make_table();
2462
				}
2463
				$t_address = explode(" ", $aliastable[$alias_url]);
2464
				if ($t_address == null) {
2465
					log_error(sprintf(gettext("Could not get usable data from '%s' URL alias."), $alias_url));
2466
					continue;
2467
				}
2468
				array_push($address, ...$t_address);
2469
			}
2470
			if (!empty($address)) {
2471
				config_set_path("aliases/alias/{$x}/address", implode(" ", $address));
2472
				$updated = true;
2473
			}
2474
		}
2475
	}
2476

    
2477
	unlock($lockkey);
2478

    
2479
	/* Report status to callers as well */
2480
	return $updated;
2481
}
2482

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

    
2513
	return true;
2514
}
2515

    
2516
function version_compare_dates($a, $b) {
2517
	$a_time = strtotime($a);
2518
	$b_time = strtotime($b);
2519

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

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

    
2604
		// First try to compare the numeric parts of the version string.
2605
		$v = version_compare_numeric($cur_num, $rem_num);
2606

    
2607
		// If the numeric parts are the same, compare the string parts.
2608
		if ($v == 0) {
2609
			return version_compare_string($cur_str, $rem_str);
2610
		}
2611
	}
2612
	return $v;
2613
}
2614
function process_alias_urltable($name, $type, $url, $freq, $forceupdate=false, $validateonly=false) {
2615
	global $g;
2616

    
2617
	if (!is_validaliasname($name) || !filter_var($url, FILTER_VALIDATE_URL)) {
2618
		return false;
2619
	}
2620

    
2621
	$urltable_prefix = "/var/db/aliastables/";
2622
	$urltable_filename = $urltable_prefix . basename($name) . ".txt";
2623
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2624

    
2625
	// Make the aliases directory if it doesn't exist
2626
	if (!file_exists($urltable_prefix)) {
2627
		mkdir($urltable_prefix);
2628
	} elseif (!is_dir($urltable_prefix)) {
2629
		unlink($urltable_prefix);
2630
		mkdir($urltable_prefix);
2631
	}
2632

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

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

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

    
2647
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2648
			if ($type == "urltable_ports") {
2649
				$parsed_contents = group_ports($parsed_contents, true);
2650
			}
2651
			if (is_array($parsed_contents)) {
2652
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2653
			} else {
2654
				touch($urltable_filename);
2655
			}
2656

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

    
2663
			unlink_if_exists($tmp_urltable_filename);
2664
		} else {
2665
			if (!$validateonly) {
2666
				touch($urltable_filename);
2667
			}
2668
			return false;
2669
		}
2670
		return true;
2671
	} else {
2672
		// File exists, and it doesn't need to be updated.
2673
		return -1;
2674
	}
2675
}
2676

    
2677
function get_include_contents($filename) {
2678
	if (is_file($filename)) {
2679
		ob_start();
2680
		include $filename;
2681
		$contents = ob_get_contents();
2682
		ob_end_clean();
2683
		return $contents;
2684
	}
2685
	return false;
2686
}
2687

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

    
2800
function get_country_name($country_code = "ALL") {
2801
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2802
		return "";
2803
	}
2804

    
2805
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2806
	$country_names_contents = file_get_contents($country_names_xml);
2807
	$country_names = xml2array($country_names_contents);
2808

    
2809
	if ($country_code == "ALL") {
2810
		$country_list = array();
2811
		foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2812
			$country_list[] = array(
2813
				"code" => $country['ISO_3166-1_Alpha-2_Code_element'],
2814
				"name" => ucwords(strtolower($country['ISO_3166-1_Country_name'])));
2815
		}
2816
		return $country_list;
2817
	}
2818

    
2819
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2820
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2821
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2822
		}
2823
	}
2824
	return "";
2825
}
2826

    
2827
/* Return the list of country codes to be used on CAs and certs */
2828
function get_cert_country_codes() {
2829
	$countries = get_country_name();
2830

    
2831
	$country_codes = array();
2832
	foreach ($countries as $country) {
2833
		$country_codes[$country['code']] = $country['code'];
2834
	}
2835
	ksort($country_codes);
2836

    
2837
	/* Preserve historical order: None, US, CA, other countries */
2838
	$first_items[''] = gettext("None");
2839
	$first_items['US'] = $country_codes['US'];
2840
	$first_items['CA'] = $country_codes['CA'];
2841
	unset($country_codes['US']);
2842
	unset($country_codes['CA']);
2843

    
2844
	return array_merge($first_items, $country_codes);
2845
}
2846

    
2847
/* sort by interface only, retain the original order of rules that apply to
2848
   the same interface */
2849
function filter_rules_sort() {
2850
	init_config_arr(array('filter', 'rule'));
2851
	$rules = config_get_path('filter/rule', []);
2852

    
2853
	/* mark each rule with the sequence number (to retain the order while sorting) */
2854
	for ($i = 0; isset($rules[$i]); $i++) {
2855
		$rules[$i]['seq'] =$i;
2856
	}
2857
	usort($rules, "filter_rules_compare");
2858

    
2859
	/* strip the sequence numbers again */
2860
	for ($i = 0; isset($rules[$i]); $i++) {
2861
		unset($rules[$i]['seq']);
2862
	}
2863

    
2864
	/* commit changes */
2865
	config_set_path('filter/rule', $rules);
2866
}
2867
function filter_rules_compare($a, $b) {
2868
	if (isset($a['floating']) && isset($b['floating'])) {
2869
		return $a['seq'] - $b['seq'];
2870
	} else if (isset($a['floating'])) {
2871
		return -1;
2872
	} else if (isset($b['floating'])) {
2873
		return 1;
2874
	} else if ($a['interface'] == $b['interface']) {
2875
		return $a['seq'] - $b['seq'];
2876
	} else {
2877
		return compare_interface_friendly_names($a['interface'], $b['interface']);
2878
	}
2879
}
2880

    
2881
function generate_ipv6_from_mac($mac) {
2882
	$elements = explode(":", $mac);
2883
	if (count($elements) <> 6) {
2884
		return false;
2885
	}
2886

    
2887
	$i = 0;
2888
	$ipv6 = "fe80::";
2889
	foreach ($elements as $byte) {
2890
		if ($i == 0) {
2891
			$hexadecimal = substr($byte, 1, 2);
2892
			$bitmap = base_convert($hexadecimal, 16, 2);
2893
			$bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT);
2894
			$bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3, 4);
2895
			$byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16);
2896
		}
2897
		$ipv6 .= $byte;
2898
		if ($i == 1) {
2899
			$ipv6 .= ":";
2900
		}
2901
		if ($i == 3) {
2902
			$ipv6 .= ":";
2903
		}
2904
		if ($i == 2) {
2905
			$ipv6 .= "ff:fe";
2906
		}
2907

    
2908
		$i++;
2909
	}
2910
	return $ipv6;
2911
}
2912

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

    
2939
}
2940

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

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

    
2980
	$pos = strpos($ignore_if, '_virtualip');
2981
	if ($pos !== false) {
2982
		$ignore_vip_id = substr($ignore_if, $pos+10);
2983
		$ignore_vip_if = substr($ignore_if, 0, $pos);
2984
	} else {
2985
		$ignore_vip_id = -1;
2986
		$ignore_vip_if = $ignore_if;
2987
	}
2988

    
2989
	$isipv6 = is_ipaddrv6($ipaddr);
2990

    
2991
	if ($isipv6) {
2992
		$ipaddr = text_to_compressed_ip6($ipaddr);
2993
	}
2994

    
2995
	if ($check_subnets) {
2996
		$cidrprefix = intval($cidrprefix);
2997
		if ($isipv6) {
2998
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
2999
				$cidrprefix = 128;
3000
			}
3001
		} else {
3002
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
3003
				$cidrprefix = 32;
3004
			}
3005
		}
3006
		$iflist = get_configured_interface_list();
3007
		foreach ($iflist as $if => $ifname) {
3008
			if ($ignore_if == $if) {
3009
				continue;
3010
			}
3011

    
3012
			if ($isipv6) {
3013
				$if_ipv6 = get_interface_ipv6($if);
3014
				$if_snbitsv6 = get_interface_subnetv6($if);
3015
				/* do not check subnet overlapping on 6rd interfaces,
3016
				 * see https://redmine.pfsense.org/issues/12371 */ 
3017
				if ($if_ipv6 && $if_snbitsv6 &&
3018
				    ((config_get_path("interfaces/{$if}/ipaddrv6") != '6rd') || ($cidrprefix > $if_snbitsv6)) &&
3019
				    check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) {
3020
					$where_entry = array();
3021
					$where_entry['if'] = $if;
3022
					$where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if);
3023
					$where_configured[] = $where_entry;
3024
				}
3025
			} else {
3026
				$if_ipv4 = get_interface_ip($if);
3027
				$if_snbitsv4 = get_interface_subnet($if);
3028
				if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) {
3029
					$where_entry = array();
3030
					$where_entry['if'] = $if;
3031
					$where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if);
3032
					$where_configured[] = $where_entry;
3033
				}
3034
			}
3035
		}
3036
	} else {
3037
		if ($isipv6) {
3038
			$interface_list_ips = get_configured_ipv6_addresses();
3039
		} else {
3040
			$interface_list_ips = get_configured_ip_addresses();
3041
		}
3042

    
3043
		foreach ($interface_list_ips as $if => $ilips) {
3044
			if ($ignore_if == $if) {
3045
				continue;
3046
			}
3047
			if (strcasecmp($ipaddr, $ilips) == 0) {
3048
				$where_entry = array();
3049
				$where_entry['if'] = $if;
3050
				$where_entry['ip_or_subnet'] = $ilips;
3051
				$where_configured[] = $where_entry;
3052
			}
3053
		}
3054
	}
3055

    
3056
	if ($check_localip) {
3057
		if (strcasecmp($ipaddr, text_to_compressed_ip6(config_get_path('l2tp/localip', ""))) == 0) {
3058
			$where_entry = array();
3059
			$where_entry['if'] = 'l2tp';
3060
			$where_entry['ip_or_subnet'] = config_get_path('l2tp/localip');
3061
			$where_configured[] = $where_entry;
3062
		}
3063
	}
3064

    
3065
	return $where_configured;
3066
}
3067

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

    
3090
function set_language() {
3091
	global $g;
3092

    
3093
	$lang = "";
3094
	if (!empty(config_get_path('system/language'))) {
3095
		$lang = config_get_path('system/language');
3096
	} elseif (!empty($g['language'])) {
3097
		$lang = $g['language'];
3098
	}
3099
	$lang .= ".UTF-8";
3100

    
3101
	putenv("LANG={$lang}");
3102
	setlocale(LC_ALL, $lang);
3103
	textdomain("pfSense");
3104
	bindtextdomain("pfSense", "/usr/local/share/locale");
3105
	bind_textdomain_codeset("pfSense", $lang);
3106
}
3107

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

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

    
3134
	//asort($locales);
3135

    
3136
	return $locales;
3137
}
3138

    
3139
function return_hex_ipv4($ipv4) {
3140
	if (!is_ipaddrv4($ipv4)) {
3141
		return(false);
3142
	}
3143

    
3144
	/* we need the hex form of the interface IPv4 address */
3145
	$ip4arr = explode(".", $ipv4);
3146
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3147
}
3148

    
3149
function convert_ipv6_to_128bit($ipv6) {
3150
	if (!is_ipaddrv6($ipv6)) {
3151
		return(false);
3152
	}
3153

    
3154
	$ip6arr = array();
3155
	$ip6prefix = Net_IPv6::uncompress($ipv6);
3156
	$ip6arr = explode(":", $ip6prefix);
3157
	/* binary presentation of the prefix for all 128 bits. */
3158
	$ip6prefixbin = "";
3159
	foreach ($ip6arr as $element) {
3160
		$ip6prefixbin .= sprintf("%016b", hexdec($element));
3161
	}
3162
	return($ip6prefixbin);
3163
}
3164

    
3165
function convert_128bit_to_ipv6($ip6bin) {
3166
	if (strlen($ip6bin) <> 128) {
3167
		return(false);
3168
	}
3169

    
3170
	$ip6arr = array();
3171
	$ip6binarr = array();
3172
	$ip6binarr = str_split($ip6bin, 16);
3173
	foreach ($ip6binarr as $binpart) {
3174
		$ip6arr[] = dechex(bindec($binpart));
3175
	}
3176
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3177

    
3178
	return($ip6addr);
3179
}
3180

    
3181

    
3182
/* Returns the calculated bit length of the prefix delegation from the WAN interface */
3183
/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */
3184
/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */
3185
/* 6to4 is 16 bits, e.g. 65535 */
3186
function calculate_ipv6_delegation_length($if) {
3187
	$cfg = config_get_path("interfaces/{$if}");
3188
	if (!is_array($cfg)) {
3189
		return false;
3190
	}
3191

    
3192
	switch ($cfg['ipaddrv6']) {
3193
		case "6to4":
3194
			$pdlen = 16;
3195
			break;
3196
		case "6rd":
3197
			$rd6plen = explode("/", $cfg['prefix-6rd']);
3198
			$pdlen = (64 - ((int) $rd6plen[1] + (32 - (int) $cfg['prefix-6rd-v4plen'])));
3199
			break;
3200
		case "dhcp6":
3201
			$pdlen = $cfg['dhcp6-ia-pd-len'];
3202
			break;
3203
		default:
3204
			$pdlen = 0;
3205
			break;
3206
	}
3207
	return($pdlen);
3208
}
3209

    
3210
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3211
	/* convert zero-value prefix IPv6 addresses with IPv4 mapping to hex
3212
	 * see https://redmine.pfsense.org/issues/12440 */
3213
	$suffix_list = explode(':', $suffix);
3214
	if (is_ipaddrv4($suffix_list[count($suffix_list) - 1])) {
3215
		$hexsuffix = dechex(ip2long($suffix_list[count($suffix_list) - 1]));
3216
		$suffix_list[count($suffix_list) - 1] = substr($hexsuffix, 0, 4);
3217
		$suffix_list[] = substr($hexsuffix, 4, 8);
3218
		$suffix = implode(':', $suffix_list);
3219
	}	
3220
	$prefix = Net_IPv6::uncompress($prefix, true);
3221
	$suffix = Net_IPv6::uncompress($suffix, true);
3222

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

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

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

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

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

    
3303
	return $result;
3304
}
3305

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

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

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

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

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

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

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

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

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

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

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

    
3414
function get_pppoes_child_interfaces($ifpattern) {
3415
	$if_arr = array();
3416
	if ($ifpattern == "") {
3417
		return;
3418
	}
3419

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

    
3428
}
3429

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

    
3463
// Convert IPv6 addresses to lower case
3464
function addrtolower($ip) {
3465
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3466
		return(strtolower($ip));
3467
	} else {
3468
		return($ip);
3469
	}
3470
}
3471

    
3472
function compare_by_name($a, $b) {
3473
	return strcasecmp($a['name'], $b['name']);
3474
}
3475

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

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

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

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

    
3531
	/* Cannot determine the filename, so bail. */
3532
	if (empty($name)) {
3533
		exit;
3534
	}
3535

    
3536
	/* Send basic download headers */
3537
	header("Content-Type: {$contenttype}");
3538
	header("Content-Length: {$size}");
3539
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3540

    
3541
	/* Send cache headers */
3542
	if (isset($_SERVER['HTTPS'])) {
3543
		header('Pragma: ');
3544
		header('Cache-Control: ');
3545
	} else {
3546
		header("Pragma: private");
3547
		header("Cache-Control: private, must-revalidate");
3548
	}
3549

    
3550
	/* Ensure output buffering is off so PHP does not consume
3551
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3552
	while (ob_get_level()) {
3553
		@ob_end_clean();
3554
	}
3555

    
3556
	/* Send the data to the user */
3557
	if ($type == 'file') {
3558
		readfile($content);
3559
	} else {
3560
		echo $content;
3561
	}
3562

    
3563
	/* Flush any remaining output buffer */
3564
	@ob_end_flush();
3565
	exit;
3566
}
3567

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

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

    
3604
function set_curlproxy(&$ch) {
3605
	if (!empty(config_get_path('system/proxyurl'))) {
3606
		curl_setopt($ch, CURLOPT_PROXY, config_get_path('system/proxyurl'));
3607
		if (!empty(config_get_path('system/proxyport'))) {
3608
			curl_setopt($ch, CURLOPT_PROXYPORT, config_get_path('system/proxyport'));
3609
		}
3610
		$proxyuser = config_get_path('system/proxyuser');
3611
		$proxypass = config_get_path('system/proxypass');
3612
		if (!empty($proxyuser) && !empty($proxypass)) {
3613
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
3614
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$proxyuser}:{$proxypass}");
3615
		}
3616
	}
3617
}
3618
?>
(39-39/62)