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
	$ttyv0_line =
1359
	    "ttyv0\t\"/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_line;
1374
		} elseif (stristr($tty, "ttyu")) {
1375
			$ttyn = substr($tty, 0, 5);
1376
			$found[$ttyn] = 1;
1377
			$data[] = "{$ttyn}\t{$ttyu_line}";
1378
		} elseif (substr($tty, 0, 7) == 'console') {
1379
			$found['console'] = 1;
1380
			$data[] = $tty;
1381
		} else {
1382
			$data[] = $tty;
1383
		}
1384
	}
1385
	unset($on_off, $console_type, $serial_type);
1386

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

    
1397
	foreach ($items as $item) {
1398
		if (isset($found[$item])) {
1399
			continue;
1400
		}
1401

    
1402
		if ($item == 'console') {
1403
			$data[] = $console_line;
1404
		} elseif ($item == 'ttyv0') {
1405
			$data[] = $ttyv0_line;
1406
		} else {
1407
			$data[] = "{$item}\t{$ttyu_line}";
1408
		}
1409
	}
1410

    
1411
	safe_write_file($ttys_file, $data);
1412

    
1413
	unset($ttys, $ttys_file, $ttys_split, $data);
1414

    
1415
	if ($when != "upgrade") {
1416
		reload_ttys();
1417
	}
1418

    
1419
	return;
1420
}
1421

    
1422
function is_serial_enabled() {
1423
	global $g, $config;
1424

    
1425
	if (!isset($g['enableserial_force']) &&
1426
	    !isset($config['system']['enableserial'])) {
1427
		return false;
1428
	}
1429

    
1430
	return true;
1431
}
1432

    
1433
function reload_ttys() {
1434
	// Send a HUP signal to init will make it reload /etc/ttys
1435
	posix_kill(1, SIGHUP);
1436
}
1437

    
1438
function print_value_list($list, $count = 10, $separator = ",") {
1439
	$list = implode($separator, array_slice($list, 0, $count));
1440
	if (count($list) < $count) {
1441
		$list .= ".";
1442
	} else {
1443
		$list .= "...";
1444
	}
1445
	return $list;
1446
}
1447

    
1448
/* DHCP enabled on any interfaces? */
1449
function is_dhcp_server_enabled() {
1450
	global $config;
1451

    
1452
	if (!is_array($config['dhcpd'])) {
1453
		return false;
1454
	}
1455

    
1456
	foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) {
1457
		if (isset($dhcpifconf['enable']) && !empty($config['interfaces'][$dhcpif])) {
1458
			return true;
1459
		}
1460
	}
1461

    
1462
	return false;
1463
}
1464

    
1465
/* DHCP enabled on any interfaces? */
1466
function is_dhcpv6_server_enabled() {
1467
	global $config;
1468

    
1469
	if (is_array($config['interfaces'])) {
1470
		foreach ($config['interfaces'] as $ifcfg) {
1471
			if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface'])) {
1472
				return true;
1473
			}
1474
		}
1475
	}
1476

    
1477
	if (!is_array($config['dhcpdv6'])) {
1478
		return false;
1479
	}
1480

    
1481
	foreach ($config['dhcpdv6'] as $dhcpv6if => $dhcpv6ifconf) {
1482
		if (isset($dhcpv6ifconf['enable']) && !empty($config['interfaces'][$dhcpv6if])) {
1483
			return true;
1484
		}
1485
	}
1486

    
1487
	return false;
1488
}
1489

    
1490
/* radvd enabled on any interfaces? */
1491
function is_radvd_enabled() {
1492
	global $config;
1493

    
1494
	if (!is_array($config['dhcpdv6'])) {
1495
		$config['dhcpdv6'] = array();
1496
	}
1497

    
1498
	$dhcpdv6cfg = $config['dhcpdv6'];
1499
	$Iflist = get_configured_interface_list();
1500

    
1501
	/* handle manually configured DHCP6 server settings first */
1502
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1503
		if (!isset($config['interfaces'][$dhcpv6if]['enable'])) {
1504
			continue;
1505
		}
1506

    
1507
		if (!isset($dhcpv6ifconf['ramode'])) {
1508
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
1509
		}
1510

    
1511
		if ($dhcpv6ifconf['ramode'] == "disabled") {
1512
			continue;
1513
		}
1514

    
1515
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1516
		if (!is_ipaddrv6($ifcfgipv6)) {
1517
			continue;
1518
		}
1519

    
1520
		return true;
1521
	}
1522

    
1523
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
1524
	foreach ($Iflist as $if => $ifdescr) {
1525
		if (!isset($config['interfaces'][$if]['track6-interface'])) {
1526
			continue;
1527
		}
1528
		if (!isset($config['interfaces'][$if]['enable'])) {
1529
			continue;
1530
		}
1531

    
1532
		$ifcfgipv6 = get_interface_ipv6($if);
1533
		if (!is_ipaddrv6($ifcfgipv6)) {
1534
			continue;
1535
		}
1536

    
1537
		$ifcfgsnv6 = get_interface_subnetv6($if);
1538
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1539

    
1540
		if (!is_ipaddrv6($subnetv6)) {
1541
			continue;
1542
		}
1543

    
1544
		return true;
1545
	}
1546

    
1547
	return false;
1548
}
1549

    
1550
/* Any PPPoE servers enabled? */
1551
function is_pppoe_server_enabled() {
1552
	global $config;
1553

    
1554
	$pppoeenable = false;
1555

    
1556
	if (!is_array($config['pppoes']) || !is_array($config['pppoes']['pppoe'])) {
1557
		return false;
1558
	}
1559

    
1560
	foreach ($config['pppoes']['pppoe'] as $pppoes) {
1561
		if ($pppoes['mode'] == 'server') {
1562
			$pppoeenable = true;
1563
		}
1564
	}
1565

    
1566
	return $pppoeenable;
1567
}
1568

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

    
1583
/* Compute the total uptime from the ppp uptime log file in the conf directory */
1584

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

    
1600
//returns interface information
1601
function get_interface_info($ifdescr) {
1602
	global $config, $g;
1603

    
1604
	$ifinfo = array();
1605
	if (empty($config['interfaces'][$ifdescr])) {
1606
		return;
1607
	}
1608
	$ifinfo['hwif'] = $config['interfaces'][$ifdescr]['if'];
1609
	$ifinfo['enable'] = isset($config['interfaces'][$ifdescr]['enable']);
1610
	$ifinfo['if'] = get_real_interface($ifdescr);
1611

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

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

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

    
1673
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1674
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1675
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1676
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1677

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

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

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

    
1711
			if (empty($ifinfo['status'])) {
1712
				$ifinfo['status'] = "down";
1713
			}
1714

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

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

    
1766
	if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) {
1767
		$sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`);
1768
		$ifinfo['ppp_uptime'] = convert_seconds_to_dhms($sec);
1769
	}
1770

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

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

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

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

    
1854
	return $ifinfo;
1855
}
1856

    
1857
//returns cpu speed of processor. Good for determining capabilities of machine
1858
function get_cpu_speed() {
1859
	return get_single_sysctl("hw.clockrate");
1860
}
1861

    
1862
function get_uptime_sec() {
1863
	$boottime = "";
1864
	$matches = "";
1865
	$boottime = get_single_sysctl("kern.boottime");
1866
	preg_match("/sec = (\d+)/", $boottime, $matches);
1867
	$boottime = $matches[1];
1868
	if (intval($boottime) == 0) {
1869
		return 0;
1870
	}
1871

    
1872
	$uptime = time() - $boottime;
1873
	return $uptime;
1874
}
1875

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

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

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

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

    
1948
function pfsense_default_tables_size() {
1949
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1950
	return $current;
1951
}
1952

    
1953
function pfsense_default_table_entries_size() {
1954
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1955
	return (trim($current));
1956
}
1957

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

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

    
1991
/*
1992
 * load_crypto() - Load crypto modules if enabled in config.
1993
 */
1994
function load_crypto() {
1995
	global $config, $g;
1996
	$crypto_modules = array('aesni', 'cryptodev');
1997

    
1998
	$enabled_modules = explode('_', $config['system']['crypto_hardware']);
1999

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

    
2013
/*
2014
 * load_thermal_hardware() - Load temperature monitor kernel module
2015
 */
2016
function load_thermal_hardware() {
2017
	global $config, $g;
2018
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
2019

    
2020
	if (!in_array($config['system']['thermal_hardware'], $thermal_hardware_modules)) {
2021
		return false;
2022
	}
2023

    
2024
	if (!empty($config['system']['thermal_hardware']) && !is_module_loaded($config['system']['thermal_hardware'])) {
2025
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $config['system']['thermal_hardware']));
2026
		mwexec("/sbin/kldload {$config['system']['thermal_hardware']}");
2027
	}
2028
}
2029

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

    
2042
	if ($rc != 0 || !isset($output[0])) {
2043
		return false;
2044
	}
2045

    
2046
	foreach ($virtualenvs as $virtualenv) {
2047
		if (stripos($output[0], $virtualenv) !== false) {
2048
			return true;
2049
		}
2050
	}
2051

    
2052
	return false;
2053
}
2054

    
2055
function get_freebsd_version() {
2056
	$version = explode(".", php_uname("r"));
2057
	return $version[0];
2058
}
2059

    
2060
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
2061
	global $config, $g;
2062

    
2063
	$fp = fopen($destination, "wb");
2064

    
2065
	if (!$fp) {
2066
		return false;
2067
	}
2068

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

    
2084
	if (!empty($config['system']['proxyurl'])) {
2085
		curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']);
2086
		if (!empty($config['system']['proxyport'])) {
2087
			curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']);
2088
		}
2089
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
2090
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
2091
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}");
2092
		}
2093
	}
2094

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

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

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

    
2140
	if (!empty($config['system']['proxyurl'])) {
2141
		curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']);
2142
		if (!empty($config['system']['proxyport'])) {
2143
			curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']);
2144
		}
2145
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
2146
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
2147
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}");
2148
		}
2149
	}
2150

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

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

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

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

    
2248
/*
2249
 *   update_status: update top textarea dynamically.
2250
 */
2251
function update_status($status) {
2252
	global $pkg_interface;
2253

    
2254
	if ($pkg_interface == "console") {
2255
		print ("{$status}");
2256
	}
2257

    
2258
	/* ensure that contents are written out */
2259
	ob_flush();
2260
}
2261

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

    
2284
function update_alias_name($new_alias_name, $orig_alias_name) {
2285
	if (!$orig_alias_name) {
2286
		return;
2287
	}
2288

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

    
2315
function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) {
2316
	global $g, $config, $pconfig, $debug;
2317
	if (!$origname) {
2318
		return;
2319
	}
2320

    
2321
	$sectionref = &$config;
2322
	foreach ($section as $sectionname) {
2323
		if (is_array($sectionref) && isset($sectionref[$sectionname])) {
2324
			$sectionref = &$sectionref[$sectionname];
2325
		} else {
2326
			return;
2327
		}
2328
	}
2329

    
2330
	if ($debug) {
2331
		$fd = fopen("{$g['tmp_path']}/print_r", "a");
2332
		fwrite($fd, print_r($pconfig, true));
2333
	}
2334

    
2335
	if (is_array($sectionref)) {
2336
		foreach ($sectionref as $itemkey => $item) {
2337
			if ($debug) {
2338
				fwrite($fd, "$itemkey\n");
2339
			}
2340

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

    
2360
	if ($debug) {
2361
		fclose($fd);
2362
	}
2363

    
2364
}
2365

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

    
2376
	if (!file_exists($filename)) {
2377
		log_error(sprintf(gettext("Could not process non-existent file from alias: %s"), $filename));
2378
		return null;
2379
	}
2380

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

    
2446
function update_alias_url_data() {
2447
	global $config, $g;
2448

    
2449
	$updated = false;
2450

    
2451
	/* item is a url type */
2452
	$lockkey = lock('aliasurl');
2453
	if (is_array($config['aliases']['alias'])) {
2454
		foreach ($config['aliases']['alias'] as $x => $alias) {
2455
			if (empty($alias['aliasurl'])) {
2456
				continue;
2457
			}
2458

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

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

    
2490
	/* Report status to callers as well */
2491
	return $updated;
2492
}
2493

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

    
2524
	return true;
2525
}
2526

    
2527
function version_compare_dates($a, $b) {
2528
	$a_time = strtotime($a);
2529
	$b_time = strtotime($b);
2530

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

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

    
2615
		// First try to compare the numeric parts of the version string.
2616
		$v = version_compare_numeric($cur_num, $rem_num);
2617

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

    
2628
	$urltable_prefix = "/var/db/aliastables/";
2629
	$urltable_filename = $urltable_prefix . $name . ".txt";
2630
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2631

    
2632
	// Make the aliases directory if it doesn't exist
2633
	if (!file_exists($urltable_prefix)) {
2634
		mkdir($urltable_prefix);
2635
	} elseif (!is_dir($urltable_prefix)) {
2636
		unlink($urltable_prefix);
2637
		mkdir($urltable_prefix);
2638
	}
2639

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

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

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

    
2654
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2655
			if ($type == "urltable_ports") {
2656
				$parsed_contents = group_ports($parsed_contents, true);
2657
			}
2658
			if (is_array($parsed_contents)) {
2659
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2660
			} else {
2661
				touch($urltable_filename);
2662
			}
2663

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

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

    
2684
function get_include_contents($filename) {
2685
	if (is_file($filename)) {
2686
		ob_start();
2687
		include $filename;
2688
		$contents = ob_get_contents();
2689
		ob_end_clean();
2690
		return $contents;
2691
	}
2692
	return false;
2693
}
2694

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

    
2807
function get_country_name($country_code = "ALL") {
2808
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2809
		return "";
2810
	}
2811

    
2812
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2813
	$country_names_contents = file_get_contents($country_names_xml);
2814
	$country_names = xml2array($country_names_contents);
2815

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

    
2826
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2827
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2828
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2829
		}
2830
	}
2831
	return "";
2832
}
2833

    
2834
/* Return the list of country codes to be used on CAs and certs */
2835
function get_cert_country_codes() {
2836
	$countries = get_country_name();
2837

    
2838
	$country_codes = array();
2839
	foreach ($countries as $country) {
2840
		$country_codes[$country['code']] = $country['code'];
2841
	}
2842
	ksort($country_codes);
2843

    
2844
	/* Preserve historical order: None, US, CA, other countries */
2845
	$first_items[''] = gettext("None");
2846
	$first_items['US'] = $country_codes['US'];
2847
	$first_items['CA'] = $country_codes['CA'];
2848
	unset($country_codes['US']);
2849
	unset($country_codes['CA']);
2850

    
2851
	return array_merge($first_items, $country_codes);
2852
}
2853

    
2854
/* sort by interface only, retain the original order of rules that apply to
2855
   the same interface */
2856
function filter_rules_sort() {
2857
	global $config;
2858

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

    
2868
	usort($config['filter']['rule'], "filter_rules_compare");
2869

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

    
2889
function generate_ipv6_from_mac($mac) {
2890
	$elements = explode(":", $mac);
2891
	if (count($elements) <> 6) {
2892
		return false;
2893
	}
2894

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

    
2916
		$i++;
2917
	}
2918
	return $ipv6;
2919
}
2920

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

    
2947
}
2948

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

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

    
2988
	$where_configured = array();
2989

    
2990
	$pos = strpos($ignore_if, '_virtualip');
2991
	if ($pos !== false) {
2992
		$ignore_vip_id = substr($ignore_if, $pos+10);
2993
		$ignore_vip_if = substr($ignore_if, 0, $pos);
2994
	} else {
2995
		$ignore_vip_id = -1;
2996
		$ignore_vip_if = $ignore_if;
2997
	}
2998

    
2999
	$isipv6 = is_ipaddrv6($ipaddr);
3000

    
3001
	if ($isipv6) {
3002
		$ipaddr = text_to_compressed_ip6($ipaddr);
3003
	}
3004

    
3005
	if ($check_subnets) {
3006
		$cidrprefix = intval($cidrprefix);
3007
		if ($isipv6) {
3008
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
3009
				$cidrprefix = 128;
3010
			}
3011
		} else {
3012
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
3013
				$cidrprefix = 32;
3014
			}
3015
		}
3016
		$iflist = get_configured_interface_list();
3017
		foreach ($iflist as $if => $ifname) {
3018
			if ($ignore_if == $if) {
3019
				continue;
3020
			}
3021

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

    
3049
		foreach ($interface_list_ips as $if => $ilips) {
3050
			if ($ignore_if == $if) {
3051
				continue;
3052
			}
3053
			if (strcasecmp($ipaddr, $ilips) == 0) {
3054
				$where_entry = array();
3055
				$where_entry['if'] = $if;
3056
				$where_entry['ip_or_subnet'] = $ilips;
3057
				$where_configured[] = $where_entry;
3058
			}
3059
		}
3060
	}
3061

    
3062
	if ($check_localip) {
3063
		if (!is_array($config['l2tp']) && !empty($config['l2tp']['localip']) && (strcasecmp($ipaddr, text_to_compressed_ip6($config['l2tp']['localip'])) == 0)) {
3064
			$where_entry = array();
3065
			$where_entry['if'] = 'l2tp';
3066
			$where_entry['ip_or_subnet'] = $config['l2tp']['localip'];
3067
			$where_configured[] = $where_entry;
3068
		}
3069
	}
3070

    
3071
	return $where_configured;
3072
}
3073

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

    
3096
function set_language() {
3097
	global $config, $g;
3098

    
3099
	if (!empty($config['system']['language'])) {
3100
		$lang = $config['system']['language'];
3101
	} elseif (!empty($g['language'])) {
3102
		$lang = $g['language'];
3103
	}
3104
	$lang .= ".UTF-8";
3105

    
3106
	putenv("LANG={$lang}");
3107
	setlocale(LC_ALL, $lang);
3108
	textdomain("pfSense");
3109
	bindtextdomain("pfSense", "/usr/local/share/locale");
3110
	bind_textdomain_codeset("pfSense", $lang);
3111
}
3112

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

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

    
3139
	//asort($locales);
3140

    
3141
	return $locales;
3142
}
3143

    
3144
function return_hex_ipv4($ipv4) {
3145
	if (!is_ipaddrv4($ipv4)) {
3146
		return(false);
3147
	}
3148

    
3149
	/* we need the hex form of the interface IPv4 address */
3150
	$ip4arr = explode(".", $ipv4);
3151
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3152
}
3153

    
3154
function convert_ipv6_to_128bit($ipv6) {
3155
	if (!is_ipaddrv6($ipv6)) {
3156
		return(false);
3157
	}
3158

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

    
3170
function convert_128bit_to_ipv6($ip6bin) {
3171
	if (strlen($ip6bin) <> 128) {
3172
		return(false);
3173
	}
3174

    
3175
	$ip6arr = array();
3176
	$ip6binarr = array();
3177
	$ip6binarr = str_split($ip6bin, 16);
3178
	foreach ($ip6binarr as $binpart) {
3179
		$ip6arr[] = dechex(bindec($binpart));
3180
	}
3181
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3182

    
3183
	return($ip6addr);
3184
}
3185

    
3186

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

    
3194
	if (!is_array($config['interfaces'][$if])) {
3195
		return false;
3196
	}
3197

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

    
3218
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3219
	$prefix = Net_IPv6::uncompress($prefix, true);
3220
	$suffix = Net_IPv6::uncompress($suffix, true);
3221

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

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

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

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

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

    
3302
	return $result;
3303
}
3304

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

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

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

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

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

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

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

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

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

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

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

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

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

    
3430
}
3431

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

    
3468
// Convert IPv6 addresses to lower case
3469
function addrtolower($ip) {
3470
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3471
		return(strtolower($ip));
3472
	} else {
3473
		return($ip);
3474
	}
3475
}
3476

    
3477
function compare_by_name($a, $b) {
3478
	return strcasecmp($a['name'], $b['name']);
3479
}
3480

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

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

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

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

    
3536
	/* Cannot determine the filename, so bail. */
3537
	if (empty($name)) {
3538
		exit;
3539
	}
3540

    
3541
	/* Send basic download headers */
3542
	header("Content-Type: {$contenttype}");
3543
	header("Content-Length: {$size}");
3544
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3545

    
3546
	/* Send cache headers */
3547
	if (isset($_SERVER['HTTPS'])) {
3548
		header('Pragma: ');
3549
		header('Cache-Control: ');
3550
	} else {
3551
		header("Pragma: private");
3552
		header("Cache-Control: private, must-revalidate");
3553
	}
3554

    
3555
	/* Ensure output buffering is off so PHP does not consume
3556
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3557
	while (ob_get_level()) {
3558
		@ob_end_clean();
3559
	}
3560

    
3561
	/* Send the data to the user */
3562
	if ($type == 'file') {
3563
		readfile($content);
3564
	} else {
3565
		echo $content;
3566
	}
3567

    
3568
	/* Flush any remaining output buffer */
3569
	@ob_end_flush();
3570
	exit;
3571
}
3572

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

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

    
3609
?>
(38-38/61)