%PDF- %PDF-
Direktori : /backups/router/usr/local/etc/inc/ |
Current File : //backups/router/usr/local/etc/inc/util.inc |
<?php /* * Copyright (C) 2014-2023 Franco Fichtner <franco@opnsense.org> * Copyright (C) 2010 Ermal Luçi * Copyright (C) 2005-2006 Colin Smith <ethethlay@gmail.com> * Copyright (C) 2004-2007 Scott Ullrich <sullrich@gmail.com> * 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. */ require_once 'IPv6.inc'; /* XXX only two callers left, better remove this unreliable function */ function killbyname($procname, $sig = 'TERM', $waitforit = true) { /* pgrep -n only kills the newest matching process */ _killbypid(shell_safe('/bin/pgrep -anx %s', $procname), $sig, $waitforit, "name:$procname"); } function killbypid($pid_or_file, $sig = 'TERM', $waitforit = true) { $pid = $pid_or_file; if (strpos($pid_or_file, '/') !== false) { $pid = trim(@file_get_contents($pid_or_file) ?? ''); } _killbypid($pid, $sig, $waitforit, "pid:$pid_or_file"); } function _killbypid($pid, $sig, $waitforit, $caller) { if (!is_numeric($pid) || $pid <= 1) { return; } mwexecf('/bin/kill -%s %s', [$sig, $pid], $caller); if (!$waitforit || $sig == 'HUP') { return; } while (mwexecf('/bin/kill -0 %s', $pid, true) == 0) { usleep(200 * 1000); } } function isvalidpid($pidfile) { if (file_exists($pidfile)) { return mwexecf('/bin/pgrep -nF %s', $pidfile, true) == 0; } return false; } function waitforpid($pidfile, $timeout = -1) { $msec = $timeout * 1000; while (!isvalidpid($pidfile)) { usleep(200 * 1000); if ($timeout != -1) { $msec -= 200; if ($msec < 0) { return 0; } } } return trim(file_get_contents($pidfile)); } function waitfordad($grace_period = 1) { $dad_delay = (int)get_single_sysctl('net.inet6.ip6.dad_count'); if ($dad_delay) { sleep($dad_delay + $grace_period); } } function is_process_running($process) { exec('/bin/pgrep -anx ' . escapeshellarg($process), $output, $retval); return (intval($retval) == 0); } function service_by_filter($filter = []) { return service_by_name('*', $filter); } function service_by_name($name, $filter = []) { $services = plugins_services(); foreach ($services as $service) { /* skip unless filter wildcard is set */ if ($name != '*' && $service['name'] != $name) { continue; } if (!count($filter)) { /* force (mis)match if filter is empty (standard behaviour) */ $filter['name'] = $name; } foreach ($filter as $key => $value) { if ( isset($service[$key]) && ($service[$key] == $value || (is_array($service[$key]) && in_array($value, $service[$key]))) ) { return $service; } } } return []; } function service_status($service) { if (!empty($service['nocheck'])) { return true; } if (isset($service['pidfile'])) { return isvalidpid($service['pidfile']); } return is_process_running($service['name']); } function service_message($service) { /* * Emulate the following messages from rc system: * * syslog_ng is running as pid 11645. * syslog_ng is not running. */ $pid = ''; $status = false; if (!empty($service['nocheck'])) { /* do not expose pid / may not exist */ $status = true; } elseif (isset($service['pidfile'])) { $status = isvalidpid($service['pidfile']); if ($status) { /* could be wrong pid but best effort only */ $pid = ' as pid ' . trim(@file_get_contents($service['pidfile']) ?? ''); } } else { $status = is_process_running($service['name']); if ($status) { /* could be wrong pid but best effort only */ $pid = ' as pid ' . shell_safe('/bin/pgrep -anx %s', $service['name']); } } return sprintf('%s is %s.', service_name($service), $status ? 'running' . $pid : 'not running'); } function service_name($service) { return $service['name'] . (array_key_exists('id', $service) ? "[{$service['id']}]" : ''); } function service_control_get($name, $extras) { $filter = []; if (array_key_exists('id', $extras) && $extras['id'] !== '') { $filter['id'] = $extras['id']; } $service = service_by_name($name, $filter); if (empty($service)) { $name .= isset($filter['id']) ? "[{$filter['id']}]" : ''; return sprintf(gettext("Could not find unknown service `%s'"), htmlspecialchars($name)); } return $service; } function service_control_start($name, $extras) { $service = service_control_get($name, $extras); if (!is_array($service)) { return $service; } if (isset($service['configd']['start'])) { foreach ($service['configd']['start'] as $cmd) { configd_run($cmd); } } elseif (isset($service['php']['start'])) { foreach ($service['php']['start'] as $cmd) { $params = []; if (isset($service['php']['args'])) { foreach ($service['php']['args'] as $param) { $params[] = $service[$param]; } } call_user_func_array($cmd, $params); } } elseif (isset($service['mwexec']['start'])) { foreach ($service['mwexec']['start'] as $cmd) { mwexec($cmd); } } else { return sprintf(gettext("Could not start service `%s'"), htmlspecialchars(service_name($service))); } return sprintf(gettext("Service `%s' has been started."), htmlspecialchars(service_name($service))); } function service_control_stop($name, $extras) { $service = service_control_get($name, $extras); if (!is_array($service)) { return $service; } if (isset($service['configd']['stop'])) { foreach ($service['configd']['stop'] as $cmd) { configd_run($cmd); } } elseif (isset($service['php']['stop'])) { foreach ($service['php']['stop'] as $cmd) { $cmd(); } } elseif (isset($service['mwexec']['stop'])) { foreach ($service['mwexec']['stop'] as $cmd) { mwexec($cmd); } } elseif (isset($service['pidfile'])) { killbypid($service['pidfile']); } else { /* last resort, but not very elegant */ killbyname($service['name']); } return sprintf(gettext("Service `%s' has been stopped."), htmlspecialchars(service_name($service))); } function service_control_restart($name, $extras) { $service = service_control_get($name, $extras); if (!is_array($service)) { return $service; } if (isset($service['configd']['restart'])) { foreach ($service['configd']['restart'] as $cmd) { configd_run($cmd); } } elseif (isset($service['php']['restart'])) { foreach ($service['php']['restart'] as $cmd) { $params = []; if (isset($service['php']['args'])) { foreach ($service['php']['args'] as $param) { $params[] = $service[$param]; } } call_user_func_array($cmd, $params); } } elseif (isset($service['mwexec']['restart'])) { foreach ($service['mwexec']['restart'] as $cmd) { mwexec($cmd); } } else { return sprintf(gettext("Could not restart service `%s'"), htmlspecialchars(service_name($service))); } return sprintf(gettext("Service `%s' has been restarted."), htmlspecialchars(service_name($service))); } function is_subsystem_dirty($subsystem = '') { return file_exists("/tmp/{$subsystem}.dirty"); } function mark_subsystem_dirty($subsystem = '') { touch("/tmp/{$subsystem}.dirty"); } function clear_subsystem_dirty($subsystem = '') { @unlink("/tmp/{$subsystem}.dirty"); } function exit_on_bootup($callback = null, $arguments = []) { if (product::getInstance()->booting()) { if ($callback) { call_user_func_array($callback, $arguments); } /* intentionally we exit here to avoid future cleverness WRT bootup handling */ exit(0); } } /** * service logger with additional boot-time performance logging * @param $message string message to log * @param $verbose boolean verbose|bool (send to console), * @return void */ function service_log(string $message, bool $verbose = true): void { if ($verbose) { echo $message; flush(); } if (product::getInstance()->booting()) { $bt = debug_backtrace(); $caller = !empty($bt[1]['function']) ? $bt[1]['function'] : 'unknown'; file_put_contents('/var/log/boot.log', implode(' ', [ (new DateTime())->format('c'), $caller . '[' . getmypid() . ']', rtrim($message) . PHP_EOL ]), FILE_APPEND | LOCK_EX); } } /* 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); } /* return the subnet address given a host address and a subnet bit count */ function gen_subnet($ipaddr, $bits) { if (!is_ipaddr($ipaddr) || !is_numeric($bits)) { return ''; } return long2ip(ip2long($ipaddr) & gen_subnet_mask_long($bits)); } /* return the subnet address given a host address and a subnet bit count */ function gen_subnetv6($ipaddr, $bits) { if (!is_ipaddrv6($ipaddr) || !is_numeric($bits)) { return ''; } $address = Net_IPv6::getNetmask($ipaddr, $bits); $address = Net_IPv6::compress($address); return $address; } /* return the highest (broadcast) address in the subnet given a host address and a subnet bit count */ function gen_subnet_max($ipaddr, $bits) { if (!is_ipaddr($ipaddr) || !is_numeric($bits)) { return ''; } return long2ip32(ip2long($ipaddr) | ~gen_subnet_mask_long($bits)); } /* Generate end number for a given ipv6 subnet mask */ function gen_subnetv6_max($ipaddr, $bits) { $result = false; if (!is_ipaddrv6($ipaddr)) { return false; } set_error_handler( function () { return; } ); $mask = Net_IPv6::getNetmask('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', $bits); $inet_ip = (binary)inet_pton($ipaddr); if ($inet_ip) { $inet_mask = (binary)inet_pton($mask); if ($inet_mask) { $inet_end = $inet_ip | ~$inet_mask; $result = inet_ntop($inet_end); } } restore_error_handler(); return $result; } /* returns the calculated bit length of the prefix delegation from a WAN interface */ function calculate_ipv6_delegation_length($if) { global $config; $pdlen = -1; if (!isset($config['interfaces'][$if]['ipaddrv6'])) { return $pdlen; } 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'])); if ($pdlen == 0) { /* XXX bug reports this is not working, needs investigation */ $pdlen = -1; } break; case 'dhcp6': $dhcp6cfg = $config['interfaces'][$if]; if (!empty($dhcp6cfg['adv_dhcp6_config_file_override'])) { /* emit error since we cannot cope with custom config */ $pdlen = -1; } elseif (!empty($dhcp6cfg['adv_dhcp6_config_advanced']) && is_numeric($dhcp6cfg['adv_dhcp6_prefix_interface_statement_sla_len'])) { $pdlen = $dhcp6cfg['adv_dhcp6_prefix_interface_statement_sla_len']; } elseif (is_numeric($dhcp6cfg['dhcp6-ia-pd-len'])) { $pdlen = $dhcp6cfg['dhcp6-ia-pd-len']; } break; default: break; } return $pdlen; } /* 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 the smallest possible subnet mask for given IP range */ function find_smallest_cidr($ips, $family = 'inet') { return $family == 'inet6' ? find_smallest_cidr6($ips) : find_smallest_cidr4($ips); } /* Find the smallest possible subnet mask for given IPv4 range */ function find_smallest_cidr4($ips) { foreach ($ips as $id => $ip) { $ips[$id] = ip2long($ip); } for ($bits = 0; $bits <= 32; $bits += 1) { $mask = (0xffffffff << $bits) & 0xffffffff; $test = []; foreach ($ips as $ip) { $test[$ip & $mask] = true; } if (count($test) == 1) { /* one element means CIDR size matches all */ break; } } return 32 - $bits; } /* Find the smallest possible subnet mask for given IPv6 range */ function find_smallest_cidr6($ips) { foreach ($ips as $id => $ip) { $ips[$id] = unpack('N*', inet_pton($ip)); } for ($bits = 0; $bits <= 128; $bits += 1) { $mask1 = (0xffffffff << max($bits - 96, 0)) & 0xffffffff; $mask2 = (0xffffffff << max($bits - 64, 0)) & 0xffffffff; $mask3 = (0xffffffff << max($bits - 32, 0)) & 0xffffffff; $mask4 = (0xffffffff << $bits) & 0xffffffff; $test = []; foreach ($ips as $ip) { $test[sprintf('%032b%032b%032b%032b', $ip[1] & $mask1, $ip[2] & $mask2, $ip[3] & $mask3, $ip[4] & $mask4)] = true; } if (count($test) == 1) { /* one element means CIDR size matches all */ break; } } return 128 - $bits; } /* merge IPv6 address prefix of default size 64 with suffix */ function merge_ipv6_address($prefix, $suffix, $size = 64) { if (strpos($suffix, '::') !== 0) { /* do not override full addresses */ return $suffix; } $size = 128 - $size; $prefix_bits = unpack('N*', inet_pton($prefix)); $suffix_bits = unpack('N*', inet_pton($suffix)); $mask = [ 1 => (0xffffffff << max($size - 96, 0)) & 0xffffffff, 2 => (0xffffffff << max($size - 64, 0)) & 0xffffffff, 3 => (0xffffffff << max($size - 32, 0)) & 0xffffffff, 4 => (0xffffffff << $size) & 0xffffffff, ]; $result = ''; for ($pos = 1; $pos <= 4; $pos += 1) { $result .= sprintf('%08x', ($prefix_bits[$pos] & $mask[$pos]) | ($suffix_bits[$pos] & (~$mask[$pos] & 0xffffffff))); } return Net_IPv6::compress(implode(':', str_split($result, 4))); } /* returns true if $ipaddr is a valid dotted IPv4 address or an IPv6 */ function is_ipaddr($ipaddr) { if (is_ipaddrv4($ipaddr)) { return true; } if (is_ipaddrv6($ipaddr)) { return true; } 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]; } if (strpos($ipaddr, ":") === false) { return false; } elseif (strpos($ipaddr, "/") !== false) { return false; // subnet is not an address } else { 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)) { return false; } $ip_long = ip2long($ipaddr); $ip_reverse = long2ip32($ip_long); if ($ipaddr == $ip_reverse) { return true; } else { return false; } } /* returns true if $ipaddr is a valid linklocal address (inside fe80::/10) */ function is_linklocal($ipaddr) { return !!preg_match('/^fe[89ab][0-9a-f]:/i', $ipaddr ?? ''); } /* returns true if $ipaddr is a valid uniquelocal address (inside fd00::/8) */ function is_uniquelocal($ipaddr) { return !!preg_match('/^fd[0-9a-f][0-9a-f]:/i', $ipaddr ?? ''); } /* 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); } function is_ipaddrwithport($ipport) { $parts = explode(":", $ipport); $port = array_pop($parts); if (count($parts) == 1) { return is_ipaddrv4($parts[0]) && is_port($port); } elseif (count($parts) > 1) { return is_literalipaddrv6(implode(":", $parts)) && is_port($port); } 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) { if (is_alias($ipaddr)) { foreach ((new \OPNsense\Firewall\Alias())->aliasIterator() as $alias) { if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type'])) { return true; } } return false; } 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 $hostname is a valid hostname */ function is_hostname($hostname) { if (!is_string($hostname)) { return false; } if (preg_match('/^(?:(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])\.)*(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname)) { return true; } else { return false; } } /* returns true if $domain is a valid domain name */ function is_domain($domain, $allow_root = false) { if (!is_string($domain)) { return false; } elseif (preg_match('/^(?:(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])$/i', $domain)) { return true; } elseif ($allow_root && $domain == '.') { return true; } return false; } /* returns true if $macaddr is a valid MAC address */ function is_macaddr($macaddr, $partial = false) { return !!preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){' . ($partial ? '1,' : '') . '5}$/i', $macaddr); } /* returns true if $port is a valid TCP/UDP port */ function is_port($port) { return OPNsense\Firewall\Util::isPort($port, 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) { if (is_alias($port)) { foreach ((new \OPNsense\Firewall\Alias())->aliasIterator() as $alias) { if ($alias['name'] == $port && preg_match("/port/i", $alias['type'])) { return true; } } return false; } return is_port($port); } /* 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); } function get_configured_carp_interface_list() { $carp_list = []; foreach (config_read_array('virtualip', 'vip') as $vip) { if ($vip['mode'] == 'carp') { $carp_list["{$vip['interface']}_vip{$vip['vhid']}"] = $vip['subnet']; } } return $carp_list; } function get_configured_ip_aliases_list() { $alias_list = []; foreach (config_read_array('virtualip', 'vip') as $vip) { if ($vip['mode'] == 'ipalias') { $alias_list[$vip['subnet']] = $vip['interface']; } } return $alias_list; } function get_configured_interface_with_descr() { $iflist = []; foreach (legacy_config_get_interfaces(['virtual' => false]) as $if => $ifdetail) { if (isset($ifdetail['enable'])) { $iflist[$if] = $ifdetail['descr']; } } return $iflist; } function get_primary_interface_from_list($iflist = null) { $ret = null; if ($iflist === null) { $iflist = array_keys(get_configured_interface_with_descr()); } /* we pull the primary entry from a "priority" list: lan, optX or wan */ natsort($iflist); foreach ($iflist as $if) { if (preg_match('/^(lan|opt[0-9]+|wan)$/', $if)) { $ret = $if; break; } } return $ret; } /* * get_configured_ip_addresses() - Return a list of all configured * interfaces IP Addresses (ipv4+ipv6) */ function get_configured_ip_addresses() { $ip_array = []; foreach (legacy_interfaces_details() as $ifname => $if) { foreach (['ipv4', 'ipv6'] as $iptype) { if (!empty($if[$iptype])) { foreach ($if[$iptype] as $addr) { if (!empty($addr['ipaddr'])) { $scope = !empty($addr['link-local']) ? "%{$ifname}" : ''; $ip_array[$addr['ipaddr'] . $scope] = $ifname; } } } } } return $ip_array; } /****f* util/log_error * NAME * log_error - Sends a string to syslog with LOG_ERR severity. * INPUTS * $error - string containing the syslog message. * RESULT * null ******/ function log_error($error) { log_msg($error, LOG_ERR); } /****f* util/log_msg * NAME * log_msg - Sends a string to syslog with the required severity level. * INPUTS * $msg - string containing the syslog message. * $prio - syslog severity level. * RESULT * null ******/ function log_msg($msg, $prio = LOG_NOTICE) { $page = $_SERVER['SCRIPT_NAME']; if (empty($page)) { $files = get_included_files(); $page = basename($files[0]); } switch ($prio) { case LOG_DEBUG: case LOG_INFO: /* lowest handled value for the time being */ $prio = LOG_NOTICE; break; default: break; } syslog($prio, "$page: $msg"); } function url_safe($format, $args = []) { if (!is_array($args)) { /* just in case there's only one argument */ $args = [$args]; } foreach ($args as $id => $arg) { /* arguments could be empty, so force default */ $args[$id] = !empty($arg) ? urlencode($arg) : ''; } return vsprintf($format, $args); } function mwexec($command, $mute = false) { $oarr = []; $retval = 0; $garbage = exec("{$command} 2>&1", $oarr, $retval); unset($garbage); if ($retval != 0 && $mute !== true) { $output = implode(' ', $oarr); log_msg(sprintf("The command '%s'%s returned exit code '%d', the output was '%s'", $command, !empty($mute) ? "($mute) " : '', $retval, $output), LOG_ERR); unset($output); } unset($oarr); return $retval; } function mwexec_bg($command, $mute = false) { mwexec("/usr/sbin/daemon -f {$command}", $mute); } function exec_safe($format, $args = []) { if (!is_array($args)) { /* just in case there's only one argument */ $args = [$args]; } foreach ($args as $id => $arg) { $args[$id] = escapeshellarg($arg ?? ''); } return vsprintf($format, $args); } function mwexecf($format, $args = [], $mute = false) { return mwexec(exec_safe($format, $args), $mute); } function mwexecf_bg($format, $args = [], $mute = false) { mwexec_bg(exec_safe($format, $args), $mute); } function shell_safe($format, $args = []) { $ret = shell_exec(exec_safe($format, $args)); /* shell_exec() has weird semantics */ if ($ret === false || $ret === null) { $ret = ''; } /* always trim() result on a string */ return trim($ret); } /* check if an alias exists */ function is_alias($name) { return \OPNsense\Firewall\Util::isAlias($name); } function subnet_size($subnet) { if (is_subnetv4($subnet)) { list ($ip, $bits) = explode("/", $subnet); return round(exp(log(2) * (32 - $bits))); } elseif (is_subnetv6($subnet)) { list ($ip, $bits) = explode("/", $subnet); return round(exp(log(2) * (128 - $bits))); } else { return 0; } } /* 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); } /* compare two IP addresses */ function ipcmp($a, $b) { $na = inet_pton($a); $nb = inet_pton($b); if ($na < $nb) { return -1; } elseif ($na > $nb) { return 1; } else { return 0; } } /* return true if $addr is in $subnet, false if not */ function ip_in_subnet($addr, $subnet) { if (empty($subnet)) { /* discard invalid input */ } elseif (is_ipaddrv6($addr)) { return (Net_IPv6::isInNetmask($addr, $subnet)); } elseif (is_ipaddrv4($addr)) { list($ip, $mask) = explode('/', $subnet); if (is_ipaddrv4($ip) && $mask <= 32) { $mask = (0xffffffff << (32 - $mask)) & 0xffffffff; return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask)); } } return false; } function is_private_ip($iptocheck) { foreach (['10.0.0.0/8', '100.64.0.0/10', '172.16.0.0/12', '192.168.0.0/16'] as $private) { if (ip_in_subnet($iptocheck, $private) == true) { return true; } } return false; } function format_bytes($bytes) { if ($bytes >= 1024 ** 5) { return sprintf("%.2f PB", $bytes / (1024 ** 5)); } elseif ($bytes >= 1024 ** 4) { return sprintf("%.2f TB", $bytes / (1024 ** 4)); } elseif ($bytes >= 1024 ** 3) { return sprintf("%.2f GB", $bytes / (1024 ** 3)); } elseif ($bytes >= 1024 ** 2) { return sprintf("%.2f MB", $bytes / (1024 ** 2)); } elseif ($bytes >= 1024) { return sprintf("%.0f KB", $bytes / 1024); } else { return sprintf("%d bytes", $bytes); } } /* * 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 []; } if (is_array($names)) { $name_list = []; foreach ($names as $name) { $name_list[] = escapeshellarg($name); } } else { $name_list = [escapeshellarg($names)]; } exec("/sbin/sysctl -i " . implode(" ", $name_list), $output); $values = []; 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 null if it does not exist */ function get_single_sysctl($name) { $ret = null; if (!empty($name)) { $value = get_sysctl($name); if (isset($value[$name])) { $ret = $value[$name]; } } return $ret; } /* * 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, $filter = true) { $ret = []; if (empty($values)) { return $ret; } $sysctls = null; if ($filter) { exec('/sbin/sysctl -WaN', $list, $success); if ($success == 0) { $sysctls = $list; } } $value_list = []; foreach ($values as $key => $value) { if ($sysctls !== null && !in_array($key, $sysctls)) { continue; } $value_list[] = escapeshellarg($key) . '=' . escapeshellarg($value); } if (count($value_list)) { exec('/sbin/sysctl ' . implode(' ', $value_list), $output); 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 */ function set_single_sysctl($name, $value) { set_sysctl([$name => $value], false); } /****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 get_staticroutes($returnsubnetsonly = false) { global $aliastable; $allstaticroutes = []; $allsubnets = []; foreach (config_read_array('staticroutes', 'route') as $route) { if (is_subnet($route['network'])) { $allstaticroutes[] = $route; $allsubnets[] = $route['network']; } } if ($returnsubnetsonly) { return $allsubnets; } return $allstaticroutes; } 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 is_install_media() { /* * Despite unionfs underneath, / is still not writeable, * making the following the perfect test for install media. */ $file = '/.probe.for.readonly'; if (file_exists($file)) { return false; } $fd = @fopen($file, 'w'); if ($fd) { fclose($fd); return false; } return true; } function dhcp6c_duid_read() { $parts = []; $skip = 2; if (file_exists('/var/db/dhcp6c_duid')) { $size = filesize('/var/db/dhcp6c_duid'); if ($size > $skip && ($fd = fopen('/var/db/dhcp6c_duid', 'r'))) { $ret = unpack('Slen/H*buf', fread($fd, $size)); fclose($fd); if (isset($ret['len']) && isset($ret['buf'])) { if ($ret['len'] + $skip == $size && strlen($ret['buf']) == $ret['len'] * 2) { $parts = str_split($ret['buf'], 2); } } } } $duid = strtoupper(implode(':', $parts)); return $duid; } function dhcp6c_duid_write($duid) { $fd = fopen('/var/db/dhcp6c_duid', 'wb'); if ($fd) { $parts = explode(':', $duid); /* length is unsigned 16 bit integer, machine-dependent:*/ fwrite($fd, pack('S', count($parts))); /* buffer is binary string, according to advertised length: */ fwrite($fd, pack('H*', implode('', $parts))); fclose($fd); } } function dhcp6c_duid_clear() { @unlink('/var/db/dhcp6c_duid'); /* clear the backup so that it will not be restored: */ @unlink('/conf/dhcp6c_duid'); } /** * check if interface is assigned * @param $interface technical interface name * @return string interface name (lan, wan, optX) */ function is_interface_assigned($interface) { global $config; foreach (legacy_config_get_interfaces() as $if => $intf) { if (isset($intf['if']) && $intf['if'] == $interface) { return true; } } if (isset($config['vlans']['vlan'])) { foreach ($config['vlans']['vlan'] as $vlan) { if ($vlan['if'] == $interface) { return true; } } } return false; }