Project

General

Profile

Download (101 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
/*
763
 * Return true if the CARP status of at least one interface of a captive portal zone is in backup mode
764
 * This function return false if CARP is not enabled on any interface of the captive portal zone
765
 */
766
function captiveportal_ha_is_node_in_backup_mode($cpzone) {
767
	global $config;
768

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

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

    
785
/*
786
 * get_pfsync_interface_status($pfsyncinterface): returns the status of a pfsync
787
 */
788
function get_pfsync_interface_status($pfsyncinterface) {
789
	if (!does_interface_exist($pfsyncinterface)) {
790
		return;
791
	}
792

    
793
	return exec_command("/sbin/ifconfig {$pfsyncinterface} | /usr/bin/awk '/pfsync:/ {print \$5}'");
794
}
795

    
796
/*
797
 * add_rule_to_anchor($anchor, $rule): adds the specified rule to an anchor
798
 */
799
function add_rule_to_anchor($anchor, $rule, $label) {
800
	mwexec("echo " . escapeshellarg($rule) . " | /sbin/pfctl -a " . escapeshellarg($anchor) . ":" . escapeshellarg($label) . " -f -");
801
}
802

    
803
/*
804
 * remove_text_from_file
805
 * remove $text from file $file
806
 */
807
function remove_text_from_file($file, $text) {
808
	if (!file_exists($file) && !is_writable($file)) {
809
		return;
810
	}
811
	$filecontents = file_get_contents($file);
812
	$text = str_replace($text, "", $filecontents);
813
	@file_put_contents($file, $text);
814
}
815

    
816
/*
817
 *   after_sync_bump_adv_skew(): create skew values by 1S
818
 */
819
function after_sync_bump_adv_skew() {
820
	global $config, $g;
821
	$processed_skew = 1;
822
	init_config_arr(array('virtualip', 'vip'));
823
	$a_vip = &$config['virtualip']['vip'];
824
	foreach ($a_vip as $vipent) {
825
		if ($vipent['advskew'] <> "") {
826
			$processed_skew = 1;
827
			$vipent['advskew'] = $vipent['advskew']+1;
828
		}
829
	}
830
	if ($processed_skew == 1) {
831
		write_config(gettext("After synch increase advertising skew"));
832
	}
833
}
834

    
835
/*
836
 * get_filename_from_url($url): converts a url to its filename.
837
 */
838
function get_filename_from_url($url) {
839
	return basename($url);
840
}
841

    
842
/*
843
 *   get_dir: return an array of $dir
844
 */
845
function get_dir($dir) {
846
	$dir_array = array();
847
	$d = dir($dir);
848
	if (!is_object($d)) {
849
		return array();
850
	}
851
	while (false !== ($entry = $d->read())) {
852
		array_push($dir_array, $entry);
853
	}
854
	$d->close();
855
	return $dir_array;
856
}
857

    
858
/****f* pfsense-utils/WakeOnLan
859
 * NAME
860
 *   WakeOnLan - Wake a machine up using the wake on lan format/protocol
861
 * RESULT
862
 *   true/false - true if the operation was successful
863
 ******/
864
function WakeOnLan($addr, $mac) {
865
	$addr_byte = explode(':', $mac);
866
	$hw_addr = '';
867

    
868
	for ($a = 0; $a < 6; $a++) {
869
		$hw_addr .= chr(hexdec($addr_byte[$a]));
870
	}
871

    
872
	$msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255);
873

    
874
	for ($a = 1; $a <= 16; $a++) {
875
		$msg .= $hw_addr;
876
	}
877

    
878
	// send it to the broadcast address using UDP
879
	$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
880
	if ($s == false) {
881
		log_error(gettext("Error creating socket!"));
882
		log_error(sprintf(gettext("Error code is '%1\$s' - %2\$s"), socket_last_error($s), socket_strerror(socket_last_error($s))));
883
	} else {
884
		// setting a broadcast option to socket:
885
		$opt_ret = socket_set_option($s, 1, 6, TRUE);
886
		if ($opt_ret < 0) {
887
			log_error(sprintf(gettext("setsockopt() failed, error: %s"), strerror($opt_ret)));
888
		}
889
		$e = socket_sendto($s, $msg, strlen($msg), 0, $addr, 2050);
890
		socket_close($s);
891
		log_error(sprintf(gettext('Magic Packet sent (%1$s) to (%2$s) MAC=%3$s'), $e, $addr, $mac));
892
		return true;
893
	}
894

    
895
	return false;
896
}
897

    
898
/*
899
 * reverse_strrchr($haystack, $needle):  Return everything in $haystack up to the *last* instance of $needle.
900
 *					 Useful for finding paths and stripping file extensions.
901
 */
902
function reverse_strrchr($haystack, $needle) {
903
	if (!is_string($haystack)) {
904
		return;
905
	}
906
	return strrpos($haystack, $needle) ? substr($haystack, 0, strrpos($haystack, $needle) +1) : false;
907
}
908

    
909
/*
910
 *  backup_config_section($section): returns as an xml file string of
911
 *                                   the configuration section
912
 */
913
function backup_config_section($section_name) {
914
	global $config;
915
	$new_section = &$config[$section_name];
916
	/* generate configuration XML */
917
	$xmlconfig = dump_xml_config($new_section, $section_name);
918
	$xmlconfig = str_replace("<?xml version=\"1.0\"?>", "", $xmlconfig);
919
	return $xmlconfig;
920
}
921

    
922
/*
923
 *  restore_config_section($section_name, new_contents): restore a configuration section,
924
 *                                                  and write the configuration out
925
 *                                                  to disk/cf.
926
 */
927
function restore_config_section($section_name, $new_contents) {
928
	global $config, $g;
929
	$fout = fopen("{$g['tmp_path']}/tmpxml", "w");
930
	fwrite($fout, $new_contents);
931
	fclose($fout);
932

    
933
	$xml = parse_xml_config($g['tmp_path'] . "/tmpxml", null);
934
	if ($xml['pfsense']) {
935
		$xml = $xml['pfsense'];
936
	}
937
	if ($xml[$section_name]) {
938
		$section_xml = $xml[$section_name];
939
	} else {
940
		$section_xml = -1;
941
	}
942

    
943
	@unlink($g['tmp_path'] . "/tmpxml");
944
	if ($section_xml === -1) {
945
		return false;
946
	}
947

    
948
	/* Save current pkg repo to re-add on new config */
949
	unset($pkg_repo_conf_path);
950
	if ($section_name == "system" &&
951
	    isset($config['system']['pkg_repo_conf_path'])) {
952
		$pkg_repo_conf_path = $config['system']['pkg_repo_conf_path'];
953
	}
954

    
955
	$config[$section_name] = &$section_xml;
956
	if (file_exists("{$g['tmp_path']}/config.cache")) {
957
		unlink("{$g['tmp_path']}/config.cache");
958
	}
959

    
960
	/* Restore previously pkg repo configured */
961
	if ($section_name == "system") {
962
		if (isset($pkg_repo_conf_path)) {
963
			$config['system']['pkg_repo_conf_path'] =
964
			    $pkg_repo_conf_path;
965
		} elseif (isset($config['system']['pkg_repo_conf_path'])) {
966
			unset($config['system']['pkg_repo_conf_path']);
967
		}
968
	}
969

    
970
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
971
	disable_security_checks();
972
	return true;
973
}
974

    
975
/*
976
 *  merge_config_section($section_name, new_contents):   restore a configuration section,
977
 *                                                  and write the configuration out
978
 *                                                  to disk/cf.  But preserve the prior
979
 * 													structure if needed
980
 */
981
function merge_config_section($section_name, $new_contents) {
982
	global $config;
983
	$fname = get_tmp_filename();
984
	$fout = fopen($fname, "w");
985
	fwrite($fout, $new_contents);
986
	fclose($fout);
987
	$section_xml = parse_xml_config($fname, $section_name);
988
	$config[$section_name] = $section_xml;
989
	unlink($fname);
990
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
991
	disable_security_checks();
992
	return;
993
}
994

    
995
/*
996
 * rmdir_recursive($path, $follow_links=false)
997
 * Recursively remove a directory tree (rm -rf path)
998
 * This is for directories _only_
999
 */
1000
function rmdir_recursive($path, $follow_links=false) {
1001
	$to_do = glob($path);
1002
	if (!is_array($to_do)) {
1003
		$to_do = array($to_do);
1004
	}
1005
	foreach ($to_do as $workingdir) { // Handle wildcards by foreaching.
1006
		if (file_exists($workingdir)) {
1007
			if (is_dir($workingdir)) {
1008
				$dir = opendir($workingdir);
1009
				while ($entry = readdir($dir)) {
1010
					if (is_file("$workingdir/$entry") || ((!$follow_links) && is_link("$workingdir/$entry"))) {
1011
						unlink("$workingdir/$entry");
1012
					} elseif (is_dir("$workingdir/$entry") && $entry != '.' && $entry != '..') {
1013
						rmdir_recursive("$workingdir/$entry");
1014
					}
1015
				}
1016
				closedir($dir);
1017
				rmdir($workingdir);
1018
			} elseif (is_file($workingdir)) {
1019
				unlink($workingdir);
1020
			}
1021
		}
1022
	}
1023
	return;
1024
}
1025

    
1026
/*
1027
 * host_firmware_version(): Return the versions used in this install
1028
 */
1029
function host_firmware_version($tocheck = "") {
1030
	global $g, $config;
1031

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

    
1034
	return array(
1035
		"firmware" => array("version" => $g['product_version']),
1036
		"kernel"   => array("version" => $os_version),
1037
		"base"     => array("version" => $os_version),
1038
		"platform" => $g['product_label'],
1039
		"config_version" => $config['version']
1040
	);
1041
}
1042

    
1043
function get_disk_info() {
1044
	$diskout = "";
1045
	exec("/bin/df -h | /usr/bin/grep -w '/' | /usr/bin/awk '{ print $2, $3, $4, $5 }'", $diskout);
1046
	return explode(' ', $diskout[0]);
1047
}
1048

    
1049
/****f* pfsense-utils/strncpy
1050
 * NAME
1051
 *   strncpy - copy strings
1052
 * INPUTS
1053
 *   &$dst, $src, $length
1054
 * RESULT
1055
 *   none
1056
 ******/
1057
function strncpy(&$dst, $src, $length) {
1058
	if (strlen($src) > $length) {
1059
		$dst = substr($src, 0, $length);
1060
	} else {
1061
		$dst = $src;
1062
	}
1063
}
1064

    
1065
/****f* pfsense-utils/reload_interfaces_sync
1066
 * NAME
1067
 *   reload_interfaces - reload all interfaces
1068
 * INPUTS
1069
 *   none
1070
 * RESULT
1071
 *   none
1072
 ******/
1073
function reload_interfaces_sync() {
1074
	global $config, $g;
1075

    
1076
	if ($g['debug']) {
1077
		log_error(gettext("reload_interfaces_sync() is starting."));
1078
	}
1079

    
1080
	/* parse config.xml again */
1081
	$config = parse_config(true);
1082

    
1083
	/* enable routing */
1084
	system_routing_enable();
1085
	if ($g['debug']) {
1086
		log_error(gettext("Enabling system routing"));
1087
	}
1088

    
1089
	if ($g['debug']) {
1090
		log_error(gettext("Cleaning up Interfaces"));
1091
	}
1092

    
1093
	/* set up interfaces */
1094
	interfaces_configure();
1095
}
1096

    
1097
/****f* pfsense-utils/reload_all
1098
 * NAME
1099
 *   reload_all - triggers a reload of all settings
1100
 *   * INPUTS
1101
 *   none
1102
 * RESULT
1103
 *   none
1104
 ******/
1105
function reload_all() {
1106
	send_event("service reload all");
1107
}
1108

    
1109
/****f* pfsense-utils/reload_interfaces
1110
 * NAME
1111
 *   reload_interfaces - triggers a reload of all interfaces
1112
 * INPUTS
1113
 *   none
1114
 * RESULT
1115
 *   none
1116
 ******/
1117
function reload_interfaces() {
1118
	send_event("interface all reload");
1119
}
1120

    
1121
/****f* pfsense-utils/reload_all_sync
1122
 * NAME
1123
 *   reload_all - reload all settings
1124
 *   * INPUTS
1125
 *   none
1126
 * RESULT
1127
 *   none
1128
 ******/
1129
function reload_all_sync() {
1130
	global $config, $g;
1131

    
1132
	/* parse config.xml again */
1133
	$config = parse_config(true);
1134

    
1135
	/* set up our timezone */
1136
	system_timezone_configure();
1137

    
1138
	/* set up our hostname */
1139
	system_hostname_configure();
1140

    
1141
	/* make hosts file */
1142
	system_hosts_generate();
1143

    
1144
	/* generate resolv.conf */
1145
	system_resolvconf_generate();
1146

    
1147
	/* enable routing */
1148
	system_routing_enable();
1149

    
1150
	/* set up interfaces */
1151
	interfaces_configure();
1152

    
1153
	/* start dyndns service */
1154
	services_dyndns_configure();
1155

    
1156
	/* configure cron service */
1157
	configure_cron();
1158

    
1159
	/* start the NTP client */
1160
	system_ntp_configure();
1161

    
1162
	/* sync pw database */
1163
	unlink_if_exists("/etc/spwd.db.tmp");
1164
	mwexec("/usr/sbin/pwd_mkdb -d /etc/ /etc/master.passwd");
1165

    
1166
	/* restart sshd */
1167
	send_event("service restart sshd");
1168

    
1169
	/* restart webConfigurator if needed */
1170
	send_event("service restart webgui");
1171
}
1172

    
1173
function load_loader_conf($loader_conf = NULL, $local = false) {
1174

    
1175
	if ($loader_conf == NULL) {
1176
		return (NULL);
1177
	}
1178
	if (file_exists($loader_conf)) {
1179
		$input = file_get_contents($loader_conf);
1180
	} else {
1181
		$input = "";
1182
	}
1183

    
1184
	$input_split = explode("\n", $input);
1185

    
1186
	/*
1187
	 * Loop through and only add lines that are not empty and not
1188
	 * managed by us.
1189
	 */
1190
	$data = array();
1191
	/* These values should be removed from loader.conf and loader.conf.local
1192
	 * As they will be replaced when necessary. */
1193
	$remove = array("hw.usb.no_pf", "hint.mdio.0.at", "hint.e6000sw.0",
1194
	    "hw.e6000sw.default_disabled", "vm.pmap.pti",
1195
	    "net.pf.request_maxcount", "hw.hn.vf_transparent",
1196
	    "hw.hn.use_if_start");
1197
	if (!$local) {
1198
		/* These values should only be filtered in loader.conf, not .local */
1199
		$remove = array_merge($remove,
1200
		    array("autoboot_delay", "console", "comconsole_speed",
1201
		    "boot_multicons", "boot_serial", "hint.uart.0.flags",
1202
		    "hint.uart.1.flags"));
1203
	}
1204
	foreach ($input_split as $line) {
1205
		if (empty($line)) {
1206
			continue;
1207
		}
1208
		$skip = false;
1209
		list($name, $value) = explode('=', $line, 2);
1210
		foreach($remove as $rid => $rkey) {
1211
			if (strncasecmp(trim($name), $rkey, strlen($rkey)) == 0) {
1212
				$skip = true;
1213
				break;
1214
			}
1215
		}
1216
		if (!$skip) {
1217
			$data[] = $line;
1218
		}
1219
	}
1220

    
1221
	return ($data);
1222
}
1223

    
1224
function setup_loader_settings($path = "", $upgrade = false) {
1225
	global $g, $config;
1226

    
1227
	$boot_config_file = "{$path}/boot.config";
1228
	$loader_conf_file = "{$path}/boot/loader.conf";
1229

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

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

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

    
1268
	if (empty($data)) {
1269
		@unlink($boot_conf_file);
1270
	} else {
1271
		safe_write_file($boot_config_file, $data);
1272
	}
1273
	unset($data, $boot_config, $boot_config_file, $boot_config_split);
1274

    
1275
	/* Serial console - write out /boot/loader.conf */
1276
	if ($upgrade) {
1277
		system("echo \"Reading {$loader_conf_file}...\" >> /conf/upgrade_log.txt");
1278
	}
1279

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

    
1304
	if ($specific_platform['name'] == 'RCC-VE' ||
1305
	    $specific_platform['name'] == 'RCC' ||
1306
	    $specific_platform['name'] == 'SG-2220') {
1307
		$data[] = 'comconsole_port="0x2F8"';
1308
		$data[] = 'hint.uart.0.flags="0x00"';
1309
		$data[] = 'hint.uart.1.flags="0x10"';
1310
	}
1311
	$data[] = 'autoboot_delay="3"';
1312
	if (isset($config['system']['pti_disabled'])) {
1313
		$data[] = 'vm.pmap.pti="0"';
1314
	}
1315

    
1316
	/* Enable ALTQ support for hnX NICs. */
1317
	if (isset($config['system']['hn_altq_enable'])) {
1318
		$data[] = 'hw.hn.vf_transparent="0"';
1319
		$data[] = 'hw.hn.use_if_start="1"';
1320
	}
1321

    
1322
	safe_write_file($loader_conf_file, $data);
1323

    
1324
	/* Filter loader.conf.local to avoid duplicate settings. */
1325
	$loader_conf_file = "{$path}/boot/loader.conf.local";
1326
	$data = load_loader_conf($loader_conf_file, true);
1327
	if (empty($data)) {
1328
		@unlink($loader_conf_file);
1329
	} else {
1330
		safe_write_file($loader_conf_file, $data);
1331
	}
1332

    
1333
}
1334

    
1335
function console_configure($when = "save", $path = "") {
1336
	global $config;
1337
	$ttys_file = "{$path}/etc/ttys";
1338

    
1339
	/* Update the loader settings. */
1340
	setup_loader_settings($path, ($when == "upgrade"));
1341

    
1342
	$ttys = file_get_contents($ttys_file);
1343
	$ttys_split = explode("\n", $ttys);
1344

    
1345
	$data = array();
1346

    
1347
	$on_off = (is_serial_enabled() ? 'onifconsole' : 'off');
1348

    
1349
	if (isset($config['system']['disableconsolemenu'])) {
1350
		$console_type = 'Pc';
1351
		$serial_type = '3wire';
1352
	} else {
1353
		$console_type = 'al.Pc';
1354
		$serial_type = 'al.3wire';
1355
	}
1356

    
1357
	$console_line = "console\tnone\t\t\t\tunknown\toff\tsecure";
1358
	$virt_line =
1359
	    "\"/usr/libexec/getty {$console_type}\"\txterm\tonifexists secure";
1360
	$ttyu_line =
1361
	    "\"/usr/libexec/getty {$serial_type}\"\tvt100\t{$on_off}\tsecure";
1362

    
1363
	$found = array();
1364

    
1365
	foreach ($ttys_split as $tty) {
1366
		/* Ignore blank lines */
1367
		if (empty($tty)) {
1368
			continue;
1369
		}
1370

    
1371
		if (stristr($tty, "ttyv0")) {
1372
			$found['ttyv0'] = 1;
1373
			$data[] = "ttyv0\t{$virt_line}";
1374
		} elseif (stristr($tty, "xc0")) {
1375
			$found['xc0'] = 1;
1376
			$data[] = "xc0\t{$virt_line}";
1377
		} elseif (stristr($tty, "ttyu")) {
1378
			$ttyn = substr($tty, 0, 5);
1379
			$found[$ttyn] = 1;
1380
			$data[] = "{$ttyn}\t{$ttyu_line}";
1381
		} elseif (substr($tty, 0, 7) == 'console') {
1382
			$found['console'] = 1;
1383
			$data[] = $tty;
1384
		} else {
1385
			$data[] = $tty;
1386
		}
1387
	}
1388
	unset($on_off, $console_type, $serial_type);
1389

    
1390
	/* Detect missing main lines on original file and try to rebuild it */
1391
	$items = array(
1392
		'console',
1393
		'ttyv0',
1394
		'ttyu0',
1395
		'ttyu1',
1396
		'ttyu2',
1397
		'ttyu3',
1398
		'xc0'
1399
	);
1400

    
1401
	foreach ($items as $item) {
1402
		if (isset($found[$item])) {
1403
			continue;
1404
		}
1405

    
1406
		if ($item == 'console') {
1407
			$data[] = $console_line;
1408
		} elseif (($item == 'ttyv0') || ($item == 'xc0')) {
1409
			/* xc0 - Xen console, see https://redmine.pfsense.org/issues/11402 */
1410
			$data[] = "{$item}\t{$virt_line}";
1411
		} else {
1412
			$data[] = "{$item}\t{$ttyu_line}";
1413
		}
1414
	}
1415

    
1416
	safe_write_file($ttys_file, $data);
1417

    
1418
	unset($ttys, $ttys_file, $ttys_split, $data);
1419

    
1420
	if ($when != "upgrade") {
1421
		reload_ttys();
1422
	}
1423

    
1424
	return;
1425
}
1426

    
1427
function is_serial_enabled() {
1428
	global $g, $config;
1429

    
1430
	if (!isset($g['enableserial_force']) &&
1431
	    !isset($config['system']['enableserial'])) {
1432
		return false;
1433
	}
1434

    
1435
	return true;
1436
}
1437

    
1438
function reload_ttys() {
1439
	// Send a HUP signal to init will make it reload /etc/ttys
1440
	posix_kill(1, SIGHUP);
1441
}
1442

    
1443
function print_value_list($list, $count = 10, $separator = ",") {
1444
	$list = implode($separator, array_slice($list, 0, $count));
1445
	if (count($list) < $count) {
1446
		$list .= ".";
1447
	} else {
1448
		$list .= "...";
1449
	}
1450
	return $list;
1451
}
1452

    
1453
/* DHCP enabled on any interfaces? */
1454
function is_dhcp_server_enabled() {
1455
	global $config;
1456

    
1457
	if (!is_array($config['dhcpd'])) {
1458
		return false;
1459
	}
1460

    
1461
	foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) {
1462
		if (isset($dhcpifconf['enable']) && !empty($config['interfaces'][$dhcpif])) {
1463
			return true;
1464
		}
1465
	}
1466

    
1467
	return false;
1468
}
1469

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

    
1474
	if (is_array($config['interfaces'])) {
1475
		foreach ($config['interfaces'] as $ifcfg) {
1476
			if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface'])) {
1477
				return true;
1478
			}
1479
		}
1480
	}
1481

    
1482
	if (!is_array($config['dhcpdv6'])) {
1483
		return false;
1484
	}
1485

    
1486
	foreach ($config['dhcpdv6'] as $dhcpv6if => $dhcpv6ifconf) {
1487
		if (isset($dhcpv6ifconf['enable']) && !empty($config['interfaces'][$dhcpv6if])) {
1488
			return true;
1489
		}
1490
	}
1491

    
1492
	return false;
1493
}
1494

    
1495
/* radvd enabled on any interfaces? */
1496
function is_radvd_enabled() {
1497
	global $config;
1498

    
1499
	if (!is_array($config['dhcpdv6'])) {
1500
		$config['dhcpdv6'] = array();
1501
	}
1502

    
1503
	$dhcpdv6cfg = $config['dhcpdv6'];
1504
	$Iflist = get_configured_interface_list();
1505

    
1506
	/* handle manually configured DHCP6 server settings first */
1507
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1508
		if (!isset($config['interfaces'][$dhcpv6if]['enable'])) {
1509
			continue;
1510
		}
1511

    
1512
		if (!isset($dhcpv6ifconf['ramode'])) {
1513
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
1514
		}
1515

    
1516
		if ($dhcpv6ifconf['ramode'] == "disabled") {
1517
			continue;
1518
		}
1519

    
1520
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1521
		if (!is_ipaddrv6($ifcfgipv6)) {
1522
			continue;
1523
		}
1524

    
1525
		return true;
1526
	}
1527

    
1528
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
1529
	foreach ($Iflist as $if => $ifdescr) {
1530
		if (!isset($config['interfaces'][$if]['track6-interface'])) {
1531
			continue;
1532
		}
1533
		if (!isset($config['interfaces'][$if]['enable'])) {
1534
			continue;
1535
		}
1536

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

    
1542
		$ifcfgsnv6 = get_interface_subnetv6($if);
1543
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1544

    
1545
		if (!is_ipaddrv6($subnetv6)) {
1546
			continue;
1547
		}
1548

    
1549
		return true;
1550
	}
1551

    
1552
	return false;
1553
}
1554

    
1555
/* Any PPPoE servers enabled? */
1556
function is_pppoe_server_enabled() {
1557
	global $config;
1558

    
1559
	$pppoeenable = false;
1560

    
1561
	if (!is_array($config['pppoes']) || !is_array($config['pppoes']['pppoe'])) {
1562
		return false;
1563
	}
1564

    
1565
	foreach ($config['pppoes']['pppoe'] as $pppoes) {
1566
		if ($pppoes['mode'] == 'server') {
1567
			$pppoeenable = true;
1568
		}
1569
	}
1570

    
1571
	return $pppoeenable;
1572
}
1573

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

    
1588
/* Compute the total uptime from the ppp uptime log file in the conf directory */
1589

    
1590
function get_ppp_uptime($port) {
1591
	if (file_exists("/conf/{$port}.log")) {
1592
		$saved_time = file_get_contents("/conf/{$port}.log");
1593
		$uptime_data = explode("\n", $saved_time);
1594
		$sec = 0;
1595
		foreach ($uptime_data as $upt) {
1596
			$sec += substr($upt, 1 + strpos($upt, " "));
1597
		}
1598
		return convert_seconds_to_dhms($sec);
1599
	} else {
1600
		$total_time = gettext("No history data found!");
1601
		return $total_time;
1602
	}
1603
}
1604

    
1605
//returns interface information
1606
function get_interface_info($ifdescr) {
1607
	global $config, $g;
1608

    
1609
	$ifinfo = array();
1610
	if (empty($config['interfaces'][$ifdescr])) {
1611
		return;
1612
	}
1613
	$ifinfo['hwif'] = $config['interfaces'][$ifdescr]['if'];
1614
	$ifinfo['enable'] = isset($config['interfaces'][$ifdescr]['enable']);
1615
	$ifinfo['if'] = get_real_interface($ifdescr);
1616

    
1617
	$chkif = $ifinfo['if'];
1618
	$ifinfotmp = pfSense_get_interface_addresses($chkif);
1619
	$ifinfo['status'] = $ifinfotmp['status'];
1620
	if (empty($ifinfo['status'])) {
1621
		$ifinfo['status'] = "down";
1622
	}
1623
	$ifinfo['macaddr'] = $ifinfotmp['macaddr'];
1624
	$ifinfo['mtu'] = $ifinfotmp['mtu'];
1625
	$ifinfo['ipaddr'] = $ifinfotmp['ipaddr'];
1626
	$ifinfo['subnet'] = $ifinfotmp['subnet'];
1627
	$ifinfo['linklocal'] = get_interface_linklocal($ifdescr);
1628
	$ifinfo['ipaddrv6'] = get_interface_ipv6($ifdescr);
1629
	$ifinfo['subnetv6'] = get_interface_subnetv6($ifdescr);
1630
	if (isset($ifinfotmp['link0'])) {
1631
		$link0 = "down";
1632
	}
1633
	$ifinfotmp = pfSense_get_interface_stats($chkif);
1634
	// $ifinfo['inpkts'] = $ifinfotmp['inpkts'];
1635
	// $ifinfo['outpkts'] = $ifinfotmp['outpkts'];
1636
	$ifinfo['inerrs'] = $ifinfotmp['inerrs'];
1637
	$ifinfo['outerrs'] = $ifinfotmp['outerrs'];
1638
	$ifinfo['collisions'] = $ifinfotmp['collisions'];
1639

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

    
1660
	/* Block */
1661
	$pf_in4_block = preg_split("/ +/", $pfctlstats[4]);
1662
	$pf_out4_block = preg_split("/ +/", $pfctlstats[6]);
1663
	$pf_in6_block = preg_split("/ +/", $pfctlstats[8]);
1664
	$pf_out6_block = preg_split("/ +/", $pfctlstats[10]);
1665
	$in4_block = $pf_in4_block[5];
1666
	$out4_block = $pf_out4_block[5];
1667
	$in4_block_packets = $pf_in4_block[3];
1668
	$out4_block_packets = $pf_out4_block[3];
1669
	$in6_block = $pf_in6_block[5];
1670
	$out6_block = $pf_out6_block[5];
1671
	$in6_block_packets = $pf_in6_block[3];
1672
	$out6_block_packets = $pf_out6_block[3];
1673
	$ifinfo['inbytesblock'] = $in4_block + $in6_block;
1674
	$ifinfo['outbytesblock'] = $out4_block + $out6_block;
1675
	$ifinfo['inpktsblock'] = $in4_block_packets + $in6_block_packets;
1676
	$ifinfo['outpktsblock'] = $out4_block_packets + $out6_block_packets;
1677

    
1678
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1679
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1680
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1681
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1682

    
1683
	$ifconfiginfo = "";
1684
	$link_type = $config['interfaces'][$ifdescr]['ipaddr'];
1685
	switch ($link_type) {
1686
		/* DHCP? -> see if dhclient is up */
1687
		case "dhcp":
1688
			/* see if dhclient is up */
1689
			if (find_dhclient_process($ifinfo['if']) != 0) {
1690
				$ifinfo['dhcplink'] = "up";
1691
			} else {
1692
				$ifinfo['dhcplink'] = "down";
1693
			}
1694

    
1695
			break;
1696
		/* PPPoE/PPTP/L2TP interface? -> get status from virtual interface */
1697
		case "pppoe":
1698
		case "pptp":
1699
		case "l2tp":
1700
			if ($ifinfo['status'] == "up" && !isset($link0)) {
1701
				/* get PPPoE link status for dial on demand */
1702
				$ifinfo["{$link_type}link"] = "up";
1703
			} else {
1704
				$ifinfo["{$link_type}link"] = "down";
1705
			}
1706

    
1707
			break;
1708
		/* PPP interface? -> get uptime for this session and cumulative uptime from the persistent log file in conf */
1709
		case "ppp":
1710
			if ($ifinfo['status'] == "up") {
1711
				$ifinfo['ppplink'] = "up";
1712
			} else {
1713
				$ifinfo['ppplink'] = "down" ;
1714
			}
1715

    
1716
			if (empty($ifinfo['status'])) {
1717
				$ifinfo['status'] = "down";
1718
			}
1719

    
1720
			if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) {
1721
				foreach ($config['ppps']['ppp'] as $pppid => $ppp) {
1722
					if ($config['interfaces'][$ifdescr]['if'] == $ppp['if']) {
1723
						break;
1724
					}
1725
				}
1726
			}
1727
			$dev = $ppp['ports'];
1728
			if ($config['interfaces'][$ifdescr]['if'] != $ppp['if'] || empty($dev)) {
1729
				break;
1730
			}
1731
			if (!file_exists($dev)) {
1732
				$ifinfo['nodevice'] = 1;
1733
				$ifinfo['pppinfo'] = $dev . " " . gettext("device not present! Is the modem attached to the system?");
1734
			}
1735

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

    
1771
	if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) {
1772
		$sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`);
1773
		$ifinfo['ppp_uptime'] = convert_seconds_to_dhms($sec);
1774
	}
1775

    
1776
	if ($ifinfo['status'] == "up") {
1777
		/* try to determine media with ifconfig */
1778
		unset($ifconfiginfo);
1779
		exec("/sbin/ifconfig " . $ifinfo['if'], $ifconfiginfo);
1780
		$wifconfiginfo = array();
1781
		if (is_interface_wireless($ifdescr)) {
1782
			exec("/sbin/ifconfig {$ifinfo['if']} list sta", $wifconfiginfo);
1783
			array_shift($wifconfiginfo);
1784
		}
1785
		$matches = "";
1786
		foreach ($ifconfiginfo as $ici) {
1787

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

    
1798
			if (preg_match("/status: (.*)$/", $ici, $matches)) {
1799
				if ($matches[1] != "active") {
1800
					$ifinfo['status'] = $matches[1];
1801
				}
1802
				if ($ifinfo['status'] == gettext("running")) {
1803
					$ifinfo['status'] = gettext("up");
1804
				}
1805
			}
1806
			if (preg_match("/channel (\S*)/", $ici, $matches)) {
1807
				$ifinfo['channel'] = $matches[1];
1808
			}
1809
			if (preg_match("/ssid (\".*?\"|\S*)/", $ici, $matches)) {
1810
				if ($matches[1][0] == '"') {
1811
					$ifinfo['ssid'] = substr($matches[1], 1, -1);
1812
				}
1813
				else {
1814
					$ifinfo['ssid'] = $matches[1];
1815
				}
1816
			}
1817
			if (preg_match("/laggproto (.*)$/", $ici, $matches)) {
1818
				$ifinfo['laggproto'] = $matches[1];
1819
			}
1820
			if (preg_match("/laggport: (.*)$/", $ici, $matches)) {
1821
				$ifinfo['laggport'][] = $matches[1];
1822
			}
1823
		}
1824
		foreach ($wifconfiginfo as $ici) {
1825
			$elements = preg_split("/[ ]+/i", $ici);
1826
			if ($elements[0] != "") {
1827
				$ifinfo['bssid'] = $elements[0];
1828
			}
1829
			if ($elements[3] != "") {
1830
				$ifinfo['rate'] = $elements[3];
1831
			}
1832
			if ($elements[4] != "") {
1833
				$ifinfo['rssi'] = $elements[4];
1834
			}
1835
		}
1836
		/* lookup the gateway */
1837
		if (interface_has_gateway($ifdescr)) {
1838
			$ifinfo['gateway'] = get_interface_gateway($ifdescr);
1839
			$ifinfo['gatewayv6'] = get_interface_gateway_v6($ifdescr);
1840
		}
1841
	}
1842

    
1843
	$bridge = "";
1844
	$bridge = link_interface_to_bridge($ifdescr);
1845
	if ($bridge) {
1846
		$bridge_text = `/sbin/ifconfig {$bridge}`;
1847
		if (stristr($bridge_text, "blocking") <> false) {
1848
			$ifinfo['bridge'] = "<b><font color='red'>" . gettext("blocking") . "</font></b> - " . gettext("check for ethernet loops");
1849
			$ifinfo['bridgeint'] = $bridge;
1850
		} else if (stristr($bridge_text, "learning") <> false) {
1851
			$ifinfo['bridge'] = gettext("learning");
1852
			$ifinfo['bridgeint'] = $bridge;
1853
		} else if (stristr($bridge_text, "forwarding") <> false) {
1854
			$ifinfo['bridge'] = gettext("forwarding");
1855
			$ifinfo['bridgeint'] = $bridge;
1856
		}
1857
	}
1858

    
1859
	return $ifinfo;
1860
}
1861

    
1862
//returns cpu speed of processor. Good for determining capabilities of machine
1863
function get_cpu_speed() {
1864
	return get_single_sysctl("hw.clockrate");
1865
}
1866

    
1867
function get_uptime_sec() {
1868
	$boottime = "";
1869
	$matches = "";
1870
	$boottime = get_single_sysctl("kern.boottime");
1871
	preg_match("/sec = (\d+)/", $boottime, $matches);
1872
	$boottime = $matches[1];
1873
	if (intval($boottime) == 0) {
1874
		return 0;
1875
	}
1876

    
1877
	$uptime = time() - $boottime;
1878
	return $uptime;
1879
}
1880

    
1881
function resolve_host_addresses($host, $recordtypes = array(DNS_A, DNS_AAAA, DNS_CNAME), $index = true) {
1882
	$dnsresult = array();
1883
	$resolved = array();
1884
	$errreporting = error_reporting();
1885
	error_reporting($errreporting & ~E_WARNING);// dns_get_record throws a warning if nothing is resolved..
1886
	foreach ($recordtypes as $recordtype) {
1887
		$tmp = dns_get_record($host, $recordtype);
1888
		if (is_array($tmp)) {
1889
			$dnsresult = array_merge($dnsresult, $tmp);
1890
		}
1891
	}
1892
	error_reporting($errreporting);// restore original php warning/error settings.
1893
	foreach ($dnsresult as $item) {
1894
		$newitem = array();
1895
		$newitem['type'] = $item['type'];
1896
		switch ($item['type']) {
1897
			case 'CNAME':
1898
				$newitem['data'] = $item['target'];
1899
				$resolved[] = $newitem;
1900
				break;
1901
			case 'A':
1902
				$newitem['data'] = $item['ip'];
1903
				$resolved[] = $newitem;
1904
				break;
1905
			case 'AAAA':
1906
				$newitem['data'] = $item['ipv6'];
1907
				$resolved[] = $newitem;
1908
				break;
1909
		}
1910
	}
1911
	if ($index == false) {
1912
		foreach ($resolved as $res) {
1913
			$noind[] = $res['data'];
1914
		}
1915
		$resolved = $noind;
1916
	}
1917
	return $resolved;
1918
}
1919

    
1920
function add_hostname_to_watch($hostname) {
1921
	if (!is_dir("/var/db/dnscache")) {
1922
		mkdir("/var/db/dnscache");
1923
	}
1924
	$result = array();
1925
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1926
		$contents = "";
1927
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1928
		if (!empty($domips)) {
1929
			foreach ($domips as $ip) {
1930
				$contents .= "$ip\n";
1931
			}
1932
		}
1933
		file_put_contents("/var/db/dnscache/$hostname", $contents);
1934
		/* Remove empty elements */
1935
		$result = array_filter(explode("\n", $contents), 'strlen');
1936
	}
1937
	return $result;
1938
}
1939

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

    
1944
function pfsense_default_state_size() {
1945
	/* get system memory amount */
1946
	$memory = get_memory();
1947
	$physmem = $memory[0];
1948
	/* Be cautious and only allocate 10% of system memory to the state table */
1949
	$max_states = (int) ($physmem/10)*1000;
1950
	return $max_states;
1951
}
1952

    
1953
function pfsense_default_tables_size() {
1954
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1955
	return $current;
1956
}
1957

    
1958
function pfsense_default_table_entries_size() {
1959
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1960
	return (trim($current));
1961
}
1962

    
1963
/* Compare the current hostname DNS to the DNS cache we made
1964
 * if it has changed we return the old records
1965
 * if no change we return false */
1966
function compare_hostname_to_dnscache($hostname) {
1967
	if (!is_dir("/var/db/dnscache")) {
1968
		mkdir("/var/db/dnscache");
1969
	}
1970
	$hostname = trim($hostname);
1971
	if (is_readable("/var/db/dnscache/{$hostname}")) {
1972
		$oldcontents = file_get_contents("/var/db/dnscache/{$hostname}");
1973
	} else {
1974
		$oldcontents = "";
1975
	}
1976
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1977
		$contents = "";
1978
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1979
		if (!empty($domips)) {
1980
			foreach ($domips as $ip) {
1981
				$contents .= "$ip\n";
1982
			}
1983
		}
1984
	}
1985

    
1986
	if (trim($oldcontents) != trim($contents)) {
1987
		if ($g['debug']) {
1988
			log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents));
1989
		}
1990
		return ($oldcontents);
1991
	} else {
1992
		return false;
1993
	}
1994
}
1995

    
1996
/*
1997
 * load_crypto() - Load crypto modules if enabled in config.
1998
 */
1999
function load_crypto() {
2000
	global $config, $g;
2001
	$crypto_modules = array('aesni', 'cryptodev');
2002

    
2003
	$enabled_modules = explode('_', $config['system']['crypto_hardware']);
2004

    
2005
	foreach ($enabled_modules as $enmod) {
2006
		if (empty($enmod) || !in_array($enmod, $crypto_modules)) {
2007
			continue;
2008
		}
2009
		if (!is_module_loaded($enmod)) {
2010
			log_error(sprintf(gettext("Loading %s cryptographic accelerator module."), $enmod));
2011
			mute_kernel_msgs();
2012
			mwexec("/sbin/kldload " . escapeshellarg($enmod));
2013
			unmute_kernel_msgs();
2014
		}
2015
	}
2016
}
2017

    
2018
/*
2019
 * load_thermal_hardware() - Load temperature monitor kernel module
2020
 */
2021
function load_thermal_hardware() {
2022
	global $config, $g;
2023
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
2024

    
2025
	if (!in_array($config['system']['thermal_hardware'], $thermal_hardware_modules)) {
2026
		return false;
2027
	}
2028

    
2029
	if (!empty($config['system']['thermal_hardware']) && !is_module_loaded($config['system']['thermal_hardware'])) {
2030
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $config['system']['thermal_hardware']));
2031
		mwexec("/sbin/kldload {$config['system']['thermal_hardware']}");
2032
	}
2033
}
2034

    
2035
/****f* pfsense-utils/isvm
2036
 * NAME
2037
 *   isvm
2038
 * INPUTS
2039
 *	none
2040
 * RESULT
2041
 *   returns true if machine is running under a virtual environment
2042
 ******/
2043
function isvm() {
2044
	$virtualenvs = array("vmware", "parallels", "qemu", "bochs", "plex86", "VirtualBox");
2045
	$_gb = exec('/bin/kenv -q smbios.system.product 2>/dev/null', $output, $rc);
2046

    
2047
	if ($rc != 0 || !isset($output[0])) {
2048
		return false;
2049
	}
2050

    
2051
	foreach ($virtualenvs as $virtualenv) {
2052
		if (stripos($output[0], $virtualenv) !== false) {
2053
			return true;
2054
		}
2055
	}
2056

    
2057
	return false;
2058
}
2059

    
2060
function get_freebsd_version() {
2061
	$version = explode(".", php_uname("r"));
2062
	return $version[0];
2063
}
2064

    
2065
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
2066
	global $config, $g;
2067

    
2068
	$fp = fopen($destination, "wb");
2069

    
2070
	if (!$fp) {
2071
		return false;
2072
	}
2073

    
2074
	$ch = curl_init();
2075
	curl_setopt($ch, CURLOPT_URL, $url);
2076
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify_ssl);
2077
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify_ssl);
2078
	curl_setopt($ch, CURLOPT_FILE, $fp);
2079
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2080
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2081
	curl_setopt($ch, CURLOPT_HEADER, false);
2082
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2083
	if (!isset($config['system']['do_not_send_uniqueid'])) {
2084
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2085
	} else {
2086
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
2087
	}
2088

    
2089
	if (!empty($config['system']['proxyurl'])) {
2090
		curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']);
2091
		if (!empty($config['system']['proxyport'])) {
2092
			curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']);
2093
		}
2094
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
2095
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
2096
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}");
2097
		}
2098
	}
2099

    
2100
	@curl_exec($ch);
2101
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2102
	fclose($fp);
2103
	curl_close($ch);
2104
	if ($http_code == 200) {
2105
		return true;
2106
	} else {
2107
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2108
		unlink_if_exists($destination);
2109
		return false;
2110
	}
2111
}
2112

    
2113
function download_file_with_progress_bar($url, $destination, $verify_ssl = true, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) {
2114
	global $config, $g;
2115
	global $ch, $fout, $file_size, $downloaded, $config, $first_progress_update;
2116
	$file_size = 1;
2117
	$downloaded = 1;
2118
	$first_progress_update = TRUE;
2119
	/* open destination file */
2120
	$fout = fopen($destination, "wb");
2121

    
2122
	if (!$fout) {
2123
		return false;
2124
	}
2125
	/*
2126
	 *      Originally by Author: Keyvan Minoukadeh
2127
	 *      Modified by Scott Ullrich to return Content-Length size
2128
	 */
2129
	$ch = curl_init();
2130
	curl_setopt($ch, CURLOPT_URL, $url);
2131
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify_ssl);
2132
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify_ssl);
2133
	curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
2134
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2135
	curl_setopt($ch, CURLOPT_WRITEFUNCTION, $readbody);
2136
	curl_setopt($ch, CURLOPT_NOPROGRESS, '1');
2137
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2138
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2139
	if (!isset($config['system']['do_not_send_uniqueid'])) {
2140
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2141
	} else {
2142
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
2143
	}
2144

    
2145
	if (!empty($config['system']['proxyurl'])) {
2146
		curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']);
2147
		if (!empty($config['system']['proxyport'])) {
2148
			curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']);
2149
		}
2150
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
2151
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
2152
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}");
2153
		}
2154
	}
2155

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

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

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

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

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

    
2259
	if ($pkg_interface == "console") {
2260
		print ("{$status}");
2261
	}
2262

    
2263
	/* ensure that contents are written out */
2264
	ob_flush();
2265
}
2266

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

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

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

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

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

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

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

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

    
2365
	if ($debug) {
2366
		fclose($fd);
2367
	}
2368

    
2369
}
2370

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

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

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

    
2451
function update_alias_url_data() {
2452
	global $config, $g;
2453

    
2454
	$updated = false;
2455

    
2456
	/* item is a url type */
2457
	$lockkey = lock('aliasurl');
2458
	if (is_array($config['aliases']['alias'])) {
2459
		foreach ($config['aliases']['alias'] as $x => $alias) {
2460
			if (empty($alias['aliasurl'])) {
2461
				continue;
2462
			}
2463

    
2464
			$address = null;
2465
			foreach ($alias['aliasurl'] as $alias_url) {
2466
				/* fetch down and add in */
2467
				$temp_filename = tempnam("{$g['tmp_path']}/", "alias_import");
2468
				unlink($temp_filename);
2469
				$verify_ssl = isset($config['system']['checkaliasesurlcert']);
2470
				mkdir($temp_filename);
2471
				if (!download_file($alias_url, $temp_filename . "/aliases", $verify_ssl)) {
2472
					log_error(sprintf(gettext("Failed to download alias %s"), $alias_url));
2473
					continue;
2474
				}
2475

    
2476
				/* if the item is tar gzipped then extract */
2477
				if (stripos($alias_url, '.tgz')) {
2478
					if (!process_alias_tgz($temp_filename)) {
2479
						continue;
2480
					}
2481
				}
2482
				if (file_exists("{$temp_filename}/aliases")) {
2483
					$address = parse_aliases_file("{$temp_filename}/aliases", $alias['type'], 5000);
2484
					mwexec("/bin/rm -rf {$temp_filename}");
2485
				}
2486
			}
2487
			if ($address != null) {
2488
				$config['aliases']['alias'][$x]['address'] = implode(" ", $address);
2489
				$updated = true;
2490
			}
2491
		}
2492
	}
2493
	unlock($lockkey);
2494

    
2495
	/* Report status to callers as well */
2496
	return $updated;
2497
}
2498

    
2499
function process_alias_tgz($temp_filename) {
2500
	if (!file_exists('/usr/bin/tar')) {
2501
		log_error(gettext("Alias archive is a .tar/tgz file which cannot be decompressed because utility is missing!"));
2502
		return false;
2503
	}
2504
	rename("{$temp_filename}/aliases", "{$temp_filename}/aliases.tgz");
2505
	mwexec("/usr/bin/tar xzf {$temp_filename}/aliases.tgz -C {$temp_filename}/aliases/");
2506
	unlink("{$temp_filename}/aliases.tgz");
2507
	$files_to_process = return_dir_as_array("{$temp_filename}/");
2508
	/* foreach through all extracted files and build up aliases file */
2509
	$fd = @fopen("{$temp_filename}/aliases", "w");
2510
	if (!$fd) {
2511
		log_error(sprintf(gettext("Could not open %s/aliases for writing!"), $temp_filename));
2512
		return false;
2513
	}
2514
	foreach ($files_to_process as $f2p) {
2515
		$tmpfd = @fopen($f2p, 'r');
2516
		if (!$tmpfd) {
2517
			log_error(sprintf(gettext('The following file could not be read %1$s from %2$s'), $f2p, $temp_filename));
2518
			continue;
2519
		}
2520
		while (($tmpbuf = fread($tmpfd, 65536)) !== FALSE) {
2521
			fwrite($fd, $tmpbuf);
2522
		}
2523
		fclose($tmpfd);
2524
		unlink($f2p);
2525
	}
2526
	fclose($fd);
2527
	unset($tmpbuf);
2528

    
2529
	return true;
2530
}
2531

    
2532
function version_compare_dates($a, $b) {
2533
	$a_time = strtotime($a);
2534
	$b_time = strtotime($b);
2535

    
2536
	if ((!$a_time) || (!$b_time)) {
2537
		return FALSE;
2538
	} else {
2539
		if ($a_time < $b_time) {
2540
			return -1;
2541
		} elseif ($a_time == $b_time) {
2542
			return 0;
2543
		} else {
2544
			return 1;
2545
		}
2546
	}
2547
}
2548
function version_get_string_value($a) {
2549
	$strs = array(
2550
		0 => "ALPHA-ALPHA",
2551
		2 => "ALPHA",
2552
		3 => "BETA",
2553
		4 => "B",
2554
		5 => "C",
2555
		6 => "D",
2556
		7 => "RC",
2557
		8 => "RELEASE",
2558
		9 => "*"			// Matches all release levels
2559
	);
2560
	$major = 0;
2561
	$minor = 0;
2562
	foreach ($strs as $num => $str) {
2563
		if (substr($a, 0, strlen($str)) == $str) {
2564
			$major = $num;
2565
			$n = substr($a, strlen($str));
2566
			if (is_numeric($n)) {
2567
				$minor = $n;
2568
			}
2569
			break;
2570
		}
2571
	}
2572
	return "{$major}.{$minor}";
2573
}
2574
function version_compare_string($a, $b) {
2575
	// Only compare string parts if both versions give a specific release
2576
	// (If either version lacks a string part, assume intended to match all release levels)
2577
	if (isset($a) && isset($b)) {
2578
		return version_compare_numeric(version_get_string_value($a), version_get_string_value($b));
2579
	} else {
2580
		return 0;
2581
	}
2582
}
2583
function version_compare_numeric($a, $b) {
2584
	$a_arr = explode('.', rtrim($a, '.'));
2585
	$b_arr = explode('.', rtrim($b, '.'));
2586

    
2587
	foreach ($a_arr as $n => $val) {
2588
		if (array_key_exists($n, $b_arr)) {
2589
			// So far so good, both have values at this minor version level. Compare.
2590
			if ($val > $b_arr[$n]) {
2591
				return 1;
2592
			} elseif ($val < $b_arr[$n]) {
2593
				return -1;
2594
			}
2595
		} else {
2596
			// a is greater, since b doesn't have any minor version here.
2597
			return 1;
2598
		}
2599
	}
2600
	if (count($b_arr) > count($a_arr)) {
2601
		// b is longer than a, so it must be greater.
2602
		return -1;
2603
	} else {
2604
		// Both a and b are of equal length and value.
2605
		return 0;
2606
	}
2607
}
2608
function pfs_version_compare($cur_time, $cur_text, $remote) {
2609
	// First try date compare
2610
	$v = version_compare_dates($cur_time, $remote);
2611
	if ($v === FALSE) {
2612
		// If that fails, try to compare by string
2613
		// Before anything else, simply test if the strings are equal
2614
		if (($cur_text == $remote) || ($cur_time == $remote)) {
2615
			return 0;
2616
		}
2617
		list($cur_num, $cur_str) = explode('-', $cur_text);
2618
		list($rem_num, $rem_str) = explode('-', $remote);
2619

    
2620
		// First try to compare the numeric parts of the version string.
2621
		$v = version_compare_numeric($cur_num, $rem_num);
2622

    
2623
		// If the numeric parts are the same, compare the string parts.
2624
		if ($v == 0) {
2625
			return version_compare_string($cur_str, $rem_str);
2626
		}
2627
	}
2628
	return $v;
2629
}
2630
function process_alias_urltable($name, $type, $url, $freq, $forceupdate=false, $validateonly=false) {
2631
	global $g, $config;
2632

    
2633
	$urltable_prefix = "/var/db/aliastables/";
2634
	$urltable_filename = $urltable_prefix . $name . ".txt";
2635
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2636

    
2637
	// Make the aliases directory if it doesn't exist
2638
	if (!file_exists($urltable_prefix)) {
2639
		mkdir($urltable_prefix);
2640
	} elseif (!is_dir($urltable_prefix)) {
2641
		unlink($urltable_prefix);
2642
		mkdir($urltable_prefix);
2643
	}
2644

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

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

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

    
2659
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2660
			if ($type == "urltable_ports") {
2661
				$parsed_contents = group_ports($parsed_contents, true);
2662
			}
2663
			if (is_array($parsed_contents)) {
2664
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2665
			} else {
2666
				touch($urltable_filename);
2667
			}
2668

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

    
2675
			unlink_if_exists($tmp_urltable_filename);
2676
		} else {
2677
			if (!$validateonly) {
2678
				touch($urltable_filename);
2679
			}
2680
			return false;
2681
		}
2682
		return true;
2683
	} else {
2684
		// File exists, and it doesn't need to be updated.
2685
		return -1;
2686
	}
2687
}
2688

    
2689
function get_include_contents($filename) {
2690
	if (is_file($filename)) {
2691
		ob_start();
2692
		include $filename;
2693
		$contents = ob_get_contents();
2694
		ob_end_clean();
2695
		return $contents;
2696
	}
2697
	return false;
2698
}
2699

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

    
2812
function get_country_name($country_code = "ALL") {
2813
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2814
		return "";
2815
	}
2816

    
2817
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2818
	$country_names_contents = file_get_contents($country_names_xml);
2819
	$country_names = xml2array($country_names_contents);
2820

    
2821
	if ($country_code == "ALL") {
2822
		$country_list = array();
2823
		foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2824
			$country_list[] = array(
2825
				"code" => $country['ISO_3166-1_Alpha-2_Code_element'],
2826
				"name" => ucwords(strtolower($country['ISO_3166-1_Country_name'])));
2827
		}
2828
		return $country_list;
2829
	}
2830

    
2831
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2832
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2833
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2834
		}
2835
	}
2836
	return "";
2837
}
2838

    
2839
/* Return the list of country codes to be used on CAs and certs */
2840
function get_cert_country_codes() {
2841
	$countries = get_country_name();
2842

    
2843
	$country_codes = array();
2844
	foreach ($countries as $country) {
2845
		$country_codes[$country['code']] = $country['code'];
2846
	}
2847
	ksort($country_codes);
2848

    
2849
	/* Preserve historical order: None, US, CA, other countries */
2850
	$first_items[''] = gettext("None");
2851
	$first_items['US'] = $country_codes['US'];
2852
	$first_items['CA'] = $country_codes['CA'];
2853
	unset($country_codes['US']);
2854
	unset($country_codes['CA']);
2855

    
2856
	return array_merge($first_items, $country_codes);
2857
}
2858

    
2859
/* sort by interface only, retain the original order of rules that apply to
2860
   the same interface */
2861
function filter_rules_sort() {
2862
	global $config;
2863

    
2864
	init_config_arr(array('filter', 'rule'));
2865
	/* mark each rule with the sequence number (to retain the order while sorting) */
2866
	for ($i = 0; isset($config['filter']['rule'][$i]); $i++) {
2867
		if (!is_array($config['filter']['rule'][$i])) {
2868
			$config['filter']['rule'][$i] = array();
2869
		}
2870
		$config['filter']['rule'][$i]['seq'] = $i;
2871
	}
2872

    
2873
	usort($config['filter']['rule'], "filter_rules_compare");
2874

    
2875
	/* strip the sequence numbers again */
2876
	for ($i = 0; isset($config['filter']['rule'][$i]); $i++) {
2877
		unset($config['filter']['rule'][$i]['seq']);
2878
	}
2879
}
2880
function filter_rules_compare($a, $b) {
2881
	if (isset($a['floating']) && isset($b['floating'])) {
2882
		return $a['seq'] - $b['seq'];
2883
	} else if (isset($a['floating'])) {
2884
		return -1;
2885
	} else if (isset($b['floating'])) {
2886
		return 1;
2887
	} else if ($a['interface'] == $b['interface']) {
2888
		return $a['seq'] - $b['seq'];
2889
	} else {
2890
		return compare_interface_friendly_names($a['interface'], $b['interface']);
2891
	}
2892
}
2893

    
2894
function generate_ipv6_from_mac($mac) {
2895
	$elements = explode(":", $mac);
2896
	if (count($elements) <> 6) {
2897
		return false;
2898
	}
2899

    
2900
	$i = 0;
2901
	$ipv6 = "fe80::";
2902
	foreach ($elements as $byte) {
2903
		if ($i == 0) {
2904
			$hexadecimal = substr($byte, 1, 2);
2905
			$bitmap = base_convert($hexadecimal, 16, 2);
2906
			$bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT);
2907
			$bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3, 4);
2908
			$byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16);
2909
		}
2910
		$ipv6 .= $byte;
2911
		if ($i == 1) {
2912
			$ipv6 .= ":";
2913
		}
2914
		if ($i == 3) {
2915
			$ipv6 .= ":";
2916
		}
2917
		if ($i == 2) {
2918
			$ipv6 .= "ff:fe";
2919
		}
2920

    
2921
		$i++;
2922
	}
2923
	return $ipv6;
2924
}
2925

    
2926
/****f* pfsense-utils/load_mac_manufacturer_table
2927
 * NAME
2928
 *   load_mac_manufacturer_table
2929
 * INPUTS
2930
 *   none
2931
 * RESULT
2932
 *   returns associative array with MAC-Manufacturer pairs
2933
 ******/
2934
function load_mac_manufacturer_table() {
2935
	/* load MAC-Manufacture data from the file */
2936
	$macs = false;
2937
	if (file_exists("/usr/local/share/nmap/nmap-mac-prefixes")) {
2938
		$macs=file("/usr/local/share/nmap/nmap-mac-prefixes");
2939
	}
2940
	if ($macs) {
2941
		foreach ($macs as $line) {
2942
			if (preg_match('/([0-9A-Fa-f]{6}) (.*)$/', $line, $matches)) {
2943
				/* store values like this $mac_man['000C29']='VMware' */
2944
				$mac_man["$matches[1]"] = $matches[2];
2945
			}
2946
		}
2947
		return $mac_man;
2948
	} else {
2949
		return -1;
2950
	}
2951

    
2952
}
2953

    
2954
/****f* pfsense-utils/is_ipaddr_configured
2955
 * NAME
2956
 *   is_ipaddr_configured
2957
 * INPUTS
2958
 *   IP Address to check.
2959
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2960
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2961
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2962
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2963
 *     If check_subnets is true and cidrprefix is specified,
2964
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2965
 * RESULT
2966
 *   returns true if the IP Address is configured and present on this device or overlaps a configured subnet.
2967
*/
2968
function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2969
	if (count(where_is_ipaddr_configured($ipaddr, $ignore_if, $check_localip, $check_subnets, $cidrprefix))) {
2970
		return true;
2971
	}
2972
	return false;
2973
}
2974

    
2975
/****f* pfsense-utils/where_is_ipaddr_configured
2976
 * NAME
2977
 *   where_is_ipaddr_configured
2978
 * INPUTS
2979
 *   IP Address to check.
2980
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2981
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2982
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2983
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2984
 *     If check_subnets is true and cidrprefix is specified,
2985
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2986
 * RESULT
2987
 *   Returns an array of the interfaces 'if' plus IP address or subnet 'ip_or_subnet' that match or overlap the IP address to check.
2988
 *   If there are no matches then an empty array is returned.
2989
*/
2990
function where_is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2991
	global $config;
2992

    
2993
	$where_configured = array();
2994

    
2995
	$pos = strpos($ignore_if, '_virtualip');
2996
	if ($pos !== false) {
2997
		$ignore_vip_id = substr($ignore_if, $pos+10);
2998
		$ignore_vip_if = substr($ignore_if, 0, $pos);
2999
	} else {
3000
		$ignore_vip_id = -1;
3001
		$ignore_vip_if = $ignore_if;
3002
	}
3003

    
3004
	$isipv6 = is_ipaddrv6($ipaddr);
3005

    
3006
	if ($isipv6) {
3007
		$ipaddr = text_to_compressed_ip6($ipaddr);
3008
	}
3009

    
3010
	if ($check_subnets) {
3011
		$cidrprefix = intval($cidrprefix);
3012
		if ($isipv6) {
3013
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
3014
				$cidrprefix = 128;
3015
			}
3016
		} else {
3017
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
3018
				$cidrprefix = 32;
3019
			}
3020
		}
3021
		$iflist = get_configured_interface_list();
3022
		foreach ($iflist as $if => $ifname) {
3023
			if ($ignore_if == $if) {
3024
				continue;
3025
			}
3026

    
3027
			if ($isipv6) {
3028
				$if_ipv6 = get_interface_ipv6($if);
3029
				$if_snbitsv6 = get_interface_subnetv6($if);
3030
				if ($if_ipv6 && $if_snbitsv6 && check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) {
3031
					$where_entry = array();
3032
					$where_entry['if'] = $if;
3033
					$where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if);
3034
					$where_configured[] = $where_entry;
3035
				}
3036
			} else {
3037
				$if_ipv4 = get_interface_ip($if);
3038
				$if_snbitsv4 = get_interface_subnet($if);
3039
				if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) {
3040
					$where_entry = array();
3041
					$where_entry['if'] = $if;
3042
					$where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if);
3043
					$where_configured[] = $where_entry;
3044
				}
3045
			}
3046
		}
3047
	} else {
3048
		if ($isipv6) {
3049
			$interface_list_ips = get_configured_ipv6_addresses();
3050
		} else {
3051
			$interface_list_ips = get_configured_ip_addresses();
3052
		}
3053

    
3054
		foreach ($interface_list_ips as $if => $ilips) {
3055
			if ($ignore_if == $if) {
3056
				continue;
3057
			}
3058
			if (strcasecmp($ipaddr, $ilips) == 0) {
3059
				$where_entry = array();
3060
				$where_entry['if'] = $if;
3061
				$where_entry['ip_or_subnet'] = $ilips;
3062
				$where_configured[] = $where_entry;
3063
			}
3064
		}
3065
	}
3066

    
3067
	if ($check_localip) {
3068
		if (!is_array($config['l2tp']) && !empty($config['l2tp']['localip']) && (strcasecmp($ipaddr, text_to_compressed_ip6($config['l2tp']['localip'])) == 0)) {
3069
			$where_entry = array();
3070
			$where_entry['if'] = 'l2tp';
3071
			$where_entry['ip_or_subnet'] = $config['l2tp']['localip'];
3072
			$where_configured[] = $where_entry;
3073
		}
3074
	}
3075

    
3076
	return $where_configured;
3077
}
3078

    
3079
/****f* pfsense-utils/pfSense_handle_custom_code
3080
 * NAME
3081
 *   pfSense_handle_custom_code
3082
 * INPUTS
3083
 *   directory name to process
3084
 * RESULT
3085
 *   globs the directory and includes the files
3086
 */
3087
function pfSense_handle_custom_code($src_dir) {
3088
	// Allow extending of the nat edit page and include custom input validation
3089
	if (is_dir("$src_dir")) {
3090
		$cf = glob($src_dir . "/*.inc");
3091
		foreach ($cf as $nf) {
3092
			if ($nf == "." || $nf == "..") {
3093
				continue;
3094
			}
3095
			// Include the extra handler
3096
			include_once("$nf");
3097
		}
3098
	}
3099
}
3100

    
3101
function set_language() {
3102
	global $config, $g;
3103

    
3104
	if (!empty($config['system']['language'])) {
3105
		$lang = $config['system']['language'];
3106
	} elseif (!empty($g['language'])) {
3107
		$lang = $g['language'];
3108
	}
3109
	$lang .= ".UTF-8";
3110

    
3111
	putenv("LANG={$lang}");
3112
	setlocale(LC_ALL, $lang);
3113
	textdomain("pfSense");
3114
	bindtextdomain("pfSense", "/usr/local/share/locale");
3115
	bind_textdomain_codeset("pfSense", $lang);
3116
}
3117

    
3118
function get_locale_list() {
3119
	$locales = array(
3120
		"bs" => gettext("Bosnian"),
3121
		"zh_CN" => gettext("Chinese"),
3122
		"zh_Hans_CN" => gettext("Chinese (Simplified, China)"),
3123
		"zh_Hans_HK" => gettext("Chinese (Simplified, Hong Kong SAR China)"),
3124
		"zh_Hant_TW" => gettext("Chinese (Traditional, Taiwan)"),
3125
		"nl" => gettext("Dutch"),
3126
		"en_US" => gettext("English"),
3127
		"fr" => gettext("French"),
3128
		"de_DE" => gettext("German (Germany)"),
3129
		"it" => gettext("Italian"),
3130
		"ko" => gettext("Korean"),
3131
		"nb" => gettext("Norwegian Bokmål"),
3132
		"pl" => gettext("Polish"),
3133
		"pt_PT" => gettext("Portuguese"),
3134
		"pt_BR" => gettext("Portuguese (Brazil)"),
3135
		"ru" => gettext("Russian"),
3136
		"es" => gettext("Spanish"),
3137
		"es_AR" => gettext("Spanish (Argentina)"),
3138
	);
3139

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

    
3144
	//asort($locales);
3145

    
3146
	return $locales;
3147
}
3148

    
3149
function return_hex_ipv4($ipv4) {
3150
	if (!is_ipaddrv4($ipv4)) {
3151
		return(false);
3152
	}
3153

    
3154
	/* we need the hex form of the interface IPv4 address */
3155
	$ip4arr = explode(".", $ipv4);
3156
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3157
}
3158

    
3159
function convert_ipv6_to_128bit($ipv6) {
3160
	if (!is_ipaddrv6($ipv6)) {
3161
		return(false);
3162
	}
3163

    
3164
	$ip6arr = array();
3165
	$ip6prefix = Net_IPv6::uncompress($ipv6);
3166
	$ip6arr = explode(":", $ip6prefix);
3167
	/* binary presentation of the prefix for all 128 bits. */
3168
	$ip6prefixbin = "";
3169
	foreach ($ip6arr as $element) {
3170
		$ip6prefixbin .= sprintf("%016b", hexdec($element));
3171
	}
3172
	return($ip6prefixbin);
3173
}
3174

    
3175
function convert_128bit_to_ipv6($ip6bin) {
3176
	if (strlen($ip6bin) <> 128) {
3177
		return(false);
3178
	}
3179

    
3180
	$ip6arr = array();
3181
	$ip6binarr = array();
3182
	$ip6binarr = str_split($ip6bin, 16);
3183
	foreach ($ip6binarr as $binpart) {
3184
		$ip6arr[] = dechex(bindec($binpart));
3185
	}
3186
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3187

    
3188
	return($ip6addr);
3189
}
3190

    
3191

    
3192
/* Returns the calculated bit length of the prefix delegation from the WAN interface */
3193
/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */
3194
/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */
3195
/* 6to4 is 16 bits, e.g. 65535 */
3196
function calculate_ipv6_delegation_length($if) {
3197
	global $config;
3198

    
3199
	if (!is_array($config['interfaces'][$if])) {
3200
		return false;
3201
	}
3202

    
3203
	switch ($config['interfaces'][$if]['ipaddrv6']) {
3204
		case "6to4":
3205
			$pdlen = 16;
3206
			break;
3207
		case "6rd":
3208
			$rd6cfg = $config['interfaces'][$if];
3209
			$rd6plen = explode("/", $rd6cfg['prefix-6rd']);
3210
			$pdlen = (64 - ((int) $rd6plen[1] + (32 - (int) $rd6cfg['prefix-6rd-v4plen'])));
3211
			break;
3212
		case "dhcp6":
3213
			$dhcp6cfg = $config['interfaces'][$if];
3214
			$pdlen = $dhcp6cfg['dhcp6-ia-pd-len'];
3215
			break;
3216
		default:
3217
			$pdlen = 0;
3218
			break;
3219
	}
3220
	return($pdlen);
3221
}
3222

    
3223
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3224
	$prefix = Net_IPv6::uncompress($prefix, true);
3225
	$suffix = Net_IPv6::uncompress($suffix, true);
3226

    
3227
	/*
3228
	 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
3229
	 *                ^^^^ ^
3230
	 *                |||| \-> 64
3231
	 *                |||\---> 63, 62, 61, 60
3232
	 *                ||\----> 56
3233
	 *                |\-----> 52
3234
	 *                \------> 48
3235
	 */
3236

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

    
3268
	return text_to_compressed_ip6(substr($prefix, 0, $prefix_len) .
3269
	    substr($suffix, $prefix_len));
3270
}
3271

    
3272
function dhcpv6_pd_str_help($pdlen) {
3273
	$result = '';
3274

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

    
3307
	return $result;
3308
}
3309

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

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

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

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

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

    
3368
function zte_rssi_to_string($rssi) {
3369
	return huawei_rssi_to_string($rssi);
3370
}
3371

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

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

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

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

    
3407
function get_configured_pppoe_server_interfaces() {
3408
	global $config;
3409
	$iflist = array();
3410
	if (is_array($config['pppoes']['pppoe'])) {
3411
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
3412
			if ($pppoe['mode'] == "server") {
3413
				$int = "poes". $pppoe['pppoeid'];
3414
				$iflist[$int] = strtoupper($int);
3415
			}
3416
		}
3417
	}
3418
	return $iflist;
3419
}
3420

    
3421
function get_pppoes_child_interfaces($ifpattern) {
3422
	$if_arr = array();
3423
	if ($ifpattern == "") {
3424
		return;
3425
	}
3426

    
3427
	exec("/sbin/ifconfig", $out, $ret);
3428
	foreach ($out as $line) {
3429
		if (preg_match("/^({$ifpattern}-[0-9]+):/i", $line, $match)) {
3430
			$if_arr[] = $match[1];
3431
		}
3432
	}
3433
	return $if_arr;
3434

    
3435
}
3436

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

    
3473
// Convert IPv6 addresses to lower case
3474
function addrtolower($ip) {
3475
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3476
		return(strtolower($ip));
3477
	} else {
3478
		return($ip);
3479
	}
3480
}
3481

    
3482
function compare_by_name($a, $b) {
3483
	return strcasecmp($a['name'], $b['name']);
3484
}
3485

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

    
3517
/****f* pfsense-utils/send_download_data
3518
 * NAME
3519
 *   send_download_data - Send content to a user's browser as a file to download
3520
 * INPUTS
3521
 *   $type        : The type of download, either 'data' to send the contents of
3522
 *                    a variable or 'file' to send the contents of a file on the
3523
 *                    filesystem.
3524
 *   $content     : For 'data' type, the content to send the user. For 'file'
3525
 *                    type, the full path to the file to send.
3526
 *   $userfilename: The filename presented to the user when downloading. For
3527
 *                    'file' type, this may be omitted and the basename of
3528
 *                    $content will be used instead.
3529
 *   $contenttype : MIME content type of the data. Default "application/octet-stream"
3530
 * RESULT
3531
 *   Sends the data to the browser as a file to download.
3532
 ******/
3533

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

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

    
3541
	/* Cannot determine the filename, so bail. */
3542
	if (empty($name)) {
3543
		exit;
3544
	}
3545

    
3546
	/* Send basic download headers */
3547
	header("Content-Type: {$contenttype}");
3548
	header("Content-Length: {$size}");
3549
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3550

    
3551
	/* Send cache headers */
3552
	if (isset($_SERVER['HTTPS'])) {
3553
		header('Pragma: ');
3554
		header('Cache-Control: ');
3555
	} else {
3556
		header("Pragma: private");
3557
		header("Cache-Control: private, must-revalidate");
3558
	}
3559

    
3560
	/* Ensure output buffering is off so PHP does not consume
3561
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3562
	while (ob_get_level()) {
3563
		@ob_end_clean();
3564
	}
3565

    
3566
	/* Send the data to the user */
3567
	if ($type == 'file') {
3568
		readfile($content);
3569
	} else {
3570
		echo $content;
3571
	}
3572

    
3573
	/* Flush any remaining output buffer */
3574
	@ob_end_flush();
3575
	exit;
3576
}
3577

    
3578
// Test whether the hostname in a URL can be resolved with a very short timeout
3579
function is_url_hostname_resolvable($url) {
3580
	$urlhostname = parse_url($url, PHP_URL_HOST);
3581
	if (empty($urlhostname)) {
3582
		return false;
3583
	}
3584
	putenv("RES_OPTIONS=timeout:3 attempts:1");
3585
	$resolvable = ($urlhostname !== gethostbyname($urlhostname));
3586
	putenv("RES_OPTIONS");
3587
	return $resolvable;
3588
}
3589

    
3590
function get_pf_timeouts () {
3591
	$pftimeout = array();
3592
	exec("/sbin/pfctl -st", $pfctlst, $retval);
3593
	if ($retval == 0) {
3594
		foreach ($pfctlst as $pfst) {
3595
			preg_match('/([a-z]+)\.([a-z]+)\s+([0-9]+)/', $pfst, $timeout);
3596
			if ($timeout[1] == "other") {
3597
				$proto = "Other";
3598
			} else {
3599
				$proto = strtoupper($timeout[1]);
3600
			}
3601
			if ($timeout[2] == "finwait") {
3602
				$type = "FIN Wait";
3603
			} else {
3604
				$type = ucfirst($timeout[2]);
3605
			}
3606
			$pftimeout[$proto][$type]['name'] = $proto . " " . $type;
3607
			$pftimeout[$proto][$type]['keyname'] = $timeout[1] . $timeout[2] . "timeout";
3608
			$pftimeout[$proto][$type]['value'] = $timeout[3];
3609
		}
3610
	}
3611
	return $pftimeout;
3612
}
3613

    
3614
?>
(38-38/61)