diff options
Diffstat (limited to 'src/etc/inc')
71 files changed, 79427 insertions, 0 deletions
diff --git a/src/etc/inc/CHAP.inc b/src/etc/inc/CHAP.inc new file mode 100644 index 0000000..6eb22f7 --- /dev/null +++ b/src/etc/inc/CHAP.inc @@ -0,0 +1,463 @@ +<?php +/* +Copyright (c) 2002-2010, Michael Bretterklieber <michael@bretterklieber.com> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This code cannot simply be copied and put under the GNU Public License or +any other GPL-like (LGPL, GPL2) License. + + $Id: CHAP.php 302857 2010-08-28 21:12:59Z mbretter $ +*/ + +require_once 'PEAR.inc'; + +/** +* Classes for generating packets for various CHAP Protocols: +* CHAP-MD5: RFC1994 +* MS-CHAPv1: RFC2433 +* MS-CHAPv2: RFC2759 +* +* @package Crypt_CHAP +* @author Michael Bretterklieber <michael@bretterklieber.com> +* @access public +* @version $Revision: 302857 $ +*/ + +/** + * class Crypt_CHAP + * + * Abstract base class for CHAP + * + * @package Crypt_CHAP + */ +class Crypt_CHAP extends PEAR +{ + /** + * Random binary challenge + * @var string + */ + var $challenge = null; + + /** + * Binary response + * @var string + */ + var $response = null; + + /** + * User password + * @var string + */ + var $password = null; + + /** + * Id of the authentication request. Should incremented after every request. + * @var integer + */ + var $chapid = 1; + + /** + * Constructor + * + * Generates a random challenge + * @return void + */ + function Crypt_CHAP() + { + $this->PEAR(); + $this->generateChallenge(); + } + + /** + * Generates a random binary challenge + * + * @param string $varname Name of the property + * @param integer $size Size of the challenge in Bytes + * @return void + */ + function generateChallenge($varname = 'challenge', $size = 8) + { + $this->$varname = ''; + for ($i = 0; $i < $size; $i++) { + $this->$varname .= pack('C', 1 + mt_rand() % 255); + } + return $this->$varname; + } + + /** + * Generates the response. Overwrite this. + * + * @return void + */ + function challengeResponse() + { + } + +} + +/** + * class Crypt_CHAP_MD5 + * + * Generate CHAP-MD5 Packets + * + * @package Crypt_CHAP + */ +class Crypt_CHAP_MD5 extends Crypt_CHAP +{ + + /** + * Generates the response. + * + * CHAP-MD5 uses MD5-Hash for generating the response. The Hash consists + * of the chapid, the plaintext password and the challenge. + * + * @return string + */ + function challengeResponse() + { + return pack('H*', md5(pack('C', $this->chapid) . $this->password . $this->challenge)); + } +} + +/** + * class Crypt_CHAP_MSv1 + * + * Generate MS-CHAPv1 Packets. MS-CHAP doesen't use the plaintext password, it uses the + * NT-HASH wich is stored in the SAM-Database or in the smbpasswd, if you are using samba. + * The NT-HASH is MD4(str2unicode(plaintextpass)). + * You need the hash extension for this class. + * + * @package Crypt_CHAP + */ +class Crypt_CHAP_MSv1 extends Crypt_CHAP +{ + /** + * Wether using deprecated LM-Responses or not. + * 0 = use LM-Response, 1 = use NT-Response + * @var bool + */ + var $flags = 1; + + /** + * Constructor + * + * Loads the hash extension + * @return void + */ + function Crypt_CHAP_MSv1() + { + $this->Crypt_CHAP(); + $this->loadExtension('hash'); + } + + /** + * Generates the NT-HASH from the given plaintext password. + * + * @access public + * @return string + */ + function ntPasswordHash($password = null) + { + if (isset($password)) { + return pack('H*',hash('md4', $this->str2unicode($password))); + } else { + return pack('H*',hash('md4', $this->str2unicode($this->password))); + } + } + + /** + * Converts ascii to unicode. + * + * @access public + * @return string + */ + function str2unicode($str) + { + $uni = ''; + $str = (string) $str; + for ($i = 0; $i < strlen($str); $i++) { + $a = ord($str{$i}) << 8; + $uni .= sprintf("%X", $a); + } + return pack('H*', $uni); + } + + /** + * Generates the NT-Response. + * + * @access public + * @return string + */ + function challengeResponse() + { + return $this->_challengeResponse(); + } + + /** + * Generates the NT-Response. + * + * @access public + * @return string + */ + function ntChallengeResponse() + { + return $this->_challengeResponse(false); + } + + /** + * Generates the LAN-Manager-Response. + * + * @access public + * @return string + */ + function lmChallengeResponse() + { + return $this->_challengeResponse(true); + } + + /** + * Generates the response. + * + * Generates the response using DES. + * + * @param bool $lm wether generating LAN-Manager-Response + * @access private + * @return string + */ + function _challengeResponse($lm = false) + { + if ($lm) { + $hash = $this->lmPasswordHash(); + } else { + $hash = $this->ntPasswordHash(); + } + + while (strlen($hash) < 21) { + $hash .= "\0"; + } + + $td = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, ''); + $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); + $key = $this->_desAddParity(substr($hash, 0, 7)); + mcrypt_generic_init($td, $key, $iv); + $resp1 = mcrypt_generic($td, $this->challenge); + mcrypt_generic_deinit($td); + + $key = $this->_desAddParity(substr($hash, 7, 7)); + mcrypt_generic_init($td, $key, $iv); + $resp2 = mcrypt_generic($td, $this->challenge); + mcrypt_generic_deinit($td); + + $key = $this->_desAddParity(substr($hash, 14, 7)); + mcrypt_generic_init($td, $key, $iv); + $resp3 = mcrypt_generic($td, $this->challenge); + mcrypt_generic_deinit($td); + mcrypt_module_close($td); + + return $resp1 . $resp2 . $resp3; + } + + /** + * Generates the LAN-Manager-HASH from the given plaintext password. + * + * @access public + * @return string + */ + function lmPasswordHash($password = null) + { + $plain = isset($password) ? $password : $this->password; + + $plain = substr(strtoupper($plain), 0, 14); + while (strlen($plain) < 14) { + $plain .= "\0"; + } + + return $this->_desHash(substr($plain, 0, 7)) . $this->_desHash(substr($plain, 7, 7)); + } + + /** + * Generates an irreversible HASH. + * + * @access private + * @return string + */ + function _desHash($plain) + { + $key = $this->_desAddParity($plain); + $td = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, ''); + $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND); + mcrypt_generic_init($td, $key, $iv); + $hash = mcrypt_generic($td, 'KGS!@#$%'); + mcrypt_generic_deinit($td); + mcrypt_module_close($td); + return $hash; + } + + /** + * Adds the parity bit to the given DES key. + * + * @access private + * @param string $key 7-Bytes Key without parity + * @return string + */ + function _desAddParity($key) + { + static $odd_parity = array( + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, + 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, + 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, + 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, + 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, + 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, + 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, + 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, + 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, + 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254); + + $bin = ''; + for ($i = 0; $i < strlen($key); $i++) { + $bin .= sprintf('%08s', decbin(ord($key{$i}))); + } + + $str1 = explode('-', substr(chunk_split($bin, 7, '-'), 0, -1)); + $x = ''; + foreach($str1 as $s) { + $x .= sprintf('%02s', dechex($odd_parity[bindec($s . '0')])); + } + + return pack('H*', $x); + + } + + /** + * Generates the response-packet. + * + * @param bool $lm wether including LAN-Manager-Response + * @access private + * @return string + */ + function response($lm = false) + { + $ntresp = $this->ntChallengeResponse(); + if ($lm) { + $lmresp = $this->lmChallengeResponse(); + } else { + $lmresp = str_repeat ("\0", 24); + } + + // Response: LM Response, NT Response, flags (0 = use LM Response, 1 = use NT Response) + return $lmresp . $ntresp . pack('C', !$lm); + } +} + +/** + * class Crypt_CHAP_MSv2 + * + * Generate MS-CHAPv2 Packets. This version of MS-CHAP uses a 16 Bytes authenticator + * challenge and a 16 Bytes peer Challenge. LAN-Manager responses no longer exists + * in this version. The challenge is already a SHA1 challenge hash of both challenges + * and of the username. + * + * @package Crypt_CHAP + */ +class Crypt_CHAP_MSv2 extends Crypt_CHAP_MSv1 +{ + /** + * The username + * @var string + */ + var $username = null; + + /** + * The 16 Bytes random binary peer challenge + * @var string + */ + var $peerChallenge = null; + + /** + * The 16 Bytes random binary authenticator challenge + * @var string + */ + var $authChallenge = null; + + /** + * Constructor + * + * Generates the 16 Bytes peer and authentication challenge + * @return void + */ + function Crypt_CHAP_MSv2() + { + $this->Crypt_CHAP_MSv1(); + $this->generateChallenge('peerChallenge', 16); + $this->generateChallenge('authChallenge', 16); + } + + /** + * Generates a hash from the NT-HASH. + * + * @access public + * @param string $nthash The NT-HASH + * @return string + */ + function ntPasswordHashHash($nthash) + { + return pack('H*',hash('md4', $nthash)); + } + + /** + * Generates the challenge hash from the peer and the authenticator challenge and + * the username. SHA1 is used for this, but only the first 8 Bytes are used. + * + * @access public + * @return string + */ + function challengeHash() + { + return substr(pack('H*',hash('sha1', $this->peerChallenge . $this->authChallenge . $this->username)), 0, 8); + } + + /** + * Generates the response. + * + * @access public + * @return string + */ + function challengeResponse() + { + $this->challenge = $this->challengeHash(); + return $this->_challengeResponse(); + } +} + + +?> diff --git a/src/etc/inc/IPv6.inc b/src/etc/inc/IPv6.inc new file mode 100644 index 0000000..faacb8d --- /dev/null +++ b/src/etc/inc/IPv6.inc @@ -0,0 +1,1110 @@ +<?php + +/* + pfSense_MODULE: utils +*/ + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * This file contains the implementation of the Net_IPv6 class + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to the New BSD license, that is + * available through the world-wide-web at + * http://www.opensource.org/licenses/bsd-license.php + * If you did not receive a copy of the new BSDlicense and are unable + * to obtain it through the world-wide-web, please send a note to + * license@php.net so we can mail you a copy immediately + * + * @category Net + * @package Net_IPv6 + * @author Alexander Merz <alexander.merz@web.de> + * @copyright 2003-2005 The PHP Group + * @license BSD License http://www.opensource.org/licenses/bsd-license.php + * @version CVS: $Id$ + * @link http://pear.php.net/package/Net_IPv6 + */ + +// {{{ constants + +/** + * Error message if netmask bits was not found + * @see isInNetmask + */ +define("NET_IPV6_NO_NETMASK_MSG", "Netmask length not found"); + +/** + * Error code if netmask bits was not found + * @see isInNetmask + */ +define("NET_IPV6_NO_NETMASK", 10); + +/** + * Address Type: Unassigned (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_UNASSIGNED", 1); + +/** + * Address Type: Reserved (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_RESERVED", 11); + +/** + * Address Type: Reserved for NSAP Allocation (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_RESERVED_NSAP", 12); + +/** + * Address Type: Reserved for IPX Allocation (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_RESERVED_IPX", 13); + +/** + * Address Type: Reserved for Geographic-Based Unicast Addresses + * (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC", 14); + +/** + * Address Type: Provider-Based Unicast Address (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_UNICAST_PROVIDER", 22); + +/** + * Address Type: Multicast Addresses (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_MULTICAST", 31); + +/** + * Address Type: Link Local Use Addresses (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_LOCAL_LINK", 42); + +/** + * Address Type: Link Local Use Addresses (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_LOCAL_SITE", 43); + +/** + * Address Type: Address range to embedded IPv4 ip in an IPv6 address (RFC 4291, Section 2.5.5) + * @see getAddressType() + */ +define("NET_IPV6_IPV4MAPPING", 51); + +/** + * Address Type: Unspecified (RFC 4291, Section 2.5.2) + * @see getAddressType() + */ +define("NET_IPV6_UNSPECIFIED", 52); + +/** + * Address Type: Unspecified (RFC 4291, Section 2.5.3) + * @see getAddressType() + */ +define("NET_IPV6_LOOPBACK", 53); + +/** + * Address Type: address can not assigned to a specific type + * @see getAddressType() + */ +define("NET_IPV6_UNKNOWN_TYPE", 1001); + +// }}} +// {{{ Net_IPv6 + +/** + * Class to validate and to work with IPv6 addresses. + * + * @category Net + * @package Net_IPv6 + * @author Alexander Merz <alexander.merz@web.de> + * @author <elfrink at introweb dot nl> + * @author Josh Peck <jmp at joshpeck dot org> + * @copyright 2003-2010 The PHP Group + * @license BSD License http://www.opensource.org/licenses/bsd-license.php + * @version Release: 1.1.0RC5 + * @link http://pear.php.net/package/Net_IPv6 + */ +class Net_IPv6 +{ + + // {{{ separate() + /** + * Separates an IPv6 address into the address and a prefix length part + * + * @param String $ip the (compressed) IP as Hex representation + * + * @return Array the first element is the IP, the second the prefix length + * @since 1.2.0 + * @access public + * @static + */ + static function separate($ip) + { + + $addr = $ip; + $spec = ''; + + if(false === strrpos($ip, '/')) { + + return array($addr, $spec); + + } + + $elements = explode('/', $ip); + + if(2 == count($elements)) { + + $addr = $elements[0]; + $spec = $elements[1]; + + } + + return array($addr, $spec); + + } + // }}} + + // {{{ removeNetmaskSpec() + + /** + * Removes a possible existing prefix length/ netmask specification at an IP address. + * + * @param String $ip the (compressed) IP as Hex representation + * + * @return String the IP without netmask length + * @since 1.1.0 + * @access public + * @static + */ + static function removeNetmaskSpec($ip) + { + + $elements = Net_IPv6::separate($ip); + + return $elements[0]; + + } + // }}} + // {{{ removePrefixLength() + + /** + * Tests for a prefix length specification in the address + * and removes the prefix length, if exists + * + * The method is technically identical to removeNetmaskSpec() and + * will be dropped in a future release. + * + * @param String $ip a valid ipv6 address + * + * @return String the address without a prefix length + * @access public + * @static + * @see removeNetmaskSpec() + * @deprecated + */ + static function removePrefixLength($ip) + { + $pos = strrpos($ip, '/'); + + if (false !== $pos) { + + return substr($ip, 0, $pos); + + } + + return $ip; + } + + // }}} + // {{{ getNetmaskSpec() + + /** + * Returns a possible existing prefix length/netmask specification on an IP address. + * + * @param String $ip the (compressed) IP as Hex representation + * + * @return String the netmask spec + * @since 1.1.0 + * @access public + * @static + */ + static function getNetmaskSpec($ip) + { + + $elements = Net_IPv6::separate($ip); + + return $elements[1]; + + } + + // }}} + // {{{ getPrefixLength() + + /** + * Tests for a prefix length specification in the address + * and returns the prefix length, if exists + * + * The method is technically identical to getNetmaskSpec() and + * will be dropped in a future release. + * + * @param String $ip a valid ipv6 address + * + * @return Mixed the prefix as String or false, if no prefix was found + * @access public + * @static + * @deprecated + */ + static function getPrefixLength($ip) + { + if (preg_match("/^([0-9a-fA-F:]{2,39})\/(\d{1,3})*$/", + $ip, $matches)) { + + return $matches[2]; + + } else { + + return false; + + } + + } + + // }}} + // {{{ getNetmask() + + /** + * Calculates the network prefix based on the netmask bits. + * + * @param String $ip the (compressed) IP in Hex format + * @param int $bits if the number of netmask bits is not part of the IP + * you must provide the number of bits + * + * @return String the network prefix + * @since 1.1.0 + * @access public + * @static + */ + static function getNetmask($ip, $bits = null) + { + if (null==$bits) { + + $elements = explode('/', $ip); + + if (2 == count($elements)) { + + $addr = $elements[0]; + $bits = $elements[1]; + + } else { + + include_once 'PEAR.inc'; + + return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG, + NET_IPV6_NO_NETMASK); + } + + } else { + + $addr = $ip; + + } + + $addr = Net_IPv6::uncompress($addr); + $binNetmask = str_repeat('1', $bits).str_repeat('0', 128 - $bits); + + return Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($addr) & $binNetmask); + } + + // }}} + // {{{ isInNetmask() + + /** + * Checks if an (compressed) IP is in a specific address space. + * + * If the IP does not contain the number of netmask bits (F8000::FFFF/16) + * then you have to use the $bits parameter. + * + * @param String $ip the IP to check (eg. F800::FFFF) + * @param String $netmask the netmask (eg F800::) + * @param int $bits the number of netmask bits to compare, + * if not given in $ip + * + * @return boolean true if $ip is in the netmask + * @since 1.1.0 + * @access public + * @static + */ + static function isInNetmask($ip, $netmask, $bits=null) + { + // try to get the bit count + + if (null == $bits) { + + $elements = explode('/', $ip); + + if (2 == count($elements)) { + + $ip = $elements[0]; + $bits = $elements[1]; + + } else if (null == $bits) { + + $elements = explode('/', $netmask); + + if (2 == count($elements)) { + + $netmask = $elements[0]; + $bits = $elements[1]; + + } + + if (null == $bits) { + + include_once 'PEAR.inc'; + return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG, + NET_IPV6_NO_NETMASK); + + } + + } + + } + + $binIp = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($ip)); + $binNetmask = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($netmask)); + + if (null != $bits + && "" != $bits + && 0 == strncmp($binNetmask, $binIp, $bits)) { + + return true; + + } + + return false; + } + + // }}} + // {{{ getAddressType() + + /** + * Returns the type of an IPv6 address. + * + * RFC 2373, Section 2.3 describes several types of addresses in + * the IPv6 address space. + * Several address types are markers for reserved spaces and as + * a consequence are subject to change. + * + * @param String $ip the IP address in Hex format, + * compressed IPs are allowed + * + * @return int one of the address type constants + * @access public + * @since 1.1.0 + * @static + * + * @see NET_IPV6_UNASSIGNED + * @see NET_IPV6_RESERVED + * @see NET_IPV6_RESERVED_NSAP + * @see NET_IPV6_RESERVED_IPX + * @see NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC + * @see NET_IPV6_UNICAST_PROVIDER + * @see NET_IPV6_MULTICAST + * @see NET_IPV6_LOCAL_LINK + * @see NET_IPV6_LOCAL_SITE + * @see NET_IPV6_IPV4MAPPING + * @see NET_IPV6_UNSPECIFIED + * @see NET_IPV6_LOOPBACK + * @see NET_IPV6_UNKNOWN_TYPE + */ + static function getAddressType($ip) + { + $ip = Net_IPv6::removeNetmaskSpec($ip); + $binip = Net_IPv6::_ip2Bin($ip); + + if(0 == strncmp(str_repeat('0', 128), $binip, 128)) { // ::/128 + + return NET_IPV6_UNSPECIFIED; + + } else if(0 == strncmp(str_repeat('0', 127).'1', $binip, 128)) { // ::/128 + + return NET_IPV6_LOOPBACK; + + } else if (0 == strncmp(str_repeat('0', 80).str_repeat('1', 16), $binip, 96)) { // ::ffff/96 + + return NET_IPV6_IPV4MAPPING; + + } else if (0 == strncmp('1111111010', $binip, 10)) { + + return NET_IPV6_LOCAL_LINK; + + } else if (0 == strncmp('1111111011', $binip, 10)) { + + return NET_IPV6_LOCAL_SITE; + + } else if (0 == strncmp('111111100', $binip, 9)) { + + return NET_IPV6_UNASSIGNED; + + } else if (0 == strncmp('11111111', $binip, 8)) { + + return NET_IPV6_MULTICAST; + + } else if (0 == strncmp('00000000', $binip, 8)) { + + return NET_IPV6_RESERVED; + + } else if (0 == strncmp('00000001', $binip, 8) + || 0 == strncmp('1111110', $binip, 7)) { + + return NET_IPV6_UNASSIGNED; + + } else if (0 == strncmp('0000001', $binip, 7)) { + + return NET_IPV6_RESERVED_NSAP; + + } else if (0 == strncmp('0000010', $binip, 7)) { + + return NET_IPV6_RESERVED_IPX; + + } else if (0 == strncmp('0000011', $binip, 7) || + 0 == strncmp('111110', $binip, 6) || + 0 == strncmp('11110', $binip, 5) || + 0 == strncmp('00001', $binip, 5) || + 0 == strncmp('1110', $binip, 4) || + 0 == strncmp('0001', $binip, 4) || + 0 == strncmp('001', $binip, 3) || + 0 == strncmp('011', $binip, 3) || + 0 == strncmp('101', $binip, 3) || + 0 == strncmp('110', $binip, 3)) { + + return NET_IPV6_UNASSIGNED; + + } else if (0 == strncmp('010', $binip, 3)) { + + return NET_IPV6_UNICAST_PROVIDER; + + } else if (0 == strncmp('100', $binip, 3)) { + + return NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC; + + } + + return NET_IPV6_UNKNOWN_TYPE; + } + + // }}} + // {{{ Uncompress() + + /** + * Uncompresses an IPv6 address + * + * RFC 2373 allows you to compress zeros in an address to '::'. This + * function expects a valid IPv6 address and expands the '::' to + * the required zeros. + * + * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 + * ::1 -> 0:0:0:0:0:0:0:1 + * + * Note: You can also pass an invalid IPv6 address (usually as part of the process + * of validation by checkIPv6, which will validate the return string). + * This function will insert the "0" between "::" even if this results in too many + * numbers in total. It is NOT the purpose of this function to validate your input. + * + * Example of calling with invalid input: 1::2:3:4:5:6:7:8:9 -> 1:0:2:3:4:5:6:7:8:9 + * + * @param String $ip a (possibly) valid IPv6-address (hex format) + * @param Boolean $leadingZeros if true, leading zeros are added to each + * block of the address + * (FF01::101 -> + * FF01:0000:0000:0000:0000:0000:0000:0101) + * + * @return String the uncompressed IPv6-address (hex format) + * @access public + * @see Compress() + * @static + * @author Pascal Uhlmann + */ + static function uncompress($ip, $leadingZeros = false) + { + + $prefix = Net_IPv6::getPrefixLength($ip); + + if (false === $prefix) { + + $prefix = ''; + + } else { + + $ip = Net_IPv6::removePrefixLength($ip); + $prefix = '/'.$prefix; + + } + + $netmask = Net_IPv6::getNetmaskSpec($ip); + $uip = Net_IPv6::removeNetmaskSpec($ip); + + $c1 = -1; + $c2 = -1; + + if (false !== strpos($uip, '::') ) { + + list($ip1, $ip2) = explode('::', $uip); + + if ("" == $ip1) { + + $c1 = -1; + + } else { + + $pos = 0; + + if (0 < ($pos = substr_count($ip1, ':'))) { + + $c1 = $pos; + + } else { + + $c1 = 0; + + } + } + if ("" == $ip2) { + + $c2 = -1; + + } else { + + $pos = 0; + + if (0 < ($pos = substr_count($ip2, ':'))) { + + $c2 = $pos; + + } else { + + $c2 = 0; + + } + + } + + if (strstr($ip2, '.')) { + + $c2++; + + } + if (-1 == $c1 && -1 == $c2) { // :: + + $uip = "0:0:0:0:0:0:0:0"; + + } else if (-1 == $c1) { // ::xxx + + $fill = str_repeat('0:', 7-$c2); + $uip = str_replace('::', $fill, $uip); + + } else if (-1 == $c2) { // xxx:: + + $fill = str_repeat(':0', 7-$c1); + $uip = str_replace('::', $fill, $uip); + + } else { // xxx::xxx + + $fill = str_repeat(':0:', max(1, 6-$c2-$c1)); + $uip = str_replace('::', $fill, $uip); + $uip = str_replace('::', ':', $uip); + + } + } + + if(true == $leadingZeros) { + + $uipT = array(); + $uiparts = explode(':', $uip); + + foreach($uiparts as $p) { + + $uipT[] = sprintf('%04s', $p); + + } + + $uip = implode(':', $uipT); + } + + if ('' != $netmask) { + + $uip = $uip.'/'.$netmask; + + } + + return $uip.$prefix; + } + + // }}} + // {{{ Compress() + + /** + * Compresses an IPv6 address + * + * RFC 2373 allows you to compress zeros in an address to '::'. This + * function expects a valid IPv6 address and compresses successive zeros + * to '::' + * + * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 + * 0:0:0:0:0:0:0:1 -> ::1 + * + * When $ip is an already compressed address and $force is false, the method returns + * the value as is, even if the address can be compressed further. + * + * Example: FF01::0:1 -> FF01::0:1 + * + * To enforce maximum compression, you can set the second argument $force to true. + * + * Example: FF01::0:1 -> FF01::1 + * + * @param String $ip a valid IPv6-address (hex format) + * @param boolean $force if true the address will be compressed as best as possible (since 1.2.0) + * + * @return String the compressed IPv6-address (hex format) + * @access public + * @see Uncompress() + * @static + * @author elfrink at introweb dot nl + */ + static function compress($ip, $force = false) + { + + if(false !== strpos($ip, '::')) { // its already compressed + + if(true == $force) { + + $ip = Net_IPv6::uncompress($ip); + + } else { + + return $ip; + + } + + } + + $prefix = Net_IPv6::getPrefixLength($ip); + + if (false === $prefix) { + + $prefix = ''; + + } else { + + $ip = Net_IPv6::removePrefixLength($ip); + $prefix = '/'.$prefix; + + } + + $netmask = Net_IPv6::getNetmaskSpec($ip); + $ip = Net_IPv6::removeNetmaskSpec($ip); + + $ipp = explode(':', $ip); + + for ($i = 0; $i < count($ipp); $i++) { + + $ipp[$i] = dechex(hexdec($ipp[$i])); + + } + + $cip = ':' . join(':', $ipp) . ':'; + + preg_match_all("/(:0)(:0)+/", $cip, $zeros); + + if (count($zeros[0]) > 0) { + + $match = ''; + + foreach ($zeros[0] as $zero) { + + if (strlen($zero) > strlen($match)) { + + $match = $zero; + + } + } + + $cip = preg_replace('/' . $match . '/', ':', $cip, 1); + + } + + $cip = preg_replace('/((^:)|(:$))/', '', $cip); + $cip = preg_replace('/((^:)|(:$))/', '::', $cip); + + if (empty($cip)) { + + $cip = "::"; + + } + + if ('' != $netmask) { + + $cip = $cip.'/'.$netmask; + + } + + return $cip.$prefix; + + } + + // }}} + // {{{ recommendedFormat() + /** + * Represent IPv6 address in RFC5952 format. + * + * @param String $ip a valid IPv6-address (hex format) + * + * @return String the recommended representation of IPv6-address (hex format) + * @access public + * @see compress() + * @static + * @author koyama at hoge dot org + * @todo This method may become a part of compress() in a further releases + */ + static function recommendedFormat($ip) + { + $compressed = self::compress($ip, true); + // RFC5952 4.2.2 + // The symbol "::" MUST NOT be used to shorten just one + // 16-bit 0 field. + if ((substr_count($compressed, ':') == 7) && + (strpos($compressed, '::') !== false)) { + $compressed = str_replace('::', ':0:', $compressed); + } + return $compressed; + } + // }}} + + // {{{ isCompressible() + + /** + * Checks, if an IPv6 address can be compressed + * + * @param String $ip a valid IPv6 address + * + * @return Boolean true, if address can be compressed + * + * @access public + * @since 1.2.0b + * @static + * @author Manuel Schmitt + */ + static function isCompressible($ip) + { + + return (bool)($ip != Net_IPv6::compress($address)); + + } + + // }}} + // {{{ SplitV64() + + /** + * Splits an IPv6 address into the IPv6 and a possible IPv4 part + * + * RFC 2373 allows you to note the last two parts of an IPv6 address as + * an IPv4 compatible address + * + * Example: 0:0:0:0:0:0:13.1.68.3 + * 0:0:0:0:0:FFFF:129.144.52.38 + * + * @param String $ip a valid IPv6-address (hex format) + * @param Boolean $uncompress if true, the address will be uncompressed + * before processing + * + * @return Array [0] contains the IPv6 part, + * [1] the IPv4 part (hex format) + * @access public + * @static + */ + static function SplitV64($ip, $uncompress = true) + { + $ip = Net_IPv6::removeNetmaskSpec($ip); + + if ($uncompress) { + + $ip = Net_IPv6::Uncompress($ip); + + } + + if (strstr($ip, '.')) { + + $pos = strrpos($ip, ':'); + $ip{$pos} = '_'; + $ipPart = explode('_', $ip); + + return $ipPart; + + } else { + + return array($ip, ""); + + } + } + + // }}} + // {{{ checkIPv6() + + /** + * Checks an IPv6 address + * + * Checks if the given IP is IPv6-compatible + * + * @param String $ip a valid IPv6-address + * + * @return Boolean true if $ip is an IPv6 address + * @access public + * @static + */ + static function checkIPv6($ip) + { + + $elements = Net_IPv6::separate($ip); + + $ip = $elements[0]; + + if('' != $elements[1] && ( !is_numeric($elements[1]) || 0 > $elements[1] || 128 < $elements[1])) { + + return false; + + } + + $ipPart = Net_IPv6::SplitV64($ip); + $count = 0; + + if (!empty($ipPart[0])) { + $ipv6 = explode(':', $ipPart[0]); + + foreach($ipv6 as $element) { // made a validate precheck + if(!preg_match('/[0-9a-fA-F]*/', $element)) { + return false; + } + } + + for ($i = 0; $i < count($ipv6); $i++) { + + if(4 < strlen($ipv6[$i])) { + + return false; + + } + + $dec = hexdec($ipv6[$i]); + $hex = strtoupper(preg_replace("/^[0]{1,3}(.*[0-9a-fA-F])$/", + "\\1", + $ipv6[$i])); + + if ($ipv6[$i] >= 0 && $dec <= 65535 + && $hex == strtoupper(dechex($dec))) { + + $count++; + + } + + } + + if (8 == $count) { + + return true; + + } else if (6 == $count and !empty($ipPart[1])) { + + $ipv4 = explode('.', $ipPart[1]); + $count = 0; + + for ($i = 0; $i < count($ipv4); $i++) { + + if ($ipv4[$i] >= 0 && (integer)$ipv4[$i] <= 255 + && preg_match("/^\d{1,3}$/", $ipv4[$i])) { + + $count++; + + } + + } + + if (4 == $count) { + + return true; + + } + + } else { + + return false; + + } + + } else { + + return false; + + } + + } + + // }}} + + // {{{ _parseAddress() + + /** + * Returns the lowest and highest IPv6 address + * for a given IP and netmask specification + * + * The netmask may be a part of the $ip or + * the number of netmask bits is provided via $bits + * + * The result is an indexed array. The key 'start' + * contains the lowest possible IP address. The key + * 'end' the highest address. + * + * @param String $ipToParse the IPv6 address + * @param String $bits the optional count of netmask bits + * + * @return Array ['start', 'end'] the lowest and highest IPv6 address + * @access public + * @static + * @author Nicholas Williams + */ + + static function parseAddress($ipToParse, $bits = null) + { + + $ip = null; + $bitmask = null; + + if ( null == $bits ) { + + $elements = explode('/', $ipToParse); + + if ( 2 == count($elements) ) { + + $ip = Net_IPv6::uncompress($elements[0]); + $bitmask = $elements[1]; + + } else { + + include_once 'PEAR.inc'; + + return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG, + NET_IPV6_NO_NETMASK); + } + } else { + + $ip = Net_IPv6::uncompress($ipToParse); + $bitmask = $bits; + + } + + $binNetmask = str_repeat('1', $bitmask). + str_repeat('0', 128 - $bitmask); + $maxNetmask = str_repeat('1', 128); + $netmask = Net_IPv6::_bin2Ip($binNetmask); + + $startAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) + & $binNetmask); + $endAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) + | ($binNetmask ^ $maxNetmask)); + + return array('start' => $startAddress, 'end' => $endAddress); + } + + // }}} + + // {{{ _ip2Bin() + + /** + * Converts an IPv6 address from Hex into Binary representation. + * + * @param String $ip the IP to convert (a:b:c:d:e:f:g:h), + * compressed IPs are allowed + * + * @return String the binary representation + * @access private + @ @since 1.1.0 + */ + static function _ip2Bin($ip) + { + $binstr = ''; + + $ip = Net_IPv6::removeNetmaskSpec($ip); + $ip = Net_IPv6::Uncompress($ip); + + $parts = explode(':', $ip); + + foreach ( $parts as $v ) { + + $str = base_convert($v, 16, 2); + $binstr .= str_pad($str, 16, '0', STR_PAD_LEFT); + + } + + return $binstr; + } + + // }}} + // {{{ _bin2Ip() + + /** + * Converts an IPv6 address from Binary into Hex representation. + * + * @param String $bin the IP address as binary + * + * @return String the uncompressed Hex representation + * @access private + @ @since 1.1.0 + */ + static function _bin2Ip($bin) + { + $ip = ""; + + if (strlen($bin) < 128) { + + $bin = str_pad($bin, 128, '0', STR_PAD_LEFT); + + } + + $parts = str_split($bin, "16"); + + foreach ( $parts as $v ) { + + $str = base_convert($v, 2, 16); + $ip .= $str.":"; + + } + + $ip = substr($ip, 0, -1); + + return $ip; + } + + // }}} +} +// }}} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ + +?> diff --git a/src/etc/inc/PEAR.inc b/src/etc/inc/PEAR.inc new file mode 100644 index 0000000..a280602 --- /dev/null +++ b/src/etc/inc/PEAR.inc @@ -0,0 +1,1103 @@ +<?php +/** + * PEAR, the PHP Extension and Application Repository + * + * PEAR class and PEAR_Error class + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Sterling Hughes <sterling@php.net> + * @author Stig Bakken <ssb@php.net> + * @author Tomas V.V.Cox <cox@idecnet.com> + * @author Greg Beaver <cellog@php.net> + * @copyright 1997-2010 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/**#@+ + * ERROR constants + */ +define('PEAR_ERROR_RETURN', 1); +define('PEAR_ERROR_PRINT', 2); +define('PEAR_ERROR_TRIGGER', 4); +define('PEAR_ERROR_DIE', 8); +define('PEAR_ERROR_CALLBACK', 16); +/** + * WARNING: obsolete + * @deprecated + */ +define('PEAR_ERROR_EXCEPTION', 32); +/**#@-*/ +define('PEAR_ZE2', (function_exists('version_compare') && + version_compare(zend_version(), "2-dev", "ge"))); + +if (substr(PHP_OS, 0, 3) == 'WIN') { + define('OS_WINDOWS', true); + define('OS_UNIX', false); + define('PEAR_OS', 'Windows'); +} else { + define('OS_WINDOWS', false); + define('OS_UNIX', true); + define('PEAR_OS', 'Unix'); // blatant assumption +} + +$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; +$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; +$GLOBALS['_PEAR_destructor_object_list'] = array(); +$GLOBALS['_PEAR_shutdown_funcs'] = array(); +$GLOBALS['_PEAR_error_handler_stack'] = array(); + +@ini_set('track_errors', true); + +/** + * Base class for other PEAR classes. Provides rudimentary + * emulation of destructors. + * + * If you want a destructor in your class, inherit PEAR and make a + * destructor method called _yourclassname (same name as the + * constructor, but with a "_" prefix). Also, in your constructor you + * have to call the PEAR constructor: $this->PEAR();. + * The destructor method will be called without parameters. Note that + * at in some SAPI implementations (such as Apache), any output during + * the request shutdown (in which destructors are called) seems to be + * discarded. If you need to get any debug information from your + * destructor, use error_log(), syslog() or something similar. + * + * IMPORTANT! To use the emulated destructors you need to create the + * objects by reference: $obj =& new PEAR_child; + * + * @category pear + * @package PEAR + * @author Stig Bakken <ssb@php.net> + * @author Tomas V.V. Cox <cox@idecnet.com> + * @author Greg Beaver <cellog@php.net> + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @see PEAR_Error + * @since Class available since PHP 4.0.2 + * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear + */ +class PEAR +{ + /** + * Whether to enable internal debug messages. + * + * @var bool + * @access private + */ + var $_debug = false; + + /** + * Default error mode for this object. + * + * @var int + * @access private + */ + var $_default_error_mode = null; + + /** + * Default error options used for this object when error mode + * is PEAR_ERROR_TRIGGER. + * + * @var int + * @access private + */ + var $_default_error_options = null; + + /** + * Default error handler (callback) for this object, if error mode is + * PEAR_ERROR_CALLBACK. + * + * @var string + * @access private + */ + var $_default_error_handler = ''; + + /** + * Which class to use for error objects. + * + * @var string + * @access private + */ + var $_error_class = 'PEAR_Error'; + + /** + * An array of expected errors. + * + * @var array + * @access private + */ + var $_expected_errors = array(); + + /** + * Constructor. Registers this object in + * $_PEAR_destructor_object_list for destructor emulation if a + * destructor object exists. + * + * @param string $error_class (optional) which class to use for + * error objects, defaults to PEAR_Error. + * @access public + * @return void + */ + function PEAR($error_class = null) + { + $classname = strtolower(get_class($this)); + if ($this->_debug) { + print "PEAR constructor called, class=$classname\n"; + } + + if ($error_class !== null) { + $this->_error_class = $error_class; + } + + while ($classname && strcasecmp($classname, "pear")) { + $destructor = "_$classname"; + if (method_exists($this, $destructor)) { + global $_PEAR_destructor_object_list; + $_PEAR_destructor_object_list[] = &$this; + if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { + register_shutdown_function("_PEAR_call_destructors"); + $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; + } + break; + } else { + $classname = get_parent_class($classname); + } + } + } + + /** + * Destructor (the emulated type of...). Does nothing right now, + * but is included for forward compatibility, so subclass + * destructors should always call it. + * + * See the note in the class description about output from + * destructors. + * + * @access public + * @return void + */ + function _PEAR() { + if ($this->_debug) { + printf("PEAR destructor called, class=%s\n", strtolower(get_class($this))); + } + } + + /** + * If you have a class that's mostly/entirely static, and you need static + * properties, you can use this method to simulate them. Eg. in your method(s) + * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); + * You MUST use a reference, or they will not persist! + * + * @access public + * @param string $class The calling classname, to prevent clashes + * @param string $var The variable to retrieve. + * @return mixed A reference to the variable. If not set it will be + * auto initialised to NULL. + */ + function &getStaticProperty($class, $var) + { + static $properties; + if (!isset($properties[$class])) { + $properties[$class] = array(); + } + + if (!array_key_exists($var, $properties[$class])) { + $properties[$class][$var] = null; + } + + return $properties[$class][$var]; + } + + /** + * Use this function to register a shutdown method for static + * classes. + * + * @access public + * @param mixed $func The function name (or array of class/method) to call + * @param mixed $args The arguments to pass to the function + * @return void + */ + function registerShutdownFunc($func, $args = array()) + { + // if we are called statically, there is a potential + // that no shutdown func is registered. Bug #6445 + if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { + register_shutdown_function("_PEAR_call_destructors"); + $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; + } + $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); + } + + /** + * Tell whether a value is a PEAR error. + * + * @param mixed $data the value to test + * @param int $code if $data is an error object, return true + * only if $code is a string and + * $obj->getMessage() == $code or + * $code is an integer and $obj->getCode() == $code + * @access public + * @return bool true if parameter is an error + */ + function isError($data, $code = null) + { + if (!is_object($data)) { + return false; + } + if (!is_a($data, 'PEAR_Error')) { + return false; + } + + if (is_null($code)) { + return true; + } elseif (is_string($code)) { + return $data->getMessage() == $code; + } + + return $data->getCode() == $code; + } + + /** + * Sets how errors generated by this object should be handled. + * Can be invoked both in objects and statically. If called + * statically, setErrorHandling sets the default behaviour for all + * PEAR objects. If called in an object, setErrorHandling sets + * the default behaviour for that object. + * + * @param int $mode + * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. + * + * @param mixed $options + * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one + * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * + * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected + * to be the callback function or method. A callback + * function is a string with the name of the function, a + * callback method is an array of two elements: the element + * at index 0 is the object, and the element at index 1 is + * the name of the method to call in the object. + * + * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is + * a printf format string used when printing the error + * message. + * + * @access public + * @return void + * @see PEAR_ERROR_RETURN + * @see PEAR_ERROR_PRINT + * @see PEAR_ERROR_TRIGGER + * @see PEAR_ERROR_DIE + * @see PEAR_ERROR_CALLBACK + * @see PEAR_ERROR_EXCEPTION + * + * @since PHP 4.0.5 + */ + function setErrorHandling($mode = null, $options = null) + { + if (isset($this) && is_a($this, 'PEAR')) { + $setmode = &$this->_default_error_mode; + $setoptions = &$this->_default_error_options; + } else { + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + } + + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + } + + /** + * This method is used to tell which errors you expect to get. + * Expected errors are always returned with error mode + * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, + * and this method pushes a new element onto it. The list of + * expected errors are in effect until they are popped off the + * stack with the popExpect() method. + * + * Note that this method can not be called statically + * + * @param mixed $code a single error code or an array of error codes to expect + * + * @return int the new depth of the "expected errors" stack + * @access public + */ + function expectError($code = '*') + { + if (is_array($code)) { + array_push($this->_expected_errors, $code); + } else { + array_push($this->_expected_errors, array($code)); + } + return count($this->_expected_errors); + } + + /** + * This method pops one element off the expected error codes + * stack. + * + * @return array the list of error codes that were popped + */ + function popExpect() + { + return array_pop($this->_expected_errors); + } + + /** + * This method checks unsets an error code if available + * + * @param mixed error code + * @return bool true if the error code was unset, false otherwise + * @access private + * @since PHP 4.3.0 + */ + function _checkDelExpect($error_code) + { + $deleted = false; + foreach ($this->_expected_errors as $key => $error_array) { + if (in_array($error_code, $error_array)) { + unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); + $deleted = true; + } + + // clean up empty arrays + if (0 == count($this->_expected_errors[$key])) { + unset($this->_expected_errors[$key]); + } + } + + return $deleted; + } + + /** + * This method deletes all occurrences of the specified element from + * the expected error codes stack. + * + * @param mixed $error_code error code that should be deleted + * @return mixed list of error codes that were deleted or error + * @access public + * @since PHP 4.3.0 + */ + function delExpect($error_code) + { + $deleted = false; + if ((is_array($error_code) && (0 != count($error_code)))) { + // $error_code is a non-empty array here; we walk through it trying + // to unset all values + foreach ($error_code as $key => $error) { + $deleted = $this->_checkDelExpect($error) ? true : false; + } + + return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } elseif (!empty($error_code)) { + // $error_code comes alone, trying to unset it + if ($this->_checkDelExpect($error_code)) { + return true; + } + + return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } + + // $error_code is empty + return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME + } + + /** + * This method is a wrapper that returns an instance of the + * configured error class with this object's default error + * handling applied. If the $mode and $options parameters are not + * specified, the object's defaults are used. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION. + * + * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter + * specifies the PHP-internal error level (one of + * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * If $mode is PEAR_ERROR_CALLBACK, this + * parameter specifies the callback function or + * method. In other error modes this parameter + * is ignored. + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @param string $error_class The returned error object will be + * instantiated from this class, if specified. + * + * @param bool $skipmsg If true, raiseError will only pass error codes, + * the error message parameter will be dropped. + * + * @access public + * @return object a PEAR error object + * @see PEAR::setErrorHandling + * @since PHP 4.0.5 + */ + function &raiseError($message = null, + $code = null, + $mode = null, + $options = null, + $userinfo = null, + $error_class = null, + $skipmsg = false) + { + // The error is yet a PEAR error object + if (is_object($message)) { + $code = $message->getCode(); + $userinfo = $message->getUserInfo(); + $error_class = $message->getType(); + $message->error_message_prefix = ''; + $message = $message->getMessage(); + + // Make sure right data gets passed. + $r = new ReflectionClass($error_class); + $c = $r->getConstructor(); + $p = array_shift($c->getParameters()); + $skipmsg = ($p->getName() != 'message'); + } + + if ( + isset($this) && + isset($this->_expected_errors) && + count($this->_expected_errors) > 0 && + count($exp = end($this->_expected_errors)) + ) { + if ($exp[0] == "*" || + (is_int(reset($exp)) && in_array($code, $exp)) || + (is_string(reset($exp)) && in_array($message, $exp)) + ) { + $mode = PEAR_ERROR_RETURN; + } + } + + // No mode given, try global ones + if ($mode === null) { + // Class error handler + if (isset($this) && isset($this->_default_error_mode)) { + $mode = $this->_default_error_mode; + $options = $this->_default_error_options; + // Global error handler + } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { + $mode = $GLOBALS['_PEAR_default_error_mode']; + $options = $GLOBALS['_PEAR_default_error_options']; + } + } + + if ($error_class !== null) { + $ec = $error_class; + } elseif (isset($this) && isset($this->_error_class)) { + $ec = $this->_error_class; + } else { + $ec = 'PEAR_Error'; + } + + if (intval(PHP_VERSION) < 5) { + // little non-eval hack to fix bug #12147 + include 'PEAR/FixPHP5PEARWarnings.php'; + return $a; + } + + if ($skipmsg) { + $a = new $ec($code, $mode, $options, $userinfo); + } else { + $a = new $ec($message, $code, $mode, $options, $userinfo); + } + + return $a; + } + + /** + * Simpler form of raiseError with fewer options. In most cases + * message, code and userinfo are enough. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @access public + * @return object a PEAR error object + * @see PEAR::raiseError + */ + function &throwError($message = null, $code = null, $userinfo = null) + { + if (isset($this) && is_a($this, 'PEAR')) { + $a = &$this->raiseError($message, $code, null, null, $userinfo); + return $a; + } + + $a = &PEAR::raiseError($message, $code, null, null, $userinfo); + return $a; + } + + function staticPushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + $stack[] = array($def_mode, $def_options); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $def_mode = $mode; + $def_options = $options; + break; + + case PEAR_ERROR_CALLBACK: + $def_mode = $mode; + // class/object method callback + if (is_callable($options)) { + $def_options = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + $stack[] = array($mode, $options); + return true; + } + + function staticPopErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + return true; + } + + /** + * Push a new error handler on top of the error handler options stack. With this + * you can easily override the actual error handler for some code and restore + * it later with popErrorHandling. + * + * @param mixed $mode (same as setErrorHandling) + * @param mixed $options (same as setErrorHandling) + * + * @return bool Always true + * + * @see PEAR::setErrorHandling + */ + function pushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + if (isset($this) && is_a($this, 'PEAR')) { + $def_mode = &$this->_default_error_mode; + $def_options = &$this->_default_error_options; + } else { + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + } + $stack[] = array($def_mode, $def_options); + + if (isset($this) && is_a($this, 'PEAR')) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + $stack[] = array($mode, $options); + return true; + } + + /** + * Pop the last error handler used + * + * @return bool Always true + * + * @see PEAR::pushErrorHandling + */ + function popErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + if (isset($this) && is_a($this, 'PEAR')) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + return true; + } + + /** + * OS independent PHP extension load. Remember to take care + * on the correct extension name for case sensitive OSes. + * + * @param string $ext The extension name + * @return bool Success or not on the dl() call + */ + static function loadExtension($ext) + { + if (extension_loaded($ext)) { + return true; + } + + // if either returns true dl() will produce a FATAL error, stop that + if ( + function_exists('dl') === false || + ini_get('enable_dl') != 1 || + ini_get('safe_mode') == 1 + ) { + return false; + } + + if (OS_WINDOWS) { + $suffix = '.dll'; + } elseif (PHP_OS == 'HP-UX') { + $suffix = '.sl'; + } elseif (PHP_OS == 'AIX') { + $suffix = '.a'; + } elseif (PHP_OS == 'OSX') { + $suffix = '.bundle'; + } else { + $suffix = '.so'; + } + + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + } +} + +if (PEAR_ZE2) { + /** + * This is only meant for PHP 5 to get rid of certain strict warning + * that doesn't get hidden since it's in the shutdown function + */ + class PEAR5 + { + /** + * If you have a class that's mostly/entirely static, and you need static + * properties, you can use this method to simulate them. Eg. in your method(s) + * do this: $myVar = &PEAR5::getStaticProperty('myclass', 'myVar'); + * You MUST use a reference, or they will not persist! + * + * @access public + * @param string $class The calling classname, to prevent clashes + * @param string $var The variable to retrieve. + * @return mixed A reference to the variable. If not set it will be + * auto initialised to NULL. + */ + static function &getStaticProperty($class, $var) + { + static $properties; + if (!isset($properties[$class])) { + $properties[$class] = array(); + } + + if (!array_key_exists($var, $properties[$class])) { + $properties[$class][$var] = null; + } + + return $properties[$class][$var]; + } + } +} + +function _PEAR_call_destructors() +{ + global $_PEAR_destructor_object_list; + if (is_array($_PEAR_destructor_object_list) && + sizeof($_PEAR_destructor_object_list)) + { + reset($_PEAR_destructor_object_list); + if (PEAR_ZE2) { + $destructLifoExists = PEAR5::getStaticProperty('PEAR', 'destructlifo'); + } else { + $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo'); + } + + if ($destructLifoExists) { + $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list); + } + + while (list($k, $objref) = each($_PEAR_destructor_object_list)) { + $classname = get_class($objref); + while ($classname) { + $destructor = "_$classname"; + if (method_exists($objref, $destructor)) { + $objref->$destructor(); + break; + } else { + $classname = get_parent_class($classname); + } + } + } + // Empty the object list to ensure that destructors are + // not called more than once. + $_PEAR_destructor_object_list = array(); + } + + // Now call the shutdown functions + if ( + isset($GLOBALS['_PEAR_shutdown_funcs']) && + is_array($GLOBALS['_PEAR_shutdown_funcs']) && + !empty($GLOBALS['_PEAR_shutdown_funcs']) + ) { + foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { + call_user_func_array($value[0], $value[1]); + } + } +} + +/** + * Standard PEAR error class for PHP 4 + * + * This class is superseded by {@link PEAR_Exception} in PHP 5 + * + * @category pear + * @package PEAR + * @author Stig Bakken <ssb@php.net> + * @author Tomas V.V. Cox <cox@idecnet.com> + * @author Gregory Beaver <cellog@php.net> + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/manual/en/core.pear.pear-error.php + * @see PEAR::raiseError(), PEAR::throwError() + * @since Class available since PHP 4.0.2 + */ +class PEAR_Error +{ + var $error_message_prefix = ''; + var $mode = PEAR_ERROR_RETURN; + var $level = E_USER_NOTICE; + var $code = -1; + var $message = ''; + var $userinfo = ''; + var $backtrace = null; + + /** + * PEAR_Error constructor + * + * @param string $message message + * + * @param int $code (optional) error code + * + * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN, + * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION + * + * @param mixed $options (optional) error level, _OR_ in the case of + * PEAR_ERROR_CALLBACK, the callback function or object/method + * tuple. + * + * @param string $userinfo (optional) additional user/debug info + * + * @access public + * + */ + function PEAR_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + if ($mode === null) { + $mode = PEAR_ERROR_RETURN; + } + $this->message = $message; + $this->code = $code; + $this->mode = $mode; + $this->userinfo = $userinfo; + + if (PEAR_ZE2) { + $skiptrace = PEAR5::getStaticProperty('PEAR_Error', 'skiptrace'); + } else { + $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace'); + } + + if (!$skiptrace) { + $this->backtrace = debug_backtrace(); + if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) { + unset($this->backtrace[0]['object']); + } + } + + if ($mode & PEAR_ERROR_CALLBACK) { + $this->level = E_USER_NOTICE; + $this->callback = $options; + } else { + if ($options === null) { + $options = E_USER_NOTICE; + } + + $this->level = $options; + $this->callback = null; + } + + if ($this->mode & PEAR_ERROR_PRINT) { + if (is_null($options) || is_int($options)) { + $format = "%s"; + } else { + $format = $options; + } + + printf($format, $this->getMessage()); + } + + if ($this->mode & PEAR_ERROR_TRIGGER) { + trigger_error($this->getMessage(), $this->level); + } + + if ($this->mode & PEAR_ERROR_DIE) { + $msg = $this->getMessage(); + if (is_null($options) || is_int($options)) { + $format = "%s"; + if (substr($msg, -1) != "\n") { + $msg .= "\n"; + } + } else { + $format = $options; + } + die(sprintf($format, $msg)); + } + + if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) { + call_user_func($this->callback, $this); + } + + if ($this->mode & PEAR_ERROR_EXCEPTION) { + trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING); + eval('$e = new Exception($this->message, $this->code);throw($e);'); + } + } + + /** + * Get the error mode from an error object. + * + * @return int error mode + * @access public + */ + function getMode() + { + return $this->mode; + } + + /** + * Get the callback function/method from an error object. + * + * @return mixed callback function or object/method array + * @access public + */ + function getCallback() + { + return $this->callback; + } + + /** + * Get the error message from an error object. + * + * @return string full error message + * @access public + */ + function getMessage() + { + return ($this->error_message_prefix . $this->message); + } + + /** + * Get error code from an error object + * + * @return int error code + * @access public + */ + function getCode() + { + return $this->code; + } + + /** + * Get the name of this error/exception. + * + * @return string error/exception name (type) + * @access public + */ + function getType() + { + return get_class($this); + } + + /** + * Get additional user-supplied information. + * + * @return string user-supplied information + * @access public + */ + function getUserInfo() + { + return $this->userinfo; + } + + /** + * Get additional debug information supplied by the application. + * + * @return string debug information + * @access public + */ + function getDebugInfo() + { + return $this->getUserInfo(); + } + + /** + * Get the call backtrace from where the error was generated. + * Supported with PHP 4.3.0 or newer. + * + * @param int $frame (optional) what frame to fetch + * @return array Backtrace, or NULL if not available. + * @access public + */ + function getBacktrace($frame = null) + { + if (defined('PEAR_IGNORE_BACKTRACE')) { + return null; + } + if ($frame === null) { + return $this->backtrace; + } + return $this->backtrace[$frame]; + } + + function addUserInfo($info) + { + if (empty($this->userinfo)) { + $this->userinfo = $info; + } else { + $this->userinfo .= " ** $info"; + } + } + + function __toString() + { + return $this->getMessage(); + } + + /** + * Make a string representation of this object. + * + * @return string a string with an object summary + * @access public + */ + function toString() + { + $modes = array(); + $levels = array(E_USER_NOTICE => 'notice', + E_USER_WARNING => 'warning', + E_USER_ERROR => 'error'); + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_array($this->callback)) { + $callback = (is_object($this->callback[0]) ? + strtolower(get_class($this->callback[0])) : + $this->callback[0]) . '::' . + $this->callback[1]; + } else { + $callback = $this->callback; + } + return sprintf('[%s: message="%s" code=%d mode=callback '. + 'callback=%s prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + $callback, $this->error_message_prefix, + $this->userinfo); + } + if ($this->mode & PEAR_ERROR_PRINT) { + $modes[] = 'print'; + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + $modes[] = 'trigger'; + } + if ($this->mode & PEAR_ERROR_DIE) { + $modes[] = 'die'; + } + if ($this->mode & PEAR_ERROR_RETURN) { + $modes[] = 'return'; + } + return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. + 'prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + implode("|", $modes), $levels[$this->level], + $this->error_message_prefix, + $this->userinfo); + } +} + +/* + * Local Variables: + * mode: php + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/src/etc/inc/auth.inc b/src/etc/inc/auth.inc new file mode 100644 index 0000000..ad98c5c --- /dev/null +++ b/src/etc/inc/auth.inc @@ -0,0 +1,1643 @@ +<?php +/* $Id$ */ +/* + Copyright (C) 2010 Ermal Luçi + All rights reserved. + + Copyright (C) 2007, 2008 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Copyright (C) 2005-2006 Bill Marquette <bill.marquette@gmail.com> + All rights reserved. + + Copyright (C) 2006 Paul Taylor <paultaylor@winn-dixie.com>. + All rights reserved. + + Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_BUILDER_BINARIES: /usr/sbin/pw /bin/cp + pfSense_MODULE: auth +*/ + +/* + * NOTE : Portions of the mschapv2 support was based on the BSD licensed CHAP.php + * file courtesy of Michael Retterklieber. + */ +if (!$do_not_include_config_gui_inc) { + require_once("config.gui.inc"); +} + +// Will be changed to false if security checks fail +$security_passed = true; + +/* If this function doesn't exist, we're being called from Captive Portal or + another internal subsystem which does not include authgui.inc */ +if (function_exists("display_error_form") && !isset($config['system']['webgui']['nodnsrebindcheck'])) { + /* DNS ReBinding attack prevention. https://redmine.pfsense.org/issues/708 */ + $found_host = false; + + /* Either a IPv6 address with or without a alternate port */ + if (strstr($_SERVER['HTTP_HOST'], "]")) { + $http_host_port = explode("]", $_SERVER['HTTP_HOST']); + /* v6 address has more parts, drop the last part */ + if (count($http_host_port) > 1) { + array_pop($http_host_port); + $http_host = str_replace(array("[", "]"), "", implode(":", $http_host_port)); + } else { + $http_host = str_replace(array("[", "]"), "", implode(":", $http_host_port)); + } + } else { + $http_host = explode(":", $_SERVER['HTTP_HOST']); + $http_host = $http_host[0]; + } + if (is_ipaddr($http_host) or $_SERVER['SERVER_ADDR'] == "127.0.0.1" or + strcasecmp($http_host, "localhost") == 0 or $_SERVER['SERVER_ADDR'] == "::1") { + $found_host = true; + } + if (strcasecmp($http_host, $config['system']['hostname'] . "." . $config['system']['domain']) == 0 or + strcasecmp($http_host, $config['system']['hostname']) == 0) { + $found_host = true; + } + + if (is_array($config['dyndnses']['dyndns']) && !$found_host) { + foreach ($config['dyndnses']['dyndns'] as $dyndns) { + if (strcasecmp($dyndns['host'], $http_host) == 0) { + $found_host = true; + break; + } + } + } + + if (is_array($config['dnsupdates']['dnsupdate']) && !$found_host) { + foreach ($config['dnsupdates']['dnsupdate'] as $rfc2136) { + if (strcasecmp($rfc2136['host'], $http_host) == 0) { + $found_host = true; + break; + } + } + } + + if (!empty($config['system']['webgui']['althostnames']) && !$found_host) { + $althosts = explode(" ", $config['system']['webgui']['althostnames']); + foreach ($althosts as $ah) { + if (strcasecmp($ah, $http_host) == 0 or strcasecmp($ah, $_SERVER['SERVER_ADDR']) == 0) { + $found_host = true; + break; + } + } + } + + if ($found_host == false) { + if (!security_checks_disabled()) { + display_error_form("501", gettext("Potential DNS Rebind attack detected, see http://en.wikipedia.org/wiki/DNS_rebinding<br />Try accessing the router by IP address instead of by hostname.")); + exit; + } + $security_passed = false; + } +} + +// If the HTTP_REFERER is something other than ourselves then disallow. +if (function_exists("display_error_form") && !isset($config['system']['webgui']['nohttpreferercheck'])) { + if ($_SERVER['HTTP_REFERER']) { + if (file_exists("{$g['tmp_path']}/setupwizard_lastreferrer")) { + if ($_SERVER['HTTP_REFERER'] == file_get_contents("{$g['tmp_path']}/setupwizard_lastreferrer")) { + unlink("{$g['tmp_path']}/setupwizard_lastreferrer"); + header("Refresh: 1; url=index.php"); +?> +<!DOCTYPE html> +<html lang="en"> +<head> + <link rel="stylesheet" href="/bootstrap/css/pfSense.css" /> + <title><?=gettext("Redirecting..."); ?></title> +</head> +<body id="error" class="no-menu"> + <div id="jumbotron"> + <div class="container"> + <div class="col-sm-offset-3 col-sm-6 col-xs-12"> + <p><?=gettext("Redirecting to the dashboard...")?></p> + </div> + </div> + </div> +</body> +</html> +<?php + exit; + } + } + $found_host = false; + $referrer_host = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST); + $referrer_host = str_replace(array("[", "]"), "", $referrer_host); + if ($referrer_host) { + if (strcasecmp($referrer_host, $config['system']['hostname'] . "." . $config['system']['domain']) == 0 || + strcasecmp($referrer_host, $config['system']['hostname']) == 0) { + $found_host = true; + } + + if (!empty($config['system']['webgui']['althostnames']) && !$found_host) { + $althosts = explode(" ", $config['system']['webgui']['althostnames']); + foreach ($althosts as $ah) { + if (strcasecmp($referrer_host, $ah) == 0) { + $found_host = true; + break; + } + } + } + + if (is_array($config['dyndnses']['dyndns']) && !$found_host) { + foreach ($config['dyndnses']['dyndns'] as $dyndns) { + if (strcasecmp($dyndns['host'], $referrer_host) == 0) { + $found_host = true; + break; + } + } + } + + if (is_array($config['dnsupdates']['dnsupdate']) && !$found_host) { + foreach ($config['dnsupdates']['dnsupdate'] as $rfc2136) { + if (strcasecmp($rfc2136['host'], $referrer_host) == 0) { + $found_host = true; + break; + } + } + } + + if (!$found_host) { + $interface_list_ips = get_configured_ip_addresses(); + foreach ($interface_list_ips as $ilips) { + if (strcasecmp($referrer_host, $ilips) == 0) { + $found_host = true; + break; + } + } + $interface_list_ipv6s = get_configured_ipv6_addresses(); + foreach ($interface_list_ipv6s as $ilipv6s) { + if (strcasecmp($referrer_host, $ilipv6s) == 0) { + $found_host = true; + break; + } + } + if ($referrer_host == "127.0.0.1" || $referrer_host == "localhost") { + // allow SSH port forwarded connections and links from localhost + $found_host = true; + } + } + } + if ($found_host == false) { + if (!security_checks_disabled()) { + display_error_form("501", "An HTTP_REFERER was detected other than what is defined in System -> Advanced (" . htmlspecialchars($_SERVER['HTTP_REFERER']) . "). You can disable this check if needed in System -> Advanced -> Admin."); + exit; + } + $security_passed = false; + } + } else { + $security_passed = false; + } +} + +if (function_exists("display_error_form") && $security_passed) { + /* Security checks passed, so it should be OK to turn them back on */ + restore_security_checks(); +} +unset($security_passed); + +$groupindex = index_groups(); +$userindex = index_users(); + +function index_groups() { + global $g, $debug, $config, $groupindex; + + $groupindex = array(); + + if (is_array($config['system']['group'])) { + $i = 0; + foreach ($config['system']['group'] as $groupent) { + $groupindex[$groupent['name']] = $i; + $i++; + } + } + + return ($groupindex); +} + +function index_users() { + global $g, $debug, $config; + + if (is_array($config['system']['user'])) { + $i = 0; + foreach ($config['system']['user'] as $userent) { + $userindex[$userent['name']] = $i; + $i++; + } + } + + return ($userindex); +} + +function & getUserEntry($name) { + global $debug, $config, $userindex; + if (isset($userindex[$name])) { + return $config['system']['user'][$userindex[$name]]; + } +} + +function & getUserEntryByUID($uid) { + global $debug, $config; + + if (is_array($config['system']['user'])) { + foreach ($config['system']['user'] as & $user) { + if ($user['uid'] == $uid) { + return $user; + } + } + } + + return false; +} + +function & getGroupEntry($name) { + global $debug, $config, $groupindex; + if (isset($groupindex[$name])) { + return $config['system']['group'][$groupindex[$name]]; + } +} + +function & getGroupEntryByGID($gid) { + global $debug, $config; + + if (is_array($config['system']['group'])) { + foreach ($config['system']['group'] as & $group) { + if ($group['gid'] == $gid) { + return $group; + } + } + } + + return false; +} + +function get_user_privileges(& $user) { + + $privs = $user['priv']; + if (!is_array($privs)) { + $privs = array(); + } + + $names = local_user_get_groups($user, true); + + foreach ($names as $name) { + $group = getGroupEntry($name); + if (is_array($group['priv'])) { + $privs = array_merge( $privs, $group['priv']); + } + } + + return $privs; +} + +function userHasPrivilege($userent, $privid = false) { + + if (!$privid || !is_array($userent)) { + return false; + } + + $privs = get_user_privileges($userent); + + if (!is_array($privs)) { + return false; + } + + if (!in_array($privid, $privs)) { + return false; + } + + return true; +} + +function local_backed($username, $passwd) { + + $user = getUserEntry($username); + if (!$user) { + return false; + } + + if (is_account_disabled($username) || is_account_expired($username)) { + return false; + } + + if ($user['password']) { + if (crypt($passwd, $user['password']) == $user['password']) { + return true; + } + } + + if ($user['md5-hash']) { + if (md5($passwd) == $user['md5-hash']) { + return true; + } + } + + return false; +} + +function local_sync_accounts() { + global $debug, $config; + conf_mount_rw(); + + /* remove local users to avoid uid conflicts */ + $fd = popen("/usr/sbin/pw usershow -a", "r"); + if ($fd) { + while (!feof($fd)) { + $line = explode(":",fgets($fd)); + if (((!strncmp($line[0], "_", 1)) || ($line[2] < 2000) || ($line[2] > 65000)) && ($line[0] != "admin")) { + continue; + } + /* + * If a crontab was created to user, pw userdel will be interactive and + * can cause issues. Just remove crontab before run it when necessary + */ + unlink_if_exists("/var/cron/tabs/{$line[0]}"); + $cmd = "/usr/sbin/pw userdel -n '{$line[0]}'"; + if ($debug) { + log_error(sprintf(gettext("Running: %s"), $cmd)); + } + mwexec($cmd); + } + pclose($fd); + } + + /* remove local groups to avoid gid conflicts */ + $gids = array(); + $fd = popen("/usr/sbin/pw groupshow -a", "r"); + if ($fd) { + while (!feof($fd)) { + $line = explode(":",fgets($fd)); + if (!strncmp($line[0], "_", 1)) { + continue; + } + if ($line[2] < 2000) { + continue; + } + if ($line[2] > 65000) { + continue; + } + $cmd = "/usr/sbin/pw groupdel {$line[2]}"; + if ($debug) { + log_error(sprintf(gettext("Running: %s"), $cmd)); + } + mwexec($cmd); + } + pclose($fd); + } + + /* make sure the all group exists */ + $allgrp = getGroupEntryByGID(1998); + local_group_set($allgrp, true); + + /* sync all local users */ + if (is_array($config['system']['user'])) { + foreach ($config['system']['user'] as $user) { + local_user_set($user); + } + } + + /* sync all local groups */ + if (is_array($config['system']['group'])) { + foreach ($config['system']['group'] as $group) { + local_group_set($group); + } + } + + conf_mount_ro(); + +} + +function local_user_set(& $user) { + global $g, $debug; + + if (empty($user['password'])) { + log_error("There is something wrong in your config because user {$user['name']} password is missing!"); + return; + } + + conf_mount_rw(); + + $home_base = "/home/"; + $user_uid = $user['uid']; + $user_name = $user['name']; + $user_home = "{$home_base}{$user_name}"; + $user_shell = "/etc/rc.initial"; + $user_group = "nobody"; + + // Ensure $home_base exists and is writable + if (!is_dir($home_base)) { + mkdir($home_base, 0755); + } + + $lock_account = false; + /* configure shell type */ + /* Cases here should be ordered by most privileged to least privileged. */ + if (userHasPrivilege($user, "user-shell-access") || userHasPrivilege($user, "page-all")) { + $user_shell = "/bin/tcsh"; + } elseif (userHasPrivilege($user, "user-copy-files")) { + $user_shell = "/usr/local/bin/scponly"; + } elseif (userHasPrivilege($user, "user-ssh-tunnel")) { + $user_shell = "/usr/local/sbin/ssh_tunnel_shell"; + } elseif (userHasPrivilege($user, "user-ipsec-xauth-dialin")) { + $user_shell = "/sbin/nologin"; + } else { + $user_shell = "/sbin/nologin"; + $lock_account = true; + } + + /* Lock out disabled or expired users, unless it's root/admin. */ + if ((is_account_disabled($user_name) || is_account_expired($user_name)) && ($user_uid != 0)) { + $user_shell = "/sbin/nologin"; + $lock_account = true; + } + + /* root user special handling */ + if ($user_uid == 0) { + $cmd = "/usr/sbin/pw usermod -q -n root -s /bin/sh -H 0"; + if ($debug) { + log_error(sprintf(gettext("Running: %s"), $cmd)); + } + $fd = popen($cmd, "w"); + fwrite($fd, $user['password']); + pclose($fd); + $user_group = "wheel"; + $user_home = "/root"; + $user_shell = "/etc/rc.initial"; + } + + /* read from pw db */ + $fd = popen("/usr/sbin/pw usershow -n {$user_name} 2>&1", "r"); + $pwread = fgets($fd); + pclose($fd); + $userattrs = explode(":", trim($pwread)); + + /* determine add or mod */ + if (($userattrs[0] != $user['name']) || (!strncmp($pwread, "pw:", 3))) { + $user_op = "useradd -m -k /etc/skel -o"; + } else { + $user_op = "usermod"; + } + + $comment = str_replace(array(":", "!", "@"), " ", $user['descr']); + /* add or mod pw db */ + $cmd = "/usr/sbin/pw {$user_op} -q -u {$user_uid} -n {$user_name}". + " -g {$user_group} -s {$user_shell} -d {$user_home}". + " -c ".escapeshellarg($comment)." -H 0 2>&1"; + + if ($debug) { + log_error(sprintf(gettext("Running: %s"), $cmd)); + } + $fd = popen($cmd, "w"); + fwrite($fd, $user['password']); + pclose($fd); + + /* create user directory if required */ + if (!is_dir($user_home)) { + mkdir($user_home, 0700); + } + @chown($user_home, $user_name); + @chgrp($user_home, $user_group); + + /* write out ssh authorized key file */ + if ($user['authorizedkeys']) { + if (!is_dir("{$user_home}/.ssh")) { + @mkdir("{$user_home}/.ssh", 0700); + @chown("{$user_home}/.ssh", $user_name); + } + $keys = base64_decode($user['authorizedkeys']); + @file_put_contents("{$user_home}/.ssh/authorized_keys", $keys); + @chown("{$user_home}/.ssh/authorized_keys", $user_name); + } else { + unlink_if_exists("{$user_home}/.ssh/authorized_keys"); + } + + $un = $lock_account ? "" : "un"; + exec("/usr/sbin/pw {$un}lock {$user_name} -q"); + + conf_mount_ro(); +} + +function local_user_del($user) { + global $debug; + + /* remove all memberships */ + local_user_set_groups($user); + + /* Don't remove /root */ + if ($user['uid'] != 0) { + $rmhome = "-r"; + } + + /* read from pw db */ + $fd = popen("/usr/sbin/pw usershow -n {$user['name']} 2>&1", "r"); + $pwread = fgets($fd); + pclose($fd); + $userattrs = explode(":", trim($pwread)); + + if ($userattrs[0] != $user['name']) { + log_error("Tried to remove user {$user['name']} but got user {$userattrs[0]} instead. Bailing."); + return; + } + + /* delete from pw db */ + $cmd = "/usr/sbin/pw userdel -n {$user['name']} {$rmhome}"; + + if ($debug) { + log_error(sprintf(gettext("Running: %s"), $cmd)); + } + mwexec($cmd); + + /* Delete user from groups needs a call to write_config() */ + local_group_del_user($user); +} + +function local_user_set_password(&$user, $password) { + + $user['password'] = crypt($password); + $user['md5-hash'] = md5($password); + + // Converts ascii to unicode. + $astr = (string) $password; + $ustr = ''; + for ($i = 0; $i < strlen($astr); $i++) { + $a = ord($astr{$i}) << 8; + $ustr.= sprintf("%X", $a); + } + +} + +function local_user_get_groups($user, $all = false) { + global $debug, $config; + + $groups = array(); + if (!is_array($config['system']['group'])) { + return $groups; + } + + foreach ($config['system']['group'] as $group) { + if ( $all || ( !$all && ($group['name'] != "all"))) { + if (is_array($group['member'])) { + if (in_array($user['uid'], $group['member'])) { + $groups[] = $group['name']; + } + } + } + } + + if ($all) { + $groups[] = "all"; + } + + sort($groups); + + return $groups; + +} + +function local_user_set_groups($user, $new_groups = NULL ) { + global $debug, $config, $groupindex; + + if (!is_array($config['system']['group'])) { + return; + } + + $cur_groups = local_user_get_groups($user, true); + $mod_groups = array(); + + if (!is_array($new_groups)) { + $new_groups = array(); + } + + if (!is_array($cur_groups)) { + $cur_groups = array(); + } + + /* determine which memberships to add */ + foreach ($new_groups as $groupname) { + if ($groupname == '' || in_array($groupname,$cur_groups)) { + continue; + } + $group = & $config['system']['group'][$groupindex[$groupname]]; + $group['member'][] = $user['uid']; + $mod_groups[] = $group; + } + unset($group); + + /* determine which memberships to remove */ + foreach ($cur_groups as $groupname) { + if (in_array($groupname,$new_groups)) { + continue; + } + if (!isset($config['system']['group'][$groupindex[$groupname]])) { + continue; + } + $group = & $config['system']['group'][$groupindex[$groupname]]; + if (is_array($group['member'])) { + $index = array_search($user['uid'], $group['member']); + array_splice($group['member'], $index, 1); + $mod_groups[] = $group; + } + } + unset($group); + + /* sync all modified groups */ + foreach ($mod_groups as $group) { + local_group_set($group); + } +} + +function local_group_del_user($user) { + global $config; + + if (!is_array($config['system']['group'])) { + return; + } + + foreach ($config['system']['group'] as $group) { + if (is_array($group['member'])) { + foreach ($group['member'] as $idx => $uid) { + if ($user['uid'] == $uid) { + unset($config['system']['group']['member'][$idx]); + } + } + } + } +} + +function local_group_set($group, $reset = false) { + global $debug; + + $group_name = $group['name']; + $group_gid = $group['gid']; + $group_members = ''; + if (!$reset && !empty($group['member']) && count($group['member']) > 0) { + $group_members = implode(",",$group['member']); + } + + if (empty($group_name)) { + return; + } + + /* read from group db */ + $fd = popen("/usr/sbin/pw groupshow {$group_name} 2>&1", "r"); + $pwread = fgets($fd); + pclose($fd); + + /* determine add or mod */ + if (!strncmp($pwread, "pw:", 3)) { + $group_op = "groupadd"; + } else { + $group_op = "groupmod"; + } + + /* add or mod group db */ + $cmd = "/usr/sbin/pw {$group_op} {$group_name} -g {$group_gid} -M '{$group_members}' 2>&1"; + + if ($debug) { + log_error(sprintf(gettext("Running: %s"), $cmd)); + } + mwexec($cmd); + +} + +function local_group_del($group) { + global $debug; + + /* delete from group db */ + $cmd = "/usr/sbin/pw groupdel {$group['name']}"; + + if ($debug) { + log_error(sprintf(gettext("Running: %s"), $cmd)); + } + mwexec($cmd); +} + +function ldap_test_connection($authcfg) { + global $debug, $config, $g; + + if ($authcfg) { + if (strstr($authcfg['ldap_urltype'], "Standard")) { + $ldapproto = "ldap"; + } else { + $ldapproto = "ldaps"; + } + $ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']); + $ldapport = $authcfg['ldap_port']; + if (!empty($ldapport)) { + $ldapserver .= ":{$ldapport}"; + } + $ldapbasedn = $authcfg['ldap_basedn']; + $ldapbindun = $authcfg['ldap_binddn']; + $ldapbindpw = $authcfg['ldap_bindpw']; + } else { + return false; + } + + /* first check if there is even an LDAP server populated */ + if ( !$ldapserver) { + return false; + } + + /* Setup CA environment if needed. */ + ldap_setup_caenv($authcfg); + + /* connect and see if server is up */ + $error = false; + if (!($ldap = ldap_connect($ldapserver))) { + $error = true; + } + + if ($error == true) { + log_error(sprintf(gettext("ERROR! Could not connect to server %s."), $ldapname)); + return false; + } + + return true; +} + +function ldap_setup_caenv($authcfg) { + global $g; + require_once("certs.inc"); + + unset($caref); + if (empty($authcfg['ldap_caref']) || !strstr($authcfg['ldap_urltype'], "SSL")) { + putenv('LDAPTLS_REQCERT=never'); + return; + } else { + $caref = lookup_ca($authcfg['ldap_caref']); + if (!$caref) { + log_error(sprintf(gettext("LDAP: Could not lookup CA by reference for host %s."), $authcfg['ldap_caref'])); + /* XXX: Prevent for credential leaking since we cannot setup the CA env. Better way? */ + putenv('LDAPTLS_REQCERT=hard'); + return; + } + if (!is_dir("{$g['varrun_path']}/certs")) { + @mkdir("{$g['varrun_path']}/certs"); + } + if (file_exists("{$g['varrun_path']}/certs/{$caref['refid']}.ca")) { + @unlink("{$g['varrun_path']}/certs/{$caref['refid']}.ca"); + } + file_put_contents("{$g['varrun_path']}/certs/{$caref['refid']}.ca", base64_decode($caref['crt'])); + @chmod("{$g['varrun_path']}/certs/{$caref['refid']}.ca", 0600); + putenv('LDAPTLS_REQCERT=hard'); + /* XXX: Probably even the hashed link should be created for this? */ + putenv("LDAPTLS_CACERTDIR={$g['varrun_path']}/certs"); + putenv("LDAPTLS_CACERT={$g['varrun_path']}/certs/{$caref['refid']}.ca"); + } +} + +function ldap_test_bind($authcfg) { + global $debug, $config, $g; + + if ($authcfg) { + if (strstr($authcfg['ldap_urltype'], "Standard")) { + $ldapproto = "ldap"; + } else { + $ldapproto = "ldaps"; + } + $ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']); + $ldapport = $authcfg['ldap_port']; + if (!empty($ldapport)) { + $ldapserver .= ":{$ldapport}"; + } + $ldapbasedn = $authcfg['ldap_basedn']; + $ldapbindun = $authcfg['ldap_binddn']; + $ldapbindpw = $authcfg['ldap_bindpw']; + $ldapver = $authcfg['ldap_protver']; + if (empty($ldapbndun) || empty($ldapbindpw)) { + $ldapanon = true; + } else { + $ldapanon = false; + } + } else { + return false; + } + + /* first check if there is even an LDAP server populated */ + if (!$ldapserver) { + return false; + } + + /* Setup CA environment if needed. */ + ldap_setup_caenv($authcfg); + + /* connect and see if server is up */ + $error = false; + if (!($ldap = ldap_connect($ldapserver))) { + $error = true; + } + + if ($error == true) { + log_error(sprintf(gettext("ERROR! Could not connect to server %s."), $ldapname)); + return false; + } + + ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); + ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING); + ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver); + + $ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun; + $ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw; + if ($ldapanon == true) { + if (!($res = @ldap_bind($ldap))) { + @ldap_close($ldap); + return false; + } + } else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) { + @ldap_close($ldap); + return false; + } + + @ldap_unbind($ldap); + + return true; +} + +function ldap_get_user_ous($show_complete_ou=true, $authcfg) { + global $debug, $config, $g; + + if (!function_exists("ldap_connect")) { + return; + } + + $ous = array(); + + if ($authcfg) { + if (strstr($authcfg['ldap_urltype'], "Standard")) { + $ldapproto = "ldap"; + } else { + $ldapproto = "ldaps"; + } + $ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']); + $ldapport = $authcfg['ldap_port']; + if (!empty($ldapport)) { + $ldapserver .= ":{$ldapport}"; + } + $ldapbasedn = $authcfg['ldap_basedn']; + $ldapbindun = $authcfg['ldap_binddn']; + $ldapbindpw = $authcfg['ldap_bindpw']; + $ldapver = $authcfg['ldap_protver']; + if (empty($ldapbindun) || empty($ldapbindpw)) { + $ldapanon = true; + } else { + $ldapanon = false; + } + $ldapname = $authcfg['name']; + $ldapfallback = false; + $ldapscope = $authcfg['ldap_scope']; + } else { + return false; + } + + /* first check if there is even an LDAP server populated */ + if (!$ldapserver) { + log_error(gettext("ERROR! ldap_get_user_ous() backed selected with no LDAP authentication server defined.")); + return $ous; + } + + /* Setup CA environment if needed. */ + ldap_setup_caenv($authcfg); + + /* connect and see if server is up */ + $error = false; + if (!($ldap = ldap_connect($ldapserver))) { + $error = true; + } + + if ($error == true) { + log_error(sprintf(gettext("ERROR! Could not connect to server %s."), $ldapname)); + return $ous; + } + + $ldapfilter = "(|(ou=*)(cn=Users))"; + + ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); + ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING); + ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver); + + $ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun; + $ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw; + if ($ldapanon == true) { + if (!($res = @ldap_bind($ldap))) { + log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind anonymously to server %s."), $ldapname)); + @ldap_close($ldap); + return $ous; + } + } else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) { + log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind to server %s."), $ldapname)); + @ldap_close($ldap); + return $ous; + } + + if ($ldapscope == "one") { + $ldapfunc = "ldap_list"; + } else { + $ldapfunc = "ldap_search"; + } + + $search = @$ldapfunc($ldap, $ldapbasedn, $ldapfilter); + $info = @ldap_get_entries($ldap, $search); + + if (is_array($info)) { + foreach ($info as $inf) { + if (!$show_complete_ou) { + $inf_split = explode(",", $inf['dn']); + $ou = $inf_split[0]; + $ou = str_replace("OU=","", $ou); + $ou = str_replace("CN=","", $ou); + } else { + if ($inf['dn']) { + $ou = $inf['dn']; + } + } + if ($ou) { + $ous[] = $ou; + } + } + } + + @ldap_unbind($ldap); + + return $ous; +} + +function ldap_get_groups($username, $authcfg) { + global $debug, $config; + + if (!function_exists("ldap_connect")) { + return; + } + + if (!$username) { + return false; + } + + if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) { + $username_split = explode("@", $username); + $username = $username_split[0]; + } + + if (stristr($username, "\\")) { + $username_split = explode("\\", $username); + $username = $username_split[0]; + } + + //log_error("Getting LDAP groups for {$username}."); + if ($authcfg) { + if (strstr($authcfg['ldap_urltype'], "Standard")) { + $ldapproto = "ldap"; + } else { + $ldapproto = "ldaps"; + } + $ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']); + $ldapport = $authcfg['ldap_port']; + if (!empty($ldapport)) { + $ldapserver .= ":{$ldapport}"; + } + $ldapbasedn = $authcfg['ldap_basedn']; + $ldapbindun = $authcfg['ldap_binddn']; + $ldapbindpw = $authcfg['ldap_bindpw']; + $ldapauthcont = $authcfg['ldap_authcn']; + $ldapnameattribute = strtolower($authcfg['ldap_attr_user']); + $ldapgroupattribute = strtolower($authcfg['ldap_attr_member']); + $ldapfilter = "({$ldapnameattribute}={$username})"; + $ldaptype = ""; + $ldapver = $authcfg['ldap_protver']; + if (empty($ldapbindun) || empty($ldapbindpw)) { + $ldapanon = true; + } else { + $ldapanon = false; + } + $ldapname = $authcfg['name']; + $ldapfallback = false; + $ldapscope = $authcfg['ldap_scope']; + } else { + return false; + } + + $ldapdn = $_SESSION['ldapdn']; + + /*Convert attribute to lowercase. php ldap arrays put everything in lowercase */ + $ldapgroupattribute = strtolower($ldapgroupattribute); + $memberof = array(); + + /* Setup CA environment if needed. */ + ldap_setup_caenv($authcfg); + + /* connect and see if server is up */ + $error = false; + if (!($ldap = ldap_connect($ldapserver))) { + $error = true; + } + + if ($error == true) { + log_error(sprintf(gettext("ERROR! ldap_get_groups() Could not connect to server %s."), $ldapname)); + return memberof; + } + + ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); + ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING); + ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver); + + /* bind as user that has rights to read group attributes */ + $ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun; + $ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw; + if ($ldapanon == true) { + if (!($res = @ldap_bind($ldap))) { + log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind anonymously to server %s."), $ldapname)); + @ldap_close($ldap); + return false; + } + } else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) { + log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind to server %s."), $ldapname)); + @ldap_close($ldap); + return memberof; + } + + /* get groups from DN found */ + /* use ldap_read instead of search so we don't have to do a bunch of extra work */ + /* since we know the DN is in $_SESSION['ldapdn'] */ + //$search = ldap_read($ldap, $ldapdn, "(objectclass=*)", array($ldapgroupattribute)); + if ($ldapscope == "one") { + $ldapfunc = "ldap_list"; + } else { + $ldapfunc = "ldap_search"; + } + + $search = @$ldapfunc($ldap, $ldapdn, $ldapfilter, array($ldapgroupattribute)); + $info = @ldap_get_entries($ldap, $search); + + $countem = $info["count"]; + + if (is_array($info[0][$ldapgroupattribute])) { + /* Iterate through the groups and throw them into an array */ + foreach ($info[0][$ldapgroupattribute] as $member) { + if (stristr($member, "CN=") !== false) { + $membersplit = explode(",", $member); + $memberof[] = preg_replace("/CN=/i", "", $membersplit[0]); + } + } + } + + /* Time to close LDAP connection */ + @ldap_unbind($ldap); + + $groups = print_r($memberof,true); + + //log_error("Returning groups ".$groups." for user $username"); + + return $memberof; +} + +function ldap_format_host($host) { + return is_ipaddrv6($host) ? "[$host]" : $host ; +} + +function ldap_backed($username, $passwd, $authcfg) { + global $debug, $config; + + if (!$username) { + return; + } + + if (!function_exists("ldap_connect")) { + return; + } + + if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) { + $username_split = explode("@", $username); + $username = $username_split[0]; + } + if (stristr($username, "\\")) { + $username_split = explode("\\", $username); + $username = $username_split[0]; + } + + if ($authcfg) { + if (strstr($authcfg['ldap_urltype'], "Standard")) { + $ldapproto = "ldap"; + } else { + $ldapproto = "ldaps"; + } + $ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']); + $ldapport = $authcfg['ldap_port']; + if (!empty($ldapport)) { + $ldapserver .= ":{$ldapport}"; + } + $ldapbasedn = $authcfg['ldap_basedn']; + $ldapbindun = $authcfg['ldap_binddn']; + $ldapbindpw = $authcfg['ldap_bindpw']; + if (empty($ldapbindun) || empty($ldapbindpw)) { + $ldapanon = true; + } else { + $ldapanon = false; + } + $ldapauthcont = $authcfg['ldap_authcn']; + $ldapnameattribute = strtolower($authcfg['ldap_attr_user']); + $ldapextendedqueryenabled = $authcfg['ldap_extended_enabled']; + $ldapextendedquery = $authcfg['ldap_extended_query']; + $ldapfilter = ""; + if (!$ldapextendedqueryenabled) { + $ldapfilter = "({$ldapnameattribute}={$username})"; + } else { + $ldapfilter = "(&({$ldapnameattribute}={$username})({$ldapextendedquery}))"; + } + $ldaptype = ""; + $ldapver = $authcfg['ldap_protver']; + $ldapname = $authcfg['name']; + $ldapscope = $authcfg['ldap_scope']; + } else { + return false; + } + + /* first check if there is even an LDAP server populated */ + if (!$ldapserver) { + if ($ldapfallback) { + log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined. Defaulting to local user database. Visit System -> User Manager.")); + return local_backed($username, $passwd); + } else { + log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined.")); + } + + return false; + } + + /* Setup CA environment if needed. */ + ldap_setup_caenv($authcfg); + + ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); + ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING); + ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver); + + /* Make sure we can connect to LDAP */ + $error = false; + if (!($ldap = ldap_connect($ldapserver))) { + $error = true; + } + + if ($error == true) { + log_error(sprintf(gettext("ERROR! Could not connect to server %s."), $ldapname)); + return false; + } + + /* ok, its up. now, lets bind as the bind user so we can search it */ + $error = false; + $ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun; + $ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw; + if ($ldapanon == true) { + if (!($res = @ldap_bind($ldap))) { + $error = true; + } + } else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) { + $error = true; + } + + if ($error == true) { + @ldap_close($ldap); + log_error(sprintf(gettext("ERROR! Could not bind to server %s."), $ldapname)); + return false; + } + + /* Get LDAP Authcontainers and split em up. */ + $ldac_splits = explode(";", $ldapauthcont); + + /* setup the usercount so we think we haven't found anyone yet */ + $usercount = 0; + + /*****************************************************************/ + /* We First find the user based on username and filter */ + /* Then, once we find the first occurrence of that person */ + /* We set session variables to point to the OU and DN of the */ + /* Person. To later be used by ldap_get_groups. */ + /* that way we don't have to search twice. */ + /*****************************************************************/ + if ($debug) { + log_auth(sprintf(gettext("Now Searching for %s in directory."), $username)); + } + /* Iterate through the user containers for search */ + foreach ($ldac_splits as $i => $ldac_split) { + $ldac_split = isset($authcfg['ldap_utf8']) ? utf8_encode($ldac_split) : $ldac_split; + $ldapfilter = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapfilter) : $ldapfilter; + $ldapsearchbasedn = isset($authcfg['ldap_utf8']) ? utf8_encode("{$ldac_split},{$ldapbasedn}") : "{$ldac_split},{$ldapbasedn}"; + /* Make sure we just use the first user we find */ + if ($debug) { + log_auth(sprintf(gettext('Now Searching in server %1$s, container %2$s with filter %3$s.'), $ldapname, utf8_decode($ldac_split), utf8_decode($ldapfilter))); + } + if ($ldapscope == "one") { + $ldapfunc = "ldap_list"; + } else { + $ldapfunc = "ldap_search"; + } + /* Support legacy auth container specification. */ + if (stristr($ldac_split, "DC=") || empty($ldapbasedn)) { + $search = @$ldapfunc($ldap,$ldac_split,$ldapfilter); + } else { + $search = @$ldapfunc($ldap,$ldapsearchbasedn,$ldapfilter); + } + if (!$search) { + log_error(sprintf(gettext("Search resulted in error: %s"), ldap_error($ldap))); + continue; + } + $info = ldap_get_entries($ldap,$search); + $matches = $info['count']; + if ($matches == 1) { + $userdn = $_SESSION['ldapdn'] = $info[0]['dn']; + $_SESSION['ldapou'] = $ldac_split[$i]; + $_SESSION['ldapon'] = "true"; + $usercount = 1; + break; + } + } + + if ($usercount != 1) { + @ldap_unbind($ldap); + log_error(gettext("ERROR! Either LDAP search failed, or multiple users were found.")); + return false; + } + + /* Now lets bind as the user we found */ + $passwd = isset($authcfg['ldap_utf8']) ? utf8_encode($passwd) : $passwd; + if (!($res = @ldap_bind($ldap, $userdn, $passwd))) { + log_error(sprintf(gettext('ERROR! Could not login to server %1$s as user %2$s: %3$s'), $ldapname, $username, ldap_error($ldap))); + @ldap_unbind($ldap); + return false; + } + + if ($debug) { + $userdn = isset($authcfg['ldap_utf8']) ? utf8_decode($userdn) : $userdn; + log_auth(sprintf(gettext('Logged in successfully as %1$s via LDAP server %2$s with DN = %3$s.'), $username, $ldapname, $userdn)); + } + + /* At this point we are bound to LDAP so the user was auth'd okay. Close connection. */ + @ldap_unbind($ldap); + + return true; +} + +function radius_backed($username, $passwd, $authcfg, &$attributes = array()) { + global $debug, $config; + $ret = false; + + require_once("radius.inc"); + + $rauth = new Auth_RADIUS_PAP($username, $passwd); + if ($authcfg) { + $radiusservers = array(); + $radiusservers[0]['ipaddr'] = $authcfg['host']; + $radiusservers[0]['port'] = $authcfg['radius_auth_port']; + $radiusservers[0]['sharedsecret'] = $authcfg['radius_secret']; + $radiusservers[0]['timeout'] = $authcfg['radius_timeout']; + } else { + return false; + } + + /* Add new servers to our instance */ + foreach ($radiusservers as $radsrv) { + $timeout = (is_numeric($radsrv['timeout'])) ? $radsrv['timeout'] : 5; + $rauth->addServer($radsrv['ipaddr'], $radsrv['port'], $radsrv['sharedsecret'], $timeout); + } + + if (PEAR::isError($rauth->start())) { + $retvalue['auth_val'] = 1; + $retvalue['error'] = $rauth->getError(); + if ($debug) { + printf(gettext("Radius start: %s<br />\n"), $retvalue['error']); + } + } + + // XXX - billm - somewhere in here we need to handle securid challenge/response + + /* Send request */ + $result = $rauth->send(); + if (PEAR::isError($result)) { + $retvalue['auth_val'] = 1; + $retvalue['error'] = $result->getMessage(); + if ($debug) { + printf(gettext("Radius send failed: %s<br />\n"), $retvalue['error']); + } + } else if ($result === true) { + if ($rauth->getAttributes()) { + $attributes = $rauth->listAttributes(); + } + $retvalue['auth_val'] = 2; + if ($debug) { + printf(gettext("Radius Auth succeeded")."<br />\n"); + } + $ret = true; + } else { + $retvalue['auth_val'] = 3; + if ($debug) { + printf(gettext("Radius Auth rejected")."<br />\n"); + } + } + + // close OO RADIUS_AUTHENTICATION + $rauth->close(); + + return $ret; +} + +/* + $attributes must contain a "class" key containing the groups and local + groups must exist to match. +*/ +function radius_get_groups($attributes) { + $groups = array(); + if (!empty($attributes) && is_array($attributes) && !empty($attributes['class'])) { + $groups = explode(";", $attributes['class']); + foreach ($groups as & $grp) { + $grp = trim($grp); + if (strtolower(substr($grp, 0, 3)) == "ou=") { + $grp = substr($grp, 3); + } + } + } + return $groups; +} + +function get_user_expiration_date($username) { + $user = getUserEntry($username); + if ($user['expires']) { + return $user['expires']; + } +} + +function is_account_expired($username) { + $expirydate = get_user_expiration_date($username); + if ($expirydate) { + if (strtotime("-1 day") > strtotime(date("m/d/Y",strtotime($expirydate)))) { + return true; + } + } + + return false; +} + +function is_account_disabled($username) { + $user = getUserEntry($username); + if (isset($user['disabled'])) { + return true; + } + + return false; +} + +function auth_get_authserver($name) { + global $config; + + if (is_array($config['system']['authserver'])) { + foreach ($config['system']['authserver'] as $authcfg) { + if ($authcfg['name'] == $name) { + return $authcfg; + } + } + } + if ($name == "Local Database") { + return array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']); + } +} + +function auth_get_authserver_list() { + global $config; + + $list = array(); + + if (is_array($config['system']['authserver'])) { + foreach ($config['system']['authserver'] as $authcfg) { + /* Add support for disabled entries? */ + $list[$authcfg['name']] = $authcfg; + } + } + + $list["Local Database"] = array( "name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']); + return $list; +} + +function getUserGroups($username, $authcfg, &$attributes = array()) { + global $config; + + $allowed_groups = array(); + + switch ($authcfg['type']) { + case 'ldap': + $allowed_groups = @ldap_get_groups($username, $authcfg); + break; + case 'radius': + $allowed_groups = @radius_get_groups($attributes); + break; + default: + $user = getUserEntry($username); + $allowed_groups = @local_user_get_groups($user, true); + break; + } + + $member_groups = array(); + if (is_array($config['system']['group'])) { + foreach ($config['system']['group'] as $group) { + if (in_array($group['name'], $allowed_groups)) { + $member_groups[] = $group['name']; + } + } + } + + return $member_groups; +} + +function authenticate_user($username, $password, $authcfg = NULL, &$attributes = array()) { + + if (!$authcfg) { + return local_backed($username, $password); + } + + $authenticated = false; + switch ($authcfg['type']) { + case 'ldap': + if (ldap_backed($username, $password, $authcfg)) { + $authenticated = true; + } + break; + case 'radius': + if (radius_backed($username, $password, $authcfg, $attributes)) { + $authenticated = true; + } + break; + default: + /* lookup user object by name */ + if (local_backed($username, $password)) { + $authenticated = true; + } + break; + } + + return $authenticated; +} + +function session_auth() { + global $config, $_SESSION, $page; + + // Handle HTTPS httponly and secure flags + $currentCookieParams = session_get_cookie_params(); + session_set_cookie_params( + $currentCookieParams["lifetime"], + $currentCookieParams["path"], + NULL, + ($config['system']['webgui']['protocol'] == "https"), + true + ); + + if (!session_id()) { + session_start(); + } + + // Detect protocol change + if (!isset($_POST['login']) && !empty($_SESSION['Logged_In']) && $_SESSION['protocol'] != $config['system']['webgui']['protocol']) { + return false; + } + + /* Validate incoming login request */ + $attributes = array(); + if (isset($_POST['login']) && !empty($_POST['usernamefld']) && !empty($_POST['passwordfld'])) { + $authcfg = auth_get_authserver($config['system']['webgui']['authmode']); + if (authenticate_user($_POST['usernamefld'], $_POST['passwordfld'], $authcfg, $attributes) || + authenticate_user($_POST['usernamefld'], $_POST['passwordfld'])) { + // Generate a new id to avoid session fixation + session_regenerate_id(); + $_SESSION['Logged_In'] = "True"; + $_SESSION['Username'] = $_POST['usernamefld']; + $_SESSION['user_radius_attributes'] = $attributes; + $_SESSION['last_access'] = time(); + $_SESSION['protocol'] = $config['system']['webgui']['protocol']; + if (!isset($config['system']['webgui']['quietlogin'])) { + log_auth(sprintf(gettext("Successful login for user '%1\$s' from: %2\$s"), $_POST['usernamefld'], $_SERVER['REMOTE_ADDR'])); + } + if (isset($_POST['postafterlogin'])) { + return true; + } else { + if (empty($page)) { + $page = "/"; + } + header("Location: {$page}"); + } + exit; + } else { + /* give the user an error message */ + $_SESSION['Login_Error'] = "Username or Password incorrect"; + log_auth("webConfigurator authentication error for '{$_POST['usernamefld']}' from {$_SERVER['REMOTE_ADDR']}"); + if (isAjax()) { + echo "showajaxmessage('{$_SESSION['Login_Error']}');"; + return; + } + } + } + + /* Show login page if they aren't logged in */ + if (empty($_SESSION['Logged_In'])) { + return false; + } + + /* If session timeout isn't set, we don't mark sessions stale */ + if (!isset($config['system']['webgui']['session_timeout'])) { + /* Default to 4 hour timeout if one is not set */ + if ($_SESSION['last_access'] < (time() - 14400)) { + $_GET['logout'] = true; + $_SESSION['Logout'] = true; + } else { + $_SESSION['last_access'] = time(); + } + } else if (intval($config['system']['webgui']['session_timeout']) == 0) { + /* only update if it wasn't ajax */ + if (!isAjax()) { + $_SESSION['last_access'] = time(); + } + } else { + /* Check for stale session */ + if ($_SESSION['last_access'] < (time() - ($config['system']['webgui']['session_timeout'] * 60))) { + $_GET['logout'] = true; + $_SESSION['Logout'] = true; + } else { + /* only update if it wasn't ajax */ + if (!isAjax()) { + $_SESSION['last_access'] = time(); + } + } + } + + /* user hit the logout button */ + if (isset($_GET['logout'])) { + + if ($_SESSION['Logout']) { + log_error(sprintf(gettext("Session timed out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR'])); + } else { + log_error(sprintf(gettext("User logged out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR'])); + } + + /* wipe out $_SESSION */ + $_SESSION = array(); + + if (isset($_COOKIE[session_name()])) { + setcookie(session_name(), '', time()-42000, '/'); + } + + /* and destroy it */ + session_destroy(); + + $scriptName = explode("/", $_SERVER["SCRIPT_FILENAME"]); + $scriptElms = count($scriptName); + $scriptName = $scriptName[$scriptElms-1]; + + if (isAjax()) { + return false; + } + + /* redirect to page the user is on, it'll prompt them to login again */ + header("Location: {$scriptName}"); + + return false; + } + + /* + * this is for debugging purpose if you do not want to use Ajax + * to submit a HTML form. It basically disables the observation + * of the submit event and hence does not trigger Ajax. + */ + if ($_GET['disable_ajax']) { + $_SESSION['NO_AJAX'] = "True"; + } + + /* + * Same to re-enable Ajax. + */ + if ($_GET['enable_ajax']) { + unset($_SESSION['NO_AJAX']); + } + + return true; +} + +?> diff --git a/src/etc/inc/authgui.inc b/src/etc/inc/authgui.inc new file mode 100644 index 0000000..721be47 --- /dev/null +++ b/src/etc/inc/authgui.inc @@ -0,0 +1,285 @@ +<?php +/* $Id$ */ +/* + Copyright (C) 2007, 2008 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Copyright (C) 2005-2006 Bill Marquette <bill.marquette@gmail.com> + All rights reserved. + + Copyright (C) 2006 Paul Taylor <paultaylor@winn-dixie.com>. + All rights reserved. + + Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_MODULE: authgui +*/ + +include_once("auth.inc"); +include_once("priv.inc"); +if (!function_exists('platform_booting')) { + require_once('globals.inc'); +} + +/* Authenticate user - exit if failed */ +if (!session_auth()) { + display_login_form(); + exit; +} + +/* + * Once here, the user has authenticated with the web server. + * We give them access only to the appropriate pages based on + * the user or group privileges. + */ +$allowedpages = getAllowedPages($_SESSION['Username'], $_SESSION['user_radius_attributes']); + +/* + * redirect to first allowed page if requesting a wrong url + */ +if (!isAllowedPage($_SERVER['REQUEST_URI'])) { + if (count($allowedpages) > 0) { + $page = str_replace('*', '', $allowedpages[0]); + $_SESSION['Post_Login'] = true; + require_once("functions.inc"); + pfSenseHeader("/{$page}"); + + $username = empty($_SESSION["Username"]) ? "(system)" : $_SESSION['Username']; + if (!empty($_SERVER['REMOTE_ADDR'])) { + $username .= '@' . $_SERVER['REMOTE_ADDR']; + } + log_error("{$username} attempted to access {$_SERVER['SCRIPT_NAME']} but does not have access to that page. Redirecting to {$page}."); + + exit; + } else { + display_error_form("201", gettext("No page assigned to this user! Click here to logout.")); + exit; + } +} else { + $_SESSION['Post_Login'] = true; +} + +/* + * redirect browsers post-login to avoid pages + * taking action in response to a POST request + */ +if (!$_SESSION['Post_Login']) { + $_SESSION['Post_Login'] = true; + require_once("functions.inc"); + pfSenseHeader($_SERVER['REQUEST_URI']); + exit; +} + +/* + * Close session data to allow other scripts from same host to come in. + * A session can be reactivated from calling session_start again + */ +session_commit(); + +/* + * determine if the user is allowed access to the requested page + */ +function display_error_form($http_code, $desc) { + global $config, $g; + $g['theme'] = get_current_theme(); + if (isAjax()) { + printf(gettext('Error: %1$s Description: %2$s'), $http_code, $desc); + return; + } + +?> +<<<<<<< HEAD +<!DOCTYPE html> +<html lang="en"> +<head> + <link rel="stylesheet" href="/bootstrap/css/pfSense.css" /> + <title><?=gettext("Error: not allowed"); ?></title> +</head> +<body id="error" class="no-menu"> + <div id="jumbotron"> + <div class="container"> + <div class="col-sm-offset-3 col-sm-6 col-xs-12"> + <!-- FIXME: We really need to POST the logout action --> + <div class="alert alert-danger" role="alert"><a href="index.php?logout"><?=$desc;?></a></div> + </div> + </div> + </div> +</body> +</html> +<?php + +} // end function + + +function display_login_form() { + require_once("globals.inc"); + global $config, $g; + $g['theme'] = get_current_theme(); + + unset($input_errors); + + if (isAjax()) { + if (isset($_POST['login'])) { + if ($_SESSION['Logged_In'] <> "True") { + isset($_SESSION['Login_Error']) ? $login_error = $_SESSION['Login_Error'] : $login_error = gettext("unknown reason"); + printf("showajaxmessage('" . gettext("Invalid login (%s).") . "')", $login_error); + } + if (file_exists("{$g['tmp_path']}/webconfigurator.lock")) { + // TODO: add the IP from the user who did lock the device + $whom = file_get_contents("{$g['tmp_path']}/webconfigurator.lock"); + printf("showajaxmessage('" . gettext("This device is currently being maintained by: %s.") . "');", $whom); + } + } + exit; + } + +/* Check against locally configured IP addresses, which will catch when someone + port forwards WebGUI access from WAN to an internal IP on the router. */ +global $FilterIflist, $nifty_background; +$local_ip = false; +if (strpos($_SERVER['HTTP_HOST'], ":") === FALSE) { + $http_host_port = explode(":", $_SERVER['HTTP_HOST']); + $http_host = $http_host_port[0]; +} else { + $http_host = $_SERVER['HTTP_HOST']; +} +if (empty($FilterIflist)) { + require_once('filter.inc'); + require_once('shaper.inc'); + filter_generate_optcfg_array(); +} +foreach ($FilterIflist as $iflist) { + if ($iflist['ip'] == $http_host) { + $local_ip = true; + } else if ($iflist['ipv6'] == $http_host) { + $local_ip = true; + } else if (is_array($iflist['vips'])) { + foreach ($iflist['vips'] as $vip) { + if ($vip['ip'] == $http_host) { + $local_ip = true; + break; + } + } + unset($vip); + } + if ($local_ip == true) { + break; + } +} +unset($FilterIflist); +unset($iflist); + +if ($local_ip == false) { + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as $ovpns) { + if (is_ipaddrv4($http_host) && !empty($ovpns['tunnel_network']) && ip_in_subnet($http_host, $ovpns['tunnel_network'])) { + $local_ip = true; + } else if (is_ipaddrv6($http_host) && !empty($ovpns['tunnel_networkv6']) && ip_in_subnet($http_host, $ovpns['tunnel_networkv6'])) { + $local_ip = true; + } + if ($local_ip == true) { + break; + } + } + } +} + +?> +<!DOCTYPE html> +<html lang="en"> +<head> + <link rel="stylesheet" href="/bootstrap/css/pfSense.css" /> + <title><?=gettext("Login"); ?></title> + <script>var events = events || [];</script> +</head> +<body id="login" class="no-menu"> + <div id="jumbotron"> + <div class="container"> + <div class="col-sm-offset-3 col-sm-6 col-xs-12"> +<?php + if(is_ipaddr($http_host) && !$local_ip && !isset($config['system']['webgui']['nohttpreferercheck'])) { + $nifty_background = "#999"; + print_info_box(gettext("You are accessing this router by an IP address not configured locally, which may be forwarded by NAT or other means. <br /><br />If you did not setup this forwarding, you may be the target of a man-in-the-middle attack.")); + } + + $loginautocomplete = isset($config['system']['webgui']['loginautocomplete']) ? '' : 'autocomplete="off"'; +?> + + <div class="panel panel-default"> + <div class="panel-heading"> + <h2 class="panel-title">Login to pfSense</h2> + </div> + + <div class="panel-body"> +<?php if (!empty($_SESSION['Login_Error'])): ?> + <div class="alert alert-danger" role="alert"><?=$_SESSION['Login_Error'];?></div> +<?php endif ?> + <div class="alert alert-warning" class="hidden" id="no_cookies"><?= gettext("Your browser must support cookies to login."); ?></div> + + <form method="post" <?= $loginautocomplete ?> action="<?=$_SERVER['SCRIPT_NAME'];?>" class="form-horizontal"> + <div class="form-group"> + <label for="usernamefld" class="col-sm-3 control-label">Username</label> + <div class="col-sm-9 col-md-7"> + <input type="text" class="form-control" name="usernamefld" id="usernamefld" placeholder="Enter your username"> + </div> + </div> + + <div class="form-group"> + <label for="passwordfld" class="col-sm-3 control-label">Password</label> + <div class="col-sm-9 col-md-7"> + <input type="password" class="form-control" name="passwordfld" id="passwordfld" placeholder="Enter your password"> + </div> + </div> + + <div class="form-group"> + <div class="col-sm-offset-3 col-sm-9 col-md-7"> + <button type="submit" class="btn btn-primary" name="login">Login</button> + </div> + </div> + </form> + </div> + </div> + </div> + </div> + + <script> + events.push(function() { + document.cookie= + "cookie_test=1" + + "<?php echo $config['system']['webgui']['protocol'] == 'https' ? '; secure' : '';?>"; + + if (document.cookie.indexOf("cookie_test") == -1) + document.getElementById("no_cookies").style.display=""; + else + document.getElementById("no_cookies").style.display="none"; + + // Delete it + document.cookie = "cookie_test=1; expires=Thu, 01-Jan-1970 00:00:01 GMT"; + }); + </script> +<?php +require('foot.inc'); + +} // end function diff --git a/src/etc/inc/basic_sasl_client.inc b/src/etc/inc/basic_sasl_client.inc new file mode 100644 index 0000000..c817664 --- /dev/null +++ b/src/etc/inc/basic_sasl_client.inc @@ -0,0 +1,63 @@ +<?php +/* + * basic_sasl_client.php + * + * @(#) $Id: basic_sasl_client.php,v 1.1 2004/11/17 08:01:23 mlemos Exp $ + * + */ + +define("SASL_BASIC_STATE_START", 0); +define("SASL_BASIC_STATE_DONE", 1); + +class basic_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_BASIC_STATE_START; + + Function Initialize(&$client) + { + return(1); + } + + Function Start(&$client, &$message, &$interactions) + { + if ($this->state!=SASL_BASIC_STATE_START) + { + $client->error="Basic authentication state is not at the start"; + return(SASL_FAIL); + } + $this->credentials=array( + "user"=>"", + "password"=>"" + ); + $defaults=array( + ); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if ($status==SASL_CONTINUE) + { + $message=$this->credentials["user"].":".$this->credentials["password"]; + $this->state=SASL_BASIC_STATE_DONE; + } + else + { + Unset($message); + } + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch ($this->state) + { + case SASL_BASIC_STATE_DONE: + $client->error="Basic authentication was finished without success"; + return(SASL_FAIL); + default: + $client->error="invalid Basic authentication step state"; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/src/etc/inc/captiveportal.inc b/src/etc/inc/captiveportal.inc new file mode 100644 index 0000000..e95cc75 --- /dev/null +++ b/src/etc/inc/captiveportal.inc @@ -0,0 +1,2420 @@ +<?php +/* + captiveportal.inc + part of pfSense (https://www.pfsense.org) + Copyright (C) 2004-2011 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2009-2012 Ermal Luçi <eri@pfsense.org> + Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>. + + originally part of m0n0wall (http://m0n0.ch/wall) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + This version of captiveportal.inc has been modified by Rob Parker + <rob.parker@keycom.co.uk> to include changes for per-user bandwidth management + via returned RADIUS attributes. This page has been modified to delete any + added rules which may have been created by other per-user code (index.php, etc). + These changes are (c) 2004 Keycom PLC. + + pfSense_BUILDER_BINARIES: /sbin/ipfw /sbin/route + pfSense_BUILDER_BINARIES: /usr/local/sbin/lighttpd /usr/local/bin/minicron /sbin/pfctl + pfSense_BUILDER_BINARIES: /bin/hostname /bin/cp + pfSense_MODULE: captiveportal +*/ + +/* include all configuration functions */ +require_once("config.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("radius.inc"); +require_once("voucher.inc"); + +function get_default_captive_portal_html() { + global $config, $g, $cpzone; + + $htmltext = <<<EOD +<html> +<body> +<form method="post" action="\$PORTAL_ACTION\$"> + <input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$"> + <input name="zone" type="hidden" value="\$PORTAL_ZONE\$"> + <center> + <table cellpadding="6" cellspacing="0" width="550" height="380" style="border:1px solid #000000"> + <tr height="10" bgcolor="#990000"> + <td style="border-bottom:1px solid #000000"> + <font color='white'> + <b> + {$g['product_name']} captive portal + </b> + </font> + </td> + </tr> + <tr> + <td> + <div id="mainlevel"> + <center> + <table width="100%" border="0" cellpadding="5" cellspacing="0"> + <tr> + <td> + <center> + <div id="mainarea"> + <center> + <table width="100%" border="0" cellpadding="5" cellspacing="5"> + <tr> + <td> + <div id="maindivarea"> + <center> + <div id='statusbox'> + <font color='red' face='arial' size='+1'> + <b> + \$PORTAL_MESSAGE\$ + </b> + </font> + </div> + <br /> + <div id='loginbox'> + <table> + <tr><td colspan="2"><center>Welcome to the {$g['product_name']} Captive Portal!</td></tr> + <tr><td> </td></tr> + <tr><td align="right">Username:</td><td><input name="auth_user" type="text" style="border: 1px dashed;"></td></tr> + <tr><td align="right">Password:</td><td><input name="auth_pass" type="password" style="border: 1px dashed;"></td></tr> + <tr><td> </td></tr> + +EOD; + + if (isset($config['voucher'][$cpzone]['enable'])) { + $htmltext .= <<<EOD + <tr> + <td align="right">Enter Voucher Code: </td> + <td><input name="auth_voucher" type="text" style="border:1px dashed;" size="22"></td> + </tr> + +EOD; + } + + $htmltext .= <<<EOD + <tr> + <td colspan="2"><center><input name="accept" type="submit" value="Continue"></center></td> + </tr> + </table> + </div> + </center> + </div> + </td> + </tr> + </table> + </center> + </div> + </center> + </td> + </tr> + </table> + </center> + </div> + </td> + </tr> + </table> + </center> +</form> +</body> +</html> + +EOD; + + return $htmltext; +} + +function captiveportal_load_modules() { + global $config; + + mute_kernel_msgs(); + if (!is_module_loaded("ipfw.ko")) { + mwexec("/sbin/kldload ipfw"); + /* make sure ipfw is not on pfil hooks */ + set_sysctl(array( + "net.inet.ip.pfil.inbound" => "pf", "net.inet6.ip6.pfil.inbound" => "pf", + "net.inet.ip.pfil.outbound" => "pf", "net.inet6.ip6.pfil.outbound" => "pf") + ); + } + /* Activate layer2 filtering */ + set_sysctl(array("net.link.ether.ipfw" => "1", "net.inet.ip.fw.one_pass" => "1")); + + /* Always load dummynet now that even allowed ip and mac passthrough use it. */ + if (!is_module_loaded("dummynet.ko")) { + mwexec("/sbin/kldload dummynet"); + set_sysctl(array("net.inet.ip.dummynet.io_fast" => "1", "net.inet.ip.dummynet.hash_size" => "256")); + } + unmute_kernel_msgs(); +} + +function captiveportal_configure() { + global $config, $cpzone, $cpzoneid; + + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cpkey => $cp) { + $cpzone = $cpkey; + $cpzoneid = $cp['zoneid']; + captiveportal_configure_zone($cp); + } + } +} + +function captiveportal_configure_zone($cpcfg) { + global $config, $g, $cpzone, $cpzoneid; + + $captiveportallck = lock("captiveportal{$cpzone}", LOCK_EX); + + if (isset($cpcfg['enable'])) { + + if (platform_booting()) { + echo "Starting captive portal({$cpcfg['zone']})... "; + + /* remove old information */ + unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db"); + } else { + captiveportal_syslog("Reconfiguring captive portal({$cpcfg['zone']})."); + } + + /* init ipfw rules */ + captiveportal_init_rules(true); + + /* kill any running minicron */ + killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid"); + + /* initialize minicron interval value */ + $croninterval = $cpcfg['croninterval'] ? $cpcfg['croninterval'] : 60; + + /* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */ + if ((!is_numeric($croninterval)) || ($croninterval < 10)) { + $croninterval = 60; + } + + /* write portal page */ + if (is_array($cpcfg['page']) && $cpcfg['page']['htmltext']) { + $htmltext = base64_decode($cpcfg['page']['htmltext']); + } else { + /* example/template page */ + $htmltext = get_default_captive_portal_html(); + } + + $fd = @fopen("{$g['varetc_path']}/captiveportal_{$cpzone}.html", "w"); + if ($fd) { + // Special case handling. Convert so that we can pass this page + // through the PHP interpreter later without clobbering the vars. + $htmltext = str_replace("\$PORTAL_ZONE\$", "#PORTAL_ZONE#", $htmltext); + $htmltext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $htmltext); + $htmltext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $htmltext); + $htmltext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $htmltext); + $htmltext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $htmltext); + $htmltext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $htmltext); + $htmltext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $htmltext); + if ($cpcfg['preauthurl']) { + $htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext); + $htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext); + } + fwrite($fd, $htmltext); + fclose($fd); + } + unset($htmltext); + + /* write error page */ + if (is_array($cpcfg['page']) && $cpcfg['page']['errtext']) { + $errtext = base64_decode($cpcfg['page']['errtext']); + } else { + /* example page */ + $errtext = get_default_captive_portal_html(); + } + + $fd = @fopen("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html", "w"); + if ($fd) { + // Special case handling. Convert so that we can pass this page + // through the PHP interpreter later without clobbering the vars. + $errtext = str_replace("\$PORTAL_ZONE\$", "#PORTAL_ZONE#", $errtext); + $errtext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $errtext); + $errtext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $errtext); + $errtext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $errtext); + $errtext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $errtext); + $errtext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $errtext); + $errtext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $errtext); + if ($cpcfg['preauthurl']) { + $errtext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $errtext); + $errtext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $errtext); + } + fwrite($fd, $errtext); + fclose($fd); + } + unset($errtext); + + /* write logout page */ + if (is_array($cpcfg['page']) && $cpcfg['page']['logouttext']) { + $logouttext = base64_decode($cpcfg['page']['logouttext']); + } else { + /* example page */ + $logouttext = <<<EOD +<html> +<head><title>Redirecting...</title></head> +<body> +<span style="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;"> +<b>Redirecting to <a href="<?=\$my_redirurl;?>"><?=\$my_redirurl;?></a>...</b> +</span> +<script type="text/javascript"> +//<![CDATA[ +LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64'); +if (LogoutWin) { + LogoutWin.document.write('<html>'); + LogoutWin.document.write('<head><title>Logout</title></head>') ; + LogoutWin.document.write('<body bgcolor="#435370">'); + LogoutWin.document.write('<div align="center" style="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ; + LogoutWin.document.write('<b>Click the button below to disconnect</b><p />'); + LogoutWin.document.write('<form method="POST" action="<?=\$logouturl;?>">'); + LogoutWin.document.write('<input name="logout_id" type="hidden" value="<?=\$sessionid;?>" />'); + LogoutWin.document.write('<input name="zone" type="hidden" value="<?=\$cpzone;?>" />'); + LogoutWin.document.write('<input name="logout" type="submit" value="Logout" />'); + LogoutWin.document.write('</form>'); + LogoutWin.document.write('</div></body>'); + LogoutWin.document.write('</html>'); + LogoutWin.document.close(); +} + +document.location.href="<?=\$my_redirurl;?>"; +//]]> +</script> +</body> +</html> + +EOD; + } + + $fd = @fopen("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html", "w"); + if ($fd) { + fwrite($fd, $logouttext); + fclose($fd); + } + unset($logouttext); + + /* write elements */ + captiveportal_write_elements(); + + /* kill any running mini_httpd */ + killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal.pid"); + killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal-SSL.pid"); + + /* start up the webserving daemon */ + captiveportal_init_webgui_zone($cpcfg); + + /* Kill any existing prunecaptiveportal processes */ + if (file_exists("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid")) { + killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid"); + } + + /* start pruning process (interval defaults to 60 seconds) */ + mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/cp_prunedb_{$cpzone}.pid " . + "/etc/rc.prunecaptiveportal {$cpzone}"); + + /* generate radius server database */ + unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db"); + captiveportal_init_radius_servers(); + + if (platform_booting()) { + /* send Accounting-On to server */ + captiveportal_send_server_accounting(); + echo "done\n"; + } + + } else { + killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal.pid"); + killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal-SSL.pid"); + killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid"); + @unlink("{$g['varetc_path']}/captiveportal_{$cpzone}.html"); + @unlink("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html"); + @unlink("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html"); + + captiveportal_radius_stop_all(); + + /* send Accounting-Off to server */ + if (!platform_booting()) { + captiveportal_send_server_accounting(true); + } + + /* remove old information */ + unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db"); + unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db"); + unlink_if_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"); + /* Release allocated pipes for this zone */ + captiveportal_free_dnrules(); + + mwexec("/sbin/ipfw zone {$cpzoneid} destroy", true); + + if (empty($config['captiveportal'])) { + set_single_sysctl("net.link.ether.ipfw", "0"); + } else { + /* Deactivate ipfw(4) if not needed */ + $cpactive = false; + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cpkey => $cp) { + if (isset($cp['enable'])) { + $cpactive = true; + break; + } + } + } + if ($cpactive === false) { + set_single_sysctl("net.link.ether.ipfw", "0"); + } + } + } + + unlock($captiveportallck); + + return 0; +} + +function captiveportal_init_webgui() { + global $config, $cpzone; + + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cpkey => $cp) { + $cpzone = $cpkey; + captiveportal_init_webgui_zone($cp); + } + } +} + +function captiveportal_init_webgui_zonename($zone) { + global $config, $cpzone; + + if (isset($config['captiveportal'][$zone])) { + $cpzone = $zone; + captiveportal_init_webgui_zone($config['captiveportal'][$zone]); + } +} + +function captiveportal_init_webgui_zone($cpcfg) { + global $g, $config, $cpzone; + + if (!isset($cpcfg['enable'])) { + return; + } + + if (isset($cpcfg['httpslogin'])) { + $cert = lookup_cert($cpcfg['certref']); + $crt = base64_decode($cert['crt']); + $key = base64_decode($cert['prv']); + $ca = ca_chain($cert); + + /* generate lighttpd configuration */ + if (!empty($cpcfg['listenporthttps'])) { + $listenporthttps = $cpcfg['listenporthttps']; + } else { + $listenporthttps = 8001 + $cpcfg['zoneid']; + } + system_generate_lighty_config("{$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal-SSL.conf", + $crt, $key, $ca, "lighty-{$cpzone}-CaptivePortal-SSL.pid", $listenporthttps, "/usr/local/captiveportal", + "cert-{$cpzone}-portal.pem", "ca-{$cpzone}-portal.pem", $cpzone); + } + + /* generate lighttpd configuration */ + if (!empty($cpcfg['listenporthttp'])) { + $listenporthttp = $cpcfg['listenporthttp']; + } else { + $listenporthttp = 8000 + $cpcfg['zoneid']; + } + system_generate_lighty_config("{$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal.conf", + "", "", "", "lighty-{$cpzone}-CaptivePortal.pid", $listenporthttp, "/usr/local/captiveportal", + "", "", $cpzone); + + @unlink("{$g['varrun']}/lighty-{$cpzone}-CaptivePortal.pid"); + /* attempt to start lighttpd */ + $res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal.conf"); + + /* fire up https instance */ + if (isset($cpcfg['httpslogin'])) { + @unlink("{$g['varrun']}/lighty-{$cpzone}-CaptivePortal-SSL.pid"); + $res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal-SSL.conf"); + } +} + +function captiveportal_init_rules_byinterface($interface) { + global $cpzone, $cpzoneid, $config; + + if (!is_array($config['captiveportal'])) { + return; + } + + foreach ($config['captiveportal'] as $cpkey => $cp) { + $cpzone = $cpkey; + $cpzoneid = $cp['zoneid']; + $cpinterfaces = explode(",", $cp['interface']); + if (in_array($interface, $cpinterfaces)) { + captiveportal_init_rules(); + break; + } + } +} + +/* reinit will disconnect all users, be careful! */ +function captiveportal_init_rules($reinit = false) { + global $config, $g, $cpzone, $cpzoneid; + + if (!isset($config['captiveportal'][$cpzone]['enable'])) { + return; + } + + captiveportal_load_modules(); + mwexec("/sbin/ipfw zone {$cpzoneid} create", true); + + /* Cleanup so nothing is leaked */ + captiveportal_free_dnrules(); + unlink_if_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"); + + $cpips = array(); + $ifaces = get_configured_interface_list(); + $cpinterfaces = explode(",", $config['captiveportal'][$cpzone]['interface']); + $firsttime = 0; + foreach ($cpinterfaces as $cpifgrp) { + if (!isset($ifaces[$cpifgrp])) { + continue; + } + $tmpif = get_real_interface($cpifgrp); + if (!empty($tmpif)) { + $cpipm = get_interface_ip($cpifgrp); + if (is_ipaddr($cpipm)) { + $cpips[] = $cpipm; + if (!is_array($config['virtualip']) || !is_array($config['virtualip']['vip'])) { + continue; + } + foreach ($config['virtualip']['vip'] as $vip) { + if (($vip['interface'] == $cpifgrp) && (($vip['mode'] == "carp") || ($vip['mode'] == "ipalias"))) { + $cpips[] = $vip['subnet']; + } + } + } + mwexec("/sbin/ipfw zone {$cpzoneid} madd {$tmpif}", true); + } + } + if (count($cpips) > 0) { + $cpactive = true; + } else { + return false; + } + + if ($reinit == false) { + $captiveportallck = lock("captiveportal{$cpzone}"); + } + + $cprules = <<<EOD + +flush +add 65291 allow pfsync from any to any +add 65292 allow carp from any to any + +# layer 2: pass ARP +add 65301 pass layer2 mac-type arp,rarp +# pfsense requires for WPA +add 65302 pass layer2 mac-type 0x888e,0x88c7 +# PPP Over Ethernet Session Stage/Discovery Stage +add 65303 pass layer2 mac-type 0x8863,0x8864 + +# layer 2: block anything else non-IP(v4/v6) +add 65307 deny layer2 not mac-type ip,ipv6 + +EOD; + + $rulenum = 65310; + /* These tables contain host ips */ + $cprules .= "add {$rulenum} pass ip from any to table(100) in\n"; + $rulenum++; + $cprules .= "add {$rulenum} pass ip from table(100) to any out\n"; + $rulenum++; + $ips = ""; + foreach ($cpips as $cpip) { + $cprules .= "table 100 add {$cpip}\n"; + } + $cprules .= "table 100 add 255.255.255.255\n"; + $cprules .= "add {$rulenum} pass ip from any to {$ips} in\n"; + $rulenum++; + $cprules .= "add {$rulenum} pass ip from {$ips} to any out\n"; + $rulenum++; + $cprules .= "add {$rulenum} pass icmp from {$ips} to any out icmptype 0\n"; + $rulenum++; + $cprules .= "add {$rulenum} pass icmp from any to {$ips} in icmptype 8 \n"; + $rulenum++; + /* Allowed ips */ + $cprules .= "add {$rulenum} pipe tablearg ip from table(3) to any in\n"; + $rulenum++; + $cprules .= "add {$rulenum} pipe tablearg ip from any to table(4) in\n"; + $rulenum++; + $cprules .= "add {$rulenum} pipe tablearg ip from table(3) to any out\n"; + $rulenum++; + $cprules .= "add {$rulenum} pipe tablearg ip from any to table(4) out\n"; + $rulenum++; + + /* Authenticated users rules. */ + $cprules .= "add {$rulenum} pipe tablearg ip from table(1) to any in\n"; + $rulenum++; + $cprules .= "add {$rulenum} pipe tablearg ip from any to table(2) out\n"; + $rulenum++; + + if (!empty($config['captiveportal'][$cpzone]['listenporthttp'])) { + $listenporthttp = $config['captiveportal'][$cpzone]['listenporthttp']; + } else { + $listenporthttp = 8000 + $cpzoneid; + } + + if (isset($config['captiveportal'][$cpzone]['httpslogin'])) { + if (!empty($config['captiveportal'][$cpzone]['listenporthttps'])) { + $listenporthttps = $config['captiveportal'][$cpzone]['listenporthttps']; + } else { + $listenporthttps = 8001 + $cpzoneid; + } + if (!isset($config['captiveportal'][$cpzone]['nohttpsforwards'])) { + $cprules .= "add 65531 fwd 127.0.0.1,{$listenporthttps} tcp from any to any dst-port 443 in\n"; + } + } + + $cprules .= <<<EOD + +# redirect non-authenticated clients to captive portal +add 65532 fwd 127.0.0.1,{$listenporthttp} tcp from any to any dst-port 80 in +# let the responses from the captive portal web server back out +add 65533 pass tcp from any to any out +# block everything else +add 65534 deny all from any to any + +EOD; + + /* generate passthru mac database */ + $cprules .= captiveportal_passthrumac_configure(true); + $cprules .= "\n"; + + /* allowed ipfw rules to make allowed ip work */ + $cprules .= captiveportal_allowedip_configure(); + + /* allowed ipfw rules to make allowed hostnames work */ + $cprules .= captiveportal_allowedhostname_configure(); + + /* load rules */ + file_put_contents("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules", $cprules); + mwexec("/sbin/ipfw -x {$cpzoneid} -q {$g['tmp_path']}/ipfw_{$cpzone}.cp.rules", true); + //@unlink("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules"); + unset($cprules); + + if ($reinit == false) { + unlock($captiveportallck); + } +} + +/* + * Remove clients that have been around for longer than the specified amount of time + * db file structure: + * timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time,interim_interval + * (password is in Base64 and only saved when reauthentication is enabled) + */ +function captiveportal_prune_old() { + global $g, $config, $cpzone, $cpzoneid; + + if (empty($cpzone)) { + return; + } + + $cpcfg = $config['captiveportal'][$cpzone]; + $vcpcfg = $config['voucher'][$cpzone]; + + /* check for expired entries */ + $idletimeout = 0; + $timeout = 0; + if (!empty($cpcfg['timeout']) && is_numeric($cpcfg['timeout'])) { + $timeout = $cpcfg['timeout'] * 60; + } + + if (!empty($cpcfg['idletimeout']) && is_numeric($cpcfg['idletimeout'])) { + $idletimeout = $cpcfg['idletimeout'] * 60; + } + + /* Is there any job to do? */ + if (!$timeout && !$idletimeout && !isset($cpcfg['reauthenticate']) && + !isset($cpcfg['radiussession_timeout']) && !isset($vcpcfg['enable'])) { + return; + } + + $radiussrvs = captiveportal_get_radius_servers(); + + /* Read database */ + /* NOTE: while this can be simplified in non radius case keep as is for now */ + $cpdb = captiveportal_read_db(); + + $unsetindexes = array(); + $voucher_needs_sync = false; + /* + * Snapshot the time here to use for calculation to speed up the process. + * If something is missed next run will catch it! + */ + $pruning_time = time(); + $stop_time = $pruning_time; + foreach ($cpdb as $cpentry) { + + $timedout = false; + $term_cause = 1; + if (empty($cpentry[11])) { + $cpentry[11] = 'first'; + } + $radiusservers = $radiussrvs[$cpentry[11]]; + + /* hard timeout? */ + if ($timeout) { + if (($pruning_time - $cpentry[0]) >= $timeout) { + $timedout = true; + $term_cause = 5; // Session-Timeout + } + } + + /* Session-Terminate-Time */ + if (!$timedout && !empty($cpentry[9])) { + if ($pruning_time >= $cpentry[9]) { + $timedout = true; + $term_cause = 5; // Session-Timeout + } + } + + /* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */ + $uidletimeout = (is_numeric($cpentry[8])) ? $cpentry[8] : $idletimeout; + /* if an idle timeout is specified, get last activity timestamp from ipfw */ + if (!$timedout && $uidletimeout > 0) { + $lastact = captiveportal_get_last_activity($cpentry[2], $cpentry[3]); + /* If the user has logged on but not sent any traffic they will never be logged out. + * We "fix" this by setting lastact to the login timestamp. + */ + $lastact = $lastact ? $lastact : $cpentry[0]; + if ($lastact && (($pruning_time - $lastact) >= $uidletimeout)) { + $timedout = true; + $term_cause = 4; // Idle-Timeout + $stop_time = $lastact; // Entry added to comply with WISPr + } + } + + /* if vouchers are configured, activate session timeouts */ + if (!$timedout && isset($vcpcfg['enable']) && !empty($cpentry[7])) { + if ($pruning_time >= ($cpentry[0] + $cpentry[7])) { + $timedout = true; + $term_cause = 5; // Session-Timeout + $voucher_needs_sync = true; + } + } + + /* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */ + if (!$timedout && isset($cpcfg['radiussession_timeout']) && !empty($cpentry[7])) { + if ($pruning_time >= ($cpentry[0] + $cpentry[7])) { + $timedout = true; + $term_cause = 5; // Session-Timeout + } + } + + if ($timedout) { + captiveportal_disconnect($cpentry, $radiusservers, $term_cause, $stop_time); + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "TIMEOUT"); + $unsetindexes[] = $cpentry[5]; + } + + /* do periodic RADIUS reauthentication? */ + if (!$timedout && !empty($radiusservers)) { + if (isset($cpcfg['radacct_enable'])) { + if ($cpcfg['reauthenticateacct'] == "stopstart") { + /* stop and restart accounting */ + RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno + $cpentry[4], // username + $cpentry[5], // sessionid + $cpentry[0], // start time + $radiusservers, + $cpentry[2], // clientip + $cpentry[3], // clientmac + 10); // NAS Request + $clientsn = (is_ipaddrv6($cpentry[2])) ? 128 : 32; + $_gb = @pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XZEROENTRY, 1, $cpentry[2], $clientsn, $cpentry[3]); + $_gb = @pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XZEROENTRY, 2, $cpentry[2], $clientsn, $cpentry[3]); + RADIUS_ACCOUNTING_START($cpentry[1], // ruleno + $cpentry[4], // username + $cpentry[5], // sessionid + $radiusservers, + $cpentry[2], // clientip + $cpentry[3]); // clientmac + } else if ($cpcfg['reauthenticateacct'] == "interimupdate") { + $session_time = $pruning_time - $cpentry[0]; + if (!empty($cpentry[10]) && $cpentry[10] > 60) { + $interval = $cpentry[10]; + } else { + $interval = 0; + } + $past_interval_min = ($session_time > $interval); + if ($interval != 0) { + $within_interval = ($session_time % $interval >= 0 && $session_time % $interval <= 59); + } + if ($interval === 0 || ($interval > 0 && $past_interval_min && $within_interval)) { + RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno + $cpentry[4], // username + $cpentry[5], // sessionid + $cpentry[0], // start time + $radiusservers, + $cpentry[2], // clientip + $cpentry[3], // clientmac + 10, // NAS Request + true); // Interim Updates + } + } + } + + /* check this user against RADIUS again */ + if (isset($cpcfg['reauthenticate'])) { + $auth_list = RADIUS_AUTHENTICATION($cpentry[4], // username + base64_decode($cpentry[6]), // password + $radiusservers, + $cpentry[2], // clientip + $cpentry[3], // clientmac + $cpentry[1]); // ruleno + if ($auth_list['auth_val'] == 3) { + captiveportal_disconnect($cpentry, $radiusservers, 17); + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_DISCONNECT", $auth_list['reply_message']); + $unsetindexes[] = $cpentry[5]; + } else if ($auth_list['auth_val'] == 2) { + captiveportal_reapply_attributes($cpentry, $auth_list); + } + } + } + } + unset($cpdb); + + captiveportal_prune_old_automac(); + + if ($voucher_needs_sync == true) { + /* Trigger a sync of the vouchers on config */ + send_event("service sync vouchers"); + } + + /* write database */ + if (!empty($unsetindexes)) { + captiveportal_remove_entries($unsetindexes); + } +} + +function captiveportal_prune_old_automac() { + global $g, $config, $cpzone, $cpzoneid; + + if (is_array($config['captiveportal'][$cpzone]['passthrumac']) && isset($config['captiveportal'][$cpzone]['passthrumacaddusername'])) { + $tmpvoucherdb = array(); + $macrules = ""; + $writecfg = false; + foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $eid => $emac) { + if ($emac['logintype'] == "voucher") { + if (isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) { + if (isset($tmpvoucherdb[$emac['username']])) { + $temac = $config['captiveportal'][$cpzone]['passthrumac'][$tmpvoucherdb[$emac['username']]]; + $ruleno = captiveportal_get_ipfw_passthru_ruleno($temac['mac']); + $pipeno = captiveportal_get_dn_passthru_ruleno($temac['mac']); + if ($ruleno) { + captiveportal_free_ipfw_ruleno($ruleno); + $macrules .= "delete {$ruleno}"; + ++$ruleno; + $macrules .= "delete {$ruleno}"; + } + if ($pipeno) { + captiveportal_free_dn_ruleno($pipeno); + $macrules .= "pipe delete {$pipeno}\n"; + ++$pipeno; + $macrules .= "pipe delete {$pipeno}\n"; + } + $writecfg = true; + captiveportal_logportalauth($temac['username'], $temac['mac'], $temac['ip'], "DUPLICATE {$temac['username']} LOGIN - TERMINATING OLD SESSION"); + unset($config['captiveportal'][$cpzone]['passthrumac'][$tmpvoucherdb[$emac['username']]]); + } + $tmpvoucherdb[$emac['username']] = $eid; + } + if (voucher_auth($emac['username']) <= 0) { + $ruleno = captiveportal_get_ipfw_passthru_ruleno($emac['mac']); + $pipeno = captiveportal_get_dn_passthru_ruleno($emac['mac']); + if ($ruleno) { + captiveportal_free_ipfw_ruleno($ruleno); + $macrules .= "delete {$ruleno}"; + ++$ruleno; + $macrules .= "delete {$ruleno}"; + } + if ($pipeno) { + captiveportal_free_dn_ruleno($pipeno); + $macrules .= "pipe delete {$pipeno}\n"; + ++$pipeno; + $macrules .= "pipe delete {$pipeno}\n"; + } + $writecfg = true; + captiveportal_logportalauth($emac['username'], $emac['mac'], $emac['ip'], "EXPIRED {$emac['username']} LOGIN - TERMINATING SESSION"); + unset($config['captiveportal'][$cpzone]['passthrumac'][$eid]); + } + } + } + unset($tmpvoucherdb); + if (!empty($macrules)) { + @file_put_contents("{$g['tmp_path']}/macentry.prunerules.tmp", $macrules); + unset($macrules); + mwexec("/sbin/ipfw -x {$cpzoneid} -q {$g['tmp_path']}/macentry.prunerules.tmp"); + } + if ($writecfg === true) { + write_config("Prune session for auto-added macs"); + } + } +} + +/* remove a single client according to the DB entry */ +function captiveportal_disconnect($dbent, $radiusservers, $term_cause = 1, $stop_time = null) { + global $g, $config, $cpzone, $cpzoneid; + + $stop_time = (empty($stop_time)) ? time() : $stop_time; + + /* this client needs to be deleted - remove ipfw rules */ + if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && !empty($radiusservers)) { + RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno + $dbent[4], // username + $dbent[5], // sessionid + $dbent[0], // start time + $radiusservers, + $dbent[2], // clientip + $dbent[3], // clientmac + $term_cause, // Acct-Terminate-Cause + false, + $stop_time); + } + + if (is_ipaddr($dbent[2])) { + /* Delete client's ip entry from tables 1 and 2. */ + $clientsn = (is_ipaddrv6($dbent[2])) ? 128 : 32; + pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XDEL, 1, $dbent[2], $clientsn, $dbent[3]); + pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XDEL, 2, $dbent[2], $clientsn, $dbent[3]); + /* XXX: Redundant?! Ensure all pf(4) states are killed. */ + $_gb = @pfSense_kill_states($dbent[2]); + $_gb = @pfSense_kill_srcstates($dbent[2]); + } + + /* + * These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal + * We could get an error if the pipe doesn't exist but everything should still be fine + */ + if (!empty($dbent[1])) { + $_gb = @pfSense_pipe_action("pipe delete {$dbent[1]}"); + $_gb = @pfSense_pipe_action("pipe delete " . ($dbent[1]+1)); + + /* Release the ruleno so it can be reallocated to new clients. */ + captiveportal_free_dn_ruleno($dbent[1]); + } + + // XMLRPC Call over to the master Voucher node + if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) { + $syncip = $config['voucher'][$cpzone]['vouchersyncdbip']; + $syncport = $config['voucher'][$cpzone]['vouchersyncport']; + $syncpass = $config['voucher'][$cpzone]['vouchersyncpass']; + $vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername']; + $remote_status = xmlrpc_sync_voucher_disconnect($dbent, $syncip, $syncport, $syncpass, $vouchersyncusername, $term_cause, $stop_time); + } + +} + +/* remove a single client by sessionid */ +function captiveportal_disconnect_client($sessionid, $term_cause = 1, $logoutReason = "LOGOUT") { + global $g, $config; + + $radiusservers = captiveportal_get_radius_servers(); + + /* read database */ + $result = captiveportal_read_db("WHERE sessionid = '{$sessionid}'"); + + /* find entry */ + if (!empty($result)) { + captiveportal_write_db("DELETE FROM captiveportal WHERE sessionid = '{$sessionid}'"); + + foreach ($result as $cpentry) { + if (empty($cpentry[11])) { + $cpentry[11] = 'first'; + } + captiveportal_disconnect($cpentry, $radiusservers[$cpentry[11]], $term_cause); + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT"); + } + unset($result); + } +} + +/* send RADIUS acct stop for all current clients */ +function captiveportal_radius_stop_all() { + global $config, $cpzone; + + if (!isset($config['captiveportal'][$cpzone]['radacct_enable'])) { + return; + } + + $radiusservers = captiveportal_get_radius_servers(); + if (!empty($radiusservers)) { + $cpdb = captiveportal_read_db(); + foreach ($cpdb as $cpentry) { + if (empty($cpentry[11])) { + $cpentry[11] = 'first'; + } + if (!empty($radiusservers[$cpentry[11]])) { + RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno + $cpentry[4], // username + $cpentry[5], // sessionid + $cpentry[0], // start time + $radiusservers[$cpentry[11]], + $cpentry[2], // clientip + $cpentry[3], // clientmac + 7); // Admin Reboot + } + } + } +} + +function captiveportal_passthrumac_configure_entry($macent, $pipeinrule = false) { + global $config, $g, $cpzone; + + $bwUp = 0; + if (!empty($macent['bw_up'])) { + $bwUp = $macent['bw_up']; + } else if (!empty($config['captiveportal'][$cpzone]['bwdefaultup'])) { + $bwUp = $config['captiveportal'][$cpzone]['bwdefaultup']; + } + $bwDown = 0; + if (!empty($macent['bw_down'])) { + $bwDown = $macent['bw_down']; + } else if (!empty($config['captiveportal'][$cpzone]['bwdefaultdn'])) { + $bwDown = $config['captiveportal'][$cpzone]['bwdefaultdn']; + } + + $ruleno = captiveportal_get_next_ipfw_ruleno(); + + if ($macent['action'] == 'pass') { + $rules = ""; + $pipeno = captiveportal_get_next_dn_ruleno(); + + $pipeup = $pipeno; + if ($pipeinrule == true) { + $_gb = @pfSense_pipe_action("pipe {$pipeno} config bw {$bwUp}Kbit/s queue 100 buckets 16"); + } else { + $rules .= "pipe {$pipeno} config bw {$bwUp}Kbit/s queue 100 buckets 16\n"; + } + + $pipedown = $pipeno + 1; + if ($pipeinrule == true) { + $_gb = @pfSense_pipe_action("pipe {$pipedown} config bw {$bwDown}Kbit/s queue 100 buckets 16"); + } else { + $rules .= "pipe {$pipedown} config bw {$bwDown}Kbit/s queue 100 buckets 16\n"; + } + + $rules .= "add {$ruleno} pipe {$pipeup} ip from any to any MAC any {$macent['mac']}\n"; + $ruleno++; + $rules .= "add {$ruleno} pipe {$pipedown} ip from any to any MAC {$macent['mac']} any\n"; + } + + return $rules; +} + +function captiveportal_passthrumac_delete_entry($macent) { + $rules = ""; + + if ($macent['action'] == 'pass') { + $ruleno = captiveportal_get_ipfw_passthru_ruleno($macent['mac']); + + if (!$ruleno) { + return $rules; + } + + captiveportal_free_ipfw_ruleno($ruleno); + + $rules .= "delete {$ruleno}\n"; + $rules .= "delete " . ++$ruleno . "\n"; + + $pipeno = captiveportal_get_dn_passthru_ruleno($macent['mac']); + + if (!empty($pipeno)) { + captiveportal_free_dn_ruleno($pipeno); + $rules .= "pipe delete " . $pipeno . "\n"; + $rules .= "pipe delete " . ++$pipeno . "\n"; + } + } + + return $rules; +} + +function captiveportal_passthrumac_configure($filename = false, $startindex = 0, $stopindex = 0) { + global $config, $g, $cpzone; + + $rules = ""; + + if (is_array($config['captiveportal'][$cpzone]['passthrumac'])) { + if ($stopindex > 0) { + $fd = fopen($filename, "w"); + for ($idx = $startindex; $idx <= $stopindex; $idx++) { + if (isset($config['captiveportal'][$cpzone]['passthrumac'][$idx])) { + $rules = captiveportal_passthrumac_configure_entry($config['captiveportal'][$cpzone]['passthrumac'][$idx]); + fwrite($fd, $rules); + } + } + fclose($fd); + + return; + } else { + $nentries = count($config['captiveportal'][$cpzone]['passthrumac']); + if ($nentries > 2000) { + $nloops = $nentries / 1000; + $remainder= $nentries % 1000; + for ($i = 0; $i < $nloops; $i++) { + mwexec_bg("/usr/local/sbin/fcgicli -f /etc/rc.captiveportal_configure_mac -d \"cpzone={$cpzone}&startidx=" . ($i * 1000) . "&stopidx=" . ((($i+1) * 1000) - 1) . "\""); + } + if ($remainder > 0) { + mwexec_bg("/usr/local/sbin/fcgicli -f /etc/rc.captiveportal_configure_mac -d \"cpzone={$cpzone}&startidx=" . ($i * 1000) . "&stopidx=" . (($i* 1000) + $remainder) ."\""); + } + } else { + foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $macent) { + $rules .= captiveportal_passthrumac_configure_entry($macent, true); + } + } + } + } + + return $rules; +} + +function captiveportal_passthrumac_findbyname($username) { + global $config, $cpzone; + + if (is_array($config['captiveportal'][$cpzone]['passthrumac'])) { + foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $macent) { + if ($macent['username'] == $username) { + return $macent; + } + } + } + return NULL; +} + +/* + * table (3=IN)/(4=OUT) hold allowed ip's without bw limits + */ +function captiveportal_allowedip_configure_entry($ipent, $ishostname = false) { + global $g; + + /* Instead of copying this entire function for something + * easy such as hostname vs ip address add this check + */ + if ($ishostname === true) { + if (!platform_booting()) { + $ipaddress = gethostbyname($ipent['hostname']); + if (!is_ipaddr($ipaddress)) { + return; + } + } else { + $ipaddress = ""; + } + } else { + $ipaddress = $ipent['ip']; + } + + $rules = ""; + $cp_filterdns_conf = ""; + $enBwup = 0; + if (!empty($ipent['bw_up'])) { + $enBwup = intval($ipent['bw_up']); + } else if (!empty($config['captiveportal'][$cpzone]['bwdefaultup'])) { + $enBwup = $config['captiveportal'][$cpzone]['bwdefaultup']; + } + $enBwdown = 0; + if (!empty($ipent['bw_down'])) { + $enBwdown = intval($ipent['bw_down']); + } else if (!empty($config['captiveportal'][$cpzone]['bwdefaultdn'])) { + $enBwdown = $config['captiveportal'][$cpzone]['bwdefaultdn']; + } + + $pipeno = captiveportal_get_next_dn_ruleno(); + $_gb = @pfSense_pipe_action("pipe {$pipeno} config bw {$enBwup}Kbit/s queue 100 buckets 16"); + $pipedown = $pipeno + 1; + $_gb = @pfSense_pipe_action("pipe {$pipedown} config bw {$enBwdown}Kbit/s queue 100 buckets 16"); + if ($ishostname === true) { + $cp_filterdns_conf .= "ipfw {$ipent['hostname']} 3 pipe {$pipeno}\n"; + $cp_filterdns_conf .= "ipfw {$ipent['hostname']} 4 pipe {$pipedown}\n"; + if (!is_ipaddr($ipaddress)) { + return array("", $cp_filterdns_conf); + } + } + $subnet = ""; + if (!empty($ipent['sn'])) { + $subnet = "/{$ipent['sn']}"; + } + $rules .= "table 3 add {$ipaddress}{$subnet} {$pipeno}\n"; + $rules .= "table 4 add {$ipaddress}{$subnet} {$pipedown}\n"; + + if ($ishostname === true) { + return array($rules, $cp_filterdns_conf); + } else { + return $rules; + } +} + +function captiveportal_allowedhostname_configure() { + global $config, $g, $cpzone, $cpzoneid; + + $rules = ""; + if (is_array($config['captiveportal'][$cpzone]['allowedhostname'])) { + $rules = "\n# captiveportal_allowedhostname_configure()\n"; + $cp_filterdns_conf = ""; + foreach ($config['captiveportal'][$cpzone]['allowedhostname'] as $hostnameent) { + $tmprules = captiveportal_allowedip_configure_entry($hostnameent, true); + $rules .= $tmprules[0]; + $cp_filterdns_conf .= $tmprules[1]; + } + $cp_filterdns_filename = "{$g['varetc_path']}/filterdns-{$cpzone}-captiveportal.conf"; + @file_put_contents($cp_filterdns_filename, $cp_filterdns_conf); + unset($cp_filterdns_conf); + if (isvalidpid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid")) { + sigkillbypid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid", "HUP"); + } else { + mwexec("/usr/local/sbin/filterdns -p {$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid -i 300 -c {$cp_filterdns_filename} -y {$cpzoneid} -d 1"); + } + } else { + killbypid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid"); + @unlink("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid"); + } + + return $rules; +} + +function captiveportal_allowedip_configure() { + global $config, $g, $cpzone; + + $rules = ""; + if (is_array($config['captiveportal'][$cpzone]['allowedip'])) { + foreach ($config['captiveportal'][$cpzone]['allowedip'] as $ipent) { + $rules .= captiveportal_allowedip_configure_entry($ipent); + } + } + + return $rules; +} + +/* get last activity timestamp given client IP address */ +function captiveportal_get_last_activity($ip, $mac = NULL, $table = 1) { + global $cpzoneid; + + $ipfwoutput = pfSense_ipfw_getTablestats($cpzoneid, IP_FW_TABLE_XLISTENTRY, $table, $ip, $mac); + /* Reading only from one of the tables is enough of approximation. */ + if (is_array($ipfwoutput)) { + /* Workaround for #46652 */ + if ($ipfwoutput['packets'] > 0) { + return $ipfwoutput['timestamp']; + } else { + return 0; + } + } + + return 0; +} + +function captiveportal_init_radius_servers() { + global $config, $g, $cpzone; + + /* generate radius server database */ + if ($config['captiveportal'][$cpzone]['radiusip'] && + (!isset($config['captiveportal'][$cpzone]['auth_method']) || $config['captiveportal'][$cpzone]['auth_method'] == "radius")) { + $radiusip = $config['captiveportal'][$cpzone]['radiusip']; + $radiusip2 = ($config['captiveportal'][$cpzone]['radiusip2']) ? $config['captiveportal'][$cpzone]['radiusip2'] : null; + $radiusip3 = ($config['captiveportal'][$cpzone]['radiusip3']) ? $config['captiveportal'][$cpzone]['radiusip3'] : null; + $radiusip4 = ($config['captiveportal'][$cpzone]['radiusip4']) ? $config['captiveportal'][$cpzone]['radiusip4'] : null; + + if ($config['captiveportal'][$cpzone]['radiusport']) { + $radiusport = $config['captiveportal'][$cpzone]['radiusport']; + } else { + $radiusport = 1812; + } + if ($config['captiveportal'][$cpzone]['radiusacctport']) { + $radiusacctport = $config['captiveportal'][$cpzone]['radiusacctport']; + } else { + $radiusacctport = 1813; + } + if ($config['captiveportal'][$cpzone]['radiusport2']) { + $radiusport2 = $config['captiveportal'][$cpzone]['radiusport2']; + } else { + $radiusport2 = 1812; + } + if ($config['captiveportal'][$cpzone]['radiusport3']) { + $radiusport3 = $config['captiveportal'][$cpzone]['radiusport3']; + } else { + $radiusport3 = 1812; + } + if ($config['captiveportal'][$cpzone]['radiusport4']) { + $radiusport4 = $config['captiveportal'][$cpzone]['radiusport4']; + } else { + $radiusport4 = 1812; + } + + $radiuskey = $config['captiveportal'][$cpzone]['radiuskey']; + $radiuskey2 = $config['captiveportal'][$cpzone]['radiuskey2']; + $radiuskey3 = $config['captiveportal'][$cpzone]['radiuskey3']; + $radiuskey4 = $config['captiveportal'][$cpzone]['radiuskey4']; + + $cprdsrvlck = lock("captiveportalradius{$cpzone}", LOCK_EX); + $fd = @fopen("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db", "w"); + if (!$fd) { + captiveportal_syslog("Error: cannot open radius DB file in captiveportal_configure().\n"); + unlock($cprdsrvlck); + return 1; + } + if (isset($radiusip)) { + fwrite($fd, $radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . ",first"); + } + if (isset($radiusip2)) { + fwrite($fd, "\n" . $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2 . ",first"); + } + if (isset($radiusip3)) { + fwrite($fd, "\n" . $radiusip3 . "," . $radiusport3 . "," . $radiusacctport . "," . $radiuskey3 . ",second"); + } + if (isset($radiusip4)) { + fwrite($fd, "\n" . $radiusip4 . "," . $radiusport4 . "," . $radiusacctport . "," . $radiuskey4 . ",second"); + } + + fclose($fd); + unlock($cprdsrvlck); + } +} + +/* read RADIUS servers into array */ +function captiveportal_get_radius_servers() { + global $g, $cpzone; + + $cprdsrvlck = lock("captiveportalradius{$cpzone}"); + if (file_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db")) { + $radiusservers = array(); + $cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db", + FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if ($cpradiusdb) { + foreach ($cpradiusdb as $cpradiusentry) { + $line = trim($cpradiusentry); + if ($line) { + $radsrv = array(); + list($radsrv['ipaddr'], $radsrv['port'], $radsrv['acctport'], $radsrv['key'], $context) = explode(",", $line); + } + if (empty($context)) { + if (!is_array($radiusservers['first'])) { + $radiusservers['first'] = array(); + } + $radiusservers['first'] = $radsrv; + } else { + if (!is_array($radiusservers[$context])) { + $radiusservers[$context] = array(); + } + $radiusservers[$context][] = $radsrv; + } + } + } + unlock($cprdsrvlck); + return $radiusservers; + } + + unlock($cprdsrvlck); + return false; +} + +/* log successful captive portal authentication to syslog */ +/* part of this code from php.net */ +function captiveportal_logportalauth($user, $mac, $ip, $status, $message = null) { + // Log it + if (!$message) { + $message = "{$status}: {$user}, {$mac}, {$ip}"; + } else { + $message = trim($message); + $message = "{$status}: {$user}, {$mac}, {$ip}, {$message}"; + } + captiveportal_syslog($message); +} + +/* log simple messages to syslog */ +function captiveportal_syslog($message) { + global $cpzone; + + $message = trim($message); + $message = "Zone: {$cpzone} - {$message}"; + openlog("logportalauth", LOG_PID, LOG_LOCAL4); + // Log it + syslog(LOG_INFO, $message); + closelog(); +} + +function radius($username, $password, $clientip, $clientmac, $type, $radiusctx = null) { + global $g, $config, $cpzoneid; + + $pipeno = captiveportal_get_next_dn_ruleno(); + + /* If the pool is empty, return appropriate message and fail authentication */ + if (empty($pipeno)) { + $auth_list = array(); + $auth_list['auth_val'] = 1; + $auth_list['error'] = "System reached maximum login capacity"; + return $auth_list; + } + + $radiusservers = captiveportal_get_radius_servers(); + + if (is_null($radiusctx)) { + $radiusctx = 'first'; + } + + $auth_list = RADIUS_AUTHENTICATION($username, + $password, + $radiusservers[$radiusctx], + $clientip, + $clientmac, + $pipeno); + + if ($auth_list['auth_val'] == 2) { + captiveportal_logportalauth($username, $clientmac, $clientip, $type); + $sessionid = portal_allow($clientip, + $clientmac, + $username, + $password, + $auth_list, + $pipeno, + $radiusctx); + } else { + captiveportal_free_dn_ruleno($pipeno); + } + + return $auth_list; +} + +function captiveportal_opendb() { + global $g, $cpzone; + + $db_path = "{$g['vardb_path']}/captiveportal{$cpzone}.db"; + $createquery = "CREATE TABLE IF NOT EXISTS captiveportal (" . + "allow_time INTEGER, pipeno INTEGER, ip TEXT, mac TEXT, username TEXT, " . + "sessionid TEXT, bpassword TEXT, session_timeout INTEGER, idle_timeout INTEGER, " . + "session_terminate_time INTEGER, interim_interval INTEGER, radiusctx TEXT); " . + "CREATE UNIQUE INDEX IF NOT EXISTS idx_active ON captiveportal (sessionid, username); " . + "CREATE INDEX IF NOT EXISTS user ON captiveportal (username); " . + "CREATE INDEX IF NOT EXISTS ip ON captiveportal (ip); " . + "CREATE INDEX IF NOT EXISTS starttime ON captiveportal (allow_time)"; + + try { + $DB = new SQLite3($db_path); + } catch (Exception $e) { + captiveportal_syslog("Could not open {$db_path} as an sqlite database for {$cpzone}. Error message: " . $e->getMessage() . " -- Trying again."); + unlink_if_exists($db_path); + try { + $DB = new SQLite3($db_path); + } catch (Exception $e) { + captiveportal_syslog("Still could not open {$db_path} as an sqlite database for {$cpzone}. Error message: " . $e->getMessage() . " -- Remove the database file manually and ensure there is enough free space."); + return; + } + } + + if (!$DB) { + captiveportal_syslog("Could not open {$db_path} as an sqlite database for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Trying again."); + unlink_if_exists($db_path); + $DB = new SQLite3($db_path); + if (!$DB) { + captiveportal_syslog("Still could not open {$db_path} as an sqlite database for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Remove the database file manually and ensure there is enough free space."); + return; + } + } + + if (! $DB->exec($createquery)) { + captiveportal_syslog("Error during table {$cpzone} creation. Error message: {$DB->lastErrorMsg()}. Resetting and trying again."); + + /* If unable to initialize the database, reset and try again. */ + $DB->close(); + unset($DB); + unlink_if_exists($db_path); + $DB = new SQLite3($db_path); + if ($DB->exec($createquery)) { + captiveportal_syslog("Successfully reinitialized tables for {$cpzone} -- database has been reset."); + } else { + captiveportal_syslog("Still unable to create tables for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Remove the database file manually and try again."); + } + } + + return $DB; +} + +/* read captive portal DB into array */ +function captiveportal_read_db($query = "") { + $cpdb = array(); + + $DB = captiveportal_opendb(); + if ($DB) { + $response = $DB->query("SELECT * FROM captiveportal {$query}"); + if ($response != FALSE) { + while ($row = $response->fetchArray()) { + $cpdb[] = $row; + } + } + $DB->close(); + } + + return $cpdb; +} + +function captiveportal_remove_entries($remove) { + + if (!is_array($remove) || empty($remove)) { + return; + } + + $query = "DELETE FROM captiveportal WHERE sessionid in ("; + foreach ($remove as $idx => $unindex) { + $query .= "'{$unindex}'"; + if ($idx < (count($remove) - 1)) { + $query .= ","; + } + } + $query .= ")"; + captiveportal_write_db($query); +} + +/* write captive portal DB */ +function captiveportal_write_db($queries) { + global $g; + + if (is_array($queries)) { + $query = implode(";", $queries); + } else { + $query = $queries; + } + + $DB = captiveportal_opendb(); + if ($DB) { + $DB->exec("BEGIN TRANSACTION"); + $result = $DB->exec($query); + if (!$result) { + captiveportal_syslog("Trying to modify DB returned error: {$DB->lastErrorMsg()}"); + } else { + $DB->exec("END TRANSACTION"); + } + $DB->close(); + return $result; + } else { + return true; + } +} + +function captiveportal_write_elements() { + global $g, $config, $cpzone; + + $cpcfg = $config['captiveportal'][$cpzone]; + + if (!is_dir($g['captiveportal_element_path'])) { + @mkdir($g['captiveportal_element_path']); + } + + if (is_array($cpcfg['element'])) { + conf_mount_rw(); + foreach ($cpcfg['element'] as $data) { + if (!@file_put_contents("{$g['captiveportal_element_path']}/{$data['name']}", base64_decode($data['content']))) { + printf(gettext("Error: cannot open '%s' in captiveportal_write_elements()%s"), $data['name'], "\n"); + return 1; + } + if (!file_exists("{$g['captiveportal_path']}/{$data['name']}")) { + @symlink("{$g['captiveportal_element_path']}/{$data['name']}", "{$g['captiveportal_path']}/{$data['name']}"); + } + } + conf_mount_ro(); + } + + return 0; +} + +function captiveportal_free_dnrules($rulenos_start = 2000, $rulenos_range_max = 64500) { + global $cpzone; + + $cpruleslck = lock("captiveportalrulesdn", LOCK_EX); + if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) { + $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules")); + $ridx = $rulenos_start; + while ($ridx < $rulenos_range_max) { + if ($rules[$ridx] == $cpzone) { + $rules[$ridx] = false; + $ridx++; + $rules[$ridx] = false; + $ridx++; + } else { + $ridx += 2; + } + } + file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules)); + unset($rules); + } + unlock($cpruleslck); +} + +function captiveportal_get_next_dn_ruleno($rulenos_start = 2000, $rulenos_range_max = 64500) { + global $config, $g, $cpzone; + + $cpruleslck = lock("captiveportalrulesdn", LOCK_EX); + $ruleno = 0; + if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) { + $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules")); + $ridx = $rulenos_start; + while ($ridx < $rulenos_range_max) { + if (empty($rules[$ridx])) { + $ruleno = $ridx; + $rules[$ridx] = $cpzone; + $ridx++; + $rules[$ridx] = $cpzone; + break; + } else { + $ridx += 2; + } + } + } else { + $rules = array_pad(array(), $rulenos_range_max, false); + $ruleno = $rulenos_start; + $rules[$rulenos_start] = $cpzone; + $rulenos_start++; + $rules[$rulenos_start] = $cpzone; + } + file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules)); + unlock($cpruleslck); + unset($rules); + + return $ruleno; +} + +function captiveportal_free_dn_ruleno($ruleno) { + global $config, $g; + + $cpruleslck = lock("captiveportalrulesdn", LOCK_EX); + if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) { + $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules")); + $rules[$ruleno] = false; + $ruleno++; + $rules[$ruleno] = false; + file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules)); + unset($rules); + } + unlock($cpruleslck); +} + +function captiveportal_get_dn_passthru_ruleno($value) { + global $config, $g, $cpzone, $cpzoneid; + + $cpcfg = $config['captiveportal'][$cpzone]; + if (!isset($cpcfg['enable'])) { + return NULL; + } + + $cpruleslck = lock("captiveportalrulesdn", LOCK_EX); + $ruleno = NULL; + if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) { + $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules")); + unset($output); + $_gb = exec("/sbin/ipfw -x {$cpzoneid} show | /usr/bin/grep " . escapeshellarg($value) . " | /usr/bin/grep -v grep | /usr/bin/awk '{print $5}' | /usr/bin/head -n 1", $output); + $ruleno = intval($output[0]); + if (!$rules[$ruleno]) { + $ruleno = NULL; + } + unset($rules); + } + unlock($cpruleslck); + + return $ruleno; +} + +/* + * This function will calculate the lowest free firewall ruleno + * within the range specified based on the actual logged on users + * + */ +function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2, $rulenos_range_max = 64500) { + global $config, $g, $cpzone; + + $cpcfg = $config['captiveportal'][$cpzone]; + if (!isset($cpcfg['enable'])) { + return NULL; + } + + $cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX); + $ruleno = 0; + if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) { + $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")); + $ridx = $rulenos_start; + while ($ridx < $rulenos_range_max) { + if (empty($rules[$ridx])) { + $ruleno = $ridx; + $rules[$ridx] = $cpzone; + $ridx++; + $rules[$ridx] = $cpzone; + break; + } else { + /* + * This allows our traffic shaping pipes to be the in pipe the same as ruleno + * and the out pipe ruleno + 1. + */ + $ridx += 2; + } + } + } else { + $rules = array_pad(array(), $rulenos_range_max, false); + $ruleno = $rulenos_start; + $rules[$rulenos_start] = $cpzone; + $rulenos_start++; + $rules[$rulenos_start] = $cpzone; + } + file_put_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules", serialize($rules)); + unlock($cpruleslck); + unset($rules); + + return $ruleno; +} + +function captiveportal_free_ipfw_ruleno($ruleno) { + global $config, $g, $cpzone; + + $cpcfg = $config['captiveportal'][$cpzone]; + if (!isset($cpcfg['enable'])) { + return NULL; + } + + $cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX); + if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) { + $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")); + $rules[$ruleno] = false; + $ruleno++; + $rules[$ruleno] = false; + file_put_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules", serialize($rules)); + unset($rules); + } + unlock($cpruleslck); +} + +function captiveportal_get_ipfw_passthru_ruleno($value) { + global $config, $g, $cpzone, $cpzoneid; + + $cpcfg = $config['captiveportal'][$cpzone]; + if (!isset($cpcfg['enable'])) { + return NULL; + } + + $cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX); + $ruleno = NULL; + if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) { + $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")); + unset($output); + $_gb = exec("/sbin/ipfw -x {$cpzoneid} show | /usr/bin/grep " . escapeshellarg($value) . " | /usr/bin/grep -v grep | /usr/bin/awk '{print $1}' | /usr/bin/head -n 1", $output); + $ruleno = intval($output[0]); + if (!$rules[$ruleno]) { + $ruleno = NULL; + } + unset($rules); + } + unlock($cpruleslck); + + return $ruleno; +} + +/** + * This function will calculate the traffic produced by a client + * based on its firewall rule + * + * Point of view: NAS + * + * Input means: from the client + * Output means: to the client + * + */ + +function getVolume($ip, $mac = NULL) { + global $config, $cpzone, $cpzoneid; + + $reverse = empty($config['captiveportal'][$cpzone]['reverseacct']) ? false : true; + $volume = array(); + // Initialize vars properly, since we don't want NULL vars + $volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ; + + $ipfw = pfSense_ipfw_getTablestats($cpzoneid, IP_FW_TABLE_XLISTENTRY, 1, $ip, $mac); + if (is_array($ipfw)) { + if ($reverse) { + $volume['output_pkts'] = $ipfw['packets']; + $volume['output_bytes'] = $ipfw['bytes']; + } + else { + $volume['input_pkts'] = $ipfw['packets']; + $volume['input_bytes'] = $ipfw['bytes']; + } + } + + $ipfw = pfSense_ipfw_getTablestats($cpzoneid, IP_FW_TABLE_XLISTENTRY, 2, $ip, $mac); + if (is_array($ipfw)) { + if ($reverse) { + $volume['input_pkts'] = $ipfw['packets']; + $volume['input_bytes'] = $ipfw['bytes']; + } + else { + $volume['output_pkts'] = $ipfw['packets']; + $volume['output_bytes'] = $ipfw['bytes']; + } + } + + return $volume; +} + +/** + * Get the NAS-IP-Address based on the current wan address + * + * Use functions in interfaces.inc to find this out + * + */ + +function getNasIP() { + global $config, $cpzone; + + if (empty($config['captiveportal'][$cpzone]['radiussrcip_attribute'])) { + $nasIp = get_interface_ip(); + } else { + if (is_ipaddr($config['captiveportal'][$cpzone]['radiussrcip_attribute'])) { + $nasIp = $config['captiveportal'][$cpzone]['radiussrcip_attribute']; + } else { + $nasIp = get_interface_ip($config['captiveportal'][$cpzone]['radiussrcip_attribute']); + } + } + + if (!is_ipaddr($nasIp)) { + $nasIp = "0.0.0.0"; + } + + return $nasIp; +} + +function portal_ip_from_client_ip($cliip) { + global $config, $cpzone; + + $isipv6 = is_ipaddrv6($cliip); + $interfaces = explode(",", $config['captiveportal'][$cpzone]['interface']); + foreach ($interfaces as $cpif) { + if ($isipv6) { + $ip = get_interface_ipv6($cpif); + $sn = get_interface_subnetv6($cpif); + } else { + $ip = get_interface_ip($cpif); + $sn = get_interface_subnet($cpif); + } + if (ip_in_subnet($cliip, "{$ip}/{$sn}")) { + return $ip; + } + } + + $inet = ($isipv6) ? '-inet6' : '-inet'; + $iface = exec_command("/sbin/route -n get {$inet} {$cliip} | /usr/bin/awk '/interface/ { print \$2; };'"); + $iface = trim($iface, "\n"); + if (!empty($iface)) { + $ip = ($isipv6) ? find_interface_ipv6($iface) : find_interface_ip($iface); + if (is_ipaddr($ip)) { + return $ip; + } + } + + // doesn't match up to any particular interface + // so let's set the portal IP to what PHP says + // the server IP issuing the request is. + // allows same behavior as 1.2.x where IP isn't + // in the subnet of any CP interface (static routes, etc.) + // rather than forcing to DNS hostname resolution + $ip = $_SERVER['SERVER_ADDR']; + if (is_ipaddr($ip)) { + return $ip; + } + + return false; +} + +function portal_hostname_from_client_ip($cliip) { + global $config, $cpzone; + + $cpcfg = $config['captiveportal'][$cpzone]; + + if (isset($cpcfg['httpslogin'])) { + $listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : ($cpcfg['zoneid'] + 8001); + $ourhostname = $cpcfg['httpsname']; + + if ($listenporthttps != 443) { + $ourhostname .= ":" . $listenporthttps; + } + } else { + $listenporthttp = $cpcfg['listenporthttp'] ? $cpcfg['listenporthttp'] : ($cpcfg['zoneid'] + 8000); + $ifip = portal_ip_from_client_ip($cliip); + if (!$ifip) { + $ourhostname = "{$config['system']['hostname']}.{$config['system']['domain']}"; + } else { + $ourhostname = (is_ipaddrv6($ifip)) ? "[{$ifip}]" : "{$ifip}"; + } + + if ($listenporthttp != 80) { + $ourhostname .= ":" . $listenporthttp; + } + } + + return $ourhostname; +} + +/* functions move from index.php */ + +function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null) { + global $g, $config, $cpzone; + + /* Get captive portal layout */ + if ($type == "redir") { + header("Location: {$redirurl}"); + return; + } else if ($type == "login") { + $htmltext = get_include_contents("{$g['varetc_path']}/captiveportal_{$cpzone}.html"); + } else { + $htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html"); + } + + $cpcfg = $config['captiveportal'][$cpzone]; + + /* substitute the PORTAL_REDIRURL variable */ + if ($cpcfg['preauthurl']) { + $htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext); + $htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext); + } + + /* substitute other variables */ + $ourhostname = portal_hostname_from_client_ip($clientip); + $protocol = (isset($cpcfg['httpslogin'])) ? 'https://' : 'http://'; + $htmltext = str_replace("\$PORTAL_ACTION\$", "{$protocol}{$ourhostname}/", $htmltext); + $htmltext = str_replace("#PORTAL_ACTION#", "{$protocol}{$ourhostname}/", $htmltext); + + $htmltext = str_replace("\$PORTAL_ZONE\$", htmlspecialchars($cpzone), $htmltext); + $htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext); + $htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext); + $htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext); + $htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext); + + // Special handling case for captive portal master page so that it can be ran + // through the PHP interpreter using the include method above. We convert the + // $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out. + $htmltext = str_replace("#PORTAL_ZONE#", htmlspecialchars($cpzone), $htmltext); + $htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext); + $htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext); + $htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext); + $htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext); + $htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext); + $htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext); + + echo $htmltext; +} + +function portal_mac_radius($clientmac, $clientip) { + global $config, $cpzone; + + $radmac_secret = $config['captiveportal'][$cpzone]['radmac_secret']; + + /* authentication against the radius server */ + $username = mac_format($clientmac); + $auth_list = radius($username, $radmac_secret, $clientip, $clientmac, "MACHINE LOGIN"); + if ($auth_list['auth_val'] == 2) { + return TRUE; + } + + if (!empty($auth_list['url_redirection'])) { + portal_reply_page($auth_list['url_redirection'], "redir"); + } + + return FALSE; +} + +function captiveportal_reapply_attributes($cpentry, $attributes) { + global $config, $cpzone, $g; + + if (isset($config['captiveportal'][$cpzone]['peruserbw'])) { + $dwfaultbw_up = !empty($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0; + $dwfaultbw_down = !empty($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0; + } else { + $dwfaultbw_up = $dwfaultbw_down = 0; + } + $bw_up = !empty($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $dwfaultbw_up; + $bw_down = !empty($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $dwfaultbw_down; + $bw_up_pipeno = $cpentry[1]; + $bw_down_pipeno = $cpentry[1]+1; + + $_gb = @pfSense_pipe_action("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16"); + $_gb = @pfSense_pipe_action("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16"); + //captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_BANDWIDTH_REAPPLY", "{$bw_up}/{$bw_down}"); + + unset($bw_up_pipeno, $bw_down_pipeno, $bw_up, $bw_down); +} + +function portal_allow($clientip, $clientmac, $username, $password = null, $attributes = null, $pipeno = null, $radiusctx = null) { + global $redirurl, $g, $config, $type, $passthrumac, $_POST, $cpzone, $cpzoneid; + + // Ensure we create an array if we are missing attributes + if (!is_array($attributes)) { + $attributes = array(); + } + + unset($sessionid); + + /* Do not allow concurrent login execution. */ + $cpdblck = lock("captiveportaldb{$cpzone}", LOCK_EX); + + if ($attributes['voucher']) { + $remaining_time = $attributes['session_timeout']; + } + + $writecfg = false; + /* Find an existing session */ + if ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && $passthrumac) { + if (isset($config['captiveportal'][$cpzone]['passthrumacadd'])) { + $mac = captiveportal_passthrumac_findbyname($username); + if (!empty($mac)) { + if ($_POST['replacemacpassthru']) { + foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $idx => $macent) { + if ($macent['mac'] == $mac['mac']) { + $macrules = ""; + $ruleno = captiveportal_get_ipfw_passthru_ruleno($mac['mac']); + $pipeno = captiveportal_get_dn_passthru_ruleno($mac['mac']); + if ($ruleno) { + captiveportal_free_ipfw_ruleno($ruleno); + $macrules .= "delete {$ruleno}\n"; + ++$ruleno; + $macrules .= "delete {$ruleno}\n"; + } + if ($pipeno) { + captiveportal_free_dn_ruleno($pipeno); + $macrules .= "pipe delete {$pipeno}\n"; + ++$pipeno; + $macrules .= "pipe delete {$pipeno}\n"; + } + unset($config['captiveportal'][$cpzone]['passthrumac'][$idx]); + $mac['action'] = 'pass'; + $mac['mac'] = $clientmac; + $config['captiveportal'][$cpzone]['passthrumac'][] = $mac; + $macrules .= captiveportal_passthrumac_configure_entry($mac); + file_put_contents("{$g['tmp_path']}/macentry_{$cpzone}.rules.tmp", $macrules); + mwexec("/sbin/ipfw -x {$cpzoneid} -q {$g['tmp_path']}/macentry_{$cpzone}.rules.tmp"); + $writecfg = true; + $sessionid = true; + break; + } + } + } else { + portal_reply_page($redirurl, "error", "Username: {$username} is already authenticated using another MAC address.", + $clientmac, $clientip, $username, $password); + unlock($cpdblck); + return; + } + } + } + } + + /* read in client database */ + $query = "WHERE ip = '{$clientip}'"; + $tmpusername = strtolower($username); + if (isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) { + $query .= " OR (username != 'unauthenticated' AND lower(username) = '{$tmpusername}')"; + } + $cpdb = captiveportal_read_db($query); + + /* Snapshot the timestamp */ + $allow_time = time(); + $radiusservers = captiveportal_get_radius_servers(); + $unsetindexes = array(); + if (is_null($radiusctx)) { + $radiusctx = 'first'; + } + + foreach ($cpdb as $cpentry) { + if (empty($cpentry[11])) { + $cpentry[11] = 'first'; + } + /* on the same ip */ + if ($cpentry[2] == $clientip) { + if (isset($config['captiveportal'][$cpzone]['nomacfilter']) || $cpentry[3] == $clientmac) { + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - REUSING OLD SESSION"); + } else { + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - REUSING IP {$cpentry[2]} WITH DIFFERENT MAC ADDRESS {$cpentry[3]}"); + } + $sessionid = $cpentry[5]; + break; + } elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) { + // user logged in with an active voucher. Check for how long and calculate + // how much time we can give him (voucher credit - used time) + $remaining_time = $cpentry[0] + $cpentry[7] - $allow_time; + if ($remaining_time < 0) { // just in case. + $remaining_time = 0; + } + + /* This user was already logged in so we disconnect the old one */ + captiveportal_disconnect($cpentry, $radiusservers[$cpentry[11]], 13); + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - TERMINATING OLD SESSION"); + $unsetindexes[] = $cpentry[5]; + break; + } elseif ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && ($username != 'unauthenticated')) { + /* on the same username */ + if (strcasecmp($cpentry[4], $username) == 0) { + /* This user was already logged in so we disconnect the old one */ + captiveportal_disconnect($cpentry, $radiusservers[$cpentry[11]], 13); + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - TERMINATING OLD SESSION"); + $unsetindexes[] = $cpentry[5]; + break; + } + } + } + unset($cpdb); + + if (!empty($unsetindexes)) { + captiveportal_remove_entries($unsetindexes); + } + + if ($attributes['voucher'] && $remaining_time <= 0) { + return 0; // voucher already used and no time left + } + + if (!isset($sessionid)) { + /* generate unique session ID */ + $tod = gettimeofday(); + $sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16); + + if ($passthrumac) { + $mac = array(); + $mac['action'] = 'pass'; + $mac['mac'] = $clientmac; + $mac['ip'] = $clientip; /* Used only for logging */ + if (isset($config['captiveportal'][$cpzone]['passthrumacaddusername'])) { + $mac['username'] = $username; + if ($attributes['voucher']) { + $mac['logintype'] = "voucher"; + } + } + if ($username == "unauthenticated") { + $mac['descr'] = "Auto-added"; + } else { + $mac['descr'] = "Auto-added for user {$username}"; + } + if (!empty($bw_up)) { + $mac['bw_up'] = $bw_up; + } + if (!empty($bw_down)) { + $mac['bw_down'] = $bw_down; + } + if (!is_array($config['captiveportal'][$cpzone]['passthrumac'])) { + $config['captiveportal'][$cpzone]['passthrumac'] = array(); + } + $config['captiveportal'][$cpzone]['passthrumac'][] = $mac; + unlock($cpdblck); + $macrules = captiveportal_passthrumac_configure_entry($mac); + file_put_contents("{$g['tmp_path']}/macentry_{$cpzone}.rules.tmp", $macrules); + mwexec("/sbin/ipfw -x {$cpzoneid} -q {$g['tmp_path']}/macentry_{$cpzone}.rules.tmp"); + $writecfg = true; + } else { + /* See if a pipeno is passed, if not start sessions because this means there isn't one atm */ + if (is_null($pipeno)) { + $pipeno = captiveportal_get_next_dn_ruleno(); + } + + /* if the pool is empty, return appropriate message and exit */ + if (is_null($pipeno)) { + portal_reply_page($redirurl, "error", "System reached maximum login capacity"); + log_error("Zone: {$cpzone} - WARNING! Captive portal has reached maximum login capacity"); + unlock($cpdblck); + return; + } + + if (isset($config['captiveportal'][$cpzone]['peruserbw'])) { + $dwfaultbw_up = !empty($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0; + $dwfaultbw_down = !empty($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0; + } else { + $dwfaultbw_up = $dwfaultbw_down = 0; + } + $bw_up = !empty($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $dwfaultbw_up; + $bw_down = !empty($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $dwfaultbw_down; + + $bw_up_pipeno = $pipeno; + $bw_down_pipeno = $pipeno + 1; + //$bw_up /= 1000; // Scale to Kbit/s + $_gb = @pfSense_pipe_action("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16"); + $_gb = @pfSense_pipe_action("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16"); + + $clientsn = (is_ipaddrv6($clientip)) ? 128 : 32; + if (!isset($config['captiveportal'][$cpzone]['nomacfilter'])) { + $_gb = @pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XADD, 1, $clientip, $clientsn, $clientmac, $bw_up_pipeno); + } else { + $_gb = @pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XADD, 1, $clientip, $clientsn, NULL, $bw_up_pipeno); + } + + if (!isset($config['captiveportal'][$cpzone]['nomacfilter'])) { + $_gb = @pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XADD, 2, $clientip, $clientsn, $clientmac, $bw_down_pipeno); + } else { + $_gb = @pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XADD, 2, $clientip, $clientsn, NULL, $bw_down_pipeno); + } + + if ($attributes['voucher']) { + $attributes['session_timeout'] = $remaining_time; + } + + /* handle empty attributes */ + $session_timeout = (!empty($attributes['session_timeout'])) ? $attributes['session_timeout'] : 'NULL'; + $idle_timeout = (!empty($attributes['idle_timeout'])) ? $attributes['idle_timeout'] : 'NULL'; + $session_terminate_time = (!empty($attributes['session_terminate_time'])) ? $attributes['session_terminate_time'] : 'NULL'; + $interim_interval = (!empty($attributes['interim_interval'])) ? $attributes['interim_interval'] : 'NULL'; + + /* escape username */ + $safe_username = SQLite3::escapeString($username); + + /* encode password in Base64 just in case it contains commas */ + $bpassword = base64_encode($password); + $insertquery = "INSERT INTO captiveportal (allow_time, pipeno, ip, mac, username, sessionid, bpassword, session_timeout, idle_timeout, session_terminate_time, interim_interval, radiusctx) "; + $insertquery .= "VALUES ({$allow_time}, {$pipeno}, '{$clientip}', '{$clientmac}', '{$safe_username}', '{$sessionid}', '{$bpassword}', "; + $insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval}, '{$radiusctx}')"; + + /* store information to database */ + captiveportal_write_db($insertquery); + unlock($cpdblck); + unset($insertquery, $bpassword); + + if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && !empty($radiusservers[$radiusctx])) { + $acct_val = RADIUS_ACCOUNTING_START($pipeno, $username, $sessionid, $radiusservers[$radiusctx], $clientip, $clientmac); + if ($acct_val == 1) { + captiveportal_logportalauth($username, $clientmac, $clientip, $type, "RADIUS ACCOUNTING FAILED"); + } + } + } + } else { + /* NOTE: #3062-11 If the pipeno has been allocated free it to not DoS the CP and maintain proper operation as in radius() case */ + if (!is_null($pipeno)) { + captiveportal_free_dn_ruleno($pipeno); + } + + unlock($cpdblck); + } + + if ($writecfg == true) { + write_config(); + } + + /* redirect user to desired destination */ + if (!empty($attributes['url_redirection'])) { + $my_redirurl = $attributes['url_redirection']; + } else if (!empty($redirurl)) { + $my_redirurl = $redirurl; + } else if (!empty($config['captiveportal'][$cpzone]['redirurl'])) { + $my_redirurl = $config['captiveportal'][$cpzone]['redirurl']; + } + + if (isset($config['captiveportal'][$cpzone]['logoutwin_enable']) && !$passthrumac) { + $ourhostname = portal_hostname_from_client_ip($clientip); + $protocol = (isset($config['captiveportal'][$cpzone]['httpslogin'])) ? 'https://' : 'http://'; + $logouturl = "{$protocol}{$ourhostname}/"; + + if (isset($attributes['reply_message'])) { + $message = $attributes['reply_message']; + } else { + $message = 0; + } + + include("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html"); + + } else { + portal_reply_page($my_redirurl, "redir", "Just redirect the user."); + } + + return $sessionid; +} + + +/* + * Used for when pass-through credits are enabled. + * Returns true when there was at least one free login to deduct for the MAC. + * Expired entries are removed as they are seen. + * Active entries are updated according to the configuration. + */ +function portal_consume_passthrough_credit($clientmac) { + global $config, $cpzone; + + if (!empty($config['captiveportal'][$cpzone]['freelogins_count']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_count'])) { + $freeloginscount = $config['captiveportal'][$cpzone]['freelogins_count']; + } else { + return false; + } + + if (!empty($config['captiveportal'][$cpzone]['freelogins_resettimeout']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_resettimeout'])) { + $resettimeout = $config['captiveportal'][$cpzone]['freelogins_resettimeout']; + } else { + return false; + } + + if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac) { + return false; + } + + $updatetimeouts = isset($config['captiveportal'][$cpzone]['freelogins_updatetimeouts']); + + /* + * Read database of used MACs. Lines are a comma-separated list + * of the time, MAC, then the count of pass-through credits remaining. + */ + $usedmacs = captiveportal_read_usedmacs_db(); + + $currenttime = time(); + $found = false; + foreach ($usedmacs as $key => $usedmac) { + $usedmac = explode(",", $usedmac); + + if ($usedmac[1] == $clientmac) { + if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) { + if ($usedmac[2] < 1) { + if ($updatetimeouts) { + $usedmac[0] = $currenttime; + unset($usedmacs[$key]); + $usedmacs[] = implode(",", $usedmac); + captiveportal_write_usedmacs_db($usedmacs); + } + + return false; + } else { + $usedmac[2] -= 1; + $usedmacs[$key] = implode(",", $usedmac); + } + + $found = true; + } else { + unset($usedmacs[$key]); + } + + break; + } else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime) { + unset($usedmacs[$key]); + } + } + + if (!$found) { + $usedmac = array($currenttime, $clientmac, $freeloginscount - 1); + $usedmacs[] = implode(",", $usedmac); + } + + captiveportal_write_usedmacs_db($usedmacs); + return true; +} + +function captiveportal_read_usedmacs_db() { + global $g, $cpzone; + + $cpumaclck = lock("captiveusedmacs{$cpzone}"); + if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db")) { + $usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if (!$usedmacs) { + $usedmacs = array(); + } + } else { + $usedmacs = array(); + } + + unlock($cpumaclck); + return $usedmacs; +} + +function captiveportal_write_usedmacs_db($usedmacs) { + global $g, $cpzone; + + $cpumaclck = lock("captiveusedmacs{$cpzone}", LOCK_EX); + @file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", implode("\n", $usedmacs)); + unlock($cpumaclck); +} + +function captiveportal_blocked_mac($mac) { + global $config, $g, $cpzone; + + if (empty($mac) || !is_macaddr($mac)) { + return false; + } + + if (!is_array($config['captiveportal'][$cpzone]['passthrumac'])) { + return false; + } + + foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $passthrumac) { + if (($passthrumac['action'] == 'block') && + ($passthrumac['mac'] == strtolower($mac))) { + return true; + } + } + + return false; + +} + +function captiveportal_send_server_accounting($off = false) { + global $cpzone, $config; + + if (!isset($config['captiveportal'][$cpzone]['radacct_enable'])) { + return; + } + if ($off) { + $racct = new Auth_RADIUS_Acct_Off; + } else { + $racct = new Auth_RADIUS_Acct_On; + } + $radiusservers = captiveportal_get_radius_servers(); + if (empty($radiusservers)) { + return; + } + foreach ($radiusservers['first'] as $radsrv) { + // Add a new server to our instance + $racct->addServer($radsrv['ipaddr'], $radsrv['acctport'], $radsrv['key']); + } + if (PEAR::isError($racct->start())) { + $retvalue['acct_val'] = 1; + $retvalue['error'] = $racct->getMessage(); + + // If we encounter an error immediately stop this function and go back + $racct->close(); + return $retvalue; + } + // Send request + $result = $racct->send(); + // Evaluation of the response + // 5 -> Accounting-Response + // See RFC2866 for this. + if (PEAR::isError($result)) { + $retvalue['acct_val'] = 1; + $retvalue['error'] = $result->getMessage(); + } else if ($result === true) { + $retvalue['acct_val'] = 5 ; + } else { + $retvalue['acct_val'] = 1 ; + } + + $racct->close(); + return $retvalue; +} + +function captiveportal_isip_logged($clientip) { + global $g, $cpzone; + + /* read in client database */ + $query = "WHERE ip = '{$clientip}'"; + $cpdb = captiveportal_read_db($query); + foreach ($cpdb as $cpentry) { + return $cpentry; + } +} +?> diff --git a/src/etc/inc/certs.inc b/src/etc/inc/certs.inc new file mode 100644 index 0000000..9c99952 --- /dev/null +++ b/src/etc/inc/certs.inc @@ -0,0 +1,867 @@ +<?php +/* $Id$ */ +/* + certs.inc + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2010 Jim Pingle <jimp@pfsense.org> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_MODULE: certificate_manager +*/ + +define("OPEN_SSL_CONF_PATH", "/etc/ssl/openssl.cnf"); + +require_once("functions.inc"); + +global $openssl_digest_algs; +$openssl_digest_algs = array("sha1", "sha224", "sha256", "sha384", "sha512"); + +global $openssl_crl_status; +$openssl_crl_status = array( + OCSP_REVOKED_STATUS_NOSTATUS => "No Status (default)", + OCSP_REVOKED_STATUS_UNSPECIFIED => "Unspecified", + OCSP_REVOKED_STATUS_KEYCOMPROMISE => "Key Compromise", + OCSP_REVOKED_STATUS_CACOMPROMISE => "CA Compromise", + OCSP_REVOKED_STATUS_AFFILIATIONCHANGED => "Affiliation Changed", + OCSP_REVOKED_STATUS_SUPERSEDED => "Superseded", + OCSP_REVOKED_STATUS_CESSATIONOFOPERATION => "Cessation of Operation", + OCSP_REVOKED_STATUS_CERTIFICATEHOLD => "Certificate Hold" +); + +function & lookup_ca($refid) { + global $config; + + if (is_array($config['ca'])) { + foreach ($config['ca'] as & $ca) { + if ($ca['refid'] == $refid) { + return $ca; + } + } + } + + return false; +} + +function & lookup_ca_by_subject($subject) { + global $config; + + if (is_array($config['ca'])) { + foreach ($config['ca'] as & $ca) { + $ca_subject = cert_get_subject($ca['crt']); + if ($ca_subject == $subject) { + return $ca; + } + } + } + + return false; +} + +function & lookup_cert($refid) { + global $config; + + if (is_array($config['cert'])) { + foreach ($config['cert'] as & $cert) { + if ($cert['refid'] == $refid) { + return $cert; + } + } + } + + return false; +} + +function & lookup_cert_by_name($name) { + global $config; + if (is_array($config['cert'])) { + foreach ($config['cert'] as & $cert) { + if ($cert['descr'] == $name) { + return $cert; + } + } + } +} + +function & lookup_crl($refid) { + global $config; + + if (is_array($config['crl'])) { + foreach ($config['crl'] as & $crl) { + if ($crl['refid'] == $refid) { + return $crl; + } + } + } + + return false; +} + +function ca_chain_array(& $cert) { + if ($cert['caref']) { + $chain = array(); + $crt = lookup_ca($cert['caref']); + $chain[] = $crt; + while ($crt) { + $caref = $crt['caref']; + if ($caref) { + $crt = lookup_ca($caref); + } else { + $crt = false; + } + if ($crt) { + $chain[] = $crt; + } + } + return $chain; + } + return false; +} + +function ca_chain(& $cert) { + if ($cert['caref']) { + $ca = ""; + $cas = ca_chain_array($cert); + if (is_array($cas)) { + foreach ($cas as & $ca_cert) { + $ca .= base64_decode($ca_cert['crt']); + $ca .= "\n"; + } + } + return $ca; + } + return ""; +} + +function ca_import(& $ca, $str, $key = "", $serial = 0) { + global $config; + + $ca['crt'] = base64_encode($str); + if (!empty($key)) { + $ca['prv'] = base64_encode($key); + } + if (!empty($serial)) { + $ca['serial'] = $serial; + } + $subject = cert_get_subject($str, false); + $issuer = cert_get_issuer($str, false); + + // Find my issuer unless self-signed + if ($issuer <> $subject) { + $issuer_crt =& lookup_ca_by_subject($issuer); + if ($issuer_crt) { + $ca['caref'] = $issuer_crt['refid']; + } + } + + /* Correct if child certificate was loaded first */ + if (is_array($config['ca'])) { + foreach ($config['ca'] as & $oca) { + $issuer = cert_get_issuer($oca['crt']); + if ($ca['refid'] <> $oca['refid'] && $issuer == $subject) { + $oca['caref'] = $ca['refid']; + } + } + } + if (is_array($config['cert'])) { + foreach ($config['cert'] as & $cert) { + $issuer = cert_get_issuer($cert['crt']); + if ($issuer == $subject) { + $cert['caref'] = $ca['refid']; + } + } + } + return true; +} + +function ca_create(& $ca, $keylen, $lifetime, $dn, $digest_alg = "sha256") { + + $args = array( + "x509_extensions" => "v3_ca", + "digest_alg" => $digest_alg, + "private_key_bits" => (int)$keylen, + "private_key_type" => OPENSSL_KEYTYPE_RSA, + "encrypt_key" => false); + + // generate a new key pair + $res_key = openssl_pkey_new($args); + if (!$res_key) { + return false; + } + + // generate a certificate signing request + $res_csr = openssl_csr_new($dn, $res_key, $args); + if (!$res_csr) { + return false; + } + + // self sign the certificate + $res_crt = openssl_csr_sign($res_csr, null, $res_key, $lifetime, $args); + if (!$res_crt) { + return false; + } + + // export our certificate data + if (!openssl_pkey_export($res_key, $str_key) || + !openssl_x509_export($res_crt, $str_crt)) { + return false; + } + + // return our ca information + $ca['crt'] = base64_encode($str_crt); + $ca['prv'] = base64_encode($str_key); + $ca['serial'] = 0; + + return true; +} + +function ca_inter_create(& $ca, $keylen, $lifetime, $dn, $caref, $digest_alg = "sha256") { + // Create Intermediate Certificate Authority + $signing_ca =& lookup_ca($caref); + if (!$signing_ca) { + return false; + } + + $signing_ca_res_crt = openssl_x509_read(base64_decode($signing_ca['crt'])); + $signing_ca_res_key = openssl_pkey_get_private(array(0 => base64_decode($signing_ca['prv']) , 1 => "")); + if (!$signing_ca_res_crt || !$signing_ca_res_key) { + return false; + } + $signing_ca_serial = ++$signing_ca['serial']; + + $args = array( + "x509_extensions" => "v3_ca", + "digest_alg" => $digest_alg, + "private_key_bits" => (int)$keylen, + "private_key_type" => OPENSSL_KEYTYPE_RSA, + "encrypt_key" => false); + + // generate a new key pair + $res_key = openssl_pkey_new($args); + if (!$res_key) { + return false; + } + + // generate a certificate signing request + $res_csr = openssl_csr_new($dn, $res_key, $args); + if (!$res_csr) { + return false; + } + + // Sign the certificate + $res_crt = openssl_csr_sign($res_csr, $signing_ca_res_crt, $signing_ca_res_key, $lifetime, $args, $signing_ca_serial); + if (!$res_crt) { + return false; + } + + // export our certificate data + if (!openssl_pkey_export($res_key, $str_key) || + !openssl_x509_export($res_crt, $str_crt)) { + return false; + } + + // return our ca information + $ca['crt'] = base64_encode($str_crt); + $ca['prv'] = base64_encode($str_key); + $ca['serial'] = 0; + + return true; +} + +function cert_import(& $cert, $crt_str, $key_str) { + + $cert['crt'] = base64_encode($crt_str); + $cert['prv'] = base64_encode($key_str); + + $subject = cert_get_subject($crt_str, false); + $issuer = cert_get_issuer($crt_str, false); + + // Find my issuer unless self-signed + if ($issuer <> $subject) { + $issuer_crt =& lookup_ca_by_subject($issuer); + if ($issuer_crt) { + $cert['caref'] = $issuer_crt['refid']; + } + } + return true; +} + +function cert_create(& $cert, $caref, $keylen, $lifetime, $dn, $type = "user", $digest_alg = "sha256") { + + $cert['type'] = $type; + + if ($type != "self-signed") { + $cert['caref'] = $caref; + $ca =& lookup_ca($caref); + if (!$ca) { + return false; + } + + $ca_str_crt = base64_decode($ca['crt']); + $ca_str_key = base64_decode($ca['prv']); + $ca_res_crt = openssl_x509_read($ca_str_crt); + $ca_res_key = openssl_pkey_get_private(array(0 => $ca_str_key, 1 => "")); + if (!$ca_res_key) { + return false; + } + $ca_serial = ++$ca['serial']; + } + + switch ($type) { + case "ca": + $cert_type = "v3_ca"; + break; + case "server": + case "self-signed": + $cert_type = "server"; + break; + default: + $cert_type = "usr_cert"; + break; + } + + // in case of using Subject Alternative Names use other sections (with postfix '_san') + // pass subjectAltName over environment variable 'SAN' + if ($dn['subjectAltName']) { + putenv("SAN={$dn['subjectAltName']}"); // subjectAltName can be set _only_ via configuration file + $cert_type .= '_san'; + unset($dn['subjectAltName']); + } + + $args = array( + "x509_extensions" => $cert_type, + "digest_alg" => $digest_alg, + "private_key_bits" => (int)$keylen, + "private_key_type" => OPENSSL_KEYTYPE_RSA, + "encrypt_key" => false); + + // generate a new key pair + $res_key = openssl_pkey_new($args); + if (!$res_key) { + return false; + } + + // If this is a self-signed cert, blank out the CA and sign with the cert's key + if ($type == "self-signed") { + $ca = null; + $ca_res_crt = null; + $ca_res_key = $res_key; + $ca_serial = 0; + $cert['type'] = "server"; + } + + // generate a certificate signing request + $res_csr = openssl_csr_new($dn, $res_key, $args); + if (!$res_csr) { + return false; + } + + // sign the certificate using an internal CA + $res_crt = openssl_csr_sign($res_csr, $ca_res_crt, $ca_res_key, $lifetime, + $args, $ca_serial); + if (!$res_crt) { + return false; + } + + // export our certificate data + if (!openssl_pkey_export($res_key, $str_key) || + !openssl_x509_export($res_crt, $str_crt)) { + return false; + } + + // return our certificate information + $cert['crt'] = base64_encode($str_crt); + $cert['prv'] = base64_encode($str_key); + + return true; +} + +function csr_generate(& $cert, $keylen, $dn, $digest_alg = "sha256") { + + $args = array( + "x509_extensions" => "v3_req", + "digest_alg" => $digest_alg, + "private_key_bits" => (int)$keylen, + "private_key_type" => OPENSSL_KEYTYPE_RSA, + "encrypt_key" => false); + + // generate a new key pair + $res_key = openssl_pkey_new($args); + if (!$res_key) { + return false; + } + + // generate a certificate signing request + $res_csr = openssl_csr_new($dn, $res_key, $args); + if (!$res_csr) { + return false; + } + + // export our request data + if (!openssl_pkey_export($res_key, $str_key) || + !openssl_csr_export($res_csr, $str_csr)) { + return false; + } + + // return our request information + $cert['csr'] = base64_encode($str_csr); + $cert['prv'] = base64_encode($str_key); + + return true; +} + +function csr_complete(& $cert, $str_crt) { + + // return our request information + $cert['crt'] = base64_encode($str_crt); + unset($cert['csr']); + + return true; +} + +function csr_get_subject($str_crt, $decode = true) { + + if ($decode) { + $str_crt = base64_decode($str_crt); + } + + $components = openssl_csr_get_subject($str_crt); + + if (empty($components) || !is_array($components)) { + return "unknown"; + } + + ksort($components); + foreach ($components as $a => $v) { + if (!strlen($subject)) { + $subject = "{$a}={$v}"; + } else { + $subject = "{$a}={$v}, {$subject}"; + } + } + + return $subject; +} + +function cert_get_subject($str_crt, $decode = true) { + + if ($decode) { + $str_crt = base64_decode($str_crt); + } + + $inf_crt = openssl_x509_parse($str_crt); + $components = $inf_crt['subject']; + + if (empty($components) || !is_array($components)) { + return "unknown"; + } + + ksort($components); + foreach ($components as $a => $v) { + if (is_array($v)) { + ksort($v); + foreach ($v as $w) { + $asubject = "{$a}={$w}"; + $subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject; + } + } else { + $asubject = "{$a}={$v}"; + $subject = (strlen($subject)) ? "{$asubject}, {$subject}" : $asubject; + } + } + + return $subject; +} + +function cert_get_subject_array($crt) { + $str_crt = base64_decode($crt); + $inf_crt = openssl_x509_parse($str_crt); + $components = $inf_crt['subject']; + + if (!is_array($components)) { + return; + } + + $subject_array = array(); + + foreach ($components as $a => $v) { + $subject_array[] = array('a' => $a, 'v' => $v); + } + + return $subject_array; +} + +function cert_get_subject_hash($crt) { + $str_crt = base64_decode($crt); + $inf_crt = openssl_x509_parse($str_crt); + return $inf_crt['subject']; +} + +function cert_get_issuer($str_crt, $decode = true) { + + if ($decode) { + $str_crt = base64_decode($str_crt); + } + + $inf_crt = openssl_x509_parse($str_crt); + $components = $inf_crt['issuer']; + + if (empty($components) || !is_array($components)) { + return "unknown"; + } + + ksort($components); + foreach ($components as $a => $v) { + if (is_array($v)) { + ksort($v); + foreach ($v as $w) { + $aissuer = "{$a}={$w}"; + $issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer; + } + } else { + $aissuer = "{$a}={$v}"; + $issuer = (strlen($issuer)) ? "{$aissuer}, {$issuer}" : $aissuer; + } + } + + return $issuer; +} + +/* this function works on x509 (crt), rsa key (prv), and req(csr) */ +function cert_get_modulus($str_crt, $decode = true, $type = "crt") { + if ($decode) { + $str_crt = base64_decode($str_crt); + } + + $modulus = ""; + if (in_array($type, array("crt", "prv", "csr"))) { + $type = str_replace(array("crt", "prv", "csr"), array("x509", "rsa", "req"), $type); + $modulus = exec("echo \"{$str_crt}\" | openssl {$type} -noout -modulus"); + } + return $modulus; +} +function csr_get_modulus($str_crt, $decode = true) { + return cert_get_modulus($str_crt, $decode, "csr"); +} + +function cert_get_purpose($str_crt, $decode = true) { + if ($decode) { + $str_crt = base64_decode($str_crt); + } + $crt_details = openssl_x509_parse($str_crt); + $purpose = array(); + $purpose['ca'] = (stristr($crt_details['extensions']['basicConstraints'], 'CA:TRUE') === false) ? 'No': 'Yes'; + $purpose['server'] = ($crt_details['extensions']['nsCertType'] == "SSL Server") ? 'Yes': 'No'; + return $purpose; +} + +function cert_get_dates($str_crt, $decode = true) { + if ($decode) { + $str_crt = base64_decode($str_crt); + } + $crt_details = openssl_x509_parse($str_crt); + if ($crt_details['validFrom_time_t'] > 0) { + $start = date('r', $crt_details['validFrom_time_t']); + } + if ($crt_details['validTo_time_t'] > 0) { + $end = date('r', $crt_details['validTo_time_t']); + } + return array($start, $end); +} + +function cert_get_serial($str_crt, $decode = true) { + if ($decode) { + $str_crt = base64_decode($str_crt); + } + $crt_details = openssl_x509_parse($str_crt); + if (isset($crt_details['serialNumber']) && !empty($crt_details['serialNumber'])) { + return $crt_details['serialNumber']; + } else { + return NULL; + } +} + +function prv_get_modulus($str_crt, $decode = true) { + return cert_get_modulus($str_crt, $decode, "prv"); +} + +function is_user_cert($certref) { + global $config; + if (!is_array($config['system']['user'])) { + return; + } + foreach ($config['system']['user'] as $user) { + if (!is_array($user['cert'])) { + continue; + } + foreach ($user['cert'] as $cert) { + if ($certref == $cert) { + return true; + } + } + } + return false; +} + +function is_openvpn_server_cert($certref) { + global $config; + if (!is_array($config['openvpn']['openvpn-server'])) { + return; + } + foreach ($config['openvpn']['openvpn-server'] as $ovpns) { + if ($ovpns['certref'] == $certref) { + return true; + } + } + return false; +} + +function is_openvpn_client_cert($certref) { + global $config; + if (!is_array($config['openvpn']['openvpn-client'])) { + return; + } + foreach ($config['openvpn']['openvpn-client'] as $ovpnc) { + if ($ovpnc['certref'] == $certref) { + return true; + } + } + return false; +} + +function is_ipsec_cert($certref) { + global $config; + if (!is_array($config['ipsec']['phase1'])) { + return; + } + foreach ($config['ipsec']['phase1'] as $ipsec) { + if ($ipsec['certref'] == $certref) { + return true; + } + } + return false; +} + +function is_webgui_cert($certref) { + global $config; + if (($config['system']['webgui']['ssl-certref'] == $certref) && + ($config['system']['webgui']['protocol'] != "http")) { + return true; + } +} + +function is_captiveportal_cert($certref) { + global $config; + if (!is_array($config['captiveportal'])) { + return; + } + foreach ($config['captiveportal'] as $portal) { + if (isset($portal['enable']) && isset($portal['httpslogin']) && ($portal['certref'] == $certref)) { + return true; + } + } + return false; +} + +function cert_in_use($certref) { + return (is_webgui_cert($certref) || + is_user_cert($certref) || + is_openvpn_server_cert($certref) || + is_openvpn_client_cert($certref) || + is_ipsec_cert($certref) || + is_captiveportal_cert($certref)); +} + +function crl_create(& $crl, $caref, $name, $serial = 0, $lifetime = 9999) { + global $config; + $ca =& lookup_ca($caref); + if (!$ca) { + return false; + } + $crl['descr'] = $name; + $crl['caref'] = $caref; + $crl['serial'] = $serial; + $crl['lifetime'] = $lifetime; + $crl['cert'] = array(); + $crl_res = crl_update($crl); + $config['crl'][] = $crl; + return $crl_res; +} + +function crl_update(& $crl) { + global $config; + $ca =& lookup_ca($crl['caref']); + if (!$ca) { + return false; + } + // If we have text but no certs, it was imported and cannot be updated. + if (($crl["method"] != "internal") && (!empty($crl['text']) && empty($crl['cert']))) { + return false; + } + $crl['serial']++; + $ca_str_crt = base64_decode($ca['crt']); + $ca_str_key = base64_decode($ca['prv']); + $crl_res = openssl_crl_new($ca_str_crt, $crl['serial'], $crl['lifetime']); + if (is_array($crl['cert']) && (count($crl['cert']) > 0)) { + foreach ($crl['cert'] as $cert) { + openssl_crl_revoke_cert($crl_res, base64_decode($cert["crt"]), $cert["revoke_time"], $cert["reason"]); + } + } + openssl_crl_export($crl_res, $crl_text, $ca_str_key); + $crl['text'] = base64_encode($crl_text); + return $crl_res; +} + +function cert_revoke($cert, & $crl, $reason = OCSP_REVOKED_STATUS_UNSPECIFIED) { + global $config; + if (is_cert_revoked($cert, $crl['refid'])) { + return true; + } + // If we have text but no certs, it was imported and cannot be updated. + if (!is_crl_internal($crl)) { + return false; + } + $cert["reason"] = $reason; + $cert["revoke_time"] = time(); + $crl["cert"][] = $cert; + crl_update($crl); + return true; +} + +function cert_unrevoke($cert, & $crl) { + global $config; + if (!is_crl_internal($crl)) { + return false; + } + foreach ($crl['cert'] as $id => $rcert) { + if (($rcert['refid'] == $cert['refid']) || ($rcert['descr'] == $cert['descr'])) { + unset($crl['cert'][$id]); + if (count($crl['cert']) == 0) { + // Protect against accidentally switching the type to imported, for older CRLs + if (!isset($crl['method'])) { + $crl['method'] = "internal"; + } + crl_update($crl); + } else { + crl_update($crl); + } + return true; + } + } + return false; +} + +/* Compare two certificates to see if they match. */ +function cert_compare($cert1, $cert2) { + /* Ensure two certs are identical by first checking that their issuers match, then + subjects, then serial numbers, and finally the moduli. Anything less strict + could accidentally count two similar, but different, certificates as + being identical. */ + $c1 = base64_decode($cert1['crt']); + $c2 = base64_decode($cert2['crt']); + if ((cert_get_issuer($c1, false) == cert_get_issuer($c2, false)) && + (cert_get_subject($c1, false) == cert_get_subject($c2, false)) && + (cert_get_serial($c1, false) == cert_get_serial($c2, false)) && + (cert_get_modulus($c1, false) == cert_get_modulus($c2, false))) { + return true; + } + return false; +} + +function is_cert_revoked($cert, $crlref = "") { + global $config; + if (!is_array($config['crl'])) { + return false; + } + + if (!empty($crlref)) { + $crl = lookup_crl($crlref); + if (!is_array($crl['cert'])) { + return false; + } + foreach ($crl['cert'] as $rcert) { + if (cert_compare($rcert, $cert)) { + return true; + } + } + } else { + foreach ($config['crl'] as $crl) { + if (!is_array($crl['cert'])) { + continue; + } + foreach ($crl['cert'] as $rcert) { + if (cert_compare($rcert, $cert)) { + return true; + } + } + } + } + return false; +} + +function is_openvpn_server_crl($crlref) { + global $config; + if (!is_array($config['openvpn']['openvpn-server'])) { + return; + } + foreach ($config['openvpn']['openvpn-server'] as $ovpns) { + if (!empty($ovpns['crlref']) && ($ovpns['crlref'] == $crlref)) { + return true; + } + } + return false; +} + +// Keep this general to allow for future expansion. See cert_in_use() above. +function crl_in_use($crlref) { + return (is_openvpn_server_crl($crlref)); +} + +function is_crl_internal($crl) { + return (!(!empty($crl['text']) && empty($crl['cert'])) || ($crl["method"] == "internal")); +} + +function cert_get_cn($crt, $isref = false) { + /* If this is a certref, not an actual cert, look up the cert first */ + if ($isref) { + $cert = lookup_cert($crt); + /* If it's not a valid cert, bail. */ + if (!(is_array($cert) && !empty($cert['crt']))) { + return ""; + } + $cert = $cert['crt']; + } else { + $cert = $crt; + } + $sub = cert_get_subject_array($cert); + if (is_array($sub)) { + foreach ($sub as $s) { + if (strtoupper($s['a']) == "CN") { + return $s['v']; + } + } + } + return ""; +} + +?> diff --git a/src/etc/inc/config.console.inc b/src/etc/inc/config.console.inc new file mode 100644 index 0000000..df3fa6f --- /dev/null +++ b/src/etc/inc/config.console.inc @@ -0,0 +1,560 @@ +<?php +/****h* pfSense/config + * NAME + * config.inc - Functions to manipulate config.xml + * DESCRIPTION + * This include contains various config.xml specific functions. + * HISTORY + * $Id$ + ****** + + config.console.inc + Copyright (C) 2004-2010 Scott Ullrich + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + + pfSense_BUILDER_BINARIES: /sbin/mount /sbin/umount /sbin/halt /sbin/fsck + pfSense_MODULE: config +*/ + +function set_networking_interfaces_ports() { + global $noreboot; + global $config; + global $g; + global $fp; + + $fp = fopen('php://stdin', 'r'); + + $memory = get_memory(); + $physmem = $memory[0]; + $realmem = $memory[1]; + + if ($physmem < $g['minimum_ram_warning']) { + echo "\n\n\n"; + echo gettext("DANGER! WARNING! ACHTUNG!") . "\n\n"; + printf(gettext("%s requires *AT LEAST* %s RAM to function correctly.%s"), $g['product_name'], $g['minimum_ram_warning_text'], "\n"); + printf(gettext("Only (%s) MB RAM has been detected, with (%s) available to %s.%s"), $realmem, $physmem, $g['product_name'], "\n"); + echo "\n" . gettext("Press ENTER to continue.") . " "; + fgets($fp); + echo "\n"; + } + + $iflist = get_interface_list(); + + /* Function flow is based on $key and $auto_assign or the lack thereof */ + $key = null; + + /* Only present auto interface option if running from LiveCD and interface mismatch*/ + if ((preg_match("/cdrom/", $g['platform'])) && is_interface_mismatch()) { + $auto_assign = false; + } + + echo <<<EOD + +Valid interfaces are: + + +EOD; + + if (!is_array($iflist)) { + echo gettext("No interfaces found!") . "\n"; + $iflist = array(); + } else { + foreach ($iflist as $iface => $ifa) { + echo sprintf("% -7s%s %s %s\n", $iface, $ifa['mac'], + $ifa['up'] ? " (up)" : "(down)", $ifa['dmesg']); + } + } + + if ($auto_assign) { + echo <<<EOD + + !!! LiveCD Detected: Auto Interface Option !!!! +BEGIN MANUAL CONFIGURATION OR WE WILL PROCEED WITH AUTO CONFIGURATION. + +EOD; + } + + echo <<<EOD + +Do you want to set up VLANs first? + +If you are not going to use VLANs, or only for optional interfaces, you should +say no here and use the webConfigurator to configure VLANs later, if required. + +Do you want to set up VLANs now [y|n]? +EOD; + + if ($auto_assign) { + $key = timeout(); + } else { + $key = chop(fgets($fp)); + } + + if (!isset($key) and $auto_assign) { // Auto Assign Interfaces + do { + echo <<<EOD + + !!! Auto Assigning Interfaces !!! + +For installation purposes, you must plug in at least one NIC +for the LAN connection. If you plug in a second NIC it will be +assigned to WAN. Otherwise, we'll temporarily assign WAN to the +next available NIC found regardless of activity. You should +assign and configure the WAN interface according to your requirements + +If you haven't plugged in any network cables yet, +now is the time to do so. +We'll keep trying until you do. + +Searching for active interfaces... + +EOD; + unset($wanif, $lanif); + + $media_iflist = $plugged_in = array(); + $media_iflist = get_interface_list("media"); + foreach ($media_iflist as $iface => $ifa) { + if ($ifa['up']) { + $plugged_in[] = $iface; + } + } + + $lanif = array_shift($plugged_in); + $wanif = array_shift($plugged_in); + + if (isset($lanif) && !isset($wanif)) { + foreach ($iflist as $iface => $ifa) { + if ($iface != $lanif) { + $wanif = $iface; + break; + } + } + } + + echo <<<EOD + +Assigned WAN to : $wanif +Assigned LAN to : $lanif + +If you don't like this assignment, +press any key to go back to manual configuration. + +EOD; + $key = timeout(20); + if (isset($key)) { + return; + } + } while (!isset($wanif)); + + $config['system']['enablesshd'] = 'enabled'; + $key = 'y'; + + } else { + //Manually assign interfaces + if (in_array($key, array('y', 'Y'))) { + vlan_setup(); + } + + if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) { + + echo "\n\n" . gettext("VLAN interfaces:") . "\n\n"; + foreach ($config['vlans']['vlan'] as $vlan) { + + echo sprintf("% -16s%s\n", "{$vlan['if']}_vlan{$vlan['tag']}", + "VLAN tag {$vlan['tag']}, parent interface {$vlan['if']}"); + + $iflist[$vlan['if'] . '_vlan' . $vlan['tag']] = array(); + } + } + + echo <<<EOD + +If you do not know the names of your interfaces, you may choose to use +auto-detection. In that case, disconnect all interfaces now before +hitting 'a' to initiate auto detection. + +EOD; + + do { + echo "\n" . gettext("Enter the WAN interface name or 'a' for auto-detection:") . " "; + $wanif = chop(fgets($fp)); + if ($wanif === "") { + return; + } + if ($wanif === "a") { + $wanif = autodetect_interface("WAN", $fp); + } else if (!array_key_exists($wanif, $iflist)) { + printf(gettext("%sInvalid interface name '%s'%s"), "\n", $wanif, "\n"); + unset($wanif); + continue; + } + } while (!$wanif); + + do { + printf(gettext("%sEnter the LAN interface name or 'a' for auto-detection %s" . + "NOTE: this enables full Firewalling/NAT mode.%s" . + "(or nothing if finished):%s"), "\n", "\n", "\n", " "); + + $lanif = chop(fgets($fp)); + + if ($lanif == "exit") { + exit; + } + + if ($lanif == "") { + /* It is OK to have just a WAN, without a LAN so break if the user does not want LAN. */ + break; + } + + if ($lanif === "a") { + $lanif = autodetect_interface("LAN", $fp); + } else if (!array_key_exists($lanif, $iflist)) { + printf(gettext("%sInvalid interface name '%s'%s"), "\n", $lanif, "\n"); + unset($lanif); + continue; + } + } while (!$lanif); + + /* optional interfaces */ + $i = 0; + $optif = array(); + + if ($lanif <> "") { + while (1) { + if ($optif[$i]) { + $i++; + } + $io = $i + 1; + + if ($config['interfaces']['opt' . $io]['descr']) { + printf(gettext("%sOptional interface %s description found: %s"), "\n", $io, $config['interfaces']['opt' . $io]['descr']); + } + + printf(gettext("%sEnter the Optional %s interface name or 'a' for auto-detection%s" . + "(or nothing if finished):%s"), "\n", $io, "\n", " "); + + $optif[$i] = chop(fgets($fp)); + + if ($optif[$i]) { + if ($optif[$i] === "a") { + $ad = autodetect_interface(gettext("Optional") . " " . $io, $fp); + if ($ad) { + $optif[$i] = $ad; + } else { + unset($optif[$i]); + } + } else if (!array_key_exists($optif[$i], $iflist)) { + printf(gettext("%sInvalid interface name '%s'%s"), "\n", $optif[$i], "\n"); + unset($optif[$i]); + continue; + } + } else { + unset($optif[$i]); + break; + } + } + } + + /* check for double assignments */ + $ifarr = array_merge(array($lanif, $wanif), $optif); + + for ($i = 0; $i < (count($ifarr)-1); $i++) { + for ($j = ($i+1); $j < count($ifarr); $j++) { + if ($ifarr[$i] == $ifarr[$j]) { + echo <<<EOD + +Error: you cannot assign the same interface name twice! + +EOD; + fclose($fp); + return; + } + } + } + + echo "\n" . gettext("The interfaces will be assigned as follows:") . "\n\n"; + + echo "WAN -> " . $wanif . "\n"; + if ($lanif != "") { + echo "LAN -> " . $lanif . "\n"; + } + for ($i = 0; $i < count($optif); $i++) { + echo "OPT" . ($i+1) . " -> " . $optif[$i] . "\n"; + } + + echo <<<EOD + +Do you want to proceed [y|n]? +EOD; + $key = chop(fgets($fp)); + } + + if (in_array($key, array('y', 'Y'))) { + if ($lanif) { + if (!is_array($config['interfaces']['lan'])) { + $config['interfaces']['lan'] = array(); + } + $config['interfaces']['lan']['if'] = $lanif; + $config['interfaces']['lan']['enable'] = true; + } elseif (!platform_booting() && !$auto_assign) { + +echo <<<EODD + +You have chosen to remove the LAN interface. + +Would you like to remove the LAN IP address and +unload the interface now? [y|n]? +EODD; + + if (strcasecmp(chop(fgets($fp)), "y") == 0) { + if (isset($config['interfaces']['lan']) && $config['interfaces']['lan']['if']) { + mwexec("/sbin/ifconfig " . $config['interfaces']['lan']['if'] . " delete"); + } + } + if (isset($config['interfaces']['lan'])) { + unset($config['interfaces']['lan']); + } + if (isset($config['dhcpd']['lan'])) { + unset($config['dhcpd']['lan']); + } + if (isset($config['interfaces']['lan']['if'])) { + unset($config['interfaces']['lan']['if']); + } + if (isset($config['interfaces']['wan']['blockpriv'])) { + unset($config['interfaces']['wan']['blockpriv']); + } + if (isset($config['shaper'])) { + unset($config['shaper']); + } + if (isset($config['ezshaper'])) { + unset($config['ezshaper']); + } + if (isset($config['nat'])) { + unset($config['nat']); + } + } else { + if (isset($config['interfaces']['lan']['if'])) { + mwexec("/sbin/ifconfig " . $config['interfaces']['lan']['if'] . " delete"); + } + if (isset($config['interfaces']['lan'])) { + unset($config['interfaces']['lan']); + } + if (isset($config['dhcpd']['lan'])) { + unset($config['dhcpd']['lan']); + } + if (isset($config['interfaces']['lan']['if'])) { + unset($config['interfaces']['lan']['if']); + } + if (isset($config['interfaces']['wan']['blockpriv'])) { + unset($config['interfaces']['wan']['blockpriv']); + } + if (isset($config['shaper'])) { + unset($config['shaper']); + } + if (isset($config['ezshaper'])) { + unset($config['ezshaper']); + } + if (isset($config['nat'])) { + unset($config['nat']); + } + } + if (preg_match($g['wireless_regex'], $lanif)) { + if (is_array($config['interfaces']['lan']) && + !is_array($config['interfaces']['lan']['wireless'])) { + $config['interfaces']['lan']['wireless'] = array(); + } + } else { + if (isset($config['interfaces']['lan'])) { + unset($config['interfaces']['lan']['wireless']); + } + } + + if (!is_array($config['interfaces']['wan'])) { + $config['interfaces']['wan'] = array(); + } + $config['interfaces']['wan']['if'] = $wanif; + $config['interfaces']['wan']['enable'] = true; + if (preg_match($g['wireless_regex'], $wanif)) { + if (is_array($config['interfaces']['wan']) && + !is_array($config['interfaces']['wan']['wireless'])) { + $config['interfaces']['wan']['wireless'] = array(); + } + } else { + if (isset($config['interfaces']['wan'])) { + unset($config['interfaces']['wan']['wireless']); + } + } + + for ($i = 0; $i < count($optif); $i++) { + if (!is_array($config['interfaces']['opt' . ($i+1)])) { + $config['interfaces']['opt' . ($i+1)] = array(); + } + + $config['interfaces']['opt' . ($i+1)]['if'] = $optif[$i]; + + /* wireless interface? */ + if (preg_match($g['wireless_regex'], $optif[$i])) { + if (!is_array($config['interfaces']['opt' . ($i+1)]['wireless'])) { + $config['interfaces']['opt' . ($i+1)]['wireless'] = array(); + } + } else { + unset($config['interfaces']['opt' . ($i+1)]['wireless']); + } + + if (empty($config['interfaces']['opt' . ($i+1)]['descr'])) { + $config['interfaces']['opt' . ($i+1)]['descr'] = "OPT" . ($i+1); + unset($config['interfaces']['opt' . ($i+1)]['enable']); + } + } + + /* remove all other (old) optional interfaces */ + for (; isset($config['interfaces']['opt' . ($i+1)]); $i++) { + unset($config['interfaces']['opt' . ($i+1)]); + } + + printf(gettext("%sWriting configuration..."), "\n"); + write_config("Console assignment of interfaces"); + printf(gettext("done.%s"), "\n"); + + fclose($fp); + + if (platform_booting()) { + return; + } + + echo gettext("One moment while we reload the settings..."); + echo gettext(" done!") . "\n"; + + touch("{$g['tmp_path']}/assign_complete"); + + } +} + +function autodetect_interface($ifname, $fp) { + $iflist_prev = get_interface_list("media"); + echo <<<EOD + +Connect the {$ifname} interface now and make sure that the link is up. +Then press ENTER to continue. + +EOD; + fgets($fp); + $iflist = get_interface_list("media"); + + foreach ($iflist_prev as $ifn => $ifa) { + if (!$ifa['up'] && $iflist[$ifn]['up']) { + printf(gettext("Detected link-up on interface %s.%s"), $ifn, "\n"); + return $ifn; + } + } + + printf(gettext("No link-up detected.%s"), "\n"); + + return null; +} + +function interfaces_setup() { + global $iflist, $config, $g, $fp; + + $iflist = get_interface_list(); +} + +function vlan_setup() { + global $iflist, $config, $g, $fp; + + $iflist = get_interface_list(); + + if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) { + + echo <<<EOD + +WARNING: all existing VLANs will be cleared if you proceed! + +Do you want to proceed [y|n]? +EOD; + + if (strcasecmp(chop(fgets($fp)), "y") != 0) { + return; + } + } + + $config['vlans']['vlan'] = array(); + echo "\n"; + + $vlanif = 0; + + while (1) { + $vlan = array(); + + echo "\n\n" . gettext("VLAN Capable interfaces:") . "\n\n"; + if (!is_array($iflist)) { + echo gettext("No interfaces found!") . "\n"; + } else { + $vlan_capable = 0; + foreach ($iflist as $iface => $ifa) { + if (is_jumbo_capable($iface)) { + echo sprintf("% -8s%s%s\n", $iface, $ifa['mac'], + $ifa['up'] ? " (up)" : ""); + $vlan_capable++; + } + } + } + + if ($vlan_capable == 0) { + echo gettext("No VLAN capable interfaces detected.") . "\n"; + return; + } + + echo "\n" . gettext("Enter the parent interface name for the new VLAN (or nothing if finished):") . " "; + $vlan['if'] = chop(fgets($fp)); + + if ($vlan['if']) { + if (!array_key_exists($vlan['if'], $iflist) or + !is_jumbo_capable($vlan['if'])) { + printf(gettext("%sInvalid interface name '%s'%s"), "\n", $vlan['if'], "\n"); + continue; + } + } else { + break; + } + + echo gettext("Enter the VLAN tag (1-4094):") . " "; + $vlan['tag'] = chop(fgets($fp)); + $vlan['vlanif'] = "{$vlan['if']}_vlan{$vlan['tag']}"; + if (!is_numericint($vlan['tag']) || ($vlan['tag'] < 1) || ($vlan['tag'] > 4094)) { + printf(gettext("%sInvalid VLAN tag '%s'%s"), "\n", $vlan['tag'], "\n"); + continue; + } + + $config['vlans']['vlan'][] = $vlan; + $vlanif++; + } +} + +?> diff --git a/src/etc/inc/config.gui.inc b/src/etc/inc/config.gui.inc new file mode 100644 index 0000000..56b5555 --- /dev/null +++ b/src/etc/inc/config.gui.inc @@ -0,0 +1,96 @@ +<?php +/****h* pfSense/config + * NAME + * config.gui.inc - Functions to manipulate config.xml + * DESCRIPTION + * This include contains various config.xml specific functions. + * HISTORY + * $Id$ + ****** + + config.gui.inc + Copyright (C) 2004-2010 Scott Ullrich + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + + pfSense_BUILDER_BINARIES: /sbin/mount /sbin/umount /sbin/halt /sbin/fsck + pfSense_MODULE: config +*/ + +require_once("globals.inc"); + +/* do not load this file twice. */ +if ($config_parsed == true) { + return; +} else { + $config_parsed = true; +} + +/* include globals from notices.inc /utility/XML parser files */ +require_once('config.lib.inc'); +require_once("notices.inc"); +require_once("util.inc"); +require_once("IPv6.inc"); +if (file_exists("/cf/conf/use_xmlreader")) { + require_once("xmlreader.inc"); +} else { + require_once("xmlparse.inc"); +} +require_once("crypt.inc"); + +/* read platform */ +if (file_exists("{$g['etc_path']}/platform")) { + $g['platform'] = chop(file_get_contents("{$g['etc_path']}/platform")); +} else { + $g['platform'] = "unknown"; +} + +/* if /debugging exists, lets set $debugging + so we can output more information */ +if (file_exists("/debugging")) { + $debugging = true; + $g['debug'] = true; +} + +$config = parse_config(); + +/* set timezone */ +$timezone = $config['system']['timezone']; +if (!$timezone) { + $timezone = "Etc/UTC"; +} +date_default_timezone_set("$timezone"); + +if ($config_parsed == true) { + /* process packager manager custom rules */ + if (is_dir("/usr/local/pkg/parse_config")) { + run_plugins("/usr/local/pkg/parse_config/"); + } +} + +?> diff --git a/src/etc/inc/config.inc b/src/etc/inc/config.inc new file mode 100644 index 0000000..4792ac3 --- /dev/null +++ b/src/etc/inc/config.inc @@ -0,0 +1,226 @@ +<?php +/****h* pfSense/config + * NAME + * config.inc - Functions to manipulate config.xml + * DESCRIPTION + * This include contains various config.xml specific functions. + * HISTORY + * $Id$ + ****** + + config.inc + Copyright (C) 2004-2010 Scott Ullrich + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + + pfSense_BUILDER_BINARIES: /sbin/mount /sbin/umount /sbin/halt /sbin/fsck + pfSense_MODULE: config +*/ + +if (!function_exists('platform_booting')) { + require_once('globals.inc'); +} + +/* do not load this file twice. */ +//if (in_array("/etc/inc/config.inc", get_included_files())) +// return; + +// Set the memory limit to 128M on i386. When someone has something like 500+ tunnels +// the parser needs quite a bit of ram. Do not remove this line unless you +// know what you are doing. If in doubt, check with dev@ _/FIRST/_! +if (!$ARCH) { + $ARCH = php_uname("m"); +} + +// Set memory limit to 256M on amd64. +if ($ARCH == "amd64") { + ini_set("memory_limit", "256M"); +} else { + ini_set("memory_limit", "128M"); +} + +/* include globals from notices.inc /utility/XML parser files */ +require_once("notices.inc"); +require_once("util.inc"); +require_once("IPv6.inc"); +require_once('config.lib.inc'); +if (file_exists("/cf/conf/use_xmlreader")) { + require_once("xmlreader.inc"); +} else { + require_once("xmlparse.inc"); +} +require_once("crypt.inc"); + +/* read platform */ +if (file_exists("{$g['etc_path']}/platform")) { + $g['platform'] = chop(file_get_contents("{$g['etc_path']}/platform")); +} else { + $g['platform'] = "unknown"; +} + +/* if /debugging exists, lets set $debugging + so we can output more information */ +if (file_exists("/debugging")) { + $debugging = true; + $g['debug'] = true; +} + +if (platform_booting(true)) { + echo "."; + if (file_exists("/cf/conf/config.xml")) { + $config_contents = file_get_contents("/cf/conf/config.xml"); + if (stristr($config_contents, "<m0n0wall>") == true) { + echo "."; + /* user has just upgraded from m0n0wall, replace root xml tags */ + log_error(gettext("Upgrading m0n0wall configuration to pfSense... ")); + $config_contents = str_replace("m0n0wall", "pfsense", $config_contents); + if (!config_validate("{$g['conf_path']}/config.xml")) { + log_error(gettext("ERROR! Could not convert m0n0wall -> pfsense in config.xml")); + } + conf_mount_rw(); + file_put_contents("/cf/conf/config.xml", $config_contents); + conf_mount_ro(); + } + unset($config_contents); + } + /* if our config file exists bail out, we're already set. */ + else if (!file_exists($g['cf_conf_path'] . "/config.xml")) { + echo "."; + /* find the device where config.xml resides and write out an fstab */ + unset($cfgdevice); + echo "."; + /* check if there's already an fstab (NFS booting?) */ + if (!file_exists("{$g['etc_path']}/fstab")) { + echo "."; + if (strstr($g['platform'], "cdrom")) { + /* config is on floppy disk for CD-ROM version */ + $cfgdevice = $cfgpartition = "fd0"; + $_gb = exec('/sbin/dmesg -a', $dmesg); + if (preg_match("/da0/", $dmesg) == true) { + $cfgdevice = $cfgpartition = "da0" ; + if (mwexec("/sbin/mount -r /dev/{$cfgdevice} /cf")) { + /* could not mount, fallback to floppy */ + $cfgdevice = $cfgpartition = "fd0"; + } + } + unset($dmesg); + $cfgfstype = "msdosfs"; + echo gettext("CDROM build") . "\n"; + echo " " . gettext("CFG:") . " {$cfgpartition}\n"; + echo " " . gettext("CFG:") . " {$cfgpartition}\n"; + echo " " . gettext("TYPE:") . " {$cfgfstype}\n"; + } else { + echo "."; + /* probe kernel known disks until we find one with config.xml */ + $disks = explode(" ", get_single_sysctl("kern.disks")); + foreach ($disks as $mountdisk) { + /* skip mfs mounted filesystems */ + if (strstr($mountdisk, "md")) { + continue; + } + if (mwexec("/sbin/mount -r /dev/{$mountdisk}a {$g['cf_path']}") == 0) { + if (file_exists("{$g['cf_conf_path']}/config.xml")) { + /* found it */ + $cfgdevice = $mountdisk; + $cfgpartition = $cfgdevice . "a"; + $cfgfstype = "ufs"; + printf(gettext("Found configuration on %s.%s"), $cfgdevice, "\n"); + } + + mwexec("/sbin/umount -f {$g['cf_path']}"); + + if ($cfgdevice) { + break; + } + } + if (mwexec("/sbin/mount -r /dev/{$mountdisk}d {$g['cf_path']}") == 0) { + if (platform_booting()) { + echo "."; + } + if (file_exists("{$g['cf_conf_path']}/config.xml")) { + /* found it */ + $cfgdevice = $mountdisk; + $cfgpartition = $cfgdevice . "d"; + $cfgfstype = "ufs"; + printf(gettext("Found configuration on %s.%s"), $cfgdevice, "\n"); + } + + mwexec("/sbin/umount -f {$g['cf_path']}"); + + if ($cfgdevice) { + break; + } + } + } + } + echo "."; + if (!$cfgdevice) { + $last_backup = discover_last_backup(); + if ($last_backup) { + log_error(gettext("No config.xml found, attempting last known config restore.")); + file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", ""); + restore_backup("/cf/conf/backup/{$last_backup}"); + } else { + log_error(gettext("No config.xml or config backups found, resetting to factory defaults.")); + restore_backup('/conf.default/config.xml'); + } + } + + /* write device name to a file for rc.firmware */ + file_put_contents("{$g['varetc_path']}/cfdevice", $cfgdevice . "\n"); + + /* write out an fstab */ + + $fstab = "/dev/{$cfgpartition} {$g['cf_path']} {$cfgfstype} ro,noatime 1 1\n"; + $fstab .= "proc /proc procfs rw 0 0\n"; + file_put_contents("{$g['etc_path']}/fstab", $fstab); + } + echo "."; + /* mount all filesystems */ + mwexec("/sbin/mount -a"); + } + echo "."; +} + +$config = parse_config(); + +/* set timezone */ +$timezone = $config['system']['timezone']; +if (!$timezone) { + $timezone = "Etc/UTC"; +} +date_default_timezone_set("$timezone"); + +if ($config_parsed == true) { + /* process packager manager custom rules */ + if (is_dir("/usr/local/pkg/parse_config")) { + run_plugins("/usr/local/pkg/parse_config/"); + } +} + +?> diff --git a/src/etc/inc/config.lib.inc b/src/etc/inc/config.lib.inc new file mode 100644 index 0000000..222d9d8 --- /dev/null +++ b/src/etc/inc/config.lib.inc @@ -0,0 +1,1020 @@ +<?php +/****h* pfSense/config + * NAME + * config.lib.inc - Functions to manipulate config.xml + * DESCRIPTION + * This include contains various config.xml specific functions. + * HISTORY + * $Id$ + ****** + + config.lib.inc + Ported from config.inc by Erik Kristensen + Copyright (C) 2004-2010 Scott Ullrich + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + + pfSense_BUILDER_BINARIES: /sbin/mount /sbin/umount /sbin/halt + pfSense_MODULE: config +*/ + +/****f* config/encrypted_configxml + * NAME + * encrypted_configxml - Checks to see if config.xml is encrypted and if so, prompts to unlock. + * INPUTS + * None + * RESULT + * $config - rewrites config.xml without encryption + ******/ +function encrypted_configxml() { + global $g, $config; + + if (!file_exists($g['conf_path'] . "/config.xml")) { + return; + } + + if (!platform_booting()) { + return; + } + + $configtxt = file_get_contents($g['conf_path'] . "/config.xml"); + if (tagfile_deformat($configtxt, $configtxt, "config.xml")) { + $fp = fopen('php://stdin', 'r'); + $data = ""; + echo "\n\n*** Encrypted config.xml detected ***\n"; + while ($data == "") { + echo "\nEnter the password to decrypt config.xml: "; + $decrypt_password = chop(fgets($fp)); + $data = decrypt_data($configtxt, $decrypt_password); + if (!strstr($data, "<pfsense>")) { + $data = ""; + } + if ($data) { + $fd = fopen($g['conf_path'] . "/config.xml.tmp", "w"); + fwrite($fd, $data); + fclose($fd); + exec("/bin/mv {$g['conf_path']}/config.xml.tmp {$g['conf_path']}/config.xml"); + echo "\n" . gettext("Config.xml unlocked.") . "\n"; + fclose($fp); + pfSense_fsync("{$g['conf_path']}/config.xml"); + } else { + echo "\n" . gettext("Invalid password entered. Please try again.") . "\n"; + } + } + } +} + +/****f* config/parse_config + * NAME + * parse_config - Read in config.cache or config.xml if needed and return $config array + * INPUTS + * $parse - boolean to force parse_config() to read config.xml and generate config.cache + * RESULT + * $config - array containing all configuration variables + ******/ +function parse_config($parse = false) { + global $g, $config_parsed, $config_extra; + + $lockkey = lock('config'); + $config_parsed = false; + + if (!file_exists("{$g['conf_path']}/config.xml") || filesize("{$g['conf_path']}/config.xml") == 0) { + $last_backup = discover_last_backup(); + if ($last_backup) { + log_error(gettext("No config.xml found, attempting last known config restore.")); + file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", ""); + restore_backup("{$g['conf_path']}/backup/{$last_backup}"); + } else { + unlock($lockkey); + die(gettext("Config.xml is corrupted and is 0 bytes. Could not restore a previous backup.")); + } + } + + if (platform_booting(true)) { + echo "."; + } + + // Check for encrypted config.xml + encrypted_configxml(); + + if (!$parse) { + if (file_exists($g['tmp_path'] . '/config.cache')) { + $config = unserialize(file_get_contents($g['tmp_path'] . '/config.cache')); + if (is_null($config)) { + $parse = true; + } + } else { + $parse = true; + } + } + if ($parse == true) { + if (!file_exists($g['conf_path'] . "/config.xml")) { + if (platform_booting(true)) { + echo "."; + } + log_error("No config.xml found, attempting last known config restore."); + file_notice("config.xml", "No config.xml found, attempting last known config restore.", "pfSenseConfigurator", ""); + $last_backup = discover_last_backup(); + if ($last_backup) { + restore_backup("/cf/conf/backup/{$last_backup}"); + } else { + log_error(gettext("Could not restore config.xml.")); + unlock($lockkey); + die(gettext("Config.xml is corrupted and is 0 bytes. Could not restore a previous backup.")); + } + } + $config = parse_xml_config($g['conf_path'] . '/config.xml', array($g['xml_rootobj'], 'pfsense')); + if ($config == -1) { + $last_backup = discover_last_backup(); + if ($last_backup) { + restore_backup("/cf/conf/backup/{$last_backup}"); + } else { + log_error(gettext("Could not restore config.xml.")); + unlock($lockkey); + die("Config.xml is corrupted and is 0 bytes. Could not restore a previous backup."); + } + } + generate_config_cache($config); + } + + if (platform_booting(true)) { + echo "."; + } + + $config_parsed = true; + unlock($lockkey); + + alias_make_table($config); + + return $config; +} + +/****f* config/generate_config_cache + * NAME + * generate_config_cache - Write serialized configuration to cache. + * INPUTS + * $config - array containing current firewall configuration + * RESULT + * boolean - true on completion + ******/ +function generate_config_cache($config) { + global $g, $config_extra; + + $configcache = fopen($g['tmp_path'] . '/config.cache', "w"); + fwrite($configcache, serialize($config)); + fclose($configcache); + pfSense_fsync("{$g['tmp_path']}/config.cache"); + + unset($configcache); + /* Used for config.extra.xml */ + if (file_exists($g['tmp_path'] . '/config.extra.cache') && $config_extra) { + $configcacheextra = fopen($g['tmp_path'] . '/config.extra.cache', "w"); + fwrite($configcacheextra, serialize($config_extra)); + fclose($configcacheextra); + pfSense_fsync("{$g['tmp_path']}/config.extra.cache"); + unset($configcacheextra); + } +} + +function discover_last_backup() { + $backups = glob('/cf/conf/backup/*.xml'); + $last_backup = ""; + $last_mtime = 0; + foreach ($backups as $backup) { + if (filemtime($backup) > $last_mtime) { + $last_mtime = filemtime($backup); + $last_backup = $backup; + } + } + + return basename($last_backup); +} + +function restore_backup($file) { + global $g; + + if (file_exists($file)) { + conf_mount_rw(); + unlink_if_exists("{$g['tmp_path']}/config.cache"); + copy("$file", "/cf/conf/config.xml"); + pfSense_fsync("/cf/conf/config.xml"); + pfSense_fsync($g['conf_path']); + disable_security_checks(); + log_error(sprintf(gettext('%1$s is restoring the configuration %2$s'), $g['product_name'], $file)); + file_notice("config.xml", sprintf(gettext('%1$s is restoring the configuration %2$s'), $g['product_name'], $file), "pfSenseConfigurator", ""); + conf_mount_ro(); + } +} + +/****f* config/parse_config_bootup + * NAME + * parse_config_bootup - Bootup-specific configuration checks. + * RESULT + * null + ******/ +function parse_config_bootup() { + global $config, $g; + + if (platform_booting()) { + echo "."; + } + + $lockkey = lock('config'); + if (!file_exists("{$g['conf_path']}/config.xml")) { + if (platform_booting()) { + if (strstr($g['platform'], "cdrom")) { + /* try copying the default config. to the floppy */ + echo gettext("Resetting factory defaults...") . "\n"; + reset_factory_defaults(true); + if (!file_exists("{$g['conf_path']}/config.xml")) { + echo gettext("No XML configuration file found - using factory defaults.\n" . + "Make sure that the configuration floppy disk with the conf/config.xml\n" . + "file is inserted. If it isn't, your configuration changes will be lost\n" . + "on reboot.\n"); + } + } else { + $last_backup = discover_last_backup(); + if ($last_backup) { + log_error("No config.xml found, attempting last known config restore."); + file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", ""); + restore_backup("/cf/conf/backup/{$last_backup}"); + } + if (!file_exists("{$g['conf_path']}/config.xml")) { + echo sprintf(gettext("XML configuration file not found. %s cannot continue booting."), $g['product_name']) . "\n"; + unlock($lockkey); + mwexec("/sbin/halt"); + exit; + } + log_error("Last known config found and restored. Please double check your configuration file for accuracy."); + file_notice("config.xml", gettext("Last known config found and restored. Please double check your configuration file for accuracy."), "pfSenseConfigurator", ""); + } + } else { + unlock($lockkey); + log_error(gettext("Could not find a usable configuration file! Exiting....")); + exit(0); + } + } + + if (filesize("{$g['conf_path']}/config.xml") == 0) { + $last_backup = discover_last_backup(); + if ($last_backup) { + log_error(gettext("No config.xml found, attempting last known config restore.")); + file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", ""); + restore_backup("{$g['conf_path']}/backup/{$last_backup}"); + } else { + unlock($lockkey); + die(gettext("Config.xml is corrupted and is 0 bytes. Could not restore a previous backup.")); + } + } + unlock($lockkey); + + $config = parse_config(true); + + if ((float)$config['version'] > (float)$g['latest_config']) { + echo <<<EOD + + +******************************************************************************* +* WARNING! * +* The current configuration has been created with a newer version of {$g['product_name']} * +* than this one! This can lead to serious misbehavior and even security * +* holes! You are urged to either upgrade to a newer version of {$g['product_name']} or * +* revert to the default configuration immediately! * +******************************************************************************* + + +EOD; + } + + /* make alias table (for faster lookups) */ + alias_make_table($config); +} + +/****f* config/conf_mount_rw + * NAME + * conf_mount_rw - Mount filesystems read/write. + * RESULT + * null + ******/ +/* mount flash card read/write */ +function conf_mount_rw() { + global $g, $config; + + /* do not mount on cdrom platform */ + if ($g['platform'] == "cdrom" or $g['platform'] == "pfSense") { + return; + } + + if ((refcount_reference(1000) > 1) && is_writable("/")) { + return; + } + + $status = mwexec("/sbin/mount -u -w -o sync,noatime {$g['cf_path']}"); + if ($status <> 0) { + if (platform_booting()) { + echo gettext("/cf Filesystem is dirty.") . "\n"; + } + $status = mwexec("/sbin/mount -u -w -o sync,noatime {$g['cf_path']}"); + } + + /* if the platform is soekris or wrap or pfSense, lets mount the + * compact flash cards root. + */ + $status = mwexec("/sbin/mount -u -w -o sync,noatime /"); + /* we could not mount this correctly. */ + if ($status <> 0) { + log_error(gettext("/ File system is dirty.")); + $status = mwexec("/sbin/mount -u -w -o sync,noatime /"); + } + + mark_subsystem_dirty('mount'); +} + +/****f* config/conf_mount_ro + * NAME + * conf_mount_ro - Mount filesystems readonly. + * RESULT + * null + ******/ +function conf_mount_ro() { + global $g, $config; + + /* Do not trust $g['platform'] since this can be clobbered during factory reset. */ + $platform = trim(file_get_contents("/etc/platform")); + /* do not umount on cdrom or pfSense platforms */ + if ($platform == "cdrom" or $platform == "pfSense") { + return; + } + + if (refcount_unreference(1000) > 0) { + return; + } + + if (isset($config['system']['nanobsd_force_rw'])) { + return; + } + + if (platform_booting()) { + return; + } + + clear_subsystem_dirty('mount'); + /* sync data, then force a remount of /cf */ + pfSense_fsync($g['cf_path']); + mwexec("/sbin/mount -u -r -f -o sync,noatime {$g['cf_path']}"); + mwexec("/sbin/mount -u -r -f -o sync,noatime /"); +} + +/****f* config/convert_config + * NAME + * convert_config - Attempt to update config.xml. + * DESCRIPTION + * convert_config() reads the current global configuration + * and attempts to convert it to conform to the latest + * config.xml version. This allows major formatting changes + * to be made with a minimum of breakage. + * RESULT + * null + ******/ +/* convert configuration, if necessary */ +function convert_config() { + global $config, $g; + $now = date("H:i:s"); + log_error(sprintf(gettext("Start Configuration upgrade at %s, set execution timeout to 15 minutes"), $now)); + //ini_set("max_execution_time", "900"); + + /* special case upgrades */ + /* fix every minute crontab bogons entry */ + if (is_array($config['cron'])) { + $cron_item_count = count($config['cron']['item']); + for ($x = 0; $x < $cron_item_count; $x++) { + if (stristr($config['cron']['item'][$x]['command'], "rc.update_bogons.sh")) { + if ($config['cron']['item'][$x]['hour'] == "*") { + $config['cron']['item'][$x]['hour'] = "3"; + write_config(gettext("Updated bogon update frequency to 3am")); + log_error(gettext("Updated bogon update frequency to 3am")); + } + } + } + } + if ($config['version'] == $g['latest_config']) { + return; /* already at latest version */ + } + + // Save off config version + $prev_version = $config['version']; + + include_once('auth.inc'); + include_once('upgrade_config.inc'); + if (file_exists("/etc/inc/upgrade_config_custom.inc")) { + include_once("upgrade_config_custom.inc"); + } + /* Loop and run upgrade_VER_to_VER() until we're at current version */ + while ($config['version'] < $g['latest_config']) { + $cur = $config['version'] * 10; + $next = $cur + 1; + $migration_function = sprintf('upgrade_%03d_to_%03d', $cur, $next); + if (function_exists($migration_function)) { + $migration_function(); + } + $migration_function = "{$migration_function}_custom"; + if (function_exists($migration_function)) { + $migration_function(); + } + $config['version'] = sprintf('%.1f', $next / 10); + if (platform_booting()) { + echo "."; + } + } + + $now = date("H:i:s"); + log_error(sprintf(gettext("Ended Configuration upgrade at %s"), $now)); + + if ($prev_version != $config['version']) { + write_config(sprintf(gettext('Upgraded config version level from %1$s to %2$s'), $prev_version, $config['version'])); + } +} + +/****f* config/safe_write_file + * NAME + * safe_write_file - Write a file out atomically + * DESCRIPTION + * safe_write_file() Writes a file out atomically by first writing to a + * temporary file of the same name but ending with the pid of the current + * process, them renaming the temporary file over the original. + * INPUTS + * $filename - string containing the filename of the file to write + * $content - string containing the file content to write to file + * $force_binary - boolean denoting whether we should force binary + * mode writing. + * RESULT + * boolean - true if successful, false if not + ******/ +function safe_write_file($file, $content, $force_binary) { + $tmp_file = $file . "." . getmypid(); + $write_mode = $force_binary ? "wb" : "w"; + + $fd = fopen($tmp_file, $write_mode); + if (!$fd) { + // Unable to open temporary file for writing + return false; + } + if (!fwrite($fd, $content)) { + // Unable to write to temporary file + fclose($fd); + return false; + } + fflush($fd); + fclose($fd); + + if (!pfSense_fsync($tmp_file) || !rename($tmp_file, $file)) { + // Unable to move temporary file to original + @unlink($tmp_file); + return false; + } + + // Sync file before returning + return pfSense_fsync($file); +} + +/****f* config/write_config + * NAME + * write_config - Backup and write the firewall configuration. + * DESCRIPTION + * write_config() handles backing up the current configuration, + * applying changes, and regenerating the configuration cache. + * INPUTS + * $desc - string containing the a description of configuration changes + * $backup - boolean: do not back up current configuration if false. + * RESULT + * null + ******/ +/* save the system configuration */ +function write_config($desc="Unknown", $backup = true) { + global $config, $g; + + if (!empty($_SERVER['REMOTE_ADDR'])) { + if (!session_id()) { + @session_start(); + } + if (!empty($_SESSION['Username']) && ($_SESSION['Username'] != "admin")) { + $user = getUserEntry($_SESSION['Username']); + if (is_array($user) && userHasPrivilege($user, "user-config-readonly")) { + session_commit(); + return false; + } + } + } + + if (!isset($argc)) { + session_commit(); + } + + if ($backup) { + backup_config(); + } + + $config['revision'] = make_config_revision_entry($desc); + + conf_mount_rw(); + $lockkey = lock('config', LOCK_EX); + + /* generate configuration XML */ + $xmlconfig = dump_xml_config($config, $g['xml_rootobj']); + + /* write new configuration */ + if (!safe_write_file("{$g['cf_conf_path']}/config.xml", $xmlconfig, false)) { + log_error(gettext("WARNING: Config contents could not be saved. Could not open file!")); + unlock($lockkey); + file_notice("config.xml", sprintf(gettext("Unable to open %s/config.xml for writing in write_config()%s"), $g['cf_conf_path'], "\n")); + return -1; + } + + cleanup_backupcache(true); + + /* re-read configuration */ + /* NOTE: We assume that the file can be parsed since we wrote it. */ + $config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']); + if ($config == -1) { + copy("{$g['conf_path']}/config.xml", "{$g['conf_path']}/config.xml.bad"); + $last_backup = discover_last_backup(); + if ($last_backup) { + restore_backup("/cf/conf/backup/{$last_backup}"); + $config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']); + if (platform_booting()) { + echo "\n\n ************** WARNING **************"; + echo "\n\n Configuration could not be validated. A previous configuration was restored. \n"; + echo "\n The failed configuration file has been saved as {$g['conf_path']}/config.xml.bad \n\n"; + } + } else { + log_error(gettext("Could not restore config.xml.")); + } + } else { + generate_config_cache($config); + } + + unlock($lockkey); + + unlink_if_exists("/usr/local/pkg/pf/carp_sync_client.php"); + + /* tell kernel to sync fs data */ + conf_mount_ro(); + + /* sync carp entries to other firewalls */ + carp_sync_client(); + + if (is_dir("/usr/local/pkg/write_config")) { + /* process packager manager custom rules */ + run_plugins("/usr/local/pkg/write_config/"); + } + + return $config; +} + +/****f* config/reset_factory_defaults + * NAME + * reset_factory_defaults - Reset the system to its default configuration. + * RESULT + * integer - indicates completion + ******/ +function reset_factory_defaults($lock = false) { + global $g; + + conf_mount_rw(); + if (!$lock) { + $lockkey = lock('config', LOCK_EX); + } + + /* create conf directory, if necessary */ + safe_mkdir("{$g['cf_conf_path']}"); + + /* clear out /conf */ + $dh = opendir($g['conf_path']); + while ($filename = readdir($dh)) { + if (($filename != ".") && ($filename != "..")) { + unlink_if_exists($g['conf_path'] . "/" . $filename); + } + } + closedir($dh); + unlink_if_exists($g['tmp_path'] . "/config.cache"); + + /* copy default configuration */ + copy("{$g['conf_default_path']}/config.xml", "{$g['conf_path']}/config.xml"); + + disable_security_checks(); + + /* call the wizard */ + touch("/conf/trigger_initial_wizard"); + if (!$lock) { + unlock($lockkey); + } + conf_mount_ro(); + setup_serial_port(); + return 0; +} + +function config_restore($conffile) { + global $config, $g; + + if (!file_exists($conffile)) { + return 1; + } + + backup_config(); + + conf_mount_rw(); + + $lockkey = lock('config', LOCK_EX); + + unlink_if_exists("{$g['tmp_path']}/config.cache"); + copy($conffile, "{$g['cf_conf_path']}/config.xml"); + + disable_security_checks(); + + unlock($lockkey); + + $config = parse_config(true); + + conf_mount_ro(); + + write_config(gettext("Reverted to") . " " . array_pop(explode("/", $conffile)) . ".", false); + + return 0; +} + +function config_install($conffile) { + global $config, $g; + + if (!file_exists($conffile)) { + return 1; + } + + if (!config_validate("{$conffile}")) { + return 1; + } + + if (platform_booting()) { + echo gettext("Installing configuration...") . "\n"; + } else { + log_error(gettext("Installing configuration ....")); + } + + conf_mount_rw(); + $lockkey = lock('config', LOCK_EX); + + copy($conffile, "{$g['conf_path']}/config.xml"); + + disable_security_checks(); + + /* unlink cache file if it exists */ + if (file_exists("{$g['tmp_path']}/config.cache")) { + unlink("{$g['tmp_path']}/config.cache"); + } + + unlock($lockkey); + conf_mount_ro(); + + return 0; +} + +/* + * Disable security checks for DNS rebind and HTTP referrer until next time + * they pass (or reboot), to aid in preventing accidental lockout when + * restoring settings like hostname, domain, IP addresses, and settings + * related to the DNS rebind and HTTP referrer checks. + * Intended for use when restoring a configuration or directly + * modifying config.xml without an unconditional reboot. + */ +function disable_security_checks() { + global $g; + touch("{$g['tmp_path']}/disable_security_checks"); +} + +/* Restores security checks. Should be called after all succeed. */ +function restore_security_checks() { + global $g; + unlink_if_exists("{$g['tmp_path']}/disable_security_checks"); +} + +/* Returns status of security check temporary disable. */ +function security_checks_disabled() { + global $g; + return file_exists("{$g['tmp_path']}/disable_security_checks"); +} + +function config_validate($conffile) { + + global $g, $xmlerr; + + $xml_parser = xml_parser_create(); + + if (!($fp = fopen($conffile, "r"))) { + $xmlerr = gettext("XML error: unable to open file"); + return false; + } + + while ($data = fread($fp, 4096)) { + if (!xml_parse($xml_parser, $data, feof($fp))) { + $xmlerr = sprintf(gettext('%1$s at line %2$d'), + xml_error_string(xml_get_error_code($xml_parser)), + xml_get_current_line_number($xml_parser)); + return false; + } + } + xml_parser_free($xml_parser); + + fclose($fp); + + return true; +} + +function cleanup_backupcache($lock = false) { + global $g; + $i = false; + + $revisions = get_config_backup_count(); + + if (!$lock) { + $lockkey = lock('config'); + } + + conf_mount_rw(); + + $backups = get_backups(); + if ($backups) { + $baktimes = $backups['versions']; + unset($backups['versions']); + } else { + $backups = array(); + $baktimes = array(); + } + $newbaks = array(); + $bakfiles = glob($g['cf_conf_path'] . "/backup/config-*"); + $tocache = array(); + + foreach ($bakfiles as $backup) { // Check for backups in the directory not represented in the cache. + $backupsize = filesize($backup); + if ($backupsize == 0) { + unlink($backup); + continue; + } + $backupexp = explode('-', $backup); + $backupexp = explode('.', array_pop($backupexp)); + $tocheck = array_shift($backupexp); + unset($backupexp); + if (!in_array($tocheck, $baktimes)) { + $i = true; + if (platform_booting()) { + echo "."; + } + $newxml = parse_xml_config($backup, array($g['xml_rootobj'], 'pfsense')); + if ($newxml == "-1") { + log_error(sprintf(gettext("The backup cache file %s is corrupted. Unlinking."), $backup)); + unlink($backup); + log_error(sprintf(gettext("The backup cache file %s is corrupted. Unlinking."), $backup)); + continue; + } + if ($newxml['revision']['description'] == "") { + $newxml['revision']['description'] = "Unknown"; + } + if ($newxml['version'] == "") { + $newxml['version'] = "?"; + } + $tocache[$tocheck] = array('description' => $newxml['revision']['description'], 'version' => $newxml['version'], 'filesize' => $backupsize); + } + } + foreach ($backups as $checkbak) { + if (count(preg_grep('/' . $checkbak['time'] . '/i', $bakfiles)) != 0) { + $newbaks[] = $checkbak; + } else { + $i = true; + if (platform_booting()) print " " . $tocheck . "r"; + } + } + foreach ($newbaks as $todo) { + $tocache[$todo['time']] = array('description' => $todo['description'], 'version' => $todo['version'], 'filesize' => $todo['filesize']); + } + if (is_int($revisions) and (count($tocache) > $revisions)) { + $toslice = array_slice(array_keys($tocache), 0, $revisions); + foreach ($toslice as $sliced) { + $newcache[$sliced] = $tocache[$sliced]; + } + foreach ($tocache as $version => $versioninfo) { + if (!in_array($version, array_keys($newcache))) { + unlink_if_exists($g['conf_path'] . '/backup/config-' . $version . '.xml'); + } + } + $tocache = $newcache; + } + $bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w"); + fwrite($bakout, serialize($tocache)); + fclose($bakout); + pfSense_fsync("{$g['cf_conf_path']}/backup/backup.cache"); + conf_mount_ro(); + + if (!$lock) { + unlock($lockkey); + } +} + +function get_backups() { + global $g; + if (file_exists("{$g['cf_conf_path']}/backup/backup.cache")) { + $confvers = unserialize(file_get_contents("{$g['cf_conf_path']}/backup/backup.cache")); + $bakvers = array_keys($confvers); + $toreturn = array(); + sort($bakvers); + // $bakvers = array_reverse($bakvers); + foreach (array_reverse($bakvers) as $bakver) { + $toreturn[] = array('time' => $bakver, 'description' => $confvers[$bakver]['description'], 'version' => $confvers[$bakver]['version'], 'filesize' => $confvers[$bakver]['filesize']); + } + } else { + return false; + } + $toreturn['versions'] = $bakvers; + return $toreturn; +} + +function backup_config() { + global $config, $g; + + if ($g['platform'] == "cdrom") { + return; + } + + conf_mount_rw(); + + /* Create backup directory if needed */ + safe_mkdir("{$g['cf_conf_path']}/backup"); + if ($config['revision']['time'] == "") { + $baktime = 0; + } else { + $baktime = $config['revision']['time']; + } + + if ($config['revision']['description'] == "") { + $bakdesc = "Unknown"; + } else { + $bakdesc = $config['revision']['description']; + } + + $bakver = ($config['version'] == "") ? "?" : $config['version']; + $bakfilename = $g['cf_conf_path'] . '/backup/config-' . $baktime . '.xml'; + copy($g['cf_conf_path'] . '/config.xml', $bakfilename); + + if (file_exists($g['cf_conf_path'] . '/backup/backup.cache')) { + $backupcache = unserialize(file_get_contents($g['cf_conf_path'] . '/backup/backup.cache')); + } else { + $backupcache = array(); + } + $backupcache[$baktime] = array('description' => $bakdesc, 'version' => $bakver, 'filesize' => filesize($bakfilename)); + $bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w"); + fwrite($bakout, serialize($backupcache)); + fclose($bakout); + pfSense_fsync("{$g['cf_conf_path']}/backup/backup.cache"); + + conf_mount_ro(); + + return true; +} + +function set_device_perms() { + $devices = array( + 'pf' => array( + 'user' => 'root', + 'group' => 'proxy', + 'mode' => 0660), + ); + + foreach ($devices as $name => $attr) { + $path = "/dev/$name"; + if (file_exists($path)) { + chown($path, $attr['user']); + chgrp($path, $attr['group']); + chmod($path, $attr['mode']); + } + } +} + +function get_config_user() { + if (empty($_SESSION["Username"])) { + $username = getenv("USER"); + if (empty($conuser) || $conuser == "root") { + $username = "(system)"; + } + } else { + $username = $_SESSION["Username"]; + } + + if (!empty($_SERVER['REMOTE_ADDR'])) { + $username .= '@' . $_SERVER['REMOTE_ADDR']; + } + + return $username; +} + +function make_config_revision_entry($desc = null, $override_user = null) { + if (empty($override_user)) { + $username = get_config_user(); + } else { + $username = $override_user; + } + + $revision = array(); + + if (time() > mktime(0, 0, 0, 9, 1, 2004)) { /* make sure the clock settings are plausible */ + $revision['time'] = time(); + } + + /* Log the running script so it's not entirely unlogged what changed */ + if ($desc == "Unknown") { + $desc = sprintf(gettext("%s made unknown change"), $_SERVER['SCRIPT_NAME']); + } + if (!empty($desc)) { + $revision['description'] = "{$username}: " . $desc; + } + $revision['username'] = $username; + return $revision; +} + +function get_config_backup_count() { + global $config, $g; + if (isset($config['system']['backupcount']) && is_numeric($config['system']['backupcount']) && ($config['system']['backupcount'] >= 0)) { + return intval($config['system']['backupcount']); + } elseif ($g['platform'] == "nanobsd") { + return 5; + } else { + return 30; + } +} + +function pfSense_clear_globals() { + global $config, $FilterIfList, $GatewaysList, $filterdns, $aliases, $aliastable; + + $error = error_get_last(); + + if ($error !== NULL) { + if ($error['type'] == E_ERROR) { + $errorstr = "PHP ERROR: Type: {$error['type']}, File: {$error['file']}, Line: {$error['line']}, Message: {$error['message']}"; + print($errorstr); + log_error($errorstr); + } else if ($error['type'] != E_NOTICE) { + $errorstr = "PHP WARNING: Type: {$error['type']}, File: {$error['file']}, Line: {$error['line']}, Message: {$error['message']}"; + // XXX: comment out for now, should re-enable post-2.2 + //print($errorstr); + //log_error($errorstr); + } + } + + if (isset($FilterIfList)) { + unset($FilterIfList); + } + + if (isset($GatewaysList)) { + unset($GatewaysList); + } + + /* Used for the hostname dns resolver */ + if (isset($filterdns)) { + unset($filterdns); + } + + /* Used for aliases and interface macros */ + if (isset($aliases)) { + unset($aliases); + } + if (isset($aliastable)) { + unset($aliastable); + } + + unset($config); +} + +register_shutdown_function('pfSense_clear_globals'); + +?> diff --git a/src/etc/inc/cram_md5_sasl_client.inc b/src/etc/inc/cram_md5_sasl_client.inc new file mode 100644 index 0000000..69bd625 --- /dev/null +++ b/src/etc/inc/cram_md5_sasl_client.inc @@ -0,0 +1,67 @@ +<?php +/* + * cram_md5_sasl_client.php + * + * @(#) $Id: cram_md5_sasl_client.php,v 1.3 2004/11/17 08:00:37 mlemos Exp $ + * + */ + +define("SASL_CRAM_MD5_STATE_START", 0); +define("SASL_CRAM_MD5_STATE_RESPOND_CHALLENGE", 1); +define("SASL_CRAM_MD5_STATE_DONE", 2); + +class cram_md5_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_CRAM_MD5_STATE_START; + + Function Initialize(&$client) + { + return(1); + } + + Function HMACMD5($key,$text) + { + $key=(strlen($key)<64 ? str_pad($key,64,"\0") : substr($key,0,64)); + return(md5((str_repeat("\x5c", 64)^$key).pack("H32", md5((str_repeat("\x36", 64)^$key).$text)))); + } + + Function Start(&$client, &$message, &$interactions) + { + if($this->state!=SASL_CRAM_MD5_STATE_START) + { + $client->error="CRAM-MD5 authentication state is not at the start"; + return(SASL_FAIL); + } + $this->credentials=array( + "user"=>"", + "password"=>"" + ); + $defaults=array(); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if($status==SASL_CONTINUE) + $this->state=SASL_CRAM_MD5_STATE_RESPOND_CHALLENGE; + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch($this->state) + { + case SASL_CRAM_MD5_STATE_RESPOND_CHALLENGE: + $message=$this->credentials["user"]." ".$this->HMACMD5($this->credentials["password"], $response); + $this->state=SASL_CRAM_MD5_STATE_DONE; + break; + case SASL_CRAM_MD5_STATE_DONE: + $client->error="CRAM-MD5 authentication was finished without success"; + return(SASL_FAIL); + default: + $client->error="invalid CRAM-MD5 authentication step state"; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/src/etc/inc/crypt.inc b/src/etc/inc/crypt.inc new file mode 100644 index 0000000..8d96b26 --- /dev/null +++ b/src/etc/inc/crypt.inc @@ -0,0 +1,101 @@ +<?php + +/* $Id$ */ +/* + Copyright (C) 2008 Shrew Soft Inc + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_BUILDER_BINARIES: /usr/bin/openssl + pfSense_MODULE: crypto + +*/ + + function crypt_data($val, $pass, $opt) { + $file = tempnam("/tmp", "php-encrypt"); + file_put_contents("{$file}.dec", $val); + exec("/usr/bin/openssl enc {$opt} -aes-256-cbc -in {$file}.dec -out {$file}.enc -k " . escapeshellarg($pass)); + if (file_exists("{$file}.enc")) { + $result = file_get_contents("{$file}.enc"); + } else { + $result = ""; + log_error("Failed to encrypt/decrypt data!"); + } + @unlink($file); + @unlink("{$file}.dec"); + @unlink("{$file}.enc"); + return $result; + } + + function encrypt_data(& $data, $pass) { + return base64_encode(crypt_data($data, $pass, "-e")); + } + + function decrypt_data(& $data, $pass) { + return crypt_data(base64_decode($data), $pass, "-d"); + } + + function tagfile_reformat($in, & $out, $tag) { + + $out = "---- BEGIN {$tag} ----\n"; + + $size = 80; + $oset = 0; + while ($size >= 64) { + $line = substr($in, $oset, 64); + $out .= $line."\n"; + $size = strlen($line); + $oset += $size; + } + + $out .= "---- END {$tag} ----\n"; + + return true; + } + + function tagfile_deformat($in, & $out, $tag) { + + $btag_val = "---- BEGIN {$tag} ----"; + $etag_val = "---- END {$tag} ----"; + + $btag_len = strlen($btag_val); + $etag_len = strlen($etag_val); + + $btag_pos = stripos($in, $btag_val); + $etag_pos = stripos($in, $etag_val); + + if (($btag_pos === false) || ($etag_pos === false)) { + return false; + } + + $body_pos = $btag_pos + $btag_len; + $body_len = strlen($in); + $body_len -= $btag_len; + $body_len -= $etag_len + 1; + + $out = substr($in, $body_pos, $body_len); + + return true; + } + +?> diff --git a/src/etc/inc/digest_sasl_client.inc b/src/etc/inc/digest_sasl_client.inc new file mode 100644 index 0000000..924887d --- /dev/null +++ b/src/etc/inc/digest_sasl_client.inc @@ -0,0 +1,135 @@ +<?php +/* + * digest_sasl_client.php + * + * @(#) $Id: digest_sasl_client.php,v 1.1 2005/10/27 05:24:15 mlemos Exp $ + * + */ + +define('SASL_DIGEST_STATE_START', 0); +define('SASL_DIGEST_STATE_RESPOND_CHALLENGE', 1); +define('SASL_DIGEST_STATE_DONE', 2); + +class digest_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_DIGEST_STATE_START; + + Function unq($string) + { + return(($string[0]=='"' && $string[strlen($string)-1]=='"') ? substr($string, 1, strlen($string)-2) : $string); + } + + Function H($data) + { + return md5($data); + } + + Function KD($secret, $data) + { + return $this->H($secret.':'.$data); + } + + Function Initialize(&$client) + { + return(1); + } + + Function Start(&$client, &$message, &$interactions) + { + if($this->state!=SASL_DIGEST_STATE_START) + { + $client->error='Digest authentication state is not at the start'; + return(SASL_FAIL); + } + $this->credentials=array( + 'user'=>'', + 'password'=>'', + 'uri'=>'', + 'method'=>'', + 'session'=>'' + ); + $defaults=array(); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if($status==SASL_CONTINUE) + $this->state=SASL_DIGEST_STATE_RESPOND_CHALLENGE; + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch($this->state) + { + case SASL_DIGEST_STATE_RESPOND_CHALLENGE: + $values=explode(',',$response); + $parameters=array(); + for($v=0; $v<count($values); $v++) + $parameters[strtok(trim($values[$v]), '=')]=strtok(''); + + $message='username="'.$this->credentials['user'].'"'; + if(!IsSet($parameters[$p='realm']) + && !IsSet($parameters[$p='nonce'])) + { + $client->error='Digest authentication parameter '.$p.' is missing from the server response'; + return(SASL_FAIL); + } + $message.=', realm='.$parameters['realm']; + $message.=', nonce='.$parameters['nonce']; + $message.=', uri="'.$this->credentials['uri'].'"'; + if(IsSet($parameters['algorithm'])) + { + $algorithm=$this->unq($parameters['algorithm']); + $message.=', algorithm='.$parameters['algorithm']; + } + else + $algorithm=''; + + $realm=$this->unq($parameters['realm']); + $nonce=$this->unq($parameters['nonce']); + if(IsSet($parameters['qop'])) + { + switch($qop=$this->unq($parameters['qop'])) + { + case "auth": + $cnonce=$this->credentials['session']; + break; + default: + $client->error='Digest authentication quality of protection '.$qop.' is not yet supported'; + return(SASL_FAIL); + } + } + $nc_value='00000001'; + if(IsSet($parameters['qop']) + && !strcmp($algorithm, 'MD5-sess')) + $A1=$this->H($this->credentials['user'].':'. $realm.':'. $this->credentials['password']).':'.$nonce.':'.$cnonce; + else + $A1=$this->credentials['user'].':'. $realm.':'. $this->credentials['password']; + $A2=$this->credentials['method'].':'.$this->credentials['uri']; + if(IsSet($parameters['qop'])) + $response=$this->KD($this->H($A1), $nonce.':'. $nc_value.':'. $cnonce.':'. $qop.':'. $this->H($A2)); + else + $response=$this->KD($this->H($A1), $nonce.':'. $this->H($A2)); + $message.=', response="'.$response.'"'; + if(IsSet($parameters['opaque'])) + $message.=', opaque='.$parameters['opaque']; + if(IsSet($parameters['qop'])) + $message.=', qop="'.$qop.'"'; + $message.=', nc='.$nc_value; + if(IsSet($parameters['qop'])) + $message.=', cnonce="'.$cnonce.'"'; + $client->encode_response=0; + $this->state=SASL_DIGEST_STATE_DONE; + break; + case SASL_DIGEST_STATE_DONE: + $client->error='Digest authentication was finished without success'; + return(SASL_FAIL); + default: + $client->error='invalid Digest authentication step state'; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/src/etc/inc/dot.hushlogin b/src/etc/inc/dot.hushlogin new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/etc/inc/dot.hushlogin diff --git a/src/etc/inc/dyndns.class b/src/etc/inc/dyndns.class new file mode 100644 index 0000000..cb21fb5 --- /dev/null +++ b/src/etc/inc/dyndns.class @@ -0,0 +1,1634 @@ +<?php + /* + * PHP.updateDNS (pfSense version) + * + * +====================================================+ + * Services Supported: + * - DynDns (dyndns.org) [dynamic, static, custom] + * - DHSDns (dhs.org) + * - No-IP (no-ip.com) + * - EasyDNS (easydns.com) + * - DHS (www.dhs.org) + * - HN (hn.org) -- incomplete checking! + * - DynS (dyns.org) + * - ZoneEdit (zoneedit.com) + * - FreeDNS (freedns.afraid.org) + * - Loopia (loopia.se) + * - StaticCling (staticcling.org) + * - DNSexit (dnsexit.com) + * - OpenDNS (opendns.com) + * - Namecheap (namecheap.com) + * - HE.net (dns.he.net) + * - HE.net IPv6 (dns.he.net) + * - HE.net Tunnelbroker IP update (ipv4.tunnelbroker.net) + * - SelfHost (selfhost.de) + * - Amazon Route 53 (aws.amazon.com) + * - DNS-O-Matic (dnsomatic.com) + * - Custom DDNS (any URL) + * - Custom DDNS IPv6 (any URL) + * - CloudFlare (www.cloudflare.com) + * - Eurodns (eurodns.com) + * - GratisDNS (gratisdns.dk) + * - City Network (citynetwork.se) + * - GleSYS (glesys.com) + * - DNSimple (dnsimple.com) + * - Google Domains (domains.google.com) + * - DNS Made Easy (www.dnsmadeeasy.com) + * +----------------------------------------------------+ + * Requirements: + * - PHP version 4.0.2 or higher with the CURL Library and the PCRE Library + * +----------------------------------------------------+ + * Public Functions + * - updatedns() + * + * Private Functions + * - _update() + * - _checkStatus() + * - _error() + * - _detectChange() + * - _debug() + * - _checkIP() + * +----------------------------------------------------+ + * DynDNS Dynamic - Last Tested: 12 July 2005 + * DynDNS Static - Last Tested: NEVER + * DynDNS Custom - Last Tested: NEVER + * No-IP - Last Tested: 20 July 2008 + * HN.org - Last Tested: 12 July 2005 + * EasyDNS - Last Tested: 20 July 2008 + * DHS - Last Tested: 12 July 2005 + * ZoneEdit - Last Tested: NEVER + * Dyns - Last Tested: NEVER + * ODS - Last Tested: 02 August 2005 + * FreeDNS - Last Tested: 23 Feb 2011 + * Loopia - Last Tested: NEVER + * StaticCling - Last Tested: 27 April 2006 + * DNSexit - Last Tested: 20 July 2008 + * OpenDNS - Last Tested: 4 August 2008 + * Namecheap - Last Tested: 31 August 2010 + * HE.net - Last Tested: 7 July 2013 + * HE.net IPv6 - Last Tested: 7 July 2013 + * HE.net Tunnel - Last Tested: 28 June 2011 + * SelfHost - Last Tested: 26 December 2011 + * Amazon Route 53 - Last tested: 01 April 2012 + * DNS-O-Matic - Last Tested: 9 September 2010 + * CloudFlare - Last Tested: 30 May 2013 + * Eurodns - Last Tested: 27 June 2013 + * GratisDNS - Last Tested: 15 August 2012 + * OVH DynHOST - Last Tested: NEVER + * City Network - Last Tested: 13 November 2013 + * GleSYS - Last Tested: 3 February 2015 + * DNSimple - Last Tested: 09 February 2015 + * Google Domains - Last Tested: 27 April 2015 + * DNS Made Easy - Last Tested: 27 April 2015 + * +====================================================+ + * + * @author E.Kristensen + * @link http://www.idylldesigns.com/projects/phpdns/ + * @version 0.8 + * @updated 13 October 05 at 21:02:42 GMT + * + * DNSexit/OpenDNS support and multiwan extension for pfSense by Ermal Luçi + * Custom DNS support by Matt Corallo + * + */ + + class updatedns { + var $_cacheFile; + var $_cacheFile_v6; + var $_debugFile; + var $_UserAgent = 'User-Agent: phpDynDNS/0.7'; + var $_errorVerbosity = 0; + var $_dnsService; + var $_dnsUser; + var $_dnsPass; + var $_dnsHost; + var $_dnsIP; + var $_dnsWildcard; + var $_dnsMX; + var $_dnsBackMX; + var $_dnsServer; + var $_dnsPort; + var $_dnsUpdateURL; + var $_dnsZoneID; + var $_dnsTTL; + var $status; + var $_debugID; + var $_if; + var $_dnsResultMatch; + var $_dnsRequestIf; + var $_dnsRequestIfIP; + var $_dnsVerboseLog; + var $_curlIpresolveV4; + var $_curlSslVerifypeer; + var $_dnsMaxCacheAgeDays; + var $_dnsDummyUpdateDone; + var $_forceUpdateNeeded; + var $_useIPv6; + + /* + * Public Constructor Function (added 12 July 05) [beta] + * - Gets the dice rolling for the update. + * - $dnsResultMatch should only be used with $dnsService = 'custom' + * - $dnsResultMatch is parsed for '%IP%', which is the IP the provider was updated to, + * - it is otherwise expected to be exactly identical to what is returned by the Provider. + * - $dnsUser, and $dnsPass indicate HTTP Auth for custom DNS, if they are needed in the URL (GET Variables), include them in $dnsUpdateURL. + * - $For custom requests, $dnsUpdateURL is parsed for '%IP%', which is replaced with the new IP. + */ + function updatedns ($dnsService = '', $dnsHost = '', $dnsUser = '', $dnsPass = '', + $dnsWildcard = 'OFF', $dnsMX = '', $dnsIf = '', $dnsBackMX = '', + $dnsServer = '', $dnsPort = '', $dnsUpdateURL = '', $forceUpdate = false, + $dnsZoneID ='', $dnsTTL='', $dnsResultMatch = '', $dnsRequestIf = '', + $dnsID = '', $dnsVerboseLog = false, $curlIpresolveV4 = false, $curlSslVerifypeer = true) { + + global $config, $g; + + $this->_cacheFile = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($dnsHost) . "{$dnsID}.cache"; + $this->_cacheFile_v6 = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($dnsHost) . "{$dnsID}_v6.cache"; + $this->_debugFile = "{$g['varetc_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($dnsHost) . "{$dnsID}.debug"; + + $this->_curlIpresolveV4 = $curlIpresolveV4; + $this->_curlSslVerifypeer = $curlSslVerifypeer; + $this->_dnsVerboseLog = $dnsVerboseLog; + if ($this->_dnsVerboseLog) { + log_error("DynDns: updatedns() starting"); + } + + $dyndnslck = lock("DDNS".$dnsID, LOCK_EX); + + if (!$dnsService) $this->_error(2); + switch ($dnsService) { + case 'freedns': + if (!$dnsHost) $this->_error(5); + break; + case 'namecheap': + if (!$dnsPass) $this->_error(4); + if (!$dnsHost) $this->_error(5); + break; + case 'route53': + if (!$dnsZoneID) $this->_error(8); + if (!$dnsTTL) $this->_error(9); + break; + case 'custom': + if (!$dnsUpdateURL) $this->_error(7); + break; + default: + if (!$dnsUser) $this->_error(3); + if (!$dnsPass) $this->_error(4); + if (!$dnsHost) $this->_error(5); + } + + switch ($dnsService) { + case 'he-net-v6': + case 'custom-v6': + $this->_useIPv6 = true; + break; + default: + $this->_useIPv6 = false; + } + $this->_dnsService = strtolower($dnsService); + $this->_dnsUser = $dnsUser; + $this->_dnsPass = $dnsPass; + $this->_dnsHost = $dnsHost; + $this->_dnsServer = $dnsServer; + $this->_dnsPort = $dnsPort; + $this->_dnsWildcard = $dnsWildcard; + $this->_dnsMX = $dnsMX; + $this->_dnsZoneID = $dnsZoneID; + $this->_dnsTTL = $dnsTTL; + $this->_if = get_failover_interface($dnsIf); + $this->_checkIP(); + $this->_dnsUpdateURL = $dnsUpdateURL; + $this->_dnsResultMatch = $dnsResultMatch; + $this->_dnsRequestIf = get_failover_interface($dnsRequestIf); + if ($this->_dnsVerboseLog) { + log_error("DynDNS ({$this->_dnsHost}): running get_failover_interface for {$dnsRequestIf}. found {$this->_dnsRequestIf}"); + } + $this->_dnsRequestIfIP = get_interface_ip($dnsRequestIf); + $this->_dnsMaxCacheAgeDays = 25; + $this->_dnsDummyUpdateDone = false; + $this->_forceUpdateNeeded = $forceUpdate; + + // Ensure that we were able to lookup the IP + if (!is_ipaddr($this->_dnsIP)) { + log_error("DynDNS ({$this->_dnsHost}) There was an error trying to determine the public IP for interface - {$dnsIf}({$this->_if}). Probably interface is not a WAN interface."); + unlock($dyndnslck); + return; + } + + $this->_debugID = rand(1000000, 9999999); + + if ($forceUpdate == false && $this->_detectChange() == false) { + $this->_error(10); + } else { + switch ($this->_dnsService) { + case 'glesys': + case 'dnsomatic': + case 'dyndns': + case 'dyndns-static': + case 'dyndns-custom': + case 'dhs': + case 'noip': + case 'noip-free': + case 'easydns': + case 'hn': + case 'zoneedit': + case 'dyns': + case 'ods': + case 'freedns': + case 'loopia': + case 'staticcling': + case 'dnsexit': + case 'custom': + case 'custom-v6': + case 'opendns': + case 'namecheap': + case 'he-net': + case 'he-net-v6': + case 'selfhost': + case 'he-net-tunnelbroker': + case 'route53': + case 'cloudflare': + case 'eurodns': + case 'gratisdns': + case 'ovh-dynhost': + case 'citynetwork': + case 'dnsimple': + case 'googledomains': + case 'dnsmadeeasy': + $this->_update(); + if ($this->_dnsDummyUpdateDone == true) { + // If a dummy update was needed, then sleep a while and do the update again to put the proper address back. + // Some providers (e.g. No-IP free accounts) need to have at least 1 address change every month. + // If the address has not changed recently, or the user did "Force Update", then the code does + // a dummy address change for providers like this. + sleep(10); + $this->_update(); + } + break; + default: + $this->_error(6); + break; + } + } + + unlock($dyndnslck); + } + + /* + * Private Function (added 12 July 05) [beta] + * Send Update To Selected Service. + */ + function _update() { + + if ($this->_dnsVerboseLog) { + log_error("DynDNS ({$this->_dnsHost}): DynDns _update() starting."); + } + + if ($this->_dnsService != 'ods' and $this->_dnsService != 'route53 ') { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $this->_dnsRequestIf); + curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical + } + + switch ($this->_dnsService) { + case 'glesys': + $needsIP = TRUE; + if ($this->_dnsVerboseLog) { + log_error("DynDNS: ({$this->_dnsHost}) DNS update() starting."); + } + $server = 'https://api.glesys.com/domain/updaterecord/format/json'; + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + $post_data['recordid'] = $this->_dnsHost; + $post_data['data'] = $this->_dnsIP; + curl_setopt($ch, CURLOPT_URL, $server); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); + break; + case 'dyndns': + case 'dyndns-static': + case 'dyndns-custom': + $needsIP = FALSE; + if ($this->_dnsVerboseLog) { + log_error("DynDNS: ({$this->_dnsHost}) DNS update() starting."); + } + if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") { + $this->_dnsWildcard = "ON"; + } + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + $server = "https://members.dyndns.org/nic/update"; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, $server .$port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO'); + break; + case 'dhs': + $needsIP = TRUE; + $post_data['hostscmd'] = 'edit'; + $post_data['hostscmdstage'] = '2'; + $post_data['type'] = '4'; + $post_data['updatetype'] = 'Online'; + $post_data['mx'] = $this->_dnsMX; + $post_data['mx2'] = ''; + $post_data['txt'] = ''; + $post_data['offline_url'] = ''; + $post_data['cloak'] = 'Y'; + $post_data['cloak_title'] = ''; + $post_data['ip'] = $this->_dnsIP; + $post_data['domain'] = 'dyn.dhs.org'; + $post_data['hostname'] = $this->_dnsHost; + $post_data['submit'] = 'Update'; + $server = "https://members.dhs.org/nic/hosts"; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, '{$server}{$port}'); + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); + break; + case 'noip': + case 'noip-free': + $needsIP = TRUE; + $server = "https://dynupdate.no-ip.com/ducupdate.php"; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + if (($this->_dnsService == "noip-free") && + ($this->_forceUpdateNeeded == true) && + ($this->_dnsDummyUpdateDone == false)) { + // Update the IP to a dummy value to force No-IP free accounts to see a change. + $iptoset = "192.168.1.1"; + $this->_dnsDummyUpdateDone = true; + log_error("DynDNS ({$this->_dnsHost}): Processing dummy update on No-IP free account. IP temporarily set to " . $iptoset); + } else { + $iptoset = $this->_dnsIP; + } + curl_setopt($ch, CURLOPT_URL, $server . $port . '?username=' . urlencode($this->_dnsUser) . '&pass=' . urlencode($this->_dnsPass) . '&hostname=' . $this->_dnsHost.'&ip=' . $iptoset); + break; + case 'easydns': + $needsIP = TRUE; + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + $server = "https://members.easydns.com/dyn/dyndns.php"; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=' . $this->_dnsBackMX); + break; + case 'hn': + $needsIP = TRUE; + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + $server = "http://dup.hn.org/vanity/update"; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, $server . $port . '?ver=1&IP=' . $this->_dnsIP); + break; + case 'zoneedit': + $needsIP = FALSE; + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + + $server = "https://dynamic.zoneedit.com/auth/dynamic.html"; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, "{$server}{$port}?host=" .$this->_dnsHost); + break; + case 'dyns': + $needsIP = FALSE; + $server = "https://www.dyns.cx/postscript011.php"; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, $server . $port . '?username=' . urlencode($this->_dnsUser) . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost); + break; + case 'ods': + $needsIP = FALSE; + $misc_errno = 0; + $misc_error = ""; + $server = "ods.org"; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + $this->con['socket'] = fsockopen("{$server}{$port}", "7070", $misc_errno, $misc_error, 30); + /* Check that we have connected */ + if (!$this->con['socket']) { + print "error! could not connect."; + break; + } + /* Here is the loop. Read the incoming data (from the socket connection) */ + while (!feof($this->con['socket'])) { + $this->con['buffer']['all'] = trim(fgets($this->con['socket'], 4096)); + $code = substr($this->con['buffer']['all'], 0, 3); + sleep(1); + switch ($code) { + case 100: + fputs($this->con['socket'], "LOGIN ".$this->_dnsUser." ".$this->_dnsPass."\n"); + break; + case 225: + fputs($this->con['socket'], "DELRR ".$this->_dnsHost." A\n"); + break; + case 901: + fputs($this->con['socket'], "ADDRR ".$this->_dnsHost." A ".$this->_dnsIP."\n"); + break; + case 795: + fputs($this->con['socket'], "QUIT\n"); + break; + } + } + $this->_checkStatus(0, $code); + break; + case 'freedns': + $needIP = FALSE; + curl_setopt($ch, CURLOPT_URL, 'https://freedns.afraid.org/dynamic/update.php?' . $this->_dnsPass); + break; + case 'dnsexit': + $needsIP = TRUE; + curl_setopt($ch, CURLOPT_URL, 'https://www.dnsexit.com/RemoteUpdate.sv?login='.$this->_dnsUser. '&password='.$this->_dnsPass.'&host='.$this->_dnsHost.'&myip='.$this->_dnsIP); + break; + case 'loopia': + $needsIP = TRUE; + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + curl_setopt($ch, CURLOPT_URL, 'https://dns.loopia.se/XDynDNSServer/XDynDNS.php?hostname='.$this->_dnsHost.'&myip='.$this->_dnsIP); + break; + case 'opendns': + $needsIP = FALSE; + if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON"; + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + $server = "https://updates.opendns.com/nic/update?hostname=". $this->_dnsHost; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, $server .$port); + break; + + case 'staticcling': + $needsIP = FALSE; + curl_setopt($ch, CURLOPT_URL, 'https://www.staticcling.org/update.html?login='.$this->_dnsUser.'&pass='.$this->_dnsPass); + break; + case 'dnsomatic': + /* Example syntax + https://username:password@updates.dnsomatic.com/nic/update?hostname=yourhostname&myip=ipaddress&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG + */ + $needsIP = FALSE; + if ($this->_dnsVerboseLog) { + log_error("DNS-O-Matic: DNS update() starting."); + } + if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") { + $this->_dnsWildcard = "ON"; + } + /* + Reference: https://www.dnsomatic.com/wiki/api + DNS-O-Matic usernames are 3-25 characters. + DNS-O-Matic passwords are 6-20 characters. + All ASCII letters and numbers accepted. + Dots, dashes, and underscores allowed, but not at the beginning or end of the string. + Required: "rawurlencode" http://www.php.net/manual/en/function.rawurlencode.php + Encodes the given string according to RFC 3986. + */ + $server = "https://" . rawurlencode($this->_dnsUser) . ":" . rawurlencode($this->_dnsPass) . "@updates.dnsomatic.com/nic/update?hostname="; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NOCHG'); + break; + case 'namecheap': + /* Example: + https://dynamicdns.park-your-domain.com/update?host=[host_name]&domain=[domain.com]&password=[domain_password]&ip=[your_ip] + */ + $needsIP = FALSE; + if ($this->_dnsVerboseLog) { + log_error("Namecheap ({$this->_dnsHost}): DNS update() starting."); + } + $dparts = explode(".", trim($this->_dnsHost)); + $domain_part_count = ($dparts[count($dparts)-1] == "uk") ? 3 : 2; + $domain_offset = count($dparts) - $domain_part_count; + $hostname = implode(".", array_slice($dparts, 0, $domain_offset)); + $domain = implode(".", array_slice($dparts, $domain_offset)); + $dnspass = trim($this->_dnsPass); + $server = "https://dynamicdns.park-your-domain.com/update?host={$hostname}&domain={$domain}&password={$dnspass}&ip={$this->_dnsIP}"; + curl_setopt($ch, CURLOPT_URL, $server); + break; + case 'he-net': + case 'he-net-v6': + $needsIP = FALSE; + if ($this->_dnsVerboseLog) { + log_error("HE.net ({$this->_dnsHost}): DNS update() starting."); + } + $server = "https://dyn.dns.he.net/nic/update?"; + curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&password=' . $this->_dnsPass . '&myip=' . $this->_dnsIP); + break; + case 'he-net-tunnelbroker': + $needsIP = FALSE; + if ($this->_dnsVerboseLog) { + log_error("HE.net Tunnelbroker: DNS update() starting."); + } + $server = "https://ipv4.tunnelbroker.net/ipv4_end.php?"; + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass); + curl_setopt($ch, CURLOPT_URL, $server . 'tid=' . $this->_dnsHost); + break; + case 'selfhost': + $needsIP = FALSE; + if ($this->_dnsVerboseLog) { + log_error("SelfHost: DNS update() starting."); + } + if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") { + $this->_dnsWildcard = "ON"; + } + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + $server = "https://carol.selfhost.de/nic/update"; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, $server .$port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO'); + break; + case 'route53': + if ($this->_dnsVerboseLog) { + log_error("Route53 ({$this->_dnsHost}): DNS update() starting."); + } + + /* Setting Variables */ + $hostname = "{$this->_dnsHost}."; + $ZoneID = $this->_dnsZoneID; + $AccessKeyId = $this->_dnsUser; + $SecretAccessKey = $this->_dnsPass; + $NewIP = $this->_dnsIP; + $NewTTL = $this->_dnsTTL; + + /* Include Route 53 Library Class */ + require_once('/etc/inc/r53.class'); + + /* Set Amazon AWS Credentials for this record */ + $r53 = new Route53($AccessKeyId, $SecretAccessKey); + + /* Function to find old values of records in Route 53 */ + if (!function_exists('Searchrecords')) { + function SearchRecords($records, $name) { + $result = array(); + foreach ($records as $record) { + if (strtolower($record['Name']) == strtolower($name)) { + $result [] = $record; + } + } + return ($result) ? $result : false; + } + } + + $records = $r53->listResourceRecordSets("/hostedzone/$ZoneID"); + + /* Get IP for your hostname in Route 53 */ + if (false !== ($a_result = SearchRecords($records['ResourceRecordSets'], "$hostname"))) { + $OldTTL = $a_result[0][TTL]; + $OldIP = $a_result[0][ResourceRecords][0]; + } else { + $OldIP = ""; + } + + /* Check if we need to update DNS Record */ + if ($OldIP !== $NewIP) { + if (!empty($OldIP)) { + /* Your Hostname already exists, deleting and creating it again */ + $changes = array(); + $changes[] = $r53->prepareChange(DELETE, $hostname, A, $OldTTL, $OldIP); + $changes[] = $r53->prepareChange(CREATE, $hostname, A, $NewTTL, $NewIP); + $result = $r53->changeResourceRecordSets("/hostedzone/$ZoneID", $changes); + } else { + /* Your Hostname does not exist yet, creating it */ + $changes = $r53->prepareChange(CREATE, $hostname, A, $NewTTL, $NewIP); + $result = $r53->changeResourceRecordSets("/hostedzone/$ZoneID", $changes); + } + } + $this->_checkStatus(0, $result); + break; + case 'custom': + case 'custom-v6': + if ($this->_dnsVerboseLog) { + log_error("Custom DDNS ({$this->_dnsHost}): DNS update() starting."); + } + if (strstr($this->dnsUpdateURL, "%IP%")) {$needsIP = TRUE;} else {$needsIP = FALSE;} + if ($this->_dnsUser != '') { + if ($this->_curlIpresolveV4) { + curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + } + if ($this->_curlSslVerifypeer) { + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE); + } else { + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); + } + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}"); + } + $server = str_replace("%IP%", $this->_dnsIP, $this->_dnsUpdateURL); + if ($this->_dnsVerboseLog) { + log_error("Sending request to: ".$server); + } + curl_setopt($ch, CURLOPT_URL, $server); + break; + case 'cloudflare': + $needsIP = TRUE; + $dnsServer ='www.cloudflare.com'; + $dnsHost = str_replace(' ', '', $this->_dnsHost); + $URL = "https://{$dnsServer}/api.html?a=DIUP&email={$this->_dnsUser}&tkn={$this->_dnsPass}&ip={$this->_dnsIP}&hosts={$dnsHost}"; + curl_setopt($ch, CURLOPT_URL, $URL); + break; + case 'eurodns': + $needsIP = TRUE; + if ($this->_dnsVerboseLog) { + log_error("EuroDynDns ({$this->_dnsHost}) DNS update() starting."); + } + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + $server = "https://update.eurodyndns.org/update/"; + $port = ""; + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, $server .$port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP); + break; + case 'gratisdns': + $needsIP = TRUE; + if ($this->_dnsVerboseLog) { + log_error("GratisDNS.dk ({$this->_dnsHost}): DNS update() starting."); + } + $server = "https://ssl.gratisdns.dk/ddns.phtml"; + list($hostname, $domain) = explode(".", $this->_dnsHost, 2); + curl_setopt($ch, CURLOPT_URL, $server . '?u=' . $this->_dnsUser . '&p=' . $this->_dnsPass . '&h=' . $this->_dnsHost . '&d=' . $domain . '&i=' . $this->_dnsIP); + break; + case 'ovh-dynhost': + $needsIP = FALSE; + if ($this->_dnsVerboseLog) { + log_error("OVH DynHOST: ({$this->_dnsHost}) DNS update() starting."); + } + if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON"; + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + $server = "https://www.ovh.com/nic/update"; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, $server .$port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO'); + break; + case 'citynetwork': + $needsIP = TRUE; + if ($this->_dnsVerboseLog) { + log_error("City Network: ({$this->_dnsHost}) DNS update() starting."); + } + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + $server = 'https://dyndns.citynetwork.se/nic/update'; + $port = ""; + if ($this->_dnsServer) { + $server = $this->_dnsServer; + } + if ($this->_dnsPort) { + $port = ":" . $this->_dnsPort; + } + curl_setopt($ch, CURLOPT_URL, $server .$port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP); + break; + case 'dnsimple': + /* Uses DNSimple's REST API + Requires username and Account API token passed in header + Piggybacks on Route 53's ZoneID field for DNSimple record ID + Data sent as JSON */ + $needsIP = TRUE; + $server = 'https://api.dnsimple.com/v1/domains/'; + $token = $this->_dnsUser . ':' . $this->_dnsPass; + $jsondata = '{"record":{"content":"' . $this->_dnsIP . '","ttl":"' . $this->_dnsTTL . '"}}'; + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-Type: application/json', 'X-DNSimple-Token: ' . $token)); + curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsHost . '/records/' . $this->_dnsZoneID); + curl_setopt($ch, CURLOPT_POSTFIELDS, $jsondata); + break; + case 'googledomains': + $needsIP = FALSE; + if ($this->_dnsVerboseLog) { + log_error("Google Domains: ({$this->_dnsHost}) DNS update() starting."); + } + $post_data['username:password'] = $this->_dnsUser . ':' . $this->_dnsPass; + $post_data['hostname'] = $this->_dnsHost; + $post_data['myip'] = $this->_dnsIP; + $post_data['offline'] = 'no'; + $server = "https://domains.google.com/nic/update"; + $port = ""; + curl_setopt($ch, CURLOPT_URL, 'https://domains.google.com/nic/update'); + curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); + break; + case 'dnsmadeeasy': + $needsIP = TRUE; + if ($this->_dnsVerboseLog) { + log_error("DNS Made Easy ({$this->_dnsHost}): DNS update() starting."); + } + $server = "https://cp.dnsmadeeasy.com/servlet/updateip"; + curl_setopt($ch, CURLOPT_URL, $server . '?username=' . $this->_dnsUser . '&password=' . $this->_dnsPass . '&id=' . $this->_dnsHost . '&ip=' . $this->_dnsIP); + break; + default: + break; + } + if ($this->_dnsService != 'ods' and $this->_dnsService != 'route53') { + $data = curl_exec($ch); + $this->_checkStatus($ch, $data); + @curl_close($ch); + } + } + + /* + * Private Function (added 12 July 2005) [beta] + * Retrieve Update Status + */ + function _checkStatus($ch, $data) { + if ($this->_dnsVerboseLog) { + log_error("DynDNS ({$this->_dnsHost}): DynDns _checkStatus() starting."); + log_error("DynDNS ({$this->_dnsHost}): Current Service: {$this->_dnsService}"); + } + $successful_update = false; + if ($this->_dnsService != 'ods' and $this->_dnsService != 'route53' && @curl_error($ch)) { + $status = "Curl error occurred: " . curl_error($ch); + log_error($status); + $this->status = $status; + return; + } + switch ($this->_dnsService) { + case 'glesys': + if (preg_match('/Record updated/i', $data)) { + $status = "GleSYS ({$this->_dnsHost}): (Success) IP Address Changed Successfully! (" . $this->_dnsIP . ")"; + $successful_update = true; + } else { + $status = "GleSYS ({$this->_dnsHost}): (Unknown Response)"; + log_error("GleSYS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'dnsomatic': + if (preg_match('/badauth/i', $data)) { + $status = "DNS-O-Matic ({$this->_dnsHost}): The DNS-O-Matic username or password specified are incorrect. No updates will be distributed to services until this is resolved."; + } else if (preg_match('/notfqdn /i', $data)) { + $status = "DNS-O-Matic ({$this->_dnsHost}): The hostname specified is not a fully-qualified domain name. If no hostnames included, notfqdn will be returned once."; + } else if (preg_match('/nohost/i', $data)) { + $status = "DNS-O-Matic ({$this->_dnsHost}): The hostname passed could not be matched to any services configured. The service field will be blank in the return code."; + } else if (preg_match('/numhost/i', $data)) { + $status = "DNS-O-Matic ({$this->_dnsHost}): You may update up to 20 hosts. numhost is returned if you try to update more than 20 or update a round-robin."; + } else if (preg_match('/abuse/i', $data)) { + $status = "DNS-O-Matic ({$this->_dnsHost}): The hostname is blocked for update abuse."; + } else if (preg_match('/good/i', $data)) { + $status = "DNS-O-Matic ({$this->_dnsHost}): (Success) IP Address Changed Successfully! (" . $this->_dnsIP . ")"; + $successful_update = true; + } else if (preg_match('/dnserr/i', $data)) { + $status = "DNS-O-Matic ({$this->_dnsHost}): DNS error encountered. Stop updating for 30 minutes."; + } else { + $status = "DNS-O-Matic ({$this->_dnsHost}): (Unknown Response)"; + log_error("DNS-O-Matic ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'citynetwork': + if (preg_match('/notfqdn/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Not A FQDN!"; + } else if (preg_match('/nohost/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) No such host"; + } else if (preg_match('/nochg/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) No Change In IP Address"; + $successful_update = true; + } else if (preg_match('/good/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Changed Successfully! (" . $this->_dnsIP . ")"; + $successful_update = true; + } else if (preg_match('/badauth/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) User Authorization Failed"; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'ovh-dynhost': + case 'dyndns': + if (preg_match('/notfqdn/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Not A FQDN!"; + } else if (preg_match('/nochg/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) No Change In IP Address"; + $successful_update = true; + } else if (preg_match('/good/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Changed Successfully! (" . $this->_dnsIP . ")"; + $successful_update = true; + } else if (preg_match('/noauth/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) User Authorization Failed"; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'dyndns-static': + if (preg_match('/notfqdn/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Not A FQDN!"; + } else if (preg_match('/nochg/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) No Change In IP Address"; + $successful_update = true; + } else if (preg_match('/good/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Changed Successfully!"; + $successful_update = true; + } else if (preg_match('/noauth/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) User Authorization Failed"; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'dyndns-custom': + if (preg_match('/notfqdn/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Not A FQDN!"; + } else if (preg_match('/nochg/i', $data)) { + $status = "phpDynDNS: (Success) No Change In IP Address"; + $successful_update = true; + } else if (preg_match('/good/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Changed Successfully!"; + $successful_update = true; + } else if (preg_match('/noauth/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) User Authorization Failed"; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'dhs': + break; + case 'noip': + case 'noip-free': + list($ip, $code) = explode(":", $data); + switch ($code) { + case 0: + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP address is current, no update performed."; + $successful_update = true; + break; + case 1: + $status = "phpDynDNS ({$this->_dnsHost}): (Success) DNS hostname update successful."; + $successful_update = true; + break; + case 2: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Hostname supplied does not exist."; + break; + case 3: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Invalid Username."; + break; + case 4: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Invalid Password."; + break; + case 5: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) To many updates sent."; + break; + case 6: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Account disabled due to violation of No-IP terms of service."; + break; + case 7: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Invalid IP. IP Address submitted is improperly formatted or is a private IP address or is on a blacklist."; + break; + case 8: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Disabled / Locked Hostname."; + break; + case 9: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Host updated is configured as a web redirect and no update was performed."; + break; + case 10: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Group supplied does not exist."; + break; + case 11: + $status = "phpDynDNS ({$this->_dnsHost}): (Success) DNS group update is successful."; + $successful_update = true; + break; + case 12: + $status = "phpDynDNS ({$this->_dnsHost}): (Success) DNS group is current, no update performed."; + $successful_update = true; + break; + case 13: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Update client support not available for supplied hostname or group."; + break; + case 14: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Hostname supplied does not have offline settings configured."; + break; + case 99: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Client disabled. Client should exit and not perform any more updates without user intervention."; + break; + case 100: + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Client disabled. Client should exit and not perform any more updates without user intervention."; + break; + default: + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + $this->_debug("Unknown Response: ".$data); + break; + } + break; + case 'easydns': + if (preg_match('/NOACCESS/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Authentication Failed: Username and/or Password was Incorrect."; + } else if (preg_match('/NOSERVICE/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) No Service: Dynamic DNS Service has been disabled for this domain."; + } else if (preg_match('/ILLEGAL INPUT/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Illegal Input: Self-Explanatory"; + } else if (preg_match('/TOOSOON/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Too Soon: Not Enough Time Has Elapsed Since Last Update"; + } else if (preg_match('/NOERROR/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Updated Successfully!"; + $successful_update = true; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'hn': + /* FIXME: add checks */ + break; + case 'zoneedit': + if (preg_match('/799/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error 799) Update Failed!"; + } else if (preg_match('/700/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error 700) Update Failed!"; + } else if (preg_match('/200/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Updated Successfully!"; + $successful_update = true; + } else if (preg_match('/201/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Updated Successfully!"; + $successful_update = true; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'dyns': + if (preg_match("/400/i", $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Bad Request - The URL was malformed. Required parameters were not provided."; + } else if (preg_match('/402/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Update Too Soon - You have tried updating to quickly since last change."; + } else if (preg_match('/403/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Database Error - There was a server-sided database error."; + } else if (preg_match('/405/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Hostname Error - The hostname (".$this->_dnsHost.") doesn't belong to you."; + } else if (preg_match('/200/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Updated Successfully!"; + $successful_update = true; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'ods': + if (preg_match("/299/i", $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Updated Successfully!"; + $successful_update = true; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'freedns': + if (preg_match("/has not changed./i", $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) No Change In IP Address"; + $successful_update = true; + } else if (preg_match("/Updated/i", $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Changed Successfully!"; + $successful_update = true; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'dnsexit': + if (preg_match("/is the same/i", $data)) { + $status = "phpDynDns ({$this->_dnsHost}): (Success) No Change In IP Address"; + $successful_update = true; + } else if (preg_match("/Success/i", $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Changed Successfully!"; + $successful_update = true; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'loopia': + if (preg_match("/nochg/i", $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) No Change In IP Address"; + $successful_update = true; + } else if (preg_match("/good/i", $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Changed Successfully!"; + $successful_update = true; + } else if (preg_match('/badauth/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) User Authorization Failed"; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'opendns': + if (preg_match('/badauth/i', $data)) { + $status = "phpDynDNS({$this->_dnsHost}): (Error) Not a valid username or password!"; + } else if (preg_match('/nohost/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Hostname you are trying to update does not exist."; + $successful_update = true; + } else if (preg_match('/good/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Changed Successfully! (" . $this->_dnsIP . ")"; + $successful_update = true; + } else if (preg_match('/yours/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) hostname specified exists, but not under the username specified."; + } else if (preg_match('/abuse/i', $data)) { + $status = "phpDynDns ({$this->_dnsHost}): (Error) Updating too frequently, considered abuse."; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'staticcling': + if (preg_match("/invalid ip/i", $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Bad Request - The IP provided was invalid."; + } else if (preg_match('/required info missing/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Bad Request - Required parameters were not provided."; + } else if (preg_match('/invalid characters/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Bad Request - Illegal characters in either the username or the password."; + } else if (preg_match('/bad password/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Invalid password."; + } else if (preg_match('/account locked/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) This account has been administratively locked."; + } else if (preg_match('/update too frequent/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Updating too frequently."; + } else if (preg_match('/DB error/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Server side error."; + } else if (preg_match('/success/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Updated Successfully!"; + $successful_update = true; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'namecheap': + $tmp = str_replace("^M", "", $data); + $ncresponse = @xml2array($tmp); + if (preg_match("/internal server error/i", $data)) { + $status = "phpDynDNS: (Error) Server side error."; + } else if (preg_match("/request is badly formed/i", $data)) { + $status = "phpDynDNS: (Error) Badly Formed Request (check your settings)."; + } else if ($ncresponse['interface-response']['ErrCount'] === "0") { + $status = "phpDynDNS: (Success) IP Address Updated Successfully!"; + $successful_update = true; + } else if (is_numeric($ncresponse['interface-response']['ErrCount']) && ($ncresponse['interface-response']['ErrCount'] > 0)) { + $status = "phpDynDNS: (Error) " . implode(", ", $ncresponse["interface-response"]["errors"]); + $successful_update = true; + } else { + $status = "phpDynDNS: (Unknown Response)"; + log_error("phpDynDNS: PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + + case 'he-net': + case 'he-net-v6': + if (preg_match("/badip/i", $data)) { + $status = "phpDynDNS: (Error) Bad Request - The IP provided was invalid."; + } else if (preg_match('/nohost/i', $data)) { + $status = "phpDynDNS: (Error) Bad Request - A hostname was not provided."; + } else if (preg_match('/badauth/i', $data)) { + $status = "phpDynDNS: (Error) Invalid username or password."; + } else if (preg_match('/good/i', $data)) { + $status = "phpDynDNS: (Success) IP Address Updated Successfully!"; + $successful_update = true; + } else if (preg_match('/nochg/i', $data)) { + $status = "phpDynDNS: (Success) No Change In IP Address."; + $successful_update = true; + } else { + $status = "phpDynDNS: (Unknown Response)"; + log_error("phpDynDNS: PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'he-net-tunnelbroker': + /* + -ERROR: Missing parameter(s). + -ERROR: Invalid API key or password + -ERROR: Tunnel not found + -ERROR: Another tunnel exists for this IP. + -ERROR: This tunnel is already associated with this IP address + +OK: Tunnel endpoint updated to: x.x.x.x + */ + if (preg_match("/Missing parameter/i", $data)) { + $status = "phpDynDNS: (Error) Bad Request - Missing/Invalid Parameters."; + } else if (preg_match('/Tunnel not found/i', $data)) { + $status = "phpDynDNS: (Error) Bad Request - Invalid Tunnel ID."; + } else if (preg_match('/Invalid API key or password/i', $data)) { + $status = "phpDynDNS: (Error) Invalid username or password."; + } else if (preg_match('/OK:/i', $data)) { + $status = "phpDynDNS: (Success) IP Address Updated Successfully!"; + $successful_update = true; + } else if (preg_match('/This tunnel is already associated with this IP address/i', $data)) { + $status = "phpDynDNS: (Success) No Change In IP Address."; + $successful_update = true; + } else { + $status = "phpDynDNS: (Unknown Response)"; + log_error("phpDynDNS: PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'selfhost': + if (preg_match('/notfqdn/i', $data)) { + $status = "phpDynDNS: (Error) Not A FQDN!"; + } else if (preg_match('/nochg/i', $data)) { + $status = "phpDynDNS: (Success) No Change In IP Address"; + $successful_update = true; + } else if (preg_match('/good/i', $data)) { + $status = "phpDynDNS: (Success) IP Address Changed Successfully! (" . $this->_dnsIP . ")"; + $successful_update = true; + } else if (preg_match('/noauth/i', $data)) { + $status = "phpDynDNS: (Error) User Authorization Failed"; + } else { + $status = "phpDynDNS: (Unknown Response)"; + log_error("phpDynDNS: PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'route53': + $successful_update = true; + break; + case 'custom': + case 'custom-v6': + $successful_update = false; + if ($this->_dnsResultMatch == "") { + $successful_update = true; + } else { + $this->_dnsResultMatch = str_replace("%IP%", $this->_dnsIP, $this->_dnsResultMatch); + $matches = preg_split("/(?<!\\\\)\\|/", $this->_dnsResultMatch); + foreach ($matches as $match) { + $match= str_replace("\\|", "|", $match); + if (strcmp($match, trim($data, "\t\n\r")) == 0) { + $successful_update = true; + } + } + unset ($matches); + } + if ($successful_update == true) { + $status = "phpDynDNS: (Success) IP Address Updated Successfully!"; + } else { + $status = "phpDynDNS: (Error) Result did not match."; + } + break; + case 'cloudflare': + // receive multiple results + $data = explode("\n", $data); + $lines = count($data)-1; + + // loop over the lines + for ($pos = 0; ($successful_update || $pos == 0) && $pos < $lines; $pos++) { + $resp = $data[$pos]; + if (preg_match('/UAUTH/i', $resp)) { + $status = "DynDNS: The username specified is not authorized to update this hostname and domain."; + } else if (preg_match('/NOHOST/i', $resp)) { + $status = "DynDNS: No valid FQDN (fully qualified domain name) was specified"; + } else if (preg_match('/INVLDHST/i', $resp)) { + $status = "DynDNS: An invalid hostname was specified. This may be due to the fact the hostname has not been created in the system. Creating new host names via clients is not supported."; + } else if (preg_match('/INVLDIP/i', $resp)) { + $status = "DynDNS: The IP address given is not valid."; + } else if (preg_match('/DUPHST/i', $resp)) { + $status = "DynDNS: Duplicate values exist for a record. Only single values for records are supported currently."; + } else if (preg_match('/NOUPDATE/i', $resp)) { + $status = "DynDNS: No changes made to the hostname (" . strtok($resp, ' ') . "). Continual updates with no changes lead to blocked clients."; + $successful_update = true; //success if it is the same so that it saves + } else if (preg_match('/OK/i', $resp)) { + $status = "DynDNS: (Success) (" . strtok($resp, ' ') . ") IP Address for Changed Successfully!"; + $successful_update = true; + } else { + $status = "DynDNS: (Unknown Response)"; + log_error("DynDNS: PAYLOAD: {$resp}"); + $this->_debug($resp); + } + log_error($status); + } + break; + case 'eurodns': + if (preg_match('/notfqdn/i', $data)) { + $status = "phpDynDNS: (Error) Not A FQDN!"; + } else if (preg_match('/nochg/i', $data)) { + $status = "phpDynDNS: (Success) No Change In IP Address"; + $successful_update = true; + } else if (preg_match('/good/i', $data)) { + $status = "phpDynDNS: (Success) IP Address Changed Successfully! (" . $this->_dnsIP . ")"; + $successful_update = true; + } else if (preg_match('/badauth/i', $data)) { + $status = "phpDynDNS: (Error) User Authorization Failed"; + } else { + $status = "phpDynDNS: (Unknown Response)"; + log_error("phpDynDNS: PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'gratisdns': + if (preg_match('/Forkerte værdier/i', $data)) { + $status = "phpDynDNS: (Error) Wrong values - Update could not be completed."; + } else if (preg_match('/Bruger login: Bruger eksistere ikke/i', $data)) { + $status = "phpDynDNS: (Error) Unknown username - User does not exist."; + } else if (preg_match('/Bruger login: 1Fejl i kodeord/i', $data)) { + $status = "phpDynDNS: (Error) Wrong password - Remember password is case sensitive."; + } else if (preg_match('/Domæne kan IKKE administreres af bruger/i', $data)) { + $status = "phpDynDNS: (Error) User unable to administer the selected domain."; + } else if (preg_match('/OK/i', $data)) { + $status = "phpDynDNS: (Success) IP Address Updated Successfully!"; + $successful_update = true; + } else { + $status = "phpDynDNS: (Unknown Response)"; + log_error("phpDynDNS: PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'dnsimple': + /* Responds with HTTP 200 on success. + Responds with HTTP 4xx on error. + Returns JSON data as body */ + $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $header = substr($data, 0, $header_size); + $body = substr($data, $header_size); + if (preg_match("/Status: 200\s/i", $header)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Updated Successfully!"; + $successful_update = true; + } else if (preg_match("/Status: 4\d\d\s/i", $header)) { + $arrbody = json_decode($body, true); + $message = $arrbody['message'] . "."; + if (isset($arrbody['errors']['content'])) { + foreach ($arrbody['errors']['content'] as $key => $content) { + $message .= " " . $content . "."; + } + } + $status = "phpDynDNS ({$this->_dnsHost}): (Error) " . $message; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$body}"); + $this->_debug($body); + } + break; + case 'googledomains': + if (preg_match('/notfqdn/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Not A FQDN"; + } else if (preg_match('/nochg/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) No Change In IP Address"; + $successful_update = true; + } else if (preg_match('/good/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Success) IP Address Changed Successfully! (" . $this->_dnsIP . ")"; + $successful_update = true; + } else if (preg_match('/badauth/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) User Authorization Failed"; + } else if (preg_match('/nohost/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Hostname does not exist or DynDNS not enabled"; + } else if (preg_match('/badagent/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Bad request"; + } else if (preg_match('/abuse/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Dynamic DNS access has been blocked!"; + } else if (preg_match('/911/i', $data)) { + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Error on Google's end, retry in 5 minutes"; + } else { + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + } + break; + case 'dnsmadeeasy': + switch ($data) { + case 'success': + $status = "phpDynDNS({$this->_dnsHost}): (Success) IP Address Changed Successfully! (" . $this->_dnsIP . ")"; + $successful_update = true; + break; + case 'error-auth': + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Invalid username or password"; + break; + case 'error-auth-suspend': + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Account suspended"; + break; + case 'error-auth-voided': + $status = "phpDynDNS ({$this->_dnsHost}): (Error) Account revoked"; + break; + case 'error-record-invalid': + $status = "phpDynDns ({$this->_dnsHost}): (Error) Record does not exist in the system. Unable to update record"; + break; + case 'error-record-auth': + $status = "phpDynDns ({$this->_dnsHost}): (Error) User does not have access to this record"; + break; + case 'error-record-ip-same': + $status = "phpDynDns ({$this->_dnsHost}): (Success) No change in IP Address"; + $successful_update = true; + break; + case 'error-system': + $status = "phpDynDns ({$this->_dnsHost}): (Error) General system error recognized by the system"; + break; + case 'error': + $status = "phpDynDns ({$this->_dnsHost}): (Error) General system error unrecognized by the system"; + break; + default: + $status = "phpDynDNS ({$this->_dnsHost}): (Unknown Response)"; + log_error("phpDynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); + $this->_debug($data); + break; + } + break; + } + + if ($successful_update == true) { + /* Write WAN IP to cache file */ + $wan_ip = $this->_checkIP(); + conf_mount_rw(); + if ($this->_useIPv6 == false && $wan_ip > 0) { + $currentTime = time(); + notify_all_remote(sprintf(gettext("DynDNS updated IP Address on %s (%s) to %s"), convert_real_interface_to_friendly_descr($this->_if), $this->_if, $wan_ip)); + log_error("phpDynDNS: updating cache file {$this->_cacheFile}: {$wan_ip}"); + @file_put_contents($this->_cacheFile, "{$wan_ip}:{$currentTime}"); + } else { + @unlink($this->_cacheFile); + } + if ($this->_useIPv6 == true && $wan_ip > 0) { + $currentTime = time(); + notify_all_remote(sprintf(gettext("DynDNS updated IPv6 Address on %s (%s) to %s"), convert_real_interface_to_friendly_descr($this->_if), $this->_if, $wan_ip)); + log_error("phpDynDNS: updating cache file {$this->_cacheFile_v6}: {$wan_ip}"); + @file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}"); + } else { + @unlink($this->_cacheFile_v6); + } + conf_mount_ro(); + } + $this->status = $status; + log_error($status); + } + + /* + * Private Function (added 12 July 05) [beta] + * Return Error, Set Last Error, and Die. + */ + function _error($errorNumber = '1') { + switch ($errorNumber) { + case 0: + break; + case 2: + $error = 'phpDynDNS: (ERROR!) No Dynamic DNS Service provider was selected.'; + break; + case 3: + $error = 'phpDynDNS: (ERROR!) No Username Provided.'; + break; + case 4: + $error = 'phpDynDNS: (ERROR!) No Password Provided.'; + break; + case 5: + $error = 'phpDynDNS: (ERROR!) No Hostname Provided.'; + break; + case 6: + $error = 'phpDynDNS: (ERROR!) The Dynamic DNS Service provided is not yet supported.'; + break; + case 7: + $error = 'phpDynDNS: (ERROR!) No Update URL Provided.'; + break; + case 8: + $status = "Route 53: (Error) Invalid ZoneID"; + break; + case 9: + $status = "Route 53: (Error) Invalid TTL"; + break; + case 10: + $error = "phpDynDNS ({$this->_dnsHost}): No change in my IP address and/or " . $this->_dnsMaxCacheAgeDays . " days has not passed. Not updating dynamic DNS entry."; + break; + default: + $error = "phpDynDNS: (ERROR!) Unknown Response."; + /* FIXME: $data isn't in scope here */ + /* $this->_debug($data); */ + break; + } + $this->lastError = $error; + log_error($error); + } + + /* + * Private Function (added 12 July 05) [beta] + * - Detect whether or not IP needs to be updated. + * | Written Specifically for pfSense (https://www.pfsense.org) may + * | work with other systems. pfSense base is FreeBSD. + */ + function _detectChange() { + global $debug; + + if ($debug) { + log_error("DynDns ({$this->_dnsHost}): _detectChange() starting."); + } + + $currentTime = time(); + + $wan_ip = $this->_checkIP(); + if ($wan_ip == 0) { + log_error("DynDns ({$this->_dnsHost}): Current WAN IP could not be determined, skipping update process."); + return false; + } + $log_error = "DynDns ({$this->_dnsHost}): Current WAN IP: {$wan_ip} "; + + if ($this->_useIPv6 == true) { + if (file_exists($this->_cacheFile_v6)) { + $contents = file_get_contents($this->_cacheFile_v6); + list($cacheIP, $cacheTime) = explode('|', $contents); + $this->_debug($cacheIP.'/'.$cacheTime); + $initial = false; + $log_error .= "Cached IPv6: {$cacheIP} "; + } else { + conf_mount_rw(); + $cacheIP = '::'; + @file_put_contents($this->_cacheFile, "::|{$currentTime}"); + conf_mount_ro(); + $cacheTime = $currentTime; + $initial = true; + $log_error .= "No Cached IPv6 found."; + } + } else { + if (file_exists($this->_cacheFile)) { + $contents = file_get_contents($this->_cacheFile); + list($cacheIP, $cacheTime) = explode(':', $contents); + $this->_debug($cacheIP.'/'.$cacheTime); + $initial = false; + $log_error .= "Cached IP: {$cacheIP} "; + } else { + conf_mount_rw(); + $cacheIP = '0.0.0.0'; + @file_put_contents($this->_cacheFile, "0.0.0.0:{$currentTime}"); + conf_mount_ro(); + $cacheTime = $currentTime; + $initial = true; + $log_error .= "No Cached IP found."; + } + } + if ($this->_dnsVerboseLog) { + log_error($log_error); + } + + // Convert seconds = days * hr/day * min/hr * sec/min + $maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60; + + $needs_updating = FALSE; + /* lets determine if the item needs updating */ + if ($cacheIP != $wan_ip) { + $needs_updating = true; + $update_reason = "DynDns: cacheIP != wan_ip. Updating. "; + $update_reason .= "Cached IP: {$cacheIP} WAN IP: {$wan_ip} "; + } + if (($currentTime - $cacheTime) > $maxCacheAgeSecs) { + $needs_updating = true; + $this->_forceUpdateNeeded = true; + $update_reason = "DynDns: More than " . $this->_dnsMaxCacheAgeDays . " days. Updating. "; + $update_reason .= "{$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} "; + } + if ($initial == true) { + $needs_updating = true; + $update_reason .= "Initial update. "; + } + + /* finally if we need updating then store the + * new cache value and return true + */ + if ($needs_updating == true) { + if ($this->_dnsVerboseLog) { + log_error("DynDns ({$this->_dnsHost}): {$update_reason}"); + } + return true; + } + + return false; + } + + /* + * Private Function (added 16 July 05) [beta] + * - Writes debug information to a file. + * - This function is only called when a unknown response + * - status is returned from a DynDNS service provider. + */ + function _debug($data) { + global $g; + + if (!$g['debug']) { + return; + } + $string = date('m-d-y h:i:s').' - ('.$this->_debugID.') - ['.$this->_dnsService.'] - '.$data."\n"; + conf_mount_rw(); + $file = fopen($this->_debugFile, 'a'); + fwrite($file, $string); + fclose($file); + conf_mount_ro(); + } + function _checkIP() { + global $debug; + + if ($debug) { + log_error("DynDns ({$this->_dnsHost}): _checkIP() starting."); + } + + if ($this->_useIPv6 == true) { + $ip_address = find_interface_ipv6($this->_if); + if (!is_ipaddrv6($ip_address)) { + return 0; + } + } else { + $ip_address = find_interface_ip($this->_if); + if (!is_ipaddr($ip_address)) { + return 0; + } + } + if ($this->_useIPv6 == false && is_private_ip($ip_address)) { + $hosttocheck = "checkip.dyndns.org"; + $try = 0; + while ($try < 3) { + $checkip = gethostbyname($hosttocheck); + if (is_ipaddr($checkip)) { + break; + } + $try++; + } + if ($try >= 3) { + log_error("Dyndns debug information ({$this->_dnsHost}): Could not resolve {$hosttocheck} to IP using interface IP {$ip_address}."); + return 0; + } + $ip_ch = curl_init("http://{$checkip}"); + curl_setopt($ip_ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ip_ch, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_setopt($ip_ch, CURLOPT_INTERFACE, 'host!' . $ip_address); + curl_setopt($ip_ch, CURLOPT_CONNECTTIMEOUT, '30'); + curl_setopt($ip_ch, CURLOPT_TIMEOUT, 120); + if ($this->_useIPv6 == false) { + curl_setopt($ip_ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + } + $ip_result_page = curl_exec($ip_ch); + curl_close($ip_ch); + $ip_result_decoded = urldecode($ip_result_page); + preg_match('/Current IP Address: (.*)<\/body>/', $ip_result_decoded, $matches); + $ip_address = trim($matches[1]); + if (is_ipaddr($ip_address)) { + if ($this->_dnsVerboseLog) { + log_error("DynDns ({$this->_dnsHost}): {$ip_address} extracted from {$hosttocheck}"); + } + } else { + log_error("DynDns ({$this->_dnsHost}): IP address could not be extracted from {$hosttocheck}"); + return 0; + } + } else { + if ($this->_dnsVerboseLog) { + log_error("DynDns ({$this->_dnsHost}): {$ip_address} extracted from local system."); + } + } + $this->_dnsIP = $ip_address; + + return $ip_address; + } + + } + +?> diff --git a/src/etc/inc/easyrule.inc b/src/etc/inc/easyrule.inc new file mode 100644 index 0000000..c46e84d --- /dev/null +++ b/src/etc/inc/easyrule.inc @@ -0,0 +1,495 @@ +<?php +/* + easyrule.inc + + Copyright (C) 2009-2010 Jim Pingle (jpingle@gmail.com) + Originally Sponsored By Anathematic @ pfSense Forums + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: + pfSense_MODULE: filter +*/ + +$blockaliasname = 'EasyRuleBlockHosts'; +$protocols_with_ports = array('tcp', 'udp'); +require_once("functions.inc"); +require_once("util.inc"); +require_once("config.inc"); + +function easyrule_find_rule_interface($int) { + global $config; + /* Borrowed from firewall_rules.php */ + $iflist = get_configured_interface_with_descr(false, true); + + if ($config['pptpd']['mode'] == "server") { + $iflist['pptp'] = "PPTP VPN"; + } + + if ($config['pppoe']['mode'] == "server") { + $iflist['pppoe'] = "PPPoE Server"; + } + + if ($config['l2tp']['mode'] == "server") { + $iflist['l2tp'] = "L2TP VPN"; + } + + /* add ipsec interfaces */ + if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) { + $iflist["enc0"] = "IPSEC"; + } + + if (isset($iflist[$int])) { + return $int; + } + + foreach ($iflist as $if => $ifd) { + if (strtolower($int) == strtolower($ifd)) { + return $if; + } + } + + if (substr($int, 0, 4) == "ovpn") { + return "openvpn"; + } + + return false; +} + +function easyrule_block_rule_exists($int = 'wan', $ipproto = "inet") { + global $blockaliasname, $config; + /* No rules, we we know it doesn't exist */ + if (!is_array($config['filter']['rule'])) { + return false; + } + + /* Search through the rules for one referencing our alias */ + foreach ($config['filter']['rule'] as $rule) { + if (!is_array($rule) || !is_array($rule['source'])) { + continue; + } + $checkproto = isset($rule['ipprotocol']) ? $rule['ipprotocol'] : "inet"; + if ($rule['source']['address'] == $blockaliasname . strtoupper($int) && ($rule['interface'] == $int) && ($checkproto == $ipproto)) { + return true; + } + } + return false; +} + +function easyrule_block_rule_create($int = 'wan', $ipproto = "inet") { + global $blockaliasname, $config; + /* If the alias doesn't exist, exit. + * Can't create an empty alias, and we don't know a host */ + if (easyrule_block_alias_getid($int) === false) { + return false; + } + + /* If the rule already exists, no need to do it again */ + if (easyrule_block_rule_exists($int, $ipproto)) { + return true; + } + + /* No rules, start a new array */ + if (!is_array($config['filter']['rule'])) { + $config['filter']['rule'] = array(); + } + + filter_rules_sort(); + $a_filter = &$config['filter']['rule']; + + /* Make up a new rule */ + $filterent = array(); + $filterent['type'] = 'block'; + $filterent['interface'] = $int; + $filterent['ipprotocol'] = $ipproto; + $filterent['source']['address'] = $blockaliasname . strtoupper($int); + $filterent['destination']['any'] = ''; + $filterent['descr'] = gettext("Easy Rule: Blocked from Firewall Log View"); + $filterent['created'] = make_config_revision_entry(null, gettext("Easy Rule")); + + array_splice($a_filter, 0, 0, array($filterent)); + + return true; +} + +function easyrule_block_alias_getid($int = 'wan') { + global $blockaliasname, $config; + if (!is_array($config['aliases'])) { + return false; + } + + /* Hunt down an alias with the name we want, return its id */ + foreach ($config['aliases']['alias'] as $aliasid => $alias) { + if ($alias['name'] == $blockaliasname . strtoupper($int)) { + return $aliasid; + } + } + + return false; +} + +function easyrule_block_alias_add($host, $int = 'wan') { + global $blockaliasname, $config; + /* If the host isn't a valid IP address, bail */ + $host = trim($host, "[]"); + if (!is_ipaddr($host) && !is_subnet($host)) { + return false; + } + + /* If there are no aliases, start an array */ + if (!is_array($config['aliases']['alias'])) { + $config['aliases']['alias'] = array(); + } + + $a_aliases = &$config['aliases']['alias']; + + /* Try to get the ID if the alias already exists */ + $id = easyrule_block_alias_getid($int); + if ($id === false) { + unset($id); + } + + $alias = array(); + + if (is_subnet($host)) { + list($host, $mask) = explode("/", $host); + } elseif (is_specialnet($host)) { + $mask = 0; + } elseif (is_ipaddrv6($host)) { + $mask = 128; + } else { + $mask = 32; + } + + if (isset($id) && $a_aliases[$id]) { + + // Catch case when the list is empty + if (empty($a_aliases[$id]['address'])) { + $a_address = array(); + $a_detail = array(); + } else { + $a_address = explode(" ", $a_aliases[$id]['address']); + + /* Make sure this IP isn't already in the list. */ + if (in_array($host.'/'.$mask, $a_address)) { + return true; + } + $a_detail = explode("||", $a_aliases[$id]['detail']); + } + + /* Since the alias already exists, just add to it. */ + $alias['name'] = $a_aliases[$id]['name']; + $alias['type'] = $a_aliases[$id]['type']; + $alias['descr'] = $a_aliases[$id]['descr']; + + $a_address[] = $host.'/'.$mask; + $a_detail[] = gettext('Entry added') . ' ' . date('r'); + + $alias['address'] = join(" ", $a_address); + $alias['detail'] = join("||", $a_detail); + + } else { + /* Create a new alias with all the proper information */ + $alias['name'] = $blockaliasname . strtoupper($int); + $alias['type'] = 'network'; + $alias['descr'] = gettext("Hosts blocked from Firewall Log view"); + + $alias['address'] = $host . '/' . $mask; + $alias['detail'] = gettext('Entry added') . ' ' . date('r') . '||'; + } + + /* Replace the old alias if needed, otherwise tack it on the end */ + if (isset($id) && $a_aliases[$id]) { + $a_aliases[$id] = $alias; + } else { + $a_aliases[] = $alias; + } + + // Sort list + $a_aliases = msort($a_aliases, "name"); + + return true; +} + +function easyrule_block_host_add($host, $int = 'wan', $ipproto = "inet") { + global $retval; + /* Bail if the supplied host is not a valid IP address */ + $host = trim($host, "[]"); + if (!is_ipaddr($host) && !is_subnet($host)) { + return false; + } + + /* Flag whether or not we need to reload the filter */ + $dirty = false; + + /* Attempt to add this host to the alias */ + if (easyrule_block_alias_add($host, $int)) { + $dirty = true; + } else { + /* Couldn't add the alias, or adding the host failed. */ + return false; + } + + /* Attempt to add the firewall rule if it doesn't exist. + * Failing to add the rule isn't necessarily an error, it may + * have been modified by the user in some way. Adding to the + * Alias is what's important. + */ + if (!easyrule_block_rule_exists($int, $ipproto)) { + if (easyrule_block_rule_create($int, $ipproto)) { + $dirty = true; + } else { + return false; + } + } + + /* If needed, write the config and reload the filter */ + if ($dirty) { + write_config(); + $retval = filter_configure(); + if (!empty($_SERVER['DOCUMENT_ROOT'])) { + header("Location: firewall_aliases.php"); + exit; + } else { + return true; + } + } else { + return false; + } +} + +function easyrule_pass_rule_add($int, $proto, $srchost, $dsthost, $dstport, $ipproto) { + global $config; + + /* No rules, start a new array */ + if (!is_array($config['filter']['rule'])) { + $config['filter']['rule'] = array(); + } + + filter_rules_sort(); + $a_filter = &$config['filter']['rule']; + + /* Make up a new rule */ + $filterent = array(); + $filterent['type'] = 'pass'; + $filterent['interface'] = $int; + $filterent['ipprotocol'] = $ipproto; + $filterent['descr'] = gettext("Easy Rule: Passed from Firewall Log View"); + + if ($proto != "any") { + $filterent['protocol'] = $proto; + } else { + unset($filterent['protocol']); + } + + /* Default to only allow echo requests, since that's what most people want and + * it should be a safe choice. */ + if ($proto == "icmp") { + $filterent['icmptype'] = 'echoreq'; + } + + if ((strtolower($proto) == "icmp6") || (strtolower($proto) == "icmpv6")) { + $filterent['protocol'] = "icmp"; + } + + if (is_subnet($srchost)) { + list($srchost, $srcmask) = explode("/", $srchost); + } elseif (is_specialnet($srchost)) { + $srcmask = 0; + } elseif (is_ipaddrv6($srchost)) { + $srcmask = 128; + } else { + $srcmask = 32; + } + + if (is_subnet($dsthost)) { + list($dsthost, $dstmask) = explode("/", $dsthost); + } elseif (is_specialnet($dsthost)) { + $dstmask = 0; + } elseif (is_ipaddrv6($dsthost)) { + $dstmask = 128; + } else { + $dstmask = 32; + } + + pconfig_to_address($filterent['source'], $srchost, $srcmask); + pconfig_to_address($filterent['destination'], $dsthost, $dstmask, '', $dstport, $dstport); + + $filterent['created'] = make_config_revision_entry(null, gettext("Easy Rule")); + $a_filter[] = $filterent; + + write_config($filterent['descr']); + $retval = filter_configure(); + if (!empty($_SERVER['DOCUMENT_ROOT'])) { + header("Location: firewall_rules.php?if={$int}"); + exit; + } else { + return true; + } +} + +function easyrule_parse_block($int, $src, $ipproto = "inet") { + if (!empty($src) && !empty($int)) { + $src = trim($src, "[]"); + if (!is_ipaddr($src) && !is_subnet($src)) { + return gettext("Tried to block invalid IP:") . ' ' . htmlspecialchars($src); + } + $int = easyrule_find_rule_interface($int); + if ($int === false) { + return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int); + } + if (easyrule_block_host_add($src, $int, $ipproto)) { + return gettext("Host added successfully"); + } else { + return gettext("Failed to create block rule, alias, or add host."); + } + } else { + return gettext("Tried to block but had no host IP or interface"); + } + return gettext("Unknown block error."); +} + +function easyrule_parse_unblock($int, $host, $ipproto = "inet") { + global $blockaliasname, $config; + + if (!empty($host) && !empty($int)) { + $host = trim($host, "[]"); + if (!is_ipaddr($host) && !is_subnet($host)) { + return gettext("Tried to unblock invalid IP:") . ' ' . htmlspecialchars($host); + } + $real_int = easyrule_find_rule_interface($int); + if ($real_int === false) { + return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int); + } + + /* Try to get the ID - will fail if there are no rules/alias on this interface */ + $id = easyrule_block_alias_getid($real_int); + if ($id === false || !$config['aliases']['alias'][$id]) { + return gettext("No block rules set on interface:") . ' ' . htmlspecialchars($int); + } + + $alias = &$config['aliases']['alias'][$id]; + + if (is_subnet($host)) { + list($host, $mask) = explode("/", $host); + } elseif (is_specialnet($host)) { + $mask = 0; + } elseif (is_ipaddrv6($host)) { + $mask = 128; + } else { + $mask = 32; + } + + // Create the expected string representation + $unblock = $host.'/'.$mask; + + $a_address = explode(" ", $config['aliases']['alias'][$id]['address']); + $a_detail = explode("||", $config['aliases']['alias'][$id]['detail']); + + if (($key = array_search($unblock, $a_address)) !== false) { + unset($a_address[$key]); + unset($a_detail[$key]); + // Write back the result to the config array + $config['aliases']['alias'][$id]['address'] = join(" ", $a_address); + $config['aliases']['alias'][$id]['detail'] = join("||", $a_detail); + + // Update config + write_config(); + $retval = filter_configure(); + if (!empty($_SERVER['DOCUMENT_ROOT'])) { + header("Location: firewall_aliases.php"); + exit; + } else { + return gettext("Host unblocked successfully"); + } + } else { + return gettext("Host ist not on block list: " . $host); + } + } + + return gettext("Tried to unblock but had no host IP or interface"); + +} + +function easyrule_parse_getblock($int = 'wan', $sep = "\n") { + global $blockaliasname, $config; + + $real_int = easyrule_find_rule_interface($int); + if ($real_int === false) { + return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int); + } + + /* Try to get the ID - will fail if there are no rules/alias on this interface */ + $id = easyrule_block_alias_getid($real_int); + + if ($id === false || !$config['aliases']['alias'][$id] || empty($config['aliases']['alias'][$id]['address'])) { + return gettext("No block rules set on interface:") . ' ' . htmlspecialchars($int); + } + return join($sep, explode(" ", $config['aliases']['alias'][$id]['address'])); + +} + +function easyrule_parse_pass($int, $proto, $src, $dst, $dstport = 0, $ipproto = "inet") { + /* Check for valid int, srchost, dsthost, dstport, and proto */ + global $protocols_with_ports; + $src = trim($src, "[]"); + $dst = trim($dst, "[]"); + + if (!empty($int) && !empty($proto) && !empty($src) && !empty($dst)) { + $int = easyrule_find_rule_interface($int); + if ($int === false) { + return gettext("Invalid interface for pass rule:") . ' ' . htmlspecialchars($int); + } + if (getprotobyname($proto) == -1) { + return gettext("Invalid protocol for pass rule:") . ' ' . htmlspecialchars($proto); + } + if (!is_ipaddr($src) && !is_subnet($src) && !is_ipaddroralias($src) && !is_specialnet($src)) { + return gettext("Tried to pass invalid source IP:") . ' ' . htmlspecialchars($src); + } + if (!is_ipaddr($dst) && !is_subnet($dst) && !is_ipaddroralias($dst) && !is_specialnet($dst)) { + return gettext("Tried to pass invalid destination IP:") . ' ' . htmlspecialchars($dst); + } + if (in_array($proto, $protocols_with_ports)) { + if (empty($dstport)) { + return gettext("Missing destination port:") . ' ' . htmlspecialchars($dstport); + } + if (!is_port($dstport) && ($dstport != "any")) { + return gettext("Tried to pass invalid destination port:") . ' ' . htmlspecialchars($dstport); + } + } else { + $dstport = 0; + } + /* Should have valid input... */ + if (easyrule_pass_rule_add($int, $proto, $src, $dst, $dstport, $ipproto)) { + return gettext("Successfully added pass rule!"); + } else { + return gettext("Failed to add pass rule."); + } + } else { + return gettext("Missing parameters for pass rule."); + } + return gettext("Unknown pass error."); +} + +?> diff --git a/src/etc/inc/filter.inc b/src/etc/inc/filter.inc new file mode 100644 index 0000000..36bbe2b --- /dev/null +++ b/src/etc/inc/filter.inc @@ -0,0 +1,4228 @@ +<?php +/* $Id$ */ +/* + filter.inc + Copyright (C) 2004-2006 Scott Ullrich + Copyright (C) 2005 Bill Marquette + Copyright (C) 2006 Peter Allgeyer + Copyright (C) 2008-2010 Ermal Luçi + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_BUILDER_BINARIES: /sbin/kldload /usr/sbin/tcpdump /sbin/pfctl /bin/rm + pfSense_BUILDER_BINARIES: /usr/sbin/inetd + pfSense_MODULE: filter +*/ + + +/* holds the items that will be executed *AFTER* the filter is fully loaded */ +$after_filter_configure_run = array(); + +/* For installing cron job of schedules */ +$time_based_rules = false; + +/* Used to hold the interface list that will be used on ruleset creation. */ +$FilterIflist = array(); + +/* Create a global array to avoid errors on rulesets. */ +$GatewaysList = array(); + +/* Used for the hostname dns resolver */ +$filterdns = array(); + +/* Used for aliases and interface macros */ +$aliases = ""; + +/* ICMP v4 types */ +$icmptypes = array( + "" => gettext("any"), + "echoreq" => gettext("Echo request"), + "echorep" => gettext("Echo reply"), + "unreach" => gettext("Destination unreachable"), + "squench" => gettext("Source quench"), + "redir" => gettext("Redirect"), + "althost" => gettext("Alternate Host"), + "routeradv" => gettext("Router advertisement"), + "routersol" => gettext("Router solicitation"), + "timex" => gettext("Time exceeded"), + "paramprob" => gettext("Invalid IP header"), + "timereq" => gettext("Timestamp"), + "timerep" => gettext("Timestamp reply"), + "inforeq" => gettext("Information request"), + "inforep" => gettext("Information reply"), + "maskreq" => gettext("Address mask request"), + "maskrep" => gettext("Address mask reply"), + "trace" => gettext("Traceroute"), + "dataconv" => gettext("Datagram conversion error"), + "mobredir" => gettext("Mobile host redirect"), + "ipv6-where" => gettext("IPv6 where-are-you"), + "ipv6-here" => gettext("IPv6 I-am-here"), + "mobregreq" => gettext("Mobile registration request"), + "mobregrep" => gettext("Mobile registration reply"), + "skip" => gettext("SKIP"), + "photuris" => gettext("Photuris") +); + +/* ICMP v6 types */ +$icmp6types = array( + "" => gettext("any"), + "unreach" => gettext("Destination unreachable"), + "toobig" => gettext("Packet too big"), + "timex" => gettext("Time exceeded"), + "paramprob" => gettext("Parameter problem"), + "echoreq" => gettext("Echo request"), + "echorep" => gettext("Echo reply"), + "groupqry" => gettext("Group membership query"), + "listqry" => gettext("Multicast listener query"), + "grouprep" => gettext("Group membership report"), + "listenrep" => gettext("Multicast listener report"), + "groupterm" => gettext("Group membership termination"), + "listendone" => gettext("Multicast listener done"), + "routersol" => gettext("Router solicitation"), + "routeradv" => gettext("Router advertisement"), + "neighbrsol" => gettext("Neighbor solicitation"), + "neighbradv" => gettext("Neighbor advertisement"), + "redir" => gettext("Redirect"), + "routrrenum" => gettext("Router renumbering"), + "wrureq" => gettext("Who are you request"), + "wrurep" => gettext("Who are you reply"), + "fqdnreq" => gettext("FQDN query"), + "fqdnrep" => gettext("FQDN reply"), + "niqry" => gettext("Node information request"), + "nirep" => gettext("Node information reply"), + "mtraceresp" => gettext("mtrace resp"), + "mtrace" => gettext("mtrace messages") +); + +global $tracker; +global $negate_tracker; +$tracker = 1000000000; +$negate_tracker = 10000000; + +function filter_rule_tracker($tracker) { + global $tracker; + + return (++$tracker); +} + +function filter_negaterule_tracker() { + global $negate_tracker; + + ++$negate_tracker; + return "tracker {$negate_tracker} "; +} + +function fix_rule_label($descr) { + $descr = str_replace('"', '', $descr); + if (strlen($descr) > 63) { + return substr($descr, 0, 60) . "..."; + } else { + return $descr; + } +} + +function is_bogonsv6_used() { + global $config, $g; + # Only use bogonsv6 table if IPv6 Allow is on, and at least 1 enabled interface also has "blockbogons" enabled. + $usebogonsv6 = false; + if (isset($config['system']['ipv6allow'])) { + foreach ($config['interfaces'] as $ifacedata) { + if (isset($ifacedata['enable']) && isset($ifacedata['blockbogons'])) { + $usebogonsv6 = true; + break; + } + } + } + return $usebogonsv6; +} + +function filter_pflog_start($kill_first = false) { + global $config, $g; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_pflog_start() being called $mt\n"; + } + if ((!file_exists("{$g['varrun_path']}/filterlog.pid")) || + (!isvalidpid("{$g['varrun_path']}/filterlog.pid"))) { + mwexec("/usr/local/sbin/filterlog -i pflog0 -p {$g['varrun_path']}/filterlog.pid"); + } +} + +/* reload filter async */ +function filter_configure() { + global $g; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_configure() being called $mt\n"; + } + + /* + * NOTE: Check here for bootup status since this should not be triggered during bootup. + * The reason is that rc.bootup calls filter_configure_sync directly which does this too. + */ + if (!platform_booting()) { + send_event("filter reload"); + } +} + +function filter_delete_states_for_down_gateways() { + global $config, $GatewaysList; + + if (isset($config['system']['kill_states'])) { + return; + } + + $any_gateway_down = false; + $a_gateways = return_gateways_status(); + if (is_array($GatewaysList)) { + foreach ($GatewaysList as $gwname => $gateway) { + if (empty($gateway['monitor'])) { + continue; + } + if (!is_ipaddr($gateway['monitor'])) { + continue; + } + if (strstr($gateway['monitor'], "127.0.0.")) { + continue; + } + if (empty($a_gateways[$gateway['monitor']])) { + continue; + } + $gwstatus =& $a_gateways[$gateway['monitor']]; + if (strstr($gwstatus['status'], "down")) { + $any_gateway_down = true; + break; + } + } + } + if ($any_gateway_down == true) { + mwexec("/sbin/pfctl -Fs"); + } +} + +/* reload filter sync */ +function filter_configure_sync($delete_states_if_needed = true) { + global $config, $g, $after_filter_configure_run, $FilterIflist; + global $time_based_rules, $filterdns, $aliases, $dummynet_name_list; + + /* Use filter lock to not allow concurrent filter reloads during this run. */ + $filterlck = lock('filter', LOCK_EX); + + filter_pflog_start(); + update_filter_reload_status(gettext("Initializing")); + + /* invalidate interface cache */ + get_interface_arr(true); + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_configure_sync() being called $mt\n"; + } + /* Get interface list to work with. */ + filter_generate_optcfg_array(); + if (platform_booting() == true) { + echo gettext("Configuring firewall"); + } + + /* generate aliases */ + if (platform_booting() == true) { + echo "."; + } + update_filter_reload_status(gettext("Creating aliases")); + $aliases = filter_generate_aliases(); + $gateways = filter_generate_gateways(); + if (platform_booting() == true) { + echo "."; + } + update_filter_reload_status(gettext("Generating Limiter rules")); + $dummynet_rules = filter_generate_dummynet_rules(); + $dummynet_name_list = get_unique_dnqueue_list(); + update_filter_reload_status(gettext("Generating NAT rules")); + /* generate nat rules */ + $natrules = filter_nat_rules_generate(); + if (platform_booting() == true) { + echo "."; + } + update_filter_reload_status(gettext("Generating filter rules")); + /* generate pfctl rules */ + $pfrules = filter_rules_generate(); + /* generate altq, limiter */ + if (platform_booting() == true) { + echo "."; + } + update_filter_reload_status(gettext("Generating ALTQ queues")); + $altq_queues = filter_generate_altq_queues(); + update_filter_reload_status(gettext("Generating Layer7 rules")); + generate_layer7_files(); + if (platform_booting() == true) { + echo "."; + } + update_filter_reload_status(gettext("Loading filter rules")); + /* enable pf if we need to, otherwise disable */ + if (!isset ($config['system']['disablefilter'])) { + mwexec("/sbin/pfctl -e", true); + } else { + mwexec("/sbin/pfctl -d", true); + unlink_if_exists("{$g['tmp_path']}/filter_loading"); + update_filter_reload_status(gettext("Filter is disabled. Not loading rules.")); + if (platform_booting() == true) { + echo gettext("done.") . "\n"; + } + unlock($filterlck); + return; + } + + $limitrules = ""; + /* User defined maximum table entries in Advanced menu. */ + if ($config['system']['maximumtableentries'] <> "" && is_numeric($config['system']['maximumtableentries'])) { + $limitrules .= "set limit table-entries {$config['system']['maximumtableentries']}\n"; + } + + if ($config['system']['optimization'] <> "") { + $limitrules .= "set optimization {$config['system']['optimization']}\n"; + if ($config['system']['optimization'] == "conservative") { + $limitrules .= "set timeout { udp.first 300, udp.single 150, udp.multiple 900 }\n"; + } + } else { + $limitrules .= "set optimization normal\n"; + } + + $timeoutlist = ""; + if (isset($config['system']['tcpfirsttimeout']) && is_numericint($config['system']['tcpfirsttimeout'])) { + $timeoutlist .= " tcp.first {$config['system']['tcpfirsttimeout']} "; + } + if (isset($config['system']['tcpopeningtimeout']) && is_numericint($config['system']['tcpopeningtimeout'])) { + $timeoutlist .= " tcp.opening {$config['system']['tcpopeningtimeout']} "; + } + if (isset($config['system']['tcpestablishedtimeout']) && is_numericint($config['system']['tcpestablishedtimeout'])) { + $timeoutlist .= " tcp.established {$config['system']['tcpestablishedtimeout']} "; + } + if (isset($config['system']['tcpclosingtimeout']) && is_numericint($config['system']['tcpclosingtimeout'])) { + $timeoutlist .= " tcp.closing {$config['system']['tcpclosingtimeout']} "; + } + if (isset($config['system']['tcpfinwaittimeout']) && is_numericint($config['system']['tcpfinwaittimeout'])) { + $timeoutlist .= " tcp.finwait {$config['system']['tcpfinwaittimeout']} "; + } + if (isset($config['system']['tcpclosedtimeout']) && is_numericint($config['system']['tcpclosedtimeout'])) { + $timeoutlist .= " tcp.closed {$config['system']['tcpclosedtimeout']} "; + } + if (isset($config['system']['udpfirsttimeout']) && is_numericint($config['system']['udpfirsttimeout'])) { + $timeoutlist .= " udp.first {$config['system']['udpfirsttimeout']} "; + } + if (isset($config['system']['udpsingletimeout']) && is_numericint($config['system']['udpsingletimeout'])) { + $timeoutlist .= " udp.single {$config['system']['udpsingletimeout']} "; + } + if (isset($config['system']['udpmultipletimeout']) && is_numericint($config['system']['udpmultipletimeout'])) { + $timeoutlist .= " udp.multiple {$config['system']['udpmultipletimeout']} "; + } + if (isset($config['system']['icmpfirsttimeout']) && is_numericint($config['system']['icmpfirsttimeout'])) { + $timeoutlist .= " icmp.first {$config['system']['icmpfirsttimeout']} "; + } + if (isset($config['system']['icmperrortimeout']) && is_numericint($config['system']['icmperrortimeout'])) { + $timeoutlist .= " icmp.error {$config['system']['icmperrortimeout']} "; + } + if (isset($config['system']['otherfirsttimeout']) && is_numericint($config['system']['otherfirsttimeout'])) { + $timeoutlist .= " other.first {$config['system']['otherfirsttimeout']} "; + } + if (isset($config['system']['othersingletimeout']) && is_numericint($config['system']['othersingletimeout'])) { + $timeoutlist .= " other.single {$config['system']['othersingletimeout']} "; + } + if (isset($config['system']['othermultipletimeout']) && is_numericint($config['system']['othermultipletimeout'])) { + $timeoutlist .= " other.multiple {$config['system']['othermultipletimeout']} "; + } + + if ($timeoutlist <> "") { + $limitrules .= "set timeout { $timeoutlist }\n"; + } + + if (!empty($config['system']['adaptivestart']) && !empty($config['system']['adaptiveend'])) { + $limitrules .= "set timeout { adaptive.start {$config['system']['adaptivestart']}, adaptive.end {$config['system']['adaptiveend']} }\n"; + } + + if ($config['system']['maximumstates'] <> "" && is_numeric($config['system']['maximumstates'])) { + /* User defined maximum states in Advanced menu. */ + $limitrules .= "set limit states {$config['system']['maximumstates']}\n"; + $limitrules .= "set limit src-nodes {$config['system']['maximumstates']}\n"; + } else { + $max_states = pfsense_default_state_size(); + $limitrules .= "set limit states {$max_states}\n"; + $limitrules .= "set limit src-nodes {$max_states}\n"; + } + + /* Frag limit. pf default is 5000 */ + if ($config['system']['maximumfrags'] <> "" && is_numeric($config['system']['maximumfrags'])) { + $limitrules .= "set limit frags {$config['system']['maximumfrags']}\n"; + } + + if (isset($config['system']['lb_use_sticky']) && is_numeric($config['system']['srctrack']) && ($config['system']['srctrack'] > 0)) { + $limitrules .= "set timeout src.track {$config['system']['srctrack']}\n"; + } + + $rules = ""; + $rules = "{$limitrules}\n"; + $rules .= "{$aliases} \n"; + $rules .= "{$gateways} \n"; + update_filter_reload_status(gettext("Setting up logging information")); + $rules .= filter_setup_logging_interfaces(); + $rules .= "\n"; + $rules .= "set skip on pfsync0\n"; + $rules .= "\n"; + update_filter_reload_status(gettext("Setting up SCRUB information")); + $rules .= filter_generate_scrubing(); + $rules .= "\n"; + $rules .= "{$altq_queues}\n"; + $rules .= "{$natrules}\n"; + $rules .= "{$pfrules}\n"; + $rules .= discover_pkg_rules("filter"); + + unset($aliases, $gateways, $altq_queues, $natrules, $pfrules); + + // Copy rules.debug to rules.debug.old + if (file_exists("{$g['tmp_path']}/rules.debug")) { + @copy("{$g['tmp_path']}/rules.debug", "{$g['tmp_path']}/rules.debug.old"); + } + + if (!@file_put_contents("{$g['tmp_path']}/rules.debug", $rules, LOCK_EX)) { + log_error("WARNING: Could not write new rules!"); + unlock($filterlck); + return; + } + + @file_put_contents("{$g['tmp_path']}/rules.limits", $limitrules); + mwexec("/sbin/pfctl -Of {$g['tmp_path']}/rules.limits"); + unset($rules, $limitrules); + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "pfctl being called at $mt\n"; + } + unset($rules_loading, $rules_error); + $_grbg = exec("/sbin/pfctl -o basic -f {$g['tmp_path']}/rules.debug 2>&1", $rules_error, $rules_loading); + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "pfctl done at $mt\n"; + } + /* + * check for a error while loading the rules file. if an error has occurred + * then output the contents of the error to the caller + */ + if ($rules_loading <> 0) { + $saved_line_error = $rules_error[0]; + $line_error = explode(":", $rules_error[0]); + $line_number = $line_error[1]; + $line_split = file("{$g['tmp_path']}/rules.debug"); + if (is_array($line_split)) { + $line_error = sprintf(gettext('The line in question reads [%1$d]: %2$s'), $line_number, $line_split[$line_number-1]); + } + unset($line_split); + + /* Brutal ugly hack but required -- PF is stuck, unwedge */ + if (strstr("$rules_error[0]", "busy")) { + exec("/sbin/pfctl -d; /sbin/pfctl -e; /sbin/pfctl -f {$g['tmp_path']}/rules.debug"); + $error_msg = gettext("PF was wedged/busy and has been reset."); + file_notice("pf_busy", $error_msg, "pf_busy", ""); + } else { + $_grbg = exec("/sbin/pfctl -o basic -f {$g['tmp_path']}/rules.debug.old 2>&1"); + } + unset($rules_loading, $rules_error); + + if ($line_error and $line_number) { + file_notice("filter_load", sprintf(gettext('There were error(s) loading the rules: %1$s - %2$s'), $saved_line_error, $line_error), "Filter Reload", ""); + update_filter_reload_status(sprintf(gettext('There were error(s) loading the rules: %1$s - %2$s'), $saved_line_error, $line_error)); + unlock($filterlck); + return; + } + } + + # If we are not using bogonsv6 then we can remove any bogonsv6 table from the running pf (if the table is not there, the kill is still fine). + if (!is_bogonsv6_used()) { + $_grbg = exec("/sbin/pfctl -t bogonsv6 -T kill 2>/dev/null"); + } + + update_filter_reload_status(gettext("Starting up layer7 daemon")); + layer7_start_l7daemon(); + + if (!platform_booting()) { + if (!empty($filterdns)) { + @file_put_contents("{$g['varetc_path']}/filterdns.conf", implode("", $filterdns)); + unset($filterdns); + if (isvalidpid("{$g['varrun_path']}/filterdns.pid")) { + sigkillbypid("{$g['varrun_path']}/filterdns.pid", "HUP"); + } else { + /* + * FilterDNS has three debugging levels. The default chosen is 1. + * Available are level 2 and greater then 2. + */ + if (isset($config['system']['aliasesresolveinterval']) && is_numeric($config['system']['aliasesresolveinterval'])) { + $resolve_interval = $config['system']['aliasesresolveinterval']; + } else { + $resolve_interval = 300; + } + mwexec("/usr/local/sbin/filterdns -p {$g['varrun_path']}/filterdns.pid -i {$resolve_interval} -c {$g['varetc_path']}/filterdns.conf -d 1"); + } + } else { + killbypid("{$g['varrun_path']}/filterdns.pid"); + @unlink("{$g['varrun_path']}/filterdns.pid"); + } + } + + /* run items scheduled for after filter configure run */ + $fda = fopen("{$g['tmp_path']}/commands.txt", "w"); + if ($fda) { + if ($after_filter_configure_run) { + foreach ($after_filter_configure_run as $afcr) { + fwrite($fda, $afcr . "\n"); + } + unset($after_filter_configure_run); + } + + /* + * we need a way to let a user run a shell cmd after each + * filter_configure() call. run this xml command after + * each change. + */ + if ($config['system']['afterfilterchangeshellcmd'] <> "") { + fwrite($fda, $config['system']['afterfilterchangeshellcmd'] . "\n"); + } + + fclose($fda); + } + + if (file_exists("{$g['tmp_path']}/commands.txt")) { + mwexec("sh {$g['tmp_path']}/commands.txt &"); + unlink("{$g['tmp_path']}/commands.txt"); + } + + /* if time based rules are enabled then swap in the set */ + if ($time_based_rules == true) { + filter_tdr_install_cron(true); + } else { + filter_tdr_install_cron(false); + } + + if (platform_booting() == true) { + echo "."; + } + + if ($delete_states_if_needed) { + update_filter_reload_status(gettext("Processing down interface states")); + filter_delete_states_for_down_gateways(); + } + + update_filter_reload_status(gettext("Running plugins")); + + if (is_dir("/usr/local/pkg/pf/")) { + /* process packager manager custom rules */ + update_filter_reload_status(gettext("Running plugins (pf)")); + run_plugins("/usr/local/pkg/pf/"); + update_filter_reload_status(gettext("Plugins completed.")); + } + + update_filter_reload_status(gettext("Done")); + if (platform_booting() == true) { + echo gettext("done.") . "\n"; + } + + unlock($filterlck); + return 0; +} + +function filter_generate_scrubing() { + global $config, $FilterIflist; + $scrubrules = ""; + + if (isset($config['system']['maxmss_enable'])) { + $maxmss = 1400; + if (!empty($config['system']['maxmss'])) { + $maxmss = $config['system']['maxmss']; + } + + $scrubrules .= "scrub from any to <vpn_networks> max-mss {$maxmss}\n"; + $scrubrules .= "scrub from <vpn_networks> to any max-mss {$maxmss}\n"; + } + /* disable scrub option */ + foreach ($FilterIflist as $scrubif => $scrubcfg) { + if (isset($scrubcfg['virtual']) || empty($scrubcfg['descr'])) { + continue; + } + /* set up MSS clamping */ + if (($scrubcfg['mss'] <> "") && + (is_numeric($scrubcfg['mss'])) && + ($scrubcfg['if'] != "pppoe") && + ($scrubcfg['if'] != "pptp") && + ($scrubif['if'] != "l2tp")) { + $mssclamp = "max-mss " . (intval($scrubcfg['mss'] - 40)); + } else { + $mssclamp = ""; + } + /* configure no-df for linux nfs and others */ + if ($config['system']['scrubnodf']) { + $scrubnodf = "no-df"; + } else { + $scrubnodf = ""; + } + if ($config['system']['scrubrnid']) { + $scrubrnid = "random-id"; + } else { + $scrubrnid = ""; + } + if (!isset($config['system']['disablescrub'])) { + $scrubrules .= "scrub on \${$scrubcfg['descr']} all {$scrubnodf} {$scrubrnid} {$mssclamp} fragment reassemble\n"; // reassemble all directions + } else if (!empty($mssclamp)) { + $scrubrules .= "scrub on \${$scrubcfg['descr']} {$mssclamp}\n"; + } + } + return $scrubrules; +} + +function filter_generate_nested_alias($name, $alias, &$aliasnesting, &$aliasaddrnesting) { + global $aliastable, $filterdns; + + $addresses = explode(" ", $alias); + $use_filterdns = false; + $finallist = ""; + $builtlist = ""; + $urltable_nesting = ""; + $aliasnesting[$name] = $name; + $alias_type = alias_get_type($name); + foreach ($addresses as $address) { + if (empty($address)) { + continue; + } + $linelength = strlen($builtlist); + $tmpline = ""; + if (is_alias($address)) { + if (alias_get_type($address) == 'urltable') { + // Feature#1603. For this type of alias we do not need to recursively call filter_generate_nested_alias. Just load IPs from the file. + $urltable_nesting = alias_expand_urltable($address); + if (!empty($urltable_nesting)) { + $urlfile_as_arr = file($urltable_nesting); + foreach ($urlfile_as_arr as $line) { + $address= rtrim($line); + if ((strlen($tmpline) + $linelength) > 4036) { + $finallist .= "{$tmpline} \\\n"; + $tmpline = ""; + } + $tmpline .= " {$address}"; + } + } + } + /* We already expanded this alias so there is no necessity to do it again. */ + else if (!isset($aliasnesting[$address])) { + $tmpline = filter_generate_nested_alias($name, $aliastable[$address], $aliasnesting, $aliasaddrnesting); + } + } else if (!isset($aliasaddrnesting[$address])) { + if (!is_ipaddr($address) && !is_subnet($address) && !((($alias_type == 'port') || ($alias_type == 'url_ports')) && (is_port($address) || is_portrange($address))) && is_hostname($address)) { + if (!isset($filterdns["{$address}{$name}"])) { + $use_filterdns = true; + $filterdns["{$address}{$name}"] = "pf {$address} {$name}\n"; + } + continue; + } + $aliasaddrnesting[$address] = $address; + $tmpline = " {$address}"; + } + if ((strlen($tmpline)+ $linelength) > 4036) { + $finallist .= "{$builtlist} \\\n"; + $builtlist = ""; + } + if (!empty($tmpline)) { + $builtlist .= " {$tmpline}"; + } + } + $finallist .= $builtlist; + + if ($use_filterdns === true && !empty($finallist)) { + foreach (explode(" ", $finallist) as $address) { + if (empty($address)) { + continue; + } + if ((is_ipaddr($address) || is_subnet($address)) && !isset($filterdns["{$address}{$name}"])) { + $filterdns["{$address}{$name}"] = "pf {$address} {$name}\n"; + } + } + $finallist = ''; + } + + return $finallist; +} + +function filter_expand_alias($alias_name) { + global $config; + + if (isset($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $aliased) { + if ($aliased['name'] == $alias_name) { + $aliasnesting = array(); + $aliasaddrnesting = array(); + return filter_generate_nested_alias($aliased['name'], $aliased['address'], $aliasnesting, $aliasaddrnesting); + } + } + } +} + +function filter_expand_alias_array($alias_name) { + $expansion = filter_expand_alias($alias_name); + return explode(" ", preg_replace('/\s+/', ' ', trim($expansion))); +} + +function filter_generate_aliases() { + global $config, $FilterIflist, $after_filter_configure_run; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_generate_aliases() being called $mt\n"; + } + + $alias = "#System aliases\n "; + $aliases = "loopback = \"{ lo0 }\"\n"; + + foreach ($FilterIflist as $if => $ifcfg) { + if (is_array($ifcfg[0])) { + if ($ifcfg[0]['if'] == 'pppoe') { + $aliases .= "{$ifcfg[0]['descr']} = \"{ {$ifcfg[0]['if']}"; + $aliases .= " }\"\n"; + } + } elseif (!empty($ifcfg['descr']) && !empty($ifcfg['if'])) { + if ($ifcfg['type6'] == '6rd') { + $aliases .= "{$ifcfg['descr']} = \"{ {$ifcfg['if']} {$if}_stf"; + } else if ($ifcfg['type6'] == '6to4') { + $aliases .= "{$ifcfg['descr']} = \"{ {$ifcfg['if']} {$if}_stf"; + } else { + $aliases .= "{$ifcfg['descr']} = \"{ {$ifcfg['if']}"; + + if ($ifcfg['type'] == 'pptp') { + foreach (get_parent_interface($ifcfg['if']) as $parent_if) { + if ($parent_if != $ifcfg['if']) { + $aliases .= " {$parent_if}"; + } + } + } + } + $aliases .= " }\"\n"; + } + } + + $aliases .= "\n#SSH Lockout Table\n"; + $aliases .= "table <sshlockout> persist\n"; + $aliases .= "table <webConfiguratorlockout> persist\n"; + + $aliases .= "#Snort tables\n"; + $aliases .= "table <snort2c>\n"; + $aliases .= "table <virusprot>\n"; + if (!file_exists("/etc/bogons") || !file_exists("/etc/bogonsv6")) { + conf_mount_rw(); + if (!file_exists("/etc/bogons")) { + @file_put_contents("/etc/bogons", ""); + } + if (!file_exists("/etc/bogonsv6")) { + @file_put_contents("/etc/bogonsv6", ""); + } + conf_mount_ro(); + } + $aliases .= "table <bogons> persist file \"/etc/bogons\"\n"; + if (is_bogonsv6_used()) { + $aliases .= "table <bogonsv6> persist file \"/etc/bogonsv6\"\n"; + } + + $vpns_list = filter_get_vpns_list(); + if ($vpns_list) { + $aliases .= "table <vpn_networks> { $vpns_list }\n"; + } + + /* add a Negate_networks table */ + $aliases .= "table <negate_networks> "; + if ($vpns_list) { + $aliases .= "{ $vpns_list }"; + } + $aliases .= "\n"; + + $aliases .= "\n# User Aliases \n"; + /* Setup pf groups */ + if (isset($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $aliased) { + $extralias = ""; + $aliasnesting = array(); + $aliasaddrnesting = array(); + if (is_numericint($aliased['name'])) { + // skip aliases with numeric-only names. redmine #4289 + file_notice("Filter_Reload", "Aliases with numeric-only names are not valid. Skipping alias " . $aliased['name']); + continue; + } + $addrlist = filter_generate_nested_alias($aliased['name'], $aliased['address'], $aliasnesting, $aliasaddrnesting); + switch ($aliased['type']) { + case "host": + case "network": + case "url": + $tableaddrs = "{$addrlist}{$extralias}"; + if (empty($tableaddrs)) { + $aliases .= "table <{$aliased['name']}> persist\n"; + if (empty($aliased['address'])) { + $after_filter_configure_run[] = "/sbin/pfctl -T flush -t " . escapeshellarg($aliased['name']); + } + } else { + $aliases .= "table <{$aliased['name']}> { {$addrlist}{$extralias} } \n"; + } + + $aliases .= "{$aliased['name']} = \"<{$aliased['name']}>\"\n"; + break; + case "openvpn": + $openvpncfg = array(); + if ($config['openvpn']['user']) { + /* XXX: Check if we have a correct ip? */ + foreach ($config['openvpn']['user'] as $openvpn) { + $openvpncfg[$openvpn['name']] = $openvpn['ip']; + } + } + $vpn_lines = explode("\n", $addrlist); + foreach ($vpn_lines as $vpn_line) { + $vpn_address_split = explode(" ", $vpn_line); + foreach ($vpn_address_split as $vpnsplit) { + if (isset($openvpncfg[$vpnsplit])) { + $newaddress .= " "; + $newaddress .= $openvpn[$vpnsplit]; + break; + } + } + } + $aliases .= "table <{$aliased['name']}> { {$newaddress}{$extralias} } \n"; + $aliases .= "{$aliased['name']} = \"<{$aliased['name']}>\"\n"; + break; + case "urltable": + $urlfn = alias_expand_urltable($aliased['name']); + if ($urlfn) { + $aliases .= "table <{$aliased['name']}> persist file \"{$urlfn}\"\n"; + $aliases .= "{$aliased['name']} = \"<{$aliased['name']}>\"\n"; + } + break; + case "urltable_ports": + // TODO: Change it when pf supports tables with ports + $urlfn = alias_expand_urltable($aliased['name']); + if ($urlfn) { + $aliases .= "{$aliased['name']} = \"{ " . preg_replace("/\n/", " ", file_get_contents($urlfn)) . " }\"\n"; + } + break; + case "port": + case "url_ports": + $aliases .= "{$aliased['name']} = \"{ {$addrlist} }\"\n"; + break; + default: + $aliases .= "{$aliased['name']} = \"{ {$aliased['address']}{$extralias} }\"\n"; + break; + } + } + } + $result = "{$alias} \n"; + $result .= "{$aliases}"; + + return $result; +} + +function filter_generate_gateways() { + global $config, $g, $GatewaysList; + + $rules = "# Gateways\n"; + + update_filter_reload_status(gettext("Creating gateway group item...")); + + /* Lookup Gateways to be used in filter rules once */ + $GatewaysList = return_gateways_array(); + $GatewayGroupsList = return_gateway_groups_array(); + + if (is_array($GatewaysList)) { + foreach ($GatewaysList as $gwname => $gateway) { + $int = $gateway['interface']; + $gwip = $gateway['gateway']; + $route = ""; + if (!is_ipaddr($gwip)) { + $gwip = get_interface_gateway($gateway['friendlyiface']); + } + if (is_ipaddr($gwip) && !empty($int) && !isset($gateway['force_down'])) { + $route = "route-to ( {$int} {$gwip} )"; + } + if (($route === "") && isset($config['system']['skip_rules_gw_down'])) { + unset($GatewaysList[$gwname]); + } else { + $rules .= "GW{$gwname} = \" {$route} \"\n"; + } + } + } + + if (is_array($GatewayGroupsList)) { + foreach ($GatewayGroupsList as $gateway => $members) { + $route = ""; + /* hey, that's not a group member! */ + unset($members['ipprotocol']); + if (count($members) > 0) { + $foundlb = 0; + $routeto = ""; + foreach ($members as $idx => $member) { + $int = $member['int']; + $gatewayip = $member['gwip']; + if (($int <> "") && is_ipaddr($gatewayip)) { + if ($g['debug']) { + log_error(sprintf(gettext('Setting up route with %1$s on %2$s'), $gatewayip, $int)); + } + if ($member['weight'] > 1) { + $routeto .= str_repeat("( {$int} {$gatewayip} ) ", $member['weight']); + } else { + $routeto .= "( {$int} {$gatewayip} ) "; + } + $foundlb++; + } else { + log_error(sprintf(gettext("An error occurred while trying to find the interface got %s . The rule has not been added."), $gatewayip)); + } + } + $route = ""; + if ($foundlb > 0) { + $route = " route-to { {$routeto} } "; + if ($foundlb > 1) { + $route .= " round-robin "; + if (isset($config['system']['lb_use_sticky'])) { + $route .= " sticky-address "; + } + } + } + } + if (($route === "") && isset($config['system']['skip_rules_gw_down'])) { + unset($GatewayGroupsList[$gateway]); + } else { + $rules .= "GW{$gateway} = \" {$route} \"\n"; + } + } + } + + /* Create a global array to avoid errors on rulesets. */ + $GatewaysList = $GatewaysList + $GatewayGroupsList; + + $rules .= "\n"; + + return $rules; +} + +/* returns space separated list of vpn subnets */ +function filter_get_vpns_list() { + global $config; + + $vpns = ""; + $vpns_arr = array(); + + /* ipsec */ + if (isset($config['ipsec']['enable'])) { + if (is_array($config['ipsec']['phase2'])) { + foreach ($config['ipsec']['phase2'] as $ph2ent) { + if ((!$ph2ent['mobile']) && ($ph2ent['mode'] != 'transport')) { + if (!function_exists('ipsec_idinfo_to_cidr')) { + require_once("ipsec.inc"); + } + if (!is_array($ph2ent['remoteid'])) { + continue; + } + $ph2ent['remoteid']['mode'] = $ph2ent['mode']; + $vpns_subnet = ipsec_idinfo_to_cidr($ph2ent['remoteid']); + if (!is_subnet($vpns_subnet) || $vpns_subnet == "0.0.0.0/0") { + continue; + } + $vpns_arr[] = $vpns_subnet; + } + } + } + } + + /* openvpn */ + foreach (array('client', 'server') as $type) { + if (is_array($config['openvpn']["openvpn-$type"])) { + foreach ($config['openvpn']["openvpn-$type"] as $settings) { + if (is_array($settings)) { + if (!isset($settings['disable'])) { + $remote_networks = explode(',', $settings['remote_network']); + foreach ($remote_networks as $remote_network) { + if (is_subnet($remote_network) && ($remote_network <> "0.0.0.0/0")) { + $vpns_arr[] = $remote_network; + } + } + if (is_subnet($settings['tunnel_network']) && $settings['tunnel_network'] <> "0.0.0.0/0") { + $vpns_arr[] = $settings['tunnel_network']; + } + } + } + } + } + } + /* pppoe */ + if (is_array($config['pppoes']['pppoe'])) { + foreach ($config['pppoes']['pppoe'] as $pppoe) { + if ($pppoe['mode'] == "server") { + if (is_ipaddr($pppoe['remoteip'])) { + $pppoesub = gen_subnet($pppoe['remoteip'], $pppoe['pppoe_subnet']); + if (is_subnet($pppoesub)) { + $vpns_arr[] = $pppoesub; + } + } + } + } + } + + if (!empty($vpns_arr)) { + $vpns = implode(" ", $vpns_arr); + } + + return $vpns; +} + +/* returns space separated list of directly connected networks + * optionally returns an array instead, including friendly interface and gateway (if applicable) + */ +function filter_get_direct_networks_list($returnsubnetsonly = true) { + global $config, $FilterIflist, $GatewaysList; + /* build list of directly connected interfaces and networks */ + $networks = ""; + $networks_arr = array(); + if (empty($FilterIflist)) { + filter_generate_optcfg_array(); + } + foreach ($FilterIflist as $ifent => $ifcfg) { + $subnet = "{$ifcfg['sa']}/{$ifcfg['sn']}"; + if (is_subnet($subnet)) { + if ($returnsubnetsonly) { + $networks_arr[] = $subnet; + } else { + $networks_arr[] = array( + 'subnet' => $subnet, + 'if' => $ifent, + 'ip' => $ifcfg['ip']); + } + } + } + foreach (get_configured_ip_aliases_list(true) as $vip) { + $subnet = "{$vip['subnet']}/{$vip['subnet_bits']}"; + if (is_subnet($subnet) && !(is_subnetv4($subnet) && $vip['subnet_bits'] == 32) && !(is_subnetv6($subnet) && $vip['subnet_bits'] == 128)) { + if (is_subnetv4($subnet)) { + $subnet = gen_subnet($vip['subnet'], $vip['subnet_bits']) . "/{$vip['subnet_bits']}"; + } else if (is_subnetv6($subnet)) { + $subnet = gen_subnetv6($vip['subnet'], $vip['subnet_bits']) . "/{$vip['subnet_bits']}"; + } + if ($returnsubnetsonly) { + $networks_arr[] = $subnet; + } else { + $networks_arr[] = array( + 'subnet' => $subnet, + 'if' => $vip['interface'], + 'ip' => $vip['subnet']); + } + } + } + foreach (get_staticroutes() as $netent) { + if (is_subnet($netent['network'])) { + if ($returnsubnetsonly) { + $networks_arr[] = $netent['network']; + } else if (isset($GatewaysList[$netent['gateway']])) { + $networks_arr[] = array( + 'subnet' => $netent['network'], + 'if' => $GatewaysList[$netent['gateway']]['friendlyiface'], + 'gateway' => $GatewaysList[$netent['gateway']]['gateway']); + } + } + } + if ($returnsubnetsonly) { + if (!empty($networks_arr)) { + $networks = implode(" ", $networks_arr); + } + return $networks; + } else { + return $networks_arr; + } +} + +function filter_generate_optcfg_array() { + global $config, $FilterIflist; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_generate_optcfg_array() being called $mt\n"; + } + + read_layer7_config(); + /* if list */ + $iflist = get_configured_interface_with_descr(); + foreach ($iflist as $if => $ifdetail) { + $oc = $config['interfaces'][$if]; + $oic = array(); + $oic['if'] = get_real_interface($if); + if (!does_interface_exist($oic['if'])) { + continue; + } + $oic['ifv6'] = get_real_interface($if, "inet6"); + $oic['ip'] = get_interface_ip($if); + $oic['ipv6'] = get_interface_ipv6($if); + if (!is_ipaddrv4($oc['ipaddr']) && !empty($oc['ipaddr'])) { + $oic['type'] = $oc['ipaddr']; + } + if (!is_ipaddrv6($oc['ipaddrv6']) && !empty($oc['ipaddrv6'])) { + $oic['type6'] = $oc['ipaddrv6']; + } + if (!empty($oc['track6-interface'])) { + $oic['track6-interface'] = $oc['track6-interface']; + } + $oic['sn'] = get_interface_subnet($if); + $oic['snv6'] = get_interface_subnetv6($if); + $oic['mtu'] = empty($oc['mtu']) ? 1500 : $oc['mtu']; + $oic['mss'] = empty($oc['mss']) ? '' : $oc['mss']; + $oic['descr'] = $ifdetail; + $oic['sa'] = gen_subnet($oic['ip'], $oic['sn']); + $oic['sav6'] = gen_subnetv6($oic['ipv6'], $oic['snv6']); + $oic['nonat'] = $oc['nonat']; + $oic['alias-address'] = $oc['alias-address']; + $oic['alias-subnet'] = $oc['alias-subnet']; + $oic['gateway'] = $oc['gateway']; + $oic['gatewayv6'] = $oc['gatewayv6']; + $oic['spoofcheck'] = "yes"; + $oic['bridge'] = link_interface_to_bridge($if); + $vips = link_interface_to_vips($if); + if (!empty($vips)) { + foreach ($vips as $vipidx => $vip) { + if (is_ipaddrv4($vip['subnet'])) { + if (!is_array($oic['vips'])) { + $oic['vips'] = array(); + } + $oic['vips'][$vipidx]['ip'] = $vip['subnet']; + if (empty($vip['subnet_bits'])) { + $oic['vips'][$vipidx]['sn'] = 32; + } else { + $oic['vips'][$vipidx]['sn'] = $vip['subnet_bits']; + } + } else if (is_ipaddrv6($vip['subnet'])) { + if (!is_array($oic['vips6'])) { + $oic['vips6'] = array(); + } + $oic['vips6'][$vipidx]['ip'] = $vip['subnet']; + if (empty($vip['subnet_bits'])) { + $oic['vips6'][$vipidx]['sn'] = 128; + } else { + $oic['vips6'][$vipidx]['sn'] = $vip['subnet_bits']; + } + } + } + } + unset($vips); + $FilterIflist[$if] = $oic; + } + + if ($config['pptpd']['mode'] == "server" || $config['pptpd']['mode'] == "redir") { + $oic = array(); + $oic['if'] = 'pptp'; + $oic['descr'] = 'pptp'; + $oic['ip'] = $config['pptpd']['localip']; + $oic['sa'] = $config['pptpd']['remoteip']; + $oic['mode'] = $config['pptpd']['mode']; + $oic['virtual'] = true; + if ($config['pptpd']['pptp_subnet'] <> "") { + $oic['sn'] = $config['pptpd']['pptp_subnet']; + } else { + $oic['sn'] = "32"; + } + $FilterIflist['pptp'] = $oic; + } + if ($config['l2tp']['mode'] == "server") { + $oic = array(); + $oic['if'] = 'l2tp'; + $oic['descr'] = 'L2TP'; + $oic['ip'] = $config['l2tp']['localip']; + $oic['sa'] = $config['l2tp']['remoteip']; + if ($config['l2tp']['l2tp_subnet'] <> "") { + $oic['sn'] = $config['l2tp']['l2tp_subnet']; + } else { + $oic['sn'] = "32"; + } + $oic['mode'] = $config['l2tp']['mode']; + $oic['virtual'] = true; + $FilterIflist['l2tp'] = $oic; + } + if (is_array($config['pppoes']['pppoe']) && (count($config['pppoes']['pppoe']) > 0)) { + $pppoeifs = array(); + foreach ($config['pppoes']['pppoe'] as $pppoe) { + if ($pppoe['mode'] == "server") { + $oic = array(); + $oic['if'] = 'pppoe'; + $oic['descr'] = 'pppoe'; + $oic['ip'] = $pppoe['localip']; + $oic['sa'] = $pppoe['remoteip']; + $oic['mode'] = $pppoe['mode']; + $oic['virtual'] = true; + if ($pppoe['pppoe_subnet'] <> "") { + $oic['sn'] = $pppoe['pppoe_subnet']; + } else { + $oic['sn'] = "32"; + } + $pppoeifs[] = $oic; + } + } + if (count($pppoeifs)) { + $FilterIflist['pppoe'] = $pppoeifs; + } + } + /* add ipsec interfaces */ + if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) { + $oic = array(); + $oic['if'] = 'enc0'; + $oic['descr'] = 'IPsec'; + $oic['type'] = "none"; + $oic['virtual'] = true; + $FilterIflist['enc0'] = $oic; + } + /* add openvpn interfaces */ + if ($config['openvpn']['openvpn-server'] || $config['openvpn']['openvpn-client']) { + $oic = array(); + $oic['if'] = "openvpn"; + $oic['descr'] = 'OpenVPN'; + $oic['type'] = "none"; + $oic['virtual'] = true; + $FilterIflist['openvpn'] = $oic; + } + /* add interface groups */ + if (is_array($config['ifgroups']['ifgroupentry'])) { + foreach ($config['ifgroups']['ifgroupentry'] as $ifgen) { + $oc = array(); + $oc['if'] = $ifgen['ifname']; + $oc['descr'] = $ifgen['ifname']; + $oc['virtual'] = true; + $FilterIflist[$ifgen['ifname']] = $oc; + } + } +} + +function filter_flush_nat_table() { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_flush_nat_table() being called $mt\n"; + } + return mwexec("/sbin/pfctl -F nat"); +} + +function filter_flush_state_table() { + return mwexec("/sbin/pfctl -F state"); +} + +function filter_get_reflection_interfaces($natif = "") { + global $FilterIflist; + + $nat_if_list = array(); + + foreach ($FilterIflist as $ifent => $ifname) { + if ($ifname['if'] == $natif) { + continue; + } + + /* Do not add reflection redirects for interfaces with gateways */ + if (interface_has_gateway($ifent)) { + continue; + } + + $nat_if_list[] = $ifname['if']; + } + + return $nat_if_list; +} + +function filter_generate_reflection_nat($rule, &$route_table, $nat_ifs, $protocol, $target, $target_ip, $target_subnet = "") { + global $config, $FilterIflist; + + if (!isset($config['system']['enablenatreflectionhelper'])) { + return ""; + } + + // Initialize natrules holder string + $natrules = ""; + + update_filter_reload_status(sprintf(gettext("Creating reflection NAT rule for %s..."), $rule['descr'])); + + /* TODO: Add this option to port forwards page. */ + if (isset($rule['staticnatport'])) { + $static_port = " static-port"; + } else { + $static_port = " port 1024:65535"; + } + + if (!empty($protocol)) { + $protocol_text = " proto {$protocol}"; + } else { + $protocol_text = ""; + } + + if (empty($target_subnet) || !is_numeric($target_subnet)) { + $target_subnet = 32; + } + + if (!is_array($route_table)) { + /* get a simulated IPv4-only route table based on the config */ + $route_table = filter_get_direct_networks_list(false); + foreach ($route_table as $rt_key => $rt_ent) { + if (!is_subnetv4($rt_ent['subnet'])) { + unset($route_table[$rt_key]); + } + if (isset($route_table[$rt_key]) && isset($FilterIflist[$rt_ent['if']]['if'])) { + $route_table[$rt_key]['if'] = $FilterIflist[$rt_ent['if']]['if']; + } + } + } + + /* Check if the target is accessed through a static route */ + foreach ($route_table as $route) { + if (isset($route['gateway']) && is_ipaddr($route['gateway'])) { + $subnet_split = explode("/", $route['subnet']); + if (in_array($route['if'], $nat_ifs) && check_subnets_overlap($target_ip, $target_subnet, $subnet_split[0], $subnet_split[1])) { + $target_ip = $route['gateway']; + $target_subnet = 32; + break; + } + } + } + + /* Search for matching subnets in the routing table */ + foreach ($route_table as $route) { + $subnet = $route['subnet']; + $subnet_split = explode("/", $subnet); + $subnet_if = $route['if']; + /* Blacklist invalid "from" sources since they can be picked up accidentally and cause rule errors. */ + $no_reflect_from = array("l2tp"); + if (in_array($subnet_if, $nat_ifs) && check_subnets_overlap($target_ip, $target_subnet, $subnet_split[0], $subnet_split[1])) { + $ifsubnet_ip = ""; + /* Find interface IP to use for NAT */ + foreach ($route_table as $ifnetwork) { + if (isset($ifnetwork['ip']) && is_ipaddr($ifnetwork['ip']) && $ifnetwork['if'] == $subnet_if && ip_in_subnet($ifnetwork['ip'], $subnet)) { + $ifsubnet_ip = $ifnetwork['ip']; + break; + } + } + if(!empty($ifsubnet_ip) && !in_array($subnet, $no_reflect_from)) { + $subnets = array($subnet); + /* Find static routes that also need to be referenced in the NAT rule */ + foreach ($route_table as $rtentry) { + if (isset($rtentry['gateway']) && is_ipaddr($rtentry['gateway']) && $rtentry['if'] == $subnet_if && ip_in_subnet($rtentry['gateway'], $subnet)) { + $subnets[] = $rtentry['subnet']; + } + } + if (count($subnets) > 1) { + $subnet = "{ " . implode(" ", $subnets) . " }"; + } + $natrules .= "no nat on {$subnet_if}{$protocol_text} from {$subnet_if} to {$target}\n"; + $natrules .= "nat on {$subnet_if}{$protocol_text} from {$subnet} to {$target} -> {$ifsubnet_ip}{$static_port}\n"; + } + } + } + + if (!empty($natrules)) { + $natrules .= "\n"; + } + + return $natrules; +} + +function filter_generate_reflection_proxy($rule, $nordr, $rdr_ifs, $srcaddr, $dstaddr_port, &$starting_localhost_port, &$reflection_txt) { + global $FilterIflist, $config; + + // Initialize natrules holder string + $natrules = ""; + $reflection_txt = array(); + + if (!empty($rdr_ifs)) { + if ($config['system']['reflectiontimeout']) { + $reflectiontimeout = $config['system']['reflectiontimeout']; + } else { + $reflectiontimeout = "2000"; + } + + update_filter_reload_status(sprintf(gettext("Creating reflection rule for %s..."), $rule['descr'])); + + $rdr_if_list = implode(" ", $rdr_ifs); + if (count($rdr_ifs) > 1) { + $rdr_if_list = "{ {$rdr_if_list} }"; + } + + $natrules .= "\n# Reflection redirects\n"; + + $localport = $rule['local-port']; + if (!empty($localport) && is_alias($localport)) { + $localport = filter_expand_alias($localport); + $localport = explode(" ", trim($localport)); + // The translation port for rdr, when specified, does not support more than one port or range. + // Emulating for behavior consistent with the original port forward. + $localport = $localport[0]; + } + + if (is_alias($rule['destination']['port'])) { + if (empty($localport) || $rule['destination']['port'] == $rule['local-port']) { + $dstport = filter_expand_alias($rule['destination']['port']); + $dstport = array_filter(explode(" ", trim($dstport))); + $localport = ""; + } else if (!empty($localport)) { + $dstport = array($localport); + } + } else { + $dstport = array(str_replace("-", ":", $rule['destination']['port'])); + $dstport_split = explode(":", $dstport[0]); + + if (!empty($localport) && $dstport_split[0] != $rule['local-port']) { + if (!is_alias($rule['local-port']) && $dstport_split[1] && $dstport_split[0] != $dstport_split[1]) { + $localendport = $localport + ($dstport_split[1] - $dstport_split[0]); + $localport .= ":$localendport"; + } + + $dstport = array($localport); + } else { + $localport = ""; + } + } + + $dstaddr = explode(" ", $dstaddr_port); + if ($dstaddr[2]) { + $rflctintrange = array_pop($dstaddr); + array_pop($dstaddr); + } else { + return ""; + } + $dstaddr = implode(" ", $dstaddr); + if (empty($dstaddr) || trim($dstaddr) == "0.0.0.0" || strtolower(trim($dstaddr)) == "port") { + return ""; + } + + if (isset($rule['destination']['any'])) { + if (!$rule['interface']) { + $natif = "wan"; + } else { + $natif = $rule['interface']; + } + + if (!isset($FilterIflist[$natif])) { + return ""; + } + if (is_ipaddr($FilterIflist[$natif]['ip'])) { + $dstaddr = $FilterIflist[$natif]['ip']; + } else { + return ""; + } + + if (!empty($FilterIflist[$natif]['sn'])) { + $dstaddr = gen_subnet($dstaddr, $FilterIflist[$natif]['sn']) . '/' . $FilterIflist[$natif]['sn']; + } + } + + switch ($rule['protocol']) { + case "tcp/udp": + $protocol = "{ tcp udp }"; + $reflect_protos = array('tcp', 'udp'); + break; + case "tcp": + case "udp": + $protocol = $rule['protocol']; + $reflect_protos = array($rule['protocol']); + break; + default: + return ""; + break; + } + + if (!empty($nordr)) { + $natrules .= "no rdr on {$rdr_if_list} proto {$protocol} from {$srcaddr} to {$dstaddr} port {$rflctintrange}\n"; + return $natrules; + } + + if (is_alias($rule['target'])) { + $target = filter_expand_alias($rule['target']); + } else if (is_ipaddr($rule['target'])) { + $target = $rule['target']; + } else if (is_ipaddr($FilterIflist[$rule['target']]['ip'])) { + $target = $FilterIflist[$rule['target']]['ip']; + } else { + return ""; + } + $starting_localhost_port_tmp = $starting_localhost_port; + $toomanyports = false; + /* only install reflection rules for < 19991 items */ + foreach ($dstport as $loc_pt) { + if ($starting_localhost_port < 19991) { + $toadd_array = array(); + $inetdport = $starting_localhost_port; + $rflctrange = $starting_localhost_port; + + $loc_pt = explode(":", $loc_pt); + if ($loc_pt[1] && $loc_pt[1] > $loc_pt[0]) { + $delta = $loc_pt[1] - $loc_pt[0]; + } else { + $delta = 0; + } + + if (($inetdport + $delta + 1) - $starting_localhost_port_tmp > 500) { + log_error("Not installing NAT reflection rules for a port range > 500"); + $inetdport = $starting_localhost_port; + $toadd_array = array(); + $toomanyports = true; + break; + } else if (($inetdport + $delta) > 19990) { + log_error("Installing partial NAT reflection rules. Maximum 1,000 reached."); + $delta = 19990 - $inetdport; + $loc_pt[1] = $loc_pt[0] + $delta; + if ($delta == 0) { + unset($loc_pt[1]); + } + $toomanyports = true; + + if (!empty($localport)) { + if (is_alias($rule['destination']['port'])) { + $rflctintrange = alias_expand($rule['destination']['port']); + } else { + if ($dstport_split[1]) { + $dstport_split[1] = $dstport_split[0] + $inetdport + $delta - $starting_localhost_port; + } + $rflctintrange = implode(":", $dstport_split); + } + } + } + + if (empty($localport)) { + $rflctintrange = implode(":", $loc_pt); + } + if ($inetdport + $delta > $starting_localhost_port) { + $rflctrange .= ":" . ($inetdport + $delta); + } + $starting_localhost_port = $inetdport + $delta + 1; + $toadd_array = array_merge($toadd_array, range($loc_pt[0], $loc_pt[0] + $delta)); + + if (!empty($toadd_array)) { + $rtarget = explode(" ", trim($target)); + foreach ($toadd_array as $tda) { + if (empty($tda)) { + continue; + } + foreach ($reflect_protos as $reflect_proto) { + if ($reflect_proto == "udp") { + $socktype = "dgram"; + $dash_u = "-u "; + $wait = "wait\t"; + } else { + $socktype = "stream"; + $dash_u = ""; + $wait = "nowait/0"; + } + foreach ($rtarget as $targip) { + if (empty($targip)) { + continue; + } + $reflection_txt[] = "{$inetdport}\t{$socktype}\t{$reflect_proto}\t{$wait}\tnobody\t/usr/bin/nc\tnc {$dash_u}-w {$reflectiontimeout} {$targip} {$tda}\n"; + } + } + $inetdport++; + } + $natrules .= "rdr on {$rdr_if_list} proto {$protocol} from {$srcaddr} to {$dstaddr} port {$rflctintrange} tag PFREFLECT -> 127.0.0.1 port {$rflctrange}\n"; + } + } + + if ($toomanyports) { + break; + } + } + + $reflection_txt = array_unique($reflection_txt); + } + + return $natrules; +} + +function filter_nat_rules_automatic_tonathosts($with_descr = false) { + global $config, $FilterIflist, $GatewaysList; + + $tonathosts = array("127.0.0.0/8"); + $descriptions = array(gettext("localhost")); + + foreach (get_staticroutes() as $route) { + $netip = explode("/", $route['network']); + if (isset($GatewaysList[$route['gateway']])) { + $gateway =& $GatewaysList[$route['gateway']]; + if (!interface_has_gateway($gateway['interface']) && is_private_ip($netip[0])) { + $tonathosts[] = $route['network']; + $descriptions[] = gettext("static route"); + } + } + } + + /* create outbound nat entries for all local networks */ + foreach ($FilterIflist as $ocname => $oc) { + if (interface_has_gateway($ocname)) { + continue; + } + if (is_ipaddr($oc['alias-address'])) { + $tonathosts[] = "{$oc['alias-address']}/{$oc['alias-subnet']}"; + $descriptions[] = $oc['descr'] . " " . gettext("DHCP alias address"); + } + if ($oc['sa']) { + $tonathosts[] = "{$oc['sa']}/{$oc['sn']}"; + $descriptions[] = $oc['descr']; + if (isset($oc['vips']) && is_array($oc['vips'])) { + $if_subnets = array("{$oc['sa']}/{$oc['sn']}"); + foreach ($oc['vips'] as $vip) { + if (!is_ipaddrv4($vip['ip'])) { + continue; + } + + foreach ($if_subnets as $subnet) { + if (ip_in_subnet($vip['ip'], $subnet)) { + continue 2; + } + } + + $network = gen_subnet($vip['ip'], $vip['sn']); + array_unshift($tonathosts, $network . '/' . $vip['sn']); + array_unshift($descriptions, "Virtual IP ({$oc['descr']})"); + $if_subnets[] = $network . '/' . $vip['sn']; + unset($network); + } + unset($if_subnets); + } + } + } + + /* PPTP subnet */ + if (($config['pptpd']['mode'] == "server") && is_private_ip($config['pptpd']['remoteip'])) { + if (isset($config['pptpd']['n_pptp_units']) && is_numeric($config['pptpd']['n_pptp_units'])) { + $pptp_subnets = ip_range_to_subnet_array($config['pptpd']['remoteip'], + long2ip32(ip2long($config['pptpd']['remoteip'])+($config['pptpd']['n_pptp_units']-1))); + } else { + $pptp_subnets = ip_range_to_subnet_array($config['pptpd']['remoteip'], + long2ip32(ip2long($config['pptpd']['remoteip']))); + } + + foreach ($pptp_subnets as $subnet) { + $tonathosts[] = $subnet; + $descriptions[] = gettext("PPTP server"); + } + } + + /* PPPoE subnet */ + if (is_array($FilterIflist['pppoe'])) { + foreach ($FilterIflist['pppoe'] as $pppoe) { + if (is_private_ip($pppoe['ip'])) { + $tonathosts[] = "{$pppoe['sa']}/{$pppoe['sn']}"; + $descriptions[] = gettext("PPPoE server"); + } + } + } + + /* L2TP subnet */ + if (isset($FilterIflist['l2tp']) && $FilterIflist['l2tp']['mode'] == "server") { + $l2tp_sa = $FilterIflist['l2tp']['sa']; + $l2tp_sn = $FilterIflist['l2tp']['sn']; + if (is_private_ip($l2tp_sa) && !empty($l2tp_sn)) { + $tonathosts[] = "{$l2tp_sa}/{$l2tp_sn}"; + $descriptions[] = gettext("L2TP server"); + } + } + + /* add openvpn interfaces */ + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as $ovpnsrv) { + if (!isset($ovpnsrv['disable']) && !empty($ovpnsrv['tunnel_network'])) { + $tonathosts[] = $ovpnsrv['tunnel_network']; + $descriptions[] = gettext("OpenVPN server"); + } + } + } + + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as $ovpncli) { + if (!isset($ovpncli['disable']) && !empty($ovpncli['tunnel_network'])) { + $tonathosts[] = $ovpncli['tunnel_network']; + $descriptions[] = gettext("OpenVPN client"); + } + } + } + + /* IPsec mode_cfg subnet */ + if ((isset($config['ipsec']['client']['enable'])) && + (!empty($config['ipsec']['client']['pool_address'])) && + (!empty($config['ipsec']['client']['pool_netbits']))) { + $tonathosts[] = "{$config['ipsec']['client']['pool_address']}/{$config['ipsec']['client']['pool_netbits']}"; + $descriptions[] = gettext("IPsec client"); + } + + if ($with_descr) { + $combined = array(); + foreach ($tonathosts as $idx => $subnet) { + $combined[] = array( + "subnet" => $subnet, + "descr" => $descriptions[$idx]); + } + + return $combined; + } else { + return $tonathosts; + } +} + +function filter_nat_rules_outbound_automatic($src) { + global $config, $FilterIflist; + + $rules = array(); + foreach ($FilterIflist as $if => $ifcfg) { + if (substr($ifcfg['if'], 0, 4) == "ovpn") { + continue; + } + if (!interface_has_gateway($if)) { + continue; + } + + $natent = array(); + $natent['interface'] = $if; + $natent['source']['network'] = $src; + $natent['dstport'] = "500"; + $natent['target'] = ""; + $natent['destination']['any'] = true; + $natent['staticnatport'] = true; + $natent['descr'] = gettext('Auto created rule for ISAKMP'); + $rules[] = $natent; + + $natent = array(); + $natent['interface'] = $if; + $natent['source']['network'] = $src; + $natent['sourceport'] = ""; + $natent['target'] = ""; + $natent['destination']['any'] = true; + $natent['natport'] = ""; + $natent['descr'] = gettext('Auto created rule'); + if (isset($ifcfg['nonat'])) { + $natent['nonat'] = true; + } + $rules[] = $natent; + } + + return $rules; +} + +/* Generate a 'nat on' or 'no nat on' rule for given interface */ +function filter_nat_rules_generate_if ($if, $src = "any", $srcport = "", $dst = "any", $dstport = "", $natip = "", $natport = "", $nonat = false, $staticnatport = false, $proto = "", $poolopts = "") { + global $config, $FilterIflist; + /* XXX: billm - any idea if this code is needed? */ + if ($src == "/32" || $src{0} == "/") { + return "# src incorrectly specified\n"; + } + if ($natip != "") { + if (is_subnet($natip)) { + $tgt = $natip; + } elseif (is_alias($natip)) { + $tgt = "\${$natip}"; + } else { + $tgt = "{$natip}/32"; + } + } else { + $natip = get_interface_ip($if); + if (is_ipaddr($natip)) { + $tgt = "{$natip}/32"; + } else { + $tgt = "(" . $FilterIflist[$if]['if'] . ")"; + } + } + /* Add the protocol, if defined */ + if (!empty($proto) && $proto != "any") { + if ($proto == "tcp/udp") { + $protocol = " proto { tcp udp }"; + } else { + $protocol = " proto {$proto}"; + } + } else { + $protocol = ""; + } + /* Set tgt for IPv6 */ + if ($proto == "ipv6") { + $natip = get_interface_ipv6($if); + if (is_ipaddrv6($natip)) { + $tgt = "{$natip}/128"; + } + } + /* Add the hard set source port (useful for ISAKMP) */ + if ($natport != "") { + $tgt .= " port {$natport}"; + } + /* sometimes this gets called with "" instead of a value */ + if ($src == "") { + $src = "any"; + } + /* Match on this source port */ + if ($srcport != "") { + $srcportexpand = alias_expand($srcport); + if (!$srcportexpand) { + $srcportexpand = $srcport; + } + $src .= " port {$srcportexpand}"; + } + /* sometimes this gets called with "" instead of a value */ + if ($dst == "") { + $dst = "any"; + } + /* Match on this dest port */ + if ($dstport != "") { + $dstportexpand = alias_expand($dstport); + if (!$dstportexpand) { + $dstportexpand = $dstport; + } + $dst .= " port {$dstportexpand}"; + } + /* outgoing static-port option, hamachi, Grandstream, VOIP, etc */ + $staticnatport_txt = ""; + if ($staticnatport) { + $staticnatport_txt = "static-port"; + } elseif (!$natport) { + $tgt .= " port 1024:65535"; // set source port range + } + /* Allow for negating NAT entries */ + if ($nonat) { + $nat = "no nat"; + $target = ""; + $staticnatport_txt = ""; + $poolopts = ""; + } else { + $nat = "nat"; + $target = "-> {$tgt}"; + } + $if_friendly = $FilterIflist[$if]['descr']; + /* Put all the pieces together */ + if ($if_friendly) { + $natrule = "{$nat} on \${$if_friendly} {$protocol} from {$src} to {$dst} {$target} {$poolopts} {$staticnatport_txt}\n"; + } else { + $natrule .= "# Could not convert {$if} to friendly name(alias)\n"; + } + return $natrule; +} + +function filter_nat_rules_generate() { + global $config, $g, $after_filter_configure_run, $FilterIflist, $GatewaysList, $aliases; + + $natrules = "no nat proto carp\n"; + $natrules .= "no rdr proto carp\n"; + $natrules .= "nat-anchor \"natearly/*\"\n"; + + $natrules .= "nat-anchor \"natrules/*\"\n\n"; + update_filter_reload_status(gettext("Creating 1:1 rules...")); + + $reflection_txt = ""; + $route_table = ""; + + /* any 1:1 mappings? */ + if (is_array($config['nat']['onetoone'])) { + foreach ($config['nat']['onetoone'] as $rule) { + if (isset($rule['disabled'])) { + continue; + } + + $sn = ""; + $sn1 = ""; + $target = alias_expand($rule['external']); + if (!$target) { + $natrules .= "# Unresolvable alias {$rule['target']}\n"; + continue; /* unresolvable alias */ + } + + if (!$rule['interface']) { + $natif = "wan"; + } else { + $natif = $rule['interface']; + } + if (!isset($FilterIflist[$natif])) { + continue; + } + + $srcaddr = filter_generate_address($rule, 'source'); + $dstaddr = filter_generate_address($rule, 'destination'); + if (!$dstaddr) { + $dstaddr = $FilterIflist[$natif]['ip']; + } + + $srcaddr = trim($srcaddr); + $dstaddr = trim($dstaddr); + + $tmp = explode('/', $srcaddr); + $srcip = $tmp[0]; + if (!empty($tmp[1]) && is_numeric($tmp[1])) { + $sn = $tmp[1]; + $sn1 = "/{$sn}"; + } + + $natif = $FilterIflist[$natif]['if']; + + /* + * If reflection is enabled, turn on extra redirections + * for this rule by adding other interfaces to an rdr rule. + */ + if ((isset($config['system']['enablebinatreflection']) || $rule['natreflection'] == "enable") && + ($rule['natreflection'] != "disable")) { + $nat_if_list = filter_get_reflection_interfaces($natif); + } else { + $nat_if_list = array(); + } + + $natrules .= "binat on {$natif} from {$srcaddr} to {$dstaddr} -> {$target}{$sn1}\n"; + if (!empty($nat_if_list)) { + $binat_if_list = implode(" ", $nat_if_list); + $binat_if_list = "{ {$binat_if_list} }"; + $reflection_txt .= "rdr on {$binat_if_list} from {$dstaddr} to {$target}{$sn1} -> {$srcaddr} bitmask\n"; + } + + $nat_if_list = array_merge(array($natif), $nat_if_list); + $reflection_txt .= filter_generate_reflection_nat($rule, $route_table, $nat_if_list, "", $srcaddr, $srcip, $sn); + } + } + + /* Add binat rules for Network Prefix translation */ + if (is_array($config['nat']['npt'])) { + foreach ($config['nat']['npt'] as $rule) { + if (isset($rule['disabled'])) { + continue; + } + + if (!$rule['interface']) { + $natif = "wan"; + } else { + $natif = $rule['interface']; + } + if (!isset($FilterIflist[$natif])) { + continue; + } + + $srcaddr = filter_generate_address($rule, 'source'); + $dstaddr = filter_generate_address($rule, 'destination'); + + $srcaddr = trim($srcaddr); + $dstaddr = trim($dstaddr); + + $natif = $FilterIflist[$natif]['descr']; + + $natrules .= "binat on \${$natif} from {$srcaddr} to any -> {$dstaddr}\n"; + $natrules .= "binat on \${$natif} from any to {$dstaddr} -> {$srcaddr}\n"; + + } + } + + /* ipsec nat */ + if (is_array($config['ipsec']) && isset($config['ipsec']['enable'])) { + if (is_array($config['ipsec']['phase2'])) { + foreach ($config['ipsec']['phase2'] as $ph2ent) { + if ($ph2ent['mode'] != 'transport' && !empty($ph2ent['natlocalid'])) { + if (!function_exists('ipsec_idinfo_to_cidr')) { + require_once("ipsec.inc"); + } + if (!is_array($ph2ent['localid'])) { + $ph2ent['localid'] = array(); + } + $ph2ent['localid']['mode'] = $ph2ent['mode']; + $local_subnet = ipsec_idinfo_to_cidr($ph2ent['localid']); + if (empty($local_subnet) || $local_subnet == "0.0.0.0/0") { + continue; + } + if (!is_subnet($local_subnet) && !is_ipaddr($local_subnet)) { + continue; + } + if (!is_array($ph2ent['natlocalid'])) { + $ph2ent['natlocalid'] = array(); + } + $ph2ent['natlocalid']['mode'] = $ph2ent['mode']; + $natlocal_subnet = ipsec_idinfo_to_cidr($ph2ent['natlocalid']); + if (empty($natlocal_subnet) || $natlocal_subnet == "0.0.0.0/0") { + continue; + } + if (!is_subnet($natlocal_subnet) && !is_ipaddr($natlocal_subnet)) { + continue; + } + if (!is_array($ph2ent['remoteid'])) { + $ph2ent['remoteid'] = array(); + } + $ph2ent['remoteid']['mode'] = $ph2ent['mode']; + $remote_subnet = ipsec_idinfo_to_cidr($ph2ent['remoteid']); + if (empty($remote_subnet)) { + continue; + } + if (!is_subnet($remote_subnet) && !is_ipaddr($remote_subnet)) { + continue; + } + if ($remote_subnet == "0.0.0.0/0") { + $remote_subnet = "any"; + } + if (is_ipaddr($natlocal_subnet) && !is_ipaddr($local_subnet)) { + $nattype = "nat"; + } else { + list($natnet, $natmask) = explode('/', $natlocal_subnet); + list($locnet, $locmask) = explode('/', $local_subnet); + if (intval($natmask) != intval($locmask)) { + $nattype = "nat"; + } else { + $nattype = "binat"; + } + unset($natnet, $natmask, $locnet, $locmask); + } + $natrules .= "{$nattype} on enc0 from {$local_subnet} to {$remote_subnet} -> {$natlocal_subnet}\n"; + } + } + } + } + + if ($config['nat']['outbound']['mode'] == "disabled") { + $natrules .= "\n# Outbound NAT rules are disabled\n"; + } + + if ($config['nat']['outbound']['mode'] == "advanced" || $config['nat']['outbound']['mode'] == "hybrid") { + $natrules .= "\n# Outbound NAT rules (manual)\n"; + /* advanced outbound rules */ + if (is_array($config['nat']['outbound']['rule'])) { + foreach ($config['nat']['outbound']['rule'] as $obent) { + if (isset($obent['disabled'])) { + continue; + } + update_filter_reload_status(sprintf(gettext("Creating advanced outbound rule %s"), $obent['descr'])); + $src = alias_expand($obent['source']['network']); + if (!$src) { + $src = $obent['source']['network']; + } + $dst = alias_expand($obent['destination']['address']); + if (!$dst) { + $dst = $obent['destination']['address']; + } + if (isset($obent['destination']['not']) && !isset($obent['destination']['any'])) { + $dst = "!" . $dst; + } + + if (!$obent['interface'] || !isset($FilterIflist[$obent['interface']])) { + continue; + } + + $obtarget = ($obent['target'] == "other-subnet") ? $obent['targetip'] . '/' . $obent['targetip_subnet']: $obent['target']; + $poolopts = (is_subnet($obtarget) || is_alias($obtarget)) ? $obent['poolopts'] : ""; + + $natrules .= filter_nat_rules_generate_if($obent['interface'], + $src, + $obent['sourceport'], + $dst, + $obent['dstport'], + $obtarget, + $obent['natport'], + isset($obent['nonat']), + isset($obent['staticnatport']), + $obent['protocol'], + $poolopts + ); + } + } + } + + /* outbound rules */ + if ((!isset($config['nat']['outbound']['mode'])) || + ($config['nat']['outbound']['mode'] == "automatic") || + ($config['nat']['outbound']['mode'] == "hybrid")) { + $natrules .= "\n# Outbound NAT rules (automatic)\n"; + /* standard outbound rules (one for each interface) */ + update_filter_reload_status(gettext("Creating outbound NAT rules")); + $tonathosts_array = filter_nat_rules_automatic_tonathosts(); + $tonathosts = implode(" ", $tonathosts_array); + $numberofnathosts = count($tonathosts_array); + + $natrules .= "\n# Subnets to NAT \n"; + if ($numberofnathosts > 0) { + update_filter_reload_status(gettext('Creating automatic outbound rules')); + + if ($numberofnathosts > 4) { + $natrules .= "table <tonatsubnets> { {$tonathosts} }\n"; + $macroortable = "<tonatsubnets>"; + } else { + $natrules .= "tonatsubnets = \"{ {$tonathosts} }\"\n"; + $macroortable = "\$tonatsubnets"; + } + + $a_outs = filter_nat_rules_outbound_automatic($macroortable); + foreach ($a_outs as $a_out) { + $natrules .= filter_nat_rules_generate_if($a_out['interface'], + $a_out['source']['network'], + $a_out['sourceport'], + $a_out['destination']['address'], + $a_out['dstport'], + $a_out['target'], + $a_out['natport'], + isset($a_out['nonat']), + isset($a_out['staticnatport'])); + } + } + unset($tonathosts, $tonathosts_array, $numberofnathosts); + } + + /* load balancer anchor */ + $natrules .= "\n# Load balancing anchor\n"; + $natrules .= "rdr-anchor \"relayd/*\"\n"; + + update_filter_reload_status(gettext("Setting up TFTP helper")); + $natrules .= "# TFTP proxy\n"; + $natrules .= "rdr-anchor \"tftp-proxy/*\"\n"; + + if (!empty($config['system']['tftpinterface'])) { + $tftpifs = explode(",", $config['system']['tftpinterface']); + foreach ($tftpifs as $tftpif) { + if ($FilterIflist[$tftpif]) { + $natrules .= "rdr pass on {$FilterIflist[$tftpif]['if']} proto udp from any to any port tftp -> 127.0.0.1 port 6969\n"; + } + } + } + + /* DIAG: add ipv6 NAT, if requested */ + if ((isset($config['diag']['ipv6nat']['enable'])) && + (is_ipaddr($config['diag']['ipv6nat']['ipaddr'])) && + (is_array($FilterIflist['wan']))) { + /* XXX: FIX ME! IPV6 */ + $natrules .= "rdr on \${$FilterIflist['wan']['descr']} proto ipv6 from any to any -> {$config['diag']['ipv6nat']['ipaddr']}\n"; + } + + if (file_exists("/var/etc/inetd.conf")) { + @unlink("/var/etc/inetd.conf"); + } + // Open inetd.conf write handle + $inetd_fd = fopen("/var/etc/inetd.conf", "w"); + /* add tftp protocol helper */ + fwrite($inetd_fd, "tftp-proxy\tdgram\tudp\twait\t\troot\t/usr/libexec/tftp-proxy\ttftp-proxy -v\n"); + + if (isset($config['nat']['rule'])) { + /* start reflection redirects on port 19000 of localhost */ + $starting_localhost_port = 19000; + $natrules .= "# NAT Inbound Redirects\n"; + foreach ($config['nat']['rule'] as $rule) { + update_filter_reload_status(sprintf(gettext("Creating NAT rule %s"), $rule['descr'])); + + if (isset($rule['disabled'])) { + continue; + } + + /* if item is an alias, expand */ + $dstport = ""; + $dstport[0] = alias_expand($rule['destination']['port']); + if (!$dstport[0]) { + $dstport = explode("-", $rule['destination']['port']); + } + + /* if item is an alias, expand */ + $localport = alias_expand($rule['local-port']); + if (!$localport || $dstport[0] == $localport) { + $localport = ""; + } else if (is_alias($rule['local-port'])) { + $localport = filter_expand_alias($rule['local-port']); + if ($localport) { + $localport = explode(" ", trim($localport)); + $localport = $localport[0]; + $localport = " port {$localport}"; + } + } else if (is_alias($rule['destination']['port'])) { + $localport = " port {$localport}"; + } else { + if (($dstport[1]) && ($dstport[0] != $dstport[1])) { + $localendport = $localport + ($dstport[1] - $dstport[0]); + + $localport .= ":$localendport"; + } + + $localport = " port {$localport}"; + } + + switch (strtolower($rule['protocol'])) { + case "tcp/udp": + $protocol = "{ tcp udp }"; + break; + case "tcp": + case "udp": + $protocol = strtolower($rule['protocol']); + break; + default: + $protocol = strtolower($rule['protocol']); + $localport = ""; + break; + } + + $target = alias_expand($rule['target']); + if (!$target && !isset($rule['nordr'])) { + $natrules .= "# Unresolvable alias {$rule['target']}\n"; + continue; /* unresolvable alias */ + } + + if (is_alias($rule['target'])) { + $target_ip = filter_expand_alias($rule['target']); + } else if (is_ipaddr($rule['target'])) { + $target_ip = $rule['target']; + } else if (is_ipaddr($FilterIflist[$rule['target']]['ip'])) { + $target_ip = $FilterIflist[$rule['target']]['ip']; + } else { + $target_ip = $rule['target']; + } + $target_ip = trim($target_ip); + + if ($rule['associated-rule-id'] == "pass") { + $rdrpass = "pass "; + } else { + $rdrpass = ""; + } + + if (isset($rule['nordr'])) { + $nordr = "no "; + $rdrpass = ""; + } else { + $nordr = ""; + } + + if (!$rule['interface']) { + $natif = "wan"; + } else { + $natif = $rule['interface']; + } + + if (!isset($FilterIflist[$natif])) { + continue; + } + + $srcaddr = filter_generate_address($rule, 'source', true); + $dstaddr = filter_generate_address($rule, 'destination', true); + $srcaddr = trim($srcaddr); + $dstaddr = trim($dstaddr); + + if (!$dstaddr) { + $dstaddr = $FilterIflist[$natif]['ip']; + } + + $dstaddr_port = explode(" ", $dstaddr); + if (empty($dstaddr_port[0]) || strtolower(trim($dstaddr_port[0])) == "port") { + continue; // Skip port forward if no destination address found + } + $dstaddr_reflect = $dstaddr; + if (isset($rule['destination']['any'])) { + /* With reflection enabled, destination of 'any' has side effects + * that most people would not expect, so change it on reflection rules. */ + + if (!empty($FilterIflist[$natif]['ip'])) { + $dstaddr_reflect = $FilterIflist[$natif]['ip']; + } else { + // no IP, bail + continue; + } + + if (!empty($FilterIflist[$natif]['sn'])) { + $dstaddr_reflect = gen_subnet($dstaddr_reflect, $FilterIflist[$natif]['sn']) . '/' . $FilterIflist[$natif]['sn']; + } + + if ($dstaddr_port[2]) { + $dstaddr_reflect .= " port " . $dstaddr_port[2]; + } + } + + $natif = $FilterIflist[$natif]['if']; + + $reflection_type = "none"; + if ($rule['natreflection'] != "disable" && $dstaddr_port[0] != "0.0.0.0") { + if ($rule['natreflection'] == "enable") { + $reflection_type = "proxy"; + } else if ($rule['natreflection'] == "purenat") { + $reflection_type = "purenat"; + } else if (!isset($config['system']['disablenatreflection'])) { + if (isset($config['system']['enablenatreflectionpurenat'])) { + $reflection_type = "purenat"; + } else { + $reflection_type = "proxy"; + } + } + } + + if ($reflection_type != "none") { + $nat_if_list = filter_get_reflection_interfaces($natif); + } else { + $nat_if_list = array(); + } + + if (empty($nat_if_list)) { + $reflection_type = "none"; + } + + $localport_nat = $localport; + if (empty($localport_nat) && $dstaddr_port[2]) { + $localport_nat = " port " . $dstaddr_port[2]; + } + + if ($srcaddr <> "" && $dstaddr <> "" && $natif) { + $natrules .= "{$nordr}rdr {$rdrpass}on {$natif} proto {$protocol} from {$srcaddr} to {$dstaddr}" . ($nordr == "" ? " -> {$target}{$localport}" : ""); + + /* Does this rule redirect back to a internal host? */ + if (isset($rule['destination']['any']) && !isset($rule['nordr']) && !isset($config['system']['enablenatreflectionhelper']) && !interface_has_gateway($rule['interface'])) { + $rule_interface_ip = find_interface_ip($natif); + $rule_interface_subnet = find_interface_subnet($natif); + if (!empty($rule_interface_ip) && !empty($rule_interface_subnet)) { + $rule_subnet = gen_subnet($rule_interface_ip, $rule_interface_subnet); + $natrules .= "\n"; + $natrules .= "no nat on {$natif} proto tcp from ({$natif}) to {$rule_subnet}/{$rule_interface_subnet}\n"; + $natrules .= "nat on {$natif} proto tcp from {$rule_subnet}/{$rule_interface_subnet} to {$target} port {$dstport[0]} -> ({$natif})\n"; + } + } + + if ($reflection_type != "none") { + if ($reflection_type == "proxy" && !isset($rule['nordr'])) { + $natrules .= filter_generate_reflection_proxy($rule, $nordr, $nat_if_list, $srcaddr, $dstaddr, $starting_localhost_port, $reflection_rules); + $nat_if_list = array($natif); + foreach ($reflection_rules as $txtline) { + fwrite($inetd_fd, $txtline); + } + } else if ($reflection_type == "purenat" || isset($rule['nordr'])) { + $rdr_if_list = implode(" ", $nat_if_list); + if (count($nat_if_list) > 1) { + $rdr_if_list = "{ {$rdr_if_list} }"; + } + $natrules .= "\n# Reflection redirect\n"; + $natrules .= "{$nordr}rdr {$rdrpass}on {$rdr_if_list} proto {$protocol} from {$srcaddr} to {$dstaddr_reflect}" . ($nordr == "" ? " -> {$target}{$localport}" : ""); + $nat_if_list = array_merge(array($natif), $nat_if_list); + } + } + + if (empty($nat_if_list)) { + $nat_if_list = array($natif); + } + + $natrules .= "\n"; + if (!isset($rule['nordr'])) { + $natrules .= filter_generate_reflection_nat($rule, $route_table, $nat_if_list, $protocol, "{$target}{$localport_nat}", $target_ip); + } + } + } + } + fclose($inetd_fd); // Close file handle + + if (isset($config['pptpd']['mode']) && ($config['pptpd']['mode'] != "off")) { + if ($config['pptpd']['mode'] == "redir") { + $pptpdtarget = $config['pptpd']['redir']; + $natrules .= "# PPTP\n"; + $natrules .= "rdr on \${$FilterIflist['wan']['descr']} proto gre from any to any -> {$pptpdtarget}\n"; + $natrules .= "rdr on \${$FilterIflist['wan']['descr']} proto tcp from any to any port 1723 -> {$pptpdtarget}\n"; + } + } + + $natrules .= discover_pkg_rules("nat"); + + $natrules .= "# UPnPd rdr anchor\n"; + $natrules .= "rdr-anchor \"miniupnpd\"\n"; + + if (!empty($reflection_txt)) { + $natrules .= "\n# Reflection redirects and NAT for 1:1 mappings\n" . $reflection_txt; + } + + // Check if inetd is running, if not start it. If so, restart it gracefully. + $helpers = isvalidproc("inetd"); + if (file_exists("/var/etc/inetd.conf")) { + if (!$helpers) { + mwexec("/usr/sbin/inetd -wW -R 0 -a 127.0.0.1 /var/etc/inetd.conf"); + } else { + sigkillbypid("/var/run/inetd.pid", "HUP"); + } + } + + return $natrules; +} + +function filter_generate_user_rule_arr($rule) { + global $config; + update_filter_reload_status(sprintf(gettext("Creating filter rule %s ..."), $rule['descr'])); + $ret = array(); + $line = filter_generate_user_rule($rule); + $ret['rule'] = $line; + $ret['interface'] = $rule['interface']; + if ($rule['descr'] != "" and $line != "") { + $ret['descr'] = "label \"" . fix_rule_label("USER_RULE: {$rule['descr']}") . "\""; + } else { + $ret['descr'] = "label \"USER_RULE\""; + } + + return $ret; +} + +function filter_generate_port(& $rule, $target = "source", $isnat = false) { + + $src = ""; + + $rule['protocol'] = strtolower($rule['protocol']); + if (in_array($rule['protocol'], array("tcp", "udp", "tcp/udp"))) { + if ($rule[$target]['port']) { + $srcport = explode("-", $rule[$target]['port']); + $srcporta = alias_expand($srcport[0]); + if (!$srcporta) { + log_error(sprintf(gettext("filter_generate_port: %s is not a valid {$target} port."), $srcport[0])); + } else if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) { + $src .= " port {$srcporta} "; + } else if (($srcport[0] == 1) && ($srcport[1] == 65535)) { + /* no need for a port statement here */ + } else if ($isnat) { + $src .= " port {$srcport[0]}:{$srcport[1]}"; + } else { + if (is_port($srcporta) && $srcport[1] == 65535) { + $src .= " port >= {$srcporta} "; + } else if ($srcport[0] == 1) { + $src .= " port <= {$srcport[1]} "; + } else { + $srcport[0]--; + $srcport[1]++; + $src .= " port {$srcport[0]} >< {$srcport[1]} "; + } + } + } + } + + return $src; +} + +function filter_address_add_vips_subnets(&$subnets, $if, $not) { + global $FilterIflist; + + $if_subnets = array($subnets); + + if ($not == true) { + $subnets = "!{$subnets}"; + } + + if (!isset($FilterIflist[$if]['vips']) || !is_array($FilterIflist[$if]['vips'])) { + return; + } + + foreach ($FilterIflist[$if]['vips'] as $vip) { + foreach ($if_subnets as $subnet) { + if (ip_in_subnet($vip['ip'], $subnet)) { + continue 2; + } + } + + if (is_ipaddrv4($vip['ip'])) { + if (!is_subnetv4($if_subnets[0])) { + continue; + } + + $network = gen_subnet($vip['ip'], $vip['sn']); + } else if (is_ipaddrv6($vip['ip'])) { + if (!is_subnetv6($if_subnets[0])) { + continue; + } + + $network = gen_subnetv6($vip['ip'], $vip['sn']); + } else { + continue; + } + + $subnets .= ' ' . ($not == true ? '!' : '') . $network . '/' . $vip['sn']; + $if_subnets[] = $network . '/' . $vip['sn']; + } + unset($if_subnets); + + if (strpos($subnets, ' ') !== false) { + $subnets = "{ {$subnets} }"; + } +} + +function filter_generate_address(& $rule, $target = "source", $isnat = false) { + global $FilterIflist, $config; + $src = ""; + + if (isset($rule[$target]['any'])) { + $src = "any"; + } else if ($rule[$target]['network']) { + if (strstr($rule[$target]['network'], "opt")) { + $optmatch = ""; + $matches = ""; + if ($rule['ipprotocol'] == "inet6") { + if (preg_match("/opt([0-9]*)$/", $rule[$target]['network'], $optmatch)) { + $opt_sa = $FilterIflist["opt{$optmatch[1]}"]['sav6']; + if (!is_ipaddrv6($opt_sa)) { + return ""; + } + $src = $opt_sa . "/" . $FilterIflist["opt{$optmatch[1]}"]['snv6']; + /* check for opt$NUMip here */ + } else if (preg_match("/opt([0-9]*)ip/", $rule[$target]['network'], $matches)) { + $src = $FilterIflist["opt{$matches[1]}"]['ipv6']; + if (!is_ipaddrv6($src)) { + return ""; + } + if (isset($rule[$target]['not'])) { + $src = " !{$src}"; + } + } + } else { + if (preg_match("/opt([0-9]*)$/", $rule[$target]['network'], $optmatch)) { + $opt_sa = $FilterIflist["opt{$optmatch[1]}"]['sa']; + if (!is_ipaddrv4($opt_sa)) { + return ""; + } + $src = $opt_sa . "/" . $FilterIflist["opt{$optmatch[1]}"]['sn']; + /* check for opt$NUMip here */ + } else if (preg_match("/opt([0-9]*)ip/", $rule[$target]['network'], $matches)) { + $src = $FilterIflist["opt{$matches[1]}"]['ip']; + if (!is_ipaddrv4($src)) { + return ""; + } + if (isset($rule[$target]['not'])) { + $src = " !{$src}"; + } + } + } + } else { + if ($rule['ipprotocol'] == "inet6") { + switch ($rule[$target]['network']) { + case 'wan': + $wansa = $FilterIflist['wan']['sav6']; + if (!is_ipaddrv6($wansa)) { + return ""; + } + $wansn = $FilterIflist['wan']['snv6']; + $src = "{$wansa}/{$wansn}"; + break; + case 'wanip': + $src = $FilterIflist["wan"]['ipv6']; + if (!is_ipaddrv6($src)) { + return ""; + } + break; + case 'lanip': + $src = $FilterIflist["lan"]['ipv6']; + if (!is_ipaddrv6($src)) { + return ""; + } + break; + case 'lan': + $lansa = $FilterIflist['lan']['sav6']; + if (!is_ipaddrv6($lansa)) { + return ""; + } + $lansn = $FilterIflist['lan']['snv6']; + $src = "{$lansa}/{$lansn}"; + break; + case '(self)': + $src = "(self)"; + break; + case 'pptp': + $pptpsav6 = gen_subnetv6($FilterIflist['pptp']['sav6'], $FilterIflist['pptp']['snv6']); + $pptpsnv6 = $FilterIflist['pptp']['snv6']; + $src = "{$pptpsav6}/{$pptpsnv6}"; + break; + case 'pppoe': + if (is_array($FilterIflist['pppoe'])) { + $pppoesav6 = gen_subnetv6($FilterIflist['pppoe'][0]['ipv6'], $FilterIflist['pppoe'][0]['snv6']); + $pppoesnv6 = $FilterIflist['pppoe'][0]['snv6']; + $src = "{$pppoesav6}/{$pppoesnv6}"; + } + } + if (isset($rule[$target]['not']) && !is_subnet($src)) { + $src = " !{$src}"; + } + } else { + switch ($rule[$target]['network']) { + case 'wan': + $wansa = $FilterIflist['wan']['sa']; + if (!is_ipaddrv4($wansa)) { + return ""; + } + $wansn = $FilterIflist['wan']['sn']; + $src = "{$wansa}/{$wansn}"; + break; + case 'wanip': + $src = $FilterIflist["wan"]['ip']; + break; + case 'lanip': + $src = $FilterIflist["lan"]['ip']; + break; + case 'lan': + $lansa = $FilterIflist['lan']['sa']; + if (!is_ipaddrv4($lansa)) { + return ""; + } + $lansn = $FilterIflist['lan']['sn']; + $src = "{$lansa}/{$lansn}"; + break; + case '(self)': + $src = "(self)"; + break; + case 'pptp': + if (isset($config['pptpd']['n_pptp_units']) && is_numeric($config['pptpd']['n_pptp_units'])) { + $pptp_subnets = ip_range_to_subnet_array($config['pptpd']['remoteip'], long2ip32(ip2long($config['pptpd']['remoteip'])+($config['pptpd']['n_pptp_units']-1))); + } else { + $pptp_subnets = ip_range_to_subnet_array($config['pptpd']['remoteip'], long2ip32(ip2long($config['pptpd']['remoteip']))); + } + if (empty($pptp_subnets)) { + return ""; + } + if (isset($rule[$target]['not'])) { + array_walk($pptp_subnets, function (&$value, $key) { + $value="!{$value}"; + }); + } + $src = "{ " . implode(" ", $pptp_subnets) . " }"; + break; + case 'pppoe': + /* XXX: This needs to be fixed somehow! */ + if (is_array($FilterIflist['pppoe'])) { + $pppoesa = gen_subnet($FilterIflist['pppoe'][0]['ip'], $FilterIflist['pppoe'][0]['sn']); + $pppoesn = $FilterIflist['pppoe'][0]['sn']; + $src = "{$pppoesa}/{$pppoesn}"; + } + break; + } + if ((isset($rule[$target]['not'])) && + (!is_subnet($src)) && + (strpos($src, '{') === false)) { + $src = " !{$src}"; + } + } + } + if (is_subnet($src)) { + filter_address_add_vips_subnets($src, $rule[$target]['network'], isset($rule[$target]['not'])); + } + } else if ($rule[$target]['address']) { + $expsrc = alias_expand($rule[$target]['address']); + if (isset($rule[$target]['not'])) { + $not = "!"; + } else { + $not = ""; + } + $src = " {$not} {$expsrc}"; + } + + if (empty($src)) { + return ''; + } + + $src .= filter_generate_port($rule, $target, $isnat); + + return $src; +} + +function filter_generate_user_rule($rule) { + global $config, $g, $FilterIflist, $GatewaysList; + global $layer7_rules_list, $dummynet_name_list; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_generate_user_rule() being called $mt\n"; + } + /* don't include disabled rules */ + if (isset($rule['disabled'])) { + return "# rule " . $rule['descr'] . " disabled \n"; + } + update_filter_reload_status("Creating filter rules {$rule['descr']} ..."); + $pptpdcfg = $config['pptpd']; + $int = ""; + $aline = array(); + + /* Check to see if the interface is in our list */ + if (isset($rule['floating'])) { + if (isset($rule['interface']) && $rule['interface'] <> "") { + $interfaces = explode(",", $rule['interface']); + $ifliste = ""; + foreach ($interfaces as $iface) { + if (array_key_exists($iface, $FilterIflist)) { + $ifliste .= " " . $FilterIflist[$iface]['if'] . " "; + } + } + if ($ifliste <> "") { + $aline['interface'] = " on { {$ifliste} } "; + } else { + $aline['interface'] = ""; + } + } else { + $aline['interface'] = ""; + } + } else if (!array_key_exists($rule['interface'], $FilterIflist)) { + foreach ($FilterIflist as $oc) { + $items .= $oc['descr'] . " "; + } + return "# array key \"{$rule['interface']}\" does not exist for \"" . $rule['descr'] . "\" in array: {{$items}}"; + } else if ((array_key_exists($rule['interface'], $FilterIflist)) && + (is_array($FilterIflist[$rule['interface']])) && + (is_array($FilterIflist[$rule['interface']][0]))) { + /* Currently the only case for this is the pppoe server. There should be an existing macro with this name. */ + $aline['interface'] = " on \$" . $rule['interface'] . " "; + } else { + $aline['interface'] = " on \$" . $FilterIflist[$rule['interface']]['descr'] . " "; + } + $ifcfg = $FilterIflist[$rule['interface']]; + if ($pptpdcfg['mode'] != "server") { + if (($rule['source']['network'] == "pptp") || + ($rule['destination']['network'] == "pptp")) { + return "# source network or destination network == pptp on " . $rule['descr']; + } + } + + switch ($rule['ipprotocol']) { + case "inet": + $aline['ipprotocol'] = "inet"; + break; + case "inet6": + $aline['ipprotocol'] = "inet6"; + break; + default: + $aline['ipprotocol'] = ""; + break; + } + + /* check for unresolvable aliases */ + if ($rule['source']['address'] && !alias_expand($rule['source']['address'])) { + $error_text = "Unresolvable source alias '{$rule['source']['address']}' for rule '{$rule['descr']}'"; + file_notice("Filter_Reload", $error_text); + return "# {$error_text}"; + } + if ($rule['destination']['address'] && !alias_expand($rule['destination']['address'])) { + $error_text = "Unresolvable destination alias '{$rule['destination']['address']}' for rule '{$rule['descr']}'"; + file_notice("Filter_Reload", $error_text); + return "# {$error_text}"; + } + update_filter_reload_status("Setting up pass/block rules"); + $type = $rule['type']; + if ($type != "pass" && $type != "block" && $type != "reject" && $type != "match") { + /* default (for older rules) is pass */ + $type = "pass"; + } + if ($type == "reject") { + $aline['type'] = "block return "; + } else { + $aline['type'] = $type . " "; + } + if (isset($rule['floating']) && $rule['floating'] == "yes") { + if ($rule['direction'] != "any") { + $aline['direction'] = " " . $rule['direction'] . " "; + } + } else { + /* ensure the direction is in */ + $aline['direction'] = " in "; + } + if (isset($rule['log'])) { + $aline['log'] = "log "; + } + if (!isset($rule['floating']) || isset($rule['quick'])) { + $aline['quick'] = " quick "; + } + + /* set the gateway interface */ + update_filter_reload_status(sprintf(gettext("Setting up pass/block rules %s"), $rule['descr'])); + + /* do not process reply-to for gateway'd rules */ + if ($rule['gateway'] == "" && $aline['direction'] <> "" && (interface_has_gateway($rule['interface']) || interface_has_gatewayv6($rule['interface'])) && !isset($config['system']['disablereplyto']) && !isset($rule['disablereplyto']) && $type != "match") { + if ($rule['ipprotocol'] == "inet6") { + $rg = get_interface_gateway_v6($rule['interface']); + if (is_ipaddrv6($rg)) { + $aline['reply'] = "reply-to ( {$ifcfg['ifv6']} {$rg} ) "; + } else if ($rule['interface'] <> "pptp") { + log_error(sprintf(gettext("Could not find IPv6 gateway for interface (%s)."), $rule['interface'])); + } + } else { + $rg = get_interface_gateway($rule['interface']); + if (is_ipaddrv4($rg)) { + $aline['reply'] = "reply-to ( {$ifcfg['if']} {$rg} ) "; + } else if ($rule['interface'] <> "pptp") { + log_error(sprintf(gettext("Could not find IPv4 gateway for interface (%s)."), $rule['interface'])); + } + } + } + /* if user has selected a custom gateway, lets work with it */ + else if ($rule['gateway'] <> "" && $type == "pass") { + if (isset($GatewaysList[$rule['gateway']])) { + /* Add the load balanced gateways */ + $aline['route'] = " \$GW{$rule['gateway']} "; + } else if (isset($config['system']['skip_rules_gw_down'])) { + return "# rule " . $rule['descr'] . " disabled because gateway " . $rule['gateway'] . " is down "; + } else { + log_error("The gateway: {$rule['gateway']} is invalid or unknown, not using it."); + } + } + + if (isset($rule['protocol']) && !empty($rule['protocol'])) { + if ($rule['protocol'] == "tcp/udp") { + $aline['prot'] = " proto { tcp udp } "; + } elseif (($rule['protocol'] == "icmp") && ($rule['ipprotocol'] == "inet6")) { + $aline['prot'] = " proto ipv6-icmp "; + } elseif ($rule['protocol'] == "icmp") { + $aline['prot'] = " proto icmp "; + } else { + $aline['prot'] = " proto {$rule['protocol']} "; + } + } else { + if ($rule['source']['port'] <> "" || $rule['destination']['port'] <> "") { + $aline['prot'] = " proto tcp "; + } + } + update_filter_reload_status(sprintf(gettext("Creating rule %s"), $rule['descr'])); + + /* source address */ + $src = trim(filter_generate_address($rule, "source")); + if (empty($src) || ($src == "/")) { + return "# at the break!"; + } + $aline['src'] = " from $src "; + + /* OS signatures */ + if (($rule['protocol'] == "tcp") && ($rule['os'] <> "")) { + $aline['os'] = " os \"{$rule['os']}\" "; + } + + /* destination address */ + $dst = trim(filter_generate_address($rule, "destination")); + if (empty($dst) || ($dst == "/")) { + return "# returning at dst $dst == \"/\""; + } + $aline['dst'] = "to $dst "; + + //Layer7 support + $l7_present = false; + $l7_structures = array(); + if (isset($rule['l7container']) && $rule['l7container'] != "none") { + $l7_present = true; + $l7rule =& $layer7_rules_list[$rule['l7container']]; + $l7_structures = $l7rule->get_unique_structures(); + $aline['divert'] = "divert-to " . $l7rule->GetRPort() . " "; + } + if (($rule['protocol'] == "icmp") && $rule['icmptype'] && ($rule['ipprotocol'] == "inet")) { + $aline['icmp-type'] = "icmp-type {$rule['icmptype']} "; + } + if (($rule['protocol'] == "icmp") && $rule['icmptype'] && ($rule['ipprotocol'] == "inet6")) { + $aline['icmp6-type'] = "icmp6-type {$rule['icmptype']} "; + } + if (!empty($rule['tag'])) { + if (ctype_digit($rule['tag'])) { + $aline['tag'] = " tag \"" .$rule['tag']. "\" "; + } else { + $aline['tag'] = " tag " .$rule['tag']. " "; + } + } + if (!empty($rule['tagged'])) { + $aline['tagged'] = " tagged " .$rule['tagged'] . " "; + } + if (!empty($rule['dscp'])) { + switch (strtolower($rule['dscp'])) { + case 'va': + $aline['dscp'] = " dscp \"44\" "; + break; + case 'VA': + $aline['dscp'] = " dscp \"44\" "; + break; + case 'cs1': + $aline['dscp'] = " dscp \"8\" "; + break; + case 'cs2': + $aline['dscp'] = " dscp \"16\" "; + break; + case 'cs3': + $aline['dscp'] = " dscp \"24\" "; + break; + case 'cs4': + $aline['dscp'] = " dscp \"32\" "; + break; + case 'cs5': + $aline['dscp'] = " dscp \"40\" "; + break; + case 'cs6': + $aline['dscp'] = " dscp \"48\" "; + break; + case 'cs7': + $aline['dscp'] = " dscp \"56\" "; + break; + default: + $aline['dscp'] = " dscp " . $rule['dscp'] . " "; + break; + } + } + if (!empty($rule['vlanprio']) && ($rule['vlanprio'] != "none")) { + $aline['vlanprio'] = " ieee8021q-pcp " . $rule['vlanprio'] . " "; + } + if (!empty($rule['vlanprioset']) && ($rule['vlanprioset'] != "none")) { + $aline['vlanprioset'] = " ieee8021q-setpcp " . $rule['vlanprioset'] . " "; + } + if ($type == "pass") { + if (isset($rule['allowopts'])) { + $aline['allowopts'] = " allow-opts "; + } + } + $aline['flags'] = ""; + if ($rule['protocol'] == "tcp") { + if (isset($rule['tcpflags_any'])) { + $aline['flags'] = "flags any "; + } else if (!empty($rule['tcpflags2'])) { + $aline['flags'] = "flags "; + if (!empty($rule['tcpflags1'])) { + $flags1 = explode(",", $rule['tcpflags1']); + foreach ($flags1 as $flag1) { + // CWR flag needs special treatment + if ($flag1[0] == "c") { + $aline['flags'] .= "W"; + } else { + $aline['flags'] .= strtoupper($flag1[0]); + } + } + } + $aline['flags'] .= "/"; + if (!empty($rule['tcpflags2'])) { + $flags2 = explode(",", $rule['tcpflags2']); + foreach ($flags2 as $flag2) { + // CWR flag needs special treatment + if ($flag2[0] == "c") { + $aline['flags'] .= "W"; + } else { + $aline['flags'] .= strtoupper($flag2[0]); + } + } + } + $aline['flags'] .= " "; + } else { + $aline['flags'] = "flags S/SA "; + } + } + if ($type == "pass") { + /* + * # keep state + * works with TCP, UDP, and ICMP. + * # modulate state + * works only with TCP. pfSense will generate strong Initial Sequence Numbers (ISNs) + * for packets matching this rule. + * # synproxy state + * proxies incoming TCP connections to help protect servers from spoofed TCP SYN floods. + * This option includes the functionality of keep state and modulate state combined. + * # none + * do not use state mechanisms to keep track. this is only useful if your doing advanced + * queueing in certain situations. please check the faq. + */ + $noadvoptions = false; + if (isset($rule['statetype']) && $rule['statetype'] <> "") { + switch ($rule['statetype']) { + case "none": + $noadvoptions = true; + $aline['flags'] .= " no state "; + break; + case "modulate state": + case "synproxy state": + if ($rule['protocol'] == "tcp") { + $aline['flags'] .= "{$rule['statetype']} "; + } + break; + case "sloppy state": + $aline['flags'] .= "keep state "; + $rule['sloppy'] = true; + break; + default: + $aline['flags'] .= "{$rule['statetype']} "; + break; + } + } else { + $aline['flags'] .= "keep state "; + } + + if ($noadvoptions == false && isset($rule['nopfsync'])) { + $rule['nopfsync'] = true; + } + + if ($noadvoptions == false || $l7_present) { + if ((isset($rule['source-track']) and $rule['source-track'] <> "") or + (isset($rule['max']) and $rule['max'] <> "") or + (isset($rule['max-src-nodes']) and $rule['max-src-nodes'] <> "") or + (isset($rule['max-src-states']) and $rule['max-src-states'] <> "") or + ((in_array($rule['protocol'], array("tcp", "tcp/udp"))) and + ((isset($rule['statetimeout']) and $rule['statetimeout'] <> "") or + (isset($rule['max-src-conn']) and $rule['max-src-conn'] <> "") or + (isset($rule['max-src-conn-rate']) and $rule['max-src-conn-rate'] <> "") or + (isset($rule['max-src-conn-rates']) and $rule['max-src-conn-rates'] <> ""))) or + (isset($rule['sloppy'])) or + (isset($rule['nopfsync'])) or + ($l7_present)) { + $aline['flags'] .= "( "; + if (isset($rule['sloppy'])) { + $aline['flags'] .= "sloppy "; + } + if (isset($rule['nopfsync'])) { + $aline['flags'] .= "no-sync "; + } + if (isset($rule['source-track']) and $rule['source-track'] <> "") { + $aline['flags'] .= "source-track rule "; + } + if (isset($rule['max']) and $rule['max'] <> "") { + $aline['flags'] .= "max " . $rule['max'] . " "; + } + if (isset($rule['max-src-nodes']) and $rule['max-src-nodes'] <> "") { + $aline['flags'] .= "max-src-nodes " . $rule['max-src-nodes'] . " "; + } + if ((in_array($rule['protocol'], array("tcp", "tcp/udp"))) and + (isset($rule['max-src-conn'])) and + ($rule['max-src-conn'] <> "")) { + $aline['flags'] .= "max-src-conn " . $rule['max-src-conn'] . " "; + } + if (isset($rule['max-src-states']) and $rule['max-src-states'] <> "") { + $aline['flags'] .= "max-src-states " . $rule['max-src-states'] . " "; + } + if ((in_array($rule['protocol'], array("tcp", "tcp/udp"))) and + (isset($rule['statetimeout'])) and + ($rule['statetimeout'] <> "")) { + $aline['flags'] .= "tcp.established " . $rule['statetimeout'] . " "; + } + if ((in_array($rule['protocol'], array("tcp", "tcp/udp"))) and + (isset($rule['max-src-conn-rate'])) and + ($rule['max-src-conn-rate'] <> "") and + (isset($rule['max-src-conn-rates'])) and + ($rule['max-src-conn-rates'] <> "")) { + $aline['flags'] .= "max-src-conn-rate " . $rule['max-src-conn-rate'] . " "; + $aline['flags'] .= "/" . $rule['max-src-conn-rates'] . ", overload <virusprot> flush global "; + } + + if (!empty($aline['divert'])) { + $aline['flags'] .= "max-packets 8 "; + } + + $aline['flags'] .= " ) "; + } + } + } + if ($rule['defaultqueue'] <> "") { + $aline['queue'] = " queue (".$rule['defaultqueue']; + if ($rule['ackqueue'] <> "") { + $aline['queue'] .= "," . $rule['ackqueue']; + } + $aline['queue'] .= ") "; + } + if ($rule['dnpipe'] <> "") { + if (!empty($dummynet_name_list[$rule['dnpipe']])) { + if ($dummynet_name_list[$rule['dnpipe']][0] == "?") { + $aline['dnpipe'] = " dnqueue( "; + $aline['dnpipe'] .= substr($dummynet_name_list[$rule['dnpipe']], 1); + if ($rule['pdnpipe'] <> "") { + $aline['dnpipe'] .= "," . substr($dummynet_name_list[$rule['pdnpipe']], 1); + } + } else { + $aline['dnpipe'] = " dnpipe ( " . $dummynet_name_list[$rule['dnpipe']]; + if ($rule['pdnpipe'] <> "") { + $aline['dnpipe'] .= "," . $dummynet_name_list[$rule['pdnpipe']]; + } + } + $aline['dnpipe'] .= ") "; + } + } + + /* is a time based rule schedule attached? */ + if (!empty($rule['sched']) && !empty($config['schedules'])) { + $aline['schedlabel'] = ""; + foreach ($config['schedules']['schedule'] as $sched) { + if ($sched['name'] == $rule['sched']) { + if (!filter_get_time_based_rule_status($sched)) { + if (!isset($config['system']['schedule_states'])) { + mwexec("/sbin/pfctl -y {$sched['schedlabel']}"); + } + return "# schedule finished - {$rule['descr']}"; + } else if ($g['debug']) { + log_error("[TDR DEBUG] status true -- rule type '$type'"); + } + + $aline['schedlabel'] = " schedule \"{$sched['schedlabel']}\" "; + break; + } + } + } + + if (!empty($rule['tracker'])) { + $aline['tracker'] = "tracker {$rule['tracker']} "; + } + + $line = ""; + /* exception(s) to a user rules can go here. */ + /* rules with a gateway or pool should create another rule for routing to vpns */ + if ((($aline['route'] <> "") && (trim($aline['type']) == "pass") && strstr($dst, "any")) && (!isset($config['system']['disablenegate']))) { + /* negate VPN/PPTP/PPPoE/Static Route networks for load balancer/gateway rules */ + $negate_networks = " to <negate_networks> " . filter_generate_port($rule, "destination"); + $line .= $aline['type'] . $aline['direction'] . $aline['log'] . $aline['quick'] . + $aline['interface'] . $aline['ipprotocol'] . $aline['prot'] . $aline['src'] . $aline['os'] . + $negate_networks . $aline['icmp-type'] . $aline['icmp6-type'] . $aline['tag'] . $aline['tagged'] . + $aline['vlanprio'] . $aline['vlanprioset'] . $aline['dscp'] . filter_negaterule_tracker() . $aline['allowopts'] . $aline['flags'] . + $aline['queue'] . $aline['dnpipe'] . $aline['schedlabel'] . + " label \"NEGATE_ROUTE: Negate policy routing for destination\"\n"; + + } + /* piece together the actual user rule */ + $line .= $aline['type'] . $aline['direction'] . $aline['log'] . $aline['quick'] . $aline['interface'] . + $aline['reply'] . $aline['route'] . $aline['ipprotocol'] . $aline['prot'] . $aline['src'] . $aline['os'] . $aline['dst'] . + $aline['divert'] . $aline['icmp-type'] . $aline['icmp6-type'] . $aline['tag'] . $aline['tagged'] . $aline['dscp'] . $aline['tracker'] . + $aline['vlanprio'] . $aline['vlanprioset'] . $aline['allowopts'] . $aline['flags'] . $aline['queue'] . $aline['dnpipe'] . $aline['schedlabel']; + + unset($aline); + + return $line; +} + +function filter_rules_generate() { + global $config, $g, $FilterIflist, $time_based_rules, $GatewaysList, $tracker; + + $fix_rule_label = 'fix_rule_label'; + $increment_tracker = 'filter_rule_tracker'; + + update_filter_reload_status(gettext("Creating default rules")); + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_rules_generate() being called $mt\n"; + } + + $pptpdcfg = $config['pptpd']; + + $ipfrules = ""; + $ipfrules .= discover_pkg_rules("pfearly"); + + /* relayd */ + $ipfrules .= "anchor \"relayd/*\"\n"; + /* OpenVPN user rules from radius */ + $ipfrules .= "anchor \"openvpn/*\"\n"; + /* IPsec user rules from radius */ + $ipfrules .= "anchor \"ipsec/*\"\n"; + # BEGIN OF firewall rules + /* default block logging? */ + $log = array(); + if (!isset($config['syslog']['nologdefaultblock'])) { + $log['block'] = "log"; + } + if (isset($config['syslog']['nologdefaultpass'])) { + $log['pass'] = "log"; + } + + $saved_tracker = $tracker; + + if (!isset($config['system']['ipv6allow'])) { + $ipfrules .= "# Allow IPv6 on loopback\n"; + $ipfrules .= "pass in {$log['pass']} quick on \$loopback inet6 all tracker {$increment_tracker($tracker)} label \"pass IPv6 loopback\"\n"; + $ipfrules .= "pass out {$log['pass']} quick on \$loopback inet6 all tracker {$increment_tracker($tracker)} label \"pass IPv6 loopback\"\n"; + $ipfrules .= "# Block all IPv6\n"; + $ipfrules .= "block in {$log['block']} quick inet6 all tracker {$increment_tracker($tracker)} label \"Block all IPv6\"\n"; + $ipfrules .= "block out {$log['block']} quick inet6 all tracker {$increment_tracker($tracker)} label \"Block all IPv6\"\n"; + } + + $saved_tracker += 100; + $tracker = $saved_tracker; + + if (!isset($config['system']['no_apipa_block'])) { + $ipfrules .= <<<EOD +# block IPv4 link-local. Per RFC 3927, link local "MUST NOT" be forwarded by a routing device, +# and clients "MUST NOT" send such packets to a router. FreeBSD won't route 169.254./16, but +# route-to can override that, causing problems such as in redmine #2073 +block in {$log['block']} quick from 169.254.0.0/16 to any tracker {$increment_tracker($tracker)} label "Block IPv4 link-local" +block in {$log['block']} quick from any to 169.254.0.0/16 tracker {$increment_tracker($tracker)} label "Block IPv4 link-local" + +EOD; + } + + $ipfrules .= <<<EOD +#--------------------------------------------------------------------------- +# default deny rules +#--------------------------------------------------------------------------- +block in {$log['block']} inet all tracker {$increment_tracker($tracker)} label "Default deny rule IPv4" +block out {$log['block']} inet all tracker {$increment_tracker($tracker)} label "Default deny rule IPv4" +block in {$log['block']} inet6 all tracker {$increment_tracker($tracker)} label "Default deny rule IPv6" +block out {$log['block']} inet6 all tracker {$increment_tracker($tracker)} label "Default deny rule IPv6" + +# IPv6 ICMP is not auxilary, it is required for operation +# See man icmp6(4) +# 1 unreach Destination unreachable +# 2 toobig Packet too big +# 128 echoreq Echo service request +# 129 echorep Echo service reply +# 133 routersol Router solicitation +# 134 routeradv Router advertisement +# 135 neighbrsol Neighbor solicitation +# 136 neighbradv Neighbor advertisement +pass {$log['pass']} quick inet6 proto ipv6-icmp from any to any icmp6-type {1,2,135,136} tracker {$increment_tracker($tracker)} keep state + +# Allow only bare essential icmpv6 packets (NS, NA, and RA, echoreq, echorep) +pass out {$log['pass']} quick inet6 proto ipv6-icmp from fe80::/10 to fe80::/10 icmp6-type {129,133,134,135,136} tracker {$increment_tracker($tracker)} keep state +pass out {$log['pass']} quick inet6 proto ipv6-icmp from fe80::/10 to ff02::/16 icmp6-type {129,133,134,135,136} tracker {$increment_tracker($tracker)} keep state +pass in {$log['pass']} quick inet6 proto ipv6-icmp from fe80::/10 to fe80::/10 icmp6-type {128,133,134,135,136} tracker {$increment_tracker($tracker)} keep state +pass in {$log['pass']} quick inet6 proto ipv6-icmp from ff02::/16 to fe80::/10 icmp6-type {128,133,134,135,136} tracker {$increment_tracker($tracker)} keep state +pass in {$log['pass']} quick inet6 proto ipv6-icmp from fe80::/10 to ff02::/16 icmp6-type {128,133,134,135,136} tracker {$increment_tracker($tracker)} keep state + +# We use the mighty pf, we cannot be fooled. +block {$log['block']} quick inet proto { tcp, udp } from any port = 0 to any tracker {$increment_tracker($tracker)} label "Block traffic from port 0" +block {$log['block']} quick inet proto { tcp, udp } from any to any port = 0 tracker {$increment_tracker($tracker)} label "Block traffic to port 0" +block {$log['block']} quick inet6 proto { tcp, udp } from any port = 0 to any tracker {$increment_tracker($tracker)} label "Block traffic from port 0" +block {$log['block']} quick inet6 proto { tcp, udp } from any to any port = 0 tracker {$increment_tracker($tracker)} label "Block traffic to port 0" + +# Snort package +block {$log['block']} quick from <snort2c> to any tracker {$increment_tracker($tracker)} label "Block snort2c hosts" +block {$log['block']} quick from any to <snort2c> tracker {$increment_tracker($tracker)} label "Block snort2c hosts" + +EOD; + + $saved_tracker += 100; + $tracker = $saved_tracker; + + $ipfrules .= filter_process_carp_rules($log); + + $saved_tracker += 100; + $tracker = $saved_tracker; + + $ipfrules .= "\n# SSH lockout\n"; + if (is_array($config['system']['ssh']) && !empty($config['system']['ssh']['port'])) { + $ipfrules .= "block in {$log['block']} quick proto tcp from <sshlockout> to (self) port "; + $ipfrules .= $config['system']['ssh']['port']; + $ipfrules .= " tracker {$increment_tracker($tracker)} label \"sshlockout\"\n"; + } else { + if ($config['system']['ssh']['port'] <> "") { + $sshport = $config['system']['ssh']['port']; + } else { + $sshport = 22; + } + if ($sshport) { + $ipfrules .= "block in {$log['block']} quick proto tcp from <sshlockout> to (self) port {$sshport} tracker {$increment_tracker($tracker)} label \"sshlockout\"\n"; + } + } + + $saved_tracker += 50; + $tracker = $saved_tracker; + + $ipfrules .= "\n# webConfigurator lockout\n"; + if (!$config['system']['webgui']['port']) { + if ($config['system']['webgui']['protocol'] == "http") { + $webConfiguratorlockoutport = "80"; + } else { + $webConfiguratorlockoutport = "443"; + } + } else { + $webConfiguratorlockoutport = $config['system']['webgui']['port']; + } + if ($webConfiguratorlockoutport) { + $ipfrules .= "block in {$log['block']} quick proto tcp from <webConfiguratorlockout> to (self) port {$webConfiguratorlockoutport} tracker {$increment_tracker($tracker)} label \"webConfiguratorlockout\"\n"; + } + + $saved_tracker += 100; + $tracker = $saved_tracker; + + /* + * Support for allow limiting of TCP connections by establishment rate + * Useful for protecting against sudden outbursts, etc. + */ + $ipfrules .= "block in {$log['block']} quick from <virusprot> to any tracker 1000000400 label \"virusprot overload table\"\n"; + + $saved_tracker += 100; + $tracker = $saved_tracker; + + /* if captive portal is enabled, ensure that access to this port + * is allowed on a locked down interface + */ + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cpcfg) { + if (!isset($cpcfg['enable'])) { + continue; + } + $cpinterfaces = explode(",", $cpcfg['interface']); + $cpiflist = array(); + $cpiplist = array(); + foreach ($cpinterfaces as $cpifgrp) { + if (!isset($FilterIflist[$cpifgrp])) { + continue; + } + $tmpif = get_real_interface($cpifgrp); + if (!empty($tmpif)) { + $cpiflist[] = "{$tmpif}"; + $cpipm = get_interface_ip($cpifgrp); + if (is_ipaddr($cpipm)) { + $cpiplist[] = $cpipm; + if (!is_array($config['virtualip']) || !is_array($config['virtualip']['vip'])) { + continue; + } + foreach ($config['virtualip']['vip'] as $vip) { + if (($vip['interface'] == $cpifgrp) && (($vip['mode'] == "carp") || ($vip['mode'] == "ipalias"))) { + $cpiplist[] = $vip['subnet']; + } + } + } + } + } + if (count($cpiplist) > 0 && count($cpiflist) > 0) { + $cpinterface = implode(" ", $cpiflist); + $cpaddresses = implode(" ", $cpiplist); + $listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : 8000 + ($cpcfg['zoneid'] + 1); + $listenporthttp = $cpcfg['listenporthttp'] ? $cpcfg['listenporthttp'] : 8000 + $cpcfg['zoneid']; + $portalias = $listenporthttps; + $portalias .= " {$listenporthttp}"; + $ipfrules .= "pass in {$log['pass']} quick on { {$cpinterface} } proto tcp from any to { {$cpaddresses} } port { {$portalias} } tracker {$increment_tracker($tracker)} keep state(sloppy)\n"; + $ipfrules .= "pass out {$log['pass']} quick on { {$cpinterface} } proto tcp from any to any flags any tracker {$increment_tracker($tracker)} keep state(sloppy)\n"; + } + } + } + + $bogontableinstalled = 0; + foreach ($FilterIflist as $on => $oc) { + $saved_tracker += 10; + $tracker = $saved_tracker; + + if (isset($config['system']['ipv6allow']) && ($oc['type6'] == "slaac" || $oc['type6'] == "dhcp6")) { + // The DHCPv6 client rules ***MUST BE ABOVE BOGONSV6!*** https://redmine.pfsense.org/issues/3395 + $ipfrules .= <<<EOD +# allow our DHCPv6 client out to the {$oc['descr']} +pass in {$log['pass']} quick on \${$oc['descr']} proto udp from fe80::/10 port = 546 to fe80::/10 port = 546 tracker {$increment_tracker($tracker)} label "{$fix_rule_label("allow dhcpv6 client in {$oc['descr']}")}" +pass in {$log['pass']} quick on \${$oc['descr']} proto udp from any port = 547 to any port = 546 tracker {$increment_tracker($tracker)} label "{$fix_rule_label("allow dhcpv6 client in {$oc['descr']}")}" +pass out {$log['pass']} quick on \${$oc['descr']} proto udp from any port = 546 to any port = 547 tracker {$increment_tracker($tracker)} label "{$fix_rule_label("allow dhcpv6 client out {$oc['descr']}")}" + +EOD; + } + + /* XXX: Not static but give a step of 1000 for each interface to at least be able to match rules. */ + $saved_tracker += 1000; + $tracker = $saved_tracker; + + /* block bogon networks */ + /* http://www.cymru.com/Documents/bogon-bn-nonagg.txt */ + /* file is automatically in cron every 3000 minutes */ + if (!isset($config['syslog']['nologbogons'])) { + $bogonlog = "log"; + } else { + $bogonlog = ""; + } + + if (isset($config['interfaces'][$on]['blockbogons'])) { + $ipfrules .= <<<EOD +# block bogon networks (IPv4) +# http://www.cymru.com/Documents/bogon-bn-nonagg.txt +block in $bogonlog quick on \${$oc['descr']} from <bogons> to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("block bogon IPv4 networks from {$oc['descr']}")}" + +EOD; + + if (isset($config['system']['ipv6allow'])) { + $ipfrules .= <<<EOD +# block bogon networks (IPv6) +# http://www.team-cymru.org/Services/Bogons/fullbogons-ipv6.txt +block in $bogonlog quick on \${$oc['descr']} from <bogonsv6> to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("block bogon IPv6 networks from {$oc['descr']}")}" + +EOD; + } + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + + $isbridged = false; + if (is_array($config['bridges']['bridged'])) { + foreach ($config['bridges']['bridged'] as $oc2) { + if (stristr($oc2['members'], $on)) { + $isbridged = true; + break; + } + } + } + + if ($oc['ip'] && !($isbridged) && isset($oc['spoofcheck'])) { + $ipfrules .= filter_rules_spoofcheck_generate($on, $oc, $log); + } + + /* block private networks ? */ + if (!isset($config['syslog']['nologprivatenets'])) { + $privnetlog = "log"; + } else { + $privnetlog = ""; + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + + if (isset($config['interfaces'][$on]['blockpriv'])) { + if ($isbridged == false) { + $ipfrules .= <<<EOD +# block anything from private networks on interfaces with the option set +block in $privnetlog quick on \${$oc['descr']} from 10.0.0.0/8 to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Block private networks from {$oc['descr']} block 10/8")}" +block in $privnetlog quick on \${$oc['descr']} from 127.0.0.0/8 to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Block private networks from {$oc['descr']} block 127/8")}" +block in $privnetlog quick on \${$oc['descr']} from 172.16.0.0/12 to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Block private networks from {$oc['descr']} block 172.16/12")}" +block in $privnetlog quick on \${$oc['descr']} from 192.168.0.0/16 to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Block private networks from {$oc['descr']} block 192.168/16")}" +block in $privnetlog quick on \${$oc['descr']} from fc00::/7 to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Block ULA networks from {$oc['descr']} block fc00::/7")}" + +EOD; + } + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + + switch ($oc['type']) { + case "pptp": + $ipfrules .= <<<EOD +# allow PPTP client +pass in {$log['pass']} on \${$oc['descr']} proto tcp from any to any port = 1723 flags S/SA modulate state tracker {$increment_tracker($tracker)} label "{$fix_rule_label("allow PPTP client on {$oc['descr']}")}" +pass in {$log['pass']} on \${$oc['descr']} proto gre from any to any keep state tracker {$increment_tracker($tracker)} label "{$fix_rule_label("allow PPTP client on {$oc['descr']}")}" + +EOD; + break; + case "dhcp": + $ipfrules .= <<<EOD +# allow our DHCP client out to the {$oc['descr']} +pass in {$log['pass']} on \${$oc['descr']} proto udp from any port = 67 to any port = 68 tracker {$increment_tracker($tracker)} label "{$fix_rule_label("allow dhcp client out {$oc['descr']}")}" +pass out {$log['pass']} on \${$oc['descr']} proto udp from any port = 68 to any port = 67 tracker {$increment_tracker($tracker)} label "{$fix_rule_label("allow dhcp client out {$oc['descr']}")}" +# Not installing DHCP server firewall rules for {$oc['descr']} which is configured for DHCP. + +EOD; + + break; + case "pppoe": + case "none": + /* XXX: Nothing to do in this case?! */ + break; + default: + /* allow access to DHCP server on interfaces */ + if (isset($config['dhcpd'][$on]['enable'])) { + $ipfrules .= <<<EOD +# allow access to DHCP server on {$oc['descr']} +pass in {$log['pass']} quick on \${$oc['descr']} proto udp from any port = 68 to 255.255.255.255 port = 67 tracker {$increment_tracker($tracker)} label "allow access to DHCP server" + +EOD; + if (is_ipaddrv4($oc['ip'])) { + $ipfrules .= <<<EOD +pass in {$log['pass']} quick on \${$oc['descr']} proto udp from any port = 68 to {$oc['ip']} port = 67 tracker {$increment_tracker($tracker)} label "allow access to DHCP server" +pass out {$log['pass']} quick on \${$oc['descr']} proto udp from {$oc['ip']} port = 67 to any port = 68 tracker {$increment_tracker($tracker)} label "allow access to DHCP server" + +EOD; + } + + if (is_ipaddrv4($oc['ip']) && $config['dhcpd'][$on]['failover_peerip'] <> "") { + $ipfrules .= <<<EOD +# allow access to DHCP failover on {$oc['descr']} from {$config['dhcpd'][$on]['failover_peerip']} +pass in {$log['pass']} quick on \${$oc['descr']} proto { tcp udp } from {$config['dhcpd'][$on]['failover_peerip']} to {$oc['ip']} port = 519 tracker {$increment_tracker($tracker)} label "allow access to DHCP failover" +pass in {$log['pass']} quick on \${$oc['descr']} proto { tcp udp } from {$config['dhcpd'][$on]['failover_peerip']} to {$oc['ip']} port = 520 tracker {$increment_tracker($tracker)} label "allow access to DHCP failover" + +EOD; + } + + } + break; + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + switch ($oc['type6']) { + case "6rd": + $ipfrules .= <<<EOD +# allow our proto 41 traffic from the 6RD border relay in +pass in {$log['pass']} on \${$oc['descr']} proto 41 from {$config['interfaces'][$on]['gateway-6rd']} to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Allow 6in4 traffic in for 6rd on {$oc['descr']}")}" +pass out {$log['pass']} on \${$oc['descr']} proto 41 from any to {$config['interfaces'][$on]['gateway-6rd']} tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Allow 6in4 traffic out for 6rd on {$oc['descr']}")}" + +EOD; + /* XXX: Really need to allow 6rd traffic coming in for v6 this is against default behaviour! */ + if (0 && is_ipaddrv6($oc['ipv6'])) { + $ipfrules .= <<<EOD +pass in {$log['pass']} on \${$oc['descr']} inet6 from any to {$oc['ipv6']}/{$oc['snv6']} tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Allow 6rd traffic in for 6rd on {$oc['descr']}")}" +pass out {$log['pass']} on \${$oc['descr']} inet6 from {$oc['ipv6']}/{$oc['snv6']} to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Allow 6rd traffic out for 6rd on {$oc['descr']}")}" + +EOD; + } + break; + case "6to4": + if (is_ipaddrv4($oc['ip'])) { + $ipfrules .= <<<EOD +# allow our proto 41 traffic from the 6to4 border relay in +pass in {$log['pass']} on \${$oc['descr']} proto 41 from any to {$oc['ip']} tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Allow 6in4 traffic in for 6to4 on {$oc['descr']}")}" +pass out {$log['pass']} on \${$oc['descr']} proto 41 from {$oc['ip']} to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Allow 6in4 traffic out for 6to4 on {$oc['descr']}")}" + +EOD; + } + /* XXX: Really need to allow 6to4 traffic coming in for v6 this is against default behaviour! */ + if (0 && is_ipaddrv6($oc['ipv6'])) { + $ipfrules .= <<<EOD +pass in {$log['pass']} on \${$oc['descr']} inet6 from any to {$oc['ipv6']}/{$oc['snv6']} tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Allow 6in4 traffic in for 6to4 on {$oc['descr']}")}" +pass out {$log['pass']} on \${$oc['descr']} inet6 from {$oc['ipv6']}/{$oc['snv6']} to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("Allow 6in4 traffic out for 6to4 on {$oc['descr']}")}" + +EOD; + } + break; + default: + if ((is_array($config['dhcpdv6'][$on]) && isset($config['dhcpdv6'][$on]['enable'])) || + (isset($oc['track6-interface'])) || + (is_array($config['dhcrelay6']) && !empty($config['dhcrelay6']['interface']) && in_array($on, explode(',', $config['dhcrelay6']['interface'])))) { + $ipfrules .= <<<EOD +# allow access to DHCPv6 server on {$oc['descr']} +# We need inet6 icmp for stateless autoconfig and dhcpv6 +pass {$log['pass']} quick on \${$oc['descr']} inet6 proto udp from fe80::/10 to fe80::/10 port = 546 tracker {$increment_tracker($tracker)} label "allow access to DHCPv6 server" +pass {$log['pass']} quick on \${$oc['descr']} inet6 proto udp from fe80::/10 to ff02::/16 port = 546 tracker {$increment_tracker($tracker)} label "allow access to DHCPv6 server" +pass {$log['pass']} quick on \${$oc['descr']} inet6 proto udp from fe80::/10 to ff02::/16 port = 547 tracker {$increment_tracker($tracker)} label "allow access to DHCPv6 server" +pass {$log['pass']} quick on \${$oc['descr']} inet6 proto udp from ff02::/16 to fe80::/10 port = 547 tracker {$increment_tracker($tracker)} label "allow access to DHCPv6 server" + +EOD; + if (is_ipaddrv6($oc['ipv6'])) { + $ipfrules .= <<<EOD +pass in {$log['pass']} quick on \${$oc['descr']} inet6 proto udp from fe80::/10 to {$oc['ipv6']} port = 546 tracker {$increment_tracker($tracker)} label "allow access to DHCPv6 server" +pass out {$log['pass']} quick on \${$oc['descr']} inet6 proto udp from {$oc['ipv6']} port = 547 to fe80::/10 tracker {$increment_tracker($tracker)} label "allow access to DHCPv6 server" + +EOD; + } + } + break; + } + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + + /* + * NB: The loopback rules are needed here since the antispoof would take precedence then. + * If you ever add the 'quick' keyword to the antispoof rules above move the loopback + * rules before them. + */ + $ipfrules .= <<<EOD + +# loopback +pass in {$log['pass']} on \$loopback inet all tracker {$increment_tracker($tracker)} label "pass IPv4 loopback" +pass out {$log['pass']} on \$loopback inet all tracker {$increment_tracker($tracker)} label "pass IPv4 loopback" +pass in {$log['pass']} on \$loopback inet6 all tracker {$increment_tracker($tracker)} label "pass IPv6 loopback" +pass out {$log['pass']} on \$loopback inet6 all tracker {$increment_tracker($tracker)} label "pass IPv6 loopback" +# let out anything from the firewall host itself and decrypted IPsec traffic +pass out {$log['pass']} inet all keep state allow-opts tracker {$increment_tracker($tracker)} label "let out anything IPv4 from firewall host itself" +pass out {$log['pass']} inet6 all keep state allow-opts tracker {$increment_tracker($tracker)} label "let out anything IPv6 from firewall host itself" + +EOD; + + $saved_tracker += 100; + $tracker = $saved_tracker; + foreach ($FilterIflist as $ifdescr => $ifcfg) { + if (isset($ifcfg['virtual'])) { + continue; + } + + $gw = get_interface_gateway($ifdescr); + if (is_ipaddrv4($gw) && is_ipaddrv4($ifcfg['ip'])) { + $ipfrules .= "pass out {$log['pass']} route-to ( {$ifcfg['if']} {$gw} ) from {$ifcfg['ip']} to !{$ifcfg['sa']}/{$ifcfg['sn']} tracker {$increment_tracker($tracker)} keep state allow-opts label \"let out anything from firewall host itself\"\n"; + if (is_array($ifcfg['vips'])) { + foreach ($ifcfg['vips'] as $vip) { + if (ip_in_subnet($vip['ip'], "{$ifcfg['sa']}/{$ifcfg['sn']}")) { + $ipfrules .= "pass out {$log['pass']} route-to ( {$ifcfg['if']} {$gw} ) from {$vip['ip']} to !{$ifcfg['sa']}/{$ifcfg['sn']} tracker {$increment_tracker($tracker)} keep state allow-opts label \"let out anything from firewall host itself\"\n"; + } else { + $ipfrules .= "pass out {$log['pass']} route-to ( {$ifcfg['if']} {$gw} ) from {$vip['ip']} to !" . gen_subnet($vip['ip'], $vip['sn']) . "/{$vip['sn']} tracker {$increment_tracker($tracker)} keep state allow-opts label \"let out anything from firewall host itself\"\n"; + } + } + } + } + + $gwv6 = get_interface_gateway_v6($ifdescr); + $stf = get_real_interface($ifdescr, "inet6"); + $pdlen = 64 - calculate_ipv6_delegation_length($ifdescr); + if (is_ipaddrv6($gwv6) && is_ipaddrv6($ifcfg['ipv6'])) { + $ipfrules .= "pass out {$log['pass']} route-to ( {$stf} {$gwv6} ) inet6 from {$ifcfg['ipv6']} to !{$ifcfg['ipv6']}/{$pdlen} tracker {$increment_tracker($tracker)} keep state allow-opts label \"let out anything from firewall host itself\"\n"; + if (is_array($ifcfg['vips6'])) { + foreach ($ifcfg['vips6'] as $vip) { + $ipfrules .= "pass out {$log['pass']} route-to ( {$stf} {$gwv6} ) inet6 from {$vip['ip']} to !{$vip['ip']}/{$pdlen} tracker {$increment_tracker($tracker)} keep state allow-opts label \"let out anything from firewall host itself\"\n"; + } + } + } + } + + + $saved_tracker += 300; + $tracker = $saved_tracker; + /* add ipsec interfaces */ + if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) { + $ipfrules .= "pass out {$log['pass']} on \$IPsec all tracker {$increment_tracker($tracker)} tracker {$increment_tracker($tracker)} keep state label \"IPsec internal host to host\"\n"; + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + if (is_array($config['system']['webgui']) && !isset($config['system']['webgui']['noantilockout'])) { + $alports = filter_get_antilockout_ports(); + + if (count($config['interfaces']) > 1 && !empty($FilterIflist['lan']['if'])) { + /* if antilockout is enabled, LAN exists and has + * an IP and subnet mask assigned + */ + $lanif = $FilterIflist['lan']['if']; + $ipfrules .= <<<EOD +# make sure the user cannot lock himself out of the webConfigurator or SSH +pass in {$log['pass']} quick on {$lanif} proto tcp from any to ({$lanif}) port { {$alports} } tracker {$increment_tracker($tracker)} keep state label "anti-lockout rule" + +EOD; + } else if (count($config['interfaces']) == 1) { + /* single-interface deployment, add to WAN */ + $wanif = $FilterIflist["wan"]['if']; + $ipfrules .= <<<EOD +# make sure the user cannot lock himself out of the webConfigurator or SSH +pass in {$log['pass']} quick on {$wanif} proto tcp from any to ({$wanif}) port { {$alports} } tracker {$increment_tracker($tracker)} keep state label "anti-lockout rule" + +EOD; + } + unset($alports); + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + /* PPTPd enabled? */ + if ($pptpdcfg['mode'] && ($pptpdcfg['mode'] != "off") && !isset($config['system']['disablevpnrules'])) { + if ($pptpdcfg['mode'] == "server") { + $pptpdtarget = get_interface_ip(); + } else { + $pptpdtarget = $pptpdcfg['redir']; + } + if (is_ipaddr($pptpdtarget) and is_array($FilterIflist['wan'])) { + $ipfrules .= <<<EOD +# PPTPd rules +pass in {$log['pass']} on \${$FilterIflist['wan']['descr']} proto tcp from any to $pptpdtarget port = 1723 tracker {$increment_tracker($tracker)} modulate state label "{$fix_rule_label("allow pptpd {$pptpdtarget}")}" +pass in {$log['pass']} on \${$FilterIflist['wan']['descr']} proto gre from any to any tracker {$increment_tracker($tracker)} keep state label "allow gre pptpd" + +EOD; + + } else { + /* this shouldnt ever happen but instead of breaking the clients ruleset + * log an error. + */ + log_error("ERROR! PPTP enabled but could not resolve the \$pptpdtarget"); + } + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + if (isset($config['nat']['rule']) && is_array($config['nat']['rule'])) { + foreach ($config['nat']['rule'] as $rule) { + if ((!isset($config['system']['disablenatreflection']) || $rule['natreflection'] == "enable") && + ($rule['natreflection'] != "disable")) { + $ipfrules .= "# NAT Reflection rules\n"; + $ipfrules .= <<<EOD +pass in {$log['pass']} inet tagged PFREFLECT tracker {$increment_tracker($tracker)} keep state label "NAT REFLECT: Allow traffic to localhost" + +EOD; + break; + } + } + } + + if (isset($config['filter']['rule'])) { + /* Pre-cache all our rules so we only have to generate them once */ + $rule_arr1 = array(); + $rule_arr2 = array(); + $rule_arr3 = array(); + $vpn_and_ppp_ifs = array("l2tp", "pptp", "pppoe", "enc0", "openvpn"); + /* + * NB: The order must be: Floating rules, then interface group and then regular ones. + */ + foreach ($config['filter']['rule'] as $rule) { + update_filter_reload_status("Pre-caching {$rule['descr']}..."); + if (isset ($rule['disabled'])) { + continue; + } + + if (!empty($rule['ipprotocol']) && $rule['ipprotocol'] == "inet46") { + if (isset($rule['floating'])) { + $rule['ipprotocol'] = "inet"; + $rule_arr1[] = filter_generate_user_rule_arr($rule); + $rule['ipprotocol'] = "inet6"; + $rule_arr1[] = filter_generate_user_rule_arr($rule); + } else if (is_interface_group($rule['interface']) || in_array($rule['interface'], $vpn_and_ppp_ifs)) { + $rule['ipprotocol'] = "inet"; + $rule_arr2[] = filter_generate_user_rule_arr($rule); + $rule['ipprotocol'] = "inet6"; + $rule_arr2[] = filter_generate_user_rule_arr($rule); + } else { + $rule['ipprotocol'] = "inet"; + $rule_arr3[] = filter_generate_user_rule_arr($rule); + $rule['ipprotocol'] = "inet6"; + $rule_arr3[] = filter_generate_user_rule_arr($rule); + } + $rule['ipprotocol'] = "inet46"; + } else { + if (isset($rule['floating'])) { + $rule_arr1[] = filter_generate_user_rule_arr($rule); + } else if (is_interface_group($rule['interface']) || in_array($rule['interface'], $vpn_and_ppp_ifs)) { + $rule_arr2[] = filter_generate_user_rule_arr($rule); + } else { + $rule_arr3[] = filter_generate_user_rule_arr($rule); + } + } + if ($rule['sched']) { + $time_based_rules = true; + } + } + + $ipfrules .= "\n# User-defined rules follow\n"; + $ipfrules .= "\nanchor \"userrules/*\"\n"; + /* Generate user rule lines */ + foreach ($rule_arr1 as $rule) { + if (isset($rule['disabled'])) { + continue; + } + if (!$rule['rule']) { + continue; + } + $ipfrules .= "{$rule['rule']} {$rule['descr']}\n"; + } + foreach ($rule_arr2 as $rule) { + if (isset($rule['disabled'])) { + continue; + } + if (!$rule['rule']) { + continue; + } + $ipfrules .= "{$rule['rule']} {$rule['descr']}\n"; + } + foreach ($rule_arr3 as $rule) { + if (isset($rule['disabled'])) { + continue; + } + if (!$rule['rule']) { + continue; + } + $ipfrules .= "{$rule['rule']} {$rule['descr']}\n"; + } + unset($rule_arr1, $rule_arr2, $rule_arr3); + } + + $saved_tracker += 100; + $tracker = $saved_tracker; + + /* pass traffic between statically routed subnets and the subnet on the + * interface in question to avoid problems with complicated routing + * topologies + */ + if (isset($config['filter']['bypassstaticroutes']) && is_array($config['staticroutes']['route']) && count($config['staticroutes']['route'])) { + $ipfrules .= "# Add rules to bypass firewall rules for static routes\n"; + foreach (get_staticroutes() as $route) { + $friendly = $GatewaysList[$route['gateway']]['friendlyiface']; + if (is_array($FilterIflist[$friendly])) { + $oc = $FilterIflist[$friendly]; + $routeent = explode("/", $route['network']); + unset($sa); + if (is_ipaddrv4($oc['ip'])) { + $sa = $oc['sa']; + $sn = $oc['sn']; + } + if ($sa && is_ipaddrv4($routeent[0])) { + $ipfrules .= <<<EOD +pass {$log['pass']} quick on \${$oc['descr']} proto tcp from {$sa}/{$sn} to {$route['network']} flags any tracker {$increment_tracker($tracker)} keep state(sloppy) label "pass traffic between statically routed subnets" +pass {$log['pass']} quick on \${$oc['descr']} from {$sa}/{$sn} to {$route['network']} tracker {$increment_tracker($tracker)} keep state(sloppy) label "pass traffic between statically routed subnets" +pass {$log['pass']} quick on \${$oc['descr']} proto tcp from {$route['network']} to {$sa}/{$sn} flags any tracker {$increment_tracker($tracker)} keep state(sloppy) label "pass traffic between statically routed subnets" +pass {$log['pass']} quick on \${$oc['descr']} from {$route['network']} to {$sa}/{$sn} tracker {$increment_tracker($tracker)} keep state(sloppy) label "pass traffic between statically routed subnets" + +EOD; + } + unset($sa); + if (is_ipaddrv6($oc['ipv6'])) { + $sa = $oc['sav6']; + $sn = $oc['snv6']; + } + if ($sa && is_ipaddrv6($routeent[0])) { + $ipfrules .= <<<EOD +pass {$log['pass']} quick on \${$oc['descr']} inet6 proto tcp from {$sa}/{$sn} to {$route['network']} flags any tracker {$increment_tracker($tracker)} keep state(sloppy) label "pass traffic between statically routed subnets" +pass {$log['pass']} quick on \${$oc['descr']} inet6 from {$sa}/{$sn} to {$route['network']} tracker {$increment_tracker($tracker)} keep state(sloppy) label "pass traffic between statically routed subnets" +pass {$log['pass']} quick on \${$oc['descr']} inet6 proto tcp from {$route['network']} to {$sa}/{$sn} flags any tracker {$increment_tracker($tracker)} keep state(sloppy) label "pass traffic between statically routed subnets" +pass {$log['pass']} quick on \${$oc['descr']} inet6 from {$route['network']} to {$sa}/{$sn} tracker {$increment_tracker($tracker)} keep state(sloppy) label "pass traffic between statically routed subnets" + +EOD; + } + } + } + } + + update_filter_reload_status(gettext("Creating IPsec rules...")); + $saved_tracker += 100000; + $tracker = $saved_tracker; + $ipfrules .= filter_generate_ipsec_rules($log); + + $ipfrules .= "\nanchor \"tftp-proxy/*\"\n"; + + $saved_tracker += 200; + $tracker = $saved_tracker; + update_filter_reload_status("Creating uPNP rules..."); + if (is_array($config['installedpackages']['miniupnpd']) && is_array($config['installedpackages']['miniupnpd']['config'][0])) { + if (isset($config['installedpackages']['miniupnpd']['config'][0]['enable'])) { + $ipfrules .= "anchor \"miniupnpd\"\n"; + } + + if (is_array($config['installedpackages']['miniupnpd'][0]['config'])) { + $upnp_interfaces = explode(",", $config['installedpackages']['miniupnpd'][0]['config']['iface_array']); + foreach ($upnp_interfaces as $upnp_if) { + if (is_array($FilterIflist[$upnp_if])) { + $oc = $FilterIflist[$upnp_if]; + unset($sa); + if ($oc['ip']) { + $sa = $oc['sa']; + $sn = $oc['sn']; + } + if ($sa) { + $ipfrules .= <<<EOD +pass in {$log['pass']} on \${$oc['descr']} proto tcp from {$sa}/{$sn} to 239.255.255.250/32 port 1900 tracker {$increment_tracker($tracker)} keep state label "pass multicast traffic to miniupnpd" + +EOD; + } + } + } + } + } + + return $ipfrules; +} + +function filter_rules_spoofcheck_generate($ifname, $ifcfg, $log) { + global $g, $config, $tracker; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_rules_spoofcheck_generate() being called $mt\n"; + } + $ipfrules = "antispoof {$log['block']} for \${$ifcfg['descr']} tracker {$tracker}\n"; + $tracker++; + + return $ipfrules; +} + +/* COMPAT Function */ +function tdr_install_cron($should_install) { + log_error(gettext("Please use filter_tdr_install_cron() function tdr_install_cron will be deprecated!")); + filter_tdr_install_cron($should_install); +} + +/****f* filter/filter_tdr_install_cron + * NAME + * filter_tdr_install_cron + * INPUTS + * $should_install true if the cron entry should be installed, false + * if the entry should be removed if it is present + * RESULT + * none + ******/ +function filter_tdr_install_cron($should_install) { + global $config, $g; + + if (platform_booting() == true) { + return; + } + + if (!is_array($config['cron'])) { + $config['cron'] = array(); + } + if (!is_array($config['cron']['item'])) { + $config['cron']['item'] = array(); + } + + $x = 0; + $is_installed = false; + foreach ($config['cron']['item'] as $item) { + if (strstr($item['command'], "filter_configure_sync")) { + $is_installed = true; + break; + } + $x++; + } + + switch ($should_install) { + case true: + if (!$is_installed) { + $cron_item = array(); + $cron_item['minute'] = "0,15,30,45"; + $cron_item['hour'] = "*"; + $cron_item['mday'] = "*"; + $cron_item['month'] = "*"; + $cron_item['wday'] = "*"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/etc/rc.filter_configure_sync"; + $config['cron']['item'][] = $cron_item; + write_config(gettext("Installed 15 minute filter reload for Time Based Rules")); + configure_cron(); + } + break; + case false: + if ($is_installed) { + unset($config['cron']['item'][$x]); + write_config(gettext("Removed 15 minute filter reload for Time Based Rules")); + configure_cron(); + } + break; + } +} + +/****f* filter/filter_get_time_based_rule_status + * NAME + * filter_get_time_based_rule_status + * INPUTS + * xml schedule block + * RESULT + * true/false - true if the rule should be installed + ******/ +/* + <schedules> + <schedule> + <name>ScheduleMultipleTime</name> + <descr>main descr</descr> + <time> + <position>0,1,2</position> + <hour>0:0-24:0</hour> + <desc>time range 2</desc> + </time> + <time> + <position>4,5,6</position> + <hour>0:0-24:0</hour> + <desc>time range 1</desc> + </time> + </schedule> + </schedules> +*/ +function filter_get_time_based_rule_status($schedule) { + + /* no schedule? rule should be installed */ + if (empty($schedule)) { + return true; + } + /* + * iterate through time blocks and determine + * if the rule should be installed or not. + */ + foreach ($schedule['timerange'] as $timeday) { + if (empty($timeday['month'])) { + $monthstatus = true; + } else { + $monthstatus = filter_tdr_month($timeday['month']); + } + if (empty($timeday['day'])) { + $daystatus = true; + } else { + $daystatus = filter_tdr_day($timeday['day']); + } + if (empty($timeday['hour'])) { + $hourstatus = true; + } else { + $hourstatus = filter_tdr_hour($timeday['hour']); + } + if (empty($timeday['position'])) { + $positionstatus = true; + } else { + $positionstatus = filter_tdr_position($timeday['position']); + } + + if ($monthstatus == true && $daystatus == true && $positionstatus == true && $hourstatus == true) { + return true; + } + } + + return false; +} + +function filter_tdr_day($schedule) { + global $g; + + if ($g['debug']) { + log_error("[TDR DEBUG] filter_tdr_day($schedule)"); + } + + /* + * Calculate day of month. + * IE: 29th of may + */ + $date = date("d"); + $defined_days = explode(",", $schedule); + foreach ($defined_days as $dd) { + if ($date == $dd) { + return true; + } + } + return false; +} +function filter_tdr_hour($schedule) { + global $g; + + /* $schedule should be a string such as 16:00-19:00 */ + $tmp = explode("-", $schedule); + $starting_time = strtotime($tmp[0]); + $ending_time = strtotime($tmp[1]); + $now = strtotime("now"); + if ($g['debug']) { + log_error("[TDR DEBUG] S: $starting_time E: $ending_time N: $now"); + } + if ($now >= $starting_time and $now < $ending_time) { + return true; + } + return false; +} + +function filter_tdr_position($schedule) { + global $g; + + /* + * Calculate position, ie: day of week. + * Sunday = 7, Monday = 1, Tuesday = 2 + * Weds = 3, Thursday = 4, Friday = 5, + * Saturday = 6 + * ... + */ + $weekday = date("w"); + if ($g['debug']) { + log_error("[TDR DEBUG] filter_tdr_position($schedule) $weekday"); + } + if ($weekday == 0) { + $weekday = 7; + } + $schedule_days = explode(",", $schedule); + foreach ($schedule_days as $day) { + if ($day == $weekday) { + return true; + } + } + return false; +} + +function filter_tdr_month($schedule) { + global $g; + + /* + * Calculate month + */ + $todays_month = date("n"); + $months = explode(",", $schedule); + if ($g['debug']) { + log_error("[TDR DEBUG] filter_tdr_month($schedule)"); + } + foreach ($months as $month) { + if ($month == $todays_month) { + return true; + } + } + return false; +} + +function filter_setup_logging_interfaces() { + global $config, $FilterIflist; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_setup_logging_interfaces() being called $mt\n"; + } + $rules = ""; + if (isset($FilterIflist['lan'])) { + $rules .= "set loginterface {$FilterIflist['lan']['if']}\n"; + } else if (isset($FilterIflist['wan'])) { + $rules .= "set loginterface {$FilterIflist['wan']['if']}\n"; + } + + return $rules; +} + +function filter_process_carp_rules($log) { + global $g, $config, $tracker; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_process_carp_rules() being called $mt\n"; + } + + $increment_tracker = 'filter_rule_tracker'; + $lines = ""; + /* return if there are no carp configured items */ + if (!empty($config['hasync']) or !empty($config['virtualip']['vip'])) { + $lines .= "block in {$log['block']} quick proto carp from (self) to any tracker {$increment_tracker($tracker)}\n"; + $lines .= "pass {$log['pass']} quick proto carp tracker {$increment_tracker($tracker)}\n"; + } + return $lines; +} + +/* Generate IPsec Filter Items */ +function filter_generate_ipsec_rules($log = array()) { + global $config, $g, $FilterIflist, $tracker; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_generate_ipsec_rules() being called $mt\n"; + } + + if (isset($config['system']['disablevpnrules'])) { + return "\n# VPN Rules not added disabled in System->Advanced.\n"; + } + + $increment_tracker = 'filter_rule_tracker'; + + $ipfrules = "\n# VPN Rules\n"; + if ((isset($config['ipsec']['enable'])) && + (is_array($config['ipsec']['phase1']))) { + /* step through all phase1 entries */ + foreach ($config['ipsec']['phase1'] as $ph1ent) { + $tracker += 10; + + if (isset ($ph1ent['disabled'])) { + continue; + } + /* determine local and remote peer addresses */ + if (!isset($ph1ent['mobile'])) { + if (!function_exists('ipsec_get_phase1_dst')) { + require_once("ipsec.inc"); + } + $rgip = ipsec_get_phase1_dst($ph1ent); + if (!$rgip) { + $ipfrules .= "# ERROR! Unable to determine remote IPsec peer address for {$ph1ent['remote-gateway']}\n"; + continue; + } + } else { + $rgip = " any "; + } + /* Determine best description */ + if ($ph1ent['descr']) { + $descr = $ph1ent['descr']; + } else { + $descr = $rgip; + } + /* + * Step through all phase2 entries and determine + * which protocols are in use with this peer + */ + $prot_used_esp = false; + $prot_used_ah = false; + if (is_array($config['ipsec']['phase2'])) { + foreach ($config['ipsec']['phase2'] as $ph2ent) { + /* only evaluate ph2's bound to our ph1 */ + if ($ph2ent['ikeid'] != $ph1ent['ikeid']) { + continue; + } + if ($ph2ent['protocol'] == 'esp') { + $prot_used_esp = true; + } + if ($ph2ent['protocol'] == 'ah') { + $prot_used_ah = true; + } + } + } + + if (strpos($ph1ent['interface'], "_vip")) { + $parentinterface = get_configured_carp_interface_list($ph1ent['interface'], '', 'iface'); + } else { + $parentinterface = $ph1ent['interface']; + } + if (empty($FilterIflist[$parentinterface]['descr'])) { + $ipfrules .= "# Could not locate interface for IPsec: {$descr}\n"; + continue; + } + + unset($gateway); + /* add endpoint routes to correct gateway on interface if the + remote endpoint is not on this interface's subnet */ + if ((isset($ph1ent['mobile']) || is_ipaddrv4($rgip)) && (interface_has_gateway($parentinterface))) { + $parentifsubnet = get_interface_ip($parentinterface) . "/" . get_interface_subnet($parentinterface); + if (isset($ph1ent['mobile']) || !ip_in_subnet($rgip, $parentifsubnet)) { + $gateway = get_interface_gateway($parentinterface); + $interface = $FilterIflist[$parentinterface]['if']; + + $route_to = " route-to ( $interface $gateway ) "; + $reply_to = " reply-to ( $interface $gateway ) "; + } + } else if ((isset($ph1ent['mobile']) || is_ipaddrv6($rgip)) && (interface_has_gatewayv6($parentinterface))) { + $parentifsubnet = get_interface_ipv6($parentinterface) . "/" . get_interface_subnetv6($parentinterface); + if (isset($ph1ent['mobile']) || !ip_in_subnet($rgip, $parentifsubnet)) { + $gateway = get_interface_gateway_v6($parentinterface); + $interface = $FilterIflist[$parentinterface]['if']; + + $route_to = " route-to ( $interface $gateway ) "; + $reply_to = " reply-to ( $interface $gateway ) "; + } + } + + /* Just in case */ + if ((!is_ipaddr($gateway) || empty($interface))) { + $route_to = " "; + $reply_to = " "; + } + + /* Add rules to allow IKE to pass */ + $shorttunneldescr = substr($descr, 0, 35); + $ipfrules .= <<<EOD +pass out {$log['pass']} $route_to proto udp from any to {$rgip} port = 500 tracker {$increment_tracker($tracker)} keep state label "IPsec: {$shorttunneldescr} - outbound isakmp" +pass in {$log['pass']} on \${$FilterIflist[$parentinterface]['descr']} $reply_to proto udp from {$rgip} to any port = 500 tracker {$increment_tracker($tracker)} keep state label "IPsec: {$shorttunneldescr} - inbound isakmp" + +EOD; + /* If NAT-T is enabled, add additional rules */ + if ($ph1ent['nat_traversal'] != "off") { + $ipfrules .= <<<EOD +pass out {$log['pass']} $route_to proto udp from any to {$rgip} port = 4500 tracker {$increment_tracker($tracker)} keep state label "IPsec: {$shorttunneldescr} - outbound nat-t" +pass in {$log['pass']} on \${$FilterIflist[$parentinterface]['descr']} $reply_to proto udp from {$rgip} to any port = 4500 tracker {$increment_tracker($tracker)} keep state label "IPsec: {$shorttunneldescr} - inbound nat-t" + +EOD; + } + /* Add rules to allow the protocols in use */ + if ($prot_used_esp) { + $ipfrules .= <<<EOD +pass out {$log['pass']} $route_to proto esp from any to {$rgip} tracker {$increment_tracker($tracker)} keep state label "IPsec: {$shorttunneldescr} - outbound esp proto" +pass in {$log['pass']} on \${$FilterIflist[$parentinterface]['descr']} $reply_to proto esp from {$rgip} to any tracker {$increment_tracker($tracker)} keep state label "IPsec: {$shorttunneldescr} - inbound esp proto" + +EOD; + } + if ($prot_used_ah) { + $ipfrules .= <<<EOD +pass out {$log['pass']} $route_to proto ah from any to {$rgip} tracker {$increment_tracker($tracker)} keep state label "IPsec: {$shorttunneldescr} - outbound ah proto" +pass in {$log['pass']} on \${$FilterIflist[$parentinterface]['descr']} $reply_to proto ah from {$rgip} to any tracker {$increment_tracker($tracker)} keep state label "IPsec: {$shorttunneldescr} - inbound ah proto" + +EOD; + } + } + } + return($ipfrules); +} + +function discover_pkg_rules($ruletype) { + global $config, $g, $aliases; + + /* Bail if there is no pkg directory, or if the package files might be out of sync. */ + if (!is_dir("/usr/local/pkg") || file_exists('/conf/needs_package_sync')) { + return ""; + } + + $rules = ""; + $files = glob("/usr/local/pkg/*.inc"); + foreach ($files as $pkg_inc) { + update_filter_reload_status(sprintf(gettext('Checking for %1$s PF hooks in package %2$s'), $ruletype, $pkg_inc)); + $pkg = basename($pkg_inc, ".inc"); + $pkg_generate_rules = "{$pkg}_generate_rules"; + if (!function_exists($pkg_generate_rules)) { + require_once($pkg_inc); + } + if (function_exists($pkg_generate_rules)) { + update_filter_reload_status(sprintf(gettext('Processing early %1$s rules for package %2$s'), $ruletype, $pkg_inc)); + $tmprules = $pkg_generate_rules("$ruletype"); + file_put_contents("{$g['tmp_path']}/rules.test.packages", $aliases . $tmprules); + $status = mwexec("/sbin/pfctl -nf {$g['tmp_path']}/rules.test.packages"); + if ($status <> 0) { + $errorrules = sprintf(gettext("There was an error while parsing the package filter rules for %s."), $pkg_inc) . "\n"; + log_error($errorrules); + file_put_contents("{$g['tmp_path']}/rules.packages.{$pkg}", "#{$errorrules}\n{$tmprules}\n"); + continue; + } + $rules .= $tmprules; + } + } + return $rules; +} + +function filter_get_antilockout_ports($wantarray = false) { + global $config; + + $lockoutports = array(); + $guiport = ($config['system']['webgui']['protocol'] == "https") ? "443" : "80"; + $guiport = empty($config['system']['webgui']['port']) ? $guiport : $config['system']['webgui']['port']; + $lockoutports[] = $guiport; + + if (($config['system']['webgui']['protocol'] == "https") && !isset($config['system']['webgui']['disablehttpredirect']) && ($guiport != "80")) { + $lockoutports[] = "80"; + } + + if (isset($config['system']['enablesshd'])) { + $lockoutports[] = empty($config['system']['ssh']['port']) ? "22" : $config['system']['ssh']['port']; + } + + if ($wantarray) { + return $lockoutports; + } else { + return implode(" ", $lockoutports); + } + +} + +?> diff --git a/src/etc/inc/filter_log.inc b/src/etc/inc/filter_log.inc new file mode 100644 index 0000000..11ee6de --- /dev/null +++ b/src/etc/inc/filter_log.inc @@ -0,0 +1,441 @@ +<?php +/* $Id$ */ +/* + filter_log.inc + part of pfSesne by Scott Ullrich + originally based on m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2009 Jim Pingle <myfirstname>@<mylastname>.org + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +/* + pfSense_BUILDER_BINARIES: /usr/sbin/fifolog_reader /usr/bin/tail /usr/local/sbin/clog + pfSense_MODULE: filter +*/ + +require 'config.inc'; + +global $buffer_rules_rdr, $buffer_rules_normal; +$buffer_rules_rdr = array(); +$buffer_rules_normal = array(); + +/* format filter logs */ +function conv_log_filter($logfile, $nentries, $tail = 50, $filtertext = "", $filterinterface = null) { + global $config, $g; + + /* Make sure this is a number before using it in a system call */ + if (!(is_numeric($tail))) { + return; + } + + if ($filtertext) { + $tail = 5000; + } + + /* Always do a reverse tail, to be sure we're grabbing the 'end' of the log. */ + $logarr = ""; + + if (isset($config['system']['usefifolog'])) { + exec("/usr/sbin/fifolog_reader " . escapeshellarg($logfile) . " | /usr/bin/grep 'filterlog:' | /usr/bin/tail -r -n {$tail}", $logarr); + } else { + exec("/usr/local/sbin/clog " . escapeshellarg($logfile) . " | grep -v \"CLOG\" | grep -v \"\033\" | /usr/bin/grep 'filterlog:' | /usr/bin/tail -r -n {$tail}", $logarr); + } + + $filterlog = array(); + $counter = 0; + + $filterinterface = strtoupper($filterinterface); + foreach ($logarr as $logent) { + if ($counter >= $nentries) { + break; + } + + $flent = parse_filter_line($logent); + if (!$filterinterface || ($filterinterface == $flent['interface'])) { + if ((($flent != "") && (!is_array($filtertext)) && (match_filter_line ($flent, $filtertext))) || + (($flent != "") && ( is_array($filtertext)) && (match_filter_field($flent, $filtertext)))) { + $counter++; + $filterlog[] = $flent; + } + } + } + /* Since the lines are in reverse order, flip them around if needed based on the user's preference */ + return isset($config['syslog']['reverse']) ? $filterlog : array_reverse($filterlog); +} + +function escape_filter_regex($filtertext) { + /* If the caller (user) has not already put a backslash before a slash, to escape it in the regex, */ + /* then this will do it. Take out any "\/" already there, then turn all ordinary "/" into "\/". */ + return str_replace('/', '\/', str_replace('\/', '/', $filtertext)); +} + +function match_filter_line($flent, $filtertext = "") { + if (!$filtertext) { + return true; + } + $filtertext = escape_filter_regex(str_replace(' ', '\s+', $filtertext)); + return @preg_match("/{$filtertext}/i", implode(" ", array_values($flent))); +} + +function match_filter_field($flent, $fields) { + foreach ($fields as $key => $field) { + if ($field == "All") { + continue; + } + if ((strpos($field, '!') === 0)) { + $field = substr($field, 1); + if (strtolower($key) == 'act') { + if (in_arrayi($flent[$key], explode(" ", $field))) { + return false; + } + } else { + $field_regex = escape_filter_regex($field); + if (@preg_match("/{$field_regex}/i", $flent[$key])) { + return false; + } + } + } else { + if (strtolower($key) == 'act') { + if (!in_arrayi($flent[$key], explode(" ", $field))) { + return false; + } + } else { + $field_regex = escape_filter_regex($field); + if (!@preg_match("/{$field_regex}/i", $flent[$key])) { + return false; + } + } + } + } + return true; +} + +// Case Insensitive in_array function +function in_arrayi($needle, $haystack) { + return in_array(strtolower($needle), array_map('strtolower', $haystack)); +} + +function parse_filter_line($line) { + global $config, $g; + + $flent = array(); + $log_split = ""; + + if (!preg_match("/(.*)\s(.*)\sfilterlog:\s(.*)$/", $line, $log_split)) { + return ""; + } + + list($all, $flent['time'], $host, $rule) = $log_split; + + $rule_data = explode(",", $rule); + $field = 0; + + $flent['rulenum'] = $rule_data[$field++]; + $flent['subrulenum'] = $rule_data[$field++]; + $flent['anchor'] = $rule_data[$field++]; + $flent['tracker'] = $rule_data[$field++]; + $flent['realint'] = $rule_data[$field++]; + $flent['interface'] = convert_real_interface_to_friendly_descr($flent['realint']); + $flent['reason'] = $rule_data[$field++]; + $flent['act'] = $rule_data[$field++]; + $flent['direction'] = $rule_data[$field++]; + $flent['version'] = $rule_data[$field++]; + + if ($flent['version'] == '4' || $flent['version'] == '6') { + if ($flent['version'] == '4') { + $flent['tos'] = $rule_data[$field++]; + $flent['ecn'] = $rule_data[$field++]; + $flent['ttl'] = $rule_data[$field++]; + $flent['id'] = $rule_data[$field++]; + $flent['offset'] = $rule_data[$field++]; + $flent['flags'] = $rule_data[$field++]; + $flent['protoid'] = $rule_data[$field++]; + $flent['proto'] = strtoupper($rule_data[$field++]); + } else { + $flent['class'] = $rule_data[$field++]; + $flent['flowlabel'] = $rule_data[$field++]; + $flent['hlim'] = $rule_data[$field++]; + $flent['proto'] = $rule_data[$field++]; + $flent['protoid'] = $rule_data[$field++]; + } + + $flent['length'] = $rule_data[$field++]; + $flent['srcip'] = $rule_data[$field++]; + $flent['dstip'] = $rule_data[$field++]; + + if ($flent['protoid'] == '6' || $flent['protoid'] == '17') { // TCP or UDP + $flent['srcport'] = $rule_data[$field++]; + $flent['dstport'] = $rule_data[$field++]; + + $flent['src'] = $flent['srcip'] . ':' . $flent['srcport']; + $flent['dst'] = $flent['dstip'] . ':' . $flent['dstport']; + + $flent['datalen'] = $rule_data[$field++]; + if ($flent['protoid'] == '6') { // TCP + $flent['tcpflags'] = $rule_data[$field++]; + $flent['seq'] = $rule_data[$field++]; + $flent['ack'] = $rule_data[$field++]; + $flent['window'] = $rule_data[$field++]; + $flent['urg'] = $rule_data[$field++]; + $flent['options'] = explode(";",$rule_data[$field++]); + } + } else if ($flent['protoid'] == '1') { // ICMP + $flent['src'] = $flent['srcip']; + $flent['dst'] = $flent['dstip']; + + $flent['icmp_type'] = $rule_data[$field++]; + + switch ($flent['icmp_type']) { + case "request": + case "reply": + $flent['icmp_id'] = $rule_data[$field++]; + $flent['icmp_seq'] = $rule_data[$field++]; + break; + case "unreachproto": + $flent['icmp_dstip'] = $rule_data[$field++]; + $flent['icmp_protoid'] = $rule_data[$field++]; + break; + case "unreachport": + $flent['icmp_dstip'] = $rule_data[$field++]; + $flent['icmp_protoid'] = $rule_data[$field++]; + $flent['icmp_port'] = $rule_data[$field++]; + break; + case "unreach": + case "timexceed": + case "paramprob": + case "redirect": + case "maskreply": + $flent['icmp_descr'] = $rule_data[$field++]; + break; + case "needfrag": + $flent['icmp_dstip'] = $rule_data[$field++]; + $flent['icmp_mtu'] = $rule_data[$field++]; + break; + case "tstamp": + $flent['icmp_id'] = $rule_data[$field++]; + $flent['icmp_seq'] = $rule_data[$field++]; + break; + case "tstampreply": + $flent['icmp_id'] = $rule_data[$field++]; + $flent['icmp_seq'] = $rule_data[$field++]; + $flent['icmp_otime'] = $rule_data[$field++]; + $flent['icmp_rtime'] = $rule_data[$field++]; + $flent['icmp_ttime'] = $rule_data[$field++]; + break; + default : + $flent['icmp_descr'] = $rule_data[$field++]; + break; + } + + } else if ($flent['protoid'] == '2') { // IGMP + $flent['src'] = $flent['srcip']; + $flent['dst'] = $flent['dstip']; + } else if ($flent['protoid'] == '112') { // CARP + $flent['type'] = $rule_data[$field++]; + $flent['ttl'] = $rule_data[$field++]; + $flent['vhid'] = $rule_data[$field++]; + $flent['version'] = $rule_data[$field++]; + $flent['advskew'] = $rule_data[$field++]; + $flent['advbase'] = $rule_data[$field++]; + } + } else { + if ($g['debug']) { + log_error(sprintf(gettext("There was a error parsing rule number: %s. Please report to mailing list or forum."), $flent['rulenum'])); + } + return ""; + } + + /* If there is a src, a dst, and a time, then the line should be usable/good */ + if (!((trim($flent['src']) == "") || (trim($flent['dst']) == "") || (trim($flent['time']) == ""))) { + return $flent; + } else { + if ($g['debug']) { + log_error(sprintf(gettext("There was a error parsing rule: %s. Please report to mailing list or forum."), $errline)); + } + return ""; + } +} + +function get_port_with_service($port, $proto) { + if (!$port) { + return ''; + } + + $service = getservbyport($port, $proto); + $portstr = ""; + if ($service) { + $portstr = sprintf('<span title="' . gettext('Service %1$s/%2$s: %3$s') . '">' . htmlspecialchars($port) . '</span>', $port, $proto, $service); + } else { + $portstr = htmlspecialchars($port); + } + return ':' . $portstr; +} + +function find_rule_by_number($rulenum, $trackernum, $type="block") { + global $g; + + /* Passing arbitrary input to grep could be a Very Bad Thing(tm) */ + if (!is_numeric($rulenum) || !is_numeric($trackernum) || !in_array($type, array('pass', 'block', 'match', 'rdr'))) { + return; + } + + if ($trackernum == "0") { + $lookup_pattern = "^@{$rulenum}\([0-9]+\)[[:space:]]{$type}[[:space:]].*[[:space:]]log[[:space:]]"; + } else { + $lookup_pattern = "^@[0-9]+\({$trackernum}\)[[:space:]]{$type}[[:space:]].*[[:space:]]log[[:space:]]"; + } + + /* At the moment, miniupnpd is the only thing I know of that + generates logging rdr rules */ + unset($buffer); + if ($type == "rdr") { + $_gb = exec("/sbin/pfctl -vvPsn -a \"miniupnpd\" | /usr/bin/egrep " . escapeshellarg("^@{$rulenum}"), $buffer); + } else { + if (file_exists("{$g['tmp_path']}/rules.debug")) { + $_gb = exec("/sbin/pfctl -vvPnf {$g['tmp_path']}/rules.debug 2>/dev/null | /usr/bin/egrep " . escapeshellarg($lookup_pattern), $buffer); + } else { + $_gb = exec("/sbin/pfctl -vvPsr | /usr/bin/egrep " . escapeshellarg($lookup_pattern), $buffer); + } + } + if (is_array($buffer)) { + return $buffer[0]; + } + + return ""; +} + +function buffer_rules_load() { + global $g, $buffer_rules_rdr, $buffer_rules_normal; + unset($buffer, $buffer_rules_rdr, $buffer_rules_normal); + /* Redeclare globals after unset to work around PHP */ + global $buffer_rules_rdr, $buffer_rules_normal; + $buffer_rules_rdr = array(); + $buffer_rules_normal = array(); + + $_gb = exec("/sbin/pfctl -vvPsn -a \"miniupnpd\" | grep '^@'", $buffer); + if (is_array($buffer)) { + foreach ($buffer as $line) { + list($key, $value) = explode (" ", $line, 2); + $buffer_rules_rdr[$key] = $value; + } + } + unset($buffer, $_gb); + if (file_exists("{$g['tmp_path']}/rules.debug")) { + $_gb = exec("/sbin/pfctl -vvPnf {$g['tmp_path']}/rules.debug 2>/dev/null | /usr/bin/egrep '^@[0-9]+\([0-9]+\)[[:space:]].*[[:space:]]log[[:space:]]' | /usr/bin/egrep -v '^@[0-9]+\([0-9]+\)[[:space:]](nat|rdr|binat|no|scrub)'", $buffer); + } else { + $_gb = exec("/sbin/pfctl -vvPsr | /usr/bin/egrep '^@[0-9]+\([0-9]+\)[[:space:]].*[[:space:]]log[[:space:]]'", $buffer); + } + + if (is_array($buffer)) { + foreach ($buffer as $line) { + list($key, $value) = explode (" ", $line, 2); + # pfctl rule number output with tracker number: @dd(dddddddddd) + $matches = array(); + if (preg_match('/\@(?P<rulenum>\d+)\((?<trackernum>\d+)\)/', $key, $matches) == 1) { + if ($matches['trackernum'] > 0) { + $key = $matches['trackernum']; + } else { + $key = "@{$matches['rulenum']}"; + } + } + $buffer_rules_normal[$key] = $value; + } + } + unset($_gb, $buffer); +} + +function buffer_rules_clear() { + unset($GLOBALS['buffer_rules_normal']); + unset($GLOBALS['buffer_rules_rdr']); +} + +function find_rule_by_number_buffer($rulenum, $trackernum, $type) { + global $g, $buffer_rules_rdr, $buffer_rules_normal; + + if ($trackernum == "0") { + $lookup_key = "@{$rulenum}"; + } else { + $lookup_key = $trackernum; + } + + if ($type == "rdr") { + $ruleString = $buffer_rules_rdr[$lookup_key]; + //TODO: get the correct 'description' part of a RDR log line. currently just first 30 characters.. + $rulename = substr($ruleString,0,30); + } else { + $ruleString = $buffer_rules_normal[$lookup_key]; + list(,$rulename,) = explode("\"",$ruleString); + $rulename = str_replace("USER_RULE: ",'<img src="/themes/'.$g['theme'].'/images/icons/icon_frmfld_user.png" width="11" height="12" title="USER_RULE" alt="USER_RULE"/> ',$rulename); + } + return "{$rulename} ({$lookup_key})"; +} + +function find_action_image($action) { + global $g; + if ((strstr(strtolower($action), "p")) || (strtolower($action) == "rdr")) { + return "/themes/{$g['theme']}/images/icons/icon_pass.gif"; + } else if (strstr(strtolower($action), "r")) { + return "/themes/{$g['theme']}/images/icons/icon_reject.gif"; + } else { + return "/themes/{$g['theme']}/images/icons/icon_block.gif"; + } +} + +/* AJAX specific handlers */ +function handle_ajax($nentries, $tail = 50) { + global $config; + if ($_GET['lastsawtime'] or $_POST['lastsawtime']) { + global $filter_logfile,$filterent; + if ($_GET['lastsawtime']) { + $lastsawtime = $_GET['lastsawtime']; + } + if ($_POST['lastsawtime']) { + $lastsawtime = $_POST['lastsawtime']; + } + /* compare lastsawrule's time stamp to filter logs. + * afterwards return the newer records so that client + * can update AJAX interface screen. + */ + $new_rules = ""; + $filterlog = conv_log_filter($filter_logfile, $nentries, $tail); + /* We need this to always be in forward order for the AJAX update to work properly */ + $filterlog = isset($config['syslog']['reverse']) ? array_reverse($filterlog) : $filterlog; + foreach ($filterlog as $log_row) { + $row_time = strtotime($log_row['time']); + $img = "<img border='0' src='" . find_action_image($log_row['act']) . "' alt={$log_row['act']} title={$log_row['act']} />"; + if ($row_time > $lastsawtime) { + if ($log_row['proto'] == "TCP") { + $log_row['proto'] .= ":{$log_row['tcpflags']}"; + } + + $btn = "<a href=\"#\" class=\"btn btn-danger btn-xs\" onClick=\"javascript:getURL('diag_logs_filter.php?getrulenum={$log_row['rulenum']},{$log_row['rulenum']}', outputrule);\">" . gettext("Block") . " </a>"; + $new_rules .= "{$btn}||{$log_row['time']}||{$log_row['interface']}||{$log_row['srcip']}||{$log_row['srcport']}||{$log_row['dstip']}||{$log_row['dstport']}||{$log_row['proto']}||{$log_row['version']}||" . time() . "||\n"; + } + } + echo $new_rules; + exit; + } +} + +?> diff --git a/src/etc/inc/functions.inc b/src/etc/inc/functions.inc new file mode 100644 index 0000000..2c8f4c8 --- /dev/null +++ b/src/etc/inc/functions.inc @@ -0,0 +1,158 @@ +<?php +/* $Id$ */ +/* + functions.inc + Copyright (C) 2004-2006 Scott Ullrich + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_MODULE: utils + +*/ + +/* BEGIN compatibility goo with HEAD */ +if (!function_exists("gettext")) { + function gettext($text) { + return $text; + } +} + +if (!function_exists("pfSenseHeader")) { + /****f* pfsense-utils/pfSenseHeader + * NAME + * pfSenseHeader + * INPUTS + * none + * RESULT + * Javascript header change or browser Location: + ******/ + function pfSenseHeader($text) { + global $_SERVER; + if (isAjax()) { + if ($_SERVER['HTTPS'] == "on") { + $protocol = "https"; + } else { + $protocol = "http"; + } + + $port = ":{$_SERVER['SERVER_PORT']}"; + if ($_SERVER['SERVER_PORT'] == "80" && $protocol == "http") { + $port = ""; + } + if ($_SERVER['SERVER_PORT'] == "443" && $protocol == "https") { + $port = ""; + } + $complete_url = "{$protocol}://{$_SERVER['SERVER_NAME']}{$port}/{$text}"; + echo "\ndocument.location.href = '{$complete_url}';\n"; + } else { + header("Location: $text"); + } + } +} +/* END compatibility goo with HEAD */ + +/*fetch menu notices function*/ +if (!function_exists("get_menu_messages")) { + function get_menu_messages() { + global $g, $config; + if (are_notices_pending()) { + $notices = get_notices(); + $requests = array(); + + ## Get Query Arguments from URL ### + foreach ($_REQUEST as $key => $value) { + if ($key != "PHPSESSID") { + $requests[] = $key.'='.$value; + } + } + if (is_array($requests)) { + $request_string = implode("&", $requests); + } + + if (is_array($notices)) { + $notice_msgs = "<table colspan=\'6\' id=\'notice_table\'>"; + $alert_style="style=\'color:#ffffff; filter:Glow(color=#ff0000, strength=12);\' "; + $notice = "<a href=\'#\' onclick=notice_action(\'acknowledge\',\'all\');domTT_close(this); {$alert_style}>".gettext("Acknowledge All Notices")."</a>"; + $alert_link="title=\'".gettext("Click to Acknowledge")."\' {$alert_style}"; + $domtt_width=500; + foreach ($notices as $key => $value) { + $date = date("m-d-y H:i:s", $key); + $noticemsg = ($value['notice'] != "" ? $value['notice'] : $value['id']); + $noticemsg = preg_replace("/(\"|\'|\n|<.?\w+>)/i", "", $noticemsg); + if ((strlen($noticemsg)* 8) > $domtt_width) { + $domtt_width=(strlen($noticemsg) *8); + } + if ((strlen($noticemsg)* 8) > 900) { + $domtt_width= 900; + } + $alert_action ="onclick=notice_action(\'acknowledge\',\'{$key}\');domTT_close(this);jQuery(this).parent().parent().remove();"; + $notice_msgs .= "<tr><td valign=\'top\' width=\'120\'><a href=\'#\' {$alert_link} {$alert_action}>{$date}</a></td><td valign=\'top\'><a href=\'#\' {$alert_link} {$alert_action}>[ ".htmlspecialchars($noticemsg)."]</a></td></tr>"; + } + $notice_msgs .="</table>"; + + $domtt= "onclick=\"domTT_activate(this, event, 'caption', '{$notice}','content', '<br />{$notice_msgs}', 'trail', false, 'delay', 0, 'fade', 'both', 'fadeMax', 93, 'styleClass', 'niceTitle','width','{$domtt_width}','y',5,'type', 'sticky');\""; + $menu_messages="<div id='alerts'>\n"; + if (count($notices) == 1) { + $msg= sprintf("%1$02d", count($notices)) . " " . gettext("unread notice"); + } else { + $msg= sprintf("%1$02d", count($notices)) . " " . gettext("unread notices"); + } + $menu_messages .= "<div id='marquee-text' style='z-index:1001;'><a href='#' {$domtt}><b> .:. {$msg} .:. </b></a></div>\n"; + $menu_messages .= "</div>\n"; + } + } else { + $menu_messages = '<div id="hostname">'; + $menu_messages .= $config['system']['hostname'] . "." . $config['system']['domain']; + $menu_messages .= '</div>'; + } + return ($menu_messages); + } +} + +if (!function_exists("dom_title")) { + function dom_title($title_msg, $width=NULL) { + $width=preg_replace("/\D+/", "", $width); + if (!empty($width)) { + $width=",'width',$width"; + } + if (!empty($title_msg)) { + $title_msg=preg_replace("/\s+/", " ", $title_msg); + $title_msg=preg_replace("/'/", "\'", $title_msg); + return "onmouseout=\"this.style.color = ''; domTT_mouseout(this, event);\" onmouseover=\"domTT_activate(this, event, 'content', '{$title_msg}', 'trail', true, 'delay', 250, 'fade', 'both', 'fadeMax', 93, 'styleClass', 'niceTitle' $width);\""; + } + } +} +/* include all configuration functions */ +require_once("interfaces.inc"); +require_once("gwlb.inc"); +require_once("services.inc"); +require_once("pfsense-utils.inc"); +require_once("certs.inc"); +require_once("system.inc"); +require_once("vslb.inc"); + +?> diff --git a/src/etc/inc/globals.inc b/src/etc/inc/globals.inc new file mode 100644 index 0000000..66107c0 --- /dev/null +++ b/src/etc/inc/globals.inc @@ -0,0 +1,195 @@ +<?php +/* $Id$ */ +/* + globals.inc + part of pfSense (https://www.pfsense.org) + Copyright (C) 2004-2010 Scott Ullrich + + Originally Part of m0n0wall + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_MODULE: utils + +*/ + +global $g; +$g = array( + "base_packages" => "siproxd", + "event_address" => "unix:///var/run/check_reload_status", + "factory_shipped_username" => "admin", + "factory_shipped_password" => "pfsense", + "upload_path" => "/root", + "dhcpd_chroot_path" => "/var/dhcpd", + "unbound_chroot_path" => "/var/unbound", + "var_path" => "/var", + "varrun_path" => "/var/run", + "varetc_path" => "/var/etc", + "vardb_path" => "/var/db", + "varlog_path" => "/var/log", + "etc_path" => "/etc", + "tmp_path" => "/tmp", + "conf_path" => "/conf", + "conf_default_path" => "/conf.default", + "cf_path" => "/cf", + "cf_conf_path" => "/cf/conf", + "www_path" => "/usr/local/www", + "xml_rootobj" => "pfsense", + "admin_group" => "admins", + "product_name" => "pfSense", + "product_version" => trim(file_get_contents("/etc/version"), " \n"), + "product_copyright" => "Electric Sheep Fencing LLC", + "product_copyright_url" => "http://www.electricsheepfencing.com", + "product_copyright_years" => "2004 - ".date("Y"), + "product_website" => "www.pfsense.org", + "product_website_footer" => "https://www.pfsense.org/?gui=bootstrap", + "product_email" => "coreteam@pfsense.org", + "hideplatform" => false, + "hidedownloadbackup" => false, + "hidebackupbeforeupgrade" => false, + "disablethemeselection" => false, + "disablehelpmenu" => false, + "disablehelpicon" => false, + "disablecrashreporter" => false, + "crashreporterurl" => "https://crashreporter.pfsense.org/crash_reporter.php", + "debug" => false, + "latest_config" => "12.1", + "nopkg_platforms" => array("cdrom"), + "minimum_ram_warning" => "101", + "minimum_ram_warning_text" => "128 MB", + "wan_interface_name" => "wan", + "xmlrpcbaseurl" => "https://packages.pfsense.org", + "captiveportal_path" => "/usr/local/captiveportal", + "captiveportal_element_path" => "/var/db/cpelements", + "captiveportal_element_sizelimit" => 1048576, + "xmlrpcpath" => "/xmlrpc.php", + "embeddedbootupslice" => "/dev/ad0a", + "services_dhcp_server_enable" => true, + "wireless_regex" => "/^(ndis|wi|ath|an|ral|ural|iwi|wlan|rum|run|bwn|zyd|mwl|bwi|ipw|iwn|malo|uath|upgt|urtw|wpi)/", + "help_base_url" => "/help.php", + "pkg_prefix" => "pfSense-pkg-" +); + +/* IP TOS flags */ +$iptos = array("lowdelay", "throughput", "reliability"); + +/* TCP flags */ +$tcpflags = array("syn", "ack", "fin", "rst", "psh", "urg", "ece", "cwr"); + +if (file_exists("/etc/platform")) { + $arch = php_uname("m"); + + if (strstr($g['product_version'], "-RELEASE")) { + /* This is only necessary for RELEASE */ + $arch = ($arch == "i386") ? "" : '/' . $arch; + /* Full installs and NanoBSD use the same update directory and manifest in 2.x */ + $g['update_url']="https://updates.pfsense.org/_updaters{$arch}"; + $g['update_manifest']="https://updates.pfsense.org/manifest"; + } else { + /* Full installs and NanoBSD use the same update directory and manifest in 2.x */ + $g['update_url']="https://snapshots.pfsense.org/FreeBSD_releng/10.1/{$arch}/pfSense_HEAD/.updaters/"; + $g['update_manifest']="https://updates.pfSense.org/manifest"; + } + + $g['platform'] = trim(file_get_contents("/etc/platform")); + if ($g['platform'] == "nanobsd") { + $g['firmware_update_text']="pfSense-*.img.gz"; + $g['hidedownloadbackup'] = true; + $g['hidebackupbeforeupgrade'] = true; + + } else { + $g['firmware_update_text']="pfSense-*.tgz"; + } +} + +/* Default sysctls */ +$sysctls = array("net.inet.ip.portrange.first" => "1024", + "net.inet.tcp.blackhole" => "2", + "net.inet.udp.blackhole" => "1", + "net.inet.ip.random_id" => "1", + "net.inet.tcp.drop_synfin" => "1", + "net.inet.ip.redirect" => "1", + "net.inet6.ip6.redirect" => "1", + "net.inet6.ip6.use_tempaddr" => "0", + "net.inet6.ip6.prefer_tempaddr" => "0", + "net.inet.tcp.syncookies" => "1", + "net.inet.tcp.recvspace" => "65228", + "net.inet.tcp.sendspace" => "65228", + "net.inet.ip.fastforwarding" => "0", + "net.inet.tcp.delayed_ack" => "0", + "net.inet.udp.maxdgram" => "57344", + "net.link.bridge.pfil_onlyip" => "0", + "net.link.bridge.pfil_member" => "1", + "net.link.bridge.pfil_bridge" => "0", + "net.link.tap.user_open" => "1", + "kern.randompid" => "347", + "net.inet.ip.intr_queue_maxlen" => "1000", + "hw.syscons.kbd_reboot" => "0", + "net.inet.tcp.log_debug" => "0", + "net.inet.tcp.tso" => "1", + "net.inet.icmp.icmplim" => "0", + "vfs.read_max" => "32", + "kern.ipc.maxsockbuf" => "4262144", + "net.inet.ip.process_options" => 0, + "kern.random.sys.harvest.interrupt" => 0, + "kern.random.sys.harvest.point_to_point" => 0, + "kern.random.sys.harvest.ethernet" => 0, + "net.route.netisr_maxqlen" => 1024, + "net.inet.udp.checksum" => 1, + "net.bpf.zerocopy_enable" => 1, + "net.inet.icmp.reply_from_interface" => 1, + "net.inet6.ip6.rfc6204w3" => 1, + "net.enc.out.ipsec_bpf_mask" => "0x0001", + "net.enc.out.ipsec_filter_mask" => "0x0001", + "net.enc.in.ipsec_bpf_mask" => "0x0002", + "net.enc.in.ipsec_filter_mask" => "0x0002", + "net.key.preferred_oldsa" => "0", + "net.inet.carp.senderr_demotion_factor" => 0, /* Do not demote CARP for interface send errors */ + "net.pfsync.carp_demotion_factor" => 0 /* Do not demote CARP for pfsync errors */ +); + +/* Include override values for the above if needed. If the file doesn't exist, don't try to load it. */ +if (file_exists("/etc/inc/globals_override.inc")) { + @include("globals_override.inc"); +} + +function platform_booting($on_console = false) { + global $g; + + if ($g['booting'] || file_exists("{$g['varrun_path']}/booting")) { + if ($on_console == false || php_sapi_name() != 'fpm-fcgi') { + return true; + } + } + + return false; +} + +if (file_exists("{$g['cf_conf_path']}/enableserial_force")) { + $g['enableserial_force'] = true; +} + +$config_parsed = false; + +?> diff --git a/src/etc/inc/gmirror.inc b/src/etc/inc/gmirror.inc new file mode 100644 index 0000000..24508e9 --- /dev/null +++ b/src/etc/inc/gmirror.inc @@ -0,0 +1,348 @@ +<?php +/* + gmirror.inc + Copyright (C) 2009-2014 Jim Pingle + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INClUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +global $balance_methods; +$balance_methods = array("load", "prefer", "round-robin", "split"); + +/* Create a status array for each mirror and its disk components. */ +function gmirror_get_status() { + $status = ""; + exec("/sbin/gmirror status -s", $status); + $mirrors = array(); + + /* Empty output = no mirrors found */ + if (count($status) > 0) { + /* Loop through gmirror status output. */ + foreach ($status as $line) { + /* Split the line by whitespace */ + $all = preg_split("/[\s\t]+/", trim($line), 3); + if (count($all) == 3) { + /* If there are three items on a line, it is mirror name, status, and component */ + $currentmirror = basename($all[0]); + $mirrors[$currentmirror]['name'] = basename($all[0]); + $mirrors[$currentmirror]['status'] = $all[1]; + if (!is_array($mirrors[$currentmirror]['components'])) { + $mirrors[$currentmirror]['components'] = array(); + } + $mirrors[$currentmirror]['components'][] = $all[2]; + } + } + } + /* Return an hash of mirrors and components */ + return $mirrors; +} + +/* Get only status word for a single mirror. */ +function gmirror_get_status_single($mirror) { + $status = ""; + $mirror_status = gmirror_get_status(); + return $mirror_status[$mirror]['status']; +} + +/* Generate an HTML formatted status for mirrors and disks in a small format for the widget */ +function gmirror_html_status() { + $mirrors = gmirror_get_status(); + $output = ""; + if (count($mirrors) < 1) { +?> +<div class="alert"> + <p>No Mirrors Found</p> +</div> +<?php + return; + } + +?> +<table class="table table-striped table-hover"> + <thead> + <tr> + <th>Name</td> + <th>Status</td> + <th>Component</td> + </tr> + </thead> + <tbody> +<?php foreach ($mirrors as $mirror => $name): ?> + <tr> + <td rowspan="<?=count($name["components"])?>"><?=$name['name']?></td> + <td rowspan="<?=count($name["components"])?>"><?=$name['status']?></td> + <td><?=$name['components'][0]?></td> + </tr> +<?php if (count($name["components"]) > 1): ?> + <?php foreach (array_slice($name["components"], 1) as $component): ?> + <tr> + <td><?=$component?></td> + </tr> + <?php endforeach; ?> + <?php endif; ?> +<?php endforeach; +} + +/* List all disks in the system (potential gmirror targets) */ +function gmirror_get_disks() { + $disklist = ""; + /* Get a list of disks in a scriptable way, exclude optical drives */ + exec("/sbin/geom disk status -s | /usr/bin/grep -v '[[:blank:]]*cd[[:digit:]]*' | /usr/bin/awk '{print $1;}'", $disklist); + return $disklist; +} + +/* List all potential gmirror consumers */ +function gmirror_get_unused_consumers() { + $consumerlist = ""; + $disklist = gmirror_get_disks(); + /* Get a list of consumers, exclude existing mirrors and diskid entries */ + exec("/sbin/geom part status -s | /usr/bin/egrep -v '(mirror|diskid)' | /usr/bin/awk '{print $1, $3;}'", $consumerlist); + $all_consumers = array(); + foreach ($consumerlist as $cl) { + $parts = explode(" ", $cl); + foreach ($parts as $part) { + $all_consumers[] = $part; + } + } + foreach ($disklist as $d) { + if (!is_consumer_used($d) && !in_array($d, $all_consumers)) { + $all_consumers[] = $d; + } + } + return $all_consumers; +} + +/* List all existing geom mirrors */ +function gmirror_get_mirrors() { + $mirrorlist = ""; + exec("/sbin/gmirror list | /usr/bin/grep '^Geom name:' | /usr/bin/awk '{print $3;}'", $mirrorlist); + return $mirrorlist; +} + + +/* List all consumers for a given mirror */ +function gmirror_get_consumers_in_mirror($mirror) { + if (!is_valid_mirror($mirror)) { + return array(); + } + + $consumers = array(); + exec("/sbin/gmirror status -s " . escapeshellarg($mirror) . " | /usr/bin/awk '{print $3;}'", $consumers); + return $consumers; +} + +/* Test if a given consumer is a member of an existing mirror */ +function is_consumer_in_mirror($consumer, $mirror) { + if (!is_valid_consumer($consumer) || !is_valid_mirror($mirror)) { + return false; + } + + $mirrorconsumers = gmirror_get_consumers_in_mirror($mirror); + return in_array(basename($consumer), $mirrorconsumers); +} + +/* Test if a mirror exists */ +function is_valid_mirror($mirror) { + $mirrors = gmirror_get_mirrors(); + return in_array($mirror, $mirrors); +} + +/* Test if a disk is valid/exists */ +function is_valid_disk($disk) { + $adisks = gmirror_get_disks(); + return in_array(basename($disk), $adisks); +} + +/* Test if a consumer is valid and in use in a mirror */ +function is_consumer_used($consumer) { + $found = false; + $mirrors = gmirror_get_mirrors(); + foreach ($mirrors as $mirror) { + $consumers = gmirror_get_consumers_in_mirror($mirror); + if (in_array($consumer, $consumers)) { + return true; + } + } + return false; +} + +/* Test if a consumer is valid and not in use */ +function is_consumer_unused($consumer) { + $consumers = gmirror_get_unused_consumers(); + return in_array($consumer, $consumers); +} + +/* Test if a consumer is valid (either a disk or partition) */ +function is_valid_consumer($consumer) { + return (is_consumer_unused($consumer) || is_consumer_used($consumer)); +} + +/* Remove all disconnected drives from a mirror */ +function gmirror_forget_disconnected($mirror) { + if (!is_valid_mirror($mirror)) { + return false; + } + return mwexec("/sbin/gmirror forget " . escapeshellarg($mirror)); +} + +/* Insert another consumer into a mirror */ +function gmirror_insert_consumer($mirror, $consumer) { + if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer)) { + return false; + } + return mwexec("/sbin/gmirror insert " . escapeshellarg($mirror) . " " . escapeshellarg($consumer)); +} + +/* Remove consumer from a mirror and clear its metadata */ +function gmirror_remove_consumer($mirror, $consumer) { + if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer)) { + return false; + } + return mwexec("/sbin/gmirror remove " . escapeshellarg($mirror) . " " . escapeshellarg($consumer)); +} + +/* Wipe geom info from drive (if mirror is not running) */ +function gmirror_clear_consumer($consumer) { + if (!is_valid_consumer($consumer)) { + return false; + } + return mwexec("/sbin/gmirror clear " . escapeshellarg($consumer)); +} + +/* Find the balance method used by a given mirror */ +function gmirror_get_mirror_balance($mirror) { + if (!is_valid_mirror($mirror)) { + return false; + } + $balancemethod = ""; + exec("/sbin/gmirror list " . escapeshellarg($mirror) . " | /usr/bin/grep '^Balance:' | /usr/bin/awk '{print $2;}'", $balancemethod); + return $balancemethod[0]; +} + +/* Change balance algorithm of the mirror */ +function gmirror_configure_balance($mirror, $balancemethod) { + global $balance_methods; + if (!is_valid_mirror($mirror) || !in_array($balancemethod, $balance_methods)) { + return false; + } + return mwexec("/sbin/gmirror configure -b " . escapeshellarg($balancemethod) . " " . escapeshellarg($mirror)); +} + +/* Force a mirror member to rebuild */ +function gmirror_force_rebuild($mirror, $consumer) { + if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer)) { + return false; + } + return mwexec("/sbin/gmirror rebuild " . escapeshellarg($mirror) . " " . escapeshellarg($consumer)); +} + +/* Show all metadata on the physical consumer */ +function gmirror_get_consumer_metadata($consumer) { + if (!is_valid_consumer($consumer)) { + return array(); + } + $output = ""; + exec("/sbin/gmirror dump " . escapeshellarg($consumer), $output); + return array_map('trim', $output); +} + +/* Test if a consumer has metadata, indicating it is a member of a mirror (active or inactive) */ +function gmirror_consumer_has_metadata($consumer) { + return (count(gmirror_get_consumer_metadata($consumer)) > 0); +} + +/* Find the mirror to which this consumer belongs */ +function gmirror_get_consumer_metadata_mirror($consumer) { + if (!is_valid_consumer($consumer)) { + return array(); + } + $metadata = gmirror_get_consumer_metadata($consumer); + foreach ($metadata as $line) { + if (substr($line, 0, 5) == "name:") { + list ($key, $value) = explode(":", $line, 2); + return trim($value); + } + } +} + +/* Deactivate consumer, removing it from service in the mirror, but leave metadata intact */ +function gmirror_deactivate_consumer($mirror, $consumer) { + if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer)) { + return false; + } + return mwexec("/sbin/gmirror deactivate " . escapeshellarg($mirror) . " " . escapeshellarg($consumer)); +} + +/* Reactivate a deactivated consumer */ +function gmirror_activate_consumer($mirror, $consumer) { + if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer)) { + return false; + } + return mwexec("/sbin/gmirror activate " . escapeshellarg($mirror) . " " . escapeshellarg($consumer)); +} + +/* Find the size of the given mirror */ +function gmirror_get_mirror_size($mirror) { + if (!is_valid_mirror($mirror)) { + return false; + } + $mirrorsize = ""; + exec("/sbin/gmirror list " . escapeshellarg($mirror) . " | /usr/bin/grep 'Mediasize:' | /usr/bin/head -n 1 | /usr/bin/awk '{print $2;}'", $mirrorsize); + return $mirrorsize[0]; +} + +/* Return a list of all potential consumers on a disk with sizes. The geom part + list output is a little odd, we can't get the output for just the disk, if the disk contains + slices those get output also. */ +function gmirror_get_all_unused_consumer_sizes_on_disk($disk) { + if (!is_valid_disk($disk) || !is_consumer_unused($disk)) { + return array(); + } + $output = ""; + exec("/sbin/geom part list " . escapeshellarg($disk) . " | /usr/bin/egrep '(Name:|Mediasize:)' | /usr/bin/cut -c4- | /usr/bin/sed -l -e 'N;s/\\nMediasize://;P;D;' | /usr/bin/cut -c7-", $output); + if (empty($output)) { + exec("/sbin/geom disk list " . escapeshellarg($disk) . " | /usr/bin/egrep '(Name:|Mediasize:)' | /usr/bin/cut -c4- | /usr/bin/sed -l -e 'N;s/\\nMediasize://;P;D;' | /usr/bin/cut -c7-", $output); + } + $disk_contents = array(); + foreach ($output as $line) { + list($name, $size, $humansize) = explode(" ", $line, 3); + $consumer = array(); + $consumer['name'] = $name; + $consumer['size'] = $size; + $consumer['humansize'] = $humansize; + $disk_contents[] = $consumer; + } + return $disk_contents; +} + +/* Get only the size for one specific potential consumer. */ +function gmirror_get_unused_consumer_size($consumer) { + $consumersizes = gmirror_get_all_unused_consumer_sizes_on_disk($consumer); + foreach ($consumersizes as $csize) { + if ($csize['name'] == $consumer) { + return $csize['size']; + } + } + return -1; +} +?> diff --git a/src/etc/inc/growl.class b/src/etc/inc/growl.class new file mode 100644 index 0000000..8f639e5 --- /dev/null +++ b/src/etc/inc/growl.class @@ -0,0 +1,102 @@ +<?PHP +/* + pfSense_MODULE: notifications +*/ + + class Growl + { + const GROWL_PRIORITY_LOW = -2; + const GROWL_PRIORITY_MODERATE = -1; + const GROWL_PRIORITY_NORMAL = 0; + const GROWL_PRIORITY_HIGH = 1; + const GROWL_PRIORITY_EMERGENCY = 2; + + private $appName; + private $address; + private $notifications; + private $password; + private $port; + + public function __construct($address, $password = '', $app_name = 'PHP-Growl') + { + $this->appName = utf8_encode($app_name); + $this->address = $address; + $this->notifications = array(); + $this->password = $password; + $this->port = 9887; + } + + public function addNotification($name, $enabled = true) + { + $this->notifications[] = array('name' => utf8_encode($name), 'enabled' => $enabled); + } + + public function register() + { + $data = ''; + $defaults = ''; + $num_defaults = 0; + + for ($i = 0; $i < count($this->notifications); $i++) + { + $data .= pack('n', strlen($this->notifications[$i]['name'])) . $this->notifications[$i]['name']; + if ($this->notifications[$i]['enabled']) + { + $defaults .= pack('c', $i); + $num_defaults++; + } + } + + // pack(Protocol version, type, app name, number of notifications to register) + $data = pack('c2nc2', 1, 0, strlen($this->appName), count($this->notifications), $num_defaults) . $this->appName . $data . $defaults; + $data .= pack('H32', md5($data . $this->password)); + + return $this->send($data); + } + + public function notify($name, $title, $message, $priority = 0, $sticky = false) + { + $name = utf8_encode($name); + $title = utf8_encode($title); + $message = utf8_encode($message); + $priority = intval($priority); + + $flags = ($priority & 7) * 2; + if ($priority < 0) $flags |= 8; + if ($sticky) $flags |= 1; + + // pack(protocol version, type, priority/sticky flags, notification name length, title length, message length. app name length) + $data = pack('c2n5', 1, 1, $flags, strlen($name), strlen($title), strlen($message), strlen($this->appName)); + $data .= $name . $title . $message . $this->appName; + $data .= pack('H32', md5($data . $this->password)); + + return $this->send($data); + } + + private function send($data) + { + if (function_exists('socket_create') && function_exists('socket_sendto')) + { + $sck = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + if ($sck) { + socket_sendto($sck, $data, strlen($data), 0x100, $this->address, $this->port); + return true; + } + } + elseif (function_exists('fsockopen')) + { + if ($this->address) { + $fp = @fsockopen('udp://' . $this->address, $this->port); + if ($fp) { + fwrite($fp, $data); + fclose($fp); + return true; + } + } + } + + return false; + } + } + +?>
\ No newline at end of file diff --git a/src/etc/inc/gwlb.inc b/src/etc/inc/gwlb.inc new file mode 100644 index 0000000..9880cdc --- /dev/null +++ b/src/etc/inc/gwlb.inc @@ -0,0 +1,1252 @@ +<?php +/* + gwlb.inc + Copyright (C) 2008 Bill Marquette, Seth Mos + Copyright (C) 2010 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_BUILDER_BINARIES: /sbin/route /usr/local/sbin/apinger + pfSense_MODULE: routing + + */ +require_once("config.inc"); +require_once("rrd.inc"); + +/* Returns an array of default values used for apinger.conf */ +function return_apinger_defaults() { + return array( + "latencylow" => "200", + "latencyhigh" => "500", + "losslow" => "10", + "losshigh" => "20", + "interval" => "1", + "down" => "10", + "avg_delay_samples" => "10", + "avg_loss_samples" => "50", + "avg_loss_delay_samples" => "20"); +} + +/* + * Creates monitoring configuration file and + * adds appropriate static routes. + */ +function setup_gateways_monitor() { + global $config, $g; + + $gateways_arr = return_gateways_array(); + if (!is_array($gateways_arr)) { + log_error("No gateways to monitor. Apinger will not be run."); + killbypid("{$g['varrun_path']}/apinger.pid"); + @unlink("{$g['varrun_path']}/apinger.status"); + return; + } + + $apinger_debug = ""; + if (isset($config['system']['apinger_debug'])) { + $apinger_debug = "debug on"; + } + + $apinger_default = return_apinger_defaults(); + $apingerconfig = <<<EOD + +# pfSense apinger configuration file. Automatically Generated! + +{$apinger_debug} + +## User and group the pinger should run as +user "root" +group "wheel" + +## Mailer to use (default: "/usr/lib/sendmail -t") +#mailer "/var/qmail/bin/qmail-inject" + +## Location of the pid-file (default: "/var/run/apinger.pid") +pid_file "{$g['varrun_path']}/apinger.pid" + +## Format of timestamp (%s macro) (default: "%b %d %H:%M:%S") +#timestamp_format "%Y%m%d%H%M%S" + +status { + ## File where the status information should be written to + file "{$g['varrun_path']}/apinger.status" + ## Interval between file updates + ## when 0 or not set, file is written only when SIGUSR1 is received + interval 5s +} + +######################################## +# RRDTool status gathering configuration +# Interval between RRD updates +rrd interval 60s; + +## These parameters can be overridden in a specific alarm configuration +alarm default { + command on "/usr/local/sbin/pfSctl -c 'service reload dyndns %T' -c 'service reload ipsecdns' -c 'service reload openvpn %T' -c 'filter reload' " + command off "/usr/local/sbin/pfSctl -c 'service reload dyndns %T' -c 'service reload ipsecdns' -c 'service reload openvpn %T' -c 'filter reload' " + combine 10s +} + +## "Down" alarm definition. +## This alarm will be fired when target doesn't respond for 30 seconds. +alarm down "down" { + time {$apinger_default['down']}s +} + +## "Delay" alarm definition. +## This alarm will be fired when responses are delayed more than 200ms +## it will be canceled, when the delay drops below 100ms +alarm delay "delay" { + delay_low {$apinger_default['latencylow']}ms + delay_high {$apinger_default['latencyhigh']}ms +} + +## "Loss" alarm definition. +## This alarm will be fired when packet loss goes over 20% +## it will be canceled, when the loss drops below 10% +alarm loss "loss" { + percent_low {$apinger_default['losslow']} + percent_high {$apinger_default['losshigh']} +} + +target default { + ## How often the probe should be sent + interval {$apinger_default['interval']}s + + ## How many replies should be used to compute average delay + ## for controlling "delay" alarms + avg_delay_samples {$apinger_default['avg_delay_samples']} + + ## How many probes should be used to compute average loss + avg_loss_samples {$apinger_default['avg_loss_samples']} + + ## The delay (in samples) after which loss is computed + ## without this delays larger than interval would be treated as loss + avg_loss_delay_samples {$apinger_default['avg_loss_delay_samples']} + + ## Names of the alarms that may be generated for the target + alarms "down","delay","loss" + + ## Location of the RRD + #rrd file "{$g['vardb_path']}/rrd/apinger-%t.rrd" +} + +EOD; + + $monitor_ips = array(); + foreach ($gateways_arr as $name => $gateway) { + /* Do not monitor if such was requested */ + if (isset($gateway['monitor_disable'])) { + continue; + } + if (empty($gateway['monitor']) || !is_ipaddr($gateway['monitor'])) { + if (is_ipaddr($gateway['gateway'])) { + $gateway['monitor'] = $gateway['gateway']; + } else { /* No chance to get an ip to monitor skip target. */ + continue; + } + } + + /* if the monitor address is already used before, skip */ + if (in_array($gateway['monitor'], $monitor_ips)) { + continue; + } + + /* Interface ip is needed since apinger will bind a socket to it. + * However the config GUI should already have checked this and when + * PPoE is used the IP address is set to "dynamic". So using is_ipaddrv4 + * or is_ipaddrv6 to identify packet type would be wrong, especially as + * further checks (that can cope with the "dynamic" case) are present inside + * the if block. So using $gateway['ipprotocol'] is the better option. + */ + if ($gateway['ipprotocol'] == "inet") { // This is an IPv4 gateway... + $gwifip = find_interface_ip($gateway['interface'], true); + if (!is_ipaddrv4($gwifip)) { + continue; //Skip this target + } + + if ($gwifip == "0.0.0.0") { + continue; //Skip this target - the gateway is still waiting for DHCP + } + + /* + * If the gateway is the same as the monitor we do not add a + * route as this will break the routing table. + * Add static routes for each gateway with their monitor IP + * not strictly necessary but is a added level of protection. + */ + if (is_ipaddrv4($gateway['gateway']) && $gateway['monitor'] != $gateway['gateway']) { + log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}"); + if (interface_isppp_type($gateway['friendlyiface'])) { + mwexec("/sbin/route change -host " . escapeshellarg($gateway['monitor']) . + " -iface " . escapeshellarg($gateway['interface']), true); + } else { + mwexec("/sbin/route change -host " . escapeshellarg($gateway['monitor']) . + " " . escapeshellarg($gateway['gateway']), true); + } + + pfSense_kill_states("0.0.0.0/0", $gateway['monitor'], $gateway['interface'], "icmp"); + } + } else if ($gateway['ipprotocol'] == "inet6") { // This is an IPv6 gateway... + if ($gateway['monitor'] == $gateway['gateway']) { + /* link locals really need a different src ip */ + if (is_linklocal($gateway['gateway'])) { + if (!strpos($gateway['gateway'], '%')) { + $gateway['gateway'] .= '%' . $gateway['interface']; + } + $gwifip = find_interface_ipv6_ll($gateway['interface'], true); + } else { + $gwifip = find_interface_ipv6($gateway['interface'], true); + } + } else { + /* 'monitor' has been set, so makes sure it has precedence over + * 'gateway' in defining the source IP. Otherwise if 'gateway' + * is a local link and 'monitor' is global routable then the + * ICMP6 response would not find its way back home... + */ + $gwifip = find_interface_ipv6($gateway['interface'], true); + } + + /* Make sure srcip and target have scope defined when they are ll */ + if (is_linklocal($gwifip) && !strpos($gwifip, '%')) { + $gwifip .= '%' . $gateway['interface']; + } + if (is_linklocal($gateway['monitor']) && !strpos($gateway['monitor'], '%')) { + $gateway['monitor'] .= "%{$gateway['interface']}"; + } + + if (!is_ipaddrv6($gwifip)) { + continue; //Skip this target + } + + /* + * If the gateway is the same as the monitor we do not add a + * route as this will break the routing table. + * Add static routes for each gateway with their monitor IP + * not strictly necessary but is a added level of protection. + */ + if ($gateway['gateway'] != $gateway['monitor']) { + log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}"); + if (interface_isppp_type($gateway['friendlyiface'])) { + mwexec("/sbin/route change -host -inet6 " . escapeshellarg($gateway['monitor']) . + " -iface " . escapeshellarg($gateway['interface']), true); + } else { + mwexec("/sbin/route change -host -inet6 " . escapeshellarg($gateway['monitor']) . + " " . escapeshellarg($gateway['gateway']), true); + } + + pfSense_kill_states("::0.0.0.0/0", $gateway['monitor'], $gateway['interface'], "icmpv6"); + } + } else { + continue; + } + + $monitor_ips[] = $gateway['monitor']; + $apingercfg = "target \"{$gateway['monitor']}\" {\n"; + $apingercfg .= " description \"{$name}\"\n"; + $apingercfg .= " srcip \"{$gwifip}\"\n"; + + ## How often the probe should be sent + if (!empty($gateway['interval']) && is_numeric($gateway['interval'])) { + $interval = intval($gateway['interval']); # Restrict to Integer + if ($interval < 1) { + $interval = 1; # Minimum + } + if ($interval != $apinger_default['interval']) { # If not default value + $apingercfg .= " interval " . $interval . "s\n"; + } + } + + ## How many replies should be used to compute average delay + ## for controlling "delay" alarms + if (!empty($gateway['avg_delay_samples']) && is_numeric($gateway['avg_delay_samples'])) { + $avg_delay_samples = intval($gateway['avg_delay_samples']); # Restrict to Integer + if ($avg_delay_samples < 1) { + $avg_delay_samples = 1; # Minimum + } + if ($avg_delay_samples != $apinger_default['avg_delay_samples']) { # If not default value + $apingercfg .= " avg_delay_samples " . $avg_delay_samples . "\n"; + } + } + + ## How many probes should be used to compute average loss + if (!empty($gateway['avg_loss_samples']) && is_numeric($gateway['avg_loss_samples'])) { + $avg_loss_samples = intval($gateway['avg_loss_samples']); # Restrict to Integer + if ($avg_loss_samples < 1) { + $avg_loss_samples = 1; # Minimum + } + if ($avg_loss_samples != $apinger_default['avg_loss_samples']) { # If not default value + $apingercfg .= " avg_loss_samples " . $avg_loss_samples . "\n"; + } + } + + ## The delay (in samples) after which loss is computed + ## without this delays larger than interval would be treated as loss + if (!empty($gateway['avg_loss_delay_samples']) && is_numeric($gateway['avg_loss_delay_samples'])) { + $avg_loss_delay_samples = intval($gateway['avg_loss_delay_samples']); # Restrict to Integer + if ($avg_loss_delay_samples < 1) { + $avg_loss_delay_samples = 1; # Minimum + } + if ($avg_loss_delay_samples != $apinger_default['avg_loss_delay_samples']) { # If not default value + $apingercfg .= " avg_loss_delay_samples " . $avg_loss_delay_samples . "\n"; + } + } + + $alarms = ""; + $alarmscfg = ""; + $override = false; + if (!empty($gateway['losslow'])) { + $alarmscfg .= "alarm loss \"{$name}loss\" {\n"; + $alarmscfg .= "\tpercent_low {$gateway['losslow']}\n"; + $alarmscfg .= "\tpercent_high {$gateway['losshigh']}\n"; + $alarmscfg .= "}\n"; + $alarms .= "\"{$name}loss\""; + $override = true; + } else { + if ($override == true) { + $alarms .= ","; + } + $alarms .= "\"loss\""; + $override = true; + } + if (!empty($gateway['latencylow'])) { + $alarmscfg .= "alarm delay \"{$name}delay\" {\n"; + $alarmscfg .= "\tdelay_low {$gateway['latencylow']}ms\n"; + $alarmscfg .= "\tdelay_high {$gateway['latencyhigh']}ms\n"; + $alarmscfg .= "}\n"; + if ($override == true) { + $alarms .= ","; + } + $alarms .= "\"{$name}delay\""; + $override = true; + } else { + if ($override == true) { + $alarms .= ","; + } + $alarms .= "\"delay\""; + $override = true; + } + if (!empty($gateway['down'])) { + $alarmscfg .= "alarm down \"{$name}down\" {\n"; + $alarmscfg .= "\ttime {$gateway['down']}s\n"; + $alarmscfg .= "}\n"; + if ($override == true) { + $alarms .= ","; + } + $alarms .= "\"{$name}down\""; + $override = true; + } else { + if ($override == true) { + $alarms .= ","; + } + $alarms .= "\"down\""; + $override = true; + } + if ($override == true) { + $apingercfg .= "\talarms override {$alarms};\n"; + } + + if (isset($gateway['force_down'])) { + $apingercfg .= "\tforce_down on\n"; + } + + $apingercfg .= " rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n"; + $apingercfg .= "}\n"; + $apingercfg .= "\n"; + + $apingerconfig .= $alarmscfg; + $apingerconfig .= $apingercfg; + + # Create gateway quality RRD with settings more suitable for pfSense graph set, + # since apinger uses default step (300; 5 minutes) and other settings that don't + # match the pfSense gateway quality graph set. + create_gateway_quality_rrd("{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd"); + } + @file_put_contents("{$g['varetc_path']}/apinger.conf", $apingerconfig); + unset($apingerconfig); + + /* Restart apinger process */ + if (isvalidpid("{$g['varrun_path']}/apinger.pid")) { + sigkillbypid("{$g['varrun_path']}/apinger.pid", "HUP"); + } else { + /* start a new apinger process */ + @unlink("{$g['varrun_path']}/apinger.status"); + sleep(1); + mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf"); + sleep(1); + sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1"); + } + + return 0; +} + +/* return the status of the apinger targets as a array */ +function return_gateways_status($byname = false) { + global $config, $g; + + $apingerstatus = array(); + /* Always get the latest status from apinger */ + if (file_exists("{$g['varrun_path']}/apinger.pid")) { + sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1"); + } + if (file_exists("{$g['varrun_path']}/apinger.status")) { + $apingerstatus = file("{$g['varrun_path']}/apinger.status"); + } else { + $apingerstatus = array(); + } + + $status = array(); + foreach ($apingerstatus as $line) { + $info = explode("|", $line); + if ($byname == false) { + $target = $info[0]; + } else { + $target = $info[2]; + } + + $status[$target] = array(); + $status[$target]['monitorip'] = $info[0]; + $status[$target]['srcip'] = $info[1]; + $status[$target]['name'] = $info[2]; + $status[$target]['lastcheck'] = $info[5] ? date('r', $info[5]) : date('r'); + $status[$target]['delay'] = empty($info[6]) ? "0ms" : round($info[6], 1) ."ms" ; + $status[$target]['loss'] = empty($info[7]) ? "0.0%" : round($info[7], 1) . "%"; + $status[$target]['status'] = trim($info[8]); + } + + /* tack on any gateways that have monitoring disabled + * or are down, which could cause gateway groups to fail */ + $gateways_arr = return_gateways_array(); + foreach ($gateways_arr as $gwitem) { + if (!isset($gwitem['monitor_disable'])) { + continue; + } + if (!is_ipaddr($gwitem['monitorip'])) { + $realif = $gwitem['interface']; + $tgtip = get_interface_gateway($realif); + if (!is_ipaddr($tgtip)) { + $tgtip = "none"; + } + $srcip = find_interface_ip($realif); + } else { + $tgtip = $gwitem['monitorip']; + $srcip = find_interface_ip($realif); + } + if ($byname == true) { + $target = $gwitem['name']; + } else { + $target = $tgtip; + } + + /* failsafe for down interfaces */ + if ($target == "none") { + $target = $gwitem['name']; + $status[$target]['name'] = $gwitem['name']; + $status[$target]['lastcheck'] = date('r'); + $status[$target]['delay'] = "0.0ms"; + $status[$target]['loss'] = "100.0%"; + $status[$target]['status'] = "down"; + } else { + $status[$target]['monitorip'] = $tgtip; + $status[$target]['srcip'] = $srcip; + $status[$target]['name'] = $gwitem['name']; + $status[$target]['lastcheck'] = date('r'); + $status[$target]['delay'] = "0.0ms"; + $status[$target]['loss'] = "0.0%"; + $status[$target]['status'] = "none"; + } + } + return($status); +} + +/* Return all configured gateways on the system */ +function return_gateways_array($disabled = false, $localhost = false, $inactive = false) { + global $config, $g; + + $gateways_arr = array(); + $gateways_arr_temp = array(); + + $found_defaultv4 = 0; + $found_defaultv6 = 0; + + // Ensure the interface cache is up to date first + $interfaces = get_interface_arr(true); + $interfaces_v4 = array(); + $interfaces_v6 = array(); + + $i = -1; + /* Process/add all the configured gateways. */ + if (is_array($config['gateways']['gateway_item'])) { + foreach ($config['gateways']['gateway_item'] as $gateway) { + /* Increment it here to do not skip items */ + $i++; + + if (empty($config['interfaces'][$gateway['interface']])) { + if ($inactive === false) { + continue; + } else { + $gateway['inactive'] = true; + } + } + $wancfg = $config['interfaces'][$gateway['interface']]; + + /* skip disabled interfaces */ + if ($disabled === false && (!isset($wancfg['enable']))) { + continue; + } + + /* if the gateway is dynamic and we can find the IPv4, Great! */ + if (empty($gateway['gateway']) || $gateway['gateway'] == "dynamic") { + if ($gateway['ipprotocol'] == "inet") { + /* we know which interfaces is dynamic, this should be made a function */ + $gateway['gateway'] = get_interface_gateway($gateway['interface']); + /* no IP address found, set to dynamic */ + if (!is_ipaddrv4($gateway['gateway'])) { + $gateway['gateway'] = "dynamic"; + } + $gateway['dynamic'] = true; + } + + /* if the gateway is dynamic and we can find the IPv6, Great! */ + else if ($gateway['ipprotocol'] == "inet6") { + /* we know which interfaces is dynamic, this should be made a function, and for v6 too */ + $gateway['gateway'] = get_interface_gateway_v6($gateway['interface']); + /* no IPv6 address found, set to dynamic */ + if (!is_ipaddrv6($gateway['gateway'])) { + $gateway['gateway'] = "dynamic"; + } + $gateway['dynamic'] = true; + } + } else { + /* getting this detection right is hard at this point because we still don't + * store the address family in the gateway item */ + if (is_ipaddrv4($gateway['gateway'])) { + $gateway['ipprotocol'] = "inet"; + } else if (is_ipaddrv6($gateway['gateway'])) { + $gateway['ipprotocol'] = "inet6"; + } + } + + if (isset($gateway['monitor_disable'])) { + $gateway['monitor_disable'] = true; + } else if (empty($gateway['monitor'])) { + $gateway['monitor'] = $gateway['gateway']; + } + + $gateway['friendlyiface'] = $gateway['interface']; + + /* special treatment for tunnel interfaces */ + if ($gateway['ipprotocol'] == "inet6") { + $gateway['interface'] = get_real_interface($gateway['interface'], "inet6", false, false); + $interfaces_v6[$gateway['friendlyiface']] = $gateway['friendlyiface']; + } else { + $gateway['interface'] = get_real_interface($gateway['interface'], "all", false, false); + $interfaces_v4[$gateway['friendlyiface']] = $gateway['friendlyiface']; + } + + /* entry has a default flag, use it */ + if (isset($gateway['defaultgw'])) { + if ($gateway['ipprotocol'] == "inet") { + $gateway['defaultgw'] = true; + $found_defaultv4 = 1; + } else if ($gateway['ipprotocol'] == "inet6") { + $gateway['defaultgw'] = true; + $found_defaultv6 = 1; + } + } + /* include the gateway index as the attribute */ + $gateway['attribute'] = $i; + + /* Remember all the gateway names, even ones to be skipped because they are disabled. */ + /* Then we can easily know and match them later when attempting to add dynamic gateways to the list. */ + $gateways_arr_temp[$gateway['name']] = $gateway; + + /* skip disabled gateways if the caller has not asked for them to be returned. */ + if (!($disabled === false && isset($gateway['disabled']))) { + $gateways_arr[$gateway['name']] = $gateway; + } + } + } + unset($gateway); + + /* Loop through all interfaces with a gateway and add it to a array */ + if ($disabled == false) { + $iflist = get_configured_interface_with_descr(); + } else { + $iflist = get_configured_interface_with_descr(false, true); + } + + /* Process/add dynamic v4 gateways. */ + foreach ($iflist as $ifname => $friendly) { + if (!interface_has_gateway($ifname)) { + continue; + } + + if (empty($config['interfaces'][$ifname])) { + continue; + } + + $ifcfg = &$config['interfaces'][$ifname]; + if (!isset($ifcfg['enable'])) { + continue; + } + + if (!empty($ifcfg['ipaddr']) && is_ipaddrv4($ifcfg['ipaddr'])) { + continue; + } + + if (isset($interfaces_v4[$ifname])) { + continue; + } + + $ctype = ""; + switch ($ifcfg['ipaddr']) { + case "dhcp": + case "pppoe": + case "pptp": + case "ppp": + $ctype = strtoupper($ifcfg['ipaddr']); + break; + default: + $tunnelif = substr($ifcfg['if'], 0, 3); + if (substr($ifcfg['if'], 0, 4) == "ovpn") { + // if current iface is an ovpn server endpoint then check its type, skip tap only + if (substr($ifcfg['if'], 4, 1) == 's') { + $ovpnid = substr($ifcfg['if'], 5); + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as & $ovpnserverconf) { + if ($ovpnserverconf['vpnid'] == $ovpnid) { + if ($ovpnserverconf['dev_mode'] == "tap") { + continue 3; + } + } + } + } + } + $ctype = "VPNv4"; + } else if ($tunnelif == "gif" || $tunnelif == "gre") { + $ctype = "TUNNELv4"; + } + break; + } + $ctype = "_". strtoupper($ctype); + + $gateway = array(); + $gateway['dynamic'] = false; + $gateway['ipprotocol'] = "inet"; + $gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']); + $gateway['interface'] = get_real_interface($ifname); + $gateway['friendlyiface'] = $ifname; + $gateway['name'] = "{$friendly}{$ctype}"; + $gateway['attribute'] = "system"; + + if (($gateway['dynamic'] === "default") && ($found_defaultv4 == 0)) { + $gateway['defaultgw'] = true; + $gateway['dynamic'] = true; + $found_defaultv4 = 1; + } + /* Loopback dummy for dynamic interfaces without a IP */ + if (!is_ipaddrv4($gateway['gateway']) && $gateway['dynamic'] == true) { + $gateway['gateway'] = "dynamic"; + } + + /* automatically skip known static and dynamic gateways that were previously processed */ + foreach ($gateways_arr_temp as $gateway_item) { + if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name'])&& ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) || + (($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))) { + continue 2; + } + } + + if (is_ipaddrv4($gateway['gateway'])) { + $gateway['monitor'] = $gateway['gateway']; + } + + $gateway['descr'] = "Interface {$friendly}{$ctype} Gateway"; + $gateways_arr[$gateway['name']] = $gateway; + } + unset($gateway); + + /* Process/add dynamic v6 gateways. */ + foreach ($iflist as $ifname => $friendly) { + /* If the user has disabled IPv6, they probably don't want any IPv6 gateways. */ + if (!isset($config['system']['ipv6allow'])) { + break; + } + + if (!interface_has_gatewayv6($ifname)) { + continue; + } + + if (empty($config['interfaces'][$ifname])) { + continue; + } + + $ifcfg = &$config['interfaces'][$ifname]; + if (!isset($ifcfg['enable'])) { + continue; + } + + if (!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6'])) { + continue; + } + + if (isset($interfaces_v6[$ifname])) { + continue; + } + + $ctype = ""; + switch ($ifcfg['ipaddrv6']) { + case "slaac": + case "dhcp6": + case "6to4": + case "6rd": + $ctype = strtoupper($ifcfg['ipaddrv6']); + break; + default: + $tunnelif = substr($ifcfg['if'], 0, 3); + if (substr($ifcfg['if'], 0, 4) == "ovpn") { + // if current iface is an ovpn server endpoint then check its type, skip tap only + if (substr($ifcfg['if'], 4, 1) == 's') { + $ovpnid = substr($ifcfg['if'], 5); + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as & $ovpnserverconf) { + if ($ovpnserverconf['vpnid'] == $ovpnid) { + if ($ovpnserverconf['dev_mode'] == "tap") { + continue 3; + } + } + } + } + } + $ctype = "VPNv6"; + } else if ($tunnelif == "gif" || $tunnelif == "gre") { + $ctype = "TUNNELv6"; + } + break; + } + $ctype = "_". strtoupper($ctype); + + $gateway = array(); + $gateway['dynamic'] = false; + $gateway['ipprotocol'] = "inet6"; + $gateway['gateway'] = get_interface_gateway_v6($ifname, $gateway['dynamic']); + $gateway['interface'] = get_real_interface($ifname, "inet6"); + switch ($ifcfg['ipaddrv6']) { + case "6rd": + case "6to4": + $gateway['dynamic'] = "default"; + break; + } + $gateway['friendlyiface'] = $ifname; + $gateway['name'] = "{$friendly}{$ctype}"; + $gateway['attribute'] = "system"; + + if (($gateway['dynamic'] === "default") && ($found_defaultv6 == 0)) { + $gateway['defaultgw'] = true; + $gateway['dynamic'] = true; + $found_defaultv6 = 1; + } + + /* Loopback dummy for dynamic interfaces without a IP */ + if (!is_ipaddrv6($gateway['gateway']) && $gateway['dynamic'] == true) { + $gateway['gateway'] = "dynamic"; + } + + /* automatically skip known static and dynamic gateways that were previously processed */ + foreach ($gateways_arr_temp as $gateway_item) { + if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name']) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) || + (($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))) { + continue 2; + } + } + + if (is_ipaddrv6($gateway['gateway'])) { + $gateway['monitor'] = $gateway['gateway']; + } + + $gateway['descr'] = "Interface {$friendly}{$ctype} Gateway"; + $gateways_arr[$gateway['name']] = $gateway; + } + unset($gateway); + + /* FIXME: Should this be enabled. + * Some interface like wan might be default but have no info recorded + * the config. */ + /* this is a fallback if all else fails and we want to get packets out @smos */ + if ($found_defaultv4 == 0 || $found_defaultv6 == 0) { + foreach ($gateways_arr as &$gateway) { + if (($gateway['friendlyiface'] == "wan") && ($found_defaultv4 == 0) && (!isset($gateway['ipprotocol']) || ($gateway['ipprotocol'] == "inet"))) { + if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgw")) { + $gateway['defaultgw'] = true; + $found_defaultv4 = 1; + } + } + else if (($gateway['friendlyiface'] == "wan") && ($found_defaultv6 == 0) && ($gateway['ipprotocol'] == "inet6")) { + if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgwv6")) { + $gateway['defaultgw'] = true; + $found_defaultv6 = 1; + } + } + } + } + + if ($localhost === true) { + /* attach localhost for Null routes */ + $gwlo4 = array(); + $gwlo4['name'] = "Null4"; + $gwlo4['interface'] = "lo0"; + $gwlo4['ipprotocol'] = "inet"; + $gwlo4['gateway'] = "127.0.0.1"; + $gwlo6 = array(); + $gwlo6['name'] = "Null6"; + $gwlo6['interface'] = "lo0"; + $gwlo6['ipprotocol'] = "inet6"; + $gwlo6['gateway'] = "::1"; + $gateways_arr['Null4'] = $gwlo4; + $gateways_arr['Null6'] = $gwlo6; + } + return($gateways_arr); +} + +function fixup_default_gateway($ipprotocol, $gateways_status, $gateways_arr) { + global $config, $g; + /* + * NOTE: The code below is meant to replace the default gateway when it goes down. + * This facilitates services running on pfSense itself and are not handled by a PBR to continue working. + */ + $upgw = ''; + $dfltgwname = ''; + $dfltgwdown = false; + $dfltgwfound = false; + foreach ($gateways_arr as $gwname => $gwsttng) { + if (($gwsttng['ipprotocol'] == $ipprotocol) && isset($gwsttng['defaultgw'])) { + $dfltgwfound = true; + $dfltgwname = $gwname; + if (!isset($gwsttng['monitor_disable']) && $gateways_status[$gwname]['status'] != "none") { + $dfltgwdown = true; + } + } + /* Keep a record of the last up gateway */ + /* XXX: Blacklist lan for now since it might cause issues to those who have a gateway set for it */ + if (empty($upgw) && ($gwsttng['ipprotocol'] == $ipprotocol) && (isset($gwsttng['monitor_disable']) || $gateways_status[$gwname]['status'] == "none") && $gwsttng[$gwname]['friendlyiface'] != "lan") { + $upgw = $gwname; + } + if ($dfltgwdown == true && !empty($upgw)) { + break; + } + } + if ($dfltgwfound == false) { + $gwname = convert_friendly_interface_to_friendly_descr("wan"); + if (!empty($gateways_status[$gwname]) && stristr($gateways_status[$gwname]['status'], "down")) { + $dfltgwdown = true; + } + } + if ($dfltgwdown == true && !empty($upgw)) { + if ($gateways_arr[$upgw]['gateway'] == "dynamic") { + $gateways_arr[$upgw]['gateway'] = get_interface_gateway($gateways_arr[$upgw]['friendlyiface']); + } + if (is_ipaddr($gateways_arr[$upgw]['gateway'])) { + log_error("Default gateway down setting {$upgw} as default!"); + if (is_ipaddrv6($gateways_arr[$upgw]['gateway'])) { + $inetfamily = "-inet6"; + } else { + $inetfamily = "-inet"; + } + mwexec("/sbin/route change {$inetfamily} default {$gateways_arr[$upgw]['gateway']}"); + } + } else if (!empty($dfltgwname)) { + $defaultgw = trim(exec("/sbin/route -n get -{$ipprotocol} default | /usr/bin/awk '/gateway:/ {print $2}'"), " \n"); + if ($ipprotocol == 'inet6' && !is_ipaddrv6($gateways_arr[$dfltgwname]['gateway'])) { + return; + } + if ($ipprotocol == 'inet' && !is_ipaddrv4($gateways_arr[$dfltgwname]['gateway'])) { + return; + } + if ($defaultgw != $gateways_arr[$dfltgwname]['gateway']) { + mwexec("/sbin/route change -{$ipprotocol} default {$gateways_arr[$dfltgwname]['gateway']}"); + } + } +} + +/* + * Return an array with all gateway groups with name as key + * All gateway groups will be processed before returning the array. + */ +function return_gateway_groups_array() { + global $config, $g; + + /* fetch the current gateways status */ + $gateways_status = return_gateways_status(true); + $gateways_arr = return_gateways_array(); + $gateway_groups_array = array(); + + if (isset($config['system']['gw_switch_default'])) { + fixup_default_gateway("inet", $gateways_status, $gateways_arr); + fixup_default_gateway("inet6", $gateways_status, $gateways_arr); + } + if (is_array($config['gateways']['gateway_group'])) { + $carplist = get_configured_carp_interface_list(); + foreach ($config['gateways']['gateway_group'] as $group) { + /* create array with group gateways members separated by tier */ + $tiers = array(); + $backupplan = array(); + $gwvip_arr = array(); + foreach ($group['item'] as $item) { + list($gwname, $tier, $vipname) = explode("|", $item); + + if (is_ipaddr($carplist[$vipname])) { + if (!is_array($gwvip_arr[$group['name']])) { + $gwvip_arr[$group['name']] = array(); + } + $gwvip_arr[$group['name']][$gwname] = $vipname; + } + + /* Do it here rather than reiterating again the group in case no member is up. */ + if (!is_array($backupplan[$tier])) { + $backupplan[$tier] = array(); + } + $backupplan[$tier][] = $gwname; + + /* check if the gateway is available before adding it to the array */ + if (is_array($gateways_status[$gwname])) { + $status = $gateways_status[$gwname]; + $gwdown = false; + if (stristr($status['status'], "down")) { + $msg = sprintf(gettext("MONITOR: %s is down, omitting from routing group {$group['name']}"), $gwname); + $gwdown = true; + } else if (stristr($status['status'], "loss") && strstr($group['trigger'], "loss")) { + /* packet loss */ + $msg = sprintf(gettext("MONITOR: %s has packet loss, omitting from routing group {$group['name']}"), $gwname); + $gwdown = true; + } else if (stristr($status['status'], "delay") && strstr($group['trigger'] , "latency")) { + /* high latency */ + $msg = sprintf(gettext("MONITOR: %s has high latency, omitting from routing group {$group['name']}"), $gwname); + $gwdown = true; + } + if ($gwdown == true) { + log_error($msg); + notify_via_growl($msg); + notify_via_smtp($msg); + } else { + /* Online add member */ + if (!is_array($tiers[$tier])) { + $tiers[$tier] = array(); + } + $tiers[$tier][] = $gwname; + } + } else if (isset($gateways_arr[$gwname]['monitor_disable'])) { + $tiers[$tier][] = $gwname; + } + } + $tiers_count = count($tiers); + if ($tiers_count == 0) { + /* Oh dear, we have no members! Engage Plan B */ + if (!platform_booting()) { + $msg = gettext("Gateways status could not be determined, considering all as up/active. (Group: {$group['name']})"); + log_error($msg); + notify_via_growl($msg); + //notify_via_smtp($msg); + } + $tiers = $backupplan; + } + /* sort the tiers array by the tier key */ + ksort($tiers); + + /* we do not really foreach the tiers as we stop after the first tier */ + foreach ($tiers as $tieridx => $tier) { + /* process all gateways in this tier */ + foreach ($tier as $member) { + /* determine interface gateway */ + if (isset($gateways_arr[$member])) { + $gateway = $gateways_arr[$member]; + $int = $gateway['interface']; + $gatewayip = ""; + if (is_ipaddr($gateway['gateway'])) { + $gatewayip = $gateway['gateway']; + } else if (!empty($int)) { + $gatewayip = get_interface_gateway($gateway['friendlyiface']); + } + + if (!empty($int)) { + $gateway_groups_array[$group['name']]['ipprotocol'] = $gateway['ipprotocol']; + if (is_ipaddr($gatewayip)) { + $groupmember = array(); + $groupmember['int'] = $int; + $groupmember['gwip'] = $gatewayip; + $groupmember['weight'] = isset($gateway['weight']) ? $gateway['weight'] : 1; + if (is_array($gwvip_arr[$group['name']])&& !empty($gwvip_arr[$group['name']][$member])) { + $groupmember['vip'] = $gwvip_arr[$group['name']][$member]; + } + $gateway_groups_array[$group['name']][] = $groupmember; + } + } + } + } + /* we should have the 1st available tier now, exit stage left */ + if (count($gateway_groups_array[$group['name']]) > 0) { + break; + } else { + log_error("GATEWAYS: Group {$group['name']} did not have any gateways up on tier {$tieridx}!"); + } + } + } + } + + return ($gateway_groups_array); +} + +/* Update DHCP WAN Interface ip address in gateway group item */ +function dhclient_update_gateway_groups_defaultroute($interface = "wan") { + global $config, $g; + foreach ($config['gateways']['gateway_item'] as & $gw) { + if ($gw['interface'] == $interface) { + $current_gw = get_interface_gateway($interface); + if ($gw['gateway'] <> $current_gw) { + $gw['gateway'] = $current_gw; + $changed = true; + } + } + } + if ($changed && $current_gw) { + write_config(sprintf(gettext('Updating gateway group gateway for %1$s - new gateway is %2$s'), $interfac, $current_gw)); + } +} + +function lookup_gateway_ip_by_name($name, $disabled = false) { + + $gateways_arr = return_gateways_array($disabled, true); + foreach ($gateways_arr as $gname => $gw) { + if ($gw['name'] === $name || $gname === $name) { + return $gw['gateway']; + } + } + + return false; +} + +function lookup_gateway_monitor_ip_by_name($name) { + + $gateways_arr = return_gateways_array(false, true); + if (!empty($gateways_arr[$name])) { + $gateway = $gateways_arr[$name]; + if (!is_ipaddr($gateway['monitor'])) { + return $gateway['gateway']; + } + + return $gateway['monitor']; + } + + return (false); +} + +function lookup_gateway_interface_by_name($name) { + + $gateways_arr = return_gateways_array(false, true); + if (!empty($gateways_arr[$name])) { + $interfacegw = $gateways_arr[$name]['friendlyiface']; + return ($interfacegw); + } + + return (false); +} + +function get_interface_gateway($interface, &$dynamic = false) { + global $config, $g; + + if (substr($interface, 0, 4) == '_vip') { + $interface = get_configured_carp_interface_list($interface, 'inet', 'iface'); + } + + $gw = NULL; + $gwcfg = $config['interfaces'][$interface]; + if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) { + foreach ($config['gateways']['gateway_item'] as $gateway) { + if (($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) { + $gw = $gateway['gateway']; + break; + } + } + } + + // for dynamic interfaces we handle them through the $interface_router file. + if (($gw == NULL || !is_ipaddrv4($gw)) && !is_ipaddrv4($gwcfg['ipaddr'])) { + $realif = get_real_interface($interface); + if (file_exists("{$g['tmp_path']}/{$realif}_router")) { + $gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n"); + $dynamic = true; + } + if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw")) { + $dynamic = "default"; + } + + } + + /* return gateway */ + return ($gw); +} + +function get_interface_gateway_v6($interface, &$dynamic = false) { + global $config, $g; + + if (substr($interface, 0, 4) == '_vip') { + $interface = get_configured_carp_interface_list($interface, 'inet6', 'iface'); + } + + $gw = NULL; + $gwcfg = $config['interfaces'][$interface]; + if (!empty($gwcfg['gatewayv6']) && is_array($config['gateways']['gateway_item'])) { + foreach ($config['gateways']['gateway_item'] as $gateway) { + if (($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) { + $gw = $gateway['gateway']; + break; + } + } + } + + // for dynamic interfaces we handle them through the $interface_router file. + if (($gw == NULL || !is_ipaddrv6($gw)) && !is_ipaddrv6($gwcfg['ipaddrv6'])) { + $realif = get_real_interface($interface); + if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) { + $gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n"); + $dynamic = true; + } + if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6")) { + $dynamic = "default"; + } + } + /* return gateway */ + return ($gw); +} + +/* Check a IP address against a gateway IP or name + * to verify it's address family */ +function validate_address_family($ipaddr, $gwname, $disabled = false) { + $v4ip = false; + $v6ip = false; + $v4gw = false; + $v6gw = false; + + if (is_ipaddrv4($ipaddr)) { + $v4ip = true; + } + if (is_ipaddrv6($ipaddr)) { + $v6ip = true; + } + if (is_ipaddrv4($gwname)) { + $v4gw = true; + } + if (is_ipaddrv6($gwname)) { + $v6gw = true; + } + + if ($v4ip && $v4gw) { + return true; + } + if ($v6ip && $v6gw) { + return true; + } + + /* still no match, carry on, lookup gateways */ + if (is_ipaddrv4(lookup_gateway_ip_by_name($gwname, $disabled))) { + $v4gw = true; + } + if (is_ipaddrv6(lookup_gateway_ip_by_name($gwname, $disabled))) { + $v6gw = true; + } + + $gw_array = return_gateways_array(); + if (is_array($gw_array[$gwname])) { + switch ($gw_array[$gwname]['ipprotocol']) { + case "inet": + $v4gw = true; + break; + case "inet6": + $v6gw = true; + break; + } + } + + if ($v4ip && $v4gw) { + return true; + } + if ($v6ip && $v6gw) { + return true; + } + + return false; +} + +/* check if a interface is part of a gateway group */ +function interface_gateway_group_member($interface) { + global $config; + + if (is_array($config['gateways']['gateway_group'])) { + $groups = $config['gateways']['gateway_group']; + } else { + return false; + } + + $gateways_arr = return_gateways_array(false, true); + foreach ($groups as $group) { + if (is_array($group['item'])) { + foreach ($group['item'] as $item) { + $elements = explode("|", $item); + $gwname = $elements[0]; + if ($interface == $gateways_arr[$gwname]['interface']) { + unset($gateways_arr); + return true; + } + } + } + } + unset($gateways_arr); + + return false; +} + +function gateway_is_gwgroup_member($name) { + global $config; + + if (is_array($config['gateways']['gateway_group'])) { + $groups = $config['gateways']['gateway_group']; + } else { + return false; + } + + $members = array(); + foreach ($groups as $group) { + if (is_array($group['item'])) { + foreach ($group['item'] as $item) { + $elements = explode("|", $item); + $gwname = $elements[0]; + if ($name == $elements[0]) { + $members[] = $group['name']; + } + } + } + } + + return $members; +} +?>
\ No newline at end of file diff --git a/src/etc/inc/interfaces.inc b/src/etc/inc/interfaces.inc new file mode 100644 index 0000000..4d9389a --- /dev/null +++ b/src/etc/inc/interfaces.inc @@ -0,0 +1,5814 @@ +<?php +/* + interfaces.inc + Copyright (C) 2004-2008 Scott Ullrich + Copyright (C) 2008-2009 Ermal Luçi + All rights reserved. + + function interfaces_wireless_configure is + Copyright (C) 2005 Espen Johansen + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notices, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notices, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_BUILDER_BINARIES: /sbin/dhclient /bin/sh /usr/bin/grep /usr/bin/xargs /usr/bin/awk /usr/local/sbin/choparp + pfSense_BUILDER_BINARIES: /sbin/ifconfig /sbin/route /usr/sbin/ngctl /usr/sbin/arp /bin/kill /usr/local/sbin/mpd5 + pfSense_BUILDER_BINARIES: /usr/local/sbin/dhcp6c + pfSense_MODULE: interfaces + +*/ + +/* include all configuration functions */ +require_once("globals.inc"); +require_once("util.inc"); +require_once("gwlb.inc"); + +function interfaces_bring_up($interface) { + if (!$interface) { + log_error(gettext("interfaces_bring_up() was called but no variable defined.")); + log_error("Backtrace: " . debug_backtrace()); + return; + } + pfSense_interface_flags($interface, IFF_UP); +} + +/* + * Return the interface array + */ +function get_interface_arr($flush = false) { + global $interface_arr_cache; + + /* If the cache doesn't exist, build it */ + if (!isset($interface_arr_cache) or $flush) { + $interface_arr_cache = pfSense_interface_listget(); + } + + return $interface_arr_cache; +} + +/* + * does_interface_exist($interface): return true or false if a interface is + * detected. + */ +function does_interface_exist($interface, $flush = true) { + global $config; + + if (!$interface) { + return false; + } + + $ints = get_interface_arr($flush); + if (in_array($interface, $ints)) { + return true; + } else { + return false; + } +} + +/* + * does_vip_exist($vip): return true or false if a vip is + * configured. + */ +function does_vip_exist($vip) { + global $config; + + if (!$vip) { + return false; + } + + + switch ($vip['mode']) { + case "carp": + case "ipalias": + /* XXX: Make proper checks? */ + $realif = get_real_interface($vip['interface']); + if (!does_interface_exist($realif)) { + return false; + } + break; + case "proxyarp": + /* XXX: Implement this */ + default: + return false; + } + + $ifacedata = pfSense_getall_interface_addresses($realif); + foreach ($ifacedata as $vipips) { + if ($vipips == "{$vip['subnet']}/{$vip['subnet_bits']}") { + return true; + } + } + + return false; +} + +function interface_netgraph_needed($interface = "wan") { + global $config; + + $found = false; + if (!empty($config['pptpd']) && + $config['pptpd']['mode'] == "server") { + $found = true; + } + if ($found == false && !empty($config['l2tp']) && + $config['l2tp']['mode'] == "server") { + $found = true; + } + if ($found == false && is_array($config['pppoes']['pppoe'])) { + foreach ($config['pppoes']['pppoe'] as $pppoe) { + if ($pppoe['mode'] != "server") { + continue; + } + if ($pppoe['interface'] == $interface) { + $found = true; + break; + } + } + } + if ($found == false) { + $found = interface_isppp_type($interface); + } + + if ($found == false) { + $realif = get_real_interface($interface); + if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) { + foreach ($config['ppps']['ppp'] as $pppid => $ppp) { + $ports = explode(',', $ppp['ports']); + foreach ($ports as $pid => $port) { + $port = get_real_interface($port); + if ($realif == $port) { + $found = true; + break; + } + /* Find the parent interfaces of the vlans in the MLPPP configs + * there should be only one element in the array here + * -- this could be better . . . */ + $parent_if = get_parent_interface($port); + if ($realif == $parent_if[0]) { + $found = true; + break; + } + } + } + } + } + + if ($found == false) { + $realif = get_real_interface($interface); + pfSense_ngctl_detach("{$realif}:", $realif); + } + /* NOTE: We make sure for this on interface_ppps_configure() + * no need to do it here again. + * else + * pfSense_ngctl_attach(".", $realif); + */ +} + +function interfaces_loopback_configure() { + global $g; + + if (platform_booting()) { + echo gettext("Configuring loopback interface..."); + } + pfSense_interface_setaddress("lo0", "127.0.0.1"); + interfaces_bring_up("lo0"); + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + return 0; +} + +function interfaces_vlan_configure($realif = "") { + global $config, $g; + if (platform_booting()) { + echo gettext("Configuring VLAN interfaces..."); + } + if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) { + foreach ($config['vlans']['vlan'] as $vlan) { + if (empty($vlan['vlanif'])) { + $vlan['vlanif'] = "{$vlan['if']}_vlan{$vlan['tag']}"; + } + if (!empty($realif) && $realif != $vlan['vlanif']) { + continue; + } + + /* XXX: Maybe we should report any errors?! */ + interface_vlan_configure($vlan); + } + } + if (platform_booting()) { + echo gettext("done.") . "\n"; + } +} + +function interface_vlan_configure(&$vlan) { + global $config, $g; + + if (!is_array($vlan)) { + log_error(gettext("VLAN: called with wrong options. Problems with config!")); + return; + } + $if = $vlan['if']; + $vlanif = empty($vlan['vlanif']) ? "{$if}_vlan{$vlan['tag']}" : $vlan['vlanif']; + $tag = $vlan['tag']; + + if (empty($if)) { + log_error(gettext("interface_vlan_configure called with if undefined.")); + return; + } + + /* make sure the parent interface is up */ + interfaces_bring_up($if); + /* Since we are going to add vlan(4) try to enable all that hardware supports. */ + pfSense_interface_capabilities($if, IFCAP_VLAN_HWTAGGING|IFCAP_VLAN_MTU|IFCAP_VLAN_HWFILTER); + + if (!empty($vlanif) && does_interface_exist($vlanif)) { + interface_bring_down($vlanif, true); + } else { + $tmpvlanif = pfSense_interface_create("vlan"); + pfSense_interface_rename($tmpvlanif, $vlanif); + pfSense_ngctl_name("{$tmpvlanif}:", $vlanif); + } + + pfSense_vlan_create($vlanif, $if, $tag); + + interfaces_bring_up($vlanif); + + /* invalidate interface cache */ + get_interface_arr(true); + + /* XXX: ermal -- for now leave it here at the moment it does not hurt. */ + interfaces_bring_up($if); + + return $vlanif; +} + +function interface_qinq_configure(&$vlan, $fd = NULL) { + global $config, $g; + + if (!is_array($vlan)) { + log_error(sprintf(gettext("QinQ compat VLAN: called with wrong options. Problems with config!%s"), "\n")); + return; + } + + $qinqif = $vlan['if']; + $tag = $vlan['tag']; + if (empty($qinqif)) { + log_error(sprintf(gettext("interface_qinq_configure called with if undefined.%s"), "\n")); + return; + } + + if (!does_interface_exist($qinqif)) { + log_error(sprintf(gettext("interface_qinq_configure called with invalid if.%s"), "\n")); + return; + } + + $vlanif = interface_vlan_configure($vlan); + + if ($fd == NULL) { + $exec = true; + $fd = fopen("{$g['tmp_path']}/netgraphcmd", "w"); + } else { + $exec = false; + } + /* make sure the parent is converted to ng_vlan(4) and is up */ + interfaces_bring_up($qinqif); + + pfSense_ngctl_attach(".", $qinqif); + if (!empty($vlanif) && does_interface_exist($vlanif)) { + fwrite($fd, "shutdown {$qinqif}qinq:\n"); + exec("/usr/sbin/ngctl msg {$qinqif}qinq: gettable", $result); + if (empty($result)) { + fwrite($fd, "mkpeer {$qinqif}: vlan lower downstream\n"); + fwrite($fd, "name {$qinqif}:lower {$vlanif}qinq\n"); + fwrite($fd, "connect {$qinqif}: {$vlanif}qinq: upper nomatch\n"); + } + } else { + fwrite($fd, "mkpeer {$qinqif}: vlan lower downstream\n"); + fwrite($fd, "name {$qinqif}:lower {$vlanif}qinq\n"); + fwrite($fd, "connect {$qinqif}: {$vlanif}qinq: upper nomatch\n"); + } + + /* invalidate interface cache */ + get_interface_arr(true); + + if (!stristr($qinqif, "_vlan")) { + mwexec("/sbin/ifconfig {$qinqif} promisc\n"); + } + + $macaddr = get_interface_mac($qinqif); + if (!empty($vlan['members'])) { + $members = explode(" ", $vlan['members']); + foreach ($members as $qtag) { + $qinq = array(); + $qinq['tag'] = $qtag; + $qinq['if'] = $vlanif; + interface_qinq2_configure($qinq, $fd, $macaddr); + } + } + if ($exec == true) { + fclose($fd); + mwexec("/usr/sbin/ngctl -f {$g['tmp_path']}/netgraphcmd"); + } + + interfaces_bring_up($qinqif); + if (!empty($vlan['members'])) { + $members = explode(" ", $vlan['members']); + foreach ($members as $qif) { + interfaces_bring_up("{$vlanif}_{$qif}"); + } + } + + return $vlanif; +} + +function interfaces_qinq_configure() { + global $config, $g; + if (platform_booting()) { + echo gettext("Configuring QinQ interfaces..."); + } + if (is_array($config['qinqs']['qinqentry']) && count($config['qinqs']['qinqentry'])) { + foreach ($config['qinqs']['qinqentry'] as $qinq) { + /* XXX: Maybe we should report any errors?! */ + interface_qinq_configure($qinq); + } + } + if (platform_booting()) { + echo gettext("done.") . "\n"; + } +} + +function interface_qinq2_configure(&$qinq, $fd, $macaddr) { + global $config, $g; + + if (!is_array($qinq)) { + log_error(sprintf(gettext("QinQ compat VLAN: called with wrong options. Problems with config!%s"), "\n")); + return; + } + + $if = $qinq['if']; + $tag = $qinq['tag']; + $vlanif = "{$if}_{$tag}"; + if (empty($if)) { + log_error(sprintf(gettext("interface_qinq2_configure called with if undefined.%s"), "\n")); + return; + } + + fwrite($fd, "shutdown {$if}h{$tag}:\n"); + fwrite($fd, "mkpeer {$if}qinq: eiface {$if}{$tag} ether\n"); + fwrite($fd, "name {$if}qinq:{$if}{$tag} {$if}h{$tag}\n"); + fwrite($fd, "msg {$if}qinq: addfilter { vlan={$tag} hook=\"{$if}{$tag}\" }\n"); + fwrite($fd, "msg {$if}h{$tag}: setifname \"{$vlanif}\"\n"); + fwrite($fd, "msg {$if}h{$tag}: set {$macaddr}\n"); + + /* invalidate interface cache */ + get_interface_arr(true); + + return $vlanif; +} + +function interfaces_create_wireless_clones() { + global $config, $g; + + if (platform_booting()) { + echo gettext("Creating wireless clone interfaces..."); + } + + $iflist = get_configured_interface_list(); + + foreach ($iflist as $if) { + $realif = $config['interfaces'][$if]['if']; + if (is_interface_wireless($realif)) { + interface_wireless_clone(interface_get_wireless_clone($realif), $config['interfaces'][$if]); + } + } + + if (isset($config['wireless']['clone']) && is_array($config['wireless']['clone']) && count($config['wireless']['clone'])) { + foreach ($config['wireless']['clone'] as $clone) { + if (empty($clone['cloneif'])) { + continue; + } + if (does_interface_exist($clone['cloneif'])) { + continue; + } + /* XXX: Maybe we should report any errors?! */ + interface_wireless_clone($clone['cloneif'], $clone); + } + } + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + +} + +function interfaces_bridge_configure($checkmember = 0, $realif = "") { + global $config; + + $i = 0; + if (is_array($config['bridges']['bridged']) && count($config['bridges']['bridged'])) { + foreach ($config['bridges']['bridged'] as $bridge) { + if (empty($bridge['bridgeif'])) { + $bridge['bridgeif'] = "bridge{$i}"; + } + if (!empty($realif) && $realif != $bridge['bridgeif']) { + continue; + } + + if ($checkmember == 1) { + /* XXX: It should not be possible no? */ + if (strstr($bridge['if'], '_vip')) { + continue; + } + $members = explode(',', $bridge['members']); + foreach ($members as $member) { + if (!empty($config['interfaces'][$bridge['if']]) && $config['interfaces'][$bridge['if']]['ipaddrv6'] == "track6") { + continue 2; + } + } + } + else if ($checkmember == 2) { + $members = explode(',', $bridge['members']); + foreach ($members as $member) { + if (empty($config['interfaces'][$bridge['if']]) || $config['interfaces'][$bridge['if']]['ipaddrv6'] != "track6") { + continue 2; + } + } + } + /* XXX: Maybe we should report any errors?! */ + interface_bridge_configure($bridge, $checkmember); + $i++; + } + } +} + +function interface_bridge_configure(&$bridge, $checkmember = 0) { + global $config, $g; + + if (!is_array($bridge)) { + return; + } + + if (empty($bridge['members'])) { + log_error(sprintf(gettext("No members found on %s"), $bridge['bridgeif'])); + return; + } + + $members = explode(',', $bridge['members']); + if (!count($members)) { + return; + } + + /* Calculate smaller mtu and enforce it */ + $smallermtu = 0; + $commonrx = true; + $commontx = true; + $foundgif = false; + foreach ($members as $member) { + $realif = get_real_interface($member); + $mtu = get_interface_mtu($realif); + if (substr($realif, 0, 3) == "gif") { + $foundgif = true; + if ($checkmember == 1) { + return; + } + if ($mtu <= 1500) { + continue; + } + } + if ($smallermtu == 0 && !empty($mtu)) { + $smallermtu = $mtu; + } else if (!empty($mtu) && $mtu < $smallermtu) { + $smallermtu = $mtu; + } + } + if ($foundgif == false && $checkmember == 2) { + return; + } + + /* Just in case anything is not working well */ + if ($smallermtu == 0) { + $smallermtu = 1500; + } + + if (platform_booting() || !empty($bridge['bridgeif'])) { + pfSense_interface_destroy($bridge['bridgeif']); + pfSense_interface_create($bridge['bridgeif']); + $bridgeif = escapeshellarg($bridge['bridgeif']); + } else { + $bridgeif = pfSense_interface_create("bridge"); + $bridge['bridgeif'] = $bridgeif; + } + + $bridgemtu = interface_find_child_cfgmtu($bridge['bridgeif']); + if ($bridgemtu > $smallermtu) { + $smallermtu = $bridgemtu; + } + + $checklist = get_configured_interface_list(); + + /* Add interfaces to bridge */ + foreach ($members as $member) { + if (empty($checklist[$member])) { + continue; + } + $realif = get_real_interface($member); + if (!$realif) { + log_error(gettext("realif not defined in interfaces bridge - up")); + continue; + } + /* make sure the parent interface is up */ + pfSense_interface_mtu($realif, $smallermtu); + interfaces_bring_up($realif); + enable_hardware_offloading($member); + pfSense_bridge_add_member($bridge['bridgeif'], $realif); + } + + if (isset($bridge['enablestp'])) { + /* Choose spanning tree proto */ + mwexec("/sbin/ifconfig {$bridgeif} proto " . escapeshellarg($bridge['proto'])); + + if (!empty($bridge['stp'])) { + $stpifs = explode(',', $bridge['stp']); + foreach ($stpifs as $stpif) { + $realif = get_real_interface($stpif); + mwexec("/sbin/ifconfig {$bridgeif} stp {$realif}"); + } + } + if (!empty($bridge['maxage'])) { + mwexec("/sbin/ifconfig {$bridgeif} maxage " . escapeshellarg($bridge['maxage'])); + } + if (!empty($bridge['fwdelay'])) { + mwexec("/sbin/ifconfig {$bridgeif} fwddelay " . escapeshellarg($bridge['fwdelay'])); + } + if (!empty($bridge['hellotime'])) { + mwexec("/sbin/ifconfig {$bridgeif} hellotime " . escapeshellarg($bridge['hellotime'])); + } + if (!empty($bridge['priority'])) { + mwexec("/sbin/ifconfig {$bridgeif} priority " . escapeshellarg($bridge['priority'])); + } + if (!empty($bridge['holdcnt'])) { + mwexec("/sbin/ifconfig {$bridgeif} holdcnt " . escapeshellarg($bridge['holdcnt'])); + } + if (!empty($bridge['ifpriority'])) { + $pconfig = explode(",", $bridge['ifpriority']); + $ifpriority = array(); + foreach ($pconfig as $cfg) { + $embcfg = explode_assoc(":", $cfg); + foreach ($embcfg as $key => $value) { + $ifpriority[$key] = $value; + } + } + foreach ($ifpriority as $key => $value) { + $realif = get_real_interface($key); + mwexec("/sbin/ifconfig ${bridgeif} ifpriority {$realif} " . escapeshellarg($value)); + } + } + if (!empty($bridge['ifpathcost'])) { + $pconfig = explode(",", $bridge['ifpathcost']); + $ifpathcost = array(); + foreach ($pconfig as $cfg) { + $embcfg = explode_assoc(":", $cfg); + foreach ($embcfg as $key => $value) { + $ifpathcost[$key] = $value; + } + } + foreach ($ifpathcost as $key => $value) { + $realif = get_real_interface($key); + mwexec("/sbin/ifconfig ${bridgeif} ifpathcost {$realif} " . escapeshellarg($value)); + } + } + } + + if ($bridge['maxaddr'] <> "") { + mwexec("/sbin/ifconfig {$bridgeif} maxaddr " . escapeshellarg($bridge['maxaddr'])); + } + if ($bridge['timeout'] <> "") { + mwexec("/sbin/ifconfig {$bridgeif} timeout " . escapeshellarg($bridge['timeout'])); + } + if ($bridge['span'] <> "") { + $realif = get_real_interface($bridge['span']); + mwexec("/sbin/ifconfig {$bridgeif} span {$realif}"); + } + if (!empty($bridge['edge'])) { + $edgeifs = explode(',', $bridge['edge']); + foreach ($edgeifs as $edgeif) { + $realif = get_real_interface($edgeif); + mwexec("/sbin/ifconfig {$bridgeif} edge {$realif}"); + } + } + if (!empty($bridge['autoedge'])) { + $edgeifs = explode(',', $bridge['autoedge']); + foreach ($edgeifs as $edgeif) { + $realif = get_real_interface($edgeif); + mwexec("/sbin/ifconfig {$bridgeif} -autoedge {$realif}"); + } + } + if (!empty($bridge['ptp'])) { + $ptpifs = explode(',', $bridge['ptp']); + foreach ($ptpifs as $ptpif) { + $realif = get_real_interface($ptpif); + mwexec("/sbin/ifconfig {$bridgeif} ptp {$realif}"); + } + } + if (!empty($bridge['autoptp'])) { + $ptpifs = explode(',', $bridge['autoptp']); + foreach ($ptpifs as $ptpif) { + $realif = get_real_interface($ptpif); + mwexec("/sbin/ifconfig {$bridgeif} -autoptp {$realif}"); + } + } + if (!empty($bridge['static'])) { + $stickyifs = explode(',', $bridge['static']); + foreach ($stickyifs as $stickyif) { + $realif = get_real_interface($stickyif); + mwexec("/sbin/ifconfig {$bridgeif} sticky {$realif}"); + } + } + if (!empty($bridge['private'])) { + $privateifs = explode(',', $bridge['private']); + foreach ($privateifs as $privateif) { + $realif = get_real_interface($privateif); + mwexec("/sbin/ifconfig {$bridgeif} private {$realif}"); + } + } + + if ($bridge['bridgeif']) { + interfaces_bring_up($bridge['bridgeif']); + } else { + log_error(gettext("bridgeif not defined -- could not bring interface up")); + } +} + +function interface_bridge_add_member($bridgeif, $interface, $flagsapplied = false) { + + if (!does_interface_exist($bridgeif) || !does_interface_exist($interface)) { + return; + } + + if ($flagsapplied == false) { + $mtu = get_interface_mtu($bridgeif); + $mtum = get_interface_mtu($interface); + if ($mtu != $mtum && !(substr($interface, 0, 3) == "gif" && $mtu <= 1500)) { + pfSense_interface_mtu($interface, $mtu); + } + + hardware_offloading_applyflags($interface); + interfaces_bring_up($interface); + } + + pfSense_bridge_add_member($bridgeif, $interface); +} + +function interfaces_lagg_configure($realif = "") { + global $config, $g; + if (platform_booting()) { + echo gettext("Configuring LAGG interfaces..."); + } + $i = 0; + if (is_array($config['laggs']['lagg']) && count($config['laggs']['lagg'])) { + foreach ($config['laggs']['lagg'] as $lagg) { + if (empty($lagg['laggif'])) { + $lagg['laggif'] = "lagg{$i}"; + } + if (!empty($realif) && $realif != $lagg['laggif']) { + continue; + } + /* XXX: Maybe we should report any errors?! */ + interface_lagg_configure($lagg); + $i++; + } + } + if (platform_booting()) { + echo gettext("done.") . "\n"; + } +} + +function interface_lagg_configure($lagg) { + global $config, $g; + + if (!is_array($lagg)) { + return -1; + } + + $members = explode(',', $lagg['members']); + if (!count($members)) { + return -1; + } + + if (platform_booting() || !(empty($lagg['laggif']))) { + pfSense_interface_destroy($lagg['laggif']); + pfSense_interface_create($lagg['laggif']); + $laggif = $lagg['laggif']; + } else { + $laggif = pfSense_interface_create("lagg"); + } + + /* Check if MTU was defined for this lagg interface */ + $lagg_mtu = interface_find_child_cfgmtu($laggif); + if ($lagg_mtu == 0) { + /* Calculate smaller mtu and enforce it */ + $smallermtu = 0; + foreach ($members as $member) { + $mtu = get_interface_mtu($member); + if ($smallermtu == 0 && !empty($mtu)) { + $smallermtu = $mtu; + } else if (!empty($mtu) && $mtu < $smallermtu) { + $smallermtu = $mtu; + } + } + $lagg_mtu = $smallermtu; + } + + /* Just in case anything is not working well */ + if ($lagg_mtu == 0) { + $lagg_mtu = 1500; + } + + foreach ($members as $member) { + if (!does_interface_exist($member)) { + continue; + } + /* make sure the parent interface is up */ + pfSense_interface_mtu($member, $lagg_mtu); + interfaces_bring_up($member); + hardware_offloading_applyflags($member); + mwexec("/sbin/ifconfig " . escapeshellarg($laggif) . " laggport " . escapeshellarg($member)); + } + pfSense_interface_capabilities($laggif, -$flags_off); + pfSense_interface_capabilities($laggif, $flags_on); + + mwexec("/sbin/ifconfig {$laggif} laggproto " . escapeshellarg($lagg['proto'])); + + interfaces_bring_up($laggif); + + return $laggif; +} + +function interfaces_gre_configure($checkparent = 0, $realif = "") { + global $config; + + if (is_array($config['gres']['gre']) && count($config['gres']['gre'])) { + foreach ($config['gres']['gre'] as $i => $gre) { + if (empty($gre['greif'])) { + $gre['greif'] = "gre{$i}"; + } + if (!empty($realif) && $realif != $gre['greif']) { + continue; + } + + if ($checkparent == 1) { + if (substr($gre['if'], 0, 4) == '_vip') { + continue; + } + if (substr($gre['if'], 0, 5) == '_lloc') { + continue; + } + if (!empty($config['interfaces'][$gre['if']]) && $config['interfaces'][$gre['if']]['ipaddrv6'] == "track6") { + continue; + } + } else if ($checkparent == 2) { + if ((substr($gre['if'], 0, 4) != '_vip' && substr($gre['if'], 0, 5) != '_lloc') && + (empty($config['interfaces'][$gre['if']]) || $config['interfaces'][$gre['if']]['ipaddrv6'] != "track6")) { + continue; + } + } + /* XXX: Maybe we should report any errors?! */ + interface_gre_configure($gre); + } + } +} + +/* NOTE: $grekey is not used but useful for passing this function to array_walk. */ +function interface_gre_configure(&$gre, $grekey = "") { + global $config, $g; + + if (!is_array($gre)) { + return -1; + } + + $realif = get_real_interface($gre['if']); + $realifip = get_interface_ip($gre['if']); + + /* make sure the parent interface is up */ + interfaces_bring_up($realif); + + if (platform_booting() || !(empty($gre['greif']))) { + pfSense_interface_destroy($gre['greif']); + pfSense_interface_create($gre['greif']); + $greif = $gre['greif']; + } else { + $greif = pfSense_interface_create("gre"); + } + + /* Do not change the order here for more see gre(4) NOTES section. */ + mwexec("/sbin/ifconfig {$greif} tunnel {$realifip} " . escapeshellarg($gre['remote-addr'])); + if ((is_ipaddrv6($gre['tunnel-local-addr'])) || (is_ipaddrv6($gre['tunnel-remote-addr']))) { + /* XXX: The prefixlen argument for tunnels of ipv6 is useless since it needs to be 128 as enforced by kernel */ + //mwexec("/sbin/ifconfig {$greif} inet6 " . escapeshellarg($gre['tunnel-local-addr']) . " " . escapeshellarg($gre['tunnel-remote-addr']) . " prefixlen /" . escapeshellarg($gre['tunnel-remote-net'])); + mwexec("/sbin/ifconfig {$greif} inet6 " . escapeshellarg($gre['tunnel-local-addr']) . " " . escapeshellarg($gre['tunnel-remote-addr']) . " prefixlen 128"); + } else { + mwexec("/sbin/ifconfig {$greif} " . escapeshellarg($gre['tunnel-local-addr']) . " " . escapeshellarg($gre['tunnel-remote-addr']) . " netmask " . gen_subnet_mask($gre['tunnel-remote-net'])); + } + if (isset($gre['link0'])) { + pfSense_interface_flags($greif, IFF_LINK0); + } + if (isset($gre['link1'])) { + pfSense_interface_flags($greif, IFF_LINK1); + } + if (isset($gre['link2'])) { + pfSense_interface_flags($greif, IFF_LINK2); + } + + if ($greif) { + interfaces_bring_up($greif); + } else { + log_error(gettext("Could not bring greif up -- variable not defined.")); + } + + if (isset($gre['link1']) && $gre['link1']) { + mwexec("/sbin/route add " . escapeshellarg($gre['tunnel-remote-addr']) . "/" . escapeshellarg($gre['tunnel-remote-net']) . " " . escapeshellarg($gre['tunnel-local-addr'])); + } + if (is_ipaddrv4($gre['tunnel-remote-addr'])) { + file_put_contents("{$g['tmp_path']}/{$greif}_router", $gre['tunnel-remote-addr']); + } + if (is_ipaddrv6($gre['tunnel-remote-addr'])) { + file_put_contents("{$g['tmp_path']}/{$greif}_routerv6", $gre['tunnel-remote-addr']); + } + + interfaces_bring_up($greif); + + return $greif; +} + +function interfaces_gif_configure($checkparent = 0, $realif = "") { + global $config; + + if (is_array($config['gifs']['gif']) && count($config['gifs']['gif'])) { + foreach ($config['gifs']['gif'] as $i => $gif) { + if (empty($gif['gifif'])) { + $gre['gifif'] = "gif{$i}"; + } + if (!empty($realif) && $realif != $gif['gifif']) { + continue; + } + + if ($checkparent == 1) { + if (substr($gif['if'], 0, 4) == '_vip') { + continue; + } + if (substr($gif['if'], 0, 5) == '_lloc') { + continue; + } + if (!empty($config['interfaces'][$gif['if']]) && $config['interfaces'][$gif['if']]['ipaddrv6'] == "track6") { + continue; + } + } + else if ($checkparent == 2) { + if ((substr($gif['if'], 0, 4) != '_vip' && substr($gif['if'], 0, 5) != '_lloc') && + (empty($config['interfaces'][$gif['if']]) || $config['interfaces'][$gif['if']]['ipaddrv6'] != "track6")) { + continue; + } + } + /* XXX: Maybe we should report any errors?! */ + interface_gif_configure($gif); + } + } +} + +/* NOTE: $gifkey is not used but useful for passing this function to array_walk. */ +function interface_gif_configure(&$gif, $gifkey = "") { + global $config, $g; + + if (!is_array($gif)) { + return -1; + } + + $realif = get_real_interface($gif['if']); + $ipaddr = get_interface_ip($gif['if']); + + if (is_ipaddrv4($gif['remote-addr'])) { + if (is_ipaddrv4($ipaddr)) { + $realifip = $ipaddr; + } else { + $realifip = get_interface_ip($gif['if']); + } + $realifgw = get_interface_gateway($gif['if']); + } else if (is_ipaddrv6($gif['remote-addr'])) { + if (is_ipaddrv6($ipaddr)) { + $realifip = $ipaddr; + } else { + $realifip = get_interface_ipv6($gif['if']); + } + $realifgw = get_interface_gateway_v6($gif['if']); + } + /* make sure the parent interface is up */ + if ($realif) { + interfaces_bring_up($realif); + } else { + log_error(gettext("could not bring realif up -- variable not defined -- interface_gif_configure()")); + } + + if (platform_booting() || !(empty($gif['gifif']))) { + pfSense_interface_destroy($gif['gifif']); + pfSense_interface_create($gif['gifif']); + $gifif = $gif['gifif']; + } else { + $gifif = pfSense_interface_create("gif"); + } + + /* Do not change the order here for more see gif(4) NOTES section. */ + mwexec("/sbin/ifconfig {$gifif} tunnel {$realifip} " . escapeshellarg($gif['remote-addr'])); + if ((is_ipaddrv6($gif['tunnel-local-addr'])) || (is_ipaddrv6($gif['tunnel-remote-addr']))) { + /* XXX: The prefixlen argument for tunnels of ipv6 is useless since it needs to be 128 as enforced by kernel */ + //mwexec("/sbin/ifconfig {$gifif} inet6 " . escapeshellarg($gif['tunnel-local-addr']) . " " . escapeshellarg($gif['tunnel-remote-addr']) . " prefixlen /" . escapeshellarg($gif['tunnel-remote-net'])); + mwexec("/sbin/ifconfig {$gifif} inet6 " . escapeshellarg($gif['tunnel-local-addr']) . " " . escapeshellarg($gif['tunnel-remote-addr']) . " prefixlen 128"); + } else { + mwexec("/sbin/ifconfig {$gifif} " . escapeshellarg($gif['tunnel-local-addr']) . " " . escapeshellarg($gif['tunnel-remote-addr']) . " netmask " . gen_subnet_mask($gif['tunnel-remote-net'])); + } + if (isset($gif['link0'])) { + pfSense_interface_flags($gifif, IFF_LINK0); + } + if (isset($gif['link1'])) { + pfSense_interface_flags($gifif, IFF_LINK1); + } + if ($gifif) { + interfaces_bring_up($gifif); + } else { + log_error(gettext("could not bring gifif up -- variable not defined")); + } + + if (!platform_booting()) { + $iflist = get_configured_interface_list(); + foreach ($iflist as $ifname) { + if ($config['interfaces'][$ifname]['if'] == $gifif) { + if (get_interface_gateway($ifname)) { + system_routing_configure($ifname); + break; + } + if (get_interface_gateway_v6($ifname)) { + system_routing_configure($ifname); + break; + } + } + } + } + + + if (is_ipaddrv4($gif['tunnel-remote-addr'])) { + file_put_contents("{$g['tmp_path']}/{$gifif}_router", $gif['tunnel-remote-addr']); + } + if (is_ipaddrv6($gif['tunnel-remote-addr'])) { + file_put_contents("{$g['tmp_path']}/{$gifif}_routerv6", $gif['tunnel-remote-addr']); + } + + if (is_ipaddrv4($realifgw)) { + mwexec("/sbin/route change -host " . escapeshellarg($gif['remote-addr']) . " {$realifgw}"); + } + if (is_ipaddrv6($realifgw)) { + mwexec("/sbin/route change -host -inet6 " . escapeshellarg($gif['remote-addr']) . " {$realifgw}"); + } + + interfaces_bring_up($gifif); + + return $gifif; +} + +function interfaces_configure() { + global $config, $g; + + /* Set up our loopback interface */ + interfaces_loopback_configure(); + + /* create the unconfigured wireless clones */ + interfaces_create_wireless_clones(); + + /* set up LAGG virtual interfaces */ + interfaces_lagg_configure(); + + /* set up VLAN virtual interfaces */ + interfaces_vlan_configure(); + + interfaces_qinq_configure(); + + $iflist = get_configured_interface_with_descr(); + $delayed_list = array(); + $bridge_list = array(); + $track6_list = array(); + + /* This is needed to speedup interfaces on bootup. */ + $reload = false; + if (!platform_booting()) { + $reload = true; + } + + foreach ($iflist as $if => $ifname) { + $realif = $config['interfaces'][$if]['if']; + if (strstr($realif, "bridge")) { + $bridge_list[$if] = $ifname; + } else if (strstr($realif, "gre")) { + $delayed_list[$if] = $ifname; + } else if (strstr($realif, "gif")) { + $delayed_list[$if] = $ifname; + } else if (strstr($realif, "ovpn")) { + //echo "Delaying OpenVPN interface configuration...done.\n"; + continue; + } else if (!empty($config['interfaces'][$if]['ipaddrv6']) && $config['interfaces'][$if]['ipaddrv6'] == "track6") { + $track6_list[$if] = $ifname; + } else { + if (platform_booting()) { + printf(gettext("Configuring %s interface..."), $ifname); + } + + if ($g['debug']) { + log_error(sprintf(gettext("Configuring %s"), $ifname)); + } + interface_configure($if, $reload); + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + } + } + + /* + * NOTE: The following function parameter consists of + * 1 - Do not load gre/gif/bridge with parent/member as vip + * 2 - Do load gre/gif/bridge with parent/member as vip + */ + + /* set up GRE virtual interfaces */ + interfaces_gre_configure(1); + + /* set up GIF virtual interfaces */ + interfaces_gif_configure(1); + + /* set up BRIDGe virtual interfaces */ + interfaces_bridge_configure(1); + + foreach ($track6_list as $if => $ifname) { + if (platform_booting()) { + printf(gettext("Configuring %s interface..."), $ifname); + } + if ($g['debug']) { + log_error(sprintf(gettext("Configuring %s"), $ifname)); + } + + interface_configure($if, $reload); + + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + } + + /* bring up vip interfaces */ + interfaces_vips_configure(); + + /* set up GRE virtual interfaces */ + interfaces_gre_configure(2); + + /* set up GIF virtual interfaces */ + interfaces_gif_configure(2); + + foreach ($delayed_list as $if => $ifname) { + if (platform_booting()) { + printf(gettext("Configuring %s interface..."), $ifname); + } + if ($g['debug']) { + log_error(sprintf(gettext("Configuring %s"), $ifname)); + } + + interface_configure($if, $reload); + + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + } + + /* set up BRIDGe virtual interfaces */ + interfaces_bridge_configure(2); + + foreach ($bridge_list as $if => $ifname) { + if (platform_booting()) { + printf(gettext("Configuring %s interface..."), $ifname); + } + if ($g['debug']) { + log_error(sprintf(gettext("Configuring %s"), $ifname)); + } + + interface_configure($if, $reload); + + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + } + + /* configure interface groups */ + interfaces_group_setup(); + + if (!platform_booting()) { + /* reconfigure static routes (kernel may have deleted them) */ + system_routing_configure(); + + /* reload IPsec tunnels */ + vpn_ipsec_configure(); + + /* reload dhcpd (interface enabled/disabled status may have changed) */ + services_dhcpd_configure(); + + /* restart dnsmasq or unbound */ + if (isset($config['dnsmasq']['enable'])) { + services_dnsmasq_configure(); + } elseif (isset($config['unbound']['enable'])) { + services_unbound_configure(); + } + } + + return 0; +} + +function interface_reconfigure($interface = "wan", $reloadall = false) { + interface_bring_down($interface); + interface_configure($interface, $reloadall); +} + +function interface_vip_bring_down($vip) { + global $g; + + if (strpos($vip['interface'], '_vip')) { + if (is_ipaddrv6($vip['subnet'])) { + $family = 'inet6'; + } else { + $family = 'inet'; + } + + $carpvip = get_configured_carp_interface_list($vip['interface'], $family, 'vip'); + $iface = $carpvip['interface']; + } else { + $iface = $vip['interface']; + } + + $vipif = get_real_interface($iface); + switch ($vip['mode']) { + case "proxyarp": + if (file_exists("{$g['varrun_path']}/choparp_{$vipif}.pid")) { + killbypid("{$g['varrun_path']}/choparp_{$vipif}.pid"); + } + break; + case "ipalias": + if (does_interface_exist($vipif)) { + if (is_ipaddrv6($vip['subnet'])) { + mwexec("/sbin/ifconfig {$vipif} inet6 " . escapeshellarg($vip['subnet']) . " -alias"); + } else { + pfSense_interface_deladdress($vipif, $vip['subnet']); + } + } + break; + case "carp": + /* XXX: Is enough to delete ip address? */ + if (does_interface_exist($vipif)) { + if (is_ipaddrv6($vip['subnet'])) { + mwexec("/sbin/ifconfig {$vipif} inet6 " . escapeshellarg($vip['subnet']) . " delete"); + } else { + pfSense_interface_deladdress($vipif, $vip['subnet']); + } + } + break; + } +} + +function interface_bring_down($interface = "wan", $destroy = false, $ifacecfg = false) { + global $config, $g; + + if (!isset($config['interfaces'][$interface])) { + return; + } + + if ($g['debug']) { + log_error("Calling interface down for interface {$interface}, destroy is " . (($destroy) ? 'true' : 'false')); + } + + /* + * NOTE: The $realifv6 is needed when WANv4 is type PPP and v6 is DHCP and the option v6 from v4 is used. + * In this case the real $realif of v4 is different from that of v6 for operation. + * Keep this in mind while doing changes here! + */ + if ($ifacecfg === false) { + $ifcfg = $config['interfaces'][$interface]; + $ppps = $config['ppps']['ppp']; + $realif = get_real_interface($interface); + $realifv6 = get_real_interface($interface, "inet6", true); + } elseif (!is_array($ifacecfg)) { + log_error(gettext("Wrong parameters used during interface_bring_down")); + $ifcfg = $config['interfaces'][$interface]; + $ppps = $config['ppps']['ppp']; + $realif = get_real_interface($interface); + $realifv6 = get_real_interface($interface, "inet6", true); + } else { + $ifcfg = $ifacecfg['ifcfg']; + $ppps = $ifacecfg['ppps']; + if (isset($ifacecfg['ifcfg']['realif'])) { + $realif = $ifacecfg['ifcfg']['realif']; + /* XXX: Any better way? */ + $realifv6 = $realif; + } else { + $realif = get_real_interface($interface); + $realifv6 = get_real_interface($interface, "inet6", true); + } + } + + switch ($ifcfg['ipaddr']) { + case "ppp": + case "pppoe": + case "pptp": + case "l2tp": + if (is_array($ppps) && count($ppps)) { + foreach ($ppps as $pppid => $ppp) { + if ($realif == $ppp['if']) { + if (isset($ppp['ondemand']) && !$destroy) { + send_event("interface reconfigure {$interface}"); + break; + } + if (file_exists("{$g['varrun_path']}/{$ppp['type']}_{$interface}.pid")) { + killbypid("{$g['varrun_path']}/{$ppp['type']}_{$interface}.pid"); + sleep(2); + } + unlink_if_exists("{$g['varetc_path']}/mpd_{$interface}.conf"); + break; + } + } + } + break; + case "dhcp": + kill_dhclient_process($realif); + unlink_if_exists("{$g['varetc_path']}/dhclient_{$interface}.conf"); + if (does_interface_exist("$realif")) { + mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " delete", true); + interface_ipalias_cleanup($interface); + if ($destroy == true) { + pfSense_interface_flags($realif, -IFF_UP); + } + mwexec("/usr/sbin/arp -d -i " . escapeshellarg($realif) . " -a"); + } + break; + default: + if (does_interface_exist("$realif")) { + mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " delete", true); + interface_ipalias_cleanup($interface); + if ($destroy == true) { + pfSense_interface_flags($realif, -IFF_UP); + } + mwexec("/usr/sbin/arp -d -i " . escapeshellarg($realif) . " -a"); + } + break; + } + + $track6 = array(); + switch ($ifcfg['ipaddrv6']) { + case "slaac": + case "dhcp6": + $pidv6 = find_dhcp6c_process($realif); + if ($pidv6) { + posix_kill($pidv6, SIGTERM); + } + sleep(3); + unlink_if_exists("{$g['varetc_path']}/dhcp6c_{$interface}.conf"); + unlink_if_exists("{$g['varetc_path']}/dhcp6c_{$interface}_script.sh"); + unlink_if_exists("{$g['varetc_path']}/rtsold_{$realifv6}_script.sh"); + if (does_interface_exist($realifv6)) { + $ip6 = find_interface_ipv6($realifv6); + if (is_ipaddrv6($ip6) && $ip6 != "::") { + mwexec("/sbin/ifconfig " . escapeshellarg($realifv6) . " inet6 {$ip6} delete", true); + } + interface_ipalias_cleanup($interface, "inet6"); + if ($destroy == true) { + pfSense_interface_flags($realif, -IFF_UP); + } + //mwexec("/usr/sbin/arp -d -i " . escapeshellarg($realif) . " -a"); + } + $track6 = link_interface_to_track6($interface); + break; + case "6rd": + case "6to4": + $realif = "{$interface}_stf"; + if (does_interface_exist("$realif")) { + /* destroy stf interface if tunnel is being disabled or tunnel type is being changed */ + if (($ifcfg['ipaddrv6'] == '6rd' && (!isset($config['interfaces'][$interface]['ipaddrv6']) || $config['interfaces'][$interface]['ipaddrv6'] != '6rd')) || + ($ifcfg['ipaddrv6'] == '6to4' && (!isset($config['interfaces'][$interface]['ipaddrv6']) || $config['interfaces'][$interface]['ipaddrv6'] != '6to4'))) { + $destroy = true; + } else { + /* get_interface_ipv6() returns empty value if interface is being disabled */ + $ip6 = get_interface_ipv6($interface); + if (is_ipaddrv6($ip6)) { + mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 {$ip6} delete", true); + } + } + interface_ipalias_cleanup($interface, "inet6"); + if ($destroy == true) { + pfSense_interface_flags($realif, -IFF_UP); + } + } + $track6 = link_interface_to_track6($interface); + break; + default: + if (does_interface_exist("$realif")) { + $ip6 = get_interface_ipv6($interface); + if (is_ipaddrv6($ip6)) { + mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 {$ip6} delete", true); + } + if (!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6'])) { + mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 {$ifcfg['ipaddrv6']} delete", true); + } + interface_ipalias_cleanup($interface, "inet6"); + if ($destroy == true) { + pfSense_interface_flags($realif, -IFF_UP); + } + //mwexec("/usr/sbin/arp -d -i " . escapeshellarg($realif) . " -a"); + } + $track6 = link_interface_to_track6($interface); + break; + } + + if (!empty($track6) && is_array($track6)) { + if (!function_exists('services_dhcpd_configure')) { + require_once('services.inc'); + } + /* Bring down radvd and dhcp6 on these interfaces */ + services_dhcpd_configure('inet6', $track6); + } + + $old_router = ''; + if (file_exists("{$g['tmp_path']}/{$realif}_router")) { + $old_router = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router")); + } + + /* remove interface up file if it exists */ + unlink_if_exists("{$g['tmp_path']}/{$realif}up"); + unlink_if_exists("{$g['vardb_path']}/{$interface}ip"); + unlink_if_exists("{$g['vardb_path']}/{$interface}ipv6"); + unlink_if_exists("{$g['tmp_path']}/{$realif}_router"); + unlink_if_exists("{$g['tmp_path']}/{$realif}_routerv6"); + unlink_if_exists("{$g['varetc_path']}/nameserver_{$realif}"); + unlink_if_exists("{$g['varetc_path']}/searchdomain_{$realif}"); + + /* hostapd and wpa_supplicant do not need to be running when the interface is down. + * They will also use 100% CPU if running after the wireless clone gets deleted. */ + if (is_array($ifcfg['wireless'])) { + kill_hostapd($realif); + mwexec(kill_wpasupplicant($realif)); + } + + if ($destroy == true) { + if (preg_match("/^[a-z0-9]+^tun|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_stf$/i", $realif)) { + pfSense_interface_destroy($realif); + } + } + + return; +} + +function interfaces_carp_set_maintenancemode($carp_maintenancemode) { + global $config; + if (isset($config["virtualip_carp_maintenancemode"]) && $carp_maintenancemode == false) { + unset($config["virtualip_carp_maintenancemode"]); + write_config("Leave CARP maintenance mode"); + } else if (!isset($config["virtualip_carp_maintenancemode"]) && $carp_maintenancemode == true) { + $config["virtualip_carp_maintenancemode"] = true; + write_config("Enter CARP maintenance mode"); + } + + $viparr = &$config['virtualip']['vip']; + foreach ($viparr as $vip) { + if ($vip['mode'] == "carp") { + interface_carp_configure($vip); + } + } +} + +function interface_isppp_type($interface) { + global $config; + + if (!is_array($config['interfaces'][$interface])) { + return false; + } + + switch ($config['interfaces'][$interface]['ipaddr']) { + case 'pptp': + case 'l2tp': + case 'pppoe': + case 'ppp': + return true; + break; + default: + return false; + break; + } +} + +function interfaces_ptpid_used($ptpid) { + global $config; + + if (is_array($config['ppps']['ppp'])) { + foreach ($config['ppps']['ppp'] as & $settings) { + if ($ptpid == $settings['ptpid']) { + return true; + } + } + } + + return false; +} + +function interfaces_ptpid_next() { + + $ptpid = 0; + while (interfaces_ptpid_used($ptpid)) { + $ptpid++; + } + + return $ptpid; +} + +function getMPDCRONSettings($pppif) { + global $config; + + $cron_cmd_file = "{$g['varetc_path']}/pppoe_restart_{$pppif}"; + if (is_array($config['cron']['item'])) { + foreach ($config['cron']['item'] as $i => $item) { + if (stripos($item['command'], $cron_cmd_file) !== false) { + return array("ID" => $i, "ITEM" => $item); + } + } + } + + return NULL; +} + +function handle_pppoe_reset($post_array) { + global $config, $g; + + $pppif = "{$post_array['type']}{$post_array['ptpid']}"; + $cron_cmd_file = "{$g['varetc_path']}/pppoe_restart_{$pppif}"; + + if (!is_array($config['cron']['item'])) { + $config['cron']['item'] = array(); + } + + $itemhash = getMPDCRONSettings($pppif); + + // reset cron items if necessary and return + if (empty($post_array['pppoe-reset-type'])) { + if (isset($itemhash)) { + unset($config['cron']['item'][$itemhash['ID']]); + } + sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP"); + return; + } + + if (empty($itemhash)) { + $itemhash = array(); + } + $item = array(); + if (isset($post_array['pppoe-reset-type']) && $post_array['pppoe-reset-type'] == "custom") { + $item['minute'] = $post_array['pppoe_resetminute']; + $item['hour'] = $post_array['pppoe_resethour']; + if (isset($post_array['pppoe_resetdate']) && $post_array['pppoe_resetdate'] <> "") { + $date = explode("/", $post_array['pppoe_resetdate']); + $item['mday'] = $date[1]; + $item['month'] = $date[0]; + } else { + $item['mday'] = "*"; + $item['month'] = "*"; + } + $item['wday'] = "*"; + $item['who'] = "root"; + $item['command'] = $cron_cmd_file; + } else if (isset($post_array['pppoe-reset-type']) && $post_array['pppoe-reset-type'] == "preset") { + switch ($post_array['pppoe_pr_preset_val']) { + case "monthly": + $item['minute'] = "0"; + $item['hour'] = "0"; + $item['mday'] = "1"; + $item['month'] = "*"; + $item['wday'] = "*"; + break; + case "weekly": + $item['minute'] = "0"; + $item['hour'] = "0"; + $item['mday'] = "*"; + $item['month'] = "*"; + $item['wday'] = "0"; + break; + case "daily": + $item['minute'] = "0"; + $item['hour'] = "0"; + $item['mday'] = "*"; + $item['month'] = "*"; + $item['wday'] = "*"; + break; + case "hourly": + $item['minute'] = "0"; + $item['hour'] = "*"; + $item['mday'] = "*"; + $item['month'] = "*"; + $item['wday'] = "*"; + break; + } // end switch + $item['who'] = "root"; + $item['command'] = $cron_cmd_file; + } + if (empty($item)) { + return; + } + if (isset($itemhash['ID'])) { + $config['cron']['item'][$itemhash['ID']] = $item; + } else { + $config['cron']['item'][] = $item; + } +} + +/* + * This function can configure PPPoE, MLPPP (PPPoE), PPTP. + * It writes the mpd config file to /var/etc every time the link is opened. + */ +function interface_ppps_configure($interface) { + global $config, $g; + + /* Return for unassigned interfaces. This is a minimum requirement. */ + if (empty($config['interfaces'][$interface])) { + return 0; + } + $ifcfg = $config['interfaces'][$interface]; + if (!isset($ifcfg['enable'])) { + return 0; + } + + // mpd5 requires a /var/spool/lock directory for PPP modem links. + if (!is_dir("/var/spool/lock")) { + mkdir("/var/spool/lock", 0777, true); + } + // mpd5 modem chat script expected in the same directory as the mpd_xxx.conf files + if (!file_exists("{$g['varetc_path']}/mpd.script")) { + @symlink("/usr/local/sbin/mpd.script", "{$g['varetc_path']}/mpd.script"); + } + + if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) { + foreach ($config['ppps']['ppp'] as $pppid => $ppp) { + if ($ifcfg['if'] == $ppp['if']) { + break; + } + } + } + if (!$ppp || $ifcfg['if'] != $ppp['if']) { + log_error(sprintf(gettext("Can't find PPP config for %s in interface_ppps_configure()."), $ifcfg['if'])); + return 0; + } + $pppif = $ifcfg['if']; + if ($ppp['type'] == "ppp") { + $type = "modem"; + } else { + $type = $ppp['type']; + } + $upper_type = strtoupper($ppp['type']); + + /* XXX: This does not make sense and may create trouble + * comment it for now to be removed later on. + if (platform_booting()) { + $descr = isset($ifcfg['descr']) ? $ifcfg['descr'] : strtoupper($interface); + echo "starting {$pppif} link..."; + if (isvalidpid("{$g['varrun_path']}/{$ppp['type']}_{$interface}.pid")) + return 0; + } + */ + + $ports = explode(',', $ppp['ports']); + if ($type != "modem") { + foreach ($ports as $pid => $port) { + $ports[$pid] = get_real_interface($port); + if (empty($ports[$pid])) { + return 0; + } + } + } + $localips = explode(',', $ppp['localip']); + $gateways = explode(',', $ppp['gateway']); + $subnets = explode(',', $ppp['subnet']); + + /* We bring up the parent interface first because if DHCP is configured on the parent we need + * to obtain an address first so we can write it in the mpd .conf file for PPTP and L2TP configs + */ + foreach ($ports as $pid => $port) { + switch ($ppp['type']) { + case "pppoe": + /* Bring the parent interface up */ + interfaces_bring_up($port); + pfSense_ngctl_attach(".", $port); + /* Enable setautosrc to automatically change mac address if parent interface's changes */ + mwexec("ngctl msg {$port}: setautosrc 1"); + break; + case "pptp": + case "l2tp": + /* configure interface */ + if (is_ipaddr($localips[$pid])) { + // Manually configure interface IP/subnet + pfSense_interface_setaddress($port, "{$localips[$pid]}/{$subnets[$pid]}"); + interfaces_bring_up($port); + } else if (empty($localips[$pid])) { + $localips[$pid] = get_interface_ip($port); // try to get the interface IP from the port + } + + if (!is_ipaddr($localips[$pid])) { + log_error("Could not get a Local IP address for PPTP/L2TP link on {$port} in interfaces_ppps_configure. Using 0.0.0.0 ip!"); + $localips[$pid] = "0.0.0.0"; + } + if (!is_ipaddr($gateways[$pid])) { + log_error(sprintf(gettext('Could not get a PPTP/L2TP Remote IP address from %1$s for %2$s in interfaces_ppps_configure.'), $dhcp_gateway, $gway)); + return 0; + } + pfSense_ngctl_attach(".", $port); + break; + case "ppp": + if (!file_exists("{$port}")) { + log_error(sprintf(gettext("Device %s does not exist. PPP link cannot start without the modem device."), $port)); + return 0; + } + break; + default: + log_error(sprintf(gettext("Unknown %s configured as ppp interface."), $type)); + break; + } + } + + if (is_array($ports) && count($ports) > 1) { + $multilink = "enable"; + } else { + $multilink = "disable"; + } + + if ($type == "modem") { + if (is_ipaddr($ppp['localip'])) { + $localip = $ppp['localip']; + } else { + $localip = '0.0.0.0'; + } + + if (is_ipaddr($ppp['gateway'])) { + $gateway = $ppp['gateway']; + } else { + $gateway = "10.64.64.{$pppid}"; + } + $ranges = "{$localip}/0 {$gateway}/0"; + + if (empty($ppp['apnum'])) { + $ppp['apnum'] = 1; + } + } else { + $ranges = "0.0.0.0/0 0.0.0.0/0"; + } + + if (isset($ppp['ondemand'])) { + $ondemand = "enable"; + } else { + $ondemand = "disable"; + } + if (!isset($ppp['idletimeout'])) { + $ppp['idletimeout'] = 0; + } + + if (empty($ppp['username']) && $type == "modem") { + $ppp['username'] = "user"; + $ppp['password'] = "none"; + } + if (empty($ppp['password']) && $type == "modem") { + $passwd = "none"; + } else { + $passwd = base64_decode($ppp['password']); + } + + $bandwidths = explode(',', $ppp['bandwidth']); + $defaultmtu = "1492"; + if (!empty($ifcfg['mtu'])) { + $defaultmtu = intval($ifcfg['mtu']); + } + $mtus = explode(',', $ppp['mtu']); + $mrus = explode(',', $ppp['mru']); + + if (isset($ppp['mrru'])) { + $mrrus = explode(',', $ppp['mrru']); + } + + // Construct the mpd.conf file + $mpdconf = <<<EOD +startup: + # configure the console + set console close + # configure the web server + set web close + +default: +{$ppp['type']}client: + create bundle static {$interface} + set bundle enable ipv6cp + set iface name {$pppif} + +EOD; + $setdefaultgw = false; + $founddefaultgw = false; + if (is_array($config['gateways']['gateway_item'])) { + foreach ($config['gateways']['gateway_item'] as $gateway) { + if ($interface == $gateway['interface'] && isset($gateway['defaultgw'])) { + $setdefaultgw = true; + break; + } else if (isset($gateway['defaultgw']) && !empty($gateway['interface'])) { + $founddefaultgw = true; + break; + } + } + } + + if (($interface == "wan" && $founddefaultgw == false) || $setdefaultgw == true) { + $setdefaultgw = true; + $mpdconf .= <<<EOD + set iface route default + +EOD; + } + $mpdconf .= <<<EOD + set iface {$ondemand} on-demand + set iface idle {$ppp['idletimeout']} + +EOD; + + if (isset($ppp['ondemand'])) { + $mpdconf .= <<<EOD + set iface addrs 10.10.1.1 10.10.1.2 + +EOD; + } + + if (isset($ppp['tcpmssfix'])) { + $tcpmss = "disable"; + } else { + $tcpmss = "enable"; + } + $mpdconf .= <<<EOD + set iface {$tcpmss} tcpmssfix + +EOD; + + $mpdconf .= <<<EOD + set iface up-script /usr/local/sbin/ppp-linkup + set iface down-script /usr/local/sbin/ppp-linkdown + set ipcp ranges {$ranges} + +EOD; + if (isset($ppp['vjcomp'])) { + $mpdconf .= <<<EOD + set ipcp no vjcomp + +EOD; + } + + if (isset($config['system']['dnsallowoverride'])) { + $mpdconf .= <<<EOD + set ipcp enable req-pri-dns + set ipcp enable req-sec-dns + +EOD; + } + + if (!isset($ppp['verbose_log'])) { + $mpdconf .= <<<EOD + #log -bund -ccp -chat -iface -ipcp -lcp -link + +EOD; + } + + foreach ($ports as $pid => $port) { + $port = get_real_interface($port); + $mpdconf .= <<<EOD + + create link static {$interface}_link{$pid} {$type} + set link action bundle {$interface} + set link {$multilink} multilink + set link keep-alive 10 60 + set link max-redial 0 + +EOD; + if (isset($ppp['shortseq'])) { + $mpdconf .= <<<EOD + set link no shortseq + +EOD; + } + + if (isset($ppp['acfcomp'])) { + $mpdconf .= <<<EOD + set link no acfcomp + +EOD; + } + + if (isset($ppp['protocomp'])) { + $mpdconf .= <<<EOD + set link no protocomp + +EOD; + } + + $mpdconf .= <<<EOD + set link disable chap pap + set link accept chap pap eap + set link disable incoming + +EOD; + + + if (!empty($bandwidths[$pid])) { + $mpdconf .= <<<EOD + set link bandwidth {$bandwidths[$pid]} + +EOD; + } + + if (empty($mtus[$pid])) { + $mtus[$pid] = $defaultmtu; + } + $mpdconf .= <<<EOD + set link mtu {$mtus[$pid]} + +EOD; + + if (!empty($mrus[$pid])) { + $mpdconf .= <<<EOD + set link mru {$mrus[$pid]} + +EOD; + } + + if (!empty($mrrus[$pid])) { + $mpdconf .= <<<EOD + set link mrru {$mrrus[$pid]} + +EOD; + } + + $mpdconf .= <<<EOD + set auth authname "{$ppp['username']}" + set auth password {$passwd} + +EOD; + if ($type == "modem") { + $mpdconf .= <<<EOD + set modem device {$ppp['ports']} + set modem script DialPeer + set modem idle-script Ringback + set modem watch -cd + set modem var \$DialPrefix "DT" + set modem var \$Telephone "{$ppp['phone']}" + +EOD; + } + if (isset($ppp['connect-timeout']) && $type == "modem") { + $mpdconf .= <<<EOD + set modem var \$ConnectTimeout "{$ppp['connect-timeout']}" + +EOD; + } + if (isset($ppp['initstr']) && $type == "modem") { + $initstr = base64_decode($ppp['initstr']); + $mpdconf .= <<<EOD + set modem var \$InitString "{$initstr}" + +EOD; + } + if (isset($ppp['simpin']) && $type == "modem") { + if ($ppp['pin-wait'] == "") { + $ppp['pin-wait'] = 0; + } + $mpdconf .= <<<EOD + set modem var \$SimPin "{$ppp['simpin']}" + set modem var \$PinWait "{$ppp['pin-wait']}" + +EOD; + } + if (isset($ppp['apn']) && $type == "modem") { + $mpdconf .= <<<EOD + set modem var \$APN "{$ppp['apn']}" + set modem var \$APNum "{$ppp['apnum']}" + +EOD; + } + if ($type == "pppoe") { + // Send a null service name if none is set. + $provider = isset($ppp['provider']) ? $ppp['provider'] : ""; + $mpdconf .= <<<EOD + set pppoe service "{$provider}" + +EOD; + } + if ($type == "pppoe") { + $mpdconf .= <<<EOD + set pppoe iface {$port} + +EOD; + } + + if ($type == "pptp" || $type == "l2tp") { + $mpdconf .= <<<EOD + set {$type} self {$localips[$pid]} + set {$type} peer {$gateways[$pid]} + +EOD; + } + + $mpdconf .= "\topen\n"; + } //end foreach ($port) + + + /* Generate mpd.conf. If mpd_[interface].conf exists in the conf path, then link to it instead of generating a fresh conf file. */ + if (file_exists("{$g['conf_path']}/mpd_{$interface}.conf")) { + @symlink("{$g['conf_path']}/mpd_{$interface}.conf", "{$g['varetc_path']}/mpd_{$interface}.conf"); + } else { + $fd = fopen("{$g['varetc_path']}/mpd_{$interface}.conf", "w"); + if (!$fd) { + log_error(sprintf(gettext("Error: cannot open mpd_%s.conf in interface_ppps_configure().%s"), $interface, "\n")); + return 0; + } + // Write out mpd_ppp.conf + fwrite($fd, $mpdconf); + fclose($fd); + unset($mpdconf); + } + + // Create the uptime log if requested and if it doesn't exist already, or delete it if it is no longer requested. + if (isset($ppp['uptime'])) { + if (!file_exists("/conf/{$pppif}.log")) { + conf_mount_rw(); + file_put_contents("/conf/{$pppif}.log", ''); + conf_mount_ro(); + } + } else { + if (file_exists("/conf/{$pppif}.log")) { + conf_mount_rw(); + @unlink("/conf/{$pppif}.log"); + conf_mount_ro(); + } + } + + /* clean up old lock files */ + foreach ($ports as $port) { + if (file_exists("{$g['var_path']}/spool/lock/LCK..{$port}")) { + unlink("{$g['var_path']}/spool/lock/LCK..{$port}"); + } + } + + /* fire up mpd */ + mwexec("/usr/local/sbin/mpd5 -b -k -d {$g['varetc_path']} -f mpd_{$interface}.conf -p {$g['varrun_path']}/" . + escapeshellarg($ppp['type']) . "_{$interface}.pid -s ppp " . escapeshellarg($ppp['type']) . "client"); + + // Check for PPPoE periodic reset request + if ($type == "pppoe") { + if (!empty($ppp['pppoe-reset-type'])) { + interface_setup_pppoe_reset_file($ppp['if'], $interface); + } else { + interface_setup_pppoe_reset_file($ppp['if']); + } + } + /* wait for upto 10 seconds for the interface to appear (ppp(oe)) */ + $i = 0; + while ($i < 3) { + sleep(10); + if (does_interface_exist($ppp['if'], true)) { + break; + } + $i++; + } + + /* we only support the 3gstats.php for huawei modems for now. Will add more later. */ + /* We should be able to launch the right version for each modem */ + /* We can also guess the mondev from the manufacturer */ + exec("usbconfig | egrep -ie '(huawei)'", $usbmodemoutput); + mwexec("/bin/ps auxww|grep \"{$interface}\" |grep \"[3]gstats\" | awk '{print $2}' |xargs kill"); + foreach ($ports as $port) { + if (preg_match("/huawei/i", implode("\n", $usbmodemoutput))) { + $mondev = substr(basename($port), 0, -1); + $devlist = glob("/dev/{$mondev}?"); + $mondev = basename(end($devlist)); + } + if (preg_match("/zte/i", implode("\n", $usbmodemoutput))) { + $mondev = substr(basename($port), 0, -1) . "1"; + } + if ($mondev != '') { + log_error("Starting 3gstats.php on device '{$mondev}' for interface '{$interface}'"); + mwexec_bg("/usr/local/bin/3gstats.php {$mondev} {$interface}"); + } + } + + return 1; +} + +function interfaces_sync_setup() { + global $g, $config; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "interfaces_sync_setup() being called $mt\n"; + } + + if (platform_booting()) { + echo gettext("Configuring CARP settings..."); + mute_kernel_msgs(); + } + + /* suck in configuration items */ + if ($config['hasync']) { + $pfsyncenabled = $config['hasync']['pfsyncenabled']; + $pfsyncinterface = $config['hasync']['pfsyncinterface']; + $pfsyncpeerip = $config['hasync']['pfsyncpeerip']; + } else { + unset($pfsyncinterface); + unset($pfsyncenabled); + } + + set_sysctl(array( + "net.inet.carp.preempt" => "1", + "net.inet.carp.log" => "1") + ); + + if (!empty($pfsyncinterface)) { + $carp_sync_int = get_real_interface($pfsyncinterface); + } else { + unset($carp_sync_int); + } + + /* setup pfsync interface */ + if (isset($carp_sync_int) and isset($pfsyncenabled)) { + if (is_ipaddr($pfsyncpeerip)) { + $syncpeer = "syncpeer {$pfsyncpeerip}"; + } else { + $syncpeer = "-syncpeer"; + } + + mwexec("/sbin/ifconfig pfsync0 syncdev {$carp_sync_int} {$syncpeer} up", false); + mwexec("/sbin/ifconfig pfsync0 -defer", false); + + sleep(1); + + /* XXX: Handle an issue with pfsync(4) and carp(4). In a cluster carp will come up before pfsync(4) has updated and so will cause issues + * for existing sessions. + */ + log_error("waiting for pfsync..."); + $i = 0; + while (intval(trim(`/sbin/ifconfig pfsync0 | /usr/bin/grep 'syncok: 0' | /usr/bin/grep -v grep | /usr/bin/wc -l`)) == 0 && $i < 30) { + $i++; + sleep(1); + } + log_error("pfsync done in $i seconds."); + log_error("Configuring CARP settings finalize..."); + } else { + mwexec("/sbin/ifconfig pfsync0 -syncdev -syncpeer down", false); + } + + if ($config['virtualip']['vip']) { + set_single_sysctl("net.inet.carp.allow", "1"); + } else { + set_single_sysctl("net.inet.carp.allow", "0"); + } + + if (platform_booting()) { + unmute_kernel_msgs(); + echo gettext("done.") . "\n"; + } +} + +function interface_proxyarp_configure($interface = "") { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "interface_proxyarp_configure() being called $mt\n"; + } + + /* kill any running choparp */ + if (empty($interface)) { + killbyname("choparp"); + } else { + $vipif = get_real_interface($interface); + if (file_exists("{$g['varrun_path']}/choparp_{$vipif}.pid")) { + killbypid("{$g['varrun_path']}/choparp_{$vipif}.pid"); + } + } + + $paa = array(); + if (!empty($config['virtualip']) && is_array($config['virtualip']['vip'])) { + + /* group by interface */ + foreach ($config['virtualip']['vip'] as $vipent) { + if ($vipent['mode'] === "proxyarp") { + if ($vipent['interface']) { + $proxyif = $vipent['interface']; + } else { + $proxyif = "wan"; + } + + if (!empty($interface) && $interface != $proxyif) { + continue; + } + + if (!is_array($paa[$proxyif])) { + $paa[$proxyif] = array(); + } + + $paa[$proxyif][] = $vipent; + } + } + } + + if (!empty($interface)) { + if (is_array($paa[$interface])) { + $paaifip = get_interface_ip($interface); + if (!is_ipaddr($paaifip)) { + return; + } + $args = get_real_interface($interface) . " auto"; + foreach ($paa[$interface] as $paent) { + if (isset($paent['subnet'])) { + $args .= " " . escapeshellarg("{$paent['subnet']}/{$paent['subnet_bits']}"); + } else if (isset($paent['range'])) { + $args .= " " . escapeshellarg($paent['range']['from'] . "-" . $paent['range']['to']); + } + } + mwexec_bg("/usr/local/sbin/choparp " . $args); + } + } else if (count($paa) > 0) { + foreach ($paa as $paif => $paents) { + $paaifip = get_interface_ip($paif); + if (!is_ipaddr($paaifip)) { + continue; + } + $args = get_real_interface($paif) . " auto"; + foreach ($paents as $paent) { + if (isset($paent['subnet'])) { + $args .= " " . escapeshellarg("{$paent['subnet']}/{$paent['subnet_bits']}"); + } else if (isset($paent['range'])) { + $args .= " " . escapeshellarg($paent['range']['from'] . "-" . $paent['range']['to']); + } + } + mwexec_bg("/usr/local/sbin/choparp " . $args); + } + } +} + +function interface_ipalias_cleanup($interface, $inet = "inet4") { + global $g, $config; + + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['mode'] == "ipalias" && $vip['interface'] == $interface) { + if ($inet == "inet6" && is_ipaddrv6($vip['subnet'])) { + interface_vip_bring_down($vip); + } else if ($inet == "inet4" && is_ipaddrv4($vip['subnet'])) { + interface_vip_bring_down($vip); + } + } + } + } +} + +function interfaces_vips_configure($interface = "") { + global $g, $config; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "interfaces_vips_configure() being called $mt\n"; + } + $paa = array(); + if (is_array($config['virtualip']['vip'])) { + $carp_setuped = false; + $anyproxyarp = false; + foreach ($config['virtualip']['vip'] as $vip) { + switch ($vip['mode']) { + case "proxyarp": + /* nothing it is handled on interface_proxyarp_configure() */ + if ($interface <> "" && $vip['interface'] <> $interface) { + continue; + } + $anyproxyarp = true; + break; + case "ipalias": + if ($interface <> "" && $vip['interface'] <> $interface) { + continue; + } + interface_ipalias_configure($vip); + break; + case "carp": + if ($interface <> "" && $vip['interface'] <> $interface) { + continue; + } + if ($carp_setuped == false) { + $carp_setuped = true; + } + interface_carp_configure($vip); + break; + } + } + if ($carp_setuped == true) { + interfaces_sync_setup(); + } + if ($anyproxyarp == true) { + interface_proxyarp_configure(); + } + } +} + +function interface_ipalias_configure(&$vip) { + global $config; + + if ($vip['mode'] != 'ipalias') { + return; + } + + if ($vip['interface'] != 'lo0' && stripos($vip['interface'], '_vip') === false) { + if (!isset($config['interfaces'][$vip['interface']])) { + return; + } + + if (!isset($config['interfaces'][$vip['interface']]['enable'])) { + return; + } + } + + $af = 'inet'; + if (is_ipaddrv6($vip['subnet'])) { + $af = 'inet6'; + } + $iface = $vip['interface']; + $vipadd = ''; + if (strpos($vip['interface'], '_vip')) { + $carpvip = get_configured_carp_interface_list($vip['interface'], $af, 'vip'); + $iface = $carpvip['interface']; + $vipadd = "vhid {$carpvip['vhid']}"; + } + $if = get_real_interface($iface); + mwexec("/sbin/ifconfig " . escapeshellarg($if) ." {$af} ". escapeshellarg($vip['subnet']) ."/" . escapeshellarg($vip['subnet_bits']) . " alias {$vipadd}"); + unset($iface, $af, $if, $carpvip, $vipadd); +} + +function interface_reload_carps($cif) { + global $config; + + $carpifs = link_ip_to_carp_interface(find_interface_ip($cif)); + if (empty($carpifs)) { + return; + } + + $carps = explode(" ", $carpifs); + if (is_array($config['virtualip']['vip'])) { + $viparr = &$config['virtualip']['vip']; + foreach ($viparr as $vip) { + if (in_array($vip['carpif'], $carps)) { + switch ($vip['mode']) { + case "carp": + interface_vip_bring_down($vip); + sleep(1); + interface_carp_configure($vip); + break; + case "ipalias": + interface_vip_bring_down($vip); + sleep(1); + interface_ipalias_configure($vip); + break; + } + } + } + } +} + +function interface_carp_configure(&$vip) { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "interface_carp_configure() being called $mt\n"; + } + + if ($vip['mode'] != "carp") { + return; + } + + /* NOTE: Maybe its useless nowadays */ + $realif = get_real_interface($vip['interface']); + if (!does_interface_exist($realif)) { + file_notice("CARP", sprintf(gettext("Interface specified for the virtual IP address %s does not exist. Skipping this VIP."), $vip['subnet']), "Firewall: Virtual IP", ""); + return; + } + + $vip_password = $vip['password']; + $vip_password = escapeshellarg(addslashes(str_replace(" ", "", $vip_password))); + if ($vip['password'] != "") { + $password = " pass {$vip_password}"; + } + + $advbase = ""; + if (!empty($vip['advbase'])) { + $advbase = "advbase " . escapeshellarg($vip['advbase']); + } + + $carp_maintenancemode = isset($config["virtualip_carp_maintenancemode"]); + if ($carp_maintenancemode) { + $advskew = "advskew 254"; + } else { + $advskew = "advskew " . escapeshellarg($vip['advskew']); + } + + mwexec("/sbin/ifconfig {$realif} vhid " . escapeshellarg($vip['vhid']) . " {$advskew} {$advbase} {$password}"); + + if (is_ipaddrv4($vip['subnet'])) { + mwexec("/sbin/ifconfig {$realif} " . escapeshellarg($vip['subnet']) . "/" . escapeshellarg($vip['subnet_bits']) . " alias vhid " . escapeshellarg($vip['vhid'])); + } else if (is_ipaddrv6($vip['subnet'])) { + mwexec("/sbin/ifconfig {$realif} inet6 " . escapeshellarg($vip['subnet']) . " prefixlen " . escapeshellarg($vip['subnet_bits']) . " alias vhid " . escapeshellarg($vip['vhid'])); + } + + return $realif; +} + +function interface_wireless_clone($realif, $wlcfg) { + global $config, $g; + /* Check to see if interface has been cloned as of yet. + * If it has not been cloned then go ahead and clone it. + */ + $needs_clone = false; + if (is_array($wlcfg['wireless'])) { + $wlcfg_mode = $wlcfg['wireless']['mode']; + } else { + $wlcfg_mode = $wlcfg['mode']; + } + switch ($wlcfg_mode) { + case "hostap": + $mode = "wlanmode hostap"; + break; + case "adhoc": + $mode = "wlanmode adhoc"; + break; + default: + $mode = ""; + break; + } + $baseif = interface_get_wireless_base($wlcfg['if']); + if (does_interface_exist($realif)) { + exec("/sbin/ifconfig " . escapeshellarg($realif), $output, $ret); + $ifconfig_str = implode($output); + if (($wlcfg_mode == "hostap") && (!preg_match("/hostap/si", $ifconfig_str))) { + log_error(sprintf(gettext("Interface %s changed to hostap mode"), $realif)); + $needs_clone = true; + } + if (($wlcfg_mode == "adhoc") && (!preg_match("/adhoc/si", $ifconfig_str))) { + log_error(sprintf(gettext("Interface %s changed to adhoc mode"), $realif)); + $needs_clone = true; + } + if (($wlcfg_mode == "bss") && (preg_match("/hostap|adhoc/si", $ifconfig_str))) { + log_error(sprintf(gettext("Interface %s changed to infrastructure mode"), $realif)); + $needs_clone = true; + } + } else { + $needs_clone = true; + } + + if ($needs_clone == true) { + /* remove previous instance if it exists */ + if (does_interface_exist($realif)) { + pfSense_interface_destroy($realif); + } + + log_error(sprintf(gettext("Cloning new wireless interface %s"), $realif)); + // Create the new wlan interface. FreeBSD returns the new interface name. + // example: wlan2 + exec("/sbin/ifconfig wlan create wlandev {$baseif} {$mode} bssid 2>&1", $out, $ret); + if ($ret <> 0) { + log_error(sprintf(gettext('Failed to clone interface %1$s with error code %2$s, output %3$s'), $baseif, $ret, $out[0])); + return false; + } + $newif = trim($out[0]); + // Rename the interface to {$parentnic}_wlan{$number}#: EX: ath0_wlan0 + pfSense_interface_rename($newif, $realif); + file_put_contents("{$g['tmp_path']}/{$realif}_oldmac", get_interface_mac($realif)); + } + return true; +} + +function interface_sync_wireless_clones(&$ifcfg, $sync_changes = false) { + global $config, $g; + + $shared_settings = array('standard', 'turbo', 'protmode', 'txpower', 'channel', + 'diversity', 'txantenna', 'rxantenna', 'distance', + 'regdomain', 'regcountry', 'reglocation'); + + if (!is_interface_wireless($ifcfg['if'])) { + return; + } + + $baseif = interface_get_wireless_base($ifcfg['if']); + + // Sync shared settings for assigned clones + $iflist = get_configured_interface_list(false, true); + foreach ($iflist as $if) { + if ($baseif == interface_get_wireless_base($config['interfaces'][$if]['if']) && $ifcfg['if'] != $config['interfaces'][$if]['if']) { + if (isset($config['interfaces'][$if]['wireless']['standard']) || $sync_changes) { + foreach ($shared_settings as $setting) { + if ($sync_changes) { + if (isset($ifcfg['wireless'][$setting])) { + $config['interfaces'][$if]['wireless'][$setting] = $ifcfg['wireless'][$setting]; + } else if (isset($config['interfaces'][$if]['wireless'][$setting])) { + unset($config['interfaces'][$if]['wireless'][$setting]); + } + } else { + if (isset($config['interfaces'][$if]['wireless'][$setting])) { + $ifcfg['wireless'][$setting] = $config['interfaces'][$if]['wireless'][$setting]; + } else if (isset($ifcfg['wireless'][$setting])) { + unset($ifcfg['wireless'][$setting]); + } + } + } + if (!$sync_changes) { + break; + } + } + } + } + + // Read or write settings at shared area + if (isset($config['wireless']['interfaces'][$baseif]) && is_array($config['wireless']['interfaces'][$baseif])) { + foreach ($shared_settings as $setting) { + if ($sync_changes) { + if (isset($ifcfg['wireless'][$setting])) { + $config['wireless']['interfaces'][$baseif][$setting] = $ifcfg['wireless'][$setting]; + } else if (isset($config['wireless']['interfaces'][$baseif][$setting])) { + unset($config['wireless']['interfaces'][$baseif][$setting]); + } + } else if (isset($config['wireless']['interfaces'][$baseif][$setting])) { + if (isset($config['wireless']['interfaces'][$baseif][$setting])) { + $ifcfg['wireless'][$setting] = $config['wireless']['interfaces'][$baseif][$setting]; + } else if (isset($ifcfg['wireless'][$setting])) { + unset($ifcfg['wireless'][$setting]); + } + } + } + } + + // Sync the mode on the clone creation page with the configured mode on the interface + if (interface_is_wireless_clone($ifcfg['if']) && isset($config['wireless']['clone']) && is_array($config['wireless']['clone'])) { + foreach ($config['wireless']['clone'] as &$clone) { + if ($clone['cloneif'] == $ifcfg['if']) { + if ($sync_changes) { + $clone['mode'] = $ifcfg['wireless']['mode']; + } else { + $ifcfg['wireless']['mode'] = $clone['mode']; + } + break; + } + } + unset($clone); + } +} + +function interface_wireless_configure($if, &$wl, &$wlcfg) { + global $config, $g; + + /* open up a shell script that will be used to output the commands. + * since wireless is changing a lot, these series of commands are fragile + * and will sometimes need to be verified by a operator by executing the command + * and returning the output of the command to the developers for inspection. please + * do not change this routine from a shell script to individual exec commands. -sullrich + */ + + // Remove script file + unlink_if_exists("{$g['tmp_path']}/{$if}_setup.sh"); + + // Clone wireless nic if needed. + interface_wireless_clone($if, $wl); + + // Reject inadvertent changes to shared settings in case the interface hasn't been configured. + interface_sync_wireless_clones($wl, false); + + $fd_set = fopen("{$g['tmp_path']}/{$if}_setup.sh", "w"); + fwrite($fd_set, "#!/bin/sh\n"); + fwrite($fd_set, "# {$g['product_name']} wireless configuration script.\n\n"); + + $wlan_setup_log = fopen("{$g['tmp_path']}/{$if}_setup.log", "w"); + + /* set values for /path/program */ + $hostapd = "/usr/sbin/hostapd"; + $wpa_supplicant = "/usr/sbin/wpa_supplicant"; + $ifconfig = "/sbin/ifconfig"; + $sysctl = "/sbin/sysctl"; + $killall = "/usr/bin/killall"; + + /* Set all wireless ifconfig variables (split up to get rid of needed checking) */ + + $wlcmd = array(); + $wl_sysctl = array(); + /* Make sure it's up */ + $wlcmd[] = "up"; + /* Set a/b/g standard */ + $standard = str_replace(" Turbo", "", $wlcfg['standard']); + /* skip mode entirely for "auto" */ + if ($wlcfg['standard'] != "auto") { + $wlcmd[] = "mode " . escapeshellarg($standard); + } + + /* XXX: Disable ampdu for now on mwl when running in 11n mode + * to prevent massive packet loss under certain conditions. */ + if (preg_match("/^mwl/i", $if) && ($standard == "11ng" || $standard == "11na")) { + $wlcmd[] = "-ampdu"; + } + + /* Set ssid */ + if ($wlcfg['ssid']) { + $wlcmd[] = "ssid " .escapeshellarg($wlcfg['ssid']); + } + + /* Set 802.11g protection mode */ + $wlcmd[] = "protmode " . escapeshellarg($wlcfg['protmode']); + + /* set wireless channel value */ + if (isset($wlcfg['channel'])) { + if ($wlcfg['channel'] == "0") { + $wlcmd[] = "channel any"; + } else { + $wlcmd[] = "channel " . escapeshellarg($wlcfg['channel']); + } + } + + /* Set antenna diversity value */ + if (isset($wlcfg['diversity'])) { + $wl_sysctl[] = "diversity=" . escapeshellarg($wlcfg['diversity']); + } + + /* Set txantenna value */ + if (isset($wlcfg['txantenna'])) { + $wl_sysctl[] = "txantenna=" . escapeshellarg($wlcfg['txantenna']); + } + + /* Set rxantenna value */ + if (isset($wlcfg['rxantenna'])) { + $wl_sysctl[] = "rxantenna=" . escapeshellarg($wlcfg['rxantenna']); + } + + /* set Distance value */ + if ($wlcfg['distance']) { + $distance = escapeshellarg($wlcfg['distance']); + } + + /* Set wireless hostap mode */ + if ($wlcfg['mode'] == "hostap") { + $wlcmd[] = "mediaopt hostap"; + } else { + $wlcmd[] = "-mediaopt hostap"; + } + + /* Set wireless adhoc mode */ + if ($wlcfg['mode'] == "adhoc") { + $wlcmd[] = "mediaopt adhoc"; + } else { + $wlcmd[] = "-mediaopt adhoc"; + } + + /* Not necessary to set BSS mode as this is default if adhoc and/or hostap is NOT set */ + + /* handle hide ssid option */ + if (isset($wlcfg['hidessid']['enable'])) { + $wlcmd[] = "hidessid"; + } else { + $wlcmd[] = "-hidessid"; + } + + /* handle pureg (802.11g) only option */ + if (isset($wlcfg['pureg']['enable'])) { + $wlcmd[] = "mode 11g pureg"; + } else { + $wlcmd[] = "-pureg"; + } + + /* handle puren (802.11n) only option */ + if (isset($wlcfg['puren']['enable'])) { + $wlcmd[] = "puren"; + } else { + $wlcmd[] = "-puren"; + } + + /* enable apbridge option */ + if (isset($wlcfg['apbridge']['enable'])) { + $wlcmd[] = "apbridge"; + } else { + $wlcmd[] = "-apbridge"; + } + + /* handle turbo option */ + if (isset($wlcfg['turbo']['enable'])) { + $wlcmd[] = "mediaopt turbo"; + } else { + $wlcmd[] = "-mediaopt turbo"; + } + + /* handle txpower setting */ + // or don't. this has issues at the moment. + /* + if ($wlcfg['txpower'] <> "" && is_numeric($wlcfg['txpower'])) { + $wlcmd[] = "txpower " . escapeshellarg($wlcfg['txpower']); + }*/ + + /* handle wme option */ + if (isset($wlcfg['wme']['enable'])) { + $wlcmd[] = "wme"; + } else { + $wlcmd[] = "-wme"; + } + + /* set up wep if enabled */ + $wepset = ""; + if (isset($wlcfg['wep']['enable']) && is_array($wlcfg['wep']['key'])) { + switch ($wlcfg['wpa']['auth_algs']) { + case "1": + $wepset .= "authmode open wepmode on "; + break; + case "2": + $wepset .= "authmode shared wepmode on "; + break; + case "3": + $wepset .= "authmode mixed wepmode on "; + } + $i = 1; + foreach ($wlcfg['wep']['key'] as $wepkey) { + $wepset .= "wepkey " . escapeshellarg("{$i}:{$wepkey['value']}") . " "; + if (isset($wepkey['txkey'])) { + $wlcmd[] = "weptxkey {$i} "; + } + $i++; + } + $wlcmd[] = $wepset; + } else if (isset($wlcfg['wpa']['enable'])) { + $wlcmd[] = "authmode wpa wepmode off "; + } else { + $wlcmd[] = "authmode open wepmode off "; + } + + kill_hostapd($if); + mwexec(kill_wpasupplicant("{$if}")); + + /* generate wpa_supplicant/hostap config if wpa is enabled */ + conf_mount_rw(); + + switch ($wlcfg['mode']) { + case 'bss': + if (isset($wlcfg['wpa']['enable'])) { + $wpa .= <<<EOD +ctrl_interface={$g['varrun_path']}/wpa_supplicant +ctrl_interface_group=0 +ap_scan=1 +#fast_reauth=1 +network={ +ssid="{$wlcfg['ssid']}" +scan_ssid=1 +priority=5 +key_mgmt={$wlcfg['wpa']['wpa_key_mgmt']} +psk="{$wlcfg['wpa']['passphrase']}" +pairwise={$wlcfg['wpa']['wpa_pairwise']} +group={$wlcfg['wpa']['wpa_pairwise']} +} +EOD; + + @file_put_contents("{$g['varetc_path']}/wpa_supplicant_{$if}.conf", $wpa); + unset($wpa); + } + break; + case 'hostap': + if (!empty($wlcfg['wpa']['passphrase'])) { + $wpa_passphrase = "wpa_passphrase={$wlcfg['wpa']['passphrase']}\n"; + } else { + $wpa_passphrase = ""; + } + if (isset($wlcfg['wpa']['enable'])) { + $wpa .= <<<EOD +interface={$if} +driver=bsd +logger_syslog=-1 +logger_syslog_level=0 +logger_stdout=-1 +logger_stdout_level=0 +dump_file={$g['tmp_path']}/hostapd_{$if}.dump +ctrl_interface={$g['varrun_path']}/hostapd +ctrl_interface_group=wheel +#accept_mac_file={$g['tmp_path']}/hostapd_{$if}.accept +#deny_mac_file={$g['tmp_path']}/hostapd_{$if}.deny +#macaddr_acl={$wlcfg['wpa']['macaddr_acl']} +ssid={$wlcfg['ssid']} +debug={$wlcfg['wpa']['debug_mode']} +auth_algs={$wlcfg['wpa']['auth_algs']} +wpa={$wlcfg['wpa']['wpa_mode']} +wpa_key_mgmt={$wlcfg['wpa']['wpa_key_mgmt']} +wpa_pairwise={$wlcfg['wpa']['wpa_pairwise']} +wpa_group_rekey={$wlcfg['wpa']['wpa_group_rekey']} +wpa_gmk_rekey={$wlcfg['wpa']['wpa_gmk_rekey']} +wpa_strict_rekey={$wlcfg['wpa']['wpa_strict_rekey']} +{$wpa_passphrase} + +EOD; + + if (isset($wlcfg['wpa']['rsn_preauth'])) { + $wpa .= <<<EOD +# Enable the next lines for preauth when roaming. Interface = wired or wireless interface talking to the AP you want to roam from/to +rsn_preauth=1 +rsn_preauth_interfaces={$if} + +EOD; + } + if (is_array($wlcfg['wpa']['ieee8021x']) && isset($wlcfg['wpa']['ieee8021x']['enable'])) { + $wpa .= "ieee8021x=1\n"; + + if (!empty($wlcfg['auth_server_addr']) && !empty($wlcfg['auth_server_shared_secret'])) { + $auth_server_port = "1812"; + if (!empty($wlcfg['auth_server_port']) && is_numeric($wlcfg['auth_server_port'])) { + $auth_server_port = intval($wlcfg['auth_server_port']); + } + $wpa .= <<<EOD + +auth_server_addr={$wlcfg['auth_server_addr']} +auth_server_port={$auth_server_port} +auth_server_shared_secret={$wlcfg['auth_server_shared_secret']} + +EOD; + if (!empty($wlcfg['auth_server_addr2']) && !empty($wlcfg['auth_server_shared_secret2'])) { + $auth_server_port2 = "1812"; + if (!empty($wlcfg['auth_server_port2']) && is_numeric($wlcfg['auth_server_port2'])) { + $auth_server_port2 = intval($wlcfg['auth_server_port2']); + } + + $wpa .= <<<EOD +auth_server_addr={$wlcfg['auth_server_addr2']} +auth_server_port={$auth_server_port2} +auth_server_shared_secret={$wlcfg['auth_server_shared_secret2']} + +EOD; + } + } + } + + @file_put_contents("{$g['varetc_path']}/hostapd_{$if}.conf", $wpa); + unset($wpa); + } + break; + } + + /* + * all variables are set, lets start up everything + */ + + $baseif = interface_get_wireless_base($if); + preg_match("/^(.*?)([0-9]*)$/", $baseif, $baseif_split); + $wl_sysctl_prefix = 'dev.' . $baseif_split[1] . '.' . $baseif_split[2]; + + /* set sysctls for the wireless interface */ + if (!empty($wl_sysctl)) { + fwrite($fd_set, "# sysctls for {$baseif}\n"); + foreach ($wl_sysctl as $wl_sysctl_line) { + fwrite($fd_set, "{$sysctl} {$wl_sysctl_prefix}.{$wl_sysctl_line}\n"); + } + } + + /* set ack timers according to users preference (if he/she has any) */ + if ($distance) { + fwrite($fd_set, "# Enable ATH distance settings\n"); + fwrite($fd_set, "/sbin/athctrl.sh -i {$baseif} -d {$distance}\n"); + } + + if (isset($wlcfg['wpa']['enable'])) { + if ($wlcfg['mode'] == "bss") { + fwrite($fd_set, "{$wpa_supplicant} -B -i {$if} -c {$g['varetc_path']}/wpa_supplicant_{$if}.conf\n"); + } + if ($wlcfg['mode'] == "hostap") { + /* add line to script to restore old mac to make hostapd happy */ + if (file_exists("{$g['tmp_path']}/{$if}_oldmac")) { + $if_oldmac = file_get_contents("{$g['tmp_path']}/{$if}_oldmac"); + if (is_macaddr($if_oldmac)) { + fwrite($fd_set, "{$ifconfig} " . escapeshellarg($if) . + " link " . escapeshellarg($if_oldmac) . "\n"); + } + } + + fwrite($fd_set, "{$hostapd} -B -P {$g['varrun_path']}/hostapd_{$if}.pid {$g['varetc_path']}/hostapd_{$if}.conf\n"); + + /* add line to script to restore spoofed mac after running hostapd */ + if (file_exists("{$g['tmp_path']}/{$if}_oldmac")) { + if ($wl['spoofmac']) { + $if_curmac = $wl['spoofmac']; + } else { + $if_curmac = get_interface_mac($if); + } + if (is_macaddr($if_curmac)) { + fwrite($fd_set, "{$ifconfig} " . escapeshellarg($if) . + " link " . escapeshellarg($if_curmac) . "\n"); + } + } + } + } + + fclose($fd_set); + conf_mount_ro(); + + /* Making sure regulatory settings have actually changed + * before applying, because changing them requires bringing + * down all wireless networks on the interface. */ + exec("{$ifconfig} " . escapeshellarg($if), $output); + $ifconfig_str = implode($output); + unset($output); + $reg_changing = false; + + /* special case for the debug country code */ + if ($wlcfg['regcountry'] == 'DEBUG' && !preg_match("/\sregdomain\s+DEBUG\s/si", $ifconfig_str)) { + $reg_changing = true; + } else if ($wlcfg['regdomain'] && !preg_match("/\sregdomain\s+{$wlcfg['regdomain']}\s/si", $ifconfig_str)) { + $reg_changing = true; + } else if ($wlcfg['regcountry'] && !preg_match("/\scountry\s+{$wlcfg['regcountry']}\s/si", $ifconfig_str)) { + $reg_changing = true; + } else if ($wlcfg['reglocation'] == 'anywhere' && preg_match("/\s(indoor|outdoor)\s/si", $ifconfig_str)) { + $reg_changing = true; + } else if ($wlcfg['reglocation'] && $wlcfg['reglocation'] != 'anywhere' && !preg_match("/\s{$wlcfg['reglocation']}\s/si", $ifconfig_str)) { + $reg_changing = true; + } + + if ($reg_changing) { + /* set regulatory domain */ + if ($wlcfg['regdomain']) { + $wlregcmd[] = "regdomain " . escapeshellarg($wlcfg['regdomain']); + } + + /* set country */ + if ($wlcfg['regcountry']) { + $wlregcmd[] = "country " . escapeshellarg($wlcfg['regcountry']); + } + + /* set location */ + if ($wlcfg['reglocation']) { + $wlregcmd[] = escapeshellarg($wlcfg['reglocation']); + } + + $wlregcmd_args = implode(" ", $wlregcmd); + + /* build a complete list of the wireless clones for this interface */ + $clone_list = array(); + if (does_interface_exist(interface_get_wireless_clone($baseif))) { + $clone_list[] = interface_get_wireless_clone($baseif); + } + if (isset($config['wireless']['clone']) && is_array($config['wireless']['clone'])) { + foreach ($config['wireless']['clone'] as $clone) { + if ($clone['if'] == $baseif) { + $clone_list[] = $clone['cloneif']; + } + } + } + + /* find which clones are up and bring them down */ + $clones_up = array(); + foreach ($clone_list as $clone_if) { + $clone_status = pfSense_get_interface_addresses($clone_if); + if ($clone_status['status'] == 'up') { + $clones_up[] = $clone_if; + mwexec("{$ifconfig} " . escapeshellarg($clone_if) . " down"); + } + } + + /* apply the regulatory settings */ + mwexec("{$ifconfig} " . escapeshellarg($if) . " {$wlregcmd_args}"); + fwrite($wlan_setup_log, "$ifconfig" . escapeshellarg($if) . "$wlregcmd_args \n"); + + /* bring the clones back up that were previously up */ + foreach ($clones_up as $clone_if) { + interfaces_bring_up($clone_if); + + /* + * Rerun the setup script for the interface if it isn't this interface, the interface + * is in infrastructure mode, and WPA is enabled. + * This can be removed if wpa_supplicant stops dying when you bring the interface down. + */ + if ($clone_if != $if) { + $friendly_if = convert_real_interface_to_friendly_interface_name($clone_if); + if ((!empty($friendly_if)) && + ($config['interfaces'][$friendly_if]['wireless']['mode'] == "bss") && + (isset($config['interfaces'][$friendly_if]['wireless']['wpa']['enable']))) { + mwexec("/bin/sh {$g['tmp_path']}/" . escapeshellarg($clone_if) . "_setup.sh"); + } + } + } + } + + /* 20150318 cmb - Note: the below no longer appears to be true on FreeBSD 10.x, so don't set + * mode twice (for now at least). This can be removed entirely in the future if no problems are found + + * The mode must be specified in a separate command before ifconfig + * will allow the mode and channel at the same time in the next. */ + //mwexec("/sbin/ifconfig " . escapeshellarg($if) . " mode " . escapeshellarg($standard)); + //fwrite($wlan_setup_log, "/sbin/ifconfig " . escapeshellarg($if) . " mode " . escapeshellarg($standard) . "\n"); + + /* configure wireless */ + $wlcmd_args = implode(" ", $wlcmd); + mwexec("/sbin/ifconfig " . escapeshellarg($if) . " " . $wlcmd_args, false); + fwrite($wlan_setup_log, "/sbin/ifconfig " . escapeshellarg($if) . " " . "$wlcmd_args \n"); + fclose($wlan_setup_log); + + unset($wlcmd_args, $wlcmd); + + + sleep(1); + /* execute hostapd and wpa_supplicant if required in shell */ + mwexec("/bin/sh {$g['tmp_path']}/" . escapeshellarg($if) . "_setup.sh"); + + return 0; + +} + +function kill_hostapd($interface) { + global $g; + + if (isvalidpid("{$g['varrun_path']}/hostapd_{$interface}.pid")) { + return killbypid("{$g['varrun_path']}/hostapd_{$interface}.pid"); + } +} + +function kill_wpasupplicant($interface) { + return "/bin/pkill -f \"wpa_supplicant .*{$interface}\\.conf\"\n"; +} + +function find_dhclient_process($interface) { + if ($interface) { + $pid = `/bin/pgrep -axf "dhclient: {$interface}"`; + } else { + $pid = 0; + } + + return intval($pid); +} + +function kill_dhclient_process($interface) { + if (empty($interface) || !does_interface_exist($interface)) { + return; + } + + $i = 0; + while ((($pid = find_dhclient_process($interface)) != 0) && ($i < 3)) { + /* 3rd time make it die for sure */ + $sig = ($i == 2 ? SIGKILL : SIGTERM); + posix_kill($pid, $sig); + sleep(1); + $i++; + } + unset($i); +} + +function find_dhcp6c_process($interface) { + global $g; + + if ($interface && isvalidpid("{$g['varrun_path']}/dhcp6c_{$interface}.pid")) { + $pid = trim(file_get_contents("{$g['varrun_path']}/dhcp6c_{$interface}.pid"), " \n"); + } else { + return(false); + } + + return intval($pid); +} + +function interface_virtual_create($interface) { + global $config; + + if (strstr($interface, "_vlan")) { + interfaces_vlan_configure($vlan); + } else if (substr($interface, 0, 3) == "gre") { + interfaces_gre_configure(0, $interface); + } else if (substr($interface, 0, 3) == "gif") { + interfaces_gif_configure(0, $interface); + } else if (substr($interface, 0, 5) == "ovpns") { + if (is_array($config['openvpn']) && is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as $server) { + if ($interface == "ovpns{$server['vpnid']}") { + if (!function_exists('openvpn_resync')) { + require_once('openvpn.inc'); + } + log_error("OpenVPN: Resync server {$server['description']}"); + openvpn_resync('server', $server); + } + } + unset($server); + } + } else if (substr($interface, 0, 5) == "ovpnc") { + if (is_array($config['openvpn']) && is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as $client) { + if ($interface == "ovpnc{$client['vpnid']}") { + if (!function_exists('openvpn_resync')) { + require_once('openvpn.inc'); + } + log_error("OpenVPN: Resync server {$client['description']}"); + openvpn_resync('client', $client); + } + } + unset($client); + } + } else if (substr($interface, 0, 4) == "lagg") { + interfaces_lagg_configure($interface); + } else if (substr($interface, 0, 6) == "bridge") { + interfaces_bridge_configure(0, $interface); + } +} + +function interface_vlan_mtu_configured($realhwif, $mtu) { + global $config; + + if (is_array($config['vlans']) && is_array($config['vlans']['vlan'])) { + foreach ($config['vlans']['vlan'] as $vlan) { + if ($vlan['if'] != $realhwif) { + continue; + } + $assignedport = convert_real_interface_to_friendly_interface_name($vlan['vlanif']); + if (!empty($assignedport) && !empty($config['interfaces'][$assignedport]['mtu'])) { + if (intval($config['interfaces'][$assignedport]['mtu']) > $mtu) { + $mtu = $config['interfaces'][$assignedport]['mtu']; + } + } + } + } + + return $mtu; +} + +function interface_vlan_adapt_mtu($vlanifs, $mtu) { + global $config; + + if (!is_array($vlanifs)) { + return; + } + + /* All vlans need to use the same mtu value as their parent. */ + foreach ($vlanifs as $vlan) { + $assignedport = convert_real_interface_to_friendly_interface_name($vlan['vlanif']); + if (!empty($assignedport)) { + if (!empty($config['interfaces'][$assignedport]['mtu'])) { + pfSense_interface_mtu($vlan['vlanif'], $config['interfaces'][$assignedport]['mtu']); + } else { + if (get_interface_mtu($vlan['vlanif']) != $mtu) { + pfSense_interface_mtu($vlan['vlanif'], $mtu); + } + } + } else if (get_interface_mtu($vlan['vlanif']) != $mtu) { + pfSense_interface_mtu($vlan['vlanif'], $mtu); + } + } +} + +function interface_configure($interface = "wan", $reloadall = false, $linkupevent = false) { + global $config, $g; + global $interface_sn_arr_cache, $interface_ip_arr_cache; + global $interface_snv6_arr_cache, $interface_ipv6_arr_cache; + + $wancfg = $config['interfaces'][$interface]; + + if (!isset($wancfg['enable'])) { + return; + } + + $realif = get_real_interface($interface); + $realhwif_array = get_parent_interface($interface); + // Need code to handle MLPPP if we ever use $realhwif for MLPPP handling + $realhwif = $realhwif_array[0]; + + if (!platform_booting() && !(substr($realif, 0, 4) == "ovpn")) { + /* remove all IPv4 and IPv6 addresses */ + $tmpifaces = pfSense_getall_interface_addresses($realif); + if (is_array($tmpifaces)) { + foreach ($tmpifaces as $tmpiface) { + if (is_ipaddrv6($tmpiface) || is_subnetv6($tmpiface)) { + if (!is_linklocal($tmpiface)) { + mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 {$tmpiface} delete"); + } + } else { + if (is_subnetv4($tmpiface)) { + $tmpip = explode('/', $tmpiface); + $tmpip = $tmpip[0]; + } else { + $tmpip = $tmpiface; + } + pfSense_interface_deladdress($realif, $tmpip); + } + } + } + + /* only bring down the interface when both v4 and v6 are set to NONE */ + if (empty($wancfg['ipaddr']) && empty($wancfg['ipaddrv6'])) { + interface_bring_down($interface); + } + } + + $interface_to_check = $realif; + if (interface_isppp_type($interface)) { + $interface_to_check = $realhwif; + } + + /* Need to check that the interface exists or not in the case where its coming back from disabled state see #3270 */ + if (!platform_booting() && (in_array(substr($realif, 0, 3), array("gre", "gif")) || !does_interface_exist($interface_to_check))) { + interface_virtual_create($interface_to_check); + } + + /* Disable Accepting router advertisements unless specifically requested */ + if ($g['debug']) { + log_error("Deny router advertisements for interface {$interface}"); + } + mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 -accept_rtadv", true); + + /* wireless configuration? */ + if (is_array($wancfg['wireless'])) { + interface_wireless_configure($realif, $wancfg, $wancfg['wireless']); + } + + $mac = get_interface_mac($realhwif); + /* + * Don't try to reapply the spoofed MAC if it's already applied. + * When ifconfig link is used, it cycles the interface down/up, which triggers + * the interface config again, which attempts to spoof the MAC again, + * which cycles the link again... + */ + if ($wancfg['spoofmac'] && ($wancfg['spoofmac'] != $mac)) { + mwexec("/sbin/ifconfig " . escapeshellarg($realhwif) . + " link " . escapeshellarg($wancfg['spoofmac'])); + } else { + + if ($mac == "ff:ff:ff:ff:ff:ff") { + /* this is not a valid mac address. generate a + * temporary mac address so the machine can get online. + */ + echo gettext("Generating new MAC address."); + $random_mac = generate_random_mac_address(); + mwexec("/sbin/ifconfig " . escapeshellarg($realhwif) . + " link " . escapeshellarg($random_mac)); + $wancfg['spoofmac'] = $random_mac; + write_config(); + file_notice("MAC Address altered", sprintf(gettext('The INVALID MAC address (ff:ff:ff:ff:ff:ff) on interface %1$s has been automatically replaced with %2$s'), $realif, $random_mac), "Interfaces"); + } + } + + /* media */ + if ($wancfg['media'] || $wancfg['mediaopt']) { + $cmd = "/sbin/ifconfig " . escapeshellarg($realhwif); + if ($wancfg['media']) { + $cmd .= " media " . escapeshellarg($wancfg['media']); + } + if ($wancfg['mediaopt']) { + $cmd .= " mediaopt " . escapeshellarg($wancfg['mediaopt']); + } + mwexec($cmd); + } + + /* Apply hw offloading policies as configured */ + enable_hardware_offloading($interface); + + /* invalidate interface/ip/sn cache */ + get_interface_arr(true); + unset($interface_ip_arr_cache[$realif]); + unset($interface_sn_arr_cache[$realif]); + unset($interface_ipv6_arr_cache[$realif]); + unset($interface_snv6_arr_cache[$realif]); + + $tunnelif = substr($realif, 0, 3); + + if (does_interface_exist($wancfg['if'])) { + interfaces_bring_up($wancfg['if']); + } + + if (!empty($wancfg['mtu'])) { + if (stristr($realif, "_vlan")) { + $assignedparent = convert_real_interface_to_friendly_interface_name($realhwif); + if (!empty($assignedparent) && !empty($config['interfaces'][$assignedparent]['mtu'])) { + $parentmtu = $config['interfaces'][$assignedparent]['mtu']; + if ($wancfg['mtu'] > $parentmtu) { + log_error("There is a conflict on MTU between parent {$realhwif} and VLAN({$realif})"); + } + } else { + $parentmtu = 0; + } + + $parentmtu = interface_vlan_mtu_configured($realhwif, $parentmtu); + + if (get_interface_mtu($realhwif) != $parentmtu) { + pfSense_interface_mtu($realhwif, $parentmtu); + } + + /* All vlans need to use the same mtu value as their parent. */ + interface_vlan_adapt_mtu(link_interface_to_vlans($realhwif), $parentmtu); + } else if (substr($realif, 0, 4) == 'lagg') { + /* LAGG interface must be destroyed and re-created to change MTU */ + if ($wancfg['mtu'] != get_interface_mtu($realif)) { + if (isset($config['laggs']['lagg']) && is_array($config['laggs']['lagg'])) { + foreach ($config['laggs']['lagg'] as $lagg) { + if ($lagg['laggif'] == $realif) { + interface_lagg_configure($lagg); + break; + } + } + } + } + } else { + if ($wancfg['mtu'] != get_interface_mtu($realif)) { + pfSense_interface_mtu($realif, $wancfg['mtu']); + } + + /* This case is needed when the parent of vlans is being configured */ + $vlans = link_interface_to_vlans($realif); + if (is_array($vlans)) { + interface_vlan_adapt_mtu($vlans, $wancfg['mtu']); + } + unset($vlans); + } + /* XXX: What about gre/gif/.. ? */ + } + + switch ($wancfg['ipaddr']) { + case 'dhcp': + interface_dhcp_configure($interface); + break; + case 'pppoe': + case 'l2tp': + case 'pptp': + case 'ppp': + interface_ppps_configure($interface); + break; + default: + /* XXX: Kludge for now related to #3280 */ + if (!in_array($tunnelif, array("gif", "gre", "ovp"))) { + if (is_ipaddrv4($wancfg['ipaddr']) && $wancfg['subnet'] <> "") { + pfSense_interface_setaddress($realif, "{$wancfg['ipaddr']}/{$wancfg['subnet']}"); + } + } + break; + } + + switch ($wancfg['ipaddrv6']) { + case 'slaac': + case 'dhcp6': + interface_dhcpv6_configure($interface, $wancfg); + break; + case '6rd': + interface_6rd_configure($interface, $wancfg); + break; + case '6to4': + interface_6to4_configure($interface, $wancfg); + break; + case 'track6': + interface_track6_configure($interface, $wancfg, $linkupevent); + break; + default: + /* XXX: Kludge for now related to #3280 */ + if (!in_array($tunnelif, array("gif", "gre", "ovp"))) { + if (is_ipaddrv6($wancfg['ipaddrv6']) && $wancfg['subnetv6'] <> "") { + //pfSense_interface_setaddress($realif, "{$wancfg['ipaddrv6']}/{$wancfg['subnetv6']}"); + // FIXME: Add IPv6 Support to the pfSense module + mwexec("/sbin/ifconfig " . escapeshellarg($realif) . " inet6 {$wancfg['ipaddrv6']} prefixlen " . escapeshellarg($wancfg['subnetv6'])); + } + } + break; + } + + interface_netgraph_needed($interface); + + if (!platform_booting()) { + link_interface_to_vips($interface, "update"); + + if ($tunnelif != 'gre') { + unset($gre); + $gre = link_interface_to_gre($interface); + if (!empty($gre)) { + array_walk($gre, 'interface_gre_configure'); + } + } + + if ($tunnelif != 'gif') { + unset($gif); + $gif = link_interface_to_gif ($interface); + if (!empty($gif)) { + array_walk($gif, 'interface_gif_configure'); + } + } + + if ($linkupevent == false || substr($realif, 0, 4) == "ovpn") { + unset($bridgetmp); + $bridgetmp = link_interface_to_bridge($interface); + if (!empty($bridgetmp)) { + interface_bridge_add_member($bridgetmp, $realif); + } + } + + $grouptmp = link_interface_to_group($interface); + if (!empty($grouptmp)) { + array_walk($grouptmp, 'interface_group_add_member'); + } + + if ($interface == "lan") { + /* make new hosts file */ + system_hosts_generate(); + } + + if ($reloadall == true) { + + /* reconfigure static routes (kernel may have deleted them) */ + system_routing_configure($interface); + + /* reload ipsec tunnels */ + send_event("service reload ipsecdns"); + + /* restart dnsmasq or unbound */ + if (isset($config['dnsmasq']['enable'])) { + services_dnsmasq_configure(); + } elseif (isset($config['unbound']['enable'])) { + services_unbound_configure(); + } + + /* update dyndns */ + send_event("service reload dyndns {$interface}"); + + /* reload captive portal */ + if (!function_exists('captiveportal_init_rules_byinterface')) { + require_once('captiveportal.inc'); + } + captiveportal_init_rules_byinterface($interface); + } + } + + interfaces_staticarp_configure($interface); + return 0; +} + +function interface_track6_configure($interface = "lan", $wancfg, $linkupevent = false) { + global $config, $g; + + if (!is_array($wancfg)) { + return; + } + + if (!isset($wancfg['enable'])) { + return; + } + + /* If the interface is not configured via another, exit */ + if (empty($wancfg['track6-interface'])) { + return; + } + + /* always configure a link-local of fe80::1:1 on the track6 interfaces */ + $realif = get_real_interface($interface); + $linklocal = find_interface_ipv6_ll($realif); + if (!empty($linklocal)) { + mwexec("/sbin/ifconfig {$realif} inet6 {$linklocal} delete"); + } + /* XXX: This might break for good on a carp installation using link-local as network ips */ + /* XXX: Probably should remove? */ + mwexec("/sbin/ifconfig {$realif} inet6 fe80::1:1%{$realif}"); + + $trackcfg = $config['interfaces'][$wancfg['track6-interface']]; + if (!isset($trackcfg['enable'])) { + log_error("Interface {$interface} tracking non-existant interface {$wancfg['track6-interface']}"); + return; + } + + switch ($trackcfg['ipaddrv6']) { + case "6to4": + if ($g['debug']) { + log_error("Interface {$interface} configured via {$wancfg['track6-interface']} type {$type}"); + } + interface_track6_6to4_configure($interface, $wancfg); + break; + case "6rd": + if ($g['debug']) { + log_error("Interface {$interface} configured via {$wancfg['track6-interface']} type {$type}"); + } + interface_track6_6rd_configure($interface, $wancfg); + break; + case "dhcp6": + if ($linkupevent == true) { + /* + * NOTE: Usually come here from rc.linkup calling so just call directly instead of generating event + * Instead of disrupting all other v4 configuration just restart DHCPv6 client for now + * + * XXX: Probably DHCPv6 client should handle this automagically itself? + */ + $parentrealif = get_real_interface($wancfg['track6-interface']); + $pidv6 = find_dhcp6c_process($parentrealif); + if ($pidv6) { + posix_kill($pidv6, SIGHUP); + } + } + break; + } + + if ($linkupevent == false) { + if (!function_exists('services_dhcpd_configure')) { + require_once("services.inc"); + } + + if (isset($config['unbound']['enable'])) { + services_unbound_configure(); + } + + services_dhcpd_configure("inet6"); + } + + return 0; +} + +function interface_track6_6rd_configure($interface = "lan", $lancfg) { + global $config, $g; + global $interface_ipv6_arr_cache; + global $interface_snv6_arr_cache; + + if (!is_array($lancfg)) { + return; + } + + /* If the interface is not configured via another, exit */ + if (empty($lancfg['track6-interface'])) { + return; + } + + $wancfg = $config['interfaces'][$lancfg['track6-interface']]; + if (empty($wancfg)) { + log_error("Interface {$interface} tracking non-existant interface {$lancfg['track6-interface']}"); + return; + } + + $ip4address = get_interface_ip($lancfg['track6-interface']); + if (!is_ipaddrv4($ip4address)) { /* XXX: This should not be needed by 6rd || (is_private_ip($ip4address))) { */ + log_error("The interface IPv4 '{$ip4address}' address on interface '{$lancfg['track6-interface']}' is not valid, not configuring 6RD tunnel"); + return; + } + $hexwanv4 = return_hex_ipv4($ip4address); + + /* create the long prefix notation for math, save the prefix length */ + $rd6prefix = explode("/", $wancfg['prefix-6rd']); + $rd6prefixlen = $rd6prefix[1]; + $rd6prefix = Net_IPv6::uncompress($rd6prefix[0]); + + /* binary presentation of the prefix for all 128 bits. */ + $rd6lanbin = convert_ipv6_to_128bit($rd6prefix); + + /* just save the left prefix length bits */ + $rd6lanbin = substr($rd6lanbin, 0, $rd6prefixlen); + /* add the v4 address, offset n bits from the left */ + $rd6lanbin .= substr(sprintf("%032b", hexdec($hexwanv4)), (0 + $wancfg['prefix-6rd-v4plen']), 32); + + /* add the custom prefix id, max 32bits long? (64 bits - (prefixlen + (32 - v4plen)) */ + /* 64 - (37 + (32 - 17)) = 8 == /52 */ + $restbits = 64 - ($rd6prefixlen + (32 - $wancfg['prefix-6rd-v4plen'])); + // echo "64 - (prefixlen {$rd6prefixlen} + v4len (32 - {$wancfg['prefix-6rd-v4plen']})) = {$restbits} \n"; + $rd6lanbin .= substr(sprintf("%032b", str_pad($lancfg['track6-prefix-id'], 32, "0", STR_PAD_LEFT)), (32 - $restbits), 32); + /* fill the rest out with zeros */ + $rd6lanbin = str_pad($rd6lanbin, 128, "0", STR_PAD_RIGHT); + + /* convert the 128 bits for the lan address back into a valid IPv6 address */ + $rd6lan = convert_128bit_to_ipv6($rd6lanbin) ."1"; + + $lanif = get_real_interface($interface); + $oip = find_interface_ipv6($lanif); + if (is_ipaddrv6($oip)) { + mwexec("/sbin/ifconfig {$lanif} inet6 {$oip} delete"); + } + unset($interface_ipv6_arr_cache[$lanif]); + unset($interface_snv6_arr_cache[$lanif]); + log_error("rd6 {$interface} with ipv6 address {$rd6lan} based on {$lancfg['track6-interface']} ipv4 {$ip4address}"); + mwexec("/sbin/ifconfig {$lanif} inet6 {$rd6lan} prefixlen 64"); + + return 0; +} + +function interface_track6_6to4_configure($interface = "lan", $lancfg) { + global $config, $g; + global $interface_ipv6_arr_cache; + global $interface_snv6_arr_cache; + + if (!is_array($lancfg)) { + return; + } + + /* If the interface is not configured via another, exit */ + if (empty($lancfg['track6-interface'])) { + return; + } + + $wancfg = $config['interfaces'][$lancfg['track6-interface']]; + if (empty($wancfg)) { + log_error("Interface {$interface} tracking non-existant interface {$lancfg['track6-interface']}"); + return; + } + + $ip4address = get_interface_ip($lancfg['track6-interface']); + if (!is_ipaddrv4($ip4address) || is_private_ip($ip4address)) { + log_error("The interface IPv4 '{$ip4address}' address on interface '{$lancfg['track6-interface']}' is not public, not configuring 6RD tunnel"); + return; + } + $hexwanv4 = return_hex_ipv4($ip4address); + + /* create the long prefix notation for math, save the prefix length */ + $sixto4prefix = "2002::"; + $sixto4prefixlen = 16; + $sixto4prefix = Net_IPv6::uncompress($sixto4prefix); + + /* binary presentation of the prefix for all 128 bits. */ + $sixto4lanbin = convert_ipv6_to_128bit($sixto4prefix); + + /* just save the left prefix length bits */ + $sixto4lanbin = substr($sixto4lanbin, 0, $sixto4prefixlen); + /* add the v4 address */ + $sixto4lanbin .= sprintf("%032b", hexdec($hexwanv4)); + /* add the custom prefix id */ + $sixto4lanbin .= sprintf("%016b", $lancfg['track6-prefix-id']); + /* fill the rest out with zeros */ + $sixto4lanbin = str_pad($sixto4lanbin, 128, "0", STR_PAD_RIGHT); + + /* convert the 128 bits for the lan address back into a valid IPv6 address */ + $sixto4lan = convert_128bit_to_ipv6($sixto4lanbin) ."1"; + + $lanif = get_real_interface($interface); + $oip = find_interface_ipv6($lanif); + if (is_ipaddrv6($oip)) { + mwexec("/sbin/ifconfig {$lanif} inet6 {$oip} delete"); + } + unset($interface_ipv6_arr_cache[$lanif]); + unset($interface_snv6_arr_cache[$lanif]); + log_error("sixto4 {$interface} with ipv6 address {$sixto4lan} based on {$lancfg['track6-interface']} ipv4 {$ip4address}"); + mwexec("/sbin/ifconfig {$lanif} inet6 {$sixto4lan} prefixlen 64"); + + return 0; +} + +function interface_6rd_configure($interface = "wan", $wancfg) { + global $config, $g; + + /* because this is a tunnel interface we can only function + * with a public IPv4 address on the interface */ + + if (!is_array($wancfg)) { + return; + } + + if (!is_module_loaded('if_stf.ko')) { + mwexec('/sbin/kldload if_stf.ko'); + } + + $wanif = get_real_interface($interface); + $ip4address = find_interface_ip($wanif); + if (!is_ipaddrv4($ip4address)) { + log_error("The interface IPv4 '{$ip4address}' address on interface '{$wanif}' is not public, not configuring 6RD tunnel"); + return false; + } + $hexwanv4 = return_hex_ipv4($ip4address); + + if (!is_numeric($wancfg['prefix-6rd-v4plen'])) { + $wancfg['prefix-6rd-v4plen'] = 0; + } + + /* create the long prefix notation for math, save the prefix length */ + $rd6prefix = explode("/", $wancfg['prefix-6rd']); + $rd6prefixlen = $rd6prefix[1]; + $brgw = explode('.', $wancfg['gateway-6rd']); + $rd6brgw = substr(Net_IPv6::_ip2Bin($rd6prefix[0]), 0, $rd6prefixlen); + $rd6brgw .= str_pad(decbin($brgw[0]), 8, '0', STR_PAD_LEFT) . str_pad(decbin($brgw[1]), 8, '0', STR_PAD_LEFT) . str_pad(decbin($brgw[2]), 8, '0', STR_PAD_LEFT) . str_pad(decbin($brgw[3]), 8, '0', STR_PAD_LEFT); + if (strlen($rd6brgw) < 128) { + $rd6brgw = str_pad($rd6brgw, 128, '0', STR_PAD_RIGHT); + } + $rd6brgw = Net_IPv6::compress(Net_IPv6::_bin2Ip($rd6brgw)); + unset($brgw); + $rd6prefix = Net_IPv6::uncompress($rd6prefix[0]); + + /* binary presentation of the prefix for all 128 bits. */ + $rd6prefixbin = convert_ipv6_to_128bit($rd6prefix); + + /* just save the left prefix length bits */ + $rd6prefixbin = substr($rd6prefixbin, 0, $rd6prefixlen); + /* if the prefix length is not 32 bits we need to shave bits off from the left of the v4 address. */ + $rd6prefixbin .= substr(sprintf("%032b", hexdec($hexwanv4)), $wancfg['prefix-6rd-v4plen'], 32); + /* fill out the rest with 0's */ + $rd6prefixbin = str_pad($rd6prefixbin, 128, "0", STR_PAD_RIGHT); + + /* convert the 128 bits for the broker address back into a valid IPv6 address */ + $rd6prefix = convert_128bit_to_ipv6($rd6prefixbin); + + + /* XXX: need to extend to support variable prefix size for v4 */ + if (!is_module_loaded("if_stf")) { + mwexec("/sbin/kldload if_stf.ko"); + } + $stfiface = "{$interface}_stf"; + if (does_interface_exist($stfiface)) { + pfSense_interface_destroy($stfiface); + } + $tmpstfiface = pfSense_interface_create("stf"); + pfSense_interface_rename($tmpstfiface, $stfiface); + pfSense_interface_flags($stfiface, IFF_LINK2); + mwexec("/sbin/ifconfig {$stfiface} inet6 {$rd6prefix}/{$rd6prefixlen}"); + mwexec("/sbin/ifconfig {$stfiface} stfv4br " . escapeshellarg($wancfg['gateway-6rd'])); + if ($wancfg['prefix-6rd-v4plen'] >= 0 && $wancfg['prefix-6rd-v4plen'] <= 32) { + mwexec("/sbin/ifconfig {$stfiface} stfv4net {$ip4address}/" . escapeshellarg($wancfg['prefix-6rd-v4plen'])); + } + if ($g['debug']) { + log_error("Created 6rd interface {$stfiface} {$rd6prefix}/{$rd6prefixlen}"); + } + + /* write out a default router file */ + file_put_contents("{$g['tmp_path']}/{$wanif}_routerv6", "{$rd6brgw}\n"); + file_put_contents("{$g['tmp_path']}/{$wanif}_defaultgwv6", "{$rd6brgw}\n"); + + $ip4gateway = get_interface_gateway($interface); + if (is_ipaddrv4($ip4gateway)) { + mwexec("/sbin/route change -host " . escapeshellarg($wancfg['gateway-6rd']) . " {$ip4gateway}"); + } + + /* configure dependent interfaces */ + if (!platform_booting()) { + link_interface_to_track6($interface, "update"); + } + + return 0; +} + +function interface_6to4_configure($interface = "wan", $wancfg) { + global $config, $g; + + /* because this is a tunnel interface we can only function + * with a public IPv4 address on the interface */ + + if (!is_array($wancfg)) { + return; + } + + $wanif = get_real_interface($interface); + $ip4address = find_interface_ip($wanif); + if ((!is_ipaddrv4($ip4address)) || (is_private_ip($ip4address))) { + log_error("The interface IPv4 '{$ip4address}' address on interface '{$wanif}' is not public, not configuring 6RD tunnel"); + return false; + } + + /* create the long prefix notation for math, save the prefix length */ + $stfprefixlen = 16; + $stfprefix = Net_IPv6::uncompress("2002::"); + $stfarr = explode(":", $stfprefix); + $v4prefixlen = "0"; + + /* we need the hex form of the interface IPv4 address */ + $ip4arr = explode(".", $ip4address); + $hexwanv4 = ""; + foreach ($ip4arr as $octet) { + $hexwanv4 .= sprintf("%02x", $octet); + } + + /* we need the hex form of the broker IPv4 address */ + $ip4arr = explode(".", "192.88.99.1"); + $hexbrv4 = ""; + foreach ($ip4arr as $octet) { + $hexbrv4 .= sprintf("%02x", $octet); + } + + /* binary presentation of the prefix for all 128 bits. */ + $stfprefixbin = ""; + foreach ($stfarr as $element) { + $stfprefixbin .= sprintf("%016b", hexdec($element)); + } + /* just save the left prefix length bits */ + $stfprefixstartbin = substr($stfprefixbin, 0, $stfprefixlen); + + /* if the prefix length is not 32 bits we need to shave bits off from the left of the v4 address. */ + $stfbrokerbin = substr(sprintf("%032b", hexdec($hexbrv4)), $v4prefixlen, 32); + $stfbrokerbin = str_pad($stfprefixstartbin . $stfbrokerbin, 128, "0", STR_PAD_RIGHT); + + /* for the local subnet too. */ + $stflanbin = substr(sprintf("%032b", hexdec($hexwanv4)), $v4prefixlen, 32); + $stflanbin = str_pad($stfprefixstartbin . $stflanbin, 128, "0", STR_PAD_RIGHT); + + /* convert the 128 bits for the broker address back into a valid IPv6 address */ + $stfbrarr = array(); + $stfbrbinarr = array(); + $stfbrbinarr = str_split($stfbrokerbin, 16); + foreach ($stfbrbinarr as $bin) { + $stfbrarr[] = dechex(bindec($bin)); + } + $stfbrgw = Net_IPv6::compress(implode(":", $stfbrarr)); + + /* convert the 128 bits for the broker address back into a valid IPv6 address */ + $stflanarr = array(); + $stflanbinarr = array(); + $stflanbinarr = str_split($stflanbin, 16); + foreach ($stflanbinarr as $bin) { + $stflanarr[] = dechex(bindec($bin)); + } + $stflanpr = Net_IPv6::compress(implode(":", $stflanarr)); + $stflanarr[7] = 1; + $stflan = Net_IPv6::compress(implode(":", $stflanarr)); + + /* setup the stf interface */ + if (!is_module_loaded("if_stf")) { + mwexec("/sbin/kldload if_stf.ko"); + } + $stfiface = "{$interface}_stf"; + if (does_interface_exist($stfiface)) { + pfSense_interface_destroy($stfiface); + } + $tmpstfiface = pfSense_interface_create("stf"); + pfSense_interface_rename($tmpstfiface, $stfiface); + pfSense_interface_flags($stfiface, IFF_LINK2); + mwexec("/sbin/ifconfig {$stfiface} inet6 {$stflanpr} prefixlen 16"); + + if ($g['debug']) { + log_error("Set IPv6 address inet6 {$stflanpr} prefixlen 16 for {$stfiface}, route {$stfbrgw}"); + } + + /* write out a default router file */ + file_put_contents("{$g['tmp_path']}/{$wanif}_routerv6", "{$stfbrgw}"); + file_put_contents("{$g['tmp_path']}/{$wanif}_defaultgwv6", "{$stfbrgw}"); + + $ip4gateway = get_interface_gateway($interface); + if (is_ipaddrv4($ip4gateway)) { + mwexec("/sbin/route change -host 192.88.99.1 {$ip4gateway}"); + } + + if (!platform_booting()) { + link_interface_to_track6($interface, "update"); + } + + return 0; +} + +function interface_dhcpv6_configure($interface = "wan", $wancfg) { + global $config, $g; + + if (!is_array($wancfg)) { + return; + } + + $wanif = get_real_interface($interface, "inet6"); + $dhcp6cconf = ""; + + if ($wancfg['adv_dhcp6_config_file_override']) { + // DHCP6 Config File Override + $dhcp6cconf = DHCP6_Config_File_Override($wancfg, $wanif); + } elseif ($wancfg['adv_dhcp6_config_advanced']) { + // DHCP6 Config File Advanced + $dhcp6cconf = DHCP6_Config_File_Advanced($interface, $wancfg, $wanif); + } else { + // DHCP6 Config File Basic + $dhcp6cconf .= "interface {$wanif} {\n"; + + /* for SLAAC interfaces we do fire off a dhcp6 client for just our name servers */ + if ($wancfg['ipaddrv6'] == "slaac") { + $dhcp6cconf .= "\tinformation-only;\n"; + $dhcp6cconf .= "\trequest domain-name-servers;\n"; + $dhcp6cconf .= "\trequest domain-name;\n"; + $dhcp6cconf .= "\tscript \"{$g['varetc_path']}/dhcp6c_{$interface}_script.sh\"; # we'd like some nameservers please\n"; + $dhcp6cconf .= "};\n"; + } else { + $trackiflist = array(); + $iflist = link_interface_to_track6($interface); + foreach ($iflist as $ifname => $ifcfg) { + if (is_numeric($ifcfg['track6-prefix-id'])) { + $trackiflist[$ifname] = $ifcfg; + } + } + + /* skip address request if this is set */ + if (!isset($wancfg['dhcp6prefixonly'])) { + $dhcp6cconf .= "\tsend ia-na 0;\t# request stateful address\n"; + } + if (is_numeric($wancfg['dhcp6-ia-pd-len']) && !empty($trackiflist)) { + $dhcp6cconf .= "\tsend ia-pd 0;\t# request prefix delegation\n"; + } + + $dhcp6cconf .= "\trequest domain-name-servers;\n"; + $dhcp6cconf .= "\trequest domain-name;\n"; + $dhcp6cconf .= "\tscript \"{$g['varetc_path']}/dhcp6c_{$interface}_script.sh\"; # we'd like some nameservers please\n"; + $dhcp6cconf .= "};\n"; + + if (!isset($wancfg['dhcp6prefixonly'])) { + $dhcp6cconf .= "id-assoc na 0 { };\n"; + } + + if (is_numeric($wancfg['dhcp6-ia-pd-len']) && !empty($trackiflist)) { + /* Setup the prefix delegation */ + $dhcp6cconf .= "id-assoc pd 0 {\n"; + $preflen = 64 - $wancfg['dhcp6-ia-pd-len']; + if (isset($wancfg['dhcp6-ia-pd-send-hint'])) { + $dhcp6cconf .= "\tprefix ::/{$preflen} infinity;\n"; + } + foreach ($trackiflist as $friendly => $ifcfg) { + if ($g['debug']) { + log_error("setting up $ifdescr - {$ifcfg['track6-prefix-id']}"); + } + $realif = get_real_interface($friendly); + $dhcp6cconf .= "\tprefix-interface {$realif} {\n"; + $dhcp6cconf .= "\t\tsla-id {$ifcfg['track6-prefix-id']};\n"; + $dhcp6cconf .= "\t\tsla-len {$wancfg['dhcp6-ia-pd-len']};\n"; + $dhcp6cconf .= "\t};\n"; + } + unset($preflen, $iflist, $ifcfg, $ifname); + $dhcp6cconf .= "};\n"; + } + unset($trackiflist); + } + } + + /* wide-dhcp6c works for now. */ + if (!@file_put_contents("{$g['varetc_path']}/dhcp6c_{$interface}.conf", $dhcp6cconf)) { + printf("Error: cannot open dhcp6c_{$interface}.conf in interface_dhcpv6_configure() for writing.\n"); + unset($dhcp6cconf); + return 1; + } + unset($dhcp6cconf); + + $dhcp6cscript = "#!/bin/sh\n"; + $dhcp6cscript .= "# This shell script launches /etc/rc.newwanipv6 with a interface argument.\n"; + $dhcp6cscript .= "dmips=\${new_domain_name_servers}\n"; + $dhcp6cscript .= "dmnames=\${new_domain_name}\n"; + $dhcp6cscript .= "/usr/local/sbin/fcgicli -f /etc/rc.newwanipv6 -d \"interface={$wanif}&dmnames=\${dmnames}&dmips=\${dmips}\"\n"; + /* Add wide-dhcp6c shell script here. Because we can not pass a argument to it. */ + if (!@file_put_contents("{$g['varetc_path']}/dhcp6c_{$interface}_script.sh", $dhcp6cscript)) { + printf("Error: cannot open dhcp6c_{$interface}_script.sh in interface_dhcpv6_configure() for writing.\n"); + unset($dhcp6cscript); + return 1; + } + unset($dhcp6cscript); + @chmod("{$g['varetc_path']}/dhcp6c_{$interface}_script.sh", 0755); + + $rtsoldscript = "#!/bin/sh\n"; + $rtsoldscript .= "# This shell script launches dhcp6c and configured gateways for this interface.\n"; + $rtsoldscript .= "echo $2 > {$g['tmp_path']}/{$wanif}_routerv6\n"; + $rtsoldscript .= "echo $2 > {$g['tmp_path']}/{$wanif}_defaultgwv6\n"; + $rtsoldscript .= "/usr/bin/logger -t rtsold \"Recieved RA specifying route \$2 for interface {$interface}({$wanif})\"\n"; + $rtsoldscript .= "if [ -f {$g['varrun_path']}/dhcp6c_{$wanif}.pid ]; then\n"; + $rtsoldscript .= "\t/bin/pkill -F {$g['varrun_path']}/dhcp6c_{$wanif}.pid\n"; + $rtsoldscript .= "\t/bin/sleep 1\n"; + $rtsoldscript .= "fi\n"; + $rtsoldscript .= "/usr/local/sbin/dhcp6c -d -c {$g['varetc_path']}/dhcp6c_{$interface}.conf -p {$g['varrun_path']}/dhcp6c_{$wanif}.pid {$wanif}\n"; + $rtsoldscript .= "/usr/bin/logger -t rtsold \"Starting dhcp6 client for interface {$interface}({$wanif})\"\n"; + /* Add wide-dhcp6c shell script here. Because we can not pass a argument to it. */ + if (!@file_put_contents("{$g['varetc_path']}/rtsold_{$wanif}_script.sh", $rtsoldscript)) { + printf("Error: cannot open rtsold_{$interface}_script.sh in interface_dhcpv6_configure() for writing.\n"); + unset($rtsoldscript); + return 1; + } + unset($rtsoldscript); + @chmod("{$g['varetc_path']}/rtsold_{$wanif}_script.sh", 0755); + + /* accept router advertisements for this interface */ + set_single_sysctl("net.inet6.ip6.accept_rtadv", "1"); + log_error("Accept router advertisements on interface {$wanif} "); + mwexec("/sbin/ifconfig {$wanif} inet6 accept_rtadv"); + + /* fire up rtsold for IPv6 RAs first, this backgrounds immediately. It will call dhcp6c */ + if (isvalidpid("{$g['varrun_path']}/rtsold_{$wanif}.pid")) { + killbypid("{$g['varrun_path']}/rtsold_{$wanif}.pid"); + sleep(2); + } + mwexec("/usr/sbin/rtsold -1 -p {$g['varrun_path']}/rtsold_{$wanif}.pid -O {$g['varetc_path']}/rtsold_{$wanif}_script.sh {$wanif}"); + + /* NOTE: will be called from rtsold invoked script + * link_interface_to_track6($interface, "update"); + */ + + return 0; +} + +function DHCP6_Config_File_Advanced($interface, $wancfg, $wanif) { + global $g; + + $send_options = ""; + if ($wancfg['adv_dhcp6_interface_statement_send_options'] != '') { + $options = explode(',', $wancfg['adv_dhcp6_interface_statement_send_options']); + foreach ($options as $option) { + $send_options .= "\tsend " . trim($option) . ";\n"; + } + } + + $request_options = ""; + if ($wancfg['adv_dhcp6_interface_statement_request_options'] != '') { + $options = explode(',', $wancfg['adv_dhcp6_interface_statement_request_options']); + foreach ($options as $option) { + $request_options .= "\trequest " . trim($option) . ";\n"; + } + } + + $information_only = ""; + if ($wancfg['adv_dhcp6_interface_statement_information_only_enable'] != '') { + $information_only = "\tinformation-only;\n"; + } + + $script = "\tscript \"{$g['varetc_path']}/dhcp6c_{$interface}_script.sh\";\n"; + if ($wancfg['adv_dhcp6_interface_statement_script'] != '') { + $script = "\tscript \"{$wancfg['adv_dhcp6_interface_statement_script']}\";\n"; + } + + $interface_statement = "interface"; + $interface_statement .= " {$wanif}"; + $interface_statement .= " {\n"; + $interface_statement .= "$send_options"; + $interface_statement .= "$request_options"; + $interface_statement .= "$information_only"; + $interface_statement .= "$script"; + $interface_statement .= "};\n"; + + $id_assoc_statement_address = ""; + if ($wancfg['adv_dhcp6_id_assoc_statement_address_enable'] != '') { + $id_assoc_statement_address .= "id-assoc"; + $id_assoc_statement_address .= " na"; + if (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_address_id'])) { + $id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address_id']}"; + } + $id_assoc_statement_address .= " { "; + + if (($wancfg['adv_dhcp6_id_assoc_statement_address'] != '') && + (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_address_pltime']) || + ($wancfg['adv_dhcp6_id_assoc_statement_address_pltime'] == 'infinity'))) { + $id_assoc_statement_address .= "\n\taddress"; + $id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address']}"; + $id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address_pltime']}"; + if ((is_numeric($wancfg['adv_dhcp6_id_assoc_statement_address_vltime'])) || + ($wancfg['adv_dhcp6_id_assoc_statement_address_vltime'] == 'infinity')) { + $id_assoc_statement_address .= " {$wancfg['adv_dhcp6_id_assoc_statement_address_vltime']}"; + } + $id_assoc_statement_address .= ";\n"; + } + + $id_assoc_statement_address .= "};\n"; + } + + $id_assoc_statement_prefix = ""; + if ($wancfg['adv_dhcp6_id_assoc_statement_prefix_enable'] != '') { + $id_assoc_statement_prefix .= "id-assoc"; + $id_assoc_statement_prefix .= " pd"; + if (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_prefix_id'])) { + $id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix_id']}"; + } + $id_assoc_statement_prefix .= " { "; + + if (($wancfg['adv_dhcp6_id_assoc_statement_prefix'] != '') && + (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime']) || + ($wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime'] == 'infinity'))) { + $id_assoc_statement_prefix .= "\n\tprefix"; + $id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix']}"; + $id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix_pltime']}"; + if ((is_numeric($wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime'])) || + ($wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime'] == 'infinity')) { + $id_assoc_statement_prefix .= " {$wancfg['adv_dhcp6_id_assoc_statement_prefix_vltime']}"; + } + $id_assoc_statement_prefix .= ";"; + } + + if (is_numeric($wancfg['adv_dhcp6_prefix_interface_statement_sla_id'])) { + $id_assoc_statement_prefix .= "\n\tprefix-interface"; + $id_assoc_statement_prefix .= " {$wanif}"; + $id_assoc_statement_prefix .= " {\n"; + $id_assoc_statement_prefix .= "\t\tsla-id {$wancfg['adv_dhcp6_prefix_interface_statement_sla_id']};\n"; + if (($wancfg['adv_dhcp6_prefix_interface_statement_sla_len'] >= 0) && + ($wancfg['adv_dhcp6_prefix_interface_statement_sla_len'] <= 128)) { + $id_assoc_statement_prefix .= "\t\tsla-len {$wancfg['adv_dhcp6_prefix_interface_statement_sla_len']};\n"; + } + $id_assoc_statement_prefix .= "\t};"; + } + + if (($wancfg['adv_dhcp6_id_assoc_statement_prefix'] != '') || + (is_numeric($wancfg['adv_dhcp6_prefix_interface_statement_sla_id']))) { + $id_assoc_statement_prefix .= "\n"; + } + + $id_assoc_statement_prefix .= "};\n"; + } + + $authentication_statement = ""; + if (($wancfg['adv_dhcp6_authentication_statement_authname'] != '') && + ($wancfg['adv_dhcp6_authentication_statement_protocol'] == 'delayed')) { + $authentication_statement .= "authentication"; + $authentication_statement .= " {$wancfg['adv_dhcp6_authentication_statement_authname']}"; + $authentication_statement .= " {\n"; + $authentication_statement .= "\tprotocol {$wancfg['adv_dhcp6_authentication_statement_protocol']};\n"; + if (preg_match("/(hmac(-)?md5)||(HMAC(-)?MD5)/", $wancfg['adv_dhcp6_authentication_statement_algorithm'])) { + $authentication_statement .= "\talgorithm {$wancfg['adv_dhcp6_authentication_statement_algorithm']};\n"; + } + if ($wancfg['adv_dhcp6_authentication_statement_rdm'] == 'monocounter') { + $authentication_statement .= "\trdm {$wancfg['adv_dhcp6_authentication_statement_rdm']};\n"; + } + $authentication_statement .= "};\n"; + } + + $key_info_statement = ""; + if (($wancfg['adv_dhcp6_key_info_statement_keyname'] != '') && + ($wancfg['adv_dhcp6_key_info_statement_realm'] != '') && + (is_numeric($wancfg['adv_dhcp6_key_info_statement_keyid'])) && + ($wancfg['adv_dhcp6_key_info_statement_secret'] != '')) { + $key_info_statement .= "keyinfo"; + $key_info_statement .= " {$wancfg['adv_dhcp6_key_info_statement_keyname']}"; + $key_info_statement .= " {\n"; + $key_info_statement .= "\trealm \"{$wancfg['adv_dhcp6_key_info_statement_realm']}\";\n"; + $key_info_statement .= "\tkeyid {$wancfg['adv_dhcp6_key_info_statement_keyid']};\n"; + $key_info_statement .= "\tsecret \"{$wancfg['adv_dhcp6_key_info_statement_secret']}\";\n"; + if (preg_match("/((([0-9]{4}-)?[0-9]{2}[0-9]{2} )?[0-9]{2}:[0-9]{2})||(foreever)/", $wancfg['adv_dhcp6_key_info_statement_expire'])) { + $key_info_statement .= "\texpire \"{$wancfg['adv_dhcp6_key_info_statement_expire']}\";\n"; + } + $key_info_statement .= "};\n"; + } + + $dhcp6cconf = $interface_statement; + $dhcp6cconf .= $id_assoc_statement_address; + $dhcp6cconf .= $id_assoc_statement_prefix; + $dhcp6cconf .= $authentication_statement; + $dhcp6cconf .= $key_info_statement; + + $dhcp6cconf = DHCP6_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf); + + return $dhcp6cconf; +} + + +function DHCP6_Config_File_Override($wancfg, $wanif) { + + $dhcp6cconf = @file_get_contents($wancfg['adv_dhcp6_config_file_override_path']); + + if ($dhcp6cconf === false) { + log_error("Error: cannot open {$wancfg['adv_dhcp6_config_file_override_path']} in DHCP6_Config_File_Override() for reading.\n"); + return ''; + } else { + return DHCP6_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf);; + } +} + + +function DHCP6_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf) { + + $dhcp6cconf = DHCP_Config_File_Substitutions($wancfg, $wanif, $dhcp6cconf); + + return $dhcp6cconf; +} + + +function interface_dhcp_configure($interface = "wan") { + global $config, $g; + + $wancfg = $config['interfaces'][$interface]; + $wanif = $wancfg['if']; + if (empty($wancfg)) { + $wancfg = array(); + } + + /* generate dhclient_wan.conf */ + $fd = fopen("{$g['varetc_path']}/dhclient_{$interface}.conf", "w"); + if (!$fd) { + printf(printf(gettext("Error: cannot open dhclient_%s.conf in interface_dhcp_configure() for writing.%s"), $interface, "\n")); + return 1; + } + + if ($wancfg['dhcphostname']) { + $dhclientconf_hostname = "send dhcp-client-identifier \"{$wancfg['dhcphostname']}\";\n"; + $dhclientconf_hostname .= "\tsend host-name \"{$wancfg['dhcphostname']}\";\n"; + } else { + $dhclientconf_hostname = ""; + } + + $wanif = get_real_interface($interface); + if (empty($wanif)) { + log_error(sprintf(gettext("Invalid interface \"%s\" in interface_dhcp_configure()"), $interface)); + return 0; + } + $dhclientconf = ""; + + $dhclientconf .= <<<EOD +interface "{$wanif}" { +timeout 60; +retry 15; +select-timeout 0; +initial-interval 1; + {$dhclientconf_hostname} + script "/sbin/dhclient-script"; +EOD; + + if (is_ipaddrv4($wancfg['dhcprejectfrom'])) { + $dhclientconf .= <<<EOD + + reject {$wancfg['dhcprejectfrom']}; +EOD; + } + $dhclientconf .= <<<EOD + +} + +EOD; + + // DHCP Config File Advanced + if ($wancfg['adv_dhcp_config_advanced']) { + $dhclientconf = DHCP_Config_File_Advanced($interface, $wancfg, $wanif); + } + + if (is_ipaddr($wancfg['alias-address'])) { + $subnetmask = gen_subnet_mask($wancfg['alias-subnet']); + $dhclientconf .= <<<EOD +alias { + interface "{$wanif}"; + fixed-address {$wancfg['alias-address']}; + option subnet-mask {$subnetmask}; +} + +EOD; + } + + // DHCP Config File Override + if ($wancfg['adv_dhcp_config_file_override']) { + $dhclientconf = DHCP_Config_File_Override($wancfg, $wanif); + } + + fwrite($fd, $dhclientconf); + fclose($fd); + + /* bring wan interface up before starting dhclient */ + if ($wanif) { + interfaces_bring_up($wanif); + } else { + log_error(printf(gettext("Could not bring up %s interface in interface_dhcp_configure()"), $wanif)); + } + + /* Make sure dhclient is not running */ + kill_dhclient_process($wanif); + + /* fire up dhclient */ + mwexec("/sbin/dhclient -c {$g['varetc_path']}/dhclient_{$interface}.conf {$wanif} > {$g['tmp_path']}/{$wanif}_output 2> {$g['tmp_path']}/{$wanif}_error_output"); + + return 0; +} + +function DHCP_Config_File_Advanced($interface, $wancfg, $wanif) { + + $hostname = ""; + if ($wancfg['dhcphostname'] != '') { + $hostname = "\tsend host-name \"{$wancfg['dhcphostname']}\";\n"; + } + + /* DHCP Protocol Timings */ + $protocol_timings = array ('adv_dhcp_pt_timeout' => "timeout", 'adv_dhcp_pt_retry' => "retry", 'adv_dhcp_pt_select_timeout' => "select-timeout", 'adv_dhcp_pt_reboot' => "reboot", 'adv_dhcp_pt_backoff_cutoff' => "backoff-cutoff", 'adv_dhcp_pt_initial_interval' => "initial-interval"); + foreach ($protocol_timings as $Protocol_Timing => $PT_Name) { + $pt_variable = "{$Protocol_Timing}"; + ${$pt_variable} = ""; + if ($wancfg[$Protocol_Timing] != "") { + ${$pt_variable} = "{$PT_Name} {$wancfg[$Protocol_Timing]};\n"; + } + } + + $send_options = ""; + if ($wancfg['adv_dhcp_send_options'] != '') { + $options = explode(',', $wancfg['adv_dhcp_send_options']); + foreach ($options as $option) { + $send_options .= "\tsend " . trim($option) . ";\n"; + } + } + + $request_options = ""; + if ($wancfg['adv_dhcp_request_options'] != '') { + $request_options = "\trequest {$wancfg['adv_dhcp_request_options']};\n"; + } + + $required_options = ""; + if ($wancfg['adv_dhcp_required_options'] != '') { + $required_options = "\trequire {$wancfg['adv_dhcp_required_options']};\n"; + } + + $option_modifiers = ""; + if ($wancfg['adv_dhcp_option_modifiers'] != '') { + $modifiers = explode(',', $wancfg['adv_dhcp_option_modifiers']); + foreach ($modifiers as $modifier) { + $option_modifiers .= "\t" . trim($modifier) . ";\n"; + } + } + + $dhclientconf = "interface \"{$wanif}\" {\n"; + $dhclientconf .= "\n"; + $dhclientconf .= "# DHCP Protocol Timing Values\n"; + $dhclientconf .= "{$adv_dhcp_pt_timeout}"; + $dhclientconf .= "{$adv_dhcp_pt_retry}"; + $dhclientconf .= "{$adv_dhcp_pt_select_timeout}"; + $dhclientconf .= "{$adv_dhcp_pt_reboot}"; + $dhclientconf .= "{$adv_dhcp_pt_backoff_cutoff}"; + $dhclientconf .= "{$adv_dhcp_pt_initial_interval}"; + $dhclientconf .= "\n"; + $dhclientconf .= "# DHCP Protocol Options\n"; + $dhclientconf .= "{$hostname}"; + $dhclientconf .= "{$send_options}"; + $dhclientconf .= "{$request_options}"; + $dhclientconf .= "{$required_options}"; + $dhclientconf .= "{$option_modifiers}"; + $dhclientconf .= "\n"; + $dhclientconf .= "\tscript \"/sbin/dhclient-script\";\n"; + $dhclientconf .= "}\n"; + + $dhclientconf = DHCP_Config_File_Substitutions($wancfg, $wanif, $dhclientconf); + + return $dhclientconf; +} + + +function DHCP_Config_File_Override($wancfg, $wanif) { + + $dhclientconf = @file_get_contents($wancfg['adv_dhcp_config_file_override_path']); + + if ($dhclientconf === false) { + log_error("Error: cannot open {$wancfg['adv_dhcp_config_file_override_path']} in DHCP_Config_File_Override() for reading.\n"); + return ''; + } else { + return DHCP_Config_File_Substitutions($wancfg, $wanif, $dhclientconf); + } +} + + +function DHCP_Config_File_Substitutions($wancfg, $wanif, $dhclientconf) { + + /* Apply Interface Substitutions */ + $dhclientconf = str_replace("{interface}", "{$wanif}", $dhclientconf); + + /* Apply Hostname Substitutions */ + $dhclientconf = str_replace("{hostname}", $wancfg['dhcphostname'], $dhclientconf); + + /* Arrays of MAC Address Types, Cases, Delimiters */ + /* ASCII or HEX, Upper or Lower Case, Various Delimiters (none, space, colon, hyphen, period) */ + $various_mac_types = array("mac_addr_ascii", "mac_addr_hex"); + $various_mac_cases = array("U", "L"); + $various_mac_delimiters = array("", " ", ":", "-", "."); + + /* Apply MAC Address Substitutions */ + foreach ($various_mac_types as $various_mac_type) { + foreach ($various_mac_cases as $various_mac_case) { + foreach ($various_mac_delimiters as $various_mac_delimiter) { + + $res = stripos($dhclientconf, $various_mac_type . $various_mac_case . $various_mac_delimiter); + if ($res !== false) { + + /* Get MAC Address as ASCII String With Colon (:) delimiters */ + if ("$various_mac_case" == "U") { + $dhcpclientconf_mac = strtoupper(get_interface_mac($wanif)); + } + if ("$various_mac_case" == "L") { + $dhcpclientconf_mac = strtolower(get_interface_mac($wanif)); + } + + if ("$various_mac_type" == "mac_addr_hex") { + /* Convert MAC ascii string to HEX with colon (:) delimiters. */ + $dhcpclientconf_mac = str_replace(":", "", $dhcpclientconf_mac); + $dhcpclientconf_mac_hex = ""; + $delimiter = ""; + for ($i = 0; $i < strlen($dhcpclientconf_mac); $i++) { + $dhcpclientconf_mac_hex .= $delimiter. bin2hex($dhcpclientconf_mac[$i]); + $delimiter = ":"; + } + $dhcpclientconf_mac = $dhcpclientconf_mac_hex; + } + + /* MAC Address Delimiter Substitutions */ + $dhcpclientconf_mac = str_replace(":", $various_mac_delimiter, $dhcpclientconf_mac); + + /* Apply MAC Address Substitutions */ + $dhclientconf = str_replace("{" . $various_mac_type . $various_mac_case . $various_mac_delimiter . "}", $dhcpclientconf_mac, $dhclientconf); + } + } + } + } + + return $dhclientconf; +} + +function interfaces_group_setup() { + global $config; + + if (!is_array($config['ifgroups']['ifgroupentry'])) { + return; + } + + foreach ($config['ifgroups']['ifgroupentry'] as $groupar) { + interface_group_setup($groupar); + } + + return; +} + +function interface_group_setup(&$groupname /* The parameter is an array */) { + global $config; + + if (!is_array($groupname)) { + return; + } + $members = explode(" ", $groupname['members']); + foreach ($members as $ifs) { + $realif = get_real_interface($ifs); + if ($realif && does_interface_exist($realif)) { + mwexec("/sbin/ifconfig {$realif} group {$groupname['ifname']}"); + } + } + + return; +} + +function is_interface_group($if) { + global $config; + + if (is_array($config['ifgroups']['ifgroupentry'])) { + foreach ($config['ifgroups']['ifgroupentry'] as $groupentry) { + if ($groupentry['ifname'] === $if) { + return true; + } + } + } + + return false; +} + +function interface_group_add_member($interface, $groupname) { + $interface = get_real_interface($interface); + if (does_interface_exist($interface)) { + mwexec("/sbin/ifconfig {$interface} group " . escapeshellarg($groupname), true); + } +} + +/* COMPAT Function */ +function convert_friendly_interface_to_real_interface_name($interface) { + return get_real_interface($interface); +} + +/* COMPAT Function */ +function get_real_wan_interface($interface = "wan") { + return get_real_interface($interface); +} + +/* COMPAT Function */ +function get_current_wan_address($interface = "wan") { + return get_interface_ip($interface); +} + +/* + * convert_real_interface_to_friendly_interface_name($interface): convert fxp0 -> wan, etc. + */ +function convert_real_interface_to_friendly_interface_name($interface = "wan", $checkparent = false) { + global $config; + + if (stripos($interface, "_vip")) { + foreach ($config['virtualip']['vip'] as $counter => $vip) { + if ($vip['mode'] == "carp") { + if ($interface == "{$vip['interface']}_vip{$vip['vhid']}") { + return $vip['interface']; + } + } + } + } + + /* XXX: For speed reasons reference directly the interface array */ + $ifdescrs = &$config['interfaces']; + //$ifdescrs = get_configured_interface_list(false, true); + + foreach ($ifdescrs as $if => $ifname) { + if ($if == $interface || $ifname['if'] == $interface) { + return $if; + } + + if (get_real_interface($if) == $interface) { + return $if; + } + + if ($checkparent == false) { + continue; + } + + $int = get_parent_interface($if, true); + if (is_array($int)) { + foreach ($int as $iface) { + if ($iface == $interface) { + return $if; + } + } + } + } + + if ($interface == "enc0") { + return 'IPsec'; + } +} + +/* attempt to resolve interface to friendly descr */ +function convert_friendly_interface_to_friendly_descr($interface) { + global $config; + + switch ($interface) { + case "l2tp": + $ifdesc = "L2TP"; + break; + case "pptp": + $ifdesc = "PPTP"; + break; + case "pppoe": + $ifdesc = "PPPoE"; + break; + case "openvpn": + $ifdesc = "OpenVPN"; + break; + case "enc0": + case "ipsec": + case "IPsec": + $ifdesc = "IPsec"; + break; + default: + if (isset($config['interfaces'][$interface])) { + if (empty($config['interfaces'][$interface]['descr'])) { + $ifdesc = strtoupper($interface); + } else { + $ifdesc = strtoupper($config['interfaces'][$interface]['descr']); + } + break; + } else if (substr($interface, 0, 4) == '_vip') { + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $counter => $vip) { + if ($vip['mode'] == "carp") { + if ($interface == "{$vip['interface']}_vip{$vip['vhid']}") { + return "{$vip['subnet']} - {$vip['descr']}"; + } + } + } + } + } else if (substr($interface, 0, 5) == '_lloc') { + return get_interface_linklocal($interface); + } else { + /* if list */ + $ifdescrs = get_configured_interface_with_descr(false, true); + foreach ($ifdescrs as $if => $ifname) { + if ($if == $interface || $ifname == $interface) { + return $ifname; + } + } + } + break; + } + + return $ifdesc; +} + +function convert_real_interface_to_friendly_descr($interface) { + + $ifdesc = convert_real_interface_to_friendly_interface_name("{$interface}"); + + if (!empty($ifdesc)) { + return convert_friendly_interface_to_friendly_descr($ifdesc); + } + + return $interface; +} + +/* + * get_parent_interface($interface): + * --returns the (real or virtual) parent interface(s) array for a given interface friendly name (i.e. wan) + * or virtual interface (i.e. vlan) + * (We need array because MLPPP and bridge interfaces have more than one parent.) + * -- returns $interface passed in if $interface parent is not found + * -- returns empty array if an invalid interface is passed + * (Only handles ppps and vlans now.) + */ +function get_parent_interface($interface, $avoidrecurse = false) { + global $config; + + $parents = array(); + //Check that we got a valid interface passed + $realif = get_real_interface($interface); + if ($realif == NULL) { + return $parents; + } + + // If we got a real interface, find it's friendly assigned name + if ($interface == $realif && $avoidrecurse == false) { + $interface = convert_real_interface_to_friendly_interface_name($interface); + } + + if (!empty($interface) && isset($config['interfaces'][$interface])) { + $ifcfg = $config['interfaces'][$interface]; + switch ($ifcfg['ipaddr']) { + case "ppp": + case "pppoe": + case "pptp": + case "l2tp": + if (empty($parents)) { + if (is_array($config['ppps']['ppp'])) { + foreach ($config['ppps']['ppp'] as $pppidx => $ppp) { + if ($ifcfg['if'] == $ppp['if']) { + $ports = explode(',', $ppp['ports']); + foreach ($ports as $pid => $parent_if) { + $parents[$pid] = get_real_interface($parent_if); + } + break; + } + } + } + } + break; + case "dhcp": + case "static": + default: + // Handle _vlans + if (strpos($realif, '_vlan') !== FALSE) { + if (is_array($config['vlans']['vlan'])) { + foreach ($config['vlans']['vlan'] as $vlanidx => $vlan) { + if ($ifcfg['if'] == $vlan['vlanif']) { + $parents[0] = $vlan['if']; + break; + } + } + } + } + break; + } + } + + if (empty($parents)) { + $parents[0] = $realif; + } + + return $parents; +} + +function interface_is_wireless_clone($wlif) { + if (!stristr($wlif, "_wlan")) { + return false; + } else { + return true; + } +} + +function interface_get_wireless_base($wlif) { + if (!stristr($wlif, "_wlan")) { + return $wlif; + } else { + return substr($wlif, 0, stripos($wlif, "_wlan")); + } +} + +function interface_get_wireless_clone($wlif) { + if (!stristr($wlif, "_wlan")) { + return $wlif . "_wlan0"; + } else { + return $wlif; + } +} + +function get_real_interface($interface = "wan", $family = "all", $realv6iface = false, $flush = true) { + global $config, $g; + + $wanif = NULL; + + switch ($interface) { + case "l2tp": + $wanif = "l2tp"; + break; + case "pptp": + $wanif = "pptp"; + break; + case "pppoe": + $wanif = "pppoe"; + break; + case "openvpn": + $wanif = "openvpn"; + break; + case "ipsec": + case "enc0": + $wanif = "enc0"; + break; + case "ppp": + $wanif = "ppp"; + break; + default: + if (substr($interface, 0, 4) == '_vip') { + $wanif = get_configured_carp_interface_list($interface, '', 'iface'); + if (!empty($wanif)) { + $wanif = get_real_interface($wanif, $family); + } + break; + } else if (substr($interface, 0, 5) == '_lloc') { + $interface = substr($interface, 5); + } else if (does_interface_exist($interface, $flush)) { + /* + * If a real interface was already passed simply + * pass the real interface back. This encourages + * the usage of this function in more cases so that + * we can combine logic for more flexibility. + */ + $wanif = $interface; + break; + } + + if (empty($config['interfaces'][$interface])) { + break; + } + + $cfg = &$config['interfaces'][$interface]; + + if ($family == "inet6") { + switch ($cfg['ipaddrv6']) { + case "6rd": + case "6to4": + $wanif = "{$interface}_stf"; + break; + case 'pppoe': + case 'ppp': + case 'l2tp': + case 'pptp': + if (is_array($cfg['wireless']) || preg_match($g['wireless_regex'], $cfg['if'])) { + $wanif = interface_get_wireless_clone($cfg['if']); + } else { + $wanif = $cfg['if']; + } + break; + default: + switch ($cfg['ipaddr']) { + case 'pppoe': + case 'ppp': + case 'l2tp': + case 'pptp': + if (isset($cfg['dhcp6usev4iface']) && $realv6iface === false) { + $wanif = $cfg['if']; + } else { + $parents = get_parent_interface($interface); + if (!empty($parents[0])) { + $wanif = $parents[0]; + } else { + $wanif = $cfg['if']; + } + } + break; + default: + if (is_array($cfg['wireless']) || preg_match($g['wireless_regex'], $cfg['if'])) { + $wanif = interface_get_wireless_clone($cfg['if']); + } else { + $wanif = $cfg['if']; + } + break; + } + break; + } + } else { + // Wireless cloned NIC support (FreeBSD 8+) + // interface name format: $parentnic_wlanparentnic# + // example: ath0_wlan0 + if (is_array($cfg['wireless']) || preg_match($g['wireless_regex'], $cfg['if'])) { + $wanif = interface_get_wireless_clone($cfg['if']); + } else { + $wanif = $cfg['if']; + } + } + break; + } + + return $wanif; +} + +/* Guess the physical interface by providing a IP address */ +function guess_interface_from_ip($ipaddress) { + + $family = ''; + if (is_ipaddrv4($ipaddress)) { + $family = 'inet'; + } + if (empty($family) && is_ipaddrv6($ipaddress)) { + $family = 'inet6'; + } + + if (empty($family)) { + return false; + } + + /* create a route table we can search */ + $output = ''; + $_gb = exec("/sbin/route -n get -{$family} " . escapeshellarg($ipaddress) . " | /usr/bin/awk '/interface/ { print \$2; };'", $output); + $output[0] = trim($output[0], " \n"); + if (!empty($output[0])) { + return $output[0]; + } + + return false; +} + +/* + * find_ip_interface($ip): return the interface where an ip is defined + * (or if $bits is specified, where an IP within the subnet is defined) + */ +function find_ip_interface($ip, $bits = null) { + if (!is_ipaddr($ip)) { + return false; + } + + $isv6ip = is_ipaddrv6($ip); + + /* if list */ + $ifdescrs = get_configured_interface_list(); + + foreach ($ifdescrs as $ifdescr => $ifname) { + $ifip = ($isv6ip) ? get_interface_ipv6($ifname) : get_interface_ip($ifname); + if (is_null($ifip)) { + continue; + } + if (is_null($bits)) { + if ($ip == $ifip) { + $int = get_real_interface($ifname); + return $int; + } + } else { + if (ip_in_subnet($ifip, $ip . "/" . $bits)) { + $int = get_real_interface($ifname); + return $int; + } + } + } + + return false; +} + +/* + * find_virtual_ip_alias($ip): return the virtual IP alias where an IP is found + * (or if $bits is specified, where an IP within the subnet is found) + */ +function find_virtual_ip_alias($ip, $bits = null) { + global $config; + + if (!is_array($config['virtualip']['vip'])) { + return false; + } + if (!is_ipaddr($ip)) { + return false; + } + + $isv6ip = is_ipaddrv6($ip); + + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['mode'] === "ipalias") { + if (is_ipaddrv6($vip['subnet']) != $isv6ip) { + continue; + } + if (is_null($bits)) { + if (ip_in_subnet($ip, $vip['subnet'] . "/" . $vip['subnet_bits'])) { + return $vip; + } + } else { + if (($isv6ip && check_subnetsv6_overlap($ip, $bits, $vip['subnet'], $vip['subnet_bits'])) || + (!$isv6ip && check_subnets_overlap($ip, $bits, $vip['subnet'], $vip['subnet_bits']))) { + return $vip; + } + } + } + } + return false; +} + +/* + * find_number_of_created_carp_interfaces: return the number of carp interfaces + */ +function find_number_of_created_carp_interfaces() { + return `/sbin/ifconfig | grep "carp:" | wc -l`; +} + +/* + * find_carp_interface($ip): return the carp interface where an ip is defined + */ +function find_carp_interface($ip) { + global $config; + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['mode'] == "carp") { + if (is_ipaddrv4($ip)) { + $carp_ip = get_interface_ip($vip['interface']); + } + if (is_ipaddrv6($ip)) { + $carp_ip = get_interface_ipv6($vip['interface']); + } + exec("/sbin/ifconfig", $output, $return); + foreach ($output as $line) { + $elements = preg_split("/[ ]+/i", $line); + if (strstr($elements[0], "vip")) { + $curif = str_replace(":", "", $elements[0]); + } + if (stristr($line, $ip)) { + $if = $curif; + continue; + } + } + + if ($if) { + return $if; + } + } + } + } +} + +function link_carp_interface_to_parent($interface) { + global $config; + + if (empty($interface)) { + return; + } + + $carp_ip = get_interface_ip($interface); + $carp_ipv6 = get_interface_ipv6($interface); + + if ((!is_ipaddrv4($carp_ip)) && (!is_ipaddrv6($carp_ipv6))) { + return; + } + + /* if list */ + $ifdescrs = get_configured_interface_list(); + foreach ($ifdescrs as $ifdescr => $ifname) { + /* check IPv4 */ + if (is_ipaddrv4($carp_ip)) { + $interfaceip = get_interface_ip($ifname); + $subnet_bits = get_interface_subnet($ifname); + $subnet_ip = gen_subnet("{$interfaceip}", "{$subnet_bits}"); + if (ip_in_subnet($carp_ip, "{$subnet_ip}/{$subnet_bits}")) { + return $ifname; + } + } + /* Check IPv6 */ + if (is_ipaddrv6($carp_ipv6)) { + $interfaceipv6 = get_interface_ipv6($ifname); + $prefixlen = get_interface_subnetv6($ifname); + if (ip_in_subnet($carp_ipv6, "{$interfaceipv6}/{$prefixlen}")) { + return $ifname; + } + } + } + return ""; +} + + +/****f* interfaces/link_ip_to_carp_interface + * NAME + * link_ip_to_carp_interface - Find where a CARP interface links to. + * INPUTS + * $ip + * RESULT + * $carp_ints + ******/ +function link_ip_to_carp_interface($ip) { + global $config; + + if (!is_ipaddr($ip)) { + return; + } + + $carp_ints = ""; + if (is_array($config['virtualip']['vip'])) { + $first = 0; + $carp_int = array(); + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['mode'] == "carp") { + $carp_ip = $vip['subnet']; + $carp_sn = $vip['subnet_bits']; + $carp_nw = gen_subnet($carp_ip, $carp_sn); + if (ip_in_subnet($ip, "{$carp_nw}/{$carp_sn}")) { + $carp_int[] = get_real_interface($vip['interface']); + } + } + } + if (!empty($carp_int)) { + $carp_ints = implode(" ", array_unique($carp_int)); + } + } + + return $carp_ints; +} + +function link_interface_to_track6($int, $action = "") { + global $config; + + if (empty($int)) { + return; + } + + if (is_array($config['interfaces'])) { + $list = array(); + foreach ($config['interfaces'] as $ifname => $ifcfg) { + if (!isset($ifcfg['enable'])) { + continue; + } + if (!empty($ifcfg['ipaddrv6']) && $ifcfg['track6-interface'] == $int) { + if ($action == "update") { + interface_track6_configure($ifname, $ifcfg); + } else if ($action == "") { + $list[$ifname] = $ifcfg; + } + } + } + return $list; + } +} + +function interface_find_child_cfgmtu($realiface) { + global $config; + + $interface = convert_real_interface_to_friendly_interface_name($realiface); + $vlans = link_interface_to_vlans($realiface); + $bridge = link_interface_to_bridge($realiface); + if (!empty($interface)) { + $gifs = link_interface_to_gif($interface); + $gres = link_interface_to_gre($interface); + } else { + $gifs = array(); + $gres = array(); + } + + $mtu = 0; + if (is_array($vlans)) { + foreach ($vlans as $vlan) { + $ifass = convert_real_interface_to_friendly_interface_name($vlan['vlanif']); + if (empty($ifass)) { + continue; + } + if (!empty($config['interfaces'][$ifass]['mtu'])) { + if (intval($config['interfaces'][$ifass]['mtu']) > $mtu) { + $mtu = intval($config['interfaces'][$ifass]['mtu']); + } + } + } + } + if (is_array($gifs)) { + foreach ($gifs as $vlan) { + $ifass = convert_real_interface_to_friendly_interface_name($vlan['gifif']); + if (empty($ifass)) { + continue; + } + if (!empty($config['interfaces'][$ifass]['mtu'])) { + if (intval($config['interfaces'][$ifass]['mtu']) > $mtu) { + $mtu = intval($config['interfaces'][$ifass]['mtu']); + } + } + } + } + if (is_array($gres)) { + foreach ($gres as $vlan) { + $ifass = convert_real_interface_to_friendly_interface_name($vlan['greif']); + if (empty($ifass)) { + continue; + } + if (!empty($config['interfaces'][$ifass]['mtu'])) { + if (intval($config['interfaces'][$ifass]['mtu']) > $mtu) { + $mtu = intval($config['interfaces'][$ifass]['mtu']); + } + } + } + } + $ifass = convert_real_interface_to_friendly_interface_name($bridge); + if (!empty($ifass) && !empty($config['interfaces'][$ifass]['mtu'])) { + if (intval($config['interfaces'][$ifass]['mtu']) > $mtu) { + $mtu = intval($config['interfaces'][$ifass]['mtu']); + } + } + unset($vlans, $bridge, $gifs, $gres, $ifass, $vlan); + + return $mtu; +} + +function link_interface_to_vlans($int, $action = "") { + global $config; + + if (empty($int)) { + return; + } + + if (is_array($config['vlans']['vlan'])) { + $ifaces = array(); + foreach ($config['vlans']['vlan'] as $vlan) { + if ($int == $vlan['if']) { + if ($action == "update") { + interfaces_bring_up($int); + } else { + $ifaces[$vlan['tag']] = $vlan; + } + } + } + if (!empty($ifaces)) { + return $ifaces; + } + } +} + +function link_interface_to_vips($int, $action = "", $vhid = '') { + global $config; + + if (is_array($config['virtualip']['vip'])) { + $result = array(); + foreach ($config['virtualip']['vip'] as $vip) { + if ($int == $vip['interface']) { + if ($action == "update") { + interfaces_vips_configure($int); + } else { + if (empty($vhid) || ($vhid == $vip['vhid'])) { + $result[] = $vip; + } + } + } + } + return $result; + } +} + +/****f* interfaces/link_interface_to_bridge + * NAME + * link_interface_to_bridge - Finds out a bridge group for an interface + * INPUTS + * $ip + * RESULT + * bridge[0-99] + ******/ +function link_interface_to_bridge($int) { + global $config; + + if (is_array($config['bridges']['bridged'])) { + foreach ($config['bridges']['bridged'] as $bridge) { + if (in_array($int, explode(',', $bridge['members']))) { + return "{$bridge['bridgeif']}"; + } + } + } +} + +function link_interface_to_group($int) { + global $config; + + $result = array(); + + if (is_array($config['ifgroups']['ifgroupentry'])) { + foreach ($config['ifgroups']['ifgroupentry'] as $group) { + if (in_array($int, explode(" ", $group['members']))) { + $result[$group['ifname']] = $int; + } + } + } + + return $result; +} + +function link_interface_to_gre($interface) { + global $config; + + $result = array(); + + if (is_array($config['gres']['gre'])) { + foreach ($config['gres']['gre'] as $gre) { + if ($gre['if'] == $interface) { + $result[] = $gre; + } + } + } + + return $result; +} + +function link_interface_to_gif($interface) { + global $config; + + $result = array(); + + if (is_array($config['gifs']['gif'])) { + foreach ($config['gifs']['gif'] as $gif) { + if ($gif['if'] == $interface) { + $result[] = $gif; + } + } + } + + return $result; +} + +/* + * find_interface_ip($interface): return the interface ip (first found) + */ +function find_interface_ip($interface, $flush = false) { + global $interface_ip_arr_cache; + global $interface_sn_arr_cache; + + $interface = str_replace("\n", "", $interface); + + if (!does_interface_exist($interface)) { + return; + } + + /* Setup IP cache */ + if (!isset($interface_ip_arr_cache[$interface]) or $flush) { + $ifinfo = pfSense_get_interface_addresses($interface); + $interface_ip_arr_cache[$interface] = $ifinfo['ipaddr']; + $interface_sn_arr_cache[$interface] = $ifinfo['subnetbits']; + } + + return $interface_ip_arr_cache[$interface]; +} + +/* + * find_interface_ipv6($interface): return the interface ip (first found) + */ +function find_interface_ipv6($interface, $flush = false) { + global $interface_ipv6_arr_cache; + global $interface_snv6_arr_cache; + global $config; + + $interface = trim($interface); + $interface = get_real_interface($interface); + + if (!does_interface_exist($interface)) { + return; + } + + /* Setup IP cache */ + if (!isset($interface_ipv6_arr_cache[$interface]) or $flush) { + $ifinfo = pfSense_get_interface_addresses($interface); + $interface_ipv6_arr_cache[$interface] = $ifinfo['ipaddr6']; + $interface_snv6_arr_cache[$interface] = $ifinfo['subnetbits6']; + } + + return $interface_ipv6_arr_cache[$interface]; +} + +/* + * find_interface_ipv6_ll($interface): return the interface ipv6 link local (first found) + */ +function find_interface_ipv6_ll($interface, $flush = false) { + global $interface_llv6_arr_cache; + global $config; + + $interface = str_replace("\n", "", $interface); + + if (!does_interface_exist($interface)) { + return; + } + + /* Setup IP cache */ + if (!isset($interface_llv6_arr_cache[$interface]) or $flush) { + $ifinfo = pfSense_getall_interface_addresses($interface); + foreach ($ifinfo as $line) { + if (strstr($line, ":")) { + $parts = explode("/", $line); + if (is_linklocal($parts[0])) { + $ifinfo['linklocal'] = $parts[0]; + } + } + } + $interface_llv6_arr_cache[$interface] = $ifinfo['linklocal']; + } + return $interface_llv6_arr_cache[$interface]; +} + +function find_interface_subnet($interface, $flush = false) { + global $interface_sn_arr_cache; + global $interface_ip_arr_cache; + + $interface = str_replace("\n", "", $interface); + if (does_interface_exist($interface) == false) { + return; + } + + if (!isset($interface_sn_arr_cache[$interface]) or $flush) { + $ifinfo = pfSense_get_interface_addresses($interface); + $interface_ip_arr_cache[$interface] = $ifinfo['ipaddr']; + $interface_sn_arr_cache[$interface] = $ifinfo['subnetbits']; + } + + return $interface_sn_arr_cache[$interface]; +} + +function find_interface_subnetv6($interface, $flush = false) { + global $interface_snv6_arr_cache; + global $interface_ipv6_arr_cache; + + $interface = str_replace("\n", "", $interface); + if (does_interface_exist($interface) == false) { + return; + } + + if (!isset($interface_snv6_arr_cache[$interface]) or $flush) { + $ifinfo = pfSense_get_interface_addresses($interface); + $interface_ipv6_arr_cache[$interface] = $ifinfo['ipaddr6']; + $interface_snv6_arr_cache[$interface] = $ifinfo['subnetbits6']; + } + + return $interface_snv6_arr_cache[$interface]; +} + +function ip_in_interface_alias_subnet($interface, $ipalias) { + global $config; + + if (empty($interface) || !is_ipaddr($ipalias)) { + return false; + } + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $vip) { + switch ($vip['mode']) { + case "ipalias": + if ($vip['interface'] <> $interface) { + break; + } + $subnet = is_ipaddrv6($ipalias) ? gen_subnetv6($vip['subnet'], $vip['subnet_bits']) : gen_subnet($vip['subnet'], $vip['subnet_bits']); + if (ip_in_subnet($ipalias, $subnet . "/" . $vip['subnet_bits'])) { + return true; + } + break; + } + } + } + + return false; +} + +function get_possible_listen_ips($include_ipv6_link_local=false) { + + $interfaces = get_configured_interface_with_descr(); + foreach ($interfaces as $iface => $ifacename) { + if ($include_ipv6_link_local) { + /* This is to avoid going though added ll below */ + if (substr($iface, 0, 5) == '_lloc') { + continue; + } + $llip = find_interface_ipv6_ll(get_real_interface($iface)); + if (!empty($llip)) { + $interfaces["_lloc{$iface}"] = "{$ifacename} IPv6 Link-Local"; + } + } + } + /* XXX: Maybe use array_merge below? */ + $carplist = get_configured_carp_interface_list(); + foreach ($carplist as $cif => $carpip) { + $interfaces[$cif] = $carpip . ' (' . get_vip_descr($carpip) . ')'; + } + $aliaslist = get_configured_ip_aliases_list(); + foreach ($aliaslist as $aliasip => $aliasif) { + $interfaces[$aliasip] = $aliasip . ' (' . get_vip_descr($aliasip) . ')'; + } + + $interfaces['lo0'] = 'Localhost'; + + return $interfaces; +} + +function get_possible_traffic_source_addresses($include_ipv6_link_local=false) { + global $config; + + $sourceips = get_possible_listen_ips($include_ipv6_link_local); + foreach (array('server', 'client') as $mode) { + if (is_array($config['openvpn']["openvpn-{$mode}"])) { + foreach ($config['openvpn']["openvpn-{$mode}"] as $id => $setting) { + if (!isset($setting['disable'])) { + $sourceips_key = 'ovpn' . substr($mode, 0, 1) . $setting['vpnid']; + $sourceips[$sourceips_key] = gettext("OpenVPN") . " ".$mode.": ".htmlspecialchars($setting['description']); + } + } + } + } + return $sourceips; +} + +function get_interface_ip($interface = "wan") { + + $realif = get_failover_interface($interface); + if (!$realif) { + return null; + } + + if (substr($realif, 0, 4) == '_vip') { + return get_configured_carp_interface_list($realif, 'inet', 'ip'); + } + + if (strstr($realif, "_vip")) { + return get_configured_carp_interface_list($realif); + } + + $curip = find_interface_ip($realif); + if ($curip && is_ipaddr($curip) && ($curip != "0.0.0.0")) { + return $curip; + } else { + return null; + } +} + +function get_interface_ipv6($interface = "wan", $flush = false) { + global $config; + + $realif = get_failover_interface($interface, 'inet6'); + if (!$realif) { + return null; + } + + if (substr($realif, 0, 4) == '_vip') { + return get_configured_carp_interface_list($realif, 'inet6', 'ip'); + } else if (substr($realif, 0, 5) == '_lloc') { + return get_interface_linklocal($interface); + } + + if (is_array($config['interfaces'][$interface])) { + switch ($config['interfaces'][$interface]['ipaddr']) { + case 'pppoe': + case 'l2tp': + case 'pptp': + case 'ppp': + if ($config['interfaces'][$interface]['ipaddrv6'] == 'dhcp6') { + $realif = get_real_interface($interface, 'inet6', true); + } + break; + } + } + + $curip = find_interface_ipv6($realif, $flush); + if ($curip && is_ipaddrv6($curip) && ($curip != "::")) { + return $curip; + } else { + /* + * NOTE: On the case when only the prefix is requested, + * the communication on WAN will be done over link-local. + */ + if (is_array($config['interfaces'][$interface]) && isset($config['interfaces'][$interface]['dhcp6prefixonly'])) { + $curip = find_interface_ipv6_ll($realif, $flush); + if ($curip && is_ipaddrv6($curip) && ($curip != "::")) { + return $curip; + } + } + } + return null; +} + +function get_interface_linklocal($interface = "wan") { + + $realif = get_failover_interface($interface, 'inet6'); + if (!$realif) { + return null; + } + + if (substr($interface, 0, 4) == '_vip') { + $realif = get_real_interface($interface); + } else if (substr($interface, 0, 5) == '_lloc') { + $realif = get_real_interface(substr($interface, 5)); + } + + $curip = find_interface_ipv6_ll($realif); + if ($curip && is_ipaddrv6($curip) && ($curip != "::")) { + return $curip; + } else { + return null; + } +} + +function get_interface_subnet($interface = "wan") { + + if (substr($interface, 0, 4) == '_vip') { + return get_configured_carp_interface_list($interface, 'inet', 'subnet'); + } + + $realif = get_real_interface($interface); + if (!$realif) { + return null; + } + + $cursn = find_interface_subnet($realif); + if (!empty($cursn)) { + return $cursn; + } + + return null; +} + +function get_interface_subnetv6($interface = "wan") { + + if (substr($interface, 0, 4) == '_vip') { + return get_configured_carp_interface_list($interface, 'inet6', 'subnet'); + } else if (substr($interface, 0, 5) == '_lloc') { + $interface = substr($interface, 5); + } + + $realif = get_real_interface($interface, 'inet6'); + if (!$realif) { + return null; + } + + $cursn = find_interface_subnetv6($realif); + if (!empty($cursn)) { + return $cursn; + } + + return null; +} + +/* return outside interfaces with a gateway */ +function get_interfaces_with_gateway() { + global $config; + + $ints = array(); + + /* loop interfaces, check config for outbound */ + foreach ($config['interfaces'] as $ifdescr => $ifname) { + switch ($ifname['ipaddr']) { + case "dhcp": + case "pppoe": + case "pptp": + case "l2tp": + case "ppp": + $ints[$ifdescr] = $ifdescr; + break; + default: + if (substr($ifname['if'], 0, 4) == "ovpn" || + !empty($ifname['gateway'])) { + $ints[$ifdescr] = $ifdescr; + } + break; + } + } + return $ints; +} + +/* return true if interface has a gateway */ +function interface_has_gateway($friendly) { + global $config; + + if (!empty($config['interfaces'][$friendly])) { + $ifname = &$config['interfaces'][$friendly]; + switch ($ifname['ipaddr']) { + case "dhcp": + case "pppoe": + case "pptp": + case "l2tp": + case "ppp": + return true; + break; + default: + if (substr($ifname['if'], 0, 4) == "ovpn") { + return true; + } + $tunnelif = substr($ifname['if'], 0, 3); + if ($tunnelif == "gif" || $tunnelif == "gre") { + return true; + } + if (!empty($ifname['gateway'])) { + return true; + } + break; + } + } + + return false; +} + +/* return true if interface has a gateway */ +function interface_has_gatewayv6($friendly) { + global $config; + + if (!empty($config['interfaces'][$friendly])) { + $ifname = &$config['interfaces'][$friendly]; + switch ($ifname['ipaddrv6']) { + case "slaac": + case "dhcp6": + case "6to4": + case "6rd": + return true; + break; + default: + if (substr($ifname['if'], 0, 4) == "ovpn") { + return true; + } + $tunnelif = substr($ifname['if'], 0, 3); + if ($tunnelif == "gif" || $tunnelif == "gre") { + return true; + } + if (!empty($ifname['gatewayv6'])) { + return true; + } + break; + } + } + + return false; +} + +/****f* interfaces/is_altq_capable + * NAME + * is_altq_capable - Test if interface is capable of using ALTQ + * INPUTS + * $int - string containing interface name + * RESULT + * boolean - true or false + ******/ + +function is_altq_capable($int) { + /* Per: + * http://www.freebsd.org/cgi/man.cgi?query=altq&apropos=0&sektion=0&manpath=FreeBSD+8.3-RELEASE&arch=default&format=html + * Only the following drivers have ALTQ support + * 20150328 - removed wireless drivers - ath, awi, bwn, iwi, ipw, ral, rum, run, wi - for now. redmine #4406 + */ + $capable = array("ae", "age", "alc", "ale", "an", "aue", "axe", "bce", + "bfe", "bge", "bridge", "cas", "dc", "de", "ed", "em", "ep", "epair", "et", "fxp", "gem", + "hme", "hn", "igb", "ixgbe", "jme", "le", "lem", "msk", "mxge", "my", "nfe", + "nge", "npe", "nve", "re", "rl", "sf", "sge", "sis", "sk", + "ste", "stge", "ti", "txp", "udav", "ural", "vge", "vmx", "vr", "vte", "xl", + "ndis", "tun", "ovpns", "ovpnc", "vlan", "pppoe", "pptp", "ng", + "l2tp", "ppp", "vtnet"); + + $int_family = remove_ifindex($int); + + if (in_array($int_family, $capable)) { + return true; + } else if (stristr($int, "l2tp")) { /* VLANs are named $parent_$vlan now */ + return true; + } else if (stristr($int, "_vlan")) { /* VLANs are named $parent_$vlan now */ + return true; + } else if (stristr($int, "_wlan")) { /* WLANs are named $parent_$wlan now */ + return true; + } else { + return false; + } +} + +/****f* interfaces/is_interface_wireless + * NAME + * is_interface_wireless - Returns if an interface is wireless + * RESULT + * $tmp - Returns if an interface is wireless + ******/ +function is_interface_wireless($interface) { + global $config, $g; + + $friendly = convert_real_interface_to_friendly_interface_name($interface); + if (!isset($config['interfaces'][$friendly]['wireless'])) { + if (preg_match($g['wireless_regex'], $interface)) { + if (isset($config['interfaces'][$friendly])) { + $config['interfaces'][$friendly]['wireless'] = array(); + } + return true; + } + return false; + } else { + return true; + } +} + +function get_wireless_modes($interface) { + /* return wireless modes and channels */ + $wireless_modes = array(); + + $cloned_interface = get_real_interface($interface); + + if ($cloned_interface && is_interface_wireless($cloned_interface)) { + $chan_list = "/sbin/ifconfig {$cloned_interface} list chan"; + $stack_list = "/usr/bin/awk -F\"Channel \" '{ gsub(/\\*/, \" \"); print \$2 \"\\\n\" \$3 }'"; + $format_list = "/usr/bin/awk '{print \$5 \" \" \$6 \",\" \$1}'"; + + $interface_channels = ""; + exec("$chan_list | $stack_list | sort -u | $format_list 2>&1", $interface_channels); + $interface_channel_count = count($interface_channels); + + $c = 0; + while ($c < $interface_channel_count) { + $channel_line = explode(",", $interface_channels["$c"]); + $wireless_mode = trim($channel_line[0]); + $wireless_channel = trim($channel_line[1]); + if (trim($wireless_mode) != "") { + /* if we only have 11g also set 11b channels */ + if ($wireless_mode == "11g") { + if (!isset($wireless_modes["11b"])) { + $wireless_modes["11b"] = array(); + } + } else if ($wireless_mode == "11g ht") { + if (!isset($wireless_modes["11b"])) { + $wireless_modes["11b"] = array(); + } + if (!isset($wireless_modes["11g"])) { + $wireless_modes["11g"] = array(); + } + $wireless_mode = "11ng"; + } else if ($wireless_mode == "11a ht") { + if (!isset($wireless_modes["11a"])) { + $wireless_modes["11a"] = array(); + } + $wireless_mode = "11na"; + } + $wireless_modes["$wireless_mode"]["$c"] = $wireless_channel; + } + $c++; + } + } + return($wireless_modes); +} + +/* return channel numbers, frequency, max txpower, and max regulation txpower */ +function get_wireless_channel_info($interface) { + $wireless_channels = array(); + + $cloned_interface = get_real_interface($interface); + + if ($cloned_interface && is_interface_wireless($cloned_interface)) { + $chan_list = "/sbin/ifconfig {$cloned_interface} list txpower"; + $stack_list = "/usr/bin/awk -F\"Channel \" '{ gsub(/\\*/, \" \"); print \$2 \"\\\n\" \$3 }'"; + $format_list = "/usr/bin/awk '{print \$1 \",\" \$3 \" \" \$4 \",\" \$5 \",\" \$7}'"; + + $interface_channels = ""; + exec("$chan_list | $stack_list | sort -u | $format_list 2>&1", $interface_channels); + + foreach ($interface_channels as $channel_line) { + $channel_line = explode(",", $channel_line); + if (!isset($wireless_channels[$channel_line[0]])) { + $wireless_channels[$channel_line[0]] = $channel_line; + } + } + } + return($wireless_channels); +} + +/****f* interfaces/get_interface_mtu + * NAME + * get_interface_mtu - Return the mtu of an interface + * RESULT + * $tmp - Returns the mtu of an interface + ******/ +function get_interface_mtu($interface) { + $mtu = pfSense_interface_getmtu($interface); + return $mtu['mtu']; +} + +function get_interface_mac($interface) { + + $macinfo = pfSense_get_interface_addresses($interface); + return $macinfo["macaddr"]; +} + +/****f* pfsense-utils/generate_random_mac_address + * NAME + * generate_random_mac - generates a random mac address + * INPUTS + * none + * RESULT + * $mac - a random mac address + ******/ +function generate_random_mac_address() { + $mac = "02"; + for ($x = 0; $x < 5; $x++) { + $mac .= ":" . dechex(rand(16, 255)); + } + return $mac; +} + +/****f* interfaces/is_jumbo_capable + * NAME + * is_jumbo_capable - Test if interface is jumbo frame capable. Useful for determining VLAN capability. + * INPUTS + * $int - string containing interface name + * RESULT + * boolean - true or false + ******/ +function is_jumbo_capable($iface) { + $iface = trim($iface); + $capable = pfSense_get_interface_addresses($iface); + + if (isset($capable['caps']['vlanmtu'])) { + return true; + } + + // hack for some lagg modes missing vlanmtu, but work fine w/VLANs + if (substr($iface, 0, 4) == "lagg") { + return true; + } + + return false; +} + +function interface_setup_pppoe_reset_file($pppif, $iface="") { + global $g; + + $cron_file = "{$g['varetc_path']}/pppoe_restart_{$pppif}"; + + if (!empty($iface) && !empty($pppif)) { + $cron_cmd = <<<EOD +#!/bin/sh +/usr/local/sbin/pfSctl -c 'interface reload {$iface}' +/usr/bin/logger -t {$pppif} "PPPoE periodic reset executed on {$iface}" + +EOD; + + @file_put_contents($cron_file, $cron_cmd); + chmod($cron_file, 0755); + sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP"); + } else { + unlink_if_exists($cron_file); + } +} + +function get_interface_default_mtu($type = "ethernet") { + switch ($type) { + case "gre": + return 1476; + break; + case "gif": + return 1280; + break; + case "tun": + case "vlan": + case "tap": + case "ethernet": + default: + return 1500; + break; + } + + /* Never reached */ + return 1500; +} + +function get_vip_descr($ipaddress) { + global $config; + + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['subnet'] == $ipaddress) { + return ($vip['descr']); + } + } + return ""; +} + +function interfaces_staticarp_configure($if) { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "interfaces_staticarp_configure($if) being called $mt\n"; + } + + $ifcfg = $config['interfaces'][$if]; + + if (empty($if) || empty($ifcfg['if']) || !isset($ifcfg['enable'])) { + return 0; + } + + /* Enable staticarp, if enabled */ + if (isset($config['dhcpd'][$if]['staticarp'])) { + mwexec("/sbin/ifconfig " . escapeshellarg($ifcfg['if']) . " staticarp "); + mwexec("/usr/sbin/arp -d -i " . escapeshellarg($ifcfg['if']) . " -a > /dev/null 2>&1 "); + if (is_array($config['dhcpd'][$if]['staticmap'])) { + foreach ($config['dhcpd'][$if]['staticmap'] as $arpent) { + mwexec("/usr/sbin/arp -s " . escapeshellarg($arpent['ipaddr']) . " " . escapeshellarg($arpent['mac'])); + } + } + } else { + mwexec("/sbin/ifconfig " . escapeshellarg($ifcfg['if']) . " -staticarp "); + mwexec("/usr/sbin/arp -d -i " . escapeshellarg($ifcfg['if']) . " -a > /dev/null 2>&1 "); + if (is_array($config['dhcpd'][$if]) && is_array($config['dhcpd'][$if]['staticmap'])) { + foreach ($config['dhcpd'][$if]['staticmap'] as $arpent) { + if (isset($arpent['arp_table_static_entry'])) { + mwexec("/usr/sbin/arp -s " . escapeshellarg($arpent['ipaddr']) . " " . escapeshellarg($arpent['mac'])); + } + } + } + } + + return 0; +} + +function get_failover_interface($interface, $family = "all") { + global $config; + + /* shortcut to get_real_interface if we find it in the config */ + if (is_array($config['interfaces'][$interface])) { + return get_real_interface($interface, $family); + } + + /* compare against gateway groups */ + $a_groups = return_gateway_groups_array(); + if (is_array($a_groups[$interface])) { + /* we found a gateway group, fetch the interface or vip */ + if (!empty($a_groups[$interface][0]['vip'])) { + return $a_groups[$interface][0]['vip']; + } else { + return $a_groups[$interface][0]['int']; + } + } + /* fall through to get_real_interface */ + /* XXX: Really needed? */ + return get_real_interface($interface, $family); +} + +function remove_ifindex($ifname) { + return preg_replace("/[0-9]+$/", "", $ifname); +} + +?> diff --git a/src/etc/inc/ipsec.attributes.php b/src/etc/inc/ipsec.attributes.php new file mode 100644 index 0000000..8a8ed5f --- /dev/null +++ b/src/etc/inc/ipsec.attributes.php @@ -0,0 +1,200 @@ +<?php +/* + ipsec.attributes.php + Copyright (C) 2011-2012 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +if (empty($common_name)) { + $common_name = getenv("common_name"); + if (empty($common_name)) { + $common_name = getenv("username"); + } +} + +function cisco_to_cidr($addr) { + if (!is_ipaddr($addr)) { + return 0; + } + $mask = decbin(~ip2long($addr)); + $mask = substr($mask, -32); + $k = 0; + for ($i = 0; $i <= 32; $i++) { + $k += intval($mask[$i]); + } + return $k; +} + +function cisco_extract_index($prule) { + + $index = explode("#", $prule); + if (is_numeric($index[1])) { + return intval($index[1]); + } else { + syslog(LOG_WARNING, "Error parsing rule {$prule}: Could not extract index"); + } + return -1;; +} + +function parse_cisco_acl($attribs) { + global $attributes; + if (!is_array($attribs)) { + return ""; + } + + $devname = "enc0"; + $finalrules = ""; + if (is_array($attribs['ciscoavpair'])) { + $inrules = array(); + $outrules = array(); + foreach ($attribs['ciscoavpair'] as $avrules) { + $rule = explode("=", $avrules); + $dir = ""; + if (strstr($rule[0], "inacl")) { + $dir = "in"; + } else if (strstr($rule[0], "outacl")) { + $dir = "out"; + } else if (strstr($rule[0], "dns-servers")) { + $attributes['dns-servers'] = explode(" ", $rule[1]); + continue; + } else if (strstr($rule[0], "route")) { + if (!is_array($attributes['routes'])) { + $attributes['routes'] = array(); + } + $attributes['routes'][] = $rule[1]; + continue; + } + $rindex = cisco_extract_index($rule[0]); + if ($rindex < 0) { + continue; + } + + $rule = $rule[1]; + $rule = explode(" ", $rule); + $tmprule = ""; + $index = 0; + $isblock = false; + if ($rule[$index] == "permit") { + $tmprule = "pass {$dir} quick on {$devname} "; + } else if ($rule[$index] == "deny") { + //continue; + $isblock = true; + $tmprule = "block {$dir} quick on {$devname} "; + } else { + continue; + } + + $index++; + + switch ($rule[$index]) { + case "tcp": + case "udp": + $tmprule .= "proto {$rule[$index]} "; + break; + } + + $index++; + /* Source */ + if (trim($rule[$index]) == "host") { + $index++; + $tmprule .= "from {$rule[$index]} "; + $index++; + if ($isblock == true) { + $isblock = false; + } + } else if (trim($rule[$index]) == "any") { + $tmprule .= "from any"; + $index++; + } else { + $tmprule .= "from {$rule[$index]}"; + $index++; + $netmask = cisco_to_cidr($rule[$index]); + $tmprule .= "/{$netmask} "; + $index++; + if ($isblock == true) { + $isblock = false; + } + } + /* Destination */ + if (trim($rule[$index]) == "host") { + $index++; + $tmprule .= "to {$rule[$index]} "; + $index++; + if ($isblock == true) { + $isblock = false; + } + } else if (trim($rule[$index]) == "any") { + $index++; + $tmprule .= "to any"; + } else { + $tmprule .= "to {$rule[$index]}"; + $index++; + $netmask = cisco_to_cidr($rule[$index]); + $tmprule .= "/{$netmask} "; + $index++; + if ($isblock == true) { + $isblock = false; + } + } + + if ($isblock == true) { + continue; + } + + if ($dir == "in") { + $inrules[$rindex] = $tmprule; + } else if ($dir == "out") { + $outrules[$rindex] = $tmprule; + } + } + + + $state = ""; + if (!empty($outrules)) { + $state = "no state"; + } + ksort($inrules, SORT_NUMERIC); + foreach ($inrules as $inrule) { + $finalrules .= "{$inrule} {$state}\n"; + } + if (!empty($outrules)) { + ksort($outrules, SORT_NUMERIC); + foreach ($outrules as $outrule) { + $finalrules .= "{$outrule} {$state}\n"; + } + } + } + return $finalrules; +} + +$rules = parse_cisco_acl($attributes); +if (!empty($rules)) { + $pid = posix_getpid(); + @file_put_contents("/tmp/ipsec_{$pid}{$common_name}.rules", $rules); + mwexec("/sbin/pfctl -a " . escapeshellarg("ipsec/{$common_name}") . " -f {$g['tmp_path']}/ipsec_{$pid}" . escapeshellarg($common_name) . ".rules"); + @unlink("{$g['tmp_path']}/ipsec_{$pid}{$common_name}.rules"); +} + +?> diff --git a/src/etc/inc/ipsec.auth-user.php b/src/etc/inc/ipsec.auth-user.php new file mode 100755 index 0000000..2589598 --- /dev/null +++ b/src/etc/inc/ipsec.auth-user.php @@ -0,0 +1,169 @@ +#!/usr/local/bin/php-cgi -f +<?php +/* + ipsec.auth-user.php + + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2010 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ +/* + pfSense_BUILDER_BINARIES: + pfSense_MODULE: openvpn +*/ +/* + * ipsec calls this script to authenticate a user + * based on a username and password. We lookup these + * in our config.xml file and check the credentials. + */ + +require_once("globals.inc"); +require_once("config.inc"); +require_once("radius.inc"); +require_once("auth.inc"); +require_once("interfaces.inc"); + +/** + * Get the NAS-Identifier + * + * We will use our local hostname to make up the nas_id + */ +if (!function_exists("getNasID")) { +function getNasID() { + global $g; + + $nasId = gethostname(); + if (empty($nasId)) { + $nasId = $g['product_name']; + } + return $nasId; +} +} + +/** + * Get the NAS-IP-Address based on the current wan address + * + * Use functions in interfaces.inc to find this out + * + */ +if (!function_exists("getNasIP")) { +function getNasIP() { + $nasIp = get_interface_ip(); + if (!$nasIp) { + $nasIp = "0.0.0.0"; + } + return $nasIp; +} +} +/* setup syslog logging */ +openlog("charon", LOG_ODELAY, LOG_AUTH); + +if (isset($_GET['username'])) { + $authmodes = explode(",", $_GET['authcfg']); + $username = $_GET['username']; + $password = $_GET['password']; + $common_name = $_GET['cn']; +} else { + /* read data from environment */ + $username = getenv("username"); + $password = getenv("password"); + $common_name = getenv("common_name"); + $authmodes = explode(",", getenv("authcfg")); +} + +if (!$username || !$password) { + syslog(LOG_ERR, "invalid user authentication environment"); + if (isset($_GET['username'])) { + echo "FAILED"; + closelog(); + return; + } else { + closelog(); + exit (-1); + } +} + +$authenticated = false; + +if (($strictusercn === true) && ($common_name != $username)) { + syslog(LOG_WARNING, "Username does not match certificate common name ({$username} != {$common_name}), access denied.\n"); + if (isset($_GET['username'])) { + echo "FAILED"; + closelog(); + return; + } else { + closelog(); + exit (1); + } +} + +$attributes = array(); +foreach ($authmodes as $authmode) { + $authcfg = auth_get_authserver($authmode); + if (!$authcfg && $authmode != "local") { + continue; + } + + $authenticated = authenticate_user($username, $password, $authcfg, $attributes); + if ($authenticated == true) { + if (stristr($authmode, "local")) { + $user = getUserEntry($username); + if (!is_array($user) || !userHasPrivilege($user, "user-ipsec-xauth-dialin")) { + $authenticated = false; + syslog(LOG_WARNING, "user '{$username}' cannot authenticate through IPsec since the required privileges are missing.\n"); + continue; + } + } + break; + } +} + +if ($authenticated == false) { + syslog(LOG_WARNING, "user '{$username}' could not authenticate.\n"); + if (isset($_GET['username'])) { + echo "FAILED"; + closelog(); + return; + } else { + closelog(); + exit (-1); + } +} + +if (file_exists("/etc/inc/ipsec.attributes.php")) { + include_once("/etc/inc/ipsec.attributes.php"); +} + +syslog(LOG_NOTICE, "user '{$username}' authenticated\n"); +closelog(); + +if (isset($_GET['username'])) { + echo "OK"; +} else { + exit (0); +} + +?> diff --git a/src/etc/inc/ipsec.inc b/src/etc/inc/ipsec.inc new file mode 100644 index 0000000..6654166 --- /dev/null +++ b/src/etc/inc/ipsec.inc @@ -0,0 +1,777 @@ +<?php +/* + ipsec.inc + Copyright (C) 2007 Scott Ullrich + Copyright (C) 2008 Shrew Soft Inc + All rights reserved. + + Parts of this code was originally based on vpn_ipsec_sad.php + Copyright (C) 2003-2004 Manuel Kasper + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_BUILDER_BINARIES: /sbin/setkey /sbin/route + pfSense_MODULE: ipsec + +*/ + +/* IPsec defines */ +global $ipsec_loglevels; +$ipsec_loglevels = array("dmn" => "Daemon", "mgr" => "SA Manager", "ike" => "IKE SA", "chd" => "IKE Child SA", + "job" => "Job Processing", "cfg" => "Configuration backend", "knl" => "Kernel Interface", + "net" => "Networking", "asn" => "ASN encoding", "enc" => "Message encoding", + "imc" => "Integrity checker", "imv" => "Integrity Verifier", "pts" => "Platform Trust Service", + "tls" => "TLS handler", "esp" => "IPsec traffic", "lib" => "StrongSwan Lib"); + +global $my_identifier_list; +$my_identifier_list = array( + 'myaddress' => array('desc' => gettext('My IP address'), 'mobile' => true), + 'address' => array('desc' => gettext('IP address'), 'mobile' => true), + 'fqdn' => array('desc' => gettext('Distinguished name'), 'mobile' => true), + 'user_fqdn' => array('desc' => gettext('User distinguished name'), 'mobile' => true), + 'asn1dn' => array('desc' => gettext('ASN.1 distinguished Name'), 'mobile' => true), + 'keyid tag' => array('desc' => gettext('KeyID tag'), 'mobile' => true), + 'dyn_dns' => array('desc' => gettext('Dynamic DNS'), 'mobile' => true)); + +global $peer_identifier_list; +$peer_identifier_list = array( + 'any' => array('desc' => gettext('Any'), 'mobile' => true), + 'peeraddress' => array('desc' => gettext('Peer IP address'), 'mobile' => false), + 'address' => array('desc' => gettext('IP address'), 'mobile' => false), + 'fqdn' => array('desc' => gettext('Distinguished name'), 'mobile' => true), + 'user_fqdn' => array('desc' => gettext('User distinguished name'), 'mobile' => true), + 'asn1dn' => array('desc' => gettext('ASN.1 distinguished Name'), 'mobile' => true), + 'keyid tag' => array('desc' =>gettext('KeyID tag'), 'mobile' => true)); + +global $ipsec_idhandling; +$ipsec_idhandling = array( + 'yes' => 'YES', 'no' => 'NO', 'never' => 'NEVER', 'keep' => 'KEEP' + ); + +global $p1_ealgos; +$p1_ealgos = array( + 'aes' => array('name' => 'AES', 'keysel' => array('lo' => 128, 'hi' => 256, 'step' => 64)), + 'aes128gcm' => array('name' => 'AES128-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)), + 'aes192gcm' => array('name' => 'AES192-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)), + 'aes256gcm' => array('name' => 'AES256-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)), + 'blowfish' => array('name' => 'Blowfish', 'keysel' => array('lo' => 128, 'hi' => 256, 'step' => 64)), + '3des' => array('name' => '3DES'), + 'cast128' => array('name' => 'CAST128'), + 'des' => array('name' => 'DES')); + +global $p2_ealgos; +$p2_ealgos = array( + 'aes' => array('name' => 'AES', 'keysel' => array('lo' => 128, 'hi' => 256, 'step' => 64)), + 'aes128gcm' => array('name' => 'AES128-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)), + 'aes192gcm' => array('name' => 'AES192-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)), + 'aes256gcm' => array('name' => 'AES256-GCM', 'keysel' => array('lo' => 64, 'hi' => 128, 'step' => 32)), + 'blowfish' => array('name' => 'Blowfish', 'keysel' => array('lo' => 128, 'hi' => 256, 'step' => 64)), + '3des' => array('name' => '3DES'), + 'cast128' => array('name' => 'CAST128'), + 'des' => array('name' => 'DES')); + +global $p1_halgos; +$p1_halgos = array( + 'md5' => 'MD5', + 'sha1' => 'SHA1', + 'sha256' => 'SHA256', + 'sha384' => 'SHA384', + 'sha512' => 'SHA512', + 'aesxcbc' => 'AES-XCBC' +); + +global $p1_dhgroups; +$p1_dhgroups = array( + 1 => '1 (768 bit)', + 2 => '2 (1024 bit)', + 5 => '5 (1536 bit)', + 14 => '14 (2048 bit)', + 15 => '15 (3072 bit)', + 16 => '16 (4096 bit)', + 17 => '17 (6144 bit)', + 18 => '18 (8192 bit)', + 19 => '19 (nist ecp256)', + 20 => '20 (nist ecp384)', + 21 => '21 (nist ecp521)', + 22 => '22 (1024(sub 160) bit)', + 23 => '23 (2048(sub 224) bit)', + 24 => '24 (2048(sub 256) bit)', + 28 => '28 (brainpool ecp256)', + 29 => '29 (brainpool ecp384)', + 30 => '30 (brainpool ecp512)' +); + +global $p2_halgos; +$p2_halgos = array( + 'hmac_md5' => 'MD5', + 'hmac_sha1' => 'SHA1', + 'hmac_sha256' => 'SHA256', + 'hmac_sha384' => 'SHA384', + 'hmac_sha512' => 'SHA512', + 'aesxcbc' => 'AES-XCBC' +); + +global $p1_authentication_methods; +$p1_authentication_methods = array( + 'hybrid_rsa_server' => array('name' => 'Hybrid RSA + Xauth', 'mobile' => true), + 'xauth_rsa_server' => array('name' => 'Mutual RSA + Xauth', 'mobile' => true), + 'xauth_psk_server' => array('name' => 'Mutual PSK + Xauth', 'mobile' => true), + 'eap-tls' => array('name' => 'EAP-TLS', 'mobile' => true), + 'eap-radius' => array('name' => 'EAP-RADIUS', 'mobile' => true), + 'eap-mschapv2' => array('name' => 'EAP-MSChapv2', 'mobile' => true), + 'rsasig' => array('name' => 'Mutual RSA', 'mobile' => false), + 'pre_shared_key' => array('name' => 'Mutual PSK', 'mobile' => false)); + +global $ipsec_preshared_key_type; +$ipsec_preshared_key_type = array( + 'PSK' => 'PSK', + 'EAP' => 'EAP' + ); + +global $p2_modes; +$p2_modes = array( + 'tunnel' => 'Tunnel IPv4', + 'tunnel6' => 'Tunnel IPv6', + 'transport' => 'Transport'); + +global $p2_protos; +$p2_protos = array( + 'esp' => 'ESP', + 'ah' => 'AH'); + +global $p2_pfskeygroups; +$p2_pfskeygroups = array( + 0 => 'off', + 1 => '1 (768 bit)', + 2 => '2 (1024 bit)', + 5 => '5 (1536 bit)', + 14 => '14 (2048 bit)', + 15 => '15 (3072 bit)', + 16 => '16 (4096 bit)', + 17 => '17 (6144 bit)', + 18 => '18 (8192 bit)', + 19 => '19 (nist ecp256)', + 20 => '20 (nist ecp384)', + 21 => '21 (nist ecp521)', + 28 => '28 (brainpool ecp256)', + 29 => '29 (brainpool ecp384)', + 30 => '30 (brainpool ecp512)' +); + +/* + * ikeid management functions + */ + +function ipsec_ikeid_used($ikeid) { + global $config; + + foreach ($config['ipsec']['phase1'] as $ph1ent) { + if ($ikeid == $ph1ent['ikeid']) { + return true; + } + } + + return false; +} + +function ipsec_ikeid_next() { + + $ikeid = 1; + while (ipsec_ikeid_used($ikeid)) { + $ikeid++; + } + + return $ikeid; +} + +/* + * Return phase1 local address + */ +function ipsec_get_phase1_src(& $ph1ent) { + + if ($ph1ent['interface']) { + if (!is_ipaddr($ph1ent['interface'])) { + if (strpos($ph1ent['interface'], '_vip')) { + $if = $ph1ent['interface']; + } else { + $if = get_failover_interface($ph1ent['interface']); + } + if ($ph1ent['protocol'] == "inet6") { + $interfaceip = get_interface_ipv6($if); + } else { + $interfaceip = get_interface_ip($if); + } + } else { + $interfaceip = $ph1ent['interface']; + } + } else { + $if = "wan"; + if ($ph1ent['protocol'] == "inet6") { + $interfaceip = get_interface_ipv6($if); + } else { + $interfaceip = get_interface_ip($if); + } + } + + return $interfaceip; +} + +/* + * Return phase1 local address + */ +function ipsec_get_phase1_dst(& $ph1ent) { + global $g; + + if (empty($ph1ent['remote-gateway'])) { + return false; + } + $rg = $ph1ent['remote-gateway']; + if (!is_ipaddr($rg)) { + if (!platform_booting()) { + return resolve_retry($rg); + } + } + if (!is_ipaddr($rg)) { + return false; + } + + return $rg; +} + +/* + * Return phase2 idinfo in cidr format + */ +function ipsec_idinfo_to_cidr(& $idinfo, $addrbits = false, $mode = "") { + global $config; + + switch ($idinfo['type']) { + case "address": + if ($addrbits) { + if ($mode == "tunnel6") { + return $idinfo['address']."/128"; + } else { + return $idinfo['address']."/32"; + } + } else { + return $idinfo['address']; + } + break; /* NOTREACHED */ + case "network": + return "{$idinfo['address']}/{$idinfo['netbits']}"; + break; /* NOTREACHED */ + case "none": + case "mobile": + return '0.0.0.0/0'; + break; /* NOTREACHED */ + default: + if (empty($mode) && !empty($idinfo['mode'])) { + $mode = $idinfo['mode']; + } + + if ($mode == "tunnel6") { + $address = get_interface_ipv6($idinfo['type']); + $netbits = get_interface_subnetv6($idinfo['type']); + $address = gen_subnetv6($address, $netbits); + return "{$address}/{$netbits}"; + } else { + $address = get_interface_ip($idinfo['type']); + $netbits = get_interface_subnet($idinfo['type']); + $address = gen_subnet($address, $netbits); + return "{$address}/{$netbits}"; + } + break; /* NOTREACHED */ + } +} + +/* + * Return phase2 idinfo in address/netmask format + */ +function ipsec_idinfo_to_subnet(& $idinfo, $addrbits = false) { + global $config; + + switch ($idinfo['type']) { + case "address": + if ($addrbits) { + if ($idinfo['mode'] == "tunnel6") { + return $idinfo['address']."/128"; + } else { + return $idinfo['address']."/255.255.255.255"; + } + } else { + return $idinfo['address']; + } + break; /* NOTREACHED */ + case "none": + case "network": + return $idinfo['address']."/".gen_subnet_mask($idinfo['netbits']); + break; /* NOTREACHED */ + case "mobile": + return "0.0.0.0/0"; + break; /* NOTREACHED */ + default: + if ($idinfo['mode'] == "tunnel6") { + $address = get_interface_ipv6($idinfo['type']); + $netbits = get_interface_subnetv6($idinfo['type']); + $address = gen_subnetv6($address, $netbits); + return $address."/".$netbits; + } else { + $address = get_interface_ip($idinfo['type']); + $netbits = get_interface_subnet($idinfo['type']); + $address = gen_subnet($address, $netbits); + return $address."/".$netbits; + } + break; /* NOTREACHED */ + } +} + +/* + * Return phase2 idinfo in text format + */ +function ipsec_idinfo_to_text(& $idinfo) { + global $config; + + switch ($idinfo['type']) { + case "address": + return $idinfo['address']; + break; /* NOTREACHED */ + case "network": + return $idinfo['address']."/".$idinfo['netbits']; + break; /* NOTREACHED */ + case "mobile": + return gettext("Mobile Client"); + break; /* NOTREACHED */ + case "none": + return gettext("None"); + break; /* NOTREACHED */ + default: + if (!empty($config['interfaces'][$idinfo['type']])) { + return convert_friendly_interface_to_friendly_descr($idinfo['type']); + } else { + return strtoupper($idinfo['type']); + } + break; /* NOTREACHED */ + } +} + +/* + * Return phase1 association for phase2 + */ +function ipsec_lookup_phase1(& $ph2ent, & $ph1ent) { + global $config; + + if (!is_array($config['ipsec'])) { + return false; + } + if (!is_array($config['ipsec']['phase1'])) { + return false; + } + if (empty($config['ipsec']['phase1'])) { + return false; + } + + foreach ($config['ipsec']['phase1'] as $ph1tmp) { + if ($ph1tmp['ikeid'] == $ph2ent['ikeid']) { + $ph1ent = $ph1tmp; + return $ph1ent; + } + } + + return false; +} + +/* + * Check phase1 communications status + */ +function ipsec_phase1_status(&$ipsec_status, $ikeid) { + + foreach ($ipsec_status as $ike) { + if ($ike['id'] == $ikeid) { + if ($ike['status'] == 'established') { + return true; + } + } + } + + return false; +} + +/* + * Check phase2 communications status + */ +function ipsec_phase2_status(&$ipsec_status, &$phase2) { + + if (ipsec_lookup_phase1($ph2ent, $ph1ent)) { + return ipsec_phase1_status($ipsec_status, $ph1ent['ikeid']); + } + + return false; +} + +function ipsec_smp_dump_status() { + global $config, $g, $custom_listtags; + + if (isset($config['ipsec']['enable'])) { + if (!file_exists("{$g['varrun_path']}/charon.xml")) { + log_error("IPsec daemon not running or has a problem!"); + return; + } + } else { + return; + } + + $fd = @fsockopen("unix://{$g['varrun_path']}/charon.xml"); + if (!$fd) { + log_error("Could not read status from IPsec"); + return; + } + $query = '<?xml version="1.0"?><message xmlns="http://www.strongswan.org/smp/1.0" type="request" id="1">'; + $query .= '<query><ikesalist/></query></message>'; + + @fwrite($fd, $query); + $response = ""; + while (!strstr($sread, "</message>")) { + $sread = fgets($fd); + if ($sread === false) { + break; + } + $response .= $sread; + } + fclose($fd); + + if ($sread === false) { + log_error("Error during reading of status from IPsec"); + return; + } + + @file_put_contents("{$g['tmp_path']}/smp_status.xml", $response); + unset($response, $sread); + + $custom_listtags = array('ikesa', 'childsa', 'network', 'auth'); + $response = parse_xml_config("{$g['tmp_path']}/smp_status.xml", "message"); + @unlink("{$g['tmp_path']}/smp_status.xml"); + unset($custom_listtags); + + return $response; +} + +/* + * Return dump of SPD table + */ +function ipsec_dump_spd() { + $fd = @popen("/sbin/setkey -DP", "r"); + $spd = array(); + if ($fd) { + while (!feof($fd)) { + $line = chop(fgets($fd)); + if (!$line) { + continue; + } + if ($line == "No SPD entries.") { + break; + } + if ($line[0] != "\t") { + if (is_array($cursp)) { + $spd[] = $cursp; + } + $cursp = array(); + $linea = explode(" ", $line); + $cursp['srcid'] = substr($linea[0], 0, strpos($linea[0], "[")); + $cursp['dstid'] = substr($linea[1], 0, strpos($linea[1], "[")); + $i = 0; + } else if (is_array($cursp)) { + $line = trim($line, "\t\r\n "); + $linea = explode(" ", $line); + switch ($i) { + case 1: + if ($linea[1] == "none") /* don't show default anti-lockout rule */ { + unset($cursp); + } else { + $cursp['dir'] = $linea[0]; + } + break; + case 2: + $upperspec = explode("/", $linea[0]); + $cursp['proto'] = $upperspec[0]; + list($cursp['src'], $cursp['dst']) = explode("-", $upperspec[2]); + $cursp['reqid'] = substr($upperspec[3], strpos($upperspec[3], "#")+1); + break; + } + } + $i++; + } + if (is_array($cursp) && count($cursp)) { + $spd[] = $cursp; + } + pclose($fd); + } + + return $spd; +} + +/* + * Return dump of SAD table + */ +function ipsec_dump_sad() { + $fd = @popen("/sbin/setkey -D", "r"); + $sad = array(); + if ($fd) { + while (!feof($fd)) { + $line = chop(fgets($fd)); + if (!$line || $line[0] == " ") { + continue; + } + if ($line == "No SAD entries.") { + break; + } + if ($line[0] != "\t") { + if (is_array($cursa)) { + $sad[] = $cursa; + } + $cursa = array(); + list($cursa['src'], $cursa['dst']) = explode(" ", $line); + } else { + $line = trim($line, "\t\n\r "); + $linea = explode(" ", $line); + foreach ($linea as $idx => $linee) { + if ($linee == 'esp' || $linee == 'ah' || $linee[0] == '#') { + $cursa['proto'] = $linee; + } else if (substr($linee, 0, 3) == 'spi') { + $cursa['spi'] = substr($linee, strpos($linee, 'x') + 1, -1); + } else if (substr($linee, 0, 5) == 'reqid') { + $cursa['reqid'] = substr($linee, strpos($linee, 'x') + 1, -1); + } else if (substr($linee, 0, 2) == 'E:') { + $cursa['ealgo'] = $linea[$idx + 1]; + break; + } else if (substr($linee, 0, 2) == 'A:') { + $cursa['aalgo'] = $linea[$idx + 1]; + break; + } else if (substr($linee, 0, 8) == 'current:') { + $cursa['data'] = substr($linea[$idx + 1], 0, strpos($linea[$idx + 1], 'bytes') - 1) . ' B'; + break; + } + } + } + } + if (is_array($cursa) && count($cursa)) { + $sad[] = $cursa; + } + pclose($fd); + } + + return $sad; +} + +/* + * Return dump of mobile user list + */ +function ipsec_dump_mobile() { + global $g, $custom_listtags; + + $_gb = exec("/usr/local/sbin/ipsec stroke leases > {$g['tmp_path']}/strongswan_leases.xml"); + + if (!file_exists("{$g['tmp_path']}/strongswan_leases.xml")) { + log_error(gettext("Unable to find IPsec daemon leases file. Could not display mobile user stats!")); + return array(); + } + + /* This is needed for fixing #4130 */ + if (filesize("{$g['tmp_path']}/strongswan_leases.xml") < 200) { + return array(); + } + + $custom_listtags = array('lease', 'pool'); + $response = parse_xml_config("{$g['tmp_path']}/strongswan_leases.xml", "leases"); + @unlink("{$g['tmp_path']}/strongswan_leases.xml"); + unset($custom_listtags, $_gb); + + return $response; +} + +function ipsec_mobilekey_sort() { + global $config; + + function mobilekeycmp($a, $b) { + return strcmp($a['ident'][0], $b['ident'][0]); + } + + usort($config['ipsec']['mobilekey'], "mobilekeycmp"); +} + +function ipsec_get_number_of_phase2($ikeid) { + global $config; + $a_phase2 = $config['ipsec']['phase2']; + + $nbph2 = 0; + + if (is_array($a_phase2) && count($a_phase2)) { + foreach ($a_phase2 as $ph2tmp) { + if ($ph2tmp['ikeid'] == $ikeid) { + $nbph2++; + } + } + } + + return $nbph2; +} + +function ipsec_get_descr($ikeid) { + global $config; + + if (!isset($config['ipsec']['phase1']) || + !is_array($config['ipsec']['phase1'])) { + return ''; + } + + foreach ($config['ipsec']['phase1'] as $p1) { + if ($p1['ikeid'] == $ikeid) { + return $p1['descr']; + } + } + + return ''; +} + +function ipsec_get_phase1($ikeid) { + global $config; + + if (!isset($config['ipsec']['phase1']) || + !is_array($config['ipsec']['phase1'])) { + return ''; + } + + $a_phase1 = $config['ipsec']['phase1']; + foreach ($a_phase1 as $p1) { + if ($p1['ikeid'] == $ikeid) { + return $p1; + } + } + unset($a_phase1); +} + +function ipsec_fixup_ip($ipaddr) { + if (is_ipaddrv6($ipaddr) || is_subnetv6($ipaddr)) { + return Net_IPv6::compress(Net_IPv6::uncompress($ipaddr)); + } else { + return $ipaddr; + } +} + +function ipsec_find_id(& $ph1ent, $side = "local", $rgmap = array()) { + if ($side == "local") { + $id_type = $ph1ent['myid_type']; + $id_data = $ph1ent['myid_data']; + + $addr = ipsec_get_phase1_src($ph1ent); + if (!$addr) { + return array(); + } + } elseif ($side == "peer") { + $id_type = $ph1ent['peerid_type']; + $id_data = $ph1ent['peerid_data']; + + if (isset($ph1ent['mobile'])) { + $addr = "%any"; + } else { + $addr = $ph1ent['remote-gateway']; + } + } else { + return array(); + } + + + $thisid_type = $id_type; + switch ($thisid_type) { + case 'myaddress': + $thisid_type = 'address'; + $thisid_data = $addr; + break; + case 'dyn_dns': + $thisid_type = 'dns'; + $thisid_data = $id_data; + break; + case 'peeraddress': + $thisid_type = 'address'; + $thisid_data = $rgmap[$ph1ent['remote-gateway']]; + break; + case 'address': + $thisid_data = $id_data; + break; + case 'fqdn': + $thisid_data = "{$id_data}"; + break; + case 'keyid tag': + $thisid_type = 'keyid'; + $thisid_data = "{$id_data}"; + break; + case 'user_fqdn': + $thisid_type = 'userfqdn'; + $thisid_data = "{$id_data}"; + break; + case 'asn1dn': + $thisid_data = $id_data; + break; + } + return array($thisid_type, $thisid_data); +} + +function ipsec_fixup_network($network) { + if (substr($network, -3) == '|/0') { + $result = substr($network, 0, -3); + } else { + $tmp = explode('|', $network); + if (isset($tmp[1])) { + $result = $tmp[1]; + } else { + $result = $tmp[0]; + } + unset($tmp); + } + + return $result; +} + +function ipsec_new_reqid() { + global $config; + + if (!is_array($config['ipsec']) || !is_array($config['ipsec']['phase2'])) { + return; + } + + $ipsecreqid = lock('ipsecreqids', LOCK_EX); + $keyids = array(); + $keyid = 1; + foreach ($config['ipsec']['phase2'] as $ph2) { + $keyids[$ph2['reqid']] = $ph2['reqid']; + } + + for ($i = 1; $i < 16000; $i++) { + if (!isset($keyids[$i])) { + $keyid = $i; + break; + } + } + unlock($ipsecreqid); + + return $keyid; +} + +?> diff --git a/src/etc/inc/itemid.inc b/src/etc/inc/itemid.inc new file mode 100644 index 0000000..c698cfa --- /dev/null +++ b/src/etc/inc/itemid.inc @@ -0,0 +1,108 @@ +<?php + +/* + pfSense_MODULE: utils +*/ + +/* + Copyright (C) 2009 Janne Enberg <janne.enberg@lietu.net> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ + +/****f* itemid/delete_id + * NAME + * delete_id - delete an item with ['id'] = $id from $array + * INPUTS + * $id - int: The ID to delete + * $array - array to delete the item from + * RESULT + * boolean - true if item was found and deleted + ******/ +function delete_id($id, &$array) { + // Index to delete + $delete_index = NULL; + + if (!is_array($array)) { + return false; + } + + // Search for the item in the array + foreach ($array as $key => $item) { + // If this item is the one we want to delete + if (isset($item['associated-rule-id']) && $item['associated-rule-id'] == $id) { + $delete_index = $key; + break; + } + } + + // If we found the item, unset it + if ($delete_index !== NULL) { + unset($array[$delete_index]); + return true; + } else { + return false; + } + +} + +/****f* itemid/get_id + * NAME + * get_id - Get an item id with ['associated-rule-id'] = $id from $array + * INPUTS + * $id - string: The ID to get + * $array - array to get the item from + * RESULT + * mixed - The id, NULL if not found + ******/ +function get_id($id, &$array) { + // Use $foo = &get_id('id', array('id'=>'value')); + + if (!is_array($array)) { + return false; + } + + // Search for the item in the array + foreach ($array as $key => $item) { + // If this item is the one we want to delete + if (isset($item['associated-rule-id']) && $item['associated-rule-id'] == $id) { + return $key; + } + } + + return false; +} + +/****f* itemid/get_unique_id + * NAME + * get_unique_id - get a unique identifier + * RESULT + * string - unique id + ******/ +function get_unique_id() { + + return uniqid("nat_", true); +} + +?>
\ No newline at end of file diff --git a/src/etc/inc/led.inc b/src/etc/inc/led.inc new file mode 100644 index 0000000..a08eef4 --- /dev/null +++ b/src/etc/inc/led.inc @@ -0,0 +1,356 @@ +<? +/* + * led.inc + * + * (C) 2009 Jim Pingle <jimp@pfsense.org> + * + * LED control library that wraps around the functionality of led(4) + * + */ +/* + pfSense_BUILDER_BINARIES: /bin/echo + pfSense_MODULE: utils +*/ + +$led_root = "/dev/led/led"; + +/* + * Send the control string to an LED + */ +function led_ctl($led, $str) { + global $led_root; + if (led_exists($led)) { + exec("/bin/echo " . escapeshellarg($str) . " > {$led_root}{$led}"); + return true; + } + return false; +} + +/* + * Blink an LED at set speed from 1-9 (1=Very Fast, 9=Very Slow) + */ +function led_blink($led, $speed = 0) { + switch ($speed) { + case "reallyfast": + case "veryfast": + $speed = 1; + break; + case "fast": + $speed = 3; + break; + case "medium": + $speed = 5; + break; + case "slow": + $speed = 7; + break; + case "reallyslow": + case "veryslow": + $speed = 9; + break; + } + if (is_numeric($speed) && ($speed > 0) && ($speed < 10)) { + return led_ctl($led, "f{$speed}"); + } + return false; +} + +/* + * Blink an LED in a specific pattern + * Letters A-J are on from 1/10s to 1s + * Letters a-j are off from 1/10s to 1s + */ +function led_pattern($led, $pattern, $repeat = true) { + /* End with a . to stop after one iteration. */ + $end = $repeat ? "" : "."; + return led_ctl($led, "s{$pattern}{$end}"); +} + +/* + * Encode a text message into morse code, and send it to an LED + */ +function led_morse($led, $message) { + return led_ctl($led, "m" . str_to_morse($message)); +} + +/* + * Blink digits out on LED at 1/10s intervals + * e.g 1=1 blink, 8=8 blinks + * 0 is 10 pulses. + * One second pause between digits. + */ +function led_digit($led, $digitstring) { + $i = 0; + $dstring = "d"; + while ($i < strlen($digitstring)) { + $thisdigit = substr($digitstring, $i++, 1); + if (is_numeric($thisdigit)) { + $dstring .= $thisdigit; + } + } + led_ctl($led, $dstring); +} + +/* + * Turn an LED on + */ +function led_on($led) { + led_ctl($led, "1"); +} + +/* + * Turn an LED off + */ +function led_off($led) { + led_ctl($led, "0"); +} + +/* + * Find the number of LEDs present on the system. + */ +function led_count() { + global $led_root; + $count = 0; + $leds = array(); + if (is_dir(dirname($led_root))) { + $leds = glob("{$led_root}*"); + $count = count($leds); + } + return $count; +} + +/* + * Test to see if a given LED exists. + */ +function led_exists($led) { + global $led_root; + if (!is_numeric($led)) { + return false; + } + return file_exists("{$led_root}{$led}"); +} + +/* + * Sweep across three LEDs in a K.I.T.T.-like way. + */ +function led_kitt() { + led_pattern(1, 'AaaaaA'); + led_pattern(2, 'aAaaAa'); + led_pattern(3, 'aaAAaa'); +} + +/* + * Custom pattern for assigning interfaces + */ +function led_assigninterfaces() { + led_pattern(1, 'AaaAaaaaaaaaaaaa'); + led_pattern(2, 'aaaaaAaaAaaaaaaa'); + led_pattern(3, 'aaaaaaaaaaAaaAaa'); +} + +/* + * Return the three LEDs to a standard setup (1=on, 2 and 3 = off) + */ +function led_normalize() { + led_on(1); + led_off(2); + led_off(3); +} + +/* + * Shut off ALL LEDs. + */ +function led_alloff() { + led_off(1); + led_off(2); + led_off(3); +} + +/* + * Translate a string to morse code. Characters not known to have a + * valid morse code representation will be ignored. + */ +function str_to_morse($string) { + $i = 0; + $morsestring = ""; + while ($i < strlen($string)) { + $morsestring .= char_to_morse(substr($string, $i++, 1)) . " "; + } + return $morsestring . "\n"; +} + +/* + * Translate a single character to morse code. Characters not known + * to have a valid morse code representation will be ignored. + */ +function char_to_morse($char) { + switch (strtoupper($char)) { + case "A": + return ".-"; + break; + case "B": + return "-..."; + break; + case "C": + return "-.-."; + break; + case "D": + return "-.."; + break; + case "E": + return "."; + break; + case "F": + return "..-."; + break; + case "G": + return "--."; + break; + case "H": + return "...."; + break; + case "I": + return ".."; + break; + case "J": + return ".---"; + break; + case "K": + return "-.-"; + break; + case "L": + return ".-.."; + break; + case "M": + return "--"; + break; + case "N": + return "-."; + break; + case "O": + return "---"; + break; + case "P": + return ".--."; + break; + case "Q": + return "--.-"; + break; + case "R": + return ".-."; + break; + case "S": + return "..."; + break; + case "T": + return "-"; + break; + case "U": + return "..-"; + break; + case "V": + return "...-"; + break; + case "W": + return ".--"; + break; + case "X": + return "-..-"; + break; + case "Y": + return "-.--"; + break; + case "Z": + return "--.."; + break; + case "0": + return "-----"; + break; + case "1": + return ".----"; + break; + case "2": + return "..---"; + break; + case "3": + return "...--"; + break; + case "4": + return "....-"; + break; + case "5": + return "....."; + break; + case "6": + return "-...."; + break; + case "7": + return "--..."; + break; + case "8": + return "---.."; + break; + case "9": + return "----."; + break; + case ".": + return ".-.-.-"; + break; + case ",": + return "--..--"; + break; + case "?": + return "..--.."; + break; + case "'": + return ".----."; + break; + case "!": + return "-.-.--"; + break; + case "/": + return "-..-."; + break; + case "(": + return "-.--."; + break; + case ")": + return "-.--.-"; + break; + case "&": + return ".-..."; + break; + case ":": + return "---..."; + break; + case ";": + return "-.-.-."; + break; + case "=": + return "-...-"; + break; + case "+": + return ".-.-."; + break; + case "-": + return "-....-"; + break; + case "_": + return "..--.-"; + break; + case "$": + return "...-..-"; + break; + case "@": + return ".--.-."; + break; + case '"': + return ".-..-."; + break; + default: + return ""; + break; + } +} + +?>
\ No newline at end of file diff --git a/src/etc/inc/login_sasl_client.inc b/src/etc/inc/login_sasl_client.inc new file mode 100644 index 0000000..f5cc050 --- /dev/null +++ b/src/etc/inc/login_sasl_client.inc @@ -0,0 +1,69 @@ +<?php +/* + * login_sasl_client.php + * + * @(#) $Id: login_sasl_client.php,v 1.2 2004/11/17 08:00:37 mlemos Exp $ + * + */ + +define("SASL_LOGIN_STATE_START", 0); +define("SASL_LOGIN_STATE_IDENTIFY_USER", 1); +define("SASL_LOGIN_STATE_IDENTIFY_PASSWORD", 2); +define("SASL_LOGIN_STATE_DONE", 3); + +class login_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_LOGIN_STATE_START; + + Function Initialize(&$client) + { + return(1); + } + + Function Start(&$client, &$message, &$interactions) + { + if ($this->state!=SASL_LOGIN_STATE_START) + { + $client->error="LOGIN authentication state is not at the start"; + return(SASL_FAIL); + } + $this->credentials=array( + "user"=>"", + "password"=>"", + "realm"=>"" + ); + $defaults=array( + "realm"=>"" + ); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if ($status==SASL_CONTINUE) + $this->state=SASL_LOGIN_STATE_IDENTIFY_USER; + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch ($this->state) + { + case SASL_LOGIN_STATE_IDENTIFY_USER: + $message=$this->credentials["user"].(strlen($this->credentials["realm"]) ? "@".$this->credentials["realm"] : ""); + $this->state=SASL_LOGIN_STATE_IDENTIFY_PASSWORD; + break; + case SASL_LOGIN_STATE_IDENTIFY_PASSWORD: + $message=$this->credentials["password"]; + $this->state=SASL_LOGIN_STATE_DONE; + break; + case SASL_LOGIN_STATE_DONE: + $client->error="LOGIN authentication was finished without success"; + break; + default: + $client->error="invalid LOGIN authentication step state"; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/src/etc/inc/meta.inc b/src/etc/inc/meta.inc new file mode 100644 index 0000000..ac8bdfc --- /dev/null +++ b/src/etc/inc/meta.inc @@ -0,0 +1,215 @@ +<?php +/* + Copyright (C) 2008 Shrew Soft Inc + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_MODULE: utils + +*/ + +/* + * The meta data format used in pfSense is denoted using markers + * followed by the appropriate value or value pair. All markers + * are prefixed with a ##| sequence. The + suffix is used to + * denote the beginning of a tag block followed by the tag name. + * A - suffix is used to denote the end of a tag block. Values + * are denoted using the * suffix and can optionally be expressed + * as a key value pair. An example of a metadata tag block ... + * + * ###|+INFO + * ###|*BLAH + * ###|*TEXT=SOME TEXT + * ###|-INFO + * + * After calling read_file_metadata, the result array would + * contain the following information ... + * + * metadata['<filename>']['INFO']['BLAH'][0] == true + * metadata['<filename>']['INFO']['TEXT'][0] == "SOME TEXT" + * + * NOTE: All statements must be at the beginning of a line and + * contiguous for a tag. The example shown above would not be + * processed due to the extra ' * ' comment chars. + * + */ + +/* + * locate php files for a given path + */ + +function list_phpfiles($path, & $found) { + + if (!is_array($found)) { + $found = array(); + } + + $dir = opendir($path); + if (!$dir) { + printf(gettext("list_phpfiles: unable to examine path %s\n"), $path); + return; + } + + while ($fname = readdir($dir)) { + if ($fname == "." || $fname == ".." || $fname[0] == '.') { + continue; + } + if (fnmatch('*.php', $fname)) { + $found[] = $fname; + } + } +} + +/* + * read embedded metadata from a file + */ + +function read_file_metadata($fpath, & $metadata, $taglist = false) { + + if (!is_array($metadata)) { + $metadata = array(); + } + + if ($taglist) { + $taglist = explode(",", $taglist); + } + + $fname = $fpath; + $slash = strrpos($fname, "/"); + if ($slash) { + $fname = substr($fname, $slash + 1); + } + + $fdata = @file_get_contents($fpath); + if (!$fdata) { + printf(gettext("unable to read %s\n"), $fpath); + continue; + } + + $offset = 0; + + $tags = array(); + + while (true) { + + $tagbeg_off = stripos($fdata, "##|+", $offset); + if ($tagbeg_off === false) { + break; + } + + $tagbeg_trm = stripos($fdata, "\n", $tagbeg_off); + if ($tagbeg_trm === false) { + break; + } + + $tagend_off = stripos($fdata, "##|-", $tagbeg_trm); + if ($tagend_off === false) { + break; + } + + $tagend_trm = stripos($fdata, "\n", $tagend_off); + if ($tagend_trm === false) { + break; + } + + $tagbeg_len = $tagbeg_trm - $tagbeg_off; + $tagend_len = $tagend_trm - $tagend_off; + + $tagbeg = substr($fdata, $tagbeg_off + 4, $tagbeg_len - 4); + $tagend = substr($fdata, $tagend_off + 4, $tagend_len - 4); + + if ($tagbeg != $tagend) { + printf(gettext("error: tag mismatch ( %1\$s != %2\$s ) in '%3\$s'%4\$s"), $tagbeg, $tagend, $fpath, "\n"); + break; + } + + $mdata_off = $tagbeg_trm + 1; + $mdata_trm = $tagend_off - 1; + $mdata_len = $mdata_trm - $mdata_off; + + $mdata = substr($fdata, $mdata_off, $mdata_len); + + if (!strlen($mdata)) { + printf(gettext("warning: tag %1\$s has no data in '%2\$s'%3\$s"), $tagbeg, $fpath, "\n"); + break; + } + + $offset = $tagend_trm + 1; + + if (is_array($taglist)) { + if (!in_array($tagbeg, $taglist)) { + continue; + } + } + + $vals = array(); + + $lines = explode("\n", $mdata); + foreach ($lines as $line) { + + if (!strlen($line)) { + continue; + } + + $valtag = stripos($line, "##|*"); + if ($valtag === false || $valtag) { + printf(gettext("warning: tag %1\$s has malformed data in '%2\$s'%3\$s"), $tagbeg, $fpath, "\n"); + continue; + } + + $value = substr($line, 4, strlen($line) - 1); + $vlist = explode("=", $value); + + unset($vname); + unset($vdata); + + switch (count($vlist)) { + case 1: + $vname = $vlist[0]; + $vdata = true; + break; + case 2: + $vname = $vlist[0]; + $vdata = $vlist[1]; + break; + } + + if (!isset($vname) || !isset($vdata)) { + printf(gettext("warning: tag %1\$s has invalid data in '%2\$s'%3\$s"), $tagbeg, $fpath, "\n"); + continue; + } + + $vals[$vname][] = $vdata; + } + + if (count($vals)) { + $tags[$tagbeg] = $vals; + } + } + + if (count($tags)) { + $metadata[$fname] = $tags; + } +} + +?> diff --git a/src/etc/inc/notices.inc b/src/etc/inc/notices.inc new file mode 100644 index 0000000..891c18b --- /dev/null +++ b/src/etc/inc/notices.inc @@ -0,0 +1,453 @@ +<?php +/****h* pfSense/notices + NAME + notices.inc - pfSense notice utilities + DESCRIPTION + This include contains the pfSense notice facilities. + HISTORY + $Id$ + + Copyright (C) 2009 Scott Ullrich (sullrich@gmail.com) + Copyright (C) 2005 Colin Smith (ethethlay@gmail.com) + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +/* + pfSense_BUILDER_BINARIES: /bin/echo + pfSense_MODULE: notifications +*/ + +require_once("globals.inc"); +require_once("led.inc"); + +$notice_path = $g['tmp_path'] . '/notices'; +$smtp_authentication_mechanisms = array( + 'PLAIN' => 'PLAIN', + 'LOGIN' => 'LOGIN'); +/* Other SMTP Authentication Mechanisms that could be supported. + * Note that MD5 is no longer considered secure. + * 'GSSAPI' => 'GSSAPI ' . gettext("Generic Security Services Application Program Interface") + * 'DIGEST-MD5' => 'DIGEST-MD5 ' . gettext("Digest access authentication") + * 'MD5' => 'MD5' + * 'CRAM-MD5' => 'CRAM-MD5' +*/ + +/****f* notices/file_notice + * NAME + * file_notice + * INPUTS + * $id, $notice, $category, $url, $priority + * RESULT + * Files a notice and kicks off the various alerts, smtp, growl, system log, LED's, etc. + ******/ +function file_notice($id, $notice, $category = "General", $url = "", $priority = 1) { + /* + * $category - Category that this notice should be displayed under. This can be arbitrary, + * but a page must be set to receive this messages for it to be displayed. + * + * $priority - A notice's priority. Higher numbers indicate greater severity. + * 0 = informational, 1 = warning, 2 = error, etc. This may also be arbitrary, + */ + global $notice_path; + if (!$queue = get_notices()) { + $queue = array(); + } + $queuekey = time(); + $toqueue = array( + 'id' => $id, + 'notice' => $notice, + 'url' => $url, + 'category' => $category, + 'priority' => $priority, + ); + $queue[$queuekey] = $toqueue; + $queueout = fopen($notice_path, "w"); + if (!$queueout) { + log_error(printf(gettext("Could not open %s for writing"), $notice_path)); + return; + } + fwrite($queueout, serialize($queue)); + fclose($queueout); + log_error("New alert found: $notice"); + /* soekris */ + if (file_exists("/dev/led/error")) { + exec("/bin/echo 1 > /dev/led/error"); + } + /* wrap & alix */ + led_normalize(); + led_morse(1, 'sos'); + notify_via_growl($notice); + notify_via_smtp($notice); + return $queuekey; +} + +/****f* notices/get_notices + * NAME + * get_notices + * INPUTS + * $category + * RESULT + * Returns a specific notices text + ******/ +function get_notices($category = "all") { + global $g; + + if (file_exists("{$g['tmp_path']}/notices")) { + $queue = unserialize(file_get_contents("{$g['tmp_path']}/notices")); + if (!$queue) { + return false; + } + if ($category != 'all') { + foreach ($queue as $time => $notice) { + if (strtolower($notice['category']) == strtolower($category)) { + $toreturn[$time] = $notice; + } + } + return $toreturn; + } else { + return $queue; + } + } else { + return false; + } +} + +/****f* notices/close_notice + * NAME + * close_notice + * INPUTS + * $id + * RESULT + * Removes a notice from the list + ******/ +function close_notice($id) { + global $notice_path; + require_once("util.inc"); + /* soekris */ + if (file_exists("/dev/led/error")) { + exec("/bin/echo 0 > /dev/led/error"); + } + /* wrap & alix */ + led_normalize(); + $ids = array(); + if (!$notices = get_notices()) { + return; + } + if ($id == "all") { + unlink_if_exists($notice_path); + return; + } + foreach (array_keys($notices) as $time) { + if ($id == $time) { + unset($notices[$id]); + break; + } + } + foreach ($notices as $key => $notice) { + $ids[$key] = $notice['id']; + } + foreach ($ids as $time => $tocheck) { + if ($id == $tocheck) { + unset($notices[$time]); + break; + } + } + if (count($notices) != 0) { + $queueout = fopen($notice_path, "w"); + fwrite($queueout, serialize($notices)); + fclose($queueout); + } else { + unlink_if_exists($notice_path); + } + + return; +} + +/****f* notices/dump_xml_notices + * NAME + * dump_xml_notices + * INPUTS + * NONE + * RESULT + * Outputs notices in XML formatted text + ******/ +function dump_xml_notices() { + if (file_exists("/cf/conf/use_xmlreader")) { + require_once("xmlreader.inc"); + } else { + require_once("xmlparse.inc"); + } + global $notice_path, $listtags; + $listtags[] = 'notice'; + if (!$notices = get_notices()) { + return; + } + foreach ($notices as $time => $notice) { + $notice['time'] = $time; + $toput['notice'][] = $notice; + } + $xml = dump_xml_config($toput, 'notices'); + return $xml; +} + +/****f* notices/print_notices + * NAME + * print_notices + * INPUTS + * $notices, $category + * RESULT + * prints notices to the GUI + ******/ +function print_notices($notices, $category = "all") { + foreach ($notices as $notice) { + if ($category != "all") { + if (in_array($notice['category'], $category)) { + $categories[] = $notice['category']; + } + } else { + $categories[] = $notice['category']; + } + } + $categories = array_unique($categories); + sort($categories); + foreach ($categories as $category) { + $toreturn .= "<ul><li>{$category}<ul>"; + foreach ($notices as $notice) { + if (strtolower($notice['category']) == strtolower($category)) { + if ($notice['id'] != "") { + if ($notice['url'] != "") { + $toreturn .= "<li><a href={$notice['url']}>{$notice['id']}</a> - {$notice['notice']}</li>"; + } else { + $toreturn .= "<li>{$notice['id']} - {$notice['notice']}</li>"; + } + } + } + } + $toreturn .= "</ul></li></ul>"; + } + return $toreturn; +} + +/****f* notices/print_notice_box + * NAME + * print_notice_box + * INPUTS + * $category + * RESULT + * prints an info box to the GUI + ******/ +function print_notice_box($category = "all") { + $notices = get_notices(); + if (!$notices) { + return; + } + print_info_box_np(print_notices($notices, $category)); + return; +} + +/****f* notices/are_notices_pending + * NAME + * are_notices_pending + * INPUTS + * $category to check + * RESULT + * returns true if notices are pending, false if they are not + ******/ +function are_notices_pending($category = "all") { + global $notice_path; + if (file_exists($notice_path)) { + return true; + } + return false; +} + +/****f* notices/notify_via_smtp + * NAME + * notify_via_smtp + * INPUTS + * notification string to send as an email + * RESULT + * returns true if message was sent + ******/ +function notify_via_smtp($message, $force = false) { + global $config, $g; + if (platform_booting()) { + return; + } + + if (isset($config['notifications']['smtp']['disable']) && !$force) { + return; + } + + /* Do NOT send the same message twice */ + if (file_exists("/var/db/notices_lastmsg.txt")) { + $lastmsg = trim(file_get_contents("/var/db/notices_lastmsg.txt")); + if ($lastmsg == $message) { + return; + } + } + + /* Store last message sent to avoid spamming */ + $fd = fopen("/var/db/notices_lastmsg.txt", "w"); + fwrite($fd, $message); + fclose($fd); + + send_smtp_message($message, "{$config['system']['hostname']}.{$config['system']['domain']} - Notification", $force); + return; +} + +function send_smtp_message($message, $subject = "(no subject)", $force = false) { + global $config, $g; + require_once("sasl.inc"); + require_once("smtp.inc"); + + if (isset($config['notifications']['smtp']['disable']) && !$force) { + return; + } + + if (!$config['notifications']['smtp']['ipaddress']) { + return; + } + + if (!$config['notifications']['smtp']['notifyemailaddress']) { + return; + } + + $smtp = new smtp_class; + + $from = "pfsense@{$config['system']['hostname']}.{$config['system']['domain']}"; + $to = $config['notifications']['smtp']['notifyemailaddress']; + + $smtp->host_name = $config['notifications']['smtp']['ipaddress']; + $smtp->host_port = empty($config['notifications']['smtp']['port']) ? 25 : $config['notifications']['smtp']['port']; + + $smtp->direct_delivery = 0; + $smtp->ssl = (isset($config['notifications']['smtp']['ssl'])) ? 1 : 0; + $smtp->tls = (isset($config['notifications']['smtp']['tls'])) ? 1 : 0; + $smtp->debug = 0; + $smtp->html_debug = 0; + $smtp->localhost = $config['system']['hostname'] . "." . $config['system']['domain']; + + if ($config['notifications']['smtp']['fromaddress']) { + $from = $config['notifications']['smtp']['fromaddress']; + } + + // Use SMTP Auth if fields are filled out + if ($config['notifications']['smtp']['username'] && + $config['notifications']['smtp']['password']) { + if (isset($config['notifications']['smtp']['authentication_mechanism'])) { + $smtp->authentication_mechanism = $config['notifications']['smtp']['authentication_mechanism']; + } else { + $smtp->authentication_mechanism = "PLAIN"; + } + $smtp->user = $config['notifications']['smtp']['username']; + $smtp->password = $config['notifications']['smtp']['password']; + } + + $headers = array( + "From: {$from}", + "To: {$to}", + "Subject: {$subject}", + "Date: " . date("r") + ); + + if ($smtp->SendMessage($from, preg_split('/\s*,\s*/', trim($to)), $headers, $message)) { + log_error(sprintf(gettext("Message sent to %s OK"), $to)); + return; + } else { + log_error(sprintf(gettext('Could not send the message to %1$s -- Error: %2$s'), $to, $smtp->error)); + return(sprintf(gettext('Could not send the message to %1$s -- Error: %2$s'), $to, $smtp->error)); + } +} + +/****f* notices/notify_via_growl + * NAME + * notify_via_growl + * INPUTS + * notification string to send + * RESULT + * returns true if message was sent + ******/ +function notify_via_growl($message, $force=false) { + require_once("growl.class"); + global $config, $g; + + if (isset($config['notifications']['growl']['disable']) && !$force) { + return; + } + + /* Do NOT send the same message twice */ + if (file_exists("/var/db/growlnotices_lastmsg.txt")) { + $lastmsg = trim(file_get_contents("/var/db/growlnotices_lastmsg.txt")); + if ($lastmsg == $message) { + return; + } + } + + $hostname = $config['system']['hostname'] . "." . $config['system']['domain']; + $growl_ip = $config['notifications']['growl']['ipaddress']; + $growl_password = $config['notifications']['growl']['password']; + $growl_name = $config['notifications']['growl']['name']; + $growl_notification = $config['notifications']['growl']['notification_name']; + + if (!empty($growl_ip) && (is_ipaddr($growl_ip) || dns_get_record($growl_ip, DNS_A) || dns_get_record($growl_ip, DNS_AAAA))) { + $growl = new Growl($growl_ip, $growl_password, $growl_name); + $growl->notify("{$growl_notification}", gettext(sprintf("%s (%s) - Notification", $g['product_name'], $hostname)), "{$message}"); + } + + /* Store last message sent to avoid spamming */ + $fd = fopen("/var/db/growlnotices_lastmsg.txt", "w"); + fwrite($fd, $message); + fclose($fd); +} + +/****f* notices/register_via_growl + * NAME + * register_via_growl + * INPUTS + * none + * RESULT + * none + ******/ +function register_via_growl() { + require_once("growl.class"); + global $config; + $growl_ip = $config['notifications']['growl']['ipaddress']; + $growl_password = $config['notifications']['growl']['password']; + $growl_name = $config['notifications']['growl']['name']; + $growl_notification = $config['notifications']['growl']['notification_name']; + + if ($growl_ip) { + $growl = new Growl($growl_ip, $growl_password, $growl_name); + $growl->addNotification($growl_notification); + $growl->register(); + } +} + +/* Notify via remote methods only - not via GUI. */ +function notify_all_remote($msg) { + notify_via_smtp($msg); + notify_via_growl($msg); +} + +?> diff --git a/src/etc/inc/ntlm_sasl_client.inc b/src/etc/inc/ntlm_sasl_client.inc new file mode 100644 index 0000000..18e5658 --- /dev/null +++ b/src/etc/inc/ntlm_sasl_client.inc @@ -0,0 +1,180 @@ +<?php +/* + * ntlm_sasl_client.php + * + * @(#) $Id: ntlm_sasl_client.php,v 1.3 2004/11/17 08:00:37 mlemos Exp $ + * + */ + +define("SASL_NTLM_STATE_START", 0); +define("SASL_NTLM_STATE_IDENTIFY_DOMAIN", 1); +define("SASL_NTLM_STATE_RESPOND_CHALLENGE", 2); +define("SASL_NTLM_STATE_DONE", 3); + +class ntlm_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_NTLM_STATE_START; + + Function Initialize(&$client) + { + if (!function_exists($function="mcrypt_encrypt") || + !function_exists($function="hash")) + { + $extensions=array( + "mcrypt_encrypt"=>"mcrypt", + "hash"=>"hash" + ); + $client->error="the extension ".$extensions[$function]." required by the NTLM SASL client class is not available in this PHP configuration"; + return(0); + } + return(1); + } + + Function ASCIIToUnicode($ascii) + { + for ($unicode="",$a=0;$a<strlen($ascii);$a++) + $unicode.=substr($ascii,$a,1).chr(0); + return($unicode); + } + + Function TypeMsg1($domain,$workstation) + { + $domain_length=strlen($domain); + $workstation_length=strlen($workstation); + $workstation_offset=32; + $domain_offset=$workstation_offset+$workstation_length; + return( + "NTLMSSP\0". + "\x01\x00\x00\x00". + "\x07\x32\x00\x00". + pack("v",$domain_length). + pack("v",$domain_length). + pack("V",$domain_offset). + pack("v",$workstation_length). + pack("v",$workstation_length). + pack("V",$workstation_offset). + $workstation. + $domain + ); + } + + Function NTLMResponse($challenge,$password) + { + $unicode=$this->ASCIIToUnicode($password); + $md4=hash("md4", $unicode); + $padded=$md4.str_repeat(chr(0),21-strlen($md4)); + $iv_size=mcrypt_get_iv_size(MCRYPT_DES,MCRYPT_MODE_ECB); + $iv=mcrypt_create_iv($iv_size,MCRYPT_RAND); + for ($response="",$third=0;$third<21;$third+=7) + { + for ($packed="",$p=$third;$p<$third+7;$p++) + $packed.=str_pad(decbin(ord(substr($padded,$p,1))),8,"0",STR_PAD_LEFT); + for ($key="",$p=0;$p<strlen($packed);$p+=7) + { + $s=substr($packed,$p,7); + $b=$s.((substr_count($s,"1") % 2) ? "0" : "1"); + $key.=chr(bindec($b)); + } + $ciphertext=mcrypt_encrypt(MCRYPT_DES,$key,$challenge,MCRYPT_MODE_ECB,$iv); + $response.=$ciphertext; + } + return $response; + } + + Function TypeMsg3($ntlm_response,$user,$domain,$workstation) + { + $domain_unicode=$this->ASCIIToUnicode($domain); + $domain_length=strlen($domain_unicode); + $domain_offset=64; + $user_unicode=$this->ASCIIToUnicode($user); + $user_length=strlen($user_unicode); + $user_offset=$domain_offset+$domain_length; + $workstation_unicode=$this->ASCIIToUnicode($workstation); + $workstation_length=strlen($workstation_unicode); + $workstation_offset=$user_offset+$user_length; + $lm=""; + $lm_length=strlen($lm); + $lm_offset=$workstation_offset+$workstation_length; + $ntlm=$ntlm_response; + $ntlm_length=strlen($ntlm); + $ntlm_offset=$lm_offset+$lm_length; + $session=""; + $session_length=strlen($session); + $session_offset=$ntlm_offset+$ntlm_length; + return( + "NTLMSSP\0". + "\x03\x00\x00\x00". + pack("v",$lm_length). + pack("v",$lm_length). + pack("V",$lm_offset). + pack("v",$ntlm_length). + pack("v",$ntlm_length). + pack("V",$ntlm_offset). + pack("v",$domain_length). + pack("v",$domain_length). + pack("V",$domain_offset). + pack("v",$user_length). + pack("v",$user_length). + pack("V",$user_offset). + pack("v",$workstation_length). + pack("v",$workstation_length). + pack("V",$workstation_offset). + pack("v",$session_length). + pack("v",$session_length). + pack("V",$session_offset). + "\x01\x02\x00\x00". + $domain_unicode. + $user_unicode. + $workstation_unicode. + $lm. + $ntlm + ); + } + + Function Start(&$client, &$message, &$interactions) + { + if ($this->state!=SASL_NTLM_STATE_START) + { + $client->error="NTLM authentication state is not at the start"; + return(SASL_FAIL); + } + $this->credentials=array( + "user"=>"", + "password"=>"", + "realm"=>"", + "workstation"=>"" + ); + $defaults=array(); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if ($status==SASL_CONTINUE) + $this->state=SASL_NTLM_STATE_IDENTIFY_DOMAIN; + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch ($this->state) + { + case SASL_NTLM_STATE_IDENTIFY_DOMAIN: + $message=$this->TypeMsg1($this->credentials["realm"],$this->credentials["workstation"]); + $this->state=SASL_NTLM_STATE_RESPOND_CHALLENGE; + break; + case SASL_NTLM_STATE_RESPOND_CHALLENGE: + $ntlm_response=$this->NTLMResponse(substr($response,24,8),$this->credentials["password"]); + $message=$this->TypeMsg3($ntlm_response,$this->credentials["user"],$this->credentials["realm"],$this->credentials["workstation"]); + $this->state=SASL_NTLM_STATE_DONE; + break; + case SASL_NTLM_STATE_DONE: + $client->error="NTLM authentication was finished without success"; + return(SASL_FAIL); + default: + $client->error="invalid NTLM authentication step state"; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?> diff --git a/src/etc/inc/openvpn.attributes.php b/src/etc/inc/openvpn.attributes.php new file mode 100644 index 0000000..467d691 --- /dev/null +++ b/src/etc/inc/openvpn.attributes.php @@ -0,0 +1,203 @@ +<?php +/* + openvpn.attributes.php + Copyright (C) 2011-2012 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +if (empty($common_name)) { + $common_name = getenv("common_name"); + if (empty($common_name)) { + $common_name = getenv("username"); + } +} + +$devname = getenv("dev"); +if (empty($devname)) { + $devname = "openvpn"; +} + +function cisco_to_cidr($addr) { + if (!is_ipaddr($addr)) { + return 0; + } + $mask = decbin(~ip2long($addr)); + $mask = substr($mask, -32); + $k = 0; + for ($i = 0; $i <= 32; $i++) { + $k += intval($mask[$i]); + } + return $k; +} + +function cisco_extract_index($prule) { + + $index = explode("#", $prule); + if (is_numeric($index[1])) { + return intval($index[1]); + } else { + syslog(LOG_WARNING, "Error parsing rule {$prule}: Could not extract index"); + } + return -1;; +} + +function parse_cisco_acl($attribs) { + global $devname, $attributes; + if (!is_array($attribs)) { + return ""; + } + $finalrules = ""; + if (is_array($attribs['ciscoavpair'])) { + $inrules = array(); + $outrules = array(); + foreach ($attribs['ciscoavpair'] as $avrules) { + $rule = explode("=", $avrules); + $dir = ""; + if (strstr($rule[0], "inacl")) { + $dir = "in"; + } else if (strstr($rule[0], "outacl")) { + $dir = "out"; + } else if (strstr($rule[0], "dns-servers")) { + $attributes['dns-servers'] = explode(" ", $rule[1]); + continue; + } else if (strstr($rule[0], "route")) { + if (!is_array($attributes['routes'])) { + $attributes['routes'] = array(); + } + $attributes['routes'][] = $rule[1]; + continue; + } + $rindex = cisco_extract_index($rule[0]); + if ($rindex < 0) { + continue; + } + + $rule = $rule[1]; + $rule = explode(" ", $rule); + $tmprule = ""; + $index = 0; + $isblock = false; + if ($rule[$index] == "permit") { + $tmprule = "pass {$dir} quick on {$devname} "; + } else if ($rule[$index] == "deny") { + //continue; + $isblock = true; + $tmprule = "block {$dir} quick on {$devname} "; + } else { + continue; + } + + $index++; + + switch ($rule[$index]) { + case "tcp": + case "udp": + $tmprule .= "proto {$rule[$index]} "; + break; + } + + $index++; + /* Source */ + if (trim($rule[$index]) == "host") { + $index++; + $tmprule .= "from {$rule[$index]} "; + $index++; + if ($isblock == true) { + $isblock = false; + } + } else if (trim($rule[$index]) == "any") { + $tmprule .= "from any"; + $index++; + } else { + $tmprule .= "from {$rule[$index]}"; + $index++; + $netmask = cisco_to_cidr($rule[$index]); + $tmprule .= "/{$netmask} "; + $index++; + if ($isblock == true) { + $isblock = false; + } + } + /* Destination */ + if (trim($rule[$index]) == "host") { + $index++; + $tmprule .= "to {$rule[$index]} "; + $index++; + if ($isblock == true) { + $isblock = false; + } + } else if (trim($rule[$index]) == "any") { + $index++; + $tmprule .= "to any"; + } else { + $tmprule .= "to {$rule[$index]}"; + $index++; + $netmask = cisco_to_cidr($rule[$index]); + $tmprule .= "/{$netmask} "; + $index++; + if ($isblock == true) { + $isblock = false; + } + } + + if ($isblock == true) { + continue; + } + + if ($dir == "in") { + $inrules[$rindex] = $tmprule; + } else if ($dir == "out") { + $outrules[$rindex] = $tmprule; + } + } + + + $state = ""; + if (!empty($outrules)) { + $state = "no state"; + } + ksort($inrules, SORT_NUMERIC); + foreach ($inrules as $inrule) { + $finalrules .= "{$inrule} {$state}\n"; + } + if (!empty($outrules)) { + ksort($outrules, SORT_NUMERIC); + foreach ($outrules as $outrule) { + $finalrules .= "{$outrule} {$state}\n"; + } + } + } + return $finalrules; +} + +$rules = parse_cisco_acl($attributes); +if (!empty($rules)) { + $pid = posix_getpid(); + @file_put_contents("/tmp/ovpn_{$pid}{$common_name}.rules", $rules); + mwexec("/sbin/pfctl -a " . escapeshellarg("openvpn/{$common_name}") . " -f {$g['tmp_path']}/ovpn_{$pid}" . escapeshellarg($common_name) . ".rules"); + @unlink("{$g['tmp_path']}/ovpn_{$pid}{$common_name}.rules"); +} + +?> diff --git a/src/etc/inc/openvpn.auth-user.php b/src/etc/inc/openvpn.auth-user.php new file mode 100644 index 0000000..e108a4f --- /dev/null +++ b/src/etc/inc/openvpn.auth-user.php @@ -0,0 +1,213 @@ +#!/usr/local/bin/php-cgi -f +<?php +/* $Id$ */ +/* + openvpn.auth-user.php + + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2010 Ermal Luçi + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ +/* + pfSense_BUILDER_BINARIES: + pfSense_MODULE: openvpn +*/ +/* + * OpenVPN calls this script to authenticate a user + * based on a username and password. We lookup these + * in our config.xml file and check the credentials. + */ + +require_once("globals.inc"); +require_once("config.inc"); +require_once("radius.inc"); +require_once("auth.inc"); +require_once("interfaces.inc"); + +/** + * Get the NAS-Identifier + * + * We will use our local hostname to make up the nas_id + */ +if (!function_exists("getNasID")) { +function getNasID() { + global $g; + + $nasId = gethostname(); + if (empty($nasId)) { + $nasId = $g['product_name']; + } + return $nasId; +} +} + +/** + * Get the NAS-IP-Address based on the current wan address + * + * Use functions in interfaces.inc to find this out + * + */ +if (!function_exists("getNasIP")) { +function getNasIP() { + $nasIp = get_interface_ip(); + if (!$nasIp) { + $nasIp = "0.0.0.0"; + } + return $nasIp; +} +} +/* setup syslog logging */ +openlog("openvpn", LOG_ODELAY, LOG_AUTH); + +if (isset($_GET['username'])) { + $authmodes = explode(",", $_GET['authcfg']); + $username = base64_decode(str_replace('%3D', '=', $_GET['username'])); + $password = base64_decode(str_replace('%3D', '=', $_GET['password'])); + $common_name = $_GET['cn']; + $modeid = $_GET['modeid']; + $strictusercn = $_GET['strictcn'] == "false" ? false : true; +} else { + /* read data from environment */ + $username = getenv("username"); + $password = getenv("password"); + $common_name = getenv("common_name"); +} + +if (!$username || !$password) { + syslog(LOG_ERR, "invalid user authentication environment"); + if (isset($_GET['username'])) { + echo "FAILED"; + closelog(); + return; + } else { + closelog(); + return (-1); + } +} + +/* Replaced by a sed with proper variables used below(ldap parameters). */ +//<template> + +if (file_exists("{$g['varetc_path']}/openvpn/{$modeid}.ca")) { + putenv("LDAPTLS_CACERT={$g['varetc_path']}/openvpn/{$modeid}.ca"); + putenv("LDAPTLS_REQCERT=never"); +} + +$authenticated = false; + +if (($strictusercn === true) && ($common_name != $username)) { + syslog(LOG_WARNING, "Username does not match certificate common name ({$username} != {$common_name}), access denied.\n"); + if (isset($_GET['username'])) { + echo "FAILED"; + closelog(); + return; + } else { + closelog(); + return (1); + } +} + +if (!is_array($authmodes)) { + syslog(LOG_WARNING, "No authentication server has been selected to authenticate against. Denying authentication for user {$username}"); + if (isset($_GET['username'])) { + echo "FAILED"; + closelog(); + return; + } else { + closelog(); + return (1); + } +} + +$attributes = array(); +foreach ($authmodes as $authmode) { + $authcfg = auth_get_authserver($authmode); + if (!$authcfg && $authmode != "local") { + continue; + } + + $authenticated = authenticate_user($username, $password, $authcfg, $attributes); + if ($authenticated == true) { + break; + } +} + +if ($authenticated == false) { + syslog(LOG_WARNING, "user '{$username}' could not authenticate.\n"); + if (isset($_GET['username'])) { + echo "FAILED"; + closelog(); + return; + } else { + closelog(); + return (-1); + } +} + +if (file_exists("/etc/inc/openvpn.attributes.php")) { + include_once("/etc/inc/openvpn.attributes.php"); +} + +$content = ""; +if (is_array($attributes['dns-servers'])) { + foreach ($attributes['dns-servers'] as $dnssrv) { + if (is_ipaddr($dnssrv)) { + $content .= "push \"dhcp-option DNS {$dnssrv}\"\n"; + } + } +} +if (is_array($attributes['routes'])) { + foreach ($attributes['routes'] as $route) { + $content .= "push \"route {$route} vpn_gateway\"\n"; + } +} + +if (isset($attributes['framed_ip'])) { +/* XXX: only use when TAP windows driver >= 8.2.x */ +/* if (isset($attributes['framed_mask'])) { + $content .= "topology subnet\n"; + $content .= "ifconfig-push {$attributes['framed_ip']} {$attributes['framed_mask']}"; + } else { +*/ + $content .= "topology net30\n"; + $content .= "ifconfig-push {$attributes['framed_ip']} ". long2ip((ip2long($attributes['framed_ip']) + 1)); +// } +} + +if (!empty($content)) { + @file_put_contents("{$g['tmp_path']}/{$username}", $content); +} + +syslog(LOG_NOTICE, "user '{$username}' authenticated\n"); +closelog(); + +if (isset($_GET['username'])) { + echo "OK"; +} else { + return (0); +} + +?> diff --git a/src/etc/inc/openvpn.inc b/src/etc/inc/openvpn.inc new file mode 100644 index 0000000..945e7ff --- /dev/null +++ b/src/etc/inc/openvpn.inc @@ -0,0 +1,1589 @@ +<?php +/* + openvpn.inc part of pfSense + + Copyright (C) 2008 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Copyright (C) 2006 Fernando Lemos + All rights reserved. + + This file was rewritten from scratch by Fernando Lemos but + *MIGHT* contain code previously written by: + + Copyright (C) 2005 Peter Allgeyer <allgeyer_AT_web.de> + All rights reserved. + + Copyright (C) 2004 Peter Curran (peter@closeconsultants.com). + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notices, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notices, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + + pfSense_BUILDER_BINARIES: /usr/local/sbin/openvpn /usr/bin/openssl /sbin/ifconfig + pfSense_MODULE: openvpn + +*/ +require_once('config.inc'); +require_once("certs.inc"); +require_once('pfsense-utils.inc'); +require_once("auth.inc"); + +global $openvpn_prots; +$openvpn_prots = array("UDP", "UDP6", "TCP", "TCP6"); + +global $openvpn_dev_mode; +$openvpn_dev_mode = array("tun", "tap"); + +global $openvpn_verbosity_level; +$openvpn_verbosity_level = array( + 0 => "none", + 1 => "default", + 2 => "2", + 3 => "3 (recommended)", + 4 => "4", + 5 => "5", + 6 => "6", + 7 => "7", + 8 => "8", + 9 => "9", + 10 => "10", + 11 => "11" +); + +/* + * The User Auth mode below is disabled because + * OpenVPN erroneously requires that we provide + * a CA configuration parameter. In this mode, + * clients don't send a certificate so there is + * no need for a CA. If we require that admins + * provide one in the pfSense UI due to a bogus + * requirement imposed by OpenVPN, it could be + * considered very confusing ( I know I was ). + * + * -mgrooms + */ + +global $openvpn_dh_lengths; +$openvpn_dh_lengths = array( + 1024, 2048, 4096); + +global $openvpn_cert_depths; +$openvpn_cert_depths = array( + 1 => "One (Client+Server)", + 2 => "Two (Client+Intermediate+Server)", + 3 => "Three (Client+2xIntermediate+Server)", + 4 => "Four (Client+3xIntermediate+Server)", + 5 => "Five (Client+4xIntermediate+Server)" +); + +global $openvpn_server_modes; +$openvpn_server_modes = array( + 'p2p_tls' => gettext("Peer to Peer ( SSL/TLS )"), + 'p2p_shared_key' => gettext("Peer to Peer ( Shared Key )"), + 'server_tls' => gettext("Remote Access ( SSL/TLS )"), + 'server_user' => gettext("Remote Access ( User Auth )"), + 'server_tls_user' => gettext("Remote Access ( SSL/TLS + User Auth )")); + +global $openvpn_client_modes; +$openvpn_client_modes = array( + 'p2p_tls' => gettext("Peer to Peer ( SSL/TLS )"), + 'p2p_shared_key' => gettext("Peer to Peer ( Shared Key )")); + +global $openvpn_compression_modes; +$openvpn_compression_modes = array( + '' => gettext("No Preference"), + 'no' => gettext("Disabled - No Compression"), + 'adaptive' => gettext("Enabled with Adaptive Compression"), + 'yes' => gettext("Enabled without Adaptive Compression")); + +function openvpn_create_key() { + + $fp = popen("/usr/local/sbin/openvpn --genkey --secret /dev/stdout 2>/dev/null", "r"); + if (!$fp) { + return false; + } + + $rslt = stream_get_contents($fp); + pclose($fp); + + return $rslt; +} + +function openvpn_create_dhparams($bits) { + + $fp = popen("/usr/bin/openssl dhparam {$bits} 2>/dev/null", "r"); + if (!$fp) { + return false; + } + + $rslt = stream_get_contents($fp); + pclose($fp); + + return $rslt; +} + +function openvpn_vpnid_used($vpnid) { + global $config; + + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as & $settings) { + if ($vpnid == $settings['vpnid']) { + return true; + } + } + } + + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as & $settings) { + if ($vpnid == $settings['vpnid']) { + return true; + } + } + } + + return false; +} + +function openvpn_vpnid_next() { + + $vpnid = 1; + while (openvpn_vpnid_used($vpnid)) { + $vpnid++; + } + + return $vpnid; +} + +function openvpn_port_used($prot, $interface, $port, $curvpnid = 0) { + global $config; + + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as & $settings) { + if (isset($settings['disable'])) { + continue; + } + + if ($curvpnid != 0 && $curvpnid == $settings['vpnid']) { + continue; + } + + if ($port == $settings['local_port'] && $prot == $settings['protocol'] && + ($interface == $settings['interface'] || $interface == "any" || $settings['interface'] == "any")) { + return $settings['vpnid']; + } + } + } + + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as & $settings) { + if (isset($settings['disable'])) { + continue; + } + + if ($curvpnid != 0 && $curvpnid == $settings['vpnid']) { + continue; + } + + if ($port == $settings['local_port'] && $prot == $settings['protocol'] && + ($interface == $settings['interface'] || $interface == "any" || $settings['interface'] == "any")) { + return $settings['vpnid']; + } + } + } + + return 0; +} + +function openvpn_port_next($prot, $interface = "wan") { + + $port = 1194; + while (openvpn_port_used($prot, $interface, $port)) { + $port++; + } + while (openvpn_port_used($prot, "any", $port)) { + $port++; + } + + return $port; +} + +function openvpn_get_cipherlist() { + + $ciphers = array(); + $cipher_out = shell_exec('/usr/local/sbin/openvpn --show-ciphers | /usr/bin/grep "default key" | /usr/bin/awk \'{print $1, "(" $2 "-" $3 ")";}\''); + $cipher_lines = explode("\n", trim($cipher_out)); + sort($cipher_lines); + foreach ($cipher_lines as $line) { + $words = explode(' ', $line); + $ciphers[$words[0]] = "{$words[0]} {$words[1]}"; + } + $ciphers["none"] = gettext("None (No Encryption)"); + return $ciphers; +} + +function openvpn_get_digestlist() { + + $digests = array(); + $digest_out = shell_exec('/usr/local/sbin/openvpn --show-digests | /usr/bin/grep "digest size" | /usr/bin/awk \'{print $1, "(" $2 "-" $3 ")";}\''); + $digest_lines = explode("\n", trim($digest_out)); + sort($digest_lines); + foreach ($digest_lines as $line) { + $words = explode(' ', $line); + $digests[$words[0]] = "{$words[0]} {$words[1]}"; + } + $digests["none"] = gettext("None (No Authentication)"); + return $digests; +} + +function openvpn_get_engines() { + $openssl_engines = array('none' => 'No Hardware Crypto Acceleration'); + exec("/usr/bin/openssl engine -t -c", $openssl_engine_output); + $openssl_engine_output = implode("\n", $openssl_engine_output); + $openssl_engine_output = preg_replace("/\\n\\s+/", "|", $openssl_engine_output); + $openssl_engine_output = explode("\n", $openssl_engine_output); + + foreach ($openssl_engine_output as $oeo) { + $keep = true; + $details = explode("|", $oeo); + $engine = array_shift($details); + $linematch = array(); + preg_match("/\((.*)\)\s(.*)/", $engine, $linematch); + foreach ($details as $dt) { + if (strpos($dt, "unavailable") !== FALSE) { + $keep = false; + } + if (strpos($dt, "available") !== FALSE) { + continue; + } + if (strpos($dt, "[") !== FALSE) { + $ciphers = trim($dt, "[]"); + } + } + if (!empty($ciphers)) { + $ciphers = " - " . $ciphers; + } + if (strlen($ciphers) > 60) { + $ciphers = substr($ciphers, 0, 60) . " ... "; + } + if ($keep) { + $openssl_engines[$linematch[1]] = $linematch[2] . $ciphers; + } + } + return $openssl_engines; +} + +function openvpn_validate_engine($engine) { + $engines = openvpn_get_engines(); + return array_key_exists($engine, $engines); +} + +function openvpn_validate_host($value, $name) { + $value = trim($value); + if (empty($value) || (!is_domain($value) && !is_ipaddr($value))) { + return sprintf(gettext("The field '%s' must contain a valid IP address or domain name."), $name); + } + return false; +} + +function openvpn_validate_port($value, $name) { + $value = trim($value); + if (empty($value) || !is_numeric($value) || $value < 0 || ($value > 65535)) { + return sprintf(gettext("The field '%s' must contain a valid port, ranging from 0 to 65535."), $name); + } + return false; +} + +function openvpn_validate_cidr($value, $name, $multiple = false, $ipproto = "ipv4") { + $value = trim($value); + $error = false; + if (empty($value)) { + return false; + } + $networks = explode(',', $value); + + if (!$multiple && (count($networks) > 1)) { + return sprintf(gettext("The field '%s' must contain a single valid %s CIDR range."), $name, $ipproto); + } + + foreach ($networks as $network) { + if ($ipproto == "ipv4") { + $error = !openvpn_validate_cidr_ipv4($network); + } else { + $error = !openvpn_validate_cidr_ipv6($network); + } + if ($error) { + break; + } + } + + if ($error) { + return sprintf(gettext("The field '%s' must contain only valid %s CIDR range(s) separated by commas."), $name, $ipproto); + } else { + return false; + } +} + +function openvpn_validate_cidr_ipv4($value) { + $value = trim($value); + if (!empty($value)) { + list($ip, $mask) = explode('/', $value); + if (!is_ipaddrv4($ip) or !is_numeric($mask) or ($mask > 32) or ($mask < 0)) { + return false; + } + } + return true; +} + +function openvpn_validate_cidr_ipv6($value) { + $value = trim($value); + if (!empty($value)) { + list($ipv6, $prefix) = explode('/', $value); + if (empty($prefix)) { + $prefix = "128"; + } + if (!is_ipaddrv6($ipv6) or !is_numeric($prefix) or ($prefix > 128) or ($prefix < 0)) { + return false; + } + } + return true; +} + +function openvpn_add_dhcpopts(& $settings, & $conf) { + + if (!empty($settings['dns_domain'])) { + $conf .= "push \"dhcp-option DOMAIN {$settings['dns_domain']}\"\n"; + } + + if (!empty($settings['dns_server1'])) { + $conf .= "push \"dhcp-option DNS {$settings['dns_server1']}\"\n"; + } + if (!empty($settings['dns_server2'])) { + $conf .= "push \"dhcp-option DNS {$settings['dns_server2']}\"\n"; + } + if (!empty($settings['dns_server3'])) { + $conf .= "push \"dhcp-option DNS {$settings['dns_server3']}\"\n"; + } + if (!empty($settings['dns_server4'])) { + $conf .= "push \"dhcp-option DNS {$settings['dns_server4']}\"\n"; + } + + if (!empty($settings['push_register_dns'])) { + $conf .= "push \"register-dns\"\n"; + } + + if (!empty($settings['ntp_server1'])) { + $conf .= "push \"dhcp-option NTP {$settings['ntp_server1']}\"\n"; + } + if (!empty($settings['ntp_server2'])) { + $conf .= "push \"dhcp-option NTP {$settings['ntp_server2']}\"\n"; + } + + if ($settings['netbios_enable']) { + + if (!empty($settings['dhcp_nbttype']) && ($settings['dhcp_nbttype'] != 0)) { + $conf .= "push \"dhcp-option NBT {$settings['dhcp_nbttype']}\"\n"; + } + if (!empty($settings['dhcp_nbtscope'])) { + $conf .= "push \"dhcp-option NBS {$settings['dhcp_nbtscope']}\"\n"; + } + + if (!empty($settings['wins_server1'])) { + $conf .= "push \"dhcp-option WINS {$settings['wins_server1']}\"\n"; + } + if (!empty($settings['wins_server2'])) { + $conf .= "push \"dhcp-option WINS {$settings['wins_server2']}\"\n"; + } + + if (!empty($settings['nbdd_server1'])) { + $conf .= "push \"dhcp-option NBDD {$settings['nbdd_server1']}\"\n"; + } + } + + if ($settings['gwredir']) { + $conf .= "push \"redirect-gateway def1\"\n"; + } +} + +function openvpn_add_custom(& $settings, & $conf) { + + if ($settings['custom_options']) { + + $options = explode(';', $settings['custom_options']); + + if (is_array($options)) { + foreach ($options as $option) { + $conf .= "$option\n"; + } + } else { + $conf .= "{$settings['custom_options']}\n"; + } + } +} + +function openvpn_add_keyfile(& $data, & $conf, $mode_id, $directive, $opt = "") { + global $g; + + $fpath = $g['varetc_path']."/openvpn/{$mode_id}.{$directive}"; + openvpn_create_dirs(); + file_put_contents($fpath, base64_decode($data)); + //chown($fpath, 'nobody'); + //chgrp($fpath, 'nobody'); + @chmod($fpath, 0600); + + $conf .= "{$directive} {$fpath} {$opt}\n"; +} + +function openvpn_reconfigure($mode, $settings) { + global $g, $config; + + if (empty($settings)) { + return; + } + if (isset($settings['disable'])) { + return; + } + openvpn_create_dirs(); + /* + * NOTE: Deleting tap devices causes spontaneous reboots. Instead, + * we use a vpnid number which is allocated for a particular client + * or server configuration. ( see openvpn_vpnid_next() ) + */ + + $vpnid = $settings['vpnid']; + $mode_id = $mode.$vpnid; + + if (isset($settings['dev_mode'])) { + $tunname = "{$settings['dev_mode']}{$vpnid}"; + } else { + /* defaults to tun */ + $tunname = "tun{$vpnid}"; + $settings['dev_mode'] = "tun"; + } + + if ($mode == "server") { + $devname = "ovpns{$vpnid}"; + } else { + $devname = "ovpnc{$vpnid}"; + } + + /* is our device already configured */ + if (!does_interface_exist($devname)) { + + /* create the tap device if required */ + if (!file_exists("/dev/{$tunname}")) { + exec("/sbin/ifconfig " . escapeshellarg($tunname) . " create"); + } + + /* rename the device */ + mwexec("/sbin/ifconfig " . escapeshellarg($tunname) . " name " . escapeshellarg($devname)); + + /* add the device to the openvpn group and make sure it's UP*/ + mwexec("/sbin/ifconfig " . escapeshellarg($devname) . " group openvpn up"); + + $ifname = convert_real_interface_to_friendly_interface_name($devname); + $grouptmp = link_interface_to_group($ifname); + if (!empty($grouptmp)) { + array_walk($grouptmp, 'interface_group_add_member'); + } + unset($grouptmp, $ifname); + } + + $pfile = $g['varrun_path'] . "/openvpn_{$mode_id}.pid"; + $proto = strtolower($settings['protocol']); + if (substr($settings['protocol'], 0, 3) == "TCP") { + $proto = "{$proto}-{$mode}"; + } + $dev_mode = $settings['dev_mode']; + $cipher = $settings['crypto']; + // OpenVPN defaults to SHA1, so use it when unset to maintain compatibility. + $digest = !empty($settings['digest']) ? $settings['digest'] : "SHA1"; + + $interface = get_failover_interface($settings['interface']); + // The IP address in the settings can be an IPv4 or IPv6 address associated with the interface + $ipaddr = $settings['ipaddr']; + + // If a specific ip address (VIP) is requested, use it. + // Otherwise, if a specific interface is requested, use it + // If "any" interface was selected, local directive will be omitted. + if (is_ipaddrv4($ipaddr)) { + $iface_ip=$ipaddr; + } else { + if ((!empty($interface)) && (strcmp($interface, "any"))) { + $iface_ip=get_interface_ip($interface); + } + } + if (is_ipaddrv6($ipaddr)) { + $iface_ipv6 = $ipaddr; + } else { + if ((!empty($interface)) && (strcmp($interface, "any"))) { + $iface_ipv6=get_interface_ipv6($interface); + } + } + + + $conf = "dev {$devname}\n"; + if (isset($settings['verbosity_level'])) { + $conf .= "verb {$settings['verbosity_level']}\n"; + } + + $conf .= "dev-type {$settings['dev_mode']}\n"; + switch ($settings['dev_mode']) { + case "tun": + if (!$settings['no_tun_ipv6']) { + $conf .= "tun-ipv6\n"; + } + break; + } + $conf .= "dev-node /dev/{$tunname}\n"; + $conf .= "writepid {$pfile}\n"; + $conf .= "#user nobody\n"; + $conf .= "#group nobody\n"; + $conf .= "script-security 3\n"; + $conf .= "daemon\n"; + $conf .= "keepalive 10 60\n"; + $conf .= "ping-timer-rem\n"; + $conf .= "persist-tun\n"; + $conf .= "persist-key\n"; + $conf .= "proto {$proto}\n"; + $conf .= "cipher {$cipher}\n"; + $conf .= "auth {$digest}\n"; + $conf .= "up /usr/local/sbin/ovpn-linkup\n"; + $conf .= "down /usr/local/sbin/ovpn-linkdown\n"; + if (file_exists("/usr/local/sbin/openvpn.attributes.sh")) { + switch ($settings['mode']) { + case 'server_user': + case 'server_tls_user': + $conf .= "client-connect /usr/local/sbin/openvpn.attributes.sh\n"; + $conf .= "client-disconnect /usr/local/sbin/openvpn.attributes.sh\n"; + break; + } + } + + /* Determine the local IP to use - and make sure it matches with the selected protocol. */ + if (is_ipaddrv4($iface_ip) && (stristr($settings['protocol'], "6") === false)) { + $conf .= "local {$iface_ip}\n"; + } elseif (is_ipaddrv6($iface_ipv6) && (stristr($settings['protocol'], "6") !== false)) { + $conf .= "local {$iface_ipv6}\n"; + } + + if (openvpn_validate_engine($settings['engine']) && ($settings['engine'] != "none")) { + $conf .= "engine {$settings['engine']}\n"; + } + + // server specific settings + if ($mode == 'server') { + + list($ip, $cidr) = explode('/', $settings['tunnel_network']); + list($ipv6, $prefix) = explode('/', $settings['tunnel_networkv6']); + $mask = gen_subnet_mask($cidr); + + // configure tls modes + switch ($settings['mode']) { + case 'p2p_tls': + case 'server_tls': + case 'server_user': + case 'server_tls_user': + $conf .= "tls-server\n"; + break; + } + + // configure p2p/server modes + switch ($settings['mode']) { + case 'p2p_tls': + // If the CIDR is less than a /30, OpenVPN will complain if you try to + // use the server directive. It works for a single client without it. + // See ticket #1417 + if (!empty($ip) && !empty($mask) && ($cidr < 30)) { + $conf .= "server {$ip} {$mask}\n"; + $conf .= "client-config-dir {$g['varetc_path']}/openvpn-csc\n"; + if (is_ipaddr($ipv6)) { + $conf .= "server-ipv6 {$ipv6}/{$prefix}\n"; + } + } + case 'p2p_shared_key': + if (!empty($ip) && !empty($mask)) { + list($ip1, $ip2) = openvpn_get_interface_ip($ip, $mask); + if ($settings['dev_mode'] == 'tun') { + $conf .= "ifconfig {$ip1} {$ip2}\n"; + } else { + $conf .= "ifconfig {$ip1} {$mask}\n"; + } + } + if (!empty($ipv6) && !empty($prefix)) { + list($ipv6_1, $ipv6_2) = openvpn_get_interface_ipv6($ipv6, $prefix); + if ($settings['dev_mode'] == 'tun') { + $conf .= "ifconfig-ipv6 {$ipv6_1} {$ipv6_2}\n"; + } else { + $conf .= "ifconfig-ipv6 {$ipv6_1} {$prefix}\n"; + } + } + break; + case 'server_tls': + case 'server_user': + case 'server_tls_user': + if (!empty($ip) && !empty($mask)) { + $conf .= "server {$ip} {$mask}\n"; + if (is_ipaddr($ipv6)) { + $conf .= "server-ipv6 {$ipv6}/{$prefix}\n"; + } + $conf .= "client-config-dir {$g['varetc_path']}/openvpn-csc\n"; + } else { + if ($settings['serverbridge_dhcp']) { + if ((!empty($settings['serverbridge_interface'])) && (strcmp($settings['serverbridge_interface'], "none"))) { + $biface_ip=get_interface_ip($settings['serverbridge_interface']); + $biface_sm=gen_subnet_mask(get_interface_subnet($settings['serverbridge_interface'])); + if (is_ipaddrv4($biface_ip) && is_ipaddrv4($settings['serverbridge_dhcp_start']) && is_ipaddrv4($settings['serverbridge_dhcp_end'])) { + $conf .= "server-bridge {$biface_ip} {$biface_sm} {$settings['serverbridge_dhcp_start']} {$settings['serverbridge_dhcp_end']}\n"; + $conf .= "client-config-dir {$g['varetc_path']}/openvpn-csc\n"; + } else { + $conf .= "mode server\n"; + } + } else { + $conf .= "mode server\n"; + } + } + } + break; + } + + // configure user auth modes + switch ($settings['mode']) { + case 'server_user': + $conf .= "client-cert-not-required\n"; + case 'server_tls_user': + /* username-as-common-name is not compatible with server-bridge */ + if (stristr($conf, "server-bridge") === false) { + $conf .= "username-as-common-name\n"; + } + if (!empty($settings['authmode'])) { + $strictusercn = "false"; + if ($settings['strictusercn']) { + $strictusercn = "true"; + } + $conf .= "auth-user-pass-verify \"/usr/local/sbin/ovpn_auth_verify user '{$settings['authmode']}' {$strictusercn} {$mode_id}\" via-env\n"; + } + break; + } + if (!isset($settings['cert_depth']) && (strstr($settings['mode'], 'tls'))) { + $settings['cert_depth'] = 1; + } + if (is_numeric($settings['cert_depth'])) { + if (($mode == 'client') && empty($settings['certref'])) { + $cert = ""; + } else { + $cert = lookup_cert($settings['certref']); + /* XXX: Seems not used at all! */ + $servercn = urlencode(cert_get_cn($cert['crt'])); + $conf .= "tls-verify \"/usr/local/sbin/ovpn_auth_verify tls '{$servercn}' {$settings['cert_depth']}\"\n"; + } + } + + // The local port to listen on + $conf .= "lport {$settings['local_port']}\n"; + + // The management port to listen on + // Use unix socket to overcome the problem on any type of server + $conf .= "management {$g['varetc_path']}/openvpn/{$mode_id}.sock unix\n"; + //$conf .= "management 127.0.0.1 {$settings['local_port']}\n"; + + if ($settings['maxclients']) { + $conf .= "max-clients {$settings['maxclients']}\n"; + } + + // Can we push routes + if ($settings['local_network']) { + $conf .= openvpn_gen_routes($settings['local_network'], "ipv4", true); + } + if ($settings['local_networkv6']) { + $conf .= openvpn_gen_routes($settings['local_networkv6'], "ipv6", true); + } + + switch ($settings['mode']) { + case 'server_tls': + case 'server_user': + case 'server_tls_user': + // Configure client dhcp options + openvpn_add_dhcpopts($settings, $conf); + if ($settings['client2client']) { + $conf .= "client-to-client\n"; + } + break; + } + if (isset($settings['duplicate_cn'])) { + $conf .= "duplicate-cn\n"; + } + } + + // client specific settings + + if ($mode == 'client') { + + // configure p2p mode + switch ($settings['mode']) { + case 'p2p_tls': + $conf .= "tls-client\n"; + case 'shared_key': + $conf .= "client\n"; + break; + } + + // If there is no bind option at all (ip and/or port), add "nobind" directive + // Otherwise, use the local port if defined, failing that, use lport 0 to + // ensure a random source port. + if ((empty($iface_ip)) && (!$settings['local_port'])) { + $conf .= "nobind\n"; + } elseif ($settings['local_port']) { + $conf .= "lport {$settings['local_port']}\n"; + } else { + $conf .= "lport 0\n"; + } + + // Use unix socket to overcome the problem on any type of server + $conf .= "management {$g['varetc_path']}/openvpn/{$mode_id}.sock unix\n"; + + // The remote server + $conf .= "remote {$settings['server_addr']} {$settings['server_port']}\n"; + + if (!empty($settings['use_shaper'])) { + $conf .= "shaper {$settings['use_shaper']}\n"; + } + + if (!empty($settings['tunnel_network'])) { + list($ip, $mask) = explode('/', $settings['tunnel_network']); + $mask = gen_subnet_mask($mask); + list($ip1, $ip2) = openvpn_get_interface_ip($ip, $mask); + if ($settings['dev_mode'] == 'tun') { + $conf .= "ifconfig {$ip2} {$ip1}\n"; + } else { + $conf .= "ifconfig {$ip2} {$mask}\n"; + } + } + + if (!empty($settings['tunnel_networkv6'])) { + list($ipv6, $prefix) = explode('/', $settings['tunnel_networkv6']); + list($ipv6_1, $ipv6_2) = openvpn_get_interface_ipv6($ipv6, $prefix); + if ($settings['dev_mode'] == 'tun') { + $conf .= "ifconfig-ipv6 {$ipv6_2} {$ipv6_1}\n"; + } else { + $conf .= "ifconfig-ipv6 {$ipv6_2} {$prefix}\n"; + } + } + + if ($settings['auth_user'] || $settings['auth_pass']) { + $up_file = "{$g['varetc_path']}/openvpn/{$mode_id}.up"; + $conf .= "auth-user-pass {$up_file}\n"; + if ($settings['auth_user']) { + $userpass = "{$settings['auth_user']}\n"; + } else { + $userpass = ""; + } + if ($settings['auth_pass']) { + $userpass .= "{$settings['auth_pass']}\n"; + } + // If only auth_pass is given, then it acts like a user name and we put a blank line where pass would normally go. + if (!($settings['auth_user'] && $settings['auth_pass'])) { + $userpass .= "\n"; + } + file_put_contents($up_file, $userpass); + } + + if ($settings['proxy_addr']) { + $conf .= "http-proxy {$settings['proxy_addr']} {$settings['proxy_port']}"; + if ($settings['proxy_authtype'] != "none") { + $conf .= " {$g['varetc_path']}/openvpn/{$mode_id}.pas {$settings['proxy_authtype']}"; + $proxypas = "{$settings['proxy_user']}\n"; + $proxypas .= "{$settings['proxy_passwd']}\n"; + file_put_contents("{$g['varetc_path']}/openvpn/{$mode_id}.pas", $proxypas); + } + $conf .= " \n"; + } + } + + // Add a remote network route if set, and only for p2p modes. + if ((substr($settings['mode'], 0, 3) == "p2p") && (openvpn_validate_cidr($settings['remote_network'], "", true, "ipv4") === FALSE)) { + $conf .= openvpn_gen_routes($settings['remote_network'], "ipv4", false); + } + // Add a remote network route if set, and only for p2p modes. + if ((substr($settings['mode'], 0, 3) == "p2p") && (openvpn_validate_cidr($settings['remote_networkv6'], "", true, "ipv6") === FALSE)) { + $conf .= openvpn_gen_routes($settings['remote_networkv6'], "ipv6", false); + } + + // Write the settings for the keys + switch ($settings['mode']) { + case 'p2p_shared_key': + openvpn_add_keyfile($settings['shared_key'], $conf, $mode_id, "secret"); + break; + case 'p2p_tls': + case 'server_tls': + case 'server_tls_user': + case 'server_user': + $ca = lookup_ca($settings['caref']); + openvpn_add_keyfile($ca['crt'], $conf, $mode_id, "ca"); + + if (!empty($settings['certref'])) { + $cert = lookup_cert($settings['certref']); + openvpn_add_keyfile($cert['crt'], $conf, $mode_id, "cert"); + openvpn_add_keyfile($cert['prv'], $conf, $mode_id, "key"); + } + if ($mode == 'server') { + $conf .= "dh {$g['etc_path']}/dh-parameters.{$settings['dh_length']}\n"; + } + if (!empty($settings['crlref'])) { + $crl = lookup_crl($settings['crlref']); + crl_update($crl); + openvpn_add_keyfile($crl['text'], $conf, $mode_id, "crl-verify"); + } + if ($settings['tls']) { + if ($mode == "server") { + $tlsopt = 0; + } else { + $tlsopt = 1; + } + openvpn_add_keyfile($settings['tls'], $conf, $mode_id, "tls-auth", $tlsopt); + } + break; + } + + if (!empty($settings['compression'])) { + $conf .= "comp-lzo {$settings['compression']}\n"; + } + + if ($settings['passtos']) { + $conf .= "passtos\n"; + } + + if ($settings['resolve_retry']) { + $conf .= "resolv-retry infinite\n"; + } else if ($mode == 'clie} nt') { + $conf .= "resolv-retry infinite\n"; + } + + if ($settings['dynamic_ip']) { + $conf .= "persist-remote-ip\n"; + $conf .= "float\n"; + } + + if ($settings['topology_subnet']) { + $conf .= "topology subnet\n"; + } + + // New client features + if ($mode == "client") { + // Dont pull routes checkbox + if ($settings['route_no_pull']) { + $conf .= "route-nopull\n"; + } + + // Dont add/remove routes checkbox + if ($settings['route_no_exec']) { + $conf .= "route-noexec\n"; + } + } + + openvpn_add_custom($settings, $conf); + + openvpn_create_dirs(); + $fpath = "{$g['varetc_path']}/openvpn/{$mode_id}.conf"; + file_put_contents($fpath, $conf); + unset($conf); + $fpath = "{$g['varetc_path']}/openvpn/{$mode_id}.interface"; + file_put_contents($fpath, $interface); + //chown($fpath, 'nobody'); + //chgrp($fpath, 'nobody'); + @chmod("{$g['varetc_path']}/openvpn/{$mode_id}.conf", 0600); + @chmod("{$g['varetc_path']}/openvpn/{$mode_id}.interface", 0600); + @chmod("{$g['varetc_path']}/openvpn/{$mode_id}.key", 0600); + @chmod("{$g['varetc_path']}/openvpn/{$mode_id}.tls-auth", 0600); + @chmod("{$g['varetc_path']}/openvpn/{$mode_id}.conf", 0600); +} + +function openvpn_restart($mode, $settings) { + global $g, $config; + + $vpnid = $settings['vpnid']; + $mode_id = $mode.$vpnid; + + /* kill the process if running */ + $pfile = $g['varrun_path']."/openvpn_{$mode_id}.pid"; + if (file_exists($pfile)) { + + /* read the pid file */ + $pid = rtrim(file_get_contents($pfile)); + unlink($pfile); + + /* send a term signal to the process */ + posix_kill($pid, SIGTERM); + + /* wait until the process exits, or timeout and kill it */ + $i = 0; + while (posix_kill($pid, 0)) { + usleep(250000); + if ($i > 10) { + log_error("OpenVPN ID $mode_id PID $pid still running, killing."); + posix_kill($pid, SIGKILL); + usleep(500000); + } + $i++; + } + } + + if (isset($settings['disable'])) { + return; + } + + /* Do not start a client if we are a CARP backup on this vip! */ + if (($mode == "client") && (strstr($settings['interface'], "_vip") && get_carp_interface_status($settings['interface']) != "MASTER")) { + return; + } + + /* Check if client is bound to a gateway group */ + $a_groups = return_gateway_groups_array(); + if (is_array($a_groups[$settings['interface']])) { + /* the interface is a gateway group. If a vip is defined and its a CARP backup then do not start */ + if (($a_groups[$settings['interface']][0]['vip'] <> "") && (get_carp_interface_status($a_groups[$settings['interface']][0]['vip']) != "MASTER")) { + return; + } + } + + /* start the new process */ + $fpath = $g['varetc_path']."/openvpn/{$mode_id}.conf"; + openvpn_clear_route($mode, $settings); + mwexec_bg("/usr/local/sbin/openvpn --config " . escapeshellarg($fpath)); + + if (!platform_booting()) { + send_event("filter reload"); + } +} + +function openvpn_delete($mode, & $settings) { + global $g, $config; + + $vpnid = $settings['vpnid']; + $mode_id = $mode.$vpnid; + + if (isset($settings['dev_mode'])) { + $tunname = "{$settings['dev_mode']}{$vpnid}"; + } else { + /* defaults to tun */ + $tunname = "tun{$vpnid}"; + } + + if ($mode == "server") { + $devname = "ovpns{$vpnid}"; + } else { + $devname = "ovpnc{$vpnid}"; + } + + /* kill the process if running */ + $pfile = "{$g['varrun_path']}/openvpn_{$mode_id}.pid"; + if (file_exists($pfile)) { + + /* read the pid file */ + $pid = trim(file_get_contents($pfile)); + unlink($pfile); + + /* send a term signal to the process */ + posix_kill($pid, SIGTERM); + } + + /* remove the device from the openvpn group */ + mwexec("/sbin/ifconfig " . escapeshellarg($devname) . " -group openvpn"); + + /* restore the original adapter name */ + mwexec("/sbin/ifconfig " . escapeshellarg($devname) . " name " . escapeshellarg($tunname)); + + /* remove the configuration files */ + @array_map('unlink', glob("{$g['varetc_path']}/openvpn/{$mode_id}.*")); +} + +function openvpn_cleanup_csc($common_name) { + global $g, $config; + if (empty($common_name)) { + return; + } + $fpath = "{$g['varetc_path']}/openvpn-csc/" . basename($common_name); + if (is_file($fpath)) { + unlink_if_exists($fpath); + } + return; +} + +function openvpn_resync_csc(& $settings) { + global $g, $config; + + $fpath = $g['varetc_path']."/openvpn-csc/".$settings['common_name']; + + if (isset($settings['disable'])) { + unlink_if_exists($fpath); + return; + } + openvpn_create_dirs(); + + $conf = ''; + if ($settings['block']) { + $conf .= "disable\n"; + } + + if ($settings['push_reset']) { + $conf .= "push-reset\n"; + } + + if (!empty($settings['tunnel_network'])) { + list($ip, $mask) = explode('/', $settings['tunnel_network']); + $baselong = ip2long32($ip) & gen_subnet_mask_long($mask); + $serverip = long2ip32($baselong + 1); + $clientip = long2ip32($baselong + 2); + /* Because this is being pushed, the order from the client's point of view. */ + if ($settings['dev_mode'] != 'tap') { + $conf .= "ifconfig-push {$clientip} {$serverip}\n"; + } else { + $conf .= "ifconfig-push {$clientip} {$mask}\n"; + } + } + + if ($settings['local_network']) { + $conf .= openvpn_gen_routes($settings['local_network'], "ipv4", true); + } + if ($settings['local_networkv6']) { + $conf .= openvpn_gen_routes($settings['local_networkv6'], "ipv6", true); + } + + // Add a remote network iroute if set + if (openvpn_validate_cidr($settings['remote_network'], "", true, "ipv4") === FALSE) { + $conf .= openvpn_gen_routes($settings['remote_network'], "ipv4", false, true); + } + // Add a remote network iroute if set + if (openvpn_validate_cidr($settings['remote_networkv6'], "", true, "ipv6") === FALSE) { + $conf .= openvpn_gen_routes($settings['remote_networkv6'], "ipv6", false, true); + } + + openvpn_add_dhcpopts($settings, $conf); + + if ($settings['gwredir']) { + $conf .= "push \"redirect-gateway def1\"\n"; + } + + openvpn_add_custom($settings, $conf); + + file_put_contents($fpath, $conf); + chown($fpath, 'nobody'); + chgrp($fpath, 'nobody'); +} + +function openvpn_delete_csc(& $settings) { + global $g, $config; + + $fpath = $g['varetc_path']."/openvpn-csc/".$settings['common_name']; + unlink_if_exists($fpath); +} + +// Resync the configuration and restart the VPN +function openvpn_resync($mode, $settings) { + openvpn_reconfigure($mode, $settings); + openvpn_restart($mode, $settings); +} + +// Resync and restart all VPNs +function openvpn_resync_all($interface = "") { + global $g, $config; + + openvpn_create_dirs(); + + if (!is_array($config['openvpn'])) { + $config['openvpn'] = array(); + } + +/* + if (!$config['openvpn']['dh-parameters']) { + echo "Configuring OpenVPN Parameters ...\n"; + $dh_parameters = openvpn_create_dhparams(1024); + $dh_parameters = base64_encode($dh_parameters); + $config['openvpn']['dh-parameters'] = $dh_parameters; + write_config("OpenVPN DH parameters"); + } + + $path_ovdh = $g['varetc_path']."/openvpn/dh-parameters"; + if (!file_exists($path_ovdh)) { + $dh_parameters = $config['openvpn']['dh-parameters']; + $dh_parameters = base64_decode($dh_parameters); + file_put_contents($path_ovdh, $dh_parameters); + } +*/ + if ($interface <> "") { + log_error("Resyncing OpenVPN instances for interface " . convert_friendly_interface_to_friendly_descr($interface) . "."); + } else { + log_error("Resyncing OpenVPN instances."); + } + + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as & $settings) { + if ($interface <> "" && $interface != $settings['interface']) { + continue; + } + openvpn_resync('server', $settings); + } + } + + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as & $settings) { + if ($interface <> "" && $interface != $settings['interface']) { + continue; + } + openvpn_resync('client', $settings); + } + } + + if (is_array($config['openvpn']['openvpn-csc'])) { + foreach ($config['openvpn']['openvpn-csc'] as & $settings) { + openvpn_resync_csc($settings); + } + } + +} + +// Resync and restart all VPNs using a gateway group. +function openvpn_resync_gwgroup($gwgroupname = "") { + global $g, $config; + + if ($gwgroupname <> "") { + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as & $settings) { + if ($gwgroupname == $settings['interface']) { + log_error("Resyncing OpenVPN for gateway group " . $gwgroupname . " server " . $settings["description"] . "."); + openvpn_resync('server', $settings); + } + } + } + + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as & $settings) { + if ($gwgroupname == $settings['interface']) { + log_error("Resyncing OpenVPN for gateway group " . $gwgroupname . " client " . $settings["description"] . "."); + openvpn_resync('client', $settings); + } + } + } + + // Note: no need to resysnc Client Specific (csc) here, as changes to the OpenVPN real interface do not effect these. + + } else { + log_error("openvpn_resync_gwgroup called with null gwgroup parameter."); + } +} + +function openvpn_get_active_servers($type="multipoint") { + global $config, $g; + + $servers = array(); + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as & $settings) { + if (empty($settings) || isset($settings['disable'])) { + continue; + } + + $prot = $settings['protocol']; + $port = $settings['local_port']; + + $server = array(); + $server['port'] = ($settings['local_port']) ? $settings['local_port'] : 1194; + $server['mode'] = $settings['mode']; + if ($settings['description']) { + $server['name'] = "{$settings['description']} {$prot}:{$port}"; + } else { + $server['name'] = "Server {$prot}:{$port}"; + } + $server['conns'] = array(); + $server['vpnid'] = $settings['vpnid']; + $server['mgmt'] = "server{$server['vpnid']}"; + $socket = "unix://{$g['varetc_path']}/openvpn/{$server['mgmt']}.sock"; + list($tn, $sm) = explode('/', $settings['tunnel_network']); + + if ((($server['mode'] == "p2p_shared_key") || ($sm >= 30)) && ($type == "p2p")) { + $servers[] = openvpn_get_client_status($server, $socket); + } elseif (($server['mode'] != "p2p_shared_key") && ($type == "multipoint") && ($sm < 30)) { + $servers[] = openvpn_get_server_status($server, $socket); + } + } + } + return $servers; +} + +function openvpn_get_server_status($server, $socket) { + $errval = null; + $errstr = null; + $fp = @stream_socket_client($socket, $errval, $errstr, 1); + if ($fp) { + stream_set_timeout($fp, 1); + + /* send our status request */ + fputs($fp, "status 2\n"); + + /* recv all response lines */ + while (!feof($fp)) { + + /* read the next line */ + $line = fgets($fp, 1024); + + $info = stream_get_meta_data($fp); + if ($info['timed_out']) { + break; + } + + /* parse header list line */ + if (strstr($line, "HEADER")) { + continue; + } + + /* parse end of output line */ + if (strstr($line, "END") || strstr($line, "ERROR")) { + break; + } + + /* parse client list line */ + if (strstr($line, "CLIENT_LIST")) { + $list = explode(",", $line); + $conn = array(); + $conn['common_name'] = $list[1]; + $conn['remote_host'] = $list[2]; + $conn['virtual_addr'] = $list[3]; + $conn['bytes_recv'] = $list[4]; + $conn['bytes_sent'] = $list[5]; + $conn['connect_time'] = $list[6]; + $server['conns'][] = $conn; + } + /* parse routing table lines */ + if (strstr($line, "ROUTING_TABLE")) { + $list = explode(",", $line); + $conn = array(); + $conn['virtual_addr'] = $list[1]; + $conn['common_name'] = $list[2]; + $conn['remote_host'] = $list[3]; + $conn['last_time'] = $list[4]; + $server['routes'][] = $conn; + } + } + + /* cleanup */ + fclose($fp); + } else { + $conn = array(); + $conn['common_name'] = "[error]"; + $conn['remote_host'] = "Unable to contact daemon"; + $conn['virtual_addr'] = "Service not running?"; + $conn['bytes_recv'] = 0; + $conn['bytes_sent'] = 0; + $conn['connect_time'] = 0; + $server['conns'][] = $conn; + } + return $server; +} + +function openvpn_get_active_clients() { + global $config, $g; + + $clients = array(); + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as & $settings) { + + if (empty($settings) || isset($settings['disable'])) { + continue; + } + + $prot = $settings['protocol']; + $port = ($settings['local_port']) ? ":{$settings['local_port']}" : ""; + + $client = array(); + $client['port'] = $settings['local_port']; + if ($settings['description']) { + $client['name'] = "{$settings['description']} {$prot}{$port}"; + } else { + $client['name'] = "Client {$prot}{$port}"; + } + + $client['vpnid'] = $settings['vpnid']; + $client['mgmt'] = "client{$client['vpnid']}"; + $socket = "unix://{$g['varetc_path']}/openvpn/{$client['mgmt']}.sock"; + $client['status']="down"; + + $clients[] = openvpn_get_client_status($client, $socket); + } + } + return $clients; +} + +function openvpn_get_client_status($client, $socket) { + $errval = null; + $errstr = null; + $fp = @stream_socket_client($socket, $errval, $errstr, 1); + if ($fp) { + stream_set_timeout($fp, 1); + /* send our status request */ + fputs($fp, "state 1\n"); + + /* recv all response lines */ + while (!feof($fp)) { + /* read the next line */ + $line = fgets($fp, 1024); + + $info = stream_get_meta_data($fp); + if ($info['timed_out']) { + break; + } + + /* Get the client state */ + if (strstr($line,"CONNECTED")) { + $client['status']="up"; + $list = explode(",", $line); + + $client['connect_time'] = date("D M j G:i:s Y", $list[0]); + $client['virtual_addr'] = $list[3]; + $client['remote_host'] = $list[4]; + } + if (strstr($line,"CONNECTING")) { + $client['status']="connecting"; + } + if (strstr($line,"ASSIGN_IP")) { + $client['status']="waiting"; + $list = explode(",", $line); + + $client['connect_time'] = date("D M j G:i:s Y", $list[0]); + $client['virtual_addr'] = $list[3]; + } + if (strstr($line,"RECONNECTING")) { + $client['status']="reconnecting"; + $list = explode(",", $line); + + $client['connect_time'] = date("D M j G:i:s Y", $list[0]); + $client['status'] .= "; " . $list[2]; + } + /* parse end of output line */ + if (strstr($line, "END") || strstr($line, "ERROR")) { + break; + } + } + + /* If up, get read/write stats */ + if (strcmp($client['status'], "up") == 0) { + fputs($fp, "status 2\n"); + /* recv all response lines */ + while (!feof($fp)) { + /* read the next line */ + $line = fgets($fp, 1024); + + $info = stream_get_meta_data($fp); + if ($info['timed_out']) { + break; + } + + if (strstr($line,"TCP/UDP read bytes")) { + $list = explode(",", $line); + $client['bytes_recv'] = $list[1]; + } + + if (strstr($line,"TCP/UDP write bytes")) { + $list = explode(",", $line); + $client['bytes_sent'] = $list[1]; + } + + /* parse end of output line */ + if (strstr($line, "END")) { + break; + } + } + } + + fclose($fp); + + } else { + $DisplayNote=true; + $client['remote_host'] = "Unable to contact daemon"; + $client['virtual_addr'] = "Service not running?"; + $client['bytes_recv'] = 0; + $client['bytes_sent'] = 0; + $client['connect_time'] = 0; + } + return $client; +} + +function openvpn_refresh_crls() { + global $g, $config; + + openvpn_create_dirs(); + + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as $settings) { + if (empty($settings)) { + continue; + } + if (isset($settings['disable'])) { + continue; + } + // Write the settings for the keys + switch ($settings['mode']) { + case 'p2p_tls': + case 'server_tls': + case 'server_tls_user': + case 'server_user': + if (!empty($settings['crlref'])) { + $crl = lookup_crl($settings['crlref']); + crl_update($crl); + $fpath = $g['varetc_path']."/openvpn/server{$settings['vpnid']}.crl-verify"; + file_put_contents($fpath, base64_decode($crl['text'])); + @chmod($fpath, 0644); + } + break; + } + } + } +} + +function openvpn_create_dirs() { + global $g; + if (!is_dir("{$g['varetc_path']}/openvpn")) { + safe_mkdir("{$g['varetc_path']}/openvpn", 0750); + } + if (!is_dir("{$g['varetc_path']}/openvpn-csc")) { + safe_mkdir("{$g['varetc_path']}/openvpn-csc", 0750); + } +} + +function openvpn_get_interface_ip($ip, $mask) { + $baselong = ip2long32($ip) & ip2long($mask); + $ip1 = long2ip32($baselong + 1); + $ip2 = long2ip32($baselong + 2); + return array($ip1, $ip2); +} + +function openvpn_get_interface_ipv6($ipv6, $prefix) { + $basev6 = gen_subnetv6($ipv6, $prefix); + // Is there a better way to do this math? + $ipv6_arr = explode(':', $basev6); + $last = hexdec(array_pop($ipv6_arr)); + $ipv6_1 = Net_IPv6::compress(Net_IPv6::uncompress(implode(':', $ipv6_arr) . ':' . dechex($last + 1))); + $ipv6_2 = Net_IPv6::compress(Net_IPv6::uncompress(implode(':', $ipv6_arr) . ':' . dechex($last + 2))); + return array($ipv6_1, $ipv6_2); +} + +function openvpn_clear_route($mode, $settings) { + if (empty($settings['tunnel_network'])) { + return; + } + list($ip, $cidr) = explode('/', $settings['tunnel_network']); + $mask = gen_subnet_mask($cidr); + $clear_route = false; + + switch ($settings['mode']) { + case 'shared_key': + $clear_route = true; + break; + case 'p2p_tls': + case 'p2p_shared_key': + if ($cidr == 30) { + $clear_route = true; + } + break; + } + + if ($clear_route && !empty($ip) && !empty($mask)) { + list($ip1, $ip2) = openvpn_get_interface_ip($ip, $mask); + $ip_to_clear = ($mode == "server") ? $ip1 : $ip2; + /* XXX: Family for route? */ + mwexec("/sbin/route -q delete {$ip_to_clear}"); + } +} + +function openvpn_gen_routes($value, $ipproto = "ipv4", $push = false, $iroute = false) { + $routes = ""; + if (empty($value)) { + return ""; + } + $networks = explode(',', $value); + + foreach ($networks as $network) { + if ($ipproto == "ipv4") { + $route = openvpn_gen_route_ipv4($network, $iroute); + } else { + $route = openvpn_gen_route_ipv6($network, $iroute); + } + + if ($push) { + $routes .= "push \"{$route}\"\n"; + } else { + $routes .= "{$route}\n"; + } + } + return $routes; +} + +function openvpn_gen_route_ipv4($network, $iroute = false) { + $i = ($iroute) ? "i" : ""; + list($ip, $mask) = explode('/', trim($network)); + $mask = gen_subnet_mask($mask); + return "{$i}route $ip $mask"; +} + +function openvpn_gen_route_ipv6($network, $iroute = false) { + $i = ($iroute) ? "i" : ""; + list($ipv6, $prefix) = explode('/', trim($network)); + if (empty($prefix)) { + $prefix = "128"; + } + return "{$i}route-ipv6 ${ipv6}/${prefix}"; +} + +function openvpn_get_settings($mode, $vpnid) { + global $config; + + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as $settings) { + if (isset($settings['disable'])) { + continue; + } + + if ($vpnid != 0 && $vpnid == $settings['vpnid']) { + return $settings; + } + } + } + + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as $settings) { + if (isset($settings['disable'])) { + continue; + } + + if ($vpnid != 0 && $vpnid == $settings['vpnid']) { + return $settings; + } + } + } + + return array(); +} + +function openvpn_restart_by_vpnid($mode, $vpnid) { + $settings = openvpn_get_settings($mode, $vpnid); + openvpn_restart($mode, $settings); +} + +?> diff --git a/src/etc/inc/openvpn.tls-verify.php b/src/etc/inc/openvpn.tls-verify.php new file mode 100644 index 0000000..9e21342 --- /dev/null +++ b/src/etc/inc/openvpn.tls-verify.php @@ -0,0 +1,97 @@ +#!/usr/local/bin/php-cgi -f +<?php +/* $Id$ */ +/* + openvpn.tls-verify.php + + Copyright (C) 2011 Jim Pingle + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ +/* + pfSense_BUILDER_BINARIES: + pfSense_MODULE: openvpn +*/ +/* + * OpenVPN calls this script to validate a certificate + * This script is called ONCE per DEPTH of the certificate chain + * Normal operation would have two runs - one for the server certificate + * and one for the client certificate. Beyond that, you're dealing with + * intermediates. + */ + +require_once("globals.inc"); +require_once("config.inc"); +require_once("interfaces.inc"); + +openlog("openvpn", LOG_ODELAY, LOG_AUTH); + +/* read data from command line */ +if (isset($_GET['certdepth'])) { + $cert_depth = $_GET['certdepth']; + $cert_subject = urldecode($_GET['certsubject']); + $allowed_depth = $_GET['depth']; + $server_cn = $_GET['servercn']; +} else { + $cert_depth = intval($argv[1]); + $cert_subject = $argv[2]; +} + +/* Reserved for future use in case we decide to verify CNs and such as well +$subj = explode("/", $cert_subject); +foreach ($subj at $s) { + list($n, $v) = explode("=", $s); + if ($n == "CN") { + $common_name = $v; + } +} +*/ + +/* Replaced by sed with proper variables used below ( $server_cn and $allowed_depth ). */ +//<template> + +if (isset($allowed_depth) && ($cert_depth > $allowed_depth)) { + syslog(LOG_WARNING, "Certificate depth {$cert_depth} exceeded max allowed depth of {$allowed_depth}.\n"); + if (isset($_GET['certdepth'])) { + echo "FAILED"; + closelog(); + return; + } else { + closelog(); + exit(1); + } +} + +// Debug +//syslog(LOG_WARNING, "Found certificate {$argv[2]} with depth {$cert_depth}\n"); + +closelog(); +if (isset($_GET['certdepth'])) { + echo "OK"; +} else { + exit(0); +} + +?> diff --git a/src/etc/inc/pfsense-utils.inc b/src/etc/inc/pfsense-utils.inc new file mode 100644 index 0000000..2cedc52 --- /dev/null +++ b/src/etc/inc/pfsense-utils.inc @@ -0,0 +1,3204 @@ +<?php +/****h* pfSense/pfsense-utils + NAME + pfsense-utils.inc - Utilities specific to pfSense + DESCRIPTION + This include contains various pfSense specific functions. + HISTORY + $Id$ + + Copyright (C) 2004-2007 Scott Ullrich (sullrich@gmail.com) + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig /sbin/pfctl /usr/local/bin/php-cgi /usr/bin/netstat + pfSense_BUILDER_BINARIES: /bin/df /usr/bin/grep /usr/bin/awk /bin/rm /usr/sbin/pwd_mkdb /usr/bin/host + pfSense_BUILDER_BINARIES: /sbin/kldload + pfSense_MODULE: utils +*/ + +/****f* pfsense-utils/have_natpfruleint_access + * NAME + * have_natpfruleint_access + * INPUTS + * none + * RESULT + * returns true if user has access to edit a specific firewall nat port forward interface + ******/ +function have_natpfruleint_access($if) { + $security_url = "firewall_nat_edit.php?if=". strtolower($if); + if (isAllowedPage($security_url, $allowed)) { + return true; + } + return false; +} + +/****f* pfsense-utils/have_ruleint_access + * NAME + * have_ruleint_access + * INPUTS + * none + * RESULT + * returns true if user has access to edit a specific firewall interface + ******/ +function have_ruleint_access($if) { + $security_url = "firewall_rules.php?if=". strtolower($if); + if (isAllowedPage($security_url)) { + return true; + } + return false; +} + +/****f* pfsense-utils/does_url_exist + * NAME + * does_url_exist + * INPUTS + * none + * RESULT + * returns true if a url is available + ******/ +function does_url_exist($url) { + $fd = fopen("$url","r"); + if ($fd) { + fclose($fd); + return true; + } else { + return false; + } +} + +/****f* pfsense-utils/is_private_ip + * NAME + * is_private_ip + * INPUTS + * none + * RESULT + * returns true if an ip address is in a private range + ******/ +function is_private_ip($iptocheck) { + $isprivate = false; + $ip_private_list=array( + "10.0.0.0/8", + "100.64.0.0/10", + "172.16.0.0/12", + "192.168.0.0/16", + ); + foreach ($ip_private_list as $private) { + if (ip_in_subnet($iptocheck,$private)==true) { + $isprivate = true; + } + } + return $isprivate; +} + +/****f* pfsense-utils/get_tmp_file + * NAME + * get_tmp_file + * INPUTS + * none + * RESULT + * returns a temporary filename + ******/ +function get_tmp_file() { + global $g; + return "{$g['tmp_path']}/tmp-" . time(); +} + +/****f* pfsense-utils/get_dns_servers + * NAME + * get_dns_servers - get system dns servers + * INPUTS + * none + * RESULT + * $dns_servers - an array of the dns servers + ******/ +function get_dns_servers() { + $dns_servers = array(); + if (file_exists("/etc/resolv.conf")) { + $dns_s = file("/etc/resolv.conf", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + } + if (is_array($dns_s)) { + foreach ($dns_s as $dns) { + $matches = ""; + if (preg_match("/nameserver (.*)/", $dns, $matches)) { + $dns_servers[] = $matches[1]; + } + } + } + return array_unique($dns_servers); +} + +function hardware_offloading_applyflags($iface) { + global $config; + + $flags_on = 0; + $flags_off = 0; + $options = pfSense_get_interface_addresses($iface); + + if (isset($config['system']['disablechecksumoffloading'])) { + if (isset($options['encaps']['txcsum'])) { + $flags_off |= IFCAP_TXCSUM; + } + if (isset($options['encaps']['rxcsum'])) { + $flags_off |= IFCAP_RXCSUM; + } + } else { + if (isset($options['caps']['txcsum'])) { + $flags_on |= IFCAP_TXCSUM; + } + if (isset($options['caps']['rxcsum'])) { + $flags_on |= IFCAP_RXCSUM; + } + } + + if (isset($config['system']['disablesegmentationoffloading'])) { + $flags_off |= IFCAP_TSO; + } else if (isset($options['caps']['tso']) || isset($options['caps']['tso4']) || isset($options['caps']['tso6'])) { + $flags_on |= IFCAP_TSO; + } + + if (isset($config['system']['disablelargereceiveoffloading'])) { + $flags_off |= IFCAP_LRO; + } else if (isset($options['caps']['lro'])) { + $flags_on |= IFCAP_LRO; + } + + /* if the NIC supports polling *AND* it is enabled in the GUI */ + if (!isset($config['system']['polling'])) { + $flags_off |= IFCAP_POLLING; + } else if (isset($options['caps']['polling'])) { + $flags_on |= IFCAP_POLLING; + } + + pfSense_interface_capabilities($iface, -$flags_off); + pfSense_interface_capabilities($iface, $flags_on); +} + +/****f* pfsense-utils/enable_hardware_offloading + * NAME + * enable_hardware_offloading - Enable a NIC's supported hardware features. + * INPUTS + * $interface - string containing the physical interface to work on. + * RESULT + * null + * NOTES + * This function only supports the fxp driver's loadable microcode. + ******/ +function enable_hardware_offloading($interface) { + global $g, $config; + + $int = get_real_interface($interface); + if (empty($int)) { + return; + } + + if (!isset($config['system']['do_not_use_nic_microcode'])) { + /* translate wan, lan, opt -> real interface if needed */ + $int_family = preg_split("/[0-9]+/", $int); + $supported_ints = array('fxp'); + if (in_array($int_family, $supported_ints)) { + if (does_interface_exist($int)) { + pfSense_interface_flags($int, IFF_LINK0); + } + } + } + + /* This is mostly for vlans and ppp types */ + $realhwif = get_parent_interface($interface); + if ($realhwif[0] == $int) { + hardware_offloading_applyflags($int); + } else { + hardware_offloading_applyflags($realhwif[0]); + hardware_offloading_applyflags($int); + } +} + +/****f* pfsense-utils/interface_supports_polling + * NAME + * checks to see if an interface supports polling according to man polling + * INPUTS + * + * RESULT + * true or false + * NOTES + * + ******/ +function interface_supports_polling($iface) { + $opts = pfSense_get_interface_addresses($iface); + if (is_array($opts) && isset($opts['caps']['polling'])) { + return true; + } + + return false; +} + +/****f* pfsense-utils/is_alias_inuse + * NAME + * checks to see if an alias is currently in use by a rule + * INPUTS + * + * RESULT + * true or false + * NOTES + * + ******/ +function is_alias_inuse($alias) { + global $g, $config; + + if ($alias == "") { + return false; + } + /* loop through firewall rules looking for alias in use */ + if (is_array($config['filter']['rule'])) { + foreach ($config['filter']['rule'] as $rule) { + if ($rule['source']['address']) { + if ($rule['source']['address'] == $alias) { + return true; + } + } + if ($rule['destination']['address']) { + if ($rule['destination']['address'] == $alias) { + return true; + } + } + } + } + /* loop through nat rules looking for alias in use */ + if (is_array($config['nat']['rule'])) { + foreach ($config['nat']['rule'] as $rule) { + if ($rule['target'] && $rule['target'] == $alias) { + return true; + } + if ($rule['source']['address'] && $rule['source']['address'] == $alias) { + return true; + } + if ($rule['destination']['address'] && $rule['destination']['address'] == $alias) { + return true; + } + } + } + return false; +} + +/****f* pfsense-utils/is_schedule_inuse + * NAME + * checks to see if a schedule is currently in use by a rule + * INPUTS + * + * RESULT + * true or false + * NOTES + * + ******/ +function is_schedule_inuse($schedule) { + global $g, $config; + + if ($schedule == "") { + return false; + } + /* loop through firewall rules looking for schedule in use */ + if (is_array($config['filter']['rule'])) { + foreach ($config['filter']['rule'] as $rule) { + if ($rule['sched'] == $schedule) { + return true; + } + } + } + return false; +} + +/****f* pfsense-utils/setup_polling + * NAME + * sets up polling + * INPUTS + * + * RESULT + * null + * NOTES + * + ******/ +function setup_polling() { + global $g, $config; + + if (isset($config['system']['polling'])) { + set_single_sysctl("kern.polling.idle_poll", "1"); + } else { + set_single_sysctl("kern.polling.idle_poll", "0"); + } + + if ($config['system']['polling_each_burst']) { + set_single_sysctl("kern.polling.each_burst", $config['system']['polling_each_burst']); + } + if ($config['system']['polling_burst_max']) { + set_single_sysctl("kern.polling.burst_max", $config['system']['polling_burst_max']); + } + if ($config['system']['polling_user_frac']) { + set_single_sysctl("kern.polling.user_frac", $config['system']['polling_user_frac']); + } +} + +/****f* pfsense-utils/setup_microcode + * NAME + * enumerates all interfaces and calls enable_hardware_offloading which + * enables a NIC's supported hardware features. + * INPUTS + * + * RESULT + * null + * NOTES + * This function only supports the fxp driver's loadable microcode. + ******/ +function setup_microcode() { + + /* if list */ + $iflist = get_configured_interface_list(false, true); + foreach ($iflist as $if => $ifdescr) { + enable_hardware_offloading($if); + } + unset($iflist); +} + +/****f* pfsense-utils/get_carp_status + * NAME + * get_carp_status - Return whether CARP is enabled or disabled. + * RESULT + * boolean - true if CARP is enabled, false if otherwise. + ******/ +function get_carp_status() { + /* grab the current status of carp */ + $status = get_single_sysctl('net.inet.carp.allow'); + return (intval($status) > 0); +} + +/* + * convert_ip_to_network_format($ip, $subnet): converts an ip address to network form + + */ +function convert_ip_to_network_format($ip, $subnet) { + $ipsplit = explode('.', $ip); + $string = $ipsplit[0] . "." . $ipsplit[1] . "." . $ipsplit[2] . ".0/" . $subnet; + return $string; +} + +/* + * get_carp_interface_status($carpinterface): returns the status of a carp ip + */ +function get_carp_interface_status($carpinterface) { + + $interface = get_real_interface($interface); + $carp_query = ''; + $_gb = exec("/sbin/ifconfig $interface | /usr/bin/grep -v grep | /usr/bin/grep carp: | /usr/bin/head -n 1", $carp_query); + foreach ($carp_query as $int) { + if (stripos($int, "MASTER")) { + return "MASTER"; + } + if (stripos($int, "BACKUP")) { + return "BACKUP"; + } + if (stripos($int, "INIT")) { + return "INIT"; + } + } + return; +} + +/* + * get_pfsync_interface_status($pfsyncinterface): returns the status of a pfsync + */ +function get_pfsync_interface_status($pfsyncinterface) { + if (!does_interface_exist($pfsyncinterface)) { + return; + } + + return exec_command("/sbin/ifconfig {$pfsyncinterface} | /usr/bin/awk '/pfsync:/ {print \$5}'"); +} + +/* + * add_rule_to_anchor($anchor, $rule): adds the specified rule to an anchor + */ +function add_rule_to_anchor($anchor, $rule, $label) { + mwexec("echo " . escapeshellarg($rule) . " | /sbin/pfctl -a " . escapeshellarg($anchor) . ":" . escapeshellarg($label) . " -f -"); +} + +/* + * remove_text_from_file + * remove $text from file $file + */ +function remove_text_from_file($file, $text) { + if (!file_exists($file) && !is_writable($file)) { + return; + } + $filecontents = file_get_contents($file); + $text = str_replace($text, "", $filecontents); + @file_put_contents($file, $text); +} + +/* + * after_sync_bump_adv_skew(): create skew values by 1S + */ +function after_sync_bump_adv_skew() { + global $config, $g; + $processed_skew = 1; + $a_vip = &$config['virtualip']['vip']; + foreach ($a_vip as $vipent) { + if ($vipent['advskew'] <> "") { + $processed_skew = 1; + $vipent['advskew'] = $vipent['advskew']+1; + } + } + if ($processed_skew == 1) { + write_config(gettext("After synch increase advertising skew")); + } +} + +/* + * get_filename_from_url($url): converts a url to its filename. + */ +function get_filename_from_url($url) { + return basename($url); +} + +/* + * get_dir: return an array of $dir + */ +function get_dir($dir) { + $dir_array = array(); + $d = dir($dir); + while (false !== ($entry = $d->read())) { + array_push($dir_array, $entry); + } + $d->close(); + return $dir_array; +} + +/****f* pfsense-utils/WakeOnLan + * NAME + * WakeOnLan - Wake a machine up using the wake on lan format/protocol + * RESULT + * true/false - true if the operation was successful + ******/ +function WakeOnLan($addr, $mac) { + $addr_byte = explode(':', $mac); + $hw_addr = ''; + + for ($a=0; $a < 6; $a++) { + $hw_addr .= chr(hexdec($addr_byte[$a])); + } + + $msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255); + + for ($a = 1; $a <= 16; $a++) { + $msg .= $hw_addr; + } + + // send it to the broadcast address using UDP + $s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + if ($s == false) { + log_error(gettext("Error creating socket!")); + log_error(sprintf(gettext("Error code is '%1\$s' - %2\$s"), socket_last_error($s), socket_strerror(socket_last_error($s)))); + } else { + // setting a broadcast option to socket: + $opt_ret = socket_set_option($s, 1, 6, TRUE); + if ($opt_ret < 0) { + log_error(sprintf(gettext("setsockopt() failed, error: %s"), strerror($opt_ret))); + } + $e = socket_sendto($s, $msg, strlen($msg), 0, $addr, 2050); + socket_close($s); + log_error(sprintf(gettext('Magic Packet sent (%1$s) to {%2$s} MAC=%3$s'), $e, $addr, $mac)); + return true; + } + + return false; +} + +/* + * reverse_strrchr($haystack, $needle): Return everything in $haystack up to the *last* instance of $needle. + * Useful for finding paths and stripping file extensions. + */ +function reverse_strrchr($haystack, $needle) { + if (!is_string($haystack)) { + return; + } + return strrpos($haystack, $needle) ? substr($haystack, 0, strrpos($haystack, $needle) +1) : false; +} + +/* + * backup_config_section($section): returns as an xml file string of + * the configuration section + */ +function backup_config_section($section_name) { + global $config; + $new_section = &$config[$section_name]; + /* generate configuration XML */ + $xmlconfig = dump_xml_config($new_section, $section_name); + $xmlconfig = str_replace("<?xml version=\"1.0\"?>", "", $xmlconfig); + return $xmlconfig; +} + +/* + * restore_config_section($section_name, new_contents): restore a configuration section, + * and write the configuration out + * to disk/cf. + */ +function restore_config_section($section_name, $new_contents) { + global $config, $g; + conf_mount_rw(); + $fout = fopen("{$g['tmp_path']}/tmpxml","w"); + fwrite($fout, $new_contents); + fclose($fout); + + $xml = parse_xml_config($g['tmp_path'] . "/tmpxml", null); + if ($xml['pfsense']) { + $xml = $xml['pfsense']; + } + else if ($xml['m0n0wall']) { + $xml = $xml['m0n0wall']; + } + if ($xml[$section_name]) { + $section_xml = $xml[$section_name]; + } else { + $section_xml = -1; + } + + @unlink($g['tmp_path'] . "/tmpxml"); + if ($section_xml === -1) { + return false; + } + $config[$section_name] = &$section_xml; + if (file_exists("{$g['tmp_path']}/config.cache")) { + unlink("{$g['tmp_path']}/config.cache"); + } + write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name)); + disable_security_checks(); + conf_mount_ro(); + return true; +} + +/* + * merge_config_section($section_name, new_contents): restore a configuration section, + * and write the configuration out + * to disk/cf. But preserve the prior + * structure if needed + */ +function merge_config_section($section_name, $new_contents) { + global $config; + conf_mount_rw(); + $fname = get_tmp_filename(); + $fout = fopen($fname, "w"); + fwrite($fout, $new_contents); + fclose($fout); + $section_xml = parse_xml_config($fname, $section_name); + $config[$section_name] = $section_xml; + unlink($fname); + write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name)); + disable_security_checks(); + conf_mount_ro(); + return; +} + +/* + * http_post($server, $port, $url, $vars): does an http post to a web server + * posting the vars array. + * written by nf@bigpond.net.au + */ +function http_post($server, $port, $url, $vars) { + $user_agent = "Mozilla/4.0 (compatible; MSIE 5.5; Windows 98)"; + $urlencoded = ""; + while (list($key,$value) = each($vars)) { + $urlencoded.= urlencode($key) . "=" . urlencode($value) . "&"; + } + $urlencoded = substr($urlencoded,0,-1); + $content_length = strlen($urlencoded); + $headers = "POST $url HTTP/1.1 +Accept: */* +Accept-Language: en-au +Content-Type: application/x-www-form-urlencoded +User-Agent: $user_agent +Host: $server +Connection: Keep-Alive +Cache-Control: no-cache +Content-Length: $content_length + +"; + + $errno = ""; + $errstr = ""; + $fp = fsockopen($server, $port, $errno, $errstr); + if (!$fp) { + return false; + } + + fputs($fp, $headers); + fputs($fp, $urlencoded); + + $ret = ""; + while (!feof($fp)) { + $ret.= fgets($fp, 1024); + } + fclose($fp); + + return $ret; +} + +/* + * php_check_syntax($code_tocheck, $errormessage): checks $code_to_check for errors + */ +if (!function_exists('php_check_syntax')) { + global $g; + function php_check_syntax($code_to_check, &$errormessage) { + return false; + $fout = fopen("{$g['tmp_path']}/codetocheck.php","w"); + $code = $_POST['content']; + $code = str_replace("<?php", "", $code); + $code = str_replace("?>", "", $code); + fwrite($fout, "<?php\n\n"); + fwrite($fout, $code_to_check); + fwrite($fout, "\n\n?>\n"); + fclose($fout); + $command = "/usr/local/bin/php-cgi -l {$g['tmp_path']}/codetocheck.php"; + $output = exec_command($command); + if (stristr($output, "Errors parsing") == false) { + echo "false\n"; + $errormessage = ''; + return(false); + } else { + $errormessage = $output; + return(true); + } + } +} + +/* + * php_check_filename_syntax($filename, $errormessage): checks the file $filename for errors + */ +if (!function_exists('php_check_syntax')) { + function php_check_syntax($code_to_check, &$errormessage) { + return false; + $command = "/usr/local/bin/php-cgi -l " . escapeshellarg($code_to_check); + $output = exec_command($command); + if (stristr($output, "Errors parsing") == false) { + echo "false\n"; + $errormessage = ''; + return(false); + } else { + $errormessage = $output; + return(true); + } + } +} + +/* + * rmdir_recursive($path,$follow_links=false) + * Recursively remove a directory tree (rm -rf path) + * This is for directories _only_ + */ +function rmdir_recursive($path,$follow_links=false) { + $to_do = glob($path); + if (!is_array($to_do)) { + $to_do = array($to_do); + } + foreach ($to_do as $workingdir) { // Handle wildcards by foreaching. + if (file_exists($workingdir)) { + if (is_dir($workingdir)) { + $dir = opendir($workingdir); + while ($entry = readdir($dir)) { + if (is_file("$workingdir/$entry") || ((!$follow_links) && is_link("$workingdir/$entry"))) { + unlink("$workingdir/$entry"); + } elseif (is_dir("$workingdir/$entry") && $entry!='.' && $entry!='..') { + rmdir_recursive("$workingdir/$entry"); + } + } + closedir($dir); + rmdir($workingdir); + } elseif (is_file($workingdir)) { + unlink($workingdir); + } + } + } + return; +} + +/* + * call_pfsense_method(): Call a method exposed by the pfsense.org XMLRPC server. + */ +function call_pfsense_method($method, $params, $timeout = 0) { + global $g, $config; + + $xmlrpc_base_url = get_active_xml_rpc_base_url(); + $xmlrpc_path = $g['xmlrpcpath']; + + $xmlrpcfqdn = preg_replace("(https?://)", "", $xmlrpc_base_url); + $ip = gethostbyname($xmlrpcfqdn); + if ($ip == $xmlrpcfqdn) { + return false; + } + + $msg = new XML_RPC_Message($method, array(XML_RPC_Encode($params))); + $port = 0; + $proxyurl = ""; + $proxyport = 0; + $proxyuser = ""; + $proxypass = ""; + if (!empty($config['system']['proxyurl'])) { + $proxyurl = $config['system']['proxyurl']; + } + if (!empty($config['system']['proxyport']) && is_numeric($config['system']['proxyport'])) { + $proxyport = $config['system']['proxyport']; + } + if (!empty($config['system']['proxyuser'])) { + $proxyuser = $config['system']['proxyuser']; + } + if (!empty($config['system']['proxypass'])) { + $proxypass = $config['system']['proxypass']; + } + $cli = new XML_RPC_Client($xmlrpc_path, $xmlrpc_base_url, $port, $proxyurl, $proxyport, $proxyuser, $proxypass); + // If the ALT PKG Repo has a username/password set, use it. + if ($config['system']['altpkgrepo']['username'] && + $config['system']['altpkgrepo']['password']) { + $username = $config['system']['altpkgrepo']['username']; + $password = $config['system']['altpkgrepo']['password']; + $cli->setCredentials($username, $password); + } + $resp = $cli->send($msg, $timeout); + if (!is_object($resp)) { + log_error(sprintf(gettext("XMLRPC communication error: %s"), $cli->errstr)); + return false; + } elseif ($resp->faultCode()) { + log_error(sprintf(gettext('XMLRPC request failed with error %1$s: %2$s'), $resp->faultCode(), $resp->faultString())); + return false; + } else { + return XML_RPC_Decode($resp->value()); + } +} + +/* + * check_firmware_version(): Check whether the current firmware installed is the most recently released. + */ +function check_firmware_version($tocheck = "all", $return_php = true) { + global $g, $config; + + $xmlrpc_base_url = get_active_xml_rpc_base_url(); + $xmlrpcfqdn = preg_replace("(https?://)", "", $xmlrpc_base_url); + $ip = gethostbyname($xmlrpcfqdn); + if ($ip == $xmlrpcfqdn) { + return false; + } + $version = php_uname('r'); + $version = explode('-', $version); + $rawparams = array("firmware" => array("version" => $g['product_version']), + "kernel" => array("version" => $version[0]), + "base" => array("version" => $version[0]), + "platform" => trim(file_get_contents('/etc/platform')), + "config_version" => $config['version'] + ); + unset($version); + + if ($tocheck == "all") { + $params = $rawparams; + } else { + foreach ($tocheck as $check) { + $params['check'] = $rawparams['check']; + $params['platform'] = $rawparams['platform']; + } + } + if ($config['system']['firmware']['branch']) { + $params['branch'] = $config['system']['firmware']['branch']; + } + + /* XXX: What is this method? */ + if (!($versions = call_pfsense_method('pfsense.get_firmware_version', $params))) { + return false; + } else { + $versions["current"] = $params; + } + + return $versions; +} + +/* + * host_firmware_version(): Return the versions used in this install + */ +function host_firmware_version($tocheck = "") { + global $g, $config; + + $os_version = trim(substr(php_uname("r"), 0, strpos(php_uname("r"), '-'))); + + return array( + "firmware" => array("version" => $g['product_version']), + "kernel" => array("version" => $os_version), + "base" => array("version" => $os_version), + "platform" => trim(file_get_contents('/etc/platform', " \n")), + "config_version" => $config['version'] + ); +} + +function get_disk_info() { + $diskout = ""; + exec("/bin/df -h | /usr/bin/grep -w '/' | /usr/bin/awk '{ print $2, $3, $4, $5 }'", $diskout); + return explode(' ', $diskout[0]); +} + +/****f* pfsense-utils/strncpy + * NAME + * strncpy - copy strings + * INPUTS + * &$dst, $src, $length + * RESULT + * none + ******/ +function strncpy(&$dst, $src, $length) { + if (strlen($src) > $length) { + $dst = substr($src, 0, $length); + } else { + $dst = $src; + } +} + +/****f* pfsense-utils/reload_interfaces_sync + * NAME + * reload_interfaces - reload all interfaces + * INPUTS + * none + * RESULT + * none + ******/ +function reload_interfaces_sync() { + global $config, $g; + + if ($g['debug']) { + log_error(gettext("reload_interfaces_sync() is starting.")); + } + + /* parse config.xml again */ + $config = parse_config(true); + + /* enable routing */ + system_routing_enable(); + if ($g['debug']) { + log_error(gettext("Enabling system routing")); + } + + if ($g['debug']) { + log_error(gettext("Cleaning up Interfaces")); + } + + /* set up interfaces */ + interfaces_configure(); +} + +/****f* pfsense-utils/reload_all + * NAME + * reload_all - triggers a reload of all settings + * * INPUTS + * none + * RESULT + * none + ******/ +function reload_all() { + send_event("service reload all"); +} + +/****f* pfsense-utils/reload_interfaces + * NAME + * reload_interfaces - triggers a reload of all interfaces + * INPUTS + * none + * RESULT + * none + ******/ +function reload_interfaces() { + send_event("interface all reload"); +} + +/****f* pfsense-utils/reload_all_sync + * NAME + * reload_all - reload all settings + * * INPUTS + * none + * RESULT + * none + ******/ +function reload_all_sync() { + global $config, $g; + + /* parse config.xml again */ + $config = parse_config(true); + + /* set up our timezone */ + system_timezone_configure(); + + /* set up our hostname */ + system_hostname_configure(); + + /* make hosts file */ + system_hosts_generate(); + + /* generate resolv.conf */ + system_resolvconf_generate(); + + /* enable routing */ + system_routing_enable(); + + /* set up interfaces */ + interfaces_configure(); + + /* start dyndns service */ + services_dyndns_configure(); + + /* configure cron service */ + configure_cron(); + + /* start the NTP client */ + system_ntp_configure(); + + /* sync pw database */ + conf_mount_rw(); + unlink_if_exists("/etc/spwd.db.tmp"); + mwexec("/usr/sbin/pwd_mkdb -d /etc/ /etc/master.passwd"); + conf_mount_ro(); + + /* restart sshd */ + send_event("service restart sshd"); + + /* restart webConfigurator if needed */ + send_event("service restart webgui"); +} + +function setup_serial_port($when="save", $path="") { + global $g, $config; + conf_mount_rw(); + $ttys_file = "{$path}/etc/ttys"; + $boot_config_file = "{$path}/boot.config"; + $loader_conf_file = "{$path}/boot/loader.conf"; + /* serial console - write out /boot.config */ + if (file_exists($boot_config_file)) { + $boot_config = file_get_contents($boot_config_file); + } else { + $boot_config = ""; + } + + $serialspeed = (is_numeric($config['system']['serialspeed'])) ? $config['system']['serialspeed'] : "115200"; + if ($g['platform'] != "cdrom") { + $serial_only = false; + + if (($g['platform'] == "nanobsd") && !file_exists("/etc/nano_use_vga.txt")) { + $serial_only = true; + } else { + $specific_platform = system_identify_specific_platform(); + if ($specific_platform['name'] == 'RCC-VE' || + $specific_platform['name'] == 'RCC-DFF') { + $serial_only = true; + } + } + + $boot_config_split = explode("\n", $boot_config); + $fd = fopen($boot_config_file,"w"); + if ($fd) { + foreach ($boot_config_split as $bcs) { + if (stristr($bcs, "-D") || stristr($bcs, "-h")) { + /* DONT WRITE OUT, WE'LL DO IT LATER */ + } else { + if ($bcs <> "") { + fwrite($fd, "{$bcs}\n"); + } + } + } + if ($serial_only === true) { + fwrite($fd, "-S{$serialspeed} -h"); + } else if (is_serial_enabled()) { + fwrite($fd, "-S{$serialspeed} -D"); + } + fclose($fd); + } + + /* serial console - write out /boot/loader.conf */ + if ($when == "upgrade") { + system("echo \"Reading {$loader_conf_file}...\" >> /conf/upgrade_log.txt"); + } + $boot_config = file_get_contents($loader_conf_file); + $boot_config_split = explode("\n", $boot_config); + if (count($boot_config_split) > 0) { + $new_boot_config = array(); + // Loop through and only add lines that are not empty, and which + // do not contain a console directive. + foreach ($boot_config_split as $bcs) { + if (!empty($bcs) && + (stripos($bcs, "console") === false) && + (stripos($bcs, "boot_multicons") === false) && + (stripos($bcs, "boot_serial") === false) && + (stripos($bcs, "hw.usb.no_pf") === false) && + (stripos($bcs, "hint.uart.0.flags") === false) && + (stripos($bcs, "hint.uart.1.flags") === false)) { + $new_boot_config[] = $bcs; + } + } + + if ($serial_only === true) { + $new_boot_config[] = 'boot_serial="YES"'; + $new_boot_config[] = 'console="comconsole"'; + } else if (is_serial_enabled()) { + $new_boot_config[] = 'boot_multicons="YES"'; + $new_boot_config[] = 'boot_serial="YES"'; + $primaryconsole = isset($g['primaryconsole_force']) ? $g['primaryconsole_force'] : $config['system']['primaryconsole']; + switch ($primaryconsole) { + case "video": + $new_boot_config[] = 'console="vidconsole,comconsole"'; + break; + case "serial": + default: + $new_boot_config[] = 'console="comconsole,vidconsole"'; + } + } + $new_boot_config[] = 'comconsole_speed="' . $serialspeed . '"'; + + $specplatform = system_identify_specific_platform(); + if ($specplatform['name'] == 'RCC-VE' || + $specplatform['name'] == 'RCC-DFF') { + $new_boot_config[] = 'comconsole_port="0x2F8"'; + $new_boot_config[] = 'hint.uart.0.flags="0x00"'; + $new_boot_config[] = 'hint.uart.1.flags="0x10"'; + } + $new_boot_config[] = 'hw.usb.no_pf="1"'; + + file_put_contents($loader_conf_file, implode("\n", $new_boot_config) . "\n"); + } + } + $ttys = file_get_contents($ttys_file); + $ttys_split = explode("\n", $ttys); + $fd = fopen($ttys_file, "w"); + + $on_off = (is_serial_enabled() ? 'onifconsole' : 'off'); + + if (isset($config['system']['disableconsolemenu'])) { + $console_type = 'Pc'; + $serial_type = 'std.' . $serialspeed; + } else { + $console_type = 'al.Pc'; + $serial_type = 'al.' . $serialspeed; + } + foreach ($ttys_split as $tty) { + if (stristr($tty, "ttyv0")) { + fwrite($fd, "ttyv0 \"/usr/libexec/getty {$console_type}\" cons25 on secure\n"); + } else if (stristr($tty, "ttyu")) { + $ttyn = substr($tty, 0, 5); + fwrite($fd, "{$ttyn} \"/usr/libexec/getty {$serial_type}\" cons25 {$on_off} secure\n"); + } else { + fwrite($fd, $tty . "\n"); + } + } + unset($on_off, $console_type, $serial_type); + fclose($fd); + if ($when != "upgrade") { + reload_ttys(); + } + + conf_mount_ro(); + return; +} + +function is_serial_enabled() { + global $g, $config; + + if (!isset($g['enableserial_force']) && + !isset($config['system']['enableserial']) && + ($g['platform'] == "pfSense" || $g['platform'] == "cdrom" || file_exists("/etc/nano_use_vga.txt"))) { + return false; + } + + return true; +} + +function reload_ttys() { + // Send a HUP signal to init will make it reload /etc/ttys + posix_kill(1, SIGHUP); +} + +function print_value_list($list, $count = 10, $separator = ",") { + $list = implode($separator, array_slice($list, 0, $count)); + if (count($list) < $count) { + $list .= "."; + } else { + $list .= "..."; + } + return $list; +} + +/* DHCP enabled on any interfaces? */ +function is_dhcp_server_enabled() { + global $config; + + if (!is_array($config['dhcpd'])) { + return false; + } + + foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) { + if (isset($dhcpifconf['enable']) && !empty($config['interfaces'][$dhcpif])) { + return true; + } + } + + return false; +} + +/* DHCP enabled on any interfaces? */ +function is_dhcpv6_server_enabled() { + global $config; + + if (is_array($config['interfaces'])) { + foreach ($config['interfaces'] as $ifcfg) { + if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface'])) { + return true; + } + } + } + + if (!is_array($config['dhcpdv6'])) { + return false; + } + + foreach ($config['dhcpdv6'] as $dhcpv6if => $dhcpv6ifconf) { + if (isset($dhcpv6ifconf['enable']) && !empty($config['interfaces'][$dhcpv6if])) { + return true; + } + } + + return false; +} + +/* radvd enabled on any interfaces? */ +function is_radvd_enabled() { + global $config; + + if (!is_array($config['dhcpdv6'])) { + $config['dhcpdv6'] = array(); + } + + $dhcpdv6cfg = $config['dhcpdv6']; + $Iflist = get_configured_interface_list(); + + /* handle manually configured DHCP6 server settings first */ + foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) { + if (!isset($config['interfaces'][$dhcpv6if]['enable'])) { + continue; + } + + if (!isset($dhcpv6ifconf['ramode'])) { + $dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode']; + } + + if ($dhcpv6ifconf['ramode'] == "disabled") { + continue; + } + + $ifcfgipv6 = get_interface_ipv6($dhcpv6if); + if (!is_ipaddrv6($ifcfgipv6)) { + continue; + } + + return true; + } + + /* handle DHCP-PD prefixes and 6RD dynamic interfaces */ + foreach ($Iflist as $if => $ifdescr) { + if (!isset($config['interfaces'][$if]['track6-interface'])) { + continue; + } + if (!isset($config['interfaces'][$if]['enable'])) { + continue; + } + + $ifcfgipv6 = get_interface_ipv6($if); + if (!is_ipaddrv6($ifcfgipv6)) { + continue; + } + + $ifcfgsnv6 = get_interface_subnetv6($if); + $subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6); + + if (!is_ipaddrv6($subnetv6)) { + continue; + } + + return true; + } + + return false; +} + +/* Any PPPoE servers enabled? */ +function is_pppoe_server_enabled() { + global $config; + + $pppoeenable = false; + + if (!is_array($config['pppoes']) || !is_array($config['pppoes']['pppoe'])) { + return false; + } + + foreach ($config['pppoes']['pppoe'] as $pppoes) { + if ($pppoes['mode'] == 'server') { + $pppoeenable = true; + } + } + + return $pppoeenable; +} + +function convert_seconds_to_hms($sec) { + $min=$hrs=0; + if ($sec != 0) { + $min = floor($sec/60); + $sec %= 60; + } + if ($min != 0) { + $hrs = floor($min/60); + $min %= 60; + } + if ($sec < 10) { + $sec = "0".$sec; + } + if ($min < 10) { + $min = "0".$min; + } + if ($hrs < 10) { + $hrs = "0".$hrs; + } + $result = $hrs.":".$min.":".$sec; + return $result; +} + +/* Compute the total uptime from the ppp uptime log file in the conf directory */ + +function get_ppp_uptime($port) { + if (file_exists("/conf/{$port}.log")) { + $saved_time = file_get_contents("/conf/{$port}.log"); + $uptime_data = explode("\n",$saved_time); + $sec=0; + foreach ($uptime_data as $upt) { + $sec += substr($upt, 1 + strpos($upt, " ")); + } + return convert_seconds_to_hms($sec); + } else { + $total_time = gettext("No history data found!"); + return $total_time; + } +} + +//returns interface information +function get_interface_info($ifdescr) { + global $config, $g; + + $ifinfo = array(); + if (empty($config['interfaces'][$ifdescr])) { + return; + } + $ifinfo['hwif'] = $config['interfaces'][$ifdescr]['if']; + $ifinfo['if'] = get_real_interface($ifdescr); + + $chkif = $ifinfo['if']; + $ifinfotmp = pfSense_get_interface_addresses($chkif); + $ifinfo['status'] = $ifinfotmp['status']; + if (empty($ifinfo['status'])) { + $ifinfo['status'] = "down"; + } + $ifinfo['macaddr'] = $ifinfotmp['macaddr']; + $ifinfo['mtu'] = $ifinfotmp['mtu']; + $ifinfo['ipaddr'] = $ifinfotmp['ipaddr']; + $ifinfo['subnet'] = $ifinfotmp['subnet']; + $ifinfo['linklocal'] = get_interface_linklocal($ifdescr); + $ifinfo['ipaddrv6'] = get_interface_ipv6($ifdescr); + $ifinfo['subnetv6'] = get_interface_subnetv6($ifdescr); + if (isset($ifinfotmp['link0'])) { + $link0 = "down"; + } + $ifinfotmp = pfSense_get_interface_stats($chkif); + // $ifinfo['inpkts'] = $ifinfotmp['inpkts']; + // $ifinfo['outpkts'] = $ifinfotmp['outpkts']; + $ifinfo['inerrs'] = $ifinfotmp['inerrs']; + $ifinfo['outerrs'] = $ifinfotmp['outerrs']; + $ifinfo['collisions'] = $ifinfotmp['collisions']; + + /* Use pfctl for non wrapping 64 bit counters */ + /* Pass */ + exec("/sbin/pfctl -vvsI -i {$chkif}", $pfctlstats); + $pf_in4_pass = preg_split("/ +/ ", $pfctlstats[3]); + $pf_out4_pass = preg_split("/ +/", $pfctlstats[5]); + $pf_in6_pass = preg_split("/ +/ ", $pfctlstats[7]); + $pf_out6_pass = preg_split("/ +/", $pfctlstats[9]); + $in4_pass = $pf_in4_pass[5]; + $out4_pass = $pf_out4_pass[5]; + $in4_pass_packets = $pf_in4_pass[3]; + $out4_pass_packets = $pf_out4_pass[3]; + $in6_pass = $pf_in6_pass[5]; + $out6_pass = $pf_out6_pass[5]; + $in6_pass_packets = $pf_in6_pass[3]; + $out6_pass_packets = $pf_out6_pass[3]; + $ifinfo['inbytespass'] = $in4_pass + $in6_pass; + $ifinfo['outbytespass'] = $out4_pass + $out6_pass; + $ifinfo['inpktspass'] = $in4_pass_packets + $in6_pass_packets; + $ifinfo['outpktspass'] = $out4_pass_packets + $out6_pass_packets; + + /* Block */ + $pf_in4_block = preg_split("/ +/", $pfctlstats[4]); + $pf_out4_block = preg_split("/ +/", $pfctlstats[6]); + $pf_in6_block = preg_split("/ +/", $pfctlstats[8]); + $pf_out6_block = preg_split("/ +/", $pfctlstats[10]); + $in4_block = $pf_in4_block[5]; + $out4_block = $pf_out4_block[5]; + $in4_block_packets = $pf_in4_block[3]; + $out4_block_packets = $pf_out4_block[3]; + $in6_block = $pf_in6_block[5]; + $out6_block = $pf_out6_block[5]; + $in6_block_packets = $pf_in6_block[3]; + $out6_block_packets = $pf_out6_block[3]; + $ifinfo['inbytesblock'] = $in4_block + $in6_block; + $ifinfo['outbytesblock'] = $out4_block + $out6_block; + $ifinfo['inpktsblock'] = $in4_block_packets + $in6_block_packets; + $ifinfo['outpktsblock'] = $out4_block_packets + $out6_block_packets; + + $ifinfo['inbytes'] = $in4_pass + $in6_pass; + $ifinfo['outbytes'] = $out4_pass + $out6_pass; + $ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets; + $ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets; + + $ifconfiginfo = ""; + $link_type = $config['interfaces'][$ifdescr]['ipaddr']; + switch ($link_type) { + /* DHCP? -> see if dhclient is up */ + case "dhcp": + /* see if dhclient is up */ + if (find_dhclient_process($ifinfo['if']) != 0) { + $ifinfo['dhcplink'] = "up"; + } else { + $ifinfo['dhcplink'] = "down"; + } + + break; + /* PPPoE/PPTP/L2TP interface? -> get status from virtual interface */ + case "pppoe": + case "pptp": + case "l2tp": + if ($ifinfo['status'] == "up" && !isset($link0)) { + /* get PPPoE link status for dial on demand */ + $ifinfo["{$link_type}link"] = "up"; + } else { + $ifinfo["{$link_type}link"] = "down"; + } + + break; + /* PPP interface? -> get uptime for this session and cumulative uptime from the persistent log file in conf */ + case "ppp": + if ($ifinfo['status'] == "up") { + $ifinfo['ppplink'] = "up"; + } else { + $ifinfo['ppplink'] = "down" ; + } + + if (empty($ifinfo['status'])) { + $ifinfo['status'] = "down"; + } + + if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) { + foreach ($config['ppps']['ppp'] as $pppid => $ppp) { + if ($config['interfaces'][$ifdescr]['if'] == $ppp['if']) { + break; + } + } + } + $dev = $ppp['ports']; + if ($config['interfaces'][$ifdescr]['if'] != $ppp['if'] || empty($dev)) { + break; + } + if (!file_exists($dev)) { + $ifinfo['nodevice'] = 1; + $ifinfo['pppinfo'] = $dev . " " . gettext("device not present! Is the modem attached to the system?"); + } + + $usbmodemoutput = array(); + exec("usbconfig", $usbmodemoutput); + $mondev = "{$g['tmp_path']}/3gstats.{$ifdescr}"; + if (file_exists($mondev)) { + $cellstats = file($mondev); + /* skip header */ + $a_cellstats = explode(",", $cellstats[1]); + if (preg_match("/huawei/i", implode("\n", $usbmodemoutput))) { + $ifinfo['cell_rssi'] = huawei_rssi_to_string($a_cellstats[1]); + $ifinfo['cell_mode'] = huawei_mode_to_string($a_cellstats[2], $a_cellstats[3]); + $ifinfo['cell_simstate'] = huawei_simstate_to_string($a_cellstats[10]); + $ifinfo['cell_service'] = huawei_service_to_string(trim($a_cellstats[11])); + } + if (preg_match("/zte/i", implode("\n", $usbmodemoutput))) { + $ifinfo['cell_rssi'] = zte_rssi_to_string($a_cellstats[1]); + $ifinfo['cell_mode'] = zte_mode_to_string($a_cellstats[2], $a_cellstats[3]); + $ifinfo['cell_simstate'] = zte_simstate_to_string($a_cellstats[10]); + $ifinfo['cell_service'] = zte_service_to_string(trim($a_cellstats[11])); + } + $ifinfo['cell_upstream'] = $a_cellstats[4]; + $ifinfo['cell_downstream'] = trim($a_cellstats[5]); + $ifinfo['cell_sent'] = $a_cellstats[6]; + $ifinfo['cell_received'] = trim($a_cellstats[7]); + $ifinfo['cell_bwupstream'] = $a_cellstats[8]; + $ifinfo['cell_bwdownstream'] = trim($a_cellstats[9]); + } + // Calculate cumulative uptime for PPP link. Useful for connections that have per minute/hour contracts so you don't go over! + if (isset($ppp['uptime'])) { + $ifinfo['ppp_uptime_accumulated'] = "(".get_ppp_uptime($ifinfo['if']).")"; + } + break; + default: + break; + } + + if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) { + $sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`); + $ifinfo['ppp_uptime'] = convert_seconds_to_hms($sec); + } + + if ($ifinfo['status'] == "up") { + /* try to determine media with ifconfig */ + unset($ifconfiginfo); + exec("/sbin/ifconfig " . $ifinfo['if'], $ifconfiginfo); + $wifconfiginfo = array(); + if (is_interface_wireless($ifdescr)) { + exec("/sbin/ifconfig {$ifinfo['if']} list sta", $wifconfiginfo); + array_shift($wifconfiginfo); + } + $matches = ""; + foreach ($ifconfiginfo as $ici) { + + /* don't list media/speed for wireless cards, as it always + displays 2 Mbps even though clients can connect at 11 Mbps */ + if (preg_match("/media: .*? \((.*?)\)/", $ici, $matches)) { + $ifinfo['media'] = $matches[1]; + } else if (preg_match("/media: Ethernet (.*)/", $ici, $matches)) { + $ifinfo['media'] = $matches[1]; + } else if (preg_match("/media: IEEE 802.11 Wireless Ethernet (.*)/", $ici, $matches)) { + $ifinfo['media'] = $matches[1]; + } + + if (preg_match("/status: (.*)$/", $ici, $matches)) { + if ($matches[1] != "active") { + $ifinfo['status'] = $matches[1]; + } + if ($ifinfo['status'] == gettext("running")) { + $ifinfo['status'] = gettext("up"); + } + } + if (preg_match("/channel (\S*)/", $ici, $matches)) { + $ifinfo['channel'] = $matches[1]; + } + if (preg_match("/ssid (\".*?\"|\S*)/", $ici, $matches)) { + if ($matches[1][0] == '"') { + $ifinfo['ssid'] = substr($matches[1], 1, -1); + } + else { + $ifinfo['ssid'] = $matches[1]; + } + } + if (preg_match("/laggproto (.*)$/", $ici, $matches)) { + $ifinfo['laggproto'] = $matches[1]; + } + if (preg_match("/laggport: (.*)$/", $ici, $matches)) { + $ifinfo['laggport'][] = $matches[1]; + } + } + foreach ($wifconfiginfo as $ici) { + $elements = preg_split("/[ ]+/i", $ici); + if ($elements[0] != "") { + $ifinfo['bssid'] = $elements[0]; + } + if ($elements[3] != "") { + $ifinfo['rate'] = $elements[3]; + } + if ($elements[4] != "") { + $ifinfo['rssi'] = $elements[4]; + } + } + /* lookup the gateway */ + if (interface_has_gateway($ifdescr)) { + $ifinfo['gateway'] = get_interface_gateway($ifdescr); + $ifinfo['gatewayv6'] = get_interface_gateway_v6($ifdescr); + } + } + + $bridge = ""; + $bridge = link_interface_to_bridge($ifdescr); + if ($bridge) { + $bridge_text = `/sbin/ifconfig {$bridge}`; + if (stristr($bridge_text, "blocking") <> false) { + $ifinfo['bridge'] = "<b><font color='red'>" . gettext("blocking") . "</font></b> - " . gettext("check for ethernet loops"); + $ifinfo['bridgeint'] = $bridge; + } else if (stristr($bridge_text, "learning") <> false) { + $ifinfo['bridge'] = gettext("learning"); + $ifinfo['bridgeint'] = $bridge; + } else if (stristr($bridge_text, "forwarding") <> false) { + $ifinfo['bridge'] = gettext("forwarding"); + $ifinfo['bridgeint'] = $bridge; + } + } + + return $ifinfo; +} + +//returns cpu speed of processor. Good for determining capabilities of machine +function get_cpu_speed() { + return get_single_sysctl("hw.clockrate"); +} + +function get_uptime_sec() { + $boottime = ""; + $matches = ""; + $boottime = get_single_sysctl("kern.boottime"); + preg_match("/sec = (\d+)/", $boottime, $matches); + $boottime = $matches[1]; + if (intval($boottime) == 0) { + return 0; + } + + $uptime = time() - $boottime; + return $uptime; +} + +function add_hostname_to_watch($hostname) { + if (!is_dir("/var/db/dnscache")) { + mkdir("/var/db/dnscache"); + } + $result = array(); + if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) { + $domrecords = array(); + $domips = array(); + exec("host -t A " . escapeshellarg($hostname), $domrecords, $rethost); + if ($rethost == 0) { + foreach ($domrecords as $domr) { + $doml = explode(" ", $domr); + $domip = $doml[3]; + /* fill array with domain ip addresses */ + if (is_ipaddr($domip)) { + $domips[] = $domip; + } + } + } + sort($domips); + $contents = ""; + if (!empty($domips)) { + foreach ($domips as $ip) { + $contents .= "$ip\n"; + } + } + file_put_contents("/var/db/dnscache/$hostname", $contents); + /* Remove empty elements */ + $result = array_filter(explode("\n", $contents), 'strlen'); + } + return $result; +} + +function is_fqdn($fqdn) { + $hostname = false; + if (preg_match("/[-A-Z0-9\.]+\.[-A-Z0-9\.]+/i", $fqdn)) { + $hostname = true; + } + if (preg_match("/\.\./", $fqdn)) { + $hostname = false; + } + if (preg_match("/^\./i", $fqdn)) { + $hostname = false; + } + if (preg_match("/\//i", $fqdn)) { + $hostname = false; + } + return($hostname); +} + +function pfsense_default_state_size() { + /* get system memory amount */ + $memory = get_memory(); + $physmem = $memory[0]; + /* Be cautious and only allocate 10% of system memory to the state table */ + $max_states = (int) ($physmem/10)*1000; + return $max_states; +} + +function pfsense_default_tables_size() { + $current = `pfctl -sm | grep ^tables | awk '{print $4};'`; + return $current; +} + +function pfsense_default_table_entries_size() { + $current = `pfctl -sm | grep table-entries | awk '{print $4};'`; + return $current; +} + +/* Compare the current hostname DNS to the DNS cache we made + * if it has changed we return the old records + * if no change we return false */ +function compare_hostname_to_dnscache($hostname) { + if (!is_dir("/var/db/dnscache")) { + mkdir("/var/db/dnscache"); + } + $hostname = trim($hostname); + if (is_readable("/var/db/dnscache/{$hostname}")) { + $oldcontents = file_get_contents("/var/db/dnscache/{$hostname}"); + } else { + $oldcontents = ""; + } + if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) { + $domrecords = array(); + $domips = array(); + exec("host -t A " . escapeshellarg($hostname), $domrecords, $rethost); + if ($rethost == 0) { + foreach ($domrecords as $domr) { + $doml = explode(" ", $domr); + $domip = $doml[3]; + /* fill array with domain ip addresses */ + if (is_ipaddr($domip)) { + $domips[] = $domip; + } + } + } + sort($domips); + $contents = ""; + if (!empty($domips)) { + foreach ($domips as $ip) { + $contents .= "$ip\n"; + } + } + } + + if (trim($oldcontents) != trim($contents)) { + if ($g['debug']) { + log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents)); + } + return ($oldcontents); + } else { + return false; + } +} + +/* + * load_crypto() - Load crypto modules if enabled in config. + */ +function load_crypto() { + global $config, $g; + $crypto_modules = array('glxsb', 'aesni'); + + if (!in_array($config['system']['crypto_hardware'], $crypto_modules)) { + return false; + } + + if (!empty($config['system']['crypto_hardware']) && !is_module_loaded($config['system']['crypto_hardware'])) { + log_error("Loading {$config['system']['crypto_hardware']} cryptographic accelerator module."); + mwexec("/sbin/kldload {$config['system']['crypto_hardware']}"); + } +} + +/* + * load_thermal_hardware() - Load temperature monitor kernel module + */ +function load_thermal_hardware() { + global $config, $g; + $thermal_hardware_modules = array('coretemp', 'amdtemp'); + + if (!in_array($config['system']['thermal_hardware'], $thermal_hardware_modules)) { + return false; + } + + if (!empty($config['system']['thermal_hardware']) && !is_module_loaded($config['system']['thermal_hardware'])) { + log_error("Loading {$config['system']['thermal_hardware']} thermal monitor module."); + mwexec("/sbin/kldload {$config['system']['thermal_hardware']}"); + } +} + +/****f* pfsense-utils/isvm + * NAME + * isvm + * INPUTS + * none + * RESULT + * returns true if machine is running under a virtual environment + ******/ +function isvm() { + $virtualenvs = array("vmware", "parallels", "qemu", "bochs", "plex86", "VirtualBox"); + $_gb = exec('/bin/kenv smbios.system.product 2>/dev/null', $output, $rc); + + if ($rc != 0 || !isset($output[0])) { + return false; + } + + foreach ($virtualenvs as $virtualenv) { + if (stripos($output[0], $virtualenv) !== false) { + return true; + } + } + + return false; +} + +function get_freebsd_version() { + $version = explode(".", php_uname("r")); + return $version[0]; +} + +function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) { + global $config, $g; + + $fp = fopen($destination, "wb"); + + if (!$fp) { + return false; + } + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify_ssl); + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + if (!isset($config['system']['host_uuid'])) { + curl_setopt($ch, CURLOPT_USERAGENT, $g['product_name'] . '/' . $g['product_version'] . ' : ' . get_single_sysctl('kern.hostuuid')); + } else { + curl_setopt($ch, CURLOPT_USERAGENT, $g['product_name'] . '/' . $g['product_version']); + } + + if (!empty($config['system']['proxyurl'])) { + curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']); + if (!empty($config['system']['proxyport'])) { + curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']); + } + if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) { + @curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE); + curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}"); + } + } + + @curl_exec($ch); + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + fclose($fp); + curl_close($ch); + return ($http_code == 200) ? true : $http_code; +} + +function download_file_with_progress_bar($url_file, $destination_file, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) { + global $config, $g; + global $ch, $fout, $file_size, $downloaded, $config, $first_progress_update; + $file_size = 1; + $downloaded = 1; + $first_progress_update = TRUE; + /* open destination file */ + $fout = fopen($destination_file, "wb"); + + /* + * Originally by Author: Keyvan Minoukadeh + * Modified by Scott Ullrich to return Content-Length size + */ + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url_file); + curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header'); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_WRITEFUNCTION, $readbody); + curl_setopt($ch, CURLOPT_NOPROGRESS, '1'); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + if (!isset($config['system']['host_uuid'])) { + curl_setopt($ch, CURLOPT_USERAGENT, $g['product_name'] . '/' . $g['product_version'] . ' : ' . get_single_sysctl('kern.hostuuid')); + } else { + curl_setopt($ch, CURLOPT_USERAGENT, $g['product_name'] . '/' . $g['product_version']); + } + + if (!empty($config['system']['proxyurl'])) { + curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']); + if (!empty($config['system']['proxyport'])) { + curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']); + } + if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) { + @curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE); + curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}"); + } + } + + @curl_exec($ch); + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($fout) { + fclose($fout); + } + curl_close($ch); + return ($http_code == 200) ? true : $http_code; +} + +function read_header($ch, $string) { + global $file_size, $fout; + $length = strlen($string); + $regs = ""; + preg_match("/(Content-Length:) (.*)/", $string, $regs); + if ($regs[2] <> "") { + $file_size = intval($regs[2]); + } + ob_flush(); + return $length; +} + +function read_body($ch, $string) { + global $fout, $file_size, $downloaded, $sendto, $static_status, $static_output, $lastseen, $first_progress_update; + global $pkg_interface; + $length = strlen($string); + $downloaded += intval($length); + if ($file_size > 0) { + $downloadProgress = round(100 * (1 - $downloaded / $file_size), 0); + $downloadProgress = 100 - $downloadProgress; + } else { + $downloadProgress = 0; + } + if ($lastseen <> $downloadProgress and $downloadProgress < 101) { + if ($sendto == "status") { + if ($pkg_interface == "console") { + if (($downloadProgress % 10) == 0 || $downloadProgress < 10) { + $tostatus = $static_status . $downloadProgress . "%"; + if ($downloadProgress == 100) { + $tostatus = $tostatus . "\r"; + } + update_status($tostatus); + } + } else { + $tostatus = $static_status . $downloadProgress . "%"; + update_status($tostatus); + } + } else { + if ($pkg_interface == "console") { + if (($downloadProgress % 10) == 0 || $downloadProgress < 10) { + $tooutput = $static_output . $downloadProgress . "%"; + if ($downloadProgress == 100) { + $tooutput = $tooutput . "\r"; + } + update_output_window($tooutput); + } + } else { + $tooutput = $static_output . $downloadProgress . "%"; + update_output_window($tooutput); + } + } + if (($pkg_interface != "console") || (($downloadProgress % 10) == 0) || ($downloadProgress < 10)) { + update_progress_bar($downloadProgress, $first_progress_update); + $first_progress_update = FALSE; + } + $lastseen = $downloadProgress; + } + if ($fout) { + fwrite($fout, $string); + } + ob_flush(); + return $length; +} + +/* + * update_output_window: update bottom textarea dynamically. + */ +function update_output_window($text) { + global $pkg_interface; + $log = preg_replace("/\n/", "\\n", $text); + if ($pkg_interface != "console") { +?> +<script> + document.getElementById("output").textContent="<?=htmlspecialchars($log)?>"; + document.getElementById("output").scrollTop = document.getElementById("output").scrollHeight; +</script> +<?php + } + /* ensure that contents are written out */ + ob_flush(); +} + +/* + * update_status: update top textarea dynamically. + */ +function update_status($status) { + global $pkg_interface; + if ($pkg_interface == "console") { + echo "\r{$status}"; + } else { + echo '<script>document.getElementById("status").innerText="'. htmlspecialchars($status).'";</script>'; + } + + /* ensure that contents are written out */ + ob_flush(); +} + +/* + * update_progress_bar($percent, $first_time): updates the javascript driven progress bar. + */ +function update_progress_bar($percent, $first_time) { + global $pkg_interface; + if ($percent > 100) { + $percent = 1; + } + if ($pkg_interface <> "console") { + echo '<script>document.getElementById("progressbar").style.width="'. $percent.'%";</script>'; + } else { + if (!($first_time)) { + echo "\x08\x08\x08\x08\x08"; + } + echo sprintf("%4d%%", $percent); + } +} + +/* Split() is being DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 6.0.0. Relying on this feature is highly discouraged. */ +if (!function_exists("split")) { + function split($separator, $haystack, $limit = null) { + log_error("deprecated split() call with separator '{$separator}'"); + return preg_split($separator, $haystack, $limit); + } +} + +function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) { + global $g, $config, $pconfig, $debug; + if (!$origname) { + return; + } + + $sectionref = &$config; + foreach ($section as $sectionname) { + if (is_array($sectionref) && isset($sectionref[$sectionname])) { + $sectionref = &$sectionref[$sectionname]; + } else { + return; + } + } + + if ($debug) { + $fd = fopen("{$g['tmp_path']}/print_r", "a"); + fwrite($fd, print_r($pconfig, true)); + } + + if (is_array($sectionref)) { + foreach ($sectionref as $itemkey => $item) { + if ($debug) { + fwrite($fd, "$itemkey\n"); + } + + $fieldfound = true; + $fieldref = &$sectionref[$itemkey]; + foreach ($field as $fieldname) { + if (is_array($fieldref) && isset($fieldref[$fieldname])) { + $fieldref = &$fieldref[$fieldname]; + } else { + $fieldfound = false; + break; + } + } + if ($fieldfound && $fieldref == $origname) { + if ($debug) { + fwrite($fd, "Setting old alias value $origname to $new_alias_name\n"); + } + $fieldref = $new_alias_name; + } + } + } + + if ($debug) { + fclose($fd); + } + +} + +function parse_aliases_file($filename, $type = "url", $max_items = -1) { + /* + * $filename = file to process for example blocklist like DROP: http://www.spamhaus.org/drop/drop.txt + * $type = if set to 'url' then subnets and ips will be returned, + * if set to 'url_ports' port-ranges and ports will be returned + * $max_items = sets the maximum amount of valid items to load, -1 the default defines there is no limit. + * + * RETURNS an array of ip subnets and ip's or ports and port-ranges, returns NULL upon a error conditions (file not found) + */ + + $fd = @fopen($filename, 'r'); + if (!$fd) { + log_error(gettext("Could not process aliases from alias: {$alias_url}")); + return null; + } + $items = array(); + /* NOTE: fgetss() is not a typo RTFM before being smart */ + while (($fc = fgetss($fd)) !== FALSE) { + $tmp = trim($fc, " \t\n\r"); + if (empty($tmp)) { + continue; + } + $tmp_str = strstr($tmp, '#', true); + if (!empty($tmp_str)) { + $tmp = $tmp_str; + } + $tmp_str = strstr($tmp, ' ', true); + if (!empty($tmp_str)) { + $tmp = $tmp_str; + } + $valid = ($type == "url" && (is_ipaddr($tmp) || is_subnet($tmp))) || + ($type == "url_ports" && (is_port($tmp) || is_portrange($tmp))); + if ($valid) { + $items[] = $tmp; + if (count($items) == $max_items) { + break; + } + } + } + fclose($fd); + return $items; +} + +function update_alias_url_data() { + global $config, $g; + + $updated = false; + + /* item is a url type */ + $lockkey = lock('aliasurl'); + if (is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $x => $alias) { + if (empty($alias['aliasurl'])) { + continue; + } + + $address = null; + foreach ($alias['aliasurl'] as $alias_url) { + /* fetch down and add in */ + $temp_filename = tempnam("{$g['tmp_path']}/", "alias_import"); + unlink($temp_filename); + $verify_ssl = isset($config['system']['checkaliasesurlcert']); + mkdir($temp_filename); + download_file($alias_url, $temp_filename . "/aliases", $verify_ssl); + + /* if the item is tar gzipped then extract */ + if (stripos($alias_url, '.tgz')) { + if (!process_alias_tgz($temp_filename)) { + continue; + } + } else if (stripos($alias_url, '.zip')) { + if (!process_alias_unzip($temp_filename)) { + continue; + } + } + if (file_exists("{$temp_filename}/aliases")) { + $address = parse_aliases_file("{$temp_filename}/aliases", $alias['type'], 3000); + mwexec("/bin/rm -rf {$temp_filename}"); + } + } + if ($address != null) { + $config['aliases']['alias'][$x]['address'] = implode(" ", $address); + $updated = true; + } + } + } + unlock($lockkey); + + /* Report status to callers as well */ + return $updated; +} + +function process_alias_unzip($temp_filename) { + if (!file_exists("/usr/local/bin/unzip")) { + log_error(gettext("Alias archive is a .zip file which cannot be decompressed because utility is missing!")); + return false; + } + rename("{$temp_filename}/aliases", "{$temp_filename}/aliases.zip"); + mwexec("/usr/local/bin/unzip {$temp_filename}/aliases.tgz -d {$temp_filename}/aliases/"); + unlink("{$temp_filename}/aliases.zip"); + $files_to_process = return_dir_as_array("{$temp_filename}/"); + /* foreach through all extracted files and build up aliases file */ + $fd = @fopen("{$temp_filename}/aliases", "w"); + if (!$fd) { + log_error(gettext("Could not open {$temp_filename}/aliases for writing!")); + return false; + } + foreach ($files_to_process as $f2p) { + $tmpfd = @fopen($f2p, 'r'); + if (!$tmpfd) { + log_error(gettext("The following file could not be read {$f2p} from {$temp_filename}")); + continue; + } + while (($tmpbuf = fread($tmpfd, 65536)) !== FALSE) { + fwrite($fd, $tmpbuf); + } + fclose($tmpfd); + unlink($f2p); + } + fclose($fd); + unset($tmpbuf); + + return true; +} + +function process_alias_tgz($temp_filename) { + if (!file_exists('/usr/bin/tar')) { + log_error(gettext("Alias archive is a .tar/tgz file which cannot be decompressed because utility is missing!")); + return false; + } + rename("{$temp_filename}/aliases", "{$temp_filename}/aliases.tgz"); + mwexec("/usr/bin/tar xzf {$temp_filename}/aliases.tgz -C {$temp_filename}/aliases/"); + unlink("{$temp_filename}/aliases.tgz"); + $files_to_process = return_dir_as_array("{$temp_filename}/"); + /* foreach through all extracted files and build up aliases file */ + $fd = @fopen("{$temp_filename}/aliases", "w"); + if (!$fd) { + log_error(gettext("Could not open {$temp_filename}/aliases for writing!")); + return false; + } + foreach ($files_to_process as $f2p) { + $tmpfd = @fopen($f2p, 'r'); + if (!$tmpfd) { + log_error(gettext("The following file could not be read {$f2p} from {$temp_filename}")); + continue; + } + while (($tmpbuf = fread($tmpfd, 65536)) !== FALSE) { + fwrite($fd, $tmpbuf); + } + fclose($tmpfd); + unlink($f2p); + } + fclose($fd); + unset($tmpbuf); + + return true; +} + +function version_compare_dates($a, $b) { + $a_time = strtotime($a); + $b_time = strtotime($b); + + if ((!$a_time) || (!$b_time)) { + return FALSE; + } else { + if ($a_time < $b_time) { + return -1; + } elseif ($a_time == $b_time) { + return 0; + } else { + return 1; + } + } +} +function version_get_string_value($a) { + $strs = array( + 0 => "ALPHA-ALPHA", + 2 => "ALPHA", + 3 => "BETA", + 4 => "B", + 5 => "C", + 6 => "D", + 7 => "RC", + 8 => "RELEASE", + 9 => "*" // Matches all release levels + ); + $major = 0; + $minor = 0; + foreach ($strs as $num => $str) { + if (substr($a, 0, strlen($str)) == $str) { + $major = $num; + $n = substr($a, strlen($str)); + if (is_numeric($n)) { + $minor = $n; + } + break; + } + } + return "{$major}.{$minor}"; +} +function version_compare_string($a, $b) { + // Only compare string parts if both versions give a specific release + // (If either version lacks a string part, assume intended to match all release levels) + if (isset($a) && isset($b)) { + return version_compare_numeric(version_get_string_value($a), version_get_string_value($b)); + } else { + return 0; + } +} +function version_compare_numeric($a, $b) { + $a_arr = explode('.', rtrim($a, '.0')); + $b_arr = explode('.', rtrim($b, '.0')); + + foreach ($a_arr as $n => $val) { + if (array_key_exists($n, $b_arr)) { + // So far so good, both have values at this minor version level. Compare. + if ($val > $b_arr[$n]) { + return 1; + } elseif ($val < $b_arr[$n]) { + return -1; + } + } else { + // a is greater, since b doesn't have any minor version here. + return 1; + } + } + if (count($b_arr) > count($a_arr)) { + // b is longer than a, so it must be greater. + return -1; + } else { + // Both a and b are of equal length and value. + return 0; + } +} +function pfs_version_compare($cur_time, $cur_text, $remote) { + // First try date compare + $v = version_compare_dates($cur_time, $remote); + if ($v === FALSE) { + // If that fails, try to compare by string + // Before anything else, simply test if the strings are equal + if (($cur_text == $remote) || ($cur_time == $remote)) { + return 0; + } + list($cur_num, $cur_str) = explode('-', $cur_text); + list($rem_num, $rem_str) = explode('-', $remote); + + // First try to compare the numeric parts of the version string. + $v = version_compare_numeric($cur_num, $rem_num); + + // If the numeric parts are the same, compare the string parts. + if ($v == 0) { + return version_compare_string($cur_str, $rem_str); + } + } + return $v; +} +function process_alias_urltable($name, $url, $freq, $forceupdate=false) { + global $config; + + $urltable_prefix = "/var/db/aliastables/"; + $urltable_filename = $urltable_prefix . $name . ".txt"; + + // Make the aliases directory if it doesn't exist + if (!file_exists($urltable_prefix)) { + mkdir($urltable_prefix); + } elseif (!is_dir($urltable_prefix)) { + unlink($urltable_prefix); + mkdir($urltable_prefix); + } + + // If the file doesn't exist or is older than update_freq days, fetch a new copy. + if (!file_exists($urltable_filename) || + ((time() - filemtime($urltable_filename)) > ($freq * 86400 - 90)) || + $forceupdate) { + + // Try to fetch the URL supplied + conf_mount_rw(); + unlink_if_exists($urltable_filename . ".tmp"); + $verify_ssl = isset($config['system']['checkaliasesurlcert']); + if (download_file($url, $urltable_filename . ".tmp", $verify_ssl)) { + mwexec("/usr/bin/sed -E 's/\;.*//g; /^[[:space:]]*($|#)/d' ". escapeshellarg($urltable_filename . ".tmp") . " > " . escapeshellarg($urltable_filename)); + if (alias_get_type($name) == "urltable_ports") { + $ports = explode("\n", str_replace("\r", "", file_get_contents($urltable_filename))); + $ports = group_ports($ports); + file_put_contents($urltable_filename, implode("\n", $ports)); + } + unlink_if_exists($urltable_filename . ".tmp"); + } else { + touch($urltable_filename); + } + conf_mount_ro(); + return true; + } else { + // File exists, and it doesn't need to be updated. + return -1; + } +} +function get_real_slice_from_glabel($label) { + $label = escapeshellarg($label); + return trim(`/sbin/glabel list | /usr/bin/grep -B2 ufs/{$label} | /usr/bin/head -n 1 | /usr/bin/cut -f3 -d' '`); +} +function nanobsd_get_boot_slice() { + return trim(`/sbin/mount | /usr/bin/grep pfsense | /usr/bin/cut -d'/' -f4 | /usr/bin/cut -d' ' -f1`); +} +function nanobsd_get_boot_drive() { + return trim(`/sbin/glabel list | /usr/bin/grep -B2 ufs/pfsense | /usr/bin/head -n 1 | /usr/bin/cut -f3 -d' ' | /usr/bin/cut -d's' -f1`); +} +function nanobsd_get_active_slice() { + $boot_drive = nanobsd_get_boot_drive(); + $active = trim(`gpart show $boot_drive | grep '\[active\]' | awk '{print $3;}'`); + + return "{$boot_drive}s{$active}"; +} +function nanobsd_get_size() { + return strtoupper(file_get_contents("/etc/nanosize.txt")); +} +function nanobsd_switch_boot_slice() { + global $SLICE, $OLDSLICE, $TOFLASH, $COMPLETE_PATH, $COMPLETE_BOOT_PATH; + global $GLABEL_SLICE, $UFS_ID, $OLD_UFS_ID, $BOOTFLASH; + global $BOOT_DEVICE, $REAL_BOOT_DEVICE, $BOOT_DRIVE, $ACTIVE_SLICE; + nanobsd_detect_slice_info(); + + if ($BOOTFLASH == $ACTIVE_SLICE) { + $slice = $TOFLASH; + } else { + $slice = $BOOTFLASH; + } + + for ($i = 0; $i < ob_get_level(); $i++) { + ob_end_flush(); + } + ob_implicit_flush(1); + if (strstr($slice, "s2")) { + $ASLICE="2"; + $AOLDSLICE="1"; + $AGLABEL_SLICE="pfsense1"; + $AUFS_ID="1"; + $AOLD_UFS_ID="0"; + } else { + $ASLICE="1"; + $AOLDSLICE="2"; + $AGLABEL_SLICE="pfsense0"; + $AUFS_ID="0"; + $AOLD_UFS_ID="1"; + } + $ATOFLASH="{$BOOT_DRIVE}s{$ASLICE}"; + $ACOMPLETE_PATH="{$BOOT_DRIVE}s{$ASLICE}a"; + $ABOOTFLASH="{$BOOT_DRIVE}s{$AOLDSLICE}"; + conf_mount_rw(); + set_single_sysctl("kern.geom.debugflags", "16"); + exec("gpart set -a active -i {$ASLICE} {$BOOT_DRIVE}"); + exec("/usr/sbin/boot0cfg -s {$ASLICE} -v /dev/{$BOOT_DRIVE}"); + // We can't update these if they are mounted now. + if ($BOOTFLASH != $slice) { + exec("/sbin/tunefs -L ${AGLABEL_SLICE} /dev/$ACOMPLETE_PATH"); + nanobsd_update_fstab($AGLABEL_SLICE, $ACOMPLETE_PATH, $AOLD_UFS_ID, $AUFS_ID); + } + set_single_sysctl("kern.geom.debugflags", "0"); + conf_mount_ro(); +} +function nanobsd_clone_slice() { + global $SLICE, $OLDSLICE, $TOFLASH, $COMPLETE_PATH, $COMPLETE_BOOT_PATH; + global $GLABEL_SLICE, $UFS_ID, $OLD_UFS_ID, $BOOTFLASH; + global $BOOT_DEVICE, $REAL_BOOT_DEVICE, $BOOT_DRIVE, $ACTIVE_SLICE; + nanobsd_detect_slice_info(); + + for ($i = 0; $i < ob_get_level(); $i++) { + ob_end_flush(); + } + ob_implicit_flush(1); + set_single_sysctl("kern.geom.debugflags", "16"); + exec("/bin/dd if=/dev/zero of=/dev/{$TOFLASH} bs=1m count=1"); + exec("/bin/dd if=/dev/{$BOOTFLASH} of=/dev/{$TOFLASH} bs=64k"); + exec("/sbin/tunefs -L {$GLABEL_SLICE} /dev/{$COMPLETE_PATH}"); + $status = nanobsd_update_fstab($GLABEL_SLICE, $COMPLETE_PATH, $OLD_UFS_ID, $UFS_ID); + set_single_sysctl("kern.geom.debugflags", "0"); + if ($status) { + return false; + } else { + return true; + } +} +function nanobsd_update_fstab($gslice, $complete_path, $oldufs, $newufs) { + $tmppath = "/tmp/{$gslice}"; + $fstabpath = "/tmp/{$gslice}/etc/fstab"; + + mkdir($tmppath); + exec("/sbin/fsck_ufs -y /dev/{$complete_path}"); + exec("/sbin/mount /dev/ufs/{$gslice} {$tmppath}"); + copy("/etc/fstab", $fstabpath); + + if (!file_exists($fstabpath)) { + $fstab = <<<EOF +/dev/ufs/{$gslice} / ufs ro,noatime 1 1 +/dev/ufs/cf /cf ufs ro,noatime 1 1 +EOF; + if (file_put_contents($fstabpath, $fstab)) { + $status = true; + } else { + $status = false; + } + } else { + $status = exec("sed -i \"\" \"s/pfsense{$oldufs}/pfsense{$newufs}/g\" {$fstabpath}"); + } + exec("/sbin/umount {$tmppath}"); + rmdir($tmppath); + + return $status; +} +function nanobsd_detect_slice_info() { + global $SLICE, $OLDSLICE, $TOFLASH, $COMPLETE_PATH, $COMPLETE_BOOT_PATH; + global $GLABEL_SLICE, $UFS_ID, $OLD_UFS_ID, $BOOTFLASH; + global $BOOT_DEVICE, $REAL_BOOT_DEVICE, $BOOT_DRIVE, $ACTIVE_SLICE; + + $BOOT_DEVICE=nanobsd_get_boot_slice(); + $REAL_BOOT_DEVICE=get_real_slice_from_glabel($BOOT_DEVICE); + $BOOT_DRIVE=nanobsd_get_boot_drive(); + $ACTIVE_SLICE=nanobsd_get_active_slice(); + + // Detect which slice is active and set information. + if (strstr($REAL_BOOT_DEVICE, "s1")) { + $SLICE="2"; + $OLDSLICE="1"; + $GLABEL_SLICE="pfsense1"; + $UFS_ID="1"; + $OLD_UFS_ID="0"; + + } else { + $SLICE="1"; + $OLDSLICE="2"; + $GLABEL_SLICE="pfsense0"; + $UFS_ID="0"; + $OLD_UFS_ID="1"; + } + $TOFLASH="{$BOOT_DRIVE}s{$SLICE}"; + $COMPLETE_PATH="{$BOOT_DRIVE}s{$SLICE}a"; + $COMPLETE_BOOT_PATH="{$BOOT_DRIVE}s{$OLDSLICE}"; + $BOOTFLASH="{$BOOT_DRIVE}s{$OLDSLICE}"; +} + +function nanobsd_friendly_slice_name($slicename) { + global $g; + return strtolower(str_ireplace('pfsense', $g['product_name'], $slicename)); +} + +function get_include_contents($filename) { + if (is_file($filename)) { + ob_start(); + include $filename; + $contents = ob_get_contents(); + ob_end_clean(); + return $contents; + } + return false; +} + +/* This xml 2 array function is courtesy of the php.net comment section on xml_parse. + * it is roughly 4 times faster then our existing pfSense parser but due to the large + * size of the RRD xml dumps this is required. + * The reason we do not use it for pfSense is that it does not know about array fields + * which causes it to fail on array fields with single items. Possible Todo? + */ +function xml2array($contents, $get_attributes = 1, $priority = 'tag') { + if (!function_exists('xml_parser_create')) { + return array (); + } + $parser = xml_parser_create(''); + xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8"); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); + xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + xml_parse_into_struct($parser, trim($contents), $xml_values); + xml_parser_free($parser); + if (!$xml_values) { + return; //Hmm... + } + $xml_array = array (); + $parents = array (); + $opened_tags = array (); + $arr = array (); + $current = & $xml_array; + $repeated_tag_index = array (); + foreach ($xml_values as $data) { + unset ($attributes, $value); + extract($data); + $result = array (); + $attributes_data = array (); + if (isset ($value)) { + if ($priority == 'tag') { + $result = $value; + } else { + $result['value'] = $value; + } + } + if (isset ($attributes) and $get_attributes) { + foreach ($attributes as $attr => $val) { + if ($priority == 'tag') { + $attributes_data[$attr] = $val; + } else { + $result['attr'][$attr] = $val; //Set all the attributes in a array called 'attr' + } + } + } + if ($type == "open") { + $parent[$level -1] = & $current; + if (!is_array($current) or (!in_array($tag, array_keys($current)))) { + $current[$tag] = $result; + if ($attributes_data) { + $current[$tag . '_attr'] = $attributes_data; + } + $repeated_tag_index[$tag . '_' . $level] = 1; + $current = & $current[$tag]; + } else { + if (isset ($current[$tag][0])) { + $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result; + $repeated_tag_index[$tag . '_' . $level]++; + } else { + $current[$tag] = array ( + $current[$tag], + $result + ); + $repeated_tag_index[$tag . '_' . $level] = 2; + if (isset ($current[$tag . '_attr'])) { + $current[$tag]['0_attr'] = $current[$tag . '_attr']; + unset ($current[$tag . '_attr']); + } + } + $last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1; + $current = & $current[$tag][$last_item_index]; + } + } elseif ($type == "complete") { + if (!isset ($current[$tag])) { + $current[$tag] = $result; + $repeated_tag_index[$tag . '_' . $level] = 1; + if ($priority == 'tag' and $attributes_data) { + $current[$tag . '_attr'] = $attributes_data; + } + } else { + if (isset ($current[$tag][0]) and is_array($current[$tag])) { + $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result; + if ($priority == 'tag' and $get_attributes and $attributes_data) { + $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data; + } + $repeated_tag_index[$tag . '_' . $level]++; + } else { + $current[$tag] = array ( + $current[$tag], + $result + ); + $repeated_tag_index[$tag . '_' . $level] = 1; + if ($priority == 'tag' and $get_attributes) { + if (isset ($current[$tag . '_attr'])) { + $current[$tag]['0_attr'] = $current[$tag . '_attr']; + unset ($current[$tag . '_attr']); + } + if ($attributes_data) { + $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data; + } + } + $repeated_tag_index[$tag . '_' . $level]++; //0 and 1 index is already taken + } + } + } elseif ($type == 'close') { + $current = & $parent[$level -1]; + } + } + return ($xml_array); +} + +function get_country_name($country_code) { + if ($country_code != "ALL" && strlen($country_code) != 2) { + return ""; + } + + $country_names_xml = "/usr/local/share/mobile-broadband-provider-info/iso_3166-1_list_en.xml"; + $country_names_contents = file_get_contents($country_names_xml); + $country_names = xml2array($country_names_contents); + + if ($country_code == "ALL") { + $country_list = array(); + foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) { + $country_list[] = array( + "code" => $country['ISO_3166-1_Alpha-2_Code_element'], + "name" => ucwords(strtolower($country['ISO_3166-1_Country_name']))); + } + return $country_list; + } + + foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) { + if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) { + return ucwords(strtolower($country['ISO_3166-1_Country_name'])); + } + } + return ""; +} + +/* sort by interface only, retain the original order of rules that apply to + the same interface */ +function filter_rules_sort() { + global $config; + + /* mark each rule with the sequence number (to retain the order while sorting) */ + for ($i = 0; isset($config['filter']['rule'][$i]); $i++) { + $config['filter']['rule'][$i]['seq'] = $i; + } + + usort($config['filter']['rule'], "filter_rules_compare"); + + /* strip the sequence numbers again */ + for ($i = 0; isset($config['filter']['rule'][$i]); $i++) { + unset($config['filter']['rule'][$i]['seq']); + } +} +function filter_rules_compare($a, $b) { + if (isset($a['floating']) && isset($b['floating'])) { + return $a['seq'] - $b['seq']; + } else if (isset($a['floating'])) { + return -1; + } else if (isset($b['floating'])) { + return 1; + } else if ($a['interface'] == $b['interface']) { + return $a['seq'] - $b['seq']; + } else { + return compare_interface_friendly_names($a['interface'], $b['interface']); + } +} + +function generate_ipv6_from_mac($mac) { + $elements = explode(":", $mac); + if (count($elements) <> 6) { + return false; + } + + $i = 0; + $ipv6 = "fe80::"; + foreach ($elements as $byte) { + if ($i == 0) { + $hexadecimal = substr($byte, 1, 2); + $bitmap = base_convert($hexadecimal, 16, 2); + $bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT); + $bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3,4); + $byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16); + } + $ipv6 .= $byte; + if ($i == 1) { + $ipv6 .= ":"; + } + if ($i == 3) { + $ipv6 .= ":"; + } + if ($i == 2) { + $ipv6 .= "ff:fe"; + } + + $i++; + } + return $ipv6; +} + +/****f* pfsense-utils/load_mac_manufacturer_table + * NAME + * load_mac_manufacturer_table + * INPUTS + * none + * RESULT + * returns associative array with MAC-Manufacturer pairs + ******/ +function load_mac_manufacturer_table() { + /* load MAC-Manufacture data from the file */ + $macs = false; + if (file_exists("/usr/local/share/nmap/nmap-mac-prefixes")) { + $macs=file("/usr/local/share/nmap/nmap-mac-prefixes"); + } + if ($macs) { + foreach ($macs as $line) { + if (preg_match('/([0-9A-Fa-f]{6}) (.*)$/', $line, $matches)) { + /* store values like this $mac_man['000C29']='VMware' */ + $mac_man["$matches[1]"]=$matches[2]; + } + } + return $mac_man; + } else { + return -1; + } + +} + +/****f* pfsense-utils/is_ipaddr_configured + * NAME + * is_ipaddr_configured + * INPUTS + * IP Address to check. + * If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip + * check_localip - if true then also check for matches with PPTP and LT2P addresses + * check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address + * cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr. + * If check_subnets is true and cidrprefix is specified, + * then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address + * RESULT + * returns true if the IP Address is configured and present on this device or overlaps a configured subnet. +*/ +function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") { + if (count(where_is_ipaddr_configured($ipaddr, $ignore_if, $check_localip, $check_subnets, $cidrprefix))) { + return true; + } + return false; +} + +/****f* pfsense-utils/where_is_ipaddr_configured + * NAME + * where_is_ipaddr_configured + * INPUTS + * IP Address to check. + * If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip + * check_localip - if true then also check for matches with PPTP and LT2P addresses + * check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address + * cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr. + * If check_subnets is true and cidrprefix is specified, + * then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address + * RESULT + * Returns an array of the interfaces 'if' plus IP address or subnet 'ip_or_subnet' that match or overlap the IP address to check. + * If there are no matches then an empty array is returned. +*/ +function where_is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") { + global $config; + + $where_configured = array(); + + $pos = strpos($ignore_if, '_virtualip'); + if ($pos !== false) { + $ignore_vip_id = substr($ignore_if, $pos+10); + $ignore_vip_if = substr($ignore_if, 0, $pos); + } else { + $ignore_vip_id = -1; + $ignore_vip_if = $ignore_if; + } + + $isipv6 = is_ipaddrv6($ipaddr); + + if ($check_subnets) { + $cidrprefix = intval($cidrprefix); + if ($isipv6) { + if (($cidrprefix < 1) || ($cidrprefix > 128)) { + $cidrprefix = 128; + } + } else { + if (($cidrprefix < 1) || ($cidrprefix > 32)) { + $cidrprefix = 32; + } + } + $iflist = get_configured_interface_list(); + foreach ($iflist as $if => $ifname) { + if ($ignore_if == $if) { + continue; + } + + if ($isipv6) { + $if_ipv6 = get_interface_ipv6($if); + $if_snbitsv6 = get_interface_subnetv6($if); + if ($if_ipv6 && $if_snbitsv6 && check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) { + $where_entry = array(); + $where_entry['if'] = $if; + $where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if); + $where_configured[] = $where_entry; + } + } else { + $if_ipv4 = get_interface_ip($if); + $if_snbitsv4 = get_interface_subnet($if); + if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) { + $where_entry = array(); + $where_entry['if'] = $if; + $where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if); + $where_configured[] = $where_entry; + } + } + } + } else { + if ($isipv6) { + $interface_list_ips = get_configured_ipv6_addresses(); + } else { + $interface_list_ips = get_configured_ip_addresses(); + } + + foreach ($interface_list_ips as $if => $ilips) { + if ($ignore_if == $if) { + continue; + } + if (strcasecmp($ipaddr, $ilips) == 0) { + $where_entry = array(); + $where_entry['if'] = $if; + $where_entry['ip_or_subnet'] = $ilips; + $where_configured[] = $where_entry; + } + } + } + + $interface_list_vips = get_configured_vips_list(true); + foreach ($interface_list_vips as $id => $vip) { + /* Skip CARP interfaces here since they were already checked above */ + if ($id == $ignore_vip_id || (substr($ignore_if, 0, 4) == '_vip') && substr($ignore_vip_if, 5) == $vip['uniqdid']) { + continue; + } + if (strcasecmp($ipaddr, $vip['ipaddr']) == 0) { + $where_entry = array(); + $where_entry['if'] = $vip['if']; + $where_entry['ip_or_subnet'] = $vip['ipaddr']; + $where_configured[] = $where_entry; + } + } + + if ($check_localip) { + if (is_array($config['pptpd']) && !empty($config['pptpd']['localip']) && (strcasecmp($ipaddr, $config['pptpd']['localip']) == 0)) { + $where_entry = array(); + $where_entry['if'] = 'pptp'; + $where_entry['ip_or_subnet'] = $config['pptpd']['localip']; + $where_configured[] = $where_entry; + } + + if (!is_array($config['l2tp']) && !empty($config['l2tp']['localip']) && (strcasecmp($ipaddr, $config['l2tp']['localip']) == 0)) { + $where_entry = array(); + $where_entry['if'] = 'l2tp'; + $where_entry['ip_or_subnet'] = $config['l2tp']['localip']; + $where_configured[] = $where_entry; + } + } + + return $where_configured; +} + +/****f* pfsense-utils/pfSense_handle_custom_code + * NAME + * pfSense_handle_custom_code + * INPUTS + * directory name to process + * RESULT + * globs the directory and includes the files + */ +function pfSense_handle_custom_code($src_dir) { + // Allow extending of the nat edit page and include custom input validation + if (is_dir("$src_dir")) { + $cf = glob($src_dir . "/*.inc"); + foreach ($cf as $nf) { + if ($nf == "." || $nf == "..") { + continue; + } + // Include the extra handler + include("$nf"); + } + } +} + +function set_language($lang = 'en_US', $encoding = "UTF-8") { + putenv("LANG={$lang}.{$encoding}"); + setlocale(LC_ALL, "{$lang}.{$encoding}"); + textdomain("pfSense"); + bindtextdomain("pfSense","/usr/local/share/locale"); + bind_textdomain_codeset("pfSense","{$lang}.{$encoding}"); +} + +function get_locale_list() { + $locales = array( + "en_US" => gettext("English"), + "pt_BR" => gettext("Portuguese (Brazil)"), + "tr" => gettext("Turkish"), + ); + asort($locales); + return $locales; +} + +function system_get_language_code() { + global $config, $g_languages; + + // a language code, as per [RFC3066] + $language = $config['system']['language']; + //$code = $g_languages[$language]['code']; + $code = str_replace("_", "-", $language); + + if (empty($code)) { + $code = "en-US"; // Set default code. + } + + return $code; +} + +function system_get_language_codeset() { + global $config, $g_languages; + + $language = $config['system']['language']; + $codeset = $g_languages[$language]['codeset']; + + if (empty($codeset)) { + $codeset = "UTF-8"; // Set default codeset. + } + + return $codeset; +} + +/* Available languages/locales */ +$g_languages = array ( + "sq" => array("codeset" => "UTF-8", "desc" => gettext("Albanian")), + "bg" => array("codeset" => "UTF-8", "desc" => gettext("Bulgarian")), + "zh_CN" => array("codeset" => "UTF-8", "desc" => gettext("Chinese (Simplified)")), + "zh_TW" => array("codeset" => "UTF-8", "desc" => gettext("Chinese (Traditional)")), + "nl" => array("codeset" => "UTF-8", "desc" => gettext("Dutch")), + "da" => array("codeset" => "UTF-8", "desc" => gettext("Danish")), + "en_US" => array("codeset" => "UTF-8", "desc" => gettext("English")), + "fi" => array("codeset" => "UTF-8", "desc" => gettext("Finnish")), + "fr" => array("codeset" => "UTF-8", "desc" => gettext("French")), + "de" => array("codeset" => "UTF-8", "desc" => gettext("German")), + "el" => array("codeset" => "UTF-8", "desc" => gettext("Greek")), + "hu" => array("codeset" => "UTF-8", "desc" => gettext("Hungarian")), + "it" => array("codeset" => "UTF-8", "desc" => gettext("Italian")), + "ja" => array("codeset" => "UTF-8", "desc" => gettext("Japanese")), + "ko" => array("codeset" => "UTF-8", "desc" => gettext("Korean")), + "lv" => array("codeset" => "UTF-8", "desc" => gettext("Latvian")), + "nb" => array("codeset" => "UTF-8", "desc" => gettext("Norwegian (Bokmal)")), + "pl" => array("codeset" => "UTF-8", "desc" => gettext("Polish")), + "pt_BR" => array("codeset" => "UTF-8", "desc" => gettext("Portuguese (Brazil)")), + "pt" => array("codeset" => "UTF-8", "desc" => gettext("Portuguese (Portugal)")), + "ro" => array("codeset" => "UTF-8", "desc" => gettext("Romanian")), + "ru" => array("codeset" => "UTF-8", "desc" => gettext("Russian")), + "sl" => array("codeset" => "UTF-8", "desc" => gettext("Slovenian")), + "tr" => array("codeset" => "UTF-8", "desc" => gettext("Turkish")), + "es" => array("codeset" => "UTF-8", "desc" => gettext("Spanish")), + "sv" => array("codeset" => "UTF-8", "desc" => gettext("Swedish")), + "sk" => array("codeset" => "UTF-8", "desc" => gettext("Slovak")), + "cs" => array("codeset" => "UTF-8", "desc" => gettext("Czech")) +); + +function return_hex_ipv4($ipv4) { + if (!is_ipaddrv4($ipv4)) { + return(false); + } + + /* we need the hex form of the interface IPv4 address */ + $ip4arr = explode(".", $ipv4); + return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3])); +} + +function convert_ipv6_to_128bit($ipv6) { + if (!is_ipaddrv6($ipv6)) { + return(false); + } + + $ip6arr = array(); + $ip6prefix = Net_IPv6::uncompress($ipv6); + $ip6arr = explode(":", $ip6prefix); + /* binary presentation of the prefix for all 128 bits. */ + $ip6prefixbin = ""; + foreach ($ip6arr as $element) { + $ip6prefixbin .= sprintf("%016b", hexdec($element)); + } + return($ip6prefixbin); +} + +function convert_128bit_to_ipv6($ip6bin) { + if (strlen($ip6bin) <> 128) { + return(false); + } + + $ip6arr = array(); + $ip6binarr = array(); + $ip6binarr = str_split($ip6bin, 16); + foreach ($ip6binarr as $binpart) { + $ip6arr[] = dechex(bindec($binpart)); + } + $ip6addr = Net_IPv6::compress(implode(":", $ip6arr)); + + return($ip6addr); +} + + +/* Returns the calculated bit length of the prefix delegation from the WAN interface */ +/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */ +/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */ +/* 6to4 is 16 bits, e.g. 65535 */ +function calculate_ipv6_delegation_length($if) { + global $config; + + if (!is_array($config['interfaces'][$if])) { + return false; + } + + switch ($config['interfaces'][$if]['ipaddrv6']) { + case "6to4": + $pdlen = 16; + break; + case "6rd": + $rd6cfg = $config['interfaces'][$if]; + $rd6plen = explode("/", $rd6cfg['prefix-6rd']); + $pdlen = (64 - ($rd6plen[1] + (32 - $rd6cfg['prefix-6rd-v4plen']))); + break; + case "dhcp6": + $dhcp6cfg = $config['interfaces'][$if]; + $pdlen = $dhcp6cfg['dhcp6-ia-pd-len']; + break; + default: + $pdlen = 0; + break; + } + return($pdlen); +} + +function huawei_rssi_to_string($rssi) { + $dbm = array(); + $i = 0; + $dbstart = -113; + while ($i < 32) { + $dbm[$i] = $dbstart + ($i * 2); + $i++; + } + $percent = round(($rssi / 31) * 100); + $string = "rssi:{$rssi} level:{$dbm[$rssi]}dBm percent:{$percent}%"; + return $string; +} + +function huawei_mode_to_string($mode, $submode) { + $modes[0] = "None"; + $modes[1] = "AMPS"; + $modes[2] = "CDMA"; + $modes[3] = "GSM/GPRS"; + $modes[4] = "HDR"; + $modes[5] = "WCDMA"; + $modes[6] = "GPS"; + + $submodes[0] = "No Service"; + $submodes[1] = "GSM"; + $submodes[2] = "GPRS"; + $submodes[3] = "EDGE"; + $submodes[4] = "WCDMA"; + $submodes[5] = "HSDPA"; + $submodes[6] = "HSUPA"; + $submodes[7] = "HSDPA+HSUPA"; + $submodes[8] = "TD-SCDMA"; + $submodes[9] = "HSPA+"; + $string = "{$modes[$mode]}, {$submodes[$submode]} Mode"; + return $string; +} + +function huawei_service_to_string($state) { + $modes[0] = "No"; + $modes[1] = "Restricted"; + $modes[2] = "Valid"; + $modes[3] = "Restricted Regional"; + $modes[4] = "Powersaving"; + $string = "{$modes[$state]} Service"; + return $string; +} + +function huawei_simstate_to_string($state) { + $modes[0] = "Invalid SIM/locked"; + $modes[1] = "Valid SIM"; + $modes[2] = "Invalid SIM CS"; + $modes[3] = "Invalid SIM PS"; + $modes[4] = "Invalid SIM CS/PS"; + $modes[255] = "Missing SIM"; + $string = "{$modes[$state]} State"; + return $string; +} + +function zte_rssi_to_string($rssi) { + return huawei_rssi_to_string($rssi); +} + +function zte_mode_to_string($mode, $submode) { + $modes[0] = "No Service"; + $modes[1] = "Limited Service"; + $modes[2] = "GPRS"; + $modes[3] = "GSM"; + $modes[4] = "UMTS"; + $modes[5] = "EDGE"; + $modes[6] = "HSDPA"; + + $submodes[0] = "CS_ONLY"; + $submodes[1] = "PS_ONLY"; + $submodes[2] = "CS_PS"; + $submodes[3] = "CAMPED"; + $string = "{$modes[$mode]}, {$submodes[$submode]} Mode"; + return $string; +} + +function zte_service_to_string($state) { + $modes[0] = "Initializing"; + $modes[1] = "Network Lock error"; + $modes[2] = "Network Locked"; + $modes[3] = "Unlocked or correct MCC/MNC"; + $string = "{$modes[$state]} Service"; + return $string; +} + +function zte_simstate_to_string($state) { + $modes[0] = "No action"; + $modes[1] = "Network lock"; + $modes[2] = "(U)SIM card lock"; + $modes[3] = "Network Lock and (U)SIM card Lock"; + $string = "{$modes[$state]} State"; + return $string; +} + +function get_configured_pppoe_server_interfaces() { + global $config; + $iflist = array(); + if (is_array($config['pppoes']['pppoe'])) { + foreach ($config['pppoes']['pppoe'] as $pppoe) { + if ($pppoe['mode'] == "server") { + $int = "poes". $pppoe['pppoeid']; + $iflist[$int] = strtoupper($int); + } + } + } + return $iflist; +} + +function get_pppoes_child_interfaces($ifpattern) { + $if_arr = array(); + if ($ifpattern == "") { + return; + } + + exec("ifconfig", $out, $ret); + foreach ($out as $line) { + if (preg_match("/^({$ifpattern}[0-9]+):/i", $line, $match)) { + $if_arr[] = $match[1]; + } + } + return $if_arr; + +} + +/****f* pfsense-utils/pkg_call_plugins + * NAME + * pkg_call_plugins + * INPUTS + * $plugin_type value used to search in package configuration if the plugin is used, also used to create the function name + * $plugin_params parameters to pass to the plugin function for passing multiple parameters a array can be used. + * RESULT + * returns associative array results from the plugin calls for each package + * NOTES + * This generic function can be used to notify or retrieve results from functions that are defined in packages. + ******/ +function pkg_call_plugins($plugin_type, $plugin_params) { + global $g, $config; + $results = array(); + if (!is_array($config['installedpackages']['package'])) { + return $results; + } + foreach ($config['installedpackages']['package'] as $package) { + if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) { + continue; + } + $pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $package['configurationfile'], 'packagegui'); + $pkgname = substr(reverse_strrchr($package['configurationfile'], "."),0,-1); + if (is_array($pkg_config['plugins']['item'])) { + foreach ($pkg_config['plugins']['item'] as $plugin) { + if ($plugin['type'] == $plugin_type) { + if (file_exists($pkg_config['include_file'])) { + require_once($pkg_config['include_file']); + } else { + continue; + } + $plugin_function = $pkgname . '_'. $plugin_type; + $results[$pkgname] = call_user_func($plugin_function, $plugin_params); + } + } + } + } + return $results; +} + +/* Function to find and return the active XML RPC base URL to avoid code duplication */ +function get_active_xml_rpc_base_url() { + global $config, $g; + /* If the user has activated the option to enable an alternate xmlrpcbaseurl, and it's not empty, then use it */ + if (isset($config['system']['altpkgrepo']['enable']) && !empty($config['system']['altpkgrepo']['xmlrpcbaseurl'])) { + return $config['system']['altpkgrepo']['xmlrpcbaseurl']; + } else { + return $g['xmlrpcbaseurl']; + } +} + +?> diff --git a/src/etc/inc/pkg-utils.inc b/src/etc/inc/pkg-utils.inc new file mode 100644 index 0000000..d18efe8 --- /dev/null +++ b/src/etc/inc/pkg-utils.inc @@ -0,0 +1,953 @@ +<?php +/****h* pfSense/pkg-utils + NAME + pkg-utils.inc - Package subsystem + DESCRIPTION + This file contains various functions used by the pfSense package system. + HISTORY + $Id$ + + Copyright (C) 2010 Ermal Luçi + Copyright (C) 2005-2006 Colin Smith (ethethlay@gmail.com) + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/cd /usr/bin/tar /usr/sbin/fifolog_create /bin/chmod + pfSense_BUILDER_BINARIES: /usr/sbin/pkg_add /usr/sbin/pkg_info /usr/sbin/pkg_delete /bin/rm + pfSense_MODULE: pkg +*/ + +require_once("globals.inc"); +require_once("service-utils.inc"); + +if (file_exists("/cf/conf/use_xmlreader")) { + require_once("xmlreader.inc"); +} else { + require_once("xmlparse.inc"); +} + +require_once("pfsense-utils.inc"); + +if (!function_exists("pkg_debug")) { + /* set up logging if needed */ + function pkg_debug($msg) { + global $g, $debug, $fd_log; + + if (!$debug) { + return; + } + + if (!$fd_log) { + if (!$fd_log = fopen("{$g['tmp_path']}/pkg_mgr_debug.log", "w")) { + update_output_window("Warning, could not open log for writing."); + } + } + @fwrite($fd_log, $msg); + } +} + +global $g; +if (!isset($g['platform'])) { + $g['platform'] = trim(file_get_contents("/etc/platform")); +} + +/* Remove pkg_prefix from package name if it's present */ +function pkg_remove_prefix(&$pkg_name) { + global $g; + + if (substr($pkg_name, 0, strlen($g['pkg_prefix'])) == $g['pkg_prefix']) { + $pkg_name = substr($pkg_name, strlen($g['pkg_prefix'])); + } +} + +/* Execute a pkg call */ +function pkg_call($params, $mute = false) { + global $static_output, $g, $config; + + if (empty($params)) { + return false; + } + + $user_agent = $g['product_name'] . '/' . $g['product_version']; + if (!isset($config['system']['host_uuid'])) { + $user_agent .= ' : ' . get_single_sysctl('kern.hostuuid'); + } + + $env = array( + "HTTP_USER_AGENT" => $user_agent, + "ASSUME_ALWAYS_YES" => "true" + ); + + $debug_fifo = $g['tmp_path'] . "/pkg-debug.fifo"; + if (!file_exists($debug_fifo)) { + posix_mkfifo($debug_fifo, 0600); + } + + if (filetype($debug_fifo) == 'fifo') { + $env["EVENT_PIPE"] = $debug_fifo; + } + + $descriptorspec = array( + 1 => array("pipe", "w"), /* stdout */ + 2 => array("pipe", "w") /* stderr */ + ); + + pkg_debug("pkg_call(): {$params}\n"); + $process = proc_open("/usr/sbin/pkg {$params}", $descriptorspec, $pipes, '/', $env); + + if (!is_resource($process)) { + return false; + } + + stream_set_blocking($pipes[1], 0); + stream_set_blocking($pipes[2], 0); + + /* XXX: should be a tunnable? */ + $timeout = 300; // seconds + $error_log = ''; + + do { + $write = array(); + $read = array($pipes[1], $pipes[2]); + $except = array(); + + $stream = stream_select($read, $write, $except, null, $timeout); + if ($stream !== FALSE && $stream > 0) { + foreach ($read as $pipe) { + $content = stream_get_contents($pipe); + if ($content == '') { + continue; + } + if ($pipe === $pipes[1]) { + if (!$mute) { + $static_output .= $content; + update_output_window($static_output); + } + flush(); + } else if ($pipe === $pipes[2]) { + $error_log .= $content; + } + } + } + $status = proc_get_status($process); + } while ($status['running']); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + $rc = $status['exitcode']; + + pkg_debug("pkg_call(): rc = {$rc}\n"); + if ($rc == 0) { + return true; + } + + pkg_debug("pkg_call(): error_log\n{$error_log}\n"); + if (!$mute) { + $static_output .= "\n\n" . sprintf(gettext("ERROR!!! An error occurred on pkg execution (rc = %d) with parameters '%s':"), $rc, $params) . "\n" . $error_log; + update_output_window($static_output); + } + return false; +} + +/* Execute pkg with $params, fill stdout and stderr and return pkg rc */ +function pkg_exec($params, &$stdout, &$stderr) { + global $g, $config; + + if (empty($params)) { + return -1; + } + + $user_agent = $g['product_name'] . '/' . $g['product_version']; + if (!isset($config['system']['host_uuid'])) { + $user_agent .= ' : ' . get_single_sysctl('kern.hostuuid'); + } + + $env = array( + "HTTP_USER_AGENT" => $user_agent, + "ASSUME_ALWAYS_YES" => "true" + ); + + $debug_fifo = $g['tmp_path'] . "/pkg-debug.fifo"; + if (!file_exists($debug_fifo)) { + posix_mkfifo($debug_fifo, 0600); + } + + if (filetype($debug_fifo) == 'fifo') { + $env["EVENT_PIPE"] = $debug_fifo; + } + + $descriptorspec = array( + 1 => array("pipe", "w"), /* stdout */ + 2 => array("pipe", "w") /* stderr */ + ); + + pkg_debug("pkg_exec(): {$params}\n"); + $process = proc_open("/usr/sbin/pkg {$params}", $descriptorspec, $pipes, '/', $env); + + if (!is_resource($process)) { + return -1; + } + + $stdout = ''; + while (($l = fgets($pipes[1])) !== FALSE) { + $stdout .= $l; + } + fclose($pipes[1]); + + $stderr = ''; + while (($l = fgets($pipes[2])) !== FALSE) { + $stderr .= $l; + } + fclose($pipes[2]); + + return proc_close($process); +} + +/* Check if package is installed */ +function is_pkg_installed($pkg_name) { + global $g; + + pkg_remove_prefix($pkg_name); + + return pkg_call("info -e " . $g['pkg_prefix'] . $pkg_name, true); +} + +/* Install package, $pkg_name should not contain prefix */ +function pkg_install($pkg_name) { + global $g; + $result = false; + + pkg_remove_prefix($pkg_name); + + pkg_debug("Installing package {$pkg_name}\n"); + if (!is_pkg_installed($pkg_name)) { + $result = pkg_call("install -y " . $g['pkg_prefix'] . $pkg_name); + /* Cleanup cacke to free disk space */ + pkg_call("clean -y"); + } + + return $result; +} + +/* Delete package from FreeBSD, $pkg_name should not contain prefix */ +function pkg_delete($pkg_name) { + global $g; + + pkg_remove_prefix($pkg_name); + + pkg_debug("Removing package {$pkg_name}\n"); + if (is_pkg_installed($pkg_name)) { + pkg_call("delete -y " . $g['pkg_prefix'] . $pkg_name); + /* Cleanup unecessary dependencies */ + pkg_call("autoremove -y"); + } +} + +/* Check if package is present in config.xml */ +function is_package_installed($package_name) { + return (get_package_id($package_name) != -1); +} + +/* Find package array index */ +function get_package_id($package_name) { + global $config; + + if (!is_array($config['installedpackages']['package'])) { + return -1; + } + + foreach ($config['installedpackages']['package'] as $idx => $pkg) { + if ($pkg['name'] == $package_name) { + return $idx; + } + } + + return -1; +} + +/* Keep backward compatibility since snort/suricata use this function */ +function get_pkg_id($package_name) { + return get_package_id($package_name); +} + +/* Return internal_name when it's defined, otherwise, returns name */ +function get_package_internal_name($package_data) { + if (isset($package_data['internal_name']) && ($package_data['internal_name'] != "")) { + /* e.g. name is Ipguard-dev, internal name is ipguard */ + return $package_data['internal_name']; + } else { + return $package_data['name']; + } +} + +/* Get information about packages */ +function get_pkg_info($pkgs = 'all', $info = 'all') { + global $g, $static_output; + + $out = ''; + $err = ''; + + if ($pkgs == 'all') { + $pkgs = $g['pkg_prefix']; + } + + /* Make sure repo metadata is up2date */ + $static_output .= "\n" . gettext("Updating package repository metadada...") . "\n"; + update_status($static_output); + if (!pkg_call("update")) { + $static_output .= "\n" . gettext("ERROR: An error occurred when updating packages repository. Aborting...") . "\n"; + update_status($static_output); + return array(); + } + + $rc = pkg_exec("search -U --raw-format json-compact " . $pkgs, $out, $err); + + if ($rc != 0) { + $static_output .= "\n" . gettext("ERROR: Error trying to get packages list. Aborting...") . "\n"; + $static_output .= $err; + update_status($static_output); + return array(); + } + + $result = array(); + $pkgs_info = explode("\n", $out); + foreach ($pkgs_info as $pkg_info_json) { + $pkg_info = json_decode($pkg_info_json, true); + if (!isset($pkg_info['name'])) { + continue; + } + + $result[] = $pkg_info; + unset($pkg_info); + } + + return $result; +} + +/* + * resync_all_package_configs() Force packages to setup their configuration and rc.d files. + * This function may also print output to the terminal indicating progress. + */ +function resync_all_package_configs($show_message = false) { + global $config, $pkg_interface, $g; + + log_error(gettext("Resyncing configuration for all packages.")); + + if (!is_array($config['installedpackages']['package'])) { + return; + } + + if ($show_message == true) { + echo "Syncing packages:"; + } + + conf_mount_rw(); + + foreach ($config['installedpackages']['package'] as $idx => $package) { + if (empty($package['name'])) { + continue; + } + if ($show_message == true) { + echo " " . $package['name']; + } + if (platform_booting() != true) { + stop_service(get_package_internal_name($package)); + } + sync_package($package['name']); + if ($pkg_interface == "console") { + echo "\n" . gettext("Syncing packages:"); + } + } + + if ($show_message == true) { + echo " done.\n"; + } + + @unlink("/conf/needs_package_sync"); + conf_mount_ro(); +} + +function uninstall_package($package_name) { + global $config, $static_output; + + $internal_name = $package_name; + $id = get_package_id($package_name); + if ($id >= 0) { + $internal_name = get_package_internal_name($config['installedpackages']['package'][$id]); + stop_service($internal_name); + } + + if (is_pkg_installed($internal_name)) { + $static_output .= "Removing package...\n"; + update_output_window($static_output); + pkg_delete($internal_name); + } else { + delete_package_xml($package_name); + } + + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); +} + +/* Run <custom_php_resync_config_command> */ +function sync_package($package_name) { + global $config, $builder_package_install; + + // If this code is being called by pfspkg_installer + // which the builder system uses then return (ignore). + if ($builder_package_install) { + return; + } + + if (empty($config['installedpackages']['package'])) { + return; + } + + if (($pkg_id = get_package_id($package_name)) == -1) { + return; // This package doesn't really exist - exit the function. + } + + if (!is_array($config['installedpackages']['package'][$pkg_id])) { + return; // No package belongs to the pkg_id passed to this function. + } + + $package =& $config['installedpackages']['package'][$pkg_id]; + if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) { + log_error(sprintf(gettext("The %s package is missing its configuration file and must be reinstalled."), $package['name'])); + delete_package_xml($package['name']); + return; + } + + $pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $package['configurationfile'], "packagegui"); + if (isset($pkg_config['nosync'])) { + return; + } + + /* Bring in package include files */ + if (!empty($pkg_config['include_file'])) { + $include_file = $pkg_config['include_file']; + if (file_exists($include_file)) { + require_once($include_file); + } else { + log_error("Reinstalling package {$package['name']} because its include file({$include_file}) is missing!"); + uninstall_package($package['name']); + if (install_package($package['name']) != 0) { + log_error("Reinstalling package {$package['name']} failed. Take appropriate measures!!!"); + return; + } + if (file_exists($include_file)) { + require_once($include_file); + } else { + return; + } + } + } + + if (!empty($pkg_config['custom_php_global_functions'])) { + eval($pkg_config['custom_php_global_functions']); + } + if (!empty($pkg_config['custom_php_resync_config_command'])) { + eval($pkg_config['custom_php_resync_config_command']); + } +} + +/* Read info.xml installed by package and return an array */ +function read_package_config($package_name) { + global $g; + + $pkg_info_xml = '/usr/local/share/' . $g['pkg_prefix'] . $package_name . '/info.xml'; + + if (!file_exists($pkg_info_xml)) { + return false; + } + + $pkg_info = parse_xml_config_pkg($pkg_info_xml, 'pfsensepkgs'); + + if (empty($pkg_info)) { + return false; + } + + /* it always returns an array with 1 item */ + return $pkg_info['package'][0]; +} + +function get_after_install_info($package_name) { + $pkg_config = read_package_config($package_name); + + if (isset($pkg_config['after_install_info'])) { + return $pkg_config['after_install_info']; + } + + return ''; +} + +function eval_once($toeval) { + global $evaled; + if (!$evaled) { + $evaled = array(); + } + $evalmd5 = md5($toeval); + if (!in_array($evalmd5, $evaled)) { + @eval($toeval); + $evaled[] = $evalmd5; + } + return; +} + +function install_package($package_name) { + global $g, $config, $static_output, $pkg_interface; + + if ($pkg_interface == "console") { + echo "\n"; + } + + return pkg_install($package_name); +} + +function install_package_xml($package_name) { + global $g, $config, $static_output, $pkg_interface; + + if ($pkg_interface == "console") { + echo "\n"; + } + + if (($pkg_info = read_package_config($package_name)) == false) { + return false; + } + + /* safe side. Write config below will send to ro again. */ + conf_mount_rw(); + + pkg_debug(gettext("Beginning package installation.") . "\n"); + log_error(sprintf(gettext('Beginning package installation for %s .'), $pkg_info['name'])); + $static_output .= sprintf(gettext("Beginning package installation for %s .\n"), $pkg_info['name']); + update_status($static_output); + + /* add package information to config.xml */ + $pkgid = get_package_id($pkg_info['name']); + $static_output .= gettext("Saving updated package information...") . " "; + update_output_window($static_output); + if ($pkgid == -1) { + $config['installedpackages']['package'][] = $pkg_info; + $changedesc = sprintf(gettext("Installed %s package."), $pkg_info['name']); + $to_output = gettext("done.") . "\n"; + } else { + $config['installedpackages']['package'][$pkgid] = $pkg_info; + $changedesc = sprintf(gettext("Overwrote previous installation of %s."), $pkg_info['name']); + $to_output = gettext("overwrite!") . "\n"; + } + unlink_if_exists('/conf/needs_package_sync'); + conf_mount_ro(); + write_config("Intermediate config write during package install for {$pkg_info['name']}."); + $static_output .= $to_output; + update_output_window($static_output); + + if (($pkgid = get_package_id($package_name)) == -1) { + $static_output .= sprintf(gettext("The %s package is not installed.%sInstallation aborted."), $package_name, "\n\n"); + update_output_window($static_output); + if ($pkg_interface <> "console") { + echo "\n<script>document.getElementById('progressbar').style.visibility='hidden';</script>"; + echo "\n<script>document.getElementById('progholder').style.visibility='hidden';</script>"; + } + + uninstall_package($package_name); + write_config($changedesc); + log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name'])); + $static_output .= gettext("Failed to install package.") . "\n"; + update_output_window($static_output); + return false; + } + + $configfile = substr(strrchr($pkg_info['config_file'], '/'), 1); + if (file_exists("/usr/local/pkg/" . $configfile)) { + $static_output .= gettext("Loading package configuration... "); + update_output_window($static_output); + $pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $configfile, "packagegui"); + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); + $static_output .= gettext("Configuring package components...\n"); + if (!empty($pkg_config['filter_rules_needed'])) { + $config['installedpackages']['package'][$pkgid]['filter_rule_function'] = $pkg_config['filter_rules_needed']; + } + update_output_window($static_output); + /* modify system files */ + + /* if a require exists, include it. this will + * show us where an error exists in a package + * instead of making us blindly guess + */ + $missing_include = false; + if ($pkg_config['include_file'] <> "") { + $static_output .= gettext("Loading package instructions...") . "\n"; + update_output_window($static_output); + if (file_exists($pkg_config['include_file'])) { + pkg_debug("require_once('{$pkg_config['include_file']}')\n"); + require_once($pkg_config['include_file']); + } else { + pkg_debug("Missing include {$pkg_config['include_file']}\n"); + $missing_include = true; + $static_output .= "Include " . basename($pkg_config['include_file']) . " is missing!\n"; + update_output_window($static_output); + + uninstall_package($package_name); + write_config($changedesc); + log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name'])); + $static_output .= gettext("Failed to install package.") . "\n"; + update_output_window($static_output); + return false; + } + } + + /* custom commands */ + $static_output .= gettext("Custom commands...") . "\n"; + update_output_window($static_output); + if ($missing_include == false) { + if ($pkg_config['custom_php_global_functions'] <> "") { + $static_output .= gettext("Executing custom_php_global_functions()..."); + update_output_window($static_output); + eval_once($pkg_config['custom_php_global_functions']); + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); + } + if ($pkg_config['custom_php_install_command']) { + $static_output .= gettext("Executing custom_php_install_command()..."); + update_output_window($static_output); + eval_once($pkg_config['custom_php_install_command']); + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); + } + if ($pkg_config['custom_php_resync_config_command'] <> "") { + $static_output .= gettext("Executing custom_php_resync_config_command()..."); + update_output_window($static_output); + eval_once($pkg_config['custom_php_resync_config_command']); + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); + } + } + /* sidebar items */ + if (is_array($pkg_config['menu'])) { + $static_output .= gettext("Menu items... "); + update_output_window($static_output); + foreach ($pkg_config['menu'] as $menu) { + if (is_array($config['installedpackages']['menu'])) { + foreach ($config['installedpackages']['menu'] as $amenu) { + if ($amenu['name'] == $menu['name']) { + continue 2; + } + } + } else { + $config['installedpackages']['menu'] = array(); + } + $config['installedpackages']['menu'][] = $menu; + } + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); + } + /* services */ + if (is_array($pkg_config['service'])) { + $static_output .= gettext("Services... "); + update_output_window($static_output); + foreach ($pkg_config['service'] as $service) { + if (is_array($config['installedpackages']['service'])) { + foreach ($config['installedpackages']['service'] as $aservice) { + if ($aservice['name'] == $service['name']) { + continue 2; + } + } + } else { + $config['installedpackages']['service'] = array(); + } + $config['installedpackages']['service'][] = $service; + } + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); + } + } else { + pkg_debug("Unable to find config file\n"); + $static_output .= gettext("Loading package configuration... failed!") . "\n\n" . gettext("Installation aborted."); + update_output_window($static_output); + pkg_debug(gettext("Unable to load package configuration. Installation aborted.") ."\n"); + if ($pkg_interface <> "console") { + echo "\n<script>document.getElementById('progressbar').style.visibility='hidden';</script>"; + echo "\n<script>document.getElementById('progholder').style.visibility='hidden';</script>"; + } + + uninstall_package($package_name); + write_config($changedesc); + log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name'])); + $static_output .= gettext("Failed to install package.") . "\n"; + update_output_window($static_output); + return false; + } + + /* set up package logging streams */ + if ($pkg_info['logging']) { + system_syslogd_start(); + } + + $static_output .= gettext("Writing configuration... "); + update_output_window($static_output); + write_config($changedesc); + log_error(sprintf(gettext("Successfully installed package: %s."), $pkg_info['name'])); + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); + if ($pkg_info['after_install_info']) { + update_output_window($pkg_info['after_install_info']); + } + + return true; +} + +function delete_package($package_name) { + global $config, $g, $static_output; + + if (!is_package_installed($package_name)) { + return; + } + + $static_output .= sprintf(gettext("Starting package deletion for %s..."), $package_name); + update_output_window($static_output); + + pkg_delete($package_name); + $static_output .= "done.\n"; + update_output_window($static_output); + + return; +} + +function delete_package_xml($package_name, $when = "post-deinstall") { + global $g, $config, $static_output, $pkg_interface; + + conf_mount_rw(); + + $pkgid = get_package_id($package_name); + if ($pkgid == -1) { + $static_output .= sprintf(gettext("The %s package is not installed.%sDeletion aborted."), $package_name, "\n\n"); + update_output_window($static_output); + if ($pkg_interface <> "console") { + echo "\n<script>document.getElementById('progressbar').style.visibility='hidden';</script>"; + echo "\n<script>document.getElementById('progholder').style.visibility='hidden';</script>"; + } + ob_flush(); + sleep(1); + conf_mount_ro(); + return; + } + pkg_debug(sprintf(gettext("Removing %s package... "), $package_name)); + $static_output .= sprintf(gettext("Removing %s components..."), $package_name) . "\n"; + update_output_window($static_output); + /* parse package configuration */ + $packages = &$config['installedpackages']['package']; + $menus =& $config['installedpackages']['menu']; + $services = &$config['installedpackages']['service']; + $pkg_info =& $packages[$pkgid]; + if (file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) { + $pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $packages[$pkgid]['configurationfile'], "packagegui"); + /* remove menu items */ + if (is_array($pkg_config['menu'])) { + $static_output .= gettext("Menu items... "); + update_output_window($static_output); + if (is_array($pkg_config['menu']) && is_array($menus)) { + foreach ($pkg_config['menu'] as $menu) { + foreach ($menus as $key => $instmenu) { + if ($instmenu['name'] == $menu['name']) { + unset($menus[$key]); + break; + } + } + } + } + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); + } + /* remove services */ + if (is_array($pkg_config['service'])) { + $static_output .= gettext("Services... "); + update_output_window($static_output); + if (is_array($pkg_config['service']) && is_array($services)) { + foreach ($pkg_config['service'] as $service) { + foreach ($services as $key => $instservice) { + if ($instservice['name'] == $service['name']) { + if (platform_booting() != true) { + stop_service($service['name']); + } + if ($service['rcfile']) { + $prefix = RCFILEPREFIX; + if (!empty($service['prefix'])) { + $prefix = $service['prefix']; + } + if (file_exists("{$prefix}{$service['rcfile']}")) { + @unlink("{$prefix}{$service['rcfile']}"); + } + } + unset($services[$key]); + } + } + } + } + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); + } + /* + * XXX: Otherwise inclusion of config.inc again invalidates actions taken. + * Same is done during installation. + */ + write_config("Intermediate config write during package removal for {$package_name}."); + + /* + * If a require exists, include it. this will + * show us where an error exists in a package + * instead of making us blindly guess + */ + $missing_include = false; + if ($pkg_config['include_file'] <> "") { + $static_output .= gettext("Loading package instructions...") . "\n"; + update_output_window($static_output); + if (file_exists($pkg_config['include_file'])) { + pkg_debug("require_once(\"{$pkg_config['include_file']}\")\n"); + require_once($pkg_config['include_file']); + } else { + pkg_debug("Missing include {$pkg_config['include_file']}\n"); + $missing_include = true; + update_output_window($static_output); + $static_output .= "Include file " . basename($pkg_config['include_file']) . " could not be found for inclusion.\n"; + } + } + /* ermal + * NOTE: It is not possible to handle parse errors on eval. + * So we prevent it from being run at all to not interrupt all the other code. + */ + if ($when == "deinstall" && $missing_include == false) { + /* evaluate this package's global functions and pre deinstall commands */ + if ($pkg_config['custom_php_global_functions'] <> "") { + eval_once($pkg_config['custom_php_global_functions']); + } + if ($pkg_config['custom_php_pre_deinstall_command'] <> "") { + eval_once($pkg_config['custom_php_pre_deinstall_command']); + } + } + /* deinstall commands */ + if ($when == "post-deinstall" && $pkg_config['custom_php_deinstall_command'] <> "") { + $static_output .= gettext("Deinstall commands... "); + update_output_window($static_output); + if ($missing_include == false) { + eval_once($pkg_config['custom_php_deinstall_command']); + $static_output .= gettext("done.") . "\n"; + } else { + $static_output .= "\nNot executing custom deinstall hook because an include is missing.\n"; + } + update_output_window($static_output); + } + } + /* syslog */ + if (is_array($pkg_info['logging']) && $pkg_info['logging']['logfile_name'] <> "") { + $static_output .= "Syslog entries... "; + update_output_window($static_output); + remove_text_from_file("/etc/syslog.conf", $pkg_info['logging']['facilityname'] . "\t\t\t\t" . $pkg_info['logging']['logfilename']); + system_syslogd_start(); + @unlink("{$g['varlog_path']}/{$pkg_info['logging']['logfilename']}"); + $static_output .= "done.\n"; + update_output_window($static_output); + } + + conf_mount_ro(); + /* remove config.xml entries */ + $static_output .= gettext("Configuration... "); + update_output_window($static_output); + unset($config['installedpackages']['package'][$pkgid]); + $static_output .= gettext("done.") . "\n"; + update_output_window($static_output); + write_config("Removed {$package_name} package.\n"); +} + +function pkg_reinstall_all() { + global $g, $config; + + // XXX: implement + return; +} + +function stop_packages() { + require_once("config.inc"); + require_once("functions.inc"); + require_once("filter.inc"); + require_once("shaper.inc"); + require_once("captiveportal.inc"); + require_once("pkg-utils.inc"); + require_once("pfsense-utils.inc"); + require_once("service-utils.inc"); + + global $config, $g; + + log_error("Stopping all packages."); + + $rcfiles = glob(RCFILEPREFIX . "*.sh"); + if (!$rcfiles) { + $rcfiles = array(); + } else { + $rcfiles = array_flip($rcfiles); + if (!$rcfiles) { + $rcfiles = array(); + } + } + + if (is_array($config['installedpackages']['package'])) { + foreach ($config['installedpackages']['package'] as $package) { + echo " Stopping package {$package['name']}..."; + $internal_name = get_package_internal_name($package); + stop_service($internal_name); + unset($rcfiles[RCFILEPREFIX . strtolower($internal_name) . ".sh"]); + echo "done.\n"; + } + } + + foreach ($rcfiles as $rcfile => $number) { + $shell = @popen("/bin/sh", "w"); + if ($shell) { + echo " Stopping {$rcfile}..."; + if (!@fwrite($shell, "{$rcfile} stop >>/tmp/bootup_messages 2>&1")) { + if ($shell) { + pclose($shell); + } + $shell = @popen("/bin/sh", "w"); + } + echo "done.\n"; + pclose($shell); + } + } +} + +function verify_all_package_servers() { + // XXX: Remove it after GUI is ready + return true; +} + +function check_package_server_ssl() { + // XXX: Remove it after GUI is ready + return true; +} + +?> diff --git a/src/etc/inc/plain_sasl_client.inc b/src/etc/inc/plain_sasl_client.inc new file mode 100644 index 0000000..691580c --- /dev/null +++ b/src/etc/inc/plain_sasl_client.inc @@ -0,0 +1,99 @@ +<?php +/* + * plain_sasl_client.php + * + * @(#) $Id: plain_sasl_client.php,v 1.2 2004/11/17 08:00:37 mlemos Exp $ + * + */ + +define("SASL_PLAIN_STATE_START", 0); +define("SASL_PLAIN_STATE_IDENTIFY", 1); +define("SASL_PLAIN_STATE_DONE", 2); + +define("SASL_PLAIN_DEFAULT_MODE", 0); +define("SASL_PLAIN_EXIM_MODE", 1); +define("SASL_PLAIN_EXIM_DOCUMENTATION_MODE", 2); + +class plain_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_PLAIN_STATE_START; + + Function Initialize(&$client) + { + return(1); + } + + Function Start(&$client, &$message, &$interactions) + { + if ($this->state!=SASL_PLAIN_STATE_START) + { + $client->error="PLAIN authentication state is not at the start"; + return(SASL_FAIL); + } + $this->credentials=array( + "user"=>"", + "password"=>"", + "realm"=>"", + "mode"=>"" + ); + $defaults=array( + "realm"=>"", + "mode"=>"" + ); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if ($status==SASL_CONTINUE) + { + switch ($this->credentials["mode"]) + { + case SASL_PLAIN_EXIM_MODE: + $message=$this->credentials["user"]."\0".$this->credentials["password"]."\0"; + break; + case SASL_PLAIN_EXIM_DOCUMENTATION_MODE: + $message="\0".$this->credentials["user"]."\0".$this->credentials["password"]; + break; + default: + $message=$this->credentials["user"]."\0".$this->credentials["user"].(strlen($this->credentials["realm"]) ? "@".$this->credentials["realm"] : "")."\0".$this->credentials["password"]; + break; + } + $this->state=SASL_PLAIN_STATE_DONE; + } + else + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch ($this->state) + { +/* + case SASL_PLAIN_STATE_IDENTIFY: + switch ($this->credentials["mode"]) + { + case SASL_PLAIN_EXIM_MODE: + $message=$this->credentials["user"]."\0".$this->credentials["password"]."\0"; + break; + case SASL_PLAIN_EXIM_DOCUMENTATION_MODE: + $message="\0".$this->credentials["user"]."\0".$this->credentials["password"]; + break; + default: + $message=$this->credentials["user"]."\0".$this->credentials["user"].(strlen($this->credentials["realm"]) ? "@".$this->credentials["realm"] : "")."\0".$this->credentials["password"]; + break; + } + var_dump($message); + $this->state=SASL_PLAIN_STATE_DONE; + break; +*/ + case SASL_PLAIN_STATE_DONE: + $client->error="PLAIN authentication was finished without success"; + return(SASL_FAIL); + default: + $client->error="invalid PLAIN authentication step state"; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/src/etc/inc/priv.defs.inc b/src/etc/inc/priv.defs.inc new file mode 100644 index 0000000..7d2154f --- /dev/null +++ b/src/etc/inc/priv.defs.inc @@ -0,0 +1,1403 @@ +<?php +/* + * priv.defs.inc - Generated privilege definitions + * + */ + +$priv_list = array(); + +$priv_list['page-all'] = array(); +$priv_list['page-all']['name'] = gettext("WebCfg - All pages"); +$priv_list['page-all']['descr'] = gettext("Allow access to all pages"); +$priv_list['page-all']['match'] = array(); +$priv_list['page-all']['match'][] = "*"; + +$priv_list['page-status-carp'] = array(); +$priv_list['page-status-carp']['name'] = gettext("WebCfg - Status: CARP page"); +$priv_list['page-status-carp']['descr'] = gettext("Allow access to the 'Status: CARP' page."); +$priv_list['page-status-carp']['match'] = array(); +$priv_list['page-status-carp']['match'][] = "carp_status.php*"; + +$priv_list['page-diagnostics-crash-reporter'] = array(); +$priv_list['page-diagnostics-crash-reporter']['name'] = gettext("WebCfg - Crash reporter"); +$priv_list['page-diagnostics-crash-reporter']['descr'] = gettext("Uploads crash reports to pfSense and or deletes crash reports."); +$priv_list['page-diagnostics-crash-reporter']['match'] = array(); +$priv_list['page-diagnostics-crash-reporter']['match'][] = "crash_reporter.php*"; + +$priv_list['page-diagnostics-arptable'] = array(); +$priv_list['page-diagnostics-arptable']['name'] = gettext("WebCfg - Diagnostics: ARP Table page"); +$priv_list['page-diagnostics-arptable']['descr'] = gettext("Allow access to the 'Diagnostics: ARP Table' page."); +$priv_list['page-diagnostics-arptable']['match'] = array(); +$priv_list['page-diagnostics-arptable']['match'][] = "diag_arp.php*"; + +$priv_list['page-diagnostics-authentication'] = array(); +$priv_list['page-diagnostics-authentication']['name'] = gettext("WebCfg - Diagnostics: Authentication page"); +$priv_list['page-diagnostics-authentication']['descr'] = gettext("Allow access to the 'Diagnostics: Authentication' page."); +$priv_list['page-diagnostics-authentication']['match'] = array(); +$priv_list['page-diagnostics-authentication']['match'][] = "diag_authentication.php*"; + +$priv_list['page-diagnostics-backup/restore'] = array(); +$priv_list['page-diagnostics-backup/restore']['name'] = gettext("WebCfg - Diagnostics: Backup/restore page"); +$priv_list['page-diagnostics-backup/restore']['descr'] = gettext("Allow access to the 'Diagnostics: Backup/restore' page."); +$priv_list['page-diagnostics-backup/restore']['match'] = array(); +$priv_list['page-diagnostics-backup/restore']['match'][] = "diag_backup.php*"; + +$priv_list['page-diagnostics-configurationhistory'] = array(); +$priv_list['page-diagnostics-configurationhistory']['name'] = gettext("WebCfg - Diagnostics: Configuration History page"); +$priv_list['page-diagnostics-configurationhistory']['descr'] = gettext("Allow access to the 'Diagnostics: Configuration History' page."); +$priv_list['page-diagnostics-configurationhistory']['match'] = array(); +$priv_list['page-diagnostics-configurationhistory']['match'][] = "diag_confbak.php*"; + +$priv_list['page-diagnostics-factorydefaults'] = array(); +$priv_list['page-diagnostics-factorydefaults']['name'] = gettext("WebCfg - Diagnostics: Factory defaults page"); +$priv_list['page-diagnostics-factorydefaults']['descr'] = gettext("Allow access to the 'Diagnostics: Factory defaults' page."); +$priv_list['page-diagnostics-factorydefaults']['match'] = array(); +$priv_list['page-diagnostics-factorydefaults']['match'][] = "diag_defaults.php*"; + +$priv_list['page-diagnostics-ndptable'] = array(); +$priv_list['page-diagnostics-ndptable']['name'] = gettext("Webcfg - Diagnostics: NDP Table page"); +$priv_list['page-diagnostics-ndptable']['descr'] = gettext("Allow access to the 'Diagnostics: NDP Table' page."); +$priv_list['page-diagnostics-ndptable']['match'] = array(); +$priv_list['page-diagnostics-ndptable']['match'][] = "diag_ndp.php*"; + +$priv_list['page-diagnostics-restore-full-backup'] = array(); +$priv_list['page-diagnostics-restore-full-backup']['name'] = gettext("Webcfg - Diagnostics: Restore full backup"); +$priv_list['page-diagnostics-restore-full-backup']['descr'] = gettext("Allow access to the 'Diagnostics: Restore Full Backup' page."); +$priv_list['page-diagnostics-restore-full-backup']['match'] = array(); +$priv_list['page-diagnostics-restore-full-backup']['match'][] = "system_firmware_restorefullbackup.php"; + +$priv_list['page-diagnostics-showstates'] = array(); +$priv_list['page-diagnostics-showstates']['name'] = gettext("WebCfg - Diagnostics: Show States page"); +$priv_list['page-diagnostics-showstates']['descr'] = gettext("Allow access to the 'Diagnostics: Show States' page."); +$priv_list['page-diagnostics-showstates']['match'] = array(); +$priv_list['page-diagnostics-showstates']['match'][] = "diag_dump_states.php*"; + +$priv_list['page-diagnostics-sockets'] = array(); +$priv_list['page-diagnostics-sockets']['name'] = gettext("WebCfg - Diagnostics: Sockets page"); +$priv_list['page-diagnostics-sockets']['descr'] = gettext("Allow access to the 'Diagnostics: Sockets' page."); +$priv_list['page-diagnostics-sockets']['match'] = array(); +$priv_list['page-diagnostics-sockets']['match'][] = "diag_sockets.php*"; + +$priv_list['page-diagnostics-testport'] = array(); +$priv_list['page-diagnostics-testport']['name'] = gettext("Webcfg - Diagnostics: Test Port"); +$priv_list['page-diagnostics-testport']['descr'] = gettext("Allow access to the 'Diagnostics: Test Port' page."); +$priv_list['page-diagnostics-testport']['match'] = array(); +$priv_list['page-diagnostics-testport']['match'][] = "diag_testport.php*"; + +$priv_list['page-status-ipsec'] = array(); +$priv_list['page-status-ipsec']['name'] = gettext("WebCfg - Status: IPsec page"); +$priv_list['page-status-ipsec']['descr'] = gettext("Allow access to the 'Status: IPsec' page."); +$priv_list['page-status-ipsec']['match'] = array(); +$priv_list['page-status-ipsec']['match'][] = "diag_ipsec.php*"; + +$priv_list['page-status-ipsec-leases'] = array(); +$priv_list['page-status-ipsec-leases']['name'] = gettext("WebCfg - Status: IPsec: Leasespage"); +$priv_list['page-status-ipsec-leases']['descr'] = gettext("Allow access to the 'Status: IPsec: Leases' page."); +$priv_list['page-status-ipsec-leases']['match'] = array(); +$priv_list['page-status-ipsec-leases']['match'][] = "diag_ipsec_leases.php*"; + +$priv_list['page-status-ipsec-sad'] = array(); +$priv_list['page-status-ipsec-sad']['name'] = gettext("WebCfg - Status: IPsec: SAD page"); +$priv_list['page-status-ipsec-sad']['descr'] = gettext("Allow access to the 'Status: IPsec: SAD' page."); +$priv_list['page-status-ipsec-sad']['match'] = array(); +$priv_list['page-status-ipsec-sad']['match'][] = "diag_ipsec_sad.php*"; + +$priv_list['page-status-ipsec-spd'] = array(); +$priv_list['page-status-ipsec-spd']['name'] = gettext("WebCfg - Status: IPsec: SPD page"); +$priv_list['page-status-ipsec-spd']['descr'] = gettext("Allow access to the 'Status: IPsec: SPD' page."); +$priv_list['page-status-ipsec-spd']['match'] = array(); +$priv_list['page-status-ipsec-spd']['match'][] = "diag_ipsec_spd.php*"; + +$priv_list['page-status-ntp'] = array(); +$priv_list['page-status-ntp']['name'] = gettext("Webcfg - Status: NTP page"); +$priv_list['page-status-ntp']['descr'] = gettext("Allow access to the 'Status: NTP' page."); +$priv_list['page-status-ntp']['match'] = array(); +$priv_list['page-status-ntp']['match'][] = "status_ntpd.php*"; + +$priv_list['page-ipsecxml'] = array(); +$priv_list['page-ipsecxml']['name'] = gettext("WebCfg - Diag IPsec XML page"); +$priv_list['page-ipsecxml']['descr'] = gettext("Allow access to the 'Diag IPsec XML' page."); +$priv_list['page-ipsecxml']['match'] = array(); +$priv_list['page-ipsecxml']['match'][] = "diag_ipsec_xml.php"; + +$priv_list['page-diagnostics-logs-system'] = array(); +$priv_list['page-diagnostics-logs-system']['name'] = gettext("WebCfg - Diagnostics: Logs: System page"); +$priv_list['page-diagnostics-logs-system']['descr'] = gettext("Allow access to the 'Diagnostics: Logs: System' page."); +$priv_list['page-diagnostics-logs-system']['match'] = array(); +$priv_list['page-diagnostics-logs-system']['match'][] = "diag_logs.php*"; + +$priv_list['page-status-systemlogs-portalauth'] = array(); +$priv_list['page-status-systemlogs-portalauth']['name'] = gettext("WebCfg - Status: System logs: Portal Auth page"); +$priv_list['page-status-systemlogs-portalauth']['descr'] = gettext("Allow access to the 'Status: System logs: Portal Auth' page."); +$priv_list['page-status-systemlogs-portalauth']['match'] = array(); +$priv_list['page-status-systemlogs-portalauth']['match'][] = "diag_logs_auth.php*"; + +$priv_list['page-diagnostics-logs-dhcp'] = array(); +$priv_list['page-diagnostics-logs-dhcp']['name'] = gettext("WebCfg - Diagnostics: Logs: DHCP page"); +$priv_list['page-diagnostics-logs-dhcp']['descr'] = gettext("Allow access to the 'Diagnostics: Logs: DHCP' page."); +$priv_list['page-diagnostics-logs-dhcp']['match'] = array(); +$priv_list['page-diagnostics-logs-dhcp']['match'][] = "diag_logs_dhcp.php*"; + +$priv_list['page-diagnostics-logs-firewall'] = array(); +$priv_list['page-diagnostics-logs-firewall']['name'] = gettext("WebCfg - Diagnostics: Logs: Firewall page"); +$priv_list['page-diagnostics-logs-firewall']['descr'] = gettext("Allow access to the 'Diagnostics: Logs: Firewall' page."); +$priv_list['page-diagnostics-logs-firewall']['match'] = array(); +$priv_list['page-diagnostics-logs-firewall']['match'][] = "diag_logs_filter.php*"; + +$priv_list['page-diagnostics-logs-gateways'] = array(); +$priv_list['page-diagnostics-logs-gateways']['name'] = gettext("WebCfg - Diagnostics: Logs: Gateways page"); +$priv_list['page-diagnostics-logs-gateways']['descr'] = gettext("Allow access to the 'Diagnostics: Logs: System: Gateways' page."); +$priv_list['page-diagnostics-logs-gateways']['match'] = array(); +$priv_list['page-diagnostics-logs-gateways']['match'][] = "diag_logs_gateways.php*"; + +$priv_list['page-diagnostics-logs-resolver'] = array(); +$priv_list['page-diagnostics-logs-resolver']['name'] = gettext("WebCfg - Diagnostics: Logs: Resolver page"); +$priv_list['page-diagnostics-logs-resolver']['descr'] = gettext("Allow access to the 'Diagnostics: Logs: System: Resolver' page."); +$priv_list['page-diagnostics-logs-resolver']['match'] = array(); +$priv_list['page-diagnostics-logs-resolver']['match'][] = "diag_logs_resolver.php*"; + +$priv_list['page-hidden-nolongerincluded'] = array(); +$priv_list['page-hidden-nolongerincluded']['name'] = gettext("WebCfg - Hidden: No longer included page"); +$priv_list['page-hidden-nolongerincluded']['descr'] = gettext("Allow access to the 'Hidden: No longer included' page."); +$priv_list['page-hidden-nolongerincluded']['match'] = array(); +$priv_list['page-hidden-nolongerincluded']['match'][] = "diag_logs_filter_dynamic.php*"; + +$priv_list['page-status-systemlogs-ipsecvpn'] = array(); +$priv_list['page-status-systemlogs-ipsecvpn']['name'] = gettext("WebCfg - Status: System logs: IPsec VPN page"); +$priv_list['page-status-systemlogs-ipsecvpn']['descr'] = gettext("Allow access to the 'Status: System logs: IPsec VPN' page."); +$priv_list['page-status-systemlogs-ipsecvpn']['match'] = array(); +$priv_list['page-status-systemlogs-ipsecvpn']['match'][] = "diag_logs_ipsec.php*"; + +$priv_list['page-status-systemlogs-ntpd'] = array(); +$priv_list['page-status-systemlogs-ntpd']['name'] = gettext("WebCfg - Status: System logs: NTP page"); +$priv_list['page-status-systemlogs-ntpd']['descr'] = gettext("Allow access to the 'Status: System logs: NTP' page."); +$priv_list['page-status-systemlogs-ntpd']['match'] = array(); +$priv_list['page-status-systemlogs-ntpd']['match'][] = "diag_logs_ntpd.php*"; + +$priv_list['page-status-systemlogs-openvpn'] = array(); +$priv_list['page-status-systemlogs-openvpn']['name'] = gettext("WebCfg - Status: System logs: OpenVPN page"); +$priv_list['page-status-systemlogs-openvpn']['descr'] = gettext("Allow access to the 'Status: System logs: OpenVPN' page."); +$priv_list['page-status-systemlogs-openvpn']['match'] = array(); +$priv_list['page-status-systemlogs-openvpn']['match'][] = "diag_logs_openvpn.php*"; + +$priv_list['page-status-systemlogs-ppp'] = array(); +$priv_list['page-status-systemlogs-ppp']['name'] = gettext("WebCfg - Status: System logs: IPsec VPN page"); +$priv_list['page-status-systemlogs-ppp']['descr'] = gettext("Allow access to the 'Status: System logs: IPsec VPN' page."); +$priv_list['page-status-systemlogs-ppp']['match'] = array(); +$priv_list['page-status-systemlogs-ppp']['match'][] = "diag_logs_ppp.php*"; + +$priv_list['page-status-systemlogs-loadbalancer'] = array(); +$priv_list['page-status-systemlogs-loadbalancer']['name'] = gettext("WebCfg - Status: System logs: Load Balancer page"); +$priv_list['page-status-systemlogs-loadbalancer']['descr'] = gettext("Allow access to the 'Status: System logs: Load Balancer' page."); +$priv_list['page-status-systemlogs-loadbalancer']['match'] = array(); +$priv_list['page-status-systemlogs-loadbalancer']['match'][] = "diag_logs_relayd.php*"; + +$priv_list['page-status-systemlogs-routing'] = array(); +$priv_list['page-status-systemlogs-routing']['name'] = gettext("Webcfg - Status: System logs: Routing page"); +$priv_list['page-status-systemlogs-routing']['descr'] = gettext("Allow access to the 'Status: System logs: System: Routing' page."); +$priv_list['page-status-systemlogs-routing']['match'] = array(); +$priv_list['page-status-systemlogs-routing']['match'][] = "diag_logs_routing.php*"; + +$priv_list['page-status-systemlogs-wireless'] = array(); +$priv_list['page-status-systemlogs-wireless']['name'] = gettext("Webcfg - Status: System logs: Wireless page"); +$priv_list['page-status-systemlogs-wireless']['descr'] = gettext("Allow access to the 'Status: System logs: System: Wireless' page."); +$priv_list['page-status-systemlogs-wireless']['match'] = array(); +$priv_list['page-status-systemlogs-wireless']['match'][] = "diag_logs_wireless.php*"; + +$priv_list['page-diagnostics-logs-settings'] = array(); +$priv_list['page-diagnostics-logs-settings']['name'] = gettext("WebCfg - Diagnostics: Logs: Settings page"); +$priv_list['page-diagnostics-logs-settings']['descr'] = gettext("Allow access to the 'Diagnostics: Logs: Settings' page."); +$priv_list['page-diagnostics-logs-settings']['match'] = array(); +$priv_list['page-diagnostics-logs-settings']['match'][] = "diag_logs_settings.php*"; + +$priv_list['page-diagnostics-logs-pptpvpn'] = array(); +$priv_list['page-diagnostics-logs-pptpvpn']['name'] = gettext("WebCfg - Diagnostics: Logs: VPN page"); +$priv_list['page-diagnostics-logs-pptpvpn']['descr'] = gettext("Allow access to the 'Diagnostics: Logs: VPN' page."); +$priv_list['page-diagnostics-logs-pptpvpn']['match'] = array(); +$priv_list['page-diagnostics-logs-pptpvpn']['match'][] = "diag_logs_vpn.php*"; + +$priv_list['page-diagnostics-nanobsd'] = array(); +$priv_list['page-diagnostics-nanobsd']['name'] = gettext("WebCfg - Diagnostics: NanoBSD"); +$priv_list['page-diagnostics-nanobsd']['descr'] = gettext("Allow access to the 'Diagnostics: NanoBSD' page."); +$priv_list['page-diagnostics-nanobsd']['match'] = array(); +$priv_list['page-diagnostics-nanobsd']['match'][] = "diag_nanobsd.php*"; + +$priv_list['page-diagnostics-packetcapture'] = array(); +$priv_list['page-diagnostics-packetcapture']['name'] = gettext("WebCfg - Diagnostics: Packet Capture page"); +$priv_list['page-diagnostics-packetcapture']['descr'] = gettext("Allow access to the 'Diagnostics: Packet Capture' page."); +$priv_list['page-diagnostics-packetcapture']['match'] = array(); +$priv_list['page-diagnostics-packetcapture']['match'][] = "diag_packet_capture.php*"; + +$priv_list['page-diagnostics-patters'] = array(); +$priv_list['page-diagnostics-patters']['name'] = gettext("WebCfg - Diagnostics: Patterns page"); +$priv_list['page-diagnostics-patters']['descr'] = gettext("Allow access to the 'Diagnostics: Patterns' page."); +$priv_list['page-diagnostics-patters']['match'] = array(); +$priv_list['page-diagnostics-patters']['match'][] = "patterns.php*"; + +$priv_list['page-diagnostics-limiter-info'] = array(); +$priv_list['page-diagnostics-limiter-info']['name'] = gettext("Diagnostics: Limiter Info"); +$priv_list['page-diagnostics-limiter-info']['descr'] = gettext("Allows access to the 'Diagnostics: Limiter Info' page"); +$priv_list['page-diagnostics-limiter-info']['match'] = array(); +$priv_list['page-diagnostics-limiter-info']['match'][] = "diag_limiter_info.php*"; + +$priv_list['page-diagnostics-pf-info'] = array(); +$priv_list['page-diagnostics-pf-info']['name'] = gettext("Diagnostics: pfInfo"); +$priv_list['page-diagnostics-pf-info']['descr'] = gettext("Allows access to the 'Diagnostics: pfInfo' page"); +$priv_list['page-diagnostics-pf-info']['match'] = array(); +$priv_list['page-diagnostics-pf-info']['match'][] = "diag_pf_info.php*"; + +$priv_list['page-diagnostics-system-activity'] = array(); +$priv_list['page-diagnostics-system-activity']['name'] = gettext("WebCfg - Diagnostics: System Activity"); +$priv_list['page-diagnostics-system-activity']['descr'] = gettext("Allows access to the 'Diagnostics: System Activity' page"); +$priv_list['page-diagnostics-system-activity']['match'] = array(); +$priv_list['page-diagnostics-system-activity']['match'][] = "diag_system_activity.php*"; + +$priv_list['page-diagnostics-system-pftop'] = array(); +$priv_list['page-diagnostics-system-pftop']['name'] = gettext("Diagnostics: pfTop"); +$priv_list['page-diagnostics-system-pftop']['descr'] = gettext("Allows access to the 'Diagnostics: pfTop' page"); +$priv_list['page-diagnostics-system-pftop']['match'] = array(); +$priv_list['page-diagnostics-system-pftop']['match'][] = "diag_system_pftop.php*"; + +$priv_list['page-diagnostics-ping'] = array(); +$priv_list['page-diagnostics-ping']['name'] = gettext("WebCfg - Diagnostics: Ping page"); +$priv_list['page-diagnostics-ping']['descr'] = gettext("Allow access to the 'Diagnostics: Ping' page."); +$priv_list['page-diagnostics-ping']['match'] = array(); +$priv_list['page-diagnostics-ping']['match'][] = "diag_ping.php*"; + +$priv_list['page-status-packagelogs'] = array(); +$priv_list['page-status-packagelogs']['name'] = gettext("WebCfg - Status: Package logs page"); +$priv_list['page-status-packagelogs']['descr'] = gettext("Allow access to the 'Status: Package logs' page."); +$priv_list['page-status-packagelogs']['match'] = array(); +$priv_list['page-status-packagelogs']['match'][] = "diag_pkglogs.php*"; + +$priv_list['page-diagnostics-resetstate'] = array(); +$priv_list['page-diagnostics-resetstate']['name'] = gettext("WebCfg - Diagnostics: Reset state page"); +$priv_list['page-diagnostics-resetstate']['descr'] = gettext("Allow access to the 'Diagnostics: Reset state' page."); +$priv_list['page-diagnostics-resetstate']['match'] = array(); +$priv_list['page-diagnostics-resetstate']['match'][] = "diag_resetstate.php*"; + +$priv_list['page-diagnostics-routingtables'] = array(); +$priv_list['page-diagnostics-routingtables']['name'] = gettext("WebCfg - Diagnostics: Routing tables page"); +$priv_list['page-diagnostics-routingtables']['descr'] = gettext("Allow access to the 'Diagnostics: Routing tables' page."); +$priv_list['page-diagnostics-routingtables']['match'] = array(); +$priv_list['page-diagnostics-routingtables']['match'][] = "diag_routes.php*"; + +$priv_list['page-diagnostics-statessummary'] = array(); +$priv_list['page-diagnostics-statessummary']['name'] = gettext("WebCfg - Diagnostics: States Summary page"); +$priv_list['page-diagnostics-statessummary']['descr'] = gettext("Allow access to the 'Diagnostics: States Summary' page."); +$priv_list['page-diagnostics-statessummary']['match'] = array(); +$priv_list['page-diagnostics-statessummary']['match'][] = "diag_states_summary.php*"; + +$priv_list['page-diagnostics-tables'] = array(); +$priv_list['page-diagnostics-tables']['name'] = gettext("WebCfg - Diagnostics: PF Table IP addresses"); +$priv_list['page-diagnostics-tables']['descr'] = gettext("Allow access to the 'Diagnostics: Tables' page."); +$priv_list['page-diagnostics-tables']['match'] = array(); +$priv_list['page-diagnostics-tables']['match'][] = "diag_tables.php*"; + +$priv_list['page-diagnostics-traceroute'] = array(); +$priv_list['page-diagnostics-traceroute']['name'] = gettext("WebCfg - Diagnostics: Traceroute page"); +$priv_list['page-diagnostics-traceroute']['descr'] = gettext("Allow access to the 'Diagnostics: Traceroute' page."); +$priv_list['page-diagnostics-traceroute']['match'] = array(); +$priv_list['page-diagnostics-traceroute']['match'][] = "diag_traceroute.php*"; + +$priv_list['page-diagnostics-edit'] = array(); +$priv_list['page-diagnostics-edit']['name'] = gettext("WebCfg - Diagnostics: Edit FIle"); +$priv_list['page-diagnostics-edit']['descr'] = gettext("Allow access to the 'Diagnostics: Edit File' page."); +$priv_list['page-diagnostics-edit']['match'] = array(); +$priv_list['page-diagnostics-edit']['match'][] = "edit.php*"; +$priv_list['page-diagnostics-edit']['match'][] = "browser.php*"; +$priv_list['page-diagnostics-edit']['match'][] = "filebrowser/browser.php*"; + +$priv_list['page-diagnostics-command'] = array(); +$priv_list['page-diagnostics-command']['name'] = gettext("WebCfg - Diagnostics: Command page"); +$priv_list['page-diagnostics-command']['descr'] = gettext("Allow access to the 'Diagnostics: Command' page."); +$priv_list['page-diagnostics-command']['match'] = array(); +$priv_list['page-diagnostics-command']['match'][] = "exec.php*"; + +$priv_list['page-firewall-aliases'] = array(); +$priv_list['page-firewall-aliases']['name'] = gettext("WebCfg - Firewall: Aliases page"); +$priv_list['page-firewall-aliases']['descr'] = gettext("Allow access to the 'Firewall: Aliases' page."); +$priv_list['page-firewall-aliases']['match'] = array(); +$priv_list['page-firewall-aliases']['match'][] = "firewall_aliases.php*"; + +$priv_list['page-firewall-alias-edit'] = array(); +$priv_list['page-firewall-alias-edit']['name'] = gettext("WebCfg - Firewall: Alias: Edit page"); +$priv_list['page-firewall-alias-edit']['descr'] = gettext("Allow access to the 'Firewall: Alias: Edit' page."); +$priv_list['page-firewall-alias-edit']['match'] = array(); +$priv_list['page-firewall-alias-edit']['match'][] = "firewall_aliases_edit.php*"; + +$priv_list['page-firewall-alias-import'] = array(); +$priv_list['page-firewall-alias-import']['name'] = gettext("WebCfg - Firewall: Alias: Import page"); +$priv_list['page-firewall-alias-import']['descr'] = gettext("Allow access to the 'Firewall: Alias: Import' page."); +$priv_list['page-firewall-alias-import']['match'] = array(); +$priv_list['page-firewall-alias-import']['match'][] = "firewall_aliases_import.php*"; + +$priv_list['page-firewall-nat-npt'] = array(); +$priv_list['page-firewall-nat-npt']['name'] = gettext("Webcfg - Firewall: NAT: NPT page"); +$priv_list['page-firewall-nat-npt']['descr'] = gettext("Allow access to the 'Firewall: NAT: NPT' page."); +$priv_list['page-firewall-nat-npt']['match'] = array(); +$priv_list['page-firewall-nat-npt']['match'][] = "firewall_nat_npt.php*"; + +$priv_list['page-firewall-nat-npt-edit'] = array(); +$priv_list['page-firewall-nat-npt-edit']['name'] = gettext("Webcfg - Firewall: NAT: NPt: Edit page"); +$priv_list['page-firewall-nat-npt-edit']['descr'] = gettext("Allow access to the 'Firewall: NAT: NPt: Edit' page."); +$priv_list['page-firewall-nat-npt-edit']['match'] = array(); +$priv_list['page-firewall-nat-npt-edit']['match'][] = "firewall_nat_npt_edit.php*"; + +$priv_list['page-firewall-nat-portforward'] = array(); +$priv_list['page-firewall-nat-portforward']['name'] = gettext("WebCfg - Firewall: NAT: Port Forward page"); +$priv_list['page-firewall-nat-portforward']['descr'] = gettext("Allow access to the 'Firewall: NAT: Port Forward' page."); +$priv_list['page-firewall-nat-portforward']['match'] = array(); +$priv_list['page-firewall-nat-portforward']['match'][] = "firewall_nat.php*"; + +$priv_list['page-firewall-nat-1-1'] = array(); +$priv_list['page-firewall-nat-1-1']['name'] = gettext("WebCfg - Firewall: NAT: 1:1 page"); +$priv_list['page-firewall-nat-1-1']['descr'] = gettext("Allow access to the 'Firewall: NAT: 1:1' page."); +$priv_list['page-firewall-nat-1-1']['match'] = array(); +$priv_list['page-firewall-nat-1-1']['match'][] = "firewall_nat_1to1.php*"; + +$priv_list['page-firewall-nat-1-1-edit'] = array(); +$priv_list['page-firewall-nat-1-1-edit']['name'] = gettext("WebCfg - Firewall: NAT: 1:1: Edit page"); +$priv_list['page-firewall-nat-1-1-edit']['descr'] = gettext("Allow access to the 'Firewall: NAT: 1:1: Edit' page."); +$priv_list['page-firewall-nat-1-1-edit']['match'] = array(); +$priv_list['page-firewall-nat-1-1-edit']['match'][] = "firewall_nat_1to1_edit.php*"; + +$priv_list['page-firewall-nat-portforward-edit'] = array(); +$priv_list['page-firewall-nat-portforward-edit']['name'] = gettext("WebCfg - Firewall: NAT: Port Forward: Edit page"); +$priv_list['page-firewall-nat-portforward-edit']['descr'] = gettext("Allow access to the 'Firewall: NAT: Port Forward: Edit' page."); +$priv_list['page-firewall-nat-portforward-edit']['match'] = array(); +$priv_list['page-firewall-nat-portforward-edit']['match'][] = "firewall_nat_edit.php*"; + +$priv_list['page-firewall-nat-outbound'] = array(); +$priv_list['page-firewall-nat-outbound']['name'] = gettext("WebCfg - Firewall: NAT: Outbound page"); +$priv_list['page-firewall-nat-outbound']['descr'] = gettext("Allow access to the 'Firewall: NAT: Outbound' page."); +$priv_list['page-firewall-nat-outbound']['match'] = array(); +$priv_list['page-firewall-nat-outbound']['match'][] = "firewall_nat_out.php*"; + +$priv_list['page-firewall-nat-outbound-edit'] = array(); +$priv_list['page-firewall-nat-outbound-edit']['name'] = gettext("WebCfg - Firewall: NAT: Outbound: Edit page"); +$priv_list['page-firewall-nat-outbound-edit']['descr'] = gettext("Allow access to the 'Firewall: NAT: Outbound: Edit' page."); +$priv_list['page-firewall-nat-outbound-edit']['match'] = array(); +$priv_list['page-firewall-nat-outbound-edit']['match'][] = "firewall_nat_out_edit.php*"; + +$priv_list['page-firewall-rules'] = array(); +$priv_list['page-firewall-rules']['name'] = gettext("WebCfg - Firewall: Rules page"); +$priv_list['page-firewall-rules']['descr'] = gettext("Allow access to the 'Firewall: Rules' page."); +$priv_list['page-firewall-rules']['match'] = array(); +$priv_list['page-firewall-rules']['match'][] = "firewall_rules.php*"; + +$priv_list['page-firewall-rules-edit'] = array(); +$priv_list['page-firewall-rules-edit']['name'] = gettext("WebCfg - Firewall: Rules: Edit page"); +$priv_list['page-firewall-rules-edit']['descr'] = gettext("Allow access to the 'Firewall: Rules: Edit' page."); +$priv_list['page-firewall-rules-edit']['match'] = array(); +$priv_list['page-firewall-rules-edit']['match'][] = "firewall_rules_edit.php*"; + +$priv_list['page-firewall-schedules'] = array(); +$priv_list['page-firewall-schedules']['name'] = gettext("WebCfg - Firewall: Schedules page"); +$priv_list['page-firewall-schedules']['descr'] = gettext("Allow access to the 'Firewall: Schedules' page."); +$priv_list['page-firewall-schedules']['match'] = array(); +$priv_list['page-firewall-schedules']['match'][] = "firewall_schedule.php*"; + +$priv_list['page-firewall-schedules-edit'] = array(); +$priv_list['page-firewall-schedules-edit']['name'] = gettext("WebCfg - Firewall: Schedules: Edit page"); +$priv_list['page-firewall-schedules-edit']['descr'] = gettext("Allow access to the 'Firewall: Schedules: Edit' page."); +$priv_list['page-firewall-schedules-edit']['match'] = array(); +$priv_list['page-firewall-schedules-edit']['match'][] = "firewall_schedule_edit.php*"; + +$priv_list['page-firewall-trafficshaper'] = array(); +$priv_list['page-firewall-trafficshaper']['name'] = gettext("WebCfg - Firewall: Traffic Shaper page"); +$priv_list['page-firewall-trafficshaper']['descr'] = gettext("Allow access to the 'Firewall: Traffic Shaper' page."); +$priv_list['page-firewall-trafficshaper']['match'] = array(); +$priv_list['page-firewall-trafficshaper']['match'][] = "firewall_shaper.php*"; + +$priv_list['page-firewall-trafficshaper-layer7'] = array(); +$priv_list['page-firewall-trafficshaper-layer7']['name'] = gettext("WebCfg - Firewall: Traffic Shaper: Layer7 page"); +$priv_list['page-firewall-trafficshaper-layer7']['descr'] = gettext("Allow access to the 'Firewall: Traffic Shaper: Layer7' page."); +$priv_list['page-firewall-trafficshaper-layer7']['match'] = array(); +$priv_list['page-firewall-trafficshaper-layer7']['match'][] = "firewall_shaper_layer7.php*"; + +$priv_list['page-firewall-trafficshaper-queues'] = array(); +$priv_list['page-firewall-trafficshaper-queues']['name'] = gettext("WebCfg - Firewall: Traffic Shaper: Queues page"); +$priv_list['page-firewall-trafficshaper-queues']['descr'] = gettext("Allow access to the 'Firewall: Traffic Shaper: Queues' page."); +$priv_list['page-firewall-trafficshaper-queues']['match'] = array(); +$priv_list['page-firewall-trafficshaper-queues']['match'][] = "firewall_shaper_queues.php*"; + +$priv_list['page-firewall-trafficshaper-limiter'] = array(); +$priv_list['page-firewall-trafficshaper-limiter']['name'] = gettext("WebCfg - Firewall: Traffic Shaper: Limiter page"); +$priv_list['page-firewall-trafficshaper-limiter']['descr'] = gettext("Allow access to the 'Firewall: Traffic Shaper: Limiter' page."); +$priv_list['page-firewall-trafficshaper-limiter']['match'] = array(); +$priv_list['page-firewall-trafficshaper-limiter']['match'][] = "firewall_shaper_vinterface.php*"; + +$priv_list['page-firewall-trafficshaper-wizard'] = array(); +$priv_list['page-firewall-trafficshaper-wizard']['name'] = gettext("WebCfg - Firewall: Traffic Shaper: Wizard page"); +$priv_list['page-firewall-trafficshaper-wizard']['descr'] = gettext("Allow access to the 'Firewall: Traffic Shaper: Wizard' page."); +$priv_list['page-firewall-trafficshaper-wizard']['match'] = array(); +$priv_list['page-firewall-trafficshaper-wizard']['match'][] = "firewall_shaper_wizards.php*"; + +$priv_list['page-firewall-virtualipaddresses'] = array(); +$priv_list['page-firewall-virtualipaddresses']['name'] = gettext("WebCfg - Firewall: Virtual IP Addresses page"); +$priv_list['page-firewall-virtualipaddresses']['descr'] = gettext("Allow access to the 'Firewall: Virtual IP Addresses' page."); +$priv_list['page-firewall-virtualipaddresses']['match'] = array(); +$priv_list['page-firewall-virtualipaddresses']['match'][] = "firewall_virtual_ip.php*"; + +$priv_list['page-firewall-virtualipaddress-edit'] = array(); +$priv_list['page-firewall-virtualipaddress-edit']['name'] = gettext("WebCfg - Firewall: Virtual IP Address: Edit page"); +$priv_list['page-firewall-virtualipaddress-edit']['descr'] = gettext("Allow access to the 'Firewall: Virtual IP Address: Edit' page."); +$priv_list['page-firewall-virtualipaddress-edit']['match'] = array(); +$priv_list['page-firewall-virtualipaddress-edit']['match'][] = "firewall_virtual_ip_edit.php*"; + +$priv_list['page-getserviceproviders'] = array(); +$priv_list['page-getserviceproviders']['name'] = gettext("WebCfg - AJAX: Get Service Providers"); +$priv_list['page-getserviceproviders']['descr'] = gettext("Allow access to the 'AJAX: Service Providers' page."); +$priv_list['page-getserviceproviders']['match'] = array(); +$priv_list['page-getserviceproviders']['match'][] = "getserviceproviders.php*"; + +$priv_list['page-getstats'] = array(); +$priv_list['page-getstats']['name'] = gettext("WebCfg - AJAX: Get Stats"); +$priv_list['page-getstats']['descr'] = gettext("Allow access to the 'AJAX: Get Stats' page."); +$priv_list['page-getstats']['match'] = array(); +$priv_list['page-getstats']['match'][] = "getstats.php*"; + +$priv_list['page-diagnostics-interfacetraffic'] = array(); +$priv_list['page-diagnostics-interfacetraffic']['name'] = gettext("WebCfg - Diagnostics: Interface Traffic page"); +$priv_list['page-diagnostics-interfacetraffic']['descr'] = gettext("Allow access to the 'Diagnostics: Interface Traffic' page."); +$priv_list['page-diagnostics-interfacetraffic']['match'] = array(); +$priv_list['page-diagnostics-interfacetraffic']['match'][] = "graph.php*"; + +$priv_list['page-diagnostics-cpuutilization'] = array(); +$priv_list['page-diagnostics-cpuutilization']['name'] = gettext("WebCfg - Diagnostics: CPU Utilization page"); +$priv_list['page-diagnostics-cpuutilization']['descr'] = gettext("Allow access to the 'Diagnostics: CPU Utilization' page."); +$priv_list['page-diagnostics-cpuutilization']['match'] = array(); +$priv_list['page-diagnostics-cpuutilization']['match'][] = "graph_cpu.php*"; + +$priv_list['page-diagnostics-haltsystem'] = array(); +$priv_list['page-diagnostics-haltsystem']['name'] = gettext("WebCfg - Diagnostics: Halt system page"); +$priv_list['page-diagnostics-haltsystem']['descr'] = gettext("Allow access to the 'Diagnostics: Halt system' page."); +$priv_list['page-diagnostics-haltsystem']['match'] = array(); +$priv_list['page-diagnostics-haltsystem']['match'][] = "halt.php*"; + +$priv_list['page-requiredforjavascript'] = array(); +$priv_list['page-requiredforjavascript']['name'] = gettext("WebCfg - Required for javascript page"); +$priv_list['page-requiredforjavascript']['descr'] = gettext("Allow access to the 'Required for javascript' page."); +$priv_list['page-requiredforjavascript']['match'] = array(); +$priv_list['page-requiredforjavascript']['match'][] = "headjs.php*"; + +$priv_list['page-xmlrpcinterfacestats'] = array(); +$priv_list['page-xmlrpcinterfacestats']['name'] = gettext("WebCfg - XMLRPC Interface Stats page"); +$priv_list['page-xmlrpcinterfacestats']['descr'] = gettext("Allow access to the 'XMLRPC Interface Stats' page."); +$priv_list['page-xmlrpcinterfacestats']['match'] = array(); +$priv_list['page-xmlrpcinterfacestats']['match'][] = "ifstats.php*"; + +$priv_list['page-system-login/logout'] = array(); +$priv_list['page-system-login/logout']['name'] = gettext("WebCfg - System: Login / Logout page / Dashboard"); +$priv_list['page-system-login/logout']['descr'] = gettext("Allow access to the 'System: Login / Logout' page and Dashboard."); +$priv_list['page-system-login/logout']['match'] = array(); +$priv_list['page-system-login/logout']['match'][] = "index.php*"; + +$priv_list['page-interfaces'] = array(); +$priv_list['page-interfaces']['name'] = gettext("WebCfg - Interfaces: WAN page"); +$priv_list['page-interfaces']['descr'] = gettext("Allow access to the 'Interfaces' page."); +$priv_list['page-interfaces']['match'] = array(); +$priv_list['page-interfaces']['match'][] = "interfaces.php*"; + +$priv_list['page-interfaces-assignnetworkports'] = array(); +$priv_list['page-interfaces-assignnetworkports']['name'] = gettext("WebCfg - Interfaces: Assign network ports page"); +$priv_list['page-interfaces-assignnetworkports']['descr'] = gettext("Allow access to the 'Interfaces: Assign network ports' page."); +$priv_list['page-interfaces-assignnetworkports']['match'] = array(); +$priv_list['page-interfaces-assignnetworkports']['match'][] = "interfaces_assign.php*"; + +$priv_list['page-interfaces-bridge'] = array(); +$priv_list['page-interfaces-bridge']['name'] = gettext("WebCfg - Interfaces: Bridge page"); +$priv_list['page-interfaces-bridge']['descr'] = gettext("Allow access to the 'Interfaces: Bridge' page."); +$priv_list['page-interfaces-bridge']['match'] = array(); +$priv_list['page-interfaces-bridge']['match'][] = "interfaces_bridge.php*"; + +$priv_list['page-interfaces-bridge-edit'] = array(); +$priv_list['page-interfaces-bridge-edit']['name'] = gettext("WebCfg - Interfaces: Bridge edit page"); +$priv_list['page-interfaces-bridge-edit']['descr'] = gettext("Allow access to the 'Interfaces: Bridge : Edit' page."); +$priv_list['page-interfaces-bridge-edit']['match'] = array(); +$priv_list['page-interfaces-bridge-edit']['match'][] = "interfaces_bridge_edit.php*"; + +$priv_list['page-interfaces-gif'] = array(); +$priv_list['page-interfaces-gif']['name'] = gettext("WebCfg - Interfaces: GIF page"); +$priv_list['page-interfaces-gif']['descr'] = gettext("Allow access to the 'Interfaces: GIF' page."); +$priv_list['page-interfaces-gif']['match'] = array(); +$priv_list['page-interfaces-gif']['match'][] = "interfaces_gif.php*"; + +$priv_list['page-interfaces-gif-edit'] = array(); +$priv_list['page-interfaces-gif-edit']['name'] = gettext("WebCfg - Interfaces: GIF: Edit page"); +$priv_list['page-interfaces-gif-edit']['descr'] = gettext("Allow access to the 'Interfaces: GIF: Edit' page."); +$priv_list['page-interfaces-gif-edit']['match'] = array(); +$priv_list['page-interfaces-gif-edit']['match'][] = "interfaces_gif_edit.php*"; + +$priv_list['page-interfaces-gre'] = array(); +$priv_list['page-interfaces-gre']['name'] = gettext("WebCfg - Interfaces: GRE page"); +$priv_list['page-interfaces-gre']['descr'] = gettext("Allow access to the 'Interfaces: GRE' page."); +$priv_list['page-interfaces-gre']['match'] = array(); +$priv_list['page-interfaces-gre']['match'][] = "interfaces_gre.php*"; + +$priv_list['page-interfaces-gre-edit'] = array(); +$priv_list['page-interfaces-gre-edit']['name'] = gettext("WebCfg - Interfaces: GRE: Edit page"); +$priv_list['page-interfaces-gre-edit']['descr'] = gettext("Allow access to the 'Interfaces: GRE: Edit' page."); +$priv_list['page-interfaces-gre-edit']['match'] = array(); +$priv_list['page-interfaces-gre-edit']['match'][] = "interfaces_gre_edit.php*"; + +$priv_list['page-interfaces-groups'] = array(); +$priv_list['page-interfaces-groups']['name'] = gettext("WebCfg - Interfaces: Groups page"); +$priv_list['page-interfaces-groups']['descr'] = gettext("Create interface groups"); +$priv_list['page-interfaces-groups']['match'] = array(); +$priv_list['page-interfaces-groups']['match'][] = "interfaces_groups.php*"; + +$priv_list['page-interfaces-groups-edit'] = array(); +$priv_list['page-interfaces-groups-edit']['name'] = gettext("Interfaces: Groups: Edit page"); +$priv_list['page-interfaces-groups-edit']['descr'] = gettext("Allow access to the 'Interfaces: Groups: Edit' page."); +$priv_list['page-interfaces-groups-edit']['match'] = array(); +$priv_list['page-interfaces-groups-edit']['match'][] = "interfaces_groups_edit.php*"; + +$priv_list['page-interfaces-lagg'] = array(); +$priv_list['page-interfaces-lagg']['name'] = gettext("WebCfg - Interfaces: LAGG: page"); +$priv_list['page-interfaces-lagg']['descr'] = gettext("Edit Interface LAGG"); +$priv_list['page-interfaces-lagg']['match'] = array(); +$priv_list['page-interfaces-lagg']['match'][] = "interfaces_lagg.php*"; + +$priv_list['page-interfaces-lagg-edit'] = array(); +$priv_list['page-interfaces-lagg-edit']['name'] = gettext("Interfaces: LAGG: Edit page"); +$priv_list['page-interfaces-lagg-edit']['descr'] = gettext("Allow access to the 'Interfaces: LAGG: Edit' page."); +$priv_list['page-interfaces-lagg-edit']['match'] = array(); +$priv_list['page-interfaces-lagg-edit']['match'][] = "interfaces_lagg_edit.php*"; + +$priv_list['page-interfaces-ppps'] = array(); +$priv_list['page-interfaces-ppps']['name'] = gettext("WebCfg - Interfaces: ppps page"); +$priv_list['page-interfaces-ppps']['descr'] = gettext("Allow access to the 'Interfaces: ppps' page."); +$priv_list['page-interfaces-ppps']['match'] = array(); +$priv_list['page-interfaces-ppps']['match'][] = "interfaces_ppps.php*"; + +$priv_list['page-interfaces-ppps-edit'] = array(); +$priv_list['page-interfaces-ppps-edit']['name'] = gettext("WebCfg - Interfaces: PPPs: Edit page"); +$priv_list['page-interfaces-ppps-edit']['descr'] = gettext("Allow access to the 'Interfaces: PPPs: Edit' page."); +$priv_list['page-interfaces-ppps-edit']['match'] = array(); +$priv_list['page-interfaces-ppps-edit']['match'][] = "interfaces_ppps_edit.php*"; + +$priv_list['page-interfaces-qinq'] = array(); +$priv_list['page-interfaces-qinq']['name'] = gettext("WebCfg - Interfaces: QinQ page"); +$priv_list['page-interfaces-qinq']['descr'] = gettext("Allow access to the 'Interfaces: QinQ' page."); +$priv_list['page-interfaces-qinq']['match'] = array(); +$priv_list['page-interfaces-qinq']['match'][] = "interfaces_qinq.php*"; + +$priv_list['page-interfaces-qinq-edit'] = array(); +$priv_list['page-interfaces-qinq-edit']['name'] = gettext("Interfaces: QinQ: Edit page"); +$priv_list['page-interfaces-qinq-edit']['descr'] = gettext("Allow access to 'Interfaces: QinQ: Edit' page"); +$priv_list['page-interfaces-qinq-edit']['match'] = array(); +$priv_list['page-interfaces-qinq-edit']['match'][] = "interfaces_qinq_edit.php*"; + +$priv_list['page-interfaces-vlan'] = array(); +$priv_list['page-interfaces-vlan']['name'] = gettext("WebCfg - Interfaces: VLAN page"); +$priv_list['page-interfaces-vlan']['descr'] = gettext("Allow access to the 'Interfaces: VLAN' page."); +$priv_list['page-interfaces-vlan']['match'] = array(); +$priv_list['page-interfaces-vlan']['match'][] = "interfaces_vlan.php*"; + +$priv_list['page-interfaces-vlan-edit'] = array(); +$priv_list['page-interfaces-vlan-edit']['name'] = gettext("WebCfg - Interfaces: VLAN: Edit page"); +$priv_list['page-interfaces-vlan-edit']['descr'] = gettext("Allow access to the 'Interfaces: VLAN: Edit' page."); +$priv_list['page-interfaces-vlan-edit']['match'] = array(); +$priv_list['page-interfaces-vlan-edit']['match'][] = "interfaces_vlan_edit.php*"; + +$priv_list['page-interfaces-wireless'] = array(); +$priv_list['page-interfaces-wireless']['name'] = gettext("WebCfg - Interfaces: Wireless page"); +$priv_list['page-interfaces-wireless']['descr'] = gettext("Allow access to the 'Interfaces: Wireless' page."); +$priv_list['page-interfaces-wireless']['match'] = array(); +$priv_list['page-interfaces-wireless']['match'][] = "interfaces_wireless.php*"; + +$priv_list['page-interfaces-wireless-edit'] = array(); +$priv_list['page-interfaces-wireless-edit']['name'] = gettext("WebCfg - Interfaces: Wireless edit page"); +$priv_list['page-interfaces-wireless-edit']['descr'] = gettext("Allow access to the 'Interfaces: Wireless : Edit' page."); +$priv_list['page-interfaces-wireless-edit']['match'] = array(); +$priv_list['page-interfaces-wireless-edit']['match'][] = "interfaces_wireless_edit.php*"; + +$priv_list['page-system-license'] = array(); +$priv_list['page-system-license']['name'] = gettext("WebCfg - System: License page"); +$priv_list['page-system-license']['descr'] = gettext("Allow access to the 'System: License' page."); +$priv_list['page-system-license']['match'] = array(); +$priv_list['page-system-license']['match'][] = "license.php*"; + +$priv_list['page-services-loadbalancer-monitor'] = array(); +$priv_list['page-services-loadbalancer-monitor']['name'] = gettext("WebCfg - Services: Load Balancer: Monitors page"); +$priv_list['page-services-loadbalancer-monitor']['descr'] = gettext("Allow access to the 'Services: Load Balancer: Monitors' page."); +$priv_list['page-services-loadbalancer-monitor']['match'] = array(); +$priv_list['page-services-loadbalancer-monitor']['match'][] = "load_balancer_monitor.php*"; + +$priv_list['page-services-loadbalancer-monitor-edit'] = array(); +$priv_list['page-services-loadbalancer-monitor-edit']['name'] = gettext("WebCfg - Services: Load Balancer: Monitor: Edit page"); +$priv_list['page-services-loadbalancer-monitor-edit']['descr'] = gettext("Allow access to the 'Services: Load Balancer: Monitor: Edit' page."); +$priv_list['page-services-loadbalancer-monitor-edit']['match'] = array(); +$priv_list['page-services-loadbalancer-monitor-edit']['match'][] = "load_balancer_monitor_edit.php*"; + +$priv_list['page-loadbalancer-pool'] = array(); +$priv_list['page-loadbalancer-pool']['name'] = gettext("WebCfg - Load Balancer: Pool page"); +$priv_list['page-loadbalancer-pool']['descr'] = gettext("Allow access to the 'Load Balancer: Pool' page."); +$priv_list['page-loadbalancer-pool']['match'] = array(); +$priv_list['page-loadbalancer-pool']['match'][] = "load_balancer_pool.php*"; + +$priv_list['page-loadbalancer-pool-edit'] = array(); +$priv_list['page-loadbalancer-pool-edit']['name'] = gettext("WebCfg - Load Balancer: Pool: Edit page"); +$priv_list['page-loadbalancer-pool-edit']['descr'] = gettext("Allow access to the 'Load Balancer: Pool: Edit' page."); +$priv_list['page-loadbalancer-pool-edit']['match'] = array(); +$priv_list['page-loadbalancer-pool-edit']['match'][] = "load_balancer_pool_edit.php*"; + +$priv_list['page-services-loadbalancer-setting'] = array(); +$priv_list['page-services-loadbalancer-setting']['name'] = gettext("Webcfg - Services: Load Balancer: setting page"); +$priv_list['page-services-loadbalancer-setting']['descr'] = gettext("Allow access to the 'Settings: Load Balancer: Settings' page."); +$priv_list['page-services-loadbalancer-setting']['match'] = array(); +$priv_list['page-services-loadbalancer-setting']['match'][] = "load_balancer_setting.php*"; + +$priv_list['page-services-loadbalancer-virtualservers'] = array(); +$priv_list['page-services-loadbalancer-virtualservers']['name'] = gettext("WebCfg - Services: Load Balancer: Virtual Servers page"); +$priv_list['page-services-loadbalancer-virtualservers']['descr'] = gettext("Allow access to the 'Services: Load Balancer: Virtual Servers' page."); +$priv_list['page-services-loadbalancer-virtualservers']['match'] = array(); +$priv_list['page-services-loadbalancer-virtualservers']['match'][] = "load_balancer_virtual_server.php*"; + +$priv_list['page-services-ntpd'] = array(); +$priv_list['page-services-ntpd']['name'] = gettext("Webcfg - Services: NTP"); +$priv_list['page-services-ntpd']['descr'] = gettext("Allow access to the 'Services: NTP' page."); +$priv_list['page-services-ntpd']['match'] = array(); +$priv_list['page-services-ntpd']['match'][] = "services_ntpd.php*"; + +$priv_list['page-services-ntp-gps'] = array(); +$priv_list['page-services-ntp-gps']['name'] = gettext("Webcfg - Status: NTP GPS page"); +$priv_list['page-services-ntp-gps']['descr'] = gettext("Allow access to the 'Status: NTP Serial GPS' page."); +$priv_list['page-services-ntp-gps']['match'] = array(); +$priv_list['page-services-ntp-gps']['match'][] = "status_ntpd_gps.php*"; + +$priv_list['page-services-ntp-pps'] = array(); +$priv_list['page-services-ntp-pps']['name'] = gettext("Webcfg - Status: NTP PPS page"); +$priv_list['page-services-ntp-pps']['descr'] = gettext("Allow access to the 'Status: NTP PPS' page."); +$priv_list['page-services-ntp-pps']['match'] = array(); +$priv_list['page-services-ntp-pps']['match'][] = "status_ntpd_pps.php*"; + +$priv_list['page-loadbalancer-virtualserver-edit'] = array(); +$priv_list['page-loadbalancer-virtualserver-edit']['name'] = gettext("WebCfg - Load Balancer: Virtual Server: Edit page"); +$priv_list['page-loadbalancer-virtualserver-edit']['descr'] = gettext("Allow access to the 'Load Balancer: Virtual Server: Edit' page."); +$priv_list['page-loadbalancer-virtualserver-edit']['match'] = array(); +$priv_list['page-loadbalancer-virtualserver-edit']['match'][] = "load_balancer_virtual_server_edit.php*"; + +$priv_list['page-package-settings'] = array(); +$priv_list['page-package-settings']['name'] = gettext("WebCfg - Package: Settings page"); +$priv_list['page-package-settings']['descr'] = gettext("Allow access to the 'Package: Settings' page."); +$priv_list['page-package-settings']['match'] = array(); +$priv_list['page-package-settings']['match'][] = "pkg.php*"; + +$priv_list['page-package-edit'] = array(); +$priv_list['page-package-edit']['name'] = gettext("WebCfg - Package: Edit page"); +$priv_list['page-package-edit']['descr'] = gettext("Allow access to the 'Package: Edit' page."); +$priv_list['page-package-edit']['match'] = array(); +$priv_list['page-package-edit']['match'][] = "pkg_edit.php*"; + +$priv_list['page-system-packagemanager'] = array(); +$priv_list['page-system-packagemanager']['name'] = gettext("WebCfg - System: Package Manager page"); +$priv_list['page-system-packagemanager']['descr'] = gettext("Allow access to the 'System: Package Manager' page."); +$priv_list['page-system-packagemanager']['match'] = array(); +$priv_list['page-system-packagemanager']['match'][] = "pkg_mgr.php*"; + +$priv_list['page-system-packagemanager-installpackage'] = array(); +$priv_list['page-system-packagemanager-installpackage']['name'] = gettext("WebCfg - System: Package Manager: Install Package page"); +$priv_list['page-system-packagemanager-installpackage']['descr'] = gettext("Allow access to the 'System: Package Manager: Install Package' page."); +$priv_list['page-system-packagemanager-installpackage']['match'] = array(); +$priv_list['page-system-packagemanager-installpackage']['match'][] = "pkg_mgr_install.php*"; + +$priv_list['page-system-packagemanager-installed'] = array(); +$priv_list['page-system-packagemanager-installed']['name'] = gettext("WebCfg - System: Package Manager: Installed page"); +$priv_list['page-system-packagemanager-installed']['descr'] = gettext("Allow access to the 'System: Package Manager: Installed' page."); +$priv_list['page-system-packagemanager-installed']['match'] = array(); +$priv_list['page-system-packagemanager-installed']['match'][] = "pkg_mgr_installed.php*"; + +$priv_list['page-pkg-mgr-settings'] = array(); +$priv_list['page-pkg-mgr-settings']['name'] = gettext("WebCfg - Packages: Settings page"); +$priv_list['page-pkg-mgr-settings']['descr'] = gettext("Allow access to the 'Packages: Settings' page."); +$priv_list['page-pkg-mgr-settings']['match'] = array(); +$priv_list['page-pkg-mgr-settings']['match'][] = "pkg_mgr_settings.php*"; + +$priv_list['page-diagnostics-rebootsystem'] = array(); +$priv_list['page-diagnostics-rebootsystem']['name'] = gettext("WebCfg - Diagnostics: Reboot System page"); +$priv_list['page-diagnostics-rebootsystem']['descr'] = gettext("Allow access to the 'Diagnostics: Reboot System' page."); +$priv_list['page-diagnostics-rebootsystem']['match'] = array(); +$priv_list['page-diagnostics-rebootsystem']['match'][] = "reboot.php*"; + +$priv_list['page-diagnostics-restart-httpd'] = array(); +$priv_list['page-diagnostics-restart-httpd']['name'] = gettext("WebCfg - Diagnostics: Restart HTTPD : System page"); +$priv_list['page-diagnostics-restart-httpd']['descr'] = gettext("Allow access to the 'Diagnostics: Restart HTTPD: System' page."); +$priv_list['page-diagnostics-restart-httpd']['match'] = array(); +$priv_list['page-diagnostics-restart-httpd']['match'][] = "restart_httpd.php*"; + +$priv_list['page-services-captiveportal'] = array(); +$priv_list['page-services-captiveportal']['name'] = gettext("WebCfg - Services: Captive portal page"); +$priv_list['page-services-captiveportal']['descr'] = gettext("Allow access to the 'Services: Captive portal' page."); +$priv_list['page-services-captiveportal']['match'] = array(); +$priv_list['page-services-captiveportal']['match'][] = "services_captiveportal.php*"; + +$priv_list['page-services-captiveportal-filemanager'] = array(); +$priv_list['page-services-captiveportal-filemanager']['name'] = gettext("WebCfg - Services: Captive portal: File Manager page"); +$priv_list['page-services-captiveportal-filemanager']['descr'] = gettext("Allow access to the 'Services: Captive portal: File Manager' page."); +$priv_list['page-services-captiveportal-filemanager']['match'] = array(); +$priv_list['page-services-captiveportal-filemanager']['match'][] = "services_captiveportal_filemanager.php*"; + +$priv_list['page-services-captiveportal-allowedips'] = array(); +$priv_list['page-services-captiveportal-allowedips']['name'] = gettext("WebCfg - Services: Captive portal: Allowed IPs page"); +$priv_list['page-services-captiveportal-allowedips']['descr'] = gettext("Allow access to the 'Services: Captive portal: Allowed IPs' page."); +$priv_list['page-services-captiveportal-allowedips']['match'] = array(); +$priv_list['page-services-captiveportal-allowedips']['match'][] = "services_captiveportal_ip.php*"; + +$priv_list['page-services-captiveportal-editallowedips'] = array(); +$priv_list['page-services-captiveportal-editallowedips']['name'] = gettext("WebCfg - Services: Captive portal: Edit Allowed IPs page"); +$priv_list['page-services-captiveportal-editallowedips']['descr'] = gettext("Allow access to the 'Services: Captive portal: Edit Allowed IPs' page."); +$priv_list['page-services-captiveportal-editallowedips']['match'] = array(); +$priv_list['page-services-captiveportal-editallowedips']['match'][] = "services_captiveportal_ip_edit.php*"; + +$priv_list['page-services-captiveportal-macaddresses'] = array(); +$priv_list['page-services-captiveportal-macaddresses']['name'] = gettext("WebCfg - Services: Captive portal: Mac Addresses page"); +$priv_list['page-services-captiveportal-macaddresses']['descr'] = gettext("Allow access to the 'Services: Captive portal: Mac Addresses' page."); +$priv_list['page-services-captiveportal-macaddresses']['match'] = array(); +$priv_list['page-services-captiveportal-macaddresses']['match'][] = "services_captiveportal_mac.php*"; + +$priv_list['page-services-captiveportal-editmacaddresses'] = array(); +$priv_list['page-services-captiveportal-editmacaddresses']['name'] = gettext("WebCfg - Services: Captive portal: Edit MAC Addresses page"); +$priv_list['page-services-captiveportal-editmacaddresses']['descr'] = gettext("Allow access to the 'Services: Captive portal: Edit MAC Addresses' page."); +$priv_list['page-services-captiveportal-editmacaddresses']['match'] = array(); +$priv_list['page-services-captiveportal-editmacaddresses']['match'][] = "services_captiveportal_mac_edit.php*"; + +$priv_list['page-services-captiveportal-allowedhostnames'] = array(); +$priv_list['page-services-captiveportal-allowedhostnames']['name'] = gettext("WebCfg - Services: Captive portal: Allowed Hostnames page"); +$priv_list['page-services-captiveportal-allowedhostnames']['descr'] = gettext("Allow access to the 'Services: Captive portal: Allowed Hostnames' page."); +$priv_list['page-services-captiveportal-allowedhostnames']['match'] = array(); +$priv_list['page-services-captiveportal-allowedhostnames']['match'][] = "services_captiveportal_hostname.php*"; + +$priv_list['page-services-captiveportal-editallowedhostnames'] = array(); +$priv_list['page-services-captiveportal-editallowedhostnames']['name'] = gettext("WebCfg - Services: Captive portal: Edit Allowed Hostnames page"); +$priv_list['page-services-captiveportal-editallowedhostnames']['descr'] = gettext("Allow access to the 'Services: Captive portal: Allowed Hostnames' page."); +$priv_list['page-services-captiveportal-editallowedhostnames']['match'] = array(); +$priv_list['page-services-captiveportal-editallowedhostnames']['match'][] = "services_captiveportal_hostname_edit.php*"; + +$priv_list['page-services-captiveportal-editzones'] = array(); +$priv_list['page-services-captiveportal-editzones']['name'] = gettext("Webcfg - Services: Captive portal: Edit Zones page"); +$priv_list['page-services-captiveportal-editzones']['descr'] = gettext("Allow access to the 'Services: Captive portal: Edit Zones' page."); +$priv_list['page-services-captiveportal-editzones']['match'] = array(); +$priv_list['page-services-captiveportal-editzones']['match'][] = "services_captiveportal_zones_edit.php*"; + +$priv_list['page-services-captiveportal-vouchers'] = array(); +$priv_list['page-services-captiveportal-vouchers']['name'] = gettext("WebCfg - Services: Captive portal Vouchers page"); +$priv_list['page-services-captiveportal-vouchers']['descr'] = gettext("Allow access to the 'Services: Captive portal Vouchers' page."); +$priv_list['page-services-captiveportal-vouchers']['match'] = array(); +$priv_list['page-services-captiveportal-vouchers']['match'][] = "services_captiveportal_vouchers.php*"; + +$priv_list['page-services-captiveportal-voucher-edit'] = array(); +$priv_list['page-services-captiveportal-voucher-edit']['name'] = "WebCfg - Services: Captive portal Voucher Rolls page"; +$priv_list['page-services-captiveportal-voucher-edit']['descr'] = "Allow access to the 'Services: Captive portal Edit Voucher Rolls' page."; +$priv_list['page-services-captiveportal-voucher-edit']['match'] = array(); +$priv_list['page-services-captiveportal-voucher-edit']['match'][] = "services_captiveportal_vouchers_edit.php*"; + +$priv_list['page-services-captiveportal-zones'] = array(); +$priv_list['page-services-captiveportal-zones']['name'] = gettext("WebCfg - Services: Captive portal Zones page"); +$priv_list['page-services-captiveportal-zones']['descr'] = gettext("Allow access to the 'Services: Captive portal Zones' page."); +$priv_list['page-services-captiveportal-zones']['match'] = array(); +$priv_list['page-services-captiveportal-zones']['match'][] = "services_captiveportal_zones.php*"; + +$priv_list['page-services-dhcpserver'] = array(); +$priv_list['page-services-dhcpserver']['name'] = gettext("WebCfg - Services: DHCP server page"); +$priv_list['page-services-dhcpserver']['descr'] = gettext("Allow access to the 'Services: DHCP server' page."); +$priv_list['page-services-dhcpserver']['match'] = array(); +$priv_list['page-services-dhcpserver']['match'][] = "services_dhcp.php*"; + +$priv_list['page-services-dhcpserver-editstaticmapping'] = array(); +$priv_list['page-services-dhcpserver-editstaticmapping']['name'] = gettext("WebCfg - Services: DHCP Server : Edit static mapping page"); +$priv_list['page-services-dhcpserver-editstaticmapping']['descr'] = gettext("Allow access to the 'Services: DHCP Server : Edit static mapping' page."); +$priv_list['page-services-dhcpserver-editstaticmapping']['match'] = array(); +$priv_list['page-services-dhcpserver-editstaticmapping']['match'][] = "services_dhcp_edit.php*"; + +$priv_list['page-services-dhcprelay'] = array(); +$priv_list['page-services-dhcprelay']['name'] = gettext("WebCfg - Services: DHCP Relay page"); +$priv_list['page-services-dhcprelay']['descr'] = gettext("Allow access to the 'Services: DHCP Relay' page."); +$priv_list['page-services-dhcprelay']['match'] = array(); +$priv_list['page-services-dhcprelay']['match'][] = "services_dhcp_relay.php*"; + +$priv_list['page-services-dhcpv6server'] = array(); +$priv_list['page-services-dhcpv6server']['name'] = gettext("Webcfg - Services: DHCPv6 server page"); +$priv_list['page-services-dhcpv6server']['descr'] = gettext("Allow access to the 'Services: DHCPv6 server' page."); +$priv_list['page-services-dhcpv6server']['match'] = array(); +$priv_list['page-services-dhcpv6server']['match'][] = "services_dhcpv6.php*"; + +$priv_list['page-services-dhcpserverv6-editstaticmapping'] = array(); +$priv_list['page-services-dhcpserverv6-editstaticmapping']['name'] = gettext("Webcfg - Services: DHCPv6 Server : Edit static mapping page"); +$priv_list['page-services-dhcpserverv6-editstaticmapping']['descr'] = gettext("Allow access to the 'Services: DHCPv6 Server : Edit static mapping' page."); +$priv_list['page-services-dhcpserverv6-editstaticmapping']['match'] = array(); +$priv_list['page-services-dhcpserverv6-editstaticmapping']['match'][] = "services_dhcpv6_edit.php*"; + +$priv_list['page-services-dhcpv6relay'] = array(); +$priv_list['page-services-dhcpv6relay']['name'] = gettext("Webcfg - Services: DHCPv6 Relay page"); +$priv_list['page-services-dhcpv6relay']['descr'] = gettext("Allow access to the 'Services: DHCPv6 Relay' page."); +$priv_list['page-services-dhcpv6relay']['match'] = array(); +$priv_list['page-services-dhcpv6relay']['match'][] = "services_dhcpv6_relay.php*"; + +$priv_list['page-services-dnsforwarder'] = array(); +$priv_list['page-services-dnsforwarder']['name'] = gettext("WebCfg - Services: DNS Forwarder page"); +$priv_list['page-services-dnsforwarder']['descr'] = gettext("Allow access to the 'Services: DNS Forwarder' page."); +$priv_list['page-services-dnsforwarder']['match'] = array(); +$priv_list['page-services-dnsforwarder']['match'][] = "services_dnsmasq.php*"; + +$priv_list['page-services-dnsforwarder-editdomainoverride'] = array(); +$priv_list['page-services-dnsforwarder-editdomainoverride']['name'] = gettext("WebCfg - Services: DNS Forwarder: Edit Domain Override page"); +$priv_list['page-services-dnsforwarder-editdomainoverride']['descr'] = gettext("Allow access to the 'Services: DNS Forwarder: Edit Domain Override' page."); +$priv_list['page-services-dnsforwarder-editdomainoverride']['match'] = array(); +$priv_list['page-services-dnsforwarder-editdomainoverride']['match'][] = "services_dnsmasq_domainoverride_edit.php*"; + +$priv_list['page-services-dnsforwarder-edithost'] = array(); +$priv_list['page-services-dnsforwarder-edithost']['name'] = gettext("WebCfg - Services: DNS Forwarder: Edit host page"); +$priv_list['page-services-dnsforwarder-edithost']['descr'] = gettext("Allow access to the 'Services: DNS Forwarder: Edit host' page."); +$priv_list['page-services-dnsforwarder-edithost']['match'] = array(); +$priv_list['page-services-dnsforwarder-edithost']['match'][] = "services_dnsmasq_edit.php*"; + +$priv_list['page-services-dnsresolver'] = array(); +$priv_list['page-services-dnsresolver']['name'] = gettext("WebCfg - Services: DNS Resolver page"); +$priv_list['page-services-dnsresolver']['descr'] = gettext("Allow access to the 'Services: DNS Resolver' page."); +$priv_list['page-services-dnsresolver']['match'] = array(); +$priv_list['page-services-dnsresolver']['match'][] = "services_unbound.php*"; + +$priv_list['page-services-dnsresolver-advanced'] = array(); +$priv_list['page-services-dnsresolver-advanced']['name'] = gettext("WebCfg - Services: DNS Resolver: Advanced page"); +$priv_list['page-services-dnsresolver-advanced']['descr'] = gettext("Allow access to the 'Services: DNS Resolver: Advanced' page."); +$priv_list['page-services-dnsresolver-advanced']['match'] = array(); +$priv_list['page-services-dnsresolver-advanced']['match'][] = "services_unbound_advanced.php*"; + +$priv_list['page-services-dnsresolver-acls'] = array(); +$priv_list['page-services-dnsresolver-acls']['name'] = gettext("WebCfg - Services: DNS Resolver: Access Lists page"); +$priv_list['page-services-dnsresolver-acls']['descr'] = gettext("Allow access to the 'Services: DNS Resolver: Access Lists' page."); +$priv_list['page-services-dnsresolver-acls']['match'] = array(); +$priv_list['page-services-dnsresolver-acls']['match'][] = "services_unbound_acls.php*"; + +$priv_list['page-services-dnsresolver-editacls'] = array(); +$priv_list['page-services-dnsresolver-editacls']['name'] = gettext("WebCfg - Services: DNS Resolver: Access Lists: Edit page"); +$priv_list['page-services-dnsresolver-editacls']['descr'] = gettext("Allow access to the 'Services: DNS Resolver: Access Lists: Edit' page."); +$priv_list['page-services-dnsresolver-editacls']['match'] = array(); +$priv_list['page-services-dnsresolver-editacls']['match'][] = "services_unbound_acls_edit.php*"; + +$priv_list['page-services-dnsresolver-editdomainoverride'] = array(); +$priv_list['page-services-dnsresolver-editdomainoverride']['name'] = gettext("WebCfg - Services: DNS Resolver: Edit Domain Override page"); +$priv_list['page-services-dnsresolver-editdomainoverride']['descr'] = gettext("Allow access to the 'Services: DNS Resolver: Edit Domain Override' page."); +$priv_list['page-services-dnsresolver-editdomainoverride']['match'] = array(); +$priv_list['page-services-dnsresolver-editdomainoverride']['match'][] = "services_unbound_domainoverride_edit.php*"; + +$priv_list['page-services-dnsresolver-edithost'] = array(); +$priv_list['page-services-dnsresolver-edithost']['name'] = gettext("WebCfg - Services: DNS Resolver: Edit host page"); +$priv_list['page-services-dnsresolver-edithost']['descr'] = gettext("Allow access to the 'Services: DNS Resolver: Edit host' page."); +$priv_list['page-services-dnsresolver-edithost']['match'] = array(); +$priv_list['page-services-dnsresolver-edithost']['match'][] = "services_unbound_host_edit.php*"; + +$priv_list['page-services-dynamicdnsclients'] = array(); +$priv_list['page-services-dynamicdnsclients']['name'] = gettext("WebCfg - Services: Dynamic DNS clients page"); +$priv_list['page-services-dynamicdnsclients']['descr'] = gettext("Allow access to the 'Services: Dynamic DNS clients' page."); +$priv_list['page-services-dynamicdnsclients']['match'] = array(); +$priv_list['page-services-dynamicdnsclients']['match'][] = "services_dyndns.php*"; + +$priv_list['page-services-dynamicdnsclient'] = array(); +$priv_list['page-services-dynamicdnsclient']['name'] = gettext("WebCfg - Services: Dynamic DNS client page"); +$priv_list['page-services-dynamicdnsclient']['descr'] = gettext("Allow access to the 'Services: Dynamic DNS client' page."); +$priv_list['page-services-dynamicdnsclient']['match'] = array(); +$priv_list['page-services-dynamicdnsclient']['match'][] = "services_dyndns_edit.php*"; + +$priv_list['page-services-igmpproxy'] = array(); +$priv_list['page-services-igmpproxy']['name'] = gettext("WebCfg - Services: Igmpproxy page"); +$priv_list['page-services-igmpproxy']['descr'] = gettext("Allow access to the 'Services: Igmpproxy' page."); +$priv_list['page-services-igmpproxy']['match'] = array(); +$priv_list['page-services-igmpproxy']['match'][] = "services_igmpproxy.php*"; + +$priv_list['page-services-igmpproxy-edit'] = array(); +$priv_list['page-services-igmpproxy-edit']['name'] = gettext("Firewall: Igmpproxy: Edit page"); +$priv_list['page-services-igmpproxy-edit']['descr'] = gettext("Allow access to the 'Services: Igmpproxy: Edit' page."); +$priv_list['page-services-igmpproxy-edit']['match'] = array(); +$priv_list['page-services-igmpproxy-edit']['match'][] = "services_igmpproxy_edit.php*"; + +$priv_list['page-services-rfc2136clients'] = array(); +$priv_list['page-services-rfc2136clients']['name'] = gettext("WebCfg - Services: RFC 2136 clients page"); +$priv_list['page-services-rfc2136clients']['descr'] = gettext("Allow access to the 'Services: RFC 2136 clients' page."); +$priv_list['page-services-rfc2136clients']['match'] = array(); +$priv_list['page-services-rfc2136clients']['match'][] = "services_rfc2136.php*"; + +$priv_list['page-services-router-advertisements'] = array(); +$priv_list['page-services-router-advertisements']['name'] = gettext("Webcfg - Services: Router Advertisements page"); +$priv_list['page-services-router-advertisements']['descr'] = gettext("Allow access to the 'Services: Router Advertisements' page."); +$priv_list['page-services-router-advertisements']['match'] = array(); +$priv_list['page-services-router-advertisements']['match'][] = "services_router_advertisements.php*"; + +$priv_list['page-services-snmp'] = array(); +$priv_list['page-services-snmp']['name'] = gettext("WebCfg - Services: SNMP page"); +$priv_list['page-services-snmp']['descr'] = gettext("Allow access to the 'Services: SNMP' page."); +$priv_list['page-services-snmp']['match'] = array(); +$priv_list['page-services-snmp']['match'][] = "services_snmp.php*"; + +$priv_list['page-services-wakeonlan'] = array(); +$priv_list['page-services-wakeonlan']['name'] = gettext("WebCfg - Services: Wake on LAN page"); +$priv_list['page-services-wakeonlan']['descr'] = gettext("Allow access to the 'Services: Wake on LAN' page."); +$priv_list['page-services-wakeonlan']['match'] = array(); +$priv_list['page-services-wakeonlan']['match'][] = "services_wol.php*"; + +$priv_list['page-services-wakeonlan-edit'] = array(); +$priv_list['page-services-wakeonlan-edit']['name'] = gettext("WebCfg - Services: Wake on LAN: Edit page"); +$priv_list['page-services-wakeonlan-edit']['descr'] = gettext("Allow access to the 'Services: Wake on LAN: Edit' page."); +$priv_list['page-services-wakeonlan-edit']['match'] = array(); +$priv_list['page-services-wakeonlan-edit']['match'][] = "services_wol_edit.php*"; + +$priv_list['page-diagnostics-cpuutilization'] = array(); +$priv_list['page-diagnostics-cpuutilization']['name'] = gettext("WebCfg - Diagnostics: CPU Utilization page"); +$priv_list['page-diagnostics-cpuutilization']['descr'] = gettext("Allow access to the 'Diagnostics: CPU Utilization' page."); +$priv_list['page-diagnostics-cpuutilization']['match'] = array(); +$priv_list['page-diagnostics-cpuutilization']['match'][] = "stats.php*"; + +$priv_list['page-hidden-detailedstatus'] = array(); +$priv_list['page-hidden-detailedstatus']['name'] = gettext("WebCfg - Hidden: Detailed Status page"); +$priv_list['page-hidden-detailedstatus']['descr'] = gettext("Allow access to the 'Hidden: Detailed Status' page."); +$priv_list['page-hidden-detailedstatus']['match'] = array(); +$priv_list['page-hidden-detailedstatus']['match'][] = "status.php*"; + +$priv_list['page-status-captiveportal'] = array(); +$priv_list['page-status-captiveportal']['name'] = gettext("WebCfg - Status: Captive portal page"); +$priv_list['page-status-captiveportal']['descr'] = gettext("Allow access to the 'Status: Captive portal' page."); +$priv_list['page-status-captiveportal']['match'] = array(); +$priv_list['page-status-captiveportal']['match'][] = "status_captiveportal.php*"; + +$priv_list['page-status-captiveportal-expire'] = array(); +$priv_list['page-status-captiveportal-expire']['name'] = gettext("Webcfg - Status: Captive portal Expire Vouchers page"); +$priv_list['page-status-captiveportal-expire']['descr'] = gettext("Allow access to the 'Status: Captive portal Expire Vouchers' page."); +$priv_list['page-status-captiveportal-expire']['match'] = array(); +$priv_list['page-status-captiveportal-expire']['match'][] = "status_captiveportal_expire.php*"; + +$priv_list['page-status-captiveportal-test'] = array(); +$priv_list['page-status-captiveportal-test']['name'] = gettext("WebCfg - Status: Captive portal Test Vouchers page"); +$priv_list['page-status-captiveportal-test']['descr'] = gettext("Allow access to the 'Status: Captive portal Test Vouchers' page."); +$priv_list['page-status-captiveportal-test']['match'] = array(); +$priv_list['page-status-captiveportal-test']['match'][] = "status_captiveportal_test.php*"; + +$priv_list['page-status-captiveportal-voucher-rolls'] = array(); +$priv_list['page-status-captiveportal-voucher-rolls']['name'] = gettext("WebCfg - Status: Captive portal Voucher Rolls page"); +$priv_list['page-status-captiveportal-voucher-rolls']['descr'] = gettext("Allow access to the 'Status: Captive portal Voucher Rolls' page."); +$priv_list['page-status-captiveportal-voucher-rolls']['match'] = array(); +$priv_list['page-status-captiveportal-voucher-rolls']['match'][] = "status_captiveportal_voucher_rolls.php*"; + +$priv_list['page-status-captiveportal-vouchers'] = array(); +$priv_list['page-status-captiveportal-vouchers']['name'] = gettext("WebCfg - Status: Captive portal Vouchers page"); +$priv_list['page-status-captiveportal-vouchers']['descr'] = gettext("Allow access to the 'Status: Captive portal Vouchers' page."); +$priv_list['page-status-captiveportal-vouchers']['match'] = array(); +$priv_list['page-status-captiveportal-vouchers']['match'][] = "status_captiveportal_vouchers.php*"; + +$priv_list['page-status-dhcpleases'] = array(); +$priv_list['page-status-dhcpleases']['name'] = gettext("WebCfg - Status: DHCP leases page"); +$priv_list['page-status-dhcpleases']['descr'] = gettext("Allow access to the 'Status: DHCP leases' page."); +$priv_list['page-status-dhcpleases']['match'] = array(); +$priv_list['page-status-dhcpleases']['match'][] = "status_dhcp_leases.php*"; + +$priv_list['page-status-dhcpv6leases'] = array(); +$priv_list['page-status-dhcpv6leases']['name'] = gettext("Webcfg - Status: DHCPv6 leases page"); +$priv_list['page-status-dhcpv6leases']['descr'] = gettext("Allow access to the 'Status: DHCPv6 leases' page."); +$priv_list['page-status-dhcpv6leases']['match'] = array(); +$priv_list['page-status-dhcpv6leases']['match'][] = "status_dhcpv6_leases.php*"; + +$priv_list['page-status-filterreloadstatus'] = array(); +$priv_list['page-status-filterreloadstatus']['name'] = gettext("WebCfg - Status: Filter Reload Status page"); +$priv_list['page-status-filterreloadstatus']['descr'] = gettext("Allow access to the 'Status: Filter Reload Status' page."); +$priv_list['page-status-filterreloadstatus']['match'] = array(); +$priv_list['page-status-filterreloadstatus']['match'][] = "status_filter_reload.php*"; + +$priv_list['page-status-gatewaygroups'] = array(); +$priv_list['page-status-gatewaygroups']['name'] = gettext("WebCfg - Status: Gateway Groups page"); +$priv_list['page-status-gatewaygroups']['descr'] = gettext("Allow access to the 'Status: Gateway Groups' page."); +$priv_list['page-status-gatewaygroups']['match'] = array(); +$priv_list['page-status-gatewaygroups']['match'][] = "status_gateway_groups.php*"; + +$priv_list['page-status-gateways'] = array(); +$priv_list['page-status-gateways']['name'] = gettext("WebCfg - Status: Gateways page"); +$priv_list['page-status-gateways']['descr'] = gettext("Allow access to the 'Status: Gateways' page."); +$priv_list['page-status-gateways']['match'] = array(); +$priv_list['page-status-gateways']['match'][] = "status_gateways.php*"; + +$priv_list['page-status-trafficgraph'] = array(); +$priv_list['page-status-trafficgraph']['name'] = gettext("WebCfg - Status: Traffic Graph page"); +$priv_list['page-status-trafficgraph']['descr'] = gettext("Allow access to the 'Status: Traffic Graph' page."); +$priv_list['page-status-trafficgraph']['match'] = array(); +$priv_list['page-status-trafficgraph']['match'][] = "status_graph.php*"; +$priv_list['page-status-trafficgraph']['match'][] = "bandwidth_by_ip.php*"; +$priv_list['page-status-trafficgraph']['match'][] = "graph.php*"; +$priv_list['page-status-trafficgraph']['match'][] = "ifstats.php*"; + +$priv_list['page-status-cpuload'] = array(); +$priv_list['page-status-cpuload']['name'] = gettext("WebCfg - Status: CPU load page"); +$priv_list['page-status-cpuload']['descr'] = gettext("Allow access to the 'Status: CPU load' page."); +$priv_list['page-status-cpuload']['match'] = array(); +$priv_list['page-status-cpuload']['match'][] = "status_graph_cpu.php*"; + +$priv_list['page-status-interfaces'] = array(); +$priv_list['page-status-interfaces']['name'] = gettext("WebCfg - Status: Interfaces page"); +$priv_list['page-status-interfaces']['descr'] = gettext("Allow access to the 'Status: Interfaces' page."); +$priv_list['page-status-interfaces']['match'] = array(); +$priv_list['page-status-interfaces']['match'][] = "status_interfaces.php*"; + +$priv_list['page-status-loadbalancer-pool'] = array(); +$priv_list['page-status-loadbalancer-pool']['name'] = gettext("WebCfg - Status: Load Balancer: Pool page"); +$priv_list['page-status-loadbalancer-pool']['descr'] = gettext("Allow access to the 'Status: Load Balancer: Pool' page."); +$priv_list['page-status-loadbalancer-pool']['match'] = array(); +$priv_list['page-status-loadbalancer-pool']['match'][] = "status_lb_pool.php*"; + +$priv_list['page-status-loadbalancer-virtualserver'] = array(); +$priv_list['page-status-loadbalancer-virtualserver']['name'] = gettext("WebCfg - Status: Load Balancer: Virtual Server page"); +$priv_list['page-status-loadbalancer-virtualserver']['descr'] = gettext("Allow access to the 'Status: Load Balancer: Virtual Server' page."); +$priv_list['page-status-loadbalancer-virtualserver']['match'] = array(); +$priv_list['page-status-loadbalancer-virtualserver']['match'][] = "status_lb_vs.php*"; + +$priv_list['page-status-openvpn'] = array(); +$priv_list['page-status-openvpn']['name'] = gettext("WebCfg - Status: OpenVPN page"); +$priv_list['page-status-openvpn']['descr'] = gettext("Allow access to the 'Status: OpenVPN' page."); +$priv_list['page-status-openvpn']['match'] = array(); +$priv_list['page-status-openvpn']['match'][] = "status_openvpn.php*"; + +$priv_list['page-status-trafficshaper-queues'] = array(); +$priv_list['page-status-trafficshaper-queues']['name'] = gettext("WebCfg - Status: Traffic shaper: Queues page"); +$priv_list['page-status-trafficshaper-queues']['descr'] = gettext("Allow access to the 'Status: Traffic shaper: Queues' page."); +$priv_list['page-status-trafficshaper-queues']['match'] = array(); +$priv_list['page-status-trafficshaper-queues']['match'][] = "status_queues.php*"; + +$priv_list['page-status-rrdgraphs'] = array(); +$priv_list['page-status-rrdgraphs']['name'] = gettext("WebCfg - Status: RRD Graphs page"); +$priv_list['page-status-rrdgraphs']['descr'] = gettext("Allow access to the 'Status: RRD Graphs' page."); +$priv_list['page-status-rrdgraphs']['match'] = array(); +$priv_list['page-status-rrdgraphs']['match'][] = "status_rrd_graph.php*"; +$priv_list['page-status-rrdgraphs']['match'][] = "status_rrd_graph_img.php*"; + +$priv_list['page-status-rrdgraph-settings'] = array(); +$priv_list['page-status-rrdgraph-settings']['name'] = gettext("WebCfg - Status: RRD Graphs settings page"); +$priv_list['page-status-rrdgraph-settings']['descr'] = gettext("Allow access to the 'Status: RRD Graphs: settings' page."); +$priv_list['page-status-rrdgraph-settings']['match'] = array(); +$priv_list['page-status-rrdgraph-settings']['match'][] = "status_rrd_graph_settings.php*"; + +$priv_list['page-status-services'] = array(); +$priv_list['page-status-services']['name'] = gettext("WebCfg - Status: Services page"); +$priv_list['page-status-services']['descr'] = gettext("Allow access to the 'Status: Services' page."); +$priv_list['page-status-services']['match'] = array(); +$priv_list['page-status-services']['match'][] = "status_services.php*"; + +$priv_list['page-status-upnpstatus'] = array(); +$priv_list['page-status-upnpstatus']['name'] = gettext("WebCfg - Status: UPnP Status page"); +$priv_list['page-status-upnpstatus']['descr'] = gettext("Allow access to the 'Status: UPnP Status' page."); +$priv_list['page-status-upnpstatus']['match'] = array(); +$priv_list['page-status-upnpstatus']['match'][] = "status_upnp.php*"; + +$priv_list['page-diagnostics-wirelessstatus'] = array(); +$priv_list['page-diagnostics-wirelessstatus']['name'] = gettext("WebCfg - Status: Wireless page"); +$priv_list['page-diagnostics-wirelessstatus']['descr'] = gettext("Allow access to the 'Status: Wireless' page."); +$priv_list['page-diagnostics-wirelessstatus']['match'] = array(); +$priv_list['page-diagnostics-wirelessstatus']['match'][] = "status_wireless.php*"; + +$priv_list['page-system-generalsetup'] = array(); +$priv_list['page-system-generalsetup']['name'] = gettext("WebCfg - System: General Setup page"); +$priv_list['page-system-generalsetup']['descr'] = gettext("Allow access to the 'System: General Setup' page."); +$priv_list['page-system-generalsetup']['match'] = array(); +$priv_list['page-system-generalsetup']['match'][] = "system.php*"; + +$priv_list['page-system-advanced-admin'] = array(); +$priv_list['page-system-advanced-admin']['name'] = gettext("WebCfg - System: Advanced: Admin Access Page"); +$priv_list['page-system-advanced-admin']['descr'] = gettext("Allow access to the 'System: Advanced: Admin Access' page."); +$priv_list['page-system-advanced-admin']['match'] = array(); +$priv_list['page-system-advanced-admin']['match'][] = "system_advanced_admin.php*"; + +$priv_list['page-system-advanced-firewall'] = array(); +$priv_list['page-system-advanced-firewall']['name'] = gettext("WebCfg - System: Advanced: Firewall and NAT page"); +$priv_list['page-system-advanced-firewall']['descr'] = gettext("Allow access to the 'System: Advanced: Firewall and NAT' page."); +$priv_list['page-system-advanced-firewall']['match'] = array(); +$priv_list['page-system-advanced-firewall']['match'][] = "system_advanced_firewall.php*"; + +$priv_list['page-system-advanced-misc'] = array(); +$priv_list['page-system-advanced-misc']['name'] = gettext("WebCfg - System: Advanced: Miscellaneous page"); +$priv_list['page-system-advanced-misc']['descr'] = gettext("Allow access to the 'System: Advanced: Miscellaneous' page."); +$priv_list['page-system-advanced-misc']['match'] = array(); +$priv_list['page-system-advanced-misc']['match'][] = "system_advanced_misc.php*"; + +$priv_list['page-system-advanced-network'] = array(); +$priv_list['page-system-advanced-network']['name'] = gettext("WebCfg - System: Advanced: Networking page"); +$priv_list['page-system-advanced-network']['descr'] = gettext("Allow access to the 'System: Advanced: Networking' page."); +$priv_list['page-system-advanced-network']['match'] = array(); +$priv_list['page-system-advanced-network']['match'][] = "system_advanced_network.php*"; + +$priv_list['page-system-advanced-notifications'] = array(); +$priv_list['page-system-advanced-notifications']['name'] = gettext("WebCfg - System: Advanced: Notifications page"); +$priv_list['page-system-advanced-notifications']['descr'] = gettext("Allow access to the 'System: Advanced: Notifications' page."); +$priv_list['page-system-advanced-notifications']['match'] = array(); +$priv_list['page-system-advanced-notifications']['match'][] = "system_advanced_notifications.php*"; + +$priv_list['page-system-advanced-sysctl'] = array(); +$priv_list['page-system-advanced-sysctl']['name'] = gettext("WebCfg - System: Advanced: Tunables page"); +$priv_list['page-system-advanced-sysctl']['descr'] = gettext("Allow access to the 'System: Advanced: Tunables' page."); +$priv_list['page-system-advanced-sysctl']['match'] = array(); +$priv_list['page-system-advanced-sysctl']['match'][] = "system_advanced_sysctl.php*"; + +$priv_list['page-system-authservers'] = array(); +$priv_list['page-system-authservers']['name'] = gettext("WebCfg - System: Authentication Servers"); +$priv_list['page-system-authservers']['descr'] = gettext("Allow access to the 'System: Authentication Servers' page."); +$priv_list['page-system-authservers']['match'] = array(); +$priv_list['page-system-authservers']['match'][] = "system_authservers.php*"; + +$priv_list['page-system-camanager'] = array(); +$priv_list['page-system-camanager']['name'] = gettext("WebCfg - System: CA Manager"); +$priv_list['page-system-camanager']['descr'] = gettext("Allow access to the 'System: CA Manager' page."); +$priv_list['page-system-camanager']['match'] = array(); +$priv_list['page-system-camanager']['match'][] = "system_camanager.php*"; + +$priv_list['page-system-certmanager'] = array(); +$priv_list['page-system-certmanager']['name'] = gettext("WebCfg - System: Certificate Manager"); +$priv_list['page-system-certmanager']['descr'] = gettext("Allow access to the 'System: Certificate Manager' page."); +$priv_list['page-system-certmanager']['match'] = array(); +$priv_list['page-system-certmanager']['match'][] = "system_certmanager.php*"; + +$priv_list['page-system-crlmanager'] = array(); +$priv_list['page-system-crlmanager']['name'] = gettext("WebCfg - System: CRL Manager"); +$priv_list['page-system-crlmanager']['descr'] = gettext("Allow access to the 'System: CRL Manager' page."); +$priv_list['page-system-crlmanager']['match'] = array(); +$priv_list['page-system-crlmanager']['match'][] = "system_crlmanager.php*"; + +$priv_list['page-system-firmware-manualupdate'] = array(); +$priv_list['page-system-firmware-manualupdate']['name'] = gettext("WebCfg - System: Firmware: Manual Update page"); +$priv_list['page-system-firmware-manualupdate']['descr'] = gettext("Allow access to the 'System: Firmware: Manual Update' page."); +$priv_list['page-system-firmware-manualupdate']['match'] = array(); +$priv_list['page-system-firmware-manualupdate']['match'][] = "system_firmware.php*"; + +$priv_list['page-system-firmware-checkforupdate'] = array(); +$priv_list['page-system-firmware-checkforupdate']['name'] = gettext("WebCfg - System: Firmware: Check For Update page"); +$priv_list['page-system-firmware-checkforupdate']['descr'] = gettext("Allow access to the 'System: Firmware: Check For Update' page."); +$priv_list['page-system-firmware-checkforupdate']['match'] = array(); +$priv_list['page-system-firmware-checkforupdate']['match'][] = "system_firmware_auto.php*"; + +$priv_list['page-system-firmware-autoupdate'] = array(); +$priv_list['page-system-firmware-autoupdate']['name'] = gettext("WebCfg - System: Firmware: Auto Update page"); +$priv_list['page-system-firmware-autoupdate']['descr'] = gettext("Allow access to the 'System: Firmware: Auto Update' page."); +$priv_list['page-system-firmware-autoupdate']['match'] = array(); +$priv_list['page-system-firmware-autoupdate']['match'][] = "system_firmware_check.php*"; + +$priv_list['page-system-firmware-settings'] = array(); +$priv_list['page-system-firmware-settings']['name'] = gettext("WebCfg - System: Firmware: Settings page"); +$priv_list['page-system-firmware-settings']['descr'] = gettext("Allow access to the 'System: Firmware: Settings' page."); +$priv_list['page-system-firmware-settings']['match'] = array(); +$priv_list['page-system-firmware-settings']['match'][] = "system_firmware_settings.php*"; + +$priv_list['page-system-gatewaygroups'] = array(); +$priv_list['page-system-gatewaygroups']['name'] = gettext("WebCfg - System: Gateway Groups page"); +$priv_list['page-system-gatewaygroups']['descr'] = gettext("Allow access to the 'System: Gateway Groups' page."); +$priv_list['page-system-gatewaygroups']['match'] = array(); +$priv_list['page-system-gatewaygroups']['match'][] = "system_gateway_groups.php*"; + +$priv_list['page-system-gateways-editgatewaygroups'] = array(); +$priv_list['page-system-gateways-editgatewaygroups']['name'] = gettext("WebCfg - System: Gateways: Edit Gateway Groups page"); +$priv_list['page-system-gateways-editgatewaygroups']['descr'] = gettext("Allow access to the 'System: Gateways: Edit Gateway Groups' page."); +$priv_list['page-system-gateways-editgatewaygroups']['match'] = array(); +$priv_list['page-system-gateways-editgatewaygroups']['match'][] = "system_gateway_groups_edit.php*"; + +$priv_list['page-system-gateways'] = array(); +$priv_list['page-system-gateways']['name'] = gettext("WebCfg - System: Gateways page"); +$priv_list['page-system-gateways']['descr'] = gettext("Allow access to the 'System: Gateways' page."); +$priv_list['page-system-gateways']['match'] = array(); +$priv_list['page-system-gateways']['match'][] = "system_gateways.php*"; + +$priv_list['page-system-gateways-editgateway'] = array(); +$priv_list['page-system-gateways-editgateway']['name'] = gettext("WebCfg - System: Gateways: Edit Gateway page"); +$priv_list['page-system-gateways-editgateway']['descr'] = gettext("Allow access to the 'System: Gateways: Edit Gateway' page."); +$priv_list['page-system-gateways-editgateway']['match'] = array(); +$priv_list['page-system-gateways-editgateway']['match'][] = "system_gateways_edit.php*"; + +$priv_list['page-system-groupmanager'] = array(); +$priv_list['page-system-groupmanager']['name'] = gettext("WebCfg - System: Group manager page"); +$priv_list['page-system-groupmanager']['descr'] = gettext("Allow access to the 'System: Group manager' page."); +$priv_list['page-system-groupmanager']['match'] = array(); +$priv_list['page-system-groupmanager']['match'][] = "system_groupmanager.php*"; + +$priv_list['page-system-groupmanager-addprivs'] = array(); +$priv_list['page-system-groupmanager-addprivs']['name'] = gettext("WebCfg - System: Group Manager: Add Privileges page"); +$priv_list['page-system-groupmanager-addprivs']['descr'] = gettext("Allow access to the 'System: Group Manager: Add Privileges' page."); +$priv_list['page-system-groupmanager-addprivs']['match'] = array(); +$priv_list['page-system-groupmanager-addprivs']['match'][] = "system_groupmanager_addprivs.php*"; + +$priv_list['page-system-hasync'] = array(); +$priv_list['page-system-hasync']['name'] = gettext("Webcfg - System: High Availability Sync"); +$priv_list['page-system-hasync']['descr'] = gettext("Allow access to the 'System: High Availability Sync' page."); +$priv_list['page-system-hasync']['match'] = array(); +$priv_list['page-system-hasync']['match'][] = "system_hasync.php*"; + +$priv_list['page-system-staticroutes'] = array(); +$priv_list['page-system-staticroutes']['name'] = gettext("WebCfg - System: Static Routes page"); +$priv_list['page-system-staticroutes']['descr'] = gettext("Allow access to the 'System: Static Routes' page."); +$priv_list['page-system-staticroutes']['match'] = array(); +$priv_list['page-system-staticroutes']['match'][] = "system_routes.php*"; + +$priv_list['page-system-staticroutes-editroute'] = array(); +$priv_list['page-system-staticroutes-editroute']['name'] = gettext("WebCfg - System: Static Routes: Edit route page"); +$priv_list['page-system-staticroutes-editroute']['descr'] = gettext("Allow access to the 'System: Static Routes: Edit route' page."); +$priv_list['page-system-staticroutes-editroute']['match'] = array(); +$priv_list['page-system-staticroutes-editroute']['match'][] = "system_routes_edit.php*"; + +$priv_list['page-system-usermanager'] = array(); +$priv_list['page-system-usermanager']['name'] = gettext("WebCfg - System: User Manager page"); +$priv_list['page-system-usermanager']['descr'] = gettext("Allow access to the 'System: User Manager' page."); +$priv_list['page-system-usermanager']['match'] = array(); +$priv_list['page-system-usermanager']['match'][] = "system_usermanager.php*"; + +$priv_list['page-system-usermanager-addprivs'] = array(); +$priv_list['page-system-usermanager-addprivs']['name'] = gettext("WebCfg - System: User Manager: Add Privileges page"); +$priv_list['page-system-usermanager-addprivs']['descr'] = gettext("Allow access to the 'System: User Manager: Add Privileges' page."); +$priv_list['page-system-usermanager-addprivs']['match'] = array(); +$priv_list['page-system-usermanager-addprivs']['match'][] = "system_usermanager_addprivs.php*"; + +$priv_list['page-system-usermanager-passwordmg'] = array(); +$priv_list['page-system-usermanager-passwordmg']['name'] = gettext("WebCfg - System: User Password Manager page"); +$priv_list['page-system-usermanager-passwordmg']['descr'] = gettext("Allow access to the 'System: User Password Manager' page."); +$priv_list['page-system-usermanager-passwordmg']['match'] = array(); +$priv_list['page-system-usermanager-passwordmg']['match'][] = "system_usermanager_passwordmg.php*"; + +$priv_list['page-system-usermanager-settings'] = array(); +$priv_list['page-system-usermanager-settings']['name'] = gettext("WebCfg - System: User Manager: settings page"); +$priv_list['page-system-usermanager-settings']['descr'] = gettext("Allow access to the 'System: User Manager: settings' page."); +$priv_list['page-system-usermanager-settings']['match'] = array(); +$priv_list['page-system-usermanager-settings']['match'][] = "system_usermanager_settings.php*"; + +$priv_list['page-system-usermanager-settings-testldap'] = array(); +$priv_list['page-system-usermanager-settings-testldap']['name'] = gettext("WebCfg - System: User Manager: Settings: Test LDAP page"); +$priv_list['page-system-usermanager-settings-testldap']['descr'] = gettext("Allow access to the 'System: User Manager: Settings: Test LDAP' page."); +$priv_list['page-system-usermanager-settings-testldap']['match'] = array(); +$priv_list['page-system-usermanager-settings-testldap']['match'][] = "system_usermanager_settings_test.php*"; + +$priv_list['page-upload_progress'] = array(); +$priv_list['page-upload_progress']['name'] = gettext("WebCfg - System: Firmware: Manual Update page (progress bar)"); +$priv_list['page-upload_progress']['descr'] = gettext("Allow access to the 'System: Firmware: Manual Update: Progress bar' page."); +$priv_list['page-upload_progress']['match'] = array(); +$priv_list['page-upload_progress']['match'][] = "upload_progress*"; + +$priv_list['page-hidden-uploadconfiguration'] = array(); +$priv_list['page-hidden-uploadconfiguration']['name'] = gettext("WebCfg - Hidden: Upload Configuration page"); +$priv_list['page-hidden-uploadconfiguration']['descr'] = gettext("Allow access to the 'Hidden: Upload Configuration' page."); +$priv_list['page-hidden-uploadconfiguration']['match'] = array(); +$priv_list['page-hidden-uploadconfiguration']['match'][] = "uploadconfig.php*"; + +$priv_list['page-vpn-ipsec'] = array(); +$priv_list['page-vpn-ipsec']['name'] = gettext("WebCfg - VPN: IPsec page"); +$priv_list['page-vpn-ipsec']['descr'] = gettext("Allow access to the 'VPN: IPsec' page."); +$priv_list['page-vpn-ipsec']['match'] = array(); +$priv_list['page-vpn-ipsec']['match'][] = "vpn_ipsec.php*"; + +$priv_list['page-vpn-ipsec-listkeys'] = array(); +$priv_list['page-vpn-ipsec-listkeys']['name'] = gettext("WebCfg - VPN: IPsec: Pre-Shared Keys List"); +$priv_list['page-vpn-ipsec-listkeys']['descr'] = gettext("Allow access to the 'VPN: IPsec: Pre-Shared Keys List' page."); +$priv_list['page-vpn-ipsec-listkeys']['match'] = array(); +$priv_list['page-vpn-ipsec-listkeys']['match'][] = "vpn_ipsec_keys.php*"; + +$priv_list['page-vpn-ipsec-editkeys'] = array(); +$priv_list['page-vpn-ipsec-editkeys']['name'] = gettext("WebCfg - VPN: IPsec: Edit Pre-Shared Keys"); +$priv_list['page-vpn-ipsec-editkeys']['descr'] = gettext("Allow access to the 'VPN: IPsec: Edit Pre-Shared Keys' page."); +$priv_list['page-vpn-ipsec-editkeys']['match'] = array(); +$priv_list['page-vpn-ipsec-editkeys']['match'][] = "vpn_ipsec_keys_edit.php*"; + +$priv_list['page-vpn-ipsec-mobile'] = array(); +$priv_list['page-vpn-ipsec-mobile']['name'] = gettext("WebCfg - VPN: IPsec: Mobile page"); +$priv_list['page-vpn-ipsec-mobile']['descr'] = gettext("Allow access to the 'VPN: IPsec: Mobile' page."); +$priv_list['page-vpn-ipsec-mobile']['match'] = array(); +$priv_list['page-vpn-ipsec-mobile']['match'][] = "vpn_ipsec_mobile.php*"; + +$priv_list['page-vpn-ipsec-editphase1'] = array(); +$priv_list['page-vpn-ipsec-editphase1']['name'] = gettext("WebCfg - VPN: IPsec: Edit Phase 1 page"); +$priv_list['page-vpn-ipsec-editphase1']['descr'] = gettext("Allow access to the 'VPN: IPsec: Edit Phase 1' page."); +$priv_list['page-vpn-ipsec-editphase1']['match'] = array(); +$priv_list['page-vpn-ipsec-editphase1']['match'][] = "vpn_ipsec_phase1.php*"; + +$priv_list['page-vpn-ipsec-editphase2'] = array(); +$priv_list['page-vpn-ipsec-editphase2']['name'] = gettext("WebCfg - VPN: IPsec: Edit Phase 2 page"); +$priv_list['page-vpn-ipsec-editphase2']['descr'] = gettext("Allow access to the 'VPN: IPsec: Edit Phase 2' page."); +$priv_list['page-vpn-ipsec-editphase2']['match'] = array(); +$priv_list['page-vpn-ipsec-editphase2']['match'][] = "vpn_ipsec_phase2.php*"; + +$priv_list['page-vpn-vpnl2tp'] = array(); +$priv_list['page-vpn-vpnl2tp']['name'] = gettext("WebCfg - VPN: VPN L2TP page"); +$priv_list['page-vpn-vpnl2tp']['descr'] = gettext("Allow access to the 'VPN: VPN L2TP' page."); +$priv_list['page-vpn-vpnl2tp']['match'] = array(); +$priv_list['page-vpn-vpnl2tp']['match'][] = "vpn_l2tp.php*"; + +$priv_list['page-vpn-vpnl2tp-users'] = array(); +$priv_list['page-vpn-vpnl2tp-users']['name'] = gettext("WebCfg - VPN: VPN L2TP : Users page"); +$priv_list['page-vpn-vpnl2tp-users']['descr'] = gettext("Allow access to the 'VPN: VPN L2TP : Users' page."); +$priv_list['page-vpn-vpnl2tp-users']['match'] = array(); +$priv_list['page-vpn-vpnl2tp-users']['match'][] = "vpn_l2tp_users.php*"; + +$priv_list['page-vpn-vpnl2tp-users-edit'] = array(); +$priv_list['page-vpn-vpnl2tp-users-edit']['name'] = gettext("WebCfg - VPN: VPN L2TP : Users : Edit page"); +$priv_list['page-vpn-vpnl2tp-users-edit']['descr'] = gettext("Allow access to the 'VPN: VPN L2TP : Users : Edit' page."); +$priv_list['page-vpn-vpnl2tp-users-edit']['match'] = array(); +$priv_list['page-vpn-vpnl2tp-users-edit']['match'][] = "vpn_l2tp_users_edit.php*"; + +$priv_list['page-openvpn-client'] = array(); +$priv_list['page-openvpn-client']['name'] = gettext("WebCfg - OpenVPN: Client page"); +$priv_list['page-openvpn-client']['descr'] = gettext("Allow access to the 'OpenVPN: Client' page."); +$priv_list['page-openvpn-client']['match'] = array(); +$priv_list['page-openvpn-client']['match'][] = "vpn_openvpn_client.php*"; + +$priv_list['page-openvpn-csc'] = array(); +$priv_list['page-openvpn-csc']['name'] = gettext("WebCfg - OpenVPN: Client Specific Override page"); +$priv_list['page-openvpn-csc']['descr'] = gettext("Allow access to the 'OpenVPN: Client Specific Override' page."); +$priv_list['page-openvpn-csc']['match'] = array(); +$priv_list['page-openvpn-csc']['match'][] = "vpn_openvpn_csc.php*"; + +$priv_list['page-openvpn-server'] = array(); +$priv_list['page-openvpn-server']['name'] = gettext("WebCfg - OpenVPN: Server page"); +$priv_list['page-openvpn-server']['descr'] = gettext("Allow access to the 'OpenVPN: Server' page."); +$priv_list['page-openvpn-server']['match'] = array(); +$priv_list['page-openvpn-server']['match'][] = "vpn_openvpn_server.php*"; + +$priv_list['page-services-pppoeserver'] = array(); +$priv_list['page-services-pppoeserver']['name'] = gettext("WebCfg - Services: PPPoE Server page"); +$priv_list['page-services-pppoeserver']['descr'] = gettext("Allow access to the 'Services: PPPoE Server' page."); +$priv_list['page-services-pppoeserver']['match'] = array(); +$priv_list['page-services-pppoeserver']['match'][] = "vpn_pppoe.php*"; + +$priv_list['page-services-pppoeserver-edit'] = array(); +$priv_list['page-services-pppoeserver-edit']['name'] = gettext("WebCfg - Services: PPPoE Server: Edit page"); +$priv_list['page-services-pppoeserver-edit']['descr'] = gettext("Allow access to the 'Services: PPPoE Server: Edit' page."); +$priv_list['page-services-pppoeserver-edit']['match'] = array(); +$priv_list['page-services-pppoeserver-edit']['match'][] = "vpn_pppoe_edit.php*"; + +$priv_list['page-vpn-vpnpptp'] = array(); +$priv_list['page-vpn-vpnpptp']['name'] = gettext("WebCfg - VPN: VPN PPTP page"); +$priv_list['page-vpn-vpnpptp']['descr'] = gettext("Allow access to the 'VPN: VPN PPTP' page."); +$priv_list['page-vpn-vpnpptp']['match'] = array(); +$priv_list['page-vpn-vpnpptp']['match'][] = "vpn_pptp.php*"; + +$priv_list['page-vpn-vpnpptp-users'] = array(); +$priv_list['page-vpn-vpnpptp-users']['name'] = gettext("WebCfg - VPN: VPN PPTP: Users page"); +$priv_list['page-vpn-vpnpptp-users']['descr'] = gettext("Allow access to the 'VPN: VPN PPTP: Users' page."); +$priv_list['page-vpn-vpnpptp-users']['match'] = array(); +$priv_list['page-vpn-vpnpptp-users']['match'][] = "vpn_pptp_users.php*"; + +$priv_list['page-vpn-vpnpptp-user-edit'] = array(); +$priv_list['page-vpn-vpnpptp-user-edit']['name'] = gettext("WebCfg - VPN: VPN PPTP: User: Edit page"); +$priv_list['page-vpn-vpnpptp-user-edit']['descr'] = gettext("Allow access to the 'VPN: VPN PPTP: User: Edit' page."); +$priv_list['page-vpn-vpnpptp-user-edit']['match'] = array(); +$priv_list['page-vpn-vpnpptp-user-edit']['match'][] = "vpn_pptp_users_edit.php*"; + +$priv_list['page-pfsensewizardsubsystem'] = array(); +$priv_list['page-pfsensewizardsubsystem']['name'] = gettext("WebCfg - pfSense wizard subsystem page"); +$priv_list['page-pfsensewizardsubsystem']['descr'] = gettext("Allow access to the 'pfSense wizard subsystem' page."); +$priv_list['page-pfsensewizardsubsystem']['match'] = array(); +$priv_list['page-pfsensewizardsubsystem']['match'][] = "wizard.php*"; + +$priv_list['page-xmlrpclibrary'] = array(); +$priv_list['page-xmlrpclibrary']['name'] = gettext("WebCfg - XMLRPC Library page"); +$priv_list['page-xmlrpclibrary']['descr'] = gettext("Allow access to the 'XMLRPC Library' page."); +$priv_list['page-xmlrpclibrary']['match'] = array(); +$priv_list['page-xmlrpclibrary']['match'][] = "xmlrpc.php*"; + +$priv_list['page-firewall-easyrule'] = array(); +$priv_list['page-firewall-easyrule']['name'] = gettext("WebCfg - Firewall: Easy Rule add/status page"); +$priv_list['page-firewall-easyrule']['descr'] = gettext("Allow access to the 'Firewall: Easy Rule' add/status page."); +$priv_list['page-firewall-easyrule']['match'] = array(); +$priv_list['page-firewall-easyrule']['match'][] = "easyrule.php*"; + +$priv_rmvd = array(); + +?> diff --git a/src/etc/inc/priv.inc b/src/etc/inc/priv.inc new file mode 100644 index 0000000..851643b --- /dev/null +++ b/src/etc/inc/priv.inc @@ -0,0 +1,337 @@ +<?php +/* $Id$ */ +/* + priv.inc + Copyright (C) 2008 Shrew Soft Inc + All rights reserved. + + Copyright (C) 2007, 2008 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Copyright (C) 2005-2006 Bill Marquette <bill.marquette@gmail.com> + All rights reserved. + + Copyright (C) 2006 Paul Taylor <paultaylor@winn-dixie.com>. + All rights reserved. + + Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* + pfSense_MODULE: auth +*/ + +require_once("priv.defs.inc"); + +/* Load and process custom privs. */ +function get_priv_files($directory) { + $dir_array = array(); + if (!is_dir($directory)) { + return; + } + if ($dh = opendir($directory)) { + while (($file = readdir($dh)) !== false) { + $canadd = 0; + if ($file == ".") { + $canadd = 1; + } + if ($file == "..") { + $canadd = 1; + } + if ($canadd == 0) { + array_push($dir_array, $file); + } + } + closedir($dh); + } + if (!is_array($dir_array)) { + return; + } + return $dir_array; +} + +// Load and sort privs +$dir_array = get_priv_files("/etc/inc/priv"); +foreach ($dir_array as $file) { + if (!is_dir("/etc/inc/priv/{$file}") && stristr($file, ".inc")) { + include("/etc/inc/priv/{$file}"); + } +} +if (is_dir("/usr/local/pkg/priv")) { + $dir_array = get_priv_files("/usr/local/pkg/priv"); + foreach ($dir_array as $file) { + if (!is_dir("/usr/local/pkg/priv/{$file}") && stristr($file, ".inc")) { + include("/usr/local/pkg/priv/{$file}"); + } + } +} + +if (is_array($priv_list)) { + sort_privs($priv_list); +} + +function cmp_privkeys($a, $b) { + /* user privs at the top */ + $auser = strncmp("user-", $a, 5); + $buser = strncmp("user-", $b, 5); + if ($auser != $buser) { + return $auser - $buser; + } + + /* name compare others */ + return strcasecmp($a, $b); +} + +function sort_privs(& $privs) { + uksort($privs, "cmp_privkeys"); +} + +function cmp_page_matches($page, & $matches, $fullwc = true) { + +// $dbg_matches = implode(",", $matches); +// log_error("debug: checking page {$page} match with {$dbg_matches}"); + + if (!is_array($matches)) { + return false; + } + + /* skip any leading fwdslash */ + $test = strpos($page, "/"); + if ($test !== false && $test == 0) { + $page = substr($page, 1); + } + + /* look for a match */ + foreach ($matches as $match) { + + /* possibly ignore full wildcard match */ + if (!$fullwc && !strcmp($match , "*")) { + continue; + } + + /* compare exact or wildcard match */ + $match = str_replace(array(".", "*", "?"), array("\.", ".*", "\?"), $match); + $result = preg_match("@^/{$match}$@", "/{$page}"); + + if ($result) { + return true; + } + } + + return false; +} + +function map_page_privname($page) { + global $priv_list; + + foreach ($priv_list as $pname => $pdata) { + if (strncmp($pname, "page-", 5)) { + continue; + } + $fullwc = false; + if (!strcasecmp($page, "any")||!strcmp($page, "*")) { + $fullwc = true; + } + if (cmp_page_matches($page, $pdata['match'], $fullwc)) { + return $pname; + } + } + + return false; +} + +function get_user_privdesc(& $user) { + global $priv_list; + + $privs = array(); + + $user_privs = $user['priv']; + if (!is_array($user_privs)) { + $user_privs = array(); + } + + $names = local_user_get_groups($user, true); + + foreach ($names as $name) { + $group = getGroupEntry($name); + $group_privs = $group['priv']; + if (!is_array($group_privs)) { + continue; + } + foreach ($group_privs as $pname) { + if (in_array($pname, $user_privs)) { + continue; + } + if (!$priv_list[$pname]) { + continue; + } + $priv = $priv_list[$pname]; + $priv['group'] = $group['name']; + $privs[] = $priv; + } + } + + foreach ($user_privs as $pname) { + if ($priv_list[$pname]) { + $privs[] = $priv_list[$pname]; + } + } + + return $privs; +} + +function isAllowed($username, $page) { + global $_SESSION; + + if (!isset($username)) { + return false; + } + + /* admin/root access check */ + $user = getUserEntry($username); + if (isset($user)) { + if (isset($user['uid'])) { + if ($user['uid'] == 0) { + return true; + } + } + } + + /* user privilege access check */ + if (cmp_page_matches($page, $_SESSION['page-match'])) { + return true; + } + + return false; +} + + +function isAllowedPage($page) { + global $_SESSION; + + + $username = $_SESSION['Username']; + + if (!isset($username)) { + return false; + } + + /* admin/root access check */ + $user = getUserEntry($username); + if (isset($user)) { + if (isset($user['uid'])) { + if ($user['uid'] == 0) { + return true; + } + } + } + + /* user privilege access check */ + return cmp_page_matches($page, $_SESSION['page-match']); +} + +function getPrivPages(& $entry, & $allowed_pages) { + global $priv_list; + + if (!is_array($entry['priv'])) { + return; + } + + foreach ($entry['priv'] as $pname) { + if (strncmp($pname, "page-", 5)) { + continue; + } + $priv = &$priv_list[$pname]; + if (!is_array($priv)) { + continue; + } + $matches = &$priv['match']; + if (!is_array($matches)) { + continue; + } + foreach ($matches as $match) { + $allowed_pages[] = $match; + } + } +} + +function getAllowedPages($username, &$attributes = array()) { + global $config, $_SESSION; + + if (!function_exists("ldap_connect")) { + return; + } + + $allowed_pages = array(); + $allowed_groups = array(); + + $authcfg = auth_get_authserver($config['system']['webgui']['authmode']); + // obtain ldap groups if we are in ldap mode + if ($authcfg['type'] == "ldap") { + $allowed_groups = @ldap_get_groups($username, $authcfg); + } elseif ($authcfg['type'] == "radius") { + $allowed_groups = @radius_get_groups($attributes); + } + if (!$allowed_groups) { + // search for a local user by name + $local_user = getUserEntry($username); + + // obtain local user pages and groups if we have a local user + if ($local_user) { + getPrivPages($local_user, $allowed_pages); + $allowed_groups = local_user_get_groups($local_user); + } + } + + // build a list of allowed pages + if (is_array($config['system']['group']) && is_array($allowed_groups)) { + foreach ($config['system']['group'] as $group) { + if (in_array($group['name'], $allowed_groups)) { + getPrivPages($group, $allowed_pages); + } + } + } + +// $dbg_pages = implode(",", $allowed_pages); +// $dbg_groups = implode(",", $allowed_groups); +// log_error("debug: user {$username} groups = {$dbg_groups}"); +// log_error("debug: user {$username} pages = {$dbg_pages}"); + + $_SESSION['page-match'] = $allowed_pages; + + return $allowed_pages; +} + +function sort_user_privs($privs) { + // Privileges to place first, to redirect properly. + $priority_privs = array("page-dashboard-all", "page-system-login/logout"); + + $fprivs = array_intersect($privs, $priority_privs); + $sprivs = array_diff($privs, $priority_privs); + + return array_merge($fprivs, $sprivs); +} +?> diff --git a/src/etc/inc/priv/user.priv.inc b/src/etc/inc/priv/user.priv.inc new file mode 100644 index 0000000..6414008 --- /dev/null +++ b/src/etc/inc/priv/user.priv.inc @@ -0,0 +1,74 @@ +<?php + +global $priv_list; + +$priv_list['user-services-captiveportal-login'] = array(); +$priv_list['user-services-captiveportal-login']['name'] = gettext("User - Services - Captive portal login"); +$priv_list['user-services-captiveportal-login']['descr'] = gettext("Indicates whether the user is able to login on the captive portal."); + +$priv_list['page-help-all'] = array(); +$priv_list['page-help-all']['name'] = "WebCfg - Help pages"; +$priv_list['page-help-all']['descr'] = "Show all items on help menu"; +$priv_list['page-help-all']['match'] = array(); +$priv_list['page-help-all']['match'][] = "*help.php"; + +$priv_list['page-dashboard-all'] = array(); +$priv_list['page-dashboard-all']['name'] = "WebCfg - Dashboard (all)"; +$priv_list['page-dashboard-all']['descr'] = "Allow access to all pages required for the dashboard."; +$priv_list['page-dashboard-all']['match'] = array(); +$priv_list['page-dashboard-all']['match'][] = "index.php*"; +$priv_list['page-dashboard-all']['match'][] = "*.widget.php*"; +$priv_list['page-dashboard-all']['match'][] = "graph.php*"; +$priv_list['page-dashboard-all']['match'][] = "graph_cpu.php*"; +$priv_list['page-dashboard-all']['match'][] = "getstats.php*"; +$priv_list['page-dashboard-all']['match'][] = "ifstats.php*"; +$priv_list['page-dashboard-all']['match'][] = "diag_logs_filter_dynamic.php*"; + +$priv_list['page-dashboard-widgets'] = array(); +$priv_list['page-dashboard-widgets']['name'] = "WebCfg - Dashboard widgets (direct access)."; +$priv_list['page-dashboard-widgets']['descr'] = "Allow direct access to all Dashboard widget pages, required for some widgets using AJAX."; +$priv_list['page-dashboard-widgets']['match'] = array(); +$priv_list['page-dashboard-widgets']['match'][] = "*.widget.php*"; + +$priv_list['user-config-readonly'] = array(); +$priv_list['user-config-readonly']['name'] = "User - Config - Deny Config Write"; +$priv_list['user-config-readonly']['descr'] = "If present, ignores requests from this user to write config.xml."; + +$priv_list['user-shell-access'] = array(); +$priv_list['user-shell-access']['name'] = "User - System - Shell account access"; +$priv_list['user-shell-access']['descr'] = "Indicates whether the user is able to login for ". + "example via SSH."; + +$priv_list['user-copy-files'] = array(); +$priv_list['user-copy-files']['name'] = "User - System - Copy files"; +$priv_list['user-copy-files']['descr'] = "Indicates whether the user is allowed to copy files ". + "onto the {$g['product_name']} appliance via SCP/SFTP. ". + "If you are going to use this privilege, you must install ". + "scponly on the appliance (Hint: pkg_add -r scponly)."; + +$priv_list['user-ssh-tunnel'] = array(); +$priv_list['user-ssh-tunnel']['name'] = "User - System - SSH tunneling"; +$priv_list['user-ssh-tunnel']['descr'] = "Indicates whether the user is able to login for ". + "tunneling via SSH when they have no shell access. ". + "Note: User - System - Copy files conflicts with ". + "this privilege."; + +$priv_list['user-ipsec-xauth-dialin'] = array(); +$priv_list['user-ipsec-xauth-dialin']['name'] = "User - VPN - IPsec xauth Dialin"; +$priv_list['user-ipsec-xauth-dialin']['descr'] = "Indicates whether the user is allowed to dial in via IPsec xauth ". + "(Note: Does not allow shell access, but may allow ". + "the user to create SSH tunnels)"; + +$priv_list['user-l2tp-dialin'] = array(); +$priv_list['user-l2tp-dialin']['name'] = "User - VPN - L2TP Dialin"; +$priv_list['user-l2tp-dialin']['descr'] = "Indicates whether the user is allowed to dial in via L2TP"; + +$priv_list['user-pptp-dialin'] = array(); +$priv_list['user-pptp-dialin']['name'] = "User - VPN - PPTP Dialin"; +$priv_list['user-pptp-dialin']['descr'] = "Indicates whether the user is allowed to dial in via PPTP"; + +$priv_list['user-pppoe-dialin'] = array(); +$priv_list['user-pppoe-dialin']['name'] = "User - VPN - PPPOE Dialin"; +$priv_list['user-pppoe-dialin']['descr'] = "Indicates whether the user is allowed to dial in via PPPOE"; + +?> diff --git a/src/etc/inc/r53.class b/src/etc/inc/r53.class new file mode 100644 index 0000000..89faa26 --- /dev/null +++ b/src/etc/inc/r53.class @@ -0,0 +1,754 @@ +<?php +/** +* +* Copyright (c) 2011, Dan Myers. +* Parts copyright (c) 2008, Donovan Schonknecht. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* - Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* - Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* This is a modified BSD license (the third clause has been removed). +* The BSD license may be found here: +* http://www.opensource.org/licenses/bsd-license.php +* +* Amazon Route 53 is a trademark of Amazon.com, Inc. or its affiliates. +* +* Route53 is based on Donovan Schonknecht's Amazon S3 PHP class, found here: +* http://undesigned.org.za/2007/10/22/amazon-s3-php-class +* +*/ + +/** +* Amazon Route53 PHP class +* +* @link http://sourceforge.net/projects/php-r53/ +* version 0.9.0 +* +*/ +class Route53 +{ + const API_VERSION = '2010-10-01'; + + protected $__accessKey; // AWS Access key + protected $__secretKey; // AWS Secret key + protected $__host; + + public function getAccessKey() { return $this->__accessKey; } + public function getSecretKey() { return $this->__secretKey; } + public function getHost() { return $this->__host; } + + protected $__verifyHost = 1; + protected $__verifyPeer = 1; + + // verifyHost and verifyPeer determine whether curl verifies ssl certificates. + // It may be necessary to disable these checks on certain systems. + // These only have an effect if SSL is enabled. + public function verifyHost() { return $this->__verifyHost; } + public function enableVerifyHost($enable = true) { $this->__verifyHost = $enable; } + + public function verifyPeer() { return $this->__verifyPeer; } + public function enableVerifyPeer($enable = true) { $this->__verifyPeer = $enable; } + + /** + * Constructor + * + * @param string $accessKey Access key + * @param string $secretKey Secret key + * @return void + */ + public function __construct($accessKey = null, $secretKey = null, $host = 'route53.amazonaws.com') { + if ($accessKey !== null && $secretKey !== null) { + $this->setAuth($accessKey, $secretKey); + } + $this->__host = $host; + } + + /** + * Set AWS access key and secret key + * + * @param string $accessKey Access key + * @param string $secretKey Secret key + * @return void + */ + public function setAuth($accessKey, $secretKey) { + $this->__accessKey = $accessKey; + $this->__secretKey = $secretKey; + } + + /** + * Lists the hosted zones on the account + * + * @param string marker A pagination marker returned by a previous truncated call + * @param int maxItems The maximum number of items per page. The service uses min($maxItems, 100). + * @return A list of hosted zones + */ + public function listHostedZones($marker = null, $maxItems = 100) { + $rest = new Route53Request($this, 'hostedzone', 'GET'); + + if($marker !== null) { + $rest->setParameter('marker', $marker); + } + if($maxItems !== 100) { + $rest->setParameter('maxitems', $maxItems); + } + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('listHostedZones', $rest->error); + return false; + } + + $response = array(); + if (!isset($rest->body)) + { + return $response; + } + + $zones = array(); + foreach($rest->body->HostedZones->HostedZone as $z) + { + $zones[] = $this->parseHostedZone($z); + } + $response['HostedZone'] = $zones; + + if(isset($rest->body->MaxItems)) { + $response['MaxItems'] = (string)$rest->body->MaxItems; + } + + if(isset($rest->body->IsTruncated)) { + $response['IsTruncated'] = (string)$rest->body->IsTruncated; + if($response['IsTruncated'] == 'true') { + $response['NextMarker'] = (string)$rest->body->NextMarker; + } + } + + return $response; + } + + /** + * Retrieves information on a specified hosted zone + * + * @param string zoneId The id of the hosted zone, as returned by CreateHostedZoneResponse or ListHostedZoneResponse + * In other words, if ListHostedZoneResponse shows the zone's Id as '/hostedzone/Z1PA6795UKMFR9', + * then that full value should be passed here, including the '/hostedzone/' prefix. + * @return A data structure containing information about the specified zone + */ + public function getHostedZone($zoneId) { + // we'll strip off the leading forward slash, so we can use it as the action directly. + $zoneId = trim($zoneId, '/'); + + $rest = new Route53Request($this, $zoneId, 'GET'); + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('getHostedZone', $rest->error); + return false; + } + + $response = array(); + if (!isset($rest->body)) + { + return $response; + } + + $response['HostedZone'] = $this->parseHostedZone($rest->body->HostedZone); + $response['NameServers'] = $this->parseDelegationSet($rest->body->DelegationSet); + + return $response; + } + + /** + * Creates a new hosted zone + * + * @param string name The name of the hosted zone (e.g. "example.com.") + * @param string reference A user-specified unique reference for this request + * @param string comment An optional user-specified comment to attach to the zone + * @return A data structure containing information about the newly created zone + */ + public function createHostedZone($name, $reference, $comment = '') { + // hosted zone names must end with a period, but people will forget this a lot... + if(strrpos($name, '.') != (strlen($name) - 1)) { + $name .= '.'; + } + + $data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + $data .= '<CreateHostedZoneRequest xmlns="https://route53.amazonaws.com/doc/'.Route53::API_VERSION."/\">\n"; + $data .= '<Name>'.$name."</Name>\n"; + $data .= '<CallerReference>'.$reference."</CallerReference>\n"; + if(strlen($comment) > 0) { + $data .= "<HostedZoneConfig>\n"; + $data .= '<Comment>'.$comment."</Comment>\n"; + $data .= "</HostedZoneConfig>\n"; + } + $data .= "</CreateHostedZoneRequest>\n"; + + $rest = new Route53Request($this, 'hostedzone', 'POST', $data); + + $rest = $rest->getResponse(); + + if($rest->error === false && !in_array($rest->code, array(200, 201, 202, 204)) ) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('createHostedZone', $rest->error); + return false; + } + + $response = array(); + if (!isset($rest->body)) + { + return $response; + } + + $response['HostedZone'] = $this->parseHostedZone($rest->body->HostedZone); + $response['ChangeInfo'] = $this->parseChangeInfo($rest->body->ChangeInfo); + $response['NameServers'] = $this->parseDelegationSet($rest->body->DelegationSet); + + return $response; + } + + /** + * Retrieves information on a specified hosted zone + * + * @param string zoneId The id of the hosted zone, as returned by CreateHostedZoneResponse or ListHostedZoneResponse + * In other words, if ListHostedZoneResponse shows the zone's Id as '/hostedzone/Z1PA6795UKMFR9', + * then that full value should be passed here, including the '/hostedzone/' prefix. + * @return The change request data corresponding to this delete + */ + public function deleteHostedZone($zoneId) { + // we'll strip off the leading forward slash, so we can use it as the action directly. + $zoneId = trim($zoneId, '/'); + + $rest = new Route53Request($this, $zoneId, 'DELETE'); + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('deleteHostedZone', $rest->error); + return false; + } + + if (!isset($rest->body)) + { + return array(); + } + + return $this->parseChangeInfo($rest->body->ChangeInfo); + } + + /** + * Retrieves a list of resource record sets for a given zone + * + * @param string zoneId The id of the hosted zone, as returned by CreateHostedZoneResponse or ListHostedZoneResponse + * In other words, if ListHostedZoneResponse shows the zone's Id as '/hostedzone/Z1PA6795UKMFR9', + * then that full value should be passed here, including the '/hostedzone/' prefix. + * @param string type The type of resource record set to begin listing from. If this is specified, $name must also be specified. + * Must be one of: A, AAAA, CNAME, MX, NS, PTR, SOA, SPF, SRV, TXT + * @param string name The name at which to begin listing resource records (in the lexographic order of records). + * @param int maxItems The maximum number of results to return. The service uses min($maxItems, 100). + * @return The list of matching resource record sets + */ + public function listResourceRecordSets($zoneId, $type = '', $name = '', $maxItems = 100) { + // we'll strip off the leading forward slash, so we can use it as the action directly. + $zoneId = trim($zoneId, '/'); + + $rest = new Route53Request($this, $zoneId.'/rrset', 'GET'); + + if(strlen($type) > 0) { + $rest->setParameter('type', $type); + } + if(strlen($name) > 0) { + $rest->setParameter('name', $name); + } + if($maxItems != 100) { + $rest->setParameter('maxitems', $maxItems); + } + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('listResourceRecordSets', $rest->error); + return false; + } + + $response = array(); + if (!isset($rest->body)) + { + return $response; + } + + $recordSets = array(); + foreach($rest->body->ResourceRecordSets->ResourceRecordSet as $set) { + $recordSets[] = $this->parseResourceRecordSet($set); + } + + $response['ResourceRecordSets'] = $recordSets; + + if(isset($rest->body->MaxItems)) { + $response['MaxItems'] = (string)$rest->body->MaxItems; + } + + if(isset($rest->body->IsTruncated)) { + $response['IsTruncated'] = (string)$rest->body->IsTruncated; + if($response['IsTruncated'] == 'true') { + $response['NextRecordName'] = (string)$rest->body->NextRecordName; + $response['NextRecordType'] = (string)$rest->body->NextRecordType; + } + } + + return $response; + } + + /** + * Makes the specified resource record set changes (create or delete). + * + * @param string zoneId The id of the hosted zone, as returned by CreateHostedZoneResponse or ListHostedZoneResponse + * In other words, if ListHostedZoneResponse shows the zone's Id as '/hostedzone/Z1PA6795UKMFR9', + * then that full value should be passed here, including the '/hostedzone/' prefix. + * @param array changes An array of change objects, as they are returned by the prepareChange utility method. + * You may also pass a single change object. + * @param string comment An optional comment to attach to the change request + * @return The status of the change request + */ + public function changeResourceRecordSets($zoneId, $changes, $comment = '') { + // we'll strip off the leading forward slash, so we can use it as the action directly. + $zoneId = trim($zoneId, '/'); + + $data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + $data .= '<ChangeResourceRecordSetsRequest xmlns="https://route53.amazonaws.com/doc/'.Route53::API_VERSION."/\">\n"; + $data .= "<ChangeBatch>\n"; + + if(strlen($comment) > 0) { + $data .= '<Comment>'.$comment."</Comment>\n"; + } + + if(!is_array($changes)) { + $changes = array($changes); + } + + $data .= "<Changes>\n"; + foreach($changes as $change) { + $data .= $change; + } + $data .= "</Changes>\n"; + + $data .= "</ChangeBatch>\n"; + $data .= "</ChangeResourceRecordSetsRequest>\n"; + + $rest = new Route53Request($this, $zoneId.'/rrset', 'POST', $data); + + $rest = $rest->getResponse(); + if($rest->error === false && !in_array($rest->code, array(200, 201, 202, 204))) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('changeResourceRecordSets', $rest->error); + return false; + } + + if (!isset($rest->body)) + { + return array(); + } + + return $this->parseChangeInfo($rest->body->ChangeInfo); + } + + /** + * Retrieves information on a specified change request + * + * @param string changeId The id of the change, as returned by CreateHostedZoneResponse or ChangeResourceRecordSets + * In other words, if CreateHostedZoneResponse showed the change's Id as '/change/C2682N5HXP0BZ4', + * then that full value should be passed here, including the '/change/' prefix. + * @return The status of the change request + */ + public function getChange($changeId) { + // we'll strip off the leading forward slash, so we can use it as the action directly. + $zoneId = trim($changeId, '/'); + + $rest = new Route53Request($this, $changeId, 'GET'); + + $rest = $rest->getResponse(); + if($rest->error === false && $rest->code !== 200) { + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + } + if($rest->error !== false) { + $this->__triggerError('getChange', $rest->error); + return false; + } + + if (!isset($rest->body)) + { + return array(); + } + + return $this->parseChangeInfo($rest->body->ChangeInfo); + } + + + + /** + * Utility function to parse a HostedZone tag structure + */ + private function parseHostedZone($tag) { + $zone = array(); + $zone['Id'] = (string)$tag->Id; + $zone['Name'] = (string)$tag->Name; + $zone['CallerReference'] = (string)$tag->CallerReference; + + // these might always be set, but check just in case, since + // their values are option on CreateHostedZone requests + if(isset($tag->Config) && isset($tag->Config->Comment)) { + $zone['Config'] = array('Comment' => (string)$tag->Config->Comment); + } + + return $zone; + } + + /** + * Utility function to parse a ChangeInfo tag structure + */ + private function parseChangeInfo($tag) { + $info = array(); + $info['Id'] = (string)$tag->Id; + $info['Status'] = (string)$tag->Status; + $info['SubmittedAt'] = (string)$tag->SubmittedAt; + return $info; + } + + /** + * Utility function to parse a DelegationSet tag structure + */ + private function parseDelegationSet($tag) { + $servers = array(); + foreach($tag->NameServers->NameServer as $ns) { + $servers[] = (string)$ns; + } + return $servers; + } + + /** + * Utility function to parse a ResourceRecordSet tag structure + */ + private function parseResourceRecordSet($tag) { + $rrs = array(); + $rrs['Name'] = (string)$tag->Name; + $rrs['Type'] = (string)$tag->Type; + $rrs['TTL'] = (string)$tag->TTL; + $rrs['ResourceRecords'] = array(); + foreach($tag->ResourceRecords->ResourceRecord as $rr) { + $rrs['ResourceRecords'][] = (string)$rr->Value; + } + return $rrs; + } + + /** + * Utility function to prepare a Change object for ChangeResourceRecordSets requests. + * All fields are required. + * + * @param string action The action to perform. One of: CREATE, DELETE + * @param string name The name to perform the action on. + * If it does not end with '.', then AWS treats the name as relative to the zone root. + * @param string type The type of record being modified. + * Must be one of: A, AAAA, CNAME, MX, NS, PTR, SOA, SPF, SRV, TXT + * @param int ttl The time-to-live value for this record, in seconds. + * @param array records An array of resource records to attach to this change. + * Each member of this array can either be a string, or an array of strings. + * Passing an array of strings will attach multiple values to a single resource record. + * If a single string is passed as $records instead of an array, + * it will be treated as a single-member array. + * @return object An opaque object containing the change request. + * Do not write code that depends on the contents of this object, as it may change at any time. + */ + public function prepareChange($action, $name, $type, $ttl, $records) { + $change = "<Change>\n"; + $change .= '<Action>'.$action."</Action>\n"; + $change .= "<ResourceRecordSet>\n"; + $change .= '<Name>'.$name."</Name>\n"; + $change .= '<Type>'.$type."</Type>\n"; + $change .= '<TTL>'.$ttl."</TTL>\n"; + $change .= "<ResourceRecords>\n"; + + if(!is_array($records)) { + $records = array($records); + } + + foreach($records as $record) { + $change .= "<ResourceRecord>\n"; + if(is_array($record)) { + foreach($record as $value) { + $change .= '<Value>'.$value."</Value>\n"; + } + } + else { + $change .= '<Value>'.$record."</Value>\n"; + } + $change .= "</ResourceRecord>\n"; + } + + $change .= "</ResourceRecords>\n"; + $change .= "</ResourceRecordSet>\n"; + $change .= "</Change>\n"; + + return $change; + } + + /** + * Trigger an error message + * + * @internal Used by member functions to output errors + * @param array $error Array containing error information + * @return string + */ + public function __triggerError($functionname, $error) + { + if($error == false) { + trigger_error(sprintf("Route53::%s(): Encountered an error, but no description given", $functionname), E_USER_WARNING); + } + else if(isset($error['curl']) && $error['curl']) + { + trigger_error(sprintf("Route53::%s(): %s %s", $functionname, $error['code'], $error['message']), E_USER_WARNING); + } + else if(isset($error['Error'])) + { + $e = $error['Error']; + $message = sprintf("Route53::%s(): %s - %s: %s\nRequest Id: %s\n", $functionname, $e['Type'], $e['Code'], $e['Message'], $error['RequestId']); + trigger_error($message, E_USER_WARNING); + } + } + + /** + * Callback handler for 503 retries. + * + * @internal Used by SimpleDBRequest to call the user-specified callback, if set + * @param $attempt The number of failed attempts so far + * @return The retry delay in microseconds, or 0 to stop retrying. + */ + public function __executeServiceTemporarilyUnavailableRetryDelay($attempt) + { + if(is_callable($this->__serviceUnavailableRetryDelayCallback)) { + $callback = $this->__serviceUnavailableRetryDelayCallback; + return $callback($attempt); + } + return 0; + } +} + +final class Route53Request +{ + private $r53, $action, $verb, $data, $parameters = array(); + public $response; + + /** + * Constructor + * + * @param string $r53 The Route53 object making this request + * @param string $action SimpleDB action + * @param string $verb HTTP verb + * @param string $data For POST requests, the data being posted (optional) + * @return mixed + */ + function __construct($r53, $action, $verb, $data = '') { + $this->r53 = $r53; + $this->action = $action; + $this->verb = $verb; + $this->data = $data; + $this->response = new STDClass; + $this->response->error = false; + } + + /** + * Set request parameter + * + * @param string $key Key + * @param string $value Value + * @param boolean $replace Whether to replace the key if it already exists (default true) + * @return void + */ + public function setParameter($key, $value, $replace = true) { + if(!$replace && isset($this->parameters[$key])) + { + $temp = (array)($this->parameters[$key]); + $temp[] = $value; + $this->parameters[$key] = $temp; + } + else + { + $this->parameters[$key] = $value; + } + } + + /** + * Get the response + * + * @return object | false + */ + public function getResponse() { + + $params = array(); + foreach ($this->parameters as $var => $value) + { + if(is_array($value)) + { + foreach($value as $v) + { + $params[] = $var.'='.$this->__customUrlEncode($v); + } + } + else + { + $params[] = $var.'='.$this->__customUrlEncode($value); + } + } + + sort($params, SORT_STRING); + + $query = implode('&', $params); + + // must be in format 'Sun, 06 Nov 1994 08:49:37 GMT' + $date = gmdate('D, d M Y H:i:s e'); + + $headers = array(); + $headers[] = 'Date: '.$date; + $headers[] = 'Host: '.$this->r53->getHost(); + + $auth = 'AWS3-HTTPS AWSAccessKeyId='.$this->r53->getAccessKey(); + $auth .= ',Algorithm=HmacSHA256,Signature='.$this->__getSignature($date); + $headers[] = 'X-Amzn-Authorization: '.$auth; + + $url = 'https://'.$this->r53->getHost().'/'.Route53::API_VERSION.'/'.$this->action.'?'.$query; + + // Basic setup + $curl = curl_init(); + curl_setopt($curl, CURLOPT_USERAGENT, 'Route53/php'); + + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, ($this->r53->verifyHost() ? 1 : 0)); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, ($this->r53->verifyPeer() ? 1 : 0)); + + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); + curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback')); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + + // Request types + switch ($this->verb) { + case 'GET': break; + case 'POST': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); + if(strlen($this->data) > 0) { + curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data); + $headers[] = 'Content-Type: text/plain'; + $headers[] = 'Content-Length: '.strlen($this->data); + } + break; + case 'DELETE': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); + break; + default: break; + } + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_HEADER, false); + + // Execute, grab errors + if (curl_exec($curl)) { + $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + } else { + $this->response->error = array( + 'curl' => true, + 'code' => curl_errno($curl), + 'message' => curl_error($curl), + 'resource' => $this->resource + ); + } + + @curl_close($curl); + + // Parse body into XML + if ($this->response->error === false && isset($this->response->body)) { + $this->response->body = simplexml_load_string($this->response->body); + + // Grab Route53 errors + if (!in_array($this->response->code, array(200, 201, 202, 204)) + && isset($this->response->body->Error)) { + $error = $this->response->body->Error; + $output = array(); + $output['curl'] = false; + $output['Error'] = array(); + $output['Error']['Type'] = (string)$error->Type; + $output['Error']['Code'] = (string)$error->Code; + $output['Error']['Message'] = (string)$error->Message; + $output['RequestId'] = (string)$this->response->body->RequestId; + + $this->response->error = $output; + unset($this->response->body); + } + } + + return $this->response; + } + + /** + * CURL write callback + * + * @param resource &$curl CURL resource + * @param string &$data Data + * @return integer + */ + private function __responseWriteCallback(&$curl, &$data) { + $this->response->body .= $data; + return strlen($data); + } + + /** + * Contributed by afx114 + * URL encode the parameters as per http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?Query_QueryAuth.html + * PHP's rawurlencode() follows RFC 1738, not RFC 3986 as required by Amazon. The only difference is the tilde (~), so convert it back after rawurlencode + * See: http://www.morganney.com/blog/API/AWS-Product-Advertising-API-Requires-a-Signed-Request.php + * + * @param string $var String to encode + * @return string + */ + private function __customUrlEncode($var) { + return str_replace('%7E', '~', rawurlencode($var)); + } + + /** + * Generate the auth string using Hmac-SHA256 + * + * @internal Used by SimpleDBRequest::getResponse() + * @param string $string String to sign + * @return string + */ + private function __getSignature($string) { + return base64_encode(hash_hmac('sha256', $string, $this->r53->getSecretKey(), true)); + } +} diff --git a/src/etc/inc/radius.inc b/src/etc/inc/radius.inc new file mode 100644 index 0000000..ac610bd --- /dev/null +++ b/src/etc/inc/radius.inc @@ -0,0 +1,1208 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4: */ +/* + radius.inc + + Copyright (c) 2003, Michael Bretterklieber <michael@bretterklieber.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This code cannot simply be copied and put under the GNU Public License or + any other GPL-like (LGPL, GPL2) License. + + This version of RADIUS.php has been modified by + Jonathan De Graeve <m0n0wall@esstec.be> to integrate with M0n0wall <http://www.m0n0.ch/wall> + + Changes made include: + * StandardAttributes for M0n0wall use + * Removed internal Session-Id creation + * Adding of ReplyMessage to getAttributes() + * Adding of listAttributes() + * Adding of VENDOR Bay Networks (Nortel) + * Adding of VENDOR Nomadix + * Adding of VENDOR WISPr (Wi-Fi Alliance) + * Adding of VENDOR ChilliSpot (bandwidth-attributes only) + +*/ + +/* + pfSense_MODULE: auth +*/ + +require_once("PEAR.inc"); +require_once("radius_authentication.inc"); +require_once("radius_accounting.inc"); + +/** +* Client implementation of RADIUS. This are wrapper classes for +* the RADIUS PECL +* Provides RADIUS Authentication (RFC2865) and RADIUS Accounting (RFC2866). +* +* @package Auth_RADIUS +* @author Michael Bretterklieber <michael@bretterklieber.com> +* @access public +* @version $Revision: 1.5 $ +*/ + +PEAR::loadExtension('radius'); + +/** + * class Auth_RADIUS + * + * Abstract base class for RADIUS + * + * @package Auth_RADIUS + */ +class Auth_RADIUS extends PEAR { + + /** + * List of RADIUS servers. + * @var array + * @see addServer(), putServer() + */ + var $_servers = array(); + + /** + * Path to the configuration-file. + * @var string + * @see setConfigFile() + */ + var $_configfile = null; + + /** + * Resource. + * @var resource + * @see open(), close() + */ + var $res = null; + + /** + * Username for authentication and accounting requests. + * @var string + */ + var $username = null; + + /** + * Password for plaintext-authentication (PAP). + * @var string + */ + var $password = null; + + /** + * List of known attributes. + * @var array + * @see dumpAttributes(), getAttributes() + */ + var $attributes = array(); + + /** + * List of raw attributes. + * @var array + * @see dumpAttributes(), getAttributes() + */ + var $rawAttributes = array(); + + /** + * List of raw vendor specific attributes. + * @var array + * @see dumpAttributes(), getAttributes() + */ + var $rawVendorAttributes = array(); + + /** + * Constructor + * + * Loads the RADIUS PECL/extension + * + * @return void + */ + function Auth_RADIUS() + { + $this->PEAR(); + } + + /** + * Adds a RADIUS server to the list of servers for requests. + * + * At most 10 servers may be specified. When multiple servers + * are given, they are tried in round-robin fashion until a + * valid response is received + * + * @access public + * @param string $servername Servername or IP-Address + * @param integer $port Portnumber + * @param string $sharedSecret Shared secret + * @param integer $timeout Timeout for each request + * @param integer $maxtries Max. retries for each request + * @return void + */ + function addServer($servername = 'localhost', $port = 0, $sharedSecret = 'testing123', $timeout = 3, $maxtries = 2) + { + $this->_servers[] = array($servername, $port, $sharedSecret, $timeout, $maxtries); + } + + /** + * Returns an error message, if an error occurred. + * + * @access public + * @return string + */ + function getError() + { + return radius_strerror($this->res); + } + + /** + * Sets the configuration-file. + * + * @access public + * @param string $file Path to the configuration file + * @return void + */ + function setConfigfile($file) + { + $this->_configfile = $file; + } + + /** + * Puts an attribute. + * + * @access public + * @param integer $attrib Attribute-number + * @param mixed $port Attribute-value + * @param type $type Attribute-type + * @return bool true on success, false on error + */ + function putAttribute($attrib, $value, $type = null) + { + if ($type == null) { + $type = gettype($value); + } + + switch ($type) { + case 'integer': + // Fix a conversion error so we should be able to handle 4GB values + return radius_put_int($this->res, $attrib, (float)$value); + + case 'addr': + return radius_put_addr($this->res, $attrib, $value); + + case 'string': + default: + return radius_put_attr($this->res, $attrib, $value); + } + + } + + /** + * Puts a vendor-specific attribute. + * + * @access public + * @param integer $vendor Vendor (MSoft, Cisco, ...) + * @param integer $attrib Attribute-number + * @param mixed $port Attribute-value + * @param type $type Attribute-type + * @return bool true on success, false on error + */ + function putVendorAttribute($vendor, $attrib, $value, $type = null) + { + + if ($type == null) { + $type = gettype($value); + } + + switch ($type) { + case 'integer': + return radius_put_vendor_int($this->res, $vendor, $attrib, $value); + + case 'addr': + return radius_put_vendor_addr($this->res, $vendor,$attrib, $value); + + case 'string': + default: + return radius_put_vendor_attr($this->res, $vendor, $attrib, $value); + } + + } + + /** + * Prints known attributes received from the server. + * + * @access public + */ + function dumpAttributes() + { + foreach ($this->attributes as $name => $data) { + echo "$name:$data<br />\n"; + } + } + + /** + * Return our know attributes array received from the server. + * + * @access public + */ + function listAttributes() + { + return $this->attributes; + } + + /** + * Overwrite this. + * + * @access public + */ + function open() + { + } + + /** + * Overwrite this. + * + * @access public + */ + function createRequest() + { + } + + /** + * Puts standard attributes. + * + * These attributes will always be present in a radius request + * + * @access public + */ + function putStandardAttributes() + { + global $config, $cpzone; + + if (!function_exists("getNasIp")) { + $ipaddr = "0.0.0.0"; + } else { + $ipaddr = getNasIP(); + } + // Add support for sending NAS-IP-Address, set this explicitly as an ip_addr + $this->putAttribute(RADIUS_NAS_IP_ADDRESS, $ipaddr, "addr"); + + // Add support for sending NAS-Identifier + if (empty($config["captiveportal"][$cpzone]["radiusnasid"])) { + $nasId = php_uname("n"); + } else { + $nasId = $config["captiveportal"][$cpzone]["radiusnasid"]; + } + $this->putAttribute(RADIUS_NAS_IDENTIFIER, $nasId); + } + + /** + * Puts custom attributes. + * + * @access public + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + } + + /** + * Configures the radius library. + * + * @access public + * @param string $servername Servername or IP-Address + * @param integer $port Portnumber + * @param string $sharedSecret Shared secret + * @param integer $timeout Timeout for each request + * @param integer $maxtries Max. retries for each request + * @return bool true on success, false on error + * @see addServer() + */ + function putServer($servername, $port = 0, $sharedsecret = 'testing123', $timeout = 3, $maxtries = 3) + { + if (!radius_add_server($this->res, $servername, $port, $sharedsecret, $timeout, $maxtries)) { + return false; + } + return true; + } + + /** + * Configures the radius library via external configurationfile + * + * @access public + * @param string $servername Servername or IP-Address + * @return bool true on success, false on error + */ + function putConfigfile($file) + { + if (!radius_config($this->res, $file)) { + return false; + } + return true; + } + + /** + * Initiates a RADIUS request. + * + * @access public + * @return bool true on success, false on errors + */ + function start() + { + if (!$this->open()) { + return false; + } + + foreach ($this->_servers as $s) { + // Servername, port, sharedsecret, timeout, retries + if (!$this->putServer($s[0], $s[1], $s[2], $s[3], $s[4])) { + return false; + } + } + + if (!empty($this->_configfile)) { + if (!$this->putConfigfile($this->_configfile)) { + return false; + } + } + + $this->createRequest(); + $this->putStandardAttributes(); + $this->putAuthAttributes(); + return true; + } + + /** + * Sends a prepared RADIUS request and waits for a response + * + * @access public + * @return mixed true on success, false on reject, PEAR_Error on error + */ + function send() + { + $req = radius_send_request($this->res); + if (!$req) { + return $this->raiseError(gettext('Error sending request:') . ' ' . $this->getError()); + } + + switch($req) { + case RADIUS_ACCESS_ACCEPT: + if (is_subclass_of($this, 'auth_radius_acct')) { + return $this->raiseError(gettext('RADIUS_ACCESS_ACCEPT is unexpected for accounting')); + } + return true; + + case RADIUS_ACCESS_REJECT: + return false; + + case RADIUS_ACCOUNTING_RESPONSE: + if (is_subclass_of($this, 'auth_radius_pap')) { + return $this->raiseError(gettext('RADIUS_ACCOUNTING_RESPONSE is unexpected for authentication')); + } + return true; + + default: + return $this->raiseError(sprintf(gettext("Unexpected return value: %s"),$req)); + } + + } + + /** + * Reads all received attributes after sending the request. + * + * This methos stores know attributes in the property attributes, + * all attributes (including known attibutes) are stored in rawAttributes + * or rawVendorAttributes. + * NOTE: call this functio also even if the request was rejected, because the + * Server returns usualy an errormessage + * + * @access public + * @return bool true on success, false on error + */ + function getAttributes() + { + + while ($attrib = radius_get_attr($this->res)) { + + if (!is_array($attrib)) { + return false; + } + + $attr = $attrib['attr']; + $data = $attrib['data']; + + $this->rawAttributes[$attr] = $data; + + switch ($attr) { + case RADIUS_FRAMED_IP_ADDRESS: + $this->attributes['framed_ip'] = radius_cvt_addr($data); + break; + + case RADIUS_FRAMED_IP_NETMASK: + $this->attributes['framed_mask'] = radius_cvt_addr($data); + break; + + case RADIUS_FRAMED_MTU: + $this->attributes['framed_mtu'] = radius_cvt_int($data); + break; + + case RADIUS_FRAMED_COMPRESSION: + $this->attributes['framed_compression'] = radius_cvt_int($data); + break; + + case RADIUS_SESSION_TIMEOUT: + $this->attributes['session_timeout'] = radius_cvt_int($data); + break; + + case RADIUS_IDLE_TIMEOUT: + $this->attributes['idle_timeout'] = radius_cvt_int($data); + break; + + case RADIUS_SERVICE_TYPE: + $this->attributes['service_type'] = radius_cvt_int($data); + break; + + case RADIUS_CLASS: + $this->attributes['class'] = radius_cvt_string($data); + break; + + case RADIUS_FRAMED_PROTOCOL: + $this->attributes['framed_protocol'] = radius_cvt_int($data); + break; + + case RADIUS_FRAMED_ROUTING: + $this->attributes['framed_routing'] = radius_cvt_int($data); + break; + + case RADIUS_FILTER_ID: + $this->attributes['filter_id'] = radius_cvt_string($data); + break; + + case RADIUS_REPLY_MESSAGE: + $this->attributes['reply_message'] = radius_cvt_string($data); + break; + + case RADIUS_VENDOR_SPECIFIC: + $attribv = radius_get_vendor_attr($data); + if (!is_array($attribv)) { + return false; + } + + $vendor = $attribv['vendor']; + $attrv = $attribv['attr']; + $datav = $attribv['data']; + + $this->rawVendorAttributes[$vendor][$attrv] = $datav; + + if ($vendor == RADIUS_VENDOR_MICROSOFT) { + + switch ($attrv) { + case RADIUS_MICROSOFT_MS_CHAP2_SUCCESS: + $this->attributes['ms_chap2_success'] = radius_cvt_string($datav); + break; + + case RADIUS_MICROSOFT_MS_CHAP_ERROR: + $this->attributes['ms_chap_error'] = radius_cvt_string(substr($datav,1)); + break; + + case RADIUS_MICROSOFT_MS_CHAP_DOMAIN: + $this->attributes['ms_chap_domain'] = radius_cvt_string($datav); + break; + + case RADIUS_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY: + $this->attributes['ms_mppe_encryption_policy'] = radius_cvt_int($datav); + break; + + case RADIUS_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES: + $this->attributes['ms_mppe_encryption_types'] = radius_cvt_int($datav); + break; + + case RADIUS_MICROSOFT_MS_CHAP_MPPE_KEYS: + $demangled = radius_demangle($this->res, $datav); + $this->attributes['ms_chap_mppe_lm_key'] = substr($demangled, 0, 8); + $this->attributes['ms_chap_mppe_nt_key'] = substr($demangled, 8, RADIUS_MPPE_KEY_LEN); + break; + + case RADIUS_MICROSOFT_MS_MPPE_SEND_KEY: + $this->attributes['ms_chap_mppe_send_key'] = radius_demangle_mppe_key($this->res, $datav); + break; + + case RADIUS_MICROSOFT_MS_MPPE_RECV_KEY: + $this->attributes['ms_chap_mppe_recv_key'] = radius_demangle_mppe_key($this->res, $datav); + break; + + case RADIUS_MICROSOFT_MS_PRIMARY_DNS_SERVER: + $this->attributes['ms_primary_dns_server'] = radius_cvt_string($datav); + break; + } + } + + elseif ($vendor == 1584) { + + switch ($attrv) { + case 102: + $this->attributes['ces_group'] = radius_cvt_string($datav); + break; + } + } + + elseif ($vendor == 3309) { /* RADIUS_VENDOR_NOMADIX */ + + switch ($attrv) { + case 1: /* RADIUS_NOMADIX_BW_UP */ + $this->attributes['bw_up'] = radius_cvt_int($datav); + break; + case 2: /* RADIUS_NOMADIX_BW_DOWN */ + $this->attributes['bw_down'] = radius_cvt_int($datav); + break; + case 3: /* RADIUS_NOMADIX_URL_REDIRECTION */ + $this->attributes['url_redirection'] = radius_cvt_string($datav); + break; + case 5: /* RADIUS_NOMADIX_EXPIRATION */ + $this->attributes['expiration'] = radius_cvt_string($datav); + break; + case 7: /* RADIUS_NOMADIX_MAXBYTESUP */ + $this->attributes['maxbytesup'] = radius_cvt_int($datav); + break; + case 8: /* RADIUS_NOMADIX_MAXBYTESDOWN */ + $this->attributes['maxbytesdown'] = radius_cvt_int($datav); + break; + case 10: /* RADIUS_NOMADIX_LOGOFF_URL */ + $this->attributes['url_logoff'] = radius_cvt_string($datav); + break; + } + } + + elseif ($vendor == 14122) { /* RADIUS_VENDOR_WISPr Wi-Fi Alliance */ + + switch ($attrv) { + case 1: /* WISPr-Location-ID */ + $this->attributes['location_id'] = radius_cvt_string($datav); + break; + case 2: /* WISPr-Location-Name */ + $this->attributes['location_name'] = radius_cvt_string($datav); + break; + case 3: /* WISPr-Logoff-URL */ + $this->attributes['url_logoff'] = radius_cvt_string($datav); + break; + case 4: /* WISPr-Redirection-URL */ + $this->attributes['url_redirection'] = radius_cvt_string($datav); + break; + case 5: /* WISPr-Bandwidth-Min-Up */ + $this->attributes['bw_up_min'] = radius_cvt_int($datav); + break; + case 6: /* WISPr-Bandwidth-Min-Down */ + $this->attributes['bw_down_min'] = radius_cvt_int($datav); + break; + case 7: /* WISPr-Bandwidth-Max-Up */ + $this->attributes['bw_up'] = radius_cvt_int($datav); + break; + case 8: /* WISPr-Bandwidth-Max-Down */ + $this->attributes['bw_down'] = radius_cvt_int($datav); + break; + case 9: /* WISPr-Session-Terminate-Time */ + $this->attributes['session_terminate_time'] = radius_cvt_string($datav); + break; + case 10: /* WISPr-Session-Terminate-End-Of-Day */ + $this->attributes['session_terminate_endofday'] = radius_cvt_int($datav); + break; + case 11: /* WISPr-Billing-Class-Of-Service */ + $this->attributes['billing_class_of_service'] = radius_cvt_string($datav); + break; + } + } + + elseif ($vendor == 14559) { /* RADIUS_VENDOR_ChilliSpot */ + switch ($attrv) { + case 4: /* ChilliSpot-Bandwidth-Max-Up */ + $this->attributes['bw_up'] = radius_cvt_int($datav); + break; + case 5: /* ChilliSpot-Bandwidth-Max-Down */ + $this->attributes['bw_down'] = radius_cvt_int($datav); + break; + } + } + + elseif ($vendor == 9) { /* RADIUS_VENDOR_CISCO */ + switch ($attrv) { + case 1: /* Cisco-AVPair */ + if (!is_array($this->attributes['ciscoavpair'])) + $this->attributes['ciscoavpair'] = array(); + $this->attributes['ciscoavpair'][] = radius_cvt_string($datav); + break; + } + } + + elseif ($vendor == 8744) { /* Colubris / HP MSM wireless */ + //documented at http://bizsupport1.austin.hp.com/bc/docs/support/SupportManual/c02704528/c02704528.pdf pg 15-67 + if ($attrv == 0) { /* Colubris AV-Pair */ + $datav = explode('=', $datav); + switch ($datav[0]) { + case 'max-input-rate': + // "Controls the data rate [kbps] at which traffic can be transferred from the user to the [router]." + $this->attributes['bw_up'] = radius_cvt_int($datav[1]); + break; + case 'max-output-rate': + //"Controls the data rate [kbps] at which traffic can be transferred from the [router] to the user." + $this->attributes['bw_down'] = radius_cvt_int($datav[1]); + break; + case 'max-input-octets': + $this->attributes['maxbytesup'] = radius_cvt_int($datav[1]); + break; + case 'max-output-octets': + $this->attributes['maxbytesdown'] = radius_cvt_int($datav[1]); + break; + case 'welcome-url': + $this->attributes['url_redirection'] = radius_cvt_string($datav[1]); + break; + case 'goodbye-url': + $this->attributes['url_logoff'] = radius_cvt_string($datav[1]); + break; + } + } + } + + break; + + case 85: /* Acct-Interim-Interval: RFC 2869 */ + $this->attributes['interim_interval'] = radius_cvt_int($data); + break; + } + } + + return true; + } + + /** + * Frees resources. + * + * Calling this method is always a good idea, because all security relevant + * attributes are filled with Nullbytes to leave nothing in the mem. + * + * @access public + */ + function close() + { + if ($this->res != null) { + radius_close($this->res); + $this->res = null; + } + $this->username = str_repeat("\0", strlen($this->username)); + $this->password = str_repeat("\0", strlen($this->password)); + } + +} + +/** + * class Auth_RADIUS_PAP + * + * Class for authenticating using PAP (Plaintext) + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_PAP extends Auth_RADIUS +{ + + /** + * Constructor + * + * @param string $username Username + * @param string $password Password + * @return void + */ + function Auth_RADIUS_PAP($username = null, $password = null) + { + $this->Auth_RADIUS(); + $this->username = $username; + $this->password = $password; + } + + /** + * Creates a RADIUS resource + * + * Creates a RADIUS resource for authentication. This should be the first + * call before you make any other things with the library. + * + * @return bool true on success, false on error + */ + function open() + { + $this->res = radius_auth_open(); + if (!$this->res) { + return false; + } + return true; + } + + /** + * Creates an authentication request + * + * Creates an authentication request. + * You MUST call this method before you can put any attribute + * + * @return bool true on success, false on error + */ + function createRequest() + { + if (!radius_create_request($this->res, RADIUS_ACCESS_REQUEST)) { + return false; + } + return true; + } + + /** + * Put authentication specific attributes + * + * @return void + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + if (isset($this->password)) { + $this->putAttribute(RADIUS_USER_PASSWORD, $this->password); + } + } + +} + +/** + * class Auth_RADIUS_CHAP_MD5 + * + * Class for authenticating using CHAP-MD5 see RFC1994. + * Instead og the plaintext password the challenge and + * the response are needed. + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_CHAP_MD5 extends Auth_RADIUS_PAP +{ + /** + * 8 Bytes binary challenge + * @var string + */ + var $challenge = null; + + /** + * 16 Bytes MD5 response binary + * @var string + */ + var $response = null; + + /** + * Id of the authentication request. Should incremented after every request. + * @var integer + */ + var $chapid = 1; + + /** + * Constructor + * + * @param string $username Username + * @param string $challenge 8 Bytes Challenge (binary) + * @param integer $chapid Requestnumber + * @return void + */ + function Auth_RADIUS_CHAP_MD5($username = null, $challenge = null, $chapid = 1) + { + $this->Auth_RADIUS_PAP(); + $this->username = $username; + $this->challenge = $challenge; + $this->chapid = $chapid; + } + + /** + * Put CHAP-MD5 specific attributes + * + * For authenticating using CHAP-MD5 via RADIUS you have to put the challenge + * and the response. The chapid is inserted in the first byte of the response. + * + * @return void + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + if (isset($this->response)) { + $response = pack('C', $this->chapid) . $this->response; + $this->putAttribute(RADIUS_CHAP_PASSWORD, $response); + } + if (isset($this->challenge)) { + $this->putAttribute(RADIUS_CHAP_CHALLENGE, $this->challenge); + } + } + + /** + * Frees resources. + * + * Calling this method is always a good idea, because all security relevant + * attributes are filled with Nullbytes to leave nothing in the mem. + * + * @access public + */ + function close() + { + Auth_RADIUS_PAP::close(); + $this->challenge = str_repeat("\0", strlen($this->challenge)); + $this->response = str_repeat("\0", strlen($this->response)); + } + +} + +/** + * class Auth_RADIUS_MSCHAPv1 + * + * Class for authenticating using MS-CHAPv1 see RFC2433 + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_MSCHAPv1 extends Auth_RADIUS_CHAP_MD5 +{ + /** + * LAN-Manager-Response + * @var string + */ + var $lmResponse = null; + + /** + * Wether using deprecated LM-Responses or not. + * 0 = use LM-Response, 1 = use NT-Response + * @var bool + */ + var $flags = 1; + + /** + * Put MS-CHAPv1 specific attributes + * + * For authenticating using MS-CHAPv1 via RADIUS you have to put the challenge + * and the response. The response has this structure: + * struct rad_mschapvalue { + * u_char ident; + * u_char flags; + * u_char lm_response[24]; + * u_char response[24]; + * }; + * + * @return void + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + if (isset($this->response) || isset($this->lmResponse)) { + $lmResp = isset($this->lmResponse) ? $this->lmResponse : str_repeat ("\0", 24); + $ntResp = isset($this->response) ? $this->response : str_repeat ("\0", 24); + $resp = pack('CC', $this->chapid, $this->flags) . $lmResp . $ntResp; + $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_RESPONSE, $resp); + } + if (isset($this->challenge)) { + $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_CHALLENGE, $this->challenge); + } + } +} + +/** + * class Auth_RADIUS_MSCHAPv2 + * + * Class for authenticating using MS-CHAPv2 see RFC2759 + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_MSCHAPv2 extends Auth_RADIUS_MSCHAPv1 +{ + /** + * 16 Bytes binary challenge + * @var string + */ + var $challenge = null; + + /** + * 16 Bytes binary Peer Challenge + * @var string + */ + var $peerChallenge = null; + + /** + * Put MS-CHAPv2 specific attributes + * + * For authenticating using MS-CHAPv1 via RADIUS you have to put the challenge + * and the response. The response has this structure: + * struct rad_mschapv2value { + * u_char ident; + * u_char flags; + * u_char pchallenge[16]; + * u_char reserved[8]; + * u_char response[24]; + * }; + * where pchallenge is the peer challenge. Like for MS-CHAPv1 we set the flags field to 1. + * @return void + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + if (isset($this->response) && isset($this->peerChallenge)) { + // Response: chapid, flags (1 = use NT Response), Peer challenge, reserved, Response + $resp = pack('CCa16a8a24',$this->chapid , 1, $this->peerChallenge, str_repeat("\0", 8), $this->response); + $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP2_RESPONSE, $resp); + } + if (isset($this->challenge)) { + $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_CHALLENGE, $this->challenge); + } + } + + /** + * Frees resources. + * + * Calling this method is always a good idea, because all security relevant + * attributes are filled with Nullbytes to leave nothing in the mem. + * + * @access public + */ + function close() + { + Auth_RADIUS_MSCHAPv1::close(); + $this->peerChallenge = str_repeat("\0", strlen($this->peerChallenge)); + } +} + +/** + * class Auth_RADIUS_Acct + * + * Class for RADIUS accounting + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_Acct extends Auth_RADIUS +{ + /** + * Defines where the Authentication was made, possible values are: + * RADIUS_AUTH_RADIUS, RADIUS_AUTH_LOCAL, RADIUS_AUTH_REMOTE + * @var integer + */ + var $authentic = null; + + /** + * Defines the type of the accounting request, on of: + * RADIUS_START, RADIUS_STOP, RADIUS_ACCOUNTING_ON, RADIUS_ACCOUNTING_OFF + * @var integer + */ + var $status_type = null; + + /** + * The time the user was logged in in seconds + * @var integer + */ + var $session_time = null; + + /** + * A uniq identifier for the session of the user, maybe the PHP-Session-Id + * @var string + */ + var $session_id = null; + + /** + * Constructor + * + * This function is disabled for M0n0wall since we use our own session_id + * + * Generates a predefined session_id. We use the Remote-Address, the PID, and the Current user. + * @return void + * + function Auth_RADIUS_Acct() + { + $this->Auth_RADIUS(); + + if (isset($_SERVER)) { + $var = &$_SERVER; + } else { + $var = &$GLOBALS['HTTP_SERVER_VARS']; + } + + $this->session_id = sprintf("%s:%d-%s", isset($var['REMOTE_ADDR']) ? $var['REMOTE_ADDR'] : '127.0.0.1' , getmypid(), get_current_user()); + } + */ + + /** + * Constructor + * + */ + + function Auth_RADIUS_Acct() + { + $this->Auth_RADIUS(); + } + + /** + * Creates a RADIUS resource + * + * Creates a RADIUS resource for accounting. This should be the first + * call before you make any other things with the library. + * + * @return bool true on success, false on error + */ + function open() + { + $this->res = radius_acct_open(); + if (!$this->res) { + return false; + } + return true; + } + + /** + * Creates an accounting request + * + * Creates an accounting request. + * You MUST call this method before you can put any attribute. + * + * @return bool true on success, false on error + */ + function createRequest() + { + if (!radius_create_request($this->res, RADIUS_ACCOUNTING_REQUEST)) { + return false; + } + return true; + } + + /** + * Put attributes for accounting. + * + * Here we put some accounting values. There many more attributes for accounting, + * but for web-applications only certain attributes make sense. + * @return void + */ + function putAuthAttributes() + { + if (isset($this->username)) { + $this->putAttribute(RADIUS_USER_NAME, $this->username); + } + $this->putAttribute(RADIUS_ACCT_STATUS_TYPE, $this->status_type); + //if (isset($this->session_time) && $this->status_type == RADIUS_STOP) { + if (isset($this->session_time)) { + $this->putAttribute(RADIUS_ACCT_SESSION_TIME, $this->session_time); + } + if (isset($this->authentic)) { + $this->putAttribute(RADIUS_ACCT_AUTHENTIC, $this->authentic); + } + + $this->putStandardAttributes(); + } + +} + +/** + * class Auth_RADIUS_Acct_Start + * + * Class for RADIUS accounting. Its usualy used, after the user has logged in. + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_Acct_Start extends Auth_RADIUS_Acct +{ + /** + * Defines the type of the accounting request. + * It is set to RADIUS_START by default in this class. + * @var integer + */ + var $status_type = RADIUS_START; +} + +/** + * class Auth_RADIUS_Acct_Start + * + * Class for RADIUS accounting. Its usualy used, after the user has logged out. + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_Acct_Stop extends Auth_RADIUS_Acct +{ + /** + * Defines the type of the accounting request. + * It is set to RADIUS_STOP by default in this class. + * @var integer + */ + var $status_type = RADIUS_STOP; +} + +if (!defined('RADIUS_UPDATE')) + define('RADIUS_UPDATE', 3); + +/** + * class Auth_RADIUS_Acct_Update + * + * Class for interim RADIUS accounting updates. + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_Acct_Update extends Auth_RADIUS_Acct +{ + /** + * Defines the type of the accounting request. + * It is set to RADIUS_UPDATE by default in this class. + * @var integer + */ + var $status_type = RADIUS_UPDATE; +} + +/** + * class Auth_RADIUS_Acct_On + * + * Class for sending Accounting-On updates + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_Acct_On extends Auth_RADIUS_Acct +{ + /** + * Defines the type of the accounting request. + * It is set to RADIUS_ACCOUNTING_ON by default in this class. + * @var integer + */ + var $status_type = RADIUS_ACCOUNTING_ON; +} + +/** + * class Auth_RADIUS_Acct_Off + * + * Class for sending Accounting-Off updates + * + * @package Auth_RADIUS + */ +class Auth_RADIUS_Acct_Off extends Auth_RADIUS_Acct +{ + /** + * Defines the type of the accounting request. + * It is set to RADIUS_ACCOUNTING_OFF by default in this class. + * @var integer + */ + var $status_type = RADIUS_ACCOUNTING_OFF; +} + +?> diff --git a/src/etc/inc/rrd.inc b/src/etc/inc/rrd.inc new file mode 100644 index 0000000..86148d6 --- /dev/null +++ b/src/etc/inc/rrd.inc @@ -0,0 +1,989 @@ +<?php +/* $Id$ */ +/* + rrd.inc + Copyright (C) 2010 Seth Mos <seth.mos@dds.nl> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ + +/* + pfSense_BUILDER_BINARIES: /bin/rm /usr/bin/nice /usr/local/bin/rrdtool /bin/cd + pfSense_MODULE: rrd +*/ + +/* include all configuration functions */ + +function dump_rrd_to_xml($rrddatabase, $xmldumpfile) { + $rrdtool = "/usr/bin/nice -n20 /usr/local/bin/rrdtool"; + unlink_if_exists($xmldumpfile); + + exec("$rrdtool dump " . escapeshellarg($rrddatabase) . " {$xmldumpfile} 2>&1", $dumpout, $dumpret); + if ($dumpret <> 0) { + $dumpout = implode(" ", $dumpout); + log_error(sprintf(gettext('RRD dump failed exited with %1$s, the error is: %2$s'), $dumpret, $dumpout)); + } + return($dumpret); +} + +function restore_rrd() { + global $g, $config; + + $rrddbpath = "/var/db/rrd/"; + $rrdtool = "/usr/bin/nice -n20 /usr/local/bin/rrdtool"; + + $rrdrestore = ""; + $rrdreturn = ""; + if (file_exists("{$g['cf_conf_path']}/rrd.tgz") && (isset($config['system']['use_mfs_tmpvar']) || $g['platform'] != "pfSense")) { + foreach (glob("{$rrddbpath}/*.xml") as $xml_file) { + @unlink($xml_file); + } + unset($rrdrestore); + $_gb = exec("cd /;LANG=C /usr/bin/tar -tf {$g['cf_conf_path']}/rrd.tgz", $rrdrestore, $rrdreturn); + if ($rrdreturn != 0) { + log_error("RRD restore failed exited with $rrdreturn, the error is: $rrdrestore\n"); + return; + } + foreach ($rrdrestore as $xml_file) { + $rrd_file = '/' . substr($xml_file, 0, -4) . '.rrd'; + if (file_exists("{$rrd_file}")) { + @unlink($rrd_file); + } + file_put_contents("{$g['tmp_path']}/rrd_restore", $xml_file); + $_gb = exec("cd /;LANG=C /usr/bin/tar -xf {$g['cf_conf_path']}/rrd.tgz -T {$g['tmp_path']}/rrd_restore"); + if (!file_exists("/{$xml_file}")) { + log_error("Could not extract {$xml_file} RRD xml file from archive!"); + continue; + } + $_gb = exec("$rrdtool restore -f '/{$xml_file}' '{$rrd_file}'", $output, $status); + if ($status) { + log_error("rrdtool restore -f '{$xml_file}' '{$rrd_file}' failed returning {$status}."); + continue; + } + unset($output); + @unlink("/{$xml_file}"); + } + unset($rrdrestore); + @unlink("{$g['tmp_path']}/rrd_restore"); + /* If this backup is still there on a full install, but we aren't going to use ram disks, remove the archive since this is a transition. */ + if (($g['platform'] == "pfSense") && !isset($config['system']['use_mfs_tmpvar'])) { + unlink_if_exists("{$g['cf_conf_path']}/rrd.tgz"); + } + return true; + } + return false; +} + +function create_new_rrd($rrdcreatecmd) { + $rrdcreateoutput = array(); + $rrdcreatereturn = 0; + $_gb = exec("$rrdcreatecmd 2>&1", $rrdcreateoutput, $rrdcreatereturn); + if ($rrdcreatereturn <> 0) { + $rrdcreateoutput = implode(" ", $rrdcreateoutput); + log_error(sprintf(gettext('RRD create failed exited with %1$s, the error is: %2$s'), $rrdcreatereturn, $rrdcreateoutput)); + } + unset($rrdcreateoutput); + return $rrdcreatereturn; +} + +function migrate_rrd_format($rrdoldxml, $rrdnewxml) { + if (!file_exists("/tmp/rrd_notice_sent.txt")) { + $_gb = exec("echo 'Converting RRD configuration to new format. This might take a bit...' | wall"); + @touch("/tmp/rrd_notice_sent.txt"); + } + $numrraold = count($rrdoldxml['rra']); + $numrranew = count($rrdnewxml['rra']); + $numdsold = count($rrdoldxml['ds']); + $numdsnew = count($rrdnewxml['ds']); + log_error(sprintf(gettext('Import RRD has %1$s DS values and %2$s RRA databases, new format RRD has %3$s DS values and %4$s RRA databases'), $numdsold, $numrraold, $numdsnew , $numrranew)); + + /* add data sources not found in the old array from the new array */ + $i = 0; + foreach ($rrdnewxml['ds'] as $ds) { + if (!is_array($rrdoldxml['ds'][$i])) { + $rrdoldxml['ds'][$i] = $rrdnewxml['ds'][$i]; + /* set unknown values to 0 */ + $rrdoldxml['ds'][$i]['last_ds'] = " 0.0000000000e+00 "; + $rrdoldxml['ds'][$i]['value'] = " 0.0000000000e+00 "; + $rrdoldxml['ds'][$i]['unknown_sec'] = "0"; + } + $i++; + } + + $i = 0; + $rracountold = count($rrdoldxml['rra']); + $rracountnew = count($rrdnewxml['rra']); + /* process each RRA, which contain a database */ + foreach ($rrdnewxml['rra'] as $rra) { + if (!is_array($rrdoldxml['rra'][$i])) { + $rrdoldxml['rra'][$i] = $rrdnewxml['rra'][$i]; + } + + $d = 0; + /* process cdp_prep */ + $cdp_prep = $rra['cdp_prep']; + foreach ($cdp_prep['ds'] as $ds) { + if (!is_array($rrdoldxml['rra'][$i]['cdp_prep']['ds'][$d])) { + $rrdoldxml['rra'][$i]['cdp_prep']['ds'][$d] = $rrdnewxml['rra'][$i]['cdp_prep']['ds'][$d]; + $rrdoldxml['rra'][$i]['cdp_prep']['ds'][$d]['primary_value'] = " 0.0000000000e+00 "; + $rrdoldxml['rra'][$i]['cdp_prep']['ds'][$d]['secondary_value'] = " 0.0000000000e+00 "; + $rrdoldxml['rra'][$i]['cdp_prep']['ds'][$d]['value'] = " 0.0000000000e+00 "; + $rrdoldxml['rra'][$i]['cdp_prep']['ds'][$d]['unknown_datapoints'] = "0"; + } + $d++; + } + + /* process database */ + $rows = $rra['database']; + $k = 0; + $rowcountold = count($rrdoldxml['rra'][$i]['database']['row']); + $rowcountnew = count($rrdnewxml['rra'][$i]['database']['row']); + $rowcountdiff = $rowcountnew - $rowcountold; + /* save old rows for a bit before we put the required empty rows before it */ + $rowsdata = $rows; + $rowsempty = array(); + $r = 0; + while ($r < $rowcountdiff) { + $rowsempty[] = $rrdnewxml['rra'][$i]['database']['row'][$r]; + $r++; + } + $rows = $rowsempty + $rowsdata; + /* now foreach the rows in the database */ + foreach ($rows['row'] as $row) { + if (!is_array($rrdoldxml['rra'][$i]['database']['row'][$k])) { + $rrdoldxml['rra'][$i]['database']['row'][$k] = $rrdnewxml['rra'][$i]['database']['row'][$k]; + } + $m = 0; + $vcountold = count($rrdoldxml['rra'][$i]['database']['row'][$k]['v']); + $vcountnew = count($rrdnewxml['rra'][$i]['database']['row'][$k]['v']); + foreach ($row['v'] as $value) { + if (empty($rrdoldxml['rra'][$i]['database']['row'][$k]['v'][$m])) { + if (isset($valid)) { + $rrdoldxml['rra'][$i]['database']['row'][$k]['v'][$m] = "0.0000000000e+00 "; + } else { + $rrdoldxml['rra'][$i]['database']['row'][$k]['v'][$m] = $rrdnewxml['rra'][$i]['database']['row'][$k]['v'][$m]; + } + } else { + if ($value <> " NaN ") { + $valid = true; + } else { + $valid = false; + } + } + $m++; + } + $k++; + } + $i++; + } + + $numrranew = count($rrdoldxml['rra']); + $numdsnew = count($rrdoldxml['ds']); + log_error(sprintf(gettext('The new RRD now has %1$s DS values and %2$s RRA databases'), $numdsnew, $numrranew)); + return $rrdoldxml; +} + +function enable_rrd_graphing() { + global $config, $g, $altq_list_queues; + + if (platform_booting()) { + echo gettext("Generating RRD graphs..."); + } + + $rrddbpath = "/var/db/rrd/"; + $rrdgraphpath = "/usr/local/www/rrd"; + + $traffic = "-traffic.rrd"; + $packets = "-packets.rrd"; + $states = "-states.rrd"; + $wireless = "-wireless.rrd"; + $queues = "-queues.rrd"; + $queuesdrop = "-queuedrops.rrd"; + $spamd = "-spamd.rrd"; + $proc = "-processor.rrd"; + $mem = "-memory.rrd"; + $mbuf = "-mbuf.rrd"; + $cellular = "-cellular.rrd"; + $vpnusers = "-vpnusers.rrd"; + $captiveportalconcurrent = "-concurrent.rrd"; + $captiveportalloggedin = "-loggedin.rrd"; + $ntpd = "ntpd.rrd"; + + $rrdtool = "/usr/bin/nice -n20 /usr/local/bin/rrdtool"; + $netstat = "/usr/bin/netstat"; + $awk = "/usr/bin/awk"; + $tar = "/usr/bin/tar"; + $pfctl = "/sbin/pfctl"; + $sysctl = "/sbin/sysctl"; + $php = "/usr/local/bin/php-cgi"; + $cpustats = "/usr/local/sbin/cpustats"; + $spamd_gather = "/usr/local/bin/spamd_gather_stats.php"; + $ifconfig = "/sbin/ifconfig"; + $captiveportal_gather = "/usr/local/bin/captiveportal_gather_stats.php"; + $ntpq = "/usr/local/sbin/ntpq"; + + $rrdtrafficinterval = 60; + $rrdwirelessinterval = 60; + $rrdqueuesinterval = 60; + $rrdqueuesdropinterval = 60; + $rrdpacketsinterval = 60; + $rrdstatesinterval = 60; + $rrdspamdinterval = 60; + $rrdlbpoolinterval = 60; + $rrdprocinterval = 60; + $rrdmeminterval = 60; + $rrdmbufinterval = 60; + $rrdcellularinterval = 60; + $rrdvpninterval = 60; + $rrdcaptiveportalinterval = 60; + $rrdntpdinterval = 60; + + $trafficvalid = $rrdtrafficinterval * 2; + $wirelessvalid = $rrdwirelessinterval * 2; + $queuesvalid = $rrdqueuesinterval * 2; + $queuesdropvalid = $rrdqueuesdropinterval * 2; + $packetsvalid = $rrdpacketsinterval * 2; + $statesvalid = $rrdstatesinterval*2; + $spamdvalid = $rrdspamdinterval * 2; + $lbpoolvalid = $rrdlbpoolinterval * 2; + $procvalid = $rrdlbpoolinterval * 2; + $memvalid = $rrdmeminterval * 2; + $mbufvalid = $rrdmbufinterval * 2; + $cellularvalid = $rrdcellularinterval * 2; + $vpnvalid = $rrdvpninterval * 2; + $captiveportalvalid = $rrdcaptiveportalinterval * 2; + $ntpdvalid = $rrdntpdinterval * 2; + + /* Assume 2*10GigE for now */ + $downstream = 2500000000; + $upstream = 2500000000; + + /* read the shaper config */ + read_altq_config(); + + if (isset ($config['rrd']['enable'])) { + + /* create directory if needed */ + if (!is_dir($rrddbpath)) { + mkdir($rrddbpath, 0775); + } + chown($rrddbpath, "nobody"); + + if (platform_booting()) { + restore_rrd(); + } + + /* db update script */ + $rrdupdatesh = "#!/bin/sh\n"; + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "export TERM=dumb\n"; + $rrdupdatesh .= "\n"; + $rrdupdatesh .= 'echo $$ > ' . $g['varrun_path'] . '/updaterrd.sh.pid'; + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "counter=1\n"; + $rrdupdatesh .= "while [ \"\$counter\" -ne 0 ]\n"; + $rrdupdatesh .= "do\n"; + $rrdupdatesh .= ""; + + $i = 0; + $ifdescrs = get_configured_interface_with_descr(); + /* IPsec counters */ + $ifdescrs['ipsec'] = "IPsec"; + /* OpenVPN server counters */ + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as $server) { + $serverid = "ovpns" . $server['vpnid']; + $ifdescrs[$serverid] = "{$server['description']}"; + } + } + + if (platform_booting()) { + if (!is_dir("{$g['vardb_path']}/rrd")) { + mkdir("{$g['vardb_path']}/rrd", 0775); + } + + @chown("{$g['vardb_path']}/rrd", "nobody"); + } + + /* process all real and pseudo interfaces */ + foreach ($ifdescrs as $ifname => $ifdescr) { + $temp = get_real_interface($ifname); + if ($temp <> "") { + $realif = $temp; + } + + /* TRAFFIC, set up the rrd file */ + if (!file_exists("$rrddbpath$ifname$traffic")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$traffic --step $rrdtrafficinterval "; + $rrdcreate .= "DS:inpass:COUNTER:$trafficvalid:0:$downstream "; + $rrdcreate .= "DS:outpass:COUNTER:$trafficvalid:0:$upstream "; + $rrdcreate .= "DS:inblock:COUNTER:$trafficvalid:0:$downstream "; + $rrdcreate .= "DS:outblock:COUNTER:$trafficvalid:0:$upstream "; + $rrdcreate .= "DS:inpass6:COUNTER:$trafficvalid:0:$downstream "; + $rrdcreate .= "DS:outpass6:COUNTER:$trafficvalid:0:$upstream "; + $rrdcreate .= "DS:inblock6:COUNTER:$trafficvalid:0:$downstream "; + $rrdcreate .= "DS:outblock6:COUNTER:$trafficvalid:0:$upstream "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $rrddbpath$ifname$traffic N:U:U:U:U:U:U:U:U"); + } + + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "# polling traffic for interface $ifname $realif IPv4/IPv6 counters \n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ifname$traffic N:"; + $rrdupdatesh .= "`$pfctl -vvsI -i {$realif} | awk '\\\n"; + $rrdupdatesh .= "/In4\/Pass/ { b4pi = \$6 };/Out4\/Pass/ { b4po = \$6 };/In4\/Block/ { b4bi = \$6 };/Out4\/Block/ { b4bo = \$6 };\\\n"; + $rrdupdatesh .= "/In6\/Pass/ { b6pi = \$6 };/Out6\/Pass/ { b6po = \$6 };/In6\/Block/ { b6bi = \$6 };/Out6\/Block/ { b6bo = \$6 };\\\n"; + $rrdupdatesh .= "END {print b4pi \":\" b4po \":\" b4bi \":\" b4bo \":\" b6pi \":\" b6po \":\" b6bi \":\" b6bo};'`\n"; + + /* PACKETS, set up the rrd file */ + if (!file_exists("$rrddbpath$ifname$packets")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$packets --step $rrdpacketsinterval "; + $rrdcreate .= "DS:inpass:COUNTER:$packetsvalid:0:$downstream "; + $rrdcreate .= "DS:outpass:COUNTER:$packetsvalid:0:$upstream "; + $rrdcreate .= "DS:inblock:COUNTER:$packetsvalid:0:$downstream "; + $rrdcreate .= "DS:outblock:COUNTER:$packetsvalid:0:$upstream "; + $rrdcreate .= "DS:inpass6:COUNTER:$packetsvalid:0:$downstream "; + $rrdcreate .= "DS:outpass6:COUNTER:$packetsvalid:0:$upstream "; + $rrdcreate .= "DS:inblock6:COUNTER:$packetsvalid:0:$downstream "; + $rrdcreate .= "DS:outblock6:COUNTER:$packetsvalid:0:$upstream "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $rrddbpath$ifname$packets N:U:U:U:U:U:U:U:U"); + } + + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "# polling packets for interface $ifname $realif \n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ifname$packets N:"; + $rrdupdatesh .= "`$pfctl -vvsI -i {$realif} | awk '\\\n"; + $rrdupdatesh .= "/In4\/Pass/ { b4pi = \$4 };/Out4\/Pass/ { b4po = \$4 };/In4\/Block/ { b4bi = \$4 };/Out4\/Block/ { b4bo = \$4 };\\\n"; + $rrdupdatesh .= "/In6\/Pass/ { b6pi = \$4 };/Out6\/Pass/ { b6po = \$4 };/In6\/Block/ { b6bi = \$4 };/Out6\/Block/ { b6bo = \$4 };\\\n"; + $rrdupdatesh .= "END {print b4pi \":\" b4po \":\" b4bi \":\" b4bo \":\" b6pi \":\" b6po \":\" b6bi \":\" b6bo};'`\n"; + + /* WIRELESS, set up the rrd file */ + if ($config['interfaces'][$ifname]['wireless']['mode'] == "bss") { + if (!file_exists("$rrddbpath$ifname$wireless")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$wireless --step $rrdwirelessinterval "; + $rrdcreate .= "DS:snr:GAUGE:$wirelessvalid:0:1000 "; + $rrdcreate .= "DS:rate:GAUGE:$wirelessvalid:0:1000 "; + $rrdcreate .= "DS:channel:GAUGE:$wirelessvalid:0:1000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $rrddbpath$ifname$wireless N:U:U:U"); + } + + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "# polling wireless for interface $ifname $realif \n"; + $rrdupdatesh .= "WIFI=`$ifconfig {$realif} list sta| $awk 'gsub(\"M\", \"\") {getline 2;print substr(\$5, 0, length(\$5)-2) \":\" $4 \":\" $3}'`\n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ifname$wireless N:\${WIFI}\n"; + } + + /* OpenVPN, set up the rrd file */ + if (stristr($ifname, "ovpns")) { + if (!file_exists("$rrddbpath$ifname$vpnusers")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$vpnusers --step $rrdvpninterval "; + $rrdcreate .= "DS:users:GAUGE:$vpnvalid:0:10000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $rrddbpath$ifname$vpnusers N:U"); + } + + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as $server) { + if ("ovpns{$server['vpnid']}" == $ifname) { + $port = $server['local_port']; + $vpnid = $server['vpnid']; + } + } + } + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "# polling vpn users for interface $ifname $realif port $port\n"; + $rrdupdatesh .= "list_current_users() {\n"; + $rrdupdatesh .= " sleep 0.2\n"; + $rrdupdatesh .= " echo \"status 2\"\n"; + $rrdupdatesh .= " sleep 0.2\n"; + $rrdupdatesh .= " echo \"quit\"\n"; + $rrdupdatesh .= "}\n"; + $rrdupdatesh .= "OVPN=`list_current_users | nc -U {$g['varetc_path']}/openvpn/server{$vpnid}.sock | awk -F\",\" '/^CLIENT_LIST/ {print \$2}' | wc -l | awk '{print $1}'`\n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ifname$vpnusers N:\${OVPN}\n"; + } + + /* QUEUES, set up the queues databases */ + if ($altq_list_queues[$ifname]) { + $altq =& $altq_list_queues[$ifname]; + /* NOTE: Is it worth as its own function?! */ + switch ($altq->GetBwscale()) { + case "Gb": + $factor = 1024 * 1024 * 1024; + break; + case "Mb": + $factor = 1024 * 1024; + break; + case "Kb": + $factor = 1024; + break; + case "b": + default: + $factor = 1; + break; + } + $qbandwidth = $altq->GetBandwidth() * $factor; + if ($qbandwidth <= 0) { + $qbandwidth = 100 * 1000 * 1000; /* 100Mbit */ + } + $qlist =& $altq->get_queue_list($notused); + if (!file_exists("$rrddbpath$ifname$queues")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$queues --step $rrdqueuesinterval "; + /* loop list of shaper queues */ + $q = 0; + foreach ($qlist as $qname => $q) { + $rrdcreate .= "DS:$qname:COUNTER:$queuesvalid:0:$qbandwidth "; + } + + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + if (!file_exists("$rrddbpath$ifname$queuesdrop")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$queuesdrop --step $rrdqueuesdropinterval "; + /* loop list of shaper queues */ + $q = 0; + foreach ($qlist as $qname => $q) { + $rrdcreate .= "DS:$qname:COUNTER:$queuesdropvalid:0:$qbandwidth "; + } + + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + if (platform_booting()) { + $rrdqcommand = "-t "; + $rrducommand = "N"; + $qi = 0; + foreach ($qlist as $qname => $q) { + if ($qi == 0) { + $rrdqcommand .= "{$qname}"; + } else { + $rrdqcommand .= ":{$qname}"; + } + $qi++; + $rrducommand .= ":U"; + } + mwexec("$rrdtool update $rrddbpath$ifname$queues $rrdqcommand $rrducommand"); + mwexec("$rrdtool update $rrddbpath$ifname$queuesdrop $rrdqcommand $rrducommand"); + } + + /* awk function to gather shaper data */ + /* yes, it's special */ + $rrdupdatesh .= "` pfctl -vsq -i {$realif} | awk 'BEGIN {printf \"$rrdtool update $rrddbpath$ifname$queues \" } "; + $rrdupdatesh .= "{ "; + $rrdupdatesh .= "if ((\$1 == \"queue\") && ( \$2 ~ /^q/ )) { "; + $rrdupdatesh .= " dsname = dsname \":\" \$2 ; "; + $rrdupdatesh .= " q=1; "; + $rrdupdatesh .= "} "; + $rrdupdatesh .= " else if ((\$4 == \"bytes:\") && ( q == 1 ) ) { "; + $rrdupdatesh .= " dsdata = dsdata \":\" \$5 ; "; + $rrdupdatesh .= " q=0; "; + $rrdupdatesh .= "} "; + $rrdupdatesh .= "} END { "; + $rrdupdatesh .= " dsname = substr(dsname,2); "; + $rrdupdatesh .= " dsdata = substr(dsdata,2); "; + $rrdupdatesh .= " printf \"-t \" dsname \" N:\" dsdata }' "; + $rrdupdatesh .= " dsname=\"\" dsdata=\"\"`\n\n"; + + $rrdupdatesh .= "` pfctl -vsq -i {$realif} | awk 'BEGIN {printf \"$rrdtool update $rrddbpath$ifname$queuesdrop \" } "; + $rrdupdatesh .= "{ "; + $rrdupdatesh .= "if ((\$1 == \"queue\") && ( \$2 ~ /^q/ )) { "; + $rrdupdatesh .= " dsname = dsname \":\" \$2 ; "; + $rrdupdatesh .= " q=1; "; + $rrdupdatesh .= "} "; + $rrdupdatesh .= " else if ((\$4 == \"bytes:\") && ( q == 1 ) ) { "; + $rrdupdatesh .= " dsdata = dsdata \":\" \$8 ; "; + $rrdupdatesh .= " q=0; "; + $rrdupdatesh .= "} "; + $rrdupdatesh .= "} END { "; + $rrdupdatesh .= " dsname = substr(dsname,2); "; + $rrdupdatesh .= " dsdata = substr(dsdata,2); "; + $rrdupdatesh .= " printf \"-t \" dsname \" N:\" dsdata }' "; + $rrdupdatesh .= " dsname=\"\" dsdata=\"\"`\n\n"; + } + + /* 3G interfaces */ + if (preg_match("/ppp[0-9]+/i", $realif)) { + if (!file_exists("$rrddbpath$ifname$cellular")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$cellular --step $rrdcellularinterval "; + $rrdcreate .= "DS:rssi:GAUGE:$cellularvalid:0:100 "; + $rrdcreate .= "DS:upstream:GAUGE:$cellularvalid:0:100000000 "; + $rrdcreate .= "DS:downstream:GAUGE:$cellularvalid:0:100000000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $rrddbpath$ifname$cellular N:U:U:U"); + } + + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "# polling 3G\n"; + $rrdupdatesh .= "GSTATS=`awk -F, 'getline 2 {print \$2 \":\" \$8 \":\" \$9}' < /tmp/3gstats.$ifname`\n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ifname$cellular N:\"\$GSTATS\""; + } + + } + $i++; + + /* System only statistics */ + $ifname = "system"; + + /* STATES, create pf states database */ + if (!file_exists("$rrddbpath$ifname$states")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$states --step $rrdstatesinterval "; + $rrdcreate .= "DS:pfrate:GAUGE:$statesvalid:0:10000000 "; + $rrdcreate .= "DS:pfstates:GAUGE:$statesvalid:0:10000000 "; + $rrdcreate .= "DS:pfnat:GAUGE:$statesvalid:0:10000000 "; + $rrdcreate .= "DS:srcip:GAUGE:$statesvalid:0:10000000 "; + $rrdcreate .= "DS:dstip:GAUGE:$statesvalid:0:10000000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $rrddbpath$ifname$states N:U:U:U:U:U"); + } + + /* the pf states gathering function. */ + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "pfctl_si_out=\"` $pfctl -si > /tmp/pfctl_si_out `\"\n"; + $rrdupdatesh .= "pfctl_ss_out=\"` $pfctl -ss > /tmp/pfctl_ss_out`\"\n"; + $rrdupdatesh .= "pfrate=\"` cat /tmp/pfctl_si_out | egrep \"inserts|removals\" | awk '{ pfrate = \$3 + pfrate } {print pfrate}'|tail -1 `\"\n"; + $rrdupdatesh .= "pfstates=\"` cat /tmp/pfctl_ss_out | egrep -v \"<\\-.*?<\\-|\\->.*?\\->\" | wc -l|sed 's/ //g'`\"\n"; + $rrdupdatesh .= "pfnat=\"` cat /tmp/pfctl_ss_out | egrep '<\\-.*?<\\-|\\->.*?\\->' | wc -l|sed 's/ //g' `\"\n"; + $rrdupdatesh .= "srcip=\"` cat /tmp/pfctl_ss_out | egrep -v '<\\-.*?<\\-|\\->.*?\\->' | grep '\\->' | awk '{print \$3}' | awk -F: '{print \$1}' | sort -u|wc -l|sed 's/ //g' `\"\n"; + $rrdupdatesh .= "dstip=\"` cat /tmp/pfctl_ss_out | egrep -v '<\\-.*?<\\-|\\->.*?\\->' | grep '<\\-' | awk '{print \$3}' | awk -F: '{print \$1}' | sort -u|wc -l|sed 's/ //g' `\"\n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ifname$states N:\$pfrate:\$pfstates:\$pfnat:\$srcip:\$dstip\n\n"; + + /* End pf states statistics */ + + /* CPU, create CPU statistics database */ + if (!file_exists("$rrddbpath$ifname$proc")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$proc --step $rrdprocinterval "; + $rrdcreate .= "DS:user:GAUGE:$procvalid:0:10000000 "; + $rrdcreate .= "DS:nice:GAUGE:$procvalid:0:10000000 "; + $rrdcreate .= "DS:system:GAUGE:$procvalid:0:10000000 "; + $rrdcreate .= "DS:interrupt:GAUGE:$procvalid:0:10000000 "; + $rrdcreate .= "DS:processes:GAUGE:$procvalid:0:10000000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $rrddbpath$ifname$proc N:U:U:U:U:U"); + } + + /* the CPU stats gathering function. */ + $rrdupdatesh .= "CPU=`$cpustats | cut -f1-4 -d':'`\n"; + /* Using ps uxaH will count all processes including system threads. Top was undercounting. */ + $rrdupdatesh .= "PROCS=`ps uxaH | wc -l | awk '{print \$1;}'`\n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ifname$proc N:\${CPU}:\${PROCS}\n"; + + /* End CPU statistics */ + + /* Memory, create Memory statistics database */ + if (!file_exists("$rrddbpath$ifname$mem")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$mem --step $rrdmeminterval "; + $rrdcreate .= "DS:active:GAUGE:$memvalid:0:10000000 "; + $rrdcreate .= "DS:inactive:GAUGE:$memvalid:0:10000000 "; + $rrdcreate .= "DS:free:GAUGE:$memvalid:0:10000000 "; + $rrdcreate .= "DS:cache:GAUGE:$memvalid:0:10000000 "; + $rrdcreate .= "DS:wire:GAUGE:$memvalid:0:10000000 "; + $rrdcreate .= "RRA:MIN:0.5:1:1200 "; + $rrdcreate .= "RRA:MIN:0.5:5:720 "; + $rrdcreate .= "RRA:MIN:0.5:60:1860 "; + $rrdcreate .= "RRA:MIN:0.5:1440:2284 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + $rrdcreate .= "RRA:MAX:0.5:1:1200 "; + $rrdcreate .= "RRA:MAX:0.5:5:720 "; + $rrdcreate .= "RRA:MAX:0.5:60:1860 "; + $rrdcreate .= "RRA:MAX:0.5:1440:2284"; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $rrddbpath$ifname$mem N:U:U:U:U:U"); + } + + /* the Memory stats gathering function. */ + $rrdupdatesh .= "MEM=`$sysctl -n vm.stats.vm.v_page_count vm.stats.vm.v_active_count vm.stats.vm.v_inactive_count vm.stats.vm.v_free_count vm.stats.vm.v_cache_count vm.stats.vm.v_wire_count | "; + $rrdupdatesh .= " $awk '{getline active;getline inactive;getline free;getline cache;getline wire;printf "; + $rrdupdatesh .= "((active/$0) * 100)\":\"((inactive/$0) * 100)\":\"((free/$0) * 100)\":\"((cache/$0) * 100)\":\"(wire/$0 * 100)}'`\n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ifname$mem N:\${MEM}\n"; + + /* End Memory statistics */ + + /* mbuf, create mbuf statistics database */ + if (!file_exists("$rrddbpath$ifname$mbuf")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$mbuf --step $rrdmbufinterval "; + $rrdcreate .= "DS:current:GAUGE:$mbufvalid:0:10000000 "; + $rrdcreate .= "DS:cache:GAUGE:$mbufvalid:0:10000000 "; + $rrdcreate .= "DS:total:GAUGE:$mbufvalid:0:10000000 "; + $rrdcreate .= "DS:max:GAUGE:$mbufvalid:0:10000000 "; + $rrdcreate .= "RRA:MIN:0.5:1:1200 "; + $rrdcreate .= "RRA:MIN:0.5:5:720 "; + $rrdcreate .= "RRA:MIN:0.5:60:1860 "; + $rrdcreate .= "RRA:MIN:0.5:1440:2284 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + $rrdcreate .= "RRA:MAX:0.5:1:1200 "; + $rrdcreate .= "RRA:MAX:0.5:5:720 "; + $rrdcreate .= "RRA:MAX:0.5:60:1860 "; + $rrdcreate .= "RRA:MAX:0.5:1440:2284"; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $rrddbpath$ifname$mbuf N:U:U:U:U"); + } + + /* the mbuf stats gathering function. */ + $rrdupdatesh .= "MBUF=`$netstat -m | "; + $rrdupdatesh .= " $awk '/mbuf clusters in use/ { gsub(/\//, \":\", $1); print $1; }'`\n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ifname$mbuf N:\${MBUF}\n"; + + /* End mbuf statistics */ + + /* SPAMD, set up the spamd rrd file */ + if (isset($config['installedpackages']['spamdsettings']) && + $config['installedpackages']['spamdsettings']['config'][0]['enablerrd']) { + /* set up the spamd rrd file */ + if (!file_exists("$rrddbpath$ifname$spamd")) { + $rrdcreate = "$rrdtool create $rrddbpath$ifname$spamd --step $rrdspamdinterval "; + $rrdcreate .= "DS:conn:GAUGE:$spamdvalid:0:10000 "; + $rrdcreate .= "DS:time:GAUGE:$spamdvalid:0:86400 "; + $rrdcreate .= "RRA:MIN:0.5:1:1200 "; + $rrdcreate .= "RRA:MIN:0.5:5:720 "; + $rrdcreate .= "RRA:MIN:0.5:60:1860 "; + $rrdcreate .= "RRA:MIN:0.5:1440:2284 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + $rrdcreate .= "RRA:MAX:0.5:1:1200 "; + $rrdcreate .= "RRA:MAX:0.5:5:720 "; + $rrdcreate .= "RRA:MAX:0.5:60:1860 "; + $rrdcreate .= "RRA:MAX:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "# polling spamd for connections and tarpitness \n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ifname$spamd \\\n"; + $rrdupdatesh .= "`$php -q $spamd_gather`\n"; + + } + /* End System statistics */ + + /* Captive Portal statistics, set up the rrd file */ + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cpkey => $cp) { + if (!isset($cp['enable'])) { + continue; + } + + $ifname= "captiveportal"; + $concurrent_filename = $rrddbpath . $ifname . '-' . $cpkey . $captiveportalconcurrent; + if (!file_exists("$concurrent_filename")) { + $rrdcreate = "$rrdtool create $concurrent_filename --step $rrdcaptiveportalinterval "; + $rrdcreate .= "DS:concurrentusers:GAUGE:$captiveportalvalid:0:10000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + $rrdcreate .= "RRA:MIN:0.5:1:1200 "; + $rrdcreate .= "RRA:MIN:0.5:5:720 "; + $rrdcreate .= "RRA:MIN:0.5:60:1860 "; + $rrdcreate .= "RRA:MIN:0.5:1440:2284 "; + $rrdcreate .= "RRA:MAX:0.5:1:1200 "; + $rrdcreate .= "RRA:MAX:0.5:5:720 "; + $rrdcreate .= "RRA:MAX:0.5:60:1860 "; + $rrdcreate .= "RRA:MAX:0.5:1440:2284 "; + $rrdcreate .= "RRA:LAST:0.5:1:1200 "; + $rrdcreate .= "RRA:LAST:0.5:5:720 "; + $rrdcreate .= "RRA:LAST:0.5:60:1860 "; + $rrdcreate .= "RRA:LAST:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $concurrent_filename N:U"); + } + + /* the Captive Portal stats gathering function. */ + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "# polling Captive Portal for number of concurrent users\n"; + $rrdupdatesh .= "CP=`${php} -q ${captiveportal_gather} '${cpkey}' 'concurrent'`\n"; + $rrdupdatesh .= "$rrdtool update $concurrent_filename \${CP}\n"; + + $loggedin_filename = $rrddbpath . $ifname . '-' . $cpkey . $captiveportalloggedin; + if (!file_exists("$loggedin_filename")) { + $rrdcreate = "$rrdtool create $loggedin_filename --step $rrdcaptiveportalinterval "; + $rrdcreate .= "DS:loggedinusers:GAUGE:$captiveportalvalid:0:10000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + $rrdcreate .= "RRA:MIN:0.5:1:1200 "; + $rrdcreate .= "RRA:MIN:0.5:5:720 "; + $rrdcreate .= "RRA:MIN:0.5:60:1860 "; + $rrdcreate .= "RRA:MIN:0.5:1440:2284 "; + $rrdcreate .= "RRA:MAX:0.5:1:1200 "; + $rrdcreate .= "RRA:MAX:0.5:5:720 "; + $rrdcreate .= "RRA:MAX:0.5:60:1860 "; + $rrdcreate .= "RRA:MAX:0.5:1440:2284 "; + $rrdcreate .= "RRA:LAST:0.5:1:1200 "; + $rrdcreate .= "RRA:LAST:0.5:5:720 "; + $rrdcreate .= "RRA:LAST:0.5:60:1860 "; + $rrdcreate .= "RRA:LAST:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $loggedin_filename N:U"); + } + + /* the Captive Portal stats gathering function. */ + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "# polling Captive Portal for number of logged in users\n"; + $rrdupdatesh .= "CP=`${php} -q ${captiveportal_gather} '${cpkey}' 'loggedin'`\n"; + $rrdupdatesh .= "$rrdtool update $loggedin_filename \${CP}\n"; + + } + } + /* End Captive Portal statistics */ + + /* NTP, set up the ntpd rrd file */ + if (isset($config['ntpd']['statsgraph'])) { + /* set up the ntpd rrd file */ + if (!file_exists("$rrddbpath$ntpd")) { + $rrdcreate = "$rrdtool create $rrddbpath$ntpd --step $rrdntpdinterval "; + $rrdcreate .= "DS:offset:GAUGE:$ntpdvalid:0:1000 "; + $rrdcreate .= "DS:sjit:GAUGE:$ntpdvalid:0:1000 "; + $rrdcreate .= "DS:cjit:GAUGE:$ntpdvalid:0:1000 "; + $rrdcreate .= "DS:wander:GAUGE:$ntpdvalid:0:1000 "; + $rrdcreate .= "DS:freq:GAUGE:$ntpdvalid:0:1000 "; + $rrdcreate .= "DS:disp:GAUGE:$ntpdvalid:0:1000 "; + $rrdcreate .= "RRA:MIN:0.5:1:1200 "; + $rrdcreate .= "RRA:MIN:0.5:5:720 "; + $rrdcreate .= "RRA:MIN:0.5:60:1860 "; + $rrdcreate .= "RRA:MIN:0.5:1440:2284 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + $rrdcreate .= "RRA:MAX:0.5:1:1200 "; + $rrdcreate .= "RRA:MAX:0.5:5:720 "; + $rrdcreate .= "RRA:MAX:0.5:60:1860 "; + $rrdcreate .= "RRA:MAX:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + mwexec("$rrdtool update $rrddbpath$ntpd N:U:U:U:U:U:U"); + } + + /* the ntp stats gathering function. */ + $rrdupdatesh .= "\n"; + $rrdupdatesh .= "$ntpq -c rv | $awk 'BEGIN{ RS=\",\"}{ print }' >> /tmp/ntp-rrdstats.$$\n"; + $rrdupdatesh .= "NOFFSET=`grep offset /tmp/ntp-rrdstats.$$ | awk 'BEGIN{FS=\"=\"}{print $2}'`\n"; + $rrdupdatesh .= "NFREQ=`grep frequency /tmp/ntp-rrdstats.$$ | awk 'BEGIN{FS=\"=\"}{print $2}'`\n"; + $rrdupdatesh .= "NSJIT=`grep sys_jitter /tmp/ntp-rrdstats.$$ | awk 'BEGIN{FS=\"=\"}{print $2}'`\n"; + $rrdupdatesh .= "NCJIT=`grep clk_jitter /tmp/ntp-rrdstats.$$ | awk 'BEGIN{FS=\"=\"}{print $2}'`\n"; + $rrdupdatesh .= "NWANDER=`grep clk_wander /tmp/ntp-rrdstats.$$ | awk 'BEGIN{FS=\"=\"}{print $2}'`\n"; + $rrdupdatesh .= "NDISPER=`grep rootdisp /tmp/ntp-rrdstats.$$ | awk 'BEGIN{FS=\"=\"}{print $2}'`\n"; + $rrdupdatesh .= "$rrdtool update $rrddbpath$ntpd \N:\${NOFFSET}:\${NSJIT}:\${NCJIT}:\${NWANDER}:\${NFREQ}:\${NDISPER}\n"; + $rrdupdatesh .= "rm /tmp/ntp-rrdstats.$$\n"; + $rrdupdatesh .= "\n"; + + } + /* End NTP statistics */ + + $rrdupdatesh .= "sleep 60\n"; + $rrdupdatesh .= "done\n"; + log_error(gettext("Creating rrd update script")); + /* write the rrd update script */ + $updaterrdscript = "{$g['vardb_path']}/rrd/updaterrd.sh"; + $fd = fopen("$updaterrdscript", "w"); + fwrite($fd, "$rrdupdatesh"); + fclose($fd); + + unset($rrdupdatesh); + + /* kill off traffic collectors */ + kill_traffic_collector(); + + /* start traffic collector */ + mwexec_bg("/usr/bin/nice -n20 /bin/sh $updaterrdscript"); + + } else { + /* kill off traffic collectors */ + kill_traffic_collector(); + } + + $databases = glob("{$rrddbpath}/*.rrd"); + foreach ($databases as $database) { + chown($database, "nobody"); + } + + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + +} + +# Create gateway quality RRD with settings suitable for pfSense graph set. +function create_gateway_quality_rrd($rrd_file) { + global $g; + + $rrdinterval = 60; + $valid = $rrdinterval * 2; + $rrdtool = "/usr/bin/nice -n20 /usr/local/bin/rrdtool"; + + /* GATEWAY QUALITY, set up the rrd file */ + if (!file_exists("$rrd_file")) { + $rrdcreate = "$rrdtool create $rrd_file --step $rrdinterval "; + $rrdcreate .= "DS:loss:GAUGE:$valid:0:100 "; + $rrdcreate .= "DS:delay:GAUGE:$valid:0:100000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1200 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:720 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1860 "; + $rrdcreate .= "RRA:AVERAGE:0.5:1440:2284 "; + + create_new_rrd($rrdcreate); + unset($rrdcreate); + } + + /* enter UNKNOWN values in the RRD so it knows we rebooted. */ + if (platform_booting()) { + if (!is_dir("{$g['vardb_path']}/rrd")) { + mkdir("{$g['vardb_path']}/rrd", 0775); + } + + @chown("{$g['vardb_path']}/rrd", "nobody"); + + mwexec("$rrdtool update $rrd_file N:U:U"); + } + unset($rrdtool, $rrdinterval, $valid, $rrd_file); +} + +function kill_traffic_collector() { + global $g; + + killbypid("{$g['varrun_path']}/updaterrd.sh.pid"); +} + +?> diff --git a/src/etc/inc/sasl.inc b/src/etc/inc/sasl.inc new file mode 100644 index 0000000..a9582da --- /dev/null +++ b/src/etc/inc/sasl.inc @@ -0,0 +1,422 @@ +<?php +/* + * sasl.php + * + * @(#) $Id: sasl.php,v 1.11 2005/10/31 18:43:27 mlemos Exp $ + * + */ + +define("SASL_INTERACT", 2); +define("SASL_CONTINUE", 1); +define("SASL_OK", 0); +define("SASL_FAIL", -1); +define("SASL_NOMECH", -4); + +class sasl_interact_class +{ + var $id; + var $challenge; + var $prompt; + var $default_result; + var $result; +}; + +/* +{metadocument}<?xml version="1.0" encoding="ISO-8859-1" ?> +<class> + + <package>net.manuellemos.sasl</package> + + <version>@(#) $Id: sasl.php,v 1.11 2005/10/31 18:43:27 mlemos Exp $</version> + <copyright>Copyright © (C) Manuel Lemos 2004</copyright> + <title>Simple Authentication and Security Layer client</title> + <author>Manuel Lemos</author> + <authoraddress>mlemos-at-acm.org</authoraddress> + + <documentation> + <idiom>en</idiom> + <purpose>Provide a common interface to plug-in driver classes that + implement different mechanisms for authentication used by clients of + standard protocols like SMTP, POP3, IMAP, HTTP, etc.. Currently the + supported authentication mechanisms are: <tt>PLAIN</tt>, + <tt>LOGIN</tt>, <tt>CRAM-MD5</tt>, <tt>Digest</tt> and <tt>NTML</tt> + (Windows or Samba).</purpose> + <usage>.</usage> + </documentation> + +{/metadocument} +*/ + +class sasl_client_class +{ + /* Public variables */ + +/* +{metadocument} + <variable> + <name>error</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Store the message that is returned when an error + occurs.</purpose> + <usage>Check this variable to understand what happened when a call to + any of the class functions has failed.<paragraphbreak /> + This class uses cumulative error handling. This means that if one + class functions that may fail is called and this variable was + already set to an error message due to a failure in a previous call + to the same or other function, the function will also fail and does + not do anything.<paragraphbreak /> + This allows programs using this class to safely call several + functions that may fail and only check the failure condition after + the last function call.<paragraphbreak /> + Just set this variable to an empty string to clear the error + condition.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $error=''; + +/* +{metadocument} + <variable> + <name>mechanism</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Store the name of the mechanism that was selected during the + call to the <functionlink>Start</functionlink> function.</purpose> + <usage>You can access this variable but do not change it.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $mechanism=''; + +/* +{metadocument} + <variable> + <name>encode_response</name> + <type>BOOLEAN</type> + <value>1</value> + <documentation> + <purpose>Let the drivers inform the applications whether responses + need to be encoded.</purpose> + <usage>Applications should check this variable before sending + authentication responses to the server to determine if the + responses need to be encoded, eventually with base64 algorithm.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $encode_response=1; + + /* Private variables */ + + var $driver; + var $drivers=array( + "Digest" => array("digest_sasl_client_class", "digest_sasl_client.inc" ), + "CRAM-MD5" => array("cram_md5_sasl_client_class", "cram_md5_sasl_client.inc" ), + "LOGIN" => array("login_sasl_client_class", "login_sasl_client.inc" ), + "NTLM" => array("ntlm_sasl_client_class", "ntlm_sasl_client.inc" ), + "PLAIN" => array("plain_sasl_client_class", "plain_sasl_client.inc" ), + "Basic" => array("basic_sasl_client_class", "basic_sasl_client.inc" ) + ); + var $credentials=array(); + + /* Public functions */ + +/* +{metadocument} + <function> + <name>SetCredential</name> + <type>VOID</type> + <documentation> + <purpose>Store the value of a credential that may be used by any of + the supported mechanisms to process the authentication messages and + responses.</purpose> + <usage>Call this function before starting the authentication dialog + to pass all the credential values that be needed to use the type + of authentication that the applications may need.</usage> + <returnvalue>.</returnvalue> + </documentation> + <argument> + <name>key</name> + <type>STRING</type> + <documentation> + <purpose>Specify the name of the credential key.</purpose> + </documentation> + </argument> + <argument> + <name>value</name> + <type>STRING</type> + <documentation> + <purpose>Specify the value for the credential.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function SetCredential($key,$value) + { + $this->credentials[$key]=$value; + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>GetCredentials</name> + <type>INTEGER</type> + <documentation> + <purpose>Retrieve the values of one or more credentials to be used by + the authentication mechanism classes.</purpose> + <usage>This is meant to be used by authentication mechanism driver + classes to retrieve the credentials that may be needed.</usage> + <returnvalue>The function may return <tt>SASL_CONTINUE</tt> if it + succeeded, or <tt>SASL_NOMECH</tt> if it was not possible to + retrieve one of the requested credentials.</returnvalue> + </documentation> + <argument> + <name>credentials</name> + <type>HASH</type> + <documentation> + <purpose>Reference to an associative array variable with all the + credentials that are being requested. The function initializes + this associative array values.</purpose> + </documentation> + </argument> + <argument> + <name>defaults</name> + <type>HASH</type> + <documentation> + <purpose>Associative arrays with default values for credentials + that may have not been defined.</purpose> + </documentation> + </argument> + <argument> + <name>interactions</name> + <type>ARRAY</type> + <documentation> + <purpose>Not yet in use. It is meant to provide context + information to retrieve credentials that may be obtained + interacting with the user.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function GetCredentials(&$credentials,$defaults,&$interactions) + { + Reset($credentials); + $end=(GetType($key=Key($credentials))!="string"); + for(;!$end;) + { + if(!IsSet($this->credentials[$key])) + { + if(IsSet($defaults[$key])) + $credentials[$key]=$defaults[$key]; + else + { + $this->error="the requested credential ".$key." is not defined"; + return(SASL_NOMECH); + } + } + else + $credentials[$key]=$this->credentials[$key]; + Next($credentials); + $end=(GetType($key=Key($credentials))!="string"); + } + return(SASL_CONTINUE); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>Start</name> + <type>INTEGER</type> + <documentation> + <purpose>Process the initial authentication step initializing the + driver class that implements the first of the list of requested + mechanisms that is supported by this SASL client library + implementation.</purpose> + <usage>Call this function specifying a list of mechanisms that the + server supports. If the <argumentlink> + <argument>message</argument> + <function>Start</function> + </argumentlink> argument returns a string, it should be sent to + the server as initial message. Check the + <variablelink>encode_response</variablelink> variable to determine + whether the initial message needs to be encoded, eventually with + base64 algorithm, before it is sent to the server.</usage> + <returnvalue>The function may return <tt>SASL_CONTINUE</tt> if it + could start one of the requested authentication mechanisms. It + may return <tt>SASL_NOMECH</tt> if it was not possible to start + any of the requested mechanisms. It returns <tt>SASL_FAIL</tt> or + other value in case of error.</returnvalue> + </documentation> + <argument> + <name>mechanisms</name> + <type>ARRAY</type> + <inout /> + <documentation> + <purpose>Define the list of names of authentication mechanisms + supported by the that should be tried.</purpose> + </documentation> + </argument> + <argument> + <name>message</name> + <type>STRING</type> + <out /> + <documentation> + <purpose>Return the initial message that should be sent to the + server to start the authentication dialog. If this value is + undefined, no message should be sent to the server.</purpose> + </documentation> + </argument> + <argument> + <name>interactions</name> + <type>ARRAY</type> + <documentation> + <purpose>Not yet in use. It is meant to provide context + information to interact with the end user.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function Start($mechanisms, &$message, &$interactions) + { + if(strlen($this->error)) + return(SASL_FAIL); + if(IsSet($this->driver)) + return($this->driver->Start($this,$message,$interactions)); + $no_mechanism_error=""; + for($m=0;$m<count($mechanisms);$m++) + { + $mechanism=$mechanisms[$m]; + if(IsSet($this->drivers[$mechanism])) + { + if(!class_exists($this->drivers[$mechanism][0])) + require(dirname(__FILE__)."/".$this->drivers[$mechanism][1]); + $this->driver=new $this->drivers[$mechanism][0]; + if($this->driver->Initialize($this)) + { + $this->encode_response=1; + $status=$this->driver->Start($this,$message,$interactions); + switch($status) + { + case SASL_NOMECH: + Unset($this->driver); + if(strlen($no_mechanism_error)==0) + $no_mechanism_error=$this->error; + $this->error=""; + break; + case SASL_CONTINUE: + $this->mechanism=$mechanism; + return($status); + default: + Unset($this->driver); + $this->error=""; + return($status); + } + } + else + { + Unset($this->driver); + if(strlen($no_mechanism_error)==0) + $no_mechanism_error=$this->error; + $this->error=""; + } + } + } + $this->error=(strlen($no_mechanism_error) ? $no_mechanism_error : "it was not requested any of the authentication mechanisms that are supported"); + return(SASL_NOMECH); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>Step</name> + <type>INTEGER</type> + <documentation> + <purpose>Process the authentication steps after the initial step, + until the authentication iteration dialog is complete.</purpose> + <usage>Call this function iteratively after a successful initial + step calling the <functionlink>Start</functionlink> function.</usage> + <returnvalue>The function returns <tt>SASL_CONTINUE</tt> if step was + processed successfully, or returns <tt>SASL_FAIL</tt> in case of + error.</returnvalue> + </documentation> + <argument> + <name>response</name> + <type>STRING</type> + <in /> + <documentation> + <purpose>Pass the response returned by the server to the previous + step.</purpose> + </documentation> + </argument> + <argument> + <name>message</name> + <type>STRING</type> + <out /> + <documentation> + <purpose>Return the message that should be sent to the server to + continue the authentication dialog. If this value is undefined, + no message should be sent to the server.</purpose> + </documentation> + </argument> + <argument> + <name>interactions</name> + <type>ARRAY</type> + <documentation> + <purpose>Not yet in use. It is meant to provide context + information to interact with the end user.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function Step($response, &$message, &$interactions) + { + if(strlen($this->error)) + return(SASL_FAIL); + return($this->driver->Step($this,$response,$message,$interactions)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +}; + +/* + +{metadocument} +</class> +{/metadocument} + +*/ + +?> diff --git a/src/etc/inc/service-utils.inc b/src/etc/inc/service-utils.inc new file mode 100644 index 0000000..2fa75cf --- /dev/null +++ b/src/etc/inc/service-utils.inc @@ -0,0 +1,751 @@ +<?php +/****h* pfSense/service-utils + NAME + service-utils.inc - Service facility + DESCRIPTION + This file contains various functions used by the pfSense service facility. + HISTORY + $Id$ + + Copyright (C) 2005-2006 Colin Smith (ethethlay@gmail.com) + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ + +/* + pfSense_BUILDER_BINARIES: /bin/pgrep /bin/sh /usr/bin/killall + pfSense_MODULE: utils +*/ +require_once("globals.inc"); +require_once("captiveportal.inc"); +require_once("openvpn.inc"); +require_once("ipsec.inc"); +require_once("vpn.inc"); +require_once("vslb.inc"); +require_once("gwlb.inc"); + +define("RCFILEPREFIX", "/usr/local/etc/rc.d/"); +function write_rcfile($params) { + global $g; + + safe_mkdir(RCFILEPREFIX); + $rcfile_fullname = RCFILEPREFIX . $params['file']; + if (!file_exists($rcfile_fullname) && !is_link($rcfile_fullname) && !touch($rcfile_fullname)) { + return false; + } + + if (!is_writable($rcfile_fullname) || empty($params['start'])) { + return false; + } + + $towrite = "#!/bin/sh\n"; + $towrite .= "# This file was automatically generated\n# by the {$g['product_name']} service handler.\n\n"; + + /* write our rc functions */ + $towrite .= "rc_start() {\n"; + $towrite .= "\t{$params['start']}\n"; + $towrite .= "}\n\n"; + if (!empty($params['stop'])) { + $tokill =& $params['stop']; + } else if (!empty($params['executable'])) { + /* just nuke the executable */ + $tokill = "/usr/bin/killall " . escapeshellarg($params['executable']); + } else { + /* make an educated guess (bad) */ + $tokill = array_pop(explode('/', array_shift(explode(' ', $params['start'])))); + } + $towrite .= "rc_stop() {\n"; + $towrite .= "\t{$tokill}\n"; + $towrite .= "}\n\n"; + + /* begin rcfile logic */ + $towrite .= "case \$1 in\n\tstart)\n\t\trc_start\n\t\t;;\n\tstop)\n\t\trc_stop\n\t\t;;\n\trestart)\n\t\trc_stop\n\t\trc_start\n\t\t;;\nesac\n\n"; + + @file_put_contents($rcfile_fullname, $towrite); + unset($towrite); + @chmod("{$rcfile_fullname}", 0755); + + return; +} + +function start_service($name) { + global $config; + + if (empty($name)) { + return; + } + + if (is_array($config['installedpackages']) && is_array($config['installedpackages']['service'])) { + foreach ($config['installedpackages']['service'] as $service) { + if (strtolower($service['name']) == strtolower($name)) { + if ($service['rcfile']) { + $prefix = RCFILEPREFIX; + if (!empty($service['prefix'])) { + $prefix =& $service['prefix']; + } + if (file_exists("{$prefix}{$service['rcfile']}") || is_link("{$prefix}{$service['rcfile']}")) { + mwexec_bg("{$prefix}{$service['rcfile']} start"); + } + } + if (!empty($service['startcmd'])) { + eval($service['startcmd']); + } + break; + } + } + } +} + +function stop_service($name) { + global $config; + + if (empty($name)) { + return; + } + + if (is_array($config['installedpackages']) && is_array($config['installedpackages']['service'])) { + foreach ($config['installedpackages']['service'] as $service) { + if (strtolower($service['name']) == strtolower($name)) { + if ($service['rcfile']) { + $prefix = RCFILEPREFIX; + if (!empty($service['prefix'])) { + $prefix =& $service['prefix']; + } + if (file_exists("{$prefix}{$service['rcfile']}") || is_link("{$prefix}{$service['rcfile']}")) { + mwexec("{$prefix}{$service['rcfile']} stop"); + } + return; + } + if (!empty($service['stopcmd'])) { + eval($service['stopcmd']); + } + + break; + } + } + } +} + +function restart_service($name) { + global $config; + + if (empty($name)) { + return; + } + + stop_service($name); + start_service($name); + + if (is_array($config['installedpackages']) && is_array($config['installedpackages']['service'])) { + foreach ($config['installedpackages']['service'] as $service) { + if (strtolower($service['name']) == strtolower($name)) { + if ($service['restartcmd']) { + eval($service['restartcmd']); + } + break; + } + } + } +} + +function is_pid_running($pidfile) { + if (!file_exists($pidfile)) { + return false; + } + + return (isvalidpid($pidfile)); +} + +function is_dhcp_running($interface) { + $status = find_dhclient_process($interface); + if ($status != 0) { + return true; + } + return false; +} + +function restart_service_if_running($service) { + global $config; + if (is_service_running($service)) { + restart_service($service); + } + return; +} + +function is_service_enabled($service_name) { + global $config; + if ($service_name == "") { + return false; + } + if (is_array($config['installedpackages'])) { + if (isset($config['installedpackages'][$service_name]['config'][0]['enable']) && + ((empty($config['installedpackages'][$service_name]['config'][0]['enable'])) || + ($config['installedpackages'][$service_name]['config'][0]['enable'] === 'off'))) { + return false; + } + } + return true; +} + +function is_service_running($service, $ps = "") { + global $config; + + if (is_array($config['installedpackages']['service'])) { + foreach ($config['installedpackages']['service'] as $aservice) { + if (strtolower($service) == strtolower($aservice['name'])) { + if ($aservice['custom_php_service_status_command'] <> "") { + eval("\$rc={$aservice['custom_php_service_status_command']};"); + return $rc; + } + if (empty($aservice['executable'])) { + return false; + } + if (is_process_running($aservice['executable'])) { + return true; + } + + return false; + } + } + } + + if (is_process_running($service)) { + return true; + } + + return false; +} + +function get_services() { + global $config; + if (is_array($config['installedpackages']['service'])) { + $services = $config['installedpackages']['service']; + } else { + $services = array(); + } + + /* + * Add services that are in the base. + */ + if (is_radvd_enabled()) { + $pconfig = array(); + $pconfig['name'] = "radvd"; + $pconfig['description'] = gettext("Router Advertisement Daemon"); + $services[] = $pconfig; + } + + if (isset($config['dnsmasq']['enable'])) { + $pconfig = array(); + $pconfig['name'] = "dnsmasq"; + $pconfig['description'] = gettext("DNS Forwarder"); + $services[] = $pconfig; + } + + if (isset($config['unbound']['enable'])) { + $pconfig = array(); + $pconfig['name'] = "unbound"; + $pconfig['description'] = gettext("DNS Resolver"); + $services[] = $pconfig; + } + + $pconfig = array(); + $pconfig['name'] = "ntpd"; + $pconfig['description'] = gettext("NTP clock sync"); + $services[] = $pconfig; + + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $zone => $setting) { + if (isset($setting['enable'])) { + $pconfig = array(); + $pconfig['name'] = "captiveportal"; + $pconfig['zone'] = $zone; + $pconfig['description'] = gettext("Captive Portal") . ": ".htmlspecialchars($setting['zone']); + $services[] = $pconfig; + } + } + } + + $iflist = array(); + $ifdescrs = get_configured_interface_list(); + foreach ($ifdescrs as $if) { + $oc = $config['interfaces'][$if]; + if ($oc['if'] && (!link_interface_to_bridge($if))) { + $iflist[$if] = $if; + } + } + + if (isset($config['dhcrelay']['enable'])) { + $pconfig = array(); + $pconfig['name'] = "dhcrelay"; + $pconfig['description'] = gettext("DHCP Relay"); + $services[] = $pconfig; + } + + if (isset($config['dhcrelay6']['enable'])) { + $pconfig = array(); + $pconfig['name'] = "dhcrelay6"; + $pconfig['description'] = gettext("DHCPv6 Relay"); + $services[] = $pconfig; + } + + if (is_dhcp_server_enabled()) { + $pconfig = array(); + $pconfig['name'] = "dhcpd"; + $pconfig['description'] = gettext("DHCP Service"); + $services[] = $pconfig; + } + + $gateways_arr = return_gateways_array(); + if (is_array($gateways_arr)) { + $pconfig = array(); + $pconfig['name'] = "apinger"; + $pconfig['description'] = gettext("Gateway Monitoring Daemon"); + $services[] = $pconfig; + } + + if (isset($config['snmpd']['enable'])) { + $pconfig = array(); + $pconfig['name'] = "bsnmpd"; + $pconfig['description'] = gettext("SNMP Service"); + $services[] = $pconfig; + } + + if (is_array($config['igmpproxy']['igmpentry']) && (count($config['igmpproxy']['igmpentry']) > 0)) { + $pconfig = array(); + $pconfig['name'] = "igmpproxy"; + $pconfig['description'] = gettext("IGMP proxy"); + $services[] = $pconfig; + } + + if (isset($config['installedpackages']['miniupnpd']) && $config['installedpackages']['miniupnpd']['config'][0]['enable']) { + $pconfig = array(); + $pconfig['name'] = "miniupnpd"; + $pconfig['description'] = gettext("UPnP Service"); + $services[] = $pconfig; + } + + if (isset($config['installedpackages']['routed']) && $config['installedpackages']['routed']['config'][0]['enable']) { + $pconfig = array(); + $pconfig['name'] = "routed"; + $pconfig['description'] = gettext("RIP Daemon"); + $services[] = $pconfig; + } + + if (isset($config['ipsec']['enable'])) { + $pconfig = array(); + $pconfig['name'] = "ipsec"; + $pconfig['description'] = gettext("IPsec VPN"); + $services[] = $pconfig; + } + + if (isset($config['system']['enablesshd'])) { + $pconfig = array(); + $pconfig['name'] = "sshd"; + $pconfig['description'] = gettext("Secure Shell Daemon"); + $services[] = $pconfig; + } + + foreach (array('server', 'client') as $mode) { + if (is_array($config['openvpn']["openvpn-{$mode}"])) { + foreach ($config['openvpn']["openvpn-{$mode}"] as $id => $setting) { + if (!isset($setting['disable'])) { + $pconfig = array(); + $pconfig['name'] = "openvpn"; + $pconfig['mode'] = $mode; + $pconfig['id'] = $id; + $pconfig['vpnid'] = $setting['vpnid']; + $pconfig['description'] = gettext("OpenVPN") . " ".$mode.": ".htmlspecialchars($setting['description']); + $services[] = $pconfig; + } + } + } + } + + if (count($config['load_balancer']['virtual_server']) && count($config['load_balancer']['lbpool'])) { + $pconfig = array(); + $pconfig['name'] = "relayd"; + $pconfig['description'] = gettext("Server load balancing daemon"); + $services[] = $pconfig; + } + return $services; +} + +function find_service_by_name($name) { + $services = get_services(); + foreach ($services as $service) { + if ($service["name"] == $name) { + return $service; + } + } + return array(); +} + +function find_service_by_openvpn_vpnid($vpnid) { + $services = get_services(); + foreach ($services as $service) { + if (($service["name"] == "openvpn") && isset($service["vpnid"]) && ($service["vpnid"] == $vpnid)) { + return $service; + } + } + return array(); +} + +function find_service_by_cp_zone($zone) { + $services = get_services(); + foreach ($services as $service) { + if (($service["name"] == "captiveportal") && isset($service["zone"]) && ($service["zone"] == $zone)) { + return $service; + } + } + return array(); +} + +function service_name_compare($a, $b) { + if (strtolower($a['name']) == strtolower($b['name'])) { + return 0; + } + return (strtolower($a['name']) < strtolower($b['name'])) ? -1 : 1; +} + +function get_pkg_descr($package_name) { + global $config; + if (is_array($config['installedpackages']['package'])) { + foreach ($config['installedpackages']['package'] as $pkg) { + if ($pkg['name'] == $package_name) { + return $pkg['descr']; + } + } + } + return gettext("Not available."); +} + +function get_service_status($service) { + global $g; + switch ($service['name']) { + case "openvpn": + $running = is_pid_running("{$g['varrun_path']}/openvpn_{$service['mode']}{$service['vpnid']}.pid"); + break; + case "captiveportal": + $running = is_pid_running("{$g['varrun_path']}/lighty-{$service['zone']}-CaptivePortal.pid"); + if (isset($config['captiveportal'][$service['zone']]['httpslogin'])) { + $running = $running && is_pid_running("{$g['varrun_path']}/lighty-{$service['zone']}-CaptivePortal-SSL.pid"); + } + break; + case "vhosts-http": + $running = is_pid_running("{$g['varrun_path']}/vhosts-http.pid"); + break; + case "dhcrelay6": + $running = is_pid_running("{$g['varrun_path']}/dhcrelay6.pid"); + break; + case 'ipsec': + $running = is_pid_running("{$g['varrun_path']}/charon.pid"); + break; + default: + $running = is_service_running($service['name']); + } + return $running; +} + +function get_service_status_icon($service, $withtext = true, $smallicon = false) { + global $g; + $output = ""; + if (get_service_status($service)) { + $statustext = gettext("Running"); + $output .= "<img style=\"vertical-align:middle\" title=\"" . sprintf(gettext("%s Service is"), $service["name"]) . " {$statustext}\" src=\"/themes/" . $g["theme"] . "/images/icons/"; + $output .= ($smallicon) ? "icon_pass.gif" : "icon_service_running.gif"; + $output .= "\" alt=\"status\" /> "; + if ($withtext) { + $output .= " " . $statustext; + } + } else { + $service_enabled = is_service_enabled($service['name']); + $statustext = ($service_enabled) ? gettext("Stopped") : gettext("Disabled"); + $output .= "<img style=\"vertical-align:middle\" title=\"" . sprintf(gettext("%s Service is"), $service["name"]) . " {$statustext}\" src=\"/themes/" . $g["theme"] . "/images/icons/"; + $output .= ($smallicon) ? "icon_block.gif" : "icon_service_stopped.gif"; + $output .= "\" alt=\"status\" /> "; + if ($withtext) { + $output .= " <font color=\"white\">{$statustext}</font>"; + } + } + return $output; +} + +function get_service_control_links($service, $addname = false) { + $output = ""; + $stitle = ($addname) ? $service['name'] . " " : ""; + + switch ($service['name']) { + case "openvpn": + $link = '<a title="%s" href="status_services.php?mode=%s&service='.$service['name'].'.&vpnmode='.$service['mode'].'.&id='.$service['vpnid'].'">'; + break; + case "captiveportal": + $link = '<a title="%s" href="status_services.php?mode=%s&service='.$service['name'].'.&zone='.$service['zone'].'">'; + break; + default: + $link = '<a title="%s" href="status_services.php?mode=%s&service='.$service['name'].'">'; + } + + if (get_service_status($service)) { + switch ($service['name']) { + case "openvpn": + $output .= "<a href='status_services.php?mode=restartservice&service={$service['name']}&vpnmode={$service['mode']}&id={$service['vpnid']}'>"; + break; + case "captiveportal": + $output .= "<a href='status_services.php?mode=restartservice&service={$service['name']}&zone={$service['zone']}'>"; + break; + default: + $output .= "<a href='status_services.php?mode=restartservice&service={$service['name']}'>"; + } + $output .= "<img style=\"vertical-align:middle\" title='" . sprintf(gettext("Restart %sService"), $stitle) . "' border='0' src='/themes/".$g['theme']."/images/icons/icon_service_restart.gif' alt='restart' /></a>\n"; + switch ($service['name']) { + case "openvpn": + $output .= "<a href='status_services.php?mode=stopservice&service={$service['name']}&vpnmode={$service['mode']}&id={$service['vpnid']}'>"; + break; + case "captiveportal": + $output .= "<a href='status_services.php?mode=stopservice&service={$service['name']}&zone={$service['zone']}'>"; + break; + default: + $output .= "<a href='status_services.php?mode=stopservice&service={$service['name']}'>"; + } + $output .= "<img style=\"vertical-align:middle\" title='" . sprintf(gettext("Stop %sService"), $stitle) . "' border='0' src='/themes/".$g['theme']."/images/icons/icon_service_stop.gif' alt='stop' />"; + $output .= "</a>"; + } else { + $service_enabled = is_service_enabled($service['name']); + + if ($service['name'] == 'openvpn' || $service['name'] == 'captiveportal' || $service_enabled) { + $output .= sprintf($link, sprintf(gettext("Start %sService"), $stitle), 'startservice'); + $output .= '<i class="icon icon-play-circle"></i></a> '; + } + } + + return $output; +} + +function service_control_start($name, $extras) { + global $g; + switch ($name) { + case 'radvd': + services_radvd_configure(); + break; + case 'captiveportal': + $zone = htmlspecialchars($extras['zone']); + captiveportal_init_webgui_zonename($zone); + break; + case 'ntpd': + case 'openntpd': + system_ntp_configure(); + break; + case 'apinger': + setup_gateways_monitor(); + break; + case 'bsnmpd': + services_snmpd_configure(); + break; + case 'dhcrelay': + services_dhcrelay_configure(); + break; + case 'dhcrelay6': + services_dhcrelay6_configure(); + break; + case 'dnsmasq': + services_dnsmasq_configure(); + break; + case 'unbound': + services_unbound_configure(); + break; + case 'dhcpd': + services_dhcpd_configure(); + break; + case 'igmpproxy': + services_igmpproxy_configure(); + break; + case 'miniupnpd': + upnp_action('start'); + break; + case 'ipsec': + vpn_ipsec_force_reload(); + break; + case 'sshd': + send_event("service restart sshd"); + break; + case 'openvpn': + $vpnmode = isset($extras['vpnmode']) ? htmlspecialchars($extras['vpnmode']) : htmlspecialchars($extras['mode']); + if (($vpnmode == "server") || ($vpnmode == "client")) { + $id = isset($extras['vpnid']) ? htmlspecialchars($extras['vpnid']) : htmlspecialchars($extras['id']); + $configfile = "{$g['varetc_path']}/openvpn/{$vpnmode}{$id}.conf"; + if (file_exists($configfile)) { + openvpn_restart_by_vpnid($vpnmode, $id); + } + } + break; + case 'relayd': + relayd_configure(); + break; + default: + start_service($name); + break; + } + return sprintf(gettext("%s has been started."), htmlspecialchars($name)); +} +function service_control_stop($name, $extras) { + global $g; + switch ($name) { + case 'radvd': + killbypid("{$g['varrun_path']}/radvd.pid"); + break; + case 'captiveportal': + $zone = htmlspecialchars($extras['zone']); + killbypid("{$g['varrun_path']}/lighty-{$zone}-CaptivePortal.pid"); + killbypid("{$g['varrun_path']}/lighty-{$zone}-CaptivePortal-SSL.pid"); + break; + case 'ntpd': + killbyname("ntpd"); + break; + case 'openntpd': + killbyname("openntpd"); + break; + case 'apinger': + killbypid("{$g['varrun_path']}/apinger.pid"); + break; + case 'bsnmpd': + killbypid("{$g['varrun_path']}/snmpd.pid"); + break; + case 'choparp': + killbyname("choparp"); + break; + case 'dhcpd': + killbyname("dhcpd"); + break; + case 'dhcrelay': + killbypid("{$g['varrun_path']}/dhcrelay.pid"); + break; + case 'dhcrelay6': + killbypid("{$g['varrun_path']}/dhcrelay6.pid"); + break; + case 'dnsmasq': + killbypid("{$g['varrun_path']}/dnsmasq.pid"); + break; + case 'unbound': + killbypid("{$g['varrun_path']}/unbound.pid"); + break; + case 'igmpproxy': + killbyname("igmpproxy"); + break; + case 'miniupnpd': + upnp_action('stop'); + break; + case 'sshd': + killbyname("sshd"); + break; + case 'ipsec': + exec("/usr/local/sbin/ipsec stop"); + break; + case 'openvpn': + $vpnmode = htmlspecialchars($extras['vpnmode']); + if (($vpnmode == "server") or ($vpnmode == "client")) { + $id = htmlspecialchars($extras['id']); + $pidfile = "{$g['varrun_path']}/openvpn_{$vpnmode}{$id}.pid"; + killbypid($pidfile); + } + break; + case 'relayd': + mwexec('pkill relayd'); + break; + default: + stop_service($name); + break; + } + return sprintf(gettext("%s has been stopped."), htmlspecialchars($name)); +} + +function service_control_restart($name, $extras) { + global $g; + switch ($name) { + case 'radvd': + services_radvd_configure(); + break; + case 'captiveportal': + $zone = htmlspecialchars($extras['zone']); + killbypid("{$g['varrun_path']}/lighty-{$zone}-CaptivePortal.pid"); + killbypid("{$g['varrun_path']}/lighty-{$zone}-CaptivePortal-SSL.pid"); + captiveportal_init_webgui_zonename($zone); + break; + case 'ntpd': + case 'openntpd': + system_ntp_configure(); + break; + case 'apinger': + killbypid("{$g['varrun_path']}/apinger.pid"); + setup_gateways_monitor(); + break; + case 'bsnmpd': + services_snmpd_configure(); + break; + case 'dhcrelay': + services_dhcrelay_configure(); + break; + case 'dhcrelay6': + services_dhcrelay6_configure(); + break; + case 'dnsmasq': + services_dnsmasq_configure(); + break; + case 'unbound': + services_unbound_configure(); + break; + case 'dhcpd': + services_dhcpd_configure(); + break; + case 'igmpproxy': + services_igmpproxy_configure(); + break; + case 'miniupnpd': + upnp_action('restart'); + break; + case 'ipsec': + vpn_ipsec_force_reload(); + break; + case 'sshd': + send_event("service restart sshd"); + break; + case 'openvpn': + $vpnmode = htmlspecialchars($extras['vpnmode']); + if ($vpnmode == "server" || $vpnmode == "client") { + $id = htmlspecialchars($extras['id']); + $configfile = "{$g['varetc_path']}/openvpn/{$vpnmode}{$id}.conf"; + if (file_exists($configfile)) { + openvpn_restart_by_vpnid($vpnmode, $id); + } + } + break; + case 'relayd': + relayd_configure(true); + break; + default: + restart_service($name); + break; + } + return sprintf(gettext("%s has been restarted."), htmlspecialchars($name)); +} + +?> diff --git a/src/etc/inc/services.inc b/src/etc/inc/services.inc new file mode 100644 index 0000000..333261d --- /dev/null +++ b/src/etc/inc/services.inc @@ -0,0 +1,2535 @@ +<?php +/* + services.inc + part of the pfSense project (https://www.pfsense.org) + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + Copyright (C) 2010 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/killall /bin/pgrep /bin/sh /usr/local/sbin/dhcpd /usr/local/sbin/igmpproxy + pfSense_BUILDER_BINARIES: /sbin/ifconfig /usr/local/sbin/dnsmasq + pfSense_BUILDER_BINARIES: /usr/local/sbin/miniupnpd /usr/sbin/radvd + pfSense_BUILDER_BINARIES: /usr/local/sbin/dhcleases6 /usr/sbin/bsnmpd + pfSense_MODULE: utils +*/ + +define('DYNDNS_PROVIDER_VALUES', 'citynetwork cloudflare custom custom-v6 dhs dnsexit dnsimple dnsmadeeasy dnsomatic dyndns dyndns-custom dyndns-static dyns easydns eurodns freedns glesys googledomains gratisdns he-net he-net-v6 he-net-tunnelbroker loopia namecheap noip noip-free ods opendns ovh-dynhost route53 selfhost zoneedit'); +define('DYNDNS_PROVIDER_DESCRIPTIONS', 'City Network,CloudFlare,Custom,Custom (v6),DHS,DNSexit,DNSimple,DNS Made Easy,DNS-O-Matic,DynDNS (dynamic),DynDNS (custom),DynDNS (static),DyNS,easyDNS,Euro Dns,freeDNS,GleSYS,Google Domains,GratisDNS,HE.net,HE.net (v6),HE.net Tunnelbroker,Loopia,Namecheap,No-IP,No-IP (free),ODS.org,OpenDNS,OVH DynHOST,Route 53,SelfHost,ZoneEdit'); + +/* implement ipv6 route advertising daemon */ +function services_radvd_configure($blacklist = array()) { + global $config, $g; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "services_radvd_configure() being called $mt\n"; + } + + if (!is_array($config['dhcpdv6'])) { + $config['dhcpdv6'] = array(); + } + + $Iflist = get_configured_interface_list(); + $Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces()); + $carplist = get_configured_carp_interface_list(); + + $radvdconf = "# Automatically Generated, do not edit\n"; + + /* Process all links which need the router advertise daemon */ + $radvdifs = array(); + + /* handle manually configured DHCP6 server settings first */ + foreach ($config['dhcpdv6'] as $dhcpv6if => $dhcpv6ifconf) { + if (!is_array($config['interfaces'][$dhcpv6if])) { + continue; + } + if (!isset($config['interfaces'][$dhcpv6if]['enable'])) { + continue; + } + + /* Do not put in the config an interface which is down */ + if (isset($blacklist[$dhcpv6if])) { + continue; + } + if (!isset($dhcpv6ifconf['ramode'])) { + $dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode']; + } + + /* are router advertisements enabled? */ + if ($dhcpv6ifconf['ramode'] == "disabled") { + continue; + } + + if (!isset($dhcpv6ifconf['rapriority'])) { + $dhcpv6ifconf['rapriority'] = "medium"; + } + + /* always start with the real parent, we override with the carp if later */ + $carpif = false; + /* check if we need to listen on a CARP interface */ + if (!empty($dhcpv6ifconf['rainterface'])) { + if (!empty($carplist[$dhcpv6ifconf['rainterface']])) { + $dhcpv6if = $dhcpv6ifconf['rainterface']; + $carpif = true; + } + } + + if (strstr($dhcpv6if, "_vip")) { + // CARP IP, check if it's enabled and find parent + if (!get_carp_status() || get_carp_interface_status($dhcpv6if) != "MASTER") { + continue; + } + $ifparent = link_carp_interface_to_parent($dhcpv6if); + $realif = convert_friendly_interface_to_real_interface_name($ifparent); + } else { + $realif = get_real_interface($dhcpv6if, "inet6"); + } + + if (isset($radvdifs[$realif])) { + continue; + } + + $ifcfgipv6 = get_interface_ipv6($dhcpv6if); + if (!is_ipaddrv6($ifcfgipv6)) { + continue; + } + + $ifcfgsnv6 = get_interface_subnetv6($dhcpv6if); + $subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6); + $radvdifs[$realif] = $realif; + + $radvdconf .= "# Generated for DHCPv6 Server $dhcpv6if\n"; + $radvdconf .= "interface {$realif} {\n"; + if (strstr($realif, "ovpn")) { + $radvdconf .= "\tUnicastOnly on;\n"; + } + $radvdconf .= "\tAdvSendAdvert on;\n"; + $radvdconf .= "\tMinRtrAdvInterval 5;\n"; + $radvdconf .= "\tMaxRtrAdvInterval 20;\n"; + $mtu = get_interface_mtu($realif); + if (is_numeric($mtu)) { + $radvdconf .= "\tAdvLinkMTU {$mtu};\n"; + } else { + $radvdconf .= "\tAdvLinkMTU 1280;\n"; + } + // $radvdconf .= "\tDeprecatePrefix on;\n"; + switch ($dhcpv6ifconf['rapriority']) { + case "low": + $radvdconf .= "\tAdvDefaultPreference low;\n"; + break; + case "high": + $radvdconf .= "\tAdvDefaultPreference high;\n"; + break; + default: + $radvdconf .= "\tAdvDefaultPreference medium;\n"; + break; + } + switch ($dhcpv6ifconf['ramode']) { + case "managed": + case "assist": + $radvdconf .= "\tAdvManagedFlag on;\n"; + $radvdconf .= "\tAdvOtherConfigFlag on;\n"; + break; + case "stateless_dhcp": + $radvdconf .= "\tAdvManagedFlag off;\n"; + $radvdconf .= "\tAdvOtherConfigFlag on;\n"; + break; + } + $radvdconf .= "\tprefix {$subnetv6}/{$ifcfgsnv6} {\n"; + if ($carpif == true) { + $radvdconf .= "\t\tDeprecatePrefix off;\n"; + } else { + $radvdconf .= "\t\tDeprecatePrefix on;\n"; + } + switch ($dhcpv6ifconf['ramode']) { + case "managed": + $radvdconf .= "\t\tAdvOnLink on;\n"; + $radvdconf .= "\t\tAdvAutonomous off;\n"; + $radvdconf .= "\t\tAdvRouterAddr on;\n"; + break; + case "router": + $radvdconf .= "\t\tAdvOnLink off;\n"; + $radvdconf .= "\t\tAdvAutonomous off;\n"; + $radvdconf .= "\t\tAdvRouterAddr on;\n"; + break; + case "stateless_dhcp": + case "assist": + $radvdconf .= "\t\tAdvOnLink on;\n"; + $radvdconf .= "\t\tAdvAutonomous on;\n"; + $radvdconf .= "\t\tAdvRouterAddr on;\n"; + break; + case "unmanaged": + $radvdconf .= "\t\tAdvOnLink on;\n"; + $radvdconf .= "\t\tAdvAutonomous on;\n"; + $radvdconf .= "\t\tAdvRouterAddr on;\n"; + break; + } + $radvdconf .= "\t};\n"; + + if (is_array($dhcpv6ifconf['subnets']['item'])) { + foreach ($dhcpv6ifconf['subnets']['item'] as $subnet) { + if (is_subnetv6($subnet)) { + $radvdconf .= "\tprefix {$subnet} {\n"; + if ($carpif == true) { + $radvdconf .= "\t\tDeprecatePrefix off;\n"; + } else { + $radvdconf .= "\t\tDeprecatePrefix on;\n"; + } + switch ($dhcpv6ifconf['ramode']) { + case "managed": + $radvdconf .= "\t\tAdvOnLink on;\n"; + $radvdconf .= "\t\tAdvAutonomous off;\n"; + $radvdconf .= "\t\tAdvRouterAddr on;\n"; + break; + case "router": + $radvdconf .= "\t\tAdvOnLink off;\n"; + $radvdconf .= "\t\tAdvAutonomous off;\n"; + $radvdconf .= "\t\tAdvRouterAddr on;\n"; + break; + case "assist": + $radvdconf .= "\t\tAdvOnLink on;\n"; + $radvdconf .= "\t\tAdvAutonomous on;\n"; + $radvdconf .= "\t\tAdvRouterAddr on;\n"; + break; + case "unmanaged": + $radvdconf .= "\t\tAdvOnLink on;\n"; + $radvdconf .= "\t\tAdvAutonomous on;\n"; + $radvdconf .= "\t\tAdvRouterAddr on;\n"; + break; + } + $radvdconf .= "\t};\n"; + } + } + } + if ($carpif === true) { + $radvdconf .= "\troute ::/0 {\n"; + $radvdconf .= "\t\tRemoveRoute off;\n"; + $radvdconf .= "\t};\n"; + } else { + $radvdconf .= "\troute ::/0 {\n"; + $radvdconf .= "\t\tRemoveRoute on;\n"; + $radvdconf .= "\t};\n"; + } + + /* add DNS servers */ + $dnslist = array(); + if (isset($dhcpv6ifconf['rasamednsasdhcp6']) && is_array($dhcpv6ifconf['dnsserver']) && !empty($dhcpv6ifconf['dnsserver'])) { + foreach ($dhcpv6ifconf['dnsserver'] as $server) { + if (is_ipaddrv6($server)) { + $dnslist[] = $server; + } + } + } elseif (!isset($dhcpv6ifconf['rasamednsasdhcp6']) && isset($dhcpv6ifconf['radnsserver']) && is_array($dhcpv6ifconf['radnsserver'])) { + foreach ($dhcpv6ifconf['radnsserver'] as $server) { + if (is_ipaddrv6($server)) { + $dnslist[] = $server; + } + } + } elseif (isset($config['dnsmasq']['enable']) || isset($config['unbound']['enable'])) { + $dnslist[] = get_interface_ipv6($realif); + } elseif (is_array($config['system']['dnsserver']) && !empty($config['system']['dnsserver'])) { + foreach ($config['system']['dnsserver'] as $server) { + if (is_ipaddrv6($server)) { + $dnslist[] = $server; + } + } + } + if (count($dnslist) > 0) { + $dnsstring = implode(" ", $dnslist); + if ($dnsstring <> "") { + $radvdconf .= "\tRDNSS {$dnsstring} { };\n"; + } + } + if (!empty($dhcpv6ifconf['domain'])) { + $radvdconf .= "\tDNSSL {$dhcpv6ifconf['domain']} { };\n"; + } elseif (!empty($config['system']['domain'])) { + $radvdconf .= "\tDNSSL {$config['system']['domain']} { };\n"; + } + $radvdconf .= "};\n"; + } + + /* handle DHCP-PD prefixes and 6RD dynamic interfaces */ + foreach ($Iflist as $if => $ifdescr) { + if (!isset($config['interfaces'][$if]['track6-interface'])) { + continue; + } + if (!isset($config['interfaces'][$if]['enable'])) { + continue; + } + /* Do not put in the config an interface which is down */ + if (isset($blacklist[$if])) { + continue; + } + $trackif = $config['interfaces'][$if]['track6-interface']; + if (empty($config['interfaces'][$trackif])) { + continue; + } + + if (strstr($if, "_vip")) { + // CARP IP, find parent + $ifparent = link_carp_interface_to_parent($if); + $realif = convert_friendly_interface_to_real_interface_name($ifparent); + } else { + $realif = get_real_interface($if, "inet6"); + } + + /* prevent duplicate entries, manual overrides */ + if (isset($radvdifs[$realif])) { + continue; + } + + $ifcfgipv6 = get_interface_ipv6($if); + if (!is_ipaddrv6($ifcfgipv6)) { + $subnetv6 = "::"; + $ifcfgsnv6 = "64"; + } else { + $ifcfgsnv6 = get_interface_subnetv6($if); + $subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6); + } + $radvdifs[$realif] = $realif; + + $autotype = $config['interfaces'][$trackif]['ipaddrv6']; + + if ($g['debug']) { + log_error("configuring RA on {$if} for type {$autotype} radvd subnet {$subnetv6}/{$ifcfgsnv6}"); + } + + $radvdconf .= "# Generated config for {$autotype} delegation from {$trackif} on {$if}\n"; + $radvdconf .= "interface {$realif} {\n"; + $radvdconf .= "\tAdvSendAdvert on;\n"; + $radvdconf .= "\tMinRtrAdvInterval 3;\n"; + $radvdconf .= "\tMaxRtrAdvInterval 10;\n"; + $mtu = get_interface_mtu($realif); + if (is_numeric($mtu)) { + $radvdconf .= "\tAdvLinkMTU {$mtu};\n"; + } else { + $radvdconf .= "\tAdvLinkMTU 1280;\n"; + } + $radvdconf .= "\tAdvOtherConfigFlag on;\n"; + $radvdconf .= "\t\tprefix {$subnetv6}/{$ifcfgsnv6} {\n"; + $radvdconf .= "\t\tAdvOnLink on;\n"; + $radvdconf .= "\t\tAdvAutonomous on;\n"; + $radvdconf .= "\t\tAdvRouterAddr on;\n"; + $radvdconf .= "\t};\n"; + + /* add DNS servers */ + $dnslist = array(); + if (isset($config['dnsmasq']['enable']) || isset($config['unbound']['enable'])) { + $dnslist[] = $ifcfgipv6; + } elseif (is_array($config['system']['dnsserver']) && !empty($config['system']['dnsserver'])) { + foreach ($config['system']['dnsserver'] as $server) { + if (is_ipaddrv6($server)) { + $dnslist[] = $server; + } + } + } + if (count($dnslist) > 0) { + $dnsstring = implode(" ", $dnslist); + if (!empty($dnsstring)) { + $radvdconf .= "\tRDNSS {$dnsstring} { };\n"; + } + } + if (!empty($config['system']['domain'])) { + $radvdconf .= "\tDNSSL {$config['system']['domain']} { };\n"; + } + $radvdconf .= "};\n"; + } + + /* write radvd.conf */ + if (!@file_put_contents("{$g['varetc_path']}/radvd.conf", $radvdconf)) { + log_error("Error: cannot open radvd.conf in services_radvd_configure().\n"); + if (platform_booting()) { + printf("Error: cannot open radvd.conf in services_radvd_configure().\n"); + } + } + unset($radvdconf); + + if (count($radvdifs) > 0) { + if (isvalidpid("{$g['varrun_path']}/radvd.pid")) { + sigkillbypid("{$g['varrun_path']}/radvd.pid", "HUP"); + } else { + mwexec("/usr/local/sbin/radvd -p {$g['varrun_path']}/radvd.pid -C {$g['varetc_path']}/radvd.conf -m syslog"); + } + } else { + /* we need to shut down the radvd cleanly, it will send out the prefix + * information with a lifetime of 0 to notify clients of a (possible) new prefix */ + if (isvalidpid("{$g['varrun_path']}/radvd.pid")) { + log_error("Shutting down Router Advertisment daemon cleanly"); + killbypid("{$g['varrun_path']}/radvd.pid"); + @unlink("{$g['varrun_path']}/radvd.pid"); + } + } + return 0; +} + +function services_dhcpd_configure($family = "all", $blacklist = array()) { + global $config, $g; + + /* configure DHCPD chroot once */ + $fd = fopen("{$g['tmp_path']}/dhcpd.sh","w"); + fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}\n"); + fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/dev\n"); + fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/etc\n"); + fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/usr/local/sbin\n"); + fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/db\n"); + fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/run\n"); + fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/usr\n"); + fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/lib\n"); + fwrite($fd, "/bin/mkdir -p {$g['dhcpd_chroot_path']}/run\n"); + fwrite($fd, "/usr/sbin/chown -R dhcpd:_dhcp {$g['dhcpd_chroot_path']}/*\n"); + fwrite($fd, "/bin/cp -n /lib/libc.so.* {$g['dhcpd_chroot_path']}/lib/\n"); + fwrite($fd, "/bin/cp -n /usr/local/sbin/dhcpd {$g['dhcpd_chroot_path']}/usr/local/sbin/\n"); + fwrite($fd, "/bin/chmod a+rx {$g['dhcpd_chroot_path']}/usr/local/sbin/dhcpd\n"); + + $status = `/sbin/mount | /usr/bin/grep -v grep | /usr/bin/grep "{$g['dhcpd_chroot_path']}/dev"`; + if (!trim($status)) { + fwrite($fd, "/sbin/mount -t devfs devfs {$g['dhcpd_chroot_path']}/dev\n"); + } + fclose($fd); + mwexec("/bin/sh {$g['tmp_path']}/dhcpd.sh"); + + if ($family == "all" || $family == "inet") { + services_dhcpdv4_configure(); + } + if ($family == "all" || $family == "inet6") { + services_dhcpdv6_configure($blacklist); + services_radvd_configure($blacklist); + } +} + +function services_dhcpdv4_configure() { + global $config, $g; + $need_ddns_updates = false; + $ddns_zones = array(); + + if ($g['services_dhcp_server_enable'] == false) { + return; + } + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "services_dhcpdv4_configure($if) being called $mt\n"; + } + + /* kill any running dhcpd */ + if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid")) { + killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid"); + } + + /* DHCP enabled on any interfaces? */ + if (!is_dhcp_server_enabled()) { + return 0; + } + + /* if OLSRD is enabled, allow WAN to house DHCP. */ + if (!function_exists('is_package_installed')) { + require_once('pkg-utils.inc'); + } + if (is_package_installed('olsrd') && isset($config['installedpackages']['olsrd'])) { + foreach ($config['installedpackages']['olsrd']['config'] as $olsrd) { + if (isset($olsrd['enable']) && $olsrd['enable'] == "on") { + $is_olsr_enabled = true; + break; + } + } + } + + if (platform_booting()) { + /* restore the leases, if we have them */ + if (file_exists("{$g['cf_conf_path']}/dhcpleases.tgz")) { + $dhcprestore = ""; + $dhcpreturn = ""; + exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcpleases.tgz 2>&1", $dhcprestore, $dhcpreturn); + $dhcprestore = implode(" ", $dhcprestore); + if ($dhcpreturn <> 0) { + log_error(sprintf(gettext('DHCP leases restore failed exited with %1$s, the error is: %2$s%3$s'), $dhcpreturn, $dhcprestore, "\n")); + } + } + /* If this backup is still there on a full install, but we aren't going to use ram disks, remove the archive since this is a transition. */ + if (($g['platform'] == "pfSense") && !isset($config['system']['use_mfs_tmpvar'])) { + unlink_if_exists("{$g['cf_conf_path']}/dhcpleases.tgz"); + } + } + + $syscfg = $config['system']; + if (!is_array($config['dhcpd'])) { + $config['dhcpd'] = array(); + } + $dhcpdcfg = $config['dhcpd']; + $Iflist = get_configured_interface_list(); + + /* Only consider DNS servers with IPv4 addresses for the IPv4 DHCP server. */ + $dns_arrv4 = array(); + if (is_array($syscfg['dnsserver'])) { + foreach ($syscfg['dnsserver'] as $dnsserver) { + if (is_ipaddrv4($dnsserver)) { + $dns_arrv4[] = $dnsserver; + } + } + } + + if (platform_booting()) { + echo gettext("Starting DHCP service..."); + } else { + sleep(1); + } + + $custoptions = ""; + foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) { + if (is_array($dhcpifconf['numberoptions']) && is_array($dhcpifconf['numberoptions']['item'])) { + foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) { + if (!empty($item['type'])) { + $itemtype = $item['type']; + } else { + $itemtype = "text"; + } + $custoptions .= "option custom-{$dhcpif}-{$itemidx} code {$item['number']} = {$itemtype};\n"; + } + } + } + + $dhcpdconf = <<<EOD + +option domain-name "{$syscfg['domain']}"; +option ldap-server code 95 = text; +option domain-search-list code 119 = text; +option arch code 93 = unsigned integer 16; # RFC4578 +{$custoptions} +default-lease-time 7200; +max-lease-time 86400; +log-facility local7; +one-lease-per-client true; +deny duplicates; +ping-check true; +update-conflict-detection false; + +EOD; + + if (!isset($dhcpifconf['disableauthoritative'])) { + $dhcpdconf .= "authoritative;\n"; + } + + if (isset($dhcpifconf['alwaysbroadcast'])) { + $dhcpdconf .= "always-broadcast on\n"; + } + + $dhcpdifs = array(); + $enable_add_routers = false; + $gateways_arr = return_gateways_array(); + /* only add a routers line if the system has any IPv4 gateway at all */ + /* a static route has a gateway, manually overriding this field always works */ + foreach ($gateways_arr as $gwitem) { + if ($gwitem['ipprotocol'] == "inet") { + $enable_add_routers = true; + break; + } + } + + /* loop through and determine if we need to setup + * failover peer "bleh" entries + */ + foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) { + + if (!isset($config['interfaces'][$dhcpif]['enable'])) { + continue; + } + + interfaces_staticarp_configure($dhcpif); + + if (!isset($dhcpifconf['enable'])) { + continue; + } + + if ($dhcpifconf['failover_peerip'] <> "") { + $intip = get_interface_ip($dhcpif); + /* + * yep, failover peer is defined. + * does it match up to a defined vip? + */ + $skew = 110; + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $vipent) { + if ($vipent['interface'] == $dhcpif) { + $carp_nw = gen_subnet($vipent['subnet'], $vipent['subnet_bits']); + if (ip_in_subnet($dhcpifconf['failover_peerip'], "{$carp_nw}/{$vipent['subnet_bits']}")) { + /* this is the interface! */ + if (is_numeric($vipent['advskew']) && (intval($vipent['advskew']) < 20)) { + $skew = 0; + break; + } + } + } + } + } else { + log_error(gettext("Warning! DHCP Failover setup and no CARP virtual IPs defined!")); + } + if ($skew > 10) { + $type = "secondary"; + $my_port = "520"; + $peer_port = "519"; + } else { + $my_port = "519"; + $peer_port = "520"; + $type = "primary"; + $dhcpdconf_pri = "split 128;\n"; + $dhcpdconf_pri .= " mclt 600;\n"; + } + + if (is_ipaddrv4($intip)) { + $dhcpdconf .= <<<EOPP +failover peer "dhcp_{$dhcpif}" { + {$type}; + address {$intip}; + port {$my_port}; + peer address {$dhcpifconf['failover_peerip']}; + peer port {$peer_port}; + max-response-delay 10; + max-unacked-updates 10; + {$dhcpdconf_pri} + load balance max seconds 3; +} +\n +EOPP; + } + } + } + + foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) { + + $newzone = array(); + $ifcfg = $config['interfaces'][$dhcpif]; + + if (!isset($dhcpifconf['enable']) || !isset($Iflist[$dhcpif])) { + continue; + } + $ifcfgip = get_interface_ip($dhcpif); + $ifcfgsn = get_interface_subnet($dhcpif); + $subnet = gen_subnet($ifcfgip, $ifcfgsn); + $subnetmask = gen_subnet_mask($ifcfgsn); + + if (!is_ipaddr($subnet)) { + continue; + } + + if ($is_olsr_enabled == true) { + if ($dhcpifconf['netmask']) { + $subnetmask = gen_subnet_mask($dhcpifconf['netmask']); + } + } + + $all_pools = array(); + $all_pools[] = $dhcpifconf; + if (is_array($dhcpifconf['pool'])) { + $all_pools = array_merge($all_pools, $dhcpifconf['pool']); + } + + $dnscfg = ""; + + if ($dhcpifconf['domain']) { + $dnscfg .= " option domain-name \"{$dhcpifconf['domain']}\";\n"; + } + + if ($dhcpifconf['domainsearchlist'] <> "") { + $dnscfg .= " option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpifconf['domainsearchlist'])) . "\";\n"; + } + + if (isset($dhcpifconf['ddnsupdate'])) { + $need_ddns_updates = true; + $newzone = array(); + if ($dhcpifconf['ddnsdomain'] <> "") { + $newzone['domain-name'] = $dhcpifconf['ddnsdomain']; + $dnscfg .= " ddns-domainname \"{$dhcpifconf['ddnsdomain']}\";\n"; + } else { + $newzone['domain-name'] = $config['system']['domain']; + } + $revsubnet = explode(".", $subnet); + $revsubnet = array_reverse($revsubnet); + foreach ($revsubnet as $octet) { + if ($octet != "0") { + break; + } + array_shift($revsubnet); + } + $newzone['ptr-domain'] = implode(".", $revsubnet) . ".in-addr.arpa"; + } + + if (is_array($dhcpifconf['dnsserver']) && ($dhcpifconf['dnsserver'][0])) { + $dnscfg .= " option domain-name-servers " . join(",", $dhcpifconf['dnsserver']) . ";"; + if ($newzone['domain-name']) { + $newzone['dns-servers'] = $dhcpifconf['dnsserver']; + } + } else if (isset($config['dnsmasq']['enable'])) { + $dnscfg .= " option domain-name-servers {$ifcfgip};"; + if ($newzone['domain-name'] && is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) { + $newzone['dns-servers'] = $syscfg['dnsserver']; + } + } else if (isset($config['unbound']['enable'])) { + $dnscfg .= " option domain-name-servers {$ifcfgip};"; + } else if (!empty($dns_arrv4)) { + $dnscfg .= " option domain-name-servers " . join(",", $dns_arrv4) . ";"; + if ($newzone['domain-name']) { + $newzone['dns-servers'] = $dns_arrv4; + } + } + + /* Create classes - These all contain comma separated lists. Join them into one + big comma separated string then split them all up. */ + $all_mac_strings = array(); + if (is_array($dhcpifconf['pool'])) { + foreach ($all_pools as $poolconf) { + $all_mac_strings[] = $poolconf['mac_allow']; + $all_mac_strings[] = $poolconf['mac_deny']; + } + } + $all_mac_strings[] = $dhcpifconf['mac_allow']; + $all_mac_strings[] = $dhcpifconf['mac_deny']; + if (!empty($all_mac_strings)) { + $all_mac_list = array_unique(explode(',', implode(',', $all_mac_strings))); + foreach ($all_mac_list as $mac) { + if (empty($mac)) { + continue; + } + $dhcpdconf .= 'class "' . str_replace(':', '', $mac) . '" {' . "\n"; + // Skip the first octet of the MAC address - for media type, typically Ethernet ("01") and match the rest. + $dhcpdconf .= ' match if substring (hardware, 1, ' . (substr_count($mac, ':') + 1) . ') = ' . $mac . ';' . "\n"; + $dhcpdconf .= '}' . "\n"; + } + } + + $dhcpdconf .= "subnet {$subnet} netmask {$subnetmask} {\n"; + + // Setup pool options + foreach ($all_pools as $poolconf) { + $dhcpdconf .= " pool {\n"; + /* is failover dns setup? */ + if (is_array($poolconf['dnsserver']) && $poolconf['dnsserver'][0] <> "") { + $dhcpdconf .= " option domain-name-servers {$poolconf['dnsserver'][0]}"; + if ($poolconf['dnsserver'][1] <> "") { + $dhcpdconf .= ",{$poolconf['dnsserver'][1]}"; + } + if ($poolconf['dnsserver'][2] <> "") { + $dhcpdconf .= ",{$poolconf['dnsserver'][2]}"; + } + if ($poolconf['dnsserver'][3] <> "") { + $dhcpdconf .= ",{$poolconf['dnsserver'][3]}"; + } + $dhcpdconf .= ";\n"; + } + + /* allow/deny MACs */ + $mac_allow_list = array_unique(explode(',', $poolconf['mac_allow'])); + foreach ($mac_allow_list as $mac) { + if (empty($mac)) { + continue; + } + $dhcpdconf .= " allow members of \"" . str_replace(':', '', $mac) . "\";\n"; + } + $mac_deny_list = array_unique(explode(',', $poolconf['mac_deny'])); + foreach ($mac_deny_list as $mac) { + if (empty($mac)) { + continue; + } + $dhcpdconf .= " deny members of \"" . str_replace(':', '', $mac) . "\";\n"; + } + + if ($poolconf['failover_peerip'] <> "") { + $dhcpdconf .= " deny dynamic bootp clients;\n"; + } + + if (isset($poolconf['denyunknown'])) { + $dhcpdconf .= " deny unknown-clients;\n"; + } + + if ($poolconf['gateway'] && $poolconf['gateway'] != "none" && ($poolconf['gateway'] != $dhcpifconf['gateway'])) { + $dhcpdconf .= " option routers {$poolconf['gateway']};\n"; + } + + if ($dhcpifconf['failover_peerip'] <> "") { + $dhcpdconf .= " failover peer \"dhcp_{$dhcpif}\";\n"; + } + + $pdnscfg = ""; + + if ($poolconf['domain'] && ($poolconf['domain'] != $dhcpifconf['domain'])) { + $pdnscfg .= " option domain-name \"{$poolconf['domain']}\";\n"; + } + + if (!empty($poolconf['domainsearchlist']) && ($poolconf['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) { + $pdnscfg .= " option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $poolconf['domainsearchlist'])) . "\";\n"; + } + + if (isset($poolconf['ddnsupdate'])) { + if (($poolconf['ddnsdomain'] <> "") && ($poolconf['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) { + $pdnscfg .= " ddns-domainname \"{$poolconf['ddnsdomain']}\";\n"; + } + $pdnscfg .= " ddns-update-style interim;\n"; + } + + if (is_array($poolconf['dnsserver']) && ($poolconf['dnsserver'][0]) && ($poolconf['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) { + $pdnscfg .= " option domain-name-servers " . join(",", $poolconf['dnsserver']) . ";\n"; + } + $dhcpdconf .= "{$pdnscfg}"; + + // default-lease-time + if ($poolconf['defaultleasetime'] && ($poolconf['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) { + $dhcpdconf .= " default-lease-time {$poolconf['defaultleasetime']};\n"; + } + + // max-lease-time + if ($poolconf['maxleasetime'] && ($poolconf['maxleasetime'] != $dhcpifconf['maxleasetime'])) { + $dhcpdconf .= " max-lease-time {$poolconf['maxleasetime']};\n"; + } + + // netbios-name* + if (is_array($poolconf['winsserver']) && $poolconf['winsserver'][0] && ($poolconf['winsserver'][0] != $dhcpifconf['winsserver'][0])) { + $dhcpdconf .= " option netbios-name-servers " . join(",", $poolconf['winsserver']) . ";\n"; + $dhcpdconf .= " option netbios-node-type 8;\n"; + } + + // ntp-servers + if (is_array($poolconf['ntpserver']) && $poolconf['ntpserver'][0] && ($poolconf['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) { + $dhcpdconf .= " option ntp-servers " . join(",", $poolconf['ntpserver']) . ";\n"; + } + + // tftp-server-name + if (!empty($poolconf['tftp']) && ($poolconf['tftp'] != $dhcpifconf['tftp'])) { + $dhcpdconf .= " option tftp-server-name \"{$poolconf['tftp']}\";\n"; + } + + // ldap-server + if (!empty($poolconf['ldap']) && ($poolconf['ldap'] != $dhcpifconf['ldap'])) { + $dhcpdconf .= " option ldap-server \"{$poolconf['ldap']}\";\n"; + } + + // net boot information + if (isset($poolconf['netboot'])) { + if (!empty($poolconf['nextserver']) && ($poolconf['nextserver'] != $dhcpifconf['nextserver'])) { + $dhcpdconf .= " next-server {$poolconf['nextserver']};\n"; + } + if (!empty($poolconf['filename']) && ($poolconf['filename'] != $dhcpifconf['filename'])) { + $dhcpdconf .= " filename \"{$poolconf['filename']}\";\n"; + } + if (!empty($poolconf['rootpath']) && ($poolconf['rootpath'] != $dhcpifconf['rootpath'])) { + $dhcpdconf .= " option root-path \"{$poolconf['rootpath']}\";\n"; + } + } + $dhcpdconf .= " range {$poolconf['range']['from']} {$poolconf['range']['to']};\n"; + $dhcpdconf .= " }\n\n"; + } +// End of settings inside pools + + if ($dhcpifconf['gateway'] && $dhcpifconf['gateway'] != "none") { + $routers = $dhcpifconf['gateway']; + $add_routers = true; + } elseif ($dhcpifconf['gateway'] == "none") { + $add_routers = false; + } else { + $add_routers = $enable_add_routers; + $routers = $ifcfgip; + } + if ($add_routers) { + $dhcpdconf .= " option routers {$routers};\n"; + } + + $dhcpdconf .= <<<EOD +$dnscfg + +EOD; + // default-lease-time + if ($dhcpifconf['defaultleasetime']) { + $dhcpdconf .= " default-lease-time {$dhcpifconf['defaultleasetime']};\n"; + } + + // max-lease-time + if ($dhcpifconf['maxleasetime']) { + $dhcpdconf .= " max-lease-time {$dhcpifconf['maxleasetime']};\n"; + } + + // netbios-name* + if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) { + $dhcpdconf .= " option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n"; + $dhcpdconf .= " option netbios-node-type 8;\n"; + } + + // ntp-servers + if (is_array($dhcpifconf['ntpserver']) && $dhcpifconf['ntpserver'][0]) { + $dhcpdconf .= " option ntp-servers " . join(",", $dhcpifconf['ntpserver']) . ";\n"; + } + + // tftp-server-name + if ($dhcpifconf['tftp'] <> "") { + $dhcpdconf .= " option tftp-server-name \"{$dhcpifconf['tftp']}\";\n"; + } + + // Handle option, number rowhelper values + $dhcpdconf .= "\n"; + if ($dhcpifconf['numberoptions']['item']) { + foreach ($dhcpifconf['numberoptions']['item'] as $itemidx => $item) { + if (empty($item['type']) || $item['type'] == "text") { + $dhcpdconf .= " option custom-{$dhcpif}-{$itemidx} \"{$item['value']}\";\n"; + } else { + $dhcpdconf .= " option custom-{$dhcpif}-{$itemidx} {$item['value']};\n"; + } + } + } + + // ldap-server + if ($dhcpifconf['ldap'] <> "") { + $dhcpdconf .= " option ldap-server \"{$dhcpifconf['ldap']}\";\n"; + } + + // net boot information + if (isset($dhcpifconf['netboot'])) { + if ($dhcpifconf['nextserver'] <> "") { + $dhcpdconf .= " next-server {$dhcpifconf['nextserver']};\n"; + } + if (!empty($dhcpifconf['filename']) && !empty($dhcpifconf['filename32']) && !empty($dhcpifconf['filename64'])) { + $dhcpdconf .= " if option arch = 00:06 {\n"; + $dhcpdconf .= " filename \"{$dhcpifconf['filename32']}\";\n"; + $dhcpdconf .= " } else if option arch = 00:07 {\n"; + $dhcpdconf .= " filename \"{$dhcpifconf['filename64']}\";\n"; + $dhcpdconf .= " } else {\n"; + $dhcpdconf .= " filename \"{$dhcpifconf['filename']}\";\n"; + $dhcpdconf .= " }\n\n"; + } elseif (!empty($dhcpifconf['filename'])) { + $dhcpdconf .= " filename \"{$dhcpifconf['filename']}\";\n"; + } + if (!empty($dhcpifconf['rootpath'])) { + $dhcpdconf .= " option root-path \"{$dhcpifconf['rootpath']}\";\n"; + } + } + + $dhcpdconf .= <<<EOD +} + +EOD; + + /* add static mappings */ + if (is_array($dhcpifconf['staticmap'])) { + + $i = 0; + foreach ($dhcpifconf['staticmap'] as $sm) { + $dhcpdconf .= "host s_{$dhcpif}_{$i} {\n"; + + if ($sm['mac']) { + $dhcpdconf .= " hardware ethernet {$sm['mac']};\n"; + } + + if ($sm['cid']) { + $dhcpdconf .= " option dhcp-client-identifier \"{$sm['cid']}\";\n"; + } + + if ($sm['ipaddr']) { + $dhcpdconf .= " fixed-address {$sm['ipaddr']};\n"; + } + + if ($sm['hostname']) { + $dhhostname = str_replace(" ", "_", $sm['hostname']); + $dhhostname = str_replace(".", "_", $dhhostname); + $dhcpdconf .= " option host-name \"{$dhhostname}\";\n"; + } + if ($sm['filename']) { + $dhcpdconf .= " filename \"{$sm['filename']}\";\n"; + } + + if ($sm['rootpath']) { + $dhcpdconf .= " option root-path \"{$sm['rootpath']}\";\n"; + } + + if ($sm['gateway'] && ($sm['gateway'] != $dhcpifconf['gateway'])) { + $dhcpdconf .= " option routers {$sm['gateway']};\n"; + } + + $smdnscfg = ""; + + if ($sm['domain'] && ($sm['domain'] != $dhcpifconf['domain'])) { + $smdnscfg .= " option domain-name \"{$sm['domain']}\";\n"; + } + + if (!empty($sm['domainsearchlist']) && ($sm['domainsearchlist'] != $dhcpifconf['domainsearchlist'])) { + $smdnscfg .= " option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $sm['domainsearchlist'])) . "\";\n"; + } + + if (isset($sm['ddnsupdate'])) { + if (($sm['ddnsdomain'] <> "") && ($sm['ddnsdomain'] != $dhcpifconf['ddnsdomain'])) { + $pdnscfg .= " ddns-domainname \"{$sm['ddnsdomain']}\";\n"; + } + $pdnscfg .= " ddns-update-style interim;\n"; + } + + if (is_array($sm['dnsserver']) && ($sm['dnsserver'][0]) && ($sm['dnsserver'][0] != $dhcpifconf['dnsserver'][0])) { + $smdnscfg .= " option domain-name-servers " . join(",", $sm['dnsserver']) . ";\n"; + } + $dhcpdconf .= "{$smdnscfg}"; + + // default-lease-time + if ($sm['defaultleasetime'] && ($sm['defaultleasetime'] != $dhcpifconf['defaultleasetime'])) { + $dhcpdconf .= " default-lease-time {$sm['defaultleasetime']};\n"; + } + + // max-lease-time + if ($sm['maxleasetime'] && ($sm['maxleasetime'] != $dhcpifconf['maxleasetime'])) { + $dhcpdconf .= " max-lease-time {$sm['maxleasetime']};\n"; + } + + // netbios-name* + if (is_array($sm['winsserver']) && $sm['winsserver'][0] && ($sm['winsserver'][0] != $dhcpifconf['winsserver'][0])) { + $dhcpdconf .= " option netbios-name-servers " . join(",", $sm['winsserver']) . ";\n"; + $dhcpdconf .= " option netbios-node-type 8;\n"; + } + + // ntp-servers + if (is_array($sm['ntpserver']) && $sm['ntpserver'][0] && ($sm['ntpserver'][0] != $dhcpifconf['ntpserver'][0])) { + $dhcpdconf .= " option ntp-servers " . join(",", $sm['ntpserver']) . ";\n"; + } + + // tftp-server-name + if (!empty($sm['tftp']) && ($sm['tftp'] != $dhcpifconf['tftp'])) { + $dhcpdconf .= " option tftp-server-name \"{$sm['tftp']}\";\n"; + } + + $dhcpdconf .= "}\n"; + $i++; + } + } + + $dhcpdifs[] = get_real_interface($dhcpif); + if ($newzone['domain-name']) { + if ($need_ddns_updates) { + $newzone['dns-servers'] = array($dhcpifconf['ddnsdomainprimary']); + } + $ddns_zones[] = $newzone; + } + } + + if ($need_ddns_updates) { + $dhcpdconf .= "ddns-update-style interim;\n"; + $dhcpdconf .= "update-static-leases on;\n"; + + $dhcpdconf .= dhcpdkey($dhcpifconf); + $dhcpdconf .= dhcpdzones($ddns_zones, $dhcpifconf); + } + + /* write dhcpd.conf */ + if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpd.conf", $dhcpdconf)) { + printf(gettext("Error: cannot open dhcpd.conf in services_dhcpdv4_configure().%s"), "\n"); + unset($dhcpdconf); + return 1; + } + unset($dhcpdconf); + + /* create an empty leases database */ + if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases")) { + @touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"); + } + + /* make sure there isn't a stale dhcpd.pid file, which can make dhcpd fail to start. */ + /* if we get here, dhcpd has been killed and is not started yet */ + unlink_if_exists("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpd.pid"); + + /* fire up dhcpd in a chroot */ + if (count($dhcpdifs) > 0) { + mwexec("/usr/local/sbin/dhcpd -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpd.conf -pf {$g['varrun_path']}/dhcpd.pid " . + join(" ", $dhcpdifs)); + } + + if (platform_booting()) { + print "done.\n"; + } + + return 0; +} + +function dhcpdkey($dhcpifconf) { + $dhcpdconf = ""; + if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") { + $dhcpdconf .= "key {$dhcpifconf['ddnsdomainkeyname']} {\n"; + $dhcpdconf .= " algorithm hmac-md5;\n"; + $dhcpdconf .= " secret {$dhcpifconf['ddnsdomainkey']};\n"; + $dhcpdconf .= "}\n"; + } + + return $dhcpdconf; +} + +function dhcpdzones($ddns_zones, $dhcpifconf) { + $dhcpdconf = ""; + + if (is_array($ddns_zones)) { + $added_zones = array(); + foreach ($ddns_zones as $zone) { + if (!is_array($zone) || empty($zone) || !is_array($zone['dns-servers'])) { + continue; + } + $primary = $zone['dns-servers'][0]; + $secondary = empty($zone['dns-servers'][1]) ? "" : $zone['dns-servers'][1]; + + // Make sure we aren't using any invalid or IPv6 DNS servers. + if (!is_ipaddrv4($primary)) { + if (is_ipaddrv4($secondary)) { + $primary = $secondary; + $secondary = ""; + } else { + continue; + } + } + + // We don't need to add zones multiple times. + if ($zone['domain-name'] && !in_array($zone['domain-name'], $added_zones)) { + $dhcpdconf .= "zone {$zone['domain-name']}. {\n"; + $dhcpdconf .= " primary {$primary};\n"; + if (is_ipaddrv4($secondary)) { + $dhcpdconf .= " secondary {$secondary};\n"; + } + if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") { + $dhcpdconf .= " key {$dhcpifconf['ddnsdomainkeyname']};\n"; + } + $dhcpdconf .= "}\n"; + $added_zones[] = $zone['domain-name']; + } + if ($zone['ptr-domain'] && !in_array($zone['ptr-domain'], $added_zones)) { + $dhcpdconf .= "zone {$zone['ptr-domain']} {\n"; + $dhcpdconf .= " primary {$primary};\n"; + if (is_ipaddrv4($secondary)) { + $dhcpdconf .= " secondary {$secondary};\n"; + } + if ($dhcpifconf['ddnsdomainkeyname'] <> "" && $dhcpifconf['ddnsdomainkey'] <> "") { + $dhcpdconf .= " key {$dhcpifconf['ddnsdomainkeyname']};\n"; + } + $dhcpdconf .= "}\n"; + $added_zones[] = $zone['ptr-domain']; + } + } + } + + return $dhcpdconf; +} + +function services_dhcpdv6_configure($blacklist = array()) { + global $config, $g; + + if ($g['services_dhcp_server_enable'] == false) { + return; + } + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "services_dhcpd_configure($if) being called $mt\n"; + } + + /* kill any running dhcpd */ + if (isvalidpid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid")) { + killbypid("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid"); + } + if (isvalidpid("{$g['varrun_path']}/dhcpleases6.pid")) { + killbypid("{$g['varrun_path']}/dhcpleases6.pid"); + } + + /* DHCP enabled on any interfaces? */ + if (!is_dhcpv6_server_enabled()) { + return 0; + } + + if (platform_booting()) { + if ($g['platform'] != "pfSense") { + /* restore the leases, if we have them */ + if (file_exists("{$g['cf_conf_path']}/dhcp6leases.tgz")) { + $dhcprestore = ""; + $dhcpreturn = ""; + exec("cd /;LANG=C /usr/bin/tar -xzf {$g['cf_conf_path']}/dhcp6leases.tgz 2>&1", $dhcprestore, $dhcpreturn); + $dhcprestore = implode(" ", $dhcprestore); + if ($dhcpreturn <> 0) { + log_error("DHCP leases v6 restore failed exited with $dhcpreturn, the error is: $dhcprestore\n"); + } + } + } + } + + $syscfg = $config['system']; + if (!is_array($config['dhcpdv6'])) { + $config['dhcpdv6'] = array(); + } + $dhcpdv6cfg = $config['dhcpdv6']; + $Iflist = get_configured_interface_list(); + $Iflist = array_merge($Iflist, get_configured_pppoe_server_interfaces()); + + + if (platform_booting()) { + echo "Starting DHCPv6 service..."; + } else { + sleep(1); + } + + /* we add a fake entry for interfaces that are set to track6 another WAN */ + foreach ($Iflist as $ifname) { + /* Do not put in the config an interface which is down */ + if (isset($blacklist[$ifname])) { + continue; + } + if (!empty($config['interfaces'][$ifname]['track6-interface'])) { + $realif = get_real_interface($ifname, "inet6"); + $ifcfgipv6 = get_interface_ipv6($ifname); + if (!is_ipaddrv6($ifcfgipv6)) { + continue; + } + $ifcfgipv6 = Net_IPv6::getNetmask($ifcfgipv6, 64); + $trackifname = $config['interfaces'][$ifname]['track6-interface']; + $trackcfg = $config['interfaces'][$trackifname]; + $pdlen = calculate_ipv6_delegation_length($trackifname); + $ifcfgipv6arr =explode(":", $ifcfgipv6); + $dhcpdv6cfg[$ifname] = array(); + $dhcpdv6cfg[$ifname]['enable'] = true; + /* range */ + $ifcfgipv6arr[7] = "1000"; + $dhcpdv6cfg[$ifname]['range'] = array(); + $dhcpdv6cfg[$ifname]['range']['from'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr)); + $ifcfgipv6arr[7] = "2000"; + $dhcpdv6cfg[$ifname]['range']['to'] = Net_IPv6::compress(implode(":", $ifcfgipv6arr)); + /* prefix length > 0? We can add dhcp6 prefix delegation server */ + if ($pdlen > 2) { + $pdlenmax = $pdlen; + $pdlenhalf = $pdlenmax -1; + $pdlenmin = (64 - ceil($pdlenhalf / 4)); + $dhcpdv6cfg[$ifname]['prefixrange'] = array(); + $dhcpdv6cfg[$ifname]['prefixrange']['prefixlength'] = $pdlenmin; + + /* set the delegation start to half the current address block */ + $range = Net_IPv6::parseAddress($ifcfgipv6, (64 - $pdlenmax)); + $range['start'] = Net_IPv6::getNetmask($range['end'], (64 - $pdlenhalf)); + + /* set the end range to a multiple of the prefix delegation size, required by dhcpd */ + $range = Net_IPv6::parseAddress($range['end'], (64 - $pdlenhalf)); + $range['end'] = Net_IPv6::getNetmask($range['end'], (64 - round($pdlen / 2))); + + $dhcpdv6cfg[$ifname]['prefixrange']['from'] = Net_IPv6::compress($range['start']); + $dhcpdv6cfg[$ifname]['prefixrange']['to'] = Net_IPv6::compress($range['end']); + } + $dhcpdv6cfg[$ifname]['dns6ip'] = get_interface_ipv6($ifname); + } + } + + $custoptionsv6 = ""; + foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) { + if (is_array($dhcpv6ifconf['numberoptions']) && is_array($dhcpv6ifconf['numberoptions']['item'])) { + foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) { + $custoptionsv6 .= "option custom-{$dhcpv6if}-{$itemv6idx} code {$itemv6['number']} = text;\n"; + } + } + } + + if (isset($dhcpv6ifconf['netboot']) && !empty($dhcpv6ifconf['bootfile_url'])) { + $custoptionsv6 .= "option dhcp6.bootfile-url code 59 = string;\n"; + } + + $dhcpdv6conf = <<<EOD + +option domain-name "{$syscfg['domain']}"; +option ldap-server code 95 = text; +option domain-search-list code 119 = text; +{$custoptionsv6} +default-lease-time 7200; +max-lease-time 86400; +log-facility local7; +one-lease-per-client true; +deny duplicates; +ping-check true; +update-conflict-detection false; + +EOD; + + if (!isset($dhcpv6ifconf['disableauthoritative'])) { + $dhcpdv6conf .= "authoritative;\n"; + } + + if (isset($dhcpv6ifconf['alwaysbroadcast'])) { + $dhcpdv6conf .= "always-broadcast on\n"; + } + + $dhcpdv6ifs = array(); + + $dhcpv6num = 0; + $nsupdate = false; + + foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) { + + $ddns_zones = array(); + + $ifcfgv6 = $config['interfaces'][$dhcpv6if]; + + if (!isset($dhcpv6ifconf['enable']) || !isset($Iflist[$dhcpv6if]) || !isset($ifcfgv6['enable'])) { + continue; + } + $ifcfgipv6 = get_interface_ipv6($dhcpv6if); + $ifcfgsnv6 = get_interface_subnetv6($dhcpv6if); + $subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6); + + if ($is_olsr_enabled == true) { + if ($dhcpv6ifconf['netmask']) { + $subnetmask = gen_subnet_maskv6($dhcpv6ifconf['netmask']); + } + } + + $dnscfgv6 = ""; + + if ($dhcpv6ifconf['domain']) { + $dnscfgv6 .= " option domain-name \"{$dhcpv6ifconf['domain']}\";\n"; + } + + if ($dhcpv6ifconf['domainsearchlist'] <> "") { + $dnscfgv6 .= " option domain-search \"" . join("\",\"", preg_split("/[ ;]+/", $dhcpv6ifconf['domainsearchlist'])) . "\";\n"; + } + + if (isset($dhcpv6ifconf['ddnsupdate'])) { + if ($dhcpv6ifconf['ddnsdomain'] <> "") { + $dnscfgv6 .= " ddns-domainname \"{$dhcpv6ifconf['ddnsdomain']}\";\n"; + } + $dnscfgv6 .= " ddns-update-style interim;\n"; + $nsupdate = true; + } + + if (is_array($dhcpv6ifconf['dnsserver']) && ($dhcpv6ifconf['dnsserver'][0])) { + $dnscfgv6 .= " option dhcp6.name-servers " . join(",", $dhcpv6ifconf['dnsserver']) . ";"; + } else if (((isset($config['dnsmasq']['enable'])) || isset($config['unbound']['enable'])) && (is_ipaddrv6($ifcfgipv6))) { + $dnscfgv6 .= " option dhcp6.name-servers {$ifcfgipv6};"; + } else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) { + $dns_arrv6 = array(); + foreach ($syscfg['dnsserver'] as $dnsserver) { + if (is_ipaddrv6($dnsserver)) { + $dns_arrv6[] = $dnsserver; + } + } + if (!empty($dns_arrv6)) { + $dnscfgv6 .= " option dhcp6.name-servers " . join(",", $dns_arrv6) . ";"; + } + } + + if ($dhcpv6ifconf['domain']) { + $newzone = array(); + $newzone['domain-name'] = $dhcpv6ifconf['domain']; + $newzone['dns-servers'][] = $dhcpv6ifconf['ddnsdomainprimary']; + $ddns_zones[] = $newzone; + } + + if (is_ipaddrv6($ifcfgipv6)) { + $dhcpdv6conf .= "subnet6 {$subnetv6}/{$ifcfgsnv6}"; + } else { + $subnet6 = gen_subnetv6($dhcpv6ifconf['range']['from'], "64"); + $dhcpdv6conf .= "subnet6 {$subnet6}/64"; + } + $dhcpdv6conf .= " {\n"; + + $dhcpdv6conf .= <<<EOD + range6 {$dhcpv6ifconf['range']['from']} {$dhcpv6ifconf['range']['to']}; +$dnscfgv6 + +EOD; + + if (is_ipaddrv6($dhcpv6ifconf['prefixrange']['from']) && is_ipaddrv6($dhcpv6ifconf['prefixrange']['to'])) { + $dhcpdv6conf .= " prefix6 {$dhcpv6ifconf['prefixrange']['from']} {$dhcpv6ifconf['prefixrange']['to']} /{$dhcpv6ifconf['prefixrange']['prefixlength']};\n"; + } + if (is_ipaddrv6($dhcpv6ifconf['dns6ip'])) { + $dhcpdv6conf .= " option dhcp6.name-servers {$dhcpv6ifconf['dns6ip']};\n"; + } + // default-lease-time + if ($dhcpv6ifconf['defaultleasetime']) { + $dhcpdv6conf .= " default-lease-time {$dhcpv6ifconf['defaultleasetime']};\n"; + } + + // max-lease-time + if ($dhcpv6ifconf['maxleasetime']) { + $dhcpdv6conf .= " max-lease-time {$dhcpv6ifconf['maxleasetime']};\n"; + } + + // ntp-servers + if (is_array($dhcpv6ifconf['ntpserver']) && $dhcpv6ifconf['ntpserver'][0]) { + $ntpservers = array(); + foreach ($dhcpv6ifconf['ntpserver'] as $ntpserver) { + if (is_ipaddrv6($ntpserver)) { + $ntpservers[] = $ntpserver; + } + } + if (count($ntpservers) > 0) { + $dhcpdv6conf .= " option dhcp6.sntp-servers " . join(",", $dhcpv6ifconf['ntpserver']) . ";\n"; + } + } + // tftp-server-name + /* Needs ISC DHCPD support + if ($dhcpv6ifconf['tftp'] <> "") { + $dhcpdv6conf .= " option tftp-server-name \"{$dhcpv6ifconf['tftp']}\";\n"; + } + */ + + // Handle option, number rowhelper values + $dhcpdv6conf .= "\n"; + if ($dhcpv6ifconf['numberoptions']['item']) { + foreach ($dhcpv6ifconf['numberoptions']['item'] as $itemv6idx => $itemv6) { + $dhcpdv6conf .= " option custom-{$dhcpv6if}-{$itemv6idx} \"{$itemv6['value']}\";\n"; + } + } + + // ldap-server + if ($dhcpv6ifconf['ldap'] <> "") { + $dhcpdv6conf .= " option ldap-server \"{$dhcpv6ifconf['ldap']}\";\n"; + } + + // net boot information + if (isset($dhcpv6ifconf['netboot'])) { + if (!empty($dhcpv6ifconf['bootfile_url'])) { + $dhcpdv6conf .= " option dhcp6.bootfile-url \"{$dhcpv6ifconf['bootfile_url']}\";\n"; + } + } + + $dhcpdv6conf .= "}\n"; + + /* add static mappings */ + /* Needs to use DUID */ + if (is_array($dhcpv6ifconf['staticmap'])) { + $i = 0; + foreach ($dhcpv6ifconf['staticmap'] as $sm) { + $dhcpdv6conf .= <<<EOD +host s_{$dhcpv6if}_{$i} { + host-identifier option dhcp6.client-id {$sm['duid']}; + +EOD; + if ($sm['ipaddrv6']) { + $dhcpdv6conf .= " fixed-address6 {$sm['ipaddrv6']};\n"; + } + + if ($sm['hostname']) { + $dhhostname = str_replace(" ", "_", $sm['hostname']); + $dhhostname = str_replace(".", "_", $dhhostname); + $dhcpdv6conf .= " option host-name {$dhhostname};\n"; + } + if ($sm['filename']) { + $dhcpdv6conf .= " filename \"{$sm['filename']}\";\n"; + } + + if ($sm['rootpath']) { + $dhcpdv6conf .= " option root-path \"{$sm['rootpath']}\";\n"; + } + + $dhcpdv6conf .= "}\n"; + $i++; + } + } + + if ($dhcpv6ifconf['domain']) { + $dhcpdv6conf .= dhcpdkey($dhcpv6ifconf); + $dhcpdv6conf .= dhcpdzones($ddns_zones, $dhcpv6ifconf); + } + + if ($config['dhcpdv6'][$dhcpv6if]['ramode'] <> "unmanaged" && isset($config['interfaces'][$dhcpv6if]['enable'])) { + if (preg_match("/poes/si", $dhcpv6if)) { + /* magic here */ + $dhcpdv6ifs = array_merge($dhcpdv6ifs, get_pppoes_child_interfaces($dhcpv6if)); + } else { + $realif = get_real_interface($dhcpv6if, "inet6"); + if (stristr("$realif", "bridge")) { + $mac = get_interface_mac($realif); + $v6address = generate_ipv6_from_mac($mac); + /* Create link local address for bridges */ + mwexec("/sbin/ifconfig {$realif} inet6 {$v6address}"); + } + $realif = escapeshellcmd($realif); + $dhcpdv6ifs[] = $realif; + } + } + } + + if ($nsupdate) { + $dhcpdv6conf .= "ddns-update-style interim;\n"; + } else { + $dhcpdv6conf .= "ddns-update-style none;\n"; + } + + /* write dhcpdv6.conf */ + if (!@file_put_contents("{$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf", $dhcpdv6conf)) { + log_error("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n"); + if (platform_booting()) { + printf("Error: cannot open {$g['dhcpd_chroot_path']}/etc/dhcpdv6.conf in services_dhcpdv6_configure().\n"); + } + unset($dhcpdv6conf); + return 1; + } + unset($dhcpdv6conf); + + /* create an empty leases v6 database */ + if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases")) { + @touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases"); + } + + /* make sure there isn't a stale dhcpdv6.pid file, which may make dhcpdv6 fail to start. */ + /* if we get here, dhcpdv6 has been killed and is not started yet */ + unlink_if_exists("{$g['dhcpd_chroot_path']}{$g['varrun_path']}/dhcpdv6.pid"); + + /* fire up dhcpd in a chroot */ + if (count($dhcpdv6ifs) > 0) { + mwexec("/usr/local/sbin/dhcpd -6 -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf /etc/dhcpdv6.conf -pf {$g['varrun_path']}/dhcpdv6.pid " . + join(" ", $dhcpdv6ifs)); + mwexec("/usr/local/sbin/dhcpleases6 -c \"/usr/local/bin/php-cgi -f /usr/local/sbin/prefixes.php|/bin/sh\" -l {$g['dhcpd_chroot_path']}/var/db/dhcpd6.leases"); + } + if (platform_booting()) { + print gettext("done.") . "\n"; + } + + return 0; +} + +function services_igmpproxy_configure() { + global $config, $g; + + /* kill any running igmpproxy */ + killbyname("igmpproxy"); + + if (!is_array($config['igmpproxy']['igmpentry']) || (count($config['igmpproxy']['igmpentry']) == 0)) { + return 1; + } + + $iflist = get_configured_interface_list(); + + $igmpconf = <<<EOD + +##------------------------------------------------------ +## Enable Quickleave mode (Sends Leave instantly) +##------------------------------------------------------ +quickleave + +EOD; + + foreach ($config['igmpproxy']['igmpentry'] as $igmpcf) { + unset($iflist[$igmpcf['ifname']]); + $realif = get_real_interface($igmpcf['ifname']); + if (empty($igmpcf['threshold'])) { + $threshld = 1; + } else { + $threshld = $igmpcf['threshold']; + } + $igmpconf .= "phyint {$realif} {$igmpcf['type']} ratelimit 0 threshold {$threshld}\n"; + + if ($igmpcf['address'] <> "") { + $item = explode(" ", $igmpcf['address']); + foreach ($item as $iww) { + $igmpconf .= "altnet {$iww}\n"; + } + } + $igmpconf .= "\n"; + } + foreach ($iflist as $ifn) { + $realif = get_real_interface($ifn); + $igmpconf .= "phyint {$realif} disabled\n"; + } + $igmpconf .= "\n"; + + $igmpfl = fopen($g['tmp_path'] . "/igmpproxy.conf", "w"); + if (!$igmpfl) { + log_error(gettext("Could not write Igmpproxy configuration file!")); + return; + } + fwrite($igmpfl, $igmpconf); + fclose($igmpfl); + unset($igmpconf); + + /* NOTE: -d4 means everything LOG_WARNING and smaller */ + mwexec("/usr/local/sbin/igmpproxy -d4 -c {$g['tmp_path']}/igmpproxy.conf"); + log_error(gettext("Started IGMP proxy service.")); + + return 0; +} + +function services_dhcrelay_configure() { + global $config, $g; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "services_dhcrelay_configure() being called $mt\n"; + } + + /* kill any running dhcrelay */ + killbypid("{$g['varrun_path']}/dhcrelay.pid"); + + $dhcrelaycfg =& $config['dhcrelay']; + + /* DHCPRelay enabled on any interfaces? */ + if (!isset($dhcrelaycfg['enable'])) { + return 0; + } + + if (platform_booting()) { + echo gettext("Starting DHCP relay service..."); + } else { + sleep(1); + } + + $iflist = get_configured_interface_list(); + + $dhcifaces = explode(",", $dhcrelaycfg['interface']); + foreach ($dhcifaces as $dhcrelayif) { + if (!isset($iflist[$dhcrelayif]) || + link_interface_to_bridge($dhcrelayif)) { + continue; + } + + if (is_ipaddr(get_interface_ip($dhcrelayif))) { + $dhcrelayifs[] = get_real_interface($dhcrelayif); + } + } + + $srvips = explode(",", $dhcrelaycfg['server']); + if (!is_array($srvips)) { + log_error("No destination IP has been configured!"); + return; + } + + $dhcrelayifs = array_unique($dhcrelayifs); + + /* fire up dhcrelay */ + if (empty($dhcrelayifs)) { + log_error("No suitable interface found for running dhcrelay!"); + return; /* XXX */ + } + + $cmd = "/usr/local/sbin/dhcrelay -i " . implode(" -i ", $dhcrelayifs); + + if (isset($dhcrelaycfg['agentoption'])) { + $cmd .= " -a -m replace"; + } + + $cmd .= " " . implode(" ", $srvips); + mwexec($cmd); + unset($cmd); + + return 0; +} + +function services_dhcrelay6_configure() { + global $config, $g; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "services_dhcrelay6_configure() being called $mt\n"; + } + + /* kill any running dhcrelay */ + killbypid("{$g['varrun_path']}/dhcrelay6.pid"); + + $dhcrelaycfg =& $config['dhcrelay6']; + + /* DHCPv6 Relay enabled on any interfaces? */ + if (!isset($dhcrelaycfg['enable'])) { + return 0; + } + + if (platform_booting()) { + echo gettext("Starting DHCPv6 relay service..."); + } else { + sleep(1); + } + + $iflist = get_configured_interface_list(); + + $dhcifaces = explode(",", $dhcrelaycfg['interface']); + foreach ($dhcifaces as $dhcrelayif) { + if (!isset($iflist[$dhcrelayif]) || + link_interface_to_bridge($dhcrelayif)) { + continue; + } + + if (is_ipaddrv6(get_interface_ipv6($dhcrelayif))) { + $dhcrelayifs[] = get_real_interface($dhcrelayif); + } + } + $dhcrelayifs = array_unique($dhcrelayifs); + + $srvips = explode(",", $dhcrelaycfg['server']); + if (!is_array($srvips)) { + log_error("No destination IP has been configured!"); + return; + } + + /* fire up dhcrelay */ + if (empty($dhcrelayifs) || empty($srvifaces)) { + log_error("No suitable interface found for running dhcrelay -6!"); + return; /* XXX */ + } + + $cmd = "/usr/local/sbin/dhcrelay -6 -pf \"{$g['varrun_path']}/dhcrelay6.pid\""; + foreach ($dhcrelayifs as $dhcrelayif) { + $cmd .= " -l {$dhcrelayif}"; + } + foreach ($srvifaces as $srviface) { + $cmd .= " -u \"{$srviface}\""; + } + mwexec($cmd); + unset($cmd); + + return 0; +} + +function services_dyndns_configure_client($conf) { + + if (!isset($conf['enable'])) { + return; + } + + /* load up the dyndns.class */ + require_once("dyndns.class"); + + $dns = new updatedns($dnsService = $conf['type'], + $dnsHost = $conf['host'], + $dnsUser = $conf['username'], + $dnsPass = $conf['password'], + $dnsWildcard = $conf['wildcard'], + $dnsMX = $conf['mx'], + $dnsIf = "{$conf['interface']}", + $dnsBackMX = NULL, + $dnsServer = NULL, + $dnsPort = NULL, + $dnsUpdateURL = "{$conf['updateurl']}", + $forceUpdate = $conf['force'], + $dnsZoneID=$conf['zoneid'], + $dnsTTL=$conf['ttl'], + $dnsResultMatch = "{$conf['resultmatch']}", + $dnsRequestIf = "{$conf['requestif']}", + $dnsID = "{$conf['id']}", + $dnsVerboseLog = $conf['verboselog'], + $curlIpresolveV4 = $conf['curl_ipresolve_v4'], + $curlSslVerifypeer = $conf['curl_ssl_verifypeer']); +} + +function services_dyndns_configure($int = "") { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "services_dyndns_configure() being called $mt\n"; + } + + $dyndnscfg = $config['dyndnses']['dyndns']; + $gwgroups = return_gateway_groups_array(); + if (is_array($dyndnscfg)) { + if (platform_booting()) { + echo gettext("Starting DynDNS clients..."); + } + + foreach ($dyndnscfg as $dyndns) { + if ((empty($int)) || ($int == $dyndns['interface']) || (is_array($gwgroups[$dyndns['interface']]))) { + $dyndns['verboselog'] = isset($dyndns['verboselog']); + $dyndns['curl_ipresolve_v4'] = isset($dyndns['curl_ipresolve_v4']); + $dyndns['curl_ssl_verifypeer'] = isset($dyndns['curl_ssl_verifypeer']); + services_dyndns_configure_client($dyndns); + sleep(1); + } + } + + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + } + + return 0; +} + +function dyndnsCheckIP($int) { + global $config; + $ip_address = get_interface_ip($int); + if (is_private_ip($ip_address)) { + $gateways_status = return_gateways_status(true); + // If the gateway for this interface is down, then the external check cannot work. + // Avoid the long wait for the external check to timeout. + if (stristr($gateways_status[$config['interfaces'][$int]['gateway']]['status'], "down")) { + return "down"; + } + $hosttocheck = "http://checkip.dyndns.org"; + $ip_ch = curl_init($hosttocheck); + curl_setopt($ip_ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ip_ch, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_setopt($ip_ch, CURLOPT_INTERFACE, 'host!' . $ip_address); + curl_setopt($ip_ch, CURLOPT_CONNECTTIMEOUT, '30'); + curl_setopt($ip_ch, CURLOPT_TIMEOUT, 120); + curl_setopt($ip_ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + $ip_result_page = curl_exec($ip_ch); + curl_close($ip_ch); + $ip_result_decoded = urldecode($ip_result_page); + preg_match('=Current IP Address: (.*)</body>=siU', $ip_result_decoded, $matches); + $ip_address = trim($matches[1]); + } + return $ip_address; +} + +function services_dnsmasq_configure() { + global $config, $g; + $return = 0; + + // hard coded args: will be removed to avoid duplication if specified in custom_options + $standard_args = array( + "dns-forward-max" => "--dns-forward-max=5000", + "cache-size" => "--cache-size=10000", + "local-ttl" => "--local-ttl=1" + ); + + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "services_dnsmasq_configure() being called $mt\n"; + } + + /* kill any running dnsmasq */ + if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) { + sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM"); + } + + if (isset($config['dnsmasq']['enable'])) { + + if (platform_booting()) { + echo gettext("Starting DNS forwarder..."); + } else { + sleep(1); + } + + /* generate hosts file */ + if (system_hosts_generate()!=0) { + $return = 1; + } + + $args = ""; + + if (isset($config['dnsmasq']['regdhcp'])) { + $args .= " --dhcp-hostsfile={$g['varetc_path']}/hosts "; + } + + /* Setup listen port, if non-default */ + if (is_port($config['dnsmasq']['port'])) { + $args .= " --port={$config['dnsmasq']['port']} "; + } + + $listen_addresses = ""; + if (isset($config['dnsmasq']['interface'])) { + $interfaces = explode(",", $config['dnsmasq']['interface']); + foreach ($interfaces as $interface) { + if (is_ipaddrv4($interface)) { + $listen_addresses .= " --listen-address={$interface} "; + } else if (is_ipaddrv6($interface)) { + /* + * XXX: Since dnsmasq does not support link-local address + * with scope specified. These checks are being done. + */ + if (is_linklocal($interface) && strstr($interface, "%")) { + $tmpaddrll6 = explode("%", $interface); + $listen_addresses .= " --listen-address={$tmpaddrll6[0]} "; + } else { + $listen_addresses .= " --listen-address={$interface} "; + } + } else if (strstr($interface, "_vip")) { + $laddr = get_configured_carp_interface_list($interface); + if (is_ipaddr($laddr)) { + $listen_addresses .= " --listen-address={$laddr} "; + } + } else { + $if = get_real_interface($interface); + if (does_interface_exist($if)) { + $laddr = get_interface_ip($interface); + if (is_ipaddrv4($laddr)) { + $listen_addresses .= " --listen-address={$laddr} "; + } + $laddr6 = get_interface_ipv6($interface); + if (is_ipaddrv6($laddr6) && !isset($config['dnsmasq']['strictbind'])) { + /* + * XXX: Since dnsmasq does not support link-local address + * with scope specified. These checks are being done. + */ + if (is_linklocal($laddr6) && strstr($laddr6, "%")) { + $tmpaddrll6 = explode("%", $laddr6); + $listen_addresses .= " --listen-address={$tmpaddrll6[0]} "; + } else { + $listen_addresses .= " --listen-address={$laddr6} "; + } + } + } + } + } + if (!empty($listen_addresses)) { + $args .= " {$listen_addresses} "; + if (isset($config['dnsmasq']['strictbind'])) { + $args .= " --bind-interfaces "; + } + } + } + + /* If selected, then first forward reverse lookups for private IPv4 addresses to nowhere. */ + /* Only make entries for reverse domains that do not have a matching domain override. */ + if (isset($config['dnsmasq']['no_private_reverse'])) { + /* Note: Carrier Grade NAT (CGN) addresses 100.64.0.0/10 are intentionally not here. */ + /* End-users should not be aware of CGN addresses, so reverse lookups for these should not happen. */ + /* Just the pfSense WAN might get a CGN address from an ISP. */ + + // Build an array of domain overrides to help in checking for matches. + $override_a = array(); + if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) { + foreach ($config['dnsmasq']['domainoverrides'] as $override) { + $override_a[$override['domain']] = "y"; + } + } + + // Build an array of the private reverse lookup domain names + $reverse_domain_a = array("10.in-addr.arpa", "168.192.in-addr.arpa"); + // Unfortunately the 172.16.0.0/12 range does not map nicely to the in-addr.arpa scheme. + for ($subnet_num = 16; $subnet_num < 32; $subnet_num++) { + $reverse_domain_a[] = "$subnet_num.172.in-addr.arpa"; + } + + // Set the --server parameter to nowhere for each reverse domain name that was not specifically specified in a domain override. + foreach ($reverse_domain_a as $reverse_domain) { + if (!isset($override_a[$reverse_domain])) { + $args .= " --server=/$reverse_domain/ "; + } + } + unset($override_a); + unset($reverse_domain_a); + } + + /* Setup forwarded domains */ + if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) { + foreach ($config['dnsmasq']['domainoverrides'] as $override) { + if ($override['ip'] == "!") { + $override[ip] = ""; + } + $args .= ' --server=/' . $override['domain'] . '/' . $override['ip']; + } + } + + /* Allow DNS Rebind for forwarded domains */ + if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) { + if (!isset($config['system']['webgui']['nodnsrebindcheck'])) { + foreach ($config['dnsmasq']['domainoverrides'] as $override) { + $args .= ' --rebind-domain-ok=/' . $override['domain'] . '/ '; + } + } + } + + if (!isset($config['system']['webgui']['nodnsrebindcheck'])) { + $dns_rebind = "--rebind-localhost-ok --stop-dns-rebind"; + } + + if (isset($config['dnsmasq']['strict_order'])) { + $args .= " --strict-order "; + } + + if (isset($config['dnsmasq']['domain_needed'])) { + $args .= " --domain-needed "; + } + + if ($config['dnsmasq']['custom_options']) { + foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) { + $args .= " " . escapeshellarg("--{$c}"); + $p = explode('=', $c); + if (array_key_exists($p[0], $standard_args)) { + unset($standard_args[$p[0]]); + } + } + } + $args .= ' ' . implode(' ', array_values($standard_args)); + + /* run dnsmasq */ + $cmd = "/usr/local/sbin/dnsmasq --all-servers {$dns_rebind} {$args}"; + //log_error("dnsmasq command: {$cmd}"); + mwexec_bg($cmd); + unset($args); + + system_dhcpleases_configure(); + + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + } + + if (!platform_booting()) { + if (services_dhcpd_configure()!=0) { + $return = 1; + } + } + + return $return; +} + +function services_unbound_configure() { + global $config, $g; + $return = 0; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "services_unbound_configure() being called $mt\n"; + } + + // kill any running Unbound instance + if (file_exists("{$g['varrun_path']}/unbound.pid")) { + sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM"); + } + + if (isset($config['unbound']['enable'])) { + if (platform_booting()) { + echo gettext("Starting DNS Resolver..."); + } else { + sleep(1); + } + + /* generate hosts file */ + if (system_hosts_generate()!=0) { + $return = 1; + } + + require_once('/etc/inc/unbound.inc'); + sync_unbound_service(); + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + + system_dhcpleases_configure(); + } + + if (!platform_booting()) { + if (services_dhcpd_configure()!=0) { + $return = 1; + } + } + + return $return; +} + +function services_snmpd_configure() { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "services_snmpd_configure() being called $mt\n"; + } + + /* kill any running snmpd */ + sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM"); + sleep(2); + if (is_process_running("bsnmpd")) { + mwexec("/usr/bin/killall bsnmpd", true); + } + + if (isset($config['snmpd']['enable'])) { + + if (platform_booting()) { + echo gettext("Starting SNMP daemon... "); + } + + /* generate snmpd.conf */ + $fd = fopen("{$g['varetc_path']}/snmpd.conf", "w"); + if (!$fd) { + printf(gettext("Error: cannot open snmpd.conf in services_snmpd_configure().%s"),"\n"); + return 1; + } + + + $snmpdconf = <<<EOD +location := "{$config['snmpd']['syslocation']}" +contact := "{$config['snmpd']['syscontact']}" +read := "{$config['snmpd']['rocommunity']}" + +EOD; + +/* No docs on what write strings do there for disable for now. + if (isset($config['snmpd']['rwenable']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) { + $snmpdconf .= <<<EOD +# write string +write := "{$config['snmpd']['rwcommunity']}" + +EOD; + } +*/ + + + if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) { + $snmpdconf .= <<<EOD +# SNMP Trap support. +traphost := {$config['snmpd']['trapserver']} +trapport := {$config['snmpd']['trapserverport']} +trap := "{$config['snmpd']['trapstring']}" + + +EOD; + } + + $platform = trim(file_get_contents('/etc/platform')); + if (($platform == "pfSense") && ($g['product_name'] != "pfSense")) { + $platform = $g['product_name']; + } + $sysDescr = "{$g['product_name']} " . php_uname("n") . + " {$g['product_version']} {$platform} " . php_uname("s") . + " " . php_uname("r") . " " . php_uname("m"); + + $snmpdconf .= <<<EOD +system := 1 # pfSense +%snmpd +sysDescr = "{$sysDescr}" +begemotSnmpdDebugDumpPdus = 2 +begemotSnmpdDebugSyslogPri = 7 +begemotSnmpdCommunityString.0.1 = $(read) + +EOD; + +/* No docs on what write strings do there for disable for now. + if (isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])) { + $snmpdconf .= <<<EOD +begemotSnmpdCommunityString.0.2 = $(write) + +EOD; + } +*/ + + + if (isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])) { + $snmpdconf .= <<<EOD +begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4 +begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2 +begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap) + +EOD; + } + + + $snmpdconf .= <<<EOD +begemotSnmpdCommunityDisable = 1 + +EOD; + + if (isset($config['snmpd']['bindlan'])) { + $config['snmpd']['bindip'] = 'lan'; + unset($config['snmpd']['bindlan']); + } + $bind_to_ip = "0.0.0.0"; + if (isset($config['snmpd']['bindip'])) { + if (is_ipaddr($config['snmpd']['bindip'])) { + $bind_to_ip = $config['snmpd']['bindip']; + } else { + $if = get_real_interface($config['snmpd']['bindip']); + if (does_interface_exist($if)) { + $bind_to_ip = get_interface_ip($config['snmpd']['bindip']); + } + } + } + + if (is_port($config['snmpd']['pollport'])) { + $snmpdconf .= <<<EOD +begemotSnmpdPortStatus.{$bind_to_ip}.{$config['snmpd']['pollport']} = 1 + +EOD; + + } + + $snmpdconf .= <<<EOD +begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1 +begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4 + +# These are bsnmp macros not php vars. +sysContact = $(contact) +sysLocation = $(location) +sysObjectId = 1.3.6.1.4.1.12325.1.1.2.1.$(system) + +snmpEnableAuthenTraps = 2 + +EOD; + + if (is_array($config['snmpd']['modules'])) { + if (isset($config['snmpd']['modules']['mibii'])) { + $snmpdconf .= <<<EOD +begemotSnmpdModulePath."mibII" = "/usr/lib/snmp_mibII.so" + +EOD; + } + + if (isset($config['snmpd']['modules']['netgraph'])) { + $snmpdconf .= <<<EOD +begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so" +%netgraph +begemotNgControlNodeName = "snmpd" + +EOD; + } + + if (isset($config['snmpd']['modules']['pf'])) { + $snmpdconf .= <<<EOD +begemotSnmpdModulePath."pf" = "/usr/lib/snmp_pf.so" + +EOD; + } + + if (isset($config['snmpd']['modules']['hostres'])) { + /* XXX: hostres module crashes APU - ticket #4403 */ + $specplatform = system_identify_specific_platform(); + if ($specplatform['name'] == 'APU') { + log_error("'Host Resources' SNMP module was ignored because it can potentially crash system on APU boards"); + } else { + $snmpdconf .= <<<EOD +begemotSnmpdModulePath."hostres" = "/usr/lib/snmp_hostres.so" + +EOD; + } + unset($specplatform); + } + + if (isset($config['snmpd']['modules']['bridge'])) { + $snmpdconf .= <<<EOD +begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" +# config must end with blank line + +EOD; + } + if (isset($config['snmpd']['modules']['ucd'])) { + $snmpdconf .= <<<EOD +begemotSnmpdModulePath."ucd" = "/usr/local/lib/snmp_ucd.so" + +EOD; + } + if (isset($config['snmpd']['modules']['regex'])) { + $snmpdconf .= <<<EOD +begemotSnmpdModulePath."regex" = "/usr/local/lib/snmp_regex.so" + +EOD; + } + } + + fwrite($fd, $snmpdconf); + fclose($fd); + unset($snmpdconf); + + if (isset($config['snmpd']['bindlan'])) { + $bindlan = ""; + } + + /* run bsnmpd */ + mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" . + "{$bindlan} -p {$g['varrun_path']}/snmpd.pid"); + + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + } + + return 0; +} + +function services_dnsupdate_process($int = "", $updatehost = "", $forced = false) { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "services_dnsupdate_process() being called $mt\n"; + } + + /* Dynamic DNS updating active? */ + if (is_array($config['dnsupdates']['dnsupdate'])) { + $notify_text = ""; + foreach ($config['dnsupdates']['dnsupdate'] as $i => $dnsupdate) { + if (!isset($dnsupdate['enable'])) { + continue; + } + if (!empty($int) && $int != $dnsupdate['interface']) { + continue; + } + if (!empty($updatehost) && ($updatehost != $dnsupdate['host'])) { + continue; + } + + /* determine interface name */ + $if = get_real_interface($dnsupdate['interface']); + + if (isset($dnsupdate['usepublicip'])) { + $wanip = dyndnsCheckIP($dnsupdate['interface']); + } else { + $wanip = get_interface_ip($dnsupdate['interface']); + } + + $wanipv6 = get_interface_ipv6($dnsupdate['interface']); + $cacheFile = "{$g['conf_path']}/dyndns_{$dnsupdate['interface']}_rfc2136_" . escapeshellarg($dnsupdate['host']) . "_{$dnsupdate['server']}.cache"; + $currentTime = time(); + + if ($wanip || $wanipv6) { + $keyname = $dnsupdate['keyname']; + /* trailing dot */ + if (substr($keyname, -1) != ".") { + $keyname .= "."; + } + + $hostname = $dnsupdate['host']; + /* trailing dot */ + if (substr($hostname, -1) != ".") { + $hostname .= "."; + } + + /* write private key file + this is dumb - public and private keys are the same for HMAC-MD5, + but nsupdate insists on having both */ + $fd = fopen("{$g['varetc_path']}/K{$i}{$keyname}+157+00000.private", "w"); + $privkey = <<<EOD +Private-key-format: v1.2 +Algorithm: 157 (HMAC) +Key: {$dnsupdate['keydata']} + +EOD; + fwrite($fd, $privkey); + fclose($fd); + + /* write public key file */ + if ($dnsupdate['keytype'] == "zone") { + $flags = 257; + $proto = 3; + } else if ($dnsupdate['keytype'] == "host") { + $flags = 513; + $proto = 3; + } else if ($dnsupdate['keytype'] == "user") { + $flags = 0; + $proto = 2; + } + + $fd = fopen("{$g['varetc_path']}/K{$i}{$keyname}+157+00000.key", "w"); + fwrite($fd, "{$keyname} IN KEY {$flags} {$proto} 157 {$dnsupdate['keydata']}\n"); + fclose($fd); + + /* generate update instructions */ + $upinst = ""; + if (!empty($dnsupdate['server'])) { + $upinst .= "server {$dnsupdate['server']}\n"; + } + + if (file_exists($cacheFile)) { + list($cachedipv4, $cacheTimev4) = explode("|", file_get_contents($cacheFile)); + } + if (file_exists("{$cacheFile}.ipv6")) { + list($cachedipv6, $cacheTimev6) = explode("|", file_get_contents("{$cacheFile}.ipv6")); + } + + // 25 Days + $maxCacheAgeSecs = 25 * 24 * 60 * 60; + $need_update = false; + + conf_mount_rw(); + /* Update IPv4 if we have it. */ + if (is_ipaddrv4($wanip) && $dnsupdate['recordtype'] != "AAAA") { + if (($wanip != $cachedipv4) || (($currentTime - $cacheTimev4) > $maxCacheAgeSecs) || $forced) { + $upinst .= "update delete {$dnsupdate['host']}. A\n"; + $upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} A {$wanip}\n"; + $notify_text .= sprintf(gettext("DynDNS updated IP Address (A) for {$dnsupdate['host']} on %s (%s) to %s"), convert_real_interface_to_friendly_descr($if), $if, $wanip) . "\n"; + @file_put_contents($cacheFile, "{$wanip}|{$currentTime}"); + log_error("phpDynDNS: updating cache file {$cacheFile}: {$wanip}"); + $need_update = true; + } else { + log_error("phpDynDNS: Not updating {$dnsupdate['host']} A record because the IP address has not changed."); + } + } else { + @unlink($cacheFile); + } + + /* Update IPv6 if we have it. */ + if (is_ipaddrv6($wanipv6) && $dnsupdate['recordtype'] != "A") { + if (($wanipv6 != $cachedipv6) || (($currentTime - $cacheTimev6) > $maxCacheAgeSecs) || $forced) { + $upinst .= "update delete {$dnsupdate['host']}. AAAA\n"; + $upinst .= "update add {$dnsupdate['host']}. {$dnsupdate['ttl']} AAAA {$wanipv6}\n"; + $notify_text .= sprintf(gettext("DynDNS updated IPv6 Address (AAAA) for {$dnsupdate['host']} on %s (%s) to %s"), convert_real_interface_to_friendly_descr($if), $if, $wanipv6) . "\n"; + @file_put_contents("{$cacheFile}.ipv6", "{$wanipv6}|{$currentTime}"); + log_error("phpDynDNS: updating cache file {$cacheFile}.ipv6: {$wanipv6}"); + $need_update = true; + } else { + log_error("phpDynDNS: Not updating {$dnsupdate['host']} AAAA record because the IPv6 address has not changed."); + } + } else { + @unlink("{$cacheFile}.ipv6"); + } + conf_mount_ro(); + + $upinst .= "\n"; /* mind that trailing newline! */ + + if ($need_update) { + @file_put_contents("{$g['varetc_path']}/nsupdatecmds{$i}", $upinst); + unset($upinst); + /* invoke nsupdate */ + $cmd = "/usr/local/bin/nsupdate -k {$g['varetc_path']}/K{$i}{$keyname}+157+00000.key"; + if (isset($dnsupdate['usetcp'])) { + $cmd .= " -v"; + } + $cmd .= " {$g['varetc_path']}/nsupdatecmds{$i}"; + mwexec_bg($cmd); + unset($cmd); + } + } + } + if (!empty($notify_text)) { + notify_all_remote($notify_text); + } + } + + return 0; +} + +/* configure cron service */ +function configure_cron() { + global $g, $config; + + conf_mount_rw(); + /* preserve existing crontab entries */ + $crontab_contents = file("/etc/crontab", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + + for ($i = 0; $i < count($crontab_contents); $i++) { + $cron_item =& $crontab_contents[$i]; + if (strpos($cron_item, "# pfSense specific crontab entries") !== false) { + array_splice($crontab_contents, $i - 1); + break; + } + } + $crontab_contents = implode("\n", $crontab_contents) . "\n"; + + + if (is_array($config['cron']['item'])) { + $crontab_contents .= "#\n"; + $crontab_contents .= "# " . gettext("pfSense specific crontab entries") . "\n"; + $crontab_contents .= "# " .gettext("Created:") . " " . date("F j, Y, g:i a") . "\n"; + $crontab_contents .= "#\n"; + + if (isset($config['system']['proxyurl']) && !empty($config['system']['proxyurl'])) { + $http_proxy = $config['system']['proxyurl']; + if (isset($config['system']['proxyport']) && !empty($config['system']['proxyport'])) { + $http_proxy .= ':' . $config['system']['proxyport']; + } + $crontab_contents .= "HTTP_PROXY={$http_proxy}"; + } + + foreach ($config['cron']['item'] as $item) { + $crontab_contents .= "\n{$item['minute']}\t"; + $crontab_contents .= "{$item['hour']}\t"; + $crontab_contents .= "{$item['mday']}\t"; + $crontab_contents .= "{$item['month']}\t"; + $crontab_contents .= "{$item['wday']}\t"; + $crontab_contents .= "{$item['who']}\t"; + $crontab_contents .= "{$item['command']}"; + } + + $crontab_contents .= "\n#\n"; + $crontab_contents .= "# " . gettext("If possible do not add items to this file manually.") . "\n"; + $crontab_contents .= "# " . gettext("If you do so, this file must be terminated with a blank line (e.g. new line)") . "\n"; + $crontab_contents .= "#\n\n"; + } + + /* please maintain the newline at the end of file */ + file_put_contents("/etc/crontab", $crontab_contents); + unset($crontab_contents); + + /* do a HUP kill to force sync changes */ + sigkillbypid("{$g['varrun_path']}/cron.pid", "HUP"); + + conf_mount_ro(); +} + +function upnp_action ($action) { + global $g, $config; + switch ($action) { + case "start": + if (file_exists('/var/etc/miniupnpd.conf')) { + @unlink("{$g['varrun_path']}/miniupnpd.pid"); + mwexec_bg("/usr/local/sbin/miniupnpd -f /var/etc/miniupnpd.conf -P {$g['varrun_path']}/miniupnpd.pid"); + } + break; + case "stop": + killbypid("{$g['varrun_path']}/miniupnpd.pid"); + while ((int)exec("/bin/pgrep -a miniupnpd | wc -l") > 0) { + mwexec('killall miniupnpd 2>/dev/null', true); + } + mwexec('/sbin/pfctl -aminiupnpd -Fr 2>&1 >/dev/null'); + mwexec('/sbin/pfctl -aminiupnpd -Fn 2>&1 >/dev/null'); + break; + case "restart": + upnp_action('stop'); + upnp_action('start'); + break; + } +} + +function upnp_start() { + global $config; + + if (!isset($config['installedpackages']['miniupnpd']['config'])) { + return; + } + + if ($config['installedpackages']['miniupnpd']['config'][0]['enable']) { + echo gettext("Starting UPnP service... "); + require_once('/usr/local/pkg/miniupnpd.inc'); + sync_package_miniupnpd(); + echo "done.\n"; + } +} + +function install_cron_job($command, $active=false, $minute="0", $hour="*", $monthday="*", $month="*", $weekday="*", $who="root") { + global $config, $g; + + $is_installed = false; + + if (!is_array($config['cron'])) { + $config['cron'] = array(); + } + if (!is_array($config['cron']['item'])) { + $config['cron']['item'] = array(); + } + + $x=0; + foreach ($config['cron']['item'] as $item) { + if (strstr($item['command'], $command)) { + $is_installed = true; + break; + } + $x++; + } + + if ($active) { + $cron_item = array(); + $cron_item['minute'] = $minute; + $cron_item['hour'] = $hour; + $cron_item['mday'] = $monthday; + $cron_item['month'] = $month; + $cron_item['wday'] = $weekday; + $cron_item['who'] = $who; + $cron_item['command'] = $command; + if (!$is_installed) { + $config['cron']['item'][] = $cron_item; + write_config(sprintf(gettext("Installed cron job for %s"), $command)); + } else { + $config['cron']['item'][$x] = $cron_item; + write_config(sprintf(gettext("Updated cron job for %s"), $command)); + } + } else { + if ($is_installed == true) { + unset($config['cron']['item'][$x]); + write_config(sprintf(gettext("Removed cron job for %s"), $command)); + } + } + + if ($cron_changed) { + configure_cron(); + } +} + +?> diff --git a/src/etc/inc/shaper.inc b/src/etc/inc/shaper.inc new file mode 100644 index 0000000..f5c91b0 --- /dev/null +++ b/src/etc/inc/shaper.inc @@ -0,0 +1,5151 @@ +<?php +/* + shaper.inc +*/ +/* ==================================================================== + * Copyright (c) 2004-2015 Electric Sheep Fencing, LLC. All rights reserved. + * Copyright (c) 2004, 2005 Scott Ullrich + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution. (http://www.pfsense.org/). + * + * 4. The names "pfSense" and "pfSense Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * coreteam@pfsense.org. + * + * 5. Products derived from this software may not be called "pfSense" + * nor may "pfSense" appear in their names without prior written + * permission of the Electric Sheep Fencing, LLC. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * + * "This product includes software developed by the pfSense Project + * for use in the pfSense software distribution (http://www.pfsense.org/). + * + * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ==================================================================== + * + */ + +/* XXX: needs some reducing on include. */ +/* include all configuration functions. */ +require_once("globals.inc"); +require_once("functions.inc"); +require_once("util.inc"); +require_once("notices.inc"); + +/* + * I admit :) this is derived from xmlparse.inc StartElement() + */ +function &get_reference_to_me_in_config(&$mypath) { + global $config; + + $ptr =& $config['shaper']; + foreach ($mypath as $indeks) { + $ptr =& $ptr['queue'][$indeks]; + } + + return $ptr; +} + +function unset_object_by_reference(&$mypath) { + global $config; + + $ptr =& $config['shaper']; + for ($i = 0; $i < count($mypath) - 1; $i++) { + $ptr =& $ptr['queue'][$mypath[$i]]; + } + unset($ptr['queue'][$mypath[$i]]); +} + +function &get_dn_reference_to_me_in_config(&$mypath) { + global $config; + + $ptr =& $config['dnshaper']; + foreach ($mypath as $indeks) { + $ptr =& $ptr['queue'][$indeks]; + } + + return $ptr; +} + +function unset_dn_object_by_reference(&$mypath) { + global $config; + + $ptr =& $config['dnshaper']; + for ($i = 0; $i < count($mypath) - 1; $i++) { + $ptr =& $ptr['queue'][$mypath[$i]]; + } + unset($ptr['queue'][$mypath[$i]]); +} + +function clean_child_queues($type, $mypath) { + $ref = &get_reference_to_me_in_config($mypath); + + switch ($type) { + case 'HFSC': + if (isset($ref['borrow'])) { + unset($ref['borrow']); + } + if (isset($ref['hogs'])) { + unset($ref['hogs']); + } + if (isset($ref['buckets'])) { + unset($ref['buckets']); + } + break; + case 'PRIQ': + if (isset($ref['borrow'])) { + unset($ref['borrow']); + } + if (isset($ref['bandwidth'])) { + unset($ref['bandwidth']); + } + if (isset($ref['bandwidthtype'])) { + unset($ref['bandwidthtype']); + } + /* fall through */ + case 'FAIRQ': + if (isset($ref['borrow'])) { + unset($ref['borrow']); + } + /* fall through */ + case 'CBQ': + if (isset($ref['realtime'])) { + unset($ref['realtime']); + } + if (isset($ref['realtime1'])) { + unset($ref['realtime1']); + } + if (isset($ref['realtime2'])) { + unset($ref['realtime2']); + } + if (isset($ref['realtime3'])) { + unset($ref['realtime3']); + } + if (isset($ref['upperlimit'])) { + unset($ref['upperlimit']); + } + if (isset($ref['upperlimit1'])) { + unset($ref['upperlimit1']); + } + if (isset($ref['upperlimit2'])) { + unset($ref['upperlimit2']); + } + if (isset($ref['upperlimit3'])) { + unset($ref['upperlimit3']); + } + if (isset($ref['linkshare'])) { + unset($ref['linkshare']); + } + if (isset($ref['linkshare1'])) { + unset($ref['linkshare1']); + } + if (isset($ref['linkshare2'])) { + unset($ref['linkshare2']); + } + if (isset($ref['linkshare3'])) { + unset($ref['linkshare3']); + } + if (isset($ref['hogs'])) { + unset($ref['hogs']); + } + if (isset($ref['buckets'])) { + unset($ref['buckets']); + } + break; + } +} + +function get_bandwidthtype_scale($type) { + switch ($type) { + case "Gb": + $factor = 1024 * 1024 * 1024; + break; + case "Mb": + $factor = 1024 * 1024; + break; + case "Kb": + $factor = 1024; + break; + case "b": + default: + $factor = 1; + break; + } + return intval($factor); +} + +function get_hfsc_bandwidth($object, $bw) { + $pattern= "/[0-9]+/"; + if (preg_match($pattern, $bw, $match)) { + $bw_1 = $match[1]; + } else { + return 0; + } + $pattern= "/(b|Kb|Mb|Gb|%)/"; + if (preg_match($pattern, $bw, $match)) { + switch ($match[1]) { + case '%': + $bw_1 = $bw_1 / 100 * get_interface_bandwidth($object); + break; + default: + $bw_1 = $bw_1 * get_bandwidthtype_scale($match[0]); + break; + } + return floatval($bw_1); + } else { + return 0; + } +} + +function get_interface_bandwidth($object) { + global $altq_list_queues; + + $int = $object->GetInterface(); + $altq =& $altq_list_queues[$int]; + if ($altq) { + $bw_3 = $altq->GetBandwidth(); + $bw_3 = $bw_3 * get_bandwidthtype_scale($altq->GetBwscale()); + return floatval($bw_3); + } else { + return 0; + } +} + +/* + * This is duplicated here since we cannot include guiconfig.inc. + * Including it makes all stuff break. + */ +function shaper_do_input_validation($postdata, $reqdfields, $reqdfieldsn, $input_errors) { + + /* check for bad control characters */ + foreach ($postdata as $pn => $pd) { + if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) { + $input_errors[] = sprintf(gettext("The field '%s' contains invalid characters."), $pn); + } + } + + for ($i = 0; $i < count($reqdfields); $i++) { + if ($postdata[$reqdfields[$i]] == "") { + $input_errors[] = sprintf(gettext("The field '%s' is required."), $reqdfieldsn[$i]); + } + } +} + +function cleanup_queue_from_rules($queue) { + global $config; + + foreach ($config['filter']['rule'] as $rule) { + if ($rule['defaultqueue'] == $queue) { + unset($rule['defaultqueue']); + } + if ($rule['ackqueue'] == $queue) { + unset($rule['ackqueue']); + } + } +} + +function cleanup_dnqueue_from_rules($queue) { + global $config; + + foreach ($config['filter']['rule'] as $rule) { + if ($rule['dnpipe'] == $queue) { + unset($rule['dnpipe']); + } + if ($rule['pdnpipe'] == $queue) { + unset($rule['pdnpipe']); + } + } +} + +class altq_root_queue { + var $interface; + var $tbrconfig ; + var $bandwidth; + var $bandwidthtype; /* b, Kb, Mb */ + var $scheduler; + var $qlimit; + var $queues = array(); + var $qenabled = false; + var $link; + var $available_bw; /* in b/s */ + + /* Accessor functions */ + function GetAvailableBandwidth() { + return $this->available_bw; + } + function SetAvailableBandwidth($bw) { + $this->available_bw = $bw; + } + function GetDefaultQueuePresent() { + if (!empty($this->queues)) { + foreach ($this->queues as $q) { + if ($q->GetDefault()) { + return true; + } + } + } + + return false; + } + function SetLink($link) { + $this->link = $link; + } + function GetLink() { + return $this->link; + } + function GetEnabled() { + return $this->qenabled; + } + function SetEnabled($value) { + $this->qenabled = $value; + } + function CanHaveChildren() { + if ($this->GetScheduler() == "CODELQ") { + return false; + } else { + return true; + } + } + function CanBeDeleted() { + return false; + } + function GetQname() { + return $this->interface; + } + function SetQname($name) { + $this->interface = trim($name); + } + function GetInterface() { + return $this->interface; + } + function SetInterface($name) { + $this->interface = trim($name); + } + function GetTbrConfig() { + return $this->tbrconfig; + } + function SetTbrConfig($tbrconfig) { + $this->tbrconfig = $tbrconfig; + } + function GetBandwidth() { + return $this->bandwidth; + } + function SetBandwidth($bw) { + $this->bandwidth = $bw; + } + function GetBwscale() { + return $this->bandwidthtype; + } + function SetBwscale($bwscale) { + $this->bandwidthtype = $bwscale; + } + function GetScheduler() { + return $this->scheduler; + } + function SetScheduler($scheduler) { + $this->scheduler = trim($scheduler); + } + function GetQlimit() { + return $this->qlimit; + } + function SetQlimit($limit) { + $this->qlimit = $limit; + } + + function validate_input($data, &$input_errors) { + + $reqdfields[] = "bandwidth"; + $reqdfieldsn[] = gettext("Bandwidth"); + $reqdfields[] = "bandwidthtype"; + $reqdfieldsn[] = gettext("Bandwidthtype"); + + shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors); + + if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) { + $input_errors[] = gettext("Bandwidth must be an integer."); + } + if ($data['bandwidth'] < 0) { + $input_errors[] = gettext("Bandwidth cannot be negative."); + } + if ($data['qlimit'] && (!is_numeric($data['qlimit']))) { + $input_errors[] = gettext("Qlimit must be an integer."); + } + if ($data['qlimit'] < 0) { + $input_errors[] = gettext("Qlimit must be positive."); + } + if ($data['tbrconfig'] && (!is_numeric($data['tbrconfig']))) { + $input_errors[] = gettext("Tbrsize must be an integer."); + } + if ($data['tbrconfig'] < 0) { + $input_errors[] = gettext("Tbrsize must be positive."); + } + } + + /* Implement this to shorten some code on the frontend page */ + function ReadConfig(&$conf) { + if (isset($conf['tbrconfig'])) { + $this->SetTbrConfig($conf['tbrconfig']); + } else { + $this->SetTbrConfig($conf['tbrconfig']); + } + $this->SetBandwidth($conf['bandwidth']); + if ($conf['bandwidthtype'] <> "") { + $this->SetBwscale($conf['bandwidthtype']); + } + if (isset($conf['scheduler'])) { + if ($this->GetScheduler() != $conf['scheduler']) { + foreach ($this->queues as $q) { + clean_child_queues($conf['scheduler'], $this->GetLink()); + $q->clean_queue($conf['scheduler']); + } + } + $this->SetScheduler($conf['scheduler']); + } + if (isset($conf['qlimit']) && $conf['qlimit'] <> "") { + $this->SetQlimit($conf['qlimit']); + } else { + $this->SetQlimit(""); + } + if (isset($conf['name'])) { + $this->SetQname($conf['name']); + } + if (!empty($conf['enabled'])) { + $this->SetEnabled($conf['enabled']); + } else { + $this->SetEnabled(""); + } + } + + function copy_queue($interface, &$cflink) { + $cflink['interface'] = $interface; + $cflink['name'] = $interface; + $cflink['scheduler'] = $this->GetScheduler(); + $cflink['bandwidth'] = $this->GetBandwidth(); + $cflink['bandwidthtype'] = $this->GetBwscale(); + $cflink['qlimit'] = $this->GetQlimit(); + $cflink['tbrconfig'] = $this->GetTbrConfig(); + $cflink['enabled'] = $this->GetEnabled(); + if (is_array($this->queues)) { + $cflink['queue'] = array(); + foreach ($this->queues as $q) { + $cflink['queue'][$q->GetQname()] = array(); + $q->copy_queue($interface, $cflink['queue'][$q->GetQname()]); + } + } + } + + function &get_queue_list(&$q = null) { + $qlist = array(); + + //$qlist[$this->GetQname()] = & $this; + if (is_array($this->queues)) { + foreach ($this->queues as $queue) { + $queue->get_queue_list($qlist); + } + } + return $qlist; + } + + function &add_queue($interface, &$queue, &$path, &$input_errors) { + + if (!is_array($this->queues)) { + $this->queues = array(); + } + + switch ($this->GetScheduler()) { + case "PRIQ": + $q =& new priq_queue(); + break; + case "HFSC": + $q =& new hfsc_queue(); + break; + case "CBQ": + $q =& new cbq_queue(); + break; + case "FAIRQ": + $q =& new fairq_queue(); + break; + default: + /* XXX: but should not happen anyway */ + return; + break; + } + $q->SetLink($path); + $q->SetInterface($this->GetInterface()); + $q->SetEnabled("on"); + $q->SetParent($this); + $q->ReadConfig($queue); + $q->validate_input($queue, $input_errors); + if (count($input_errors)) { + log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true)); + return $q; + } + + if (isset($queue['bandwidth'])) { + switch ($queue['bandwidthtype']) { + case "%": + $myBw = $this->GetAvailableBandwidth() * $queue['bandwidth'] / 100; + break; + default: + $myBw = $queue['bandwidth'] * get_bandwidthtype_scale($queue['bandwidthtype']); + break; + } + } + $q->SetAvailableBandwidth($myBw); + $this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw); + $this->queues[$q->GetQname()] = &$q; + ref_on_altq_queue_list($this->GetQname(), $q->GetQname()); + if (is_array($queue['queue'])) { + foreach ($queue['queue'] as $key1 => $que) { + array_push($path, $key1); + $q->add_queue($q->GetInterface(), $que, $path, $input_errors); + array_pop($path); + } + } + + return $q; + } + + /* interface here might be optional */ + function &find_queue($interface, $qname) { + if ($qname == $this->GetQname()) { + return $this; + } + foreach ($this->queues as $q) { + $result =& $q->find_queue("", $qname); + if ($result) { + return $result; + } + } + } + + function &find_parentqueue($interface, $qname) { + if ($qname == $interface) { + $result = NULL; + } else if ($this->queues[$qname]) { + $result = $this; + } else if ($this->GetScheduler() <> "PRIQ") { + foreach ($this->queues as $q) { + $result = $q->find_parentqueue("", $qname); + if ($result) { + return $result; + } + } + } + } + + function build_tree() { + global $shaperIFlist; + + $tree = " <li><a href=\"firewall_shaper.php?interface=".$this->GetInterface()."&queue=". $this->GetInterface()."&action=show"; + $tree .= "\">" . $shaperIFlist[$this->GetInterface()] . "</a>"; + if (is_array($this->queues)) { + $tree .= "<ul>"; + foreach ($this->queues as $q) { + $tree .= $q->build_tree(); + } + $tree .= "</ul>"; + } + $tree .= "</li>"; + return $tree; + } + + function delete_queue() { + foreach ($this->queues as $q) { + $this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth()); + $q->delete_queue(); + } + unset_object_by_reference($this->GetLink()); + } + + function delete_all() { + if (count($this->queues)) { + foreach ($this->queues as $q) { + $q->delete_all(); + unset_object_by_reference($q->GetLink()); + unset($q); + } + unset($this->queues); + } + } + + /* + * First it spits: + * altq on $interface .............. + * then it goes like + * foreach ($queues as $qkey => $queue) { + * this->queues[$qkey]->build_rule(); + * } + */ + function build_rules(&$default = false) { + if (count($this->queues) > 0 && $this->GetEnabled() == "on") { + $default = false; + $rules = " altq on " . get_real_interface($this->GetInterface()); + if ($this->GetScheduler()) { + $rules .= " ".strtolower($this->GetScheduler()); + } + if ($this->GetQlimit() > 0) { + $rules .= " qlimit " . $this->GetQlimit() . " "; + } + if ($this->GetBandwidth()) { + $rules .= " bandwidth ".trim($this->GetBandwidth()); + if ($this->GetBwscale()) { + $rules .= $this->GetBwscale(); + } + } + if ($this->GetTbrConfig()) { + $rules .= " tbrsize ".$this->GetTbrConfig(); + } + if (count($this->queues)) { + $i = count($this->queues); + $rules .= " queue { "; + foreach ($this->queues as $qkey => $qnone) { + if ($i > 1) { + $i--; + $rules .= " {$qkey}, "; + } else { + $rules .= " {$qkey} "; + } + } + $rules .= " } \n"; + foreach ($this->queues as $q) { + $rules .= $q->build_rules($default); + } + } + + if ($default == false) { + $error = "SHAPER: no default queue specified for interface ". $this->GetInterface() . ". The interface queue will be enforced as default."; + file_notice("Shaper", $error, "Error occurred", ""); + unset($error); + return "\n"; + } + $frule .= $rules; + } else if ($this->GetEnabled() == "on" && $this->GetScheduler() == "CODELQ") { + $rules = " altq on " . get_real_interface($this->GetInterface()); + if ($this->GetScheduler()) { + $rules .= " ".strtolower($this->GetScheduler()); + } + if ($this->GetQlimit() > 0) { + $rules .= " ( qlimit " . $this->GetQlimit() . " ) "; + } + if ($this->GetBandwidth()) { + $rules .= " bandwidth ".trim($this->GetBandwidth()); + if ($this->GetBwscale()) { + $rules .= $this->GetBwscale(); + } + } + if ($this->GetTbrConfig()) { + $rules .= " tbrsize ".$this->GetTbrConfig(); + } + + $rules .= " queue"; + } + + $rules .= " \n"; + return $rules; + } + + function build_javascript() { + $javascript = "<script type=\"text/javascript\">"; + $javascript .= "//<![CDATA[\n"; + $javascript .= "function mySuspend() {"; + $javascript .= "if (document.layers && document.layers['shaperarea'] != null) "; + $javascript .= "document.layers['shaperarea'].visibility = 'hidden'; "; + $javascript .= "else if (document.all)"; + $javascript .= "document.all['shaperarea'].style.visibility = 'hidden';"; + $javascript .= "}"; + + $javascript .= "function myResume() {"; + $javascript .= "if (document.layers && document.layers['shaperarea'] != null) "; + $javascript .= "document.layers['shaperarea'].visibility = 'visible';"; + $javascript .= "else if (document.all) "; + $javascript .= "document.all['shaperarea'].style.visibility = 'visible';"; + $javascript .= "}"; + $javascript .= "//]]>"; + $javascript .= "</script>"; + + return $javascript; + } + + function build_shortform() { + global $g; + + $altq =& $this; + + if ($altq) { + $scheduler = ": " . $altq->GetScheduler(); + } + + $form = '<dl class="dl-horizontal">'; + $form .= ' <dt>'; + $form .= ' <a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&queue=' . $this->GetQname() . '&action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>'; + $form .= ' </dt>'; + $form .= ' <dd>'; + $form .= $scheduler; + $form .= ' </dd>'; + + $form .= ' <dt>'; + $form .= 'Bandwidth'; + $form .= ' </dt>'; + $form .= ' <dd>'; + $form .= $this->GetBandwidth() . ' ' . $this->GetBwscale(); + $form .= ' </dd>'; + + $form .= ' <dt>'; + $form .= 'Disable'; + $form .= ' <dt>'; + $form .= ' <dd>'; + + $form .= '<a class="btn btn-default btn-xs" href="firewall_shaper_queues.php?interface='; + $form .= $this->GetInterface() . '&queue='; + $form .= $this->GetQname() . '&action=delete">'; + $form .= gettext("Disable shaper on interface") . '</a>'; + + $form .= ' </dd>'; + + $form .= '</dl>'; + + return $form; + + } + + /* + * For requesting the parameters of the root queues + * to the user like the traffic wizard does. + */ + function build_form() { + + $sform = new Form(new Form_Button( + 'Submit', + 'Save' + )); + + $section = new Form_Section(null); + + $section->addInput(new Form_Checkbox( + 'enabled', + 'Enable/Disable', + 'Enable/disable discipline and its children', + ($this->GetEnabled() == "on"), + 'on' + )); + + $section->addInput(new Form_StaticText( + 'Name', + $this->GetQname() + )); + + $section->addInput(new Form_Select( + 'scheduler', + 'Scheduler Type', + $this->GetScheduler(), + array('HFSC' => 'HFSC', + 'CBQ' => 'CBQ', + 'FAIRQ' => 'FAIRQ', + 'CODELQ' => 'CODELQ', + 'PRIQ' => 'PRIQ') + ))->setHelp('Changing this changes all child queues! Beware you can lose information.'); + + $group = new Form_group('Bandwidth'); + + $group->add(new Form_Input( + 'bandwidth', + null, + 'number', + $this->GetBandwidth() + )); + + $group->add(new Form_Select( + 'bandwidthtype', + null, + $this->GetBwscale(), + array('Kb' => 'Kb', + 'Mb' => 'Mb', + 'Gb' => 'Gb', + 'b' => 'b') + )); + + $section->add($group); + + $section->addInput(new Form_Input( + 'qlimit', + 'Queue Limit', + 'number', + $this->GetQlimit() + )); + + $section->addInput(new Form_Input( + 'tbrconfig', + 'TRB Size', + 'number', + $this->GetTbrConfig() + ))->setHelp('Adjusts the size, in bytes, of the token bucket regulator. If not specified, heuristics based on the interface ' . + 'bandwidth are used to determine the size.'); + + $section->addInput(new Form_Input( + 'interface', + null, + 'hidden', + $this->GetInterface() + )); + + $section->addInput(new Form_Input( + 'name', + null, + 'hidden', + $this->GetQname() + )); + + $sform->add($section); + + return($sform); + } + + function update_altq_queue_data(&$data) { + $this->ReadConfig($data); + } + + /* + * Should call on each of it queues and subqueues + * the same function much like build_rules(); + */ + function wconfig() { + $cflink = &get_reference_to_me_in_config($this->GetLink()); + if (!is_array($cflink)) { + $cflink = array(); + } + $cflink['interface'] = $this->GetInterface(); + $cflink['name'] = $this->GetQname(); + $cflink['scheduler'] = $this->GetScheduler(); + $cflink['bandwidth'] = $this->GetBandwidth(); + $cflink['bandwidthtype'] = $this->GetBwscale(); + $cflink['qlimit'] = trim($this->GetQlimit()); + if (empty($cflink['qlimit'])) { + unset($cflink['qlimit']); + } + $cflink['tbrconfig'] = trim($this->GetTbrConfig()); + if (empty($cflink['tbrconfig'])) { + unset($cflink['tbrconfig']); + } + $cflink['enabled'] = $this->GetEnabled(); + if (empty($cflink['enabled'])) { + unset($cflink['enabled']); + } + } + +} + +class priq_queue { + var $qname; + var $qinterface; + var $qlimit; + var $qpriority; + var $description; + var $isparent; + var $qbandwidth; + var $qbandwidthtype; + var $qdefault = ""; + var $qrio = ""; + var $qred = ""; + var $qcodel = ""; + var $qecn = ""; + var $qack; + var $qenabled = ""; + var $qparent; + var $link; + var $available_bw; /* in b/s */ + + /* This is here to help with form building and building rules/lists */ + var $subqueues = array(); + + /* Accessor functions */ + function GetAvailableBandwidth() { + return $this->available_bw; + } + function SetAvailableBandwidth($bw) { + $this->available_bw = $bw; + } + function SetLink($link) { + $this->link = $link; + } + function GetLink() { + return $this->link; + } + function &GetParent() { + return $this->qparent; + } + function SetParent(&$parent) { + $this->qparent = &$parent; + } + function GetEnabled() { + return $this->qenabled; + } + function SetEnabled($value) { + $this->qenabled = $value; + } + function CanHaveChildren() { + return false; + } + function CanBeDeleted() { + return true; + } + function GetQname() { + return $this->qname; + } + function SetQname($name) { + $this->qname = trim($name); + } + function GetBandwidth() { + return $this->qbandwidth; + } + function SetBandwidth($bandwidth) { + $this->qbandwidth = $bandwidth; + } + function GetInterface() { + return $this->qinterface; + } + function SetInterface($name) { + $this->qinterface = trim($name); + } + function GetQlimit() { + return $this->qlimit; + } + function SetQlimit($limit) { + $this->qlimit = $limit; + } + function GetQpriority() { + return $this->qpriority; + } + function SetQpriority($priority) { + $this->qpriority = $priority; + } + function GetDescription() { + return $this->description; + } + function SetDescription($str) { + $this->description = trim($str); + } + function GetFirstime() { + return $this->firsttime; + } + function SetFirsttime($number) { + $this->firsttime = $number; + } + function GetBwscale() { + return $this->qbandwidthtype; + } + function SetBwscale($scale) { + $this->qbandwidthtype = $scale; + } + function GetDefaultQueuePresent() { + if ($this->GetDefault()) { + return true; + } + if (!empty($this->subqueues)) { + foreach ($this->subqueues as $q) { + if ($q->GetDefault()) { + return true; + } + } + } + + return false; + } + function GetDefault() { + return $this->qdefault; + } + function SetDefault($value = false) { + $this->qdefault = $value; + } + function GetCodel() { + return $this->codel; + } + function SetCodel($codel = false) { + $this->codel = $codel; + } + function GetRed() { + return $this->qred; + } + function SetRed($red = false) { + $this->qred = $red; + } + function GetRio() { + return $this->qrio; + } + function SetRio($rio = false) { + $this->qrio = $rio; + } + function GetEcn() { + return $this->qecn; + } + function SetEcn($ecn = false) { + $this->qecn = $ecn; + } + function GetAck() { + return $this->qack; + } + function SetAck($ack = false) { + $this->qack = $ack; + } + + function build_javascript() { + $javascript = "<script type=\"text/javascript\">"; + $javascript .= "//<![CDATA[\n"; + $javascript .= "function mySuspend() { \n"; + $javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n"; + $javascript .= "document.layers['shaperarea'].visibility = 'hidden';\n"; + $javascript .= "else if (document.all)\n"; + $javascript .= "document.all['shaperarea'].style.visibility = 'hidden';\n"; + $javascript .= "}\n"; + + $javascript .= "function myResume() {\n"; + $javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n"; + $javascript .= "document.layers['shaperarea'].visibility = 'visible';\n"; + $javascript .= "else if (document.all)\n"; + $javascript .= "document.all['shaperarea'].style.visibility = 'visible';\n"; + $javascript .= "}\n"; + $javascript .= "//]]>"; + $javascript .= "</script>"; + + return $javascript; + } + + function &add_queue($interface, &$qname, &$path, &$input_errors) { return; } + + /* + * Currently this will not be called unless we decide to clone a whole + * queue tree on the 'By Queues' view or support drag&drop on the tree/list + */ + function copy_queue($interface, &$cflink) { + + $cflink['name'] = $this->GetQname(); + $cflink['interface'] = $interface; + $cflink['qlimit'] = $this->GetQlimit(); + $cflink['priority'] = $this->GetQpriority(); + $cflink['description'] = $this->GetDescription(); + $cflink['enabled'] = $this->GetEnabled(); + $cflink['default'] = $this->GetDefault(); + $cflink['red'] = $this->GetRed(); + $cflink['codel'] = $this->GetCodel(); + $cflink['rio'] = $this->GetRio(); + $cflink['ecn'] = $this->GetEcn(); + + if (is_array($this->subqueues)) { + $cflinkp['queue'] = array(); + foreach ($this->subqueues as $q) { + $cflink['queue'][$q->GetQname()] = array(); + $q->copy_queue($interface, $cflink['queue'][$q->GetQname()]); + } + } + } + + function clean_queue($sched) { + clean_child_queues($sched, $this->GetLink()); + if (is_array($this->subqueues)) { + foreach ($this->subqueues as $q) { + $q->clean_queue($sched); + } + } + } + + function &get_queue_list(&$qlist) { + + $qlist[$this->GetQname()] = & $this; + if (is_array($this->subqueues)) { + foreach ($this->subqueues as $queue) { + $queue->get_queue_list($qlist); + } + } + } + + function delete_queue() { + unref_on_altq_queue_list($this->GetQname()); + cleanup_queue_from_rules($this->GetQname()); + unset_object_by_reference($this->GetLink()); + } + + function delete_all() { + if (count($this->subqueues)) { + foreach ($this->subqueues as $q) { + $q->delete_all(); + unset_object_by_reference($q->GetLink()); + unset($q); + } + unset($this->subqueues); + } + } + + function &find_queue($interface, $qname) { + if ($qname == $this->GetQname()) { + return $this; + } + } + + function find_parentqueue($interface, $qname) { return; } + + function validate_input($data, &$input_errors) { + + $reqdfields[] = "name"; + $reqdfieldsn[] = gettext("Name"); + shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors); + + if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) { + $input_errors[] = "Bandwidth must be an integer."; + } + if ($data['bandwidth'] < 0) { + $input_errors[] = "Bandwidth cannot be negative."; + } + if ($data['priority'] && (!is_numeric($data['priority']) || + ($data['priority'] < 1) || ($data['priority'] > 15))) { + $input_errors[] = gettext("The priority must be an integer between 1 and 15."); + } + if ($data['qlimit'] && (!is_numeric($data['qlimit']))) { + $input_errors[] = gettext("Queue limit must be an integer"); + } + if ($data['qlimit'] < 0) { + $input_errors[] = gettext("Queue limit must be positive"); + } + if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['newname'])) { + $input_errors[] = gettext("Queue names must be alphanumeric and _ or - only."); + } + if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['name'])) { + $input_errors[] = gettext("Queue names must be alphanumeric and _ or - only."); + } + $default = $this->GetDefault(); + if (!empty($data['default']) && altq_get_default_queue($data['interface']) && empty($default)) { + $input_errors[] = gettext("Only one default queue per interface is allowed."); + } + } + + function ReadConfig(&$q) { + if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) { + $this->SetQname($q['newname']); + } else if (!empty($q['newname'])) { + $this->SetQname($q['newname']); + } else if (isset($q['name'])) { + $this->SetQname($q['name']); + } + if (isset($q['interface'])) { + $this->SetInterface($q['interface']); + } + $this->SetBandwidth($q['bandwidth']); + if ($q['bandwidthtype'] <> "") { + $this->SetBwscale($q['bandwidthtype']); + } + if (!empty($q['qlimit'])) { + $this->SetQlimit($q['qlimit']); + } else { + $this->SetQlimit(""); // Default + } + if (!empty($q['priority'])) { + $this->SetQPriority($q['priority']); + } else { + $this->SetQpriority(""); + } + if (!empty($q['description'])) { + $this->SetDescription($q['description']); + } else { + $this->SetDescription(""); + } + if (!empty($q['red'])) { + $this->SetRed($q['red']); + } else { + $this->SetRed(); + } + if (!empty($q['codel'])) { + $this->SetCodel($q['codel']); + } else { + $this->SetCodel(); + } + if (!empty($q['rio'])) { + $this->SetRio($q['rio']); + } else { + $this->SetRio(); + } + if (!empty($q['ecn'])) { + $this->SetEcn($q['ecn']); + } else { + $this->SetEcn(); + } + if (!empty($q['default'])) { + $this->SetDefault($q['default']); + } else { + $this->SetDefault(); + } + if (!empty($q['enabled'])) { + $this->SetEnabled($q['enabled']); + } else { + $this->SetEnabled(""); + } + } + + function build_tree() { + $tree = " <li><a href=\"firewall_shaper.php?interface=". $this->GetInterface()."&queue=". $this->GetQname()."&action=show"; + $tree .= "\" "; + $tmpvalue = $this->GetDefault(); + if (!empty($tmpvalue)) { + $tree .= " class=\"navlnk\""; + } + $tree .= " >" . $this->GetQname() . "</a>"; + /* + * Not needed here! + * if (is_array($queues) { + * $tree .= "<ul>"; + * foreach ($q as $queues) + * $tree .= $queues['$q->GetName()']->build_tree(); + * endforeach + * $tree .= "</ul>"; + * } + */ + + $tree .= "</li>"; + + return $tree; + } + + /* Should return something like: + * queue $qname on $qinterface bandwidth .... + */ + function build_rules(&$default = false) { + $pfq_rule = " queue ". $this->qname; + if ($this->GetInterface()) { + $pfq_rule .= " on ".get_real_interface($this->GetInterface()); + } + $tmpvalue = $this->GetQpriority(); + if (!empty($tmpvalue)) { + $pfq_rule .= " priority ".$this->GetQpriority(); + } + $tmpvalue = $this->GetQlimit(); + if (!empty($tmpvalue)) { + $pfq_rule .= " qlimit " . $this->GetQlimit(); + } + if ($this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetDefault() || $this->GetCodel()) { + $pfq_rule .= " priq ( "; + $tmpvalue = $this->GetRed(); + if (!empty($tmpvalue)) { + $comma = 1; + $pfq_rule .= " red "; + } + $tmpvalue = $this->GetRio(); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " rio "; + } + $tmpvalue = $this->GetEcn(); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " ecn "; + } + $tmpvalue = $this->GetCodel(); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " codel "; + } + $tmpvalue = $this->GetDefault(); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $pfq_rule .= " default "; + $default = true; + } + $pfq_rule .= " ) "; + } + + $pfq_rule .= " \n"; + + return $pfq_rule; + } + + /* + * To return the html form to show to user + * for getting the parameters. + * Should do even for first time when the + * object is created and later when we may + * need to update it. (2) + */ + + function build_form() { + + $sform = new Form(); + + $section = new Form_Section(null); + + $section->addInput(new Form_Checkbox( + 'enabled', + 'Enable/Disable', + 'Enable/disable discipline and its children', + ($this->GetEnabled() == "on"), + 'on' + )); + + $section->addInput(new Form_StaticText( + 'Name', + $this->GetQname() + ))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.'); + + $section->addInput(new Form_Input( + 'priority', + 'Priority', + 'number', + $this->GetQpriority(), + ['min' => '0', 'max'=> '7'] + ))->setHelp('For hfsc, the range is 0 to 7. The default is 1. Hfsc queues with a higher priority are preferred in the case of overload.'); + + $section->addInput(new Form_Input( + 'qlimit', + 'Queue Limit', + 'number', + $this->GetQlimit() + ))->setHelp('Queue limit in packets.'); + + $group = new Form_Group('Scheduler options'); + + if (empty($this->subqueues)) { + $group->add(new Form_Checkbox( + 'default', + null, + null, + $this->GetDefault() + ))->setHelp('Default Queue'); + } + + $group->add(new Form_Checkbox( + 'red', + null, + null, + !empty($this->GetRed()) + ))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#red">' . gettext('Random Early Detection') . '</a>'); + + $group->add(new Form_Checkbox( + 'rio', + null, + null, + !empty($this->GetRio()) + ))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#rio">' . gettext('Random Early Detection In and Out') . '</a>'); + + $group->add(new Form_Checkbox( + 'ecn', + null, + null, + !empty($this->GetEcn()) + ))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#ecn">' . gettext('Explicit Congestion Notification') . '</a>'); + + $group->add(new Form_Checkbox( + 'codel', + null, + null, + !empty($this->GetCodel()) + ))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#ecn">' . gettext('Explicit Congestion Notification') . '</a>'); + + $group->setHelp('Select options for this queue'); + + $section->add($group); + + $section->addInput(new Form_Input( + 'description', + 'Description', + 'text', + $this->GetDescription() + )); + + $section->addInput(new Form_Input( + 'interface', + null, + 'hidden', + $this->GetInterface() + )); + + $sform->add($section); + + return($sform); + } + + function build_shortform() { + /* XXX: Hacks in sight. Mostly layer violations! */ + global $g, $altq_list_queues; + global $shaperIFlist; + + $altq =& $altq_list_queues[$this->GetInterface()]; + + if ($altq) { + $scheduler = $altq->GetScheduler(); + } + + $form = '<dl class="dl-horizontal">'; + $form .= ' <dt>'; + $form .= ' <a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&queue=' . $this->GetQname() . '&action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>'; + $form .= ' </dt>'; + $form .= ' <dd>'; + $form .= $scheduler; + $form .= ' </dd>'; + + $form .= ' <dt>'; + $form .= 'Bandwidth'; + $form .= ' </dt>'; + $form .= ' <dd>'; + $form .= $this->GetBandwidth() . ' ' . $this->GetBwscale(); + $form .= ' </dd>'; + + $tmpvalue = $this->GetQpriority(); + if (!empty($tmpvalue)) { + $form .= ' <dt>'; + $form .= 'Priority'; + $form .= ' <dt>'; + $form .= ' <dd>'; + $form .= 'On'; + $form .= ' </dd>'; + } + + $tmpvalue = $this->GetDefault(); + if (!empty($tmpvalue)) { + $form .= ' <dt>'; + $form .= 'Default'; + $form .= ' <dt>'; + $form .= ' <dd>'; + $form .= 'On'; + $form .= ' </dd>'; + } + + $form .= ' <dt>'; + $form .= 'Delete'; + $form .= ' <dt>'; + $form .= ' <dd>'; + + $form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface='; + $form .= $this->GetInterface() . '&queue='; + $form .= $this->GetQname() . '&action=delete">'; + $form .= gettext("Delete queue from interface") . '</a>'; + + $form .= ' </dd>'; + + $form .= '</dl>'; + + return $form; + + } + + function update_altq_queue_data(&$q) { + $this->ReadConfig($q); + } + + function wconfig() { + $cflink =& get_reference_to_me_in_config($this->GetLink()); + if (!is_array($cflink)) { + $cflink = array(); + } + $cflink['name'] = $this->GetQname(); + $cflink['interface'] = $this->GetInterface(); + $cflink['qlimit'] = trim($this->GetQlimit()); + if (empty($cflink['qlimit'])) { + unset($cflink['qlimit']); + } + $cflink['priority'] = trim($this->GetQpriority()); + if (empty($cflink['priority'])) { + unset($cflink['priority']); + } + $cflink['description'] = trim($this->GetDescription()); + if (empty($cflink['description'])) { + unset($cflink['description']); + } + $cflink['enabled'] = trim($this->GetEnabled()); + if (empty($cflink['enabled'])) { + unset($cflink['enabled']); + } + $cflink['default'] = trim($this->GetDefault()); + if (empty($cflink['default'])) { + unset($cflink['default']); + } + $cflink['red'] = trim($this->GetRed()); + if (empty($cflink['red'])) { + unset($cflink['red']); + } + $cflink['codel'] = trim($this->GetCodel()); + if (empty($cflink['codel'])) { + unset($cflink['codel']); + } + $cflink['rio'] = trim($this->GetRio()); + if (empty($cflink['rio'])) { + unset($cflink['rio']); + } + $cflink['ecn'] = trim($this->GetEcn()); + if (empty($cflink['ecn'])) { + unset($cflink['ecn']); + } + } +} + +class hfsc_queue extends priq_queue { + /* realtime */ + var $realtime; + var $r_m1; + var $r_d; + var $r_m2; + /* linkshare */ + var $linkshare; + var $l_m1; + var $l_d; + var $l_m2; + /* upperlimit */ + var $upperlimit; + var $u_m1; + var $u_d; + var $u_m2; + + /* + * HFSC can have nested queues. + */ + function CanHaveChildren() { + return true; + } + function GetRealtime() { + return $this->realtime; + } + function GetR_m1() { + return $this->r_m1; + } + function GetR_d() { + return $this->r_d; + } + function GetR_m2() { + return $this->r_m2; + } + function SetRealtime() { + $this->realtime = "on"; + } + function DisableRealtime() { + $this->realtime = ""; + } + function SetR_m1($value) { + $this->r_m1 = $value; + } + function SetR_d($value) { + $this->r_d = $value; + } + function SetR_m2($value) { + $this->r_m2 = $value; + } + function GetLinkshare() { + return $this->linkshare; + } + function DisableLinkshare() { + $this->linkshare = ""; + } + function GetL_m1() { + return $this->l_m1; + } + function GetL_d() { + return $this->l_d; + } + function GetL_m2() { + return $this->l_m2; + } + function SetLinkshare() { + $this->linkshare = "on"; + } + function SetL_m1($value) { + $this->l_m1 = $value; + } + function SetL_d($value) { + $this->l_d = $value; + } + function SetL_m2($value) { + $this->l_m2 = $value; + } + function GetUpperlimit() { + return $this->upperlimit; + } + function GetU_m1() { + return $this->u_m1; + } + function GetU_d() { + return $this->u_d; + } + function GetU_m2() { + return $this->u_m2; + } + function SetUpperlimit() { + $this->upperlimit = "on"; + } + function DisableUpperlimit() { + $this->upperlimit = ""; + } + function SetU_m1($value) { + $this->u_m1 = $value; + } + function SetU_d($value) { + $this->u_d = $value; + } + function SetU_m2($value) { + $this->u_m2 = $value; + } + + function &add_queue($interface, &$qname, &$path, &$input_errors) { + + if (!is_array($this->subqueues)) { + $this->subqueues = array(); + } + $q =& new hfsc_queue(); + $q->SetInterface($this->GetInterface()); + $q->SetParent($this); + $q->ReadConfig($qname); + $q->validate_input($qname, $input_errors); + if (count($input_errors)) { + log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true)); + return $q; + } + + $q->SetEnabled("on"); + $q->SetLink($path); + switch ($q->GetBwscale()) { + case "%": + $myBw = $this->GetAvailableBandwidth() * $qname['bandwidth'] / 100; + break; + default: + $myBw = $qname['bandwidth'] * get_bandwidthtype_scale($q->GetBwscale()); + break; + } + $q->SetAvailableBandwidth($myBw); + $this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw); + + $this->subqueues[$q->GetQname()] =& $q; //new hfsc_queue() + ref_on_altq_queue_list($this->GetQname(), $q->GetQname()); + if (is_array($qname['queue'])) { + foreach ($qname['queue'] as $key1 => $que) { + array_push($path, $key1); + $q->add_queue($q->GetInterface(), $que, $path, $input_errors); + array_pop($path); + } + } + + return $q; + } + + function copy_queue($interface, &$cflink) { + + $cflink['name'] = $this->GetQname(); + $cflink['interface'] = $interface; + $cflink['qlimit'] = trim($this->GetQlimit()); + if (empty($cflink['qlimit'])) { + unset($cflink['qlimit']); + } + $cflink['priority'] = trim($this->GetQpriority()); + if (empty($cflink['priority'])) { + unset($cflink['priority']); + } + $cflink['description'] = trim($this->GetDescription()); + if (empty($cflink['description'])) { + unset($cflink['description']); + } + $cflink['bandwidth'] = $this->GetBandwidth(); + $cflink['bandwidthtype'] = $this->GetBwscale(); + $cflink['enabled'] = trim($this->GetEnabled()); + if (empty($cflink['enabled'])) { + unset($cflink['enabled']); + } + $cflink['default'] = trim($this->GetDefault()); + if (empty($cflink['default'])) { + unset($cflink['default']); + } + $cflink['red'] = trim($this->GetRed()); + if (empty($cflink['red'])) { + unset($cflink['red']); + } + $cflink['rio'] = trim($this->GetRio()); + if (empty($cflink['rio'])) { + unset($cflink['rio']); + } + $cflink['ecn'] = trim($this->GetEcn()); + if (empty($cflink['ecn'])) { + unset($cflink['ecn']); + } + if ($this->GetLinkshare() <> "") { + if ($this->GetL_m1() <> "") { + $cflink['linkshare1'] = $this->GetL_m1(); + $cflink['linkshare2'] = $this->GetL_d(); + $cflink['linkshare'] = "on"; + } else { + unset($cflink['linkshare1']); + unset($cflink['linkshare2']); + unset($cflink['linkshare']); + } + if ($this->GetL_m2() <> "") { + $cflink['linkshare3'] = $this->GetL_m2(); + $cflink['linkshare'] = "on"; + } else { + unset($cflink['linkshare3']); + unset($cflink['linkshare']); + } + } + if ($this->GetRealtime() <> "") { + if ($this->GetR_m1() <> "") { + $cflink['realtime1'] = $this->GetR_m1(); + $cflink['realtime2'] = $this->GetR_d(); + $cflink['realtime'] = "on"; + } else { + unset($cflink['realtime1']); + unset($cflink['realtime2']); + unset($cflink['realtime']); + } + if ($this->GetR_m2() <> "") { + $cflink['realtime3'] = $this->GetR_m2(); + $cflink['realtime'] = "on"; + } else { + unset($cflink['realtime3']); + unset($cflink['realtime']); + } + } + if ($this->GetUpperlimit() <> "") { + if ($this->GetU_m1() <> "") { + $cflink['upperlimit1'] = $this->GetU_m1(); + $cflink['upperlimit2'] = $this->GetU_d(); + $cflink['upperlimit'] = "on"; + } else { + unset($cflink['upperlimit']); + unset($cflink['upperlimit1']); + unset($cflink['upperlimit2']); + } + if ($this->GetU_m2() <> "") { + $cflink['upperlimit3'] = $this->GetU_m2(); + $cflink['upperlimit'] = "on"; + } else { + unset($cflink['upperlimit3']); + unset($cflink['upperlimit']); + } + } + + if (is_array($this->subqueues)) { + $cflinkp['queue'] = array(); + foreach ($this->subqueues as $q) { + $cflink['queue'][$q->GetQname()] = array(); + $q->copy_queue($interface, $cflink['queue'][$q->GetQname()]); + } + } + } + + function delete_queue() { + unref_on_altq_queue_list($this->GetQname()); + cleanup_queue_from_rules($this->GetQname()); + $parent =& $this->GetParent(); + foreach ($this->subqueues as $q) { + $this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth()); + $q->delete_queue(); + } + unset_object_by_reference($this->GetLink()); + } + + /* + * Should search even its children + */ + function &find_queue($interface, $qname) { + if ($qname == $this->GetQname()) { + return $this; + } + + foreach ($this->subqueues as $q) { + $result =& $q->find_queue("", $qname); + if ($result) { + return $result; + } + } + } + + function &find_parentqueue($interface, $qname) { + if ($this->subqueues[$qname]) { + return $this; + } + foreach ($this->subqueues as $q) { + $result = $q->find_parentqueue("", $qname); + if ($result) { + return $result; + } + } + } + + function validate_input($data, &$input_errors) { + parent::validate_input($data, $input_errors); + + $reqdfields[] = "bandwidth"; + $reqdfieldsn[] = gettext("Bandwidth"); + $reqdfields[] = "bandwidthtype"; + $reqdfieldsn[] = gettext("Bandwidthtype"); + + shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors); + + if (isset($data['linkshare3']) && $data['linkshare3'] <> "") { + if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) { + $input_errors[] = gettext("Bandwidth must be an integer."); + } + + if ($data['bandwidth'] < 0) { + $input_errors[] = gettext("Bandwidth cannot be negative."); + } + + if ($data['bandwidthtype'] == "%") { + if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) { + $input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds."); + } + } + /* + $parent =& $this->GetParent(); + switch ($data['bandwidthtype']) { + case "%": + $myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100; + default: + $mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']); + break; + } + if ($parent->GetAvailableBandwidth() < $myBw) { + $input_errors[] = "The sum of children bandwidth exceeds that of the parent."; + } + */ + } + + if ($data['upperlimit1'] <> "" && $data['upperlimit2'] == "") { + $input_errors[] = gettext("upperlimit service curve defined but missing (d) value"); + } + if ($data['upperlimit2'] <> "" && $data['upperlimit1'] == "") { + $input_errors[] = gettext("upperlimit service curve defined but missing initial bandwidth (m1) value"); + } + if ($data['upperlimit1'] <> "" && !is_valid_shaperbw($data['upperlimit1'])) { + $input_errors[] = gettext("upperlimit m1 value needs to be Kb, Mb, Gb, or %"); + } + if ($data['upperlimit2'] <> "" && !is_numeric($data['upperlimit2'])) { + $input_errors[] = gettext("upperlimit d value needs to be numeric"); + } + if ($data['upperlimit3'] <> "" && !is_valid_shaperbw($data['upperlimit3'])) { + $input_errors[] = gettext("upperlimit m2 value needs to be Kb, Mb, Gb, or %"); + } + + /* + if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") { + $bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']); + $bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']); + if (floatval($bw_1) < floatval($bw_2)) { + $input_errors[] = ("upperlimit m1 cannot be smaller than m2"); + } + + if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) { + $input_errors[] = ("upperlimit specification exceeds 80% of allowable allocation."); + } + } + */ + if ($data['linkshare1'] <> "" && $data['linkshare2'] == "") { + $input_errors[] = gettext("linkshare service curve defined but missing (d) value"); + } + if ($data['linkshare2'] <> "" && $data['linkshare1'] == "") { + $input_errors[] = gettext("linkshare service curve defined but missing initial bandwidth (m1) value"); + } + if ($data['linkshare1'] <> "" && !is_valid_shaperbw($data['linkshare1'])) { + $input_errors[] = gettext("linkshare m1 value needs to be Kb, Mb, Gb, or %"); + } + if ($data['linkshare2'] <> "" && !is_numeric($data['linkshare2'])) { + $input_errors[] = gettext("linkshare d value needs to be numeric"); + } + if ($data['linkshare3'] <> "" && !is_valid_shaperbw($data['linkshare3'])) { + $input_errors[] = gettext("linkshare m2 value needs to be Kb, Mb, Gb, or %"); + } + if ($data['realtime1'] <> "" && $data['realtime2'] == "") { + $input_errors[] = gettext("realtime service curve defined but missing (d) value"); + } + if ($data['realtime2'] <> "" && $data['realtime1'] == "") { + $input_errors[] = gettext("realtime service curve defined but missing initial bandwidth (m1) value"); + } + + /* + if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) { + $bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']); + $bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']); + if (floatval($bw_1) < floatval($bw_2)) { + $input_errors[] = ("linkshare m1 cannot be smaller than m2"); + } + + if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) { + $input_errors[] = ("linkshare specification exceeds 80% of allowable allocation."); + } + } + */ + + if ($data['realtime1'] <> "" && !is_valid_shaperbw($data['realtime1'])) { + $input_errors[] = gettext("realtime m1 value needs to be Kb, Mb, Gb, or %"); + } + if ($data['realtime2'] <> "" && !is_numeric($data['realtime2'])) { + $input_errors[] = gettext("realtime d value needs to be numeric"); + } + if ($data['realtime3'] <> "" && !is_valid_shaperbw($data['realtime3'])) { + $input_errors[] = gettext("realtime m2 value needs to be Kb, Mb, Gb, or %"); + } + + /* + if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) { + $bw_1 = get_hfsc_bandwidth($this, $data['realtime1']); + $bw_2 = get_hfsc_bandwidth($this, $data['realtime3']); + if (floatval($bw_1) < floatval($bw_2)) { + $input_errors[] = ("realtime m1 cannot be smaller than m2"); + } + + if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) { + $input_errors[] = ("realtime specification exceeds 80% of allowable allocation."); + } + } + */ + } + + function ReadConfig(&$cflink) { + if (!empty($cflink['linkshare'])) { + if (!empty($cflink['linkshare1'])) { + $this->SetL_m1($cflink['linkshare1']); + $this->SetL_d($cflink['linkshare2']); + $this->SetLinkshare(); + } else { + $this->SetL_m1(""); + $this->SetL_d(""); + $this->DisableLinkshare(); + } + if (!empty($cflink['linkshare3'])) { + $this->SetL_m2($cflink['linkshare3']); + $this->SetLinkshare(); + } + } else { + $this->DisableLinkshare(); + } + if (!empty($cflink['realtime'])) { + if (!empty($cflink['realtime1'])) { + $this->SetR_m1($cflink['realtime1']); + $this->SetR_d($cflink['realtime2']); + $this->SetRealtime(); + } else { + $this->SetR_m1(""); + $this->SetR_d(""); + $this->DisableRealtime(); + } + if (!empty($cflink['realtime3'])) { + $this->SetR_m2($cflink['realtime3']); + $this->SetRealtime(); + } + } else { + $this->DisableRealtime(); + } + if (!empty($cflink['upperlimit'])) { + if (!empty($cflink['upperlimit1'])) { + $this->SetU_m1($cflink['upperlimit1']); + $this->SetU_d($cflink['upperlimit2']); + $this->SetUpperlimit(); + } else { + $this->SetU_m1(""); + $this->SetU_d(""); + $this->DisableUpperlimit(); + } + if (!empty($cflink['upperlimit3'])) { + $this->SetU_m2($cflink['upperlimit3']); + $this->SetUpperlimit(); + } + } else { + $this->DisableUpperlimit(); + } + parent::ReadConfig($cflink); + } + + function build_tree() { + $tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface() ."&queue=" . $this->GetQname()."&action=show"; + $tree .= "\" "; + $tmpvalue = $this->GetDefault(); + if (!empty($tmpvalue)) { + $tree .= " class=\"navlnk\""; + } + $tree .= " >" . $this->GetQname() . "</a>"; + if (is_array($this->subqueues)) { + $tree .= "<ul>"; + foreach ($this->subqueues as $q) { + $tree .= $q->build_tree(); + } + $tree .= "</ul>"; + } + $tree .= "</li>"; + return $tree; + } + + /* Even this should take children into consideration */ + function build_rules(&$default = false) { + + $pfq_rule = " queue ". $this->qname; + if ($this->GetInterface()) { + $pfq_rule .= " on ".get_real_interface($this->GetInterface()); + } + if ($this->GetBandwidth() && $this->GetBwscale()) { + $pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale(); + } + + $tmpvalue = $this->GetQlimit(); + if (!empty($tmpvalue)) { + $pfq_rule .= " qlimit " . $this->GetQlimit(); + } + if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetCodel() || $this->GetRealtime() <> "" || $this->GetLinkshare() <> "" || $this->GetUpperlimit() <> "") { + $pfq_rule .= " hfsc ( "; + $tmpvalue = $this->GetRed(); + if (!empty($tmpvalue)) { + $comma = 1; + $pfq_rule .= " red "; + } + + $tmpvalue = $this->GetRio(); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " rio "; + } + $tmpvalue = $this->GetEcn(); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " ecn "; + } + $tmpvalue = $this->GetCodel(); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " codel "; + } + $tmpvalue = $this->GetDefault(); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " default "; + $default = true; + } + + if ($this->GetRealtime() <> "") { + if ($comma) { + $pfq_rule .= " , "; + } + if ($this->GetR_m1() <> "" && $this->GetR_d() <> "" && $this->GetR_m2() <> "") { + $pfq_rule .= " realtime (".$this->GetR_m1() . ", " . $this->GetR_d().", ". $this->GetR_m2() .") "; + } else if ($this->GetR_m2() <> "") { + $pfq_rule .= " realtime " . $this->GetR_m2(); + } + $comma = 1; + } + if ($this->GetLinkshare() <> "") { + if ($comma) { + $pfq_rule .= " ,"; + } + if ($this->GetL_m1() <> "" && $this->GetL_d() <> "" && $this->GetL_m2() <> "") { + $pfq_rule .= " linkshare (".$this->GetL_m1(). ", ". $this->GetL_d(). ", ". $this->GetL_m2(). ") "; + } else if ($this->GetL_m2() <> "") { + $pfq_rule .= " linkshare " . $this->GetL_m2() . " "; + } + $comma = 1; + } + if ($this->GetUpperlimit() <> "") { + if ($comma) { + $pfq_rule .= " ,"; + } + if ($this->GetU_m1() <> "" && $this->GetU_d() <> "" && $this->GetU_m2() <> "") { + $pfq_rule .= " upperlimit (".$this->GetU_m1().", ". $this->GetU_d().", ". $this->GetU_m2(). ") "; + } else if ($this->GetU_m2() <> "") { + $pfq_rule .= " upperlimit " . $this->GetU_m2() . " "; + } + } + $pfq_rule .= " ) "; + } + if (count($this->subqueues)) { + $i = count($this->subqueues); + $pfq_rule .= " { "; + foreach ($this->subqueues as $qkey => $qnone) { + if ($i > 1) { + $i--; + $pfq_rule .= " {$qkey}, "; + } else { + $pfq_rule .= " {$qkey} "; + } + } + $pfq_rule .= " } \n"; + foreach ($this->subqueues as $q) { + $pfq_rule .= $q->build_rules($default); + } + } + + $pfq_rule .= " \n"; + + return $pfq_rule; + } + + function build_javascript() { + + $javascript = <<<EOJS +<script type="text/javascript"> +//<![CDATA[ + events.push(function(){ + + // Disables the specified input element + function disableInput(id, disable) { + $('#' + id).prop("disabled", disable); + } + + // Upperlimit + function enable_upperlimit() { + disableInput('upperlimit1', !$('#upperlimit').prop('checked')); + disableInput('upperlimit2', !$('#upperlimit').prop('checked')); + disableInput('upperlimit3', !$('#upperlimit').prop('checked')); + } + + $('#upperlimit').click(function () { + enable_upperlimit(); + }); + + enable_upperlimit(); + + // realtime + function enable_realtime() { + disableInput('realtime1', !$('#realtime').prop('checked')); + disableInput('realtime2', !$('#realtime').prop('checked')); + disableInput('realtime3', !$('#realtime').prop('checked')); + } + + $('#realtime').click(function () { + enable_realtime(); + }); + + enable_realtime(); + + // linkshare + function enable_linkshare() { + disableInput('linkshare1', !$('#linkshare').prop('checked')); + disableInput('linkshare2', !$('#linkshare').prop('checked')); + disableInput('linkshare3', !$('#linkshare').prop('checked')); + } + + $('#linkshare').click(function () { + enable_linkshare(); + }); + + enable_linkshare(); + }); +//]]> +</script> +EOJS; + + return $javascript; + } + + function build_form() { + + $sform = parent::build_form(); + + $section = new Form_Section('Service Curve (sc)'); + + $group = new Form_Group('Bandwidth'); + + $group->add(new Form_Input( + 'bandwidth', + null, + 'number', + $this->GetBandwidth() + )); + + $group->add(new Form_Select( + 'bandwidthtype', + null, + $this->GetBwscale(), + array('Kb' => 'Kb', + 'Mb' => 'Mb', + 'Gb' => 'Gb', + 'b' => 'b') + )); + + $group->setHelp('Choose the amount of bandwidth for this queue'); + + $section->add($group); + + $group = new Form_Group('Max bandwidth for queue.'); + + $group->add(new Form_Checkbox( + 'upperlimit', + null, + 'Upper Limit', + ($this->GetUpperlimit()<> "") + )); + + $group->add(new Form_Input( + 'upperlimit1', + null, + 'text', + $this->GetU_m1() + )); + + $group->add(new Form_Input( + 'upperlimit2', + null, + 'text', + $this->GetU_d() + )); + $group->add(new Form_Input( + 'upperlimit3', + null, + 'text', + $this->GetU_m2() + )); + + + $section->add($group); + + $group = new Form_Group('Min bandwidth for queue.'); + + $group->add(new Form_Checkbox( + 'realtime', + null, + 'Real Time', + ($this->GetRealtime()<> "") + )); + + $group->add(new Form_Input( + 'realtime1', + null, + 'text', + $this->GetR_m1() + )); + + $group->add(new Form_Input( + 'realtime2', + null, + 'text', + $this->GetR_d() + )); + $group->add(new Form_Input( + 'realtime3', + null, + 'text', + $this->GetR_m2() + )); + + $section->add($group); + + $group = new Form_Group('B/W share of a backlogged queue.'); + + $group->add(new Form_Checkbox( + 'linkshare', + null, + 'Link Share', + ($this->GetLinkshare()<> "") + )); + + $group->add(new Form_Input( + 'linkshare1', + null, + 'text', + $this->GetL_m1() + )); + + $group->add(new Form_Input( + 'linkshare2', + null, + 'text', + $this->GetL_d() + )); + $group->add(new Form_Input( + 'linkshare3', + null, + 'text', + $this->GetL_m2() + )); + + $group->sethelp('Bandwidth share overrides priority.' . '<br />' . + 'The format for service curve specifications is (m1, d, m2). m2 controls the bandwidth assigned to the queue. ' . + 'm1 and d are optional and can be used to control the initial bandwidth assignment. ' . + 'For the first d milliseconds the queue gets the bandwidth given as m1, afterwards the value given in m2.'); + + $section->add($group); + + $sform->add($section); + + return($sform); + } + + function update_altq_queue_data(&$data) { + $this->ReadConfig($data); + } + + function wconfig() { + $cflink =& get_reference_to_me_in_config($this->GetLink()); + if (!is_array($cflink)) { + $cflink = array(); + } + $cflink['name'] = $this->GetQname(); + $cflink['interface'] = $this->GetInterface(); + $cflink['qlimit'] = trim($this->GetQlimit()); + if (empty($cflink['qlimit'])) { + unset($cflink['qlimit']); + } + $cflink['priority'] = $this->GetQpriority(); + if (empty($cflink['priority'])) { + unset($cflink['priority']); + } + $cflink['description'] = $this->GetDescription(); + if (empty($cflink['description'])) { + unset($cflink['description']); + } + $cflink['bandwidth'] = $this->GetBandwidth(); + $cflink['bandwidthtype'] = $this->GetBwscale(); + $cflink['enabled'] = $this->GetEnabled(); + if (empty($cflink['enabled'])) { + unset($cflink['enabled']); + } + $cflink['default'] = $this->GetDefault(); + if (empty($cflink['default'])) { + unset($cflink['default']); + } + $cflink['red'] = trim($this->GetRed()); + if (empty($cflink['red'])) { + unset($cflink['red']); + } + $cflink['rio'] = $this->GetRio(); + if (empty($cflink['rio'])) { + unset($cflink['rio']); + } + $cflink['ecn'] = trim($this->GetEcn()); + if (empty($cflink['ecn'])) { + unset($cflink['ecn']); + } + $cflink['codel'] = trim($this->GetCodel()); + if (empty($cflink['codel'])) { + unset($cflink['codel']); + } + if ($this->GetLinkshare() <> "") { + if ($this->GetL_m1() <> "") { + $cflink['linkshare1'] = $this->GetL_m1(); + $cflink['linkshare2'] = $this->GetL_d(); + $cflink['linkshare'] = "on"; + } else { + unset($cflink['linkshare']); + unset($cflink['linkshare1']); + unset($cflink['linkshare2']); + } + if ($this->GetL_m2() <> "") { + $cflink['linkshare3'] = $this->GetL_m2(); + $cflink['linkshare'] = "on"; + } else { + unset($cflink['linkshare']); + unset($cflink['linkshare3']); + } + } else { + unset($cflink['linkshare']); + unset($cflink['linkshare1']); + unset($cflink['linkshare2']); + unset($cflink['linkshare3']); + } + if ($this->GetRealtime() <> "") { + if ($this->GetR_m1() <> "") { + $cflink['realtime1'] = $this->GetR_m1(); + $cflink['realtime2'] = $this->GetR_d(); + $cflink['realtime'] = "on"; + } else { + unset($cflink['realtime']); + unset($cflink['realtime1']); + unset($cflink['realtime2']); + } + if ($this->GetR_m2() <> "") { + $cflink['realtime3'] = $this->GetR_m2(); + $cflink['realtime'] = "on"; + } else { + unset($cflink['realtime']); + unset($cflink['realtime3']); + } + } else { + unset($cflink['realtime']); + unset($cflink['realtime1']); + unset($cflink['realtime2']); + unset($cflink['realtime3']); + } + if ($this->GetUpperlimit() <> "") { + if ($this->GetU_m1() <> "") { + $cflink['upperlimit1'] = $this->GetU_m1(); + $cflink['upperlimit2'] = $this->GetU_d(); + $cflink['upperlimit'] = "on"; + } else { + unset($cflink['upperlimit']); + unset($cflink['upperlimit1']); + unset($cflink['upperlimit2']); + } + if ($this->GetU_m2() <> "") { + $cflink['upperlimit3'] = $this->GetU_m2(); + $cflink['upperlimit'] = "on"; + } else { + unset($cflink['upperlimit']); + unset($cflink['upperlimit3']); + } + } else { + unset($cflink['upperlimit']); + unset($cflink['upperlimit1']); + unset($cflink['upperlimit2']); + unset($cflink['upperlimit3']); + } + } +} + +class cbq_queue extends priq_queue { + var $qborrow = ""; + + function GetBorrow() { + return $this->qborrow; + } + function SetBorrow($borrow) { + $this->qborrow = $borrow; + } + function CanHaveChildren() { + return true; + } + + function &add_queue($interface, &$qname, &$path, &$input_errors) { + + if (!is_array($this->subqueues)) { + $this->subqueues = array(); + } + $q =& new cbq_queue(); + $q->SetInterface($this->GetInterface()); + $q->SetParent($this); + $q->ReadConfig($qname); + $q->validate_input($qname, $input_errors); + if (count($input_errors)) { + log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true)); + return $q; + } + switch ($q->GetBwscale()) { + case "%": + $myBw = $this->GetAvailableBandwidth() * $qname['bandwidth'] / 100; + break; + default: + $myBw = $qname['bandwidth'] * get_bandwidthtype_scale($q->GetBwscale()); + break; + } + $q->SetAvailableBandwidth($myBw); + $this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw); + + $q->SetEnabled("on"); + $q->SetLink($path); + $this->subqueues[$q->GetQName()] = &$q; + ref_on_altq_queue_list($this->GetQname(), $q->GetQname()); + if (is_array($qname['queue'])) { + foreach ($qname['queue'] as $key1 => $que) { + array_push($path, $key1); + $q->add_queue($q->GetInterface(), $que, $path, $input_errors); + array_pop($path); + } + } + + return $q; + } + + function copy_queue($interface, &$cflink) { + + $cflink['interface'] = $interface; + $cflink['qlimit'] = trim($this->GetQlimit()); + if (empty($clink['qlimit'])) { + unset($cflink['qlimit']); + } + $cflink['priority'] = trim($this->GetQpriority()); + if (empty($cflink['priority'])) { + unset($cflink['priority']); + } + $cflink['name'] = $this->GetQname(); + $cflink['description'] = trim($this->GetDescription()); + if (empty($cflink['description'])) { + unset($cflink['description']); + } + $cflink['bandwidth'] = $this->GetBandwidth(); + $cflink['bandwidthtype'] = $this->GetBwscale(); + $cflink['enabled'] = trim($this->GetEnabled()); + if (empty($cflink['enabled'])) { + unset($cflink['enabled']); + } + $cflink['default'] = trim($this->GetDefault()); + if (empty($cflink['default'])) { + unset($cflink['default']); + } + $cflink['red'] = trim($this->GetRed()); + if (empty($cflink['red'])) { + unset($cflink['red']); + } + $cflink['rio'] = trim($this->GetRio()); + if (empty($cflink['rio'])) { + unset($cflink['rio']); + } + $cflink['ecn'] = trim($this->GetEcn()); + if (empty($cflink['ecn'])) { + unset($cflink['ecn']); + } + $cflink['borrow'] = trim($this->GetBorrow()); + if (empty($cflink['borrow'])) { + unset($cflink['borrow']); + } + if (is_array($this->queues)) { + $cflinkp['queue'] = array(); + foreach ($this->subqueues as $q) { + $cflink['queue'][$q->GetQname()] = array(); + $q->copy_queue($interface, $cflink['queue'][$q->GetQname()]); + } + } + } + + /* + * Should search even its children + */ + function &find_queue($interface, $qname) { + if ($qname == $this->GetQname()) { + return $this; + } + foreach ($this->subqueues as $q) { + $result =& $q->find_queue("", $qname); + if ($result) { + return $result; + } + } + } + + function &find_parentqueue($interface, $qname) { + if ($this->subqueues[$qname]) { + return $this; + } + foreach ($this->subqueues as $q) { + $result = $q->find_parentqueue("", $qname); + if ($result) { + return $result; + } + } + } + + function delete_queue() { + unref_on_altq_queue_list($this->GetQname()); + cleanup_queue_from_rules($this->GetQname()); + foreach ($this->subqueues as $q) { + $this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth()); + $q->delete_queue(); + } + unset_object_by_reference($this->GetLink()); + } + + function validate_input($data, &$input_errors) { + parent::validate_input($data, $input_errors); + + if ($data['priority'] > 7) { + $input_errors[] = gettext("Priority must be an integer between 1 and 7."); + } + $reqdfields[] = "bandwidth"; + $reqdfieldsn[] = gettext("Bandwidth"); + $reqdfields[] = "bandwidthtype"; + $reqdfieldsn[] = gettext("Bandwidthtype"); + + shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors); + + if ($data['bandwidth'] && !is_numeric($data['bandwidth'])) { + $input_errors[] = gettext("Bandwidth must be an integer."); + } + + + if ($data['bandwidth'] < 0) { + $input_errors[] = gettext("Bandwidth cannot be negative."); + } + + if ($data['bandwidthtype'] == "%") { + if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) { + $input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds."); + } + } + +/* + $parent =& $this->GetParent(); + switch ($data['bandwidthtype']) { + case "%": + $myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100; + break; + default: + $mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']); + break; + } + if ($parent->GetAvailableBandwidth() < floatval($myBw)) { + $input_errors[] = "The sum of the children bandwidth exceeds that of the parent."; + } + */ + } + + function ReadConfig(&$q) { + parent::ReadConfig($q); + if (!empty($q['borrow'])) { + $this->SetBorrow("on"); + } else { + $this->SetBorrow(""); + } + } + + function build_javascript() { + return parent::build_javascript(); + } + + function build_tree() { + $tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface()."&queue=" . $this->GetQname()."&action=show"; + $tree .= "\" "; + $tmpvalue = trim($this->GetDefault()); + if (!empty($tmpvalue)) { + $tree .= " class=\"navlnk\""; + } + $tree .= " >" . $this->GetQname() . "</a>"; + if (is_array($this->subqueues)) { + $tree .= "<ul>"; + foreach ($this->subqueues as $q) { + $tree .= $q->build_tree(); + } + $tree .= "</ul>"; + } + $tree .= "</li>"; + return $tree; + } + + /* Even this should take children into consideration */ + function build_rules(&$default = false) { + $pfq_rule = "queue ". $this->qname; + if ($this->GetInterface()) { + $pfq_rule .= " on ".get_real_interface($this->GetInterface()); + } + if ($this->GetBandwidth() && $this->GetBwscale()) { + $pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale(); + } + $tmpvalue = $this->GetQpriority(); + if (!empty($tmpvalue)) { + $pfq_rule .= " priority " . $this->GetQpriority(); + } + $tmpvalue = trim($this->GetQlimit()); + if (!empty($tmpvalue)) { + $pfq_rule .= " qlimit " . $this->GetQlimit(); + } + if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetBorrow() || $this->GetCodel()) { + $pfq_rule .= " cbq ( "; + $tmpvalue = trim($this->GetRed()); + if (!empty($tmpvalue)) { + $comma = 1; + $pfq_rule .= " red "; + } + $tmpvalue = trim($this->GetCodel()); + if (!empty($tmpvalue)) { + $comma = 1; + $pfq_rule .= " codel "; + } + $tmpvalue = trim($this->GetRio()); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " rio "; + } + $tmpvalue = trim($this->GetEcn()); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " ecn "; + } + $tmpvalue = trim($this->GetDefault()); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " default "; + $default = true; + } + $tmpvalue = trim($this->GetBorrow()); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= ", "; + } + $pfq_rule .= " borrow "; + } + $pfq_rule .= " ) "; + } + if (count($this->subqueues)) { + $i = count($this->subqueues); + $pfq_rule .= " { "; + foreach ($this->subqueues as $qkey => $qnone) { + if ($i > 1) { + $i--; + $pfq_rule .= " {$qkey}, "; + } else { + $pfq_rule .= " {$qkey} "; + } + } + $pfq_rule .= " } \n"; + foreach ($this->subqueues as $q) { + $pfq_rule .= $q->build_rules($default); + } + } + + $pfq_rule .= " \n"; + return $pfq_rule; + } + + function build_form() { + $sform = parent::build_form(); + + $section = new Form_Section(''); + + $group = new Form_Group('Bandwidth'); + + $group->add(new Form_Input( + 'bandwidth', + null, + 'number', + $this->GetBandwidth() + )); + + $group->add(new Form_Select( + 'bandwidthtype', + null, + $this->GetBwscale(), + array('Kb' => 'Kb', + 'Mb' => 'Mb', + 'Gb' => 'Gb', + 'b' => 'b') + )); + + $group->setHelp('Choose the amount of bandwidth for this queue'); + + $section->add($group); + + $section->addInput(new Form_Checkbox( + 'borrow', + 'Scheduler option', + 'Borrow from other queues when available', + ($this->GetBorrow() == "on") + )); + + return $sform; + } + + function update_altq_queue_data(&$data) { + $this->ReadConfig($data); + } + + function wconfig() { + $cflink =& get_reference_to_me_in_config($this->GetLink()); + if (!is_array($cflink)) { + $cflink = array(); + } + $cflink['interface'] = $this->GetInterface(); + $cflink['qlimit'] = trim($this->GetQlimit()); + if (empty($cflink['qlimit'])) { + unset($cflink['qlimit']); + } + $cflink['priority'] = $this->GetQpriority(); + if (empty($cflink['priority'])) { + unset($cflink['priority']); + } + $cflink['name'] = $this->GetQname(); + $cflink['description'] = $this->GetDescription(); + if (empty($cflink['description'])) { + unset($cflink['description']); + } + $cflink['bandwidth'] = $this->GetBandwidth(); + $cflink['bandwidthtype'] = $this->GetBwscale(); + $cflink['enabled'] = trim($this->GetEnabled()); + if (empty($cflink['enabled'])) { + unset($cflink['enabled']); + } + $cflink['default'] = trim($this->GetDefault()); + if (empty($cflink['default'])) { + unset($cflink['default']); + } + $cflink['red'] = trim($this->GetRed()); + if (empty($cflink['red'])) { + unset($cflink['red']); + } + $cflink['rio'] = trim($this->GetRio()); + if (empty($cflink['rio'])) { + unset($cflink['rio']); + } + $cflink['ecn'] = trim($this->GetEcn()); + if (empty($cflink['ecn'])) { + unset($cflink['ecn']); + } + $cflink['codel'] = trim($this->GetCodel()); + if (empty($cflink['codel'])) { + unset($cflink['codel']); + } + $cflink['borrow'] = trim($this->GetBorrow()); + if (empty($cflink['borrow'])) { + unset($cflink['borrow']); + } + } +} + +class fairq_queue extends priq_queue { + var $hogs; + var $buckets; + + function GetBuckets() { + return $this->buckets; + } + function SetBuckets($buckets) { + $this->buckets = $buckets; + } + function GetHogs() { + return $this->hogs; + } + function SetHogs($hogs) { + $this->hogs = $hogs; + } + function CanHaveChildren() { + return false; + } + + + function copy_queue($interface, &$cflink) { + $cflink['interface'] = $interface; + $cflink['qlimit'] = $this->GetQlimit(); + $cflink['priority'] = $this->GetQpriority(); + $cflink['name'] = $this->GetQname(); + $cflink['description'] = $this->GetDescription(); + $cflink['bandwidth'] = $this->GetBandwidth(); + $cflink['bandwidthtype'] = $this->GetBwscale(); + $cflink['enabled'] = $this->GetEnabled(); + $cflink['default'] = $this->GetDefault(); + $cflink['red'] = $this->GetRed(); + $cflink['rio'] = $this->GetRio(); + $cflink['ecn'] = $this->GetEcn(); + $cflink['buckets'] = $this->GetBuckets(); + $cflink['hogs'] = $this->GetHogs(); + } + + /* + * Should search even its children + */ + function &find_queue($interface, $qname) { + if ($qname == $this->GetQname()) { + return $this; + } + } + + function find_parentqueue($interface, $qname) { return; } + + function delete_queue() { + unref_on_altq_queue_list($this->GetQname()); + cleanup_queue_from_rules($this->GetQname()); + unset_object_by_reference($this->GetLink()); + } + + function validate_input($data, &$input_errors) { + parent::validate_input($data, $input_errors); + + if ($data['priority'] > 255) { + $input_errors[] = gettext("Priority must be an integer between 1 and 255."); + } + $reqdfields[] = "bandwidth"; + $reqdfieldsn[] = gettext("Bandwidth"); + $reqdfields[] = "bandwidthtype"; + $reqdfieldsn[] = gettext("Bandwidthtype"); + + shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors); + + if ($data['bandwidth'] && !is_numeric($data['bandwidth'])) { + $input_errors[] = gettext("Bandwidth must be an integer."); + } + + + if ($data['bandwidth'] < 0) { + $input_errors[] = gettext("Bandwidth cannot be negative."); + } + + + if ($data['bandwidthtype'] == "%") { + if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) { + $input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds."); + } + } + +/* + $parent =& $this->GetParent(); + switch ($data['bandwidthtype']) { + case "%": + $myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100; + default: + $mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']); + break; + } + if ($parent->GetAvailableBandwidth() < floatval($myBw)) { + $input_errors[] = "The sum of children bandwidth exceeds that of the parent."; + } +*/ + } + + function ReadConfig(&$q) { + parent::ReadConfig($q); + if (!empty($q['buckets'])) { + $this->SetBuckets($q['buckets']); + } else { + $this->SetBuckets(""); + } + if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) { + $this->SetHogs($q['hogs']); + } else { + $this->SetHogs(""); + } + } + + function build_javascript() { + return parent::build_javascript(); + } + + function build_tree() { + $tree = " <li><a href=\"firewall_shaper.php?interface=" . + $this->GetInterface()."&queue=" . $this->GetQname()."&action=show"; + $tree .= "\" "; + $tmpvalue = trim($this->GetDefault()); + if (!empty($tmpvalue)) { + $tree .= " class=\"navlnk\""; + } + $tree .= " >" . $this->GetQname() . "</a>"; + $tree .= "</li>"; + return $tree; + } + + /* Even this should take children into consideration */ + function build_rules(&$default = false) { + $pfq_rule = "queue ". $this->qname; + if ($this->GetInterface()) { + $pfq_rule .= " on ".get_real_interface($this->GetInterface()); + } + if ($this->GetBandwidth() && $this->GetBwscale()) { + $pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale(); + } + $tmpvalue = trim($this->GetQpriority()); + if (!empty($tmpvalue)) { + $pfq_rule .= " priority " . $this->GetQpriority(); + } + $tmpvalue = trim($this->GetQlimit()); + if (!empty($tmpvalue)) { + $pfq_rule .= " qlimit " . $this->GetQlimit(); + } + if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || + $this->GetEcn() || $this->GetBuckets() || $this->GetHogs() || $this->GetCodel()) { + $pfq_rule .= " fairq ( "; + $tmpvalue = trim($this->GetRed()); + if (!empty($tmpvalue)) { + $comma = 1; + $pfq_rule .= " red "; + } + $tmpvalue = trim($this->GetCodel()); + if (!empty($tmpvalue)) { + $comma = 1; + $pfq_rule .= " codel "; + } + $tmpvalue = trim($this->GetRio()); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " rio "; + } + $tmpvalue = trim($this->GetEcn()); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " ecn "; + } + $tmpvalue = trim($this->GetDefault()); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= " ,"; + } + $comma = 1; + $pfq_rule .= " default "; + $default = true; + } + $tmpvalue = trim($this->GetBuckets()); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= ", "; + } + $pfq_rule .= " buckets " . $this->GetBuckets() . " "; + } + $tmpvalue = trim($this->GetHogs()); + if (!empty($tmpvalue)) { + if ($comma) { + $pfq_rule .= ", "; + } + $pfq_rule .= " hogs " . $this->GetHogs() . " "; + } + $pfq_rule .= " ) "; + } + + $pfq_rule .= " \n"; + return $pfq_rule; + } + + function build_form() { + $form = parent::build_form(); + + $section = new Form_Section(''); + + $group = new Form_Group('Bandwidth'); + + $group->add(new Form_Input( + 'bandwidth', + null, + 'number', + $this->GetBandwidth() + )); + + $group->add(new Form_Select( + 'bandwidthtype', + null, + $this->GetBwscale(), + array('Kb' => 'Kb', + 'Mb' => 'Mb', + 'Gb' => 'Gb', + 'b' => 'b') + )); + + $group->setHelp('Choose the amount of bandwidth for this queue'); + + $section->add($group); + + + $form .= "<tr><td class=\"vncellreq\">" . gettext("Scheduler specific options") . "</td>"; + $form .= "<td class=\"vtable\"><table><tr><td>"; + $form .= "<input id=\"buckets\" name=\"buckets\" value=\""; + $tmpvalue = trim($this->GetBuckets()); + if (!empty($tmpvalue)) { + $form .= $this->GetBuckets(); + } + $form .= "\" /> " . gettext("Number of buckets available.") . "<br /></td></tr>"; + $form .= "<tr><td class=\"vtable\"><input id=\"hogs\" name=\"hogs\" value=\""; + $tmpvalue = trim($this->GetHogs()); + if (!empty($tmpvalue)) { + $form .= $this->GetHogs(); + } + $form .= "\" /> " . gettext("Bandwidth limit for hosts to not saturate link.") . "<br /></td></tr>"; + $form .= "</table></td></tr>"; + return $form; + } + + function update_altq_queue_data(&$data) { + $this->ReadConfig($data); + } + + function wconfig() { + $cflink =& get_reference_to_me_in_config($this->GetLink()); + if (!is_array($cflink)) { + $cflink = array(); + } + $cflink['interface'] = $this->GetInterface(); + $cflink['qlimit'] = trim($this->GetQlimit()); + if (empty($cflink['qlimit'])) { + unset($cflink['qlimit']); + } + $cflink['priority'] = trim($this->GetQpriority()); + if (empty($cflink['priority'])) { + unset($cflink['priority']); + } + $cflink['name'] = $this->GetQname(); + $cflink['description'] = trim($this->GetDescription()); + if (empty($cflink['description'])) { + unset($cflink['description']); + } + $cflink['bandwidth'] = $this->GetBandwidth(); + $cflink['bandwidthtype'] = $this->GetBwscale(); + $cflink['enabled'] = $this->GetEnabled(); + if (empty($cflink['enabled'])) { + unset($cflink['enabled']); + } + $cflink['default'] = trim($this->GetDefault()); + if (empty($cflink['default'])) { + unset($cflink['default']); + } + $cflink['red'] = trim($this->GetRed()); + if (empty($cflink['red'])) { + unset($cflink['red']); + } + $cflink['rio'] = trim($this->GetRio()); + if (empty($cflink['rio'])) { + unset($cflink['rio']); + } + $cflink['ecn'] = trim($this->GetEcn()); + if (empty($cflink['ecn'])) { + unset($cflink['ecn']); + } + $cflink['codel'] = trim($this->GetCodel()); + if (empty($cflink['codel'])) { + unset($cflink['codel']); + } + $cflink['buckets'] = trim($this->GetBuckets()); + if (empty($cflink['buckets'])) { + unset($cflink['buckets']); + } + $cflink['hogs'] = trim($this->GetHogs()); + if (empty($cflink['hogs'])) { + unset($cflink['hogs']); + } + } +} + + +/* + * dummynet(4) wrappers. + */ + + +/* + * List of respective objects! + */ +$dummynet_pipe_list = array(); + +class dummynet_class { + var $qname; + var $qnumber; /* dummynet(4) uses numbers instead of names; maybe integrate with pf the same as altq does?! */ + var $qlimit; + var $description; + var $qenabled; + var $link; + var $qparent; /* link to upper class so we do things easily on WF2Q+ rule creation */ + var $plr; + + var $buckets; + /* mask parameters */ + var $mask; + var $noerror; + + /* Accessor functions */ + function SetLink($link) { + $this->link = $link; + } + function GetLink() { + return $this->link; + } + function GetMask() { + if (!isset($this->mask["type"])) { + $this->mask["type"] = "none"; + } + return $this->mask; + } + function SetMask($mask) { + $this->mask = $mask; + } + function &GetParent() { + return $this->qparent; + } + function SetParent(&$parent) { + $this->qparent = &$parent; + } + function GetEnabled() { + return $this->qenabled; + } + function SetEnabled($value) { + $this->qenabled = $value; + } + function CanHaveChildren() { + return false; + } + function CanBeDeleted() { + return true; + } + function GetQname() { + return $this->qname; + } + function SetQname($name) { + $this->qname = trim($name); + } + function GetQlimit() { + return $this->qlimit; + } + function SetQlimit($limit) { + $this->qlimit = $limit; + } + function GetDescription() { + return $this->description; + } + function SetDescription($str) { + $this->description = trim($str); + } + function GetFirstime() { + return $this->firsttime; + } + function SetFirsttime($number) { + $this->firsttime = $number; + } + function GetBuckets() { + return $this->buckets; + } + function SetBuckets($buckets) { + $this->buckets = $buckets; + } + function SetNumber($number) { + $this->qnumber = $number; + } + function GetNumber() { + return $this->qnumber; + } + function GetPlr() { + return $this->plr; + } + function SetPlr($plr) { + $this->plr = $plr; + } + + function build_javascript() { + $javascript .= "<script type=\"text/javascript\">\n"; + $javascript .= "//<![CDATA[\n"; + $javascript .= "function enable_maskbits(enable_over) {\n"; + $javascript .= "var e = document.getElementById(\"mask\");\n"; + $javascript .= "if ((e.options[e.selectedIndex].text == \"none\") || enable_over) {\n"; + $javascript .= "document.iform.maskbits.disabled = 1;\n"; + $javascript .= "document.iform.maskbits.value = \"\";\n"; + $javascript .= "document.iform.maskbitsv6.disabled = 1;\n"; + $javascript .= "document.iform.maskbitsv6.value = \"\";\n"; + $javascript .= "} else {\n"; + $javascript .= "document.iform.maskbits.disabled = 0;\n"; + $javascript .= "document.iform.maskbitsv6.disabled = 0;\n"; + $javascript .= "}}\n"; + $javascript .= "//]]>\n"; + $javascript .= "</script>\n"; + return $javascript; + } + + function validate_input($data, &$input_errors) { + $reqdfields[] = "bandwidth"; + $reqdfieldsn[] = gettext("Bandwidth"); + /*$reqdfields[] = "burst"; + $reqdfieldsn[] = gettext("Burst"); */ + $reqdfields[] = "bandwidthtype"; + $reqdfieldsn[] = gettext("Bandwidthtype"); + $reqdfields[] = "newname"; + $reqdfieldsn[] = gettext("Name"); + + shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors); + + if ($data['plr'] && (!is_numeric($data['plr']) || + ($data['plr'] < 0) || ($data['plr'] > 1))) { + $input_errors[] = gettext("Plr must be a value between 0 and 1."); + } + if ($data['buckets'] && (!is_numeric($data['buckets']) || + ($data['buckets'] < 16) || ($data['buckets'] > 65535))) { + $input_errors[] = gettext("Buckets must be an integer between 16 and 65535."); + } + if ($data['qlimit'] && (!is_numeric($data['qlimit']))) { + $input_errors[] = gettext("Queue limit must be an integer"); + } + if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['newname'])) { + $input_errors[] = gettext("Queue names must be alphanumeric and _ or - only."); + } + if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['name'])) { + $input_errors[] = gettext("Queue names must be alphanumeric and _ or - only."); + } + if (isset($data['maskbits']) && ($data['maskbits'] <> "")) { + if ((!is_numeric($data['maskbits'])) || ($data['maskbits'] <= 0) || ($data['maskbits'] > 32)) { + $input_errors[] = gettext("IPV4 bit mask must be blank or numeric value between 1 and 32."); + } + } + if (isset($data['maskbitsv6']) && ($data['maskbitsv6'] <> "")) { + if ((!is_numeric($data['maskbitsv6'])) || ($data['maskbitsv6'] <= 0) || ($data['maskbitsv6'] > 128)) { + $input_errors[] = gettext("IPV6 bit mask must be blank or numeric value between 1 and 128."); + } + } + } + + function build_mask_rules(&$pfq_rule) { + $mask = $this->GetMask(); + if (!empty($mask['type'])) { + if ($mask['type'] <> 'none') { + $pfq_rule .= " mask"; + } + switch ($mask['type']) { + case 'srcaddress': + if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) { + $pfq_rule .= " src-ip6 /" . $mask['bitsv6']; + } else { + $pfq_rule .= " src-ip6 /128"; + } + if (!empty($mask['bits']) && ($mask['bits'] <> "")) { + $pfq_rule .= sprintf(" src-ip 0x%x", gen_subnet_mask_long($mask['bits'])); + } else { + $pfq_rule .= " src-ip 0xffffffff"; + } + break; + case 'dstaddress': + if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) { + $pfq_rule .= " dst-ip6 /" . $mask['bitsv6']; + } else { + $pfq_rule .= " dst-ip6 /128"; + } + if (!empty($mask['bits']) && ($mask['bits'] <> "")) { + $pfq_rule .= sprintf(" dst-ip 0x%x", gen_subnet_mask_long($mask['bits'])); + } else { + $pfq_rule .= " dst-ip 0xffffffff"; + } + break; + default: + break; + } + } + } + +} + +class dnpipe_class extends dummynet_class { + var $delay; + var $qbandwidth = array(); + var $qbandwidthtype; + + /* This is here to help on form building and building rules/lists */ + var $subqueues = array(); + + function CanHaveChildren() { + return true; + } + function SetDelay($delay) { + $this->delay = $delay; + } + function GetDelay() { + return $this->delay; + } + function delete_queue() { + cleanup_dnqueue_from_rules($this->GetQname()); + foreach ($this->subqueues as $q) { + $q->delete_queue(); + } + unset_dn_object_by_reference($this->GetLink()); + @pfSense_pipe_action("pipe delete " . $this->GetNumber()); + } + function GetBandwidth() { + return $this->qbandwidth; + } + function SetBandwidth($bandwidth) { + $this->qbandwidth = $bandwidth; + } + function GetBurst() { + return $this->qburst; + } + function SetBurst($burst) { + $this->qburst = $burst; + } + + function &add_queue($interface, &$queue, &$path, &$input_errors) { + + if (!is_array($this->subqueues)) { + $this->subqueues = array(); + } + + $q =& new dnqueue_class(); + $q->SetLink($path); + $q->SetEnabled("on"); + $q->SetPipe($this->GetQname()); + $q->SetParent($this); + $q->ReadConfig($queue); + $q->validate_input($queue, $input_errors); + if (count($input_errors)) { + log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true)); + return $q; + } + $number = dnqueue_find_nextnumber(); + $q->SetNumber($number); + $this->subqueues[$q->GetQname()] = &$q; + + return $q; + } + + function &get_queue_list(&$q = null) { + $qlist = array(); + + $qlist[$this->GetQname()] = $this->GetNumber(); + if (is_array($this->subqueues)) { + foreach ($this->subqueues as $queue) { + $queue->get_queue_list($qlist); + } + } + return $qlist; + } + + /* + * Should search even its children + */ + function &find_queue($pipe, $qname) { + if ($qname == $this->GetQname()) { + return $this; + } + foreach ($this->subqueues as $q) { + $result =& $q->find_queue("", $qname); + if ($result) { + return $result; + } + } + } + + function &find_parentqueue($pipe, $qname) { + return NULL; + } + + function validate_input($data, &$input_errors) { + parent::validate_input($data, $input_errors); + + $schedule = 0; + $schedulenone = 0; + $entries = 0; + /* XXX: Really no better way? */ + for ($i = 0; $i < 2900; $i++) { + if (!empty($data["bwsched{$i}"])) { + if ($data["bwsched{$i}"] != "none") { + $schedule++; + } else { + $schedulenone++; + } + } + if (!empty($data["bandwidth{$i}"])) { + if (!is_numeric($data["bandwidth{$i}"])) { + $input_errors[] = sprintf(gettext("Bandwidth for schedule %s must be an integer."), $data["bwsched{$i}"]); + } else if (($data["burst{$i}"] != "") && (!is_numeric($data["burst{$i}"]))) { + $input_errors[] = sprintf(gettext("Burst for schedule %s must be an integer."), $data["bwsched{$i}"]); + } else { + $entries++; + } + } + } + if ($schedule == 0 && $entries > 1) { + $input_errors[] = gettext("You need to specify a schedule for every additional entry"); + } + if ($schedulenone > 0 && $entries > 1) { + $input_errors[] = gettext("If more than one bandwidth configured all schedules need to be selected"); + } + if ($entries == 0) { + $input_errors[] = gettext("At least one bw specification is necessary"); + } + if ($data['delay'] && (!is_numeric($data['delay']))) { + $input_errors[] = gettext("Delay must be an integer."); + } + } + + function ReadConfig(&$q) { + if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) { + $this->SetQname($q['newname']); + } else if (!empty($q['newname'])) { + $this->SetQname($q['newname']); + } else { + $this->SetQname($q['name']); + } + $this->SetNumber($q['number']); + + if (!empty($_POST)) { + $bandwidth = array(); + /* XXX: Really no better way? */ + for ($i = 0; $i < 2900; $i++) { + if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") { + $bw = array(); + $bw['bw'] = $q["bandwidth{$i}"]; + $bw['burst'] = $q["burst{$i}"]; + if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) { + $bw['bwscale'] = $q["bwtype{$i}"]; + } + if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) { + $bw['bwsched'] = $q["bwsched{$i}"]; + } + $bandwidth[] = $bw; + } + } + $this->SetBandwidth($bandwidth); + } + + if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) { + $this->SetBandwidth($q['bandwidth']['item']); + $this->SetBurst($q['burst']['item']); + } + + if (isset($q['qlimit']) && $q['qlimit'] <> "") { + $this->SetQlimit($q['qlimit']); + } else { + $this->SetQlimit(""); + } + if (isset($q['mask']) && $q['mask'] <> "") { + $masktype = $q['mask']; + } else { + $masktype = ""; + } + if (isset($q['maskbits']) && $q['maskbits'] <> "") { + $maskbits = $q['maskbits']; + } else { + $maskbits = ""; + } + if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") { + $maskbitsv6 = $q['maskbitsv6']; + } else { + $maskbitsv6 = ""; + } + $this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6)); + if (isset($q['buckets']) && $q['buckets'] <> "") { + $this->SetBuckets($q['buckets']); + } else { + $this->SetBuckets(""); + } + if (isset($q['plr']) && $q['plr'] <> "") { + $this->SetPlr($q['plr']); + } else { + $this->SetPlr(""); + } + if (isset($q['delay']) && $q['delay'] <> "") { + $this->SetDelay($q['delay']); + } else { + $this->SetDelay(0); + } + if (isset($q['description']) && $q['description'] <> "") { + $this->SetDescription($q['description']); + } else { + $this->SetDescription(""); + } + $this->SetEnabled($q['enabled']); + + } + + function build_tree() { + $tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $this->GetQname() ."&queue=".$this->GetQname() ."&action=show\">"; + $tree .= $this->GetQname() . "</a>"; + if (is_array($this->subqueues)) { + $tree .= "<ul>"; + foreach ($this->subqueues as $q) { + $tree .= $q->build_tree(); + } + $tree .= "</ul>"; + } + $tree .= "</li>"; + + return $tree; + } + + function build_rules() { + global $config, $time_based_rules; + + if ($this->GetEnabled() == "") { + return; + } + + $pfq_rule = "\npipe ". $this->GetNumber() . " config "; + $found = false; + $bandwidth = $this->GetBandwidth(); + if (is_array($bandwidth)) { + foreach ($bandwidth as $bw) { + if ($bw['bwsched'] != "none") { + $time_based_rules = true; + if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) { + foreach ($config['schedules']['schedule'] as $schedule) { + if ($bw['bwsched'] == $schedule['name']) { + if (filter_get_time_based_rule_status($schedule)) { + $pfq_rule .= " bw ".trim($bw['bw']).$bw['bwscale']; + if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) { + $pfq_rule .= " burst ".trim($bw['burst']); + } + $found = true; + break; + } + } + } + } else { + $pfq_rule .= " bw 0"; + $found = true; + break; + } + } else { + $pfq_rule .= " bw ".trim($bw['bw']).$bw['bwscale']; + if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) { + $pfq_rule .= " burst ".trim($bw['burst']); + } + $found = true; + break; + } + } + if ($found == false) { + $pfq_rule .= " bw 0"; + } + } else { + $pfq_rule .= " bw 0"; + } + + if ($this->GetQlimit()) { + $pfq_rule .= " queue " . $this->GetQlimit(); + } + if ($this->GetPlr()) { + $pfq_rule .= " plr " . $this->GetPlr(); + } + if ($this->GetBuckets()) { + $pfq_rule .= " buckets " . $this->GetBuckets(); + } + if ($this->GetDelay()) { + $pfq_rule .= " delay " . $this->GetDelay(); + } + $this->build_mask_rules($pfq_rule); + + $pfq_rule .= "\n"; + + if (!empty($this->subqueues) && count($this->subqueues) > 0) { + foreach ($this->subqueues as $q) { + $pfq_rule .= $q->build_rules(); + } + } + $pfq_rule .= " \n"; + + return $pfq_rule; + } + + function update_dn_data(&$data) { + $this->ReadConfig($data); + } + + function build_javascript() { + global $g, $config; + + $javasr = parent::build_javascript(); + + //build list of schedules + $schedules = "<option value='none'>none</option>"; + if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) { + foreach ($config['schedules']['schedule'] as $schedule) { + if ($schedule['name'] <> "") { + $schedules .= "<option value='{$schedule['name']}'>{$schedule['name']}</option>"; + } + } + } + $bwopt = ""; + foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) { + $bwopt .= "<option value='{$bwidx}'>{$bw}</option>"; + } + + $javasr .= <<<EOD +<script type='text/javascript'> +//<![CDATA[ +var addBwRowTo = (function() { + + return (function (tableId) { + + var table = document.getElementById(tableId); + var totalrows = table.rows.length -1; + + var row = table.insertRow(totalrows + 1); + var cell1 = row.insertCell(0); + var cell2 = row.insertCell(1); + var cell3 = row.insertCell(2); + var cell4 = row.insertCell(3); + + cell1.innerHTML = "<input type='hidden' value='" + totalrows +"' name='bandwidth_row-" + totalrows + "' /><input type='text' class='form-control' name='bandwidth" + totalrows + "' id='bandwidth" + totalrows + "' />"; + cell2.innerHTML = "<input type='hidden' value='" + totalrows +"' name='bwtype_row-" + totalrows + "' /><select class='form-control' name='bwtype" + totalrows + "'>{$bwopt}</select>"; + cell3.innerHTML = "<input type='hidden' value='" + totalrows +"' name='bwsched_row-" + totalrows + "' /><select class='form-control' name='bwsched" + totalrows + "'>{$schedules}</select>"; + cell4.innerHTML = '<a class="btn btn-default" onclick="removeBwRow(this); return false;" href="#">Remove</a>'; + + }); +})(); + +function removeBwRow(el) { + var d = el.parentNode.parentNode.rowIndex; + document.getElementById('maintable').deleteRow(d); +} +//]]> +</script> + +EOD; + + return $javasr; + } + + // Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText + // The table has been "Bootstrapped" to match the web design while maintaining compatibility with + // with the javascript in this class + function build_bwtable() { + global $config; + + $bandwidth = $this->GetBandwidth(); + //build list of schedules + $schedules = array(); + $schedules[] = "none";//leave none to leave rule enabled all the time + if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) { + foreach ($config['schedules']['schedule'] as $schedule) { + if ($schedule['name'] != "") { + $schedules[] = $schedule['name']; + } + } + } + + $form = '<div class="table-responsive">'; + $form .= '<table id="maintable" class="table table-hover table-striped">'; + $form .= "<thead><tr>"; + $form .= "<th>Bandwidth</th>"; + //$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>"; + $form .= "<th>Bw type</th>"; + $form .= "<th>Schedule</th>"; + $form .= "<th></th>"; + $form .= "</tr></thead>"; + $form .= "<tbody>"; + + // If there are no bandwidths defined, make a blank one for convenience + if(empty($bandwidth)) + $bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none')); + + if (is_array($bandwidth)) { + foreach ($bandwidth as $bwidx => $bw) { + $form .= '<tr>'; + $form .= '<td class="col-xs-4">'; + $form .= "<input class='form-control' type=\"number\" id=\"bandwidth{$bwidx}\" name=\"bandwidth{$bwidx}\" value=\"{$bw['bw']}\" />"; + //$form .= "</td><td width='20%'>"; + //$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />"; + $form .= "</td>"; + $form .= '<td class="col-xs-4">'; + $form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">"; + + foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwsidx => $bwscale) { + $form .= "<option value=\"{$bwsidx}\""; + + if ($bw['bwscale'] == $bwsidx) { + $form .= " selected=\"selected\""; + } + + $form .= ">{$bwscale}</option>"; + } + + $form .= "</select>"; + $form .= "</td>"; + $form .= '<td class="col-xs-4">'; + $form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">"; + + foreach ($schedules as $schd) { + $selected = ""; + if ($bw['bwsched'] == $schd) { + $selected = "selected=\"selected\""; + } + + $form .= "<option value='{$schd}' {$selected}>{$schd}</option>"; + } + + $form .= "</select>"; + $form .= "</td>"; + $form .= '<td>'; + $form .= '<a type="button" class="btn btn-default" onclick="removeBwRow(this); return false;">' . gettext('Remove') . '</a>'; + $form .= "</td></tr>"; + } + } + $form .= "</tbody></table></div><br />"; + + $form .= '<a type="button" class="btn btn-sm btn-success" onclick="javascript:addBwRowTo(\'maintable\'); return false;" >'; + $form .= gettext("Add another schedule") . "</a>"; + + return($form); + } + + function build_form() { + global $g, $config, $pipe, $action, $qname; + + //build list of schedules + $schedules = array(); + $schedules[] = "none";//leave none to leave rule enabled all the time + if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) { + foreach ($config['schedules']['schedule'] as $schedule) { + if ($schedule['name'] <> "") { + $schedules[] = $schedule['name']; + } + } + } + + + $sform = new Form(); + + $section = new Form_Section('Limiters'); + + $section->addInput(new Form_Checkbox( + 'enabled', + 'Enable', + 'Enable limiter and its children', + ($this->GetEnabled() == "on"), + 'on' + )); + + $section->addInput(new Form_Input( + 'newname', + 'Name', + 'text', + $this->GetQname() + )); + + $section->addInput(new Form_Input( + 'name', + null, + 'hidden', + $this->GetQname() + )); + + if ($this->GetNumber() > 0) { + $section->addInput(new Form_Input( + 'number', + null, + 'hidden', + $this->GetNumber() + )); + } + + $bandwidth = $this->GetBandwidth(); + + // Delete a row +// if(isset($_GET['delbwrow']) && (count($bandwidth) > 0)) +// unset($bandwidth[$_GET['delbwrow']]); + + // Add a row +// if($_GET['newbwrow']) { +// array_push($bandwidth, array(count($bandwidth) => array('bw' => '', 'burst' => '', 'bwscale' => 'Kb', 'bwsched' => 'none') )); +// } + + if (is_array($bandwidth)) { + $section->addInput(new Form_StaticText( + 'Bandwidth', + $this->build_bwtable() + )); + } + + $mask = $this->GetMask(); + + $section->addInput(new Form_Select( + 'scheduler', + 'Mask', + $mask['type'], + array('none' => 'None', 'srcaddress' => 'Source addresses', 'dstaddress' => 'Destination addresses') + ))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' . + 'and queue size given above will be created for each source/destination IP address encountered, respectively. ' . + 'This makes it possible to easily specify bandwidth limits per host.'); + + $group = new Form_Group(null); + + $group->add(new Form_Select( + 'maskbits', + null, + $mask['bits'], + array_combine(range(32, 1, -1), range(32, 1, -1)) + ))->setHelp('IPV4 mask bits' . '<br />' . '255.255.255.255/?'); + + $group->add(new Form_Select( + 'maskbitsv6', + null, + $mask['bitsv6'], + array_combine(range(128, 1, -1), range(128, 1, -1)) + ))->setHelp('IPV6 mask bits' . '<br />' . '<font face="consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</font>'); + + $section->add($group); + + $section->addInput(new Form_Input( + 'description', + 'Description', + 'text', + $this->GetDescription() + ))->setHelp('You may enter a description here for your reference (not parsed).'); + + $sform->add($section); + + $section = new Form_Section('Advanced options'); + + $section->addInput(new Form_Input( + 'delay', + 'Delay (ms)', + 'text', + $this->GetDelay() > 0 ? $this->GetDelay():null + ))->setHelp('In most cases, you should specify 0 here (or leave the field empty)'); + + $section->addInput(new Form_Input( + 'plr', + 'Packet Loss Rate', + 'number', + $this->GetPlr(), + ['step' => '0.001', 'min' => '0.000'] + ))->setHelp('In most cases, you should specify 0 here (or leave the field empty). ' . + 'A value of 0.001 means one packet in 1000 gets dropped'); + + $section->addInput(new Form_Input( + 'qlimit', + 'Queue size (slots)', + 'number', + $this->GetQlimit() + ))->setHelp('In most cases, you should leave the field empty. All packets in this pipe are placed into a fixed-size queue first, ' . + 'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.'); + + $section->addInput(new Form_Input( + 'buckets', + 'Bucket size (slots)', + 'number', + $this->GetBuckets() + ))->setHelp('In most cases, you should leave this field empty. It increases the hash size set'); + + $sform->add($section); + + return($sform); + } + + function wconfig() { + $cflink =& get_dn_reference_to_me_in_config($this->GetLink()); + if (!is_array($cflink)) { + $cflink = array(); + } + $cflink['name'] = $this->GetQname(); + $cflink['number'] = $this->GetNumber(); + $cflink['qlimit'] = $this->GetQlimit(); + $cflink['plr'] = $this->GetPlr(); + $cflink['description'] = $this->GetDescription(); + + $bandwidth = $this->GetBandwidth(); + if (is_array($bandwidth)) { + $cflink['bandwidth'] = array(); + $cflink['bandwidth']['item'] = array(); + foreach ($bandwidth as $bwidx => $bw) { + $cflink['bandwidth']['item'][] = $bw; + } + } + + $cflink['enabled'] = $this->GetEnabled(); + $cflink['buckets'] = $this->GetBuckets(); + $mask = $this->GetMask(); + $cflink['mask'] = $mask['type']; + $cflink['maskbits'] = $mask['bits']; + $cflink['maskbitsv6'] = $mask['bitsv6']; + $cflink['delay'] = $this->GetDelay(); + } + +} + +class dnqueue_class extends dummynet_class { + var $pipeparent; + var $weight; + + function GetWeight() { + return $this->weight; + } + function SetWeight($weight) { + $this->weight = $weight; + } + function GetPipe() { + return $this->pipeparent; + } + function SetPipe($pipe) { + $this->pipeparent = $pipe; + } + + /* Just a stub in case we ever try to call this from the frontend. */ + function &add_queue($interface, &$queue, &$path, &$input_errors) { + return; + } + + function delete_queue() { + cleanup_dnqueue_from_rules($this->GetQname()); + unset_dn_object_by_reference($this->GetLink()); + @pfSense_pipe_action("queue delete " . $this->GetNumber()); + } + + function validate_input($data, &$input_errors) { + parent::validate_input($data, $input_errors); + + if ($data['weight'] && ((!is_numeric($data['weight'])) || + ($data['weight'] < 1 && $data['weight'] > 100))) { + $input_errors[] = gettext("Weight must be an integer between 1 and 100."); + } + } + + /* + * Should search even its children + */ + function &find_queue($pipe, $qname) { + if ($qname == $this->GetQname()) { + return $this; + } else { + return NULL; + } + } + + function &find_parentqueue($pipe, $qname) { + return $this->qparent; + } + + function &get_queue_list(&$qlist) { + if ($this->GetEnabled() == "") { + return; + } + $qlist[$this->GetQname()] = "?" .$this->GetNumber(); + } + + function ReadConfig(&$q) { + if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) { + $this->SetQname($q['newname']); + } else if (!empty($q['newname'])) { + $this->SetQname($q['newname']); + } else { + $this->SetQname($q['name']); + } + $this->SetNumber($q['number']); + if (isset($q['qlimit']) && $q['qlimit'] <> "") { + $this->SetQlimit($q['qlimit']); + } else { + $this->SetQlimit(""); + } + if (isset($q['mask']) && $q['mask'] <> "") { + $masktype = $q['mask']; + } else { + $masktype = ""; + } + if (isset($q['maskbits']) && $q['maskbits'] <> "") { + $maskbits = $q['maskbits']; + } else { + $maskbits = ""; + } + if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") { + $maskbitsv6 = $q['maskbitsv6']; + } else { + $maskbitsv6 = ""; + } + $this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6)); + if (isset($q['buckets']) && $q['buckets'] <> "") { + $this->SetBuckets($q['buckets']); + } else { + $this->SetBuckets(""); + } + if (isset($q['plr']) && $q['plr'] <> "") { + $this->SetPlr($q['plr']); + } else { + $this->SetPlr(""); + } + if (isset($q['weight']) && $q['weight'] <> "") { + $this->SetWeight($q['weight']); + } else { + $this->SetWeight(""); + } + if (isset($q['description']) && $q['description'] <> "") { + $this->SetDescription($q['description']); + } else { + $this->SetDescription(""); + } + $this->SetEnabled($q['enabled']); + } + + function build_tree() { + $parent =& $this->GetParent(); + $tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $parent->GetQname() ."&queue=" . $this->GetQname() ."&action=show\">"; + $tree .= $this->GetQname() . "</a>"; + $tree .= "</li>"; + + return $tree; + } + + function build_rules() { + if ($this->GetEnabled() == "") { + return; + } + + $parent =& $this->GetParent(); + $pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber(); + if ($this->GetQlimit()) { + $pfq_rule .= " queue " . $this->GetQlimit(); + } + if ($this->GetWeight()) { + $pfq_rule .= " weight " . $this->GetWeight(); + } + if ($this->GetBuckets()) { + $pfq_rule .= " buckets " . $this->GetBuckets(); + } + $this->build_mask_rules($pfq_rule); + $pfq_rule .= "\n"; + + return $pfq_rule; + } + + function build_javascript() { + return parent::build_javascript(); + } + + function build_form() { + global $g, $config, $pipe, $action, $qname; + + //build list of schedules + $schedules = array(); + $schedules[] = "none";//leave none to leave rule enabled all the time + if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) { + foreach ($config['schedules']['schedule'] as $schedule) { + if ($schedule['name'] <> "") { + $schedules[] = $schedule['name']; + } + } + } + + + $sform = new Form(); + + $section = new Form_Section('Limiters'); + + $section->addInput(new Form_Checkbox( + 'enabled', + 'Enable', + 'Enable this queue', + ($this->GetEnabled() == "on"), + 'on' + )); + + $section->addInput(new Form_Input( + 'newname', + 'Name', + 'text', + $this->GetQname() + )); + + $section->addInput(new Form_Input( + 'name', + null, + 'hidden', + $this->GetQname() + )); + + if ($this->GetNumber() > 0) { + $section->addInput(new Form_Input( + 'number', + null, + 'hidden', + $this->GetNumber() + )); + } + + $bandwidth = $this->GetBandwidth(); + + // Delete a row + if(isset($_GET['delbwrow']) && (count($bandwidth) > 0)) + unset($bandwidth[$_GET['delbwrow']]); + + // Add a row + if($_GET['newbwrow']) { + array_push($bandwidth, array(count($bandwidth) => array('bw' => '', 'burst' => '', 'bwscale' => 'Kb', 'bwsched' => 'none') )); + + } + + if (is_array($bandwidth)) { + $row = 0; + $numrows = count($bandwidth) - 1; + + if($numrows >= 0) { + foreach ($bandwidth as $bwidx => $bw) { + $group = new Form_Group($row == 0 ? 'Bandwidth':null); + + $group->add(new Form_Input( + 'bandwidth' . $bwidx, + null, + 'text', + $bw['bw'] + ))->setHelp($row == $numrows ? 'Bandwidth':null); + + $group->add(new Form_Select( + 'bwtype' . $bwidx, + null, + $bw['bwscale'], + array('Kb' => 'Kbit/s', 'Mb' => 'Mbit/s', 'Gb' => 'Gbit/s', 'b' => 'Bit/s') + ))->setHelp($row == $numrows ? 'Bw Type':null);; + + $group->add(new Form_Select( + 'bwsched' . $bwidx, + null, + $bw['bwsched'], + $schedules + ))->setHelp($row == $numrows ? 'Schedule':null);; + + $group->add(new Form_Button( + 'delete' + $bwidx, + 'Delete', + 'firewall_shaper_vinterface.php?pipe=' . $pipe . '&queue=' . $qname . '&action=' . $action . '&delbwrow=' . $bwidx + ))->removeClass('btn-primary')->addClass('btn-danger btn-sm'); + + if($row == $numrows) + $group->setHelp('Bandwidth is the rate (e.g. Mbit/s) to which traffic in this limiter will be restricted.'); + + $section->add($group); + $row++; + } + } + else { // The $bandwidth array exists, but is empty + $section->addInput(new Form_StaticText( + 'Bandwidth', + 'No schedules configured for this limiter.' + )); + } + + $section->addInput(new Form_Button( + 'addsched', + 'Add new schedule', + 'firewall_shaper_vinterface.php?pipe=' . $pipe . '&queue=' . $qname . '&action=' . $action . '&newbwrow=yes' + ))->removeClass('btn-primary')->addClass('btn-success btn-sm'); + } + + $mask = $this->GetMask(); + + $section->addInput(new Form_Select( + 'scheduler', + 'Mask', + $mask['type'], + array('none' => 'None', 'srcaddress' => 'Source addresses', 'dstaddress' => 'Destination addresses') + ))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' . + 'and queue size given above will be created for each source/destination IP address encountered, respectively. ' . + 'This makes it possible to easily specify bandwidth limits per host.'); + + $group = new Form_Group(null); + + $group->add(new Form_Select( + 'maskbits', + null, + $mask['bits'], + array_combine(range(32, 1, -1), range(32, 1, -1)) + ))->setHelp('IPV4 mask bits' . '<br />' . '255.255.255.255/?'); + + $group->add(new Form_Select( + 'maskbitsv6', + null, + $mask['bitsv6'], + array_combine(range(128, 1, -1), range(128, 1, -1)) + ))->setHelp('IPV6 mask bits' . '<br />' . '<font face="consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</font>'); + + $section->add($group); + + $section->addInput(new Form_Input( + 'description', + 'Description', + 'text', + $this->GetDescription() + ))->setHelp('You may enter a description here for your reference (not parsed).'); + + $sform->add($section); + + $section = new Form_Section('Advanced options'); + + $section->addInput(new Form_Input( + 'weight', + 'Weight', + 'number', + $this->GetWeight(), + ['min' => '1', 'max' => '100'] + ))->setHelp('For queues under the same parent this specifies the share that a queue gets(values range from 1 to 100),' . + ' you can leave it blank otherwise'); + + $section->addInput(new Form_Input( + 'plr', + 'Packet Loss Rate', + 'number', + $this->GetPlr(), + ['step' => '0.001', 'min' => '0.000'] + ))->setHelp('In most cases, you should specify 0 here (or leave the field empty). ' . + 'A value of 0.001 means one packet in 1000 gets dropped'); + + $section->addInput(new Form_Input( + 'qlimit', + 'Queue size (slots)', + 'number', + $this->GetQlimit() + ))->setHelp('In most cases, you should leave the field empty. All packets in this pipe are placed into a fixed-size queue first, ' . + 'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.'); + + $section->addInput(new Form_Input( + 'buckets', + 'Bucket size (slots)', + 'number', + $this->GetBuckets() + ))->setHelp('In most cases, you should leave this field empty. It increases the hash size set'); + + $section->addInput(new Form_Input( + 'pipe', + null, + 'hidden', + $this->GetPipe() + )); + + $sform->add($section); + + return($sform); + } + + function update_dn_data(&$data) { + $this->ReadConfig($data); + } + + function wconfig() { + $cflink =& get_dn_reference_to_me_in_config($this->GetLink()); + if (!is_array($cflink)) { + $cflink = array(); + } + $cflink['name'] = $this->GetQname(); + $cflink['number'] = $this->GetNumber(); + $cflink['qlimit'] = $this->GetQlimit(); + $cflink['description'] = $this->GetDescription(); + $cflink['weight'] = $this->GetWeight(); + $cflink['enabled'] = $this->GetEnabled(); + $cflink['buckets'] = $this->GetBuckets(); + $mask = $this->GetMask(); + $cflink['mask'] = $mask['type']; + $cflink['maskbits'] = $mask['bits']; + $cflink['maskbitsv6'] = $mask['bitsv6']; + } +} + +// List of layer7 objects +$layer7_rules_list = array(); + +class layer7 { + + var $rname; //alias + var $rdescription; //alias description + var $rport; //divert port + var $renabled; //rule enabled + var $rsets = array(); //array of l7 associations + + // Auxiliary functions + + function GetRName() { + return $this->rname; + } + function SetRName($rname) { + $this->rname = $rname; + } + function GetRDescription() { + return $this->rdescription; + } + function SetRDescription($rdescription) { + $this->rdescription = $rdescription; + } + function GetRPort() { + return $this->rport; + } + function SetRPort($rport) { + $this->rport = $rport; + } + function GetREnabled() { + return $this->renabled; + } + function SetREnabled($value) { + $this->renabled = $value; + } + function GetRl7() { + return $this->rsets; + } + function SetRl7($rsets) { + $this->rsets = $rsets; + } + + //Add a tuple (rule,structure,element) to the $rsets + + function add_rule($l7set) { + $this->rsets[] = $l7set; + } + + // Build the layer7 rules + function build_l7_rules() { + if ($this->GetREnabled() == "") { + return; + } + //$l7rules = "#" . $this->rdescription . "\n"; + foreach ($this->rsets as $rl7) { + $l7rules .= $rl7->build_rules(); + } + return $l7rules; + } + + // Read the config from array + function ReadConfig(&$qname, &$q) { + $this->SetRName($qname); + $this->SetREnabled($q['enabled']); + $this->SetRPort($q['divert_port']); + if (isset($q['description']) && $q['description'] <> "") { + $this->SetRDescription($q['description']); + } + $rsets = $q['l7rules']; + //Put individual rules in the array + if (is_array($rsets)) { + $this->rsets = array(); // XXX: ugly hack + foreach ($rsets as $l7r) { + $l7obj = new l7rule(); + $l7obj->SetRProtocol($l7r['protocol']); + $l7obj->SetRStructure($l7r['structure']); + $l7obj->SetRBehaviour($l7r['behaviour']); + $this->add_rule($l7obj); + } + } + } + + //Generate a random port for the divert socket + function gen_divert_port() { + $dports = get_divert_ports(); //array of used ports + $divert_port = 1; // Initialize + while (($divert_port % 2) != 0 || in_array($divert_port, $dports)) { + $divert_port = rand(40000, 60000); + } + return $divert_port; + } + + //Helps building the left tree + function build_tree() { + $tree = " <li><a href=\"firewall_shaper_layer7.php?container=" . $this->GetRName() ."&action=show\">"; + $tree .= $this->GetRName() . "</a>"; + $tree .= "</li>"; + + return $tree; + } + + function build_form() { + + $form = new Form(new Form_Button( + 'Submit', + 'Save' + )); + + $section = new Form_Section('Traffic Shaper'); + + $section->addInput(new Form_Checkbox( + 'enabled', + 'Enable/Disable', + 'Enable/disable discipline and its children', + ($this->GetREnabled() == "on"), + 'on' + )); + + $section->addInput(new Form_Input( + 'container', + 'Name', + 'text', + $this->GetRName() + )); + + $section->addInput(new Form_Input( + 'description', + 'Description', + 'text', + $this->GetRDescription() + ))->setHelp('You may enter a description here for your reference (not parsed).'); + + $form->add($section); + + return $form; + } + + //Write the setting to the $config array + function wconfig() { + global $config; + + if (!is_array($config['l7shaper']['container'])) { + $config['l7shaper']['container'] = array(); + } + // + $cflink =& get_l7c_reference_to_me_in_config($this->GetRName()); + // Test if this rule exists already + if (!$cflink) { + $cflink =& $config['l7shaper']['container'][]; + } + $cflink['name'] = $this->GetRName(); + $cflink['enabled'] = $this->GetREnabled(); + $cflink['description'] = $this->GetRDescription(); + $cflink['divert_port'] = $this->GetRPort(); + + // Destroy previously existent rules + if (is_array($cflink['rules'])) { + unset($cflink['l7rules']); + } + + $cflink['l7rules'] = array(); + + $i = 0; + foreach ($this->rsets as $rulel7) { + $cflink['l7rules'][$i]['protocol'] = $rulel7->GetRProtocol(); + $cflink['l7rules'][$i]['structure'] = $rulel7->GetRStructure(); + $cflink['l7rules'][$i]['behaviour'] = $rulel7->GetRBehaviour(); + $i++; + } + } + + //This function is necessary to help producing the overload options for keep state + function get_unique_structures() { + + $unique_structures = array("action" => false, "dummynet" => false, "altq" => false); + foreach ($this->rsets as $l7rule) { + if ($l7rule->GetRStructure() == "action") { + $unique_structures['action'] = true; + } else if ($l7rule->GetRStructure() == "limiter") { + $unique_structures['dummynet'] = true; + } else { + $unique_structures['altq'] = true; + } + } + //Delete non used structures so we don't have to check this in filter.inc + foreach ($unique_structures as $key => $value) { + if (!$value) { + unset($unique_structures[$key]); + } + } + return $unique_structures; + } + + function validate_input($data, &$input_errors) { + $reqdfields[] = "container"; + $reqdfieldsn[] = gettext("Name"); + + shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors); + + if (!preg_match("/^[a-zA-Z0-9_-]+$/", $data['container'])) { + $input_errors[] = gettext("Queue names must be alphanumeric and _ or - only."); + } + } + + function delete_l7c() { + mwexec("/bin/pkill -f 'ipfw-classifyd .* -p ". $this->GetRPort() . "'", true); + unset_l7_object_by_reference($this->GetRName()); + cleanup_l7_from_rules($this->GetRName()); + } +} + +class l7rule { + + var $rprotocol; //protocol + var $rstructure; //action, limiter, queue + var $rbehaviour; //allow, block, queue_name, pipe_number ... + + //Auxiliary Functions + + function GetRProtocol() { + return $this->rprotocol; + } + function SetRProtocol($rprotocol) { + $this->rprotocol = $rprotocol; + } + function GetRStructure() { + return $this->rstructure; + } + function SetRStructure($rstructure) { + $this->rstructure = $rstructure; + } + function GetRBehaviour() { + return $this->rbehaviour; + } + function SetRBehaviour($rbehaviour) { + $this->rbehaviour = $rbehaviour; + } + + //XXX Do we need to test any particularity for AltQ queues? + function build_rules() { + global $dummynet_pipe_list; + switch ($this->GetRStructure()) { + case "limiter": + read_dummynet_config(); + $dn_list =& get_unique_dnqueue_list(); + $found = false; + if (is_array($dn_list)) { + foreach ($dn_list as $key => $value) { + if ($key == $this->GetRBehaviour()) { + if ($value[0] == "?") { + $l7rule = $this->GetRProtocol() . " = dnqueue " . substr($value, 1) . "\n"; + } else { + $l7rule = $this->GetRProtocol() . " = dnpipe " . $value . "\n"; + } + $found = true; + } + if ($found) { + break; + } + } + } + break; + default: //This is for action and for altq + $l7rule = $this->GetRProtocol() . " = " . $this->GetRStructure() . " " . $this->GetRBehaviour() . "\n"; + break; + } + return $l7rule; + } +} + +/* + * This function allows to return an array with all the used divert socket ports + */ +function get_divert_ports() { + global $layer7_rules_list; + $dports = array(); + + foreach ($layer7_rules_list as $l7r) { + $dports[] = $l7r->GetRPort(); + } + + return $dports; +} + +function &get_l7c_reference_to_me_in_config(&$name) { + global $config; + + $ptr = NULL; + + if (is_array($config['l7shaper']['container'])) { + foreach ($config['l7shaper']['container'] as $key => $value) { + if ($value['name'] == $name) { + $ptr =& $config['l7shaper']['container'][$key]; + } + } + } + return $ptr; + // $ptr can be null. has to be checked later +} + +function unset_l7_object_by_reference(&$name) { + global $config; + + if (is_array($config['l7shaper']['container'])) { + foreach ($config['l7shaper']['container'] as $key => $value) { + if ($value['name'] == $name) { + unset($config['l7shaper']['container'][$key]['l7rules']); + unset($config['l7shaper']['container'][$key]); + break; + } + } + } +} + +function read_layer7_config() { + global $layer7_rules_list, $config; + + if (!is_array($config['l7shaper']['container']) || !count($config['l7shaper']['container'])) { + $layer7_rules_list = array(); + return; + } + + $l7cs = &$config['l7shaper']['container']; + + $layer7_rules_list = array(); + + foreach ($l7cs as $conf) { + if (empty($conf['name'])) { + continue; /* XXX: grrrrrr at php */ + } + $root =& new layer7(); + $root->ReadConfig($conf['name'],$conf); + $layer7_rules_list[$root->GetRName()] = &$root; + } +} + +function update_layer7_custom_patterns() { + global $config; + + if (!is_array($config['l7shaper']['custom_pat'])) { + return; + } + + foreach ($config['l7shaper']['custom_pat'] as $filename => $filecontent) { + if (!file_exists("/usr/local/share/protocols/" . $filename)) { + @file_put_contents("/usr/local/share/protocols/" . $filename, base64_decode($filecontent)); + } + } +} + +function generate_layer7_files() { + global $layer7_rules_list, $g; + + read_layer7_config(); + + if (!empty($layer7_rules_list)) { + if (!is_module_loaded("ipdivert.ko")) { + mwexec("/sbin/kldload ipdivert.ko"); + } + + array_map('unlink', glob("{$g['tmp_path']}/*.l7")); + } + + update_layer7_custom_patterns(); + + foreach ($layer7_rules_list as $l7rules) { + if ($l7rules->GetREnabled()) { + $filename = $l7rules->GetRName() . ".l7"; + $path = "{$g['tmp_path']}/" . $filename; + + $rules = $l7rules->build_l7_rules(); + + $fp = fopen($path,'w'); + fwrite($fp,$rules); + fclose($fp); + } + } +} + +function layer7_start_l7daemon() { + global $layer7_rules_list, $g; + + /* + * XXX: ermal - Needed ?! + * read_layer7_config(); + */ + + foreach ($layer7_rules_list as $l7rules) { + if ($l7rules->GetREnabled()) { + $filename = $l7rules->GetRName() . ".l7"; + $path = "{$g['tmp_path']}/" . $filename; + + unset($l7pid); + /* Only reread the configuration rather than restart to avoid losing information. */ + exec("/bin/pgrep -f 'ipfw-classifyd .* -p ". $l7rules->GetRPort() . "'", $l7pid); + if (count($l7pid) > 0) { + log_error(sprintf(gettext("Sending HUP signal to %s"), $l7pid[0])); + mwexec("/bin/kill -HUP {$l7pid[0]}"); + } else { + // XXX: Hardcoded number of packets to garbage collect and queue length. + $ipfw_classifyd_init = "/usr/local/sbin/ipfw-classifyd -n 8 -q 700 -c {$path} -p " . $l7rules->GetRPort() . " -P /usr/local/share/protocols"; + mwexec_bg($ipfw_classifyd_init); + } + } + } +} + +// This function uses /usr/local/share/protocols as a default directory for searching .pat files +function generate_protocols_array() { + + update_layer7_custom_patterns(); + + $protocols = return_dir_as_array("/usr/local/share/protocols"); + $protocols_new = array(); + if (is_array($protocols)) { + foreach ($protocols as $key => $proto) { + if (strstr($proto, ".pat")) { + $protocols_new[$key] =& str_replace(".pat", "", $proto); + } + } + sort($protocols_new); + } + return $protocols_new; +} + +function get_l7_unique_list() { + global $layer7_rules_list; + + $l7list = array(); + if (is_array($layer7_rules_list)) { + foreach ($layer7_rules_list as $l7c) { + if ($l7c->GetREnabled()) { + $l7list[] = $l7c->GetRName(); + } + } + } + + return $l7list; +} + +// Disable a removed l7 container from the filter +function cleanup_l7_from_rules(&$name) { + global $config; + + if (is_array($config['filter']['rule'])) { + foreach ($config['filter']['rule'] as $key => $rule) { + if ($rule['l7container'] == $name) { + unset($config['filter']['rule'][$key]['l7container']); + } + } + } +} + +function get_dummynet_name_list() { + + $dn_name_list =& get_unique_dnqueue_list(); + $dn_name = array(); + if (is_array($dn_name_list)) { + foreach ($dn_name_list as $key => $value) { + $dn_name[] = $key; + } + } + + return $dn_name; + +} + +function get_altq_name_list() { + $altq_name_list =& get_unique_queue_list(); + $altq_name = array(); + if (is_array($altq_name_list)) { + foreach ($altq_name_list as $key => $aqobj) { + $altq_name[] = $key; + } + } + + return $altq_name; +} + +/* + * XXX: TODO Make a class shaper to hide all these functions + * from the global namespace. + */ + +/* + * This is a layer violation but for now there is no way + * I can find to properly do this with PHP. + */ +function altq_get_default_queue($interface) { + global $altq_list_queues; + + $altq_tmp = $altq_list_queues[$interface]; + if ($altq_tmp) { + return $altq_tmp->GetDefaultQueuePresent(); + } else { + return false; + } +} + +function altq_check_default_queues() { + global $altq_list_queues; + + $count = 0; + if (is_array($altq_list_queues)) { + foreach ($altq_list_queues as $altq) { + if ($altq->GetDefaultQueuePresent()) { + $count++; + } + } + } + else { + $count++; + } + + return 0; +} + +function &get_unique_queue_list() { + global $altq_list_queues; + + $qlist = array(); + if (is_array($altq_list_queues)) { + foreach ($altq_list_queues as $altq) { + if ($altq->GetEnabled() == "") { + continue; + } + $tmplist =& $altq->get_queue_list(); + foreach ($tmplist as $qname => $link) { + if ($link->GetEnabled() <> "") { + $qlist[$qname] = $link; + } + } + } + } + return $qlist; +} + +function &get_unique_dnqueue_list() { + global $dummynet_pipe_list; + + $qlist = array(); + if (is_array($dummynet_pipe_list)) { + foreach ($dummynet_pipe_list as $dn) { + if ($dn->GetEnabled() == "") { + continue; + } + $tmplist =& $dn->get_queue_list(); + foreach ($tmplist as $qname => $link) { + $qlist[$qname] = $link; + } + } + } + return $qlist; +} + +function ref_on_altq_queue_list($parent, $qname) { + if (isset($GLOBALS['queue_list'][$qname])) { + $GLOBALS['queue_list'][$qname]++; + } else { + $GLOBALS['queue_list'][$qname] = 1; + } + + unref_on_altq_queue_list($parent); +} + +function unref_on_altq_queue_list($qname) { + $GLOBALS['queue_list'][$qname]--; + if ($GLOBALS['queue_list'][$qname] <= 1) { + unset($GLOBALS['queue_list'][$qname]); + } +} + +function read_altq_config() { + global $altq_list_queues, $config; + $path = array(); + + if (!is_array($config['shaper'])) { + $config['shaper'] = array(); + } + if (!is_array($config['shaper']['queue'])) { + $config['shaper']['queue'] = array(); + } + $a_int = &$config['shaper']['queue']; + + $altq_list_queues = array(); + + if (!is_array($config['shaper']['queue'])) { + return; + } + + foreach ($a_int as $key => $conf) { + $int = $conf['interface']; + $root =& new altq_root_queue(); + $root->SetInterface($int); + $altq_list_queues[$root->GetInterface()] = &$root; + $root->ReadConfig($conf); + array_push($path, $key); + $root->SetLink($path); + if (is_array($conf['queue'])) { + foreach ($conf['queue'] as $key1 => $q) { + array_push($path, $key1); + /* + * XXX: we completely ignore errors here but anyway we must have + * checked them before so no harm should be come from this. + */ + $root->add_queue($root->GetInterface(), $q, $path, $input_errors); + array_pop($path); + } + } + array_pop($path); + } +} + +function read_dummynet_config() { + global $dummynet_pipe_list, $config; + $path = array(); + + if (!is_array($config['dnshaper'])) { + $config['dnshaper'] = array(); + } + if (!is_array($config['dnshaper']['queue'])) { + $config['dnshaper']['queue'] = array(); + } + $a_int = &$config['dnshaper']['queue']; + + $dummynet_pipe_list = array(); + + if (!is_array($config['dnshaper']['queue']) || + !count($config['dnshaper']['queue'])) { + return; + } + + foreach ($a_int as $key => $conf) { + if (empty($conf['name'])) { + continue; /* XXX: grrrrrr at php */ + } + $root =& new dnpipe_class(); + $root->ReadConfig($conf); + $dummynet_pipe_list[$root->GetQname()] = &$root; + array_push($path, $key); + $root->SetLink($path); + if (is_array($conf['queue'])) { + foreach ($conf['queue'] as $key1 => $q) { + array_push($path, $key1); + /* + * XXX: we completely ignore errors here but anyway we must have + * checked them before so no harm should be come from this. + */ + $root->add_queue($root->GetQname(), $q, $path, $input_errors); + array_pop($path); + } + } + array_pop($path); + } +} + +function get_interface_list_to_show() { + global $altq_list_queues, $config; + global $shaperIFlist; + + $tree = ""; + foreach ($shaperIFlist as $shif => $shDescr) { + if ($altq_list_queues[$shif]) { + continue; + } else { + if (!is_altq_capable(get_real_interface($shif))) { + continue; + } + $tree .= " <li><a href=\"firewall_shaper.php?interface=".$shif."&action=add\">".$shDescr."</a></li>"; + } + } + + return $tree; +} + +function filter_generate_altq_queues() { + global $altq_list_queues; + + read_altq_config(); + + $altq_rules = ""; + foreach ($altq_list_queues as $altq) { + $altq_rules .= $altq->build_rules(); + } + + return $altq_rules; +} + +function dnqueue_find_nextnumber() { + global $dummynet_pipe_list; + + $dnused = array(); + if (is_array($dummynet_pipe_list)) { + foreach ($dummynet_pipe_list as $dn) { + $tmplist =& $dn->get_queue_list(); + foreach ($tmplist as $qname => $link) { + if ($link[0] == "?") { + $dnused[$qname] = substr($link, 1); + } + } + } + } + + sort($dnused, SORT_NUMERIC); + $dnnumber = 0; + $found = false; + foreach ($dnused as $dnnum) { + if (($dnnum - $dnnumber) > 1) { + $dnnumber = $dnnum - 1; + $found = true; + break; + } else { + $dnnumber = $dnnum; + } + } + + if ($found == false) { + $dnnumber++; + } + + unset($dnused, $dnnum, $found); + return $dnnumber; +} + +function dnpipe_find_nextnumber() { + global $dummynet_pipe_list; + + $dnused = array(); + foreach ($dummynet_pipe_list as $dn) { + $dnused[] = $dn->GetNumber(); + } + + sort($dnused, SORT_NUMERIC); + $dnnumber = 0; + $found = false; + foreach ($dnused as $dnnum) { + if (($dnnum - $dnnumber) > 1) { + $dnnumber = $dnnum - 1; + $found = true; + break; + } else { + $dnnumber = $dnnum; + } + } + + if ($found == false) { + $dnnumber++; + } + + unset($dnused, $dnnum, $found); + return $dnnumber; +} + +function filter_generate_dummynet_rules() { + global $g, $dummynet_pipe_list; + + read_dummynet_config(); + + $dn_rules = ""; + foreach ($dummynet_pipe_list as $dn) { + $dn_rules .= $dn->build_rules(); + } + + if (!empty($dn_rules)) { + if (!is_module_loaded("dummynet.ko")) { + mwexec("/sbin/kldload dummynet"); + set_sysctl(array( + "net.inet.ip.dummynet.io_fast" => "1", + "net.inet.ip.dummynet.hash_size" => "256" + )); + } + file_put_contents("{$g['tmp_path']}/rules.limiter", $dn_rules); + mwexec("/sbin/ipfw {$g['tmp_path']}/rules.limiter"); + } +} + +function build_iface_without_this_queue($iface, $qname) { + global $g, $altq_list_queues; + global $shaperIFlist; + + $altq =& $altq_list_queues[$iface]; + + if ($altq) { + $scheduler = $altq->GetScheduler(); + } + + $form = '<dl class="dl-horizontal">'; + + $form .= ' <dt>'; + $form .= ' <a href="firewall_shaper.php?interface=' . $iface . '&queue=' . $iface . '&action=show">' . $shaperIFlist[$iface] . '</a>'; + $form .= ' </dt>'; + $form .= ' <dd>'; + $form .= $scheduler; + $form .= ' </dd>'; + + $form .= ' <dt>'; + $form .= 'Clone'; + $form .= ' </dt>'; + $form .= ' <dd>'; + $form .= '<a class="btn btn-info btn-xs" href="firewall_shaper_queues.php?interface='; + $form .= $iface . '&queue='; + $form .= $qname . '&action=add">'; + $form .= gettext("Clone shaper on the I/F") . '</a>'; + $form .= ' </dd>'; + + $form .= '</dl>'; + + return $form; + +} + +$default_shaper_msg = sprintf(gettext("Welcome to the %s Traffic Shaper."), $g['product_name']) . "</b><br />"; +$default_shaper_msg .= gettext("The tree on the left helps you navigate through the queues.<br />" + . "Buttons at the bottom represent queue actions and are activated accordingly."); + +$dn_default_shaper_msg = $default_shaper_msg; + +?> diff --git a/src/etc/inc/simplepie/LICENSE.txt b/src/etc/inc/simplepie/LICENSE.txt new file mode 100644 index 0000000..a822a4b --- /dev/null +++ b/src/etc/inc/simplepie/LICENSE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2004-2007, Ryan Parman and Geoffrey Sneddon. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + * Neither the name of the SimplePie Team nor the names of its contributors may be used + to endorse or promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS +AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file diff --git a/src/etc/inc/simplepie/simplepie.inc b/src/etc/inc/simplepie/simplepie.inc new file mode 100644 index 0000000..32f158e --- /dev/null +++ b/src/etc/inc/simplepie/simplepie.inc @@ -0,0 +1,13663 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2008, Ryan Parman and Geoffrey Sneddon + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.1.3 + * @copyright 2004-2008 Ryan Parman, Geoffrey Sneddon + * @author Ryan Parman + * @author Geoffrey Sneddon + * @link http://simplepie.org/ SimplePie + * @link http://simplepie.org/support/ Please submit all bug reports and feature requests to the SimplePie forums + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @todo phpDoc comments + */ + +/** + * SimplePie Name + */ +define('SIMPLEPIE_NAME', 'SimplePie'); + +/** + * SimplePie Version + */ +define('SIMPLEPIE_VERSION', '1.1.3'); + +/** + * SimplePie Build + */ +define('SIMPLEPIE_BUILD', 20081219); + +/** + * SimplePie Website URL + */ +define('SIMPLEPIE_URL', 'http://simplepie.org'); + +/** + * SimplePie Useragent + * @see SimplePie::set_useragent() + */ +define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed Parser; ' . SIMPLEPIE_URL . '; Allow like Gecko) Build/' . SIMPLEPIE_BUILD); + +/** + * SimplePie Linkback + */ +define('SIMPLEPIE_LINKBACK', '<a href="' . SIMPLEPIE_URL . '" title="' . SIMPLEPIE_NAME . ' ' . SIMPLEPIE_VERSION . '">' . SIMPLEPIE_NAME . '</a>'); + +/** + * No Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_NONE', 0); + +/** + * Feed Link Element Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1); + +/** + * Local Feed Extension Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2); + +/** + * Local Feed Body Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4); + +/** + * Remote Feed Extension Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8); + +/** + * Remote Feed Body Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16); + +/** + * All Feed Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_ALL', 31); + +/** + * No known feed type + */ +define('SIMPLEPIE_TYPE_NONE', 0); + +/** + * RSS 0.90 + */ +define('SIMPLEPIE_TYPE_RSS_090', 1); + +/** + * RSS 0.91 (Netscape) + */ +define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2); + +/** + * RSS 0.91 (Userland) + */ +define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4); + +/** + * RSS 0.91 (both Netscape and Userland) + */ +define('SIMPLEPIE_TYPE_RSS_091', 6); + +/** + * RSS 0.92 + */ +define('SIMPLEPIE_TYPE_RSS_092', 8); + +/** + * RSS 0.93 + */ +define('SIMPLEPIE_TYPE_RSS_093', 16); + +/** + * RSS 0.94 + */ +define('SIMPLEPIE_TYPE_RSS_094', 32); + +/** + * RSS 1.0 + */ +define('SIMPLEPIE_TYPE_RSS_10', 64); + +/** + * RSS 2.0 + */ +define('SIMPLEPIE_TYPE_RSS_20', 128); + +/** + * RDF-based RSS + */ +define('SIMPLEPIE_TYPE_RSS_RDF', 65); + +/** + * Non-RDF-based RSS (truly intended as syndication format) + */ +define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190); + +/** + * All RSS + */ +define('SIMPLEPIE_TYPE_RSS_ALL', 255); + +/** + * Atom 0.3 + */ +define('SIMPLEPIE_TYPE_ATOM_03', 256); + +/** + * Atom 1.0 + */ +define('SIMPLEPIE_TYPE_ATOM_10', 512); + +/** + * All Atom + */ +define('SIMPLEPIE_TYPE_ATOM_ALL', 768); + +/** + * All feed types + */ +define('SIMPLEPIE_TYPE_ALL', 1023); + +/** + * No construct + */ +define('SIMPLEPIE_CONSTRUCT_NONE', 0); + +/** + * Text construct + */ +define('SIMPLEPIE_CONSTRUCT_TEXT', 1); + +/** + * HTML construct + */ +define('SIMPLEPIE_CONSTRUCT_HTML', 2); + +/** + * XHTML construct + */ +define('SIMPLEPIE_CONSTRUCT_XHTML', 4); + +/** + * base64-encoded construct + */ +define('SIMPLEPIE_CONSTRUCT_BASE64', 8); + +/** + * IRI construct + */ +define('SIMPLEPIE_CONSTRUCT_IRI', 16); + +/** + * A construct that might be HTML + */ +define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32); + +/** + * All constructs + */ +define('SIMPLEPIE_CONSTRUCT_ALL', 63); + +/** + * PCRE for HTML attributes + */ +define('SIMPLEPIE_PCRE_HTML_ATTRIBUTE', '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*'); + +/** + * PCRE for XML attributes + */ +define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*'); + +/** + * XML Namespace + */ +define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace'); + +/** + * Atom 1.0 Namespace + */ +define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom'); + +/** + * Atom 0.3 Namespace + */ +define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#'); + +/** + * RDF Namespace + */ +define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); + +/** + * RSS 0.90 Namespace + */ +define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/'); + +/** + * RSS 1.0 Namespace + */ +define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/'); + +/** + * RSS 1.0 Content Module Namespace + */ +define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/'); + +/** + * RSS 2.0 Namespace + * (Stupid, I know, but I'm certain it will confuse people less with support.) + */ +define('SIMPLEPIE_NAMESPACE_RSS_20', ''); + +/** + * DC 1.0 Namespace + */ +define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/'); + +/** + * DC 1.1 Namespace + */ +define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/'); + +/** + * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace + */ +define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#'); + +/** + * GeoRSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss'); + +/** + * Media RSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/'); + +/** + * Wrong Media RSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss'); + +/** + * iTunes RSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd'); + +/** + * XHTML Namespace + */ +define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml'); + +/** + * IANA Link Relations Registry + */ +define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/'); + +/** + * Whether we're running on PHP5 + */ +define('SIMPLEPIE_PHP5', version_compare(PHP_VERSION, '5.0.0', '>=')); + +/** + * No file source + */ +define('SIMPLEPIE_FILE_SOURCE_NONE', 0); + +/** + * Remote file source + */ +define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1); + +/** + * Local file source + */ +define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2); + +/** + * fsockopen() file source + */ +define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4); + +/** + * cURL file source + */ +define('SIMPLEPIE_FILE_SOURCE_CURL', 8); + +/** + * file_get_contents() file source + */ +define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16); + +/** + * SimplePie + * + * @package SimplePie + * @version "Razzleberry" + * @copyright 2004-2007 Ryan Parman, Geoffrey Sneddon + * @author Ryan Parman + * @author Geoffrey Sneddon + * @todo Option for type of fetching (cache, not modified header, fetch, etc.) + */ +class SimplePie +{ + /** + * @var array Raw data + * @access private + */ + var $data = array(); + + /** + * @var mixed Error string + * @access private + */ + var $error; + + /** + * @var object Instance of SimplePie_Sanitize (or other class) + * @see SimplePie::set_sanitize_class() + * @access private + */ + var $sanitize; + + /** + * @var string SimplePie Useragent + * @see SimplePie::set_useragent() + * @access private + */ + var $useragent = SIMPLEPIE_USERAGENT; + + /** + * @var string Feed URL + * @see SimplePie::set_feed_url() + * @access private + */ + var $feed_url; + + /** + * @var object Instance of SimplePie_File to use as a feed + * @see SimplePie::set_file() + * @access private + */ + var $file; + + /** + * @var string Raw feed data + * @see SimplePie::set_raw_data() + * @access private + */ + var $raw_data; + + /** + * @var int Timeout for fetching remote files + * @see SimplePie::set_timeout() + * @access private + */ + var $timeout = 10; + + /** + * @var bool Forces fsockopen() to be used for remote files instead + * of cURL, even if a new enough version is installed + * @see SimplePie::force_fsockopen() + * @access private + */ + var $force_fsockopen = false; + + /** + * @var bool Force the given data/URL to be treated as a feed no matter what + * it appears like + * @see SimplePie::force_feed() + * @access private + */ + var $force_feed = false; + + /** + * @var bool Enable/Disable XML dump + * @see SimplePie::enable_xml_dump() + * @access private + */ + var $xml_dump = false; + + /** + * @var bool Enable/Disable Caching + * @see SimplePie::enable_cache() + * @access private + */ + var $cache = true; + + /** + * @var int Cache duration (in seconds) + * @see SimplePie::set_cache_duration() + * @access private + */ + var $cache_duration = 3600; + + /** + * @var int Auto-discovery cache duration (in seconds) + * @see SimplePie::set_autodiscovery_cache_duration() + * @access private + */ + var $autodiscovery_cache_duration = 604800; // 7 Days. + + /** + * @var string Cache location (relative to executing script) + * @see SimplePie::set_cache_location() + * @access private + */ + var $cache_location = './cache'; + + /** + * @var string Function that creates the cache filename + * @see SimplePie::set_cache_name_function() + * @access private + */ + var $cache_name_function = 'md5'; + + /** + * @var bool Reorder feed by date descending + * @see SimplePie::enable_order_by_date() + * @access private + */ + var $order_by_date = true; + + /** + * @var mixed Force input encoding to be set to the follow value + * (false, or anything type-cast to false, disables this feature) + * @see SimplePie::set_input_encoding() + * @access private + */ + var $input_encoding = false; + + /** + * @var int Feed Autodiscovery Level + * @see SimplePie::set_autodiscovery_level() + * @access private + */ + var $autodiscovery = SIMPLEPIE_LOCATOR_ALL; + + /** + * @var string Class used for caching feeds + * @see SimplePie::set_cache_class() + * @access private + */ + var $cache_class = 'SimplePie_Cache'; + + /** + * @var string Class used for locating feeds + * @see SimplePie::set_locator_class() + * @access private + */ + var $locator_class = 'SimplePie_Locator'; + + /** + * @var string Class used for parsing feeds + * @see SimplePie::set_parser_class() + * @access private + */ + var $parser_class = 'SimplePie_Parser'; + + /** + * @var string Class used for fetching feeds + * @see SimplePie::set_file_class() + * @access private + */ + var $file_class = 'SimplePie_File'; + + /** + * @var string Class used for items + * @see SimplePie::set_item_class() + * @access private + */ + var $item_class = 'SimplePie_Item'; + + /** + * @var string Class used for authors + * @see SimplePie::set_author_class() + * @access private + */ + var $author_class = 'SimplePie_Author'; + + /** + * @var string Class used for categories + * @see SimplePie::set_category_class() + * @access private + */ + var $category_class = 'SimplePie_Category'; + + /** + * @var string Class used for enclosures + * @see SimplePie::set_enclosures_class() + * @access private + */ + var $enclosure_class = 'SimplePie_Enclosure'; + + /** + * @var string Class used for Media RSS <media:text> captions + * @see SimplePie::set_caption_class() + * @access private + */ + var $caption_class = 'SimplePie_Caption'; + + /** + * @var string Class used for Media RSS <media:copyright> + * @see SimplePie::set_copyright_class() + * @access private + */ + var $copyright_class = 'SimplePie_Copyright'; + + /** + * @var string Class used for Media RSS <media:credit> + * @see SimplePie::set_credit_class() + * @access private + */ + var $credit_class = 'SimplePie_Credit'; + + /** + * @var string Class used for Media RSS <media:rating> + * @see SimplePie::set_rating_class() + * @access private + */ + var $rating_class = 'SimplePie_Rating'; + + /** + * @var string Class used for Media RSS <media:restriction> + * @see SimplePie::set_restriction_class() + * @access private + */ + var $restriction_class = 'SimplePie_Restriction'; + + /** + * @var string Class used for content-type sniffing + * @see SimplePie::set_content_type_sniffer_class() + * @access private + */ + var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer'; + + /** + * @var string Class used for item sources. + * @see SimplePie::set_source_class() + * @access private + */ + var $source_class = 'SimplePie_Source'; + + /** + * @var mixed Set javascript query string parameter (false, or + * anything type-cast to false, disables this feature) + * @see SimplePie::set_javascript() + * @access private + */ + var $javascript = 'js'; + + /** + * @var int Maximum number of feeds to check with autodiscovery + * @see SimplePie::set_max_checked_feeds() + * @access private + */ + var $max_checked_feeds = 10; + + /** + * @var string Web-accessible path to the handler_favicon.php file. + * @see SimplePie::set_favicon_handler() + * @access private + */ + var $favicon_handler = ''; + + /** + * @var string Web-accessible path to the handler_image.php file. + * @see SimplePie::set_image_handler() + * @access private + */ + var $image_handler = ''; + + /** + * @var array Stores the URLs when multiple feeds are being initialized. + * @see SimplePie::set_feed_url() + * @access private + */ + var $multifeed_url = array(); + + /** + * @var array Stores SimplePie objects when multiple feeds initialized. + * @access private + */ + var $multifeed_objects = array(); + + /** + * @var array Stores the get_object_vars() array for use with multifeeds. + * @see SimplePie::set_feed_url() + * @access private + */ + var $config_settings = null; + + /** + * @var integer Stores the number of items to return per-feed with multifeeds. + * @see SimplePie::set_item_limit() + * @access private + */ + var $item_limit = 0; + + /** + * @var array Stores the default attributes to be stripped by strip_attributes(). + * @see SimplePie::strip_attributes() + * @access private + */ + var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); + + /** + * @var array Stores the default tags to be stripped by strip_htmltags(). + * @see SimplePie::strip_htmltags() + * @access private + */ + var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); + + /** + * The SimplePie class contains feed level data and options + * + * There are two ways that you can create a new SimplePie object. The first + * is by passing a feed URL as a parameter to the SimplePie constructor + * (as well as optionally setting the cache location and cache expiry). This + * will initialise the whole feed with all of the default settings, and you + * can begin accessing methods and properties immediately. + * + * The second way is to create the SimplePie object with no parameters + * at all. This will enable you to set configuration options. After setting + * them, you must initialise the feed using $feed->init(). At that point the + * object's methods and properties will be available to you. This format is + * what is used throughout this documentation. + * + * @access public + * @since 1.0 Preview Release + * @param string $feed_url This is the URL you want to parse. + * @param string $cache_location This is where you want the cache to be stored. + * @param int $cache_duration This is the number of seconds that you want to store the cache file for. + */ + function SimplePie($feed_url = null, $cache_location = null, $cache_duration = null) + { + // Other objects, instances created here so we can set options on them + $this->sanitize =& new SimplePie_Sanitize; + + // Set options if they're passed to the constructor + if ($cache_location !== null) + { + $this->set_cache_location($cache_location); + } + + if ($cache_duration !== null) + { + $this->set_cache_duration($cache_duration); + } + + // Only init the script if we're passed a feed URL + if ($feed_url !== null) + { + $this->set_feed_url($feed_url); + $this->init(); + } + } + + /** + * Used for converting object to a string + */ + function __toString() + { + return md5(serialize($this->data)); + } + + /** + * Remove items that link back to this before destroying this object + */ + function __destruct() + { + if (!empty($this->data['items'])) + { + foreach ($this->data['items'] as $item) + { + $item->__destruct(); + } + unset($this->data['items']); + } + if (!empty($this->data['ordered_items'])) + { + foreach ($this->data['ordered_items'] as $item) + { + $item->__destruct(); + } + unset($this->data['ordered_items']); + } + } + + /** + * Force the given data/URL to be treated as a feed no matter what it + * appears like + * + * @access public + * @since 1.1 + * @param bool $enable Force the given data/URL to be treated as a feed + */ + function force_feed($enable = false) + { + $this->force_feed = (bool) $enable; + } + + /** + * This is the URL of the feed you want to parse. + * + * This allows you to enter the URL of the feed you want to parse, or the + * website you want to try to use auto-discovery on. This takes priority + * over any set raw data. + * + * You can set multiple feeds to mash together by passing an array instead + * of a string for the $url. Remember that with each additional feed comes + * additional processing and resources. + * + * @access public + * @since 1.0 Preview Release + * @param mixed $url This is the URL (or array of URLs) that you want to parse. + * @see SimplePie::set_raw_data() + */ + function set_feed_url($url) + { + if (is_array($url)) + { + $this->multifeed_url = array(); + foreach ($url as $value) + { + $this->multifeed_url[] = SimplePie_Misc::fix_protocol($value, 1); + } + } + else + { + $this->feed_url = SimplePie_Misc::fix_protocol($url, 1); + } + } + + /** + * Provides an instance of SimplePie_File to use as a feed + * + * @access public + * @param object &$file Instance of SimplePie_File (or subclass) + * @return bool True on success, false on failure + */ + function set_file(&$file) + { + if (is_a($file, 'SimplePie_File')) + { + $this->feed_url = $file->url; + $this->file =& $file; + return true; + } + return false; + } + + /** + * Allows you to use a string of RSS/Atom data instead of a remote feed. + * + * If you have a feed available as a string in PHP, you can tell SimplePie + * to parse that data string instead of a remote feed. Any set feed URL + * takes precedence. + * + * @access public + * @since 1.0 Beta 3 + * @param string $data RSS or Atom data as a string. + * @see SimplePie::set_feed_url() + */ + function set_raw_data($data) + { + $this->raw_data = $data; + } + + /** + * Allows you to override the default timeout for fetching remote feeds. + * + * This allows you to change the maximum time the feed's server to respond + * and send the feed back. + * + * @access public + * @since 1.0 Beta 3 + * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed. + */ + function set_timeout($timeout = 10) + { + $this->timeout = (int) $timeout; + } + + /** + * Forces SimplePie to use fsockopen() instead of the preferred cURL + * functions. + * + * @access public + * @since 1.0 Beta 3 + * @param bool $enable Force fsockopen() to be used + */ + function force_fsockopen($enable = false) + { + $this->force_fsockopen = (bool) $enable; + } + + /** + * Outputs the raw XML content of the feed, after it has gone through + * SimplePie's filters. + * + * Used only for debugging, this function will output the XML content as + * text/xml. When SimplePie reads in a feed, it does a bit of cleaning up + * before trying to parse it. Many parts of the feed are re-written in + * memory, and in the end, you have a parsable feed. XML dump shows you the + * actual XML that SimplePie tries to parse, which may or may not be very + * different from the original feed. + * + * @access public + * @since 1.0 Preview Release + * @param bool $enable Enable XML dump + */ + function enable_xml_dump($enable = false) + { + $this->xml_dump = (bool) $enable; + } + + /** + * Enables/disables caching in SimplePie. + * + * This option allows you to disable caching all-together in SimplePie. + * However, disabling the cache can lead to longer load times. + * + * @access public + * @since 1.0 Preview Release + * @param bool $enable Enable caching + */ + function enable_cache($enable = true) + { + $this->cache = (bool) $enable; + } + + /** + * Set the length of time (in seconds) that the contents of a feed + * will be cached. + * + * @access public + * @param int $seconds The feed content cache duration. + */ + function set_cache_duration($seconds = 3600) + { + $this->cache_duration = (int) $seconds; + } + + /** + * Set the length of time (in seconds) that the autodiscovered feed + * URL will be cached. + * + * @access public + * @param int $seconds The autodiscovered feed URL cache duration. + */ + function set_autodiscovery_cache_duration($seconds = 604800) + { + $this->autodiscovery_cache_duration = (int) $seconds; + } + + /** + * Set the file system location where the cached files should be stored. + * + * @access public + * @param string $location The file system location. + */ + function set_cache_location($location = './cache') + { + $this->cache_location = (string) $location; + } + + /** + * Determines whether feed items should be sorted into reverse chronological order. + * + * @access public + * @param bool $enable Sort as reverse chronological order. + */ + function enable_order_by_date($enable = true) + { + $this->order_by_date = (bool) $enable; + } + + /** + * Allows you to override the character encoding reported by the feed. + * + * @access public + * @param string $encoding Character encoding. + */ + function set_input_encoding($encoding = false) + { + if ($encoding) + { + $this->input_encoding = (string) $encoding; + } + else + { + $this->input_encoding = false; + } + } + + /** + * Set how much feed autodiscovery to do + * + * @access public + * @see SIMPLEPIE_LOCATOR_NONE + * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY + * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION + * @see SIMPLEPIE_LOCATOR_LOCAL_BODY + * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION + * @see SIMPLEPIE_LOCATOR_REMOTE_BODY + * @see SIMPLEPIE_LOCATOR_ALL + * @param int $level Feed Autodiscovery Level (level can be a + * combination of the above constants, see bitwise OR operator) + */ + function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL) + { + $this->autodiscovery = (int) $level; + } + + /** + * Allows you to change which class SimplePie uses for caching. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_cache_class($class = 'SimplePie_Cache') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Cache')) + { + $this->cache_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for auto-discovery. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_locator_class($class = 'SimplePie_Locator') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Locator')) + { + $this->locator_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for XML parsing. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_parser_class($class = 'SimplePie_Parser') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Parser')) + { + $this->parser_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for remote file fetching. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_file_class($class = 'SimplePie_File') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_File')) + { + $this->file_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for data sanitization. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_sanitize_class($class = 'SimplePie_Sanitize') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Sanitize')) + { + $this->sanitize =& new $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for handling feed items. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_item_class($class = 'SimplePie_Item') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Item')) + { + $this->item_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for handling author data. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_author_class($class = 'SimplePie_Author') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Author')) + { + $this->author_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for handling category data. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_category_class($class = 'SimplePie_Category') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Category')) + { + $this->category_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for feed enclosures. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_enclosure_class($class = 'SimplePie_Enclosure') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Enclosure')) + { + $this->enclosure_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for <media:text> captions + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_caption_class($class = 'SimplePie_Caption') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Caption')) + { + $this->caption_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for <media:copyright> + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_copyright_class($class = 'SimplePie_Copyright') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Copyright')) + { + $this->copyright_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for <media:credit> + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_credit_class($class = 'SimplePie_Credit') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Credit')) + { + $this->credit_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for <media:rating> + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_rating_class($class = 'SimplePie_Rating') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Rating')) + { + $this->rating_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for <media:restriction> + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_restriction_class($class = 'SimplePie_Restriction') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Restriction')) + { + $this->restriction_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for content-type sniffing. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Content_Type_Sniffer')) + { + $this->content_type_sniffer_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses item sources. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_source_class($class = 'SimplePie_Source') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Source')) + { + $this->source_class = $class; + return true; + } + return false; + } + + /** + * Allows you to override the default user agent string. + * + * @access public + * @param string $ua New user agent string. + */ + function set_useragent($ua = SIMPLEPIE_USERAGENT) + { + $this->useragent = (string) $ua; + } + + /** + * Set callback function to create cache filename with + * + * @access public + * @param mixed $function Callback function + */ + function set_cache_name_function($function = 'md5') + { + if (is_callable($function)) + { + $this->cache_name_function = $function; + } + } + + /** + * Set javascript query string parameter + * + * @access public + * @param mixed $get Javascript query string parameter + */ + function set_javascript($get = 'js') + { + if ($get) + { + $this->javascript = (string) $get; + } + else + { + $this->javascript = false; + } + } + + /** + * Set options to make SP as fast as possible. Forgoes a + * substantial amount of data sanitization in favor of speed. + * + * @access public + * @param bool $set Whether to set them or not + */ + function set_stupidly_fast($set = false) + { + if ($set) + { + $this->enable_order_by_date(false); + $this->remove_div(false); + $this->strip_comments(false); + $this->strip_htmltags(false); + $this->strip_attributes(false); + $this->set_image_handler(false); + } + } + + /** + * Set maximum number of feeds to check with autodiscovery + * + * @access public + * @param int $max Maximum number of feeds to check + */ + function set_max_checked_feeds($max = 10) + { + $this->max_checked_feeds = (int) $max; + } + + function remove_div($enable = true) + { + $this->sanitize->remove_div($enable); + } + + function strip_htmltags($tags = '', $encode = null) + { + if ($tags === '') + { + $tags = $this->strip_htmltags; + } + $this->sanitize->strip_htmltags($tags); + if ($encode !== null) + { + $this->sanitize->encode_instead_of_strip($tags); + } + } + + function encode_instead_of_strip($enable = true) + { + $this->sanitize->encode_instead_of_strip($enable); + } + + function strip_attributes($attribs = '') + { + if ($attribs === '') + { + $attribs = $this->strip_attributes; + } + $this->sanitize->strip_attributes($attribs); + } + + function set_output_encoding($encoding = 'UTF-8') + { + $this->sanitize->set_output_encoding($encoding); + } + + function strip_comments($strip = false) + { + $this->sanitize->strip_comments($strip); + } + + /** + * Set element/attribute key/value pairs of HTML attributes + * containing URLs that need to be resolved relative to the feed + * + * @access public + * @since 1.0 + * @param array $element_attribute Element/attribute key/value pairs + */ + function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite')) + { + $this->sanitize->set_url_replacements($element_attribute); + } + + /** + * Set the handler to enable the display of cached favicons. + * + * @access public + * @param str $page Web-accessible path to the handler_favicon.php file. + * @param str $qs The query string that the value should be passed to. + */ + function set_favicon_handler($page = false, $qs = 'i') + { + if ($page != false) + { + $this->favicon_handler = $page . '?' . $qs . '='; + } + else + { + $this->favicon_handler = ''; + } + } + + /** + * Set the handler to enable the display of cached images. + * + * @access public + * @param str $page Web-accessible path to the handler_image.php file. + * @param str $qs The query string that the value should be passed to. + */ + function set_image_handler($page = false, $qs = 'i') + { + if ($page != false) + { + $this->sanitize->set_image_handler($page . '?' . $qs . '='); + } + else + { + $this->image_handler = ''; + } + } + + /** + * Set the limit for items returned per-feed with multifeeds. + * + * @access public + * @param integer $limit The maximum number of items to return. + */ + function set_item_limit($limit = 0) + { + $this->item_limit = (int) $limit; + } + + function init() + { + if ((function_exists('version_compare') && version_compare(PHP_VERSION, '4.3.0', '<')) || !extension_loaded('xml') || !extension_loaded('pcre')) + { + return false; + } + if (isset($_GET[$this->javascript])) + { + if (function_exists('ob_gzhandler')) + { + ob_start('ob_gzhandler'); + } + header('Content-type: text/javascript; charset: UTF-8'); + header('Cache-Control: must-revalidate'); + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days + ?> +function embed_odeo(link) { + document.writeln('<embed src="http://odeo.com/flash/audio_player_fullsize.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="440" height="80" wmode="transparent" allowScriptAccess="any" flashvars="valid_sample_rate=true&external_url='+link+'"></embed>'); +} + +function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) { + if (placeholder != '') { + document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>'); + } + else { + document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>'); + } +} + +function embed_flash(bgcolor, width, height, link, loop, type) { + document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>'); +} + +function embed_flv(width, height, link, placeholder, loop, player) { + document.writeln('<embed src="'+player+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="'+width+'" height="'+height+'" wmode="transparent" flashvars="file='+link+'&autostart=false&repeat='+loop+'&showdigits=true&showfsbutton=false"></embed>'); +} + +function embed_wmedia(width, height, link) { + document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>'); +} + <?php + exit; + } + + // Pass whatever was set with config options over to the sanitizer. + $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->cache_class); + $this->sanitize->pass_file_data($this->file_class, $this->timeout, $this->useragent, $this->force_fsockopen); + + if ($this->feed_url !== null || $this->raw_data !== null) + { + $this->data = array(); + $this->multifeed_objects = array(); + $cache = false; + + if ($this->feed_url !== null) + { + $parsed_feed_url = SimplePie_Misc::parse_url($this->feed_url); + // Decide whether to enable caching + if ($this->cache && $parsed_feed_url['scheme'] !== '') + { + $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'); + } + // If it's enabled and we don't want an XML dump, use the cache + if ($cache && !$this->xml_dump) + { + // Load the Cache + $this->data = $cache->load(); + if (!empty($this->data)) + { + // If the cache is for an outdated build of SimplePie + if (!isset($this->data['build']) || $this->data['build'] != SIMPLEPIE_BUILD) + { + $cache->unlink(); + $this->data = array(); + } + // If we've hit a collision just rerun it with caching disabled + elseif (isset($this->data['url']) && $this->data['url'] != $this->feed_url) + { + $cache = false; + $this->data = array(); + } + // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. + elseif (isset($this->data['feed_url'])) + { + // If the autodiscovery cache is still valid use it. + if ($cache->mtime() + $this->autodiscovery_cache_duration > time()) + { + // Do not need to do feed autodiscovery yet. + if ($this->data['feed_url'] == $this->data['url']) + { + $cache->unlink(); + $this->data = array(); + } + else + { + $this->set_feed_url($this->data['feed_url']); + return $this->init(); + } + } + } + // Check if the cache has been updated + elseif ($cache->mtime() + $this->cache_duration < time()) + { + // If we have last-modified and/or etag set + if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) + { + $headers = array(); + if (isset($this->data['headers']['last-modified'])) + { + $headers['if-modified-since'] = $this->data['headers']['last-modified']; + } + if (isset($this->data['headers']['etag'])) + { + $headers['if-none-match'] = '"' . $this->data['headers']['etag'] . '"'; + } + $file =& new $this->file_class($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen); + if ($file->success) + { + if ($file->status_code == 304) + { + $cache->touch(); + return true; + } + else + { + $headers = $file->headers; + } + } + else + { + unset($file); + } + } + } + // If the cache is still valid, just return true + else + { + return true; + } + } + // If the cache is empty, delete it + else + { + $cache->unlink(); + $this->data = array(); + } + } + // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. + if (!isset($file)) + { + if (is_a($this->file, 'SimplePie_File') && $this->file->url == $this->feed_url) + { + $file =& $this->file; + } + else + { + $file =& new $this->file_class($this->feed_url, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen); + } + } + // If the file connection has an error, set SimplePie::error to that and quit + if (!$file->success) + { + $this->error = $file->error; + if (!empty($this->data)) + { + return true; + } + else + { + return false; + } + } + + if (!$this->force_feed) + { + // Check if the supplied URL is a feed, if it isn't, look for it. + $locate =& new $this->locator_class($file, $this->timeout, $this->useragent, $this->file_class, $this->max_checked_feeds, $this->content_type_sniffer_class); + if (!$locate->is_feed($file)) + { + // We need to unset this so that if SimplePie::set_file() has been called that object is untouched + unset($file); + if ($file = $locate->find($this->autodiscovery)) + { + if ($cache) + { + $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); + if (!$cache->save($this)) + { + trigger_error("$cache->name is not writeable", E_USER_WARNING); + } + $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'); + } + $this->feed_url = $file->url; + } + else + { + $this->error = "A feed could not be found at $this->feed_url"; + SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); + return false; + } + } + $locate = null; + } + + $headers = $file->headers; + $data = $file->body; + $sniffer = new $this->content_type_sniffer_class($file); + $sniffed = $sniffer->get_type(); + } + else + { + $data = $this->raw_data; + } + + // Set up array of possible encodings + $encodings = array(); + + // First check to see if input has been overridden. + if ($this->input_encoding !== false) + { + $encodings[] = $this->input_encoding; + } + + $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); + $text_types = array('text/xml', 'text/xml-external-parsed-entity'); + + // RFC 3023 (only applies to sniffed content) + if (isset($sniffed)) + { + if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') + { + if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) + { + $encodings[] = strtoupper($charset[1]); + } + $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data)); + $encodings[] = 'UTF-8'; + } + elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') + { + if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) + { + $encodings[] = $charset[1]; + } + $encodings[] = 'US-ASCII'; + } + // Text MIME-type default + elseif (substr($sniffed, 0, 5) === 'text/') + { + $encodings[] = 'US-ASCII'; + } + } + + // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 + $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data)); + $encodings[] = 'UTF-8'; + $encodings[] = 'ISO-8859-1'; + + // There's no point in trying an encoding twice + $encodings = array_unique($encodings); + + // If we want the XML, just output that with the most likely encoding and quit + if ($this->xml_dump) + { + header('Content-type: text/xml; charset=' . $encodings[0]); + echo $data; + exit; + } + + // Loop through each possible encoding, till we return something, or run out of possibilities + foreach ($encodings as $encoding) + { + // Change the encoding to UTF-8 (as we always use UTF-8 internally) + if ($utf8_data = SimplePie_Misc::change_encoding($data, $encoding, 'UTF-8')) + { + // Create new parser + $parser =& new $this->parser_class(); + + // If it's parsed fine + if ($parser->parse($utf8_data, 'UTF-8')) + { + $this->data = $parser->get_data(); + if ($this->get_type() & ~SIMPLEPIE_TYPE_NONE) + { + if (isset($headers)) + { + $this->data['headers'] = $headers; + } + $this->data['build'] = SIMPLEPIE_BUILD; + + // Cache the file if caching is enabled + if ($cache && !$cache->save($this)) + { + trigger_error("$cache->name is not writeable", E_USER_WARNING); + } + return true; + } + else + { + $this->error = "A feed could not be found at $this->feed_url"; + SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); + return false; + } + } + } + } + // We have an error, just set SimplePie::error to it and quit + $this->error = sprintf('XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); + SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); + return false; + } + elseif (!empty($this->multifeed_url)) + { + $i = 0; + $success = 0; + $this->multifeed_objects = array(); + foreach ($this->multifeed_url as $url) + { + if (SIMPLEPIE_PHP5) + { + // This keyword needs to defy coding standards for PHP4 compatibility + $this->multifeed_objects[$i] = clone($this); + } + else + { + $this->multifeed_objects[$i] = $this; + } + $this->multifeed_objects[$i]->set_feed_url($url); + $success |= $this->multifeed_objects[$i]->init(); + $i++; + } + return (bool) $success; + } + else + { + return false; + } + } + + /** + * Return the error message for the occurred error + * + * @access public + * @return string Error message + */ + function error() + { + return $this->error; + } + + function get_encoding() + { + return $this->sanitize->output_encoding; + } + + function handle_content_type($mime = 'text/html') + { + if (!headers_sent()) + { + $header = "Content-type: $mime;"; + if ($this->get_encoding()) + { + $header .= ' charset=' . $this->get_encoding(); + } + else + { + $header .= ' charset=UTF-8'; + } + header($header); + } + } + + function get_type() + { + if (!isset($this->data['type'])) + { + $this->data['type'] = SIMPLEPIE_TYPE_ALL; + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10; + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03; + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'])) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10; + } + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090; + } + } + elseif (isset($this->data['child']['']['rss'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL; + if (isset($this->data['child']['']['rss'][0]['attribs']['']['version'])) + { + switch (trim($this->data['child']['']['rss'][0]['attribs']['']['version'])) + { + case '0.91': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091; + if (isset($this->data['child']['']['rss'][0]['child']['']['skiphours']['hour'][0]['data'])) + { + switch (trim($this->data['child']['']['rss'][0]['child']['']['skiphours']['hour'][0]['data'])) + { + case '0': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE; + break; + + case '24': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND; + break; + } + } + break; + + case '0.92': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092; + break; + + case '0.93': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093; + break; + + case '0.94': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094; + break; + + case '2.0': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20; + break; + } + } + } + else + { + $this->data['type'] = SIMPLEPIE_TYPE_NONE; + } + } + return $this->data['type']; + } + + /** + * Returns the URL for the favicon of the feed's website. + * + * @todo Cache atom:icon + * @access public + * @since 1.0 + */ + function get_favicon() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif (($url = $this->get_link()) !== null && preg_match('/^http(s)?:\/\//i', $url)) + { + $favicon = SimplePie_Misc::absolutize_url('/favicon.ico', $url); + + if ($this->cache && $this->favicon_handler) + { + $favicon_filename = call_user_func($this->cache_name_function, $favicon); + $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $favicon_filename, 'spi'); + + if ($cache->load()) + { + return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + $file =& new $this->file_class($favicon, $this->timeout / 10, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen); + + if ($file->success && ($file->status_code == 200 || ($file->status_code > 206 && $file->status_code < 300)) && strlen($file->body) > 0) + { + $sniffer = new $this->content_type_sniffer_class($file); + if (substr($sniffer->get_type(), 0, 6) === 'image/') + { + if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) + { + return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + trigger_error("$cache->name is not writeable", E_USER_WARNING); + return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI); + } + } + } + } + } + else + { + return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI); + } + } + return false; + } + + /** + * @todo If we have a perm redirect we should return the new URL + * @todo When we make the above change, let's support <itunes:new-feed-url> as well + * @todo Also, |atom:link|@rel=self + */ + function subscribe_url() + { + if ($this->feed_url !== null) + { + return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + function subscribe_feed() + { + if ($this->feed_url !== null) + { + return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + function subscribe_outlook() + { + if ($this->feed_url !== null) + { + return 'outlook' . $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + function subscribe_podcast() + { + if ($this->feed_url !== null) + { + return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 3), SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + function subscribe_itunes() + { + if ($this->feed_url !== null) + { + return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 4), SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + /** + * Creates the subscribe_* methods' return data + * + * @access private + * @param string $feed_url String to prefix to the feed URL + * @param string $site_url String to prefix to the site URL (and + * suffix to the feed URL) + * @return mixed URL if feed exists, false otherwise + */ + function subscribe_service($feed_url, $site_url = null) + { + if ($this->subscribe_url()) + { + $return = $this->sanitize($feed_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->feed_url); + if ($site_url !== null && $this->get_link() !== null) + { + $return .= $this->sanitize($site_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_link()); + } + return $return; + } + else + { + return null; + } + } + + function subscribe_aol() + { + return $this->subscribe_service('http://feeds.my.aol.com/add.jsp?url='); + } + + function subscribe_bloglines() + { + return urldecode($this->subscribe_service('http://www.bloglines.com/sub/')); + } + + function subscribe_eskobo() + { + return $this->subscribe_service('http://www.eskobo.com/?AddToMyPage='); + } + + function subscribe_feedfeeds() + { + return $this->subscribe_service('http://www.feedfeeds.com/add?feed='); + } + + function subscribe_feedster() + { + return $this->subscribe_service('http://www.feedster.com/myfeedster.php?action=addrss&confirm=no&rssurl='); + } + + function subscribe_google() + { + return $this->subscribe_service('http://fusion.google.com/add?feedurl='); + } + + function subscribe_gritwire() + { + return $this->subscribe_service('http://my.gritwire.com/feeds/addExternalFeed.aspx?FeedUrl='); + } + + function subscribe_msn() + { + return $this->subscribe_service('http://my.msn.com/addtomymsn.armx?id=rss&ut=', '&ru='); + } + + function subscribe_netvibes() + { + return $this->subscribe_service('http://www.netvibes.com/subscribe.php?url='); + } + + function subscribe_newsburst() + { + return $this->subscribe_service('http://www.newsburst.com/Source/?add='); + } + + function subscribe_newsgator() + { + return $this->subscribe_service('http://www.newsgator.com/ngs/subscriber/subext.aspx?url='); + } + + function subscribe_odeo() + { + return $this->subscribe_service('http://www.odeo.com/listen/subscribe?feed='); + } + + function subscribe_podnova() + { + return $this->subscribe_service('http://www.podnova.com/index_your_podcasts.srf?action=add&url='); + } + + function subscribe_rojo() + { + return $this->subscribe_service('http://www.rojo.com/add-subscription?resource='); + } + + function subscribe_yahoo() + { + return $this->subscribe_service('http://add.my.yahoo.com/rss?url='); + } + + function get_feed_tags($namespace, $tag) + { + $type = $this->get_type(); + if ($type & SIMPLEPIE_TYPE_ATOM_10) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag])) + { + return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]; + } + } + if ($type & SIMPLEPIE_TYPE_ATOM_03) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag])) + { + return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]; + } + } + if ($type & SIMPLEPIE_TYPE_RSS_RDF) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag])) + { + return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]; + } + } + if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) + { + if (isset($this->data['child']['']['rss'][0]['child'][$namespace][$tag])) + { + return $this->data['child']['']['rss'][0]['child'][$namespace][$tag]; + } + } + return null; + } + + function get_channel_tags($namespace, $tag) + { + $type = $this->get_type(); + if ($type & SIMPLEPIE_TYPE_ATOM_ALL) + { + if ($return = $this->get_feed_tags($namespace, $tag)) + { + return $return; + } + } + if ($type & SIMPLEPIE_TYPE_RSS_10) + { + if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel')) + { + if (isset($channel[0]['child'][$namespace][$tag])) + { + return $channel[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_090) + { + if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel')) + { + if (isset($channel[0]['child'][$namespace][$tag])) + { + return $channel[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) + { + if ($channel = $this->get_feed_tags('', 'channel')) + { + if (isset($channel[0]['child'][$namespace][$tag])) + { + return $channel[0]['child'][$namespace][$tag]; + } + } + } + return null; + } + + function get_image_tags($namespace, $tag) + { + $type = $this->get_type(); + if ($type & SIMPLEPIE_TYPE_RSS_10) + { + if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image')) + { + if (isset($image[0]['child'][$namespace][$tag])) + { + return $image[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_090) + { + if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image')) + { + if (isset($image[0]['child'][$namespace][$tag])) + { + return $image[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) + { + if ($image = $this->get_channel_tags('', 'image')) + { + if (isset($image[0]['child'][$namespace][$tag])) + { + return $image[0]['child'][$namespace][$tag]; + } + } + } + return null; + } + + function get_base($element = array()) + { + if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base'])) + { + return $element['xml_base']; + } + elseif ($this->get_link() !== null) + { + return $this->get_link(); + } + else + { + return $this->subscribe_url(); + } + } + + function sanitize($data, $type, $base = '') + { + return $this->sanitize->sanitize($data, $type, $base); + } + + function get_title() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags('', 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + function get_categories() + { + $categories = array(); + + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) + { + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->category_class($term, $scheme, $label); + } + foreach ((array) $this->get_channel_tags('', 'category') as $category) + { + $categories[] =& new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] =& new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + { + $categories[] =& new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($categories)) + { + return SimplePie_Misc::array_unique($categories); + } + else + { + return null; + } + } + + function get_author($key = 0) + { + $authors = $this->get_authors(); + if (isset($authors[$key])) + { + return $authors[$key]; + } + else + { + return null; + } + } + + function get_authors() + { + $authors = array(); + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) + { + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $authors[] =& new $this->author_class($name, $uri, $email); + } + } + if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) + { + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] =& new $this->author_class($name, $url, $email); + } + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + { + $authors[] =& new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] =& new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] =& new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($authors)) + { + return SimplePie_Misc::array_unique($authors); + } + else + { + return null; + } + } + + function get_contributor($key = 0) + { + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) + { + return $contributors[$key]; + } + else + { + return null; + } + } + + function get_contributors() + { + $contributors = array(); + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) + { + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] =& new $this->author_class($name, $uri, $email); + } + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + { + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] =& new $this->author_class($name, $url, $email); + } + } + + if (!empty($contributors)) + { + return SimplePie_Misc::array_unique($contributors); + } + else + { + return null; + } + } + + function get_link($key = 0, $rel = 'alternate') + { + $links = $this->get_links($rel); + if (isset($links[$key])) + { + return $links[$key]; + } + else + { + return null; + } + } + + /** + * Added for parity between the parent-level and the item/entry-level. + */ + function get_permalink() + { + return $this->get_link(0); + } + + function get_links($rel = 'alternate') + { + if (!isset($this->data['links'])) + { + $this->data['links'] = array(); + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } + } + } + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + + } + } + } + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_channel_tags('', 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + + $keys = array_keys($this->data['links']); + foreach ($keys as $key) + { + if (SimplePie_Misc::is_isegment_nz_nc($key)) + { + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } + } + elseif (substr($key, 0, 41) == SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + { + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); + } + } + + if (isset($this->data['links'][$rel])) + { + return $this->data['links'][$rel]; + } + else + { + return null; + } + } + + function get_description() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags('', 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + else + { + return null; + } + } + + function get_copyright() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags('', 'copyright')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_language() + { + if ($return = $this->get_channel_tags('', 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'])) + { + return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'])) + { + return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'])) + { + return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['headers']['content-language'])) + { + return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_latitude() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[1]; + } + else + { + return null; + } + } + + function get_longitude() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + { + return (float) $return[0]['data']; + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[2]; + } + else + { + return null; + } + } + + function get_image_title() + { + if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags('', 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_image_url() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) + { + return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags('', 'url')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + else + { + return null; + } + } + + function get_image_link() + { + if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags('', 'link')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + else + { + return null; + } + } + + function get_image_width() + { + if ($return = $this->get_image_tags('', 'width')) + { + return round($return[0]['data']); + } + elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags('', 'url')) + { + return 88.0; + } + else + { + return null; + } + } + + function get_image_height() + { + if ($return = $this->get_image_tags('', 'height')) + { + return round($return[0]['data']); + } + elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags('', 'url')) + { + return 31.0; + } + else + { + return null; + } + } + + function get_item_quantity($max = 0) + { + $qty = count($this->get_items()); + if ($max == 0) + { + return $qty; + } + else + { + return ($qty > $max) ? $max : $qty; + } + } + + function get_item($key = 0) + { + $items = $this->get_items(); + if (isset($items[$key])) + { + return $items[$key]; + } + else + { + return null; + } + } + + function get_items($start = 0, $end = 0) + { + if (!empty($this->multifeed_objects)) + { + return SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit); + } + elseif (!isset($this->data['items'])) + { + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] =& new $this->item_class($this, $items[$key]); + } + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] =& new $this->item_class($this, $items[$key]); + } + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] =& new $this->item_class($this, $items[$key]); + } + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] =& new $this->item_class($this, $items[$key]); + } + } + if ($items = $this->get_channel_tags('', 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] =& new $this->item_class($this, $items[$key]); + } + } + } + + if (!empty($this->data['items'])) + { + // If we want to order it by date, check if all items have a date, and then sort it + if ($this->order_by_date) + { + if (!isset($this->data['ordered_items'])) + { + $do_sort = true; + foreach ($this->data['items'] as $item) + { + if (!$item->get_date('U')) + { + $do_sort = false; + break; + } + } + $item = null; + $this->data['ordered_items'] = $this->data['items']; + if ($do_sort) + { + usort($this->data['ordered_items'], array(&$this, 'sort_items')); + } + } + $items = $this->data['ordered_items']; + } + else + { + $items = $this->data['items']; + } + + // Slice the data as desired + if ($end == 0) + { + return array_slice($items, $start); + } + else + { + return array_slice($items, $start, $end); + } + } + else + { + return array(); + } + } + + function sort_items($a, $b) + { + return $a->get_date('U') <= $b->get_date('U'); + } + + function merge_items($urls, $start = 0, $end = 0, $limit = 0) + { + if (is_array($urls) && sizeof($urls) > 0) + { + $items = array(); + foreach ($urls as $arg) + { + if (is_a($arg, 'SimplePie')) + { + $items = array_merge($items, $arg->get_items(0, $limit)); + } + else + { + trigger_error('Arguments must be SimplePie objects', E_USER_WARNING); + } + } + + $do_sort = true; + foreach ($items as $item) + { + if (!$item->get_date('U')) + { + $do_sort = false; + break; + } + } + $item = null; + if ($do_sort) + { + usort($items, array('SimplePie', 'sort_items')); + } + + if ($end == 0) + { + return array_slice($items, $start); + } + else + { + return array_slice($items, $start, $end); + } + } + else + { + trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING); + return array(); + } + } +} + +class SimplePie_Item +{ + var $feed; + var $data = array(); + + function SimplePie_Item($feed, $data) + { + $this->feed = $feed; + $this->data = $data; + } + + function __toString() + { + return md5(serialize($this->data)); + } + + /** + * Remove items that link back to this before destroying this object + */ + function __destruct() + { + unset($this->feed); + } + + function get_item_tags($namespace, $tag) + { + if (isset($this->data['child'][$namespace][$tag])) + { + return $this->data['child'][$namespace][$tag]; + } + else + { + return null; + } + } + + function get_base($element = array()) + { + return $this->feed->get_base($element); + } + + function sanitize($data, $type, $base = '') + { + return $this->feed->sanitize($data, $type, $base); + } + + function get_feed() + { + return $this->feed; + } + + function get_id($hash = false) + { + if (!$hash) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags('', 'guid')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (($return = $this->get_permalink()) !== null) + { + return $return; + } + elseif (($return = $this->get_title()) !== null) + { + return $return; + } + } + if ($this->get_permalink() !== null || $this->get_title() !== null) + { + return md5($this->get_permalink() . $this->get_title()); + } + else + { + return md5(serialize($this->data)); + } + } + + function get_title() + { + if (!isset($this->data['title'])) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags('', 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $this->data['title'] = null; + } + } + return $this->data['title']; + } + + function get_description($description_only = false) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags('', 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (!$description_only) + { + return $this->get_content(true); + } + else + { + return null; + } + } + + function get_content($content_only = false) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_content_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif (!$content_only) + { + return $this->get_description(true); + } + else + { + return null; + } + } + + function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + function get_categories() + { + $categories = array(); + + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) + { + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->feed->category_class($term, $scheme, $label); + } + foreach ((array) $this->get_item_tags('', 'category') as $category) + { + $categories[] =& new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] =& new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + { + $categories[] =& new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($categories)) + { + return SimplePie_Misc::array_unique($categories); + } + else + { + return null; + } + } + + function get_author($key = 0) + { + $authors = $this->get_authors(); + if (isset($authors[$key])) + { + return $authors[$key]; + } + else + { + return null; + } + } + + function get_contributor($key = 0) + { + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) + { + return $contributors[$key]; + } + else + { + return null; + } + } + + function get_contributors() + { + $contributors = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) + { + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] =& new $this->feed->author_class($name, $uri, $email); + } + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + { + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] =& new $this->feed->author_class($name, $url, $email); + } + } + + if (!empty($contributors)) + { + return SimplePie_Misc::array_unique($contributors); + } + else + { + return null; + } + } + + /** + * @todo Atom inheritance (item author, source author, feed author) + */ + function get_authors() + { + $authors = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) + { + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $authors[] =& new $this->feed->author_class($name, $uri, $email); + } + } + if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) + { + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] =& new $this->feed->author_class($name, $url, $email); + } + } + if ($author = $this->get_item_tags('', 'author')) + { + $authors[] =& new $this->feed->author_class(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + { + $authors[] =& new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] =& new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] =& new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($authors)) + { + return SimplePie_Misc::array_unique($authors); + } + elseif (($source = $this->get_source()) && ($authors = $source->get_authors())) + { + return $authors; + } + elseif ($authors = $this->feed->get_authors()) + { + return $authors; + } + else + { + return null; + } + } + + function get_copyright() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_date($date_format = 'j F Y, g:i a') + { + if (!isset($this->data['date'])) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags('', 'pubDate')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + + if (!empty($this->data['date']['raw'])) + { + $parser = SimplePie_Parse_Date::get(); + $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']); + } + else + { + $this->data['date'] = null; + } + } + if ($this->data['date']) + { + $date_format = (string) $date_format; + switch ($date_format) + { + case '': + return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); + + case 'U': + return $this->data['date']['parsed']; + + default: + return date($date_format, $this->data['date']['parsed']); + } + } + else + { + return null; + } + } + + function get_local_date($date_format = '%c') + { + if (!$date_format) + { + return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (($date = $this->get_date('U')) !== null) + { + return strftime($date_format, $date); + } + else + { + return null; + } + } + + function get_permalink() + { + $link = $this->get_link(); + $enclosure = $this->get_enclosure(0); + if ($link !== null) + { + return $link; + } + elseif ($enclosure !== null) + { + return $enclosure->get_link(); + } + else + { + return null; + } + } + + function get_link($key = 0, $rel = 'alternate') + { + $links = $this->get_links($rel); + if ($links[$key] !== null) + { + return $links[$key]; + } + else + { + return null; + } + } + + function get_links($rel = 'alternate') + { + if (!isset($this->data['links'])) + { + $this->data['links'] = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + + } + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } + } + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags('', 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags('', 'guid')) + { + if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) == 'true') + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + } + + $keys = array_keys($this->data['links']); + foreach ($keys as $key) + { + if (SimplePie_Misc::is_isegment_nz_nc($key)) + { + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } + } + elseif (substr($key, 0, 41) == SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + { + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); + } + } + if (isset($this->data['links'][$rel])) + { + return $this->data['links'][$rel]; + } + else + { + return null; + } + } + + /** + * @todo Add ability to prefer one type of content over another (in a media group). + */ + function get_enclosure($key = 0, $prefer = null) + { + $enclosures = $this->get_enclosures(); + if (isset($enclosures[$key])) + { + return $enclosures[$key]; + } + else + { + return null; + } + } + + /** + * Grabs all available enclosures (podcasts, etc.) + * + * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS. + * + * At this point, we're pretty much assuming that all enclosures for an item are the same content. Anything else is too complicated to properly support. + * + * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4). + * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists). + */ + function get_enclosures() + { + if (!isset($this->data['enclosures'])) + { + $this->data['enclosures'] = array(); + + // Elements + $captions_parent = null; + $categories_parent = null; + $copyrights_parent = null; + $credits_parent = null; + $description_parent = null; + $duration_parent = null; + $hashes_parent = null; + $keywords_parent = null; + $player_parent = null; + $ratings_parent = null; + $restrictions_parent = null; + $thumbnails_parent = null; + $title_parent = null; + + // Let's do the channel and item-level ones first, and just re-use them if we need to. + $parent = $this->get_feed(); + + // CAPTIONS + if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) + { + foreach ($captions as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions_parent[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); + } + } + elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) + { + foreach ($captions as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions_parent[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); + } + } + if (is_array($captions_parent)) + { + $captions_parent = array_values(SimplePie_Misc::array_unique($captions_parent)); + } + + // CATEGORIES + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); + } + foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); + } + foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category) + { + $term = null; + $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; + $label = null; + if (isset($category['attribs']['']['text'])) + { + $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); + + if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'])) + { + foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory) + { + if (isset($subcategory['attribs']['']['text'])) + { + $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); + } + } + } + if (is_array($categories_parent)) + { + $categories_parent = array_values(SimplePie_Misc::array_unique($categories_parent)); + } + + // COPYRIGHT + if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) + { + $copyright_url = null; + $copyright_label = null; + if (isset($copyright[0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($copyright[0]['data'])) + { + $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights_parent =& new $this->feed->copyright_class($copyright_url, $copyright_label); + } + elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) + { + $copyright_url = null; + $copyright_label = null; + if (isset($copyright[0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($copyright[0]['data'])) + { + $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights_parent =& new $this->feed->copyright_class($copyright_url, $copyright_label); + } + + // CREDITS + if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) + { + foreach ($credits as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits_parent[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); + } + } + elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) + { + foreach ($credits as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits_parent[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); + } + } + if (is_array($credits_parent)) + { + $credits_parent = array_values(SimplePie_Misc::array_unique($credits_parent)); + } + + // DESCRIPTION + if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) + { + if (isset($description_parent[0]['data'])) + { + $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) + { + if (isset($description_parent[0]['data'])) + { + $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + + // DURATION + if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration')) + { + $seconds = null; + $minutes = null; + $hours = null; + if (isset($duration_parent[0]['data'])) + { + $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + if (sizeof($temp) > 0) + { + $seconds = (int)array_pop($temp); + } + if (sizeof($temp) > 0) + { + $minutes = (int)array_pop($temp); + $seconds += $minutes * 60; + } + if (sizeof($temp) > 0) + { + $hours = (int)array_pop($temp); + $seconds += $hours * 3600; + } + unset($temp); + $duration_parent = $seconds; + } + } + + // HASHES + if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) + { + foreach ($hashes_iterator as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes_parent[] = $algo.':'.$value; + } + } + elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) + { + foreach ($hashes_iterator as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes_parent[] = $algo.':'.$value; + } + } + if (is_array($hashes_parent)) + { + $hashes_parent = array_values(SimplePie_Misc::array_unique($hashes_parent)); + } + + // KEYWORDS + if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + if (is_array($keywords_parent)) + { + $keywords_parent = array_values(SimplePie_Misc::array_unique($keywords_parent)); + } + + // PLAYER + if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) + { + if (isset($player_parent[0]['attribs']['']['url'])) + { + $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) + { + if (isset($player_parent[0]['attribs']['']['url'])) + { + $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + + // RATINGS + if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) + { + foreach ($ratings as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + } + elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) + { + foreach ($ratings as $rating) + { + $rating_scheme = 'urn:itunes'; + $rating_value = null; + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + } + elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) + { + foreach ($ratings as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + } + elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) + { + foreach ($ratings as $rating) + { + $rating_scheme = 'urn:itunes'; + $rating_value = null; + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + } + if (is_array($ratings_parent)) + { + $ratings_parent = array_values(SimplePie_Misc::array_unique($ratings_parent)); + } + + // RESTRICTIONS + if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + } + elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = 'allow'; + $restriction_type = null; + $restriction_value = 'itunes'; + if (isset($restriction['data']) && strtolower($restriction['data']) == 'yes') + { + $restriction_relationship = 'deny'; + } + $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + } + elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + } + elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = 'allow'; + $restriction_type = null; + $restriction_value = 'itunes'; + if (isset($restriction['data']) && strtolower($restriction['data']) == 'yes') + { + $restriction_relationship = 'deny'; + } + $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + } + if (is_array($restrictions_parent)) + { + $restrictions_parent = array_values(SimplePie_Misc::array_unique($restrictions_parent)); + } + + // THUMBNAILS + if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) + { + foreach ($thumbnails as $thumbnail) + { + if (isset($thumbnail['attribs']['']['url'])) + { + $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + } + elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) + { + foreach ($thumbnails as $thumbnail) + { + if (isset($thumbnail['attribs']['']['url'])) + { + $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + } + + // TITLES + if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) + { + if (isset($title_parent[0]['data'])) + { + $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) + { + if (isset($title_parent[0]['data'])) + { + $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + + // Clear the memory + unset($parent); + + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; + + // If we have media:group tags, loop through them. + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group) + { + // If we have media:content tags, loop through them. + foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) + { + if (isset($content['attribs']['']['url'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; + + // Start checking the attributes of media:content + if (isset($content['attribs']['']['bitrate'])) + { + $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['channels'])) + { + $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['duration'])) + { + $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $duration = $duration_parent; + } + if (isset($content['attribs']['']['expression'])) + { + $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['framerate'])) + { + $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['height'])) + { + $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['lang'])) + { + $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['fileSize'])) + { + $length = ceil($content['attribs']['']['fileSize']); + } + if (isset($content['attribs']['']['medium'])) + { + $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['samplingrate'])) + { + $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['type'])) + { + $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['width'])) + { + $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + + // Checking the other optional media: elements. Priority: media:content, media:group, item, channel + + // CAPTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); + } + if (is_array($captions)) + { + $captions = array_values(SimplePie_Misc::array_unique($captions)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); + } + if (is_array($captions)) + { + $captions = array_values(SimplePie_Misc::array_unique($captions)); + } + } + else + { + $captions = $captions_parent; + } + + // CATEGORIES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->feed->category_class($term, $scheme, $label); + } + } + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->feed->category_class($term, $scheme, $label); + } + } + if (is_array($categories) && is_array($categories_parent)) + { + $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent))); + } + elseif (is_array($categories)) + { + $categories = array_values(SimplePie_Misc::array_unique($categories)); + } + elseif (is_array($categories_parent)) + { + $categories = array_values(SimplePie_Misc::array_unique($categories_parent)); + } + + // COPYRIGHTS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights =& new $this->feed->copyright_class($copyright_url, $copyright_label); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights =& new $this->feed->copyright_class($copyright_url, $copyright_label); + } + else + { + $copyrights = $copyrights_parent; + } + + // CREDITS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); + } + if (is_array($credits)) + { + $credits = array_values(SimplePie_Misc::array_unique($credits)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); + } + if (is_array($credits)) + { + $credits = array_values(SimplePie_Misc::array_unique($credits)); + } + } + else + { + $credits = $credits_parent; + } + + // DESCRIPTION + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $description = $description_parent; + } + + // HASHES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(SimplePie_Misc::array_unique($hashes)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(SimplePie_Misc::array_unique($hashes)); + } + } + else + { + $hashes = $hashes_parent; + } + + // KEYWORDS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(SimplePie_Misc::array_unique($keywords)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(SimplePie_Misc::array_unique($keywords)); + } + } + else + { + $keywords = $keywords_parent; + } + + // PLAYER + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + $player = $player_parent; + } + + // RATINGS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + if (is_array($ratings)) + { + $ratings = array_values(SimplePie_Misc::array_unique($ratings)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + if (is_array($ratings)) + { + $ratings = array_values(SimplePie_Misc::array_unique($ratings)); + } + } + else + { + $ratings = $ratings_parent; + } + + // RESTRICTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + if (is_array($restrictions)) + { + $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + if (is_array($restrictions)) + { + $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); + } + } + else + { + $restrictions = $restrictions_parent; + } + + // THUMBNAILS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); + } + } + else + { + $thumbnails = $thumbnails_parent; + } + + // TITLES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $title = $title_parent; + } + + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width); + } + } + } + + // If we have standalone media:content tags, loop through them. + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) + { + foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) + { + if (isset($content['attribs']['']['url'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; + + // Start checking the attributes of media:content + if (isset($content['attribs']['']['bitrate'])) + { + $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['channels'])) + { + $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['duration'])) + { + $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $duration = $duration_parent; + } + if (isset($content['attribs']['']['expression'])) + { + $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['framerate'])) + { + $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['height'])) + { + $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['lang'])) + { + $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['fileSize'])) + { + $length = ceil($content['attribs']['']['fileSize']); + } + if (isset($content['attribs']['']['medium'])) + { + $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['samplingrate'])) + { + $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['type'])) + { + $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['width'])) + { + $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + + // Checking the other optional media: elements. Priority: media:content, media:group, item, channel + + // CAPTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); + } + if (is_array($captions)) + { + $captions = array_values(SimplePie_Misc::array_unique($captions)); + } + } + else + { + $captions = $captions_parent; + } + + // CATEGORIES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->feed->category_class($term, $scheme, $label); + } + } + if (is_array($categories) && is_array($categories_parent)) + { + $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent))); + } + elseif (is_array($categories)) + { + $categories = array_values(SimplePie_Misc::array_unique($categories)); + } + elseif (is_array($categories_parent)) + { + $categories = array_values(SimplePie_Misc::array_unique($categories_parent)); + } + else + { + $categories = null; + } + + // COPYRIGHTS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights =& new $this->feed->copyright_class($copyright_url, $copyright_label); + } + else + { + $copyrights = $copyrights_parent; + } + + // CREDITS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); + } + if (is_array($credits)) + { + $credits = array_values(SimplePie_Misc::array_unique($credits)); + } + } + else + { + $credits = $credits_parent; + } + + // DESCRIPTION + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $description = $description_parent; + } + + // HASHES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(SimplePie_Misc::array_unique($hashes)); + } + } + else + { + $hashes = $hashes_parent; + } + + // KEYWORDS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(SimplePie_Misc::array_unique($keywords)); + } + } + else + { + $keywords = $keywords_parent; + } + + // PLAYER + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + $player = $player_parent; + } + + // RATINGS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + if (is_array($ratings)) + { + $ratings = array_values(SimplePie_Misc::array_unique($ratings)); + } + } + else + { + $ratings = $ratings_parent; + } + + // RESTRICTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + if (is_array($restrictions)) + { + $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); + } + } + else + { + $restrictions = $restrictions_parent; + } + + // THUMBNAILS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); + } + } + else + { + $thumbnails = $thumbnails_parent; + } + + // TITLES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $title = $title_parent; + } + + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width); + } + } + } + + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) + { + if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] == 'enclosure') + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + if (isset($link['attribs']['']['type'])) + { + $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($link['attribs']['']['length'])) + { + $length = ceil($link['attribs']['']['length']); + } + + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); + } + } + + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) + { + if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] == 'enclosure') + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + if (isset($link['attribs']['']['type'])) + { + $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($link['attribs']['']['length'])) + { + $length = ceil($link['attribs']['']['length']); + } + + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); + } + } + + if ($enclosure = $this->get_item_tags('', 'enclosure')) + { + if (isset($enclosure[0]['attribs']['']['url'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0])); + if (isset($enclosure[0]['attribs']['']['type'])) + { + $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($enclosure[0]['attribs']['']['length'])) + { + $length = ceil($enclosure[0]['attribs']['']['length']); + } + + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); + } + } + + if (sizeof($this->data['enclosures']) == 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width)) + { + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); + } + + $this->data['enclosures'] = array_values(SimplePie_Misc::array_unique($this->data['enclosures'])); + } + if (!empty($this->data['enclosures'])) + { + return $this->data['enclosures']; + } + else + { + return null; + } + } + + function get_latitude() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[1]; + } + else + { + return null; + } + } + + function get_longitude() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + { + return (float) $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[2]; + } + else + { + return null; + } + } + + function get_source() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source')) + { + return new $this->feed->source_class($this, $return[0]); + } + else + { + return null; + } + } + + /** + * Creates the add_to_* methods' return data + * + * @access private + * @param string $item_url String to prefix to the item permalink + * @param string $title_url String to prefix to the item title + * (and suffix to the item permalink) + * @return mixed URL if feed exists, false otherwise + */ + function add_to_service($item_url, $title_url = null, $summary_url = null) + { + if ($this->get_permalink() !== null) + { + $return = $this->sanitize($item_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_permalink()); + if ($title_url !== null && $this->get_title() !== null) + { + $return .= $this->sanitize($title_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_title()); + } + if ($summary_url !== null && $this->get_description() !== null) + { + $return .= $this->sanitize($summary_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_description()); + } + return $return; + } + else + { + return null; + } + } + + function add_to_blinklist() + { + return $this->add_to_service('http://www.blinklist.com/index.php?Action=Blink/addblink.php&Description=&Url=', '&Title='); + } + + function add_to_blogmarks() + { + return $this->add_to_service('http://blogmarks.net/my/new.php?mini=1&simple=1&url=', '&title='); + } + + function add_to_delicious() + { + return $this->add_to_service('http://del.icio.us/post/?v=4&url=', '&title='); + } + + function add_to_digg() + { + return $this->add_to_service('http://digg.com/submit?url=', '&title=', '&bodytext='); + } + + function add_to_furl() + { + return $this->add_to_service('http://www.furl.net/storeIt.jsp?u=', '&t='); + } + + function add_to_magnolia() + { + return $this->add_to_service('http://ma.gnolia.com/bookmarklet/add?url=', '&title='); + } + + function add_to_myweb20() + { + return $this->add_to_service('http://myweb2.search.yahoo.com/myresults/bookmarklet?u=', '&t='); + } + + function add_to_newsvine() + { + return $this->add_to_service('http://www.newsvine.com/_wine/save?u=', '&h='); + } + + function add_to_reddit() + { + return $this->add_to_service('http://reddit.com/submit?url=', '&title='); + } + + function add_to_segnalo() + { + return $this->add_to_service('http://segnalo.com/post.html.php?url=', '&title='); + } + + function add_to_simpy() + { + return $this->add_to_service('http://www.simpy.com/simpy/LinkAdd.do?href=', '&title='); + } + + function add_to_spurl() + { + return $this->add_to_service('http://www.spurl.net/spurl.php?v=3&url=', '&title='); + } + + function add_to_wists() + { + return $this->add_to_service('http://wists.com/r.php?c=&r=', '&title='); + } + + function search_technorati() + { + return $this->add_to_service('http://www.technorati.com/search/'); + } +} + +class SimplePie_Source +{ + var $item; + var $data = array(); + + function SimplePie_Source($item, $data) + { + $this->item = $item; + $this->data = $data; + } + + function __toString() + { + return md5(serialize($this->data)); + } + + /** + * Remove items that link back to this before destroying this object + */ + function __destruct() + { + unset($this->item); + } + + function get_source_tags($namespace, $tag) + { + if (isset($this->data['child'][$namespace][$tag])) + { + return $this->data['child'][$namespace][$tag]; + } + else + { + return null; + } + } + + function get_base($element = array()) + { + return $this->item->get_base($element); + } + + function sanitize($data, $type, $base = '') + { + return $this->item->sanitize($data, $type, $base); + } + + function get_item() + { + return $this->item; + } + + function get_title() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags('', 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + function get_categories() + { + $categories = array(); + + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) + { + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->item->feed->category_class($term, $scheme, $label); + } + foreach ((array) $this->get_source_tags('', 'category') as $category) + { + $categories[] =& new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] =& new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + { + $categories[] =& new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($categories)) + { + return SimplePie_Misc::array_unique($categories); + } + else + { + return null; + } + } + + function get_author($key = 0) + { + $authors = $this->get_authors(); + if (isset($authors[$key])) + { + return $authors[$key]; + } + else + { + return null; + } + } + + function get_authors() + { + $authors = array(); + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) + { + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $authors[] =& new $this->item->feed->author_class($name, $uri, $email); + } + } + if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) + { + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] =& new $this->item->feed->author_class($name, $url, $email); + } + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + { + $authors[] =& new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] =& new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] =& new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($authors)) + { + return SimplePie_Misc::array_unique($authors); + } + else + { + return null; + } + } + + function get_contributor($key = 0) + { + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) + { + return $contributors[$key]; + } + else + { + return null; + } + } + + function get_contributors() + { + $contributors = array(); + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) + { + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] =& new $this->item->feed->author_class($name, $uri, $email); + } + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + { + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] =& new $this->item->feed->author_class($name, $url, $email); + } + } + + if (!empty($contributors)) + { + return SimplePie_Misc::array_unique($contributors); + } + else + { + return null; + } + } + + function get_link($key = 0, $rel = 'alternate') + { + $links = $this->get_links($rel); + if (isset($links[$key])) + { + return $links[$key]; + } + else + { + return null; + } + } + + /** + * Added for parity between the parent-level and the item/entry-level. + */ + function get_permalink() + { + return $this->get_link(0); + } + + function get_links($rel = 'alternate') + { + if (!isset($this->data['links'])) + { + $this->data['links'] = array(); + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } + } + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + + } + } + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_source_tags('', 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + + $keys = array_keys($this->data['links']); + foreach ($keys as $key) + { + if (SimplePie_Misc::is_isegment_nz_nc($key)) + { + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } + } + elseif (substr($key, 0, 41) == SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + { + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); + } + } + + if (isset($this->data['links'][$rel])) + { + return $this->data['links'][$rel]; + } + else + { + return null; + } + } + + function get_description() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags('', 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + else + { + return null; + } + } + + function get_copyright() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags('', 'copyright')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_language() + { + if ($return = $this->get_source_tags('', 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['xml_lang'])) + { + return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_latitude() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[1]; + } + else + { + return null; + } + } + + function get_longitude() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + { + return (float) $return[0]['data']; + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[2]; + } + else + { + return null; + } + } + + function get_image_url() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) + { + return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + else + { + return null; + } + } +} + +class SimplePie_Author +{ + var $name; + var $link; + var $email; + + // Constructor, used to input the data + function SimplePie_Author($name = null, $link = null, $email = null) + { + $this->name = $name; + $this->link = $link; + $this->email = $email; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_name() + { + if ($this->name !== null) + { + return $this->name; + } + else + { + return null; + } + } + + function get_link() + { + if ($this->link !== null) + { + return $this->link; + } + else + { + return null; + } + } + + function get_email() + { + if ($this->email !== null) + { + return $this->email; + } + else + { + return null; + } + } +} + +class SimplePie_Category +{ + var $term; + var $scheme; + var $label; + + // Constructor, used to input the data + function SimplePie_Category($term = null, $scheme = null, $label = null) + { + $this->term = $term; + $this->scheme = $scheme; + $this->label = $label; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_term() + { + if ($this->term !== null) + { + return $this->term; + } + else + { + return null; + } + } + + function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; + } + else + { + return null; + } + } + + function get_label() + { + if ($this->label !== null) + { + return $this->label; + } + else + { + return $this->get_term(); + } + } +} + +class SimplePie_Enclosure +{ + var $bitrate; + var $captions; + var $categories; + var $channels; + var $copyright; + var $credits; + var $description; + var $duration; + var $expression; + var $framerate; + var $handler; + var $hashes; + var $height; + var $javascript; + var $keywords; + var $lang; + var $length; + var $link; + var $medium; + var $player; + var $ratings; + var $restrictions; + var $samplingrate; + var $thumbnails; + var $title; + var $type; + var $width; + + // Constructor, used to input the data + function SimplePie_Enclosure($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null) + { + $this->bitrate = $bitrate; + $this->captions = $captions; + $this->categories = $categories; + $this->channels = $channels; + $this->copyright = $copyright; + $this->credits = $credits; + $this->description = $description; + $this->duration = $duration; + $this->expression = $expression; + $this->framerate = $framerate; + $this->hashes = $hashes; + $this->height = $height; + $this->javascript = $javascript; + $this->keywords = $keywords; + $this->lang = $lang; + $this->length = $length; + $this->link = $link; + $this->medium = $medium; + $this->player = $player; + $this->ratings = $ratings; + $this->restrictions = $restrictions; + $this->samplingrate = $samplingrate; + $this->thumbnails = $thumbnails; + $this->title = $title; + $this->type = $type; + $this->width = $width; + if (class_exists('idna_convert')) + { + $idn =& new idna_convert; + $parsed = SimplePie_Misc::parse_url($link); + $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); + } + $this->handler = $this->get_handler(); // Needs to load last + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_bitrate() + { + if ($this->bitrate !== null) + { + return $this->bitrate; + } + else + { + return null; + } + } + + function get_caption($key = 0) + { + $captions = $this->get_captions(); + if (isset($captions[$key])) + { + return $captions[$key]; + } + else + { + return null; + } + } + + function get_captions() + { + if ($this->captions !== null) + { + return $this->captions; + } + else + { + return null; + } + } + + function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + function get_categories() + { + if ($this->categories !== null) + { + return $this->categories; + } + else + { + return null; + } + } + + function get_channels() + { + if ($this->channels !== null) + { + return $this->channels; + } + else + { + return null; + } + } + + function get_copyright() + { + if ($this->copyright !== null) + { + return $this->copyright; + } + else + { + return null; + } + } + + function get_credit($key = 0) + { + $credits = $this->get_credits(); + if (isset($credits[$key])) + { + return $credits[$key]; + } + else + { + return null; + } + } + + function get_credits() + { + if ($this->credits !== null) + { + return $this->credits; + } + else + { + return null; + } + } + + function get_description() + { + if ($this->description !== null) + { + return $this->description; + } + else + { + return null; + } + } + + function get_duration($convert = false) + { + if ($this->duration !== null) + { + if ($convert) + { + $time = SimplePie_Misc::time_hms($this->duration); + return $time; + } + else + { + return $this->duration; + } + } + else + { + return null; + } + } + + function get_expression() + { + if ($this->expression !== null) + { + return $this->expression; + } + else + { + return 'full'; + } + } + + function get_extension() + { + if ($this->link !== null) + { + $url = SimplePie_Misc::parse_url($this->link); + if ($url['path'] !== '') + { + return pathinfo($url['path'], PATHINFO_EXTENSION); + } + } + return null; + } + + function get_framerate() + { + if ($this->framerate !== null) + { + return $this->framerate; + } + else + { + return null; + } + } + + function get_handler() + { + return $this->get_real_type(true); + } + + function get_hash($key = 0) + { + $hashes = $this->get_hashes(); + if (isset($hashes[$key])) + { + return $hashes[$key]; + } + else + { + return null; + } + } + + function get_hashes() + { + if ($this->hashes !== null) + { + return $this->hashes; + } + else + { + return null; + } + } + + function get_height() + { + if ($this->height !== null) + { + return $this->height; + } + else + { + return null; + } + } + + function get_language() + { + if ($this->lang !== null) + { + return $this->lang; + } + else + { + return null; + } + } + + function get_keyword($key = 0) + { + $keywords = $this->get_keywords(); + if (isset($keywords[$key])) + { + return $keywords[$key]; + } + else + { + return null; + } + } + + function get_keywords() + { + if ($this->keywords !== null) + { + return $this->keywords; + } + else + { + return null; + } + } + + function get_length() + { + if ($this->length !== null) + { + return $this->length; + } + else + { + return null; + } + } + + function get_link() + { + if ($this->link !== null) + { + return urldecode($this->link); + } + else + { + return null; + } + } + + function get_medium() + { + if ($this->medium !== null) + { + return $this->medium; + } + else + { + return null; + } + } + + function get_player() + { + if ($this->player !== null) + { + return $this->player; + } + else + { + return null; + } + } + + function get_rating($key = 0) + { + $ratings = $this->get_ratings(); + if (isset($ratings[$key])) + { + return $ratings[$key]; + } + else + { + return null; + } + } + + function get_ratings() + { + if ($this->ratings !== null) + { + return $this->ratings; + } + else + { + return null; + } + } + + function get_restriction($key = 0) + { + $restrictions = $this->get_restrictions(); + if (isset($restrictions[$key])) + { + return $restrictions[$key]; + } + else + { + return null; + } + } + + function get_restrictions() + { + if ($this->restrictions !== null) + { + return $this->restrictions; + } + else + { + return null; + } + } + + function get_sampling_rate() + { + if ($this->samplingrate !== null) + { + return $this->samplingrate; + } + else + { + return null; + } + } + + function get_size() + { + $length = $this->get_length(); + if ($length !== null) + { + return round($length/1048576, 2); + } + else + { + return null; + } + } + + function get_thumbnail($key = 0) + { + $thumbnails = $this->get_thumbnails(); + if (isset($thumbnails[$key])) + { + return $thumbnails[$key]; + } + else + { + return null; + } + } + + function get_thumbnails() + { + if ($this->thumbnails !== null) + { + return $this->thumbnails; + } + else + { + return null; + } + } + + function get_title() + { + if ($this->title !== null) + { + return $this->title; + } + else + { + return null; + } + } + + function get_type() + { + if ($this->type !== null) + { + return $this->type; + } + else + { + return null; + } + } + + function get_width() + { + if ($this->width !== null) + { + return $this->width; + } + else + { + return null; + } + } + + function native_embed($options='') + { + return $this->embed($options, true); + } + + /** + * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'. + */ + function embed($options = '', $native = false) + { + // Set up defaults + $audio = ''; + $video = ''; + $alt = ''; + $altclass = ''; + $loop = 'false'; + $width = 'auto'; + $height = 'auto'; + $bgcolor = '#ffffff'; + $mediaplayer = ''; + $widescreen = false; + $handler = $this->get_handler(); + $type = $this->get_real_type(); + + // Process options and reassign values as necessary + if (is_array($options)) + { + extract($options); + } + else + { + $options = explode(',', $options); + foreach($options as $option) + { + $opt = explode(':', $option, 2); + if (isset($opt[0], $opt[1])) + { + $opt[0] = trim($opt[0]); + $opt[1] = trim($opt[1]); + switch ($opt[0]) + { + case 'audio': + $audio = $opt[1]; + break; + + case 'video': + $video = $opt[1]; + break; + + case 'alt': + $alt = $opt[1]; + break; + + case 'altclass': + $altclass = $opt[1]; + break; + + case 'loop': + $loop = $opt[1]; + break; + + case 'width': + $width = $opt[1]; + break; + + case 'height': + $height = $opt[1]; + break; + + case 'bgcolor': + $bgcolor = $opt[1]; + break; + + case 'mediaplayer': + $mediaplayer = $opt[1]; + break; + + case 'widescreen': + $widescreen = $opt[1]; + break; + } + } + } + } + + $mime = explode('/', $type, 2); + $mime = $mime[0]; + + // Process values for 'auto' + if ($width == 'auto') + { + if ($mime == 'video') + { + if ($height == 'auto') + { + $width = 480; + } + elseif ($widescreen) + { + $width = round((intval($height)/9)*16); + } + else + { + $width = round((intval($height)/3)*4); + } + } + else + { + $width = '100%'; + } + } + + if ($height == 'auto') + { + if ($mime == 'audio') + { + $height = 0; + } + elseif ($mime == 'video') + { + if ($width == 'auto') + { + if ($widescreen) + { + $height = 270; + } + else + { + $height = 360; + } + } + elseif ($widescreen) + { + $height = round((intval($width)/16)*9); + } + else + { + $height = round((intval($width)/4)*3); + } + } + else + { + $height = 376; + } + } + elseif ($mime == 'audio') + { + $height = 0; + } + + // Set proper placeholder value + if ($mime == 'audio') + { + $placeholder = $audio; + } + elseif ($mime == 'video') + { + $placeholder = $video; + } + + $embed = ''; + + // Make sure the JS library is included + if (!$native) + { + static $javascript_outputted = null; + if (!$javascript_outputted && $this->javascript) + { + $embed .= '<script type="text/javascript" src="?' . htmlspecialchars($this->javascript) . '"></script>'; + $javascript_outputted = true; + } + } + + // Odeo Feed MP3's + if ($handler == 'odeo') + { + if ($native) + { + $embed .= '<embed src="http://odeo.com/flash/audio_player_fullsize.swf" pluginspage="http://adobe.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="440" height="80" wmode="transparent" allowScriptAccess="any" flashvars="valid_sample_rate=true&external_url=' . $this->get_link() . '"></embed>'; + } + else + { + $embed .= '<script type="text/javascript">embed_odeo("' . $this->get_link() . '");</script>'; + } + } + + // Flash + elseif ($handler == 'flash') + { + if ($native) + { + $embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>"; + } + else + { + $embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>"; + } + } + + // Flash Media Player file types. + // Preferred handler for MP3 file types. + elseif ($handler == 'fmedia' || ($handler == 'mp3' && $mediaplayer != '')) + { + $height += 20; + if ($native) + { + $embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>"; + } + else + { + $embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>"; + } + } + + // QuickTime 7 file types. Need to test with QuickTime 6. + // Only handle MP3's if the Flash Media Player is not present. + elseif ($handler == 'quicktime' || ($handler == 'mp3' && $mediaplayer == '')) + { + $height += 16; + if ($native) + { + if ($placeholder != ""){ + $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>"; + } + else { + $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>"; + } + } + else + { + $embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>"; + } + } + + // Windows Media + elseif ($handler == 'wmedia') + { + $height += 45; + if ($native) + { + $embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>"; + } + else + { + $embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>"; + } + } + + // Everything else + else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>'; + + return $embed; + } + + function get_real_type($find_handler = false) + { + // If it's Odeo, let's get it out of the way. + if (substr(strtolower($this->get_link()), 0, 15) == 'http://odeo.com') + { + return 'odeo'; + } + + // Mime-types by handler. + $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash + $types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player + $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime + $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media + $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3 + + if ($this->get_type() !== null) + { + $type = strtolower($this->type); + } + else + { + $type = null; + } + + // If we encounter an unsupported mime-type, check the file extension and guess intelligently. + if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3))) + { + switch (strtolower($this->get_extension())) + { + // Audio mime-types + case 'aac': + case 'adts': + $type = 'audio/acc'; + break; + + case 'aif': + case 'aifc': + case 'aiff': + case 'cdda': + $type = 'audio/aiff'; + break; + + case 'bwf': + $type = 'audio/wav'; + break; + + case 'kar': + case 'mid': + case 'midi': + case 'smf': + $type = 'audio/midi'; + break; + + case 'm4a': + $type = 'audio/x-m4a'; + break; + + case 'mp3': + case 'swa': + $type = 'audio/mp3'; + break; + + case 'wav': + $type = 'audio/wav'; + break; + + case 'wax': + $type = 'audio/x-ms-wax'; + break; + + case 'wma': + $type = 'audio/x-ms-wma'; + break; + + // Video mime-types + case '3gp': + case '3gpp': + $type = 'video/3gpp'; + break; + + case '3g2': + case '3gp2': + $type = 'video/3gpp2'; + break; + + case 'asf': + $type = 'video/x-ms-asf'; + break; + + case 'flv': + $type = 'video/x-flv'; + break; + + case 'm1a': + case 'm1s': + case 'm1v': + case 'm15': + case 'm75': + case 'mp2': + case 'mpa': + case 'mpeg': + case 'mpg': + case 'mpm': + case 'mpv': + $type = 'video/mpeg'; + break; + + case 'm4v': + $type = 'video/x-m4v'; + break; + + case 'mov': + case 'qt': + $type = 'video/quicktime'; + break; + + case 'mp4': + case 'mpg4': + $type = 'video/mp4'; + break; + + case 'sdv': + $type = 'video/sd-video'; + break; + + case 'wm': + $type = 'video/x-ms-wm'; + break; + + case 'wmv': + $type = 'video/x-ms-wmv'; + break; + + case 'wvx': + $type = 'video/x-ms-wvx'; + break; + + // Flash mime-types + case 'spl': + $type = 'application/futuresplash'; + break; + + case 'swf': + $type = 'application/x-shockwave-flash'; + break; + } + } + + if ($find_handler) + { + if (in_array($type, $types_flash)) + { + return 'flash'; + } + elseif (in_array($type, $types_fmedia)) + { + return 'fmedia'; + } + elseif (in_array($type, $types_quicktime)) + { + return 'quicktime'; + } + elseif (in_array($type, $types_wmedia)) + { + return 'wmedia'; + } + elseif (in_array($type, $types_mp3)) + { + return 'mp3'; + } + else + { + return null; + } + } + else + { + return $type; + } + } +} + +class SimplePie_Caption +{ + var $type; + var $lang; + var $startTime; + var $endTime; + var $text; + + // Constructor, used to input the data + function SimplePie_Caption($type = null, $lang = null, $startTime = null, $endTime = null, $text = null) + { + $this->type = $type; + $this->lang = $lang; + $this->startTime = $startTime; + $this->endTime = $endTime; + $this->text = $text; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_endtime() + { + if ($this->endTime !== null) + { + return $this->endTime; + } + else + { + return null; + } + } + + function get_language() + { + if ($this->lang !== null) + { + return $this->lang; + } + else + { + return null; + } + } + + function get_starttime() + { + if ($this->startTime !== null) + { + return $this->startTime; + } + else + { + return null; + } + } + + function get_text() + { + if ($this->text !== null) + { + return $this->text; + } + else + { + return null; + } + } + + function get_type() + { + if ($this->type !== null) + { + return $this->type; + } + else + { + return null; + } + } +} + +class SimplePie_Credit +{ + var $role; + var $scheme; + var $name; + + // Constructor, used to input the data + function SimplePie_Credit($role = null, $scheme = null, $name = null) + { + $this->role = $role; + $this->scheme = $scheme; + $this->name = $name; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_role() + { + if ($this->role !== null) + { + return $this->role; + } + else + { + return null; + } + } + + function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; + } + else + { + return null; + } + } + + function get_name() + { + if ($this->name !== null) + { + return $this->name; + } + else + { + return null; + } + } +} + +class SimplePie_Copyright +{ + var $url; + var $label; + + // Constructor, used to input the data + function SimplePie_Copyright($url = null, $label = null) + { + $this->url = $url; + $this->label = $label; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_url() + { + if ($this->url !== null) + { + return $this->url; + } + else + { + return null; + } + } + + function get_attribution() + { + if ($this->label !== null) + { + return $this->label; + } + else + { + return null; + } + } +} + +class SimplePie_Rating +{ + var $scheme; + var $value; + + // Constructor, used to input the data + function SimplePie_Rating($scheme = null, $value = null) + { + $this->scheme = $scheme; + $this->value = $value; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; + } + else + { + return null; + } + } + + function get_value() + { + if ($this->value !== null) + { + return $this->value; + } + else + { + return null; + } + } +} + +class SimplePie_Restriction +{ + var $relationship; + var $type; + var $value; + + // Constructor, used to input the data + function SimplePie_Restriction($relationship = null, $type = null, $value = null) + { + $this->relationship = $relationship; + $this->type = $type; + $this->value = $value; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_relationship() + { + if ($this->relationship !== null) + { + return $this->relationship; + } + else + { + return null; + } + } + + function get_type() + { + if ($this->type !== null) + { + return $this->type; + } + else + { + return null; + } + } + + function get_value() + { + if ($this->value !== null) + { + return $this->value; + } + else + { + return null; + } + } +} + +/** + * @todo Move to properly supporting RFC2616 (HTTP/1.1) + */ +class SimplePie_File +{ + var $url; + var $useragent; + var $success = true; + var $headers = array(); + var $body; + var $status_code; + var $redirects = 0; + var $error; + var $method = SIMPLEPIE_FILE_SOURCE_NONE; + + function SimplePie_File($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) + { + if (class_exists('idna_convert')) + { + $idn =& new idna_convert; + $parsed = SimplePie_Misc::parse_url($url); + $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); + } + $this->url = $url; + $this->useragent = $useragent; + if (preg_match('/^http(s)?:\/\//i', $url)) + { + if ($useragent === null) + { + $useragent = ini_get('user_agent'); + $this->useragent = $useragent; + } + if (!is_array($headers)) + { + $headers = array(); + } + if (!$force_fsockopen && function_exists('curl_exec')) + { + $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL; + $fp = curl_init(); + $headers2 = array(); + foreach ($headers as $key => $value) + { + $headers2[] = "$key: $value"; + } + if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>=')) + { + curl_setopt($fp, CURLOPT_ENCODING, ''); + } + curl_setopt($fp, CURLOPT_URL, $url); + curl_setopt($fp, CURLOPT_HEADER, 1); + curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); + curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); + curl_setopt($fp, CURLOPT_REFERER, $url); + curl_setopt($fp, CURLOPT_USERAGENT, $useragent); + curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); + if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) + { + curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); + } + + $this->headers = curl_exec($fp); + if (curl_errno($fp) == 23 || curl_errno($fp) == 61) + { + curl_setopt($fp, CURLOPT_ENCODING, 'none'); + $this->headers = curl_exec($fp); + } + if (curl_errno($fp)) + { + $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp); + $this->success = false; + } + else + { + $info = curl_getinfo($fp); + curl_close($fp); + $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1); + $this->headers = array_pop($this->headers); + $parser =& new SimplePie_HTTP_Parser($this->headers); + if ($parser->parse()) + { + $this->headers = $parser->headers; + $this->body = $parser->body; + $this->status_code = $parser->status_code; + if (($this->status_code == 300 || $this->status_code == 301 || $this->status_code == 302 || $this->status_code == 303 || $this->status_code == 307 || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) + { + $this->redirects++; + $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); + return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + } + } + } + } + else + { + $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN; + $url_parts = parse_url($url); + if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) == 'https') + { + $url_parts['host'] = "ssl://$url_parts[host]"; + $url_parts['port'] = 443; + } + if (!isset($url_parts['port'])) + { + $url_parts['port'] = 80; + } + $fp = @fsockopen($url_parts['host'], $url_parts['port'], $errno, $errstr, $timeout); + if (!$fp) + { + $this->error = 'fsockopen error: ' . $errstr; + $this->success = false; + } + else + { + stream_set_timeout($fp, $timeout); + if (isset($url_parts['path'])) + { + if (isset($url_parts['query'])) + { + $get = "$url_parts[path]?$url_parts[query]"; + } + else + { + $get = $url_parts['path']; + } + } + else + { + $get = '/'; + } + $out = "GET $get HTTP/1.0\r\n"; + $out .= "Host: $url_parts[host]\r\n"; + $out .= "User-Agent: $useragent\r\n"; + if (extension_loaded('zlib')) + { + $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n"; + } + + if (isset($url_parts['user']) && isset($url_parts['pass'])) + { + $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n"; + } + foreach ($headers as $key => $value) + { + $out .= "$key: $value\r\n"; + } + $out .= "Connection: Close\r\n\r\n"; + fwrite($fp, $out); + + $info = stream_get_meta_data($fp); + + $this->headers = ''; + while (!$info['eof'] && !$info['timed_out']) + { + $this->headers .= fread($fp, 1160); + $info = stream_get_meta_data($fp); + } + if (!$info['timed_out']) + { + $parser =& new SimplePie_HTTP_Parser($this->headers); + if ($parser->parse()) + { + $this->headers = $parser->headers; + $this->body = $parser->body; + $this->status_code = $parser->status_code; + if (($this->status_code == 300 || $this->status_code == 301 || $this->status_code == 302 || $this->status_code == 303 || $this->status_code == 307 || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) + { + $this->redirects++; + $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); + return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + } + if (isset($this->headers['content-encoding'])) + { + // Hey, we act dumb elsewhere, so let's do that here too + switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20"))) + { + case 'gzip': + case 'x-gzip': + $decoder = new SimplePie_gzdecode($this->body); + if (!$decoder->parse()) + { + $this->error = 'Unable to decode HTTP "gzip" stream'; + $this->success = false; + } + else + { + $this->body = $decoder->data; + } + break; + + case 'deflate': + if (($body = gzuncompress($this->body)) === false) + { + if (($body = gzinflate($this->body)) === false) + { + $this->error = 'Unable to decode HTTP "deflate" stream'; + $this->success = false; + } + } + $this->body = $body; + break; + + default: + $this->error = 'Unknown content coding'; + $this->success = false; + } + } + } + } + else + { + $this->error = 'fsocket timed out'; + $this->success = false; + } + fclose($fp); + } + } + } + else + { + $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS; + if (!$this->body = file_get_contents($url)) + { + $this->error = 'file_get_contents could not read the file'; + $this->success = false; + } + } + } +} + +/** + * HTTP Response Parser + * + * @package SimplePie + */ +class SimplePie_HTTP_Parser +{ + /** + * HTTP Version + * + * @access public + * @var float + */ + var $http_version = 0.0; + + /** + * Status code + * + * @access public + * @var int + */ + var $status_code = 0; + + /** + * Reason phrase + * + * @access public + * @var string + */ + var $reason = ''; + + /** + * Key/value pairs of the headers + * + * @access public + * @var array + */ + var $headers = array(); + + /** + * Body of the response + * + * @access public + * @var string + */ + var $body = ''; + + /** + * Current state of the state machine + * + * @access private + * @var string + */ + var $state = 'http_version'; + + /** + * Input data + * + * @access private + * @var string + */ + var $data = ''; + + /** + * Input data length (to avoid calling strlen() everytime this is needed) + * + * @access private + * @var int + */ + var $data_length = 0; + + /** + * Current position of the pointer + * + * @var int + * @access private + */ + var $position = 0; + + /** + * Name of the hedaer currently being parsed + * + * @access private + * @var string + */ + var $name = ''; + + /** + * Value of the hedaer currently being parsed + * + * @access private + * @var string + */ + var $value = ''; + + /** + * Create an instance of the class with the input data + * + * @access public + * @param string $data Input data + */ + function SimplePie_HTTP_Parser($data) + { + $this->data = $data; + $this->data_length = strlen($this->data); + } + + /** + * Parse the input data + * + * @access public + * @return bool true on success, false on failure + */ + function parse() + { + while ($this->state && $this->state !== 'emit' && $this->has_data()) + { + $state = $this->state; + $this->$state(); + } + $this->data = ''; + if ($this->state === 'emit' || $this->state === 'body') + { + return true; + } + else + { + $this->http_version = ''; + $this->status_code = ''; + $this->reason = ''; + $this->headers = array(); + $this->body = ''; + return false; + } + } + + /** + * Check whether there is data beyond the pointer + * + * @access private + * @return bool true if there is further data, false if not + */ + function has_data() + { + return (bool) ($this->position < $this->data_length); + } + + /** + * See if the next character is LWS + * + * @access private + * @return bool true if the next character is LWS, false if not + */ + function is_linear_whitespace() + { + return (bool) ($this->data[$this->position] === "\x09" + || $this->data[$this->position] === "\x20" + || ($this->data[$this->position] === "\x0A" + && isset($this->data[$this->position + 1]) + && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20"))); + } + + /** + * Parse the HTTP version + * + * @access private + */ + function http_version() + { + if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') + { + $len = strspn($this->data, '0123456789.', 5); + $this->http_version = substr($this->data, 5, $len); + $this->position += 5 + $len; + if (substr_count($this->http_version, '.') <= 1) + { + $this->http_version = (float) $this->http_version; + $this->position += strspn($this->data, "\x09\x20", $this->position); + $this->state = 'status'; + } + else + { + $this->state = false; + } + } + else + { + $this->state = false; + } + } + + /** + * Parse the status code + * + * @access private + */ + function status() + { + if ($len = strspn($this->data, '0123456789', $this->position)) + { + $this->status_code = (int) substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'reason'; + } + else + { + $this->state = false; + } + } + + /** + * Parse the reason phrase + * + * @access private + */ + function reason() + { + $len = strcspn($this->data, "\x0A", $this->position); + $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20"); + $this->position += $len + 1; + $this->state = 'new_line'; + } + + /** + * Deal with a new line, shifting data around as needed + * + * @access private + */ + function new_line() + { + $this->value = trim($this->value, "\x0D\x20"); + if ($this->name !== '' && $this->value !== '') + { + $this->name = strtolower($this->name); + if (isset($this->headers[$this->name])) + { + $this->headers[$this->name] .= ', ' . $this->value; + } + else + { + $this->headers[$this->name] = $this->value; + } + } + $this->name = ''; + $this->value = ''; + if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") + { + $this->position += 2; + $this->state = 'body'; + } + elseif ($this->data[$this->position] === "\x0A") + { + $this->position++; + $this->state = 'body'; + } + else + { + $this->state = 'name'; + } + } + + /** + * Parse a header name + * + * @access private + */ + function name() + { + $len = strcspn($this->data, "\x0A:", $this->position); + if (isset($this->data[$this->position + $len])) + { + if ($this->data[$this->position + $len] === "\x0A") + { + $this->position += $len; + $this->state = 'new_line'; + } + else + { + $this->name = substr($this->data, $this->position, $len); + $this->position += $len + 1; + $this->state = 'value'; + } + } + else + { + $this->state = false; + } + } + + /** + * Parse LWS, replacing consecutive LWS characters with a single space + * + * @access private + */ + function linear_whitespace() + { + do + { + if (substr($this->data, $this->position, 2) === "\x0D\x0A") + { + $this->position += 2; + } + elseif ($this->data[$this->position] === "\x0A") + { + $this->position++; + } + $this->position += strspn($this->data, "\x09\x20", $this->position); + } while ($this->has_data() && $this->is_linear_whitespace()); + $this->value .= "\x20"; + } + + /** + * See what state to move to while within non-quoted header values + * + * @access private + */ + function value() + { + if ($this->is_linear_whitespace()) + { + $this->linear_whitespace(); + } + else + { + switch ($this->data[$this->position]) + { + case '"': + $this->position++; + $this->state = 'quote'; + break; + + case "\x0A": + $this->position++; + $this->state = 'new_line'; + break; + + default: + $this->state = 'value_char'; + break; + } + } + } + + /** + * Parse a header value while outside quotes + * + * @access private + */ + function value_char() + { + $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position); + $this->value .= substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'value'; + } + + /** + * See what state to move to while within quoted header values + * + * @access private + */ + function quote() + { + if ($this->is_linear_whitespace()) + { + $this->linear_whitespace(); + } + else + { + switch ($this->data[$this->position]) + { + case '"': + $this->position++; + $this->state = 'value'; + break; + + case "\x0A": + $this->position++; + $this->state = 'new_line'; + break; + + case '\\': + $this->position++; + $this->state = 'quote_escaped'; + break; + + default: + $this->state = 'quote_char'; + break; + } + } + } + + /** + * Parse a header value while within quotes + * + * @access private + */ + function quote_char() + { + $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position); + $this->value .= substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'value'; + } + + /** + * Parse an escaped character within quotes + * + * @access private + */ + function quote_escaped() + { + $this->value .= $this->data[$this->position]; + $this->position++; + $this->state = 'quote'; + } + + /** + * Parse the body + * + * @access private + */ + function body() + { + $this->body = substr($this->data, $this->position); + $this->state = 'emit'; + } +} + +/** + * gzdecode + * + * @package SimplePie + */ +class SimplePie_gzdecode +{ + /** + * Compressed data + * + * @access private + * @see gzdecode::$data + */ + var $compressed_data; + + /** + * Size of compressed data + * + * @access private + */ + var $compressed_size; + + /** + * Minimum size of a valid gzip string + * + * @access private + */ + var $min_compressed_size = 18; + + /** + * Current position of pointer + * + * @access private + */ + var $position = 0; + + /** + * Flags (FLG) + * + * @access private + */ + var $flags; + + /** + * Uncompressed data + * + * @access public + * @see gzdecode::$compressed_data + */ + var $data; + + /** + * Modified time + * + * @access public + */ + var $MTIME; + + /** + * Extra Flags + * + * @access public + */ + var $XFL; + + /** + * Operating System + * + * @access public + */ + var $OS; + + /** + * Subfield ID 1 + * + * @access public + * @see gzdecode::$extra_field + * @see gzdecode::$SI2 + */ + var $SI1; + + /** + * Subfield ID 2 + * + * @access public + * @see gzdecode::$extra_field + * @see gzdecode::$SI1 + */ + var $SI2; + + /** + * Extra field content + * + * @access public + * @see gzdecode::$SI1 + * @see gzdecode::$SI2 + */ + var $extra_field; + + /** + * Original filename + * + * @access public + */ + var $filename; + + /** + * Human readable comment + * + * @access public + */ + var $comment; + + /** + * Don't allow anything to be set + * + * @access public + */ + function __set($name, $value) + { + trigger_error("Cannot write property $name", E_USER_ERROR); + } + + /** + * Set the compressed string and related properties + * + * @access public + */ + function SimplePie_gzdecode($data) + { + $this->compressed_data = $data; + $this->compressed_size = strlen($data); + } + + /** + * Decode the GZIP stream + * + * @access public + */ + function parse() + { + if ($this->compressed_size >= $this->min_compressed_size) + { + // Check ID1, ID2, and CM + if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") + { + return false; + } + + // Get the FLG (FLaGs) + $this->flags = ord($this->compressed_data[3]); + + // FLG bits above (1 << 4) are reserved + if ($this->flags > 0x1F) + { + return false; + } + + // Advance the pointer after the above + $this->position += 4; + + // MTIME + $mtime = substr($this->compressed_data, $this->position, 4); + // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness + if (current(unpack('S', "\x00\x01")) === 1) + { + $mtime = strrev($mtime); + } + $this->MTIME = current(unpack('l', $mtime)); + $this->position += 4; + + // Get the XFL (eXtra FLags) + $this->XFL = ord($this->compressed_data[$this->position++]); + + // Get the OS (Operating System) + $this->OS = ord($this->compressed_data[$this->position++]); + + // Parse the FEXTRA + if ($this->flags & 4) + { + // Read subfield IDs + $this->SI1 = $this->compressed_data[$this->position++]; + $this->SI2 = $this->compressed_data[$this->position++]; + + // SI2 set to zero is reserved for future use + if ($this->SI2 === "\x00") + { + return false; + } + + // Get the length of the extra field + $len = current(unpack('v', substr($this->compressed_data, $this->position, 2))); + $position += 2; + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 4; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the extra field to the given data + $this->extra_field = substr($this->compressed_data, $this->position, $len); + $this->position += $len; + } + else + { + return false; + } + } + + // Parse the FNAME + if ($this->flags & 8) + { + // Get the length of the filename + $len = strspn($this->compressed_data, "\x00", $this->position); + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 1; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the original filename to the given string + $this->filename = substr($this->compressed_data, $this->position, $len); + $this->position += $len + 1; + } + else + { + return false; + } + } + + // Parse the FCOMMENT + if ($this->flags & 16) + { + // Get the length of the comment + $len = strspn($this->compressed_data, "\x00", $this->position); + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 1; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the original comment to the given string + $this->comment = substr($this->compressed_data, $this->position, $len); + $this->position += $len + 1; + } + else + { + return false; + } + } + + // Parse the FHCRC + if ($this->flags & 2) + { + // Check the length of the string is still valid + $this->min_compressed_size += $len + 2; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Read the CRC + $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2))); + + // Check the CRC matches + if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) + { + $this->position += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + + // Decompress the actual data + if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) + { + return false; + } + else + { + $this->position = $this->compressed_size - 8; + } + + // Check CRC of data + $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4))); + $this->position += 4; + /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc)) + { + return false; + }*/ + + // Check ISIZE of data + $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4))); + $this->position += 4; + if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) + { + return false; + } + + // Wow, against all odds, we've actually got a valid gzip string + return true; + } + else + { + return false; + } + } +} + +class SimplePie_Cache +{ + /** + * Don't call the constructor. Please. + * + * @access private + */ + function SimplePie_Cache() + { + trigger_error('Please call SimplePie_Cache::create() instead of the constructor', E_USER_ERROR); + } + + /** + * Create a new SimplePie_Cache object + * + * @static + * @access public + */ + function create($location, $filename, $extension) + { + return new SimplePie_Cache_File($location, $filename, $extension); + } +} + +class SimplePie_Cache_File +{ + var $location; + var $filename; + var $extension; + var $name; + + function SimplePie_Cache_File($location, $filename, $extension) + { + $this->location = $location; + $this->filename = rawurlencode($filename); + $this->extension = rawurlencode($extension); + $this->name = "$location/$this->filename.$this->extension"; + } + + function save($data) + { + if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location)) + { + if (is_a($data, 'SimplePie')) + { + $data = $data->data; + } + + $data = serialize($data); + + if (function_exists('file_put_contents')) + { + return (bool) file_put_contents($this->name, $data); + } + else + { + $fp = fopen($this->name, 'wb'); + if ($fp) + { + fwrite($fp, $data); + fclose($fp); + return true; + } + } + } + return false; + } + + function load() + { + if (file_exists($this->name) && is_readable($this->name)) + { + return unserialize(file_get_contents($this->name)); + } + return false; + } + + function mtime() + { + if (file_exists($this->name)) + { + return filemtime($this->name); + } + return false; + } + + function touch() + { + if (file_exists($this->name)) + { + return touch($this->name); + } + return false; + } + + function unlink() + { + if (file_exists($this->name)) + { + return unlink($this->name); + } + return false; + } +} + +class SimplePie_Misc +{ + function time_hms($seconds) + { + $time = ''; + + $hours = floor($seconds / 3600); + $remainder = $seconds % 3600; + if ($hours > 0) + { + $time .= $hours.':'; + } + + $minutes = floor($remainder / 60); + $seconds = $remainder % 60; + if ($minutes < 10 && $hours > 0) + { + $minutes = '0' . $minutes; + } + if ($seconds < 10) + { + $seconds = '0' . $seconds; + } + + $time .= $minutes.':'; + $time .= $seconds; + + return $time; + } + + function absolutize_url($relative, $base) + { + if ($relative !== '') + { + $relative = SimplePie_Misc::parse_url($relative); + if ($relative['scheme'] !== '') + { + $target = $relative; + } + elseif ($base !== '') + { + $base = SimplePie_Misc::parse_url($base); + $target = SimplePie_Misc::parse_url(''); + if ($relative['authority'] !== '') + { + $target = $relative; + $target['scheme'] = $base['scheme']; + } + else + { + $target['scheme'] = $base['scheme']; + $target['authority'] = $base['authority']; + if ($relative['path'] !== '') + { + if (strpos($relative['path'], '/') === 0) + { + $target['path'] = $relative['path']; + } + elseif ($base['authority'] !== '' && $base['path'] === '') + { + $target['path'] = '/' . $relative['path']; + } + elseif (($last_segment = strrpos($base['path'], '/')) !== false) + { + $target['path'] = substr($base['path'], 0, $last_segment + 1) . $relative['path']; + } + else + { + $target['path'] = $relative['path']; + } + $target['query'] = $relative['query']; + } + else + { + $target['path'] = $base['path']; + if ($relative['query'] !== '') + { + $target['query'] = $relative['query']; + } + elseif ($base['query'] !== '') + { + $target['query'] = $base['query']; + } + } + } + $target['fragment'] = $relative['fragment']; + } + else + { + // No base URL, just return the relative URL + $target = $relative; + } + $return = SimplePie_Misc::compress_parse_url($target['scheme'], $target['authority'], $target['path'], $target['query'], $target['fragment']); + } + else + { + $return = $base; + } + $return = SimplePie_Misc::normalize_url($return); + return $return; + } + + function remove_dot_segments($input) + { + $output = ''; + while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input == '.' || $input == '..') + { + // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, + if (strpos($input, '../') === 0) + { + $input = substr($input, 3); + } + elseif (strpos($input, './') === 0) + { + $input = substr($input, 2); + } + // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, + elseif (strpos($input, '/./') === 0) + { + $input = substr_replace($input, '/', 0, 3); + } + elseif ($input == '/.') + { + $input = '/'; + } + // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, + elseif (strpos($input, '/../') === 0) + { + $input = substr_replace($input, '/', 0, 4); + $output = substr_replace($output, '', strrpos($output, '/')); + } + elseif ($input == '/..') + { + $input = '/'; + $output = substr_replace($output, '', strrpos($output, '/')); + } + // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, + elseif ($input == '.' || $input == '..') + { + $input = ''; + } + // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer + elseif (($pos = strpos($input, '/', 1)) !== false) + { + $output .= substr($input, 0, $pos); + $input = substr_replace($input, '', 0, $pos); + } + else + { + $output .= $input; + $input = ''; + } + } + return $output . $input; + } + + function get_element($realname, $string) + { + $return = array(); + $name = preg_quote($realname, '/'); + if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) + { + for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++) + { + $return[$i]['tag'] = $realname; + $return[$i]['full'] = $matches[$i][0][0]; + $return[$i]['offset'] = $matches[$i][0][1]; + if (strlen($matches[$i][3][0]) <= 2) + { + $return[$i]['self_closing'] = true; + } + else + { + $return[$i]['self_closing'] = false; + $return[$i]['content'] = $matches[$i][4][0]; + } + $return[$i]['attribs'] = array(); + if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER)) + { + for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++) + { + if (count($attribs[$j]) == 2) + { + $attribs[$j][2] = $attribs[$j][1]; + } + $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); + } + } + } + } + return $return; + } + + function element_implode($element) + { + $full = "<$element[tag]"; + foreach ($element['attribs'] as $key => $value) + { + $key = strtolower($key); + $full .= " $key=\"" . htmlspecialchars($value['data']) . '"'; + } + if ($element['self_closing']) + { + $full .= ' />'; + } + else + { + $full .= ">$element[content]</$element[tag]>"; + } + return $full; + } + + function error($message, $level, $file, $line) + { + switch ($level) + { + case E_USER_ERROR: + $note = 'PHP Error'; + break; + case E_USER_WARNING: + $note = 'PHP Warning'; + break; + case E_USER_NOTICE: + $note = 'PHP Notice'; + break; + default: + $note = 'Unknown Error'; + break; + } + error_log("$note: $message in $file on line $line", 0); + return $message; + } + + /** + * If a file has been cached, retrieve and display it. + * + * This is most useful for caching images (get_favicon(), etc.), + * however it works for all cached files. This WILL NOT display ANY + * file/image/page/whatever, but rather only display what has already + * been cached by SimplePie. + * + * @access public + * @see SimplePie::get_favicon() + * @param str $identifier_url URL that is used to identify the content. + * This may or may not be the actual URL of the live content. + * @param str $cache_location Location of SimplePie's cache. Defaults + * to './cache'. + * @param str $cache_extension The file extension that the file was + * cached with. Defaults to 'spc'. + * @param str $cache_class Name of the cache-handling class being used + * in SimplePie. Defaults to 'SimplePie_Cache', and should be left + * as-is unless you've overloaded the class. + * @param str $cache_name_function Obsolete. Exists for backwards + * compatibility reasons only. + */ + function display_cached_file($identifier_url, $cache_location = './cache', $cache_extension = 'spc', $cache_class = 'SimplePie_Cache', $cache_name_function = 'md5') + { + $cache = call_user_func(array($cache_class, 'create'), $cache_location, $identifier_url, $cache_extension); + + if ($file = $cache->load()) + { + if (isset($file['headers']['content-type'])) + { + header('Content-type:' . $file['headers']['content-type']); + } + else + { + header('Content-type: application/octet-stream'); + } + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days + echo $file['body']; + exit; + } + + die('Cached file for ' . $identifier_url . ' cannot be found.'); + } + + function fix_protocol($url, $http = 1) + { + $url = SimplePie_Misc::normalize_url($url); + $parsed = SimplePie_Misc::parse_url($url); + if ($parsed['scheme'] !== '' && $parsed['scheme'] != 'http' && $parsed['scheme'] != 'https') + { + return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http); + } + + if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url)) + { + return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http); + } + + if ($http == 2 && $parsed['scheme'] !== '') + { + return "feed:$url"; + } + elseif ($http == 3 && strtolower($parsed['scheme']) == 'http') + { + return substr_replace($url, 'podcast', 0, 4); + } + elseif ($http == 4 && strtolower($parsed['scheme']) == 'http') + { + return substr_replace($url, 'itpc', 0, 4); + } + else + { + return $url; + } + } + + function parse_url($url) + { + static $cache = array(); + if (isset($cache[$url])) + { + return $cache[$url]; + } + elseif (preg_match('/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/', $url, $match)) + { + for ($i = count($match); $i <= 9; $i++) + { + $match[$i] = ''; + } + return $cache[$url] = array('scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5], 'query' => $match[7], 'fragment' => $match[9]); + } + else + { + return $cache[$url] = array('scheme' => '', 'authority' => '', 'path' => '', 'query' => '', 'fragment' => ''); + } + } + + function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '') + { + $return = ''; + if ($scheme !== '') + { + $return .= "$scheme:"; + } + if ($authority !== '') + { + $return .= "//$authority"; + } + if ($path !== '') + { + $return .= $path; + } + if ($query !== '') + { + $return .= "?$query"; + } + if ($fragment !== '') + { + $return .= "#$fragment"; + } + return $return; + } + + function normalize_url($url) + { + $url = preg_replace_callback('/%([0-9A-Fa-f]{2})/', array('SimplePie_Misc', 'percent_encoding_normalization'), $url); + $url = SimplePie_Misc::parse_url($url); + $url['scheme'] = strtolower($url['scheme']); + if ($url['authority'] !== '') + { + $url['authority'] = strtolower($url['authority']); + $url['path'] = SimplePie_Misc::remove_dot_segments($url['path']); + } + return SimplePie_Misc::compress_parse_url($url['scheme'], $url['authority'], $url['path'], $url['query'], $url['fragment']); + } + + function percent_encoding_normalization($match) + { + $integer = hexdec($match[1]); + if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer == 0x2D || $integer == 0x2E || $integer == 0x5F || $integer == 0x7E) + { + return chr($integer); + } + else + { + return strtoupper($match[0]); + } + } + + /** + * Remove bad UTF-8 bytes + * + * PCRE Pattern to locate bad bytes in a UTF-8 string comes from W3C + * FAQ: Multilingual Forms (modified to include full ASCII range) + * + * @author Geoffrey Sneddon + * @see http://www.w3.org/International/questions/qa-forms-utf-8 + * @param string $str String to remove bad UTF-8 bytes from + * @return string UTF-8 string + */ + function utf8_bad_replace($str) + { + if (function_exists('iconv') && ($return = @iconv('UTF-8', 'UTF-8//IGNORE', $str))) + { + return $return; + } + elseif (function_exists('mb_convert_encoding') && ($return = @mb_convert_encoding($str, 'UTF-8', 'UTF-8'))) + { + return $return; + } + elseif (preg_match_all('/(?:[\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})+/', $str, $matches)) + { + return implode("\xEF\xBF\xBD", $matches[0]); + } + elseif ($str !== '') + { + return "\xEF\xBF\xBD"; + } + else + { + return ''; + } + } + + /** + * Converts a Windows-1252 encoded string to a UTF-8 encoded string + * + * @static + * @access public + * @param string $string Windows-1252 encoded string + * @return string UTF-8 encoded string + */ + function windows_1252_to_utf8($string) + { + static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF"); + + return strtr($string, $convert_table); + } + + function change_encoding($data, $input, $output) + { + $input = SimplePie_Misc::encoding($input); + $output = SimplePie_Misc::encoding($output); + + // We fail to fail on non US-ASCII bytes + if ($input === 'US-ASCII') + { + static $non_ascii_octects = ''; + if (!$non_ascii_octects) + { + for ($i = 0x80; $i <= 0xFF; $i++) + { + $non_ascii_octects .= chr($i); + } + } + $data = substr($data, 0, strcspn($data, $non_ascii_octects)); + } + + // This is first, as behaviour of this is completely predictable + if ($input === 'Windows-1252' && $output === 'UTF-8') + { + return SimplePie_Misc::windows_1252_to_utf8($data); + } + // This is second, as behaviour of this varies only with PHP version + elseif (function_exists('mb_convert_encoding') && ($return = @mb_convert_encoding($data, $output, $input))) + { + return $return; + } + // This is last, as behaviour of this varies with OS userland and PHP version + elseif (function_exists('iconv') && ($return = @iconv($input, $output, $data))) + { + return $return; + } + // If we can't do anything, just fail + else + { + return false; + } + } + + function encoding($charset) + { + /* Character sets are case-insensitive, and also need some further + normalization in the real world (though we'll return them in the form given + in their registration). */ + switch (strtolower(preg_replace('/[\x09-\x0D\x20-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]/', '', $charset))) + { + case 'adobestandardencoding': + case 'csadobestandardencoding': + return 'Adobe-Standard-Encoding'; + + case 'adobesymbolencoding': + case 'cshppsmath': + return 'Adobe-Symbol-Encoding'; + + case 'ami1251': + case 'amiga1251': + return 'Amiga-1251'; + + case 'ansix31101983': + case 'csat5001983': + case 'csiso99naplps': + case 'isoir99': + case 'naplps': + return 'ANSI_X3.110-1983'; + + case 'arabic7': + case 'asmo449': + case 'csiso89asmo449': + case 'isoir89': + case 'iso9036': + return 'ASMO_449'; + + case 'big5': + case 'csbig5': + case 'xxbig5': + return 'Big5'; + + case 'big5hkscs': + return 'Big5-HKSCS'; + + case 'bocu1': + case 'csbocu1': + return 'BOCU-1'; + + case 'brf': + case 'csbrf': + return 'BRF'; + + case 'bs4730': + case 'csiso4unitedkingdom': + case 'gb': + case 'isoir4': + case 'iso646gb': + case 'uk': + return 'BS_4730'; + + case 'bsviewdata': + case 'csiso47bsviewdata': + case 'isoir47': + return 'BS_viewdata'; + + case 'cesu8': + case 'cscesu8': + return 'CESU-8'; + + case 'ca': + case 'csa71': + case 'csaz243419851': + case 'csiso121canadian1': + case 'isoir121': + case 'iso646ca': + return 'CSA_Z243.4-1985-1'; + + case 'csa72': + case 'csaz243419852': + case 'csiso122canadian2': + case 'isoir122': + case 'iso646ca2': + return 'CSA_Z243.4-1985-2'; + + case 'csaz24341985gr': + case 'csiso123csaz24341985gr': + case 'isoir123': + return 'CSA_Z243.4-1985-gr'; + + case 'csiso139csn369103': + case 'csn369103': + case 'isoir139': + return 'CSN_369103'; + + case 'csdecmcs': + case 'dec': + case 'decmcs': + return 'DEC-MCS'; + + case 'csiso21german': + case 'de': + case 'din66003': + case 'isoir21': + case 'iso646de': + return 'DIN_66003'; + + case 'csdkus': + case 'dkus': + return 'dk-us'; + + case 'csiso646danish': + case 'dk': + case 'ds2089': + case 'iso646dk': + return 'DS_2089'; + + case 'csibmebcdicatde': + case 'ebcdicatde': + return 'EBCDIC-AT-DE'; + + case 'csebcdicatdea': + case 'ebcdicatdea': + return 'EBCDIC-AT-DE-A'; + + case 'csebcdiccafr': + case 'ebcdiccafr': + return 'EBCDIC-CA-FR'; + + case 'csebcdicdkno': + case 'ebcdicdkno': + return 'EBCDIC-DK-NO'; + + case 'csebcdicdknoa': + case 'ebcdicdknoa': + return 'EBCDIC-DK-NO-A'; + + case 'csebcdices': + case 'ebcdices': + return 'EBCDIC-ES'; + + case 'csebcdicesa': + case 'ebcdicesa': + return 'EBCDIC-ES-A'; + + case 'csebcdicess': + case 'ebcdicess': + return 'EBCDIC-ES-S'; + + case 'csebcdicfise': + case 'ebcdicfise': + return 'EBCDIC-FI-SE'; + + case 'csebcdicfisea': + case 'ebcdicfisea': + return 'EBCDIC-FI-SE-A'; + + case 'csebcdicfr': + case 'ebcdicfr': + return 'EBCDIC-FR'; + + case 'csebcdicit': + case 'ebcdicit': + return 'EBCDIC-IT'; + + case 'csebcdicpt': + case 'ebcdicpt': + return 'EBCDIC-PT'; + + case 'csebcdicuk': + case 'ebcdicuk': + return 'EBCDIC-UK'; + + case 'csebcdicus': + case 'ebcdicus': + return 'EBCDIC-US'; + + case 'csiso111ecmacyrillic': + case 'ecmacyrillic': + case 'isoir111': + case 'koi8e': + return 'ECMA-cyrillic'; + + case 'csiso17spanish': + case 'es': + case 'isoir17': + case 'iso646es': + return 'ES'; + + case 'csiso85spanish2': + case 'es2': + case 'isoir85': + case 'iso646es2': + return 'ES2'; + + case 'cseucfixwidjapanese': + case 'extendedunixcodefixedwidthforjapanese': + return 'Extended_UNIX_Code_Fixed_Width_for_Japanese'; + + case 'cseucpkdfmtjapanese': + case 'eucjp': + case 'extendedunixcodepackedformatforjapanese': + return 'Extended_UNIX_Code_Packed_Format_for_Japanese'; + + case 'gb18030': + return 'GB18030'; + + case 'cp936': + case 'gbk': + case 'ms936': + case 'windows936': + case 'csgb2312': + case 'gb2312': + case 'chinese': + case 'csiso58gb231280': + case 'gb231280': + case 'isoir58': + return 'GBK'; + + case 'cn': + case 'csiso57gb1988': + case 'gb198880': + case 'isoir57': + case 'iso646cn': + return 'GB_1988-80'; + + case 'csiso153gost1976874': + case 'gost1976874': + case 'isoir153': + case 'stsev35888': + return 'GOST_19768-74'; + + case 'csiso150': + case 'csiso150greekccitt': + case 'greekccitt': + case 'isoir150': + return 'greek-ccitt'; + + case 'csiso88greek7': + case 'greek7': + case 'isoir88': + return 'greek7'; + + case 'csiso18greek7old': + case 'greek7old': + case 'isoir18': + return 'greek7-old'; + + case 'cshpdesktop': + case 'hpdesktop': + return 'HP-DeskTop'; + + case 'cshplegal': + case 'hplegal': + return 'HP-Legal'; + + case 'cshpmath8': + case 'hpmath8': + return 'HP-Math8'; + + case 'cshppifont': + case 'hppifont': + return 'HP-Pi-font'; + + case 'cshproman8': + case 'hproman8': + case 'r8': + case 'roman8': + return 'hp-roman8'; + + case 'hzgb2312': + return 'HZ-GB-2312'; + + case 'csibmsymbols': + case 'ibmsymbols': + return 'IBM-Symbols'; + + case 'csibmthai': + case 'ibmthai': + return 'IBM-Thai'; + + case 'ccsid00858': + case 'cp00858': + case 'ibm00858': + case 'pcmultilingual850euro': + return 'IBM00858'; + + case 'ccsid00924': + case 'cp00924': + case 'ebcdiclatin9euro': + case 'ibm00924': + return 'IBM00924'; + + case 'ccsid01140': + case 'cp01140': + case 'ebcdicus37euro': + case 'ibm01140': + return 'IBM01140'; + + case 'ccsid01141': + case 'cp01141': + case 'ebcdicde273euro': + case 'ibm01141': + return 'IBM01141'; + + case 'ccsid01142': + case 'cp01142': + case 'ebcdicdk277euro': + case 'ebcdicno277euro': + case 'ibm01142': + return 'IBM01142'; + + case 'ccsid01143': + case 'cp01143': + case 'ebcdicfi278euro': + case 'ebcdicse278euro': + case 'ibm01143': + return 'IBM01143'; + + case 'ccsid01144': + case 'cp01144': + case 'ebcdicit280euro': + case 'ibm01144': + return 'IBM01144'; + + case 'ccsid01145': + case 'cp01145': + case 'ebcdices284euro': + case 'ibm01145': + return 'IBM01145'; + + case 'ccsid01146': + case 'cp01146': + case 'ebcdicgb285euro': + case 'ibm01146': + return 'IBM01146'; + + case 'ccsid01147': + case 'cp01147': + case 'ebcdicfr297euro': + case 'ibm01147': + return 'IBM01147'; + + case 'ccsid01148': + case 'cp01148': + case 'ebcdicinternational500euro': + case 'ibm01148': + return 'IBM01148'; + + case 'ccsid01149': + case 'cp01149': + case 'ebcdicis871euro': + case 'ibm01149': + return 'IBM01149'; + + case 'cp037': + case 'csibm037': + case 'ebcdiccpca': + case 'ebcdiccpnl': + case 'ebcdiccpus': + case 'ebcdiccpwt': + case 'ibm037': + return 'IBM037'; + + case 'cp038': + case 'csibm038': + case 'ebcdicint': + case 'ibm038': + return 'IBM038'; + + case 'cp273': + case 'csibm273': + case 'ibm273': + return 'IBM273'; + + case 'cp274': + case 'csibm274': + case 'ebcdicbe': + case 'ibm274': + return 'IBM274'; + + case 'cp275': + case 'csibm275': + case 'ebcdicbr': + case 'ibm275': + return 'IBM275'; + + case 'csibm277': + case 'ebcdiccpdk': + case 'ebcdiccpno': + case 'ibm277': + return 'IBM277'; + + case 'cp278': + case 'csibm278': + case 'ebcdiccpfi': + case 'ebcdiccpse': + case 'ibm278': + return 'IBM278'; + + case 'cp280': + case 'csibm280': + case 'ebcdiccpit': + case 'ibm280': + return 'IBM280'; + + case 'cp281': + case 'csibm281': + case 'ebcdicjpe': + case 'ibm281': + return 'IBM281'; + + case 'cp284': + case 'csibm284': + case 'ebcdiccpes': + case 'ibm284': + return 'IBM284'; + + case 'cp285': + case 'csibm285': + case 'ebcdiccpgb': + case 'ibm285': + return 'IBM285'; + + case 'cp290': + case 'csibm290': + case 'ebcdicjpkana': + case 'ibm290': + return 'IBM290'; + + case 'cp297': + case 'csibm297': + case 'ebcdiccpfr': + case 'ibm297': + return 'IBM297'; + + case 'cp420': + case 'csibm420': + case 'ebcdiccpar1': + case 'ibm420': + return 'IBM420'; + + case 'cp423': + case 'csibm423': + case 'ebcdiccpgr': + case 'ibm423': + return 'IBM423'; + + case 'cp424': + case 'csibm424': + case 'ebcdiccphe': + case 'ibm424': + return 'IBM424'; + + case '437': + case 'cp437': + case 'cspc8codepage437': + case 'ibm437': + return 'IBM437'; + + case 'cp500': + case 'csibm500': + case 'ebcdiccpbe': + case 'ebcdiccpch': + case 'ibm500': + return 'IBM500'; + + case 'cp775': + case 'cspc775baltic': + case 'ibm775': + return 'IBM775'; + + case '850': + case 'cp850': + case 'cspc850multilingual': + case 'ibm850': + return 'IBM850'; + + case '851': + case 'cp851': + case 'csibm851': + case 'ibm851': + return 'IBM851'; + + case '852': + case 'cp852': + case 'cspcp852': + case 'ibm852': + return 'IBM852'; + + case '855': + case 'cp855': + case 'csibm855': + case 'ibm855': + return 'IBM855'; + + case '857': + case 'cp857': + case 'csibm857': + case 'ibm857': + return 'IBM857'; + + case '860': + case 'cp860': + case 'csibm860': + case 'ibm860': + return 'IBM860'; + + case '861': + case 'cpis': + case 'cp861': + case 'csibm861': + case 'ibm861': + return 'IBM861'; + + case '862': + case 'cp862': + case 'cspc862latinhebrew': + case 'ibm862': + return 'IBM862'; + + case '863': + case 'cp863': + case 'csibm863': + case 'ibm863': + return 'IBM863'; + + case 'cp864': + case 'csibm864': + case 'ibm864': + return 'IBM864'; + + case '865': + case 'cp865': + case 'csibm865': + case 'ibm865': + return 'IBM865'; + + case '866': + case 'cp866': + case 'csibm866': + case 'ibm866': + return 'IBM866'; + + case 'cpar': + case 'cp868': + case 'csibm868': + case 'ibm868': + return 'IBM868'; + + case '869': + case 'cpgr': + case 'cp869': + case 'csibm869': + case 'ibm869': + return 'IBM869'; + + case 'cp870': + case 'csibm870': + case 'ebcdiccproece': + case 'ebcdiccpyu': + case 'ibm870': + return 'IBM870'; + + case 'cp871': + case 'csibm871': + case 'ebcdiccpis': + case 'ibm871': + return 'IBM871'; + + case 'cp880': + case 'csibm880': + case 'ebcdiccyrillic': + case 'ibm880': + return 'IBM880'; + + case 'cp891': + case 'csibm891': + case 'ibm891': + return 'IBM891'; + + case 'cp903': + case 'csibm903': + case 'ibm903': + return 'IBM903'; + + case '904': + case 'cp904': + case 'csibbm904': + case 'ibm904': + return 'IBM904'; + + case 'cp905': + case 'csibm905': + case 'ebcdiccptr': + case 'ibm905': + return 'IBM905'; + + case 'cp918': + case 'csibm918': + case 'ebcdiccpar2': + case 'ibm918': + return 'IBM918'; + + case 'cp1026': + case 'csibm1026': + case 'ibm1026': + return 'IBM1026'; + + case 'ibm1047': + return 'IBM1047'; + + case 'csiso143iecp271': + case 'iecp271': + case 'isoir143': + return 'IEC_P27-1'; + + case 'csiso49inis': + case 'inis': + case 'isoir49': + return 'INIS'; + + case 'csiso50inis8': + case 'inis8': + case 'isoir50': + return 'INIS-8'; + + case 'csiso51iniscyrillic': + case 'iniscyrillic': + case 'isoir51': + return 'INIS-cyrillic'; + + case 'csinvariant': + case 'invariant': + return 'INVARIANT'; + + case 'iso2022cn': + return 'ISO-2022-CN'; + + case 'iso2022cnext': + return 'ISO-2022-CN-EXT'; + + case 'csiso2022jp': + case 'iso2022jp': + return 'ISO-2022-JP'; + + case 'csiso2022jp2': + case 'iso2022jp2': + return 'ISO-2022-JP-2'; + + case 'csiso2022kr': + case 'iso2022kr': + return 'ISO-2022-KR'; + + case 'cswindows30latin1': + case 'iso88591windows30latin1': + return 'ISO-8859-1-Windows-3.0-Latin-1'; + + case 'cswindows31latin1': + case 'iso88591windows31latin1': + return 'ISO-8859-1-Windows-3.1-Latin-1'; + + case 'csisolatin2': + case 'iso88592': + case 'isoir101': + case 'iso88592': + case 'iso885921987': + case 'l2': + case 'latin2': + return 'ISO-8859-2'; + + case 'cswindows31latin2': + case 'iso88592windowslatin2': + return 'ISO-8859-2-Windows-Latin-2'; + + case 'csisolatin3': + case 'iso88593': + case 'isoir109': + case 'iso88593': + case 'iso885931988': + case 'l3': + case 'latin3': + return 'ISO-8859-3'; + + case 'csisolatin4': + case 'iso88594': + case 'isoir110': + case 'iso88594': + case 'iso885941988': + case 'l4': + case 'latin4': + return 'ISO-8859-4'; + + case 'csisolatincyrillic': + case 'cyrillic': + case 'iso88595': + case 'isoir144': + case 'iso88595': + case 'iso885951988': + return 'ISO-8859-5'; + + case 'arabic': + case 'asmo708': + case 'csisolatinarabic': + case 'ecma114': + case 'iso88596': + case 'isoir127': + case 'iso88596': + case 'iso885961987': + return 'ISO-8859-6'; + + case 'csiso88596e': + case 'iso88596e': + case 'iso88596e': + return 'ISO-8859-6-E'; + + case 'csiso88596i': + case 'iso88596i': + case 'iso88596i': + return 'ISO-8859-6-I'; + + case 'csisolatingreek': + case 'ecma118': + case 'elot928': + case 'greek': + case 'greek8': + case 'iso88597': + case 'isoir126': + case 'iso88597': + case 'iso885971987': + return 'ISO-8859-7'; + + case 'csisolatinhebrew': + case 'hebrew': + case 'iso88598': + case 'isoir138': + case 'iso88598': + case 'iso885981988': + return 'ISO-8859-8'; + + case 'csiso88598e': + case 'iso88598e': + case 'iso88598e': + return 'ISO-8859-8-E'; + + case 'csiso88598i': + case 'iso88598i': + case 'iso88598i': + return 'ISO-8859-8-I'; + + case 'cswindows31latin5': + case 'iso88599windowslatin5': + return 'ISO-8859-9-Windows-Latin-5'; + + case 'csisolatin6': + case 'iso885910': + case 'isoir157': + case 'iso8859101992': + case 'l6': + case 'latin6': + return 'ISO-8859-10'; + + case 'iso885913': + return 'ISO-8859-13'; + + case 'iso885914': + case 'isoceltic': + case 'isoir199': + case 'iso885914': + case 'iso8859141998': + case 'l8': + case 'latin8': + return 'ISO-8859-14'; + + case 'iso885915': + case 'latin9': + return 'ISO-8859-15'; + + case 'iso885916': + case 'isoir226': + case 'iso885916': + case 'iso8859162001': + case 'l10': + case 'latin10': + return 'ISO-8859-16'; + + case 'iso10646j1': + return 'ISO-10646-J-1'; + + case 'csunicode': + case 'iso10646ucs2': + return 'ISO-10646-UCS-2'; + + case 'csucs4': + case 'iso10646ucs4': + return 'ISO-10646-UCS-4'; + + case 'csunicodeascii': + case 'iso10646ucsbasic': + return 'ISO-10646-UCS-Basic'; + + case 'csunicodelatin1': + case 'iso10646': + case 'iso10646unicodelatin1': + return 'ISO-10646-Unicode-Latin1'; + + case 'csiso10646utf1': + case 'iso10646utf1': + return 'ISO-10646-UTF-1'; + + case 'csiso115481': + case 'iso115481': + case 'iso115481': + case 'isotr115481': + return 'ISO-11548-1'; + + case 'csiso90': + case 'isoir90': + return 'iso-ir-90'; + + case 'csunicodeibm1261': + case 'isounicodeibm1261': + return 'ISO-Unicode-IBM-1261'; + + case 'csunicodeibm1264': + case 'isounicodeibm1264': + return 'ISO-Unicode-IBM-1264'; + + case 'csunicodeibm1265': + case 'isounicodeibm1265': + return 'ISO-Unicode-IBM-1265'; + + case 'csunicodeibm1268': + case 'isounicodeibm1268': + return 'ISO-Unicode-IBM-1268'; + + case 'csunicodeibm1276': + case 'isounicodeibm1276': + return 'ISO-Unicode-IBM-1276'; + + case 'csiso646basic1983': + case 'iso646basic1983': + case 'ref': + return 'ISO_646.basic:1983'; + + case 'csiso2intlrefversion': + case 'irv': + case 'isoir2': + case 'iso646irv1983': + return 'ISO_646.irv:1983'; + + case 'csiso2033': + case 'e13b': + case 'isoir98': + case 'iso20331983': + return 'ISO_2033-1983'; + + case 'csiso5427cyrillic': + case 'isoir37': + case 'iso5427': + return 'ISO_5427'; + + case 'isoir54': + case 'iso5427cyrillic1981': + case 'iso54271981': + return 'ISO_5427:1981'; + + case 'csiso5428greek': + case 'isoir55': + case 'iso54281980': + return 'ISO_5428:1980'; + + case 'csiso6937add': + case 'isoir152': + case 'iso6937225': + return 'ISO_6937-2-25'; + + case 'csisotextcomm': + case 'isoir142': + case 'iso69372add': + return 'ISO_6937-2-add'; + + case 'csiso8859supp': + case 'isoir154': + case 'iso8859supp': + case 'latin125': + return 'ISO_8859-supp'; + + case 'csiso10367box': + case 'isoir155': + case 'iso10367box': + return 'ISO_10367-box'; + + case 'csiso15italian': + case 'isoir15': + case 'iso646it': + case 'it': + return 'IT'; + + case 'csiso13jisc6220jp': + case 'isoir13': + case 'jisc62201969': + case 'jisc62201969jp': + case 'katakana': + case 'x02017': + return 'JIS_C6220-1969-jp'; + + case 'csiso14jisc6220ro': + case 'isoir14': + case 'iso646jp': + case 'jisc62201969ro': + case 'jp': + return 'JIS_C6220-1969-ro'; + + case 'csiso42jisc62261978': + case 'isoir42': + case 'jisc62261978': + return 'JIS_C6226-1978'; + + case 'csiso87jisx0208': + case 'isoir87': + case 'jisc62261983': + case 'jisx02081983': + case 'x0208': + return 'JIS_C6226-1983'; + + case 'csiso91jisc62291984a': + case 'isoir91': + case 'jisc62291984a': + case 'jpocra': + return 'JIS_C6229-1984-a'; + + case 'csiso92jisc62991984b': + case 'isoir92': + case 'iso646jpocrb': + case 'jisc62291984b': + case 'jpocrb': + return 'JIS_C6229-1984-b'; + + case 'csiso93jis62291984badd': + case 'isoir93': + case 'jisc62291984badd': + case 'jpocrbadd': + return 'JIS_C6229-1984-b-add'; + + case 'csiso94jis62291984hand': + case 'isoir94': + case 'jisc62291984hand': + case 'jpocrhand': + return 'JIS_C6229-1984-hand'; + + case 'csiso95jis62291984handadd': + case 'isoir95': + case 'jisc62291984handadd': + case 'jpocrhandadd': + return 'JIS_C6229-1984-hand-add'; + + case 'csiso96jisc62291984kana': + case 'isoir96': + case 'jisc62291984kana': + return 'JIS_C6229-1984-kana'; + + case 'csjisencoding': + case 'jisencoding': + return 'JIS_Encoding'; + + case 'cshalfwidthkatakana': + case 'jisx0201': + case 'x0201': + return 'JIS_X0201'; + + case 'csiso159jisx02121990': + case 'isoir159': + case 'jisx02121990': + case 'x0212': + return 'JIS_X0212-1990'; + + case 'csiso141jusib1002': + case 'isoir141': + case 'iso646yu': + case 'js': + case 'jusib1002': + case 'yu': + return 'JUS_I.B1.002'; + + case 'csiso147macedonian': + case 'isoir147': + case 'jusib1003mac': + case 'macedonian': + return 'JUS_I.B1.003-mac'; + + case 'csiso146serbian': + case 'isoir146': + case 'jusib1003serb': + case 'serbian': + return 'JUS_I.B1.003-serb'; + + case 'koi7switched': + return 'KOI7-switched'; + + case 'cskoi8r': + case 'koi8r': + return 'KOI8-R'; + + case 'koi8u': + return 'KOI8-U'; + + case 'csksc5636': + case 'iso646kr': + case 'ksc5636': + return 'KSC5636'; + + case 'cskz1048': + case 'kz1048': + case 'rk1048': + case 'strk10482002': + return 'KZ-1048'; + + case 'csiso19latingreek': + case 'isoir19': + case 'latingreek': + return 'latin-greek'; + + case 'csiso27latingreek1': + case 'isoir27': + case 'latingreek1': + return 'Latin-greek-1'; + + case 'csiso158lap': + case 'isoir158': + case 'lap': + case 'latinlap': + return 'latin-lap'; + + case 'csmacintosh': + case 'mac': + case 'macintosh': + return 'macintosh'; + + case 'csmicrosoftpublishing': + case 'microsoftpublishing': + return 'Microsoft-Publishing'; + + case 'csmnem': + case 'mnem': + return 'MNEM'; + + case 'csmnemonic': + case 'mnemonic': + return 'MNEMONIC'; + + case 'csiso86hungarian': + case 'hu': + case 'isoir86': + case 'iso646hu': + case 'msz77953': + return 'MSZ_7795.3'; + + case 'csnatsdano': + case 'isoir91': + case 'natsdano': + return 'NATS-DANO'; + + case 'csnatsdanoadd': + case 'isoir92': + case 'natsdanoadd': + return 'NATS-DANO-ADD'; + + case 'csnatssefi': + case 'isoir81': + case 'natssefi': + return 'NATS-SEFI'; + + case 'csnatssefiadd': + case 'isoir82': + case 'natssefiadd': + return 'NATS-SEFI-ADD'; + + case 'csiso151cuba': + case 'cuba': + case 'isoir151': + case 'iso646cu': + case 'ncnc001081': + return 'NC_NC00-10:81'; + + case 'csiso69french': + case 'fr': + case 'isoir69': + case 'iso646fr': + case 'nfz62010': + return 'NF_Z_62-010'; + + case 'csiso25french': + case 'isoir25': + case 'iso646fr1': + case 'nfz620101973': + return 'NF_Z_62-010_(1973)'; + + case 'csiso60danishnorwegian': + case 'csiso60norwegian1': + case 'isoir60': + case 'iso646no': + case 'no': + case 'ns45511': + return 'NS_4551-1'; + + case 'csiso61norwegian2': + case 'isoir61': + case 'iso646no2': + case 'no2': + case 'ns45512': + return 'NS_4551-2'; + + case 'osdebcdicdf03irv': + return 'OSD_EBCDIC_DF03_IRV'; + + case 'osdebcdicdf041': + return 'OSD_EBCDIC_DF04_1'; + + case 'osdebcdicdf0415': + return 'OSD_EBCDIC_DF04_15'; + + case 'cspc8danishnorwegian': + case 'pc8danishnorwegian': + return 'PC8-Danish-Norwegian'; + + case 'cspc8turkish': + case 'pc8turkish': + return 'PC8-Turkish'; + + case 'csiso16portuguese': + case 'isoir16': + case 'iso646pt': + case 'pt': + return 'PT'; + + case 'csiso84portuguese2': + case 'isoir84': + case 'iso646pt2': + case 'pt2': + return 'PT2'; + + case 'cp154': + case 'csptcp154': + case 'cyrillicasian': + case 'pt154': + case 'ptcp154': + return 'PTCP154'; + + case 'scsu': + return 'SCSU'; + + case 'csiso10swedish': + case 'fi': + case 'isoir10': + case 'iso646fi': + case 'iso646se': + case 'se': + case 'sen850200b': + return 'SEN_850200_B'; + + case 'csiso11swedishfornames': + case 'isoir11': + case 'iso646se2': + case 'se2': + case 'sen850200c': + return 'SEN_850200_C'; + + case 'csshiftjis': + case 'mskanji': + case 'shiftjis': + return 'Shift_JIS'; + + case 'csiso102t617bit': + case 'isoir102': + case 't617bit': + return 'T.61-7bit'; + + case 'csiso103t618bit': + case 'isoir103': + case 't61': + case 't618bit': + return 'T.61-8bit'; + + case 'csiso128t101g2': + case 'isoir128': + case 't101g2': + return 'T.101-G2'; + + case 'cstscii': + case 'tscii': + return 'TSCII'; + + case 'csunicode11': + case 'unicode11': + return 'UNICODE-1-1'; + + case 'csunicode11utf7': + case 'unicode11utf7': + return 'UNICODE-1-1-UTF-7'; + + case 'csunknown8bit': + case 'unknown8bit': + return 'UNKNOWN-8BIT'; + + case 'ansix341968': + case 'ansix341986': + case 'ascii': + case 'cp367': + case 'csascii': + case 'ibm367': + case 'isoir6': + case 'iso646us': + case 'iso646irv1991': + case 'us': + case 'usascii': + return 'US-ASCII'; + + case 'csusdk': + case 'usdk': + return 'us-dk'; + + case 'utf7': + return 'UTF-7'; + + case 'utf8': + return 'UTF-8'; + + case 'utf16': + return 'UTF-16'; + + case 'utf16be': + return 'UTF-16BE'; + + case 'utf16le': + return 'UTF-16LE'; + + case 'utf32': + return 'UTF-32'; + + case 'utf32be': + return 'UTF-32BE'; + + case 'utf32le': + return 'UTF-32LE'; + + case 'csventurainternational': + case 'venturainternational': + return 'Ventura-International'; + + case 'csventuramath': + case 'venturamath': + return 'Ventura-Math'; + + case 'csventuraus': + case 'venturaus': + return 'Ventura-US'; + + case 'csiso70videotexsupp1': + case 'isoir70': + case 'videotexsuppl': + return 'videotex-suppl'; + + case 'csviqr': + case 'viqr': + return 'VIQR'; + + case 'csviscii': + case 'viscii': + return 'VISCII'; + + case 'cswindows31j': + case 'windows31j': + return 'Windows-31J'; + + case 'iso885911': + case 'tis620': + return 'Windows-874'; + + case 'cseuckr': + case 'euckr': + case 'windows949': + case 'csksc56011987': + case 'isoir149': + case 'korean': + case 'ksc5601': + case 'ksc56011987': + case 'ksc56011989': + return 'Windows-949'; + + case 'windows1250': + return 'windows-1250'; + + case 'windows1251': + return 'windows-1251'; + + case 'cp819': + case 'csisolatin1': + case 'ibm819': + case 'iso88591': + case 'isoir100': + case 'iso885911987': + case 'l1': + case 'latin1': + case 'windows1252': + return 'Windows-1252'; + + case 'windows1252': + return 'windows-1252'; + + case 'windows1253': + return 'windows-1253'; + + case 'csisolatin5': + case 'iso88599': + case 'isoir148': + case 'iso885991989': + case 'l5': + case 'latin5': + case 'windows1254': + return 'Windows-1254'; + + case 'windows1255': + return 'windows-1255'; + + case 'windows1256': + return 'windows-1256'; + + case 'windows1257': + return 'windows-1257'; + + case 'windows1258': + return 'windows-1258'; + + default: + return $charset; + } + } + + function get_curl_version() + { + if (is_array($curl = curl_version())) + { + $curl = $curl['version']; + } + elseif (substr($curl, 0, 5) == 'curl/') + { + $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5)); + } + elseif (substr($curl, 0, 8) == 'libcurl/') + { + $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8)); + } + else + { + $curl = 0; + } + return $curl; + } + + function is_subclass_of($class1, $class2) + { + if (func_num_args() != 2) + { + trigger_error('Wrong parameter count for SimplePie_Misc::is_subclass_of()', E_USER_WARNING); + } + elseif (version_compare(PHP_VERSION, '5.0.3', '>=') || is_object($class1)) + { + return is_subclass_of($class1, $class2); + } + elseif (is_string($class1) && is_string($class2)) + { + if (class_exists($class1)) + { + if (class_exists($class2)) + { + $class2 = strtolower($class2); + while ($class1 = strtolower(get_parent_class($class1))) + { + if ($class1 == $class2) + { + return true; + } + } + } + } + else + { + trigger_error('Unknown class passed as parameter', E_USER_WARNNG); + } + } + return false; + } + + /** + * Strip HTML comments + * + * @access public + * @param string $data Data to strip comments from + * @return string Comment stripped string + */ + function strip_comments($data) + { + $output = ''; + while (($start = strpos($data, '<!--')) !== false) + { + $output .= substr($data, 0, $start); + if (($end = strpos($data, '-->', $start)) !== false) + { + $data = substr_replace($data, '', 0, $end + 3); + } + else + { + $data = ''; + } + } + return $output . $data; + } + + function parse_date($dt) + { + $parser = SimplePie_Parse_Date::get(); + return $parser->parse($dt); + } + + /** + * Decode HTML entities + * + * @static + * @access public + * @param string $data Input data + * @return string Output data + */ + function entities_decode($data) + { + $decoder = new SimplePie_Decode_HTML_Entities($data); + return $decoder->parse(); + } + + /** + * Remove RFC822 comments + * + * @access public + * @param string $data Data to strip comments from + * @return string Comment stripped string + */ + function uncomment_rfc822($string) + { + $string = (string) $string; + $position = 0; + $length = strlen($string); + $depth = 0; + + $output = ''; + + while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) + { + $output .= substr($string, $position, $pos - $position); + $position = $pos + 1; + if ($string[$pos - 1] !== '\\') + { + $depth++; + while ($depth && $position < $length) + { + $position += strcspn($string, '()', $position); + if ($string[$position - 1] === '\\') + { + $position++; + continue; + } + elseif (isset($string[$position])) + { + switch ($string[$position]) + { + case '(': + $depth++; + break; + + case ')': + $depth--; + break; + } + $position++; + } + else + { + break; + } + } + } + else + { + $output .= '('; + } + } + $output .= substr($string, $position); + + return $output; + } + + function parse_mime($mime) + { + if (($pos = strpos($mime, ';')) === false) + { + return trim($mime); + } + else + { + return trim(substr($mime, 0, $pos)); + } + } + + function htmlspecialchars_decode($string, $quote_style) + { + if (function_exists('htmlspecialchars_decode')) + { + return htmlspecialchars_decode($string, $quote_style); + } + else + { + return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style))); + } + } + + function atom_03_construct_type($attribs) + { + if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) == 'base64')) + { + $mode = SIMPLEPIE_CONSTRUCT_BASE64; + } + else + { + $mode = SIMPLEPIE_CONSTRUCT_NONE; + } + if (isset($attribs['']['type'])) + { + switch (strtolower(trim($attribs['']['type']))) + { + case 'text': + case 'text/plain': + return SIMPLEPIE_CONSTRUCT_TEXT | $mode; + + case 'html': + case 'text/html': + return SIMPLEPIE_CONSTRUCT_HTML | $mode; + + case 'xhtml': + case 'application/xhtml+xml': + return SIMPLEPIE_CONSTRUCT_XHTML | $mode; + + default: + return SIMPLEPIE_CONSTRUCT_NONE | $mode; + } + } + else + { + return SIMPLEPIE_CONSTRUCT_TEXT | $mode; + } + } + + function atom_10_construct_type($attribs) + { + if (isset($attribs['']['type'])) + { + switch (strtolower(trim($attribs['']['type']))) + { + case 'text': + return SIMPLEPIE_CONSTRUCT_TEXT; + + case 'html': + return SIMPLEPIE_CONSTRUCT_HTML; + + case 'xhtml': + return SIMPLEPIE_CONSTRUCT_XHTML; + + default: + return SIMPLEPIE_CONSTRUCT_NONE; + } + } + return SIMPLEPIE_CONSTRUCT_TEXT; + } + + function atom_10_content_construct_type($attribs) + { + if (isset($attribs['']['type'])) + { + $type = strtolower(trim($attribs['']['type'])); + switch ($type) + { + case 'text': + return SIMPLEPIE_CONSTRUCT_TEXT; + + case 'html': + return SIMPLEPIE_CONSTRUCT_HTML; + + case 'xhtml': + return SIMPLEPIE_CONSTRUCT_XHTML; + } + if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) == 'text/') + { + return SIMPLEPIE_CONSTRUCT_NONE; + } + else + { + return SIMPLEPIE_CONSTRUCT_BASE64; + } + } + else + { + return SIMPLEPIE_CONSTRUCT_TEXT; + } + } + + function is_isegment_nz_nc($string) + { + return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string); + } + + function space_separated_tokens($string) + { + $space_characters = "\x20\x09\x0A\x0B\x0C\x0D"; + $string_length = strlen($string); + + $position = strspn($string, $space_characters); + $tokens = array(); + + while ($position < $string_length) + { + $len = strcspn($string, $space_characters, $position); + $tokens[] = substr($string, $position, $len); + $position += $len; + $position += strspn($string, $space_characters, $position); + } + + return $tokens; + } + + function array_unique($array) + { + if (version_compare(PHP_VERSION, '5.2', '>=')) + { + return array_unique($array); + } + else + { + $array = (array) $array; + $new_array = array(); + $new_array_strings = array(); + foreach ($array as $key => $value) + { + if (is_object($value)) + { + if (method_exists($value, '__toString')) + { + $cmp = $value->__toString(); + } + else + { + trigger_error('Object of class ' . get_class($value) . ' could not be converted to string', E_USER_ERROR); + } + } + elseif (is_array($value)) + { + $cmp = (string) reset($value); + } + else + { + $cmp = (string) $value; + } + if (!in_array($cmp, $new_array_strings)) + { + $new_array[$key] = $value; + $new_array_strings[] = $cmp; + } + } + return $new_array; + } + } + + /** + * Converts a unicode codepoint to a UTF-8 character + * + * @static + * @access public + * @param int $codepoint Unicode codepoint + * @return string UTF-8 character + */ + function codepoint_to_utf8($codepoint) + { + static $cache = array(); + $codepoint = (int) $codepoint; + if (isset($cache[$codepoint])) + { + return $cache[$codepoint]; + } + elseif ($codepoint < 0) + { + return $cache[$codepoint] = false; + } + else if ($codepoint <= 0x7f) + { + return $cache[$codepoint] = chr($codepoint); + } + else if ($codepoint <= 0x7ff) + { + return $cache[$codepoint] = chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f)); + } + else if ($codepoint <= 0xffff) + { + return $cache[$codepoint] = chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); + } + else if ($codepoint <= 0x10ffff) + { + return $cache[$codepoint] = chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); + } + else + { + // U+FFFD REPLACEMENT CHARACTER + return $cache[$codepoint] = "\xEF\xBF\xBD"; + } + } + + /** + * Re-implementation of PHP 5's stripos() + * + * Returns the numeric position of the first occurrence of needle in the + * haystack string. + * + * @static + * @access string + * @param object $haystack + * @param string $needle Note that the needle may be a string of one or more + * characters. If needle is not a string, it is converted to an integer + * and applied as the ordinal value of a character. + * @param int $offset The optional offset parameter allows you to specify which + * character in haystack to start searching. The position returned is still + * relative to the beginning of haystack. + * @return bool If needle is not found, stripos() will return boolean false. + */ + function stripos($haystack, $needle, $offset = 0) + { + if (function_exists('stripos')) + { + return stripos($haystack, $needle, $offset); + } + else + { + if (is_string($needle)) + { + $needle = strtolower($needle); + } + elseif (is_int($needle) || is_bool($needle) || is_double($needle)) + { + $needle = strtolower(chr($needle)); + } + else + { + trigger_error('needle is not a string or an integer', E_USER_WARNING); + return false; + } + + return strpos(strtolower($haystack), $needle, $offset); + } + } + + /** + * Similar to parse_str() + * + * Returns an associative array of name/value pairs, where the value is an + * array of values that have used the same name + * + * @static + * @access string + * @param string $str The input string. + * @return array + */ + function parse_str($str) + { + $return = array(); + $str = explode('&', $str); + + foreach ($str as $section) + { + if (strpos($section, '=') !== false) + { + list($name, $value) = explode('=', $section, 2); + $return[urldecode($name)][] = urldecode($value); + } + else + { + $return[urldecode($section)][] = null; + } + } + + return $return; + } + + /** + * Detect XML encoding, as per XML 1.0 Appendix F.1 + * + * @todo Add support for EBCDIC + * @param string $data XML data + * @return array Possible encodings + */ + function xml_encoding($data) + { + // UTF-32 Big Endian BOM + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") + { + $encoding[] = 'UTF-32BE'; + } + // UTF-32 Little Endian BOM + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") + { + $encoding[] = 'UTF-32LE'; + } + // UTF-16 Big Endian BOM + elseif (substr($data, 0, 2) === "\xFE\xFF") + { + $encoding[] = 'UTF-16BE'; + } + // UTF-16 Little Endian BOM + elseif (substr($data, 0, 2) === "\xFF\xFE") + { + $encoding[] = 'UTF-16LE'; + } + // UTF-8 BOM + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") + { + $encoding[] = 'UTF-8'; + } + // UTF-32 Big Endian Without BOM + elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C") + { + if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E")) + { + $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-32BE'; + } + // UTF-32 Little Endian Without BOM + elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00") + { + if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00")) + { + $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-32LE'; + } + // UTF-16 Big Endian Without BOM + elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C") + { + if ($pos = strpos($data, "\x00\x3F\x00\x3E")) + { + $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-16BE'; + } + // UTF-16 Little Endian Without BOM + elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00") + { + if ($pos = strpos($data, "\x3F\x00\x3E\x00")) + { + $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-16LE'; + } + // US-ASCII (or superset) + elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C") + { + if ($pos = strpos($data, "\x3F\x3E")) + { + $parser = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-8'; + } + // Fallback to UTF-8 + else + { + $encoding[] = 'UTF-8'; + } + return $encoding; + } +} + +/** + * Decode HTML Entities + * + * This implements HTML5 as of revision 967 (2007-06-28) + * + * @package SimplePie + */ +class SimplePie_Decode_HTML_Entities +{ + /** + * Data to be parsed + * + * @access private + * @var string + */ + var $data = ''; + + /** + * Currently consumed bytes + * + * @access private + * @var string + */ + var $consumed = ''; + + /** + * Position of the current byte being parsed + * + * @access private + * @var int + */ + var $position = 0; + + /** + * Create an instance of the class with the input data + * + * @access public + * @param string $data Input data + */ + function SimplePie_Decode_HTML_Entities($data) + { + $this->data = $data; + } + + /** + * Parse the input data + * + * @access public + * @return string Output data + */ + function parse() + { + while (($this->position = strpos($this->data, '&', $this->position)) !== false) + { + $this->consume(); + $this->entity(); + $this->consumed = ''; + } + return $this->data; + } + + /** + * Consume the next byte + * + * @access private + * @return mixed The next byte, or false, if there is no more data + */ + function consume() + { + if (isset($this->data[$this->position])) + { + $this->consumed .= $this->data[$this->position]; + return $this->data[$this->position++]; + } + else + { + $this->consumed = false; + return false; + } + } + + /** + * Consume a range of characters + * + * @access private + * @param string $chars Characters to consume + * @return mixed A series of characters that match the range, or false + */ + function consume_range($chars) + { + if ($len = strspn($this->data, $chars, $this->position)) + { + $data = substr($this->data, $this->position, $len); + $this->consumed .= $data; + $this->position += $len; + return $data; + } + else + { + $this->consumed = false; + return false; + } + } + + /** + * Unconsume one byte + * + * @access private + */ + function unconsume() + { + $this->consumed = substr($this->consumed, 0, -1); + $this->position--; + } + + /** + * Decode an entity + * + * @access private + */ + function entity() + { + switch ($this->consume()) + { + case "\x09": + case "\x0A": + case "\x0B": + case "\x0C": + case "\x20": + case "\x3C": + case "\x26": + case false: + break; + + case "\x23": + switch ($this->consume()) + { + case "\x78": + case "\x58": + $range = '0123456789ABCDEFabcdef'; + $hex = true; + break; + + default: + $range = '0123456789'; + $hex = false; + $this->unconsume(); + break; + } + + if ($codepoint = $this->consume_range($range)) + { + static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8"); + + if ($hex) + { + $codepoint = hexdec($codepoint); + } + else + { + $codepoint = intval($codepoint); + } + + if (isset($windows_1252_specials[$codepoint])) + { + $replacement = $windows_1252_specials[$codepoint]; + } + else + { + $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint); + } + + if ($this->consume() != ';') + { + $this->unconsume(); + } + + $consumed_length = strlen($this->consumed); + $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length); + $this->position += strlen($replacement) - $consumed_length; + } + break; + + default: + static $entities = array('Aacute' => "\xC3\x81", 'aacute' => "\xC3\xA1", 'Aacute;' => "\xC3\x81", 'aacute;' => "\xC3\xA1", 'Acirc' => "\xC3\x82", 'acirc' => "\xC3\xA2", 'Acirc;' => "\xC3\x82", 'acirc;' => "\xC3\xA2", 'acute' => "\xC2\xB4", 'acute;' => "\xC2\xB4", 'AElig' => "\xC3\x86", 'aelig' => "\xC3\xA6", 'AElig;' => "\xC3\x86", 'aelig;' => "\xC3\xA6", 'Agrave' => "\xC3\x80", 'agrave' => "\xC3\xA0", 'Agrave;' => "\xC3\x80", 'agrave;' => "\xC3\xA0", 'alefsym;' => "\xE2\x84\xB5", 'Alpha;' => "\xCE\x91", 'alpha;' => "\xCE\xB1", 'AMP' => "\x26", 'amp' => "\x26", 'AMP;' => "\x26", 'amp;' => "\x26", 'and;' => "\xE2\x88\xA7", 'ang;' => "\xE2\x88\xA0", 'apos;' => "\x27", 'Aring' => "\xC3\x85", 'aring' => "\xC3\xA5", 'Aring;' => "\xC3\x85", 'aring;' => "\xC3\xA5", 'asymp;' => "\xE2\x89\x88", 'Atilde' => "\xC3\x83", 'atilde' => "\xC3\xA3", 'Atilde;' => "\xC3\x83", 'atilde;' => "\xC3\xA3", 'Auml' => "\xC3\x84", 'auml' => "\xC3\xA4", 'Auml;' => "\xC3\x84", 'auml;' => "\xC3\xA4", 'bdquo;' => "\xE2\x80\x9E", 'Beta;' => "\xCE\x92", 'beta;' => "\xCE\xB2", 'brvbar' => "\xC2\xA6", 'brvbar;' => "\xC2\xA6", 'bull;' => "\xE2\x80\xA2", 'cap;' => "\xE2\x88\xA9", 'Ccedil' => "\xC3\x87", 'ccedil' => "\xC3\xA7", 'Ccedil;' => "\xC3\x87", 'ccedil;' => "\xC3\xA7", 'cedil' => "\xC2\xB8", 'cedil;' => "\xC2\xB8", 'cent' => "\xC2\xA2", 'cent;' => "\xC2\xA2", 'Chi;' => "\xCE\xA7", 'chi;' => "\xCF\x87", 'circ;' => "\xCB\x86", 'clubs;' => "\xE2\x99\xA3", 'cong;' => "\xE2\x89\x85", 'COPY' => "\xC2\xA9", 'copy' => "\xC2\xA9", 'COPY;' => "\xC2\xA9", 'copy;' => "\xC2\xA9", 'crarr;' => "\xE2\x86\xB5", 'cup;' => "\xE2\x88\xAA", 'curren' => "\xC2\xA4", 'curren;' => "\xC2\xA4", 'Dagger;' => "\xE2\x80\xA1", 'dagger;' => "\xE2\x80\xA0", 'dArr;' => "\xE2\x87\x93", 'darr;' => "\xE2\x86\x93", 'deg' => "\xC2\xB0", 'deg;' => "\xC2\xB0", 'Delta;' => "\xCE\x94", 'delta;' => "\xCE\xB4", 'diams;' => "\xE2\x99\xA6", 'divide' => "\xC3\xB7", 'divide;' => "\xC3\xB7", 'Eacute' => "\xC3\x89", 'eacute' => "\xC3\xA9", 'Eacute;' => "\xC3\x89", 'eacute;' => "\xC3\xA9", 'Ecirc' => "\xC3\x8A", 'ecirc' => "\xC3\xAA", 'Ecirc;' => "\xC3\x8A", 'ecirc;' => "\xC3\xAA", 'Egrave' => "\xC3\x88", 'egrave' => "\xC3\xA8", 'Egrave;' => "\xC3\x88", 'egrave;' => "\xC3\xA8", 'empty;' => "\xE2\x88\x85", 'emsp;' => "\xE2\x80\x83", 'ensp;' => "\xE2\x80\x82", 'Epsilon;' => "\xCE\x95", 'epsilon;' => "\xCE\xB5", 'equiv;' => "\xE2\x89\xA1", 'Eta;' => "\xCE\x97", 'eta;' => "\xCE\xB7", 'ETH' => "\xC3\x90", 'eth' => "\xC3\xB0", 'ETH;' => "\xC3\x90", 'eth;' => "\xC3\xB0", 'Euml' => "\xC3\x8B", 'euml' => "\xC3\xAB", 'Euml;' => "\xC3\x8B", 'euml;' => "\xC3\xAB", 'euro;' => "\xE2\x82\xAC", 'exist;' => "\xE2\x88\x83", 'fnof;' => "\xC6\x92", 'forall;' => "\xE2\x88\x80", 'frac12' => "\xC2\xBD", 'frac12;' => "\xC2\xBD", 'frac14' => "\xC2\xBC", 'frac14;' => "\xC2\xBC", 'frac34' => "\xC2\xBE", 'frac34;' => "\xC2\xBE", 'frasl;' => "\xE2\x81\x84", 'Gamma;' => "\xCE\x93", 'gamma;' => "\xCE\xB3", 'ge;' => "\xE2\x89\xA5", 'GT' => "\x3E", 'gt' => "\x3E", 'GT;' => "\x3E", 'gt;' => "\x3E", 'hArr;' => "\xE2\x87\x94", 'harr;' => "\xE2\x86\x94", 'hearts;' => "\xE2\x99\xA5", 'hellip;' => "\xE2\x80\xA6", 'Iacute' => "\xC3\x8D", 'iacute' => "\xC3\xAD", 'Iacute;' => "\xC3\x8D", 'iacute;' => "\xC3\xAD", 'Icirc' => "\xC3\x8E", 'icirc' => "\xC3\xAE", 'Icirc;' => "\xC3\x8E", 'icirc;' => "\xC3\xAE", 'iexcl' => "\xC2\xA1", 'iexcl;' => "\xC2\xA1", 'Igrave' => "\xC3\x8C", 'igrave' => "\xC3\xAC", 'Igrave;' => "\xC3\x8C", 'igrave;' => "\xC3\xAC", 'image;' => "\xE2\x84\x91", 'infin;' => "\xE2\x88\x9E", 'int;' => "\xE2\x88\xAB", 'Iota;' => "\xCE\x99", 'iota;' => "\xCE\xB9", 'iquest' => "\xC2\xBF", 'iquest;' => "\xC2\xBF", 'isin;' => "\xE2\x88\x88", 'Iuml' => "\xC3\x8F", 'iuml' => "\xC3\xAF", 'Iuml;' => "\xC3\x8F", 'iuml;' => "\xC3\xAF", 'Kappa;' => "\xCE\x9A", 'kappa;' => "\xCE\xBA", 'Lambda;' => "\xCE\x9B", 'lambda;' => "\xCE\xBB", 'lang;' => "\xE3\x80\x88", 'laquo' => "\xC2\xAB", 'laquo;' => "\xC2\xAB", 'lArr;' => "\xE2\x87\x90", 'larr;' => "\xE2\x86\x90", 'lceil;' => "\xE2\x8C\x88", 'ldquo;' => "\xE2\x80\x9C", 'le;' => "\xE2\x89\xA4", 'lfloor;' => "\xE2\x8C\x8A", 'lowast;' => "\xE2\x88\x97", 'loz;' => "\xE2\x97\x8A", 'lrm;' => "\xE2\x80\x8E", 'lsaquo;' => "\xE2\x80\xB9", 'lsquo;' => "\xE2\x80\x98", 'LT' => "\x3C", 'lt' => "\x3C", 'LT;' => "\x3C", 'lt;' => "\x3C", 'macr' => "\xC2\xAF", 'macr;' => "\xC2\xAF", 'mdash;' => "\xE2\x80\x94", 'micro' => "\xC2\xB5", 'micro;' => "\xC2\xB5", 'middot' => "\xC2\xB7", 'middot;' => "\xC2\xB7", 'minus;' => "\xE2\x88\x92", 'Mu;' => "\xCE\x9C", 'mu;' => "\xCE\xBC", 'nabla;' => "\xE2\x88\x87", 'nbsp' => "\xC2\xA0", 'nbsp;' => "\xC2\xA0", 'ndash;' => "\xE2\x80\x93", 'ne;' => "\xE2\x89\xA0", 'ni;' => "\xE2\x88\x8B", 'not' => "\xC2\xAC", 'not;' => "\xC2\xAC", 'notin;' => "\xE2\x88\x89", 'nsub;' => "\xE2\x8A\x84", 'Ntilde' => "\xC3\x91", 'ntilde' => "\xC3\xB1", 'Ntilde;' => "\xC3\x91", 'ntilde;' => "\xC3\xB1", 'Nu;' => "\xCE\x9D", 'nu;' => "\xCE\xBD", 'Oacute' => "\xC3\x93", 'oacute' => "\xC3\xB3", 'Oacute;' => "\xC3\x93", 'oacute;' => "\xC3\xB3", 'Ocirc' => "\xC3\x94", 'ocirc' => "\xC3\xB4", 'Ocirc;' => "\xC3\x94", 'ocirc;' => "\xC3\xB4", 'OElig;' => "\xC5\x92", 'oelig;' => "\xC5\x93", 'Ograve' => "\xC3\x92", 'ograve' => "\xC3\xB2", 'Ograve;' => "\xC3\x92", 'ograve;' => "\xC3\xB2", 'oline;' => "\xE2\x80\xBE", 'Omega;' => "\xCE\xA9", 'omega;' => "\xCF\x89", 'Omicron;' => "\xCE\x9F", 'omicron;' => "\xCE\xBF", 'oplus;' => "\xE2\x8A\x95", 'or;' => "\xE2\x88\xA8", 'ordf' => "\xC2\xAA", 'ordf;' => "\xC2\xAA", 'ordm' => "\xC2\xBA", 'ordm;' => "\xC2\xBA", 'Oslash' => "\xC3\x98", 'oslash' => "\xC3\xB8", 'Oslash;' => "\xC3\x98", 'oslash;' => "\xC3\xB8", 'Otilde' => "\xC3\x95", 'otilde' => "\xC3\xB5", 'Otilde;' => "\xC3\x95", 'otilde;' => "\xC3\xB5", 'otimes;' => "\xE2\x8A\x97", 'Ouml' => "\xC3\x96", 'ouml' => "\xC3\xB6", 'Ouml;' => "\xC3\x96", 'ouml;' => "\xC3\xB6", 'para' => "\xC2\xB6", 'para;' => "\xC2\xB6", 'part;' => "\xE2\x88\x82", 'permil;' => "\xE2\x80\xB0", 'perp;' => "\xE2\x8A\xA5", 'Phi;' => "\xCE\xA6", 'phi;' => "\xCF\x86", 'Pi;' => "\xCE\xA0", 'pi;' => "\xCF\x80", 'piv;' => "\xCF\x96", 'plusmn' => "\xC2\xB1", 'plusmn;' => "\xC2\xB1", 'pound' => "\xC2\xA3", 'pound;' => "\xC2\xA3", 'Prime;' => "\xE2\x80\xB3", 'prime;' => "\xE2\x80\xB2", 'prod;' => "\xE2\x88\x8F", 'prop;' => "\xE2\x88\x9D", 'Psi;' => "\xCE\xA8", 'psi;' => "\xCF\x88", 'QUOT' => "\x22", 'quot' => "\x22", 'QUOT;' => "\x22", 'quot;' => "\x22", 'radic;' => "\xE2\x88\x9A", 'rang;' => "\xE3\x80\x89", 'raquo' => "\xC2\xBB", 'raquo;' => "\xC2\xBB", 'rArr;' => "\xE2\x87\x92", 'rarr;' => "\xE2\x86\x92", 'rceil;' => "\xE2\x8C\x89", 'rdquo;' => "\xE2\x80\x9D", 'real;' => "\xE2\x84\x9C", 'REG' => "\xC2\xAE", 'reg' => "\xC2\xAE", 'REG;' => "\xC2\xAE", 'reg;' => "\xC2\xAE", 'rfloor;' => "\xE2\x8C\x8B", 'Rho;' => "\xCE\xA1", 'rho;' => "\xCF\x81", 'rlm;' => "\xE2\x80\x8F", 'rsaquo;' => "\xE2\x80\xBA", 'rsquo;' => "\xE2\x80\x99", 'sbquo;' => "\xE2\x80\x9A", 'Scaron;' => "\xC5\xA0", 'scaron;' => "\xC5\xA1", 'sdot;' => "\xE2\x8B\x85", 'sect' => "\xC2\xA7", 'sect;' => "\xC2\xA7", 'shy' => "\xC2\xAD", 'shy;' => "\xC2\xAD", 'Sigma;' => "\xCE\xA3", 'sigma;' => "\xCF\x83", 'sigmaf;' => "\xCF\x82", 'sim;' => "\xE2\x88\xBC", 'spades;' => "\xE2\x99\xA0", 'sub;' => "\xE2\x8A\x82", 'sube;' => "\xE2\x8A\x86", 'sum;' => "\xE2\x88\x91", 'sup;' => "\xE2\x8A\x83", 'sup1' => "\xC2\xB9", 'sup1;' => "\xC2\xB9", 'sup2' => "\xC2\xB2", 'sup2;' => "\xC2\xB2", 'sup3' => "\xC2\xB3", 'sup3;' => "\xC2\xB3", 'supe;' => "\xE2\x8A\x87", 'szlig' => "\xC3\x9F", 'szlig;' => "\xC3\x9F", 'Tau;' => "\xCE\xA4", 'tau;' => "\xCF\x84", 'there4;' => "\xE2\x88\xB4", 'Theta;' => "\xCE\x98", 'theta;' => "\xCE\xB8", 'thetasym;' => "\xCF\x91", 'thinsp;' => "\xE2\x80\x89", 'THORN' => "\xC3\x9E", 'thorn' => "\xC3\xBE", 'THORN;' => "\xC3\x9E", 'thorn;' => "\xC3\xBE", 'tilde;' => "\xCB\x9C", 'times' => "\xC3\x97", 'times;' => "\xC3\x97", 'TRADE;' => "\xE2\x84\xA2", 'trade;' => "\xE2\x84\xA2", 'Uacute' => "\xC3\x9A", 'uacute' => "\xC3\xBA", 'Uacute;' => "\xC3\x9A", 'uacute;' => "\xC3\xBA", 'uArr;' => "\xE2\x87\x91", 'uarr;' => "\xE2\x86\x91", 'Ucirc' => "\xC3\x9B", 'ucirc' => "\xC3\xBB", 'Ucirc;' => "\xC3\x9B", 'ucirc;' => "\xC3\xBB", 'Ugrave' => "\xC3\x99", 'ugrave' => "\xC3\xB9", 'Ugrave;' => "\xC3\x99", 'ugrave;' => "\xC3\xB9", 'uml' => "\xC2\xA8", 'uml;' => "\xC2\xA8", 'upsih;' => "\xCF\x92", 'Upsilon;' => "\xCE\xA5", 'upsilon;' => "\xCF\x85", 'Uuml' => "\xC3\x9C", 'uuml' => "\xC3\xBC", 'Uuml;' => "\xC3\x9C", 'uuml;' => "\xC3\xBC", 'weierp;' => "\xE2\x84\x98", 'Xi;' => "\xCE\x9E", 'xi;' => "\xCE\xBE", 'Yacute' => "\xC3\x9D", 'yacute' => "\xC3\xBD", 'Yacute;' => "\xC3\x9D", 'yacute;' => "\xC3\xBD", 'yen' => "\xC2\xA5", 'yen;' => "\xC2\xA5", 'yuml' => "\xC3\xBF", 'Yuml;' => "\xC5\xB8", 'yuml;' => "\xC3\xBF", 'Zeta;' => "\xCE\x96", 'zeta;' => "\xCE\xB6", 'zwj;' => "\xE2\x80\x8D", 'zwnj;' => "\xE2\x80\x8C"); + + for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++) + { + $consumed = substr($this->consumed, 1); + if (isset($entities[$consumed])) + { + $match = $consumed; + } + } + + if ($match !== null) + { + $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1); + $this->position += strlen($entities[$match]) - strlen($consumed) - 1; + } + break; + } + } +} + +/** + * Date Parser + * + * @package SimplePie + */ +class SimplePie_Parse_Date +{ + /** + * Input data + * + * @access protected + * @var string + */ + var $date; + + /** + * List of days, calendar day name => ordinal day number in the week + * + * @access protected + * @var array + */ + var $day = array( + // English + 'mon' => 1, + 'monday' => 1, + 'tue' => 2, + 'tuesday' => 2, + 'wed' => 3, + 'wednesday' => 3, + 'thu' => 4, + 'thursday' => 4, + 'fri' => 5, + 'friday' => 5, + 'sat' => 6, + 'saturday' => 6, + 'sun' => 7, + 'sunday' => 7, + // Dutch + 'maandag' => 1, + 'dinsdag' => 2, + 'woensdag' => 3, + 'donderdag' => 4, + 'vrijdag' => 5, + 'zaterdag' => 6, + 'zondag' => 7, + // French + 'lundi' => 1, + 'mardi' => 2, + 'mercredi' => 3, + 'jeudi' => 4, + 'vendredi' => 5, + 'samedi' => 6, + 'dimanche' => 7, + // German + 'montag' => 1, + 'dienstag' => 2, + 'mittwoch' => 3, + 'donnerstag' => 4, + 'freitag' => 5, + 'samstag' => 6, + 'sonnabend' => 6, + 'sonntag' => 7, + // Italian + 'lunedì' => 1, + 'martedì' => 2, + 'mercoledì' => 3, + 'giovedì' => 4, + 'venerdì' => 5, + 'sabato' => 6, + 'domenica' => 7, + // Spanish + 'lunes' => 1, + 'martes' => 2, + 'miércoles' => 3, + 'jueves' => 4, + 'viernes' => 5, + 'sábado' => 6, + 'domingo' => 7, + // Finnish + 'maanantai' => 1, + 'tiistai' => 2, + 'keskiviikko' => 3, + 'torstai' => 4, + 'perjantai' => 5, + 'lauantai' => 6, + 'sunnuntai' => 7, + // Hungarian + 'hétfő' => 1, + 'kedd' => 2, + 'szerda' => 3, + 'csütörtok' => 4, + 'péntek' => 5, + 'szombat' => 6, + 'vasárnap' => 7, + // Greek + 'Δευ' => 1, + 'Τρι' => 2, + 'Τετ' => 3, + 'Πεμ' => 4, + 'Παρ' => 5, + 'Σαβ' => 6, + 'Κυρ' => 7, + ); + + /** + * List of months, calendar month name => calendar month number + * + * @access protected + * @var array + */ + var $month = array( + // English + 'jan' => 1, + 'january' => 1, + 'feb' => 2, + 'february' => 2, + 'mar' => 3, + 'march' => 3, + 'apr' => 4, + 'april' => 4, + 'may' => 5, + // No long form of May + 'jun' => 6, + 'june' => 6, + 'jul' => 7, + 'july' => 7, + 'aug' => 8, + 'august' => 8, + 'sep' => 9, + 'september' => 8, + 'oct' => 10, + 'october' => 10, + 'nov' => 11, + 'november' => 11, + 'dec' => 12, + 'december' => 12, + // Dutch + 'januari' => 1, + 'februari' => 2, + 'maart' => 3, + 'april' => 4, + 'mei' => 5, + 'juni' => 6, + 'juli' => 7, + 'augustus' => 8, + 'september' => 9, + 'oktober' => 10, + 'november' => 11, + 'december' => 12, + // French + 'janvier' => 1, + 'février' => 2, + 'mars' => 3, + 'avril' => 4, + 'mai' => 5, + 'juin' => 6, + 'juillet' => 7, + 'août' => 8, + 'septembre' => 9, + 'octobre' => 10, + 'novembre' => 11, + 'décembre' => 12, + // German + 'januar' => 1, + 'februar' => 2, + 'märz' => 3, + 'april' => 4, + 'mai' => 5, + 'juni' => 6, + 'juli' => 7, + 'august' => 8, + 'september' => 9, + 'oktober' => 10, + 'november' => 11, + 'dezember' => 12, + // Italian + 'gennaio' => 1, + 'febbraio' => 2, + 'marzo' => 3, + 'aprile' => 4, + 'maggio' => 5, + 'giugno' => 6, + 'luglio' => 7, + 'agosto' => 8, + 'settembre' => 9, + 'ottobre' => 10, + 'novembre' => 11, + 'dicembre' => 12, + // Spanish + 'enero' => 1, + 'febrero' => 2, + 'marzo' => 3, + 'abril' => 4, + 'mayo' => 5, + 'junio' => 6, + 'julio' => 7, + 'agosto' => 8, + 'septiembre' => 9, + 'setiembre' => 9, + 'octubre' => 10, + 'noviembre' => 11, + 'diciembre' => 12, + // Finnish + 'tammikuu' => 1, + 'helmikuu' => 2, + 'maaliskuu' => 3, + 'huhtikuu' => 4, + 'toukokuu' => 5, + 'kesäkuu' => 6, + 'heinäkuu' => 7, + 'elokuu' => 8, + 'suuskuu' => 9, + 'lokakuu' => 10, + 'marras' => 11, + 'joulukuu' => 12, + // Hungarian + 'január' => 1, + 'február' => 2, + 'március' => 3, + 'április' => 4, + 'május' => 5, + 'június' => 6, + 'július' => 7, + 'augusztus' => 8, + 'szeptember' => 9, + 'október' => 10, + 'november' => 11, + 'december' => 12, + // Greek + 'Ιαν' => 1, + 'Φεβ' => 2, + 'Μάώ' => 3, + 'Μαώ' => 3, + 'Απρ' => 4, + 'Μάι' => 5, + 'Μαϊ' => 5, + 'Μαι' => 5, + 'Ιούν' => 6, + 'Ιον' => 6, + 'Ιούλ' => 7, + 'Ιολ' => 7, + 'Αύγ' => 8, + 'Αυγ' => 8, + 'Σεπ' => 9, + 'Οκτ' => 10, + 'Νοέ' => 11, + 'Δεκ' => 12, + ); + + /** + * List of timezones, abbreviation => offset from UTC + * + * @access protected + * @var array + */ + var $timezone = array( + 'ACDT' => 37800, + 'ACIT' => 28800, + 'ACST' => 34200, + 'ACT' => -18000, + 'ACWDT' => 35100, + 'ACWST' => 31500, + 'AEDT' => 39600, + 'AEST' => 36000, + 'AFT' => 16200, + 'AKDT' => -28800, + 'AKST' => -32400, + 'AMDT' => 18000, + 'AMT' => -14400, + 'ANAST' => 46800, + 'ANAT' => 43200, + 'ART' => -10800, + 'AZOST' => -3600, + 'AZST' => 18000, + 'AZT' => 14400, + 'BIOT' => 21600, + 'BIT' => -43200, + 'BOT' => -14400, + 'BRST' => -7200, + 'BRT' => -10800, + 'BST' => 3600, + 'BTT' => 21600, + 'CAST' => 18000, + 'CAT' => 7200, + 'CCT' => 23400, + 'CDT' => -18000, + 'CEDT' => 7200, + 'CET' => 3600, + 'CGST' => -7200, + 'CGT' => -10800, + 'CHADT' => 49500, + 'CHAST' => 45900, + 'CIST' => -28800, + 'CKT' => -36000, + 'CLDT' => -10800, + 'CLST' => -14400, + 'COT' => -18000, + 'CST' => -21600, + 'CVT' => -3600, + 'CXT' => 25200, + 'DAVT' => 25200, + 'DTAT' => 36000, + 'EADT' => -18000, + 'EAST' => -21600, + 'EAT' => 10800, + 'ECT' => -18000, + 'EDT' => -14400, + 'EEST' => 10800, + 'EET' => 7200, + 'EGT' => -3600, + 'EKST' => 21600, + 'EST' => -18000, + 'FJT' => 43200, + 'FKDT' => -10800, + 'FKST' => -14400, + 'FNT' => -7200, + 'GALT' => -21600, + 'GEDT' => 14400, + 'GEST' => 10800, + 'GFT' => -10800, + 'GILT' => 43200, + 'GIT' => -32400, + 'GST' => 14400, + 'GST' => -7200, + 'GYT' => -14400, + 'HAA' => -10800, + 'HAC' => -18000, + 'HADT' => -32400, + 'HAE' => -14400, + 'HAP' => -25200, + 'HAR' => -21600, + 'HAST' => -36000, + 'HAT' => -9000, + 'HAY' => -28800, + 'HKST' => 28800, + 'HMT' => 18000, + 'HNA' => -14400, + 'HNC' => -21600, + 'HNE' => -18000, + 'HNP' => -28800, + 'HNR' => -25200, + 'HNT' => -12600, + 'HNY' => -32400, + 'IRDT' => 16200, + 'IRKST' => 32400, + 'IRKT' => 28800, + 'IRST' => 12600, + 'JFDT' => -10800, + 'JFST' => -14400, + 'JST' => 32400, + 'KGST' => 21600, + 'KGT' => 18000, + 'KOST' => 39600, + 'KOVST' => 28800, + 'KOVT' => 25200, + 'KRAST' => 28800, + 'KRAT' => 25200, + 'KST' => 32400, + 'LHDT' => 39600, + 'LHST' => 37800, + 'LINT' => 50400, + 'LKT' => 21600, + 'MAGST' => 43200, + 'MAGT' => 39600, + 'MAWT' => 21600, + 'MDT' => -21600, + 'MESZ' => 7200, + 'MEZ' => 3600, + 'MHT' => 43200, + 'MIT' => -34200, + 'MNST' => 32400, + 'MSDT' => 14400, + 'MSST' => 10800, + 'MST' => -25200, + 'MUT' => 14400, + 'MVT' => 18000, + 'MYT' => 28800, + 'NCT' => 39600, + 'NDT' => -9000, + 'NFT' => 41400, + 'NMIT' => 36000, + 'NOVST' => 25200, + 'NOVT' => 21600, + 'NPT' => 20700, + 'NRT' => 43200, + 'NST' => -12600, + 'NUT' => -39600, + 'NZDT' => 46800, + 'NZST' => 43200, + 'OMSST' => 25200, + 'OMST' => 21600, + 'PDT' => -25200, + 'PET' => -18000, + 'PETST' => 46800, + 'PETT' => 43200, + 'PGT' => 36000, + 'PHOT' => 46800, + 'PHT' => 28800, + 'PKT' => 18000, + 'PMDT' => -7200, + 'PMST' => -10800, + 'PONT' => 39600, + 'PST' => -28800, + 'PWT' => 32400, + 'PYST' => -10800, + 'PYT' => -14400, + 'RET' => 14400, + 'ROTT' => -10800, + 'SAMST' => 18000, + 'SAMT' => 14400, + 'SAST' => 7200, + 'SBT' => 39600, + 'SCDT' => 46800, + 'SCST' => 43200, + 'SCT' => 14400, + 'SEST' => 3600, + 'SGT' => 28800, + 'SIT' => 28800, + 'SRT' => -10800, + 'SST' => -39600, + 'SYST' => 10800, + 'SYT' => 7200, + 'TFT' => 18000, + 'THAT' => -36000, + 'TJT' => 18000, + 'TKT' => -36000, + 'TMT' => 18000, + 'TOT' => 46800, + 'TPT' => 32400, + 'TRUT' => 36000, + 'TVT' => 43200, + 'TWT' => 28800, + 'UYST' => -7200, + 'UYT' => -10800, + 'UZT' => 18000, + 'VET' => -14400, + 'VLAST' => 39600, + 'VLAT' => 36000, + 'VOST' => 21600, + 'VUT' => 39600, + 'WAST' => 7200, + 'WAT' => 3600, + 'WDT' => 32400, + 'WEST' => 3600, + 'WFT' => 43200, + 'WIB' => 25200, + 'WIT' => 32400, + 'WITA' => 28800, + 'WKST' => 18000, + 'WST' => 28800, + 'YAKST' => 36000, + 'YAKT' => 32400, + 'YAPT' => 36000, + 'YEKST' => 21600, + 'YEKT' => 18000, + ); + + /** + * Cached PCRE for SimplePie_Parse_Date::$day + * + * @access protected + * @var string + */ + var $day_pcre; + + /** + * Cached PCRE for SimplePie_Parse_Date::$month + * + * @access protected + * @var string + */ + var $month_pcre; + + /** + * Array of user-added callback methods + * + * @access private + * @var array + */ + var $built_in = array(); + + /** + * Array of user-added callback methods + * + * @access private + * @var array + */ + var $user = array(); + + /** + * Create new SimplePie_Parse_Date object, and set self::day_pcre, + * self::month_pcre, and self::built_in + * + * @access private + */ + function SimplePie_Parse_Date() + { + $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')'; + $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')'; + + static $cache; + if (!isset($cache[get_class($this)])) + { + if (extension_loaded('Reflection')) + { + $class = new ReflectionClass(get_class($this)); + $methods = $class->getMethods(); + $all_methods = array(); + foreach ($methods as $method) + { + $all_methods[] = $method->getName(); + } + } + else + { + $all_methods = get_class_methods($this); + } + + foreach ($all_methods as $method) + { + if (strtolower(substr($method, 0, 5)) === 'date_') + { + $cache[get_class($this)][] = $method; + } + } + } + + foreach ($cache[get_class($this)] as $method) + { + $this->built_in[] = $method; + } + } + + /** + * Get the object + * + * @access public + */ + function get() + { + static $object; + if (!$object) + { + $object = new SimplePie_Parse_Date; + } + return $object; + } + + /** + * Parse a date + * + * @final + * @access public + * @param string $date Date to parse + * @return int Timestamp corresponding to date string, or false on failure + */ + function parse($date) + { + foreach ($this->user as $method) + { + if (($returned = call_user_func($method, $date)) !== false) + { + return $returned; + } + } + + foreach ($this->built_in as $method) + { + if (($returned = call_user_func(array(&$this, $method), $date)) !== false) + { + return $returned; + } + } + + return false; + } + + /** + * Add a callback method to parse a date + * + * @final + * @access public + * @param callback $callback + */ + function add_callback($callback) + { + if (is_callable($callback)) + { + $this->user[] = $callback; + } + else + { + trigger_error('User-supplied function must be a valid callback', E_USER_WARNING); + } + } + + /** + * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as + * well as allowing any of upper or lower case "T", horizontal tabs, or + * spaces to be used as the time separator (including more than one)) + * + * @access protected + * @return int Timestamp + */ + function date_w3cdtf($date) + { + static $pcre; + if (!$pcre) + { + $year = '([0-9]{4})'; + $month = $day = $hour = $minute = $second = '([0-9]{2})'; + $decimal = '([0-9]*)'; + $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))'; + $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Year + 2: Month + 3: Day + 4: Hour + 5: Minute + 6: Second + 7: Decimal fraction of a second + 8: Zulu + 9: Timezone ± + 10: Timezone hours + 11: Timezone minutes + */ + + // Fill in empty matches + for ($i = count($match); $i <= 3; $i++) + { + $match[$i] = '1'; + } + + for ($i = count($match); $i <= 7; $i++) + { + $match[$i] = '0'; + } + + // Numeric timezone + if (isset($match[9]) && $match[9] !== '') + { + $timezone = $match[10] * 3600; + $timezone += $match[11] * 60; + if ($match[9] === '-') + { + $timezone = 0 - $timezone; + } + } + else + { + $timezone = 0; + } + + // Convert the number of seconds to an integer, taking decimals into account + $second = round($match[6] + $match[7] / pow(10, strlen($match[7]))); + + return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone; + } + else + { + return false; + } + } + + /** + * Remove RFC822 comments + * + * @access protected + * @param string $data Data to strip comments from + * @return string Comment stripped string + */ + function remove_rfc2822_comments($string) + { + $string = (string) $string; + $position = 0; + $length = strlen($string); + $depth = 0; + + $output = ''; + + while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) + { + $output .= substr($string, $position, $pos - $position); + $position = $pos + 1; + if ($string[$pos - 1] !== '\\') + { + $depth++; + while ($depth && $position < $length) + { + $position += strcspn($string, '()', $position); + if ($string[$position - 1] === '\\') + { + $position++; + continue; + } + elseif (isset($string[$position])) + { + switch ($string[$position]) + { + case '(': + $depth++; + break; + + case ')': + $depth--; + break; + } + $position++; + } + else + { + break; + } + } + } + else + { + $output .= '('; + } + } + $output .= substr($string, $position); + + return $output; + } + + /** + * Parse RFC2822's date format + * + * @access protected + * @return int Timestamp + */ + function date_rfc2822($date) + { + static $pcre; + if (!$pcre) + { + $wsp = '[\x09\x20]'; + $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)'; + $optional_fws = $fws . '?'; + $day_name = $this->day_pcre; + $month = $this->month_pcre; + $day = '([0-9]{1,2})'; + $hour = $minute = $second = '([0-9]{2})'; + $year = '([0-9]{2,4})'; + $num_zone = '([+\-])([0-9]{2})([0-9]{2})'; + $character_zone = '([A-Z]{1,5})'; + $zone = '(?:' . $num_zone . '|' . $character_zone . ')'; + $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i'; + } + if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Day + 3: Month + 4: Year + 5: Hour + 6: Minute + 7: Second + 8: Timezone ± + 9: Timezone hours + 10: Timezone minutes + 11: Alphabetic timezone + */ + + // Find the month number + $month = $this->month[strtolower($match[3])]; + + // Numeric timezone + if ($match[8] !== '') + { + $timezone = $match[9] * 3600; + $timezone += $match[10] * 60; + if ($match[8] === '-') + { + $timezone = 0 - $timezone; + } + } + // Character timezone + elseif (isset($this->timezone[strtoupper($match[11])])) + { + $timezone = $this->timezone[strtoupper($match[11])]; + } + // Assume everything else to be -0000 + else + { + $timezone = 0; + } + + // Deal with 2/3 digit years + if ($match[4] < 50) + { + $match[4] += 2000; + } + elseif ($match[4] < 1000) + { + $match[4] += 1900; + } + + // Second is optional, if it is empty set it to zero + if ($match[7] !== '') + { + $second = $match[7]; + } + else + { + $second = 0; + } + + return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone; + } + else + { + return false; + } + } + + /** + * Parse RFC850's date format + * + * @access protected + * @return int Timestamp + */ + function date_rfc850($date) + { + static $pcre; + if (!$pcre) + { + $space = '[\x09\x20]+'; + $day_name = $this->day_pcre; + $month = $this->month_pcre; + $day = '([0-9]{1,2})'; + $year = $hour = $minute = $second = '([0-9]{2})'; + $zone = '([A-Z]{1,5})'; + $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Day + 3: Month + 4: Year + 5: Hour + 6: Minute + 7: Second + 8: Timezone + */ + + // Month + $month = $this->month[strtolower($match[3])]; + + // Character timezone + if (isset($this->timezone[strtoupper($match[8])])) + { + $timezone = $this->timezone[strtoupper($match[8])]; + } + // Assume everything else to be -0000 + else + { + $timezone = 0; + } + + // Deal with 2 digit year + if ($match[4] < 50) + { + $match[4] += 2000; + } + else + { + $match[4] += 1900; + } + + return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone; + } + else + { + return false; + } + } + + /** + * Parse C99's asctime()'s date format + * + * @access protected + * @return int Timestamp + */ + function date_asctime($date) + { + static $pcre; + if (!$pcre) + { + $space = '[\x09\x20]+'; + $wday_name = $this->day_pcre; + $mon_name = $this->month_pcre; + $day = '([0-9]{1,2})'; + $hour = $sec = $min = '([0-9]{2})'; + $year = '([0-9]{4})'; + $terminator = '\x0A?\x00?'; + $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Month + 3: Day + 4: Hour + 5: Minute + 6: Second + 7: Year + */ + + $month = $this->month[strtolower($match[2])]; + return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]); + } + else + { + return false; + } + } + + /** + * Parse dates using strtotime() + * + * @access protected + * @return int Timestamp + */ + function date_strtotime($date) + { + $strtotime = strtotime($date); + if ($strtotime === -1 || $strtotime === false) + { + return false; + } + else + { + return $strtotime; + } + } +} + +/** + * Content-type sniffing + * + * @package SimplePie + */ +class SimplePie_Content_Type_Sniffer +{ + /** + * File object + * + * @var SimplePie_File + * @access private + */ + var $file; + + /** + * Create an instance of the class with the input file + * + * @access public + * @param SimplePie_Content_Type_Sniffer $file Input file + */ + function SimplePie_Content_Type_Sniffer($file) + { + $this->file = $file; + } + + /** + * Get the Content-Type of the specified file + * + * @access public + * @return string Actual Content-Type + */ + function get_type() + { + if (isset($this->file->headers['content-type'])) + { + if (!isset($this->file->headers['content-encoding']) + && ($this->file->headers['content-type'] === 'text/plain' + || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1' + || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1')) + { + return $this->text_or_binary(); + } + + if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) + { + $official = substr($this->file->headers['content-type'], 0, $pos); + } + else + { + $official = $this->file->headers['content-type']; + } + $official = strtolower($official); + + if ($official === 'unknown/unknown' + || $official === 'application/unknown') + { + return $this->unknown(); + } + elseif (substr($official, -4) === '+xml' + || $official === 'text/xml' + || $official === 'application/xml') + { + return $official; + } + elseif (substr($official, 0, 6) === 'image/') + { + if ($return = $this->image()) + { + return $return; + } + else + { + return $official; + } + } + elseif ($official === 'text/html') + { + return $this->feed_or_html(); + } + else + { + return $official; + } + } + else + { + return $this->unknown(); + } + } + + /** + * Sniff text or binary + * + * @access private + * @return string Actual Content-Type + */ + function text_or_binary() + { + if (substr($this->file->body, 0, 2) === "\xFE\xFF" + || substr($this->file->body, 0, 2) === "\xFF\xFE" + || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF" + || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") + { + return 'text/plain'; + } + elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) + { + return 'application/octect-stream'; + } + else + { + return 'text/plain'; + } + } + + /** + * Sniff unknown + * + * @access private + * @return string Actual Content-Type + */ + function unknown() + { + $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20"); + if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html' + || strtolower(substr($this->file->body, $ws, 5)) === '<html' + || strtolower(substr($this->file->body, $ws, 7)) === '<script') + { + return 'text/html'; + } + elseif (substr($this->file->body, 0, 5) === '%PDF-') + { + return 'application/pdf'; + } + elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') + { + return 'application/postscript'; + } + elseif (substr($this->file->body, 0, 6) === 'GIF87a' + || substr($this->file->body, 0, 6) === 'GIF89a') + { + return 'image/gif'; + } + elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") + { + return 'image/png'; + } + elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") + { + return 'image/jpeg'; + } + elseif (substr($this->file->body, 0, 2) === "\x42\x4D") + { + return 'image/bmp'; + } + else + { + return $this->text_or_binary(); + } + } + + /** + * Sniff images + * + * @access private + * @return string Actual Content-Type + */ + function image() + { + if (substr($this->file->body, 0, 6) === 'GIF87a' + || substr($this->file->body, 0, 6) === 'GIF89a') + { + return 'image/gif'; + } + elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") + { + return 'image/png'; + } + elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") + { + return 'image/jpeg'; + } + elseif (substr($this->file->body, 0, 2) === "\x42\x4D") + { + return 'image/bmp'; + } + else + { + return false; + } + } + + /** + * Sniff HTML + * + * @access private + * @return string Actual Content-Type + */ + function feed_or_html() + { + $len = strlen($this->file->body); + $pos = strspn($this->file->body, "\x09\x0A\x0D\x20"); + + while ($pos < $len) + { + switch ($this->file->body[$pos]) + { + case "\x09": + case "\x0A": + case "\x0D": + case "\x20": + $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); + continue 2; + + case '<': + $pos++; + break; + + default: + return 'text/html'; + } + + if (substr($this->file->body, $pos, 3) === '!--') + { + $pos += 3; + if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) + { + $pos += 3; + } + else + { + return 'text/html'; + } + } + elseif (substr($this->file->body, $pos, 1) === '!') + { + if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) + { + $pos++; + } + else + { + return 'text/html'; + } + } + elseif (substr($this->file->body, $pos, 1) === '?') + { + if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) + { + $pos += 2; + } + else + { + return 'text/html'; + } + } + elseif (substr($this->file->body, $pos, 3) === 'rss' + || substr($this->file->body, $pos, 7) === 'rdf:RDF') + { + return 'application/rss+xml'; + } + elseif (substr($this->file->body, $pos, 4) === 'feed') + { + return 'application/atom+xml'; + } + else + { + return 'text/html'; + } + } + + return 'text/html'; + } +} + +/** + * Parses the XML Declaration + * + * @package SimplePie + */ +class SimplePie_XML_Declaration_Parser +{ + /** + * XML Version + * + * @access public + * @var string + */ + var $version = '1.0'; + + /** + * Encoding + * + * @access public + * @var string + */ + var $encoding = 'UTF-8'; + + /** + * Standalone + * + * @access public + * @var bool + */ + var $standalone = false; + + /** + * Current state of the state machine + * + * @access private + * @var string + */ + var $state = 'before_version_name'; + + /** + * Input data + * + * @access private + * @var string + */ + var $data = ''; + + /** + * Input data length (to avoid calling strlen() everytime this is needed) + * + * @access private + * @var int + */ + var $data_length = 0; + + /** + * Current position of the pointer + * + * @var int + * @access private + */ + var $position = 0; + + /** + * Create an instance of the class with the input data + * + * @access public + * @param string $data Input data + */ + function SimplePie_XML_Declaration_Parser($data) + { + $this->data = $data; + $this->data_length = strlen($this->data); + } + + /** + * Parse the input data + * + * @access public + * @return bool true on success, false on failure + */ + function parse() + { + while ($this->state && $this->state !== 'emit' && $this->has_data()) + { + $state = $this->state; + $this->$state(); + } + $this->data = ''; + if ($this->state === 'emit') + { + return true; + } + else + { + $this->version = ''; + $this->encoding = ''; + $this->standalone = ''; + return false; + } + } + + /** + * Check whether there is data beyond the pointer + * + * @access private + * @return bool true if there is further data, false if not + */ + function has_data() + { + return (bool) ($this->position < $this->data_length); + } + + /** + * Advance past any whitespace + * + * @return int Number of whitespace characters passed + */ + function skip_whitespace() + { + $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position); + $this->position += $whitespace; + return $whitespace; + } + + /** + * Read value + */ + function get_value() + { + $quote = substr($this->data, $this->position, 1); + if ($quote === '"' || $quote === "'") + { + $this->position++; + $len = strcspn($this->data, $quote, $this->position); + if ($this->has_data()) + { + $value = substr($this->data, $this->position, $len); + $this->position += $len + 1; + return $value; + } + } + return false; + } + + function before_version_name() + { + if ($this->skip_whitespace()) + { + $this->state = 'version_name'; + } + else + { + $this->state = false; + } + } + + function version_name() + { + if (substr($this->data, $this->position, 7) === 'version') + { + $this->position += 7; + $this->skip_whitespace(); + $this->state = 'version_equals'; + } + else + { + $this->state = false; + } + } + + function version_equals() + { + if (substr($this->data, $this->position, 1) === '=') + { + $this->position++; + $this->skip_whitespace(); + $this->state = 'version_value'; + } + else + { + $this->state = false; + } + } + + function version_value() + { + if ($this->version = $this->get_value()) + { + $this->skip_whitespace(); + if ($this->has_data()) + { + $this->state = 'encoding_name'; + } + else + { + $this->state = 'emit'; + } + } + else + { + $this->state = 'standalone_name'; + } + } + + function encoding_name() + { + if (substr($this->data, $this->position, 8) === 'encoding') + { + $this->position += 8; + $this->skip_whitespace(); + $this->state = 'encoding_equals'; + } + else + { + $this->state = false; + } + } + + function encoding_equals() + { + if (substr($this->data, $this->position, 1) === '=') + { + $this->position++; + $this->skip_whitespace(); + $this->state = 'encoding_value'; + } + else + { + $this->state = false; + } + } + + function encoding_value() + { + if ($this->encoding = $this->get_value()) + { + $this->skip_whitespace(); + if ($this->has_data()) + { + $this->state = 'standalone_name'; + } + else + { + $this->state = 'emit'; + } + } + else + { + $this->state = false; + } + } + + function standalone_name() + { + if (substr($this->data, $this->position, 10) === 'standalone') + { + $this->position += 10; + $this->skip_whitespace(); + $this->state = 'standalone_equals'; + } + else + { + $this->state = false; + } + } + + function standalone_equals() + { + if (substr($this->data, $this->position, 1) === '=') + { + $this->position++; + $this->skip_whitespace(); + $this->state = 'standalone_value'; + } + else + { + $this->state = false; + } + } + + function standalone_value() + { + if ($standalone = $this->get_value()) + { + switch ($standalone) + { + case 'yes': + $this->standalone = true; + break; + + case 'no': + $this->standalone = false; + break; + + default: + $this->state = false; + return; + } + + $this->skip_whitespace(); + if ($this->has_data()) + { + $this->state = false; + } + else + { + $this->state = 'emit'; + } + } + else + { + $this->state = false; + } + } +} + +class SimplePie_Locator +{ + var $useragent; + var $timeout; + var $file; + var $local = array(); + var $elsewhere = array(); + var $file_class = 'SimplePie_File'; + var $cached_entities = array(); + var $http_base; + var $base; + var $base_location = 0; + var $checked_feeds = 0; + var $max_checked_feeds = 10; + var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer'; + + function SimplePie_Locator(&$file, $timeout = 10, $useragent = null, $file_class = 'SimplePie_File', $max_checked_feeds = 10, $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer') + { + $this->file =& $file; + $this->file_class = $file_class; + $this->useragent = $useragent; + $this->timeout = $timeout; + $this->max_checked_feeds = $max_checked_feeds; + $this->content_type_sniffer_class = $content_type_sniffer_class; + } + + function find($type = SIMPLEPIE_LOCATOR_ALL) + { + if ($this->is_feed($this->file)) + { + return $this->file; + } + + if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) + { + $sniffer = new $this->content_type_sniffer_class($this->file); + if ($sniffer->get_type() !== 'text/html') + { + return null; + } + } + + if ($type & ~SIMPLEPIE_LOCATOR_NONE) + { + $this->get_base(); + } + + if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery()) + { + return $working; + } + + if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links()) + { + if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) + { + return $working; + } + + if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) + { + return $working; + } + + if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) + { + return $working; + } + + if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) + { + return $working; + } + } + return null; + } + + function is_feed(&$file) + { + if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) + { + $sniffer = new $this->content_type_sniffer_class($file); + $sniffed = $sniffer->get_type(); + if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml'))) + { + return true; + } + else + { + return false; + } + } + elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL) + { + return true; + } + else + { + return false; + } + } + + function get_base() + { + $this->http_base = $this->file->url; + $this->base = $this->http_base; + $elements = SimplePie_Misc::get_element('base', $this->file->body); + foreach ($elements as $element) + { + if ($element['attribs']['href']['data'] !== '') + { + $this->base = SimplePie_Misc::absolutize_url(trim($element['attribs']['href']['data']), $this->http_base); + $this->base_location = $element['offset']; + break; + } + } + } + + function autodiscovery() + { + $links = array_merge(SimplePie_Misc::get_element('link', $this->file->body), SimplePie_Misc::get_element('a', $this->file->body), SimplePie_Misc::get_element('area', $this->file->body)); + $done = array(); + foreach ($links as $link) + { + if ($this->checked_feeds == $this->max_checked_feeds) + { + break; + } + if (isset($link['attribs']['href']['data']) && isset($link['attribs']['rel']['data'])) + { + $rel = array_unique(SimplePie_Misc::space_separated_tokens(strtolower($link['attribs']['rel']['data']))); + + if ($this->base_location < $link['offset']) + { + $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base); + } + else + { + $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base); + } + + if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !empty($link['attribs']['type']['data']) && in_array(strtolower(SimplePie_Misc::parse_mime($link['attribs']['type']['data'])), array('application/rss+xml', 'application/atom+xml')))) + { + $this->checked_feeds++; + $feed =& new $this->file_class($href, $this->timeout, 5, null, $this->useragent); + if ($this->is_feed($feed)) + { + return $feed; + } + } + $done[] = $href; + } + } + return null; + } + + function get_links() + { + $links = SimplePie_Misc::get_element('a', $this->file->body); + foreach ($links as $link) + { + if (isset($link['attribs']['href']['data'])) + { + $href = trim($link['attribs']['href']['data']); + $parsed = SimplePie_Misc::parse_url($href); + if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme'])) + { + if ($this->base_location < $link['offset']) + { + $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base); + } + else + { + $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base); + } + + $current = SimplePie_Misc::parse_url($this->file->url); + + if ($parsed['authority'] === '' || $parsed['authority'] == $current['authority']) + { + $this->local[] = $href; + } + else + { + $this->elsewhere[] = $href; + } + } + } + } + $this->local = array_unique($this->local); + $this->elsewhere = array_unique($this->elsewhere); + if (!empty($this->local) || !empty($this->elsewhere)) + { + return true; + } + return null; + } + + function extension(&$array) + { + foreach ($array as $key => $value) + { + if ($this->checked_feeds == $this->max_checked_feeds) + { + break; + } + if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml'))) + { + $this->checked_feeds++; + $feed =& new $this->file_class($value, $this->timeout, 5, null, $this->useragent); + if ($this->is_feed($feed)) + { + return $feed; + } + else + { + unset($array[$key]); + } + } + } + return null; + } + + function body(&$array) + { + foreach ($array as $key => $value) + { + if ($this->checked_feeds == $this->max_checked_feeds) + { + break; + } + if (preg_match('/(rss|rdf|atom|xml)/i', $value)) + { + $this->checked_feeds++; + $feed =& new $this->file_class($value, $this->timeout, 5, null, $this->useragent); + if ($this->is_feed($feed)) + { + return $feed; + } + else + { + unset($array[$key]); + } + } + } + return null; + } +} + +class SimplePie_Parser +{ + var $error_code; + var $error_string; + var $current_line; + var $current_column; + var $current_byte; + var $separator = ' '; + var $feed = false; + var $namespace = array(''); + var $element = array(''); + var $xml_base = array(''); + var $xml_base_explicit = array(false); + var $xml_lang = array(''); + var $data = array(); + var $datas = array(array()); + var $current_xhtml_construct = -1; + var $encoding; + + function parse(&$data, $encoding) + { + // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character + if (strtoupper($encoding) == 'US-ASCII') + { + $this->encoding = 'UTF-8'; + } + else + { + $this->encoding = $encoding; + } + + // Strip BOM: + // UTF-32 Big Endian BOM + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") + { + $data = substr($data, 4); + } + // UTF-32 Little Endian BOM + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") + { + $data = substr($data, 4); + } + // UTF-16 Big Endian BOM + elseif (substr($data, 0, 2) === "\xFE\xFF") + { + $data = substr($data, 2); + } + // UTF-16 Little Endian BOM + elseif (substr($data, 0, 2) === "\xFF\xFE") + { + $data = substr($data, 2); + } + // UTF-8 BOM + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") + { + $data = substr($data, 3); + } + + if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false) + { + $declaration = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); + if ($declaration->parse()) + { + $data = substr($data, $pos + 2); + $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data; + } + else + { + $this->error_string = 'SimplePie bug! Please report this!'; + return false; + } + } + + // Work around libxml bug + $data = str_replace('<', '<', $data); + $data = str_replace('>', '>', $data); + $data = str_replace('&', '&', $data); + $data = str_replace(''', ''', $data); + $data = str_replace('"', '"', $data); + + $return = true; + + // Create the parser + $xml = xml_parser_create_ns($this->encoding, $this->separator); + xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0); + xml_set_object($xml, $this); + xml_set_character_data_handler($xml, 'cdata'); + xml_set_element_handler($xml, 'tag_open', 'tag_close'); + + // Parse! + if (!xml_parse($xml, $data, true)) + { + $this->error_code = xml_get_error_code($xml); + $this->error_string = xml_error_string($this->error_code); + $return = false; + } + $this->current_line = xml_get_current_line_number($xml); + $this->current_column = xml_get_current_column_number($xml); + $this->current_byte = xml_get_current_byte_index($xml); + xml_parser_free($xml); + return $return; + } + + function get_error_code() + { + return $this->error_code; + } + + function get_error_string() + { + return $this->error_string; + } + + function get_current_line() + { + return $this->current_line; + } + + function get_current_column() + { + return $this->current_column; + } + + function get_current_byte() + { + return $this->current_byte; + } + + function get_data() + { + return $this->data; + } + + function tag_open($parser, $tag, $attributes) + { + if ($this->feed === 0) + { + return; + } + elseif ($this->feed == false) + { + if (in_array($tag, array( + SIMPLEPIE_NAMESPACE_ATOM_10 . $this->separator . 'feed', + SIMPLEPIE_NAMESPACE_ATOM_03 . $this->separator . 'feed', + 'rss', + SIMPLEPIE_NAMESPACE_RDF . $this->separator . 'RDF' + ))) + { + $this->feed = 1; + } + } + else + { + $this->feed++; + } + + list($this->namespace[], $this->element[]) = $this->split_ns($tag); + + $attribs = array(); + foreach ($attributes as $name => $value) + { + list($attrib_namespace, $attribute) = $this->split_ns($name); + $attribs[$attrib_namespace][$attribute] = $value; + } + + if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base'])) + { + $this->xml_base[] = SimplePie_Misc::absolutize_url($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)); + $this->xml_base_explicit[] = true; + } + else + { + $this->xml_base[] = end($this->xml_base); + $this->xml_base_explicit[] = end($this->xml_base_explicit); + } + + if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang'])) + { + $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang']; + } + else + { + $this->xml_lang[] = end($this->xml_lang); + } + + if ($this->current_xhtml_construct >= 0) + { + $this->current_xhtml_construct++; + if (end($this->namespace) == SIMPLEPIE_NAMESPACE_XHTML) + { + $this->data['data'] .= '<' . end($this->element); + if (isset($attribs[''])) + { + foreach ($attribs[''] as $name => $value) + { + $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"'; + } + } + $this->data['data'] .= '>'; + } + } + else + { + $this->datas[] =& $this->data; + $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][]; + $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang)); + if ((end($this->namespace) == SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] == 'xml') + || (end($this->namespace) == SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] == 'xhtml')) + { + $this->current_xhtml_construct = 0; + } + } + } + + function cdata($parser, $cdata) + { + if ($this->current_xhtml_construct >= 0) + { + $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding); + } + elseif ($this->feed > 1) + { + $this->data['data'] .= $cdata; + } + } + + function tag_close($parser, $tag) + { + if (!$this->feed) + { + return; + } + + if ($this->current_xhtml_construct >= 0) + { + $this->current_xhtml_construct--; + if (end($this->namespace) == SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'))) + { + $this->data['data'] .= '</' . end($this->element) . '>'; + } + } + if ($this->current_xhtml_construct == -1) + { + $this->data =& $this->datas[$this->feed]; + array_pop($this->datas); + } + + array_pop($this->element); + array_pop($this->namespace); + array_pop($this->xml_base); + array_pop($this->xml_base_explicit); + array_pop($this->xml_lang); + $this->feed--; + } + + function split_ns($string) + { + static $cache = array(); + if (!isset($cache[$string])) + { + if ($pos = strpos($string, $this->separator)) + { + static $separator_length; + if (!$separator_length) + { + $separator_length = strlen($this->separator); + } + $namespace = substr($string, 0, $pos); + $local_name = substr($string, $pos + $separator_length); + if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES) + { + $namespace = SIMPLEPIE_NAMESPACE_ITUNES; + } + + // Normalize the Media RSS namespaces + if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG) + { + $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS; + } + $cache[$string] = array($namespace, $local_name); + } + else + { + $cache[$string] = array('', $string); + } + } + return $cache[$string]; + } +} + +/** + * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags + */ +class SimplePie_Sanitize +{ + // Private vars + var $base; + + // Options + var $remove_div = true; + var $image_handler = ''; + var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); + var $encode_instead_of_strip = false; + var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); + var $strip_comments = false; + var $output_encoding = 'UTF-8'; + var $enable_cache = true; + var $cache_location = './cache'; + var $cache_name_function = 'md5'; + var $cache_class = 'SimplePie_Cache'; + var $file_class = 'SimplePie_File'; + var $timeout = 10; + var $useragent = ''; + var $force_fsockopen = false; + + var $replace_url_attributes = array( + 'a' => 'href', + 'area' => 'href', + 'blockquote' => 'cite', + 'del' => 'cite', + 'form' => 'action', + 'img' => array('longdesc', 'src'), + 'input' => 'src', + 'ins' => 'cite', + 'q' => 'cite' + ); + + function remove_div($enable = true) + { + $this->remove_div = (bool) $enable; + } + + function set_image_handler($page = false) + { + if ($page) + { + $this->image_handler = (string) $page; + } + else + { + $this->image_handler = false; + } + } + + function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache') + { + if (isset($enable_cache)) + { + $this->enable_cache = (bool) $enable_cache; + } + + if ($cache_location) + { + $this->cache_location = (string) $cache_location; + } + + if ($cache_name_function) + { + $this->cache_name_function = (string) $cache_name_function; + } + + if ($cache_class) + { + $this->cache_class = (string) $cache_class; + } + } + + function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false) + { + if ($file_class) + { + $this->file_class = (string) $file_class; + } + + if ($timeout) + { + $this->timeout = (string) $timeout; + } + + if ($useragent) + { + $this->useragent = (string) $useragent; + } + + if ($force_fsockopen) + { + $this->force_fsockopen = (string) $force_fsockopen; + } + } + + function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style')) + { + if ($tags) + { + if (is_array($tags)) + { + $this->strip_htmltags = $tags; + } + else + { + $this->strip_htmltags = explode(',', $tags); + } + } + else + { + $this->strip_htmltags = false; + } + } + + function encode_instead_of_strip($encode = false) + { + $this->encode_instead_of_strip = (bool) $encode; + } + + function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc')) + { + if ($attribs) + { + if (is_array($attribs)) + { + $this->strip_attributes = $attribs; + } + else + { + $this->strip_attributes = explode(',', $attribs); + } + } + else + { + $this->strip_attributes = false; + } + } + + function strip_comments($strip = false) + { + $this->strip_comments = (bool) $strip; + } + + function set_output_encoding($encoding = 'UTF-8') + { + $this->output_encoding = (string) $encoding; + } + + /** + * Set element/attribute key/value pairs of HTML attributes + * containing URLs that need to be resolved relative to the feed + * + * @access public + * @since 1.0 + * @param array $element_attribute Element/attribute key/value pairs + */ + function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite')) + { + $this->replace_url_attributes = (array) $element_attribute; + } + + function sanitize($data, $type, $base = '') + { + $data = trim($data); + if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI) + { + if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) + { + if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) + { + $type |= SIMPLEPIE_CONSTRUCT_HTML; + } + else + { + $type |= SIMPLEPIE_CONSTRUCT_TEXT; + } + } + + if ($type & SIMPLEPIE_CONSTRUCT_BASE64) + { + $data = base64_decode($data); + } + + if ($type & SIMPLEPIE_CONSTRUCT_XHTML) + { + if ($this->remove_div) + { + $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data); + $data = preg_replace('/<\/div>$/', '', $data); + } + else + { + $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data); + } + } + + if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML)) + { + // Strip comments + if ($this->strip_comments) + { + $data = SimplePie_Misc::strip_comments($data); + } + + // Strip out HTML tags and attributes that might cause various security problems. + // Based on recommendations by Mark Pilgrim at: + // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely + if ($this->strip_htmltags) + { + foreach ($this->strip_htmltags as $tag) + { + $pcre = "/<($tag)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$tag" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>|(\/)?>)/siU'; + while (preg_match($pcre, $data)) + { + $data = preg_replace_callback($pcre, array(&$this, 'do_strip_htmltags'), $data); + } + } + } + + if ($this->strip_attributes) + { + foreach ($this->strip_attributes as $attrib) + { + $data = preg_replace('/(<[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*)' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . trim($attrib) . '(?:\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>/', '\1\2\3>', $data); + } + } + + // Replace relative URLs + $this->base = $base; + foreach ($this->replace_url_attributes as $element => $attributes) + { + $data = $this->replace_urls($data, $element, $attributes); + } + + // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags. + if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) + { + $images = SimplePie_Misc::get_element('img', $data); + foreach ($images as $img) + { + if (isset($img['attribs']['src']['data'])) + { + $image_url = call_user_func($this->cache_name_function, $img['attribs']['src']['data']); + $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $image_url, 'spi'); + + if ($cache->load()) + { + $img['attribs']['src']['data'] = $this->image_handler . $image_url; + $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data); + } + else + { + $file =& new $this->file_class($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen); + $headers = $file->headers; + + if ($file->success && ($file->status_code == 200 || ($file->status_code > 206 && $file->status_code < 300))) + { + if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) + { + $img['attribs']['src']['data'] = $this->image_handler . $image_url; + $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data); + } + else + { + trigger_error("$cache->name is not writeable", E_USER_WARNING); + } + } + } + } + } + } + + // Having (possibly) taken stuff out, there may now be whitespace at the beginning/end of the data + $data = trim($data); + } + + if ($type & SIMPLEPIE_CONSTRUCT_IRI) + { + $data = SimplePie_Misc::absolutize_url($data, $base); + } + + if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI)) + { + $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8'); + } + + if ($this->output_encoding != 'UTF-8') + { + $data = SimplePie_Misc::change_encoding($data, 'UTF-8', $this->output_encoding); + } + } + return $data; + } + + function replace_urls($data, $tag, $attributes) + { + if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) + { + $elements = SimplePie_Misc::get_element($tag, $data); + foreach ($elements as $element) + { + if (is_array($attributes)) + { + foreach ($attributes as $attribute) + { + if (isset($element['attribs'][$attribute]['data'])) + { + $element['attribs'][$attribute]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attribute]['data'], $this->base); + $new_element = SimplePie_Misc::element_implode($element); + $data = str_replace($element['full'], $new_element, $data); + $element['full'] = $new_element; + } + } + } + elseif (isset($element['attribs'][$attributes]['data'])) + { + $element['attribs'][$attributes]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attributes]['data'], $this->base); + $data = str_replace($element['full'], SimplePie_Misc::element_implode($element), $data); + } + } + } + return $data; + } + + function do_strip_htmltags($match) + { + if ($this->encode_instead_of_strip) + { + if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) + { + $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8'); + $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8'); + return "<$match[1]$match[2]>$match[3]</$match[1]>"; + } + else + { + return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8'); + } + } + elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) + { + return $match[4]; + } + else + { + return ''; + } + } +} + +?> diff --git a/src/etc/inc/smtp.inc b/src/etc/inc/smtp.inc new file mode 100644 index 0000000..035a30a --- /dev/null +++ b/src/etc/inc/smtp.inc @@ -0,0 +1,862 @@ +<?php +/* + * smtp.php + * + * @(#) $Header$ + * + */ + +/* + pfSense_MODULE: notifications +*/ + +class smtp_class +{ + var $user=""; + var $realm=""; + var $password=""; + var $workstation=""; + var $authentication_mechanism=""; + var $host_name=""; + var $host_port=25; + var $ssl=0; + var $tls=0; + var $localhost=""; + var $timeout=0; + var $data_timeout=0; + var $direct_delivery=0; + var $error=""; + var $debug=0; + var $html_debug=0; + var $esmtp=1; + var $esmtp_host=""; + var $esmtp_extensions=array(); + var $maximum_piped_recipients=100; + var $exclude_address=""; + var $getmxrr="GetMXRR"; + var $pop3_auth_host=""; + var $pop3_auth_port=110; + + /* private variables - DO NOT ACCESS */ + + var $state="Disconnected"; + var $connection=0; + var $pending_recipients=0; + var $next_token=""; + var $direct_sender=""; + var $connected_domain=""; + var $result_code; + var $disconnected_error=0; + + /* Private methods - DO NOT CALL */ + + Function Tokenize($string,$separator="") + { + if(!strcmp($separator,"")) + { + $separator=$string; + $string=$this->next_token; + } + for($character=0;$character<strlen($separator);$character++) + { + if(GetType($position=strpos($string,$separator[$character]))=="integer") + $found=(IsSet($found) ? min($found,$position) : $position); + } + if(IsSet($found)) + { + $this->next_token=substr($string,$found+1); + return(substr($string,0,$found)); + } + else + { + $this->next_token=""; + return($string); + } + } + + Function OutputDebug($message) + { + $message.="\n"; + if($this->html_debug) + $message=str_replace("\n","<br />\n",HtmlEntities($message)); + echo $message; + flush(); + } + + Function SetDataAccessError($error) + { + $this->error=$error; + if(function_exists("socket_get_status")) + { + $status=socket_get_status($this->connection); + if($status["timed_out"]) + $this->error.=gettext(": data access time out"); + elseif($status["eof"]) + { + $this->error.=gettext(": the server disconnected"); + $this->disconnected_error=1; + } + } + } + + Function GetLine() + { + for($line="";;) + { + if(feof($this->connection)) + { + $this->error=gettext("reached the end of data while reading from the SMTP server connection"); + return(""); + } + if(GetType($data=@fgets($this->connection,100))!="string" + || strlen($data)==0) + { + $this->SetDataAccessError(gettext("it was not possible to read line from the SMTP server")); + return(""); + } + $line.=$data; + $length=strlen($line); + if($length>=2 + && substr($line,$length-2,2)=="\r\n") + { + $line=substr($line,0,$length-2); + if($this->debug) + $this->OutputDebug("S $line"); + return($line); + } + } + } + + Function PutLine($line) + { + if($this->debug) + $this->OutputDebug("C $line"); + if(!@fputs($this->connection,"$line\r\n")) + { + $this->SetDataAccessError(gettext("it was not possible to send a line to the SMTP server")); + return(0); + } + return(1); + } + + Function PutData(&$data) + { + if(strlen($data)) + { + if($this->debug) + $this->OutputDebug("C $data"); + if(!@fputs($this->connection,$data)) + { + $this->SetDataAccessError(gettext("it was not possible to send data to the SMTP server")); + return(0); + } + } + return(1); + } + + Function VerifyResultLines($code,&$responses) + { + $responses=array(); + Unset($this->result_code); + while(strlen($line=$this->GetLine($this->connection))) + { + if(IsSet($this->result_code)) + { + if(strcmp($this->Tokenize($line," -"),$this->result_code)) + { + $this->error=$line; + return(0); + } + } + else + { + $this->result_code=$this->Tokenize($line," -"); + if(GetType($code)=="array") + { + for($codes=0;$codes<count($code) && strcmp($this->result_code,$code[$codes]);$codes++); + if($codes>=count($code)) + { + $this->error=$line; + return(0); + } + } + else + { + if(strcmp($this->result_code,$code)) + { + $this->error=$line; + return(0); + } + } + } + $responses[]=$this->Tokenize(""); + if(!strcmp($this->result_code,$this->Tokenize($line," "))) + return(1); + } + return(-1); + } + + Function FlushRecipients() + { + if($this->pending_sender) + { + if($this->VerifyResultLines("250",$responses)<=0) + return(0); + $this->pending_sender=0; + } + for(;$this->pending_recipients;$this->pending_recipients--) + { + if($this->VerifyResultLines(array("250","251"),$responses)<=0) + return(0); + } + return(1); + } + + Function ConnectToHost($domain, $port, $resolve_message) + { + if($this->ssl || $this->tls) + { + $version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7"); + $php_version=intval($version[0])*1000000+intval($version[1])*1000+intval($version[2]); + if($php_version<4003000) + return(gettext("establishing SSL connections requires at least PHP version 4.3.0")); + if(!function_exists("extension_loaded") + || !extension_loaded("openssl")) + return(gettext("establishing SSL connections requires the OpenSSL extension enabled")); + } + if(preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/',$domain)) + $ip=$domain; + else + { + if($this->debug) + $this->OutputDebug($resolve_message); + if(!strcmp($ip=@gethostbyname($domain),$domain)) + return(sprintf(gettext("could not resolve host \"%s\""), $domain)); + } + if(strlen($this->exclude_address) + && !strcmp(@gethostbyname($this->exclude_address),$ip)) + return(sprintf(gettext("domain \"%s\" resolved to an address excluded to be valid"), $domain)); + if($this->debug) + $this->OutputDebug(sprintf(gettext('Connecting to host address "%1$s" port %2$s...'), $ip, $port)); + if(($this->connection=($this->timeout ? @fsockopen(($this->ssl ? "ssl://" : "").$ip,$port,$errno,$error,$this->timeout) : @fsockopen(($this->ssl ? "ssl://" : "").$ip,$port)))) + return(""); + $error=($this->timeout ? strval($error) : "??"); + switch($error) + { + case "-3": + return(gettext("-3 socket could not be created")); + case "-4": + return(sprintf(gettext("-4 dns lookup on hostname \"%s\" failed"), $domain)); + case "-5": + return(gettext("-5 connection refused or timed out")); + case "-6": + return(gettext("-6 fdopen() call failed")); + case "-7": + return(gettext("-7 setvbuf() call failed")); + } + return(sprintf(gettext('could not connect to the host "%1$s": %2$s'), $domain, $error)); + } + + Function SASLAuthenticate($mechanisms, $credentials, &$authenticated, &$mechanism) + { + $authenticated=0; + if(!function_exists("class_exists") + || !class_exists("sasl_client_class")) + { + $this->error=gettext("it is not possible to authenticate using the specified mechanism because the SASL library class is not loaded"); + return(0); + } + $sasl=new sasl_client_class; + $sasl->SetCredential("user",$credentials["user"]); + $sasl->SetCredential("password",$credentials["password"]); + if(IsSet($credentials["realm"])) + $sasl->SetCredential("realm",$credentials["realm"]); + if(IsSet($credentials["workstation"])) + $sasl->SetCredential("workstation",$credentials["workstation"]); + if(IsSet($credentials["mode"])) + $sasl->SetCredential("mode",$credentials["mode"]); + do + { + $status=$sasl->Start($mechanisms,$message,$interactions); + } + while($status==SASL_INTERACT); + switch($status) + { + case SASL_CONTINUE: + break; + case SASL_NOMECH: + if(strlen($this->authentication_mechanism)) + { + $this->error=printf(gettext('authenticated mechanism %1$s may not be used: %2$s'), $this->authentication_mechanism, $sasl->error); + return(0); + } + break; + default: + $this->error=gettext("Could not start the SASL authentication client:") . " ".$sasl->error; + return(0); + } + if(strlen($mechanism=$sasl->mechanism)) + { + if($this->PutLine("AUTH ".$sasl->mechanism.(IsSet($message) ? " ".base64_encode($message) : ""))==0) + { + $this->error=gettext("Could not send the AUTH command"); + return(0); + } + if(!$this->VerifyResultLines(array("235","334"),$responses)) + return(0); + switch($this->result_code) + { + case "235": + $response=""; + $authenticated=1; + break; + case "334": + $response=base64_decode($responses[0]); + break; + default: + $this->error=gettext("Authentication error:") . " ".$responses[0]; + return(0); + } + for(;!$authenticated;) + { + do + { + $status=$sasl->Step($response,$message,$interactions); + } + while($status==SASL_INTERACT); + switch($status) + { + case SASL_CONTINUE: + if($this->PutLine(base64_encode($message))==0) + { + $this->error=gettext("Could not send the authentication step message"); + return(0); + } + if(!$this->VerifyResultLines(array("235","334"),$responses)) + return(0); + switch($this->result_code) + { + case "235": + $response=""; + $authenticated=1; + break; + case "334": + $response=base64_decode($responses[0]); + break; + default: + $this->error=gettext("Authentication error:") . " ".$responses[0]; + return(0); + } + break; + default: + $this->error=gettext("Could not process the SASL authentication step:") . " ".$sasl->error; + return(0); + } + } + } + return(1); + } + + /* Public methods */ + + Function Connect($domain="") + { + if(strcmp($this->state,"Disconnected")) + { + $this->error=gettext("connection is already established"); + return(0); + } + $this->disconnected_error=0; + $this->error=$error=""; + $this->esmtp_host=""; + $this->esmtp_extensions=array(); + $hosts=array(); + if($this->direct_delivery) + { + if(strlen($domain)==0) + return(1); + $hosts=$weights=$mxhosts=array(); + $getmxrr=$this->getmxrr; + if(function_exists($getmxrr) + && $getmxrr($domain,$hosts,$weights)) + { + for($host=0;$host<count($hosts);$host++) + $mxhosts[$weights[$host]]=$hosts[$host]; + KSort($mxhosts); + for(Reset($mxhosts),$host=0;$host<count($mxhosts);Next($mxhosts),$host++) + $hosts[$host]=$mxhosts[Key($mxhosts)]; + } + else + { + if(strcmp(@gethostbyname($domain),$domain)!=0) + $hosts[]=$domain; + } + } + else + { + if(strlen($this->host_name)) + $hosts[]=$this->host_name; + if(strlen($this->pop3_auth_host)) + { + $user=$this->user; + if(strlen($user)==0) + { + $this->error=gettext("it was not specified the POP3 authentication user"); + return(0); + } + $password=$this->password; + if(strlen($password)==0) + { + $this->error=gettext("it was not specified the POP3 authentication password"); + return(0); + } + $domain=$this->pop3_auth_host; + $this->error=$this->ConnectToHost($domain, $this->pop3_auth_port, sprintf(gettext("Resolving POP3 authentication host \"%s\"..."), $domain)); + if(strlen($this->error)) + return(0); + if(strlen($response=$this->GetLine())==0) + return(0); + if(strcmp($this->Tokenize($response," "),"+OK")) + { + $this->error=gettext("POP3 authentication server greeting was not found"); + return(0); + } + if(!$this->PutLine("USER ".$this->user) + || strlen($response=$this->GetLine())==0) + return(0); + if(strcmp($this->Tokenize($response," "),"+OK")) + { + $this->error=gettext("POP3 authentication user was not accepted:") . " ".$this->Tokenize("\r\n"); + return(0); + } + if(!$this->PutLine("PASS ".$password) + || strlen($response=$this->GetLine())==0) + return(0); + if(strcmp($this->Tokenize($response," "),"+OK")) + { + $this->error=gettext("POP3 authentication password was not accepted:") . " ".$this->Tokenize("\r\n"); + return(0); + } + fclose($this->connection); + $this->connection=0; + } + } + if(count($hosts)==0) + { + $this->error=gettext("could not determine the SMTP to connect"); + return(0); + } + for($host=0, $error="not connected";strlen($error) && $host<count($hosts);$host++) + { + $domain=$hosts[$host]; + $error=$this->ConnectToHost($domain, $this->host_port, sprintf(gettext("Resolving SMTP server domain \"%s\"..."), $domain)); + } + if(strlen($error)) + { + $this->error=$error; + return(0); + } + $timeout=($this->data_timeout ? $this->data_timeout : $this->timeout); + if($timeout + && function_exists("socket_set_timeout")) + socket_set_timeout($this->connection,$timeout,0); + if($this->debug) + $this->OutputDebug(sprintf(gettext("Connected to SMTP server \"%s\"."), $domain)); + if($this->VerifyResultLines("220",$responses)>0) + { + // Send our HELLO + $success = $this->hello($this->hostname()); + if ($this->tls) + $success = $this->startTLS(); + + if($success + && strlen($this->user) + && strlen($this->pop3_auth_host)==0) + { + if(!IsSet($this->esmtp_extensions["AUTH"])) + { + $this->error = gettext("server does not require authentication"); + $success=0; + } + else + { + if(strlen($this->authentication_mechanism)) + $mechanisms=array($this->authentication_mechanism); + else + { + $mechanisms=array(); + for($authentication=$this->Tokenize($this->esmtp_extensions["AUTH"]," ");strlen($authentication);$authentication=$this->Tokenize(" ")) + $mechanisms[]=$authentication; + } + $credentials=array( + "user"=>$this->user, + "password"=>$this->password + ); + if(strlen($this->realm)) + $credentials["realm"]=$this->realm; + if(strlen($this->workstation)) + $credentials["workstation"]=$this->workstation; + $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism); + if(!$success + && !strcmp($mechanism,"PLAIN")) + { + /* + * Author: Russell Robinson, 25 May 2003, http://www.tectite.com/ + * Purpose: Try various AUTH PLAIN authentication methods. + */ + $mechanisms=array("PLAIN"); + $credentials=array( + "user"=>$this->user, + "password"=>$this->password + ); + if(strlen($this->realm)) + { + /* + * According to: http://www.sendmail.org/~ca/email/authrealms.html#authpwcheck_method + * some sendmails won't accept the realm, so try again without it + */ + $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism); + } + if(!$success) + { + /* + * It was seen an EXIM configuration like this: + * user^password^unused + */ + $credentials["mode"]=SASL_PLAIN_EXIM_DOCUMENTATION_MODE; + $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism); + } + if(!$success) + { + /* + * ... though: http://exim.work.de/exim-html-3.20/doc/html/spec_36.html + * specifies: ^user^password + */ + $credentials["mode"]=SASL_PLAIN_EXIM_MODE; + $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism); + } + } + if($success + && strlen($mechanism)==0) + { + $this->error=gettext("it is not supported any of the authentication mechanisms required by the server"); + $success=0; + } + } + } + } + if($success) + { + $this->state="Connected"; + $this->connected_domain=$domain; + } + else + { + fclose($this->connection); + $this->connection=0; + } + return($success); + } + + Function hostname() { + if(!strcmp($localhost=$this->localhost,"") + && !strcmp($localhost=getenv("SERVER_NAME"),"") + && !strcmp($localhost=getenv("HOST"),"") + && !strcmp($localhost=getenv("HOSTNAME"),"") + && !strcmp($localhost=gethostname(),"")) + $localhost="localhost"; + + return $localhost; + } + + Function hello() + { + $success = 0; + $fallback = 1; + if ($this->esmtp || strlen($this->user)) { + if ($this->PutLine("EHLO ".$this->hostname())) { + if (($success_code = $this->VerifyResultLines("250",$responses)) > 0) { + $this->esmtp_host = $this->Tokenize($responses[0]," "); + for($response=1;$response<count($responses);$response++) { + $extension = strtoupper($this->Tokenize($responses[$response]," ")); + $this->esmtp_extensions[$extension]=$this->Tokenize(""); + } + $success = 1; + $fallback = 0; + } else { + if ($success_code == 0) { + $code = $this->Tokenize($this->error," -"); + switch($code) { + case "421": + $fallback=0; + break; + } + } + } + } else + $fallback=0; + } + + if ($fallback) { + if ($this->PutLine("HELO $localhost") && $this->VerifyResultLines("250",$responses)>0) + $success=1; + } + return $success; + } + + Function startTLS() { + if ($this->PutLine("STARTTLS") && $this->VerifyResultLines("220",$responses)>0) { + if (!stream_socket_enable_crypto($this->connection,true,STREAM_CRYPTO_METHOD_TLS_CLIENT)) { + return false; + } else { + // Resend HELO since session has been reset + return $this->hello($this->hostname); + } + } else + return false; + } + + Function MailFrom($sender) + { + if($this->direct_delivery) + { + switch($this->state) + { + case "Disconnected": + $this->direct_sender=$sender; + return(1); + case "Connected": + $sender=$this->direct_sender; + break; + default: + $this->error=gettext("direct delivery connection is already established and sender is already set"); + return(0); + } + } + else + { + if(strcmp($this->state,"Connected")) + { + $this->error=gettext("connection is not in the initial state"); + return(0); + } + } + $this->error=""; + if(!$this->PutLine("MAIL FROM:<$sender>")) + return(0); + if(!IsSet($this->esmtp_extensions["PIPELINING"]) + && $this->VerifyResultLines("250",$responses)<=0) + return(0); + $this->state="SenderSet"; + if(IsSet($this->esmtp_extensions["PIPELINING"])) + $this->pending_sender=1; + $this->pending_recipients=0; + return(1); + } + + Function SetRecipient($recipient) + { + if($this->direct_delivery) + { + if(GetType($at=strrpos($recipient,"@"))!="integer") + return(gettext("it was not specified a valid direct recipient")); + $domain=substr($recipient,$at+1); + switch($this->state) + { + case "Disconnected": + if(!$this->Connect($domain)) + return(0); + if(!$this->MailFrom("")) + { + $error=$this->error; + $this->Disconnect(); + $this->error=$error; + return(0); + } + break; + case "SenderSet": + case "RecipientSet": + if(strcmp($this->connected_domain,$domain)) + { + $this->error=gettext("it is not possible to deliver directly to recipients of different domains"); + return(0); + } + break; + default: + $this->error=gettext("connection is already established and the recipient is already set"); + return(0); + } + } + else + { + switch($this->state) + { + case "SenderSet": + case "RecipientSet": + break; + default: + $this->error=gettext("connection is not in the recipient setting state"); + return(0); + } + } + $this->error=""; + if(!$this->PutLine("RCPT TO:<$recipient>")) + return(0); + if(IsSet($this->esmtp_extensions["PIPELINING"])) + { + $this->pending_recipients++; + if($this->pending_recipients>=$this->maximum_piped_recipients) + { + if(!$this->FlushRecipients()) + return(0); + } + } + else + { + if($this->VerifyResultLines(array("250","251"),$responses)<=0) + return(0); + } + $this->state="RecipientSet"; + return(1); + } + + Function StartData() + { + if(strcmp($this->state,"RecipientSet")) + { + $this->error=gettext("connection is not in the start sending data state"); + return(0); + } + $this->error=""; + if(!$this->PutLine("DATA")) + return(0); + if($this->pending_recipients) + { + if(!$this->FlushRecipients()) + return(0); + } + if($this->VerifyResultLines("354",$responses)<=0) + return(0); + $this->state="SendingData"; + return(1); + } + + Function PrepareData(&$data,&$output,$preg=1) + { + if($preg + && function_exists("preg_replace")) + $output=preg_replace(array("/\n\n|\r\r/","/(^|[^\r])\n/","/\r([^\n]|\$)/D","/(^|\n)\\./"),array("\r\n\r\n","\\1\r\n","\r\n\\1","\\1.."),$data); + else + $output=ereg_replace("(^|\n)\\.","\\1..",ereg_replace("\r([^\n]|\$)","\r\n\\1",ereg_replace("(^|[^\r])\n","\\1\r\n",ereg_replace("\n\n|\r\r","\r\n\r\n",$data)))); + } + + Function SendData($data) + { + if(strcmp($this->state,"SendingData")) + { + $this->error=gettext("connection is not in the sending data state"); + return(0); + } + $this->error=""; + return($this->PutData($data)); + } + + Function EndSendingData() + { + if(strcmp($this->state,"SendingData")) + { + $this->error=gettext("connection is not in the sending data state"); + return(0); + } + $this->error=""; + if(!$this->PutLine("\r\n.") + || $this->VerifyResultLines("250",$responses)<=0) + return(0); + $this->state="Connected"; + return(1); + } + + Function ResetConnection() + { + switch($this->state) + { + case "Connected": + return(1); + case "SendingData": + $this->error="can not reset the connection while sending data"; + return(0); + case "Disconnected": + $this->error="can not reset the connection before it is established"; + return(0); + } + $this->error=""; + if(!$this->PutLine("RSET") + || $this->VerifyResultLines("250",$responses)<=0) + return(0); + $this->state="Connected"; + return(1); + } + + Function Disconnect($quit=1) + { + if(!strcmp($this->state,"Disconnected")) + { + $this->error=gettext("it was not previously established a SMTP connection"); + return(0); + } + $this->error=""; + if(!strcmp($this->state,"Connected") + && $quit + && (!$this->PutLine("QUIT") + || ($this->VerifyResultLines("221",$responses)<=0 + && !$this->disconnected_error))) + return(0); + if($this->disconnected_error) + $this->disconnected_error=0; + else + fclose($this->connection); + $this->connection=0; + $this->state="Disconnected"; + if($this->debug) + $this->OutputDebug("Disconnected."); + return(1); + } + + Function SendMessage($sender,$recipients,$headers,$body) + { + if(($success=$this->Connect())) + { + if(($success=$this->MailFrom($sender))) + { + for($recipient=0;$recipient<count($recipients);$recipient++) + { + if(!($success=$this->SetRecipient($recipients[$recipient]))) + break; + } + if($success + && ($success=$this->StartData())) + { + for($header_data="",$header=0;$header<count($headers);$header++) + $header_data.=$headers[$header]."\r\n"; + if(($success=$this->SendData($header_data."\r\n"))) + { + $this->PrepareData($body,$body_data); + $success=$this->SendData($body_data); + } + if($success) + $success=$this->EndSendingData(); + } + } + $error=$this->error; + $disconnect_success=$this->Disconnect($success); + if($success) + $success=$disconnect_success; + else + $this->error=$error; + } + return($success); + } + +}; + +?> diff --git a/src/etc/inc/system.inc b/src/etc/inc/system.inc new file mode 100644 index 0000000..41e798e --- /dev/null +++ b/src/etc/inc/system.inc @@ -0,0 +1,2258 @@ +<?php +/* $Id$ */ +/* + system.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_BUILDER_BINARIES: /usr/sbin/powerd /usr/bin/killall /sbin/route + pfSense_BUILDER_BINARIES: /bin/hostname /bin/ls /usr/sbin/syslogd + pfSense_BUILDER_BINARIES: /usr/sbin/pccardd /usr/local/sbin/lighttpd /bin/chmod /bin/mkdir + pfSense_BUILDER_BINARIES: /usr/bin/tar /usr/local/sbin/ntpd /usr/local/sbin/ntpdate + pfSense_BUILDER_BINARIES: /usr/bin/nohup /sbin/dmesg /usr/local/sbin/atareinit /sbin/kldload + pfSense_BUILDER_BINARIES: /usr/local/sbin/filterdns + pfSense_MODULE: utils +*/ + +function activate_powerd() { + global $config, $g; + + if (is_process_running("powerd")) { + exec("/usr/bin/killall powerd"); + } + if (isset($config['system']['powerd_enable'])) { + if ($g["platform"] == "nanobsd") { + exec("/sbin/kldload cpufreq"); + } + + $ac_mode = "hadp"; + if (!empty($config['system']['powerd_ac_mode'])) { + $ac_mode = $config['system']['powerd_ac_mode']; + } + + $battery_mode = "hadp"; + if (!empty($config['system']['powerd_battery_mode'])) { + $battery_mode = $config['system']['powerd_battery_mode']; + } + + $normal_mode = "hadp"; + if (!empty($config['system']['powerd_normal_mode'])) { + $normal_mode = $config['system']['powerd_normal_mode']; + } + + mwexec("/usr/sbin/powerd -b $battery_mode -a $ac_mode -n $normal_mode"); + } +} + +function get_default_sysctl_value($id) { + global $sysctls; + + if (isset($sysctls[$id])) { + return $sysctls[$id]; + } +} + +function get_sysctl_descr($sysctl) { + unset($output); + $_gb = exec("/sbin/sysctl -nd {$sysctl}", $output); + + return $output[0]; +} + +function system_get_sysctls() { + global $config, $sysctls; + + $disp_sysctl = array(); + $disp_cache = array(); + if (is_array($config['sysctl']) && is_array($config['sysctl']['item'])) { + foreach ($config['sysctl']['item'] as $id => $tunable) { + if ($tunable['value'] == "default") { + $value = get_default_sysctl_value($tunable['tunable']); + } else { + $value = $tunable['value']; + } + + $disp_sysctl[$id] = $tunable; + $disp_sysctl[$id]['modified'] = true; + $disp_cache[$tunable['tunable']] = 'set'; + } + } + + foreach ($sysctls as $sysctl => $value) { + if (isset($disp_cache[$sysctl])) { + continue; + } + + $disp_sysctl[$sysctl] = array('tunable' => $sysctl, 'value' => $value, 'descr' => get_sysctl_descr($sysctl)); + } + unset($disp_cache); + return $disp_sysctl; +} + +function activate_sysctls() { + global $config, $g, $sysctls; + + if (is_array($config['sysctl']) && is_array($config['sysctl']['item'])) { + foreach ($config['sysctl']['item'] as $tunable) { + if ($tunable['value'] == "default") { + $value = get_default_sysctl_value($tunable['tunable']); + } else { + $value = $tunable['value']; + } + + $sysctls[$tunable['tunable']] = $value; + } + } + + set_sysctl($sysctls); +} + +function system_resolvconf_generate($dynupdate = false) { + global $config, $g; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_resolvconf_generate() being called $mt\n"; + } + + $syscfg = $config['system']; + + if ((((isset($config['dnsmasq']['enable'])) && + (!isset($config['dnsmasq']['port']) || $config['dnsmasq']['port'] == "53") && + (empty($config['dnsmasq']['interface']) || + in_array("lo0", explode(",", $config['dnsmasq']['interface'])))) || + ((isset($config['unbound']['enable'])) && + (!isset($config['unbound']['port']) || $config['unbound']['port'] == "53") && + (empty($config['unbound']['active_interface']) || + in_array("lo0", explode(",", $config['unbound']['active_interface'])) || + in_array("all", explode(",", $config['unbound']['active_interface']), true)))) && + (!isset($config['system']['dnslocalhost']))) { + $resolvconf .= "nameserver 127.0.0.1\n"; + } + + if (isset($syscfg['dnsallowoverride'])) { + /* get dynamically assigned DNS servers (if any) */ + $ns = array_unique(get_searchdomains()); + foreach ($ns as $searchserver) { + if ($searchserver) { + $resolvconf .= "search {$searchserver}\n"; + } + } + $ns = array_unique(get_nameservers()); + foreach ($ns as $nameserver) { + if ($nameserver) { + $resolvconf .= "nameserver $nameserver\n"; + } + } + } else { + $ns = array(); + // Do not create blank search/domain lines, it can break tools like dig. + if ($syscfg['domain']) { + $resolvconf .= "search {$syscfg['domain']}\n"; + } + } + if (is_array($syscfg['dnsserver'])) { + foreach ($syscfg['dnsserver'] as $sys_dnsserver) { + if ($sys_dnsserver && (!in_array($sys_dnsserver, $ns))) { + $resolvconf .= "nameserver $sys_dnsserver\n"; + } + } + } + + // Add EDNS support + if (isset($config['unbound']['enable']) && isset($config['unbound']['edns'])) { + $resolvconf .= "options edns0\n"; + } + + $dnslock = lock('resolvconf', LOCK_EX); + + $fd = fopen("{$g['varetc_path']}/resolv.conf", "w"); + if (!$fd) { + printf("Error: cannot open resolv.conf in system_resolvconf_generate().\n"); + unlock($dnslock); + return 1; + } + + fwrite($fd, $resolvconf); + fclose($fd); + + // Prevent resolvconf(8) from rewriting our resolv.conf + $fd = fopen("{$g['varetc_path']}/resolvconf.conf", "w"); + if (!$fd) { + printf("Error: cannot open resolvconf.conf in system_resolvconf_generate().\n"); + return 1; + } + fwrite($fd, "resolv_conf=\"/dev/null\"\n"); + fclose($fd); + + if (!platform_booting()) { + /* restart dhcpd (nameservers may have changed) */ + if (!$dynupdate) { + services_dhcpd_configure(); + } + } + + /* setup static routes for DNS servers. */ + for ($dnscounter=1; $dnscounter<5; $dnscounter++) { + /* setup static routes for dns servers */ + $dnsgw = "dns{$dnscounter}gw"; + if (isset($config['system'][$dnsgw])) { + $gwname = $config['system'][$dnsgw]; + if (($gwname <> "") && ($gwname <> "none")) { + $gatewayip = lookup_gateway_ip_by_name($gwname); + if (is_ipaddrv4($gatewayip)) { + /* dns server array starts at 0 */ + $dnscountermo = $dnscounter - 1; + mwexec("/sbin/route change -host " . $syscfg['dnsserver'][$dnscountermo] . " {$gatewayip}"); + if (isset($config['system']['route-debug'])) { + $mt = microtime(); + log_error("ROUTING debug: $mt - route change -host {$syscfg['dnsserver'][$dnscountermo]} $gatewayip "); + } + } + if (is_ipaddrv6($gatewayip)) { + /* dns server array starts at 0 */ + $dnscountermo = $dnscounter - 1; + mwexec("/sbin/route change -host -inet6 " . $syscfg['dnsserver'][$dnscountermo] . " {$gatewayip}"); + if (isset($config['system']['route-debug'])) { + $mt = microtime(); + log_error("ROUTING debug: $mt - route change -host -inet6 {$syscfg['dnsserver'][$dnscountermo]} $gatewayip "); + } + } + } + } + } + + unlock($dnslock); + + return 0; +} + +function get_searchdomains() { + global $config, $g; + + $master_list = array(); + + // Read in dhclient nameservers + $search_list = glob("/var/etc/searchdomain_*"); + if (is_array($search_list)) { + foreach ($search_list as $fdns) { + $contents = file($fdns, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if (!is_array($contents)) { + continue; + } + foreach ($contents as $dns) { + if (is_hostname($dns)) { + $master_list[] = $dns; + } + } + } + } + + return $master_list; +} + +function get_nameservers() { + global $config, $g; + $master_list = array(); + + // Read in dhclient nameservers + $dns_lists = glob("/var/etc/nameserver_*"); + if (is_array($dns_lists)) { + foreach ($dns_lists as $fdns) { + $contents = file($fdns, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if (!is_array($contents)) { + continue; + } + foreach ($contents as $dns) { + if (is_ipaddr($dns)) { + $master_list[] = $dns; + } + } + } + } + + // Read in any extra nameservers + if (file_exists("/var/etc/nameservers.conf")) { + $dns_s = file("/var/etc/nameservers.conf", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if (is_array($dns_s)) { + foreach ($dns_s as $dns) { + if (is_ipaddr($dns)) { + $master_list[] = $dns; + } + } + } + } + + return $master_list; +} + +function system_hosts_generate() { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_hosts_generate() being called $mt\n"; + } + + $syscfg = $config['system']; + if (isset($config['unbound']) && isset($config['unbound']['enable'])) { + $dnsmasqcfg = $config['unbound']; + } else { + $dnsmasqcfg = $config['dnsmasq']; + } + + $hosts = "127.0.0.1 localhost localhost.{$syscfg['domain']}\n"; + $hosts .= "::1 localhost localhost.{$syscfg['domain']}\n"; + $lhosts = ""; + $dhosts = ""; + + if ($config['interfaces']['lan']) { + $cfgip = get_interface_ip("lan"); + if (is_ipaddr($cfgip)) { + $hosts .= "{$cfgip} {$syscfg['hostname']}.{$syscfg['domain']} {$syscfg['hostname']}\n"; + } + $cfgipv6 = get_interface_ipv6("lan"); + if (is_ipaddrv6($cfgipv6)) { + $hosts .= "{$cfgipv6} {$syscfg['hostname']}.{$syscfg['domain']} {$syscfg['hostname']}\n"; + } + } else { + $sysiflist = get_configured_interface_list(); + $hosts_if_found = false; + foreach ($sysiflist as $sysif) { + if (!interface_has_gateway($sysif)) { + $cfgip = get_interface_ip($sysif); + if (is_ipaddr($cfgip)) { + $hosts .= "{$cfgip} {$syscfg['hostname']}.{$syscfg['domain']} {$syscfg['hostname']}\n"; + $hosts_if_found = true; + } + $cfgipv6 = get_interface_ipv6($sysif); + if (is_ipaddrv6($cfgipv6)) { + $hosts .= "{$cfgipv6} {$syscfg['hostname']}.{$syscfg['domain']} {$syscfg['hostname']}\n"; + $hosts_if_found = true; + } + if ($hosts_if_found == true) { + break; + } + } + } + } + + if (isset($dnsmasqcfg['enable'])) { + if (!is_array($dnsmasqcfg['hosts'])) { + $dnsmasqcfg['hosts'] = array(); + } + + foreach ($dnsmasqcfg['hosts'] as $host) { + if ($host['host'] || $host['host'] == "0") { + $lhosts .= "{$host['ip']} {$host['host']}.{$host['domain']} {$host['host']}\n"; + } else { + $lhosts .= "{$host['ip']} {$host['domain']}\n"; + } + if (!is_array($host['aliases']) || !is_array($host['aliases']['item'])) { + continue; + } + foreach ($host['aliases']['item'] as $alias) { + if ($alias['host'] || $alias['host'] == "0") { + $lhosts .= "{$host['ip']} {$alias['host']}.{$alias['domain']} {$alias['host']}\n"; + } else { + $lhosts .= "{$host['ip']} {$alias['domain']}\n"; + } + } + } + if (isset($dnsmasqcfg['regdhcpstatic']) && is_array($config['dhcpd'])) { + foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) { + if (is_array($dhcpifconf['staticmap']) && isset($dhcpifconf['enable'])) { + foreach ($dhcpifconf['staticmap'] as $host) { + if ($host['ipaddr'] && $host['hostname'] && $host['domain']) { + $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$host['domain']} {$host['hostname']}\n"; + } else if ($host['ipaddr'] && $host['hostname'] && $dhcpifconf['domain']) { + $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$dhcpifconf['domain']} {$host['hostname']}\n"; + } else if ($host['ipaddr'] && $host['hostname']) { + $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$syscfg['domain']} {$host['hostname']}\n"; + } + } + } + } + } + if (isset($dnsmasqcfg['regdhcpstatic']) && is_array($config['dhcpdv6'])) { + foreach ($config['dhcpdv6'] as $dhcpif => $dhcpifconf) { + if (is_array($dhcpifconf['staticmap']) && isset($dhcpifconf['enable'])) { + foreach ($dhcpifconf['staticmap'] as $host) { + if ($host['ipaddrv6'] && $host['hostname'] && $host['domain']) { + $dhosts .= "{$host['ipaddrv6']} {$host['hostname']}.{$host['domain']} {$host['hostname']}\n"; + } else if ($host['ipaddrv6'] && $host['hostname'] && $dhcpifconf['domain']) { + $dhosts .= "{$host['ipaddrv6']} {$host['hostname']}.{$dhcpifconf['domain']} {$host['hostname']}\n"; + } else if ($host['ipaddrv6'] && $host['hostname']) { + $dhosts .= "{$host['ipaddrv6']} {$host['hostname']}.{$syscfg['domain']} {$host['hostname']}\n"; + } + } + } + } + } + + if (isset($dnsmasqcfg['dhcpfirst'])) { + $hosts .= $dhosts . $lhosts; + } else { + $hosts .= $lhosts . $dhosts; + } + } + + /* + * Do not remove this because dhcpleases monitors with kqueue it needs to be + * killed before writing to hosts files. + */ + if (file_exists("{$g['varrun_path']}/dhcpleases.pid")) { + sigkillbypid("{$g['varrun_path']}/dhcpleases.pid", "TERM"); + @unlink("{$g['varrun_path']}/dhcpleases.pid"); + } + $fd = fopen("{$g['varetc_path']}/hosts", "w"); + if (!$fd) { + log_error("Error: cannot open hosts file in system_hosts_generate().\n"); + return 1; + } + fwrite($fd, $hosts); + fclose($fd); + + if (isset($config['unbound']['enable'])) { + require_once("unbound.inc"); + unbound_hosts_generate(); + } + + return 0; +} + +function system_dhcpleases_configure() { + global $config, $g; + + /* Start the monitoring process for dynamic dhcpclients. */ + if ((isset($config['dnsmasq']['enable']) && isset($config['dnsmasq']['regdhcp'])) || + (isset($config['unbound']['enable']) && isset($config['unbound']['regdhcp']))) { + /* Make sure we do not error out */ + mwexec("/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/db"); + if (!file_exists("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases")) { + @touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"); + } + + if (isset($config['unbound']['enable'])) { + $dns_pid = "unbound.pid"; + $unbound_conf = "-u {$g['unbound_chroot_path']}/dhcpleases_entries.conf"; + } else { + $dns_pid = "dnsmasq.pid"; + $unbound_conf = ""; + } + + $pidfile = "{$g['varrun_path']}/dhcpleases.pid"; + if (isvalidpid($pidfile)) { + /* Make sure dhcpleases is using correct unbound or dnsmasq */ + $_gb = exec("/bin/pgrep -F {$pidfile} -f {$dns_pid}", $output, $retval); + if (intval($retval) == 0) { + sigkillbypid($pidfile, "HUP"); + return; + } else { + sigkillbypid($pidfile, "TERM"); + } + } + + /* To ensure we do not start multiple instances of dhcpleases, perform some clean-up first. */ + if (is_process_running("dhcpleases")) { + sigkillbyname('dhcpleases', "TERM"); + } + @unlink($pidfile); + mwexec("/usr/local/sbin/dhcpleases -l {$g['dhcpd_chroot_path']}/var/db/dhcpd.leases -d {$config['system']['domain']} -p {$g['varrun_path']}/{$dns_pid} {$unbound_conf} -h {$g['varetc_path']}/hosts"); + } else { + sigkillbypid($pidfile, "TERM"); + @unlink($pidfile); + } +} + +function system_hostname_configure() { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_hostname_configure() being called $mt\n"; + } + + $syscfg = $config['system']; + + /* set hostname */ + $status = mwexec("/bin/hostname " . + escapeshellarg("{$syscfg['hostname']}.{$syscfg['domain']}")); + + /* Setup host GUID ID. This is used by ZFS. */ + mwexec("/etc/rc.d/hostid start"); + + return $status; +} + +function system_routing_configure($interface = "") { + global $config, $g; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_routing_configure() being called $mt\n"; + } + + $gatewayip = ""; + $interfacegw = ""; + $gatewayipv6 = ""; + $interfacegwv6 = ""; + $foundgw = false; + $foundgwv6 = false; + /* tack on all the hard defined gateways as well */ + if (is_array($config['gateways']['gateway_item'])) { + array_map('unlink', glob("{$g['tmp_path']}/*_defaultgw{,v6}", GLOB_BRACE)); + foreach ($config['gateways']['gateway_item'] as $gateway) { + if (isset($gateway['defaultgw'])) { + if ($foundgw == false && ($gateway['ipprotocol'] != "inet6" && (is_ipaddrv4($gateway['gateway']) || $gateway['gateway'] == "dynamic"))) { + if (strpos($gateway['gateway'], ":")) { + continue; + } + if ($gateway['gateway'] == "dynamic") { + $gateway['gateway'] = get_interface_gateway($gateway['interface']); + } + $gatewayip = $gateway['gateway']; + $interfacegw = $gateway['interface']; + if (!empty($gateway['interface'])) { + $defaultif = get_real_interface($gateway['interface']); + if ($defaultif) { + @file_put_contents("{$g['tmp_path']}/{$defaultif}_defaultgw", $gateway['gateway']); + } + } + $foundgw = true; + } else if ($foundgwv6 == false && ($gateway['ipprotocol'] == "inet6" && (is_ipaddrv6($gateway['gateway']) || $gateway['gateway'] == "dynamic"))) { + if ($gateway['gateway'] == "dynamic") { + $gateway['gateway'] = get_interface_gateway_v6($gateway['interface']); + } + $gatewayipv6 = $gateway['gateway']; + $interfacegwv6 = $gateway['interface']; + if (!empty($gateway['interface'])) { + $defaultifv6 = get_real_interface($gateway['interface']); + if ($defaultifv6) { + @file_put_contents("{$g['tmp_path']}/{$defaultifv6}_defaultgwv6", $gateway['gateway']); + } + } + $foundgwv6 = true; + } + } + if ($foundgw === true && $foundgwv6 === true) { + break; + } + } + } + if ($foundgw == false) { + $defaultif = get_real_interface("wan"); + $interfacegw = "wan"; + $gatewayip = get_interface_gateway("wan"); + @file_put_contents("{$g['tmp_path']}/{$defaultif}_defaultgw", $gatewayip); + } + if ($foundgwv6 == false) { + $defaultifv6 = get_real_interface("wan"); + $interfacegwv6 = "wan"; + $gatewayipv6 = get_interface_gateway_v6("wan"); + @file_put_contents("{$g['tmp_path']}/{$defaultifv6}_defaultgwv6", $gatewayipv6); + } + $dont_add_route = false; + /* if OLSRD is enabled, allow WAN to house DHCP. */ + if (is_array($config['installedpackages']['olsrd'])) { + foreach ($config['installedpackages']['olsrd']['config'] as $olsrd) { + if (($olsrd['enabledyngw'] == "on") && ($olsrd['enable'] == "on")) { + $dont_add_route = true; + log_error(sprintf(gettext("Not adding default route because OLSR dynamic gateway is enabled."))); + break; + } + } + } + + if ($dont_add_route == false) { + if (!empty($interface) && $interface != $interfacegw) { + ; + } else if (is_ipaddrv4($gatewayip)) { + log_error("ROUTING: setting default route to $gatewayip"); + mwexec("/sbin/route change -inet default " . escapeshellarg($gatewayip)); + } + + if (!empty($interface) && $interface != $interfacegwv6) { + ; + } else if (is_ipaddrv6($gatewayipv6)) { + $ifscope = ""; + if (is_linklocal($gatewayipv6) && !strpos($gatewayipv6, '%')) { + $ifscope = "%{$defaultifv6}"; + } + log_error("ROUTING: setting IPv6 default route to {$gatewayipv6}{$ifscope}"); + mwexec("/sbin/route change -inet6 default " . escapeshellarg("{$gatewayipv6}{$ifscope}")); + } + } + + system_staticroutes_configure($interface, false); + + return 0; +} + +function system_staticroutes_configure($interface = "", $update_dns = false) { + global $config, $g, $aliastable; + + $filterdns_list = array(); + + $static_routes = get_staticroutes(false, true); + if (count($static_routes)) { + $gateways_arr = return_gateways_array(false, true); + + foreach ($static_routes as $rtent) { + if (empty($gateways_arr[$rtent['gateway']])) { + log_error(sprintf(gettext("Static Routes: Gateway IP could not be found for %s"), $rtent['network'])); + continue; + } + $gateway = $gateways_arr[$rtent['gateway']]; + if (!empty($interface) && $interface != $gateway['friendlyiface']) { + continue; + } + + $gatewayip = $gateway['gateway']; + $interfacegw = $gateway['interface']; + + $blackhole = ""; + if (!strcasecmp("Null", substr($rtent['gateway'], 0, 3))) { + $blackhole = "-blackhole"; + } + + if (!is_fqdn($rtent['network']) && !is_subnet($rtent['network'])) { + continue; + } + + $dnscache = array(); + if ($update_dns === true) { + if (is_subnet($rtent['network'])) { + continue; + } + $dnscache = explode("\n", trim(compare_hostname_to_dnscache($rtent['network']))); + if (empty($dnscache)) { + continue; + } + } + + if (is_subnet($rtent['network'])) { + $ips = array($rtent['network']); + } else { + if (!isset($rtent['disabled'])) { + $filterdns_list[] = $rtent['network']; + } + $ips = add_hostname_to_watch($rtent['network']); + } + + foreach ($dnscache as $ip) { + if (in_array($ip, $ips)) { + continue; + } + mwexec("/sbin/route delete " . escapeshellarg($ip), true); + if (isset($config['system']['route-debug'])) { + $mt = microtime(); + log_error("ROUTING debug: $mt - route delete $ip "); + } + } + + if (isset($rtent['disabled'])) { + /* XXX: This can break things by deleting routes that shouldn't be deleted - OpenVPN, dynamic routing scenarios, etc. redmine #3709 */ + foreach ($ips as $ip) { + mwexec("/sbin/route delete " . escapeshellarg($ip), true); + if (isset($config['system']['route-debug'])) { + $mt = microtime(); + log_error("ROUTING debug: $mt - route delete $ip "); + } + } + continue; + } + + foreach ($ips as $ip) { + if (is_ipaddrv4($ip)) { + $ip .= "/32"; + } + // do NOT do the same check here on v6, is_ipaddrv6 returns true when including the CIDR mask. doing so breaks v6 routes + + $inet = (is_subnetv6($ip) ? "-inet6" : "-inet"); + + $cmd = "/sbin/route change {$inet} {$blackhole} " . escapeshellarg($ip) . " "; + + if (is_subnet($ip)) { + if (is_ipaddr($gatewayip)) { + mwexec($cmd . escapeshellarg($gatewayip)); + if (isset($config['system']['route-debug'])) { + $mt = microtime(); + log_error("ROUTING debug: $mt - $cmd $gatewayip"); + } + } else if (!empty($interfacegw)) { + mwexec($cmd . "-iface " . escapeshellarg($interfacegw)); + if (isset($config['system']['route-debug'])) { + $mt = microtime(); + log_error("ROUTING debug: $mt - $cmd -iface $interfacegw "); + } + } + } + } + } + unset($gateways_arr); + } + unset($static_routes); + + if ($update_dns === false) { + if (count($filterdns_list)) { + $interval = 60; + $hostnames = ""; + array_unique($filterdns_list); + foreach ($filterdns_list as $hostname) { + $hostnames .= "cmd {$hostname} '/usr/local/sbin/pfSctl -c \"service reload routedns\"'\n"; + } + file_put_contents("{$g['varetc_path']}/filterdns-route.hosts", $hostnames); + unset($hostnames); + + if (isvalidpid("{$g['varrun_path']}/filterdns-route.pid")) { + sigkillbypid("{$g['varrun_path']}/filterdns-route.pid", "HUP"); + } else { + mwexec("/usr/local/sbin/filterdns -p {$g['varrun_path']}/filterdns-route.pid -i {$interval} -c {$g['varetc_path']}/filterdns-route.hosts -d 1"); + } + } else { + killbypid("{$g['varrun_path']}/filterdns-route.pid"); + @unlink("{$g['varrun_path']}/filterdns-route.pid"); + } + } + unset($filterdns_list); + + return 0; +} + +function system_routing_enable() { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_routing_enable() being called $mt\n"; + } + + set_sysctl(array( + "net.inet.ip.forwarding" => "1", + "net.inet6.ip6.forwarding" => "1" + )); + + return; +} + +function system_syslogd_fixup_server($server) { + /* If it's an IPv6 IP alone, encase it in brackets */ + if (is_ipaddrv6($server)) { + return "[$server]"; + } else { + return $server; + } +} + +function system_syslogd_get_remote_servers($syslogcfg, $facility = "*.*") { + // Rather than repeatedly use the same code, use this function to build a list of remote servers. + $facility .= " ". + $remote_servers = ""; + $pad_to = 56; + $padding = ceil(($pad_to - strlen($facility))/8)+1; + if ($syslogcfg['remoteserver']) { + $remote_servers .= "{$facility}" . str_repeat("\t", $padding) . "@" . system_syslogd_fixup_server($syslogcfg['remoteserver']) . "\n"; + } + if ($syslogcfg['remoteserver2']) { + $remote_servers .= "{$facility}" . str_repeat("\t", $padding) . "@" . system_syslogd_fixup_server($syslogcfg['remoteserver2']) . "\n"; + } + if ($syslogcfg['remoteserver3']) { + $remote_servers .= "{$facility}" . str_repeat("\t", $padding) . "@" . system_syslogd_fixup_server($syslogcfg['remoteserver3']) . "\n"; + } + return $remote_servers; +} + +function system_syslogd_start() { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_syslogd_start() being called $mt\n"; + } + + mwexec("/etc/rc.d/hostid start"); + + $syslogcfg = $config['syslog']; + + if (platform_booting()) { + echo gettext("Starting syslog..."); + } + + if (is_process_running("fifolog_writer")) { + mwexec('/bin/pkill fifolog_writer'); + } + + // Which logging type are we using this week?? + if (isset($config['system']['disablesyslogclog'])) { + $log_directive = ""; + $log_create_directive = "/usr/bin/touch "; + $log_size = ""; + } else if (isset($config['system']['usefifolog'])) { + $log_directive = "|/usr/sbin/fifolog_writer "; + $log_size = isset($config['syslog']['logfilesize']) ? $config['syslog']['logfilesize'] : "10240"; + $log_create_directive = "/usr/sbin/fifolog_create -s "; + } else { // Defaults to CLOG + $log_directive = "%"; + $log_size = isset($config['syslog']['logfilesize']) ? $config['syslog']['logfilesize'] : "10240"; + $log_create_directive = "/usr/local/sbin/clog -i -s "; + } + + $syslogd_extra = ""; + if (isset($syslogcfg)) { + $separatelogfacilities = array('ntp', 'ntpd', 'ntpdate', 'charon', 'ipsec_starter', 'openvpn', 'pptps', 'poes', 'l2tps', 'relayd', 'hostapd', 'dnsmasq', 'filterdns', 'unbound', 'dhcpd', 'dhcrelay', 'dhclient', 'dhcp6c', 'apinger', 'radvd', 'routed', 'olsrd', 'zebra', 'ospfd', 'bgpd', 'miniupnpd', 'filterlog'); + $syslogconf = ""; + if ($config['installedpackages']['package']) { + foreach ($config['installedpackages']['package'] as $package) { + if ($package['logging']) { + array_push($separatelogfacilities, $package['logging']['facilityname']); + if (!is_file($g['varlog_path'].'/'.$package['logging']['logfilename'])) { + mwexec("{$log_create_directive} {$log_size} {$g['varlog_path']}/{$package['logging']['logfilename']}"); + } + $syslogconf .= "!{$package['logging']['facilityname']}\n*.*\t\t\t\t\t\t {$log_directive}{$g['varlog_path']}/{$package['logging']['logfilename']}\n"; + } + } + } + $facilitylist = implode(',', array_unique($separatelogfacilities)); + $syslogconf .= "!radvd,routed,olsrd,zebra,ospfd,bgpd,miniupnpd\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/routing.log\n"; + } + + $syslogconf .= "!ntp,ntpd,ntpdate\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/ntpd.log\n"; + } + + $syslogconf .= "!ppp\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/ppp.log\n"; + } + + $syslogconf .= "!pptps\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/pptps.log\n"; + } + + $syslogconf .= "!poes\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/poes.log\n"; + } + + $syslogconf .= "!l2tps\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/l2tps.log\n"; + } + + $syslogconf .= "!charon,ipsec_starter\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/ipsec.log\n"; + } + if (isset($syslogcfg['vpn'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "*.*"); + } + + $syslogconf .= "!openvpn\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/openvpn.log\n"; + } + if (isset($syslogcfg['vpn'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "*.*"); + } + + $syslogconf .= "!apinger\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/gateways.log\n"; + } + if (isset($syslogcfg['apinger'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "*.*"); + } + + $syslogconf .= "!dnsmasq,filterdns,unbound\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/resolver.log\n"; + } + + $syslogconf .= "!dhcpd,dhcrelay,dhclient,dhcp6c\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/dhcpd.log\n"; + } + if (isset($syslogcfg['dhcp'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "*.*"); + } + + $syslogconf .= "!relayd\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/relayd.log\n"; + } + if (isset($syslogcfg['relayd'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "*.*"); + } + + $syslogconf .= "!hostapd\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/wireless.log\n"; + } + if (isset($syslogcfg['hostapd'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "*.*"); + } + + $syslogconf .= "!filterlog\n"; + $syslogconf .= "*.* {$log_directive}{$g['varlog_path']}/filter.log\n"; + if (isset($syslogcfg['filter'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "*.*"); + } + + $syslogconf .= "!-{$facilitylist}\n"; + if (!isset($syslogcfg['disablelocallogging'])) { + $syslogconf .= <<<EOD +local3.* {$log_directive}{$g['varlog_path']}/vpn.log +local4.* {$log_directive}{$g['varlog_path']}/portalauth.log +local7.* {$log_directive}{$g['varlog_path']}/dhcpd.log +*.notice;kern.debug;lpr.info;mail.crit;daemon.none; {$log_directive}{$g['varlog_path']}/system.log +news.err;local0.none;local3.none;local4.none; {$log_directive}{$g['varlog_path']}/system.log +local7.none {$log_directive}{$g['varlog_path']}/system.log +security.* {$log_directive}{$g['varlog_path']}/system.log +auth.info;authpriv.info;daemon.info {$log_directive}{$g['varlog_path']}/system.log +auth.info;authpriv.info |exec /usr/local/sbin/sshlockout_pf 15 +*.emerg * + +EOD; + } + if (isset($syslogcfg['vpn'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "local3.*"); + } + if (isset($syslogcfg['portalauth'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "local4.*"); + } + if (isset($syslogcfg['dhcp'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "local7.*"); + } + if (isset($syslogcfg['system'])) { + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "*.notice;kern.debug;lpr.info;mail.crit;"); + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "news.err;local0.none;local3.none;local7.none"); + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "security.*"); + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "auth.info;authpriv.info;daemon.info"); + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "*.emerg"); + } + if (isset($syslogcfg['logall'])) { + // Make everything mean everything, including facilities excluded above. + $syslogconf .= "!*\n"; + $syslogconf .= system_syslogd_get_remote_servers($syslogcfg, "*.*"); + } + + if (isset($syslogcfg['zmqserver'])) { + $syslogconf .= <<<EOD +*.* ^{$syslogcfg['zmqserver']} + +EOD; + } + /* write syslog.conf */ + if (!@file_put_contents("{$g['varetc_path']}/syslog.conf", $syslogconf)) { + printf(gettext("Error: cannot open syslog.conf in system_syslogd_start().%s"), "\n"); + unset($syslogconf); + return 1; + } + unset($syslogconf); + + // Ensure that the log directory exists + if (!is_dir("{$g['dhcpd_chroot_path']}/var/run")) { + exec("/bin/mkdir -p {$g['dhcpd_chroot_path']}/var/run"); + } + + $sourceip = ""; + if (!empty($syslogcfg['sourceip'])) { + if ($syslogcfg['ipproto'] == "ipv6") { + $ifaddr = is_ipaddr($syslogcfg['sourceip']) ? $syslogcfg['sourceip'] : get_interface_ipv6($syslogcfg['sourceip']); + if (!is_ipaddr($ifaddr)) { + $ifaddr = get_interface_ip($syslogcfg['sourceip']); + } + } else { + $ifaddr = is_ipaddr($syslogcfg['sourceip']) ? $syslogcfg['sourceip'] : get_interface_ip($syslogcfg['sourceip']); + if (!is_ipaddr($ifaddr)) { + $ifaddr = get_interface_ipv6($syslogcfg['sourceip']); + } + } + if (is_ipaddr($ifaddr)) { + $sourceip = "-b {$ifaddr}"; + } + } + + $syslogd_extra = "-f {$g['varetc_path']}/syslog.conf {$sourceip}"; + } + + if (isvalidpid("{$g['varrun_path']}/syslog.pid")) { + sigkillbypid("{$g['varrun_path']}/syslog.pid", "TERM"); + usleep(100000); // syslogd often doesn't respond to a TERM quickly enough for the starting of syslogd below to be successful + } + + if (isvalidpid("{$g['varrun_path']}/syslog.pid")) { + // if it still hasn't responded to the TERM, KILL it. + sigkillbypid("{$g['varrun_path']}/syslog.pid", "KILL"); + usleep(100000); + } + + + $retval = mwexec_bg("/usr/sbin/syslogd -s -c -c -l {$g['dhcpd_chroot_path']}/var/run/log -P {$g['varrun_path']}/syslog.pid {$syslogd_extra}"); + + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + + return $retval; +} + +function system_webgui_create_certificate() { + global $config, $g; + + if (!is_array($config['ca'])) { + $config['ca'] = array(); + } + $a_ca =& $config['ca']; + if (!is_array($config['cert'])) { + $config['cert'] = array(); + } + $a_cert =& $config['cert']; + log_error("Creating SSL Certificate for this host"); + + $cert = array(); + $cert['refid'] = uniqid(); + $cert['descr'] = gettext("webConfigurator default ({$cert['refid']})"); + + $dn = array( + 'countryName' => "US", + 'stateOrProvinceName' => "State", + 'localityName' => "Locality", + 'organizationName' => "{$g['product_name']} webConfigurator Self-Signed Certificate", + 'emailAddress' => "admin@{$config['system']['hostname']}.{$config['system']['domain']}", + 'commonName' => "{$config['system']['hostname']}-{$cert['refid']}"); + $old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */ + if (!cert_create($cert, null, 2048, 2000, $dn, "self-signed", "sha256")) { + while ($ssl_err = openssl_error_string()) { + log_error("Error creating WebGUI Certificate: openssl library returns: " . $ssl_err); + } + error_reporting($old_err_level); + return null; + } + error_reporting($old_err_level); + + $a_cert[] = $cert; + $config['system']['webgui']['ssl-certref'] = $cert['refid']; + write_config(gettext("Generated new self-signed HTTPS certificate ({$cert['refid']})")); + return $cert; +} + +function system_webgui_start() { + global $config, $g; + + if (platform_booting()) { + echo gettext("Starting webConfigurator..."); + } + + chdir($g['www_path']); + + /* defaults */ + $portarg = "80"; + $crt = ""; + $key = ""; + $ca = ""; + + /* non-standard port? */ + if (isset($config['system']['webgui']['port']) && $config['system']['webgui']['port'] <> "") { + $portarg = "{$config['system']['webgui']['port']}"; + } + + if ($config['system']['webgui']['protocol'] == "https") { + // Ensure that we have a webConfigurator CERT + $cert =& lookup_cert($config['system']['webgui']['ssl-certref']); + if (!is_array($cert) || !$cert['crt'] || !$cert['prv']) { + $cert = system_webgui_create_certificate(); + } + $crt = base64_decode($cert['crt']); + $key = base64_decode($cert['prv']); + + if (!$config['system']['webgui']['port']) { + $portarg = "443"; + } + $ca = ca_chain($cert); + } + + /* generate lighttpd configuration */ + system_generate_lighty_config("{$g['varetc_path']}/lighty-webConfigurator.conf", + $crt, $key, $ca, "lighty-webConfigurator.pid", $portarg, "/usr/local/www/", + "cert.pem", "ca.pem"); + + /* kill any running lighttpd */ + killbypid("{$g['varrun_path']}/lighty-webConfigurator.pid"); + + sleep(1); + + @unlink("{$g['varrun_path']}/lighty-webConfigurator.pid"); + + /* attempt to start lighthttpd */ + $res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-webConfigurator.conf"); + + if (platform_booting()) { + if ($res == 0) { + echo gettext("done.") . "\n"; + } else { + echo gettext("failed!") . "\n"; + } + } + + return $res; +} + +function system_generate_lighty_config($filename, + $cert, + $key, + $ca, + $pid_file, + $port = 80, + $document_root = "/usr/local/www/", + $cert_location = "cert.pem", + $ca_location = "ca.pem", + $captive_portal = false) { + + global $config, $g; + + if (!is_dir("{$g['tmp_path']}/lighttpdcompress")) { + mkdir("{$g['tmp_path']}/lighttpdcompress"); + } + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_generate_lighty_config() being called $mt\n"; + } + + if ($captive_portal !== false) { + $captiveportal = ",\"mod_rewrite\",\"mod_evasive\""; + $captive_portal_rewrite = "url.rewrite-once = ( \"(.*captiveportal.*)\" => \"$1\", \"(.*)\" => \"/index.php?zone={$captive_portal}&redirurl=$1\" )\n"; + + $maxprocperip = $config['captiveportal'][$captive_portal]['maxprocperip']; + if (empty($maxprocperip)) { + $maxprocperip = 10; + } + $captive_portal_mod_evasive = "evasive.max-conns-per-ip = {$maxprocperip}"; + + $server_upload_dirs = "server.upload-dirs = ( \"{$g['tmp_path']}/captiveportal/\" )\n"; + if (!is_dir("{$g['tmp_path']}/captiveportal")) { + @mkdir("{$g['tmp_path']}/captiveportal", 0555); + } + $server_max_request_size = "server.max-request-size = 384"; + $cgi_config = ""; + } else { + $captiveportal = ",\"mod_cgi\""; + $captive_portal_rewrite = ""; + $captive_portal_mod_evasive = ""; + $server_upload_dirs = "server.upload-dirs = ( \"{$g['upload_path']}/\", \"{$g['tmp_path']}/\", \"/var/\" )\n"; + $server_max_request_size = "server.max-request-size = 2097152"; + $cgi_config = "cgi.assign = ( \".cgi\" => \"\" )"; + } + + if (empty($port)) { + $lighty_port = "80"; + } else { + $lighty_port = $port; + } + + $memory = get_memory(); + $realmem = $memory[1]; + + // Determine web GUI process settings and take into account low memory systems + if ($realmem < 255) { + $max_procs = 1; + } else { + $max_procs = ($config['system']['webgui']['max_procs']) ? $config['system']['webgui']['max_procs'] : 2; + } + + // Ramp up captive portal max procs, assuming each PHP process can consume up to 64MB RAM + if ($captive_portal !== false) { + if ($realmem > 135 and $realmem < 256) { + $max_procs += 1; // 2 worker processes + } else if ($realmem > 255 and $realmem < 513) { + $max_procs += 2; // 3 worker processes + } else if ($realmem > 512) { + $max_procs += 4; // 6 worker processes + } + if ($max_procs > 1) { + $max_php_children = intval($max_procs/2); + } else { + $max_php_children = 1; + } + + } else { + if ($realmem < 78) { + $max_php_children = 0; + } else { + $max_php_children = 1; + } + } + + if (!isset($config['syslog']['nologlighttpd'])) { + $lighty_use_syslog = <<<EOD +## where to send error-messages to +server.errorlog-use-syslog="enable" +EOD; + } + + + if ($captive_portal !== false) { + $fast_cgi_path = "{$g['tmp_path']}/php-fastcgi-{$captive_portal}.socket"; + $fastcgi_config = <<<EOD +#### fastcgi module +## read fastcgi.txt for more info +fastcgi.server = ( ".php" => + ( "localhost" => + ( + "socket" => "{$fast_cgi_path}", + "max-procs" => {$max_procs}, + "bin-environment" => ( + "PHP_FCGI_CHILDREN" => "{$max_php_children}", + "PHP_FCGI_MAX_REQUESTS" => "500" + ), + "bin-path" => "/usr/local/bin/php-cgi" + ) + ) +) + +EOD; + } else { + $fast_cgi_path = "{$g['varrun_path']}/php-fpm.socket"; + $fastcgi_config = <<<EOD +#### fastcgi module +## read fastcgi.txt for more info +fastcgi.server = ( ".php" => + ( "localhost" => + ( + "socket" => "{$fast_cgi_path}", + "broken-scriptfilename" => "enable" + ) + ) +) + +EOD; + } + + + $lighty_config = <<<EOD +# +# lighttpd configuration file +# +# use a it as base for lighttpd 1.0.0 and above +# +############ Options you really have to take care of #################### + +## FreeBSD! +server.event-handler = "freebsd-kqueue" +server.network-backend = "writev" +#server.use-ipv6 = "enable" + +## modules to load +server.modules = ( "mod_access", "mod_expire", "mod_compress", "mod_redirect", + {$captiveportal}, "mod_fastcgi" +) + +server.max-keep-alive-requests = 15 +server.max-keep-alive-idle = 30 + +## a static document-root, for virtual-hosting take look at the +## server.virtual-* options +server.document-root = "{$document_root}" +{$captive_portal_rewrite} + +# Maximum idle time with nothing being written (php downloading) +server.max-write-idle = 999 + +{$lighty_use_syslog} + +# files to check for if .../ is requested +server.indexfiles = ( "index.php", "index.html", + "index.htm", "default.htm" ) + +# mimetype mapping +mimetype.assign = ( + ".pdf" => "application/pdf", + ".sig" => "application/pgp-signature", + ".spl" => "application/futuresplash", + ".class" => "application/octet-stream", + ".ps" => "application/postscript", + ".torrent" => "application/x-bittorrent", + ".dvi" => "application/x-dvi", + ".gz" => "application/x-gzip", + ".pac" => "application/x-ns-proxy-autoconfig", + ".swf" => "application/x-shockwave-flash", + ".tar.gz" => "application/x-tgz", + ".tgz" => "application/x-tgz", + ".tar" => "application/x-tar", + ".zip" => "application/zip", + ".mp3" => "audio/mpeg", + ".m3u" => "audio/x-mpegurl", + ".wma" => "audio/x-ms-wma", + ".wax" => "audio/x-ms-wax", + ".ogg" => "audio/x-wav", + ".wav" => "audio/x-wav", + ".gif" => "image/gif", + ".jpg" => "image/jpeg", + ".jpeg" => "image/jpeg", + ".png" => "image/png", + ".xbm" => "image/x-xbitmap", + ".xpm" => "image/x-xpixmap", + ".xwd" => "image/x-xwindowdump", + ".css" => "text/css", + ".html" => "text/html", + ".htm" => "text/html", + ".js" => "text/javascript", + ".asc" => "text/plain", + ".c" => "text/plain", + ".conf" => "text/plain", + ".text" => "text/plain", + ".txt" => "text/plain", + ".dtd" => "text/xml", + ".xml" => "text/xml", + ".mpeg" => "video/mpeg", + ".mpg" => "video/mpeg", + ".mov" => "video/quicktime", + ".qt" => "video/quicktime", + ".avi" => "video/x-msvideo", + ".asf" => "video/x-ms-asf", + ".asx" => "video/x-ms-asf", + ".wmv" => "video/x-ms-wmv", + ".bz2" => "application/x-bzip", + ".tbz" => "application/x-bzip-compressed-tar", + ".tar.bz2" => "application/x-bzip-compressed-tar" + ) + +# Use the "Content-Type" extended attribute to obtain mime type if possible +#mimetypes.use-xattr = "enable" + +## deny access the file-extensions +# +# ~ is for backupfiles from vi, emacs, joe, ... +# .inc is often used for code includes which should in general not be part +# of the document-root +url.access-deny = ( "~", ".inc" ) + + +######### Options that are good to be but not necessary to be changed ####### + +## disable server header +server.tag = "" + +## bind to port (default: 80) + +EOD; + + $lighty_config .= "server.bind = \"0.0.0.0\"\n"; + $lighty_config .= "server.port = {$lighty_port}\n"; + $lighty_config .= "\$SERVER[\"socket\"] == \"0.0.0.0:{$lighty_port}\" { }\n"; + $lighty_config .= "\$SERVER[\"socket\"] == \"[::]:{$lighty_port}\" { \n"; + if ($cert <> "" and $key <> "") { + $lighty_config .= "\n"; + $lighty_config .= "## ssl configuration\n"; + $lighty_config .= "ssl.engine = \"enable\"\n"; + $lighty_config .= "ssl.pemfile = \"{$g['varetc_path']}/{$cert_location}\"\n\n"; + if ($ca <> "") { + $lighty_config .= "ssl.ca-file = \"{$g['varetc_path']}/{$ca_location}\"\n\n"; + } + } + $lighty_config .= " }\n"; + + + $lighty_config .= <<<EOD + +## error-handler for status 404 +#server.error-handler-404 = "/error-handler.html" +#server.error-handler-404 = "/error-handler.php" + +## to help the rc.scripts +server.pid-file = "{$g['varrun_path']}/{$pid_file}" + +## virtual directory listings +server.dir-listing = "disable" + +## enable debugging +debug.log-request-header = "disable" +debug.log-response-header = "disable" +debug.log-request-handling = "disable" +debug.log-file-not-found = "disable" + +# gzip compression +compress.cache-dir = "{$g['tmp_path']}/lighttpdcompress/" +compress.filetype = ("text/plain","text/css", "text/xml", "text/javascript" ) + +{$server_upload_dirs} + +{$server_max_request_size} + +{$fastcgi_config} + +{$cgi_config} + +{$captive_portal_mod_evasive} + +expire.url = ( + "" => "access 50 hours", + ) + +EOD; + + $cert = str_replace("\r", "", $cert); + $key = str_replace("\r", "", $key); + $ca = str_replace("\r", "", $ca); + + $cert = str_replace("\n\n", "\n", $cert); + $key = str_replace("\n\n", "\n", $key); + $ca = str_replace("\n\n", "\n", $ca); + + if ($cert <> "" and $key <> "") { + $fd = fopen("{$g['varetc_path']}/{$cert_location}", "w"); + if (!$fd) { + printf(gettext("Error: cannot open cert.pem in system_webgui_start().%s"), "\n"); + return 1; + } + chmod("{$g['varetc_path']}/{$cert_location}", 0600); + fwrite($fd, $cert); + fwrite($fd, "\n"); + fwrite($fd, $key); + fclose($fd); + if (!(empty($ca) || (strlen(trim($ca)) == 0))) { + $fd = fopen("{$g['varetc_path']}/{$ca_location}", "w"); + if (!$fd) { + printf(gettext("Error: cannot open ca.pem in system_webgui_start().%s"), "\n"); + return 1; + } + chmod("{$g['varetc_path']}/{$ca_location}", 0600); + fwrite($fd, $ca); + fclose($fd); + } + $lighty_config .= "\n"; + $lighty_config .= "## " . gettext("ssl configuration") . "\n"; + $lighty_config .= "ssl.engine = \"enable\"\n"; + $lighty_config .= "ssl.pemfile = \"{$g['varetc_path']}/{$cert_location}\"\n\n"; + + // SSLv2/3 is deprecated, force use of TLS + $lighty_config .= "ssl.use-sslv2 = \"disable\"\n"; + $lighty_config .= "ssl.use-sslv3 = \"disable\"\n"; + + // where ssl.cipher-list is set, this is automatically enabled, but set it explicitly anyway. + $lighty_config .= "ssl.honor-cipher-order = \"enable\"\n"; + + $lighty_config .= "ssl.cipher-list = \"AES128+EECDH:AES256+EECDH:AES128+EDH:AES256+EDH:AES128-SHA:AES256-SHA:!aNULL:!eNULL:!DSS\"\n"; + + if (!(empty($ca) || (strlen(trim($ca)) == 0))) { + $lighty_config .= "ssl.ca-file = \"{$g['varetc_path']}/{$ca_location}\"\n\n"; + } + } + + // Add HTTP to HTTPS redirect + if ($captive_portal === false && $config['system']['webgui']['protocol'] == "https" && !isset($config['system']['webgui']['disablehttpredirect'])) { + if ($lighty_port != "443") { + $redirectport = ":{$lighty_port}"; + } + $lighty_config .= <<<EOD +\$SERVER["socket"] == ":80" { + \$HTTP["host"] =~ "(.*)" { + url.redirect = ( "^/(.*)" => "https://%1{$redirectport}/$1" ) + } +} +\$SERVER["socket"] == "[::]:80" { + \$HTTP["host"] =~ "(.*)" { + url.redirect = ( "^/(.*)" => "https://%1{$redirectport}/$1" ) + } +} +EOD; + } + + $fd = fopen("{$filename}", "w"); + if (!$fd) { + printf(gettext("Error: cannot open %s in system_generate_lighty_config().%s"), $filename, "\n"); + return 1; + } + fwrite($fd, $lighty_config); + fclose($fd); + + return 0; + +} + +function system_timezone_configure() { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_timezone_configure() being called $mt\n"; + } + + $syscfg = $config['system']; + + if (platform_booting()) { + echo gettext("Setting timezone..."); + } + + /* extract appropriate timezone file */ + $timezone = $syscfg['timezone']; + if ($timezone) { + exec('/usr/bin/tar -tvzf /usr/share/zoneinfo.tgz', $tzs); + foreach ($tzs as $tz) { + if (preg_match(",{$timezone}$,", $tz)) { + break; + } + if (preg_match(",{$timezone} link to *(.*)$,", $tz, $matches)) { + $timezone = $matches[1]; + break; + } + } + } else { + $timezone = "Etc/UTC"; + } + + conf_mount_rw(); + + exec("LANG=C /usr/bin/tar xzfO /usr/share/zoneinfo.tgz " . + escapeshellarg($timezone) . " > /etc/localtime"); + + mwexec("sync"); + conf_mount_ro(); + + if (platform_booting()) { + echo gettext("done.") . "\n"; + } +} + +function system_ntp_setup_gps($serialport) { + global $config, $g; + $gps_device = '/dev/gps0'; + $serialport = '/dev/'.$serialport; + + if (!file_exists($serialport)) { + return false; + } + + conf_mount_rw(); + // Create symlink that ntpd requires + unlink_if_exists($gps_device); + @symlink($serialport, $gps_device); + + $gpsbaud = '4800'; + if (is_array($config['ntpd']) && is_array($config['ntpd']['gps']) && !empty($config['ntpd']['gps']['speed'])) { + switch ($config['ntpd']['gps']['speed']) { + case '16': + $gpsbaud = '9600'; + break; + case '32': + $gpsbaud = '19200'; + break; + case '48': + $gpsbaud = '38400'; + break; + case '64': + $gpsbaud = '57600'; + break; + case '80': + $gpsbaud = '115200'; + break; + } + } + + /* Configure the serial port for raw IO and set the speed */ + mwexec("stty -f {$serialport}.init raw speed {$gpsbaud}"); + + /* Send the following to the GPS port to initialize the GPS */ + if (is_array($config['ntpd']) && is_array($config['ntpd']['gps']) && !empty($config['ntpd']['gps']['type'])) { + $gps_init = base64_decode($config['ntpd']['gps']['initcmd']); + } else { + $gps_init = base64_decode('JFBVQlgsNDAsR1NWLDAsMCwwLDAqNTkNCiRQVUJYLDQwLEdMTCwwLDAsMCwwKjVDDQokUFVCWCw0MCxaREEsMCwwLDAsMCo0NA0KJFBVQlgsNDAsVlRHLDAsMCwwLDAqNUUNCiRQVUJYLDQwLEdTViwwLDAsMCwwKjU5DQokUFVCWCw0MCxHU0EsMCwwLDAsMCo0RQ0KJFBVQlgsNDAsR0dBLDAsMCwwLDANCiRQVUJYLDQwLFRYVCwwLDAsMCwwDQokUFVCWCw0MCxSTUMsMCwwLDAsMCo0Ng0KJFBVQlgsNDEsMSwwMDA3LDAwMDMsNDgwMCwwDQokUFVCWCw0MCxaREEsMSwxLDEsMQ=='); + } + + /* XXX: Why not file_put_contents to the device */ + @file_put_contents('/tmp/gps.init', $gps_init); + mwexec("cat /tmp/gps.init > {$serialport}"); + + /* Add /etc/remote entry in case we need to read from the GPS with tip */ + if (intval(`grep -c '^gps0' /etc/remote`) == 0) { + @file_put_contents("/etc/remote", "gps0:dv={$serialport}:br#{$gpsbaud}:pa=none:", FILE_APPEND); + } + + conf_mount_ro(); + + return true; +} + +function system_ntp_setup_pps($serialport) { + global $config, $g; + + $pps_device = '/dev/pps0'; + $serialport = '/dev/'.$serialport; + + if (!file_exists($serialport)) { + return false; + } + + conf_mount_rw(); + // Create symlink that ntpd requires + unlink_if_exists($pps_device); + @symlink($serialport, $pps_device); + + conf_mount_ro(); + + return true; +} + + +function system_ntp_configure($start_ntpd=true) { + global $config, $g; + + $driftfile = "/var/db/ntpd.drift"; + $statsdir = "/var/log/ntp"; + $gps_device = '/dev/gps0'; + + safe_mkdir($statsdir); + + if (!is_array($config['ntpd'])) { + $config['ntpd'] = array(); + } + + $ntpcfg = "# \n"; + $ntpcfg .= "# pfSense ntp configuration file \n"; + $ntpcfg .= "# \n\n"; + $ntpcfg .= "tinker panic 0 \n"; + + /* Add Orphan mode */ + $ntpcfg .= "# Orphan mode stratum\n"; + $ntpcfg .= 'tos orphan '; + if (!empty($config['ntpd']['orphan'])) { + $ntpcfg .= $config['ntpd']['orphan']; + } else { + $ntpcfg .= '12'; + } + $ntpcfg .= "\n"; + + /* Add PPS configuration */ + if (is_array($config['ntpd']['pps']) && !empty($config['ntpd']['pps']['port']) && + file_exists('/dev/'.$config['ntpd']['pps']['port']) && + system_ntp_setup_pps($config['ntpd']['pps']['port'])) { + $ntpcfg .= "\n"; + $ntpcfg .= "# PPS Setup\n"; + $ntpcfg .= 'server 127.127.22.0'; + $ntpcfg .= ' minpoll 4 maxpoll 4'; + if (empty($config['ntpd']['pps']['prefer'])) { /*note: this one works backwards */ + $ntpcfg .= ' prefer'; + } + if (!empty($config['ntpd']['pps']['noselect'])) { + $ntpcfg .= ' noselect '; + } + $ntpcfg .= "\n"; + $ntpcfg .= 'fudge 127.127.22.0'; + if (!empty($config['ntpd']['pps']['fudge1'])) { + $ntpcfg .= ' time1 '; + $ntpcfg .= $config['ntpd']['pps']['fudge1']; + } + if (!empty($config['ntpd']['pps']['flag2'])) { + $ntpcfg .= ' flag2 1'; + } + if (!empty($config['ntpd']['pps']['flag3'])) { + $ntpcfg .= ' flag3 1'; + } else { + $ntpcfg .= ' flag3 0'; + } + if (!empty($config['ntpd']['pps']['flag4'])) { + $ntpcfg .= ' flag4 1'; + } + if (!empty($config['ntpd']['pps']['refid'])) { + $ntpcfg .= ' refid '; + $ntpcfg .= $config['ntpd']['pps']['refid']; + } + $ntpcfg .= "\n"; + } + /* End PPS configuration */ + + /* Add GPS configuration */ + if (is_array($config['ntpd']['gps']) && !empty($config['ntpd']['gps']['port']) && + file_exists('/dev/'.$config['ntpd']['gps']['port']) && + system_ntp_setup_gps($config['ntpd']['gps']['port'])) { + $ntpcfg .= "\n"; + $ntpcfg .= "# GPS Setup\n"; + $ntpcfg .= 'server 127.127.20.0 mode '; + if (!empty($config['ntpd']['gps']['nmea']) || !empty($config['ntpd']['gps']['speed']) || !empty($config['ntpd']['gps']['subsec'])) { + if (!empty($config['ntpd']['gps']['nmea'])) { + $ntpmode = (int) $config['ntpd']['gps']['nmea']; + } + if (!empty($config['ntpd']['gps']['speed'])) { + $ntpmode += (int) $config['ntpd']['gps']['speed']; + } + if (!empty($config['ntpd']['gps']['subsec'])) { + $ntpmode += 128; + } + $ntpcfg .= (string) $ntpmode; + } else { + $ntpcfg .= '0'; + } + $ntpcfg .= ' minpoll 4 maxpoll 4'; + if (empty($config['ntpd']['gps']['prefer'])) { /*note: this one works backwards */ + $ntpcfg .= ' prefer'; + } + if (!empty($config['ntpd']['gps']['noselect'])) { + $ntpcfg .= ' noselect '; + } + $ntpcfg .= "\n"; + $ntpcfg .= 'fudge 127.127.20.0'; + if (!empty($config['ntpd']['gps']['fudge1'])) { + $ntpcfg .= ' time1 '; + $ntpcfg .= $config['ntpd']['gps']['fudge1']; + } + if (!empty($config['ntpd']['gps']['fudge2'])) { + $ntpcfg .= ' time2 '; + $ntpcfg .= $config['ntpd']['gps']['fudge2']; + } + if (!empty($config['ntpd']['gps']['flag1'])) { + $ntpcfg .= ' flag1 1'; + } else { + $ntpcfg .= ' flag1 0'; + } + if (!empty($config['ntpd']['gps']['flag2'])) { + $ntpcfg .= ' flag2 1'; + } + if (!empty($config['ntpd']['gps']['flag3'])) { + $ntpcfg .= ' flag3 1'; + } else { + $ntpcfg .= ' flag3 0'; + } + if (!empty($config['ntpd']['gps']['flag4'])) { + $ntpcfg .= ' flag4 1'; + } + if (!empty($config['ntpd']['gps']['refid'])) { + $ntpcfg .= ' refid '; + $ntpcfg .= $config['ntpd']['gps']['refid']; + } + $ntpcfg .= "\n"; + } elseif (is_array($config['ntpd']) && !empty($config['ntpd']['gpsport']) && + file_exists('/dev/'.$config['ntpd']['gpsport']) && + system_ntp_setup_gps($config['ntpd']['gpsport'])) { + /* This handles a 2.1 and earlier config */ + $ntpcfg .= "# GPS Setup\n"; + $ntpcfg .= "server 127.127.20.0 mode 0 minpoll 4 maxpoll 4 prefer\n"; + $ntpcfg .= "fudge 127.127.20.0 time1 0.155 time2 0.000 flag1 1 flag2 0 flag3 1\n"; + // Fall back to local clock if GPS is out of sync? + $ntpcfg .= "server 127.127.1.0\n"; + $ntpcfg .= "fudge 127.127.1.0 stratum 12\n"; + } + /* End GPS configuration */ + + $ntpcfg .= "\n\n# Upstream Servers\n"; + /* foreach through ntp servers and write out to ntpd.conf */ + foreach (explode(' ', $config['system']['timeservers']) as $ts) { + $ntpcfg .= "server {$ts} iburst maxpoll 9"; + if (substr_count($config['ntpd']['prefer'], $ts)) { + $ntpcfg .= ' prefer'; + } + if (substr_count($config['ntpd']['noselect'], $ts)) { + $ntpcfg .= ' noselect'; + } + $ntpcfg .= "\n"; + } + unset($ts); + + $ntpcfg .= "\n\n"; + $ntpcfg .= "disable monitor\n"; //prevent NTP reflection attack, see https://forum.pfsense.org/index.php/topic,67189.msg389132.html#msg389132 + if (!empty($config['ntpd']['clockstats']) || !empty($config['ntpd']['loopstats']) || !empty($config['ntpd']['peerstats'])) { + $ntpcfg .= "enable stats\n"; + $ntpcfg .= 'statistics'; + if (!empty($config['ntpd']['clockstats'])) { + $ntpcfg .= ' clockstats'; + } + if (!empty($config['ntpd']['loopstats'])) { + $ntpcfg .= ' loopstats'; + } + if (!empty($config['ntpd']['peerstats'])) { + $ntpcfg .= ' peerstats'; + } + $ntpcfg .= "\n"; + } + $ntpcfg .= "statsdir {$statsdir}\n"; + $ntpcfg .= 'logconfig =syncall +clockall'; + if (!empty($config['ntpd']['logpeer'])) { + $ntpcfg .= ' +peerall'; + } + if (!empty($config['ntpd']['logsys'])) { + $ntpcfg .= ' +sysall'; + } + $ntpcfg .= "\n"; + $ntpcfg .= "driftfile {$driftfile}\n"; + /* Access restrictions */ + $ntpcfg .= 'restrict default'; + if (empty($config['ntpd']['kod'])) { /*note: this one works backwards */ + $ntpcfg .= ' kod limited'; + } + if (empty($config['ntpd']['nomodify'])) { /*note: this one works backwards */ + $ntpcfg .= ' nomodify'; + } + if (!empty($config['ntpd']['noquery'])) { + $ntpcfg .= ' noquery'; + } + if (empty($config['ntpd']['nopeer'])) { /*note: this one works backwards */ + $ntpcfg .= ' nopeer'; + } + if (empty($config['ntpd']['notrap'])) { /*note: this one works backwards */ + $ntpcfg .= ' notrap'; + } + if (!empty($config['ntpd']['noserve'])) { + $ntpcfg .= ' noserve'; + } + $ntpcfg .= "\nrestrict -6 default"; + if (empty($config['ntpd']['kod'])) { /*note: this one works backwards */ + $ntpcfg .= ' kod limited'; + } + if (empty($config['ntpd']['nomodify'])) { /*note: this one works backwards */ + $ntpcfg .= ' nomodify'; + } + if (!empty($config['ntpd']['noquery'])) { + $ntpcfg .= ' noquery'; + } + if (empty($config['ntpd']['nopeer'])) { /*note: this one works backwards */ + $ntpcfg .= ' nopeer'; + } + if (!empty($config['ntpd']['noserve'])) { + $ntpcfg .= ' noserve'; + } + if (empty($config['ntpd']['notrap'])) { /*note: this one works backwards */ + $ntpcfg .= ' notrap'; + } + $ntpcfg .= "\n"; + + /* A leapseconds file is really only useful if this clock is stratum 1 */ + $ntpcfg .= "\n"; + if (!empty($config['ntpd']['leapsec'])) { + $leapsec .= base64_decode($config['ntpd']['leapsec']); + file_put_contents('/var/db/leap-seconds', $leapsec); + $ntpcfg .= "leapfile /var/db/leap-seconds\n"; + } + + + if (empty($config['ntpd']['interface'])) { + if (is_array($config['installedpackages']['openntpd']) && !empty($config['installedpackages']['openntpd']['config'][0]['interface'])) { + $interfaces = explode(",", $config['installedpackages']['openntpd']['config'][0]['interface']); + } else { + $interfaces = array(); + } + } else { + $interfaces = explode(",", $config['ntpd']['interface']); + } + + if (is_array($interfaces) && count($interfaces)) { + $ntpcfg .= "interface ignore all\n"; + foreach ($interfaces as $interface) { + if (strstr($interface, "_vip")) { + $interface = get_configured_carp_interface_list($interface); + } + if (!is_ipaddr($interface)) { + $interface = get_real_interface($interface); + } + if (!empty($interface)) { + $ntpcfg .= "interface listen {$interface}\n"; + } + } + } + + /* open configuration for writing or bail */ + if (!@file_put_contents("{$g['varetc_path']}/ntpd.conf", $ntpcfg)) { + log_error("Could not open {$g['varetc_path']}/ntpd.conf for writing"); + return; + } + + /* At bootup we just want to write out the config. */ + if (!$start_ntpd) { + return; + } + + /* if ntpd is running, kill it */ + while (isvalidpid("{$g['varrun_path']}/ntpd.pid")) { + killbypid("{$g['varrun_path']}/ntpd.pid"); + } + @unlink("{$g['varrun_path']}/ntpd.pid"); + + /* if /var/empty does not exist, create it */ + if (!is_dir("/var/empty")) { + mkdir("/var/empty", 0775, true); + } + + /* start opentpd, set time now and use /var/etc/ntpd.conf */ + mwexec("/usr/local/sbin/ntpd -g -c {$g['varetc_path']}/ntpd.conf -p {$g['varrun_path']}/ntpd.pid", false, true); + + // Note that we are starting up + log_error("NTPD is starting up."); + return; +} + +function sync_system_time() { + global $config, $g; + + if (platform_booting()) { + echo gettext("Syncing system time before startup..."); + } + + /* foreach through servers and write out to ntpd.conf */ + foreach (explode(' ', $config['system']['timeservers']) as $ts) { + mwexec("/usr/local/sbin/ntpdate -s $ts"); + } + + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + +} + +function system_halt() { + global $g; + + system_reboot_cleanup(); + + mwexec("/usr/bin/nohup /etc/rc.halt > /dev/null 2>&1 &"); +} + +function system_reboot() { + global $g; + + system_reboot_cleanup(); + + mwexec("nohup /etc/rc.reboot > /dev/null 2>&1 &"); +} + +function system_reboot_sync() { + global $g; + + system_reboot_cleanup(); + + mwexec("/etc/rc.reboot > /dev/null 2>&1"); +} + +function system_reboot_cleanup() { + global $config, $cpzone; + + mwexec("/usr/local/bin/beep.sh stop"); + require_once("captiveportal.inc"); + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cpzone=>$cp) { + captiveportal_radius_stop_all(); + captiveportal_send_server_accounting(true); + } + } + require_once("voucher.inc"); + voucher_save_db_to_config(); + require_once("pkg-utils.inc"); + stop_packages(); +} + +function system_do_shell_commands($early = 0) { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_do_shell_commands() being called $mt\n"; + } + + if ($early) { + $cmdn = "earlyshellcmd"; + } else { + $cmdn = "shellcmd"; + } + + if (is_array($config['system'][$cmdn])) { + + /* *cmd is an array, loop through */ + foreach ($config['system'][$cmdn] as $cmd) { + exec($cmd); + } + + } elseif ($config['system'][$cmdn] <> "") { + + /* execute single item */ + exec($config['system'][$cmdn]); + + } +} + +function system_console_configure() { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_console_configure() being called $mt\n"; + } + + if (isset($config['system']['disableconsolemenu'])) { + touch("{$g['varetc_path']}/disableconsole"); + } else { + unlink_if_exists("{$g['varetc_path']}/disableconsole"); + } +} + +function system_dmesg_save() { + global $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_dmesg_save() being called $mt\n"; + } + + $dmesg = ""; + $_gb = exec("/sbin/dmesg", $dmesg); + + /* find last copyright line (output from previous boots may be present) */ + $lastcpline = 0; + + for ($i = 0; $i < count($dmesg); $i++) { + if (strstr($dmesg[$i], "Copyright (c) 1992-")) { + $lastcpline = $i; + } + } + + $fd = fopen("{$g['varlog_path']}/dmesg.boot", "w"); + if (!$fd) { + printf(gettext("Error: cannot open dmesg.boot in system_dmesg_save().%s"), "\n"); + return 1; + } + + for ($i = $lastcpline; $i < count($dmesg); $i++) { + fwrite($fd, $dmesg[$i] . "\n"); + } + + fclose($fd); + unset($dmesg); + + return 0; +} + +function system_set_harddisk_standby() { + global $g, $config; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_set_harddisk_standby() being called $mt\n"; + } + + if (isset($config['system']['harddiskstandby'])) { + if (platform_booting()) { + echo gettext('Setting hard disk standby... '); + } + + $standby = $config['system']['harddiskstandby']; + // Check for a numeric value + if (is_numeric($standby)) { + // Sync the disk(s) + pfSense_sync(); + if (set_single_sysctl('hw.ata.standby', (int)$standby)) { + // Reinitialize ATA-drives + mwexec('/usr/local/sbin/atareinit'); + if (platform_booting()) { + echo gettext("done.") . "\n"; + } + } else if (platform_booting()) { + echo gettext("failed!") . "\n"; + } + } else if (platform_booting()) { + echo gettext("failed!") . "\n"; + } + } +} + +function system_setup_sysctl() { + global $config; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_setup_sysctl() being called $mt\n"; + } + + activate_sysctls(); + + if (isset($config['system']['sharednet'])) { + system_disable_arp_wrong_if(); + } +} + +function system_disable_arp_wrong_if() { + global $config; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_disable_arp_wrong_if() being called $mt\n"; + } + set_sysctl(array( + "net.link.ether.inet.log_arp_wrong_iface" => "0", + "net.link.ether.inet.log_arp_movements" => "0" + )); +} + +function system_enable_arp_wrong_if() { + global $config; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "system_enable_arp_wrong_if() being called $mt\n"; + } + set_sysctl(array( + "net.link.ether.inet.log_arp_wrong_iface" => "1", + "net.link.ether.inet.log_arp_movements" => "1" + )); +} + +function enable_watchdog() { + global $config; + return; + $install_watchdog = false; + $supported_watchdogs = array("Geode"); + $file = file_get_contents("/var/log/dmesg.boot"); + foreach ($supported_watchdogs as $sd) { + if (stristr($file, "Geode")) { + $install_watchdog = true; + } + } + if ($install_watchdog == true) { + if (is_process_running("watchdogd")) { + mwexec("/usr/bin/killall watchdogd", true); + } + exec("/usr/sbin/watchdogd"); + } +} + +function system_check_reset_button() { + global $g; + + $specplatform = system_identify_specific_platform(); + + switch ($specplatform['name']) { + case 'alix': + case 'wrap': + case 'FW7541': + case 'APU': + case 'RCC-VE': + case 'RCC-DFF': + break; + default: + return 0; + } + + $retval = mwexec("/usr/local/sbin/" . $specplatform['name'] . "resetbtn"); + + if ($retval == 99) { + /* user has pressed reset button for 2 seconds - + reset to factory defaults */ + echo <<<EOD + +*********************************************************************** +* Reset button pressed - resetting configuration to factory defaults. * +* The system will reboot after this completes. * +*********************************************************************** + + +EOD; + + reset_factory_defaults(); + system_reboot_sync(); + exit(0); + } + + return 0; +} + +/* attempt to identify the specific platform (for embedded systems) + Returns an array with two elements: + name => platform string (e.g. 'wrap', 'alix' etc.) + descr => human-readable description (e.g. "PC Engines WRAP") +*/ +function system_identify_specific_platform() { + global $g; + + if ($g['platform'] == 'generic-pc') { + return array('name' => 'generic-pc', 'descr' => gettext("Generic PC")); + } + + if ($g['platform'] == 'generic-pc-cdrom') { + return array('name' => 'generic-pc-cdrom', 'descr' => gettext("Generic PC (CD-ROM)")); + } + + /* Try to guess from smbios strings */ + unset($output); + $_gb = exec('/bin/kenv smbios.system.product 2>/dev/null', $output); + switch ($output[0]) { + case 'FW7541': + return (array('name' => 'FW7541', 'descr' => 'Netgate FW7541')); + break; + case 'APU': + return (array('name' => 'APU', 'descr' => 'Netgate APU')); + break; + case 'RCC-VE': + return (array('name' => 'RCC-VE', 'descr' => 'Netgate RCC-VE')); + break; + case 'DFFv2': + return (array('name' => 'RCC-DFF', 'descr' => 'Netgate RCC-DFF')); + break; + case 'SYS-5018A-FTN4': + case 'A1SAi': + return (array('name' => 'C2758', 'descr' => 'Super Micro C2758')); + break; + case 'SYS-5018D-FN4T': + return (array('name' => 'D1540-XG', 'descr' => 'Super Micro D1540-XG')); + break; + } + + /* the rest of the code only deals with 'embedded' platforms */ + if ($g['platform'] != 'nanobsd') { + return array('name' => $g['platform'], 'descr' => $g['platform']); + } + + $dmesg = get_single_sysctl('hw.model'); + + if (strpos($dmesg, "PC Engines WRAP") !== false) { + return array('name' => 'wrap', 'descr' => gettext('PC Engines WRAP')); + } + + if (strpos($dmesg, "PC Engines ALIX") !== false) { + return array('name' => 'alix', 'descr' => gettext('PC Engines ALIX')); + } + + if (preg_match("/Soekris net45../", $dmesg, $matches)) { + return array('name' => 'net45xx', 'descr' => $matches[0]); + } + + if (preg_match("/Soekris net48../", $dmesg, $matches)) { + return array('name' => 'net48xx', 'descr' => $matches[0]); + } + + if (preg_match("/Soekris net55../", $dmesg, $matches)) { + return array('name' => 'net55xx', 'descr' => $matches[0]); + } + + unset($dmesg); + + $dmesg_boot = system_get_dmesg_boot(); + if (strpos($dmesg_boot, "PC Engines ALIX") !== false) { + return array('name' => 'alix', 'descr' => gettext('PC Engines ALIX')); + } + unset($dmesg_boot); + + /* unknown embedded platform */ + return array('name' => 'embedded', 'descr' => gettext('embedded (unknown)')); +} + +function system_get_dmesg_boot() { + global $g; + + return file_get_contents("{$g['varlog_path']}/dmesg.boot"); +} + +?> diff --git a/src/etc/inc/unbound.inc b/src/etc/inc/unbound.inc new file mode 100644 index 0000000..043ced2 --- /dev/null +++ b/src/etc/inc/unbound.inc @@ -0,0 +1,717 @@ +<?php +/* + unbound.inc + part of the pfSense project (https://www.pfsense.org) + Copyright (C) 2015 Warren Baker <warren@percol8.co.za> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + pfSense_BUILDER_BINARIES: /usr/local/sbin/unbound /usr/local/sbin/unbound-anchor /usr/local/sbin/unbound-checkconf + pfSense_BUILDER_BINARIES: /usr/local/sbin/unbound-control /usr/local/sbin/unbound-control-setup + pfSense_MODULE: unbound +*/ + +/* include all configuration functions */ +require_once("config.inc"); +require_once("functions.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); + +function create_unbound_chroot_path() { + global $config, $g; + + // Configure chroot + if (!is_dir($g['unbound_chroot_path'])) { + mkdir($g['unbound_chroot_path']); + chown($g['unbound_chroot_path'], "unbound"); + chgrp($g['unbound_chroot_path'], "unbound"); + } + +} + +/* Optimize Unbound for environment */ +function unbound_optimization() { + global $config; + + $optimization_settings = array(); + + /* + * Set the number of threads equal to number of CPUs. + * Use 1 to disable threading, if for some reason this sysctl fails. + */ + $numprocs = intval(get_single_sysctl('kern.smp.cpus')); + if ($numprocs > 1) { + $optimization['number_threads'] = "num-threads: {$numprocs}"; + $optimize_num = pow(2, floor(log($numprocs, 2))); + } else { + $optimization['number_threads'] = "num-threads: 1"; + $optimize_num = 4; + } + + // Slabs to help reduce lock contention. + $optimization['msg_cache_slabs'] = "msg-cache-slabs: {$optimize_num}"; + $optimization['rrset_cache_slabs'] = "rrset-cache-slabs: {$optimize_num}"; + $optimization['infra_cache_slabs'] = "infra-cache-slabs: {$optimize_num}"; + $optimization['key_cache_slabs'] = "key-cache-slabs: {$optimize_num}"; + + /* + * Larger socket buffer for busy servers + * Check that it is set to 4MB (by default the OS has it configured to 4MB) + */ + if (is_array($config['sysctl']) && is_array($config['sysctl']['item'])) { + foreach ($config['sysctl']['item'] as $tunable) { + if ($tunable['tunable'] == 'kern.ipc.maxsockbuf') { + $so = floor(($tunable['value']/1024/1024)-4); + // Check to ensure that the number is not a negative + if ($so >= 4) { + // Limit to 32MB, users might set maxsockbuf very high for other reasons. + // We do not want unbound to fail because of that. + $so = min($so, 32); + $optimization['so_rcvbuf'] = "so-rcvbuf: {$so}m"; + } else { + unset($optimization['so_rcvbuf']); + } + } + } + } + // Safety check in case kern.ipc.maxsockbuf is not available. + if (!isset($optimization['so_rcvbuf'])) { + $optimization['so_rcvbuf'] = "#so-rcvbuf: 4m"; + } + + return $optimization; + +} + +function unbound_generate_config() { + global $config, $g; + + // Setup optimization + $optimization = unbound_optimization(); + + // Setup DNSSEC support + if (isset($config['unbound']['dnssec'])) { + $module_config = "validator iterator"; + $anchor_file = "auto-trust-anchor-file: {$g['unbound_chroot_path']}/root.key"; + } else { + $module_config = "iterator"; + } + + // Setup DNS Rebinding + if (!isset($config['system']['webgui']['nodnsrebindcheck'])) { + // Private-addresses for DNS Rebinding + $private_addr = <<<EOF +# For DNS Rebinding prevention +private-address: 10.0.0.0/8 +private-address: 172.16.0.0/12 +private-address: 169.254.0.0/16 +private-address: 192.168.0.0/16 +private-address: fd00::/8 +private-address: fe80::/10 +EOF; + } + + // Determine interfaces to run on + $bindints = ""; + if (!empty($config['unbound']['active_interface'])) { + $active_interfaces = explode(",", $config['unbound']['active_interface']); + if (in_array("all", $active_interfaces, true)) { + $bindints .= "interface: 0.0.0.0\n"; + $bindints .= "interface: ::0\n"; + $bindints .= "interface-automatic: yes\n"; + } else { + foreach ($active_interfaces as $ubif) { + if (is_ipaddr($ubif)) { + //$bindints .= "interface: $ubif\n"; -- until redmine #4062 is fixed, then uncomment this. + } else { + $intip = get_interface_ip($ubif); + if (is_ipaddrv4($intip)) { + $bindints .= "interface: $intip\n"; + } + $intip = get_interface_ipv6($ubif); + if (is_ipaddrv6($intip)) { + if (!is_linklocal($intip)) { // skipping link local for the moment to not break people's configs: https://redmine.pfsense.org/issues/4062 + $bindints .= "interface: $intip\n"; + } + } + } + } + } + } else { + $bindints .= "interface: 0.0.0.0\n"; + $bindints .= "interface: ::0\n"; + /* If the active interface array is empty, treat it the same as "All" as is done above. Otherwise it breaks CARP with a default config. */ + $bindints .= "interface-automatic: yes\n"; + } + + // Determine interfaces to run on + $outgoingints = ""; + if (!empty($config['unbound']['outgoing_interface'])) { + $outgoingints = "# Outgoing interfaces to be used\n"; + $outgoing_interfaces = explode(",", $config['unbound']['outgoing_interface']); + foreach ($outgoing_interfaces as $outif) { + $outip = get_interface_ip($outif); + if (is_ipaddr($outip)) { + $outgoingints .= "outgoing-interface: $outip\n"; + } + $outip = get_interface_ipv6($outif); + if (is_ipaddrv6($outip)) { + $outgoingints .= "outgoing-interface: $outip\n"; + } + } + } + + // Allow DNS Rebind for forwarded domains + if (isset($config['unbound']['domainoverrides']) && is_array($config['unbound']['domainoverrides'])) { + if (!isset($config['system']['webgui']['nodnsrebindcheck'])) { + $private_domains = "# Set private domains in case authoritative name server returns a Private IP address\n"; + $private_domains .= unbound_add_domain_overrides("private"); + } + $reverse_zones .= unbound_add_domain_overrides("reverse"); + } + + // Configure static Host entries + unbound_add_host_entries(); + + // Configure Domain Overrides + unbound_add_domain_overrides(); + + // Configure Unbound statistics + $statistics = unbound_statistics(); + + // Configure Unbound access-lists + unbound_acls_config(); + + // Add custom Unbound options + if ($config['unbound']['custom_options']) { + $custom_options_source = explode("\n", base64_decode($config['unbound']['custom_options'])); + $custom_options = "# Unbound custom options\n"; + foreach ($custom_options_source as $ent) { + $custom_options .= $ent."\n"; + } + } + + // Server configuration variables + $port = (is_port($config['unbound']['port'])) ? $config['unbound']['port'] : "53"; + $hide_identity = isset($config['unbound']['hideidentity']) ? "yes" : "no"; + $hide_version = isset($config['unbound']['hideversion']) ? "yes" : "no"; + $harden_dnssec_stripped = isset($config['unbound']['dnssecstripped']) ? "yes" : "no"; + $prefetch = isset($config['unbound']['prefetch']) ? "yes" : "no"; + $prefetch_key = isset($config['unbound']['prefetchkey']) ? "yes" : "no"; + $outgoing_num_tcp = (!empty($config['unbound']['outgoing_num_tcp'])) ? $config['unbound']['outgoing_num_tcp'] : "10"; + $incoming_num_tcp = (!empty($config['unbound']['incoming_num_tcp'])) ? $config['unbound']['incoming_num_tcp'] : "10"; + $edns_buffer_size = (!empty($config['unbound']['edns_buffer_size'])) ? $config['unbound']['edns_buffer_size'] : "4096"; + $num_queries_per_thread = (!empty($config['unbound']['num_queries_per_thread'])) ? $config['unbound']['num_queries_per_thread'] : "4096"; + $jostle_timeout = (!empty($config['unbound']['jostle_timeout'])) ? $config['unbound']['jostle_timeout'] : "200"; + $cache_max_ttl = (!empty($config['unbound']['cache_max_ttl'])) ? $config['unbound']['cache_max_ttl'] : "86400"; + $cache_min_ttl = (!empty($config['unbound']['cache_min_ttl'])) ? $config['unbound']['cache_min_ttl'] : "0"; + $infra_host_ttl = (!empty($config['unbound']['infra_host_ttl'])) ? $config['unbound']['infra_host_ttl'] : "900"; + $infra_cache_numhosts = (!empty($config['unbound']['infra_cache_numhosts'])) ? $config['unbound']['infra_cache_numhosts'] : "10000"; + $unwanted_reply_threshold = (!empty($config['unbound']['unwanted_reply_threshold'])) ? $config['unbound']['unwanted_reply_threshold'] : "0"; + if ($unwanted_reply_threshold == "disabled") { + $unwanted_reply_threshold = "0"; + } + $msg_cache_size = (!empty($config['unbound']['msgcachesize'])) ? $config['unbound']['msgcachesize'] : "4"; + $verbosity = isset($config['unbound']['log_verbosity']) ? $config['unbound']['log_verbosity'] : 1; + $use_caps = isset($config['unbound']['use_caps']) ? "yes" : "no"; + + // Set up forwarding if it is configured + if (isset($config['unbound']['forwarding'])) { + $dnsservers = array(); + if (isset($config['system']['dnsallowoverride'])) { + $ns = array_unique(get_nameservers()); + foreach ($ns as $nameserver) { + if ($nameserver) { + $dnsservers[] = $nameserver; + } + } + } else { + $ns = array(); + } + $sys_dnsservers = array_unique(get_dns_servers()); + foreach ($sys_dnsservers as $sys_dnsserver) { + if ($sys_dnsserver && (!in_array($sys_dnsserver, $ns))) { + $dnsservers[] = $sys_dnsserver; + } + } + + if (!empty($dnsservers)) { + $forward_conf .=<<<EOD +# Forwarding +forward-zone: + name: "." + +EOD; + foreach ($dnsservers as $dnsserver) { + $forward_conf .= "\tforward-addr: $dnsserver\n"; + } + } + } else { + $forward_conf = ""; + } + + // Size of the RRset cache == 2 * msg-cache-size per Unbound's recommendations + $rrset_cache_size = $msg_cache_size * 2; + + $unboundconf = <<<EOD +########################## +# Unbound Configuration +########################## + +## +# Server configuration +## +server: +{$reverse_zones} +chroot: {$g['unbound_chroot_path']} +username: "unbound" +directory: "{$g['unbound_chroot_path']}" +pidfile: "/var/run/unbound.pid" +use-syslog: yes +port: {$port} +verbosity: {$verbosity} +hide-identity: {$hide_identity} +hide-version: {$hide_version} +harden-glue: yes +do-ip4: yes +do-ip6: yes +do-udp: yes +do-tcp: yes +do-daemonize: yes +module-config: "{$module_config}" +unwanted-reply-threshold: {$unwanted_reply_threshold} +num-queries-per-thread: {$num_queries_per_thread} +jostle-timeout: {$jostle_timeout} +infra-host-ttl: {$infra_host_ttl} +infra-cache-numhosts: {$infra_cache_numhosts} +outgoing-num-tcp: {$outgoing_num_tcp} +incoming-num-tcp: {$incoming_num_tcp} +edns-buffer-size: {$edns_buffer_size} +cache-max-ttl: {$cache_max_ttl} +cache-min-ttl: {$cache_min_ttl} +harden-dnssec-stripped: {$harden_dnssec_stripped} +msg-cache-size: {$msg_cache_size}m +rrset-cache-size: {$rrset_cache_size}m + +{$optimization['number_threads']} +{$optimization['msg_cache_slabs']} +{$optimization['rrset_cache_slabs']} +{$optimization['infra_cache_slabs']} +{$optimization['key_cache_slabs']} +outgoing-range: 4096 +{$optimization['so_rcvbuf']} +{$anchor_file} +prefetch: {$prefetch} +prefetch-key: {$prefetch_key} +use-caps-for-id: {$use_caps} +# Statistics +{$statistics} +# Interface IP(s) to bind to +{$bindints} +{$outgoingints} + +# DNS Rebinding +{$private_addr} +{$private_domains} + +# Access lists +include: {$g['unbound_chroot_path']}/access_lists.conf + +# Static host entries +include: {$g['unbound_chroot_path']}/host_entries.conf + +# dhcp lease entries +include: {$g['unbound_chroot_path']}/dhcpleases_entries.conf + +# Domain overrides +include: {$g['unbound_chroot_path']}/domainoverrides.conf +{$forward_conf} + +{$custom_options} + +### +# Remote Control Config +### +include: {$g['unbound_chroot_path']}/remotecontrol.conf + +EOD; + + create_unbound_chroot_path(); + file_put_contents("{$g['unbound_chroot_path']}/unbound.conf", $unboundconf); + + return 0; +} + +function unbound_remote_control_setup() { + global $g; + + if (!file_exists("{$g['unbound_chroot_path']}/remotecontrol.conf") || !file_exists("{$g['unbound_chroot_path']}/unbound_control.key")) { + $remotcfg = <<<EOF +remote-control: + control-enable: yes + control-interface: 127.0.0.1 + control-port: 953 + server-key-file: "{$g['unbound_chroot_path']}/unbound_server.key" + server-cert-file: "{$g['unbound_chroot_path']}/unbound_server.pem" + control-key-file: "{$g['unbound_chroot_path']}/unbound_control.key" + control-cert-file: "{$g['unbound_chroot_path']}/unbound_control.pem" + +EOF; + + create_unbound_chroot_path(); + file_put_contents("{$g['unbound_chroot_path']}/remotecontrol.conf", $remotcfg); + + // Generate our keys + do_as_unbound_user("unbound-control-setup"); + + } +} + +// Read /etc/hosts +function read_hosts() { + + /* Open /etc/hosts and extract the only dhcpleases info + * XXX - to convert to an unbound C library which reads /etc/hosts automatically + */ + $etc_hosts = array(); + foreach (file('/etc/hosts') as $line) { + if (strpos($line, "dhcpleases automatically entered")) { + break; + } + $d = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY); + if (empty($d) || substr(reset($d), 0, 1) == "#") { + continue; + } + $ip = array_shift($d); + $fqdn = array_shift($d); + $name = array_shift($d); + if (!empty($fqdn) && $fqdn != "empty") { + if (!empty($name) && $name != "empty") { + array_push($etc_hosts, array(ipaddr => "$ip", fqdn => "$fqdn", name => "$name")); + } else { + array_push($etc_hosts, array(ipaddr => "$ip", fqdn => "$fqdn")); + } + } + } + return $etc_hosts; +} + +function sync_unbound_service() { + global $config, $g; + + create_unbound_chroot_path(); + + // Configure our Unbound service + do_as_unbound_user("unbound-anchor"); + unbound_remote_control_setup(); + unbound_generate_config(); + do_as_unbound_user("start"); + require_once("service-utils.inc"); + if (is_service_running("unbound")) { + do_as_unbound_user("restore_cache"); + } + +} + +function unbound_acl_id_used($id) { + global $config; + + if (is_array($config['unbound']['acls'])) { + foreach ($config['unbound']['acls'] as & $acls) { + if ($id == $acls['aclid']) { + return true; + } + } + } + + return false; +} + +function unbound_get_next_id() { + $aclid = 0; + while (unbound_acl_id_used($aclid)) { + $aclid++; + } + return $aclid; +} + +// Execute commands as the user unbound +function do_as_unbound_user($cmd) { + global $g; + + switch ($cmd) { + case "start": + mwexec("/usr/local/sbin/unbound -c {$g['unbound_chroot_path']}/unbound.conf"); + break; + case "stop": + mwexec("echo '/usr/local/sbin/unbound-control stop' | /usr/bin/su -m unbound", true); + break; + case "reload": + mwexec("echo '/usr/local/sbin/unbound-control reload' | /usr/bin/su -m unbound", true); + break; + case "unbound-anchor": + mwexec("echo '/usr/local/sbin/unbound-anchor -a {$g['unbound_chroot_path']}/root.key' | /usr/bin/su -m unbound", true); + break; + case "unbound-control-setup": + mwexec("echo '/usr/local/sbin/unbound-control-setup -d {$g['unbound_chroot_path']}' | /usr/bin/su -m unbound", true); + break; + default: + break; + } +} + +function unbound_add_domain_overrides($pvt_rev="") { + global $config, $g; + + $domains = $config['unbound']['domainoverrides']; + + $sorted_domains = msort($domains, "domain"); + $result = array(); + foreach ($sorted_domains as $domain) { + $domain_key = current($domain); + if (!isset($result[$domain_key])) { + $result[$domain_key] = array(); + } + $result[$domain_key][] = $domain['ip']; + } + + // Domain overrides that have multiple entries need multiple stub-addr: added + $domain_entries = ""; + foreach ($result as $domain=>$ips) { + if ($pvt_rev == "private") { + $domain_entries .= "private-domain: \"$domain\"\n"; + $domain_entries .= "domain-insecure: \"$domain\"\n"; + } else if ($pvt_rev == "reverse") { + if ((substr($domain, -14) == ".in-addr.arpa.") || (substr($domain, -13) == ".in-addr.arpa")) { + $domain_entries .= "local-zone: \"$domain\" typetransparent\n"; + } + } else { + $domain_entries .= "stub-zone:\n"; + $domain_entries .= "\tname: \"$domain\"\n"; + foreach ($ips as $ip) { + $domain_entries .= "\tstub-addr: $ip\n"; + } + $domain_entries .= "\tstub-prime: no\n"; + } + } + + if ($pvt_rev != "") { + return $domain_entries; + } else { + create_unbound_chroot_path(); + file_put_contents("{$g['unbound_chroot_path']}/domainoverrides.conf", $domain_entries); + } +} + +function unbound_add_host_entries() { + global $config, $g; + + $unbound_entries = "local-zone: \"{$config['system']['domain']}\" transparent\n"; + + $hosts = read_hosts(); + $added_ptr = array(); + foreach ($hosts as $host) { + if (is_ipaddrv4($host['ipaddr'])) { + $type = 'A'; + } else if (is_ipaddrv6($host['ipaddr'])) { + $type = 'AAAA'; + } else { + continue; + } + + if (!$added_ptr[$host['ipaddr']]) { + $unbound_entries .= "local-data-ptr: \"{$host['ipaddr']} {$host['fqdn']}\"\n"; + $added_ptr[$host['ipaddr']] = true; + } + $unbound_entries .= "local-data: \"{$host['fqdn']} {$type} {$host['ipaddr']}\"\n"; + if (isset($host['name'])) { + $unbound_entries .= "local-data: \"{$host['name']} {$type} {$host['ipaddr']}\"\n"; + } + } + + // Write out entries + create_unbound_chroot_path(); + file_put_contents("{$g['unbound_chroot_path']}/host_entries.conf", $unbound_entries); + + /* dhcpleases will write to this config file, make sure it exists */ + @touch("{$g['unbound_chroot_path']}/dhcpleases_entries.conf"); +} + +function unbound_control($action) { + global $config, $g; + + $cache_dumpfile = "/var/tmp/unbound_cache"; + + switch ($action) { + case "start": + // Start Unbound + if ($config['unbound']['enable'] == "on") { + if (!is_service_running("unbound")) { + do_as_unbound_user("start"); + } + } + break; + case "stop": + if ($config['unbound']['enable'] == "on") { + do_as_unbound_user("stop"); + } + break; + case "reload": + if ($config['unbound']['enable'] == "on") { + do_as_unbound_user("reload"); + } + break; + case "dump_cache": + // Dump Unbound's Cache + if ($config['unbound']['dumpcache'] == "on") { + do_as_unbound_user("dump_cache"); + } + break; + case "restore_cache": + // Restore Unbound's Cache + if ((is_service_running("unbound")) && ($config['unbound']['dumpcache'] == "on")) { + if (file_exists($cache_dumpfile) && filesize($cache_dumpfile) > 0) { + do_as_unbound_user("load_cache < /var/tmp/unbound_cache"); + } + } + break; + default: + break; + + } +} + +// Generation of Unbound statistics +function unbound_statistics() { + global $config; + + if ($config['stats'] == "on") { + $stats_interval = $config['unbound']['stats_interval']; + $cumulative_stats = $config['cumulative_stats']; + if ($config['extended_stats'] == "on") { + $extended_stats = "yes"; + } else { + $extended_stats = "no"; + } + } else { + $stats_interval = "0"; + $cumulative_stats = "no"; + $extended_stats = "no"; + } + /* XXX To do - add RRD graphs */ + $stats = <<<EOF +# Unbound Statistics +statistics-interval: {$stats_interval} +extended-statistics: yes +statistics-cumulative: yes + +EOF; + + return $stats; +} + +// Unbound Access lists +function unbound_acls_config() { + global $g, $config; + + if (!isset($config['unbound']['disable_auto_added_access_control'])) { + $aclcfg = "access-control: 127.0.0.1/32 allow\n"; + $aclcfg .= "access-control: ::1 allow\n"; + // Add our networks for active interfaces including localhost + if (!empty($config['unbound']['active_interface'])) { + $active_interfaces = array_flip(explode(",", $config['unbound']['active_interface'])); + if (in_array("all", $active_interfaces)) { + $active_interfaces = get_configured_interface_with_descr(); + } + } else { + $active_interfaces = get_configured_interface_with_descr(); + } + + $bindints = ""; + foreach ($active_interfaces as $ubif => $ifdesc) { + $ifip = get_interface_ip($ubif); + if (is_ipaddrv4($ifip)) { + // IPv4 is handled via NAT networks below + } + $ifip = get_interface_ipv6($ubif); + if (is_ipaddrv6($ifip)) { + if (!is_linklocal($ifip)) { + $subnet_bits = get_interface_subnetv6($ubif); + $subnet_ip = gen_subnetv6($ifip, $subnet_bits); + // only add LAN-type interfaces + if (!interface_has_gateway($ubif)) { + $aclcfg .= "access-control: {$subnet_ip}/{$subnet_bits} allow\n"; + } + } + // add for IPv6 static routes to local networks + // for safety, we include only routes reachable on an interface with no + // gateway specified - read: not an Internet connection. + $static_routes = get_staticroutes(); + foreach ($static_routes as $route) { + if ((lookup_gateway_interface_by_name($route['gateway']) == $ubif) && !interface_has_gateway($ubif)) { + // route is on this interface, interface doesn't have gateway, add it + $aclcfg .= "access-control: {$route['network']} allow\n"; + } + } + } + } + + // Generate IPv4 access-control entries using the same logic as automatic outbound NAT + if (empty($FilterIflist)) { + filter_generate_optcfg_array(); + } + $natnetworks_array = array(); + $natnetworks_array = filter_nat_rules_automatic_tonathosts(); + foreach ($natnetworks_array as $allowednet) { + $aclcfg .= "access-control: $allowednet allow \n"; + } + } + + // Configure the custom ACLs + if (is_array($config['unbound']['acls'])) { + foreach ($config['unbound']['acls'] as $unbound_acl) { + $aclcfg .= "#{$unbound_acl['aclname']}\n"; + foreach ($unbound_acl['row'] as $network) { + if ($unbound_acl['aclaction'] == "allow snoop") { + $unbound_acl['aclaction'] = "allow_snoop"; + } + $aclcfg .= "access-control: {$network['acl_network']}/{$network['mask']} {$unbound_acl['aclaction']}\n"; + } + } + } + // Write out Access list + create_unbound_chroot_path(); + file_put_contents("{$g['unbound_chroot_path']}/access_lists.conf", $aclcfg); + +} + +// Generate hosts and reload services +function unbound_hosts_generate() { + // Generate our hosts file + unbound_add_host_entries(); + + // Reload our service to read the updates + unbound_control("reload"); +} + +?> diff --git a/src/etc/inc/upgrade_config.inc b/src/etc/inc/upgrade_config.inc new file mode 100644 index 0000000..f5268d2 --- /dev/null +++ b/src/etc/inc/upgrade_config.inc @@ -0,0 +1,3833 @@ +<?php +/* + upgrade_config.inc + Copyright (C) 2004-2009 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_BUILDER_BINARIES: /usr/bin/find /bin/cd /usr/local/bin/rrdtool /usr/bin/nice + pfSense_MODULE: config +*/ + +if (!function_exists("dump_rrd_to_xml")) { + require("rrd.inc"); +} + +/* Upgrade functions must be named: +* upgrade_XXX_to_YYY + * where XXX == previous version, zero padded, and YYY == next version, zero padded + */ +function upgrade_010_to_011() { + global $config; + $opti = 1; + $ifmap = array('lan' => 'lan', 'wan' => 'wan', 'pptp' => 'pptp'); + + /* convert DMZ to optional, if necessary */ + if (isset($config['interfaces']['dmz'])) { + + $dmzcfg = &$config['interfaces']['dmz']; + + if ($dmzcfg['if']) { + $config['interfaces']['opt' . $opti] = array(); + $optcfg = &$config['interfaces']['opt' . $opti]; + + $optcfg['enable'] = $dmzcfg['enable']; + $optcfg['descr'] = "DMZ"; + $optcfg['if'] = $dmzcfg['if']; + $optcfg['ipaddr'] = $dmzcfg['ipaddr']; + $optcfg['subnet'] = $dmzcfg['subnet']; + + $ifmap['dmz'] = "opt" . $opti; + $opti++; + } + + unset($config['interfaces']['dmz']); + } + + /* convert WLAN1/2 to optional, if necessary */ + for ($i = 1; isset($config['interfaces']['wlan' . $i]); $i++) { + + if (!$config['interfaces']['wlan' . $i]['if']) { + unset($config['interfaces']['wlan' . $i]); + continue; + } + + $wlancfg = &$config['interfaces']['wlan' . $i]; + $config['interfaces']['opt' . $opti] = array(); + $optcfg = &$config['interfaces']['opt' . $opti]; + + $optcfg['enable'] = $wlancfg['enable']; + $optcfg['descr'] = "WLAN" . $i; + $optcfg['if'] = $wlancfg['if']; + $optcfg['ipaddr'] = $wlancfg['ipaddr']; + $optcfg['subnet'] = $wlancfg['subnet']; + $optcfg['bridge'] = $wlancfg['bridge']; + + $optcfg['wireless'] = array(); + $optcfg['wireless']['mode'] = $wlancfg['mode']; + $optcfg['wireless']['ssid'] = $wlancfg['ssid']; + $optcfg['wireless']['channel'] = $wlancfg['channel']; + $optcfg['wireless']['wep'] = $wlancfg['wep']; + + $ifmap['wlan' . $i] = "opt" . $opti; + + unset($config['interfaces']['wlan' . $i]); + $opti++; + } + + /* convert filter rules */ + $n = count($config['filter']['rule']); + for ($i = 0; $i < $n; $i++) { + + $fr = &$config['filter']['rule'][$i]; + + /* remap interface */ + if (array_key_exists($fr['interface'], $ifmap)) { + $fr['interface'] = $ifmap[$fr['interface']]; + } else { + /* remove the rule */ + printf(gettext("%sWarning: filter rule removed " . + "(interface '%s' does not exist anymore)."), "\n", $fr['interface']); + unset($config['filter']['rule'][$i]); + continue; + } + + /* remap source network */ + if (isset($fr['source']['network'])) { + if (array_key_exists($fr['source']['network'], $ifmap)) { + $fr['source']['network'] = $ifmap[$fr['source']['network']]; + } else { + /* remove the rule */ + printf(gettext("%sWarning: filter rule removed " . + "(source network '%s' does not exist anymore)."), "\n", $fr['source']['network']); + unset($config['filter']['rule'][$i]); + continue; + } + } + + /* remap destination network */ + if (isset($fr['destination']['network'])) { + if (array_key_exists($fr['destination']['network'], $ifmap)) { + $fr['destination']['network'] = $ifmap[$fr['destination']['network']]; + } else { + /* remove the rule */ + printf(gettext("%sWarning: filter rule removed " . + "(destination network '%s' does not exist anymore)."), "\n", $fr['destination']['network']); + unset($config['filter']['rule'][$i]); + continue; + } + } + } + + /* convert shaper rules */ + $n = count($config['pfqueueing']['rule']); + if (is_array($config['pfqueueing']['rule'])) { + for ($i = 0; $i < $n; $i++) { + + $fr = &$config['pfqueueing']['rule'][$i]; + + /* remap interface */ + if (array_key_exists($fr['interface'], $ifmap)) { + $fr['interface'] = $ifmap[$fr['interface']]; + } else { + /* remove the rule */ + printf(gettext("%sWarning: traffic shaper rule removed " . + "(interface '%s' does not exist anymore)."), "\n", $fr['interface']); + unset($config['pfqueueing']['rule'][$i]); + continue; + } + + /* remap source network */ + if (isset($fr['source']['network'])) { + if (array_key_exists($fr['source']['network'], $ifmap)) { + $fr['source']['network'] = $ifmap[$fr['source']['network']]; + } else { + /* remove the rule */ + printf(gettext("%sWarning: traffic shaper rule removed " . + "(source network '%s' does not exist anymore)."), "\n", $fr['source']['network']); + unset($config['pfqueueing']['rule'][$i]); + continue; + } + } + + /* remap destination network */ + if (isset($fr['destination']['network'])) { + if (array_key_exists($fr['destination']['network'], $ifmap)) { + $fr['destination']['network'] = $ifmap[$fr['destination']['network']]; + } else { + /* remove the rule */ + printf(gettext("%sWarning: traffic shaper rule removed " . + "(destination network '%s' does not exist anymore)."), "\n", $fr['destination']['network']); + unset($config['pfqueueing']['rule'][$i]); + continue; + } + } + } + } +} + + +function upgrade_011_to_012() { + global $config; + /* move LAN DHCP server config */ + $tmp = $config['dhcpd']; + $config['dhcpd'] = array(); + $config['dhcpd']['lan'] = $tmp; + + /* encrypt password */ + $config['system']['password'] = crypt($config['system']['password']); +} + + +function upgrade_012_to_013() { + global $config; + /* convert advanced outbound NAT config */ + for ($i = 0; isset($config['nat']['advancedoutbound']['rule'][$i]); $i++) { + $curent = &$config['nat']['advancedoutbound']['rule'][$i]; + $src = $curent['source']; + $curent['source'] = array(); + $curent['source']['network'] = $src; + $curent['destination'] = array(); + $curent['destination']['any'] = true; + } + + /* add an explicit type="pass" to all filter rules to make things consistent */ + for ($i = 0; isset($config['filter']['rule'][$i]); $i++) { + $config['filter']['rule'][$i]['type'] = "pass"; + } +} + + +function upgrade_013_to_014() { + global $config; + /* convert shaper rules (make pipes) */ + if (is_array($config['pfqueueing']['rule'])) { + $config['pfqueueing']['pipe'] = array(); + + for ($i = 0; isset($config['pfqueueing']['rule'][$i]); $i++) { + $curent = &$config['pfqueueing']['rule'][$i]; + + /* make new pipe and associate with this rule */ + $newpipe = array(); + $newpipe['descr'] = $curent['descr']; + $newpipe['bandwidth'] = $curent['bandwidth']; + $newpipe['delay'] = $curent['delay']; + $newpipe['mask'] = $curent['mask']; + $config['pfqueueing']['pipe'][$i] = $newpipe; + + $curent['targetpipe'] = $i; + + unset($curent['bandwidth']); + unset($curent['delay']); + unset($curent['mask']); + } + } +} + + +function upgrade_014_to_015() { + global $config; + /* Default route moved */ + if (isset($config['interfaces']['wan']['gateway'])) { + if ($config['interfaces']['wan']['gateway'] <> "") { + $config['system']['gateway'] = $config['interfaces']['wan']['gateway']; + } + } + unset($config['interfaces']['wan']['gateway']); + + /* Queues are no longer interface specific */ + if (isset($config['interfaces']['lan']['schedulertype'])) { + unset($config['interfaces']['lan']['schedulertype']); + } + if (isset($config['interfaces']['wan']['schedulertype'])) { + unset($config['interfaces']['wan']['schedulertype']); + } + + for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) { + if (isset($config['interfaces']['opt' . $i]['schedulertype'])) { + unset($config['interfaces']['opt' . $i]['schedulertype']); + } + } +} + + +function upgrade_015_to_016() { + global $config; + /* Alternate firmware URL moved */ + if (isset($config['system']['firmwareurl']) && isset($config['system']['firmwarename'])) { // Only convert if *both* are defined. + $config['system']['alt_firmware_url'] = array(); + $config['system']['alt_firmware_url']['enabled'] = ""; + $config['system']['alt_firmware_url']['firmware_base_url'] = $config['system']['firmwareurl']; + $config['system']['alt_firmware_url']['firmware_filename'] = $config['system']['firmwarename']; + unset($config['system']['firmwareurl'], $config['system']['firmwarename']); + } else { + unset($config['system']['firmwareurl'], $config['system']['firmwarename']); + } +} + + +function upgrade_016_to_017() { + global $config; + /* wipe previous shaper configuration */ + unset($config['shaper']['queue']); + unset($config['shaper']['rule']); + unset($config['interfaces']['wan']['bandwidth']); + unset($config['interfaces']['wan']['bandwidthtype']); + unset($config['interfaces']['lan']['bandwidth']); + unset($config['interfaces']['lan']['bandwidthtype']); + $config['shaper']['enable'] = FALSE; +} + + +function upgrade_017_to_018() { + global $config; + if (isset($config['proxyarp']) && is_array($config['proxyarp']['proxyarpnet'])) { + $proxyarp = &$config['proxyarp']['proxyarpnet']; + foreach ($proxyarp as $arpent) { + $vip = array(); + $vip['mode'] = "proxyarp"; + $vip['interface'] = $arpent['interface']; + $vip['descr'] = $arpent['descr']; + if (isset($arpent['range'])) { + $vip['range'] = $arpent['range']; + $vip['type'] = "range"; + } else { + $subnet = explode('/', $arpent['network']); + $vip['subnet'] = $subnet[0]; + if (isset($subnet[1])) { + $vip['subnet_bits'] = $subnet[1]; + $vip['type'] = "network"; + } else { + $vip['subnet_bits'] = "32"; + $vip['type'] = "single"; + } + } + $config['virtualip']['vip'][] = $vip; + } + unset($config['proxyarp']); + } + if (isset($config['installedpackages']) && isset($config['installedpackages']['carp']) && is_array($config['installedpackages']['carp']['config'])) { + $carp = &$config['installedpackages']['carp']['config']; + foreach ($carp as $carpent) { + $vip = array(); + $vip['mode'] = "carp"; + $vip['interface'] = "AUTO"; + $vip['descr'] = sprintf(gettext("CARP vhid %s"), $carpent['vhid']); + $vip['type'] = "single"; + $vip['vhid'] = $carpent['vhid']; + $vip['advskew'] = $carpent['advskew']; + $vip['password'] = $carpent['password']; + $vip['subnet'] = $carpent['ipaddress']; + $vip['subnet_bits'] = $carpent['netmask']; + $config['virtualip']['vip'][] = $vip; + } + unset($config['installedpackages']['carp']); + } + /* Server NAT is no longer needed */ + unset($config['nat']['servernat']); + + /* enable SSH */ + if ($config['version'] == "1.8") { + $config['system']['sshenabled'] = true; + } +} + + +function upgrade_018_to_019() { + global $config; + $config['theme']="metallic"; +} + + +function upgrade_019_to_020() { + global $config; + if (is_array($config['ipsec']['tunnel'])) { + reset($config['ipsec']['tunnel']); + while (list($index, $tunnel) = each($config['ipsec']['tunnel'])) { + /* Sanity check on required variables */ + /* This fixes bogus <tunnel> entries - remnant of bug #393 */ + if (!isset($tunnel['local-subnet']) && !isset($tunnel['remote-subnet'])) { + unset($config['ipsec']['tunnel'][$tunnel]); + } + } + } +} + +function upgrade_020_to_021() { + global $config; + /* shaper scheduler moved */ + if (isset($config['system']['schedulertype'])) { + $config['shaper']['schedulertype'] = $config['system']['schedulertype']; + unset($config['system']['schedulertype']); + } +} + + +function upgrade_021_to_022() { + global $config; + /* move gateway to wan interface */ + $config['interfaces']['wan']['gateway'] = $config['system']['gateway']; +} + +function upgrade_022_to_023() { + global $config; + if (isset($config['shaper'])) { + /* wipe previous shaper configuration */ + unset($config['shaper']); + } +} + + +function upgrade_023_to_024() { + global $config; +} + + +function upgrade_024_to_025() { + global $config; + $config['interfaces']['wan']['use_rrd_gateway'] = $config['system']['use_rrd_gateway']; + unset($config['system']['use_rrd_gateway']); +} + + +function upgrade_025_to_026() { + global $config; + $cron_item = array(); + $cron_item['minute'] = "0"; + $cron_item['hour'] = "*"; + $cron_item['mday'] = "*"; + $cron_item['month'] = "*"; + $cron_item['wday'] = "*"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/usr/bin/nice -n20 newsyslog"; + + $config['cron']['item'][] = $cron_item; + + $cron_item = array(); + $cron_item['minute'] = "1,31"; + $cron_item['hour'] = "0-5"; + $cron_item['mday'] = "*"; + $cron_item['month'] = "*"; + $cron_item['wday'] = "*"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/usr/bin/nice -n20 adjkerntz -a"; + + $config['cron']['item'][] = $cron_item; + + $cron_item = array(); + $cron_item['minute'] = "1"; + $cron_item['hour'] = "*"; + $cron_item['mday'] = "1"; + $cron_item['month'] = "*"; + $cron_item['wday'] = "*"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/usr/bin/nice -n20 /etc/rc.update_bogons.sh"; + + $config['cron']['item'][] = $cron_item; + + $cron_item = array(); + $cron_item['minute'] = "*/60"; + $cron_item['hour'] = "*"; + $cron_item['mday'] = "*"; + $cron_item['month'] = "*"; + $cron_item['wday'] = "*"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/usr/bin/nice -n20 /usr/local/sbin/expiretable -v -t 3600 sshlockout"; + + $config['cron']['item'][] = $cron_item; + + $cron_item = array(); + $cron_item['minute'] = "1"; + $cron_item['hour'] = "1"; + $cron_item['mday'] = "*"; + $cron_item['month'] = "*"; + $cron_item['wday'] = "*"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/usr/bin/nice -n20 /etc/rc.dyndns.update"; + + $config['cron']['item'][] = $cron_item; + + $cron_item = array(); + $cron_item['minute'] = "*/60"; + $cron_item['hour'] = "*"; + $cron_item['mday'] = "*"; + $cron_item['month'] = "*"; + $cron_item['wday'] = "*"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/usr/bin/nice -n20 /usr/local/sbin/expiretable -v -t 3600 virusprot"; + + $config['cron']['item'][] = $cron_item; + + $cron_item = array(); + $cron_item['minute'] = "*/60"; + $cron_item['hour'] = "*"; + $cron_item['mday'] = "*"; + $cron_item['month'] = "*"; + $cron_item['wday'] = "*"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/usr/bin/nice -n20 /usr/local/sbin/expiretable -t 1800 snort2c"; + + $config['cron']['item'][] = $cron_item; +} + + +function upgrade_026_to_027() { + global $config; +} + + +function upgrade_027_to_028() { + global $config; +} + + +function upgrade_028_to_029() { + global $config; + $rule_item = array(); + $a_filter = &$config['filter']['rule']; + $rule_item['interface'] = "enc0"; + $rule_item['type'] = "pass"; + $rule_item['source']['any'] = true; + $rule_item['destination']['any'] = true; + $rule_item['descr'] = gettext("Permit IPsec traffic."); + $rule_item['statetype'] = "keep state"; + $a_filter[] = $rule_item; +} + + +function upgrade_029_to_030() { + global $config; + /* enable the rrd config setting by default */ + $config['rrd']['enable'] = true; +} + + +function upgrade_030_to_031() { + global $config; + /* Insert upgrade code here */ +} + + +function upgrade_031_to_032() { + global $config; + /* Insert upgrade code here */ +} + + +function upgrade_032_to_033() { + global $config; + /* Insert upgrade code here */ +} + + +function upgrade_033_to_034() { + global $config; + /* Insert upgrade code here */ +} + + +function upgrade_034_to_035() { + global $config; + /* Insert upgrade code here */ +} + + +function upgrade_035_to_036() { + global $config; + /* Insert upgrade code here */ +} + + +function upgrade_036_to_037() { + global $config; + /* Insert upgrade code here */ +} + + +function upgrade_037_to_038() { + global $config; + /* Insert upgrade code here */ +} + + +function upgrade_038_to_039() { + global $config; + /* Insert upgrade code here */ +} + + +function upgrade_039_to_040() { + global $config, $g; + $config['system']['webgui']['auth_method'] = "session"; + $config['system']['webgui']['backing_method'] = "htpasswd"; + + if (isset ($config['system']['username'])) { + $config['system']['group'] = array(); + $config['system']['group'][0]['name'] = "admins"; + $config['system']['group'][0]['description'] = gettext("System Administrators"); + $config['system']['group'][0]['scope'] = "system"; + $config['system']['group'][0]['priv'] = "page-all"; + $config['system']['group'][0]['home'] = "index.php"; + $config['system']['group'][0]['gid'] = "110"; + + $config['system']['user'] = array(); + $config['system']['user'][0]['name'] = "{$config['system']['username']}"; + $config['system']['user'][0]['descr'] = "System Administrator"; + $config['system']['user'][0]['scope'] = "system"; + $config['system']['user'][0]['groupname'] = "admins"; + $config['system']['user'][0]['password'] = "{$config['system']['password']}"; + $config['system']['user'][0]['uid'] = "0"; + /* Ensure that we follow what this new "admin" username should be in the session. */ + $_SESSION["Username"] = "{$config['system']['username']}"; + + $config['system']['user'][0]['priv'] = array(); + $config['system']['user'][0]['priv'][0]['id'] = "lockwc"; + $config['system']['user'][0]['priv'][0]['name'] = "Lock webConfigurator"; + $config['system']['user'][0]['priv'][0]['descr'] = gettext("Indicates whether this user will lock access to the webConfigurator for other users."); + $config['system']['user'][0]['priv'][1]['id'] = "lock-ipages"; + $config['system']['user'][0]['priv'][1]['name'] = "Lock individual pages"; + $config['system']['user'][0]['priv'][1]['descr'] = gettext("Indicates whether this user will lock individual HTML pages after having accessed a particular page (the lock will be freed if the user leaves or saves the page form)."); + $config['system']['user'][0]['priv'][2]['id'] = "hasshell"; + $config['system']['user'][0]['priv'][2]['name'] = "Has shell access"; + $config['system']['user'][0]['priv'][2]['descr'] = gettext("Indicates whether this user is able to login for example via SSH."); + $config['system']['user'][0]['priv'][3]['id'] = "copyfiles"; + $config['system']['user'][0]['priv'][3]['name'] = "Is allowed to copy files"; + $config['system']['user'][0]['priv'][3]['descr'] = sprintf(gettext("Indicates whether this user is allowed to copy files onto the %s appliance via SCP/SFTP. If you are going to use this privilege, you must install scponly on the appliance (Hint: pkg_add -r scponly)."), $g['product_name']); + $config['system']['user'][0]['priv'][4]['id'] = "isroot"; + $config['system']['user'][0]['priv'][4]['name'] = "Is root user"; + $config['system']['user'][0]['priv'][4]['descr'] = gettext("This user is associated with the UNIX root user (you should associate this privilege only with one single user)."); + + $config['system']['nextuid'] = "111"; + $config['system']['nextgid'] = "111"; + + /* wipe previous auth configuration */ + unset ($config['system']['username']); + unset ($config['system']['password']); + } +} + +function upgrade_040_to_041() { + global $config; + if (!$config['sysctl']) { + $config['sysctl']['item'] = array(); + + $config['sysctl']['item'][0]['tunable'] = "net.inet.tcp.blackhole"; + $config['sysctl']['item'][0]['descr'] = gettext("Drop packets to closed TCP ports without returning a RST"); + $config['sysctl']['item'][0]['value'] = "default"; + + $config['sysctl']['item'][1]['tunable'] = "net.inet.udp.blackhole"; + $config['sysctl']['item'][1]['descr'] = gettext("Do not send ICMP port unreachable messages for closed UDP ports"); + $config['sysctl']['item'][1]['value'] = "default"; + + $config['sysctl']['item'][2]['tunable'] = "net.inet.ip.random_id"; + $config['sysctl']['item'][2]['descr'] = gettext("Randomize the ID field in IP packets (default is 0: sequential IP IDs)"); + $config['sysctl']['item'][2]['value'] = "default"; + + $config['sysctl']['item'][3]['tunable'] = "net.inet.tcp.drop_synfin"; + $config['sysctl']['item'][3]['descr'] = gettext("Drop SYN-FIN packets (breaks RFC1379, but nobody uses it anyway)"); + $config['sysctl']['item'][3]['value'] = "default"; + + $config['sysctl']['item'][4]['tunable'] = "net.inet.ip.redirect"; + $config['sysctl']['item'][4]['descr'] = gettext("Sending of IPv4 ICMP redirects"); + $config['sysctl']['item'][4]['value'] = "default"; + + $config['sysctl']['item'][5]['tunable'] = "net.inet6.ip6.redirect"; + $config['sysctl']['item'][5]['descr'] = gettext("Sending of IPv6 ICMP redirects"); + $config['sysctl']['item'][5]['value'] = "default"; + + $config['sysctl']['item'][6]['tunable'] = "net.inet.tcp.syncookies"; + $config['sysctl']['item'][6]['descr'] = gettext("Generate SYN cookies for outbound SYN-ACK packets"); + $config['sysctl']['item'][6]['value'] = "default"; + + $config['sysctl']['item'][7]['tunable'] = "net.inet.tcp.recvspace"; + $config['sysctl']['item'][7]['descr'] = gettext("Maximum incoming TCP datagram size"); + $config['sysctl']['item'][7]['value'] = "default"; + + $config['sysctl']['item'][8]['tunable'] = "net.inet.tcp.sendspace"; + $config['sysctl']['item'][8]['descr'] = gettext("Maximum outgoing TCP datagram size"); + $config['sysctl']['item'][8]['value'] = "default"; + + $config['sysctl']['item'][9]['tunable'] = "net.inet.ip.fastforwarding"; + $config['sysctl']['item'][9]['descr'] = gettext("Fastforwarding (see http://lists.freebsd.org/pipermail/freebsd-net/2004-January/002534.html)"); + $config['sysctl']['item'][9]['value'] = "default"; + + $config['sysctl']['item'][10]['tunable'] = "net.inet.tcp.delayed_ack"; + $config['sysctl']['item'][10]['descr'] = gettext("Do not delay ACK to try and piggyback it onto a data packet"); + $config['sysctl']['item'][10]['value'] = "default"; + + $config['sysctl']['item'][11]['tunable'] = "net.inet.udp.maxdgram"; + $config['sysctl']['item'][11]['descr'] = gettext("Maximum outgoing UDP datagram size"); + $config['sysctl']['item'][11]['value'] = "default"; + + $config['sysctl']['item'][12]['tunable'] = "net.link.bridge.pfil_onlyip"; + $config['sysctl']['item'][12]['descr'] = gettext("Handling of non-IP packets which are not passed to pfil (see if_bridge(4))"); + $config['sysctl']['item'][12]['value'] = "default"; + + $config['sysctl']['item'][13]['tunable'] = "net.link.tap.user_open"; + $config['sysctl']['item'][13]['descr'] = gettext("Allow unprivileged access to tap(4) device nodes"); + $config['sysctl']['item'][13]['value'] = "default"; + + $config['sysctl']['item'][15]['tunable'] = "kern.randompid"; + $config['sysctl']['item'][15]['descr'] = gettext("Randomize PID's (see src/sys/kern/kern_fork.c: sysctl_kern_randompid())"); + $config['sysctl']['item'][15]['value'] = "default"; + + $config['sysctl']['item'][16]['tunable'] = "net.inet.tcp.inflight.enable"; + $config['sysctl']['item'][16]['descr'] = gettext("The system will attempt to calculate the bandwidth delay product for each connection and limit the amount of data queued to the network to just the amount required to maintain optimum throughput. "); + $config['sysctl']['item'][16]['value'] = "default"; + + $config['sysctl']['item'][17]['tunable'] = "net.inet.icmp.icmplim"; + $config['sysctl']['item'][17]['descr'] = gettext("Set ICMP Limits"); + $config['sysctl']['item'][17]['value'] = "default"; + + $config['sysctl']['item'][18]['tunable'] = "net.inet.tcp.tso"; + $config['sysctl']['item'][18]['descr'] = gettext("TCP Offload engine"); + $config['sysctl']['item'][18]['value'] = "default"; + + $config['sysctl']['item'][19]['tunable'] = "net.inet.ip.portrange.first"; + $config['sysctl']['item'][19]['descr'] = "Set the ephemeral port range starting port"; + $config['sysctl']['item'][19]['value'] = "default"; + + $config['sysctl']['item'][20]['tunable'] = "hw.syscons.kbd_reboot"; + $config['sysctl']['item'][20]['descr'] = "Enables ctrl+alt+delete"; + $config['sysctl']['item'][20]['value'] = "default"; + + $config['sysctl']['item'][21]['tunable'] = "kern.ipc.maxsockbuf"; + $config['sysctl']['item'][21]['descr'] = "Maximum socket buffer size"; + $config['sysctl']['item'][21]['value'] = "default"; + + } +} + + +function upgrade_041_to_042() { + global $config; + if (isset($config['shaper'])) { + unset($config['shaper']); + } + if (isset($config['ezshaper'])) { + unset($config['ezshaper']); + } +} + + +function upgrade_042_to_043() { + global $config; + /* migrate old interface gateway to the new gateways config */ + $iflist = get_configured_interface_list(false, true); + $gateways = array(); + $i = 0; + foreach ($iflist as $ifname => $interface) { + if (! interface_has_gateway($ifname)) { + continue; + } + $config['gateways']['gateway_item'][$i] = array(); + if (is_ipaddr($config['interfaces'][$ifname]['gateway'])) { + $config['gateways']['gateway_item'][$i]['gateway'] = $config['interfaces'][$ifname]['gateway']; + $config['gateways']['gateway_item'][$i]['descr'] = sprintf(gettext("Interface %s Static Gateway"), $ifname); + } else { + $config['gateways']['gateway_item'][$i]['gateway'] = "dynamic"; + $config['gateways']['gateway_item'][$i]['descr'] = sprintf(gettext("Interface %s Dynamic Gateway"), $ifname); + } + $config['gateways']['gateway_item'][$i]['interface'] = $ifname; + $config['gateways']['gateway_item'][$i]['name'] = "GW_" . strtoupper($ifname); + /* add default gateway bit for wan on upgrade */ + if ($ifname == "wan") { + $config['gateways']['gateway_item'][$i]['defaultgw'] = true; + } + if (is_ipaddr($config['interfaces'][$ifname]['use_rrd_gateway'])) { + $config['gateways']['gateway_item'][$i]['monitor'] = $config['interfaces'][$ifname]['use_rrd_gateway']; + unset($config['interfaces'][$ifname]['use_rrd_gateway']); + } + $config['interfaces'][$ifname]['gateway'] = $config['gateways']['gateway_item'][$i]['name']; + + /* Update all filter rules which might reference this gateway */ + $j = 0; + foreach ($config['filter']['rule'] as $rule) { + if (is_ipaddr($rule['gateway'])) { + if ($rule['gateway'] == $config['gateways']['gateway_item'][$i]['gateway']) { + $config['filter']['rule'][$j]['gateway'] = $config['gateways']['gateway_item'][$i]['name']; + } else if ($rule['gateway'] == $ifname) { + $config['filter']['rule'][$j]['gateway'] = $config['gateways']['gateway_item'][$i]['name']; + } + } + $j++; + } + + /* rename old Quality RRD files in the process */ + $rrddbpath = "/var/db/rrd"; + $gwname = "GW_" . strtoupper($ifname); + if (is_readable("{$rrddbpath}/{$ifname}-quality.rrd")) { + rename("{$rrddbpath}/{$ifname}-quality.rrd", "{$rrddbpath}/{$gwname}-quality.rrd"); + } + $i++; + } +} + + +function upgrade_043_to_044() { + global $config; + + /* migrate static routes to the new gateways config */ + $gateways = return_gateways_array(true); + $i = 0; + if (is_array($config['staticroutes']['route'])) { + $gwmap = array(); + foreach ($config['staticroutes']['route'] as $idx => $sroute) { + $found = false; + foreach ($gateways as $gwname => $gw) { + if ($gw['gateway'] == $sroute['gateway']) { + $config['staticroutes']['route'][$idx]['gateway'] = $gwname; + $found = true; + break; + } + } + if ($gwmap[$sroute['gateway']]) { + /* We already added a gateway name for this IP */ + $config['staticroutes']['route'][$idx]['gateway'] = "{$gwmap[$sroute['gateway']]}"; + $found = true; + } + + if ($found == false) { + $gateway = array(); + $gateway['name'] = "SROUTE{$i}"; + $gwmap[$sroute['gateway']] = $gateway['name']; + $gateway['gateway'] = $sroute['gateway']; + $gateway['interface'] = $sroute['interface']; + $gateway['descr'] = sprintf(gettext("Upgraded static route for %s"), $sroute['network']); + if (!is_array($config['gateways']['gateway_item'])) { + $config['gateways']['gateway_item'] = array(); + } + $config['gateways']['gateway_item'][] = $gateway; + $config['staticroutes']['route'][$idx]['gateway'] = $gateway['name']; + $i++; + } + } + } +} + + +function upgrade_044_to_045() { + global $config; + $iflist = get_configured_interface_list(false, true); + if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) { + $i = 0; + foreach ($config['vlans']['vlan'] as $id => $vlan) { + /* Make sure to update the interfaces section with the right name */ + $vlan_name = "{$vlan['if']}_vlan{$vlan['tag']}"; + foreach ($iflist as $ifname) { + if ($config['interfaces'][$ifname]['if'] == "vlan{$i}") { + $config['interfaces'][$ifname]['if'] = $vlan_name; + continue; + } + } + $config['vlans']['vlan'][$i]['vlanif'] = "{$vlan_name}"; + $i++; + } + } +} + + +function upgrade_045_to_046() { + global $config; + /* Load up monitors that are in the default config for 2.0 but not in 1.2.3 + thus wouldn't be in an upgraded config. */ + $config['load_balancer']['monitor_type'] = array ( + array ('name' => 'ICMP', + 'type' => 'icmp', + 'descr' => 'ICMP', + 'options' => '', + ), + array ('name' => 'TCP', + 'type' => 'tcp', + 'descr' => 'Generic TCP', + 'options' => '', + ), + array ('name' => 'HTTP', + 'type' => 'http', + 'descr' => 'Generic HTTP', + 'options' => + array ('path' => '/', + 'host' => '', + 'code' => '200', + ), + ), + array ('name' => 'HTTPS', + 'type' => 'https', + 'descr' => 'Generic HTTPS', + 'options' => + array ('path' => '/', + 'host' => '', + 'code' => '200', + ), + ), + array ('name' => 'SMTP', + 'type' => 'send', + 'descr' => 'Generic SMTP', + 'options' => + array ('send' => '', + 'expect' => '220 *', + ), + ), + ); + /* Upgrade load balancer from slb to relayd */ + if (is_array($config['load_balancer']['virtual_server']) && count($config['load_balancer']['virtual_server'])) { + $vs_a = &$config['load_balancer']['virtual_server']; + $pool_a = &$config['load_balancer']['lbpool']; + $pools = array(); + /* Index pools by name */ + if (is_array($pool_a)) { + for ($i = 0; isset($pool_a[$i]); $i++) { + if ($pool_a[$i]['type'] == "server") { + $pools[$pool_a[$i]['name']] = $pool_a[$i]; + } + } + } + /* Convert sitedown entries to pools and re-attach */ + for ($i = 0; isset($vs_a[$i]); $i++) { + /* Set mode while we're here. */ + $vs_a[$i]['mode'] = "redirect_mode"; + if (isset($vs_a[$i]['sitedown'])) { + $pool = array(); + $pool['type'] = 'server'; + $pool['behaviour'] = 'balance'; + $pool['name'] = "{$vs_a[$i]['name']}-sitedown"; + $pool['descr'] = sprintf(gettext("Sitedown pool for VS: %s"), $vs_a[$i]['name']); + if (is_array($vs_a[$i]['pool'])) { + $vs_a[$i]['pool'] = $vs_a[$i]['pool'][0]; + } + $pool['port'] = $pools[$vs_a[$i]['pool']]['port']; + $pool['servers'] = array(); + $pool['servers'][] = $vs_a[$i]['sitedown']; + $pool['monitor'] = $pools[$vs_a[$i]['pool']]['monitor']; + $pool_a[] = $pool; + $vs_a[$i]['sitedown'] = $pool['name']; + } + } + } + if (count($config['load_balancer']) == 0) { + unset($config['load_balancer']); + } + mwexec('/usr/sbin/pw groupadd -n _relayd -g 913'); + mwexec('/usr/sbin/pw useradd -n _relayd -c "Relay Daemon" -d /var/empty -s /usr/sbin/nologin -u 913 -g 913'); +} + + +function upgrade_046_to_047() { + global $config; + /* Upgrade IPsec from tunnel to phase1/phase2 */ + + if (is_array($config['ipsec']['tunnel'])) { + + $a_phase1 = array(); + $a_phase2 = array(); + $ikeid = 0; + + foreach ($config['ipsec']['tunnel'] as $tunnel) { + + unset($ph1ent); + unset($ph2ent); + + /* + * attempt to locate an enabled phase1 + * entry that matches the peer gateway + */ + + if (!isset($tunnel['disabled'])) { + + $remote_gateway = $tunnel['remote-gateway']; + + foreach ($a_phase1 as $ph1tmp) { + if ($ph1tmp['remote-gateway'] == $remote_gateway) { + $ph1ent = $ph1tmp; + break; + } + } + } + + /* none found, create a new one */ + + if (!isset($ph1ent)) { + + /* build new phase1 entry */ + + $ph1ent = array(); + + $ph1ent['ikeid'] = ++$ikeid; + + if (isset($tunnel['disabled'])) { + $ph1ent['disabled'] = $tunnel['disabled']; + } + + /* convert to the new vip[$vhid] name */ + if (preg_match("/^carp/", $tunnel['interface'])) { + $carpid = str_replace("carp", "", $tunnel['interface']); + $tunnel['interface'] = "vip" . $config['virtualip']['vip'][$carpid]['vhid']; + } + $ph1ent['interface'] = $tunnel['interface']; + $ph1ent['remote-gateway'] = $tunnel['remote-gateway']; + $ph1ent['descr'] = $tunnel['descr']; + + $ph1ent['mode'] = $tunnel['p1']['mode']; + + if (isset($tunnel['p1']['myident']['myaddress'])) { + $ph1ent['myid_type'] = "myaddress"; + } + if (isset($tunnel['p1']['myident']['address'])) { + $ph1ent['myid_type'] = "address"; + $ph1ent['myid_data'] = $tunnel['p1']['myident']['address']; + } + if (isset($tunnel['p1']['myident']['fqdn'])) { + $ph1ent['myid_type'] = "fqdn"; + $ph1ent['myid_data'] = $tunnel['p1']['myident']['fqdn']; + } + if (isset($tunnel['p1']['myident']['ufqdn'])) { + $ph1ent['myid_type'] = "user_fqdn"; + $ph1ent['myid_data'] = $tunnel['p1']['myident']['ufqdn']; + } + if (isset($tunnel['p1']['myident']['asn1dn'])) { + $ph1ent['myid_type'] = "asn1dn"; + $ph1ent['myid_data'] = $tunnel['p1']['myident']['asn1dn']; + } + if (isset($tunnel['p1']['myident']['dyn_dns'])) { + $ph1ent['myid_type'] = "dyn_dns"; + $ph1ent['myid_data'] = $tunnel['p1']['myident']['dyn_dns']; + } + + $ph1ent['peerid_type'] = "peeraddress"; + + switch ($tunnel['p1']['encryption-algorithm']) { + case "des": + $ph1alg = array('name' => 'des'); + break; + case "3des": + $ph1alg = array('name' => '3des'); + break; + case "blowfish": + $ph1alg = array('name' => 'blowfish', 'keylen' => '128'); + break; + case "cast128": + $ph1alg = array('name' => 'cast128'); + break; + case "rijndael": + $ph1alg = array('name' => 'aes', 'keylen' => '128'); + break; + case "rijndael 256": + case "aes 256": + $ph1alg = array('name' => 'aes', 'keylen' => '256'); + break; + } + + $ph1ent['encryption-algorithm'] = $ph1alg; + $ph1ent['hash-algorithm'] = $tunnel['p1']['hash-algorithm']; + $ph1ent['dhgroup'] = $tunnel['p1']['dhgroup']; + $ph1ent['lifetime'] = $tunnel['p1']['lifetime']; + $ph1ent['authentication_method'] = $tunnel['p1']['authentication_method']; + + if (isset($tunnel['p1']['pre-shared-key'])) { + $ph1ent['pre-shared-key'] = $tunnel['p1']['pre-shared-key']; + } + if (isset($tunnel['p1']['cert'])) { + $ph1ent['cert'] = $tunnel['p1']['cert']; + } + if (isset($tunnel['p1']['peercert'])) { + $ph1ent['peercert'] = $tunnel['p1']['peercert']; + } + if (isset($tunnel['p1']['private-key'])) { + $ph1ent['private-key'] = $tunnel['p1']['private-key']; + } + + $ph1ent['nat_traversal'] = "on"; + $ph1ent['dpd_enable'] = 1; + $ph1ent['dpd_delay'] = 10; + $ph1ent['dpd_maxfail'] = 5; + + $a_phase1[] = $ph1ent; + } + + /* build new phase2 entry */ + + $ph2ent = array(); + + $ph2ent['ikeid'] = $ph1ent['ikeid']; + + if (isset($tunnel['disabled'])) { + $ph1ent['disabled'] = $tunnel['disabled']; + } + + $ph2ent['descr'] = sprintf(gettext("phase2 for %s"), $tunnel['descr']); + + $type = "lan"; + if ($tunnel['local-subnet']['network']) { + $type = $tunnel['local-subnet']['network']; + } + if ($tunnel['local-subnet']['address']) { + list($address,$netbits) = explode("/",$tunnel['local-subnet']['address']); + if (is_null($netbits)) { + $type = "address"; + } else { + $type = "network"; + } + } + + switch ($type) { + case "address": + $ph2ent['localid'] = array('type' => $type,'address' => $address); + break; + case "network": + $ph2ent['localid'] = array('type' => $type,'address' => $address,'netbits' => $netbits); + break; + default: + $ph2ent['localid'] = array('type' => $type); + break; + } + + list($address,$netbits) = explode("/",$tunnel['remote-subnet']); + $ph2ent['remoteid'] = array('type' => 'network','address' => $address,'netbits' => $netbits); + + $ph2ent['protocol'] = $tunnel['p2']['protocol']; + + $aes_count = 0; + foreach ($tunnel['p2']['encryption-algorithm-option'] as $tunalg) { + $aes_found = false; + switch ($tunalg) { + case "des": + $ph2alg = array('name' => 'des'); + break; + case "3des": + $ph2alg = array('name' => '3des'); + break; + case "blowfish": + $ph2alg = array('name' => 'blowfish', 'keylen' => 'auto'); + break; + case "cast128": + $ph2alg = array('name' => 'cast128'); + break; + case "rijndael": + case "rijndael 256": + case "aes 256": + $ph2alg = array('name' => 'aes', 'keylen' => 'auto'); + $aes_found = true; + $aes_count++; + break; + } + + if (!$aes_found || ($aes_count < 2)) { + $ph2ent['encryption-algorithm-option'][] = $ph2alg; + } + } + + $ph2ent['hash-algorithm-option'] = $tunnel['p2']['hash-algorithm-option']; + $ph2ent['pfsgroup'] = $tunnel['p2']['pfsgroup']; + $ph2ent['lifetime'] = $tunnel['p2']['lifetime']; + + if (isset($tunnel['pinghost']['pinghost'])) { + $ph2ent['pinghost'] = $tunnel['pinghost']; + } + + $a_phase2[] = $ph2ent; + } + + unset($config['ipsec']['tunnel']); + $config['ipsec']['phase1'] = $a_phase1; + $config['ipsec']['phase2'] = $a_phase2; + } + + /* Upgrade Mobile IPsec */ + if (isset($config['ipsec']['mobileclients']) && + is_array($config['ipsec']['mobileclients']) && + is_array($config['ipsec']['mobileclients']['p1']) && + is_array($config['ipsec']['mobileclients']['p2'])) { + + if (isset($config['ipsec']['mobileclients']['enable'])) { + $config['ipsec']['client']['enable'] = true; + $config['ipsec']['client']['user_source'] = 'system'; + $config['ipsec']['client']['group_source'] = 'system'; + } + + $mobilecfg = $config['ipsec']['mobileclients']; + + $ph1ent = array(); + $ph1ent['ikeid'] = ++$ikeid; + + if (!isset($mobilecfg['enable'])) { + $ph1ent['disabled'] = true; + } + + /* Assume WAN since mobile tunnels couldn't be on a separate interface on 1.2.x */ + $ph1ent['interface'] = 'wan'; + $ph1ent['descr'] = "Mobile Clients (upgraded)"; + $ph1ent['mode'] = $mobilecfg['p1']['mode']; + + if (isset($mobilecfg['p1']['myident']['myaddress'])) { + $ph1ent['myid_type'] = "myaddress"; + } + if (isset($mobilecfg['p1']['myident']['address'])) { + $ph1ent['myid_type'] = "address"; + $ph1ent['myid_data'] = $mobilecfg['p1']['myident']['address']; + } + if (isset($mobilecfg['p1']['myident']['fqdn'])) { + $ph1ent['myid_type'] = "fqdn"; + $ph1ent['myid_data'] = $mobilecfg['p1']['myident']['fqdn']; + } + if (isset($mobilecfg['p1']['myident']['ufqdn'])) { + $ph1ent['myid_type'] = "user_fqdn"; + $ph1ent['myid_data'] = $mobilecfg['p1']['myident']['ufqdn']; + } + if (isset($mobilecfg['p1']['myident']['asn1dn'])) { + $ph1ent['myid_type'] = "asn1dn"; + $ph1ent['myid_data'] = $mobilecfg['p1']['myident']['asn1dn']; + } + if (isset($mobilecfg['p1']['myident']['dyn_dns'])) { + $ph1ent['myid_type'] = "dyn_dns"; + $ph1ent['myid_data'] = $mobilecfg['p1']['myident']['dyn_dns']; + } + $ph1ent['peerid_type'] = "fqdn"; + $ph1ent['peerid_data'] = ""; + + switch ($mobilecfg['p1']['encryption-algorithm']) { + case "des": + $ph1alg = array('name' => 'des'); + break; + case "3des": + $ph1alg = array('name' => '3des'); + break; + case "blowfish": + $ph1alg = array('name' => 'blowfish', 'keylen' => '128'); + break; + case "cast128": + $ph1alg = array('name' => 'cast128'); + break; + case "rijndael": + $ph1alg = array('name' => 'aes', 'keylen' => '128'); + break; + case "rijndael 256": + case "aes 256": + $ph1alg = array('name' => 'aes', 'keylen' => '256'); + break; + } + + $ph1ent['encryption-algorithm'] = $ph1alg; + $ph1ent['hash-algorithm'] = $mobilecfg['p1']['hash-algorithm']; + $ph1ent['dhgroup'] = $mobilecfg['p1']['dhgroup']; + $ph1ent['lifetime'] = $mobilecfg['p1']['lifetime']; + $ph1ent['authentication_method'] = $mobilecfg['p1']['authentication_method']; + + if (isset($mobilecfg['p1']['cert'])) { + $ph1ent['cert'] = $mobilecfg['p1']['cert']; + } + if (isset($mobilecfg['p1']['peercert'])) { + $ph1ent['peercert'] = $mobilecfg['p1']['peercert']; + } + if (isset($mobilecfg['p1']['private-key'])) { + $ph1ent['private-key'] = $mobilecfg['p1']['private-key']; + } + + $ph1ent['nat_traversal'] = "on"; + $ph1ent['dpd_enable'] = 1; + $ph1ent['dpd_delay'] = 10; + $ph1ent['dpd_maxfail'] = 5; + $ph1ent['mobile'] = true; + + $ph2ent = array(); + $ph2ent['ikeid'] = $ph1ent['ikeid']; + $ph2ent['descr'] = "phase2 for ".$mobilecfg['descr']; + $ph2ent['localid'] = array('type' => 'none'); + $ph2ent['remoteid'] = array('type' => 'mobile'); + $ph2ent['protocol'] = $mobilecfg['p2']['protocol']; + + $aes_count = 0; + foreach ($mobilecfg['p2']['encryption-algorithm-option'] as $tunalg) { + $aes_found = false; + switch ($tunalg) { + case "des": + $ph2alg = array('name' => 'des'); + break; + case "3des": + $ph2alg = array('name' => '3des'); + break; + case "blowfish": + $ph2alg = array('name' => 'blowfish', 'keylen' => 'auto'); + break; + case "cast128": + $ph2alg = array('name' => 'cast128'); + break; + case "rijndael": + case "rijndael 256": + case "aes 256": + $ph2alg = array('name' => 'aes', 'keylen' => 'auto'); + $aes_found = true; + $aes_count++; + break; + } + + if (!$aes_found || ($aes_count < 2)) { + $ph2ent['encryption-algorithm-option'][] = $ph2alg; + } + } + $ph2ent['hash-algorithm-option'] = $mobilecfg['p2']['hash-algorithm-option']; + $ph2ent['pfsgroup'] = $mobilecfg['p2']['pfsgroup']; + $ph2ent['lifetime'] = $mobilecfg['p2']['lifetime']; + $ph2ent['mobile'] = true; + + $config['ipsec']['phase1'][] = $ph1ent; + $config['ipsec']['phase2'][] = $ph2ent; + unset($config['ipsec']['mobileclients']); + } +} + + +function upgrade_047_to_048() { + global $config; + if (!empty($config['dyndns'])) { + $config['dyndnses'] = array(); + $config['dyndnses']['dyndns'] = array(); + if (isset($config['dyndns'][0]['host'])) { + $tempdyn = array(); + $tempdyn['enable'] = isset($config['dyndns'][0]['enable']); + $tempdyn['type'] = $config['dyndns'][0]['type']; + $tempdyn['wildcard'] = isset($config['dyndns'][0]['wildcard']); + $tempdyn['username'] = $config['dyndns'][0]['username']; + $tempdyn['password'] = $config['dyndns'][0]['password']; + $tempdyn['host'] = $config['dyndns'][0]['host']; + $tempdyn['mx'] = $config['dyndns'][0]['mx']; + $tempdyn['interface'] = "wan"; + $tempdyn['descr'] = sprintf(gettext("Upgraded Dyndns %s"), $tempdyn['type']); + $config['dyndnses']['dyndns'][] = $tempdyn; + } + unset($config['dyndns']); + } + if (!empty($config['dnsupdate'])) { + $pconfig = $config['dnsupdate'][0]; + if (!$pconfig['ttl']) { + $pconfig['ttl'] = 60; + } + if (!$pconfig['keytype']) { + $pconfig['keytype'] = "zone"; + } + $pconfig['interface'] = "wan"; + $config['dnsupdates']['dnsupdate'][] = $pconfig; + unset($config['dnsupdate']); + } + + if (is_array($config['pppoe']) && is_array($config['pppoe'][0])) { + $pconfig = array(); + $pconfig['username'] = $config['pppoe'][0]['username']; + $pconfig['password'] = $config['pppoe'][0]['password']; + $pconfig['provider'] = $config['pppoe'][0]['provider']; + $pconfig['ondemand'] = isset($config['pppoe'][0]['ondemand']); + $pconfig['timeout'] = $config['pppoe'][0]['timeout']; + unset($config['pppoe']); + $config['interfaces']['wan']['pppoe_username'] = $pconfig['username']; + $config['interfaces']['wan']['pppoe_password'] = $pconfig['password']; + $config['interfaces']['wan']['provider'] = $pconfig['provider']; + $config['interfaces']['wan']['ondemand'] = isset($pconfig['ondemand']); + $config['interfaces']['wan']['timeout'] = $pconfig['timeout']; + } + if (is_array($config['pptp'])) { + $pconfig = array(); + $pconfig['username'] = $config['pptp']['username']; + $pconfig['password'] = $config['pptp']['password']; + $pconfig['provider'] = $config['pptp']['provider']; + $pconfig['ondemand'] = isset($config['pptp']['ondemand']); + $pconfig['timeout'] = $config['pptp']['timeout']; + unset($config['pptp']); + $config['interfaces']['wan']['pptp_username'] = $pconfig['username']; + $config['interfaces']['wan']['pptp_password'] = $pconfig['password']; + $config['interfaces']['wan']['provider'] = $pconfig['provider']; + $config['interfaces']['wan']['ondemand'] = isset($pconfig['ondemand']); + $config['interfaces']['wan']['timeout'] = $pconfig['timeout']; + } +} + + +function upgrade_048_to_049() { + global $config; + /* setup new all users group */ + $all = array(); + $all['name'] = "all"; + $all['description'] = gettext("All Users"); + $all['scope'] = "system"; + $all['gid'] = 1998; + $all['member'] = array(); + + if (!is_array($config['system']['user'])) { + $config['system']['user'] = array(); + } + if (!is_array($config['system']['group'])) { + $config['system']['group'] = array(); + } + + /* work around broken uid assignments */ + $config['system']['nextuid'] = 2000; + foreach ($config['system']['user'] as & $user) { + if (isset($user['uid']) && !$user['uid']) { + continue; + } + $user['uid'] = $config['system']['nextuid']++; + } + + /* work around broken gid assignments */ + $config['system']['nextgid'] = 2000; + foreach ($config['system']['group'] as & $group) { + if ($group['name'] == $g['admin_group']) { + $group['gid'] = 1999; + } else { + $group['gid'] = $config['system']['nextgid']++; + } + } + + /* build group membership information */ + foreach ($config['system']['group'] as & $group) { + $group['member'] = array(); + foreach ($config['system']['user'] as & $user) { + $groupnames = explode(",", $user['groupname']); + if (in_array($group['name'],$groupnames)) { + $group['member'][] = $user['uid']; + } + } + } + + /* reset user group information */ + foreach ($config['system']['user'] as & $user) { + unset($user['groupname']); + $all['member'][] = $user['uid']; + } + + /* reset group scope information */ + foreach ($config['system']['group'] as & $group) { + if ($group['name'] != $g['admin_group']) { + $group['scope'] = "user"; + } + } + + /* insert new all group */ + $groups = Array(); + $groups[] = $all; + $groups = array_merge($config['system']['group'],$groups); + $config['system']['group'] = $groups; +} + + +function upgrade_049_to_050() { + global $config; + + if (!is_array($config['system']['user'])) { + $config['system']['user'] = array(); + } + /* update user privileges */ + foreach ($config['system']['user'] as & $user) { + $privs = array(); + if (!is_array($user['priv'])) { + unset($user['priv']); + continue; + } + foreach ($user['priv'] as $priv) { + switch ($priv['id']) { + case "hasshell": + $privs[] = "user-shell-access"; + break; + case "copyfiles": + $privs[] = "user-copy-files"; + break; + } + } + $user['priv'] = $privs; + } + + /* update group privileges */ + foreach ($config['system']['group'] as & $group) { + $privs = array(); + if (!is_array($group['pages'])) { + unset($group['pages']); + continue; + } + foreach ($group['pages'] as $page) { + $priv = map_page_privname($page); + if ($priv) { + $privs[] = $priv; + } + } + unset($group['pages']); + $group['priv'] = $privs; + } + + /* sync all local account information */ + local_sync_accounts(); +} + + +function upgrade_050_to_051() { + global $config; + $pconfig = array(); + $pconfig['descr'] = "Set to 0 to disable filtering on the incoming and outgoing member interfaces."; + $pconfig['tunable'] = "net.link.bridge.pfil_member"; + $pconfig['value'] = "1"; + $config['sysctl']['item'][] = $pconfig; + $pconfig = array(); + $pconfig['descr'] = "Set to 1 to enable filtering on the bridge interface"; + $pconfig['tunable'] = "net.link.bridge.pfil_bridge"; + $pconfig['value'] = "0"; + $config['sysctl']['item'][] = $pconfig; + + unset($config['bridge']); + + $convert_bridges = false; + foreach ($config['interfaces'] as $intf) { + if (isset($intf['bridge']) && $intf['bridge'] <> "") { + $config['bridges'] = array(); + $config['bridges']['bridged'] = array(); + $convert_bridges = true; + break; + } + } + if ($convert_bridges == true) { + $i = 0; + foreach ($config['interfaces'] as $ifr => &$intf) { + if (isset($intf['bridge']) && $intf['bridge'] <> "") { + $nbridge = array(); + $nbridge['members'] = "{$ifr},{$intf['bridge']}"; + $nbridge['descr'] = sprintf(gettext("Converted bridged %s"), $ifr); + $nbridge['bridgeif'] = "bridge{$i}"; + $config['bridges']['bridged'][] = $nbridge; + unset($intf['bridge']); + $i++; + } + } + } +} + + +function upgrade_051_to_052() { + global $config; + $config['openvpn'] = array(); + if (!is_array($config['ca'])) { + $config['ca'] = array(); + } + if (!is_array($config['cert'])) { + $config['cert'] = array(); + } + + $vpnid = 1; + + /* openvpn server configurations */ + if (is_array($config['installedpackages']['openvpnserver'])) { + $config['openvpn']['openvpn-server'] = array(); + + $index = 1; + foreach ($config['installedpackages']['openvpnserver']['config'] as $server) { + + if (!is_array($server)) { + continue; + } + + if ($server['auth_method'] == "pki") { + + /* create ca entry */ + $ca = array(); + $ca['refid'] = uniqid(); + $ca['descr'] = "OpenVPN Server CA #{$index}"; + $ca['crt'] = $server['ca_cert']; + $config['ca'][] = $ca; + + /* create ca reference */ + unset($server['ca_cert']); + $server['caref'] = $ca['refid']; + + /* create a crl entry if needed */ + if (!empty($server['crl'][0])) { + $crl = array(); + $crl['refid'] = uniqid(); + $crl['descr'] = "Imported OpenVPN CRL #{$index}"; + $crl['caref'] = $ca['refid']; + $crl['text'] = $server['crl'][0]; + if (!is_array($config['crl'])) { + $config['crl'] = array(); + } + $config['crl'][] = $crl; + $server['crlref'] = $crl['refid']; + } + unset($server['crl']); + + /* create cert entry */ + $cert = array(); + $cert['refid'] = uniqid(); + $cert['descr'] = "OpenVPN Server Certificate #{$index}"; + $cert['crt'] = $server['server_cert']; + $cert['prv'] = $server['server_key']; + $config['cert'][] = $cert; + + /* create cert reference */ + unset($server['server_cert']); + unset($server['server_key']); + $server['certref'] = $cert['refid']; + + $index++; + } + + /* determine operational mode */ + if ($server['auth_method'] == 'pki') { + if ($server['nopool']) { + $server['mode'] = "p2p_tls"; + } else { + $server['mode'] = "server_tls"; + } + } else { + $server['mode'] = "p2p_shared_key"; + } + unset($server['auth_method']); + + /* modify configuration values */ + $server['dh_length'] = 1024; + unset($server['dh_params']); + if (!$server['interface']) { + $server['interface'] = 'any'; + } + $server['tunnel_network'] = $server['addresspool']; + unset($server['addresspool']); + if (isset($server['use_lzo']) && ($server['use_lzo'] == "on")) { + $server['compression'] = "on"; + unset($server['use_lzo']); + } + if ($server['nopool']) { + $server['pool_enable'] = false; + } else { + $server['pool_enable'] = "yes"; + } + unset($server['nopool']); + $server['dns_domain'] = $server['dhcp_domainname']; + unset($server['dhcp_domainname']); + + $tmparr = explode(";", $server['dhcp_dns'], 4); + $d=1; + foreach ($tmparr as $tmpa) { + $server["dns_server{$d}"] = $tmpa; + $d++; + } + unset($server['dhcp_dns']); + + $tmparr = explode(";", $server['dhcp_ntp'], 2); + $d=1; + foreach ($tmparr as $tmpa) { + $server["ntp_server{$d}"] = $tmpa; + $d++; + } + unset($server['dhcp_ntp']); + + if ($server['dhcp_nbtdisable']) { + $server['netbios_enable'] = false; + } else { + $server['netbios_enable'] = "yes"; + } + unset($server['dhcp_nbtdisable']); + $server['netbios_ntype'] = $server['dhcp_nbttype']; + unset($server['dhcp_nbttype']); + $server['netbios_scope'] = $server['dhcp_nbtscope']; + unset($server['dhcp_nbtscope']); + + $tmparr = explode(";", $server['dhcp_nbdd'], 2); + $d=1; + foreach ($tmparr as $tmpa) { + $server["nbdd_server{$d}"] = $tmpa; + $d++; + } + unset($server['dhcp_nbdd']); + + $tmparr = explode(";", $server['dhcp_wins'], 2); + $d=1; + foreach ($tmparr as $tmpa) { + $server["wins_server{$d}"] = $tmpa; + $d++; + } + unset($server['dhcp_wins']); + + if (!empty($server['disable'])) { + $server['disable'] = true; + } else { + unset($server['disable']); + } + + /* allocate vpnid */ + $server['vpnid'] = $vpnid++; + + if (!empty($server['custom_options'])) { + $cstmopts = array(); + $tmpcstmopts = explode(";", $server['custom_options']); + $assigned_if = ""; + $tmpstr = ""; + foreach ($tmpcstmopts as $tmpcstmopt) { + $tmpstr = str_replace(" ", "", $tmpcstmopt); + if (substr($tmpstr,0 ,6) == "devtun") { + $assigned_if = substr($tmpstr, 3); + continue; + } else if (substr($tmpstr, 0, 5) == "local") { + $localip = substr($tmpstr, 5); + $server['ipaddr'] = str_replace("\n", "", $localip); + } else { + $cstmopts[] = $tmpcstmopt; + } + } + $server['custom_options'] = implode(";", $cstmopts); + if (!empty($assigned_if)) { + foreach ($config['interfaces'] as $iface => $cfgif) { + if ($cfgif['if'] == $assigned_if) { + $config['interfaces'][$iface]['if'] = "ovpns{$server['vpnid']}"; + break; + } + } + } + } + + $config['openvpn']['openvpn-server'][] = $server; + } + unset($config['installedpackages']['openvpnserver']); + } + + /* openvpn client configurations */ + if (is_array($config['installedpackages']['openvpnclient'])) { + $config['openvpn']['openvpn-client'] = array(); + + $index = 1; + foreach ($config['installedpackages']['openvpnclient']['config'] as $client) { + + if (!is_array($client)) { + continue; + } + + if ($client['auth_method'] == "pki") { + + /* create ca entry */ + $ca = array(); + $ca['refid'] = uniqid(); + $ca['descr'] = "OpenVPN Client CA #{$index}"; + $ca['crt'] = $client['ca_cert']; + $ca['crl'] = $client['crl']; + $config['ca'][] = $ca; + + /* create ca reference */ + unset($client['ca_cert']); + unset($client['crl']); + $client['caref'] = $ca['refid']; + + /* create cert entry */ + $cert = array(); + $cert['refid'] = uniqid(); + $cert['descr'] = "OpenVPN Client Certificate #{$index}"; + $cert['crt'] = $client['client_cert']; + $cert['prv'] = $client['client_key']; + $config['cert'][] = $cert; + + /* create cert reference */ + unset($client['client_cert']); + unset($client['client_key']); + $client['certref'] = $cert['refid']; + + $index++; + } + + /* determine operational mode */ + if ($client['auth_method'] == 'pki') { + $client['mode'] = "p2p_tls"; + } else { + $client['mode'] = "p2p_shared_key"; + } + unset($client['auth_method']); + + /* modify configuration values */ + if (!$client['interface']) { + $client['interface'] = 'wan'; + } + $client['tunnel_network'] = $client['interface_ip']; + unset($client['interface_ip']); + $client['server_addr'] = $client['serveraddr']; + unset($client['serveraddr']); + $client['server_port'] = $client['serverport']; + unset($client['serverport']); + $client['proxy_addr'] = $client['poxy_hostname']; + unset($client['proxy_addr']); + if (isset($client['use_lzo']) && ($client['use_lzo'] == "on")) { + $client['compression'] = "on"; + unset($client['use_lzo']); + } + $client['resolve_retry'] = $client['infiniteresolvretry']; + unset($client['infiniteresolvretry']); + + /* allocate vpnid */ + $client['vpnid'] = $vpnid++; + + if (!empty($client['custom_options'])) { + $cstmopts = array(); + $tmpcstmopts = explode(";", $client['custom_options']); + $assigned_if = ""; + $tmpstr = ""; + foreach ($tmpcstmopts as $tmpcstmopt) { + $tmpstr = str_replace(" ", "", $tmpcstmopt); + if (substr($tmpstr,0 ,6) == "devtun") { + $assigned_if = substr($tmpstr, 3); + continue; + } else if (substr($tmpstr, 0, 5) == "local") { + $localip = substr($tmpstr, 5); + $client['ipaddr'] = str_replace("\n", "", $localip); + } else { + $cstmopts[] = $tmpcstmopt; + } + } + $client['custom_options'] = implode(";", $cstmopts); + if (!empty($assigned_if)) { + foreach ($config['interfaces'] as $iface => $cfgif) { + if ($cfgif['if'] == $assigned_if) { + $config['interfaces'][$iface]['if'] = "ovpnc{$client['vpnid']}"; + break; + } + } + } + } + + if (!empty($client['disable'])) { + $client['disable'] = true; + } else { + unset($client['disable']); + } + + $config['openvpn']['openvpn-client'][] = $client; + } + + unset($config['installedpackages']['openvpnclient']); + } + + /* openvpn client specific configurations */ + if (is_array($config['installedpackages']['openvpncsc'])) { + $config['openvpn']['openvpn-csc'] = array(); + + foreach ($config['installedpackages']['openvpncsc']['config'] as $csc) { + + if (!is_array($csc)) { + continue; + } + + /* modify configuration values */ + $csc['common_name'] = $csc['commonname']; + unset($csc['commonname']); + $csc['tunnel_network'] = $csc['ifconfig_push']; + unset($csc['ifconfig_push']); + $csc['dns_domain'] = $csc['dhcp_domainname']; + unset($csc['dhcp_domainname']); + + $tmparr = explode(";", $csc['dhcp_dns'], 4); + $d=1; + foreach ($tmparr as $tmpa) { + $csc["dns_server{$d}"] = $tmpa; + $d++; + } + unset($csc['dhcp_dns']); + + $tmparr = explode(";", $csc['dhcp_ntp'], 2); + $d=1; + foreach ($tmparr as $tmpa) { + $csc["ntp_server{$d}"] = $tmpa; + $d++; + } + unset($csc['dhcp_ntp']); + + if ($csc['dhcp_nbtdisable']) { + $csc['netbios_enable'] = false; + } else { + $csc['netbios_enable'] = "yes"; + } + unset($csc['dhcp_nbtdisable']); + $csc['netbios_ntype'] = $csc['dhcp_nbttype']; + unset($csc['dhcp_nbttype']); + $csc['netbios_scope'] = $csc['dhcp_nbtscope']; + unset($csc['dhcp_nbtscope']); + + $tmparr = explode(";", $csc['dhcp_nbdd'], 2); + $d=1; + foreach ($tmparr as $tmpa) { + $csc["nbdd_server{$d}"] = $tmpa; + $d++; + } + unset($csc['dhcp_nbdd']); + + $tmparr = explode(";", $csc['dhcp_wins'], 2); + $d=1; + foreach ($tmparr as $tmpa) { + $csc["wins_server{$d}"] = $tmpa; + $d++; + } + unset($csc['dhcp_wins']); + + if (!empty($csc['disable'])) { + $csc['disable'] = true; + } else { + unset($csc['disable']); + } + + $config['openvpn']['openvpn-csc'][] = $csc; + } + + unset($config['installedpackages']['openvpncsc']); + } + + if (count($config['openvpn']['openvpn-server']) > 0 || + count($config['openvpn']['openvpn-client']) > 0) { + $ovpnrule = array(); + $ovpnrule['type'] = "pass"; + $ovpnrule['interface'] = "openvpn"; + $ovpnrule['statetype'] = "keep state"; + $ovpnrule['source'] = array(); + $ovpnrule['destination'] = array(); + $ovpnrule['source']['any'] = true; + $ovpnrule['destination']['any'] = true; + $ovpnrule['descr'] = gettext("Auto added OpenVPN rule from config upgrade."); + $config['filter']['rule'][] = $ovpnrule; + } + + /* + * FIXME: hack to keep things working with no installedpackages + * or carp array in the configuration data. + */ + if (!is_array($config['installedpackages'])) { + $config['installedpackages'] = array(); + } + if (!is_array($config['installedpackages']['carp'])) { + $config['installedpackages']['carp'] = array(); + } + +} + + +function upgrade_052_to_053() { + global $config; + if (!is_array($config['ca'])) { + $config['ca'] = array(); + } + if (!is_array($config['cert'])) { + $config['cert'] = array(); + } + + /* migrate advanced admin page webui ssl to certificate manager */ + if ($config['system']['webgui']['certificate'] && + $config['system']['webgui']['private-key']) { + + /* create cert entry */ + $cert = array(); + $cert['refid'] = uniqid(); + $cert['descr'] = "webConfigurator SSL Certificate"; + $cert['crt'] = $config['system']['webgui']['certificate']; + $cert['prv'] = $config['system']['webgui']['private-key']; + $config['cert'][] = $cert; + + /* create cert reference */ + unset($config['system']['webgui']['certificate']); + unset($config['system']['webgui']['private-key']); + $config['system']['webgui']['ssl-certref'] = $cert['refid']; + } + + /* migrate advanced admin page ssh keys to user manager */ + if ($config['system']['ssh']['authorizedkeys']) { + $admin_user =& getUserEntryByUID(0); + $admin_user['authorizedkeys'] = $config['system']['ssh']['authorizedkeys']; + unset($config['system']['ssh']['authorizedkeys']); + } +} + + +function upgrade_053_to_054() { + global $config; + if (is_array($config['load_balancer']['lbpool'])) { + $lbpool_arr = $config['load_balancer']['lbpool']; + $lbpool_srv_arr = array(); + $gateway_group_arr = array(); + $gateways = return_gateways_array(); + $group_name_changes = array(); + if (! is_array($config['gateways']['gateway_item'])) { + $config['gateways']['gateway_item'] = array(); + } + + $a_gateways =& $config['gateways']['gateway_item']; + foreach ($lbpool_arr as $lbpool) { + if ($lbpool['type'] == "gateway") { + // Gateway Groups have to have valid names in pf, old lb pools did not. Clean them up. + $group_name = preg_replace("/[^A-Za-z0-9]/", "", $lbpool['name']); + // If we made and changes, check for collisions and note the change. + if ($group_name != $lbpool['name']) { + // Make sure the name isn't already in use. + foreach ($gateway_group_arr as $gwg) { + // If the name is in use, add some random bits to avoid collision. + if ($gwg['name'] == $group_name) { + $group_name .= uniqid(); + } + } + $group_name_changes[$lbpool['name']] = $group_name; + } + $gateway_group['name'] = $group_name; + $gateway_group['descr'] = $lbpool['descr']; + $gateway_group['trigger'] = "down"; + $gateway_group['item'] = array(); + $i = 0; + foreach ($lbpool['servers'] as $member) { + $split = explode("|", $member); + $interface = $split[0]; + $monitor = $split[1]; + /* on static upgraded configuration we automatically prepend GW_ */ + $static_name = "GW_" . strtoupper($interface); + if (is_ipaddr($monitor)) { + foreach ($a_gateways as & $gw) { + if ($gw['name'] == $static_name) { + $gw['monitor'] = $monitor; + } + } + } + + /* on failover increment tier. Else always assign 1 */ + if ($lbpool['behaviour'] == "failover") { + $i++; + } else { + $i = 1; + } + $gateway_group['item'][] = "$static_name|$i"; + } + $gateway_group_arr[] = $gateway_group; + } else { + $lbpool_srv_arr[] = $lbpool; + } + } + $config['load_balancer']['lbpool'] = $lbpool_srv_arr; + $config['gateways']['gateway_group'] = $gateway_group_arr; + } + // Unset lbpool if we no longer have any server pools + if (count($lbpool_srv_arr) == 0) { + if (empty($config['load_balancer'])) { + unset($config['load_balancer']); + } else { + unset($config['load_balancer']['lbpool']); + } + } else { + $config['load_balancer']['lbpool'] = $lbpool_srv_arr; + } + // Only set the gateway group array if we converted any + if (count($gateway_group_arr) != 0) { + $config['gateways']['gateway_group'] = $gateway_group_arr; + // Update any rules that had a gateway change, if any. + if (count($group_name_changes) > 0) { + foreach ($config['filter']['rule'] as & $rule) { + if (!empty($rule["gateway"]) && array_key_exists($rule["gateway"], $group_name_changes)) { + $rule["gateway"] = $group_name_changes[$rule["gateway"]]; + } + } + } + } +} + + +function upgrade_054_to_055() { + global $config; + global $g; + + /* RRD files changed for quality, traffic and packets graphs */ + //ini_set("max_execution_time", "1800"); + /* convert traffic RRD file */ + global $parsedcfg, $listtags; + $listtags = array("ds", "v", "rra", "row"); + + $rrddbpath = "/var/db/rrd/"; + $rrdtool = "/usr/bin/nice -n20 /usr/local/bin/rrdtool"; + if ($g['platform'] != "pfSense") { + /* restore the databases, if we have one */ + if (restore_rrd()) { + /* Make sure to move the rrd backup out of the way. We will make a new one after converting. */ + @rename("{$g['cf_conf_path']}/rrd.tgz", "{$g['cf_conf_path']}/backup/rrd.tgz"); + } + } + + $rrdinterval = 60; + $valid = $rrdinterval * 2; + + /* Asume GigE for now */ + $downstream = 125000000; + $upstream = 125000000; + + /* build a list of quality databases */ + /* roundtrip has become delay */ + function divide_delay($delayval) { + $delayval = floatval($delayval); + $delayval = ($delayval / 1000); + $delayval = " ". sprintf("%1.10e", $delayval) ." "; + return $delayval; + } + /* the roundtrip times need to be divided by 1000 to get seconds, really */ + $databases = array(); + if (!file_exists($rrddbpath)) { + @mkdir($rrddbpath); + } + chdir($rrddbpath); + $databases = glob("*-quality.rrd"); + rsort($databases); + foreach ($databases as $database) { + $xmldump = "{$database}.old.xml"; + $xmldumpnew = "{$database}.new.xml"; + + if (platform_booting()) { + echo "Migrate RRD database {$database} to new format for IPv6 \n"; + } + mwexec("$rrdtool tune {$rrddbpath}{$database} -r roundtrip:delay 2>&1"); + + dump_rrd_to_xml("{$rrddbpath}/{$database}", "{$g['tmp_path']}/{$xmldump}"); + $rrdold = xml2array(file_get_contents("{$g['tmp_path']}/{$xmldump}"), 1, "tag"); + $rrdold = $rrdold['rrd']; + + $i = 0; + foreach ($rrdold['rra'] as $rra) { + $l = 0; + foreach ($rra['database']['row'] as $row) { + $vnew = divide_delay($row['v'][1]); + $rrdold['rra'][$i]['database']['row'][$l]['v'][1] = $vnew; + $l++; + } + $i++; + } + + file_put_contents("{$g['tmp_path']}/{$xmldumpnew}", dump_xml_config_raw($rrdold, "rrd")); + mwexec("$rrdtool restore -f {$g['tmp_path']}/{$xmldumpnew} {$rrddbpath}/{$database} 2>&1"); + + unset($rrdold); + @unlink("{$g['tmp_path']}/{$xmldump}"); + @unlink("{$g['tmp_path']}/{$xmldumpnew}"); + } + /* let apinger recreate required files */ + if (!platform_booting()) { + setup_gateways_monitor(); + } + + /* build a list of traffic and packets databases */ + $databases = return_dir_as_array($rrddbpath, '/-(traffic|packets)\.rrd$/'); + rsort($databases); + foreach ($databases as $database) { + $databasetmp = "{$database}.tmp"; + $xmldump = "{$database}.old.xml"; + $xmldumptmp = "{$database}.tmp.xml"; + $xmldumpnew = "{$database}.new.xml"; + + if (platform_booting()) { + echo "Migrate RRD database {$database} to new format \n"; + } + /* rename DS source */ + mwexec("$rrdtool tune {$rrddbpath}/{$database} -r in:inpass 2>&1"); + mwexec("$rrdtool tune {$rrddbpath}/{$database} -r out:outpass 2>71"); + + /* dump contents to xml and move database out of the way */ + dump_rrd_to_xml("{$rrddbpath}/{$database}", "{$g['tmp_path']}/{$xmldump}"); + + /* create new rrd database file */ + $rrdcreate = "$rrdtool create {$g['tmp_path']}/{$databasetmp} --step $rrdinterval "; + $rrdcreate .= "DS:inpass:COUNTER:$valid:0:$downstream "; + $rrdcreate .= "DS:outpass:COUNTER:$valid:0:$upstream "; + $rrdcreate .= "DS:inblock:COUNTER:$valid:0:$downstream "; + $rrdcreate .= "DS:outblock:COUNTER:$valid:0:$upstream "; + $rrdcreate .= "RRA:AVERAGE:0.5:1:1000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:5:1000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:60:1000 "; + $rrdcreate .= "RRA:AVERAGE:0.5:720:1000 "; + + create_new_rrd("$rrdcreate"); + /* create temporary xml from new RRD */ + dump_rrd_to_xml("{$g['tmp_path']}/{$databasetmp}", "{$g['tmp_path']}/{$xmldumptmp}"); + + $rrdold = xml2array(file_get_contents("{$g['tmp_path']}/{$xmldump}"), 1, "tag"); + $rrdold = $rrdold['rrd']; + + $rrdnew = xml2array(file_get_contents("{$g['tmp_path']}/{$xmldumptmp}"), 1, "tag"); + $rrdnew = $rrdnew['rrd']; + + /* remove any MAX RRA's. Not needed for traffic. */ + $i = 0; + foreach ($rrdold['rra'] as $rra) { + if (trim($rra['cf']) == "MAX") { + unset($rrdold['rra'][$i]); + } + $i++; + } + + file_put_contents("{$g['tmp_path']}/{$xmldumpnew}", dump_xml_config_raw(migrate_rrd_format($rrdold, $rrdnew), "rrd")); + mwexec("$rrdtool restore -f {$g['tmp_path']}/{$xmldumpnew} {$rrddbpath}/{$database} 2>&1"); + /* we now have the rrd with the new fields, adjust the size now. */ + /* RRA 2 is 60 minutes, RRA 3 is 720 minutes */ + mwexec("/bin/sync"); + mwexec("$rrdtool resize {$rrddbpath}/{$database} 2 GROW 2000;/bin/mv resize.rrd {$rrddbpath}/{$database} 2>&1"); + mwexec("/bin/sync"); + mwexec("$rrdtool resize {$rrddbpath}/{$database} 3 GROW 2000;/bin/mv resize.rrd {$rrddbpath}/{$database} 2>&1"); + unset($rrdxmlarray); + @unlink("{$g['tmp_path']}/{$xmldump}"); + @unlink("{$g['tmp_path']}/{$xmldumpnew}"); + } + if (!platform_booting()) { + enable_rrd_graphing(); + } + /* Let's save the RRD graphs after we run enable RRD graphing */ + /* The function will restore the rrd.tgz so we will save it after */ + exec("cd /; LANG=C NO_REMOUNT=1 RRDDBPATH='{$rrddbpath}' CF_CONF_PATH='{$g['cf_conf_path']}' /etc/rc.backup_rrd.sh"); + unlink_if_exists("{$g['vardb_path']}/rrd/*.xml"); + if (platform_booting()) { + echo "Updating configuration..."; + } +} + + +function upgrade_055_to_056() { + global $config; + + if (!is_array($config['ca'])) { + $config['ca'] = array(); + } + if (!is_array($config['cert'])) { + $config['cert'] = array(); + } + + /* migrate ipsec ca's to cert manager */ + if (is_array($config['ipsec']['cacert'])) { + foreach ($config['ipsec']['cacert'] as & $cacert) { + $ca = array(); + $ca['refid'] = uniqid(); + if (is_array($cacert['cert'])) { + $ca['crt'] = $cacert['cert'][0]; + } else { + $ca['crt'] = $cacert['cert']; + } + $ca['descr'] = $cacert['ident']; + $config['ca'][] = $ca; + } + unset($config['ipsec']['cacert']); + } + + /* migrate phase1 certificates to cert manager */ + if (is_array($config['ipsec']['phase1'])) { + foreach ($config['ipsec']['phase1'] as & $ph1ent) { + $cert = array(); + $cert['refid'] = uniqid(); + $cert['descr'] = "IPsec Peer {$ph1ent['remote-gateway']} Certificate"; + if (is_array($ph1ent['cert'])) { + $cert['crt'] = $ph1ent['cert'][0]; + } else { + $cert['crt'] = $ph1ent['cert']; + } + $cert['prv'] = $ph1ent['private-key']; + $config['cert'][] = $cert; + $ph1ent['certref'] = $cert['refid']; + if ($ph1ent['cert']) { + unset($ph1ent['cert']); + } + if ($ph1ent['private-key']) { + unset($ph1ent['private-key']); + } + if ($ph1ent['peercert']) { + unset($ph1ent['peercert']); + } + } + } +} + + +function upgrade_056_to_057() { + global $config; + + if (!is_array($config['system']['user'])) { + $config['system']['user'] = array(); + } + /* migrate captivate portal to user manager */ + if (is_array($config['captiveportal']['user'])) { + foreach ($config['captiveportal']['user'] as $user) { + // avoid user conflicts + $found = false; + foreach ($config['system']['user'] as $userent) { + if ($userent['name'] == $user['name']) { + $found = true; + break; + } + } + if ($found) { + continue; + } + $user['scope'] = "user"; + if (isset($user['expirationdate'])) { + $user['expires'] = $user['expirationdate']; + unset($user['expirationdate']); + } + if (isset($user['password'])) { + $user['md5-hash'] = $user['password']; + unset($user['password']); + } + $user['uid'] = $config['system']['nextuid']++; + $config['system']['user'][] = $user; + } + unset($config['captiveportal']['user']); + } +} + +function upgrade_057_to_058() { + global $config; + /* set all phase2 entries to tunnel mode */ + if (is_array($config['ipsec']['phase2'])) { + foreach ($config['ipsec']['phase2'] as & $ph2ent) { + $ph2ent['mode'] = 'tunnel'; + } + } +} + +function upgrade_058_to_059() { + global $config; + + if (is_array($config['schedules']['schedule'])) { + foreach ($config['schedules']['schedule'] as & $schedl) { + $schedl['schedlabel'] = uniqid(); + } + } +} + +function upgrade_059_to_060() { + global $config; + require_once("/etc/inc/certs.inc"); + if (is_array($config['ca'])) { + /* Locate issuer for all CAs */ + foreach ($config['ca'] as & $ca) { + $subject = cert_get_subject($ca['crt']); + $issuer = cert_get_issuer($ca['crt']); + if ($issuer <> $subject) { + $issuer_crt =& lookup_ca_by_subject($issuer); + if ($issuer_crt) { + $ca['caref'] = $issuer_crt['refid']; + } + } + } + + /* Locate issuer for all certificates */ + if (is_array($config['cert'])) { + foreach ($config['cert'] as & $cert) { + $subject = cert_get_subject($cert['crt']); + $issuer = cert_get_issuer($cert['crt']); + if ($issuer <> $subject) { + $issuer_crt =& lookup_ca_by_subject($issuer); + if ($issuer_crt) { + $cert['caref'] = $issuer_crt['refid']; + } + } + } + } + } +} + +function upgrade_060_to_061() { + global $config; + + if (is_array($config['interfaces']['wan'])) { + $config['interfaces']['wan']['enable'] = true; + } + if (is_array($config['interfaces']['lan'])) { + $config['interfaces']['lan']['enable'] = true; + } + + /* On 1.2.3 the "mtu" field adjusted MSS. + On 2.x the "mtu" field is actually the MTU. Rename accordingly. + See redmine ticket #1886 + */ + foreach ($config['interfaces'] as $ifr => &$intf) { + if (isset($intf['mtu']) && is_numeric($intf['mtu'])) { + $intf['mss'] = $intf['mtu']; + unset($intf['mtu']); + } + } +} + +function upgrade_061_to_062() { + global $config; + + /* Convert NAT port forwarding rules */ + if (is_array($config['nat']['rule'])) { + $a_nat = &$config['nat']['rule']; + + foreach ($a_nat as &$natent) { + $natent['disabled'] = false; + $natent['nordr'] = false; + + $natent['source'] = array( + "not" => false, + "any" => true, + "port" => "" + ); + + $natent['destination'] = array( + "not" => false, + "address" => $natent['external-address'], + "port" => $natent['external-port'] + ); + + if (empty($natent['destination']['address'])) { + unset($natent['destination']['address']); + $natent['destination']['network'] = $natent['interface'] . 'ip'; + } else if ($natent['destination']['address'] == 'any') { + unset($natent['destination']['address']); + $natent['destination']['any'] = true; + } + + unset($natent['external-address']); + unset($natent['external-port']); + } + + unset($natent); + } +} + +function upgrade_062_to_063() { + /* Upgrade legacy Themes to the new pfsense_ng */ + global $config; + + switch ($config['theme']) { + case "nervecenter": + $config['theme'] = "pfsense_ng"; + break; + } + +} + +function upgrade_063_to_064() { + global $config; + $j=0; + $ifcfg = &$config['interfaces']; + + if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) { + foreach ($config['ppps']['ppp'] as $pppid => $ppp) { + $config['ppps']['ppp'][$pppid]['if'] = "ppp".$j; + $config['ppps']['ppp'][$pppid]['ptpid'] = $j; + $j++; + if (isset($ppp['port'])) { + $config['ppps']['ppp'][$pppid]['ports'] = $ppp['port']; + unset($config['ppps']['ppp'][$pppid]['port']); + } + if (!isset($ppp['type'])) { + $config['ppps']['ppp'][$pppid]['type'] = "ppp"; + } + if (isset($ppp['defaultgw'])) { + unset($config['ppps']['ppp'][$pppid]['defaultgw']); + } + } + } + + if (!is_array($config['ppps']['ppp'])) { + $config['ppps']['ppp'] = array(); + } + $a_ppps = &$config['ppps']['ppp']; + + foreach ($ifcfg as $ifname => $ifinfo) { + $ppp = array(); + // For pppoe conversion + if ($ifinfo['ipaddr'] == "pppoe" || $ifinfo['ipaddr'] == "pptp") { + if (isset($ifinfo['ptpid'])) { + continue; + } + $ppp['ptpid'] = $j; + $ppp['type'] = $ifinfo['ipaddr']; + $ppp['if'] = $ifinfo['ipaddr'].$j; + $ppp['ports'] = $ifinfo['if']; + if ($ifinfo['ipaddr'] == "pppoe") { + $ppp['username'] = $ifinfo['pppoe_username']; + $ppp['password'] = base64_encode($ifinfo['pppoe_password']); + } + if ($ifinfo['ipaddr'] == "pptp") { + $ppp['username'] = $ifinfo['pptp_username']; + $ppp['password'] = base64_encode($ifinfo['pptp_password']); + } + + if (isset($ifinfo['provider'])) { + $ppp['provider'] = $ifinfo['provider']; + } + if (isset($ifinfo['ondemand'])) { + $ppp['ondemand'] = true; + } + if (isset($ifinfo['timeout'])) { + $ppp['idletimeout'] = $ifinfo['timeout']; + } + if (isset($ifinfo['pppoe']['pppoe-reset-type'])) { + $ppp['pppoe-reset-type'] = $ifinfo['pppoe']['pppoe-reset-type']; + if (is_array($config['cron']['item'])) { + for ($i = 0; $i < count($config['cron']['item']); $i++) { + $item = $config['cron']['item'][$i]; + if (strpos($item['command'], "/conf/pppoe{$ifname}restart") !== false) { + $config['cron']['item'][$i]['command'] = "/var/etc/pppoe_restart_" . $ppp['if']; + } + } + } + } + if (isset($ifinfo['local'])) { + $ppp['localip'] = $ifinfo['local']; + } + if (isset($ifinfo['subnet'])) { + $ppp['subnet'] = $ifinfo['subnet']; + } + if (isset($ifinfo['remote'])) { + $ppp['gateway'] = $ifinfo['remote']; + } + + $ifcfg[$ifname]['if'] = $ifinfo['ipaddr'].$j; + $j++; + + unset($ifcfg[$ifname]['pppoe_username']); + unset($ifcfg[$ifname]['pppoe_password']); + unset($ifcfg[$ifname]['provider']); + unset($ifcfg[$ifname]['ondemand']); + unset($ifcfg[$ifname]['timeout']); + unset($ifcfg[$ifname]['pppoe_reset']); + unset($ifcfg[$ifname]['pppoe_preset']); + unset($ifcfg[$ifname]['pppoe']); + unset($ifcfg[$ifname]['pptp_username']); + unset($ifcfg[$ifname]['pptp_password']); + unset($ifcfg[$ifname]['local']); + unset($ifcfg[$ifname]['subnet']); + unset($ifcfg[$ifname]['remote']); + + $a_ppps[] = $ppp; + + } + } +} + +function upgrade_064_to_065() { + /* Disable TSO and LRO in upgraded configs */ + global $config; + $config['system']['disablesegmentationoffloading'] = true; + $config['system']['disablelargereceiveoffloading'] = true; +} + +function upgrade_065_to_066() { + global $config; + + $dhcrelaycfg =& $config['dhcrelay']; + + if (is_array($dhcrelaycfg)) { + $dhcrelayifs = array(); + $foundifs = false; + /* DHCPRelay enabled on any interfaces? */ + foreach ($dhcrelaycfg as $dhcrelayif => $dhcrelayifconf) { + if (isset($dhcrelayifconf['enable'])) { + $dhcrelayifs[] = $dhcrelayif; + unset($dhcrelaycfg['dhcrelayif']); + $foundifs = true; + } + } + if ($foundifs == true) { + $dhcrelaycfg['interface'] = implode(",", $dhcrelayifs); + } + } +} + +function upgrade_066_to_067() { + global $config; + if (isset($config['system']['ca'])) { + $config['ca'] = $config['system']['ca']; + unset($config['system']['ca']); + } + if (isset($config['system']['cert'])) { + $config['cert'] = $config['system']['cert']; + unset($config['system']['cert']); + } +} + +function upgrade_067_to_068() { + global $config; + + if (!empty($config['pppoe'])) { + $config['pppoes'] = array(); + $config['pppoes']['pppoe'] = array(); + $config['pppoes']['pppoe'][] = $config['pppoe'][0]; + + if (is_array($config['pppoe']['user'])) { + $username = array(); + foreach ($config['pppoe']['user'] as $user) { + $usr = $user['name'] . ":" . base64_encode($user['password']); + if ($user['ip']) { + $usr .= ":{$user['ip']}"; + } + $username[] = $usr; + } + $config['pppoes']['pppoe'][0]['username'] = implode(" ", $username); + } + unset($config['pppoe']); + } +} + +function upgrade_068_to_069() { + global $config; + if (!is_array($config['system']['user'])) { + return; + } + foreach ($config['system']['user'] as & $user) { + if (!is_array($user['cert'])) { + continue; + } + $rids = array(); + foreach ($user['cert'] as $id => $cert) { + if (!isset($cert['descr'])) { + continue; + } + $tcert = $cert; + // Make sure each cert gets a refid + if (!isset($tcert['refid'])) { + $tcert['refid'] = uniqid(); + } + // Keep the cert references for this user + $rids[] = $tcert['refid']; + $config['cert'][] = $tcert; + } + // Replace user certs with cert references instead. + if (count($rids) > 0) { + $user['cert'] = $rids; + } + } +} + +function upgrade_069_to_070() { + global $config; + + /* Convert NAT 1:1 rules */ + if (is_array($config['nat']['onetoone'])) { + foreach ($config['nat']['onetoone'] as $nidx => $natent) { + if ($natent['subnet'] == 32) { + $config['nat']['onetoone'][$nidx]['source'] = array("address" => $natent['internal']); + } else { + $config['nat']['onetoone'][$nidx]['source'] = array("address" => $natent['internal'] . "/" . $natent['subnet']); + } + + $config['nat']['onetoone'][$nidx]['destination'] = array("any" => true); + + unset($config['nat']['onetoone'][$nidx]['internal']); + unset($config['nat']['onetoone'][$nidx]['subnet']); + } + + unset($natent); + } +} + +function upgrade_070_to_071() { + global $config; + + if (is_array($config['cron']['item'])) { + foreach ($config['cron']['item'] as $idx => $cronitem) { + if (stristr($cronitem['command'], "checkreload.sh")) { + unset($config['cron']['item'][$idx]); + break; + } + } + } +} + +function rename_field(& $section, $oldname, $newname) { + if (is_array($section)) { + foreach ($section as & $item) { + if (is_array($item) && !empty($item[$oldname])) { + $item[$newname] = $item[$oldname]; + } + if (is_array($item) && isset($item[$oldname])) { + unset($item[$oldname]); + } + } + } +} + +function upgrade_071_to_072() { + global $config; + if (is_array($config['sysctl']) && is_array($config['sysctl']['item'])) { + rename_field($config['sysctl']['item'], 'desc', 'descr'); + } +} + +function upgrade_072_to_073() { + global $config; + if (!is_array($config['load_balancer'])) { + return; + } + if (is_array($config['load_balancer']['monitor_type'])) { + rename_field($config['load_balancer']['monitor_type'], 'desc', 'descr'); + } + if (is_array($config['load_balancer']['lbpool'])) { + rename_field($config['load_balancer']['lbpool'], 'desc', 'descr'); + } + if (is_array($config['load_balancer']['lbaction'])) { + rename_field($config['load_balancer']['lbaction'], 'desc', 'descr'); + } + if (is_array($config['load_balancer']['lbprotocol'])) { + rename_field($config['load_balancer']['lbprotocol'], 'desc', 'descr'); + } + if (is_array($config['load_balancer']['virtual_server'])) { + rename_field($config['load_balancer']['virtual_server'], 'desc', 'descr'); + } +} + +function upgrade_073_to_074() { + global $config; + rename_field($config['system']['user'], 'fullname', 'descr'); +} + +function upgrade_074_to_075() { + global $config; + if (is_array($config['ca'])) { + rename_field($config['ca'], 'name', 'descr'); + } + if (is_array($config['cert'])) { + rename_field($config['cert'], 'name', 'descr'); + } + if (is_array($config['crl'])) { + rename_field($config['crl'], 'name', 'descr'); + } +} + +function upgrade_075_to_076() { + global $config; + $cron_item = array(); + $cron_item['minute'] = "30"; + $cron_item['hour'] = "12"; + $cron_item['mday'] = "*"; + $cron_item['month'] = "*"; + $cron_item['wday'] = "*"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/usr/bin/nice -n20 /etc/rc.update_urltables"; + $config['cron']['item'][] = $cron_item; +} + +function upgrade_076_to_077() { + global $config; + foreach ($config['filter']['rule'] as & $rule) { + if (isset($rule['protocol']) && !empty($rule['protocol'])) { + $rule['protocol'] = strtolower($rule['protocol']); + } + } +} + +function upgrade_077_to_078() { + global $config; + if (is_array($config['pptpd']) && is_array($config['pptpd']['radius']) && + !is_array($config['pptpd']['radius']['server'])) { + $radarr = array(); + $radsvr = array(); + $radsvr['ip'] = $config['pptpd']['radius']['server']; + $radsvr['secret'] = $config['pptpd']['radius']['secret']; + $radsvr['port'] = 1812; + $radsvr['acctport'] = 1813; + $radsvr['enable'] = isset($config['pptpd']['radius']['enable']); + $radarr['accounting'] = isset($config['pptpd']['radius']['accounting']); + if ($radarr['accounting']) { + $radarr['acct_update'] = $radsvr['ip']; + } + $radarr['server'] = $radsvr; + $config['pptpd']['radius'] = $radarr; + } + if (is_array($config['pptpd'])) { + $config['pptpd']['n_pptp_units'] = empty($config['pptpd']['n_pptp_units']) ? 16 : $config['pptpd']['n_pptp_units']; + } +} +function upgrade_078_to_079() { + global $g; + /* Delete old and unused RRD file */ + unlink_if_exists("{$g['vardb_path']}/rrd/captiveportal-totalusers.rrd"); +} + +function upgrade_079_to_080() { + global $config; + + /* Upgrade config in 1.2.3 specifying a username other than admin for syncing. */ + if (!empty($config['system']['username']) && is_array($config['installedpackages']['carpsettings']) && + is_array($config['installedpackages']['carpsettings']['config'])) { + $config['installedpackages']['carpsettings']['config'][0]['username'] = $config['system']['username']; + unset($config['system']['username']); + } +} + +function upgrade_080_to_081() { + global $config; + global $g; + /* Welcome to the 2.1 migration path */ + + /* tag all the existing gateways as being IPv4 */ + $i = 0; + if (is_array($config['gateways']['gateway_item'])) { + foreach ($config['gateways']['gateway_item'] as $gw) { + $config['gateways']['gateway_item'][$i]['ipprotocol'] = "inet"; + $i++; + } + } + + /* RRD files changed for quality, traffic and packets graphs */ + /* convert traffic RRD file */ + global $parsedcfg, $listtags; + $listtags = array("ds", "v", "rra", "row"); + + $rrddbpath = "/var/db/rrd/"; + $rrdtool = "/usr/bin/nice -n20 /usr/local/bin/rrdtool"; + + if ($g['platform'] != "pfSense") { + /* restore the databases, if we have one */ + if (restore_rrd()) { + /* Make sure to move the rrd backup out of the way. We will make a new one after converting. */ + @rename("{$g['cf_conf_path']}/rrd.tgz", "{$g['cf_conf_path']}/backup/rrd.tgz"); + } + } + + $rrdinterval = 60; + $valid = $rrdinterval * 2; + + /* Asume GigE for now */ + $downstream = 125000000; + $upstream = 125000000; + + /* build a list of traffic and packets databases */ + $databases = return_dir_as_array($rrddbpath, '/-(traffic|packets)\.rrd$/'); + rsort($databases); + foreach ($databases as $database) { + $xmldump = "{$database}.old.xml"; + $xmldumpnew = "{$database}.new.xml"; + + if (platform_booting()) { + echo "Migrate RRD database {$database} to new format for IPv6.\n"; + } + + /* dump contents to xml and move database out of the way */ + dump_rrd_to_xml("{$rrddbpath}/{$database}", "{$g['tmp_path']}/{$xmldump}"); + + /* search and replace tags to add data sources */ + $ds_search = "<!-- Round Robin Archives -->"; + $ds_arr = array(); + $ds_arr[] = " <ds> + <name> inpass6 </name> + <type> COUNTER </type> + <minimal_heartbeat> {$valid} </minimal_heartbeat> + <min> 0.0000000000e+00 </min> + <max> 1.2500000000e+08 </max> + + <!-- PDP Status --> + <last_ds> 0 </last_ds> + <value> NaN </value> + <unknown_sec> 3 </unknown_sec> + </ds> + "; + $ds_arr[] = " <ds> + <name> outpass6 </name> + <type> COUNTER </type> + <minimal_heartbeat> {$valid} </minimal_heartbeat> + <min> 0.0000000000e+00 </min> + <max> 1.2500000000e+08 </max> + + <!-- PDP Status --> + <last_ds> 0 </last_ds> + <value> NaN </value> + <unknown_sec> 3 </unknown_sec> + </ds> + "; + $ds_arr[] = " <ds> + <name> inblock6 </name> + <type> COUNTER </type> + <minimal_heartbeat> {$valid} </minimal_heartbeat> + <min> 0.0000000000e+00 </min> + <max> 1.2500000000e+08 </max> + + <!-- PDP Status --> + <last_ds> 0 </last_ds> + <value> NaN </value> + <unknown_sec> 3 </unknown_sec> + </ds> + "; + $ds_arr[] = " <ds> + <name> outblock6 </name> + <type> COUNTER </type> + <minimal_heartbeat> {$valid} </minimal_heartbeat> + <min> 0.0000000000e+00 </min> + <max> 1.2500000000e+08 </max> + + <!-- PDP Status --> + <last_ds> 0 </last_ds> + <value> NaN </value> + <unknown_sec> 3 </unknown_sec> + </ds> + "; + + $cdp_search = "<\/cdp_prep>"; + $cdp_replace = "</cdp_prep>"; + $cdp_arr = array(); + $cdp_arr[] = " <ds> + <primary_value> NaN </primary_value> + <secondary_value> 0.0000000000e+00 </secondary_value> + <value> NaN </value> + <unknown_datapoints> 0 </unknown_datapoints> + </ds> + "; + $cdp_arr[] = " <ds> + <primary_value> NaN </primary_value> + <secondary_value> 0.0000000000e+00 </secondary_value> + <value> NaN </value> + <unknown_datapoints> 0 </unknown_datapoints> + </ds> + "; + $cdp_arr[] = " <ds> + <primary_value> NaN </primary_value> + <secondary_value> 0.0000000000e+00 </secondary_value> + <value> NaN </value> + <unknown_datapoints> 0 </unknown_datapoints> + </ds> + "; + $cdp_arr[] = " <ds> + <primary_value> NaN </primary_value> + <secondary_value> 0.0000000000e+00 </secondary_value> + <value> NaN </value> + <unknown_datapoints> 0 </unknown_datapoints> + </ds> + "; + + $value_search = "<\/row>"; + $value_replace = "</row>"; + $value = "<v> NaN </v>"; + + $xml = file_get_contents("{$g['tmp_path']}/{$xmldump}"); + foreach ($ds_arr as $ds) { + $xml = preg_replace("/$ds_search/s", "$ds{$ds_search}", $xml); + } + foreach ($cdp_arr as $cdp) { + $xml = preg_replace("/$cdp_search/s", "$cdp{$cdp_replace}", $xml); + } + foreach ($ds_arr as $ds) { + $xml = preg_replace("/$value_search/s", "$value{$value_replace}", $xml); + } + + file_put_contents("{$g['tmp_path']}/{$xmldumpnew}", $xml); + mwexec("$rrdtool restore -f {$g['tmp_path']}/{$xmldumpnew} {$rrddbpath}/{$database} 2>&1"); + unset($xml); + # Default /tmp tmpfs is ~40mb, do not leave temp files around + unlink_if_exists("{$g['tmp_path']}/{$xmldump}"); + unlink_if_exists("{$g['tmp_path']}/{$xmldumpnew}"); + } + if (!platform_booting()) { + enable_rrd_graphing(); + } + /* Let's save the RRD graphs after we run enable RRD graphing */ + /* The function will restore the rrd.tgz so we will save it after */ + exec("cd /; LANG=C NO_REMOUNT=1 RRDDBPATH='{$rrddbpath}' CF_CONF_PATH='{$g['cf_conf_path']}' /etc/rc.backup_rrd.sh"); + if (platform_booting()) { + echo "Updating configuration..."; + } + foreach ($config['filter']['rule'] as & $rule) { + if (isset($rule['protocol']) && !empty($rule['protocol'])) { + $rule['protocol'] = strtolower($rule['protocol']); + } + } + unset($rule); +} + +function upgrade_081_to_082() { + /* don't enable the allow IPv6 toggle */ +} + +function upgrade_082_to_083() { + global $config; + + /* Upgrade captiveportal config */ + if (!empty($config['captiveportal'])) { + $tmpcp = $config['captiveportal']; + $config['captiveportal'] = array(); + $config['captiveportal']['cpzone'] = array(); + $config['captiveportal']['cpzone'] = $tmpcp; + $config['captiveportal']['cpzone']['zoneid'] = 8000; + $config['captiveportal']['cpzone']['zone'] = "cpzone"; + if ($config['captiveportal']['cpzone']['auth_method'] == "radius") { + $config['captiveportal']['cpzone']['radius_protocol'] = "PAP"; + } + } + if (!empty($config['voucher'])) { + $tmpcp = $config['voucher']; + $config['voucher'] = array(); + $config['voucher']['cpzone'] = array(); + $config['voucher']['cpzone'] = $tmpcp; + } +} + +function upgrade_083_to_084() { + global $config; + if (!isset($config['hasync'])) { + if (!empty($config['installedpackages']) && + !empty($config['installedpackages']['carpsettings']) && + !empty($config['installedpackages']['carpsettings']['config'])) { + $config['hasync'] = $config['installedpackages']['carpsettings']['config'][0]; + unset($config['installedpackages']['carpsettings']); + } + if (empty($config['installedpackages']['carpsettings'])) { + unset($config['installedpackages']['carpsettings']); + } + if (empty($config['installedpackages'])) { + unset($config['installedpackages']); + } + } +} + +function upgrade_084_to_085() { + global $config; + + $gateway_group_arr = array(); + $gateways = return_gateways_array(); + $oldnames = array(); + /* setup translation array */ + foreach ($gateways as $name => $gw) { + if (isset($gw['dynamic'])) { + $oldname = strtoupper($config['interfaces'][$gw['friendlyiface']]['descr']); + $oldnames[$oldname] = $name; + } else { + $oldnames[$name] = $name; + } + } + + /* process the old array */ + if (is_array($config['gateways']['gateway_group'])) { + $group_array_new = array(); + foreach ($config['gateways']['gateway_group'] as $name => $group) { + if (is_array($group['item'])) { + $newlist = array(); + foreach ($group['item'] as $entry) { + $elements = explode("|", $entry); + if ($oldnames[$elements[0]] <> "") { + $newlist[] = "{$oldnames[$elements[0]]}|{$elements[1]}"; + } else { + $newlist[] = "{$elements[0]}|{$elements[1]}"; + } + } + $group['item'] = $newlist; + $group_array_new[$name] = $group; + } + } + $config['gateways']['gateway_group'] = $group_array_new; + } + /* rename old Quality RRD files in the process */ + $rrddbpath = "/var/db/rrd"; + foreach ($oldnames as $old => $new) { + if (is_readable("{$rrddbpath}/{$old}-quality.rrd")) { + @rename("{$rrddbpath}/{$old}-quality.rrd", "{$rrddbpath}/{$new}-quality.rrd"); + } + } + unset($gateways, $oldnames, $gateway_group_arr); +} + +function upgrade_085_to_086() { + global $config, $g; + + /* XXX: Gross hacks in sight */ + if (is_array($config['virtualip']['vip'])) { + $vipchg = array(); + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['mode'] != "carp") { + continue; + } + $config = array_replace_values_recursive( + $config, + '^vip' . $vip['vhid'] . '$', + "{$vip['interface']}_vip{$vip['vhid']}" + ); + } + } +} + +function upgrade_086_to_087() { + global $config, $dummynet_pipe_list; + + if (!is_array($config['dnshaper']) || !is_array($config['dnshaper']['queue'])) { + return; + } + + $dnqueue_number = 1; + $dnpipe_number = 1; + + foreach ($config['dnshaper']['queue'] as $idx => $dnpipe) { + $config['dnshaper']['queue'][$idx]['number'] = $dnpipe_number; + $dnpipe_number++; + if (is_array($dnpipe['queue'])) { + foreach ($dnpipe['queue'] as $qidx => $dnqueue) { + $config['dnshaper']['queue'][$idx]['queue'][$qidx]['number'] = $dnqueue_number; + $dnqueue_number++; + } + } + } + + unset($dnqueue_number, $dnpipe_number, $qidx, $idx, $dnpipe, $dnqueue); + + if (!is_array($config['filter']) || !is_array($config['filter']['rule'])) { + return; + } + + require_once("shaper.inc"); + read_dummynet_config(); + + $dn_list = array(); + if (is_array($dummynet_pipe_list)) { + foreach ($dummynet_pipe_list as $dn) { + $tmplist =& $dn->get_queue_list(); + foreach ($tmplist as $qname => $link) { + $dn_list[$link] = $qname; + } + } + unset($dummynet_pipe_list); + } + + foreach ($config['filter']['rule'] as $idx => $rule) { + if (!empty($rule['dnpipe'])) { + if (!empty($dn_list[$rule['dnpipe']])) { + $config['filter']['rule'][$idx]['dnpipe'] = $dn_list[$rule['dnpipe']]; + } + } + if (!empty($rule['pdnpipe'])) { + if (!empty($dn_list[$rule['pdnpipe']])) { + $config['filter']['rule'][$idx]['pdnpipe'] = $dn_list[$rule['pdnpipe']]; + } + } + } +} +function upgrade_087_to_088() { + global $config; + if (isset($config['system']['glxsb_enable'])) { + unset($config['system']['glxsb_enable']); + $config['system']['crypto_hardware'] = "glxsb"; + } +} + +function upgrade_088_to_089() { + global $config; + if (!is_array($config['ca'])) { + $config['ca'] = array(); + } + if (!is_array($config['cert'])) { + $config['cert'] = array(); + } + + /* migrate captive portal ssl to certificate manager */ + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $id => &$setting) { + if (isset($setting['httpslogin'])) { + /* create cert entry */ + $cert = array(); + $cert['refid'] = uniqid(); + $cert['descr'] = "Captive Portal Cert - {$setting['zone']}"; + $cert['crt'] = $setting['certificate']; + $cert['prv'] = $setting['private-key']; + + if (!empty($setting['cacertificate'])) { + /* create ca entry */ + $ca = array(); + $ca['refid'] = uniqid(); + $ca['descr'] = "Captive Portal CA - {$setting['zone']}"; + $ca['crt'] = $setting['cacertificate']; + $config['ca'][] = $ca; + + /* add ca reference to certificate */ + $cert['caref'] = $ca['refid']; + } + + $config['cert'][] = $cert; + + /* create cert reference */ + $setting['certref'] = $cert['refid']; + + unset($setting['certificate']); + unset($setting['private-key']); + unset($setting['cacertificate']); + + } + } + } +} + +function upgrade_089_to_090() { + global $config; + if (is_array($config['load_balancer']['virtual_server']) && count($config['load_balancer']['virtual_server'])) { + $vs_a = &$config['load_balancer']['virtual_server']; + for ($i = 0; isset($vs_a[$i]); $i++) { + if (is_array($vs_a[$i]['pool'])) { + $vs_a[$i]['poolname'] = $vs_a[$i]['pool'][0]; + unset($vs_a[$i]['pool']); + } elseif (!empty($vs_a[$i]['pool'])) { + $vs_a[$i]['poolname'] = $vs_a[$i]['pool']; + unset($vs_a[$i]['pool']); + } + } + } +} + +function upgrade_090_to_091() { + global $config; + + if (is_array($config['dnshaper']) && is_array($config['dnshaper']['queue'])) { + foreach ($config['dnshaper']['queue'] as $idx => $dnqueue) { + if (!empty($dnqueue['bandwidth'])) { + $bw = array(); + $bw['bw'] = $dnqueue['bandwidth']; + $bw['bwscale'] = $dnqueue['bandwidthtype']; + $bw['bwsched'] = "none"; + $config['dnshaper']['queue'][$idx]['bandwidth'] = array(); + $config['dnshaper']['queue'][$idx]['bandwidth']['item'] = array(); + $config['dnshaper']['queue'][$idx]['bandwidth']['item'][] = $bw; + } + } + } +} + +function upgrade_091_to_092() { + global $config; + + if (is_array($config['nat']['advancedoutbound']) && is_array($config['nat']['advancedoutbound']['rule'])) { + $nat_rules = &$config['nat']['advancedoutbound']['rule']; + for ($i = 0; isset($nat_rules[$i]); $i++) { + if (empty($nat_rules[$i]['interface'])) { + $nat_rules[$i]['interface'] = 'wan'; + } + } + } +} + +function upgrade_092_to_093() { + global $g; + + $suffixes = array("concurrent", "loggedin"); + + foreach ($suffixes as $suffix) { + if (file_exists("{$g['vardb_path']}/rrd/captiveportal-{$suffix}.rrd")) { + rename("{$g['vardb_path']}/rrd/captiveportal-{$suffix}.rrd", + "{$g['vardb_path']}/rrd/captiveportal-cpZone-{$suffix}.rrd"); + } + } + + if (!platform_booting()) { + enable_rrd_graphing(); + } +} + +function upgrade_093_to_094() { + global $config; + + if (isset($config['system']['powerd_mode'])) { + $config['system']['powerd_ac_mode'] = $config['system']['powerd_mode']; + $config['system']['powerd_battery_mode'] = $config['system']['powerd_mode']; + unset($config['system']['powerd_mode']); + } +} + +function upgrade_094_to_095() { + global $config; + + if (!isset($config['interfaces']) || !is_array($config['interfaces'])) { + return; + } + + foreach ($config['interfaces'] as $iface => $cfg) { + if (isset($cfg['ipaddrv6']) && ($cfg['ipaddrv6'] == "track6")) { + if (!isset($cfg['track6-prefix-id']) || ($cfg['track6-prefix-id'] == "")) { + $config['interfaces'][$iface]['track6-prefix-id'] = 0; + } + } + } +} + +function upgrade_095_to_096() { + global $config, $g; + + $names = array("inpass", "outpass", "inblock", "outblock", + "inpass6", "outpass6", "inblock6", "outblock6"); + $rrddbpath = "/var/db/rrd"; + $rrdtool = "/usr/local/bin/rrdtool"; + + if ($g['platform'] != "pfSense") { + /* restore the databases, if we have one */ + if (restore_rrd()) { + /* Make sure to move the rrd backup out of the way. We will make a new one after converting. */ + @rename("{$g['cf_conf_path']}/rrd.tgz", "{$g['cf_conf_path']}/backup/rrd.tgz"); + } + } + + /* Assume 2*10GigE for now */ + $stream = 2500000000; + + /* build a list of traffic and packets databases */ + $databases = return_dir_as_array($rrddbpath, '/-(traffic|packets)\.rrd$/'); + rsort($databases); + foreach ($databases as $database) { + if (platform_booting()) { + echo "Update RRD database {$database}.\n"; + } + + $cmd = "{$rrdtool} tune {$rrddbpath}/{$database}"; + foreach ($names as $name) { + $cmd .= " -a {$name}:{$stream}"; + } + mwexec("{$cmd} 2>&1"); + + } + if (!platform_booting()) { + enable_rrd_graphing(); + } + /* Let's save the RRD graphs after we run enable RRD graphing */ + /* The function will restore the rrd.tgz so we will save it after */ + exec("cd /; LANG=C NO_REMOUNT=1 RRDDBPATH='{$rrddbpath}' CF_CONF_PATH='{$g['cf_conf_path']}' /etc/rc.backup_rrd.sh"); +} + +function upgrade_096_to_097() { + global $config, $g; + /* If the user had disabled default block rule logging before, then bogon/private network logging was already off, so respect their choice. */ + if (isset($config['syslog']['nologdefaultblock'])) { + $config['syslog']['nologbogons'] = true; + $config['syslog']['nologprivatenets'] = true; + } +} + +function upgrade_097_to_098() { + global $config, $g; + /* Disable kill_states by default */ + $config['system']['kill_states'] = true; +} + +function upgrade_098_to_099() { + global $config; + + if (empty($config['dhcpd']) || !is_array($config['dhcpd'])) { + return; + } + + foreach ($config['dhcpd'] as & $dhcpifconf) { + if (isset($dhcpifconf['next-server'])) { + $dhcpifconf['nextserver'] = $dhcpifconf['next-server']; + unset($dhcpifconf['next-server']); + } + } +} + +function upgrade_099_to_100() { + require_once("/etc/inc/services.inc"); + install_cron_job("/usr/bin/nice -n20 newsyslog", false); +} + +function upgrade_100_to_101() { + global $config, $g; + + if (!is_array($config['voucher'])) { + return; + } + + foreach ($config['voucher'] as $cpzone => $cp) { + if (!is_array($cp['roll'])) { + continue; + } + foreach ($cp['roll'] as $ridx => $rcfg) { + if (!empty($rcfg['comment'])) { + $config['voucher'][$cpzone]['roll'][$ridx]['descr'] = $rcfg['comment']; + } + } + } +} + +function upgrade_101_to_102() { + global $config, $g; + + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cpzone => $cp) { + if (!is_array($cp['passthrumac'])) { + continue; + } + + foreach ($cp['passthrumac'] as $idx => $passthrumac) { + $config['captiveportal'][$cpzone]['passthrumac'][$idx]['action'] = 'pass'; + } + } + } + + /* Convert OpenVPN Compression option to the new style */ + // Nothing to do if there is no OpenVPN tag + if (isset($config['openvpn']) && is_array($config['openvpn'])) { + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as &$vpn) { + if (!empty($vpn['compression'])) { + $vpn['compression'] = "adaptive"; + } + } + } + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as &$vpn) { + if (!empty($vpn['compression'])) { + $vpn['compression'] = "adaptive"; + } + } + } + } +} + +function upgrade_102_to_103() { + global $config; + + if (isset($config['nat']['advancedoutbound']['enable'])) { + $config['nat']['advancedoutbound']['mode'] = "advanced"; + unset($config['nat']['advancedoutbound']['enable']); + } else { + $config['nat']['advancedoutbound']['mode'] = "automatic"; + } + + $config['nat']['outbound'] = $config['nat']['advancedoutbound']; + + unset($config['nat']['ipsecpassthru']); + unset($config['nat']['advancedoutbound']); +} + +function upgrade_103_to_104() { + global $config; + + $changed_privs = array( + "page-diag-system-activity" => "page-diagnostics-system-activity", + "page-interfacess-groups" => "page-interfaces-groups", + "page-interfacess-lagg" => "page-interfaces-lagg", + "page-interfacess-qinq" => "page-interfaces-qinq" + ); + + /* update user privileges */ + foreach ($config['system']['user'] as & $user) { + if (!is_array($user['priv'])) { + continue; + } + foreach ($user['priv'] as & $priv) { + if (array_key_exists($priv, $changed_privs)) { + $priv = $changed_privs[$priv]; + } + } + } + + /* update group privileges */ + foreach ($config['system']['group'] as & $group) { + if (!is_array($group['priv'])) { + continue; + } + foreach ($group['priv'] as & $priv) { + if (array_key_exists($priv, $changed_privs)) { + $priv = $changed_privs[$priv]; + } + } + } + + /* sync all local account information */ + local_sync_accounts(); +} + +function upgrade_104_to_105() { + global $config; + + if (is_array($config['captiveportal'])) { + $zoneid = 2; + foreach ($config['captiveportal'] as $cpzone => $cpcfg) { + if (empty($cpcfg['zoneid'])) { + $config['captiveportal'][$cpzone]['zoneid'] = $zoneid; + $zoneid += 2; + } else if ($cpcfg['zoneid'] > 4000) { + $config['captiveportal'][$cpzone]['zoneid'] = $zoneid; + $zoneid += 2; + } + } + } +} + +function upgrade_105_to_106() { + + /* NOTE: This entry can be reused for something else since the upgrade code was reverted */ +} + +function upgrade_106_to_107() { + global $config; + + if (is_array($config['filter']) && is_array($config['filter']['rule'])) { + $tracker = (int)microtime(true); + foreach ($config['filter']['rule'] as $ridx => $rule) { + if (empty($rule['tracker'])) { + $config['filter']['rule'][$ridx]['tracker'] = $tracker; + $tracker++; + } + } + unset($tracker, $ridx); + } + if (is_array($config['nat']) && is_array($config['nat']['rule'])) { + $tracker = (int)microtime(true); + foreach ($config['nat']['rule'] as $ridx => $rule) { + if (empty($rule['tracker'])) { + $config['nat']['rule'][$ridx]['tracker'] = $tracker; + $tracker++; + } + } + unset($tracker, $ridx); + } +} + +function upgrade_107_to_108() { + global $config; + + if (isset($config['system']['webgui']['noautocomplete'])) { + unset($config['system']['webgui']['noautocomplete']); + } else { + $config['system']['webgui']['loginautocomplete'] = true; + } +} + +function upgrade_108_to_109() { + global $config; + + if (!isset($config['filter']['rule']) || !is_array($config['filter']['rule'])) { + return; + } + + foreach ($config['filter']['rule'] as &$rule) { + if (!isset($rule['dscp']) || empty($rule['dscp'])) { + continue; + } + + $pos = strpos($rule['dscp'], ' '); + if ($pos !== false) { + $rule['dscp'] = substr($rule['dscp'], 0, $pos); + } + unset($pos); + } +} + +function upgrade_109_to_110() { + global $config; + + if (!is_array($config['ipsec']) || !is_array($config['ipsec']['phase2'])) { + return; + } + + foreach ($config['ipsec']['phase2'] as &$rule) { + if (!empty($rule['uniqid'])) { + continue; + } + + $rule['uniqid'] = uniqid(); + } +} + +function upgrade_110_to_111() { + global $config; + + /* Make sure unbound user exist */ + mwexec('/usr/sbin/pw groupadd -n unbound -g 59', true); + mwexec('/usr/sbin/pw useradd -n unbound -c "Unbound DNS Resolver" -d /var/unbound -s /usr/sbin/nologin -u 59 -g 59', true); + + /* cleanup old unbound package stuffs */ + unlink_if_exists("/usr/local/pkg/unbound.xml"); + unlink_if_exists("/usr/local/pkg/unbound.inc"); + unlink_if_exists("/usr/local/pkg/unbound_advanced.xml"); + unlink_if_exists("/usr/local/www/unbound_status.php"); + unlink_if_exists("/usr/local/www/unbound_acls.php"); + unlink_if_exists("/usr/local/bin/unbound_monitor.sh"); + unlink_if_exists("/usr/local/etc/rc.d/unbound.sh"); + + /* Remove old menu and service entries */ + if (isset($config['installedpackages']['menu']) && is_array($config['installedpackages']['menu'])) { + foreach ($config['installedpackages']['menu'] as $idx => $menu) { + if ($menu['name'] != 'Unbound DNS') { + continue; + } + + unset($config['installedpackages']['menu'][$idx]); + break; + } + } + + if (isset($config['installedpackages']['service']) && is_array($config['installedpackages']['service'])) { + foreach ($config['installedpackages']['service'] as $idx => $service) { + if ($service['name'] != 'unbound') { + continue; + } + unset($config['installedpackages']['service'][$idx]); + break; + } + } + + if (!isset($config['installedpackages']['unbound']['config'][0])) { + return; + } + + $pkg = $config['installedpackages']['unbound']['config'][0]; + + if (isset($config['installedpackages']['unboundadvanced']['config'][0])) { + $pkg = array_merge($pkg, $config['installedpackages']['unboundadvanced']['config'][0]); + } + + $new = array(); + + /* deal first with boolean fields */ + $fields = array( + "enable" => "enable", + "dnssec_status" => "dnssec", + "forwarding_mode" => "forwarding", + "regdhcp" => "regdhcp", + "regdhcpstatic" => "regdhcpstatic", + "txtsupport" => "txtsupport", + "hide_id" => "hideidentity", + "hide_version" => "hideversion", + "prefetch" => "prefetch", + "prefetch_key" => "prefetchkey", + "harden_glue" => "hardenglue", + "harden_dnssec_stripped" => "dnssec_stripped"); + + foreach ($fields as $oldk => $newk) { + if (isset($pkg[$oldk])) { + if ($pkg[$oldk] == 'on') { + $new[$newk] = true; + } + unset($pkg[$oldk]); + } + } + + $fields = array( + "active_interface" => "network_interface", + "query_interface" => "outgoing_interface", + "unbound_verbosity" => "log_verbosity", + "msg_cache_size" => "msgcachesize", + "outgoing_num_tcp" => "outgoing_num_tcp", + "incoming_num_tcp" => "incoming_num_tcp", + "edns_buffer_size" => "edns_buffer_size", + "num_queries_per_thread" => "num_queries_per_thread", + "jostle_timeout" => "jostle_timeout", + "cache_max_ttl" => "cache_max_ttl", + "cache_min_ttl" => "cache_min_ttl", + "infra_host_ttl" => "infra_host_ttl", + "infra_cache_numhosts" => "infra_cache_numhosts", + "unwanted_reply_threshold" => "unwanted_reply_threshold", + "custom_options" => "custom_options"); + + foreach ($fields as $oldk => $newk) { + if (isset($pkg[$oldk])) { + $new[$newk] = $pkg[$oldk]; + unset($pkg[$oldk]); + } + } + + if (isset($new['custom_options']) && !empty($new['custom_options'])) { + $new['custom_options'] = str_replace("\r\n", "\n", $new['custom_options']); + } + + /* Following options were removed, bring them as custom_options */ + if (isset($pkg['stats']) && $pkg['stats'] == "on") { + if (isset($pkg['stats_interval'])) { + $new['custom_options'] .= (empty($new['custom_options']) ? "" : "\n") . "statistics-interval: {$pkg['stats_interval']}"; + } + if (isset($pkg['cumulative_stats'])) { + $new['custom_options'] .= (empty($new['custom_options']) ? "" : "\n") . "statistics-cumulative: {$pkg['cumulative_stats']}"; + } + if (isset($pkg['extended_stats']) && $pkg['extended_stats'] == "on") { + $new['custom_options'] .= (empty($new['custom_options']) ? "" : "\n") . "extended-statistics: yes"; + } else { + $new['custom_options'] .= (empty($new['custom_options']) ? "" : "\n") . "extended-statistics: no"; + } + } + + $new['acls'] = array(); + if (isset($config['installedpackages']['unboundacls']['config']) && + is_array($config['installedpackages']['unboundacls']['config'])) { + foreach ($config['installedpackages']['unboundacls']['config'] as $acl) { + $new['acls'][] = $acl; + } + } + + $config['unbound'] = $new; + + if (isset($config['installedpackages']['unbound'])) { + unset($config['installedpackages']['unbound']); + } + if (isset($config['installedpackages']['unboundadvanced'])) { + unset($config['installedpackages']['unboundadvanced']); + } + if (isset($config['installedpackages']['unboundacls'])) { + unset($config['installedpackages']['unboundacls']); + } + + unset($pkg, $new); +} + +function upgrade_111_to_112() { + global $config; + + $config['cron']['item'][] = array( + 'minute' => '*/60', + 'hour' => '*', + 'mday' => '*', + 'month' => '*', + 'wday' => '*', + 'who' => 'root', + 'command' => '/usr/bin/nice -n20 /usr/local/sbin/expiretable -v -t 3600 webConfiguratorlockout' + ); +} + +function upgrade_112_to_113() { + global $config; + + if (isset($config['notifications']['smtp']['ssl']) && + $config['notifications']['smtp']['ssl'] == "checked") { + $config['notifications']['smtp']['ssl'] = true; + } else { + unset($config['notifications']['smtp']['ssl']); + } + + if (isset($config['notifications']['smtp']['tls']) && + $config['notifications']['smtp']['tls'] == "checked") { + $config['notifications']['smtp']['tls'] = true; + } else { + unset($config['notifications']['smtp']['tls']); + } +} + +function upgrade_113_to_114() { + global $config; + + if (!isset($config['ipsec']['phase1']) || + !is_array($config['ipsec']['phase1'])) { + return; + } + + foreach ($config['ipsec']['phase1'] as &$ph1ent) { + if (!isset($ph1ent['iketype'])) { + $ph1ent['iketype'] = 'ikev1'; + } + } +} + +function upgrade_114_to_115() { + global $config; + + if (isset($config['unbound']['custom_options'])) { + $config['unbound']['custom_options'] = base64_encode($config['unbound']['custom_options']); + } +} + +function upgrade_115_to_116() { + global $config; + + if (!is_array($config['ipsec']) || !is_array($config['ipsec']['phase2'])) { + return; + } + + $keyid = 1; + foreach ($config['ipsec']['phase2'] as $idx => $ph2) { + $config['ipsec']['phase2'][$idx]['reqid'] = $keyid; + $keyid++; + } +} + +function upgrade_116_to_117() { + global $config; + + if (!isset($config['ipsec']['client']) || + !isset($config['ipsec']['client']['dns_split']) || + empty($config['ipsec']['client']['dns_split'])) { + return; + } + + $config['ipsec']['client']['dns_split'] = + preg_replace('/\s*,\s*/', ' ', trim($config['ipsec']['client']['dns_split'])); + +} + +function upgrade_117_to_118() { + global $config; + + // Unset any old CA and Cert in the system section that might still be there from when upgrade_066_to_067 did not unset them. + if (isset($config['system']['ca'])) { + unset($config['system']['ca']); + } + if (isset($config['system']['cert'])) { + unset($config['system']['cert']); + } + + if (!isset($config['ipsec']['phase1'])) { + return; + } + + $a_phase1 =& $config['ipsec']['phase1']; + + foreach ($a_phase1 as &$ph1_entry) { + // update asn1dn strings from racoon's format to strongswan's + if (isset($ph1_entry['myid_type']) && $ph1_entry['myid_type'] == 'asn1dn') { + $ph1_entry['myid_data'] = + preg_replace('/\/\s*emailAddress\s*=\s*/', ', E=', $ph1_entry['myid_data']); + } + if (isset($ph1_entry['peerid_type']) && $ph1_entry['peerid_type'] == 'asn1dn') { + $ph1_entry['peerid_data'] = + preg_replace('/\/\s*emailAddress\s*=\s*/', ', E=', $ph1_entry['peerid_data']); + } + // iketype 'auto' was removed and is really v2, update accordingly + if ($ph1_entry['iketype'] == "auto") { + $ph1_entry['iketype'] = "ikev2"; + } + } +} + +function upgrade_118_to_119() { + global $config; + + if (!isset($config['ipsec']['phase1'])) { + return; + } + + // change peerid_type to 'any' for EAP types to retain previous behavior of omitting rightid + $a_phase1 =& $config['ipsec']['phase1']; + + foreach ($a_phase1 as &$ph1_entry) { + if (strstr($ph1_entry['authentication_method'], 'eap')) { + $ph1_entry['peerid_type'] = "any"; + } + } +} + +function upgrade_119_to_120() { + global $config; + + if (!isset($config['installedpackages']['miniupnpd']['config'][0])) { + return; + } + + $miniupnpd =& $config['installedpackages']['miniupnpd']['config'][0]; + + $miniupnpd['row'] = array(); + + for ($i = 1; $i <= 4; $i++) { + if (isset($miniupnpd["permuser{$i}"]) && !empty($miniupnpd["permuser{$i}"])) { + $miniupnpd['row'][] = array('permuser' => $miniupnpd["permuser{$i}"]); + } + unset($miniupnpd["permuser{$i}"]); + } +} + +function upgrade_120_to_121() { + global $config; + foreach ($config['system']['user'] as &$user) { + if (isset($user['nt-hash'])) { + unset($user['nt-hash']); + } + } +} + +?> diff --git a/src/etc/inc/util.inc b/src/etc/inc/util.inc new file mode 100644 index 0000000..b2c797b --- /dev/null +++ b/src/etc/inc/util.inc @@ -0,0 +1,2259 @@ +<?php +/* + util.inc + part of the pfSense project (https://www.pfsense.org) + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_BUILDER_BINARIES: /bin/ps /bin/kill /usr/bin/killall /sbin/ifconfig /usr/bin/netstat + pfSense_BUILDER_BINARIES: /usr/bin/awk /sbin/dmesg /sbin/ping /usr/local/sbin/gzsig /usr/sbin/arp + pfSense_BUILDER_BINARIES: /sbin/conscontrol /sbin/devd /bin/ps + pfSense_MODULE: utils +*/ + +/* kill a process by pid file */ +function killbypid($pidfile) { + return sigkillbypid($pidfile, "TERM"); +} + +function isvalidpid($pidfile) { + $output = ""; + if (file_exists($pidfile)) { + exec("/bin/pgrep -nF {$pidfile}", $output, $retval); + return (intval($retval) == 0); + } + return false; +} + +function is_process_running($process) { + $output = ""; + exec("/bin/pgrep -anx " . escapeshellarg($process), $output, $retval); + + return (intval($retval) == 0); +} + +function isvalidproc($proc) { + return is_process_running($proc); +} + +/* sigkill a process by pid file */ +/* return 1 for success and 0 for a failure */ +function sigkillbypid($pidfile, $sig) { + if (file_exists($pidfile)) { + return mwexec("/bin/pkill " . escapeshellarg("-{$sig}") . " -F {$pidfile}", true); + } + + return 0; +} + +/* kill a process by name */ +function sigkillbyname($procname, $sig) { + if (isvalidproc($procname)) { + return mwexec("/usr/bin/killall " . escapeshellarg("-{$sig}") . " " . escapeshellarg($procname), true); + } +} + +/* kill a process by name */ +function killbyname($procname) { + if (isvalidproc($procname)) { + mwexec("/usr/bin/killall " . escapeshellarg($procname)); + } +} + +function is_subsystem_dirty($subsystem = "") { + global $g; + + if ($subsystem == "") { + return false; + } + + if (file_exists("{$g['varrun_path']}/{$subsystem}.dirty")) { + return true; + } + + return false; +} + +function mark_subsystem_dirty($subsystem = "") { + global $g; + + if (!file_put_contents("{$g['varrun_path']}/{$subsystem}.dirty", "DIRTY")) { + log_error(sprintf(gettext("WARNING: Could not mark subsystem: %s dirty"), $subsystem)); + } +} + +function clear_subsystem_dirty($subsystem = "") { + global $g; + + @unlink("{$g['varrun_path']}/{$subsystem}.dirty"); +} + +function config_lock() { + return; +} +function config_unlock() { + return; +} + +/* lock configuration file */ +function lock($lock, $op = LOCK_SH) { + global $g, $cfglckkeyconsumers; + if (!$lock) { + die(gettext("WARNING: You must give a name as parameter to lock() function.")); + } + if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) { + @touch("{$g['tmp_path']}/{$lock}.lock"); + @chmod("{$g['tmp_path']}/{$lock}.lock", 0666); + } + $cfglckkeyconsumers++; + if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) { + if (flock($fp, $op)) { + return $fp; + } else { + fclose($fp); + } + } +} + +function try_lock($lock, $timeout = 5) { + global $g, $cfglckkeyconsumers; + if (!$lock) { + die(gettext("WARNING: You must give a name as parameter to try_lock() function.")); + } + if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) { + @touch("{$g['tmp_path']}/{$lock}.lock"); + @chmod("{$g['tmp_path']}/{$lock}.lock", 0666); + } + $cfglckkeyconsumers++; + if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) { + $trycounter = 0; + while (!flock($fp, LOCK_EX | LOCK_NB)) { + if ($trycounter >= $timeout) { + fclose($fp); + return NULL; + } + sleep(1); + $trycounter++; + } + + return $fp; + } + + return NULL; +} + +/* unlock configuration file */ +function unlock($cfglckkey = 0) { + global $g, $cfglckkeyconsumers; + flock($cfglckkey, LOCK_UN); + fclose($cfglckkey); + return; +} + +/* unlock forcefully configuration file */ +function unlock_force($lock) { + global $g; + + @unlink("{$g['tmp_path']}/{$lock}.lock"); +} + +function send_event($cmd) { + global $g; + + if (!isset($g['event_address'])) { + $g['event_address'] = "unix:///var/run/check_reload_status"; + } + + $try = 0; + while ($try < 3) { + $fd = @fsockopen($g['event_address']); + if ($fd) { + fwrite($fd, $cmd); + $resp = fread($fd, 4096); + if ($resp != "OK\n") { + log_error("send_event: sent {$cmd} got {$resp}"); + } + fclose($fd); + $try = 3; + } else if (!is_process_running("check_reload_status")) { + mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status"); + } + $try++; + } +} + +function send_multiple_events($cmds) { + global $g; + + if (!isset($g['event_address'])) { + $g['event_address'] = "unix:///var/run/check_reload_status"; + } + + if (!is_array($cmds)) { + return; + } + + while ($try < 3) { + $fd = @fsockopen($g['event_address']); + if ($fd) { + foreach ($cmds as $cmd) { + fwrite($fd, $cmd); + $resp = fread($fd, 4096); + if ($resp != "OK\n") { + log_error("send_event: sent {$cmd} got {$resp}"); + } + } + fclose($fd); + $try = 3; + } else if (!is_process_running("check_reload_status")) { + mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status"); + } + $try++; + } +} + +function refcount_init($reference) { + $shmid = @shmop_open($reference, "c", 0644, 10); + @shmop_write($shmid, str_pad("0", 10, "\x0", STR_PAD_RIGHT), 0); + @shmop_close($shmid); +} + +function refcount_reference($reference) { + /* Take out a lock across the shared memory read, increment, write sequence to make it atomic. */ + $shm_lck = lock("shm{$reference}", LOCK_EX); + try { + /* NOTE: A warning is generated when shared memory does not exist */ + $shmid = @shmop_open($reference, "w", 0, 0); + if (!$shmid) { + refcount_init($reference); + $shmid = @shmop_open($reference, "w", 0, 0); + if (!$shmid) { + log_error(gettext("Could not open shared memory {$reference}")); + unlock($shm_lck); + return; + } + } + $shm_data = @shmop_read($shmid, 0, 10); + $shm_data = intval($shm_data) + 1; + @shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0); + @shmop_close($shmid); + unlock($shm_lck); + } catch (Exception $e) { + log_error($e->getMessage()); + unlock($shm_lck); + } + + return $shm_data; +} + +function refcount_unreference($reference) { + /* Take out a lock across the shared memory read, decrement, write sequence to make it atomic. */ + $shm_lck = lock("shm{$reference}", LOCK_EX); + try { + $shmid = @shmop_open($reference, "w", 0, 0); + if (!$shmid) { + refcount_init($reference); + log_error(gettext("Could not open shared memory {$reference}")); + unlock($shm_lck); + return; + } + $shm_data = @shmop_read($shmid, 0, 10); + $shm_data = intval($shm_data) - 1; + if ($shm_data < 0) { + //debug_backtrace(); + log_error(sprintf(gettext("Reference %s is going negative, not doing unreference."), $reference)); + } else { + @shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0); + } + @shmop_close($shmid); + unlock($shm_lck); + } catch (Exception $e) { + log_error($e->getMessage()); + unlock($shm_lck); + } + + return $shm_data; +} + +function refcount_read($reference) { + /* This function just reads the current value of the refcount for information. */ + /* There is no need for locking. */ + $shmid = @shmop_open($reference, "a", 0, 0); + if (!$shmid) { + log_error(gettext("Could not open shared memory for read {$reference}")); + return -1; + } + $shm_data = @shmop_read($shmid, 0, 10); + @shmop_close($shmid); + return $shm_data; +} + +function is_module_loaded($module_name) { + $module_name = str_replace(".ko", "", $module_name); + $running = 0; + $_gb = exec("/sbin/kldstat -qn {$module_name} 2>&1", $_gb, $running); + if (intval($running) == 0) { + return true; + } else { + return false; + } +} + +/* validate non-negative numeric string, or equivalent numeric variable */ +function is_numericint($arg) { + return (((is_int($arg) && $arg >= 0) || (is_string($arg) && strlen($arg) > 0 && ctype_digit($arg))) ? true : false); +} + +/* Generate the (human readable) ipv4 or ipv6 subnet address (i.e., netmask, or subnet start IP) + given an (human readable) ipv4 or ipv6 host address and subnet bit count */ +function gen_subnet($ipaddr, $bits) { + if (($sn = gen_subnetv6($ipaddr, $bits)) == '') { + $sn = gen_subnetv4($ipaddr, $bits); // try to avoid rechecking IPv4/v6 + } + return $sn; +} + +/* same as gen_subnet() but accepts IPv4 only */ +function gen_subnetv4($ipaddr, $bits) { + if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) { + if ($bits == 0) { + return '0.0.0.0'; // avoids <<32 + } + return long2ip(ip2long($ipaddr) & ((0xFFFFFFFF << (32 - $bits)) & 0xFFFFFFFF)); + } + return ""; +} + +/* same as gen_subnet() but accepts IPv6 only */ +function gen_subnetv6($ipaddr, $bits) { + if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) { + return Net_IPv6::compress(Net_IPv6::getNetmask($ipaddr, $bits)); + } + return ""; +} + +/* Generate the (human readable) ipv4 or ipv6 subnet end address (i.e., highest address, end IP, or IPv4 broadcast address) + given an (human readable) ipv4 or ipv6 host address and subnet bit count. */ +function gen_subnet_max($ipaddr, $bits) { + if (($sn = gen_subnetv6_max($ipaddr, $bits)) == '') { + $sn = gen_subnetv4_max($ipaddr, $bits); // try to avoid rechecking IPv4/v6 + } + return $sn; +} + +/* same as gen_subnet_max() but validates IPv4 only */ +function gen_subnetv4_max($ipaddr, $bits) { + if (is_ipaddrv4($ipaddr) && is_numericint($bits) && $bits <= 32) { + if ($bits == 32) { + return $ipaddr; + } + return long2ip32(ip2long($ipaddr) | ~gen_subnet_mask_long($bits)); + } + return ""; +} + +/* same as gen_subnet_max() but validates IPv6 only */ +function gen_subnetv6_max($ipaddr, $bits) { + if (is_ipaddrv6($ipaddr) && is_numericint($bits) && $bits <= 128) { + $endip_bin = substr(Net_IPv6::_ip2Bin($ipaddr), 0, $bits) . str_repeat('1', 128 - $bits); + return Net_IPv6::compress(Net_IPv6::_bin2Ip($endip_bin)); + } + return ""; +} + +/* returns a subnet mask (long given a bit count) */ +function gen_subnet_mask_long($bits) { + $sm = 0; + for ($i = 0; $i < $bits; $i++) { + $sm >>= 1; + $sm |= 0x80000000; + } + return $sm; +} + +/* same as above but returns a string */ +function gen_subnet_mask($bits) { + return long2ip(gen_subnet_mask_long($bits)); +} + +/* Convert long int to IP address, truncating to 32-bits. */ +function long2ip32($ip) { + return long2ip($ip & 0xFFFFFFFF); +} + +/* Convert IP address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms. */ +function ip2long32($ip) { + return (ip2long($ip) & 0xFFFFFFFF); +} + +/* Convert IP address to unsigned long int. */ +function ip2ulong($ip) { + return sprintf("%u", ip2long32($ip)); +} + +/* Find out how many IPs are contained within a given IP range + * e.g. 192.168.0.0 to 192.168.0.255 returns 256 + */ +function ip_range_size_v4($startip, $endip) { + if (is_ipaddrv4($startip) && is_ipaddrv4($endip)) { + // Operate as unsigned long because otherwise it wouldn't work + // when crossing over from 127.255.255.255 / 128.0.0.0 barrier + return abs(ip2ulong($startip) - ip2ulong($endip)) + 1; + } + return -1; +} + +/* Find the smallest possible subnet mask which can contain a given number of IPs + * e.g. 512 IPs can fit in a /23, but 513 IPs need a /22 + */ +function find_smallest_cidr_v4($number) { + $smallest = 1; + for ($b=32; $b > 0; $b--) { + $smallest = ($number <= pow(2, $b)) ? $b : $smallest; + } + return (32-$smallest); +} + +/* Return the previous IP address before the given address */ +function ip_before($ip, $offset = 1) { + return long2ip32(ip2long($ip) - $offset); +} + +/* Return the next IP address after the given address */ +function ip_after($ip, $offset = 1) { + return long2ip32(ip2long($ip) + $offset); +} + +/* Return true if the first IP is 'before' the second */ +function ip_less_than($ip1, $ip2) { + // Compare as unsigned long because otherwise it wouldn't work when + // crossing over from 127.255.255.255 / 128.0.0.0 barrier + return ip2ulong($ip1) < ip2ulong($ip2); +} + +/* Return true if the first IP is 'after' the second */ +function ip_greater_than($ip1, $ip2) { + // Compare as unsigned long because otherwise it wouldn't work + // when crossing over from 127.255.255.255 / 128.0.0.0 barrier + return ip2ulong($ip1) > ip2ulong($ip2); +} + +/* compare two IP addresses */ +function ipcmp($a, $b) { + if (ip_less_than($a, $b)) { + return -1; + } else if (ip_greater_than($a, $b)) { + return 1; + } else { + return 0; + } +} + +/* Convert a range of IPv4 addresses to an array of individual addresses. */ +/* Note: IPv6 ranges are not yet supported here. */ +function ip_range_to_address_array($startip, $endip, $max_size = 5000) { + if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) { + return false; + } + + if (ip_greater_than($startip, $endip)) { + // Swap start and end so we can process sensibly. + $temp = $startip; + $startip = $endip; + $endip = $temp; + } + + if (ip_range_size_v4($startip, $endip) > $max_size) { + return false; + } + + // Container for IP addresses within this range. + $rangeaddresses = array(); + $end_int = ip2ulong($endip); + for ($ip_int = ip2ulong($startip); $ip_int <= $end_int; $ip_int++) { + $rangeaddresses[] = long2ip($ip_int); + } + + return $rangeaddresses; +} + +/* Convert a range of IPv4 addresses to an array of subnets which can contain the range. */ +/* Note: IPv6 ranges are not yet supported here. */ +function ip_range_to_subnet_array($startip, $endip) { + if (!is_ipaddrv4($startip) || !is_ipaddrv4($endip)) { + return array(); + } + + if (ip_greater_than($startip, $endip)) { + // Swap start and end so we can process sensibly. + $temp = $startip; + $startip = $endip; + $endip = $temp; + } + + // Container for subnets within this range. + $rangesubnets = array(); + + // Figure out what the smallest subnet is that holds the number of IPs in the given range. + $cidr = find_smallest_cidr_v4(ip_range_size_v4($startip, $endip)); + + // Loop here to reduce subnet size and retest as needed. We need to make sure + // that the target subnet is wholly contained between $startip and $endip. + for ($cidr; $cidr <= 32; $cidr++) { + // Find the network and broadcast addresses for the subnet being tested. + $targetsub_min = gen_subnet($startip, $cidr); + $targetsub_max = gen_subnet_max($startip, $cidr); + + // Check best case where the range is exactly one subnet. + if (($targetsub_min == $startip) && ($targetsub_max == $endip)) { + // Hooray, the range is exactly this subnet! + return array("{$startip}/{$cidr}"); + } + + // These remaining scenarios will find a subnet that uses the largest + // chunk possible of the range being tested, and leave the rest to be + // tested recursively after the loop. + + // Check if the subnet begins with $startip and ends before $endip + if (($targetsub_min == $startip) && ip_less_than($targetsub_max, $endip)) { + break; + } + + // Check if the subnet ends at $endip and starts after $startip + if (ip_greater_than($targetsub_min, $startip) && ($targetsub_max == $endip)) { + break; + } + + // Check if the subnet is between $startip and $endip + if (ip_greater_than($targetsub_min, $startip) && ip_less_than($targetsub_max, $endip)) { + break; + } + } + + // Some logic that will recursively search from $startip to the first IP before the start of the subnet we just found. + // NOTE: This may never be hit, the way the above algo turned out, but is left for completeness. + if ($startip != $targetsub_min) { + $rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array($startip, ip_before($targetsub_min))); + } + + // Add in the subnet we found before, to preserve ordering + $rangesubnets[] = "{$targetsub_min}/{$cidr}"; + + // And some more logic that will search after the subnet we found to fill in to the end of the range. + if ($endip != $targetsub_max) { + $rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array(ip_after($targetsub_max), $endip)); + } + return $rangesubnets; +} + +/* returns true if $range is a valid pair of IPv4 or IPv6 addresses separated by a "-" + false - if not a valid pair + true (numeric 4 or 6) - if valid, gives type of addresses */ +function is_iprange($range) { + if (substr_count($range, '-') != 1) { + return false; + } + list($ip1, $ip2) = explode ('-', $range); + if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) { + return 4; + } + if (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) { + return 6; + } + return false; +} + +/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6 + false - not valid + true (numeric 4 or 6) - if valid, gives type of address */ +function is_ipaddr($ipaddr) { + if (is_ipaddrv4($ipaddr)) { + return 4; + } + if (is_ipaddrv6($ipaddr)) { + return 6; + } + return false; +} + +/* returns true if $ipaddr is a valid IPv6 address */ +function is_ipaddrv6($ipaddr) { + if (!is_string($ipaddr) || empty($ipaddr)) { + return false; + } + if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) { + $tmpip = explode("%", $ipaddr); + $ipaddr = $tmpip[0]; + } + return Net_IPv6::checkIPv6($ipaddr); +} + +/* returns true if $ipaddr is a valid dotted IPv4 address */ +function is_ipaddrv4($ipaddr) { + if (!is_string($ipaddr) || empty($ipaddr) || ip2long($ipaddr) === FALSE) { + return false; + } + return true; +} + +/* returns true if $ipaddr is a valid IPv6 linklocal address */ +function is_linklocal($ipaddr) { + return (strtolower(substr($ipaddr, 0, 5)) == "fe80:"); +} + +/* returns scope of a linklocal address */ +function get_ll_scope($addr) { + if (!is_linklocal($addr) || !strstr($addr, "%")) { + return ""; + } + list ($ll, $scope) = explode("%", $addr); + return $scope; +} + +/* returns true if $ipaddr is a valid literal IPv6 address */ +function is_literalipaddrv6($ipaddr) { + if (preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match)) { + $ipaddr = $match[1]; + } else { + return false; + } + + return is_ipaddrv6($ipaddr); +} + +/* returns true if $iport is a valid IPv4/IPv6 address + port + false - not valid + true (numeric 4 or 6) - if valid, gives type of address */ +function is_ipaddrwithport($ipport) { + $c = strrpos($ipport, ":"); + if ($c === false) { + return false; // can't split at final colon if no colon exists + } + + if (!is_port(substr($ipport, $c + 1))) { + return false; // no valid port after last colon + } + + $ip = substr($ipport, 0, $c); // else is text before last colon a valid IP + if (is_literalipaddrv6($ip)) { + return 6; + } elseif (is_ipaddrv4($ip)) { + return 4; + } else { + return false; + } +} + +function is_hostnamewithport($hostport) { + $parts = explode(":", $hostport); + $port = array_pop($parts); + if (count($parts) == 1) { + return is_hostname($parts[0]) && is_port($port); + } else { + return false; + } +} + +/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */ +function is_ipaddroralias($ipaddr) { + global $config; + + if (is_alias($ipaddr)) { + if (is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $alias) { + if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type'])) { + return true; + } + } + } + return false; + } else { + return is_ipaddr($ipaddr); + } + +} + +/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format + false - if not a valid subnet + true (numeric 4 or 6) - if valid, gives type of subnet */ +function is_subnet($subnet) { + if (is_string($subnet) && preg_match('/^(?:([0-9.]{7,15})|([0-9a-f:]{2,39}))\/(\d{1,3})$/i', $subnet, $parts)) { + if (is_ipaddrv4($parts[1]) && $parts[3] <= 32) { + return 4; + } + if (is_ipaddrv6($parts[2]) && $parts[3] <= 128) { + return 6; + } + } + return false; +} + +/* same as is_subnet() but accepts IPv4 only */ +function is_subnetv4($subnet) { + return (is_subnet($subnet) == 4); +} + +/* same as is_subnet() but accepts IPv6 only */ +function is_subnetv6($subnet) { + return (is_subnet($subnet) == 6); +} + +/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */ +function is_subnetoralias($subnet) { + global $aliastable; + + if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet])) { + return true; + } else { + return is_subnet($subnet); + } +} + +function subnet_size($subnet) { + if (is_subnetv4($subnet)) { + list ($ip, $bits) = explode("/", $subnet); + return round(exp(log(2) * (32 - $bits))); + } + else if (is_subnetv6($subnet)) { + list ($ip, $bits) = explode("/", $subnet); + return round(exp(log(2) * (128 - $bits))); + } + else { + return 0; + } +} + + +function subnet_expand($subnet) { + if (is_subnetv4($subnet)) { + return subnetv4_expand($subnet); + } else if (is_subnetv6($subnet)) { + return subnetv6_expand($subnet); + } else { + return $subnet; + } +} + +function subnetv4_expand($subnet) { + $result = array(); + list ($ip, $bits) = explode("/", $subnet); + $net = ip2long($ip); + $mask = (0xffffffff << (32 - $bits)); + $net &= $mask; + $size = round(exp(log(2) * (32 - $bits))); + for ($i = 0; $i < $size; $i += 1) { + $result[] = long2ip($net | $i); + } + return $result; +} + +/* find out whether two subnets overlap */ +function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) { + + if (!is_numeric($bits1)) { + $bits1 = 32; + } + if (!is_numeric($bits2)) { + $bits2 = 32; + } + + if ($bits1 < $bits2) { + $relbits = $bits1; + } else { + $relbits = $bits2; + } + + $sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1); + $sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2); + + return ($sn1 == $sn2); +} + +/* find out whether two IPv6 subnets overlap */ +function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) { + $sub1_min = gen_subnetv6($subnet1, $bits1); + $sub1_max = gen_subnetv6_max($subnet1, $bits1); + $sub2_min = gen_subnetv6($subnet2, $bits2); + $sub2_max = gen_subnetv6_max($subnet2, $bits2); + + return (is_inrange_v6($sub1_min, $sub2_min, $sub2_max) || is_inrange_v6($sub1_max, $sub2_min, $sub2_max) || is_inrange_v6($sub2_min, $sub1_min, $sub1_max)); +} + +/* return true if $addr is in $subnet, false if not */ +function ip_in_subnet($addr, $subnet) { + if (is_ipaddrv6($addr) && is_subnetv6($subnet)) { + return (Net_IPv6::isInNetmask($addr, $subnet)); + } else if (is_ipaddrv4($addr) && is_subnetv4($subnet)) { + list($ip, $mask) = explode('/', $subnet); + $mask = (0xffffffff << (32 - $mask)) & 0xffffffff; + return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask)); + } + return false; +} + +/* returns true if $hostname is just a valid hostname (top part without any of the domain part) */ +function is_unqualified_hostname($hostname) { + if (!is_string($hostname)) { + return false; + } + + if (preg_match('/^(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname)) { + return true; + } else { + return false; + } +} + +/* returns true if $hostname is a valid hostname, with or without being a fully-qualified domain name. */ +function is_hostname($hostname) { + if (!is_string($hostname)) { + return false; + } + + if (is_domain($hostname)) { + if ((substr_count($hostname, ".") == 1) && ($hostname[strlen($hostname)-1] == ".")) { + /* Only a single dot at the end like "test." - hosts cannot be directly in the root domain. */ + return false; + } else { + return true; + } + } else { + return false; + } +} + +/* returns true if $domain is a valid domain name */ +function is_domain($domain) { + if (!is_string($domain)) { + return false; + } + + if (preg_match('/^(?:(?:[a-z_0-9]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9])\.)*(?:[a-z_0-9]|[a-z_0-9][a-z_0-9\-]*[a-z_0-9\.])$/i', $domain)) { + return true; + } else { + return false; + } +} + +/* returns true if $macaddr is a valid MAC address */ +function is_macaddr($macaddr, $partial=false) { + $repeat = ($partial) ? '1,5' : '5'; + return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false; +} + +/* returns true if $name is a valid name for an alias + returns NULL if a reserved word is used + returns FALSE for bad chars in the name - this allows calling code to determine what the problem was. + aliases cannot be: + bad chars: anything except a-z 0-9 and underscore + bad names: empty string, pure numeric, pure underscore + reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and "pass" */ + +function is_validaliasname($name) { + /* Array of reserved words */ + $reserved = array("port", "pass"); + + if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) { + return false; + } + if (in_array($name, $reserved, true) || getservbyname($name, "tcp") || getservbyname($name, "udp") || getprotobyname($name)) { + return; /* return NULL */ + } + return true; +} + +/* returns true if $port is a valid TCP/UDP port */ +function is_port($port) { + if (ctype_digit($port) && ((intval($port) >= 1) && (intval($port) <= 65535))) { + return true; + } + if (getservbyname($port, "tcp") || getservbyname($port, "udp")) { + return true; + } + return false; +} + +/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */ +function is_portrange($portrange) { + $ports = explode(":", $portrange); + + return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1])); +} + +/* returns true if $port is a valid port number or an alias thereof */ +function is_portoralias($port) { + global $config; + + if (is_alias($port)) { + if (is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $alias) { + if ($alias['name'] == $port && preg_match("/port/i", $alias['type'])) { + return true; + } + } + } + return false; + } else { + return is_port($port); + } +} + +/* create ranges of sequential port numbers (200:215) and remove duplicates */ +function group_ports($ports) { + if (!is_array($ports) || empty($ports)) { + return; + } + + $uniq = array(); + foreach ($ports as $port) { + if (is_portrange($port)) { + list($begin, $end) = explode(":", $port); + if ($begin > $end) { + $aux = $begin; + $begin = $end; + $end = $aux; + } + for ($i = $begin; $i <= $end; $i++) { + if (!in_array($i, $uniq)) { + $uniq[] = $i; + } + } + } else if (is_port($port)) { + if (!in_array($port, $uniq)) { + $uniq[] = $port; + } + } + } + sort($uniq, SORT_NUMERIC); + + $result = array(); + foreach ($uniq as $idx => $port) { + if ($idx == 0) { + $result[] = $port; + continue; + } + + $last = end($result); + if (is_portrange($last)) { + list($begin, $end) = explode(":", $last); + } else { + $begin = $end = $last; + } + + if ($port == ($end+1)) { + $end++; + $result[count($result)-1] = "{$begin}:{$end}"; + } else { + $result[] = $port; + } + } + + return $result; +} + +/* returns true if $val is a valid shaper bandwidth value */ +function is_valid_shaperbw($val) { + return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val)); +} + +/* returns true if $test is in the range between $start and $end */ +function is_inrange_v4($test, $start, $end) { + if ((ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start))) { + return true; + } else { + return false; + } +} + +/* returns true if $test is in the range between $start and $end */ +function is_inrange_v6($test, $start, $end) { + if ((inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start))) { + return true; + } else { + return false; + } +} + +/* returns true if $test is in the range between $start and $end */ +function is_inrange($test, $start, $end) { + return is_ipaddrv6($test) ? is_inrange_v6($test, $start, $end) : is_inrange_v4($test, $start, $end); +} + +/* XXX: return the configured carp interface list */ +function get_configured_carp_interface_list($carpinterface = '', $family = 'inet', $what = 'ip') { + global $config; + + $iflist = array(); + + if (is_array($config['virtualip']['vip'])) { + $viparr = &$config['virtualip']['vip']; + foreach ($viparr as $vip) { + switch ($vip['mode']) { + case "carp": + if (!empty($carpinterface)) { + if ($carpinterface == "_vip{$vip['uniqid']}") { + switch ($what) { + case 'subnet': + if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) { + return $vip['subnet_bits']; + } else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) { + return $vip['subnet_bits']; + } + break; + case 'iface': + if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) { + return $vip['interface']; + } else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) { + return $vip['interface']; + } + break; + case 'vip': + if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) { + return $vip; + } else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) { + return $vip; + } + break; + case 'ip': + default: + if ($family == 'inet' && is_ipaddrv4($vip['subnet'])) { + return $vip['subnet']; + } else if ($family == 'inet6' && is_ipaddrv6($vip['subnet'])) { + return $vip['subnet']; + } + break; + } + } + } else { + $iflist["_vip{$vip['uniqid']}"] = $vip['subnet']; + } + break; + } + } + } + + return $iflist; +} + +/* return the configured IP aliases list */ +function get_configured_ip_aliases_list($returnfullentry = false) { + global $config; + + $alias_list = array(); + + if (is_array($config['virtualip']['vip'])) { + $viparr = &$config['virtualip']['vip']; + foreach ($viparr as $vip) { + if ($vip['mode'] == "ipalias") { + if ($returnfullentry) { + $alias_list[$vip['subnet']] = $vip; + } else { + $alias_list[$vip['subnet']] = $vip['interface']; + } + } + } + } + + return $alias_list; +} + +/* return all configured aliases list (IP, carp, proxyarp and other) */ +function get_configured_vips_list() { + global $config; + + $alias_list = array(); + + if (is_array($config['virtualip']['vip'])) { + $viparr = &$config['virtualip']['vip']; + foreach ($viparr as $vip) { + if ($vip['mode'] == "carp") { + $alias_list[] = array("ipaddr" => $vip['subnet'], "if" => "{$vip['interface']}_vip{$vip['vhid']}"); + } else { + $alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']); + } + } + } + + return $alias_list; +} + +/* comparison function for sorting by the order in which interfaces are normally created */ +function compare_interface_friendly_names($a, $b) { + if ($a == $b) { + return 0; + } else if ($a == 'wan') { + return -1; + } else if ($b == 'wan') { + return 1; + } else if ($a == 'lan') { + return -1; + } else if ($b == 'lan') { + return 1; + } + + return strnatcmp($a, $b); +} + +/* return the configured interfaces list. */ +function get_configured_interface_list($only_opt = false, $withdisabled = false) { + global $config; + + $iflist = array(); + + /* if list */ + foreach ($config['interfaces'] as $if => $ifdetail) { + if ($only_opt && ($if == "wan" || $if == "lan")) { + continue; + } + if (isset($ifdetail['enable']) || $withdisabled == true) { + $iflist[$if] = $if; + } + } + + return $iflist; +} + +/* return the configured interfaces list. */ +function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) { + global $config; + + $iflist = array(); + + /* if list */ + foreach ($config['interfaces'] as $if => $ifdetail) { + if ($only_opt && ($if == "wan" || $if == "lan")) { + continue; + } + if (isset($ifdetail['enable']) || $withdisabled == true) { + $tmpif = get_real_interface($if); + if (!empty($tmpif)) { + $iflist[$tmpif] = $if; + } + } + } + + return $iflist; +} + +/* return the configured interfaces list with their description. */ +function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) { + global $config; + + $iflist = array(); + + /* if list */ + foreach ($config['interfaces'] as $if => $ifdetail) { + if ($only_opt && ($if == "wan" || $if == "lan")) { + continue; + } + if (isset($ifdetail['enable']) || $withdisabled == true) { + if (empty($ifdetail['descr'])) { + $iflist[$if] = strtoupper($if); + } else { + $iflist[$if] = strtoupper($ifdetail['descr']); + } + } + } + + return $iflist; +} + +/* + * get_configured_ip_addresses() - Return a list of all configured + * interfaces IP Addresses + * + */ +function get_configured_ip_addresses() { + global $config; + + if (!function_exists('get_interface_ip')) { + require_once("interfaces.inc"); + } + $ip_array = array(); + $interfaces = get_configured_interface_list(); + if (is_array($interfaces)) { + foreach ($interfaces as $int) { + $ipaddr = get_interface_ip($int); + $ip_array[$int] = $ipaddr; + } + } + $interfaces = get_configured_carp_interface_list(); + if (is_array($interfaces)) { + foreach ($interfaces as $int => $ipaddr) { + $ip_array[$int] = $ipaddr; + } + } + + /* pppoe server */ + if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) { + foreach ($config['pppoes']['pppoe'] as $pppoe) { + if ($pppoe['mode'] == "server") { + if (is_ipaddr($pppoe['localip'])) { + $int = "pppoes". $pppoe['pppoeid']; + $ip_array[$int] = $pppoe['localip']; + } + } + } + } + + return $ip_array; +} + +/* + * get_configured_ipv6_addresses() - Return a list of all configured + * interfaces IPv6 Addresses + * + */ +function get_configured_ipv6_addresses() { + require_once("interfaces.inc"); + $ipv6_array = array(); + $interfaces = get_configured_interface_list(); + if (is_array($interfaces)) { + foreach ($interfaces as $int) { + $ipaddrv6 = get_interface_ipv6($int); + $ipv6_array[$int] = $ipaddrv6; + } + } + $interfaces = get_configured_carp_interface_list(); + if (is_array($interfaces)) { + foreach ($interfaces as $int => $ipaddrv6) { + $ipv6_array[$int] = $ipaddrv6; + } + } + return $ipv6_array; +} + +/* + * get_interface_list() - Return a list of all physical interfaces + * along with MAC and status. + * + * $mode = "active" - use ifconfig -lu + * "media" - use ifconfig to check physical connection + * status (much slower) + */ +function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") { + global $config; + $upints = array(); + /* get a list of virtual interface types */ + if (!$vfaces) { + $vfaces = array( + 'bridge', + 'ppp', + 'pppoe', + 'pptp', + 'l2tp', + 'sl', + 'gif', + 'gre', + 'faith', + 'lo', + 'ng', + '_vlan', + '_wlan', + 'pflog', + 'plip', + 'pfsync', + 'enc', + 'tun', + 'carp', + 'lagg', + 'vip', + 'ipfw' + ); + } + switch ($mode) { + case "active": + $upints = pfSense_interface_listget(IFF_UP); + break; + case "media": + $intlist = pfSense_interface_listget(); + $ifconfig = ""; + exec("/sbin/ifconfig -a", $ifconfig); + $regexp = '/(' . implode('|', $intlist) . '):\s/'; + $ifstatus = preg_grep('/status:/', $ifconfig); + foreach ($ifstatus as $status) { + $int = array_shift($intlist); + if (stristr($status, "active")) { + $upints[] = $int; + } + } + break; + default: + $upints = pfSense_interface_listget(); + break; + } + /* build interface list with netstat */ + $linkinfo = ""; + exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo); + array_shift($linkinfo); + /* build ip address list with netstat */ + $ipinfo = ""; + exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo); + array_shift($ipinfo); + foreach ($linkinfo as $link) { + $friendly = ""; + $alink = explode(" ", $link); + $ifname = rtrim(trim($alink[0]), '*'); + /* trim out all numbers before checking for vfaces */ + if (!in_array(array_shift(preg_split('/\d/', $ifname)), $vfaces) && + !stristr($ifname, "_vlan") && !stristr($ifname, "_wlan")) { + $toput = array( + "mac" => trim($alink[1]), + "up" => in_array($ifname, $upints) + ); + foreach ($ipinfo as $ip) { + $aip = explode(" ", $ip); + if ($aip[0] == $ifname) { + $toput['ipaddr'] = $aip[1]; + } + } + if (is_array($config['interfaces'])) { + foreach ($config['interfaces'] as $name => $int) { + if ($int['if'] == $ifname) { + $friendly = $name; + } + } + } + switch ($keyby) { + case "physical": + if ($friendly != "") { + $toput['friendly'] = $friendly; + } + $dmesg_arr = array(); + exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr); + preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg); + $toput['dmesg'] = $dmesg[1][0]; + $iflist[$ifname] = $toput; + break; + case "ppp": + + case "friendly": + if ($friendly != "") { + $toput['if'] = $ifname; + $iflist[$friendly] = $toput; + } + break; + } + } + } + return $iflist; +} + +/****f* util/log_error +* NAME +* log_error - Sends a string to syslog. +* INPUTS +* $error - string containing the syslog message. +* RESULT +* null +******/ +function log_error($error) { + global $g; + $page = $_SERVER['SCRIPT_NAME']; + if (empty($page)) { + $files = get_included_files(); + $page = basename($files[0]); + } + syslog(LOG_ERR, "$page: $error"); + if ($g['debug']) { + syslog(LOG_WARNING, var_dump(debug_backtrace())); + } + return; +} + +/****f* util/log_auth +* NAME +* log_auth - Sends a string to syslog as LOG_AUTH facility +* INPUTS +* $error - string containing the syslog message. +* RESULT +* null +******/ +function log_auth($error) { + global $g; + $page = $_SERVER['SCRIPT_NAME']; + syslog(LOG_AUTH, "$page: $error"); + if ($g['debug']) { + syslog(LOG_WARNING, var_dump(debug_backtrace())); + } + return; +} + +/****f* util/exec_command + * NAME + * exec_command - Execute a command and return a string of the result. + * INPUTS + * $command - String of the command to be executed. + * RESULT + * String containing the command's result. + * NOTES + * This function returns the command's stdout and stderr. + ******/ +function exec_command($command) { + $output = array(); + exec($command . ' 2>&1', $output); + return(implode("\n", $output)); +} + +/* wrapper for exec() */ +function mwexec($command, $mute = false, $clearsigmask = false) { + global $g; + + if ($g['debug']) { + if (!$_SERVER['REMOTE_ADDR']) { + echo "mwexec(): $command\n"; + } + } + $oarr = array(); + $retval = 0; + + if ($clearsigmask) { + $oldset = array(); + pcntl_sigprocmask(SIG_SETMASK, array(), $oldset); + } + $garbage = exec("$command 2>&1", $oarr, $retval); + if ($clearsigmask) { + pcntl_sigprocmask(SIG_SETMASK, $oldset); + } + + if (isset($config['system']['developerspew'])) { + $mute = false; + } + if (($retval <> 0) && ($mute === false)) { + $output = implode(" ", $oarr); + log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, $output)); + unset($output); + } + unset($oarr); + return $retval; +} + +/* wrapper for exec() in background */ +function mwexec_bg($command, $clearsigmask = false) { + global $g; + + if ($g['debug']) { + if (!$_SERVER['REMOTE_ADDR']) { + echo "mwexec(): $command\n"; + } + } + + if ($clearsigmask) { + $oldset = array(); + pcntl_sigprocmask(SIG_SETMASK, array(), $oldset); + } + $_gb = exec("/usr/bin/nohup $command > /dev/null 2>&1 &"); + if ($clearsigmask) { + pcntl_sigprocmask(SIG_SETMASK, $oldset); + } + unset($_gb); +} + +/* unlink a file, if it exists */ +function unlink_if_exists($fn) { + $to_do = glob($fn); + if (is_array($to_do)) { + foreach ($to_do as $filename) { + @unlink($filename); + } + } else { + @unlink($fn); + } +} +/* make a global alias table (for faster lookups) */ +function alias_make_table($config) { + global $aliastable; + + $aliastable = array(); + + if (is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $alias) { + if ($alias['name']) { + $aliastable[$alias['name']] = $alias['address']; + } + } + } +} + +/* check if an alias exists */ +function is_alias($name) { + global $aliastable; + + return isset($aliastable[$name]); +} + +function alias_get_type($name) { + global $config; + + if (is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $alias) { + if ($name == $alias['name']) { + return $alias['type']; + } + } + } + + return ""; +} + +/* expand a host or network alias, if necessary */ +function alias_expand($name) { + global $aliastable; + + if (isset($aliastable[$name])) { + // alias names cannot be strictly numeric. redmine #4289 + if (is_numericint($name)) { + return null; + } + return "\${$name}"; + } else if (is_ipaddr($name) || is_subnet($name) || is_port($name) || is_portrange($name)) { + return "{$name}"; + } else { + return null; + } +} + +function alias_expand_urltable($name) { + global $config; + $urltable_prefix = "/var/db/aliastables/"; + $urltable_filename = $urltable_prefix . $name . ".txt"; + + if (is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $alias) { + if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) { + if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename)) { + return $urltable_filename; + } else { + send_event("service sync alias {$name}"); + break; + } + } + } + } + return null; +} + +/* verify (and remove) the digital signature on a file - returns 0 if OK */ +function verify_digital_signature($fname) { + global $g; + + if (!file_exists("/usr/local/sbin/gzsig")) { + return 4; + } + + return mwexec("/usr/local/sbin/gzsig verify {$g['etc_path']}/pubkey.pem < " . escapeshellarg($fname)); +} + +/* obtain MAC address given an IP address by looking at the ARP table */ +function arp_get_mac_by_ip($ip) { + mwexec("/sbin/ping -c 1 -t 1 " . escapeshellarg($ip), true); + $arpoutput = ""; + exec("/usr/sbin/arp -n " . escapeshellarg($ip), $arpoutput); + + if ($arpoutput[0]) { + $arpi = explode(" ", $arpoutput[0]); + $macaddr = $arpi[3]; + if (is_macaddr($macaddr)) { + return $macaddr; + } else { + return false; + } + } + + return false; +} + +/* return a fieldname that is safe for xml usage */ +function xml_safe_fieldname($fieldname) { + $replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', + '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?', + ':', ',', '.', '\'', '\\' + ); + return strtolower(str_replace($replace, "", $fieldname)); +} + +function mac_format($clientmac) { + global $config, $cpzone; + + $mac = explode(":", $clientmac); + $mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false; + + switch ($mac_format) { + case 'singledash': + return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]"; + + case 'ietf': + return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]"; + + case 'cisco': + return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]"; + + case 'unformatted': + return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]"; + + default: + return $clientmac; + } +} + +function resolve_retry($hostname, $retries = 5) { + + if (is_ipaddr($hostname)) { + return $hostname; + } + + for ($i = 0; $i < $retries; $i++) { + // FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss + $ip = gethostbyname($hostname); + + if ($ip && $ip != $hostname) { + /* success */ + return $ip; + } + + sleep(1); + } + + return false; +} + +function format_bytes($bytes) { + if ($bytes >= 1073741824) { + return sprintf("%.2f GB", $bytes/1073741824); + } else if ($bytes >= 1048576) { + return sprintf("%.2f MB", $bytes/1048576); + } else if ($bytes >= 1024) { + return sprintf("%.0f KB", $bytes/1024); + } else { + return sprintf("%d bytes", $bytes); + } +} + +function update_filter_reload_status($text) { + global $g; + + file_put_contents("{$g['varrun_path']}/filter_reload_status", $text); +} + +/****** util/return_dir_as_array + * NAME + * return_dir_as_array - Return a directory's contents as an array. + * INPUTS + * $dir - string containing the path to the desired directory. + * $filter_regex - string containing a regular expression to filter file names. Default empty. + * RESULT + * $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid. + ******/ +function return_dir_as_array($dir, $filter_regex = '') { + $dir_array = array(); + if (is_dir($dir)) { + if ($dh = opendir($dir)) { + while (($file = readdir($dh)) !== false) { + if (($file == ".") || ($file == "..")) { + continue; + } + + if (empty($filter_regex) || preg_match($filter_regex, $file)) { + array_push($dir_array, $file); + } + } + closedir($dh); + } + } + return $dir_array; +} + +function run_plugins($directory) { + global $config, $g; + + /* process packager manager custom rules */ + $files = return_dir_as_array($directory); + if (is_array($files)) { + foreach ($files as $file) { + if (stristr($file, ".sh") == true) { + mwexec($directory . $file . " start"); + } else if (!is_dir($directory . "/" . $file) && stristr($file, ".inc")) { + require_once($directory . "/" . $file); + } + } + } +} + +/* + * safe_mkdir($path, $mode = 0755) + * create directory if it doesn't already exist and isn't a file! + */ +function safe_mkdir($path, $mode = 0755) { + global $g; + + if (!is_file($path) && !is_dir($path)) { + return @mkdir($path, $mode, true); + } else { + return false; + } +} + +/* + * get_sysctl($names) + * Get values of sysctl OID's listed in $names (accepts an array or a single + * name) and return an array of key/value pairs set for those that exist + */ +function get_sysctl($names) { + if (empty($names)) { + return array(); + } + + if (is_array($names)) { + $name_list = array(); + foreach ($names as $name) { + $name_list[] = escapeshellarg($name); + } + } else { + $name_list = array(escapeshellarg($names)); + } + + exec("/sbin/sysctl -i " . implode(" ", $name_list), $output); + $values = array(); + foreach ($output as $line) { + $line = explode(": ", $line, 2); + if (count($line) == 2) { + $values[$line[0]] = $line[1]; + } + } + + return $values; +} + +/* + * get_single_sysctl($name) + * Wrapper for get_sysctl() to simplify read of a single sysctl value + * return the value for sysctl $name or empty string if it doesn't exist + */ +function get_single_sysctl($name) { + if (empty($name)) { + return ""; + } + + $value = get_sysctl($name); + if (empty($value) || !isset($value[$name])) { + return ""; + } + + return $value[$name]; +} + +/* + * set_sysctl($value_list) + * Set sysctl OID's listed as key/value pairs and return + * an array with keys set for those that succeeded + */ +function set_sysctl($values) { + if (empty($values)) { + return array(); + } + + $value_list = array(); + foreach ($values as $key => $value) { + $value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value); + } + + exec("/sbin/sysctl -i " . implode(" ", $value_list), $output, $success); + + /* Retry individually if failed (one or more read-only) */ + if ($success <> 0 && count($value_list) > 1) { + foreach ($value_list as $value) { + exec("/sbin/sysctl -i " . $value, $output); + } + } + + $ret = array(); + foreach ($output as $line) { + $line = explode(": ", $line, 2); + if (count($line) == 2) { + $ret[$line[0]] = true; + } + } + + return $ret; +} + +/* + * set_single_sysctl($name, $value) + * Wrapper to set_sysctl() to make it simple to set only one sysctl + * returns boolean meaning if it succeeded + */ +function set_single_sysctl($name, $value) { + if (empty($name)) { + return false; + } + + $result = set_sysctl(array($name => $value)); + + if (!isset($result[$name]) || $result[$name] != $value) { + return false; + } + + return true; +} + +/* + * get_memory() + * returns an array listing the amount of + * memory installed in the hardware + * [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes + * [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes + */ +function get_memory() { + $physmem = get_single_sysctl("hw.physmem"); + $realmem = get_single_sysctl("hw.realmem"); + /* convert from bytes to megabytes */ + return array(($physmem/1048576), ($realmem/1048576)); +} + +function mute_kernel_msgs() { + global $g, $config; + // Do not mute serial console. The kernel gets very very cranky + // and will start dishing you cannot control tty errors. + if ($g['platform'] == 'nanobsd') { + return; + } + if ($config['system']['enableserial']) { + return; + } + exec("/sbin/conscontrol mute on"); +} + +function unmute_kernel_msgs() { + global $g; + // Do not mute serial console. The kernel gets very very cranky + // and will start dishing you cannot control tty errors. + if ($g['platform'] == 'nanobsd') { + return; + } + exec("/sbin/conscontrol mute off"); +} + +function start_devd() { + /* Use the undocumented -q options of devd to quiet its log spamming */ + $_gb = exec("/sbin/devd -q"); + sleep(1); + unset($_gb); +} + +function is_interface_vlan_mismatch() { + global $config, $g; + + if (is_array($config['vlans']['vlan'])) { + foreach ($config['vlans']['vlan'] as $vlan) { + if (does_interface_exist($vlan['if']) == false) { + return true; + } + } + } + + return false; +} + +function is_interface_mismatch() { + global $config, $g; + + $do_assign = false; + $i = 0; + $missing_interfaces = array(); + if (is_array($config['interfaces'])) { + foreach ($config['interfaces'] as $ifname => $ifcfg) { + if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) { + // Do not check these interfaces. + $i++; + continue; + } else if (does_interface_exist($ifcfg['if']) == false) { + $missing_interfaces[] = $ifcfg['if']; + $do_assign = true; + } else { + $i++; + } + } + } + + if (file_exists("{$g['tmp_path']}/assign_complete")) { + $do_assign = false; + } + + if (!empty($missing_interfaces) && $do_assign) { + file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces)); + } else { + @unlink("{$g['tmp_path']}/missing_interfaces"); + } + + return $do_assign; +} + +/* sync carp entries to other firewalls */ +function carp_sync_client() { + global $g; + send_event("filter sync"); +} + +/****f* util/isAjax + * NAME + * isAjax - reports if the request is driven from prototype + * INPUTS + * none + * RESULT + * true/false + ******/ +function isAjax() { + return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'; +} + +/****f* util/timeout + * NAME + * timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space. + * INPUTS + * optional, seconds to wait before timeout. Default 9 seconds. + * RESULT + * returns 1 char of user input or null if no input. + ******/ +function timeout($timer = 9) { + while (!isset($key)) { + if ($timer >= 9) { + echo chr(8) . chr(8) . ($timer == 9 ? chr(32) : null) . "{$timer}"; + } else { + echo chr(8). "{$timer}"; + } + `/bin/stty -icanon min 0 time 25`; + $key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`); + `/bin/stty icanon`; + if ($key == '') { + unset($key); + } + $timer--; + if ($timer == 0) { + break; + } + } + return $key; +} + +/****f* util/msort + * NAME + * msort - sort array + * INPUTS + * $array to be sorted, field to sort by, direction of sort + * RESULT + * returns newly sorted array + ******/ +function msort($array, $id = "id", $sort_ascending = true) { + $temp_array = array(); + while (count($array)>0) { + $lowest_id = 0; + $index = 0; + foreach ($array as $item) { + if (isset($item[$id])) { + if ($array[$lowest_id][$id]) { + if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) { + $lowest_id = $index; + } + } + } + $index++; + } + $temp_array[] = $array[$lowest_id]; + $array = array_merge(array_slice($array, 0, $lowest_id), array_slice($array, $lowest_id + 1)); + } + if ($sort_ascending) { + return $temp_array; + } else { + return array_reverse($temp_array); + } +} + +/****f* util/is_URL + * NAME + * is_URL + * INPUTS + * string to check + * RESULT + * Returns true if item is a URL + ******/ +function is_URL($url) { + $match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url); + if ($match) { + return true; + } + return false; +} + +function is_file_included($file = "") { + $files = get_included_files(); + if (in_array($file, $files)) { + return true; + } + + return false; +} + +/* + * Replace a value on a deep associative array using regex + */ +function array_replace_values_recursive($data, $match, $replace) { + if (empty($data)) { + return $data; + } + + if (is_string($data)) { + $data = preg_replace("/{$match}/", $replace, $data); + } else if (is_array($data)) { + foreach ($data as $k => $v) { + $data[$k] = array_replace_values_recursive($v, $match, $replace); + } + } + + return $data; +} + +/* + This function was borrowed from a comment on PHP.net at the following URL: + http://www.php.net/manual/en/function.array-merge-recursive.php#73843 + */ +function array_merge_recursive_unique($array0, $array1) { + + $arrays = func_get_args(); + $remains = $arrays; + + // We walk through each arrays and put value in the results (without + // considering previous value). + $result = array(); + + // loop available array + foreach ($arrays as $array) { + + // The first remaining array is $array. We are processing it. So + // we remove it from remaining arrays. + array_shift($remains); + + // We don't care non array param, like array_merge since PHP 5.0. + if (is_array($array)) { + // Loop values + foreach ($array as $key => $value) { + if (is_array($value)) { + // we gather all remaining arrays that have such key available + $args = array(); + foreach ($remains as $remain) { + if (array_key_exists($key, $remain)) { + array_push($args, $remain[$key]); + } + } + + if (count($args) > 2) { + // put the recursion + $result[$key] = call_user_func_array(__FUNCTION__, $args); + } else { + foreach ($value as $vkey => $vval) { + $result[$key][$vkey] = $vval; + } + } + } else { + // simply put the value + $result[$key] = $value; + } + } + } + } + return $result; +} + + +/* + * converts a string like "a,b,c,d" + * into an array like array("a" => "b", "c" => "d") + */ +function explode_assoc($delimiter, $string) { + $array = explode($delimiter, $string); + $result = array(); + $numkeys = floor(count($array) / 2); + for ($i = 0; $i < $numkeys; $i += 1) { + $result[$array[$i * 2]] = $array[$i * 2 + 1]; + } + return $result; +} + +function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) { + global $config, $aliastable; + + /* Bail if there are no routes, but return an array always so callers don't have to check. */ + if (!is_array($config['staticroutes']['route'])) { + return array(); + } + + $allstaticroutes = array(); + $allsubnets = array(); + /* Loop through routes and expand aliases as we find them. */ + foreach ($config['staticroutes']['route'] as $route) { + if (is_alias($route['network'])) { + if (!isset($aliastable[$route['network']])) { + continue; + } + + $subnets = preg_split('/\s+/', $aliastable[$route['network']]); + foreach ($subnets as $net) { + if (!is_subnet($net)) { + if (is_ipaddrv4($net)) { + $net .= "/32"; + } else if (is_ipaddrv6($net)) { + $net .= "/128"; + } else if ($returnhostnames === false || !is_fqdn($net)) { + continue; + } + } + $temproute = $route; + $temproute['network'] = $net; + $allstaticroutes[] = $temproute; + $allsubnets[] = $net; + } + } elseif (is_subnet($route['network'])) { + $allstaticroutes[] = $route; + $allsubnets[] = $route['network']; + } + } + if ($returnsubnetsonly) { + return $allsubnets; + } else { + return $allstaticroutes; + } +} + +/****f* util/get_alias_list + * NAME + * get_alias_list - Provide a list of aliases. + * INPUTS + * $type - Optional, can be a string or array specifying what type(s) of aliases you need. + * RESULT + * Array containing list of aliases. + * If $type is unspecified, all aliases are returned. + * If $type is a string, all aliases of the type specified in $type are returned. + * If $type is an array, all aliases of any type specified in any element of $type are returned. + */ +function get_alias_list($type = null) { + global $config; + $result = array(); + if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $alias) { + if ($type === null) { + $result[] = $alias['name']; + } else if (is_array($type)) { + if (in_array($alias['type'], $type)) { + $result[] = $alias['name']; + } + } else if ($type === $alias['type']) { + $result[] = $alias['name']; + } + } + } + return $result; +} + +/* returns an array consisting of every element of $haystack that is not equal to $needle. */ +function array_exclude($needle, $haystack) { + $result = array(); + if (is_array($haystack)) { + foreach ($haystack as $thing) { + if ($needle !== $thing) { + $result[] = $thing; + } + } + } + return $result; +} + +function get_current_theme() { + global $config, $g; + /* + * if user has selected a custom template, use it. + * otherwise default to pfsense template + */ + if (($g["disablethemeselection"] === true) && !empty($g["default_theme"]) && (is_dir($g["www_path"].'/themes/'.$g["default_theme"]))) { + $theme = $g["default_theme"]; + } elseif ($config['theme'] <> "" && (is_dir($g["www_path"].'/themes/'.$config['theme']))) { + $theme = $config['theme']; + } else { + $theme = "pfsense"; + } + /* + * If this device is an apple ipod/iphone + * switch the theme to one that works with it. + */ + $lowres_ua = array("iPhone", "iPod", "iPad", "Android", "BlackBerry", "Opera Mini", "Opera Mobi", "PlayBook", "IEMobile"); + foreach ($lowres_ua as $useragent) { + if (strstr($_SERVER['HTTP_USER_AGENT'], $useragent)) { + $theme = (empty($g['theme_lowres']) && (is_dir($g["www_path"].'/themes/'.$g['theme_lowres']))) ? "pfsense" : $g['theme_lowres']; + } + } + return $theme; +} + +/* Define what is preferred, IPv4 or IPv6 */ +function prefer_ipv4_or_ipv6() { + global $config; + + if (isset($config['system']['prefer_ipv4'])) { + mwexec("/etc/rc.d/ip6addrctl prefer_ipv4"); + } else { + mwexec("/etc/rc.d/ip6addrctl prefer_ipv6"); + } +} + +/* Redirect to page passing parameters via POST */ +function post_redirect($page, $params) { + if (!is_array($params)) { + return; + } + + print "<html><body><form action=\"{$page}\" name=\"formredir\" method=\"post\">\n"; + foreach ($params as $key => $value) { + print "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\" />\n"; + } + print "</form><script type=\"text/javascript\">document.formredir.submit();</script>\n"; + print "</body></html>\n"; +} + +/* Locate disks that can be queried for S.M.A.R.T. data. */ +function get_smart_drive_list() { + $disk_list = explode(" ", get_single_sysctl("kern.disks")); + foreach ($disk_list as $id => $disk) { + // We only want certain kinds of disks for S.M.A.R.T. + // 1 is a match, 0 is no match, False is any problem processing the regex + if (preg_match("/^(ad|da|ada).*[0-9]{1,2}$/", $disk) !== 1) { + unset($disk_list[$id]); + } + } + sort($disk_list); + return $disk_list; +} + +?> diff --git a/src/etc/inc/uuid.php b/src/etc/inc/uuid.php new file mode 100644 index 0000000..700f392 --- /dev/null +++ b/src/etc/inc/uuid.php @@ -0,0 +1,327 @@ +<?php +/*- + * Copyright (c) 2008 Fredrik Lindberg - http://www.shapeshifter.se + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * UUID (RFC4122) Generator + * http://tools.ietf.org/html/rfc4122 + * + * Implements version 1, 3, 4 and 5 + */ +class UUID { + /* UUID versions */ + const UUID_TIME = 1; /* Time based UUID */ + const UUID_NAME_MD5 = 3; /* Name based (MD5) UUID */ + const UUID_RANDOM = 4; /* Random UUID */ + const UUID_NAME_SHA1 = 5; /* Name based (SHA1) UUID */ + + /* UUID formats */ + const FMT_FIELD = 100; + const FMT_STRING = 101; + const FMT_BINARY = 102; + const FMT_QWORD = 1; /* Quad-word, 128-bit (not impl.) */ + const FMT_DWORD = 2; /* Double-word, 64-bit (not impl.) */ + const FMT_WORD = 4; /* Word, 32-bit (not impl.) */ + const FMT_SHORT = 8; /* Short (not impl.) */ + const FMT_BYTE = 16; /* Byte */ + const FMT_DEFAULT = 16; + + /* Field UUID representation */ + static private $m_uuid_field = array( + 'time_low' => 0, /* 32-bit */ + 'time_mid' => 0, /* 16-bit */ + 'time_hi' => 0, /* 16-bit */ + 'clock_seq_hi' => 0, /* 8-bit */ + 'clock_seq_low' => 0, /* 8-bit */ + 'node' => array() /* 48-bit */ + ); + + static private $m_generate = array( + self::UUID_TIME => "generateTime", + self::UUID_RANDOM => "generateRandom", + self::UUID_NAME_MD5 => "generateNameMD5", + self::UUID_NAME_SHA1 => "generateNameSHA1" + ); + + static private $m_convert = array( + self::FMT_FIELD => array( + self::FMT_BYTE => "conv_field2byte", + self::FMT_STRING => "conv_field2string", + self::FMT_BINARY => "conv_field2binary" + ), + self::FMT_BYTE => array( + self::FMT_FIELD => "conv_byte2field", + self::FMT_STRING => "conv_byte2string", + self::FMT_BINARY => "conv_byte2binary" + ), + self::FMT_STRING => array( + self::FMT_BYTE => "conv_string2byte", + self::FMT_FIELD => "conv_string2field", + self::FMT_BINARY => "conv_string2binary" + ), + ); + + /* Swap byte order of a 32-bit number */ + static private function swap32($x) { + return (($x & 0x000000ff) << 24) | (($x & 0x0000ff00) << 8) | + (($x & 0x00ff0000) >> 8) | (($x & 0xff000000) >> 24); + } + + /* Swap byte order of a 16-bit number */ + static private function swap16($x) { + return (($x & 0x00ff) << 8) | (($x & 0xff00) >> 8); + } + + /* Auto-detect UUID format */ + static private function detectFormat($src) { + if (is_string($src)) + return self::FMT_STRING; + else if (is_array($src)) { + $len = count($src); + if ($len == 1 || ($len % 2) == 0) + return $len; + else + return (-1); + } + else + return self::FMT_BINARY; + } + + /* + * Public API, generate a UUID of 'type' in format 'fmt' for + * the given namespace 'ns' and node 'node' + */ + static public function generate($type, $fmt = self::FMT_BYTE, + $node = "", $ns = "") { + $func = self::$m_generate[$type]; + if (!isset($func)) + return null; + $conv = self::$m_convert[self::FMT_FIELD][$fmt]; + + $uuid = self::$func($ns, $node); + return self::$conv($uuid); + } + + /* + * Public API, convert a UUID from one format to another + */ + static public function convert($uuid, $from, $to) { + $conv = self::$m_convert[$from][$to]; + if (!isset($conv)) + return ($uuid); + + return (self::$conv($uuid)); + } + + /* + * Generate an UUID version 4 (pseudo random) + */ + static private function generateRandom($ns, $node) { + $uuid = self::$m_uuid_field; + + $uuid['time_hi'] = (4 << 12) | (mt_rand(0, 0x1000)); + $uuid['clock_seq_hi'] = (1 << 7) | mt_rand(0, 128); + $uuid['time_low'] = mt_rand(0, 0xffffffff); + $uuid['time_mid'] = mt_rand(0, 0x0000ffff); + $uuid['clock_seq_low'] = mt_rand(0, 255); + for ($i = 0; $i < 6; $i++) + $uuid['node'][$i] = mt_rand(0, 255); + return ($uuid); + } + + /* + * Generate UUID version 3 and 5 (name based) + */ + static private function generateName($ns, $node, $hash, $version) { + $ns_fmt = self::detectFormat($ns); + $field = self::convert($ns, $ns_fmt, self::FMT_FIELD); + + /* Swap byte order to keep it in big endian on all platforms */ + $field['time_low'] = self::swap32($field['time_low']); + $field['time_mid'] = self::swap16($field['time_mid']); + $field['time_hi'] = self::swap16($field['time_hi']); + + /* Convert the namespace to binary and concatenate node */ + $raw = self::convert($field, self::FMT_FIELD, self::FMT_BINARY); + $raw .= $node; + + /* Hash the namespace and node and convert to a byte array */ + $val = $hash($raw, true); + $tmp = unpack('C16', $val); + foreach (array_keys($tmp) as $key) + $byte[$key - 1] = $tmp[$key]; + + /* Convert byte array to a field array */ + $field = self::conv_byte2field($byte); + + $field['time_low'] = self::swap32($field['time_low']); + $field['time_mid'] = self::swap16($field['time_mid']); + $field['time_hi'] = self::swap16($field['time_hi']); + + /* Apply version and constants */ + $field['clock_seq_hi'] &= 0x3f; + $field['clock_seq_hi'] |= (1 << 7); + $field['time_hi'] &= 0x0fff; + $field['time_hi'] |= ($version << 12); + + return ($field); + } + static private function generateNameMD5($ns, $node) { + return self::generateName($ns, $node, "md5", + self::UUID_NAME_MD5); + } + static private function generateNameSHA1($ns, $node) { + return self::generateName($ns, $node, "sha1", + self::UUID_NAME_SHA1); + } + + /* + * Generate UUID version 1 (time based) + */ + static private function generateTime($ns, $node) { + $uuid = self::$m_uuid_field; + + /* + * Get current time in 100 ns intervals. The magic value + * is the offset between UNIX epoch and the UUID UTC + * time base October 15, 1582. + */ + $tp = gettimeofday(); + $time = ($tp['sec'] * 10000000) + ($tp['usec'] * 10) + + 0x01B21DD213814000; + + /* Work around PHP 32-bit bit-operation limits */ + $q = intval($time / 0xffffffff); + $low = $time - ($q * (0xffffffff + 1)); + $high = intval(($time - $low) / 0xffffffff); + + $uuid['time_low'] = $low; + $uuid['time_mid'] = $high & 0x0000ffff; + $uuid['time_hi'] = ($high & 0x0fff) | (self::UUID_TIME << 12); + + /* + * We don't support saved state information and generate + * a random clock sequence each time. + */ + $uuid['clock_seq_hi'] = (1 << 7) | mt_rand(0, 128); + $uuid['clock_seq_low'] = mt_rand(0, 255); + + /* + * Node should be set to the 48-bit IEEE node identifier, but + * we leave it for the user to supply the node. + */ + for ($i = 0; $i < 6; $i++) + $uuid['node'][$i] = ord(substr($node, $i, 1)); + + return ($uuid); + } + + /* Assumes correct byte order */ + static private function conv_field2byte($src) { + $uuid[0] = ($src['time_low'] & 0xff000000) >> 24; + $uuid[1] = ($src['time_low'] & 0x00ff0000) >> 16; + $uuid[2] = ($src['time_low'] & 0x0000ff00) >> 8; + $uuid[3] = ($src['time_low'] & 0x000000ff); + $uuid[4] = ($src['time_mid'] & 0xff00) >> 8; + $uuid[5] = ($src['time_mid'] & 0x00ff); + $uuid[6] = ($src['time_hi'] & 0xff00) >> 8; + $uuid[7] = ($src['time_hi'] & 0x00ff); + $uuid[8] = $src['clock_seq_hi']; + $uuid[9] = $src['clock_seq_low']; + + for ($i = 0; $i < 6; $i++) + $uuid[10+$i] = $src['node'][$i]; + + return ($uuid); + } + + static private function conv_field2string($src) { + $str = sprintf( + '%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x', + ($src['time_low']), ($src['time_mid']), ($src['time_hi']), + $src['clock_seq_hi'], $src['clock_seq_low'], + $src['node'][0], $src['node'][1], $src['node'][2], + $src['node'][3], $src['node'][4], $src['node'][5]); + return ($str); + } + + static private function conv_field2binary($src) { + $byte = self::conv_field2byte($src); + return self::conv_byte2binary($byte); + } + + static private function conv_byte2field($uuid) { + $field = self::$m_uuid_field; + $field['time_low'] = ($uuid[0] << 24) | ($uuid[1] << 16) | + ($uuid[2] << 8) | $uuid[3]; + $field['time_mid'] = ($uuid[4] << 8) | $uuid[5]; + $field['time_hi'] = ($uuid[6] << 8) | $uuid[7]; + $field['clock_seq_hi'] = $uuid[8]; + $field['clock_seq_low'] = $uuid[9]; + + for ($i = 0; $i < 6; $i++) + $field['node'][$i] = $uuid[10+$i]; + return ($field); + } + + static public function conv_byte2string($src) { + $field = self::conv_byte2field($src); + return self::conv_field2string($field); + } + + static private function conv_byte2binary($src) { + $raw = pack('C16', $src[0], $src[1], $src[2], $src[3], + $src[4], $src[5], $src[6], $src[7], $src[8], $src[9], + $src[10], $src[11], $src[12], $src[13], $src[14], $src[15]); + return ($raw); + } + + static private function conv_string2field($src) { + $parts = sscanf($src, '%x-%x-%x-%x-%02x%02x%02x%02x%02x%02x'); + $field = self::$m_uuid_field; + $field['time_low'] = ($parts[0]); + $field['time_mid'] = ($parts[1]); + $field['time_hi'] = ($parts[2]); + $field['clock_seq_hi'] = ($parts[3] & 0xff00) >> 8; + $field['clock_seq_low'] = $parts[3] & 0x00ff; + for ($i = 0; $i < 6; $i++) + $field['node'][$i] = $parts[4+$i]; + + return ($field); + } + + static private function conv_string2byte($src) { + $field = self::conv_string2field($src); + return self::conv_field2byte($field); + } + + static private function conv_string2binary($src) { + $byte = self::conv_string2byte($src); + return self::conv_byte2binary($byte); + } +} + +?>
\ No newline at end of file diff --git a/src/etc/inc/voucher.inc b/src/etc/inc/voucher.inc new file mode 100644 index 0000000..7075fa6 --- /dev/null +++ b/src/etc/inc/voucher.inc @@ -0,0 +1,785 @@ +<?php +/* + voucher.inc + Copyright (C) 2010-2012 Ermal Luçi <eri@pfsense.org> + Copyright (C) 2010 Scott Ullrich <sullrich@gmail.com> + Copyright (C) 2007 Marcel Wiget <mwiget@mac.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* + pfSense_BUILDER_BINARIES: /usr/local/bin/voucher + pfSense_MODULE: captiveportal +*/ + +/* include all configuration functions */ +if (!function_exists('captiveportal_syslog')) { + require_once("captiveportal.inc"); +} + +function xmlrpc_sync_voucher_expire($vouchers, $syncip, $port, $password, $username) { + global $g, $config, $cpzone; + require_once("xmlrpc.inc"); + + $protocol = "http"; + if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) && + $config['system']['webgui']['protocol'] == "https") { + $protocol = "https"; + } + if ($protocol == "https" || $port == "443") { + $url = "https://{$syncip}"; + } else { + $url = "http://{$syncip}"; + } + + /* Construct code that is run on remote machine */ + $method = 'pfsense.exec_php'; + $execcmd = <<<EOF + global \$cpzone; + require_once('/etc/inc/captiveportal.inc'); + require_once('/etc/inc/voucher.inc'); + \$cpzone = "$cpzone"; + voucher_expire("$vouchers"); + +EOF; + + /* assemble xmlrpc payload */ + $params = array( + XML_RPC_encode($password), + XML_RPC_encode($execcmd) + ); + + log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}."); + $msg = new XML_RPC_Message($method, $params); + $cli = new XML_RPC_Client('/xmlrpc.php', $url, $port); + $cli->setCredentials($username, $password); + $resp = $cli->send($msg, "250"); + if (!is_object($resp)) { + $error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php)."; + log_error($error); + file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", ""); + return false; + } elseif ($resp->faultCode()) { + $error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString(); + log_error($error); + file_notice("CaptivePortalVoucherSync", $error, "Error code received", ""); + return false; + } else { + log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php)."); + } + + $toreturn = XML_RPC_Decode($resp->value()); + + return $toreturn; +} + +function xmlrpc_sync_voucher_disconnect($dbent, $syncip, $port, $password, $username, $term_cause = 1, $stop_time = null) { + global $g, $config, $cpzone; + require_once("xmlrpc.inc"); + + $protocol = "http"; + if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) && + $config['system']['webgui']['protocol'] == "https") { + $protocol = "https"; + } + if ($protocol == "https" || $port == "443") { + $url = "https://{$syncip}"; + } else { + $url = "http://{$syncip}"; + } + + /* Construct code that is run on remote machine */ + $dbent_str = serialize($dbent); + $tmp_stop_time = (isset($stop_time)) ? $stop_time : "null"; + $method = 'pfsense.exec_php'; + $execcmd = <<<EOF + global \$cpzone; + require_once('/etc/inc/captiveportal.inc'); + require_once('/etc/inc/voucher.inc'); + \$cpzone = "$cpzone"; + \$radiusservers = captiveportal_get_radius_servers(); + \$dbent = unserialize("$dbent_str"); + captiveportal_disconnect(\$dbent, \$radiusservers, $term_cause, $tmp_stop_time); + +EOF; + + /* assemble xmlrpc payload */ + $params = array( + XML_RPC_encode($password), + XML_RPC_encode($execcmd) + ); + + log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}."); + $msg = new XML_RPC_Message($method, $params); + $cli = new XML_RPC_Client('/xmlrpc.php', $url, $port); + $cli->setCredentials($username, $password); + $resp = $cli->send($msg, "250"); + if (!is_object($resp)) { + $error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php)."; + log_error($error); + file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", ""); + return false; + } elseif ($resp->faultCode()) { + $error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString(); + log_error($error); + file_notice("CaptivePortalVoucherSync", $error, "Error code received", ""); + return false; + } else { + log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php)."); + } + + $toreturn = XML_RPC_Decode($resp->value()); + + return $toreturn; +} + +function xmlrpc_sync_used_voucher($voucher_received, $syncip, $port, $password, $username) { + global $g, $config, $cpzone; + require_once("xmlrpc.inc"); + + $protocol = "http"; + if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) && + $config['system']['webgui']['protocol'] == "https") { + $protocol = "https"; + } + if ($protocol == "https" || $port == "443") { + $url = "https://{$syncip}"; + } else { + $url = "http://{$syncip}"; + } + + /* Construct code that is run on remote machine */ + $method = 'pfsense.exec_php'; + $execcmd = <<<EOF + global \$cpzone; + require_once('/etc/inc/voucher.inc'); + \$cpzone = "$cpzone"; + \$timeleft = voucher_auth("$voucher_received"); + \$toreturn = array(); + \$toreturn['timeleft'] = \$timeleft; + \$toreturn['voucher'] = array(); + \$toreturn['voucher']['roll'] = \$config['voucher'][\$cpzone]['roll']; + +EOF; + + /* assemble xmlrpc payload */ + $params = array( + XML_RPC_encode($password), + XML_RPC_encode($execcmd) + ); + + log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}."); + $msg = new XML_RPC_Message($method, $params); + $cli = new XML_RPC_Client('/xmlrpc.php', $url, $port); + $cli->setCredentials($username, $password); + $resp = $cli->send($msg, "250"); + if (!is_object($resp)) { + $error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php)."; + log_error($error); + file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", ""); + return null; // $timeleft + } elseif ($resp->faultCode()) { + $error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString(); + log_error($error); + file_notice("CaptivePortalVoucherSync", $error, "Error code received", ""); + return null; // $timeleft + } else { + log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php)."); + } + $toreturn = XML_RPC_Decode($resp->value()); + if (!is_array($config['voucher'])) { + $config['voucher'] = array(); + } + + if (is_array($toreturn['voucher']) && is_array($toreturn['voucher']['roll'])) { + $config['voucher'][$cpzone]['roll'] = $toreturn['voucher']['roll']; + write_config("Captive Portal Voucher database synchronized with {$url}"); + voucher_configure_zone(true); + unset($toreturn['voucher']); + } else if (!isset($toreturn['timeleft'])) { + return null; + } + + return $toreturn['timeleft']; +} + +function voucher_expire($voucher_received) { + global $g, $config, $cpzone, $cpzoneid; + + // XMLRPC Call over to the master Voucher node + if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) { + $syncip = $config['voucher'][$cpzone]['vouchersyncdbip']; + $syncport = $config['voucher'][$cpzone]['vouchersyncport']; + $syncpass = $config['voucher'][$cpzone]['vouchersyncpass']; + $vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername']; + xmlrpc_sync_voucher_expire($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername); + } + + $voucherlck = lock("voucher{$cpzone}", LOCK_EX); + + // read rolls into assoc array with rollid as key and minutes as value + $tickets_per_roll = array(); + $minutes_per_roll = array(); + if (is_array($config['voucher'][$cpzone]['roll'])) { + foreach ($config['voucher'][$cpzone]['roll'] as $rollent) { + $tickets_per_roll[$rollent['number']] = $rollent['count']; + $minutes_per_roll[$rollent['number']] = $rollent['minutes']; + } + } + + // split into an array. Useful for multiple vouchers given + $a_vouchers_received = preg_split("/[\t\n\r ]+/s", $voucher_received); + $active_dirty = false; + $unsetindexes = array(); + + // go through all received vouchers, check their valid and extract + // Roll# and Ticket# using the external readvoucher binary + foreach ($a_vouchers_received as $voucher) { + $v = escapeshellarg($voucher); + if (strlen($voucher) < 3) { + continue; // seems too short to be a voucher! + } + + unset($output); + $_gb = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher_{$cpzone}.cfg -k {$g['varetc_path']}/voucher_{$cpzone}.public -- $v", $output); + list($status, $roll, $nr) = explode(" ", $output[0]); + if ($status == "OK") { + // check if we have this ticket on a registered roll for this ticket + if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) { + // voucher is from a registered roll. + if (!isset($active_vouchers[$roll])) { + $active_vouchers[$roll] = voucher_read_active_db($roll); + } + // valid voucher. Store roll# and ticket# + if (!empty($active_vouchers[$roll][$voucher])) { + $active_dirty = true; + unset($active_vouchers[$roll][$voucher]); + } + // check if voucher already marked as used + if (!isset($bitstring[$roll])) { + $bitstring[$roll] = voucher_read_used_db($roll); + } + $pos = $nr >> 3; // divide by 8 -> octet + $mask = 1 << ($nr % 8); + // mark bit for this voucher as used + if (!(ord($bitstring[$roll][$pos]) & $mask)) { + $bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask); + } + captiveportal_syslog("{$voucher} ({$roll}/{$nr}) forced to expire"); + + /* Check if this voucher has any active sessions */ + $cpentry = captiveportal_read_db("WHERE username = '{$voucher}'"); + if (!empty($cpentry) && !empty($cpentry[0])) { + if (empty($cpzoneid) && !empty($config['captiveportal'][$cpzone])) { + $cpzoneid = $config['captiveportal'][$cpzone]['zoneid']; + } + $cpentry = $cpentry[0]; + captiveportal_disconnect($cpentry, null, 13); + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "FORCLY TERMINATING VOUCHER {$voucher} SESSION"); + $unsetindexes[] = $cpentry[5]; + } + } else { + captiveportal_syslog("$voucher ($roll/$nr): not found on any registered Roll"); + } + } else { + // hmm, thats weird ... not what I expected + captiveportal_syslog("$voucher invalid: {$output[0]}!!"); + } + } + + // Refresh active DBs + if ($active_dirty == true) { + foreach ($active_vouchers as $roll => $active) { + voucher_write_active_db($roll, $active); + } + unset($active_vouchers); + + /* Trigger a sync of the vouchers on config */ + send_event("service sync vouchers"); + } + + // Write back the used DB's + if (is_array($bitstring)) { + foreach ($bitstring as $roll => $used) { + if (is_array($used)) { + foreach ($used as $u) { + voucher_write_used_db($roll, base64_encode($u)); + } + } else { + voucher_write_used_db($roll, base64_encode($used)); + } + } + unset($bitstring); + } + + unlock($voucherlck); + + /* Write database */ + if (!empty($unsetindexes)) { + captiveportal_remove_entries($unsetindexes); + } + + return true; +} + +/* + * Authenticate a voucher and return the remaining time credit in minutes + * if $test is set, don't mark the voucher as used nor add it to the list + * of active vouchers + * If $test is set, simply test the voucher. Don't change anything + * but return a more verbose error and result message back + */ +function voucher_auth($voucher_received, $test = 0) { + global $g, $config, $cpzone, $dbc; + + if (!isset($config['voucher'][$cpzone]['enable'])) { + return 0; + } + + // XMLRPC Call over to the master Voucher node + if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) { + $syncip = $config['voucher'][$cpzone]['vouchersyncdbip']; + $syncport = $config['voucher'][$cpzone]['vouchersyncport']; + $syncpass = $config['voucher'][$cpzone]['vouchersyncpass']; + $vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername']; + $remote_time_used = xmlrpc_sync_used_voucher($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername); + } + + $voucherlck = lock("voucher{$cpzone}", LOCK_EX); + + // read rolls into assoc array with rollid as key and minutes as value + $tickets_per_roll = array(); + $minutes_per_roll = array(); + if (is_array($config['voucher'][$cpzone]['roll'])) { + foreach ($config['voucher'][$cpzone]['roll'] as $rollent) { + $tickets_per_roll[$rollent['number']] = $rollent['count']; + $minutes_per_roll[$rollent['number']] = $rollent['minutes']; + } + } + + // split into an array. Useful for multiple vouchers given + $a_vouchers_received = preg_split("/[\t\n\r ]+/s", $voucher_received); + $error = 0; + $test_result = array(); // used to display for voucher test option in GUI + $total_minutes = 0; + $first_voucher = ""; + $first_voucher_roll = 0; + + // go through all received vouchers, check their valid and extract + // Roll# and Ticket# using the external readvoucher binary + foreach ($a_vouchers_received as $voucher) { + $v = escapeshellarg($voucher); + if (strlen($voucher) < 3) { + $test_result[] = "{$voucher} invalid: Too short!"; + captiveportal_syslog("{$voucher} invalid: Too short!"); + $error++; + continue; // seems too short to be a voucher! + } + + $result = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher_{$cpzone}.cfg -k {$g['varetc_path']}/voucher_{$cpzone}.public -- $v"); + list($status, $roll, $nr) = explode(" ", $result); + if ($status == "OK") { + if (!$first_voucher) { + // store first voucher. Thats the one we give the timecredit + $first_voucher = $voucher; + $first_voucher_roll = $roll; + } + // check if we have this ticket on a registered roll for this ticket + if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) { + // voucher is from a registered roll. + if (!isset($active_vouchers[$roll])) { + $active_vouchers[$roll] = voucher_read_active_db($roll); + } + // valid voucher. Store roll# and ticket# + if (!empty($active_vouchers[$roll][$voucher])) { + list($timestamp, $minutes) = explode(",", $active_vouchers[$roll][$voucher]); + // we have an already active voucher here. + $remaining = intval((($timestamp + (60*$minutes)) - time())/60); + $test_result[] = sprintf(gettext('%1$s (%2$s/%3$s) active and good for %4$d Minutes'), $voucher, $roll, $nr, $remaining); + $total_minutes += $remaining; + } else { + // voucher not used. Check if ticket Id is on the roll (not too high) + // and if the ticket is marked used. + // check if voucher already marked as used + if (!isset($bitstring[$roll])) { + $bitstring[$roll] = voucher_read_used_db($roll); + } + $pos = $nr >> 3; // divide by 8 -> octet + $mask = 1 << ($nr % 8); + if (ord($bitstring[$roll][$pos]) & $mask) { + $test_result[] = "$voucher ($roll/$nr) already used and expired"; + captiveportal_syslog("$voucher ($roll/$nr) already used and expired"); + $total_minutes = -1; // voucher expired + $error++; + } else { + // mark bit for this voucher as used + $bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask); + $test_result[] = "$voucher ($roll/$nr) good for {$minutes_per_roll[$roll]} Minutes"; + $total_minutes += $minutes_per_roll[$roll]; + } + } + } else { + $test_result[] = "$voucher ($roll/$nr): not found on any registered Roll"; + captiveportal_syslog("$voucher ($roll/$nr): not found on any registered Roll"); + } + } else { + // hmm, thats weird ... not what I expected + $test_result[] = "$voucher invalid: $result !!"; + captiveportal_syslog("$voucher invalid: $result !!"); + $error++; + } + } + + // if this was a test call, we're done. Return the result. + if ($test) { + if ($error) { + $test_result[] = gettext("Access denied!"); + } else { + $test_result[] = sprintf(gettext("Access granted for %d Minutes in total."), $total_minutes); + } + unlock($voucherlck); + + return $test_result; + } + + // if we had an error (one of the vouchers is invalid), return 0. + // Discussion: we could return the time remaining for good vouchers, but then + // the user wouldn't know that he used at least one invalid voucher. + if ($error) { + unlock($voucherlck); + if ($total_minutes > 0) { // probably not needed, but want to make sure + $total_minutes = 0; // we only report -1 (expired) or 0 (no access) + } + return $total_minutes; // well, at least one voucher had errors. Say NO ACCESS + } + + // If we did a XMLRPC sync earlier check the timeleft + if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) { + if (!is_null($remote_time_used)) { + $total_minutes = $remote_time_used; + } else if ($remote_time_used < $total_minutes) { + $total_minutes -= $remote_time_used; + } + } + + // All given vouchers were valid and this isn't simply a test. + // Write back the used DB's + if (is_array($bitstring)) { + foreach ($bitstring as $roll => $used) { + if (is_array($used)) { + foreach ($used as $u) { + voucher_write_used_db($roll, base64_encode($u)); + } + } else { + voucher_write_used_db($roll, base64_encode($used)); + } + } + } + + // Active DB: we only add the first voucher if multiple given + // and give that one all the time credit. This allows the user to logout and + // log in later using just the first voucher. It also keeps username limited + // to one voucher and that voucher shows the correct time credit in 'active vouchers' + if (!empty($active_vouchers[$first_voucher_roll][$first_voucher])) { + list($timestamp, $minutes) = explode(",", $active_vouchers[$first_voucher_roll][$first_voucher]); + } else { + $timestamp = time(); // new voucher + $minutes = $total_minutes; + } + + $active_vouchers[$first_voucher_roll][$first_voucher] = "$timestamp,$minutes"; + voucher_write_active_db($first_voucher_roll, $active_vouchers[$first_voucher_roll]); + + /* Trigger a sync of the vouchers on config */ + send_event("service sync vouchers"); + + unlock($voucherlck); + + return $total_minutes; +} + +function voucher_configure($sync = false) { + global $config, $g, $cpzone; + + if (is_array($config['voucher'])) { + foreach ($config['voucher'] as $voucherzone => $vcfg) { + if (platform_booting()) { + echo gettext("Enabling voucher support... "); + } + $cpzone = $voucherzone; + $error = voucher_configure_zone($sync); + if (platform_booting()) { + if ($error) { + echo "error\n"; + } else { + echo "done\n"; + } + } + } + } +} + +function voucher_configure_zone($sync = false) { + global $config, $g, $cpzone; + + if (!isset($config['voucher'][$cpzone]['enable'])) { + return 0; + } + + if ($sync == true) { + captiveportal_syslog("Writing voucher db from sync data..."); + } + + $voucherlck = lock("voucher{$cpzone}", LOCK_EX); + + /* write public key used to verify vouchers */ + $pubkey = base64_decode($config['voucher'][$cpzone]['publickey']); + $fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.public", "w"); + if (!$fd) { + captiveportal_syslog("Voucher error: cannot write voucher.public\n"); + unlock($voucherlck); + return 1; + } + fwrite($fd, $pubkey); + fclose($fd); + @chmod("{$g['varetc_path']}/voucher_{$cpzone}.public", 0600); + + /* write config file used by voucher binary to decode vouchers */ + $fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.cfg", "w"); + if (!$fd) { + printf(gettext("Error: cannot write voucher.cfg") . "\n"); + unlock($voucherlck); + return 1; + } + fwrite($fd, "{$config['voucher'][$cpzone]['rollbits']},{$config['voucher'][$cpzone]['ticketbits']},{$config['voucher'][$cpzone]['checksumbits']},{$config['voucher'][$cpzone]['magic']},{$config['voucher'][$cpzone]['charset']}\n"); + fclose($fd); + @chmod("{$g['varetc_path']}/voucher_{$cpzone}.cfg", 0600); + unlock($voucherlck); + + if ((platform_booting() || $sync == true) && is_array($config['voucher'][$cpzone]['roll'])) { + + $voucherlck = lock("voucher{$cpzone}", LOCK_EX); + + // create active and used DB per roll on ramdisk from config + foreach ($config['voucher'][$cpzone]['roll'] as $rollent) { + + $roll = $rollent['number']; + voucher_write_used_db($roll, $rollent['used']); + $minutes = $rollent['minutes']; + $active_vouchers = array(); + $a_active = &$rollent['active']; + if (is_array($a_active)) { + foreach ($a_active as $activent) { + $voucher = $activent['voucher']; + $timestamp = $activent['timestamp']; + $minutes = $activent['minutes']; + // its tempting to check for expired timestamps, but during + // bootup, we most likely don't have the correct time. + $active_vouchers[$voucher] = "$timestamp,$minutes"; + } + } + voucher_write_active_db($roll, $active_vouchers); + } + + unlock($voucherlck); + } + + return 0; +} + +/* write bitstring of used vouchers to ramdisk. + * Bitstring must already be base64_encoded! + */ +function voucher_write_used_db($roll, $vdb) { + global $g, $cpzone; + + $fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db", "w"); + if ($fd) { + fwrite($fd, $vdb . "\n"); + fclose($fd); + } else { + voucher_log(LOG_ERR, sprintf(gettext('cant write %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll)); + } +} + +/* return assoc array of active vouchers with activation timestamp + * voucher is index. + */ +function voucher_read_active_db($roll) { + global $g, $cpzone; + + $active = array(); + $dirty = 0; + $file = "{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db"; + if (file_exists($file)) { + $fd = fopen($file, "r"); + if ($fd) { + while (!feof($fd)) { + $line = trim(fgets($fd)); + if ($line) { + list($voucher, $timestamp, $minutes) = explode(",", $line); // voucher,timestamp + if ((($timestamp + (60*$minutes)) - time()) > 0) { + $active[$voucher] = "$timestamp,$minutes"; + } else { + $dirty=1; + } + } + } + fclose($fd); + if ($dirty) { // if we found expired entries, lets save our snapshot + voucher_write_active_db($roll, $active); + + /* Trigger a sync of the vouchers on config */ + send_event("service sync vouchers"); + } + } + } + return $active; +} + +/* store array of active vouchers back to DB */ +function voucher_write_active_db($roll, $active) { + global $g, $cpzone; + + if (!is_array($active)) { + return; + } + $fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db", "w"); + if ($fd) { + foreach ($active as $voucher => $value) { + fwrite($fd, "$voucher,$value\n"); + } + fclose($fd); + } +} + +/* return how many vouchers are marked used on a roll */ +function voucher_used_count($roll) { + global $g, $cpzone; + + $bitstring = voucher_read_used_db($roll); + $max = strlen($bitstring) * 8; + $used = 0; + for ($i = 1; $i <= $max; $i++) { + // check if ticket already used or not. + $pos = $i >> 3; // divide by 8 -> octet + $mask = 1 << ($i % 8); // mask to test bit in octet + if (ord($bitstring[$pos]) & $mask) { + $used++; + } + } + unset($bitstring); + + return $used; +} + +function voucher_read_used_db($roll) { + global $g, $cpzone; + + $vdb = ""; + $file = "{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db"; + if (file_exists($file)) { + $fd = fopen($file, "r"); + if ($fd) { + $vdb = trim(fgets($fd)); + fclose($fd); + } else { + voucher_log(LOG_ERR, sprintf(gettext('cant read %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll)); + } + } + return base64_decode($vdb); +} + +function voucher_unlink_db($roll) { + global $g, $cpzone; + @unlink("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db"); + @unlink("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db"); +} + +/* we share the log with captiveportal for now */ +function voucher_log($priority, $message) { + + $message = trim($message); + openlog("logportalauth", LOG_PID, LOG_LOCAL4); + syslog($priority, sprintf(gettext("Voucher: %s"), $message)); + closelog(); +} + +/* Save active and used voucher DB into XML config and write it to flash + * Called during reboot -> system_reboot_cleanup() and every active voucher change + */ +function voucher_save_db_to_config() { + global $config, $g, $cpzone; + + if (is_array($config['voucher'])) { + foreach ($config['voucher'] as $voucherzone => $vcfg) { + $cpzone = $voucherzone; + voucher_save_db_to_config_zone(); + } + } +} + +function voucher_save_db_to_config_zone() { + global $config, $g, $cpzone; + + if (!isset($config['voucher'][$cpzone]['enable'])) { + return; // no vouchers or don't want to save DB's + } + + if (!is_array($config['voucher'][$cpzone]['roll'])) { + return; + } + + $voucherlck = lock("voucher{$cpzone}", LOCK_EX); + + // walk all active rolls and save runtime DB's to flash + $a_roll = &$config['voucher'][$cpzone]['roll']; + while (list($key, $value) = each($a_roll)) { + $rollent = &$a_roll[$key]; + $roll = $rollent['number']; + $bitmask = voucher_read_used_db($roll); + $rollent['used'] = base64_encode($bitmask); + $active_vouchers = voucher_read_active_db($roll); + $db = array(); + $dbi = 1; + foreach ($active_vouchers as $voucher => $line) { + list($timestamp, $minutes) = explode(",", $line); + $activent['voucher'] = $voucher; + $activent['timestamp'] = $timestamp; + $activent['minutes'] = $minutes; + $db["v{$dbi}"] = $activent; + $dbi++; + } + $rollent['active'] = $db; + unset($active_vouchers); + } + + unlock($voucherlck); + + write_config("Syncing vouchers"); + return; +} + +?> diff --git a/src/etc/inc/vpn.inc b/src/etc/inc/vpn.inc new file mode 100644 index 0000000..2820822 --- /dev/null +++ b/src/etc/inc/vpn.inc @@ -0,0 +1,2056 @@ +<?php + +/* + vpn.inc + Copyright (C) 2004 Scott Ullrich + Copyright (C) 2008 Shrew Soft Inc + Copyright (C) 2008 Ermal Luçi + All rights reserved. + + originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_BUILDER_BINARIES: /sbin/ifconfig + pfSense_BUILDER_BINARIES: /usr/local/sbin/ipsec /usr/local/libexec/ipsec/charon /usr/local/libexec/ipsec/starter + pfSense_BUILDER_BINARIES: /usr/local/sbin/filterdns /usr/local/sbin/mpd4 + pfSense_MODULE: vpn +*/ + +require_once("ipsec.inc"); +require_once("filter.inc"); + +function vpn_ipsec_configure_loglevels($forconfig = false) { + global $config, $ipsec_loglevels; + + $cfgtext = array(); + foreach ($ipsec_loglevels as $lkey => $ldescr) { + if (!isset($config['ipsec']["ipsec_{$lkey}"]) && !$forconfig) { + mwexec("/usr/local/sbin/ipsec stroke loglevel {$lkey} -- -1", false); + } else if (is_numeric($config['ipsec']["ipsec_{$lkey}"]) && + intval($config['ipsec']["ipsec_{$lkey}"]) >= 0 && intval($config['ipsec']["ipsec_{$lkey}"]) <= 5) { + $forconfig ? $cfgtext[] = "${lkey} " . (intval($config['ipsec']["ipsec_{$lkey}"]) - 1) : + mwexec("/usr/local/sbin/ipsec stroke loglevel {$lkey} " . (intval($config['ipsec']["ipsec_{$lkey}"]) - 1) , false); + } + } + if ($forconfig) { + return implode(',', $cfgtext); + } +} + +/* include all configuration functions */ +function vpn_ipsec_convert_to_modp($index) { + + $convertion = ""; + switch ($index) { + case '1': + $convertion = "modp768"; + break; + case '2': + $convertion = "modp1024"; + break; + case '5': + $convertion = "modp1536"; + break; + case '14': + $convertion = "modp2048"; + break; + case '15': + $convertion = "modp3072"; + break; + case '16': + $convertion = "modp4096"; + break; + case '17': + $convertion = "modp6144"; + break; + case '18': + $convertion = "modp8192"; + break; + case '19': + $convertion = "ecp256"; + break; + case '20': + $convertion = "ecp384"; + break; + case '21': + $convertion = "ecp521"; + break; + case '28': + $convertion = "ecp256bp"; + break; + case '29': + $convertion = "ecp384bp"; + break; + case '30': + $convertion = "ecp512bp"; + break; + } + + return $convertion; +} + +function vpn_ipsec_configure($restart = false) { + global $config, $g, $sa, $sn, $p1_ealgos, $p2_ealgos, $ipsec_idhandling; + + /* get the automatic ping_hosts.sh ready */ + unlink_if_exists("{$g['vardb_path']}/ipsecpinghosts"); + touch("{$g['vardb_path']}/ipsecpinghosts"); + + /* service may have been enabled, disabled, or otherwise changed in a way requiring rule updates */ + filter_configure(); + + $syscfg = $config['system']; + $ipseccfg = $config['ipsec']; + if (!isset($ipseccfg['enable'])) { + /* try to stop charon */ + mwexec("/usr/local/sbin/ipsec stop"); + /* Stop dynamic monitoring */ + killbypid("{$g['varrun_path']}/filterdns-ipsec.pid"); + + /* wait for process to die */ + sleep(2); + + /* disallow IPSEC, it is off */ + mwexec("/sbin/ifconfig enc0 down"); + set_single_sysctl("net.inet.ip.ipsec_in_use", "0"); + + return 0; + } + + $a_phase1 = $config['ipsec']['phase1']; + $a_phase2 = $config['ipsec']['phase2']; + $a_client = $config['ipsec']['client']; + + $certpath = "{$g['varetc_path']}/ipsec/ipsec.d/certs"; + $capath = "{$g['varetc_path']}/ipsec/ipsec.d/cacerts"; + $keypath = "{$g['varetc_path']}/ipsec/ipsec.d/private"; + $crlpath = "{$g['varetc_path']}/ipsec/ipsec.d/crls"; + + mwexec("/sbin/ifconfig enc0 up"); + set_single_sysctl("net.inet.ip.ipsec_in_use", "1"); + if (php_uname('m') != "amd64") { + set_single_sysctl("net.inet.ipsec.directdispatch", "0"); + } + + /* needed for config files */ + if (!is_dir("{$g['varetc_path']}/ipsec")) { + mkdir("{$g['varetc_path']}/ipsec"); + } + if (!is_dir("{$g['varetc_path']}/ipsec/ipsec.d")) { + mkdir("{$g['varetc_path']}/ipsec/ipsec.d"); + } + if (!is_dir($capath)) { + mkdir($capath); + } + if (!is_dir($keypath)) { + mkdir($keypath); + } + if (!is_dir($crlpath)) { + mkdir($crlpath); + } + if (!is_dir($certpath)) { + mkdir($certpath); + } + if (!is_dir("{$g['varetc_path']}/ipsec/ipsec.d/aacerts")) { + mkdir("{$g['varetc_path']}/ipsec/ipsec.d/aacerts"); + } + if (!is_dir("{$g['varetc_path']}/ipsec/ipsec.d/acerts")) { + mkdir("{$g['varetc_path']}/ipsec/ipsec.d/acerts"); + } + if (!is_dir("{$g['varetc_path']}/ipsec/ipsec.d/ocspcerts")) { + mkdir("{$g['varetc_path']}/ipsec/ipsec.d/ocspcerts"); + } + if (!is_dir("{$g['varetc_path']}/ipsec/ipsec.d/reqs")) { + mkdir("{$g['varetc_path']}/ipsec/ipsec.d/reqs"); + } + + + if (platform_booting()) { + echo gettext("Configuring IPsec VPN... "); + } + + /* fastforwarding is not compatible with ipsec tunnels */ + set_single_sysctl("net.inet.ip.fastforwarding", "0"); + + /* resolve all local, peer addresses and setup pings */ + $ipmap = array(); + $rgmap = array(); + $filterdns_list = array(); + $listeniflist = array(); + $aggressive_mode_psk = false; + unset($iflist); + $ifacesuse = array(); + if (is_array($a_phase1) && count($a_phase1)) { + + $ipsecpinghosts = ""; + /* step through each phase1 entry */ + foreach ($a_phase1 as $ph1ent) { + if (isset($ph1ent['disabled'])) { + continue; + } + + if (strpos($ph1ent['interface'], '_vip')) { + $vpninterface = explode('_vip', $ph1ent['interface']); + $ifacesuse[] = get_real_interface($vpninterface[0]); + } else { + $vpninterface = get_failover_interface($ph1ent['interface']); + if (strpos($vpninterface, '_vip')) { + $vpninterface = explode('_vip', $vpninterface); + $ifacesuse[] = get_real_interface($vpninterface[0]); + } elseif (!empty($vpninterface)) { + $ifacesuse[] = $vpninterface; + } + } + + if ($ph1ent['mode'] == "aggressive" && ($ph1ent['authentication_method'] == "pre_shared_key" || $ph1ent['authentication_method'] == "xauth_psk_server")) { + $aggressive_mode_psk = true; + } + + $ikeid = $ph1ent['ikeid']; + $listeniflist = get_real_interface($a_phase1['interface']); + + $ep = ipsec_get_phase1_src($ph1ent); + if (!is_ipaddr($ep)) { + log_error("IPsec ERROR: Could not find phase 1 source for connection {$ph1ent['descr']}. Omitting from configuration file."); + continue; + } + + if (!in_array($ep, $ipmap)) { + $ipmap[] = $ep; + } + + /* see if this tunnel has a hostname for the remote-gateway. If so, + try to resolve it now and add it to the list for filterdns */ + + if (isset ($ph1ent['mobile'])) { + continue; + } + + $rg = $ph1ent['remote-gateway']; + + if (!is_ipaddr($rg)) { + $filterdns_list[] = "{$rg}"; + add_hostname_to_watch($rg); + if (!platform_booting()) { + $rg = resolve_retry($rg); + } + if (!is_ipaddr($rg)) { + continue; + } + } + if (array_search($rg, $rgmap)) { + log_error("The remote gateway {$rg} already exists on another phase 1 entry"); + continue; + } + $rgmap[$ph1ent['remote-gateway']] = $rg; + + if (is_array($a_phase2)) { + /* step through each phase2 entry */ + foreach ($a_phase2 as $ph2ent) { + if (isset($ph2ent['disabled'])) { + continue; + } + + if ($ikeid != $ph2ent['ikeid']) { + continue; + } + + /* add an ipsec pinghosts entry */ + if ($ph2ent['pinghost']) { + if (!is_array($iflist)) { + $iflist = get_configured_interface_list(); + } + $srcip = null; + $local_subnet = ipsec_idinfo_to_cidr($ph2ent['localid'], true, $ph2ent['mode']); + if (is_ipaddrv6($ph2ent['pinghost'])) { + foreach ($iflist as $ifent => $ifname) { + $interface_ip = get_interface_ipv6($ifent); + if (!is_ipaddrv6($interface_ip)) { + continue; + } + if (ip_in_subnet($interface_ip, $local_subnet)) { + $srcip = $interface_ip; + break; + } + } + } else { + foreach ($iflist as $ifent => $ifname) { + $interface_ip = get_interface_ip($ifent); + if (!is_ipaddrv4($interface_ip)) { + continue; + } + if ($local_subnet == "0.0.0.0/0" || ip_in_subnet($interface_ip, $local_subnet)) { + $srcip = $interface_ip; + break; + } + } + } + /* if no valid src IP was found in configured interfaces, try the vips */ + if (is_null($srcip)) { + $viplist = get_configured_vips_list(); + foreach ($viplist as $vip) { + if (ip_in_subnet($vip['ipaddr'], $local_subnet)) { + $srcip = $vip['ipaddr']; + break; + } + } + } + $dstip = $ph2ent['pinghost']; + if (is_ipaddrv6($dstip)) { + $family = "inet6"; + } else { + $family = "inet"; + } + if (is_ipaddr($srcip)) { + $ipsecpinghosts[] = "{$srcip}|{$dstip}|3|||||{$family}|\n"; + } + } + } + } + } + @file_put_contents("{$g['vardb_path']}/ipsecpinghosts", $ipsecpinghosts); + unset($ipsecpinghosts); + } + unset($iflist); + + $accept_unencrypted = ""; + if (isset($config['ipsec']['acceptunencryptedmainmode'])) { + $accept_unencrypted = "accept_unencrypted_mainmode_messages = yes"; + } + + $stronconf = ''; + if (file_exists("{$g['varetc_path']}/ipsec/strongswan.conf")) { + $stronconf = file_get_contents("{$g['varetc_path']}/ipsec/strongswan.conf"); + } + + $i_dont_care_about_security_and_use_aggressive_mode_psk = ""; + if ($aggressive_mode_psk) { + log_error("WARNING: Setting i_dont_care_about_security_and_use_aggressive_mode_psk option because a phase 1 is configured using aggressive mode with pre-shared keys. This is not a secure configuration."); + if (!empty($stronconf) && strpos($stronconf, 'i_dont_care_about_security_and_use_aggressive_mode_psk') === FALSE) { + $restart = true; + } + $i_dont_care_about_security_and_use_aggressive_mode_psk = "i_dont_care_about_security_and_use_aggressive_mode_psk=yes"; + } + + $unity_enabled = 'yes'; + if (isset($config['ipsec']['unityplugin'])) { + $unity_enabled = 'no'; + if (file_exists("/usr/local/lib/ipsec/plugins/libstrongswan-unity.so")) { + conf_mount_rw(); + mwexec("mv /usr/local/lib/ipsec/plugins/libstrongswan-unity.so /usr/local/lib/ipsec/plugins/libstrongswan-unity.MOVED"); + conf_mount_ro(); + } + } else if (file_exists("/usr/local/lib/ipsec/plugins/libstrongswan-unity.MOVED")) { + conf_mount_rw(); + mwexec("mv /usr/local/lib/ipsec/plugins/libstrongswan-unity.MOVED /usr/local/lib/ipsec/plugins/libstrongswan-unity.so"); + conf_mount_ro(); + } + + $makebeforebreak = ''; + if (isset($config['ipsec']['makebeforebreak'])) { + $makebeforebreak = 'make_before_break = yes'; + } + + if (isset($config['ipsec']['enableinterfacesuse'])) { + if (!empty($ifacesuse)) { + $ifacesuse = 'interfaces_use = ' . implode(',', array_unique($ifacesuse)); + } else { + $ifacesuse = ''; + } + } else { + $ifacesuse = ''; + } + + unset($stronconf); + + $strongswan = <<<EOD + +# Automatically generated config file - DO NOT MODIFY. Changes will be overwritten. +starter { +load_warning = no +} + +charon { +# number of worker threads in charon +threads = 16 +ikesa_table_size = 32 +ikesa_table_segments = 4 +init_limit_half_open = 1000 +install_routes = no +{$i_dont_care_about_security_and_use_aggressive_mode_psk} +{$accept_unencrypted} +cisco_unity = {$unity_enabled} +{$ifacesuse} +{$makebeforebreak} + +# And two loggers using syslog. The subsections define the facility to log +# to, currently one of: daemon, auth. +syslog { + identifier = charon + # default level to the LOG_DAEMON facility + daemon { + ike_name = yes + } + # very minimalistic IKE auditing logs to LOG_AUTHPRIV + auth { + default = -1 + ike = 1 + ike_name = yes + } +} + +EOD; + + $strongswan .= "\tplugins {\n"; + + $a_servers = auth_get_authserver_list(); + foreach ($a_servers as $id => $pconfig) { + if ($id == $config['ipsec']['client']['user_source'] && $pconfig['type'] == "radius") { + $strongswan .= <<<EOD + eap-radius { + class_group = yes + eap_start = no + servers { + primary { + address = {$pconfig['host']} + secret = {$pconfig['radius_secret']} + auth_port = {$pconfig['radius_auth_port']} + acct_port = {$pconfig['radius_acct_port']} + } + } + } + +EOD; + break; + } + } + + if (is_array($a_client) && isset($a_client['enable'])) { + $strongswan .= "\t\tattr {\n"; + if ($a_client['pool_address'] && $a_client['pool_netbits']) { + $strongswan .= "\t\t\tsubnet = {$a_client['pool_address']}/{$a_client['pool_netbits']}\n"; + } + + $cfgservers = array(); + if (!empty($a_client['dns_server1'])) { + $cfgservers[] = $a_client['dns_server1']; + } + if (!empty($a_client['dns_server2'])) { + $cfgservers[] = $a_client['dns_server2']; + } + if (!empty($a_client['dns_server3'])) { + $cfgservers[] = $a_client['dns_server3']; + } + if (!empty($a_client['dns_server4'])) { + $cfgservers[] = $a_client['dns_server4']; + } + + if (!empty($cfgservers)) { + $strongswan .= "\t\t\tdns = " . implode(",", $cfgservers) . "\n"; + } + unset($cfgservers); + $cfgservers = array(); + if (!empty($a_client['wins_server1'])) { + $cfgservers[] = $a_client['wins_server1']; + } + if (!empty($a_client['wins_server2'])) { + $cfgservers[] = $a_client['wins_server2']; + } + if (!empty($cfgservers)) { + $strongswan .= "\t\t\tnbns = " . implode(",", $cfgservers) . "\n"; + } + unset($cfgservers); + + if (isset($a_client['net_list']) && is_array($a_phase2)) { + $net_list = ''; + foreach ($a_phase2 as $ph2ent) { + if (isset($ph2ent['disabled'])) { + continue; + } + + if (!isset($ph2ent['mobile'])) { + continue; + } + + $localid = ipsec_idinfo_to_cidr($ph2ent['localid'], true, $ph2ent['mode']); + + if (!empty($net_list)) { + $net_list .= ","; + } + $net_list .= $localid; + } + + if (!empty($net_list)) { + $strongswan .= "\t\t\tsplit-include = {$net_list}\n"; + unset($net_list); + } + } + + if (!empty($a_client['dns_domain'])) { + $strongswan .= "\t\t\t# Search domain and default domain\n"; + $strongswan .= "\t\t\t28674 = \"{$a_client['dns_domain']}\"\n"; + if (empty($a_client['dns_split'])) { + $strongswan .= "\t\t\t28675 = \"{$a_client['dns_domain']}\""; + } + $strongswan .= "\n"; + } + + if (!empty($a_client['dns_split'])) { + $strongswan .= "\t\t\t28675 = {$a_client['dns_split']}\n"; + } + + if (!empty($a_client['login_banner'])) { + $strongswan .= "\t\t\t28672 = \"{$a_client['login_banner']}\"\n"; + } + + if (isset($a_client['save_passwd'])) { + $strongswan .= "\t\t\t28673 = 1\n"; + } + + if ($a_client['pfs_group']) { + $strongswan .= "\t\t\t28679 = \"{$a_client['pfs_group']}\"\n"; + } + $strongswan .= "\t\t}\n"; + + if ($a_client['user_source'] != "none") { + $strongswan .= "\t\txauth-generic {\n"; + $strongswan .= "\t\t\tscript = /etc/inc/ipsec.auth-user.php\n"; + $strongswan .= "\t\t\tauthcfg = "; + $firstsed = 0; + $authcfgs = explode(",", $a_client['user_source']); + foreach ($authcfgs as $authcfg) { + if ($firstsed > 0) { + $strongswan .= ","; + } + if ($authcfg == "system") { + $authcfg = "Local Database"; + } + $strongswan .= $authcfg; + $firstsed = 1; + } + $strongswan .= "\n"; + $strongswan .= "\t\t}\n"; + } + } + + $strongswan .= "\t}\n}\n"; + @file_put_contents("{$g['varetc_path']}/ipsec/strongswan.conf", $strongswan); + unset($strongswan); + + /* generate CA certificates files */ + if (is_array($config['ca']) && count($config['ca'])) { + foreach ($config['ca'] as $ca) { + if (!isset($ca['crt'])) { + log_error(sprintf(gettext("Error: Invalid certificate info for %s"), $ca['descr'])); + continue; + } + $cert = base64_decode($ca['crt']); + $x509cert = openssl_x509_parse(openssl_x509_read($cert)); + if (!is_array($x509cert) || !isset($x509cert['hash'])) { + log_error(sprintf(gettext("Error: Invalid certificate hash info for %s"), $ca['descr'])); + continue; + } + $fname = "{$capath}/{$x509cert['hash']}.0.crt"; + if (!@file_put_contents($fname, $cert)) { + log_error(sprintf(gettext("Error: Cannot write IPsec CA file for %s"), $ca['descr'])); + continue; + } + unset($cert); + } + } + + /* write out CRL files */ + if (is_array($config['crl']) && count($config['crl'])) { + foreach ($config['crl'] as $crl) { + if (!isset($crl['text'])) { + log_error(sprintf(gettext("Warning: Missing CRL data for %s"), $crl['descr'])); + continue; + } + $fpath = "{$crlpath}/{$crl['refid']}.crl"; + if (!@file_put_contents($fpath, base64_decode($crl['text']))) { + log_error(sprintf(gettext("Error: Cannot write IPsec CRL file for %s"), $crl['descr'])); + continue; + } + } + } + + $pskconf = ""; + + if (is_array($a_phase1) && count($a_phase1)) { + foreach ($a_phase1 as $ph1ent) { + + if (isset($ph1ent['disabled'])) { + continue; + } + + if (strstr($ph1ent['authentication_method'], 'rsa') || + in_array($ph1ent['authentication_method'], array('eap-mschapv2', 'eap-tls', 'eap-radius'))) { + $certline = ''; + + $ikeid = $ph1ent['ikeid']; + $cert = lookup_cert($ph1ent['certref']); + + if (!$cert) { + log_error(sprintf(gettext("Error: Invalid phase1 certificate reference for %s"), $ph1ent['name'])); + continue; + } + + @chmod($certpath, 0600); + + $ph1keyfile = "{$keypath}/cert-{$ikeid}.key"; + if (!file_put_contents($ph1keyfile, base64_decode($cert['prv']))) { + log_error(sprintf(gettext("Error: Cannot write phase1 key file for %s"), $ph1ent['name'])); + continue; + } + @chmod($ph1keyfile, 0600); + + $ph1certfile = "{$certpath}/cert-{$ikeid}.crt"; + if (!file_put_contents($ph1certfile, base64_decode($cert['crt']))) { + log_error(sprintf(gettext("Error: Cannot write phase1 certificate file for %s"), $ph1ent['name'])); + @unlink($ph1keyfile); + continue; + } + @chmod($ph1certfile, 0600); + + /* XXX" Traffic selectors? */ + $pskconf .= " : RSA {$ph1keyfile}\n"; + } else { + list ($myid_type, $myid_data) = ipsec_find_id($ph1ent, 'local'); + list ($peerid_type, $peerid_data) = ipsec_find_id($ph1ent, 'peer', $rgmap); + + $myid = trim($myid_data); + + if (empty($peerid_data)) { + continue; + } + + if ($myid_type == 'fqdn' && !empty($myid)) { + $myid = "@{$myid}"; + } + + $myid = isset($ph1ent['mobile']) ? trim($myid_data) : "%any"; + + $peerid = ($peerid_data != 'allusers') ? trim($peerid_data) : ''; + + if ($peerid_type == 'fqdn' && !empty($peerid)) { + $peerid = "@{$peerid}"; + } + + if (!empty($ph1ent['pre-shared-key'])) { + $pskconf .= "{$myid} {$peerid} : PSK 0s" . base64_encode(trim($ph1ent['pre-shared-key'])) . "\n"; + } + } + } + } + + /* Add user PSKs */ + if (is_array($config['system']) && is_array($config['system']['user'])) { + foreach ($config['system']['user'] as $user) { + if (!empty($user['ipsecpsk'])) { + $pskconf .= "{$myid} {$user['name']} : PSK 0s" . base64_encode($user['ipsecpsk']) . "\n"; + } + } + unset($user); + } + + /* add PSKs for mobile clients */ + if (is_array($ipseccfg['mobilekey'])) { + foreach ($ipseccfg['mobilekey'] as $key) { + if ($key['ident'] == "allusers") { + $key['ident'] = '%any'; + } + if (empty($key['type'])) { + $key['type'] = 'PSK'; + } + $pskconf .= "{$myid} {$key['ident']} : {$key['type']} 0s" . base64_encode($key['pre-shared-key']) . "\n"; + } + unset($key); + } + + @file_put_contents("{$g['varetc_path']}/ipsec/ipsec.secrets", $pskconf); + chmod("{$g['varetc_path']}/ipsec/ipsec.secrets", 0600); + unset($pskconf); + + $uniqueids = 'yes'; + if (!empty($config['ipsec']['uniqueids'])) { + if (array_key_exists($config['ipsec']['uniqueids'], $ipsec_idhandling)) { + $uniqueids = $config['ipsec']['uniqueids']; + } + } + $natfilterrules = false; + /* begin ipsec.conf */ + $ipsecconf = ""; + $enablecompression = false; + if (is_array($a_phase1) && count($a_phase1)) { + + $ipsecconf .= "# This file is automatically generated. Do not edit\n"; + $ipsecconf .= "config setup\n\tuniqueids = {$uniqueids}\n"; + $ipsecconf .= "\tcharondebug=\"" . vpn_ipsec_configure_loglevels(true) . "\"\n"; + + if (isset($config['ipsec']['strictcrlpolicy'])) { + $ipsecconf .= "\tstrictcrlpolicy = yes \n"; + } + + if (!isset($config['ipsec']['noshuntlaninterfaces'])) { + if ($config['interfaces']['lan']) { + $lanip = get_interface_ip("lan"); + if (!empty($lanip) && is_ipaddrv4($lanip)) { + $lansn = get_interface_subnet("lan"); + $lansa = gen_subnet($lanip, $lansn); + $ipsecconf .= <<<EOD + +conn bypasslan + leftsubnet = {$lansa}/{$lansn} + rightsubnet = {$lansa}/{$lansn} + authby = never + type = passthrough + auto = route + +EOD; + } + } + } + + foreach ($a_phase1 as $ph1ent) { + if (isset($ph1ent['disabled'])) { + continue; + } + + if ($ph1ent['mode'] == "aggressive") { + $aggressive = "yes"; + } else { + $aggressive = "no"; + } + + $ep = ipsec_get_phase1_src($ph1ent); + if (!$ep) { + continue; + } + + $ikeid = $ph1ent['ikeid']; + $keyexchange = "ikev1"; + $passive = "route"; + if (!empty($ph1ent['iketype'])) { + if ($ph1ent['iketype'] == "ikev2") { + $keyexchange = "ikev2"; + //$passive = "start"; + } + } + + if (isset($ph1ent['mobile'])) { + $right_spec = "%any"; + $passive = 'add'; + } else { + if (isset($ph1ent['responderonly'])) { + $passive = 'add'; + } + + $right_spec = $ph1ent['remote-gateway']; + if (is_ipaddr($right_spec)) { + $sourcehost = $right_spec; + } else { + $sourcehost = $rgmap['remote-gateway']; + } + + if ($ph1ent['protocol'] == 'inet') { + if (strpos($ph1ent['interface'], '_vip')) { + $vpninterface = explode('_vip', $ph1ent['interface']); + $ifacesuse = get_real_interface($vpninterface[0]); + $vpninterface = $vpninterface[0]; + } else { + $ifacesuse = get_failover_interface($ph1ent['interface']); + if (strpos($ifacesuse, '_vip')) { + $vpninterface = explode('_vip', $ifacesuse); + $ifacesuse = get_real_interface($vpninterface[0]); + $vpninterface = $vpninterface[0]; + } else { + $vpninterface = convert_real_interface_to_friendly_interface_name($ifacesuse); + } + } + + if (!empty($ifacesuse) && interface_has_gateway($vpninterface)) { + $gatewayip = get_interface_gateway($vpninterface); + $interfaceip = get_interface_ip($vpninterface); + $subnet_bits = get_interface_subnet($vpninterface); + $subnet_ip = gen_subnetv4($interfaceip, $subnet_bits); + /* if the remote gateway is in the local subnet, then don't add a route */ + if (!ip_in_subnet($sourcehost, "{$subnet_ip}/{$subnet_bits}")) { + if (is_ipaddrv4($gatewayip)) { + // log_error("IPSEC interface is not WAN but {$ifacesuse}, adding static route for VPN endpoint {$rgip} via {$gatewayip}"); + mwexec("/sbin/route change -host {$sourcehost} {$gatewayip}", true); + } + } + } + } else if ($ph1ent['protocol'] == 'inet6') { + if (strpos($ph1ent['interface'], '_vip')) { + $vpninterface = explode('_vip', $ph1ent['interface']); + $ifacesuse = get_real_interface($vpninterface[0]); + $vpninterface = $vpninterface[0]; + } else { + $ifacesuse = get_failover_interface($ph1ent['interface']); + if (strpos($ifacesuse, '_vip')) { + $vpninterface = explode('_vip', $ifacesuse); + $ifacesuse = get_real_interface($vpninterface[0]); + $vpninterface = $vpninterface[0]; + } else { + $vpninterface = convert_real_interface_to_friendly_interface_name($ifacesuse); + } + } + + if (!empty($ifacesuse) && interface_has_gateway($vpninterface)) { + $gatewayip = get_interface_gateway_v6($vpninterface); + $interfaceip = get_interface_ipv6($vpninterface); + $subnet_bits = get_interface_subnetv6($vpninterface); + $subnet_ip = gen_subnetv6($interfaceip, $subnet_bits); + /* if the remote gateway is in the local subnet, then don't add a route */ + if (!ip_in_subnet($sourcehost, "{$subnet_ip}/{$subnet_bits}")) { + if (is_ipaddrv6($gatewayip)) { + // log_error("IPSEC interface is not WAN but {$ifacesuse}, adding static route for VPN endpoint {$rgip} via {$gatewayip}"); + mwexec("/sbin/route change -inet6 -host {$sourcehost} {$gatewayip}", true); + } + } + } + } + } + + list ($myid_type, $myid_data) = ipsec_find_id($ph1ent, 'local'); + if ($myid_type != 'address' && $myid_type != 'keyid' && $myid_type != 'asn1dn') { + $myid_data = "{$myid_type}:{$myid_data}"; + } elseif ($myid_type == "asn1dn" && !empty($myid_data)) { + if ($myid_data[0] == '#') { + /* asn1dn needs double quotes */ + $myid_data = "\"{$myid_type}:{$myid_data}\""; + } else { + $myid_data = "\"{$myid_data}\""; + } + } + $leftid = ''; + if (!empty($myid_data)) { + $leftid = "leftid = {$myid_data}"; + } + + $peerid_spec = ''; + if (isset($ph1ent['mobile']) && ($ph1ent['authentication_method'] == "pre_shared_key" || $ph1ent['authentication_method'] == "xauth_psk_server")) { + // Only specify peer ID if we are not dealing with mobile PSK + } else { + list ($peerid_type, $peerid_data) = ipsec_find_id($ph1ent, 'peer', $rgmap); + if ($peerid_type == 'any') { + $peerid_spec = ''; + } elseif ($peerid_type != 'address' && $peerid_type != 'keyid' && $peerid_type != 'asn1dn') { + $peerid_spec = "{$peerid_type}:{$peerid_data}"; + } elseif ($peerid_type == "asn1dn") { + /* asn1dn needs double quotes */ + if ($peerid_data[0] == '#') { + $peerid_spec = "\"{$peerid_type}:{$peerid_data}\""; + } elseif (!empty($peerid_data)) { + $peerid_spec = "\"{$peerid_data}\""; + } + } else { + $peerid_spec = $peerid_data; + } + } + + if (is_array($ph1ent['encryption-algorithm']) && !empty($ph1ent['encryption-algorithm']['name']) && !empty($ph1ent['hash-algorithm'])) { + $ealgosp1 = ''; + $ealg_id = $ph1ent['encryption-algorithm']['name']; + $ealg_kl = $ph1ent['encryption-algorithm']['keylen']; + if ($ealg_kl) { + $ealgosp1 = "ike = {$ealg_id}{$ealg_kl}-{$ph1ent['hash-algorithm']}"; + } else { + $ealgosp1 = "ike = {$ealg_id}-{$ph1ent['hash-algorithm']}"; + } + + $modp = vpn_ipsec_convert_to_modp($ph1ent['dhgroup']); + if (!empty($modp)) { + $ealgosp1 .= "-{$modp}"; + } + + $ealgosp1 .= "!"; + } + + if ($ph1ent['dpd_delay'] && $ph1ent['dpd_maxfail']) { + if ($passive == "route") { + $dpdline = "dpdaction = restart"; + } else { + $dpdline = "dpdaction = clear"; + } + $dpdline .= "\n\tdpddelay = {$ph1ent['dpd_delay']}s"; + $dpdtimeout = $ph1ent['dpd_delay'] * ($ph1ent['dpd_maxfail'] + 1); + $dpdline .= "\n\tdpdtimeout = {$dpdtimeout}s"; + } else { + $dpdline = "dpdaction = none"; + } + + $ikelifeline = ''; + if ($ph1ent['lifetime']) { + $ikelifeline = "ikelifetime = {$ph1ent['lifetime']}s"; + } + + $rightsourceip = NULL; + if (isset($ph1ent['mobile']) && !empty($a_client['pool_address'])) { + $rightsourceip = "\trightsourceip = {$a_client['pool_address']}/{$a_client['pool_netbits']}\n"; + } + + $authentication = ""; + switch ($ph1ent['authentication_method']) { + case 'eap-mschapv2': + if (isset($ph1ent['mobile'])) { + $authentication = "eap_identity=%any\n\t"; + $authentication .= "leftauth=pubkey\n\trightauth=eap-mschapv2"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + } + break; + case 'eap-tls': + if (isset($ph1ent['mobile'])) { + $authentication = "eap_identity=%identity\n\t"; + $authentication .= "leftauth=pubkey\n\trightauth=eap-tls"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + } else { + $authentication = "leftauth=eap-tls\n\trightauth=eap-tls"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + } + break; + case 'eap-radius': + if (isset($ph1ent['mobile'])) { + $authentication = "eap_identity=%identity\n\t"; + $authentication .= "leftauth=pubkey\n\trightauth=eap-radius"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + } else { + $authentication = "leftauth=eap-radius\n\trightauth=eap-radius"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + } + break; + case 'xauth_rsa_server': + $authentication = "leftauth = pubkey\n\trightauth = pubkey"; + $authentication .= "\n\trightauth2 = xauth-generic"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + break; + case 'xauth_psk_server': + $authentication = "leftauth = psk\n\trightauth = psk"; + $authentication .= "\n\trightauth2 = xauth-generic"; + break; + case 'pre_shared_key': + $authentication = "leftauth = psk\n\trightauth = psk"; + break; + case 'rsasig': + $authentication = "leftauth = pubkey\n\trightauth = pubkey"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + break; + case 'hybrid_rsa_server': + $authentication = "leftauth = xauth-generic\n\trightauth = pubkey"; + $authentication .= "\n\trightauth2 = xauth"; + if (!empty($ph1ent['certref'])) { + $authentication .= "\n\tleftcert={$certpath}/cert-{$ph1ent['ikeid']}.crt"; + } + break; + } + + $left_spec = $ep; + + if (isset($ph1ent['reauth_enable'])) { + $reauth = "reauth = no"; + } else { + $reauth = "reauth = yes"; + } + if (isset($ph1ent['rekey_enable'])) { + $rekey = "rekey = no"; + } else { + $rekey = "rekey = yes"; + } + + if ($ph1ent['nat_traversal'] == 'off') { + $forceencaps = 'forceencaps = no'; + } else if ($ph1ent['nat_traversal'] == 'force') { + $forceencaps = 'forceencaps = yes'; + } else { + $forceencaps = 'forceencaps = no'; + } + + if ($ph1ent['mobike'] == 'on') { + $mobike = 'mobike = yes'; + } else { + $mobike = 'mobike = no'; + } + + $ipseclifetime = 0; + $rightsubnet_spec = array(); + $leftsubnet_spec = array(); + $reqids = array(); + $ealgoAHsp2arr = array(); + $ealgoESPsp2arr = array(); + if (is_array($a_phase2) && count($a_phase2)) { + foreach ($a_phase2 as $ph2ent) { + if ($ikeid != $ph2ent['ikeid']) { + continue; + } + + if (isset($ph2ent['disabled'])) { + continue; + } + + if (isset($ph2ent['mobile']) && !isset($a_client['enable'])) { + continue; + } + + if (($ph2ent['mode'] == 'tunnel') or ($ph2ent['mode'] == 'tunnel6')) { + $tunneltype = "type = tunnel"; + + $localid_type = $ph2ent['localid']['type']; + $leftsubnet_data = ipsec_idinfo_to_cidr($ph2ent['localid'], false, $ph2ent['mode']); + + /* Do not print localid in some cases, such as a pure-psk or psk/xauth single phase2 mobile tunnel */ + if (($localid_type == "none" || $localid_type == "mobile") && + isset($ph1ent['mobile']) && (ipsec_get_number_of_phase2($ikeid) == 1)) { + $left_spec = '%any'; + } else { + if ($localid_type != "address") { + $localid_type = "subnet"; + } + // Don't let an empty subnet into config, it can cause parse errors. Ticket #2201. + if (!is_ipaddr($leftsubnet_data) && !is_subnet($leftsubnet_data) && ($leftsubnet_data != "0.0.0.0/0")) { + log_error("Invalid IPsec Phase 2 \"{$ph2ent['descr']}\" - {$ph2ent['localid']['type']} has no subnet."); + continue; + } + if (!empty($ph2ent['natlocalid'])) { + $natleftsubnet_data = ipsec_idinfo_to_cidr($ph2ent['natlocalid'], false, $ph2ent['mode']); + if ($ph2ent['natlocalid']['type'] != "address") { + if (is_subnet($natleftsubnet_data)) { + $leftsubnet_data = "{$natleftsubnet_data}|{$leftsubnet_data}"; + } + } else { + if (is_ipaddr($natleftsubnet_data)) { + $leftsubnet_data = "{$natleftsubnet_data}|{$leftsubnet_data}"; + } + } + $natfilterrules = true; + } + } + + $leftsubnet_spec[] = $leftsubnet_data; + + if (!isset($ph2ent['mobile'])) { + $tmpsubnet = ipsec_idinfo_to_cidr($ph2ent['remoteid'], false, $ph2ent['mode']); + $rightsubnet_spec[] = $tmpsubnet; + } else if (!empty($a_client['pool_address'])) { + $rightsubnet_spec[] = "{$a_client['pool_address']}/{$a_client['pool_netbits']}"; + } + } else { + $tunneltype = "type = transport"; + + if ((($ph1ent['authentication_method'] == "xauth_psk_server") || + ($ph1ent['authentication_method'] == "pre_shared_key")) && isset($ph1ent['mobile'])) { + $left_spec = "%any"; + } else { + $tmpsubnet = ipsec_get_phase1_src($ph1ent); + $leftsubnet_spec[] = $tmpsubnet; + } + + if (!isset($ph2ent['mobile'])) { + $rightsubnet_spec[] = $right_spec; + } + } + + if (isset($a_client['pfs_group']) && isset($ph2ent['mobile'])) { + $ph2ent['pfsgroup'] = $a_client['pfs_group']; + } + + if ($ph2ent['protocol'] == 'esp') { + if (is_array($ph2ent['encryption-algorithm-option'])) { + foreach ($ph2ent['encryption-algorithm-option'] as $ealg) { + $ealg_id = $ealg['name']; + $ealg_kl = $ealg['keylen']; + + if (!empty($ealg_kl) && $ealg_kl == "auto") { + if (empty($p2_ealgos) || !is_array($p2_ealgos)) { + require("ipsec.inc"); + } + $key_hi = $p2_ealgos[$ealg_id]['keysel']['hi']; + $key_lo = $p2_ealgos[$ealg_id]['keysel']['lo']; + $key_step = $p2_ealgos[$ealg_id]['keysel']['step']; + /* XXX: in some cases where include ordering is suspect these variables + * are somehow 0 and we enter this loop forever and timeout after 900 + * seconds wrecking bootup */ + if ($key_hi != 0 and $key_lo != 0 and $key_step != 0) { + for ($keylen = $key_hi; $keylen >= $key_lo; $keylen -= $key_step) { + if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option'])) { + foreach ($ph2ent['hash-algorithm-option'] as $halgo) { + $halgo = str_replace('hmac_', '', $halgo); + $tmpealgo = "{$ealg_id}{$keylen}-{$halgo}"; + $modp = vpn_ipsec_convert_to_modp($ph2ent['pfsgroup']); + if (!empty($modp)) { + $tmpealgo .= "-{$modp}"; + } + $ealgoESPsp2arr[] = $tmpealgo; + } + } else { + $tmpealgo = "{$ealg_id}{$keylen}"; + $modp = vpn_ipsec_convert_to_modp($ph2ent['pfsgroup']); + if (!empty($modp)) { + $tmpealgo .= "-{$modp}"; + } + $ealgoESPsp2arr[] = $tmpealgo; + } + } + } + } else { + if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option'])) { + foreach ($ph2ent['hash-algorithm-option'] as $halgo) { + $halgo = str_replace('hmac_', '', $halgo); + $tmpealgo = "{$ealg_id}{$ealg_kl}-{$halgo}"; + $modp = vpn_ipsec_convert_to_modp($ph2ent['pfsgroup']); + if (!empty($modp)) { + $tmpealgo .= "-{$modp}"; + } + $ealgoESPsp2arr[] = $tmpealgo; + } + } else { + $tmpealgo = "{$ealg_id}{$ealg_kl}"; + $modp = vpn_ipsec_convert_to_modp($ph2ent['pfsgroup']); + if (!empty($modp)) { + $tmpealgo .= "-{$modp}"; + } + $ealgoESPsp2arr[] = $tmpealgo; + } + } + } + } + } else if ($ph2ent['protocol'] == 'ah') { + if (!empty($ph2ent['hash-algorithm-option']) && is_array($ph2ent['hash-algorithm-option'])) { + $modp = vpn_ipsec_convert_to_modp($ph2ent['pfsgroup']); + foreach ($ph2ent['hash-algorithm-option'] as $tmpAHalgo) { + $tmpAHalgo = str_replace('hmac_', '', $tmpAHalgo); + if (!empty($modp)) { + $tmpAHalgo = "-{$modp}"; + } + $ealgoAHsp2arr[] = $tmpAHalgo; + } + } + } + + $reqids[] = $ph2ent['reqid']; + + if (!empty($ph2ent['lifetime'])) { + if ($ipseclifetime == 0 || intval($ipseclifetime) > intval($ph2ent['lifetime'])) { + $ipseclifetime = intval($ph2ent['lifetime']); + } + } + + } + } + + $ipsecconnect =<<<EOD + fragmentation = yes + keyexchange = {$keyexchange} + {$reauth} + {$forceencaps} + {$mobike} + {$rekey} + installpolicy = yes + {$tunneltype} + {$dpdline} + auto = {$passive} + left = {$left_spec} + right = {$right_spec} + {$leftid} + +EOD; + + if (isset($config['ipsec']['compression'])) { + $ipsecconnect .= "\tcompress = yes\n"; + $enablecompression = true; + } + if (!empty($ikelifeline)) { + $ipsecconnect .= "\t{$ikelifeline}\n"; + } + if ($ipseclifetime > 0) { + $ipsecconnect .= "\tlifetime = {$ipseclifetime}s\n"; + } + if (!empty($rightsourceip)) { + $ipsecconnect .= "{$rightsourceip}"; + } + if (!empty($ealgosp1)) { + $ipsecconnect .= "\t{$ealgosp1}\n"; + } + if (!empty($ealgoAHsp2arr)) { + $ipsecconnect .= "\tah = " . join(',', $ealgoAHsp2arr) . "!\n"; + } + if (!empty($ealgoESPsp2arr)) { + $ipsecconnect .= "\tesp = " . join(',', $ealgoESPsp2arr) . "!\n"; + } + if (!empty($authentication)) { + $ipsecconnect .= "\t{$authentication}\n"; + } + if (!empty($peerid_spec)) { + $ipsecconnect .= "\trightid = {$peerid_spec}\n"; + } + if ($keyexchange == 'ikev1') { + $ipsecconnect .= "\taggressive = {$aggressive}\n"; + } + + if (!isset($ph1ent['mobile']) && $keyexchange == 'ikev1') { + if (!empty($rightsubnet_spec)) { + $ipsecfin = ''; + foreach ($rightsubnet_spec as $idx => $rsubnet) { + $ipsecfin .= "\nconn con{$ph1ent['ikeid']}00{$idx}\n"; + //if (!empty($reqids[$idx])) { + // $ipsecfin .= "\treqid = " . $reqids[$idx] . "\n"; + //} + $ipsecfin .= $ipsecconnect; + $ipsecfin .= "\trightsubnet = {$rsubnet}\n"; + $ipsecfin .= "\tleftsubnet = " . $leftsubnet_spec[$idx] . "\n"; + } + } else { + log_error("No phase2 specifications for tunnel with REQID = {$ikeid}"); + } + } else { + $ipsecfin = "\nconn con{$ph1ent['ikeid']}\n"; + //if (!empty($reqids[$idx])) { + // $ipsecfin .= "\treqid = " . $reqids[0] . "\n"; + //} + $ipsecfin .= $ipsecconnect; + if (!isset($ph1ent['mobile']) && !empty($rightsubnet_spec)) { + $tempsubnets = array(); + foreach ($rightsubnet_spec as $rightsubnet) { + $tempsubnets[$rightsubnet] = $rightsubnet; + } + $ipsecfin .= "\trightsubnet = " . join(",", $tempsubnets) . "\n"; + unset($tempsubnets, $rightsubnet); + } + if (!empty($leftsubnet_spec)) { + $tempsubnets = array(); + foreach ($leftsubnet_spec as $leftsubnet) { + $tempsubnets[$leftsubnet] = $leftsubnet; + } + $ipsecfin .= "\tleftsubnet = " . join(",", $tempsubnets) . "\n"; + unset($tempsubnets, $leftsubnet); + } + } + $ipsecconf .= $ipsecfin; + unset($ipsecfin); + } + } + + @file_put_contents("{$g['varetc_path']}/ipsec/ipsec.conf", $ipsecconf); + unset($ipsecconf); + /* end ipsec.conf */ + + if ($enablecompression === true) { + set_single_sysctl('net.inet.ipcomp.ipcomp_enable', 1); + } else { + set_single_sysctl('net.inet.ipcomp.ipcomp_enable', 0); + } + + /* manage process */ + if ($restart === true) { + mwexec("/usr/local/sbin/ipsec restart", false); + } else { + if (isvalidpid("{$g['varrun_path']}/starter.charon.pid")) { + /* Update configuration changes */ + /* Read secrets */ + mwexec("/usr/local/sbin/ipsec rereadall", false); + mwexec("/usr/local/sbin/ipsec reload", false); + } else { + mwexec("/usr/local/sbin/ipsec start", false); + } + } + + if ($natfilterrules == true) { + filter_configure(); + } + /* start filterdns, if necessary */ + if (count($filterdns_list) > 0) { + $interval = 60; + if (!empty($ipseccfg['dns-interval']) && is_numeric($ipseccfg['dns-interval'])) { + $interval = $ipseccfg['dns-interval']; + } + + $hostnames = ""; + array_unique($filterdns_list); + foreach ($filterdns_list as $hostname) { + $hostnames .= "cmd {$hostname} '/usr/local/sbin/pfSctl -c \"service reload ipsecdns\"'\n"; + } + file_put_contents("{$g['varetc_path']}/ipsec/filterdns-ipsec.hosts", $hostnames); + unset($hostnames); + + if (isvalidpid("{$g['varrun_path']}/filterdns-ipsec.pid")) { + sigkillbypid("{$g['varrun_path']}/filterdns-ipsec.pid", "HUP"); + } else { + mwexec("/usr/local/sbin/filterdns -p {$g['varrun_path']}/filterdns-ipsec.pid -i {$interval} -c {$g['varetc_path']}/ipsec/filterdns-ipsec.hosts -d 1"); + } + } else { + killbypid("{$g['varrun_path']}/filterdns-ipsec.pid"); + @unlink("{$g['varrun_path']}/filterdns-ipsec.pid"); + } + + if (platform_booting()) { + echo "done\n"; + } + + return count($filterdns_list); +} + +/* + * Forcefully restart IPsec + * This is required for when dynamic interfaces reload + * For all other occasions the normal vpn_ipsec_configure() + * will gracefully reload the settings without restarting + */ +function vpn_ipsec_force_reload($interface = "") { + global $g, $config; + + $ipseccfg = $config['ipsec']; + + if (!empty($interface) && is_array($ipseccfg['phase1'])) { + $found = false; + foreach ($ipseccfg['phase1'] as $ipsec) { + if (!isset($ipsec['disabled']) && ($ipsec['interface'] == $interface)) { + $found = true; + break; + } + } + if (!$found) { + log_error(sprintf(gettext("Ignoring IPsec reload since there are no tunnels on interface %s"), $interface)); + return; + } + } + + /* if ipsec is enabled, start up again */ + if (isset($ipseccfg['enable'])) { + log_error(gettext("Forcefully reloading IPsec")); + vpn_ipsec_configure(); + } +} + +/* master setup for vpn (mpd) */ +function vpn_setup() { + /* start pptpd */ + vpn_pptpd_configure(); + + /* start pppoe server */ + vpn_pppoes_configure(); + + /* setup l2tp */ + vpn_l2tp_configure(); +} + +function vpn_netgraph_support() { + $iflist = get_configured_interface_list(); + foreach ($iflist as $iface) { + $realif = get_real_interface($iface); + /* Get support for netgraph(4) from the nic */ + $ifinfo = pfSense_get_interface_addresses($realif); + if (!empty($ifinfo) && in_array($ifinfo['iftype'], array("ether", "vlan", "bridge"))) { + pfSense_ngctl_attach(".", $realif); + } + } +} + +function vpn_pptpd_configure() { + global $config, $g; + + $syscfg = $config['system']; + $pptpdcfg = $config['pptpd']; + + if (platform_booting()) { + if (!$pptpdcfg['mode'] || ($pptpdcfg['mode'] == "off")) { + return 0; + } + + if (platform_booting(true)) { + echo gettext("Configuring PPTP VPN service... "); + } + } else { + /* kill mpd */ + killbypid("{$g['varrun_path']}/pptp-vpn.pid"); + + /* wait for process to die */ + sleep(3); + + if (is_process_running("mpd -b")) { + killbypid("{$g['varrun_path']}/pptp-vpn.pid"); + log_error(gettext("Could not kill mpd within 3 seconds. Trying again.")); + } + + /* remove mpd.conf, if it exists */ + unlink_if_exists("{$g['varetc_path']}/pptp-vpn/mpd.conf"); + unlink_if_exists("{$g['varetc_path']}/pptp-vpn/mpd.links"); + unlink_if_exists("{$g['varetc_path']}/pptp-vpn/mpd.secret"); + } + + if (empty($pptpdcfg['n_pptp_units'])) { + log_error("Something wrong in the PPTPd configuration. Preventing starting the daemon because issues would arise."); + return; + } + + /* make sure pptp-vpn directory exists */ + if (!file_exists("{$g['varetc_path']}/pptp-vpn")) { + mkdir("{$g['varetc_path']}/pptp-vpn"); + } + + switch ($pptpdcfg['mode']) { + case 'server': + /* write mpd.conf */ + $fd = fopen("{$g['varetc_path']}/pptp-vpn/mpd.conf", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.conf in vpn_pptpd_configure().") . "\n"); + return 1; + } + + $mpdconf = <<<EOD +pptps: + +EOD; + + for ($i = 0; $i < $pptpdcfg['n_pptp_units']; $i++) { + $mpdconf .= " load pt{$i}\n"; + } + + for ($i = 0; $i < $pptpdcfg['n_pptp_units']; $i++) { + + $clientip = long2ip32(ip2long($pptpdcfg['remoteip']) + $i); + + $mpdconf .= <<<EOD + +pt{$i}: + new -i pptpd{$i} pt{$i} pt{$i} + set ipcp ranges {$pptpdcfg['localip']}/32 {$clientip}/32 + load pts + +EOD; + } + + $mpdconf .=<<<EOD + +pts: + set iface disable on-demand + set iface enable proxy-arp + set iface enable tcpmssfix + set iface idle 1800 + set iface up-script /usr/local/sbin/vpn-linkup + set iface down-script /usr/local/sbin/vpn-linkdown + set bundle enable multilink + set bundle enable crypt-reqd + set link yes acfcomp protocomp + set link no pap chap + set link enable chap-msv2 + set link mtu 1460 + set link keep-alive 10 60 + set ipcp yes vjcomp + set bundle enable compression + set ccp yes mppc + set ccp yes mpp-e128 + set ccp yes mpp-stateless + +EOD; + + if (!isset ($pptpdcfg['req128'])) { + $mpdconf .=<<<EOD + set ccp yes mpp-e40 + set ccp yes mpp-e56 + +EOD; + } + + if (isset($pptpdcfg["wins"]) && $pptpdcfg['wins'] != "") { + $mpdconf .= " set ipcp nbns {$pptpdcfg['wins']}\n"; + } + + if (!empty($pptpdcfg['dns1'])) { + $mpdconf .= " set ipcp dns " . $pptpdcfg['dns1']; + if (!empty($pptpdcfg['dns2'])) { + $mpdconf .= " " . $pptpdcfg['dns2']; + } + $mpdconf .= "\n"; + } elseif (isset ($config['dnsmasq']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (isset($config['unbound']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) { + $mpdconf .= " set ipcp dns " . join(" ", $syscfg['dnsserver']) . "\n"; + } + + if (isset ($pptpdcfg['radius']['server']['enable'])) { + $authport = (isset($pptpdcfg['radius']['server']['port']) && strlen($pptpdcfg['radius']['server']['port']) > 1) ? $pptpdcfg['radius']['server']['port'] : 1812; + $acctport = $authport + 1; + $mpdconf .=<<<EOD + set radius server {$pptpdcfg['radius']['server']['ip']} "{$pptpdcfg['radius']['server']['secret']}" {$authport} {$acctport} + +EOD; + if (isset ($pptpdcfg['radius']['server2']['enable'])) { + $authport = (isset($pptpdcfg['radius']['server2']['port']) && strlen($pptpdcfg['radius']['server2']['port']) > 1) ? $pptpdcfg['radius']['server2']['port'] : 1812; + $acctport = $authport + 1; + $mpdconf .=<<<EOD + set radius server {$pptpdcfg['radius']['server2']['ip']} "{$pptpdcfg['radius']['server2']['secret2']}" {$authport} {$acctport} + +EOD; + } + $mpdconf .=<<<EOD + set radius retries 3 + set radius timeout 10 + set auth enable radius-auth + +EOD; + + if (isset ($pptpdcfg['radius']['accounting'])) { + $mpdconf .=<<<EOD + set auth enable radius-acct + set radius acct-update 300 + +EOD; + } + } + + fwrite($fd, $mpdconf); + fclose($fd); + unset($mpdconf); + + /* write mpd.links */ + $fd = fopen("{$g['varetc_path']}/pptp-vpn/mpd.links", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.links in vpn_pptpd_configure().") . "\n"); + return 1; + } + + $mpdlinks = ""; + + for ($i = 0; $i < $pptpdcfg['n_pptp_units']; $i++) { + $mpdlinks .=<<<EOD + +pt{$i}: + set link type pptp + set pptp enable incoming + set pptp disable originate + set pptp disable windowing + +EOD; + } + + fwrite($fd, $mpdlinks); + fclose($fd); + unset($mpdlinks); + + /* write mpd.secret */ + $fd = fopen("{$g['varetc_path']}/pptp-vpn/mpd.secret", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.secret in vpn_pptpd_configure().") . "\n"); + return 1; + } + + $mpdsecret = ""; + + if (is_array($pptpdcfg['user'])) { + foreach ($pptpdcfg['user'] as $user) { + $pass = str_replace('\\', '\\\\', $user['password']); + $pass = str_replace('"', '\"', $pass); + $mpdsecret .= "{$user['name']} \"{$pass}\" {$user['ip']}\n"; + } + } + + fwrite($fd, $mpdsecret); + fclose($fd); + unset($mpdsecret); + chmod("{$g['varetc_path']}/pptp-vpn/mpd.secret", 0600); + + vpn_netgraph_support(); + + /* fire up mpd */ + mwexec("/usr/local/sbin/mpd4 -b -d {$g['varetc_path']}/pptp-vpn -p {$g['varrun_path']}/pptp-vpn.pid -s pptps pptps"); + + break; + + case 'redir': + break; + } + + if (platform_booting()) { + echo "done\n"; + } + + return 0; +} + +function vpn_pppoes_configure() { + global $config; + + if (is_array($config['pppoes']['pppoe'])) { + foreach ($config['pppoes']['pppoe'] as $pppoe) { + vpn_pppoe_configure($pppoe); + } + } +} + +function vpn_pppoe_configure(&$pppoecfg) { + global $config, $g; + + $syscfg = $config['system']; + + /* create directory if it does not exist */ + if (!is_dir("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn")) { + mkdir("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn"); + } + + if (platform_booting()) { + if (!$pppoecfg['mode'] || ($pppoecfg['mode'] == "off")) { + return 0; + } + + echo gettext("Configuring PPPoE Server service... "); + } else { + /* kill mpd */ + killbypid("{$g['varrun_path']}/pppoe{$pppoecfg['pppoeid']}-vpn.pid"); + + /* wait for process to die */ + sleep(2); + + } + + switch ($pppoecfg['mode']) { + + case 'server': + + $pppoe_interface = get_real_interface($pppoecfg['interface']); + + if ($pppoecfg['paporchap'] == "chap") { + $paporchap = "set link enable chap"; + } else { + $paporchap = "set link enable pap"; + } + + /* write mpd.conf */ + $fd = fopen("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn/mpd.conf", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.conf in vpn_pppoe_configure().") . "\n"); + return 1; + } + $mpdconf = "\n\n"; + $mpdconf .= "poes:\n"; + + for ($i = 0; $i < $pppoecfg['n_pppoe_units']; $i++) { + $mpdconf .= " load poes{$pppoecfg['pppoeid']}{$i}\n"; + } + + for ($i = 0; $i < $pppoecfg['n_pppoe_units']; $i++) { + + $clientip = long2ip32(ip2long($pppoecfg['remoteip']) + $i); + + if (isset($pppoecfg['radius']['radiusissueips']) && isset($pppoecfg['radius']['server']['enable'])) { + $issue_ip_type = "set ipcp ranges {$pppoecfg['localip']}/32 0.0.0.0/0"; + } else { + $issue_ip_type = "set ipcp ranges {$pppoecfg['localip']}/32 {$clientip}/32"; + } + + $mpdconf .=<<<EOD + +poes{$pppoecfg['pppoeid']}{$i}: + new -i poes{$pppoecfg['pppoeid']}{$i} poes{$pppoecfg['pppoeid']}{$i} poes{$pppoecfg['pppoeid']}{$i} + {$issue_ip_type} + load pppoe_standard + +EOD; + } + + $mpdconf .=<<<EOD + +pppoe_standard: + set bundle no multilink + set bundle enable compression + set auth max-logins 1 + set iface up-script /usr/local/sbin/vpn-linkup + set iface down-script /usr/local/sbin/vpn-linkdown + set iface idle 0 + set iface disable on-demand + set iface disable proxy-arp + set iface enable tcpmssfix + set iface mtu 1500 + set link no pap chap + {$paporchap} + set link keep-alive 60 180 + set ipcp yes vjcomp + set ipcp no vjcomp + set link max-redial -1 + set link mtu 1492 + set link mru 1492 + set ccp yes mpp-e40 + set ccp yes mpp-e128 + set ccp yes mpp-stateless + set link latency 1 + #set ipcp dns 10.10.1.3 + #set bundle accept encryption + +EOD; + + if (!empty($pppoecfg['dns1'])) { + $mpdconf .= " set ipcp dns " . $pppoecfg['dns1']; + if (!empty($pppoecfg['dns2'])) { + $mpdconf .= " " . $pppoecfg['dns2']; + } + $mpdconf .= "\n"; + } elseif (isset ($config['dnsmasq']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (isset ($config['unbound']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) { + $mpdconf .= " set ipcp dns " . join(" ", $syscfg['dnsserver']) . "\n"; + } + + if (isset ($pppoecfg['radius']['server']['enable'])) { + $radiusport = ""; + $radiusacctport = ""; + if (isset($pppoecfg['radius']['server']['port'])) { + $radiusport = $pppoecfg['radius']['server']['port']; + } + if (isset($pppoecfg['radius']['server']['acctport'])) { + $radiusacctport = $pppoecfg['radius']['server']['acctport']; + } + $mpdconf .=<<<EOD + set radius server {$pppoecfg['radius']['server']['ip']} "{$pppoecfg['radius']['server']['secret']}" {$radiusport} {$radiusacctport} + set radius retries 3 + set radius timeout 10 + set auth enable radius-auth + +EOD; + + if (isset ($pppoecfg['radius']['accounting'])) { + $mpdconf .=<<<EOD + set auth enable radius-acct + +EOD; + } + } + + fwrite($fd, $mpdconf); + fclose($fd); + unset($mpdconf); + + /* write mpd.links */ + $fd = fopen("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn/mpd.links", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.links in vpn_pppoe_configure().") . "\n"); + return 1; + } + + $mpdlinks = ""; + + for ($i = 0; $i < $pppoecfg['n_pppoe_units']; $i++) { + $mpdlinks .=<<<EOD + +poes{$pppoecfg['pppoeid']}{$i}: + set phys type pppoe + set pppoe iface {$pppoe_interface} + set pppoe service "*" + set pppoe disable originate + set pppoe enable incoming + +EOD; + } + + fwrite($fd, $mpdlinks); + fclose($fd); + unset($mpdlinks); + + if ($pppoecfg['username']) { + /* write mpd.secret */ + $fd = fopen("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn/mpd.secret", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.secret in vpn_pppoe_configure().") . "\n"); + return 1; + } + + $mpdsecret = "\n\n"; + + if (!empty($pppoecfg['username'])) { + $item = explode(" ", $pppoecfg['username']); + foreach ($item as $userdata) { + $data = explode(":", $userdata); + $mpdsecret .= "{$data[0]} \"" . base64_decode($data[1]) . "\" {$data[2]}\n"; + } + } + + fwrite($fd, $mpdsecret); + fclose($fd); + unset($mpdsecret); + chmod("{$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn/mpd.secret", 0600); + } + + /* Check if previous instance is still up */ + while (file_exists("{$g['varrun_path']}/pppoe{$pppoecfg['pppoeid']}-vpn.pid") && isvalidpid("{$g['varrun_path']}/pppoe{$pppoecfg['pppoeid']}-vpn.pid")) { + killbypid("{$g['varrun_path']}/pppoe{$pppoecfg['pppoeid']}-vpn.pid"); + } + + /* Get support for netgraph(4) from the nic */ + pfSense_ngctl_attach(".", $pppoe_interface); + /* fire up mpd */ + mwexec("/usr/local/sbin/mpd4 -b -d {$g['varetc_path']}/pppoe{$pppoecfg['pppoeid']}-vpn -p {$g['varrun_path']}/pppoe{$pppoecfg['pppoeid']}-vpn.pid -s poes poes"); + + break; + } + + if (platform_booting()) { + echo gettext("done") . "\n"; + } + + return 0; +} + +function vpn_l2tp_configure() { + global $config, $g; + + $syscfg = $config['system']; + $l2tpcfg = $config['l2tp']; + + /* create directory if it does not exist */ + if (!is_dir("{$g['varetc_path']}/l2tp-vpn")) { + mkdir("{$g['varetc_path']}/l2tp-vpn"); + } + + if (platform_booting()) { + if (!$l2tpcfg['mode'] || ($l2tpcfg['mode'] == "off")) { + return 0; + } + + echo gettext("Configuring l2tp VPN service... "); + } else { + /* kill mpd */ + killbypid("{$g['varrun_path']}/l2tp-vpn.pid"); + + /* wait for process to die */ + sleep(8); + + } + + /* make sure l2tp-vpn directory exists */ + if (!file_exists("{$g['varetc_path']}/l2tp-vpn")) { + mkdir("{$g['varetc_path']}/l2tp-vpn"); + } + + switch ($l2tpcfg['mode']) { + + case 'server': + if ($l2tpcfg['paporchap'] == "chap") { + $paporchap = "set link enable chap"; + } else { + $paporchap = "set link enable pap"; + } + + /* write mpd.conf */ + $fd = fopen("{$g['varetc_path']}/l2tp-vpn/mpd.conf", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.conf in vpn_l2tp_configure().") . "\n"); + return 1; + } + $mpdconf = "\n\n"; + $mpdconf .=<<<EOD +l2tps: + +EOD; + + for ($i = 0; $i < $l2tpcfg['n_l2tp_units']; $i++) { + $mpdconf .= " load l2tp{$i}\n"; + } + + for ($i = 0; $i < $l2tpcfg['n_l2tp_units']; $i++) { + + $clientip = long2ip32(ip2long($l2tpcfg['remoteip']) + $i); + + if (isset ($l2tpcfg['radius']['radiusissueips']) && isset ($l2tpcfg['radius']['enable'])) { + $issue_ip_type = "set ipcp ranges {$l2tpcfg['localip']}/32 0.0.0.0/0"; + } else { + $issue_ip_type = "set ipcp ranges {$l2tpcfg['localip']}/32 {$clientip}/32"; + } + + $mpdconf .=<<<EOD + +l2tp{$i}: + new -i l2tp{$i} l2tp{$i} l2tp{$i} + {$issue_ip_type} + load l2tp_standard + +EOD; + } + + $mpdconf .=<<<EOD + +l2tp_standard: + set bundle disable multilink + set bundle enable compression + set bundle yes crypt-reqd + set ipcp yes vjcomp + # set ipcp ranges 131.188.69.161/32 131.188.69.170/28 + set ccp yes mppc + set iface disable on-demand + set iface enable proxy-arp + set iface up-script /usr/local/sbin/vpn-linkup + set iface down-script /usr/local/sbin/vpn-linkdown + set link yes acfcomp protocomp + set link no pap chap + {$paporchap} + set link keep-alive 10 180 + +EOD; + + if (is_ipaddr($l2tpcfg['wins'])) { + $mpdconf .= " set ipcp nbns {$l2tpcfg['wins']}\n"; + } + if (is_ipaddr($l2tpcfg['dns1'])) { + $mpdconf .= " set ipcp dns " . $l2tpcfg['dns1']; + if (is_ipaddr($l2tpcfg['dns2'])) { + $mpdconf .= " " . $l2tpcfg['dns2']; + } + $mpdconf .= "\n"; + } elseif (isset ($config['dnsmasq']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (isset ($config['unbound']['enable'])) { + $mpdconf .= " set ipcp dns " . get_interface_ip("lan"); + if ($syscfg['dnsserver'][0]) { + $mpdconf .= " " . $syscfg['dnsserver'][0]; + } + $mpdconf .= "\n"; + } elseif (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) { + $mpdconf .= " set ipcp dns " . join(" ", $syscfg['dnsserver']) . "\n"; + } + + if (isset ($l2tpcfg['radius']['enable'])) { + $mpdconf .=<<<EOD + set radius server {$l2tpcfg['radius']['server']} "{$l2tpcfg['radius']['secret']}" + set radius retries 3 + set radius timeout 10 + set auth enable radius-auth + +EOD; + + if (isset ($l2tpcfg['radius']['accounting'])) { + $mpdconf .=<<<EOD + set auth enable radius-acct + +EOD; + } + } + + fwrite($fd, $mpdconf); + fclose($fd); + unset($mpdconf); + + /* write mpd.links */ + $fd = fopen("{$g['varetc_path']}/l2tp-vpn/mpd.links", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.links in vpn_l2tp_configure().") . "\n"); + return 1; + } + + $mpdlinks = ""; + + for ($i = 0; $i < $l2tpcfg['n_l2tp_units']; $i++) { + $mpdlinks .=<<<EOD + +l2tp{$i}: + set link type l2tp + set l2tp enable incoming + set l2tp disable originate + +EOD; + if (!empty($l2tpcfg['secret'])) { + $mpdlinks .= "set l2tp secret {$l2tpcfg['secret']}\n"; + } + } + + fwrite($fd, $mpdlinks); + fclose($fd); + unset($mpdlinks); + + /* write mpd.secret */ + $fd = fopen("{$g['varetc_path']}/l2tp-vpn/mpd.secret", "w"); + if (!$fd) { + printf(gettext("Error: cannot open mpd.secret in vpn_l2tp_configure().") . "\n"); + return 1; + } + + $mpdsecret = "\n\n"; + + if (is_array($l2tpcfg['user'])) { + foreach ($l2tpcfg['user'] as $user) { + $mpdsecret .= "{$user['name']} \"{$user['password']}\" {$user['ip']}\n"; + } + } + + fwrite($fd, $mpdsecret); + fclose($fd); + unset($mpdsecret); + chmod("{$g['varetc_path']}/l2tp-vpn/mpd.secret", 0600); + + vpn_netgraph_support(); + + /* fire up mpd */ + mwexec("/usr/local/sbin/mpd4 -b -d {$g['varetc_path']}/l2tp-vpn -p {$g['varrun_path']}/l2tp-vpn.pid -s l2tps l2tps"); + + break; + + case 'redir': + break; + } + + if (platform_booting()) { + echo "done\n"; + } + + return 0; +} + +?> diff --git a/src/etc/inc/vslb.inc b/src/etc/inc/vslb.inc new file mode 100644 index 0000000..05bef31 --- /dev/null +++ b/src/etc/inc/vslb.inc @@ -0,0 +1,564 @@ +<?php +/* $Id$ */ +/* + vslb.inc + Copyright (C) 2005-2008 Bill Marquette + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ + +/* + pfSense_BUILDER_BINARIES: /usr/local/sbin/relayd + pfSense_MODULE: routing +*/ + + +/* include all configuration functions */ + +class Monitor { + private $conf = array(); + function __construct($config) { + $this->conf = $config; + } + + public function p() { + return "check {$this->get('proto')}"; + } + private function get($var) { + return isset($this->$var) ? $this->$var : ""; + } + protected function config($element) { + return isset($this->conf[$element]) ? $this->conf[$element] : ""; + } +} + +class TCPMonitor extends Monitor { + protected $proto = 'tcp'; +} + +class SSLMonitor extends Monitor { + protected $proto = 'ssl'; +} + +class ICMPMonitor extends Monitor { + protected $proto = 'icmp'; +} + +class HTTPMonitor extends Monitor { + protected $proto = 'http'; + function __construct($config) { + parent::__construct($config); + } + public function p() { + $method = ($this->code() != "") ? $this->code() : $this->digest(); + return "check {$this->proto} {$this->path()} {$this->host()} {$method}"; + } + + private function path() { + return $this->config('path') != "" ? "'{$this->config('path')}'" : ""; + } + + private function host() { + return $this->config('host') != "" ? "host {$this->config('host')}" : ""; + } + + private function code() { + return $this->config('code') != "" ? "code {$this->config('code')}" : ""; + } + + private function digest() { + return $this->config('digest') != "" ? "digest {$this->config('digest')}" : ""; + } +} + +class HTTPSMonitor extends HTTPMonitor { + protected $proto = 'https'; +} + +class SendMonitor extends Monitor { + private $proto = 'send'; + function __construct($config) { + parent::__construct($config); + } + public function p() { + return "check {$this->proto} {$this->data()} expect {$this->pattern()} {$this->ssl()}"; + } + + + private function data() { + return $this->config('send') != "" ? "\"{$this->config('send')}\"" : "\"\""; + } + + private function pattern() { + return $this->config('expect') != "" ? "\"{$this->config('expect')}\"" : "\"\""; + } + + private function ssl() { + return $this->config('ssl') == true ? "ssl" : ""; + } +} + +function echo_lbaction($action) { + global $config; + + // Index actions by name + $actions_a = array(); + for ($i = 0; isset($config['load_balancer']['lbaction'][$i]); $i++) { + $actions_a[$config['load_balancer']['lbaction'][$i]['name']] = $config['load_balancer']['lbaction'][$i]; + } + + $ret = ""; + $ret .= "{$actions_a[$action]['direction']} {$actions_a[$action]['type']} {$actions_a[$action]['action']}"; + switch ($actions_a[$action]['action']) { + case 'append': + $ret .= " \"{$actions_a[$action]['options']['value']}\" to \"{$actions_a[$action]['options']['akey']}\""; + break; + case 'change': + $ret .= " \"{$actions_a[$action]['options']['akey']}\" to \"{$actions_a[$action]['options']['value']}\""; + break; + case 'expect': + $ret .= " \"{$actions_a[$action]['options']['value']}\" from \"{$actions_a[$action]['options']['akey']}\""; + break; + case 'filter': + $ret .= " \"{$actions_a[$action]['options']['value']}\" from \"{$actions_a[$action]['options']['akey']}\""; + break; + case 'hash': + $ret .= " \"{$actions_a[$action]['options']['akey']}\""; + break; + case 'log': + $ret .= " \"{$actions_a[$action]['options']['akey']}\""; + break; + } + return $ret; +} + +function relayd_configure($kill_first=false) { + global $config, $g; + + // have to do this until every call to filter.inc is + // require_once() instead of require(). + if (!function_exists('filter_expand_alias_array')) { + require_once("filter.inc"); + } + + $vs_a = $config['load_balancer']['virtual_server']; + $pool_a = $config['load_balancer']['lbpool']; + $protocol_a = $config['load_balancer']['lbprotocol']; + $setting = $config['load_balancer']['setting']; + + $check_a = array(); + + foreach ((array)$config['load_balancer']['monitor_type'] as $type) { + switch ($type['type']) { + case 'icmp': + $mon = new ICMPMonitor($type['options']); + break; + case 'tcp': + $mon = new TCPMonitor($type['options']); + break; + case 'http': + $mon = new HTTPMonitor($type['options']); + break; + case 'https': + $mon = new HTTPSMonitor($type['options']); + break; + case 'send': + $mon = new SendMonitor($type['options']); + break; + } + if ($mon) { + $check_a[$type['name']] = $mon->p(); + } + } + + + $fd = fopen("{$g['varetc_path']}/relayd.conf", "w"); + $conf .= "log updates \n"; + + /* Global timeout, interval and prefork settings + if not specified by the user: + - use a 1000 ms timeout value as in pfsense 2.0.1 and above + - leave interval and prefork empty, relayd will use its default values */ + + if (isset($setting['timeout']) && !empty($setting['timeout'])) { + $conf .= "timeout ".$setting['timeout']." \n"; + } else { + $conf .= "timeout 1000 \n"; + } + + if (isset($setting['interval']) && !empty($setting['interval'])) { + $conf .= "interval ".$setting['interval']." \n"; + } + + if (isset($setting['prefork']) && !empty($setting['prefork'])) { + $conf .= "prefork ".$setting['prefork']." \n"; + } + + /* reindex pools by name as we loop through the pools array */ + $pools = array(); + /* Virtual server pools */ + if (is_array($pool_a)) { + for ($i = 0; isset($pool_a[$i]); $i++) { + if (is_array($pool_a[$i]['servers'])) { + if (!empty($pool_a[$i]['retry'])) { + $retrytext = " retry {$pool_a[$i]['retry']}"; + } else { + $retrytext = ""; + } + $conf .= "table <{$pool_a[$i]['name']}> {\n"; + foreach ($pool_a[$i]['servers'] as $server) { + if (is_subnetv4($server)) { + foreach (subnetv4_expand($server) as $ip) { + $conf .= "\t{$ip}{$retrytext}\n"; + } + } else { + $conf .= "\t{$server}{$retrytext}\n"; + } + } + $conf .= "}\n"; + /* Index by name for easier fetching when we loop through the virtual servers */ + $pools[$pool_a[$i]['name']] = $pool_a[$i]; + } + } + } +// if (is_array($protocol_a)) { +// for ($i = 0; isset($protocol_a[$i]); $i++) { +// $proto = "{$protocol_a[$i]['type']} protocol \"{$protocol_a[$i]['name']}\" {\n"; +// if (is_array($protocol_a[$i]['lbaction'])) { +// if ($protocol_a[$i]['lbaction'][0] == "") { +// continue; +// } +// for ($a = 0; isset($protocol_a[$i]['lbaction'][$a]); $a++) { +// $proto .= " " . echo_lbaction($protocol_a[$i]['lbaction'][$a]) . "\n"; +// } +// } +// $proto .= "}\n"; +// $conf .= $proto; +// } +// } + + $conf .= "dns protocol \"dnsproto\" {\n"; + $conf .= "\t" . "tcp { nodelay, sack, socket buffer 1024, backlog 1000 }\n"; + $conf .= "}\n"; + + if (is_array($vs_a)) { + for ($i = 0; isset($vs_a[$i]); $i++) { + + $append_port_to_name = false; + if (is_alias($pools[$vs_a[$i]['poolname']]['port'])) { + $dest_port_array = filter_expand_alias_array($pools[$vs_a[$i]['poolname']]['port']); + $append_port_to_name = true; + } else { + $dest_port_array = array($pools[$vs_a[$i]['poolname']]['port']); + } + if (is_alias($vs_a[$i]['port'])) { + $src_port_array = filter_expand_alias_array($vs_a[$i]['port']); + $append_port_to_name = true; + } else if ($vs_a[$i]['port']) { + $src_port_array = array($vs_a[$i]['port']); + } else { + $src_port_array = $dest_port_array; + } + + $append_ip_to_name = false; + if (is_alias($vs_a[$i]['ipaddr'])) { + $ip_list = array(); + foreach (filter_expand_alias_array($vs_a[$i]['ipaddr']) as $item) { + log_error("item is $item"); + if (is_subnetv4($item)) { + $ip_list = array_merge($ip_list, subnetv4_expand($item)); + } else { + $ip_list[] = $item; + } + } + $append_ip_to_name = true; + } else if (is_subnetv4($vs_a[$i]['ipaddr'])) { + $ip_list = subnetv4_expand($vs_a[$i]['ipaddr']); + $append_ip_to_name = true; + } else { + $ip_list = array($vs_a[$i]['ipaddr']); + } + + for ($j = 0; $j < count($ip_list); $j += 1) { + $ip = $ip_list[$j]; + for ($k = 0; $k < count($src_port_array) && $k < count($dest_port_array); $k += 1) { + $src_port = $src_port_array[$k]; + $dest_port = $dest_port_array[$k]; + if (is_portrange($dest_port)) { + $dest_ports = explode(':', $dest_port); + $dest_port = $dest_ports[0]; + } + + $name = $vs_a[$i]['name']; + if ($append_ip_to_name) { + $name .= "_" . $j; + } + if ($append_port_to_name) { + $name .= "_" . str_replace(":", "_", $src_port); + } + + if (($vs_a[$i]['mode'] == 'relay') || ($vs_a[$i]['relay_protocol'] == 'dns')) { + $conf .= "relay \"{$name}\" {\n"; + $conf .= " listen on {$ip} port {$src_port}\n"; + + if ($vs_a[$i]['relay_protocol'] == "dns") { + $conf .= " protocol \"dnsproto\"\n"; + } else { + $conf .= " protocol \"{$vs_a[$i]['relay_protocol']}\"\n"; + } + $lbmode = ""; + if ($pools[$vs_a[$i]['poolname']]['mode'] == "loadbalance") { + $lbmode = "mode loadbalance"; + } + + $conf .= " forward to <{$vs_a[$i]['poolname']}> port {$dest_port} {$lbmode} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n"; + + if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0 && ($vs_a[$i]['relay_protocol'] != 'dns')) { + $conf .= " forward to <{$vs_a[$i]['sitedown']}> port {$dest_port} {$lbmode} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n"; + } + $conf .= "}\n"; + } else { + $conf .= "redirect \"{$name}\" {\n"; + $conf .= " listen on {$ip} port {$src_port}\n"; + $conf .= " forward to <{$vs_a[$i]['poolname']}> port {$dest_port} {$check_a[$pools[$vs_a[$i]['poolname']]['monitor']]} \n"; + + if (isset($config['system']['lb_use_sticky'])) { + $conf .= " sticky-address\n"; + } + + /* sitedown MUST use the same port as the primary pool - sucks, but it's a relayd thing */ + if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0 && ($vs_a[$i]['relay_protocol'] != 'dns')) { + $conf .= " forward to <{$vs_a[$i]['sitedown']}> port {$dest_port} {$check_a[$pools[$vs_a[$i]['sitedown']]['monitor']]} \n"; + } + + $conf .= "}\n"; + } + } + } + } + } + fwrite($fd, $conf); + fclose($fd); + + if (is_process_running('relayd')) { + if (!empty($vs_a)) { + if ($kill_first) { + mwexec('pkill relayd'); + /* Remove all active relayd anchors now that relayd is no longer running. */ + cleanup_lb_anchor("*"); + mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf"); + } else { + // it's running and there is a config, just reload + mwexec("/usr/local/sbin/relayctl reload"); + } + } else { + /* + * XXX: Something breaks our control connection with relayd + * and makes 'relayctl stop' not work + * rule reloads are the current suspect + * mwexec('/usr/local/sbin/relayctl stop'); + * returns "command failed" + */ + mwexec('pkill relayd'); + /* Remove all active relayd anchors now that relayd is no longer running. */ + cleanup_lb_anchor("*"); + } + } else { + if (!empty($vs_a)) { + // not running and there is a config, start it + /* Remove all active relayd anchors so it can start fresh. */ + cleanup_lb_anchor("*"); + mwexec("/usr/local/sbin/relayd -f {$g['varetc_path']}/relayd.conf"); + } + } +} + +function get_lb_redirects() { +/* +# relayctl show summary +Id Type Name Avlblty Status +1 redirect testvs2 active +5 table test2:80 active (3 hosts up) +11 host 192.168.1.2 91.55% up +10 host 192.168.1.3 100.00% up +9 host 192.168.1.4 88.73% up +3 table test:80 active (1 hosts up) +7 host 192.168.1.2 66.20% down +6 host 192.168.1.3 97.18% up +0 redirect testvs active +3 table test:80 active (1 hosts up) +7 host 192.168.1.2 66.20% down +6 host 192.168.1.3 97.18% up +4 table testvs-sitedown:80 active (1 hosts up) +8 host 192.168.1.4 84.51% up +# relayctl show redirects +Id Type Name Avlblty Status +1 redirect testvs2 active +0 redirect testvs active +# relayctl show redirects +Id Type Name Avlblty Status +1 redirect testvs2 active + total: 2 sessions + last: 2/60s 2/h 2/d sessions + average: 1/60s 0/h 0/d sessions +0 redirect testvs active +*/ + $rdr_a = array(); + exec('/usr/local/sbin/relayctl show redirects 2>&1', $rdr_a); + $relay_a = array(); + exec('/usr/local/sbin/relayctl show relays 2>&1', $relay_a); + $vs = array(); + $cur_entry = ""; + for ($i = 0; isset($rdr_a[$i]); $i++) { + $line = $rdr_a[$i]; + if (preg_match("/^[0-9]+/", $line)) { + $regs = array(); + if ($x = preg_match("/^[0-9]+\s+redirect\s+([^\s]+)\s+([^\s]+)/", $line, $regs)) { + $cur_entry = trim($regs[1]); + $vs[trim($regs[1])] = array(); + $vs[trim($regs[1])]['status'] = trim($regs[2]); + } + } elseif (($x = preg_match("/^\s+total:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) { + $vs[$cur_entry]['total'] = trim($regs[1]); + } elseif (($x = preg_match("/^\s+last:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) { + $vs[$cur_entry]['last'] = trim($regs[1]); + } elseif (($x = preg_match("/^\s+average:(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) { + $vs[$cur_entry]['average'] = trim($regs[1]); + } + } + $cur_entry = ""; + for ($i = 0; isset($relay_a[$i]); $i++) { + $line = $relay_a[$i]; + if (preg_match("/^[0-9]+/", $line)) { + $regs = array(); + if ($x = preg_match("/^[0-9]+\s+relay\s+([^\s]+)\s+([^\s]+)/", $line, $regs)) { + $cur_entry = trim($regs[1]); + $vs[trim($regs[1])] = array(); + $vs[trim($regs[1])]['status'] = trim($regs[2]); + } + } elseif (($x = preg_match("/^\s+total:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) { + $vs[$cur_entry]['total'] = trim($regs[1]); + } elseif (($x = preg_match("/^\s+last:\s(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) { + $vs[$cur_entry]['last'] = trim($regs[1]); + } elseif (($x = preg_match("/^\s+average:(.*)\ssessions/", $line, $regs)) && !empty($cur_entry)) { + $vs[$cur_entry]['average'] = trim($regs[1]); + } + } + return $vs; +} + +function get_lb_summary() { + $relayctl = array(); + exec('/usr/local/sbin/relayctl show summary 2>&1', $relayctl); + $relay_hosts=Array(); + foreach ((array) $relayctl as $line) { + $t = explode("\t", $line); + switch (trim($t[1])) { + case "table": + $curpool=trim($t[2]); + break; + case "host": + $curhost=trim($t[2]); + $relay_hosts[$curpool][$curhost]['avail']=trim($t[3]); + $relay_hosts[$curpool][$curhost]['state']=trim($t[4]); + break; + } + } + return $relay_hosts; +} + +/* Get a list of all relayd virtual server anchors */ +function get_lb_anchors() { + /* NOTE: These names come back prepended with "relayd/" e.g. "relayd/MyVSName" */ + return explode("\n", trim(`/sbin/pfctl -sA -a relayd | /usr/bin/awk '{print $1;}'`)); +} + +/* Remove NAT rules from a relayd anchor that is no longer in use. + $anchorname can either be * to clear all anchors or a specific anchor name.*/ +function cleanup_lb_anchor($anchorname = "*") { + $lbanchors = get_lb_anchors(); + foreach ($lbanchors as $lba) { + if (($anchorname == "*") || ($lba == "relayd/{$anchorname}")) { + /* Flush both the NAT and the Table for the anchor, so it will be completely removed by pf. */ + mwexec("/sbin/pfctl -a " . escapeshellarg($lba) . " -F nat"); + mwexec("/sbin/pfctl -a " . escapeshellarg($lba) . " -F Tables"); + } + } +} + +/* Mark an anchor for later cleanup. This will allow us to remove an old VS name */ +function cleanup_lb_mark_anchor($name) { + global $g; + /* Nothing to do! */ + if (empty($name)) { + return; + } + $filename = "{$g['tmp_path']}/relayd_anchors_remove"; + $cleanup_anchors = array(); + /* Read in any currently unapplied name changes */ + if (file_exists($filename)) { + $cleanup_anchors = explode("\n", file_get_contents($filename)); + } + /* Only add the anchor to the list if it's not already there. */ + if (!in_array($name, $cleanup_anchors)) { + $cleanup_anchors[] = $name; + } + file_put_contents($filename, implode("\n", $cleanup_anchors)); +} + +/* Cleanup relayd anchors that have been marked for cleanup. */ +function cleanup_lb_marked() { + global $g, $config; + $filename = "{$g['tmp_path']}/relayd_anchors_remove"; + $cleanup_anchors = array(); + /* Nothing to do! */ + if (!file_exists($filename)) { + return; + } else { + $cleanup_anchors = explode("\n", file_get_contents($filename)); + /* Nothing to do! */ + if (empty($cleanup_anchors)) { + return; + } + } + + /* Load current names so we can make sure we don't remove an anchor that is still in use. */ + $vs_a = $config['load_balancer']['virtual_server']; + $active_vsnames = array(); + if (is_array($vs_a)) { + foreach ($vs_a as $vs) { + $active_vsnames[] = $vs['name']; + } + } + + foreach ($cleanup_anchors as $anchor) { + /* Only cleanup an anchor if it is not still active. */ + if (!in_array($anchor, $active_vsnames)) { + cleanup_lb_anchor($anchor); + } + } + unlink_if_exists($filename); +} + +?> diff --git a/src/etc/inc/wizardapp.inc b/src/etc/inc/wizardapp.inc new file mode 100644 index 0000000..bf9f699 --- /dev/null +++ b/src/etc/inc/wizardapp.inc @@ -0,0 +1,668 @@ +<?php +/* + wizardapp.inc + part of pfSense (https://www.pfsense.org/) + + Copyright (C) 2006 Bill Marquette - bill.marquette@gmail.com. + Copyright (C) 2006 Scott Ullrich - sullrich@pfsense.com. + Copyright (C) 2008-2010 Ermal Luçi + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$gamesplist = array(); + +/* Game Consoles and Game Clients */ + +$gamesplist['playstationconsoles'] = array(); + /* Playstation 3, Playstation 4 and PS Vita */ + $gamesplist['playstationconsoles'][] = array('PS-Network-TCP', 'tcp', '10040', '10060', 'both'); + $gamesplist['playstationconsoles'][] = array('PS-Network-UDP', 'udp', '50000', '60000', 'both'); + $gamesplist['playstationconsoles'][] = array('PS-Home-TCP-1', 'tcp', '3478', '3480', 'both'); + $gamesplist['playstationconsoles'][] = array('PS-Home-TCP-2', 'tcp', '8080', '8080', 'both'); + $gamesplist['playstationconsoles'][] = array('PS-TCP-1', 'tcp', '5223', '5223', 'both'); + $gamesplist['playstationconsoles'][] = array('PS-TCP-2', 'tcp', '10070', '10080', 'both'); + $gamesplist['playstationconsoles'][] = array('PS-UDP-1', 'udp', '3478', '3479', 'both'); + $gamesplist['playstationconsoles'][] = array('PS-UDP-2', 'udp', '3658', '3658', 'both'); + $gamesplist['playstationconsoles'][] = array('PS-UDP-3', 'udp', '10070', '10070', 'both'); + $gamesplist['playstationconsoles'][] = array('PS-RemotePlay', 'tcp', '9293', '9293', 'both'); + +$gamesplist['wiiconsoles'] = array(); + /* XBox Consoles */ + $gamesplist['wiiconsoles'][] = array('Wii-Consoles-TCP-1', 'tcp', '6667', '6667', 'both'); + $gamesplist['wiiconsoles'][] = array('Wii-Consoles-TCP-2', 'tcp', '12400', '12400', 'both'); + $gamesplist['wiiconsoles'][] = array('Wii-Consoles-TCP-3', 'tcp', '28910', '28910', 'both'); + $gamesplist['wiiconsoles'][] = array('Wii-Consoles-TCP-4', 'tcp', '29900', '29901', 'both'); + $gamesplist['wiiconsoles'][] = array('Wii-Consoles-TCP-5', 'tcp', '29920', '29920', 'both'); + +$gamesplist['xboxconsoles'] = array(); + /* XBox Consoles */ + $gamesplist['xboxconsoles'][] = array('xbox-Consoles-UDP-1', 'udp', '88', '88', 'both'); + $gamesplist['xboxconsoles'][] = array('xbox-Consoles-UDP-2', 'udp', '3074', '3074', 'both'); + $gamesplist['xboxconsoles'][] = array('xbox-Consoles-TCP-1', 'tcp', '3074', '3074', 'both'); + $gamesplist['xboxconsoles'][] = array('xbox-Consoles-TCP-2', 'tcp', '3659', '3659', 'both'); + $gamesplist['xboxconsoles'][] = array('xbox-Consoles-TCP-3', 'tcp', '500', '500', 'both'); + $gamesplist['xboxconsoles'][] = array('xbox-Consoles-TCP-4', 'tcp', '3544', '3544', 'both'); + $gamesplist['xboxconsoles'][] = array('xbox-Consoles-TCP-5', 'tcp', '4500', '4500', 'both'); + +$gamesplist['battlenet'] = array(); + /* Blizzard Publishing games */ + $gamesplist['battlenet'][] = array('Battle.NET-game1-tcp', 'tcp', '6112', '6119', 'both'); //diablo, diablo2, starcraft, warcraft 2, warcraft 3 + $gamesplist['battlenet'][] = array('Battle.NET-game1-udp', 'udp', '6112', '6119', 'both'); //diablo, diablo2, starcraft, warcraft 2 + $gamesplist['battlenet'][] = array('Battle.NET-diablo2', 'tcp', '4000', '4000', 'both'); //diablo2 + $gamesplist['battlenet'][] = array('Battle.NET-game2', 'tcp', '1119', '1119', 'both'); //diablo3, starcraft 2 + $gamesplist['battlenet'][] = array('Battle.NET-game3', 'tcp', '3724', '3724', 'both'); //starcraft2 + +$gamesplist['eaorigin'] = array(); + /* EA Origin Client */ + $gamesplist['eaorigin'][] = array('EA-Origin-TCP-1', 'tcp', '1024', '1124', 'both'); + $gamesplist['eaorigin'][] = array('EA-Origin-TCP-2', 'tcp', '9960', '9969', 'both'); + $gamesplist['eaorigin'][] = array('EA-Origin-TCP-3', 'tcp', '18000', '18000', 'both'); + $gamesplist['eaorigin'][] = array('EA-Origin-TCP-4', 'tcp', '18120', '18120', 'both'); + $gamesplist['eaorigin'][] = array('EA-Origin-TCP-5', 'tcp', '18060', '18060', 'both'); + $gamesplist['eaorigin'][] = array('EA-Origin-TCP-6', 'tcp', '27900', '27900', 'both'); + $gamesplist['eaorigin'][] = array('EA-Origin-TCP-7', 'tcp', '28910', '28910', 'both'); + $gamesplist['eaorigin'][] = array('EA-Origin-TCP-8', 'tcp', '29900', '29900', 'both'); + $gamesplist['eaorigin'][] = array('EA-Origin-UDP-1', 'udp', '1024', '1124', 'both'); + $gamesplist['eaorigin'][] = array('EA-Origin-UDP-2', 'udp', '18000', '18000', 'both'); + $gamesplist['eaorigin'][] = array('EA-Origin-UDP-3', 'udp', '29900', '29900', 'both'); + +$gamesplist['steam'] = array(); + /* Steam Games */ + $gamesplist['steam'][] = array('Steam-game-udp', 'udp', '27000', '27030', 'both'); //america's army 3, cs:s, cs:go, HL2, COD: Black Ops, COD: Black Ops 2, Natural Selection 2 + $gamesplist['steam'][] = array('Steam-game-tcp', 'tcp', '27000', '27030', 'both'); //america's army 3, cs:s, cs:go, HL2, COD: Black Ops, COD: Black Ops 2, Natural Selection 2 + $gamesplist['steam'][] = array('Steam-hltv', 'udp', '27015', '27030', 'both'); + $gamesplist['steam'][] = array('Steam-1', 'udp', '4380', '4380', 'both'); + $gamesplist['steam'][] = array('Steam-2', 'udp', '1200', '1200', 'both'); + $gamesplist['steam'][] = array('Steam-voice', 'udp', '3478', '3480', 'both'); + +$gamesplist['gamesforwindowslive'] = array(); + /* Games for Windows Live */ + $gamesplist['gamesforwindowslive'][] = array('Games4WinLive-1', 'udp', '88', '88', 'both'); + $gamesplist['gamesforwindowslive'][] = array('Games4WinLive-2', 'udp', '3074', '3074', 'both'); + $gamesplist['gamesforwindowslive'][] = array('Games4WinLive-3', 'tcp', '3074', '3074', 'both'); + +/* Games */ + +$gamesplist['arma2'] = array(); + /* ARMA 2 */ + $gamesplist['arma2'][] = array('arma2', 'udp', '2302', '2310', 'both'); + +$gamesplist['arma3'] = array(); + /* ARMA 3 */ + $gamesplist['arma3'][] = array('arma3-game-traffic', 'udp', '2302', '2302', 'both'); + $gamesplist['arma3'][] = array('arma3-steam-query', 'udp', '2303', '2303', 'both'); + $gamesplist['arma3'][] = array('arma3-steam-port', 'udp', '2304', '2304', 'both'); + $gamesplist['arma3'][] = array('arma3-BattleEye-1', 'tcp', '2345', '2345', 'both'); + $gamesplist['arma3'][] = array('arma3-BattleEye-2', 'tcp', '2344', '2344', 'both'); + $gamesplist['arma3'][] = array('arma3-BattleEye-2', 'udp', '2344', '2344', 'both'); + +$gamesplist['battlefield2'] = array(); + /* Battlefield 2 */ + $gamesplist['battlefield2'][] = array('BF2-1500-4999', 'udp', '1500', '4999', 'both'); + $gamesplist['battlefield2'][] = array('BF2-4711', 'tcp', '4711', '4711', 'both'); + $gamesplist['battlefield2'][] = array('BF2-16567', 'udp', '16567', '16567', 'both'); + $gamesplist['battlefield2'][] = array('BF2-27900', 'udp', '27900', '27900', 'both'); + $gamesplist['battlefield2'][] = array('BF2-28910', 'tcp', '28910', '28910', 'both'); + $gamesplist['battlefield2'][] = array('BF2-29900-29901-UDP', 'udp', '29900', '29901', 'both'); + $gamesplist['battlefield2'][] = array('BF2-29900-29901-TCP', 'tcp', '29900', '29901', 'both'); + $gamesplist['battlefield2'][] = array('BF2-27900', 'udp', '27900', '27900', 'both'); + $gamesplist['battlefield2'][] = array('BF2-55123-55125', 'udp', '55123', '55125', 'both'); + +$gamesplist['battlefield3'] = array(); + /* Battlefield 3 and Battlefield 4 */ + $gamesplist['battlefield3'][] = array('BF3-1', 'tcp', '9988', '9988', 'both'); + $gamesplist['battlefield3'][] = array('BF3-2', 'tcp', '20000', '20100', 'both'); + $gamesplist['battlefield3'][] = array('BF3-3', 'tcp', '22990', '22990', 'both'); + $gamesplist['battlefield3'][] = array('BF3-4', 'tcp', '17502', '17502', 'both'); + $gamesplist['battlefield3'][] = array('BF3-5', 'tcp', '42127', '42127', 'both'); + $gamesplist['battlefield3'][] = array('BF3-6', 'udp', '3659', '3659', 'both'); + $gamesplist['battlefield3'][] = array('BF3-7', 'udp', '14000', '14016', 'both'); + $gamesplist['battlefield3'][] = array('BF3-8', 'udp', '22990', '23006', 'both'); + $gamesplist['battlefield3'][] = array('BF3-9', 'udp', '25200', '25300', 'both'); + $gamesplist['battlefield3'][] = array('BF3-PS-1', 'tcp', '10000', '10100', 'both'); + $gamesplist['battlefield3'][] = array('BF3-PS-2', 'tcp', '1935', '1935', 'both'); + + +$gamesplist['battlefieldbc2'] = array(); + /* Battlefield Bad Company 2 */ + $gamesplist['battlefieldbc2'][] = array('BFBC2-1', 'tcp', '18390', '18390', 'both'); + $gamesplist['battlefieldbc2'][] = array('BFBC2-2', 'tcp', '18395', '18395', 'both'); + $gamesplist['battlefieldbc2'][] = array('BFBC2-3', 'udp', '18395', '18395', 'both'); + $gamesplist['battlefieldbc2'][] = array('BFBC2-4', 'tcp', '13505', '13505', 'both'); + +$gamesplist['borderlands'] = array(); + /* Borderlands */ + $gamesplist['borderlands'][] = array('Borderlands-udp', 'udp', '7777', '7777', 'both'); + $gamesplist['borderlands'][] = array('Borderlands-tcp', 'tcp', '7777', '7777', 'both'); + +$gamesplist['callofduty'] = array(); + /* Call Of Duty */ + $gamesplist['callofduty'][] = array('CallOfDuty1', 'tcp', '28960', '28960', 'both'); + $gamesplist['callofduty'][] = array('CallOfDuty2', 'udp', '28960', '28960', 'both'); + +$gamesplist['counterstrike'] = array(); + /* counter strike */ + $gamesplist['counterstrike'][] = array('CS-Titan', 'udp', '6003', '6003', 'both'); + $gamesplist['counterstrike'][] = array('CS-Authentication', 'udp', '7002', '7002', 'both'); + $gamesplist['counterstrike'][] = array('CS-Client', 'udp', '6003', '6003', 'both'); + $gamesplist['counterstrike'][] = array('CS-Masterserver', 'udp', '27010', '27010', 'both'); + $gamesplist['counterstrike'][] = array('CS-Mod-Server', 'udp', '27011', '27011', 'both'); + $gamesplist['counterstrike'][] = array('CS-Chat', 'udp', '27012', '27012', 'both'); + $gamesplist['counterstrike'][] = array('CS-HL-Serverport1', 'udp', '27013', '27013', 'both'); + $gamesplist['counterstrike'][] = array('CS-HL-Serverport2', 'udp', '27014', '27014', 'both'); + $gamesplist['counterstrike'][] = array('CS-HL-Serverport', 'udp', '27015', '27015', 'both'); + +$gamesplist['crysis2'] = array(); + /* Crysis 2 */ + $gamesplist['crysis2'][] = array('Crysis2', 'udp', '64100', '64100', 'both'); + +$gamesplist['crysis3'] = array(); + /* Crysis 3 */ + $gamesplist['crysis3'][] = array('Crysis3-TCP-1', 'tcp', '9988', '9988', 'both'); + $gamesplist['crysis3'][] = array('Crysis3-TCP-2', 'tcp', '17502', '17502', 'both'); + $gamesplist['crysis3'][] = array('Crysis3-TCP-3', 'tcp', '25650', '25780', 'both'); + $gamesplist['crysis3'][] = array('Crysis3-TCP-4', 'tcp', '42127', '42127', 'both'); + $gamesplist['crysis3'][] = array('Crysis3-TCP-5', 'tcp', '64100', '64110', 'both'); + $gamesplist['crysis3'][] = array('Crysis3-UDP-1', 'udp', '3659', '3659', 'both'); + $gamesplist['crysis3'][] = array('Crysis3-UDP-2', 'udp', '10000', '10100', 'both'); + $gamesplist['crysis3'][] = array('Crysis3-UDP-3', 'udp', '25650', '25780', 'both'); + $gamesplist['crysis3'][] = array('Crysis3-UDP-4', 'udp', '64100', '64110', 'both'); + +$gamesplist['deadspace2'] = array(); + /* Dead Space 2 */ + $gamesplist['deadspace2'][] = array('DeadSpace2-TCP-1', 'tcp', '28910', '28910', 'both'); + $gamesplist['deadspace2'][] = array('DeadSpace2-TCP-2', 'tcp', '29900', '29901', 'both'); + $gamesplist['deadspace2'][] = array('DeadSpace2-UDP-1', 'udp', '8088', '28088', 'both'); + +$gamesplist['deadspace3'] = array(); + /* Dead Space 3 */ + $gamesplist['deadspace3'][] = array('DeadSpace3-TCP-1', 'tcp', '1024', '1124', 'both'); + $gamesplist['deadspace3'][] = array('DeadSpace3-TCP-2', 'tcp', '9960', '9969', 'both'); + $gamesplist['deadspace3'][] = array('DeadSpace3-TCP-3', 'tcp', '18000', '18000', 'both'); + $gamesplist['deadspace3'][] = array('DeadSpace3-TCP-4', 'tcp', '18120', '18120', 'both'); + $gamesplist['deadspace3'][] = array('DeadSpace3-TCP-5', 'tcp', '18060', '18060', 'both'); + $gamesplist['deadspace3'][] = array('DeadSpace3-TCP-6', 'tcp', '27900', '27900', 'both'); + $gamesplist['deadspace3'][] = array('DeadSpace3-TCP-7', 'tcp', '28910', '28910', 'both'); + $gamesplist['deadspace3'][] = array('DeadSpace3-TCP-8', 'tcp', '29900', '29900', 'both'); + $gamesplist['deadspace3'][] = array('DeadSpace3-UDP-1', 'udp', '1024', '1124', 'both'); + $gamesplist['deadspace3'][] = array('DeadSpace3-UDP-2', 'udp', '18000', '18000', 'both'); + $gamesplist['deadspace3'][] = array('DeadSpace3-UDP-3', 'udp', '29900', '29900', 'both'); + +$gamesplist['deltaforce'] = array(); + /* delta force */ + $gamesplist['deltaforce'][] = array('Delta1', 'udp', '17478', '17488', 'both'); + +$gamesplist['dirt3'] = array(); + /* ARMA 2 */ + $gamesplist['dirt3'][] = array('Dirt3-1', 'tcp', '2300', '2400', 'both'); + $gamesplist['dirt3'][] = array('Dirt3-2', 'udp', '2300', '2400', 'both'); + $gamesplist['dirt3'][] = array('Dirt3-3', 'udp', '6073', '6073', 'both'); + $gamesplist['dirt3'][] = array('Dirt3-4', 'tcp', '47624', '47624', 'both'); + +$gamesplist['doom3'] = array(); + /* doom3 */ + $gamesplist['doom3'][] = array('DOOM3-1', 'udp', '27650', '27650', 'both'); + $gamesplist['doom3'][] = array('DOOM3-2', 'udp', '27666', '27666', 'both'); + +$gamesplist['dragonage2'] = array(); + /* Dragon Age 2 */ + $gamesplist['dragonage2'][] = array('DragonAge2-TCP-1', 'tcp', '8000', '8000', 'both'); + $gamesplist['dragonage2'][] = array('DragonAge2-TCP-2', 'tcp', '12025', '12025', 'both'); + $gamesplist['dragonage2'][] = array('DragonAge2-TCP-3', 'tcp', '15101', '15325', 'both'); + $gamesplist['dragonage2'][] = array('DragonAge2-TCP-4', 'tcp', '18081', '18081', 'both'); + $gamesplist['dragonage2'][] = array('DragonAge2-TCP-5', 'tcp', '42127', '42127', 'both'); + $gamesplist['dragonage2'][] = array('DragonAge2-UDP-1', 'udp', '1900', '1900', 'both'); + $gamesplist['dragonage2'][] = array('DragonAge2-UDP-2', 'udp', '5355', '5355', 'both'); + $gamesplist['dragonage2'][] = array('DragonAge2-UDP-3', 'udp', '8001', '8001', 'both'); + +$gamesplist['empireearth'] = array(); + /* empire earth */ + $gamesplist['empireearth'][] = array('EmpireEarth-1', 'tcp', '33335', '33336', 'both'); + $gamesplist['empireearth'][] = array('EmpireEarth-2', 'udp', '33334', '33334', 'both'); + +$gamesplist['eveonline'] = array(); + /* EVE Online */ + $gamesplist['eveonline'][] = array('EVEOnline-tcp', 'tcp', '26000', '26000', 'both'); + $gamesplist['eveonline'][] = array('EVEOnline-udp', 'udp', '26000', '26000', 'both'); + $gamesplist['eveonline'][] = array('EVEOnline-alternate-tcp', 'tcp', '3724', '3724', 'both'); + $gamesplist['eveonline'][] = array('EVEOnline-alternate-udp', 'udp', '3724', '3724', 'both'); + +$gamesplist['everquest'] = array(); + /* everquest */ + $gamesplist['everquest'][] = array('Everquest-1', 'tcp', '1024', '6000', 'both'); + $gamesplist['everquest'][] = array('Everquest-2', 'tcp', '7000', '7000', 'both'); + $gamesplist['everquest'][] = array('Everquest-3', 'udp', '1024', '6000', 'both'); + $gamesplist['everquest'][] = array('Everquest-4', 'udp', '7000', '7000', 'both'); + +$gamesplist['everquest2'] = array(); + /* everquest2 */ + $gamesplist['everquest2'][] = array('Everquest2-1', 'tcp', '7000', '7000', 'both'); + $gamesplist['everquest2'][] = array('Everquest2-2', 'udp', '3016', '3021', 'both'); + $gamesplist['everquest2'][] = array('Everquest2-3', 'udp', '9100', '9100', 'both'); + $gamesplist['everquest2'][] = array('Everquest2-4', 'udp', '9700', '9703', 'both'); + $gamesplist['everquest2'][] = array('Everquest2-5', 'udp', '32800', '33000', 'both'); + +$gamesplist['farcry'] = array(); + /* far cry */ + $gamesplist['farcry'][] = array('FarCry-1', 'tcp', '49001', '49002', 'both'); + $gamesplist['farcry'][] = array('FarCry-2', 'udp', '49001', '49002', 'both'); + +$gamesplist['farcry2'] = array(); + /* FarCry 2*/ + $gamesplist['farcry2'][] = array('FarCry2-tcp', 'tcp', '9000', '9004', 'both'); + $gamesplist['farcry2'][] = array('FarCry2-udp', 'udp', '9000', '9004', 'both'); + +$gamesplist['farcry3'] = array(); + /* FarCry 3*/ + $gamesplist['farcry3'][] = array('FarCry3-game', 'udp', '9000', '9000', 'both'); + $gamesplist['farcry3'][] = array('FarCry3-punkbuster', 'udp', '10009', '10009', 'both'); + +$gamesplist['gunzonline'] = array(); + /* GunZ Online */ + $gamesplist['gunzonline'][] = array('GunZOnline', 'udp', '7700', '7700', 'both'); + +$gamesplist['halflife'] = array(); + /* halflife */ + $gamesplist['halflife'][] = array('HL-1', 'tcp', '27015', '27015', 'both'); + $gamesplist['halflife'][] = array('HL-2', 'udp', '27650', '27650', 'both'); + $gamesplist['halflife'][] = array('HL-3', 'udp', '27666', '27666', 'both'); + +$gamesplist['leagueoflegends'] = array(); + /* League of Legends */ + $gamesplist['leagueoflegends'][] = array('LeagueofLegends-1', 'udp', '5000', '5500', 'both'); + $gamesplist['leagueoflegends'][] = array('LeagueofLegends-2', 'tcp', '2099', '2099', 'both'); + $gamesplist['leagueoflegends'][] = array('LeagueofLegends-3', 'tcp', '5222', '5223', 'both'); + +$gamesplist['lineage2'] = array(); + /* Lineage II */ + $gamesplist['lineage2'][] = array('Lineage2-2009', 'tcp', '2009', '2009', 'both'); + $gamesplist['lineage2'][] = array('Lineage2-2106', 'tcp', '2106', '2106', 'both'); + $gamesplist['lineage2'][] = array('Lineage2-7777', 'tcp', '7777', '7777', 'both'); + +$gamesplist['masseffect3'] = array(); + /* MassEffect 3 */ + $gamesplist['masseffect3'][] = array('MassEffect3-UDP-1', 'udp', '5659', '5659', 'both'); + $gamesplist['masseffect3'][] = array('MassEffect3-UDP-1', 'udp', '6000', '6000', 'both'); + +$gamesplist['mechwarrioronline'] = array(); + /* MechWarrior: Online */ + $gamesplist['mechwarrioronline'][] = array('MechWarriorOnline-tcp1', 'tcp', '45461', '45461', 'both'); + $gamesplist['mechwarrioronline'][] = array('MechWarriorOnline-tcp2', 'tcp', '45464', '45464', 'both'); + $gamesplist['mechwarrioronline'][] = array('MechWarriorOnline-game', 'udp', '21000', '30000', 'both'); // 9000 ports + +$gamesplist['minecraft'] = array(); + /* Minecraft */ + $gamesplist['minecraft'][] = array('Minecraft-tcp', 'tcp', '25565', '25565', 'both'); + $gamesplist['minecraft'][] = array('Minecraft-udp', 'udp', '25565', '25565', 'both'); + +$gamesplist['operationflashpoint-dr'] = array(); + /* Operation Flashpoint: Dragon Rising */ + $gamesplist['operationflashpoint-dr'][] = array('OperationFlashpoint-DR', 'udp', '9105', '9105', 'both'); + +$gamesplist['planetside'] = array(); + /* PlanetSide */ + $gamesplist['planetside'][] = array('PlanetSide', 'tcp', '7000', '7000', 'both'); + $gamesplist['planetside'][] = array('PlanetSide', 'tcp', '7080', '7080', 'both'); + $gamesplist['planetside'][] = array('PlanetSide2', 'udp', '3016', '3021', 'both'); + $gamesplist['planetside'][] = array('PlanetSide2', 'udp', '45000', '45010', 'both'); + $gamesplist['planetside'][] = array('PlanetSide2', 'udp', '30000', '30500', 'both'); + +$gamesplist['planetside2'] = array(); + /* PlanetSide 2 */ + $gamesplist['planetside2'][] = array('PlanetSide2-game', 'udp', '20040', '20199', 'both'); + $gamesplist['planetside2'][] = array('PlanetSide2-voice', 'udp', '5062', '5062', 'both'); + + +$gamesplist['quakeiii'] = array(); + /* quake3 */ + $gamesplist['quakeiii'][] = array('Quake3', 'udp', '27910', '27919', 'both'); + +$gamesplist['quakeiv'] = array(); + /* quake4 */ + $gamesplist['quakeiv'][] = array('QuakeIV-server-udp', 'udp', '27650', '27650', 'both'); + $gamesplist['quakeiv'][] = array('QuakeIV-server-tcp', 'tcp', '27650', '27650', 'both'); + $gamesplist['quakeiv'][] = array('QuakeIV-client-udp', 'udp', '28004', '28004', 'both'); + $gamesplist['quakeiv'][] = array('QuakeIV-client-tcp', 'tcp', '28004', '28004', 'both'); + +$gamesplist['starwarstor'] = array(); + /* quake3 */ + $gamesplist['starwarstor'][] = array('StarWarsTOR-1', 'tcp', '8995', '8995', 'both'); + $gamesplist['starwarstor'][] = array('StarWarsTOR-2', 'tcp', '12000', '12999', 'both'); + $gamesplist['starwarstor'][] = array('StarWarsTOR-2', 'tcp', '20000', '30000', 'both'); + +$gamesplist['tigerwoods2004ps2'] = array(); + /* tiger woods 2004 ps2 */ + $gamesplist['tigerwoods2004ps2'][] = array('TigerWoods2004-Player', 'udp', '3658', '3658', 'both'); + $gamesplist['tigerwoods2004ps2'][] = array('TigerWoods2004-Player2', 'udp', '6000', '6000', 'both'); + $gamesplist['tigerwoods2004ps2'][] = array('TigerWoods2004-EA', 'tcp', '10300', '10301', 'both'); + +$gamesplist['tribesascend'] = array(); + /* Tribes Ascend */ + $gamesplist['tribesascend'][] = array('TribesAscend-tcp', 'tcp', '9000', '9001', 'both'); + $gamesplist['tribesascend'][] = array('TribesAscend-udp', 'udp', '9002', '9999', 'both'); + +$gamesplist['unrealtournament'] = array(); + /* Unreal Tournament */ + $gamesplist['unrealtournament'][] = array('UT-game-udp', 'udp', '7777', '7787', 'both'); + $gamesplist['unrealtournament'][] = array('UT-game-tcp', 'tcp', '7777', '7787', 'both'); + $gamesplist['unrealtournament'][] = array('UT-voice', 'udp', '3783', '3783', 'both'); + +$gamesplist['wolfet'] = array(); + /* wolfenstein enemy territory */ + $gamesplist['wolfet'][] = array('WolfET-1', 'tcp', '27960', '27960', 'both'); + +$gamesplist['wow'] = array(); + /* World of Warcraft */ + $gamesplist['wow'][] = array('WoW', 'tcp', '3724', '3724', 'both'); + $gamesplist['wow'][] = array('WoW-voice', 'udp', '1119', '1119', 'both'); + $gamesplist['wow'][] = array('WoW-voice', 'udp', '3724', '3724', 'both'); + +$voiplist = array(); + + /* asterisk server / same as vonage */ +$voiplist['Asterisk'] = array(); + $voiplist['Asterisk'][] = array('Asterisk', 'udp', '5060', '5069', 'both'); + $voiplist['Asterisk'][] = array('Asterisk', 'udp', '10000', '20000', 'both'); + + /* VoicePulse server */ +$voiplist['VoicePulse'] = array(); + $voiplist['VoicePulse'][] = array('VoicePulse', 'udp', '16384', '16482', 'both'); + $voiplist['VoicePulse'][] = array('VoicePulse', 'udp', '4569', '4569', 'both'); + + /* Panasonic Hybrid PBX */ +$voiplist['Panasonic'] = array(); + $voiplist['Panasonic'][] = array('Panasonic1', 'udp', '8000', '8063', 'both'); + $voiplist['Panasonic'][] = array('Panasonic2', 'udp', '9300', '9301', 'both'); + $voiplist['Panasonic'][] = array('Panasonic3', 'udp', '2747', '2747', 'both'); + + +$p2plist = array(); + /* To add p2p clients, push Descr,Protocol,Start,End,src/dest/both onto p2plist */ + $p2plist['aimster'] = array(); + $p2plist['aimster'][] = array('Aimster', 'tcp', '7668', '7668', 'both'); + $p2plist['bittorrent'] = array(); + $p2plist['bittorrent'][] = array('BitTorrent', 'tcp', '6881', '6999', 'both'); + $p2plist['bittorrent'][] = array('BitTorrent', 'udp', '6881', '6999', 'both'); + $p2plist['buddyshare'] = array(); + $p2plist['buddyshare'][] = array('BuddyShare', 'tcp', '7788', '7788', 'both'); + $p2plist['cutemx'] = array(); + $p2plist['cutemx'][] = array('CuteMX', 'tcp', '2340', '2340', 'both'); + $p2plist['dc++'] = array(); + $p2plist['dc++'][] = array('DC++', 'tcp', '1412', '1412', 'both'); + $p2plist['dcc'] = array(); + $p2plist['dcc'][] = array('dcc', 'tcp', '6666', '6668', 'both'); + $p2plist['directconnect'] = array(); + $p2plist['directconnect'][] = array('DirectConnect', 'tcp', '412', '412', 'both'); + $p2plist['directfileexpress'] = array(); + $p2plist['directfileexpress'][] = array('DirectFileExpress', 'tcp', '1044', '1045', 'both'); + $p2plist['edonkey2000'] = array(); + $p2plist['edonkey2000'][] = array('EDonkey2000', 'tcp', '4661', '4665', 'both'); + $p2plist['fastTrack'] = array(); + $p2plist['fastTrack'][] = array('FastTrack', 'tcp', '1214', '1214', 'both'); + $p2plist['gnutella'] = array(); + $p2plist['gnutella'][] = array('Gnutella-TCP', 'tcp', '6346', '6346', 'both'); + $p2plist['gnutella'][] = array('Gnutella-UDP', 'udp', '6346', '6346', 'both'); + $p2plist['grouper'] = array(); + $p2plist['grouper'][] = array('grouper', 'tcp', '8038', '8039', 'both'); + $p2plist['hotcomm'] = array(); + $p2plist['hotcomm'][] = array('hotComm', 'tcp', '28864', '28865', 'both'); + $p2plist['hotlineconnect'] = array(); + $p2plist['hotlineconnect'][] = array('HotlineConnect', 'tcp', '5500', '5503', 'both'); + $p2plist['imesh'] = array(); + $p2plist['imesh'][] = array('iMesh', 'tcp', '4329', '4329', 'both'); + $p2plist['napster'] = array(); + $p2plist['napster'][] = array('Napster', 'tcp', '6699', '6701', 'both'); + $p2plist['opennap'] = array(); + $p2plist['opennap'][] = array('OpenNap', 'tcp', '8888', '8889', 'both'); + $p2plist['scour'] = array(); + $p2plist['scour'][] = array('Scour', 'tcp', '8311', '8311', 'both'); + $p2plist['shareaza'] = array(); + $p2plist['shareaza'][] = array('Shareaza', 'tcp', '6346', '6346', 'both'); + $p2plist['songspy'] = array(); + $p2plist['songspy'][] = array('SongSpy', 'tcp', '5190', '5190', 'both'); + $p2plist['winmx'] = array(); + $p2plist['winmx'][] = array('WinMX', 'tcp', '6699', '6699', 'both'); + + + +$othersplist = array(); + /* Unlike other areas we are posting the queue H or L or BLANK */ + + /* Remote Service / Terminal emulation */ + + $othersplist['appleremotedesktop'] = array(); + /* apple remote desktop */ + $othersplist['appleremotedesktop'][] = array('AppleRemoteDesktop1', 'tcp', '3283', '3283', 'both'); + $othersplist['appleremotedesktop'][] = array('AppleRemoteDesktop2', 'tcp', '5900', '5900', 'both'); + $othersplist['appleremotedesktop'][] = array('AppleRemoteDesktop3', 'udp', '3283', '3283', 'both'); + $othersplist['appleremotedesktop'][] = array('AppleRemoteDesktop4', 'udp', '5900', '5900', 'both'); + + $othersplist['msrdp'] = array(); + /* MSRDP */ + $othersplist['msrdp'][] = array('MSRDP', 'tcp', '3389', '3389', 'both'); + + $othersplist['pcanywhere'] = array(); + /* symantec pc anywhere */ + $othersplist['pcanywhere'][] = array('PCAnywhere-1', 'tcp', '5631', '5631', 'both'); + $othersplist['pcanywhere'][] = array('PCAnywhere-2', 'udp', '5632', '5632', 'both'); + + $othersplist['vnc'] = array(); + /* virtual network control */ + $othersplist['vnc'][] = array('VNC', 'tcp', '5900', '5930', 'both'); + + /* Messanger Clients */ + + $othersplist['aolinstantmessenger'] = array(); + /* AIM */ + $othersplist['aolinstantmessenger'][] = array('AIM', 'tcp', '5190', '5190', 'both'); + + $othersplist['facetime'] = array(); + /* Facetime */ + $othersplist['facetime'][] = array('Facetime-UDP-1', 'udp', '3478', '3479', 'both'); + $othersplist['facetime'][] = array('Facetime-TCP-1', 'tcp', '16384', '16387', 'both'); + $othersplist['facetime'][] = array('Facetime-TCP-2', 'tcp', '16393', '16402', 'both'); + + $othersplist['googlehangouts'] = array(); + /* Google Hangouts */ + $othersplist['googlehangouts'][] = array('GoogleHangouts-UDP', 'udp', '19302', '19309', 'both'); + $othersplist['googlehangouts'][] = array('GoogleHangouts-TCP', 'tcp', '19305', '19309', 'both'); + + $othersplist['icq'] = array(); + /* icq */ + $othersplist['icq'][] = array('ICQ1', 'tcp', '5190', '5190', 'both'); + $othersplist['icq'][] = array('ICQ2', 'udp', '5190', '5190', 'both'); + + $othersplist['irc'] = array(); + /* internet relay chat */ + $othersplist['irc'][] = array('IRC', 'tcp', '6667', '6670', 'both'); + + $othersplist['jabber'] = array(); + /* jabber */ + $othersplist['jabber'][] = array('IRC', 'tcp', '5222', '5222', 'both'); + $othersplist['jabber'][] = array('IRC', 'tcp', '5223', '5223', 'both'); + $othersplist['jabber'][] = array('IRC', 'tcp', '5269', '5269', 'both'); + + $othersplist['msnmessenger'] = array(); + /* msn messenger */ + $othersplist['msnmessenger'][] = array('MSN1', 'tcp', '1863', '1863', 'both'); + $othersplist['msnmessenger'][] = array('MSN2', 'tcp', '6891', '6900', 'both'); + $othersplist['msnmessenger'][] = array('MSN3', 'tcp', '6901', '6901', 'both'); + $othersplist['msnmessenger'][] = array('MSN4', 'udp', '6901', '6901', 'both'); + + $othersplist['teamspeak'] = array(); + /* teamspeak */ + $othersplist['teamspeak'][] = array('TeamSpeak-1', 'tcp', '14534', '14534', 'both'); + $othersplist['teamspeak'][] = array('TeamSpeak-2', 'tcp', '51234', '51234', 'both'); + $othersplist['teamspeak'][] = array('TeamSpeak-3', 'udp', '8767', '8768', 'both'); + + $othersplist['teamspeak3'] = array(); + /* teamspeak 3 */ + $othersplist['teamspeak3'][] = array('TeamSpeak3-FileTransfer', 'tcp', '30033', '30033', 'both'); + $othersplist['teamspeak3'][] = array('TeamSpeak3-ServerQuery', 'tcp', '10011', '10011', 'both'); + $othersplist['teamspeak3'][] = array('TeamSpeak3-Voice', 'udp', '9987', '9987', 'both'); + $othersplist['teamspeak3'][] = array('TeamSpeak3-TSDNS', 'tcp', '41144', '41144', 'both'); + + $othersplist['ventrilo'] = array(); + /* ventrilo */ + $othersplist['ventrilo'][] = array('Ventrilo-TCP', 'tcp', '3784', '3784', 'both'); + $othersplist['ventrilo'][] = array('Ventrilo-UDP', 'udp', '3784', '3784', 'both'); + $othersplist['ventrilo'][] = array('Ventrilo-Voice', 'udp', '6100', '6100', 'both'); + + /* VPN */ + + $othersplist['pptp'] = array(); + /* PPTP */ + $othersplist['pptp'][] = array('PPTP', 'tcp', '1723', '1723', 'both'); + $othersplist['pptp'][] = array('PPTPGRE', 'gre', '', '', 'both'); + + $othersplist['ipsec'] = array(); + /* IPSEC */ + $othersplist['ipsec'][] = array('IPSEC', 'udp', '500', '500', 'both'); + $othersplist['ipsec'][] = array('IPSEC', 'ah', '', '', 'both'); + $othersplist['ipsec'][] = array('IPSEC', 'esp', '', '', 'both'); + + /* Multimedia/Streaming */ + + $othersplist['itunesradio'] = array(); + /* Apple iTunes Radio Stream */ + $othersplist['itunesradio'][] = array('iTunesRadio', 'tcp', '42000', '42999', 'both'); + + $othersplist['streamingmp3'] = array(); + /* streaming mp3 media aka shoutcast */ + $othersplist['streamingmp3'][] = array('STREAMINGMP3', 'tcp', '8000', '8100', 'both'); + + $othersplist['rtsp'] = array(); + /* realtime streaming protocol */ + $othersplist['rtsp'][] = array('RTSP1', 'tcp', '554', '554', 'both'); + + $othersplist['rtmp'] = array(); + /* Real-Time Messaging Protocol */ + $othersplist['rtmp'][] = array('RTMP', 'tcp', '1935', '1935', 'both'); + + /* Web */ + + $othersplist['http'] = array(); + /* HTTP aka Web Traffic */ + $othersplist['http'][] = array('HTTP', 'tcp', '80', '80', 'both'); + $othersplist['http'][] = array('HTTPS', 'tcp', '443', '443', 'both'); + + /* Mail */ + + $othersplist['imap'] = array(); + /* IMAP */ + $othersplist['imap'][] = array('IMAP', 'tcp', '143', '143', 'both'); + $othersplist['imap'][] = array('IMAP-Secure', 'tcp', '993', '993', 'both'); + + $othersplist['lotusnotes'] = array(); + /* lotus notes */ + $othersplist['lotusnotes'][] = array('LotusNotes1', 'tcp', '1352', '1352', 'both'); + $othersplist['lotusnotes'][] = array('LotusNotes2', 'udp', '1352', '1352', 'both'); + + $othersplist['pop3'] = array(); + /* Post Office Protocol - POP3 */ + $othersplist['pop3'][] = array('POP3', 'tcp', '110', '110', 'both'); + $othersplist['pop3'][] = array('POP3-Secure', 'tcp', '995', '995', 'both'); + + $othersplist['smtp'] = array(); + /* SMTP */ + $othersplist['smtp'][] = array('SMTP', 'tcp', '25', '25', 'both'); + $othersplist['smtp'][] = array('SMTP-Secure-1', 'tcp', '465', '465', 'both'); + $othersplist['smtp'][] = array('SMTP-Secure-2', 'tcp', '587', '587', 'both'); + + /* Game Downloader */ + + //NOTE: Battle.net-Downloader runs on this port range. Don't want that up with the game que. + $othersplist['battlenetdownloader'] = array(); + $othersplist['battlenetdownloader'][] = array('Battle.NET-Downloader', 'tcp', '6881', '6999', 'both'); + + //NOTE: steam downloads, probably don't want this in the game que + $othersplist['steamdownloader'] = array(); + $othersplist['steamdownloader'][] = array('Steam-Downloader', 'tcp', '27014', '27050', 'both'); + + /* Miscellaneous */ + + $othersplist['apns'] = array(); + /* Apple Push Notification Service */ + $othersplist['apns'][] = array('APNS', 'tcp', '5223', '5223', 'both'); + $othersplist['apns'][] = array('APNS', 'tcp', '2195', '2196', 'both'); + + $othersplist['applemobilesync'] = array(); + /* Apple Mobile Sync */ + $othersplist['applemobilesync'][] = array('AppleMobileSync', 'tcp', '2336', '2336', 'both'); + + $othersplist['crashplan'] = array(); + /* crashplan */ + $othersplist['crashplan'][] = array('CrashPlan-1', 'tcp', '4282', '4282', 'both'); + $othersplist['crashplan'][] = array('CrashPlan-2', 'tcp', '4285', '4285', 'both'); + + $othersplist['cvsup'] = array(); + /* cvs */ + $othersplist['cvsup'][] = array('cvsup', 'tcp', '5999', '5999', 'both'); + + $othersplist['dns'] = array(); + /* domain name system */ + $othersplist['dns'][] = array('DNS1', 'tcp', '53', '53', 'both'); + $othersplist['dns'][] = array('DNS2', 'udp', '53', '53', 'both'); + + $othersplist['git'] = array(); + /* GIT */ + $othersplist['git'][] = array('git', 'tcp', '9418', '9418', 'both'); + + $othersplist['hbci'] = array(); + /* HBCI */ + $othersplist['hbci'][] = array('HBCI', 'tcp', '3000', '3000', 'both'); + + $othersplist['icmp'] = array(); + /* ICMP */ + $othersplist['icmp'][] = array('ICMP', 'icmp', '', '', 'both'); + + $othersplist['mysqlserver'] = array(); + /* mysql server */ + $othersplist['mysqlserver'][] = array('MySQL1', 'tcp', '3306', '3306', 'both'); + + $othersplist['nntp'] = array(); + /* nntp */ + $othersplist['nntp'][] = array('NNTP1', 'tcp', '119', '119', 'both'); + $othersplist['nntp'][] = array('NNTP2', 'udp', '119', '119', 'both'); + + $othersplist['slingbox'] = array(); + /* slingbox */ + $othersplist['slingbox'][] = array('Slingbox1', 'tcp', '5001', '5001', 'both'); + $othersplist['slingbox'][] = array('Slingbox2', 'udp', '5001', '5001', 'both'); + + $othersplist['smb'] = array(); + /* Microsoft SMB and friends */ + $othersplist['smb'][] = array('SMB1', 'tcp', '445', '445', 'both'); + $othersplist['smb'][] = array('SMB2', 'tcp', '137-139', '137-139', 'both'); + + $othersplist['snmp'] = array(); + /* Simple network management protocol */ + $othersplist['snmp'][] = array('SNMP', 'tcp', '161', '161', 'both'); + $othersplist['snmp'][] = array('SNMP2', 'udp', '161', '161', 'both'); + + $othersplist['subversion'] = array(); + /* subversion */ + $othersplist['subversion'][] = array('subversion', 'tcp', '3690', '3690', 'both'); + +?> diff --git a/src/etc/inc/xmlparse.inc b/src/etc/inc/xmlparse.inc new file mode 100644 index 0000000..08d9b19 --- /dev/null +++ b/src/etc/inc/xmlparse.inc @@ -0,0 +1,334 @@ +<?php +/* $Id$ */ +/* + xmlparse.inc + functions to parse/dump configuration files in XML format + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* The following items will be treated as arrays in config.xml */ +function listtags() { + /* + * Please keep this list alpha sorted and no longer than 80 characters + * I know it's a pain, but it's a pain to find stuff too if it's not + */ + $ret = array( + 'acls', 'alias', 'aliasurl', 'allowedip', 'allowedhostname', 'authserver', + 'bridged', 'build_port_path', + 'ca', 'cacert', 'cert', 'crl', 'clone', 'config', 'container', 'columnitem', + 'depends_on_package', 'disk', 'dnsserver', 'dnsupdate', 'domainoverrides', 'dyndns', + 'earlyshellcmd', 'element', 'encryption-algorithm-option', + 'field', 'fieldname', + 'gateway_item', 'gateway_group', 'gif', 'gre', 'group', + 'hash-algorithm-option', 'hosts', 'member', 'ifgroupentry', 'igmpentry', 'interface_array', 'item', 'key', + 'lagg', 'lbaction', 'lbpool', 'l7rules', 'lbprotocol', + 'member', 'menu', 'tab', 'mobilekey', 'monitor_type', 'mount', + 'npt', 'ntpserver', + 'onetoone', 'openvpn-server', 'openvpn-client', 'openvpn-csc', 'option', + 'package', 'passthrumac', 'phase1', 'phase2', 'ppp', 'pppoe', 'priv', 'proxyarpnet', 'pool', + 'qinqentry', 'queue', + 'pages', 'pipe', 'radnsserver', 'roll', 'route', 'row', 'rrddatafile', 'rule', + 'schedule', 'service', 'servernat', 'servers', + 'serversdisabled', 'shellcmd', 'staticmap', 'subqueue', + 'timerange', 'tunnel', 'user', 'vip', 'virtual_server', 'vlan', + 'winsserver', 'wolentry', 'widget' + ); + return array_flip($ret); +} + +/* Package XML tags that should be treated as a list not as a traditional array */ +function listtags_pkg() { + $ret = array('build_port_path', 'depends_on_package', 'onetoone', 'queue', 'rule', 'servernat', 'alias', 'additional_files_needed', 'tab', 'template', 'menu', 'rowhelperfield', 'service', 'step', 'package', 'columnitem', 'option', 'item', 'field', 'package', 'file'); + + return array_flip($ret); +} + +function startElement($parser, $name, $attrs) { + global $parsedcfg, $depth, $curpath, $havedata, $listtags; + + array_push($curpath, strtolower($name)); + + $ptr =& $parsedcfg; + foreach ($curpath as $path) { + $ptr =& $ptr[$path]; + } + + /* is it an element that belongs to a list? */ + if (isset($listtags[strtolower($name)])) { + + /* is there an array already? */ + if (!is_array($ptr)) { + /* make an array */ + $ptr = array(); + } + + array_push($curpath, count($ptr)); + + } else if (isset($ptr)) { + /* multiple entries not allowed for this element, bail out */ + die(sprintf(gettext('XML error: %1$s at line %2$d cannot occur more than once') . "\n", + $name, + xml_get_current_line_number($parser))); + } + + $depth++; + $havedata = $depth; +} + +function endElement($parser, $name) { + global $depth, $curpath, $parsedcfg, $havedata, $listtags; + + if ($havedata == $depth) { + $ptr =& $parsedcfg; + foreach ($curpath as $path) { + $ptr =& $ptr[$path]; + } + $ptr = ""; + } + + array_pop($curpath); + + if (isset($listtags[strtolower($name)])) { + array_pop($curpath); + } + + $depth--; +} + +function cData($parser, $data) { + global $depth, $curpath, $parsedcfg, $havedata; + + $data = trim($data, "\t\n\r"); + + if ($data != "") { + $ptr =& $parsedcfg; + foreach ($curpath as $path) { + $ptr =& $ptr[$path]; + } + + if (is_string($ptr)) { + $ptr .= html_entity_decode($data); + } else { + if (trim($data, " ") != "") { + $ptr = html_entity_decode($data); + $havedata++; + } + } + } +} + +function parse_xml_config($cffile, $rootobj, $isstring = "false") { + global $listtags; + $listtags = listtags(); + if (isset($GLOBALS['custom_listtags'])) { + foreach ($GLOBALS['custom_listtags'] as $tag) { + $listtags[$tag] = $tag; + } + } + return parse_xml_config_raw($cffile, $rootobj, $isstring); +} + +function parse_xml_config_pkg($cffile, $rootobj, $isstring = "false") { + global $listtags; + $listtags = listtags_pkg(); + if (isset($GLOBALS['custom_listtags_pkg'])) { + foreach ($GLOBALS['custom_listtags_pkg'] as $tag) { + $listtags[$tag] = $tag; + } + } + $cfg =parse_xml_config_raw($cffile, $rootobj, $isstring); + if ($cfg == -1) { + return array(); + } + + return $cfg; +} + +function parse_xml_config_raw($cffile, $rootobj, $isstring = "false") { + + global $depth, $curpath, $parsedcfg, $havedata, $listtags; + $parsedcfg = array(); + $curpath = array(); + $depth = 0; + $havedata = 0; + + $xml_parser = xml_parser_create(); + + xml_set_element_handler($xml_parser, "startElement", "endElement"); + xml_set_character_data_handler($xml_parser, "cdata"); + xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 1); + + if (!($fp = fopen($cffile, "r"))) { + log_error(gettext("Error: could not open XML input") . "\n"); + return -1; + } + + while ($data = fread($fp, 4096)) { + if (!xml_parse($xml_parser, $data, feof($fp))) { + log_error(sprintf(gettext('XML error: %1$s at line %2$d in %3$s') . "\n", + xml_error_string(xml_get_error_code($xml_parser)), + xml_get_current_line_number($xml_parser), + $cffile)); + return -1; + } + } + xml_parser_free($xml_parser); + + if ($rootobj) { + if (!is_array($rootobj)) { + $rootobj = array($rootobj); + } + foreach ($rootobj as $rootobj_name) { + if ($parsedcfg[$rootobj_name]) { + break; + } + } + + if (!$parsedcfg[$rootobj_name]) { + log_error(sprintf(gettext("XML error: no %s object found!") . "\n", implode(" or ", $rootobj))); + return -1; + } + return $parsedcfg[$rootobj_name]; + } else { + return $parsedcfg; + } +} + +function dump_xml_config_sub($arr, $indent) { + + global $listtags; + + $xmlconfig = ""; + + foreach ($arr as $ent => $val) { + if (is_array($val)) { + /* is it just a list of multiple values? */ + if (isset($listtags[strtolower($ent)])) { + foreach ($val as $cval) { + if (is_array($cval)) { + if (empty($cval)) { + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "<$ent/>\n"; + } else { + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "<$ent>\n"; + $xmlconfig .= dump_xml_config_sub($cval, $indent + 1); + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "</$ent>\n"; + } + } else { + if ($cval === false) { + continue; + } + $xmlconfig .= str_repeat("\t", $indent); + if ((is_bool($cval) && $cval == true) || ($cval === "")) { + $xmlconfig .= "<$ent/>\n"; + } else if ((substr($ent, 0, 5) == "descr") || + (substr($ent, 0, 6) == "detail") || + (substr($ent, 0, 12) == "login_banner") || + (substr($ent, 0, 9) == "ldap_attr") || + (substr($ent, 0, 9) == "ldap_bind") || + (substr($ent, 0, 11) == "ldap_basedn") || + (substr($ent, 0, 18) == "ldap_authcn") || + (substr($ent, 0, 19) == "ldap_extended_query")) { + $xmlconfig .= "<$ent><![CDATA[" . htmlentities($cval) . "]]></$ent>\n"; + } else { + $xmlconfig .= "<$ent>" . htmlentities($cval) . "</$ent>\n"; + } + } + } + } else if (empty($val)) { + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "<$ent/>\n"; + } else { + /* it's an array */ + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "<$ent>\n"; + $xmlconfig .= dump_xml_config_sub($val, $indent + 1); + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "</$ent>\n"; + } + } else { + if ((is_bool($val) && ($val == true)) || ($val === "")) { + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "<$ent/>\n"; + } else if (!is_bool($val)) { + $xmlconfig .= str_repeat("\t", $indent); + if ((substr($ent, 0, 5) == "descr") || + (substr($ent, 0, 6) == "detail") || + (substr($ent, 0, 12) == "login_banner") || + (substr($ent, 0, 9) == "ldap_attr") || + (substr($ent, 0, 9) == "ldap_bind") || + (substr($ent, 0, 11) == "ldap_basedn") || + (substr($ent, 0, 18) == "ldap_authcn") || + (substr($ent, 0, 19) == "ldap_extended_query")) { + $xmlconfig .= "<$ent><![CDATA[" . htmlentities($val) . "]]></$ent>\n"; + } else { + $xmlconfig .= "<$ent>" . htmlentities($val) . "</$ent>\n"; + } + } + } + } + + return $xmlconfig; +} + +function dump_xml_config($arr, $rootobj) { + global $listtags; + $listtags = listtags(); + if (isset($GLOBALS['custom_listtags'])) { + foreach ($GLOBALS['custom_listtags'] as $tag) { + $listtags[$tag] = $tag; + } + } + return dump_xml_config_raw($arr, $rootobj); +} + +function dump_xml_config_pkg($arr, $rootobj) { + global $listtags; + $listtags = listtags_pkg(); + if (isset($GLOBALS['custom_listtags_pkg'])) { + foreach ($GLOBALS['custom_listtags_pkg'] as $tag) { + $listtags[$tag] = $tag; + } + } + return dump_xml_config_raw($arr, $rootobj); +} + +function dump_xml_config_raw($arr, $rootobj) { + + $xmlconfig = "<?xml version=\"1.0\"?" . ">\n"; + $xmlconfig .= "<$rootobj>\n"; + + $xmlconfig .= dump_xml_config_sub($arr, 1); + + $xmlconfig .= "</$rootobj>\n"; + + return $xmlconfig; +} + +?> diff --git a/src/etc/inc/xmlparse_attr.inc b/src/etc/inc/xmlparse_attr.inc new file mode 100644 index 0000000..ab90e98 --- /dev/null +++ b/src/etc/inc/xmlparse_attr.inc @@ -0,0 +1,237 @@ +<?php +/* $Id$ */ +/* + xmlparse_attr.inc + functions to parse configuration files in XML format with attributes + Copyright (C) 2010 Erik Fonnesbeck + All rights reserved. + + Based on xmlparse.inc, originally part of m0n0wall (http://m0n0.ch/wall) + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* The following items will be treated as arrays in regdomain.xml */ +function listtags_rd() { + $ret = explode(" ", + "band country flags freqband netband rd" + ); + return $ret; +} + +function startElement_attr($parser, $name, $attrs) { + global $parsedcfg, $depth, $curpath, $havedata, $listtags, $parsedattrs; + + array_push($curpath, strtolower($name)); + + $ptr =& $parsedcfg; + if (!empty($attrs)) { + $attrptr =& $parsedattrs; + $writeattrs = true; + } + foreach ($curpath as $path) { + $ptr =& $ptr[$path]; + if (isset($writeattrs)) { + $attrptr =& $attrptr[$path]; + } + } + + /* is it an element that belongs to a list? */ + if (in_array(strtolower($name), $listtags)) { + + /* is there an array already? */ + if (!is_array($ptr)) { + /* make an array */ + $ptr = array(); + } + + array_push($curpath, count($ptr)); + + if (isset($writeattrs)) { + if (!is_array($attrptr)) { + $attrptr = array(); + } + $attrptr[count($ptr)] = $attrs; + } + + } else if (isset($ptr)) { + /* multiple entries not allowed for this element, bail out */ + die(sprintf(gettext('XML error: %1$s at line %2$d cannot occur more than once') . "\n", + $name, + xml_get_current_line_number($parser))); + } else if (isset($writeattrs)) { + $attrptr = $attrs; + } + + $depth++; + $havedata = $depth; +} + +function endElement_attr($parser, $name) { + global $depth, $curpath, $parsedcfg, $havedata, $listtags; + + if ($havedata == $depth) { + $ptr =& $parsedcfg; + foreach ($curpath as $path) { + $ptr =& $ptr[$path]; + } + $ptr = ""; + } + + array_pop($curpath); + + if (in_array(strtolower($name), $listtags)) { + array_pop($curpath); + } + + $depth--; +} + +function cData_attr($parser, $data) { + global $depth, $curpath, $parsedcfg, $havedata; + + $data = trim($data, "\t\n\r"); + + if ($data != "") { + $ptr =& $parsedcfg; + foreach ($curpath as $path) { + $ptr =& $ptr[$path]; + } + + if (is_string($ptr)) { + $ptr .= html_entity_decode($data); + } else { + if (trim($data, " ") != "") { + $ptr = html_entity_decode($data); + $havedata++; + } + } + } +} + +function parse_xml_regdomain(&$rdattributes, $rdfile = '', $rootobj = 'regulatory-data') { + global $g, $listtags; + + if (empty($rdfile)) { + $rdfile = $g['etc_path'] . '/regdomain.xml'; + } + $listtags = listtags_rd(); + $parsed_xml = array(); + + if (file_exists($g['tmp_path'] . '/regdomain.cache')) { + $parsed_xml = unserialize(file_get_contents($g['tmp_path'] . '/regdomain.cache')); + if (!empty($parsed_xml)) { + $rdmain = $parsed_xml['main']; + $rdattributes = $parsed_xml['attributes']; + } + } + if (empty($parsed_xml) && file_exists($g['etc_path'] . '/regdomain.xml')) { + $rdmain = parse_xml_config_raw_attr($rdfile, $rootobj, $rdattributes); + + // unset parts that aren't used before making cache + foreach ($rdmain['regulatory-domains']['rd'] as $rdkey => $rdentry) { + if (isset($rdmain['regulatory-domains']['rd'][$rdkey]['netband'])) { + unset($rdmain['regulatory-domains']['rd'][$rdkey]['netband']); + } + if (isset($rdattributes['regulatory-domains']['rd'][$rdkey]['netband'])) { + unset($rdattributes['regulatory-domains']['rd'][$rdkey]['netband']); + } + } + if (isset($rdmain['shared-frequency-bands'])) { + unset($rdmain['shared-frequency-bands']); + } + if (isset($rdattributes['shared-frequency-bands'])) { + unset($rdattributes['shared-frequency-bands']); + } + + $parsed_xml = array('main' => $rdmain, 'attributes' => $rdattributes); + $rdcache = fopen($g['tmp_path'] . '/regdomain.cache', "w"); + fwrite($rdcache, serialize($parsed_xml)); + fclose($rdcache); + } + + return $rdmain; +} + +function parse_xml_config_raw_attr($cffile, $rootobj, &$parsed_attributes, $isstring = "false") { + + global $depth, $curpath, $parsedcfg, $havedata, $listtags, $parsedattrs; + $parsedcfg = array(); + $curpath = array(); + $depth = 0; + $havedata = 0; + + if (isset($parsed_attributes)) { + $parsedattrs = array(); + } + + $xml_parser = xml_parser_create(); + + xml_set_element_handler($xml_parser, "startElement_attr", "endElement_attr"); + xml_set_character_data_handler($xml_parser, "cData_attr"); + xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 1); + + if (!($fp = fopen($cffile, "r"))) { + log_error(gettext("Error: could not open XML input") . "\n"); + if (isset($parsed_attributes)) { + $parsed_attributes = array(); + unset($parsedattrs); + } + return -1; + } + + while ($data = fread($fp, 4096)) { + if (!xml_parse($xml_parser, $data, feof($fp))) { + log_error(sprintf(gettext('XML error: %1$s at line %2$d') . "\n", + xml_error_string(xml_get_error_code($xml_parser)), + xml_get_current_line_number($xml_parser))); + if (isset($parsed_attributes)) { + $parsed_attributes = array(); + unset($parsedattrs); + } + return -1; + } + } + xml_parser_free($xml_parser); + + if (!$parsedcfg[$rootobj]) { + log_error(sprintf(gettext("XML error: no %s object found!") . "\n", $rootobj)); + if (isset($parsed_attributes)) { + $parsed_attributes = array(); + unset($parsedattrs); + } + return -1; + } + + if (isset($parsed_attributes)) { + if ($parsedattrs[$rootobj]) { + $parsed_attributes = $parsedattrs[$rootobj]; + } + unset($parsedattrs); + } + + return $parsedcfg[$rootobj]; +} + +?> diff --git a/src/etc/inc/xmlreader.inc b/src/etc/inc/xmlreader.inc new file mode 100644 index 0000000..960acb1 --- /dev/null +++ b/src/etc/inc/xmlreader.inc @@ -0,0 +1,277 @@ +<?php +/* $Id$ */ +/* + xmlreader.inc + functions to parse/dump configuration files in XML format + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_MODULE: utils +*/ + +/* The following items will be treated as arrays in config.xml */ +function listtags() { + /* + * Please keep this list alpha sorted and no longer than 80 characters + * I know it's a pain, but it's a pain to find stuff too if it's not + */ + $ret = array( + 'acls', 'alias', 'aliasurl', 'allowedip', 'allowedhostname', 'authserver', + 'bridged', 'build_port_path', + 'ca', 'cacert', 'cert', 'crl', 'clone', 'config', 'container', 'columnitem', + 'depends_on_package', 'disk', 'dnsserver', 'dnsupdate', 'domainoverrides', 'dyndns', + 'earlyshellcmd', 'element', 'encryption-algorithm-option', + 'field', 'fieldname', + 'gateway_item', 'gateway_group', 'gif', 'gre', 'group', + 'hash-algorithm-option', 'hosts', 'member', 'ifgroupentry', 'igmpentry', 'interface_array', 'item', 'key', + 'lagg', 'lbaction', 'lbpool', 'l7rules', 'lbprotocol', + 'member', 'menu', 'tab', 'mobilekey', 'monitor_type', 'mount', + 'npt', 'ntpserver', + 'onetoone', 'openvpn-server', 'openvpn-client', 'openvpn-csc', 'option', + 'package', 'passthrumac', 'phase1', 'phase2', 'ppp', 'pppoe', 'priv', 'proxyarpnet', 'pool', + 'qinqentry', 'queue', + 'pages', 'pipe', 'radnsserver', 'roll', 'route', 'row', 'rrddatafile', 'rule', + 'schedule', 'service', 'servernat', 'servers', + 'serversdisabled', 'shellcmd', 'staticmap', 'subqueue', + 'timerange', 'tunnel', 'user', 'vip', 'virtual_server', 'vlan', + 'winsserver', 'wolentry', 'widget' + ); + return array_flip($ret); +} + +/* Package XML tags that should be treat as a list not as a traditional array */ +function listtags_pkg() { + $ret = array('depends_on_package', 'onetoone', 'queue', 'rule', 'servernat', 'alias', 'additional_files_needed', 'tab', 'template', 'menu', 'rowhelperfield', 'service', 'step', 'package', 'columnitem', 'option', 'item', 'field', 'package', 'file'); + + return array_flip($ret); +} + +function add_elements(&$cfgarray, &$parser) { + global $listtags; + + while ($parser->read()) { + switch ($parser->nodeType) { + case XMLReader::WHITESPACE: + case XMLReader::SIGNIFICANT_WHITESPACE: + break; + case XMLReader::ELEMENT: + if (isset($listtags[strtolower($parser->name)])) { + $cfgref =& $cfgarray[$parser->name][count($cfgarray[$parser->name])]; + if (!$parser->isEmptyElement) { + add_elements($cfgref, $parser); + } else { + $cfgref = array(); + } + } else { + if (isset($cfgarray[$parser->name]) && (!is_array($cfgarray[$parser->name]) || !isset($cfgarray[$parser->name][0]))) { + $nodebkp = $cfgarray[$parser->name]; + $cfgarray[$parser->name] = array(); + $cfgarray[$parser->name][] = $nodebkp; + $cfgref =& $cfgarray[$parser->name][0]; + unset($nodebkp); + } else { + $cfgref =& $cfgarray[$parser->name]; + } + + if ($parser->isEmptyElement) { + if (is_array($cfgref)) { + $cfgref[] = array(); + } else { + $cfgref = ""; + } + } else { + if (is_array($cfgref)) { + $cfgref =& $cfgarray[$parser->name][count($cfgarray[$parser->name])]; + add_elements($cfgref, $parser); + } else { + add_elements($cfgref, $parser); + } + } + } + + $i = 0; + while ($parser->moveToAttributeNo($i)) { + $cfgref[$parser->name] = $parser->value; + $i++; + } + break; + case XMLReader::TEXT: + case XMLReader::CDATA: + $cfgarray = $parser->value; + break; + case XMLReader::END_ELEMENT: + return; + break; + default: + break; + } + } +} + +function parse_xml_config($cffile, $rootobj, $isstring = "false") { + global $listtags; + + $listtags = listtags(); + if (isset($GLOBALS['custom_listtags'])) { + foreach ($GLOBALS['custom_listtags'] as $tag) { + $listtags[$tag] = $tag; + } + } + + return parse_xml_config_raw($cffile, $rootobj); +} + +function parse_xml_config_pkg($cffile, $rootobj, $isstring = "false") { + global $listtags; + + $listtags = listtags_pkg(); + if (isset($GLOBALS['custom_listtags_pkg'])) { + foreach ($GLOBALS['custom_listtags_pkg'] as $tag) { + $listtags[$tag] = $tag; + } + } + return parse_xml_config_raw($cffile, $rootobj, $isstring); +} + +function parse_xml_config_raw($cffile, $rootobj, $isstring = "false") { + global $listtags; + + $parsedcfg = array(); + + $par = new XMLReader(); + if ($par->open($cffile, "UTF-8", LIBXML_NOERROR | LIBXML_NOWARNING)) { + add_elements($parsedcfg, $par); + $par->close(); + } else { + log_error(sprintf(gettext("Error returned while trying to parse %s"), $cffile)); + } + + if ($rootobj) { + if (!is_array($rootobj)) { + $rootobj = array($rootobj); + } + foreach ($rootobj as $rootobj_name) { + if ($parsedcfg[$rootobj_name]) { + break; + } + } + + return $parsedcfg[$rootobj_name]; + } else { + return $parsedcfg; + } +} + +function dump_xml_config_sub(& $writer, $arr) { + global $listtags; + + foreach ($arr as $ent => $val) { + if (is_array($val)) { + /* is it just a list of multiple values? */ + if (isset($listtags[strtolower($ent)])) { + foreach ($val as $cval) { + if (is_array($cval)) { + if (empty($cval)) { + $writer->writeElement($ent); + } else { + $writer->startElement($ent); + dump_xml_config_sub($writer, $cval); + $writer->endElement(); + } + } else { + if ($cval === false) { + continue; + } + if ((is_bool($val) && ($val == true)) || ($val === "")) { + $writer->writeElement($ent); + } else if (!is_bool($val)) { + $writer->writeElement($ent, $cval); + } + } + } + } else if (empty($val)) { + $writer->writeElement($ent); + } else { + /* it's an array */ + $writer->startElement($ent); + dump_xml_config_sub($writer, $val); + $writer->endElement(); + } + } else { + if ((is_bool($val) && ($val == true)) || ($val === "")) { + $writer->writeElement($ent); + } else if (!is_bool($val)) { + $writer->writeElement($ent, $val); + } + } + } +} + +function dump_xml_config($arr, $rootobj) { + global $listtags; + + $listtags = listtags(); + if (isset($GLOBALS['custom_listtags'])) { + foreach ($GLOBALS['custom_listtags'] as $tag) { + $listtags[$tag] = $tag; + } + } + return dump_xml_config_raw($arr, $rootobj); +} + +function dump_xml_config_pkg($arr, $rootobj) { + global $listtags; + + $listtags = listtags_pkg(); + if (isset($GLOBALS['custom_listtags_pkg'])) { + foreach ($GLOBALS['custom_listtags_pkg'] as $tag) { + $listtags[$tag] = $tag; + } + } + return dump_xml_config_raw($arr, $rootobj); +} + +function dump_xml_config_raw($arr, $rootobj) { + + $writer = new XMLWriter(); + $writer->openMemory(); + $writer->setIndent(true); + $writer->setIndentString("\t"); + $writer->startDocument("1.0", "UTF-8"); + $writer->startElement($rootobj); + + dump_xml_config_sub($writer, $arr); + + $writer->endElement(); + $writer->endDocument(); + $xmlconfig = $writer->outputMemory(true); + + return $xmlconfig; +} + +?> diff --git a/src/etc/inc/xmlrpc.inc b/src/etc/inc/xmlrpc.inc new file mode 100644 index 0000000..e96e783 --- /dev/null +++ b/src/etc/inc/xmlrpc.inc @@ -0,0 +1,148 @@ +<?php +/* + $Id$ + + xmlrpc.inc + Copyright (C) 2005-2006 Colin Smith + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + pfSense_BUILDER_BINARIES: + pfSense_MODULE: utils +*/ + +require_once("auth.inc"); +require_once("xmlrpc_client.inc"); + +/* + * xmlrpc_params_to_php: Convert params array passed from XMLRPC server into a PHP array and return it. + */ +function xmlrpc_params_to_php($params) { + $array = array(); + for ($i = 0; $i < $params->getNumParams(); $i++) { + $value = $params->getParam($i); + $array[] = XML_RPC_decode($value); + } + return $array; +} + +/* + * xmlrpc_value_to_php: Convert an XMLRPC value into a PHP scalar/array and return it. + */ +function xmlrpc_value_to_php($raw_value) { + /* + switch ($raw_value->kindOf()) { + case "scalar": + if ($raw_value->scalartyp() == "boolean") { + $return = (boolean) $raw_value->scalarval(); + } + $return = $raw_value->scalarval(); + break; + case "array": + $return = array(); + for ($i = 0; $i < $raw_value->arraysize(); $i++) { + $value = $raw_value->arraymem($i); + $return[] = xmlrpc_value_to_php($value); + } + break; + case "struct": + $return = array(); + for ($i = 0; $i < $raw_value->arraysize(); $i++) { + list($key, $value) = $raw_value->structeach(); + $return[$key] = xmlrpc_value_to_php($value); + } + break; + } + */ + return XML_RPC_decode($raw_value); +} + +/* + * php_value_to_xmlrpc: Convert a PHP scalar or array into its XMLRPC equivalent. + */ +function php_value_to_xmlrpc($value, $force_array = false) { + $toreturn = XML_RPC_encode($value); + return $force_array ? array($toreturn) : $toreturn; + /* + if (gettype($value) == "array") { + $xmlrpc_type = "array"; + $toreturn = array(); + foreach ($value as $key => $val) { + if (is_string($key)) { + $xmlrpc_type = "struct"; + } + $toreturn[$key] = php_value_to_xmlrpc($val); + } + return new XML_RPC_Value($toreturn, $xmlrpc_type); + } else { + if ($force_array == true) { + return new XML_RPC_Value(array(new XML_RPC_Value($value, gettype($value))), "array"); + } else { + return new XML_RPC_Value($value, gettype($value)); + } + } + */ +} + +/* + * xmlrpc_auth: Handle basic crypt() authentication of an XMLRPC request. This function assumes that + * $params[0] contains the local system's plaintext password and removes the password from + * the array before returning it. + */ +function xmlrpc_auth(&$params) { + global $config, $_SERVER; + + /* XXX: Should teach caller to pass username and use it here. */ + /* XXX: Should clarify from old behaviour what is in params[0] that differs from params['xmlrpcauth'] */ + if (isset($config['system']['webgui']['authmode'])) { + $authcfg = auth_get_authserver($config['system']['webgui']['authmode']); + if (authenticate_user("admin", $params[0], $authcfg) || + authenticate_user("admin", $params[0])) { + array_shift($params); + unset($params['xmlrpcauth']); + return true; + } else if (!empty($params['xmlrpcauth']) && (authenticate_user("admin", $params['xmlrpcauth'], $authcfg) || + authenticate_user("admin", $params['xmlrpcauth']))) { + array_shift($params); + unset($params['xmlrpcauth']); + return true; + } + } else if (authenticate_user("admin", $params[0])) { + array_shift($params); + unset($params['xmlrpcauth']); + return true; + } else if (!empty($params['xmlrpcauth']) && authenticate_user("admin", $params['xmlrpcauth'])) { + array_shift($params); + unset($params['xmlrpcauth']); + return true; + } + + array_shift($params); + unset($params['xmlrpcauth']); + log_error("webConfigurator authentication error for 'admin' from {$_SERVER['REMOTE_ADDR']} during sync settings."); + return false; +} + +?> diff --git a/src/etc/inc/xmlrpc_client.inc b/src/etc/inc/xmlrpc_client.inc new file mode 100644 index 0000000..fbbf977 --- /dev/null +++ b/src/etc/inc/xmlrpc_client.inc @@ -0,0 +1,2060 @@ +<?php + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * PHP implementation of the XML-RPC protocol + * + * This is a PEAR-ified version of Useful inc's XML-RPC for PHP. + * It has support for HTTP transport, proxies and authentication. + * + * PHP versions 4 and 5 + * + * @category Web Services + * @package XML_RPC + * @author Edd Dumbill <edd@usefulinc.com> + * @author Stig Bakken <stig@php.net> + * @author Martin Jansen <mj@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License + * @version SVN: $Id: RPC.php 300961 2010-07-03 02:17:34Z danielc $ + * @link http://pear.php.net/package/XML_RPC + */ + + +if (!function_exists('xml_parser_create')) { + include_once 'PEAR.inc'; + PEAR::loadExtension('xml'); +} + +/**#@+ + * Error constants + */ +/** + * Parameter values don't match parameter types + */ +define('XML_RPC_ERROR_INVALID_TYPE', 101); +/** + * Parameter declared to be numeric but the values are not + */ +define('XML_RPC_ERROR_NON_NUMERIC_FOUND', 102); +/** + * Communication error + */ +define('XML_RPC_ERROR_CONNECTION_FAILED', 103); +/** + * The array or struct has already been started + */ +define('XML_RPC_ERROR_ALREADY_INITIALIZED', 104); +/** + * Incorrect parameters submitted + */ +define('XML_RPC_ERROR_INCORRECT_PARAMS', 105); +/** + * Programming error by developer + */ +define('XML_RPC_ERROR_PROGRAMMING', 106); +/**#@-*/ + + +/** + * Data types + * @global string $GLOBALS['XML_RPC_I4'] + */ +$GLOBALS['XML_RPC_I4'] = 'i4'; + +/** + * Data types + * @global string $GLOBALS['XML_RPC_Int'] + */ +$GLOBALS['XML_RPC_Int'] = 'int'; + +/** + * Data types + * @global string $GLOBALS['XML_RPC_Boolean'] + */ +$GLOBALS['XML_RPC_Boolean'] = 'boolean'; + +/** + * Data types + * @global string $GLOBALS['XML_RPC_Double'] + */ +$GLOBALS['XML_RPC_Double'] = 'double'; + +/** + * Data types + * @global string $GLOBALS['XML_RPC_String'] + */ +$GLOBALS['XML_RPC_String'] = 'string'; + +/** + * Data types + * @global string $GLOBALS['XML_RPC_DateTime'] + */ +$GLOBALS['XML_RPC_DateTime'] = 'dateTime.iso8601'; + +/** + * Data types + * @global string $GLOBALS['XML_RPC_Base64'] + */ +$GLOBALS['XML_RPC_Base64'] = 'base64'; + +/** + * Data types + * @global string $GLOBALS['XML_RPC_Array'] + */ +$GLOBALS['XML_RPC_Array'] = 'array'; + +/** + * Data types + * @global string $GLOBALS['XML_RPC_Struct'] + */ +$GLOBALS['XML_RPC_Struct'] = 'struct'; + + +/** + * Data type meta-types + * @global array $GLOBALS['XML_RPC_Types'] + */ +$GLOBALS['XML_RPC_Types'] = array( + $GLOBALS['XML_RPC_I4'] => 1, + $GLOBALS['XML_RPC_Int'] => 1, + $GLOBALS['XML_RPC_Boolean'] => 1, + $GLOBALS['XML_RPC_String'] => 1, + $GLOBALS['XML_RPC_Double'] => 1, + $GLOBALS['XML_RPC_DateTime'] => 1, + $GLOBALS['XML_RPC_Base64'] => 1, + $GLOBALS['XML_RPC_Array'] => 2, + $GLOBALS['XML_RPC_Struct'] => 3, +); + + +/** + * Error message numbers + * @global array $GLOBALS['XML_RPC_err'] + */ +$GLOBALS['XML_RPC_err'] = array( + 'unknown_method' => 1, + 'invalid_return' => 2, + 'incorrect_params' => 3, + 'introspect_unknown' => 4, + 'http_error' => 5, + 'not_response_object' => 6, + 'invalid_request' => 7, +); + +/** + * Error message strings + * @global array $GLOBALS['XML_RPC_str'] + */ +$GLOBALS['XML_RPC_str'] = array( + 'unknown_method' => gettext("Unknown method"), + 'invalid_return' => gettext("Invalid return payload: enable debugging to examine incoming payload"), + 'incorrect_params' => gettext("Incorrect parameters passed to method"), + 'introspect_unknown' => gettext("Can't introspect: method unknown"), + 'http_error' => gettext("Didn't receive 200 OK from remote server."), + 'not_response_object' => gettext("The requested method didn't return an XML_RPC_Response object."), + 'invalid_request' => gettext("Invalid request payload"), +); + + +/** + * Default XML encoding (ISO-8859-1, UTF-8 or US-ASCII) + * @global string $GLOBALS['XML_RPC_defencoding'] + */ +$GLOBALS['XML_RPC_defencoding'] = 'UTF-8'; + +/** + * User error codes start at 800 + * @global int $GLOBALS['XML_RPC_erruser'] + */ +$GLOBALS['XML_RPC_erruser'] = 800; + +/** + * XML parse error codes start at 100 + * @global int $GLOBALS['XML_RPC_errxml'] + */ +$GLOBALS['XML_RPC_errxml'] = 100; + + +/** + * Compose backslashes for escaping regexp + * @global string $GLOBALS['XML_RPC_backslash'] + */ +$GLOBALS['XML_RPC_backslash'] = chr(92) . chr(92); + + +/** + * Should we automatically base64 encode strings that contain characters + * which can cause PHP's SAX-based XML parser to break? + * @global boolean $GLOBALS['XML_RPC_auto_base64'] + */ +$GLOBALS['XML_RPC_auto_base64'] = true; + + +/** + * Valid parents of XML elements + * @global array $GLOBALS['XML_RPC_valid_parents'] + */ +$GLOBALS['XML_RPC_valid_parents'] = array( + 'BOOLEAN' => array('VALUE'), + 'I4' => array('VALUE'), + 'INT' => array('VALUE'), + 'STRING' => array('VALUE'), + 'DOUBLE' => array('VALUE'), + 'DATETIME.ISO8601' => array('VALUE'), + 'BASE64' => array('VALUE'), + 'ARRAY' => array('VALUE'), + 'STRUCT' => array('VALUE'), + 'PARAM' => array('PARAMS'), + 'METHODNAME' => array('METHODCALL'), + 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'), + 'MEMBER' => array('STRUCT'), + 'NAME' => array('MEMBER'), + 'DATA' => array('ARRAY'), + 'FAULT' => array('METHODRESPONSE'), + 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'), +); + + +/** + * Stores state during parsing + * + * quick explanation of components: + * + ac = accumulates values + * + qt = decides if quotes are needed for evaluation + * + cm = denotes struct or array (comma needed) + * + isf = indicates a fault + * + lv = indicates "looking for a value": implements the logic + * to allow values with no types to be strings + * + params = stores parameters in method calls + * + method = stores method name + * + * @global array $GLOBALS['XML_RPC_xh'] + */ +$GLOBALS['XML_RPC_xh'] = array(); + + +/** + * Start element handler for the XML parser + * + * @return void + */ +function XML_RPC_se($parser_resource, $name, $attrs) +{ + global $XML_RPC_xh, $XML_RPC_valid_parents; + + $parser = (int) $parser_resource; + + // if invalid xmlrpc already detected, skip all processing + if ($XML_RPC_xh[$parser]['isf'] >= 2) { + return; + } + + // check for correct element nesting + // top level element can only be of 2 types + if (count($XML_RPC_xh[$parser]['stack']) == 0) { + if ($name != 'METHODRESPONSE' && $name != 'METHODCALL') { + $XML_RPC_xh[$parser]['isf'] = 2; + $XML_RPC_xh[$parser]['isf_reason'] = gettext('missing top level xmlrpc element'); + return; + } + } else { + // not top level element: see if parent is OK + if (!in_array($XML_RPC_xh[$parser]['stack'][0], $XML_RPC_valid_parents[$name])) { + $name = preg_replace('@[^a-zA-Z0-9._-]@', '', $name); + $XML_RPC_xh[$parser]['isf'] = 2; + $XML_RPC_xh[$parser]['isf_reason'] = sprintf(gettext('xmlrpc element %1$s cannot be child of %2$s'), $name, $XML_RPC_xh[$parser]['stack'][0]); + return; + } + } + + switch ($name) { + case 'STRUCT': + $XML_RPC_xh[$parser]['cm']++; + + // turn quoting off + $XML_RPC_xh[$parser]['qt'] = 0; + + $cur_val = array(); + $cur_val['value'] = array(); + $cur_val['members'] = 1; + array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val); + break; + + case 'ARRAY': + $XML_RPC_xh[$parser]['cm']++; + + // turn quoting off + $XML_RPC_xh[$parser]['qt'] = 0; + + $cur_val = array(); + $cur_val['value'] = array(); + $cur_val['members'] = 0; + array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val); + break; + + case 'NAME': + $XML_RPC_xh[$parser]['ac'] = ''; + break; + + case 'FAULT': + $XML_RPC_xh[$parser]['isf'] = 1; + break; + + case 'PARAM': + $XML_RPC_xh[$parser]['valuestack'] = array(); + break; + + case 'VALUE': + $XML_RPC_xh[$parser]['lv'] = 1; + $XML_RPC_xh[$parser]['vt'] = $GLOBALS['XML_RPC_String']; + $XML_RPC_xh[$parser]['ac'] = ''; + $XML_RPC_xh[$parser]['qt'] = 0; + // look for a value: if this is still 1 by the + // time we reach the first data segment then the type is string + // by implication and we need to add in a quote + break; + + case 'I4': + case 'INT': + case 'STRING': + case 'BOOLEAN': + case 'DOUBLE': + case 'DATETIME.ISO8601': + case 'BASE64': + $XML_RPC_xh[$parser]['ac'] = ''; // reset the accumulator + + if ($name == 'DATETIME.ISO8601' || $name == 'STRING') { + $XML_RPC_xh[$parser]['qt'] = 1; + + if ($name == 'DATETIME.ISO8601') { + $XML_RPC_xh[$parser]['vt'] = $GLOBALS['XML_RPC_DateTime']; + } + + } elseif ($name == 'BASE64') { + $XML_RPC_xh[$parser]['qt'] = 2; + } else { + // No quoting is required here -- but + // at the end of the element we must check + // for data format errors. + $XML_RPC_xh[$parser]['qt'] = 0; + } + break; + + case 'MEMBER': + $XML_RPC_xh[$parser]['ac'] = ''; + break; + + case 'DATA': + case 'METHODCALL': + case 'METHODNAME': + case 'METHODRESPONSE': + case 'PARAMS': + // valid elements that add little to processing + break; + } + + + // Save current element to stack + array_unshift($XML_RPC_xh[$parser]['stack'], $name); + + if ($name != 'VALUE') { + $XML_RPC_xh[$parser]['lv'] = 0; + } +} + +/** + * End element handler for the XML parser + * + * @return void + */ +function XML_RPC_ee($parser_resource, $name) +{ + global $XML_RPC_xh; + + $parser = (int) $parser_resource; + + if ($XML_RPC_xh[$parser]['isf'] >= 2) { + return; + } + + // push this element from stack + // NB: if XML validates, correct opening/closing is guaranteed and + // we do not have to check for $name == $curr_elem. + // we also checked for proper nesting at start of elements... + $curr_elem = array_shift($XML_RPC_xh[$parser]['stack']); + + switch ($name) { + case 'STRUCT': + case 'ARRAY': + $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']); + $XML_RPC_xh[$parser]['value'] = $cur_val['value']; + $XML_RPC_xh[$parser]['vt'] = strtolower($name); + $XML_RPC_xh[$parser]['cm']--; + break; + + case 'NAME': + $XML_RPC_xh[$parser]['valuestack'][0]['name'] = $XML_RPC_xh[$parser]['ac']; + break; + + case 'BOOLEAN': + // special case here: we translate boolean 1 or 0 into PHP + // constants true or false + if ($XML_RPC_xh[$parser]['ac'] == '1') { + $XML_RPC_xh[$parser]['ac'] = 'true'; + } else { + $XML_RPC_xh[$parser]['ac'] = 'false'; + } + + $XML_RPC_xh[$parser]['vt'] = strtolower($name); + // Drop through intentionally. + + case 'I4': + case 'INT': + case 'STRING': + case 'DOUBLE': + case 'DATETIME.ISO8601': + case 'BASE64': + if ($XML_RPC_xh[$parser]['qt'] == 1) { + // we use double quotes rather than single so backslashification works OK + $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac']; + } elseif ($XML_RPC_xh[$parser]['qt'] == 2) { + $XML_RPC_xh[$parser]['value'] = base64_decode($XML_RPC_xh[$parser]['ac']); + } elseif ($name == 'BOOLEAN') { + $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac']; + } else { + // we have an I4, INT or a DOUBLE + // we must check that only 0123456789-.<space> are characters here + if (!preg_match("@^[+-]?[0123456789 \t\.]+$@", $XML_RPC_xh[$parser]['ac'])) { + XML_RPC_Base::raiseError(gettext('Non-numeric value received in INT or DOUBLE'), + XML_RPC_ERROR_NON_NUMERIC_FOUND); + $XML_RPC_xh[$parser]['value'] = XML_RPC_ERROR_NON_NUMERIC_FOUND; + } else { + // it's ok, add it on + $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac']; + } + } + + $XML_RPC_xh[$parser]['ac'] = ''; + $XML_RPC_xh[$parser]['qt'] = 0; + $XML_RPC_xh[$parser]['lv'] = 3; // indicate we've found a value + break; + + case 'VALUE': + if ($XML_RPC_xh[$parser]['vt'] == $GLOBALS['XML_RPC_String']) { + if (strlen($XML_RPC_xh[$parser]['ac']) > 0) { + $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac']; + } elseif ($XML_RPC_xh[$parser]['lv'] == 1) { + // The <value> element was empty. + $XML_RPC_xh[$parser]['value'] = ''; + } + } + + $temp = new XML_RPC_Value($XML_RPC_xh[$parser]['value'], $XML_RPC_xh[$parser]['vt']); + + $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']); + if (is_array($cur_val)) { + if ($cur_val['members']==0) { + $cur_val['value'][] = $temp; + } else { + $XML_RPC_xh[$parser]['value'] = $temp; + } + array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val); + } else { + $XML_RPC_xh[$parser]['value'] = $temp; + } + break; + + case 'MEMBER': + $XML_RPC_xh[$parser]['ac'] = ''; + $XML_RPC_xh[$parser]['qt'] = 0; + + $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']); + if (is_array($cur_val)) { + if ($cur_val['members']==1) { + $cur_val['value'][$cur_val['name']] = $XML_RPC_xh[$parser]['value']; + } + array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val); + } + break; + + case 'DATA': + $XML_RPC_xh[$parser]['ac'] = ''; + $XML_RPC_xh[$parser]['qt'] = 0; + break; + + case 'PARAM': + $XML_RPC_xh[$parser]['params'][] = $XML_RPC_xh[$parser]['value']; + break; + + case 'METHODNAME': + case 'RPCMETHODNAME': + $XML_RPC_xh[$parser]['method'] = preg_replace("@^[\n\r\t ]+@", '', + $XML_RPC_xh[$parser]['ac']); + break; + } + + // if it's a valid type name, set the type + if (isset($GLOBALS['XML_RPC_Types'][strtolower($name)])) { + $XML_RPC_xh[$parser]['vt'] = strtolower($name); + } +} + +/** + * Character data handler for the XML parser + * + * @return void + */ +function XML_RPC_cd($parser_resource, $data) +{ + global $XML_RPC_xh, $XML_RPC_backslash; + + $parser = (int) $parser_resource; + + if ($XML_RPC_xh[$parser]['lv'] != 3) { + // "lookforvalue==3" means that we've found an entire value + // and should discard any further character data + + if ($XML_RPC_xh[$parser]['lv'] == 1) { + // if we've found text and we're just in a <value> then + // turn quoting on, as this will be a string + $XML_RPC_xh[$parser]['qt'] = 1; + // and say we've found a value + $XML_RPC_xh[$parser]['lv'] = 2; + } + + // replace characters that eval would + // do special things with + if (!isset($XML_RPC_xh[$parser]['ac'])) { + $XML_RPC_xh[$parser]['ac'] = ''; + } + $XML_RPC_xh[$parser]['ac'] .= $data; + } +} + +/** + * The common methods and properties for all of the XML_RPC classes + * + * @category Web Services + * @package XML_RPC + * @author Edd Dumbill <edd@usefulinc.com> + * @author Stig Bakken <stig@php.net> + * @author Martin Jansen <mj@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_RPC + */ +class XML_RPC_Base { + + /** + * PEAR Error handling + * + * @return object PEAR_Error object + */ + function raiseError($msg, $code) + { + include_once 'PEAR.inc'; + if (is_object(@$this)) { + log_error(get_class($this) . ': ' . $msg . " {$code}"); + return PEAR::raiseError(get_class($this) . ': ' . $msg, $code); + } else { + log_error("XML_RPC: " . ': ' . $msg . " {$code}"); + return PEAR::raiseError('XML_RPC: ' . $msg, $code); + } + } + + /** + * Tell whether something is a PEAR_Error object + * + * @param mixed $value the item to check + * + * @return bool whether $value is a PEAR_Error object or not + * + * @access public + */ + function isError($value) + { + return is_a($value, 'PEAR_Error'); + } +} + +/** + * The methods and properties for submitting XML RPC requests + * + * @category Web Services + * @package XML_RPC + * @author Edd Dumbill <edd@usefulinc.com> + * @author Stig Bakken <stig@php.net> + * @author Martin Jansen <mj@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_RPC + */ +class XML_RPC_Client extends XML_RPC_Base { + + /** + * The path and name of the RPC server script you want the request to go to + * @var string + */ + var $path = ''; + + /** + * The name of the remote server to connect to + * @var string + */ + var $server = ''; + + /** + * The protocol to use in contacting the remote server + * @var string + */ + var $protocol = 'http://'; + + /** + * The port for connecting to the remote server + * + * The default is 80 for http:// connections + * and 443 for https:// and ssl:// connections. + * + * @var integer + */ + var $port = 80; + + /** + * A user name for accessing the RPC server + * @var string + * @see XML_RPC_Client::setCredentials() + */ + var $username = ''; + + /** + * A password for accessing the RPC server + * @var string + * @see XML_RPC_Client::setCredentials() + */ + var $password = ''; + + /** + * The name of the proxy server to use, if any + * @var string + */ + var $proxy = ''; + + /** + * The protocol to use in contacting the proxy server, if any + * @var string + */ + var $proxy_protocol = 'http://'; + + /** + * The port for connecting to the proxy server + * + * The default is 8080 for http:// connections + * and 443 for https:// and ssl:// connections. + * + * @var integer + */ + var $proxy_port = 8080; + + /** + * A user name for accessing the proxy server + * @var string + */ + var $proxy_user = ''; + + /** + * A password for accessing the proxy server + * @var string + */ + var $proxy_pass = ''; + + /** + * The error number, if any + * @var integer + */ + var $errno = 0; + + /** + * The error message, if any + * @var string + */ + var $errstr = ''; + + /** + * The current debug mode (1 = on, 0 = off) + * @var integer + */ + var $debug = 0; + + /** + * The HTTP headers for the current request. + * @var string + */ + var $headers = ''; + + + /** + * Sets the object's properties + * + * @param string $path the path and name of the RPC server script + * you want the request to go to + * @param string $server the URL of the remote server to connect to. + * If this parameter doesn't specify a + * protocol and $port is 443, ssl:// is + * assumed. + * @param integer $port a port for connecting to the remote server. + * Defaults to 80 for http:// connections and + * 443 for https:// and ssl:// connections. + * @param string $proxy the URL of the proxy server to use, if any. + * If this parameter doesn't specify a + * protocol and $port is 443, ssl:// is + * assumed. + * @param integer $proxy_port a port for connecting to the remote server. + * Defaults to 8080 for http:// connections and + * 443 for https:// and ssl:// connections. + * @param string $proxy_user a user name for accessing the proxy server + * @param string $proxy_pass a password for accessing the proxy server + * + * @return void + */ + function XML_RPC_Client($path, $server, $port = 0, + $proxy = '', $proxy_port = 0, + $proxy_user = '', $proxy_pass = '') + { + $this->path = $path; + $this->proxy_user = $proxy_user; + $this->proxy_pass = $proxy_pass; + + preg_match('@^(http://|https://|ssl://)?(.*)$@', $server, $match); + if ($match[1] == '') { + if ($port == 443) { + $this->server = $match[2]; + $this->protocol = 'ssl://'; + $this->port = 443; + } else { + $this->server = $match[2]; + if ($port) { + $this->port = $port; + } + } + } elseif ($match[1] == 'http://') { + $this->server = $match[2]; + if ($port) { + $this->port = $port; + } + } else { + $this->server = $match[2]; + $this->protocol = 'ssl://'; + if ($port) { + $this->port = $port; + } else { + $this->port = 443; + } + } + + if ($proxy) { + preg_match('@^(http://|https://|ssl://)?(.*)$@', $proxy, $match); + if ($match[1] == '') { + if ($proxy_port == 443) { + $this->proxy = $match[2]; + $this->proxy_protocol = 'ssl://'; + $this->proxy_port = 443; + } else { + $this->proxy = $match[2]; + if ($proxy_port) { + $this->proxy_port = $proxy_port; + } + } + } elseif ($match[1] == 'http://') { + $this->proxy = $match[2]; + if ($proxy_port) { + $this->proxy_port = $proxy_port; + } + } else { + $this->proxy = $match[2]; + $this->proxy_protocol = 'ssl://'; + if ($proxy_port) { + $this->proxy_port = $proxy_port; + } else { + $this->proxy_port = 443; + } + } + } + } + + /** + * Change the current debug mode + * + * @param int $in where 1 = on, 0 = off + * + * @return void + */ + function setDebug($in) + { + if ($in) { + $this->debug = 1; + } else { + $this->debug = 0; + } + } + + /** + * Sets whether strings that contain characters which may cause PHP's + * SAX-based XML parser to break should be automatically base64 encoded + * + * This is is a workaround for systems that don't have PHP's mbstring + * extension available. + * + * @param int $in where 1 = on, 0 = off + * + * @return void + */ + function setAutoBase64($in) + { + if ($in) { + $GLOBALS['XML_RPC_auto_base64'] = true; + } else { + $GLOBALS['XML_RPC_auto_base64'] = false; + } + } + + /** + * Set username and password properties for connecting to the RPC server + * + * @param string $u the user name + * @param string $p the password + * + * @return void + * + * @see XML_RPC_Client::$username, XML_RPC_Client::$password + */ + function setCredentials($u, $p) + { + $this->username = $u; + $this->password = $p; + } + + /** + * Transmit the RPC request via HTTP 1.0 protocol + * + * @param object $msg the XML_RPC_Message object + * @param int $timeout how many seconds to wait for the request + * + * @return object an XML_RPC_Response object. 0 is returned if any + * problems happen. + * + * @see XML_RPC_Message, XML_RPC_Client::XML_RPC_Client(), + * XML_RPC_Client::setCredentials() + */ + function send($msg, $timeout = 0) + { + if (!is_a($msg, 'XML_RPC_Message')) { + $this->errstr = sprintf( + gettext( + "send()'s %s parameter must be an XML_RPC_Message object." + ), $msg); + $this->raiseError($this->errstr, XML_RPC_ERROR_PROGRAMMING); + return 0; + } + $msg->debug = $this->debug; + return $this->sendPayloadHTTP10($msg, $this->server, $this->port, + $timeout, $this->username, + $this->password); + } + + /** + * Transmit the RPC request via HTTP 1.0 protocol + * + * Requests should be sent using XML_RPC_Client send() rather than + * calling this method directly. + * + * @param object $msg the XML_RPC_Message object + * @param string $server the server to send the request to + * @param int $port the server port send the request to + * @param int $timeout how many seconds to wait for the request + * before giving up + * @param string $username a user name for accessing the RPC server + * @param string $password a password for accessing the RPC server + * + * @return object an XML_RPC_Response object. 0 is returned if any + * problems happen. + * + * @access protected + * @see XML_RPC_Client::send() + */ + function sendPayloadHTTP10($msg, $server, $port, $timeout = 0, + $username = '', $password = '') + { + // Pre-emptive BC hacks for fools calling sendPayloadHTTP10() directly + if ($username != $this->username) { + $this->setCredentials($username, $password); + } + + // Only create the payload if it was not created previously + if (empty($msg->payload)) { + $msg->createPayload(); + } + $this->createHeaders($msg); + + $op = $this->headers . "\r\n\r\n"; + $op .= $msg->payload; + + if ($this->debug) { + print "\n<pre>---SENT---\n"; + print $op; + print "\n---END---</pre>\n"; + } + + /* + * If we're using a proxy open a socket to the proxy server + * instead to the xml-rpc server + */ + if ($this->proxy) { + if ($this->proxy_protocol == 'http://') { + $protocol = ''; + } else { + $protocol = $this->proxy_protocol; + } + if ($timeout > 0) { + $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port, + $this->errno, $this->errstr, $timeout); + } else { + $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port, + $this->errno, $this->errstr); + } + } else { + if ($this->protocol == 'http://') { + $protocol = ''; + } else { + $protocol = $this->protocol; + } + if ($timeout > 0) { + $fp = @fsockopen($protocol . $server, $port, + $this->errno, $this->errstr, $timeout); + } else { + $fp = @fsockopen($protocol . $server, $port, + $this->errno, $this->errstr); + } + } + + /* + * Just raising the error without returning it is strange, + * but keep it here for backwards compatibility. + */ + if (!$fp && $this->proxy) { + $this->raiseError(sprintf(gettext('Connection to proxy server + %1$s:%2$s failed. %3$s') + ,$this->proxy,$this->proxy_port,$this->errstr), + XML_RPC_ERROR_CONNECTION_FAILED); + return 0; + } elseif (!$fp) { + $this->raiseError(sprintf(gettext('Connection to RPC server + %1$s:%2$s failed. %3$s') + ,$server,$port,$this->errstr), + XML_RPC_ERROR_CONNECTION_FAILED); + return 0; + } + + if ($timeout) { + /* + * Using socket_set_timeout() because stream_set_timeout() + * was introduced in 4.3.0, but we need to support 4.2.0. + */ + socket_set_timeout($fp, $timeout); + } + + if (!fputs($fp, $op, strlen($op))) { + $this->errstr = 'Write error'; + return 0; + } + $resp = $msg->parseResponseFile($fp); + + $meta = socket_get_status($fp); + if ($meta['timed_out']) { + fclose($fp); + $this->errstr = 'RPC server did not send response before timeout.'; + $this->raiseError($this->errstr, XML_RPC_ERROR_CONNECTION_FAILED); + return 0; + } + + fclose($fp); + return $resp; + } + + /** + * Determines the HTTP headers and puts it in the $headers property + * + * @param object $msg the XML_RPC_Message object + * + * @return boolean TRUE if okay, FALSE if the message payload isn't set. + * + * @access protected + */ + function createHeaders($msg) + { + if (empty($msg->payload)) { + return false; + } + if ($this->proxy) { + $this->headers = 'POST ' . ($this->protocol=='ssl://'?'https://':$this->protocol). $this->server; + if ($this->proxy_port) { + $this->headers .= ':' . $this->port; + } + } else { + $this->headers = 'POST '; + } + $this->headers .= $this->path. " HTTP/1.0\r\n"; + + $this->headers .= "User-Agent: PEAR XML_RPC\r\n"; + $this->headers .= 'Host: ' . $this->server . "\r\n"; + + if ($this->proxy && $this->proxy_user) { + $this->headers .= 'Proxy-Authorization: Basic ' + . base64_encode("$this->proxy_user:$this->proxy_pass") + . "\r\n"; + } + + // thanks to Grant Rauscher <grant7@firstworld.net> for this + if ($this->username) { + $this->headers .= 'Authorization: Basic ' + . base64_encode("$this->username:$this->password") + . "\r\n"; + } + + $this->headers .= "Content-Type: text/xml\r\n"; + $this->headers .= 'Content-Length: ' . strlen($msg->payload); + return true; + } +} + +/** + * The methods and properties for interpreting responses to XML RPC requests + * + * @category Web Services + * @package XML_RPC + * @author Edd Dumbill <edd@usefulinc.com> + * @author Stig Bakken <stig@php.net> + * @author Martin Jansen <mj@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_RPC + */ +class XML_RPC_Response extends XML_RPC_Base +{ + var $xv; + var $fn; + var $fs; + var $hdrs; + + /** + * @return void + */ + function XML_RPC_Response($val, $fcode = 0, $fstr = '') + { + if ($fcode != 0) { + $this->fn = $fcode; + $this->fs = htmlspecialchars($fstr); + } else { + $this->xv = $val; + } + } + + /** + * @return int the error code + */ + function faultCode() + { + if (isset($this->fn)) { + return $this->fn; + } else { + return 0; + } + } + + /** + * @return string the error string + */ + function faultString() + { + return $this->fs; + } + + /** + * @return mixed the value + */ + function value() + { + return $this->xv; + } + + /** + * @return string the error message in XML format + */ + function serialize() + { + $rs = "<methodResponse>\n"; + if ($this->fn) { + $rs .= "<fault> + <value> + <struct> + <member> + <name>faultCode</name> + <value><int>" . $this->fn . "</int></value> + </member> + <member> + <name>faultString</name> + <value><string>" . $this->fs . "</string></value> + </member> + </struct> + </value> +</fault>"; + } else { + $rs .= "<params>\n<param>\n" . $this->xv->serialize() . + "</param>\n</params>"; + } + $rs .= "\n</methodResponse>"; + return $rs; + } +} + +/** + * The methods and properties for composing XML RPC messages + * + * @category Web Services + * @package XML_RPC + * @author Edd Dumbill <edd@usefulinc.com> + * @author Stig Bakken <stig@php.net> + * @author Martin Jansen <mj@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_RPC + */ +class XML_RPC_Message extends XML_RPC_Base +{ + /** + * Should the payload's content be passed through mb_convert_encoding()? + * + * @see XML_RPC_Message::setConvertPayloadEncoding() + * @since Property available since Release 1.5.1 + * @var boolean + */ + var $convert_payload_encoding = false; + + /** + * The current debug mode (1 = on, 0 = off) + * @var integer + */ + var $debug = 0; + + /** + * The encoding to be used for outgoing messages + * + * Defaults to the value of <var>$GLOBALS['XML_RPC_defencoding']</var> + * + * @var string + * @see XML_RPC_Message::setSendEncoding(), + * $GLOBALS['XML_RPC_defencoding'], XML_RPC_Message::xml_header() + */ + var $send_encoding = ''; + + /** + * The method presently being evaluated + * @var string + */ + var $methodname = ''; + + /** + * @var array + */ + var $params = array(); + + /** + * The XML message being generated + * @var string + */ + var $payload = ''; + + /** + * Should extra line breaks be removed from the payload? + * @since Property available since Release 1.4.6 + * @var boolean + */ + var $remove_extra_lines = true; + + /** + * The XML response from the remote server + * @since Property available since Release 1.4.6 + * @var string + */ + var $response_payload = ''; + + + /** + * @return void + */ + function XML_RPC_Message($meth, $pars = 0) + { + $this->methodname = $meth; + if (is_array($pars) && sizeof($pars) > 0) { + for ($i = 0; $i < sizeof($pars); $i++) { + $this->addParam($pars[$i]); + } + } + } + + /** + * Produces the XML declaration including the encoding attribute + * + * The encoding is determined by this class' <var>$send_encoding</var> + * property. If the <var>$send_encoding</var> property is not set, use + * <var>$GLOBALS['XML_RPC_defencoding']</var>. + * + * @return string the XML declaration and <methodCall> element + * + * @see XML_RPC_Message::setSendEncoding(), + * XML_RPC_Message::$send_encoding, $GLOBALS['XML_RPC_defencoding'] + */ + function xml_header() + { + global $XML_RPC_defencoding; + + if (!$this->send_encoding) { + $this->send_encoding = $XML_RPC_defencoding; + } + return '<?xml version="1.0" encoding="' . $this->send_encoding . '"?>' + . "\n<methodCall>\n"; + } + + /** + * @return string the closing </methodCall> tag + */ + function xml_footer() + { + return "</methodCall>\n"; + } + + /** + * Fills the XML_RPC_Message::$payload property + * + * Part of the process makes sure all line endings are in DOS format + * (CRLF), which is probably required by specifications. + * + * If XML_RPC_Message::setConvertPayloadEncoding() was set to true, + * the payload gets passed through mb_convert_encoding() + * to ensure the payload matches the encoding set in the + * XML declaration. The encoding type can be manually set via + * XML_RPC_Message::setSendEncoding(). + * + * @return void + * + * @uses XML_RPC_Message::xml_header(), XML_RPC_Message::xml_footer() + * @see XML_RPC_Message::setSendEncoding(), $GLOBALS['XML_RPC_defencoding'], + * XML_RPC_Message::setConvertPayloadEncoding() + */ + function createPayload() + { + $this->payload = $this->xml_header(); + $this->payload .= '<methodName>' . $this->methodname . "</methodName>\n"; + $this->payload .= "<params>\n"; + for ($i = 0; $i < sizeof($this->params); $i++) { + $p = $this->params[$i]; + $this->payload .= "<param>\n" . $p->serialize() . "</param>\n"; + } + $this->payload .= "</params>\n"; + $this->payload .= $this->xml_footer(); + if ($this->remove_extra_lines) { + $this->payload = preg_replace("@[\r\n]+@", "\r\n", $this->payload); + } else { + $this->payload = preg_replace("@\r\n|\n|\r|\n\r@", "\r\n", $this->payload); + } + if ($this->convert_payload_encoding) { + $this->payload = mb_convert_encoding($this->payload, $this->send_encoding); + } + } + + /** + * @return string the name of the method + */ + function method($meth = '') + { + if ($meth != '') { + $this->methodname = $meth; + } + return $this->methodname; + } + + /** + * @return string the payload + */ + function serialize() + { + $this->createPayload(); + return $this->payload; + } + + /** + * @return void + */ + function addParam($par) + { + $this->params[] = $par; + } + + /** + * Obtains an XML_RPC_Value object for the given parameter + * + * @param int $i the index number of the parameter to obtain + * + * @return object the XML_RPC_Value object. + * If the parameter doesn't exist, an XML_RPC_Response object. + * + * @since Returns XML_RPC_Response object on error since Release 1.3.0 + */ + function getParam($i) + { + global $XML_RPC_err, $XML_RPC_str; + + if (isset($this->params[$i])) { + return $this->params[$i]; + } else { + $this->raiseError(gettext('The submitted request did not contain this parameter'), + XML_RPC_ERROR_INCORRECT_PARAMS); + return new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'], + $XML_RPC_str['incorrect_params']); + } + } + + /** + * @return int the number of parameters + */ + function getNumParams() + { + return sizeof($this->params); + } + + /** + * Sets whether the payload's content gets passed through + * mb_convert_encoding() + * + * Returns PEAR_ERROR object if mb_convert_encoding() isn't available. + * + * @param int $in where 1 = on, 0 = off + * + * @return void + * + * @see XML_RPC_Message::setSendEncoding() + * @since Method available since Release 1.5.1 + */ + function setConvertPayloadEncoding($in) + { + if ($in && !function_exists('mb_convert_encoding')) { + return $this->raiseError(gettext('mb_convert_encoding() is not available'), + XML_RPC_ERROR_PROGRAMMING); + } + $this->convert_payload_encoding = $in; + } + + /** + * Sets the XML declaration's encoding attribute + * + * @param string $type the encoding type (ISO-8859-1, UTF-8 or US-ASCII) + * + * @return void + * + * @see XML_RPC_Message::setConvertPayloadEncoding(), XML_RPC_Message::xml_header() + * @since Method available since Release 1.2.0 + */ + function setSendEncoding($type) + { + $this->send_encoding = $type; + } + + /** + * Determine the XML's encoding via the encoding attribute + * in the XML declaration + * + * If the encoding parameter is not set or is not ISO-8859-1, UTF-8 + * or US-ASCII, $XML_RPC_defencoding will be returned. + * + * @param string $data the XML that will be parsed + * + * @return string the encoding to be used + * + * @link http://php.net/xml_parser_create + * @since Method available since Release 1.2.0 + */ + function getEncoding($data) + { + global $XML_RPC_defencoding; + + if (preg_match('@<\?xml[^>]*\s*encoding\s*=\s*[\'"]([^"\']*)[\'"]@', + $data, $match)) + { + $match[1] = trim(strtoupper($match[1])); + switch ($match[1]) { + case 'ISO-8859-1': + case 'UTF-8': + case 'US-ASCII': + return $match[1]; + break; + + default: + return $XML_RPC_defencoding; + } + } else { + return $XML_RPC_defencoding; + } + } + + /** + * @return object a new XML_RPC_Response object + */ + function parseResponseFile($fp) + { + $ipd = ''; + while ($data = @fread($fp, 8192)) { + $ipd .= $data; + } + return $this->parseResponse($ipd); + } + + /** + * @return object a new XML_RPC_Response object + */ + function parseResponse($data = '') + { + global $XML_RPC_xh, $XML_RPC_err, $XML_RPC_str, $XML_RPC_defencoding; + + $encoding = $this->getEncoding($data); + $parser_resource = xml_parser_create($encoding); + $parser = (int) $parser_resource; + + $XML_RPC_xh = array(); + $XML_RPC_xh[$parser] = array(); + + $XML_RPC_xh[$parser]['cm'] = 0; + $XML_RPC_xh[$parser]['isf'] = 0; + $XML_RPC_xh[$parser]['ac'] = ''; + $XML_RPC_xh[$parser]['qt'] = ''; + $XML_RPC_xh[$parser]['stack'] = array(); + $XML_RPC_xh[$parser]['valuestack'] = array(); + + xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true); + xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee'); + xml_set_character_data_handler($parser_resource, 'XML_RPC_cd'); + + $hdrfnd = 0; + if ($this->debug) { + print "\n<pre>---GOT---\n"; + print isset($_SERVER['SERVER_PROTOCOL']) ? htmlspecialchars($data) : $data; + print "\n---END---</pre>\n"; + } + + // See if response is a 200 or a 100 then a 200, else raise error. + // But only do this if we're using the HTTP protocol. + if (preg_match('@^HTTP@', $data) && + !preg_match('@^HTTP/[0-9\.]+ 200 @', $data) && + !preg_match('@^HTTP/[0-9\.]+ 10[0-9]([A-Z ]+)?[\r\n]+HTTP/[0-9\.]+ 200@', $data)) + { + $errstr = substr($data, 0, strpos($data, "\n") - 1); + error_log(sprintf(gettext("HTTP error, got response: %s"),$errstr)); + $r = new XML_RPC_Response(0, $XML_RPC_err['http_error'], + $XML_RPC_str['http_error'] . ' (' . + $errstr . ')'); + xml_parser_free($parser_resource); + return $r; + } + + // gotta get rid of headers here + if (!$hdrfnd && ($brpos = strpos($data,"\r\n\r\n"))) { + $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos); + $data = substr($data, $brpos + 4); + $hdrfnd = 1; + } + + /* + * be tolerant of junk after methodResponse + * (e.g. javascript automatically inserted by free hosts) + * thanks to Luca Mariano <luca.mariano@email.it> + */ + $data = substr($data, 0, strpos($data, "</methodResponse>") + 17); + $this->response_payload = $data; + + if (!xml_parse($parser_resource, $data, sizeof($data))) { + // thanks to Peter Kocks <peter.kocks@baygate.com> + if (xml_get_current_line_number($parser_resource) == 1) { + /* We already error on this in the GUI, no need to log it and cause a PHP error. */ + //$errstr = gettext("XML error at line 1, check URL"); + } else { + $errstr = sprintf('XML error: %s at line %d', + xml_error_string(xml_get_error_code($parser_resource)), + xml_get_current_line_number($parser_resource)); + } + if (!empty($errstr)) + error_log($errstr); + $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'], + $XML_RPC_str['invalid_return']); + xml_parser_free($parser_resource); + return $r; + } + + xml_parser_free($parser_resource); + + if ($this->debug) { + print "\n<pre>---PARSED---\n"; + var_dump($XML_RPC_xh[$parser]['value']); + print "---END---</pre>\n"; + } + + if ($XML_RPC_xh[$parser]['isf'] > 1) { + $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'], + $XML_RPC_str['invalid_return'].' '.$XML_RPC_xh[$parser]['isf_reason']); + } elseif (!is_object($XML_RPC_xh[$parser]['value'])) { + // then something odd has happened + // and it's time to generate a client side error + // indicating something odd went on + $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'], + $XML_RPC_str['invalid_return']); + } else { + $v = $XML_RPC_xh[$parser]['value']; + if ($XML_RPC_xh[$parser]['isf']) { + $f = $v->structmem('faultCode'); + $fs = $v->structmem('faultString'); + $r = new XML_RPC_Response($v, $f->scalarval(), + $fs->scalarval()); + } else { + $r = new XML_RPC_Response($v); + } + } + $r->hdrs = preg_split("@\r?\n@", $XML_RPC_xh[$parser]['ha'][1]); + return $r; + } +} + +/** + * The methods and properties that represent data in XML RPC format + * + * @category Web Services + * @package XML_RPC + * @author Edd Dumbill <edd@usefulinc.com> + * @author Stig Bakken <stig@php.net> + * @author Martin Jansen <mj@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_RPC + */ +class XML_RPC_Value extends XML_RPC_Base +{ + var $me = array(); + var $mytype = 0; + + /** + * @return void + */ + function XML_RPC_Value($val = -1, $type = '') + { + $this->me = array(); + $this->mytype = 0; + if ($val != -1 || $type != '') { + if ($type == '') { + $type = 'string'; + } + if (!array_key_exists($type, $GLOBALS['XML_RPC_Types'])) { + // XXX + // need some way to report this error + } elseif ($GLOBALS['XML_RPC_Types'][$type] == 1) { + $this->addScalar($val, $type); + } elseif ($GLOBALS['XML_RPC_Types'][$type] == 2) { + $this->addArray($val); + } elseif ($GLOBALS['XML_RPC_Types'][$type] == 3) { + $this->addStruct($val); + } + } + } + + /** + * @return int returns 1 if successful or 0 if there are problems + */ + function addScalar($val, $type = 'string') + { + if ($this->mytype == 1) { + $this->raiseError(gettext('Scalar can have only one value'), + XML_RPC_ERROR_INVALID_TYPE); + return 0; + } + $typeof = $GLOBALS['XML_RPC_Types'][$type]; + if ($typeof != 1) { + $this->raiseError( + sprintf(gettext("Not a scalar type (%s)"), $typeof), + XML_RPC_ERROR_INVALID_TYPE); + return 0; + } + + if ($type == $GLOBALS['XML_RPC_Boolean']) { + if (strcasecmp($val, 'true') == 0 + || $val == 1 + || ($val == true && strcasecmp($val, 'false'))) + { + $val = 1; + } else { + $val = 0; + } + } + + if ($this->mytype == 2) { + // we're adding to an array here + $ar = $this->me['array']; + $ar[] = new XML_RPC_Value($val, $type); + $this->me['array'] = $ar; + } else { + // a scalar, so set the value and remember we're scalar + $this->me[$type] = $val; + $this->mytype = $typeof; + } + return 1; + } + + /** + * @return int returns 1 if successful or 0 if there are problems + */ + function addArray($vals) + { + if ($this->mytype != 0) { + $this->raiseError( + sprintf(gettext('Already initialized as a [%s]'), $this->kindOf()), + XML_RPC_ERROR_ALREADY_INITIALIZED); + return 0; + } + $this->mytype = $GLOBALS['XML_RPC_Types']['array']; + $this->me['array'] = $vals; + return 1; + } + + /** + * @return int returns 1 if successful or 0 if there are problems + */ + function addStruct($vals) + { + if ($this->mytype != 0) { + $this->raiseError( + sprintf(gettext('Already initialized as a [%s]'), $this->kindOf()), + XML_RPC_ERROR_ALREADY_INITIALIZED); + return 0; + } + $this->mytype = $GLOBALS['XML_RPC_Types']['struct']; + $this->me['struct'] = $vals; + return 1; + } + + /** + * @return void + */ + function dump($ar) + { + reset($ar); + foreach ($ar as $key => $val) { + echo "$key => $val<br />"; + if ($key == 'array') { + foreach ($val as $key2 => $val2) { + echo "-- $key2 => $val2<br />"; + } + } + } + } + + /** + * @return string the data type of the current value + */ + function kindOf() + { + switch ($this->mytype) { + case 3: + return 'struct'; + + case 2: + return 'array'; + + case 1: + return 'scalar'; + + default: + return 'undef'; + } + } + + /** + * @return string the data in XML format + */ + function serializedata($typ, $val) + { + $rs = ''; + if (!array_key_exists($typ, $GLOBALS['XML_RPC_Types'])) { + // XXX + // need some way to report this error + return; + } + switch ($GLOBALS['XML_RPC_Types'][$typ]) { + case 3: + // struct + $rs .= "<struct>\n"; + reset($val); + foreach ($val as $key2 => $val2) { + $rs .= "<member><name>" . htmlspecialchars($key2) . "</name>\n"; + $rs .= $this->serializeval($val2); + $rs .= "</member>\n"; + } + $rs .= '</struct>'; + break; + + case 2: + // array + $rs .= "<array>\n<data>\n"; + foreach ($val as $value) { + $rs .= $this->serializeval($value); + } + $rs .= "</data>\n</array>"; + break; + + case 1: + switch ($typ) { + case $GLOBALS['XML_RPC_Base64']: + $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>"; + break; + case $GLOBALS['XML_RPC_Boolean']: + $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>"; + break; + case $GLOBALS['XML_RPC_String']: + $rs .= "<${typ}>" . htmlspecialchars($val). "</${typ}>"; + break; + default: + $rs .= "<${typ}>${val}</${typ}>"; + } + } + return $rs; + } + + /** + * @return string the data in XML format + */ + function serialize() + { + return $this->serializeval($this); + } + + /** + * @return string the data in XML format + */ + function serializeval($o) + { + if (!is_object($o) || empty($o->me) || !is_array($o->me)) { + return ''; + } + $ar = $o->me; + reset($ar); + list($typ, $val) = each($ar); + return '<value>' . $this->serializedata($typ, $val) . "</value>\n"; + } + + /** + * @return mixed the contents of the element requested + */ + function structmem($m) + { + return $this->me['struct'][$m]; + } + + /** + * @return void + */ + function structreset() + { + reset($this->me['struct']); + } + + /** + * @return the key/value pair of the struct's current element + */ + function structeach() + { + return each($this->me['struct']); + } + + /** + * @return mixed the current value + */ + function getval() + { + // UNSTABLE + + reset($this->me); + $b = current($this->me); + + // contributed by I Sofer, 2001-03-24 + // add support for nested arrays to scalarval + // i've created a new method here, so as to + // preserve back compatibility + + if (is_array($b)) { + foreach ($b as $id => $cont) { + $b[$id] = $cont->scalarval(); + } + } + + // add support for structures directly encoding php objects + if (is_object($b)) { + $t = get_object_vars($b); + foreach ($t as $id => $cont) { + $t[$id] = $cont->scalarval(); + } + foreach ($t as $id => $cont) { + $b->$id = $cont; + } + } + + // end contrib + return $b; + } + + /** + * @return mixed the current element's scalar value. If the value is + * not scalar, FALSE is returned. + */ + function scalarval() + { + reset($this->me); + $v = current($this->me); + if (!is_scalar($v)) { + $v = false; + } + return $v; + } + + /** + * @return string + */ + function scalartyp() + { + reset($this->me); + $a = key($this->me); + if ($a == $GLOBALS['XML_RPC_I4']) { + $a = $GLOBALS['XML_RPC_Int']; + } + return $a; + } + + /** + * @return mixed the struct's current element + */ + function arraymem($m) + { + return $this->me['array'][$m]; + } + + /** + * @return int the number of elements in the array + */ + function arraysize() + { + reset($this->me); + list($a, $b) = each($this->me); + return sizeof($b); + } + + /** + * Determines if the item submitted is an XML_RPC_Value object + * + * @param mixed $val the variable to be evaluated + * + * @return bool TRUE if the item is an XML_RPC_Value object + * + * @static + * @since Method available since Release 1.3.0 + */ + function isValue($val) + { + return (strtolower(get_class($val)) == 'xml_rpc_value'); + } +} + +/** + * Return an ISO8601 encoded string + * + * While timezones ought to be supported, the XML-RPC spec says: + * + * "Don't assume a timezone. It should be specified by the server in its + * documentation what assumptions it makes about timezones." + * + * This routine always assumes localtime unless $utc is set to 1, in which + * case UTC is assumed and an adjustment for locale is made when encoding. + * + * @return string the formatted date + */ +function XML_RPC_iso8601_encode($timet, $utc = 0) +{ + if (!$utc) { + $t = strftime('%Y%m%dT%H:%M:%S', $timet); + } else { + if (function_exists('gmstrftime')) { + // gmstrftime doesn't exist in some versions + // of PHP + $t = gmstrftime('%Y%m%dT%H:%M:%S', $timet); + } else { + $t = strftime('%Y%m%dT%H:%M:%S', $timet - date('Z')); + } + } + return $t; +} + +/** + * Convert a datetime string into a Unix timestamp + * + * While timezones ought to be supported, the XML-RPC spec says: + * + * "Don't assume a timezone. It should be specified by the server in its + * documentation what assumptions it makes about timezones." + * + * This routine always assumes localtime unless $utc is set to 1, in which + * case UTC is assumed and an adjustment for locale is made when encoding. + * + * @return int the unix timestamp of the date submitted + */ +function XML_RPC_iso8601_decode($idate, $utc = 0) +{ + $t = 0; + if (preg_match('@([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})@', $idate, $regs)) { + if ($utc) { + $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); + } else { + $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); + } + } + return $t; +} + +/** + * Converts an XML_RPC_Value object into native PHP types + * + * @param object $XML_RPC_val the XML_RPC_Value object to decode + * + * @return mixed the PHP values + */ +function XML_RPC_decode($XML_RPC_val) +{ + $kind = $XML_RPC_val->kindOf(); + + if ($kind == 'scalar') { + return $XML_RPC_val->scalarval(); + + } elseif ($kind == 'array') { + $size = $XML_RPC_val->arraysize(); + $arr = array(); + for ($i = 0; $i < $size; $i++) { + $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i)); + } + return $arr; + + } elseif ($kind == 'struct') { + $XML_RPC_val->structreset(); + $arr = array(); + while (list($key, $value) = $XML_RPC_val->structeach()) { + $arr[$key] = XML_RPC_decode($value); + } + return $arr; + } +} + +/** + * Converts native PHP types into an XML_RPC_Value object + * + * @param mixed $php_val the PHP value or variable you want encoded + * + * @return object the XML_RPC_Value object + */ +function XML_RPC_encode($php_val) +{ + $type = gettype($php_val); + $XML_RPC_val = new XML_RPC_Value; + + switch ($type) { + case 'array': + if (empty($php_val)) { + $XML_RPC_val->addArray($php_val); + break; + } + $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1)); + if (empty($tmp)) { + $arr = array(); + foreach ($php_val as $k => $v) { + $arr[$k] = XML_RPC_encode($v); + } + $XML_RPC_val->addArray($arr); + break; + } + // fall though if it's not an enumerated array + + case 'object': + $arr = array(); + foreach ($php_val as $k => $v) { + $arr[$k] = XML_RPC_encode($v); + } + $XML_RPC_val->addStruct($arr); + break; + + case 'integer': + $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_Int']); + break; + + case 'double': + $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_Double']); + break; + + case 'string': + case 'NULL': + if (preg_match('@^[0-9]{8}\T{1}[0-9]{2}\:[0-9]{2}\:[0-9]{2}$@', $php_val)) { + $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_DateTime']); + } elseif ($GLOBALS['XML_RPC_auto_base64'] + && preg_match("@[^ -~\t\r\n]@", $php_val)) + { + // Characters other than alpha-numeric, punctuation, SP, TAB, + // LF and CR break the XML parser, encode value via Base 64. + $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_Base64']); + } else { + $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_String']); + } + break; + + case 'boolean': + // Add support for encoding/decoding of booleans, since they + // are supported in PHP + // by <G_Giunta_2001-02-29> + $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_Boolean']); + break; + + case 'unknown type': + default: + $XML_RPC_val = false; + } + return $XML_RPC_val; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ + +?> diff --git a/src/etc/inc/xmlrpc_server.inc b/src/etc/inc/xmlrpc_server.inc new file mode 100644 index 0000000..f4d8a46 --- /dev/null +++ b/src/etc/inc/xmlrpc_server.inc @@ -0,0 +1,678 @@ +<?php + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * Server commands for our PHP implementation of the XML-RPC protocol + * + * This is a PEAR-ified version of Useful inc's XML-RPC for PHP. + * It has support for HTTP transport, proxies and authentication. + * + * PHP versions 4 and 5 + * + * @category Web Services + * @package XML_RPC + * @author Edd Dumbill <edd@usefulinc.com> + * @author Stig Bakken <stig@php.net> + * @author Martin Jansen <mj@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License + * @version SVN: $Id: Server.php 300961 2010-07-03 02:17:34Z danielc $ + * @link http://pear.php.net/package/XML_RPC + */ + + +/** + * Pull in the XML_RPC class + */ +require_once 'xmlrpc_client.inc'; + + +/** + * signature for system.listMethods: return = array, + * parameters = a string or nothing + * @global array $GLOBALS['XML_RPC_Server_listMethods_sig'] + */ +$GLOBALS['XML_RPC_Server_listMethods_sig'] = array( + array($GLOBALS['XML_RPC_Array'], + $GLOBALS['XML_RPC_String'] + ), + array($GLOBALS['XML_RPC_Array']) +); + +/** + * docstring for system.listMethods + * @global string $GLOBALS['XML_RPC_Server_listMethods_doc'] + */ +$GLOBALS['XML_RPC_Server_listMethods_doc'] = gettext('This method lists all the' + . ' methods that the XML-RPC server knows how to dispatch'); + +/** + * signature for system.methodSignature: return = array, + * parameters = string + * @global array $GLOBALS['XML_RPC_Server_methodSignature_sig'] + */ +$GLOBALS['XML_RPC_Server_methodSignature_sig'] = array( + array($GLOBALS['XML_RPC_Array'], + $GLOBALS['XML_RPC_String'] + ) +); + +/** + * docstring for system.methodSignature + * @global string $GLOBALS['XML_RPC_Server_methodSignature_doc'] + */ +$GLOBALS['XML_RPC_Server_methodSignature_doc'] = gettext('Returns an array of known' + . ' signatures (an array of arrays) for the method name passed. If' + . ' no signatures are known, returns a none-array (test for type !=' + . ' array to detect missing signature)'); + +/** + * signature for system.methodHelp: return = string, + * parameters = string + * @global array $GLOBALS['XML_RPC_Server_methodHelp_sig'] + */ +$GLOBALS['XML_RPC_Server_methodHelp_sig'] = array( + array($GLOBALS['XML_RPC_String'], + $GLOBALS['XML_RPC_String'] + ) +); + +/** + * docstring for methodHelp + * @global string $GLOBALS['XML_RPC_Server_methodHelp_doc'] + */ +$GLOBALS['XML_RPC_Server_methodHelp_doc'] = gettext('Returns help text if defined' + . ' for the method passed, otherwise returns an empty string'); + +/** + * dispatch map for the automatically declared XML-RPC methods. + * @global array $GLOBALS['XML_RPC_Server_dmap'] + */ +$GLOBALS['XML_RPC_Server_dmap'] = array( + 'system.listMethods' => array( + 'function' => 'XML_RPC_Server_listMethods', + 'signature' => $GLOBALS['XML_RPC_Server_listMethods_sig'], + 'docstring' => $GLOBALS['XML_RPC_Server_listMethods_doc'] + ), + 'system.methodHelp' => array( + 'function' => 'XML_RPC_Server_methodHelp', + 'signature' => $GLOBALS['XML_RPC_Server_methodHelp_sig'], + 'docstring' => $GLOBALS['XML_RPC_Server_methodHelp_doc'] + ), + 'system.methodSignature' => array( + 'function' => 'XML_RPC_Server_methodSignature', + 'signature' => $GLOBALS['XML_RPC_Server_methodSignature_sig'], + 'docstring' => $GLOBALS['XML_RPC_Server_methodSignature_doc'] + ) +); + +/** + * @global string $GLOBALS['XML_RPC_Server_debuginfo'] + */ +$GLOBALS['XML_RPC_Server_debuginfo'] = ''; + + +/** + * Lists all the methods that the XML-RPC server knows how to dispatch + * + * @return object a new XML_RPC_Response object + */ +function XML_RPC_Server_listMethods($server, $m) +{ + global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap; + + $v = new XML_RPC_Value(); + $outAr = array(); + foreach ($server->dmap as $key => $val) { + $outAr[] = new XML_RPC_Value($key, 'string'); + } + foreach ($XML_RPC_Server_dmap as $key => $val) { + $outAr[] = new XML_RPC_Value($key, 'string'); + } + $v->addArray($outAr); + return new XML_RPC_Response($v); +} + +/** + * Returns an array of known signatures (an array of arrays) + * for the given method + * + * If no signatures are known, returns a none-array + * (test for type != array to detect missing signature) + * + * @return object a new XML_RPC_Response object + */ +function XML_RPC_Server_methodSignature($server, $m) +{ + global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap; + + $methName = $m->getParam(0); + $methName = $methName->scalarval(); + if (strpos($methName, 'system.') === 0) { + $dmap = $XML_RPC_Server_dmap; + $sysCall = 1; + } else { + $dmap = $server->dmap; + $sysCall = 0; + } + // print "<!-- ${methName} -->\n"; + if (isset($dmap[$methName])) { + if ($dmap[$methName]['signature']) { + $sigs = array(); + $thesigs = $dmap[$methName]['signature']; + for ($i = 0; $i < sizeof($thesigs); $i++) { + $cursig = array(); + $inSig = $thesigs[$i]; + for ($j = 0; $j < sizeof($inSig); $j++) { + $cursig[] = new XML_RPC_Value($inSig[$j], 'string'); + } + $sigs[] = new XML_RPC_Value($cursig, 'array'); + } + $r = new XML_RPC_Response(new XML_RPC_Value($sigs, 'array')); + } else { + $r = new XML_RPC_Response(new XML_RPC_Value('undef', 'string')); + } + } else { + $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'], + $XML_RPC_str['introspect_unknown']); + } + return $r; +} + +/** + * Returns help text if defined for the method passed, otherwise returns + * an empty string + * + * @return object a new XML_RPC_Response object + */ +function XML_RPC_Server_methodHelp($server, $m) +{ + global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap; + + $methName = $m->getParam(0); + $methName = $methName->scalarval(); + if (strpos($methName, 'system.') === 0) { + $dmap = $XML_RPC_Server_dmap; + $sysCall = 1; + } else { + $dmap = $server->dmap; + $sysCall = 0; + } + + if (isset($dmap[$methName])) { + if ($dmap[$methName]['docstring']) { + $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]['docstring']), + 'string'); + } else { + $r = new XML_RPC_Response(new XML_RPC_Value('', 'string')); + } + } else { + $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'], + $XML_RPC_str['introspect_unknown']); + } + return $r; +} + +/** + * @return void + */ +function XML_RPC_Server_debugmsg($m) +{ + global $XML_RPC_Server_debuginfo; + $XML_RPC_Server_debuginfo = $XML_RPC_Server_debuginfo . $m . "\n"; +} + + +/** + * A server for receiving and replying to XML RPC requests + * + * <code> + * $server = new XML_RPC_Server( + * array( + * 'isan8' => + * array( + * 'function' => 'is_8', + * 'signature' => + * array( + * array('boolean', 'int'), + * array('boolean', 'int', 'boolean'), + * array('boolean', 'string'), + * array('boolean', 'string', 'boolean'), + * ), + * 'docstring' => 'Is the value an 8?' + * ), + * ), + * 1, + * 0 + * ); + * </code> + * + * @category Web Services + * @package XML_RPC + * @author Edd Dumbill <edd@usefulinc.com> + * @author Stig Bakken <stig@php.net> + * @author Martin Jansen <mj@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_RPC + */ +class XML_RPC_Server +{ + /** + * Should the payload's content be passed through mb_convert_encoding()? + * + * @see XML_RPC_Server::setConvertPayloadEncoding() + * @since Property available since Release 1.5.1 + * @var boolean + */ + var $convert_payload_encoding = false; + + /** + * The dispatch map, listing the methods this server provides. + * @var array + */ + var $dmap = array(); + + /** + * The present response's encoding + * @var string + * @see XML_RPC_Message::getEncoding() + */ + var $encoding = ''; + + /** + * Debug mode (0 = off, 1 = on) + * @var integer + */ + var $debug = 0; + + /** + * The response's HTTP headers + * @var string + */ + var $server_headers = ''; + + /** + * The response's XML payload + * @var string + */ + var $server_payload = ''; + + + /** + * The HTTP request data + * @null + */ + var $client_data = ''; + + /** + * Constructor for the XML_RPC_Server class + * + * @param array $dispMap the dispatch map. An associative array + * explaining each function. The keys of the main + * array are the procedure names used by the + * clients. The value is another associative array + * that contains up to three elements: + * + The 'function' element's value is the name + * of the function or method that gets called. + * To define a class' method: 'class::method'. + * + The 'signature' element (optional) is an + * array describing the return values and + * parameters + * + The 'docstring' element (optional) is a + * string describing what the method does + * @param int $serviceNow should the HTTP response be sent now? + * (1 = yes, 0 = no) + * @param int $debug should debug output be displayed? + * (1 = yes, 0 = no) + * + * @return void + */ + function XML_RPC_Server($dispMap, $serviceNow = 1, $debug = 0) + { + + if ($debug) { + $this->debug = 1; + } else { + $this->debug = 0; + } + + $this->dmap = $dispMap; + + if ($serviceNow) { + $this->service(); + } else { + $this->createServerPayload(); + $this->createServerHeaders(); + } + } + + /** + * @return string the debug information if debug debug mode is on + */ + function serializeDebug() + { + global $XML_RPC_Server_debuginfo; + + if ($this->debug) { + XML_RPC_Server_debugmsg('vvv POST DATA RECEIVED BY SERVER vvv' . "\n" + . $this->server_payload . $this->client_data + . "\n" . '^^^ END POST DATA ^^^'); + } + + if ($XML_RPC_Server_debuginfo != '') { + return "<!-- PEAR XML_RPC SERVER DEBUG INFO:\n\n" + . str_replace('--', '- - ', $XML_RPC_Server_debuginfo) + . "-->\n"; + } else { + return ''; + } + } + + /** + * Sets whether the payload's content gets passed through + * mb_convert_encoding() + * + * Returns PEAR_ERROR object if mb_convert_encoding() isn't available. + * + * @param int $in where 1 = on, 0 = off + * + * @return void + * + * @see XML_RPC_Message::getEncoding() + * @since Method available since Release 1.5.1 + */ + function setConvertPayloadEncoding($in) + { + if ($in && !function_exists('mb_convert_encoding')) { + return $this->raiseError('mb_convert_encoding() is not available', + XML_RPC_ERROR_PROGRAMMING); + } + $this->convert_payload_encoding = $in; + } + + /** + * Sends the response + * + * The encoding and content-type are determined by + * XML_RPC_Message::getEncoding() + * + * @return void + * + * @uses XML_RPC_Server::createServerPayload(), + * XML_RPC_Server::createServerHeaders() + */ + function service() + { + if (!$this->server_payload) { + $this->createServerPayload(); + } + if (!$this->server_headers) { + $this->createServerHeaders(); + } + + /* + * $server_headers needs to remain a string for compatibility with + * old scripts using this package, but PHP 4.4.2 no longer allows + * line breaks in header() calls. So, we split each header into + * an individual call. The initial replace handles the off chance + * that someone composed a single header with multiple lines, which + * the RFCs allow. + */ + $this->server_headers = preg_replace("@[\r\n]+[ \t]+@", + ' ', trim($this->server_headers)); + $headers = preg_split("@[\r\n]+@", $this->server_headers); + foreach ($headers as $header) + { + header($header); + } + + print $this->server_payload; + } + + /** + * Generates the payload and puts it in the $server_payload property + * + * If XML_RPC_Server::setConvertPayloadEncoding() was set to true, + * the payload gets passed through mb_convert_encoding() + * to ensure the payload matches the encoding set in the + * XML declaration. The encoding type can be manually set via + * XML_RPC_Message::setSendEncoding(). + * + * @return void + * + * @uses XML_RPC_Server::parseRequest(), XML_RPC_Server::$encoding, + * XML_RPC_Response::serialize(), XML_RPC_Server::serializeDebug() + * @see XML_RPC_Server::setConvertPayloadEncoding() + */ + function createServerPayload() + { + $this->client_data = file_get_contents("php://input"); + + $r = $this->parseRequest($this->client_data); + $this->server_payload = '<?xml version="1.0" encoding="' + . $this->encoding . '"?>' . "\n" + . $this->serializeDebug() + . $r->serialize(); + if ($this->convert_payload_encoding) { + $this->server_payload = mb_convert_encoding($this->server_payload, + $this->encoding); + } + } + + /** + * Determines the HTTP headers and puts them in the $server_headers + * property + * + * @return boolean TRUE if okay, FALSE if $server_payload isn't set. + * + * @uses XML_RPC_Server::createServerPayload(), + * XML_RPC_Server::$server_headers + */ + function createServerHeaders() + { + if (!$this->server_payload) { + return false; + } + $this->server_headers = 'Content-Length: ' + . strlen($this->server_payload) . "\r\n" + . 'Content-Type: text/xml;' + . ' charset=' . $this->encoding; + return true; + } + + /** + * @return array + */ + function verifySignature($in, $sig) + { + for ($i = 0; $i < sizeof($sig); $i++) { + // check each possible signature in turn + $cursig = $sig[$i]; + if (sizeof($cursig) == $in->getNumParams() + 1) { + $itsOK = 1; + for ($n = 0; $n < $in->getNumParams(); $n++) { + $p = $in->getParam($n); + // print "<!-- $p -->\n"; + if ($p->kindOf() == 'scalar') { + $pt = $p->scalartyp(); + } else { + $pt = $p->kindOf(); + } + // $n+1 as first type of sig is return type + if ($pt != $cursig[$n+1]) { + $itsOK = 0; + $pno = $n+1; + $wanted = $cursig[$n+1]; + $got = $pt; + break; + } + } + if ($itsOK) { + return array(1); + } + } + } + if (isset($wanted)) { + return array(0, "Wanted ${wanted}, got ${got} at param ${pno}"); + } else { + $allowed = array(); + foreach ($sig as $val) { + end($val); + $allowed[] = key($val); + } + $allowed = array_unique($allowed); + $last = count($allowed) - 1; + if ($last > 0) { + $allowed[$last] = 'or ' . $allowed[$last]; + } + return array(0, + 'Signature permits ' . implode(', ', $allowed) + . ' parameters but the request had ' + . $in->getNumParams()); + } + } + + /** + * @return object a new XML_RPC_Response object + * + * @uses XML_RPC_Message::getEncoding(), XML_RPC_Server::$encoding + */ + function parseRequest($data = '') + { + global $XML_RPC_xh, + $XML_RPC_err, $XML_RPC_str, $XML_RPC_errxml, + $XML_RPC_defencoding, $XML_RPC_Server_dmap; + + if ($data == '') { + $data = file_get_contents("php://input"); + $this->client_data = $data; + } + + $this->encoding = XML_RPC_Message::getEncoding($data); + $parser_resource = xml_parser_create($this->encoding); + $parser = (int) $parser_resource; + + $XML_RPC_xh[$parser] = array(); + $XML_RPC_xh[$parser]['cm'] = 0; + $XML_RPC_xh[$parser]['isf'] = 0; + $XML_RPC_xh[$parser]['params'] = array(); + $XML_RPC_xh[$parser]['method'] = ''; + $XML_RPC_xh[$parser]['stack'] = array(); + $XML_RPC_xh[$parser]['valuestack'] = array(); + + $plist = ''; + + // decompose incoming XML into request structure + + xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true); + xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee'); + xml_set_character_data_handler($parser_resource, 'XML_RPC_cd'); + if (!xml_parse($parser_resource, $data, 1)) { + // return XML error as a faultCode + $r = new XML_RPC_Response(0, + $XML_RPC_errxml+xml_get_error_code($parser_resource), + sprintf('XML error: %s at line %d', + xml_error_string(xml_get_error_code($parser_resource)), + xml_get_current_line_number($parser_resource))); + xml_parser_free($parser_resource); + } elseif ($XML_RPC_xh[$parser]['isf']>1) { + $r = new XML_RPC_Response(0, + $XML_RPC_err['invalid_request'], + $XML_RPC_str['invalid_request'] + . ': ' + . $XML_RPC_xh[$parser]['isf_reason']); + xml_parser_free($parser_resource); + } else { + xml_parser_free($parser_resource); + $m = new XML_RPC_Message($XML_RPC_xh[$parser]['method']); + // now add parameters in + for ($i = 0; $i < sizeof($XML_RPC_xh[$parser]['params']); $i++) { + // print '<!-- ' . $XML_RPC_xh[$parser]['params'][$i]. "-->\n"; + $plist .= "$i - " . var_export($XML_RPC_xh[$parser]['params'][$i], true) . " \n"; + $m->addParam($XML_RPC_xh[$parser]['params'][$i]); + } + + if ($this->debug) { + XML_RPC_Server_debugmsg($plist); + } + + // now to deal with the method + $methName = $XML_RPC_xh[$parser]['method']; + if (strpos($methName, 'system.') === 0) { + $dmap = $XML_RPC_Server_dmap; + $sysCall = 1; + } else { + $dmap = $this->dmap; + $sysCall = 0; + } + + if (isset($dmap[$methName]['function']) + && is_string($dmap[$methName]['function']) + && strpos($dmap[$methName]['function'], '::') !== false) + { + $dmap[$methName]['function'] = + explode('::', $dmap[$methName]['function']); + } + + if (isset($dmap[$methName]['function']) + && is_callable($dmap[$methName]['function'])) + { + // dispatch if exists + if (isset($dmap[$methName]['signature'])) { + $sr = $this->verifySignature($m, + $dmap[$methName]['signature'] ); + } + if (!isset($dmap[$methName]['signature']) || $sr[0]) { + // if no signature or correct signature + if ($sysCall) { + $r = call_user_func($dmap[$methName]['function'], $this, $m); + } else { + $r = call_user_func($dmap[$methName]['function'], $m); + } + if (!is_a($r, 'XML_RPC_Response')) { + $r = new XML_RPC_Response(0, $XML_RPC_err['not_response_object'], + $XML_RPC_str['not_response_object']); + } + } else { + $r = new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'], + $XML_RPC_str['incorrect_params'] + . ': ' . $sr[1]); + } + } else { + // else prepare error response + $r = new XML_RPC_Response(0, $XML_RPC_err['unknown_method'], + $XML_RPC_str['unknown_method']); + } + } + return $r; + } + + /** + * Echos back the input packet as a string value + * + * @return void + * + * Useful for debugging. + */ + function echoInput() + { + $r = new XML_RPC_Response(0); + $r->xv = new XML_RPC_Value("'Aha said I: '" . $this->client_data, 'string'); + print $r->serialize(); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ + +?> diff --git a/src/etc/inc/zeromq.inc b/src/etc/inc/zeromq.inc new file mode 100644 index 0000000..6b513d3 --- /dev/null +++ b/src/etc/inc/zeromq.inc @@ -0,0 +1,340 @@ +<?php +/* + zeromq.inc + part of the pfSense project (https://www.pfsense.org) + Copyright 2010 Scott Ullrich <sullrich@gmail.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +define('ZEROMQ_AUTH_FAIL', 'authfail'); +define('ZEROMQ_TRUE', 'true'); +define('ZEROMQ_FASLE', 'false'); + +$do_not_include_config_gui_inc = true; +require_once("auth.inc"); + +//$debug = true; + +/* zeromq_send: Send a message to a member node */ +function zeromq_send($protocol = "tcp", $ipaddress = "127.0.0.1", $port = "8888", + $method, $params, $username, $password) { + + global $debug; + + /* Set calling function and auth information */ + $xmlparams = array( + $username, + $password, + $method, + $params + ); + + /* Create new queue object */ + $queue = new ZMQSocket(new ZMQContext(), ZMQ::SOCKET_REQ, "MySock1"); + $queue->connect("{$protocol}://{$ipaddress}:{$port}"); + + /* Assign socket 1 to the queue, send and receive */ + $result = $queue->send(serialize($xmlparams))->recv(); + + /* xmlrpc_params_to_php() the result and return */ + $unserializedresult = unserialize($result); + + /* Return the result to the caller */ + return $unserializedresult; +} + +function zeromq_server($protocol = "tcp", $ipaddress = "127.0.0.1", $port = "8888") { + global $debug; + if (!$ipaddress || !$port) { + if ($debug) { + echo "ERROR: You must pass, proto, ipaddress and port\n"; + } + return; + } + if ($debug) { + echo "Creating ZMQSocket()\n"; + } + $server = new ZMQSocket(new ZMQContext(), ZMQ::SOCKET_REP); + if ($debug) { + echo "Binding to {$protocol}://{$ipaddress}:{$port}\n"; + } + $server->bind("{$protocol}://{$ipaddress}:{$port}"); + if ($debug) { + echo "Entering while() loop\n"; + } + while ($msg = $server->recv()) { + // Convert the XML to a PHP array + $message = unserialize($msg); + if ($debug) { + echo "Message received:\n"; + print_r($message); + } + switch ($message[2]) { + case "pfsense.exec_shell": + $function_to_call = "exec_shell_zeromq"; + break; + case "pfsense.exec_php": + $function_to_call = "exec_php_zeromq"; + break; + case "pfsense.filter_configure": + $function_to_call = "filter_configure_zeromq"; + break; + case "pfsense.interfaces_carp_configure": + $function_to_call = "interfaces_carp_configure_zeromq"; + break; + case "pfsense.backup_config_section": + $function_to_call = "backup_config_section_zeromq"; + break; + case "pfsense.restore_config_section": + $function_to_call = "restore_config_section_zeromq"; + break; + case "pfsense.merge_config_section": + $function_to_call = "merge_config_section_zeromq"; + break; + case "pfsense.merge_installedpackages_section_zeromq": + $function_to_call = "merge_installedpackages_section_zeromq"; + break; + case "pfsense.check_firmware_version": + $function_to_call = "check_firmware_version_zeromq"; + break; + case "pfsense.reboot": + $function_to_call = "reboot_zeromq"; + break; + case "pfsense.get_notices": + $function_to_call = "get_notices_zeromq"; + break; + } + if (!$function_to_call) { + if ($debug) { + echo "ERROR: Could not find a function to call"; + } + return; + } else { + if ($debug) { + echo "Invoking function {$message[2]}()\n;"; + } + } + /* Call function that is being invoked */ + $result = $function_to_call($message); + /* echo back the result */ + $server->send($result); + } +} + +function zeromq_auth($params) { + global $config, $g, $debug; + + $username = $params[0]; + $passwd = $params[1]; + + $user = getUserEntry($username); + if (!$user) { + if ($debug) { + echo "Could not locate user $username with getUserEntry()\n"; + } + return false; + } + + if (is_account_disabled($username) || is_account_expired($username)) { + if ($debug) { + echo "Returning account expired/disabled\n"; + } + return false; + } + + if ($user['password']) { + $passwd = crypt($passwd, $user['password']); + if ($passwd == $user['password']) { + return true; + } + } + + if ($user['md5-hash']) { + $passwd = md5($passwd); + if ($passwd == $user['md5-hash']) { + return true; + } + } + + if ($debug) { + echo "zeromq_auth() fall through == false\n"; + } + + return false; +} + +function exec_php_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + if ($debug) { + echo "Auth failed in exec_shell_zeromq()\n"; + } + return ZEROMQ_AUTH_FAIL; + } + $exec_php = $params[3]; + if ($debug) { + echo "Running exec_php_zeromq(): {$exec_php}\n"; + } + eval($exec_php); + if ($toreturn) { + return serialize($toreturn); + } else { + return ZEROMQ_FASLE; + } +} + +function exec_shell_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + if ($debug) { + echo "Auth failed in exec_shell_zeromq()\n"; + } + return ZEROMQ_AUTH_FAIL; + } + $shell_cmd = $params[3]; + if ($debug) { + echo "Running exec_shell_zeromq(): {$shell_cmd}\n"; + } + mwexec($shell_cmd); + return ZEROMQ_FASLE; +} + +function backup_config_section_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + return ZEROMQ_AUTH_FAIL; + } + $val = array_intersect_key($config, array_flip($params[3])); + return serialize($val); +} + +function restore_config_section_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + return ZEROMQ_AUTH_FAIL; + } + $config = array_merge($config, $params[3]); + $mergedkeys = implode(",", array_keys($params[3])); + write_config(sprintf(gettext("Merged in config (%s sections) from ZeroMQ client."), $mergedkeys)); + return ZEROMQ_FASLE; +} + +function merge_installedpackages_section_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + return ZEROMQ_AUTH_FAIL; + } + $config['installedpackages'] = array_merge($config['installedpackages'], $params[0]); + $mergedkeys = implode(",", array_keys($params[3])); + write_config(sprintf(gettext("Merged in config (%s sections) from ZeroMQ client."), $mergedkeys)); + return ZEROMQ_FASLE; +} + +function merge_config_section_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + return ZEROMQ_AUTH_FAIL; + } + $config = array_merge_recursive_unique($config, $params[0]); + $mergedkeys = implode(",", array_keys($params[3])); + write_config("Merged in config ({$mergedkeys} sections) from ZeroMQ client."); + return ZEROMQ_FASLE; +} + +function filter_configure_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + return ZEROMQ_AUTH_FAIL; + } + filter_configure(); + system_routing_configure(); + setup_gateways_monitor(); + relayd_configure(); + require_once("openvpn.inc"); + openvpn_resync_all(); + services_dhcpd_configure(); + if (isset($config['dnsmasq']['enable'])) { + services_dnsmasq_configure(); + } elseif (isset($config['unbound']['enable'])) { + services_unbound_configure(); + } + local_sync_accounts(); + return ZEROMQ_FASLE; +} + +function interfaces_carp_configure_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + return ZEROMQ_AUTH_FAIL; + } + interfaces_sync_setup(); + interfaces_vips_configure(); + return ZEROMQ_FASLE; +} + +function check_firmware_version_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + return ZEROMQ_AUTH_FAIL; + } + return serialize(check_firmware_version(false)); +} + +function reboot_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + return ZEROMQ_AUTH_FAIL; + } + mwexec_bg("/etc/rc.reboot"); + return ZEROMQ_FASLE; +} + +function get_notices_zeromq($raw_params) { + global $config, $g, $debug; + $params = $raw_params; + if (zeromq_auth($raw_params) == false) { + return ZEROMQ_AUTH_FAIL; + } + if (!function_exists("get_notices")) { + require("notices.inc"); + } + if (!$params) { + $toreturn = get_notices(); + } else { + $toreturn = get_notices($params); + } + return serialize($toreturn); +} + +?> |