Project

General

Profile

Download (102 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-2021 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
/****f* pfsense-utils/have_natpfruleint_access
25
 * NAME
26
 *   have_natpfruleint_access
27
 * INPUTS
28
 *	none
29
 * RESULT
30
 *   returns true if user has access to edit a specific firewall nat port forward interface
31
 ******/
32
function have_natpfruleint_access($if) {
33
	$security_url = "firewall_nat_edit.php?if=". strtolower($if);
34
	if (isAllowedPage($security_url, $allowed)) {
35
		return true;
36
	}
37
	return false;
38
}
39

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

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

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

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

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

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

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

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

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

    
162
	if (is_array($cssfiles)) {
163
		arsort($cssfiles);
164
		$usrcss = $pfscss = $betacss = array();
165

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

    
180
		$css = array_merge($pfscss, $betacss, $usrcss);
181

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

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

    
201
	$csslist = get_css_files();
202

    
203
	if (!isset($csslist[$value])) {
204
		$value = "pfSense.css";
205
	}
206

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

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

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

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

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

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

    
282
	if (((int) $value < 1) || ((int) $value > 6)) {
283
		$value = 2;
284
	}
285

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

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

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

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

    
330
	$group = new Form_Group('Associated Panels Show/Hide');
331

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

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

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

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

    
360
	$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.');
361

    
362
	$section->add($group);
363
}
364

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

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

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

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

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

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

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

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

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

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

    
485
	if ($has_created_time || $has_updated_time) {
486
		$section = new Form_Section('Rule Information');
487

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

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

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

    
515
		$form->add($section);
516
	}
517
}
518

    
519
function hardware_offloading_applyflags($iface) {
520
	global $config;
521

    
522
	$flags_on = 0;
523
	$flags_off = 0;
524
	$options = pfSense_get_interface_addresses($iface);
525

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

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

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

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

    
575
/****f* pfsense-utils/enable_hardware_offloading
576
 * NAME
577
 *   enable_hardware_offloading - Enable a NIC's supported hardware features.
578
 * INPUTS
579
 *   $interface	- string containing the physical interface to work on.
580
 * RESULT
581
 *   null
582
 * NOTES
583
 *   This function only supports the fxp driver's loadable microcode.
584
 ******/
585
function enable_hardware_offloading($interface) {
586
	global $g, $config;
587

    
588
	$int = get_real_interface($interface);
589
	if (empty($int)) {
590
		return;
591
	}
592

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

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

    
614
/****f* pfsense-utils/is_alias_inuse
615
 * NAME
616
 *   checks to see if an alias is currently in use by a rule
617
 * INPUTS
618
 *
619
 * RESULT
620
 *   true or false
621
 * NOTES
622
 *
623
 ******/
624
function is_alias_inuse($alias) {
625
	global $g, $config;
626

    
627
	if ($alias == "") {
628
		return false;
629
	}
630
	/* loop through firewall rules looking for alias in use */
631
	if (is_array($config['filter']['rule'])) {
632
		foreach ($config['filter']['rule'] as $rule) {
633
			if ($rule['source']['address']) {
634
				if ($rule['source']['address'] == $alias) {
635
					return true;
636
				}
637
			}
638
			if ($rule['destination']['address']) {
639
				if ($rule['destination']['address'] == $alias) {
640
					return true;
641
				}
642
			}
643
		}
644
	}
645
	/* loop through nat rules looking for alias in use */
646
	if (is_array($config['nat']['rule'])) {
647
		foreach ($config['nat']['rule'] as $rule) {
648
			if ($rule['target'] && $rule['target'] == $alias) {
649
				return true;
650
			}
651
			if ($rule['source']['address'] && $rule['source']['address'] == $alias) {
652
				return true;
653
			}
654
			if ($rule['destination']['address'] && $rule['destination']['address'] == $alias) {
655
				return true;
656
			}
657
		}
658
	}
659
	return false;
660
}
661

    
662
/****f* pfsense-utils/is_schedule_inuse
663
 * NAME
664
 *   checks to see if a schedule is currently in use by a rule
665
 * INPUTS
666
 *
667
 * RESULT
668
 *   true or false
669
 * NOTES
670
 *
671
 ******/
672
function is_schedule_inuse($schedule) {
673
	global $g, $config;
674

    
675
	if ($schedule == "") {
676
		return false;
677
	}
678
	/* loop through firewall rules looking for schedule in use */
679
	if (is_array($config['filter']['rule'])) {
680
		foreach ($config['filter']['rule'] as $rule) {
681
			if ($rule['sched'] == $schedule) {
682
				return true;
683
			}
684
		}
685
	}
686
	return false;
687
}
688

    
689
/****f* pfsense-utils/setup_microcode
690
 * NAME
691
 *   enumerates all interfaces and calls enable_hardware_offloading which
692
 *   enables a NIC's supported hardware features.
693
 * INPUTS
694
 *
695
 * RESULT
696
 *   null
697
 * NOTES
698
 *   This function only supports the fxp driver's loadable microcode.
699
 ******/
700
function setup_microcode() {
701

    
702
	/* if list */
703
	$iflist = get_configured_interface_list(true);
704
	foreach ($iflist as $if => $ifdescr) {
705
		enable_hardware_offloading($if);
706
	}
707
	unset($iflist);
708
}
709

    
710
/****f* pfsense-utils/get_carp_status
711
 * NAME
712
 *   get_carp_status - Return whether CARP is enabled or disabled.
713
 * RESULT
714
 *   boolean	- true if CARP is enabled, false if otherwise.
715
 ******/
716
function get_carp_status() {
717
	/* grab the current status of carp */
718
	$status = get_single_sysctl('net.inet.carp.allow');
719
	return (intval($status) > 0);
720
}
721

    
722
/*
723
 * convert_ip_to_network_format($ip, $subnet): converts an ip address to network form
724

    
725
 */
726
function convert_ip_to_network_format($ip, $subnet) {
727
	$ipsplit = explode('.', $ip);
728
	$string = $ipsplit[0] . "." . $ipsplit[1] . "." . $ipsplit[2] . ".0/" . $subnet;
729
	return $string;
730
}
731

    
732
/*
733
 * get_carp_interface_status($carpid): returns the status of a carp uniqid
734
 */
735
function get_carp_interface_status($carpid) {
736

    
737
	$carpiface = get_configured_vip_interface($carpid);
738
	if ($carpiface == NULL)
739
		return "";
740
	$interface = get_real_interface($carpiface);
741
	if ($interface == NULL)
742
		return "";
743
	$vip = get_configured_vip($carpid);
744
	if ($vip == NULL || !isset($vip['vhid']))
745
		return "";
746

    
747
	$vhid = $vip['vhid'];
748
	$carp_query = '';
749
	$_gb = exec("/sbin/ifconfig {$interface} | /usr/bin/grep \"carp:.* vhid {$vhid} \"", $carp_query);
750
	foreach ($carp_query as $int) {
751
		if (stripos($int, "MASTER"))
752
			return "MASTER";
753
		elseif (stripos($int, "BACKUP"))
754
			return "BACKUP";
755
		elseif (stripos($int, "INIT"))
756
			return "INIT";
757
	}
758

    
759
	return "";
760
}
761

    
762
function get_carp_bind_status($interface) {
763
	global $config;
764

    
765
	$carpstatus = get_carp_interface_status($interface);
766
	if (!empty($carpstatus)) {
767
		return $carpstatus;
768
	} else {
769
		foreach ($config['virtualip']['vip'] as $vip) {
770
			if ($interface == "_vip{$vip['uniqid']}") { 
771
				return get_carp_interface_status($vip['interface']);
772
			}
773
		}
774
	}
775
}
776

    
777
/*
778
 * Return true if the CARP status of at least one interface of a captive portal zone is in backup mode
779
 * This function return false if CARP is not enabled on any interface of the captive portal zone
780
 */
781
function captiveportal_ha_is_node_in_backup_mode($cpzone) {
782
	global $config;
783

    
784
	$cpinterfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
785

    
786
	if (is_array($config['virtualip']['vip'])) {
787
		foreach ($cpinterfaces as $interface) {
788
			foreach ($config['virtualip']['vip'] as $vip) {
789
				if (($vip['interface'] == $interface) && ($vip['mode'] == "carp")) {
790
					if (get_carp_interface_status("_vip{$vip['uniqid']}") != "MASTER") {
791
						return true;
792
					}
793
				}
794
			}
795
		}
796
	}
797
	return false;
798
}
799

    
800
/*
801
 * get_pfsync_interface_status($pfsyncinterface): returns the status of a pfsync
802
 */
803
function get_pfsync_interface_status($pfsyncinterface) {
804
	if (!does_interface_exist($pfsyncinterface)) {
805
		return;
806
	}
807

    
808
	return exec_command("/sbin/ifconfig {$pfsyncinterface} | /usr/bin/awk '/pfsync:/ {print \$5}'");
809
}
810

    
811
/*
812
 * add_rule_to_anchor($anchor, $rule): adds the specified rule to an anchor
813
 */
814
function add_rule_to_anchor($anchor, $rule, $label) {
815
	mwexec("echo " . escapeshellarg($rule) . " | /sbin/pfctl -a " . escapeshellarg($anchor) . ":" . escapeshellarg($label) . " -f -");
816
}
817

    
818
/*
819
 * remove_text_from_file
820
 * remove $text from file $file
821
 */
822
function remove_text_from_file($file, $text) {
823
	if (!file_exists($file) && !is_writable($file)) {
824
		return;
825
	}
826
	$filecontents = file_get_contents($file);
827
	$text = str_replace($text, "", $filecontents);
828
	@file_put_contents($file, $text);
829
}
830

    
831
/*
832
 *   after_sync_bump_adv_skew(): create skew values by 1S
833
 */
834
function after_sync_bump_adv_skew() {
835
	global $config, $g;
836
	$processed_skew = 1;
837
	init_config_arr(array('virtualip', 'vip'));
838
	$a_vip = &$config['virtualip']['vip'];
839
	foreach ($a_vip as $vipent) {
840
		if ($vipent['advskew'] <> "") {
841
			$processed_skew = 1;
842
			$vipent['advskew'] = $vipent['advskew']+1;
843
		}
844
	}
845
	if ($processed_skew == 1) {
846
		write_config(gettext("After synch increase advertising skew"));
847
	}
848
}
849

    
850
/*
851
 * get_filename_from_url($url): converts a url to its filename.
852
 */
853
function get_filename_from_url($url) {
854
	return basename($url);
855
}
856

    
857
/*
858
 *   get_dir: return an array of $dir
859
 */
860
function get_dir($dir) {
861
	$dir_array = array();
862
	$d = dir($dir);
863
	if (!is_object($d)) {
864
		return array();
865
	}
866
	while (false !== ($entry = $d->read())) {
867
		array_push($dir_array, $entry);
868
	}
869
	$d->close();
870
	return $dir_array;
871
}
872

    
873
/****f* pfsense-utils/WakeOnLan
874
 * NAME
875
 *   WakeOnLan - Wake a machine up using the wake on lan format/protocol
876
 * RESULT
877
 *   true/false - true if the operation was successful
878
 ******/
879
function WakeOnLan($addr, $mac) {
880
	$addr_byte = explode(':', $mac);
881
	$hw_addr = '';
882

    
883
	for ($a = 0; $a < 6; $a++) {
884
		$hw_addr .= chr(hexdec($addr_byte[$a]));
885
	}
886

    
887
	$msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255);
888

    
889
	for ($a = 1; $a <= 16; $a++) {
890
		$msg .= $hw_addr;
891
	}
892

    
893
	// send it to the broadcast address using UDP
894
	$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
895
	if ($s == false) {
896
		log_error(gettext("Error creating socket!"));
897
		log_error(sprintf(gettext("Error code is '%1\$s' - %2\$s"), socket_last_error($s), socket_strerror(socket_last_error($s))));
898
	} else {
899
		// setting a broadcast option to socket:
900
		$opt_ret = socket_set_option($s, 1, 6, TRUE);
901
		if ($opt_ret < 0) {
902
			log_error(sprintf(gettext("setsockopt() failed, error: %s"), strerror($opt_ret)));
903
		}
904
		$e = socket_sendto($s, $msg, strlen($msg), 0, $addr, 2050);
905
		socket_close($s);
906
		log_error(sprintf(gettext('Magic Packet sent (%1$s) to (%2$s) MAC=%3$s'), $e, $addr, $mac));
907
		return true;
908
	}
909

    
910
	return false;
911
}
912

    
913
/*
914
 * reverse_strrchr($haystack, $needle):  Return everything in $haystack up to the *last* instance of $needle.
915
 *					 Useful for finding paths and stripping file extensions.
916
 */
917
function reverse_strrchr($haystack, $needle) {
918
	if (!is_string($haystack)) {
919
		return;
920
	}
921
	return strrpos($haystack, $needle) ? substr($haystack, 0, strrpos($haystack, $needle) +1) : false;
922
}
923

    
924
/*
925
 *  backup_config_section($section): returns as an xml file string of
926
 *                                   the configuration section
927
 */
928
function backup_config_section($section_name) {
929
	global $config;
930
	$new_section = &$config[$section_name];
931
	/* generate configuration XML */
932
	$xmlconfig = dump_xml_config($new_section, $section_name);
933
	$xmlconfig = str_replace("<?xml version=\"1.0\"?>", "", $xmlconfig);
934
	return $xmlconfig;
935
}
936

    
937
/*
938
 *  restore_config_section($section_name, new_contents): restore a configuration section,
939
 *                                                  and write the configuration out
940
 *                                                  to disk/cf.
941
 */
942
function restore_config_section($section_name, $new_contents) {
943
	global $config, $g;
944
	$fout = fopen("{$g['tmp_path']}/tmpxml", "w");
945
	fwrite($fout, $new_contents);
946
	fclose($fout);
947

    
948
	$xml = parse_xml_config($g['tmp_path'] . "/tmpxml", null);
949
	if ($xml['pfsense']) {
950
		$xml = $xml['pfsense'];
951
	}
952
	if ($xml[$section_name]) {
953
		$section_xml = $xml[$section_name];
954
	} else {
955
		$section_xml = -1;
956
	}
957

    
958
	@unlink($g['tmp_path'] . "/tmpxml");
959
	if ($section_xml === -1) {
960
		return false;
961
	}
962

    
963
	/* Save current pkg repo to re-add on new config */
964
	unset($pkg_repo_conf_path);
965
	if ($section_name == "system" &&
966
	    isset($config['system']['pkg_repo_conf_path'])) {
967
		$pkg_repo_conf_path = $config['system']['pkg_repo_conf_path'];
968
	}
969

    
970
	$config[$section_name] = &$section_xml;
971
	if (file_exists("{$g['tmp_path']}/config.cache")) {
972
		unlink("{$g['tmp_path']}/config.cache");
973
	}
974

    
975
	/* Restore previously pkg repo configured */
976
	if ($section_name == "system") {
977
		if (isset($pkg_repo_conf_path)) {
978
			$config['system']['pkg_repo_conf_path'] =
979
			    $pkg_repo_conf_path;
980
		} elseif (isset($config['system']['pkg_repo_conf_path'])) {
981
			unset($config['system']['pkg_repo_conf_path']);
982
		}
983
	}
984

    
985
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
986
	disable_security_checks();
987
	return true;
988
}
989

    
990
/*
991
 *  merge_config_section($section_name, new_contents):   restore a configuration section,
992
 *                                                  and write the configuration out
993
 *                                                  to disk/cf.  But preserve the prior
994
 * 													structure if needed
995
 */
996
function merge_config_section($section_name, $new_contents) {
997
	global $config;
998
	$fname = get_tmp_filename();
999
	$fout = fopen($fname, "w");
1000
	fwrite($fout, $new_contents);
1001
	fclose($fout);
1002
	$section_xml = parse_xml_config($fname, $section_name);
1003
	$config[$section_name] = $section_xml;
1004
	unlink($fname);
1005
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
1006
	disable_security_checks();
1007
	return;
1008
}
1009

    
1010
/*
1011
 * rmdir_recursive($path, $follow_links=false)
1012
 * Recursively remove a directory tree (rm -rf path)
1013
 * This is for directories _only_
1014
 */
1015
function rmdir_recursive($path, $follow_links=false) {
1016
	$to_do = glob($path);
1017
	if (!is_array($to_do)) {
1018
		$to_do = array($to_do);
1019
	}
1020
	foreach ($to_do as $workingdir) { // Handle wildcards by foreaching.
1021
		if (file_exists($workingdir)) {
1022
			if (is_dir($workingdir)) {
1023
				$dir = opendir($workingdir);
1024
				while ($entry = readdir($dir)) {
1025
					if (is_file("$workingdir/$entry") || ((!$follow_links) && is_link("$workingdir/$entry"))) {
1026
						unlink("$workingdir/$entry");
1027
					} elseif (is_dir("$workingdir/$entry") && $entry != '.' && $entry != '..') {
1028
						rmdir_recursive("$workingdir/$entry");
1029
					}
1030
				}
1031
				closedir($dir);
1032
				rmdir($workingdir);
1033
			} elseif (is_file($workingdir)) {
1034
				unlink($workingdir);
1035
			}
1036
		}
1037
	}
1038
	return;
1039
}
1040

    
1041
/*
1042
 * host_firmware_version(): Return the versions used in this install
1043
 */
1044
function host_firmware_version($tocheck = "") {
1045
	global $g, $config;
1046

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

    
1049
	return array(
1050
		"firmware" => array("version" => $g['product_version']),
1051
		"kernel"   => array("version" => $os_version),
1052
		"base"     => array("version" => $os_version),
1053
		"platform" => $g['product_label'],
1054
		"config_version" => $config['version']
1055
	);
1056
}
1057

    
1058
function get_disk_info() {
1059
	$diskout = "";
1060
	exec("/bin/df -h | /usr/bin/grep -w '/' | /usr/bin/awk '{ print $2, $3, $4, $5 }'", $diskout);
1061
	return explode(' ', $diskout[0]);
1062
}
1063

    
1064
/****f* pfsense-utils/strncpy
1065
 * NAME
1066
 *   strncpy - copy strings
1067
 * INPUTS
1068
 *   &$dst, $src, $length
1069
 * RESULT
1070
 *   none
1071
 ******/
1072
function strncpy(&$dst, $src, $length) {
1073
	if (strlen($src) > $length) {
1074
		$dst = substr($src, 0, $length);
1075
	} else {
1076
		$dst = $src;
1077
	}
1078
}
1079

    
1080
/****f* pfsense-utils/reload_interfaces_sync
1081
 * NAME
1082
 *   reload_interfaces - reload all interfaces
1083
 * INPUTS
1084
 *   none
1085
 * RESULT
1086
 *   none
1087
 ******/
1088
function reload_interfaces_sync() {
1089
	global $config, $g;
1090

    
1091
	if ($g['debug']) {
1092
		log_error(gettext("reload_interfaces_sync() is starting."));
1093
	}
1094

    
1095
	/* parse config.xml again */
1096
	$config = parse_config(true);
1097

    
1098
	/* enable routing */
1099
	system_routing_enable();
1100
	if ($g['debug']) {
1101
		log_error(gettext("Enabling system routing"));
1102
	}
1103

    
1104
	if ($g['debug']) {
1105
		log_error(gettext("Cleaning up Interfaces"));
1106
	}
1107

    
1108
	/* set up interfaces */
1109
	interfaces_configure();
1110
}
1111

    
1112
/****f* pfsense-utils/reload_all
1113
 * NAME
1114
 *   reload_all - triggers a reload of all settings
1115
 *   * INPUTS
1116
 *   none
1117
 * RESULT
1118
 *   none
1119
 ******/
1120
function reload_all() {
1121
	send_event("service reload all");
1122
}
1123

    
1124
/****f* pfsense-utils/reload_interfaces
1125
 * NAME
1126
 *   reload_interfaces - triggers a reload of all interfaces
1127
 * INPUTS
1128
 *   none
1129
 * RESULT
1130
 *   none
1131
 ******/
1132
function reload_interfaces() {
1133
	send_event("interface all reload");
1134
}
1135

    
1136
/****f* pfsense-utils/reload_all_sync
1137
 * NAME
1138
 *   reload_all - reload all settings
1139
 *   * INPUTS
1140
 *   none
1141
 * RESULT
1142
 *   none
1143
 ******/
1144
function reload_all_sync() {
1145
	global $config, $g;
1146

    
1147
	/* parse config.xml again */
1148
	$config = parse_config(true);
1149

    
1150
	/* set up our timezone */
1151
	system_timezone_configure();
1152

    
1153
	/* set up our hostname */
1154
	system_hostname_configure();
1155

    
1156
	/* make hosts file */
1157
	system_hosts_generate();
1158

    
1159
	/* generate resolv.conf */
1160
	system_resolvconf_generate();
1161

    
1162
	/* enable routing */
1163
	system_routing_enable();
1164

    
1165
	/* set up interfaces */
1166
	interfaces_configure();
1167

    
1168
	/* start dyndns service */
1169
	services_dyndns_configure();
1170

    
1171
	/* configure cron service */
1172
	configure_cron();
1173

    
1174
	/* start the NTP client */
1175
	system_ntp_configure();
1176

    
1177
	/* sync pw database */
1178
	unlink_if_exists("/etc/spwd.db.tmp");
1179
	mwexec("/usr/sbin/pwd_mkdb -d /etc/ /etc/master.passwd");
1180

    
1181
	/* restart sshd */
1182
	send_event("service restart sshd");
1183

    
1184
	/* restart webConfigurator if needed */
1185
	send_event("service restart webgui");
1186
}
1187

    
1188
function load_loader_conf($loader_conf = NULL, $local = false) {
1189

    
1190
	if ($loader_conf == NULL) {
1191
		return (NULL);
1192
	}
1193
	if (file_exists($loader_conf)) {
1194
		$input = file_get_contents($loader_conf);
1195
	} else {
1196
		$input = "";
1197
	}
1198

    
1199
	$input_split = explode("\n", $input);
1200

    
1201
	/*
1202
	 * Loop through and only add lines that are not empty and not
1203
	 * managed by us.
1204
	 */
1205
	$data = array();
1206
	/* These values should be removed from loader.conf and loader.conf.local
1207
	 * As they will be replaced when necessary. */
1208
	$remove = array("hw.usb.no_pf", "hint.mdio.0.at", "hint.e6000sw.0",
1209
	    "hw.e6000sw.default_disabled", "vm.pmap.pti",
1210
	    "net.pf.request_maxcount", "hw.hn.vf_transparent",
1211
	    "hw.hn.use_if_start", "net.link.ifqmaxlen");
1212
	if (!$local) {
1213
		/* These values should only be filtered in loader.conf, not .local */
1214
		$remove = array_merge($remove,
1215
		    array("autoboot_delay", "console", "comconsole_speed", "comconsole_port",
1216
		    "boot_multicons", "boot_serial", "hint.uart.0.flags",
1217
		    "hint.uart.1.flags"));
1218
	}
1219
	foreach ($input_split as $line) {
1220
		if (empty($line)) {
1221
			continue;
1222
		}
1223
		$skip = false;
1224
		list($name, $value) = explode('=', $line, 2);
1225
		foreach($remove as $rid => $rkey) {
1226
			if (strncasecmp(trim($name), $rkey, strlen($rkey)) == 0) {
1227
				$skip = true;
1228
				break;
1229
			}
1230
		}
1231
		if (!$skip) {
1232
			$data[] = $line;
1233
		}
1234
	}
1235

    
1236
	return ($data);
1237
}
1238

    
1239
function setup_loader_settings($path = "", $upgrade = false) {
1240
	global $g, $config;
1241

    
1242
	$boot_config_file = "{$path}/boot.config";
1243
	$loader_conf_file = "{$path}/boot/loader.conf";
1244

    
1245
	$serialspeed = (is_numeric($config['system']['serialspeed'])) ? $config['system']['serialspeed'] : "115200";
1246

    
1247
	$vga_only = false;
1248
	$serial_only = false;
1249
	$specific_platform = system_identify_specific_platform();
1250
	$video_console_type = (get_single_sysctl("machdep.bootmethod") == "UEFI") ? "efi" : "vidconsole";
1251
	if ($specific_platform['name'] == '1540') {
1252
		$vga_only = true;
1253
	} elseif ($specific_platform['name'] == 'Turbot Dual-E') {
1254
		$g['primaryconsole_force'] = "video";
1255
	} elseif ($specific_platform['name'] == 'RCC-VE' ||
1256
	    $specific_platform['name'] == 'RCC' ||
1257
	    $specific_platform['name'] == 'SG-2220' ||
1258
	    $specific_platform['name'] == 'apu2') {
1259
		$serial_only = true;
1260
	}
1261

    
1262
	/* Serial console - write out /boot.config */
1263
	if (file_exists($boot_config_file)) {
1264
		$boot_config = file_get_contents($boot_config_file);
1265
	} else {
1266
		$boot_config = "";
1267
	}
1268
	$boot_config_split = explode("\n", $boot_config);
1269
	$data = array();
1270
	foreach ($boot_config_split as $bcs) {
1271
		/* Ignore -D and -h lines now */
1272
		if (!empty($bcs) && !stristr($bcs, "-D") &&
1273
		    !stristr($bcs, "-h")) {
1274
			$data[] = $bcs;
1275
		}
1276
	}
1277
	if ($serial_only === true) {
1278
		$data[] = "-S{$serialspeed} -h";
1279
	} elseif (is_serial_enabled()) {
1280
		$data[] = "-S{$serialspeed} -D";
1281
	}
1282

    
1283
	if (empty($data)) {
1284
		@unlink($boot_conf_file);
1285
	} else {
1286
		safe_write_file($boot_config_file, $data);
1287
	}
1288
	unset($data, $boot_config, $boot_config_file, $boot_config_split);
1289

    
1290
	/* Serial console - write out /boot/loader.conf */
1291
	if ($upgrade) {
1292
		system("echo \"Reading {$loader_conf_file}...\" >> /conf/upgrade_log.txt");
1293
	}
1294

    
1295
	$data = load_loader_conf($loader_conf_file, false);
1296
	if ($serial_only === true) {
1297
		$data[] = 'boot_serial="YES"';
1298
		$data[] = 'console="comconsole"';
1299
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1300
	} elseif ($vga_only === true) {
1301
		$data[] = "console=\"{$video_console_type}\"";
1302
	} elseif (is_serial_enabled()) {
1303
		$data[] = 'boot_multicons="YES"';
1304
		$data[] = 'boot_serial="YES"';
1305
		$primaryconsole = isset($g['primaryconsole_force']) ?
1306
		    $g['primaryconsole_force'] :
1307
		    $config['system']['primaryconsole'];
1308
		switch ($primaryconsole) {
1309
			case "video":
1310
				$data[] = "console=\"{$video_console_type},comconsole\"";
1311
				break;
1312
			case "serial":
1313
			default:
1314
				$data[] = "console=\"comconsole,{$video_console_type}\"";
1315
		}
1316
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1317
	}
1318

    
1319
	if ($specific_platform['name'] == 'RCC-VE' ||
1320
	    $specific_platform['name'] == 'RCC' ||
1321
	    $specific_platform['name'] == 'SG-2220') {
1322
		$data[] = 'comconsole_port="0x2F8"';
1323
		$data[] = 'hint.uart.0.flags="0x00"';
1324
		$data[] = 'hint.uart.1.flags="0x10"';
1325
	}
1326
	$data[] = 'autoboot_delay="3"';
1327
	if (isset($config['system']['pti_disabled'])) {
1328
		$data[] = 'vm.pmap.pti="0"';
1329
	}
1330

    
1331
	/* Enable ALTQ support for hnX NICs. */
1332
	if (isset($config['system']['hn_altq_enable'])) {
1333
		$data[] = 'hw.hn.vf_transparent="0"';
1334
		$data[] = 'hw.hn.use_if_start="1"';
1335
	}
1336

    
1337
	/* Set maximum send queue length. */
1338
	$data[] = 'net.link.ifqmaxlen="128"';
1339

    
1340
	safe_write_file($loader_conf_file, $data);
1341

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

    
1351
}
1352

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

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

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

    
1363
	$data = array();
1364

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

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

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

    
1381
	$found = array();
1382

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

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

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

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

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

    
1434
	safe_write_file($ttys_file, $data);
1435

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

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

    
1442
	return;
1443
}
1444

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

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

    
1453
	return true;
1454
}
1455

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

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

    
1471
/* DHCP enabled on any interfaces? */
1472
function is_dhcp_server_enabled() {
1473
	global $config;
1474

    
1475
	if (!is_array($config['dhcpd'])) {
1476
		return false;
1477
	}
1478

    
1479
	foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) {
1480
		if (isset($dhcpifconf['enable']) && !empty($config['interfaces'][$dhcpif])) {
1481
			return true;
1482
		}
1483
	}
1484

    
1485
	return false;
1486
}
1487

    
1488
/* DHCP enabled on any interfaces? */
1489
function is_dhcpv6_server_enabled() {
1490
	global $config;
1491

    
1492
	if (is_array($config['interfaces'])) {
1493
		foreach ($config['interfaces'] as $ifcfg) {
1494
			if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface'])) {
1495
				return true;
1496
			}
1497
		}
1498
	}
1499

    
1500
	if (!is_array($config['dhcpdv6'])) {
1501
		return false;
1502
	}
1503

    
1504
	foreach ($config['dhcpdv6'] as $dhcpv6if => $dhcpv6ifconf) {
1505
		if (isset($dhcpv6ifconf['enable']) && !empty($config['interfaces'][$dhcpv6if])) {
1506
			return true;
1507
		}
1508
	}
1509

    
1510
	return false;
1511
}
1512

    
1513
/* radvd enabled on any interfaces? */
1514
function is_radvd_enabled() {
1515
	global $config;
1516

    
1517
	if (!is_array($config['dhcpdv6'])) {
1518
		$config['dhcpdv6'] = array();
1519
	}
1520

    
1521
	$dhcpdv6cfg = $config['dhcpdv6'];
1522
	$Iflist = get_configured_interface_list();
1523

    
1524
	/* handle manually configured DHCP6 server settings first */
1525
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1526
		if (!isset($config['interfaces'][$dhcpv6if]['enable'])) {
1527
			continue;
1528
		}
1529

    
1530
		if (!isset($dhcpv6ifconf['ramode'])) {
1531
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
1532
		}
1533

    
1534
		if ($dhcpv6ifconf['ramode'] == "disabled") {
1535
			continue;
1536
		}
1537

    
1538
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1539
		if (!is_ipaddrv6($ifcfgipv6)) {
1540
			continue;
1541
		}
1542

    
1543
		return true;
1544
	}
1545

    
1546
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
1547
	foreach ($Iflist as $if => $ifdescr) {
1548
		if (!isset($config['interfaces'][$if]['track6-interface'])) {
1549
			continue;
1550
		}
1551
		if (!isset($config['interfaces'][$if]['enable'])) {
1552
			continue;
1553
		}
1554

    
1555
		$ifcfgipv6 = get_interface_ipv6($if);
1556
		if (!is_ipaddrv6($ifcfgipv6)) {
1557
			continue;
1558
		}
1559

    
1560
		$ifcfgsnv6 = get_interface_subnetv6($if);
1561
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1562

    
1563
		if (!is_ipaddrv6($subnetv6)) {
1564
			continue;
1565
		}
1566

    
1567
		return true;
1568
	}
1569

    
1570
	return false;
1571
}
1572

    
1573
/* Any PPPoE servers enabled? */
1574
function is_pppoe_server_enabled() {
1575
	global $config;
1576

    
1577
	$pppoeenable = false;
1578

    
1579
	if (!is_array($config['pppoes']) || !is_array($config['pppoes']['pppoe'])) {
1580
		return false;
1581
	}
1582

    
1583
	foreach ($config['pppoes']['pppoe'] as $pppoes) {
1584
		if ($pppoes['mode'] == 'server') {
1585
			$pppoeenable = true;
1586
		}
1587
	}
1588

    
1589
	return $pppoeenable;
1590
}
1591

    
1592
/* Optional arg forces hh:mm:ss without days */
1593
function convert_seconds_to_dhms($sec, $showhoursonly = false) {
1594
	if (!is_numericint($sec)) {
1595
		return '-';
1596
	}
1597
	// FIXME: When we move to PHP 7 we can use "intdiv($sec % X, Y)" etc
1598
	list($d, $h, $m, $s) = array(	(int)($showhoursonly ? 0 : $sec/86400),
1599
					(int)(($showhoursonly ? $sec : $sec % 86400)/3600),
1600
					(int)(($sec % 3600)/60),
1601
					$sec % 60
1602
				);
1603
	return ($d > 0 ? $d . 'd ' : '') . sprintf('%02d:%02d:%02d', $h, $m, $s);
1604
}
1605

    
1606
/* Compute the total uptime from the ppp uptime log file in the conf directory */
1607

    
1608
function get_ppp_uptime($port) {
1609
	if (file_exists("/conf/{$port}.log")) {
1610
		$saved_time = file_get_contents("/conf/{$port}.log");
1611
		$uptime_data = explode("\n", $saved_time);
1612
		$sec = 0;
1613
		foreach ($uptime_data as $upt) {
1614
			$sec += substr($upt, 1 + strpos($upt, " "));
1615
		}
1616
		return convert_seconds_to_dhms($sec);
1617
	} else {
1618
		$total_time = gettext("No history data found!");
1619
		return $total_time;
1620
	}
1621
}
1622

    
1623
//returns interface information
1624
function get_interface_info($ifdescr) {
1625
	global $config, $g;
1626

    
1627
	$ifinfo = array();
1628
	if (empty($config['interfaces'][$ifdescr])) {
1629
		return;
1630
	}
1631
	$ifinfo['hwif'] = $config['interfaces'][$ifdescr]['if'];
1632
	$ifinfo['enable'] = isset($config['interfaces'][$ifdescr]['enable']);
1633
	$ifinfo['if'] = get_real_interface($ifdescr);
1634

    
1635
	$chkif = $ifinfo['if'];
1636
	$ifinfotmp = pfSense_get_interface_addresses($chkif);
1637
	$ifinfo['status'] = $ifinfotmp['status'];
1638
	if (empty($ifinfo['status'])) {
1639
		$ifinfo['status'] = "down";
1640
	}
1641
	$ifinfo['macaddr'] = $ifinfotmp['macaddr'];
1642
	$ifinfo['mtu'] = $ifinfotmp['mtu'];
1643
	$ifinfo['ipaddr'] = $ifinfotmp['ipaddr'];
1644
	$ifinfo['subnet'] = $ifinfotmp['subnet'];
1645
	$ifinfo['linklocal'] = get_interface_linklocal($ifdescr);
1646
	$ifinfo['ipaddrv6'] = get_interface_ipv6($ifdescr);
1647
	$ifinfo['subnetv6'] = get_interface_subnetv6($ifdescr);
1648
	if (isset($ifinfotmp['link0'])) {
1649
		$link0 = "down";
1650
	}
1651
	$ifinfotmp = pfSense_get_interface_stats($chkif);
1652
	// $ifinfo['inpkts'] = $ifinfotmp['inpkts'];
1653
	// $ifinfo['outpkts'] = $ifinfotmp['outpkts'];
1654
	$ifinfo['inerrs'] = $ifinfotmp['inerrs'];
1655
	$ifinfo['outerrs'] = $ifinfotmp['outerrs'];
1656
	$ifinfo['collisions'] = $ifinfotmp['collisions'];
1657

    
1658
	/* Use pfctl for non wrapping 64 bit counters */
1659
	/* Pass */
1660
	exec("/sbin/pfctl -vvsI -i {$chkif}", $pfctlstats);
1661
	$pf_in4_pass = preg_split("/ +/ ", $pfctlstats[3]);
1662
	$pf_out4_pass = preg_split("/ +/", $pfctlstats[5]);
1663
	$pf_in6_pass = preg_split("/ +/ ", $pfctlstats[7]);
1664
	$pf_out6_pass = preg_split("/ +/", $pfctlstats[9]);
1665
	$in4_pass = $pf_in4_pass[5];
1666
	$out4_pass = $pf_out4_pass[5];
1667
	$in4_pass_packets = $pf_in4_pass[3];
1668
	$out4_pass_packets = $pf_out4_pass[3];
1669
	$in6_pass = $pf_in6_pass[5];
1670
	$out6_pass = $pf_out6_pass[5];
1671
	$in6_pass_packets = $pf_in6_pass[3];
1672
	$out6_pass_packets = $pf_out6_pass[3];
1673
	$ifinfo['inbytespass'] = $in4_pass + $in6_pass;
1674
	$ifinfo['outbytespass'] = $out4_pass + $out6_pass;
1675
	$ifinfo['inpktspass'] = $in4_pass_packets + $in6_pass_packets;
1676
	$ifinfo['outpktspass'] = $out4_pass_packets + $out6_pass_packets;
1677

    
1678
	/* Block */
1679
	$pf_in4_block = preg_split("/ +/", $pfctlstats[4]);
1680
	$pf_out4_block = preg_split("/ +/", $pfctlstats[6]);
1681
	$pf_in6_block = preg_split("/ +/", $pfctlstats[8]);
1682
	$pf_out6_block = preg_split("/ +/", $pfctlstats[10]);
1683
	$in4_block = $pf_in4_block[5];
1684
	$out4_block = $pf_out4_block[5];
1685
	$in4_block_packets = $pf_in4_block[3];
1686
	$out4_block_packets = $pf_out4_block[3];
1687
	$in6_block = $pf_in6_block[5];
1688
	$out6_block = $pf_out6_block[5];
1689
	$in6_block_packets = $pf_in6_block[3];
1690
	$out6_block_packets = $pf_out6_block[3];
1691
	$ifinfo['inbytesblock'] = $in4_block + $in6_block;
1692
	$ifinfo['outbytesblock'] = $out4_block + $out6_block;
1693
	$ifinfo['inpktsblock'] = $in4_block_packets + $in6_block_packets;
1694
	$ifinfo['outpktsblock'] = $out4_block_packets + $out6_block_packets;
1695

    
1696
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1697
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1698
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1699
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1700

    
1701
	$ifconfiginfo = "";
1702
	$link_type = $config['interfaces'][$ifdescr]['ipaddr'];
1703
	switch ($link_type) {
1704
		/* DHCP? -> see if dhclient is up */
1705
		case "dhcp":
1706
			/* see if dhclient is up */
1707
			if (find_dhclient_process($ifinfo['if']) != 0) {
1708
				$ifinfo['dhcplink'] = "up";
1709
			} else {
1710
				$ifinfo['dhcplink'] = "down";
1711
			}
1712

    
1713
			break;
1714
		/* PPPoE/PPTP/L2TP interface? -> get status from virtual interface */
1715
		case "pppoe":
1716
		case "pptp":
1717
		case "l2tp":
1718
			if ($ifinfo['status'] == "up" && !isset($link0)) {
1719
				/* get PPPoE link status for dial on demand */
1720
				$ifinfo["{$link_type}link"] = "up";
1721
			} else {
1722
				$ifinfo["{$link_type}link"] = "down";
1723
			}
1724

    
1725
			break;
1726
		/* PPP interface? -> get uptime for this session and cumulative uptime from the persistent log file in conf */
1727
		case "ppp":
1728
			if ($ifinfo['status'] == "up") {
1729
				$ifinfo['ppplink'] = "up";
1730
			} else {
1731
				$ifinfo['ppplink'] = "down" ;
1732
			}
1733

    
1734
			if (empty($ifinfo['status'])) {
1735
				$ifinfo['status'] = "down";
1736
			}
1737

    
1738
			if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) {
1739
				foreach ($config['ppps']['ppp'] as $pppid => $ppp) {
1740
					if ($config['interfaces'][$ifdescr]['if'] == $ppp['if']) {
1741
						break;
1742
					}
1743
				}
1744
			}
1745
			$dev = $ppp['ports'];
1746
			if ($config['interfaces'][$ifdescr]['if'] != $ppp['if'] || empty($dev)) {
1747
				break;
1748
			}
1749
			if (!file_exists($dev)) {
1750
				$ifinfo['nodevice'] = 1;
1751
				$ifinfo['pppinfo'] = $dev . " " . gettext("device not present! Is the modem attached to the system?");
1752
			}
1753

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

    
1789
	if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) {
1790
		$sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`);
1791
		$ifinfo['ppp_uptime'] = convert_seconds_to_dhms($sec);
1792
	}
1793

    
1794
	if ($ifinfo['status'] == "up") {
1795
		/* try to determine media with ifconfig */
1796
		unset($ifconfiginfo);
1797
		exec("/sbin/ifconfig " . $ifinfo['if'], $ifconfiginfo);
1798
		$wifconfiginfo = array();
1799
		if (is_interface_wireless($ifdescr)) {
1800
			exec("/sbin/ifconfig {$ifinfo['if']} list sta", $wifconfiginfo);
1801
			array_shift($wifconfiginfo);
1802
		}
1803
		$matches = "";
1804
		foreach ($ifconfiginfo as $ici) {
1805

    
1806
			/* don't list media/speed for wireless cards, as it always
1807
			   displays 2 Mbps even though clients can connect at 11 Mbps */
1808
			if (preg_match("/media: .*? \((.*?)\)/", $ici, $matches)) {
1809
				$ifinfo['media'] = $matches[1];
1810
			} else if (preg_match("/media: Ethernet (.*)/", $ici, $matches)) {
1811
				$ifinfo['media'] = $matches[1];
1812
			} else if (preg_match("/media: IEEE 802.11 Wireless Ethernet (.*)/", $ici, $matches)) {
1813
				$ifinfo['media'] = $matches[1];
1814
			}
1815

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

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

    
1879
	return $ifinfo;
1880
}
1881

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

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

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

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

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

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

    
1964
function pfsense_default_state_size() {
1965
	/* get system memory amount */
1966
	$memory = get_memory();
1967
	$physmem = $memory[0];
1968
	/* Be cautious and only allocate 10% of system memory to the state table */
1969
	$max_states = (int) ($physmem/10)*1000;
1970
	return $max_states;
1971
}
1972

    
1973
function pfsense_default_tables_size() {
1974
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1975
	return $current;
1976
}
1977

    
1978
function pfsense_default_table_entries_size() {
1979
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1980
	return (trim($current));
1981
}
1982

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

    
2006
	if (trim($oldcontents) != trim($contents)) {
2007
		if ($g['debug']) {
2008
			log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents));
2009
		}
2010
		return ($oldcontents);
2011
	} else {
2012
		return false;
2013
	}
2014
}
2015

    
2016
/*
2017
 * load_crypto() - Load crypto modules if enabled in config.
2018
 */
2019
function load_crypto() {
2020
	global $config, $g;
2021
	$crypto_modules = array('aesni', 'cryptodev');
2022

    
2023
	$enabled_modules = explode('_', $config['system']['crypto_hardware']);
2024

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

    
2038
/*
2039
 * load_thermal_hardware() - Load temperature monitor kernel module
2040
 */
2041
function load_thermal_hardware() {
2042
	global $config, $g;
2043
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
2044

    
2045
	if (!in_array($config['system']['thermal_hardware'], $thermal_hardware_modules)) {
2046
		return false;
2047
	}
2048

    
2049
	if (!empty($config['system']['thermal_hardware']) && !is_module_loaded($config['system']['thermal_hardware'])) {
2050
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $config['system']['thermal_hardware']));
2051
		mwexec("/sbin/kldload {$config['system']['thermal_hardware']}");
2052
	}
2053
}
2054

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

    
2067
	if ($rc != 0 || !isset($output[0])) {
2068
		return false;
2069
	}
2070

    
2071
	foreach ($virtualenvs as $virtualenv) {
2072
		if (stripos($output[0], $virtualenv) !== false) {
2073
			return true;
2074
		}
2075
	}
2076

    
2077
	return false;
2078
}
2079

    
2080
function get_freebsd_version() {
2081
	$version = explode(".", php_uname("r"));
2082
	return $version[0];
2083
}
2084

    
2085
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
2086
	global $config, $g;
2087

    
2088
	$fp = fopen($destination, "wb");
2089

    
2090
	if (!$fp) {
2091
		return false;
2092
	}
2093

    
2094
	$ch = curl_init();
2095
	curl_setopt($ch, CURLOPT_URL, $url);
2096
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify_ssl);
2097
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify_ssl);
2098
	curl_setopt($ch, CURLOPT_FILE, $fp);
2099
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2100
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2101
	curl_setopt($ch, CURLOPT_HEADER, false);
2102
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2103
	if (!isset($config['system']['do_not_send_uniqueid'])) {
2104
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2105
	} else {
2106
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
2107
	}
2108

    
2109
	set_curlproxy($ch);
2110

    
2111
	@curl_exec($ch);
2112
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2113
	fclose($fp);
2114
	curl_close($ch);
2115
	if ($http_code == 200) {
2116
		return true;
2117
	} else {
2118
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2119
		unlink_if_exists($destination);
2120
		return false;
2121
	}
2122
}
2123

    
2124
function download_file_with_progress_bar($url, $destination, $verify_ssl = true, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) {
2125
	global $config, $g;
2126
	global $ch, $fout, $file_size, $downloaded, $config, $first_progress_update;
2127
	$file_size = 1;
2128
	$downloaded = 1;
2129
	$first_progress_update = TRUE;
2130
	/* open destination file */
2131
	$fout = fopen($destination, "wb");
2132

    
2133
	if (!$fout) {
2134
		return false;
2135
	}
2136
	/*
2137
	 *      Originally by Author: Keyvan Minoukadeh
2138
	 *      Modified by Scott Ullrich to return Content-Length size
2139
	 */
2140
	$ch = curl_init();
2141
	curl_setopt($ch, CURLOPT_URL, $url);
2142
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify_ssl);
2143
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify_ssl);
2144
	curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
2145
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2146
	curl_setopt($ch, CURLOPT_WRITEFUNCTION, $readbody);
2147
	curl_setopt($ch, CURLOPT_NOPROGRESS, '1');
2148
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2149
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2150
	if (!isset($config['system']['do_not_send_uniqueid'])) {
2151
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2152
	} else {
2153
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
2154
	}
2155

    
2156
	set_curlproxy($ch);
2157

    
2158
	@curl_exec($ch);
2159
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2160
	fclose($fout);
2161
	curl_close($ch);
2162
	if ($http_code == 200) {
2163
		return true;
2164
	} else {
2165
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2166
		unlink_if_exists($destination);
2167
		return false;
2168
	}
2169
}
2170

    
2171
function read_header($ch, $string) {
2172
	global $file_size, $fout;
2173
	$length = strlen($string);
2174
	$regs = "";
2175
	preg_match("/(Content-Length:) (.*)/", $string, $regs);
2176
	if ($regs[2] <> "") {
2177
		$file_size = intval($regs[2]);
2178
	}
2179
	ob_flush();
2180
	return $length;
2181
}
2182

    
2183
function read_body($ch, $string) {
2184
	global $fout, $file_size, $downloaded, $sendto, $static_status, $static_output, $lastseen, $first_progress_update;
2185
	global $pkg_interface;
2186
	$length = strlen($string);
2187
	$downloaded += intval($length);
2188
	if ($file_size > 0) {
2189
		$downloadProgress = round(100 * (1 - $downloaded / $file_size), 0);
2190
		$downloadProgress = 100 - $downloadProgress;
2191
	} else {
2192
		$downloadProgress = 0;
2193
	}
2194
	if ($lastseen <> $downloadProgress and $downloadProgress < 101) {
2195
		if ($sendto == "status") {
2196
			if ($pkg_interface == "console") {
2197
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2198
					$tostatus = $static_status . $downloadProgress . "%";
2199
					if ($downloadProgress == 100) {
2200
						$tostatus = $tostatus . "\r";
2201
					}
2202
					update_status($tostatus);
2203
				}
2204
			} else {
2205
				$tostatus = $static_status . $downloadProgress . "%";
2206
				update_status($tostatus);
2207
			}
2208
		} else {
2209
			if ($pkg_interface == "console") {
2210
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2211
					$tooutput = $static_output . $downloadProgress . "%";
2212
					if ($downloadProgress == 100) {
2213
						$tooutput = $tooutput . "\r";
2214
					}
2215
					update_output_window($tooutput);
2216
				}
2217
			} else {
2218
				$tooutput = $static_output . $downloadProgress . "%";
2219
				update_output_window($tooutput);
2220
			}
2221
		}
2222
		if (($pkg_interface != "console") || (($downloadProgress % 10) == 0) || ($downloadProgress < 10)) {
2223
			update_progress_bar($downloadProgress, $first_progress_update);
2224
			$first_progress_update = FALSE;
2225
		}
2226
		$lastseen = $downloadProgress;
2227
	}
2228
	if ($fout) {
2229
		fwrite($fout, $string);
2230
	}
2231
	ob_flush();
2232
	return $length;
2233
}
2234

    
2235
/*
2236
 *   update_output_window: update bottom textarea dynamically.
2237
 */
2238
function update_output_window($text) {
2239
	global $pkg_interface;
2240
	$log = preg_replace("/\n/", "\\n", $text);
2241
	if ($pkg_interface != "console") {
2242
?>
2243
<script type="text/javascript">
2244
//<![CDATA[
2245
	document.getElementById("output").textContent="<?=htmlspecialchars($log)?>";
2246
	document.getElementById("output").scrollTop = document.getElementById("output").scrollHeight;
2247
//]]>
2248
</script>
2249
<?php
2250
	}
2251
	/* ensure that contents are written out */
2252
	ob_flush();
2253
}
2254

    
2255
/*
2256
 *   update_status: update top textarea dynamically.
2257
 */
2258
function update_status($status) {
2259
	global $pkg_interface;
2260

    
2261
	if ($pkg_interface == "console") {
2262
		print ("{$status}");
2263
	}
2264

    
2265
	/* ensure that contents are written out */
2266
	ob_flush();
2267
}
2268

    
2269
/*
2270
 * update_progress_bar($percent, $first_time): updates the javascript driven progress bar.
2271
 */
2272
function update_progress_bar($percent, $first_time) {
2273
	global $pkg_interface;
2274
	if ($percent > 100) {
2275
		$percent = 1;
2276
	}
2277
	if ($pkg_interface <> "console") {
2278
		echo '<script type="text/javascript">';
2279
		echo "\n//<![CDATA[\n";
2280
		echo 'document.getElementById("progressbar").style.width="'. $percent.'%"';
2281
		echo "\n//]]>\n";
2282
		echo '</script>';
2283
	} else {
2284
		if (!($first_time)) {
2285
			echo "\x08\x08\x08\x08\x08";
2286
		}
2287
		echo sprintf("%4d%%", $percent);
2288
	}
2289
}
2290

    
2291
function update_alias_name($new_alias_name, $orig_alias_name) {
2292
	if (!$orig_alias_name) {
2293
		return;
2294
	}
2295

    
2296
	// Firewall rules
2297
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2298
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2299
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2300
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2301
	// NAT Rules
2302
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2303
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2304
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2305
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2306
	update_alias_names_upon_change(array('nat', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2307
	update_alias_names_upon_change(array('nat', 'rule'), array('local-port'), $new_alias_name, $orig_alias_name);
2308
	// NAT 1:1 Rules
2309
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('external'), $new_alias_name, $orig_alias_name);
2310
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2311
	update_alias_names_upon_change(array('nat', 'onetoone'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2312
	// NAT Outbound Rules
2313
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('source', 'network'), $new_alias_name, $orig_alias_name);
2314
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('sourceport'), $new_alias_name, $orig_alias_name);
2315
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2316
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('dstport'), $new_alias_name, $orig_alias_name);
2317
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2318
	// Alias in an alias
2319
	update_alias_names_upon_change(array('aliases', 'alias'), array('address'), $new_alias_name, $orig_alias_name);
2320
}
2321

    
2322
function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) {
2323
	global $g, $config, $pconfig, $debug;
2324
	if (!$origname) {
2325
		return;
2326
	}
2327

    
2328
	$sectionref = &$config;
2329
	foreach ($section as $sectionname) {
2330
		if (is_array($sectionref) && isset($sectionref[$sectionname])) {
2331
			$sectionref = &$sectionref[$sectionname];
2332
		} else {
2333
			return;
2334
		}
2335
	}
2336

    
2337
	if ($debug) {
2338
		$fd = fopen("{$g['tmp_path']}/print_r", "a");
2339
		fwrite($fd, print_r($pconfig, true));
2340
	}
2341

    
2342
	if (is_array($sectionref)) {
2343
		foreach ($sectionref as $itemkey => $item) {
2344
			if ($debug) {
2345
				fwrite($fd, "$itemkey\n");
2346
			}
2347

    
2348
			$fieldfound = true;
2349
			$fieldref = &$sectionref[$itemkey];
2350
			foreach ($field as $fieldname) {
2351
				if (is_array($fieldref) && isset($fieldref[$fieldname])) {
2352
					$fieldref = &$fieldref[$fieldname];
2353
				} else {
2354
					$fieldfound = false;
2355
					break;
2356
				}
2357
			}
2358
			if ($fieldfound && $fieldref == $origname) {
2359
				if ($debug) {
2360
					fwrite($fd, "Setting old alias value $origname to $new_alias_name\n");
2361
				}
2362
				$fieldref = $new_alias_name;
2363
			}
2364
		}
2365
	}
2366

    
2367
	if ($debug) {
2368
		fclose($fd);
2369
	}
2370

    
2371
}
2372

    
2373
function parse_aliases_file($filename, $type = "url", $max_items = -1, $kflc = false) {
2374
	/*
2375
	 * $filename = file to process for example blocklist like DROP:  http://www.spamhaus.org/drop/drop.txt
2376
	 * $type = if set to 'url' then subnets and ips will be returned,
2377
	 *         if set to 'url_ports' port-ranges and ports will be returned
2378
	 * $max_items = sets the maximum amount of valid items to load, -1 the default defines there is no limit.
2379
	 *
2380
	 * RETURNS an array of ip subnets and ip's or ports and port-ranges, returns NULL upon a error conditions (file not found)
2381
	 */
2382

    
2383
	if (!file_exists($filename)) {
2384
		log_error(sprintf(gettext("Could not process non-existent file from alias: %s"), $filename));
2385
		return null;
2386
	}
2387

    
2388
	if (filesize($filename) == 0) {
2389
		log_error(sprintf(gettext("Could not process empty file from alias: %s"), $filename));
2390
		return null;
2391
	}
2392
	$fd = @fopen($filename, 'r');
2393
	if (!$fd) {
2394
		log_error(sprintf(gettext("Could not process aliases from alias: %s"), $filename));
2395
		return null;
2396
	}
2397
	$items = array();
2398
	$comments = array();
2399
	/* NOTE: fgetss() is not a typo RTFM before being smart */
2400
	while (($fc = fgetss($fd)) !== FALSE) {
2401
		$tmp = alias_idn_to_ascii(trim($fc, " \t\n\r"));
2402
		if (empty($tmp)) {
2403
			continue;
2404
		}
2405
		if (($kflc) && (strpos($tmp, '#') === 0)) {	// Keep Full Line Comments (lines beginning with #).
2406
			$comments[] = $tmp;
2407
		} else {
2408
			$tmp_str = strstr($tmp, '#', true);
2409
			if (!empty($tmp_str)) {
2410
				$tmp = $tmp_str;
2411
			}
2412
			$tmp_str = strstr($tmp, ' ', true);
2413
			if (!empty($tmp_str)) {
2414
				$tmp = $tmp_str;
2415
			}
2416
			switch ($type) {
2417
				case "url":
2418
				case "urltable":
2419
					if (is_ipaddr($tmp) || is_subnet($tmp)) {
2420
						$items[] = $tmp;
2421
						break;
2422
					}
2423
					if (is_fqdn($tmp)) {
2424
						$results = resolve_host_addresses($tmp, array(DNS_A, DNS_AAAA), false);
2425
						if (!empty($results)) {
2426
							foreach ($results as $ip) {
2427
								if (is_ipaddr($ip)) {
2428
									$items[] = $ip;
2429
								}
2430
							}
2431
						}
2432
					}
2433
					break;
2434
				case "url_ports":
2435
				case "urltable_ports":
2436
					if (is_port_or_range($tmp)) {
2437
						$items[] = $tmp;
2438
					}
2439
					break;
2440
				default:
2441
					/* unknown type */
2442
					break;
2443
				}
2444
			if (count($items) == $max_items) {
2445
				break;
2446
			}
2447
		}
2448
	}
2449
	fclose($fd);
2450
	return array_merge($comments, $items);
2451
}
2452

    
2453
function update_alias_url_data() {
2454
	global $config, $g, $aliastable;
2455

    
2456
	$updated = false;
2457

    
2458
	/* item is a url type */
2459
	$lockkey = lock('aliasurl');
2460
	if (is_array($config['aliases']['alias'])) {
2461
		$aliases = array();
2462
		$aliases_nested = array();
2463

    
2464
		foreach ($config['aliases']['alias'] as $x => $alias) {
2465
			if (empty($alias['aliasurl'])) {
2466
				continue;
2467
			}
2468
			foreach ($alias['aliasurl'] as $alias_url) {
2469
				if (is_alias($alias_url)) {
2470
					// process nested URL aliases after URL-only aliases
2471
					$aliases_nested[] = $x;
2472
					continue 2;
2473
				}
2474
			}
2475
			$aliases[] = $x;
2476
		}
2477

    
2478
		foreach (array_merge($aliases, $aliases_nested) as $x) {
2479

    
2480
			$address = array();
2481
			$type = $config['aliases']['alias'][$x]['type'];
2482
			foreach ($config['aliases']['alias'][$x]['aliasurl'] as $alias_url) {
2483
				/* fetch down and add in */
2484
				if (is_URL($alias_url)) {
2485
					$temp_filename = tempnam("{$g['tmp_path']}/", "alias_import");
2486
					rmdir_recursive($temp_filename);
2487
					$verify_ssl = isset($config['system']['checkaliasesurlcert']);
2488
					mkdir($temp_filename);
2489
					if (!download_file($alias_url, $temp_filename . "/aliases", $verify_ssl)) {
2490
						log_error(sprintf(gettext("Failed to download alias %s"), $alias_url));
2491
						rmdir_recursive($temp_filename);
2492
						continue;
2493
					}
2494

    
2495
					/* if the item is tar gzipped then extract */
2496
					if (stripos($alias_url, '.tgz') && !process_alias_tgz($temp_filename)) {
2497
						log_error(sprintf(gettext("Could not unpack tgz from the URL '%s'."), $alias_url));
2498
						rmdir_recursive($temp_filename);
2499
						continue;
2500
					}
2501
					if (file_exists("{$temp_filename}/aliases")) {
2502
						$t_address = parse_aliases_file("{$temp_filename}/aliases", $type, 5000);
2503
						if ($t_address == null) {
2504
							/* nothing was found */
2505
							log_error(sprintf(gettext("Could not fetch usable data from '%s'."), $alias_url));
2506
							//rmdir_recursive($temp_filename);
2507
							continue;
2508
						} else {
2509
							array_push($address, ...$t_address);
2510
						}
2511
						unset($t_address);
2512
					}
2513
					rmdir_recursive($temp_filename);
2514
				} elseif (is_alias($alias_url)) {
2515
					/* nested URL alias, see https://redmine.pfsense.org/issues/11863 */
2516
					if (!$aliastable) {
2517
						alias_make_table();
2518
					}
2519
					$t_address = explode(" ", $aliastable[$alias_url]);
2520
					if ($t_address == null) {
2521
						log_error(sprintf(gettext("Could not get usable data from '%s' URL alias."), $alias_url));
2522
						continue;
2523
					}
2524
					array_push($address, ...$t_address);
2525
				}
2526
				if (!empty($address)) {
2527
					$config['aliases']['alias'][$x]['address'] = implode(" ", $address);
2528
					$updated = true;
2529
				}
2530
			}
2531
		}
2532
	}
2533
	unlock($lockkey);
2534

    
2535
	/* Report status to callers as well */
2536
	return $updated;
2537
}
2538

    
2539
function process_alias_tgz($temp_filename) {
2540
	if (!file_exists('/usr/bin/tar')) {
2541
		log_error(gettext("Alias archive is a .tar/tgz file which cannot be decompressed because utility is missing!"));
2542
		return false;
2543
	}
2544
	rename("{$temp_filename}/aliases", "{$temp_filename}/aliases.tgz");
2545
	mwexec("/usr/bin/tar xzf {$temp_filename}/aliases.tgz -C {$temp_filename}/aliases/");
2546
	unlink("{$temp_filename}/aliases.tgz");
2547
	$files_to_process = return_dir_as_array("{$temp_filename}/");
2548
	/* foreach through all extracted files and build up aliases file */
2549
	$fd = @fopen("{$temp_filename}/aliases", "w");
2550
	if (!$fd) {
2551
		log_error(sprintf(gettext("Could not open %s/aliases for writing!"), $temp_filename));
2552
		return false;
2553
	}
2554
	foreach ($files_to_process as $f2p) {
2555
		$tmpfd = @fopen($f2p, 'r');
2556
		if (!$tmpfd) {
2557
			log_error(sprintf(gettext('The following file could not be read %1$s from %2$s'), $f2p, $temp_filename));
2558
			continue;
2559
		}
2560
		while (($tmpbuf = fread($tmpfd, 65536)) !== FALSE) {
2561
			fwrite($fd, $tmpbuf);
2562
		}
2563
		fclose($tmpfd);
2564
		unlink($f2p);
2565
	}
2566
	fclose($fd);
2567
	unset($tmpbuf);
2568

    
2569
	return true;
2570
}
2571

    
2572
function version_compare_dates($a, $b) {
2573
	$a_time = strtotime($a);
2574
	$b_time = strtotime($b);
2575

    
2576
	if ((!$a_time) || (!$b_time)) {
2577
		return FALSE;
2578
	} else {
2579
		if ($a_time < $b_time) {
2580
			return -1;
2581
		} elseif ($a_time == $b_time) {
2582
			return 0;
2583
		} else {
2584
			return 1;
2585
		}
2586
	}
2587
}
2588
function version_get_string_value($a) {
2589
	$strs = array(
2590
		0 => "ALPHA-ALPHA",
2591
		2 => "ALPHA",
2592
		3 => "BETA",
2593
		4 => "B",
2594
		5 => "C",
2595
		6 => "D",
2596
		7 => "RC",
2597
		8 => "RELEASE",
2598
		9 => "*"			// Matches all release levels
2599
	);
2600
	$major = 0;
2601
	$minor = 0;
2602
	foreach ($strs as $num => $str) {
2603
		if (substr($a, 0, strlen($str)) == $str) {
2604
			$major = $num;
2605
			$n = substr($a, strlen($str));
2606
			if (is_numeric($n)) {
2607
				$minor = $n;
2608
			}
2609
			break;
2610
		}
2611
	}
2612
	return "{$major}.{$minor}";
2613
}
2614
function version_compare_string($a, $b) {
2615
	// Only compare string parts if both versions give a specific release
2616
	// (If either version lacks a string part, assume intended to match all release levels)
2617
	if (isset($a) && isset($b)) {
2618
		return version_compare_numeric(version_get_string_value($a), version_get_string_value($b));
2619
	} else {
2620
		return 0;
2621
	}
2622
}
2623
function version_compare_numeric($a, $b) {
2624
	$a_arr = explode('.', rtrim($a, '.'));
2625
	$b_arr = explode('.', rtrim($b, '.'));
2626

    
2627
	foreach ($a_arr as $n => $val) {
2628
		if (array_key_exists($n, $b_arr)) {
2629
			// So far so good, both have values at this minor version level. Compare.
2630
			if ($val > $b_arr[$n]) {
2631
				return 1;
2632
			} elseif ($val < $b_arr[$n]) {
2633
				return -1;
2634
			}
2635
		} else {
2636
			// a is greater, since b doesn't have any minor version here.
2637
			return 1;
2638
		}
2639
	}
2640
	if (count($b_arr) > count($a_arr)) {
2641
		// b is longer than a, so it must be greater.
2642
		return -1;
2643
	} else {
2644
		// Both a and b are of equal length and value.
2645
		return 0;
2646
	}
2647
}
2648
function pfs_version_compare($cur_time, $cur_text, $remote) {
2649
	// First try date compare
2650
	$v = version_compare_dates($cur_time, $remote);
2651
	if ($v === FALSE) {
2652
		// If that fails, try to compare by string
2653
		// Before anything else, simply test if the strings are equal
2654
		if (($cur_text == $remote) || ($cur_time == $remote)) {
2655
			return 0;
2656
		}
2657
		list($cur_num, $cur_str) = explode('-', $cur_text);
2658
		list($rem_num, $rem_str) = explode('-', $remote);
2659

    
2660
		// First try to compare the numeric parts of the version string.
2661
		$v = version_compare_numeric($cur_num, $rem_num);
2662

    
2663
		// If the numeric parts are the same, compare the string parts.
2664
		if ($v == 0) {
2665
			return version_compare_string($cur_str, $rem_str);
2666
		}
2667
	}
2668
	return $v;
2669
}
2670
function process_alias_urltable($name, $type, $url, $freq, $forceupdate=false, $validateonly=false) {
2671
	global $g, $config;
2672

    
2673
	$urltable_prefix = "/var/db/aliastables/";
2674
	$urltable_filename = $urltable_prefix . $name . ".txt";
2675
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2676

    
2677
	// Make the aliases directory if it doesn't exist
2678
	if (!file_exists($urltable_prefix)) {
2679
		mkdir($urltable_prefix);
2680
	} elseif (!is_dir($urltable_prefix)) {
2681
		unlink($urltable_prefix);
2682
		mkdir($urltable_prefix);
2683
	}
2684

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

    
2690
		// Try to fetch the URL supplied
2691
		unlink_if_exists($tmp_urltable_filename);
2692
		$verify_ssl = isset($config['system']['checkaliasesurlcert']);
2693
		if (download_file($url, $tmp_urltable_filename, $verify_ssl)) {
2694
			// Convert lines that begin with '$' or ';' to comments '#' instead of deleting them.
2695
			mwexec("/usr/bin/sed -i \"\" -E 's/^[[:space:]]*($|#|;)/#/g; /^#/!s/\;.*//g;' ". escapeshellarg($tmp_urltable_filename));
2696

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

    
2699
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2700
			if ($type == "urltable_ports") {
2701
				$parsed_contents = group_ports($parsed_contents, true);
2702
			}
2703
			if (is_array($parsed_contents)) {
2704
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2705
			} else {
2706
				touch($urltable_filename);
2707
			}
2708

    
2709
			/* Remove existing archive and create an up to date archive if RAM disk is enabled. */
2710
			unlink_if_exists("{$g['cf_conf_path']}/RAM_Disk_Store/{$name}.txt.tgz");
2711
			if (isset($config['system']['use_mfs_tmpvar']) && !file_exists("/conf/ram_disks_failed")) {
2712
				mwexec("/usr/bin/tar -czf " . escapeshellarg("{$g['cf_conf_path']}/RAM_Disk_Store/{$name}.txt.tgz") . " -C / " . escapeshellarg($urltable_filename));
2713
			}
2714

    
2715
			unlink_if_exists($tmp_urltable_filename);
2716
		} else {
2717
			if (!$validateonly) {
2718
				touch($urltable_filename);
2719
			}
2720
			return false;
2721
		}
2722
		return true;
2723
	} else {
2724
		// File exists, and it doesn't need to be updated.
2725
		return -1;
2726
	}
2727
}
2728

    
2729
function get_include_contents($filename) {
2730
	if (is_file($filename)) {
2731
		ob_start();
2732
		include $filename;
2733
		$contents = ob_get_contents();
2734
		ob_end_clean();
2735
		return $contents;
2736
	}
2737
	return false;
2738
}
2739

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

    
2852
function get_country_name($country_code = "ALL") {
2853
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2854
		return "";
2855
	}
2856

    
2857
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2858
	$country_names_contents = file_get_contents($country_names_xml);
2859
	$country_names = xml2array($country_names_contents);
2860

    
2861
	if ($country_code == "ALL") {
2862
		$country_list = array();
2863
		foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2864
			$country_list[] = array(
2865
				"code" => $country['ISO_3166-1_Alpha-2_Code_element'],
2866
				"name" => ucwords(strtolower($country['ISO_3166-1_Country_name'])));
2867
		}
2868
		return $country_list;
2869
	}
2870

    
2871
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2872
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2873
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2874
		}
2875
	}
2876
	return "";
2877
}
2878

    
2879
/* Return the list of country codes to be used on CAs and certs */
2880
function get_cert_country_codes() {
2881
	$countries = get_country_name();
2882

    
2883
	$country_codes = array();
2884
	foreach ($countries as $country) {
2885
		$country_codes[$country['code']] = $country['code'];
2886
	}
2887
	ksort($country_codes);
2888

    
2889
	/* Preserve historical order: None, US, CA, other countries */
2890
	$first_items[''] = gettext("None");
2891
	$first_items['US'] = $country_codes['US'];
2892
	$first_items['CA'] = $country_codes['CA'];
2893
	unset($country_codes['US']);
2894
	unset($country_codes['CA']);
2895

    
2896
	return array_merge($first_items, $country_codes);
2897
}
2898

    
2899
/* sort by interface only, retain the original order of rules that apply to
2900
   the same interface */
2901
function filter_rules_sort() {
2902
	global $config;
2903

    
2904
	init_config_arr(array('filter', 'rule'));
2905
	/* mark each rule with the sequence number (to retain the order while sorting) */
2906
	for ($i = 0; isset($config['filter']['rule'][$i]); $i++) {
2907
		if (!is_array($config['filter']['rule'][$i])) {
2908
			$config['filter']['rule'][$i] = array();
2909
		}
2910
		$config['filter']['rule'][$i]['seq'] = $i;
2911
	}
2912

    
2913
	usort($config['filter']['rule'], "filter_rules_compare");
2914

    
2915
	/* strip the sequence numbers again */
2916
	for ($i = 0; isset($config['filter']['rule'][$i]); $i++) {
2917
		unset($config['filter']['rule'][$i]['seq']);
2918
	}
2919
}
2920
function filter_rules_compare($a, $b) {
2921
	if (isset($a['floating']) && isset($b['floating'])) {
2922
		return $a['seq'] - $b['seq'];
2923
	} else if (isset($a['floating'])) {
2924
		return -1;
2925
	} else if (isset($b['floating'])) {
2926
		return 1;
2927
	} else if ($a['interface'] == $b['interface']) {
2928
		return $a['seq'] - $b['seq'];
2929
	} else {
2930
		return compare_interface_friendly_names($a['interface'], $b['interface']);
2931
	}
2932
}
2933

    
2934
function generate_ipv6_from_mac($mac) {
2935
	$elements = explode(":", $mac);
2936
	if (count($elements) <> 6) {
2937
		return false;
2938
	}
2939

    
2940
	$i = 0;
2941
	$ipv6 = "fe80::";
2942
	foreach ($elements as $byte) {
2943
		if ($i == 0) {
2944
			$hexadecimal = substr($byte, 1, 2);
2945
			$bitmap = base_convert($hexadecimal, 16, 2);
2946
			$bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT);
2947
			$bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3, 4);
2948
			$byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16);
2949
		}
2950
		$ipv6 .= $byte;
2951
		if ($i == 1) {
2952
			$ipv6 .= ":";
2953
		}
2954
		if ($i == 3) {
2955
			$ipv6 .= ":";
2956
		}
2957
		if ($i == 2) {
2958
			$ipv6 .= "ff:fe";
2959
		}
2960

    
2961
		$i++;
2962
	}
2963
	return $ipv6;
2964
}
2965

    
2966
/****f* pfsense-utils/load_mac_manufacturer_table
2967
 * NAME
2968
 *   load_mac_manufacturer_table
2969
 * INPUTS
2970
 *   none
2971
 * RESULT
2972
 *   returns associative array with MAC-Manufacturer pairs
2973
 ******/
2974
function load_mac_manufacturer_table() {
2975
	/* load MAC-Manufacture data from the file */
2976
	$macs = false;
2977
	if (file_exists("/usr/local/share/nmap/nmap-mac-prefixes")) {
2978
		$macs=file("/usr/local/share/nmap/nmap-mac-prefixes");
2979
	}
2980
	if ($macs) {
2981
		foreach ($macs as $line) {
2982
			if (preg_match('/([0-9A-Fa-f]{6}) (.*)$/', $line, $matches)) {
2983
				/* store values like this $mac_man['000C29']='VMware' */
2984
				$mac_man["$matches[1]"] = $matches[2];
2985
			}
2986
		}
2987
		return $mac_man;
2988
	} else {
2989
		return -1;
2990
	}
2991

    
2992
}
2993

    
2994
/****f* pfsense-utils/is_ipaddr_configured
2995
 * NAME
2996
 *   is_ipaddr_configured
2997
 * INPUTS
2998
 *   IP Address to check.
2999
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
3000
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
3001
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
3002
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
3003
 *     If check_subnets is true and cidrprefix is specified,
3004
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
3005
 * RESULT
3006
 *   returns true if the IP Address is configured and present on this device or overlaps a configured subnet.
3007
*/
3008
function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
3009
	if (count(where_is_ipaddr_configured($ipaddr, $ignore_if, $check_localip, $check_subnets, $cidrprefix))) {
3010
		return true;
3011
	}
3012
	return false;
3013
}
3014

    
3015
/****f* pfsense-utils/where_is_ipaddr_configured
3016
 * NAME
3017
 *   where_is_ipaddr_configured
3018
 * INPUTS
3019
 *   IP Address to check.
3020
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
3021
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
3022
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
3023
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
3024
 *     If check_subnets is true and cidrprefix is specified,
3025
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
3026
 * RESULT
3027
 *   Returns an array of the interfaces 'if' plus IP address or subnet 'ip_or_subnet' that match or overlap the IP address to check.
3028
 *   If there are no matches then an empty array is returned.
3029
*/
3030
function where_is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
3031
	global $config;
3032

    
3033
	$where_configured = array();
3034

    
3035
	$pos = strpos($ignore_if, '_virtualip');
3036
	if ($pos !== false) {
3037
		$ignore_vip_id = substr($ignore_if, $pos+10);
3038
		$ignore_vip_if = substr($ignore_if, 0, $pos);
3039
	} else {
3040
		$ignore_vip_id = -1;
3041
		$ignore_vip_if = $ignore_if;
3042
	}
3043

    
3044
	$isipv6 = is_ipaddrv6($ipaddr);
3045

    
3046
	if ($isipv6) {
3047
		$ipaddr = text_to_compressed_ip6($ipaddr);
3048
	}
3049

    
3050
	if ($check_subnets) {
3051
		$cidrprefix = intval($cidrprefix);
3052
		if ($isipv6) {
3053
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
3054
				$cidrprefix = 128;
3055
			}
3056
		} else {
3057
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
3058
				$cidrprefix = 32;
3059
			}
3060
		}
3061
		$iflist = get_configured_interface_list();
3062
		foreach ($iflist as $if => $ifname) {
3063
			if ($ignore_if == $if) {
3064
				continue;
3065
			}
3066

    
3067
			if ($isipv6) {
3068
				$if_ipv6 = get_interface_ipv6($if);
3069
				$if_snbitsv6 = get_interface_subnetv6($if);
3070
				if ($if_ipv6 && $if_snbitsv6 && check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) {
3071
					$where_entry = array();
3072
					$where_entry['if'] = $if;
3073
					$where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if);
3074
					$where_configured[] = $where_entry;
3075
				}
3076
			} else {
3077
				$if_ipv4 = get_interface_ip($if);
3078
				$if_snbitsv4 = get_interface_subnet($if);
3079
				if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) {
3080
					$where_entry = array();
3081
					$where_entry['if'] = $if;
3082
					$where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if);
3083
					$where_configured[] = $where_entry;
3084
				}
3085
			}
3086
		}
3087
	} else {
3088
		if ($isipv6) {
3089
			$interface_list_ips = get_configured_ipv6_addresses();
3090
		} else {
3091
			$interface_list_ips = get_configured_ip_addresses();
3092
		}
3093

    
3094
		foreach ($interface_list_ips as $if => $ilips) {
3095
			if ($ignore_if == $if) {
3096
				continue;
3097
			}
3098
			if (strcasecmp($ipaddr, $ilips) == 0) {
3099
				$where_entry = array();
3100
				$where_entry['if'] = $if;
3101
				$where_entry['ip_or_subnet'] = $ilips;
3102
				$where_configured[] = $where_entry;
3103
			}
3104
		}
3105
	}
3106

    
3107
	if ($check_localip) {
3108
		if (!is_array($config['l2tp']) && !empty($config['l2tp']['localip']) && (strcasecmp($ipaddr, text_to_compressed_ip6($config['l2tp']['localip'])) == 0)) {
3109
			$where_entry = array();
3110
			$where_entry['if'] = 'l2tp';
3111
			$where_entry['ip_or_subnet'] = $config['l2tp']['localip'];
3112
			$where_configured[] = $where_entry;
3113
		}
3114
	}
3115

    
3116
	return $where_configured;
3117
}
3118

    
3119
/****f* pfsense-utils/pfSense_handle_custom_code
3120
 * NAME
3121
 *   pfSense_handle_custom_code
3122
 * INPUTS
3123
 *   directory name to process
3124
 * RESULT
3125
 *   globs the directory and includes the files
3126
 */
3127
function pfSense_handle_custom_code($src_dir) {
3128
	// Allow extending of the nat edit page and include custom input validation
3129
	if (is_dir("$src_dir")) {
3130
		$cf = glob($src_dir . "/*.inc");
3131
		foreach ($cf as $nf) {
3132
			if ($nf == "." || $nf == "..") {
3133
				continue;
3134
			}
3135
			// Include the extra handler
3136
			include_once("$nf");
3137
		}
3138
	}
3139
}
3140

    
3141
function set_language() {
3142
	global $config, $g;
3143

    
3144
	if (!empty($config['system']['language'])) {
3145
		$lang = $config['system']['language'];
3146
	} elseif (!empty($g['language'])) {
3147
		$lang = $g['language'];
3148
	}
3149
	$lang .= ".UTF-8";
3150

    
3151
	putenv("LANG={$lang}");
3152
	setlocale(LC_ALL, $lang);
3153
	textdomain("pfSense");
3154
	bindtextdomain("pfSense", "/usr/local/share/locale");
3155
	bind_textdomain_codeset("pfSense", $lang);
3156
}
3157

    
3158
function get_locale_list() {
3159
	$locales = array(
3160
		"bs" => gettext("Bosnian"),
3161
		"zh_CN" => gettext("Chinese"),
3162
		"zh_Hans_CN" => gettext("Chinese (Simplified, China)"),
3163
		"zh_Hans_HK" => gettext("Chinese (Simplified, Hong Kong SAR China)"),
3164
		"zh_Hant_TW" => gettext("Chinese (Traditional, Taiwan)"),
3165
		"nl" => gettext("Dutch"),
3166
		"en_US" => gettext("English"),
3167
		"fr" => gettext("French"),
3168
		"de_DE" => gettext("German (Germany)"),
3169
		"it" => gettext("Italian"),
3170
		"ko" => gettext("Korean"),
3171
		"nb" => gettext("Norwegian Bokmål"),
3172
		"pl" => gettext("Polish"),
3173
		"pt_PT" => gettext("Portuguese"),
3174
		"pt_BR" => gettext("Portuguese (Brazil)"),
3175
		"ru" => gettext("Russian"),
3176
		"es" => gettext("Spanish"),
3177
		"es_AR" => gettext("Spanish (Argentina)"),
3178
	);
3179

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

    
3184
	//asort($locales);
3185

    
3186
	return $locales;
3187
}
3188

    
3189
function return_hex_ipv4($ipv4) {
3190
	if (!is_ipaddrv4($ipv4)) {
3191
		return(false);
3192
	}
3193

    
3194
	/* we need the hex form of the interface IPv4 address */
3195
	$ip4arr = explode(".", $ipv4);
3196
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3197
}
3198

    
3199
function convert_ipv6_to_128bit($ipv6) {
3200
	if (!is_ipaddrv6($ipv6)) {
3201
		return(false);
3202
	}
3203

    
3204
	$ip6arr = array();
3205
	$ip6prefix = Net_IPv6::uncompress($ipv6);
3206
	$ip6arr = explode(":", $ip6prefix);
3207
	/* binary presentation of the prefix for all 128 bits. */
3208
	$ip6prefixbin = "";
3209
	foreach ($ip6arr as $element) {
3210
		$ip6prefixbin .= sprintf("%016b", hexdec($element));
3211
	}
3212
	return($ip6prefixbin);
3213
}
3214

    
3215
function convert_128bit_to_ipv6($ip6bin) {
3216
	if (strlen($ip6bin) <> 128) {
3217
		return(false);
3218
	}
3219

    
3220
	$ip6arr = array();
3221
	$ip6binarr = array();
3222
	$ip6binarr = str_split($ip6bin, 16);
3223
	foreach ($ip6binarr as $binpart) {
3224
		$ip6arr[] = dechex(bindec($binpart));
3225
	}
3226
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3227

    
3228
	return($ip6addr);
3229
}
3230

    
3231

    
3232
/* Returns the calculated bit length of the prefix delegation from the WAN interface */
3233
/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */
3234
/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */
3235
/* 6to4 is 16 bits, e.g. 65535 */
3236
function calculate_ipv6_delegation_length($if) {
3237
	global $config;
3238

    
3239
	if (!is_array($config['interfaces'][$if])) {
3240
		return false;
3241
	}
3242

    
3243
	switch ($config['interfaces'][$if]['ipaddrv6']) {
3244
		case "6to4":
3245
			$pdlen = 16;
3246
			break;
3247
		case "6rd":
3248
			$rd6cfg = $config['interfaces'][$if];
3249
			$rd6plen = explode("/", $rd6cfg['prefix-6rd']);
3250
			$pdlen = (64 - ((int) $rd6plen[1] + (32 - (int) $rd6cfg['prefix-6rd-v4plen'])));
3251
			break;
3252
		case "dhcp6":
3253
			$dhcp6cfg = $config['interfaces'][$if];
3254
			$pdlen = $dhcp6cfg['dhcp6-ia-pd-len'];
3255
			break;
3256
		default:
3257
			$pdlen = 0;
3258
			break;
3259
	}
3260
	return($pdlen);
3261
}
3262

    
3263
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3264
	$prefix = Net_IPv6::uncompress($prefix, true);
3265
	$suffix = Net_IPv6::uncompress($suffix, true);
3266

    
3267
	/*
3268
	 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
3269
	 *                ^^^^ ^
3270
	 *                |||| \-> 64
3271
	 *                |||\---> 63, 62, 61, 60
3272
	 *                ||\----> 56
3273
	 *                |\-----> 52
3274
	 *                \------> 48
3275
	 */
3276

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

    
3308
	return text_to_compressed_ip6(substr($prefix, 0, $prefix_len) .
3309
	    substr($suffix, $prefix_len));
3310
}
3311

    
3312
function dhcpv6_pd_str_help($pdlen) {
3313
	$result = '';
3314

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

    
3347
	return $result;
3348
}
3349

    
3350
function huawei_rssi_to_string($rssi) {
3351
	$dbm = array();
3352
	$i = 0;
3353
	$dbstart = -113;
3354
	while ($i < 32) {
3355
		$dbm[$i] = $dbstart + ($i * 2);
3356
		$i++;
3357
	}
3358
	$percent = round(($rssi / 31) * 100);
3359
	$string = "rssi:{$rssi} level:{$dbm[$rssi]}dBm percent:{$percent}%";
3360
	return $string;
3361
}
3362

    
3363
function huawei_mode_to_string($mode, $submode) {
3364
	$modes[0] = gettext("None");
3365
	$modes[1] = "AMPS";
3366
	$modes[2] = "CDMA";
3367
	$modes[3] = "GSM/GPRS";
3368
	$modes[4] = "HDR";
3369
	$modes[5] = "WCDMA";
3370
	$modes[6] = "GPS";
3371

    
3372
	$submodes[0] = gettext("No Service");
3373
	$submodes[1] = "GSM";
3374
	$submodes[2] = "GPRS";
3375
	$submodes[3] = "EDGE";
3376
	$submodes[4] = "WCDMA";
3377
	$submodes[5] = "HSDPA";
3378
	$submodes[6] = "HSUPA";
3379
	$submodes[7] = "HSDPA+HSUPA";
3380
	$submodes[8] = "TD-SCDMA";
3381
	$submodes[9] = "HSPA+";
3382
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3383
	return $string;
3384
}
3385

    
3386
function huawei_service_to_string($state) {
3387
	$modes[0] = gettext("No Service");
3388
	$modes[1] = gettext("Restricted Service");
3389
	$modes[2] = gettext("Valid Service");
3390
	$modes[3] = gettext("Restricted Regional Service");
3391
	$modes[4] = gettext("Powersaving Service");
3392
	$modes[255] = gettext("Unknown Service");
3393
	$string = $modes[$state];
3394
	return $string;
3395
}
3396

    
3397
function huawei_simstate_to_string($state) {
3398
	$modes[0] = gettext("Invalid SIM/locked State");
3399
	$modes[1] = gettext("Valid SIM State");
3400
	$modes[2] = gettext("Invalid SIM CS State");
3401
	$modes[3] = gettext("Invalid SIM PS State");
3402
	$modes[4] = gettext("Invalid SIM CS/PS State");
3403
	$modes[255] = gettext("Missing SIM State");
3404
	$string = $modes[$state];
3405
	return $string;
3406
}
3407

    
3408
function zte_rssi_to_string($rssi) {
3409
	return huawei_rssi_to_string($rssi);
3410
}
3411

    
3412
function zte_mode_to_string($mode, $submode) {
3413
	$modes[0] = gettext("No Service");
3414
	$modes[1] = gettext("Limited Service");
3415
	$modes[2] = "GPRS";
3416
	$modes[3] = "GSM";
3417
	$modes[4] = "UMTS";
3418
	$modes[5] = "EDGE";
3419
	$modes[6] = "HSDPA";
3420

    
3421
	$submodes[0] = "CS_ONLY";
3422
	$submodes[1] = "PS_ONLY";
3423
	$submodes[2] = "CS_PS";
3424
	$submodes[3] = "CAMPED";
3425
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3426
	return $string;
3427
}
3428

    
3429
function zte_service_to_string($service) {
3430
	$modes[0] = gettext("Initializing Service");
3431
	$modes[1] = gettext("Network Lock error Service");
3432
	$modes[2] = gettext("Network Locked Service");
3433
	$modes[3] = gettext("Unlocked or correct MCC/MNC Service");
3434
	$string = $modes[$service];
3435
	return $string;
3436
}
3437

    
3438
function zte_simstate_to_string($state) {
3439
	$modes[0] = gettext("No action State");
3440
	$modes[1] = gettext("Network lock State");
3441
	$modes[2] = gettext("(U)SIM card lock State");
3442
	$modes[3] = gettext("Network Lock and (U)SIM card Lock State");
3443
	$string = $modes[$state];
3444
	return $string;
3445
}
3446

    
3447
function get_configured_pppoe_server_interfaces() {
3448
	global $config;
3449
	$iflist = array();
3450
	if (is_array($config['pppoes']['pppoe'])) {
3451
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
3452
			if ($pppoe['mode'] == "server") {
3453
				$int = "poes". $pppoe['pppoeid'];
3454
				$iflist[$int] = strtoupper($int);
3455
			}
3456
		}
3457
	}
3458
	return $iflist;
3459
}
3460

    
3461
function get_pppoes_child_interfaces($ifpattern) {
3462
	$if_arr = array();
3463
	if ($ifpattern == "") {
3464
		return;
3465
	}
3466

    
3467
	exec("/sbin/ifconfig", $out, $ret);
3468
	foreach ($out as $line) {
3469
		if (preg_match("/^({$ifpattern}-[0-9]+):/i", $line, $match)) {
3470
			$if_arr[] = $match[1];
3471
		}
3472
	}
3473
	return $if_arr;
3474

    
3475
}
3476

    
3477
/****f* pfsense-utils/pkg_call_plugins
3478
 * NAME
3479
 *   pkg_call_plugins
3480
 * INPUTS
3481
 *   $plugin_type value used to search in package configuration if the plugin is used, also used to create the function name
3482
 *   $plugin_params parameters to pass to the plugin function for passing multiple parameters a array can be used.
3483
 * RESULT
3484
 *   returns associative array results from the plugin calls for each package
3485
 * NOTES
3486
 *   This generic function can be used to notify or retrieve results from functions that are defined in packages.
3487
 ******/
3488
function pkg_call_plugins($plugin_type, $plugin_params) {
3489
	global $g, $config;
3490
	$results = array();
3491
	if (!is_array($config['installedpackages']['package'])) {
3492
		return $results;
3493
	}
3494
	foreach ($config['installedpackages']['package'] as $package) {
3495
		if (is_array($package['plugins']['item'])) {
3496
			foreach ($package['plugins']['item'] as $plugin) {
3497
				if ($plugin['type'] == $plugin_type) {
3498
					if (file_exists($package['include_file'])) {
3499
						require_once($package['include_file']);
3500
					} else {
3501
						continue;
3502
					}
3503
					$pkgname = substr(reverse_strrchr($package['configurationfile'], "."), 0, -1);
3504
					$plugin_function = $pkgname . '_'. $plugin_type;
3505
					$results[$pkgname] = call_user_func($plugin_function, $plugin_params);
3506
				}
3507
			}
3508
		}
3509
	}
3510
	return $results;
3511
}
3512

    
3513
// Convert IPv6 addresses to lower case
3514
function addrtolower($ip) {
3515
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3516
		return(strtolower($ip));
3517
	} else {
3518
		return($ip);
3519
	}
3520
}
3521

    
3522
function compare_by_name($a, $b) {
3523
	return strcasecmp($a['name'], $b['name']);
3524
}
3525

    
3526
/****f* pfsense-utils/getarraybyref
3527
 * NAME
3528
 *   getarraybyref
3529
 * INPUTS
3530
 *   $array the array of which a items array needs to be found.
3531
 *   $args.. the sub-items to be retrieved/created
3532
 * RESULT
3533
 *   returns the array that was retrieved from configuration, its created if it does not exist
3534
 * NOTES
3535
 *   Used by haproxy / acme / others.?. .
3536
 *   can be used like this:  $a_certificates = getarraybyref($config, 'installedpackages', 'acme', 'certificates', 'item');
3537
 ******/
3538
function &getarraybyref(&$array) {
3539
	if (!isset($array)) {
3540
		return false;
3541
	}
3542
	if (!is_array($array)) {
3543
		$array = array();
3544
	}
3545
	$item = &$array;
3546
	$arg = func_get_args();
3547
	for($i = 1; $i < count($arg); $i++) {
3548
		$itemindex = $arg[$i];
3549
		if (!is_array($item[$itemindex])) {
3550
			$item[$itemindex] = array();
3551
		}
3552
		$item = &$item[$itemindex];
3553
	}
3554
	return $item;
3555
}
3556

    
3557
/****f* pfsense-utils/send_download_data
3558
 * NAME
3559
 *   send_download_data - Send content to a user's browser as a file to download
3560
 * INPUTS
3561
 *   $type        : The type of download, either 'data' to send the contents of
3562
 *                    a variable or 'file' to send the contents of a file on the
3563
 *                    filesystem.
3564
 *   $content     : For 'data' type, the content to send the user. For 'file'
3565
 *                    type, the full path to the file to send.
3566
 *   $userfilename: The filename presented to the user when downloading. For
3567
 *                    'file' type, this may be omitted and the basename of
3568
 *                    $content will be used instead.
3569
 *   $contenttype : MIME content type of the data. Default "application/octet-stream"
3570
 * RESULT
3571
 *   Sends the data to the browser as a file to download.
3572
 ******/
3573

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

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

    
3581
	/* Cannot determine the filename, so bail. */
3582
	if (empty($name)) {
3583
		exit;
3584
	}
3585

    
3586
	/* Send basic download headers */
3587
	header("Content-Type: {$contenttype}");
3588
	header("Content-Length: {$size}");
3589
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3590

    
3591
	/* Send cache headers */
3592
	if (isset($_SERVER['HTTPS'])) {
3593
		header('Pragma: ');
3594
		header('Cache-Control: ');
3595
	} else {
3596
		header("Pragma: private");
3597
		header("Cache-Control: private, must-revalidate");
3598
	}
3599

    
3600
	/* Ensure output buffering is off so PHP does not consume
3601
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3602
	while (ob_get_level()) {
3603
		@ob_end_clean();
3604
	}
3605

    
3606
	/* Send the data to the user */
3607
	if ($type == 'file') {
3608
		readfile($content);
3609
	} else {
3610
		echo $content;
3611
	}
3612

    
3613
	/* Flush any remaining output buffer */
3614
	@ob_end_flush();
3615
	exit;
3616
}
3617

    
3618
// Test whether the hostname in a URL can be resolved with a very short timeout
3619
function is_url_hostname_resolvable($url) {
3620
	$urlhostname = parse_url($url, PHP_URL_HOST);
3621
	if (empty($urlhostname)) {
3622
		return false;
3623
	}
3624
	putenv("RES_OPTIONS=timeout:3 attempts:1");
3625
	$resolvable = ($urlhostname !== gethostbyname($urlhostname));
3626
	putenv("RES_OPTIONS");
3627
	return $resolvable;
3628
}
3629

    
3630
function get_pf_timeouts () {
3631
	$pftimeout = array();
3632
	exec("/sbin/pfctl -st", $pfctlst, $retval);
3633
	if ($retval == 0) {
3634
		foreach ($pfctlst as $pfst) {
3635
			preg_match('/([a-z]+)\.([a-z]+)\s+([0-9]+)/', $pfst, $timeout);
3636
			if ($timeout[1] == "other") {
3637
				$proto = "Other";
3638
			} else {
3639
				$proto = strtoupper($timeout[1]);
3640
			}
3641
			if ($timeout[2] == "finwait") {
3642
				$type = "FIN Wait";
3643
			} else {
3644
				$type = ucfirst($timeout[2]);
3645
			}
3646
			$pftimeout[$proto][$type]['name'] = $proto . " " . $type;
3647
			$pftimeout[$proto][$type]['keyname'] = $timeout[1] . $timeout[2] . "timeout";
3648
			$pftimeout[$proto][$type]['value'] = $timeout[3];
3649
		}
3650
	}
3651
	return $pftimeout;
3652
}
3653

    
3654
function set_curlproxy(&$ch) {
3655
	global $config;
3656

    
3657
	if (!empty($config['system']['proxyurl'])) {
3658
		curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']);
3659
		if (!empty($config['system']['proxyport'])) {
3660
			curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']);
3661
		}
3662
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
3663
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
3664
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}");
3665
		}
3666
	}
3667
}
3668

    
3669
?>
(38-38/61)