Warning: file_get_contents(https://raw.githubusercontent.com/Den1xxx/Filemanager/master/languages/ru.json): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 88

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 215

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 216

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 217

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 218

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 219

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 220
PK!( index.htmlnu[ 403 Forbidden

Directory access is forbidden.

PK!bL_** Loader.phpnu[ TRUE); /** * List of paths to load libraries from * * @var array */ protected $_ci_library_paths = array(APPPATH, BASEPATH); /** * List of paths to load models from * * @var array */ protected $_ci_model_paths = array(APPPATH); /** * List of paths to load helpers from * * @var array */ protected $_ci_helper_paths = array(APPPATH, BASEPATH); /** * List of cached variables * * @var array */ protected $_ci_cached_vars = array(); /** * List of loaded classes * * @var array */ protected $_ci_classes = array(); /** * List of loaded models * * @var array */ protected $_ci_models = array(); /** * List of loaded helpers * * @var array */ protected $_ci_helpers = array(); /** * List of class name mappings * * @var array */ protected $_ci_varmap = array( 'unit_test' => 'unit', 'user_agent' => 'agent' ); // -------------------------------------------------------------------- /** * Class constructor * * Sets component load paths, gets the initial output buffering level. * * @return void */ public function __construct() { $this->_ci_ob_level = ob_get_level(); $this->_ci_classes =& is_loaded(); log_message('info', 'Loader Class Initialized'); } // -------------------------------------------------------------------- /** * Initializer * * @todo Figure out a way to move this to the constructor * without breaking *package_path*() methods. * @uses CI_Loader::_ci_autoloader() * @used-by CI_Controller::__construct() * @return void */ public function initialize() { $this->_ci_autoloader(); } // -------------------------------------------------------------------- /** * Is Loaded * * A utility method to test if a class is in the self::$_ci_classes array. * * @used-by Mainly used by Form Helper function _get_validation_object(). * * @param string $class Class name to check for * @return string|bool Class object name if loaded or FALSE */ public function is_loaded($class) { return array_search(ucfirst($class), $this->_ci_classes, TRUE); } // -------------------------------------------------------------------- /** * Library Loader * * Loads and instantiates libraries. * Designed to be called from application controllers. * * @param string $library Library name * @param array $params Optional parameters to pass to the library class constructor * @param string $object_name An optional object name to assign to * @return object */ public function library($library, $params = NULL, $object_name = NULL) { if (empty($library)) { return $this; } elseif (is_array($library)) { foreach ($library as $key => $value) { if (is_int($key)) { $this->library($value, $params); } else { $this->library($key, $params, $value); } } return $this; } if ($params !== NULL && ! is_array($params)) { $params = NULL; } $this->_ci_load_library($library, $params, $object_name); return $this; } // -------------------------------------------------------------------- /** * Model Loader * * Loads and instantiates models. * * @param string $model Model name * @param string $name An optional object name to assign to * @param bool $db_conn An optional database connection configuration to initialize * @return object */ public function model($model, $name = '', $db_conn = FALSE) { if (empty($model)) { return $this; } elseif (is_array($model)) { foreach ($model as $key => $value) { is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn); } return $this; } $path = ''; // Is the model in a sub-folder? If so, parse out the filename and path. if (($last_slash = strrpos($model, '/')) !== FALSE) { // The path is in front of the last slash $path = substr($model, 0, ++$last_slash); // And the model name behind it $model = substr($model, $last_slash); } if (empty($name)) { $name = $model; } if (in_array($name, $this->_ci_models, TRUE)) { return $this; } $CI =& get_instance(); if (isset($CI->$name)) { throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name); } if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE)) { if ($db_conn === TRUE) { $db_conn = ''; } $this->database($db_conn, FALSE, TRUE); } // Note: All of the code under this condition used to be just: // // load_class('Model', 'core'); // // However, load_class() instantiates classes // to cache them for later use and that prevents // MY_Model from being an abstract class and is // sub-optimal otherwise anyway. if ( ! class_exists('CI_Model', FALSE)) { $app_path = APPPATH.'core'.DIRECTORY_SEPARATOR; if (file_exists($app_path.'Model.php')) { require_once($app_path.'Model.php'); if ( ! class_exists('CI_Model', FALSE)) { throw new RuntimeException($app_path."Model.php exists, but doesn't declare class CI_Model"); } } elseif ( ! class_exists('CI_Model', FALSE)) { require_once(BASEPATH.'core'.DIRECTORY_SEPARATOR.'Model.php'); } $class = config_item('subclass_prefix').'Model'; if (file_exists($app_path.$class.'.php')) { require_once($app_path.$class.'.php'); if ( ! class_exists($class, FALSE)) { throw new RuntimeException($app_path.$class.".php exists, but doesn't declare class ".$class); } } } $model = ucfirst($model); if ( ! class_exists($model, FALSE)) { foreach ($this->_ci_model_paths as $mod_path) { if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) { continue; } require_once($mod_path.'models/'.$path.$model.'.php'); if ( ! class_exists($model, FALSE)) { throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model); } break; } if ( ! class_exists($model, FALSE)) { throw new RuntimeException('Unable to locate the model you have specified: '.$model); } } elseif ( ! is_subclass_of($model, 'CI_Model')) { throw new RuntimeException("Class ".$model." already exists and doesn't extend CI_Model"); } $this->_ci_models[] = $name; $CI->$name = new $model(); return $this; } // -------------------------------------------------------------------- /** * Database Loader * * @param mixed $params Database configuration options * @param bool $return Whether to return the database object * @param bool $query_builder Whether to enable Query Builder * (overrides the configuration setting) * * @return object|bool Database object if $return is set to TRUE, * FALSE on failure, CI_Loader instance in any other case */ public function database($params = '', $return = FALSE, $query_builder = NULL) { // Grab the super object $CI =& get_instance(); // Do we even need to load the database class? if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id)) { return FALSE; } require_once(BASEPATH.'database/DB.php'); if ($return === TRUE) { return DB($params, $query_builder); } // Initialize the db variable. Needed to prevent // reference errors with some configurations $CI->db = ''; // Load the DB class $CI->db =& DB($params, $query_builder); return $this; } // -------------------------------------------------------------------- /** * Load the Database Utilities Class * * @param object $db Database object * @param bool $return Whether to return the DB Utilities class object or not * @return object */ public function dbutil($db = NULL, $return = FALSE) { $CI =& get_instance(); if ( ! is_object($db) OR ! ($db instanceof CI_DB)) { class_exists('CI_DB', FALSE) OR $this->database(); $db =& $CI->db; } require_once(BASEPATH.'database/DB_utility.php'); require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php'); $class = 'CI_DB_'.$db->dbdriver.'_utility'; if ($return === TRUE) { return new $class($db); } $CI->dbutil = new $class($db); return $this; } // -------------------------------------------------------------------- /** * Load the Database Forge Class * * @param object $db Database object * @param bool $return Whether to return the DB Forge class object or not * @return object */ public function dbforge($db = NULL, $return = FALSE) { $CI =& get_instance(); if ( ! is_object($db) OR ! ($db instanceof CI_DB)) { class_exists('CI_DB', FALSE) OR $this->database(); $db =& $CI->db; } require_once(BASEPATH.'database/DB_forge.php'); require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php'); if ( ! empty($db->subdriver)) { $driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php'; if (file_exists($driver_path)) { require_once($driver_path); $class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge'; } } else { $class = 'CI_DB_'.$db->dbdriver.'_forge'; } if ($return === TRUE) { return new $class($db); } $CI->dbforge = new $class($db); return $this; } // -------------------------------------------------------------------- /** * View Loader * * Loads "view" files. * * @param string $view View name * @param array $vars An associative array of data * to be extracted for use in the view * @param bool $return Whether to return the view output * or leave it to the Output class * @return object|string */ public function view($view, $vars = array(), $return = FALSE) { return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return)); } // -------------------------------------------------------------------- /** * Generic File Loader * * @param string $path File path * @param bool $return Whether to return the file output * @return object|string */ public function file($path, $return = FALSE) { return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return)); } // -------------------------------------------------------------------- /** * Set Variables * * Once variables are set they become available within * the controller class and its "view" files. * * @param array|object|string $vars * An associative array or object containing values * to be set, or a value's name if string * @param string $val Value to set, only used if $vars is a string * @return object */ public function vars($vars, $val = '') { if (is_string($vars)) { $vars = array($vars => $val); } $vars = $this->_ci_object_to_array($vars); if (is_array($vars) && count($vars) > 0) { foreach ($vars as $key => $val) { $this->_ci_cached_vars[$key] = $val; } } return $this; } // -------------------------------------------------------------------- /** * Clear Cached Variables * * Clears the cached variables. * * @return CI_Loader */ public function clear_vars() { $this->_ci_cached_vars = array(); return $this; } // -------------------------------------------------------------------- /** * Get Variable * * Check if a variable is set and retrieve it. * * @param string $key Variable name * @return mixed The variable or NULL if not found */ public function get_var($key) { return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL; } // -------------------------------------------------------------------- /** * Get Variables * * Retrieves all loaded variables. * * @return array */ public function get_vars() { return $this->_ci_cached_vars; } // -------------------------------------------------------------------- /** * Helper Loader * * @param string|string[] $helpers Helper name(s) * @return object */ public function helper($helpers = array()) { foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper) { if (isset($this->_ci_helpers[$helper])) { continue; } // Is this a helper extension request? $ext_helper = config_item('subclass_prefix').$helper; $ext_loaded = FALSE; foreach ($this->_ci_helper_paths as $path) { if (file_exists($path.'helpers/'.$ext_helper.'.php')) { include_once($path.'helpers/'.$ext_helper.'.php'); $ext_loaded = TRUE; } } // If we have loaded extensions - check if the base one is here if ($ext_loaded === TRUE) { $base_helper = BASEPATH.'helpers/'.$helper.'.php'; if ( ! file_exists($base_helper)) { show_error('Unable to load the requested file: helpers/'.$helper.'.php'); } include_once($base_helper); $this->_ci_helpers[$helper] = TRUE; log_message('info', 'Helper loaded: '.$helper); continue; } // No extensions found ... try loading regular helpers and/or overrides foreach ($this->_ci_helper_paths as $path) { if (file_exists($path.'helpers/'.$helper.'.php')) { include_once($path.'helpers/'.$helper.'.php'); $this->_ci_helpers[$helper] = TRUE; log_message('info', 'Helper loaded: '.$helper); break; } } // unable to load the helper if ( ! isset($this->_ci_helpers[$helper])) { show_error('Unable to load the requested file: helpers/'.$helper.'.php'); } } return $this; } // -------------------------------------------------------------------- /** * Load Helpers * * An alias for the helper() method in case the developer has * written the plural form of it. * * @uses CI_Loader::helper() * @param string|string[] $helpers Helper name(s) * @return object */ public function helpers($helpers = array()) { return $this->helper($helpers); } // -------------------------------------------------------------------- /** * Language Loader * * Loads language files. * * @param string|string[] $files List of language file names to load * @param string Language name * @return object */ public function language($files, $lang = '') { get_instance()->lang->load($files, $lang); return $this; } // -------------------------------------------------------------------- /** * Config Loader * * Loads a config file (an alias for CI_Config::load()). * * @uses CI_Config::load() * @param string $file Configuration file name * @param bool $use_sections Whether configuration values should be loaded into their own section * @param bool $fail_gracefully Whether to just return FALSE or display an error message * @return bool TRUE if the file was loaded correctly or FALSE on failure */ public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE) { return get_instance()->config->load($file, $use_sections, $fail_gracefully); } // -------------------------------------------------------------------- /** * Driver Loader * * Loads a driver library. * * @param string|string[] $library Driver name(s) * @param array $params Optional parameters to pass to the driver * @param string $object_name An optional object name to assign to * * @return object|bool Object or FALSE on failure if $library is a string * and $object_name is set. CI_Loader instance otherwise. */ public function driver($library, $params = NULL, $object_name = NULL) { if (is_array($library)) { foreach ($library as $key => $value) { if (is_int($key)) { $this->driver($value, $params); } else { $this->driver($key, $params, $value); } } return $this; } elseif (empty($library)) { return FALSE; } if ( ! class_exists('CI_Driver_Library', FALSE)) { // We aren't instantiating an object here, just making the base class available require BASEPATH.'libraries/Driver.php'; } // We can save the loader some time since Drivers will *always* be in a subfolder, // and typically identically named to the library if ( ! strpos($library, '/')) { $library = ucfirst($library).'/'.$library; } return $this->library($library, $params, $object_name); } // -------------------------------------------------------------------- /** * Add Package Path * * Prepends a parent path to the library, model, helper and config * path arrays. * * @see CI_Loader::$_ci_library_paths * @see CI_Loader::$_ci_model_paths * @see CI_Loader::$_ci_helper_paths * @see CI_Config::$_config_paths * * @param string $path Path to add * @param bool $view_cascade (default: TRUE) * @return object */ public function add_package_path($path, $view_cascade = TRUE) { $path = rtrim($path, '/').'/'; array_unshift($this->_ci_library_paths, $path); array_unshift($this->_ci_model_paths, $path); array_unshift($this->_ci_helper_paths, $path); $this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths; // Add config file path $config =& $this->_ci_get_component('config'); $config->_config_paths[] = $path; return $this; } // -------------------------------------------------------------------- /** * Get Package Paths * * Return a list of all package paths. * * @param bool $include_base Whether to include BASEPATH (default: FALSE) * @return array */ public function get_package_paths($include_base = FALSE) { return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths; } // -------------------------------------------------------------------- /** * Remove Package Path * * Remove a path from the library, model, helper and/or config * path arrays if it exists. If no path is provided, the most recently * added path will be removed removed. * * @param string $path Path to remove * @return object */ public function remove_package_path($path = '') { $config =& $this->_ci_get_component('config'); if ($path === '') { array_shift($this->_ci_library_paths); array_shift($this->_ci_model_paths); array_shift($this->_ci_helper_paths); array_shift($this->_ci_view_paths); array_pop($config->_config_paths); } else { $path = rtrim($path, '/').'/'; foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var) { if (($key = array_search($path, $this->{$var})) !== FALSE) { unset($this->{$var}[$key]); } } if (isset($this->_ci_view_paths[$path.'views/'])) { unset($this->_ci_view_paths[$path.'views/']); } if (($key = array_search($path, $config->_config_paths)) !== FALSE) { unset($config->_config_paths[$key]); } } // make sure the application default paths are still in the array $this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH))); $this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH))); $this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH))); $this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE)); $config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH))); return $this; } // -------------------------------------------------------------------- /** * Internal CI Data Loader * * Used to load views and files. * * Variables are prefixed with _ci_ to avoid symbol collision with * variables made available to view files. * * @used-by CI_Loader::view() * @used-by CI_Loader::file() * @param array $_ci_data Data to load * @return object */ protected function _ci_load($_ci_data) { // Set the default data variables foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val) { $$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE; } $file_exists = FALSE; // Set the path to the requested file if (is_string($_ci_path) && $_ci_path !== '') { $_ci_x = explode('/', $_ci_path); $_ci_file = end($_ci_x); } else { $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION); $_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view; foreach ($this->_ci_view_paths as $_ci_view_file => $cascade) { if (file_exists($_ci_view_file.$_ci_file)) { $_ci_path = $_ci_view_file.$_ci_file; $file_exists = TRUE; break; } if ( ! $cascade) { break; } } } if ( ! $file_exists && ! file_exists($_ci_path)) { show_error('Unable to load the requested file: '.$_ci_file); } // This allows anything loaded using $this->load (views, files, etc.) // to become accessible from within the Controller and Model functions. $_ci_CI =& get_instance(); foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var) { if ( ! isset($this->$_ci_key)) { $this->$_ci_key =& $_ci_CI->$_ci_key; } } /* * Extract and cache variables * * You can either set variables using the dedicated $this->load->vars() * function or via the second parameter of this function. We'll merge * the two types and cache them so that views that are embedded within * other views can have access to these variables. */ if (is_array($_ci_vars)) { foreach (array_keys($_ci_vars) as $key) { if (strncmp($key, '_ci_', 4) === 0) { unset($_ci_vars[$key]); } } $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars); } extract($this->_ci_cached_vars); /* * Buffer the output * * We buffer the output for two reasons: * 1. Speed. You get a significant speed boost. * 2. So that the final rendered template can be post-processed by * the output class. Why do we need post processing? For one thing, * in order to show the elapsed page load time. Unless we can * intercept the content right before it's sent to the browser and * then stop the timer it won't be accurate. */ ob_start(); // If the PHP installation does not support short tags we'll // do a little string replacement, changing the short tags // to standard PHP echo statements. if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE) { echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace(' $this->_ci_ob_level + 1) { ob_end_flush(); } else { $_ci_CI->output->append_output(ob_get_contents()); @ob_end_clean(); } return $this; } // -------------------------------------------------------------------- /** * Internal CI Library Loader * * @used-by CI_Loader::library() * @uses CI_Loader::_ci_init_library() * * @param string $class Class name to load * @param mixed $params Optional parameters to pass to the class constructor * @param string $object_name Optional object name to assign to * @return void */ protected function _ci_load_library($class, $params = NULL, $object_name = NULL) { // Get the class name, and while we're at it trim any slashes. // The directory path can be included as part of the class name, // but we don't want a leading slash $class = str_replace('.php', '', trim($class, '/')); // Was the path included with the class name? // We look for a slash to determine this if (($last_slash = strrpos($class, '/')) !== FALSE) { // Extract the path $subdir = substr($class, 0, ++$last_slash); // Get the filename from the path $class = substr($class, $last_slash); } else { $subdir = ''; } $class = ucfirst($class); // Is this a stock library? There are a few special conditions if so ... if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php')) { return $this->_ci_load_stock_library($class, $subdir, $params, $object_name); } // Let's search for the requested library file and load it. foreach ($this->_ci_library_paths as $path) { // BASEPATH has already been checked for if ($path === BASEPATH) { continue; } $filepath = $path.'libraries/'.$subdir.$class.'.php'; // Safety: Was the class already loaded by a previous call? if (class_exists($class, FALSE)) { // Before we deem this to be a duplicate request, let's see // if a custom object name is being supplied. If so, we'll // return a new instance of the object if ($object_name !== NULL) { $CI =& get_instance(); if ( ! isset($CI->$object_name)) { return $this->_ci_init_library($class, '', $params, $object_name); } } log_message('debug', $class.' class already loaded. Second attempt ignored.'); return; } // Does the file exist? No? Bummer... elseif ( ! file_exists($filepath)) { continue; } include_once($filepath); return $this->_ci_init_library($class, '', $params, $object_name); } // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified? if ($subdir === '') { return $this->_ci_load_library($class.'/'.$class, $params, $object_name); } // If we got this far we were unable to find the requested class. log_message('error', 'Unable to load the requested class: '.$class); show_error('Unable to load the requested class: '.$class); } // -------------------------------------------------------------------- /** * Internal CI Stock Library Loader * * @used-by CI_Loader::_ci_load_library() * @uses CI_Loader::_ci_init_library() * * @param string $library_name Library name to load * @param string $file_path Path to the library filename, relative to libraries/ * @param mixed $params Optional parameters to pass to the class constructor * @param string $object_name Optional object name to assign to * @return void */ protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name) { $prefix = 'CI_'; if (class_exists($prefix.$library_name, FALSE)) { if (class_exists(config_item('subclass_prefix').$library_name, FALSE)) { $prefix = config_item('subclass_prefix'); } // Before we deem this to be a duplicate request, let's see // if a custom object name is being supplied. If so, we'll // return a new instance of the object if ($object_name !== NULL) { $CI =& get_instance(); if ( ! isset($CI->$object_name)) { return $this->_ci_init_library($library_name, $prefix, $params, $object_name); } } log_message('debug', $library_name.' class already loaded. Second attempt ignored.'); return; } $paths = $this->_ci_library_paths; array_pop($paths); // BASEPATH array_pop($paths); // APPPATH (needs to be the first path checked) array_unshift($paths, APPPATH); foreach ($paths as $path) { if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php')) { // Override include_once($path); if (class_exists($prefix.$library_name, FALSE)) { return $this->_ci_init_library($library_name, $prefix, $params, $object_name); } else { log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name); } } } include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php'); // Check for extensions $subclass = config_item('subclass_prefix').$library_name; foreach ($paths as $path) { if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php')) { include_once($path); if (class_exists($subclass, FALSE)) { $prefix = config_item('subclass_prefix'); break; } else { log_message('debug', $path.' exists, but does not declare '.$subclass); } } } return $this->_ci_init_library($library_name, $prefix, $params, $object_name); } // -------------------------------------------------------------------- /** * Internal CI Library Instantiator * * @used-by CI_Loader::_ci_load_stock_library() * @used-by CI_Loader::_ci_load_library() * * @param string $class Class name * @param string $prefix Class name prefix * @param array|null|bool $config Optional configuration to pass to the class constructor: * FALSE to skip; * NULL to search in config paths; * array containing configuration data * @param string $object_name Optional object name to assign to * @return void */ protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL) { // Is there an associated config file for this class? Note: these should always be lowercase if ($config === NULL) { // Fetch the config paths containing any package paths $config_component = $this->_ci_get_component('config'); if (is_array($config_component->_config_paths)) { $found = FALSE; foreach ($config_component->_config_paths as $path) { // We test for both uppercase and lowercase, for servers that // are case-sensitive with regard to file names. Load global first, // override with environment next if (file_exists($path.'config/'.strtolower($class).'.php')) { include($path.'config/'.strtolower($class).'.php'); $found = TRUE; } elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php')) { include($path.'config/'.ucfirst(strtolower($class)).'.php'); $found = TRUE; } if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php')) { include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'); $found = TRUE; } elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php')) { include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'); $found = TRUE; } // Break on the first found configuration, thus package // files are not overridden by default paths if ($found === TRUE) { break; } } } } $class_name = $prefix.$class; // Is the class name valid? if ( ! class_exists($class_name, FALSE)) { log_message('error', 'Non-existent class: '.$class_name); show_error('Non-existent class: '.$class_name); } // Set the variable name we will assign the class to // Was a custom class name supplied? If so we'll use it if (empty($object_name)) { $object_name = strtolower($class); if (isset($this->_ci_varmap[$object_name])) { $object_name = $this->_ci_varmap[$object_name]; } } // Don't overwrite existing properties $CI =& get_instance(); if (isset($CI->$object_name)) { if ($CI->$object_name instanceof $class_name) { log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted."); return; } show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance."); } // Save the class name and object name $this->_ci_classes[$object_name] = $class; // Instantiate the class $CI->$object_name = isset($config) ? new $class_name($config) : new $class_name(); } // -------------------------------------------------------------------- /** * CI Autoloader * * Loads component listed in the config/autoload.php file. * * @used-by CI_Loader::initialize() * @return void */ protected function _ci_autoloader() { if (file_exists(APPPATH.'config/autoload.php')) { include(APPPATH.'config/autoload.php'); } if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php')) { include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'); } if ( ! isset($autoload)) { return; } // Autoload packages if (isset($autoload['packages'])) { foreach ($autoload['packages'] as $package_path) { $this->add_package_path($package_path); } } // Load any custom config file if (count($autoload['config']) > 0) { foreach ($autoload['config'] as $val) { $this->config($val); } } // Autoload helpers and languages foreach (array('helper', 'language') as $type) { if (isset($autoload[$type]) && count($autoload[$type]) > 0) { $this->$type($autoload[$type]); } } // Autoload drivers if (isset($autoload['drivers'])) { $this->driver($autoload['drivers']); } // Load libraries if (isset($autoload['libraries']) && count($autoload['libraries']) > 0) { // Load the database driver. if (in_array('database', $autoload['libraries'])) { $this->database(); $autoload['libraries'] = array_diff($autoload['libraries'], array('database')); } // Load all other libraries $this->library($autoload['libraries']); } // Autoload models if (isset($autoload['model'])) { $this->model($autoload['model']); } } // -------------------------------------------------------------------- /** * CI Object to Array translator * * Takes an object as input and converts the class variables to * an associative array with key/value pairs. * * @param object $object Object data to translate * @return array */ protected function _ci_object_to_array($object) { return is_object($object) ? get_object_vars($object) : $object; } // -------------------------------------------------------------------- /** * CI Component getter * * Get a reference to a specific library or model. * * @param string $component Component name * @return bool */ protected function &_ci_get_component($component) { $CI =& get_instance(); return $CI->$component; } // -------------------------------------------------------------------- /** * Prep filename * * This function prepares filenames of various items to * make their loading more reliable. * * @param string|string[] $filename Filename(s) * @param string $extension Filename extension * @return array */ protected function _ci_prep_filename($filename, $extension) { if ( ! is_array($filename)) { return array(strtolower(str_replace(array($extension, '.php'), '', $filename).$extension)); } else { foreach ($filename as $key => $val) { $filename[$key] = strtolower(str_replace(array($extension, '.php'), '', $val).$extension); } return $filename; } } } PK!S S Controller.phpnu[ $class) { $this->$var =& load_class($class); } $this->load =& load_class('Loader', 'core'); $this->load->initialize(); log_message('info', 'Controller Class Initialized'); } // -------------------------------------------------------------------- /** * Get the CI singleton * * @static * @return object */ public static function &get_instance() { return self::$instance; } } PK!s~~Log.phpnu[ 1, 'DEBUG' => 2, 'INFO' => 3, 'ALL' => 4); /** * mbstring.func_override flag * * @var bool */ protected static $func_override; // -------------------------------------------------------------------- /** * Class constructor * * @return void */ public function __construct() { $config =& get_config(); isset(self::$func_override) OR self::$func_override = (extension_loaded('mbstring') && ini_get('mbstring.func_override')); $this->_log_path = ($config['log_path'] !== '') ? $config['log_path'] : APPPATH.'logs/'; $this->_file_ext = (isset($config['log_file_extension']) && $config['log_file_extension'] !== '') ? ltrim($config['log_file_extension'], '.') : 'php'; file_exists($this->_log_path) OR mkdir($this->_log_path, 0755, TRUE); if ( ! is_dir($this->_log_path) OR ! is_really_writable($this->_log_path)) { $this->_enabled = FALSE; } if (is_numeric($config['log_threshold'])) { $this->_threshold = (int) $config['log_threshold']; } elseif (is_array($config['log_threshold'])) { $this->_threshold = 0; $this->_threshold_array = array_flip($config['log_threshold']); } if ( ! empty($config['log_date_format'])) { $this->_date_fmt = $config['log_date_format']; } if ( ! empty($config['log_file_permissions']) && is_int($config['log_file_permissions'])) { $this->_file_permissions = $config['log_file_permissions']; } } // -------------------------------------------------------------------- /** * Write Log File * * Generally this function will be called using the global log_message() function * * @param string $level The error level: 'error', 'debug' or 'info' * @param string $msg The error message * @return bool */ public function write_log($level, $msg) { if ($this->_enabled === FALSE) { return FALSE; } $level = strtoupper($level); if (( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold)) && ! isset($this->_threshold_array[$this->_levels[$level]])) { return FALSE; } $filepath = $this->_log_path.'log-'.date('Y-m-d').'.'.$this->_file_ext; $message = ''; if ( ! file_exists($filepath)) { $newfile = TRUE; // Only add protection to php files if ($this->_file_ext === 'php') { $message .= "\n\n"; } } if ( ! $fp = @fopen($filepath, 'ab')) { return FALSE; } flock($fp, LOCK_EX); // Instantiating DateTime with microseconds appended to initial date is needed for proper support of this format if (strpos($this->_date_fmt, 'u') !== FALSE) { $microtime_full = microtime(TRUE); $microtime_short = sprintf("%06d", ($microtime_full - floor($microtime_full)) * 1000000); $date = new DateTime(date('Y-m-d H:i:s.'.$microtime_short, $microtime_full)); $date = $date->format($this->_date_fmt); } else { $date = date($this->_date_fmt); } $message .= $this->_format_line($level, $date, $msg); for ($written = 0, $length = self::strlen($message); $written < $length; $written += $result) { if (($result = fwrite($fp, self::substr($message, $written))) === FALSE) { break; } } flock($fp, LOCK_UN); fclose($fp); if (isset($newfile) && $newfile === TRUE) { chmod($filepath, $this->_file_permissions); } return is_int($result); } // -------------------------------------------------------------------- /** * Format the log line. * * This is for extensibility of log formatting * If you want to change the log format, extend the CI_Log class and override this method * * @param string $level The error level * @param string $date Formatted date string * @param string $message The log message * @return string Formatted log line with a new line character '\n' at the end */ protected function _format_line($level, $date, $message) { return $level.' - '.$date.' --> '.$message."\n"; } // -------------------------------------------------------------------- /** * Byte-safe strlen() * * @param string $str * @return int */ protected static function strlen($str) { return (self::$func_override) ? mb_strlen($str, '8bit') : strlen($str); } // -------------------------------------------------------------------- /** * Byte-safe substr() * * @param string $str * @param int $start * @param int $length * @return string */ protected static function substr($str, $start, $length = NULL) { if (self::$func_override) { // mb_substr($str, $start, null, '8bit') returns an empty // string on PHP 5.3 isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start); return mb_substr($str, $start, $length, '8bit'); } return isset($length) ? substr($str, $start, $length) : substr($str, $start); } } PK!UX_3_3 Router.phpnu[config =& load_class('Config', 'core'); $this->uri =& load_class('URI', 'core'); $this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE); // If a directory override is configured, it has to be set before any dynamic routing logic is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']); $this->_set_routing(); // Set any routing overrides that may exist in the main index file if (is_array($routing)) { empty($routing['controller']) OR $this->set_class($routing['controller']); empty($routing['function']) OR $this->set_method($routing['function']); } log_message('info', 'Router Class Initialized'); } // -------------------------------------------------------------------- /** * Set route mapping * * Determines what should be served based on the URI request, * as well as any "routes" that have been set in the routing config file. * * @return void */ protected function _set_routing() { // Load the routes.php file. It would be great if we could // skip this for enable_query_strings = TRUE, but then // default_controller would be empty ... if (file_exists(APPPATH.'config/routes.php')) { include(APPPATH.'config/routes.php'); } if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php')) { include(APPPATH.'config/'.ENVIRONMENT.'/routes.php'); } // Validate & get reserved routes if (isset($route) && is_array($route)) { isset($route['default_controller']) && $this->default_controller = $route['default_controller']; isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes']; unset($route['default_controller'], $route['translate_uri_dashes']); $this->routes = $route; } // Are query strings enabled in the config file? Normally CI doesn't utilize query strings // since URI segments are more search-engine friendly, but they can optionally be used. // If this feature is enabled, we will gather the directory/class/method a little differently if ($this->enable_query_strings) { // If the directory is set at this time, it means an override exists, so skip the checks if ( ! isset($this->directory)) { $_d = $this->config->item('directory_trigger'); $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : ''; if ($_d !== '') { $this->uri->filter_uri($_d); $this->set_directory($_d); } } $_c = trim($this->config->item('controller_trigger')); if ( ! empty($_GET[$_c])) { $this->uri->filter_uri($_GET[$_c]); $this->set_class($_GET[$_c]); $_f = trim($this->config->item('function_trigger')); if ( ! empty($_GET[$_f])) { $this->uri->filter_uri($_GET[$_f]); $this->set_method($_GET[$_f]); } $this->uri->rsegments = array( 1 => $this->class, 2 => $this->method ); } else { $this->_set_default_controller(); } // Routing rules don't apply to query strings and we don't need to detect // directories, so we're done here return; } // Is there anything to parse? if ($this->uri->uri_string !== '') { $this->_parse_routes(); } else { $this->_set_default_controller(); } } // -------------------------------------------------------------------- /** * Set request route * * Takes an array of URI segments as input and sets the class/method * to be called. * * @used-by CI_Router::_parse_routes() * @param array $segments URI segments * @return void */ protected function _set_request($segments = array()) { $segments = $this->_validate_request($segments); // If we don't have any segments left - try the default controller; // WARNING: Directories get shifted out of the segments array! if (empty($segments)) { $this->_set_default_controller(); return; } if ($this->translate_uri_dashes === TRUE) { $segments[0] = str_replace('-', '_', $segments[0]); if (isset($segments[1])) { $segments[1] = str_replace('-', '_', $segments[1]); } } $this->set_class($segments[0]); if (isset($segments[1])) { $this->set_method($segments[1]); } else { $segments[1] = 'index'; } array_unshift($segments, NULL); unset($segments[0]); $this->uri->rsegments = $segments; } // -------------------------------------------------------------------- /** * Set default controller * * @return void */ protected function _set_default_controller() { if (empty($this->default_controller)) { show_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.'); } // Is the method being specified? if (sscanf($this->default_controller, '%[^/]/%s', $class, $method) !== 2) { $method = 'index'; } if ( ! file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php')) { // This will trigger 404 later return; } $this->set_class($class); $this->set_method($method); // Assign routed segments, index starting from 1 $this->uri->rsegments = array( 1 => $class, 2 => $method ); log_message('debug', 'No URI present. Default controller set.'); } // -------------------------------------------------------------------- /** * Validate request * * Attempts validate the URI request and determine the controller path. * * @used-by CI_Router::_set_request() * @param array $segments URI segments * @return mixed URI segments */ protected function _validate_request($segments) { $c = count($segments); $directory_override = isset($this->directory); // Loop through our segments and return as soon as a controller // is found or when such a directory doesn't exist while ($c-- > 0) { $test = $this->directory .ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]); if ( ! file_exists(APPPATH.'controllers/'.$test.'.php') && $directory_override === FALSE && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]) ) { $this->set_directory(array_shift($segments), TRUE); continue; } return $segments; } // This means that all segments were actually directories return $segments; } // -------------------------------------------------------------------- /** * Parse Routes * * Matches any routes that may exist in the config/routes.php file * against the URI to determine if the class/method need to be remapped. * * @return void */ protected function _parse_routes() { // Turn the segment array into a URI string $uri = implode('/', $this->uri->segments); // Get HTTP verb $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli'; // Loop through the route array looking for wildcards foreach ($this->routes as $key => $val) { // Check if route format is using HTTP verbs if (is_array($val)) { $val = array_change_key_case($val, CASE_LOWER); if (isset($val[$http_verb])) { $val = $val[$http_verb]; } else { continue; } } // Convert wildcards to RegEx $key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key); // Does the RegEx match? if (preg_match('#^'.$key.'$#', $uri, $matches)) { // Are we using callbacks to process back-references? if ( ! is_string($val) && is_callable($val)) { // Remove the original string from the matches array. array_shift($matches); // Execute the callback using the values in matches as its parameters. $val = call_user_func_array($val, $matches); } // Are we using the default routing method for back-references? elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE) { $val = preg_replace('#^'.$key.'$#', $val, $uri); } $this->_set_request(explode('/', $val)); return; } } // If we got this far it means we didn't encounter a // matching route so we'll set the site default route $this->_set_request(array_values($this->uri->segments)); } // -------------------------------------------------------------------- /** * Set class name * * @param string $class Class name * @return void */ public function set_class($class) { $this->class = str_replace(array('/', '.'), '', $class); } // -------------------------------------------------------------------- /** * Fetch the current class * * @deprecated 3.0.0 Read the 'class' property instead * @return string */ public function fetch_class() { return $this->class; } // -------------------------------------------------------------------- /** * Set method name * * @param string $method Method name * @return void */ public function set_method($method) { $this->method = $method; } // -------------------------------------------------------------------- /** * Fetch the current method * * @deprecated 3.0.0 Read the 'method' property instead * @return string */ public function fetch_method() { return $this->method; } // -------------------------------------------------------------------- /** * Set directory name * * @param string $dir Directory name * @param bool $append Whether we're appending rather than setting the full value * @return void */ public function set_directory($dir, $append = FALSE) { if ($append !== TRUE OR empty($this->directory)) { $this->directory = str_replace('.', '', trim($dir, '/')).'/'; } else { $this->directory .= str_replace('.', '', trim($dir, '/')).'/'; } } // -------------------------------------------------------------------- /** * Fetch directory * * Feches the sub-directory (if any) that contains the requested * controller class. * * @deprecated 3.0.0 Read the 'directory' property instead * @return string */ public function fetch_directory() { return $this->directory; } } PK! oo Security.phpnu[', '<', '>', "'", '"', '&', '$', '#', '{', '}', '[', ']', '=', ';', '?', '%20', '%22', '%3c', // < '%253c', // < '%3e', // > '%0e', // > '%28', // ( '%29', // ) '%2528', // ( '%26', // & '%24', // $ '%3f', // ? '%3b', // ; '%3d' // = ); /** * Character set * * Will be overridden by the constructor. * * @var string */ public $charset = 'UTF-8'; /** * XSS Hash * * Random Hash for protecting URLs. * * @var string */ protected $_xss_hash; /** * CSRF Hash * * Random hash for Cross Site Request Forgery protection cookie * * @var string */ protected $_csrf_hash; /** * CSRF Expire time * * Expiration time for Cross Site Request Forgery protection cookie. * Defaults to two hours (in seconds). * * @var int */ protected $_csrf_expire = 7200; /** * CSRF Token name * * Token name for Cross Site Request Forgery protection cookie. * * @var string */ protected $_csrf_token_name = 'ci_csrf_token'; /** * CSRF Cookie name * * Cookie name for Cross Site Request Forgery protection cookie. * * @var string */ protected $_csrf_cookie_name = 'ci_csrf_token'; /** * List of never allowed strings * * @var array */ protected $_never_allowed_str = array( 'document.cookie' => '[removed]', 'document.write' => '[removed]', '.parentNode' => '[removed]', '.innerHTML' => '[removed]', '-moz-binding' => '[removed]', '' => '-->', ' '<![CDATA[', '' => '<comment>', '<%' => '<%' ); /** * List of never allowed regex replacements * * @var array */ protected $_never_allowed_regex = array( 'javascript\s*:', '(document|(document\.)?window)\.(location|on\w*)', 'expression\s*(\(|&\#40;)', // CSS and IE 'vbscript\s*:', // IE, surprise! 'wscript\s*:', // IE 'jscript\s*:', // IE 'vbs\s*:', // IE 'Redirect\s+30\d', "([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?" ); /** * Class constructor * * @return void */ public function __construct() { // Is CSRF protection enabled? if (config_item('csrf_protection')) { // CSRF config foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key) { if (NULL !== ($val = config_item($key))) { $this->{'_'.$key} = $val; } } // Append application specific cookie prefix if ($cookie_prefix = config_item('cookie_prefix')) { $this->_csrf_cookie_name = $cookie_prefix.$this->_csrf_cookie_name; } // Set the CSRF hash $this->_csrf_set_hash(); } $this->charset = strtoupper(config_item('charset')); log_message('info', 'Security Class Initialized'); } // -------------------------------------------------------------------- /** * CSRF Verify * * @return CI_Security */ public function csrf_verify() { // If it's not a POST request we will set the CSRF cookie if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') { return $this->csrf_set_cookie(); } // Check if URI has been whitelisted from CSRF checks if ($exclude_uris = config_item('csrf_exclude_uris')) { $uri = load_class('URI', 'core'); foreach ($exclude_uris as $excluded) { if (preg_match('#^'.$excluded.'$#i'.(UTF8_ENABLED ? 'u' : ''), $uri->uri_string())) { return $this; } } } // Do the tokens exist in both the _POST and _COOKIE arrays? if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]) OR $_POST[$this->_csrf_token_name] !== $_COOKIE[$this->_csrf_cookie_name]) // Do the tokens match? { $this->csrf_show_error(); } // We kill this since we're done and we don't want to pollute the _POST array unset($_POST[$this->_csrf_token_name]); // Regenerate on every submission? if (config_item('csrf_regenerate')) { // Nothing should last forever unset($_COOKIE[$this->_csrf_cookie_name]); $this->_csrf_hash = NULL; } $this->_csrf_set_hash(); $this->csrf_set_cookie(); log_message('info', 'CSRF token verified'); return $this; } // -------------------------------------------------------------------- /** * CSRF Set Cookie * * @codeCoverageIgnore * @return CI_Security */ public function csrf_set_cookie() { $expire = time() + $this->_csrf_expire; $secure_cookie = (bool) config_item('cookie_secure'); if ($secure_cookie && ! is_https()) { return FALSE; } setcookie( $this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie, config_item('cookie_httponly') ); log_message('info', 'CSRF cookie sent'); return $this; } // -------------------------------------------------------------------- /** * Show CSRF Error * * @return void */ public function csrf_show_error() { show_error('The action you have requested is not allowed.', 403); } // -------------------------------------------------------------------- /** * Get CSRF Hash * * @see CI_Security::$_csrf_hash * @return string CSRF hash */ public function get_csrf_hash() { return $this->_csrf_hash; } // -------------------------------------------------------------------- /** * Get CSRF Token Name * * @see CI_Security::$_csrf_token_name * @return string CSRF token name */ public function get_csrf_token_name() { return $this->_csrf_token_name; } // -------------------------------------------------------------------- /** * XSS Clean * * Sanitizes data so that Cross Site Scripting Hacks can be * prevented. This method does a fair amount of work but * it is extremely thorough, designed to prevent even the * most obscure XSS attempts. Nothing is ever 100% foolproof, * of course, but I haven't been able to get anything passed * the filter. * * Note: Should only be used to deal with data upon submission. * It's not something that should be used for general * runtime processing. * * @link http://channel.bitflux.ch/wiki/XSS_Prevention * Based in part on some code and ideas from Bitflux. * * @link http://ha.ckers.org/xss.html * To help develop this script I used this great list of * vulnerabilities along with a few other hacks I've * harvested from examining vulnerabilities in other programs. * * @param string|string[] $str Input data * @param bool $is_image Whether the input is an image * @return string */ public function xss_clean($str, $is_image = FALSE) { // Is the string an array? if (is_array($str)) { while (list($key) = each($str)) { $str[$key] = $this->xss_clean($str[$key]); } return $str; } // Remove Invisible Characters $str = remove_invisible_characters($str); /* * URL Decode * * Just in case stuff like this is submitted: * * Google * * Note: Use rawurldecode() so it does not remove plus signs */ if (stripos($str, '%') !== false) { do { $oldstr = $str; $str = rawurldecode($str); $str = preg_replace_callback('#%(?:\s*[0-9a-f]){2,}#i', array($this, '_urldecodespaces'), $str); } while ($oldstr !== $str); unset($oldstr); } /* * Convert character entities to ASCII * * This permits our tests below to work reliably. * We only convert entities that are within tags since * these are the ones that will pose security problems. */ $str = preg_replace_callback("/[^a-z0-9>]+[a-z0-9]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str); $str = preg_replace_callback('/<\w+.*/si', array($this, '_decode_entity'), $str); // Remove Invisible Characters Again! $str = remove_invisible_characters($str); /* * Convert all tabs to spaces * * This prevents strings like this: ja vascript * NOTE: we deal with spaces between characters later. * NOTE: preg_replace was found to be amazingly slow here on * large blocks of data, so we use str_replace. */ $str = str_replace("\t", ' ', $str); // Capture converted string for later comparison $converted_string = $str; // Remove Strings that are never allowed $str = $this->_do_never_allowed($str); /* * Makes PHP tags safe * * Note: XML tags are inadvertently replaced too: * * '), array('<?', '?>'), $str); } /* * Compact any exploded words * * This corrects words like: j a v a s c r i p t * These words are compacted back to their correct state. */ $words = array( 'javascript', 'expression', 'vbscript', 'jscript', 'wscript', 'vbs', 'script', 'base64', 'applet', 'alert', 'document', 'write', 'cookie', 'window', 'confirm', 'prompt', 'eval' ); foreach ($words as $word) { $word = implode('\s*', str_split($word)).'\s*'; // We only want to do this when it is followed by a non-word character // That way valid stuff like "dealer to" does not become "dealerto" $str = preg_replace_callback('#('.substr($word, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str); } /* * Remove disallowed Javascript in links or img tags * We used to do some version comparisons and use of stripos(), * but it is dog slow compared to these simplified non-capturing * preg_match(), especially if the pattern exists in the string * * Note: It was reported that not only space characters, but all in * the following pattern can be parsed as separators between a tag name * and its attributes: [\d\s"\'`;,\/\=\(\x00\x0B\x09\x0C] * ... however, remove_invisible_characters() above already strips the * hex-encoded ones, so we'll skip them below. */ do { $original = $str; if (preg_match('/]+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str); } if (preg_match('/]*?)(?:\s?/?>|$)#si', array($this, '_js_img_removal'), $str); } if (preg_match('/script|xss/i', $str)) { $str = preg_replace('##si', '[removed]', $str); } } while ($original !== $str); unset($original); /* * Sanitize naughty HTML elements * * If a tag containing any of the words in the list * below is found, the tag gets converted to entities. * * So this: * Becomes: <blink> */ $pattern = '#' .'<((?/*\s*)(?[a-z0-9]+)(?=[^a-z0-9]|$)' // tag start and name, followed by a non-tag character .'[^\s\042\047a-z0-9>/=]*' // a valid attribute character immediately after the tag would count as a separator // optional attributes .'(?(?:[\s\042\047/=]*' // non-attribute characters, excluding > (tag close) for obvious reasons .'[^\s\042\047>/=]+' // attribute characters // optional attribute-value .'(?:\s*=' // attribute-value separator .'(?:[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*))' // single, double or non-quoted value .')?' // end optional attribute-value group .')*)' // end optional attributes group .'[^>]*)(?\>)?#isS'; // Note: It would be nice to optimize this for speed, BUT // only matching the naughty elements here results in // false positives and in turn - vulnerabilities! do { $old_str = $str; $str = preg_replace_callback($pattern, array($this, '_sanitize_naughty_html'), $str); } while ($old_str !== $str); unset($old_str); /* * Sanitize naughty scripting elements * * Similar to above, only instead of looking for * tags it looks for PHP and JavaScript commands * that are disallowed. Rather than removing the * code, it simply converts the parenthesis to entities * rendering the code un-executable. * * For example: eval('some code') * Becomes: eval('some code') */ $str = preg_replace( '#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', '\\1\\2(\\3)', $str ); // Final clean up // This adds a bit of extra precaution in case // something got through the above filters $str = $this->_do_never_allowed($str); /* * Images are Handled in a Special Way * - Essentially, we want to know that after all of the character * conversion is done whether any unwanted, likely XSS, code was found. * If not, we return TRUE, as the image is clean. * However, if the string post-conversion does not matched the * string post-removal of XSS, then it fails, as there was unwanted XSS * code found and removed/changed during processing. */ if ($is_image === TRUE) { return ($str === $converted_string); } return $str; } // -------------------------------------------------------------------- /** * XSS Hash * * Generates the XSS hash if needed and returns it. * * @see CI_Security::$_xss_hash * @return string XSS hash */ public function xss_hash() { if ($this->_xss_hash === NULL) { $rand = $this->get_random_bytes(16); $this->_xss_hash = ($rand === FALSE) ? md5(uniqid(mt_rand(), TRUE)) : bin2hex($rand); } return $this->_xss_hash; } // -------------------------------------------------------------------- /** * Get random bytes * * @param int $length Output length * @return string */ public function get_random_bytes($length) { if (empty($length) OR ! ctype_digit((string) $length)) { return FALSE; } if (function_exists('random_bytes')) { try { // The cast is required to avoid TypeError return random_bytes((int) $length); } catch (Exception $e) { // If random_bytes() can't do the job, we can't either ... // There's no point in using fallbacks. log_message('error', $e->getMessage()); return FALSE; } } // Unfortunately, none of the following PRNGs is guaranteed to exist ... if (defined('MCRYPT_DEV_URANDOM') && ($output = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)) !== FALSE) { return $output; } if (is_readable('/dev/urandom') && ($fp = fopen('/dev/urandom', 'rb')) !== FALSE) { // Try not to waste entropy ... is_php('5.4') && stream_set_chunk_size($fp, $length); $output = fread($fp, $length); fclose($fp); if ($output !== FALSE) { return $output; } } if (function_exists('openssl_random_pseudo_bytes')) { return openssl_random_pseudo_bytes($length); } return FALSE; } // -------------------------------------------------------------------- /** * HTML Entities Decode * * A replacement for html_entity_decode() * * The reason we are not using html_entity_decode() by itself is because * while it is not technically correct to leave out the semicolon * at the end of an entity most browsers will still interpret the entity * correctly. html_entity_decode() does not convert entities without * semicolons, so we are left with our own little solution here. Bummer. * * @link http://php.net/html-entity-decode * * @param string $str Input * @param string $charset Character set * @return string */ public function entity_decode($str, $charset = NULL) { if (strpos($str, '&') === FALSE) { return $str; } static $_entities; isset($charset) OR $charset = $this->charset; $flag = is_php('5.4') ? ENT_COMPAT | ENT_HTML5 : ENT_COMPAT; if ( ! isset($_entities)) { $_entities = array_map('strtolower', get_html_translation_table(HTML_ENTITIES, $flag, $charset)); // If we're not on PHP 5.4+, add the possibly dangerous HTML 5 // entities to the array manually if ($flag === ENT_COMPAT) { $_entities[':'] = ':'; $_entities['('] = '('; $_entities[')'] = ')'; $_entities["\n"] = ' '; $_entities["\t"] = ' '; } } do { $str_compare = $str; // Decode standard entities, avoiding false positives if (preg_match_all('/&[a-z]{2,}(?![a-z;])/i', $str, $matches)) { $replace = array(); $matches = array_unique(array_map('strtolower', $matches[0])); foreach ($matches as &$match) { if (($char = array_search($match.';', $_entities, TRUE)) !== FALSE) { $replace[$match] = $char; } } $str = str_replace(array_keys($replace), array_values($replace), $str); } // Decode numeric & UTF16 two byte entities $str = html_entity_decode( preg_replace('/(&#(?:x0*[0-9a-f]{2,5}(?![0-9a-f;])|(?:0*\d{2,4}(?![0-9;]))))/iS', '$1;', $str), $flag, $charset ); if ($flag === ENT_COMPAT) { $str = str_replace(array_values($_entities), array_keys($_entities), $str); } } while ($str_compare !== $str); return $str; } // -------------------------------------------------------------------- /** * Sanitize Filename * * @param string $str Input file name * @param bool $relative_path Whether to preserve paths * @return string */ public function sanitize_filename($str, $relative_path = FALSE) { $bad = $this->filename_bad_chars; if ( ! $relative_path) { $bad[] = './'; $bad[] = '/'; } $str = remove_invisible_characters($str, FALSE); do { $old = $str; $str = str_replace($bad, '', $str); } while ($old !== $str); return stripslashes($str); } // ---------------------------------------------------------------- /** * Strip Image Tags * * @param string $str * @return string */ public function strip_image_tags($str) { return preg_replace( array( '##i', '#`]+)).*?\>#i' ), '\\2', $str ); } // ---------------------------------------------------------------- /** * URL-decode taking spaces into account * * @see https://github.com/bcit-ci/CodeIgniter/issues/4877 * @param array $matches * @return string */ protected function _urldecodespaces($matches) { $input = $matches[0]; $nospaces = preg_replace('#\s+#', '', $input); return ($nospaces === $input) ? $input : rawurldecode($nospaces); } // ---------------------------------------------------------------- /** * Compact Exploded Words * * Callback method for xss_clean() to remove whitespace from * things like 'j a v a s c r i p t'. * * @used-by CI_Security::xss_clean() * @param array $matches * @return string */ protected function _compact_exploded_words($matches) { return preg_replace('/\s+/s', '', $matches[1]).$matches[2]; } // -------------------------------------------------------------------- /** * Sanitize Naughty HTML * * Callback method for xss_clean() to remove naughty HTML elements. * * @used-by CI_Security::xss_clean() * @param array $matches * @return string */ protected function _sanitize_naughty_html($matches) { static $naughty_tags = array( 'alert', 'area', 'prompt', 'confirm', 'applet', 'audio', 'basefont', 'base', 'behavior', 'bgsound', 'blink', 'body', 'embed', 'expression', 'form', 'frameset', 'frame', 'head', 'html', 'ilayer', 'iframe', 'input', 'button', 'select', 'isindex', 'layer', 'link', 'meta', 'keygen', 'object', 'plaintext', 'style', 'script', 'textarea', 'title', 'math', 'video', 'svg', 'xml', 'xss' ); static $evil_attributes = array( 'on\w+', 'style', 'xmlns', 'formaction', 'form', 'xlink:href', 'FSCommand', 'seekSegmentTime' ); // First, escape unclosed tags if (empty($matches['closeTag'])) { return '<'.$matches[1]; } // Is the element that we caught naughty? If so, escape it elseif (in_array(strtolower($matches['tagName']), $naughty_tags, TRUE)) { return '<'.$matches[1].'>'; } // For other tags, see if their attributes are "evil" and strip those elseif (isset($matches['attributes'])) { // We'll store the already fitlered attributes here $attributes = array(); // Attribute-catching pattern $attributes_pattern = '#' .'(?[^\s\042\047>/=]+)' // attribute characters // optional attribute-value .'(?:\s*=(?[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*)))' // attribute-value separator .'#i'; // Blacklist pattern for evil attribute names $is_evil_pattern = '#^('.implode('|', $evil_attributes).')$#i'; // Each iteration filters a single attribute do { // Strip any non-alpha characters that may preceed an attribute. // Browsers often parse these incorrectly and that has been a // of numerous XSS issues we've had. $matches['attributes'] = preg_replace('#^[^a-z]+#i', '', $matches['attributes']); if ( ! preg_match($attributes_pattern, $matches['attributes'], $attribute, PREG_OFFSET_CAPTURE)) { // No (valid) attribute found? Discard everything else inside the tag break; } if ( // Is it indeed an "evil" attribute? preg_match($is_evil_pattern, $attribute['name'][0]) // Or does it have an equals sign, but no value and not quoted? Strip that too! OR (trim($attribute['value'][0]) === '') ) { $attributes[] = 'xss=removed'; } else { $attributes[] = $attribute[0][0]; } $matches['attributes'] = substr($matches['attributes'], $attribute[0][1] + strlen($attribute[0][0])); } while ($matches['attributes'] !== ''); $attributes = empty($attributes) ? '' : ' '.implode(' ', $attributes); return '<'.$matches['slash'].$matches['tagName'].$attributes.'>'; } return $matches[0]; } // -------------------------------------------------------------------- /** * JS Link Removal * * Callback method for xss_clean() to sanitize links. * * This limits the PCRE backtracks, making it more performance friendly * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in * PHP 5.2+ on link-heavy strings. * * @used-by CI_Security::xss_clean() * @param array $match * @return string */ protected function _js_link_removal($match) { return str_replace( $match[1], preg_replace( '#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|_filter_attributes($match[1]) ), $match[0] ); } // -------------------------------------------------------------------- /** * JS Image Removal * * Callback method for xss_clean() to sanitize image tags. * * This limits the PCRE backtracks, making it more performance friendly * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in * PHP 5.2+ on image tag heavy strings. * * @used-by CI_Security::xss_clean() * @param array $match * @return string */ protected function _js_img_removal($match) { return str_replace( $match[1], preg_replace( '#src=.*?(?:(?:alert|prompt|confirm|eval)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|_filter_attributes($match[1]) ), $match[0] ); } // -------------------------------------------------------------------- /** * Attribute Conversion * * @used-by CI_Security::xss_clean() * @param array $match * @return string */ protected function _convert_attribute($match) { return str_replace(array('>', '<', '\\'), array('>', '<', '\\\\'), $match[0]); } // -------------------------------------------------------------------- /** * Filter Attributes * * Filters tag attributes for consistency and safety. * * @used-by CI_Security::_js_img_removal() * @used-by CI_Security::_js_link_removal() * @param string $str * @return string */ protected function _filter_attributes($str) { $out = ''; if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches)) { foreach ($matches[0] as $match) { $out .= preg_replace('#/\*.*?\*/#s', '', $match); } } return $out; } // -------------------------------------------------------------------- /** * HTML Entity Decode Callback * * @used-by CI_Security::xss_clean() * @param array $match * @return string */ protected function _decode_entity($match) { // Protect GET variables in URLs // 901119URL5918AMP18930PROTECT8198 $match = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-/]+)|i', $this->xss_hash().'\\1=\\2', $match[0]); // Decode, then un-protect URL GET vars return str_replace( $this->xss_hash(), '&', $this->entity_decode($match, $this->charset) ); } // -------------------------------------------------------------------- /** * Do Never Allowed * * @used-by CI_Security::xss_clean() * @param string * @return string */ protected function _do_never_allowed($str) { $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str); foreach ($this->_never_allowed_regex as $regex) { $str = preg_replace('#'.$regex.'#is', '[removed]', $str); } return $str; } // -------------------------------------------------------------------- /** * Set CSRF Hash and Cookie * * @return string */ protected function _csrf_set_hash() { if ($this->_csrf_hash === NULL) { // If the cookie exists we will use its value. // We don't necessarily want to regenerate it with // each page load since a page could contain embedded // sub-pages causing this feature to fail if (isset($_COOKIE[$this->_csrf_cookie_name]) && is_string($_COOKIE[$this->_csrf_cookie_name]) && preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1) { return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; } $rand = $this->get_random_bytes(16); $this->_csrf_hash = ($rand === FALSE) ? md5(uniqid(mt_rand(), TRUE)) : bin2hex($rand); } return $this->_csrf_hash; } } PK!o~<$<$ Config.phpnu[config =& get_config(); // Set the base_url automatically if none was provided if (empty($this->config['base_url'])) { if (isset($_SERVER['SERVER_ADDR'])) { if (strpos($_SERVER['SERVER_ADDR'], ':') !== FALSE) { $server_addr = '['.$_SERVER['SERVER_ADDR'].']'; } else { $server_addr = $_SERVER['SERVER_ADDR']; } $base_url = (is_https() ? 'https' : 'http').'://'.$server_addr .substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME']))); } else { $base_url = 'http://localhost/'; } $this->set_item('base_url', $base_url); } log_message('info', 'Config Class Initialized'); } // -------------------------------------------------------------------- /** * Load Config File * * @param string $file Configuration file name * @param bool $use_sections Whether configuration values should be loaded into their own section * @param bool $fail_gracefully Whether to just return FALSE or display an error message * @return bool TRUE if the file was loaded correctly or FALSE on failure */ public function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) { $file = ($file === '') ? 'config' : str_replace('.php', '', $file); $loaded = FALSE; foreach ($this->_config_paths as $path) { foreach (array($file, ENVIRONMENT.DIRECTORY_SEPARATOR.$file) as $location) { $file_path = $path.'config/'.$location.'.php'; if (in_array($file_path, $this->is_loaded, TRUE)) { return TRUE; } if ( ! file_exists($file_path)) { continue; } include($file_path); if ( ! isset($config) OR ! is_array($config)) { if ($fail_gracefully === TRUE) { return FALSE; } show_error('Your '.$file_path.' file does not appear to contain a valid configuration array.'); } if ($use_sections === TRUE) { $this->config[$file] = isset($this->config[$file]) ? array_merge($this->config[$file], $config) : $config; } else { $this->config = array_merge($this->config, $config); } $this->is_loaded[] = $file_path; $config = NULL; $loaded = TRUE; log_message('debug', 'Config file loaded: '.$file_path); } } if ($loaded === TRUE) { return TRUE; } elseif ($fail_gracefully === TRUE) { return FALSE; } show_error('The configuration file '.$file.'.php does not exist.'); } // -------------------------------------------------------------------- /** * Fetch a config file item * * @param string $item Config item name * @param string $index Index name * @return string|null The configuration item or NULL if the item doesn't exist */ public function item($item, $index = '') { if ($index == '') { return isset($this->config[$item]) ? $this->config[$item] : NULL; } return isset($this->config[$index], $this->config[$index][$item]) ? $this->config[$index][$item] : NULL; } // -------------------------------------------------------------------- /** * Fetch a config file item with slash appended (if not empty) * * @param string $item Config item name * @return string|null The configuration item or NULL if the item doesn't exist */ public function slash_item($item) { if ( ! isset($this->config[$item])) { return NULL; } elseif (trim($this->config[$item]) === '') { return ''; } return rtrim($this->config[$item], '/').'/'; } // -------------------------------------------------------------------- /** * Site URL * * Returns base_url . index_page [. uri_string] * * @uses CI_Config::_uri_string() * * @param string|string[] $uri URI string or an array of segments * @param string $protocol * @return string */ public function site_url($uri = '', $protocol = NULL) { $base_url = $this->slash_item('base_url'); if (isset($protocol)) { // For protocol-relative links if ($protocol === '') { $base_url = substr($base_url, strpos($base_url, '//')); } else { $base_url = $protocol.substr($base_url, strpos($base_url, '://')); } } if (empty($uri)) { return $base_url.$this->item('index_page'); } $uri = $this->_uri_string($uri); if ($this->item('enable_query_strings') === FALSE) { $suffix = isset($this->config['url_suffix']) ? $this->config['url_suffix'] : ''; if ($suffix !== '') { if (($offset = strpos($uri, '?')) !== FALSE) { $uri = substr($uri, 0, $offset).$suffix.substr($uri, $offset); } else { $uri .= $suffix; } } return $base_url.$this->slash_item('index_page').$uri; } elseif (strpos($uri, '?') === FALSE) { $uri = '?'.$uri; } return $base_url.$this->item('index_page').$uri; } // ------------------------------------------------------------- /** * Base URL * * Returns base_url [. uri_string] * * @uses CI_Config::_uri_string() * * @param string|string[] $uri URI string or an array of segments * @param string $protocol * @return string */ public function base_url($uri = '', $protocol = NULL) { $base_url = $this->slash_item('base_url'); if (isset($protocol)) { // For protocol-relative links if ($protocol === '') { $base_url = substr($base_url, strpos($base_url, '//')); } else { $base_url = $protocol.substr($base_url, strpos($base_url, '://')); } } return $base_url.$this->_uri_string($uri); } // ------------------------------------------------------------- /** * Build URI string * * @used-by CI_Config::site_url() * @used-by CI_Config::base_url() * * @param string|string[] $uri URI string or an array of segments * @return string */ protected function _uri_string($uri) { if ($this->item('enable_query_strings') === FALSE) { is_array($uri) && $uri = implode('/', $uri); return ltrim($uri, '/'); } elseif (is_array($uri)) { return http_build_query($uri); } return $uri; } // -------------------------------------------------------------------- /** * System URL * * @deprecated 3.0.0 Encourages insecure practices * @return string */ public function system_url() { $x = explode('/', preg_replace('|/*(.+?)/*$|', '\\1', BASEPATH)); return $this->slash_item('base_url').end($x).'/'; } // -------------------------------------------------------------------- /** * Set a config file item * * @param string $item Config item key * @param string $value Config item value * @return void */ public function set_item($item, $value) { $this->config[$item] = $value; } } PK!Λ>>Utf8.phpnu[is_ascii($str) === FALSE) { if (MB_ENABLED) { $str = mb_convert_encoding($str, 'UTF-8', 'UTF-8'); } elseif (ICONV_ENABLED) { $str = @iconv('UTF-8', 'UTF-8//IGNORE', $str); } } return $str; } // -------------------------------------------------------------------- /** * Remove ASCII control characters * * Removes all ASCII control characters except horizontal tabs, * line feeds, and carriage returns, as all others can cause * problems in XML. * * @param string $str String to clean * @return string */ public function safe_ascii_for_xml($str) { return remove_invisible_characters($str, FALSE); } // -------------------------------------------------------------------- /** * Convert to UTF-8 * * Attempts to convert a string to UTF-8. * * @param string $str Input string * @param string $encoding Input encoding * @return string $str encoded in UTF-8 or FALSE on failure */ public function convert_to_utf8($str, $encoding) { if (MB_ENABLED) { return mb_convert_encoding($str, 'UTF-8', $encoding); } elseif (ICONV_ENABLED) { return @iconv($encoding, 'UTF-8', $str); } return FALSE; } // -------------------------------------------------------------------- /** * Is ASCII? * * Tests if a string is standard 7-bit ASCII or not. * * @param string $str String to check * @return bool */ public function is_ascii($str) { return (preg_match('/[^\x00-\x7F]/S', $str) === 0); } } PK!bA>>CodeIgniter.phpnu[ '_ENV', 'G' => '_GET', 'P' => '_POST', 'C' => '_COOKIE', 'S' => '_SERVER') as $key => $superglobal) { if (strpos($_registered, $key) === FALSE) { continue; } foreach (array_keys($$superglobal) as $var) { if (isset($GLOBALS[$var]) && ! in_array($var, $_protected, TRUE)) { $GLOBALS[$var] = NULL; } } } } } /* * ------------------------------------------------------ * Define a custom error handler so we can log PHP errors * ------------------------------------------------------ */ set_error_handler('_error_handler'); set_exception_handler('_exception_handler'); register_shutdown_function('_shutdown_handler'); /* * ------------------------------------------------------ * Set the subclass_prefix * ------------------------------------------------------ * * Normally the "subclass_prefix" is set in the config file. * The subclass prefix allows CI to know if a core class is * being extended via a library in the local application * "libraries" folder. Since CI allows config items to be * overridden via data set in the main index.php file, * before proceeding we need to know if a subclass_prefix * override exists. If so, we will set this value now, * before any classes are loaded * Note: Since the config file data is cached it doesn't * hurt to load it here. */ if ( ! empty($assign_to_config['subclass_prefix'])) { get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix'])); } /* * ------------------------------------------------------ * Should we use a Composer autoloader? * ------------------------------------------------------ */ if ($composer_autoload = config_item('composer_autoload')) { if ($composer_autoload === TRUE) { file_exists(APPPATH.'vendor/autoload.php') ? require_once(APPPATH.'vendor/autoload.php') : log_message('error', '$config[\'composer_autoload\'] is set to TRUE but '.APPPATH.'vendor/autoload.php was not found.'); } elseif (file_exists($composer_autoload)) { require_once($composer_autoload); } else { log_message('error', 'Could not find the specified $config[\'composer_autoload\'] path: '.$composer_autoload); } } /* * ------------------------------------------------------ * Start the timer... tick tock tick tock... * ------------------------------------------------------ */ $BM =& load_class('Benchmark', 'core'); $BM->mark('total_execution_time_start'); $BM->mark('loading_time:_base_classes_start'); /* * ------------------------------------------------------ * Instantiate the hooks class * ------------------------------------------------------ */ $EXT =& load_class('Hooks', 'core'); /* * ------------------------------------------------------ * Is there a "pre_system" hook? * ------------------------------------------------------ */ $EXT->call_hook('pre_system'); /* * ------------------------------------------------------ * Instantiate the config class * ------------------------------------------------------ * * Note: It is important that Config is loaded first as * most other classes depend on it either directly or by * depending on another class that uses it. * */ $CFG =& load_class('Config', 'core'); // Do we have any manually set config items in the index.php file? if (isset($assign_to_config) && is_array($assign_to_config)) { foreach ($assign_to_config as $key => $value) { $CFG->set_item($key, $value); } } /* * ------------------------------------------------------ * Important charset-related stuff * ------------------------------------------------------ * * Configure mbstring and/or iconv if they are enabled * and set MB_ENABLED and ICONV_ENABLED constants, so * that we don't repeatedly do extension_loaded() or * function_exists() calls. * * Note: UTF-8 class depends on this. It used to be done * in it's constructor, but it's _not_ class-specific. * */ $charset = strtoupper(config_item('charset')); ini_set('default_charset', $charset); if (extension_loaded('mbstring')) { define('MB_ENABLED', TRUE); // mbstring.internal_encoding is deprecated starting with PHP 5.6 // and it's usage triggers E_DEPRECATED messages. @ini_set('mbstring.internal_encoding', $charset); // This is required for mb_convert_encoding() to strip invalid characters. // That's utilized by CI_Utf8, but it's also done for consistency with iconv. mb_substitute_character('none'); } else { define('MB_ENABLED', FALSE); } // There's an ICONV_IMPL constant, but the PHP manual says that using // iconv's predefined constants is "strongly discouraged". if (extension_loaded('iconv')) { define('ICONV_ENABLED', TRUE); // iconv.internal_encoding is deprecated starting with PHP 5.6 // and it's usage triggers E_DEPRECATED messages. @ini_set('iconv.internal_encoding', $charset); } else { define('ICONV_ENABLED', FALSE); } if (is_php('5.6')) { ini_set('php.internal_encoding', $charset); } /* * ------------------------------------------------------ * Load compatibility features * ------------------------------------------------------ */ require_once(BASEPATH.'core/compat/mbstring.php'); require_once(BASEPATH.'core/compat/hash.php'); require_once(BASEPATH.'core/compat/password.php'); require_once(BASEPATH.'core/compat/standard.php'); /* * ------------------------------------------------------ * Instantiate the UTF-8 class * ------------------------------------------------------ */ $UNI =& load_class('Utf8', 'core'); /* * ------------------------------------------------------ * Instantiate the URI class * ------------------------------------------------------ */ $URI =& load_class('URI', 'core'); /* * ------------------------------------------------------ * Instantiate the routing class and set the routing * ------------------------------------------------------ */ $RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL); /* * ------------------------------------------------------ * Instantiate the output class * ------------------------------------------------------ */ $OUT =& load_class('Output', 'core'); /* * ------------------------------------------------------ * Is there a valid cache file? If so, we're done... * ------------------------------------------------------ */ if ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE) { exit; } /* * ----------------------------------------------------- * Load the security class for xss and csrf support * ----------------------------------------------------- */ $SEC =& load_class('Security', 'core'); /* * ------------------------------------------------------ * Load the Input class and sanitize globals * ------------------------------------------------------ */ $IN =& load_class('Input', 'core'); /* * ------------------------------------------------------ * Load the Language class * ------------------------------------------------------ */ $LANG =& load_class('Lang', 'core'); /* * ------------------------------------------------------ * Load the app controller and local controller * ------------------------------------------------------ * */ // Load the base controller class require_once BASEPATH.'core/Controller.php'; /** * Reference to the CI_Controller method. * * Returns current CI instance object * * @return CI_Controller */ function &get_instance() { return CI_Controller::get_instance(); } if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php')) { require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'; } // Set a mark point for benchmarking $BM->mark('loading_time:_base_classes_end'); /* * ------------------------------------------------------ * Sanity checks * ------------------------------------------------------ * * The Router class has already validated the request, * leaving us with 3 options here: * * 1) an empty class name, if we reached the default * controller, but it didn't exist; * 2) a query string which doesn't go through a * file_exists() check * 3) a regular request for a non-existing page * * We handle all of these as a 404 error. * * Furthermore, none of the methods in the app controller * or the loader class can be called via the URI, nor can * controller methods that begin with an underscore. */ $e404 = FALSE; $class = ucfirst($RTR->class); $method = $RTR->method; if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php')) { $e404 = TRUE; } else { require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php'); if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method)) { $e404 = TRUE; } elseif (method_exists($class, '_remap')) { $params = array($method, array_slice($URI->rsegments, 2)); $method = '_remap'; } elseif ( ! method_exists($class, $method)) { $e404 = TRUE; } /** * DO NOT CHANGE THIS, NOTHING ELSE WORKS! * * - method_exists() returns true for non-public methods, which passes the previous elseif * - is_callable() returns false for PHP 4-style constructors, even if there's a __construct() * - method_exists($class, '__construct') won't work because CI_Controller::__construct() is inherited * - People will only complain if this doesn't work, even though it is documented that it shouldn't. * * ReflectionMethod::isConstructor() is the ONLY reliable check, * knowing which method will be executed as a constructor. */ elseif ( ! is_callable(array($class, $method)) && strcasecmp($class, $method) === 0) { $reflection = new ReflectionMethod($class, $method); if ( ! $reflection->isPublic() OR $reflection->isConstructor()) { $e404 = TRUE; } } } if ($e404) { if ( ! empty($RTR->routes['404_override'])) { if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2) { $error_method = 'index'; } $error_class = ucfirst($error_class); if ( ! class_exists($error_class, FALSE)) { if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php')) { require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'); $e404 = ! class_exists($error_class, FALSE); } // Were we in a directory? If so, check for a global override elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php')) { require_once(APPPATH.'controllers/'.$error_class.'.php'); if (($e404 = ! class_exists($error_class, FALSE)) === FALSE) { $RTR->directory = ''; } } } else { $e404 = FALSE; } } // Did we reset the $e404 flag? If so, set the rsegments, starting from index 1 if ( ! $e404) { $class = $error_class; $method = $error_method; $URI->rsegments = array( 1 => $class, 2 => $method ); } else { show_404($RTR->directory.$class.'/'.$method); } } if ($method !== '_remap') { $params = array_slice($URI->rsegments, 2); } /* * ------------------------------------------------------ * Is there a "pre_controller" hook? * ------------------------------------------------------ */ $EXT->call_hook('pre_controller'); /* * ------------------------------------------------------ * Instantiate the requested controller * ------------------------------------------------------ */ // Mark a start point so we can benchmark the controller $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start'); $CI = new $class(); /* * ------------------------------------------------------ * Is there a "post_controller_constructor" hook? * ------------------------------------------------------ */ $EXT->call_hook('post_controller_constructor'); /* * ------------------------------------------------------ * Call the requested method * ------------------------------------------------------ */ call_user_func_array(array(&$CI, $method), $params); // Mark a benchmark end point $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end'); /* * ------------------------------------------------------ * Is there a "post_controller" hook? * ------------------------------------------------------ */ $EXT->call_hook('post_controller'); /* * ------------------------------------------------------ * Send the final rendered output to the browser * ------------------------------------------------------ */ if ($EXT->call_hook('display_override') === FALSE) { $OUT->_display(); } /* * ------------------------------------------------------ * Is there a "post_system" hook? * ------------------------------------------------------ */ $EXT->call_hook('post_system'); PK!V IIcompat/mbstring.phpnu[ 0, 'algoName' => 'unknown', 'options' => array()) : array('algo' => 1, 'algoName' => 'bcrypt', 'options' => array('cost' => $hash)); } } // ------------------------------------------------------------------------ if ( ! function_exists('password_hash')) { /** * password_hash() * * @link http://php.net/password_hash * @param string $password * @param int $algo * @param array $options * @return mixed */ function password_hash($password, $algo, array $options = array()) { static $func_override; isset($func_override) OR $func_override = (extension_loaded('mbstring') && ini_get('mbstring.func_override')); if ($algo !== 1) { trigger_error('password_hash(): Unknown hashing algorithm: '.(int) $algo, E_USER_WARNING); return NULL; } if (isset($options['cost']) && ($options['cost'] < 4 OR $options['cost'] > 31)) { trigger_error('password_hash(): Invalid bcrypt cost parameter specified: '.(int) $options['cost'], E_USER_WARNING); return NULL; } if (isset($options['salt']) && ($saltlen = ($func_override ? mb_strlen($options['salt'], '8bit') : strlen($options['salt']))) < 22) { trigger_error('password_hash(): Provided salt is too short: '.$saltlen.' expecting 22', E_USER_WARNING); return NULL; } elseif ( ! isset($options['salt'])) { if (function_exists('random_bytes')) { try { $options['salt'] = random_bytes(16); } catch (Exception $e) { log_message('error', 'compat/password: Error while trying to use random_bytes(): '.$e->getMessage()); return FALSE; } } elseif (defined('MCRYPT_DEV_URANDOM')) { $options['salt'] = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM); } elseif (DIRECTORY_SEPARATOR === '/' && (is_readable($dev = '/dev/arandom') OR is_readable($dev = '/dev/urandom'))) { if (($fp = fopen($dev, 'rb')) === FALSE) { log_message('error', 'compat/password: Unable to open '.$dev.' for reading.'); return FALSE; } // Try not to waste entropy ... is_php('5.4') && stream_set_chunk_size($fp, 16); $options['salt'] = ''; for ($read = 0; $read < 16; $read = ($func_override) ? mb_strlen($options['salt'], '8bit') : strlen($options['salt'])) { if (($read = fread($fp, 16 - $read)) === FALSE) { log_message('error', 'compat/password: Error while reading from '.$dev.'.'); return FALSE; } $options['salt'] .= $read; } fclose($fp); } elseif (function_exists('openssl_random_pseudo_bytes')) { $is_secure = NULL; $options['salt'] = openssl_random_pseudo_bytes(16, $is_secure); if ($is_secure !== TRUE) { log_message('error', 'compat/password: openssl_random_pseudo_bytes() set the $cryto_strong flag to FALSE'); return FALSE; } } else { log_message('error', 'compat/password: No CSPRNG available.'); return FALSE; } $options['salt'] = str_replace('+', '.', rtrim(base64_encode($options['salt']), '=')); } elseif ( ! preg_match('#^[a-zA-Z0-9./]+$#D', $options['salt'])) { $options['salt'] = str_replace('+', '.', rtrim(base64_encode($options['salt']), '=')); } isset($options['cost']) OR $options['cost'] = 10; return (strlen($password = crypt($password, sprintf('$2y$%02d$%s', $options['cost'], $options['salt']))) === 60) ? $password : FALSE; } } // ------------------------------------------------------------------------ if ( ! function_exists('password_needs_rehash')) { /** * password_needs_rehash() * * @link http://php.net/password_needs_rehash * @param string $hash * @param int $algo * @param array $options * @return bool */ function password_needs_rehash($hash, $algo, array $options = array()) { $info = password_get_info($hash); if ($algo !== $info['algo']) { return TRUE; } elseif ($algo === 1) { $options['cost'] = isset($options['cost']) ? (int) $options['cost'] : 10; return ($info['options']['cost'] !== $options['cost']); } // Odd at first glance, but according to a comment in PHP's own unit tests, // because it is an unknown algorithm - it's valid and therefore doesn't // need rehashing. return FALSE; } } // ------------------------------------------------------------------------ if ( ! function_exists('password_verify')) { /** * password_verify() * * @link http://php.net/password_verify * @param string $password * @param string $hash * @return bool */ function password_verify($password, $hash) { if (strlen($hash) !== 60 OR strlen($password = crypt($password, $hash)) !== 60) { return FALSE; } $compare = 0; for ($i = 0; $i < 60; $i++) { $compare |= (ord($password[$i]) ^ ord($hash[$i])); } return ($compare === 0); } } PK!(compat/index.htmlnu[ 403 Forbidden

Directory access is forbidden.

PK!%'compat/hash.phpnu[ 32, 'haval128,3' => 128, 'haval160,3' => 128, 'haval192,3' => 128, 'haval224,3' => 128, 'haval256,3' => 128, 'haval128,4' => 128, 'haval160,4' => 128, 'haval192,4' => 128, 'haval224,4' => 128, 'haval256,4' => 128, 'haval128,5' => 128, 'haval160,5' => 128, 'haval192,5' => 128, 'haval224,5' => 128, 'haval256,5' => 128, 'md2' => 16, 'md4' => 64, 'md5' => 64, 'ripemd128' => 64, 'ripemd160' => 64, 'ripemd256' => 64, 'ripemd320' => 64, 'salsa10' => 64, 'salsa20' => 64, 'sha1' => 64, 'sha224' => 64, 'sha256' => 64, 'sha384' => 128, 'sha512' => 128, 'snefru' => 32, 'snefru256' => 32, 'tiger128,3' => 64, 'tiger160,3' => 64, 'tiger192,3' => 64, 'tiger128,4' => 64, 'tiger160,4' => 64, 'tiger192,4' => 64, 'whirlpool' => 64 ); if (isset($block_sizes[$algo]) && strlen($password) > $block_sizes[$algo]) { $password = hash($algo, $password, TRUE); } $hash = ''; // Note: Blocks are NOT 0-indexed for ($bc = ceil($length / $hash_length), $bi = 1; $bi <= $bc; $bi++) { $key = $derived_key = hash_hmac($algo, $salt.pack('N', $bi), $password, TRUE); for ($i = 1; $i < $iterations; $i++) { $derived_key ^= $key = hash_hmac($algo, $key, $password, TRUE); } $hash .= $derived_key; } // This is not RFC-compatible, but we're aiming for natural PHP compatibility return substr($raw_output ? $hash : bin2hex($hash), 0, $length); } } PK!Ӝ8RR Output.phpnu[_zlib_oc = (bool) ini_get('zlib.output_compression'); $this->_compress_output = ( $this->_zlib_oc === FALSE && config_item('compress_output') === TRUE && extension_loaded('zlib') ); isset(self::$func_override) OR self::$func_override = (extension_loaded('mbstring') && ini_get('mbstring.func_override')); // Get mime types for later $this->mimes =& get_mimes(); log_message('info', 'Output Class Initialized'); } // -------------------------------------------------------------------- /** * Get Output * * Returns the current output string. * * @return string */ public function get_output() { return $this->final_output; } // -------------------------------------------------------------------- /** * Set Output * * Sets the output string. * * @param string $output Output data * @return CI_Output */ public function set_output($output) { $this->final_output = $output; return $this; } // -------------------------------------------------------------------- /** * Append Output * * Appends data onto the output string. * * @param string $output Data to append * @return CI_Output */ public function append_output($output) { $this->final_output .= $output; return $this; } // -------------------------------------------------------------------- /** * Set Header * * Lets you set a server header which will be sent with the final output. * * Note: If a file is cached, headers will not be sent. * @todo We need to figure out how to permit headers to be cached. * * @param string $header Header * @param bool $replace Whether to replace the old header value, if already set * @return CI_Output */ public function set_header($header, $replace = TRUE) { // If zlib.output_compression is enabled it will compress the output, // but it will not modify the content-length header to compensate for // the reduction, causing the browser to hang waiting for more data. // We'll just skip content-length in those cases. if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0) { return $this; } $this->headers[] = array($header, $replace); return $this; } // -------------------------------------------------------------------- /** * Set Content-Type Header * * @param string $mime_type Extension of the file we're outputting * @param string $charset Character set (default: NULL) * @return CI_Output */ public function set_content_type($mime_type, $charset = NULL) { if (strpos($mime_type, '/') === FALSE) { $extension = ltrim($mime_type, '.'); // Is this extension supported? if (isset($this->mimes[$extension])) { $mime_type =& $this->mimes[$extension]; if (is_array($mime_type)) { $mime_type = current($mime_type); } } } $this->mime_type = $mime_type; if (empty($charset)) { $charset = config_item('charset'); } $header = 'Content-Type: '.$mime_type .(empty($charset) ? '' : '; charset='.$charset); $this->headers[] = array($header, TRUE); return $this; } // -------------------------------------------------------------------- /** * Get Current Content-Type Header * * @return string 'text/html', if not already set */ public function get_content_type() { for ($i = 0, $c = count($this->headers); $i < $c; $i++) { if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1) { return $content_type; } } return 'text/html'; } // -------------------------------------------------------------------- /** * Get Header * * @param string $header * @return string */ public function get_header($header) { // Combine headers already sent with our batched headers $headers = array_merge( // We only need [x][0] from our multi-dimensional array array_map('array_shift', $this->headers), headers_list() ); if (empty($headers) OR empty($header)) { return NULL; } for ($i = 0, $c = count($headers); $i < $c; $i++) { if (strncasecmp($header, $headers[$i], $l = self::strlen($header)) === 0) { return trim(self::substr($headers[$i], $l+1)); } } return NULL; } // -------------------------------------------------------------------- /** * Set HTTP Status Header * * As of version 1.7.2, this is an alias for common function * set_status_header(). * * @param int $code Status code (default: 200) * @param string $text Optional message * @return CI_Output */ public function set_status_header($code = 200, $text = '') { set_status_header($code, $text); return $this; } // -------------------------------------------------------------------- /** * Enable/disable Profiler * * @param bool $val TRUE to enable or FALSE to disable * @return CI_Output */ public function enable_profiler($val = TRUE) { $this->enable_profiler = is_bool($val) ? $val : TRUE; return $this; } // -------------------------------------------------------------------- /** * Set Profiler Sections * * Allows override of default/config settings for * Profiler section display. * * @param array $sections Profiler sections * @return CI_Output */ public function set_profiler_sections($sections) { if (isset($sections['query_toggle_count'])) { $this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count']; unset($sections['query_toggle_count']); } foreach ($sections as $section => $enable) { $this->_profiler_sections[$section] = ($enable !== FALSE); } return $this; } // -------------------------------------------------------------------- /** * Set Cache * * @param int $time Cache expiration time in minutes * @return CI_Output */ public function cache($time) { $this->cache_expiration = is_numeric($time) ? $time : 0; return $this; } // -------------------------------------------------------------------- /** * Display Output * * Processes and sends finalized output data to the browser along * with any server headers and profile data. It also stops benchmark * timers so the page rendering speed and memory usage can be shown. * * Note: All "view" data is automatically put into $this->final_output * by controller class. * * @uses CI_Output::$final_output * @param string $output Output data override * @return void */ public function _display($output = '') { // Note: We use load_class() because we can't use $CI =& get_instance() // since this function is sometimes called by the caching mechanism, // which happens before the CI super object is available. $BM =& load_class('Benchmark', 'core'); $CFG =& load_class('Config', 'core'); // Grab the super object if we can. if (class_exists('CI_Controller', FALSE)) { $CI =& get_instance(); } // -------------------------------------------------------------------- // Set the output data if ($output === '') { $output =& $this->final_output; } // -------------------------------------------------------------------- // Do we need to write a cache file? Only if the controller does not have its // own _output() method and we are not dealing with a cache file, which we // can determine by the existence of the $CI object above if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output')) { $this->_write_cache($output); } // -------------------------------------------------------------------- // Parse out the elapsed time and memory usage, // then swap the pseudo-variables with the data $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end'); if ($this->parse_exec_vars === TRUE) { $memory = round(memory_get_usage() / 1024 / 1024, 2).'MB'; $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output); } // -------------------------------------------------------------------- // Is compression requested? if (isset($CI) // This means that we're not serving a cache file, if we were, it would already be compressed && $this->_compress_output === TRUE && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) { ob_start('ob_gzhandler'); } // -------------------------------------------------------------------- // Are there any server headers to send? if (count($this->headers) > 0) { foreach ($this->headers as $header) { @header($header[0], $header[1]); } } // -------------------------------------------------------------------- // Does the $CI object exist? // If not we know we are dealing with a cache file so we'll // simply echo out the data and exit. if ( ! isset($CI)) { if ($this->_compress_output === TRUE) { if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) { header('Content-Encoding: gzip'); header('Content-Length: '.self::strlen($output)); } else { // User agent doesn't support gzip compression, // so we'll have to decompress our cache $output = gzinflate(self::substr($output, 10, -8)); } } echo $output; log_message('info', 'Final output sent to browser'); log_message('debug', 'Total execution time: '.$elapsed); return; } // -------------------------------------------------------------------- // Do we need to generate profile data? // If so, load the Profile class and run it. if ($this->enable_profiler === TRUE) { $CI->load->library('profiler'); if ( ! empty($this->_profiler_sections)) { $CI->profiler->set_sections($this->_profiler_sections); } // If the output data contains closing and tags // we will remove them and add them back after we insert the profile data $output = preg_replace('|.*?|is', '', $output, -1, $count).$CI->profiler->run(); if ($count > 0) { $output .= ''; } } // Does the controller contain a function named _output()? // If so send the output there. Otherwise, echo it. if (method_exists($CI, '_output')) { $CI->_output($output); } else { echo $output; // Send it to the browser! } log_message('info', 'Final output sent to browser'); log_message('debug', 'Total execution time: '.$elapsed); } // -------------------------------------------------------------------- /** * Write Cache * * @param string $output Output data to cache * @return void */ public function _write_cache($output) { $CI =& get_instance(); $path = $CI->config->item('cache_path'); $cache_path = ($path === '') ? APPPATH.'cache/' : $path; if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path)) { log_message('error', 'Unable to write cache file: '.$cache_path); return; } $uri = $CI->config->item('base_url') .$CI->config->item('index_page') .$CI->uri->uri_string(); if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) { if (is_array($cache_query_string)) { $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); } else { $uri .= '?'.$_SERVER['QUERY_STRING']; } } $cache_path .= md5($uri); if ( ! $fp = @fopen($cache_path, 'w+b')) { log_message('error', 'Unable to write cache file: '.$cache_path); return; } if (flock($fp, LOCK_EX)) { // If output compression is enabled, compress the cache // itself, so that we don't have to do that each time // we're serving it if ($this->_compress_output === TRUE) { $output = gzencode($output); if ($this->get_header('content-type') === NULL) { $this->set_content_type($this->mime_type); } } $expire = time() + ($this->cache_expiration * 60); // Put together our serialized info. $cache_info = serialize(array( 'expire' => $expire, 'headers' => $this->headers )); $output = $cache_info.'ENDCI--->'.$output; for ($written = 0, $length = self::strlen($output); $written < $length; $written += $result) { if (($result = fwrite($fp, self::substr($output, $written))) === FALSE) { break; } } flock($fp, LOCK_UN); } else { log_message('error', 'Unable to secure a file lock for file at: '.$cache_path); return; } fclose($fp); if (is_int($result)) { chmod($cache_path, 0640); log_message('debug', 'Cache file written: '.$cache_path); // Send HTTP cache-control headers to browser to match file cache settings. $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire); } else { @unlink($cache_path); log_message('error', 'Unable to write the complete cache content at: '.$cache_path); } } // -------------------------------------------------------------------- /** * Update/serve cached output * * @uses CI_Config * @uses CI_URI * * @param object &$CFG CI_Config class instance * @param object &$URI CI_URI class instance * @return bool TRUE on success or FALSE on failure */ public function _display_cache(&$CFG, &$URI) { $cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path'); // Build the file path. The file name is an MD5 hash of the full URI $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string; if (($cache_query_string = $CFG->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) { if (is_array($cache_query_string)) { $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); } else { $uri .= '?'.$_SERVER['QUERY_STRING']; } } $filepath = $cache_path.md5($uri); if ( ! file_exists($filepath) OR ! $fp = @fopen($filepath, 'rb')) { return FALSE; } flock($fp, LOCK_SH); $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : ''; flock($fp, LOCK_UN); fclose($fp); // Look for embedded serialized file info. if ( ! preg_match('/^(.*)ENDCI--->/', $cache, $match)) { return FALSE; } $cache_info = unserialize($match[1]); $expire = $cache_info['expire']; $last_modified = filemtime($filepath); // Has the file expired? if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path)) { // If so we'll delete it. @unlink($filepath); log_message('debug', 'Cache file has expired. File deleted.'); return FALSE; } else { // Or else send the HTTP cache control headers. $this->set_cache_header($last_modified, $expire); } // Add headers from cache file. foreach ($cache_info['headers'] as $header) { $this->set_header($header[0], $header[1]); } // Display the cache $this->_display(self::substr($cache, self::strlen($match[0]))); log_message('debug', 'Cache file is current. Sending it to browser.'); return TRUE; } // -------------------------------------------------------------------- /** * Delete cache * * @param string $uri URI string * @return bool */ public function delete_cache($uri = '') { $CI =& get_instance(); $cache_path = $CI->config->item('cache_path'); if ($cache_path === '') { $cache_path = APPPATH.'cache/'; } if ( ! is_dir($cache_path)) { log_message('error', 'Unable to find cache path: '.$cache_path); return FALSE; } if (empty($uri)) { $uri = $CI->uri->uri_string(); if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) { if (is_array($cache_query_string)) { $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); } else { $uri .= '?'.$_SERVER['QUERY_STRING']; } } } $cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').ltrim($uri, '/')); if ( ! @unlink($cache_path)) { log_message('error', 'Unable to delete cache file for '.$uri); return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Set Cache Header * * Set the HTTP headers to match the server-side file cache settings * in order to reduce bandwidth. * * @param int $last_modified Timestamp of when the page was last modified * @param int $expiration Timestamp of when should the requested page expire from cache * @return void */ public function set_cache_header($last_modified, $expiration) { $max_age = $expiration - $_SERVER['REQUEST_TIME']; if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { $this->set_status_header(304); exit; } else { header('Pragma: public'); header('Cache-Control: max-age='.$max_age.', public'); header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT'); header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT'); } } // -------------------------------------------------------------------- /** * Byte-safe strlen() * * @param string $str * @return int */ protected static function strlen($str) { return (self::$func_override) ? mb_strlen($str, '8bit') : strlen($str); } // -------------------------------------------------------------------- /** * Byte-safe substr() * * @param string $str * @param int $start * @param int $length * @return string */ protected static function substr($str, $start, $length = NULL) { if (self::$func_override) { // mb_substr($str, $start, null, '8bit') returns an empty // string on PHP 5.3 isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start); return mb_substr($str, $start, $length, '8bit'); } return isset($length) ? substr($str, $start, $length) : substr($str, $start); } } PK!9`;`;URI.phpnu[config =& load_class('Config', 'core'); // If query strings are enabled, we don't need to parse any segments. // However, they don't make sense under CLI. if (is_cli() OR $this->config->item('enable_query_strings') !== TRUE) { $this->_permitted_uri_chars = $this->config->item('permitted_uri_chars'); // If it's a CLI request, ignore the configuration if (is_cli()) { $uri = $this->_parse_argv(); } else { $protocol = $this->config->item('uri_protocol'); empty($protocol) && $protocol = 'REQUEST_URI'; switch ($protocol) { case 'AUTO': // For BC purposes only case 'REQUEST_URI': $uri = $this->_parse_request_uri(); break; case 'QUERY_STRING': $uri = $this->_parse_query_string(); break; case 'PATH_INFO': default: $uri = isset($_SERVER[$protocol]) ? $_SERVER[$protocol] : $this->_parse_request_uri(); break; } } $this->_set_uri_string($uri); } log_message('info', 'URI Class Initialized'); } // -------------------------------------------------------------------- /** * Set URI String * * @param string $str * @return void */ protected function _set_uri_string($str) { // Filter out control characters and trim slashes $this->uri_string = trim(remove_invisible_characters($str, FALSE), '/'); if ($this->uri_string !== '') { // Remove the URL suffix, if present if (($suffix = (string) $this->config->item('url_suffix')) !== '') { $slen = strlen($suffix); if (substr($this->uri_string, -$slen) === $suffix) { $this->uri_string = substr($this->uri_string, 0, -$slen); } } $this->segments[0] = NULL; // Populate the segments array foreach (explode('/', trim($this->uri_string, '/')) as $val) { $val = trim($val); // Filter segments for security $this->filter_uri($val); if ($val !== '') { $this->segments[] = $val; } } unset($this->segments[0]); } } // -------------------------------------------------------------------- /** * Parse REQUEST_URI * * Will parse REQUEST_URI and automatically detect the URI from it, * while fixing the query string if necessary. * * @return string */ protected function _parse_request_uri() { if ( ! isset($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])) { return ''; } // parse_url() returns false if no host is present, but the path or query string // contains a colon followed by a number $uri = parse_url('http://dummy'.$_SERVER['REQUEST_URI']); $query = isset($uri['query']) ? $uri['query'] : ''; $uri = isset($uri['path']) ? $uri['path'] : ''; if (isset($_SERVER['SCRIPT_NAME'][0])) { if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) { $uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME'])); } elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) { $uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); } } // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct // URI is found, and also fixes the QUERY_STRING server var and $_GET array. if (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0) { $query = explode('?', $query, 2); $uri = $query[0]; $_SERVER['QUERY_STRING'] = isset($query[1]) ? $query[1] : ''; } else { $_SERVER['QUERY_STRING'] = $query; } parse_str($_SERVER['QUERY_STRING'], $_GET); if ($uri === '/' OR $uri === '') { return '/'; } // Do some final cleaning of the URI and return it return $this->_remove_relative_directory($uri); } // -------------------------------------------------------------------- /** * Parse QUERY_STRING * * Will parse QUERY_STRING and automatically detect the URI from it. * * @return string */ protected function _parse_query_string() { $uri = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); if (trim($uri, '/') === '') { return ''; } elseif (strncmp($uri, '/', 1) === 0) { $uri = explode('?', $uri, 2); $_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : ''; $uri = $uri[0]; } parse_str($_SERVER['QUERY_STRING'], $_GET); return $this->_remove_relative_directory($uri); } // -------------------------------------------------------------------- /** * Parse CLI arguments * * Take each command line argument and assume it is a URI segment. * * @return string */ protected function _parse_argv() { $args = array_slice($_SERVER['argv'], 1); return $args ? implode('/', $args) : ''; } // -------------------------------------------------------------------- /** * Remove relative directory (../) and multi slashes (///) * * Do some final cleaning of the URI and return it, currently only used in self::_parse_request_uri() * * @param string $uri * @return string */ protected function _remove_relative_directory($uri) { $uris = array(); $tok = strtok($uri, '/'); while ($tok !== FALSE) { if (( ! empty($tok) OR $tok === '0') && $tok !== '..') { $uris[] = $tok; } $tok = strtok('/'); } return implode('/', $uris); } // -------------------------------------------------------------------- /** * Filter URI * * Filters segments for malicious characters. * * @param string $str * @return void */ public function filter_uri(&$str) { if ( ! empty($str) && ! empty($this->_permitted_uri_chars) && ! preg_match('/^['.$this->_permitted_uri_chars.']+$/i'.(UTF8_ENABLED ? 'u' : ''), $str)) { show_error('The URI you submitted has disallowed characters.', 400); } } // -------------------------------------------------------------------- /** * Fetch URI Segment * * @see CI_URI::$segments * @param int $n Index * @param mixed $no_result What to return if the segment index is not found * @return mixed */ public function segment($n, $no_result = NULL) { return isset($this->segments[$n]) ? $this->segments[$n] : $no_result; } // -------------------------------------------------------------------- /** * Fetch URI "routed" Segment * * Returns the re-routed URI segment (assuming routing rules are used) * based on the index provided. If there is no routing, will return * the same result as CI_URI::segment(). * * @see CI_URI::$rsegments * @see CI_URI::segment() * @param int $n Index * @param mixed $no_result What to return if the segment index is not found * @return mixed */ public function rsegment($n, $no_result = NULL) { return isset($this->rsegments[$n]) ? $this->rsegments[$n] : $no_result; } // -------------------------------------------------------------------- /** * URI to assoc * * Generates an associative array of URI data starting at the supplied * segment index. For example, if this is your URI: * * example.com/user/search/name/joe/location/UK/gender/male * * You can use this method to generate an array with this prototype: * * array ( * name => joe * location => UK * gender => male * ) * * @param int $n Index (default: 3) * @param array $default Default values * @return array */ public function uri_to_assoc($n = 3, $default = array()) { return $this->_uri_to_assoc($n, $default, 'segment'); } // -------------------------------------------------------------------- /** * Routed URI to assoc * * Identical to CI_URI::uri_to_assoc(), only it uses the re-routed * segment array. * * @see CI_URI::uri_to_assoc() * @param int $n Index (default: 3) * @param array $default Default values * @return array */ public function ruri_to_assoc($n = 3, $default = array()) { return $this->_uri_to_assoc($n, $default, 'rsegment'); } // -------------------------------------------------------------------- /** * Internal URI-to-assoc * * Generates a key/value pair from the URI string or re-routed URI string. * * @used-by CI_URI::uri_to_assoc() * @used-by CI_URI::ruri_to_assoc() * @param int $n Index (default: 3) * @param array $default Default values * @param string $which Array name ('segment' or 'rsegment') * @return array */ protected function _uri_to_assoc($n = 3, $default = array(), $which = 'segment') { if ( ! is_numeric($n)) { return $default; } if (isset($this->keyval[$which], $this->keyval[$which][$n])) { return $this->keyval[$which][$n]; } $total_segments = "total_{$which}s"; $segment_array = "{$which}_array"; if ($this->$total_segments() < $n) { return (count($default) === 0) ? array() : array_fill_keys($default, NULL); } $segments = array_slice($this->$segment_array(), ($n - 1)); $i = 0; $lastval = ''; $retval = array(); foreach ($segments as $seg) { if ($i % 2) { $retval[$lastval] = $seg; } else { $retval[$seg] = NULL; $lastval = $seg; } $i++; } if (count($default) > 0) { foreach ($default as $val) { if ( ! array_key_exists($val, $retval)) { $retval[$val] = NULL; } } } // Cache the array for reuse isset($this->keyval[$which]) OR $this->keyval[$which] = array(); $this->keyval[$which][$n] = $retval; return $retval; } // -------------------------------------------------------------------- /** * Assoc to URI * * Generates a URI string from an associative array. * * @param array $array Input array of key/value pairs * @return string URI string */ public function assoc_to_uri($array) { $temp = array(); foreach ((array) $array as $key => $val) { $temp[] = $key; $temp[] = $val; } return implode('/', $temp); } // -------------------------------------------------------------------- /** * Slash segment * * Fetches an URI segment with a slash. * * @param int $n Index * @param string $where Where to add the slash ('trailing' or 'leading') * @return string */ public function slash_segment($n, $where = 'trailing') { return $this->_slash_segment($n, $where, 'segment'); } // -------------------------------------------------------------------- /** * Slash routed segment * * Fetches an URI routed segment with a slash. * * @param int $n Index * @param string $where Where to add the slash ('trailing' or 'leading') * @return string */ public function slash_rsegment($n, $where = 'trailing') { return $this->_slash_segment($n, $where, 'rsegment'); } // -------------------------------------------------------------------- /** * Internal Slash segment * * Fetches an URI Segment and adds a slash to it. * * @used-by CI_URI::slash_segment() * @used-by CI_URI::slash_rsegment() * * @param int $n Index * @param string $where Where to add the slash ('trailing' or 'leading') * @param string $which Array name ('segment' or 'rsegment') * @return string */ protected function _slash_segment($n, $where = 'trailing', $which = 'segment') { $leading = $trailing = '/'; if ($where === 'trailing') { $leading = ''; } elseif ($where === 'leading') { $trailing = ''; } return $leading.$this->$which($n).$trailing; } // -------------------------------------------------------------------- /** * Segment Array * * @return array CI_URI::$segments */ public function segment_array() { return $this->segments; } // -------------------------------------------------------------------- /** * Routed Segment Array * * @return array CI_URI::$rsegments */ public function rsegment_array() { return $this->rsegments; } // -------------------------------------------------------------------- /** * Total number of segments * * @return int */ public function total_segments() { return count($this->segments); } // -------------------------------------------------------------------- /** * Total number of routed segments * * @return int */ public function total_rsegments() { return count($this->rsegments); } // -------------------------------------------------------------------- /** * Fetch URI string * * @return string CI_URI::$uri_string */ public function uri_string() { return $this->uri_string; } // -------------------------------------------------------------------- /** * Fetch Re-routed URI string * * @return string */ public function ruri_string() { return ltrim(load_class('Router', 'core')->directory, '/').implode('/', $this->rsegments); } } PK!REXX Input.phpnu[_allow_get_array = (config_item('allow_get_array') === TRUE); $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); $this->_enable_csrf = (config_item('csrf_protection') === TRUE); $this->_standardize_newlines = (bool) config_item('standardize_newlines'); $this->security =& load_class('Security', 'core'); // Do we need the UTF-8 class? if (UTF8_ENABLED === TRUE) { $this->uni =& load_class('Utf8', 'core'); } // Sanitize global arrays $this->_sanitize_globals(); // CSRF Protection check if ($this->_enable_csrf === TRUE && ! is_cli()) { $this->security->csrf_verify(); } log_message('info', 'Input Class Initialized'); } // -------------------------------------------------------------------- /** * Fetch from array * * Internal method used to retrieve values from global arrays. * * @param array &$array $_GET, $_POST, $_COOKIE, $_SERVER, etc. * @param mixed $index Index for item to be fetched from $array * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = NULL) { is_bool($xss_clean) OR $xss_clean = $this->_enable_xss; // If $index is NULL, it means that the whole $array is requested isset($index) OR $index = array_keys($array); // allow fetching multiple keys at once if (is_array($index)) { $output = array(); foreach ($index as $key) { $output[$key] = $this->_fetch_from_array($array, $key, $xss_clean); } return $output; } if (isset($array[$index])) { $value = $array[$index]; } elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation { $value = $array; for ($i = 0; $i < $count; $i++) { $key = trim($matches[0][$i], '[]'); if ($key === '') // Empty notation will return the value as array { break; } if (isset($value[$key])) { $value = $value[$key]; } else { return NULL; } } } else { return NULL; } return ($xss_clean === TRUE) ? $this->security->xss_clean($value) : $value; } // -------------------------------------------------------------------- /** * Fetch an item from the GET array * * @param mixed $index Index for item to be fetched from $_GET * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function get($index = NULL, $xss_clean = NULL) { return $this->_fetch_from_array($_GET, $index, $xss_clean); } // -------------------------------------------------------------------- /** * Fetch an item from the POST array * * @param mixed $index Index for item to be fetched from $_POST * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function post($index = NULL, $xss_clean = NULL) { return $this->_fetch_from_array($_POST, $index, $xss_clean); } // -------------------------------------------------------------------- /** * Fetch an item from POST data with fallback to GET * * @param string $index Index for item to be fetched from $_POST or $_GET * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function post_get($index, $xss_clean = NULL) { return isset($_POST[$index]) ? $this->post($index, $xss_clean) : $this->get($index, $xss_clean); } // -------------------------------------------------------------------- /** * Fetch an item from GET data with fallback to POST * * @param string $index Index for item to be fetched from $_GET or $_POST * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function get_post($index, $xss_clean = NULL) { return isset($_GET[$index]) ? $this->get($index, $xss_clean) : $this->post($index, $xss_clean); } // -------------------------------------------------------------------- /** * Fetch an item from the COOKIE array * * @param mixed $index Index for item to be fetched from $_COOKIE * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function cookie($index = NULL, $xss_clean = NULL) { return $this->_fetch_from_array($_COOKIE, $index, $xss_clean); } // -------------------------------------------------------------------- /** * Fetch an item from the SERVER array * * @param mixed $index Index for item to be fetched from $_SERVER * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function server($index, $xss_clean = NULL) { return $this->_fetch_from_array($_SERVER, $index, $xss_clean); } // ------------------------------------------------------------------------ /** * Fetch an item from the php://input stream * * Useful when you need to access PUT, DELETE or PATCH request data. * * @param string $index Index for item to be fetched * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ public function input_stream($index = NULL, $xss_clean = NULL) { // Prior to PHP 5.6, the input stream can only be read once, // so we'll need to check if we have already done that first. if ( ! is_array($this->_input_stream)) { // $this->raw_input_stream will trigger __get(). parse_str($this->raw_input_stream, $this->_input_stream); is_array($this->_input_stream) OR $this->_input_stream = array(); } return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean); } // ------------------------------------------------------------------------ /** * Set cookie * * Accepts an arbitrary number of parameters (up to 7) or an associative * array in the first parameter containing all the values. * * @param string|mixed[] $name Cookie name or an array containing parameters * @param string $value Cookie value * @param int $expire Cookie expiration time in seconds * @param string $domain Cookie domain (e.g.: '.yourdomain.com') * @param string $path Cookie path (default: '/') * @param string $prefix Cookie name prefix * @param bool $secure Whether to only transfer cookies via SSL * @param bool $httponly Whether to only makes the cookie accessible via HTTP (no javascript) * @return void */ public function set_cookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE, $httponly = FALSE) { if (is_array($name)) { // always leave 'name' in last place, as the loop will break otherwise, due to $$item foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name') as $item) { if (isset($name[$item])) { $$item = $name[$item]; } } } if ($prefix === '' && config_item('cookie_prefix') !== '') { $prefix = config_item('cookie_prefix'); } if ($domain == '' && config_item('cookie_domain') != '') { $domain = config_item('cookie_domain'); } if ($path === '/' && config_item('cookie_path') !== '/') { $path = config_item('cookie_path'); } if ($secure === FALSE && config_item('cookie_secure') === TRUE) { $secure = config_item('cookie_secure'); } if ($httponly === FALSE && config_item('cookie_httponly') !== FALSE) { $httponly = config_item('cookie_httponly'); } if ( ! is_numeric($expire)) { $expire = time() - 86500; } else { $expire = ($expire > 0) ? time() + $expire : 0; } setcookie($prefix.$name, $value, $expire, $path, $domain, $secure, $httponly); } // -------------------------------------------------------------------- /** * Fetch the IP Address * * Determines and validates the visitor's IP address. * * @return string IP address */ public function ip_address() { if ($this->ip_address !== FALSE) { return $this->ip_address; } $proxy_ips = config_item('proxy_ips'); if ( ! empty($proxy_ips) && ! is_array($proxy_ips)) { $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips)); } $this->ip_address = $this->server('REMOTE_ADDR'); if ($proxy_ips) { foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header) { if (($spoof = $this->server($header)) !== NULL) { // Some proxies typically list the whole chain of IP // addresses through which the client has reached us. // e.g. client_ip, proxy_ip1, proxy_ip2, etc. sscanf($spoof, '%[^,]', $spoof); if ( ! $this->valid_ip($spoof)) { $spoof = NULL; } else { break; } } } if ($spoof) { for ($i = 0, $c = count($proxy_ips); $i < $c; $i++) { // Check if we have an IP address or a subnet if (strpos($proxy_ips[$i], '/') === FALSE) { // An IP address (and not a subnet) is specified. // We can compare right away. if ($proxy_ips[$i] === $this->ip_address) { $this->ip_address = $spoof; break; } continue; } // We have a subnet ... now the heavy lifting begins isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.'; // If the proxy entry doesn't match the IP protocol - skip it if (strpos($proxy_ips[$i], $separator) === FALSE) { continue; } // Convert the REMOTE_ADDR IP address to binary, if needed if ( ! isset($ip, $sprintf)) { if ($separator === ':') { // Make sure we're have the "full" IPv6 format $ip = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($this->ip_address, ':')), $this->ip_address ) ); for ($j = 0; $j < 8; $j++) { $ip[$j] = intval($ip[$j], 16); } $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b'; } else { $ip = explode('.', $this->ip_address); $sprintf = '%08b%08b%08b%08b'; } $ip = vsprintf($sprintf, $ip); } // Split the netmask length off the network address sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen); // Again, an IPv6 address is most likely in a compressed form if ($separator === ':') { $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr)); for ($j = 0; $j < 8; $j++) { $netaddr[$i] = intval($netaddr[$j], 16); } } else { $netaddr = explode('.', $netaddr); } // Convert to binary and finally compare if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) { $this->ip_address = $spoof; break; } } } } if ( ! $this->valid_ip($this->ip_address)) { return $this->ip_address = '0.0.0.0'; } return $this->ip_address; } // -------------------------------------------------------------------- /** * Validate IP Address * * @param string $ip IP address * @param string $which IP protocol: 'ipv4' or 'ipv6' * @return bool */ public function valid_ip($ip, $which = '') { switch (strtolower($which)) { case 'ipv4': $which = FILTER_FLAG_IPV4; break; case 'ipv6': $which = FILTER_FLAG_IPV6; break; default: $which = NULL; break; } return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which); } // -------------------------------------------------------------------- /** * Fetch User Agent string * * @return string|null User Agent string or NULL if it doesn't exist */ public function user_agent($xss_clean = NULL) { return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean); } // -------------------------------------------------------------------- /** * Sanitize Globals * * Internal method serving for the following purposes: * * - Unsets $_GET data, if query strings are not enabled * - Cleans POST, COOKIE and SERVER data * - Standardizes newline characters to PHP_EOL * * @return void */ protected function _sanitize_globals() { // Is $_GET data allowed? If not we'll set the $_GET to an empty array if ($this->_allow_get_array === FALSE) { $_GET = array(); } elseif (is_array($_GET)) { foreach ($_GET as $key => $val) { $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); } } // Clean $_POST Data if (is_array($_POST)) { foreach ($_POST as $key => $val) { $_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); } } // Clean $_COOKIE Data if (is_array($_COOKIE)) { // Also get rid of specially treated cookies that might be set by a server // or silly application, that are of no use to a CI application anyway // but that when present will trip our 'Disallowed Key Characters' alarm // http://www.ietf.org/rfc/rfc2109.txt // note that the key names below are single quoted strings, and are not PHP variables unset( $_COOKIE['$Version'], $_COOKIE['$Path'], $_COOKIE['$Domain'] ); foreach ($_COOKIE as $key => $val) { if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE) { $_COOKIE[$cookie_key] = $this->_clean_input_data($val); } else { unset($_COOKIE[$key]); } } } // Sanitize PHP_SELF $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); log_message('debug', 'Global POST, GET and COOKIE data sanitized'); } // -------------------------------------------------------------------- /** * Clean Input Data * * Internal method that aids in escaping data and * standardizing newline characters to PHP_EOL. * * @param string|string[] $str Input string(s) * @return string */ protected function _clean_input_data($str) { if (is_array($str)) { $new_array = array(); foreach (array_keys($str) as $key) { $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($str[$key]); } return $new_array; } /* We strip slashes if magic quotes is on to keep things consistent NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and it will probably not exist in future versions at all. */ if ( ! is_php('5.4') && get_magic_quotes_gpc()) { $str = stripslashes($str); } // Clean UTF-8 if supported if (UTF8_ENABLED === TRUE) { $str = $this->uni->clean_string($str); } // Remove control characters $str = remove_invisible_characters($str, FALSE); // Standardize newlines if needed if ($this->_standardize_newlines === TRUE) { return preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $str); } return $str; } // -------------------------------------------------------------------- /** * Clean Keys * * Internal method that helps to prevent malicious users * from trying to exploit keys we make sure that keys are * only named with alpha-numeric text and a few other items. * * @param string $str Input string * @param bool $fatal Whether to terminate script exection * or to return FALSE if an invalid * key is encountered * @return string|bool */ protected function _clean_input_keys($str, $fatal = TRUE) { if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str)) { if ($fatal === TRUE) { return FALSE; } else { set_status_header(503); echo 'Disallowed Key Characters.'; exit(7); // EXIT_USER_INPUT } } // Clean UTF-8 if supported if (UTF8_ENABLED === TRUE) { return $this->uni->clean_string($str); } return $str; } // -------------------------------------------------------------------- /** * Request Headers * * @param bool $xss_clean Whether to apply XSS filtering * @return array */ public function request_headers($xss_clean = FALSE) { // If header is already defined, return it immediately if ( ! empty($this->headers)) { return $this->_fetch_from_array($this->headers, NULL, $xss_clean); } // In Apache, you can simply call apache_request_headers() if (function_exists('apache_request_headers')) { $this->headers = apache_request_headers(); } else { isset($_SERVER['CONTENT_TYPE']) && $this->headers['Content-Type'] = $_SERVER['CONTENT_TYPE']; foreach ($_SERVER as $key => $val) { if (sscanf($key, 'HTTP_%s', $header) === 1) { // take SOME_HEADER and turn it into Some-Header $header = str_replace('_', ' ', strtolower($header)); $header = str_replace(' ', '-', ucwords($header)); $this->headers[$header] = $_SERVER[$key]; } } } return $this->_fetch_from_array($this->headers, NULL, $xss_clean); } // -------------------------------------------------------------------- /** * Get Request Header * * Returns the value of a single member of the headers class member * * @param string $index Header name * @param bool $xss_clean Whether to apply XSS filtering * @return string|null The requested header on success or NULL on failure */ public function get_request_header($index, $xss_clean = FALSE) { static $headers; if ( ! isset($headers)) { empty($this->headers) && $this->request_headers(); foreach ($this->headers as $key => $value) { $headers[strtolower($key)] = $value; } } $index = strtolower($index); if ( ! isset($headers[$index])) { return NULL; } return ($xss_clean === TRUE) ? $this->security->xss_clean($headers[$index]) : $headers[$index]; } // -------------------------------------------------------------------- /** * Is AJAX request? * * Test to see if a request contains the HTTP_X_REQUESTED_WITH header. * * @return bool */ public function is_ajax_request() { return ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'); } // -------------------------------------------------------------------- /** * Is CLI request? * * Test to see if a request was made from the command line. * * @deprecated 3.0.0 Use is_cli() instead * @return bool */ public function is_cli_request() { return is_cli(); } // -------------------------------------------------------------------- /** * Get Request Method * * Return the request method * * @param bool $upper Whether to return in upper or lower case * (default: FALSE) * @return string */ public function method($upper = FALSE) { return ($upper) ? strtoupper($this->server('REQUEST_METHOD')) : strtolower($this->server('REQUEST_METHOD')); } // ------------------------------------------------------------------------ /** * Magic __get() * * Allows read access to protected properties * * @param string $name * @return mixed */ public function __get($name) { if ($name === 'raw_input_stream') { isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input'); return $this->_raw_input_stream; } elseif ($name === 'ip_address') { return $this->ip_address; } } } PK!YYLang.phpnu[load($value, $idiom, $return, $add_suffix, $alt_path); } return; } $langfile = str_replace('.php', '', $langfile); if ($add_suffix === TRUE) { $langfile = preg_replace('/_lang$/', '', $langfile).'_lang'; } $langfile .= '.php'; if (empty($idiom) OR ! preg_match('/^[a-z_-]+$/i', $idiom)) { $config =& get_config(); $idiom = empty($config['language']) ? 'english' : $config['language']; } if ($return === FALSE && isset($this->is_loaded[$langfile]) && $this->is_loaded[$langfile] === $idiom) { return; } // Load the base file, so any others found can override it $basepath = BASEPATH.'language/'.$idiom.'/'.$langfile; if (($found = file_exists($basepath)) === TRUE) { include($basepath); } // Do we have an alternative path to look in? if ($alt_path !== '') { $alt_path .= 'language/'.$idiom.'/'.$langfile; if (file_exists($alt_path)) { include($alt_path); $found = TRUE; } } else { foreach (get_instance()->load->get_package_paths(TRUE) as $package_path) { $package_path .= 'language/'.$idiom.'/'.$langfile; if ($basepath !== $package_path && file_exists($package_path)) { include($package_path); $found = TRUE; break; } } } if ($found !== TRUE) { show_error('Unable to load the requested language file: language/'.$idiom.'/'.$langfile); } if ( ! isset($lang) OR ! is_array($lang)) { log_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile); if ($return === TRUE) { return array(); } return; } if ($return === TRUE) { return $lang; } $this->is_loaded[$langfile] = $idiom; $this->language = array_merge($this->language, $lang); log_message('info', 'Language file loaded: language/'.$idiom.'/'.$langfile); return TRUE; } // -------------------------------------------------------------------- /** * Language line * * Fetches a single line of text from the language array * * @param string $line Language line key * @param bool $log_errors Whether to log an error message if the line is not found * @return string Translation */ public function line($line, $log_errors = TRUE) { $value = isset($this->language[$line]) ? $this->language[$line] : FALSE; // Because killer robots like unicorns! if ($value === FALSE && $log_errors === TRUE) { log_message('error', 'Could not find the language line "'.$line.'"'); } return $value; } } PK!).jjExceptions.phpnu[ 'Error', E_WARNING => 'Warning', E_PARSE => 'Parsing Error', E_NOTICE => 'Notice', E_CORE_ERROR => 'Core Error', E_CORE_WARNING => 'Core Warning', E_COMPILE_ERROR => 'Compile Error', E_COMPILE_WARNING => 'Compile Warning', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_STRICT => 'Runtime Notice' ); /** * Class constructor * * @return void */ public function __construct() { $this->ob_level = ob_get_level(); // Note: Do not log messages from this constructor. } // -------------------------------------------------------------------- /** * Exception Logger * * Logs PHP generated error messages * * @param int $severity Log level * @param string $message Error message * @param string $filepath File path * @param int $line Line number * @return void */ public function log_exception($severity, $message, $filepath, $line) { $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity; log_message('error', 'Severity: '.$severity.' --> '.$message.' '.$filepath.' '.$line); } // -------------------------------------------------------------------- /** * 404 Error Handler * * @uses CI_Exceptions::show_error() * * @param string $page Page URI * @param bool $log_error Whether to log the error * @return void */ public function show_404($page = '', $log_error = TRUE) { if (is_cli()) { $heading = 'Not Found'; $message = 'The controller/method pair you requested was not found.'; } else { $heading = '404 Page Not Found'; $message = 'The page you requested was not found.'; } // By default we log this, but allow a dev to skip it if ($log_error) { log_message('error', $heading.': '.$page); } echo $this->show_error($heading, $message, 'error_404', 404); exit(4); // EXIT_UNKNOWN_FILE } // -------------------------------------------------------------------- /** * General Error Page * * Takes an error message as input (either as a string or an array) * and displays it using the specified template. * * @param string $heading Page heading * @param string|string[] $message Error message * @param string $template Template name * @param int $status_code (default: 500) * * @return string Error page output */ public function show_error($heading, $message, $template = 'error_general', $status_code = 500) { $templates_path = config_item('error_views_path'); if (empty($templates_path)) { $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR; } if (is_cli()) { $message = "\t".(is_array($message) ? implode("\n\t", $message) : $message); $template = 'cli'.DIRECTORY_SEPARATOR.$template; } else { set_status_header($status_code); $message = '

'.(is_array($message) ? implode('

', $message) : $message).'

'; $template = 'html'.DIRECTORY_SEPARATOR.$template; } if (ob_get_level() > $this->ob_level + 1) { ob_end_flush(); } ob_start(); include($templates_path.$template.'.php'); $buffer = ob_get_contents(); ob_end_clean(); return $buffer; } // -------------------------------------------------------------------- public function show_exception($exception) { $templates_path = config_item('error_views_path'); if (empty($templates_path)) { $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR; } $message = $exception->getMessage(); if (empty($message)) { $message = '(null)'; } if (is_cli()) { $templates_path .= 'cli'.DIRECTORY_SEPARATOR; } else { $templates_path .= 'html'.DIRECTORY_SEPARATOR; } if (ob_get_level() > $this->ob_level + 1) { ob_end_flush(); } ob_start(); include($templates_path.'error_exception.php'); $buffer = ob_get_contents(); ob_end_clean(); echo $buffer; } // -------------------------------------------------------------------- /** * Native PHP error handler * * @param int $severity Error level * @param string $message Error message * @param string $filepath File path * @param int $line Line number * @return string Error page output */ public function show_php_error($severity, $message, $filepath, $line) { $templates_path = config_item('error_views_path'); if (empty($templates_path)) { $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR; } $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity; // For safety reasons we don't show the full file path in non-CLI requests if ( ! is_cli()) { $filepath = str_replace('\\', '/', $filepath); if (FALSE !== strpos($filepath, '/')) { $x = explode('/', $filepath); $filepath = $x[count($x)-2].'/'.end($x); } $template = 'html'.DIRECTORY_SEPARATOR.'error_php'; } else { $template = 'cli'.DIRECTORY_SEPARATOR.'error_php'; } if (ob_get_level() > $this->ob_level + 1) { ob_end_flush(); } ob_start(); include($templates_path.$template.'.php'); $buffer = ob_get_contents(); ob_end_clean(); echo $buffer; } } PK!.: Benchmark.phpnu[marker[$name] = microtime(TRUE); } // -------------------------------------------------------------------- /** * Elapsed time * * Calculates the time difference between two marked points. * * If the first parameter is empty this function instead returns the * {elapsed_time} pseudo-variable. This permits the full system * execution time to be shown in a template. The output class will * swap the real value for this variable. * * @param string $point1 A particular marked point * @param string $point2 A particular marked point * @param int $decimals Number of decimal places * * @return string Calculated elapsed time on success, * an '{elapsed_string}' if $point1 is empty * or an empty string if $point1 is not found. */ public function elapsed_time($point1 = '', $point2 = '', $decimals = 4) { if ($point1 === '') { return '{elapsed_time}'; } if ( ! isset($this->marker[$point1])) { return ''; } if ( ! isset($this->marker[$point2])) { $this->marker[$point2] = microtime(TRUE); } return number_format($this->marker[$point2] - $this->marker[$point1], $decimals); } // -------------------------------------------------------------------- /** * Memory Usage * * Simply returns the {memory_usage} marker. * * This permits it to be put it anywhere in a template * without the memory being calculated until the end. * The output class will swap the real value for this variable. * * @return string '{memory_usage}' */ public function memory_usage() { return '{memory_usage}'; } } PK!\6  Model.phpnu[$key; } } PK!gIUU Common.phpnu[='); } return $_is_php[$version]; } } // ------------------------------------------------------------------------ if ( ! function_exists('is_really_writable')) { /** * Tests for file writability * * is_writable() returns TRUE on Windows servers when you really can't write to * the file, based on the read-only attribute. is_writable() is also unreliable * on Unix servers if safe_mode is on. * * @link https://bugs.php.net/bug.php?id=54709 * @param string * @return bool */ function is_really_writable($file) { // If we're on a Unix server with safe_mode off we call is_writable if (DIRECTORY_SEPARATOR === '/' && (is_php('5.4') OR ! ini_get('safe_mode'))) { return is_writable($file); } /* For Windows servers and safe_mode "on" installations we'll actually * write a file then read it. Bah... */ if (is_dir($file)) { $file = rtrim($file, '/').'/'.md5(mt_rand()); if (($fp = @fopen($file, 'ab')) === FALSE) { return FALSE; } fclose($fp); @chmod($file, 0777); @unlink($file); return TRUE; } elseif ( ! is_file($file) OR ($fp = @fopen($file, 'ab')) === FALSE) { return FALSE; } fclose($fp); return TRUE; } } // ------------------------------------------------------------------------ if ( ! function_exists('load_class')) { /** * Class registry * * This function acts as a singleton. If the requested class does not * exist it is instantiated and set to a static variable. If it has * previously been instantiated the variable is returned. * * @param string the class name being requested * @param string the directory where the class should be found * @param string an optional argument to pass to the class constructor * @return object */ function &load_class($class, $directory = 'libraries', $param = NULL) { static $_classes = array(); // Does the class exist? If so, we're done... if (isset($_classes[$class])) { return $_classes[$class]; } $name = FALSE; // Look for the class first in the local application/libraries folder // then in the native system/libraries folder foreach (array(APPPATH, BASEPATH) as $path) { if (file_exists($path.$directory.'/'.$class.'.php')) { $name = 'CI_'.$class; if (class_exists($name, FALSE) === FALSE) { require_once($path.$directory.'/'.$class.'.php'); } break; } } // Is the request a class extension? If so we load it too if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php')) { $name = config_item('subclass_prefix').$class; if (class_exists($name, FALSE) === FALSE) { require_once(APPPATH.$directory.'/'.$name.'.php'); } } // Did we find the class? if ($name === FALSE) { // Note: We use exit() rather than show_error() in order to avoid a // self-referencing loop with the Exceptions class set_status_header(503); echo 'Unable to locate the specified class: '.$class.'.php'; exit(5); // EXIT_UNK_CLASS } // Keep track of what we just loaded is_loaded($class); $_classes[$class] = isset($param) ? new $name($param) : new $name(); return $_classes[$class]; } } // -------------------------------------------------------------------- if ( ! function_exists('is_loaded')) { /** * Keeps track of which libraries have been loaded. This function is * called by the load_class() function above * * @param string * @return array */ function &is_loaded($class = '') { static $_is_loaded = array(); if ($class !== '') { $_is_loaded[strtolower($class)] = $class; } return $_is_loaded; } } // ------------------------------------------------------------------------ if ( ! function_exists('get_config')) { /** * Loads the main config.php file * * This function lets us grab the config file even if the Config class * hasn't been instantiated yet * * @param array * @return array */ function &get_config(Array $replace = array()) { static $config; if (empty($config)) { $file_path = APPPATH.'config/config.php'; $found = FALSE; if (file_exists($file_path)) { $found = TRUE; require($file_path); } // Is the config file in the environment folder? if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php')) { require($file_path); } elseif ( ! $found) { set_status_header(503); echo 'The configuration file does not exist.'; exit(3); // EXIT_CONFIG } // Does the $config array exist in the file? if ( ! isset($config) OR ! is_array($config)) { set_status_header(503); echo 'Your config file does not appear to be formatted correctly.'; exit(3); // EXIT_CONFIG } } // Are any values being dynamically added or replaced? foreach ($replace as $key => $val) { $config[$key] = $val; } return $config; } } // ------------------------------------------------------------------------ if ( ! function_exists('config_item')) { /** * Returns the specified config item * * @param string * @return mixed */ function config_item($item) { static $_config; if (empty($_config)) { // references cannot be directly assigned to static variables, so we use an array $_config[0] =& get_config(); } return isset($_config[0][$item]) ? $_config[0][$item] : NULL; } } // ------------------------------------------------------------------------ if ( ! function_exists('get_mimes')) { /** * Returns the MIME types array from config/mimes.php * * @return array */ function &get_mimes() { static $_mimes; if (empty($_mimes)) { if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php')) { $_mimes = include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'); } elseif (file_exists(APPPATH.'config/mimes.php')) { $_mimes = include(APPPATH.'config/mimes.php'); } else { $_mimes = array(); } } return $_mimes; } } // ------------------------------------------------------------------------ if ( ! function_exists('is_https')) { /** * Is HTTPS? * * Determines if the application is accessed via an encrypted * (HTTPS) connection. * * @return bool */ function is_https() { if ( ! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') { return TRUE; } elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') { return TRUE; } elseif ( ! empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') { return TRUE; } return FALSE; } } // ------------------------------------------------------------------------ if ( ! function_exists('is_cli')) { /** * Is CLI? * * Test to see if a request was made from the command line. * * @return bool */ function is_cli() { return (PHP_SAPI === 'cli' OR defined('STDIN')); } } // ------------------------------------------------------------------------ if ( ! function_exists('show_error')) { /** * Error Handler * * This function lets us invoke the exception class and * display errors using the standard error template located * in application/views/errors/error_general.php * This function will send the error page directly to the * browser and exit. * * @param string * @param int * @param string * @return void */ function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered') { $status_code = abs($status_code); if ($status_code < 100) { $exit_status = $status_code + 9; // 9 is EXIT__AUTO_MIN if ($exit_status > 125) // 125 is EXIT__AUTO_MAX { $exit_status = 1; // EXIT_ERROR } $status_code = 500; } else { $exit_status = 1; // EXIT_ERROR } $_error =& load_class('Exceptions', 'core'); echo $_error->show_error($heading, $message, 'error_general', $status_code); exit($exit_status); } } // ------------------------------------------------------------------------ if ( ! function_exists('show_404')) { /** * 404 Page Handler * * This function is similar to the show_error() function above * However, instead of the standard error template it displays * 404 errors. * * @param string * @param bool * @return void */ function show_404($page = '', $log_error = TRUE) { $_error =& load_class('Exceptions', 'core'); $_error->show_404($page, $log_error); exit(4); // EXIT_UNKNOWN_FILE } } // ------------------------------------------------------------------------ if ( ! function_exists('log_message')) { /** * Error Logging Interface * * We use this as a simple mechanism to access the logging * class and send messages to be logged. * * @param string the error level: 'error', 'debug' or 'info' * @param string the error message * @return void */ function log_message($level, $message) { static $_log; if ($_log === NULL) { // references cannot be directly assigned to static variables, so we use an array $_log[0] =& load_class('Log', 'core'); } $_log[0]->write_log($level, $message); } } // ------------------------------------------------------------------------ if ( ! function_exists('set_status_header')) { /** * Set HTTP Status Header * * @param int the status code * @param string * @return void */ function set_status_header($code = 200, $text = '') { if (is_cli()) { return; } if (empty($code) OR ! is_numeric($code)) { show_error('Status codes must be numeric', 500); } if (empty($text)) { is_int($code) OR $code = (int) $code; $stati = array( 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 422 => 'Unprocessable Entity', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 511 => 'Network Authentication Required', ); if (isset($stati[$code])) { $text = $stati[$code]; } else { show_error('No status text available. Please check your status code number or supply your own message text.', 500); } } if (strpos(PHP_SAPI, 'cgi') === 0) { header('Status: '.$code.' '.$text, TRUE); } else { $server_protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; header($server_protocol.' '.$code.' '.$text, TRUE, $code); } } } // -------------------------------------------------------------------- if ( ! function_exists('_error_handler')) { /** * Error Handler * * This is the custom error handler that is declared at the (relative) * top of CodeIgniter.php. The main reason we use this is to permit * PHP errors to be logged in our own log files since the user may * not have access to server logs. Since this function effectively * intercepts PHP errors, however, we also need to display errors * based on the current error_reporting level. * We do that with the use of a PHP error template. * * @param int $severity * @param string $message * @param string $filepath * @param int $line * @return void */ function _error_handler($severity, $message, $filepath, $line) { $is_error = (((E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $severity) === $severity); // When an error occurred, set the status header to '500 Internal Server Error' // to indicate to the client something went wrong. // This can't be done within the $_error->show_php_error method because // it is only called when the display_errors flag is set (which isn't usually // the case in a production environment) or when errors are ignored because // they are above the error_reporting threshold. if ($is_error) { set_status_header(500); } // Should we ignore the error? We'll get the current error_reporting // level and add its bits with the severity bits to find out. if (($severity & error_reporting()) !== $severity) { return; } $_error =& load_class('Exceptions', 'core'); $_error->log_exception($severity, $message, $filepath, $line); // Should we display the error? if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors'))) { $_error->show_php_error($severity, $message, $filepath, $line); } // If the error is fatal, the execution of the script should be stopped because // errors can't be recovered from. Halting the script conforms with PHP's // default error handling. See http://www.php.net/manual/en/errorfunc.constants.php if ($is_error) { exit(1); // EXIT_ERROR } } } // ------------------------------------------------------------------------ if ( ! function_exists('_exception_handler')) { /** * Exception Handler * * Sends uncaught exceptions to the logger and displays them * only if display_errors is On so that they don't show up in * production environments. * * @param Exception $exception * @return void */ function _exception_handler($exception) { $_error =& load_class('Exceptions', 'core'); $_error->log_exception('error', 'Exception: '.$exception->getMessage(), $exception->getFile(), $exception->getLine()); is_cli() OR set_status_header(500); // Should we display the error? if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors'))) { $_error->show_exception($exception); } exit(1); // EXIT_ERROR } } // ------------------------------------------------------------------------ if ( ! function_exists('_shutdown_handler')) { /** * Shutdown Handler * * This is the shutdown handler that is declared at the top * of CodeIgniter.php. The main reason we use this is to simulate * a complete custom exception handler. * * E_STRICT is purposively neglected because such events may have * been caught. Duplication or none? None is preferred for now. * * @link http://insomanic.me.uk/post/229851073/php-trick-catching-fatal-errors-e-error-with-a * @return void */ function _shutdown_handler() { $last_error = error_get_last(); if (isset($last_error) && ($last_error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING))) { _error_handler($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); } } } // -------------------------------------------------------------------- if ( ! function_exists('remove_invisible_characters')) { /** * Remove Invisible Characters * * This prevents sandwiching null characters * between ascii characters, like Java\0script. * * @param string * @param bool * @return string */ function remove_invisible_characters($str, $url_encoded = TRUE) { $non_displayables = array(); // every control character except newline (dec 10), // carriage return (dec 13) and horizontal tab (dec 09) if ($url_encoded) { $non_displayables[] = '/%0[0-8bcef]/i'; // url encoded 00-08, 11, 12, 14, 15 $non_displayables[] = '/%1[0-9a-f]/i'; // url encoded 16-31 } $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 do { $str = preg_replace($non_displayables, '', $str, -1, $count); } while ($count); return $str; } } // ------------------------------------------------------------------------ if ( ! function_exists('html_escape')) { /** * Returns HTML escaped variable. * * @param mixed $var The input string or array of strings to be escaped. * @param bool $double_encode $double_encode set to FALSE prevents escaping twice. * @return mixed The escaped string or array of strings as a result. */ function html_escape($var, $double_encode = TRUE) { if (empty($var)) { return $var; } if (is_array($var)) { foreach (array_keys($var) as $key) { $var[$key] = html_escape($var[$key], $double_encode); } return $var; } return htmlspecialchars($var, ENT_QUOTES, config_item('charset'), $double_encode); } } // ------------------------------------------------------------------------ if ( ! function_exists('_stringify_attributes')) { /** * Stringify attributes for use in HTML tags. * * Helper function used to convert a string, array, or object * of attributes to a string. * * @param mixed string, array, object * @param bool * @return string */ function _stringify_attributes($attributes, $js = FALSE) { $atts = NULL; if (empty($attributes)) { return $atts; } if (is_string($attributes)) { return ' '.$attributes; } $attributes = (array) $attributes; foreach ($attributes as $key => $val) { $atts .= ($js) ? $key.'='.$val.',' : ' '.$key.'="'.$val.'"'; } return rtrim($atts, ','); } } // ------------------------------------------------------------------------ if ( ! function_exists('function_usable')) { /** * Function usable * * Executes a function_exists() check, and if the Suhosin PHP * extension is loaded - checks whether the function that is * checked might be disabled in there as well. * * This is useful as function_exists() will return FALSE for * functions disabled via the *disable_functions* php.ini * setting, but not for *suhosin.executor.func.blacklist* and * *suhosin.executor.disable_eval*. These settings will just * terminate script execution if a disabled function is executed. * * The above described behavior turned out to be a bug in Suhosin, * but even though a fix was commited for 0.9.34 on 2012-02-12, * that version is yet to be released. This function will therefore * be just temporary, but would probably be kept for a few years. * * @link http://www.hardened-php.net/suhosin/ * @param string $function_name Function to check for * @return bool TRUE if the function exists and is safe to call, * FALSE otherwise. */ function function_usable($function_name) { static $_suhosin_func_blacklist; if (function_exists($function_name)) { if ( ! isset($_suhosin_func_blacklist)) { $_suhosin_func_blacklist = extension_loaded('suhosin') ? explode(',', trim(ini_get('suhosin.executor.func.blacklist'))) : array(); } return ! in_array($function_name, $_suhosin_func_blacklist, TRUE); } return FALSE; } } PK! kk Hooks.phpnu[item('enable_hooks') === FALSE) { return; } // Grab the "hooks" definition file. if (file_exists(APPPATH.'config/hooks.php')) { include(APPPATH.'config/hooks.php'); } if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/hooks.php')) { include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'); } // If there are no hooks, we're done. if ( ! isset($hook) OR ! is_array($hook)) { return; } $this->hooks =& $hook; $this->enabled = TRUE; } // -------------------------------------------------------------------- /** * Call Hook * * Calls a particular hook. Called by CodeIgniter.php. * * @uses CI_Hooks::_run_hook() * * @param string $which Hook name * @return bool TRUE on success or FALSE on failure */ public function call_hook($which = '') { if ( ! $this->enabled OR ! isset($this->hooks[$which])) { return FALSE; } if (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function'])) { foreach ($this->hooks[$which] as $val) { $this->_run_hook($val); } } else { $this->_run_hook($this->hooks[$which]); } return TRUE; } // -------------------------------------------------------------------- /** * Run Hook * * Runs a particular hook * * @param array $data Hook details * @return bool TRUE on success or FALSE on failure */ protected function _run_hook($data) { // Closures/lambda functions and array($object, 'method') callables if (is_callable($data)) { is_array($data) ? $data[0]->{$data[1]}() : $data(); return TRUE; } elseif ( ! is_array($data)) { return FALSE; } // ----------------------------------- // Safety - Prevents run-away loops // ----------------------------------- // If the script being called happens to have the same // hook call within it a loop can happen if ($this->_in_progress === TRUE) { return; } // ----------------------------------- // Set file path // ----------------------------------- if ( ! isset($data['filepath'], $data['filename'])) { return FALSE; } $filepath = APPPATH.$data['filepath'].'/'.$data['filename']; if ( ! file_exists($filepath)) { return FALSE; } // Determine and class and/or function names $class = empty($data['class']) ? FALSE : $data['class']; $function = empty($data['function']) ? FALSE : $data['function']; $params = isset($data['params']) ? $data['params'] : ''; if (empty($function)) { return FALSE; } // Set the _in_progress flag $this->_in_progress = TRUE; // Call the requested class and/or function if ($class !== FALSE) { // The object is stored? if (isset($this->_objects[$class])) { if (method_exists($this->_objects[$class], $function)) { $this->_objects[$class]->$function($params); } else { return $this->_in_progress = FALSE; } } else { class_exists($class, FALSE) OR require_once($filepath); if ( ! class_exists($class, FALSE) OR ! method_exists($class, $function)) { return $this->_in_progress = FALSE; } // Store the object and execute the method $this->_objects[$class] = new $class(); $this->_objects[$class]->$function($params); } } else { function_exists($function) OR require_once($filepath); if ( ! function_exists($function)) { return $this->_in_progress = FALSE; } $function($params); } $this->_in_progress = FALSE; return TRUE; } } PK!-8%y7y7rdata.hnu[#ifndef RBIMPL_RDATA_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RDATA_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Defines struct ::RData. */ #include "ruby/internal/config.h" #ifdef STDC_HEADERS # include #endif #include "ruby/internal/attr/deprecated.h" #include "ruby/internal/attr/warning.h" #include "ruby/internal/cast.h" #include "ruby/internal/core/rbasic.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/fl_type.h" #include "ruby/internal/value.h" #include "ruby/internal/value_type.h" #include "ruby/defines.h" /** @cond INTERNAL_MACRO */ #ifdef RUBY_UNTYPED_DATA_WARNING # /* Take that. */ #elif defined(RUBY_EXPORT) # define RUBY_UNTYPED_DATA_WARNING 1 #else # define RUBY_UNTYPED_DATA_WARNING 0 #endif #define RBIMPL_DATA_FUNC(f) RBIMPL_CAST((void (*)(void *))(f)) #define RBIMPL_ATTRSET_UNTYPED_DATA_FUNC() \ RBIMPL_ATTR_WARNING(("untyped Data is unsafe; use TypedData instead")) \ RBIMPL_ATTR_DEPRECATED(("by TypedData")) #define RBIMPL_MACRO_SELECT(x, y) x ## y #define RUBY_MACRO_SELECT(x, y) RBIMPL_MACRO_SELECT(x, y) /** @endcond */ /** * Convenient casting macro. * * @param obj An object, which is in fact an ::RData. * @return The passed object casted to ::RData. */ #define RDATA(obj) RBIMPL_CAST((struct RData *)(obj)) /** * Convenient getter macro. * * @param obj An object, which is in fact an ::RData. * @return The passed object's ::RData::data field. */ #define DATA_PTR(obj) RDATA(obj)->data /** * This is a value you can set to ::RData::dfree. Setting this means the data * was allocated using ::ruby_xmalloc() (or variants), and shall be freed using * ::ruby_xfree(). * * @warning Do not use this if you want to use system malloc, because the * system and Ruby might or might not share the same malloc * implementation. */ #define RUBY_DEFAULT_FREE RBIMPL_DATA_FUNC(-1) /** * This is a value you can set to ::RData::dfree. Setting this means the data * is managed by someone else, like, statically allocated. Of course you are * on your own then. */ #define RUBY_NEVER_FREE RBIMPL_DATA_FUNC(0) /** * @private * * @deprecated This macro once was a thing in the old days, but makes no sense * any longer today. Exists here for backwards compatibility * only. You can safely forget about it. */ #define RUBY_UNTYPED_DATA_FUNC(f) f RBIMPL_ATTRSET_UNTYPED_DATA_FUNC() /* #define RUBY_DATA_FUNC(func) ((void (*)(void*))(func)) */ /** * This is the type of callbacks registered to ::RData. The argument is the * `data` field. */ typedef void (*RUBY_DATA_FUNC)(void*); /** * @deprecated * * Old "untyped" user data. It has roughly the same usage as struct * ::RTypedData, but lacked several features such as support for compaction GC. * Use of this struct is not recommended any longer. If it is dead necessary, * please inform the core devs about your usage. * * @internal * * @shyouhei tried to add RBIMPL_ATTR_DEPRECATED for this type but that yielded * too many warnings in the core. Maybe we want to retry later... Just add * deprecated document for now. */ struct RData { /** Basic part, including flags and class. */ struct RBasic basic; /** * This function is called when the object is experiencing GC marks. If it * contains references to other Ruby objects, you need to mark them also. * Otherwise GC will smash your data. * * @see rb_gc_mark() * @warning This is called during GC runs. Object allocations are * impossible at that moment (that is why GC runs). */ RUBY_DATA_FUNC dmark; /** * This function is called when the object is no longer used. You need to * do whatever necessary to avoid memory leaks. * * @warning This is called during GC runs. Object allocations are * impossible at that moment (that is why GC runs). */ RUBY_DATA_FUNC dfree; /** Pointer to the actual C level struct that you want to wrap. */ void *data; }; RBIMPL_SYMBOL_EXPORT_BEGIN() /** * This is the primitive way to wrap an existing C struct into ::RData. * * @param[in] klass Ruby level class of the returning object. * @param[in] datap Pointer to the target C struct. * @param[in] dmark Mark function. * @param[in] dfree Free function. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return An allocated object that wraps `datap`. */ VALUE rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree); /** * Identical to rb_data_object_wrap(), except it allocates a new data region * internally instead of taking an existing one. The allocation is done using * ruby_calloc(). Hence it makes no sense to pass anything other than * ::RUBY_DEFAULT_FREE to the last argument. * * @param[in] klass Ruby level class of the returning object. * @param[in] size Requested size of memory to allocate. * @param[in] dmark Mark function. * @param[in] dfree Free function. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return An allocated object that wraps a new `size` byte region. */ VALUE rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree); /** * @private * Documented in include/ruby/internal/globals.h */ RUBY_EXTERN VALUE rb_cObject; RBIMPL_SYMBOL_EXPORT_END() /** * Converts sval, a pointer to your struct, into a Ruby object. * * @param klass A ruby level class. * @param mark Mark function. * @param free Free function. * @param sval A pointer to your struct. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return A created Ruby object. */ #define Data_Wrap_Struct(klass, mark, free, sval) \ rb_data_object_wrap( \ (klass), \ (sval), \ RBIMPL_DATA_FUNC(mark), \ RBIMPL_DATA_FUNC(free)) /** * @private * * This is an implementation detail of #Data_Make_Struct. People don't use it * directly. * * @param result Variable name of created Ruby object. * @param klass Ruby level class of the object. * @param type Type name of the C struct. * @param size Size of the C struct. * @param mark Mark function. * @param free Free function. * @param sval Variable name of created C struct. */ #define Data_Make_Struct0(result, klass, type, size, mark, free, sval) \ VALUE result = rb_data_object_zalloc( \ (klass), \ (size), \ RBIMPL_DATA_FUNC(mark), \ RBIMPL_DATA_FUNC(free)); \ (sval) = RBIMPL_CAST((type *)DATA_PTR(result)); \ RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval)) /** * Identical to #Data_Wrap_Struct, except it allocates a new data region * internally instead of taking an existing one. The allocation is done using * ruby_calloc(). Hence it makes no sense to pass anything other than * ::RUBY_DEFAULT_FREE to the `free` argument. * * @param klass Ruby level class of the returning object. * @param type Type name of the C struct. * @param mark Mark function. * @param free Free function. * @param sval Variable name of created C struct. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return A created Ruby object. */ #ifdef HAVE_STMT_AND_DECL_IN_EXPR #define Data_Make_Struct(klass, type, mark, free, sval) \ RB_GNUC_EXTENSION({ \ Data_Make_Struct0( \ data_struct_obj, \ klass, \ type, \ sizeof(type), \ mark, \ free, \ sval); \ data_struct_obj; \ }) #else #define Data_Make_Struct(klass, type, mark, free, sval) \ rb_data_object_make( \ (klass), \ RBIMPL_DATA_FUNC(mark), \ RBIMPL_DATA_FUNC(free), \ RBIMPL_CAST((void **)&(sval)), \ sizeof(type)) #endif /** * Obtains a C struct from inside of a wrapper Ruby object. * * @param obj An instance of ::RData. * @param type Type name of the C struct. * @param sval Variable name of obtained C struct. * @return Unwrapped C struct that `obj` holds. */ #define Data_Get_Struct(obj, type, sval) \ ((sval) = RBIMPL_CAST((type*)rb_data_object_get(obj))) RBIMPL_ATTRSET_UNTYPED_DATA_FUNC() /** * @private * * This is an implementation detail of rb_data_object_wrap(). People don't use * it directly. * * @param[in] klass Ruby level class of the returning object. * @param[in] ptr Pointer to the target C struct. * @param[in] mark Mark function. * @param[in] free Free function. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return An allocated object that wraps `datap`. */ static inline VALUE rb_data_object_wrap_warning(VALUE klass, void *ptr, RUBY_DATA_FUNC mark, RUBY_DATA_FUNC free) { return rb_data_object_wrap(klass, ptr, mark, free); } /** * @private * * This is an implementation detail of #Data_Get_Struct. People don't use it * directly. * * @param[in] obj An instance of ::RData. * @return Unwrapped C struct that `obj` holds. */ static inline void * rb_data_object_get(VALUE obj) { Check_Type(obj, RUBY_T_DATA); return DATA_PTR(obj); } RBIMPL_ATTRSET_UNTYPED_DATA_FUNC() /** * @private * * This is an implementation detail of #Data_Get_Struct. People don't use it * directly. * * @param[in] obj An instance of ::RData. * @return Unwrapped C struct that `obj` holds. */ static inline void * rb_data_object_get_warning(VALUE obj) { return rb_data_object_get(obj); } #if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P) # define rb_data_object_wrap_warning(klass, ptr, mark, free) \ RB_GNUC_EXTENSION( \ __builtin_choose_expr( \ __builtin_constant_p(klass) && !(klass), \ rb_data_object_wrap(klass, ptr, mark, free), \ (rb_data_object_wrap_warning)(klass, ptr, mark, free))) #endif /** * This is an implementation detail of #Data_Make_Struct. People don't use it * directly. * * @param[in] klass Ruby level class of the returning object. * @param[in] mark_func Mark function. * @param[in] free_func Free function. * @param[in] datap Variable of created C struct. * @param[in] size Requested size of allocation. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return A created Ruby object. * @post `*datap` holds the created C struct. */ static inline VALUE rb_data_object_make(VALUE klass, RUBY_DATA_FUNC mark_func, RUBY_DATA_FUNC free_func, void **datap, size_t size) { Data_Make_Struct0(result, klass, void, size, mark_func, free_func, *datap); return result; } RBIMPL_ATTR_DEPRECATED(("by: rb_data_object_wrap")) /** @deprecated This function was renamed to rb_data_object_wrap(). */ static inline VALUE rb_data_object_alloc(VALUE klass, void *data, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree) { return rb_data_object_wrap(klass, data, dmark, dfree); } /** @cond INTERNAL_MACRO */ #define rb_data_object_wrap_0 rb_data_object_wrap #define rb_data_object_wrap_1 rb_data_object_wrap_warning #define rb_data_object_wrap_2 rb_data_object_wrap_ /* Used here vvvv */ #define rb_data_object_wrap RUBY_MACRO_SELECT(rb_data_object_wrap_2, RUBY_UNTYPED_DATA_WARNING) #define rb_data_object_get_0 rb_data_object_get #define rb_data_object_get_1 rb_data_object_get_warning #define rb_data_object_get_2 rb_data_object_get_ /* Used here vvvv */ #define rb_data_object_get RUBY_MACRO_SELECT(rb_data_object_get_2, RUBY_UNTYPED_DATA_WARNING) #define rb_data_object_make_0 rb_data_object_make #define rb_data_object_make_1 rb_data_object_make_warning #define rb_data_object_make_2 rb_data_object_make_ /* Used here vvvv */ #define rb_data_object_make RUBY_MACRO_SELECT(rb_data_object_make_2, RUBY_UNTYPED_DATA_WARNING) /** @endcond */ #endif /* RBIMPL_RDATA_H */ PK!]_ss robject.hnu[#ifndef RBIMPL_ROBJECT_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_ROBJECT_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Defines struct ::RObject. */ #include "ruby/internal/config.h" #ifdef HAVE_STDINT_H # include #endif #include "ruby/internal/attr/artificial.h" #include "ruby/internal/attr/deprecated.h" #include "ruby/internal/attr/pure.h" #include "ruby/internal/cast.h" #include "ruby/internal/fl_type.h" #include "ruby/internal/value.h" #include "ruby/internal/value_type.h" /** * Convenient casting macro. * * @param obj An object, which is in fact an ::RObject. * @return The passed object casted to ::RObject. */ #define ROBJECT(obj) RBIMPL_CAST((struct RObject *)(obj)) /** @cond INTERNAL_MACRO */ #define ROBJECT_EMBED_LEN_MAX ROBJECT_EMBED_LEN_MAX #define ROBJECT_EMBED ROBJECT_EMBED #define ROBJECT_IV_CAPACITY ROBJECT_IV_CAPACITY #define ROBJECT_IVPTR ROBJECT_IVPTR /** @endcond */ /** * @private * * Bits that you can set to ::RBasic::flags. */ enum ruby_robject_flags { /** * This flag has something to do with memory footprint. If the object is * "small" enough, ruby tries to be creative to abuse padding bits of * struct ::RObject for storing instance variables. This flag denotes that * situation. * * @warning This bit has to be considered read-only. Setting/clearing * this bit without corresponding fix up must cause immediate * SEGV. Also, internal structures of an object change * dynamically and transparently throughout of its lifetime. * Don't assume it being persistent. * * @internal * * 3rd parties must not be aware that there even is more than one way to * store instance variables. Might better be hidden. */ ROBJECT_EMBED = RUBY_FL_USER1 }; #if !USE_RVARGC /** * This is an enum because GDB wants it (rather than a macro). People need not * bother. */ enum ruby_robject_consts { /** Max possible number of instance variables that can be embedded. */ ROBJECT_EMBED_LEN_MAX = RBIMPL_EMBED_LEN_MAX_OF(VALUE) }; #endif struct st_table; /** * Ruby's ordinal objects. Unless otherwise special cased, all predefined and * user-defined classes share this struct to hold their instances. */ struct RObject { /** Basic part, including flags and class. */ struct RBasic basic; /** Object's specific fields. */ union { /** * Object that use separated memory region for instance variables use * this pattern. */ struct { /** Pointer to a C array that holds instance variables. */ VALUE *ivptr; /** * This is a table that holds instance variable name to index * mapping. Used when accessing instance variables using names. * * @internal * * This is a shortcut for `RCLASS_IV_INDEX_TBL(rb_obj_class(obj))`. */ struct rb_id_table *iv_index_tbl; } heap; #if USE_RVARGC /* Embedded instance variables. When an object is small enough, it * uses this area to store the instance variables. * * This is a length 1 array because: * 1. GCC has a bug that does not optimize C flexible array members * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452) * 2. Zero length arrays are not supported by all compilers */ VALUE ary[1]; #else /** * Embedded instance variables. When an object is small enough, it * uses this area to store the instance variables. */ VALUE ary[ROBJECT_EMBED_LEN_MAX]; #endif } as; }; /* Offsets for YJIT */ #ifndef __cplusplus static const int32_t ROBJECT_OFFSET_AS_HEAP_IVPTR = offsetof(struct RObject, as.heap.ivptr); static const int32_t ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL = offsetof(struct RObject, as.heap.iv_index_tbl); static const int32_t ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary); #endif RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Queries the instance variables. * * @param[in] obj Object in question. * @return Its instance variables, in C array. * @pre `obj` must be an instance of ::RObject. * * @internal * * @shyouhei finds no reason for this to be visible from extension libraries. */ static inline VALUE * ROBJECT_IVPTR(VALUE obj) { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); struct RObject *const ptr = ROBJECT(obj); if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) { return ptr->as.ary; } else { return ptr->as.heap.ivptr; } } #endif /* RBIMPL_ROBJECT_H */ PK!SVrbasic.hnu[#ifndef RBIMPL_RBASIC_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RBASIC_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Defines struct ::RBasic. */ #include "ruby/internal/attr/artificial.h" #include "ruby/internal/attr/constexpr.h" #include "ruby/internal/attr/forceinline.h" #include "ruby/internal/attr/noalias.h" #include "ruby/internal/attr/pure.h" #include "ruby/internal/cast.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/special_consts.h" #include "ruby/internal/value.h" #include "ruby/assert.h" /** * Convenient casting macro. * * @param obj Arbitrary Ruby object. * @return The passed object casted to ::RBasic. */ #define RBASIC(obj) RBIMPL_CAST((struct RBasic *)(obj)) /** @cond INTERNAL_MACRO */ #define RBASIC_CLASS RBASIC_CLASS #define RBIMPL_RVALUE_EMBED_LEN_MAX 3 #define RVALUE_EMBED_LEN_MAX RVALUE_EMBED_LEN_MAX #define RBIMPL_EMBED_LEN_MAX_OF(T) \ RBIMPL_CAST((int)(sizeof(VALUE[RBIMPL_RVALUE_EMBED_LEN_MAX]) / (sizeof(T)))) /** @endcond */ /** * This is an enum because GDB wants it (rather than a macro). People need not * bother. */ enum ruby_rvalue_flags { /** Max possible number of objects that can be embedded. */ RVALUE_EMBED_LEN_MAX = RBIMPL_RVALUE_EMBED_LEN_MAX }; /** * Ruby's object's, base components. Every single ruby objects have them in * common. */ struct RUBY_ALIGNAS(SIZEOF_VALUE) RBasic { /** * Per-object flags. Each ruby objects have their own characteristics * apart from their classes. For instance whether an object is frozen or * not is not controlled by its class. This is where such properties are * stored. * * @see enum ::ruby_fl_type * * @note This is ::VALUE rather than an enum for alignment purpose. Back * in the 1990s there were no such thing like `_Alignas` in C. */ VALUE flags; /** * Class of an object. Every object has its class. Also, everything is an * object in Ruby. This means classes are also objects. Classes have * their own classes, classes of classes have their classes, too ... and * it recursively continues forever. * * Also note the `const` qualifier. In ruby an object cannot "change" its * class. */ const VALUE klass; #ifdef __cplusplus public: RBIMPL_ATTR_CONSTEXPR(CXX11) RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_FORCEINLINE() RBIMPL_ATTR_NOALIAS() /** * We need to define this explicit constructor because the field `klass` is * const-qualified above, which effectively defines the implicit default * constructor as "deleted" (as of C++11) -- No way but to define one by * ourselves. */ RBasic() : flags(RBIMPL_VALUE_NULL), klass(RBIMPL_VALUE_NULL) { } # define RBASIC_INIT RBasic() #else # define RBASIC_INIT {RBIMPL_VALUE_NULL} #endif }; RBIMPL_SYMBOL_EXPORT_BEGIN() /** * Make the object invisible from Ruby code. * * It is useful to let Ruby's GC manage your internal data structure -- The * object keeps being managed by GC, but `ObjectSpace.each_object` never yields * the object. * * Note that the object also lose a way to call a method on it. * * @param[out] obj A Ruby object. * @return The passed object. * @post The object is destructively modified to be invisible. * @see rb_obj_reveal */ VALUE rb_obj_hide(VALUE obj); /** * Make a hidden object visible again. * * It is the caller's responsibility to pass the right `klass` which `obj` * originally used to belong to. * * @param[out] obj A Ruby object. * @param[in] klass Class of `obj`. * @return Passed `obj`. * @pre `obj` was previously hidden. * @post `obj`'s class is `klass`. * @see rb_obj_hide */ VALUE rb_obj_reveal(VALUE obj, VALUE klass); /* do not use this API to change klass information */ RBIMPL_SYMBOL_EXPORT_END() RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Queries the class of an object. * * @param[in] obj An object. * @return Its class. */ static inline VALUE RBASIC_CLASS(VALUE obj) { RBIMPL_ASSERT_OR_ASSUME(! RB_SPECIAL_CONST_P(obj)); return RBASIC(obj)->klass; } #endif /* RBIMPL_RBASIC_H */ PK!Y rclass.hnu[#ifndef RBIMPL_RCLASS_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RCLASS_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Routines to manipulate struct RClass. * @note The struct RClass itself is opaque. */ #include "ruby/internal/dllexport.h" #include "ruby/internal/value.h" #include "ruby/internal/cast.h" /** @cond INTERNAL_MACRO */ #define RMODULE_IS_REFINEMENT RMODULE_IS_REFINEMENT /** @endcond */ /** * Convenient casting macro. * * @param obj An object, which is in fact an RClass. * @return The passed object casted to RClass. */ #define RCLASS(obj) RBIMPL_CAST((struct RClass *)(obj)) /** @alias{RCLASS} */ #define RMODULE RCLASS /** @alias{rb_class_get_superclass} */ #define RCLASS_SUPER rb_class_get_superclass /** * @private * * Bits that you can set to ::RBasic::flags. * * @internal * * Why is it here, given RClass itself is not? */ enum ruby_rmodule_flags { /** * This flag has something to do with refinements. A module created using * rb_mod_refine() has this flag set. This is the bit which controls * difference between normal inclusion versus refinements. */ RMODULE_IS_REFINEMENT = RUBY_FL_USER3 }; struct RClass; /* Opaque, declared here for RCLASS() macro. */ RBIMPL_SYMBOL_EXPORT_BEGIN() /** * Returns the superclass of a class. * @param[in] klass An object of RClass. * @retval RUBY_Qfalse `klass` has no super class. * @retval otherwise Raw superclass of `klass` * @see rb_class_superclass * * ### Q&A ### * * - Q: How can a class have no super class? * * - A: `klass` could be a module. Or it could be ::rb_cBasicObject. * * - Q: What do you mean by "raw" superclass? * * - A: This is a really good question. The answer is that this function * returns something different from what you would normally expect. On * occasions ruby inserts hidden classes in a hierarchy of class * inheritance behind-the-scene. Such classes are called "iclass"es and * distinguished using ::RUBY_T_ICLASS in C level. They are truly * transparent from Ruby level but can be accessed from C, by using this * API. */ VALUE rb_class_get_superclass(VALUE klass); RBIMPL_SYMBOL_EXPORT_END() #endif /* RBIMPL_RCLASS_H */ PK!JjpAArmatch.hnu[#ifndef RBIMPL_RMATCH_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RMATCH_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Defines struct ::RMatch. */ #include "ruby/internal/attr/artificial.h" #include "ruby/internal/attr/pure.h" #include "ruby/internal/cast.h" #include "ruby/internal/core/rbasic.h" #include "ruby/internal/value.h" #include "ruby/internal/value_type.h" #include "ruby/assert.h" /** * Convenient casting macro. * * @param obj An object, which is in fact an ::RMatch. * @return The passed object casted to ::RMatch. */ #define RMATCH(obj) RBIMPL_CAST((struct RMatch *)(obj)) /** @cond INTERNAL_MACRO */ #define RMATCH_REGS RMATCH_REGS /** @endcond */ struct re_patter_buffer; /* a.k.a. OnigRegexType, defined in onigmo.h */ struct re_registers; /* Also in onigmo.h */ /** * @old{re_pattern_buffer} * * @internal * * @shyouhei wonders: is anyone actively using this typedef ...? */ typedef struct re_pattern_buffer Regexp; /** * Represents the region of a capture group. This is basically for caching * purpose. re_registers have similar concepts (`beg` and `end`) but they are * in `ptrdiff_t*`. In order for us to implement `MatchData#offset` that info * has to be converted to offset integers. This is the struct to hold such * things. * * @internal * * But why on earth it has to be visible from extension libraries? */ struct rmatch_offset { long beg; /**< Beginning of a group. */ long end; /**< End of a group. */ }; /** Represents a match. */ struct rmatch { /** * "Registers" of a match. This is a quasi-opaque struct that holds * execution result of a match. Roughly resembles `&~`. */ struct re_registers regs; /** Capture group offsets, in C array. */ struct rmatch_offset *char_offset; /** Number of ::rmatch_offset that ::rmatch::char_offset holds. */ int char_offset_num_allocated; }; /** * Regular expression execution context. When a regular expression "matches" * to a string, it generates capture groups etc. This struct holds that info. * Visible from Ruby as an instance of `MatchData`. * * @note There is no way for extension libraries to manually generate this * struct except by actually exercising the match operation of a regular * expression. */ struct RMatch { /** Basic part, including flags and class. */ struct RBasic basic; /** * The target string that the match was made against. */ VALUE str; /** * The result of this match. */ struct rmatch *rmatch; /** * The expression of this match. */ VALUE regexp; /* RRegexp */ }; RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Queries the raw ::re_registers. * * @param[in] match A match object * @pre `match` must be of ::RMatch. * @return Its execution result. * @note Good. So you are aware of the fact that it could return NULL. * Yes. It actually does. This is a really bizarre thing. The * situation is about `String#gsub` and its family. They take * strings as arguments, like `"foo".sub("bar", "baz")`. On such * situations, in order to optimise memory allocations, these * methods do not involve regular expressions at all. They just * sequentially scan the receiver. Okay. The story begins here. * Even when they do not kick our regexp engine, there must be * backref objects e.g. `$&`. But how? You know what? Ruby fakes * them. It allocates an empty ::RMatch and behaves as if there * were execution contexts. In reality there weren't. No * ::re_registers are allocated then. There is no way for this * function but to return NULL for those fake ::RMatch. This is * the reason for the nullability of this function. */ static inline struct re_registers * RMATCH_REGS(VALUE match) { RBIMPL_ASSERT_TYPE(match, RUBY_T_MATCH); RBIMPL_ASSERT_OR_ASSUME(RMATCH(match)->rmatch != NULL); return &RMATCH(match)->rmatch->regs; } #endif /* RBIMPL_RMATCH_H */ PK!}"NN rstring.hnu[#ifndef RBIMPL_RSTRING_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RSTRING_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Defines struct ::RString. */ #include "ruby/internal/config.h" #include "ruby/internal/arithmetic/long.h" #include "ruby/internal/attr/artificial.h" #include "ruby/internal/attr/pure.h" #include "ruby/internal/cast.h" #include "ruby/internal/core/rbasic.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/fl_type.h" #include "ruby/internal/value_type.h" #include "ruby/internal/warning_push.h" #include "ruby/assert.h" /** * Convenient casting macro. * * @param obj An object, which is in fact an ::RString. * @return The passed object casted to ::RString. */ #define RSTRING(obj) RBIMPL_CAST((struct RString *)(obj)) /** @cond INTERNAL_MACRO */ #define RSTRING_NOEMBED RSTRING_NOEMBED #if !USE_RVARGC #define RSTRING_EMBED_LEN_MASK RSTRING_EMBED_LEN_MASK #define RSTRING_EMBED_LEN_SHIFT RSTRING_EMBED_LEN_SHIFT #define RSTRING_EMBED_LEN_MAX RSTRING_EMBED_LEN_MAX #endif #define RSTRING_FSTR RSTRING_FSTR #define RSTRING_EMBED_LEN RSTRING_EMBED_LEN #define RSTRING_LEN RSTRING_LEN #define RSTRING_LENINT RSTRING_LENINT #define RSTRING_PTR RSTRING_PTR #define RSTRING_END RSTRING_END /** @endcond */ /** * @name Conversion of Ruby strings into C's * * @{ */ /** * Ensures that the parameter object is a String. This is done by calling its * `to_str` method. * * @param[in,out] v Arbitrary Ruby object. * @exception rb_eTypeError No implicit conversion defined. * @post `v` is a String. */ #define StringValue(v) rb_string_value(&(v)) /** * Identical to #StringValue, except it returns a `char*`. * * @param[in,out] v Arbitrary Ruby object. * @exception rb_eTypeError No implicit conversion defined. * @return Converted Ruby string's backend C string. * @post `v` is a String. */ #define StringValuePtr(v) rb_string_value_ptr(&(v)) /** * Identical to #StringValuePtr, except it additionally checks for the contents * for viability as a C string. Ruby can accept wider range of contents as * strings, compared to C. This function is to check that. * * @param[in,out] v Arbitrary Ruby object. * @exception rb_eTypeError No implicit conversion defined. * @exception rb_eArgError String is not C-compatible. * @return Converted Ruby string's backend C string. * @post `v` is a String. */ #define StringValueCStr(v) rb_string_value_cstr(&(v)) /** * @private * * @deprecated This macro once was a thing in the old days, but makes no sense * any longer today. Exists here for backwards compatibility * only. You can safely forget about it. */ #define SafeStringValue(v) StringValue(v) /** * Identical to #StringValue, except it additionally converts the string's * encoding to default external encoding. Ruby has a concept called encodings. * A string can have different encoding than the environment expects. Someone * has to make sure its contents be converted to something suitable. This is * that routine. Call it when necessary. * * @param[in,out] v Arbitrary Ruby object. * @exception rb_eTypeError No implicit conversion defined. * @return Converted Ruby string's backend C string. * @post `v` is a String. * * @internal * * Not sure but it seems this macro does not raise on encoding * incompatibilities? Doesn't sound right to @shyouhei. */ #define ExportStringValue(v) do { \ StringValue(v); \ (v) = rb_str_export(v); \ } while (0) /** @} */ /** * @private * * Bits that you can set to ::RBasic::flags. * * @warning These enums are not the only bits we use for strings. * * @internal * * Actually all bits through FL_USER1 to FL_USER19 are used for strings. Why * only this tiny part of them are made public here? @shyouhei can find no * reason. */ enum ruby_rstring_flags { /** * This flag has something to do with memory footprint. If the string is * short enough, ruby tries to be creative to abuse padding bits of struct * ::RString for storing contents. If this flag is set that string does * _not_ do that, to resort to good old fashioned external allocation * strategy instead. * * @warning This bit has to be considered read-only. Setting/clearing * this bit without corresponding fix up must cause immediate * SEGV. Also, internal structures of a string change * dynamically and transparently throughout of its lifetime. * Don't assume it being persistent. * * @internal * * 3rd parties must not be aware that there even is more than one way to * store a string. Might better be hidden. */ RSTRING_NOEMBED = RUBY_FL_USER1, #if !USE_RVARGC /** * When a string employs embedded strategy (see ::RSTRING_NOEMBED), these * bits are used to store the number of bytes actually filled into * ::RString::ary. * * @internal * * 3rd parties must not be aware that there even is more than one way to * store a string. Might better be hidden. */ RSTRING_EMBED_LEN_MASK = RUBY_FL_USER2 | RUBY_FL_USER3 | RUBY_FL_USER4 | RUBY_FL_USER5 | RUBY_FL_USER6, #endif /* Actually, string encodings are also encoded into the flags, using * remaining bits.*/ /** * This flag has something to do with infamous "f"string. What is a * fstring? Well it is a special subkind of strings that is immutable, * deduped globally, and managed by our GC. It is much like a Symbol (in * fact Symbols are dynamic these days and are backended using fstrings). * This concept has been silently introduced at some point in 2.x era. * Since then it gained wider acceptance in the core. But extension * libraries could not know that until very recently. Strings of this flag * live in a special Limbo deep inside of the interpreter. Never try to * manipulate it by hand. * * @internal * * Fstrings are not the only variant strings that we implement today. * Other things are behind-the-scene. This is the only one that is visible * from extension library. There is no clear reason why it has to be. * Given there are more "polite" ways to create fstrings, it seems this bit * need not be exposed to extension libraries. Might better be hidden. */ RSTRING_FSTR = RUBY_FL_USER17 }; #if !USE_RVARGC /** * This is an enum because GDB wants it (rather than a macro). People need not * bother. */ enum ruby_rstring_consts { /** Where ::RSTRING_EMBED_LEN_MASK resides. */ RSTRING_EMBED_LEN_SHIFT = RUBY_FL_USHIFT + 2, /** Max possible number of characters that can be embedded. */ RSTRING_EMBED_LEN_MAX = RBIMPL_EMBED_LEN_MAX_OF(char) - 1 }; #endif /** * Ruby's String. A string in ruby conceptually has these information: * * - Encoding of the string. * - Length of the string. * - Contents of the string. * * It is worth noting that a string is _not_ an array of characters in ruby. * It has never been. In 1.x a string was an array of integers. Since 2.x a * string is no longer an array of anything. A string is a string -- just like * a Time is not an integer. */ struct RString { /** Basic part, including flags and class. */ struct RBasic basic; /** String's specific fields. */ union { /** * Strings that use separated memory region for contents use this * pattern. */ struct { /** * Length of the string, not including terminating NUL character. * * @note This is in bytes. */ long len; /** * Pointer to the contents of the string. In the old days each * string had dedicated memory regions. That is no longer true * today, but there still are strings of such properties. This * field could be used to point such things. */ char *ptr; /** Auxiliary info. */ union { /** * Capacity of `*ptr`. A continuous memory region of at least * `capa` bytes is expected to exist at `*ptr`. This can be * bigger than `len`. */ long capa; /** * Parent of the string. Nowadays strings can share their * contents each other, constructing gigantic nest of objects. * This situation is called "shared", and this is the field to * control such properties. */ VALUE shared; } aux; } heap; /** Embedded contents. */ struct { #if USE_RVARGC long len; /* This is a length 1 array because: * 1. GCC has a bug that does not optimize C flexible array members * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452) * 2. Zero length arrays are not supported by all compilers */ char ary[1]; #else /** * When a string is short enough, it uses this area to store the * contents themselves. This was impractical in the 20th century, * but these days 64 bit machines can typically hold 24 bytes here. * Could be sufficiently large. In this case the length is encoded * into the flags. */ char ary[RSTRING_EMBED_LEN_MAX + 1]; #endif } embed; } as; }; RBIMPL_SYMBOL_EXPORT_BEGIN() /** * Identical to rb_check_string_type(), except it raises exceptions in case of * conversion failures. * * @param[in] obj Target object. * @exception rb_eTypeError No implicit conversion to String. * @return Return value of `obj.to_str`. * @see rb_io_get_io * @see rb_ary_to_ary */ VALUE rb_str_to_str(VALUE obj); /** * Identical to rb_str_to_str(), except it fills the passed pointer with the * converted object. * * @param[in,out] ptr Pointer to a variable of target object. * @exception rb_eTypeError No implicit conversion to String. * @return Return value of `obj.to_str`. * @post `*ptr` is the return value. */ VALUE rb_string_value(volatile VALUE *ptr); /** * Identical to rb_str_to_str(), except it returns the converted string's * backend memory region. * * @param[in,out] ptr Pointer to a variable of target object. * @exception rb_eTypeError No implicit conversion to String. * @post `*ptr` is the return value of `obj.to_str`. * @return Pointer to the contents of the return value. */ char *rb_string_value_ptr(volatile VALUE *ptr); /** * Identical to rb_string_value_ptr(), except it additionally checks for the * contents for viability as a C string. Ruby can accept wider range of * contents as strings, compared to C. This function is to check that. * * @param[in,out] ptr Pointer to a variable of target object. * @exception rb_eTypeError No implicit conversion to String. * @exception rb_eArgError String is not C-compatible. * @post `*ptr` is the return value of `obj.to_str`. * @return Pointer to the contents of the return value. */ char *rb_string_value_cstr(volatile VALUE *ptr); /** * Identical to rb_str_to_str(), except it additionally converts the string * into default external encoding. Ruby has a concept called encodings. A * string can have different encoding than the environment expects. Someone * has to make sure its contents be converted to something suitable. This is * that routine. Call it when necessary. * * @param[in] obj Target object. * @exception rb_eTypeError No implicit conversion to String. * @return Converted ruby string of default external encoding. */ VALUE rb_str_export(VALUE obj); /** * Identical to rb_str_export(), except it converts into the locale encoding * instead. * * @param[in] obj Target object. * @exception rb_eTypeError No implicit conversion to String. * @return Converted ruby string of locale encoding. */ VALUE rb_str_export_locale(VALUE obj); RBIMPL_ATTR_ERROR(("rb_check_safe_str() and Check_SafeStr() are obsolete; use StringValue() instead")) /** * @private * * @deprecated This function once was a thing in the old days, but makes no * sense any longer today. Exists here for backwards * compatibility only. You can safely forget about it. */ void rb_check_safe_str(VALUE); /** * @private * * @deprecated This macro once was a thing in the old days, but makes no sense * any longer today. Exists here for backwards compatibility * only. You can safely forget about it. */ #define Check_SafeStr(v) rb_check_safe_str(RBIMPL_CAST((VALUE)(v))) /** * @private * * Prints diagnostic message to stderr when RSTRING_PTR or RSTRING_END * is NULL. * * @param[in] func The function name where encountered NULL pointer. */ void rb_debug_rstring_null_ptr(const char *func); RBIMPL_SYMBOL_EXPORT_END() RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Queries the length of the string. * * @param[in] str String in question. * @return Its length, in bytes. * @pre `str` must be an instance of ::RString, and must has its * ::RSTRING_NOEMBED flag off. * * @internal * * This was a macro before. It was inevitable to be public, since macros are * global constructs. But should it be forever? Now that it is a function, * @shyouhei thinks it could just be eliminated, hidden into implementation * details. */ static inline long RSTRING_EMBED_LEN(VALUE str) { RBIMPL_ASSERT_TYPE(str, RUBY_T_STRING); RBIMPL_ASSERT_OR_ASSUME(! RB_FL_ANY_RAW(str, RSTRING_NOEMBED)); #if USE_RVARGC long f = RSTRING(str)->as.embed.len; return f; #else VALUE f = RBASIC(str)->flags; f &= RSTRING_EMBED_LEN_MASK; f >>= RSTRING_EMBED_LEN_SHIFT; return RBIMPL_CAST((long)f); #endif } RBIMPL_WARNING_PUSH() #if RBIMPL_COMPILER_IS(Intel) RBIMPL_WARNING_IGNORED(413) #endif RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * @private * * "Expands" an embedded string into an ordinal one. This is a function that * returns aggregated type. The returned struct always has its `as.heap.len` * an `as.heap.ptr` fields set appropriately. * * This is an implementation detail that 3rd parties should never bother. */ static inline struct RString rbimpl_rstring_getmem(VALUE str) { RBIMPL_ASSERT_TYPE(str, RUBY_T_STRING); if (RB_FL_ANY_RAW(str, RSTRING_NOEMBED)) { return *RSTRING(str); } else { /* Expecting compilers to optimize this on-stack struct away. */ struct RString retval = {RBASIC_INIT}; retval.as.heap.len = RSTRING_EMBED_LEN(str); retval.as.heap.ptr = RSTRING(str)->as.embed.ary; return retval; } } RBIMPL_WARNING_POP() RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Queries the length of the string. * * @param[in] str String in question. * @return Its length, in bytes. * @pre `str` must be an instance of ::RString. */ static inline long RSTRING_LEN(VALUE str) { return rbimpl_rstring_getmem(str).as.heap.len; } RBIMPL_ATTR_ARTIFICIAL() /** * Queries the contents pointer of the string. * * @param[in] str String in question. * @return Pointer to its contents. * @pre `str` must be an instance of ::RString. */ static inline char * RSTRING_PTR(VALUE str) { char *ptr = rbimpl_rstring_getmem(str).as.heap.ptr; if (RB_UNLIKELY(! ptr)) { /* :BEWARE: @shyouhei thinks that currently, there are rooms for this * function to return NULL. In the 20th century that was a pointless * concern. However struct RString can hold fake strings nowadays. It * seems no check against NULL are exercised around handling of them * (one of such usages is located in marshal.c, which scares * @shyouhei). Better check here for maximum safety. * * Also, this is not rb_warn() because RSTRING_PTR() can be called * during GC (see what obj_info() does). rb_warn() needs to allocate * Ruby objects. That is not possible at this moment. */ rb_debug_rstring_null_ptr("RSTRING_PTR"); } return ptr; } RBIMPL_ATTR_ARTIFICIAL() /** * Queries the end of the contents pointer of the string. * * @param[in] str String in question. * @return Pointer to its end of contents. * @pre `str` must be an instance of ::RString. */ static inline char * RSTRING_END(VALUE str) { struct RString buf = rbimpl_rstring_getmem(str); if (RB_UNLIKELY(! buf.as.heap.ptr)) { /* Ditto. */ rb_debug_rstring_null_ptr("RSTRING_END"); } return &buf.as.heap.ptr[buf.as.heap.len]; } RBIMPL_ATTR_ARTIFICIAL() /** * Identical to RSTRING_LEN(), except it differs for the return type. * * @param[in] str String in question. * @exception rb_eRangeError Too long. * @return Its length, in bytes. * @pre `str` must be an instance of ::RString. * * @internal * * This API seems redundant but has actual usages. */ static inline int RSTRING_LENINT(VALUE str) { return rb_long2int(RSTRING_LEN(str)); } /** * Convenient macro to obtain the contents and length at once. * * @param str String in question. * @param ptrvar Variable where its contents is stored. * @param lenvar Variable where its length is stored. */ #ifdef HAVE_STMT_AND_DECL_IN_EXPR # define RSTRING_GETMEM(str, ptrvar, lenvar) \ __extension__ ({ \ struct RString rbimpl_str = rbimpl_rstring_getmem(str); \ (ptrvar) = rbimpl_str.as.heap.ptr; \ (lenvar) = rbimpl_str.as.heap.len; \ }) #else # define RSTRING_GETMEM(str, ptrvar, lenvar) \ ((ptrvar) = RSTRING_PTR(str), \ (lenvar) = RSTRING_LEN(str)) #endif /* HAVE_STMT_AND_DECL_IN_EXPR */ #endif /* RBIMPL_RSTRING_H */ PK!H 66rhash.hnu[#ifndef RBIMPL_RHASH_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RHASH_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Routines to manipulate struct RHash. * @note The struct RHash itself is opaque. */ #include "ruby/internal/config.h" #ifdef STDC_HEADERS # include #endif #include "ruby/internal/dllexport.h" #include "ruby/internal/value.h" #if !defined RUBY_EXPORT && !defined RUBY_NO_OLD_COMPATIBILITY # include "ruby/backward.h" #endif /** * Retrieves the internal table. * * @param[in] h An instance of RHash. * @pre `h` must be of ::RUBY_T_HASH. * @return A struct st_table which has the contents of this hash. * @note Nowadays as Ruby evolved over ages, RHash has multiple backend * storage engines. `h`'s backend is not guaranteed to be a * st_table. This function creates one when necessary. */ #define RHASH_TBL(h) rb_hash_tbl(h, __FILE__, __LINE__) /** * @private * * @deprecated This macro once was a thing in the old days, but makes no sense * any longer today. Exists here for backwards compatibility * only. You can safely forget about it. * * @internal * * Declaration of rb_hash_iter_lev() is at include/ruby/backward.h. */ #define RHASH_ITER_LEV(h) rb_hash_iter_lev(h) /** * @private * * @deprecated This macro once was a thing in the old days, but makes no sense * any longer today. Exists here for backwards compatibility * only. You can safely forget about it. * * @internal * * Declaration of rb_hash_ifnone() is at include/ruby/backward.h. */ #define RHASH_IFNONE(h) rb_hash_ifnone(h) /** * Queries the size of the hash. Size here means the number of keys that the * hash stores. * * @param[in] h An instance of RHash. * @pre `h` must be of ::RUBY_T_HASH. * @return The size of the hash. */ #define RHASH_SIZE(h) rb_hash_size_num(h) /** * Checks if the hash is empty. * * @param[in] h An instance of RHash. * @pre `h` must be of ::RUBY_T_HASH. * @retval true It is. * @retval false It isn't. */ #define RHASH_EMPTY_P(h) (RHASH_SIZE(h) == 0) /** * Destructively updates the default value of the hash. * * @param[out] h An instance of RHash. * @param[in] ifnone Arbitrary default value. * @pre `h` must be of ::RUBY_T_HASH. * * @internal * * But why you can set this, given rb_hash_ifnone() doesn't exist? */ #define RHASH_SET_IFNONE(h, ifnone) rb_hash_set_ifnone((VALUE)h, ifnone) struct st_table; /* in ruby/st.h */ RBIMPL_SYMBOL_EXPORT_BEGIN() /** * This is the implementation detail of #RHASH_SIZE. People don't call this * directly. * * @param[in] hash An instance of RHash. * @pre `hash` must be of ::RUBY_T_HASH. * @return The size of the hash. */ size_t rb_hash_size_num(VALUE hash); /** * This is the implementation detail of #RHASH_TBL. People don't call this * directly. * * @param[in] hash An instance of RHash. * @param[in] file The `__FILE__`. * @param[in] line The `__LINE__`. * @pre `hash` must be of ::RUBY_T_HASH. * @return Table that has the contents of the hash. */ struct st_table *rb_hash_tbl(VALUE hash, const char *file, int line); /** * This is the implementation detail of #RHASH_SET_IFNONE. People don't call * this directly. * * @param[out] hash An instance of RHash. * @param[in] ifnone Arbitrary default value. * @pre `hash` must be of ::RUBY_T_HASH. */ VALUE rb_hash_set_ifnone(VALUE hash, VALUE ifnone); RBIMPL_SYMBOL_EXPORT_END() #endif /* RBIMPL_RHASH_H */ PK!5} rbignum.hnu[#ifndef RBIMPL_RBIGNUM_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RBIGNUM_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Routines to manipulate struct RBignum. * @note The struct RBignum itself is opaque. */ #include "ruby/internal/dllexport.h" #include "ruby/internal/value.h" #include "ruby/internal/value_type.h" #include "ruby/internal/stdbool.h" #define RBIGNUM_SIGN rb_big_sign /**< @alias{rb_big_sign} */ /** @cond INTERNAL_MACRO */ #define RBIGNUM_POSITIVE_P RBIGNUM_POSITIVE_P #define RBIGNUM_NEGATIVE_P RBIGNUM_NEGATIVE_P /** @endcond */ RBIMPL_SYMBOL_EXPORT_BEGIN() /** * The "sign" of a bignum. * * @param[in] num An object of RBignum. * @retval 1 It is greater than or equal to zero. * @retval 0 It is less than zero. * * @internal * * Implementation wise, unlike fixnums (which are 2's complement), bignums are * signed magnitude system. Theoretically it could be possible to have * negative zero instances. But in reality there is no way to create such * thing. Nobody ever needed that kind of insanity. */ int rb_big_sign(VALUE num); RBIMPL_SYMBOL_EXPORT_END() /** * Checks if the bignum is positive. * @param[in] b An object of RBignum. * @retval false `b` is less than zero. * @retval true Otherwise. */ static inline bool RBIGNUM_POSITIVE_P(VALUE b) { RBIMPL_ASSERT_TYPE(b, RUBY_T_BIGNUM); return RBIGNUM_SIGN(b); } /** * Checks if the bignum is negative. * @param[in] b An object of RBignum. * @retval true `b` is less than zero. * @retval false Otherwise. */ static inline bool RBIGNUM_NEGATIVE_P(VALUE b) { RBIMPL_ASSERT_TYPE(b, RUBY_T_BIGNUM); return ! RBIGNUM_POSITIVE_P(b); } #endif /* RBIMPL_RBIGNUM_H */ PK!kk rstruct.hnu[#ifndef RBIMPL_RSTRUCT_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RSTRUCT_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Routines to manipulate struct RStruct. * @note The struct RStruct itself is opaque. */ #include "ruby/internal/attr/artificial.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/value.h" #include "ruby/internal/value_type.h" #include "ruby/internal/arithmetic/long.h" #include "ruby/internal/arithmetic/int.h" #if !defined RUBY_EXPORT && !defined RUBY_NO_OLD_COMPATIBILITY # include "ruby/backward.h" #endif /** * @private * * @deprecated This macro once was a thing in the old days, but makes no sense * any longer today. Exists here for backwards compatibility * only. You can safely forget about it. * * @internal * * Declaration of rb_struct_ptr() is at include/ruby/backward.h. */ #define RSTRUCT_PTR(st) rb_struct_ptr(st) /** @cond INTERNAL_MACRO */ #define RSTRUCT_LEN RSTRUCT_LEN #define RSTRUCT_SET RSTRUCT_SET #define RSTRUCT_GET RSTRUCT_GET /** @endcond */ RBIMPL_SYMBOL_EXPORT_BEGIN() /** * Returns the number of struct members. * * @param[in] st An instance of RStruct. * @return The number of members of `st`. * @pre `st` must be of ::RUBY_T_STRUCT. */ VALUE rb_struct_size(VALUE st); /** * Resembles `Struct#[]`. * * @param[in] st An instance of RStruct. * @param[in] k Index a.k.a. key of the struct. * @exception rb_eTypeError `k` is neither Numeric, Symbol, nor String. * @exception rb_eIndexError Numerical index out of range. * @exception rb_eNameError No such key. * @return The member stored at `k` in `st`. * @pre `st` must be of ::RUBY_T_STRUCT. */ VALUE rb_struct_aref(VALUE st, VALUE k); /** * Resembles `Struct#[]=`. * * @param[out] st An instance of RStruct. * @param[in] k Index a.k.a. key of the struct. * @param[in] v Value to store. * @exception rb_eTypeError `k` is neither Numeric, Symbol, nor String. * @exception rb_eIndexError Numerical index out of range. * @exception rb_eNameError No such key. * @return Passed `v`. * @pre `st` must be of ::RUBY_T_STRUCT. * @post `v` is stored at `k` in `st`. */ VALUE rb_struct_aset(VALUE st, VALUE k, VALUE v); RBIMPL_SYMBOL_EXPORT_END() RBIMPL_ATTR_ARTIFICIAL() /** @copydoc rb_struct_size() */ static inline long RSTRUCT_LEN(VALUE st) { RBIMPL_ASSERT_TYPE(st, RUBY_T_STRUCT); return RB_NUM2LONG(rb_struct_size(st)); } RBIMPL_ATTR_ARTIFICIAL() /** @copydoc rb_struct_aset() */ static inline VALUE RSTRUCT_SET(VALUE st, int k, VALUE v) { RBIMPL_ASSERT_TYPE(st, RUBY_T_STRUCT); return rb_struct_aset(st, INT2NUM(k), (v)); } RBIMPL_ATTR_ARTIFICIAL() /** @copydoc rb_struct_aref() */ static inline VALUE RSTRUCT_GET(VALUE st, int k) { RBIMPL_ASSERT_TYPE(st, RUBY_T_STRUCT); return rb_struct_aref(st, INT2NUM(k)); } #endif /* RBIMPL_RSTRUCT_H */ PK!SmC rregexp.hnu[#ifndef RBIMPL_RREGEXP_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RREGEXP_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Defines struct ::RRegexp. */ #include "ruby/internal/attr/artificial.h" #include "ruby/internal/attr/pure.h" #include "ruby/internal/cast.h" #include "ruby/internal/core/rbasic.h" #include "ruby/internal/core/rstring.h" #include "ruby/internal/value.h" #include "ruby/internal/value_type.h" /** * Convenient casting macro. * * @param obj An object, which is in fact an ::RRegexp. * @return The passed object casted to ::RRegexp. */ #define RREGEXP(obj) RBIMPL_CAST((struct RRegexp *)(obj)) /** * Convenient accessor macro. * * @param obj An object, which is in fact an ::RRegexp. * @return The passed object's pattern buffer. */ #define RREGEXP_PTR(obj) (RREGEXP(obj)->ptr) /** @cond INTERNAL_MACRO */ #define RREGEXP_SRC RREGEXP_SRC #define RREGEXP_SRC_PTR RREGEXP_SRC_PTR #define RREGEXP_SRC_LEN RREGEXP_SRC_LEN #define RREGEXP_SRC_END RREGEXP_SRC_END /** @endcond */ struct re_patter_buffer; /* a.k.a. OnigRegexType, defined in onigmo.h */ /** * Ruby's regular expression. A regexp is compiled into its own intermediate * representation. This one holds that info. Regexp "match" operation then * executes that IR. */ struct RRegexp { /** Basic part, including flags and class. */ struct RBasic basic; /** * The pattern buffer. This is a quasi-opaque struct that holds compiled * intermediate representation of the regular expression. * * @note Compilation of a regexp could be delayed until actual match. */ struct re_pattern_buffer *ptr; /** Source code of this expression. */ const VALUE src; /** * Reference count. A regexp match can take extraordinarily long time to * run. Ruby's regular expression is heavily extended and not a regular * language any longer; runs in NP-time in practice. Now, Ruby also has * threads and GVL. In order to prevent long GVL lockup, our regexp engine * can release it on occasions. This means that multiple threads can touch * a regular expressions at once. That itself is okay. But their cleanup * phase shall wait for all the concurrent runs, to prevent use-after-free * situation. This field is used to count such threads that are executing * this particular pattern buffer. * * @warning Of course, touching this field from extension libraries causes * catastrophic effects. Just leave it. */ unsigned long usecnt; }; RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Convenient getter function. * * @param[in] rexp The regular expression in question. * @return The source code of the regular expression. * @pre `rexp` must be of ::RRegexp. */ static inline VALUE RREGEXP_SRC(VALUE rexp) { RBIMPL_ASSERT_TYPE(rexp, RUBY_T_REGEXP); VALUE ret = RREGEXP(rexp)->src; RBIMPL_ASSERT_TYPE(ret, RUBY_T_STRING); return ret; } RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Convenient getter function. * * @param[in] rexp The regular expression in question. * @return The source code of the regular expression, in C's string. * @pre `rexp` must be of ::RRegexp. * * @internal * * It seems nobody uses this function in the wild. Subject to hide? */ static inline char * RREGEXP_SRC_PTR(VALUE rexp) { return RSTRING_PTR(RREGEXP_SRC(rexp)); } RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Convenient getter function. * * @param[in] rexp The regular expression in question. * @return The length of the source code of the regular expression. * @pre `rexp` must be of ::RRegexp. * * @internal * * It seems nobody uses this function in the wild. Subject to hide? */ static inline long RREGEXP_SRC_LEN(VALUE rexp) { return RSTRING_LEN(RREGEXP_SRC(rexp)); } RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Convenient getter function. * * @param[in] rexp The regular expression in question. * @return The end of the source code of the regular expression. * @pre `rexp` must be of ::RRegexp. * * @internal * * It seems nobody uses this function in the wild. Subject to hide? */ static inline char * RREGEXP_SRC_END(VALUE rexp) { return RSTRING_END(RREGEXP_SRC(rexp)); } #endif /* RBIMPL_RREGEXP_H */ PK!%OcGGrarray.hnu[#ifndef RBIMPL_RARRAY_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RARRAY_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Defines struct ::RArray. */ #include "ruby/internal/arithmetic/long.h" #include "ruby/internal/attr/artificial.h" #include "ruby/internal/attr/constexpr.h" #include "ruby/internal/attr/maybe_unused.h" #include "ruby/internal/attr/pure.h" #include "ruby/internal/cast.h" #include "ruby/internal/core/rbasic.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/fl_type.h" #include "ruby/internal/rgengc.h" #include "ruby/internal/stdbool.h" #include "ruby/internal/value.h" #include "ruby/internal/value_type.h" #include "ruby/assert.h" /** * @private * @warning Do not touch this macro. * @warning It is an implementation detail. * @warning The value of this macro must match for ruby itself and all * extension libraries, otherwise serious memory corruption shall * occur. */ #ifndef USE_TRANSIENT_HEAP # define USE_TRANSIENT_HEAP 1 #endif /** * Convenient casting macro. * * @param obj An object, which is in fact an ::RArray. * @return The passed object casted to ::RArray. */ #define RARRAY(obj) RBIMPL_CAST((struct RArray *)(obj)) /** @cond INTERNAL_MACRO */ #define RARRAY_EMBED_FLAG RARRAY_EMBED_FLAG #define RARRAY_EMBED_LEN_MASK RARRAY_EMBED_LEN_MASK #define RARRAY_EMBED_LEN_MAX RARRAY_EMBED_LEN_MAX #define RARRAY_EMBED_LEN_SHIFT RARRAY_EMBED_LEN_SHIFT #if USE_TRANSIENT_HEAP # define RARRAY_TRANSIENT_FLAG RARRAY_TRANSIENT_FLAG #else # define RARRAY_TRANSIENT_FLAG 0 #endif /** @endcond */ #define RARRAY_LEN rb_array_len /**< @alias{rb_array_len} */ #define RARRAY_CONST_PTR rb_array_const_ptr /**< @alias{rb_array_const_ptr} */ #define RARRAY_CONST_PTR_TRANSIENT rb_array_const_ptr_transient /**< @alias{rb_array_const_ptr_transient} */ /** @cond INTERNAL_MACRO */ #if defined(__fcc__) || defined(__fcc_version) || \ defined(__FCC__) || defined(__FCC_VERSION) /* workaround for old version of Fujitsu C Compiler (fcc) */ # define FIX_CONST_VALUE_PTR(x) ((const VALUE *)(x)) #else # define FIX_CONST_VALUE_PTR(x) (x) #endif #define RARRAY_EMBED_LEN RARRAY_EMBED_LEN #define RARRAY_LENINT RARRAY_LENINT #define RARRAY_TRANSIENT_P RARRAY_TRANSIENT_P #define RARRAY_ASET RARRAY_ASET #define RARRAY_PTR RARRAY_PTR /** @endcond */ /** * @private * * Bits that you can set to ::RBasic::flags. * * @warning These enums are not the only bits we use for arrays. * * @internal * * Unlike strings, flag usages for arrays are scattered across the entire * source codes. @shyouhei doesn't know the complete list. But what is listed * here is at least incomplete. */ enum ruby_rarray_flags { /** * This flag has something to do with memory footprint. If the array is * "small" enough, ruby tries to be creative to abuse padding bits of * struct ::RArray for storing its contents. This flag denotes that * situation. * * @warning This bit has to be considered read-only. Setting/clearing * this bit without corresponding fix up must cause immediate * SEGV. Also, internal structures of an array change * dynamically and transparently throughout of its lifetime. * Don't assume it being persistent. * * @internal * * 3rd parties must not be aware that there even is more than one way to * store array elements. It was a bad idea to expose this to them. */ RARRAY_EMBED_FLAG = RUBY_FL_USER1, /* RUBY_FL_USER2 is for ELTS_SHARED */ /** * When an array employs embedded strategy (see ::RARRAY_EMBED_FLAG), these * bits are used to store the number of elements actually filled into * ::RArray::ary. * * @internal * * 3rd parties must not be aware that there even is more than one way to * store array elements. It was a bad idea to expose this to them. */ #if USE_RVARGC RARRAY_EMBED_LEN_MASK = RUBY_FL_USER9 | RUBY_FL_USER8 | RUBY_FL_USER7 | RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 | RUBY_FL_USER3 #else RARRAY_EMBED_LEN_MASK = RUBY_FL_USER4 | RUBY_FL_USER3 #endif #if USE_TRANSIENT_HEAP , /** * This flag has something to do with an array's "transiency". A transient * array is an array of young generation (of generational GC), who stores * its elements inside of dedicated memory pages called a transient heap. * Not every young generation share that storage scheme, but elder * generations must no join. * * @internal * * 3rd parties must not be aware that there even is more than one way to * store array elements. It was a bad idea to expose this to them. */ RARRAY_TRANSIENT_FLAG = RUBY_FL_USER13 #endif }; /** * This is an enum because GDB wants it (rather than a macro). People need not * bother. */ enum ruby_rarray_consts { /** Where ::RARRAY_EMBED_LEN_MASK resides. */ RARRAY_EMBED_LEN_SHIFT = RUBY_FL_USHIFT + 3 #if !USE_RVARGC , /** Max possible number elements that can be embedded. */ RARRAY_EMBED_LEN_MAX = RBIMPL_EMBED_LEN_MAX_OF(VALUE) #endif }; /** Ruby's array. */ struct RArray { /** Basic part, including flags and class. */ struct RBasic basic; /** Array's specific fields. */ union { /** * Arrays that use separated memory region for elements use this * pattern. */ struct { /** Number of elements of the array. */ long len; /** Auxiliary info. */ union { /** * Capacity of `*ptr`. A continuous memory region of at least * `capa` elements is expected to exist at `*ptr`. This can be * bigger than `len`. */ long capa; /** * Parent of the array. Nowadays arrays can share their * backend memory regions each other, constructing gigantic * nest of objects. This situation is called "shared", and * this is the field to control such properties. */ #if defined(__clang__) /* <- clang++ is sane */ || \ !defined(__cplusplus) /* <- C99 is sane */ || \ (__cplusplus > 199711L) /* <- C++11 is sane */ const #endif VALUE shared_root; } aux; /** * Pointer to the C array that holds the elements of the array. In * the old days each array had dedicated memory regions. That is * no longer true today, but there still are arrays of such * properties. This field could be used to point such things. */ const VALUE *ptr; } heap; /** * Embedded elements. When an array is short enough, it uses this area * to store its elements. In this case the length is encoded into the * flags. */ #if USE_RVARGC /* This is a length 1 array because: * 1. GCC has a bug that does not optimize C flexible array members * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452) * 2. Zero length arrays are not supported by all compilers */ const VALUE ary[1]; #else const VALUE ary[RARRAY_EMBED_LEN_MAX]; #endif } as; }; RBIMPL_SYMBOL_EXPORT_BEGIN() /** * @private * * Declares a section of code where raw pointers are used. This is an * implementation detail of #RARRAY_PTR_USE. People don't use it directly. * * @param[in] ary An object of ::RArray. * @return `ary`'s backend C array. */ VALUE *rb_ary_ptr_use_start(VALUE ary); /** * @private * * Declares an end of a section formerly started by rb_ary_ptr_use_start(). * This is an implementation detail of #RARRAY_PTR_USE. People don't use it * directly. * * @param[in] a An object of ::RArray. */ void rb_ary_ptr_use_end(VALUE a); #if USE_TRANSIENT_HEAP /** * Destructively converts an array of transient backend into ordinal one. * * @param[out] a An object of ::RArray. * @pre `a` must be a transient array. * @post `a` gets out of transient heap, destructively. */ void rb_ary_detransient(VALUE a); #endif RBIMPL_SYMBOL_EXPORT_END() RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Queries the length of the array. * * @param[in] ary Array in question. * @return Its number of elements. * @pre `ary` must be an instance of ::RArray, and must has its * ::RARRAY_EMBED_FLAG flag set. * * @internal * * This was a macro before. It was inevitable to be public, since macros are * global constructs. But should it be forever? Now that it is a function, * @shyouhei thinks it could just be eliminated, hidden into implementation * details. */ static inline long RARRAY_EMBED_LEN(VALUE ary) { RBIMPL_ASSERT_TYPE(ary, RUBY_T_ARRAY); RBIMPL_ASSERT_OR_ASSUME(RB_FL_ANY_RAW(ary, RARRAY_EMBED_FLAG)); VALUE f = RBASIC(ary)->flags; f &= RARRAY_EMBED_LEN_MASK; f >>= RARRAY_EMBED_LEN_SHIFT; return RBIMPL_CAST((long)f); } RBIMPL_ATTR_PURE_UNLESS_DEBUG() /** * Queries the length of the array. * * @param[in] a Array in question. * @return Its number of elements. * @pre `a` must be an instance of ::RArray. */ static inline long rb_array_len(VALUE a) { RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY); if (RB_FL_ANY_RAW(a, RARRAY_EMBED_FLAG)) { return RARRAY_EMBED_LEN(a); } else { return RARRAY(a)->as.heap.len; } } RBIMPL_ATTR_ARTIFICIAL() /** * Identical to rb_array_len(), except it differs for the return type. * * @param[in] ary Array in question. * @exception rb_eRangeError Too long. * @return Its number of elements. * @pre `ary` must be an instance of ::RArray. * * @internal * * This API seems redundant but has actual usages. */ static inline int RARRAY_LENINT(VALUE ary) { return rb_long2int(RARRAY_LEN(ary)); } RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Queries if the array is a transient array. * * @param[in] ary Array in question. * @retval true Yes it is. * @retval false No it isn't. * @pre `ary` must be an instance of ::RArray. * * @internal * * @shyouhei doesn't understand the benefit of this function called from * extension libraries. */ static inline bool RARRAY_TRANSIENT_P(VALUE ary) { RBIMPL_ASSERT_TYPE(ary, RUBY_T_ARRAY); #if USE_TRANSIENT_HEAP return RB_FL_ANY_RAW(ary, RARRAY_TRANSIENT_FLAG); #else return false; #endif } RBIMPL_ATTR_PURE_UNLESS_DEBUG() /** * @private * * This is an implementation detail of RARRAY_PTR(). People do not use it * directly. * * @param[in] a An object of ::RArray. * @return Its backend storage. */ static inline const VALUE * rb_array_const_ptr_transient(VALUE a) { RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY); if (RB_FL_ANY_RAW(a, RARRAY_EMBED_FLAG)) { return FIX_CONST_VALUE_PTR(RARRAY(a)->as.ary); } else { return FIX_CONST_VALUE_PTR(RARRAY(a)->as.heap.ptr); } } #if ! USE_TRANSIENT_HEAP RBIMPL_ATTR_PURE_UNLESS_DEBUG() #endif /** * @private * * This is an implementation detail of RARRAY_PTR(). People do not use it * directly. * * @param[in] a An object of ::RArray. * @return Its backend storage. * @post `a` is not a transient array. */ static inline const VALUE * rb_array_const_ptr(VALUE a) { RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY); #if USE_TRANSIENT_HEAP if (RARRAY_TRANSIENT_P(a)) { rb_ary_detransient(a); } #endif return rb_array_const_ptr_transient(a); } /** * @private * * This is an implementation detail of #RARRAY_PTR_USE. People do not use it * directly. * * @param[in] a An object of ::RArray. * @param[in] allow_transient Whether `a` can be transient or not. * @return Its backend storage. * @post `a` is not a transient array unless `allow_transient`. */ static inline VALUE * rb_array_ptr_use_start(VALUE a, RBIMPL_ATTR_MAYBE_UNUSED() int allow_transient) { RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY); #if USE_TRANSIENT_HEAP if (!allow_transient) { if (RARRAY_TRANSIENT_P(a)) { rb_ary_detransient(a); } } #endif return rb_ary_ptr_use_start(a); } /** * @private * * This is an implementation detail of #RARRAY_PTR_USE. People do not use it * directly. * * @param[in] a An object of ::RArray. * @param[in] allow_transient Whether `a` can be transient or not. */ static inline void rb_array_ptr_use_end(VALUE a, RBIMPL_ATTR_MAYBE_UNUSED() int allow_transient) { RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY); rb_ary_ptr_use_end(a); } /** * @private * * This is an implementation detail of #RARRAY_PTR_USE. People do not use it * directly. */ #define RBIMPL_RARRAY_STMT(flag, ary, var, expr) do { \ RBIMPL_ASSERT_TYPE((ary), RUBY_T_ARRAY); \ const VALUE rbimpl_ary = (ary); \ VALUE *var = rb_array_ptr_use_start(rbimpl_ary, (flag)); \ expr; \ rb_array_ptr_use_end(rbimpl_ary, (flag)); \ } while (0) /** * @private * * This is an implementation detail of #RARRAY_PTR_USE. People do not use it * directly. */ #define RARRAY_PTR_USE_END(a) rb_array_ptr_use_end(a, 0) /** * Declares a section of code where raw pointers are used. In case you need to * touch the raw C array instead of polite CAPIs, then that operation shall be * wrapped using this macro. * * ```CXX * const auto ary = rb_eval_string("[...]"); * const auto len = RARRAY_LENINT(ary); * const auto symwrite = rb_intern("write"); * * RARRAY_PTR_USE(ary, ptr, { * rb_funcallv(rb_stdout, symwrite, len, ptr); * }); * ``` * * @param ary An object of ::RArray. * @param ptr_name A variable name which points the C array in `expr`. * @param expr The expression that touches `ptr_name`. * * @internal * * For historical reasons use of this macro is not enforced. There are * extension libraries in the wild which call RARRAY_PTR() without it. We want * them use it... Maybe some transition path can be implemented later. */ #define RARRAY_PTR_USE(ary, ptr_name, expr) \ RBIMPL_RARRAY_STMT(0, ary, ptr_name, expr) /** * Identical to #RARRAY_PTR_USE, except the pointer can be a transient one. * * @param ary An object of ::RArray. * @param ptr_name A variable name which points the C array in `expr`. * @param expr The expression that touches `ptr_name`. */ #define RARRAY_PTR_USE_TRANSIENT(ary, ptr_name, expr) \ RBIMPL_RARRAY_STMT(1, ary, ptr_name, expr) /** * Wild use of a C pointer. This function accesses the backend storage * directly. This is slower than #RARRAY_PTR_USE_TRANSIENT. It exercises * extra manoeuvres to protect our generational GC. Use of this function is * considered archaic. Use a modern way instead. * * @param[in] ary An object of ::RArray. * @return The backend C array. * * @internal * * That said... there are extension libraries in the wild who uses it. We * cannot but continue supporting. */ static inline VALUE * RARRAY_PTR(VALUE ary) { RBIMPL_ASSERT_TYPE(ary, RUBY_T_ARRAY); VALUE tmp = RB_OBJ_WB_UNPROTECT_FOR(ARRAY, ary); return RBIMPL_CAST((VALUE *)RARRAY_CONST_PTR(tmp)); } /** * Assigns an object in an array. * * @param[out] ary Destination array object. * @param[in] i Index of `ary`. * @param[in] v Arbitrary ruby object. * @pre `ary` must be an instance of ::RArray. * @pre `ary`'s length must be longer than or equal to `i`. * @pre `i` must be greater than or equal to zero. * @post `ary`'s `i`th element is set to `v`. */ static inline void RARRAY_ASET(VALUE ary, long i, VALUE v) { RARRAY_PTR_USE_TRANSIENT(ary, ptr, RB_OBJ_WRITE(ary, &ptr[i], v)); } /** * @deprecated * * :FIXME: we want to convert RARRAY_AREF into an inline function (to add rooms * for more sanity checks). However there were situations where the address of * this macro is taken i.e. &RARRAY_AREF(...). They cannot be possible if this * is not a macro. Such usages are abuse, and we eliminated them internally. * However we are afraid of similar things to remain in the wild. This macro * remains as it is due to that. If we could warn such usages we can set a * transition path, but currently no way is found to do so. */ #define RARRAY_AREF(a, i) RARRAY_CONST_PTR_TRANSIENT(a)[i] #endif /* RBIMPL_RARRAY_H */ PK!ޓ1W1W rtypeddata.hnu[#ifndef RBIMPL_RTYPEDDATA_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RTYPEDDATA_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Defines struct ::RTypedData. */ #include "ruby/internal/config.h" #ifdef STDC_HEADERS # include #endif #include "ruby/internal/assume.h" #include "ruby/internal/attr/artificial.h" #include "ruby/internal/attr/flag_enum.h" #include "ruby/internal/attr/nonnull.h" #include "ruby/internal/attr/pure.h" #include "ruby/internal/cast.h" #include "ruby/internal/core/rbasic.h" #include "ruby/internal/core/rdata.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/error.h" #include "ruby/internal/fl_type.h" #include "ruby/internal/stdbool.h" #include "ruby/internal/value_type.h" /** * @private * * @deprecated This macro once was a thing in the old days, but makes no sense * any longer today. Exists here for backwards compatibility * only. You can safely forget about it. */ #define HAVE_TYPE_RB_DATA_TYPE_T 1 /** * @private * * @deprecated This macro once was a thing in the old days, but makes no sense * any longer today. Exists here for backwards compatibility * only. You can safely forget about it. */ #define HAVE_RB_DATA_TYPE_T_FUNCTION 1 /** * @private * * @deprecated This macro once was a thing in the old days, but makes no sense * any longer today. Exists here for backwards compatibility * only. You can safely forget about it. */ #define HAVE_RB_DATA_TYPE_T_PARENT 1 /** * This is a value you can set to ::rb_data_type_struct::dfree. Setting this * means the data was allocated using ::ruby_xmalloc() (or variants), and shall * be freed using ::ruby_xfree(). * * @warning Do not use this if you want to use system malloc, because the * system and Ruby might or might not share the same malloc * implementation. */ #define RUBY_TYPED_DEFAULT_FREE RUBY_DEFAULT_FREE /** * This is a value you can set to ::rb_data_type_struct::dfree. Setting this * means the data is managed by someone else, like, statically allocated. Of * course you are on your own then. */ #define RUBY_TYPED_NEVER_FREE RUBY_NEVER_FREE /** * Convenient casting macro. * * @param obj An object, which is in fact an ::RTypedData. * @return The passed object casted to ::RTypedData. */ #define RTYPEDDATA(obj) RBIMPL_CAST((struct RTypedData *)(obj)) /** * Convenient getter macro. * * @param v An object, which is in fact an ::RTypedData. * @return The passed object's ::RTypedData::data field. */ #define RTYPEDDATA_DATA(v) (RTYPEDDATA(v)->data) /** @old{rb_check_typeddata} */ #define Check_TypedStruct(v, t) \ rb_check_typeddata(RBIMPL_CAST((VALUE)(v)), (t)) /** @cond INTERNAL_MACRO */ #define RTYPEDDATA_P RTYPEDDATA_P #define RTYPEDDATA_TYPE RTYPEDDATA_TYPE #define RUBY_TYPED_FREE_IMMEDIATELY RUBY_TYPED_FREE_IMMEDIATELY #define RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE #define RUBY_TYPED_WB_PROTECTED RUBY_TYPED_WB_PROTECTED #define RUBY_TYPED_PROMOTED1 RUBY_TYPED_PROMOTED1 /** @endcond */ /** * @private * * Bits for rb_data_type_struct::flags. */ enum RBIMPL_ATTR_FLAG_ENUM() rbimpl_typeddata_flags { /** * This flag has something to do with Ruby's global interpreter lock. For * maximum safety, Ruby locks the entire VM during GC. However your * callback functions could unintentionally unlock it, for instance when * they try to flush an IO buffer. Such operations are dangerous (threads * then run alongside of GC). By default, to prevent those scenario, * callbacks are deferred until the GC engine is 100% sure threads can run. * This flag skips that; structs with it are deallocated during the sweep * phase. * * Using this flag needs deep understanding of both GC and threads. You * would better leave it unspecified. */ RUBY_TYPED_FREE_IMMEDIATELY = 1, /** * This flag has something to do with Ractor. Multiple Ractors run without * protecting each other. Sharing an object among Ractors is basically * dangerous, disabled by default. This flag is used to bypass that * restriction. but setting it is not enough. In addition to do so, an * object also has to be frozen, and be passed to * rb_ractor_make_shareable() before being actually shareable. Of course, * you have to manually prevent race conditions then. * * Using this flag needs deep understanding of multithreaded programming. * You would better leave it unspecified. */ RUBY_TYPED_FROZEN_SHAREABLE = RUBY_FL_SHAREABLE, /** * This flag has something to do with our garbage collector. These days * ruby objects are "generational". There are those who are young and * those who are old. Young objects are prone to die; monitored relatively * extensively by the garbage collector. OTOH old objects tend to live * longer. They are relatively rarely considered. This basically works. * But there is one tweak that has to be exercised. When an elder object * has reference(s) to younger one(s), that referenced objects must not * die. In order to detect additions of such references, old generations * are protected by write barriers. It is a very difficult hack to * appropriately insert write barriers everywhere. This mechanism is * disabled by default for 3rd party extensions (they never get aged). By * specifying this flag you can enable the generational feature to your * data structure. Of course, you have to manually insert write barriers * then. * * Using this flag needs deep understanding of GC internals, often at the * level of source code. You would better leave it unspecified. */ RUBY_TYPED_WB_PROTECTED = RUBY_FL_WB_PROTECTED, /* THIS FLAG DEPENDS ON Ruby version */ /** * This flag is mysterious. It seems nobody is currently using it. The * intention of this flag is also unclear. We need further investigations. */ RUBY_TYPED_PROMOTED1 = RUBY_FL_PROMOTED1 /* THIS FLAG DEPENDS ON Ruby version */ }; /** * This is the struct that holds necessary info for a struct. It roughly * resembles a Ruby level class; multiple objects can share a ::rb_data_type_t * instance. */ typedef struct rb_data_type_struct rb_data_type_t; /** @copydoc rb_data_type_t */ struct rb_data_type_struct { /** * Name of structs of this kind. This is used for diagnostic purposes. * This has to be unique in the process, but doesn't has to be a valid * C/Ruby identifier. */ const char *wrap_struct_name; /** Function pointers. Resembles C++ `vtbl`.*/ struct { /** * This function is called when the object is experiencing GC marks. * If it contains references to other Ruby objects, you need to mark * them also. Otherwise GC will smash your data. * * @see rb_gc_mark() * @warning This is called during GC runs. Object allocations are * impossible at that moment (that is why GC runs). */ RUBY_DATA_FUNC dmark; /** * This function is called when the object is no longer used. You need * to do whatever necessary to avoid memory leaks. * * @warning This is called during GC runs. Object allocations are * impossible at that moment (that is why GC runs). */ RUBY_DATA_FUNC dfree; /** * This function is to query the size of the underlying memory regions. * * @internal * * This function has only one usage, which is form inside of * `ext/objspace`. */ size_t (*dsize)(const void *); /** * This function is called when the object is relocated. Like * ::rb_data_type_struct::dmark, you need to update references to Ruby * objects inside of your structs. * * @see rb_gc_location() * @warning This is called during GC runs. Object allocations are * impossible at that moment (that is why GC runs). */ RUBY_DATA_FUNC dcompact; /** * This field is reserved for future extension. For now, it must be * filled with zeros. */ void *reserved[1]; /* For future extension. This array *must* be filled with ZERO. */ } function; /** * Parent of this class. Sometimes C structs have inheritance-like * relationships. An example is `struct sockaddr` and its family. If you * design such things, make ::rb_data_type_t for each of them and connect * using this field. Ruby can then transparently cast your data back and * forth when you call #TypedData_Get_Struct(). * * ```CXX * struct parent { }; * static inline const rb_data_type_t parent_type = { * .wrap_struct_name = "parent", * }; * * struct child: public parent { }; * static inline const rb_data_type_t child_type = { * .wrap_struct_name = "child", * .parent = &parent_type, * }; * * // This function can take both parent_class and child_class. * static inline struct parent * * get_parent(VALUE v) * { * struct parent *p; * TypedData_Get_Struct(v, parent_type, struct parent, p); * return p; * } * ``` */ const rb_data_type_t *parent; /** * Type-specific static data. This area can be used for any purpose by a * programmer who define the type. Ruby does not manage this at all. */ void *data; /* This area can be used for any purpose by a programmer who define the type. */ /** * Type-specific behavioural characteristics. This is a bitfield. It is * an EXTREMELY WISE IDEA to leave this field blank. It is designed so * that setting zero is the safest thing to do. If you risk to set any * bits on, you have to know exactly what you are doing. * * @internal * * Why it has to be a ::VALUE? @shyouhei doesn't understand the design. */ VALUE flags; /* RUBY_FL_WB_PROTECTED */ }; /** * "Typed" user data. By using this, extension libraries can wrap a C struct * to make it visible from Ruby. For instance if you have a `struct timeval`, * and you want users to use it, * * ```CXX * static inline const rb_data_type_t timeval_type = { * // Note that unspecified fields are 0-filled by default. * .wrap_struct_name = "timeval", * .function = { * .dmark = nullptr, // no need to mark * .dfree = RUBY_TYPED_DEFAULT_FREE, // use ruby_xfree() * .dsize = [](auto) { * return sizeof(struct timeval); * }, * }, * }; * * extern "C" void * Init_timeval(void) * { * auto klass = rb_define_class("YourName", rb_cObject); * * rb_define_alloc_func(klass, [](auto klass) { * struct timeval *t; * auto ret = TypedData_Make_Struct( * klass, struct timeval, &timeval_type, t); * * if (auto i = gettimeofday(t, nullptr); i == -1) { * rb_sys_fail("gettimeofday(3)"); * } * else { * return ret; * } * }); * } * ``` */ struct RTypedData { /** The part that all ruby objects have in common. */ struct RBasic basic; /** * This field stores various information about how Ruby should handle a * data. This roughly resembles a Ruby level class (apart from method * definition etc.) */ const rb_data_type_t *type; /** * This has to be always 1. * * @internal * * Why, then, this is not a const ::VALUE? */ VALUE typed_flag; /** Pointer to the actual C level struct that you want to wrap. */ void *data; }; RBIMPL_SYMBOL_EXPORT_BEGIN() RBIMPL_ATTR_NONNULL((3)) /** * This is the primitive way to wrap an existing C struct into ::RTypedData. * * @param[in] klass Ruby level class of the returning object. * @param[in] datap Pointer to the target C struct. * @param[in] type The characteristics of the passed data. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return An allocated object that wraps `datap`. */ VALUE rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type); /** * Identical to rb_data_typed_object_wrap(), except it allocates a new data * region internally instead of taking an existing one. The allocation is done * using ruby_calloc(). Hence it makes no sense for `type->function.dfree` to * be anything other than ::RUBY_TYPED_DEFAULT_FREE. * * @param[in] klass Ruby level class of the returning object. * @param[in] size Requested size of memory to allocate. * @param[in] type The characteristics of the passed data. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return An allocated object that wraps a new `size` byte region. */ VALUE rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type); /** * Checks for the domestic relationship between the two. * * @param[in] child A data type supposed to be a child of `parent`. * @param[in] parent A data type supposed to be a parent of `child`. * @retval true `child` is a descendent of `parent`. * @retval false Otherwise. * * @internal * * You can path NULL to both arguments, don't know what that means though. */ int rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent); /** * Checks if the given object is of given kind. * * @param[in] obj An instance of ::RTypedData. * @param[in] data_type Expected data type of `obj`. * @retval true `obj` is of `data_type`. * @retval false Otherwise. */ int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type); /** * Identical to rb_typeddata_is_kind_of(), except it raises exceptions instead * of returning false. * * @param[in] obj An instance of ::RTypedData. * @param[in] data_type Expected data type of `obj`. * @exception rb_eTypeError obj is not of `data_type`. * @return Unwrapped C struct that `obj` holds. * @post Upon successful return `obj`'s type is guaranteed `data_type`. */ void *rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type); RBIMPL_SYMBOL_EXPORT_END() /** * Converts sval, a pointer to your struct, into a Ruby object. * * @param klass A ruby level class. * @param data_type The type of `sval`. * @param sval A pointer to your struct. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return A created Ruby object. */ #define TypedData_Wrap_Struct(klass,data_type,sval)\ rb_data_typed_object_wrap((klass),(sval),(data_type)) /** * @private * * This is an implementation detail of #TypedData_Make_Struct. People don't * use it directly. * * @param result Variable name of created Ruby object. * @param klass Ruby level class of the object. * @param type Type name of the C struct. * @param size Size of the C struct. * @param data_type The data type describing `type`. * @param sval Variable name of created C struct. */ #define TypedData_Make_Struct0(result, klass, type, size, data_type, sval) \ VALUE result = rb_data_typed_object_zalloc(klass, size, data_type); \ (sval) = RBIMPL_CAST((type *)RTYPEDDATA_DATA(result)); \ RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval)) /** * Identical to #TypedData_Wrap_Struct, except it allocates a new data region * internally instead of taking an existing one. The allocation is done using * ruby_calloc(). Hence it makes no sense for `data_type->function.dfree` to * be anything other than ::RUBY_TYPED_DEFAULT_FREE. * * @param klass Ruby level class of the object. * @param type Type name of the C struct. * @param data_type The data type describing `type`. * @param sval Variable name of created C struct. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return A created Ruby object. */ #ifdef HAVE_STMT_AND_DECL_IN_EXPR #define TypedData_Make_Struct(klass, type, data_type, sval) \ RB_GNUC_EXTENSION({ \ TypedData_Make_Struct0( \ data_struct_obj, \ klass, \ type, \ sizeof(type), \ data_type, \ sval); \ data_struct_obj; \ }) #else #define TypedData_Make_Struct(klass, type, data_type, sval) \ rb_data_typed_object_make( \ (klass), \ (data_type), \ RBIMPL_CAST((void **)&(sval)), \ sizeof(type)) #endif /** * Obtains a C struct from inside of a wrapper Ruby object. * * @param obj An instance of ::RTypedData. * @param type Type name of the C struct. * @param data_type The data type describing `type`. * @param sval Variable name of obtained C struct. * @exception rb_eTypeError `obj` is not a kind of `data_type`. * @return Unwrapped C struct that `obj` holds. */ #define TypedData_Get_Struct(obj,type,data_type,sval) \ ((sval) = RBIMPL_CAST((type *)rb_check_typeddata((obj), (data_type)))) RBIMPL_ATTR_PURE() RBIMPL_ATTR_ARTIFICIAL() /** * @private * * This is an implementation detail of Check_Type(). People don't use it * directly. * * @param[in] obj Object in question * @retval true `obj` is an instance of ::RTypedData. * @retval false `obj` is an instance of ::RData. * @pre `obj` must be a Ruby object of ::RUBY_T_DATA. */ static inline bool rbimpl_rtypeddata_p(VALUE obj) { return RTYPEDDATA(obj)->typed_flag == 1; } RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** * Checks whether the passed object is ::RTypedData or ::RData. * * @param[in] obj Object in question * @retval true `obj` is an instance of ::RTypedData. * @retval false `obj` is an instance of ::RData. * @pre `obj` must be a Ruby object of ::RUBY_T_DATA. */ static inline bool RTYPEDDATA_P(VALUE obj) { #if RUBY_DEBUG if (RB_UNLIKELY(! RB_TYPE_P(obj, RUBY_T_DATA))) { Check_Type(obj, RUBY_T_DATA); RBIMPL_UNREACHABLE_RETURN(false); } #endif return rbimpl_rtypeddata_p(obj); } RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /* :TODO: can this function be __attribute__((returns_nonnull)) or not? */ /** * Queries for the type of given object. * * @param[in] obj Object in question * @return Data type struct that corresponds to `obj`. * @pre `obj` must be an instance of ::RTypedData. */ static inline const struct rb_data_type_struct * RTYPEDDATA_TYPE(VALUE obj) { #if RUBY_DEBUG if (RB_UNLIKELY(! RTYPEDDATA_P(obj))) { rb_unexpected_type(obj, RUBY_T_DATA); RBIMPL_UNREACHABLE_RETURN(NULL); } #endif return RTYPEDDATA(obj)->type; } /** * While we don't stop you from using this function, it seems to be an * implementation detail of #TypedData_Make_Struct, which is preferred over * this one. * * @param[in] klass Ruby level class of the returning object. * @param[in] type The data type * @param[out] datap Return pointer. * @param[in] size Size of the C struct. * @exception rb_eTypeError `klass` is not a class. * @exception rb_eNoMemError Out of memory. * @return A created Ruby object. * @post `*datap` points to the C struct wrapped by the returned object. */ static inline VALUE rb_data_typed_object_make(VALUE klass, const rb_data_type_t *type, void **datap, size_t size) { TypedData_Make_Struct0(result, klass, void, size, type, *datap); return result; } RBIMPL_ATTR_DEPRECATED(("by: rb_data_typed_object_wrap")) /** @deprecated This function was renamed to rb_data_typed_object_wrap(). */ static inline VALUE rb_data_typed_object_alloc(VALUE klass, void *datap, const rb_data_type_t *type) { return rb_data_typed_object_wrap(klass, datap, type); } #endif /* RBIMPL_RTYPEDDATA_H */ PK!xִZZrfile.hnu[#ifndef RBIMPL_RFILE_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_RFILE_H /** * @file * @author Ruby developers * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Defines struct ::RFile. */ #include "ruby/internal/core/rbasic.h" #include "ruby/internal/cast.h" /* rb_io_t is in ruby/io.h. The header file has historically not been included * into ruby/ruby.h. We follow that tradition. */ struct rb_io_t; /** * Ruby's File and IO. Ruby's IO are not just file descriptors. They have * buffers. They also have encodings. Various information are controlled * using this struct. */ struct RFile { /** Basic part, including flags and class. */ struct RBasic basic; /** IO's specific fields. */ struct rb_io_t *fptr; }; /** * Convenient casting macro. * * @param obj An object, which is in fact an ::RFile. * @return The passed object casted to ::RFile. */ #define RFILE(obj) RBIMPL_CAST((struct RFile *)(obj)) #endif /* RBIMPL_RFILE_H */ PK!4;{g only.pmnu[package lib::core::only; use strict; use warnings FATAL => 'all'; use Config; sub import { @INC = @Config{qw(privlibexp archlibexp)}; return } =head1 NAME lib::core::only - Remove all non-core paths from @INC to avoid site/vendor dirs =head1 SYNOPSIS use lib::core::only; # now @INC contains only the two core directories To get only the core directories plus the ones for the local::lib in scope: $ perl -mlocal::lib -Mlib::core::only -Mlocal::lib=~/perl5 myscript.pl To attempt to do a self-contained build (but note this will not reliably propagate into subprocesses, see the CAVEATS below): $ PERL5OPT='-mlocal::lib -Mlib::core::only -Mlocal::lib=~/perl5' cpan Please note that it is necessary to use C twice for this to work. First so that C doesn't prevent C from loading (it's not currently in core) and then again after C so that the local paths are not removed. =head1 DESCRIPTION lib::core::only is simply a shortcut to say "please reduce my @INC to only the core lib and archlib (architecture-specific lib) directories of this perl". You might want to do this to ensure a local::lib contains only the code you need, or to test an L tree, or to avoid known bad vendor packages. You might want to use this to try and install a self-contained tree of perl modules. Be warned that that probably won't work (see L). This module was extracted from L's --self-contained feature, and contains the only part that ever worked. I apologise to anybody who thought anything else did. =head1 CAVEATS This does B propagate properly across perl invocations like local::lib's stuff does. It can't. It's only a module import, so it B. If you want to cascade it across invocations, you can set the PERL5OPT environment variable to '-Mlib::core::only' and it'll sort of work. But be aware that taint mode ignores this, so some modules' build and test code probably will as well. You also need to be aware that perl's command line options are not processed in order - -I options take effect before -M options, so perl -Mlib::core::only -Ilib is unlike to do what you want - it's exactly equivalent to: perl -Mlib::core::only If you want to combine a core-only @INC with additional paths, you need to add the additional paths using -M options and the L module: perl -Mlib::core::only -Mlib=lib # or if you're trying to test compiled code: perl -Mlib::core::only -Mblib For more information on the impossibility of sanely propagating this across module builds without help from the build program, see L - and for ways to achieve the old --self-contained feature's results, look at L's tree function, and at L's --local-lib-contained feature. =head1 AUTHOR Matt S. Trout =head1 LICENSE This library is free software under the same terms as perl itself. =head1 COPYRIGHT (c) 2010 the lib::core::only L as specified above. =cut 1; PK! __init__.pynu[PK![sBuBu nftables.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2018 Red Hat, Inc. # # Authors: # Eric Garver # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # from __future__ import absolute_import import copy import json import ipaddress from firewall.core.logger import log from firewall.functions import check_mac, getPortRange, normalizeIP6, \ check_single_address, check_address from firewall.errors import FirewallError, UNKNOWN_ERROR, INVALID_RULE, \ INVALID_ICMPTYPE, INVALID_TYPE, INVALID_ENTRY, \ INVALID_PORT from firewall.core.rich import Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark, \ Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock from firewall.core.icmp import ICMP_TYPES, ICMPV6_TYPES from nftables.nftables import Nftables TABLE_NAME = "firewalld" TABLE_NAME_POLICY = TABLE_NAME + "_" + "policy_drop" POLICY_CHAIN_PREFIX = "policy_" # Map iptables (table, chain) to hooks and priorities. # These are well defined by NF_IP_PRI_* defines in netfilter. # # This is analogous to ipXtables.BUILT_IN_CHAINS, but we omit the chains that # are only used for direct rules. # # Note: All hooks use their standard position + NFT_HOOK_OFFSET. This means # iptables will have DROP precedence. It also means that even if iptables # ACCEPTs a packet it may still be dropped later by firewalld's rules. # NFT_HOOK_OFFSET = 10 IPTABLES_TO_NFT_HOOK = { #"security": { # "INPUT": ("input", 50 + NFT_HOOK_OFFSET), # "OUTPUT": ("output", 50 + NFT_HOOK_OFFSET), # "FORWARD": ("forward", 50 + NFT_HOOK_OFFSET), #}, "raw": { # "PREROUTING": ("prerouting", -300 + NFT_HOOK_OFFSET), # "OUTPUT": ("output", -300 + NFT_HOOK_OFFSET), }, "mangle": { "PREROUTING": ("prerouting", -150 + NFT_HOOK_OFFSET), # "POSTROUTING": ("postrouting", -150 + NFT_HOOK_OFFSET), # "INPUT": ("input", -150 + NFT_HOOK_OFFSET), # "OUTPUT": ("output", -150 + NFT_HOOK_OFFSET), # "FORWARD": ("forward", -150 + NFT_HOOK_OFFSET), }, "nat": { "PREROUTING": ("prerouting", -100 + NFT_HOOK_OFFSET), "POSTROUTING": ("postrouting", 100 + NFT_HOOK_OFFSET), # "INPUT": ("input", 100 + NFT_HOOK_OFFSET), # "OUTPUT": ("output", -100 + NFT_HOOK_OFFSET), }, "filter": { "PREROUTING": ("prerouting", 0 + NFT_HOOK_OFFSET), "INPUT": ("input", 0 + NFT_HOOK_OFFSET), "FORWARD": ("forward", 0 + NFT_HOOK_OFFSET), "OUTPUT": ("output", 0 + NFT_HOOK_OFFSET), }, } class nftables(object): name = "nftables" policies_supported = True def __init__(self, fw): self._fw = fw self.restore_command_exists = True self.available_tables = [] self.rule_to_handle = {} self.rule_ref_count = {} self.rich_rule_priority_counts = {} self.policy_priority_counts = {} self.zone_source_index_cache = {} self.created_tables = {"inet": [], "ip": [], "ip6": []} self.nftables = Nftables() self.nftables.set_echo_output(True) self.nftables.set_handle_output(True) def _run_replace_zone_source(self, rule, zone_source_index_cache): for verb in ["add", "insert", "delete"]: if verb in rule: break if "%%ZONE_SOURCE%%" in rule[verb]["rule"]: zone_source = (rule[verb]["rule"]["%%ZONE_SOURCE%%"]["zone"], rule[verb]["rule"]["%%ZONE_SOURCE%%"]["address"]) del rule[verb]["rule"]["%%ZONE_SOURCE%%"] elif "%%ZONE_INTERFACE%%" in rule[verb]["rule"]: zone_source = None del rule[verb]["rule"]["%%ZONE_INTERFACE%%"] else: return family = rule[verb]["rule"]["family"] if zone_source and verb == "delete": if family in zone_source_index_cache and \ zone_source in zone_source_index_cache[family]: zone_source_index_cache[family].remove(zone_source) elif verb != "delete": if family not in zone_source_index_cache: zone_source_index_cache[family] = [] if zone_source: # order source based dispatch by zone name if zone_source not in zone_source_index_cache[family]: zone_source_index_cache[family].append(zone_source) zone_source_index_cache[family].sort(key=lambda x: x[0]) index = zone_source_index_cache[family].index(zone_source) else: if self._fw._allow_zone_drifting: index = 0 else: index = len(zone_source_index_cache[family]) _verb_snippet = rule[verb] del rule[verb] if index == 0: rule["insert"] = _verb_snippet else: index -= 1 # point to the rule before insertion point rule["add"] = _verb_snippet rule["add"]["rule"]["index"] = index def reverse_rule(self, dict): if "insert" in dict: return {"delete": copy.deepcopy(dict["insert"])} elif "add" in dict: return {"delete": copy.deepcopy(dict["add"])} else: raise FirewallError(UNKNOWN_ERROR, "Failed to reverse rule") def _set_rule_replace_priority(self, rule, priority_counts, token): for verb in ["add", "insert", "delete"]: if verb in rule: break if token in rule[verb]["rule"]: priority = rule[verb]["rule"][token] del rule[verb]["rule"][token] if type(priority) != int: raise FirewallError(INVALID_RULE, "priority must be followed by a number") chain = (rule[verb]["rule"]["family"], rule[verb]["rule"]["chain"]) # family, chain # Add the rule to the priority counts. We don't need to store the # rule, just bump the ref count for the priority value. if verb == "delete": if chain not in priority_counts or \ priority not in priority_counts[chain] or \ priority_counts[chain][priority] <= 0: raise FirewallError(UNKNOWN_ERROR, "nonexistent or underflow of priority count") priority_counts[chain][priority] -= 1 else: if chain not in priority_counts: priority_counts[chain] = {} if priority not in priority_counts[chain]: priority_counts[chain][priority] = 0 # calculate index of new rule index = 0 for p in sorted(priority_counts[chain].keys()): if p == priority and verb == "insert": break index += priority_counts[chain][p] if p == priority and verb == "add": break priority_counts[chain][priority] += 1 _verb_snippet = rule[verb] del rule[verb] if index == 0: rule["insert"] = _verb_snippet else: index -= 1 # point to the rule before insertion point rule["add"] = _verb_snippet rule["add"]["rule"]["index"] = index def _get_rule_key(self, rule): for verb in ["add", "insert", "delete"]: if verb in rule and "rule" in rule[verb]: rule_key = copy.deepcopy(rule[verb]["rule"]) for non_key in ["index", "handle", "position"]: if non_key in rule_key: del rule_key[non_key] # str(rule_key) is insufficient because dictionary order is # not stable.. so abuse the JSON library rule_key = json.dumps(rule_key, sort_keys=True) return rule_key # Not a rule (it's a table, chain, etc) return None def set_rules(self, rules, log_denied): _valid_verbs = ["add", "insert", "delete", "flush", "replace"] _valid_add_verbs = ["add", "insert", "replace"] _deduplicated_rules = [] _executed_rules = [] rich_rule_priority_counts = copy.deepcopy(self.rich_rule_priority_counts) policy_priority_counts = copy.deepcopy(self.policy_priority_counts) zone_source_index_cache = copy.deepcopy(self.zone_source_index_cache) rule_ref_count = self.rule_ref_count.copy() for rule in rules: if type(rule) != dict: raise FirewallError(UNKNOWN_ERROR, "rule must be a dictionary, rule: %s" % (rule)) for verb in _valid_verbs: if verb in rule: break if verb not in rule: raise FirewallError(INVALID_RULE, "no valid verb found, rule: %s" % (rule)) rule_key = self._get_rule_key(rule) # rule deduplication if rule_key in rule_ref_count: log.debug2("%s: prev rule ref cnt %d, %s", self.__class__, rule_ref_count[rule_key], rule_key) if verb != "delete": rule_ref_count[rule_key] += 1 continue elif rule_ref_count[rule_key] > 1: rule_ref_count[rule_key] -= 1 continue elif rule_ref_count[rule_key] == 1: rule_ref_count[rule_key] -= 1 else: raise FirewallError(UNKNOWN_ERROR, "rule ref count bug: rule_key '%s', cnt %d" % (rule_key, rule_ref_count[rule_key])) elif rule_key and verb != "delete": rule_ref_count[rule_key] = 1 _deduplicated_rules.append(rule) _rule = copy.deepcopy(rule) if rule_key: # filter empty rule expressions. Rich rules add quite a bit of # them, but it makes the rest of the code simpler. libnftables # does not tolerate them. _rule[verb]["rule"]["expr"] = list(filter(None, _rule[verb]["rule"]["expr"])) self._set_rule_replace_priority(_rule, rich_rule_priority_counts, "%%RICH_RULE_PRIORITY%%") self._set_rule_replace_priority(_rule, policy_priority_counts, "%%POLICY_PRIORITY%%") self._run_replace_zone_source(_rule, zone_source_index_cache) # delete using rule handle if verb == "delete": _rule = {"delete": {"rule": {"family": _rule["delete"]["rule"]["family"], "table": _rule["delete"]["rule"]["table"], "chain": _rule["delete"]["rule"]["chain"], "handle": self.rule_to_handle[rule_key]}}} _executed_rules.append(_rule) json_blob = {"nftables": [{"metainfo": {"json_schema_version": 1}}] + _executed_rules} if log.getDebugLogLevel() >= 3: # guarded with if statement because json.dumps() is expensive. log.debug3("%s: calling python-nftables with JSON blob: %s", self.__class__, json.dumps(json_blob)) rc, output, error = self.nftables.json_cmd(json_blob) if rc != 0: raise ValueError("'%s' failed: %s\nJSON blob:\n%s" % ("python-nftables", error, json.dumps(json_blob))) self.rich_rule_priority_counts = rich_rule_priority_counts self.policy_priority_counts = policy_priority_counts self.zone_source_index_cache = zone_source_index_cache self.rule_ref_count = rule_ref_count index = 0 for rule in _deduplicated_rules: index += 1 # +1 due to metainfo rule_key = self._get_rule_key(rule) if not rule_key: continue if "delete" in rule: del self.rule_to_handle[rule_key] del self.rule_ref_count[rule_key] continue for verb in _valid_add_verbs: if verb in output["nftables"][index]: break if verb not in output["nftables"][index]: continue self.rule_to_handle[rule_key] = output["nftables"][index][verb]["rule"]["handle"] def set_rule(self, rule, log_denied): self.set_rules([rule], log_denied) return "" def get_available_tables(self, table=None): # Tables always exist in nftables return [table] if table else IPTABLES_TO_NFT_HOOK.keys() def _build_delete_table_rules(self, table): # To avoid nftables returning ENOENT we always add the table before # deleting to guarantee it will exist. # # In the future, this add+delete should be replaced with "destroy", but # that verb is too new to rely upon. rules = [] for family in ["inet", "ip", "ip6"]: rules.append({"add": {"table": {"family": family, "name": table}}}) rules.append({"delete": {"table": {"family": family, "name": table}}}) return rules def build_flush_rules(self): # Policy is stashed in a separate table that we're _not_ going to # flush. As such, we retain the policy rule handles and ref counts. saved_rule_to_handle = {} saved_rule_ref_count = {} for rule in self._build_set_policy_rules_ct_rules(True): policy_key = self._get_rule_key(rule) if policy_key in self.rule_to_handle: saved_rule_to_handle[policy_key] = self.rule_to_handle[policy_key] saved_rule_ref_count[policy_key] = self.rule_ref_count[policy_key] self.rule_to_handle = saved_rule_to_handle self.rule_ref_count = saved_rule_ref_count self.rich_rule_priority_counts = {} self.policy_priority_counts = {} self.zone_source_index_cache = {} for family in ["inet", "ip", "ip6"]: if TABLE_NAME in self.created_tables[family]: self.created_tables[family].remove(TABLE_NAME) return self._build_delete_table_rules(TABLE_NAME) def _build_set_policy_rules_ct_rules(self, enable): add_del = { True: "add", False: "delete" }[enable] rules = [] for hook in ["input", "forward", "output"]: rules.append({add_del: {"rule": {"family": "inet", "table": TABLE_NAME_POLICY, "chain": "%s_%s" % ("filter", hook), "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": None}]}}}) return rules def build_set_policy_rules(self, policy): # Policy is not exposed to the user. It's only to make sure we DROP # packets while reloading and for panic mode. As such, using hooks with # a higher priority than our base chains is sufficient. rules = [] if policy == "PANIC": rules.append({"add": {"table": {"family": "inet", "name": TABLE_NAME_POLICY}}}) self.created_tables["inet"].append(TABLE_NAME_POLICY) # Use "raw" priority for panic mode. This occurs before # conntrack, mangle, nat, etc for hook in ["prerouting", "output"]: rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME_POLICY, "name": "%s_%s" % ("raw", hook), "type": "filter", "hook": hook, "prio": -300 + NFT_HOOK_OFFSET - 1, "policy": "drop"}}}) if policy == "DROP": rules.append({"add": {"table": {"family": "inet", "name": TABLE_NAME_POLICY}}}) self.created_tables["inet"].append(TABLE_NAME_POLICY) # To drop everything except existing connections we use # "filter" because it occurs _after_ conntrack. for hook in ["input", "forward", "output"]: rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME_POLICY, "name": "%s_%s" % ("filter", hook), "type": "filter", "hook": hook, "prio": 0 + NFT_HOOK_OFFSET - 1, "policy": "drop"}}}) rules += self._build_set_policy_rules_ct_rules(True) elif policy == "ACCEPT": for rule in self._build_set_policy_rules_ct_rules(False): policy_key = self._get_rule_key(rule) if policy_key in self.rule_to_handle: rules.append(rule) rules += self._build_delete_table_rules(TABLE_NAME_POLICY) if TABLE_NAME_POLICY in self.created_tables["inet"]: self.created_tables["inet"].remove(TABLE_NAME_POLICY) else: FirewallError(UNKNOWN_ERROR, "not implemented") return rules def supported_icmp_types(self, ipv=None): supported = set() if ipv is None or ipv == "ipv4": supported.update(ICMP_TYPES.keys()) if ipv is None or ipv == "ipv6": supported.update(ICMPV6_TYPES.keys()) return list(supported) def build_default_tables(self): default_tables = [] for family in ["inet", "ip", "ip6"]: default_tables.append({"add": {"table": {"family": family, "name": TABLE_NAME}}}) self.created_tables[family].append(TABLE_NAME) return default_tables def build_default_rules(self, log_denied="off"): default_rules = [] for chain in IPTABLES_TO_NFT_HOOK["mangle"].keys(): default_rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME, "name": "mangle_%s" % chain, "type": "filter", "hook": "%s" % IPTABLES_TO_NFT_HOOK["mangle"][chain][0], "prio": IPTABLES_TO_NFT_HOOK["mangle"][chain][1]}}}) for dispatch_suffix in ["POLICIES_pre", "ZONES_SOURCE", "ZONES", "POLICIES_post"] if self._fw._allow_zone_drifting else ["POLICIES_pre", "ZONES", "POLICIES_post"]: default_rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME, "name": "mangle_%s_%s" % (chain, dispatch_suffix)}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "mangle_%s" % chain, "expr": [{"jump": {"target": "mangle_%s_%s" % (chain, dispatch_suffix)}}]}}}) for family in ["ip", "ip6"]: for chain in IPTABLES_TO_NFT_HOOK["nat"].keys(): default_rules.append({"add": {"chain": {"family": family, "table": TABLE_NAME, "name": "nat_%s" % chain, "type": "nat", "hook": "%s" % IPTABLES_TO_NFT_HOOK["nat"][chain][0], "prio": IPTABLES_TO_NFT_HOOK["nat"][chain][1]}}}) for dispatch_suffix in ["POLICIES_pre", "ZONES_SOURCE", "ZONES", "POLICIES_post"] if self._fw._allow_zone_drifting else ["POLICIES_pre", "ZONES", "POLICIES_post"]: default_rules.append({"add": {"chain": {"family": family, "table": TABLE_NAME, "name": "nat_%s_%s" % (chain, dispatch_suffix)}}}) default_rules.append({"add": {"rule": {"family": family, "table": TABLE_NAME, "chain": "nat_%s" % chain, "expr": [{"jump": {"target": "nat_%s_%s" % (chain, dispatch_suffix)}}]}}}) for chain in IPTABLES_TO_NFT_HOOK["filter"].keys(): default_rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME, "name": "filter_%s" % chain, "type": "filter", "hook": "%s" % IPTABLES_TO_NFT_HOOK["filter"][chain][0], "prio": IPTABLES_TO_NFT_HOOK["filter"][chain][1]}}}) # filter, INPUT default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": None}]}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "INPUT", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": None}]}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "INPUT", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": None}]}}}) for dispatch_suffix in ["POLICIES_pre", "ZONES_SOURCE", "ZONES", "POLICIES_post"] if self._fw._allow_zone_drifting else ["POLICIES_pre", "ZONES", "POLICIES_post"]: default_rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME, "name": "filter_%s_%s" % ("INPUT", dispatch_suffix)}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "INPUT", "expr": [{"jump": {"target": "filter_%s_%s" % ("INPUT", dispatch_suffix)}}]}}}) if log_denied != "off": default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, self._pkttype_match_fragment(log_denied), {"log": {"prefix": "STATE_INVALID_DROP: "}}]}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": None}]}}}) if log_denied != "off": default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "INPUT", "expr": [self._pkttype_match_fragment(log_denied), {"log": {"prefix": "FINAL_REJECT: "}}]}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "INPUT", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}) # filter, FORWARD default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": None}]}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "FORWARD", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": None}]}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "FORWARD", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": None}]}}}) for dispatch_suffix in ["POLICIES_pre"]: default_rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME, "name": "filter_%s_%s" % ("FORWARD", dispatch_suffix)}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "FORWARD", "expr": [{"jump": {"target": "filter_%s_%s" % ("FORWARD", dispatch_suffix)}}]}}}) for direction in ["IN", "OUT"]: for dispatch_suffix in ["ZONES_SOURCE", "ZONES"] if self._fw._allow_zone_drifting else ["ZONES"]: default_rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME, "name": "filter_%s_%s_%s" % ("FORWARD", direction, dispatch_suffix)}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "FORWARD", "expr": [{"jump": {"target": "filter_%s_%s_%s" % ("FORWARD", direction, dispatch_suffix)}}]}}}) for dispatch_suffix in ["POLICIES_post"]: default_rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME, "name": "filter_%s_%s" % ("FORWARD", dispatch_suffix)}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "FORWARD", "expr": [{"jump": {"target": "filter_%s_%s" % ("FORWARD", dispatch_suffix)}}]}}}) if log_denied != "off": default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, self._pkttype_match_fragment(log_denied), {"log": {"prefix": "STATE_INVALID_DROP: "}}]}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": None}]}}}) if log_denied != "off": default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "FORWARD", "expr": [self._pkttype_match_fragment(log_denied), {"log": {"prefix": "FINAL_REJECT: "}}]}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "FORWARD", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}) # filter, OUTPUT default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "OUTPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": None}]}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_OUTPUT", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "lo"}}, {"accept": None}]}}}) for dispatch_suffix in ["POLICIES_pre"]: default_rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME, "name": "filter_%s_%s" % ("OUTPUT", dispatch_suffix)}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "OUTPUT", "expr": [{"jump": {"target": "filter_%s_%s" % ("OUTPUT", dispatch_suffix)}}]}}}) for dispatch_suffix in ["POLICIES_post"]: default_rules.append({"add": {"chain": {"family": "inet", "table": TABLE_NAME, "name": "filter_%s_%s" % ("OUTPUT", dispatch_suffix)}}}) default_rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s" % "OUTPUT", "expr": [{"jump": {"target": "filter_%s_%s" % ("OUTPUT", dispatch_suffix)}}]}}}) return default_rules def get_zone_table_chains(self, table): if table == "filter": return ["INPUT", "FORWARD_IN", "FORWARD_OUT"] if table == "mangle": return ["PREROUTING"] if table == "nat": return ["PREROUTING", "POSTROUTING"] return [] def build_policy_ingress_egress_rules(self, enable, policy, table, chain, ingress_interfaces, egress_interfaces, ingress_sources, egress_sources, family="inet"): # nat tables need to use ip/ip6 family if table == "nat" and family == "inet": rules = [] rules.extend(self.build_policy_ingress_egress_rules(enable, policy, table, chain, ingress_interfaces, egress_interfaces, ingress_sources, egress_sources, family="ip")) rules.extend(self.build_policy_ingress_egress_rules(enable, policy, table, chain, ingress_interfaces, egress_interfaces, ingress_sources, egress_sources, family="ip6")) return rules p_obj = self._fw.policy.get_policy(policy) chain_suffix = "pre" if p_obj.priority < 0 else "post" isSNAT = True if (table == "nat" and chain == "POSTROUTING") else False _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX, isSNAT) ingress_fragments = [] egress_fragments = [] if ingress_interfaces: ingress_fragments.append({"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": {"set": list(ingress_interfaces)}}}) if egress_interfaces: egress_fragments.append({"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": {"set": list(egress_interfaces)}}}) ipv_to_family = {"ipv4": "ip", "ipv6": "ip6"} if ingress_sources: for src in ingress_sources: # skip if this source doesn't apply to the current family. if table == "nat": ipv = self._fw.zone.check_source(src) if ipv in ipv_to_family and family != ipv_to_family[ipv]: continue ingress_fragments.append(self._rule_addr_fragment("saddr", src)) if egress_sources: for dst in egress_sources: # skip if this source doesn't apply to the current family. if table == "nat": ipv = self._fw.zone.check_source(dst) if ipv in ipv_to_family and family != ipv_to_family[ipv]: continue egress_fragments.append(self._rule_addr_fragment("daddr", dst)) def _generate_policy_dispatch_rule(ingress_fragment, egress_fragment): expr_fragments = [] if ingress_fragment: expr_fragments.append(ingress_fragment) if egress_fragment: expr_fragments.append(egress_fragment) expr_fragments.append({"jump": {"target": "%s_%s" % (table, _policy)}}) rule = {"family": family, "table": TABLE_NAME, "chain": "%s_%s_POLICIES_%s" % (table, chain, chain_suffix), "expr": expr_fragments} rule.update(self._policy_priority_fragment(p_obj)) if enable: return {"add": {"rule": rule}} else: return {"delete": {"rule": rule}} rules = [] if ingress_fragments: # zone --> [zone, ANY, HOST] for ingress_fragment in ingress_fragments: if egress_fragments: # zone --> zone for egress_fragment in egress_fragments: rules.append(_generate_policy_dispatch_rule(ingress_fragment, egress_fragment)) elif table =="nat" and egress_sources: # if the egress source is not for the current family (there # are no egress fragments), then avoid creating an invalid # catch all rule. pass else: # zone --> [ANY, HOST] rules.append(_generate_policy_dispatch_rule(ingress_fragment, None)) elif table =="nat" and ingress_sources: # if the ingress source is not for the current family (there are no # ingress fragments), then avoid creating an invalid catch all # rule. pass else: # [ANY, HOST] --> [zone, ANY, HOST] if egress_fragments: # [ANY, HOST] --> zone for egress_fragment in egress_fragments: rules.append(_generate_policy_dispatch_rule(None, egress_fragment)) elif table =="nat" and egress_sources: # if the egress source is not for the current family (there are # no egress fragments), then avoid creating an invalid catch # all rule. pass else: # [ANY, HOST] --> [ANY, HOST] rules.append(_generate_policy_dispatch_rule(None, None)) return rules def build_zone_source_interface_rules(self, enable, zone, policy, interface, table, chain, append=False, family="inet"): # nat tables needs to use ip/ip6 family if table == "nat" and family == "inet": rules = [] rules.extend(self.build_zone_source_interface_rules(enable, zone, policy, interface, table, chain, append, "ip")) rules.extend(self.build_zone_source_interface_rules(enable, zone, policy, interface, table, chain, append, "ip6")) return rules isSNAT = True if (table == "nat" and chain == "POSTROUTING") else False _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX, isSNAT=isSNAT) opt = { "PREROUTING": "iifname", "POSTROUTING": "oifname", "INPUT": "iifname", "FORWARD_IN": "iifname", "FORWARD_OUT": "oifname", "OUTPUT": "oifname", }[chain] if interface[len(interface)-1] == "+": interface = interface[:len(interface)-1] + "*" action = "goto" if interface == "*": expr_fragments = [{action: {"target": "%s_%s" % (table, _policy)}}] else: expr_fragments = [{"match": {"left": {"meta": {"key": opt}}, "op": "==", "right": interface}}, {action: {"target": "%s_%s" % (table, _policy)}}] if enable and not append: verb = "insert" rule = {"family": family, "table": TABLE_NAME, "chain": "%s_%s_ZONES" % (table, chain), "expr": expr_fragments} rule.update(self._zone_interface_fragment()) elif enable: verb = "add" rule = {"family": family, "table": TABLE_NAME, "chain": "%s_%s_ZONES" % (table, chain), "expr": expr_fragments} else: verb = "delete" rule = {"family": family, "table": TABLE_NAME, "chain": "%s_%s_ZONES" % (table, chain), "expr": expr_fragments} if not append: rule.update(self._zone_interface_fragment()) return [{verb: {"rule": rule}}] def build_zone_source_address_rules(self, enable, zone, policy, address, table, chain, family="inet"): # nat tables needs to use ip/ip6 family if table == "nat" and family == "inet": rules = [] if address.startswith("ipset:"): ipset_family = self._set_get_family(address[len("ipset:"):]) else: ipset_family = None if check_address("ipv4", address) or check_mac(address) or ipset_family == "ip": rules.extend(self.build_zone_source_address_rules(enable, zone, policy, address, table, chain, "ip")) if check_address("ipv6", address) or check_mac(address) or ipset_family == "ip6": rules.extend(self.build_zone_source_address_rules(enable, zone, policy, address, table, chain, "ip6")) return rules isSNAT = True if (table == "nat" and chain == "POSTROUTING") else False _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX, isSNAT=isSNAT) add_del = { True: "insert", False: "delete" }[enable] opt = { "PREROUTING": "saddr", "POSTROUTING": "daddr", "INPUT": "saddr", "FORWARD_IN": "saddr", "FORWARD_OUT": "daddr", "OUTPUT": "daddr", }[chain] if self._fw._allow_zone_drifting: zone_dispatch_chain = "%s_%s_ZONES_SOURCE" % (table, chain) else: zone_dispatch_chain = "%s_%s_ZONES" % (table, chain) action = "goto" rule = {"family": family, "table": TABLE_NAME, "chain": zone_dispatch_chain, "expr": [self._rule_addr_fragment(opt, address), {action: {"target": "%s_%s" % (table, _policy)}}]} rule.update(self._zone_source_fragment(zone, address)) return [{add_del: {"rule": rule}}] def build_policy_chain_rules(self, enable, policy, table, chain, family="inet"): # nat tables needs to use ip/ip6 family if table == "nat" and family == "inet": rules = [] rules.extend(self.build_policy_chain_rules(enable, policy, table, chain, "ip")) rules.extend(self.build_policy_chain_rules(enable, policy, table, chain, "ip6")) return rules add_del = { True: "add", False: "delete" }[enable] isSNAT = True if (table == "nat" and chain == "POSTROUTING") else False _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX, isSNAT=isSNAT) rules = [] rules.append({add_del: {"chain": {"family": family, "table": TABLE_NAME, "name": "%s_%s" % (table, _policy)}}}) for chain_suffix in ["pre", "log", "deny", "allow", "post"]: rules.append({add_del: {"chain": {"family": family, "table": TABLE_NAME, "name": "%s_%s_%s" % (table, _policy, chain_suffix)}}}) for chain_suffix in ["pre", "log", "deny", "allow", "post"]: rules.append({add_del: {"rule": {"family": family, "table": TABLE_NAME, "chain": "%s_%s" % (table, _policy), "expr": [{"jump": {"target": "%s_%s_%s" % (table, _policy, chain_suffix)}}]}}}) target = self._fw.policy._policies[policy].target if self._fw.get_log_denied() != "off": if table == "filter": if target in ["REJECT", "%%REJECT%%", "DROP"]: log_suffix = target if target == "%%REJECT%%": log_suffix = "REJECT" rules.append({add_del: {"rule": {"family": family, "table": TABLE_NAME, "chain": "%s_%s" % (table, _policy), "expr": [self._pkttype_match_fragment(self._fw.get_log_denied()), {"log": {"prefix": "\"filter_%s_%s: \"" % (_policy, log_suffix)}}]}}}) if table == "filter" and \ target in ["ACCEPT", "REJECT", "%%REJECT%%", "DROP"]: if target in ["%%REJECT%%", "REJECT"]: target_fragment = self._reject_fragment() else: target_fragment = {target.lower(): None} rules.append({add_del: {"rule": {"family": family, "table": TABLE_NAME, "chain": "%s_%s" % (table, _policy), "expr": [target_fragment]}}}) if not enable: rules.reverse() return rules def _pkttype_match_fragment(self, pkttype): if pkttype == "all": return {} elif pkttype in ["unicast", "broadcast", "multicast"]: return {"match": {"left": {"meta": {"key": "pkttype"}}, "op": "==", "right": pkttype}} raise FirewallError(INVALID_RULE, "Invalid pkttype \"%s\"", pkttype) def _reject_types_fragment(self, reject_type): frags = { # REJECT_TYPES : "icmp-host-prohibited" : {"reject": {"type": "icmp", "expr": "host-prohibited"}}, "host-prohib" : {"reject": {"type": "icmp", "expr": "host-prohibited"}}, "icmp-net-prohibited" : {"reject": {"type": "icmp", "expr": "net-prohibited"}}, "net-prohib" : {"reject": {"type": "icmp", "expr": "net-prohibited"}}, "icmp-admin-prohibited" : {"reject": {"type": "icmp", "expr": "admin-prohibited"}}, "admin-prohib" : {"reject": {"type": "icmp", "expr": "admin-prohibited"}}, "icmp6-adm-prohibited" : {"reject": {"type": "icmpv6", "expr": "admin-prohibited"}}, "adm-prohibited" : {"reject": {"type": "icmpv6", "expr": "admin-prohibited"}}, "icmp-net-unreachable" : {"reject": {"type": "icmp", "expr": "net-unreachable"}}, "net-unreach" : {"reject": {"type": "icmp", "expr": "net-unreachable"}}, "icmp-host-unreachable" : {"reject": {"type": "icmp", "expr": "host-unreachable"}}, "host-unreach" : {"reject": {"type": "icmp", "expr": "host-unreachable"}}, "icmp-port-unreachable" : {"reject": {"type": "icmp", "expr": "port-unreachable"}}, "icmp6-port-unreachable" : {"reject": {"type": "icmpv6", "expr": "port-unreachable"}}, "port-unreach" : {"reject": {"type": "icmpx", "expr": "port-unreachable"}}, "icmp-proto-unreachable" : {"reject": {"type": "icmp", "expr": "prot-unreachable"}}, "proto-unreach" : {"reject": {"type": "icmp", "expr": "prot-unreachable"}}, "icmp6-addr-unreachable" : {"reject": {"type": "icmpv6", "expr": "addr-unreachable"}}, "addr-unreach" : {"reject": {"type": "icmpv6", "expr": "addr-unreachable"}}, "icmp6-no-route" : {"reject": {"type": "icmpv6", "expr": "no-route"}}, "no-route" : {"reject": {"type": "icmpv6", "expr": "no-route"}}, "tcp-reset" : {"reject": {"type": "tcp reset"}}, "tcp-rst" : {"reject": {"type": "tcp reset"}}, } return frags[reject_type] def _reject_fragment(self): return {"reject": {"type": "icmpx", "expr": "admin-prohibited"}} def _icmp_match_fragment(self): return {"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}} def _rich_rule_limit_fragment(self, limit): if not limit: return {} rich_to_nft = { "s" : "second", "m" : "minute", "h" : "hour", "d" : "day", } rate, duration = limit.value_parse() d = { "rate": rate, "per": rich_to_nft[duration], } burst = limit.burst_parse() if burst is not None: d["burst"] = burst return {"limit": d} def _rich_rule_chain_suffix(self, rich_rule): if type(rich_rule.element) in [Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock]: # These are special and don't have an explicit action pass elif rich_rule.action: if type(rich_rule.action) not in [Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark]: raise FirewallError(INVALID_RULE, "Unknown action %s" % type(rich_rule.action)) else: raise FirewallError(INVALID_RULE, "No rule action specified.") if rich_rule.priority == 0: if type(rich_rule.element) in [Rich_Masquerade, Rich_ForwardPort] or \ type(rich_rule.action) in [Rich_Accept, Rich_Mark]: return "allow" elif type(rich_rule.element) in [Rich_IcmpBlock] or \ type(rich_rule.action) in [Rich_Reject, Rich_Drop]: return "deny" elif rich_rule.priority < 0: return "pre" else: return "post" def _rich_rule_chain_suffix_from_log(self, rich_rule): if not rich_rule.log and not rich_rule.audit: raise FirewallError(INVALID_RULE, "Not log or audit") if rich_rule.priority == 0: return "log" elif rich_rule.priority < 0: return "pre" else: return "post" def _zone_interface_fragment(self): return {"%%ZONE_INTERFACE%%": None} def _zone_source_fragment(self, zone, address): if check_single_address("ipv6", address): address = normalizeIP6(address) elif check_address("ipv6", address): addr_split = address.split("/") address = normalizeIP6(addr_split[0]) + "/" + addr_split[1] return {"%%ZONE_SOURCE%%": {"zone": zone, "address": address}} def _policy_priority_fragment(self, policy): return {"%%POLICY_PRIORITY%%": policy.priority} def _rich_rule_priority_fragment(self, rich_rule): if not rich_rule or rich_rule.priority == 0: return {} return {"%%RICH_RULE_PRIORITY%%": rich_rule.priority} def _rich_rule_log(self, policy, rich_rule, enable, table, expr_fragments): if not rich_rule.log: return {} _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "add", False: "delete" }[enable] chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule) log_options = {} if rich_rule.log.prefix: log_options["prefix"] = "%s" % rich_rule.log.prefix if rich_rule.log.level: level = "warn" if "warning" == rich_rule.log.level else rich_rule.log.level log_options["level"] = "%s" % level rule = {"family": "inet", "table": TABLE_NAME, "chain": "%s_%s_%s" % (table, _policy, chain_suffix), "expr": expr_fragments + [self._rich_rule_limit_fragment(rich_rule.log.limit), {"log": log_options}]} rule.update(self._rich_rule_priority_fragment(rich_rule)) return {add_del: {"rule": rule}} def _rich_rule_audit(self, policy, rich_rule, enable, table, expr_fragments): if not rich_rule.audit: return {} _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "add", False: "delete" }[enable] chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule) rule = {"family": "inet", "table": TABLE_NAME, "chain": "%s_%s_%s" % (table, _policy, chain_suffix), "expr": expr_fragments + [self._rich_rule_limit_fragment(rich_rule.audit.limit), {"log": {"level": "audit"}}]} rule.update(self._rich_rule_priority_fragment(rich_rule)) return {add_del: {"rule": rule}} def _rich_rule_action(self, policy, rich_rule, enable, table, expr_fragments): if not rich_rule.action: return {} _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "add", False: "delete" }[enable] chain_suffix = self._rich_rule_chain_suffix(rich_rule) chain = "%s_%s_%s" % (table, _policy, chain_suffix) if type(rich_rule.action) == Rich_Accept: rule_action = {"accept": None} elif type(rich_rule.action) == Rich_Reject: if rich_rule.action.type: rule_action = self._reject_types_fragment(rich_rule.action.type) else: rule_action = {"reject": None} elif type(rich_rule.action) == Rich_Drop: rule_action = {"drop": None} elif type(rich_rule.action) == Rich_Mark: table = "mangle" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) chain = "%s_%s_%s" % (table, _policy, chain_suffix) value = rich_rule.action.set.split("/") if len(value) > 1: rule_action = {"mangle": {"key": {"meta": {"key": "mark"}}, "value": {"^": [{"&": [{"meta": {"key": "mark"}}, value[1]]}, value[0]]}}} else: rule_action = {"mangle": {"key": {"meta": {"key": "mark"}}, "value": value[0]}} else: raise FirewallError(INVALID_RULE, "Unknown action %s" % type(rich_rule.action)) rule = {"family": "inet", "table": TABLE_NAME, "chain": chain, "expr": expr_fragments + [self._rich_rule_limit_fragment(rich_rule.action.limit), rule_action]} rule.update(self._rich_rule_priority_fragment(rich_rule)) return {add_del: {"rule": rule}} def _rule_addr_fragment(self, addr_field, address, invert=False): if address.startswith("ipset:"): return self._set_match_fragment(address[len("ipset:"):], True if "daddr" == addr_field else False, invert) else: if check_mac(address): family = "ether" elif check_single_address("ipv4", address): family = "ip" elif check_address("ipv4", address): family = "ip" normalized_address = ipaddress.IPv4Network(address, strict=False) address = {"prefix": {"addr": normalized_address.network_address.compressed, "len": normalized_address.prefixlen}} elif check_single_address("ipv6", address): family = "ip6" address = normalizeIP6(address) else: family = "ip6" addr_len = address.split("/") address = {"prefix": {"addr": normalizeIP6(addr_len[0]), "len": int(addr_len[1])}} return {"match": {"left": {"payload": {"protocol": family, "field": addr_field}}, "op": "!=" if invert else "==", "right": address}} def _rich_rule_family_fragment(self, rich_family): if not rich_family: return {} if rich_family not in ["ipv4", "ipv6"]: raise FirewallError(INVALID_RULE, "Invalid family" % rich_family) return {"match": {"left": {"meta": {"key": "nfproto"}}, "op": "==", "right": rich_family}} def _rich_rule_destination_fragment(self, rich_dest): if not rich_dest: return {} if rich_dest.addr: address = rich_dest.addr elif rich_dest.ipset: address = "ipset:" + rich_dest.ipset return self._rule_addr_fragment("daddr", address, invert=rich_dest.invert) def _rich_rule_source_fragment(self, rich_source): if not rich_source: return {} if rich_source.addr: address = rich_source.addr elif hasattr(rich_source, "mac") and rich_source.mac: address = rich_source.mac elif hasattr(rich_source, "ipset") and rich_source.ipset: address = "ipset:" + rich_source.ipset return self._rule_addr_fragment("saddr", address, invert=rich_source.invert) def _port_fragment(self, port): range = getPortRange(port) if isinstance(range, int) and range < 0: raise FirewallError(INVALID_PORT) elif len(range) == 1: return range[0] else: return {"range": [range[0], range[1]]} def build_policy_ports_rules(self, enable, policy, proto, port, destination=None, rich_rule=None): add_del = { True: "add", False: "delete" }[enable] table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) expr_fragments = [] if rich_rule: expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family)) if destination: expr_fragments.append(self._rule_addr_fragment("daddr", destination)) if rich_rule: expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination)) expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) expr_fragments.append({"match": {"left": {"payload": {"protocol": proto, "field": "dport"}}, "op": "==", "right": self._port_fragment(port)}}) if not rich_rule or type(rich_rule.action) != Rich_Mark: expr_fragments.append({"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}) rules = [] if rich_rule: rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments)) rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments)) rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments)) else: rules.append({add_del: {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "%s_%s_allow" % (table, _policy), "expr": expr_fragments + [{"accept": None}]}}}) return rules def build_policy_protocol_rules(self, enable, policy, protocol, destination=None, rich_rule=None): add_del = { True: "add", False: "delete" }[enable] table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) expr_fragments = [] if rich_rule: expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family)) if destination: expr_fragments.append(self._rule_addr_fragment("daddr", destination)) if rich_rule: expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination)) expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) expr_fragments.append({"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": protocol}}) if not rich_rule or type(rich_rule.action) != Rich_Mark: expr_fragments.append({"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}) rules = [] if rich_rule: rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments)) rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments)) rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments)) else: rules.append({add_del: {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "%s_%s_allow" % (table, _policy), "expr": expr_fragments + [{"accept": None}]}}}) return rules def build_policy_source_ports_rules(self, enable, policy, proto, port, destination=None, rich_rule=None): add_del = { True: "add", False: "delete" }[enable] table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) expr_fragments = [] if rich_rule: expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family)) if destination: expr_fragments.append(self._rule_addr_fragment("daddr", destination)) if rich_rule: expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination)) expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) expr_fragments.append({"match": {"left": {"payload": {"protocol": proto, "field": "sport"}}, "op": "==", "right": self._port_fragment(port)}}) if not rich_rule or type(rich_rule.action) != Rich_Mark: expr_fragments.append({"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}) rules = [] if rich_rule: rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments)) rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments)) rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments)) else: rules.append({add_del: {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "%s_%s_allow" % (table, _policy), "expr": expr_fragments + [{"accept": None}]}}}) return rules def build_policy_helper_ports_rules(self, enable, policy, proto, port, destination, helper_name, module_short_name): table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "add", False: "delete" }[enable] rules = [] if enable: rules.append({"add": {"ct helper": {"family": "inet", "table": TABLE_NAME, "name": "helper-%s-%s" % (helper_name, proto), "type": module_short_name, "protocol": proto}}}) expr_fragments = [] if destination: expr_fragments.append(self._rule_addr_fragment("daddr", destination)) expr_fragments.append({"match": {"left": {"payload": {"protocol": proto, "field": "dport"}}, "op": "==", "right": self._port_fragment(port)}}) expr_fragments.append({"ct helper": "helper-%s-%s" % (helper_name, proto)}) rules.append({add_del: {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s_allow" % (_policy), "expr": expr_fragments}}}) return rules def build_zone_forward_rules(self, enable, zone, policy, table, interface=None, source=None): add_del = { True: "add", False: "delete" }[enable] _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) rules = [] if interface: if interface[len(interface)-1] == "+": interface = interface[:len(interface)-1] + "*" expr = [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": interface}}, {"accept": None}] else: # source expr = [self._rule_addr_fragment("daddr", source), {"accept": None}] rule = {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s_allow" % (_policy), "expr": expr} rules.append({add_del: {"rule": rule}}) return rules def _build_policy_masquerade_nat_rules(self, enable, policy, family, rich_rule=None): table = "nat" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX, isSNAT=True) add_del = { True: "add", False: "delete" }[enable] expr_fragments = [] if rich_rule: expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination)) expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) chain_suffix = self._rich_rule_chain_suffix(rich_rule) else: chain_suffix = "allow" rule = {"family": family, "table": TABLE_NAME, "chain": "nat_%s_%s" % (_policy, chain_suffix), "expr": expr_fragments + [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "!=", "right": "lo"}}, {"masquerade": None}]} rule.update(self._rich_rule_priority_fragment(rich_rule)) return [{add_del: {"rule": rule}}] def build_policy_masquerade_rules(self, enable, policy, rich_rule=None): # nat tables needs to use ip/ip6 family rules = [] if rich_rule and (rich_rule.family and rich_rule.family == "ipv6" or rich_rule.source and check_address("ipv6", rich_rule.source.addr)): rules.extend(self._build_policy_masquerade_nat_rules(enable, policy, "ip6", rich_rule)) elif rich_rule and (rich_rule.family and rich_rule.family == "ipv4" or rich_rule.source and check_address("ipv4", rich_rule.source.addr)): rules.extend(self._build_policy_masquerade_nat_rules(enable, policy, "ip", rich_rule)) else: rules.extend(self._build_policy_masquerade_nat_rules(enable, policy, "ip", rich_rule)) table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "add", False: "delete" }[enable] expr_fragments = [] if rich_rule: expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination)) expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) chain_suffix = self._rich_rule_chain_suffix(rich_rule) else: chain_suffix = "allow" rule = {"family": "inet", "table": TABLE_NAME, "chain": "filter_%s_%s" % (_policy, chain_suffix), "expr": expr_fragments + [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": None}]} rule.update(self._rich_rule_priority_fragment(rich_rule)) rules.append({add_del: {"rule": rule}}) return rules def _build_policy_forward_port_nat_rules(self, enable, policy, port, protocol, toaddr, toport, family, rich_rule=None): table = "nat" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "add", False: "delete" }[enable] expr_fragments = [] if rich_rule: expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination)) expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) chain_suffix = self._rich_rule_chain_suffix(rich_rule) else: chain_suffix = "allow" expr_fragments.append({"match": {"left": {"payload": {"protocol": protocol, "field": "dport"}}, "op": "==", "right": self._port_fragment(port)}}) if toaddr: if check_single_address("ipv6", toaddr): toaddr = normalizeIP6(toaddr) if toport and toport != "": expr_fragments.append({"dnat": {"addr": toaddr, "port": self._port_fragment(toport)}}) else: expr_fragments.append({"dnat": {"addr": toaddr}}) else: expr_fragments.append({"redirect": {"port": self._port_fragment(toport)}}) rule = {"family": family, "table": TABLE_NAME, "chain": "nat_%s_%s" % (_policy, chain_suffix), "expr": expr_fragments} rule.update(self._rich_rule_priority_fragment(rich_rule)) return [{add_del: {"rule": rule}}] def build_policy_forward_port_rules(self, enable, policy, port, protocol, toport, toaddr, rich_rule=None): rules = [] if rich_rule and (rich_rule.family and rich_rule.family == "ipv6" or toaddr and check_single_address("ipv6", toaddr)): rules.extend(self._build_policy_forward_port_nat_rules(enable, policy, port, protocol, toaddr, toport, "ip6", rich_rule)) elif rich_rule and (rich_rule.family and rich_rule.family == "ipv4" or toaddr and check_single_address("ipv4", toaddr)): rules.extend(self._build_policy_forward_port_nat_rules(enable, policy, port, protocol, toaddr, toport, "ip", rich_rule)) else: if toaddr and check_single_address("ipv6", toaddr): rules.extend(self._build_policy_forward_port_nat_rules(enable, policy, port, protocol, toaddr, toport, "ip6", rich_rule)) else: rules.extend(self._build_policy_forward_port_nat_rules(enable, policy, port, protocol, toaddr, toport, "ip", rich_rule)) return rules def _icmp_types_fragments(self, protocol, type, code=None): fragments = [{"match": {"left": {"payload": {"protocol": protocol, "field": "type"}}, "op": "==", "right": type}}] if code is not None: fragments.append({"match": {"left": {"payload": {"protocol": protocol, "field": "code"}}, "op": "==", "right": code}}) return fragments def _icmp_types_to_nft_fragments(self, ipv, icmp_type): if ipv == "ipv4" and icmp_type in ICMP_TYPES: _type, _code, _omit_code = ICMP_TYPES[icmp_type] return self._icmp_types_fragments("icmp", _type, None if _omit_code else _code) elif ipv == "ipv6" and icmp_type in ICMPV6_TYPES: _type, _code, _omit_code = ICMPV6_TYPES[icmp_type] return self._icmp_types_fragments("icmpv6", _type, None if _omit_code else _code) else: raise FirewallError(INVALID_ICMPTYPE, "ICMP type '%s' not supported by %s for %s" % (icmp_type, self.name, ipv)) def build_policy_icmp_block_rules(self, enable, policy, ict, rich_rule=None): table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "add", False: "delete" }[enable] if rich_rule and rich_rule.ipvs: ipvs = rich_rule.ipvs elif ict.destination: ipvs = [] if "ipv4" in ict.destination: ipvs.append("ipv4") if "ipv6" in ict.destination: ipvs.append("ipv6") else: ipvs = ["ipv4", "ipv6"] rules = [] for ipv in ipvs: if self._fw.policy.query_icmp_block_inversion(policy): final_chain = "%s_%s_allow" % (table, _policy) target_fragment = {"accept": None} else: final_chain = "%s_%s_deny" % (table, _policy) target_fragment = self._reject_fragment() expr_fragments = [] if rich_rule: expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family)) expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination)) expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) expr_fragments.extend(self._icmp_types_to_nft_fragments(ipv, ict.name)) if rich_rule: rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments)) rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments)) if rich_rule.action: rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments)) else: chain_suffix = self._rich_rule_chain_suffix(rich_rule) rule = {"family": "inet", "table": TABLE_NAME, "chain": "%s_%s_%s" % (table, _policy, chain_suffix), "expr": expr_fragments + [self._reject_fragment()]} rule.update(self._rich_rule_priority_fragment(rich_rule)) rules.append({add_del: {"rule": rule}}) else: if self._fw.get_log_denied() != "off" and not self._fw.policy.query_icmp_block_inversion(policy): rules.append({add_del: {"rule": {"family": "inet", "table": TABLE_NAME, "chain": final_chain, "expr": (expr_fragments + [self._pkttype_match_fragment(self._fw.get_log_denied()), {"log": {"prefix": "\"%s_%s_ICMP_BLOCK: \"" % (table, policy)}}])}}}) rules.append({add_del: {"rule": {"family": "inet", "table": TABLE_NAME, "chain": final_chain, "expr": expr_fragments + [target_fragment]}}}) return rules def build_policy_icmp_block_inversion_rules(self, enable, policy): table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) rules = [] add_del = { True: "add", False: "delete" }[enable] if self._fw.policy.query_icmp_block_inversion(policy): target_fragment = self._reject_fragment() else: target_fragment = {"accept": None} # WARN: The "index" used here must be kept in sync with # build_policy_chain_rules() # rules.append({add_del: {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "%s_%s" % (table, _policy), "index": 4, "expr": [self._icmp_match_fragment(), target_fragment]}}}) if self._fw.get_log_denied() != "off" and self._fw.policy.query_icmp_block_inversion(policy): rules.append({add_del: {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "%s_%s" % (table, _policy), "index": 4, "expr": [self._icmp_match_fragment(), self._pkttype_match_fragment(self._fw.get_log_denied()), {"log": {"prefix": "%s_%s_ICMP_BLOCK: " % (table, policy)}}]}}}) return rules def build_rpfilter_rules(self, log_denied=False): rules = [] expr_fragments = [{"match": {"left": {"meta": {"key": "nfproto"}}, "op": "==", "right": "ipv6"}}, {"match": {"left": {"fib": {"flags": ["saddr", "iif", "mark"], "result": "oif"}}, "op": "==", "right": False}}] if log_denied != "off": expr_fragments.append({"log": {"prefix": "rpfilter_DROP: "}}) expr_fragments.append({"drop": None}) rules.append({"insert": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_PREROUTING", "expr": expr_fragments}}}) # RHBZ#1058505, RHBZ#1575431 (bug in kernel 4.16-4.17) rules.append({"insert": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_PREROUTING", "expr": [{"match": {"left": {"payload": {"protocol": "icmpv6", "field": "type"}}, "op": "==", "right": {"set": ["nd-router-advert", "nd-neighbor-solicit"]}}}, {"accept": None}]}}}) return rules def build_rfc3964_ipv4_rules(self): daddr_set = ["::0.0.0.0/96", # IPv4 compatible "::ffff:0.0.0.0/96", # IPv4 mapped "2002:0000::/24", # 0.0.0.0/8 (the system has no address assigned yet) "2002:0a00::/24", # 10.0.0.0/8 (private) "2002:7f00::/24", # 127.0.0.0/8 (loopback) "2002:ac10::/28", # 172.16.0.0/12 (private) "2002:c0a8::/32", # 192.168.0.0/16 (private) "2002:a9fe::/32", # 169.254.0.0/16 (IANA Assigned DHCP link-local) "2002:e000::/19", # 224.0.0.0/4 (multicast), 240.0.0.0/4 (reserved and broadcast) ] daddr_set = [{"prefix": {"addr": x.split("/")[0], "len": int(x.split("/")[1])}} for x in daddr_set] expr_fragments = [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"set": daddr_set}}}] if self._fw._log_denied in ["unicast", "all"]: expr_fragments.append({"log": {"prefix": "RFC3964_IPv4_REJECT: "}}) expr_fragments.append(self._reject_types_fragment("addr-unreach")) rules = [] # WARN: index must be kept in sync with build_default_rules() rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_OUTPUT", "index": 1, "expr": expr_fragments}}}) rules.append({"add": {"rule": {"family": "inet", "table": TABLE_NAME, "chain": "filter_FORWARD", "index": 2, "expr": expr_fragments}}}) return rules def build_policy_rich_source_destination_rules(self, enable, policy, rich_rule): table = "filter" expr_fragments = [] expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family)) expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination)) expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) rules = [] rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments)) rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments)) rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments)) return rules def is_ipv_supported(self, ipv): if ipv in ["ipv4", "ipv6", "eb"]: return True return False def _set_type_list(self, ipv, type): ipv_addr = { "ipv4" : "ipv4_addr", "ipv6" : "ipv6_addr", } types = { "hash:ip" : ipv_addr[ipv], "hash:ip,port" : [ipv_addr[ipv], "inet_proto", "inet_service"], "hash:ip,port,ip" : [ipv_addr[ipv], "inet_proto", "inet_service", ipv_addr[ipv]], "hash:ip,port,net" : [ipv_addr[ipv], "inet_proto", "inet_service", ipv_addr[ipv]], "hash:ip,mark" : [ipv_addr[ipv], "mark"], "hash:net" : ipv_addr[ipv], "hash:net,net" : [ipv_addr[ipv], ipv_addr[ipv]], "hash:net,port" : [ipv_addr[ipv], "inet_proto", "inet_service"], "hash:net,port,net" : [ipv_addr[ipv], "inet_proto", "inet_service", ipv_addr[ipv]], "hash:net,iface" : [ipv_addr[ipv], "ifname"], "hash:mac" : "ether_addr", } if type in types: return types[type] else: raise FirewallError(INVALID_TYPE, "ipset type name '%s' is not valid" % type) def build_set_create_rules(self, name, type, options=None): if options and "family" in options and options["family"] == "inet6": ipv = "ipv6" else: ipv = "ipv4" set_dict = {"table": TABLE_NAME, "name": name, "type": self._set_type_list(ipv, type)} # Some types need the interval flag for t in type.split(":")[1].split(","): if t in ["ip", "net", "port"]: set_dict["flags"] = ["interval"] break if options: if "timeout" in options: set_dict["timeout"] = options["timeout"] if "maxelem" in options: set_dict["size"] = options["maxelem"] rules = [] for family in ["inet", "ip", "ip6"]: rule_dict = {"family": family} rule_dict.update(set_dict) rules.append({"add": {"set": rule_dict}}) return rules def set_create(self, name, type, options=None): rules = self.build_set_create_rules(name, type, options) self.set_rules(rules, self._fw.get_log_denied()) def set_destroy(self, name): for family in ["inet", "ip", "ip6"]: rule = {"delete": {"set": {"family": family, "table": TABLE_NAME, "name": name}}} self.set_rule(rule, self._fw.get_log_denied()) def _set_match_fragment(self, name, match_dest, invert=False): type_format = self._fw.ipset.get_ipset(name).type.split(":")[1].split(",") fragments = [] for i in range(len(type_format)): if type_format[i] == "port": fragments.append({"meta": {"key": "l4proto"}}) fragments.append({"payload": {"protocol": "th", "field": "dport" if match_dest else "sport"}}) elif type_format[i] in ["ip", "net", "mac"]: fragments.append({"payload": {"protocol": self._set_get_family(name), "field": "daddr" if match_dest else "saddr"}}) elif type_format[i] == "iface": fragments.append({"meta": {"key": "iifname" if match_dest else "oifname"}}) elif type_format[i] == "mark": fragments.append({"meta": {"key": "mark"}}) else: raise FirewallError("Unsupported ipset type for match fragment: %s" % (type_format[i])) return {"match": {"left": {"concat": fragments} if len(type_format) > 1 else fragments[0], "op": "!=" if invert else "==", "right": "@" + name}} def _set_entry_fragment(self, name, entry): # convert something like # 1.2.3.4,sctp:8080 (type hash:ip,port) # to # ["1.2.3.4", "sctp", "8080"] obj = self._fw.ipset.get_ipset(name) type_format = obj.type.split(":")[1].split(",") entry_tokens = entry.split(",") if len(type_format) != len(entry_tokens): raise FirewallError(INVALID_ENTRY, "Number of values does not match ipset type.") fragment = [] for i in range(len(type_format)): if type_format[i] == "port": try: index = entry_tokens[i].index(":") except ValueError: # no protocol means default tcp fragment.append("tcp") port_str = entry_tokens[i] else: fragment.append(entry_tokens[i][:index]) port_str = entry_tokens[i][index+1:] try: index = port_str.index("-") except ValueError: fragment.append(port_str) else: fragment.append({"range": [port_str[:index], port_str[index+1:]]}) elif type_format[i] in ["ip", "net"]: if '-' in entry_tokens[i]: fragment.append({"range": entry_tokens[i].split('-') }) else: try: index = entry_tokens[i].index("/") except ValueError: addr = entry_tokens[i] if "family" in obj.options and obj.options["family"] == "inet6": addr = normalizeIP6(addr) fragment.append(addr) else: addr = entry_tokens[i][:index] if "family" in obj.options and obj.options["family"] == "inet6": addr = normalizeIP6(addr) fragment.append({"prefix": {"addr": addr, "len": int(entry_tokens[i][index+1:])}}) else: fragment.append(entry_tokens[i]) return [{"concat": fragment}] if len(type_format) > 1 else fragment def build_set_add_rules(self, name, entries): rules = [] elements = [] if not isinstance(entries, (list, tuple)): entries = [entries] for element in entries: elements.extend(self._set_entry_fragment(name, element)) for family in ["inet", "ip", "ip6"]: rules.append({"add": {"element": {"family": family, "table": TABLE_NAME, "name": name, "elem": elements}}}) return rules def set_add(self, name, entry): rules = self.build_set_add_rules(name, entry) self.set_rules(rules, self._fw.get_log_denied()) def set_delete(self, name, entry): element = self._set_entry_fragment(name, entry) for family in ["inet", "ip", "ip6"]: rule = {"delete": {"element": {"family": family, "table": TABLE_NAME, "name": name, "elem": element}}} self.set_rule(rule, self._fw.get_log_denied()) def build_set_flush_rules(self, name): rules = [] for family in ["inet", "ip", "ip6"]: rule = {"flush": {"set": {"family": family, "table": TABLE_NAME, "name": name}}} rules.append(rule) return rules def set_flush(self, name): rules = self.build_set_flush_rules(name) self.set_rules(rules, self._fw.get_log_denied()) def _set_get_family(self, name): ipset = self._fw.ipset.get_ipset(name) if ipset.type == "hash:mac": family = "ether" elif ipset.options and "family" in ipset.options \ and ipset.options["family"] == "inet6": family = "ip6" else: family = "ip" return family def set_restore(self, set_name, type_name, entries, create_options=None, entry_options=None): rules = [] rules.extend(self.build_set_create_rules(set_name, type_name, create_options)) rules.extend(self.build_set_flush_rules(set_name)) # avoid large memory usage by chunking the entries for i in range(0, len(entries), 1000): rules.extend(self.build_set_add_rules(set_name, entries[i : i + 1000])) self.set_rules(rules, self._fw.get_log_denied()) rules.clear() else: self.set_rules(rules, self._fw.get_log_denied()) PK!Ɵ8C$$ helper.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """The helper maxnamelen""" HELPER_MAXNAMELEN = 32 PK!َ:  fw_ifcfg.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2010-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """Functions to search for and change ifcfg files""" __all__ = [ "search_ifcfg_of_interface", "ifcfg_set_zone_of_interface" ] import os import os.path from firewall import config from firewall.core.logger import log from firewall.core.io.ifcfg import ifcfg def search_ifcfg_of_interface(interface): """search ifcfg file for the interface in config.IFCFGDIR""" # Return quickly if config.IFCFGDIR does not exist if not os.path.exists(config.IFCFGDIR): return None for filename in sorted(os.listdir(config.IFCFGDIR)): if not filename.startswith("ifcfg-"): continue for ignored in [ ".bak", ".orig", ".rpmnew", ".rpmorig", ".rpmsave", "-range" ]: if filename.endswith(ignored): continue if "." in filename: continue ifcfg_file = ifcfg("%s/%s" % (config.IFCFGDIR, filename)) ifcfg_file.read() if ifcfg_file.get("DEVICE") == interface: return ifcfg_file # Wasn't found above, so assume filename matches the device we want filename = "%s/ifcfg-%s" % (config.IFCFGDIR, interface) if os.path.exists(filename): ifcfg_file = ifcfg(filename) ifcfg_file.read() return ifcfg_file return None def ifcfg_set_zone_of_interface(zone, interface): """Set zone (ZONE=) in the ifcfg file that uses the interface (DEVICE=)""" if zone is None: zone = "" ifcfg_file = search_ifcfg_of_interface(interface) if ifcfg_file is not None and ifcfg_file.get("ZONE") != zone and not \ (ifcfg_file.get("ZONE") is None and zone == ""): log.debug1("Setting ZONE=%s in '%s'" % (zone, ifcfg_file.filename)) ifcfg_file.set("ZONE", zone) ifcfg_file.write() PK!gg fw_service.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "FirewallService" ] from firewall import errors from firewall.errors import FirewallError class FirewallService(object): def __init__(self, fw): self._fw = fw self._services = { } def __repr__(self): return '%s(%r)' % (self.__class__, self._services) def cleanup(self): self._services.clear() # zones def get_services(self): return sorted(self._services.keys()) def check_service(self, service): if service not in self._services: raise FirewallError(errors.INVALID_SERVICE, service) def get_service(self, service): self.check_service(service) return self._services[service] def add_service(self, obj): self._services[obj.name] = obj def remove_service(self, service): self.check_service(service) del self._services[service] PK!,:<<io/__init__.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2012 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # fix xmlplus to be compatible with the python xml sax parser and python 3 # by adding __contains__ to xml.sax.xmlreader.AttributesImpl import xml if "_xmlplus" in xml.__file__: from xml.sax.xmlreader import AttributesImpl if not hasattr(AttributesImpl, "__contains__"): # this is missing: def __AttributesImpl__contains__(self, name): return name in getattr(self, "_attrs") # add it using the name __contains__ setattr(AttributesImpl, "__contains__", __AttributesImpl__contains__) from xml.sax.saxutils import XMLGenerator if not hasattr(XMLGenerator, "_write"): # this is missing: def __XMLGenerator_write(self, text): getattr(self, "_out").write(text) # add it using the name _write setattr(XMLGenerator, "_write", __XMLGenerator_write) PK!/ io/helper.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "Helper", "helper_reader", "helper_writer" ] import xml.sax as sax import os import io import shutil from firewall import config from firewall.functions import u2b_if_py2 from firewall.core.io.io_object import PY2, IO_Object, \ IO_Object_ContentHandler, IO_Object_XMLGenerator, check_port, \ check_tcpudp from firewall.core.logger import log from firewall import errors from firewall.errors import FirewallError class Helper(IO_Object): IMPORT_EXPORT_STRUCTURE = ( ( "version", "" ), # s ( "short", "" ), # s ( "description", "" ), # s ( "family", "", ), # s ( "module", "", ), # s ( "ports", [ ( "", "" ), ], ), # a(ss) ) DBUS_SIGNATURE = '(sssssa(ss))' ADDITIONAL_ALNUM_CHARS = [ "-", "." ] PARSER_REQUIRED_ELEMENT_ATTRS = { "short": None, "description": None, "helper": [ "module" ], } PARSER_OPTIONAL_ELEMENT_ATTRS = { "helper": [ "name", "version", "family" ], "port": [ "port", "protocol" ], } def __init__(self): super(Helper, self).__init__() self.version = "" self.short = "" self.description = "" self.module = "" self.family = "" self.ports = [ ] def cleanup(self): self.version = "" self.short = "" self.description = "" self.module = "" self.family = "" del self.ports[:] def encode_strings(self): """ HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.""" self.version = u2b_if_py2(self.version) self.short = u2b_if_py2(self.short) self.description = u2b_if_py2(self.description) self.module = u2b_if_py2(self.module) self.family = u2b_if_py2(self.family) self.ports = [(u2b_if_py2(po),u2b_if_py2(pr)) for (po,pr) in self.ports] def check_ipv(self, ipv): ipvs = [ 'ipv4', 'ipv6' ] if ipv not in ipvs: raise FirewallError(errors.INVALID_IPV, "'%s' not in '%s'" % (ipv, ipvs)) def _check_config(self, config, item, all_config): if item == "ports": for port in config: check_port(port[0]) check_tcpudp(port[1]) elif item == "module": if not config.startswith("nf_conntrack_"): raise FirewallError( errors.INVALID_MODULE, "'%s' does not start with 'nf_conntrack_'" % config) if len(config.replace("nf_conntrack_", "")) < 1: raise FirewallError(errors.INVALID_MODULE, "Module name '%s' too short" % config) # PARSER class helper_ContentHandler(IO_Object_ContentHandler): def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) self.item.parser_check_element_attrs(name, attrs) if name == "helper": if "version" in attrs: self.item.version = attrs["version"] if "family" in attrs: self.item.check_ipv(attrs["family"]) self.item.family = attrs["family"] if "module" in attrs: if not attrs["module"].startswith("nf_conntrack_"): raise FirewallError( errors.INVALID_MODULE, "'%s' does not start with 'nf_conntrack_'" % \ attrs["module"]) if len(attrs["module"].replace("nf_conntrack_", "")) < 1: raise FirewallError( errors.INVALID_MODULE, "Module name '%s' too short" % attrs["module"]) self.item.module = attrs["module"] elif name == "short": pass elif name == "description": pass elif name == "port": check_port(attrs["port"]) check_tcpudp(attrs["protocol"]) entry = (attrs["port"], attrs["protocol"]) if entry not in self.item.ports: self.item.ports.append(entry) else: log.warning("Port '%s/%s' already set, ignoring.", attrs["port"], attrs["protocol"]) def helper_reader(filename, path): helper = Helper() if not filename.endswith(".xml"): raise FirewallError(errors.INVALID_NAME, "'%s' is missing .xml suffix" % filename) helper.name = filename[:-4] helper.check_name(helper.name) helper.filename = filename helper.path = path helper.builtin = False if path.startswith(config.ETC_FIREWALLD) else True helper.default = helper.builtin handler = helper_ContentHandler(helper) parser = sax.make_parser() parser.setContentHandler(handler) name = "%s/%s" % (path, filename) with open(name, "rb") as f: source = sax.InputSource(None) source.setByteStream(f) try: parser.parse(source) except sax.SAXParseException as msg: raise FirewallError(errors.INVALID_HELPER, "not a valid helper file: %s" % \ msg.getException()) del handler del parser if PY2: helper.encode_strings() return helper def helper_writer(helper, path=None): _path = path if path else helper.path if helper.filename: name = "%s/%s" % (_path, helper.filename) else: name = "%s/%s.xml" % (_path, helper.name) if os.path.exists(name): try: shutil.copy2(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) dirpath = os.path.dirname(name) if dirpath.startswith(config.ETC_FIREWALLD) and not os.path.exists(dirpath): if not os.path.exists(config.ETC_FIREWALLD): os.mkdir(config.ETC_FIREWALLD, 0o750) os.mkdir(dirpath, 0o750) f = io.open(name, mode='wt', encoding='UTF-8') handler = IO_Object_XMLGenerator(f) handler.startDocument() # start helper element attrs = {} attrs["module"] = helper.module if helper.version and helper.version != "": attrs["version"] = helper.version if helper.family and helper.family != "": attrs["family"] = helper.family handler.startElement("helper", attrs) handler.ignorableWhitespace("\n") # short if helper.short and helper.short != "": handler.ignorableWhitespace(" ") handler.startElement("short", { }) handler.characters(helper.short) handler.endElement("short") handler.ignorableWhitespace("\n") # description if helper.description and helper.description != "": handler.ignorableWhitespace(" ") handler.startElement("description", { }) handler.characters(helper.description) handler.endElement("description") handler.ignorableWhitespace("\n") # ports for port in helper.ports: handler.ignorableWhitespace(" ") handler.simpleElement("port", { "port": port[0], "protocol": port[1] }) handler.ignorableWhitespace("\n") # end helper element handler.endElement('helper') handler.ignorableWhitespace("\n") handler.endDocument() f.close() del handler PK!Cc=== io/direct.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import xml.sax as sax import os import io import shutil from firewall import config from firewall.fw_types import LastUpdatedOrderedDict from firewall.functions import splitArgs, joinArgs, u2b_if_py2 from firewall.core.io.io_object import IO_Object, IO_Object_ContentHandler, \ IO_Object_XMLGenerator from firewall.core.logger import log from firewall.core import ipXtables from firewall.core import ebtables from firewall import errors from firewall.errors import FirewallError class direct_ContentHandler(IO_Object_ContentHandler): def __init__(self, item): IO_Object_ContentHandler.__init__(self, item) self.direct = False def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) self.item.parser_check_element_attrs(name, attrs) if name == "direct": if self.direct: raise FirewallError(errors.PARSE_ERROR, "More than one direct tag.") self.direct = True elif name == "chain": if not self.direct: log.error("Parse Error: chain outside of direct") return ipv = attrs["ipv"] table = attrs["table"] chain = attrs["chain"] self.item.add_chain(u2b_if_py2(ipv), u2b_if_py2(table), u2b_if_py2(chain)) elif name == "rule": if not self.direct: log.error("Parse Error: rule outside of direct") return ipv = attrs["ipv"] if ipv not in [ "ipv4", "ipv6", "eb" ]: raise FirewallError(errors.INVALID_IPV, "'%s' not from {'ipv4'|'ipv6'|'eb'}" % ipv) table = attrs["table"] chain = attrs["chain"] try: priority = int(attrs["priority"]) except ValueError: log.error("Parse Error: %s is not a valid priority" % attrs["priority"]) return self._rule = [ u2b_if_py2(ipv), u2b_if_py2(table), u2b_if_py2(chain), priority ] elif name == "passthrough": if not self.direct: log.error("Parse Error: command outside of direct") return ipv = attrs["ipv"] self._passthrough = [ u2b_if_py2(ipv) ] else: log.error('Unknown XML element %s' % name) return def endElement(self, name): IO_Object_ContentHandler.endElement(self, name) if name == "rule": if self._element: # add arguments self._rule.append([ u2b_if_py2(x) for x in splitArgs(self._element) ]) self.item.add_rule(*self._rule) else: log.error("Error: rule does not have any arguments, ignoring.") self._rule = None elif name == "passthrough": if self._element: # add arguments self._passthrough.append([ u2b_if_py2(x) for x in splitArgs(self._element) ]) self.item.add_passthrough(*self._passthrough) else: log.error("Error: passthrough does not have any arguments, " + "ignoring.") self._passthrough = None class Direct(IO_Object): """ Direct class """ IMPORT_EXPORT_STRUCTURE = ( # chain: [ ipv, table, [ chain ] ] ( "chains", [ ( "", "", "" ), ], ), # a(sss) # rule: [ ipv, table, chain, [ priority, [ arg ] ] ] ( "rules", [ ( "", "", "", 0, [ "" ] ), ], ), # a(sssias) # passthrough: [ ipv, [ [ arg ] ] ] ( "passthroughs", [ ( "", [ "" ]), ], ), # a(sas) ) DBUS_SIGNATURE = '(a(sss)a(sssias)a(sas))' PARSER_REQUIRED_ELEMENT_ATTRS = { "direct": None, "chain": [ "ipv", "table", "chain" ], "rule": [ "ipv", "table", "chain", "priority" ], "passthrough": [ "ipv" ] } PARSER_OPTIONAL_ELEMENT_ATTRS = { } def __init__(self, filename): super(Direct, self).__init__() self.filename = filename self.chains = LastUpdatedOrderedDict() self.rules = LastUpdatedOrderedDict() self.passthroughs = LastUpdatedOrderedDict() def _check_config(self, conf, item, all_conf): pass # check arg lists def export_config(self): ret = [ ] x = [ ] for key in self.chains: for chain in self.chains[key]: x.append(tuple(list(key) + list([chain]))) ret.append(x) x = [ ] for key in self.rules: for rule in self.rules[key]: x.append(tuple((key[0], key[1], key[2], rule[0], list(rule[1])))) ret.append(x) x = [ ] for key in self.passthroughs: for rule in self.passthroughs[key]: x.append(tuple((key, list(rule)))) ret.append(x) return tuple(ret) def import_config(self, conf): self.cleanup() self.check_config(conf) for i,(element,dummy) in enumerate(self.IMPORT_EXPORT_STRUCTURE): if element == "chains": for x in conf[i]: self.add_chain(*x) if element == "rules": for x in conf[i]: self.add_rule(*x) if element == "passthroughs": for x in conf[i]: self.add_passthrough(*x) def cleanup(self): self.chains.clear() self.rules.clear() self.passthroughs.clear() def output(self): print("chains") for key in self.chains: print(" (%s, %s): %s" % (key[0], key[1], ",".join(self.chains[key]))) print("rules") for key in self.rules: print(" (%s, %s, %s):" % (key[0], key[1], key[2])) for (priority,args) in self.rules[key]: print(" (%d, ('%s'))" % (priority, "','".join(args))) print("passthroughs") for key in self.passthroughs: print(" %s:" % (key)) for args in self.passthroughs[key]: print(" ('%s')" % ("','".join(args))) def _check_ipv(self, ipv): ipvs = ['ipv4', 'ipv6', 'eb'] if ipv not in ipvs: raise FirewallError(errors.INVALID_IPV, "'%s' not in '%s'" % (ipv, ipvs)) def _check_ipv_table(self, ipv, table): self._check_ipv(ipv) tables = ipXtables.BUILT_IN_CHAINS.keys() if ipv in ['ipv4', 'ipv6'] \ else ebtables.BUILT_IN_CHAINS.keys() if table not in tables: raise FirewallError(errors.INVALID_TABLE, "'%s' not in '%s'" % (table, tables)) # chains def add_chain(self, ipv, table, chain): self._check_ipv_table(ipv, table) key = (ipv, table) if key not in self.chains: self.chains[key] = [ ] if chain not in self.chains[key]: self.chains[key].append(chain) else: log.warning("Chain '%s' for table '%s' with ipv '%s' " % \ (chain, table, ipv) + "already in list, ignoring") def remove_chain(self, ipv, table, chain): self._check_ipv_table(ipv, table) key = (ipv, table) if key in self.chains and chain in self.chains[key]: self.chains[key].remove(chain) if len(self.chains[key]) == 0: del self.chains[key] else: raise ValueError( \ "Chain '%s' with table '%s' with ipv '%s' not in list" % \ (chain, table, ipv)) def query_chain(self, ipv, table, chain): self._check_ipv_table(ipv, table) key = (ipv, table) return (key in self.chains and chain in self.chains[key]) def get_chains(self, ipv, table): self._check_ipv_table(ipv, table) key = (ipv, table) if key in self.chains: return self.chains[key] else: raise ValueError("No chains for table '%s' with ipv '%s'" % \ (table, ipv)) def get_all_chains(self): return self.chains # rules def add_rule(self, ipv, table, chain, priority, args): self._check_ipv_table(ipv, table) key = (ipv, table, chain) if key not in self.rules: self.rules[key] = LastUpdatedOrderedDict() value = (priority, tuple(args)) if value not in self.rules[key]: self.rules[key][value] = priority else: log.warning("Rule '%s' for table '%s' and chain '%s' " % \ ("',".join(args), table, chain) + "with ipv '%s' and priority %d " % (ipv, priority) + "already in list, ignoring") def remove_rule(self, ipv, table, chain, priority, args): self._check_ipv_table(ipv, table) key = (ipv, table, chain) value = (priority, tuple(args)) if key in self.rules and value in self.rules[key]: del self.rules[key][value] if len(self.rules[key]) == 0: del self.rules[key] else: raise ValueError("Rule '%s' for table '%s' and chain '%s' " % \ ("',".join(args), table, chain) + \ "with ipv '%s' and priority %d not in list" % (ipv, priority)) def remove_rules(self, ipv, table, chain): self._check_ipv_table(ipv, table) key = (ipv, table, chain) if key in self.rules: for value in self.rules[key].keys(): del self.rules[key][value] if len(self.rules[key]) == 0: del self.rules[key] def query_rule(self, ipv, table, chain, priority, args): self._check_ipv_table(ipv, table) key = (ipv, table, chain) value = (priority, tuple(args)) return (key in self.rules and value in self.rules[key]) def get_rules(self, ipv, table, chain): self._check_ipv_table(ipv, table) key = (ipv, table, chain) if key in self.rules: return self.rules[key] else: raise ValueError("No rules for table '%s' and chain '%s' " %\ (table, chain) + "with ipv '%s'" % (ipv)) def get_all_rules(self): return self.rules # # passthrough # def add_passthrough(self, ipv, args): self._check_ipv(ipv) if ipv not in self.passthroughs: self.passthroughs[ipv] = [ ] if args not in self.passthroughs[ipv]: self.passthroughs[ipv].append(args) else: log.warning("Passthrough '%s' for ipv '%s'" % \ ("',".join(args), ipv) + "already in list, ignoring") def remove_passthrough(self, ipv, args): self._check_ipv(ipv) if ipv in self.passthroughs and args in self.passthroughs[ipv]: self.passthroughs[ipv].remove(args) if len(self.passthroughs[ipv]) == 0: del self.passthroughs[ipv] else: raise ValueError("Passthrough '%s' for ipv '%s'" % \ ("',".join(args), ipv) + "not in list") def query_passthrough(self, ipv, args): self._check_ipv(ipv) return ipv in self.passthroughs and args in self.passthroughs[ipv] def get_passthroughs(self, ipv): self._check_ipv(ipv) if ipv in self.passthroughs: return self.passthroughs[ipv] else: raise ValueError("No passthroughs for ipv '%s'" % (ipv)) def get_all_passthroughs(self): return self.passthroughs # read def read(self): self.cleanup() if not self.filename.endswith(".xml"): raise FirewallError(errors.INVALID_NAME, "'%s' is missing .xml suffix" % self.filename) handler = direct_ContentHandler(self) parser = sax.make_parser() parser.setContentHandler(handler) with open(self.filename, "rb") as f: source = sax.InputSource(None) source.setByteStream(f) try: parser.parse(source) except sax.SAXParseException as msg: raise FirewallError(errors.INVALID_TYPE, "Not a valid file: %s" % \ msg.getException()) def write(self): if os.path.exists(self.filename): try: shutil.copy2(self.filename, "%s.old" % self.filename) except Exception as msg: raise IOError("Backup of '%s' failed: %s" % (self.filename, msg)) if not os.path.exists(config.ETC_FIREWALLD): os.mkdir(config.ETC_FIREWALLD, 0o750) f = io.open(self.filename, mode='wt', encoding='UTF-8') handler = IO_Object_XMLGenerator(f) handler.startDocument() # start whitelist element handler.startElement("direct", { }) handler.ignorableWhitespace("\n") # chains for key in self.chains: (ipv, table) = key for chain in self.chains[key]: handler.ignorableWhitespace(" ") handler.simpleElement("chain", { "ipv": ipv, "table": table, "chain": chain }) handler.ignorableWhitespace("\n") # rules for key in self.rules: (ipv, table, chain) = key for (priority, args) in self.rules[key]: if len(args) < 1: continue handler.ignorableWhitespace(" ") handler.startElement("rule", { "ipv": ipv, "table": table, "chain": chain, "priority": "%d" % priority }) handler.ignorableWhitespace(sax.saxutils.escape(joinArgs(args))) handler.endElement("rule") handler.ignorableWhitespace("\n") # passthroughs for ipv in self.passthroughs: for args in self.passthroughs[ipv]: if len(args) < 1: continue handler.ignorableWhitespace(" ") handler.startElement("passthrough", { "ipv": ipv }) handler.ignorableWhitespace(sax.saxutils.escape(joinArgs(args))) handler.endElement("passthrough") handler.ignorableWhitespace("\n") # end zone element handler.endElement("direct") handler.ignorableWhitespace("\n") handler.endDocument() f.close() del handler PK!m io/ifcfg.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """ifcfg file parser""" __all__ = [ "ifcfg" ] import os.path import io import tempfile import shutil from firewall.core.logger import log from firewall.functions import b2u, u2b, PY2 class ifcfg(object): def __init__(self, filename): self._config = { } self._deleted = [ ] self.filename = filename self.clear() def clear(self): self._config = { } self._deleted = [ ] def cleanup(self): self._config.clear() def get(self, key): return self._config.get(key.strip()) def set(self, key, value): _key = b2u(key.strip()) self._config[_key] = b2u(value.strip()) if _key in self._deleted: self._deleted.remove(_key) def __str__(self): s = "" for (key, value) in self._config.items(): if s: s += '\n' s += '%s=%s' % (key, value) return u2b(s) if PY2 else s # load self.filename def read(self): self.clear() try: f = open(self.filename, "r") except Exception as msg: log.error("Failed to load '%s': %s", self.filename, msg) raise for line in f: if not line: break line = line.strip() if len(line) < 1 or line[0] in ['#', ';']: continue # get key/value pair pair = [ x.strip() for x in line.split("=", 1) ] if len(pair) != 2: continue if len(pair[1]) >= 2 and \ pair[1].startswith('"') and pair[1].endswith('"'): pair[1] = pair[1][1:-1] if pair[1] == '': continue elif self._config.get(pair[0]) is not None: log.warning("%s: Duplicate option definition: '%s'", self.filename, line.strip()) continue self._config[pair[0]] = pair[1] f.close() def write(self): if len(self._config) < 1: # no changes: nothing to do return # handled keys done = [ ] try: temp_file = tempfile.NamedTemporaryFile( mode='wt', prefix="%s." % os.path.basename(self.filename), dir=os.path.dirname(self.filename), delete=False) except Exception as msg: log.error("Failed to open temporary file: %s" % msg) raise modified = False empty = False try: f = io.open(self.filename, mode='rt', encoding='UTF-8') except Exception as msg: if os.path.exists(self.filename): log.error("Failed to open '%s': %s" % (self.filename, msg)) raise else: f = None else: for line in f: if not line: break # remove newline line = line.strip("\n") if len(line) < 1: if not empty: temp_file.write(u"\n") empty = True elif line[0] == '#': empty = False temp_file.write(line) temp_file.write(u"\n") else: p = line.split("=", 1) if len(p) != 2: empty = False temp_file.write(line+u"\n") continue key = p[0].strip() value = p[1].strip() if len(value) >= 2 and \ value.startswith('"') and value.endswith('"'): value = value[1:-1] # check for modified key/value pairs if key not in done: if key in self._config and self._config[key] != value: empty = False temp_file.write(u'%s=%s\n' % (key, self._config[key])) modified = True elif key in self._deleted: modified = True else: empty = False temp_file.write(line+u"\n") done.append(key) else: modified = True # write remaining key/value pairs if len(self._config) > 0: for (key, value) in self._config.items(): if key in done: continue if not empty: empty = True temp_file.write(u'%s=%s\n' % (key, value)) modified = True if f: f.close() temp_file.close() if not modified: # not modified: remove tempfile os.remove(temp_file.name) return # make backup if os.path.exists(self.filename): try: shutil.copy2(self.filename, "%s.bak" % self.filename) except Exception as msg: os.remove(temp_file.name) raise IOError("Backup of '%s' failed: %s" % (self.filename, msg)) # copy tempfile try: shutil.move(temp_file.name, self.filename) except Exception as msg: os.remove(temp_file.name) raise IOError("Failed to create '%s': %s" % (self.filename, msg)) else: os.chmod(self.filename, 0o600) PK!5io/icmptype.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "IcmpType", "icmptype_reader", "icmptype_writer" ] import xml.sax as sax import os import io import shutil from firewall import config from firewall.functions import u2b_if_py2 from firewall.core.io.io_object import PY2, IO_Object, \ IO_Object_ContentHandler, IO_Object_XMLGenerator from firewall.core.logger import log from firewall import errors from firewall.errors import FirewallError class IcmpType(IO_Object): IMPORT_EXPORT_STRUCTURE = ( ( "version", "" ), # s ( "short", "" ), # s ( "description", "" ), # s ( "destination", [ "", ], ), # as ) DBUS_SIGNATURE = '(sssas)' ADDITIONAL_ALNUM_CHARS = [ "_", "-" ] PARSER_REQUIRED_ELEMENT_ATTRS = { "short": None, "description": None, "icmptype": None, } PARSER_OPTIONAL_ELEMENT_ATTRS = { "icmptype": [ "name", "version" ], "destination": [ "ipv4", "ipv6" ], } def __init__(self): super(IcmpType, self).__init__() self.version = "" self.short = "" self.description = "" self.destination = [ ] def cleanup(self): self.version = "" self.short = "" self.description = "" del self.destination[:] def encode_strings(self): """ HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.""" self.version = u2b_if_py2(self.version) self.short = u2b_if_py2(self.short) self.description = u2b_if_py2(self.description) self.destination = [u2b_if_py2(m) for m in self.destination] def _check_config(self, config, item, all_config): if item == "destination": for destination in config: if destination not in [ "ipv4", "ipv6" ]: raise FirewallError(errors.INVALID_DESTINATION, "'%s' not from {'ipv4'|'ipv6'}" % \ destination) # PARSER class icmptype_ContentHandler(IO_Object_ContentHandler): def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) self.item.parser_check_element_attrs(name, attrs) if name == "icmptype": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'" % attrs["name"]) if "version" in attrs: self.item.version = attrs["version"] elif name == "short": pass elif name == "description": pass elif name == "destination": for x in [ "ipv4", "ipv6" ]: if x in attrs and \ attrs[x].lower() in [ "yes", "true" ]: self.item.destination.append(str(x)) def icmptype_reader(filename, path): icmptype = IcmpType() if not filename.endswith(".xml"): raise FirewallError(errors.INVALID_NAME, "%s is missing .xml suffix" % filename) icmptype.name = filename[:-4] icmptype.check_name(icmptype.name) icmptype.filename = filename icmptype.path = path icmptype.builtin = False if path.startswith(config.ETC_FIREWALLD) else True icmptype.default = icmptype.builtin handler = icmptype_ContentHandler(icmptype) parser = sax.make_parser() parser.setContentHandler(handler) name = "%s/%s" % (path, filename) with open(name, "rb") as f: source = sax.InputSource(None) source.setByteStream(f) try: parser.parse(source) except sax.SAXParseException as msg: raise FirewallError(errors.INVALID_ICMPTYPE, "not a valid icmptype file: %s" % \ msg.getException()) del handler del parser if PY2: icmptype.encode_strings() return icmptype def icmptype_writer(icmptype, path=None): _path = path if path else icmptype.path if icmptype.filename: name = "%s/%s" % (_path, icmptype.filename) else: name = "%s/%s.xml" % (_path, icmptype.name) if os.path.exists(name): try: shutil.copy2(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) dirpath = os.path.dirname(name) if dirpath.startswith(config.ETC_FIREWALLD) and not os.path.exists(dirpath): if not os.path.exists(config.ETC_FIREWALLD): os.mkdir(config.ETC_FIREWALLD, 0o750) os.mkdir(dirpath, 0o750) f = io.open(name, mode='wt', encoding='UTF-8') handler = IO_Object_XMLGenerator(f) handler.startDocument() # start icmptype element attrs = {} if icmptype.version and icmptype.version != "": attrs["version"] = icmptype.version handler.startElement("icmptype", attrs) handler.ignorableWhitespace("\n") # short if icmptype.short and icmptype.short != "": handler.ignorableWhitespace(" ") handler.startElement("short", { }) handler.characters(icmptype.short) handler.endElement("short") handler.ignorableWhitespace("\n") # description if icmptype.description and icmptype.description != "": handler.ignorableWhitespace(" ") handler.startElement("description", { }) handler.characters(icmptype.description) handler.endElement("description") handler.ignorableWhitespace("\n") # destination if icmptype.destination: handler.ignorableWhitespace(" ") attrs = { } for x in icmptype.destination: attrs[x] = "yes" handler.simpleElement("destination", attrs) handler.ignorableWhitespace("\n") # end icmptype element handler.endElement('icmptype') handler.ignorableWhitespace("\n") handler.endDocument() f.close() del handler PK!z! -io/__pycache__/functions.cpython-36.opt-1.pycnu[3 Yjy@sddlZddlmZddlmZddlmZddlmZddl m Z ddl m Z ddl mZdd lmZdd lmZdd lmZdd lmZdd lmZddZdS)N)config) FirewallError)FirewallConfig) zone_reader)service_reader) ipset_reader)icmptype_reader) helper_reader) policy_reader)Direct)LockdownWhitelist)firewalld_confc -Cs|t|}t|jtjtjgdt|jtjtj gdt |j tj tj gdt|jtjtjgdt|jtjtjgdt|jtjtjgdd}x |jD]}x||dD]}tjj|sqxttj|D]}|j dryD||d||}|d kr||_!|j"|j#||d|Wqt$k rT}zt$|j%d ||j&fWYdd}~Xqt'k r}zt'd ||fWYdd}~XqXqWqWqWtjj(tj)r:y$t*tj)}|j+|j,|j-Wnpt$k r}zt$|j%d tj)|j&fWYdd}~Xn6t'k r8}zt'd tj)|fWYdd}~XnXtjj(tj.ry$t/tj.}|j+|j,|j-Wnpt$k r}zt$|j%d tj.|j&fWYdd}~Xn6t'k r}zt'd tj.|fWYdd}~XnXtjj(tj0rxyt1tj0}|j+Wnpt$k rB}zt$|j%d tj0|j&fWYdd}~Xn6t'k rv}zt'd tj0|fWYdd}~XnXdS) N)readeradddirs)ZipsethelperZicmptypeZservicezonepolicyrz.xmlrrrrz'%s': %s)rr)2rrZ add_ipsetrZFIREWALLD_IPSETSZETC_FIREWALLD_IPSETSr Z add_helperZFIREWALLD_HELPERSZETC_FIREWALLD_HELPERSrZ add_icmptypeZFIREWALLD_ICMPTYPESZETC_FIREWALLD_ICMPTYPESrZ add_serviceZFIREWALLD_SERVICESZETC_FIREWALLD_SERVICESrZadd_zoneZFIREWALLD_ZONESZETC_FIREWALLD_ZONESr Zadd_policy_objectZFIREWALLD_POLICIESZETC_FIREWALLD_POLICIESkeysospathisdirsortedlistdirendswith fw_configZcheck_config_dictZexport_config_dictrcodemsg ExceptionisfileZFIREWALLD_DIRECTr read check_configZ export_configZLOCKDOWN_WHITELISTr ZFIREWALLD_CONFr ) fwrZreadersrZ_dirfileobjerrorrr&/usr/lib/python3.6/functions.pyr!&sz   &. ($ ($  (r!)rZfirewallrZfirewall.errorsrZfirewall.core.fw_configrZfirewall.core.io.zonerZfirewall.core.io.servicerZfirewall.core.io.ipsetrZfirewall.core.io.icmptyperZfirewall.core.io.helperr Zfirewall.core.io.policyr Zfirewall.core.io.directr Z#firewall.core.io.lockdown_whitelistr Zfirewall.core.io.firewalld_confr r!r&r&r&r's            PK!?xR`/`/'io/__pycache__/io_object.cpython-36.pycnu[3 Yj5@sdZddddddddgZd d ljZd d ljjZd d lZd d lZd d lm Z d d l m Z d d l m Z d dl mZd dlmZejdkZGdddeZGdddeZGdddeZGdddeZGdddejjZGdddejZddZddZddZ ddZ!d S)z5Generic io_object handler, io specific check methods.PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator check_port check_tcpudpcheck_protocol check_addressN) OrderedDict) functions)b2u)errors) FirewallError3c@s|eZdZdZfZdZgZiZiZddZ ddZ ddZ d d Z d d Z d dZddZddZddZddZddZdS)rz; Abstract IO_Object as base for icmptype, service and zone z()cCs"d|_d|_d|_d|_d|_dS)NF)filenamepathnamedefaultZbuiltin)selfr/usr/lib/python3.6/io_object.py__init__2s zIO_Object.__init__cCs6g}x(|jD]}|jtjt||dq Wt|S)Nr )IMPORT_EXPORT_STRUCTUREappendcopydeepcopygetattrtuple)rretxrrr export_config9s zIO_Object.export_configcCsXi}tdd|jD}x:|D]2}t||sAsz0IO_Object.export_config_dict..)dictrr isinstanceboolrr)rconf type_formatskeyrrrexport_config_dict?s  zIO_Object.export_config_dictcCs|j|xt|jD]~\}\}}t||tr~g}t}x,||D] }||krD|j||j|qDW~t||t j |qt||t j ||qWdS)N) check_config enumeraterr&listsetraddsetattrrr)rr(ielementZdummyZ_confZ_setr rrr import_configGs  zIO_Object.import_configc Cs~|j|xn|D]f}t||s0ttjdj|t||tr`t||tt j t j ||qt||t j ||qWdS)Nz-Internal error. '{}' is not a valid attribute) check_config_dicthasattrrr Z UNKNOWN_ERRORformatr&r.r1r fromkeysrr)rr(r*rrrimport_config_dictWs   "zIO_Object.import_config_dictcCszt|ts(ttjd|tdt|ft|dkr@ttjdx4|D],}|j rF||j krFttjd||fqFWdS)Nz'%s' not of type %s, but %srr"zname can't be emptyz'%s' is not allowed in '%s') r&strrr INVALID_TYPEtypelenZ INVALID_NAMEisalnumADDITIONAL_ALNUM_CHARS)rrcharrrr check_namecs     zIO_Object.check_namecCsjt|t|jkr0ttjdt|t|jfi}x&t|jD]\}\}}||||<q@W|j|dS)Nz structure size mismatch %d != %d)r=rrr r;r-r5)rr(Z conf_dictr2r yrrrr,pszIO_Object.check_configcCsrtdd|jD}xX|D]P}|dd|jDkrDttjdj||j|||||j||||qWdS)NcSsg|]}|d|dfqS)r r"r)r#r rrrr$|sz/IO_Object.check_config_dict..cSsg|] \}}|qSrr)r#r rBrrrr$~szoption '{}' is not valid)r%rrr ZINVALID_OPTIONr7_check_config_structure _check_config)rr(r)r*rrrr5{s  zIO_Object.check_config_dictcCsdS)Nr)rZdummy1Zdummy2Zdummy3rrrrDszIO_Object._check_configc Cs`t|t|s,ttjd|t|t|ft|trrt|dkrRttjd|x|D]}|j||dqXWnt|trt|t|krttjd|t|fxt |D]\}}|j|||qWnt|t r\t|j d\}}xn|j D]b\}}t|t|s,ttjd|t|t|ft|t|sttjd|t|t|fqWdS)Nz'%s' not of type %s, but %sr"zlen('%s') != 1r zlen('%s') != %d) r&r<rr r;r.r=rCrr-r%items) rr(Z structurer r2valueZskeyZsvaluer*rrrrCs8      z!IO_Object._check_config_structurecCs|j}d}||jkrdd}|j|dk rdx:|j|D],}||krL|j|q4ttjd||fq4W||jkrd}x$|j|D]}||kr~|j|q~W|sttjd|x |D]}ttjd||fqWdS)NFTzMissing attribute %s for %szUnexpected element %sz%s: Unexpected attribute %s)ZgetNamesPARSER_REQUIRED_ELEMENT_ATTRSremoverr Z PARSE_ERRORPARSER_OPTIONAL_ELEMENT_ATTRS)rrattrsZ_attrsfoundr rrrparser_check_element_attrss,     z$IO_Object.parser_check_element_attrsN)__name__ __module__ __qualname____doc__rZDBUS_SIGNATUREr?rGrIrr!r+r4r9rAr,r5rDrCrLrrrrr)s"   !cs$eZdZfddZddZZS)UnexpectedElementErrorcstt|j||_dS)N)superrQrr)rr) __class__rrrszUnexpectedElementError.__init__cCs d|jS)NzUnexpected element '%s')r)rrrr__str__szUnexpectedElementError.__str__)rMrNrOrrT __classcell__rr)rSrrQs rQcs$eZdZfddZddZZS)MissingAttributeErrorcstt|j||_||_dS)N)rRrVrr attribute)rrrW)rSrrrszMissingAttributeError.__init__cCsd|j|jfS)Nz$Element '%s': missing '%s' attribute)rrW)rrrrrTszMissingAttributeError.__str__)rMrNrOrrTrUrr)rSrrVs rVcs$eZdZfddZddZZS)UnexpectedAttributeErrorcstt|j||_||_dS)N)rRrXrrrW)rrrW)rSrrrsz!UnexpectedAttributeError.__init__cCsd|j|jfS)Nz'Element '%s': unexpected attribute '%s')rrW)rrrrrTsz UnexpectedAttributeError.__str__)rMrNrOrrTrUrr)rSrrXs rXc@s4eZdZddZddZddZddZd d Zd S) rcCs||_d|_dS)Nr)item_element)rrYrrrrsz!IO_Object_ContentHandler.__init__cCs d|_dS)Nr)rZ)rrrr startDocumentsz&IO_Object_ContentHandler.startDocumentcCs d|_dS)Nr)rZ)rrrJrrr startElementsz%IO_Object_ContentHandler.startElementcCs*|dkr|j|j_n|dkr&|j|j_dS)Nshort description)rZrYr]r^)rrrrr endElements z#IO_Object_ContentHandler.endElementcCs|j|jdd7_dS)N  )rZreplace)rcontentrrr characterssz#IO_Object_ContentHandler.charactersN)rMrNrOrr[r\r_rdrrrrrs c@s<eZdZddZddZddZddZd d Zd d Zd S)rcCsNtjjj||j|_|j|_ig|_|jd|_ g|_ d|_ d|_ d|_ dS)Nr"zutf-8F)saxhandlerContentHandlerrwrite_writeflushZ_flushZ _ns_contextsZ_current_contextZ_undeclared_ns_mapsZ _encodingZ_pending_start_elementZ_short_empty_elements)routrrrrs zIO_Object_XMLGenerator.__init__cCs*trdd|jD}tjj|||dS)a saxutils.XMLGenerator.startElement() expects name and attrs to be unicode and bad things happen if any of them is (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. cSsi|]\}}t|t|qSr)r )r#rrFrrr sz7IO_Object_XMLGenerator.startElement..N)rrEsaxutils XMLGeneratorr\)rrrJrrrr\sz#IO_Object_XMLGenerator.startElementcCstrX|jdt|x4|jD](\}}|jdt|tjt|fq W|jdnF|jd|x,|jD] \}}|jd|tj|fqpW|jddS)z* slightly modified startElement() N)rrjr rErnZ quoteattr)rrrJrFrrr simpleElements  z$IO_Object_XMLGenerator.simpleElementcCstjj|t|dS)z saxutils.XMLGenerator.endElement() expects name to be unicode and bad things happen if it's (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. N)rnror_r )rrrrrr_sz!IO_Object_XMLGenerator.endElementcCstjj|t|dS)z saxutils.XMLGenerator.characters() expects content to be unicode and bad things happen if it's (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. N)rnrordr )rrcrrrrd%sz!IO_Object_XMLGenerator.characterscCstjj|t|dS)a saxutils.XMLGenerator.ignorableWhitespace() expects content to be unicode and bad things happen if it's (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. N)rnroignorableWhitespacer )rrcrrrrr-sz*IO_Object_XMLGenerator.ignorableWhitespaceN) rMrNrOrr\rqr_rdrrrrrrrs  cCstj|}|dkr$ttjd|n`|dkr>ttjd|nF|dkrXttjd|n,t|dkr|d|dkrttjd|dS) Nzport number in '%s' is too bigr"z'%s' is invalid port rangezport range '%s' is ambiguousr re)r Z getPortRangerr Z INVALID_PORTr=)ZportZ port_rangerrrr5s    cCs|dkrttjd|dS)Ntcpudpsctpdccpz)'%s' not from {'tcp'|'udp'|'sctp'|'dccp'})rurvrwrx)rr INVALID_PROTOCOL)protocolrrrrDscCstj|sttj|dS)N)r Z checkProtocolrr ry)rzrrrrJs cCs$tj||s ttjd||fdS)Nz'%s' is not valid %s address)r rrr Z INVALID_ADDR)ZipvZaddrrrrrNs )"rP__all__Zxml.saxrfZxml.sax.saxutilsrnrsys collectionsr Zfirewallr Zfirewall.functionsr r Zfirewall.errorsrversionrobjectr ExceptionrQrVrXrgrhrrorrrrrrrrrs0           CPK!g22(io/__pycache__/zone.cpython-36.opt-1.pycnu[3 YjM@sdddgZddljZddlZddlZddlZddlmZddlm Z m Z m Z m Z m Z mZmZddlmZmZddlmZmZmZmZdd lmZmZmZmZdd lmZdd lm Z dd lm!Z!dd l"m#Z#GdddeZ$GdddeZ%dddZ&dddZ'dS)Zone zone_reader zone_writerN)config) checkIPnMask checkIP6nMaskcheckInterfaceuniqifymax_zone_name_len u2b_if_py2 check_mac)DEFAULT_ZONE_TARGET ZONE_TARGETS)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator)common_startElementcommon_endElementcommon_check_config common_writer)rich)log)errors) FirewallErrorcsfeZdZdZd@dAdBdCdDd dgfd dEgfd dgfdFd dGgfddgfddgfddgfddgfddHgfdIdJfZdddgZddddgddgdgdgdddgdgddddgddgddddddgdgddZddddgd gd!d"gd#d$gd%d&d'd#d(gd%d'd(gd)d*gd+gd,gd- Zed.d/Z fd0d1Z d2d3Z d4d5Z fd6d7Z fd8d9Zd:d;Zfdd?ZZS)Krz Zone class versionshort descriptionUNUSEDFtargetservicesports icmp_blocks masquerade forward_ports interfacessources rules_str protocols source_portsicmp_block_inversionforward_-/Nnameportprotocolvalueset)rrzoneservicer1z icmp-blockz icmp-typer,z forward-port interfacerulesource destinationr2z source-portrZauditZacceptrejectZdropZmarklimitzicmp-block-inversion immutableZenabledzto-portzto-addrfamilyZpriorityaddressmacinvertipsetprefixleveltypeZburst) r5r$z forward-portr8r9r:rr;r<cCs8x&ttjD]\}\}}||kr |Sq WttjddS)Nz index_of()) enumeraterIMPORT_EXPORT_STRUCTURErrZ UNKNOWN_ERROR)elementiZelZdummyrJ/usr/lib/python3.6/zone.pyindex_ofdsz Zone.index_ofcstt|jd|_d|_d|_d|_t|_g|_ g|_ g|_ g|_ d|_ d|_g|_g|_g|_g|_d|_g|_g|_d|_d|_d|_dS)NrF)superr__init__rrrrr r r!r"r)r#r,r$r%r*r&r' fw_configrulesr(r+combinedapplied)self) __class__rJrKrNks,z Zone.__init__cCsd|_d|_d|_d|_t|_|jdd=|jdd=|jdd=|j dd=d|_ d|_ |j dd=|j dd=|jdd=|jdd=d|_|jdd=|jdd=d|_d|_d|_dS)NrF)rrrrr r r!r"r)r#r,r$r%r*r&r'rOrPr(r+rQrR)rSrJrJrKcleanups*          z Zone.cleanupcCst|j|_t|j|_t|j|_t|j|_dd|jD|_dd|jD|_dd|jD|_dd|jD|_dd|j D|_ dd|j D|_ dd|j D|_ d d|j D|_ d d|j D|_ d d|jD|_d S) z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSsg|] }t|qSrJ)r ).0srJrJrK sz'Zone.encode_strings..cSs g|]\}}t|t|fqSrJ)r )rVpoprrJrJrKrXscSsg|] }t|qSrJ)r )rVrZrJrJrKrXscSsg|] }t|qSrJ)r )rVrIrJrJrKrXscSs0g|](\}}}}t|t|t|t|fqSrJ)r )rVZp1Zp2Zp3Zp4rJrJrKrXscSs g|]\}}t|t|fqSrJ)r )rVrYrZrJrJrKrXscSsg|] }t|qSrJ)r )rVrIrJrJrKrXscSsg|] }t|qSrJ)r )rVrWrJrJrKrXscSsg|] }t|qSrJ)r )rVrWrJrJrKrXscSsg|] }t|qSrJ)r )rVrWrJrJrKrXsN)r rrrr r!r"r)r#r%r*r&r'rPr()rSrJrJrKencode_stringss     zZone.encode_stringscsN|dkr8dd|D|_tt|j|dd|jDntt|j||dS)Nr(cSsg|]}tj|dqS))Zrule_str)rZ Rich_Rule)rVrWrJrJrKrXsz$Zone.__setattr__..cSsg|] }t|qSrJ)str)rVrWrJrJrKrXs)rPrMr __setattr__)rSr0r3)rTrJrKr]s zZone.__setattr__cstt|j}|d=|S)Nr)rMrexport_config_dict)rSZconf)rTrJrKr^szZone.export_config_dictcCsLt|||||dkr.|tkr*ttj|n|dkrxl|D]d}t|sTttj||jr}||j krq||jj |jkrttjdj ||qWqWdS)Nr r&z)interface '{}' already bound to zone '{}'r'zipset:z&source '{}' already bound to zone '{}')rrrrINVALID_TARGETrZINVALID_INTERFACErOZ get_zonesr0Zget_zoner&formatrrr startswith INVALID_ADDRr')rSritemZ all_configr7r5r9rJrJrK _check_configs6       zZone._check_configcstt|j||jdr,ttjd|n|jdrHttjd|n|jddkrhttjd|nnd|kr|d|j d}n|}t |t krttjd|t |t |j f|j r||j jkrttjddS)Nr/z'%s' can't start with '/'z'%s' can't end with '/'zmore than one '/' in '%s'z'Zone of '%s' has %d chars, max is %d %sz+Zones can't have the same name as a policy.)rMr check_namerarr INVALID_NAMEendswithcountfindlenr rQrOZget_policy_objectsZ NAME_CONFLICT)rSr0Z checked_name)rTrJrKrfs,      zZone.check_namec Csd|_d|_d|_d|_d|_x$|jD]}||jkr&|jj|q&Wx$|jD]}||jkrL|jj|qLWx$|jD]}||jkrr|jj|qrWx$|j D]}||j kr|j j|qWx$|j D]}||j kr|j j|qWx$|j D]}||j kr|j j|qW|j rd|_ |j rd|_ x(|jD]}||jkr&|jj|q&Wx(|jD]}||jkrP|jj|qPWx,|jD]"} |jj| |jjt| qzW|jrd|_dS)NTr)rQfilenamerrrr&appendr'r!r"r)r#r,r$r%r*rPr(r\r+) rSr5r7r9r6r1protoZicmpr,r8rJrJrKcombinesL                  z Zone.combine)rr)rr)rr)rF)r r)rr)r$F)rrrr)rr)r+F)r,F)__name__ __module__ __qualname____doc__rGZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRS staticmethodrLrNrUr[r]r^rdrfro __classcell__rJrJ)rTrKr(sx         c@s$eZdZddZddZddZdS)zone_ContentHandlercCs"tj||d|_d|_d|_dS)NF)rrN_rule _rule_errorZ _limit_ok)rSrcrJrJrKrN s zzone_ContentHandler.__init__c Cstj||||jrdS|jj||t|||r6dS|dkrd|krVtjd|dd|krj|d|j_d|krtjd|dd|kr|d}|t krt t j ||dkr|t kr||j_n|d kr|jjrtjd nd |j_n|d krh|jrtjd d |_dSd|kr.tjdd |_dS|d|jjkrT|jjj|dntjd|dn8|dkrf|jr |jjrtjdt|jd |_dSd}d|kr|djd$krd }d}}}d|kr|d}d|kr|d}d|kr|d}tj||||d|j_dSd|krBd|krBtjddSd|krdd|krdtjddSd|kr~tjd|dd|krtjddSd|krt|d rt|d rt|d rt t j|dd|kr$d|d}||jjkr|jjj|ntjd |dd|kr|d}||jjkrT|jjj|ntjd |dn:|d!kr|jjrtjd"nd |j_ntjd#|dSdS)%Nr5r0z'Ignoring deprecated attribute name='%s'rr=z,Ignoring deprecated attribute immutable='%s'r rr,zForward already set, ignoring.Tr7z$Invalid rule: interface use in rule.z Invalid interface: Name missing.z%Interface '%s' already set, ignoring.r9z:Invalid rule: More than one source in rule '%s', ignoring.FrAyestruer?r@rB)rAz$Invalid source: No address no ipset.z"Invalid source: Address and ipset.r>z)Ignoring deprecated attribute family='%s'z+Invalid source: Invertion not allowed here.zipset:%sz"Source '%s' already set, ignoring.zicmp-block-inversionz+Icmp-Block-Inversion already set, ignoring.zUnknown XML element '%s')ryrz)r startElementrxrcZparser_check_element_attrsrrZwarningrrrrr_r r r,rwr&rmr9r\lowerrZ Rich_Sourcerrr rbr'r+) rSr0attrsr rAZaddrr@rBentryrJrJrKr{&s                                       z zone_ContentHandler.startElementcCstj||t||dS)N)r endElementr)rSr0rJrJrKrs zzone_ContentHandler.endElementN)rprqrrrNr{rrJrJrJrKrvsprvFc Cst}|jds ttjd||dd |_|s>|j|j||_||_|j t j rZdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r} zttjd| jWYdd} ~ XnXWdQRX~~tr|j|S) Nz.xmlz'%s' is missing .xml suffixFTz%s/%srbznot a valid zone file: %s)rrhrrrgr0rfrlpathrar ETC_FIREWALLDZbuiltindefaultrvsaxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZ INVALID_ZONEZ getExceptionrr[) rlrZ no_check_namer5handlerparserr0fr9msgrJrJrKrs:        (c Cs\|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|ji}|jr|jd kr|j|d <|jtkr*|j|d <|jd ||jd t||x8t|jD]*} |jd|jdd| i|jd qVWx\t|jD]N} |jdd| kr|jdd| ddin|jdd| i|jd qW|jr |jd|jdi|jd |jr2|jd|jdi|jd |jd |jd |j |j!~dS)Nz%s/%sz %s/%s.xmlz%s.oldzBackup of file '%s' failed: %siZwtzUTF-8)modeencodingrrr r5 z r7r0zipset:r9rBr?zicmp-block-inversionr,)"rrlr0osexistsshutilZcopy2 ExceptionrerrordirnamerarrmkdiriorrZ startDocumentrr r r{ZignorableWhitespacerr r&Z simpleElementr'r+r,rZ endDocumentclose) r5r_pathr0rdirpathrrr}r7r9rJrJrKrs`                     )F)N)(__all__Zxml.saxrrrrZfirewallrZfirewall.functionsrrrr r r r Zfirewall.core.baser rZfirewall.core.io.io_objectrrrrZfirewall.core.io.policyrrrrZ firewall.corerZfirewall.core.loggerrrZfirewall.errorsrrrvrrrJrJrJrKs$   $    x| PK!qޝ&io/__pycache__/icmptype.cpython-36.pycnu[3 Yj@sdddgZddljZddlZddlZddlZddlmZddlm Z ddl m Z m Z m Z mZddlmZdd lmZdd lmZGd dde ZGd d d e ZddZdddZdS)IcmpTypeicmptype_readericmptype_writerN)config) u2b_if_py2)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator)log)errors) FirewallErrorcspeZdZdddddgffZdZddgZd d d d Zd dgd d gdZfddZddZ ddZ ddZ Z S)rversionshort description destinationz(sssas)_-N)rricmptypenameipv4ipv6)rrcs*tt|jd|_d|_d|_g|_dS)Nr)superr__init__rrrr)self) __class__/usr/lib/python3.6/icmptype.pyr8s zIcmpType.__init__cCs"d|_d|_d|_|jdd=dS)Nr)rrrr)rrrrcleanup?szIcmpType.cleanupcCs:t|j|_t|j|_t|j|_dd|jD|_dS)z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSsg|] }t|qSr)r).0mrrr Lsz+IcmpType.encode_strings..N)rrrrr)rrrrencode_stringsEs   zIcmpType.encode_stringscCs2|dkr.x$|D]}|dkrttjd|qWdS)Nrrrz'%s' not from {'ipv4'|'ipv6'})rr)r r ZINVALID_DESTINATION)rritemZ all_configrrrr _check_configNs  zIcmpType._check_config)rr)rr)rr) __name__ __module__ __qualname__ZIMPORT_EXPORT_STRUCTUREZDBUS_SIGNATUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSrrr#r% __classcell__rr)rrr%s    c@seZdZddZdS)icmptype_ContentHandlercCstj||||jj|||dkrTd|kr>tjd|dd|kr|d|j_nT|dkr^nJ|dkrhn@|dkrx6d D].}||krv||jd krv|jjj t |qvWdS)Nrrz'Ignoring deprecated attribute name='%s'rrrrrryestrue)rr)r+r,) r startElementr$Zparser_check_element_attrsr Zwarningrlowerrappendstr)rrattrsxrrrr-Ys"  z$icmptype_ContentHandler.startElementN)r&r'r(r-rrrrr*Xsr*c Cst}|jds ttjd||dd |_|j|j||_||_|j t j rVdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r}zttjd|jWYdd}~XnXWdQRX~~tr|j|S) Nz.xmlz%s is missing .xml suffixFTz%s/%srbznot a valid icmptype file: %s)rendswithr r Z INVALID_NAMErZ check_namefilenamepath startswithr ETC_FIREWALLDZbuiltindefaultr*saxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZINVALID_ICMPTYPEZ getExceptionrr#) r7r8rhandlerparserrfsourcemsgrrrrms8        (c Cs.|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|ji}|jr|jd kr|j|d <|jd ||jd |jrt|jd krt|jd |jdi|j|j|jd|jd |jr|jd kr|jd |jdi|j|j|jd|jd |jr|jd i}x|jD]} d|| <qW|jd||jd |jd |jd |j|j~dS)Nz%s/%sz %s/%s.xmlz%s.oldzBackup of file '%s' failed: %siZwtzUTF-8)modeencodingrrr z rrr+r)r8r7rosexistsshutilZcopy2 Exceptionr errordirnamer9rr:mkdirior=r Z startDocumentrr-ZignorableWhitespacerZ charactersZ endElementrrZ simpleElementZ endDocumentclose) rr8_pathrrCdirpathrAr?r1r2rrrrs\                       )N)__all__Zxml.saxr<rGrNrIZfirewallrZfirewall.functionsrZfirewall.core.io.io_objectrrr r Zfirewall.core.loggerr r Zfirewall.errorsr rr*rrrrrrs       3PK!@*2io/__pycache__/firewalld_conf.cpython-36.opt-1.pycnu[3 Yj5 @s~ddlZddlZddlZddlZddlmZddlmZddl m Z m Z m Z ddddd d d d d ddddg Z GdddeZdS)N)config)log)b2uu2bPY2 DefaultZone MinimalMark CleanupOnExitCleanupModulesOnExitLockdown IPv6_rpfilterIndividualCalls LogDeniedAutomaticHelpersFirewallBackendFlushAllOnReload RFC3964_IPv4AllowZoneDriftingc@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dS)firewalld_confcCsi|_g|_||_|jdS)N)_config_deletedfilenameclear)selfrr$/usr/lib/python3.6/firewalld_conf.py__init__&szfirewalld_conf.__init__cCsi|_g|_dS)N)rr)rrrrr,szfirewalld_conf.clearcCs|jjg|_dS)N)rrr)rrrrcleanup0s zfirewalld_conf.cleanupcCs|jj|jS)N)rgetstrip)rkeyrrrr4szfirewalld_conf.getcCs8t|j}t|j|j|<||jkr4|jj|dS)N)rrrrremove)rr valueZ_keyrrrset7s  zfirewalld_conf.setcCsHd}x2|jjD]$\}}|r$|d7}|d||f7}qWtrDt|S|S)N z%s=%s)ritemsrr)rsr r"rrr__str__=s zfirewalld_conf.__str__cCs|jyt|jd}Wn8tk rR}ztjd|j||jdtj|jdt tj |jdtj rpdnd|jdtj rdnd|jd tj rdnd|jd tjrdnd|jd tjrdnd|jd tj|jd tj|jdtj|jdtjr dnd|jdtjr"dnd|jdtjr:dndWYdd}~XnXx|D]}|shP|j}t|dks\|dd.krq\dd|jdD}t|dkrtjd|jq\nr|dtkrtjd|jq\nN|ddkrtjd|jq\n*|jj|ddk r:tjd|jq\|d|j|d<q\W|j|jdstjdtj|jdt tj|jd}y t|WnPttfk r|dk rtj d |r|ndtj |jdt tj YnX|jd}| s|j!d/krJ|dk r2tj d#|r(|ndtj |jdtj rDdnd|jd}| sj|j!d0kr|dk rtj d$|r|ndtj |jdtj rdnd|jd }| s|j!d1kr|dk rtj d%|r|ndtj |jd tj rdnd|jd }| s"|j!d2kr^|dk rFtj d&|r<|ndtj|jd tjrXdnd|jd }| s~|j!d3kr|dk rtj d'|r|ndtj|jd tjrdnd|jd }| s|tj"kr|dk rtj d(|tj|jd t tj|jd }| s&|j!tj#kr\|dk rJtj d)|r@|ndtj|jd t tj|jd}| s~|j!tj$kr|dk rtj d*|r|ndtj|jdt tj|jd}| s|j!d4kr |dk rtj d+|r|ndtj|jdt tj|jd}| s*|j!d5kr`|dk rNtj d,|rD|ndtj|jdt tj|jd}| s|j!d6kr|dk rtj d-|r|ndtj|jdt tjdS)7NrzFailed to load '%s': %srrr yesnor r r r rrrrrrr#;cSsg|] }|jqSr)r).0xrrr bsz'firewalld_conf.read..=zInvalid option definition: '%s'zInvalid option: '%s'r$zMissing value: '%s'z!Duplicate option definition: '%s'z0DefaultZone is not set, using default value '%s'z7MinimalMark '%s' is not valid, using default value '%d'falsetruez7CleanupOnExit '%s' is not valid, using default value %sz>CleanupModulesOnExit '%s' is not valid, using default value %sz2Lockdown '%s' is not valid, using default value %sz7IPv6_rpfilter '%s' is not valid, using default value %sz9IndividualCalls '%s' is not valid, using default value %sz3LogDenied '%s' is invalid, using default value '%s'z:AutomaticHelpers '%s' is not valid, using default value %sz9FirewallBackend '%s' is not valid, using default value %sz:FlushAllOnReload '%s' is not valid, using default value %sz6RFC3964_IPv4 '%s' is not valid, using default value %sz;AllowZoneDrifting '%s' is not valid, using default value %s)r-r.)r+r4r*r5)r+r4r*r5)r*r5r+r4)r*r5r+r4)r*r5r+r4)r*r5r+r4)r*r5r+r4)r*r5r+r4)%ropenr Exceptionrerrorr#rZ FALLBACK_ZONEstrZFALLBACK_MINIMAL_MARKZFALLBACK_CLEANUP_ON_EXITZ FALLBACK_CLEANUP_MODULES_ON_EXITZFALLBACK_LOCKDOWNZFALLBACK_IPV6_RPFILTERZFALLBACK_INDIVIDUAL_CALLSZFALLBACK_LOG_DENIEDZFALLBACK_AUTOMATIC_HELPERSZFALLBACK_FIREWALL_BACKENDZFALLBACK_FLUSH_ALL_ON_RELOADZFALLBACK_RFC3964_IPV4ZFALLBACK_ALLOW_ZONE_DRIFTINGrlensplit valid_keysrrcloseint ValueError TypeErrorZwarninglowerZLOG_DENIED_VALUESZAUTOMATIC_HELPERS_VALUESZFIREWALL_BACKEND_VALUES)rfmsglineZpairr"rrrreadFs                                       zfirewalld_conf.readc :Cst|jdkrdSg}tjjtjs2tjtjdy.tj ddtjj |j tjj |j dd}Wn2t k r}ztjd|WYdd}~XnXd}d}ytj|j dd d }WnPt k r}z0tjj|j rtjd |j |fnd}WYdd}~Xn6Xx0|D]&}|sP|jd }t|dkrH|s2|jd d }n|ddkrpd}|j||jd n|jd}t|dkrd}|j|d q |dj} |dj} | |kr.| |jkr|j| | krd}|jd| |j| fd }n$| |jkrd }nd}|j|d |j| nd }q Wt|jdkrx^|jjD]P\} } | |krjqT| dkrxqT|s|jd d }|jd| | fd }qTW|r|j|j|stj|jdStjj|j r@ytj|j d|j WnBt k r>}z$tj|jtd|j |fWYdd}~XnXytj|j|j WnBt k r}z$tj|jtd|j |fWYdd}~XnXtj|j ddS)Nr,iZwtz%s.F)modeprefixdirdeletez!Failed to open temporary file: %sZrtzUTF-8)rFencodingzFailed to open '%s': %sr%Trr-r2r3z%s=%s rrz%s.oldzBackup of '%s' failed: %szFailed to create '%s': %si)rr) r:rospathexistsrZ ETC_FIREWALLDmkdirtempfileZNamedTemporaryFilebasenamerdirnamer7rr8ior6rwriter;rappendr&r=r!nameshutilZcopy2IOErrorZmovechmod) rdoneZ temp_filerCZmodifiedemptyrBrDpr r"rrrrSs                  $ $zfirewalld_conf.writeN) __name__ __module__ __qualname__rrrrr#r(rErSrrrrr%s r)Zos.pathrKrRrOrVZfirewallrZfirewall.core.loggerrZfirewall.functionsrrrr<objectrrrrrs  PK!qޝ,io/__pycache__/icmptype.cpython-36.opt-1.pycnu[3 Yj@sdddgZddljZddlZddlZddlZddlmZddlm Z ddl m Z m Z m Z mZddlmZdd lmZdd lmZGd dde ZGd d d e ZddZdddZdS)IcmpTypeicmptype_readericmptype_writerN)config) u2b_if_py2)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator)log)errors) FirewallErrorcspeZdZdddddgffZdZddgZd d d d Zd dgd d gdZfddZddZ ddZ ddZ Z S)rversionshort description destinationz(sssas)_-N)rricmptypenameipv4ipv6)rrcs*tt|jd|_d|_d|_g|_dS)Nr)superr__init__rrrr)self) __class__/usr/lib/python3.6/icmptype.pyr8s zIcmpType.__init__cCs"d|_d|_d|_|jdd=dS)Nr)rrrr)rrrrcleanup?szIcmpType.cleanupcCs:t|j|_t|j|_t|j|_dd|jD|_dS)z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSsg|] }t|qSr)r).0mrrr Lsz+IcmpType.encode_strings..N)rrrrr)rrrrencode_stringsEs   zIcmpType.encode_stringscCs2|dkr.x$|D]}|dkrttjd|qWdS)Nrrrz'%s' not from {'ipv4'|'ipv6'})rr)r r ZINVALID_DESTINATION)rritemZ all_configrrrr _check_configNs  zIcmpType._check_config)rr)rr)rr) __name__ __module__ __qualname__ZIMPORT_EXPORT_STRUCTUREZDBUS_SIGNATUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSrrr#r% __classcell__rr)rrr%s    c@seZdZddZdS)icmptype_ContentHandlercCstj||||jj|||dkrTd|kr>tjd|dd|kr|d|j_nT|dkr^nJ|dkrhn@|dkrx6d D].}||krv||jd krv|jjj t |qvWdS)Nrrz'Ignoring deprecated attribute name='%s'rrrrrryestrue)rr)r+r,) r startElementr$Zparser_check_element_attrsr Zwarningrlowerrappendstr)rrattrsxrrrr-Ys"  z$icmptype_ContentHandler.startElementN)r&r'r(r-rrrrr*Xsr*c Cst}|jds ttjd||dd |_|j|j||_||_|j t j rVdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r}zttjd|jWYdd}~XnXWdQRX~~tr|j|S) Nz.xmlz%s is missing .xml suffixFTz%s/%srbznot a valid icmptype file: %s)rendswithr r Z INVALID_NAMErZ check_namefilenamepath startswithr ETC_FIREWALLDZbuiltindefaultr*saxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZINVALID_ICMPTYPEZ getExceptionrr#) r7r8rhandlerparserrfsourcemsgrrrrms8        (c Cs.|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|ji}|jr|jd kr|j|d <|jd ||jd |jrt|jd krt|jd |jdi|j|j|jd|jd |jr|jd kr|jd |jdi|j|j|jd|jd |jr|jd i}x|jD]} d|| <qW|jd||jd |jd |jd |j|j~dS)Nz%s/%sz %s/%s.xmlz%s.oldzBackup of file '%s' failed: %siZwtzUTF-8)modeencodingrrr z rrr+r)r8r7rosexistsshutilZcopy2 Exceptionr errordirnamer9rr:mkdirior=r Z startDocumentrr-ZignorableWhitespacerZ charactersZ endElementrrZ simpleElementZ endDocumentclose) rr8_pathrrCdirpathrAr?r1r2rrrrs\                       )N)__all__Zxml.saxr<rGrNrIZfirewallrZfirewall.functionsrZfirewall.core.io.io_objectrrr r Zfirewall.core.loggerr r Zfirewall.errorsr rr*rrrrrrs       3PK!(# +io/__pycache__/service.cpython-36.opt-1.pycnu[3 Yj2@sdddgZddljZddlZddlZddlZddlmZddlm Z ddl m Z m Z m Z mZmZmZmZmZddlmZdd lmZdd lmZGd dde ZGd d d e ZddZdddZdS)Serviceservice_readerservice_writerN)config) u2b_if_py2)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator check_port check_tcpudpcheck_protocol check_address)log)errors) FirewallErrorc seZdZd d!d"dd#gfddgfdddifddgfd d$gfd dgfd dgff Zd d gZddddZddgddgdgdgddgddgdgdgdZfddZddZddZ ddZ Z S)%rversionshort descriptionportsmodules destination protocols source_portsincludeshelpers_-N)rrservicenameportprotocolvalueipv4ipv6r)rr!r"modulerz source-portincludehelpercsNtt|jd|_d|_d|_g|_g|_g|_i|_ g|_ g|_ g|_ dS)Nr) superr__init__rrrrrrrrrr)self) __class__/usr/lib/python3.6/service.pyr*DszService.__init__cCshd|_d|_d|_|jdd=|jdd=|jdd=|jj|jdd=|j dd=|j dd=dS)Nr) rrrrrrrclearrrr)r+r-r-r.cleanupQs      zService.cleanupcCst|j|_t|j|_t|j|_dd|jD|_dd|jD|_dd|jjD|_dd|jD|_dd|j D|_ dd|j D|_ d d|j D|_ d S) z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSs g|]\}}t|t|fqSr-)r).0poprr-r-r. dsz*Service.encode_strings..cSsg|] }t|qSr-)r)r1mr-r-r.r4escSsi|]\}}t|t|qSr-)r)r1kvr-r-r. fsz*Service.encode_strings..cSsg|] }t|qSr-)r)r1r3r-r-r.r4gscSs g|]\}}t|t|fqSr-)r)r1r2r3r-r-r.r4hscSsg|] }t|qSr-)r)r1sr-r-r.r4jscSsg|] }t|qSr-)r)r1r9r-r-r.r4ksN) rrrrrrritemsrrrr)r+r-r-r.encode_strings]s    zService.encode_stringscCs:|dkrJx>|D]6}|ddkr8t|dt|dqt|dqWn|dkrjx|D] }t|qXWn|dkrx|D]}t|dt|dqxWn|dkrx|D]*}|dkrttjd |t|||qWn^|d kr6xR|D]J}|jd r|jd d}d |kr|jd d}t |dkrttj |qWdS)Nrrrrrrr$r%z'%s' not in {'ipv4'|'ipv6'}r nf_conntrack_rr)r$r%) r r r rrZINVALID_DESTINATIONr startswithreplacelenZINVALID_MODULE)r+ritemZ all_configr!protorr&r-r-r. _check_configms8              zService._check_config)rr)rr)rr)rr)rr) __name__ __module__ __qualname__ZIMPORT_EXPORT_STRUCTUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSr*r0r;rD __classcell__r-r-)r,r.r&s4     c@seZdZddZdS)service_ContentHandlercCs0tj||||jj|||dkrTd|krxRdD]J}||krt|||||jjkr&tjd|n|||jj|<qWn|dkr|d}|jdr~|jdd}d|kr~|jdd}||jjkr|jjj |n tjd|n|dkr|d|jjkr|jjj |dntjd|dn@|dkr,|d|jjkr|jjj |dntjd|ddS)Nrr z'Ignoring deprecated attribute name='%s'rrrr!rr"z#Port '%s/%s' already set, ignoring.z$Protocol '%s' already set, ignoring.r#z source-portz)SourcePort '%s/%s' already set, ignoring.rr$r%z2Destination address for '%s' already set, ignoringr&r=rrz"Module '%s' already set, ignoring.r'z#Include '%s' already set, ignoring.r(z"Helper '%s' already set, ignoring.)r$r%)r startElementrBZparser_check_element_attrsrZwarningrr r rappendr rrrrr?r@rrr)r+r attrsentryxr&r-r-r.rJs                       z#service_ContentHandler.startElementN)rErFrGrJr-r-r-r.rIsrIc Cst}|jds ttjd||dd |_|j|j||_||_|j t j rVdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r}zttjd|jWYdd}~XnXWdQRX~~tr|j|S) Nz.xmlz'%s' is missing .xml suffixFTz%s/%srbznot a valid service file: %s)rendswithrrZ INVALID_NAMEr Z check_namefilenamepathr?r ETC_FIREWALLDZbuiltindefaultrIsaxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZINVALID_SERVICEZ getExceptionrr;) rSrTrhandlerparserr fsourcemsgr-r-r.rs8        (cCsr|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|ji}|jr|jd kr|j|d <|jd ||jd |jrt|jd krt|jd |jdi|j|j|jd|jd |jr|jd kr|jd |jdi|j|j|jd|jd x>|jD]4} |jd |jd| d| dd|jd qWx4|jD]*} |jd |jdd| i|jd qWx>|jD]4} |jd |jd| d| dd|jd qs    (   mQPK!))*io/__pycache__/helper.cpython-36.opt-1.pycnu[3 Yj @sdddgZddljZddlZddlZddlZddlmZddlm Z ddl m Z m Z m Z mZmZmZddlmZdd lmZdd lmZGd dde ZGd d d e ZddZdddZdS)Helper helper_reader helper_writerN)config) u2b_if_py2)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator check_port check_tcpudp)log)errors) FirewallErrorcseZdZddddddd gffZdZd d gZd d dgd Zd ddgddgdZfddZddZ ddZ ddZ ddZ Z S)!rversionshort descriptionfamilymoduleportsz (sssssa(ss))-.N)rrhelpernameportprotocol)rrcs6tt|jd|_d|_d|_d|_d|_g|_dS)Nr) superr__init__rrrrrr)self) __class__/usr/lib/python3.6/helper.pyr;szHelper.__init__cCs.d|_d|_d|_d|_d|_|jdd=dS)Nr)rrrrrr)rr!r!r"cleanupDs zHelper.cleanupcCsRt|j|_t|j|_t|j|_t|j|_t|j|_dd|jD|_dS)z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSs g|]\}}t|t|fqSr!)r).0ZpoZprr!r!r" Usz)Helper.encode_strings..N)rrrrrrr)rr!r!r"encode_stringsLs      zHelper.encode_stringscCs(ddg}||kr$ttjd||fdS)NZipv4Zipv6z'%s' not in '%s')rrZ INVALID_IPV)rZipvZipvsr!r!r" check_ipvWszHelper.check_ipvcCsz|dkr0xl|D]}t|dt|dqWnF|dkrv|jdsRttjd|t|jdddkrvttjd|dS) Nrrr nf_conntrack_z('%s' does not start with 'nf_conntrack_'rzModule name '%s' too short)r r startswithrrINVALID_MODULElenreplace)rritemZ all_configrr!r!r" _check_config]s    zHelper._check_config)rr)rr)rr)rr)rr)rr)__name__ __module__ __qualname__ZIMPORT_EXPORT_STRUCTUREZDBUS_SIGNATUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSrr#r&r'r/ __classcell__r!r!)r r"r&s$     c@seZdZddZdS)helper_ContentHandlercCs>tj||||jj|||dkrd|kr8|d|j_d|kr\|jj|d|d|j_d|kr|djdstt j d|dt |dj dddkrtt j d |d|d|j_ nz|d krnp|d krnf|d kr:t|d t|d |d |d f}||jjkr$|jjj|ntjd|d |d dS)Nrrrrr)z('%s' does not start with 'nf_conntrack_'rr(zModule name '%s' too shortrrrrz#Port '%s/%s' already set, ignoring.)r startElementr.Zparser_check_element_attrsrr'rr*rrr+r,r-rr r rappendr Zwarning)rrattrsentryr!r!r"r5ns>      z"helper_ContentHandler.startElementN)r0r1r2r5r!r!r!r"r4msr4c Cst}|jds ttjd||dd |_|j|j||_||_|j t j rVdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r}zttjd|jWYdd}~XnXWdQRX~~tr|j|S) Nz.xmlz'%s' is missing .xml suffixFTz%s/%srbznot a valid helper file: %s)rendswithrrZ INVALID_NAMErZ check_namefilenamepathr*r ETC_FIREWALLDZbuiltindefaultr4saxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZINVALID_HELPERZ getExceptionrr&) r=r>rhandlerparserrfsourcemsgr!r!r"rs8        (c CsP|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|ji}|j|d <|jr|jd kr|j|d <|jr<|jd kr<|j|d <|jd ||jd|jr|jd kr|jd|jdi|j|j|jd|jd|jr|jd kr|jd|jdi|j|j|jd|jdx>|jD]4} |jd|jd| d| dd|jdqW|jd |jd|j|j~dS)Nz%s/%sz %s/%s.xmlz%s.oldzBackup of file '%s' failed: %siZwtzUTF-8)modeencodingrrrrr z rrrrr()rr) r>r=rosexistsshutilZcopy2 Exceptionr errordirnamer*rr?mkdiriorBr Z startDocumentrrrr5ZignorableWhitespacerZ charactersZ endElementrrZ simpleElementZ endDocumentclose) rr>_pathrrHdirpathrFrDr7rr!r!r"rs\                       )N)__all__Zxml.saxrArLrSrNZfirewallrZfirewall.functionsrZfirewall.core.io.io_objectrrr r r r Zfirewall.core.loggerr rZfirewall.errorsrrr4rrr!r!r!r"s        G#PK! h%h%6io/__pycache__/lockdown_whitelist.cpython-36.opt-1.pycnu[3 Yj1@sddljZddlZddlZddlZddlmZddlmZm Z m Z m Z ddl m Z ddlmZmZmZmZmZmZddlmZddlmZGdd d e ZGd d d e ZdS) N)config)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator)log)uniqify checkUsercheckUid checkCommand checkContext u2b_if_py2)errors) FirewallErrorc@seZdZddZddZdS)!lockdown_whitelist_ContentHandlercCstj||d|_dS)NF)r__init__ whitelist)selfitemr(/usr/lib/python3.6/lockdown_whitelist.pyr%s z*lockdown_whitelist_ContentHandler.__init__c CsVtj||||jj|||dkr@|jr6ttjdd|_n|dkrr|js\tj ddS|d}|jj |n|dkr|jstj ddSd |kryt |d }Wn&t k rtj d |d dSX|jj |nd|kr|jj|dn\|d kr@|jstj d dSd |kr.tj ddS|jj|d ntj d|dSdS)NrzMore than one whitelist.Tcommandz)Parse Error: command outside of whitelistnameuserz&Parse Error: user outside of whitelistidz"Parse Error: %s is not a valid uidselinuxz)Parse Error: selinux outside of whitelistcontextzParse Error: no contextzUnknown XML element %s)r startElementrZparser_check_element_attrsrrrZ PARSE_ERRORrerror add_commandint ValueErroradd_uidadd_user add_context)rrZattrsruidrrrr)sJ        z.lockdown_whitelist_ContentHandler.startElementN)__name__ __module__ __qualname__rrrrrrr$srcs4eZdZdZddgfddgfddgfddgffZdZd gZd d gd d gd Zddd giZfddZ ddZ ddZ ddZ ddZ ddZddZddZd d!Zd"d#Zd$d%Zd&d'Zd(d)Zd*d+Zd,d-Zd.d/Zd0d1Zd2d3Zd4d5Zd6d7Zd8d9Zd:d;Zdd?Z d@dAZ!dBdCZ"Z#S)DLockdownWhitelistz LockdownWhitelist class commandscontextsusersuidsrz (asasasai)_Nrr)rrrrrrcs6tt|j||_d|_g|_g|_g|_g|_dS)N) superr)rfilenameparserr*r,r-r.)rr1) __class__rrrnszLockdownWhitelist.__init__cCs|d kr.x|D]}|j||dd |qWnv|dkrLt|sttj|nX|dkrjt|sttj|n:|dkrt|sttj|n|d krt |sttj |dS) Nr*r,r-r.rrrr%)r*r,r-r.) _check_configr rrINVALID_COMMANDr INVALID_CONTEXTr INVALID_USERr INVALID_UID)rrrZ all_configxrrrr6ys zLockdownWhitelist._check_configcCs4|jdd=|jdd=|jdd=|jdd=dS)N)r*r,r-r.)rrrrcleanups   zLockdownWhitelist.cleanupcCs:dd|jD|_dd|jD|_dd|jD|_dS)z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSsg|] }t|qSr)r ).0r;rrr sz4LockdownWhitelist.encode_strings..cSsg|] }t|qSr)r )r=r;rrrr>scSsg|] }t|qSr)r )r=r;rrrr>sN)r*r,r-)rrrrencode_stringssz LockdownWhitelist.encode_stringscCs@t|sttj|||jkr,|jj|nttjd|dS)Nz!Command "%s" already in whitelist)r rrr7r*appendALREADY_ENABLED)rrrrrrs   zLockdownWhitelist.add_commandcCs,||jkr|jj|nttjd|dS)NzCommand "%s" not in whitelist.)r*removerr NOT_ENABLED)rrrrrremove_commands z LockdownWhitelist.remove_commandcCs ||jkS)N)r*)rrrrr has_commandszLockdownWhitelist.has_commandcCsBx<|jD]2}|jdr.|j|ddr:dSq||krdSqWdS)N*r4TFr5)r*endswith startswith)rrZ_commandrrr match_commands  zLockdownWhitelist.match_commandcCs|jS)N)r*)rrrr get_commandsszLockdownWhitelist.get_commandscCsDt|sttjt|||jkr0|jj|nttjd|dS)NzUid "%s" already in whitelist)r rrr:strr.r@rA)rr%rrrr"s  zLockdownWhitelist.add_uidcCs,||jkr|jj|nttjd|dS)NzUid "%s" not in whitelist.)r.rBrrrC)rr%rrr remove_uids zLockdownWhitelist.remove_uidcCs ||jkS)N)r.)rr%rrrhas_uidszLockdownWhitelist.has_uidcCs ||jkS)N)r.)rr%rrr match_uidszLockdownWhitelist.match_uidcCs|jS)N)r.)rrrrget_uidsszLockdownWhitelist.get_uidscCs@t|sttj|||jkr,|jj|nttjd|dS)NzUser "%s" already in whitelist)r rrr9r-r@rA)rrrrrr#s   zLockdownWhitelist.add_usercCs,||jkr|jj|nttjd|dS)NzUser "%s" not in whitelist.)r-rBrrrC)rrrrr remove_users zLockdownWhitelist.remove_usercCs ||jkS)N)r-)rrrrrhas_userszLockdownWhitelist.has_usercCs ||jkS)N)r-)rrrrr match_userszLockdownWhitelist.match_usercCs|jS)N)r-)rrrr get_usersszLockdownWhitelist.get_userscCs@t|sttj|||jkr,|jj|nttjd|dS)Nz!Context "%s" already in whitelist)r rrr8r,r@rA)rrrrrr$"s   zLockdownWhitelist.add_contextcCs,||jkr|jj|nttjd|dS)NzContext "%s" not in whitelist.)r,rBrrrC)rrrrrremove_context,s z LockdownWhitelist.remove_contextcCs ||jkS)N)r,)rrrrr has_context3szLockdownWhitelist.has_contextcCs ||jkS)N)r,)rrrrr match_context6szLockdownWhitelist.match_contextcCs|jS)N)r,)rrrr get_contexts9szLockdownWhitelist.get_contextscCs|j|jjds&ttjd|jt|}tj}|j |y|j |jWn8tj k r}zttj d|j WYdd}~XnX~~tr|jdS)Nz.xmlz'%s' is missing .xml suffixzNot a valid file: %s)r<r1rGrrZ INVALID_NAMErsaxZ make_parserZsetContentHandlerparseZSAXParseExceptionZ INVALID_TYPEZ getExceptionrr?)rhandlerr2msgrrrread>s"   zLockdownWhitelist.readcCstjj|jr\ytj|jd|jWn4tk rZ}ztd|j|fWYdd}~XnXtjjtj sxtj tj dt j |jddd}t |}|j|jdi|jdx6t|jD](}|jd |jd d |i|jdqWx:t|jD],}|jd |jd d t|i|jdqWx8t|jD]*}|jd |jd d |i|jdq0Wx8t|jD]*}|jd |jdd|i|jdqjW|jd|jd|j|j~dS)Nz%s.oldzBackup of '%s' failed: %siZwtzUTF-8)modeencodingr z rrrrrr)ospathexistsr1shutilZcopy2 ExceptionIOErrorrZ ETC_FIREWALLDmkdirioopenrZ startDocumentrZignorableWhitespacerr*Z simpleElementr.rKr-r,Z endElementZ endDocumentclose)rr[frZrr%rrrrrwriteQsB$         zLockdownWhitelist.write)$r&r'r(__doc__ZIMPORT_EXPORT_STRUCTUREZDBUS_SIGNATUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSrr6r<r?rrDrErIrJr"rLrMrNrOr#rPrQrRrSr$rTrUrVrWr\rk __classcell__rr)r3rr)WsL         1 r))Zxml.saxrXr`rgrcZfirewallrZfirewall.core.io.io_objectrrrrZfirewall.core.loggerrZfirewall.functionsrr r r r r rZfirewall.errorsrrr)rrrrs      3PK!FmRR$io/__pycache__/policy.cpython-36.pycnu[3 YjϢ@s dddgZddljZddlZddlZddlZddlmZddlm Z m Z ddlm Z m Z m Z ddlmZmZmZdd lmZmZmZmZmZmZdd lmZdd lmZdd lmZdd lmZddZ ddZ!ddZ"ddZ#ddZ$GdddeZ%GdddeZ&dddZ'dddZ(dS) Policy policy_reader policy_writerN)config)checkIPcheckIP6)uniqifymax_policy_name_lenportStr)DEFAULT_POLICY_TARGETPOLICY_TARGETSDEFAULT_POLICY_PRIORITY) IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator check_port check_tcpudpcheck_protocol)rich)log)errors) FirewallErrorc Cs|dkr n|dkrn|dkr|jr`|jjrJtjdt|jd|_dStj|d|j_dS|d|jj kr|jj j |dntjd|dn|dkrN|jr|jjrtjdt|jd|_dStj |d|d |j_dSt |dt |d t|dd |d f}||jjkr4|jjj |ntjd |d|d nN|d kr|jr|jjrtjdt|jd|_dStj|d |j_nBt|d |d |jjkr|jjj |d ntjd |d n|dkrh|jr.|jjrtjdt|jd|_dStj|d|j_dS|d|jjkrT|jjj |dntjd|dn4|dkr|jr|jjrtjdt|jd|_dStj|d|j_dStjd|dn|dkr2|jr|jjrtjdt|jd|_dStj|j_n|jjr&tjdnd|j_nj|dkrd}d|krR|d}d}d|krh|d}|jr|jjrtjdt|jd|_dStj|d|d |||j_dSt |dt |d |rt ||r t| r t| r ttjd|t|dd |d t|d t|f}||jjkrL|jjj |n6tjd|d|d |rld|nd|r|d|ndn|dkr@|jr|jjrtjdt|jd|_dStj|d|d |j_dSt |dt |d t|dd |d f}||jj kr&|jj j |ntjd|d|d n\|dkr|jsftjdd|_dS|jj!rtjd t|jdSd!}d}d"|kr|d"}d}d#|kr|d#}d$|kr|d$j"dLkrd}tj#||||j_!n|dMkr|jstjd+d|_dS|jj$r0tjd,d|_dS|d'krHtj%|j_$nh|d(krxd} d-|krh|d-} tj&| |j_$n8|d)krtj'|j_$n |d*kr|d.} tj(| |j_$|jj$|_)n|d/kr^|jstjd0dS|jjrtjd1dSd} d2|kr*|d2} | dNkr*tjd;d|_dSd<|kr<|d<nd} tj*| | |j_|jj|_)n>|d=kr|js~tjd>dS|jj+rtjd?t|jd|_dStj,|j_+|jj+|_)n|d@kr,d} dA}dB|kr|dB} | dOkrtjdE|dBd|_dSdF|krt-|dF}tj.| |dG|_np|dHkr|j)sRtjdId|_dS|j)j/rxtjdJt|jd|_dS|d }tj0||j1dK|j)_/nd!SdS)PNshort descriptionservicez;Invalid rule: More than one element in rule '%s', ignoring.Tnamez#Service '%s' already set, ignoring.portprotocol-z#Port '%s/%s' already set, ignoring.valuez$Protocol '%s' already set, ignoring.z icmp-blockz&icmp-block '%s' already set, ignoring.z icmp-typez-Invalid rule: icmp-block '%s' outside of rule masqueradez!Masquerade already set, ignoring.z forward-portzto-portzto-addrz#to-addr '%s' is not a valid addressz-Forward port %s/%s%s%s already set, ignoring.z >%sz @%sz source-portz*Source port '%s/%s' already set, ignoring. destinationz)Invalid rule: Destination outside of rulez?Invalid rule: More than one destination in rule '%s', ignoring.Faddressipsetinvertyestrueacceptrejectdropmarkz$Invalid rule: Action outside of rulez"Invalid rule: More than one actiontypesetrz!Invalid rule: Log outside of rulezInvalid rule: More than one loglevelemergalertcriterrorwarningnoticeinfodebugzInvalid rule: Invalid log levelprefixauditz#Invalid rule: Audit outside of rulez9Invalid rule: More than one audit in rule '%s', ignoring.rulerfamilyipv4ipv6z&Invalid rule: Rule family "%s" invalidpriority)r:r=limitz4Invalid rule: Limit outside of action, log and auditz9Invalid rule: More than one limit in rule '%s', ignoring.burst)r&r')r(r)r*r+)r/r0r1r2r3r4r5r6)r;r<)2_ruleelementrr3str _rule_errorr Rich_Serviceitemservicesappend Rich_Portrrr ports Rich_Protocolr protocolsRich_IcmpBlock icmp_blocks Rich_IcmpTypeRich_Masquerader Rich_ForwardPortrrrr INVALID_ADDR forward_portsRich_SourcePort source_portsr"lowerZRich_Destinationaction Rich_Accept Rich_Reject Rich_Drop Rich_Mark _limit_okZRich_Logr8Z Rich_Auditint Rich_Ruler>Z Rich_Limitget)objrattrsentryto_portZto_addrr%r#r$Z_typeZ_setr.r7r:r=rrc/usr/lib/python3.6/policy.pycommon_startElements                                                                            recCs|dkr|jsy|jjWn6tk rR}ztjd|t|jWYdd}~XnLXt|j|jjkr|jj j |j|jjj t|jntjdt|jd|_d|_n|d krd|_ dS) Nr9z%s: %sz Rule '%s' already set, ignoring.Fr(r)r*r+rr8)r(r)r*r+rr8) rCr@Zcheck Exceptionrr3rBrE rules_strrulesrGr[)r_rercrcrdcommon_endElements& rjcCst|trdnd}|dkrT|jrT|jj}x$|D]}||kr0ttjd|q0Wn|dkrx$|D]}t|dt|dqbWnb|dkrx|D] }t |qWn@|d kr|jr|jj } x$|D]} | | krttj d | qWn|d krx|D]} t| dt| d| d  r>| d  r>ttj d| | d rTt| d | d rt | d  rt| d  rttjd| d qWnT|dkrx&|D]}t|dt|dqWn|dkrx|D]} tj| d} |jr| jrt| jtjst| jtjr|jj } | jj| krLttj d | jjnH| jr|jj| jj}|jr| j|jkrttj d| j| jjfnL|jrt| jtjr|jj}| jj|krttjdj||j| jjqWdS)NrZZonerFz '%s' not among existing servicesrIrrKrMz"'%s' not among existing icmp typesrRz$'%s' is missing to-port AND to-addr z#to-addr '%s' is not a valid addressrTrg rich_rules)rule_strz3rich rule family '%s' conflicts with icmp type '%s'z){} '{}': '{}' not among existing services)rgrn) isinstancer fw_configZ get_servicesrrZINVALID_SERVICErrrZ get_icmptypesZINVALID_ICMPTYPEINVALID_FORWARDrrrQrr]rArLrNrr:Z get_icmptyper"rDformat)r_rrE all_configZobj_typeZexisting_servicesrrprotoZexisting_icmptypesZicmptypefwd_portr9Zobj_richZictrcrcrdcommon_check_config2s                      rwcCs0d|ji}|j}|dk r ||d<|jd|dS)Nrr?r>)rr? simpleElement)handlerr>dr?rcrcrd_handler_add_rich_limitxs  r{c Cs|jrF|jdkrF|jd|jdi|j|j|jd|jd|jr|jdkr|jd|jdi|j|j|jd|jdx6t|jD](}|jd|jdd|i|jdqWx@t|j D]2}|jd|jd|d |d d |jdqWx8t|j D]*}|jd|jd d |i|jdqWx8t|j D]*}|jd|jdd|i|jdqLW|j r|jd|jdi|jdxt|j D]}|jd|d |d d }|dr|ddkr|d|d<|dr|ddkr|d|d<|jd||jdqWxBt|jD]4}|jd|jd|d |d d |jdq>WxT|jD]H}i}|jr|j|d<|jd krt|j|d<|jd|jd||jd|jrVi}|jjr|jj|d<|jjr|jj|d<|jjr$|jj|d<|jjr6d|d<|jd|jd||jd|jri}|jjrx|jj|d<|jjr|jj|d<|jjrd|d<|jd|jd ||jd|jrxd} i}t|jtjkrd} |jj|d<nbt|jtjkr(d} |jj|d<|jj |d <n0t|jtj!krNd } |jj"|d <n t|jtj#krfd} nt|jtj$krd} |jj|d<nt|jtj%krd!} |jj|d<nt|jtj&krd} |jj|d<|jj |d <|jj'dkr|jj'|d<|jj(dkrX|jj(|d<nFt|jtj)krBd} |jj|d<|jj |d <nt*t+j,d"t|j|jd|j| ||jd|j-ri}|j-j.r|j-j.|d#<|j-j/r|j-j/|d$<|j-j0r|jd|jd%||jd&t1||j-j0|jd'|jd%n|jd|jd%||jd|j2ri}|j2j0rx|jd|jd(i|jd&t1||j2j0|jd'|jd(n|jd|jd(||jd|j3rd} i}t|j3tj4krd)} n|t|j3tj5krd*} |j3jr<|j3j|d+<nNt|j3tj6krd,} n6t|j3tj7kr*d-} |j3j8|d.<nt-j9d/t|j3|j3j0r|jd|j| ||jd&t1||j3j0|jd'|j| n|jd|j| ||jd|jd|jd|jdqWdS)0Nr!z r rrrrrrk)rrrrz icmp-blockr rlzto-portrmzto-addrz forward-portz source-portr:r=r9r#macr$Truer%z sourcer"z icmp-typez"Unknown element '%s' in obj_writerr7r.rz z r8r(r)r,r*r+r-zUnknown action '%s'):rignorableWhitespace startElementZ characters endElementrrrFrxrIrKrMr rRrTrhr:r=rBraddrr}r$r%r"rAr,rrDrrHrrrJrrOrLrNrPrb to_addressrSrrZINVALID_OBJECTrr7r.r>r{r8rVrWrXrYrZr-r3) r_ryrrrZicmpZforwardr`r9rArVrcrcrd common_writers\                                                                                        rcsPeZdZd7ZdZeZdgZd8d9d:d;d dgfd dgfddgfddgfdd?gfd@ddgfddgffZdddgZ dddgdgddgdgdgdddgddddgddgddddddgdgdgdgdZ ddgdd gd!dgd"d#d$d!d%gd"d$d%gd&d'gd(gd)gd*Z fd+d,Z d-d.Z fd/d0Zfd1d2Zd3d4Zfd5d6ZZS)Ariirversionr!rrtargetrFrIrMr FrRrnrKrTr= ingress_zones egress_zones_r/Nrrrrr-)rrpolicyrrz icmp-blockz icmp-typer z forward-portr9rr"rz source-portrr8r(r)r*r+r>z ingress-zonez egress-zonezto-portzto-addrr:r#r}r%r$r7r.r,r?)rz forward-portr9rr"rr)r>cstt|jd|_d|_d|_t|_g|_g|_ g|_ g|_ d|_ g|_ g|_d|_g|_g|_d|_|j|_d|_g|_g|_dS)Nr!F)superr__init__rrrr rrFrIrKrMr rRrTrqrhrgappliedpriority_defaultr=Zderived_from_zonerr)self) __class__rcrdrs(zPolicy.__init__cCsd|_d|_d|_t|_|jdd=|jdd=|jdd=|jdd=d|_ |j dd=|j dd=d|_ |j dd=|jdd=d|_|j|_|jdd=|jdd=dS)Nr!F)rrrr rrFrIrKrMr rRrTrqrhrgrrr=rr)rrcrcrdcleanups$         zPolicy.cleanupcs"|dkr|jSttt||SdS)Nrn)rggetattrrr)rr)rrcrd __getattr__szPolicy.__getattr__csB|dkr,dd|D|_dd|jD|_ntt|j||dS)NrncSsg|]}tj|dqS))ro)rr]).0srcrcrd sz&Policy.__setattr__..cSsg|] }t|qSrc)rB)rrrcrcrdrs)rhrgrr __setattr__)rrr)rrcrdrszPolicy.__setattr__c Cst|||||dkr2|tkr.ttjd|n|dkrz||jksX||jksX||jkrvttjd||j|j|jfn|dkrhddg}|j r||j j 7}x|D]}||krttj d ||dkrt ddgt |@s|dkrt |t |grttj d ||dkr|dkr8d|kr8d|dksT|dkrd|krd|dkrttj d qWn|d kr|rd|krd|dkrttj d nxd|krd|dkrttj dxR|dD]F}|dkrސq|j j |}|j rd|j j|krttj dqWn|dkr4x|D]}tj|d}|jrt|jtjrd|kr|d|dkr|ttj d nxd|kr,d|dkrttj dxR|dD]F}|dkrq|j j |}|j rd|j j|krttj dqWq,|jrt|jtjrd|kr,d|dkr@|jjrttjdnt|dr,|jjs`ttjdd|dkr,x|dD]8}|j j |}|j rxd|j j|krxttj dqxWnv|jr,t|jtjr,d|kr,xR|dD]F}|dkrq|j j |}|j rd|j j|krttj dqWq,Wn|dkrx|D]} d|krnd|dkrnttj dnd|krDd|dkr| drttjdnt|drD| dsttjdd|dkrDxD|dD]8}|j j |}|j rd|j j|krttj dqWqDWdS)Nrz'%s' is invalid targetr=zQ%d is invalid priority. Must be in range [%d, %d]. The following are reserved: %srrANYHOSTz'%s' not among existing zonesz>'%s' may only contain one of: many regular zones, ANY, or HOSTzF'HOST' can only appear in either ingress or egress zones, but not bothr z.'masquerade' is invalid for egress zone 'HOST'z/'masquerade' is invalid for ingress zone 'HOST'Z interfaceszR'masquerade' cannot be used in a policy if an ingress zone has assigned interfacesrn)rozAA 'forward-port' with 'to-addr' is invalid for egress zone 'HOST'zC'forward-port' requires 'to-addr' if egress zone is 'ANY' or a zonezS'forward-port' cannot be used in a policy if an egress zone has assigned interfaceszR'mark' action cannot be used in a policy if an egress zone has assigned interfacesrRz1'forward-port' is invalid for ingress zone 'HOST'rm)rr)rr)rr)rr)rwr rrINVALID_TARGETpriority_reserved priority_max priority_minZINVALID_PRIORITYrq get_zonesZ INVALID_ZONEr-Zget_zoneZget_zone_config_dictrr]rArprOrPrrrrVrZ) rrrErtZexisting_zoneszoneZz_objr9r_rvrcrcrd _check_configs       "                           zPolicy._check_configcstt|j||jdr,ttjd|n|jdrHttjd|n|jddkrhttjd|njd|kr|d|j d}n|}t |t krttjd|t |t f|j r||j j krttjddS)Nrz'%s' can't start with '/'z'%s' can't end with '/'rkzmore than one '/' in '%s'z&Policy of '%s' has %d chars, max is %dz,Policies can't have the same name as a zone.)rr check_name startswithrr INVALID_NAMEendswithcountfindlenr rqrZ NAME_CONFLICT)rrZ checked_name)rrcrdr,s*      zPolicy.check_namei)rr!)rr!)rr!)rr!)r!r!)r F)r!r!r!r!)r!r!)r=r)__name__ __module__ __qualname__rrr rrZIMPORT_EXPORT_STRUCTUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSrrrrrr __classcell__rcrc)rrdrZsr        ^c@s$eZdZddZddZddZdS)policy_ContentHandlercCs"tj||d|_d|_d|_dS)NF)rrr@rCr[)rrErcrcrdrHs zpolicy_ContentHandler.__init__cCstj||||jrdS|jj||t|||r6dS|dkrd|krR|d|j_d|krjt|d|j_d|kr|d}|t krt t j ||r||j_ n^|dkr|d|jjkr|jjj|dntjd|dn|dkr |d|jjkr|jjj|dntjd |dn|d kr|jsFtjd d |_dS|jjrltjd t|jd |_dSd}d|kr|djdkrd }d}}}d|kr|d}d|kr|d}d|kr|d}tj||||d|j_dStjd|dSdS)Nrrr=rz ingress-zonerz(Ingress zone '%s' already set, ignoring.z egress-zonez'Egress zone '%s' already set, ignoring.rz$Invalid rule: Source outside of ruleTz:Invalid rule: More than one source in rule '%s', ignoring.Fr%r&r'r#r}r$)r%zUnknown XML element '%s')r&r')rrrCrEZparser_check_element_attrsrerr\r=r rrrrrrGrr3rr@rrBrUrZ Rich_Source)rrr`rr%rr}r$rcrcrdrNsf                 z"policy_ContentHandler.startElementcCstj||t||dS)N)rrrj)rrrcrcrdrs z policy_ContentHandler.endElementN)rrrrrrrcrcrcrdrGs@rFc Cst}|jds ttjd||dd |_|s>|j|j||_||_|j t j rZdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r} zttjd| jWYdd} ~ XnXWdQRX~~|S) Nz.xmlz'%s' is missing .xml suffixFTz%s/%srbznot a valid policy file: %s)rrrrrrrfilenamepathrr ETC_FIREWALLDZbuiltindefaultrsaxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZINVALID_POLICYZ getException) rrZ no_check_namerryparserrfrmsgrcrcrdrs6        (c Cs|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|ji}|jr|jd kr|j|d <|j|jkr0t|j|d <|j|d <|jd ||jdt||x8t|jD]*} |jd|jdd| i|jdqfWx8t|jD]*} |jd|jdd| i|jdqW|jd |jd|j |j!~dS)Nz%s/%sz %s/%s.xmlz%s.oldzBackup of file '%s' failed: %siZwtzUTF-8)modeencodingr!rr=rrr|z z ingress-zonerz egress-zone)"rrrosexistsshutilZcopy2rfrr2dirnamerrrmkdiriorrZ startDocumentrr=rrBrrrrrrrxrrZ endDocumentclose) rr_pathrrdirpathrryr`rrcrcrdrsN             )F)N))__all__Zxml.saxrrrrZfirewallrZfirewall.functionsrrrr r Zfirewall.core.baser r r Zfirewall.core.io.io_objectrrrrrrZ firewall.corerZfirewall.core.loggerrrZfirewall.errorsrrerjrwr{rrrrrrcrcrcrds4        F[nL PK!@*,io/__pycache__/firewalld_conf.cpython-36.pycnu[3 Yj5 @s~ddlZddlZddlZddlZddlmZddlmZddl m Z m Z m Z ddddd d d d d ddddg Z GdddeZdS)N)config)log)b2uu2bPY2 DefaultZone MinimalMark CleanupOnExitCleanupModulesOnExitLockdown IPv6_rpfilterIndividualCalls LogDeniedAutomaticHelpersFirewallBackendFlushAllOnReload RFC3964_IPv4AllowZoneDriftingc@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dS)firewalld_confcCsi|_g|_||_|jdS)N)_config_deletedfilenameclear)selfrr$/usr/lib/python3.6/firewalld_conf.py__init__&szfirewalld_conf.__init__cCsi|_g|_dS)N)rr)rrrrr,szfirewalld_conf.clearcCs|jjg|_dS)N)rrr)rrrrcleanup0s zfirewalld_conf.cleanupcCs|jj|jS)N)rgetstrip)rkeyrrrr4szfirewalld_conf.getcCs8t|j}t|j|j|<||jkr4|jj|dS)N)rrrrremove)rr valueZ_keyrrrset7s  zfirewalld_conf.setcCsHd}x2|jjD]$\}}|r$|d7}|d||f7}qWtrDt|S|S)N z%s=%s)ritemsrr)rsr r"rrr__str__=s zfirewalld_conf.__str__cCs|jyt|jd}Wn8tk rR}ztjd|j||jdtj|jdt tj |jdtj rpdnd|jdtj rdnd|jd tj rdnd|jd tjrdnd|jd tjrdnd|jd tj|jd tj|jdtj|jdtjr dnd|jdtjr"dnd|jdtjr:dndWYdd}~XnXx|D]}|shP|j}t|dks\|dd.krq\dd|jdD}t|dkrtjd|jq\nr|dtkrtjd|jq\nN|ddkrtjd|jq\n*|jj|ddk r:tjd|jq\|d|j|d<q\W|j|jdstjdtj|jdt tj|jd}y t|WnPttfk r|dk rtj d |r|ndtj |jdt tj YnX|jd}| s|j!d/krJ|dk r2tj d#|r(|ndtj |jdtj rDdnd|jd}| sj|j!d0kr|dk rtj d$|r|ndtj |jdtj rdnd|jd }| s|j!d1kr|dk rtj d%|r|ndtj |jd tj rdnd|jd }| s"|j!d2kr^|dk rFtj d&|r<|ndtj|jd tjrXdnd|jd }| s~|j!d3kr|dk rtj d'|r|ndtj|jd tjrdnd|jd }| s|tj"kr|dk rtj d(|tj|jd t tj|jd }| s&|j!tj#kr\|dk rJtj d)|r@|ndtj|jd t tj|jd}| s~|j!tj$kr|dk rtj d*|r|ndtj|jdt tj|jd}| s|j!d4kr |dk rtj d+|r|ndtj|jdt tj|jd}| s*|j!d5kr`|dk rNtj d,|rD|ndtj|jdt tj|jd}| s|j!d6kr|dk rtj d-|r|ndtj|jdt tjdS)7NrzFailed to load '%s': %srrr yesnor r r r rrrrrrr#;cSsg|] }|jqSr)r).0xrrr bsz'firewalld_conf.read..=zInvalid option definition: '%s'zInvalid option: '%s'r$zMissing value: '%s'z!Duplicate option definition: '%s'z0DefaultZone is not set, using default value '%s'z7MinimalMark '%s' is not valid, using default value '%d'falsetruez7CleanupOnExit '%s' is not valid, using default value %sz>CleanupModulesOnExit '%s' is not valid, using default value %sz2Lockdown '%s' is not valid, using default value %sz7IPv6_rpfilter '%s' is not valid, using default value %sz9IndividualCalls '%s' is not valid, using default value %sz3LogDenied '%s' is invalid, using default value '%s'z:AutomaticHelpers '%s' is not valid, using default value %sz9FirewallBackend '%s' is not valid, using default value %sz:FlushAllOnReload '%s' is not valid, using default value %sz6RFC3964_IPv4 '%s' is not valid, using default value %sz;AllowZoneDrifting '%s' is not valid, using default value %s)r-r.)r+r4r*r5)r+r4r*r5)r*r5r+r4)r*r5r+r4)r*r5r+r4)r*r5r+r4)r*r5r+r4)r*r5r+r4)%ropenr Exceptionrerrorr#rZ FALLBACK_ZONEstrZFALLBACK_MINIMAL_MARKZFALLBACK_CLEANUP_ON_EXITZ FALLBACK_CLEANUP_MODULES_ON_EXITZFALLBACK_LOCKDOWNZFALLBACK_IPV6_RPFILTERZFALLBACK_INDIVIDUAL_CALLSZFALLBACK_LOG_DENIEDZFALLBACK_AUTOMATIC_HELPERSZFALLBACK_FIREWALL_BACKENDZFALLBACK_FLUSH_ALL_ON_RELOADZFALLBACK_RFC3964_IPV4ZFALLBACK_ALLOW_ZONE_DRIFTINGrlensplit valid_keysrrcloseint ValueError TypeErrorZwarninglowerZLOG_DENIED_VALUESZAUTOMATIC_HELPERS_VALUESZFIREWALL_BACKEND_VALUES)rfmsglineZpairr"rrrreadFs                                       zfirewalld_conf.readc :Cst|jdkrdSg}tjjtjs2tjtjdy.tj ddtjj |j tjj |j dd}Wn2t k r}ztjd|WYdd}~XnXd}d}ytj|j dd d }WnPt k r}z0tjj|j rtjd |j |fnd}WYdd}~Xn6Xx0|D]&}|sP|jd }t|dkrH|s2|jd d }n|ddkrpd}|j||jd n|jd}t|dkrd}|j|d q |dj} |dj} | |kr.| |jkr|j| | krd}|jd| |j| fd }n$| |jkrd }nd}|j|d |j| nd }q Wt|jdkrx^|jjD]P\} } | |krjqT| dkrxqT|s|jd d }|jd| | fd }qTW|r|j|j|stj|jdStjj|j r@ytj|j d|j WnBt k r>}z$tj|jtd|j |fWYdd}~XnXytj|j|j WnBt k r}z$tj|jtd|j |fWYdd}~XnXtj|j ddS)Nr,iZwtz%s.F)modeprefixdirdeletez!Failed to open temporary file: %sZrtzUTF-8)rFencodingzFailed to open '%s': %sr%Trr-r2r3z%s=%s rrz%s.oldzBackup of '%s' failed: %szFailed to create '%s': %si)rr) r:rospathexistsrZ ETC_FIREWALLDmkdirtempfileZNamedTemporaryFilebasenamerdirnamer7rr8ior6rwriter;rappendr&r=r!nameshutilZcopy2IOErrorZmovechmod) rdoneZ temp_filerCZmodifiedemptyrBrDpr r"rrrrSs                  $ $zfirewalld_conf.writeN) __name__ __module__ __qualname__rrrrr#r(rErSrrrrr%s r)Zos.pathrKrRrOrVZfirewallrZfirewall.core.loggerrZfirewall.functionsrrrr<objectrrrrrs  PK!))$io/__pycache__/helper.cpython-36.pycnu[3 Yj @sdddgZddljZddlZddlZddlZddlmZddlm Z ddl m Z m Z m Z mZmZmZddlmZdd lmZdd lmZGd dde ZGd d d e ZddZdddZdS)Helper helper_reader helper_writerN)config) u2b_if_py2)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator check_port check_tcpudp)log)errors) FirewallErrorcseZdZddddddd gffZdZd d gZd d dgd Zd ddgddgdZfddZddZ ddZ ddZ ddZ Z S)!rversionshort descriptionfamilymoduleportsz (sssssa(ss))-.N)rrhelpernameportprotocol)rrcs6tt|jd|_d|_d|_d|_d|_g|_dS)Nr) superr__init__rrrrrr)self) __class__/usr/lib/python3.6/helper.pyr;szHelper.__init__cCs.d|_d|_d|_d|_d|_|jdd=dS)Nr)rrrrrr)rr!r!r"cleanupDs zHelper.cleanupcCsRt|j|_t|j|_t|j|_t|j|_t|j|_dd|jD|_dS)z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSs g|]\}}t|t|fqSr!)r).0ZpoZprr!r!r" Usz)Helper.encode_strings..N)rrrrrrr)rr!r!r"encode_stringsLs      zHelper.encode_stringscCs(ddg}||kr$ttjd||fdS)NZipv4Zipv6z'%s' not in '%s')rrZ INVALID_IPV)rZipvZipvsr!r!r" check_ipvWszHelper.check_ipvcCsz|dkr0xl|D]}t|dt|dqWnF|dkrv|jdsRttjd|t|jdddkrvttjd|dS) Nrrr nf_conntrack_z('%s' does not start with 'nf_conntrack_'rzModule name '%s' too short)r r startswithrrINVALID_MODULElenreplace)rritemZ all_configrr!r!r" _check_config]s    zHelper._check_config)rr)rr)rr)rr)rr)rr)__name__ __module__ __qualname__ZIMPORT_EXPORT_STRUCTUREZDBUS_SIGNATUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSrr#r&r'r/ __classcell__r!r!)r r"r&s$     c@seZdZddZdS)helper_ContentHandlercCs>tj||||jj|||dkrd|kr8|d|j_d|kr\|jj|d|d|j_d|kr|djdstt j d|dt |dj dddkrtt j d |d|d|j_ nz|d krnp|d krnf|d kr:t|d t|d |d |d f}||jjkr$|jjj|ntjd|d |d dS)Nrrrrr)z('%s' does not start with 'nf_conntrack_'rr(zModule name '%s' too shortrrrrz#Port '%s/%s' already set, ignoring.)r startElementr.Zparser_check_element_attrsrr'rr*rrr+r,r-rr r rappendr Zwarning)rrattrsentryr!r!r"r5ns>      z"helper_ContentHandler.startElementN)r0r1r2r5r!r!r!r"r4msr4c Cst}|jds ttjd||dd |_|j|j||_||_|j t j rVdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r}zttjd|jWYdd}~XnXWdQRX~~tr|j|S) Nz.xmlz'%s' is missing .xml suffixFTz%s/%srbznot a valid helper file: %s)rendswithrrZ INVALID_NAMErZ check_namefilenamepathr*r ETC_FIREWALLDZbuiltindefaultr4saxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZINVALID_HELPERZ getExceptionrr&) r=r>rhandlerparserrfsourcemsgr!r!r"rs8        (c CsP|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|ji}|j|d <|jr|jd kr|j|d <|jr<|jd kr<|j|d <|jd ||jd|jr|jd kr|jd|jdi|j|j|jd|jd|jr|jd kr|jd|jdi|j|j|jd|jdx>|jD]4} |jd|jd| d| dd|jdqW|jd |jd|j|j~dS)Nz%s/%sz %s/%s.xmlz%s.oldzBackup of file '%s' failed: %siZwtzUTF-8)modeencodingrrrrr z rrrrr()rr) r>r=rosexistsshutilZcopy2 Exceptionr errordirnamer*rr?mkdiriorBr Z startDocumentrrrr5ZignorableWhitespacerZ charactersZ endElementrrZ simpleElementZ endDocumentclose) rr>_pathrrHdirpathrFrDr7rr!r!r"rs\                       )N)__all__Zxml.saxrArLrSrNZfirewallrZfirewall.functionsrZfirewall.core.io.io_objectrrr r r r Zfirewall.core.loggerr rZfirewall.errorsrrr4rrr!r!r!r"s        G#PK!Vq/)io/__pycache__/ifcfg.cpython-36.opt-1.pycnu[3 Yj@s^dZdgZddlZddlZddlZddlZddlmZddl m Z m Z m Z Gddde ZdS)zifcfg file parserifcfgN)log)b2uu2bPY2c@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dS)rcCsi|_g|_||_|jdS)N)_config_deletedfilenameclear)selfr r /usr/lib/python3.6/ifcfg.py__init__#szifcfg.__init__cCsi|_g|_dS)N)rr)r r r r r )sz ifcfg.clearcCs|jjdS)N)rr )r r r r cleanup-sz ifcfg.cleanupcCs|jj|jS)N)rgetstrip)r keyr r r r0sz ifcfg.getcCs8t|j}t|j|j|<||jkr4|jj|dS)N)rrrrremove)r rvalueZ_keyr r r set3s  z ifcfg.setcCsHd}x2|jjD]$\}}|r$|d7}|d||f7}qWtrDt|S|S)N z%s=%s)ritemsrr)r srrr r r __str__9s z ifcfg.__str__cCsB|jyt|jd}Wn4tk rL}ztjd|j|WYdd}~XnXx|D]}|s^P|j}t|dksT|ddkrqTdd|jd dD}t|d krqTt|dd kr|dj d r|dj d r|ddd|d<|dd krqTn,|j j |ddk r tj d |j|jqT|d|j |d<qTW|jdS)NrzFailed to load '%s': %sr#;cSsg|] }|jqSr )r).0xr r r Qszifcfg.read..="rz%%s: Duplicate option definition: '%s')rr)r openr Exceptionrerrorrlensplit startswithendswithrrZwarningclose)r fmsglineZpairr r r readBs2   z ifcfg.readc :Cst|jdkrdSg}y.tjddtjj|jtjj|jdd}Wn2t k rv}zt j d|WYdd}~XnXd}d}yt j |jddd }WnNt k r}z0tjj|jrt j d |j|fnd}WYdd}~XndXx^|D]T}|sP|jd }t|dkr(|sD|jd d }q|d dkrPd}|j||jd q|jdd}t|dkr~d}|j|d q|d j} |dj} t| dkr| jdr| jdr| dd} | |kr@| |jkr|j| | krd}|jd| |j| fd }n$| |jkr"d }nd}|j|d |j| qd }qWt|jd krxF|jjD]8\} } | |krzqd|sd }|jd| | fd }qdW|r|j|j|stj|jdStjj|jr8ytj|jd|jWnBt k r6}z$tj|jtd|j|fWYdd}~XnXytj|j|jWnBt k r}z$tj|jtd|j|fWYdd}~XnXtj|jddS)NrZwtz%s.F)modeprefixdirdeletez!Failed to open temporary file: %sZrtzUTF-8)r2encodingzFailed to open '%s': %srTrrr"r#r$z%s=%s z%s.bakzBackup of '%s' failed: %szFailed to create '%s': %sir%)r)rtempfileZNamedTemporaryFileospathbasenamer dirnamer'rr(ior&existsrwriter*r+r,rappendrr-rnameshutilZcopy2IOErrorZmovechmod) r doneZ temp_filer/Zmodifiedemptyr.r0prrr r r r>_s               $ $z ifcfg.writeN) __name__ __module__ __qualname__rr rrrrr1r>r r r r r"s )__doc____all__Zos.pathr8r<r7rAZfirewall.core.loggerrZfirewall.functionsrrrobjectrr r r r s PK!9),),#io/__pycache__/ipset.cpython-36.pycnu[3 YjU@sdZdddgZddljZddlZddlZddlZddlmZddl m Z m Z m Z m Z mZmZmZmZmZddlmZmZmZmZdd lmZmZdd lmZmZmZmZdd l m!Z!dd lm"Z"dd l#m$Z$GdddeZ%GdddeZ&ddZ'dddZ(dS)z$ipset io XML handler, reader, writerIPSet ipset_reader ipset_writerN)config) checkIPcheckIP6 checkIPnMask checkIP6nMask u2b_if_py2 check_mac check_portcheckInterface checkProtocol)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator) IPSET_TYPESIPSET_CREATE_OPTIONS)check_icmp_namecheck_icmp_type_codecheck_icmpv6_namecheck_icmpv6_type_code)log)errors) FirewallErrorcseZdZddd d!dddifddgffZdZd d d d gZd d dgdgd dZdgdgdZfddZddZ ddZ e ddZ ddZ fddZZS)"rversionshort descriptiontypeoptionsentriesz (ssssa{ss}as)_-:.Nname)rripsetoptionentryvalue)r(r)cs<tt|jd|_d|_d|_d|_g|_i|_d|_ dS)NrF) superr__init__rrrr r"r!applied)self) __class__/usr/lib/python3.6/ipset.pyr-CszIPSet.__init__cCs8d|_d|_d|_d|_|jdd=|jjd|_dS)NrF)rrrr r"r!clearr.)r/r1r1r2cleanupMs  z IPSet.cleanupcCs\t|j|_t|j|_t|j|_t|j|_dd|jjD|_dd|jD|_dS)z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSsi|]\}}t|t|qSr1)r ).0kvr1r1r2 ^sz(IPSet.encode_strings..cSsg|] }t|qSr1)r )r5er1r1r2 `sz(IPSet.encode_strings..N)r rrrr r!itemsr")r/r1r1r2encode_stringsVs    zIPSet.encode_stringscCsrd}d|kr|ddkrd}|jds6ttjd||ddjd}|jd}t|t|ksnt|d krttjd ||fxtt|D]}||}||}|d krd |ko|dkrh|d krttjd |||f|jd } t| dkrttjd||||fx| D]J} |dkr2t|  sH|dkrt |  rttjd| |||fqWnh|dkr|dkrttjd||||f|dkrt } nt} nt } | |sjttjd||||fq|dkr@d |kr|jd } t| dkrttjd||||f|dkr0t| d sJ|dkrft | d rfttjd| d|||f|dkrt | d  s|dkr>t | d  r>ttjd| d |||fn|j dr|dko|dko|dksttjd||||f|dkrt | s&|dkrjt | rjttjd||||fq|dkrvt | s`|dkrjttjd||fq|dkrld|krL|jd} t| dkrttjd|| ddkrP|dkrttjd||ft| d  rd| d krttjd| d |fn6| d jd\} } t| | sJttjd| d |fqj| dd2kr|dkr|ttjd||ft| d  rd| d krttjd"| d |fn6| d jd\} } t| | sJttjd"| d |fn^| dd3kr$t| d r$ttjd'| d|fn&t| d sjttjd(| d |fnt|sjttjd)||fq|d*kr |jd+ryt|d,}Wn*tk rttjd-||fYnXn8y t|}Wn*tk rttjd-||fYnX|dks |d.krjttjd-||fq|d/krZt| sDt|d0krjttjd1||fqttjd|qWdS)4NZipv4familyinet6Zipv6zhash:zipset type '%s' not usable,z)entry '%s' does not match ipset type '%s'Zipr$z invalid address '%s' in '%s'[%d]z.invalid address range '%s' in '%s' for %s (%s)z(invalid address '%s' in '%s' for %s (%s)z0.0.0.0rZnetz/0zhash:net,ifaceZmacz00:00:00:00:00:00z invalid mac address '%s' in '%s'Zportr%zinvalid port '%s'Zicmpz(invalid protocol for family '%s' in '%s'/zinvalid icmp type '%s' in '%s'icmpv6 ipv6-icmpz invalid icmpv6 type '%s' in '%s'tcpsctpudpudplitezinvalid protocol '%s' in '%s'zinvalid port '%s'in '%s'zinvalid port '%s' in '%s'ZmarkZ0xzinvalid mark '%s' in '%s'lZifacezinvalid interface '%s' in '%s')rDrE)rFrGrHrI) startswithrr INVALID_IPSETsplitlenZ INVALID_ENTRYrangerrrr endswithr rrrrrr int ValueErrorr )r*r!Z ipset_typer=flagsr;iflagitemZsplitsZ_splitZip_checkZ_type_codeZint_valr1r1r2 check_entrybsT                                  zIPSet.check_entrycCs|dkr |tkr ttjd||dkrx|jD]}|tkrNttjd||dkryt||}Wn,tk rttj d|||fYnX|d krttj d |||fq2|d kr2||dkr2ttj ||q2WdS)Nr z'%s' is not valid ipset typer!zipset invalid option '%s'timeouthashsizemaxelemz)Option '%s': Value '%s' is not an integerrz#Option '%s': Value '%s' is negativer=inetr>)rZr[r\)r]r>) rrr INVALID_TYPEkeysrrMrRrS INVALID_VALUEINVALID_FAMILY)r/rrWZ all_configkey int_valuer1r1r2 _check_config&s2   zIPSet._check_configcsrd|dkr6|dddkr6t|ddkr6ttjx&|dD]}tj||d|dq@Wtt|j|dS)NrZ0r?r)rOrrZIPSET_WITH_TIMEOUTrrYr, import_config)r/rr*)r0r1r2rhAs  zIPSet.import_config)rr)rr)rr)r r)__name__ __module__ __qualname__ZIMPORT_EXPORT_STRUCTUREZDBUS_SIGNATUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSr-r4r< staticmethodrYrdrh __classcell__r1r1)r0r2r,s,       Ec@seZdZddZddZdS)ipset_ContentHandlerc Cstj||||jj|||dkrpd|krX|dtkrLttjd|d|d|j_d|krl|d|j_ nz|dkr|nn|dkrnb|dkrd}d |kr|d }|d dkrttj d|d |jjdko|d dkrttj d|d |jjf|d dkr&| r&ttj d|d |d dkry t |}Wn.t k rnttj d|d |fYnX|dkrttj d|d |f|d d kr|dkrttj||d |jjkr||jj|d <ntjd|d dS)Nr(r z%srrrr)rr+r'r=rZr[r\zUnknown option '%s'zhash:macz%Unsupported option '%s' for type '%s'z&Missing mandatory value of option '%s'z)Option '%s': Value '%s' is not an integerrz#Option '%s': Value '%s' is negativer]r>z Option %s already set, ignoring.)r=rZr[r\)r=)r=rZr[r\)rZr[r\)r]r>)r startElementrWZparser_check_element_attrsrrrr^r rZINVALID_OPTIONrRrSr`rar!rwarning)r/r'attrsr+rcr1r1r2roLsd      z!ipset_ContentHandler.startElementcCs(tj|||dkr$|jjj|jdS)Nr*)r endElementrWr"appendZ_element)r/r'r1r1r2rrs zipset_ContentHandler.endElementN)rirjrkrorrr1r1r1r2rnKs7rnc %Cst}|jds ttjd||dd|_|j|j||_||_|j t j rVdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r}zttjd|jWYdd}~XnXWdQRX~~d |jkrF|jd d krFt|jd krFtjd |j|jdd=d } t} x| t|jkr|j| | krtjd |j| |jj| nry|j |j| |j|j!Wn<tk r} ztjd| |jj| WYdd} ~ XnX| j"|j| | d7} qRW~ t#r|j$|S)Nz.xmlz'%s' is missing .xml suffixreFTz%s/%srbznot a valid ipset file: %srZrfrz6ipset '%s': timeout option is set, entries are ignoredzEntry %s already set, ignoring.z %s, ignoring.rA)%rrQrrZ INVALID_NAMEr'Z check_namefilenamepathrLr ETC_FIREWALLDZbuiltindefaultrnsaxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionrMZ getExceptionr!rOr"rrpsetpoprYr addrr<) rvrwr(handlerparserr'fsourcemsgrUZ entries_setr9r1r1r2rs^        (  c Cs|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|jd |ji}|jr|jd kr|j|d <|jd ||jd |jrz|jd krz|jd|jdi|j|j|jd|jd |jr|jd kr|jd|jdi|j|j|jd|jd xZ|jjD]L\} } |jd| d kr|jd| | dn|jdd| i|jd qWxD|jD]:} |jd|jdi|j| |jd|jd q(W|jd |jd |j|j ~dS)Nz%s/%sz %s/%s.xmlz%s.oldzBackup of file '%s' failed: %siZwtzUTF-8)modeencodingr rrr( z rrr))r'r+r'r*)!rwrvr'osexistsshutilZcopy2 ExceptionrerrordirnamerLrrxmkdirior{rZ startDocumentr rroZignorableWhitespacerZ charactersrrrr!r;Z simpleElementr"Z endDocumentclose) r(rw_pathr'rdirpathrrrqrbr+r*r1r1r2rsf                           )N))__doc____all__Zxml.saxrzrrrZfirewallrZfirewall.functionsrrrr r r r r rZfirewall.core.io.io_objectrrrrZfirewall.core.ipsetrrZfirewall.core.icmprrrrZfirewall.core.loggerrrZfirewall.errorsrrrnrrr1r1r1r2s&   ,   !=5PK!z! 'io/__pycache__/functions.cpython-36.pycnu[3 Yjy@sddlZddlmZddlmZddlmZddlmZddl m Z ddl m Z ddl mZdd lmZdd lmZdd lmZdd lmZdd lmZddZdS)N)config) FirewallError)FirewallConfig) zone_reader)service_reader) ipset_reader)icmptype_reader) helper_reader) policy_reader)Direct)LockdownWhitelist)firewalld_confc -Cs|t|}t|jtjtjgdt|jtjtj gdt |j tj tj gdt|jtjtjgdt|jtjtjgdt|jtjtjgdd}x |jD]}x||dD]}tjj|sqxttj|D]}|j dryD||d||}|d kr||_!|j"|j#||d|Wqt$k rT}zt$|j%d ||j&fWYdd}~Xqt'k r}zt'd ||fWYdd}~XqXqWqWqWtjj(tj)r:y$t*tj)}|j+|j,|j-Wnpt$k r}zt$|j%d tj)|j&fWYdd}~Xn6t'k r8}zt'd tj)|fWYdd}~XnXtjj(tj.ry$t/tj.}|j+|j,|j-Wnpt$k r}zt$|j%d tj.|j&fWYdd}~Xn6t'k r}zt'd tj.|fWYdd}~XnXtjj(tj0rxyt1tj0}|j+Wnpt$k rB}zt$|j%d tj0|j&fWYdd}~Xn6t'k rv}zt'd tj0|fWYdd}~XnXdS) N)readeradddirs)ZipsethelperZicmptypeZservicezonepolicyrz.xmlrrrrz'%s': %s)rr)2rrZ add_ipsetrZFIREWALLD_IPSETSZETC_FIREWALLD_IPSETSr Z add_helperZFIREWALLD_HELPERSZETC_FIREWALLD_HELPERSrZ add_icmptypeZFIREWALLD_ICMPTYPESZETC_FIREWALLD_ICMPTYPESrZ add_serviceZFIREWALLD_SERVICESZETC_FIREWALLD_SERVICESrZadd_zoneZFIREWALLD_ZONESZETC_FIREWALLD_ZONESr Zadd_policy_objectZFIREWALLD_POLICIESZETC_FIREWALLD_POLICIESkeysospathisdirsortedlistdirendswith fw_configZcheck_config_dictZexport_config_dictrcodemsg ExceptionisfileZFIREWALLD_DIRECTr read check_configZ export_configZLOCKDOWN_WHITELISTr ZFIREWALLD_CONFr ) fwrZreadersrZ_dirfileobjerrorrr&/usr/lib/python3.6/functions.pyr!&sz   &. ($ ($  (r!)rZfirewallrZfirewall.errorsrZfirewall.core.fw_configrZfirewall.core.io.zonerZfirewall.core.io.servicerZfirewall.core.io.ipsetrZfirewall.core.io.icmptyperZfirewall.core.io.helperr Zfirewall.core.io.policyr Zfirewall.core.io.directr Z#firewall.core.io.lockdown_whitelistr Zfirewall.core.io.firewalld_confr r!r&r&r&r's            PK!?xR`/`/-io/__pycache__/io_object.cpython-36.opt-1.pycnu[3 Yj5@sdZddddddddgZd d ljZd d ljjZd d lZd d lZd d lm Z d d l m Z d d l m Z d dl mZd dlmZejdkZGdddeZGdddeZGdddeZGdddeZGdddejjZGdddejZddZddZddZ ddZ!d S)z5Generic io_object handler, io specific check methods.PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator check_port check_tcpudpcheck_protocol check_addressN) OrderedDict) functions)b2u)errors) FirewallError3c@s|eZdZdZfZdZgZiZiZddZ ddZ ddZ d d Z d d Z d dZddZddZddZddZddZdS)rz; Abstract IO_Object as base for icmptype, service and zone z()cCs"d|_d|_d|_d|_d|_dS)NF)filenamepathnamedefaultZbuiltin)selfr/usr/lib/python3.6/io_object.py__init__2s zIO_Object.__init__cCs6g}x(|jD]}|jtjt||dq Wt|S)Nr )IMPORT_EXPORT_STRUCTUREappendcopydeepcopygetattrtuple)rretxrrr export_config9s zIO_Object.export_configcCsXi}tdd|jD}x:|D]2}t||sAsz0IO_Object.export_config_dict..)dictrr isinstanceboolrr)rconf type_formatskeyrrrexport_config_dict?s  zIO_Object.export_config_dictcCs|j|xt|jD]~\}\}}t||tr~g}t}x,||D] }||krD|j||j|qDW~t||t j |qt||t j ||qWdS)N) check_config enumeraterr&listsetraddsetattrrr)rr(ielementZdummyZ_confZ_setr rrr import_configGs  zIO_Object.import_configc Cs~|j|xn|D]f}t||s0ttjdj|t||tr`t||tt j t j ||qt||t j ||qWdS)Nz-Internal error. '{}' is not a valid attribute) check_config_dicthasattrrr Z UNKNOWN_ERRORformatr&r.r1r fromkeysrr)rr(r*rrrimport_config_dictWs   "zIO_Object.import_config_dictcCszt|ts(ttjd|tdt|ft|dkr@ttjdx4|D],}|j rF||j krFttjd||fqFWdS)Nz'%s' not of type %s, but %srr"zname can't be emptyz'%s' is not allowed in '%s') r&strrr INVALID_TYPEtypelenZ INVALID_NAMEisalnumADDITIONAL_ALNUM_CHARS)rrcharrrr check_namecs     zIO_Object.check_namecCsjt|t|jkr0ttjdt|t|jfi}x&t|jD]\}\}}||||<q@W|j|dS)Nz structure size mismatch %d != %d)r=rrr r;r-r5)rr(Z conf_dictr2r yrrrr,pszIO_Object.check_configcCsrtdd|jD}xX|D]P}|dd|jDkrDttjdj||j|||||j||||qWdS)NcSsg|]}|d|dfqS)r r"r)r#r rrrr$|sz/IO_Object.check_config_dict..cSsg|] \}}|qSrr)r#r rBrrrr$~szoption '{}' is not valid)r%rrr ZINVALID_OPTIONr7_check_config_structure _check_config)rr(r)r*rrrr5{s  zIO_Object.check_config_dictcCsdS)Nr)rZdummy1Zdummy2Zdummy3rrrrDszIO_Object._check_configc Cs`t|t|s,ttjd|t|t|ft|trrt|dkrRttjd|x|D]}|j||dqXWnt|trt|t|krttjd|t|fxt |D]\}}|j|||qWnt|t r\t|j d\}}xn|j D]b\}}t|t|s,ttjd|t|t|ft|t|sttjd|t|t|fqWdS)Nz'%s' not of type %s, but %sr"zlen('%s') != 1r zlen('%s') != %d) r&r<rr r;r.r=rCrr-r%items) rr(Z structurer r2valueZskeyZsvaluer*rrrrCs8      z!IO_Object._check_config_structurecCs|j}d}||jkrdd}|j|dk rdx:|j|D],}||krL|j|q4ttjd||fq4W||jkrd}x$|j|D]}||kr~|j|q~W|sttjd|x |D]}ttjd||fqWdS)NFTzMissing attribute %s for %szUnexpected element %sz%s: Unexpected attribute %s)ZgetNamesPARSER_REQUIRED_ELEMENT_ATTRSremoverr Z PARSE_ERRORPARSER_OPTIONAL_ELEMENT_ATTRS)rrattrsZ_attrsfoundr rrrparser_check_element_attrss,     z$IO_Object.parser_check_element_attrsN)__name__ __module__ __qualname____doc__rZDBUS_SIGNATUREr?rGrIrr!r+r4r9rAr,r5rDrCrLrrrrr)s"   !cs$eZdZfddZddZZS)UnexpectedElementErrorcstt|j||_dS)N)superrQrr)rr) __class__rrrszUnexpectedElementError.__init__cCs d|jS)NzUnexpected element '%s')r)rrrr__str__szUnexpectedElementError.__str__)rMrNrOrrT __classcell__rr)rSrrQs rQcs$eZdZfddZddZZS)MissingAttributeErrorcstt|j||_||_dS)N)rRrVrr attribute)rrrW)rSrrrszMissingAttributeError.__init__cCsd|j|jfS)Nz$Element '%s': missing '%s' attribute)rrW)rrrrrTszMissingAttributeError.__str__)rMrNrOrrTrUrr)rSrrVs rVcs$eZdZfddZddZZS)UnexpectedAttributeErrorcstt|j||_||_dS)N)rRrXrrrW)rrrW)rSrrrsz!UnexpectedAttributeError.__init__cCsd|j|jfS)Nz'Element '%s': unexpected attribute '%s')rrW)rrrrrTsz UnexpectedAttributeError.__str__)rMrNrOrrTrUrr)rSrrXs rXc@s4eZdZddZddZddZddZd d Zd S) rcCs||_d|_dS)Nr)item_element)rrYrrrrsz!IO_Object_ContentHandler.__init__cCs d|_dS)Nr)rZ)rrrr startDocumentsz&IO_Object_ContentHandler.startDocumentcCs d|_dS)Nr)rZ)rrrJrrr startElementsz%IO_Object_ContentHandler.startElementcCs*|dkr|j|j_n|dkr&|j|j_dS)Nshort description)rZrYr]r^)rrrrr endElements z#IO_Object_ContentHandler.endElementcCs|j|jdd7_dS)N  )rZreplace)rcontentrrr characterssz#IO_Object_ContentHandler.charactersN)rMrNrOrr[r\r_rdrrrrrs c@s<eZdZddZddZddZddZd d Zd d Zd S)rcCsNtjjj||j|_|j|_ig|_|jd|_ g|_ d|_ d|_ d|_ dS)Nr"zutf-8F)saxhandlerContentHandlerrwrite_writeflushZ_flushZ _ns_contextsZ_current_contextZ_undeclared_ns_mapsZ _encodingZ_pending_start_elementZ_short_empty_elements)routrrrrs zIO_Object_XMLGenerator.__init__cCs*trdd|jD}tjj|||dS)a saxutils.XMLGenerator.startElement() expects name and attrs to be unicode and bad things happen if any of them is (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. cSsi|]\}}t|t|qSr)r )r#rrFrrr sz7IO_Object_XMLGenerator.startElement..N)rrEsaxutils XMLGeneratorr\)rrrJrrrr\sz#IO_Object_XMLGenerator.startElementcCstrX|jdt|x4|jD](\}}|jdt|tjt|fq W|jdnF|jd|x,|jD] \}}|jd|tj|fqpW|jddS)z* slightly modified startElement() N)rrjr rErnZ quoteattr)rrrJrFrrr simpleElements  z$IO_Object_XMLGenerator.simpleElementcCstjj|t|dS)z saxutils.XMLGenerator.endElement() expects name to be unicode and bad things happen if it's (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. N)rnror_r )rrrrrr_sz!IO_Object_XMLGenerator.endElementcCstjj|t|dS)z saxutils.XMLGenerator.characters() expects content to be unicode and bad things happen if it's (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. N)rnrordr )rrcrrrrd%sz!IO_Object_XMLGenerator.characterscCstjj|t|dS)a saxutils.XMLGenerator.ignorableWhitespace() expects content to be unicode and bad things happen if it's (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. N)rnroignorableWhitespacer )rrcrrrrr-sz*IO_Object_XMLGenerator.ignorableWhitespaceN) rMrNrOrr\rqr_rdrrrrrrrs  cCstj|}|dkr$ttjd|n`|dkr>ttjd|nF|dkrXttjd|n,t|dkr|d|dkrttjd|dS) Nzport number in '%s' is too bigr"z'%s' is invalid port rangezport range '%s' is ambiguousr re)r Z getPortRangerr Z INVALID_PORTr=)ZportZ port_rangerrrr5s    cCs|dkrttjd|dS)Ntcpudpsctpdccpz)'%s' not from {'tcp'|'udp'|'sctp'|'dccp'})rurvrwrx)rr INVALID_PROTOCOL)protocolrrrrDscCstj|sttj|dS)N)r Z checkProtocolrr ry)rzrrrrJs cCs$tj||s ttjd||fdS)Nz'%s' is not valid %s address)r rrr Z INVALID_ADDR)ZipvZaddrrrrrNs )"rP__all__Zxml.saxrfZxml.sax.saxutilsrnrsys collectionsr Zfirewallr Zfirewall.functionsr r Zfirewall.errorsrversionrobjectr ExceptionrQrVrXrgrhrrorrrrrrrrrs0           CPK! h%h%0io/__pycache__/lockdown_whitelist.cpython-36.pycnu[3 Yj1@sddljZddlZddlZddlZddlmZddlmZm Z m Z m Z ddl m Z ddlmZmZmZmZmZmZddlmZddlmZGdd d e ZGd d d e ZdS) N)config)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator)log)uniqify checkUsercheckUid checkCommand checkContext u2b_if_py2)errors) FirewallErrorc@seZdZddZddZdS)!lockdown_whitelist_ContentHandlercCstj||d|_dS)NF)r__init__ whitelist)selfitemr(/usr/lib/python3.6/lockdown_whitelist.pyr%s z*lockdown_whitelist_ContentHandler.__init__c CsVtj||||jj|||dkr@|jr6ttjdd|_n|dkrr|js\tj ddS|d}|jj |n|dkr|jstj ddSd |kryt |d }Wn&t k rtj d |d dSX|jj |nd|kr|jj|dn\|d kr@|jstj d dSd |kr.tj ddS|jj|d ntj d|dSdS)NrzMore than one whitelist.Tcommandz)Parse Error: command outside of whitelistnameuserz&Parse Error: user outside of whitelistidz"Parse Error: %s is not a valid uidselinuxz)Parse Error: selinux outside of whitelistcontextzParse Error: no contextzUnknown XML element %s)r startElementrZparser_check_element_attrsrrrZ PARSE_ERRORrerror add_commandint ValueErroradd_uidadd_user add_context)rrZattrsruidrrrr)sJ        z.lockdown_whitelist_ContentHandler.startElementN)__name__ __module__ __qualname__rrrrrrr$srcs4eZdZdZddgfddgfddgfddgffZdZd gZd d gd d gd Zddd giZfddZ ddZ ddZ ddZ ddZ ddZddZddZd d!Zd"d#Zd$d%Zd&d'Zd(d)Zd*d+Zd,d-Zd.d/Zd0d1Zd2d3Zd4d5Zd6d7Zd8d9Zd:d;Zdd?Z d@dAZ!dBdCZ"Z#S)DLockdownWhitelistz LockdownWhitelist class commandscontextsusersuidsrz (asasasai)_Nrr)rrrrrrcs6tt|j||_d|_g|_g|_g|_g|_dS)N) superr)rfilenameparserr*r,r-r.)rr1) __class__rrrnszLockdownWhitelist.__init__cCs|d kr.x|D]}|j||dd |qWnv|dkrLt|sttj|nX|dkrjt|sttj|n:|dkrt|sttj|n|d krt |sttj |dS) Nr*r,r-r.rrrr%)r*r,r-r.) _check_configr rrINVALID_COMMANDr INVALID_CONTEXTr INVALID_USERr INVALID_UID)rrrZ all_configxrrrr6ys zLockdownWhitelist._check_configcCs4|jdd=|jdd=|jdd=|jdd=dS)N)r*r,r-r.)rrrrcleanups   zLockdownWhitelist.cleanupcCs:dd|jD|_dd|jD|_dd|jD|_dS)z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSsg|] }t|qSr)r ).0r;rrr sz4LockdownWhitelist.encode_strings..cSsg|] }t|qSr)r )r=r;rrrr>scSsg|] }t|qSr)r )r=r;rrrr>sN)r*r,r-)rrrrencode_stringssz LockdownWhitelist.encode_stringscCs@t|sttj|||jkr,|jj|nttjd|dS)Nz!Command "%s" already in whitelist)r rrr7r*appendALREADY_ENABLED)rrrrrrs   zLockdownWhitelist.add_commandcCs,||jkr|jj|nttjd|dS)NzCommand "%s" not in whitelist.)r*removerr NOT_ENABLED)rrrrrremove_commands z LockdownWhitelist.remove_commandcCs ||jkS)N)r*)rrrrr has_commandszLockdownWhitelist.has_commandcCsBx<|jD]2}|jdr.|j|ddr:dSq||krdSqWdS)N*r4TFr5)r*endswith startswith)rrZ_commandrrr match_commands  zLockdownWhitelist.match_commandcCs|jS)N)r*)rrrr get_commandsszLockdownWhitelist.get_commandscCsDt|sttjt|||jkr0|jj|nttjd|dS)NzUid "%s" already in whitelist)r rrr:strr.r@rA)rr%rrrr"s  zLockdownWhitelist.add_uidcCs,||jkr|jj|nttjd|dS)NzUid "%s" not in whitelist.)r.rBrrrC)rr%rrr remove_uids zLockdownWhitelist.remove_uidcCs ||jkS)N)r.)rr%rrrhas_uidszLockdownWhitelist.has_uidcCs ||jkS)N)r.)rr%rrr match_uidszLockdownWhitelist.match_uidcCs|jS)N)r.)rrrrget_uidsszLockdownWhitelist.get_uidscCs@t|sttj|||jkr,|jj|nttjd|dS)NzUser "%s" already in whitelist)r rrr9r-r@rA)rrrrrr#s   zLockdownWhitelist.add_usercCs,||jkr|jj|nttjd|dS)NzUser "%s" not in whitelist.)r-rBrrrC)rrrrr remove_users zLockdownWhitelist.remove_usercCs ||jkS)N)r-)rrrrrhas_userszLockdownWhitelist.has_usercCs ||jkS)N)r-)rrrrr match_userszLockdownWhitelist.match_usercCs|jS)N)r-)rrrr get_usersszLockdownWhitelist.get_userscCs@t|sttj|||jkr,|jj|nttjd|dS)Nz!Context "%s" already in whitelist)r rrr8r,r@rA)rrrrrr$"s   zLockdownWhitelist.add_contextcCs,||jkr|jj|nttjd|dS)NzContext "%s" not in whitelist.)r,rBrrrC)rrrrrremove_context,s z LockdownWhitelist.remove_contextcCs ||jkS)N)r,)rrrrr has_context3szLockdownWhitelist.has_contextcCs ||jkS)N)r,)rrrrr match_context6szLockdownWhitelist.match_contextcCs|jS)N)r,)rrrr get_contexts9szLockdownWhitelist.get_contextscCs|j|jjds&ttjd|jt|}tj}|j |y|j |jWn8tj k r}zttj d|j WYdd}~XnX~~tr|jdS)Nz.xmlz'%s' is missing .xml suffixzNot a valid file: %s)r<r1rGrrZ INVALID_NAMErsaxZ make_parserZsetContentHandlerparseZSAXParseExceptionZ INVALID_TYPEZ getExceptionrr?)rhandlerr2msgrrrread>s"   zLockdownWhitelist.readcCstjj|jr\ytj|jd|jWn4tk rZ}ztd|j|fWYdd}~XnXtjjtj sxtj tj dt j |jddd}t |}|j|jdi|jdx6t|jD](}|jd |jd d |i|jdqWx:t|jD],}|jd |jd d t|i|jdqWx8t|jD]*}|jd |jd d |i|jdq0Wx8t|jD]*}|jd |jdd|i|jdqjW|jd|jd|j|j~dS)Nz%s.oldzBackup of '%s' failed: %siZwtzUTF-8)modeencodingr z rrrrrr)ospathexistsr1shutilZcopy2 ExceptionIOErrorrZ ETC_FIREWALLDmkdirioopenrZ startDocumentrZignorableWhitespacerr*Z simpleElementr.rKr-r,Z endElementZ endDocumentclose)rr[frZrr%rrrrrwriteQsB$         zLockdownWhitelist.write)$r&r'r(__doc__ZIMPORT_EXPORT_STRUCTUREZDBUS_SIGNATUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSrr6r<r?rrDrErIrJr"rLrMrNrOr#rPrQrRrSr$rTrUrVrWr\rk __classcell__rr)r3rr)WsL         1 r))Zxml.saxrXr`rgrcZfirewallrZfirewall.core.io.io_objectrrrrZfirewall.core.loggerrZfirewall.functionsrr r r r r rZfirewall.errorsrrr)rrrrs      3PK!(# %io/__pycache__/service.cpython-36.pycnu[3 Yj2@sdddgZddljZddlZddlZddlZddlmZddlm Z ddl m Z m Z m Z mZmZmZmZmZddlmZdd lmZdd lmZGd dde ZGd d d e ZddZdddZdS)Serviceservice_readerservice_writerN)config) u2b_if_py2)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator check_port check_tcpudpcheck_protocol check_address)log)errors) FirewallErrorc seZdZd d!d"dd#gfddgfdddifddgfd d$gfd dgfd dgff Zd d gZddddZddgddgdgdgddgddgdgdgdZfddZddZddZ ddZ Z S)%rversionshort descriptionportsmodules destination protocols source_portsincludeshelpers_-N)rrservicenameportprotocolvalueipv4ipv6r)rr!r"modulerz source-portincludehelpercsNtt|jd|_d|_d|_g|_g|_g|_i|_ g|_ g|_ g|_ dS)Nr) superr__init__rrrrrrrrrr)self) __class__/usr/lib/python3.6/service.pyr*DszService.__init__cCshd|_d|_d|_|jdd=|jdd=|jdd=|jj|jdd=|j dd=|j dd=dS)Nr) rrrrrrrclearrrr)r+r-r-r.cleanupQs      zService.cleanupcCst|j|_t|j|_t|j|_dd|jD|_dd|jD|_dd|jjD|_dd|jD|_dd|j D|_ dd|j D|_ d d|j D|_ d S) z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSs g|]\}}t|t|fqSr-)r).0poprr-r-r. dsz*Service.encode_strings..cSsg|] }t|qSr-)r)r1mr-r-r.r4escSsi|]\}}t|t|qSr-)r)r1kvr-r-r. fsz*Service.encode_strings..cSsg|] }t|qSr-)r)r1r3r-r-r.r4gscSs g|]\}}t|t|fqSr-)r)r1r2r3r-r-r.r4hscSsg|] }t|qSr-)r)r1sr-r-r.r4jscSsg|] }t|qSr-)r)r1r9r-r-r.r4ksN) rrrrrrritemsrrrr)r+r-r-r.encode_strings]s    zService.encode_stringscCs:|dkrJx>|D]6}|ddkr8t|dt|dqt|dqWn|dkrjx|D] }t|qXWn|dkrx|D]}t|dt|dqxWn|dkrx|D]*}|dkrttjd |t|||qWn^|d kr6xR|D]J}|jd r|jd d}d |kr|jd d}t |dkrttj |qWdS)Nrrrrrrr$r%z'%s' not in {'ipv4'|'ipv6'}r nf_conntrack_rr)r$r%) r r r rrZINVALID_DESTINATIONr startswithreplacelenZINVALID_MODULE)r+ritemZ all_configr!protorr&r-r-r. _check_configms8              zService._check_config)rr)rr)rr)rr)rr) __name__ __module__ __qualname__ZIMPORT_EXPORT_STRUCTUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSr*r0r;rD __classcell__r-r-)r,r.r&s4     c@seZdZddZdS)service_ContentHandlercCs0tj||||jj|||dkrTd|krxRdD]J}||krt|||||jjkr&tjd|n|||jj|<qWn|dkr|d}|jdr~|jdd}d|kr~|jdd}||jjkr|jjj |n tjd|n|dkr|d|jjkr|jjj |dntjd|dn@|dkr,|d|jjkr|jjj |dntjd|ddS)Nrr z'Ignoring deprecated attribute name='%s'rrrr!rr"z#Port '%s/%s' already set, ignoring.z$Protocol '%s' already set, ignoring.r#z source-portz)SourcePort '%s/%s' already set, ignoring.rr$r%z2Destination address for '%s' already set, ignoringr&r=rrz"Module '%s' already set, ignoring.r'z#Include '%s' already set, ignoring.r(z"Helper '%s' already set, ignoring.)r$r%)r startElementrBZparser_check_element_attrsrZwarningrr r rappendr rrrrr?r@rrr)r+r attrsentryxr&r-r-r.rJs                       z#service_ContentHandler.startElementN)rErFrGrJr-r-r-r.rIsrIc Cst}|jds ttjd||dd |_|j|j||_||_|j t j rVdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r}zttjd|jWYdd}~XnXWdQRX~~tr|j|S) Nz.xmlz'%s' is missing .xml suffixFTz%s/%srbznot a valid service file: %s)rendswithrrZ INVALID_NAMEr Z check_namefilenamepathr?r ETC_FIREWALLDZbuiltindefaultrIsaxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZINVALID_SERVICEZ getExceptionrr;) rSrTrhandlerparserr fsourcemsgr-r-r.rs8        (cCsr|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|ji}|jr|jd kr|j|d <|jd ||jd |jrt|jd krt|jd |jdi|j|j|jd|jd |jr|jd kr|jd |jdi|j|j|jd|jd x>|jD]4} |jd |jd| d| dd|jd qWx4|jD]*} |jd |jdd| i|jd qWx>|jD]4} |jd |jd| d| dd|jd qs    (   mQPK!g22"io/__pycache__/zone.cpython-36.pycnu[3 YjM@sdddgZddljZddlZddlZddlZddlmZddlm Z m Z m Z m Z m Z mZmZddlmZmZddlmZmZmZmZdd lmZmZmZmZdd lmZdd lm Z dd lm!Z!dd l"m#Z#GdddeZ$GdddeZ%dddZ&dddZ'dS)Zone zone_reader zone_writerN)config) checkIPnMask checkIP6nMaskcheckInterfaceuniqifymax_zone_name_len u2b_if_py2 check_mac)DEFAULT_ZONE_TARGET ZONE_TARGETS)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator)common_startElementcommon_endElementcommon_check_config common_writer)rich)log)errors) FirewallErrorcsfeZdZdZd@dAdBdCdDd dgfd dEgfd dgfdFd dGgfddgfddgfddgfddgfddHgfdIdJfZdddgZddddgddgdgdgdddgdgddddgddgddddddgdgddZddddgd gd!d"gd#d$gd%d&d'd#d(gd%d'd(gd)d*gd+gd,gd- Zed.d/Z fd0d1Z d2d3Z d4d5Z fd6d7Z fd8d9Zd:d;Zfdd?ZZS)Krz Zone class versionshort descriptionUNUSEDFtargetservicesports icmp_blocks masquerade forward_ports interfacessources rules_str protocols source_portsicmp_block_inversionforward_-/Nnameportprotocolvalueset)rrzoneservicer1z icmp-blockz icmp-typer,z forward-port interfacerulesource destinationr2z source-portrZauditZacceptrejectZdropZmarklimitzicmp-block-inversion immutableZenabledzto-portzto-addrfamilyZpriorityaddressmacinvertipsetprefixleveltypeZburst) r5r$z forward-portr8r9r:rr;r<cCs8x&ttjD]\}\}}||kr |Sq WttjddS)Nz index_of()) enumeraterIMPORT_EXPORT_STRUCTURErrZ UNKNOWN_ERROR)elementiZelZdummyrJ/usr/lib/python3.6/zone.pyindex_ofdsz Zone.index_ofcstt|jd|_d|_d|_d|_t|_g|_ g|_ g|_ g|_ d|_ d|_g|_g|_g|_g|_d|_g|_g|_d|_d|_d|_dS)NrF)superr__init__rrrrr r r!r"r)r#r,r$r%r*r&r' fw_configrulesr(r+combinedapplied)self) __class__rJrKrNks,z Zone.__init__cCsd|_d|_d|_d|_t|_|jdd=|jdd=|jdd=|j dd=d|_ d|_ |j dd=|j dd=|jdd=|jdd=d|_|jdd=|jdd=d|_d|_d|_dS)NrF)rrrrr r r!r"r)r#r,r$r%r*r&r'rOrPr(r+rQrR)rSrJrJrKcleanups*          z Zone.cleanupcCst|j|_t|j|_t|j|_t|j|_dd|jD|_dd|jD|_dd|jD|_dd|jD|_dd|j D|_ dd|j D|_ dd|j D|_ d d|j D|_ d d|j D|_ d d|jD|_d S) z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSsg|] }t|qSrJ)r ).0srJrJrK sz'Zone.encode_strings..cSs g|]\}}t|t|fqSrJ)r )rVpoprrJrJrKrXscSsg|] }t|qSrJ)r )rVrZrJrJrKrXscSsg|] }t|qSrJ)r )rVrIrJrJrKrXscSs0g|](\}}}}t|t|t|t|fqSrJ)r )rVZp1Zp2Zp3Zp4rJrJrKrXscSs g|]\}}t|t|fqSrJ)r )rVrYrZrJrJrKrXscSsg|] }t|qSrJ)r )rVrIrJrJrKrXscSsg|] }t|qSrJ)r )rVrWrJrJrKrXscSsg|] }t|qSrJ)r )rVrWrJrJrKrXscSsg|] }t|qSrJ)r )rVrWrJrJrKrXsN)r rrrr r!r"r)r#r%r*r&r'rPr()rSrJrJrKencode_stringss     zZone.encode_stringscsN|dkr8dd|D|_tt|j|dd|jDntt|j||dS)Nr(cSsg|]}tj|dqS))Zrule_str)rZ Rich_Rule)rVrWrJrJrKrXsz$Zone.__setattr__..cSsg|] }t|qSrJ)str)rVrWrJrJrKrXs)rPrMr __setattr__)rSr0r3)rTrJrKr]s zZone.__setattr__cstt|j}|d=|S)Nr)rMrexport_config_dict)rSZconf)rTrJrKr^szZone.export_config_dictcCsLt|||||dkr.|tkr*ttj|n|dkrxl|D]d}t|sTttj||jr}||j krq||jj |jkrttjdj ||qWqWdS)Nr r&z)interface '{}' already bound to zone '{}'r'zipset:z&source '{}' already bound to zone '{}')rrrrINVALID_TARGETrZINVALID_INTERFACErOZ get_zonesr0Zget_zoner&formatrrr startswith INVALID_ADDRr')rSritemZ all_configr7r5r9rJrJrK _check_configs6       zZone._check_configcstt|j||jdr,ttjd|n|jdrHttjd|n|jddkrhttjd|nnd|kr|d|j d}n|}t |t krttjd|t |t |j f|j r||j jkrttjddS)Nr/z'%s' can't start with '/'z'%s' can't end with '/'zmore than one '/' in '%s'z'Zone of '%s' has %d chars, max is %d %sz+Zones can't have the same name as a policy.)rMr check_namerarr INVALID_NAMEendswithcountfindlenr rQrOZget_policy_objectsZ NAME_CONFLICT)rSr0Z checked_name)rTrJrKrfs,      zZone.check_namec Csd|_d|_d|_d|_d|_x$|jD]}||jkr&|jj|q&Wx$|jD]}||jkrL|jj|qLWx$|jD]}||jkrr|jj|qrWx$|j D]}||j kr|j j|qWx$|j D]}||j kr|j j|qWx$|j D]}||j kr|j j|qW|j rd|_ |j rd|_ x(|jD]}||jkr&|jj|q&Wx(|jD]}||jkrP|jj|qPWx,|jD]"} |jj| |jjt| qzW|jrd|_dS)NTr)rQfilenamerrrr&appendr'r!r"r)r#r,r$r%r*rPr(r\r+) rSr5r7r9r6r1protoZicmpr,r8rJrJrKcombinesL                  z Zone.combine)rr)rr)rr)rF)r r)rr)r$F)rrrr)rr)r+F)r,F)__name__ __module__ __qualname____doc__rGZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRS staticmethodrLrNrUr[r]r^rdrfro __classcell__rJrJ)rTrKr(sx         c@s$eZdZddZddZddZdS)zone_ContentHandlercCs"tj||d|_d|_d|_dS)NF)rrN_rule _rule_errorZ _limit_ok)rSrcrJrJrKrN s zzone_ContentHandler.__init__c Cstj||||jrdS|jj||t|||r6dS|dkrd|krVtjd|dd|krj|d|j_d|krtjd|dd|kr|d}|t krt t j ||dkr|t kr||j_n|d kr|jjrtjd nd |j_n|d krh|jrtjd d |_dSd|kr.tjdd |_dS|d|jjkrT|jjj|dntjd|dn8|dkrf|jr |jjrtjdt|jd |_dSd}d|kr|djd$krd }d}}}d|kr|d}d|kr|d}d|kr|d}tj||||d|j_dSd|krBd|krBtjddSd|krdd|krdtjddSd|kr~tjd|dd|krtjddSd|krt|d rt|d rt|d rt t j|dd|kr$d|d}||jjkr|jjj|ntjd |dd|kr|d}||jjkrT|jjj|ntjd |dn:|d!kr|jjrtjd"nd |j_ntjd#|dSdS)%Nr5r0z'Ignoring deprecated attribute name='%s'rr=z,Ignoring deprecated attribute immutable='%s'r rr,zForward already set, ignoring.Tr7z$Invalid rule: interface use in rule.z Invalid interface: Name missing.z%Interface '%s' already set, ignoring.r9z:Invalid rule: More than one source in rule '%s', ignoring.FrAyestruer?r@rB)rAz$Invalid source: No address no ipset.z"Invalid source: Address and ipset.r>z)Ignoring deprecated attribute family='%s'z+Invalid source: Invertion not allowed here.zipset:%sz"Source '%s' already set, ignoring.zicmp-block-inversionz+Icmp-Block-Inversion already set, ignoring.zUnknown XML element '%s')ryrz)r startElementrxrcZparser_check_element_attrsrrZwarningrrrrr_r r r,rwr&rmr9r\lowerrZ Rich_Sourcerrr rbr'r+) rSr0attrsr rAZaddrr@rBentryrJrJrKr{&s                                       z zone_ContentHandler.startElementcCstj||t||dS)N)r endElementr)rSr0rJrJrKrs zzone_ContentHandler.endElementN)rprqrrrNr{rrJrJrJrKrvsprvFc Cst}|jds ttjd||dd |_|s>|j|j||_||_|j t j rZdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r} zttjd| jWYdd} ~ XnXWdQRX~~tr|j|S) Nz.xmlz'%s' is missing .xml suffixFTz%s/%srbznot a valid zone file: %s)rrhrrrgr0rfrlpathrar ETC_FIREWALLDZbuiltindefaultrvsaxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZ INVALID_ZONEZ getExceptionrr[) rlrZ no_check_namer5handlerparserr0fr9msgrJrJrKrs:        (c Cs\|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|ji}|jr|jd kr|j|d <|jtkr*|j|d <|jd ||jd t||x8t|jD]*} |jd|jdd| i|jd qVWx\t|jD]N} |jdd| kr|jdd| ddin|jdd| i|jd qW|jr |jd|jdi|jd |jr2|jd|jdi|jd |jd |jd |j |j!~dS)Nz%s/%sz %s/%s.xmlz%s.oldzBackup of file '%s' failed: %siZwtzUTF-8)modeencodingrrr r5 z r7r0zipset:r9rBr?zicmp-block-inversionr,)"rrlr0osexistsshutilZcopy2 ExceptionrerrordirnamerarrmkdiriorrZ startDocumentrr r r{ZignorableWhitespacerr r&Z simpleElementr'r+r,rZ endDocumentclose) r5r_pathr0rdirpathrrr}r7r9rJrJrKrs`                     )F)N)(__all__Zxml.saxrrrrZfirewallrZfirewall.functionsrrrr r r r Zfirewall.core.baser rZfirewall.core.io.io_objectrrrrZfirewall.core.io.policyrrrrZ firewall.corerZfirewall.core.loggerrrZfirewall.errorsrrrvrrrJrJrJrKs$   $    x| PK!Vq/#io/__pycache__/ifcfg.cpython-36.pycnu[3 Yj@s^dZdgZddlZddlZddlZddlZddlmZddl m Z m Z m Z Gddde ZdS)zifcfg file parserifcfgN)log)b2uu2bPY2c@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dS)rcCsi|_g|_||_|jdS)N)_config_deletedfilenameclear)selfr r /usr/lib/python3.6/ifcfg.py__init__#szifcfg.__init__cCsi|_g|_dS)N)rr)r r r r r )sz ifcfg.clearcCs|jjdS)N)rr )r r r r cleanup-sz ifcfg.cleanupcCs|jj|jS)N)rgetstrip)r keyr r r r0sz ifcfg.getcCs8t|j}t|j|j|<||jkr4|jj|dS)N)rrrrremove)r rvalueZ_keyr r r set3s  z ifcfg.setcCsHd}x2|jjD]$\}}|r$|d7}|d||f7}qWtrDt|S|S)N z%s=%s)ritemsrr)r srrr r r __str__9s z ifcfg.__str__cCsB|jyt|jd}Wn4tk rL}ztjd|j|WYdd}~XnXx|D]}|s^P|j}t|dksT|ddkrqTdd|jd dD}t|d krqTt|dd kr|dj d r|dj d r|ddd|d<|dd krqTn,|j j |ddk r tj d |j|jqT|d|j |d<qTW|jdS)NrzFailed to load '%s': %sr#;cSsg|] }|jqSr )r).0xr r r Qszifcfg.read..="rz%%s: Duplicate option definition: '%s')rr)r openr Exceptionrerrorrlensplit startswithendswithrrZwarningclose)r fmsglineZpairr r r readBs2   z ifcfg.readc :Cst|jdkrdSg}y.tjddtjj|jtjj|jdd}Wn2t k rv}zt j d|WYdd}~XnXd}d}yt j |jddd }WnNt k r}z0tjj|jrt j d |j|fnd}WYdd}~XndXx^|D]T}|sP|jd }t|dkr(|sD|jd d }q|d dkrPd}|j||jd q|jdd}t|dkr~d}|j|d q|d j} |dj} t| dkr| jdr| jdr| dd} | |kr@| |jkr|j| | krd}|jd| |j| fd }n$| |jkr"d }nd}|j|d |j| qd }qWt|jd krxF|jjD]8\} } | |krzqd|sd }|jd| | fd }qdW|r|j|j|stj|jdStjj|jr8ytj|jd|jWnBt k r6}z$tj|jtd|j|fWYdd}~XnXytj|j|jWnBt k r}z$tj|jtd|j|fWYdd}~XnXtj|jddS)NrZwtz%s.F)modeprefixdirdeletez!Failed to open temporary file: %sZrtzUTF-8)r2encodingzFailed to open '%s': %srTrrr"r#r$z%s=%s z%s.bakzBackup of '%s' failed: %szFailed to create '%s': %sir%)r)rtempfileZNamedTemporaryFileospathbasenamer dirnamer'rr(ior&existsrwriter*r+r,rappendrr-rnameshutilZcopy2IOErrorZmovechmod) r doneZ temp_filer/Zmodifiedemptyr.r0prrr r r r>_s               $ $z ifcfg.writeN) __name__ __module__ __qualname__rr rrrrr1r>r r r r r"s )__doc____all__Zos.pathr8r<r7rAZfirewall.core.loggerrZfirewall.functionsrrrobjectrr r r r s PK!I>..*io/__pycache__/direct.cpython-36.opt-1.pycnu[3 Yj=@sddljZddlZddlZddlZddlmZddlmZddl m Z m Z m Z ddl mZmZmZddlmZddlmZddlmZdd lmZdd lmZGd d d eZGd ddeZdS)N)config)LastUpdatedOrderedDict) splitArgsjoinArgs u2b_if_py2) IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator)log) ipXtables)ebtables)errors) FirewallErrorc@s$eZdZddZddZddZdS)direct_ContentHandlercCstj||d|_dS)NF)r__init__direct)selfitemr/usr/lib/python3.6/direct.pyr(s zdirect_ContentHandler.__init__c Cstj||||jj|||dkr@|jr6ttjdd|_n>|dkr|js\tj ddS|d}|d}|d}|jj t |t |t |n|dkr6|jstj d dS|d}|dkrttj d ||d}|d}yt |d}Wn(tk rtj d|ddSXt |t |t ||g|_nH|dkrl|jsVtj ddS|d}t |g|_ntj d|dSdS)NrzMore than one direct tag.Tchainz$Parse Error: chain outside of directipvtablerulez#Parse Error: rule outside of directipv4ipv6ebz"'%s' not from {'ipv4'|'ipv6'|'eb'}priorityz'Parse Error: %s is not a valid priority passthroughz&Parse Error: command outside of directzUnknown XML element %s)rrr)r startElementrZparser_check_element_attrsrrr Z PARSE_ERRORr error add_chainr INVALID_IPVint ValueError_rule _passthrough)rnameZattrsrrrrrrrr,sT          z"direct_ContentHandler.startElementcCstj|||dkrX|jrF|jjddt|jD|jj|jn tj dd|_nJ|dkr|jr|j jddt|jD|jj |j n tj d d|_ dS) NrcSsg|] }t|qSr)r).0xrrr dsz4direct_ContentHandler.endElement..z2Error: rule does not have any arguments, ignoring.rcSsg|] }t|qSr)r)r(r)rrrr*msz0Error: passthrough does not have any arguments, z ignoring.z9Error: passthrough does not have any arguments, ignoring.) r endElementZ_elementr%appendrradd_ruler r r&add_passthrough)rr'rrrr+^s     z direct_ContentHandler.endElementN)__name__ __module__ __qualname__rrr+rrrrr's2rcs<eZdZdZddBgfddddddgfgfdddgfgffZdZdd d d gd d d d gd gd ZiZfddZddZ ddZ ddZ ddZ ddZ ddZddZddZd d!Zd"d#Zd$d%Zd&d'Zd(d)Zd*d+Zd,d-Zd.d/Zd0d1Zd2d3Zd4d5Zd6d7Zd8d9Zd:d;Zdd?Z d@dAZ!Z"S)CDirectz Direct class chainsrulesr passthroughsz(a(sss)a(sssias)a(sas))Nrrrr)rrrrcs0tt|j||_t|_t|_t|_dS)N)superr2rfilenamerr3r5r6)rr8) __class__rrrs zDirect.__init__cCsdS)Nr)rconfrZall_confrrr _check_configszDirect._check_configc Csg}g}x>|jD]4}x.|j|D] }|jtt|t|gq WqW|j|g}xR|jD]H}xB|j|D]4}|jt|d|d|d|dt|dfqnWq^W|j|g}x8|jD].}x(|j|D]}|jt|t|fqWqW|j|t|S)Nr)r3r,tuplelistr5r6)rretr)keyrrrrr export_configs$ $     zDirect.export_configcCs|j|j|xt|jD]x\}\}}|dkrNx||D]}|j|qr rRrL)rrrrrrMrAvaluerrrr-s     zDirect.add_rulecCs|j|||||f}|t|f}||jkrb||j|krb|j||=t|j|dkr|j|=n$tddj|||fd||fdS)Nrz(Rule '%s' for table '%s' and chain '%s' z',z)with ipv '%s' and priority %d not in list)rQr>r5rTr$rL)rrrrrrMrArYrrr remove_rules     zDirect.remove_rulecCsb|j|||||f}||jkr^x"|j|jD]}|j||=q0Wt|j|dkr^|j|=dS)Nr)rQr5rPrT)rrrrrArYrrr remove_rules"s   zDirect.remove_rulescCs:|j|||||f}|t|f}||jko8||j|kS)N)rQr>r5)rrrrrrMrArYrrr query_rule+s   zDirect.query_rulecCsF|j|||||f}||jkr*|j|Std||fd|dS)Nz'No rules for table '%s' and chain '%s' z with ipv '%s')rQr5r$)rrrrrArrr get_rules1s     zDirect.get_rulescCs|jS)N)r5)rrrr get_all_rules:szDirect.get_all_rulescCs^|j|||jkrg|j|<||j|kr>|j|j|ntjddj||fddS)NzPassthrough '%s' for ipv '%s'z',zalready in list, ignoring)rOr6r,r rRrL)rrrMrrrr.?s   zDirect.add_passthroughcCsl|j|||jkrN||j|krN|j|j|t|j|dkrh|j|=ntddj||fddS)NrzPassthrough '%s' for ipv '%s'z',z not in list)rOr6rSrTr$rL)rrrMrrrremove_passthroughIs  zDirect.remove_passthroughcCs"|j|||jko ||j|kS)N)rOr6)rrrMrrrquery_passthroughSs zDirect.query_passthroughcCs.|j|||jkr|j|Std|dS)NzNo passthroughs for ipv '%s')rOr6r$)rrrrrget_passthroughsWs   zDirect.get_passthroughscCs|jS)N)r6)rrrrget_all_passthroughs^szDirect.get_all_passthroughscCs|j|jjds&ttjd|jt|}tj}|j |t |jdb}tj d}|j |y|j |Wn8tjk r}zttjd|jWYdd}~XnXWdQRXdS)Nz.xmlz'%s' is missing .xml suffixrbzNot a valid file: %s)rCr8endswithrr Z INVALID_NAMErsaxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZ INVALID_TYPEZ getException)rhandlerparserfsourcemsgrrrreadcs      z Direct.readc CsBtjj|jr\ytj|jd|jWn4tk rZ}ztd|j|fWYdd}~XnXtjjtj sxtj tj dt j |jddd}t |}|j|jdi|jdxR|jD]H}|\}}x:|j|D],}|jd |jd |||d |jdqWqWx|jD]}|\}}}xx|j|D]j\}} t| d kr@q&|jd |jd |||d|d|jtjjt| |jd |jdq&Wq Wx||jD]r}xj|j|D]\} t| d krȐq|jd |jdd|i|jtjjt| |jd|jdqWqW|jd|jd|j|j~dS)Nz%s.oldzBackup of '%s' failed: %siZwtzUTF-8)modeencodingr z r)rrrr<rz%d)rrrrrr)ospathexistsr8shutilZcopy2 ExceptionIOErrorrZ ETC_FIREWALLDmkdiriorfr Z startDocumentrZignorableWhitespacer3Z simpleElementr5rTreZsaxutilsescaperr+r6Z endDocumentclose) rrlrjrhrArrrrrMrrrwriteusZ$                z Direct.write)r4r4r4)#r/r0r1__doc__rEZDBUS_SIGNATUREZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSrr;rBrHrCrNrOrQr!rUrVrWrXr-rZr[r\r]r^r.r_r`rarbrmr{ __classcell__rr)r9rr2usH            r2)Zxml.saxrerqrxrtZfirewallrZfirewall.fw_typesrZfirewall.functionsrrrZfirewall.core.io.io_objectrrr Zfirewall.core.loggerr Z firewall.corer r r Zfirewall.errorsrrr2rrrrs        NPK!r&io/__pycache__/__init__.cpython-36.pycnu[3 Yj<@sjddlZdejkrfddlmZeedss      PK!r,io/__pycache__/__init__.cpython-36.opt-1.pycnu[3 Yj<@sjddlZdejkrfddlmZeedss      PK!9),),)io/__pycache__/ipset.cpython-36.opt-1.pycnu[3 YjU@sdZdddgZddljZddlZddlZddlZddlmZddl m Z m Z m Z m Z mZmZmZmZmZddlmZmZmZmZdd lmZmZdd lmZmZmZmZdd l m!Z!dd lm"Z"dd l#m$Z$GdddeZ%GdddeZ&ddZ'dddZ(dS)z$ipset io XML handler, reader, writerIPSet ipset_reader ipset_writerN)config) checkIPcheckIP6 checkIPnMask checkIP6nMask u2b_if_py2 check_mac check_portcheckInterface checkProtocol)PY2 IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator) IPSET_TYPESIPSET_CREATE_OPTIONS)check_icmp_namecheck_icmp_type_codecheck_icmpv6_namecheck_icmpv6_type_code)log)errors) FirewallErrorcseZdZddd d!dddifddgffZdZd d d d gZd d dgdgd dZdgdgdZfddZddZ ddZ e ddZ ddZ fddZZS)"rversionshort descriptiontypeoptionsentriesz (ssssa{ss}as)_-:.Nname)rripsetoptionentryvalue)r(r)cs<tt|jd|_d|_d|_d|_g|_i|_d|_ dS)NrF) superr__init__rrrr r"r!applied)self) __class__/usr/lib/python3.6/ipset.pyr-CszIPSet.__init__cCs8d|_d|_d|_d|_|jdd=|jjd|_dS)NrF)rrrr r"r!clearr.)r/r1r1r2cleanupMs  z IPSet.cleanupcCs\t|j|_t|j|_t|j|_t|j|_dd|jjD|_dd|jD|_dS)z HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.cSsi|]\}}t|t|qSr1)r ).0kvr1r1r2 ^sz(IPSet.encode_strings..cSsg|] }t|qSr1)r )r5er1r1r2 `sz(IPSet.encode_strings..N)r rrrr r!itemsr")r/r1r1r2encode_stringsVs    zIPSet.encode_stringscCsrd}d|kr|ddkrd}|jds6ttjd||ddjd}|jd}t|t|ksnt|d krttjd ||fxtt|D]}||}||}|d krd |ko|dkrh|d krttjd |||f|jd } t| dkrttjd||||fx| D]J} |dkr2t|  sH|dkrt |  rttjd| |||fqWnh|dkr|dkrttjd||||f|dkrt } nt} nt } | |sjttjd||||fq|dkr@d |kr|jd } t| dkrttjd||||f|dkr0t| d sJ|dkrft | d rfttjd| d|||f|dkrt | d  s|dkr>t | d  r>ttjd| d |||fn|j dr|dko|dko|dksttjd||||f|dkrt | s&|dkrjt | rjttjd||||fq|dkrvt | s`|dkrjttjd||fq|dkrld|krL|jd} t| dkrttjd|| ddkrP|dkrttjd||ft| d  rd| d krttjd| d |fn6| d jd\} } t| | sJttjd| d |fqj| dd2kr|dkr|ttjd||ft| d  rd| d krttjd"| d |fn6| d jd\} } t| | sJttjd"| d |fn^| dd3kr$t| d r$ttjd'| d|fn&t| d sjttjd(| d |fnt|sjttjd)||fq|d*kr |jd+ryt|d,}Wn*tk rttjd-||fYnXn8y t|}Wn*tk rttjd-||fYnX|dks |d.krjttjd-||fq|d/krZt| sDt|d0krjttjd1||fqttjd|qWdS)4NZipv4familyinet6Zipv6zhash:zipset type '%s' not usable,z)entry '%s' does not match ipset type '%s'Zipr$z invalid address '%s' in '%s'[%d]z.invalid address range '%s' in '%s' for %s (%s)z(invalid address '%s' in '%s' for %s (%s)z0.0.0.0rZnetz/0zhash:net,ifaceZmacz00:00:00:00:00:00z invalid mac address '%s' in '%s'Zportr%zinvalid port '%s'Zicmpz(invalid protocol for family '%s' in '%s'/zinvalid icmp type '%s' in '%s'icmpv6 ipv6-icmpz invalid icmpv6 type '%s' in '%s'tcpsctpudpudplitezinvalid protocol '%s' in '%s'zinvalid port '%s'in '%s'zinvalid port '%s' in '%s'ZmarkZ0xzinvalid mark '%s' in '%s'lZifacezinvalid interface '%s' in '%s')rDrE)rFrGrHrI) startswithrr INVALID_IPSETsplitlenZ INVALID_ENTRYrangerrrr endswithr rrrrrr int ValueErrorr )r*r!Z ipset_typer=flagsr;iflagitemZsplitsZ_splitZip_checkZ_type_codeZint_valr1r1r2 check_entrybsT                                  zIPSet.check_entrycCs|dkr |tkr ttjd||dkrx|jD]}|tkrNttjd||dkryt||}Wn,tk rttj d|||fYnX|d krttj d |||fq2|d kr2||dkr2ttj ||q2WdS)Nr z'%s' is not valid ipset typer!zipset invalid option '%s'timeouthashsizemaxelemz)Option '%s': Value '%s' is not an integerrz#Option '%s': Value '%s' is negativer=inetr>)rZr[r\)r]r>) rrr INVALID_TYPEkeysrrMrRrS INVALID_VALUEINVALID_FAMILY)r/rrWZ all_configkey int_valuer1r1r2 _check_config&s2   zIPSet._check_configcsrd|dkr6|dddkr6t|ddkr6ttjx&|dD]}tj||d|dq@Wtt|j|dS)NrZ0r?r)rOrrZIPSET_WITH_TIMEOUTrrYr, import_config)r/rr*)r0r1r2rhAs  zIPSet.import_config)rr)rr)rr)r r)__name__ __module__ __qualname__ZIMPORT_EXPORT_STRUCTUREZDBUS_SIGNATUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSr-r4r< staticmethodrYrdrh __classcell__r1r1)r0r2r,s,       Ec@seZdZddZddZdS)ipset_ContentHandlerc Cstj||||jj|||dkrpd|krX|dtkrLttjd|d|d|j_d|krl|d|j_ nz|dkr|nn|dkrnb|dkrd}d |kr|d }|d dkrttj d|d |jjdko|d dkrttj d|d |jjf|d dkr&| r&ttj d|d |d dkry t |}Wn.t k rnttj d|d |fYnX|dkrttj d|d |f|d d kr|dkrttj||d |jjkr||jj|d <ntjd|d dS)Nr(r z%srrrr)rr+r'r=rZr[r\zUnknown option '%s'zhash:macz%Unsupported option '%s' for type '%s'z&Missing mandatory value of option '%s'z)Option '%s': Value '%s' is not an integerrz#Option '%s': Value '%s' is negativer]r>z Option %s already set, ignoring.)r=rZr[r\)r=)r=rZr[r\)rZr[r\)r]r>)r startElementrWZparser_check_element_attrsrrrr^r rZINVALID_OPTIONrRrSr`rar!rwarning)r/r'attrsr+rcr1r1r2roLsd      z!ipset_ContentHandler.startElementcCs(tj|||dkr$|jjj|jdS)Nr*)r endElementrWr"appendZ_element)r/r'r1r1r2rrs zipset_ContentHandler.endElementN)rirjrkrorrr1r1r1r2rnKs7rnc %Cst}|jds ttjd||dd|_|j|j||_||_|j t j rVdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r}zttjd|jWYdd}~XnXWdQRX~~d |jkrF|jd d krFt|jd krFtjd |j|jdd=d } t} x| t|jkr|j| | krtjd |j| |jj| nry|j |j| |j|j!Wn<tk r} ztjd| |jj| WYdd} ~ XnX| j"|j| | d7} qRW~ t#r|j$|S)Nz.xmlz'%s' is missing .xml suffixreFTz%s/%srbznot a valid ipset file: %srZrfrz6ipset '%s': timeout option is set, entries are ignoredzEntry %s already set, ignoring.z %s, ignoring.rA)%rrQrrZ INVALID_NAMEr'Z check_namefilenamepathrLr ETC_FIREWALLDZbuiltindefaultrnsaxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionrMZ getExceptionr!rOr"rrpsetpoprYr addrr<) rvrwr(handlerparserr'fsourcemsgrUZ entries_setr9r1r1r2rs^        (  c Cs|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|jd |ji}|jr|jd kr|j|d <|jd ||jd |jrz|jd krz|jd|jdi|j|j|jd|jd |jr|jd kr|jd|jdi|j|j|jd|jd xZ|jjD]L\} } |jd| d kr|jd| | dn|jdd| i|jd qWxD|jD]:} |jd|jdi|j| |jd|jd q(W|jd |jd |j|j ~dS)Nz%s/%sz %s/%s.xmlz%s.oldzBackup of file '%s' failed: %siZwtzUTF-8)modeencodingr rrr( z rrr))r'r+r'r*)!rwrvr'osexistsshutilZcopy2 ExceptionrerrordirnamerLrrxmkdirior{rZ startDocumentr rroZignorableWhitespacerZ charactersrrrr!r;Z simpleElementr"Z endDocumentclose) r(rw_pathr'rdirpathrrrqrbr+r*r1r1r2rsf                           )N))__doc____all__Zxml.saxrzrrrZfirewallrZfirewall.functionsrrrr r r r r rZfirewall.core.io.io_objectrrrrZfirewall.core.ipsetrrZfirewall.core.icmprrrrZfirewall.core.loggerrrZfirewall.errorsrrrnrrr1r1r1r2s&   ,   !=5PK!I>..$io/__pycache__/direct.cpython-36.pycnu[3 Yj=@sddljZddlZddlZddlZddlmZddlmZddl m Z m Z m Z ddl mZmZmZddlmZddlmZddlmZdd lmZdd lmZGd d d eZGd ddeZdS)N)config)LastUpdatedOrderedDict) splitArgsjoinArgs u2b_if_py2) IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator)log) ipXtables)ebtables)errors) FirewallErrorc@s$eZdZddZddZddZdS)direct_ContentHandlercCstj||d|_dS)NF)r__init__direct)selfitemr/usr/lib/python3.6/direct.pyr(s zdirect_ContentHandler.__init__c Cstj||||jj|||dkr@|jr6ttjdd|_n>|dkr|js\tj ddS|d}|d}|d}|jj t |t |t |n|dkr6|jstj d dS|d}|dkrttj d ||d}|d}yt |d}Wn(tk rtj d|ddSXt |t |t ||g|_nH|dkrl|jsVtj ddS|d}t |g|_ntj d|dSdS)NrzMore than one direct tag.Tchainz$Parse Error: chain outside of directipvtablerulez#Parse Error: rule outside of directipv4ipv6ebz"'%s' not from {'ipv4'|'ipv6'|'eb'}priorityz'Parse Error: %s is not a valid priority passthroughz&Parse Error: command outside of directzUnknown XML element %s)rrr)r startElementrZparser_check_element_attrsrrr Z PARSE_ERRORr error add_chainr INVALID_IPVint ValueError_rule _passthrough)rnameZattrsrrrrrrrr,sT          z"direct_ContentHandler.startElementcCstj|||dkrX|jrF|jjddt|jD|jj|jn tj dd|_nJ|dkr|jr|j jddt|jD|jj |j n tj d d|_ dS) NrcSsg|] }t|qSr)r).0xrrr dsz4direct_ContentHandler.endElement..z2Error: rule does not have any arguments, ignoring.rcSsg|] }t|qSr)r)r(r)rrrr*msz0Error: passthrough does not have any arguments, z ignoring.z9Error: passthrough does not have any arguments, ignoring.) r endElementZ_elementr%appendrradd_ruler r r&add_passthrough)rr'rrrr+^s     z direct_ContentHandler.endElementN)__name__ __module__ __qualname__rrr+rrrrr's2rcs<eZdZdZddBgfddddddgfgfdddgfgffZdZdd d d gd d d d gd gd ZiZfddZddZ ddZ ddZ ddZ ddZ ddZddZddZd d!Zd"d#Zd$d%Zd&d'Zd(d)Zd*d+Zd,d-Zd.d/Zd0d1Zd2d3Zd4d5Zd6d7Zd8d9Zd:d;Zdd?Z d@dAZ!Z"S)CDirectz Direct class chainsrulesr passthroughsz(a(sss)a(sssias)a(sas))Nrrrr)rrrrcs0tt|j||_t|_t|_t|_dS)N)superr2rfilenamerr3r5r6)rr8) __class__rrrs zDirect.__init__cCsdS)Nr)rconfrZall_confrrr _check_configszDirect._check_configc Csg}g}x>|jD]4}x.|j|D] }|jtt|t|gq WqW|j|g}xR|jD]H}xB|j|D]4}|jt|d|d|d|dt|dfqnWq^W|j|g}x8|jD].}x(|j|D]}|jt|t|fqWqW|j|t|S)Nr)r3r,tuplelistr5r6)rretr)keyrrrrr export_configs$ $     zDirect.export_configcCs|j|j|xt|jD]x\}\}}|dkrNx||D]}|j|qr rRrL)rrrrrrMrAvaluerrrr-s     zDirect.add_rulecCs|j|||||f}|t|f}||jkrb||j|krb|j||=t|j|dkr|j|=n$tddj|||fd||fdS)Nrz(Rule '%s' for table '%s' and chain '%s' z',z)with ipv '%s' and priority %d not in list)rQr>r5rTr$rL)rrrrrrMrArYrrr remove_rules     zDirect.remove_rulecCsb|j|||||f}||jkr^x"|j|jD]}|j||=q0Wt|j|dkr^|j|=dS)Nr)rQr5rPrT)rrrrrArYrrr remove_rules"s   zDirect.remove_rulescCs:|j|||||f}|t|f}||jko8||j|kS)N)rQr>r5)rrrrrrMrArYrrr query_rule+s   zDirect.query_rulecCsF|j|||||f}||jkr*|j|Std||fd|dS)Nz'No rules for table '%s' and chain '%s' z with ipv '%s')rQr5r$)rrrrrArrr get_rules1s     zDirect.get_rulescCs|jS)N)r5)rrrr get_all_rules:szDirect.get_all_rulescCs^|j|||jkrg|j|<||j|kr>|j|j|ntjddj||fddS)NzPassthrough '%s' for ipv '%s'z',zalready in list, ignoring)rOr6r,r rRrL)rrrMrrrr.?s   zDirect.add_passthroughcCsl|j|||jkrN||j|krN|j|j|t|j|dkrh|j|=ntddj||fddS)NrzPassthrough '%s' for ipv '%s'z',z not in list)rOr6rSrTr$rL)rrrMrrrremove_passthroughIs  zDirect.remove_passthroughcCs"|j|||jko ||j|kS)N)rOr6)rrrMrrrquery_passthroughSs zDirect.query_passthroughcCs.|j|||jkr|j|Std|dS)NzNo passthroughs for ipv '%s')rOr6r$)rrrrrget_passthroughsWs   zDirect.get_passthroughscCs|jS)N)r6)rrrrget_all_passthroughs^szDirect.get_all_passthroughscCs|j|jjds&ttjd|jt|}tj}|j |t |jdb}tj d}|j |y|j |Wn8tjk r}zttjd|jWYdd}~XnXWdQRXdS)Nz.xmlz'%s' is missing .xml suffixrbzNot a valid file: %s)rCr8endswithrr Z INVALID_NAMErsaxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZ INVALID_TYPEZ getException)rhandlerparserfsourcemsgrrrreadcs      z Direct.readc CsBtjj|jr\ytj|jd|jWn4tk rZ}ztd|j|fWYdd}~XnXtjjtj sxtj tj dt j |jddd}t |}|j|jdi|jdxR|jD]H}|\}}x:|j|D],}|jd |jd |||d |jdqWqWx|jD]}|\}}}xx|j|D]j\}} t| d kr@q&|jd |jd |||d|d|jtjjt| |jd |jdq&Wq Wx||jD]r}xj|j|D]\} t| d krȐq|jd |jdd|i|jtjjt| |jd|jdqWqW|jd|jd|j|j~dS)Nz%s.oldzBackup of '%s' failed: %siZwtzUTF-8)modeencodingr z r)rrrr<rz%d)rrrrrr)ospathexistsr8shutilZcopy2 ExceptionIOErrorrZ ETC_FIREWALLDmkdiriorfr Z startDocumentrZignorableWhitespacer3Z simpleElementr5rTreZsaxutilsescaperr+r6Z endDocumentclose) rrlrjrhrArrrrrMrrrwriteusZ$                z Direct.write)r4r4r4)#r/r0r1__doc__rEZDBUS_SIGNATUREZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSrr;rBrHrCrNrOrQr!rUrVrWrXr-rZr[r\r]r^r.r_r`rarbrmr{ __classcell__rr)r9rr2usH            r2)Zxml.saxrerqrxrtZfirewallrZfirewall.fw_typesrZfirewall.functionsrrrZfirewall.core.io.io_objectrrr Zfirewall.core.loggerr Z firewall.corer r r Zfirewall.errorsrrr2rrrrs        NPK!FmRR*io/__pycache__/policy.cpython-36.opt-1.pycnu[3 YjϢ@s dddgZddljZddlZddlZddlZddlmZddlm Z m Z ddlm Z m Z m Z ddlmZmZmZdd lmZmZmZmZmZmZdd lmZdd lmZdd lmZdd lmZddZ ddZ!ddZ"ddZ#ddZ$GdddeZ%GdddeZ&dddZ'dddZ(dS) Policy policy_reader policy_writerN)config)checkIPcheckIP6)uniqifymax_policy_name_lenportStr)DEFAULT_POLICY_TARGETPOLICY_TARGETSDEFAULT_POLICY_PRIORITY) IO_ObjectIO_Object_ContentHandlerIO_Object_XMLGenerator check_port check_tcpudpcheck_protocol)rich)log)errors) FirewallErrorc Cs|dkr n|dkrn|dkr|jr`|jjrJtjdt|jd|_dStj|d|j_dS|d|jj kr|jj j |dntjd|dn|dkrN|jr|jjrtjdt|jd|_dStj |d|d |j_dSt |dt |d t|dd |d f}||jjkr4|jjj |ntjd |d|d nN|d kr|jr|jjrtjdt|jd|_dStj|d |j_nBt|d |d |jjkr|jjj |d ntjd |d n|dkrh|jr.|jjrtjdt|jd|_dStj|d|j_dS|d|jjkrT|jjj |dntjd|dn4|dkr|jr|jjrtjdt|jd|_dStj|d|j_dStjd|dn|dkr2|jr|jjrtjdt|jd|_dStj|j_n|jjr&tjdnd|j_nj|dkrd}d|krR|d}d}d|krh|d}|jr|jjrtjdt|jd|_dStj|d|d |||j_dSt |dt |d |rt ||r t| r t| r ttjd|t|dd |d t|d t|f}||jjkrL|jjj |n6tjd|d|d |rld|nd|r|d|ndn|dkr@|jr|jjrtjdt|jd|_dStj|d|d |j_dSt |dt |d t|dd |d f}||jj kr&|jj j |ntjd|d|d n\|dkr|jsftjdd|_dS|jj!rtjd t|jdSd!}d}d"|kr|d"}d}d#|kr|d#}d$|kr|d$j"dLkrd}tj#||||j_!n|dMkr|jstjd+d|_dS|jj$r0tjd,d|_dS|d'krHtj%|j_$nh|d(krxd} d-|krh|d-} tj&| |j_$n8|d)krtj'|j_$n |d*kr|d.} tj(| |j_$|jj$|_)n|d/kr^|jstjd0dS|jjrtjd1dSd} d2|kr*|d2} | dNkr*tjd;d|_dSd<|kr<|d<nd} tj*| | |j_|jj|_)n>|d=kr|js~tjd>dS|jj+rtjd?t|jd|_dStj,|j_+|jj+|_)n|d@kr,d} dA}dB|kr|dB} | dOkrtjdE|dBd|_dSdF|krt-|dF}tj.| |dG|_np|dHkr|j)sRtjdId|_dS|j)j/rxtjdJt|jd|_dS|d }tj0||j1dK|j)_/nd!SdS)PNshort descriptionservicez;Invalid rule: More than one element in rule '%s', ignoring.Tnamez#Service '%s' already set, ignoring.portprotocol-z#Port '%s/%s' already set, ignoring.valuez$Protocol '%s' already set, ignoring.z icmp-blockz&icmp-block '%s' already set, ignoring.z icmp-typez-Invalid rule: icmp-block '%s' outside of rule masqueradez!Masquerade already set, ignoring.z forward-portzto-portzto-addrz#to-addr '%s' is not a valid addressz-Forward port %s/%s%s%s already set, ignoring.z >%sz @%sz source-portz*Source port '%s/%s' already set, ignoring. destinationz)Invalid rule: Destination outside of rulez?Invalid rule: More than one destination in rule '%s', ignoring.Faddressipsetinvertyestrueacceptrejectdropmarkz$Invalid rule: Action outside of rulez"Invalid rule: More than one actiontypesetrz!Invalid rule: Log outside of rulezInvalid rule: More than one loglevelemergalertcriterrorwarningnoticeinfodebugzInvalid rule: Invalid log levelprefixauditz#Invalid rule: Audit outside of rulez9Invalid rule: More than one audit in rule '%s', ignoring.rulerfamilyipv4ipv6z&Invalid rule: Rule family "%s" invalidpriority)r:r=limitz4Invalid rule: Limit outside of action, log and auditz9Invalid rule: More than one limit in rule '%s', ignoring.burst)r&r')r(r)r*r+)r/r0r1r2r3r4r5r6)r;r<)2_ruleelementrr3str _rule_errorr Rich_Serviceitemservicesappend Rich_Portrrr ports Rich_Protocolr protocolsRich_IcmpBlock icmp_blocks Rich_IcmpTypeRich_Masquerader Rich_ForwardPortrrrr INVALID_ADDR forward_portsRich_SourcePort source_portsr"lowerZRich_Destinationaction Rich_Accept Rich_Reject Rich_Drop Rich_Mark _limit_okZRich_Logr8Z Rich_Auditint Rich_Ruler>Z Rich_Limitget)objrattrsentryto_portZto_addrr%r#r$Z_typeZ_setr.r7r:r=rrc/usr/lib/python3.6/policy.pycommon_startElements                                                                            recCs|dkr|jsy|jjWn6tk rR}ztjd|t|jWYdd}~XnLXt|j|jjkr|jj j |j|jjj t|jntjdt|jd|_d|_n|d krd|_ dS) Nr9z%s: %sz Rule '%s' already set, ignoring.Fr(r)r*r+rr8)r(r)r*r+rr8) rCr@Zcheck Exceptionrr3rBrE rules_strrulesrGr[)r_rercrcrdcommon_endElements& rjcCst|trdnd}|dkrT|jrT|jj}x$|D]}||kr0ttjd|q0Wn|dkrx$|D]}t|dt|dqbWnb|dkrx|D] }t |qWn@|d kr|jr|jj } x$|D]} | | krttj d | qWn|d krx|D]} t| dt| d| d  r>| d  r>ttj d| | d rTt| d | d rt | d  rt| d  rttjd| d qWnT|dkrx&|D]}t|dt|dqWn|dkrx|D]} tj| d} |jr| jrt| jtjst| jtjr|jj } | jj| krLttj d | jjnH| jr|jj| jj}|jr| j|jkrttj d| j| jjfnL|jrt| jtjr|jj}| jj|krttjdj||j| jjqWdS)NrZZonerFz '%s' not among existing servicesrIrrKrMz"'%s' not among existing icmp typesrRz$'%s' is missing to-port AND to-addr z#to-addr '%s' is not a valid addressrTrg rich_rules)rule_strz3rich rule family '%s' conflicts with icmp type '%s'z){} '{}': '{}' not among existing services)rgrn) isinstancer fw_configZ get_servicesrrZINVALID_SERVICErrrZ get_icmptypesZINVALID_ICMPTYPEINVALID_FORWARDrrrQrr]rArLrNrr:Z get_icmptyper"rDformat)r_rrE all_configZobj_typeZexisting_servicesrrprotoZexisting_icmptypesZicmptypefwd_portr9Zobj_richZictrcrcrdcommon_check_config2s                      rwcCs0d|ji}|j}|dk r ||d<|jd|dS)Nrr?r>)rr? simpleElement)handlerr>dr?rcrcrd_handler_add_rich_limitxs  r{c Cs|jrF|jdkrF|jd|jdi|j|j|jd|jd|jr|jdkr|jd|jdi|j|j|jd|jdx6t|jD](}|jd|jdd|i|jdqWx@t|j D]2}|jd|jd|d |d d |jdqWx8t|j D]*}|jd|jd d |i|jdqWx8t|j D]*}|jd|jdd|i|jdqLW|j r|jd|jdi|jdxt|j D]}|jd|d |d d }|dr|ddkr|d|d<|dr|ddkr|d|d<|jd||jdqWxBt|jD]4}|jd|jd|d |d d |jdq>WxT|jD]H}i}|jr|j|d<|jd krt|j|d<|jd|jd||jd|jrVi}|jjr|jj|d<|jjr|jj|d<|jjr$|jj|d<|jjr6d|d<|jd|jd||jd|jri}|jjrx|jj|d<|jjr|jj|d<|jjrd|d<|jd|jd ||jd|jrxd} i}t|jtjkrd} |jj|d<nbt|jtjkr(d} |jj|d<|jj |d <n0t|jtj!krNd } |jj"|d <n t|jtj#krfd} nt|jtj$krd} |jj|d<nt|jtj%krd!} |jj|d<nt|jtj&krd} |jj|d<|jj |d <|jj'dkr|jj'|d<|jj(dkrX|jj(|d<nFt|jtj)krBd} |jj|d<|jj |d <nt*t+j,d"t|j|jd|j| ||jd|j-ri}|j-j.r|j-j.|d#<|j-j/r|j-j/|d$<|j-j0r|jd|jd%||jd&t1||j-j0|jd'|jd%n|jd|jd%||jd|j2ri}|j2j0rx|jd|jd(i|jd&t1||j2j0|jd'|jd(n|jd|jd(||jd|j3rd} i}t|j3tj4krd)} n|t|j3tj5krd*} |j3jr<|j3j|d+<nNt|j3tj6krd,} n6t|j3tj7kr*d-} |j3j8|d.<nt-j9d/t|j3|j3j0r|jd|j| ||jd&t1||j3j0|jd'|j| n|jd|j| ||jd|jd|jd|jdqWdS)0Nr!z r rrrrrrk)rrrrz icmp-blockr rlzto-portrmzto-addrz forward-portz source-portr:r=r9r#macr$Truer%z sourcer"z icmp-typez"Unknown element '%s' in obj_writerr7r.rz z r8r(r)r,r*r+r-zUnknown action '%s'):rignorableWhitespace startElementZ characters endElementrrrFrxrIrKrMr rRrTrhr:r=rBraddrr}r$r%r"rAr,rrDrrHrrrJrrOrLrNrPrb to_addressrSrrZINVALID_OBJECTrr7r.r>r{r8rVrWrXrYrZr-r3) r_ryrrrZicmpZforwardr`r9rArVrcrcrd common_writers\                                                                                        rcsPeZdZd7ZdZeZdgZd8d9d:d;d dgfd dgfddgfddgfdd?gfd@ddgfddgffZdddgZ dddgdgddgdgdgdddgddddgddgddddddgdgdgdgdZ ddgdd gd!dgd"d#d$d!d%gd"d$d%gd&d'gd(gd)gd*Z fd+d,Z d-d.Z fd/d0Zfd1d2Zd3d4Zfd5d6ZZS)Ariirversionr!rrtargetrFrIrMr FrRrnrKrTr= ingress_zones egress_zones_r/Nrrrrr-)rrpolicyrrz icmp-blockz icmp-typer z forward-portr9rr"rz source-portrr8r(r)r*r+r>z ingress-zonez egress-zonezto-portzto-addrr:r#r}r%r$r7r.r,r?)rz forward-portr9rr"rr)r>cstt|jd|_d|_d|_t|_g|_g|_ g|_ g|_ d|_ g|_ g|_d|_g|_g|_d|_|j|_d|_g|_g|_dS)Nr!F)superr__init__rrrr rrFrIrKrMr rRrTrqrhrgappliedpriority_defaultr=Zderived_from_zonerr)self) __class__rcrdrs(zPolicy.__init__cCsd|_d|_d|_t|_|jdd=|jdd=|jdd=|jdd=d|_ |j dd=|j dd=d|_ |j dd=|jdd=d|_|j|_|jdd=|jdd=dS)Nr!F)rrrr rrFrIrKrMr rRrTrqrhrgrrr=rr)rrcrcrdcleanups$         zPolicy.cleanupcs"|dkr|jSttt||SdS)Nrn)rggetattrrr)rr)rrcrd __getattr__szPolicy.__getattr__csB|dkr,dd|D|_dd|jD|_ntt|j||dS)NrncSsg|]}tj|dqS))ro)rr]).0srcrcrd sz&Policy.__setattr__..cSsg|] }t|qSrc)rB)rrrcrcrdrs)rhrgrr __setattr__)rrr)rrcrdrszPolicy.__setattr__c Cst|||||dkr2|tkr.ttjd|n|dkrz||jksX||jksX||jkrvttjd||j|j|jfn|dkrhddg}|j r||j j 7}x|D]}||krttj d ||dkrt ddgt |@s|dkrt |t |grttj d ||dkr|dkr8d|kr8d|dksT|dkrd|krd|dkrttj d qWn|d kr|rd|krd|dkrttj d nxd|krd|dkrttj dxR|dD]F}|dkrސq|j j |}|j rd|j j|krttj dqWn|dkr4x|D]}tj|d}|jrt|jtjrd|kr|d|dkr|ttj d nxd|kr,d|dkrttj dxR|dD]F}|dkrq|j j |}|j rd|j j|krttj dqWq,|jrt|jtjrd|kr,d|dkr@|jjrttjdnt|dr,|jjs`ttjdd|dkr,x|dD]8}|j j |}|j rxd|j j|krxttj dqxWnv|jr,t|jtjr,d|kr,xR|dD]F}|dkrq|j j |}|j rd|j j|krttj dqWq,Wn|dkrx|D]} d|krnd|dkrnttj dnd|krDd|dkr| drttjdnt|drD| dsttjdd|dkrDxD|dD]8}|j j |}|j rd|j j|krttj dqWqDWdS)Nrz'%s' is invalid targetr=zQ%d is invalid priority. Must be in range [%d, %d]. The following are reserved: %srrANYHOSTz'%s' not among existing zonesz>'%s' may only contain one of: many regular zones, ANY, or HOSTzF'HOST' can only appear in either ingress or egress zones, but not bothr z.'masquerade' is invalid for egress zone 'HOST'z/'masquerade' is invalid for ingress zone 'HOST'Z interfaceszR'masquerade' cannot be used in a policy if an ingress zone has assigned interfacesrn)rozAA 'forward-port' with 'to-addr' is invalid for egress zone 'HOST'zC'forward-port' requires 'to-addr' if egress zone is 'ANY' or a zonezS'forward-port' cannot be used in a policy if an egress zone has assigned interfaceszR'mark' action cannot be used in a policy if an egress zone has assigned interfacesrRz1'forward-port' is invalid for ingress zone 'HOST'rm)rr)rr)rr)rr)rwr rrINVALID_TARGETpriority_reserved priority_max priority_minZINVALID_PRIORITYrq get_zonesZ INVALID_ZONEr-Zget_zoneZget_zone_config_dictrr]rArprOrPrrrrVrZ) rrrErtZexisting_zoneszoneZz_objr9r_rvrcrcrd _check_configs       "                           zPolicy._check_configcstt|j||jdr,ttjd|n|jdrHttjd|n|jddkrhttjd|njd|kr|d|j d}n|}t |t krttjd|t |t f|j r||j j krttjddS)Nrz'%s' can't start with '/'z'%s' can't end with '/'rkzmore than one '/' in '%s'z&Policy of '%s' has %d chars, max is %dz,Policies can't have the same name as a zone.)rr check_name startswithrr INVALID_NAMEendswithcountfindlenr rqrZ NAME_CONFLICT)rrZ checked_name)rrcrdr,s*      zPolicy.check_namei)rr!)rr!)rr!)rr!)r!r!)r F)r!r!r!r!)r!r!)r=r)__name__ __module__ __qualname__rrr rrZIMPORT_EXPORT_STRUCTUREZADDITIONAL_ALNUM_CHARSZPARSER_REQUIRED_ELEMENT_ATTRSZPARSER_OPTIONAL_ELEMENT_ATTRSrrrrrr __classcell__rcrc)rrdrZsr        ^c@s$eZdZddZddZddZdS)policy_ContentHandlercCs"tj||d|_d|_d|_dS)NF)rrr@rCr[)rrErcrcrdrHs zpolicy_ContentHandler.__init__cCstj||||jrdS|jj||t|||r6dS|dkrd|krR|d|j_d|krjt|d|j_d|kr|d}|t krt t j ||r||j_ n^|dkr|d|jjkr|jjj|dntjd|dn|dkr |d|jjkr|jjj|dntjd |dn|d kr|jsFtjd d |_dS|jjrltjd t|jd |_dSd}d|kr|djdkrd }d}}}d|kr|d}d|kr|d}d|kr|d}tj||||d|j_dStjd|dSdS)Nrrr=rz ingress-zonerz(Ingress zone '%s' already set, ignoring.z egress-zonez'Egress zone '%s' already set, ignoring.rz$Invalid rule: Source outside of ruleTz:Invalid rule: More than one source in rule '%s', ignoring.Fr%r&r'r#r}r$)r%zUnknown XML element '%s')r&r')rrrCrEZparser_check_element_attrsrerr\r=r rrrrrrGrr3rr@rrBrUrZ Rich_Source)rrr`rr%rr}r$rcrcrdrNsf                 z"policy_ContentHandler.startElementcCstj||t||dS)N)rrrj)rrrcrcrdrs z policy_ContentHandler.endElementN)rrrrrrrcrcrcrdrGs@rFc Cst}|jds ttjd||dd |_|s>|j|j||_||_|j t j rZdnd|_ |j |_ t|}tj}|j|d||f}t|db}tjd}|j|y|j|Wn8tjk r} zttjd| jWYdd} ~ XnXWdQRX~~|S) Nz.xmlz'%s' is missing .xml suffixFTz%s/%srbznot a valid policy file: %s)rrrrrrrfilenamepathrr ETC_FIREWALLDZbuiltindefaultrsaxZ make_parserZsetContentHandleropenZ InputSourceZ setByteStreamparseZSAXParseExceptionZINVALID_POLICYZ getException) rrZ no_check_namerryparserrfrmsgrcrcrdrs6        (c Cs|r|n|j}|jr$d||jf}nd||jf}tjj|rytj|d|Wn0tk r}ztj d||WYdd}~XnXtjj |}|j t j rtjj| rtjjt j stjt j dtj|dtj|ddd}t|}|ji}|jr|jd kr|j|d <|j|jkr0t|j|d <|j|d <|jd ||jdt||x8t|jD]*} |jd|jdd| i|jdqfWx8t|jD]*} |jd|jdd| i|jdqW|jd |jd|j |j!~dS)Nz%s/%sz %s/%s.xmlz%s.oldzBackup of file '%s' failed: %siZwtzUTF-8)modeencodingr!rr=rrr|z z ingress-zonerz egress-zone)"rrrosexistsshutilZcopy2rfrr2dirnamerrrmkdiriorrZ startDocumentrr=rrBrrrrrrrxrrZ endDocumentclose) rr_pathrrdirpathrryr`rrcrcrdrsN             )F)N))__all__Zxml.saxrrrrZfirewallrZfirewall.functionsrrrr r Zfirewall.core.baser r r Zfirewall.core.io.io_objectrrrrrrZ firewall.corerZfirewall.core.loggerrrZfirewall.errorsrrerjrwr{rrrrrrcrcrcrds4        F[nL PK!oUU io/ipset.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2015-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """ipset io XML handler, reader, writer""" __all__ = [ "IPSet", "ipset_reader", "ipset_writer" ] import xml.sax as sax import os import io import shutil from firewall import config from firewall.functions import checkIP, checkIP6, checkIPnMask, \ checkIP6nMask, u2b_if_py2, check_mac, check_port, checkInterface, \ checkProtocol from firewall.core.io.io_object import PY2, IO_Object, \ IO_Object_ContentHandler, IO_Object_XMLGenerator from firewall.core.ipset import IPSET_TYPES, IPSET_CREATE_OPTIONS from firewall.core.icmp import check_icmp_name, check_icmp_type_code, \ check_icmpv6_name, check_icmpv6_type_code from firewall.core.logger import log from firewall import errors from firewall.errors import FirewallError class IPSet(IO_Object): IMPORT_EXPORT_STRUCTURE = ( ( "version", "" ), # s ( "short", "" ), # s ( "description", "" ), # s ( "type", "" ), # s ( "options", { "": "", }, ), # a{ss} ( "entries", [ "" ], ), # as ) DBUS_SIGNATURE = '(ssssa{ss}as)' ADDITIONAL_ALNUM_CHARS = [ "_", "-", ":", "." ] PARSER_REQUIRED_ELEMENT_ATTRS = { "short": None, "description": None, "ipset": [ "type" ], "option": [ "name" ], "entry": None, } PARSER_OPTIONAL_ELEMENT_ATTRS = { "ipset": [ "version" ], "option": [ "value" ], } def __init__(self): super(IPSet, self).__init__() self.version = "" self.short = "" self.description = "" self.type = "" self.entries = [ ] self.options = { } self.applied = False def cleanup(self): self.version = "" self.short = "" self.description = "" self.type = "" del self.entries[:] self.options.clear() self.applied = False def encode_strings(self): """ HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.""" self.version = u2b_if_py2(self.version) self.short = u2b_if_py2(self.short) self.description = u2b_if_py2(self.description) self.type = u2b_if_py2(self.type) self.options = { u2b_if_py2(k):u2b_if_py2(v) for k, v in self.options.items() } self.entries = [ u2b_if_py2(e) for e in self.entries ] @staticmethod def check_entry(entry, options, ipset_type): family = "ipv4" if "family" in options: if options["family"] == "inet6": family = "ipv6" if not ipset_type.startswith("hash:"): raise FirewallError(errors.INVALID_IPSET, "ipset type '%s' not usable" % ipset_type) flags = ipset_type[5:].split(",") items = entry.split(",") if len(flags) != len(items) or len(flags) < 1: raise FirewallError( errors.INVALID_ENTRY, "entry '%s' does not match ipset type '%s'" % \ (entry, ipset_type)) for i in range(len(flags)): flag = flags[i] item = items[i] if flag == "ip": if "-" in item and family == "ipv4": # IP ranges only with plain IPs, no masks if i > 1: raise FirewallError( errors.INVALID_ENTRY, "invalid address '%s' in '%s'[%d]" % \ (item, entry, i)) splits = item.split("-") if len(splits) != 2: raise FirewallError( errors.INVALID_ENTRY, "invalid address range '%s' in '%s' for %s (%s)" % \ (item, entry, ipset_type, family)) for _split in splits: if (family == "ipv4" and not checkIP(_split)) or \ (family == "ipv6" and not checkIP6(_split)): raise FirewallError( errors.INVALID_ENTRY, "invalid address '%s' in '%s' for %s (%s)" % \ (_split, entry, ipset_type, family)) else: # IPs with mask only allowed in the first # position of the type if family == "ipv4": if item == "0.0.0.0": raise FirewallError( errors.INVALID_ENTRY, "invalid address '%s' in '%s' for %s (%s)" % \ (item, entry, ipset_type, family)) if i == 0: ip_check = checkIPnMask else: ip_check = checkIP else: ip_check = checkIP6 if not ip_check(item): raise FirewallError( errors.INVALID_ENTRY, "invalid address '%s' in '%s' for %s (%s)" % \ (item, entry, ipset_type, family)) elif flag == "net": if "-" in item: # IP ranges only with plain IPs, no masks splits = item.split("-") if len(splits) != 2: raise FirewallError( errors.INVALID_ENTRY, "invalid address range '%s' in '%s' for %s (%s)" % \ (item, entry, ipset_type, family)) # First part can only be a plain IP if (family == "ipv4" and not checkIP(splits[0])) or \ (family == "ipv6" and not checkIP6(splits[0])): raise FirewallError( errors.INVALID_ENTRY, "invalid address '%s' in '%s' for %s (%s)" % \ (splits[0], entry, ipset_type, family)) # Second part can also have a mask if (family == "ipv4" and not checkIPnMask(splits[1])) or \ (family == "ipv6" and not checkIP6nMask(splits[1])): raise FirewallError( errors.INVALID_ENTRY, "invalid address '%s' in '%s' for %s (%s)" % \ (splits[1], entry, ipset_type, family)) else: # IPs with mask allowed in all positions, but no /0 if item.endswith("/0"): if not (family == "ipv6" and i == 0 and ipset_type == "hash:net,iface"): raise FirewallError( errors.INVALID_ENTRY, "invalid address '%s' in '%s' for %s (%s)" % \ (item, entry, ipset_type, family)) if (family == "ipv4" and not checkIPnMask(item)) or \ (family == "ipv6" and not checkIP6nMask(item)): raise FirewallError( errors.INVALID_ENTRY, "invalid address '%s' in '%s' for %s (%s)" % \ (item, entry, ipset_type, family)) elif flag == "mac": # ipset does not allow to add 00:00:00:00:00:00 if not check_mac(item) or item == "00:00:00:00:00:00": raise FirewallError( errors.INVALID_ENTRY, "invalid mac address '%s' in '%s'" % (item, entry)) elif flag == "port": if ":" in item: splits = item.split(":") if len(splits) != 2: raise FirewallError( errors.INVALID_ENTRY, "invalid port '%s'" % (item)) if splits[0] == "icmp": if family != "ipv4": raise FirewallError( errors.INVALID_ENTRY, "invalid protocol for family '%s' in '%s'" % \ (family, entry)) if not check_icmp_name(splits[1]) and "/" not in splits[1]: raise FirewallError( errors.INVALID_ENTRY, "invalid icmp type '%s' in '%s'" % \ (splits[1], entry)) else: (_type, _code) = splits[1].split("/") if not check_icmp_type_code(_type, _code): raise FirewallError( errors.INVALID_ENTRY, "invalid icmp type '%s' in '%s'" % (splits[1], entry), ) elif splits[0] in [ "icmpv6", "ipv6-icmp" ]: if family != "ipv6": raise FirewallError( errors.INVALID_ENTRY, "invalid protocol for family '%s' in '%s'" % \ (family, entry)) if not check_icmpv6_name(splits[1]) and "/" not in splits[1]: raise FirewallError( errors.INVALID_ENTRY, "invalid icmpv6 type '%s' in '%s'" % \ (splits[1], entry)) else: (_type, _code) = splits[1].split("/") if not check_icmpv6_type_code(_type, _code): raise FirewallError( errors.INVALID_ENTRY, "invalid icmpv6 type '%s' in '%s'" % (splits[1], entry), ) elif splits[0] not in [ "tcp", "sctp", "udp", "udplite" ] \ and not checkProtocol(splits[0]): raise FirewallError( errors.INVALID_ENTRY, "invalid protocol '%s' in '%s'" % (splits[0], entry)) elif not check_port(splits[1]): raise FirewallError( errors.INVALID_ENTRY, "invalid port '%s'in '%s'" % (splits[1], entry)) else: if not check_port(item): raise FirewallError( errors.INVALID_ENTRY, "invalid port '%s' in '%s'" % (item, entry)) elif flag == "mark": if item.startswith("0x"): try: int_val = int(item, 16) except ValueError: raise FirewallError( errors.INVALID_ENTRY, "invalid mark '%s' in '%s'" % (item, entry)) else: try: int_val = int(item) except ValueError: raise FirewallError( errors.INVALID_ENTRY, "invalid mark '%s' in '%s'" % (item, entry)) if int_val < 0 or int_val > 4294967295: raise FirewallError( errors.INVALID_ENTRY, "invalid mark '%s' in '%s'" % (item, entry)) elif flag == "iface": if not checkInterface(item) or len(item) > 15: raise FirewallError( errors.INVALID_ENTRY, "invalid interface '%s' in '%s'" % (item, entry)) else: raise FirewallError(errors.INVALID_IPSET, "ipset type '%s' not usable" % ipset_type) def _check_config(self, config, item, all_config): if item == "type": if config not in IPSET_TYPES: raise FirewallError(errors.INVALID_TYPE, "'%s' is not valid ipset type" % config) if item == "options": for key in config.keys(): if key not in IPSET_CREATE_OPTIONS: raise FirewallError(errors.INVALID_IPSET, "ipset invalid option '%s'" % key) if key in [ "timeout", "hashsize", "maxelem" ]: try: int_value = int(config[key]) except ValueError: raise FirewallError( errors.INVALID_VALUE, "Option '%s': Value '%s' is not an integer" % \ (key, config[key])) if int_value < 0: raise FirewallError( errors.INVALID_VALUE, "Option '%s': Value '%s' is negative" % \ (key, config[key])) elif key == "family" and \ config[key] not in [ "inet", "inet6" ]: raise FirewallError(errors.INVALID_FAMILY, config[key]) def import_config(self, config): if "timeout" in config[4] and config[4]["timeout"] != "0": if len(config[5]) != 0: raise FirewallError(errors.IPSET_WITH_TIMEOUT) for entry in config[5]: IPSet.check_entry(entry, config[4], config[3]) super(IPSet, self).import_config(config) # PARSER class ipset_ContentHandler(IO_Object_ContentHandler): def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) self.item.parser_check_element_attrs(name, attrs) if name == "ipset": if "type" in attrs: if attrs["type"] not in IPSET_TYPES: raise FirewallError(errors.INVALID_TYPE, "%s" % attrs["type"]) self.item.type = attrs["type"] if "version" in attrs: self.item.version = attrs["version"] elif name == "short": pass elif name == "description": pass elif name == "option": value = "" if "value" in attrs: value = attrs["value"] if attrs["name"] not in \ [ "family", "timeout", "hashsize", "maxelem" ]: raise FirewallError( errors.INVALID_OPTION, "Unknown option '%s'" % attrs["name"]) if self.item.type == "hash:mac" and attrs["name"] in [ "family" ]: raise FirewallError( errors.INVALID_OPTION, "Unsupported option '%s' for type '%s'" % \ (attrs["name"], self.item.type)) if attrs["name"] in [ "family", "timeout", "hashsize", "maxelem" ] \ and not value: raise FirewallError( errors.INVALID_OPTION, "Missing mandatory value of option '%s'" % attrs["name"]) if attrs["name"] in [ "timeout", "hashsize", "maxelem" ]: try: int_value = int(value) except ValueError: raise FirewallError( errors.INVALID_VALUE, "Option '%s': Value '%s' is not an integer" % \ (attrs["name"], value)) if int_value < 0: raise FirewallError( errors.INVALID_VALUE, "Option '%s': Value '%s' is negative" % \ (attrs["name"], value)) if attrs["name"] == "family" and value not in [ "inet", "inet6" ]: raise FirewallError(errors.INVALID_FAMILY, value) if attrs["name"] not in self.item.options: self.item.options[attrs["name"]] = value else: log.warning("Option %s already set, ignoring.", attrs["name"]) # nothing to do for entry and entries here def endElement(self, name): IO_Object_ContentHandler.endElement(self, name) if name == "entry": self.item.entries.append(self._element) def ipset_reader(filename, path): ipset = IPSet() if not filename.endswith(".xml"): raise FirewallError(errors.INVALID_NAME, "'%s' is missing .xml suffix" % filename) ipset.name = filename[:-4] ipset.check_name(ipset.name) ipset.filename = filename ipset.path = path ipset.builtin = False if path.startswith(config.ETC_FIREWALLD) else True ipset.default = ipset.builtin handler = ipset_ContentHandler(ipset) parser = sax.make_parser() parser.setContentHandler(handler) name = "%s/%s" % (path, filename) with open(name, "rb") as f: source = sax.InputSource(None) source.setByteStream(f) try: parser.parse(source) except sax.SAXParseException as msg: raise FirewallError(errors.INVALID_IPSET, "not a valid ipset file: %s" % \ msg.getException()) del handler del parser if "timeout" in ipset.options and ipset.options["timeout"] != "0" and \ len(ipset.entries) > 0: # no entries visible for ipsets with timeout log.warning("ipset '%s': timeout option is set, entries are ignored", ipset.name) del ipset.entries[:] i = 0 entries_set = set() while i < len(ipset.entries): if ipset.entries[i] in entries_set: log.warning("Entry %s already set, ignoring.", ipset.entries[i]) ipset.entries.pop(i) else: try: ipset.check_entry(ipset.entries[i], ipset.options, ipset.type) except FirewallError as e: log.warning("%s, ignoring.", e) ipset.entries.pop(i) else: entries_set.add(ipset.entries[i]) i += 1 del entries_set if PY2: ipset.encode_strings() return ipset def ipset_writer(ipset, path=None): _path = path if path else ipset.path if ipset.filename: name = "%s/%s" % (_path, ipset.filename) else: name = "%s/%s.xml" % (_path, ipset.name) if os.path.exists(name): try: shutil.copy2(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) dirpath = os.path.dirname(name) if dirpath.startswith(config.ETC_FIREWALLD) and not os.path.exists(dirpath): if not os.path.exists(config.ETC_FIREWALLD): os.mkdir(config.ETC_FIREWALLD, 0o750) os.mkdir(dirpath, 0o750) f = io.open(name, mode='wt', encoding='UTF-8') handler = IO_Object_XMLGenerator(f) handler.startDocument() # start ipset element attrs = { "type": ipset.type } if ipset.version and ipset.version != "": attrs["version"] = ipset.version handler.startElement("ipset", attrs) handler.ignorableWhitespace("\n") # short if ipset.short and ipset.short != "": handler.ignorableWhitespace(" ") handler.startElement("short", { }) handler.characters(ipset.short) handler.endElement("short") handler.ignorableWhitespace("\n") # description if ipset.description and ipset.description != "": handler.ignorableWhitespace(" ") handler.startElement("description", { }) handler.characters(ipset.description) handler.endElement("description") handler.ignorableWhitespace("\n") # options for key,value in ipset.options.items(): handler.ignorableWhitespace(" ") if value != "": handler.simpleElement("option", { "name": key, "value": value }) else: handler.simpleElement("option", { "name": key }) handler.ignorableWhitespace("\n") # entries for entry in ipset.entries: handler.ignorableWhitespace(" ") handler.startElement("entry", { }) handler.characters(entry) handler.endElement("entry") handler.ignorableWhitespace("\n") # end ipset element handler.endElement('ipset') handler.ignorableWhitespace("\n") handler.endDocument() f.close() del handler PK! ϢϢ io/policy.pynu[# -*- coding: utf-8 -*- # # SPDX-License-Identifier: GPL-2.0-or-later __all__ = [ "Policy", "policy_reader", "policy_writer" ] import xml.sax as sax import os import io import shutil from firewall import config from firewall.functions import checkIP, checkIP6 from firewall.functions import uniqify, max_policy_name_len, portStr from firewall.core.base import DEFAULT_POLICY_TARGET, POLICY_TARGETS, DEFAULT_POLICY_PRIORITY from firewall.core.io.io_object import IO_Object, \ IO_Object_ContentHandler, IO_Object_XMLGenerator, check_port, \ check_tcpudp, check_protocol from firewall.core import rich from firewall.core.logger import log from firewall import errors from firewall.errors import FirewallError def common_startElement(obj, name, attrs): if name == "short": pass elif name == "description": pass elif name == "service": if obj._rule: if obj._rule.element: log.warning("Invalid rule: More than one element in rule '%s', ignoring.", str(obj._rule)) obj._rule_error = True return True obj._rule.element = rich.Rich_Service(attrs["name"]) return True if attrs["name"] not in obj.item.services: obj.item.services.append(attrs["name"]) else: log.warning("Service '%s' already set, ignoring.", attrs["name"]) elif name == "port": if obj._rule: if obj._rule.element: log.warning("Invalid rule: More than one element in rule '%s', ignoring.", str(obj._rule)) obj._rule_error = True return True obj._rule.element = rich.Rich_Port(attrs["port"], attrs["protocol"]) return True check_port(attrs["port"]) check_tcpudp(attrs["protocol"]) entry = (portStr(attrs["port"], "-"), attrs["protocol"]) if entry not in obj.item.ports: obj.item.ports.append(entry) else: log.warning("Port '%s/%s' already set, ignoring.", attrs["port"], attrs["protocol"]) elif name == "protocol": if obj._rule: if obj._rule.element: log.warning("Invalid rule: More than one element in rule '%s', ignoring.", str(obj._rule)) obj._rule_error = True return True obj._rule.element = rich.Rich_Protocol(attrs["value"]) else: check_protocol(attrs["value"]) if attrs["value"] not in obj.item.protocols: obj.item.protocols.append(attrs["value"]) else: log.warning("Protocol '%s' already set, ignoring.", attrs["value"]) elif name == "icmp-block": if obj._rule: if obj._rule.element: log.warning("Invalid rule: More than one element in rule '%s', ignoring.", str(obj._rule)) obj._rule_error = True return True obj._rule.element = rich.Rich_IcmpBlock(attrs["name"]) return True if attrs["name"] not in obj.item.icmp_blocks: obj.item.icmp_blocks.append(attrs["name"]) else: log.warning("icmp-block '%s' already set, ignoring.", attrs["name"]) elif name == "icmp-type": if obj._rule: if obj._rule.element: log.warning("Invalid rule: More than one element in rule '%s', ignoring.", str(obj._rule)) obj._rule_error = True return True obj._rule.element = rich.Rich_IcmpType(attrs["name"]) return True else: log.warning("Invalid rule: icmp-block '%s' outside of rule", attrs["name"]) elif name == "masquerade": if obj._rule: if obj._rule.element: log.warning("Invalid rule: More than one element in rule '%s', ignoring.", str(obj._rule)) obj._rule_error = True return True obj._rule.element = rich.Rich_Masquerade() else: if obj.item.masquerade: log.warning("Masquerade already set, ignoring.") else: obj.item.masquerade = True elif name == "forward-port": to_port = "" if "to-port" in attrs: to_port = attrs["to-port"] to_addr = "" if "to-addr" in attrs: to_addr = attrs["to-addr"] if obj._rule: if obj._rule.element: log.warning("Invalid rule: More than one element in rule '%s', ignoring.", str(obj._rule)) obj._rule_error = True return True obj._rule.element = rich.Rich_ForwardPort(attrs["port"], attrs["protocol"], to_port, to_addr) return True check_port(attrs["port"]) check_tcpudp(attrs["protocol"]) if to_port: check_port(to_port) if to_addr: if not checkIP(to_addr) and not checkIP6(to_addr): raise FirewallError(errors.INVALID_ADDR, "to-addr '%s' is not a valid address" \ % to_addr) entry = (portStr(attrs["port"], "-"), attrs["protocol"], portStr(to_port, "-"), str(to_addr)) if entry not in obj.item.forward_ports: obj.item.forward_ports.append(entry) else: log.warning("Forward port %s/%s%s%s already set, ignoring.", attrs["port"], attrs["protocol"], " >%s" % to_port if to_port else "", " @%s" % to_addr if to_addr else "") elif name == "source-port": if obj._rule: if obj._rule.element: log.warning("Invalid rule: More than one element in rule '%s', ignoring.", str(obj._rule)) obj._rule_error = True return True obj._rule.element = rich.Rich_SourcePort(attrs["port"], attrs["protocol"]) return True check_port(attrs["port"]) check_tcpudp(attrs["protocol"]) entry = (portStr(attrs["port"], "-"), attrs["protocol"]) if entry not in obj.item.source_ports: obj.item.source_ports.append(entry) else: log.warning("Source port '%s/%s' already set, ignoring.", attrs["port"], attrs["protocol"]) elif name == "destination": if not obj._rule: log.warning('Invalid rule: Destination outside of rule') obj._rule_error = True return True if obj._rule.destination: log.warning("Invalid rule: More than one destination in rule '%s', ignoring.", str(obj._rule)) return True invert = False address = None if "address" in attrs: address = attrs["address"] ipset = None if "ipset" in attrs: ipset = attrs["ipset"] if "invert" in attrs and \ attrs["invert"].lower() in [ "yes", "true" ]: invert = True obj._rule.destination = rich.Rich_Destination(address, ipset, invert) elif name in [ "accept", "reject", "drop", "mark" ]: if not obj._rule: log.warning('Invalid rule: Action outside of rule') obj._rule_error = True return True if obj._rule.action: log.warning('Invalid rule: More than one action') obj._rule_error = True return True if name == "accept": obj._rule.action = rich.Rich_Accept() elif name == "reject": _type = None if "type" in attrs: _type = attrs["type"] obj._rule.action = rich.Rich_Reject(_type) elif name == "drop": obj._rule.action = rich.Rich_Drop() elif name == "mark": _set = attrs["set"] obj._rule.action = rich.Rich_Mark(_set) obj._limit_ok = obj._rule.action elif name == "log": if not obj._rule: log.warning('Invalid rule: Log outside of rule') return True if obj._rule.log: log.warning('Invalid rule: More than one log') return True level = None if "level" in attrs: level = attrs["level"] if level not in [ "emerg", "alert", "crit", "error", "warning", "notice", "info", "debug" ]: log.warning('Invalid rule: Invalid log level') obj._rule_error = True return True prefix = attrs["prefix"] if "prefix" in attrs else None obj._rule.log = rich.Rich_Log(prefix, level) obj._limit_ok = obj._rule.log elif name == "audit": if not obj._rule: log.warning('Invalid rule: Audit outside of rule') return True if obj._rule.audit: log.warning("Invalid rule: More than one audit in rule '%s', ignoring.", str(obj._rule)) obj._rule_error = True return True obj._rule.audit = rich.Rich_Audit() obj._limit_ok = obj._rule.audit elif name == "rule": family = None priority = 0 if "family" in attrs: family = attrs["family"] if family not in [ "ipv4", "ipv6" ]: log.warning('Invalid rule: Rule family "%s" invalid', attrs["family"]) obj._rule_error = True return True if "priority" in attrs: priority = int(attrs["priority"]) obj._rule = rich.Rich_Rule(family=family, priority=priority) elif name == "limit": if not obj._limit_ok: log.warning('Invalid rule: Limit outside of action, log and audit') obj._rule_error = True return True if obj._limit_ok.limit: log.warning("Invalid rule: More than one limit in rule '%s', ignoring.", str(obj._rule)) obj._rule_error = True return True value = attrs["value"] obj._limit_ok.limit = rich.Rich_Limit(value, attrs.get("burst")) else: return False return True def common_endElement(obj, name): if name == "rule": if not obj._rule_error: try: obj._rule.check() except Exception as e: log.warning("%s: %s", e, str(obj._rule)) else: if str(obj._rule) not in obj.item.rules_str: obj.item.rules.append(obj._rule) obj.item.rules_str.append(str(obj._rule)) else: log.warning("Rule '%s' already set, ignoring.", str(obj._rule)) obj._rule = None obj._rule_error = False elif name in [ "accept", "reject", "drop", "mark", "log", "audit" ]: obj._limit_ok = None def common_check_config(obj, config, item, all_config): obj_type = "Policy" if isinstance(obj, Policy) else "Zone" if item == "services" and obj.fw_config: existing_services = obj.fw_config.get_services() for service in config: if service not in existing_services: raise FirewallError(errors.INVALID_SERVICE, "'%s' not among existing services" % \ service) elif item == "ports": for port in config: check_port(port[0]) check_tcpudp(port[1]) elif item == "protocols": for proto in config: check_protocol(proto) elif item == "icmp_blocks" and obj.fw_config: existing_icmptypes = obj.fw_config.get_icmptypes() for icmptype in config: if icmptype not in existing_icmptypes: raise FirewallError(errors.INVALID_ICMPTYPE, "'%s' not among existing icmp types" % \ icmptype) elif item == "forward_ports": for fwd_port in config: check_port(fwd_port[0]) check_tcpudp(fwd_port[1]) if not fwd_port[2] and not fwd_port[3]: raise FirewallError( errors.INVALID_FORWARD, "'%s' is missing to-port AND to-addr " % fwd_port) if fwd_port[2]: check_port(fwd_port[2]) if fwd_port[3]: if not checkIP(fwd_port[3]) and not checkIP6(fwd_port[3]): raise FirewallError( errors.INVALID_ADDR, "to-addr '%s' is not a valid address" % fwd_port[3]) elif item == "source_ports": for port in config: check_port(port[0]) check_tcpudp(port[1]) elif item in ["rules_str", "rich_rules"]: for rule in config: obj_rich = rich.Rich_Rule(rule_str=rule) if obj.fw_config and obj_rich.element and (isinstance(obj_rich.element, rich.Rich_IcmpBlock) or isinstance(obj_rich.element, rich.Rich_IcmpType)): existing_icmptypes = obj.fw_config.get_icmptypes() if obj_rich.element.name not in existing_icmptypes: raise FirewallError(errors.INVALID_ICMPTYPE, "'%s' not among existing icmp types" % \ obj_rich.element.name) elif obj_rich.family: ict = obj.fw_config.get_icmptype(obj_rich.element.name) if ict.destination and obj_rich.family not in ict.destination: raise FirewallError(errors.INVALID_ICMPTYPE, "rich rule family '%s' conflicts with icmp type '%s'" % \ (obj_rich.family, obj_rich.element.name)) elif obj.fw_config and isinstance(obj_rich.element, rich.Rich_Service): existing_services = obj.fw_config.get_services() if obj_rich.element.name not in existing_services: raise FirewallError( errors.INVALID_SERVICE, "{} '{}': '{}' not among existing services".format( obj_type, obj.name, obj_rich.element.name ), ) def _handler_add_rich_limit(handler, limit): d = {"value": limit.value} burst = limit.burst if burst is not None: d["burst"] = burst handler.simpleElement("limit", d) def common_writer(obj, handler): # short if obj.short and obj.short != "": handler.ignorableWhitespace(" ") handler.startElement("short", { }) handler.characters(obj.short) handler.endElement("short") handler.ignorableWhitespace("\n") # description if obj.description and obj.description != "": handler.ignorableWhitespace(" ") handler.startElement("description", { }) handler.characters(obj.description) handler.endElement("description") handler.ignorableWhitespace("\n") # services for service in uniqify(obj.services): handler.ignorableWhitespace(" ") handler.simpleElement("service", { "name": service }) handler.ignorableWhitespace("\n") # ports for port in uniqify(obj.ports): handler.ignorableWhitespace(" ") handler.simpleElement("port", { "port": port[0], "protocol": port[1] }) handler.ignorableWhitespace("\n") # protocols for protocol in uniqify(obj.protocols): handler.ignorableWhitespace(" ") handler.simpleElement("protocol", { "value": protocol }) handler.ignorableWhitespace("\n") # icmp-blocks for icmp in uniqify(obj.icmp_blocks): handler.ignorableWhitespace(" ") handler.simpleElement("icmp-block", { "name": icmp }) handler.ignorableWhitespace("\n") # masquerade if obj.masquerade: handler.ignorableWhitespace(" ") handler.simpleElement("masquerade", { }) handler.ignorableWhitespace("\n") # forward-ports for forward in uniqify(obj.forward_ports): handler.ignorableWhitespace(" ") attrs = { "port": forward[0], "protocol": forward[1] } if forward[2] and forward[2] != "" : attrs["to-port"] = forward[2] if forward[3] and forward[3] != "" : attrs["to-addr"] = forward[3] handler.simpleElement("forward-port", attrs) handler.ignorableWhitespace("\n") # source-ports for port in uniqify(obj.source_ports): handler.ignorableWhitespace(" ") handler.simpleElement("source-port", { "port": port[0], "protocol": port[1] }) handler.ignorableWhitespace("\n") # rules for rule in obj.rules: attrs = { } if rule.family: attrs["family"] = rule.family if rule.priority != 0: attrs["priority"] = str(rule.priority) handler.ignorableWhitespace(" ") handler.startElement("rule", attrs) handler.ignorableWhitespace("\n") # source if rule.source: attrs = { } if rule.source.addr: attrs["address"] = rule.source.addr if rule.source.mac: attrs["mac"] = rule.source.mac if rule.source.ipset: attrs["ipset"] = rule.source.ipset if rule.source.invert: attrs["invert"] = "True" handler.ignorableWhitespace(" ") handler.simpleElement("source", attrs) handler.ignorableWhitespace("\n") # destination if rule.destination: attrs = { } if rule.destination.addr: attrs["address"] = rule.destination.addr if rule.destination.ipset: attrs["ipset"] = rule.destination.ipset if rule.destination.invert: attrs["invert"] = "True" handler.ignorableWhitespace(" ") handler.simpleElement("destination", attrs) handler.ignorableWhitespace("\n") # element if rule.element: element = "" attrs = { } if type(rule.element) == rich.Rich_Service: element = "service" attrs["name"] = rule.element.name elif type(rule.element) == rich.Rich_Port: element = "port" attrs["port"] = rule.element.port attrs["protocol"] = rule.element.protocol elif type(rule.element) == rich.Rich_Protocol: element = "protocol" attrs["value"] = rule.element.value elif type(rule.element) == rich.Rich_Masquerade: element = "masquerade" elif type(rule.element) == rich.Rich_IcmpBlock: element = "icmp-block" attrs["name"] = rule.element.name elif type(rule.element) == rich.Rich_IcmpType: element = "icmp-type" attrs["name"] = rule.element.name elif type(rule.element) == rich.Rich_ForwardPort: element = "forward-port" attrs["port"] = rule.element.port attrs["protocol"] = rule.element.protocol if rule.element.to_port != "": attrs["to-port"] = rule.element.to_port if rule.element.to_address != "": attrs["to-addr"] = rule.element.to_address elif type(rule.element) == rich.Rich_SourcePort: element = "source-port" attrs["port"] = rule.element.port attrs["protocol"] = rule.element.protocol else: raise FirewallError( errors.INVALID_OBJECT, "Unknown element '%s' in obj_writer" % type(rule.element)) handler.ignorableWhitespace(" ") handler.simpleElement(element, attrs) handler.ignorableWhitespace("\n") # rule.element # log if rule.log: attrs = { } if rule.log.prefix: attrs["prefix"] = rule.log.prefix if rule.log.level: attrs["level"] = rule.log.level if rule.log.limit: handler.ignorableWhitespace(" ") handler.startElement("log", attrs) handler.ignorableWhitespace("\n ") _handler_add_rich_limit(handler, rule.log.limit) handler.ignorableWhitespace("\n ") handler.endElement("log") else: handler.ignorableWhitespace(" ") handler.simpleElement("log", attrs) handler.ignorableWhitespace("\n") # audit if rule.audit: attrs = {} if rule.audit.limit: handler.ignorableWhitespace(" ") handler.startElement("audit", { }) handler.ignorableWhitespace("\n ") _handler_add_rich_limit(handler, rule.audit.limit) handler.ignorableWhitespace("\n ") handler.endElement("audit") else: handler.ignorableWhitespace(" ") handler.simpleElement("audit", attrs) handler.ignorableWhitespace("\n") # action if rule.action: action = "" attrs = { } if type(rule.action) == rich.Rich_Accept: action = "accept" elif type(rule.action) == rich.Rich_Reject: action = "reject" if rule.action.type: attrs["type"] = rule.action.type elif type(rule.action) == rich.Rich_Drop: action = "drop" elif type(rule.action) == rich.Rich_Mark: action = "mark" attrs["set"] = rule.action.set else: log.warning("Unknown action '%s'", type(rule.action)) if rule.action.limit: handler.ignorableWhitespace(" ") handler.startElement(action, attrs) handler.ignorableWhitespace("\n ") _handler_add_rich_limit(handler, rule.action.limit) handler.ignorableWhitespace("\n ") handler.endElement(action) else: handler.ignorableWhitespace(" ") handler.simpleElement(action, attrs) handler.ignorableWhitespace("\n") handler.ignorableWhitespace(" ") handler.endElement("rule") handler.ignorableWhitespace("\n") class Policy(IO_Object): priority_min = -32768 priority_max = 32767 priority_default = DEFAULT_POLICY_PRIORITY priority_reserved = [0] IMPORT_EXPORT_STRUCTURE = ( ( "version", "" ), # s ( "short", "" ), # s ( "description", "" ), # s ( "target", "" ), # s ( "services", [ "", ], ), # as ( "ports", [ ( "", "" ), ], ), # a(ss) ( "icmp_blocks", [ "", ], ), # as ( "masquerade", False ), # b ( "forward_ports", [ ( "", "", "", "" ), ], ), # a(ssss) ( "rich_rules", [ "" ] ), # as ( "protocols", [ "", ], ), # as ( "source_ports", [ ( "", "" ), ], ), # a(ss) ( "priority", 0 ), # i ( "ingress_zones", [ "" ] ), # as ( "egress_zones", [ "" ] ), # as ) ADDITIONAL_ALNUM_CHARS = [ "_", "-", "/" ] PARSER_REQUIRED_ELEMENT_ATTRS = { "short": None, "description": None, "policy": ["target"], "service": [ "name" ], "port": [ "port", "protocol" ], "icmp-block": [ "name" ], "icmp-type": [ "name" ], "masquerade": None, "forward-port": [ "port", "protocol" ], "rule": None, "source": None, "destination": None, "protocol": [ "value" ], "source-port": [ "port", "protocol" ], "log": None, "audit": None, "accept": None, "reject": None, "drop": None, "mark": [ "set" ], "limit": [ "value" ], "ingress-zone": [ "name" ], "egress-zone": [ "name" ], } PARSER_OPTIONAL_ELEMENT_ATTRS = { "policy": [ "version", "priority" ], "forward-port": [ "to-port", "to-addr" ], "rule": [ "family", "priority" ], "source": [ "address", "mac", "invert", "family", "ipset" ], "destination": [ "address", "invert", "ipset" ], "log": [ "prefix", "level" ], "reject": [ "type" ], "limit": ["burst"], } def __init__(self): super(Policy, self).__init__() self.version = "" self.short = "" self.description = "" self.target = DEFAULT_POLICY_TARGET self.services = [ ] self.ports = [ ] self.protocols = [ ] self.icmp_blocks = [ ] self.masquerade = False self.forward_ports = [ ] self.source_ports = [ ] self.fw_config = None # to be able to check services and a icmp_blocks self.rules = [ ] self.rules_str = [ ] self.applied = False self.priority = self.priority_default self.derived_from_zone = None self.ingress_zones = [] self.egress_zones = [] def cleanup(self): self.version = "" self.short = "" self.description = "" self.target = DEFAULT_POLICY_TARGET del self.services[:] del self.ports[:] del self.protocols[:] del self.icmp_blocks[:] self.masquerade = False del self.forward_ports[:] del self.source_ports[:] self.fw_config = None # to be able to check services and a icmp_blocks del self.rules[:] del self.rules_str[:] self.applied = False self.priority = self.priority_default del self.ingress_zones[:] del self.egress_zones[:] def __getattr__(self, name): if name == "rich_rules": return self.rules_str else: return getattr(super(Policy, self), name) def __setattr__(self, name, value): if name == "rich_rules": self.rules = [rich.Rich_Rule(rule_str=s) for s in value] # must convert back to string to get the canonical string. self.rules_str = [str(s) for s in self.rules] else: super(Policy, self).__setattr__(name, value) def _check_config(self, config, item, all_config): common_check_config(self, config, item, all_config) if item == "target": if config not in POLICY_TARGETS: raise FirewallError(errors.INVALID_TARGET, "'%s' is invalid target" % (config)) elif item == "priority": if config in self.priority_reserved or \ config > self.priority_max or \ config < self.priority_min: raise FirewallError(errors.INVALID_PRIORITY, "%d is invalid priority. Must be in range [%d, %d]. The following are reserved: %s" % (config, self.priority_min, self.priority_max, self.priority_reserved)) elif item in ["ingress_zones", "egress_zones"]: existing_zones = ["ANY", "HOST"] if self.fw_config: existing_zones += self.fw_config.get_zones() for zone in config: if zone not in existing_zones: raise FirewallError(errors.INVALID_ZONE, "'%s' not among existing zones" % (zone)) if ((zone not in ["ANY", "HOST"] and (set(["ANY", "HOST"]) & set(config))) or \ (zone in ["ANY", "HOST"] and (set(config) - set([zone])))): raise FirewallError(errors.INVALID_ZONE, "'%s' may only contain one of: many regular zones, ANY, or HOST" % (item)) if zone == "HOST" and \ ((item == "ingress_zones" and "egress_zones" in all_config and "HOST" in all_config["egress_zones"]) or \ (item == "egress_zones" and "ingress_zones" in all_config and "HOST" in all_config["ingress_zones"])): raise FirewallError(errors.INVALID_ZONE, "'HOST' can only appear in either ingress or egress zones, but not both") elif item == "masquerade" and config: if "egress_zones" in all_config and "HOST" in all_config["egress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'masquerade' is invalid for egress zone 'HOST'") elif "ingress_zones" in all_config: if "HOST" in all_config["ingress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'masquerade' is invalid for ingress zone 'HOST'") for zone in all_config["ingress_zones"]: if zone == "ANY": continue z_obj = self.fw_config.get_zone(zone) if self.fw_config and "interfaces" in self.fw_config.get_zone_config_dict(z_obj): raise FirewallError(errors.INVALID_ZONE, "'masquerade' cannot be used in a policy if an ingress zone has assigned interfaces") elif item == "rich_rules": for rule in config: obj = rich.Rich_Rule(rule_str=rule) if obj.element and isinstance(obj.element, rich.Rich_Masquerade): if "egress_zones" in all_config and "HOST" in all_config["egress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'masquerade' is invalid for egress zone 'HOST'") elif "ingress_zones" in all_config: if "HOST" in all_config["ingress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'masquerade' is invalid for ingress zone 'HOST'") for zone in all_config["ingress_zones"]: if zone == "ANY": continue z_obj = self.fw_config.get_zone(zone) if self.fw_config and "interfaces" in self.fw_config.get_zone_config_dict(z_obj): raise FirewallError(errors.INVALID_ZONE, "'masquerade' cannot be used in a policy if an ingress zone has assigned interfaces") elif obj.element and isinstance(obj.element, rich.Rich_ForwardPort): if "egress_zones" in all_config: if "HOST" in all_config["egress_zones"]: if obj.element.to_address: raise FirewallError(errors.INVALID_FORWARD, "A 'forward-port' with 'to-addr' is invalid for egress zone 'HOST'") elif all_config["egress_zones"]: if not obj.element.to_address: raise FirewallError(errors.INVALID_FORWARD, "'forward-port' requires 'to-addr' if egress zone is 'ANY' or a zone") if "ANY" not in all_config["egress_zones"]: for zone in all_config["egress_zones"]: z_obj = self.fw_config.get_zone(zone) if self.fw_config and "interfaces" in self.fw_config.get_zone_config_dict(z_obj): raise FirewallError(errors.INVALID_ZONE, "'forward-port' cannot be used in a policy if an egress zone has assigned interfaces") elif obj.action and isinstance(obj.action, rich.Rich_Mark): if "egress_zones" in all_config: for zone in all_config["egress_zones"]: if zone in ["ANY", "HOST"]: continue z_obj = self.fw_config.get_zone(zone) if self.fw_config and "interfaces" in self.fw_config.get_zone_config_dict(z_obj): raise FirewallError(errors.INVALID_ZONE, "'mark' action cannot be used in a policy if an egress zone has assigned interfaces") elif item == "forward_ports": for fwd_port in config: if "ingress_zones" in all_config and "HOST" in all_config["ingress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'forward-port' is invalid for ingress zone 'HOST'") elif "egress_zones" in all_config: if "HOST" in all_config["egress_zones"]: if fwd_port[3]: raise FirewallError(errors.INVALID_FORWARD, "A 'forward-port' with 'to-addr' is invalid for egress zone 'HOST'") elif all_config["egress_zones"]: if not fwd_port[3]: raise FirewallError(errors.INVALID_FORWARD, "'forward-port' requires 'to-addr' if egress zone is 'ANY' or a zone") if "ANY" not in all_config["egress_zones"]: for zone in all_config["egress_zones"]: z_obj = self.fw_config.get_zone(zone) if self.fw_config and "interfaces" in self.fw_config.get_zone_config_dict(z_obj): raise FirewallError(errors.INVALID_ZONE, "'forward-port' cannot be used in a policy if an egress zone has assigned interfaces") def check_name(self, name): super(Policy, self).check_name(name) if name.startswith('/'): raise FirewallError(errors.INVALID_NAME, "'%s' can't start with '/'" % name) elif name.endswith('/'): raise FirewallError(errors.INVALID_NAME, "'%s' can't end with '/'" % name) elif name.count('/') > 1: raise FirewallError(errors.INVALID_NAME, "more than one '/' in '%s'" % name) else: if "/" in name: checked_name = name[:name.find('/')] else: checked_name = name if len(checked_name) > max_policy_name_len(): raise FirewallError(errors.INVALID_NAME, "Policy of '%s' has %d chars, max is %d" % ( name, len(checked_name), max_policy_name_len())) if self.fw_config: if checked_name in self.fw_config.get_zones(): raise FirewallError(errors.NAME_CONFLICT, "Policies can't have the same name as a zone.") # PARSER class policy_ContentHandler(IO_Object_ContentHandler): def __init__(self, item): IO_Object_ContentHandler.__init__(self, item) self._rule = None self._rule_error = False self._limit_ok = None def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) if self._rule_error: return self.item.parser_check_element_attrs(name, attrs) if common_startElement(self, name, attrs): return elif name == "policy": if "version" in attrs: self.item.version = attrs["version"] if "priority" in attrs: self.item.priority = int(attrs["priority"]) if "target" in attrs: target = attrs["target"] if target not in POLICY_TARGETS: raise FirewallError(errors.INVALID_TARGET, target) if target: self.item.target = target elif name == "ingress-zone": if attrs["name"] not in self.item.ingress_zones: self.item.ingress_zones.append(attrs["name"]) else: log.warning("Ingress zone '%s' already set, ignoring.", attrs["name"]) elif name == "egress-zone": if attrs["name"] not in self.item.egress_zones: self.item.egress_zones.append(attrs["name"]) else: log.warning("Egress zone '%s' already set, ignoring.", attrs["name"]) elif name == "source": if not self._rule: log.warning('Invalid rule: Source outside of rule') self._rule_error = True return if self._rule.source: log.warning("Invalid rule: More than one source in rule '%s', ignoring.", str(self._rule)) self._rule_error = True return invert = False if "invert" in attrs and \ attrs["invert"].lower() in [ "yes", "true" ]: invert = True addr = mac = ipset = None if "address" in attrs: addr = attrs["address"] if "mac" in attrs: mac = attrs["mac"] if "ipset" in attrs: ipset = attrs["ipset"] self._rule.source = rich.Rich_Source(addr, mac, ipset, invert=invert) return else: log.warning("Unknown XML element '%s'", name) return def endElement(self, name): IO_Object_ContentHandler.endElement(self, name) common_endElement(self, name) def policy_reader(filename, path, no_check_name=False): policy = Policy() if not filename.endswith(".xml"): raise FirewallError(errors.INVALID_NAME, "'%s' is missing .xml suffix" % filename) policy.name = filename[:-4] if not no_check_name: policy.check_name(policy.name) policy.filename = filename policy.path = path policy.builtin = False if path.startswith(config.ETC_FIREWALLD) else True policy.default = policy.builtin handler = policy_ContentHandler(policy) parser = sax.make_parser() parser.setContentHandler(handler) name = "%s/%s" % (path, filename) with open(name, "rb") as f: source = sax.InputSource(None) source.setByteStream(f) try: parser.parse(source) except sax.SAXParseException as msg: raise FirewallError(errors.INVALID_POLICY, "not a valid policy file: %s" % \ msg.getException()) del handler del parser return policy def policy_writer(policy, path=None): _path = path if path else policy.path if policy.filename: name = "%s/%s" % (_path, policy.filename) else: name = "%s/%s.xml" % (_path, policy.name) if os.path.exists(name): try: shutil.copy2(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) dirpath = os.path.dirname(name) if dirpath.startswith(config.ETC_FIREWALLD) and not os.path.exists(dirpath): if not os.path.exists(config.ETC_FIREWALLD): os.mkdir(config.ETC_FIREWALLD, 0o750) os.mkdir(dirpath, 0o750) f = io.open(name, mode='wt', encoding='UTF-8') handler = IO_Object_XMLGenerator(f) handler.startDocument() # start policy element attrs = {} if policy.version and policy.version != "": attrs["version"] = policy.version if policy.priority != policy.priority_default: attrs["priority"] = str(policy.priority) attrs["target"] = policy.target handler.startElement("policy", attrs) handler.ignorableWhitespace("\n") common_writer(policy, handler) # ingress-zones for zone in uniqify(policy.ingress_zones): handler.ignorableWhitespace(" ") handler.simpleElement("ingress-zone", { "name": zone }) handler.ignorableWhitespace("\n") # egress-zones for zone in uniqify(policy.egress_zones): handler.ignorableWhitespace(" ") handler.simpleElement("egress-zone", { "name": zone }) handler.ignorableWhitespace("\n") # end policy element handler.endElement("policy") handler.ignorableWhitespace("\n") handler.endDocument() f.close() del handler PK!γ11io/lockdown_whitelist.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import xml.sax as sax import os import io import shutil from firewall import config from firewall.core.io.io_object import PY2, IO_Object, \ IO_Object_ContentHandler, IO_Object_XMLGenerator from firewall.core.logger import log from firewall.functions import uniqify, checkUser, checkUid, checkCommand, \ checkContext, u2b_if_py2 from firewall import errors from firewall.errors import FirewallError class lockdown_whitelist_ContentHandler(IO_Object_ContentHandler): def __init__(self, item): IO_Object_ContentHandler.__init__(self, item) self.whitelist = False def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) self.item.parser_check_element_attrs(name, attrs) if name == "whitelist": if self.whitelist: raise FirewallError(errors.PARSE_ERROR, "More than one whitelist.") self.whitelist = True elif name == "command": if not self.whitelist: log.error("Parse Error: command outside of whitelist") return command = attrs["name"] self.item.add_command(command) elif name == "user": if not self.whitelist: log.error("Parse Error: user outside of whitelist") return if "id" in attrs: try: uid = int(attrs["id"]) except ValueError: log.error("Parse Error: %s is not a valid uid" % attrs["id"]) return self.item.add_uid(uid) elif "name" in attrs: self.item.add_user(attrs["name"]) elif name == "selinux": if not self.whitelist: log.error("Parse Error: selinux outside of whitelist") return if "context" not in attrs: log.error("Parse Error: no context") return self.item.add_context(attrs["context"]) else: log.error('Unknown XML element %s' % name) return class LockdownWhitelist(IO_Object): """ LockdownWhitelist class """ IMPORT_EXPORT_STRUCTURE = ( ( "commands", [ "" ] ), # as ( "contexts", [ "" ] ), # as ( "users", [ "" ] ), # as ( "uids", [ 0 ] ) # ai ) DBUS_SIGNATURE = '(asasasai)' ADDITIONAL_ALNUM_CHARS = [ "_" ] PARSER_REQUIRED_ELEMENT_ATTRS = { "whitelist": None, "command": [ "name" ], "user": None, # "group": None, "selinux": [ "context" ], } PARSER_OPTIONAL_ELEMENT_ATTRS = { "user": [ "id", "name" ], # "group": [ "id", "name" ], } def __init__(self, filename): super(LockdownWhitelist, self).__init__() self.filename = filename self.parser = None self.commands = [ ] self.contexts = [ ] self.users = [ ] self.uids = [ ] # self.gids = [ ] # self.groups = [ ] def _check_config(self, config, item, all_config): if item in [ "commands", "contexts", "users", "uids" ]: for x in config: self._check_config(x, item[:-1], all_config) elif item == "command": if not checkCommand(config): raise FirewallError(errors.INVALID_COMMAND, config) elif item == "context": if not checkContext(config): raise FirewallError(errors.INVALID_CONTEXT, config) elif item == "user": if not checkUser(config): raise FirewallError(errors.INVALID_USER, config) elif item == "uid": if not checkUid(config): raise FirewallError(errors.INVALID_UID, config) def cleanup(self): del self.commands[:] del self.contexts[:] del self.users[:] del self.uids[:] # del self.gids[:] # del self.groups[:] def encode_strings(self): """ HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.""" self.commands = [ u2b_if_py2(x) for x in self.commands ] self.contexts = [ u2b_if_py2(x) for x in self.contexts ] self.users = [ u2b_if_py2(x) for x in self.users ] # commands def add_command(self, command): if not checkCommand(command): raise FirewallError(errors.INVALID_COMMAND, command) if command not in self.commands: self.commands.append(command) else: raise FirewallError(errors.ALREADY_ENABLED, 'Command "%s" already in whitelist' % command) def remove_command(self, command): if command in self.commands: self.commands.remove(command) else: raise FirewallError(errors.NOT_ENABLED, 'Command "%s" not in whitelist.' % command) def has_command(self, command): return (command in self.commands) def match_command(self, command): for _command in self.commands: if _command.endswith("*"): if command.startswith(_command[:-1]): return True else: if _command == command: return True return False def get_commands(self): return self.commands # user ids def add_uid(self, uid): if not checkUid(uid): raise FirewallError(errors.INVALID_UID, str(uid)) if uid not in self.uids: self.uids.append(uid) else: raise FirewallError(errors.ALREADY_ENABLED, 'Uid "%s" already in whitelist' % uid) def remove_uid(self, uid): if uid in self.uids: self.uids.remove(uid) else: raise FirewallError(errors.NOT_ENABLED, 'Uid "%s" not in whitelist.' % uid) def has_uid(self, uid): return (uid in self.uids) def match_uid(self, uid): return (uid in self.uids) def get_uids(self): return self.uids # users def add_user(self, user): if not checkUser(user): raise FirewallError(errors.INVALID_USER, user) if user not in self.users: self.users.append(user) else: raise FirewallError(errors.ALREADY_ENABLED, 'User "%s" already in whitelist' % user) def remove_user(self, user): if user in self.users: self.users.remove(user) else: raise FirewallError(errors.NOT_ENABLED, 'User "%s" not in whitelist.' % user) def has_user(self, user): return (user in self.users) def match_user(self, user): return (user in self.users) def get_users(self): return self.users # # group ids # # def add_gid(self, gid): # if gid not in self.gids: # self.gids.append(gid) # # def remove_gid(self, gid): # if gid in self.gids: # self.gids.remove(gid) # else: # raise FirewallError(errors.NOT_ENABLED, # 'Gid "%s" not in whitelist.' % gid) # # def has_gid(self, gid): # return (gid in self.gids) # # def match_gid(self, gid): # return (gid in self.gids) # # def get_gids(self): # return self.gids # # groups # # def add_group(self, group): # if group not in self.groups: # self.groups.append(group) # # def remove_group(self, group): # if group in self.groups: # self.groups.remove(group) # else: # raise FirewallError(errors.NOT_ENABLED, # 'Group "%s" not in whitelist.' % group) # # def has_group(self, group): # return (group in self.groups) # # def match_group(self, group): # return (group in self.groups) # # def get_groups(self): # return self.groups # selinux contexts def add_context(self, context): if not checkContext(context): raise FirewallError(errors.INVALID_CONTEXT, context) if context not in self.contexts: self.contexts.append(context) else: raise FirewallError(errors.ALREADY_ENABLED, 'Context "%s" already in whitelist' % context) def remove_context(self, context): if context in self.contexts: self.contexts.remove(context) else: raise FirewallError(errors.NOT_ENABLED, 'Context "%s" not in whitelist.' % context) def has_context(self, context): return (context in self.contexts) def match_context(self, context): return (context in self.contexts) def get_contexts(self): return self.contexts # read and write def read(self): self.cleanup() if not self.filename.endswith(".xml"): raise FirewallError(errors.INVALID_NAME, "'%s' is missing .xml suffix" % self.filename) handler = lockdown_whitelist_ContentHandler(self) parser = sax.make_parser() parser.setContentHandler(handler) try: parser.parse(self.filename) except sax.SAXParseException as msg: raise FirewallError(errors.INVALID_TYPE, "Not a valid file: %s" % \ msg.getException()) del handler del parser if PY2: self.encode_strings() def write(self): if os.path.exists(self.filename): try: shutil.copy2(self.filename, "%s.old" % self.filename) except Exception as msg: raise IOError("Backup of '%s' failed: %s" % (self.filename, msg)) if not os.path.exists(config.ETC_FIREWALLD): os.mkdir(config.ETC_FIREWALLD, 0o750) f = io.open(self.filename, mode='wt', encoding='UTF-8') handler = IO_Object_XMLGenerator(f) handler.startDocument() # start whitelist element handler.startElement("whitelist", { }) handler.ignorableWhitespace("\n") # commands for command in uniqify(self.commands): handler.ignorableWhitespace(" ") handler.simpleElement("command", { "name": command }) handler.ignorableWhitespace("\n") for uid in uniqify(self.uids): handler.ignorableWhitespace(" ") handler.simpleElement("user", { "id": str(uid) }) handler.ignorableWhitespace("\n") for user in uniqify(self.users): handler.ignorableWhitespace(" ") handler.simpleElement("user", { "name": user }) handler.ignorableWhitespace("\n") # for gid in uniqify(self.gids): # handler.ignorableWhitespace(" ") # handler.simpleElement("user", { "id": str(gid) }) # handler.ignorableWhitespace("\n") # for group in uniqify(self.groups): # handler.ignorableWhitespace(" ") # handler.simpleElement("group", { "name": group }) # handler.ignorableWhitespace("\n") for context in uniqify(self.contexts): handler.ignorableWhitespace(" ") handler.simpleElement("selinux", { "context": context }) handler.ignorableWhitespace("\n") # end whitelist element handler.endElement("whitelist") handler.ignorableWhitespace("\n") handler.endDocument() f.close() del handler PK!(55io/io_object.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """Generic io_object handler, io specific check methods.""" __all__ = [ "PY2", "IO_Object", "IO_Object_ContentHandler", "IO_Object_XMLGenerator", "check_port", "check_tcpudp", "check_protocol", "check_address" ] import xml.sax as sax import xml.sax.saxutils as saxutils import copy import sys from collections import OrderedDict from firewall import functions from firewall.functions import b2u from firewall import errors from firewall.errors import FirewallError PY2 = sys.version < '3' class IO_Object(object): """ Abstract IO_Object as base for icmptype, service and zone """ IMPORT_EXPORT_STRUCTURE = ( ) DBUS_SIGNATURE = '()' ADDITIONAL_ALNUM_CHARS = [ ] # additional to alnum PARSER_REQUIRED_ELEMENT_ATTRS = { } PARSER_OPTIONAL_ELEMENT_ATTRS = { } def __init__(self): self.filename = "" self.path = "" self.name = "" self.default = False self.builtin = False def export_config(self): ret = [ ] for x in self.IMPORT_EXPORT_STRUCTURE: ret.append(copy.deepcopy(getattr(self, x[0]))) return tuple(ret) def export_config_dict(self): conf = {} type_formats = dict([(x[0], x[1]) for x in self.IMPORT_EXPORT_STRUCTURE]) for key in type_formats: if getattr(self, key) or isinstance(getattr(self, key), bool): conf[key] = copy.deepcopy(getattr(self, key)) return conf def import_config(self, conf): self.check_config(conf) for i,(element,dummy) in enumerate(self.IMPORT_EXPORT_STRUCTURE): if isinstance(conf[i], list): # remove duplicates without changing the order _conf = [ ] _set = set() for x in conf[i]: if x not in _set: _conf.append(x) _set.add(x) del _set setattr(self, element, copy.deepcopy(_conf)) else: setattr(self, element, copy.deepcopy(conf[i])) def import_config_dict(self, conf): self.check_config_dict(conf) for key in conf: if not hasattr(self, key): raise FirewallError(errors.UNKNOWN_ERROR, "Internal error. '{}' is not a valid attribute".format(key)) if isinstance(conf[key], list): # maintain list order while removing duplicates setattr(self, key, list(OrderedDict.fromkeys(copy.deepcopy(conf[key])))) else: setattr(self, key, copy.deepcopy(conf[key])) def check_name(self, name): if not isinstance(name, str): raise FirewallError(errors.INVALID_TYPE, "'%s' not of type %s, but %s" % (name, type(""), type(name))) if len(name) < 1: raise FirewallError(errors.INVALID_NAME, "name can't be empty") for char in name: if not char.isalnum() and char not in self.ADDITIONAL_ALNUM_CHARS: raise FirewallError( errors.INVALID_NAME, "'%s' is not allowed in '%s'" % ((char, name))) def check_config(self, conf): if len(conf) != len(self.IMPORT_EXPORT_STRUCTURE): raise FirewallError( errors.INVALID_TYPE, "structure size mismatch %d != %d" % \ (len(conf), len(self.IMPORT_EXPORT_STRUCTURE))) conf_dict = {} for i,(x,y) in enumerate(self.IMPORT_EXPORT_STRUCTURE): conf_dict[x] = conf[i] self.check_config_dict(conf_dict) def check_config_dict(self, conf): type_formats = dict([(x[0], x[1]) for x in self.IMPORT_EXPORT_STRUCTURE]) for key in conf: if key not in [x for (x,y) in self.IMPORT_EXPORT_STRUCTURE]: raise FirewallError(errors.INVALID_OPTION, "option '{}' is not valid".format(key)) self._check_config_structure(conf[key], type_formats[key]) self._check_config(conf[key], key, conf) def _check_config(self, dummy1, dummy2, dummy3): # to be overloaded by sub classes return def _check_config_structure(self, conf, structure): if not isinstance(conf, type(structure)): raise FirewallError(errors.INVALID_TYPE, "'%s' not of type %s, but %s" % \ (conf, type(structure), type(conf))) if isinstance(structure, list): # same type elements, else struct if len(structure) != 1: raise FirewallError(errors.INVALID_TYPE, "len('%s') != 1" % structure) for x in conf: self._check_config_structure(x, structure[0]) elif isinstance(structure, tuple): if len(structure) != len(conf): raise FirewallError(errors.INVALID_TYPE, "len('%s') != %d" % (conf, len(structure))) for i,value in enumerate(structure): self._check_config_structure(conf[i], value) elif isinstance(structure, dict): # only one key value pair in structure (skey, svalue) = list(structure.items())[0] for (key, value) in conf.items(): if not isinstance(key, type(skey)): raise FirewallError(errors.INVALID_TYPE, "'%s' not of type %s, but %s" % (\ key, type(skey), type(key))) if not isinstance(value, type(svalue)): raise FirewallError(errors.INVALID_TYPE, "'%s' not of type %s, but %s" % (\ value, type(svalue), type(value))) # check required elements and attributes and also optional attributes def parser_check_element_attrs(self, name, attrs): _attrs = attrs.getNames() found = False if name in self.PARSER_REQUIRED_ELEMENT_ATTRS: found = True if self.PARSER_REQUIRED_ELEMENT_ATTRS[name] is not None: for x in self.PARSER_REQUIRED_ELEMENT_ATTRS[name]: if x in _attrs: _attrs.remove(x) else: raise FirewallError( errors.PARSE_ERROR, "Missing attribute %s for %s" % (x, name)) if name in self.PARSER_OPTIONAL_ELEMENT_ATTRS: found = True for x in self.PARSER_OPTIONAL_ELEMENT_ATTRS[name]: if x in _attrs: _attrs.remove(x) if not found: raise FirewallError(errors.PARSE_ERROR, "Unexpected element %s" % name) # raise attributes[0] for x in _attrs: raise FirewallError(errors.PARSE_ERROR, "%s: Unexpected attribute %s" % (name, x)) # PARSER class UnexpectedElementError(Exception): def __init__(self, name): super(UnexpectedElementError, self).__init__() self.name = name def __str__(self): return "Unexpected element '%s'" % (self.name) class MissingAttributeError(Exception): def __init__(self, name, attribute): super(MissingAttributeError, self).__init__() self.name = name self.attribute = attribute def __str__(self): return "Element '%s': missing '%s' attribute" % \ (self.name, self.attribute) class UnexpectedAttributeError(Exception): def __init__(self, name, attribute): super(UnexpectedAttributeError, self).__init__() self.name = name self.attribute = attribute def __str__(self): return "Element '%s': unexpected attribute '%s'" % \ (self.name, self.attribute) class IO_Object_ContentHandler(sax.handler.ContentHandler): def __init__(self, item): self.item = item self._element = "" def startDocument(self): self._element = "" def startElement(self, name, attrs): self._element = "" def endElement(self, name): if name == "short": self.item.short = self._element elif name == "description": self.item.description = self._element def characters(self, content): self._element += content.replace('\n', ' ') class IO_Object_XMLGenerator(saxutils.XMLGenerator): def __init__(self, out): # fix memory leak in saxutils.XMLGenerator.__init__: # out = _gettextwriter(out, encoding) # creates unbound object results in garbage in gc # # saxutils.XMLGenerator.__init__(self, out, "utf-8") # replaced by modified saxutils.XMLGenerator.__init__ code: sax.handler.ContentHandler.__init__(self) self._write = out.write self._flush = out.flush self._ns_contexts = [{}] # contains uri -> prefix dicts self._current_context = self._ns_contexts[-1] self._undeclared_ns_maps = [] self._encoding = "utf-8" self._pending_start_element = False self._short_empty_elements = False def startElement(self, name, attrs): """ saxutils.XMLGenerator.startElement() expects name and attrs to be unicode and bad things happen if any of them is (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. """ if PY2: attrs = { b2u(name):b2u(value) for name, value in attrs.items() } saxutils.XMLGenerator.startElement(self, name, attrs) def simpleElement(self, name, attrs): """ slightly modified startElement() """ if PY2: self._write(u'<' + b2u(name)) for (name, value) in attrs.items(): self._write(u' %s=%s' % (b2u(name), saxutils.quoteattr(b2u(value)))) self._write(u'/>') else: self._write('<' + name) for (name, value) in attrs.items(): self._write(' %s=%s' % (name, saxutils.quoteattr(value))) self._write('/>') def endElement(self, name): """ saxutils.XMLGenerator.endElement() expects name to be unicode and bad things happen if it's (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. """ saxutils.XMLGenerator.endElement(self, b2u(name)) def characters(self, content): """ saxutils.XMLGenerator.characters() expects content to be unicode and bad things happen if it's (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. """ saxutils.XMLGenerator.characters(self, b2u(content)) def ignorableWhitespace(self, content): """ saxutils.XMLGenerator.ignorableWhitespace() expects content to be unicode and bad things happen if it's (utf-8) encoded. We override the method here to sanitize this case. Can be removed once we drop Python2 support. """ saxutils.XMLGenerator.ignorableWhitespace(self, b2u(content)) def check_port(port): port_range = functions.getPortRange(port) if port_range == -2: raise FirewallError(errors.INVALID_PORT, "port number in '%s' is too big" % port) elif port_range == -1: raise FirewallError(errors.INVALID_PORT, "'%s' is invalid port range" % port) elif port_range is None: raise FirewallError(errors.INVALID_PORT, "port range '%s' is ambiguous" % port) elif len(port_range) == 2 and port_range[0] >= port_range[1]: raise FirewallError(errors.INVALID_PORT, "'%s' is invalid port range" % port) def check_tcpudp(protocol): if protocol not in [ "tcp", "udp", "sctp", "dccp" ]: raise FirewallError(errors.INVALID_PROTOCOL, "'%s' not from {'tcp'|'udp'|'sctp'|'dccp'}" % \ protocol) def check_protocol(protocol): if not functions.checkProtocol(protocol): raise FirewallError(errors.INVALID_PROTOCOL, protocol) def check_address(ipv, addr): if not functions.check_address(ipv, addr): raise FirewallError(errors.INVALID_ADDR, "'%s' is not valid %s address" % (addr, ipv)) PK!*yyio/functions.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2018 Red Hat, Inc. # # Authors: # Eric Garver # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os from firewall import config from firewall.errors import FirewallError from firewall.core.fw_config import FirewallConfig from firewall.core.io.zone import zone_reader from firewall.core.io.service import service_reader from firewall.core.io.ipset import ipset_reader from firewall.core.io.icmptype import icmptype_reader from firewall.core.io.helper import helper_reader from firewall.core.io.policy import policy_reader from firewall.core.io.direct import Direct from firewall.core.io.lockdown_whitelist import LockdownWhitelist from firewall.core.io.firewalld_conf import firewalld_conf def check_config(fw): fw_config = FirewallConfig(fw) readers = { "ipset": {"reader": ipset_reader, "add": fw_config.add_ipset, "dirs": [config.FIREWALLD_IPSETS, config.ETC_FIREWALLD_IPSETS], }, "helper": {"reader": helper_reader, "add": fw_config.add_helper, "dirs": [config.FIREWALLD_HELPERS, config.ETC_FIREWALLD_HELPERS], }, "icmptype": {"reader": icmptype_reader, "add": fw_config.add_icmptype, "dirs": [config.FIREWALLD_ICMPTYPES, config.ETC_FIREWALLD_ICMPTYPES], }, "service": {"reader": service_reader, "add": fw_config.add_service, "dirs": [config.FIREWALLD_SERVICES, config.ETC_FIREWALLD_SERVICES], }, "zone": {"reader": zone_reader, "add": fw_config.add_zone, "dirs": [config.FIREWALLD_ZONES, config.ETC_FIREWALLD_ZONES], }, "policy": {"reader": policy_reader, "add": fw_config.add_policy_object, "dirs": [config.FIREWALLD_POLICIES, config.ETC_FIREWALLD_POLICIES], }, } for reader in readers.keys(): for _dir in readers[reader]["dirs"]: if not os.path.isdir(_dir): continue for file in sorted(os.listdir(_dir)): if file.endswith(".xml"): try: obj = readers[reader]["reader"](file, _dir) if reader in ["zone", "policy"]: obj.fw_config = fw_config obj.check_config_dict(obj.export_config_dict()) readers[reader]["add"](obj) except FirewallError as error: raise FirewallError(error.code, "'%s': %s" % (file, error.msg)) except Exception as msg: raise Exception("'%s': %s" % (file, msg)) if os.path.isfile(config.FIREWALLD_DIRECT): try: obj = Direct(config.FIREWALLD_DIRECT) obj.read() obj.check_config(obj.export_config()) except FirewallError as error: raise FirewallError(error.code, "'%s': %s" % (config.FIREWALLD_DIRECT, error.msg)) except Exception as msg: raise Exception("'%s': %s" % (config.FIREWALLD_DIRECT, msg)) if os.path.isfile(config.LOCKDOWN_WHITELIST): try: obj = LockdownWhitelist(config.LOCKDOWN_WHITELIST) obj.read() obj.check_config(obj.export_config()) except FirewallError as error: raise FirewallError(error.code, "'%s': %s" % (config.LOCKDOWN_WHITELIST, error.msg)) except Exception as msg: raise Exception("'%s': %s" % (config.LOCKDOWN_WHITELIST, msg)) if os.path.isfile(config.FIREWALLD_CONF): try: obj = firewalld_conf(config.FIREWALLD_CONF) obj.read() except FirewallError as error: raise FirewallError(error.code, "'%s': %s" % (config.FIREWALLD_CONF, error.msg)) except Exception as msg: raise Exception("'%s': %s" % (config.FIREWALLD_CONF, msg)) PK!8MM io/zone.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "Zone", "zone_reader", "zone_writer" ] import xml.sax as sax import os import io import shutil from firewall import config from firewall.functions import checkIPnMask, checkIP6nMask, checkInterface, uniqify, max_zone_name_len, u2b_if_py2, check_mac from firewall.core.base import DEFAULT_ZONE_TARGET, ZONE_TARGETS from firewall.core.io.io_object import PY2, IO_Object, \ IO_Object_ContentHandler, IO_Object_XMLGenerator from firewall.core.io.policy import common_startElement, common_endElement, common_check_config, common_writer from firewall.core import rich from firewall.core.logger import log from firewall import errors from firewall.errors import FirewallError class Zone(IO_Object): """ Zone class """ IMPORT_EXPORT_STRUCTURE = ( ( "version", "" ), # s ( "short", "" ), # s ( "description", "" ), # s ( "UNUSED", False ), # b ( "target", "" ), # s ( "services", [ "", ], ), # as ( "ports", [ ( "", "" ), ], ), # a(ss) ( "icmp_blocks", [ "", ], ), # as ( "masquerade", False ), # b ( "forward_ports", [ ( "", "", "", "" ), ], ), # a(ssss) ( "interfaces", [ "" ] ), # as ( "sources", [ "" ] ), # as ( "rules_str", [ "" ] ), # as ( "protocols", [ "", ], ), # as ( "source_ports", [ ( "", "" ), ], ), # a(ss) ( "icmp_block_inversion", False ), # b ( "forward", False ), # b ) ADDITIONAL_ALNUM_CHARS = [ "_", "-", "/" ] PARSER_REQUIRED_ELEMENT_ATTRS = { "short": None, "description": None, "zone": None, "service": [ "name" ], "port": [ "port", "protocol" ], "icmp-block": [ "name" ], "icmp-type": [ "name" ], "forward": None, "forward-port": [ "port", "protocol" ], "interface": [ "name" ], "rule": None, "source": None, "destination": None, "protocol": [ "value" ], "source-port": [ "port", "protocol" ], "log": None, "audit": None, "accept": None, "reject": None, "drop": None, "mark": [ "set" ], "limit": [ "value" ], "icmp-block-inversion": None, } PARSER_OPTIONAL_ELEMENT_ATTRS = { "zone": [ "name", "immutable", "target", "version" ], "masquerade": [ "enabled" ], "forward-port": [ "to-port", "to-addr" ], "rule": [ "family", "priority" ], "source": [ "address", "mac", "invert", "family", "ipset" ], "destination": [ "address", "invert", "ipset" ], "log": [ "prefix", "level" ], "reject": [ "type" ], "limit": ["burst"], } @staticmethod def index_of(element): for i, (el, dummy) in enumerate(Zone.IMPORT_EXPORT_STRUCTURE): if el == element: return i raise FirewallError(errors.UNKNOWN_ERROR, "index_of()") def __init__(self): super(Zone, self).__init__() self.version = "" self.short = "" self.description = "" self.UNUSED = False self.target = DEFAULT_ZONE_TARGET self.services = [ ] self.ports = [ ] self.protocols = [ ] self.icmp_blocks = [ ] self.forward = False self.masquerade = False self.forward_ports = [ ] self.source_ports = [ ] self.interfaces = [ ] self.sources = [ ] self.fw_config = None # to be able to check services and a icmp_blocks self.rules = [ ] self.rules_str = [ ] self.icmp_block_inversion = False self.combined = False self.applied = False def cleanup(self): self.version = "" self.short = "" self.description = "" self.UNUSED = False self.target = DEFAULT_ZONE_TARGET del self.services[:] del self.ports[:] del self.protocols[:] del self.icmp_blocks[:] self.forward = False self.masquerade = False del self.forward_ports[:] del self.source_ports[:] del self.interfaces[:] del self.sources[:] self.fw_config = None # to be able to check services and a icmp_blocks del self.rules[:] del self.rules_str[:] self.icmp_block_inversion = False self.combined = False self.applied = False def encode_strings(self): """ HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.""" self.version = u2b_if_py2(self.version) self.short = u2b_if_py2(self.short) self.description = u2b_if_py2(self.description) self.target = u2b_if_py2(self.target) self.services = [u2b_if_py2(s) for s in self.services] self.ports = [(u2b_if_py2(po),u2b_if_py2(pr)) for (po,pr) in self.ports] self.protocols = [u2b_if_py2(pr) for pr in self.protocols] self.icmp_blocks = [u2b_if_py2(i) for i in self.icmp_blocks] self.forward_ports = [(u2b_if_py2(p1),u2b_if_py2(p2),u2b_if_py2(p3),u2b_if_py2(p4)) for (p1,p2,p3,p4) in self.forward_ports] self.source_ports = [(u2b_if_py2(po),u2b_if_py2(pr)) for (po,pr) in self.source_ports] self.interfaces = [u2b_if_py2(i) for i in self.interfaces] self.sources = [u2b_if_py2(s) for s in self.sources] self.rules = [u2b_if_py2(s) for s in self.rules] self.rules_str = [u2b_if_py2(s) for s in self.rules_str] def __setattr__(self, name, value): if name == "rules_str": self.rules = [rich.Rich_Rule(rule_str=s) for s in value] # must convert back to string to get the canonical string. super(Zone, self).__setattr__(name, [str(s) for s in self.rules]) else: super(Zone, self).__setattr__(name, value) def export_config_dict(self): conf = super(Zone, self).export_config_dict() del conf["UNUSED"] return conf def _check_config(self, config, item, all_config): common_check_config(self, config, item, all_config) if item == "target": if config not in ZONE_TARGETS: raise FirewallError(errors.INVALID_TARGET, config) elif item == "interfaces": for interface in config: if not checkInterface(interface): raise FirewallError(errors.INVALID_INTERFACE, interface) if self.fw_config: for zone in self.fw_config.get_zones(): if zone == self.name: continue if interface in self.fw_config.get_zone(zone).interfaces: raise FirewallError(errors.INVALID_INTERFACE, "interface '{}' already bound to zone '{}'".format(interface, zone)) elif item == "sources": for source in config: if not checkIPnMask(source) and not checkIP6nMask(source) and \ not check_mac(source) and not source.startswith("ipset:"): raise FirewallError(errors.INVALID_ADDR, source) if self.fw_config: for zone in self.fw_config.get_zones(): if zone == self.name: continue if source in self.fw_config.get_zone(zone).sources: raise FirewallError(errors.INVALID_ADDR, "source '{}' already bound to zone '{}'".format(source, zone)) def check_name(self, name): super(Zone, self).check_name(name) if name.startswith('/'): raise FirewallError(errors.INVALID_NAME, "'%s' can't start with '/'" % name) elif name.endswith('/'): raise FirewallError(errors.INVALID_NAME, "'%s' can't end with '/'" % name) elif name.count('/') > 1: raise FirewallError(errors.INVALID_NAME, "more than one '/' in '%s'" % name) else: if "/" in name: checked_name = name[:name.find('/')] else: checked_name = name if len(checked_name) > max_zone_name_len(): raise FirewallError(errors.INVALID_NAME, "Zone of '%s' has %d chars, max is %d %s" % ( name, len(checked_name), max_zone_name_len(), self.combined)) if self.fw_config: if checked_name in self.fw_config.get_policy_objects(): raise FirewallError(errors.NAME_CONFLICT, "Zones can't have the same name as a policy.") def combine(self, zone): self.combined = True self.filename = None self.version = "" self.short = "" self.description = "" for interface in zone.interfaces: if interface not in self.interfaces: self.interfaces.append(interface) for source in zone.sources: if source not in self.sources: self.sources.append(source) for service in zone.services: if service not in self.services: self.services.append(service) for port in zone.ports: if port not in self.ports: self.ports.append(port) for proto in zone.protocols: if proto not in self.protocols: self.protocols.append(proto) for icmp in zone.icmp_blocks: if icmp not in self.icmp_blocks: self.icmp_blocks.append(icmp) if zone.forward: self.forward = True if zone.masquerade: self.masquerade = True for forward in zone.forward_ports: if forward not in self.forward_ports: self.forward_ports.append(forward) for port in zone.source_ports: if port not in self.source_ports: self.source_ports.append(port) for rule in zone.rules: self.rules.append(rule) self.rules_str.append(str(rule)) if zone.icmp_block_inversion: self.icmp_block_inversion = True # PARSER class zone_ContentHandler(IO_Object_ContentHandler): def __init__(self, item): IO_Object_ContentHandler.__init__(self, item) self._rule = None self._rule_error = False self._limit_ok = None def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) if self._rule_error: return self.item.parser_check_element_attrs(name, attrs) if common_startElement(self, name, attrs): return elif name == "zone": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'", attrs["name"]) if "version" in attrs: self.item.version = attrs["version"] if "immutable" in attrs: log.warning("Ignoring deprecated attribute immutable='%s'", attrs["immutable"]) if "target" in attrs: target = attrs["target"] if target not in ZONE_TARGETS: raise FirewallError(errors.INVALID_TARGET, target) if target != "" and target != DEFAULT_ZONE_TARGET: self.item.target = target elif name == "forward": if self.item.forward: log.warning("Forward already set, ignoring.") else: self.item.forward = True elif name == "interface": if self._rule: log.warning('Invalid rule: interface use in rule.') self._rule_error = True return # zone bound to interface if "name" not in attrs: log.warning('Invalid interface: Name missing.') self._rule_error = True return if attrs["name"] not in self.item.interfaces: self.item.interfaces.append(attrs["name"]) else: log.warning("Interface '%s' already set, ignoring.", attrs["name"]) elif name == "source": if self._rule: if self._rule.source: log.warning("Invalid rule: More than one source in rule '%s', ignoring.", str(self._rule)) self._rule_error = True return invert = False if "invert" in attrs and \ attrs["invert"].lower() in [ "yes", "true" ]: invert = True addr = mac = ipset = None if "address" in attrs: addr = attrs["address"] if "mac" in attrs: mac = attrs["mac"] if "ipset" in attrs: ipset = attrs["ipset"] self._rule.source = rich.Rich_Source(addr, mac, ipset, invert=invert) return # zone bound to source if "address" not in attrs and "ipset" not in attrs: log.warning('Invalid source: No address no ipset.') return if "address" in attrs and "ipset" in attrs: log.warning('Invalid source: Address and ipset.') return if "family" in attrs: log.warning("Ignoring deprecated attribute family='%s'", attrs["family"]) if "invert" in attrs: log.warning('Invalid source: Invertion not allowed here.') return if "address" in attrs: if not checkIPnMask(attrs["address"]) and \ not checkIP6nMask(attrs["address"]) and \ not check_mac(attrs["address"]): raise FirewallError(errors.INVALID_ADDR, attrs["address"]) if "ipset" in attrs: entry = "ipset:%s" % attrs["ipset"] if entry not in self.item.sources: self.item.sources.append(entry) else: log.warning("Source '%s' already set, ignoring.", attrs["address"]) if "address" in attrs: entry = attrs["address"] if entry not in self.item.sources: self.item.sources.append(entry) else: log.warning("Source '%s' already set, ignoring.", attrs["address"]) elif name == "icmp-block-inversion": if self.item.icmp_block_inversion: log.warning("Icmp-Block-Inversion already set, ignoring.") else: self.item.icmp_block_inversion = True else: log.warning("Unknown XML element '%s'", name) return def endElement(self, name): IO_Object_ContentHandler.endElement(self, name) common_endElement(self, name) def zone_reader(filename, path, no_check_name=False): zone = Zone() if not filename.endswith(".xml"): raise FirewallError(errors.INVALID_NAME, "'%s' is missing .xml suffix" % filename) zone.name = filename[:-4] if not no_check_name: zone.check_name(zone.name) zone.filename = filename zone.path = path zone.builtin = False if path.startswith(config.ETC_FIREWALLD) else True zone.default = zone.builtin handler = zone_ContentHandler(zone) parser = sax.make_parser() parser.setContentHandler(handler) name = "%s/%s" % (path, filename) with open(name, "rb") as f: source = sax.InputSource(None) source.setByteStream(f) try: parser.parse(source) except sax.SAXParseException as msg: raise FirewallError(errors.INVALID_ZONE, "not a valid zone file: %s" % \ msg.getException()) del handler del parser if PY2: zone.encode_strings() return zone def zone_writer(zone, path=None): _path = path if path else zone.path if zone.filename: name = "%s/%s" % (_path, zone.filename) else: name = "%s/%s.xml" % (_path, zone.name) if os.path.exists(name): try: shutil.copy2(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) dirpath = os.path.dirname(name) if dirpath.startswith(config.ETC_FIREWALLD) and not os.path.exists(dirpath): if not os.path.exists(config.ETC_FIREWALLD): os.mkdir(config.ETC_FIREWALLD, 0o750) os.mkdir(dirpath, 0o750) f = io.open(name, mode='wt', encoding='UTF-8') handler = IO_Object_XMLGenerator(f) handler.startDocument() # start zone element attrs = {} if zone.version and zone.version != "": attrs["version"] = zone.version if zone.target != DEFAULT_ZONE_TARGET: attrs["target"] = zone.target handler.startElement("zone", attrs) handler.ignorableWhitespace("\n") common_writer(zone, handler) # interfaces for interface in uniqify(zone.interfaces): handler.ignorableWhitespace(" ") handler.simpleElement("interface", { "name": interface }) handler.ignorableWhitespace("\n") # source for source in uniqify(zone.sources): handler.ignorableWhitespace(" ") if "ipset:" in source: handler.simpleElement("source", { "ipset": source[6:] }) else: handler.simpleElement("source", { "address": source }) handler.ignorableWhitespace("\n") # icmp-block-inversion if zone.icmp_block_inversion: handler.ignorableWhitespace(" ") handler.simpleElement("icmp-block-inversion", { }) handler.ignorableWhitespace("\n") # forward if zone.forward: handler.ignorableWhitespace(" ") handler.simpleElement("forward", { }) handler.ignorableWhitespace("\n") # end zone element handler.endElement("zone") handler.ignorableWhitespace("\n") handler.endDocument() f.close() del handler PK!9155io/firewalld_conf.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2012 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os.path import io import tempfile import shutil from firewall import config from firewall.core.logger import log from firewall.functions import b2u, u2b, PY2 valid_keys = [ "DefaultZone", "MinimalMark", "CleanupOnExit", "CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter", "IndividualCalls", "LogDenied", "AutomaticHelpers", "FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4", "AllowZoneDrifting" ] class firewalld_conf(object): def __init__(self, filename): self._config = { } self._deleted = [ ] self.filename = filename self.clear() def clear(self): self._config = { } self._deleted = [ ] def cleanup(self): self._config.clear() self._deleted = [ ] def get(self, key): return self._config.get(key.strip()) def set(self, key, value): _key = b2u(key.strip()) self._config[_key] = b2u(value.strip()) if _key in self._deleted: self._deleted.remove(_key) def __str__(self): s = "" for (key,value) in self._config.items(): if s: s += '\n' s += '%s=%s' % (key, value) return u2b(s) if PY2 else s # load self.filename def read(self): self.clear() try: f = open(self.filename, "r") except Exception as msg: log.error("Failed to load '%s': %s", self.filename, msg) self.set("DefaultZone", config.FALLBACK_ZONE) self.set("MinimalMark", str(config.FALLBACK_MINIMAL_MARK)) self.set("CleanupOnExit", "yes" if config.FALLBACK_CLEANUP_ON_EXIT else "no") self.set("CleanupModulesOnExit", "yes" if config.FALLBACK_CLEANUP_MODULES_ON_EXIT else "no") self.set("Lockdown", "yes" if config.FALLBACK_LOCKDOWN else "no") self.set("IPv6_rpfilter","yes" if config.FALLBACK_IPV6_RPFILTER else "no") self.set("IndividualCalls", "yes" if config.FALLBACK_INDIVIDUAL_CALLS else "no") self.set("LogDenied", config.FALLBACK_LOG_DENIED) self.set("AutomaticHelpers", config.FALLBACK_AUTOMATIC_HELPERS) self.set("FirewallBackend", config.FALLBACK_FIREWALL_BACKEND) self.set("FlushAllOnReload", "yes" if config.FALLBACK_FLUSH_ALL_ON_RELOAD else "no") self.set("RFC3964_IPv4", "yes" if config.FALLBACK_RFC3964_IPV4 else "no") self.set("AllowZoneDrifting", "yes" if config.FALLBACK_ALLOW_ZONE_DRIFTING else "no") raise for line in f: if not line: break line = line.strip() if len(line) < 1 or line[0] in ['#', ';']: continue # get key/value pair pair = [ x.strip() for x in line.split("=") ] if len(pair) != 2: log.error("Invalid option definition: '%s'", line.strip()) continue elif pair[0] not in valid_keys: log.error("Invalid option: '%s'", line.strip()) continue elif pair[1] == '': log.error("Missing value: '%s'", line.strip()) continue elif self._config.get(pair[0]) is not None: log.error("Duplicate option definition: '%s'", line.strip()) continue self._config[pair[0]] = pair[1] f.close() # check default zone if not self.get("DefaultZone"): log.error("DefaultZone is not set, using default value '%s'", config.FALLBACK_ZONE) self.set("DefaultZone", str(config.FALLBACK_ZONE)) # check minimal mark value = self.get("MinimalMark") try: int(value) except (ValueError, TypeError): if value is not None: log.warning("MinimalMark '%s' is not valid, using default " "value '%d'", value if value else '', config.FALLBACK_MINIMAL_MARK) self.set("MinimalMark", str(config.FALLBACK_MINIMAL_MARK)) # check cleanup on exit value = self.get("CleanupOnExit") if not value or value.lower() not in [ "no", "false", "yes", "true" ]: if value is not None: log.warning("CleanupOnExit '%s' is not valid, using default " "value %s", value if value else '', config.FALLBACK_CLEANUP_ON_EXIT) self.set("CleanupOnExit", "yes" if config.FALLBACK_CLEANUP_ON_EXIT else "no") # check module cleanup on exit value = self.get("CleanupModulesOnExit") if not value or value.lower() not in [ "no", "false", "yes", "true" ]: if value is not None: log.warning("CleanupModulesOnExit '%s' is not valid, using default " "value %s", value if value else '', config.FALLBACK_CLEANUP_MODULES_ON_EXIT) self.set("CleanupModulesOnExit", "yes" if config.FALLBACK_CLEANUP_MODULES_ON_EXIT else "no") # check lockdown value = self.get("Lockdown") if not value or value.lower() not in [ "yes", "true", "no", "false" ]: if value is not None: log.warning("Lockdown '%s' is not valid, using default " "value %s", value if value else '', config.FALLBACK_LOCKDOWN) self.set("Lockdown", "yes" if config.FALLBACK_LOCKDOWN else "no") # check ipv6_rpfilter value = self.get("IPv6_rpfilter") if not value or value.lower() not in [ "yes", "true", "no", "false" ]: if value is not None: log.warning("IPv6_rpfilter '%s' is not valid, using default " "value %s", value if value else '', config.FALLBACK_IPV6_RPFILTER) self.set("IPv6_rpfilter","yes" if config.FALLBACK_IPV6_RPFILTER else "no") # check individual calls value = self.get("IndividualCalls") if not value or value.lower() not in [ "yes", "true", "no", "false" ]: if value is not None: log.warning("IndividualCalls '%s' is not valid, using default " "value %s", value if value else '', config.FALLBACK_INDIVIDUAL_CALLS) self.set("IndividualCalls", "yes" if config.FALLBACK_INDIVIDUAL_CALLS else "no") # check log denied value = self.get("LogDenied") if not value or value not in config.LOG_DENIED_VALUES: if value is not None: log.warning("LogDenied '%s' is invalid, using default value '%s'", value, config.FALLBACK_LOG_DENIED) self.set("LogDenied", str(config.FALLBACK_LOG_DENIED)) # check automatic helpers value = self.get("AutomaticHelpers") if not value or value.lower() not in config.AUTOMATIC_HELPERS_VALUES: if value is not None: log.warning("AutomaticHelpers '%s' is not valid, using default " "value %s", value if value else '', config.FALLBACK_AUTOMATIC_HELPERS) self.set("AutomaticHelpers", str(config.FALLBACK_AUTOMATIC_HELPERS)) value = self.get("FirewallBackend") if not value or value.lower() not in config.FIREWALL_BACKEND_VALUES: if value is not None: log.warning("FirewallBackend '%s' is not valid, using default " "value %s", value if value else '', config.FALLBACK_FIREWALL_BACKEND) self.set("FirewallBackend", str(config.FALLBACK_FIREWALL_BACKEND)) value = self.get("FlushAllOnReload") if not value or value.lower() not in [ "yes", "true", "no", "false" ]: if value is not None: log.warning("FlushAllOnReload '%s' is not valid, using default " "value %s", value if value else '', config.FALLBACK_FLUSH_ALL_ON_RELOAD) self.set("FlushAllOnReload", str(config.FALLBACK_FLUSH_ALL_ON_RELOAD)) value = self.get("RFC3964_IPv4") if not value or value.lower() not in [ "yes", "true", "no", "false" ]: if value is not None: log.warning("RFC3964_IPv4 '%s' is not valid, using default " "value %s", value if value else '', config.FALLBACK_RFC3964_IPV4) self.set("RFC3964_IPv4", str(config.FALLBACK_RFC3964_IPV4)) value = self.get("AllowZoneDrifting") if not value or value.lower() not in [ "yes", "true", "no", "false" ]: if value is not None: log.warning("AllowZoneDrifting '%s' is not valid, using default " "value %s", value if value else '', config.FALLBACK_ALLOW_ZONE_DRIFTING) self.set("AllowZoneDrifting", str(config.FALLBACK_ALLOW_ZONE_DRIFTING)) # save to self.filename if there are key/value changes def write(self): if len(self._config) < 1: # no changes: nothing to do return # handled keys done = [ ] if not os.path.exists(config.ETC_FIREWALLD): os.mkdir(config.ETC_FIREWALLD, 0o750) try: temp_file = tempfile.NamedTemporaryFile(mode='wt', prefix="%s." % os.path.basename(self.filename), dir=os.path.dirname(self.filename), delete=False) except Exception as msg: log.error("Failed to open temporary file: %s" % msg) raise modified = False empty = False try: f= io.open(self.filename, mode='rt', encoding='UTF-8') except Exception as msg: if os.path.exists(self.filename): log.error("Failed to open '%s': %s" % (self.filename, msg)) raise else: f = None else: for line in f: if not line: break # remove newline line = line.strip("\n") if len(line) < 1: if not empty: temp_file.write(u"\n") empty = True elif line[0] == '#': empty = False temp_file.write(line) temp_file.write(u"\n") else: p = line.split("=") if len(p) != 2: empty = False temp_file.write(line+u"\n") continue key = p[0].strip() value = p[1].strip() # check for modified key/value pairs if key not in done: if (key in self._config and \ self._config[key] != value): empty = False temp_file.write(u'%s=%s\n' % (key, self._config[key])) modified = True elif key in self._deleted: modified = True else: empty = False temp_file.write(line+u"\n") done.append(key) else: modified = True # write remaining key/value pairs if len(self._config) > 0: for (key,value) in self._config.items(): if key in done: continue if key in ["MinimalMark", "AutomaticHelpers"]: # omit deprecated from new config continue if not empty: temp_file.write(u"\n") empty = True temp_file.write(u'%s=%s\n' % (key, value)) modified = True if f: f.close() temp_file.close() if not modified: # not modified: remove tempfile os.remove(temp_file.name) return # make backup if os.path.exists(self.filename): try: shutil.copy2(self.filename, "%s.old" % self.filename) except Exception as msg: os.remove(temp_file.name) raise IOError("Backup of '%s' failed: %s" % (self.filename, msg)) # copy tempfile try: shutil.move(temp_file.name, self.filename) except Exception as msg: os.remove(temp_file.name) raise IOError("Failed to create '%s': %s" % (self.filename, msg)) else: os.chmod(self.filename, 0o600) PK!mL22 io/service.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "Service", "service_reader", "service_writer" ] import xml.sax as sax import os import io import shutil from firewall import config from firewall.functions import u2b_if_py2 from firewall.core.io.io_object import PY2, IO_Object, \ IO_Object_ContentHandler, IO_Object_XMLGenerator, check_port, \ check_tcpudp, check_protocol, check_address from firewall.core.logger import log from firewall import errors from firewall.errors import FirewallError class Service(IO_Object): IMPORT_EXPORT_STRUCTURE = ( ( "version", "" ), ( "short", "" ), ( "description", "" ), ( "ports", [ ( "", "" ), ], ), ( "modules", [ "", ], ), ( "destination", { "": "", }, ), ( "protocols", [ "", ], ), ( "source_ports", [ ( "", "" ), ], ), ( "includes", [ "" ], ), ( "helpers", [ "", ], ), ) ADDITIONAL_ALNUM_CHARS = [ "_", "-" ] PARSER_REQUIRED_ELEMENT_ATTRS = { "short": None, "description": None, "service": None, } PARSER_OPTIONAL_ELEMENT_ATTRS = { "service": [ "name", "version" ], "port": [ "port", "protocol" ], "protocol": [ "value" ], "module": [ "name" ], "destination": [ "ipv4", "ipv6" ], "source-port": [ "port", "protocol" ], "include": [ "service" ], "helper": [ "name" ], } def __init__(self): super(Service, self).__init__() self.version = "" self.short = "" self.description = "" self.ports = [ ] self.protocols = [ ] self.modules = [ ] self.destination = { } self.source_ports = [ ] self.includes = [ ] self.helpers = [ ] def cleanup(self): self.version = "" self.short = "" self.description = "" del self.ports[:] del self.protocols[:] del self.modules[:] self.destination.clear() del self.source_ports[:] del self.includes[:] del self.helpers[:] def encode_strings(self): """ HACK. I haven't been able to make sax parser return strings encoded (because of python 2) instead of in unicode. Get rid of it once we throw out python 2 support.""" self.version = u2b_if_py2(self.version) self.short = u2b_if_py2(self.short) self.description = u2b_if_py2(self.description) self.ports = [(u2b_if_py2(po),u2b_if_py2(pr)) for (po,pr) in self.ports] self.modules = [u2b_if_py2(m) for m in self.modules] self.destination = {u2b_if_py2(k):u2b_if_py2(v) for k,v in self.destination.items()} self.protocols = [u2b_if_py2(pr) for pr in self.protocols] self.source_ports = [(u2b_if_py2(po),u2b_if_py2(pr)) for (po,pr) in self.source_ports] self.includes = [u2b_if_py2(s) for s in self.includes] self.helpers = [u2b_if_py2(s) for s in self.helpers] def _check_config(self, config, item, all_config): if item == "ports": for port in config: if port[0] != "": check_port(port[0]) check_tcpudp(port[1]) else: # only protocol check_protocol(port[1]) elif item == "protocols": for proto in config: check_protocol(proto) elif item == "source_ports": for port in config: check_port(port[0]) check_tcpudp(port[1]) elif item == "destination": for destination in config: if destination not in [ "ipv4", "ipv6" ]: raise FirewallError(errors.INVALID_DESTINATION, "'%s' not in {'ipv4'|'ipv6'}" % \ destination) check_address(destination, config[destination]) elif item == "modules": for module in config: if module.startswith("nf_conntrack_"): module = module.replace("nf_conntrack_", "") if "_" in module: module = module.replace("_", "-") if len(module) < 2: raise FirewallError(errors.INVALID_MODULE, module) # PARSER class service_ContentHandler(IO_Object_ContentHandler): def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) self.item.parser_check_element_attrs(name, attrs) if name == "service": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'", attrs["name"]) if "version" in attrs: self.item.version = attrs["version"] elif name == "short": pass elif name == "description": pass elif name == "port": if attrs["port"] != "": check_port(attrs["port"]) check_tcpudp(attrs["protocol"]) entry = (attrs["port"], attrs["protocol"]) if entry not in self.item.ports: self.item.ports.append(entry) else: log.warning("Port '%s/%s' already set, ignoring.", attrs["port"], attrs["protocol"]) else: check_protocol(attrs["protocol"]) if attrs["protocol"] not in self.item.protocols: self.item.protocols.append(attrs["protocol"]) else: log.warning("Protocol '%s' already set, ignoring.", attrs["protocol"]) elif name == "protocol": check_protocol(attrs["value"]) if attrs["value"] not in self.item.protocols: self.item.protocols.append(attrs["value"]) else: log.warning("Protocol '%s' already set, ignoring.", attrs["value"]) elif name == "source-port": check_port(attrs["port"]) check_tcpudp(attrs["protocol"]) entry = (attrs["port"], attrs["protocol"]) if entry not in self.item.source_ports: self.item.source_ports.append(entry) else: log.warning("SourcePort '%s/%s' already set, ignoring.", attrs["port"], attrs["protocol"]) elif name == "destination": for x in [ "ipv4", "ipv6" ]: if x in attrs: check_address(x, attrs[x]) if x in self.item.destination: log.warning("Destination address for '%s' already set, ignoring", x) else: self.item.destination[x] = attrs[x] elif name == "module": module = attrs["name"] if module.startswith("nf_conntrack_"): module = module.replace("nf_conntrack_", "") if "_" in module: module = module.replace("_", "-") if module not in self.item.modules: self.item.modules.append(module) else: log.warning("Module '%s' already set, ignoring.", module) elif name == "include": if attrs["service"] not in self.item.includes: self.item.includes.append(attrs["service"]) else: log.warning("Include '%s' already set, ignoring.", attrs["service"]) elif name == "helper": if attrs["name"] not in self.item.helpers: self.item.helpers.append(attrs["name"]) else: log.warning("Helper '%s' already set, ignoring.", attrs["name"]) def service_reader(filename, path): service = Service() if not filename.endswith(".xml"): raise FirewallError(errors.INVALID_NAME, "'%s' is missing .xml suffix" % filename) service.name = filename[:-4] service.check_name(service.name) service.filename = filename service.path = path service.builtin = False if path.startswith(config.ETC_FIREWALLD) else True service.default = service.builtin handler = service_ContentHandler(service) parser = sax.make_parser() parser.setContentHandler(handler) name = "%s/%s" % (path, filename) with open(name, "rb") as f: source = sax.InputSource(None) source.setByteStream(f) try: parser.parse(source) except sax.SAXParseException as msg: raise FirewallError(errors.INVALID_SERVICE, "not a valid service file: %s" % \ msg.getException()) del handler del parser if PY2: service.encode_strings() return service def service_writer(service, path=None): _path = path if path else service.path if service.filename: name = "%s/%s" % (_path, service.filename) else: name = "%s/%s.xml" % (_path, service.name) if os.path.exists(name): try: shutil.copy2(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) dirpath = os.path.dirname(name) if dirpath.startswith(config.ETC_FIREWALLD) and not os.path.exists(dirpath): if not os.path.exists(config.ETC_FIREWALLD): os.mkdir(config.ETC_FIREWALLD, 0o750) os.mkdir(dirpath, 0o750) f = io.open(name, mode='wt', encoding='UTF-8') handler = IO_Object_XMLGenerator(f) handler.startDocument() # start service element attrs = {} if service.version and service.version != "": attrs["version"] = service.version handler.startElement("service", attrs) handler.ignorableWhitespace("\n") # short if service.short and service.short != "": handler.ignorableWhitespace(" ") handler.startElement("short", { }) handler.characters(service.short) handler.endElement("short") handler.ignorableWhitespace("\n") # description if service.description and service.description != "": handler.ignorableWhitespace(" ") handler.startElement("description", { }) handler.characters(service.description) handler.endElement("description") handler.ignorableWhitespace("\n") # ports for port in service.ports: handler.ignorableWhitespace(" ") handler.simpleElement("port", { "port": port[0], "protocol": port[1] }) handler.ignorableWhitespace("\n") # protocols for protocol in service.protocols: handler.ignorableWhitespace(" ") handler.simpleElement("protocol", { "value": protocol }) handler.ignorableWhitespace("\n") # source ports for port in service.source_ports: handler.ignorableWhitespace(" ") handler.simpleElement("source-port", { "port": port[0], "protocol": port[1] }) handler.ignorableWhitespace("\n") # modules for module in service.modules: handler.ignorableWhitespace(" ") handler.simpleElement("module", { "name": module }) handler.ignorableWhitespace("\n") # destination if len(service.destination) > 0: handler.ignorableWhitespace(" ") handler.simpleElement("destination", service.destination) handler.ignorableWhitespace("\n") # includes for include in service.includes: handler.ignorableWhitespace(" ") handler.simpleElement("include", { "service": include }) handler.ignorableWhitespace("\n") # helpers for helper in service.helpers: handler.ignorableWhitespace(" ") handler.simpleElement("helper", { "name": helper }) handler.ignorableWhitespace("\n") # end service element handler.endElement('service') handler.ignorableWhitespace("\n") handler.endDocument() f.close() del handler PK!Dprog.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2010-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import subprocess __all__ = ["runProg"] def runProg(prog, argv=None, stdin=None): if argv is None: argv = [] args = [prog] + argv input_string = None if stdin: with open(stdin, 'r') as handle: input_string = handle.read().encode() env = {'LANG': 'C'} try: process = subprocess.Popen(args, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, close_fds=True, env=env) except OSError: return (255, '') (output, err_output) = process.communicate(input_string) output = output.decode('utf-8', 'replace') return (process.returncode, output) PK!\yy fw_zone.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import time import copy from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET, SOURCE_IPSET_TYPES from firewall.core.fw_transaction import FirewallTransaction from firewall.core.io.policy import Policy from firewall.core.logger import log from firewall.core.rich import Rich_Service, Rich_Port, Rich_Protocol, Rich_SourcePort, Rich_ForwardPort, \ Rich_IcmpBlock, Rich_IcmpType, Rich_Masquerade, Rich_Mark from firewall.functions import checkIPnMask, checkIP6nMask, check_mac from firewall import errors from firewall.errors import FirewallError from firewall.fw_types import LastUpdatedOrderedDict class FirewallZone(object): ZONE_POLICY_PRIORITY = 0 def __init__(self, fw): self._fw = fw self._zones = { } self._zone_policies = { } def __repr__(self): return '%s(%r)' % (self.__class__, self._zones) def cleanup(self): self._zones.clear() self._zone_policies.clear() def new_transaction(self): return FirewallTransaction(self._fw) def policy_name_from_zones(self, fromZone, toZone): return "zone_{fromZone}_{toZone}".format(fromZone=fromZone, toZone=toZone) # zones def get_zones(self): return sorted(self._zones.keys()) def get_active_zones(self): active_zones = [] for zone in self.get_zones(): if self.list_interfaces(zone) or self.list_sources(zone): active_zones.append(zone) return active_zones def get_zone_of_interface(self, interface): interface_id = self.__interface_id(interface) for zone in self._zones: if interface_id in self._zones[zone].settings["interfaces"]: # an interface can only be part of one zone return zone return None def get_zone_of_source(self, source): source_id = self.__source_id(source) for zone in self._zones: if source_id in self._zones[zone].settings["sources"]: # a source_id can only be part of one zone return zone return None def get_zone(self, zone): z = self._fw.check_zone(zone) return self._zones[z] def policy_obj_from_zone_obj(self, z_obj, fromZone, toZone): p_obj = Policy() p_obj.derived_from_zone = z_obj.name p_obj.name = self.policy_name_from_zones(fromZone, toZone) p_obj.priority = self.ZONE_POLICY_PRIORITY p_obj.target = z_obj.target p_obj.ingress_zones = [fromZone] p_obj.egress_zones = [toZone] # copy zone permanent config to policy permanent config # WARN: This assumes the same attribute names. # for setting in ["services", "ports", "masquerade", "forward_ports", "source_ports", "icmp_blocks", "rules", "protocols"]: if fromZone == z_obj.name and toZone == "HOST" and \ setting in ["services", "ports", "source_ports", "icmp_blocks", "protocols"]: # zone --> HOST setattr(p_obj, setting, copy.deepcopy(getattr(z_obj, setting))) elif fromZone == "ANY" and toZone == z_obj.name and setting in ["masquerade"]: # any zone --> zone setattr(p_obj, setting, copy.deepcopy(getattr(z_obj, setting))) elif fromZone == z_obj.name and toZone == "ANY" and \ setting in ["icmp_blocks", "forward_ports"]: # zone --> any zone setattr(p_obj, setting, copy.deepcopy(getattr(z_obj, setting))) elif setting in ["rules"]: p_obj.rules = [] for rule in z_obj.rules: current_policy = self.policy_name_from_zones(fromZone, toZone) if current_policy in self._rich_rule_to_policies(z_obj.name, rule): p_obj.rules.append(copy.deepcopy(rule)) return p_obj def add_zone(self, obj): obj.settings = { x : LastUpdatedOrderedDict() for x in ["interfaces", "sources", "icmp_block_inversion", "forward"] } self._zones[obj.name] = obj self._zone_policies[obj.name] = [] # Create policy objects, will need many: # - (zone --> HOST) - ports, service, etc # - (any zone --> zone) - masquerade # - (zone --> any zone) - ICMP block, icmp block inversion # - also includes forward-ports because it works on (nat, # PREROUTING) and therefore applies to redirects to the local # host or dnat to a different host. # - also includes rich rule "mark" action for the same reason # for fromZone,toZone in [(obj.name, "HOST"), ("ANY", obj.name), (obj.name, "ANY")]: p_obj = self.policy_obj_from_zone_obj(obj, fromZone, toZone) self._fw.policy.add_policy(p_obj) self._zone_policies[obj.name].append(p_obj.name) self.copy_permanent_to_runtime(obj.name) def copy_permanent_to_runtime(self, zone): obj = self._zones[zone] for arg in obj.interfaces: self.add_interface(zone, arg, allow_apply=False) for arg in obj.sources: self.add_source(zone, arg, allow_apply=False) if obj.forward: self.add_forward(zone) if obj.icmp_block_inversion: self.add_icmp_block_inversion(zone) def remove_zone(self, zone): obj = self._zones[zone] if obj.applied: self.unapply_zone_settings(zone) obj.settings.clear() del self._zones[zone] del self._zone_policies[zone] def apply_zones(self, use_transaction=None): for zone in self.get_zones(): z_obj = self._zones[zone] if len(z_obj.interfaces) > 0 or len(z_obj.sources) > 0: log.debug1("Applying zone '%s'", zone) self.apply_zone_settings(zone, use_transaction=use_transaction) def set_zone_applied(self, zone, applied): obj = self._zones[zone] obj.applied = applied # zone from chain def zone_from_chain(self, chain): if "_" not in chain: # no zone chain return None splits = chain.split("_") if len(splits) < 2: return None _chain = None for x in SHORTCUTS: if splits[0] == SHORTCUTS[x]: _chain = x if _chain is not None: # next part needs to be zone name if splits[1] not in self.get_zones(): return None if len(splits) == 2 or \ (len(splits) == 3 and splits[2] in [ "pre", "log", "deny", "allow", "post" ]): return (splits[1], _chain) return None def policy_from_chain(self, chain): x = self.zone_from_chain(chain) if x is None: return None (zone, _chain) = x # derived from _get_table_chains_for_zone_dispatch() if _chain in ["PREROUTING", "FORWARD_IN"]: fromZone = zone toZone = "ANY" elif _chain in ["INPUT"]: fromZone = zone toZone = "HOST" elif _chain in ["POSTROUTING", "FORWARD_OUT"]: fromZone = "ANY" toZone = zone else: raise FirewallError(errors.INVALID_CHAIN, "chain '%s' can't be mapped to a policy" % (chain)) return (self.policy_name_from_zones(fromZone, toZone), _chain) def create_zone_base_by_chain(self, ipv, table, chain, use_transaction=None): # Create zone base chains if the chain is reserved for a zone if ipv in [ "ipv4", "ipv6" ]: x = self.policy_from_chain(chain) if x is not None: (policy, _chain) = self.policy_from_chain(chain) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction self._fw.policy.gen_chain_rules(policy, True, table, _chain, transaction) if use_transaction is None: transaction.execute(True) # settings # generate settings record with sender, timeout def __gen_settings(self, timeout, sender): ret = { "date": time.time(), "sender": sender, "timeout": timeout, } return ret def get_settings(self, zone): return self.get_zone(zone).settings def _zone_settings(self, enable, zone, transaction): settings = self.get_settings(zone) for key in settings: for args in settings[key]: if key == "interfaces": self._interface(enable, zone, args, transaction) elif key == "sources": self._source(enable, zone, args[0], args[1], transaction) elif key == "icmp_block_inversion": continue elif key == "forward": # no need to call this when applying the zone as the rules # will be generated when adding the interfaces/sources pass else: log.warning("Zone '%s': Unknown setting '%s:%s', " "unable to apply", zone, key, args) # ICMP-block-inversion is always applied if enable: self._icmp_block_inversion(enable, zone, transaction) def apply_zone_settings(self, zone, use_transaction=None): _zone = self._fw.check_zone(zone) obj = self._zones[_zone] if obj.applied: return obj.applied = True if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction for policy in self._zone_policies[_zone]: log.debug1("Applying policy (%s) derived from zone '%s'", policy, zone) self._fw.policy.apply_policy_settings(policy, use_transaction=transaction) self._zone_settings(True, _zone, transaction) if use_transaction is None: transaction.execute(True) def unapply_zone_settings(self, zone, use_transaction=None): _zone = self._fw.check_zone(zone) obj = self._zones[_zone] if not obj.applied: return if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction for policy in self._zone_policies[_zone]: self._fw.policy.unapply_policy_settings(policy, use_transaction=transaction) self._zone_settings(False, _zone, transaction) if use_transaction is None: transaction.execute(True) def get_config_with_settings(self, zone): """ :return: exported config updated with runtime settings """ obj = self.get_zone(zone) conf_dict = self.get_config_with_settings_dict(zone) conf_list = [] for i in range(16): # tuple based API has 16 elements if obj.IMPORT_EXPORT_STRUCTURE[i][0] not in conf_dict: # old API needs the empty elements as well. Grab it from the # class otherwise we don't know the type. conf_list.append(copy.deepcopy(getattr(obj, obj.IMPORT_EXPORT_STRUCTURE[i][0]))) else: conf_list.append(conf_dict[obj.IMPORT_EXPORT_STRUCTURE[i][0]]) return tuple(conf_list) def get_config_with_settings_dict(self, zone): """ :return: exported config updated with runtime settings """ permanent = self.get_zone(zone).export_config_dict() if permanent["target"] == DEFAULT_ZONE_TARGET: permanent["target"] = "default" runtime = { "services": self.list_services(zone), "ports": self.list_ports(zone), "icmp_blocks": self.list_icmp_blocks(zone), "masquerade": self.query_masquerade(zone), "forward_ports": self.list_forward_ports(zone), "interfaces": self.list_interfaces(zone), "sources": self.list_sources(zone), "rules_str": self.list_rules(zone), "protocols": self.list_protocols(zone), "source_ports": self.list_source_ports(zone), "icmp_block_inversion": self.query_icmp_block_inversion(zone), "forward": self.query_forward(zone), } return self._fw.combine_runtime_with_permanent_settings(permanent, runtime) def set_config_with_settings_dict(self, zone, settings, sender): # stupid wrappers to convert rich rule string to rich rule object from firewall.core.rich import Rich_Rule def add_rule_wrapper(zone, rule_str, timeout=0, sender=None): self.add_rule(zone, Rich_Rule(rule_str=rule_str), timeout=0, sender=sender) def remove_rule_wrapper(zone, rule_str): self.remove_rule(zone, Rich_Rule(rule_str=rule_str)) setting_to_fn = { "services": (self.add_service, self.remove_service), "ports": (self.add_port, self.remove_port), "icmp_blocks": (self.add_icmp_block, self.remove_icmp_block), "masquerade": (self.add_masquerade, self.remove_masquerade), "forward_ports": (self.add_forward_port, self.remove_forward_port), "interfaces": (self.add_interface, self.remove_interface), "sources": (self.add_source, self.remove_source), "rules_str": (add_rule_wrapper, remove_rule_wrapper), "protocols": (self.add_protocol, self.remove_protocol), "source_ports": (self.add_source_port, self.remove_source_port), "icmp_block_inversion": (self.add_icmp_block_inversion, self.remove_icmp_block_inversion), "forward": (self.add_forward, self.remove_forward), } old_settings = self.get_config_with_settings_dict(zone) (add_settings, remove_settings) = self._fw.get_added_and_removed_settings(old_settings, settings) for key in remove_settings: if isinstance(remove_settings[key], list): for args in remove_settings[key]: if isinstance(args, tuple): setting_to_fn[key][1](zone, *args) else: setting_to_fn[key][1](zone, args) else: # bool setting_to_fn[key][1](zone) for key in add_settings: if isinstance(add_settings[key], list): for args in add_settings[key]: if key in ["interfaces", "sources"]: # no timeout arg setting_to_fn[key][0](zone, args, sender=sender) else: if isinstance(args, tuple): setting_to_fn[key][0](zone, *args, timeout=0, sender=sender) else: setting_to_fn[key][0](zone, args, timeout=0, sender=sender) else: # bool if key in ["icmp_block_inversion"]: # no timeout arg setting_to_fn[key][0](zone, sender=sender) else: setting_to_fn[key][0](zone, timeout=0, sender=sender) # INTERFACES def check_interface(self, interface): self._fw.check_interface(interface) def interface_get_sender(self, zone, interface): _zone = self._fw.check_zone(zone) _obj = self._zones[_zone] interface_id = self.__interface_id(interface) if interface_id in _obj.settings["interfaces"]: settings = _obj.settings["interfaces"][interface_id] if "sender" in settings and settings["sender"] is not None: return settings["sender"] return None def __interface_id(self, interface): self.check_interface(interface) return interface def add_interface(self, zone, interface, sender=None, use_transaction=None, allow_apply=True): self._fw.check_panic() _zone = self._fw.check_zone(zone) _obj = self._zones[_zone] interface_id = self.__interface_id(interface) if interface_id in _obj.settings["interfaces"]: raise FirewallError(errors.ZONE_ALREADY_SET, "'%s' already bound to '%s'" % (interface, zone)) if self.get_zone_of_interface(interface) is not None: raise FirewallError(errors.ZONE_CONFLICT, "'%s' already bound to a zone" % interface) log.debug1("Setting zone of interface '%s' to '%s'" % (interface, _zone)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if not _obj.applied and allow_apply: self.apply_zone_settings(zone, use_transaction=transaction) transaction.add_fail(self.set_zone_applied, _zone, False) if allow_apply: self._interface(True, _zone, interface, transaction) self.__register_interface(_obj, interface_id, zone, sender) transaction.add_fail(self.__unregister_interface, _obj, interface_id) if use_transaction is None: transaction.execute(True) return _zone def __register_interface(self, _obj, interface_id, zone, sender): _obj.settings["interfaces"][interface_id] = \ self.__gen_settings(0, sender) # add information whether we add to default or specific zone _obj.settings["interfaces"][interface_id]["__default__"] = \ (not zone or zone == "") def change_zone_of_interface(self, zone, interface, sender=None): self._fw.check_panic() _old_zone = self.get_zone_of_interface(interface) _new_zone = self._fw.check_zone(zone) if _new_zone == _old_zone: return _old_zone if _old_zone is not None: self.remove_interface(_old_zone, interface) _zone = self.add_interface(zone, interface, sender) return _zone def change_default_zone(self, old_zone, new_zone, use_transaction=None): self._fw.check_panic() if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction self.apply_zone_settings(new_zone, transaction) self._interface(True, new_zone, "+", transaction, append=True) if old_zone is not None and old_zone != "": self._interface(False, old_zone, "+", transaction, append=True) if use_transaction is None: transaction.execute(True) def remove_interface(self, zone, interface, use_transaction=None): self._fw.check_panic() zoi = self.get_zone_of_interface(interface) if zoi is None: raise FirewallError(errors.UNKNOWN_INTERFACE, "'%s' is not in any zone" % interface) _zone = zoi if zone == "" else self._fw.check_zone(zone) if zoi != _zone: raise FirewallError(errors.ZONE_CONFLICT, "remove_interface(%s, %s): zoi='%s'" % \ (zone, interface, zoi)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction _obj = self._zones[_zone] interface_id = self.__interface_id(interface) transaction.add_post(self.__unregister_interface, _obj, interface_id) self._interface(False, _zone, interface, transaction) if use_transaction is None: transaction.execute(True) return _zone def __unregister_interface(self, _obj, interface_id): if interface_id in _obj.settings["interfaces"]: del _obj.settings["interfaces"][interface_id] def query_interface(self, zone, interface): return self.__interface_id(interface) in self.get_settings(zone)["interfaces"] def list_interfaces(self, zone): return self.get_settings(zone)["interfaces"].keys() # SOURCES def check_source(self, source, applied=False): if checkIPnMask(source): return "ipv4" elif checkIP6nMask(source): return "ipv6" elif check_mac(source): return "" elif source.startswith("ipset:"): self._check_ipset_type_for_source(source[6:]) if applied: self._check_ipset_applied(source[6:]) return self._ipset_family(source[6:]) else: raise FirewallError(errors.INVALID_ADDR, source) def __source_id(self, source, applied=False): ipv = self.check_source(source, applied=applied) return (ipv, source) def add_source(self, zone, source, sender=None, use_transaction=None, allow_apply=True): self._fw.check_panic() _zone = self._fw.check_zone(zone) _obj = self._zones[_zone] if check_mac(source): source = source.upper() source_id = self.__source_id(source, applied=allow_apply) if source_id in _obj.settings["sources"]: raise FirewallError(errors.ZONE_ALREADY_SET, "'%s' already bound to '%s'" % (source, _zone)) if self.get_zone_of_source(source) is not None: raise FirewallError(errors.ZONE_CONFLICT, "'%s' already bound to a zone" % source) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if not _obj.applied and allow_apply: self.apply_zone_settings(zone, use_transaction=transaction) transaction.add_fail(self.set_zone_applied, _zone, False) if allow_apply: self._source(True, _zone, source_id[0], source_id[1], transaction) self.__register_source(_obj, source_id, zone, sender) transaction.add_fail(self.__unregister_source, _obj, source_id) if use_transaction is None: transaction.execute(True) return _zone def __register_source(self, _obj, source_id, zone, sender): _obj.settings["sources"][source_id] = \ self.__gen_settings(0, sender) # add information whether we add to default or specific zone _obj.settings["sources"][source_id]["__default__"] = (not zone or zone == "") def change_zone_of_source(self, zone, source, sender=None): self._fw.check_panic() _old_zone = self.get_zone_of_source(source) _new_zone = self._fw.check_zone(zone) if _new_zone == _old_zone: return _old_zone if check_mac(source): source = source.upper() if _old_zone is not None: self.remove_source(_old_zone, source) _zone = self.add_source(zone, source, sender) return _zone def remove_source(self, zone, source, use_transaction=None): self._fw.check_panic() if check_mac(source): source = source.upper() zos = self.get_zone_of_source(source) if zos is None: raise FirewallError(errors.UNKNOWN_SOURCE, "'%s' is not in any zone" % source) _zone = zos if zone == "" else self._fw.check_zone(zone) if zos != _zone: raise FirewallError(errors.ZONE_CONFLICT, "remove_source(%s, %s): zos='%s'" % \ (zone, source, zos)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction _obj = self._zones[_zone] source_id = self.__source_id(source) transaction.add_post(self.__unregister_source, _obj, source_id) self._source(False, _zone, source_id[0], source_id[1], transaction) if use_transaction is None: transaction.execute(True) return _zone def __unregister_source(self, _obj, source_id): if source_id in _obj.settings["sources"]: del _obj.settings["sources"][source_id] def query_source(self, zone, source): if check_mac(source): source = source.upper() return self.__source_id(source) in self.get_settings(zone)["sources"] def list_sources(self, zone): return [ k[1] for k in self.get_settings(zone)["sources"].keys() ] def _interface(self, enable, zone, interface, transaction, append=False): for backend in self._fw.enabled_backends(): if not backend.policies_supported: continue for policy in self._zone_policies[zone]: for (table, chain) in self._fw.policy._get_table_chains_for_zone_dispatch(policy): rules = backend.build_zone_source_interface_rules(enable, zone, policy, interface, table, chain, append) transaction.add_rules(backend, rules) # intra zone forward policy = self.policy_name_from_zones(zone, "ANY") # Skip adding wildcard/catch-all interface (for default # zone). Otherwise it would allow forwarding from interface # in default zone -> interface not in default zone (but in # a different zone). if self.get_settings(zone)["forward"] and interface not in ["+", "*"]: rules = backend.build_zone_forward_rules(enable, zone, policy, "filter", interface=interface) transaction.add_rules(backend, rules) # update policy dispatch for any policy using this zone in ingress # or egress for policy in self._fw.policy.get_policies_not_derived_from_zone(): if zone not in self._fw.policy.list_ingress_zones(policy) and \ zone not in self._fw.policy.list_egress_zones(policy): continue if policy in self._fw.policy.get_active_policies_not_derived_from_zone() and self._fw.policy.get_policy(policy).applied: # first remove the old set of interfaces using the current zone # settings. if not enable and len(self.list_interfaces(zone)) == 1: self._fw.policy.unapply_policy_settings(policy, use_transaction=transaction) else: self._fw.policy._ingress_egress_zones(False, policy, transaction) # after the transaction ends and therefore the interface # has been added to the zone's settings, update the # dependent policies transaction.add_post(lambda p: (p in self._fw.policy.get_active_policies_not_derived_from_zone()) and \ self._fw.policy._ingress_egress_zones_transaction(True, p), policy) elif enable: transaction.add_post(lambda p: (p in self._fw.policy.get_active_policies_not_derived_from_zone()) and \ self._fw.policy.apply_policy_settings(p), policy) # IPSETS def _ipset_family(self, name): if self._ipset_type(name) == "hash:mac": return None return self._fw.ipset.get_family(name, applied=False) def _ipset_type(self, name): return self._fw.ipset.get_type(name, applied=False) def _ipset_match_flags(self, name, flag): return ",".join([flag] * self._fw.ipset.get_dimension(name)) def _check_ipset_applied(self, name): return self._fw.ipset.check_applied(name) def _check_ipset_type_for_source(self, name): _type = self._ipset_type(name) if _type not in SOURCE_IPSET_TYPES: raise FirewallError( errors.INVALID_IPSET, "ipset '%s' with type '%s' not usable as source" % \ (name, _type)) def _source(self, enable, zone, ipv, source, transaction): # For mac source bindings ipv is an empty string, the mac source will # be added for ipv4 and ipv6 for backend in [self._fw.get_backend_by_ipv(ipv)] if ipv else self._fw.enabled_backends(): if not backend.policies_supported: continue for policy in self._zone_policies[zone]: for (table, chain) in self._fw.policy._get_table_chains_for_zone_dispatch(policy): rules = backend.build_zone_source_address_rules(enable, zone, policy, source, table, chain) transaction.add_rules(backend, rules) # intra zone forward policy = self.policy_name_from_zones(zone, "ANY") if self.get_settings(zone)["forward"]: rules = backend.build_zone_forward_rules(enable, zone, policy, "filter", source=source) transaction.add_rules(backend, rules) # update policy dispatch for any policy using this zone in ingress # or egress for policy in self._fw.policy.get_policies_not_derived_from_zone(): if zone not in self._fw.policy.list_ingress_zones(policy) and \ zone not in self._fw.policy.list_egress_zones(policy): continue if policy in self._fw.policy.get_active_policies_not_derived_from_zone() and self._fw.policy.get_policy(policy).applied: # first remove the old set of sources using the current zone # settings. if not enable and len(self.list_sources(zone)) == 1: self._fw.policy.unapply_policy_settings(policy, use_transaction=transaction) else: self._fw.policy._ingress_egress_zones(False, policy, transaction) # after the transaction ends and therefore the sources # has been added to the zone's settings, update the # dependent policies transaction.add_post(lambda p: (p in self._fw.policy.get_active_policies_not_derived_from_zone()) and \ self._fw.policy._ingress_egress_zones_transaction(True, p), policy) elif enable: transaction.add_post(lambda p: (p in self._fw.policy.get_active_policies_not_derived_from_zone()) and \ self._fw.policy.apply_policy_settings(p), policy) def add_service(self, zone, service, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_service(p_name, service, timeout, sender) return zone def remove_service(self, zone, service): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_service(p_name, service) return zone def query_service(self, zone, service): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.query_service(p_name, service) def list_services(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.list_services(p_name) def add_port(self, zone, port, protocol, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_port(p_name, port, protocol, timeout, sender) return zone def remove_port(self, zone, port, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_port(p_name, port, protocol) return zone def query_port(self, zone, port, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.query_port(p_name, port, protocol) def list_ports(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.list_ports(p_name) def add_source_port(self, zone, source_port, protocol, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_source_port(p_name, source_port, protocol, timeout, sender) return zone def remove_source_port(self, zone, source_port, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_source_port(p_name, source_port, protocol) return zone def query_source_port(self, zone, source_port, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.query_source_port(p_name, source_port, protocol) def list_source_ports(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.list_source_ports(p_name) def _rich_rule_to_policies(self, zone, rule): zone = self._fw.check_zone(zone) if type(rule.action) == Rich_Mark: return [self.policy_name_from_zones(zone, "ANY")] elif type(rule.element) in [Rich_Service, Rich_Port, Rich_Protocol, Rich_SourcePort]: return [self.policy_name_from_zones(zone, "HOST")] elif type(rule.element) in [Rich_IcmpBlock, Rich_IcmpType]: return [self.policy_name_from_zones(zone, "HOST"), self.policy_name_from_zones(zone, "ANY")] elif type(rule.element) in [Rich_ForwardPort]: return [self.policy_name_from_zones(zone, "ANY")] elif type(rule.element) in [Rich_Masquerade]: return [self.policy_name_from_zones("ANY", zone)] elif rule.element is None: return [self.policy_name_from_zones(zone, "HOST")] else: raise FirewallError("Rich rule type (%s) not handled." % (type(rule.element))) def add_rule(self, zone, rule, timeout=0, sender=None): for p_name in self._rich_rule_to_policies(zone, rule): self._fw.policy.add_rule(p_name, rule, timeout, sender) return zone def remove_rule(self, zone, rule): for p_name in self._rich_rule_to_policies(zone, rule): self._fw.policy.remove_rule(p_name, rule) return zone def query_rule(self, zone, rule): ret = True for p_name in self._rich_rule_to_policies(zone, rule): ret = ret and self._fw.policy.query_rule(p_name, rule) return ret def list_rules(self, zone): zone = self._fw.check_zone(zone) ret = set() for p_name in [self.policy_name_from_zones(zone, "ANY"), self.policy_name_from_zones(zone, "HOST"), self.policy_name_from_zones("ANY", zone)]: ret.update(set(self._fw.policy.list_rules(p_name))) return list(ret) def add_protocol(self, zone, protocol, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_protocol(p_name, protocol, timeout, sender) return zone def remove_protocol(self, zone, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_protocol(p_name, protocol) return zone def query_protocol(self, zone, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.query_protocol(p_name, protocol) def list_protocols(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.list_protocols(p_name) def add_masquerade(self, zone, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones("ANY", zone) self._fw.policy.add_masquerade(p_name, timeout, sender) return zone def remove_masquerade(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones("ANY", zone) self._fw.policy.remove_masquerade(p_name) return zone def query_masquerade(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones("ANY", zone) return self._fw.policy.query_masquerade(p_name) def add_forward_port(self, zone, port, protocol, toport=None, toaddr=None, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.add_forward_port(p_name, port, protocol, toport, toaddr, timeout, sender) return zone def remove_forward_port(self, zone, port, protocol, toport=None, toaddr=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.remove_forward_port(p_name, port, protocol, toport, toaddr) return zone def query_forward_port(self, zone, port, protocol, toport=None, toaddr=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "ANY") return self._fw.policy.query_forward_port(p_name, port, protocol, toport, toaddr) def list_forward_ports(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "ANY") return self._fw.policy.list_forward_ports(p_name) def add_icmp_block(self, zone, icmp, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_icmp_block(p_name, icmp, timeout, sender) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.add_icmp_block(p_name, icmp, timeout, sender) return zone def remove_icmp_block(self, zone, icmp): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_icmp_block(p_name, icmp) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.remove_icmp_block(p_name, icmp) return zone def query_icmp_block(self, zone, icmp): zone = self._fw.check_zone(zone) p_name_host = self.policy_name_from_zones(zone, "HOST") p_name_fwd = self.policy_name_from_zones(zone, "ANY") return self._fw.policy.query_icmp_block(p_name_host, icmp) and \ self._fw.policy.query_icmp_block(p_name_fwd, icmp) def list_icmp_blocks(self, zone): zone = self._fw.check_zone(zone) p_name_host = self.policy_name_from_zones(zone, "HOST") p_name_fwd = self.policy_name_from_zones(zone, "ANY") return sorted(set(self._fw.policy.list_icmp_blocks(p_name_host) + self._fw.policy.list_icmp_blocks(p_name_fwd))) def add_icmp_block_inversion(self, zone, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_icmp_block_inversion(p_name, sender) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.add_icmp_block_inversion(p_name, sender) return zone def _icmp_block_inversion(self, enable, zone, transaction): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy._icmp_block_inversion(enable, p_name, transaction) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy._icmp_block_inversion(enable, p_name, transaction) def remove_icmp_block_inversion(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_icmp_block_inversion(p_name) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.remove_icmp_block_inversion(p_name) return zone def query_icmp_block_inversion(self, zone): zone = self._fw.check_zone(zone) p_name_host = self.policy_name_from_zones(zone, "HOST") p_name_fwd = self.policy_name_from_zones(zone, "ANY") return self._fw.policy.query_icmp_block_inversion(p_name_host) and \ self._fw.policy.query_icmp_block_inversion(p_name_fwd) def _forward(self, enable, zone, transaction): p_name = self.policy_name_from_zones(zone, "ANY") for interface in self._zones[zone].settings["interfaces"]: for backend in self._fw.enabled_backends(): if not backend.policies_supported: continue rules = backend.build_zone_forward_rules(enable, zone, p_name, "filter", interface=interface) transaction.add_rules(backend, rules) for ipv,source in self._zones[zone].settings["sources"]: for backend in [self._fw.get_backend_by_ipv(ipv)] if ipv else self._fw.enabled_backends(): if not backend.policies_supported: continue rules = backend.build_zone_forward_rules(enable, zone, p_name, "filter", source=source) transaction.add_rules(backend, rules) def __forward_id(self): return True def add_forward(self, zone, timeout=0, sender=None, use_transaction=None): _zone = self._fw.check_zone(zone) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._zones[_zone] forward_id = self.__forward_id() if forward_id in _obj.settings["forward"]: raise FirewallError(errors.ALREADY_ENABLED, "forward already enabled in '%s'" % _zone) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._forward(True, _zone, transaction) self.__register_forward(_obj, forward_id, timeout, sender) transaction.add_fail(self.__unregister_forward, _obj, forward_id) if use_transaction is None: transaction.execute(True) return _zone def __register_forward(self, _obj, forward_id, timeout, sender): _obj.settings["forward"][forward_id] = \ self.__gen_settings(timeout, sender) def remove_forward(self, zone, use_transaction=None): _zone = self._fw.check_zone(zone) self._fw.check_panic() _obj = self._zones[_zone] forward_id = self.__forward_id() if forward_id not in _obj.settings["forward"]: raise FirewallError(errors.NOT_ENABLED, "forward not enabled in '%s'" % _zone) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._forward(False, _zone, transaction) transaction.add_post(self.__unregister_forward, _obj, forward_id) if use_transaction is None: transaction.execute(True) return _zone def __unregister_forward(self, _obj, forward_id): if forward_id in _obj.settings["forward"]: del _obj.settings["forward"][forward_id] def query_forward(self, zone): return self.__forward_id() in self.get_settings(zone)["forward"] PK!iI%% ipXtables.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2010-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os.path import copy from firewall.core.prog import runProg from firewall.core.logger import log from firewall.functions import tempFile, readfile, splitArgs, check_mac, portStr, \ check_single_address, check_address, normalizeIP6 from firewall import config from firewall.errors import FirewallError, INVALID_PASSTHROUGH, INVALID_RULE, UNKNOWN_ERROR, INVALID_ADDR, \ INVALID_ICMPTYPE from firewall.core.rich import Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark, \ Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock from firewall.core.icmp import ICMP_TYPES, ICMPV6_TYPES import string POLICY_CHAIN_PREFIX = "" BUILT_IN_CHAINS = { "security": [ "INPUT", "OUTPUT", "FORWARD" ], "raw": [ "PREROUTING", "OUTPUT" ], "mangle": [ "PREROUTING", "POSTROUTING", "INPUT", "OUTPUT", "FORWARD" ], "nat": [ "PREROUTING", "POSTROUTING", "OUTPUT" ], "filter": [ "INPUT", "OUTPUT", "FORWARD" ], } DEFAULT_REJECT_TYPE = { "ipv4": "icmp-host-prohibited", "ipv6": "icmp6-adm-prohibited", } ICMP = { "ipv4": "icmp", "ipv6": "ipv6-icmp", } # ipv ebtables also uses this # def common_reverse_rule(args): """ Inverse valid rule """ replace_args = { # Append "-A": "-D", "--append": "--delete", # Insert "-I": "-D", "--insert": "--delete", # New chain "-N": "-X", "--new-chain": "--delete-chain", } ret_args = args[:] for arg in replace_args: try: idx = ret_args.index(arg) except Exception: continue if arg in [ "-I", "--insert" ]: # With insert rulenum, then remove it if it is a number # Opt at position idx, chain at position idx+1, [rulenum] at # position idx+2 try: int(ret_args[idx+2]) except Exception: pass else: ret_args.pop(idx+2) ret_args[idx] = replace_args[arg] return ret_args def common_reverse_passthrough(args): """ Reverse valid passthough rule """ replace_args = { # Append "-A": "-D", "--append": "--delete", # Insert "-I": "-D", "--insert": "--delete", # New chain "-N": "-X", "--new-chain": "--delete-chain", } ret_args = args[:] for x in replace_args: try: idx = ret_args.index(x) except ValueError: continue if x in [ "-I", "--insert" ]: # With insert rulenum, then remove it if it is a number # Opt at position idx, chain at position idx+1, [rulenum] at # position idx+2 try: int(ret_args[idx+2]) except ValueError: pass else: ret_args.pop(idx+2) ret_args[idx] = replace_args[x] return ret_args raise FirewallError(INVALID_PASSTHROUGH, "no '-A', '-I' or '-N' arg") # ipv ebtables also uses this # def common_check_passthrough(args): """ Check if passthough rule is valid (only add, insert and new chain rules are allowed) """ args = set(args) not_allowed = set(["-C", "--check", # check rule "-D", "--delete", # delete rule "-R", "--replace", # replace rule "-L", "--list", # list rule "-S", "--list-rules", # print rules "-F", "--flush", # flush rules "-Z", "--zero", # zero rules "-X", "--delete-chain", # delete chain "-P", "--policy", # policy "-E", "--rename-chain"]) # rename chain) # intersection of args and not_allowed is not empty, i.e. # something from args is not allowed if len(args & not_allowed) > 0: raise FirewallError(INVALID_PASSTHROUGH, "arg '%s' is not allowed" % list(args & not_allowed)[0]) # args need to contain one of -A, -I, -N needed = set(["-A", "--append", "-I", "--insert", "-N", "--new-chain"]) # empty intersection of args and needed, i.e. # none from args contains any needed command if len(args & needed) == 0: raise FirewallError(INVALID_PASSTHROUGH, "no '-A', '-I' or '-N' arg") class ip4tables(object): ipv = "ipv4" name = "ip4tables" policies_supported = True def __init__(self, fw): self._fw = fw self._command = config.COMMANDS[self.ipv] self._restore_command = config.COMMANDS["%s-restore" % self.ipv] self.wait_option = self._detect_wait_option() self.restore_wait_option = self._detect_restore_wait_option() self.fill_exists() self.available_tables = [] self.rich_rule_priority_counts = {} self.policy_priority_counts = {} self.zone_source_index_cache = [] self.our_chains = {} # chains created by firewalld def fill_exists(self): self.command_exists = os.path.exists(self._command) self.restore_command_exists = os.path.exists(self._restore_command) def __run(self, args): # convert to string list if self.wait_option and self.wait_option not in args: _args = [self.wait_option] + ["%s" % item for item in args] else: _args = ["%s" % item for item in args] log.debug2("%s: %s %s", self.__class__, self._command, " ".join(_args)) (status, ret) = runProg(self._command, _args) if status != 0: raise ValueError("'%s %s' failed: %s" % (self._command, " ".join(_args), ret)) return ret def _rule_replace(self, rule, pattern, replacement): try: i = rule.index(pattern) except ValueError: return False else: rule[i:i+1] = replacement return True def is_chain_builtin(self, ipv, table, chain): return table in BUILT_IN_CHAINS and \ chain in BUILT_IN_CHAINS[table] def build_chain_rules(self, add, table, chain): rule = [ "-t", table ] if add: rule.append("-N") else: rule.append("-X") rule.append(chain) return [rule] def build_rule(self, add, table, chain, index, args): rule = [ "-t", table ] if add: rule += [ "-I", chain, str(index) ] else: rule += [ "-D", chain ] rule += args return rule def reverse_rule(self, args): return common_reverse_rule(args) def check_passthrough(self, args): common_check_passthrough(args) def reverse_passthrough(self, args): return common_reverse_passthrough(args) def passthrough_parse_table_chain(self, args): table = "filter" try: i = args.index("-t") except ValueError: pass else: if len(args) >= i+1: table = args[i+1] chain = None for opt in [ "-A", "--append", "-I", "--insert", "-N", "--new-chain" ]: try: i = args.index(opt) except ValueError: pass else: if len(args) >= i+1: chain = args[i+1] return (table, chain) def _run_replace_zone_source(self, rule, zone_source_index_cache): try: i = rule.index("%%ZONE_SOURCE%%") rule.pop(i) zone = rule.pop(i) if "-m" == rule[4]: # ipset/mac zone_source = (zone, rule[7]) # (zone, address) else: zone_source = (zone, rule[5]) # (zone, address) except ValueError: try: i = rule.index("%%ZONE_INTERFACE%%") rule.pop(i) zone_source = None except ValueError: return rule_add = True if rule[0] in ["-D", "--delete"]: rule_add = False if zone_source and not rule_add: if zone_source in zone_source_index_cache: zone_source_index_cache.remove(zone_source) elif rule_add: if zone_source: # order source based dispatch by zone name if zone_source not in zone_source_index_cache: zone_source_index_cache.append(zone_source) zone_source_index_cache.sort(key=lambda x: x[0]) index = zone_source_index_cache.index(zone_source) else: if self._fw._allow_zone_drifting: index = 0 else: index = len(zone_source_index_cache) rule[0] = "-I" rule.insert(2, "%d" % (index + 1)) def _set_rule_replace_priority(self, rule, priority_counts, token): """ Change something like -t filter -I public_IN %%RICH_RULE_PRIORITY%% 123 or -t filter -A public_IN %%RICH_RULE_PRIORITY%% 321 into -t filter -I public_IN 4 or -t filter -I public_IN """ try: i = rule.index(token) except ValueError: pass else: rule_add = True insert = False insert_add_index = -1 rule.pop(i) priority = rule.pop(i) if type(priority) != int: raise FirewallError(INVALID_RULE, "priority must be followed by a number") table = "filter" for opt in [ "-t", "--table" ]: try: j = rule.index(opt) except ValueError: pass else: if len(rule) >= j+1: table = rule[j+1] for opt in [ "-A", "--append", "-I", "--insert", "-D", "--delete" ]: try: insert_add_index = rule.index(opt) except ValueError: pass else: if len(rule) >= insert_add_index+1: chain = rule[insert_add_index+1] if opt in [ "-I", "--insert" ]: insert = True if opt in [ "-D", "--delete" ]: rule_add = False chain = (table, chain) # Add the rule to the priority counts. We don't need to store the # rule, just bump the ref count for the priority value. if not rule_add: if chain not in priority_counts or \ priority not in priority_counts[chain] or \ priority_counts[chain][priority] <= 0: raise FirewallError(UNKNOWN_ERROR, "nonexistent or underflow of priority count") priority_counts[chain][priority] -= 1 else: if chain not in priority_counts: priority_counts[chain] = {} if priority not in priority_counts[chain]: priority_counts[chain][priority] = 0 # calculate index of new rule index = 1 for p in sorted(priority_counts[chain].keys()): if p == priority and insert: break index += priority_counts[chain][p] if p == priority: break priority_counts[chain][priority] += 1 rule[insert_add_index] = "-I" rule.insert(insert_add_index+2, "%d" % index) def set_rules(self, rules, log_denied): temp_file = tempFile() table_rules = { } rich_rule_priority_counts = copy.deepcopy(self.rich_rule_priority_counts) policy_priority_counts = copy.deepcopy(self.policy_priority_counts) zone_source_index_cache = copy.deepcopy(self.zone_source_index_cache) for _rule in rules: rule = _rule[:] # replace %%REJECT%% self._rule_replace(rule, "%%REJECT%%", \ ["REJECT", "--reject-with", DEFAULT_REJECT_TYPE[self.ipv]]) # replace %%ICMP%% self._rule_replace(rule, "%%ICMP%%", [ICMP[self.ipv]]) # replace %%LOGTYPE%% try: i = rule.index("%%LOGTYPE%%") except ValueError: pass else: if log_denied == "off": continue if log_denied in [ "unicast", "broadcast", "multicast" ]: rule[i:i+1] = [ "-m", "pkttype", "--pkt-type", log_denied ] else: rule.pop(i) self._set_rule_replace_priority(rule, rich_rule_priority_counts, "%%RICH_RULE_PRIORITY%%") self._set_rule_replace_priority(rule, policy_priority_counts, "%%POLICY_PRIORITY%%") self._run_replace_zone_source(rule, zone_source_index_cache) table = "filter" # get table form rule for opt in [ "-t", "--table" ]: try: i = rule.index(opt) except ValueError: pass else: if len(rule) >= i+1: rule.pop(i) table = rule.pop(i) # we can not use joinArgs here, because it would use "'" instead # of '"' for the start and end of the string, this breaks # iptables-restore for i in range(len(rule)): for c in string.whitespace: if c in rule[i] and not (rule[i].startswith('"') and rule[i].endswith('"')): rule[i] = '"%s"' % rule[i] table_rules.setdefault(table, []).append(rule) for table in table_rules: rules = table_rules[table] temp_file.write("*%s\n" % table) for rule in rules: temp_file.write(" ".join(rule) + "\n") temp_file.write("COMMIT\n") temp_file.close() stat = os.stat(temp_file.name) log.debug2("%s: %s %s", self.__class__, self._restore_command, "%s: %d" % (temp_file.name, stat.st_size)) args = [ ] if self.restore_wait_option: args.append(self.restore_wait_option) args.append("-n") (status, ret) = runProg(self._restore_command, args, stdin=temp_file.name) if log.getDebugLogLevel() > 2: lines = readfile(temp_file.name) if lines is not None: i = 1 for line in lines: log.debug3("%8d: %s" % (i, line), nofmt=1, nl=0) if not line.endswith("\n"): log.debug3("", nofmt=1) i += 1 os.unlink(temp_file.name) if status != 0: raise ValueError("'%s %s' failed: %s" % (self._restore_command, " ".join(args), ret)) self.rich_rule_priority_counts = rich_rule_priority_counts self.policy_priority_counts = policy_priority_counts self.zone_source_index_cache = zone_source_index_cache def set_rule(self, rule, log_denied): # replace %%REJECT%% self._rule_replace(rule, "%%REJECT%%", \ ["REJECT", "--reject-with", DEFAULT_REJECT_TYPE[self.ipv]]) # replace %%ICMP%% self._rule_replace(rule, "%%ICMP%%", [ICMP[self.ipv]]) # replace %%LOGTYPE%% try: i = rule.index("%%LOGTYPE%%") except ValueError: pass else: if log_denied == "off": return "" if log_denied in [ "unicast", "broadcast", "multicast" ]: rule[i:i+1] = [ "-m", "pkttype", "--pkt-type", log_denied ] else: rule.pop(i) rich_rule_priority_counts = copy.deepcopy(self.rich_rule_priority_counts) policy_priority_counts = copy.deepcopy(self.policy_priority_counts) zone_source_index_cache = copy.deepcopy(self.zone_source_index_cache) self._set_rule_replace_priority(rule, rich_rule_priority_counts, "%%RICH_RULE_PRIORITY%%") self._set_rule_replace_priority(rule, policy_priority_counts, "%%POLICY_PRIORITY%%") self._run_replace_zone_source(rule, zone_source_index_cache) output = self.__run(rule) self.rich_rule_priority_counts = rich_rule_priority_counts self.policy_priority_counts = policy_priority_counts self.zone_source_index_cache = zone_source_index_cache return output def get_available_tables(self, table=None): ret = [] tables = [ table ] if table else BUILT_IN_CHAINS.keys() for table in tables: if table in self.available_tables: ret.append(table) else: try: self.__run(["-t", table, "-L", "-n"]) self.available_tables.append(table) ret.append(table) except ValueError: log.debug1("%s table '%s' does not exist (or not enough permission to check)." % (self.ipv, table)) return ret def _detect_wait_option(self): wait_option = "" ret = runProg(self._command, ["-w", "-L", "-n"]) # since iptables-1.4.20 if ret[0] == 0: wait_option = "-w" # wait for xtables lock ret = runProg(self._command, ["-w10", "-L", "-n"]) # since iptables > 1.4.21 if ret[0] == 0: wait_option = "-w10" # wait max 10 seconds log.debug2("%s: %s will be using %s option.", self.__class__, self._command, wait_option) return wait_option def _detect_restore_wait_option(self): temp_file = tempFile() temp_file.write("#foo") temp_file.close() wait_option = "" for test_option in ["-w", "--wait=2"]: ret = runProg(self._restore_command, [test_option], stdin=temp_file.name) if ret[0] == 0 and "invalid option" not in ret[1] \ and "unrecognized option" not in ret[1]: wait_option = test_option break log.debug2("%s: %s will be using %s option.", self.__class__, self._restore_command, wait_option) os.unlink(temp_file.name) return wait_option def build_flush_rules(self): self.rich_rule_priority_counts = {} self.policy_priority_counts = {} self.zone_source_index_cache = [] rules = [] for table in BUILT_IN_CHAINS.keys(): if not self.get_available_tables(table): continue # Flush firewall rules: -F # Delete firewall chains: -X # Set counter to zero: -Z for flag in [ "-F", "-X", "-Z" ]: rules.append(["-t", table, flag]) return rules def build_set_policy_rules(self, policy): rules = [] _policy = "DROP" if policy == "PANIC" else policy for table in BUILT_IN_CHAINS.keys(): if not self.get_available_tables(table): continue if table == "nat": continue for chain in BUILT_IN_CHAINS[table]: rules.append(["-t", table, "-P", chain, _policy]) return rules def supported_icmp_types(self, ipv=None): supported = set() if ipv is None or self.ipv == "ipv4": supported.update(ICMP_TYPES.keys()) if ipv is None or self.ipv == "ipv6": supported.update(ICMPV6_TYPES.keys()) return list(supported) def build_default_tables(self): # nothing to do, they always exist return [] def build_default_rules(self, log_denied="off"): default_rules = {} if self.get_available_tables("security"): default_rules["security"] = [ ] self.our_chains["security"] = set() for chain in BUILT_IN_CHAINS["security"]: default_rules["security"].append("-N %s_direct" % chain) default_rules["security"].append("-A %s -j %s_direct" % (chain, chain)) self.our_chains["security"].add("%s_direct" % chain) if self.get_available_tables("raw"): default_rules["raw"] = [ ] self.our_chains["raw"] = set() for chain in BUILT_IN_CHAINS["raw"]: default_rules["raw"].append("-N %s_direct" % chain) default_rules["raw"].append("-A %s -j %s_direct" % (chain, chain)) self.our_chains["raw"].add("%s_direct" % chain) if chain == "PREROUTING": for dispatch_suffix in ["POLICIES_pre", "ZONES_SOURCE", "ZONES", "POLICIES_post"] if self._fw._allow_zone_drifting else ["POLICIES_pre", "ZONES", "POLICIES_post"]: default_rules["raw"].append("-N %s_%s" % (chain, dispatch_suffix)) default_rules["raw"].append("-A %s -j %s_%s" % (chain, chain, dispatch_suffix)) self.our_chains["raw"].update(set(["%s_%s" % (chain, dispatch_suffix)])) if self.get_available_tables("mangle"): default_rules["mangle"] = [ ] self.our_chains["mangle"] = set() for chain in BUILT_IN_CHAINS["mangle"]: default_rules["mangle"].append("-N %s_direct" % chain) default_rules["mangle"].append("-A %s -j %s_direct" % (chain, chain)) self.our_chains["mangle"].add("%s_direct" % chain) if chain == "PREROUTING": for dispatch_suffix in ["POLICIES_pre", "ZONES_SOURCE", "ZONES", "POLICIES_post"] if self._fw._allow_zone_drifting else ["POLICIES_pre", "ZONES", "POLICIES_post"]: default_rules["mangle"].append("-N %s_%s" % (chain, dispatch_suffix)) default_rules["mangle"].append("-A %s -j %s_%s" % (chain, chain, dispatch_suffix)) self.our_chains["mangle"].update(set(["%s_%s" % (chain, dispatch_suffix)])) if self.get_available_tables("nat"): default_rules["nat"] = [ ] self.our_chains["nat"] = set() for chain in BUILT_IN_CHAINS["nat"]: default_rules["nat"].append("-N %s_direct" % chain) default_rules["nat"].append("-A %s -j %s_direct" % (chain, chain)) self.our_chains["nat"].add("%s_direct" % chain) if chain in [ "PREROUTING", "POSTROUTING" ]: for dispatch_suffix in ["POLICIES_pre", "ZONES_SOURCE", "ZONES", "POLICIES_post"] if self._fw._allow_zone_drifting else ["POLICIES_pre", "ZONES", "POLICIES_post"]: default_rules["nat"].append("-N %s_%s" % (chain, dispatch_suffix)) default_rules["nat"].append("-A %s -j %s_%s" % (chain, chain, dispatch_suffix)) self.our_chains["nat"].update(set(["%s_%s" % (chain, dispatch_suffix)])) default_rules["filter"] = [] self.our_chains["filter"] = set() default_rules["filter"].append("-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED,DNAT -j ACCEPT") default_rules["filter"].append("-A INPUT -i lo -j ACCEPT") default_rules["filter"].append("-N INPUT_direct") default_rules["filter"].append("-A INPUT -j INPUT_direct") self.our_chains["filter"].update(set("INPUT_direct")) for dispatch_suffix in ["POLICIES_pre", "ZONES_SOURCE", "ZONES", "POLICIES_post"] if self._fw._allow_zone_drifting else ["POLICIES_pre", "ZONES", "POLICIES_post"]: default_rules["filter"].append("-N INPUT_%s" % (dispatch_suffix)) default_rules["filter"].append("-A INPUT -j INPUT_%s" % (dispatch_suffix)) self.our_chains["filter"].update(set("INPUT_%s" % (dispatch_suffix))) if log_denied != "off": default_rules["filter"].append("-A INPUT -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: '") default_rules["filter"].append("-A INPUT -m conntrack --ctstate INVALID -j DROP") if log_denied != "off": default_rules["filter"].append("-A INPUT %%LOGTYPE%% -j LOG --log-prefix 'FINAL_REJECT: '") default_rules["filter"].append("-A INPUT -j %%REJECT%%") default_rules["filter"].append("-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED,DNAT -j ACCEPT") default_rules["filter"].append("-A FORWARD -i lo -j ACCEPT") default_rules["filter"].append("-N FORWARD_direct") default_rules["filter"].append("-A FORWARD -j FORWARD_direct") self.our_chains["filter"].update(set("FORWARD_direct")) for dispatch_suffix in ["POLICIES_pre"]: default_rules["filter"].append("-N FORWARD_%s" % (dispatch_suffix)) default_rules["filter"].append("-A FORWARD -j FORWARD_%s" % (dispatch_suffix)) self.our_chains["filter"].update(set("FORWARD_%s" % (dispatch_suffix))) for direction in ["IN", "OUT"]: for dispatch_suffix in ["ZONES_SOURCE", "ZONES"] if self._fw._allow_zone_drifting else ["ZONES"]: default_rules["filter"].append("-N FORWARD_%s_%s" % (direction, dispatch_suffix)) default_rules["filter"].append("-A FORWARD -j FORWARD_%s_%s" % (direction, dispatch_suffix)) self.our_chains["filter"].update(set("FORWARD_%s_%s" % (direction, dispatch_suffix))) for dispatch_suffix in ["POLICIES_post"]: default_rules["filter"].append("-N FORWARD_%s" % (dispatch_suffix)) default_rules["filter"].append("-A FORWARD -j FORWARD_%s" % (dispatch_suffix)) self.our_chains["filter"].update(set("FORWARD_%s" % (dispatch_suffix))) if log_denied != "off": default_rules["filter"].append("-A FORWARD -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: '") default_rules["filter"].append("-A FORWARD -m conntrack --ctstate INVALID -j DROP") if log_denied != "off": default_rules["filter"].append("-A FORWARD %%LOGTYPE%% -j LOG --log-prefix 'FINAL_REJECT: '") default_rules["filter"].append("-A FORWARD -j %%REJECT%%") default_rules["filter"] += [ "-N OUTPUT_direct", "-A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", "-A OUTPUT -o lo -j ACCEPT", "-A OUTPUT -j OUTPUT_direct", ] self.our_chains["filter"].update(set("OUTPUT_direct")) for dispatch_suffix in ["POLICIES_pre"]: default_rules["filter"].append("-N OUTPUT_%s" % (dispatch_suffix)) default_rules["filter"].append("-A OUTPUT -j OUTPUT_%s" % (dispatch_suffix)) self.our_chains["filter"].update(set("OUTPUT_%s" % (dispatch_suffix))) for dispatch_suffix in ["POLICIES_post"]: default_rules["filter"].append("-N OUTPUT_%s" % (dispatch_suffix)) default_rules["filter"].append("-A OUTPUT -j OUTPUT_%s" % (dispatch_suffix)) self.our_chains["filter"].update(set("OUTPUT_%s" % (dispatch_suffix))) final_default_rules = [] for table in default_rules: if table not in self.get_available_tables(): continue for rule in default_rules[table]: final_default_rules.append(["-t", table] + splitArgs(rule)) return final_default_rules def get_zone_table_chains(self, table): if table == "filter": return { "INPUT", "FORWARD_IN", "FORWARD_OUT" } if table == "mangle": if "mangle" in self.get_available_tables(): return { "PREROUTING" } if table == "nat": if "nat" in self.get_available_tables(): return { "PREROUTING", "POSTROUTING" } if table == "raw": if "raw" in self.get_available_tables(): return { "PREROUTING" } return {} def build_policy_ingress_egress_rules(self, enable, policy, table, chain, ingress_interfaces, egress_interfaces, ingress_sources, egress_sources): p_obj = self._fw.policy.get_policy(policy) chain_suffix = "pre" if p_obj.priority < 0 else "post" isSNAT = True if (table == "nat" and chain == "POSTROUTING") else False _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX, isSNAT) ingress_fragments = [] egress_fragments = [] for interface in ingress_interfaces: ingress_fragments.append(["-i", interface]) for interface in egress_interfaces: egress_fragments.append(["-o", interface]) for addr in ingress_sources: ipv = self._fw.zone.check_source(addr) if ipv in ["ipv4", "ipv6"] and not self.is_ipv_supported(ipv): continue ingress_fragments.append(self._rule_addr_fragment("-s", addr)) for addr in egress_sources: ipv = self._fw.zone.check_source(addr) if ipv in ["ipv4", "ipv6"] and not self.is_ipv_supported(ipv): continue # iptables can not match destination MAC if check_mac(addr) and chain in ["POSTROUTING", "FORWARD_OUT", "OUTPUT"]: continue egress_fragments.append(self._rule_addr_fragment("-d", addr)) def _generate_policy_dispatch_rule(ingress_fragment, egress_fragment): add_del = {True: "-A", False: "-D" }[enable] rule = ["-t", table, add_del, "%s_POLICIES_%s" % (chain, chain_suffix), "%%POLICY_PRIORITY%%", p_obj.priority] if ingress_fragment: rule.extend(ingress_fragment) if egress_fragment: rule.extend(egress_fragment) rule.extend(["-j", _policy]) return rule rules = [] if ingress_fragments: # zone --> [zone, ANY, HOST] for ingress_fragment in ingress_fragments: # zone --> zone if egress_fragments: for egress_fragment in egress_fragments: rules.append(_generate_policy_dispatch_rule(ingress_fragment, egress_fragment)) elif egress_sources: # if the egress source is not for the current family (there # are no egress fragments), then avoid creating an invalid # catch all rule. pass else: rules.append(_generate_policy_dispatch_rule(ingress_fragment, None)) elif ingress_sources: # if the ingress source is not for the current family (there are no # ingress fragments), then avoid creating an invalid catch all # rule. pass else: # [ANY, HOST] --> [zone, ANY, HOST] # [ANY, HOST] --> zone if egress_fragments: for egress_fragment in egress_fragments: rules.append(_generate_policy_dispatch_rule(None, egress_fragment)) elif egress_sources: # if the egress source is not for the current family (there # are no egress fragments), then avoid creating an invalid # catch all rule. pass else: # [ANY, HOST] --> [ANY, HOST] rules.append(_generate_policy_dispatch_rule(None, None)) return rules def build_zone_source_interface_rules(self, enable, zone, policy, interface, table, chain, append=False): isSNAT = True if (table == "nat" and chain == "POSTROUTING") else False _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX, isSNAT=isSNAT) opt = { "PREROUTING": "-i", "POSTROUTING": "-o", "INPUT": "-i", "FORWARD_IN": "-i", "FORWARD_OUT": "-o", "OUTPUT": "-o", }[chain] action = "-g" if enable and not append: rule = [ "-I", "%s_ZONES" % chain, "%%ZONE_INTERFACE%%" ] elif enable: rule = [ "-A", "%s_ZONES" % chain ] else: rule = [ "-D", "%s_ZONES" % chain ] if not append: rule += ["%%ZONE_INTERFACE%%"] rule += [ "-t", table, opt, interface, action, _policy ] return [rule] def _rule_addr_fragment(self, opt, address, invert=False): if address.startswith("ipset:"): name = address[6:] if opt == "-d": opt = "dst" else: opt = "src" flags = ",".join([opt] * self._fw.ipset.get_dimension(name)) return ["-m", "set", "--match-set", name, flags] elif check_mac(address): # outgoing can not be set if opt == "-d": raise FirewallError(INVALID_ADDR, "Can't match a destination MAC.") return ["-m", "mac", "--mac-source", address.upper()] else: if check_single_address("ipv6", address): address = normalizeIP6(address) elif check_address("ipv6", address): addr_split = address.split("/") address = normalizeIP6(addr_split[0]) + "/" + addr_split[1] return [opt, address] def build_zone_source_address_rules(self, enable, zone, policy, address, table, chain): add_del = { True: "-I", False: "-D" }[enable] isSNAT = True if (table == "nat" and chain == "POSTROUTING") else False _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX, isSNAT=isSNAT) opt = { "PREROUTING": "-s", "POSTROUTING": "-d", "INPUT": "-s", "FORWARD_IN": "-s", "FORWARD_OUT": "-d", "OUTPUT": "-d", }[chain] if self._fw._allow_zone_drifting: zone_dispatch_chain = "%s_ZONES_SOURCE" % (chain) else: zone_dispatch_chain = "%s_ZONES" % (chain) # iptables can not match destination MAC if check_mac(address) and chain in ["POSTROUTING", "FORWARD_OUT", "OUTPUT"]: return [] rule = [add_del, zone_dispatch_chain, "%%ZONE_SOURCE%%", zone, "-t", table] rule.extend(self._rule_addr_fragment(opt, address)) rule.extend(["-g", _policy]) return [rule] def build_policy_chain_rules(self, enable, policy, table, chain): add_del_chain = { True: "-N", False: "-X" }[enable] add_del_rule = { True: "-A", False: "-D" }[enable] isSNAT = True if (table == "nat" and chain == "POSTROUTING") else False _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX, isSNAT=isSNAT) self.our_chains[table].update(set([_policy, "%s_log" % _policy, "%s_deny" % _policy, "%s_pre" % _policy, "%s_post" % _policy, "%s_allow" % _policy])) rules = [] rules.append([ add_del_chain, _policy, "-t", table ]) rules.append([ add_del_chain, "%s_pre" % _policy, "-t", table ]) rules.append([ add_del_chain, "%s_log" % _policy, "-t", table ]) rules.append([ add_del_chain, "%s_deny" % _policy, "-t", table ]) rules.append([ add_del_chain, "%s_allow" % _policy, "-t", table ]) rules.append([ add_del_chain, "%s_post" % _policy, "-t", table ]) rules.append([ add_del_rule, _policy, "-t", table, "-j", "%s_pre" % _policy ]) rules.append([ add_del_rule, _policy, "-t", table, "-j", "%s_log" % _policy ]) rules.append([ add_del_rule, _policy, "-t", table, "-j", "%s_deny" % _policy ]) rules.append([ add_del_rule, _policy, "-t", table, "-j", "%s_allow" % _policy ]) rules.append([ add_del_rule, _policy, "-t", table, "-j", "%s_post" % _policy ]) target = self._fw.policy._policies[policy].target if self._fw.get_log_denied() != "off": if table == "filter": if target in [ "REJECT", "%%REJECT%%" ]: rules.append([ add_del_rule, _policy, "-t", table, "%%LOGTYPE%%", "-j", "LOG", "--log-prefix", "\"%s_REJECT: \"" % _policy ]) if target == "DROP": rules.append([ add_del_rule, _policy, "-t", table, "%%LOGTYPE%%", "-j", "LOG", "--log-prefix", "\"%s_DROP: \"" % _policy ]) if table == "filter" and \ target in [ "ACCEPT", "REJECT", "%%REJECT%%", "DROP" ]: rules.append([ add_del_rule, _policy, "-t", table, "-j", target ]) if not enable: rules.reverse() return rules def _rule_limit(self, limit): if not limit: return [] s = ["-m", "limit", "--limit", limit.value] if limit.burst is not None: s += ["--limit-burst", limit.burst] return s def _rich_rule_chain_suffix(self, rich_rule): if type(rich_rule.element) in [Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock]: # These are special and don't have an explicit action pass elif rich_rule.action: if type(rich_rule.action) not in [Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark]: raise FirewallError(INVALID_RULE, "Unknown action %s" % type(rich_rule.action)) else: raise FirewallError(INVALID_RULE, "No rule action specified.") if rich_rule.priority == 0: if type(rich_rule.element) in [Rich_Masquerade, Rich_ForwardPort] or \ type(rich_rule.action) in [Rich_Accept, Rich_Mark]: return "allow" elif type(rich_rule.element) in [Rich_IcmpBlock] or \ type(rich_rule.action) in [Rich_Reject, Rich_Drop]: return "deny" elif rich_rule.priority < 0: return "pre" else: return "post" def _rich_rule_chain_suffix_from_log(self, rich_rule): if not rich_rule.log and not rich_rule.audit: raise FirewallError(INVALID_RULE, "Not log or audit") if rich_rule.priority == 0: return "log" elif rich_rule.priority < 0: return "pre" else: return "post" def _rich_rule_priority_fragment(self, rich_rule): if rich_rule.priority == 0: return [] return ["%%RICH_RULE_PRIORITY%%", rich_rule.priority] def _rich_rule_log(self, policy, rich_rule, enable, table, rule_fragment): if not rich_rule.log: return [] _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "-A", False: "-D" }[enable] chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule) rule = ["-t", table, add_del, "%s_%s" % (_policy, chain_suffix)] rule += self._rich_rule_priority_fragment(rich_rule) rule += rule_fragment + [ "-j", "LOG" ] if rich_rule.log.prefix: rule += [ "--log-prefix", "'%s'" % rich_rule.log.prefix ] if rich_rule.log.level: rule += [ "--log-level", "%s" % rich_rule.log.level ] rule += self._rule_limit(rich_rule.log.limit) return rule def _rich_rule_audit(self, policy, rich_rule, enable, table, rule_fragment): if not rich_rule.audit: return [] add_del = { True: "-A", False: "-D" }[enable] _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule) rule = ["-t", table, add_del, "%s_%s" % (_policy, chain_suffix)] rule += self._rich_rule_priority_fragment(rich_rule) rule += rule_fragment if type(rich_rule.action) == Rich_Accept: _type = "accept" elif type(rich_rule.action) == Rich_Reject: _type = "reject" elif type(rich_rule.action) == Rich_Drop: _type = "drop" else: _type = "unknown" rule += [ "-j", "AUDIT", "--type", _type ] rule += self._rule_limit(rich_rule.audit.limit) return rule def _rich_rule_action(self, policy, rich_rule, enable, table, rule_fragment): if not rich_rule.action: return [] add_del = { True: "-A", False: "-D" }[enable] _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) chain_suffix = self._rich_rule_chain_suffix(rich_rule) chain = "%s_%s" % (_policy, chain_suffix) if type(rich_rule.action) == Rich_Accept: rule_action = [ "-j", "ACCEPT" ] elif type(rich_rule.action) == Rich_Reject: rule_action = [ "-j", "REJECT" ] if rich_rule.action.type: rule_action += [ "--reject-with", rich_rule.action.type ] elif type(rich_rule.action) == Rich_Drop: rule_action = [ "-j", "DROP" ] elif type(rich_rule.action) == Rich_Mark: table = "mangle" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) chain = "%s_%s" % (_policy, chain_suffix) rule_action = [ "-j", "MARK", "--set-xmark", rich_rule.action.set ] else: raise FirewallError(INVALID_RULE, "Unknown action %s" % type(rich_rule.action)) rule = ["-t", table, add_del, chain] rule += self._rich_rule_priority_fragment(rich_rule) rule += rule_fragment + rule_action rule += self._rule_limit(rich_rule.action.limit) return rule def _rich_rule_destination_fragment(self, rich_dest): if not rich_dest: return [] rule_fragment = [] if rich_dest.addr: if rich_dest.invert: rule_fragment.append("!") if check_single_address("ipv6", rich_dest.addr): rule_fragment += [ "-d", normalizeIP6(rich_dest.addr) ] elif check_address("ipv6", rich_dest.addr): addr_split = rich_dest.addr.split("/") rule_fragment += [ "-d", normalizeIP6(addr_split[0]) + "/" + addr_split[1] ] else: rule_fragment += [ "-d", rich_dest.addr ] elif rich_dest.ipset: rule_fragment += [ "-m", "set" ] if rich_dest.invert: rule_fragment.append("!") flags = self._fw.zone._ipset_match_flags(rich_dest.ipset, "dst") rule_fragment += [ "--match-set", rich_dest.ipset, flags ] return rule_fragment def _rich_rule_source_fragment(self, rich_source): if not rich_source: return [] rule_fragment = [] if rich_source.addr: if rich_source.invert: rule_fragment.append("!") if check_single_address("ipv6", rich_source.addr): rule_fragment += [ "-s", normalizeIP6(rich_source.addr) ] elif check_address("ipv6", rich_source.addr): addr_split = rich_source.addr.split("/") rule_fragment += [ "-s", normalizeIP6(addr_split[0]) + "/" + addr_split[1] ] else: rule_fragment += [ "-s", rich_source.addr ] elif hasattr(rich_source, "mac") and rich_source.mac: rule_fragment += [ "-m", "mac" ] if rich_source.invert: rule_fragment.append("!") rule_fragment += [ "--mac-source", rich_source.mac ] elif hasattr(rich_source, "ipset") and rich_source.ipset: rule_fragment += [ "-m", "set" ] if rich_source.invert: rule_fragment.append("!") flags = self._fw.zone._ipset_match_flags(rich_source.ipset, "src") rule_fragment += [ "--match-set", rich_source.ipset, flags ] return rule_fragment def build_policy_ports_rules(self, enable, policy, proto, port, destination=None, rich_rule=None): add_del = { True: "-A", False: "-D" }[enable] table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) rule_fragment = [ "-p", proto ] if port: rule_fragment += [ "--dport", "%s" % portStr(port) ] if destination: rule_fragment += [ "-d", destination ] if rich_rule: rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) rule_fragment += self._rich_rule_source_fragment(rich_rule.source) if not rich_rule or type(rich_rule.action) != Rich_Mark: rule_fragment += [ "-m", "conntrack", "--ctstate", "NEW,UNTRACKED" ] rules = [] if rich_rule: rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment)) rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment)) rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment)) else: rules.append([add_del, "%s_allow" % (_policy), "-t", table] + rule_fragment + [ "-j", "ACCEPT" ]) return rules def build_policy_protocol_rules(self, enable, policy, protocol, destination=None, rich_rule=None): add_del = { True: "-A", False: "-D" }[enable] table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) rule_fragment = [ "-p", protocol ] if destination: rule_fragment += [ "-d", destination ] if rich_rule: rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) rule_fragment += self._rich_rule_source_fragment(rich_rule.source) if not rich_rule or type(rich_rule.action) != Rich_Mark: rule_fragment += [ "-m", "conntrack", "--ctstate", "NEW,UNTRACKED" ] rules = [] if rich_rule: rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment)) rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment)) rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment)) else: rules.append([add_del, "%s_allow" % (_policy), "-t", table] + rule_fragment + [ "-j", "ACCEPT" ]) return rules def build_policy_source_ports_rules(self, enable, policy, proto, port, destination=None, rich_rule=None): add_del = { True: "-A", False: "-D" }[enable] table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) rule_fragment = [ "-p", proto ] if port: rule_fragment += [ "--sport", "%s" % portStr(port) ] if destination: rule_fragment += [ "-d", destination ] if rich_rule: rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) rule_fragment += self._rich_rule_source_fragment(rich_rule.source) if not rich_rule or type(rich_rule.action) != Rich_Mark: rule_fragment += [ "-m", "conntrack", "--ctstate", "NEW,UNTRACKED" ] rules = [] if rich_rule: rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment)) rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment)) rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment)) else: rules.append([add_del, "%s_allow" % (_policy), "-t", table] + rule_fragment + [ "-j", "ACCEPT" ]) return rules def build_policy_helper_ports_rules(self, enable, policy, proto, port, destination, helper_name, module_short_name): table = "raw" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "-A", False: "-D" }[enable] rule = [ add_del, "%s_allow" % (_policy), "-t", "raw", "-p", proto ] if port: rule += [ "--dport", "%s" % portStr(port) ] if destination: rule += [ "-d", destination ] rule += [ "-j", "CT", "--helper", module_short_name ] return [rule] def build_zone_forward_rules(self, enable, zone, policy, table, interface=None, source=None): add_del = { True: "-A", False: "-D" }[enable] _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) rules = [] if interface: rules.append(["-t", "filter", add_del, "%s_allow" % _policy, "-o", interface, "-j", "ACCEPT"]) else: # source # iptables can not match destination MAC if check_mac(source): return [] rules.append(["-t", "filter", add_del, "%s_allow" % _policy] + self._rule_addr_fragment("-d", source) + ["-j", "ACCEPT"]) return rules def build_policy_masquerade_rules(self, enable, policy, rich_rule=None): table = "nat" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX, isSNAT=True) add_del = { True: "-A", False: "-D" }[enable] rule_fragment = [] if rich_rule: chain_suffix = self._rich_rule_chain_suffix(rich_rule) rule_fragment += self._rich_rule_priority_fragment(rich_rule) rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) rule_fragment += self._rich_rule_source_fragment(rich_rule.source) else: chain_suffix = "allow" rules = [] rules.append(["-t", "nat", add_del, "%s_%s" % (_policy, chain_suffix)] + rule_fragment + [ "!", "-o", "lo", "-j", "MASQUERADE" ]) rule_fragment = [] if rich_rule: chain_suffix = self._rich_rule_chain_suffix(rich_rule) rule_fragment += self._rich_rule_priority_fragment(rich_rule) rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) rule_fragment += self._rich_rule_source_fragment(rich_rule.source) else: chain_suffix = "allow" table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) rules.append(["-t", "filter", add_del, "%s_%s" % (_policy, chain_suffix)] + rule_fragment + ["-m", "conntrack", "--ctstate", "NEW,UNTRACKED", "-j", "ACCEPT" ]) return rules def build_policy_forward_port_rules(self, enable, policy, port, protocol, toport, toaddr, rich_rule=None): table = "nat" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "-A", False: "-D" }[enable] to = "" if toaddr: if check_single_address("ipv6", toaddr): to += "[%s]" % normalizeIP6(toaddr) else: to += toaddr if toport and toport != "": to += ":%s" % portStr(toport, "-") rule_fragment = [] if rich_rule: chain_suffix = self._rich_rule_chain_suffix(rich_rule) rule_fragment = self._rich_rule_priority_fragment(rich_rule) rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) rule_fragment += self._rich_rule_source_fragment(rich_rule.source) else: chain_suffix = "allow" rules = [] if rich_rule: rules.append(self._rich_rule_log(policy, rich_rule, enable, "nat", rule_fragment)) rules.append(["-t", "nat", add_del, "%s_%s" % (_policy, chain_suffix)] + rule_fragment + ["-p", protocol, "--dport", portStr(port), "-j", "DNAT", "--to-destination", to]) return rules def _icmp_types_fragment(self, icmp_type): if self.ipv == "ipv4" and icmp_type in ICMP_TYPES: _type, _code, _omit_code = ICMP_TYPES[icmp_type] _type_str = str(_type) if _omit_code else str(_type) + "/" + str(_code) return ["-m", "icmp", "--icmp-type", _type_str] elif self.ipv == "ipv6" and icmp_type in ICMPV6_TYPES: _type, _code, _omit_code = ICMPV6_TYPES[icmp_type] _type_str = str(_type) if _omit_code else str(_type) + "/" + str(_code) return ["-m", "icmp6", "--icmpv6-type", _type_str] else: raise FirewallError( INVALID_ICMPTYPE, f"ICMP type {icmp_type} not supported by {self.name}" ) def build_policy_icmp_block_rules(self, enable, policy, ict, rich_rule=None): table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) add_del = { True: "-A", False: "-D" }[enable] if self.ipv == "ipv4": proto = [ "-p", "icmp" ] match = self._icmp_types_fragment(ict.name) else: proto = [ "-p", "ipv6-icmp" ] match = self._icmp_types_fragment(ict.name) rules = [] if self._fw.policy.query_icmp_block_inversion(policy): final_chain = "%s_allow" % (_policy) final_target = "ACCEPT" else: final_chain = "%s_deny" % (_policy) final_target = "%%REJECT%%" rule_fragment = [] if rich_rule: rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) rule_fragment += self._rich_rule_source_fragment(rich_rule.source) rule_fragment += proto + match if rich_rule: rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment)) rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment)) if rich_rule.action: rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment)) else: chain_suffix = self._rich_rule_chain_suffix(rich_rule) rules.append(["-t", table, add_del, "%s_%s" % (_policy, chain_suffix)] + self._rich_rule_priority_fragment(rich_rule) + rule_fragment + [ "-j", "%%REJECT%%" ]) else: if self._fw.get_log_denied() != "off" and final_target != "ACCEPT": rules.append([ add_del, final_chain, "-t", table ] + rule_fragment + [ "%%LOGTYPE%%", "-j", "LOG", "--log-prefix", "\"%s_ICMP_BLOCK: \"" % policy ]) rules.append([ add_del, final_chain, "-t", table ] + rule_fragment + [ "-j", final_target ]) return rules def build_policy_icmp_block_inversion_rules(self, enable, policy): table = "filter" _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) rules = [] rule_idx = 6 if self._fw.policy.query_icmp_block_inversion(policy): ibi_target = "%%REJECT%%" if self._fw.get_log_denied() != "off": if enable: rule = [ "-I", _policy, str(rule_idx) ] else: rule = [ "-D", _policy ] rule = rule + [ "-t", table, "-p", "%%ICMP%%", "%%LOGTYPE%%", "-j", "LOG", "--log-prefix", "\"%s_ICMP_BLOCK: \"" % _policy ] rules.append(rule) rule_idx += 1 else: ibi_target = "ACCEPT" if enable: rule = [ "-I", _policy, str(rule_idx) ] else: rule = [ "-D", _policy ] rule = rule + [ "-t", table, "-p", "%%ICMP%%", "-j", ibi_target ] rules.append(rule) return rules def build_policy_rich_source_destination_rules(self, enable, policy, rich_rule): table = "filter" rule_fragment = [] rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) rule_fragment += self._rich_rule_source_fragment(rich_rule.source) rules = [] rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment)) rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment)) rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment)) return rules def is_ipv_supported(self, ipv): return ipv == self.ipv class ip6tables(ip4tables): ipv = "ipv6" name = "ip6tables" def build_rpfilter_rules(self, log_denied=False): rules = [] rules.append([ "-I", "PREROUTING", "-t", "mangle", "-m", "rpfilter", "--invert", "--validmark", "-j", "DROP" ]) if log_denied != "off": rules.append([ "-I", "PREROUTING", "-t", "mangle", "-m", "rpfilter", "--invert", "--validmark", "-j", "LOG", "--log-prefix", "rpfilter_DROP: " ]) rules.append([ "-I", "PREROUTING", "-t", "mangle", "-p", "ipv6-icmp", "--icmpv6-type=neighbour-solicitation", "-j", "ACCEPT" ]) # RHBZ#1575431, kernel bug in 4.16-4.17 rules.append([ "-I", "PREROUTING", "-t", "mangle", "-p", "ipv6-icmp", "--icmpv6-type=router-advertisement", "-j", "ACCEPT" ]) # RHBZ#1058505 return rules def build_rfc3964_ipv4_rules(self): daddr_list = [ "::0.0.0.0/96", # IPv4 compatible "::ffff:0.0.0.0/96", # IPv4 mapped "2002:0000::/24", # 0.0.0.0/8 (the system has no address assigned yet) "2002:0a00::/24", # 10.0.0.0/8 (private) "2002:7f00::/24", # 127.0.0.0/8 (loopback) "2002:ac10::/28", # 172.16.0.0/12 (private) "2002:c0a8::/32", # 192.168.0.0/16 (private) "2002:a9fe::/32", # 169.254.0.0/16 (IANA Assigned DHCP link-local) "2002:e000::/19", # 224.0.0.0/4 (multicast), 240.0.0.0/4 (reserved and broadcast) ] chain_name = "RFC3964_IPv4" self.our_chains["filter"].add(chain_name) rules = [] rules.append(["-t", "filter", "-N", chain_name]) for daddr in daddr_list: rules.append(["-t", "filter", "-I", chain_name, "-d", daddr, "-j", "REJECT", "--reject-with", "addr-unreach"]) if self._fw._log_denied in ["unicast", "all"]: rules.append(["-t", "filter", "-I", chain_name, "-d", daddr, "-j", "LOG", "--log-prefix", "\"RFC3964_IPv4_REJECT: \""]) # Inject into FORWARD and OUTPUT chains rules.append(["-t", "filter", "-I", "OUTPUT", "4", "-j", chain_name]) rules.append(["-t", "filter", "-I", "FORWARD", "4", "-j", chain_name]) return rules PK!  modules.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2010-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """modules backend""" __all__ = [ "modules" ] from firewall.core.prog import runProg from firewall.core.logger import log from firewall.config import COMMANDS class modules(object): def __init__(self): self._load_command = COMMANDS["modprobe"] # Use rmmod instead of modprobe -r (RHBZ#1031102) self._unload_command = COMMANDS["rmmod"] def __repr__(self): return '%s' % (self.__class__) def loaded_modules(self): """ get all loaded kernel modules and their dependencies """ mods = [ ] deps = { } try: with open("/proc/modules", "r") as f: for line in f: if not line: break line = line.strip() splits = line.split() mods.append(splits[0]) if splits[3] != "-": deps[splits[0]] = splits[3].split(",")[:-1] else: deps[splits[0]] = [ ] except FileNotFoundError: pass return mods, deps # [loaded modules], {module:[dependants]} def load_module(self, module): log.debug2("%s: %s %s", self.__class__, self._load_command, module) return runProg(self._load_command, [ module ]) def unload_module(self, module): log.debug2("%s: %s %s", self.__class__, self._unload_command, module) return runProg(self._unload_command, [ module ]) def get_deps(self, module, deps, ret): """ get all dependants of a module """ if module not in deps: return for mod in deps[module]: self.get_deps(mod, deps, ret) if mod not in ret: ret.append(mod) if module not in ret: ret.append(module) def get_firewall_modules(self): """ get all loaded firewall-related modules """ mods = [ ] (mods2, deps) = self.loaded_modules() self.get_deps("nf_conntrack", deps, mods) # these modules don't have dependants listed in /proc/modules for bad_bad_module in ["nf_conntrack_ipv4", "nf_conntrack_ipv6"]: if bad_bad_module in mods: # move them to end of list, so we'll remove them later mods.remove(bad_bad_module) mods.insert(-1, bad_bad_module) for mod in mods2: if mod in [ "ip_tables", "ip6_tables", "ebtables" ] or \ mod.startswith("iptable_") or mod.startswith("ip6table_") or \ mod.startswith("nf_") or mod.startswith("xt_") or \ mod.startswith("ipt_") or mod.startswith("ip6t_") : self.get_deps(mod, deps, mods) return mods def unload_firewall_modules(self): """ unload all firewall-related modules """ for module in self.get_firewall_modules(): (status, ret) = self.unload_module(module) if status != 0: log.debug1("Failed to unload module '%s': %s" %(module, ret)) PK!}mSq~q~*__pycache__/fw_config.cpython-36.opt-1.pycnu[3 Yj@sdgZddlZddlZddlZddlZddlmZddlmZddl m Z m Z m Z ddl mZmZmZddlmZmZmZddlmZmZmZdd lmZmZmZdd lmZmZm Z dd lm!Z!dd l"m#Z#Gd dde$Z%dS)FirewallConfigN)config)log)IcmpTypeicmptype_readericmptype_writer)Serviceservice_readerservice_writer)Zone zone_reader zone_writer)IPSet ipset_reader ipset_writer)Helper helper_reader helper_writer)Policy policy_reader policy_writer)errors) FirewallErrorc@s$eZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ ddZ ddZ ddZddZddZddZdd Zd!d"Zd#d$Zd%d&Zd'd(Zd)d*Zd+d,Zd-d.Zd/d0Zd1d2Zd3d4Zd5d6Zd7d8Zd9d:Zd;d<Z d=d>Z!d?d@Z"dAdBZ#dCdDZ$dEdFZ%dGdHZ&dIdJZ'dKdLZ(dMdNZ)dOdPZ*dQdRZ+dSdTZ,dUdVZ-dWdXZ.dYdZZ/d[d\Z0d]d^Z1d_d`Z2dadbZ3dcddZ4dedfZ5dgdhZ6didjZ7dkdlZ8dmdnZ9dodpZ:dqdrZ;dsdtZdydzZ?d{d|Z@d}d~ZAddZBddZCddZDddZEddZFddZGddZHddZIddZJddZKddZLddZMddZNddZOddZPddZQddZRddZSddZTddZUddZVddZWddZXddZYddZZddZ[ddZ\ddZ]ddZ^ddZ_ddZ`ddZaddZbdd„ZcddĄZdddƄZedS)rcCs||_|jdS)N)_fw_FirewallConfig__init_vars)selffwr/usr/lib/python3.6/fw_config.py__init__(szFirewallConfig.__init__cCsHd|j|j|j|j|j|j|j|j|j|j |j |j |j |j |j|jfS)Nz>%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)) __class___ipsets _icmptypes _services_zones_helpersZpolicy_objects_builtin_ipsets_builtin_icmptypes_builtin_services_builtin_zones_builtin_helpers_builtin_policy_objects_firewalld_conf _policies_direct)rrrr__repr__,s zFirewallConfig.__repr__cCs^i|_i|_i|_i|_i|_i|_i|_i|_i|_i|_ i|_ i|_ d|_ d|_ d|_dS)N)r!r"r#r$r%_policy_objectsr&r'r(r)r*r+r,r-r.)rrrrZ __init_vars6szFirewallConfig.__init_varscCs4x,t|jjD]}|j|j|j|=qWx,t|jjD]}|j|j|j|=q>Wx,t|jjD]}|j|j|j|=qlWx,t|jjD]}|j|j|j|=qWx,t|jjD]}|j|j|j|=qWx,t|jjD]}|j|j|j|=qWx.t|j jD]}|j |j|j |=q$Wx.t|j jD]}|j |j|j |=qTWx.t|j jD]}|j |j|j |=qWx.t|j jD]}|j |j|j |=qW|j r|j j|` d|_ |jr |jj|`d|_|jr(|jj|`d|_|jdS)N)listr&keyscleanupr!r'r"r(r#r)r$r*r%r,r-r.r)rxrrrr3GsV         zFirewallConfig.cleanupcCs |jjjS)N)rpoliciesZquery_lockdown)rrrrlockdown_enabled~szFirewallConfig.lockdown_enabledcCs|jjj||S)N)rr5 access_check)rkeyvaluerrrr7szFirewallConfig.access_checkcCs ||_dS)N)r,)rconfrrrset_firewalld_confsz!FirewallConfig.set_firewalld_confcCs|jS)N)r,)rrrrget_firewalld_confsz!FirewallConfig.get_firewalld_confcCs(tjjtjs|jjn |jjdS)N)ospathexistsrZFIREWALLD_CONFr,clearread)rrrrupdate_firewalld_confs z$FirewallConfig.update_firewalld_confcCs ||_dS)N)r-)rr5rrr set_policiesszFirewallConfig.set_policiescCs|jS)N)r-)rrrr get_policiesszFirewallConfig.get_policiescCs,tjjtjs|jjjn |jjjdS)N) r=r>r?rZLOCKDOWN_WHITELISTr-Zlockdown_whitelistr3rA)rrrrupdate_lockdown_whitelistsz(FirewallConfig.update_lockdown_whitelistcCs ||_dS)N)r.)rZdirectrrr set_directszFirewallConfig.set_directcCs|jS)N)r.)rrrr get_directszFirewallConfig.get_directcCs(tjjtjs|jjn |jjdS)N)r=r>r?rZFIREWALLD_DIRECTr.r3rA)rrrr update_directs zFirewallConfig.update_directcCs$ttt|jjt|jjS)N)sortedsetr1r!r2r&)rrrr get_ipsetsszFirewallConfig.get_ipsetscCs$|jr||j|j<n ||j|j<dS)N)builtinr&namer!)robjrrr add_ipsetszFirewallConfig.add_ipsetcCs8||jkr|j|S||jkr(|j|Sttj|dS)N)r!r&rr INVALID_IPSET)rrMrrr get_ipsets     zFirewallConfig.get_ipsetcCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._ipsets[%s] != objz'%s' not a built-in ipset)rMr!rr NO_DEFAULTSr& _remove_ipset)rrNrrrload_ipset_defaultss    z"FirewallConfig.load_ipset_defaultscCs|jS)N) export_config)rrNrrrget_ipset_configszFirewallConfig.get_ipset_configcCsj|jrPtj|}|j|tj|_d|_|j|jkr:d|_|j|t||S|j|t||SdS)NF) rLcopy import_configrETC_FIREWALLD_IPSETSr>defaultrOr)rrNr:r4rrrset_ipset_configs     zFirewallConfig.set_ipset_configcCsx||jks||jkr$ttjd|t}|j||j|||_d||_ t j |_ d|_ d|_t||j||S)Nznew_ipset(): '%s'z%s.xmlFT)r!r&rr NAME_CONFLICTr check_namerXrMfilenamerrYr>rLrZrrO)rrMr:r4rrr new_ipsets     zFirewallConfig.new_ipsetcCstjj|}tjj|}tjj|s|tjkrx|jjD]D}|j|}|j |kr:|j|=|j |j krvd|j |j fSd|fSq:WnHxF|j jD]8}|j |}|j |kr|j |=|j |jkrd|fSdSqWdSt j d|yt||}Wn0tk r}zt jd||dSd}~XnX|j |j krJ|j |jkrJ|j|d|fS|tjkr|j |jkr|j|j j|_||j|j <d|fS|j |j kr|j |j =||j |j <|j |jkrd|fSd Sd S) NupdateremovezLoading ipset file '%s'z"Failed to load ipset file '%s': %snew)NN)NN)NN)NN)NN)r=r>basenamedirnamer?rrYr!r2r^rMr&rdebug1r ExceptionerrorrOrZ)rrMr^r>r4rNmsgrrrupdate_ipset_from_pathsP                z%FirewallConfig.update_ipset_from_pathcCs|j|jkrttj|j|jtjkr>ttjd|jtjfd|j|jf}yt j |d|Wn:t k r}zt j d||tj|WYdd}~XnX|j|j=dS)Nz '%s' != '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr!rrrPr>rrYINVALID_DIRECTORYshutilmoverfrrgr=ra)rrNrMrhrrrrS8s   zFirewallConfig._remove_ipsetcCs$|js|j r ttjd|jdS)Nz'%s' is built-in ipset)rLrZrrZ BUILTIN_IPSETrM)rrNrrrcheck_builtin_ipsetIsz"FirewallConfig.check_builtin_ipsetcCs|j||j|dS)N)rmrS)rrNrrr remove_ipsetNs zFirewallConfig.remove_ipsetcCs$|j||j||}|j||S)N)rm _copy_ipsetrS)rrNrMr_rrr rename_ipsetRs   zFirewallConfig.rename_ipsetcCs|j||jS)N)r_rU)rrNrMrrrroXszFirewallConfig._copy_ipsetcCs$ttt|jjt|jjS)N)rIrJr1r"r2r')rrrr get_icmptypes]szFirewallConfig.get_icmptypescCs$|jr||j|j<n ||j|j<dS)N)rLr'rMr")rrNrrr add_icmptypeaszFirewallConfig.add_icmptypecCs8||jkr|j|S||jkr(|j|Sttj|dS)N)r"r'rrINVALID_ICMPTYPE)rrMrrr get_icmptypegs     zFirewallConfig.get_icmptypecCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._icmptypes[%s] != objz'%s' not a built-in icmptype)rMr"rrrRr'_remove_icmptype)rrNrrrload_icmptype_defaultsns    z%FirewallConfig.load_icmptype_defaultscCs|jS)N)rU)rrNrrrget_icmptype_configzsz"FirewallConfig.get_icmptype_configcCsj|jrPtj|}|j|tj|_d|_|j|jkr:d|_|j|t||S|j|t||SdS)NF) rLrWrXrETC_FIREWALLD_ICMPTYPESr>rZrrr)rrNr:r4rrrset_icmptype_config}s     z"FirewallConfig.set_icmptype_configcCsx||jks||jkr$ttjd|t}|j||j|||_d||_ t j |_ d|_ d|_t||j||S)Nznew_icmptype(): '%s'z%s.xmlFT)r"r'rrr\rr]rXrMr^rrxr>rLrZrrr)rrMr:r4rrr new_icmptypes     zFirewallConfig.new_icmptypecCstjj|}tjj|}tjj|s|tjkrx|jjD]D}|j|}|j |kr:|j|=|j |j krvd|j |j fSd|fSq:WnHxF|j jD]8}|j |}|j |kr|j |=|j |jkrd|fSdSqWdSt j d|yt||}Wn0tk r}zt jd||dSd}~XnX|j |j krJ|j |jkrJ|j|d|fS|tjkr|j |jkr|j|j j|_||j|j <d|fS|j |j kr|j |j =||j |j <|j |jkrd|fSd Sd S) Nr`razLoading icmptype file '%s'z%Failed to load icmptype file '%s': %srb)NN)NN)NN)NN)NN)r=r>rcrdr?rrxr"r2r^rMr'rrerrfrgrrrZ)rrMr^r>r4rNrhrrrupdate_icmptype_from_pathsP                z(FirewallConfig.update_icmptype_from_pathcCs|j|jkrttj|j|jtjkr>ttjd|jtjfd|j|jf}yt j |d|Wn:t k r}zt j d||tj|WYdd}~XnX|j|j=dS)Nz '%s' != '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr"rrrsr>rrxrjrkrlrfrrgr=ra)rrNrMrhrrrrus  zFirewallConfig._remove_icmptypecCs$|js|j r ttjd|jdS)Nz'%s' is built-in icmp type)rLrZrrZBUILTIN_ICMPTYPErM)rrNrrrcheck_builtin_icmptypesz%FirewallConfig.check_builtin_icmptypecCs|j||j|dS)N)r|ru)rrNrrrremove_icmptypes zFirewallConfig.remove_icmptypecCs$|j||j||}|j||S)N)r|_copy_icmptyperu)rrNrMrzrrrrename_icmptypes   zFirewallConfig.rename_icmptypecCs|j||jS)N)rzrU)rrNrMrrrr~szFirewallConfig._copy_icmptypecCs$ttt|jjt|jjS)N)rIrJr1r#r2r()rrrr get_services szFirewallConfig.get_servicescCs$|jr||j|j<n ||j|j<dS)N)rLr(rMr#)rrNrrr add_serviceszFirewallConfig.add_servicecCs<||jkr|j|S||jkr(|j|Sttjd|dS)Nzget_service(): '%s')r#r(rrINVALID_SERVICE)rrMrrr get_services     zFirewallConfig.get_servicecCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._services[%s] != objz'%s' not a built-in service)rMr#rrrRr(_remove_service)rrNrrrload_service_defaultss    z$FirewallConfig.load_service_defaultscCsr|j}g}x\tdD]P}|j|d|krN|jtjt||j|dq|j||j|dqWt|S)Nr)export_config_dictrangeIMPORT_EXPORT_STRUCTUREappendrWdeepcopygetattrtuple)rrN conf_dict conf_listirrrget_service_config's"z!FirewallConfig.get_service_configcCs|jS)N)r)rrNrrrget_service_config_dict3sz&FirewallConfig.get_service_config_dictcCsi}x&t|D]\}}|||j|d<qW|jr|tj|}|j|tj|_d|_|j|jkrfd|_|j |t ||S|j|t ||SdS)NrF) enumeraterrLrWimport_config_dictrETC_FIREWALLD_SERVICESr>rZrr )rrNr:rrr9r4rrrset_service_config6s      z!FirewallConfig.set_service_configcCsj|jrPtj|}|j|tj|_d|_|j|jkr:d|_|j|t||S|j|t||SdS)NF) rLrWrrrr>rZrr )rrNr:r4rrrset_service_config_dictJs     z&FirewallConfig.set_service_config_dictcCs||jks||jkr$ttjd|i}x&t|D]\}}||tj|d<q2Wt}|j||j |||_ d||_ t j |_d|_d|_t||j||S)Nznew_service(): '%s'rz%s.xmlFT)r#r(rrr\rrrr]rrMr^rrr>rLrZr r)rrMr:rrr9r4rrr new_serviceZs"     zFirewallConfig.new_servicecCsx||jks||jkr$ttjd|t}|j||j|||_d||_ t j |_ d|_ d|_t||j||S)Nznew_service(): '%s'z%s.xmlFT)r#r(rrr\rr]rrMr^rrr>rLrZr r)rrMr:r4rrrnew_service_dictqs     zFirewallConfig.new_service_dictcCstjj|}tjj|}tjj|s|tjkrx|jjD]D}|j|}|j |kr:|j|=|j |j krvd|j |j fSd|fSq:WnHxF|j jD]8}|j |}|j |kr|j |=|j |jkrd|fSdSqWdSt j d|yt||}Wn0tk r}zt jd||dSd}~XnX|j |j krJ|j |jkrJ|j|d|fS|tjkr|j |jkr|j|j j|_||j|j <d|fS|j |j kr|j |j =||j |j <|j |jkrd|fSd Sd S) Nr`razLoading service file '%s'z$Failed to load service file '%s': %srb)NN)NN)NN)NN)NN)r=r>rcrdr?rrr#r2r^rMr(rrer rfrgrrZ)rrMr^r>r4rNrhrrrupdate_service_from_pathsP                z'FirewallConfig.update_service_from_pathcCs|j|jkrttj|j|jtjkr>ttjd|jtjfd|j|jf}yt j |d|Wn:t k r}zt j d||tj|WYdd}~XnX|j|j=dS)Nz '%s' != '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr#rrrr>rrrjrkrlrfrrgr=ra)rrNrMrhrrrrs  zFirewallConfig._remove_servicecCs$|js|j r ttjd|jdS)Nz'%s' is built-in service)rLrZrrZBUILTIN_SERVICErM)rrNrrrcheck_builtin_servicesz$FirewallConfig.check_builtin_servicecCs|j||j|dS)N)rr)rrNrrrremove_services zFirewallConfig.remove_servicecCs$|j||j||}|j||S)N)r _copy_servicer)rrNrMrrrrrename_services   zFirewallConfig.rename_servicecCs|j||jS)N)rr)rrNrMrrrrszFirewallConfig._copy_servicecCs$ttt|jjt|jjS)N)rIrJr1r$r2r))rrrr get_zonesszFirewallConfig.get_zonescCs$|jr||j|j<n ||j|j<dS)N)rLr)rMr$)rrNrrradd_zoneszFirewallConfig.add_zonecCs(||jkr|j|=||jkr$|j|=dS)N)r)r$)rrMrrr forget_zones  zFirewallConfig.forget_zonecCs<||jkr|j|S||jkr(|j|Sttjd|dS)Nzget_zone(): %s)r$r)rr INVALID_ZONE)rrMrrrget_zones     zFirewallConfig.get_zonecCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._zones[%s] != objz'%s' not a built-in zone)rMr$rrrRr) _remove_zone)rrNrrrload_zone_defaultss    z!FirewallConfig.load_zone_defaultscCsr|j}g}x\tdD]P}|j|d|krN|jtjt||j|dq|j||j|dqWt|S)Nr)rrrrrWrrr)rrNrrrrrrget_zone_configs"zFirewallConfig.get_zone_configcCs|jS)N)r)rrNrrrget_zone_config_dictsz#FirewallConfig.get_zone_config_dictcCsi}x&t|D]\}}|||j|d<qW|jrtj|}||_|j|tj|_d|_|j|jkrld|_ |j |t ||S||_|j|t ||SdS)NrF) rrrLrW fw_configrrETC_FIREWALLD_ZONESr>rZrr )rrNr:rrr9r4rrrset_zone_config s$     zFirewallConfig.set_zone_configcCsv|jrVtj|}||_|j|tj|_d|_|j|jkr@d|_|j|t ||S||_|j|t ||SdS)NF) rLrWrrrrr>rZrr )rrNr:r4rrrset_zone_config_dict6s     z#FirewallConfig.set_zone_config_dictcCs||jks||jkr$ttjd|i}x&t|D]\}}||tj|d<q2Wt}||_|j ||j |||_ d||_ t j|_d|_d|_t||j||S)Nznew_zone(): '%s'rz%s.xmlFT)r$r)rrr\rr rrr]rrMr^rrr>rLrZr r)rrMr:rrr9r4rrrnew_zoneHs"    zFirewallConfig.new_zonecCs~||jks||jkr$ttjd|t}||_|j||j|||_ d||_ t j |_ d|_d|_t||j||S)Nznew_zone(): '%s'z%s.xmlFT)r$r)rrr\r rr]rrMr^rrr>rLrZr r)rrMr:r4rrr new_zone_dict_s    zFirewallConfig.new_zone_dictcCstjj|}tjj|}tjj|s|jtjrx|jj D]D}|j|}|j |kr<|j|=|j |j krxd|j |j fSd|fSqrcrdr? startswithrrr$r2r^rMr)rrer rfrgrlenrrZ)rrMr^r>r4rNrhrrrupdate_zone_from_pathrsZ                z$FirewallConfig.update_zone_from_pathcCs|j|jkrttj|j|jjtjs@ttj d|jtjfd|j|jf}yt j |d|Wn:t k r}zt jd||tj|WYdd}~XnX|j|j=dS)Nz'%s' doesn't start with '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr$rrrr>rrrrjrkrlrfrrgr=ra)rrNrMrhrrrrs zFirewallConfig._remove_zonecCs$|js|j r ttjd|jdS)Nz'%s' is built-in zone)rLrZrrZ BUILTIN_ZONErM)rrNrrrcheck_builtin_zonesz!FirewallConfig.check_builtin_zonecCs|j||j|dS)N)rr)rrNrrr remove_zones zFirewallConfig.remove_zonec CsN|j||j}|j|y|j||}Wn|j|j|YnX|S)N)rrrrrM)rrNrMZobj_confrrrr rename_zones  zFirewallConfig.rename_zonecCs$ttt|jjt|jjS)N)rIrJr1r0r2r+)rrrrget_policy_objectssz!FirewallConfig.get_policy_objectscCs$|jr||j|j<n ||j|j<dS)N)rLr+rMr0)rrNrrradd_policy_objectsz FirewallConfig.add_policy_objectcCs<||jkr|j|S||jkr(|j|Sttjd|dS)Nzget_policy_object(): %s)r0r+rrINVALID_POLICY)rrMrrrget_policy_objects     z FirewallConfig.get_policy_objectcCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._policy_objects[%s] != objz'%s' not a built-in policy)rMr0rrrRr+_remove_policy_object)rrNrrrload_policy_object_defaultss    z*FirewallConfig.load_policy_object_defaultscCs|jS)N)r)rrNrrrget_policy_object_config_dictsz,FirewallConfig.get_policy_object_config_dictcCsv|jrVtj|}||_|j|tj|_d|_|j|jkr@d|_|j|t ||S||_|j|t ||SdS)NF) rLrWrrrETC_FIREWALLD_POLICIESr>rZrr)rrNr:r4rrrset_policy_object_config_dicts     z,FirewallConfig.set_policy_object_config_dictcCs~||jks||jkr$ttjd|t}||_|j||j|||_ d||_ t j |_ d|_d|_t||j||S)Nznew_policy_object(): '%s'z%s.xmlFT)r0r+rrr\rrr]rrMr^rrr>rLrZrr)rrMr:r4rrrnew_policy_object_dicts    z%FirewallConfig.new_policy_object_dictcCstjj|}tjj|}tjj|s|jtjrx|jj D]D}|j|}|j |kr<|j|=|j |j krxd|j |j fSd|fSqrcrdr?rrrr0r2r^rMr+rrerrfrgrrrrZ)rrMr^r>r4rNrhrrrupdate_policy_object_from_path,sZ                z-FirewallConfig.update_policy_object_from_pathcCs|j|jkrttj|j|jjtjs@ttj d|jtjfd|j|jf}yt j |d|Wn:t k r}zt jd||tj|WYdd}~XnX|j|j=dS)Nz'%s' doesn't start with '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr0rrrr>rrrrjrkrlrfrrgr=ra)rrNrMrhrrrrys z$FirewallConfig._remove_policy_objectcCs$|js|j r ttjd|jdS)Nz'%s' is built-in policy)rLrZrrZBUILTIN_POLICYrM)rrNrrrcheck_builtin_policy_objectsz*FirewallConfig.check_builtin_policy_objectcCs|j||j|dS)N)rr)rrNrrrremove_policy_objects z#FirewallConfig.remove_policy_objectcCs$|j||j||}|j||S)N)r_copy_policy_objectr)rrNrMZnew_policy_objectrrrrename_policy_objects   z#FirewallConfig.rename_policy_objectcCs|j||jS)N)rr)rrNrMrrrrsz"FirewallConfig._copy_policy_objectcCs$ttt|jjt|jjS)N)rIrJr1r%r2r*)rrrr get_helpersszFirewallConfig.get_helperscCs$|jr||j|j<n ||j|j<dS)N)rLr*rMr%)rrNrrr add_helperszFirewallConfig.add_helpercCs8||jkr|j|S||jkr(|j|Sttj|dS)N)r%r*rrINVALID_HELPER)rrMrrr get_helpers     zFirewallConfig.get_helpercCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._helpers[%s] != objz'%s' not a built-in helper)rMr%rrrRr*_remove_helper)rrNrrrload_helper_defaultss    z#FirewallConfig.load_helper_defaultscCs|jS)N)rU)rrNrrrget_helper_configsz FirewallConfig.get_helper_configcCsj|jrPtj|}|j|tj|_d|_|j|jkr:d|_|j|t||S|j|t||SdS)NF) rLrWrXrETC_FIREWALLD_HELPERSr>rZrr)rrNr:r4rrrset_helper_configs     z FirewallConfig.set_helper_configcCsx||jks||jkr$ttjd|t}|j||j|||_d||_ t j |_ d|_ d|_t||j||S)Nznew_helper(): '%s'z%s.xmlFT)r%r*rrr\rr]rXrMr^rrr>rLrZrr)rrMr:r4rrr new_helpers     zFirewallConfig.new_helpercCstjj|}tjj|}tjj|s|tjkrx|jjD]D}|j|}|j |kr:|j|=|j |j krvd|j |j fSd|fSq:WnHxF|j jD]8}|j |}|j |kr|j |=|j |jkrd|fSdSqWdSt j d|yt||}Wn0tk r}zt jd||dSd}~XnX|j |j krJ|j |jkrJ|j|d|fS|tjkr|j |jkr|j|j j|_||j|j <d|fS|j |j kr|j |j =||j |j <|j |jkrd|fSd Sd S) Nr`razLoading helper file '%s'z#Failed to load helper file '%s': %srb)NN)NN)NN)NN)NN)r=r>rcrdr?rrr%r2r^rMr*rrerrfrgrrZ)rrMr^r>r4rNrhrrrupdate_helper_from_pathsP                z&FirewallConfig.update_helper_from_pathcCs|j|jkrttj|j|jtjkr>ttjd|jtjfd|j|jf}yt j |d|Wn:t k r}zt j d||tj|WYdd}~XnX|j|j=dS)Nz '%s' != '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr%rrrr>rrrjrkrlrfrrgr=ra)rrNrMrhrrrr&s   zFirewallConfig._remove_helpercCs$|js|j r ttjd|jdS)Nz'%s' is built-in helper)rLrZrrZBUILTIN_HELPERrM)rrNrrrcheck_builtin_helper7sz#FirewallConfig.check_builtin_helpercCs|j||j|dS)N)rr)rrNrrr remove_helper<s zFirewallConfig.remove_helpercCs$|j||j||}|j||S)N)r _copy_helperr)rrNrMrrrr rename_helper@s   zFirewallConfig.rename_helpercCs|j||jS)N)rrU)rrNrMrrrrFszFirewallConfig._copy_helperN)f__name__ __module__ __qualname__rr/rr3r6r7r;r<rBrCrDrErFrGrHrKrOrQrTrVr[r_rirSrmrnrprorqrrrtrvrwryrzr{rur|r}rr~rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr's 7 E E  E  M M E)&__all__rWr=Zos.pathrkZfirewallrZfirewall.core.loggerrZfirewall.core.io.icmptyperrrZfirewall.core.io.servicerr r Zfirewall.core.io.zoner r r Zfirewall.core.io.ipsetrrrZfirewall.core.io.helperrrrZfirewall.core.io.policyrrrrZfirewall.errorsrobjectrrrrrs    PK!jyy*__pycache__/ipXtables.cpython-36.opt-1.pycnu[3 Yj%@s<ddlZddlZddlmZddlmZddlmZm Z m Z m Z m Z m Z mZmZddlmZddlmZmZmZmZmZmZddlmZmZmZmZmZmZm Z ddl!m"Z"m#Z#ddl$Z$d Z%d d d gd d gd dd d d gd dd gd d d gdZ&dddZ'dddZ(ddZ)ddZ*ddZ+Gddde,Z-Gddde-Z.dS)N)runProg)log)tempFilereadfile splitArgs check_macportStrcheck_single_address check_address normalizeIP6)config) FirewallErrorINVALID_PASSTHROUGH INVALID_RULE UNKNOWN_ERROR INVALID_ADDRINVALID_ICMPTYPE) Rich_Accept Rich_Reject Rich_Drop Rich_MarkRich_MasqueradeRich_ForwardPortRich_IcmpBlock) ICMP_TYPES ICMPV6_TYPESINPUTOUTPUTFORWARD PREROUTING POSTROUTING)securityrawmanglenatfilterzicmp-host-prohibitedzicmp6-adm-prohibited)ipv4ipv6icmpz ipv6-icmpcCsddddddd}|dd}x~|D]v}y|j|}Wntk rLw$YnX|d kryt||d Wntk r~YnX|j|d ||||<q$W|S) z Inverse valid rule z-Dz--deletez-Xz--delete-chain)z-Az--appendz-Iz--insertz-Nz --new-chainN-I--insert)r*r+)index Exceptionintpop)args replace_argsret_argsargidxr6/usr/lib/python3.6/ipXtables.pycommon_reverse_rule;s(  r8cCsddddddd}|dd}x|D]x}y|j|}Wntk rLw$YnX|d kryt||d Wntk r~YnX|j|d ||||<|SWttd dS) z Reverse valid passthough rule z-Dz--deletez-Xz--delete-chain)z-Az--appendz-Iz--insertz-Nz --new-chainN-I--insertr,zno '-A', '-I' or '-N' arg)r9r:)r- ValueErrorr/r0r r)r1r2r3xr5r6r6r7common_reverse_passthrough`s,   r=cCst|}tddddddddd d d d d dddddddg}t||@dkrbttdt||@dtddddddg}t||@dkrttddS)zZ Check if passthough rule is valid (only add, insert and new chain rules are allowed) z-Cz--checkz-Dz--deletez-Rz --replacez-Lz--listz-Sz --list-rulesz-Fz--flushz-Zz--zeroz-Xz--delete-chainz-Pz--policyz-Ez--rename-chainrzarg '%s' is not allowedz-Az--appendz-Iz--insertz-Nz --new-chainzno '-A', '-I' or '-N' argN)setlenr rlist)r1Z not_allowedZneededr6r6r7common_check_passthroughs*  rAc@seZdZdZdZdZddZddZddZd d Z d d Z d dZ ddZ ddZ ddZddZddZddZddZddZdd Zdjd"d#Zd$d%Zd&d'Zd(d)Zd*d+Zdkd,d-Zd.d/Zdld1d2Zd3d4Zd5d6Zdmd8d9Zdnd:d;Z dd?Z"d@dAZ#dBdCZ$dDdEZ%dFdGZ&dHdIZ'dJdKZ(dLdMZ)dNdOZ*dPdQZ+dodRdSZ,dpdTdUZ-dqdVdWZ.dXdYZ/drdZd[Z0dsd\d]Z1dtd^d_Z2d`daZ3dudbdcZ4dddeZ5dfdgZ6dhdiZ7d!S)v ip4tablesr'TcCsd||_tj|j|_tjd|j|_|j|_|j|_ |j g|_ i|_ i|_ g|_i|_dS)Nz %s-restore)_fwr ZCOMMANDSipv_command_restore_command_detect_wait_option wait_option_detect_restore_wait_optionrestore_wait_option fill_existsavailable_tablesrich_rule_priority_countspolicy_priority_countszone_source_index_cache our_chains)selffwr6r6r7__init__s  zip4tables.__init__cCs$tjj|j|_tjj|j|_dS)N)ospathexistsrEZcommand_existsrFZrestore_command_exists)rQr6r6r7rKszip4tables.fill_existscCs|jr(|j|kr(|jgdd|D}ndd|D}tjd|j|jdj|t|j|\}}|dkrtd|jdj||f|S)NcSsg|] }d|qS)z%sr6).0itemr6r6r7 sz#ip4tables.__run..cSsg|] }d|qS)z%sr6)rWrXr6r6r7rYsz %s: %s %s rz'%s %s' failed: %s)rHrdebug2 __class__rEjoinrr;)rQr1Z_argsstatusretr6r6r7Z__runszip4tables.__runc Cs<y|j|}Wntk r"dSX||||d<dSdS)NFT)r-r;)rQrulepatternZ replacementir6r6r7 _rule_replaces zip4tables._rule_replacecCs|tko|t|kS)N)BUILT_IN_CHAINS)rQrDtablechainr6r6r7is_chain_builtinszip4tables.is_chain_builtincCs2d|g}|r|jdn |jd|j||gS)Nz-tz-Nz-X)append)rQaddrfrgrar6r6r7build_chain_ruless    zip4tables.build_chain_rulescCs8d|g}|r |d|t|g7}n |d|g7}||7}|S)Nz-tz-Iz-D)str)rQrjrfrgr-r1rar6r6r7 build_rules  zip4tables.build_rulecCst|S)N)r8)rQr1r6r6r7 reverse_ruleszip4tables.reverse_rulecCs t|dS)N)rA)rQr1r6r6r7check_passthroughszip4tables.check_passthroughcCst|S)N)r=)rQr1r6r6r7reverse_passthroughszip4tables.reverse_passthroughcCsd}y|jd}Wntk r&YnXt||dkrD||d}d}xLd D]D}y|j|}Wntk rtYqNXt||dkrN||d}qNW||fS) Nr&z-tr`-A--append-I--insert-N --new-chain)rqrrrsrtrurv)r-r;r?)rQr1rfrcrgoptr6r6r7passthrough_parse_table_chains$ z'ip4tables.passthrough_parse_table_chaincCs4yH|jd}|j||j|}d|dkr:||df}n ||df}WnFtk ry|jd}|j|d}Wntk rdSXYnXd}|ddkrd }|r| r||kr|j|nn|r0|r||kr|j||jd d d|j|}n|jjr d}nt|}d|d<|j dd|ddS)Nz%%ZONE_SOURCE%%z-mz%%ZONE_INTERFACE%%Tr-D--deleteFcSs|dS)Nrr6)r<r6r6r7(sz4ip4tables._run_replace_zone_source..)keyz-Ir,z%dr`)r|r}) r-r0r;removerisortrC_allow_zone_driftingr?insert)rQrarOrczoneZ zone_sourcerule_addr-r6r6r7_run_replace_zone_source s>             z"ip4tables._run_replace_zone_sourcecCsy|j|}Wntk r$YnXd}d}d}|j||j|}t|tkr\ttdd} xLdD]D} y|j| } Wntk rYqfXt|| dkrf|| d} qfWxhdD]`} y|j| }Wntk rYqXt||dkr||d} | dkrd}| dkrd}qW| | f} |sp| |ksP||| ksP|| |dkrZttd|| |d8<n| |kri|| <||| krd|| |<d} xHt || j D]4}||kr|rP| || |7} ||krPqW|| |d7<d ||<|j |dd| dS)a Change something like -t filter -I public_IN %%RICH_RULE_PRIORITY%% 123 or -t filter -A public_IN %%RICH_RULE_PRIORITY%% 321 into -t filter -I public_IN 4 or -t filter -I public_IN TFr`z%priority must be followed by a numberr&-t--table-A--append-I--insert-D--deleterz*nonexistent or underflow of priority countr,z%dN)rr)rrrrrr)rr)rr) r-r;r0typer/r rr?rsortedkeysr)rQraZpriority_countstokenrcrrZinsert_add_indexpriorityrfrwjrgr-pr6r6r7_set_rule_replace_priority4sj             z$ip4tables._set_rule_replace_prioritycCsPt}i}tj|j}tj|j}tj|j}x|D]}|dd} |j| dddt|jg|j| dt |jgy| j d} Wnt k rYn8X|dkrq6|d$krd d d |g| | | d <n | j | |j | |d|j | |d|j| |d} xZd%D]R} y| j | } Wnt k r,Yn(Xt| | d kr| j | | j | } qWxhtt| D]X} xPtjD]F} | | | krt| | jdo| | jd rtd| | | | <qtWqhW|j| gj| q6WxR|D]J} || }|jd| x"|D]} |jdj| dqW|jdqW|jtj|j}tjd|j|j d|j|j!fg}|j"rz|j|j"|jdt#|j ||jd\}}tj$dkr t%|j}|dk r d } xH|D]@}tj&d| |fd dd |jdstj&d!d d"| d 7} qWtj'|j|dkr:t d#|j dj||f||_||_||_dS)&Nz %%REJECT%%REJECTz --reject-withz%%ICMP%%z %%LOGTYPE%%offunicast broadcast multicastz-mpkttypez --pkt-typer`z%%RICH_RULE_PRIORITY%%z%%POLICY_PRIORITY%%r&-t--table"z"%s"z*%s rZ zCOMMIT z %s: %s %sz%s: %dz-n)stdinr,z%8d: %sr)nofmtnlr)rz'%s %s' failed: %s)rrr)rr)(rcopydeepcopyrMrNrOrdDEFAULT_REJECT_TYPErDICMPr-r;r0rrr?rangestringZ whitespace startswithendswith setdefaultriwriter]closerTstatnamerr[r\rFst_sizerJrZgetDebugLogLevelrZdebug3unlink)rQrules log_denied temp_fileZ table_rulesrMrNrOZ_rulerarcrfrwcrr1r^r_linesliner6r6r7 set_ruless                    zip4tables.set_rulesc Cs|j|dddt|jg|j|dt|jgy|jd}Wntk rRYn:X|dkr`dS|dkrd d d |g|||d<n |j|tj|j }tj|j }tj|j }|j ||d|j ||d|j |||j|}||_ ||_ ||_ |S)Nz %%REJECT%%rz --reject-withz%%ICMP%%z %%LOGTYPE%%rrrrrz-mrz --pkt-typer`z%%RICH_RULE_PRIORITY%%z%%POLICY_PRIORITY%%)rrr)rdrrDrr-r;r0rrrMrNrOrr_ip4tables__run)rQrarrcrMrNrOoutputr6r6r7set_rules.      zip4tables.set_ruleNc Csg}|r|gntj}xx|D]p}||jkr6|j|qy,|jd|ddg|jj||j|Wqtk rtjd|j|fYqXqW|S)Nz-tz-Lz-nzA%s table '%s' does not exist (or not enough permission to check).) rerrLrirr;rZdebug1rD)rQrfr_Ztablesr6r6r7get_available_tabless    zip4tables.get_available_tablescCs`d}t|jdddg}|ddkr\d}t|jdddg}|ddkrHd}tjd|j|j||S)Nrz-wz-Lz-nrz-w10z%s: %s will be using %s option.)rrErr[r\)rQrHr_r6r6r7rGs  zip4tables._detect_wait_optioncCst}|jd|jd}xJd D]B}t|j|g|jd}|ddkr"d|dkr"d |dkr"|}Pq"Wtjd |j|j|t j |j|S) Nz#foor-w--wait=2)rrzinvalid optionr`zunrecognized optionz%s: %s will be using %s option.)rr) rrrrrFrrr[r\rTr)rQrrHZ test_optionr_r6r6r7rI$s    z%ip4tables._detect_restore_wait_optioncCsVi|_i|_g|_g}x:tjD].}|j|s0q xdD]}|jd||gq6Wq W|S)N-F-X-Zz-t)rrr)rMrNrOrerrri)rQrrfflagr6r6r7build_flush_rules7s  zip4tables.build_flush_rulescCsfg}|dkrdn|}xLtjD]@}|j|s.q|dkr8qx$t|D]}|jd|d||gqBWqW|S)NZPANICDROPr%z-tz-P)rerrri)rQpolicyr_policyrfrgr6r6r7build_set_policy_rulesFs z ip4tables.build_set_policy_rulescCsNt}|dks|jdkr&|jtj|dks8|jdkrF|jtjt|S)Nr'r()r>rDupdaterrrr@)rQrDZ supportedr6r6r7supported_icmp_typesRs zip4tables.supported_icmp_typescCsgS)Nr6)rQr6r6r7build_default_tables\szip4tables.build_default_tablesrc Csi}|jdrpg|d<t|jd<xLtdD]@}|djd||djd||f|jdjd|q,W|jdr\g|d<t|jd<xtdD]}|djd||djd||f|jdjd||dkrxt|jjrddd d gndd d gD]R}|djd ||f|djd |||f|jdjtd ||fgqWqW|jdrNg|d<t|jd<xtdD]}|djd||djd||f|jdjd||dkrxv|jjrddd d gndd d gD]R}|djd ||f|djd |||f|jdjtd ||fgqWqW|jdr@g|d<t|jd<xtdD]}|djd||djd||f|jdjd||d9krxxv|jjrddd d gndd d gD]R}|djd ||f|djd |||f|jdjtd ||fgqWqxWg|d<t|jd<|djd|djd|djd|djd|jdjtdxf|jjrddd d gndd d gD]B}|djd||djd||jdjtd|qW|dkr |djd|djd|dkrF|djd|djd|djd|djd |djd!|djd"|jdjtd#xJd:D]B}|djd$||djd%||jdjtd&|qWxzd;D]r}xj|jjr dd gnd gD]N}|djd)||f|djd*||f|jdjtd+||fqWqWxJdD]B}|djd5||djd6||jdjtd7|q~Wg}xJ|D]B}||jkrqx(||D]}|jd8|gt |qWqW|S)?Nr"z -N %s_directz-A %s -j %s_directz %s_directr#r POLICIES_preZ ZONES_SOURCEZZONES POLICIES_postz-N %s_%sz-A %s -j %s_%sz%s_%sr$r%r!r&zB-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED,DNAT -j ACCEPTz-A INPUT -i lo -j ACCEPTz-N INPUT_directz-A INPUT -j INPUT_directZ INPUT_directz -N INPUT_%sz-A INPUT -j INPUT_%szINPUT_%srz^-A INPUT -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: 'z/-A INPUT -m conntrack --ctstate INVALID -j DROPz9-A INPUT %%LOGTYPE%% -j LOG --log-prefix 'FINAL_REJECT: 'z-A INPUT -j %%REJECT%%zD-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED,DNAT -j ACCEPTz-A FORWARD -i lo -j ACCEPTz-N FORWARD_directz-A FORWARD -j FORWARD_directZFORWARD_directz -N FORWARD_%sz-A FORWARD -j FORWARD_%sz FORWARD_%sINOUTz-N FORWARD_%s_%sz-A FORWARD -j FORWARD_%s_%sz FORWARD_%s_%sz`-A FORWARD -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: 'z1-A FORWARD -m conntrack --ctstate INVALID -j DROPz;-A FORWARD %%LOGTYPE%% -j LOG --log-prefix 'FINAL_REJECT: 'z-A FORWARD -j %%REJECT%%z-N OUTPUT_directz>-A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPTz-A OUTPUT -o lo -j ACCEPTz-A OUTPUT -j OUTPUT_directZ OUTPUT_directz -N OUTPUT_%sz-A OUTPUT -j OUTPUT_%sz OUTPUT_%sz-t)r r!)r)rr)r)r)r) rr>rPrerirjrCrrr) rQrZ default_rulesrgZdispatch_suffix directionZfinal_default_rulesrfrar6r6r7build_default_rules`s    $(   &*   &* &    (       "zip4tables.build_default_rulescCsf|dkrdddhS|dkr,d|jkr,dhS|dkrHd|jkrHddhS|d krbd |jkrbdhSiS) Nr&r FORWARD_IN FORWARD_OUTr$r r%r!r#)r)rQrfr6r6r7get_zone_table_chainss    zip4tables.get_zone_table_chainsc s|jjj|jdkrdnddkr4dkr4dnd} |jjj|t| g} g} x|D]} | jd| gqZWx|D]} | jd | gqvWxB|D]:} |jjj| }|dkr|j | rq| j|j d | qWx\|D]T} |jjj| }|dkr|j | rqt | rdkrq| j|j d| qWfdd}g}| rx| D]F}| rx8| D]}|j|||qdWn|rn|j||dqTWnH|rn@| rx8| D]}|j|d|qWn|rn|j|dd|S)Nrprepostr%r!TFz-iz-or'r(z-srrz-dcsVddd}d|dfdjg}|r6|j||rD|j||jdg|S)Nz-Az-D)TFz-tz%s_POLICIES_%sz%%POLICY_PRIORITY%%z-j)rextend)ingress_fragmentegress_fragmentadd_delra)rrg chain_suffixenablep_objrfr6r7_generate_policy_dispatch_rules   zSip4tables.build_policy_ingress_egress_rules.._generate_policy_dispatch_rule)r'r()r'r()r!rr) rCrZ get_policyrpolicy_base_chain_namePOLICY_CHAIN_PREFIXrirZ check_sourceis_ipv_supported_rule_addr_fragmentr)rQrrrfrgZingress_interfacesZegress_interfacesZingress_sourcesZegress_sourcesisSNATZingress_fragmentsZegress_fragments interfaceaddrrDrrrrr6)rrgrrrrfr7!build_policy_ingress_egress_rulessR        z+ip4tables.build_policy_ingress_egress_rulesFc Cs|dkr|dkrdnd}|jjj||t|d} ddddddd|} d } |rb| rbd d |d g} n,|rtd d |g} ndd |g} |s| d g7} | d|| || | g7} | gS)Nr%r!TF)rz-iz-o)r r!rrrrz-gz-Iz%s_ZONESz%%ZONE_INTERFACE%%z-Az-Dz-t)rCrrr) rQrrrrrfrgrirrrwactionrar6r6r7!build_zone_source_interface_rules6s&   z+ip4tables.build_zone_source_interface_rulescCs|jdrP|dd}|dkr$d}nd}dj|g|jjj|}ddd ||gSt|rz|dkrjttd dd d |jgSt d |rt |}n,t d |r|j d}t |dd|d}||gSdS)Nzipset:z-ddstsrc,z-mr>z --match-setzCan't match a destination MAC.macz --mac-sourcer(/rr`) rr]rCipsetZ get_dimensionrr rupperr r r split)rQrwaddressinvertrflags addr_splitr6r6r7rPs"       zip4tables._rule_addr_fragmentc Csddd|}|dkr"|dkr"dnd}|jjj||t|d} d d d d d d d |} |jjrdd |} nd |} t|r|dkrgS|| d|d|g} | j|j| || jd| g| gS)Nz-Iz-D)TFr%r!TF)rz-sz-d)r r!rrrrz%s_ZONES_SOURCEz%s_ZONESrrz%%ZONE_SOURCE%%z-tz-g)r!rr)rCrrrrrrr) rQrrrrrfrgrrrrwZzone_dispatch_chainrar6r6r7build_zone_source_address_rulesfs& z)ip4tables.build_zone_source_address_rulesc Cs>ddd|}ddd|}|dkr0|dkr0dnd }|jjj||t|d }|j|jt|d |d |d |d|d|gg} | j||d|g| j|d |d|g| j|d |d|g| j|d |d|g| j|d|d|g| j|d|d|g| j||d|dd |g| j||d|dd |g| j||d|dd |g| j||d|dd|g| j||d|dd|g|jjj|j } |jj dkr|dkr| dkr| j||d|ddddd|g | dkr| j||d|ddddd|g |dkr,| dkr,| j||d|d| g|s:| j | S)Nz-Nz-X)TFz-Az-Dr%r!TF)rz%s_logz%s_denyz%s_prez%s_postz%s_allowz-tz-jrr&r %%REJECT%%z %%LOGTYPE%%LOGz --log-prefixz "%s_REJECT: "rz "%s_DROP: "ACCEPT)rr)rrrr) rCrrrrPrr>riZ _policiestargetget_log_deniedreverse) rQrrrfrgZ add_del_chainZ add_del_rulerrrrr6r6r7build_policy_chain_rulessN       z"ip4tables.build_policy_chain_rulescCs2|sgSddd|jg}|jdk r.|d|jg7}|S)Nz-mlimitz--limitz --limit-burst)valueZburst)rQrsr6r6r7 _rule_limits  zip4tables._rule_limitcCst|jtttgkrn<|jrHt|jtttt gkrRt t dt|jn t t d|j dkrt|jttgkst|jtt gkrdSt|jtgkst|jttgkrdSn|j dkrdSdSdS)NzUnknown action %szNo rule action specified.rallowZdenyrr) relementrrrrrrrrr rr)rQ rich_ruler6r6r7_rich_rule_chain_suffixs    z!ip4tables._rich_rule_chain_suffixcCs>|j r|j rttd|jdkr(dS|jdkr6dSdSdS)NzNot log or auditrrrr)rauditr rr)rQrr6r6r7 _rich_rule_chain_suffix_from_logs   z*ip4tables._rich_rule_chain_suffix_from_logcCs|jdkrgSd|jgS)Nrz%%RICH_RULE_PRIORITY%%)r)rQrr6r6r7_rich_rule_priority_fragments z&ip4tables._rich_rule_priority_fragmentc Cs|js gS|jjj||t}ddd|}|j|}d||d||fg} | |j|7} | |ddg7} |jjr| dd |jjg7} |jjr| d d |jjg7} | |j |jj 7} | S) Nz-Az-D)TFz-tz%s_%sz-jrz --log-prefixz'%s'z --log-levelz%s) rrCrrrrr prefixlevelrr) rQrrrrf rule_fragmentrrrrar6r6r7_rich_rule_logs zip4tables._rich_rule_logc Cs|js gSddd|}|jjj||t}|j|}d||d||fg} | |j|7} | |7} t|jt krrd} n,t|jt krd} nt|jt krd} nd } | d d d | g7} | |j |jj 7} | S) Nz-Az-D)TFz-tz%s_%sZacceptZrejectZdropunknownz-jZAUDITz--type)rrCrrrrr rrrrrrr) rQrrrrfr rrrra_typer6r6r7_rich_rule_audits$ zip4tables._rich_rule_auditc Cs2|js gSddd|}|jjj||t}|j|}d||f} t|jtkrXddg} nt|jtkrddg} |jjr| d|jjg7} nnt|jt krdd g} nVt|jt krd }|jjj||t}d||f} dd d |jj g} nt t d t|jd||| g} | |j|7} | || 7} | |j|jj7} | S)Nz-Az-D)TFz%s_%sz-jrrz --reject-withrr$MARKz --set-xmarkzUnknown action %sz-t)rrCrrrrrrrrrr>r rr rr) rQrrrrfr rrrrgZ rule_actionrar6r6r7_rich_rule_actions4       zip4tables._rich_rule_actioncCs|sgSg}|jr|jr"|jdtd|jrB|dt|jg7}qtd|jr||jjd}|dt|dd|dg7}q|d|jg7}nD|jr|ddg7}|jr|jd|jj j |jd }|d |j|g7}|S) N!r(z-drrr`z-mr>rz --match-set) rrrir r r rrrCr_ipset_match_flags)rQZ rich_destr rrr6r6r7_rich_rule_destination_fragment1s&    "  z)ip4tables._rich_rule_destination_fragmentcCs|sgSg}|jr|jr"|jdtd|jrB|dt|jg7}nHtd|jr||jjd}|dt|dd|dg7}n|d|jg7}nt|dr|jr|ddg7}|jr|jd|d |jg7}nPt|d o|j r|dd g7}|jr|jd|j j j |j d }|d |j |g7}|S)Nrr(z-srrr`rz-mz --mac-sourcerr>rz --match-set) rrrir r r rhasattrrrrCrr)rQZ rich_sourcer rrr6r6r7_rich_rule_source_fragmentIs0    "    z$ip4tables._rich_rule_source_fragmentc Csddd|}d}|jjj||t} d|g} |rD| ddt|g7} |rT| d|g7} |rx| |j|j7} | |j|j7} | st |j t kr| d d d d g7} g} |r| j |j ||||| | j |j||||| | j |j||||| n"| j |d | d|g| ddg| S)Nz-Az-D)TFr&z-pz--dportz%sz-dz-m conntrackz --ctstatez NEW,UNTRACKEDz%s_allowz-tz-jr)rCrrrrr destinationrsourcerrrrir rr) rQrrprotoportrrrrfrr rr6r6r7build_policy_ports_rulesfs* z"ip4tables.build_policy_ports_rulesc Csddd|}d}|jjj||t}d|g} |r<| d|g7} |r`| |j|j7} | |j|j7} | stt|j t kr| ddd d g7} g} |r| j |j ||||| | j |j ||||| | j |j||||| n"| j |d |d |g| d dg| S)Nz-Az-D)TFr&z-pz-dz-mrz --ctstatez NEW,UNTRACKEDz%s_allowz-tz-jr)rCrrrrrrrrrrrir rr) rQrrprotocolrrrrfrr rr6r6r7build_policy_protocol_ruless& z%ip4tables.build_policy_protocol_rulesc Csddd|}d}|jjj||t} d|g} |rD| ddt|g7} |rT| d|g7} |rx| |j|j7} | |j|j7} | st |j t kr| d d d d g7} g} |r| j |j ||||| | j |j||||| | j |j||||| n"| j |d | d|g| ddg| S)Nz-Az-D)TFr&z-pz--sportz%sz-dz-mrz --ctstatez NEW,UNTRACKEDz%s_allowz-tz-jr)rCrrrrrrrrrrrrir rr) rQrrrrrrrrfrr rr6r6r7build_policy_source_ports_ruless* z)ip4tables.build_policy_source_ports_rulesc Csvd}|jjj||t} ddd|} | d| ddd|g} |rP| dd t|g7} |r`| d |g7} | d d d |g7} | gS)Nr#z-Az-D)TFz%s_allowz-tz-pz--dportz%sz-dz-jZCTz--helper)rCrrrr) rQrrrrrZ helper_nameZmodule_short_namerfrrrar6r6r7build_policy_helper_ports_ruless z)ip4tables.build_policy_helper_ports_rulesc Csddd|}|jjj||t}g} |rH| jdd|d|d|dd gn6t|rTgS| jdd|d|g|jd |dd g| S) Nz-Az-D)TFz-tr&z%s_allowz-oz-jrz-d)rCrrrrirr) rQrrrrfrrrrrr6r6r7build_zone_forward_ruless z"ip4tables.build_zone_forward_rulesc Cs,d}|jjj||tdd}ddd|}g}|rj|j|}||j|7}||j|j7}||j|j 7}nd}g} | j dd|d ||fg|d d d d dgg}|r|j|}||j|7}||j|j7}||j|j 7}nd}d}|jjj||t}| j dd|d ||fg|ddddd dg| S)Nr%T)rz-Az-D)TFrz-tz%s_%srz-oloz-jZ MASQUERADEr&z-mrz --ctstatez NEW,UNTRACKEDr) rCrrrrr rrrrri) rQrrrrfrrr rrr6r6r7build_policy_masquerade_ruless6  z'ip4tables.build_policy_masquerade_rulesc Cs d}|jjj||t} ddd|} d} |rPtd|rH| dt|7} n| |7} |rn|dkrn| dt|d 7} g} |r|j|} |j|} | |j |j 7} | |j |j 7} nd } g}|r|j |j|||d| |j d d| d | | fg| d |dt|ddd| g|S)Nr%z-Az-D)TFrr(z[%s]z:%s-rz-tz%s_%sz-pz--dportz-jZDNATz--to-destination)rCrrrr r rrr rrrrrir )rQrrrrZtoportZtoaddrrrfrrZtor rrr6r6r7build_policy_forward_port_ruless2     z)ip4tables.build_policy_forward_port_rulescCs|jdkrL|tkrLt|\}}}|r,t|nt|dt|}ddd|gS|jdkr|tkrt|\}}}|rxt|nt|dt|}ddd|gSttd |d |jdS) Nr'rz-mr)z --icmp-typer(Zicmp6z --icmpv6-typez ICMP type z not supported by )rDrrlrr rr)rQZ icmp_typer_codeZ _omit_codeZ _type_strr6r6r7_icmp_types_fragments    zip4tables._icmp_types_fragmentcCsd}|jjj||t}ddd|}|jdkrDddg}|j|j} nddg}|j|j} g} |jjj|rxd |} d } n d |} d } g} |r| |j|j 7} | |j |j 7} | || 7} |rL| j |j ||||| | j |j||||| |jr| j |j||||| n:|j|}| j d ||d||fg|j|| dd gn`|jjdkr| d kr| j || d |g| ddddd|g| j || d |g| d| g| S)Nr&z-Az-D)TFr'z-pr)z ipv6-icmpz%s_allowrz%s_denyz %%REJECT%%z-tz%s_%sz-jrz %%LOGTYPE%%rz --log-prefixz"%s_ICMP_BLOCK: ")rCrrrrDr(rquery_icmp_block_inversionrrrrrir rrrrr r)rQrrZictrrfrrrmatchrZ final_chainZ final_targetr rr6r6r7build_policy_icmp_block_rules,sJ      z'ip4tables.build_policy_icmp_block_rulesc Csd}|jjj||t}g}d}|jjj|rd}|jjdkr|rRd|t|g}nd|g}|d|dd d d d d d|g }|j||d7}nd}|rd|t|g}nd|g}|d|dd d |g}|j||S)Nr&rz %%REJECT%%rz-Iz-Dz-tz-pz%%ICMP%%z %%LOGTYPE%%z-jrz --log-prefixz"%s_ICMP_BLOCK: "r`r)rCrrrr)rrlri) rQrrrfrrZrule_idxZ ibi_targetrar6r6r7'build_policy_icmp_block_inversion_rules]s.     z1ip4tables.build_policy_icmp_block_inversion_rulescCsxd}g}||j|j7}||j|j7}g}|j|j||||||j|j||||||j|j||||||S)Nr&)rrrrrir rr)rQrrrrfr rr6r6r7*build_policy_rich_source_destination_rulessz4ip4tables.build_policy_rich_source_destination_rulescCs ||jkS)N)rD)rQrDr6r6r7rszip4tables.is_ipv_supported)N)N)r)F)F)NN)NN)NN)NN)N)N)N)8__name__ __module__ __qualname__rDrZpolicies_supportedrSrKrrdrhrkrmrnrorprxrrrrrrGrIrrrrrrrrrrrrrrr r rrrrrrr r!r"r$r&r(r+r,r-rr6r6r6r7rBsj     )Pa#    zN  0 "     & ! 1"rBc@s&eZdZdZdZdddZddZdS) ip6tablesr(Fc Csg}|jddddddddd d g |d krL|jddddddddd d d dg |jdddddddd dg |jdddddddd dg |S)Nz-Ir z-tr$z-mZrpfilterz--invertz --validmarkz-jrrrz --log-prefixzrpfilter_DROP: z-pz ipv6-icmpz$--icmpv6-type=neighbour-solicitationrz"--icmpv6-type=router-advertisement)ri)rQrrr6r6r7build_rpfilter_ruless$        zip6tables.build_rpfilter_rulesc Csddddddddd g }d }|jd j|g}|jd d d |gxT|D]L}|jd d d|d|ddddg |jjdkrF|jd d d|d|ddddg qFW|jd d dddd|g|jd d dddd|g|S)Nz ::0.0.0.0/96z::ffff:0.0.0.0/96z2002:0000::/24z2002:0a00::/24z2002:7f00::/24z2002:ac10::/28z2002:c0a8::/32z2002:a9fe::/32z2002:e000::/19Z RFC3964_IPv4r&z-tz-Nz-Iz-dz-jrz --reject-withz addr-unreachrallrz --log-prefixz"RFC3964_IPv4_REJECT: "r4r)rr3)rPrjrirCZ _log_denied)rQZ daddr_listZ chain_namerZdaddrr6r6r7build_rfc3964_ipv4_ruless4       z"ip6tables.build_rfc3964_ipv4_rulesN)F)r.r/r0rDrr2r5r6r6r6r7r1s r1)/Zos.pathrTrZfirewall.core.progrZfirewall.core.loggerrZfirewall.functionsrrrrrr r r Zfirewallr Zfirewall.errorsr rrrrrZfirewall.core.richrrrrrrrZfirewall.core.icmprrrrrerrr8r=rAobjectrBr1r6r6r6r7sB  (  $ %* oPK!}mSq~q~$__pycache__/fw_config.cpython-36.pycnu[3 Yj@sdgZddlZddlZddlZddlZddlmZddlmZddl m Z m Z m Z ddl mZmZmZddlmZmZmZddlmZmZmZdd lmZmZmZdd lmZmZm Z dd lm!Z!dd l"m#Z#Gd dde$Z%dS)FirewallConfigN)config)log)IcmpTypeicmptype_readericmptype_writer)Serviceservice_readerservice_writer)Zone zone_reader zone_writer)IPSet ipset_reader ipset_writer)Helper helper_reader helper_writer)Policy policy_reader policy_writer)errors) FirewallErrorc@s$eZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ ddZ ddZ ddZddZddZddZdd Zd!d"Zd#d$Zd%d&Zd'd(Zd)d*Zd+d,Zd-d.Zd/d0Zd1d2Zd3d4Zd5d6Zd7d8Zd9d:Zd;d<Z d=d>Z!d?d@Z"dAdBZ#dCdDZ$dEdFZ%dGdHZ&dIdJZ'dKdLZ(dMdNZ)dOdPZ*dQdRZ+dSdTZ,dUdVZ-dWdXZ.dYdZZ/d[d\Z0d]d^Z1d_d`Z2dadbZ3dcddZ4dedfZ5dgdhZ6didjZ7dkdlZ8dmdnZ9dodpZ:dqdrZ;dsdtZdydzZ?d{d|Z@d}d~ZAddZBddZCddZDddZEddZFddZGddZHddZIddZJddZKddZLddZMddZNddZOddZPddZQddZRddZSddZTddZUddZVddZWddZXddZYddZZddZ[ddZ\ddZ]ddZ^ddZ_ddZ`ddZaddZbdd„ZcddĄZdddƄZedS)rcCs||_|jdS)N)_fw_FirewallConfig__init_vars)selffwr/usr/lib/python3.6/fw_config.py__init__(szFirewallConfig.__init__cCsHd|j|j|j|j|j|j|j|j|j|j |j |j |j |j |j|jfS)Nz>%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)) __class___ipsets _icmptypes _services_zones_helpersZpolicy_objects_builtin_ipsets_builtin_icmptypes_builtin_services_builtin_zones_builtin_helpers_builtin_policy_objects_firewalld_conf _policies_direct)rrrr__repr__,s zFirewallConfig.__repr__cCs^i|_i|_i|_i|_i|_i|_i|_i|_i|_i|_ i|_ i|_ d|_ d|_ d|_dS)N)r!r"r#r$r%_policy_objectsr&r'r(r)r*r+r,r-r.)rrrrZ __init_vars6szFirewallConfig.__init_varscCs4x,t|jjD]}|j|j|j|=qWx,t|jjD]}|j|j|j|=q>Wx,t|jjD]}|j|j|j|=qlWx,t|jjD]}|j|j|j|=qWx,t|jjD]}|j|j|j|=qWx,t|jjD]}|j|j|j|=qWx.t|j jD]}|j |j|j |=q$Wx.t|j jD]}|j |j|j |=qTWx.t|j jD]}|j |j|j |=qWx.t|j jD]}|j |j|j |=qW|j r|j j|` d|_ |jr |jj|`d|_|jr(|jj|`d|_|jdS)N)listr&keyscleanupr!r'r"r(r#r)r$r*r%r,r-r.r)rxrrrr3GsV         zFirewallConfig.cleanupcCs |jjjS)N)rpoliciesZquery_lockdown)rrrrlockdown_enabled~szFirewallConfig.lockdown_enabledcCs|jjj||S)N)rr5 access_check)rkeyvaluerrrr7szFirewallConfig.access_checkcCs ||_dS)N)r,)rconfrrrset_firewalld_confsz!FirewallConfig.set_firewalld_confcCs|jS)N)r,)rrrrget_firewalld_confsz!FirewallConfig.get_firewalld_confcCs(tjjtjs|jjn |jjdS)N)ospathexistsrZFIREWALLD_CONFr,clearread)rrrrupdate_firewalld_confs z$FirewallConfig.update_firewalld_confcCs ||_dS)N)r-)rr5rrr set_policiesszFirewallConfig.set_policiescCs|jS)N)r-)rrrr get_policiesszFirewallConfig.get_policiescCs,tjjtjs|jjjn |jjjdS)N) r=r>r?rZLOCKDOWN_WHITELISTr-Zlockdown_whitelistr3rA)rrrrupdate_lockdown_whitelistsz(FirewallConfig.update_lockdown_whitelistcCs ||_dS)N)r.)rZdirectrrr set_directszFirewallConfig.set_directcCs|jS)N)r.)rrrr get_directszFirewallConfig.get_directcCs(tjjtjs|jjn |jjdS)N)r=r>r?rZFIREWALLD_DIRECTr.r3rA)rrrr update_directs zFirewallConfig.update_directcCs$ttt|jjt|jjS)N)sortedsetr1r!r2r&)rrrr get_ipsetsszFirewallConfig.get_ipsetscCs$|jr||j|j<n ||j|j<dS)N)builtinr&namer!)robjrrr add_ipsetszFirewallConfig.add_ipsetcCs8||jkr|j|S||jkr(|j|Sttj|dS)N)r!r&rr INVALID_IPSET)rrMrrr get_ipsets     zFirewallConfig.get_ipsetcCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._ipsets[%s] != objz'%s' not a built-in ipset)rMr!rr NO_DEFAULTSr& _remove_ipset)rrNrrrload_ipset_defaultss    z"FirewallConfig.load_ipset_defaultscCs|jS)N) export_config)rrNrrrget_ipset_configszFirewallConfig.get_ipset_configcCsj|jrPtj|}|j|tj|_d|_|j|jkr:d|_|j|t||S|j|t||SdS)NF) rLcopy import_configrETC_FIREWALLD_IPSETSr>defaultrOr)rrNr:r4rrrset_ipset_configs     zFirewallConfig.set_ipset_configcCsx||jks||jkr$ttjd|t}|j||j|||_d||_ t j |_ d|_ d|_t||j||S)Nznew_ipset(): '%s'z%s.xmlFT)r!r&rr NAME_CONFLICTr check_namerXrMfilenamerrYr>rLrZrrO)rrMr:r4rrr new_ipsets     zFirewallConfig.new_ipsetcCstjj|}tjj|}tjj|s|tjkrx|jjD]D}|j|}|j |kr:|j|=|j |j krvd|j |j fSd|fSq:WnHxF|j jD]8}|j |}|j |kr|j |=|j |jkrd|fSdSqWdSt j d|yt||}Wn0tk r}zt jd||dSd}~XnX|j |j krJ|j |jkrJ|j|d|fS|tjkr|j |jkr|j|j j|_||j|j <d|fS|j |j kr|j |j =||j |j <|j |jkrd|fSd Sd S) NupdateremovezLoading ipset file '%s'z"Failed to load ipset file '%s': %snew)NN)NN)NN)NN)NN)r=r>basenamedirnamer?rrYr!r2r^rMr&rdebug1r ExceptionerrorrOrZ)rrMr^r>r4rNmsgrrrupdate_ipset_from_pathsP                z%FirewallConfig.update_ipset_from_pathcCs|j|jkrttj|j|jtjkr>ttjd|jtjfd|j|jf}yt j |d|Wn:t k r}zt j d||tj|WYdd}~XnX|j|j=dS)Nz '%s' != '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr!rrrPr>rrYINVALID_DIRECTORYshutilmoverfrrgr=ra)rrNrMrhrrrrS8s   zFirewallConfig._remove_ipsetcCs$|js|j r ttjd|jdS)Nz'%s' is built-in ipset)rLrZrrZ BUILTIN_IPSETrM)rrNrrrcheck_builtin_ipsetIsz"FirewallConfig.check_builtin_ipsetcCs|j||j|dS)N)rmrS)rrNrrr remove_ipsetNs zFirewallConfig.remove_ipsetcCs$|j||j||}|j||S)N)rm _copy_ipsetrS)rrNrMr_rrr rename_ipsetRs   zFirewallConfig.rename_ipsetcCs|j||jS)N)r_rU)rrNrMrrrroXszFirewallConfig._copy_ipsetcCs$ttt|jjt|jjS)N)rIrJr1r"r2r')rrrr get_icmptypes]szFirewallConfig.get_icmptypescCs$|jr||j|j<n ||j|j<dS)N)rLr'rMr")rrNrrr add_icmptypeaszFirewallConfig.add_icmptypecCs8||jkr|j|S||jkr(|j|Sttj|dS)N)r"r'rrINVALID_ICMPTYPE)rrMrrr get_icmptypegs     zFirewallConfig.get_icmptypecCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._icmptypes[%s] != objz'%s' not a built-in icmptype)rMr"rrrRr'_remove_icmptype)rrNrrrload_icmptype_defaultsns    z%FirewallConfig.load_icmptype_defaultscCs|jS)N)rU)rrNrrrget_icmptype_configzsz"FirewallConfig.get_icmptype_configcCsj|jrPtj|}|j|tj|_d|_|j|jkr:d|_|j|t||S|j|t||SdS)NF) rLrWrXrETC_FIREWALLD_ICMPTYPESr>rZrrr)rrNr:r4rrrset_icmptype_config}s     z"FirewallConfig.set_icmptype_configcCsx||jks||jkr$ttjd|t}|j||j|||_d||_ t j |_ d|_ d|_t||j||S)Nznew_icmptype(): '%s'z%s.xmlFT)r"r'rrr\rr]rXrMr^rrxr>rLrZrrr)rrMr:r4rrr new_icmptypes     zFirewallConfig.new_icmptypecCstjj|}tjj|}tjj|s|tjkrx|jjD]D}|j|}|j |kr:|j|=|j |j krvd|j |j fSd|fSq:WnHxF|j jD]8}|j |}|j |kr|j |=|j |jkrd|fSdSqWdSt j d|yt||}Wn0tk r}zt jd||dSd}~XnX|j |j krJ|j |jkrJ|j|d|fS|tjkr|j |jkr|j|j j|_||j|j <d|fS|j |j kr|j |j =||j |j <|j |jkrd|fSd Sd S) Nr`razLoading icmptype file '%s'z%Failed to load icmptype file '%s': %srb)NN)NN)NN)NN)NN)r=r>rcrdr?rrxr"r2r^rMr'rrerrfrgrrrZ)rrMr^r>r4rNrhrrrupdate_icmptype_from_pathsP                z(FirewallConfig.update_icmptype_from_pathcCs|j|jkrttj|j|jtjkr>ttjd|jtjfd|j|jf}yt j |d|Wn:t k r}zt j d||tj|WYdd}~XnX|j|j=dS)Nz '%s' != '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr"rrrsr>rrxrjrkrlrfrrgr=ra)rrNrMrhrrrrus  zFirewallConfig._remove_icmptypecCs$|js|j r ttjd|jdS)Nz'%s' is built-in icmp type)rLrZrrZBUILTIN_ICMPTYPErM)rrNrrrcheck_builtin_icmptypesz%FirewallConfig.check_builtin_icmptypecCs|j||j|dS)N)r|ru)rrNrrrremove_icmptypes zFirewallConfig.remove_icmptypecCs$|j||j||}|j||S)N)r|_copy_icmptyperu)rrNrMrzrrrrename_icmptypes   zFirewallConfig.rename_icmptypecCs|j||jS)N)rzrU)rrNrMrrrr~szFirewallConfig._copy_icmptypecCs$ttt|jjt|jjS)N)rIrJr1r#r2r()rrrr get_services szFirewallConfig.get_servicescCs$|jr||j|j<n ||j|j<dS)N)rLr(rMr#)rrNrrr add_serviceszFirewallConfig.add_servicecCs<||jkr|j|S||jkr(|j|Sttjd|dS)Nzget_service(): '%s')r#r(rrINVALID_SERVICE)rrMrrr get_services     zFirewallConfig.get_servicecCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._services[%s] != objz'%s' not a built-in service)rMr#rrrRr(_remove_service)rrNrrrload_service_defaultss    z$FirewallConfig.load_service_defaultscCsr|j}g}x\tdD]P}|j|d|krN|jtjt||j|dq|j||j|dqWt|S)Nr)export_config_dictrangeIMPORT_EXPORT_STRUCTUREappendrWdeepcopygetattrtuple)rrN conf_dict conf_listirrrget_service_config's"z!FirewallConfig.get_service_configcCs|jS)N)r)rrNrrrget_service_config_dict3sz&FirewallConfig.get_service_config_dictcCsi}x&t|D]\}}|||j|d<qW|jr|tj|}|j|tj|_d|_|j|jkrfd|_|j |t ||S|j|t ||SdS)NrF) enumeraterrLrWimport_config_dictrETC_FIREWALLD_SERVICESr>rZrr )rrNr:rrr9r4rrrset_service_config6s      z!FirewallConfig.set_service_configcCsj|jrPtj|}|j|tj|_d|_|j|jkr:d|_|j|t||S|j|t||SdS)NF) rLrWrrrr>rZrr )rrNr:r4rrrset_service_config_dictJs     z&FirewallConfig.set_service_config_dictcCs||jks||jkr$ttjd|i}x&t|D]\}}||tj|d<q2Wt}|j||j |||_ d||_ t j |_d|_d|_t||j||S)Nznew_service(): '%s'rz%s.xmlFT)r#r(rrr\rrrr]rrMr^rrr>rLrZr r)rrMr:rrr9r4rrr new_serviceZs"     zFirewallConfig.new_servicecCsx||jks||jkr$ttjd|t}|j||j|||_d||_ t j |_ d|_ d|_t||j||S)Nznew_service(): '%s'z%s.xmlFT)r#r(rrr\rr]rrMr^rrr>rLrZr r)rrMr:r4rrrnew_service_dictqs     zFirewallConfig.new_service_dictcCstjj|}tjj|}tjj|s|tjkrx|jjD]D}|j|}|j |kr:|j|=|j |j krvd|j |j fSd|fSq:WnHxF|j jD]8}|j |}|j |kr|j |=|j |jkrd|fSdSqWdSt j d|yt||}Wn0tk r}zt jd||dSd}~XnX|j |j krJ|j |jkrJ|j|d|fS|tjkr|j |jkr|j|j j|_||j|j <d|fS|j |j kr|j |j =||j |j <|j |jkrd|fSd Sd S) Nr`razLoading service file '%s'z$Failed to load service file '%s': %srb)NN)NN)NN)NN)NN)r=r>rcrdr?rrr#r2r^rMr(rrer rfrgrrZ)rrMr^r>r4rNrhrrrupdate_service_from_pathsP                z'FirewallConfig.update_service_from_pathcCs|j|jkrttj|j|jtjkr>ttjd|jtjfd|j|jf}yt j |d|Wn:t k r}zt j d||tj|WYdd}~XnX|j|j=dS)Nz '%s' != '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr#rrrr>rrrjrkrlrfrrgr=ra)rrNrMrhrrrrs  zFirewallConfig._remove_servicecCs$|js|j r ttjd|jdS)Nz'%s' is built-in service)rLrZrrZBUILTIN_SERVICErM)rrNrrrcheck_builtin_servicesz$FirewallConfig.check_builtin_servicecCs|j||j|dS)N)rr)rrNrrrremove_services zFirewallConfig.remove_servicecCs$|j||j||}|j||S)N)r _copy_servicer)rrNrMrrrrrename_services   zFirewallConfig.rename_servicecCs|j||jS)N)rr)rrNrMrrrrszFirewallConfig._copy_servicecCs$ttt|jjt|jjS)N)rIrJr1r$r2r))rrrr get_zonesszFirewallConfig.get_zonescCs$|jr||j|j<n ||j|j<dS)N)rLr)rMr$)rrNrrradd_zoneszFirewallConfig.add_zonecCs(||jkr|j|=||jkr$|j|=dS)N)r)r$)rrMrrr forget_zones  zFirewallConfig.forget_zonecCs<||jkr|j|S||jkr(|j|Sttjd|dS)Nzget_zone(): %s)r$r)rr INVALID_ZONE)rrMrrrget_zones     zFirewallConfig.get_zonecCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._zones[%s] != objz'%s' not a built-in zone)rMr$rrrRr) _remove_zone)rrNrrrload_zone_defaultss    z!FirewallConfig.load_zone_defaultscCsr|j}g}x\tdD]P}|j|d|krN|jtjt||j|dq|j||j|dqWt|S)Nr)rrrrrWrrr)rrNrrrrrrget_zone_configs"zFirewallConfig.get_zone_configcCs|jS)N)r)rrNrrrget_zone_config_dictsz#FirewallConfig.get_zone_config_dictcCsi}x&t|D]\}}|||j|d<qW|jrtj|}||_|j|tj|_d|_|j|jkrld|_ |j |t ||S||_|j|t ||SdS)NrF) rrrLrW fw_configrrETC_FIREWALLD_ZONESr>rZrr )rrNr:rrr9r4rrrset_zone_config s$     zFirewallConfig.set_zone_configcCsv|jrVtj|}||_|j|tj|_d|_|j|jkr@d|_|j|t ||S||_|j|t ||SdS)NF) rLrWrrrrr>rZrr )rrNr:r4rrrset_zone_config_dict6s     z#FirewallConfig.set_zone_config_dictcCs||jks||jkr$ttjd|i}x&t|D]\}}||tj|d<q2Wt}||_|j ||j |||_ d||_ t j|_d|_d|_t||j||S)Nznew_zone(): '%s'rz%s.xmlFT)r$r)rrr\rr rrr]rrMr^rrr>rLrZr r)rrMr:rrr9r4rrrnew_zoneHs"    zFirewallConfig.new_zonecCs~||jks||jkr$ttjd|t}||_|j||j|||_ d||_ t j |_ d|_d|_t||j||S)Nznew_zone(): '%s'z%s.xmlFT)r$r)rrr\r rr]rrMr^rrr>rLrZr r)rrMr:r4rrr new_zone_dict_s    zFirewallConfig.new_zone_dictcCstjj|}tjj|}tjj|s|jtjrx|jj D]D}|j|}|j |kr<|j|=|j |j krxd|j |j fSd|fSqrcrdr? startswithrrr$r2r^rMr)rrer rfrgrlenrrZ)rrMr^r>r4rNrhrrrupdate_zone_from_pathrsZ                z$FirewallConfig.update_zone_from_pathcCs|j|jkrttj|j|jjtjs@ttj d|jtjfd|j|jf}yt j |d|Wn:t k r}zt jd||tj|WYdd}~XnX|j|j=dS)Nz'%s' doesn't start with '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr$rrrr>rrrrjrkrlrfrrgr=ra)rrNrMrhrrrrs zFirewallConfig._remove_zonecCs$|js|j r ttjd|jdS)Nz'%s' is built-in zone)rLrZrrZ BUILTIN_ZONErM)rrNrrrcheck_builtin_zonesz!FirewallConfig.check_builtin_zonecCs|j||j|dS)N)rr)rrNrrr remove_zones zFirewallConfig.remove_zonec CsN|j||j}|j|y|j||}Wn|j|j|YnX|S)N)rrrrrM)rrNrMZobj_confrrrr rename_zones  zFirewallConfig.rename_zonecCs$ttt|jjt|jjS)N)rIrJr1r0r2r+)rrrrget_policy_objectssz!FirewallConfig.get_policy_objectscCs$|jr||j|j<n ||j|j<dS)N)rLr+rMr0)rrNrrradd_policy_objectsz FirewallConfig.add_policy_objectcCs<||jkr|j|S||jkr(|j|Sttjd|dS)Nzget_policy_object(): %s)r0r+rrINVALID_POLICY)rrMrrrget_policy_objects     z FirewallConfig.get_policy_objectcCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._policy_objects[%s] != objz'%s' not a built-in policy)rMr0rrrRr+_remove_policy_object)rrNrrrload_policy_object_defaultss    z*FirewallConfig.load_policy_object_defaultscCs|jS)N)r)rrNrrrget_policy_object_config_dictsz,FirewallConfig.get_policy_object_config_dictcCsv|jrVtj|}||_|j|tj|_d|_|j|jkr@d|_|j|t ||S||_|j|t ||SdS)NF) rLrWrrrETC_FIREWALLD_POLICIESr>rZrr)rrNr:r4rrrset_policy_object_config_dicts     z,FirewallConfig.set_policy_object_config_dictcCs~||jks||jkr$ttjd|t}||_|j||j|||_ d||_ t j |_ d|_d|_t||j||S)Nznew_policy_object(): '%s'z%s.xmlFT)r0r+rrr\rrr]rrMr^rrr>rLrZrr)rrMr:r4rrrnew_policy_object_dicts    z%FirewallConfig.new_policy_object_dictcCstjj|}tjj|}tjj|s|jtjrx|jj D]D}|j|}|j |kr<|j|=|j |j krxd|j |j fSd|fSqrcrdr?rrrr0r2r^rMr+rrerrfrgrrrrZ)rrMr^r>r4rNrhrrrupdate_policy_object_from_path,sZ                z-FirewallConfig.update_policy_object_from_pathcCs|j|jkrttj|j|jjtjs@ttj d|jtjfd|j|jf}yt j |d|Wn:t k r}zt jd||tj|WYdd}~XnX|j|j=dS)Nz'%s' doesn't start with '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr0rrrr>rrrrjrkrlrfrrgr=ra)rrNrMrhrrrrys z$FirewallConfig._remove_policy_objectcCs$|js|j r ttjd|jdS)Nz'%s' is built-in policy)rLrZrrZBUILTIN_POLICYrM)rrNrrrcheck_builtin_policy_objectsz*FirewallConfig.check_builtin_policy_objectcCs|j||j|dS)N)rr)rrNrrrremove_policy_objects z#FirewallConfig.remove_policy_objectcCs$|j||j||}|j||S)N)r_copy_policy_objectr)rrNrMZnew_policy_objectrrrrename_policy_objects   z#FirewallConfig.rename_policy_objectcCs|j||jS)N)rr)rrNrMrrrrsz"FirewallConfig._copy_policy_objectcCs$ttt|jjt|jjS)N)rIrJr1r%r2r*)rrrr get_helpersszFirewallConfig.get_helperscCs$|jr||j|j<n ||j|j<dS)N)rLr*rMr%)rrNrrr add_helperszFirewallConfig.add_helpercCs8||jkr|j|S||jkr(|j|Sttj|dS)N)r%r*rrINVALID_HELPER)rrMrrr get_helpers     zFirewallConfig.get_helpercCst|j|jkrttj|jnB|j|j|kr@ttjd|jn|j|jkr^ttjd|j|j||j|jS)Nzself._helpers[%s] != objz'%s' not a built-in helper)rMr%rrrRr*_remove_helper)rrNrrrload_helper_defaultss    z#FirewallConfig.load_helper_defaultscCs|jS)N)rU)rrNrrrget_helper_configsz FirewallConfig.get_helper_configcCsj|jrPtj|}|j|tj|_d|_|j|jkr:d|_|j|t||S|j|t||SdS)NF) rLrWrXrETC_FIREWALLD_HELPERSr>rZrr)rrNr:r4rrrset_helper_configs     z FirewallConfig.set_helper_configcCsx||jks||jkr$ttjd|t}|j||j|||_d||_ t j |_ d|_ d|_t||j||S)Nznew_helper(): '%s'z%s.xmlFT)r%r*rrr\rr]rXrMr^rrr>rLrZrr)rrMr:r4rrr new_helpers     zFirewallConfig.new_helpercCstjj|}tjj|}tjj|s|tjkrx|jjD]D}|j|}|j |kr:|j|=|j |j krvd|j |j fSd|fSq:WnHxF|j jD]8}|j |}|j |kr|j |=|j |jkrd|fSdSqWdSt j d|yt||}Wn0tk r}zt jd||dSd}~XnX|j |j krJ|j |jkrJ|j|d|fS|tjkr|j |jkr|j|j j|_||j|j <d|fS|j |j kr|j |j =||j |j <|j |jkrd|fSd Sd S) Nr`razLoading helper file '%s'z#Failed to load helper file '%s': %srb)NN)NN)NN)NN)NN)r=r>rcrdr?rrr%r2r^rMr*rrerrfrgrrZ)rrMr^r>r4rNrhrrrupdate_helper_from_pathsP                z&FirewallConfig.update_helper_from_pathcCs|j|jkrttj|j|jtjkr>ttjd|jtjfd|j|jf}yt j |d|Wn:t k r}zt j d||tj|WYdd}~XnX|j|j=dS)Nz '%s' != '%s'z %s/%s.xmlz%s.oldzBackup of file '%s' failed: %s)rMr%rrrr>rrrjrkrlrfrrgr=ra)rrNrMrhrrrr&s   zFirewallConfig._remove_helpercCs$|js|j r ttjd|jdS)Nz'%s' is built-in helper)rLrZrrZBUILTIN_HELPERrM)rrNrrrcheck_builtin_helper7sz#FirewallConfig.check_builtin_helpercCs|j||j|dS)N)rr)rrNrrr remove_helper<s zFirewallConfig.remove_helpercCs$|j||j||}|j||S)N)r _copy_helperr)rrNrMrrrr rename_helper@s   zFirewallConfig.rename_helpercCs|j||jS)N)rrU)rrNrMrrrrFszFirewallConfig._copy_helperN)f__name__ __module__ __qualname__rr/rr3r6r7r;r<rBrCrDrErFrGrHrKrOrQrTrVr[r_rirSrmrnrprorqrrrtrvrwryrzr{rur|r}rr~rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr's 7 E E  E  M M E)&__all__rWr=Zos.pathrkZfirewallrZfirewall.core.loggerrZfirewall.core.io.icmptyperrrZfirewall.core.io.servicerr r Zfirewall.core.io.zoner r r Zfirewall.core.io.ipsetrrrZfirewall.core.io.helperrrrZfirewall.core.io.policyrrrrZfirewall.errorsrobjectrrrrrs    PK!pnWMlMl__pycache__/fw.cpython-36.pycnu[3 Yj@sdgZddlZddlZddlZddlZddlZddlmZddlm Z ddl m Z ddl m Z ddl m Z ddl mZdd l mZdd lmZdd lmZdd lmZdd lmZddlmZddlmZddlmZddlmZddl m!Z!ddl"m#Z#ddl$m%Z%m&Z&ddl'm(Z(ddl)m*Z*ddl+m,Z,ddl-m.Z.ddl/m0Z0ddl1m2Z2m3Z3ddl4m5Z5ddl6m7Z7ddl8m9Z9ddl:m;Z;ddlmZ>Gd!dde?Z@dS)"FirewallN)config) functions) ipXtables)ebtables)nftables)ipset)modules)FirewallIcmpType)FirewallService) FirewallZone)FirewallDirect)FirewallConfig)FirewallPolicies) FirewallIPSet)FirewallTransaction)FirewallHelper)FirewallPolicy)nm_get_bus_namenm_get_interfaces_in_zone)log)firewalld_conf)Direct)service_reader)icmptype_reader) zone_readerZone) ipset_reader) IPSET_TYPES) helper_reader) policy_reader)errors) FirewallErrorc@seZdZdeddZddZddZdd Zd d Zdfd d ZddZ dgddZ ddZ ddZ ddZ ddZddZddZddZd d!Zd"d#Zd$d%Zd&d'Zdhd)d*Zdid+d,Zd-d.Zdjd/d0Zdkd1d2Zdld3d4Zd5d6Zd7d8Zd9d:Zd;d<Zd=d>Z d?d@Z!dAdBZ"dCdDZ#dEdFZ$dGdHZ%dIdJZ&dKdLZ'dMdNZ(dmdOdPZ)dQdRZ*dSdTZ+dUdVZ,dWdXZ-dYdZZ.d[d\Z/d]d^Z0d_d`Z1dadbZ2dcddZ3d(S)nrFcCsttj|_||_|jr>d|_d|_d|_d|_t |_ d|_ nrt j ||_d|_g|_t j||_d|_g|_tj|_d|_tj|_d|_g|_ tj||_d|_ tj|_t||_t||_t||_ t!||_"t#||_t$|_%t&||_t'||_(t)||_*|j+dS)NFT),rrFIREWALLD_CONF_firewalld_conf_offlineip4tables_enabledip6tables_enabledebtables_enabled ipset_enabledripset_supported_typesnftables_enabledr ip4tablesip4tables_backendipv4_supported_icmp_types ip6tablesip6tables_backendipv6_supported_icmp_typesrebtables_backendr ipset_backendrnftables_backendr modules_backendr icmptyper servicer zoner directrrpoliciesrrhelperrpolicy_Firewall__init_vars)selfZoffliner?/usr/lib/python3.6/fw.py__init__CsB               zFirewall.__init__cCsDd|j|j|j|j|j|j|j|j|j|j |j |j |j |j |jfS)Nz:%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)) __class__r&r'r(_state_panic _default_zone_module_refcount_markscleanup_on_exitcleanup_modules_on_exitipv6_rpfilter_enabledr)_individual_calls _log_denied)r>r?r?r@__repr__ks   zFirewall.__repr__cCsjd|_d|_d|_i|_g|_tj|_tj|_ tj |_ tj |_ tj|_tj|_tj|_tj|_tj|_dS)NZINITF)rCrDrErFrGrZFALLBACK_CLEANUP_ON_EXITrHZ FALLBACK_CLEANUP_MODULES_ON_EXITrIZFALLBACK_IPV6_RPFILTERrJZFALLBACK_INDIVIDUAL_CALLSrKZFALLBACK_LOG_DENIEDrLZFALLBACK_FIREWALL_BACKEND_firewall_backendZFALLBACK_FLUSH_ALL_ON_RELOAD_flush_all_on_reloadZFALLBACK_RFC3964_IPV4 _rfc3964_ipv4ZFALLBACK_ALLOW_ZONE_DRIFTING_allow_zone_drifting)r>r?r?r@Z __init_varstszFirewall.__init_varscCs|jr$d|jjkr$tjdd|_|jrHd|jjkrHtjdd|_|jrld|jjkrltjdd|_|j r|j r|j rtj dt j ddS)Nfilterziptables is not usable.Fzip6tables is not usable.zebtables is not usable.zNo IPv4 and IPv6 firewall.) r&r-get_available_tablesrinfo1r'r0r(r2r+fatalsysexit)r>r?r?r@ _check_tabless     zFirewall._check_tablesc Cszy|jjWn*tk r8tjdd|_g|_YnX|jj|_|jj |jj s||jj rltjdntjdd|_ |j r|jjd|_n|j r|jj|_ng|_|jj |jj s|jj rtjdntjdd|_|j r|jjd|_n|jr|jj|_ng|_|jj |jj sN|jj r>tjd ntjd d|_|jrv|j rv|jj rvtjd dS) Nz4ipset not usable, disabling ipset usage in firewall.FzFiptables-restore is missing, using individual calls for IPv4 firewall.zCiptables-restore and iptables are missing, disabling IPv4 firewall.ipv4zGip6tables-restore is missing, using individual calls for IPv6 firewall.zEip6tables-restore and ip6tables are missing, disabling IPv6 firewall.ipv6zHebtables-restore is missing, using individual calls for bridge firewall.zEebtables-restore and ebtables are missing, disabling bridge firewall.zSebtables-restore is not supporting the --noflush option, will therefore not be used)r3Zset_list ValueErrorrwarningr)r*Zset_supported_typesr-Z fill_existsrestore_command_existsZcommand_existsr&r+r4Zsupported_icmp_typesr.r0r'r1r2r(rKrestore_noflush_optiondebug1)r>r?r?r@ _start_checksL               zFirewall._start_checkc>Cstj}tjdtjy|jjWn8tk rZ}ztj|tjdWYdd}~Xn"X|jj drt|jj d}|jj dr|jj d}|dk r|j dBkrd|_ tjd|j |jj d r|jj d }|dk r|j dCkrd |_ |dk r|j dDkrd|_ tjd |j |jj drv|jj d}|dk rv|j dEkrvtjdy|j jWntk rtYnX|jj dr|jj d}|dk r|j dFkrd|_|j dGkrd |_|jrtjdn tjd|jj dr"|jj d}|dk r"|j dHkr"tjdd |_|jj drt|jj d}|dksT|j dkr\d|_n|j |_tjd|j|jj dr|jj d|_tjd|j|jj dr|jj d}|j dIkrd|_nd |_tjd|j|jj dr&|jj d}|j dJkrd|_nd |_tjd|j|jj dr||jj d}|j dKkrVd|_nd |_|jsntjdtjd |j|jjtj|j|j|j|js|jtjd!y|j jjWnZtk r }z<|j jrtjd"|j jj |ntjd"|j jj |WYdd}~XnX|jj!tj|j |j"tj#d#|j"tj$d#|j"tj%d$|j"tj&d$t'|j(j)d%krtjd&|j"tj*d'|j"tj+d'|j"tj,d(|j"tj-d(t'|j.j/d%krtjd)|j"tj0d*|j"tj1d*t'|j2j3d%kr&tj4d+t5j6d,|j"tj7d-|j"tj8d-d}x.dLD]&}||j2j3krLtj4d1|d }qLW|rt5j6d,||j2j3krd2|j2j3krd2}nd3|j2j3krd3}nd.}tjd4|||}n tjd5|t9tj:} t;jj?| |jj@tj| |jA||_B|jrdS|jCtjDd%krtEjE} tF|} |s|jG| d8|r|s|jHr|jIjJr| jKd | jL|r|rtjd9|jMjN|jO| d8| jKd | jL|jHrX|jIjJrXtjd:|jIjPtjd;|jQ| d8tjd<|j2jR| d8|j2jSd|jB| d8tjd=|jTjU| d8| jKd | jL|j>jVrVtjd>|j>jW| y| jKd | jLWnXtk r>} z$t| jXd?| jYr&| jYnd@WYdd} ~ Xntk rTYnX~ tjDd,krtEjE} tjZdA| | dS)MNz"Loading firewalld config file '%s'z0Using fallback firewalld configuration settings. DefaultZoneZ CleanupOnExitnofalseFzCleanupOnExit is set to '%s'ZCleanupModulesOnExityestrueTz#CleanupModulesOnExit is set to '%s'ZLockdownzLockdown is enabledZ IPv6_rpfilterzIPv6 rpfilter is enabledzIPV6 rpfilter is disabledZIndividualCallszIndividualCalls is enabled LogDeniedZoffzLogDenied is set to '%s'ZFirewallBackendzFirewallBackend is set to '%s'ZFlushAllOnReloadzFlushAllOnReload is set to '%s'Z RFC3964_IPv4zRFC3964_IPv4 is set to '%s'ZAllowZoneDriftingzAllowZoneDrifting is enabled. This is considered an insecure configuration option. It will be removed in a future release. Please consider disabling it now.z AllowZoneDrifting is set to '%s'zLoading lockdown whitelistz*Failed to load lockdown whitelist '%s': %srr6rzNo icmptypes found.r;r7zNo services found.r8zNo zones found.rTr<blockdroptrustedzZone '%s' is not available.ZpublicZexternalz+Default zone '%s' is not valid. Using '%s'.zUsing default zone '%s'zLoading direct rules file '%s'z)Failed to load direct rules file '%s': %s)use_transactionzUnloading firewall moduleszApplying ipsetszApplying default rule setzApplying used zoneszApplying used policiesz2Applying direct chains rules and passthrough rulesz Direct: %srNz%Flushing and applying took %f seconds)rdre)rfrg)rdre)rfrg)rdre)rfrg)rfrg)rdre)rdre)rdre)rirjrk)[rZ FALLBACK_ZONErrar#r$read Exceptionr^getlowerrHrIr:Zenable_lockdownr"rJrKrLrOrPrQrRr%Zset_firewalld_confcopydeepcopy_select_firewall_backendrbZlockdown_whitelistZquery_lockdownerrorfilenameZ set_policies_loaderZFIREWALLD_IPSETSZETC_FIREWALLD_IPSETSZFIREWALLD_ICMPTYPESZETC_FIREWALLD_ICMPTYPESlenr6 get_icmptypesZFIREWALLD_HELPERSZETC_FIREWALLD_HELPERSZFIREWALLD_SERVICESZETC_FIREWALLD_SERVICESr7 get_servicesZFIREWALLD_ZONESZETC_FIREWALLD_ZONESr8 get_zonesrWrXrYZFIREWALLD_POLICIESZETC_FIREWALLD_POLICIESrZFIREWALLD_DIRECTospathexistsr9Zset_permanent_configZ set_direct check_zonerErZZgetDebugLogLeveltimerflushr)rZ has_ipsetsexecuteclearr5unload_firewall_modulesapply_default_tablesZ apply_ipsetsapply_default_rulesZ apply_zoneschange_default_zoner<Zapply_policiesZhas_configurationZ apply_directcodemsgZdebug2)r>reloadcomplete_reloadZ default_zonervaluertzr8objZtm1 transactioneZtm2r?r?r@_startst                                                           .zFirewall._startc CsHy |jWn&tk r2d|_|jdYnXd|_|jddS)NFAILEDACCEPTRUNNING)rrnrC set_policy)r>r?r?r@starts  zFirewall.startc Cshtjj|sdS|rZ|jtjrV|dkrVt}tjj||_|j |j||_d|_ nd}x|t tj |D]h}|j ds|jtjrl|dkrltjjd||frl|jd||f|ddqld||f}tjd||y|dkrt||}|j|jjkr8|jj|j}tjd ||j|j|j|jj|jn|jjtjrNd|_ y|jj|Wn<tk r} ztjd |jt| fWYdd} ~ XnX|jjtj|n|d krFt||}|j|jjkr|jj |j}tjd ||j|j|j|jj!|jn|jjtjr$d|_ |jj"||jj"tj|n.|dkrnt#|||d }|rdtjj|tjj|d df|_|j |jtj|} |j|j$j%kr|j$j&|j}|j$j'|j|j(rtjd||j|||j)|ntjd ||j|j|jn|jjtjr,d|_ d| _ |jj*| |r^tjd||j|||j)|n |j$j*|n|dkrDt+||}|j|j,j-kr|j,j.|j}tjd ||j|j|j|j,j/|jn|jjtjrd|_ y|j,j0|Wn<tk r,} ztj1d |jt| fWYdd} ~ XnX|jj0tj|n0|dkrt2||}|j|j3j4kr|j3j5|j}tjd ||j|j|j|j3j6|jn|jjtjrd|_ |j3j7||jj7tj|n|dkrht8||}|j|j9j:kr2|j9j;|j}tjd ||j|j|j|j9j<|jn|jjtjrHd|_ |j9j=||jj>tj|n tj?d|Wqltk r} ztj@d||| WYdd} ~ XqltAk rtj@d||tjBYqlXqlW|rd|j(rd|j|j$j%krX|j$j&|j}tjd||j|j|jy|j$j'|jWntAk rHYnX|jjC|j|j$j*|dS)Nr8Fz.xmlz%s/%sT)combinezLoading %s file '%s'r6z Overloads %s '%s' ('%s/%s')z%s: %s, ignoring for run-time.r7)Z no_check_namerz Combining %s '%s' ('%s/%s')rr;r<zUnknown reader type %szFailed to load %s file '%s': %szFailed to load %s file '%s':z0 Overloading and deactivating %s '%s' ('%s/%s'))Dr{r|isdir startswithrZ ETC_FIREWALLDrbasenamenameZ check_namedefaultsortedlistdirendswithrvrrarr6rxZ get_icmptyperuZremove_icmptypeZ add_icmptyper"rVstrrqrrrr7ryZ get_serviceZremove_serviceZ add_servicerr8rzZget_zoneZ remove_zonecombinedrZadd_zonerr get_ipsets get_ipsetZ remove_ipset add_ipsetr^rr;Z get_helpersZ get_helperZ remove_helperZ add_helperr r< get_policiesZ get_policyZ remove_policyZ add_policyZadd_policy_objectrWrtrnZ exceptionZ forget_zone) r>r|Z reader_typerZ combined_zonerurrZorig_objrtZ config_objrr?r?r@rvs       $             $       zFirewall._loadercCsp|jj|jj|jj|jj|jj|jj|jj|jj|j j|j j|j dS)N) r6cleanupr7r8rr;rr9r:r<r$r=)r>r?r?r@rs          zFirewall.cleanupcCsN|jsB|jr(|j|jj|jd|jrBtjd|jj |j dS)Nrz!Unloading firewall kernel modules) r%rHrrrrIrrar5rr)r>r?r?r@stops    z Firewall.stopc Csd}d}xt|D]\}}|r0|jj|\}}n$|j|dkrDd}n|jj|\}}|dkrn|d7}||7}q|r|jj|d|j|d7<q||jkr|j|d8<|j|dkr|j|=qW||fS)NrrNrT) enumerater5 load_modulerFZ unload_module setdefault) r>Z_modulesenableZ num_failedZ error_msgsimoduleZstatusrr?r?r@handle_moduless(  zFirewall.handle_modulescCs|dkrd|_dS)NrF)r+)r>backendr?r?r@rssz!Firewall._select_firewall_backendcCs4x|jD]}|j|kr |Sq Wttjd|dS)Nz'%s' backend does not exist) all_backendsrr"r!Z UNKNOWN_ERROR)r>rrr?r?r@get_backend_by_names  zFirewall.get_backend_by_namecCs\|jr |jS|dkr |jr |jS|dkr4|jr4|jS|dkrH|jrH|jStt j d|dS)Nr[r\ebz-'%s' is not a valid backend or is unavailable) r+r4r&r-r'r0r(r2r"r! INVALID_IPV)r>ipvr?r?r@get_backend_by_ipvszFirewall.get_backend_by_ipvcCsP|dkr|jr|jS|dkr(|jr(|jS|dkr<|jr<|jSttjd|dS)Nr[r\rz-'%s' is not a valid backend or is unavailable) r&r-r'r0r(r2r"r!r)r>rr?r?r@get_direct_backend_by_ipvsz"Firewall.get_direct_backend_by_ipvcCs<|dkr|jS|dkr|jS|dkr*|jS|dkr8|jSdS)Nr,r/rrF)r&r'r(r+)r>rr?r?r@is_backend_enabledszFirewall.is_backend_enabledcCs8|jr dS|dkr|jS|dkr&|jS|dkr4|jSdS)NTr[r\rF)r+r&r'r()r>rr?r?r@is_ipv_enabledszFirewall.is_ipv_enabledcCsRg}|jr|j|jn6|jr*|j|j|jr<|j|j|jrN|j|j|S)N) r+appendr4r&r-r'r0r(r2)r>backendsr?r?r@enabled_backends s   zFirewall.enabled_backendscCsPg}|jr|j|j|jr(|j|j|jr:|j|j|jrL|j|j|S)N) r&rr-r'r0r(r2r+r4)r>rr?r?r@rs    zFirewall.all_backendsNcCsN|dkrt|}n|}x |jD]}|j||jq W|dkrJ|jddS)NT)rr add_rulesZbuild_default_tablesr)r>rlrrr?r?r@r$s zFirewall.apply_default_tablescCs|dkrt|}n|}x(|jD]}|j|j}|j||q W|jdr~|jd}d|jkr~|jr~|j |j}|j|||jdr|j r|j }|j|||dkr|j ddS)Nr\rawT) rrZbuild_default_rulesrLrrrrUrJZbuild_rpfilter_rulesrQZbuild_rfc3964_ipv4_rulesr)r>rlrrrulesZ ipv6_backendr?r?r@r0s"        zFirewall.apply_default_rulescCs|jr|jj rdSdS)NTF)r+r9Zhas_runtime_configuration)r>r?r?r@may_skip_flush_direct_backendsHsz'Firewall.may_skip_flush_direct_backendscCs`|dkrt|}n|}x2|jD]&}||jkr2q |j}|j||q W|dkr\|jddS)NT)rrrbuild_flush_rulesrr)r>rlrrrr?r?r@flush_direct_backendsNs  zFirewall.flush_direct_backendscCsp|dkrt|}n|}tjd|js4|j|dx$|jD]}|j}|j||q>W|dkrl|jddS)NzFlushing rule set)rlT) rrrarrrrrr)r>rlrrrr?r?r@r]s   zFirewall.flushcCs`|dkrt|}n|}tjd|x&|jD]}|j|}|j||q,W|dkr\|jddS)NzSetting policy to '%s'T)rrrarZbuild_set_policy_rulesrr)r>r<rlrrrr?r?r@ros   zFirewall.set_policycCsB|sdS|j|}|s&ttjd||j|s4dS|j||jS)NrNz'%s' is not a valid backend)rr"r!rrset_rulerL)r> backend_namerulerr?r?r@rs   z Firewall.rulecCs"ttd|}|j|}|s,ttjd||j|s:dS|js\|j s\|dkoX|j j rxt |D]\}}y|j ||j Wqftk r}zjtjtjtj|xFt|d|D]2}y|j |j||j Wqtk rYqXqW|WYdd}~XqfXqfWn|j||j dS)Nz'%s' is not a valid backendr)listrSrr"r!rrrKr_r2r`rrrLrnrra traceback format_excrtreversedZ reverse_ruleZ set_rules)r>rrZ_rulesrrrrr?r?r@rs.     zFirewall.rulescCs|jrttjdS)N)rDr"r!Z PANIC_MODE)r>r?r?r@ check_panicszFirewall.check_paniccCs"|}||jjkrttj||S)N)r<rr"r!ZINVALID_POLICY)r>r<Z_policyr?r?r@ check_policys zFirewall.check_policycCs8|}| s|dkr|j}||jjkr4ttj||S)NrN)get_default_zoner8rzr"r!Z INVALID_ZONE)r>r8_zoner?r?r@r~s  zFirewall.check_zonecCstj|sttj|dS)N)rZcheckInterfacer"r!ZINVALID_INTERFACE)r> interfacer?r?r@check_interfaces zFirewall.check_interfacecCs|jj|dS)N)r7 check_service)r>r7r?r?r@rszFirewall.check_servicecCstj|sttj|dS)N)r check_portr"r!Z INVALID_PORT)r>Zportr?r?r@rs zFirewall.check_portcCs*|sttj|dkr&ttjd|dS)Ntcpudpsctpdccpz''%s' not in {'tcp'|'udp'|'sctp'|'dccp'})rrrr)r"r!ZMISSING_PROTOCOLZINVALID_PROTOCOL)r>Zprotocolr?r?r@ check_tcpudps  zFirewall.check_tcpudpcCstj|sttj|dS)N)rZcheckIPr"r! INVALID_ADDR)r>Zipr?r?r@check_ips zFirewall.check_ipcCsP|dkr tj|sLttj|n,|dkr@tj|sLttj|n ttjddS)Nr[r\z'%s' not in {'ipv4'|'ipv6'})rZ checkIPnMaskr"r!rZ checkIP6nMaskr)r>rsourcer?r?r@ check_addresss  zFirewall.check_addresscCs|jj|dS)N)r6check_icmptype)r>Zicmpr?r?r@rszFirewall.check_icmptypecCs>t|tstd|t|ft|dkr:ttjd|dS)Nz%s is %s, expected intrz#timeout '%d' is not positive number) isinstanceint TypeErrortyper"r! INVALID_VALUE)r>Ztimeoutr?r?r@ check_timeouts   zFirewall.check_timeoutc Cs`|j}|j}|sNi}x&|jjD]}|jj|d||<q W|jj}|j}g}x$|jj D]} |j |jj | q^W|s|j d|j |jd} y|jd|dWn&tk r} z | } WYdd} ~ XnX|r(xL|D]D} |jj| jsx0|jjD]"} | jdkrq| j| jqWqW|s|j}||kr||krRi||<xFt||jD]2\}}|drd||||||<|||=qdWxb|jjD]T}||krx.||D]"}|jj|||||dqW||=n tjd|qWt|d kr6x(t|jD]}tjd |||=qW~x|D]} |jj| jrxx| jD]R}y|jj| j|Wn6tk r}z|jt j!kr|WYdd}~XnXqZWn|jj"| |jj#| jq>W|jj$|t%}|r,x@|jjd gD],}x$t&|D]}|jj|||d q WqW||_|jsD|j d | rVd|_'| nd|_'dS)N interfacesZDROPT)rrr __default__senderzNew zone '%s'.rz(Lost zone '%s', zone interfaces dropped.rN)rrrr)(rDrPr8rz get_settingsr9Zget_runtime_configrrrrrrrrrrnZ query_ipsetrrZ set_destroyritemschange_zone_of_interfacerrVrwkeysZentriesZ add_entryr"rr!ALREADY_ENABLEDrZ apply_ipsetZ set_configrrrC)r>rrDZ flush_allZ_zone_interfacesr8Z_direct_config_old_dzZ _ipset_objs_nameZstart_exceptionrrrZ_new_dzifacesettingsZ interface_identryrZ nm_bus_namerr?r?r@rs                zFirewall.reloadcCs|jS)N)rC)r>r?r?r@ get_stateaszFirewall.get_statecCsZ|jrttjdy|jdWn.tk rN}zttj|WYdd}~XnXd|_dS)Nzpanic mode already enabledZPANICT)rDr"r!rrrnCOMMAND_FAILED)r>rr?r?r@enable_panic_modefszFirewall.enable_panic_modecCsZ|jsttjdy|jdWn.tk rN}zttj|WYdd}~XnXd|_dS)Nzpanic mode is not enabledrF)rDr"r!Z NOT_ENABLEDrrnr)r>rr?r?r@disable_panic_modeqszFirewall.disable_panic_modecCs|jS)N)rD)r>r?r?r@query_panic_mode|szFirewall.query_panic_modecCs|jS)N)rL)r>r?r?r@get_log_deniedszFirewall.get_log_deniedcCsb|tjkr&ttjd|djtjf||jkrR||_|jj d||jj n ttj |dS)Nz'%s', choose from '%s'z','rh) rZLOG_DENIED_VALUESr"r!rjoinrrLr$setwriteZ ALREADY_SET)r>rr?r?r@set_log_denieds   zFirewall.set_log_deniedcCs|jS)N)rE)r>r?r?r@rszFirewall.get_default_zonecCs|j|}||jkr|j}||_|jjd||jj|jj|||jj|}x@t|dj D]\}}|drd|jj d|qdWn t t j |dS)NrcrrrN)r~rEr$rrr8rrrrrr"r!ZZONE_ALREADY_SET)r>r8rrZ_old_dz_settingsrrr?r?r@set_default_zones    zFirewall.set_default_zonecCsH|j}x:|jD].\}}|s(t|tr2|||<q||kr||=qW|S)N)rqrrbool)r>Z permanentZruntimerkeyrr?r?r@'combine_runtime_with_permanent_settingss  z0Firewall.combine_runtime_with_permanent_settingscCsi}i}xt|jt|jBD]}||kr"t||trt||krN||ng}tt|||||<t|t||A|@||<q"t||tst||tr|| r||rd||<q||r|| rd||<q"ttjdj t |||q"W||fS)NTFz Unhandled setting type {} key {}) rrrrrrr"r!ZINVALID_SETTINGformatr)r>Z old_settingsZ new_settingsZ add_settingsZremove_settingsroldr?r?r@get_added_and_removed_settingss   z'Firewall.get_added_and_removed_settings)F)FF)F)N)N)N)N)N)F)4__name__ __module__ __qualname__rArMr=rZrbrrrvrrrrsrrrrrrrrrrrrrrrrrr~rrrrrrrrrrrrrrrrrrrr?r?r?r@rBsh ( ;                s  )A__all__Zos.pathr{rXrqrrZfirewallrrZ firewall.corerrrrr Zfirewall.core.fw_icmptyper Zfirewall.core.fw_servicer Zfirewall.core.fw_zoner Zfirewall.core.fw_directr Zfirewall.core.fw_configrZfirewall.core.fw_policiesrZfirewall.core.fw_ipsetrZfirewall.core.fw_transactionrZfirewall.core.fw_helperrZfirewall.core.fw_policyrZfirewall.core.fw_nmrrZfirewall.core.loggerrZfirewall.core.io.firewalld_confrZfirewall.core.io.directrZfirewall.core.io.servicerZfirewall.core.io.icmptyperZfirewall.core.io.zonerrZfirewall.core.io.ipsetrZfirewall.core.ipsetrZfirewall.core.io.helperrZfirewall.core.io.policyr r!Zfirewall.errorsr"objectrr?r?r?r@sH                            PK!;.?&__pycache__/fw_icmptype.cpython-36.pycnu[3 Yj @s>dgZddlmZddlmZddlmZGdddeZdS)FirewallIcmpType)log)errors) FirewallErrorc@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dS)rcCs||_i|_dS)N)_fw _icmptypes)selffwr !/usr/lib/python3.6/fw_icmptype.py__init__szFirewallIcmpType.__init__cCsd|j|jfS)Nz%s(%r)) __class__r)rr r r __repr__!szFirewallIcmpType.__repr__cCs|jjdS)N)rclear)rr r r cleanup$szFirewallIcmpType.cleanupcCst|jjS)N)sortedrkeys)rr r r get_icmptypes)szFirewallIcmpType.get_icmptypescCs||jkrttj|dS)N)rrrZINVALID_ICMPTYPE)ricmptyper r r check_icmptype,s zFirewallIcmpType.check_icmptypecCs|j||j|S)N)rr)rrr r r get_icmptype0s zFirewallIcmpType.get_icmptypecCs|j}t|dkrddg}x|D]z}|dkrL|jj rB|jj rBq |jj}n,|dkrt|jj rj|jj rjq |jj}ng}|jj |kr t j d|j|fq W||j |j<dS)NrZipv4Zipv6z5ICMP type '%s' is not supported by the kernel for %s.) Z destinationlenrZip4tables_enabledZnftables_enabledZipv4_supported_icmp_typesZip6tables_enabledZipv6_supported_icmp_typesnamelowerrZinfo1r)robjZ orig_ipvsZipvZsupported_icmpsr r r add_icmptype4s     zFirewallIcmpType.add_icmptypecCs|j||j|=dS)N)rr)rrr r r remove_icmptypeGs z FirewallIcmpType.remove_icmptypeN) __name__ __module__ __qualname__r rrrrrrrr r r r rsN) __all__Zfirewall.core.loggerrZfirewallrZfirewall.errorsrobjectrr r r r s   PK![{ޣ*__pycache__/fw_helper.cpython-36.opt-1.pycnu[3 Yj)@s6dZdgZddlmZddlmZGdddeZdS)zhelper backendFirewallHelper)errors) FirewallErrorc@s\eZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ ddZ dS)rcCs||_i|_dS)N)Z_fw_helpers)selffwr/usr/lib/python3.6/fw_helper.py__init__szFirewallHelper.__init__cCsd|j|jfS)Nz%s(%r)) __class__r)rrrr __repr__"szFirewallHelper.__repr__cCs|jjdS)N)rclear)rrrr cleanup'szFirewallHelper.cleanupcCs||jkrttj|dS)N) get_helpersrrINVALID_HELPER)rnamerrr check_helper*s zFirewallHelper.check_helpercCs ||jkS)N)r)rrrrr query_helper.szFirewallHelper.query_helpercCst|jjS)N)sortedrkeys)rrrr r1szFirewallHelper.get_helperscCst|jdkS)Nr)lenr)rrrr has_helpers4szFirewallHelper.has_helperscCs|j||j|S)N)rr)rrrrr get_helper7s zFirewallHelper.get_helpercCs||j|j<dS)N)rr)robjrrr add_helper;szFirewallHelper.add_helpercCs"||jkrttj||j|=dS)N)rrrr)rrrrr remove_helper>s  zFirewallHelper.remove_helperN) __name__ __module__ __qualname__r r rrrrrrrrrrrr rsN)__doc____all__ZfirewallrZfirewall.errorsrobjectrrrrr s  PK!(L+CC#__pycache__/fw_ipset.cpython-36.pycnu[3 Yj%@sfdZdgZddlmZddlmZmZmZm Z ddl m Z ddl m Z ddlmZGdddeZd S) z ipset backend FirewallIPSet)log)remove_default_create_optionsnormalize_ipset_entrycheck_entry_overlaps_existingcheck_for_overlapping_entries)IPSet)errors) FirewallErrorc@seZdZddZddZddZddZd d Zd d Zd dZ d4ddZ ddZ ddZ d5ddZ ddZddZddZd6dd Zd!d"Zd#d$Zd%d&Zd7d'd(Zd)d*Zd+d,Zd-d.Zd/d0Zd1d2Zd3S)8rcCs||_i|_dS)N)_fw_ipsets)selffwr/usr/lib/python3.6/fw_ipset.py__init__#szFirewallIPSet.__init__cCsd|j|jfS)Nz%s(%r)) __class__r )r rrr__repr__'szFirewallIPSet.__repr__cCs|jjdS)N)r clear)r rrrcleanup,szFirewallIPSet.cleanupcCs||jkrttj|dS)N) get_ipsetsr r Z INVALID_IPSET)r namerrr check_ipset/s zFirewallIPSet.check_ipsetcCs ||jkS)N)r)r rrrr query_ipset3szFirewallIPSet.query_ipsetcCst|jjS)N)sortedr keys)r rrrr6szFirewallIPSet.get_ipsetscCst|jdkS)Nr)lenr )r rrr has_ipsets9szFirewallIPSet.has_ipsetsFcCs&|j||j|}|r"|j||S)N)rr check_applied_obj)r rappliedobjrrr get_ipset<s    zFirewallIPSet.get_ipsetcCs4g}|jjr|j|jj|jjr0|j|jj|S)N)r Znftables_enabledappendZnftables_backendZ ipset_enabledZ ipset_backend)r backendsrrrr#Cs zFirewallIPSet.backendscCs0|j|jjkr ttjd|j||j|j<dS)Nz'%s' is not supported by ipset.)typer Zipset_supported_typesr r Z INVALID_TYPEr r)r r rrr add_ipsetKs zFirewallIPSet.add_ipsetcCs|j|}|jrh| rhy x|jD]}|j|q"WWqttk rd}zttj|WYdd}~XqtXn tj d||j|=dS)Nz,Keeping ipset '%s' because of timeout option) r rr# set_destroy Exceptionr r COMMAND_FAILEDrdebug1)r rZkeepr backendmsgrrr remove_ipsetQs    zFirewallIPSet.remove_ipsetc<Cs$|j|}x|jD]}|jdkr|j}||krd|jksv|jddksv|j||dksvt|j||dkry|j|Wn.tk r}zt t j |WYdd}~XnX|j j ry|j|j|j|jWn0tk r}zt t j |WYdd}~Xn&Xd|_d|jkr,|jddkr,qy|j|jWn0tk rl}zt t j |WYdd}~XnXx|jD]J}y|j|j|Wn0tk r}zt t j |WYdd}~XnXqvWqy|j|j|j|j|jdWn0tk r}zt t j |WYdd}~XqXd|_qWdS)Nipsettimeout0rT)r r#rZset_get_active_terseoptionsr$rm_def_cr_optsr&r'r r r(r _individual_callsZ set_creater set_flushentriesset_add set_restore)r rr r*Zactiver+entryrrr apply_ipset]sL     &  zFirewallIPSet.apply_ipsetcCs>x8|jD],}|j|}d|_tjd||j|q WdS)NFzApplying ipset '%s')rr rrr)r9)r rr rrr apply_ipsetss  zFirewallIPSet.apply_ipsetscCsxz|jD]n}|jdkrq x\|jD]P}y|j||j|Wq$tk rr}z|jtjkrb|WYdd}~Xq$Xq$Wq WdS)NZnftables) r#rr check_appliedr&r coder NOT_APPLIED)r r*r-r+rrrflushs   zFirewallIPSet.flushTcCs|j||djS)N)r)r!r$)r rrrrrget_typeszFirewallIPSet.get_typecCst|j|ddjjdS)NT)r,)rr!r$split)r rrrr get_dimensionszFirewallIPSet.get_dimensioncCs|j|}|j|dS)N)r!r)r rr rrrr;s zFirewallIPSet.check_appliedcCs|jsttj|jdS)N)rr r r=r)r r rrrrszFirewallIPSet.check_applied_objcCs.|j||d}d|jkr*|jddkr*dSdS)N)rZfamilyZinet6Zipv6Zipv4)r!r1)r rrr rrr get_familys  zFirewallIPSet.get_familycCs|j|dd}t|}tj||j|j||jkrFttj d||ft ||jy$x|j D]}|j |j |q^WWn.tk r}zttj|WYdd}~Xn&Xd|jks|jddkr|jj|dS)NT)rz'%s' already is in '%s'r.r/)r!rr check_entryr1r$r5r r ZALREADY_ENABLEDrr#r6rr'r(r")r rr8r r*r+rrr add_entrys  zFirewallIPSet.add_entrycCs|j|dd}t|}||jkr4ttjd||fy$x|jD]}|j|j|q@WWn.t k r}zttj |WYdd}~Xn&Xd|j ks|j ddkr|jj |dS)NT)rz'%s' not in '%s'r.r/) r!rr5r r Z NOT_ENABLEDr#Z set_deleterr'r(r1remove)r rr8r r*r+rrr remove_entrys zFirewallIPSet.remove_entrycCsD|j|dd}t|}d|jkr:|jddkr:ttj|||jkS)NT)rr.r/)r!rr1r r ZIPSET_WITH_TIMEOUTr5)r rr8r rrr query_entrys  zFirewallIPSet.query_entrycCs|j|dd}|jS)NT)r)r!r5)r rr rrr get_entriesszFirewallIPSet.get_entriescCs@|j|dd}t|x|D]}tj||j|jqWd|jksN|jddkrT||_y"x|jD]}|j|j q`WWn.t k r}zt t j |WYdd}~XnXd|_yXxR|jD]F}|jjrx8|jD]}|j|j |qWq|j|j |j|j|jdqWWn0t k r4}zt t j |WYdd}~XnXd|_dS)NT)rr.r/)r!rrrDr1r$r5r#r4rr'r r r(rr r3r6r7)r rr5r r8r*r+rrr set_entriess.  zFirewallIPSet.set_entriesN)F)F)T)T)__name__ __module__ __qualname__rrrrrrrr!r#r%r,r9r:r>r?rBr;rrCrErGrHrIrJrrrrr"s0  1   N)__doc____all__Zfirewall.core.loggerrZfirewall.core.ipsetrr2rrrZfirewall.core.io.ipsetrZfirewallr Zfirewall.errorsr objectrrrrrs    PK!ܥ]Z]Z!__pycache__/logger.cpython-36.pycnu[3 Yj>y@sddddgZddlZddlZddlZddlZddlZddlZddlZddlZddl Z ddl Z Gddde Z Gddde Z Gd d d e ZGd d d e ZGd dde ZGddde ZeZdS) LogTargetFileLogLoggerlogNc@s2eZdZdZddZd ddZddZd d Zd S) rz% Abstract class for logging targets. cCs d|_dS)N)fd)selfr/usr/lib/python3.6/logger.py__init__(szLogTarget.__init__rcCs tddS)Nz%LogTarget.write is an abstract method)NotImplementedError)rdatalevelloggeris_debugrrr write+szLogTarget.writecCs tddS)Nz%LogTarget.flush is an abstract method)r )rrrr flush.szLogTarget.flushcCs tddS)Nz%LogTarget.close is an abstract method)r )rrrr close1szLogTarget.closeN)r)__name__ __module__ __qualname____doc__r rrrrrrr r&s  c@s.eZdZddZd ddZddZdd Zd S) _StdoutLogcCstj|tj|_dS)N)rr sysstdoutr)rrrr r 8s z_StdoutLog.__init__rcCs|jj||jdS)N)rrr)rr r rrrrr r<s z_StdoutLog.writecCs |jdS)N)r)rrrr rAsz_StdoutLog.closecCs|jjdS)N)rr)rrrr rDsz_StdoutLog.flushN)r)rrrr rrrrrrr r7s rc@seZdZddZdS) _StderrLogcCstj|tj|_dS)N)rr rstderrr)rrrr r Ks z_StderrLog.__init__N)rrrr rrrr rJsrc@s.eZdZddZd ddZddZdd Zd S) _SyslogLogcCs.tj|tjtjjtjdtj tj dS)Nr) rr syslogZopenlogospathbasenamerargvZLOG_PIDZ LOG_DAEMON)rrrr r Ss z_SyslogLog.__init__rcCsd}|rtj}nF||jkr"tj}n4||jkr4tj}n"||jkrFtj}n||jkrVtj }|j drt|dt |d}t |dkr|dkrtj|n tj||dS)N r) rZ LOG_DEBUGINFO1ZLOG_INFOWARNINGZ LOG_WARNINGERRORZLOG_ERRFATALZLOG_CRITendswithlen)rr r rrZpriorityrrr ras"       z_SyslogLog.writecCs tjdS)N)rZcloselog)rrrr rwsz_SyslogLog.closecCsdS)Nr)rrrr rzsz_SyslogLog.flushN)r)rrrr rrrrrrr rRs rc@s<eZdZdZdddZddZddd Zd d Zd d ZdS)rz< FileLog class. File will be opened on the first write. wcCstj|||_||_dS)N)rr filenamemode)rr+r,rrr r s zFileLog.__init__cCsv|jr dStjtjB}|jjdr,|tjO}tj|j|d|_tj |jdtj |j|j|_t j |jt j t j dS)Nai)rrO_CREATO_WRONLYr, startswithO_APPENDopenr+fchmodfdopenfcntlZF_SETFDZ FD_CLOEXEC)rflagsrrr r2s   z FileLog.openrcCs(|js|j|jj||jjdS)N)rr2rr)rr r rrrrr rs z FileLog.writecCs|js dS|jjd|_dS)N)rr)rrrr rs z FileLog.closecCs|js dS|jjdS)N)rr)rrrr rsz FileLog.flushN)r*)r) rrrrr r2rrrrrrr rs   c@seZdZdZd[Zd\Zd]Zd^Zd_ZdZ e Z e Z eZd`d d Zd d ZdaddZdbddZdcddZddddZddZddZddZddZddZd d!Zed"fd#d$Zed"fd%d&Zed"fd'd(Zed"fd)d*Zed"fd+d,Z ed"fd-d.Z!d/d0Z"d1d2Z#d3d4Z$d5d6Z%d7d8Z&d9d:Z'd;d<Z(d=d>Z)d?d@Z*dAdBZ+dCdDZ,dedEdFZ-dGdHZ.dfdIdJZ/ed"dfdKdLZ0ed"dfdMdNZ1ed"dfdOdPZ2dgdQdRZ3dSdTZ4dUdVZ5dWdXZ6dhdYdZZ7d"S)iraL Format string: %(class)s Calling class the function belongs to, else empty %(date)s Date using Logger.date_format, see time module %(domain)s Full Domain: %(module)s.%(class)s.%(function)s %(file)s Filename of the module %(function)s Function name, empty in __main__ %(label)s Label according to log function call from Logger.label %(level)d Internal logging level %(line)d Line number in module %(module)s Module name %(message)s Log message Standard levels: FATAL Fatal error messages ERROR Error messages WARNING Warning messages INFOx, x in [1..5] Information DEBUGy, y in [1..10] Debug messages NO_INFO No info output NO_DEBUG No debug output INFO_MAX Maximum info level DEBUG_MAX Maximum debug level x and y depend on info_max and debug_max from Logger class initialization. See __init__ function. Default logging targets: stdout Logs to stdout stderr Logs to stderr syslog Logs to syslog Additional arguments for logging functions (fatal, error, warning, info and debug): nl Disable newline at the end with nl=0, default is nl=1. fmt Format string for this logging entry, overloads global format string. Example: fmt="%(file)s:%(line)d %(message)s" nofmt Only output message with nofmt=1. The nofmt argument wins over the fmt argument. Example: from logger import log log.setInfoLogLevel(log.INFO1) log.setDebugLogLevel(log.DEBUG1) for i in range(1, log.INFO_MAX+1): log.setInfoLogLabel(i, "INFO%d: " % i) log.setFormat("%(date)s %(module)s:%(line)d [%(domain)s] %(label)s: " "%(level)d %(message)s") log.setDateFormat("%Y-%m-%d %H:%M:%S") fl = FileLog("/tmp/log", "a") log.addInfoLogging("*", fl) log.addDebugLogging("*", fl) log.addInfoLogging("*", log.syslog, fmt="%(label)s%(message)s") log.debug3("debug3") log.debug2("debug2") log.debug1("debug1") log.info2("info2") log.info1("info1") log.warning("warning\n", nl=0) log.error("error\n", nl=0) log.fatal("fatal") log.info(log.INFO1, "nofmt info", nofmt=1) r#r cCsi|_i|_d|_d|_i|_i|_i|_i|_i|_i|_ |dkrPt d||dkrdt d||j |_ ||_ d|_||_|j|jd|j|jd|j|jd|j|j dxNtd|j dD]:}t|d |||j|dt|d |d d ||qWxTtd|jdD]@}t|d |||j|d|t|d|dd ||qW|j|j|j|j|jd|jd|jd|j|j|j|j g|jd|jddt|j|j dD|jd|jddtd|jdDdS)z Logger class initialization r#zLogger: info_max %d is too lowrzLogger: debug_max %d is too lowz FATAL ERROR: zERROR: z WARNING: zINFO%dzinfo%dcsfddS)Ncsj|f||S)N)info)messageargskwargs)rxrr  sz3Logger.__init__....r)rrAr)rrAr rBsz!Logger.__init__..zDEBUG%dz DEBUG%d: zdebug%dcsfddS)Ncsj|f||S)N)debug)r>r?r@)rrArr rB)sz3Logger.__init__....r)rrAr)rrAr rB(sz%(label)s%(message)sz%d %b %Y %H:%M:%S*cSsg|]}|qSrr).0irrr 4sz#Logger.__init__..cSsg|]}|qSrr)rErFrrr rG6sN) _level _debug_level_format _date_format_label _debug_label_logging_debug_logging_domains_debug_domains ValueErrorr%NO_INFOINFO_MAXNO_DEBUG DEBUG_MAXsetInfoLogLabelr' TRACEBACKr&rangesetattrsetDebugLogLabelsetInfoLogLevelr$setDebugLogLevel setFormat setDateFormatsetInfoLoggingrrsetDebugLogging)rZinfo_maxZ debug_maxrHrrr r sX            zLogger.__init__cCsNxHt|j|jdD]2}||jkr$qx |j|D]\}}}|jq0WqWdS)z Close all logging targets r#N)rYr'rVrNr)rr dummytargetrrr r8s  z Logger.closerDcCs$|j|||jkr|j|S|jS)z Get info log level. ) _checkDomainrHNOTHING)rdomainrrr getInfoLogLevel@s   zLogger.getInfoLogLevelcCs8|j|||jkr|j}||jkr*|j}||j|<dS)z% Set log level [NOTHING .. INFO_MAX] N)rdrerTrH)rr rfrrr r\Gs    zLogger.setInfoLogLevelcCs*|j|||jkr$|j||jS|jS)z Get debug log level. )rdrIrU)rrfrrr getDebugLogLevelPs  zLogger.getDebugLogLevelcCs:|j||dkrd}||jkr&|j}||j|j|<dS)z- Set debug log level [NO_DEBUG .. DEBUG_MAX] rN)rdrVrUrI)rr rfrrr r]Ws   zLogger.setDebugLogLevelcCs|jS)N)rJ)rrrr getFormat`szLogger.getFormatcCs ||_dS)N)rJ)rrJrrr r^cszLogger.setFormatcCs|jS)N)rK)rrrr getDateFormatfszLogger.getDateFormatcCs ||_dS)N)rK)rrJrrr r_iszLogger.setDateFormatcCs:|j|}x*|D]"}|j||j|jd||j|<qWdS)zU Set log label for level. Level can be a single level or an array of levels. ) min_level max_levelN) _getLevels_checkLogLevelr'rTrL)rr labellevelsrrr rWls     zLogger.setInfoLogLabelcCs>|j|dd}x*|D]"}|j||j|jd||j|<qWdS)zU Set log label for level. Level can be a single level or an array of levels. r#)r)rkrlN)rmrnr$rVrM)rr rorprrr r[us    zLogger.setDebugLogLabelNcCs|j||||dddS)z Set info log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r)rN) _setLogging)rrfrcr fmtrrr r`~szLogger.setInfoLoggingcCs|j||||dddS)z Set debug log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r#)rN)rq)rrfrcr rrrrr raszLogger.setDebugLoggingcCs|j||||dddS)z Add info log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r)rN) _addLogging)rrfrcr rrrrr addInfoLoggingszLogger.addInfoLoggingcCs|j||||dddS)z Add debg log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r#)rN)rs)rrfrcr rrrrr addDebugLoggingszLogger.addDebugLoggingcCs|j||||dddS)z Delete info log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r)rN) _delLogging)rrfrcr rrrrr delInfoLoggingszLogger.delInfoLoggingcCs|j||||dddS)z Delete debug log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r#)rN)rv)rrfrcr rrrrr delDebugLoggingszLogger.delDebugLoggingcCs|j|ddS)zN Is there currently any info logging for this log level (and domain)? r)r)_isLoggingHere)rr rrr isInfoLoggingHereszLogger.isInfoLoggingHerecCs|j|ddS)zO Is there currently any debug logging for this log level (and domain)? r#)r)ry)rr rrr isDebugLoggingHereszLogger.isDebugLoggingHerecOs,|j|d|d<|j|j|f||dS)z Fatal error log. rrN) _checkKWargs_logr')rrJr?r@rrr fatals z Logger.fatalcOs,|j|d|d<|j|j|f||dS)z Error log. rrN)r|r}r&)rrJr?r@rrr errors z Logger.errorcOs,|j|d|d<|j|j|f||dS)z Warning log. rrN)r|r}r%)rrJr?r@rrr warnings zLogger.warningcOsB|j|d|jd|j|d|d<|j||j|f||dS)z Information log using info level [1..info_max]. There are additional infox functions according to info_max from __init__r#)rkrlrrN)rnrTr|r}rS)rr rJr?r@rrr r=s z Logger.infocOs<|j|d|jd|j|d|d<|j||f||dS)z Debug log using debug level [1..debug_max]. There are additional debugx functions according to debug_max from __init__r#)rkrlrN)rnrVr|r})rr rJr?r@rrr rCs z Logger.debugcCs|j|jtjgiddS)N)r?r@)r}rX traceback format_exc)rrrr exceptionszLogger.exceptioncCs&||ks||kr"td|||fdS)Nz*Level %d out of range, should be [%d..%d].)rR)rr rkrlrrr rnszLogger._checkLogLevelcCs2|sdSx$|jD]}|dkrtd|qWdS)Nnlrrnofmtz0Key '%s' is not allowed as argument for logging.)rrrr)keysrR)rr@keyrrr r|s zLogger._checkKWargscCs| s|dkrtd|dS)Nr<zDomain '%s' is not valid.)rR)rrfrrr rdszLogger._checkDomaincCs||jkrft|tst|tr$|}n|g}xp|D]0}|rL|j|d|jdq0|j||j|jdq0Wn6|rddt|j |jD}nddt|j|jD}|S)z Generate log level array. r#)rkrlcSsg|]}|qSrr)rErFrrr rGsz%Logger._getLevels..cSsg|]}|qSrr)rErFrrr rGs) ALL isinstancelisttuplernrVr'rTrYZDEBUG1)rr rrprrr rms    zLogger._getLevelscCsNt|tst|tr|}n|g}x(|D] }t|jts&td|jjq&W|S)z Generate target array. z '%s' is no valid logging target.)rrr issubclass __class__rrRr)rrctargetsZ_targetrrr _getTargetss  zLogger._getTargetscCs|r |j}|j}d|jdf}n|j}|j}|j|jdf}t|dkrP|jxVt |d|dD]@}||krrqdx0||D]$\}}}||kr||j |gj |q|WqdWdS)z% Generate dict with domain by level. r#rN) rQrOrVrPrNr'rTr)clearrY setdefaultappend)rrrPrNZ_ranger rfrbrrr _genDomainss zLogger._genDomainsc Csl|j||j||}|j|}|r,|j}n|j}x*|D]"}x|D]}|||fg||<qBWq8W|j|dS)N)rdrmrrOrNr) rrfrcr rrrrprrNrrr rqs     zLogger._setLoggingc Cst|j||j||}|j|}|r,|j}n|j}x2|D]*}x$|D]}|j|gj|||fqBWq8W|j|dS)N)rdrmrrOrNrrr) rrfrcr rrrrprrNrrr rs-s      zLogger._addLoggingc Cs|j||j||}|j|}|r,|j}n|j}x|D]|} xv|D]n}| |krPqB|||f|| kr|| j|||ft|| dkr|| =qB||jkrBtd| ||j j |fqBWq8W|j |dS)NrzDNo mathing logging for level %d, domain %s, target %s and format %s.) rdrmrrOrNremover)rrRrrr) rrfrcr rrrrprrNrHrrr rv<s&      zLogger._delLoggingcCst|j||}|sdS|dd}|r,|j}n|j}x<||D]0\}}}|dksh|j|shtj|d|rrfrrDrrr") rr)rOrNr0rrrJrr)rr rJr?r@rrrrrrNZ used_targetsrfrcrrr r}sL     z Logger._logc Csg}d}|r |j}|j}|j}n|j}|j}|j}xN|D]F}|dkrh|||kr~d}t|dkrdg}Pq8|||kr8|j|q8W| rt|dkrdS||krdStj } x$| r| j r| j d|j kr| j } qW| st d| j d} | d } x|D]}| j|rg}PqW| j} t| } xx||D]l}|jd}|dkrDq&n|dkr\|d|}n|}| t|kr| j|sdSn|j| s&dSq&Wd }||kr||}| j| j| d | jd ||tj|jtjd }|d d krd |d <d}x&||D]}|dkrqd}PqW|jjddksR|jjddksR|sRt|dkrl|j| }|rl|j|d<d |d|d<|dd kr|dd |d7<|d d kr|dd |d 7<t|dkr|S|dd }x0|D](}|j|stj|d|r|SqWdS)z Internal function. FrDTrr#Nrz Frame information not available.rr<) filelinerclassfunctionrfror Zdater?z %(domain)z%(class)rrrf)rIrQrMrHrPrLr)rrZ currentframef_back f_globalsrrRr0rfind co_filenamef_linenortimeZstrftimerKZ localtimerJrrrr)rr rZ check_domainsZ simple_matchrrPrLrffZ module_nameZ point_moduleco_lenrFdZ level_strZ domain_neededrrrrr rs                      zLogger._genDict)r7r;)rD)rD)rD)rD)r)r)r)r)8rrrrrrer'rXr&r%rrrrrrr rrgr\rhr]rir^rjr_rWr[r`rartrurwrxrzr{r~rrr=rCrrnr|rdrmrrrqrsrvryrrr}rrrrr rsdG ;           4)__all__rrrrrrrr5Zos.pathrobjectrrrrrrrrrrr s. -(*4PK![j)__pycache__/fw_transaction.cpython-36.pycnu[3 Yj@sJdZdgZddlZddlmZddlmZddlmZGddde Z dS)z!Transaction classes for firewalldFirewallTransactionN)log)errors) FirewallErrorc@seZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ ddZ ddZ ddZddZddZddZdd Zd!d"Zd#S)$rcCs(||_i|_g|_g|_g|_g|_dS)N)fwrules pre_funcs post_funcs fail_funcsmodules)selfrr $/usr/lib/python3.6/fw_transaction.py__init__!s zFirewallTransaction.__init__cCs2|jj|jdd=|jdd=|jdd=dS)N)rclearrr r )r r r rr)s   zFirewallTransaction.clearcCs|jj|jgj|dS)N)r setdefaultnameappend)r backendruler r radd_rule/szFirewallTransaction.add_rulecCsx|D]}|j||qWdS)N)r)r rrrr r r add_rules2s zFirewallTransaction.add_rulescCs|j|jko||j|jkS)N)rr)r rrr r r query_rule6szFirewallTransaction.query_rulecCs2|j|jkr.||j|jkr.|j|jj|dS)N)rrremove)r rrr r r remove_rule9szFirewallTransaction.remove_rulecGs|jj||fdS)N)rr)r funcargsr r radd_pre=szFirewallTransaction.add_precGs|jj||fdS)N)r r)r rrr r radd_post@szFirewallTransaction.add_postcGs|jj||fdS)N)r r)r rrr r radd_failCszFirewallTransaction.add_failcCs||jkr|jj|dS)N)r r)r moduler r r add_moduleFs zFirewallTransaction.add_modulecCs||jkr|jj|dS)N)r r)r r r r r remove_moduleJs z!FirewallTransaction.remove_modulecCsx|D]}|j|qWdS)N)r!)r r r r r r add_modulesNs zFirewallTransaction.add_modulescCsx|D]}|j|qWdS)N)r")r r r r r rremove_modulesRs z"FirewallTransaction.remove_modulescCstjdt||dfi}|sjxp|jD]<}x6t|j|D]$}|j|gj|jj|j |qX|j |q>W|s|jj ||} | r| \} }| rtj||ri} xH|D]@}g| |<x2t||D]"} | |j |jj|j| qWqWxb| D]Z}y|jj|| |Wn<tk r}ztjt j tj |WYdd}~XnXq0Wxh|jD]^\} }y | |WnFtk r}z(tjt j tj d| ||fWYdd}~XnXqWttj||jdS)Nz%s.execute(%s)FTz#Calling fail func %s(%s) failed: %s)rr%r&r-prerr Exceptiondebug1 traceback format_excerrorrZhandle_modulesr'r(r)r rrZCOMMAND_FAILEDpost)r r+rr r4ZerrorMsgdoner,msgZ module_returnZstatusZ undo_rulesrrrr r rexecutefsV    " & zFirewallTransaction.executecCs|tjdt|xd|jD]Z\}}y ||Wqtk rr}z(tjtjtjd|||fWYdd}~XqXqWdS)Nz%s.pre()z"Calling pre func %s(%s) failed: %s) rr%r&rr0r1r2r3r4)r rrr7r r rr/s zFirewallTransaction.precCs|tjdt|xd|jD]Z\}}y ||Wqtk rr}z(tjtjtjd|||fWYdd}~XqXqWdS)Nz %s.post()z#Calling post func %s(%s) failed: %s) rr%r&r r0r1r2r3r4)r rrr7r r rr5s zFirewallTransaction.postN)__name__ __module__ __qualname__rrrrrrrrrr!r"r#r$r-r8r/r5r r r rr s"@ ) __doc____all__r2Zfirewall.core.loggerrZfirewallrZfirewall.errorsrobjectrr r r rs    PK!ܧ __pycache__/icmp.cpython-36.pycnu[3 Yj,@sddddgZd,d-d.d/d0d1d2d3d4d5d6d7d8d9d:d;dd?d@dAdBdCdDdEdFdGdHdIdJdKdLdMdNdOdPdQdRdSdTdUd*ZdVdWdXdYdZd[d\d]d^d_d`dadbdcdddedfdgdhdidjdkdldmdndodpdqdrdsdtd$Zd%d&Zd'dZd(d)Zd*dZd+S)u ICMP_TYPES ICMPV6_TYPEScheck_icmp_type_codecheck_icmpv6_type_codeTF )*z echo-replypongzdestination-unreachableznetwork-unreachableztos-network-unreachablezhost-unreachableztos-host-unreachablezprotocol-unreachablezport-unreachablezfragmentation-neededzsource-route-failedznetwork-unknownz host-unknownznetwork-prohibitedzhost-prohibitedzTOS-network-unreachablezTOS-host-unreachablezcommunication-prohibitedzhost-precedence-violationzprecedence-cutoffz source-quenchznetwork-redirectredirectz host-redirectztos-host-redirectzTOS-network-redirectztos-network-redirectzTOS-host-redirectz echo-requestpingzrouter-advertisementzrouter-solicitationz time-exceededzttl-zero-during-transitzttl-zero-during-reassemblyzparameter-problemz ip-header-badzrequired-option-missingztimestamp-requestztimestamp-replyzaddress-mask-requestzaddress-mask-reply)zdestination-unreachablezno-routezcommunication-prohibitedz beyond-scopezaddress-unreachablezport-unreachablez failed-policyz reject-routezpacket-too-bigz time-exceededzttl-zero-during-transitzttl-zero-during-reassemblyzparameter-problemz bad-headerzunknown-header-typezunknown-optionz echo-requestrz echo-replyrzrouter-solicitationzrouter-advertisementzneighbour-solicitationzneigbour-solicitationzneighbour-advertisementzneigbour-advertisementrzmld-listener-queryzmld-listener-reportzmld-listener-donezmld2-listener-reportcCs|tkr dSdS)NTF)r)_namer&/usr/lib/python3.6/icmp.pycheck_icmp_namensr(cCs||ftjkrdSdS)NTF)rvalues)_type_coder&r&r'rsscCs|tkr dSdS)NTF)r)r%r&r&r'check_icmpv6_namexsr,cCs||ftjkrdSdS)NTF)rr))r*r+r&r&r'r}sN)rrT)rrT)rrT)rrF)rrF)rrF)rrF)rrF)rrF)rr F)rr F)rr F)rr F)rr F)rrF)rrF)rrF)rrF)rrF)rrF)r rT)r rF)r rT)r rF)r rF)r rF)r rF)r rF)rrT)rrT)r rT)rrT)rrT)rrF)rrF)rrT)rrF)rrF)rrT)rrT)rrF)rrF)rrT)rrF)rrF)rrF)rrF)rr F)rr F)rr F)rrT)rrT)rrF)rrF)r rT)r rF)r rF)r rF)rrT)rrT)rrT)rrT)rrT)rrT)rrT)rrT)rrT)rrT)r rT)r!rT)r"rT)r#rT)r$rT)__all__rrr(rr,rr&r&r&r'sPK!QQ%__pycache__/rich.cpython-36.opt-1.pycnu[3 Yj8@sdddddddddd d d d d ddddgZddlmZddlmZddlmZddlmZddlm Z Gddde Z Gddde Z Gddde Z Gddde ZGdddeZGddde ZGddde ZGddde ZGd dde ZGd!d d e ZGd"d d e ZGd#d d e ZGd$d d e ZGd%d d e ZGd&ddeZGd'dde Zd(d)d/d1d+ZGd,dde ZGd-dde Zd.S)2 Rich_SourceRich_Destination Rich_Service Rich_Port Rich_ProtocolRich_MasqueradeRich_IcmpBlock Rich_IcmpTypeRich_SourcePortRich_ForwardPortRich_Log Rich_Audit Rich_Accept Rich_Reject Rich_Drop Rich_Mark Rich_Limit Rich_Rule) functions)check_ipset_name) REJECT_TYPES)errors) FirewallErrorc@seZdZdddZddZdS)rFcCs||_|jdkrd|_||_|jdks0|jdkr8d|_n|jdk rN|jj|_||_|jdkrdd|_||_|jdkr|jdkr|jdkrttjddS)Nzno address, mac and ipset)addrmacupperipsetinvertrr INVALID_RULE)selfrrrrr!/usr/lib/python3.6/rich.py__init__$s    zRich_Source.__init__cCsjd|jr dnd}|jdk r*|d|jS|jdk rB|d|jS|jdk rZ|d|jSttjddS)Nz source%s z NOTrz address="%s"zmac="%s"z ipset="%s"zno address, mac and ipset)rrrrrrr)r retr!r!r"__str__5s   zRich_Source.__str__N)F)__name__ __module__ __qualname__r#r%r!r!r!r"r#s c@seZdZdddZddZdS)rFcCsV||_|jdkrd|_||_|jdkr,d|_||_|jdkrR|jdkrRttjddS)Nrzno address and ipset)rrrrrr)r rrrr!r!r"r#Bs  zRich_Destination.__init__cCsRd|jr dnd}|jdk r*|d|jS|jdk rB|d|jSttjddS)Nzdestination%s z NOTrz address="%s"z ipset="%s"zno address and ipset)rrrrrr)r r$r!r!r"r%Ns  zRich_Destination.__str__N)F)r&r'r(r#r%r!r!r!r"rAs c@seZdZddZddZdS)rcCs ||_dS)N)name)r r)r!r!r"r#YszRich_Service.__init__cCs d|jS)Nzservice name="%s")r))r r!r!r"r%\szRich_Service.__str__N)r&r'r(r#r%r!r!r!r"rXsc@seZdZddZddZdS)rcCs||_||_dS)N)portprotocol)r r*r+r!r!r"r#`szRich_Port.__init__cCsd|j|jfS)Nzport port="%s" protocol="%s")r*r+)r r!r!r"r%dszRich_Port.__str__N)r&r'r(r#r%r!r!r!r"r_sc@seZdZddZdS)r cCsd|j|jfS)Nz#source-port port="%s" protocol="%s")r*r+)r r!r!r"r%hszRich_SourcePort.__str__N)r&r'r(r%r!r!r!r"r gsc@seZdZddZddZdS)rcCs ||_dS)N)value)r r,r!r!r"r#mszRich_Protocol.__init__cCs d|jS)Nzprotocol value="%s")r,)r r!r!r"r%pszRich_Protocol.__str__N)r&r'r(r#r%r!r!r!r"rlsc@seZdZddZddZdS)rcCsdS)Nr!)r r!r!r"r#tszRich_Masquerade.__init__cCsdS)N masquerader!)r r!r!r"r%wszRich_Masquerade.__str__N)r&r'r(r#r%r!r!r!r"rssc@seZdZddZddZdS)rcCs ||_dS)N)r))r r)r!r!r"r#{szRich_IcmpBlock.__init__cCs d|jS)Nzicmp-block name="%s")r))r r!r!r"r%~szRich_IcmpBlock.__str__N)r&r'r(r#r%r!r!r!r"rzsc@seZdZddZddZdS)rcCs ||_dS)N)r))r r)r!r!r"r#szRich_IcmpType.__init__cCs d|jS)Nzicmp-type name="%s")r))r r!r!r"r%szRich_IcmpType.__str__N)r&r'r(r#r%r!r!r!r"rsc@seZdZddZddZdS)r cCs<||_||_||_||_|jdkr(d|_|jdkr8d|_dS)Nr)r*r+to_port to_address)r r*r+r.r/r!r!r"r#s  zRich_ForwardPort.__init__cCs<d|j|j|jdkrd|jnd|jdkr4d|jndfS)Nz(forward-port port="%s" protocol="%s"%s%srz to-port="%s"z to-addr="%s")r*r+r.r/)r r!r!r"r%szRich_ForwardPort.__str__N)r&r'r(r#r%r!r!r!r"r s c@seZdZdddZddZdS)r NcCs||_||_||_dS)N)prefixlevellimit)r r0r1r2r!r!r"r#szRich_Log.__init__cCs>d|jrd|jnd|jr$d|jnd|jr6d|jndfS)Nz log%s%s%sz prefix="%s"rz level="%s"z %s)r0r1r2)r r!r!r"r%szRich_Log.__str__)NNN)r&r'r(r#r%r!r!r!r"r s c@seZdZdddZddZdS)r NcCs ||_dS)N)r2)r r2r!r!r"r#szRich_Audit.__init__cCsd|jrd|jndS)Nzaudit%sz %sr)r2)r r!r!r"r%szRich_Audit.__str__)N)r&r'r(r#r%r!r!r!r"r s c@seZdZdddZddZdS)r NcCs ||_dS)N)r2)r r2r!r!r"r#szRich_Accept.__init__cCsd|jrd|jndS)Nzaccept%sz %sr)r2)r r!r!r"r%szRich_Accept.__str__)N)r&r'r(r#r%r!r!r!r"r s c@s&eZdZdddZddZddZdS) rNcCs||_||_dS)N)typer2)r Z_typer2r!r!r"r#szRich_Reject.__init__cCs,d|jrd|jnd|jr$d|jndfS)Nz reject%s%sz type="%s"rz %s)r3r2)r r!r!r"r%szRich_Reject.__str__cCsT|jrP|sttjd|dkrP|jt|krPdjt|}ttjd|j|fdS)Nz9When using reject type you must specify also rule family.ipv4ipv6z, z%Wrong reject type %s. Use one of: %s.)r4r5)r3rrrrjoin)r familyZ valid_typesr!r!r"checks zRich_Reject.check)NN)r&r'r(r#r%r8r!r!r!r"rs c@seZdZddZdS)rcCsd|jrd|jndS)Nzdrop%sz %sr)r2)r r!r!r"r%szRich_Drop.__str__N)r&r'r(r%r!r!r!r"rsc@s&eZdZdddZddZddZdS) rNcCs||_||_dS)N)setr2)r Z_setr2r!r!r"r#szRich_Mark.__init__cCsd|j|jrd|jndfS)Nz mark set=%s%sz %sr)r9r2)r r!r!r"r%szRich_Mark.__str__cCs|jdk r|j}n ttjdd|krv|jd}t|dkrHttj|tj|d shtj|d rttj|ntj|sttj|dS)Nz no value set/r)r9rrZ INVALID_MARKsplitlenrZ checkUINT32)r xsplitsr!r!r"r8s      zRich_Mark.check)N)r&r'r(r#r%r8r!r!r!r"rs r<<)smhdc@seZdZdddZddZeddZejddZed d Zejd d Ze d d Z ddZ e ddZ ddZ ddZdS)rNcCs||_||_dS)N)r,burst)r r,rGr!r!r"r#szRich_Limit.__init__cCs|j|jdS)N) value_parse burst_parse)r r!r!r"r8szRich_Limit.checkcCs|jS)N)_value)r r!r!r"r,szRich_Limit.valuec Csf|dkrd|_dSy|j|\}}Wntk r<|}YnX|d|}t|dd|krb||_dS)Nr:rJ)rJ _value_parsergetattr)r r,ratedurationvr!r!r"r,s cCs|jS)N)_burst)r r!r!r"rG szRich_Limit.burstc Cs\|dkrd|_dSy|j|}Wntk r8|}Yn Xt|}t|dd|krX||_dS)NrP)rP _burst_parserstrrL)r rGbr!r!r"rGs c Csd}d|kr|jd}| s(t|dkr4ttj||\}}y t|}Wnttj|YnX|dkrv|dd}|dks|dkrttj|d t||d krttjd|f|dkr|d krttjd|f||fS)Nr:r;secondminutehourdayr<rCrDrErFi'rz %s too fastz %s too slow)rTrUrVrW)rCrDrErF)r=r>rr INVALID_LIMITintDURATION_TO_MULT)r,r@rMrNr!r!r"rKs&     zRich_Limit._value_parsecCs |j|jS)N)rKrJ)r r!r!r"rH:szRich_Limit.value_parsec CsR|dkr dSy t|}Wnttj|YnX|dksB|dkrNttj||S)Nr<i)rYrrrX)rGrSr!r!r"rQ=s  zRich_Limit._burst_parsecCs |j|jS)N)rQrP)r r!r!r"rIKszRich_Limit.burst_parsecCs,d|jd}|jdk r(|d|j7}|S)Nz limit value=""z burst=)rJrP)r rCr!r!r"r%Ns zRich_Limit.__str__)N)r&r'r(r#r8propertyr,setterrG staticmethodrKrHrQrIr%r!r!r!r"rs     c@s>eZdZdZdZdddZddZd d Zd d Zd dZ dS)riiNrcCsV|dk rt||_nd|_||_d|_d|_d|_d|_d|_d|_|rR|j |dS)N) rRr7prioritysource destinationelementlogauditaction_import_from_string)r r7rule_strr_r!r!r"r#Xs zRich_Rule.__init__cCsg}x|tj|D]n}d|krp|jd}t|dksF|d sF|d rVttjd||j|d|ddq|jd|iqW|jddi|S) z Lexical analysis =r;rr<zinternal error in _lexer(): %s) attr_name attr_valuerbEOL)rZ splitArgsr=r>rrrappend)r rgtokensrattrr!r!r"_lexeris   zRich_Rule._lexerc Cs|sttjdtj|}d|_d|_d|_d|_d|_ d|_ d|_ d|_ |j |}|rv|djddkrvttjdi}g}d}x`||jddko|dgks||jd}||jd}||jd}|r|dHkrttjd|n|dIkr|dkr|jrttjd+n|dkr<|jr|d <nBt|jd |jd |jd |jd d?|_|j|j|d2}n| dkr,|dOkr|||<nN|dPkrd>|d <n:t|jd |jd |jd d?|_|j|j|d2}n| dkrd|dkrTt||_ |jn ttjd@nv| dkr|dkrt||_ |jn ttjdAn>| dkr|dQkr|||<n0t|jd|jd|_ |j|j|d2}n| dkr&|dkrt||_ |jn ttjdBn| dkr^|dkrNt||_ |jn ttjdCn|| dkrt|_ |j|j|d2}nN| d kr|dRkr|||<n@t|jd|jd|jd|jd|_ |j|j|d2}n| d!kr@|dSkr |||<n0t|jd|jd|_ |j|j|d2}n| d"kr|dTkr^|||<nN|d(krt|jd(n8t |jd|jd|jd(|_ |j|j|d2}n*| d#kr|d(kr|jd(n(t!|jd(|_ |j|j|d2}n| d$krH|d(kr|jd(n(t"|jd(|_ |j|j|d2}n| d%kr|d(krh|jd(n(t#|jd(|_ |j|j|d2}nF| d&kr|dkr|||<nF|d(kr|jd(n0t$|jd|jd(|_ |j|j|d2}n| d'kr`|dkr|||<nF|d(kr.|jd(n0t%|jd|jd(|_ |j|j|d2}nz| d(kr|dUkr||dD|<nVdE|krttjdFt&|dE|jdG|d(<|jdEd|jdGd|j|d2}|d2}qW|j'dS)VNz empty rulerrbrkrulerirjr_r7addressrrrr,r*r+to-portto-addrr)r0r1r3r9rGzbad attribute '%s'r`raservice icmp-block icmp-typer- forward-port source-portrcrdacceptdroprejectmarkr2notNOTzmore than one 'source' elementz#more than one 'destination' elementzFmore than one element. There cannot be both '%s' and '%s' in one rule.zmore than one 'log' elementzmore than one 'audit' elementzOmore than one 'action' element. There cannot be both '%s' and '%s' in one rule.zunknown element %sr<rz0'family' outside of rule. Use 'rule family=...'.z4'priority' outside of rule. Use 'rule priority=...'.z:'%s' outside of any element. Use 'rule %s= ...'.z,'%s' outside of rule. Use 'rule ... %s ...'.r4r5zH'family' attribute cannot have '%s' value. Use 'ipv4' or 'ipv6' instead.z(invalid 'priority' attribute value '%s'.zdwrong 'protocol' usage. Use either 'rule protocol value=...' or 'rule [forward-]port protocol=...'.zDattribute '%s' outside of any element. Use 'rule %s= ...'.TFzinvalid 'protocol' elementzinvalid 'service' elementzinvalid 'icmp-block' elementzinvalid 'icmp-type' elementzlimit.z limit.valuezinvalid 'limit' elementz limit.burst)r_r7rrrrrr,r*r+rsrtr)r0r1r3r9rG)rqr`rar+rur*rvrwr-rxryrcrdrzr{r|r}r2r~rrk)r+rur*rvrwr-rxry)rzr{r|r})r4r5)rrrrr)r~r)rrrr)r~r)r*r+)r*r+rsrt)r*r+)r0r1)r,rG)(rrrrZstripNonPrintableCharactersr_r7r`rarbrcrdrerpgetr>rlrY ValueErrorINVALID_PRIORITYrpopclearrrrrrrrr r r r r rrrrr8) r rgrmZattrsZ in_elementsindexrbrirjZ in_elementZerr_msgr!r!r"rfzs    ""               *      "                          (                                            zRich_Rule._import_from_stringc Cs`|jdk r"|jd kr"ttj|j|jdkrn|jdk rB|jjdk sL|jdk rVttjt|j t krnttj|j |j ks|j |j krttjd|j |j f|j dko|jdks|jdk o|j dkr |jdkrttjd|jdko|jdko|j dkr ttjdt|j tt tgkrP|jdkrP|jdkrP|jdkrPttjd|jdk rj|jjdk r|jdkrttj|jjdk rttjd|jjdk rttjd tj|j|jjsjttjt|jjn|jjdk r,|jjdk rttjd tj|jjsjttjt|jjn>|jjdk r^t|jjsjttjt|jjn ttjd |jdk r|jjdk r|jdkrttj|jjdk rttjd tj|j|jjsttjt|jjn>|jjdk rt|jjsttjt|jjn ttjd t|j t krd|j j!dksLt"|j j!d kr`ttj#t|j j!nt|j t$krtj%|j j&sttj'|j j&|j j(d!kr`ttj)|j j(nt|j t*krtj+|j j,s`ttj)|j j,nvt|j tkr<|jdk rttjd|jdk r`|jjdk r`ttjdn$t|j tkr|j j!dkslt"|j j!d krttj-t|j j!|jr`ttjdnt|j t.kr|j j!dkst"|j j!d kr`ttj-t|j j!nt|j t krtj%|j j&sttj'|j j&|j j(d"kr.ttj)|j j(|j j/dkrZ|j j0dkrZttj'|j j/|j j/dkrtj%|j j/ rttj'|j j/|j j0dkrtj1|j|j j0 rttj|j j0|jdkrttj|jdk r`ttjdnrt|j t2kr>tj%|j j&sttj'|j j&|j j(d#kr`ttj)|j j(n"|j dk r`ttjdt|j |jdk r|jj3r|jj3d$krttj4|jj3|jj5dk r|jj5j6|jdk rt|jt7t8t9gkrttj:t|j|jj5dk r|jj5j6|jdk r\t|jt8kr(|jj6|jnt|jt;krB|jj6|jj5dk r\|jj5j6dS)%Nr4r5z/'priority' attribute must be between %d and %d.rzno element, no actionz%no element, no source, no destinationzno action, no log, no auditzaddress and maczaddress and ipsetz mac and ipsetzinvalid sourcezinvalid destinationr<tcpudpsctpdccpzmasquerade and actionzmasquerade and mac sourcezicmp-block and actionrzforward-port and actionzUnknown element %semergalertcriterrorwarningnoticeinfodebug)r4r5)rrrr)rrrr)rrrr)rrrrrrrr)ZINVALID_SERVICErZ check_portr*Z INVALID_PORTr+ZINVALID_PROTOCOLrZ checkProtocolr,ZINVALID_ICMPTYPErr.r/Zcheck_single_addressr r1ZINVALID_LOG_LEVELr2r8r rrZINVALID_AUDIT_TYPEr)r r!r!r"r8hs                                          zRich_Rule.checkcCsd}|jr|d|j7}|jr,|d|j7}|jr@|d|j7}|jrT|d|j7}|jrh|d|j7}|jr||d|j7}|jr|d|j7}|jr|d|j7}tj rtj |S|S)Nrqz priority="%d"z family="%s"z %s) r_r7r`rarbrcrdrerZPY2Zu2b)r r$r!r!r"r%s$zRich_Rule.__str__i)NNr) r&r'r(rrr#rprfr8r%r!r!r!r"rTs o-NiiiQ)__all__ZfirewallrZfirewall.core.ipsetrZfirewall.core.baserrZfirewall.errorsrobjectrrrrr rrrrr r r r rrrrZrrr!r!r!r"s@      dPK!P@ @ (__pycache__/modules.cpython-36.opt-1.pycnu[3 Yj@sBdZdgZddlmZddlmZddlmZGdddeZ dS)zmodules backendmodules)runProg)log)COMMANDSc@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dS)rcCstd|_td|_dS)NZmodprobeZrmmod)r _load_command_unload_command)selfr /usr/lib/python3.6/modules.py__init__s zmodules.__init__cCs d|jS)Nz%s) __class__)rr r r __repr__$szmodules.__repr__cCsg}i}ytddp}xh|D]`}|s&P|j}|j}|j|d|ddkrp|djddd ||d<qg||d<qWWdQRXWntk rYnX||fS) z6 get all loaded kernel modules and their dependencies z /proc/modulesrr-,N)openstripsplitappendFileNotFoundError)rmodsdepsflineZsplitsr r r loaded_modules's     zmodules.loaded_modulescCs"tjd|j|j|t|j|gS)Nz %s: %s %s)rdebug2r rr)rmoduler r r load_module<szmodules.load_modulecCs"tjd|j|j|t|j|gS)Nz %s: %s %s)rrr rr)rrr r r unload_module@szmodules.unload_modulecCsT||kr dSx0||D]$}|j|||||kr|j|qW||krP|j|dS)z get all dependants of a module N)get_depsr)rrrretmodr r r r"Dszmodules.get_depscCsg}|j\}}|jd||x*dD]"}||kr$|j||jd|q$Wx^|D]V}|dks|jds|jd s|jd s|jd s|jd s|jd rP|j|||qPW|S)z) get all loaded firewall-related modules Z nf_conntracknf_conntrack_ipv4nf_conntrack_ipv6r ip_tables ip6_tablesebtablesZiptable_Z ip6table_Znf_Zxt_Zipt_Zip6t_)r%r&r)r'r(r))rr"removeinsert startswith)rrZmods2rZbad_bad_moduler$r r r get_firewall_modulesOs    zmodules.get_firewall_modulescCs>x8|jD],}|j|\}}|dkr tjd||fq WdS)z% unload all firewall-related modules rz Failed to unload module '%s': %sN)r-r!rZdebug1)rrZstatusr#r r r unload_firewall_modulesdszmodules.unload_firewall_modulesN) __name__ __module__ __qualname__r r rr r!r"r-r.r r r r rs N) __doc____all__Zfirewall.core.progrZfirewall.core.loggerrZfirewall.configrobjectrr r r r s    PK!{kS'__pycache__/helper.cpython-36.opt-1.pycnu[3 Yj$@s dZdZdS)zThe helper maxnamelen N)__doc__ZHELPER_MAXNAMELENrr/usr/lib/python3.6/helper.pysPK!;.?,__pycache__/fw_icmptype.cpython-36.opt-1.pycnu[3 Yj @s>dgZddlmZddlmZddlmZGdddeZdS)FirewallIcmpType)log)errors) FirewallErrorc@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dS)rcCs||_i|_dS)N)_fw _icmptypes)selffwr !/usr/lib/python3.6/fw_icmptype.py__init__szFirewallIcmpType.__init__cCsd|j|jfS)Nz%s(%r)) __class__r)rr r r __repr__!szFirewallIcmpType.__repr__cCs|jjdS)N)rclear)rr r r cleanup$szFirewallIcmpType.cleanupcCst|jjS)N)sortedrkeys)rr r r get_icmptypes)szFirewallIcmpType.get_icmptypescCs||jkrttj|dS)N)rrrZINVALID_ICMPTYPE)ricmptyper r r check_icmptype,s zFirewallIcmpType.check_icmptypecCs|j||j|S)N)rr)rrr r r get_icmptype0s zFirewallIcmpType.get_icmptypecCs|j}t|dkrddg}x|D]z}|dkrL|jj rB|jj rBq |jj}n,|dkrt|jj rj|jj rjq |jj}ng}|jj |kr t j d|j|fq W||j |j<dS)NrZipv4Zipv6z5ICMP type '%s' is not supported by the kernel for %s.) Z destinationlenrZip4tables_enabledZnftables_enabledZipv4_supported_icmp_typesZip6tables_enabledZipv6_supported_icmp_typesnamelowerrZinfo1r)robjZ orig_ipvsZipvZsupported_icmpsr r r add_icmptype4s     zFirewallIcmpType.add_icmptypecCs|j||j|=dS)N)rr)rrr r r remove_icmptypeGs z FirewallIcmpType.remove_icmptypeN) __name__ __module__ __qualname__r rrrrrrrr r r r rsN) __all__Zfirewall.core.loggerrZfirewallrZfirewall.errorsrobjectrr r r r s   PK!{0z%__pycache__/prog.cpython-36.opt-1.pycnu[3 Yj@sddlZdgZdddZdS)NrunProgc Cs|dkr g}|g|}d}|r@t|d}|jj}WdQRXddi}y tj|tjtjtjd|d}Wntk r|d SX|j|\}} |j dd }|j |fS) NrZLANGCT)stdinstderrstdoutZ close_fdsenvzutf-8replace)r r ) openreadencode subprocessPopenPIPEZSTDOUTOSErrorZ communicatedecode returncode) progargvrargsZ input_stringZhandlerZprocessoutputZ err_outputr/usr/lib/python3.6/prog.pyrs$    )NN)r__all__rrrrrsPK!&__pycache__/fw_nm.cpython-36.opt-1.pycnu[3 Yj@s dZddddddddgZd d lZd d lmZyejd d Wnek rTdZYn8Xyd dlmZdZWn e eej fk rdZYnXd a d dl m Z d dlmZd dlmZd d lZddZddZddZddZddZddZddZddZdd Zd!dZd"dZd#dZd S)$z(Functions for NetworkManager interactioncheck_nm_importednm_is_importednm_get_zone_of_connectionnm_set_zone_of_connectionnm_get_connectionsnm_get_connection_of_interfacenm_get_bus_namenm_get_dbus_interfaceN)GLibNMz1.0F)r T)errors) FirewallError)logcCststtjddS)zNCheck function to raise a MISSING_IMPORT error if the import of NM failed zgi.repository.NM = 1.0N) _nm_importedr r ZMISSING_IMPORTrr/usr/lib/python3.6/fw_nm.pyr0scCstS)znReturns true if NM has been properly imported @return True if import was successful, False otherwirse )rrrrrr6scCststjjdatS)zReturns the NM client object or None if the import of NM failed @return NM.Client instance if import was successful, None otherwise N) _nm_clientr ZClientnewrrrr nm_get_client<s rc Csttj|}|dkrdS|j}|dkr2dSy |jtjjtjjB@rPdSWn t k rr|j rndSYnX|j }|dkrd}|S)zGet zone of connection from NM @param connection name @return zone string setting of connection, empty string if not set, None if connection is unknown N) rrget_connection_by_uuidget_setting_connection get_flagsr SettingsConnectionFlags NM_GENERATED NM_VOLATILEAttributeError get_unsavedZget_zone) connectioncon setting_conzonerrrrEs$    cCsVttj|}|dkrdS|j}|dkr2dS|dkr>d}|jd||jddS)zSet the zone for a connection @param zone name @param connection name @return True if zone was set, else False NFrr!T)rrrrZ set_propertyZcommit_changes)r!rrr rrrrcs  c Cs~|j|jttj}xX|D]P}|jr4q&|j}|j}|j}|||<x |D]}|j}|rZ|||<qZWq&WdS)znGet active connections from NM @param connections return dict @param connections_name return dict N) clearrrget_active_connectionsget_vpnZget_idget_uuid get_devices get_ip_iface) Z connectionsZconnections_nameZactive_connections active_connameZuuidZdevicesdevip_ifacerrrrxs   c Cstg}xtjD]|}|jr$qy&|j}|jtjjtjj B@rHwWnt k rh|j rdwYnXx&|j D]}|j }|rt|j|qtWqW|S)zGGet active interfaces from NM @returns list of interface names )rrr#r$get_connectionrr rrrrrr&r'append)Zactive_interfacesr(rr*r+rrrnm_get_interfacess$  r.cCs6g}x,tD]"}t|}|t|kr |j|q W|S)N)r.rrr-)r!Z interfaces interfaceZconnrrrnm_get_interfaces_in_zones   r0cCs<tx0tjD]"}|j}|dkr(q||kr|SqWdS)zzGet device from NM which has the given IP interface @param interface name @returns NM.Device instance or None N)rrr&r')r/devicer+rrrnm_get_device_by_ip_ifacesr2c Csxtt|}|dkrdS|j}|dkr.dSy |j}|jtjj@rLdSWn tk rn|j rjdSYnX|j S)zGet connection from NM that is using the interface @param interface name @returns connection that is using interface or None N) rr2Zget_active_connectionr,rr rrrrr%)r/r1r(rrrrrs c CsRtsdSy&tj}|jtjtj}|j}~~|Stk rLt j dYnXdS)Nz(Failed to get bus name of NetworkManager) rdbusZ SystemBusZ get_objectr DBUS_INTERFACEZ DBUS_PATHZbus_name ExceptionrZdebug2)Zbusobjr)rrrrscCstsdStjS)Nr)rr r4rrrrrs)__doc____all__ZgiZ gi.repositoryr Zrequire_version ValueErrorrr ImportErrorErrorrZfirewallr Zfirewall.errorsr Zfirewall.core.loggerrr3rrrrrrr.r0r2rrrrrrrs@           PK!+0̋#__pycache__/fw_ifcfg.cpython-36.pycnu[3 Yj @sTdZddgZddlZddlZddlmZddlmZddlm Z ddZ d dZ dS) z.Functions to search for and change ifcfg filessearch_ifcfg_of_interfaceifcfg_set_zone_of_interfaceN)config)log)ifcfgcCstjjtjsdSxtttjtjD]`}|jds4q$xd D]}|j|r:q:q:Wd |krXq$t d tj|f}|j |j d |kr$|Sq$Wd tj|f}tjj|rt |}|j |SdS)z6search ifcfg file for the interface in config.IFCFGDIRNzifcfg-.bak.orig.rpmnew.rpmorig.rpmsave-range.z%s/%sZDEVICEz %s/ifcfg-%s)rrr r r r ) ospathexistsrZIFCFGDIRsortedlistdir startswithendswithrreadget) interfacefilenameZignored ifcfg_filer/usr/lib/python3.6/fw_ifcfg.pyr!s*   cCsn|dkr d}t|}|dk rj|jd|krj|jddko>|dk rjtjd||jf|jd||jdS)zYSet zone (ZONE=) in the ifcfg file that uses the interface (DEVICE=)NZZONEzSetting ZONE=%s in '%s')rrrZdebug1rsetwrite)Zzonerrrrrr?s ) __doc____all__rZos.pathZfirewallrZfirewall.core.loggerrZfirewall.core.io.ifcfgrrrrrrrs   PK!Xm "__pycache__/watcher.cpython-36.pycnu[3 Yj @s*dgZddlmZmZGdddeZdS)Watcher)GioGLibc@sdeZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ ddZ ddZ dS)rcCs"||_||_i|_i|_g|_dS)N) _callback_timeout _monitors _timeouts_blocked)selfcallbackZtimeoutr /usr/lib/python3.6/watcher.py__init__s zWatcher.__init__cCs:tjj|}|jtjjd|j|<|j|jd|jdS)Nchanged) rFile new_for_pathZmonitor_directoryFileMonitorFlagsNONErconnect_file_changed_cb)r Z directorygfiler r r add_watch_dir"s zWatcher.add_watch_dircCs:tjj|}|jtjjd|j|<|j|jd|jdS)Nr) rrrZ monitor_filerrrrr)r filenamerr r r add_watch_file(s zWatcher.add_watch_filecCs |jjS)N)rkeys)r r r r get_watches.szWatcher.get_watchescCs ||jkS)N)r)r rr r r has_watch1szWatcher.has_watchcCs |j|=dS)N)r)r rr r r remove_watch4szWatcher.remove_watchcCs||jkr|jj|dS)N)r append)r rr r r block_source7s zWatcher.block_sourcecCs||jkr|jj|dS)N)r remove)r rr r r unblock_source;s zWatcher.unblock_sourcecCs4x.t|jjD]}tj|j||j|=qWdS)N)listrrr source_remove)r rr r r clear_timeouts?szWatcher.clear_timeoutscCs ||jkr|j||j|=dS)N)r rr)r rr r r _call_callbackDs  zWatcher._call_callbackcCs|j}||jkr8||jkr4tj|j||j|=dS|tjjksh|tjjksh|tjj ksh|tjj kr||jkrtj|j||j|=tj |j |j ||j|<dS)N)Zget_parse_namer rrr#rZFileMonitorEventZCHANGEDZCREATEDZDELETEDZATTRIBUTE_CHANGEDZtimeout_add_secondsrr%)r ZmonitorZgio_fileZgio_other_fileZeventrr r r rIs       zWatcher._file_changed_cbN)__name__ __module__ __qualname__rrrrrrrr!r$r%rr r r r rsN)__all__Z gi.repositoryrrobjectrr r r r sPK!(L+CC)__pycache__/fw_ipset.cpython-36.opt-1.pycnu[3 Yj%@sfdZdgZddlmZddlmZmZmZm Z ddl m Z ddl m Z ddlmZGdddeZd S) z ipset backend FirewallIPSet)log)remove_default_create_optionsnormalize_ipset_entrycheck_entry_overlaps_existingcheck_for_overlapping_entries)IPSet)errors) FirewallErrorc@seZdZddZddZddZddZd d Zd d Zd dZ d4ddZ ddZ ddZ d5ddZ ddZddZddZd6dd Zd!d"Zd#d$Zd%d&Zd7d'd(Zd)d*Zd+d,Zd-d.Zd/d0Zd1d2Zd3S)8rcCs||_i|_dS)N)_fw_ipsets)selffwr/usr/lib/python3.6/fw_ipset.py__init__#szFirewallIPSet.__init__cCsd|j|jfS)Nz%s(%r)) __class__r )r rrr__repr__'szFirewallIPSet.__repr__cCs|jjdS)N)r clear)r rrrcleanup,szFirewallIPSet.cleanupcCs||jkrttj|dS)N) get_ipsetsr r Z INVALID_IPSET)r namerrr check_ipset/s zFirewallIPSet.check_ipsetcCs ||jkS)N)r)r rrrr query_ipset3szFirewallIPSet.query_ipsetcCst|jjS)N)sortedr keys)r rrrr6szFirewallIPSet.get_ipsetscCst|jdkS)Nr)lenr )r rrr has_ipsets9szFirewallIPSet.has_ipsetsFcCs&|j||j|}|r"|j||S)N)rr check_applied_obj)r rappliedobjrrr get_ipset<s    zFirewallIPSet.get_ipsetcCs4g}|jjr|j|jj|jjr0|j|jj|S)N)r Znftables_enabledappendZnftables_backendZ ipset_enabledZ ipset_backend)r backendsrrrr#Cs zFirewallIPSet.backendscCs0|j|jjkr ttjd|j||j|j<dS)Nz'%s' is not supported by ipset.)typer Zipset_supported_typesr r Z INVALID_TYPEr r)r r rrr add_ipsetKs zFirewallIPSet.add_ipsetcCs|j|}|jrh| rhy x|jD]}|j|q"WWqttk rd}zttj|WYdd}~XqtXn tj d||j|=dS)Nz,Keeping ipset '%s' because of timeout option) r rr# set_destroy Exceptionr r COMMAND_FAILEDrdebug1)r rZkeepr backendmsgrrr remove_ipsetQs    zFirewallIPSet.remove_ipsetc<Cs$|j|}x|jD]}|jdkr|j}||krd|jksv|jddksv|j||dksvt|j||dkry|j|Wn.tk r}zt t j |WYdd}~XnX|j j ry|j|j|j|jWn0tk r}zt t j |WYdd}~Xn&Xd|_d|jkr,|jddkr,qy|j|jWn0tk rl}zt t j |WYdd}~XnXx|jD]J}y|j|j|Wn0tk r}zt t j |WYdd}~XnXqvWqy|j|j|j|j|jdWn0tk r}zt t j |WYdd}~XqXd|_qWdS)Nipsettimeout0rT)r r#rZset_get_active_terseoptionsr$rm_def_cr_optsr&r'r r r(r _individual_callsZ set_creater set_flushentriesset_add set_restore)r rr r*Zactiver+entryrrr apply_ipset]sL     &  zFirewallIPSet.apply_ipsetcCs>x8|jD],}|j|}d|_tjd||j|q WdS)NFzApplying ipset '%s')rr rrr)r9)r rr rrr apply_ipsetss  zFirewallIPSet.apply_ipsetscCsxz|jD]n}|jdkrq x\|jD]P}y|j||j|Wq$tk rr}z|jtjkrb|WYdd}~Xq$Xq$Wq WdS)NZnftables) r#rr check_appliedr&r coder NOT_APPLIED)r r*r-r+rrrflushs   zFirewallIPSet.flushTcCs|j||djS)N)r)r!r$)r rrrrrget_typeszFirewallIPSet.get_typecCst|j|ddjjdS)NT)r,)rr!r$split)r rrrr get_dimensionszFirewallIPSet.get_dimensioncCs|j|}|j|dS)N)r!r)r rr rrrr;s zFirewallIPSet.check_appliedcCs|jsttj|jdS)N)rr r r=r)r r rrrrszFirewallIPSet.check_applied_objcCs.|j||d}d|jkr*|jddkr*dSdS)N)rZfamilyZinet6Zipv6Zipv4)r!r1)r rrr rrr get_familys  zFirewallIPSet.get_familycCs|j|dd}t|}tj||j|j||jkrFttj d||ft ||jy$x|j D]}|j |j |q^WWn.tk r}zttj|WYdd}~Xn&Xd|jks|jddkr|jj|dS)NT)rz'%s' already is in '%s'r.r/)r!rr check_entryr1r$r5r r ZALREADY_ENABLEDrr#r6rr'r(r")r rr8r r*r+rrr add_entrys  zFirewallIPSet.add_entrycCs|j|dd}t|}||jkr4ttjd||fy$x|jD]}|j|j|q@WWn.t k r}zttj |WYdd}~Xn&Xd|j ks|j ddkr|jj |dS)NT)rz'%s' not in '%s'r.r/) r!rr5r r Z NOT_ENABLEDr#Z set_deleterr'r(r1remove)r rr8r r*r+rrr remove_entrys zFirewallIPSet.remove_entrycCsD|j|dd}t|}d|jkr:|jddkr:ttj|||jkS)NT)rr.r/)r!rr1r r ZIPSET_WITH_TIMEOUTr5)r rr8r rrr query_entrys  zFirewallIPSet.query_entrycCs|j|dd}|jS)NT)r)r!r5)r rr rrr get_entriesszFirewallIPSet.get_entriescCs@|j|dd}t|x|D]}tj||j|jqWd|jksN|jddkrT||_y"x|jD]}|j|j q`WWn.t k r}zt t j |WYdd}~XnXd|_yXxR|jD]F}|jjrx8|jD]}|j|j |qWq|j|j |j|j|jdqWWn0t k r4}zt t j |WYdd}~XnXd|_dS)NT)rr.r/)r!rrrDr1r$r5r#r4rr'r r r(rr r3r6r7)r rr5r r8r*r+rrr set_entriess.  zFirewallIPSet.set_entriesN)F)F)T)T)__name__ __module__ __qualname__rrrrrrrr!r#r%r,r9r:r>r?rBr;rrCrErGrHrIrJrrrrr"s0  1   N)__doc____all__Zfirewall.core.loggerrZfirewall.core.ipsetrr2rrrZfirewall.core.io.ipsetrZfirewallr Zfirewall.errorsr objectrrrrrs    PK!jyy$__pycache__/ipXtables.cpython-36.pycnu[3 Yj%@s<ddlZddlZddlmZddlmZddlmZm Z m Z m Z m Z m Z mZmZddlmZddlmZmZmZmZmZmZddlmZmZmZmZmZmZm Z ddl!m"Z"m#Z#ddl$Z$d Z%d d d gd d gd dd d d gd dd gd d d gdZ&dddZ'dddZ(ddZ)ddZ*ddZ+Gddde,Z-Gddde-Z.dS)N)runProg)log)tempFilereadfile splitArgs check_macportStrcheck_single_address check_address normalizeIP6)config) FirewallErrorINVALID_PASSTHROUGH INVALID_RULE UNKNOWN_ERROR INVALID_ADDRINVALID_ICMPTYPE) Rich_Accept Rich_Reject Rich_Drop Rich_MarkRich_MasqueradeRich_ForwardPortRich_IcmpBlock) ICMP_TYPES ICMPV6_TYPESINPUTOUTPUTFORWARD PREROUTING POSTROUTING)securityrawmanglenatfilterzicmp-host-prohibitedzicmp6-adm-prohibited)ipv4ipv6icmpz ipv6-icmpcCsddddddd}|dd}x~|D]v}y|j|}Wntk rLw$YnX|d kryt||d Wntk r~YnX|j|d ||||<q$W|S) z Inverse valid rule z-Dz--deletez-Xz--delete-chain)z-Az--appendz-Iz--insertz-Nz --new-chainN-I--insert)r*r+)index Exceptionintpop)args replace_argsret_argsargidxr6/usr/lib/python3.6/ipXtables.pycommon_reverse_rule;s(  r8cCsddddddd}|dd}x|D]x}y|j|}Wntk rLw$YnX|d kryt||d Wntk r~YnX|j|d ||||<|SWttd dS) z Reverse valid passthough rule z-Dz--deletez-Xz--delete-chain)z-Az--appendz-Iz--insertz-Nz --new-chainN-I--insertr,zno '-A', '-I' or '-N' arg)r9r:)r- ValueErrorr/r0r r)r1r2r3xr5r6r6r7common_reverse_passthrough`s,   r=cCst|}tddddddddd d d d d dddddddg}t||@dkrbttdt||@dtddddddg}t||@dkrttddS)zZ Check if passthough rule is valid (only add, insert and new chain rules are allowed) z-Cz--checkz-Dz--deletez-Rz --replacez-Lz--listz-Sz --list-rulesz-Fz--flushz-Zz--zeroz-Xz--delete-chainz-Pz--policyz-Ez--rename-chainrzarg '%s' is not allowedz-Az--appendz-Iz--insertz-Nz --new-chainzno '-A', '-I' or '-N' argN)setlenr rlist)r1Z not_allowedZneededr6r6r7common_check_passthroughs*  rAc@seZdZdZdZdZddZddZddZd d Z d d Z d dZ ddZ ddZ ddZddZddZddZddZddZdd Zdjd"d#Zd$d%Zd&d'Zd(d)Zd*d+Zdkd,d-Zd.d/Zdld1d2Zd3d4Zd5d6Zdmd8d9Zdnd:d;Z dd?Z"d@dAZ#dBdCZ$dDdEZ%dFdGZ&dHdIZ'dJdKZ(dLdMZ)dNdOZ*dPdQZ+dodRdSZ,dpdTdUZ-dqdVdWZ.dXdYZ/drdZd[Z0dsd\d]Z1dtd^d_Z2d`daZ3dudbdcZ4dddeZ5dfdgZ6dhdiZ7d!S)v ip4tablesr'TcCsd||_tj|j|_tjd|j|_|j|_|j|_ |j g|_ i|_ i|_ g|_i|_dS)Nz %s-restore)_fwr ZCOMMANDSipv_command_restore_command_detect_wait_option wait_option_detect_restore_wait_optionrestore_wait_option fill_existsavailable_tablesrich_rule_priority_countspolicy_priority_countszone_source_index_cache our_chains)selffwr6r6r7__init__s  zip4tables.__init__cCs$tjj|j|_tjj|j|_dS)N)ospathexistsrEZcommand_existsrFZrestore_command_exists)rQr6r6r7rKszip4tables.fill_existscCs|jr(|j|kr(|jgdd|D}ndd|D}tjd|j|jdj|t|j|\}}|dkrtd|jdj||f|S)NcSsg|] }d|qS)z%sr6).0itemr6r6r7 sz#ip4tables.__run..cSsg|] }d|qS)z%sr6)rWrXr6r6r7rYsz %s: %s %s rz'%s %s' failed: %s)rHrdebug2 __class__rEjoinrr;)rQr1Z_argsstatusretr6r6r7Z__runszip4tables.__runc Cs<y|j|}Wntk r"dSX||||d<dSdS)NFT)r-r;)rQrulepatternZ replacementir6r6r7 _rule_replaces zip4tables._rule_replacecCs|tko|t|kS)N)BUILT_IN_CHAINS)rQrDtablechainr6r6r7is_chain_builtinszip4tables.is_chain_builtincCs2d|g}|r|jdn |jd|j||gS)Nz-tz-Nz-X)append)rQaddrfrgrar6r6r7build_chain_ruless    zip4tables.build_chain_rulescCs8d|g}|r |d|t|g7}n |d|g7}||7}|S)Nz-tz-Iz-D)str)rQrjrfrgr-r1rar6r6r7 build_rules  zip4tables.build_rulecCst|S)N)r8)rQr1r6r6r7 reverse_ruleszip4tables.reverse_rulecCs t|dS)N)rA)rQr1r6r6r7check_passthroughszip4tables.check_passthroughcCst|S)N)r=)rQr1r6r6r7reverse_passthroughszip4tables.reverse_passthroughcCsd}y|jd}Wntk r&YnXt||dkrD||d}d}xLd D]D}y|j|}Wntk rtYqNXt||dkrN||d}qNW||fS) Nr&z-tr`-A--append-I--insert-N --new-chain)rqrrrsrtrurv)r-r;r?)rQr1rfrcrgoptr6r6r7passthrough_parse_table_chains$ z'ip4tables.passthrough_parse_table_chaincCs4yH|jd}|j||j|}d|dkr:||df}n ||df}WnFtk ry|jd}|j|d}Wntk rdSXYnXd}|ddkrd }|r| r||kr|j|nn|r0|r||kr|j||jd d d|j|}n|jjr d}nt|}d|d<|j dd|ddS)Nz%%ZONE_SOURCE%%z-mz%%ZONE_INTERFACE%%Tr-D--deleteFcSs|dS)Nrr6)r<r6r6r7(sz4ip4tables._run_replace_zone_source..)keyz-Ir,z%dr`)r|r}) r-r0r;removerisortrC_allow_zone_driftingr?insert)rQrarOrczoneZ zone_sourcerule_addr-r6r6r7_run_replace_zone_source s>             z"ip4tables._run_replace_zone_sourcecCsy|j|}Wntk r$YnXd}d}d}|j||j|}t|tkr\ttdd} xLdD]D} y|j| } Wntk rYqfXt|| dkrf|| d} qfWxhdD]`} y|j| }Wntk rYqXt||dkr||d} | dkrd}| dkrd}qW| | f} |sp| |ksP||| ksP|| |dkrZttd|| |d8<n| |kri|| <||| krd|| |<d} xHt || j D]4}||kr|rP| || |7} ||krPqW|| |d7<d ||<|j |dd| dS)a Change something like -t filter -I public_IN %%RICH_RULE_PRIORITY%% 123 or -t filter -A public_IN %%RICH_RULE_PRIORITY%% 321 into -t filter -I public_IN 4 or -t filter -I public_IN TFr`z%priority must be followed by a numberr&-t--table-A--append-I--insert-D--deleterz*nonexistent or underflow of priority countr,z%dN)rr)rrrrrr)rr)rr) r-r;r0typer/r rr?rsortedkeysr)rQraZpriority_countstokenrcrrZinsert_add_indexpriorityrfrwjrgr-pr6r6r7_set_rule_replace_priority4sj             z$ip4tables._set_rule_replace_prioritycCsPt}i}tj|j}tj|j}tj|j}x|D]}|dd} |j| dddt|jg|j| dt |jgy| j d} Wnt k rYn8X|dkrq6|d$krd d d |g| | | d <n | j | |j | |d|j | |d|j| |d} xZd%D]R} y| j | } Wnt k r,Yn(Xt| | d kr| j | | j | } qWxhtt| D]X} xPtjD]F} | | | krt| | jdo| | jd rtd| | | | <qtWqhW|j| gj| q6WxR|D]J} || }|jd| x"|D]} |jdj| dqW|jdqW|jtj|j}tjd|j|j d|j|j!fg}|j"rz|j|j"|jdt#|j ||jd\}}tj$dkr t%|j}|dk r d } xH|D]@}tj&d| |fd dd |jdstj&d!d d"| d 7} qWtj'|j|dkr:t d#|j dj||f||_||_||_dS)&Nz %%REJECT%%REJECTz --reject-withz%%ICMP%%z %%LOGTYPE%%offunicast broadcast multicastz-mpkttypez --pkt-typer`z%%RICH_RULE_PRIORITY%%z%%POLICY_PRIORITY%%r&-t--table"z"%s"z*%s rZ zCOMMIT z %s: %s %sz%s: %dz-n)stdinr,z%8d: %sr)nofmtnlr)rz'%s %s' failed: %s)rrr)rr)(rcopydeepcopyrMrNrOrdDEFAULT_REJECT_TYPErDICMPr-r;r0rrr?rangestringZ whitespace startswithendswith setdefaultriwriter]closerTstatnamerr[r\rFst_sizerJrZgetDebugLogLevelrZdebug3unlink)rQrules log_denied temp_fileZ table_rulesrMrNrOZ_rulerarcrfrwcrr1r^r_linesliner6r6r7 set_ruless                    zip4tables.set_rulesc Cs|j|dddt|jg|j|dt|jgy|jd}Wntk rRYn:X|dkr`dS|dkrd d d |g|||d<n |j|tj|j }tj|j }tj|j }|j ||d|j ||d|j |||j|}||_ ||_ ||_ |S)Nz %%REJECT%%rz --reject-withz%%ICMP%%z %%LOGTYPE%%rrrrrz-mrz --pkt-typer`z%%RICH_RULE_PRIORITY%%z%%POLICY_PRIORITY%%)rrr)rdrrDrr-r;r0rrrMrNrOrr_ip4tables__run)rQrarrcrMrNrOoutputr6r6r7set_rules.      zip4tables.set_ruleNc Csg}|r|gntj}xx|D]p}||jkr6|j|qy,|jd|ddg|jj||j|Wqtk rtjd|j|fYqXqW|S)Nz-tz-Lz-nzA%s table '%s' does not exist (or not enough permission to check).) rerrLrirr;rZdebug1rD)rQrfr_Ztablesr6r6r7get_available_tabless    zip4tables.get_available_tablescCs`d}t|jdddg}|ddkr\d}t|jdddg}|ddkrHd}tjd|j|j||S)Nrz-wz-Lz-nrz-w10z%s: %s will be using %s option.)rrErr[r\)rQrHr_r6r6r7rGs  zip4tables._detect_wait_optioncCst}|jd|jd}xJd D]B}t|j|g|jd}|ddkr"d|dkr"d |dkr"|}Pq"Wtjd |j|j|t j |j|S) Nz#foor-w--wait=2)rrzinvalid optionr`zunrecognized optionz%s: %s will be using %s option.)rr) rrrrrFrrr[r\rTr)rQrrHZ test_optionr_r6r6r7rI$s    z%ip4tables._detect_restore_wait_optioncCsVi|_i|_g|_g}x:tjD].}|j|s0q xdD]}|jd||gq6Wq W|S)N-F-X-Zz-t)rrr)rMrNrOrerrri)rQrrfflagr6r6r7build_flush_rules7s  zip4tables.build_flush_rulescCsfg}|dkrdn|}xLtjD]@}|j|s.q|dkr8qx$t|D]}|jd|d||gqBWqW|S)NZPANICDROPr%z-tz-P)rerrri)rQpolicyr_policyrfrgr6r6r7build_set_policy_rulesFs z ip4tables.build_set_policy_rulescCsNt}|dks|jdkr&|jtj|dks8|jdkrF|jtjt|S)Nr'r()r>rDupdaterrrr@)rQrDZ supportedr6r6r7supported_icmp_typesRs zip4tables.supported_icmp_typescCsgS)Nr6)rQr6r6r7build_default_tables\szip4tables.build_default_tablesrc Csi}|jdrpg|d<t|jd<xLtdD]@}|djd||djd||f|jdjd|q,W|jdr\g|d<t|jd<xtdD]}|djd||djd||f|jdjd||dkrxt|jjrddd d gndd d gD]R}|djd ||f|djd |||f|jdjtd ||fgqWqW|jdrNg|d<t|jd<xtdD]}|djd||djd||f|jdjd||dkrxv|jjrddd d gndd d gD]R}|djd ||f|djd |||f|jdjtd ||fgqWqW|jdr@g|d<t|jd<xtdD]}|djd||djd||f|jdjd||d9krxxv|jjrddd d gndd d gD]R}|djd ||f|djd |||f|jdjtd ||fgqWqxWg|d<t|jd<|djd|djd|djd|djd|jdjtdxf|jjrddd d gndd d gD]B}|djd||djd||jdjtd|qW|dkr |djd|djd|dkrF|djd|djd|djd|djd |djd!|djd"|jdjtd#xJd:D]B}|djd$||djd%||jdjtd&|qWxzd;D]r}xj|jjr dd gnd gD]N}|djd)||f|djd*||f|jdjtd+||fqWqWxJdD]B}|djd5||djd6||jdjtd7|q~Wg}xJ|D]B}||jkrqx(||D]}|jd8|gt |qWqW|S)?Nr"z -N %s_directz-A %s -j %s_directz %s_directr#r POLICIES_preZ ZONES_SOURCEZZONES POLICIES_postz-N %s_%sz-A %s -j %s_%sz%s_%sr$r%r!r&zB-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED,DNAT -j ACCEPTz-A INPUT -i lo -j ACCEPTz-N INPUT_directz-A INPUT -j INPUT_directZ INPUT_directz -N INPUT_%sz-A INPUT -j INPUT_%szINPUT_%srz^-A INPUT -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: 'z/-A INPUT -m conntrack --ctstate INVALID -j DROPz9-A INPUT %%LOGTYPE%% -j LOG --log-prefix 'FINAL_REJECT: 'z-A INPUT -j %%REJECT%%zD-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED,DNAT -j ACCEPTz-A FORWARD -i lo -j ACCEPTz-N FORWARD_directz-A FORWARD -j FORWARD_directZFORWARD_directz -N FORWARD_%sz-A FORWARD -j FORWARD_%sz FORWARD_%sINOUTz-N FORWARD_%s_%sz-A FORWARD -j FORWARD_%s_%sz FORWARD_%s_%sz`-A FORWARD -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: 'z1-A FORWARD -m conntrack --ctstate INVALID -j DROPz;-A FORWARD %%LOGTYPE%% -j LOG --log-prefix 'FINAL_REJECT: 'z-A FORWARD -j %%REJECT%%z-N OUTPUT_directz>-A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPTz-A OUTPUT -o lo -j ACCEPTz-A OUTPUT -j OUTPUT_directZ OUTPUT_directz -N OUTPUT_%sz-A OUTPUT -j OUTPUT_%sz OUTPUT_%sz-t)r r!)r)rr)r)r)r) rr>rPrerirjrCrrr) rQrZ default_rulesrgZdispatch_suffix directionZfinal_default_rulesrfrar6r6r7build_default_rules`s    $(   &*   &* &    (       "zip4tables.build_default_rulescCsf|dkrdddhS|dkr,d|jkr,dhS|dkrHd|jkrHddhS|d krbd |jkrbdhSiS) Nr&r FORWARD_IN FORWARD_OUTr$r r%r!r#)r)rQrfr6r6r7get_zone_table_chainss    zip4tables.get_zone_table_chainsc s|jjj|jdkrdnddkr4dkr4dnd} |jjj|t| g} g} x|D]} | jd| gqZWx|D]} | jd | gqvWxB|D]:} |jjj| }|dkr|j | rq| j|j d | qWx\|D]T} |jjj| }|dkr|j | rqt | rdkrq| j|j d| qWfdd}g}| rx| D]F}| rx8| D]}|j|||qdWn|rn|j||dqTWnH|rn@| rx8| D]}|j|d|qWn|rn|j|dd|S)Nrprepostr%r!TFz-iz-or'r(z-srrz-dcsVddd}d|dfdjg}|r6|j||rD|j||jdg|S)Nz-Az-D)TFz-tz%s_POLICIES_%sz%%POLICY_PRIORITY%%z-j)rextend)ingress_fragmentegress_fragmentadd_delra)rrg chain_suffixenablep_objrfr6r7_generate_policy_dispatch_rules   zSip4tables.build_policy_ingress_egress_rules.._generate_policy_dispatch_rule)r'r()r'r()r!rr) rCrZ get_policyrpolicy_base_chain_namePOLICY_CHAIN_PREFIXrirZ check_sourceis_ipv_supported_rule_addr_fragmentr)rQrrrfrgZingress_interfacesZegress_interfacesZingress_sourcesZegress_sourcesisSNATZingress_fragmentsZegress_fragments interfaceaddrrDrrrrr6)rrgrrrrfr7!build_policy_ingress_egress_rulessR        z+ip4tables.build_policy_ingress_egress_rulesFc Cs|dkr|dkrdnd}|jjj||t|d} ddddddd|} d } |rb| rbd d |d g} n,|rtd d |g} ndd |g} |s| d g7} | d|| || | g7} | gS)Nr%r!TF)rz-iz-o)r r!rrrrz-gz-Iz%s_ZONESz%%ZONE_INTERFACE%%z-Az-Dz-t)rCrrr) rQrrrrrfrgrirrrwactionrar6r6r7!build_zone_source_interface_rules6s&   z+ip4tables.build_zone_source_interface_rulescCs|jdrP|dd}|dkr$d}nd}dj|g|jjj|}ddd ||gSt|rz|dkrjttd dd d |jgSt d |rt |}n,t d |r|j d}t |dd|d}||gSdS)Nzipset:z-ddstsrc,z-mr>z --match-setzCan't match a destination MAC.macz --mac-sourcer(/rr`) rr]rCipsetZ get_dimensionrr rupperr r r split)rQrwaddressinvertrflags addr_splitr6r6r7rPs"       zip4tables._rule_addr_fragmentc Csddd|}|dkr"|dkr"dnd}|jjj||t|d} d d d d d d d |} |jjrdd |} nd |} t|r|dkrgS|| d|d|g} | j|j| || jd| g| gS)Nz-Iz-D)TFr%r!TF)rz-sz-d)r r!rrrrz%s_ZONES_SOURCEz%s_ZONESrrz%%ZONE_SOURCE%%z-tz-g)r!rr)rCrrrrrrr) rQrrrrrfrgrrrrwZzone_dispatch_chainrar6r6r7build_zone_source_address_rulesfs& z)ip4tables.build_zone_source_address_rulesc Cs>ddd|}ddd|}|dkr0|dkr0dnd }|jjj||t|d }|j|jt|d |d |d |d|d|gg} | j||d|g| j|d |d|g| j|d |d|g| j|d |d|g| j|d|d|g| j|d|d|g| j||d|dd |g| j||d|dd |g| j||d|dd |g| j||d|dd|g| j||d|dd|g|jjj|j } |jj dkr|dkr| dkr| j||d|ddddd|g | dkr| j||d|ddddd|g |dkr,| dkr,| j||d|d| g|s:| j | S)Nz-Nz-X)TFz-Az-Dr%r!TF)rz%s_logz%s_denyz%s_prez%s_postz%s_allowz-tz-jrr&r %%REJECT%%z %%LOGTYPE%%LOGz --log-prefixz "%s_REJECT: "rz "%s_DROP: "ACCEPT)rr)rrrr) rCrrrrPrr>riZ _policiestargetget_log_deniedreverse) rQrrrfrgZ add_del_chainZ add_del_rulerrrrr6r6r7build_policy_chain_rulessN       z"ip4tables.build_policy_chain_rulescCs2|sgSddd|jg}|jdk r.|d|jg7}|S)Nz-mlimitz--limitz --limit-burst)valueZburst)rQrsr6r6r7 _rule_limits  zip4tables._rule_limitcCst|jtttgkrn<|jrHt|jtttt gkrRt t dt|jn t t d|j dkrt|jttgkst|jtt gkrdSt|jtgkst|jttgkrdSn|j dkrdSdSdS)NzUnknown action %szNo rule action specified.rallowZdenyrr) relementrrrrrrrrr rr)rQ rich_ruler6r6r7_rich_rule_chain_suffixs    z!ip4tables._rich_rule_chain_suffixcCs>|j r|j rttd|jdkr(dS|jdkr6dSdSdS)NzNot log or auditrrrr)rauditr rr)rQrr6r6r7 _rich_rule_chain_suffix_from_logs   z*ip4tables._rich_rule_chain_suffix_from_logcCs|jdkrgSd|jgS)Nrz%%RICH_RULE_PRIORITY%%)r)rQrr6r6r7_rich_rule_priority_fragments z&ip4tables._rich_rule_priority_fragmentc Cs|js gS|jjj||t}ddd|}|j|}d||d||fg} | |j|7} | |ddg7} |jjr| dd |jjg7} |jjr| d d |jjg7} | |j |jj 7} | S) Nz-Az-D)TFz-tz%s_%sz-jrz --log-prefixz'%s'z --log-levelz%s) rrCrrrrr prefixlevelrr) rQrrrrf rule_fragmentrrrrar6r6r7_rich_rule_logs zip4tables._rich_rule_logc Cs|js gSddd|}|jjj||t}|j|}d||d||fg} | |j|7} | |7} t|jt krrd} n,t|jt krd} nt|jt krd} nd } | d d d | g7} | |j |jj 7} | S) Nz-Az-D)TFz-tz%s_%sZacceptZrejectZdropunknownz-jZAUDITz--type)rrCrrrrr rrrrrrr) rQrrrrfr rrrra_typer6r6r7_rich_rule_audits$ zip4tables._rich_rule_auditc Cs2|js gSddd|}|jjj||t}|j|}d||f} t|jtkrXddg} nt|jtkrddg} |jjr| d|jjg7} nnt|jt krdd g} nVt|jt krd }|jjj||t}d||f} dd d |jj g} nt t d t|jd||| g} | |j|7} | || 7} | |j|jj7} | S)Nz-Az-D)TFz%s_%sz-jrrz --reject-withrr$MARKz --set-xmarkzUnknown action %sz-t)rrCrrrrrrrrrr>r rr rr) rQrrrrfr rrrrgZ rule_actionrar6r6r7_rich_rule_actions4       zip4tables._rich_rule_actioncCs|sgSg}|jr|jr"|jdtd|jrB|dt|jg7}qtd|jr||jjd}|dt|dd|dg7}q|d|jg7}nD|jr|ddg7}|jr|jd|jj j |jd }|d |j|g7}|S) N!r(z-drrr`z-mr>rz --match-set) rrrir r r rrrCr_ipset_match_flags)rQZ rich_destr rrr6r6r7_rich_rule_destination_fragment1s&    "  z)ip4tables._rich_rule_destination_fragmentcCs|sgSg}|jr|jr"|jdtd|jrB|dt|jg7}nHtd|jr||jjd}|dt|dd|dg7}n|d|jg7}nt|dr|jr|ddg7}|jr|jd|d |jg7}nPt|d o|j r|dd g7}|jr|jd|j j j |j d }|d |j |g7}|S)Nrr(z-srrr`rz-mz --mac-sourcerr>rz --match-set) rrrir r r rhasattrrrrCrr)rQZ rich_sourcer rrr6r6r7_rich_rule_source_fragmentIs0    "    z$ip4tables._rich_rule_source_fragmentc Csddd|}d}|jjj||t} d|g} |rD| ddt|g7} |rT| d|g7} |rx| |j|j7} | |j|j7} | st |j t kr| d d d d g7} g} |r| j |j ||||| | j |j||||| | j |j||||| n"| j |d | d|g| ddg| S)Nz-Az-D)TFr&z-pz--dportz%sz-dz-m conntrackz --ctstatez NEW,UNTRACKEDz%s_allowz-tz-jr)rCrrrrr destinationrsourcerrrrir rr) rQrrprotoportrrrrfrr rr6r6r7build_policy_ports_rulesfs* z"ip4tables.build_policy_ports_rulesc Csddd|}d}|jjj||t}d|g} |r<| d|g7} |r`| |j|j7} | |j|j7} | stt|j t kr| ddd d g7} g} |r| j |j ||||| | j |j ||||| | j |j||||| n"| j |d |d |g| d dg| S)Nz-Az-D)TFr&z-pz-dz-mrz --ctstatez NEW,UNTRACKEDz%s_allowz-tz-jr)rCrrrrrrrrrrrir rr) rQrrprotocolrrrrfrr rr6r6r7build_policy_protocol_ruless& z%ip4tables.build_policy_protocol_rulesc Csddd|}d}|jjj||t} d|g} |rD| ddt|g7} |rT| d|g7} |rx| |j|j7} | |j|j7} | st |j t kr| d d d d g7} g} |r| j |j ||||| | j |j||||| | j |j||||| n"| j |d | d|g| ddg| S)Nz-Az-D)TFr&z-pz--sportz%sz-dz-mrz --ctstatez NEW,UNTRACKEDz%s_allowz-tz-jr)rCrrrrrrrrrrrrir rr) rQrrrrrrrrfrr rr6r6r7build_policy_source_ports_ruless* z)ip4tables.build_policy_source_ports_rulesc Csvd}|jjj||t} ddd|} | d| ddd|g} |rP| dd t|g7} |r`| d |g7} | d d d |g7} | gS)Nr#z-Az-D)TFz%s_allowz-tz-pz--dportz%sz-dz-jZCTz--helper)rCrrrr) rQrrrrrZ helper_nameZmodule_short_namerfrrrar6r6r7build_policy_helper_ports_ruless z)ip4tables.build_policy_helper_ports_rulesc Csddd|}|jjj||t}g} |rH| jdd|d|d|dd gn6t|rTgS| jdd|d|g|jd |dd g| S) Nz-Az-D)TFz-tr&z%s_allowz-oz-jrz-d)rCrrrrirr) rQrrrrfrrrrrr6r6r7build_zone_forward_ruless z"ip4tables.build_zone_forward_rulesc Cs,d}|jjj||tdd}ddd|}g}|rj|j|}||j|7}||j|j7}||j|j 7}nd}g} | j dd|d ||fg|d d d d dgg}|r|j|}||j|7}||j|j7}||j|j 7}nd}d}|jjj||t}| j dd|d ||fg|ddddd dg| S)Nr%T)rz-Az-D)TFrz-tz%s_%srz-oloz-jZ MASQUERADEr&z-mrz --ctstatez NEW,UNTRACKEDr) rCrrrrr rrrrri) rQrrrrfrrr rrr6r6r7build_policy_masquerade_ruless6  z'ip4tables.build_policy_masquerade_rulesc Cs d}|jjj||t} ddd|} d} |rPtd|rH| dt|7} n| |7} |rn|dkrn| dt|d 7} g} |r|j|} |j|} | |j |j 7} | |j |j 7} nd } g}|r|j |j|||d| |j d d| d | | fg| d |dt|ddd| g|S)Nr%z-Az-D)TFrr(z[%s]z:%s-rz-tz%s_%sz-pz--dportz-jZDNATz--to-destination)rCrrrr r rrr rrrrrir )rQrrrrZtoportZtoaddrrrfrrZtor rrr6r6r7build_policy_forward_port_ruless2     z)ip4tables.build_policy_forward_port_rulescCs|jdkrL|tkrLt|\}}}|r,t|nt|dt|}ddd|gS|jdkr|tkrt|\}}}|rxt|nt|dt|}ddd|gSttd |d |jdS) Nr'rz-mr)z --icmp-typer(Zicmp6z --icmpv6-typez ICMP type z not supported by )rDrrlrr rr)rQZ icmp_typer_codeZ _omit_codeZ _type_strr6r6r7_icmp_types_fragments    zip4tables._icmp_types_fragmentcCsd}|jjj||t}ddd|}|jdkrDddg}|j|j} nddg}|j|j} g} |jjj|rxd |} d } n d |} d } g} |r| |j|j 7} | |j |j 7} | || 7} |rL| j |j ||||| | j |j||||| |jr| j |j||||| n:|j|}| j d ||d||fg|j|| dd gn`|jjdkr| d kr| j || d |g| ddddd|g| j || d |g| d| g| S)Nr&z-Az-D)TFr'z-pr)z ipv6-icmpz%s_allowrz%s_denyz %%REJECT%%z-tz%s_%sz-jrz %%LOGTYPE%%rz --log-prefixz"%s_ICMP_BLOCK: ")rCrrrrDr(rquery_icmp_block_inversionrrrrrir rrrrr r)rQrrZictrrfrrrmatchrZ final_chainZ final_targetr rr6r6r7build_policy_icmp_block_rules,sJ      z'ip4tables.build_policy_icmp_block_rulesc Csd}|jjj||t}g}d}|jjj|rd}|jjdkr|rRd|t|g}nd|g}|d|dd d d d d d|g }|j||d7}nd}|rd|t|g}nd|g}|d|dd d |g}|j||S)Nr&rz %%REJECT%%rz-Iz-Dz-tz-pz%%ICMP%%z %%LOGTYPE%%z-jrz --log-prefixz"%s_ICMP_BLOCK: "r`r)rCrrrr)rrlri) rQrrrfrrZrule_idxZ ibi_targetrar6r6r7'build_policy_icmp_block_inversion_rules]s.     z1ip4tables.build_policy_icmp_block_inversion_rulescCsxd}g}||j|j7}||j|j7}g}|j|j||||||j|j||||||j|j||||||S)Nr&)rrrrrir rr)rQrrrrfr rr6r6r7*build_policy_rich_source_destination_rulessz4ip4tables.build_policy_rich_source_destination_rulescCs ||jkS)N)rD)rQrDr6r6r7rszip4tables.is_ipv_supported)N)N)r)F)F)NN)NN)NN)NN)N)N)N)8__name__ __module__ __qualname__rDrZpolicies_supportedrSrKrrdrhrkrmrnrorprxrrrrrrGrIrrrrrrrrrrrrrrr r rrrrrrr r!r"r$r&r(r+r,r-rr6r6r6r7rBsj     )Pa#    zN  0 "     & ! 1"rBc@s&eZdZdZdZdddZddZdS) ip6tablesr(Fc Csg}|jddddddddd d g |d krL|jddddddddd d d dg |jdddddddd dg |jdddddddd dg |S)Nz-Ir z-tr$z-mZrpfilterz--invertz --validmarkz-jrrrz --log-prefixzrpfilter_DROP: z-pz ipv6-icmpz$--icmpv6-type=neighbour-solicitationrz"--icmpv6-type=router-advertisement)ri)rQrrr6r6r7build_rpfilter_ruless$        zip6tables.build_rpfilter_rulesc Csddddddddd g }d }|jd j|g}|jd d d |gxT|D]L}|jd d d|d|ddddg |jjdkrF|jd d d|d|ddddg qFW|jd d dddd|g|jd d dddd|g|S)Nz ::0.0.0.0/96z::ffff:0.0.0.0/96z2002:0000::/24z2002:0a00::/24z2002:7f00::/24z2002:ac10::/28z2002:c0a8::/32z2002:a9fe::/32z2002:e000::/19Z RFC3964_IPv4r&z-tz-Nz-Iz-dz-jrz --reject-withz addr-unreachrallrz --log-prefixz"RFC3964_IPv4_REJECT: "r4r)rr3)rPrjrirCZ _log_denied)rQZ daddr_listZ chain_namerZdaddrr6r6r7build_rfc3964_ipv4_ruless4       z"ip6tables.build_rfc3964_ipv4_rulesN)F)r.r/r0rDrr2r5r6r6r6r7r1s r1)/Zos.pathrTrZfirewall.core.progrZfirewall.core.loggerrZfirewall.functionsrrrrrr r r Zfirewallr Zfirewall.errorsr rrrrrZfirewall.core.richrrrrrrrZfirewall.core.icmprrrrrerrr8r=rAobjectrBr1r6r6r6r7sB  (  $ %* oPK!Ѩ=II$__pycache__/fw_policy.cpython-36.pycnu[3 Yj=V@sddlZddlZddlmZddlmZmZmZmZm Z m Z m Z m Z m Z mZddlmZmZmZmZmZmZmZmZmZmZmZddlmZddlmZddlm Z ddl!m"Z"dd l#m$Z$Gd d d e%Z&dS) N)log) portStr checkIPnMask checkIP6nMask checkProtocolenable_ip_forwardingcheck_single_addressportInPortRangeget_nf_conntrack_short_namecoalescePortRangebreakPortRange) Rich_Rule Rich_Accept Rich_Service Rich_Port Rich_ProtocolRich_MasqueradeRich_ForwardPortRich_SourcePortRich_IcmpBlock Rich_IcmpType Rich_Mark)FirewallTransaction)errors) FirewallError)LastUpdatedOrderedDict)SOURCE_IPSET_TYPESc@seZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ ddZ ddZ d ddZddZddZddZd d d!Zd d"d#Zdd$d%Zd&d'Zd(d)Zd*d+Zd,d-Zdd0d1Zd2d3Zdd4d5Zd6d7Zd8d9Zd:d;Zdd?Z dd@dAZ!dBdCZ"ddDdEZ#dFdGZ$dHdIZ%dJdKZ&dLdMZ'dNdOZ(dPdQZ)dRdSZ*ddTdUZ+dVdWZ,ddXdYZ-dZd[Z.d\d]Z/d^d_Z0d`daZ1dbdcZ2ddddeZ3dfdgZ4ddhdiZ5djdkZ6dldmZ7dndoZ8dpdqZ9drdsZ:dtduZ;dvdwZ<ddxdyZ=dzd{Z>dd|d}Z?d~dZ@ddZAddZBddZCddZDdddZEddZFdddZGddZHddZIddZJddZKdddZLddZMdddZNddZOddZPddZQddZRdddZSddZTdddZUddZVddZWdddZXd ddZYd!ddZZddZ[d"ddZ\ddZ]d#ddZ^ddZ_ddZ`ddZad$ddÄZbddńZcd%ddDŽZdddɄZedd˄Zfdd̈́ZgddτZhd&ddфZiddӄZjddՄZkd'ddׄZlddلZmddۄZndd݄Zodd߄ZpddZqddZrddZsddZtddZud(ddZvd)ddZwddZxddZyddZzddZ{d*ddZ|ddZ}ddZ~ddZddZddZddZddZddZd+d d ZdS(,FirewallPolicycCs||_i|_i|_dS)N)_fw_chains _policies)selffwr#/usr/lib/python3.6/fw_policy.py__init__szFirewallPolicy.__init__cCsd|j|j|jfS)Nz %s(%r, %r)) __class__rr )r!r#r#r$__repr__szFirewallPolicy.__repr__cCs|jj|jjdS)N)rclearr )r!r#r#r$cleanups zFirewallPolicy.cleanupcCs t|jS)N)rr)r!r#r#r$new_transaction$szFirewallPolicy.new_transactioncCst|jjS)N)sortedr keys)r!r#r#r$ get_policies)szFirewallPolicy.get_policiescCs8g}x*|jD]}|j|}|js|j|qWt|S)N)r- get_policyderived_from_zoneappendr+)r!Zpoliciespp_objr#r#r$"get_policies_not_derived_from_zone,s  z1FirewallPolicy.get_policies_not_derived_from_zonecCs~g}xt|jD]h}|j|}t|dt|jjjtddgB@rt|dt|jjjtddgB@r|j|qW|S)N ingress_zonesHOSTANY egress_zones)r3 get_settingssetrzoneZget_active_zonesr0)r!Zactive_policiespolicysettingsr#r#r$)get_active_policies_not_derived_from_zone4s ((z8FirewallPolicy.get_active_policies_not_derived_from_zonecCs|jj|}|j|S)N)r check_policyr )r!r;r1r#r#r$r.>s zFirewallPolicy.get_policyc Cs,dddD|_||j|j<|j|jdS)NcSsi|] }t|qSr#)r).0xr#r#r$ Csz-FirewallPolicy.add_policy..servicesports masquerade forward_ports source_ports icmp_blocksrules protocolsicmp_block_inversionr4r7) rBrCrDrErFrGrHrIrJr4r7)r<r namecopy_permanent_to_runtime)r!objr#r#r$ add_policyBs  zFirewallPolicy.add_policycCs0|j|}|jr|j||jj|j|=dS)N)r appliedunapply_policy_settingsr<r()r!r;rMr#r#r$ remove_policyNs    zFirewallPolicy.remove_policycCs|j|}|jrdSx|jD]}|j||ddqWx|jD]}|j||ddqr rOr*r/%_get_table_chains_for_policy_dispatch#_get_table_chains_for_zone_dispatchgen_chain_rulesr8_ingress_egress_zones _icmp_block _forward_port_service_port _protocol _source_port _masquerade_FirewallPolicy__ruler rr[execute) r!enabler;rb_policyrM transactiontablechainr<keyr`r#r#r$_policy_settingssj                zFirewallPolicy._policy_settingscCs|jd||ddS)NT)rb)r)r!r;rbr#r#r$rcsz$FirewallPolicy.apply_policy_settingscCs|jd||ddS)NF)rb)r)r!r;rbr#r#r$rPsz&FirewallPolicy.unapply_policy_settingsc Csr|j|j}|j||j||j||j||j||j||j||j ||j ||j |d }|j j ||S)zH :return: exported config updated with runtime settings ) rBrCrGrDrE rich_rulesrIrFr4r7)r.Zexport_config_dict list_services list_portslist_icmp_blocksquery_masqueradelist_forward_ports list_ruleslist_protocolslist_source_portslist_ingress_zoneslist_egress_zonesrZ'combine_runtime_with_permanent_settings)r!r;Z permanentZruntimer#r#r$get_config_with_settings_dictsz,FirewallPolicy.get_config_with_settings_dictc sddlmd fdd }fdd}jjfjjfjjfjj fj j f||fj j fjjfjjfjjfd }j|}jj||\}} xt| D]l} t| | trxV| | D]8} t| tr|| d|f| q|| d|| qWq|| d|qWx|D]} t|| trxn|| D]J} t| trv|| d|f| d|d n|| d|| d|d qFWn|| d|d|d q(WdS) Nr)r csj||dd|ddS)N)rkr)rgrf)r^)r;rkrgrf)r r!r#r$add_rule_wrapperszFFirewallPolicy.set_config_with_settings_dict..add_rule_wrappercsj||ddS)N)rk) remove_rule)r;rk)r r!r#r$remove_rule_wrapperszIFirewallPolicy.set_config_with_settings_dict..remove_rule_wrapper) rBrCrGrDrErrIrFr4r7rj)rgrf)rN)firewall.core.richr rWremove_servicerX remove_portrUremove_icmp_blockr_remove_masqueraderVremove_forward_portr\remove_protocolr]remove_source_portrSremove_ingress_zonerTremove_egress_zonerrZget_added_and_removed_settings isinstancelisttuple) r!r;r<rfrrZ setting_to_fnZ old_settingsZ add_settingsZremove_settingsr~r`r#)r r!r$set_config_with_settings_dicts:                z,FirewallPolicy.set_config_with_settings_dictcCs&|sttj|dkr"|jj|dS)Nr5r6)r5r6)rr INVALID_ZONEr check_zone)r!r:r#r#r$check_ingress_zones z!FirewallPolicy.check_ingress_zonecCs|j||S)N)r)r!r:r#r#r$Z__ingress_zone_id"s z FirewallPolicy.__ingress_zone_idrTc Cs|jj|}|jj||jj|j|}|j|} | |jdkrXttj d||fd|jdksd|jdks|d kr|jdrttj d|dkrd|jdkrttj d|dkr|j } n|} |rJ|j r|j d|| |j|| ||| j|j|| |j s:||jkrH|j|| d | j|j|dn|j d || n |j|| ||| j|j|| |dkr~| jd dS) Nr4z'%s' already in '%s'r6r5zI'ingress-zones' may only contain one of: many regular zones, ANY, or HOSTr7zF'HOST' can only appear in either ingress or egress zones, but not bothF)rbT)r6r5)rr> check_timeout check_panicr _FirewallPolicy__ingress_zone_idr<rrrZrr*rOro&_FirewallPolicy__register_ingress_zoneadd_fail(_FirewallPolicy__unregister_ingress_zoner=rcrerx) r!r;r:rgrfrbrRrz_objzone_idr{r#r#r$rS&s<         zFirewallPolicy.add_ingress_zonecCs|j|||jd|<dS)Nr4)_FirewallPolicy__gen_settingsr<)r!rrrgrfr#r#r$Z__register_ingress_zoneSsz&FirewallPolicy.__register_ingress_zonecCs|jj|}|jj|j|}|j|}||jdkrLttjd||f|dkr^|j }n|}|j rt |jddkr|j ||n|j d|||j|||j|j||dd||jkr|j d||n|j|j|||dkr|jd|S)Nr4z'%s' not in '%s'rjFT)rr>rr rr<rr NOT_ENABLEDr*rOlenrProrrrr=add_postrx)r!r;r:rbrzrrr{r#r#r$rVs,        z"FirewallPolicy.remove_ingress_zonecCs||jdkr|jd|=dS)Nr4)r<)r!rrr#r#r$Z__unregister_ingress_zoneysz(FirewallPolicy.__unregister_ingress_zonecCs|j||j|dkS)Nr4)rr8)r!r;r:r#r#r$query_ingress_zone}sz!FirewallPolicy.query_ingress_zonecCst|j|djS)Nr4)rr8r,)r!r;r#r#r$rsz!FirewallPolicy.list_ingress_zonescCs&|sttj|dkr"|jj|dS)Nr5r6)r5r6)rrrrr)r!r:r#r#r$check_egress_zones z FirewallPolicy.check_egress_zonecCs|j||S)N)r)r!r:r#r#r$Z__egress_zone_ids zFirewallPolicy.__egress_zone_idc Cs|jj|}|jj||jj|j|}|j|} | |jdkrXttj d||fd|jdksd|jdks|d kr|jdrttj d|dkrd|jdkrttj d|dkr|j } n|} |rJ|j r|j d|| |j|| ||| j|j|| |j s:||jkrH|j|| d | j|j|dn|j d || n |j|| ||| j|j|| |dkr~| jd dS) Nr7z'%s' already in '%s'r6r5zH'egress-zones' may only contain one of: many regular zones, ANY, or HOSTr4zF'HOST' can only appear in either ingress or egress zones, but not bothF)rbT)r6r5)rr>rrr _FirewallPolicy__egress_zone_idr<rrrZrr*rOro%_FirewallPolicy__register_egress_zoner'_FirewallPolicy__unregister_egress_zoner=rcrerx) r!r;r:rgrfrbrRrzrrr{r#r#r$rTs<         zFirewallPolicy.add_egress_zonecCs|j|||jd|<dS)Nr7)rr<)r!rrrgrfr#r#r$Z__register_egress_zonesz%FirewallPolicy.__register_egress_zonecCs|jj|}|jj|j|}|j|}||jdkrLttjd||f|dkr^|j }n|}|j rt |jddkr|j ||n|j d|||j|||j|j||dd||jkr|j d||n|j|j|||dkr|jd|S)Nr7z'%s' not in '%s'rjFT)rr>rr rr<rrrr*rOrrProrrrr=rrx)r!r;r:rbrzrrr{r#r#r$rs,        z!FirewallPolicy.remove_egress_zonecCs||jdkr|jd|=dS)Nr7)r<)r!rrr#r#r$Z__unregister_egress_zonesz'FirewallPolicy.__unregister_egress_zonecCs|j||j|dkS)Nr7)rr8)r!r;r:r#r#r$query_egress_zonesz FirewallPolicy.query_egress_zonecCst|j|djS)Nr7)rr8r,)r!r;r#r#r$rsz FirewallPolicy.list_egress_zonescCs |jdS)N)Zcheck)r!ruler#r#r$ check_ruleszFirewallPolicy.check_rulecCs|j|t|S)N)rstr)r!rr#r#r$Z __rule_ids zFirewallPolicy.__rule_idcCsx|sdS|jr,t|jrdSt|jrtdSnHt|dr@|jr@dSt|drt|jrt|j|j|j|j|j|jSdS)Nipv4ipv6macipset) Zaddrrrhasattrrr_check_ipset_type_for_source_check_ipset_applied _ipset_family)r!sourcer#r#r$_rule_source_ipvs     zFirewallPolicy._rule_source_ipvcCs|j||||dS)N) _rule_prepare)r!ryr;rr{r#r#r$Z__ruleszFirewallPolicy.__rulec CsL|jj|}|jj||jj|j|}|j|}||jdkrh|jrP|jn|} tt j d|| f|js|j rt |j t rd|jdkrtt jdd|jdkrtt jdx6|jdD](} | dkrq|jjj| rtt jd qW|j rt |j trd|jdkr,|j jrtt jd nb|jdr|j jsNtt jd x>|jdD]0} | dkrlqZ|jjj| rZtt jd qZW|jrt |jtrx>|jdD]0} | dkrq|jjj| rtt jd qW|dkr|j} n|} |jr|jd||| |j||||| j|j|||dkrH| jd|S)NrHz'%s' already in '%s'r5r7z.'masquerade' is invalid for egress zone 'HOST'r4z/'masquerade' is invalid for ingress zone 'HOST'r6zR'masquerade' cannot be used in a policy if an ingress zone has assigned interfaceszAA 'forward-port' with 'to-addr' is invalid for egress zone 'HOST'zC'forward-port' requires 'to-addr' if egress zone is 'ANY' or a zonezS'forward-port' cannot be used in a policy if an egress zone has assigned interfaceszR'mark' action cannot be used in a policy if an egress zone has assigned interfacesT)r6r5)rr>rrr _FirewallPolicy__rule_idr<r/rrrZelementrrrr:list_interfacesr to_addressINVALID_FORWARDactionrr*rOrw_FirewallPolicy__register_ruler _FirewallPolicy__unregister_rulerx) r!r;rrgrfrbrzrrule_id_namer:r{r#r#r$r^ s`                 zFirewallPolicy.add_rulecCs|j|||jd|<dS)NrH)rr<)r!rrrgrfr#r#r$Z__register_ruleEszFirewallPolicy.__register_rulec Cs|jj|}|jj|j|}|j|}||jdkr\|jrD|jn|}ttj d||f|dkrn|j }n|}|j r|j d||||j |j|||dkr|jd|S)NrHz'%s' not in '%s'FT)rr>rr rr<r/rrrr*rOrwrrrx) r!r;rrbrzrrrr{r#r#r$rIs"      zFirewallPolicy.remove_rulecCs||jdkr|jd|=dS)NrH)r<)r!rrr#r#r$Z__unregister_ruledsz FirewallPolicy.__unregister_rulecCs|j||j|dkS)NrH)rr8)r!r;rr#r#r$ query_rulehszFirewallPolicy.query_rulecCst|j|djS)NrH)rr8r,)r!r;r#r#r$rkszFirewallPolicy.list_rulescCs|jj|dS)N)r check_service)r!servicer#r#r$rpszFirewallPolicy.check_servicecCs|j||S)N)r)r!rr#r#r$Z __service_idss zFirewallPolicy.__service_idc Cs|jj|}|jj||jj|j|}|j|}||jdkrh|jrP|jn|} tt j d|| f|dkrz|j } n|} |j r|j d||| |j||||| j|j|||dkr| jd|S)NrBz'%s' already in '%s'T)rr>rrr _FirewallPolicy__service_idr<r/rrrZr*rOrr!_FirewallPolicy__register_servicer#_FirewallPolicy__unregister_servicerx) r!r;rrgrfrbrzr service_idrr{r#r#r$rWws&       zFirewallPolicy.add_servicecCs|j|||jd|<dS)NrB)rr<)r!rrrgrfr#r#r$Z__register_servicesz!FirewallPolicy.__register_servicec Cs|jj|}|jj|j|}|j|}||jdkr\|jrD|jn|}ttj d||f|dkrn|j }n|}|j r|j d||||j |j|||dkr|jd|S)NrBz'%s' not in '%s'FT)rr>rr rr<r/rrrr*rOrrrrrx) r!r;rrbrzrrrr{r#r#r$rs"      zFirewallPolicy.remove_servicecCs||jdkr|jd|=dS)NrB)r<)r!rrr#r#r$Z__unregister_servicesz#FirewallPolicy.__unregister_servicecCs|j||j|dkS)NrB)rr8)r!r;rr#r#r$ query_serviceszFirewallPolicy.query_servicecCs|j|djS)NrB)r8r,)r!r;r#r#r$rszFirewallPolicy.list_servicesc CsTg}xJ|D]B}y|jjj|}Wn tk r@ttj|YnX|j|q W|S)N)rhelper get_helperrrINVALID_HELPERr0)r!helpers_helpersr_helperr#r#r$get_helpers_for_service_helperss z.FirewallPolicy.get_helpers_for_service_helperscCsg}x|D]}y|jjj|}Wn tk r@ttj|YnXt|jdkrt|j }y|jjj|}|j |Wqtk r|rt j d|w YqXq |j |q W|S)NrjzHelper '%s' is not available) rrrrrrrrCr moduler0rr[)r!modulesryrrr_module_short_namerr#r#r$get_helpers_for_service_moduless"   z.FirewallPolicy.get_helpers_for_service_modulescCs|jj||jj|dS)N)r check_port check_tcpudp)r!portprotocolr#r#r$rs zFirewallPolicy.check_portcCs|j||t|d|fS)N-)rr)r!rrr#r#r$Z __port_ids zFirewallPolicy.__port_idcs|jj|}|jj||jj|j|}ttfdd|jd} x@| D]8} t|| drN|j rl|j n|} t t j d|| fqNWt |dd| D\} } |dkr|j}n|}|jr x$| D]}|jd|t|d |qWx$| D]}|jd |t|d |qWx:| D]2}|j|} |j|| |||j|j|| qWx*| D]"}|j|} |j|j|| qNW|dkr|jd|S) Ncs |dkS)Nrjr#)r@)rr#r$sz)FirewallPolicy.add_port..rCrz'%s:%s' already in '%s'cSsg|] \}}|qSr#r#)r?rsrtr#r#r$ sz+FirewallPolicy.add_port..TrF)rr>rrr rfilterr<r r/rrrZr r*rOrsr_FirewallPolicy__port_id_FirewallPolicy__register_portr _FirewallPolicy__unregister_portrrx)r!r;rrrgrfrbrzrexisting_port_idsport_idr added_rangesremoved_rangesr{ranger#)rr$rXs:              zFirewallPolicy.add_portcCs|j|||jd|<dS)NrC)rr<)r!rrrgrfr#r#r$Z__register_portszFirewallPolicy.__register_portcs|jj|}|jj|j|}ttfdd|jd}xB|D]}t||drBPqBW|jrf|jn|} t t j d|| ft |dd|D\} } |dkr|j } n|} |jrx$| D]} |jd|t| d | qWx$| D]} |jd |t| d | qWx:| D]2} |j| }|j||dd| j|j||qWx*| D]"} |j| }| j|j||qDW|dkr~| jd|S) Ncs |dkS)Nrjr#)r@)rr#r$rsz,FirewallPolicy.remove_port..rCrz'%s:%s' not in '%s'cSsg|] \}}|qSr#r#)r?rsrtr#r#r$r#sz.FirewallPolicy.remove_port..TrF)rr>rr rrr<r r/rrrr r*rOrsrrrrrrrx)r!r;rrrbrzrrrrrrr{rr#)rr$rs:             zFirewallPolicy.remove_portcCs||jdkr|jd|=dS)NrC)r<)r!rrr#r#r$Z__unregister_port=sz FirewallPolicy.__unregister_portcCs6x0|j|dD]\}}t||r||krdSqWdS)NrCTF)r8r )r!r;rrrsrtr#r#r$ query_portAszFirewallPolicy.query_portcCst|j|djS)NrC)rr8r,)r!r;r#r#r$rHszFirewallPolicy.list_portscCst|sttj|dS)N)rrrZINVALID_PROTOCOL)r!rr#r#r$check_protocolMszFirewallPolicy.check_protocolcCs|j||S)N)r)r!rr#r#r$Z __protocol_idQs zFirewallPolicy.__protocol_idc Cs|jj|}|jj||jj|j|}|j|}||jdkrh|jrP|jn|} tt j d|| f|dkrz|j } n|} |j r|j d||| |j||||| j|j|||dkr| jd|S)NrIz'%s' already in '%s'T)rr>rrr _FirewallPolicy__protocol_idr<r/rrrZr*rOrt"_FirewallPolicy__register_protocolr$_FirewallPolicy__unregister_protocolrx) r!r;rrgrfrbrzr protocol_idrr{r#r#r$r\Us&       zFirewallPolicy.add_protocolcCs|j|||jd|<dS)NrI)rr<)r!rrrgrfr#r#r$Z__register_protocolrsz"FirewallPolicy.__register_protocolc Cs|jj|}|jj|j|}|j|}||jdkr\|jrD|jn|}ttj d||f|dkrn|j }n|}|j r|j d||||j |j|||dkr|jd|S)NrIz'%s' not in '%s'FT)rr>rr rr<r/rrrr*rOrtrrrx) r!r;rrbrzrrrr{r#r#r$rvs$       zFirewallPolicy.remove_protocolcCs||jdkr|jd|=dS)NrI)r<)r!rrr#r#r$Z__unregister_protocolsz$FirewallPolicy.__unregister_protocolcCs|j||j|dkS)NrI)rr8)r!r;rr#r#r$query_protocolszFirewallPolicy.query_protocolcCst|j|djS)NrI)rr8r,)r!r;r#r#r$rszFirewallPolicy.list_protocolscCs|j||t|d|fS)Nr)rr)r!rrr#r#r$Z__source_port_ids zFirewallPolicy.__source_port_idcs|jj|}|jj||jj|j|}ttfdd|jd} x@| D]8} t|| drN|j rl|j n|} t t j d|| fqNWt |dd| D\} } |dkr|j}n|}|jr x$| D]}|jd|t|d |qWx$| D]}|jd |t|d |qWx:| D]2}|j|} |j|| |||j|j|| qWx*| D]"}|j|} |j|j|| qNW|dkr|jd|S) Ncs |dkS)Nrjr#)r@)rr#r$rsz0FirewallPolicy.add_source_port..rFrz'%s:%s' already in '%s'cSsg|] \}}|qSr#r#)r?rsrtr#r#r$rsz2FirewallPolicy.add_source_port..TrF)rr>rrr rrr<r r/rrrZr r*rOrur_FirewallPolicy__source_port_id%_FirewallPolicy__register_source_portr'_FirewallPolicy__unregister_source_portrrx)r!r;rrrgrfrbrzrrrrrrr{rr#)rr$r]s:              zFirewallPolicy.add_source_portcCs|j|||jd|<dS)NrF)rr<)r!rrrgrfr#r#r$Z__register_source_portsz%FirewallPolicy.__register_source_portcs|jj|}|jj|j|}ttfdd|jd}xB|D]}t||drBPqBW|jrf|jn|} t t j d|| ft |dd|D\} } |dkr|j } n|} |jrx$| D]} |jd|t| d | qWx$| D]} |jd |t| d | qWx:| D]2} |j| }|j||dd| j|j||qWx*| D]"} |j| }| j|j||qDW|dkr~| jd|S) Ncs |dkS)Nrjr#)r@)rr#r$rsz3FirewallPolicy.remove_source_port..rFrz'%s:%s' not in '%s'cSsg|] \}}|qSr#r#)r?rsrtr#r#r$rsz5FirewallPolicy.remove_source_port..TrF)rr>rr rrr<r r/rrrr r*rOrurrrrrrrx)r!r;rrrbrzrrrrrrr{rr#)rr$rs:             z!FirewallPolicy.remove_source_portcCs||jdkr|jd|=dS)NrF)r<)r!rrr#r#r$Z__unregister_source_portsz'FirewallPolicy.__unregister_source_portcCs6x0|j|dD]\}}t||r||krdSqWdS)NrFTF)r8r )r!r;rrrsrtr#r#r$query_source_portsz FirewallPolicy.query_source_portcCst|j|djS)NrF)rr8r,)r!r;r#r#r$rsz FirewallPolicy.list_source_portscCsdS)NTr#)r!r#r#r$Z__masquerade_idszFirewallPolicy.__masquerade_idc Cs8|jj|}|jj||jj|j|}|j}||jdkrb|jrN|jn|}tt j d||jsd|jdkrtt j dd|jdkrtt j dx6|jdD](} | dkrq|jj j | rtt j d qW|dkr|j} n|} |jr|jd || |j||||| j|j|||dkr4| jd |S) NrDz"masquerade already enabled in '%s'r5r7z.'masquerade' is invalid for egress zone 'HOST'r4z/'masquerade' is invalid for ingress zone 'HOST'r6zR'masquerade' cannot be used in a policy if an ingress zone has assigned interfacesT)rr>rrr _FirewallPolicy__masquerade_idr<r/rrrZrr:rr*rOrv$_FirewallPolicy__register_masquerader&_FirewallPolicy__unregister_masqueraderx) r!r;rgrfrbrzr masquerade_idrr:r{r#r#r$r_ s:          zFirewallPolicy.add_masqueradecCs|j|||jd|<dS)NrD)rr<)r!rrrgrfr#r#r$Z__register_masquerade2sz$FirewallPolicy.__register_masqueradecCs|jj|}|jj|j|}|j}||jdkrV|jrB|jn|}ttj d||dkrh|j }n|}|j r|j d|||j |j|||dkr|jd|S)NrDzmasquerade not enabled in '%s'FT)rr>rr rr<r/rrrr*rOrvrrrx)r!r;rbrzrrrr{r#r#r$r6s"      z FirewallPolicy.remove_masqueradecCs||jdkr|jd|=dS)NrD)r<)r!rrr#r#r$Z__unregister_masqueradePsz&FirewallPolicy.__unregister_masqueradecCs|j|j|dkS)NrD)rr8)r!r;r#r#r$rTszFirewallPolicy.query_masqueradecCs^|jj||jj||r(|jj||rBt||sBttj|| rZ| rZttjddS)Nz.port-forwarding is missing to-port AND to-addr)rrrrrrZ INVALID_ADDRr)r!ipvrrtoporttoaddrr#r#r$check_forward_portYs      z!FirewallPolicy.check_forward_portcCsLtd|r|jd||||n|jd||||t|d|t|dt|fS)Nrrr)rrrr)r!rrrrr#r#r$Z__forward_port_idfs   z FirewallPolicy.__forward_port_idc CsZ|jj|} |jj||jj|j| } |j||||} | | jdkrt| jrV| jn| } tt j d||||| f| jsd| jdkr|rtt j dnR| jdr|stt j dx6| jdD](} | dkrq|jj j | rtt jdqW|dkr|j}n|}| jr"|jd | ||||||j| | |||j|j| | |dkrV|jd | S) NrEz'%s:%s:%s:%s' already in '%s'r5r7zAA 'forward-port' with 'to-addr' is invalid for egress zone 'HOST'zC'forward-port' requires 'to-addr' if egress zone is 'ANY' or a zoner6zS'forward-port' cannot be used in a policy if an egress zone has assigned interfacesT)rr>rrr _FirewallPolicy__forward_port_idr<r/rrrZrr:rrr*rOrq&_FirewallPolicy__register_forward_portr(_FirewallPolicy__unregister_forward_portrx)r!r;rrrrrgrfrbrzr forward_idrr:r{r#r#r$rVnsB          zFirewallPolicy.add_forward_portcCs|j|||jd|<dS)NrE)rr<)r!rrrgrfr#r#r$Z__register_forward_portsz&FirewallPolicy.__register_forward_portc Cs|jj|}|jj|j|}|j||||} | |jdkrh|jrJ|jn|} ttj d||||| f|dkrz|j } n|} |j r|j d|| ||||| j |j|| |dkr| jd|S)NrEz'%s:%s:%s:%s' not in '%s'FT)rr>rr rr<r/rrrr*rOrqrrrx) r!r;rrrrrbrzrrrr{r#r#r$rs&     z"FirewallPolicy.remove_forward_portcCs||jdkr|jd|=dS)NrE)r<)r!rrr#r#r$Z__unregister_forward_portsz(FirewallPolicy.__unregister_forward_portcCs"|j||||}||j|dkS)NrE)rr8)r!r;rrrrrr#r#r$query_forward_portsz!FirewallPolicy.query_forward_portcCst|j|djS)NrE)rr8r,)r!r;r#r#r$rsz!FirewallPolicy.list_forward_portscCs|jj|dS)N)rZcheck_icmptype)r!icmpr#r#r$check_icmp_blockszFirewallPolicy.check_icmp_blockcCs|j||S)N)r)r!rr#r#r$Z__icmp_block_ids zFirewallPolicy.__icmp_block_idc Cs|jj|}|jj||jj|j|}|j|}||jdkrh|jrP|jn|} tt j d|| f|dkrz|j } n|} |j r|j d||| |j||||| j|j|||dkr| jd|S)NrGz'%s' already in '%s'T)rr>rrr _FirewallPolicy__icmp_block_idr<r/rrrZr*rOrp$_FirewallPolicy__register_icmp_blockr&_FirewallPolicy__unregister_icmp_blockrx) r!r;rrgrfrbrzricmp_idrr{r#r#r$rUs&       zFirewallPolicy.add_icmp_blockcCs|j|||jd|<dS)NrG)rr<)r!rr rgrfr#r#r$Z__register_icmp_blocksz$FirewallPolicy.__register_icmp_blockc Cs|jj|}|jj|j|}|j|}||jdkr\|jrD|jn|}ttj d||f|dkrn|j }n|}|j r|j d||||j |j|||dkr|jd|S)NrGz'%s' not in '%s'FT)rr>rr rr<r/rrrr*rOrprr rx) r!r;rrbrzrr rr{r#r#r$rs"      z FirewallPolicy.remove_icmp_blockcCs||jdkr|jd|=dS)NrG)r<)r!rr r#r#r$Z__unregister_icmp_block sz&FirewallPolicy.__unregister_icmp_blockcCs|j||j|dkS)NrG)rr8)r!r;rr#r#r$query_icmp_blockszFirewallPolicy.query_icmp_blockcCs|j|djS)NrG)r8r,)r!r;r#r#r$rszFirewallPolicy.list_icmp_blockscCsdS)NTr#)r!r#r#r$Z__icmp_block_inversion_idsz(FirewallPolicy.__icmp_block_inversion_idc Cs|jj|}|jj|j|}|j}||jdkrV|jrB|jn|}ttj d||dkrh|j }n|}|j rx&|j |dD]} |j d|| |qW|jd|||j||||j|j||||j rx&|j |dD]} |j d|| |qW|jd|||dkr|jd|S)NrJz,icmp-block-inversion already enabled in '%s'rGFT)rr>rr (_FirewallPolicy__icmp_block_inversion_idr<r/rrrZr*rOr8rp_icmp_block_inversion._FirewallPolicy__register_icmp_block_inversionr*_FirewallPolicy__undo_icmp_block_inversionrx) r!r;rfrbrzricmp_block_inversion_idrr{r`r#r#r$add_icmp_block_inversions6        z'FirewallPolicy.add_icmp_block_inversioncCs|jd||jd|<dS)NrrJ)rr<)r!rrrfr#r#r$Z__register_icmp_block_inversionEsz.FirewallPolicy.__register_icmp_block_inversioncCs|j}|jr6x&|j|dD]}|jd|||qW||jdkrP|jd|=|jr~x&|j|dD]}|jd|||qfW|jddS)NrGFrJT)r*rOr8rpr<rx)r!rzrrr{r`r#r#r$Z__undo_icmp_block_inversionJs z*FirewallPolicy.__undo_icmp_block_inversionc Cs|jj|}|jj|j|}|j}||jdkrV|jrB|jn|}ttj d||dkrh|j }n|}|j rx&|j |dD]}|j d|||qW|jd|||j|||j|j||d|j rx&|j |dD]}|j d|||qW|jd|||dkr|jd|S)NrJz(icmp-block-inversion not enabled in '%s'rGFT)rr>rr r r<r/rrrr*rOr8rpr0_FirewallPolicy__unregister_icmp_block_inversionrrrx) r!r;rbrzrrrr{r`r#r#r$remove_icmp_block_inversion\s6        z*FirewallPolicy.remove_icmp_block_inversioncCs||jdkr|jd|=dS)NrJ)r<)r!rrr#r#r$Z!__unregister_icmp_block_inversionsz0FirewallPolicy.__unregister_icmp_block_inversioncCs|j|j|dkS)NrJ)r r8)r!r;r#r#r$query_icmp_block_inversionsz)FirewallPolicy.query_icmp_block_inversionc Cs|jjj|}|jr*|jjj|jd}n|}|rT||jkrt||f|j|krtdSn ||jksp||f|j|krtdSx@|jjD]2}|jr||j kr|j ||||} |j || qW|j ||||fg|j |j || ||fgdS)Nr)rr;r.r/r:Z_zone_policiesrenabled_backendspolicies_supportedZget_available_tablesZbuild_policy_chain_rules add_rules_register_chainsr) r!r;creater|r}r{rMZtracking_policybackendrHr#r#r$rns$   zFirewallPolicy.gen_chain_rulescCsbx\|D]T\}}|r,|jj|gj||fq|j|j||ft|j|dkr|j|=qWdS)Nr)r setdefaultr0remover)r!r;rZtablesr|r}r#r#r$rs zFirewallPolicy._register_chainscCs$|jjj|dkrdS|jjj|S)Nzhash:mac)rrget_typeZ get_family)r!rKr#r#r$rszFirewallPolicy._ipset_familycCs|jjj|S)N)rrr)r!rKr#r#r$Z __ipset_typeszFirewallPolicy.__ipset_typecCsdj|g|jjj|S)N,)joinrrZ get_dimension)r!rKflagr#r#r$_ipset_match_flagssz!FirewallPolicy._ipset_match_flagscCs|jjj|S)N)rrZ check_applied)r!rKr#r#r$rsz#FirewallPolicy._check_ipset_appliedcCs*|j|}|tkr&ttjd||fdS)Nz.ipset '%s' with type '%s' not usable as source)_FirewallPolicy__ipset_typerrrZ INVALID_IPSET)r!rKZ_typer#r#r$rs  z+FirewallPolicy._check_ipset_type_for_sourcec st|jtkrjjj|jj}|dkr2|jjg}xR|jD]H}||krHq:j||j |t j |}||j_j |||||dq:Wg} |j r|j g} nH|jrt|jtst|jtrjjj|jjjrfdddD} j|j} | r&|j r |j | kr&ttjd| |j fn| g} | s4ddg} fdd| D} | |_x2tfdd| DD]} t|jtkrjjj|jj}g} t|jd kr|jrttjd xB| D].} | |jkr| j| r| j |j| qWn | j dx~| D]}t|jtkrj|j |}|j!|j"7}t#t|d d d }g}x|D]}|j$}t%|}|j&dd}|j ||j dkr| j|j  rqTt|j'dkr|j |n:x8|j'D].\}}| j(||||||j|}|j)| |qWqTW|j*|x4|j'D]*\}}| j+||||||}|j)| |q Wx.|j,D]$}| j-|||||}|j)| |q@Wx4|j.D]*\}}| j/||||||}|j)| |qpWqWqft|jt0kr|jj1}|jj2}j3||| j+||||d|}|j)| |qft|jt4kr<|jj5}j6|| j-|||d|}|j)| |qft|jt7kr|rzx&| D]} | j| rX|j8t9| qXW| j:|||}|j)| |qft|jt;kr4|jj1}|jj2}|jj<}|jj=}xD| D]<} | j| rj>| |||||r|r|j8t9| qW| j?|||||||}|j)| |qft|jt@kr|jj1}|jj2}j3||| j/||||d|}|j)| |nt|jtkst|jtkr>jjj|jj|j rjr|j jkrttjAd|j |jjft|jtkr |jr t|jtkr ttjd| jB|||}|j)| |n>|jdkrf| jC|||}|j)| |nttjdt|jqfWdS)N)included_servicescsg|]}|jkr|qSr#) destination)r?r)ictr#r$rsz0FirewallPolicy._rule_prepare..rrz;Source address family '%s' conflicts with rule family '%s'.csg|]}jj|r|qSr#)ris_ipv_enabled)r?r)r!r#r$rscsg|]}jj|qSr#)rget_backend_by_ipv)r?r@)r!r#r$rsrz"Destination conflict with service.cSs|jS)N)rK)r@r#r#r$rsz.FirewallPolicy._rule_prepare..)r~ conntracknatrrjz3rich rule family '%s' conflicts with icmp type '%s'z'IcmpBlock not usable with accept actionzUnknown element %s)rr)Dtyperrrr get_servicerKincludesrr0copydeepcopyrfamilyrrrconfig get_icmptyper%rrrrZ INVALID_RULEipvsr9ris_ipv_supportedrrrrrrr+rr replacerCbuild_policy_helper_ports_rulesrZ add_modulesbuild_policy_ports_rulesrIbuild_policy_protocol_rulesrFbuild_policy_source_ports_rulesrrrrrvaluerrrrbuild_policy_masquerade_rulesrZto_portrrbuild_policy_forward_port_rulesrZINVALID_ICMPTYPEbuild_policy_icmp_block_rulesZ*build_policy_rich_source_destination_rules)r!ryr;rr{r$svcincludeZ_ruler3Z source_ipvrZ destinationsrr%rrrrr nat_modulerprotorHrrrr#)r&r!r$rs                             zFirewallPolicy._rule_preparec Csb|jjj|}|j|j|}||j|j7}tt|ddd}|dkrN|g}x@|j D]6}||krdqV|j ||j ||j |||||dqVWg} xnd D]f} |jj | sq|jj| } t|jdkr| |jkr| j | |j| fq| df| kr| j | dfqWxV| D]L\} } x|D]} | j}t|}| jjdd }|j|| jd krf| j| j rfqt| jd kr|j|n:x8| jD].\}}| j||||| | j|}|j| |qWqWx2|jD](\}}| j||||| }|j| |qWx,|jD]"}| j|||| }|j| |qWx2|jD](\}}| j||||| }|j| |q,Wq WdS) NcSs|jS)N)rK)r@r#r#r$rsz)FirewallPolicy._service..)r~)r$rrrr)r*rrj)rr) rrr,rrrrr+r9r-rr0rrr'r(rr%rr r5Z add_moduler0r4rCr6rKrr7rIr8rFr9)r!ryr;rr{r$r>rr?Z backends_ipvrrr%rrrr@rrArHrr#r#r$rrsb               zFirewallPolicy._servicecCs<x6|jjD](}|jsq |j||||}|j||q WdS)N)rrrr7r)r!ryr;rrr{rrHr#r#r$rss  zFirewallPolicy._portcCs:x4|jjD]&}|jsq |j|||}|j||q WdS)N)rrrr8r)r!ryr;rr{rrHr#r#r$rts zFirewallPolicy._protocolcCs<x6|jjD](}|jsq |j||||}|j||q WdS)N)rrrr9r)r!ryr;rrr{rrHr#r#r$rus zFirewallPolicy._source_portcCs8d}|jt||jj|}|j||}|j||dS)Nr)rrrr(r;r)r!ryr;r{rrrHr#r#r$rvs    zFirewallPolicy._masqueradec CsXtd|rd}nd}|r(|r(|jt||jj|} | j||||||} |j| | dS)Nrr)rrrrr(r<r) r!ryr;r{rrrrrrrHr#r#r$rqs    zFirewallPolicy._forward_portc Cs|jjj|}xl|jjD]^}|js&qd}|jrXx&dD]}||jkr6|j|s6d}Pq6W|r^q|j|||} |j|| qWdS)NFrrT)rr) rr1r2rrr%r4r=r) r!ryr;rr{r&rZ skip_backendrrHr#r#r$rps   zFirewallPolicy._icmp_blockcCsh|j|j}|dkrdS|j| r0|dkr0dSx2|jjD]$}|jsHq<|j||}|j||q|jdD]}|jjj|drfPqfW|jd$|jd%|Sd&g}|jjs|jd'x4|jdD]}|jjj|drPqW|jd(x>|jdD]}|jjj|drPqW|jd)|jd*|SdS)+z:Create a list of (table, chain) needed for policy dispatchr6r4r5r7rrOr*rKmanglerLrPrNrMZ interfacesN)rrO)r*rK)rSrK)rLrK)rrO)rLrK)rrP)rrN)r*rK)r*rM)rSrK)rLrK)rrN)r*rK)rSrK)rLrK)r*rM)rrN)r*rM)rLrK)r*rK)rSrK)rrN)rLrK)r*rM)r*rK)rSrK)r r<rnftables_enabledr0r:r8)r!r;rMtcr:r#r#r$rlsj                 z4FirewallPolicy._get_table_chains_for_policy_dispatchcCsr|j|}d|jdkr4dg}|jjs0|jd|Sd|jdkrLdddgSd|jd krbddgStd|SdS)z8Create a list of (table, chain) needed for zone dispatchr5r7rrOrLrKr6 FORWARD_INr*rSr4 FORWARD_OUTrMzInvalid policy: %sN)rrO)rLrK)rrV)r*rK)rSrK)rrW)r*rM)r r<rrTr0r)r!r;rMrUr#r#r$rms  z2FirewallPolicy._get_table_chains_for_zone_dispatchFcCs|jjj|}|jr|j}n||}d|jdkrl|dkrBd|S|dkrRd|S|jsh|dkrhd|SnJd|jd kr|js|dkrd |Sn"d |jdkr|dkr|jrd |Sd |Sn0|dkr|rd|Sd|Sn|dkrd|Snd |jd krh|dkr*|jr d|Sd |Sn<|dkrL|rBd|Sd|Sn|dkr|jsd|SnN|js|dkrd |S|dkr|rd|Sd|Sn|dkrd|Std|||fS)Nr5r7rZIN_rLZPRE_rSr*r4ZOUT_r6ZFWDI_ZFWD_ZPOST_ZFWDO_z.Can't convert policy to chain name: %s, %s, %s)rSr*)rSrL)rSrL)rSrL)rr;r.r/r<r)r!r;r|Z policy_prefixZisSNATrMsuffixr#r#r$policy_base_chain_namesb                z%FirewallPolicy.policy_base_chain_name)N)N)N)N)rNNT)N)rNNT)N)rNN)N)rNN)N)rNN)N)rNN)N)rNN)N)rNN)N)NN)NN)NNrNN)NNN)NN)rNN)N)NN)N)N)N)NN)F)__name__ __module__ __qualname__r%r'r)r*r-r3r=r.rNrQrLrdrerr8rrcrPrrrrrSrrrrrrrrTrrrrrrrrrwr^rrrrrrrrWrrrrrrrrrrXrrrrrrrr\rrrrrrr]rrrrrrr_rrrrrrrVrrrrrrrrUr rr r rr rrrrrrrnrrr#r"rrrrrrsrtrurvrqrprrJrQrRrorlrmrYr#r#r#r$rs$  '  ?  . , # , # :     ' (   ' ( '   +     ) )  @ @    ( P r)'rhr.Zfirewall.core.loggerrZfirewall.functionsrrrrrrr r r r rr rrrrrrrrrrZfirewall.core.fw_transactionrZfirewallrZfirewall.errorsrZfirewall.fw_typesrZfirewall.core.baserobjectrr#r#r#r$s 04     PK!ܧ %__pycache__/icmp.cpython-36.opt-1.pycnu[3 Yj,@sddddgZd,d-d.d/d0d1d2d3d4d5d6d7d8d9d:d;dd?d@dAdBdCdDdEdFdGdHdIdJdKdLdMdNdOdPdQdRdSdTdUd*ZdVdWdXdYdZd[d\d]d^d_d`dadbdcdddedfdgdhdidjdkdldmdndodpdqdrdsdtd$Zd%d&Zd'dZd(d)Zd*dZd+S)u ICMP_TYPES ICMPV6_TYPEScheck_icmp_type_codecheck_icmpv6_type_codeTF )*z echo-replypongzdestination-unreachableznetwork-unreachableztos-network-unreachablezhost-unreachableztos-host-unreachablezprotocol-unreachablezport-unreachablezfragmentation-neededzsource-route-failedznetwork-unknownz host-unknownznetwork-prohibitedzhost-prohibitedzTOS-network-unreachablezTOS-host-unreachablezcommunication-prohibitedzhost-precedence-violationzprecedence-cutoffz source-quenchznetwork-redirectredirectz host-redirectztos-host-redirectzTOS-network-redirectztos-network-redirectzTOS-host-redirectz echo-requestpingzrouter-advertisementzrouter-solicitationz time-exceededzttl-zero-during-transitzttl-zero-during-reassemblyzparameter-problemz ip-header-badzrequired-option-missingztimestamp-requestztimestamp-replyzaddress-mask-requestzaddress-mask-reply)zdestination-unreachablezno-routezcommunication-prohibitedz beyond-scopezaddress-unreachablezport-unreachablez failed-policyz reject-routezpacket-too-bigz time-exceededzttl-zero-during-transitzttl-zero-during-reassemblyzparameter-problemz bad-headerzunknown-header-typezunknown-optionz echo-requestrz echo-replyrzrouter-solicitationzrouter-advertisementzneighbour-solicitationzneigbour-solicitationzneighbour-advertisementzneigbour-advertisementrzmld-listener-queryzmld-listener-reportzmld-listener-donezmld2-listener-reportcCs|tkr dSdS)NTF)r)_namer&/usr/lib/python3.6/icmp.pycheck_icmp_namensr(cCs||ftjkrdSdS)NTF)rvalues)_type_coder&r&r'rsscCs|tkr dSdS)NTF)r)r%r&r&r'check_icmpv6_namexsr,cCs||ftjkrdSdS)NTF)rr))r*r+r&r&r'r}sN)rrT)rrT)rrT)rrF)rrF)rrF)rrF)rrF)rrF)rr F)rr F)rr F)rr F)rr F)rrF)rrF)rrF)rrF)rrF)rrF)r rT)r rF)r rT)r rF)r rF)r rF)r rF)r rF)rrT)rrT)r rT)rrT)rrT)rrF)rrF)rrT)rrF)rrF)rrT)rrT)rrF)rrF)rrT)rrF)rrF)rrF)rrF)rr F)rr F)rr F)rrT)rrT)rrF)rrF)r rT)r rF)r rF)r rF)rrT)rrT)rrT)rrT)rrT)rrT)rrT)rrT)rrT)rrT)r rT)r!rT)r"rT)r#rT)r$rT)__all__rrr(rr,rr&r&r&r'sPK!{kS!__pycache__/helper.cpython-36.pycnu[3 Yj$@s dZdZdS)zThe helper maxnamelen N)__doc__ZHELPER_MAXNAMELENrr/usr/lib/python3.6/helper.pysPK!J|%__pycache__/fw_service.cpython-36.pycnu[3 Yjg@s2dgZddlmZddlmZGdddeZdS)FirewallService)errors) FirewallErrorc@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dS)rcCs||_i|_dS)N)Z_fw _services)selffwr /usr/lib/python3.6/fw_service.py__init__szFirewallService.__init__cCsd|j|jfS)Nz%s(%r)) __class__r)rrrr __repr__ szFirewallService.__repr__cCs|jjdS)N)rclear)rrrr cleanup#szFirewallService.cleanupcCst|jjS)N)sortedrkeys)rrrr get_services(szFirewallService.get_servicescCs||jkrttj|dS)N)rrrZINVALID_SERVICE)rservicerrr check_service+s zFirewallService.check_servicecCs|j||j|S)N)rr)rrrrr get_service/s zFirewallService.get_servicecCs||j|j<dS)N)rname)robjrrr add_service3szFirewallService.add_servicecCs|j||j|=dS)N)rr)rrrrr remove_service6s zFirewallService.remove_serviceN) __name__ __module__ __qualname__r r rrrrrrrrrr rsN)__all__ZfirewallrZfirewall.errorsrobjectrrrrr s  PK!l|q"q" __pycache__/ipset.cpython-36.pycnu[3 Yjq2 @sdZdddgZddlZddlZddlmZddlmZddl m Z dd l m Z dd l mZmZdd lmZd Zd ddddddddddg ZdddddZddddZGd ddeZd!dZd"dZd#d$Zd%d&Zd'd(ZdS))zThe ipset command wrapperipsetcheck_ipset_nameremove_default_create_optionsN)errors) FirewallError)runProg)log)tempFilereadfile)COMMANDS zhash:ipz hash:ip,portzhash:ip,port,ipzhash:ip,port,netz hash:ip,markzhash:netz hash:net,netz hash:net,portzhash:net,port,netzhash:net,ifacezhash:macz inet|inet6valuez value in secs)familyhashsizemaxelemtimeoutZinetZ1024Z65536)rrrc@seZdZdZddZddZddZdd Zd d Zd'd dZ ddZ ddZ ddZ d(ddZ d)ddZddZd*ddZd+ddZdd Zd!d"Zd#d$Zd%d&Zd S),rzipset command wrapper classcCstd|_d|_dS)Nr)r _commandname)selfr/usr/lib/python3.6/ipset.py__init__Ks zipset.__init__cCs^dd|D}tjd|j|jdj|t|j|\}}|dkrZtd|jdj||f|S)zCall ipset with argscSsg|] }d|qS)z%sr).0itemrrr Rszipset.__run..z %s: %s %s rz'%s %s' failed: %s)rdebug2 __class__rjoinr ValueError)rargsZ_argsstatusretrrrZ__runOsz ipset.__runcCs t|tkrttjd|dS)zCheck ipset namezipset name '%s' is not validN)lenIPSET_MAXNAMELENrrZ INVALID_NAME)rrrrr check_nameZs zipset.check_namecCsg}d}y|jdg}Wn0tk rH}ztjd|WYdd}~XnX|j}d}xT|D]L}|r|jjdd}|d|kr|dtkr|j|d|j dr\d }q\W|S) z?Return types that are supported by the ipset command and kernelz--helpzipset error: %sNFrzSupported set types:T) _ipset__runrrZdebug1 splitlinesstripsplit IPSET_TYPESappend startswith)rr"outputZexlinesZin_typeslinesplitsrrrset_supported_types`s    zipset.set_supported_typescCs(t|tks|tkr$ttjd|dS)zCheck ipset typez!ipset type name '%s' is not validN)r#r$r,rrZ INVALID_TYPE)r type_namerrr check_typeuszipset.check_typeNcCsd|j||j|d||g}t|trZx0|jD]$\}}|j||dkr2|j|q2W|j|S)z+Create an ipset with name, type and optionscreater&)r%r5 isinstancedictitemsr-r()rset_namer4optionsr keyvalrrr set_create{s     zipset.set_createcCs|j||jd|gS)NZdestroy)r%r()rr:rrr set_destroys zipset.set_destroycCsd||g}|j|S)Nadd)r()rr:entryr rrrset_adds z ipset.set_addcCsd||g}|j|S)Ndel)r()rr:rAr rrr set_deletes zipset.set_deletecCs,d||g}|r"|jddj||j|S)Ntestz%sr)r-rr()rr:rAr;r rrrrEs z ipset.testcCs2dg}|r|j||r"|j||j|jdS)Nlist )r-extendr(r+)rr:r;r rrrset_lists   zipset.set_listc Cs<|jdgd}i}d}}i}x|D] }t|dkr:q&dd|jddD}t|dkr`q&q&|d d krv|d}q&|d d kr|d}q&|d d kr&|dj}d } x^| t|kr|| } | dkrt|| kr| d7} || || <ntjd|iS| d7} qW|r$|r$|t|f||<d}}|jq&W|S)z" Get active ipsets (only headers) z-terse)r;NcSsg|] }|jqSr)r*)rxrrrrsz.ipset.set_get_active_terse..:r'rNameZTypeZHeaderrrrrnetmaskz&Malformed ipset list -terse output: %s)rrrrrN)rIr#r+rerrorrclear) rr0r"_nameZ_type_optionsr1Zpairr2ioptrrrset_get_active_tersesD            zipset.set_get_active_tersecCsdg}|r|j||j|S)Nsave)r-r()rr:r rrrrVs z ipset.savecCs|j||j|t}d|kr*d|}d||dg}|rlx0|jD]$\}} |j|| dkrD|j| qDW|jddj||jd|xN|D]F} d| krd| } |r|jd|| dj|fq|jd || fqW|jtj |j } t j d |j |jd |j | jfd g}t|j||j d \} } t jdkryt|j Wntk r`YnVXd}xNt|j D]@}t jd||fddd|jdst jddd|d7}qrWtj|j | dkrtd|jdj|| f| S)Nrz'%s'r6z-existr&z%s z flush %s z add %s %s %s z add %s %s z%s: %s restore %sz%s: %dZrestore)stdinr'rJz%8d: %sr)nofmtnlrG)rXz'%s %s' failed: %s)r%r5r r9r-writercloseosstatrrrrrst_sizerZgetDebugLogLevelr ExceptionZdebug3endswithunlinkr)rr:r4entriesZcreate_optionsZ entry_optionsZ temp_filer r<r=rAr]r!r"rSr1rrr set_restoresV         zipset.set_restorecCsdg}|r|j||j|S)Nflush)r-r()rr:r rrr set_flushs zipset.set_flushcCs|jd||gS)Nrename)r()rZ old_set_nameZ new_set_namerrrrf sz ipset.renamecCs|jd||gS)Nswap)r()rZ set_name_1Z set_name_2rrrrgsz ipset.swapcCs |jdgS)Nversion)r()rrrrrhsz ipset.version)N)N)NN)N)NN)__name__ __module__ __qualname____doc__rr(r%r3r5r>r?rBrDrErIrUrVrcrerfrgrhrrrrrHs&    '  7cCst|tkrdSdS)z"Return true if ipset name is validFT)r#r$)rrrrrs cCs8|j}x*tD]"}||krt|||kr||=qW|S)z( Return only non default create options )copyIPSET_DEFAULT_CREATE_OPTIONS)r;rRrTrrrrs   c Cshg}xX|jdD]J}y&|jd|jttj|ddWqtk rX|j|YqXqWdj|S)z! Normalize IP addresses in entry ,/F)strict)r+indexr-str ipaddress ip_networkrr)rAZ_entryZ_partrrrnormalize_ipset_entry&s rvcCsxt|jddkrdSytj|dd}Wntk r<dSXx4|D],}|jtj|ddrDttjdj ||qDWdS)z: Check if entry overlaps any entry in the list of entries rorJNF)rqz,Entry '{}' overlaps with existing entry '{}') r#r+rtruroverlapsrr INVALID_ENTRYformat)rArbZ entry_networkZitrrrrcheck_entry_overlaps_existing2s rzcCs~ydd|D}Wntk r&dSXt|dkr8dS|j|jd}x.|D]&}|j|rrttjdj|||}qPWdS)z> Check if any entry overlaps any entry in the list of entries cSsg|]}tj|ddqS)F)rq)rtru)rrKrrrrEsz1check_for_overlapping_entries..NrzEntry '{}' overlaps entry '{}') rr#sortpoprwrrrxry)rbZ prev_networkZcurrent_networkrrrcheck_for_overlapping_entriesBs 2   r})rl__all__Zos.pathr\rtZfirewallrZfirewall.errorsrZfirewall.core.progrZfirewall.core.loggerrZfirewall.functionsr r Zfirewall.configr r$r,ZIPSET_CREATE_OPTIONSrnobjectrrrrvrzr}rrrrsF      P  PK!6 ;;#__pycache__/nftables.cpython-36.pycnu[3 YjBu @s6ddlmZddlZddlZddlZddlmZddlmZm Z m Z m Z m Z ddl mZmZmZmZmZmZmZddlmZmZmZmZmZmZmZddlmZmZddl m!Z!d Z"e"d d Z#d Z$d Z%iddde%fidde%fdde%fddde%fdde%fdde%fdde%fddZ&Gddde'Z(dS))absolute_importN)log) check_mac getPortRange normalizeIP6check_single_address check_address) FirewallError UNKNOWN_ERROR INVALID_RULEINVALID_ICMPTYPE INVALID_TYPE INVALID_ENTRY INVALID_PORT) Rich_Accept Rich_Reject Rich_Drop Rich_MarkRich_MasqueradeRich_ForwardPortRich_IcmpBlock) ICMP_TYPES ICMPV6_TYPES)NftablesZ firewalld_Z policy_dropZpolicy_ PREROUTING preroutingdZ postrouting)r POSTROUTINGinputforwardoutput)rINPUTFORWARDOUTPUT)rawmanglenatfilterc@sjeZdZdZdZddZddZddZdd Zd d Z d d Z ddZ dddZ ddZ ddZddZddZdddZddZdd d!Zd"d#Zdd%d&Zdd(d)Zdd*d+Zdd,d-Zd.d/Zd0d1Zd2d3Zd4d5Zd6d7Zd8d9Zd:d;Zdd?Z!d@dAZ"dBdCZ#dDdEZ$dFdGZ%dHdIZ&ddJdKZ'dLdMZ(dNdOZ)dPdQZ*dRdSZ+ddTdUZ,ddVdWZ-ddXdYZ.dZd[Z/dd\d]Z0dd^d_Z1dd`daZ2ddbdcZ3ddddeZ4ddfdgZ5dhdiZ6ddjdkZ7dldmZ8ddndoZ9dpdqZ:drdsZ;dtduZddzd{Z?d|d}Z@dd~dZAddZBddZCddZDddZEddZFddZGddZHdddZIdS)nftablesTcCsb||_d|_g|_i|_i|_i|_i|_i|_gggd|_t |_ |j j d|j j ddS)NT)inetipip6) _fwZrestore_command_existsZavailable_tablesrule_to_handlerule_ref_countrich_rule_priority_countspolicy_priority_countszone_source_index_cachecreated_tablesrr+Zset_echo_outputZset_handle_output)selffwr8/usr/lib/python3.6/nftables.py__init__Xs znftables.__init__cCsxdD]}||krPqWd||dkr`||ddd||dddf}||dd=n(d||dkrd}||dd=ndS||dd }|r|dkr||kr|||kr||j|n|dkr||krg||<|r(|||kr||j|||jd d d ||j|}n|jjr8d }n t||}||}||=|d krf||d<n |d8}||d<||ddd<dS)Naddinsertdeletez%%ZONE_SOURCE%%rulezoneaddressz%%ZONE_INTERFACE%%familycSs|dS)Nrr8)xr8r8r9sz3nftables._run_replace_zone_source..)keyrindex)r;r<r=)removeappendsortrFr/_allow_zone_driftinglen)r6r>r4verbZ zone_sourcerArF _verb_snippetr8r8r9_run_replace_zone_sourcegsD        z!nftables._run_replace_zone_sourcecCsBd|krdtj|diSd|kr4dtj|diSttddS)Nr<r=r;zFailed to reverse rule)copydeepcopyr r )r6dictr8r8r9 reverse_rules znftables.reverse_rulec Csxd D]}||krPqW|||dkr||d|}||d|=t|tkr^ttd||dd||ddf}|dkr||ks|||ks|||dkrttd |||d 8<n||kri||<|||krd|||<d}xVt||jD]B}||kr"|dkr"P||||7}||kr|dkrPqW|||d 7<||} ||=|dkr| |d<n |d 8}| |d<||ddd <dS) Nr;r<r=r>z%priority must be followed by a numberrAchainrz*nonexistent or underflow of priority countrErF)r;r<r=)typeintr r r sortedkeys) r6r>Zpriority_countstokenrLpriorityrSrFprMr8r8r9_set_rule_replace_prioritysD          z#nftables._set_rule_replace_prioritycCsfx`d D]X}||krd||krtj||d}xd D]}||kr6||=q6Wtj|dd }|SqWdS) Nr;r<r=r>rFhandlepositionT)Z sort_keys)r;r<r=)rFr\r])rOrPjsondumps)r6r>rLrule_keyZnon_keyr8r8r9 _get_rule_keys   znftables._get_rule_keycCsLdddddg}dddg}g}g}tj|j}tj|j}tj|j} |jj} x|D]} t| tkrvtt d| x|D]} | | kr|Pq|W| | krtt d| |j | } | | krDt j d|j| | | | dkr| | d 7<qVnX| | d kr | | d 8<qVn6| | d kr,| | d 8<ntt d | | | fn| r\| dkr\d | | <|j| tj| }| rttd|| d d || d d <|j||d |j||d|j|| | dkrdd |dd d|dd d|dd d|j| dii}|j|qVWdddd iig|i}t jdkrVt jd|jtj||jj|\}}}|dkrtdd|tj|f||_||_| |_| |_d}x|D]} |d 7}|j | } | s̐qd| kr|j| =|j| =qx"|D]} | |d|krPqW| |d|kr$q|d|| d d|j| <qWdS)Nr;r<r=flushreplacez#rule must be a dictionary, rule: %szno valid verb found, rule: %sz%s: prev rule ref cnt %d, %srEz)rule ref count bug: rule_key '%s', cnt %dr>exprz%%RICH_RULE_PRIORITY%%z%%POLICY_PRIORITY%%rAtablerS)rArerSr\r+ZmetainfoZjson_schema_versionz.%s: calling python-nftables with JSON blob: %srz'%s' failed: %s JSON blob: %szpython-nftablesr\)rOrPr2r3r4r1rTrQr r r rarZdebug2 __class__rHlistr*r[rNr0ZgetDebugLogLevelZdebug3r^r_r+Zjson_cmd ValueError)r6rules log_deniedZ _valid_verbsZ_valid_add_verbsZ_deduplicated_rulesZ_executed_rulesr2r3r4r1r>rLr`Z_ruleZ json_blobZrcr#errorrFr8r8r9 set_ruless             &         znftables.set_rulescCs|j|g|dS)N)rm)r6r>rkr8r8r9set_rule:sznftables.set_ruleNcCs|r |gStjS)N)IPTABLES_TO_NFT_HOOKrW)r6rer8r8r9get_available_tables>sznftables.get_available_tablescCsFg}x policy_keyrAr8r8r9build_flush_rulesPs    znftables.build_flush_rulesc Cslddd|}g}xTdD]L}|j|ddtd d |fd d d diiddddgididdigdiiqW|S)Nr;r=)TFr!r"r#r>r,z%s_%sr*matchctrDstateinset establishedrelated)leftoprightaccept)rArerSrd)r!r"r#)rHTABLE_NAME_POLICY)r6enableadd_delrjhookr8r8r9rtgs    z)nftables._build_set_policy_rules_ct_rulesc Cstg}|dkrt|jdddtdii|jdjtx>dD]6}|jdddtd d |fd |dtd ddiiq:W|dkr|jdddtdii|jdjtx>dD]6}|jdddtd d |fd |dtd ddiiqW||jd7}nz|dkrfx4|jdD]&}|j|}||jkr |j|q W||jt7}t|jdkrp|jdjtn t t d|S)NZPANICr;rer,)rArrrr#rSz%s_%sr'r*i,rEdrop)rArerrrTrpriopolicyDROPr!r"rTACCEPTFznot implemented)rr#i)r!r"r#) rHrr5NFT_HOOK_OFFSETrtrar0rsrGr r )r6rrjrr>rvr8r8r9build_set_policy_rulestsH               znftables.build_set_policy_rulescCsJt}|dks|dkr$|jtj|dks4|dkrB|jtjt|S)Nipv4ipv6)r|updaterrWrrh)r6ipvZ supportedr8r8r9supported_icmp_typess znftables.supported_icmp_typescCs>g}x4dD],}|jdd|tdii|j|jtq W|S)Nr,r-r.r;re)rArr)r,r-r.)rHrur5)r6Zdefault_tablesrAr8r8r9build_default_tabless   znftables.build_default_tablesoffcCsg}xtdjD]}|jdddtd|ddtd|dtd|d d iixz|jjrld d d dgnd d dgD]X}|jdddtd||fdii|jdddtd|ddd||fiigdiiqvWqWxd?D]}xtdjD]}|jdd|td|ddtd|dtd|d d iix~|jjrJd d d dgnd d dgD]Z}|jdd|td||fdii|jdd|td|ddd||fiigdiiqTWqWqWxVtdjD]F}|jdddtd|ddtd|dtd|d d iiqW|jdddtddddddiid d!d"d#gid$id%digdii|jdddtdddddd&iid d'd$id%digdii|jdddtdddd(dd)iid*d+d$id%digdiix~|jjrd d d dgnd d dgD]Z}|jdddtd,d|fdii|jdddtddddd,d|fiigdiiqW|d-kr|jdddtddddddiid d!d.gid$i|j|d/d0d1iigdii|jdddtddddddiid d!d.gid$id2digdii|d-kr$|jdddtdd|j|d/d0d3iigdii|jdddtddd4d5d6d7igdii|jdddtdd8ddddiid d!d"d#gid$id%digdii|jdddtdd8dddd&iid d'd$id%digdii|jdddtdd8dd(dd)iid*d+d$id%digdiixbd@D]Z}|jdddtd,d8|fdii|jdddtdd8ddd,d8|fiigdiiqWxdAD]}xz|jjrd d gnd gD]^}|jdddtd;d8||fdii|jdddtdd8ddd;d8||fiigdiiqWqvWxbdBD]Z}|jdddtd,d8|fdii|jdddtdd8ddd,d8|fiigdiiqW|d-kr|jdddtdd8ddddiid d!d.gid$i|j|d/d0d1iigdii|jdddtdd8ddddiid d!d.gid$id2digdii|d-kr6|jdddtdd8|j|d/d0d3iigdii|jdddtdd8d4d5d6d7igdii|jdddtdd<ddddiid d!d"d#gid$id%digdii|jdddtd=dd(dd>iid*d+d$id%digdiixbdCD]Z}|jdddtd,d<|fdii|jdddtdd<ddd,d<|fiigdiiqWxbdDD]Z}|jdddtd,d<|fdii|jdddtdd<ddd,d<|fiigdiiqHW|S)ENr(r;rSr,z mangle_%sr*z%srrE)rArerrrTrr POLICIES_preZ ZONES_SOURCEZZONES POLICIES_postz mangle_%s_%s)rArerrr>jumptarget)rArerSrdr-r.r)znat_%sz nat_%s_%sz filter_%sr$rxryrDrzr{r|r}r~)rrrrZstatusdnatmetaiifnamez==loz filter_%s_%srZinvalidrprefixzSTATE_INVALID_DROP: rzFINAL_REJECT: rejecticmpxzadmin-prohibited)rTrdr%INOUTzfilter_%s_%s_%sr& filter_OUTPUToifname)r-r.)r)rr)r)r)r)rprWrHrur/rJ_pkttype_match_fragment)r6rkZ default_rulesrSZdispatch_suffixrA directionr8r8r9build_default_ruless $  (  &  .        &  &                 &   .   &               &   &znftables.build_default_rulescCs4|dkrdddgS|dkr dgS|dkr0ddgSgS) Nr*r$ FORWARD_IN FORWARD_OUTr(rr)r r8)r6rer8r8r9get_zone_table_chainss znftables.get_zone_table_chainsr,c  sdkr\dkr\g} | jj|||||dd | jj|||||dd | Sjjj|jdkrxdnddkrd krd nd } jjj|t| g} g} |r| jd d ddiiddt |idi|r| jd d ddiiddt |ididdd}|rlxT|D]L}dkrTjj j |}||krT||krTq| jj d|qW|rxT|D]L}dkrjj j |}||kr||krqx| jj d|qxWfdd}g} | rHx| D]P}| rxB| D]}| j|||qWn"dkr0|r0n| j||dqWn\dkrZ|rZnJ| rxB| D]}| j|d|qfWn"dkr|rn| j|dd| S)Nr)r,r-)rAr.rprepostr TFrxrrDrz==r|)rrrr)rrsaddrdaddrcsg}|r|j||r |j||jdddfiitdf|d}|jjrrdd|iiSdd|iiSdS) Nrrz%s_%sz%s_%s_POLICIES_%s)rArerSrdr;r>r=)rHrur_policy_priority_fragment)ingress_fragmentegress_fragmentexpr_fragmentsr>)_policyrS chain_suffixrrAp_objr6rer8r9_generate_policy_dispatch_rules    zRnftables.build_policy_ingress_egress_rules.._generate_policy_dispatch_rule) extend!build_policy_ingress_egress_rulesr/rZ get_policyrYpolicy_base_chain_namePOLICY_CHAIN_PREFIXrHrhr?Z check_source_rule_addr_fragment)r6rrrerSZingress_interfacesZegress_interfacesZingress_sourcesZegress_sourcesrArjisSNATZingress_fragmentsZegress_fragmentsZ ipv_to_familysrcrdstrrrr8)rrSrrrArr6rer9rsv          z*nftables.build_policy_ingress_egress_rulesFc  Cs|dkrT|dkrTg} | j|j|||||||d| j|j|||||||d| S|dkrh|dkrhdnd} |jjj||t| d} d d d d d d d |} |t|d d kr|dt|d d}d} |dkr| dd|| fiig}n,ddd| iid|di| dd|| fiig}|rL| rLd}|td||f|d}|j|j nP|rnd}|td||f|d}n.d}|td||f|d}|s|j|j |d|iigS)Nr)r,r-r.r TF)rrr)rr r$rrr&rE+*gotorz%s_%srxrrDz==)rrrr<z %s_%s_ZONES)rArerSrdr;r=r>) r!build_zone_source_interface_rulesr/rrrrKrur_zone_interface_fragment)r6rr?r interfacererSrHrArjrroptactionrrLr>r8r8r9rs\     z*nftables.build_zone_source_interface_rulesc Csn|dkr|dkrg}|jdr6|j|tdd} nd} td|sTt|sT| dkrp|j|j||||||dtd|st|s| dkr|j|j||||||d|S|dkr|dkrd nd } |jjj ||t | d } d d d|} ddddddd|} |jj rd||f}n d||f}d}|t ||j | ||dd|| fiigd}|j|j||| d|iigS)Nr)r,zipset:rr-rr.r TF)rr<r=)TFrr)rr r$rrr&z%s_%s_ZONES_SOURCEz %s_%s_ZONESrrz%s_%s)rArerSrdr>) startswith_set_get_familyrKrrrbuild_zone_source_address_rulesr/rrrrJrurr_zone_source_fragment)r6rr?rr@rerSrArjZ ipset_familyrrrrZzone_dispatch_chainrr>r8r8r9r<sB    z(nftables.build_zone_source_address_rulesc Cs|dkrH|dkrHg}|j|j||||d|j|j||||d|Sddd|}|dkrj|dkrjd nd }|jjj||t|d } g}|j|d |td || fdiix0d!D](} |j|d |td|| | fdiiqWxDd"D]<} |j|d|td || fddd|| | fiigdiiqW|jjj|j } |jj dkr|dkr| d#kr| } | dkrhd} |j|d|td || f|j |jj ddd| | fiigdii|dkr| d$kr| d%kr|j } n | j di} |j|d|td || f| gdii|s|j|S)&Nr)r,r-r.r;r=)TFr TF)rrSz%s_%s)rArerrrrdenyallowrz%s_%s_%sr>rr)rArerSrdrr*REJECT %%REJECT%%rrz"filter_%s_%s: "r)rrrrr)rrrrr)rrr)rrrr)rr)rbuild_policy_chain_rulesr/rrrrHruZ _policiesrget_log_deniedr_reject_fragmentlowerreverse)r6rrrerSrArjrrrrrZ log_suffixtarget_fragmentr8r8r9rjsZ      &             z!nftables.build_policy_chain_rulescCs<|dkr iS|d kr,ddddiid |d iSttd |dS) Nallunicast broadcast multicastrxrrDpkttypez==)rrrzInvalid pkttype "%s")rrr)r r )r6rr8r8r9rs  z nftables._pkttype_match_fragmentcCsddddiddddiddddiddddiddddiddddiddddiddddiddddiddddiddd diddd diddd diddd didd d diddd diddd diddd diddd diddddiddddidddiidddiid}||S)Nricmpzhost-prohibited)rTrdznet-prohibitedzadmin-prohibitedicmpv6znet-unreachablezhost-unreachablezport-unreachablerzprot-unreachablezaddr-unreachablezno-routerTz tcp reset)zicmp-host-prohibitedz host-prohibzicmp-net-prohibitedz net-prohibzicmp-admin-prohibitedz admin-prohibzicmp6-adm-prohibitedzadm-prohibitedzicmp-net-unreachablez net-unreachzicmp-host-unreachablez host-unreachzicmp-port-unreachablezicmp6-port-unreachablez port-unreachzicmp-proto-unreachablez proto-unreachzicmp6-addr-unreachablez addr-unreachzicmp6-no-routezno-routez tcp-resetztcp-rstr8)r6Z reject_typeZfragsr8r8r9_reject_types_fragments0                      znftables._reject_types_fragmentcCsddddiS)Nrrzadmin-prohibited)rTrdr8)r6r8r8r9rsznftables._reject_fragmentcCs ddddiiddddgid iS) NrxrrDl4protoz==r|rr)rrrr8)r6r8r8r9_icmp_match_fragments znftables._icmp_match_fragmentcCsP|siSddddd}|j\}}|||d}|j}|dk rH||d<d|iS) NsecondZminuteZhourZday)smhd)rateZperburstlimit)Z value_parseZ burst_parse)r6rZ rich_to_nftrZdurationrrr8r8r9_rich_rule_limit_fragments  z"nftables._rich_rule_limit_fragmentcCst|jtttgkrn<|jrHt|jtttt gkrRt t dt|jn t t d|j dkrt|jttgkst|jtt gkrdSt|jtgkst|jttgkrdSn|j dkrdSdSdS)NzUnknown action %szNo rule action specified.rrrrr) rTelementrrrrrrrrr r rY)r6 rich_ruler8r8r9_rich_rule_chain_suffixs    z nftables._rich_rule_chain_suffixcCs>|j r|j rttd|jdkr(dS|jdkr6dSdSdS)NzNot log or auditrrrr)rauditr r rY)r6rr8r8r9 _rich_rule_chain_suffix_from_logs   z)nftables._rich_rule_chain_suffix_from_logcCsddiS)Nz%%ZONE_INTERFACE%%r8)r6r8r8r9rsz!nftables._zone_interface_fragmentcCsNtd|rt|}n,td|r@|jd}t|dd|d}d||diS)Nr/rrEz%%ZONE_SOURCE%%)r?r@)rrrsplit)r6r?r@Z addr_splitr8r8r9rs     znftables._zone_source_fragmentcCs d|jiS)Nz%%POLICY_PRIORITY%%)rY)r6rr8r8r9rsz"nftables._policy_priority_fragmentcCs| s|jdkriSd|jiS)Nrz%%RICH_RULE_PRIORITY%%)rY)r6rr8r8r9_rich_rule_priority_fragmentsz%nftables._rich_rule_priority_fragmentc Cs|js iS|jjj||t}ddd|}|j|}i} |jjrPd|jj| d<|jjr|d|jjkrhdn|jj} d| | d<d td |||f||j |jj d | igd } | j |j ||d | iiS)Nr;r=)TFz%srZwarningwarnlevelr,z%s_%s_%sr)rArerSrdr>) rr/rrrrrrrurrrr) r6rrrrerrrrZ log_optionsrr>r8r8r9_rich_rule_log"s&    znftables._rich_rule_logc Cs|js iS|jjj||t}ddd|}|j|}dtd|||f||j|jjdddiigd } | j |j ||d | iiS) Nr;r=)TFr,z%s_%s_%srrr)rArerSrdr>) rr/rrrrrurrrr) r6rrrrerrrrr>r8r8r9_rich_rule_audit<s   znftables._rich_rule_auditc Cs|js iS|jjj||t}ddd|}|j|}d|||f} t|jtkr\ddi} nt|jtkr|jjr|j |jj} nddi} nt|jt krddi} nt|jt krHd}|jjj||t}d|||f} |jj j d } t| d kr,dd d d iiddd d d ii| d gi| dgidi} ndd d d ii| ddi} nttdt|jdt| ||j|jj| gd} | j|j||d| iiS)Nr;r=)TFz%s_%s_%srrrr(rrErrDmark^&r)rDvaluezUnknown action %sr,)rArerSrdr>)rr/rrrrrTrrrrrr|rrKr r rurrrr) r6rrrrerrrrrSZ rule_actionrr>r8r8r9_rich_rule_actionNsB     , znftables._rich_rule_actioncCs|jdr0|j|tddd|kr(dnd|St|r>d}ntd|rNd}nvtd|rd}tj|dd}d |jj |j d i}nDtd |rd }t |}n,d }|j d }d t |dt |dd i}dd||di|rdnd|diSdS)Nzipset:rTFetherrr-)strictr)addrrKrr.rrrErxpayload)protocolfieldz!=z==)rrr)r_set_match_fragmentrKrrr ipaddressZ IPv4NetworkZnetwork_addressZ compressedZ prefixlenrrrU)r6Z addr_fieldr@invertrAZnormalized_addressZaddr_lenr8r8r9rys( &      znftables._rule_addr_fragmentcCs6|siS|d krttd|ddddiid|d iS) NrrzInvalid familyrxrrDnfprotoz==)rrr)rr)r r )r6Z rich_familyr8r8r9_rich_rule_family_fragments  z#nftables._rich_rule_family_fragmentcCs8|siS|jr|j}n|jr&d|j}|jd||jdS)Nzipset:r)r)ripsetrr)r6Z rich_destr@r8r8r9_rich_rule_destination_fragments z(nftables._rich_rule_destination_fragmentcCsZ|siS|jr|j}n2t|dr.|jr.|j}nt|drH|jrHd|j}|jd||jdS)Nmacrzipset:r)r)rhasattrrrrr)r6Z rich_sourcer@r8r8r9_rich_rule_source_fragments z#nftables._rich_rule_source_fragmentcCsPt|}t|tr$|dkr$ttn(t|dkr8|dSd|d|dgiSdS)NrrErange)r isinstancerUr rrK)r6portrr8r8r9_port_fragments   znftables._port_fragmentc Csbddd|}d}|jjj||t} g} |r>| j|j|j|rT| j|jd||r|| j|j|j | j|j |j | jdd|dd id |j |d i| st |jtkr| jdd d diiddddgid ig} |r0| j|j||||| | j|j||||| | j|j||||| n.| j|ddtd|| f| ddigdii| S)Nr;r=)TFr*rrxrdport)rrz==)rrrryrDrzr{r|new untrackedr>r,z %s_%s_allowr)rArerSrd)r/rrrrHrrArr destinationrsourcer rTrrrrrru) r6rrprotor rrrrerrrjr8r8r9build_policy_ports_ruless:   z!nftables.build_policy_ports_rulesc CsZddd|}d}|jjj||t}g} |r>| j|j|j|rT| j|jd||r|| j|j|j | j|j |j | jdddd iid |d i| st |j tkr| jdd dd iiddddgid ig} |r(| j|j||||| | j|j||||| | j|j||||| n.| j|ddtd||f| ddigdii| S)Nr;r=)TFr*rrxrrDrz==)rrrryrzr{r|r rr>r,z %s_%s_allowr)rArerSrd)r/rrrrHrrArrrrrrTrrrrrru) r6rrrrrrrerrrjr8r8r9build_policy_protocol_ruless8   z$nftables.build_policy_protocol_rulesc Csbddd|}d}|jjj||t} g} |r>| j|j|j|rT| j|jd||r|| j|j|j | j|j |j | jdd|dd id |j |d i| st |jtkr| jdd d diiddddgid ig} |r0| j|j||||| | j|j||||| | j|j||||| n.| j|ddtd|| f| ddigdii| S)Nr;r=)TFr*rrxrsport)rrz==)rrrryrDrzr{r|r rr>r,z %s_%s_allowr)rArerSrd)r/rrrrHrrArrrrrr rTrrrrrru) r6rrrr rrrrerrrjr8r8r9build_policy_source_ports_ruless:   z(nftables.build_policy_source_ports_rulesc Csd}|jjj||t} ddd|} g} |rR| jdddtd||f||diig} |rl| j|jd || jd d |d d id|j|di| jdd||fi| j| ddtd| | dii| S)Nr*r;r=)TFz ct helperr,z helper-%s-%s)rArerrrTrrrxrr )rrz==)rrrr>zfilter_%s_allow)rArerSrd)r/rrrrHrurr ) r6rrrr rZ helper_nameZmodule_short_namererrrjrr8r8r9build_policy_helper_ports_rules)s.    z(nftables.build_policy_helper_ports_rulesc Csddd|}|jjj||t}g} |rv|t|ddkrT|dt|dd}ddd d iid |d id dig} n|jd|d dig} dtd|| d} | j|d| ii| S)Nr;r=)TFrErrrxrrDrz==)rrrrrr,zfilter_%s_allow)rArerSrdr>)r/rrrrKrrurH) r6rr?rrerrrrrjrdr>r8r8r9build_zone_forward_rulesFs"  z!nftables.build_zone_forward_rulesc Csd}|jjj||tdd}ddd|}g}|r`|j|j|j|j|j|j|j |} nd} |t d|| f|d d d d iid ddiddigd} | j |j ||d| iigS)Nr)T)rr;r=)TFrz nat_%s_%srxrrDrz!=r)rrrZ masquerade)rArerSrdr>) r/rrrrHrrrrrrurr) r6rrrArrerrrrr>r8r8r9"_build_policy_masquerade_nat_rules_s&   z+nftables._build_policy_masquerade_nat_rulesc Cs^g}|rD|jr|jdks,|jrDtd|jjrD|j|j||d|nV|r|jrX|jdksl|jrtd|jjr|j|j||d|n|j|j||d|d}|jjj||t }ddd|}g}|r|j |j |j |j |j |j|j|} nd } d td || f|d d ddiiddddgididdigd} | j|j||j |d| ii|S)Nrr.rr-r*r;r=)TFrr,z filter_%s_%srxryrDrzr{r|r r)rrrr)rArerSrdr>)rArrrrrr/rrrrHrrrrrurr) r6rrrrjrerrrrr>r8r8r9build_policy_masquerade_rulesxs8   z&nftables.build_policy_masquerade_rulesc Cs$d} |jjj|| t} ddd|} g} |r\| j|j|j| j|j|j|j |} nd} | jdd|dd id |j |d i|rt d |rt |}|r|d kr| jd||j |diq| jdd|iin| jdd|j |ii|t d| | f| d}|j|j|| d|iigS)Nr)r;r=)TFrrxrr )rrz==)rrrrrnr)rr rZredirectr z nat_%s_%s)rArerSrdr>)r/rrrrHrrrrrr rrrurr)r6rrr rtoaddrtoportrArrerrrrr>r8r8r9$_build_policy_forward_port_nat_ruless4     z-nftables._build_policy_forward_port_nat_rulesc Csg}|rF|jr|jdks&|rFtd|rF|j|j||||||d|n|r|jrZ|jdksh|rtd|r|j|j||||||d|nL|rtd|r|j|j||||||d|n|j|j||||||d||S)Nrr.rr-)rArrr) r6rrr rrrrrjr8r8r9build_policy_forward_port_ruless    z(nftables.build_policy_forward_port_rulescCsHdd|ddid|dig}|dk rD|jdd|ddid|di|S)NrxrrT)rrz==)rrrcode)rH)r6rrTr fragmentsr8r8r9_icmp_types_fragmentss  znftables._icmp_types_fragmentscCs|dkr4|tkr4t|\}}}|jd||r.dn|S|dkrh|tkrht|\}}}|jd||rbdn|Sttd||j|fdS)Nrrrrz)ICMP type '%s' not supported by %s for %s)rr rr r rr)r6rZ icmp_typeZ_type_codeZ _omit_coder8r8r9_icmp_types_to_nft_fragmentssz%nftables._icmp_types_to_nft_fragmentscCsBd}|jjj||t}ddd|}|r6|jr6|j}n<|jrjg}d|jkrT|jdd|jkrr|jdnddg}g} x|D]} |jjj|rd||f} ddi} nd ||f} |j} g} |r| j|j |j | j|j |j| j|j |j | j|j| |j|r| j|j||||| | j|j||||| |jrf| j|j||||| nN|j|}d td |||f| |jgd }|j|j|| j|d |iiq~|jjdkr|jjj| r| j|d d t| | |j|jjddd||fiigd ii| j|d d t| | | gd iiq~W| S)Nr*r;r=)TFrrz %s_%s_allowrz %s_%s_denyr,z%s_%s_%s)rArerSrdr>rrrz"%s_%s_ICMP_BLOCK: ")r/rrripvsrrHquery_icmp_block_inversionrrrArrrrr"rrrrrrrrurrrr)r6rrZictrrerrr#rjrZ final_chainrrrr>r8r8r9build_policy_icmp_block_rulessb          " " z&nftables.build_policy_icmp_block_rulescCsd}|jjj||t}g}ddd|}|jjj|r@|j}nddi}|j|ddtd||fd |j|gd ii|jj d kr|jjj|r|j|ddtd||fd |j|j |jj d d d||fiigd ii|S)Nr*r;r=)TFrr>r,z%s_%s)rArerSrFrdrrrz%s_%s_ICMP_BLOCK: ) r/rrrr$rrHrurrr)r6rrrerrjrrr8r8r9'build_policy_icmp_block_inversion_rules(s,      z0nftables.build_policy_icmp_block_inversion_rulesc Csg}ddddiidddiddd d d gd d idddig}|dkrV|jdddii|jddi|jdddtd|dii|jdddtddddddiddddgidid digdii|S)!NrxrrDrz==r)rrrZfibrZiifrZoif)flagsresultFrrrzrpfilter_DROP: rr<r>r,Zfilter_PREROUTING)rArerSrdrrrT)rrr|znd-router-advertznd-neighbor-solicitr)rHru)r6rkrjrr8r8r9build_rpfilter_rulesGs0     znftables.build_rpfilter_rulesc Csddddddddd g }d d |D}d d dddidd|idig}|jjd"krb|jdddii|j|jdg}|jdddtdd|dii|jdddtd d!|dii|S)#Nz ::0.0.0.0/96z::ffff:0.0.0.0/96z2002:0000::/24z2002:0a00::/24z2002:7f00::/24z2002:ac10::/28z2002:c0a8::/32z2002:a9fe::/32z2002:e000::/19cSs2g|]*}d|jddt|jdddiqS)rrrrE)rrK)rrU).0rBr8r8r9 nsz5nftables.build_rfc3964_ipv4_rules..rxrr.r)rrz==r|)rrrrrrrzRFC3964_IPv4_REJECT: z addr-unreachr;r>r,rrE)rArerSrFrdZfilter_FORWARD)rr)r/Z _log_deniedrHrru)r6Z daddr_setrrjr8r8r9build_rfc3964_ipv4_rulescs:   z!nftables.build_rfc3964_ipv4_rulescCsd}g}|j|j|j|j|j|j|j|j|jg}|j|j||||||j|j||||||j|j ||||||S)Nr*) rHrrArrrrrrr)r6rrrrerrjr8r8r9*build_policy_rich_source_destination_rulessz3nftables.build_policy_rich_source_destination_rulescCs|dkr dSdS)NrrebTF)rrr0r8)r6rr8r8r9is_ipv_supportedsznftables.is_ipv_supportedc Csddd}||||ddg||dd||g||dd||g||dg||||||g||ddg||dd||g||dgdd }||kr||Sttd |dS) NZ ipv4_addrZ ipv6_addr)rrZ inet_protoZ inet_servicerZifnameZ ether_addr) zhash:ipz hash:ip,portzhash:ip,port,ipzhash:ip,port,netz hash:ip,markzhash:netz hash:net,netz hash:net,portzhash:net,port,netzhash:net,ifacezhash:macz!ipset type name '%s' is not valid)r r )r6rrTZipv_addrtypesr8r8r9_set_type_lists"    znftables._set_type_listc Cs|rd|kr|ddkrd}nd}t||j||d}x0|jddjdD]}|dkrLd g|d <PqLW|rd|kr|d|d<d|kr|d|d<g}x0dD](}d|i} | j||jdd| iiqW|S)NrAinet6rr)rerrrT:rE,r-netr Zintervalr(ZtimeoutZmaxelemsizer,r.r;r|)r-r7r )r,r-r.)rur3rrrH) r6rrrToptionsrZset_dicttrjrAZ rule_dictr8r8r9build_set_create_ruless*     znftables.build_set_create_rulescCs$|j|||}|j||jjdS)N)r;rmr/r)r6rrrTr9rjr8r8r9 set_createsznftables.set_createcCs8x2dD]*}dd|t|dii}|j||jjqWdS)Nr,r-r.r=r|)rArerr)r,r-r.)ruror/r)r6rrrAr>r8r8r9 set_destroys   znftables.set_destroycCs6|jjj|jjddjd}g}xtt|D]}||dkrr|jdddii|jdd |rdd nd d iq2||dkr|jd|j||rdndd iq2||dkr|jdd|rdndiiq2||dkr|jdddiiq2t d||q2Wdt|dkrd|in|d|r&dndd|diS)Nr5rEr6r rrDrrZthr r)rrr-r7rrrZifacerrrz-Unsupported ipset type for match fragment: %srxconcatrz!=z==@)rrr)r-r7r) r/r get_ipsetrTrrrKrHrr )r6rrZ match_destr type_formatrir8r8r9rs$      znftables._set_match_fragmentc CsN|jjj|}|jjddjd}|jd}t|t|krHttdg}xtt|D]}||dkr,y||j d}Wn&t k r|j d||} Yn,X|j ||d||||dd} y| j d}Wn t k r|j | Yn(X|j d| d|| |ddgiq\||dkr d||krb|j d||jdiny||j d }WnLt k r||} d |j kr|j d d krt | } |j | Yn^X||d|} d |j kr|j d d krt | } |j d| t|||dddiq\|j ||q\Wt|dkrJd|igS|S)Nr5rEr6z+Number of values does not match ipset type.r Ztcp-rr-r7rrAr4r)rrKr>)r-r7)r/rr@rTrrKr rrrFrirHr9rrU) r6rrentryobjrAZ entry_tokensZfragmentrBrFZport_strrr8r8r9_set_entry_fragmentsL  ("znftables._set_entry_fragmentc Csjg}g}t|ttfs|g}x|D]}|j|j||q"Wx(dD] }|jdd|t||diiqBW|S)Nr,r-r.r;r)rArerrelem)r,r-r.)r rhtuplerrFrHru)r6rrentriesrjelementsrrAr8r8r9build_set_add_rules(s   znftables.build_set_add_rulescCs"|j||}|j||jjdS)N)rKrmr/r)r6rrrDrjr8r8r9set_add7s znftables.set_addcCsF|j||}x4dD],}dd|t||dii}|j||jjqWdS)Nr,r-r.r=r)rArerrrG)r,r-r.)rFruror/r)r6rrrDrrAr>r8r8r9 set_delete;s   znftables.set_deletecCs4g}x*dD]"}dd|t|dii}|j|q W|S)Nr,r-r.rbr|)rArerr)r,r-r.)rurH)r6rrrjrAr>r8r8r9build_set_flush_rulesDs  znftables.build_set_flush_rulescCs |j|}|j||jjdS)N)rNrmr/r)r6rrrjr8r8r9 set_flushMs znftables.set_flushcCsJ|jjj|}|jdkrd}n(|jrBd|jkrB|jddkrBd}nd}|S)Nzhash:macrrAr4r.r-)r/rr@rTr9)r6rrrrAr8r8r9rQs znftables._set_get_familycCsg}|j|j||||j|j|xbtdt|dD]<}|j|j||||d|j||jj|j q:W|j||jjdS)Nri) rr;rNrrKrKrmr/rclear)r6Zset_nameZ type_namerIZcreate_optionsZ entry_optionsrjrBr8r8r9 set_restore^s znftables.set_restore)N)N)r)r,)Fr,)r,)r,)F)NN)NN)NN)NN)N)N)N)N)N)N)F)N)N)F)NN)J__name__ __module__ __qualname__rrZpolicies_supportedr:rNrRr[rarmrorqrsrwrtrrrrrrrrrrrrrrrrrrrrrrrrrrrr rrrrrrrrrr r"r%r'r*r.r/r1r3r;r<r=rrFrKrLrMrNrOrrQr8r8r8r9r+Ts/.`  4  R i ; - 9   +     $ $ $   ' $   < #   4  r+iji))Z __future__rrOr^rZfirewall.core.loggerrZfirewall.functionsrrrrrZfirewall.errorsr r r r r rrZfirewall.core.richrrrrrrrZfirewall.core.icmprrZnftables.nftablesrrurrrrpobjectr+r8r8r8r9s,  $$       PK!5n  &__pycache__/fw_policies.cpython-36.pycnu[3 Yj @sVdgZddlmZddlmZddlmZddlmZddlm Z Gddde Z dS) FirewallPolicies)config)log)LockdownWhitelist)errors) FirewallErrorc@sDeZdZddZddZddZddZd d Zd d Zd dZ dS)rcCsd|_ttj|_dS)NF) _lockdownrrZLOCKDOWN_WHITELISTlockdown_whitelist)selfr !/usr/lib/python3.6/fw_policies.py__init__szFirewallPolicies.__init__cCsd|j|j|jfS)Nz %s(%r, %r)) __class__rr )r r r r __repr__#s zFirewallPolicies.__repr__cCsd|_|jjdS)NF)rr cleanup)r r r r r'szFirewallPolicies.cleanupcCs|dkr2tjd||jj|rtjddSn|dkrdtjd||jj|rtjddSnb|dkrtjd ||jj|rtjd dSn0|d krtjd ||jj|rtjd dSdS)Ncontextz#Doing access check for context "%s"zcontext matches.TZuidzDoing access check for uid %dz uid matches.userz Doing access check for user "%s"z user matches.Zcommandz#Doing access check for command "%s"zcommand matches.F)rZdebug2r Z match_contextZdebug3Z match_uidZ match_userZ match_command)r keyvaluer r r access_check-s*        zFirewallPolicies.access_checkcCs|jrttjdd|_dS)Nzenable_lockdown()T)rrrZALREADY_ENABLED)r r r r enable_lockdownDs z FirewallPolicies.enable_lockdowncCs|jsttjdd|_dS)Nzdisable_lockdown()F)rrrZ NOT_ENABLED)r r r r disable_lockdownIs z!FirewallPolicies.disable_lockdowncCs|jS)N)r)r r r r query_lockdownNszFirewallPolicies.query_lockdownN) __name__ __module__ __qualname__r rrrrrrr r r r rsN) __all__ZfirewallrZfirewall.core.loggerrZ#firewall.core.io.lockdown_whitelistrrZfirewall.errorsrobjectrr r r r s      PK!4)c RR(__pycache__/fw_zone.cpython-36.opt-1.pycnu[3 Yjy@sddlZddlZddlmZmZmZddlmZddlm Z ddl m Z ddl m Z mZmZmZmZmZmZmZmZddlmZmZmZddlmZdd lmZdd lmZGd d d e Z!dS) N) SHORTCUTSDEFAULT_ZONE_TARGETSOURCE_IPSET_TYPES)FirewallTransaction)Policy)log) Rich_Service Rich_Port Rich_ProtocolRich_SourcePortRich_ForwardPortRich_IcmpBlock Rich_IcmpTypeRich_Masquerade Rich_Mark) checkIPnMask checkIP6nMask check_mac)errors) FirewallError)LastUpdatedOrderedDictc@sNeZdZdZddZddZddZdd Zd d Zd d Z ddZ ddZ ddZ ddZ ddZddZddZddZddd Zd!d"Zd#d$Zd%d&Zdd'd(Zd)d*Zd+d,Zd-d.Zdd/d0Zdd1d2Zd3d4Zd5d6Zd7d8Zd9d:Zd;d<Z d=d>Z!dd@dAZ"dBdCZ#ddDdEZ$ddFdGZ%ddHdIZ&dJdKZ'dLdMZ(dNdOZ)ddQdRZ*ddSdTZ+ddUdVZ,dWdXZ-ddYdZZ.dd[d\Z/d]d^Z0d_d`Z1dadbZ2ddcddZ3dedfZ4dgdhZ5didjZ6dkdlZ7dmdnZ8dodpZ9ddqdrZ:dsdtZ;dudvZd{d|Z?d}d~Z@ddZAdddZBddZCddZDddZEddZFdddZGddZHddZIddZJdddZKddZLddZMddZNdddZOddZPddZQdddZRdddZSdddZTddZUdddZVddZWddZXddZYdddZZddZ[ddZ\ddZ]ddZ^ddZ_dddZ`ddZaddd„ZbddĄZcddƄZddS) FirewallZonercCs||_i|_i|_dS)N)_fw_zones_zone_policies)selffwr/usr/lib/python3.6/fw_zone.py__init__&szFirewallZone.__init__cCsd|j|jfS)Nz%s(%r)) __class__r)rrrr__repr__+szFirewallZone.__repr__cCs|jj|jjdS)N)rclearr)rrrrcleanup.s zFirewallZone.cleanupcCs t|jS)N)rr)rrrrnew_transaction2szFirewallZone.new_transactioncCsdj||dS)Nzzone_{fromZone}_{toZone})fromZonetoZone)format)rr%r&rrrpolicy_name_from_zones5sz#FirewallZone.policy_name_from_zonescCst|jjS)N)sortedrkeys)rrrr get_zones:szFirewallZone.get_zonescCs8g}x.|jD]"}|j|s&|j|r|j|qW|S)N)r+list_interfaces list_sourcesappend)rZ active_zoneszonerrrget_active_zones=s zFirewallZone.get_active_zonescCs6|j|}x&|jD]}||j|jdkr|SqWdS)N interfaces)_FirewallZone__interface_idrsettings)r interface interface_idr/rrrget_zone_of_interfaceDs   z"FirewallZone.get_zone_of_interfacecCs6|j|}x&|jD]}||j|jdkr|SqWdS)Nsources)_FirewallZone__source_idrr3)rsource source_idr/rrrget_zone_of_sourceLs   zFirewallZone.get_zone_of_sourcecCs|jj|}|j|S)N)r check_zoner)rr/zrrrget_zoneTs zFirewallZone.get_zonecCsBt}|j|_|j|||_|j|_|j|_|g|_|g|_xd D]}||jkr~|d kr~|d kr~t ||t j t ||qD|d kr||jkr|d krt ||t j t ||qD||jko|d ko|dkrt ||t j t ||qD|dkrDg|_ xB|j D]8}|j||}||j|j|kr|j jt j |qWqDW|S)Nservicesports masquerade forward_ports source_ports icmp_blocksrules protocolsHOSTANY)r?r@rArBrCrDrErF)r?r@rCrDrF)rA)rDrB)rE)rnameZderived_from_zoner(ZONE_POLICY_PRIORITYZprioritytargetZ ingress_zonesZ egress_zonessetattrcopydeepcopygetattrrE_rich_rule_to_policiesr.)rz_objr%r&p_objZsettingruleZcurrent_policyrrrpolicy_obj_from_zone_objXs6    z%FirewallZone.policy_obj_from_zone_objcCsddd D|_||j|j<g|j|j<xX|jdfd|jf|jdfgD]8\}}|j|||}|jjj||j|jj|jqFW|j |jdS) NcSsi|] }t|qSr)r).0xrrr sz)FirewallZone.add_zone..r1r7icmp_block_inversionforwardrGrH)r1r7rXrY) r3rrIrrTrpolicyZ add_policyr.copy_permanent_to_runtime)robjr%r&rRrrradd_zone~s   zFirewallZone.add_zonecCsn|j|}x|jD]}|j||ddqWx|jD]}|j||ddq2W|jrZ|j||jrj|j|dS)NF) allow_apply) rr1 add_interfacer7 add_sourcerY add_forwardrXadd_icmp_block_inversion)rr/r\argrrrr[s    z&FirewallZone.copy_permanent_to_runtimecCs8|j|}|jr|j||jj|j|=|j|=dS)N)rappliedunapply_zone_settingsr3r"r)rr/r\rrr remove_zones    zFirewallZone.remove_zoneNcCsVxP|jD]D}|j|}t|jdks4t|jdkr tjd||j||dq WdS)NrzApplying zone '%s')use_transaction)r+rlenr1r7rdebug1apply_zone_settings)rrgr/rQrrr apply_zoness   zFirewallZone.apply_zonescCs|j|}||_dS)N)rrd)rr/rdr\rrrset_zone_applieds zFirewallZone.set_zone_appliedcCsd|kr dS|jd}t|dkr&dSd}x tD]}|dt|kr0|}q0W|dk r|d|jkrhdSt|dkst|dkr|dd kr|d|fSdS) N_rprerdenyallowpost)rqrrrrsrt)splitrhrr+)rchainZsplits_chainrVrrrzone_from_chains      zFirewallZone.zone_from_chaincCst|j|}|dkrdS|\}}|d kr0|}d}n4|d krB|}d}n"|d krTd}|}nttjd||j|||fS) N PREROUTING FORWARD_INrHINPUTrG POSTROUTING FORWARD_OUTz&chain '%s' can't be mapped to a policy)ryrz)r{)r|r})rxrrZ INVALID_CHAINr()rrvrVr/rwr%r&rrrpolicy_from_chains zFirewallZone.policy_from_chainc Csj|dkrf|j|}|dk rf|j|\}}|dkr:|j}n|}|jjj|d||||dkrf|jddS)Nipv4ipv6T)rr)r~r$rrZZgen_chain_rulesexecute) ripvtablervrgrVrZrw transactionrrrcreate_zone_base_by_chains  z&FirewallZone.create_zone_base_by_chaincCstj||d}|S)N)Zdatesendertimeout)time)rrrretrrrZ__gen_settingsszFirewallZone.__gen_settingscCs |j|jS)N)r>r3)rr/rrr get_settingsszFirewallZone.get_settingscCs|j|}x|D]z}xt||D]h}|dkr<|j||||q|dkr`|j|||d|d|q|dkrlqq|dkrvqtjd|||qWqW|r|j|||dS)Nr1r7rrorXrYz3Zone '%s': Unknown setting '%s:%s', unable to apply)r _interface_sourcerZwarning_icmp_block_inversion)renabler/rr3keyargsrrr_zone_settingss  zFirewallZone._zone_settingscCs|jj|}|j|}|jr dSd|_|dkr8|j}n|}x2|j|D]$}tjd|||jjj ||dqHW|j d|||dkr|j ddS)NTz+Applying policy (%s) derived from zone '%s')rg) rr<rrdr$rrrirZapply_policy_settingsrr)rr/rg_zoner\rrZrrrrjs   z FirewallZone.apply_zone_settingscCs|jj|}|j|}|js dS|dkr2|j}n|}x$|j|D]}|jjj||dqBW|jd|||dkr||j ddS)N)rgFT) rr<rrdr$rrZunapply_policy_settingsrr)rr/rgrr\rrZrrrre,s   z"FirewallZone.unapply_zone_settingscCs~|j|}|j|}g}x\tdD]P}|j|d|krZ|jtjt||j|dq"|j||j|dq"Wt|S)zH :return: exported config updated with runtime settings r) r>get_config_with_settings_dictrangeZIMPORT_EXPORT_STRUCTUREr.rMrNrOtuple)rr/r\Z conf_dictZ conf_listirrrget_config_with_settings?s  "z%FirewallZone.get_config_with_settingsc Cs|j|j}|dtkr"d|d<|j||j||j||j||j||j||j ||j ||j ||j ||j ||j|d }|jj||S)zH :return: exported config updated with runtime settings rKdefault) r?r@rDrArBr1r7 rules_strrFrCrXrY)r>Zexport_config_dictr list_services list_portslist_icmp_blocksquery_masqueradelist_forward_portsr,r- list_ruleslist_protocolslist_source_portsquery_icmp_block_inversion query_forwardrZ'combine_runtime_with_permanent_settings)rr/Z permanentZruntimerrrrOs  z*FirewallZone.get_config_with_settings_dictc sddlmdfdd }fdd}jjfjjfjjfjj fj j fj j fjjf||fjjfjjfjjfjjfd }j|}jj||\}} xv| D]n} t| | tr$xX| | D]:} t| tr || d|f| q|| d|| qWq|| d|qWx|D]} t|| trx|| D]l} | dkr|| d|| |d nDt| tr|| d|f| d|d n|| d|| d|d q\Wn6| dkr|| d||d n|| d|d|d q>WdS)Nr) Rich_Rulecsj||dd|ddS)N)rule_strr)rr)add_rule)r/rrr)rrrradd_rule_wrapperhszDFirewallZone.set_config_with_settings_dict..add_rule_wrappercsj||ddS)N)r) remove_rule)r/r)rrrrremove_rule_wrapperjszGFirewallZone.set_config_with_settings_dict..remove_rule_wrapper) r?r@rDrArBr1r7rrFrCrXrYror1r7)r)rrrX)rN)r1r7)rX)firewall.core.richr add_serviceremove_serviceadd_port remove_portadd_icmp_blockremove_icmp_blockadd_masqueraderemove_masqueradeadd_forward_portremove_forward_portr_remove_interfacer` remove_source add_protocolremove_protocoladd_source_portremove_source_portrbremove_icmp_block_inversionraremove_forwardrrZget_added_and_removed_settings isinstancelistr) rr/r3rrrZ setting_to_fnZ old_settingsZ add_settingsZremove_settingsrrr)rrrset_config_with_settings_dictesF                    z*FirewallZone.set_config_with_settings_dictcCs|jj|dS)N)rcheck_interface)rr4rrrrszFirewallZone.check_interfacecCs\|jj|}|j|}|j|}||jdkrX|jd|}d|krX|ddk rX|dSdS)Nr1r)rr<rr2r3)rr/r4r_objr5r3rrrinterface_get_senders   z!FirewallZone.interface_get_sendercCs|j||S)N)r)rr4rrrZ__interface_ids zFirewallZone.__interface_idTc Cs|jj|jj|}|j|}|j|}||jdkrLttjd||f|j |dk rjttj d|t j d||f|dkr|j } n|} |j r|r|j|| d| j|j|d|r|jd||| |j||||| j|j|||dkr| jd|S)Nr1z'%s' already bound to '%s'z'%s' already bound to a zonez&Setting zone of interface '%s' to '%s')rgFT)r check_panicr<rr2r3rrZONE_ALREADY_SETr6 ZONE_CONFLICTrrir$rdrjadd_failrlr!_FirewallZone__register_interface#_FirewallZone__unregister_interfacer) rr/r4rrgr^rrr5rrrrr_s8            zFirewallZone.add_interfacecCs6|jd||jd|<| p"|dk|jd|d<dS)Nrr1 __default__)_FirewallZone__gen_settingsr3)rrr5r/rrrrZ__register_interfacesz!FirewallZone.__register_interfacecCsR|jj|j|}|jj|}||kr,|S|dk r@|j|||j|||}|S)N)rrr6r<rr_)rr/r4r _old_zone _new_zonerrrrchange_zone_of_interfaces    z%FirewallZone.change_zone_of_interfacecCsz|jj|dkr|j}n|}|j|||jd|d|dd|dk rd|dkrd|jd|d|dd|dkrv|jddS)NT+)r.rF)rrr$rjrr)rZold_zoneZnew_zonergrrrrchange_default_zones   z FirewallZone.change_default_zonec Cs|jj|j|}|dkr,ttjd||dkr8|n |jj|}||krbttjd|||f|dkrt|j}n|}|j |}|j |}|j |j |||j d||||dkr|jd|S)Nz'%s' is not in any zonerz"remove_interface(%s, %s): zoi='%s'FT)rrr6rrZUNKNOWN_INTERFACEr<rr$rr2add_postrrr) rr/r4rgZzoirrrr5rrrrs(       zFirewallZone.remove_interfacecCs||jdkr|jd|=dS)Nr1)r3)rrr5rrrZ__unregister_interfacesz#FirewallZone.__unregister_interfacecCs|j||j|dkS)Nr1)r2r)rr/r4rrrquery_interfaceszFirewallZone.query_interfacecCs|j|djS)Nr1)rr*)rr/rrrr,"szFirewallZone.list_interfacesFcCsxt|r dSt|rdSt|r$dS|jdrh|j|dd|rV|j|dd|j|ddSttj |dS)Nrrrzipset:) rrr startswith_check_ipset_type_for_source_check_ipset_applied _ipset_familyrrZ INVALID_ADDR)rr9rdrrr check_source's zFirewallZone.check_sourcecCs|j||d}||fS)N)rd)r)rr9rdrrrrZ __source_id6szFirewallZone.__source_idc Cs|jj|jj|}|j|}t|r0|j}|j||d}||jdkr`tt j d||f|j |dk r~tt j d||dkr|j } n|} |j r|r|j|| d| j|j|d|r|jd||d|d | |j||||| j|j|||dkr| jd|S) N)rdr7z'%s' already bound to '%s'z'%s' already bound to a zone)rgFTrro)rrr<rrupperr8r3rrrr;rr$rdrjrrlr_FirewallZone__register_source _FirewallZone__unregister_sourcer) rr/r9rrgr^rrr:rrrrr`:s4        zFirewallZone.add_sourcecCs6|jd||jd|<| p"|dk|jd|d<dS)Nrr7rr)rr3)rrr:r/rrrrZ__register_sourceaszFirewallZone.__register_sourcecCsb|jj|j|}|jj|}||kr,|St|r<|j}|dk rP|j|||j|||}|S)N)rrr;r<rrrr`)rr/r9rrrrrrrchange_zone_of_sourcegs    z"FirewallZone.change_zone_of_sourcec Cs|jjt|r|j}|j|}|dkrsz-FirewallZone.list_sources..r7)rr*)rr/rrrr-szFirewallZone.list_sourcesc sxjjD]}|jsq xPj|D]B}x<jjj|D]*\}} |j|||||| |} |j|| q8Wq$Wj|d}j |dr |d kr |j |||d|d} |j|| q WxΈjjj D]}|jjj |kr|jjj |krq|jjjkrdjjj|jrd| rsz)FirewallZone._interface..cs|jjjkojjj|S)N)rrZrr)r)rrrrs)rr)renabled_backendspolicies_supportedrrZ#_get_table_chains_for_zone_dispatchZ!build_zone_source_interface_rules add_rulesr(rbuild_zone_forward_rules"get_policies_not_derived_from_zonelist_ingress_zoneslist_egress_zonesr get_policyrdrhr,r_ingress_egress_zonesr) rrr/r4rr.backendrZrrvrEr)rrrs2 $zFirewallZone._interfacecCs$|j|dkrdS|jjj|ddS)Nzhash:macF)rd) _ipset_typeripsetZ get_family)rrIrrrrszFirewallZone._ipset_familycCs|jjj|ddS)NF)rd)rrZget_type)rrIrrrrszFirewallZone._ipset_typecCsdj|g|jjj|S)N,)joinrrZ get_dimension)rrIflagrrr_ipset_match_flagsszFirewallZone._ipset_match_flagscCs|jjj|S)N)rrZ check_applied)rrIrrrrsz!FirewallZone._check_ipset_appliedcCs*|j|}|tkr&ttjd||fdS)Nz.ipset '%s' with type '%s' not usable as source)rrrrZ INVALID_IPSET)rrIZ_typerrrrs  z)FirewallZone._check_ipset_type_for_sourcec sx|rjj|gnjjD]}|js*qxNj|D]@}x:jjj|D](\}} |j|||||| } |j|| qJWq6Wj |d}j |dr|j |||d|d} |j|| qWxΈjjj D]}|jjj |kr|jjj|krq|jjjkrljjj|jrl| rDtj|dkrDjjj||dn&jjjd|||jfdd |q|r|jfd d |qWdS) NrHrYr)r9ro)rgFcs |jjjkojjjd|S)NT)rrZrr)r)rrrrsz&FirewallZone._source..cs|jjjkojjj|S)N)rrZrr)r)rrrr s)rget_backend_by_ipvrrrrZrZbuild_zone_source_address_rulesrr(rrrrrrrrdrhr-rrr) rrr/rr9rrrZrrvrEr)rrrs2"  $zFirewallZone._sourcecCs0|jj|}|j|d}|jjj|||||S)NrG)rr<r(rZr)rr/servicerrp_namerrrr s  zFirewallZone.add_servicecCs,|jj|}|j|d}|jjj|||S)NrG)rr<r(rZr)rr/rrrrrrs  zFirewallZone.remove_servicecCs(|jj|}|j|d}|jjj||S)NrG)rr<r(rZ query_service)rr/rrrrrrs  zFirewallZone.query_servicecCs&|jj|}|j|d}|jjj|S)NrG)rr<r(rZr)rr/rrrrrs  zFirewallZone.list_servicescCs2|jj|}|j|d}|jjj||||||S)NrG)rr<r(rZr)rr/portprotocolrrrrrrr#s  zFirewallZone.add_portcCs.|jj|}|j|d}|jjj||||S)NrG)rr<r(rZr)rr/rrrrrrr)s  zFirewallZone.remove_portcCs*|jj|}|j|d}|jjj|||S)NrG)rr<r(rZ query_port)rr/rrrrrrr/s  zFirewallZone.query_portcCs&|jj|}|j|d}|jjj|S)NrG)rr<r(rZr)rr/rrrrr4s  zFirewallZone.list_portscCs2|jj|}|j|d}|jjj||||||S)NrG)rr<r(rZr)rr/ source_portrrrrrrrr9s  zFirewallZone.add_source_portcCs.|jj|}|j|d}|jjj||||S)NrG)rr<r(rZr)rr/rrrrrrr?s  zFirewallZone.remove_source_portcCs*|jj|}|j|d}|jjj|||S)NrG)rr<r(rZquery_source_port)rr/rrrrrrrEs  zFirewallZone.query_source_portcCs&|jj|}|j|d}|jjj|S)NrG)rr<r(rZr)rr/rrrrrJs  zFirewallZone.list_source_portscCs|jj|}t|jtkr(|j|dgSt|jttt t gkrL|j|dgSt|jt t gkrv|j|d|j|dgSt|jt gkr|j|dgSt|jtgkr|jd|gS|jdkr|j|dgStdt|jdS)NrHrGz Rich rule type (%s) not handled.)rr<typeactionrr(elementrr r r r rr rr)rr/rSrrrrPOs    z#FirewallZone._rich_rule_to_policiescCs.x(|j||D]}|jjj||||qW|S)N)rPrrZr)rr/rSrrrrrrrbszFirewallZone.add_rulecCs*x$|j||D]}|jjj||qW|S)N)rPrrZr)rr/rSrrrrrgszFirewallZone.remove_rulecCs2d}x(|j||D]}|o(|jjj||}qW|S)NT)rPrrZ query_rule)rr/rSrrrrrrlszFirewallZone.query_rulecCs^|jj|}t}xB|j|d|j|d|jd|gD]}|jt|jjj|q6Wt|S)NrHrG)rr<setr(updaterZrr)rr/rrrrrrrs   zFirewallZone.list_rulescCs0|jj|}|j|d}|jjj|||||S)NrG)rr<r(rZr)rr/rrrrrrrr{s  zFirewallZone.add_protocolcCs,|jj|}|j|d}|jjj|||S)NrG)rr<r(rZr)rr/rrrrrrs  zFirewallZone.remove_protocolcCs(|jj|}|j|d}|jjj||S)NrG)rr<r(rZquery_protocol)rr/rrrrrr s  zFirewallZone.query_protocolcCs&|jj|}|j|d}|jjj|S)NrG)rr<r(rZr)rr/rrrrrs  zFirewallZone.list_protocolscCs.|jj|}|jd|}|jjj||||S)NrH)rr<r(rZr)rr/rrrrrrrs  zFirewallZone.add_masqueradecCs*|jj|}|jd|}|jjj||S)NrH)rr<r(rZr)rr/rrrrrs  zFirewallZone.remove_masqueradecCs&|jj|}|jd|}|jjj|S)NrH)rr<r(rZr)rr/rrrrrs  zFirewallZone.query_masqueradec Cs6|jj|}|j|d}|jjj||||||||S)NrH)rr<r(rZr) rr/rrtoporttoaddrrrrrrrrs   zFirewallZone.add_forward_portcCs2|jj|}|j|d}|jjj||||||S)NrH)rr<r(rZr)rr/rrr r rrrrrs  z FirewallZone.remove_forward_portcCs.|jj|}|j|d}|jjj|||||S)NrH)rr<r(rZquery_forward_port)rr/rrr r rrrrr s  zFirewallZone.query_forward_portcCs&|jj|}|j|d}|jjj|S)NrH)rr<r(rZr)rr/rrrrrs  zFirewallZone.list_forward_portscCsP|jj|}|j|d}|jjj|||||j|d}|jjj|||||S)NrGrH)rr<r(rZr)rr/icmprrrrrrrs    zFirewallZone.add_icmp_blockcCsH|jj|}|j|d}|jjj|||j|d}|jjj|||S)NrGrH)rr<r(rZr)rr/r rrrrrs    zFirewallZone.remove_icmp_blockcCsD|jj|}|j|d}|j|d}|jjj||oB|jjj||S)NrGrH)rr<r(rZquery_icmp_block)rr/r  p_name_host p_name_fwdrrrrs    zFirewallZone.query_icmp_blockcCsH|jj|}|j|d}|j|d}tt|jjj||jjj|S)NrGrH)rr<r(r)rrZr)rr/rrrrrrs    zFirewallZone.list_icmp_blockscCsH|jj|}|j|d}|jjj|||j|d}|jjj|||S)NrGrH)rr<r(rZrb)rr/rrrrrrbs    z%FirewallZone.add_icmp_block_inversioncCsL|jj|}|j|d}|jjj||||j|d}|jjj|||dS)NrGrH)rr<r(rZr)rrr/rrrrrrs    z"FirewallZone._icmp_block_inversioncCsD|jj|}|j|d}|jjj||j|d}|jjj||S)NrGrH)rr<r(rZr)rr/rrrrrs    z(FirewallZone.remove_icmp_block_inversioncCs@|jj|}|j|d}|j|d}|jjj|o>|jjj|S)NrGrH)rr<r(rZr)rr/rrrrrrs    z'FirewallZone.query_icmp_block_inversionc Cs|j|d}xT|j|jdD]@}x:|jjD],}|js:q.|j|||d|d}|j||q.WqWxj|j|jdD]V\}} xL|r|jj|gn|jjD],}|jsq|j|||d| d}|j||qWqtWdS)NrHr1r)r4r7)r9) r(rr3rrrrrr) rrr/rrr4rrErr9rrr_forwards "zFirewallZone._forwardcCsdS)NTr)rrrrZ __forward_idszFirewallZone.__forward_idc Cs|jj|}|jj||jj|j|}|j}||jdkrRttj d||dkrd|j }n|}|j r||j d|||j |||||j|j|||dkr|jd|S)NrYzforward already enabled in '%s'T)rr<Z check_timeoutrr_FirewallZone__forward_idr3rrZALREADY_ENABLEDr$rdr_FirewallZone__register_forwardr!_FirewallZone__unregister_forwardr) rr/rrrgrr forward_idrrrrras$       zFirewallZone.add_forwardcCs|j|||jd|<dS)NrY)rr3)rrrrrrrrZ__register_forward.szFirewallZone.__register_forwardcCs|jj|}|jj|j|}|j}||jdkrFttjd||dkrX|j }n|}|j rp|j d|||j |j |||dkr|jd|S)NrYzforward not enabled in '%s'FT)rr<rrrr3rrZ NOT_ENABLEDr$rdrrrr)rr/rgrrrrrrrr2s       zFirewallZone.remove_forwardcCs||jdkr|jd|=dS)NrY)r3)rrrrrrZ__unregister_forwardKsz!FirewallZone.__unregister_forwardcCs|j|j|dkS)NrY)rr)rr/rrrrOszFirewallZone.query_forward)N)N)N)N)NNT)N)N)N)F)F)NNT)N)N)F)rN)rN)rN)rN)rN)rN)NNrN)NN)NN)rN)N)rNN)N)e__name__ __module__ __qualname__rJrr!r#r$r(r+r0r6r;r>rTr]r[rfrkrlrxr~rrrrrjrerrrrrr2r_rrrrrrr,rr8r`rrrrrr-rrrrrrrrrrrrrrrrrrrrPrrrrrrr rrrrrrr rrrrrrbrrrrrrarrrrrrrrr#s&     8  (      &   ,(               r)"rrMZfirewall.core.baserrrZfirewall.core.fw_transactionrZfirewall.core.io.policyrZfirewall.core.loggerrrrr r r r r rrrZfirewall.functionsrrrZfirewallrZfirewall.errorsrZfirewall.fw_typesrobjectrrrrrs   ,   PK!'*#__pycache__/ebtables.cpython-36.pycnu[3 Yj$@s&dgZddlZddlmZddlmZddlmZm Z m Z ddl m Z ddl mZddlmZmZddlZd gd d d gd d dgdZiZiZiZxejD]tZgee<eee<x\eeD]PZeejdeeejdeefeejdeeejdeqWqWGdddeZdS)ebtablesN)runProg)log)tempFilereadfile splitArgs)COMMANDS) ipXtables) FirewallError INVALID_IPVZBROUTINGZ PREROUTINGZ POSTROUTINGZOUTPUTZINPUTZFORWARD)ZbrouteZnatfilterz -N %s_directz-I %s 1 -j %s_directz-I %s_direct 1 -j RETURNz %s_directc@seZdZdZdZdZddZddZddZd d Z d d Z d dZ ddZ ddZ ddZddZddZddZddZddZd/d d!Zd"d#Zd$d%Zd&d'Zd(d)Zd0d+d,Zd-d.ZdS)1rZebFcCsBt|j|_td|j|_|j|_|j|_|jg|_ dS)Nz %s-restore) ripv_command_restore_command_detect_restore_noflush_optionZrestore_noflush_option_detect_concurrent_optionconcurrent_option fill_existsavailable_tables)selfr/usr/lib/python3.6/ebtables.py__init__9s    zebtables.__init__cCs$tjj|j|_tjj|j|_dS)N)ospathexistsrZcommand_existsrZrestore_command_exists)rrrrrAszebtables.fill_existscCs(d}t|jddg}|ddkr$d}|S)Nz --concurrentz-Lr)rr)rrretrrrrEs  z"ebtables._detect_concurrent_optionc Cs.g}y|j|dWntk r(dSXdS)NoffFT) set_rules ValueError)rrulesrrrrOs z'ebtables._detect_restore_noflush_optioncCsg}|jr |j|kr |j|j|dd|D7}tjd|j|jdj|t|j|\}}|dkr~td|jdj||f|S)NcSsg|] }d|qS)z%sr).0itemrrr ^sz"ebtables.__run..z %s: %s %s rz'%s %s' failed: %s) rappendrdebug2 __class__rjoinrr )rargsZ_argsstatusrrrrZ__runYs zebtables.__runcCs(x"dD]}||krttd|qWdS)N %%REJECT%%%%ICMP%% %%LOGTYPE%%z'%s' invalid for ebtables)r,r-r.)r r )rrulestrrrr_rule_validatefs zebtables._rule_validatecCs|tko|t|kS)N)BUILT_IN_CHAINS)rr tablechainrrris_chain_builtinlszebtables.is_chain_builtincCsJg}|r4|jd|d|g|jd|d|dddgn|jd|d|g|S)Nz-tz-Nz-I1z-jZRETURNz-X)r&)raddr3r4r!rrrbuild_chain_rulesps zebtables.build_chain_rulescCs8d|g}|r |d|t|g7}n |d|g7}||7}|S)Nz-tz-Iz-D)r0)rr7r3r4indexr*r/rrr build_rule{s  zebtables.build_rulecCs tj|S)N)r Zcommon_reverse_rule)rr*rrr reverse_ruleszebtables.reverse_rulecCstj|dS)N)r Zcommon_check_passthrough)rr*rrrcheck_passthroughszebtables.check_passthroughcCs tj|S)N)r Zcommon_reverse_passthrough)rr*rrrreverse_passthroughszebtables.reverse_passthroughc Cs<t}d}i}x|D]}|dd}|j|xTdD]L}y|j|} Wntk rZYq4Xt|| dkr4|j| |j| }q4Wx^tt|D]N} xHtjD]>} | || kr|| j do|| j d rd|| || <qWqW|j |gj |qWxD|D]<}|j d|x&||D]}|j dj|d qWqW|jtj|j} tjd |j|jd |j| jfg} | j d t|j| |jd \} }tjdkrt|j}|dk rd} xH|D]@}tjd| |fddd|j d stjddd| d7} qWtj|j| dkr8td|jdj| |fdS)Nr -t--table"z"%s"z*%s r% z %s: %s %sz%s: %dz --noflush)stdinz%8d: %sr)nofmtnlr)rEz'%s %s' failed: %s)r>r?)rr1r9r lenpoprangestringZ whitespace startswithendswith setdefaultr&writer)closerstatnamerr'r(rst_sizerZgetDebugLogLevelrZdebug3unlink)rr! log_deniedZ temp_filer3Z table_rulesZ_ruler/opticrPr*r+rlineslinerrrrsZ                zebtables.set_rulescCs|j||j|S)N)r1_ebtables__run)rr/rTrrrset_rules zebtables.set_ruleNc Csg}|r|gntj}xp|D]h}||jkr6|j|qy*|jd|dg|jj||j|Wqtk rtjd|YqXqW|S)Nz-tz-Lz#ebtables table '%s' does not exist.)r2keysrr&rZr rZdebug1)rr3rZtablesrrrget_available_tabless    zebtables.get_available_tablescCsiS)Nr)rr3rrrget_zone_table_chainsszebtables.get_zone_table_chainscCsFg}xs.     PK!Ѩ=II*__pycache__/fw_policy.cpython-36.opt-1.pycnu[3 Yj=V@sddlZddlZddlmZddlmZmZmZmZm Z m Z m Z m Z m Z mZddlmZmZmZmZmZmZmZmZmZmZmZddlmZddlmZddlm Z ddl!m"Z"dd l#m$Z$Gd d d e%Z&dS) N)log) portStr checkIPnMask checkIP6nMask checkProtocolenable_ip_forwardingcheck_single_addressportInPortRangeget_nf_conntrack_short_namecoalescePortRangebreakPortRange) Rich_Rule Rich_Accept Rich_Service Rich_Port Rich_ProtocolRich_MasqueradeRich_ForwardPortRich_SourcePortRich_IcmpBlock Rich_IcmpType Rich_Mark)FirewallTransaction)errors) FirewallError)LastUpdatedOrderedDict)SOURCE_IPSET_TYPESc@seZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ ddZ ddZ d ddZddZddZddZd d d!Zd d"d#Zdd$d%Zd&d'Zd(d)Zd*d+Zd,d-Zdd0d1Zd2d3Zdd4d5Zd6d7Zd8d9Zd:d;Zdd?Z dd@dAZ!dBdCZ"ddDdEZ#dFdGZ$dHdIZ%dJdKZ&dLdMZ'dNdOZ(dPdQZ)dRdSZ*ddTdUZ+dVdWZ,ddXdYZ-dZd[Z.d\d]Z/d^d_Z0d`daZ1dbdcZ2ddddeZ3dfdgZ4ddhdiZ5djdkZ6dldmZ7dndoZ8dpdqZ9drdsZ:dtduZ;dvdwZ<ddxdyZ=dzd{Z>dd|d}Z?d~dZ@ddZAddZBddZCddZDdddZEddZFdddZGddZHddZIddZJddZKdddZLddZMdddZNddZOddZPddZQddZRdddZSddZTdddZUddZVddZWdddZXd ddZYd!ddZZddZ[d"ddZ\ddZ]d#ddZ^ddZ_ddZ`ddZad$ddÄZbddńZcd%ddDŽZdddɄZedd˄Zfdd̈́ZgddτZhd&ddфZiddӄZjddՄZkd'ddׄZlddلZmddۄZndd݄Zodd߄ZpddZqddZrddZsddZtddZud(ddZvd)ddZwddZxddZyddZzddZ{d*ddZ|ddZ}ddZ~ddZddZddZddZddZddZd+d d ZdS(,FirewallPolicycCs||_i|_i|_dS)N)_fw_chains _policies)selffwr#/usr/lib/python3.6/fw_policy.py__init__szFirewallPolicy.__init__cCsd|j|j|jfS)Nz %s(%r, %r)) __class__rr )r!r#r#r$__repr__szFirewallPolicy.__repr__cCs|jj|jjdS)N)rclearr )r!r#r#r$cleanups zFirewallPolicy.cleanupcCs t|jS)N)rr)r!r#r#r$new_transaction$szFirewallPolicy.new_transactioncCst|jjS)N)sortedr keys)r!r#r#r$ get_policies)szFirewallPolicy.get_policiescCs8g}x*|jD]}|j|}|js|j|qWt|S)N)r- get_policyderived_from_zoneappendr+)r!Zpoliciespp_objr#r#r$"get_policies_not_derived_from_zone,s  z1FirewallPolicy.get_policies_not_derived_from_zonecCs~g}xt|jD]h}|j|}t|dt|jjjtddgB@rt|dt|jjjtddgB@r|j|qW|S)N ingress_zonesHOSTANY egress_zones)r3 get_settingssetrzoneZget_active_zonesr0)r!Zactive_policiespolicysettingsr#r#r$)get_active_policies_not_derived_from_zone4s ((z8FirewallPolicy.get_active_policies_not_derived_from_zonecCs|jj|}|j|S)N)r check_policyr )r!r;r1r#r#r$r.>s zFirewallPolicy.get_policyc Cs,dddD|_||j|j<|j|jdS)NcSsi|] }t|qSr#)r).0xr#r#r$ Csz-FirewallPolicy.add_policy..servicesports masquerade forward_ports source_ports icmp_blocksrules protocolsicmp_block_inversionr4r7) rBrCrDrErFrGrHrIrJr4r7)r<r namecopy_permanent_to_runtime)r!objr#r#r$ add_policyBs  zFirewallPolicy.add_policycCs0|j|}|jr|j||jj|j|=dS)N)r appliedunapply_policy_settingsr<r()r!r;rMr#r#r$ remove_policyNs    zFirewallPolicy.remove_policycCs|j|}|jrdSx|jD]}|j||ddqWx|jD]}|j||ddqr rOr*r/%_get_table_chains_for_policy_dispatch#_get_table_chains_for_zone_dispatchgen_chain_rulesr8_ingress_egress_zones _icmp_block _forward_port_service_port _protocol _source_port _masquerade_FirewallPolicy__ruler rr[execute) r!enabler;rb_policyrM transactiontablechainr<keyr`r#r#r$_policy_settingssj                zFirewallPolicy._policy_settingscCs|jd||ddS)NT)rb)r)r!r;rbr#r#r$rcsz$FirewallPolicy.apply_policy_settingscCs|jd||ddS)NF)rb)r)r!r;rbr#r#r$rPsz&FirewallPolicy.unapply_policy_settingsc Csr|j|j}|j||j||j||j||j||j||j||j ||j ||j |d }|j j ||S)zH :return: exported config updated with runtime settings ) rBrCrGrDrE rich_rulesrIrFr4r7)r.Zexport_config_dict list_services list_portslist_icmp_blocksquery_masqueradelist_forward_ports list_ruleslist_protocolslist_source_portslist_ingress_zoneslist_egress_zonesrZ'combine_runtime_with_permanent_settings)r!r;Z permanentZruntimer#r#r$get_config_with_settings_dictsz,FirewallPolicy.get_config_with_settings_dictc sddlmd fdd }fdd}jjfjjfjjfjj fj j f||fj j fjjfjjfjjfd }j|}jj||\}} xt| D]l} t| | trxV| | D]8} t| tr|| d|f| q|| d|| qWq|| d|qWx|D]} t|| trxn|| D]J} t| trv|| d|f| d|d n|| d|| d|d qFWn|| d|d|d q(WdS) Nr)r csj||dd|ddS)N)rkr)rgrf)r^)r;rkrgrf)r r!r#r$add_rule_wrapperszFFirewallPolicy.set_config_with_settings_dict..add_rule_wrappercsj||ddS)N)rk) remove_rule)r;rk)r r!r#r$remove_rule_wrapperszIFirewallPolicy.set_config_with_settings_dict..remove_rule_wrapper) rBrCrGrDrErrIrFr4r7rj)rgrf)rN)firewall.core.richr rWremove_servicerX remove_portrUremove_icmp_blockr_remove_masqueraderVremove_forward_portr\remove_protocolr]remove_source_portrSremove_ingress_zonerTremove_egress_zonerrZget_added_and_removed_settings isinstancelisttuple) r!r;r<rfrrZ setting_to_fnZ old_settingsZ add_settingsZremove_settingsr~r`r#)r r!r$set_config_with_settings_dicts:                z,FirewallPolicy.set_config_with_settings_dictcCs&|sttj|dkr"|jj|dS)Nr5r6)r5r6)rr INVALID_ZONEr check_zone)r!r:r#r#r$check_ingress_zones z!FirewallPolicy.check_ingress_zonecCs|j||S)N)r)r!r:r#r#r$Z__ingress_zone_id"s z FirewallPolicy.__ingress_zone_idrTc Cs|jj|}|jj||jj|j|}|j|} | |jdkrXttj d||fd|jdksd|jdks|d kr|jdrttj d|dkrd|jdkrttj d|dkr|j } n|} |rJ|j r|j d|| |j|| ||| j|j|| |j s:||jkrH|j|| d | j|j|dn|j d || n |j|| ||| j|j|| |dkr~| jd dS) Nr4z'%s' already in '%s'r6r5zI'ingress-zones' may only contain one of: many regular zones, ANY, or HOSTr7zF'HOST' can only appear in either ingress or egress zones, but not bothF)rbT)r6r5)rr> check_timeout check_panicr _FirewallPolicy__ingress_zone_idr<rrrZrr*rOro&_FirewallPolicy__register_ingress_zoneadd_fail(_FirewallPolicy__unregister_ingress_zoner=rcrerx) r!r;r:rgrfrbrRrz_objzone_idr{r#r#r$rS&s<         zFirewallPolicy.add_ingress_zonecCs|j|||jd|<dS)Nr4)_FirewallPolicy__gen_settingsr<)r!rrrgrfr#r#r$Z__register_ingress_zoneSsz&FirewallPolicy.__register_ingress_zonecCs|jj|}|jj|j|}|j|}||jdkrLttjd||f|dkr^|j }n|}|j rt |jddkr|j ||n|j d|||j|||j|j||dd||jkr|j d||n|j|j|||dkr|jd|S)Nr4z'%s' not in '%s'rjFT)rr>rr rr<rr NOT_ENABLEDr*rOlenrProrrrr=add_postrx)r!r;r:rbrzrrr{r#r#r$rVs,        z"FirewallPolicy.remove_ingress_zonecCs||jdkr|jd|=dS)Nr4)r<)r!rrr#r#r$Z__unregister_ingress_zoneysz(FirewallPolicy.__unregister_ingress_zonecCs|j||j|dkS)Nr4)rr8)r!r;r:r#r#r$query_ingress_zone}sz!FirewallPolicy.query_ingress_zonecCst|j|djS)Nr4)rr8r,)r!r;r#r#r$rsz!FirewallPolicy.list_ingress_zonescCs&|sttj|dkr"|jj|dS)Nr5r6)r5r6)rrrrr)r!r:r#r#r$check_egress_zones z FirewallPolicy.check_egress_zonecCs|j||S)N)r)r!r:r#r#r$Z__egress_zone_ids zFirewallPolicy.__egress_zone_idc Cs|jj|}|jj||jj|j|}|j|} | |jdkrXttj d||fd|jdksd|jdks|d kr|jdrttj d|dkrd|jdkrttj d|dkr|j } n|} |rJ|j r|j d|| |j|| ||| j|j|| |j s:||jkrH|j|| d | j|j|dn|j d || n |j|| ||| j|j|| |dkr~| jd dS) Nr7z'%s' already in '%s'r6r5zH'egress-zones' may only contain one of: many regular zones, ANY, or HOSTr4zF'HOST' can only appear in either ingress or egress zones, but not bothF)rbT)r6r5)rr>rrr _FirewallPolicy__egress_zone_idr<rrrZrr*rOro%_FirewallPolicy__register_egress_zoner'_FirewallPolicy__unregister_egress_zoner=rcrerx) r!r;r:rgrfrbrRrzrrr{r#r#r$rTs<         zFirewallPolicy.add_egress_zonecCs|j|||jd|<dS)Nr7)rr<)r!rrrgrfr#r#r$Z__register_egress_zonesz%FirewallPolicy.__register_egress_zonecCs|jj|}|jj|j|}|j|}||jdkrLttjd||f|dkr^|j }n|}|j rt |jddkr|j ||n|j d|||j|||j|j||dd||jkr|j d||n|j|j|||dkr|jd|S)Nr7z'%s' not in '%s'rjFT)rr>rr rr<rrrr*rOrrProrrrr=rrx)r!r;r:rbrzrrr{r#r#r$rs,        z!FirewallPolicy.remove_egress_zonecCs||jdkr|jd|=dS)Nr7)r<)r!rrr#r#r$Z__unregister_egress_zonesz'FirewallPolicy.__unregister_egress_zonecCs|j||j|dkS)Nr7)rr8)r!r;r:r#r#r$query_egress_zonesz FirewallPolicy.query_egress_zonecCst|j|djS)Nr7)rr8r,)r!r;r#r#r$rsz FirewallPolicy.list_egress_zonescCs |jdS)N)Zcheck)r!ruler#r#r$ check_ruleszFirewallPolicy.check_rulecCs|j|t|S)N)rstr)r!rr#r#r$Z __rule_ids zFirewallPolicy.__rule_idcCsx|sdS|jr,t|jrdSt|jrtdSnHt|dr@|jr@dSt|drt|jrt|j|j|j|j|j|jSdS)Nipv4ipv6macipset) Zaddrrrhasattrrr_check_ipset_type_for_source_check_ipset_applied _ipset_family)r!sourcer#r#r$_rule_source_ipvs     zFirewallPolicy._rule_source_ipvcCs|j||||dS)N) _rule_prepare)r!ryr;rr{r#r#r$Z__ruleszFirewallPolicy.__rulec CsL|jj|}|jj||jj|j|}|j|}||jdkrh|jrP|jn|} tt j d|| f|js|j rt |j t rd|jdkrtt jdd|jdkrtt jdx6|jdD](} | dkrq|jjj| rtt jd qW|j rt |j trd|jdkr,|j jrtt jd nb|jdr|j jsNtt jd x>|jdD]0} | dkrlqZ|jjj| rZtt jd qZW|jrt |jtrx>|jdD]0} | dkrq|jjj| rtt jd qW|dkr|j} n|} |jr|jd||| |j||||| j|j|||dkrH| jd|S)NrHz'%s' already in '%s'r5r7z.'masquerade' is invalid for egress zone 'HOST'r4z/'masquerade' is invalid for ingress zone 'HOST'r6zR'masquerade' cannot be used in a policy if an ingress zone has assigned interfaceszAA 'forward-port' with 'to-addr' is invalid for egress zone 'HOST'zC'forward-port' requires 'to-addr' if egress zone is 'ANY' or a zonezS'forward-port' cannot be used in a policy if an egress zone has assigned interfaceszR'mark' action cannot be used in a policy if an egress zone has assigned interfacesT)r6r5)rr>rrr _FirewallPolicy__rule_idr<r/rrrZelementrrrr:list_interfacesr to_addressINVALID_FORWARDactionrr*rOrw_FirewallPolicy__register_ruler _FirewallPolicy__unregister_rulerx) r!r;rrgrfrbrzrrule_id_namer:r{r#r#r$r^ s`                 zFirewallPolicy.add_rulecCs|j|||jd|<dS)NrH)rr<)r!rrrgrfr#r#r$Z__register_ruleEszFirewallPolicy.__register_rulec Cs|jj|}|jj|j|}|j|}||jdkr\|jrD|jn|}ttj d||f|dkrn|j }n|}|j r|j d||||j |j|||dkr|jd|S)NrHz'%s' not in '%s'FT)rr>rr rr<r/rrrr*rOrwrrrx) r!r;rrbrzrrrr{r#r#r$rIs"      zFirewallPolicy.remove_rulecCs||jdkr|jd|=dS)NrH)r<)r!rrr#r#r$Z__unregister_ruledsz FirewallPolicy.__unregister_rulecCs|j||j|dkS)NrH)rr8)r!r;rr#r#r$ query_rulehszFirewallPolicy.query_rulecCst|j|djS)NrH)rr8r,)r!r;r#r#r$rkszFirewallPolicy.list_rulescCs|jj|dS)N)r check_service)r!servicer#r#r$rpszFirewallPolicy.check_servicecCs|j||S)N)r)r!rr#r#r$Z __service_idss zFirewallPolicy.__service_idc Cs|jj|}|jj||jj|j|}|j|}||jdkrh|jrP|jn|} tt j d|| f|dkrz|j } n|} |j r|j d||| |j||||| j|j|||dkr| jd|S)NrBz'%s' already in '%s'T)rr>rrr _FirewallPolicy__service_idr<r/rrrZr*rOrr!_FirewallPolicy__register_servicer#_FirewallPolicy__unregister_servicerx) r!r;rrgrfrbrzr service_idrr{r#r#r$rWws&       zFirewallPolicy.add_servicecCs|j|||jd|<dS)NrB)rr<)r!rrrgrfr#r#r$Z__register_servicesz!FirewallPolicy.__register_servicec Cs|jj|}|jj|j|}|j|}||jdkr\|jrD|jn|}ttj d||f|dkrn|j }n|}|j r|j d||||j |j|||dkr|jd|S)NrBz'%s' not in '%s'FT)rr>rr rr<r/rrrr*rOrrrrrx) r!r;rrbrzrrrr{r#r#r$rs"      zFirewallPolicy.remove_servicecCs||jdkr|jd|=dS)NrB)r<)r!rrr#r#r$Z__unregister_servicesz#FirewallPolicy.__unregister_servicecCs|j||j|dkS)NrB)rr8)r!r;rr#r#r$ query_serviceszFirewallPolicy.query_servicecCs|j|djS)NrB)r8r,)r!r;r#r#r$rszFirewallPolicy.list_servicesc CsTg}xJ|D]B}y|jjj|}Wn tk r@ttj|YnX|j|q W|S)N)rhelper get_helperrrINVALID_HELPERr0)r!helpers_helpersr_helperr#r#r$get_helpers_for_service_helperss z.FirewallPolicy.get_helpers_for_service_helperscCsg}x|D]}y|jjj|}Wn tk r@ttj|YnXt|jdkrt|j }y|jjj|}|j |Wqtk r|rt j d|w YqXq |j |q W|S)NrjzHelper '%s' is not available) rrrrrrrrCr moduler0rr[)r!modulesryrrr_module_short_namerr#r#r$get_helpers_for_service_moduless"   z.FirewallPolicy.get_helpers_for_service_modulescCs|jj||jj|dS)N)r check_port check_tcpudp)r!portprotocolr#r#r$rs zFirewallPolicy.check_portcCs|j||t|d|fS)N-)rr)r!rrr#r#r$Z __port_ids zFirewallPolicy.__port_idcs|jj|}|jj||jj|j|}ttfdd|jd} x@| D]8} t|| drN|j rl|j n|} t t j d|| fqNWt |dd| D\} } |dkr|j}n|}|jr x$| D]}|jd|t|d |qWx$| D]}|jd |t|d |qWx:| D]2}|j|} |j|| |||j|j|| qWx*| D]"}|j|} |j|j|| qNW|dkr|jd|S) Ncs |dkS)Nrjr#)r@)rr#r$sz)FirewallPolicy.add_port..rCrz'%s:%s' already in '%s'cSsg|] \}}|qSr#r#)r?rsrtr#r#r$ sz+FirewallPolicy.add_port..TrF)rr>rrr rfilterr<r r/rrrZr r*rOrsr_FirewallPolicy__port_id_FirewallPolicy__register_portr _FirewallPolicy__unregister_portrrx)r!r;rrrgrfrbrzrexisting_port_idsport_idr added_rangesremoved_rangesr{ranger#)rr$rXs:              zFirewallPolicy.add_portcCs|j|||jd|<dS)NrC)rr<)r!rrrgrfr#r#r$Z__register_portszFirewallPolicy.__register_portcs|jj|}|jj|j|}ttfdd|jd}xB|D]}t||drBPqBW|jrf|jn|} t t j d|| ft |dd|D\} } |dkr|j } n|} |jrx$| D]} |jd|t| d | qWx$| D]} |jd |t| d | qWx:| D]2} |j| }|j||dd| j|j||qWx*| D]"} |j| }| j|j||qDW|dkr~| jd|S) Ncs |dkS)Nrjr#)r@)rr#r$rsz,FirewallPolicy.remove_port..rCrz'%s:%s' not in '%s'cSsg|] \}}|qSr#r#)r?rsrtr#r#r$r#sz.FirewallPolicy.remove_port..TrF)rr>rr rrr<r r/rrrr r*rOrsrrrrrrrx)r!r;rrrbrzrrrrrrr{rr#)rr$rs:             zFirewallPolicy.remove_portcCs||jdkr|jd|=dS)NrC)r<)r!rrr#r#r$Z__unregister_port=sz FirewallPolicy.__unregister_portcCs6x0|j|dD]\}}t||r||krdSqWdS)NrCTF)r8r )r!r;rrrsrtr#r#r$ query_portAszFirewallPolicy.query_portcCst|j|djS)NrC)rr8r,)r!r;r#r#r$rHszFirewallPolicy.list_portscCst|sttj|dS)N)rrrZINVALID_PROTOCOL)r!rr#r#r$check_protocolMszFirewallPolicy.check_protocolcCs|j||S)N)r)r!rr#r#r$Z __protocol_idQs zFirewallPolicy.__protocol_idc Cs|jj|}|jj||jj|j|}|j|}||jdkrh|jrP|jn|} tt j d|| f|dkrz|j } n|} |j r|j d||| |j||||| j|j|||dkr| jd|S)NrIz'%s' already in '%s'T)rr>rrr _FirewallPolicy__protocol_idr<r/rrrZr*rOrt"_FirewallPolicy__register_protocolr$_FirewallPolicy__unregister_protocolrx) r!r;rrgrfrbrzr protocol_idrr{r#r#r$r\Us&       zFirewallPolicy.add_protocolcCs|j|||jd|<dS)NrI)rr<)r!rrrgrfr#r#r$Z__register_protocolrsz"FirewallPolicy.__register_protocolc Cs|jj|}|jj|j|}|j|}||jdkr\|jrD|jn|}ttj d||f|dkrn|j }n|}|j r|j d||||j |j|||dkr|jd|S)NrIz'%s' not in '%s'FT)rr>rr rr<r/rrrr*rOrtrrrx) r!r;rrbrzrrrr{r#r#r$rvs$       zFirewallPolicy.remove_protocolcCs||jdkr|jd|=dS)NrI)r<)r!rrr#r#r$Z__unregister_protocolsz$FirewallPolicy.__unregister_protocolcCs|j||j|dkS)NrI)rr8)r!r;rr#r#r$query_protocolszFirewallPolicy.query_protocolcCst|j|djS)NrI)rr8r,)r!r;r#r#r$rszFirewallPolicy.list_protocolscCs|j||t|d|fS)Nr)rr)r!rrr#r#r$Z__source_port_ids zFirewallPolicy.__source_port_idcs|jj|}|jj||jj|j|}ttfdd|jd} x@| D]8} t|| drN|j rl|j n|} t t j d|| fqNWt |dd| D\} } |dkr|j}n|}|jr x$| D]}|jd|t|d |qWx$| D]}|jd |t|d |qWx:| D]2}|j|} |j|| |||j|j|| qWx*| D]"}|j|} |j|j|| qNW|dkr|jd|S) Ncs |dkS)Nrjr#)r@)rr#r$rsz0FirewallPolicy.add_source_port..rFrz'%s:%s' already in '%s'cSsg|] \}}|qSr#r#)r?rsrtr#r#r$rsz2FirewallPolicy.add_source_port..TrF)rr>rrr rrr<r r/rrrZr r*rOrur_FirewallPolicy__source_port_id%_FirewallPolicy__register_source_portr'_FirewallPolicy__unregister_source_portrrx)r!r;rrrgrfrbrzrrrrrrr{rr#)rr$r]s:              zFirewallPolicy.add_source_portcCs|j|||jd|<dS)NrF)rr<)r!rrrgrfr#r#r$Z__register_source_portsz%FirewallPolicy.__register_source_portcs|jj|}|jj|j|}ttfdd|jd}xB|D]}t||drBPqBW|jrf|jn|} t t j d|| ft |dd|D\} } |dkr|j } n|} |jrx$| D]} |jd|t| d | qWx$| D]} |jd |t| d | qWx:| D]2} |j| }|j||dd| j|j||qWx*| D]"} |j| }| j|j||qDW|dkr~| jd|S) Ncs |dkS)Nrjr#)r@)rr#r$rsz3FirewallPolicy.remove_source_port..rFrz'%s:%s' not in '%s'cSsg|] \}}|qSr#r#)r?rsrtr#r#r$rsz5FirewallPolicy.remove_source_port..TrF)rr>rr rrr<r r/rrrr r*rOrurrrrrrrx)r!r;rrrbrzrrrrrrr{rr#)rr$rs:             z!FirewallPolicy.remove_source_portcCs||jdkr|jd|=dS)NrF)r<)r!rrr#r#r$Z__unregister_source_portsz'FirewallPolicy.__unregister_source_portcCs6x0|j|dD]\}}t||r||krdSqWdS)NrFTF)r8r )r!r;rrrsrtr#r#r$query_source_portsz FirewallPolicy.query_source_portcCst|j|djS)NrF)rr8r,)r!r;r#r#r$rsz FirewallPolicy.list_source_portscCsdS)NTr#)r!r#r#r$Z__masquerade_idszFirewallPolicy.__masquerade_idc Cs8|jj|}|jj||jj|j|}|j}||jdkrb|jrN|jn|}tt j d||jsd|jdkrtt j dd|jdkrtt j dx6|jdD](} | dkrq|jj j | rtt j d qW|dkr|j} n|} |jr|jd || |j||||| j|j|||dkr4| jd |S) NrDz"masquerade already enabled in '%s'r5r7z.'masquerade' is invalid for egress zone 'HOST'r4z/'masquerade' is invalid for ingress zone 'HOST'r6zR'masquerade' cannot be used in a policy if an ingress zone has assigned interfacesT)rr>rrr _FirewallPolicy__masquerade_idr<r/rrrZrr:rr*rOrv$_FirewallPolicy__register_masquerader&_FirewallPolicy__unregister_masqueraderx) r!r;rgrfrbrzr masquerade_idrr:r{r#r#r$r_ s:          zFirewallPolicy.add_masqueradecCs|j|||jd|<dS)NrD)rr<)r!rrrgrfr#r#r$Z__register_masquerade2sz$FirewallPolicy.__register_masqueradecCs|jj|}|jj|j|}|j}||jdkrV|jrB|jn|}ttj d||dkrh|j }n|}|j r|j d|||j |j|||dkr|jd|S)NrDzmasquerade not enabled in '%s'FT)rr>rr rr<r/rrrr*rOrvrrrx)r!r;rbrzrrrr{r#r#r$r6s"      z FirewallPolicy.remove_masqueradecCs||jdkr|jd|=dS)NrD)r<)r!rrr#r#r$Z__unregister_masqueradePsz&FirewallPolicy.__unregister_masqueradecCs|j|j|dkS)NrD)rr8)r!r;r#r#r$rTszFirewallPolicy.query_masqueradecCs^|jj||jj||r(|jj||rBt||sBttj|| rZ| rZttjddS)Nz.port-forwarding is missing to-port AND to-addr)rrrrrrZ INVALID_ADDRr)r!ipvrrtoporttoaddrr#r#r$check_forward_portYs      z!FirewallPolicy.check_forward_portcCsLtd|r|jd||||n|jd||||t|d|t|dt|fS)Nrrr)rrrr)r!rrrrr#r#r$Z__forward_port_idfs   z FirewallPolicy.__forward_port_idc CsZ|jj|} |jj||jj|j| } |j||||} | | jdkrt| jrV| jn| } tt j d||||| f| jsd| jdkr|rtt j dnR| jdr|stt j dx6| jdD](} | dkrq|jj j | rtt jdqW|dkr|j}n|}| jr"|jd | ||||||j| | |||j|j| | |dkrV|jd | S) NrEz'%s:%s:%s:%s' already in '%s'r5r7zAA 'forward-port' with 'to-addr' is invalid for egress zone 'HOST'zC'forward-port' requires 'to-addr' if egress zone is 'ANY' or a zoner6zS'forward-port' cannot be used in a policy if an egress zone has assigned interfacesT)rr>rrr _FirewallPolicy__forward_port_idr<r/rrrZrr:rrr*rOrq&_FirewallPolicy__register_forward_portr(_FirewallPolicy__unregister_forward_portrx)r!r;rrrrrgrfrbrzr forward_idrr:r{r#r#r$rVnsB          zFirewallPolicy.add_forward_portcCs|j|||jd|<dS)NrE)rr<)r!rrrgrfr#r#r$Z__register_forward_portsz&FirewallPolicy.__register_forward_portc Cs|jj|}|jj|j|}|j||||} | |jdkrh|jrJ|jn|} ttj d||||| f|dkrz|j } n|} |j r|j d|| ||||| j |j|| |dkr| jd|S)NrEz'%s:%s:%s:%s' not in '%s'FT)rr>rr rr<r/rrrr*rOrqrrrx) r!r;rrrrrbrzrrrr{r#r#r$rs&     z"FirewallPolicy.remove_forward_portcCs||jdkr|jd|=dS)NrE)r<)r!rrr#r#r$Z__unregister_forward_portsz(FirewallPolicy.__unregister_forward_portcCs"|j||||}||j|dkS)NrE)rr8)r!r;rrrrrr#r#r$query_forward_portsz!FirewallPolicy.query_forward_portcCst|j|djS)NrE)rr8r,)r!r;r#r#r$rsz!FirewallPolicy.list_forward_portscCs|jj|dS)N)rZcheck_icmptype)r!icmpr#r#r$check_icmp_blockszFirewallPolicy.check_icmp_blockcCs|j||S)N)r)r!rr#r#r$Z__icmp_block_ids zFirewallPolicy.__icmp_block_idc Cs|jj|}|jj||jj|j|}|j|}||jdkrh|jrP|jn|} tt j d|| f|dkrz|j } n|} |j r|j d||| |j||||| j|j|||dkr| jd|S)NrGz'%s' already in '%s'T)rr>rrr _FirewallPolicy__icmp_block_idr<r/rrrZr*rOrp$_FirewallPolicy__register_icmp_blockr&_FirewallPolicy__unregister_icmp_blockrx) r!r;rrgrfrbrzricmp_idrr{r#r#r$rUs&       zFirewallPolicy.add_icmp_blockcCs|j|||jd|<dS)NrG)rr<)r!rr rgrfr#r#r$Z__register_icmp_blocksz$FirewallPolicy.__register_icmp_blockc Cs|jj|}|jj|j|}|j|}||jdkr\|jrD|jn|}ttj d||f|dkrn|j }n|}|j r|j d||||j |j|||dkr|jd|S)NrGz'%s' not in '%s'FT)rr>rr rr<r/rrrr*rOrprr rx) r!r;rrbrzrr rr{r#r#r$rs"      z FirewallPolicy.remove_icmp_blockcCs||jdkr|jd|=dS)NrG)r<)r!rr r#r#r$Z__unregister_icmp_block sz&FirewallPolicy.__unregister_icmp_blockcCs|j||j|dkS)NrG)rr8)r!r;rr#r#r$query_icmp_blockszFirewallPolicy.query_icmp_blockcCs|j|djS)NrG)r8r,)r!r;r#r#r$rszFirewallPolicy.list_icmp_blockscCsdS)NTr#)r!r#r#r$Z__icmp_block_inversion_idsz(FirewallPolicy.__icmp_block_inversion_idc Cs|jj|}|jj|j|}|j}||jdkrV|jrB|jn|}ttj d||dkrh|j }n|}|j rx&|j |dD]} |j d|| |qW|jd|||j||||j|j||||j rx&|j |dD]} |j d|| |qW|jd|||dkr|jd|S)NrJz,icmp-block-inversion already enabled in '%s'rGFT)rr>rr (_FirewallPolicy__icmp_block_inversion_idr<r/rrrZr*rOr8rp_icmp_block_inversion._FirewallPolicy__register_icmp_block_inversionr*_FirewallPolicy__undo_icmp_block_inversionrx) r!r;rfrbrzricmp_block_inversion_idrr{r`r#r#r$add_icmp_block_inversions6        z'FirewallPolicy.add_icmp_block_inversioncCs|jd||jd|<dS)NrrJ)rr<)r!rrrfr#r#r$Z__register_icmp_block_inversionEsz.FirewallPolicy.__register_icmp_block_inversioncCs|j}|jr6x&|j|dD]}|jd|||qW||jdkrP|jd|=|jr~x&|j|dD]}|jd|||qfW|jddS)NrGFrJT)r*rOr8rpr<rx)r!rzrrr{r`r#r#r$Z__undo_icmp_block_inversionJs z*FirewallPolicy.__undo_icmp_block_inversionc Cs|jj|}|jj|j|}|j}||jdkrV|jrB|jn|}ttj d||dkrh|j }n|}|j rx&|j |dD]}|j d|||qW|jd|||j|||j|j||d|j rx&|j |dD]}|j d|||qW|jd|||dkr|jd|S)NrJz(icmp-block-inversion not enabled in '%s'rGFT)rr>rr r r<r/rrrr*rOr8rpr0_FirewallPolicy__unregister_icmp_block_inversionrrrx) r!r;rbrzrrrr{r`r#r#r$remove_icmp_block_inversion\s6        z*FirewallPolicy.remove_icmp_block_inversioncCs||jdkr|jd|=dS)NrJ)r<)r!rrr#r#r$Z!__unregister_icmp_block_inversionsz0FirewallPolicy.__unregister_icmp_block_inversioncCs|j|j|dkS)NrJ)r r8)r!r;r#r#r$query_icmp_block_inversionsz)FirewallPolicy.query_icmp_block_inversionc Cs|jjj|}|jr*|jjj|jd}n|}|rT||jkrt||f|j|krtdSn ||jksp||f|j|krtdSx@|jjD]2}|jr||j kr|j ||||} |j || qW|j ||||fg|j |j || ||fgdS)Nr)rr;r.r/r:Z_zone_policiesrenabled_backendspolicies_supportedZget_available_tablesZbuild_policy_chain_rules add_rules_register_chainsr) r!r;creater|r}r{rMZtracking_policybackendrHr#r#r$rns$   zFirewallPolicy.gen_chain_rulescCsbx\|D]T\}}|r,|jj|gj||fq|j|j||ft|j|dkr|j|=qWdS)Nr)r setdefaultr0remover)r!r;rZtablesr|r}r#r#r$rs zFirewallPolicy._register_chainscCs$|jjj|dkrdS|jjj|S)Nzhash:mac)rrget_typeZ get_family)r!rKr#r#r$rszFirewallPolicy._ipset_familycCs|jjj|S)N)rrr)r!rKr#r#r$Z __ipset_typeszFirewallPolicy.__ipset_typecCsdj|g|jjj|S)N,)joinrrZ get_dimension)r!rKflagr#r#r$_ipset_match_flagssz!FirewallPolicy._ipset_match_flagscCs|jjj|S)N)rrZ check_applied)r!rKr#r#r$rsz#FirewallPolicy._check_ipset_appliedcCs*|j|}|tkr&ttjd||fdS)Nz.ipset '%s' with type '%s' not usable as source)_FirewallPolicy__ipset_typerrrZ INVALID_IPSET)r!rKZ_typer#r#r$rs  z+FirewallPolicy._check_ipset_type_for_sourcec st|jtkrjjj|jj}|dkr2|jjg}xR|jD]H}||krHq:j||j |t j |}||j_j |||||dq:Wg} |j r|j g} nH|jrt|jtst|jtrjjj|jjjrfdddD} j|j} | r&|j r |j | kr&ttjd| |j fn| g} | s4ddg} fdd| D} | |_x2tfdd| DD]} t|jtkrjjj|jj}g} t|jd kr|jrttjd xB| D].} | |jkr| j| r| j |j| qWn | j dx~| D]}t|jtkrj|j |}|j!|j"7}t#t|d d d }g}x|D]}|j$}t%|}|j&dd}|j ||j dkr| j|j  rqTt|j'dkr|j |n:x8|j'D].\}}| j(||||||j|}|j)| |qWqTW|j*|x4|j'D]*\}}| j+||||||}|j)| |q Wx.|j,D]$}| j-|||||}|j)| |q@Wx4|j.D]*\}}| j/||||||}|j)| |qpWqWqft|jt0kr|jj1}|jj2}j3||| j+||||d|}|j)| |qft|jt4kr<|jj5}j6|| j-|||d|}|j)| |qft|jt7kr|rzx&| D]} | j| rX|j8t9| qXW| j:|||}|j)| |qft|jt;kr4|jj1}|jj2}|jj<}|jj=}xD| D]<} | j| rj>| |||||r|r|j8t9| qW| j?|||||||}|j)| |qft|jt@kr|jj1}|jj2}j3||| j/||||d|}|j)| |nt|jtkst|jtkr>jjj|jj|j rjr|j jkrttjAd|j |jjft|jtkr |jr t|jtkr ttjd| jB|||}|j)| |n>|jdkrf| jC|||}|j)| |nttjdt|jqfWdS)N)included_servicescsg|]}|jkr|qSr#) destination)r?r)ictr#r$rsz0FirewallPolicy._rule_prepare..rrz;Source address family '%s' conflicts with rule family '%s'.csg|]}jj|r|qSr#)ris_ipv_enabled)r?r)r!r#r$rscsg|]}jj|qSr#)rget_backend_by_ipv)r?r@)r!r#r$rsrz"Destination conflict with service.cSs|jS)N)rK)r@r#r#r$rsz.FirewallPolicy._rule_prepare..)r~ conntracknatrrjz3rich rule family '%s' conflicts with icmp type '%s'z'IcmpBlock not usable with accept actionzUnknown element %s)rr)Dtyperrrr get_servicerKincludesrr0copydeepcopyrfamilyrrrconfig get_icmptyper%rrrrZ INVALID_RULEipvsr9ris_ipv_supportedrrrrrrr+rr replacerCbuild_policy_helper_ports_rulesrZ add_modulesbuild_policy_ports_rulesrIbuild_policy_protocol_rulesrFbuild_policy_source_ports_rulesrrrrrvaluerrrrbuild_policy_masquerade_rulesrZto_portrrbuild_policy_forward_port_rulesrZINVALID_ICMPTYPEbuild_policy_icmp_block_rulesZ*build_policy_rich_source_destination_rules)r!ryr;rr{r$svcincludeZ_ruler3Z source_ipvrZ destinationsrr%rrrrr nat_modulerprotorHrrrr#)r&r!r$rs                             zFirewallPolicy._rule_preparec Csb|jjj|}|j|j|}||j|j7}tt|ddd}|dkrN|g}x@|j D]6}||krdqV|j ||j ||j |||||dqVWg} xnd D]f} |jj | sq|jj| } t|jdkr| |jkr| j | |j| fq| df| kr| j | dfqWxV| D]L\} } x|D]} | j}t|}| jjdd }|j|| jd krf| j| j rfqt| jd kr|j|n:x8| jD].\}}| j||||| | j|}|j| |qWqWx2|jD](\}}| j||||| }|j| |qWx,|jD]"}| j|||| }|j| |qWx2|jD](\}}| j||||| }|j| |q,Wq WdS) NcSs|jS)N)rK)r@r#r#r$rsz)FirewallPolicy._service..)r~)r$rrrr)r*rrj)rr) rrr,rrrrr+r9r-rr0rrr'r(rr%rr r5Z add_moduler0r4rCr6rKrr7rIr8rFr9)r!ryr;rr{r$r>rr?Z backends_ipvrrr%rrrr@rrArHrr#r#r$rrsb               zFirewallPolicy._servicecCs<x6|jjD](}|jsq |j||||}|j||q WdS)N)rrrr7r)r!ryr;rrr{rrHr#r#r$rss  zFirewallPolicy._portcCs:x4|jjD]&}|jsq |j|||}|j||q WdS)N)rrrr8r)r!ryr;rr{rrHr#r#r$rts zFirewallPolicy._protocolcCs<x6|jjD](}|jsq |j||||}|j||q WdS)N)rrrr9r)r!ryr;rrr{rrHr#r#r$rus zFirewallPolicy._source_portcCs8d}|jt||jj|}|j||}|j||dS)Nr)rrrr(r;r)r!ryr;r{rrrHr#r#r$rvs    zFirewallPolicy._masqueradec CsXtd|rd}nd}|r(|r(|jt||jj|} | j||||||} |j| | dS)Nrr)rrrrr(r<r) r!ryr;r{rrrrrrrHr#r#r$rqs    zFirewallPolicy._forward_portc Cs|jjj|}xl|jjD]^}|js&qd}|jrXx&dD]}||jkr6|j|s6d}Pq6W|r^q|j|||} |j|| qWdS)NFrrT)rr) rr1r2rrr%r4r=r) r!ryr;rr{r&rZ skip_backendrrHr#r#r$rps   zFirewallPolicy._icmp_blockcCsh|j|j}|dkrdS|j| r0|dkr0dSx2|jjD]$}|jsHq<|j||}|j||q|jdD]}|jjj|drfPqfW|jd$|jd%|Sd&g}|jjs|jd'x4|jdD]}|jjj|drPqW|jd(x>|jdD]}|jjj|drPqW|jd)|jd*|SdS)+z:Create a list of (table, chain) needed for policy dispatchr6r4r5r7rrOr*rKmanglerLrPrNrMZ interfacesN)rrO)r*rK)rSrK)rLrK)rrO)rLrK)rrP)rrN)r*rK)r*rM)rSrK)rLrK)rrN)r*rK)rSrK)rLrK)r*rM)rrN)r*rM)rLrK)r*rK)rSrK)rrN)rLrK)r*rM)r*rK)rSrK)r r<rnftables_enabledr0r:r8)r!r;rMtcr:r#r#r$rlsj                 z4FirewallPolicy._get_table_chains_for_policy_dispatchcCsr|j|}d|jdkr4dg}|jjs0|jd|Sd|jdkrLdddgSd|jd krbddgStd|SdS)z8Create a list of (table, chain) needed for zone dispatchr5r7rrOrLrKr6 FORWARD_INr*rSr4 FORWARD_OUTrMzInvalid policy: %sN)rrO)rLrK)rrV)r*rK)rSrK)rrW)r*rM)r r<rrTr0r)r!r;rMrUr#r#r$rms  z2FirewallPolicy._get_table_chains_for_zone_dispatchFcCs|jjj|}|jr|j}n||}d|jdkrl|dkrBd|S|dkrRd|S|jsh|dkrhd|SnJd|jd kr|js|dkrd |Sn"d |jdkr|dkr|jrd |Sd |Sn0|dkr|rd|Sd|Sn|dkrd|Snd |jd krh|dkr*|jr d|Sd |Sn<|dkrL|rBd|Sd|Sn|dkr|jsd|SnN|js|dkrd |S|dkr|rd|Sd|Sn|dkrd|Std|||fS)Nr5r7rZIN_rLZPRE_rSr*r4ZOUT_r6ZFWDI_ZFWD_ZPOST_ZFWDO_z.Can't convert policy to chain name: %s, %s, %s)rSr*)rSrL)rSrL)rSrL)rr;r.r/r<r)r!r;r|Z policy_prefixZisSNATrMsuffixr#r#r$policy_base_chain_namesb                z%FirewallPolicy.policy_base_chain_name)N)N)N)N)rNNT)N)rNNT)N)rNN)N)rNN)N)rNN)N)rNN)N)rNN)N)rNN)N)NN)NN)NNrNN)NNN)NN)rNN)N)NN)N)N)N)NN)F)__name__ __module__ __qualname__r%r'r)r*r-r3r=r.rNrQrLrdrerr8rrcrPrrrrrSrrrrrrrrTrrrrrrrrrwr^rrrrrrrrWrrrrrrrrrrXrrrrrrrr\rrrrrrr]rrrrrrr_rrrrrrrVrrrrrrrrUr rr r rr rrrrrrrnrrr#r"rrrrrrsrtrurvrqrprrJrQrRrorlrmrYr#r#r#r$rs$  '  ?  . , # , # :     ' (   ' ( '   +     ) )  @ @    ( P r)'rhr.Zfirewall.core.loggerrZfirewall.functionsrrrrrrr r r r rr rrrrrrrrrrZfirewall.core.fw_transactionrZfirewallrZfirewall.errorsrZfirewall.fw_typesrZfirewall.core.baserobjectrr#r#r#r$s 04     PK! __pycache__/fw_nm.cpython-36.pycnu[3 Yj@s dZddddddddgZd d lZd d lmZyejd d Wnek rTdZYn8Xyd dlmZdZWn e eej fk rdZYnXd a d dl m Z d dlmZd dlmZd d lZddZddZddZddZddZddZddZddZdd Zd!dZd"dZd#dZd S)$z(Functions for NetworkManager interactioncheck_nm_importednm_is_importednm_get_zone_of_connectionnm_set_zone_of_connectionnm_get_connectionsnm_get_connection_of_interfacenm_get_bus_namenm_get_dbus_interfaceN)GLibNMz1.0F)r T)errors) FirewallError)logcCststtjddS)zNCheck function to raise a MISSING_IMPORT error if the import of NM failed zgi.repository.NM = 1.0N) _nm_importedr r ZMISSING_IMPORTrr/usr/lib/python3.6/fw_nm.pyr0scCstS)znReturns true if NM has been properly imported @return True if import was successful, False otherwirse )rrrrrr6scCststjjdatS)zReturns the NM client object or None if the import of NM failed @return NM.Client instance if import was successful, None otherwise N) _nm_clientr ZClientnewrrrr nm_get_client<s rc Csttj|}|dkrdS|j}|dkr2dSy |jtjjtjjB@rPdSWn t k rr|j rndSYnX|j }|dkrd}|S)zGet zone of connection from NM @param connection name @return zone string setting of connection, empty string if not set, None if connection is unknown N) rrget_connection_by_uuidget_setting_connection get_flagsr SettingsConnectionFlags NM_GENERATED NM_VOLATILEAttributeError get_unsavedZget_zone) connectioncon setting_conzonerrrrEs$    cCsVttj|}|dkrdS|j}|dkr2dS|dkr>d}|jd||jddS)zSet the zone for a connection @param zone name @param connection name @return True if zone was set, else False NFrr!T)rrrrZ set_propertyZcommit_changes)r!rrr rrrrcs  c Cs~|j|jttj}xX|D]P}|jr4q&|j}|j}|j}|||<x |D]}|j}|rZ|||<qZWq&WdS)znGet active connections from NM @param connections return dict @param connections_name return dict N) clearrrget_active_connectionsget_vpnZget_idget_uuid get_devices get_ip_iface) Z connectionsZconnections_nameZactive_connections active_connameZuuidZdevicesdevip_ifacerrrrxs   c Cstg}xtjD]|}|jr$qy&|j}|jtjjtjj B@rHwWnt k rh|j rdwYnXx&|j D]}|j }|rt|j|qtWqW|S)zGGet active interfaces from NM @returns list of interface names )rrr#r$get_connectionrr rrrrrr&r'append)Zactive_interfacesr(rr*r+rrrnm_get_interfacess$  r.cCs6g}x,tD]"}t|}|t|kr |j|q W|S)N)r.rrr-)r!Z interfaces interfaceZconnrrrnm_get_interfaces_in_zones   r0cCs<tx0tjD]"}|j}|dkr(q||kr|SqWdS)zzGet device from NM which has the given IP interface @param interface name @returns NM.Device instance or None N)rrr&r')r/devicer+rrrnm_get_device_by_ip_ifacesr2c Csxtt|}|dkrdS|j}|dkr.dSy |j}|jtjj@rLdSWn tk rn|j rjdSYnX|j S)zGet connection from NM that is using the interface @param interface name @returns connection that is using interface or None N) rr2Zget_active_connectionr,rr rrrrr%)r/r1r(rrrrrs c CsRtsdSy&tj}|jtjtj}|j}~~|Stk rLt j dYnXdS)Nz(Failed to get bus name of NetworkManager) rdbusZ SystemBusZ get_objectr DBUS_INTERFACEZ DBUS_PATHZbus_name ExceptionrZdebug2)Zbusobjr)rrrrscCstsdStjS)Nr)rr r4rrrrrs)__doc____all__ZgiZ gi.repositoryr Zrequire_version ValueErrorrr ImportErrorErrorrZfirewallr Zfirewall.errorsr Zfirewall.core.loggerrr3rrrrrrr.r0r2rrrrrrrs@           PK!4)c RR"__pycache__/fw_zone.cpython-36.pycnu[3 Yjy@sddlZddlZddlmZmZmZddlmZddlm Z ddl m Z ddl m Z mZmZmZmZmZmZmZmZddlmZmZmZddlmZdd lmZdd lmZGd d d e Z!dS) N) SHORTCUTSDEFAULT_ZONE_TARGETSOURCE_IPSET_TYPES)FirewallTransaction)Policy)log) Rich_Service Rich_Port Rich_ProtocolRich_SourcePortRich_ForwardPortRich_IcmpBlock Rich_IcmpTypeRich_Masquerade Rich_Mark) checkIPnMask checkIP6nMask check_mac)errors) FirewallError)LastUpdatedOrderedDictc@sNeZdZdZddZddZddZdd Zd d Zd d Z ddZ ddZ ddZ ddZ ddZddZddZddZddd Zd!d"Zd#d$Zd%d&Zdd'd(Zd)d*Zd+d,Zd-d.Zdd/d0Zdd1d2Zd3d4Zd5d6Zd7d8Zd9d:Zd;d<Z d=d>Z!dd@dAZ"dBdCZ#ddDdEZ$ddFdGZ%ddHdIZ&dJdKZ'dLdMZ(dNdOZ)ddQdRZ*ddSdTZ+ddUdVZ,dWdXZ-ddYdZZ.dd[d\Z/d]d^Z0d_d`Z1dadbZ2ddcddZ3dedfZ4dgdhZ5didjZ6dkdlZ7dmdnZ8dodpZ9ddqdrZ:dsdtZ;dudvZd{d|Z?d}d~Z@ddZAdddZBddZCddZDddZEddZFdddZGddZHddZIddZJdddZKddZLddZMddZNdddZOddZPddZQdddZRdddZSdddZTddZUdddZVddZWddZXddZYdddZZddZ[ddZ\ddZ]ddZ^ddZ_dddZ`ddZaddd„ZbddĄZcddƄZddS) FirewallZonercCs||_i|_i|_dS)N)_fw_zones_zone_policies)selffwr/usr/lib/python3.6/fw_zone.py__init__&szFirewallZone.__init__cCsd|j|jfS)Nz%s(%r)) __class__r)rrrr__repr__+szFirewallZone.__repr__cCs|jj|jjdS)N)rclearr)rrrrcleanup.s zFirewallZone.cleanupcCs t|jS)N)rr)rrrrnew_transaction2szFirewallZone.new_transactioncCsdj||dS)Nzzone_{fromZone}_{toZone})fromZonetoZone)format)rr%r&rrrpolicy_name_from_zones5sz#FirewallZone.policy_name_from_zonescCst|jjS)N)sortedrkeys)rrrr get_zones:szFirewallZone.get_zonescCs8g}x.|jD]"}|j|s&|j|r|j|qW|S)N)r+list_interfaces list_sourcesappend)rZ active_zoneszonerrrget_active_zones=s zFirewallZone.get_active_zonescCs6|j|}x&|jD]}||j|jdkr|SqWdS)N interfaces)_FirewallZone__interface_idrsettings)r interface interface_idr/rrrget_zone_of_interfaceDs   z"FirewallZone.get_zone_of_interfacecCs6|j|}x&|jD]}||j|jdkr|SqWdS)Nsources)_FirewallZone__source_idrr3)rsource source_idr/rrrget_zone_of_sourceLs   zFirewallZone.get_zone_of_sourcecCs|jj|}|j|S)N)r check_zoner)rr/zrrrget_zoneTs zFirewallZone.get_zonecCsBt}|j|_|j|||_|j|_|j|_|g|_|g|_xd D]}||jkr~|d kr~|d kr~t ||t j t ||qD|d kr||jkr|d krt ||t j t ||qD||jko|d ko|dkrt ||t j t ||qD|dkrDg|_ xB|j D]8}|j||}||j|j|kr|j jt j |qWqDW|S)Nservicesports masquerade forward_ports source_ports icmp_blocksrules protocolsHOSTANY)r?r@rArBrCrDrErF)r?r@rCrDrF)rA)rDrB)rE)rnameZderived_from_zoner(ZONE_POLICY_PRIORITYZprioritytargetZ ingress_zonesZ egress_zonessetattrcopydeepcopygetattrrE_rich_rule_to_policiesr.)rz_objr%r&p_objZsettingruleZcurrent_policyrrrpolicy_obj_from_zone_objXs6    z%FirewallZone.policy_obj_from_zone_objcCsddd D|_||j|j<g|j|j<xX|jdfd|jf|jdfgD]8\}}|j|||}|jjj||j|jj|jqFW|j |jdS) NcSsi|] }t|qSr)r).0xrrr sz)FirewallZone.add_zone..r1r7icmp_block_inversionforwardrGrH)r1r7rXrY) r3rrIrrTrpolicyZ add_policyr.copy_permanent_to_runtime)robjr%r&rRrrradd_zone~s   zFirewallZone.add_zonecCsn|j|}x|jD]}|j||ddqWx|jD]}|j||ddq2W|jrZ|j||jrj|j|dS)NF) allow_apply) rr1 add_interfacer7 add_sourcerY add_forwardrXadd_icmp_block_inversion)rr/r\argrrrr[s    z&FirewallZone.copy_permanent_to_runtimecCs8|j|}|jr|j||jj|j|=|j|=dS)N)rappliedunapply_zone_settingsr3r"r)rr/r\rrr remove_zones    zFirewallZone.remove_zoneNcCsVxP|jD]D}|j|}t|jdks4t|jdkr tjd||j||dq WdS)NrzApplying zone '%s')use_transaction)r+rlenr1r7rdebug1apply_zone_settings)rrgr/rQrrr apply_zoness   zFirewallZone.apply_zonescCs|j|}||_dS)N)rrd)rr/rdr\rrrset_zone_applieds zFirewallZone.set_zone_appliedcCsd|kr dS|jd}t|dkr&dSd}x tD]}|dt|kr0|}q0W|dk r|d|jkrhdSt|dkst|dkr|dd kr|d|fSdS) N_rprerdenyallowpost)rqrrrrsrt)splitrhrr+)rchainZsplits_chainrVrrrzone_from_chains      zFirewallZone.zone_from_chaincCst|j|}|dkrdS|\}}|d kr0|}d}n4|d krB|}d}n"|d krTd}|}nttjd||j|||fS) N PREROUTING FORWARD_INrHINPUTrG POSTROUTING FORWARD_OUTz&chain '%s' can't be mapped to a policy)ryrz)r{)r|r})rxrrZ INVALID_CHAINr()rrvrVr/rwr%r&rrrpolicy_from_chains zFirewallZone.policy_from_chainc Csj|dkrf|j|}|dk rf|j|\}}|dkr:|j}n|}|jjj|d||||dkrf|jddS)Nipv4ipv6T)rr)r~r$rrZZgen_chain_rulesexecute) ripvtablervrgrVrZrw transactionrrrcreate_zone_base_by_chains  z&FirewallZone.create_zone_base_by_chaincCstj||d}|S)N)Zdatesendertimeout)time)rrrretrrrZ__gen_settingsszFirewallZone.__gen_settingscCs |j|jS)N)r>r3)rr/rrr get_settingsszFirewallZone.get_settingscCs|j|}x|D]z}xt||D]h}|dkr<|j||||q|dkr`|j|||d|d|q|dkrlqq|dkrvqtjd|||qWqW|r|j|||dS)Nr1r7rrorXrYz3Zone '%s': Unknown setting '%s:%s', unable to apply)r _interface_sourcerZwarning_icmp_block_inversion)renabler/rr3keyargsrrr_zone_settingss  zFirewallZone._zone_settingscCs|jj|}|j|}|jr dSd|_|dkr8|j}n|}x2|j|D]$}tjd|||jjj ||dqHW|j d|||dkr|j ddS)NTz+Applying policy (%s) derived from zone '%s')rg) rr<rrdr$rrrirZapply_policy_settingsrr)rr/rg_zoner\rrZrrrrjs   z FirewallZone.apply_zone_settingscCs|jj|}|j|}|js dS|dkr2|j}n|}x$|j|D]}|jjj||dqBW|jd|||dkr||j ddS)N)rgFT) rr<rrdr$rrZunapply_policy_settingsrr)rr/rgrr\rrZrrrre,s   z"FirewallZone.unapply_zone_settingscCs~|j|}|j|}g}x\tdD]P}|j|d|krZ|jtjt||j|dq"|j||j|dq"Wt|S)zH :return: exported config updated with runtime settings r) r>get_config_with_settings_dictrangeZIMPORT_EXPORT_STRUCTUREr.rMrNrOtuple)rr/r\Z conf_dictZ conf_listirrrget_config_with_settings?s  "z%FirewallZone.get_config_with_settingsc Cs|j|j}|dtkr"d|d<|j||j||j||j||j||j||j ||j ||j ||j ||j ||j|d }|jj||S)zH :return: exported config updated with runtime settings rKdefault) r?r@rDrArBr1r7 rules_strrFrCrXrY)r>Zexport_config_dictr list_services list_portslist_icmp_blocksquery_masqueradelist_forward_portsr,r- list_ruleslist_protocolslist_source_portsquery_icmp_block_inversion query_forwardrZ'combine_runtime_with_permanent_settings)rr/Z permanentZruntimerrrrOs  z*FirewallZone.get_config_with_settings_dictc sddlmdfdd }fdd}jjfjjfjjfjj fj j fj j fjjf||fjjfjjfjjfjjfd }j|}jj||\}} xv| D]n} t| | tr$xX| | D]:} t| tr || d|f| q|| d|| qWq|| d|qWx|D]} t|| trx|| D]l} | dkr|| d|| |d nDt| tr|| d|f| d|d n|| d|| d|d q\Wn6| dkr|| d||d n|| d|d|d q>WdS)Nr) Rich_Rulecsj||dd|ddS)N)rule_strr)rr)add_rule)r/rrr)rrrradd_rule_wrapperhszDFirewallZone.set_config_with_settings_dict..add_rule_wrappercsj||ddS)N)r) remove_rule)r/r)rrrrremove_rule_wrapperjszGFirewallZone.set_config_with_settings_dict..remove_rule_wrapper) r?r@rDrArBr1r7rrFrCrXrYror1r7)r)rrrX)rN)r1r7)rX)firewall.core.richr add_serviceremove_serviceadd_port remove_portadd_icmp_blockremove_icmp_blockadd_masqueraderemove_masqueradeadd_forward_portremove_forward_portr_remove_interfacer` remove_source add_protocolremove_protocoladd_source_portremove_source_portrbremove_icmp_block_inversionraremove_forwardrrZget_added_and_removed_settings isinstancelistr) rr/r3rrrZ setting_to_fnZ old_settingsZ add_settingsZremove_settingsrrr)rrrset_config_with_settings_dictesF                    z*FirewallZone.set_config_with_settings_dictcCs|jj|dS)N)rcheck_interface)rr4rrrrszFirewallZone.check_interfacecCs\|jj|}|j|}|j|}||jdkrX|jd|}d|krX|ddk rX|dSdS)Nr1r)rr<rr2r3)rr/r4r_objr5r3rrrinterface_get_senders   z!FirewallZone.interface_get_sendercCs|j||S)N)r)rr4rrrZ__interface_ids zFirewallZone.__interface_idTc Cs|jj|jj|}|j|}|j|}||jdkrLttjd||f|j |dk rjttj d|t j d||f|dkr|j } n|} |j r|r|j|| d| j|j|d|r|jd||| |j||||| j|j|||dkr| jd|S)Nr1z'%s' already bound to '%s'z'%s' already bound to a zonez&Setting zone of interface '%s' to '%s')rgFT)r check_panicr<rr2r3rrZONE_ALREADY_SETr6 ZONE_CONFLICTrrir$rdrjadd_failrlr!_FirewallZone__register_interface#_FirewallZone__unregister_interfacer) rr/r4rrgr^rrr5rrrrr_s8            zFirewallZone.add_interfacecCs6|jd||jd|<| p"|dk|jd|d<dS)Nrr1 __default__)_FirewallZone__gen_settingsr3)rrr5r/rrrrZ__register_interfacesz!FirewallZone.__register_interfacecCsR|jj|j|}|jj|}||kr,|S|dk r@|j|||j|||}|S)N)rrr6r<rr_)rr/r4r _old_zone _new_zonerrrrchange_zone_of_interfaces    z%FirewallZone.change_zone_of_interfacecCsz|jj|dkr|j}n|}|j|||jd|d|dd|dk rd|dkrd|jd|d|dd|dkrv|jddS)NT+)r.rF)rrr$rjrr)rZold_zoneZnew_zonergrrrrchange_default_zones   z FirewallZone.change_default_zonec Cs|jj|j|}|dkr,ttjd||dkr8|n |jj|}||krbttjd|||f|dkrt|j}n|}|j |}|j |}|j |j |||j d||||dkr|jd|S)Nz'%s' is not in any zonerz"remove_interface(%s, %s): zoi='%s'FT)rrr6rrZUNKNOWN_INTERFACEr<rr$rr2add_postrrr) rr/r4rgZzoirrrr5rrrrs(       zFirewallZone.remove_interfacecCs||jdkr|jd|=dS)Nr1)r3)rrr5rrrZ__unregister_interfacesz#FirewallZone.__unregister_interfacecCs|j||j|dkS)Nr1)r2r)rr/r4rrrquery_interfaceszFirewallZone.query_interfacecCs|j|djS)Nr1)rr*)rr/rrrr,"szFirewallZone.list_interfacesFcCsxt|r dSt|rdSt|r$dS|jdrh|j|dd|rV|j|dd|j|ddSttj |dS)Nrrrzipset:) rrr startswith_check_ipset_type_for_source_check_ipset_applied _ipset_familyrrZ INVALID_ADDR)rr9rdrrr check_source's zFirewallZone.check_sourcecCs|j||d}||fS)N)rd)r)rr9rdrrrrZ __source_id6szFirewallZone.__source_idc Cs|jj|jj|}|j|}t|r0|j}|j||d}||jdkr`tt j d||f|j |dk r~tt j d||dkr|j } n|} |j r|r|j|| d| j|j|d|r|jd||d|d | |j||||| j|j|||dkr| jd|S) N)rdr7z'%s' already bound to '%s'z'%s' already bound to a zone)rgFTrro)rrr<rrupperr8r3rrrr;rr$rdrjrrlr_FirewallZone__register_source _FirewallZone__unregister_sourcer) rr/r9rrgr^rrr:rrrrr`:s4        zFirewallZone.add_sourcecCs6|jd||jd|<| p"|dk|jd|d<dS)Nrr7rr)rr3)rrr:r/rrrrZ__register_sourceaszFirewallZone.__register_sourcecCsb|jj|j|}|jj|}||kr,|St|r<|j}|dk rP|j|||j|||}|S)N)rrr;r<rrrr`)rr/r9rrrrrrrchange_zone_of_sourcegs    z"FirewallZone.change_zone_of_sourcec Cs|jjt|r|j}|j|}|dkrsz-FirewallZone.list_sources..r7)rr*)rr/rrrr-szFirewallZone.list_sourcesc sxjjD]}|jsq xPj|D]B}x<jjj|D]*\}} |j|||||| |} |j|| q8Wq$Wj|d}j |dr |d kr |j |||d|d} |j|| q WxΈjjj D]}|jjj |kr|jjj |krq|jjjkrdjjj|jrd| rsz)FirewallZone._interface..cs|jjjkojjj|S)N)rrZrr)r)rrrrs)rr)renabled_backendspolicies_supportedrrZ#_get_table_chains_for_zone_dispatchZ!build_zone_source_interface_rules add_rulesr(rbuild_zone_forward_rules"get_policies_not_derived_from_zonelist_ingress_zoneslist_egress_zonesr get_policyrdrhr,r_ingress_egress_zonesr) rrr/r4rr.backendrZrrvrEr)rrrs2 $zFirewallZone._interfacecCs$|j|dkrdS|jjj|ddS)Nzhash:macF)rd) _ipset_typeripsetZ get_family)rrIrrrrszFirewallZone._ipset_familycCs|jjj|ddS)NF)rd)rrZget_type)rrIrrrrszFirewallZone._ipset_typecCsdj|g|jjj|S)N,)joinrrZ get_dimension)rrIflagrrr_ipset_match_flagsszFirewallZone._ipset_match_flagscCs|jjj|S)N)rrZ check_applied)rrIrrrrsz!FirewallZone._check_ipset_appliedcCs*|j|}|tkr&ttjd||fdS)Nz.ipset '%s' with type '%s' not usable as source)rrrrZ INVALID_IPSET)rrIZ_typerrrrs  z)FirewallZone._check_ipset_type_for_sourcec sx|rjj|gnjjD]}|js*qxNj|D]@}x:jjj|D](\}} |j|||||| } |j|| qJWq6Wj |d}j |dr|j |||d|d} |j|| qWxΈjjj D]}|jjj |kr|jjj|krq|jjjkrljjj|jrl| rDtj|dkrDjjj||dn&jjjd|||jfdd |q|r|jfd d |qWdS) NrHrYr)r9ro)rgFcs |jjjkojjjd|S)NT)rrZrr)r)rrrrsz&FirewallZone._source..cs|jjjkojjj|S)N)rrZrr)r)rrrr s)rget_backend_by_ipvrrrrZrZbuild_zone_source_address_rulesrr(rrrrrrrrdrhr-rrr) rrr/rr9rrrZrrvrEr)rrrs2"  $zFirewallZone._sourcecCs0|jj|}|j|d}|jjj|||||S)NrG)rr<r(rZr)rr/servicerrp_namerrrr s  zFirewallZone.add_servicecCs,|jj|}|j|d}|jjj|||S)NrG)rr<r(rZr)rr/rrrrrrs  zFirewallZone.remove_servicecCs(|jj|}|j|d}|jjj||S)NrG)rr<r(rZ query_service)rr/rrrrrrs  zFirewallZone.query_servicecCs&|jj|}|j|d}|jjj|S)NrG)rr<r(rZr)rr/rrrrrs  zFirewallZone.list_servicescCs2|jj|}|j|d}|jjj||||||S)NrG)rr<r(rZr)rr/portprotocolrrrrrrr#s  zFirewallZone.add_portcCs.|jj|}|j|d}|jjj||||S)NrG)rr<r(rZr)rr/rrrrrrr)s  zFirewallZone.remove_portcCs*|jj|}|j|d}|jjj|||S)NrG)rr<r(rZ query_port)rr/rrrrrrr/s  zFirewallZone.query_portcCs&|jj|}|j|d}|jjj|S)NrG)rr<r(rZr)rr/rrrrr4s  zFirewallZone.list_portscCs2|jj|}|j|d}|jjj||||||S)NrG)rr<r(rZr)rr/ source_portrrrrrrrr9s  zFirewallZone.add_source_portcCs.|jj|}|j|d}|jjj||||S)NrG)rr<r(rZr)rr/rrrrrrr?s  zFirewallZone.remove_source_portcCs*|jj|}|j|d}|jjj|||S)NrG)rr<r(rZquery_source_port)rr/rrrrrrrEs  zFirewallZone.query_source_portcCs&|jj|}|j|d}|jjj|S)NrG)rr<r(rZr)rr/rrrrrJs  zFirewallZone.list_source_portscCs|jj|}t|jtkr(|j|dgSt|jttt t gkrL|j|dgSt|jt t gkrv|j|d|j|dgSt|jt gkr|j|dgSt|jtgkr|jd|gS|jdkr|j|dgStdt|jdS)NrHrGz Rich rule type (%s) not handled.)rr<typeactionrr(elementrr r r r rr rr)rr/rSrrrrPOs    z#FirewallZone._rich_rule_to_policiescCs.x(|j||D]}|jjj||||qW|S)N)rPrrZr)rr/rSrrrrrrrbszFirewallZone.add_rulecCs*x$|j||D]}|jjj||qW|S)N)rPrrZr)rr/rSrrrrrgszFirewallZone.remove_rulecCs2d}x(|j||D]}|o(|jjj||}qW|S)NT)rPrrZ query_rule)rr/rSrrrrrrlszFirewallZone.query_rulecCs^|jj|}t}xB|j|d|j|d|jd|gD]}|jt|jjj|q6Wt|S)NrHrG)rr<setr(updaterZrr)rr/rrrrrrrs   zFirewallZone.list_rulescCs0|jj|}|j|d}|jjj|||||S)NrG)rr<r(rZr)rr/rrrrrrrr{s  zFirewallZone.add_protocolcCs,|jj|}|j|d}|jjj|||S)NrG)rr<r(rZr)rr/rrrrrrs  zFirewallZone.remove_protocolcCs(|jj|}|j|d}|jjj||S)NrG)rr<r(rZquery_protocol)rr/rrrrrr s  zFirewallZone.query_protocolcCs&|jj|}|j|d}|jjj|S)NrG)rr<r(rZr)rr/rrrrrs  zFirewallZone.list_protocolscCs.|jj|}|jd|}|jjj||||S)NrH)rr<r(rZr)rr/rrrrrrrs  zFirewallZone.add_masqueradecCs*|jj|}|jd|}|jjj||S)NrH)rr<r(rZr)rr/rrrrrs  zFirewallZone.remove_masqueradecCs&|jj|}|jd|}|jjj|S)NrH)rr<r(rZr)rr/rrrrrs  zFirewallZone.query_masqueradec Cs6|jj|}|j|d}|jjj||||||||S)NrH)rr<r(rZr) rr/rrtoporttoaddrrrrrrrrs   zFirewallZone.add_forward_portcCs2|jj|}|j|d}|jjj||||||S)NrH)rr<r(rZr)rr/rrr r rrrrrs  z FirewallZone.remove_forward_portcCs.|jj|}|j|d}|jjj|||||S)NrH)rr<r(rZquery_forward_port)rr/rrr r rrrrr s  zFirewallZone.query_forward_portcCs&|jj|}|j|d}|jjj|S)NrH)rr<r(rZr)rr/rrrrrs  zFirewallZone.list_forward_portscCsP|jj|}|j|d}|jjj|||||j|d}|jjj|||||S)NrGrH)rr<r(rZr)rr/icmprrrrrrrs    zFirewallZone.add_icmp_blockcCsH|jj|}|j|d}|jjj|||j|d}|jjj|||S)NrGrH)rr<r(rZr)rr/r rrrrrs    zFirewallZone.remove_icmp_blockcCsD|jj|}|j|d}|j|d}|jjj||oB|jjj||S)NrGrH)rr<r(rZquery_icmp_block)rr/r  p_name_host p_name_fwdrrrrs    zFirewallZone.query_icmp_blockcCsH|jj|}|j|d}|j|d}tt|jjj||jjj|S)NrGrH)rr<r(r)rrZr)rr/rrrrrrs    zFirewallZone.list_icmp_blockscCsH|jj|}|j|d}|jjj|||j|d}|jjj|||S)NrGrH)rr<r(rZrb)rr/rrrrrrbs    z%FirewallZone.add_icmp_block_inversioncCsL|jj|}|j|d}|jjj||||j|d}|jjj|||dS)NrGrH)rr<r(rZr)rrr/rrrrrrs    z"FirewallZone._icmp_block_inversioncCsD|jj|}|j|d}|jjj||j|d}|jjj||S)NrGrH)rr<r(rZr)rr/rrrrrs    z(FirewallZone.remove_icmp_block_inversioncCs@|jj|}|j|d}|j|d}|jjj|o>|jjj|S)NrGrH)rr<r(rZr)rr/rrrrrrs    z'FirewallZone.query_icmp_block_inversionc Cs|j|d}xT|j|jdD]@}x:|jjD],}|js:q.|j|||d|d}|j||q.WqWxj|j|jdD]V\}} xL|r|jj|gn|jjD],}|jsq|j|||d| d}|j||qWqtWdS)NrHr1r)r4r7)r9) r(rr3rrrrrr) rrr/rrr4rrErr9rrr_forwards "zFirewallZone._forwardcCsdS)NTr)rrrrZ __forward_idszFirewallZone.__forward_idc Cs|jj|}|jj||jj|j|}|j}||jdkrRttj d||dkrd|j }n|}|j r||j d|||j |||||j|j|||dkr|jd|S)NrYzforward already enabled in '%s'T)rr<Z check_timeoutrr_FirewallZone__forward_idr3rrZALREADY_ENABLEDr$rdr_FirewallZone__register_forwardr!_FirewallZone__unregister_forwardr) rr/rrrgrr forward_idrrrrras$       zFirewallZone.add_forwardcCs|j|||jd|<dS)NrY)rr3)rrrrrrrrZ__register_forward.szFirewallZone.__register_forwardcCs|jj|}|jj|j|}|j}||jdkrFttjd||dkrX|j }n|}|j rp|j d|||j |j |||dkr|jd|S)NrYzforward not enabled in '%s'FT)rr<rrrr3rrZ NOT_ENABLEDr$rdrrrr)rr/rgrrrrrrrr2s       zFirewallZone.remove_forwardcCs||jdkr|jd|=dS)NrY)r3)rrrrrrZ__unregister_forwardKsz!FirewallZone.__unregister_forwardcCs|j|j|dkS)NrY)rr)rr/rrrrOszFirewallZone.query_forward)N)N)N)N)NNT)N)N)N)F)F)NNT)N)N)F)rN)rN)rN)rN)rN)rN)NNrN)NN)NN)rN)N)rNN)N)e__name__ __module__ __qualname__rJrr!r#r$r(r+r0r6r;r>rTr]r[rfrkrlrxr~rrrrrjrerrrrrr2r_rrrrrrr,rr8r`rrrrrr-rrrrrrrrrrrrrrrrrrrrPrrrrrrr rrrrrrr rrrrrrbrrrrrrarrrrrrrrr#s&     8  (      &   ,(               r)"rrMZfirewall.core.baserrrZfirewall.core.fw_transactionrZfirewall.core.io.policyrZfirewall.core.loggerrrrr r r r r rrrZfirewall.functionsrrrZfirewallrZfirewall.errorsrZfirewall.fw_typesrobjectrrrrrs   ,   PK!T22$__pycache__/fw_direct.cpython-36.pycnu[3 YjW@sndgZddlmZddlmZddlmZddlmZddlm Z ddl m Z ddl m Z Gd ddeZd S) FirewallDirect)LastUpdatedOrderedDict) ipXtables)ebtables)FirewallTransaction)log)errors) FirewallErrorc@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dNddZ ddZ ddZ dOddZddZddZddZd d!ZdPd"d#ZdQd$d%Zd&d'Zd(d)Zd*d+ZdRd,d-ZdSd.d/Zd0d1Zd2d3Zd4d5Zd6d7Zd8d9Zd:d;ZdTdd?Z!d@dAZ"dBdCZ#dDdEZ$dFdGZ%dHdIZ&dJdKZ'dLdMZ(dS)VrcCs||_|jdS)N)_fw_FirewallDirect__init_vars)selffwr/usr/lib/python3.6/fw_direct.py__init__'szFirewallDirect.__init__cCsd|j|j|j|jfS)Nz%s(%r, %r, %r)) __class___chains_rules_rule_priority_positions)r rrr__repr__+szFirewallDirect.__repr__cCs"i|_i|_i|_i|_d|_dS)N)rrr _passthroughs_obj)r rrrZ __init_vars/s zFirewallDirect.__init_varscCs |jdS)N)r )r rrrcleanup6szFirewallDirect.cleanupcCs t|jS)N)rr )r rrrnew_transaction;szFirewallDirect.new_transactioncCs ||_dS)N)r)r objrrrset_permanent_config@sz#FirewallDirect.set_permanent_configcCs*t|jt|jt|jdkr&dSdS)NrTF)lenrrr)r rrrhas_runtime_configurationCs"z(FirewallDirect.has_runtime_configurationcCsB|jr dSt|jjt|jjt|jjdkr>dSdS)NTrF)rrrget_all_chains get_all_rulesget_all_passthroughs)r rrrhas_configurationHs z FirewallDirect.has_configurationNcCsP|dkr|j}n|}|j|jj|jj|jjf||dkrL|jddS)NT)r set_configrrrr execute)r use_transaction transactionrrr apply_directQs   zFirewallDirect.apply_directc Csi}i}i}xL|jD]B}|\}}x4|j|D]&}|jj|||s,|j|gj|q,WqWxf|jD]\}|\}}}xL|j|D]>\} } |jj|||| | s|||krt||<| ||| | f<q|WqbWxP|jD]F}x@|j|D]2} |jj || s||krg||<||j| qWqW|||fS)N) rr query_chain setdefaultappendr query_rulerrquery_passthrough) r ZchainsrulesZ passthroughstable_idipvtablechainchain_idpriorityargsrrrget_runtime_configbs,      z!FirewallDirect.get_runtime_configcCs|j|j|jfS)N)rrr)r rrr get_configszFirewallDirect.get_configcCs|dkr|j}n|}|\}}}x||D]t}|\}} xf||D]Z} |j|| | sr nftables_enabledget_direct_backend_by_ipv our_chainsrZ OUR_CHAINSr rZ BUILTIN_CHAINzoneZzone_from_chainZ INVALID_CHAIN)r r.r/r0Zbuilt_in_chainsrCrrr_check_builtin_chains"     z#FirewallDirect._check_builtin_chaincCsH|r|jj|gj|n*|j|j|t|j|dkrD|j|=dS)Nr)rr(r)remover)r r-r0addrrr_register_chains zFirewallDirect._register_chaincCsV|dkr|j}n|}|jjr.|j|jj|jd|||||dkrR|jddS)NT)rr may_skip_flush_direct_backendsadd_preflush_direct_backends_chainr#)r r.r/r0r$r%rrrr6s  zFirewallDirect.add_chaincCs>|dkr|j}n|}|jd|||||dkr:|jddS)NFT)rrLr#)r r.r/r0r$r%rrr remove_chains  zFirewallDirect.remove_chaincCs:|j|||j|||||f}||jko8||j|kS)N)r@rEr)r r.r/r0r-rrrr's   zFirewallDirect.query_chaincCs,|j||||f}||jkr(|j|SgS)N)r@r)r r.r/r-rrr get_chainss    zFirewallDirect.get_chainscCsDg}x:|jD]0}|\}}x"|j|D]}|j|||fq$Wq W|S)N)rr))r rkeyr.r/r0rrrrs  zFirewallDirect.get_all_chainscCsZ|dkr|j}n|}|jjr.|j|jj|jd|||||||dkrV|jddS)NT)rr rIrJrK_ruler#)r r.r/r0r2r3r$r%rrrr8 s  zFirewallDirect.add_rulecCsB|dkr|j}n|}|jd|||||||dkr>|jddS)NFT)rrQr#)r r.r/r0r2r3r$r%rrr remove_rules  zFirewallDirect.remove_rulecCs2|j|||||f}||jko0||f|j|kS)N)r@r)r r.r/r0r2r3r1rrrr*#s   zFirewallDirect.query_rulecCs6|j|||||f}||jkr2t|j|jSgS)N)r@rlistr?)r r.r/r0r1rrr get_rules)s    zFirewallDirect.get_rulesc CsRg}xH|jD]>}|\}}}x.|j|D] \}}|j||||t|fq&Wq W|S)N)rr)rS)r rOrPr.r/r0r2r3rrrr0s    zFirewallDirect.get_all_rulescCs|rr||jkrt|j|<||j||<||jkrg}x4|jD]*}x$|j|D]}|j|t|fqWq W|S)N)rr)rS)r rOr.r3rrrr {s  z#FirewallDirect.get_all_passthroughscCs4g}||jkr0x |j|D]}|jt|qW|S)N)rr)rS)r r.rOr3rrrget_passthroughss  zFirewallDirect.get_passthroughsc Csg}x|D]}d}x|D]}y|j|}Wntk r>YqXt||krd||dkrd}||djd}x.|D]&} |dd} | | |d<|j| qxWqW|s |j|q W|S)z5Split values combined with commas for options in optsF,TN)index ValueErrorrsplitr)) r r,ZoptsZ out_rulesrYZ processedoptiitemsitemrQrrr split_values$     zFirewallDirect.split_valuec Cs*|j|||jj r2|dkr2|jjj|||||}|jj|} |jj rd| j|||rdd|}n:|jjr|dddkr| j|||ddr|dd}|||f} ||f} |r| |jkr| |j| krtt j d||||fnB| |jks| |j| krtt j d||||f|j| | }d} d } | |j krt |j | j}d }x@|t|kr|||kr| |j | ||7} |d7}qTWt|g}|j|d d g}|j|d d g}x<|D]4}|j| | j|||| t|| d7} | d7} qW|j| | ||| |j|j| | || | dS)Nr;r<z %s_directZ_directz"rule '%s' already is in '%s:%s:%s'zrule '%s' is not in '%s:%s:%s'rdrz-sz--sourcez-dz --destination)r;r<iii)r@r rArDcreate_zone_base_by_chainrBZis_chain_builtinrr rALREADY_ENABLED NOT_ENABLEDrsortedr?rrSrlr8Z build_rulerarXadd_fail)r rVr.r/r0r2r3r%rLbackendr1rUrerWZ positionsjZ args_list_argsrrrrQsZ         (   zFirewallDirect._rulecCs|j|||j|||||f}|rV||jkr||j|krttjd|||fn.||jksn||j|krttjd|||f|jj|}|j ||j ||||j ||||j |j ||| dS)Nz chain '%s' already is in '%s:%s'zchain '%s' is not in '%s:%s') r@rErr rrorpr rBZ add_rulesZbuild_chain_rulesrHrr)r rGr.r/r0r%r-rsrrrrLs$    zFirewallDirect._chainc Cs|j|t|}|rD||jkrp||j|krpttjd||fn,||jks\||j|krpttjd||f|jj|}|r|j ||dkr|j |\}}|r|r|jj j ||||} n |j |} |j|| |j||||j|j||| dS)Nzpassthrough '%s', '%s'r;r<)r;r<)r=rarr rrorpr rBZcheck_passthroughZpassthrough_parse_table_chainrDrnZreverse_passthroughr8r^rr) r rVr.r3r%Z tuple_argsrsr/r0rurrrr_'s0        zFirewallDirect._passthrough)N)N)N)N)N)N)N)N))__name__ __module__ __qualname__rrr rrrrr!r&r4r5r"r=r@rErHr6rMr'rNrr8rRr*rTrrXr]r^r9r`r+r rbrlrQrLr_rrrrr&sL  '       jN)__all__Zfirewall.fw_typesrZ firewall.corerrZfirewall.core.fw_transactionrZfirewall.core.loggerrZfirewallrZfirewall.errorsr objectrrrrrs       PK!P@ @ "__pycache__/modules.cpython-36.pycnu[3 Yj@sBdZdgZddlmZddlmZddlmZGdddeZ dS)zmodules backendmodules)runProg)log)COMMANDSc@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dS)rcCstd|_td|_dS)NZmodprobeZrmmod)r _load_command_unload_command)selfr /usr/lib/python3.6/modules.py__init__s zmodules.__init__cCs d|jS)Nz%s) __class__)rr r r __repr__$szmodules.__repr__cCsg}i}ytddp}xh|D]`}|s&P|j}|j}|j|d|ddkrp|djddd ||d<qg||d<qWWdQRXWntk rYnX||fS) z6 get all loaded kernel modules and their dependencies z /proc/modulesrr-,N)openstripsplitappendFileNotFoundError)rmodsdepsflineZsplitsr r r loaded_modules's     zmodules.loaded_modulescCs"tjd|j|j|t|j|gS)Nz %s: %s %s)rdebug2r rr)rmoduler r r load_module<szmodules.load_modulecCs"tjd|j|j|t|j|gS)Nz %s: %s %s)rrr rr)rrr r r unload_module@szmodules.unload_modulecCsT||kr dSx0||D]$}|j|||||kr|j|qW||krP|j|dS)z get all dependants of a module N)get_depsr)rrrretmodr r r r"Dszmodules.get_depscCsg}|j\}}|jd||x*dD]"}||kr$|j||jd|q$Wx^|D]V}|dks|jds|jd s|jd s|jd s|jd s|jd rP|j|||qPW|S)z) get all loaded firewall-related modules Z nf_conntracknf_conntrack_ipv4nf_conntrack_ipv6r ip_tables ip6_tablesebtablesZiptable_Z ip6table_Znf_Zxt_Zipt_Zip6t_)r%r&r)r'r(r))rr"removeinsert startswith)rrZmods2rZbad_bad_moduler$r r r get_firewall_modulesOs    zmodules.get_firewall_modulescCs>x8|jD],}|j|\}}|dkr tjd||fq WdS)z% unload all firewall-related modules rz Failed to unload module '%s': %sN)r-r!rZdebug1)rrZstatusr#r r r unload_firewall_modulesdszmodules.unload_firewall_modulesN) __name__ __module__ __qualname__r r rr r!r"r-r.r r r r rs N) __doc____all__Zfirewall.core.progrZfirewall.core.loggerrZfirewall.configrobjectrr r r r s    PK!J|+__pycache__/fw_service.cpython-36.opt-1.pycnu[3 Yjg@s2dgZddlmZddlmZGdddeZdS)FirewallService)errors) FirewallErrorc@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dS)rcCs||_i|_dS)N)Z_fw _services)selffwr /usr/lib/python3.6/fw_service.py__init__szFirewallService.__init__cCsd|j|jfS)Nz%s(%r)) __class__r)rrrr __repr__ szFirewallService.__repr__cCs|jjdS)N)rclear)rrrr cleanup#szFirewallService.cleanupcCst|jjS)N)sortedrkeys)rrrr get_services(szFirewallService.get_servicescCs||jkrttj|dS)N)rrrZINVALID_SERVICE)rservicerrr check_service+s zFirewallService.check_servicecCs|j||j|S)N)rr)rrrrr get_service/s zFirewallService.get_servicecCs||j|j<dS)N)rname)robjrrr add_service3szFirewallService.add_servicecCs|j||j|=dS)N)rr)rrrrr remove_service6s zFirewallService.remove_serviceN) __name__ __module__ __qualname__r r rrrrrrrrrr rsN)__all__ZfirewallrZfirewall.errorsrobjectrrrrr s  PK!5n  ,__pycache__/fw_policies.cpython-36.opt-1.pycnu[3 Yj @sVdgZddlmZddlmZddlmZddlmZddlm Z Gddde Z dS) FirewallPolicies)config)log)LockdownWhitelist)errors) FirewallErrorc@sDeZdZddZddZddZddZd d Zd d Zd dZ dS)rcCsd|_ttj|_dS)NF) _lockdownrrZLOCKDOWN_WHITELISTlockdown_whitelist)selfr !/usr/lib/python3.6/fw_policies.py__init__szFirewallPolicies.__init__cCsd|j|j|jfS)Nz %s(%r, %r)) __class__rr )r r r r __repr__#s zFirewallPolicies.__repr__cCsd|_|jjdS)NF)rr cleanup)r r r r r'szFirewallPolicies.cleanupcCs|dkr2tjd||jj|rtjddSn|dkrdtjd||jj|rtjddSnb|dkrtjd ||jj|rtjd dSn0|d krtjd ||jj|rtjd dSdS)Ncontextz#Doing access check for context "%s"zcontext matches.TZuidzDoing access check for uid %dz uid matches.userz Doing access check for user "%s"z user matches.Zcommandz#Doing access check for command "%s"zcommand matches.F)rZdebug2r Z match_contextZdebug3Z match_uidZ match_userZ match_command)r keyvaluer r r access_check-s*        zFirewallPolicies.access_checkcCs|jrttjdd|_dS)Nzenable_lockdown()T)rrrZALREADY_ENABLED)r r r r enable_lockdownDs z FirewallPolicies.enable_lockdowncCs|jsttjdd|_dS)Nzdisable_lockdown()F)rrrZ NOT_ENABLED)r r r r disable_lockdownIs z!FirewallPolicies.disable_lockdowncCs|jS)N)r)r r r r query_lockdownNszFirewallPolicies.query_lockdownN) __name__ __module__ __qualname__r rrrrrrr r r r rsN) __all__ZfirewallrZfirewall.core.loggerrZ#firewall.core.io.lockdown_whitelistrrZfirewall.errorsrobjectrr r r r s      PK!QQ__pycache__/rich.cpython-36.pycnu[3 Yj8@sdddddddddd d d d d ddddgZddlmZddlmZddlmZddlmZddlm Z Gddde Z Gddde Z Gddde Z Gddde ZGdddeZGddde ZGddde ZGddde ZGd dde ZGd!d d e ZGd"d d e ZGd#d d e ZGd$d d e ZGd%d d e ZGd&ddeZGd'dde Zd(d)d/d1d+ZGd,dde ZGd-dde Zd.S)2 Rich_SourceRich_Destination Rich_Service Rich_Port Rich_ProtocolRich_MasqueradeRich_IcmpBlock Rich_IcmpTypeRich_SourcePortRich_ForwardPortRich_Log Rich_Audit Rich_Accept Rich_Reject Rich_Drop Rich_Mark Rich_Limit Rich_Rule) functions)check_ipset_name) REJECT_TYPES)errors) FirewallErrorc@seZdZdddZddZdS)rFcCs||_|jdkrd|_||_|jdks0|jdkr8d|_n|jdk rN|jj|_||_|jdkrdd|_||_|jdkr|jdkr|jdkrttjddS)Nzno address, mac and ipset)addrmacupperipsetinvertrr INVALID_RULE)selfrrrrr!/usr/lib/python3.6/rich.py__init__$s    zRich_Source.__init__cCsjd|jr dnd}|jdk r*|d|jS|jdk rB|d|jS|jdk rZ|d|jSttjddS)Nz source%s z NOTrz address="%s"zmac="%s"z ipset="%s"zno address, mac and ipset)rrrrrrr)r retr!r!r"__str__5s   zRich_Source.__str__N)F)__name__ __module__ __qualname__r#r%r!r!r!r"r#s c@seZdZdddZddZdS)rFcCsV||_|jdkrd|_||_|jdkr,d|_||_|jdkrR|jdkrRttjddS)Nrzno address and ipset)rrrrrr)r rrrr!r!r"r#Bs  zRich_Destination.__init__cCsRd|jr dnd}|jdk r*|d|jS|jdk rB|d|jSttjddS)Nzdestination%s z NOTrz address="%s"z ipset="%s"zno address and ipset)rrrrrr)r r$r!r!r"r%Ns  zRich_Destination.__str__N)F)r&r'r(r#r%r!r!r!r"rAs c@seZdZddZddZdS)rcCs ||_dS)N)name)r r)r!r!r"r#YszRich_Service.__init__cCs d|jS)Nzservice name="%s")r))r r!r!r"r%\szRich_Service.__str__N)r&r'r(r#r%r!r!r!r"rXsc@seZdZddZddZdS)rcCs||_||_dS)N)portprotocol)r r*r+r!r!r"r#`szRich_Port.__init__cCsd|j|jfS)Nzport port="%s" protocol="%s")r*r+)r r!r!r"r%dszRich_Port.__str__N)r&r'r(r#r%r!r!r!r"r_sc@seZdZddZdS)r cCsd|j|jfS)Nz#source-port port="%s" protocol="%s")r*r+)r r!r!r"r%hszRich_SourcePort.__str__N)r&r'r(r%r!r!r!r"r gsc@seZdZddZddZdS)rcCs ||_dS)N)value)r r,r!r!r"r#mszRich_Protocol.__init__cCs d|jS)Nzprotocol value="%s")r,)r r!r!r"r%pszRich_Protocol.__str__N)r&r'r(r#r%r!r!r!r"rlsc@seZdZddZddZdS)rcCsdS)Nr!)r r!r!r"r#tszRich_Masquerade.__init__cCsdS)N masquerader!)r r!r!r"r%wszRich_Masquerade.__str__N)r&r'r(r#r%r!r!r!r"rssc@seZdZddZddZdS)rcCs ||_dS)N)r))r r)r!r!r"r#{szRich_IcmpBlock.__init__cCs d|jS)Nzicmp-block name="%s")r))r r!r!r"r%~szRich_IcmpBlock.__str__N)r&r'r(r#r%r!r!r!r"rzsc@seZdZddZddZdS)rcCs ||_dS)N)r))r r)r!r!r"r#szRich_IcmpType.__init__cCs d|jS)Nzicmp-type name="%s")r))r r!r!r"r%szRich_IcmpType.__str__N)r&r'r(r#r%r!r!r!r"rsc@seZdZddZddZdS)r cCs<||_||_||_||_|jdkr(d|_|jdkr8d|_dS)Nr)r*r+to_port to_address)r r*r+r.r/r!r!r"r#s  zRich_ForwardPort.__init__cCs<d|j|j|jdkrd|jnd|jdkr4d|jndfS)Nz(forward-port port="%s" protocol="%s"%s%srz to-port="%s"z to-addr="%s")r*r+r.r/)r r!r!r"r%szRich_ForwardPort.__str__N)r&r'r(r#r%r!r!r!r"r s c@seZdZdddZddZdS)r NcCs||_||_||_dS)N)prefixlevellimit)r r0r1r2r!r!r"r#szRich_Log.__init__cCs>d|jrd|jnd|jr$d|jnd|jr6d|jndfS)Nz log%s%s%sz prefix="%s"rz level="%s"z %s)r0r1r2)r r!r!r"r%szRich_Log.__str__)NNN)r&r'r(r#r%r!r!r!r"r s c@seZdZdddZddZdS)r NcCs ||_dS)N)r2)r r2r!r!r"r#szRich_Audit.__init__cCsd|jrd|jndS)Nzaudit%sz %sr)r2)r r!r!r"r%szRich_Audit.__str__)N)r&r'r(r#r%r!r!r!r"r s c@seZdZdddZddZdS)r NcCs ||_dS)N)r2)r r2r!r!r"r#szRich_Accept.__init__cCsd|jrd|jndS)Nzaccept%sz %sr)r2)r r!r!r"r%szRich_Accept.__str__)N)r&r'r(r#r%r!r!r!r"r s c@s&eZdZdddZddZddZdS) rNcCs||_||_dS)N)typer2)r Z_typer2r!r!r"r#szRich_Reject.__init__cCs,d|jrd|jnd|jr$d|jndfS)Nz reject%s%sz type="%s"rz %s)r3r2)r r!r!r"r%szRich_Reject.__str__cCsT|jrP|sttjd|dkrP|jt|krPdjt|}ttjd|j|fdS)Nz9When using reject type you must specify also rule family.ipv4ipv6z, z%Wrong reject type %s. Use one of: %s.)r4r5)r3rrrrjoin)r familyZ valid_typesr!r!r"checks zRich_Reject.check)NN)r&r'r(r#r%r8r!r!r!r"rs c@seZdZddZdS)rcCsd|jrd|jndS)Nzdrop%sz %sr)r2)r r!r!r"r%szRich_Drop.__str__N)r&r'r(r%r!r!r!r"rsc@s&eZdZdddZddZddZdS) rNcCs||_||_dS)N)setr2)r Z_setr2r!r!r"r#szRich_Mark.__init__cCsd|j|jrd|jndfS)Nz mark set=%s%sz %sr)r9r2)r r!r!r"r%szRich_Mark.__str__cCs|jdk r|j}n ttjdd|krv|jd}t|dkrHttj|tj|d shtj|d rttj|ntj|sttj|dS)Nz no value set/r)r9rrZ INVALID_MARKsplitlenrZ checkUINT32)r xsplitsr!r!r"r8s      zRich_Mark.check)N)r&r'r(r#r%r8r!r!r!r"rs r<<)smhdc@seZdZdddZddZeddZejddZed d Zejd d Ze d d Z ddZ e ddZ ddZ ddZdS)rNcCs||_||_dS)N)r,burst)r r,rGr!r!r"r#szRich_Limit.__init__cCs|j|jdS)N) value_parse burst_parse)r r!r!r"r8szRich_Limit.checkcCs|jS)N)_value)r r!r!r"r,szRich_Limit.valuec Csf|dkrd|_dSy|j|\}}Wntk r<|}YnX|d|}t|dd|krb||_dS)Nr:rJ)rJ _value_parsergetattr)r r,ratedurationvr!r!r"r,s cCs|jS)N)_burst)r r!r!r"rG szRich_Limit.burstc Cs\|dkrd|_dSy|j|}Wntk r8|}Yn Xt|}t|dd|krX||_dS)NrP)rP _burst_parserstrrL)r rGbr!r!r"rGs c Csd}d|kr|jd}| s(t|dkr4ttj||\}}y t|}Wnttj|YnX|dkrv|dd}|dks|dkrttj|d t||d krttjd|f|dkr|d krttjd|f||fS)Nr:r;secondminutehourdayr<rCrDrErFi'rz %s too fastz %s too slow)rTrUrVrW)rCrDrErF)r=r>rr INVALID_LIMITintDURATION_TO_MULT)r,r@rMrNr!r!r"rKs&     zRich_Limit._value_parsecCs |j|jS)N)rKrJ)r r!r!r"rH:szRich_Limit.value_parsec CsR|dkr dSy t|}Wnttj|YnX|dksB|dkrNttj||S)Nr<i)rYrrrX)rGrSr!r!r"rQ=s  zRich_Limit._burst_parsecCs |j|jS)N)rQrP)r r!r!r"rIKszRich_Limit.burst_parsecCs,d|jd}|jdk r(|d|j7}|S)Nz limit value=""z burst=)rJrP)r rCr!r!r"r%Ns zRich_Limit.__str__)N)r&r'r(r#r8propertyr,setterrG staticmethodrKrHrQrIr%r!r!r!r"rs     c@s>eZdZdZdZdddZddZd d Zd d Zd dZ dS)riiNrcCsV|dk rt||_nd|_||_d|_d|_d|_d|_d|_d|_|rR|j |dS)N) rRr7prioritysource destinationelementlogauditaction_import_from_string)r r7rule_strr_r!r!r"r#Xs zRich_Rule.__init__cCsg}x|tj|D]n}d|krp|jd}t|dksF|d sF|d rVttjd||j|d|ddq|jd|iqW|jddi|S) z Lexical analysis =r;rr<zinternal error in _lexer(): %s) attr_name attr_valuerbEOL)rZ splitArgsr=r>rrrappend)r rgtokensrattrr!r!r"_lexeris   zRich_Rule._lexerc Cs|sttjdtj|}d|_d|_d|_d|_d|_ d|_ d|_ d|_ |j |}|rv|djddkrvttjdi}g}d}x`||jddko|dgks||jd}||jd}||jd}|r|dHkrttjd|n|dIkr|dkr|jrttjd+n|dkr<|jr|d <nBt|jd |jd |jd |jd d?|_|j|j|d2}n| dkr,|dOkr|||<nN|dPkrd>|d <n:t|jd |jd |jd d?|_|j|j|d2}n| dkrd|dkrTt||_ |jn ttjd@nv| dkr|dkrt||_ |jn ttjdAn>| dkr|dQkr|||<n0t|jd|jd|_ |j|j|d2}n| dkr&|dkrt||_ |jn ttjdBn| dkr^|dkrNt||_ |jn ttjdCn|| dkrt|_ |j|j|d2}nN| d kr|dRkr|||<n@t|jd|jd|jd|jd|_ |j|j|d2}n| d!kr@|dSkr |||<n0t|jd|jd|_ |j|j|d2}n| d"kr|dTkr^|||<nN|d(krt|jd(n8t |jd|jd|jd(|_ |j|j|d2}n*| d#kr|d(kr|jd(n(t!|jd(|_ |j|j|d2}n| d$krH|d(kr|jd(n(t"|jd(|_ |j|j|d2}n| d%kr|d(krh|jd(n(t#|jd(|_ |j|j|d2}nF| d&kr|dkr|||<nF|d(kr|jd(n0t$|jd|jd(|_ |j|j|d2}n| d'kr`|dkr|||<nF|d(kr.|jd(n0t%|jd|jd(|_ |j|j|d2}nz| d(kr|dUkr||dD|<nVdE|krttjdFt&|dE|jdG|d(<|jdEd|jdGd|j|d2}|d2}qW|j'dS)VNz empty rulerrbrkrulerirjr_r7addressrrrr,r*r+to-portto-addrr)r0r1r3r9rGzbad attribute '%s'r`raservice icmp-block icmp-typer- forward-port source-portrcrdacceptdroprejectmarkr2notNOTzmore than one 'source' elementz#more than one 'destination' elementzFmore than one element. There cannot be both '%s' and '%s' in one rule.zmore than one 'log' elementzmore than one 'audit' elementzOmore than one 'action' element. There cannot be both '%s' and '%s' in one rule.zunknown element %sr<rz0'family' outside of rule. Use 'rule family=...'.z4'priority' outside of rule. Use 'rule priority=...'.z:'%s' outside of any element. Use 'rule %s= ...'.z,'%s' outside of rule. Use 'rule ... %s ...'.r4r5zH'family' attribute cannot have '%s' value. Use 'ipv4' or 'ipv6' instead.z(invalid 'priority' attribute value '%s'.zdwrong 'protocol' usage. Use either 'rule protocol value=...' or 'rule [forward-]port protocol=...'.zDattribute '%s' outside of any element. Use 'rule %s= ...'.TFzinvalid 'protocol' elementzinvalid 'service' elementzinvalid 'icmp-block' elementzinvalid 'icmp-type' elementzlimit.z limit.valuezinvalid 'limit' elementz limit.burst)r_r7rrrrrr,r*r+rsrtr)r0r1r3r9rG)rqr`rar+rur*rvrwr-rxryrcrdrzr{r|r}r2r~rrk)r+rur*rvrwr-rxry)rzr{r|r})r4r5)rrrrr)r~r)rrrr)r~r)r*r+)r*r+rsrt)r*r+)r0r1)r,rG)(rrrrZstripNonPrintableCharactersr_r7r`rarbrcrdrerpgetr>rlrY ValueErrorINVALID_PRIORITYrpopclearrrrrrrrr r r r r rrrrr8) r rgrmZattrsZ in_elementsindexrbrirjZ in_elementZerr_msgr!r!r"rfzs    ""               *      "                          (                                            zRich_Rule._import_from_stringc Cs`|jdk r"|jd kr"ttj|j|jdkrn|jdk rB|jjdk sL|jdk rVttjt|j t krnttj|j |j ks|j |j krttjd|j |j f|j dko|jdks|jdk o|j dkr |jdkrttjd|jdko|jdko|j dkr ttjdt|j tt tgkrP|jdkrP|jdkrP|jdkrPttjd|jdk rj|jjdk r|jdkrttj|jjdk rttjd|jjdk rttjd tj|j|jjsjttjt|jjn|jjdk r,|jjdk rttjd tj|jjsjttjt|jjn>|jjdk r^t|jjsjttjt|jjn ttjd |jdk r|jjdk r|jdkrttj|jjdk rttjd tj|j|jjsttjt|jjn>|jjdk rt|jjsttjt|jjn ttjd t|j t krd|j j!dksLt"|j j!d kr`ttj#t|j j!nt|j t$krtj%|j j&sttj'|j j&|j j(d!kr`ttj)|j j(nt|j t*krtj+|j j,s`ttj)|j j,nvt|j tkr<|jdk rttjd|jdk r`|jjdk r`ttjdn$t|j tkr|j j!dkslt"|j j!d krttj-t|j j!|jr`ttjdnt|j t.kr|j j!dkst"|j j!d kr`ttj-t|j j!nt|j t krtj%|j j&sttj'|j j&|j j(d"kr.ttj)|j j(|j j/dkrZ|j j0dkrZttj'|j j/|j j/dkrtj%|j j/ rttj'|j j/|j j0dkrtj1|j|j j0 rttj|j j0|jdkrttj|jdk r`ttjdnrt|j t2kr>tj%|j j&sttj'|j j&|j j(d#kr`ttj)|j j(n"|j dk r`ttjdt|j |jdk r|jj3r|jj3d$krttj4|jj3|jj5dk r|jj5j6|jdk rt|jt7t8t9gkrttj:t|j|jj5dk r|jj5j6|jdk r\t|jt8kr(|jj6|jnt|jt;krB|jj6|jj5dk r\|jj5j6dS)%Nr4r5z/'priority' attribute must be between %d and %d.rzno element, no actionz%no element, no source, no destinationzno action, no log, no auditzaddress and maczaddress and ipsetz mac and ipsetzinvalid sourcezinvalid destinationr<tcpudpsctpdccpzmasquerade and actionzmasquerade and mac sourcezicmp-block and actionrzforward-port and actionzUnknown element %semergalertcriterrorwarningnoticeinfodebug)r4r5)rrrr)rrrr)rrrr)rrrrrrrr)ZINVALID_SERVICErZ check_portr*Z INVALID_PORTr+ZINVALID_PROTOCOLrZ checkProtocolr,ZINVALID_ICMPTYPErr.r/Zcheck_single_addressr r1ZINVALID_LOG_LEVELr2r8r rrZINVALID_AUDIT_TYPEr)r r!r!r"r8hs                                          zRich_Rule.checkcCsd}|jr|d|j7}|jr,|d|j7}|jr@|d|j7}|jrT|d|j7}|jrh|d|j7}|jr||d|j7}|jr|d|j7}|jr|d|j7}tj rtj |S|S)Nrqz priority="%d"z family="%s"z %s) r_r7r`rarbrcrdrerZPY2Zu2b)r r$r!r!r"r%s$zRich_Rule.__str__i)NNr) r&r'r(rrr#rprfr8r%r!r!r!r"rTs o-NiiiQ)__all__ZfirewallrZfirewall.core.ipsetrZfirewall.core.baserrZfirewall.errorsrobjectrrrrr rrrrr r r r rrrrZrrr!r!r!r"s@      dPK!Xm (__pycache__/watcher.cpython-36.opt-1.pycnu[3 Yj @s*dgZddlmZmZGdddeZdS)Watcher)GioGLibc@sdeZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ ddZ ddZ dS)rcCs"||_||_i|_i|_g|_dS)N) _callback_timeout _monitors _timeouts_blocked)selfcallbackZtimeoutr /usr/lib/python3.6/watcher.py__init__s zWatcher.__init__cCs:tjj|}|jtjjd|j|<|j|jd|jdS)Nchanged) rFile new_for_pathZmonitor_directoryFileMonitorFlagsNONErconnect_file_changed_cb)r Z directorygfiler r r add_watch_dir"s zWatcher.add_watch_dircCs:tjj|}|jtjjd|j|<|j|jd|jdS)Nr) rrrZ monitor_filerrrrr)r filenamerr r r add_watch_file(s zWatcher.add_watch_filecCs |jjS)N)rkeys)r r r r get_watches.szWatcher.get_watchescCs ||jkS)N)r)r rr r r has_watch1szWatcher.has_watchcCs |j|=dS)N)r)r rr r r remove_watch4szWatcher.remove_watchcCs||jkr|jj|dS)N)r append)r rr r r block_source7s zWatcher.block_sourcecCs||jkr|jj|dS)N)r remove)r rr r r unblock_source;s zWatcher.unblock_sourcecCs4x.t|jjD]}tj|j||j|=qWdS)N)listrrr source_remove)r rr r r clear_timeouts?szWatcher.clear_timeoutscCs ||jkr|j||j|=dS)N)r rr)r rr r r _call_callbackDs  zWatcher._call_callbackcCs|j}||jkr8||jkr4tj|j||j|=dS|tjjksh|tjjksh|tjj ksh|tjj kr||jkrtj|j||j|=tj |j |j ||j|<dS)N)Zget_parse_namer rrr#rZFileMonitorEventZCHANGEDZCREATEDZDELETEDZATTRIBUTE_CHANGEDZtimeout_add_secondsrr%)r ZmonitorZgio_fileZgio_other_fileZeventrr r r rIs       zWatcher._file_changed_cbN)__name__ __module__ __qualname__rrrrrrrr!r$r%rr r r r rsN)__all__Z gi.repositoryrrobjectrr r r r sPK!{0z__pycache__/prog.cpython-36.pycnu[3 Yj@sddlZdgZdddZdS)NrunProgc Cs|dkr g}|g|}d}|r@t|d}|jj}WdQRXddi}y tj|tjtjtjd|d}Wntk r|d SX|j|\}} |j dd }|j |fS) NrZLANGCT)stdinstderrstdoutZ close_fdsenvzutf-8replace)r r ) openreadencode subprocessPopenPIPEZSTDOUTOSErrorZ communicatedecode returncode) progargvrargsZ input_stringZhandlerZprocessoutputZ err_outputr/usr/lib/python3.6/prog.pyrs$    )NN)r__all__rrrrrsPK!pnWMlMl#__pycache__/fw.cpython-36.opt-1.pycnu[3 Yj@sdgZddlZddlZddlZddlZddlZddlmZddlm Z ddl m Z ddl m Z ddl m Z ddl mZdd l mZdd lmZdd lmZdd lmZdd lmZddlmZddlmZddlmZddlmZddl m!Z!ddl"m#Z#ddl$m%Z%m&Z&ddl'm(Z(ddl)m*Z*ddl+m,Z,ddl-m.Z.ddl/m0Z0ddl1m2Z2m3Z3ddl4m5Z5ddl6m7Z7ddl8m9Z9ddl:m;Z;ddlmZ>Gd!dde?Z@dS)"FirewallN)config) functions) ipXtables)ebtables)nftables)ipset)modules)FirewallIcmpType)FirewallService) FirewallZone)FirewallDirect)FirewallConfig)FirewallPolicies) FirewallIPSet)FirewallTransaction)FirewallHelper)FirewallPolicy)nm_get_bus_namenm_get_interfaces_in_zone)log)firewalld_conf)Direct)service_reader)icmptype_reader) zone_readerZone) ipset_reader) IPSET_TYPES) helper_reader) policy_reader)errors) FirewallErrorc@seZdZdeddZddZddZdd Zd d Zdfd d ZddZ dgddZ ddZ ddZ ddZ ddZddZddZddZd d!Zd"d#Zd$d%Zd&d'Zdhd)d*Zdid+d,Zd-d.Zdjd/d0Zdkd1d2Zdld3d4Zd5d6Zd7d8Zd9d:Zd;d<Zd=d>Z d?d@Z!dAdBZ"dCdDZ#dEdFZ$dGdHZ%dIdJZ&dKdLZ'dMdNZ(dmdOdPZ)dQdRZ*dSdTZ+dUdVZ,dWdXZ-dYdZZ.d[d\Z/d]d^Z0d_d`Z1dadbZ2dcddZ3d(S)nrFcCsttj|_||_|jr>d|_d|_d|_d|_t |_ d|_ nrt j ||_d|_g|_t j||_d|_g|_tj|_d|_tj|_d|_g|_ tj||_d|_ tj|_t||_t||_t||_ t!||_"t#||_t$|_%t&||_t'||_(t)||_*|j+dS)NFT),rrFIREWALLD_CONF_firewalld_conf_offlineip4tables_enabledip6tables_enabledebtables_enabled ipset_enabledripset_supported_typesnftables_enabledr ip4tablesip4tables_backendipv4_supported_icmp_types ip6tablesip6tables_backendipv6_supported_icmp_typesrebtables_backendr ipset_backendrnftables_backendr modules_backendr icmptyper servicer zoner directrrpoliciesrrhelperrpolicy_Firewall__init_vars)selfZoffliner?/usr/lib/python3.6/fw.py__init__CsB               zFirewall.__init__cCsDd|j|j|j|j|j|j|j|j|j|j |j |j |j |j |jfS)Nz:%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)) __class__r&r'r(_state_panic _default_zone_module_refcount_markscleanup_on_exitcleanup_modules_on_exitipv6_rpfilter_enabledr)_individual_calls _log_denied)r>r?r?r@__repr__ks   zFirewall.__repr__cCsjd|_d|_d|_i|_g|_tj|_tj|_ tj |_ tj |_ tj|_tj|_tj|_tj|_tj|_dS)NZINITF)rCrDrErFrGrZFALLBACK_CLEANUP_ON_EXITrHZ FALLBACK_CLEANUP_MODULES_ON_EXITrIZFALLBACK_IPV6_RPFILTERrJZFALLBACK_INDIVIDUAL_CALLSrKZFALLBACK_LOG_DENIEDrLZFALLBACK_FIREWALL_BACKEND_firewall_backendZFALLBACK_FLUSH_ALL_ON_RELOAD_flush_all_on_reloadZFALLBACK_RFC3964_IPV4 _rfc3964_ipv4ZFALLBACK_ALLOW_ZONE_DRIFTING_allow_zone_drifting)r>r?r?r@Z __init_varstszFirewall.__init_varscCs|jr$d|jjkr$tjdd|_|jrHd|jjkrHtjdd|_|jrld|jjkrltjdd|_|j r|j r|j rtj dt j ddS)Nfilterziptables is not usable.Fzip6tables is not usable.zebtables is not usable.zNo IPv4 and IPv6 firewall.) r&r-get_available_tablesrinfo1r'r0r(r2r+fatalsysexit)r>r?r?r@ _check_tabless     zFirewall._check_tablesc Cszy|jjWn*tk r8tjdd|_g|_YnX|jj|_|jj |jj s||jj rltjdntjdd|_ |j r|jjd|_n|j r|jj|_ng|_|jj |jj s|jj rtjdntjdd|_|j r|jjd|_n|jr|jj|_ng|_|jj |jj sN|jj r>tjd ntjd d|_|jrv|j rv|jj rvtjd dS) Nz4ipset not usable, disabling ipset usage in firewall.FzFiptables-restore is missing, using individual calls for IPv4 firewall.zCiptables-restore and iptables are missing, disabling IPv4 firewall.ipv4zGip6tables-restore is missing, using individual calls for IPv6 firewall.zEip6tables-restore and ip6tables are missing, disabling IPv6 firewall.ipv6zHebtables-restore is missing, using individual calls for bridge firewall.zEebtables-restore and ebtables are missing, disabling bridge firewall.zSebtables-restore is not supporting the --noflush option, will therefore not be used)r3Zset_list ValueErrorrwarningr)r*Zset_supported_typesr-Z fill_existsrestore_command_existsZcommand_existsr&r+r4Zsupported_icmp_typesr.r0r'r1r2r(rKrestore_noflush_optiondebug1)r>r?r?r@ _start_checksL               zFirewall._start_checkc>Cstj}tjdtjy|jjWn8tk rZ}ztj|tjdWYdd}~Xn"X|jj drt|jj d}|jj dr|jj d}|dk r|j dBkrd|_ tjd|j |jj d r|jj d }|dk r|j dCkrd |_ |dk r|j dDkrd|_ tjd |j |jj drv|jj d}|dk rv|j dEkrvtjdy|j jWntk rtYnX|jj dr|jj d}|dk r|j dFkrd|_|j dGkrd |_|jrtjdn tjd|jj dr"|jj d}|dk r"|j dHkr"tjdd |_|jj drt|jj d}|dksT|j dkr\d|_n|j |_tjd|j|jj dr|jj d|_tjd|j|jj dr|jj d}|j dIkrd|_nd |_tjd|j|jj dr&|jj d}|j dJkrd|_nd |_tjd|j|jj dr||jj d}|j dKkrVd|_nd |_|jsntjdtjd |j|jjtj|j|j|j|js|jtjd!y|j jjWnZtk r }z<|j jrtjd"|j jj |ntjd"|j jj |WYdd}~XnX|jj!tj|j |j"tj#d#|j"tj$d#|j"tj%d$|j"tj&d$t'|j(j)d%krtjd&|j"tj*d'|j"tj+d'|j"tj,d(|j"tj-d(t'|j.j/d%krtjd)|j"tj0d*|j"tj1d*t'|j2j3d%kr&tj4d+t5j6d,|j"tj7d-|j"tj8d-d}x.dLD]&}||j2j3krLtj4d1|d }qLW|rt5j6d,||j2j3krd2|j2j3krd2}nd3|j2j3krd3}nd.}tjd4|||}n tjd5|t9tj:} t;jj?| |jj@tj| |jA||_B|jrdS|jCtjDd%krtEjE} tF|} |s|jG| d8|r|s|jHr|jIjJr| jKd | jL|r|rtjd9|jMjN|jO| d8| jKd | jL|jHrX|jIjJrXtjd:|jIjPtjd;|jQ| d8tjd<|j2jR| d8|j2jSd|jB| d8tjd=|jTjU| d8| jKd | jL|j>jVrVtjd>|j>jW| y| jKd | jLWnXtk r>} z$t| jXd?| jYr&| jYnd@WYdd} ~ Xntk rTYnX~ tjDd,krtEjE} tjZdA| | dS)MNz"Loading firewalld config file '%s'z0Using fallback firewalld configuration settings. DefaultZoneZ CleanupOnExitnofalseFzCleanupOnExit is set to '%s'ZCleanupModulesOnExityestrueTz#CleanupModulesOnExit is set to '%s'ZLockdownzLockdown is enabledZ IPv6_rpfilterzIPv6 rpfilter is enabledzIPV6 rpfilter is disabledZIndividualCallszIndividualCalls is enabled LogDeniedZoffzLogDenied is set to '%s'ZFirewallBackendzFirewallBackend is set to '%s'ZFlushAllOnReloadzFlushAllOnReload is set to '%s'Z RFC3964_IPv4zRFC3964_IPv4 is set to '%s'ZAllowZoneDriftingzAllowZoneDrifting is enabled. This is considered an insecure configuration option. It will be removed in a future release. Please consider disabling it now.z AllowZoneDrifting is set to '%s'zLoading lockdown whitelistz*Failed to load lockdown whitelist '%s': %srr6rzNo icmptypes found.r;r7zNo services found.r8zNo zones found.rTr<blockdroptrustedzZone '%s' is not available.ZpublicZexternalz+Default zone '%s' is not valid. Using '%s'.zUsing default zone '%s'zLoading direct rules file '%s'z)Failed to load direct rules file '%s': %s)use_transactionzUnloading firewall moduleszApplying ipsetszApplying default rule setzApplying used zoneszApplying used policiesz2Applying direct chains rules and passthrough rulesz Direct: %srNz%Flushing and applying took %f seconds)rdre)rfrg)rdre)rfrg)rdre)rfrg)rfrg)rdre)rdre)rdre)rirjrk)[rZ FALLBACK_ZONErrar#r$read Exceptionr^getlowerrHrIr:Zenable_lockdownr"rJrKrLrOrPrQrRr%Zset_firewalld_confcopydeepcopy_select_firewall_backendrbZlockdown_whitelistZquery_lockdownerrorfilenameZ set_policies_loaderZFIREWALLD_IPSETSZETC_FIREWALLD_IPSETSZFIREWALLD_ICMPTYPESZETC_FIREWALLD_ICMPTYPESlenr6 get_icmptypesZFIREWALLD_HELPERSZETC_FIREWALLD_HELPERSZFIREWALLD_SERVICESZETC_FIREWALLD_SERVICESr7 get_servicesZFIREWALLD_ZONESZETC_FIREWALLD_ZONESr8 get_zonesrWrXrYZFIREWALLD_POLICIESZETC_FIREWALLD_POLICIESrZFIREWALLD_DIRECTospathexistsr9Zset_permanent_configZ set_direct check_zonerErZZgetDebugLogLeveltimerflushr)rZ has_ipsetsexecuteclearr5unload_firewall_modulesapply_default_tablesZ apply_ipsetsapply_default_rulesZ apply_zoneschange_default_zoner<Zapply_policiesZhas_configurationZ apply_directcodemsgZdebug2)r>reloadcomplete_reloadZ default_zonervaluertzr8objZtm1 transactioneZtm2r?r?r@_startst                                                           .zFirewall._startc CsHy |jWn&tk r2d|_|jdYnXd|_|jddS)NFAILEDACCEPTRUNNING)rrnrC set_policy)r>r?r?r@starts  zFirewall.startc Cshtjj|sdS|rZ|jtjrV|dkrVt}tjj||_|j |j||_d|_ nd}x|t tj |D]h}|j ds|jtjrl|dkrltjjd||frl|jd||f|ddqld||f}tjd||y|dkrt||}|j|jjkr8|jj|j}tjd ||j|j|j|jj|jn|jjtjrNd|_ y|jj|Wn<tk r} ztjd |jt| fWYdd} ~ XnX|jjtj|n|d krFt||}|j|jjkr|jj |j}tjd ||j|j|j|jj!|jn|jjtjr$d|_ |jj"||jj"tj|n.|dkrnt#|||d }|rdtjj|tjj|d df|_|j |jtj|} |j|j$j%kr|j$j&|j}|j$j'|j|j(rtjd||j|||j)|ntjd ||j|j|jn|jjtjr,d|_ d| _ |jj*| |r^tjd||j|||j)|n |j$j*|n|dkrDt+||}|j|j,j-kr|j,j.|j}tjd ||j|j|j|j,j/|jn|jjtjrd|_ y|j,j0|Wn<tk r,} ztj1d |jt| fWYdd} ~ XnX|jj0tj|n0|dkrt2||}|j|j3j4kr|j3j5|j}tjd ||j|j|j|j3j6|jn|jjtjrd|_ |j3j7||jj7tj|n|dkrht8||}|j|j9j:kr2|j9j;|j}tjd ||j|j|j|j9j<|jn|jjtjrHd|_ |j9j=||jj>tj|n tj?d|Wqltk r} ztj@d||| WYdd} ~ XqltAk rtj@d||tjBYqlXqlW|rd|j(rd|j|j$j%krX|j$j&|j}tjd||j|j|jy|j$j'|jWntAk rHYnX|jjC|j|j$j*|dS)Nr8Fz.xmlz%s/%sT)combinezLoading %s file '%s'r6z Overloads %s '%s' ('%s/%s')z%s: %s, ignoring for run-time.r7)Z no_check_namerz Combining %s '%s' ('%s/%s')rr;r<zUnknown reader type %szFailed to load %s file '%s': %szFailed to load %s file '%s':z0 Overloading and deactivating %s '%s' ('%s/%s'))Dr{r|isdir startswithrZ ETC_FIREWALLDrbasenamenameZ check_namedefaultsortedlistdirendswithrvrrarr6rxZ get_icmptyperuZremove_icmptypeZ add_icmptyper"rVstrrqrrrr7ryZ get_serviceZremove_serviceZ add_servicerr8rzZget_zoneZ remove_zonecombinedrZadd_zonerr get_ipsets get_ipsetZ remove_ipset add_ipsetr^rr;Z get_helpersZ get_helperZ remove_helperZ add_helperr r< get_policiesZ get_policyZ remove_policyZ add_policyZadd_policy_objectrWrtrnZ exceptionZ forget_zone) r>r|Z reader_typerZ combined_zonerurrZorig_objrtZ config_objrr?r?r@rvs       $             $       zFirewall._loadercCsp|jj|jj|jj|jj|jj|jj|jj|jj|j j|j j|j dS)N) r6cleanupr7r8rr;rr9r:r<r$r=)r>r?r?r@rs          zFirewall.cleanupcCsN|jsB|jr(|j|jj|jd|jrBtjd|jj |j dS)Nrz!Unloading firewall kernel modules) r%rHrrrrIrrar5rr)r>r?r?r@stops    z Firewall.stopc Csd}d}xt|D]\}}|r0|jj|\}}n$|j|dkrDd}n|jj|\}}|dkrn|d7}||7}q|r|jj|d|j|d7<q||jkr|j|d8<|j|dkr|j|=qW||fS)NrrNrT) enumerater5 load_modulerFZ unload_module setdefault) r>Z_modulesenableZ num_failedZ error_msgsimoduleZstatusrr?r?r@handle_moduless(  zFirewall.handle_modulescCs|dkrd|_dS)NrF)r+)r>backendr?r?r@rssz!Firewall._select_firewall_backendcCs4x|jD]}|j|kr |Sq Wttjd|dS)Nz'%s' backend does not exist) all_backendsrr"r!Z UNKNOWN_ERROR)r>rrr?r?r@get_backend_by_names  zFirewall.get_backend_by_namecCs\|jr |jS|dkr |jr |jS|dkr4|jr4|jS|dkrH|jrH|jStt j d|dS)Nr[r\ebz-'%s' is not a valid backend or is unavailable) r+r4r&r-r'r0r(r2r"r! INVALID_IPV)r>ipvr?r?r@get_backend_by_ipvszFirewall.get_backend_by_ipvcCsP|dkr|jr|jS|dkr(|jr(|jS|dkr<|jr<|jSttjd|dS)Nr[r\rz-'%s' is not a valid backend or is unavailable) r&r-r'r0r(r2r"r!r)r>rr?r?r@get_direct_backend_by_ipvsz"Firewall.get_direct_backend_by_ipvcCs<|dkr|jS|dkr|jS|dkr*|jS|dkr8|jSdS)Nr,r/rrF)r&r'r(r+)r>rr?r?r@is_backend_enabledszFirewall.is_backend_enabledcCs8|jr dS|dkr|jS|dkr&|jS|dkr4|jSdS)NTr[r\rF)r+r&r'r()r>rr?r?r@is_ipv_enabledszFirewall.is_ipv_enabledcCsRg}|jr|j|jn6|jr*|j|j|jr<|j|j|jrN|j|j|S)N) r+appendr4r&r-r'r0r(r2)r>backendsr?r?r@enabled_backends s   zFirewall.enabled_backendscCsPg}|jr|j|j|jr(|j|j|jr:|j|j|jrL|j|j|S)N) r&rr-r'r0r(r2r+r4)r>rr?r?r@rs    zFirewall.all_backendsNcCsN|dkrt|}n|}x |jD]}|j||jq W|dkrJ|jddS)NT)rr add_rulesZbuild_default_tablesr)r>rlrrr?r?r@r$s zFirewall.apply_default_tablescCs|dkrt|}n|}x(|jD]}|j|j}|j||q W|jdr~|jd}d|jkr~|jr~|j |j}|j|||jdr|j r|j }|j|||dkr|j ddS)Nr\rawT) rrZbuild_default_rulesrLrrrrUrJZbuild_rpfilter_rulesrQZbuild_rfc3964_ipv4_rulesr)r>rlrrrulesZ ipv6_backendr?r?r@r0s"        zFirewall.apply_default_rulescCs|jr|jj rdSdS)NTF)r+r9Zhas_runtime_configuration)r>r?r?r@may_skip_flush_direct_backendsHsz'Firewall.may_skip_flush_direct_backendscCs`|dkrt|}n|}x2|jD]&}||jkr2q |j}|j||q W|dkr\|jddS)NT)rrrbuild_flush_rulesrr)r>rlrrrr?r?r@flush_direct_backendsNs  zFirewall.flush_direct_backendscCsp|dkrt|}n|}tjd|js4|j|dx$|jD]}|j}|j||q>W|dkrl|jddS)NzFlushing rule set)rlT) rrrarrrrrr)r>rlrrrr?r?r@r]s   zFirewall.flushcCs`|dkrt|}n|}tjd|x&|jD]}|j|}|j||q,W|dkr\|jddS)NzSetting policy to '%s'T)rrrarZbuild_set_policy_rulesrr)r>r<rlrrrr?r?r@ros   zFirewall.set_policycCsB|sdS|j|}|s&ttjd||j|s4dS|j||jS)NrNz'%s' is not a valid backend)rr"r!rrset_rulerL)r> backend_namerulerr?r?r@rs   z Firewall.rulecCs"ttd|}|j|}|s,ttjd||j|s:dS|js\|j s\|dkoX|j j rxt |D]\}}y|j ||j Wqftk r}zjtjtjtj|xFt|d|D]2}y|j |j||j Wqtk rYqXqW|WYdd}~XqfXqfWn|j||j dS)Nz'%s' is not a valid backendr)listrSrr"r!rrrKr_r2r`rrrLrnrra traceback format_excrtreversedZ reverse_ruleZ set_rules)r>rrZ_rulesrrrrr?r?r@rs.     zFirewall.rulescCs|jrttjdS)N)rDr"r!Z PANIC_MODE)r>r?r?r@ check_panicszFirewall.check_paniccCs"|}||jjkrttj||S)N)r<rr"r!ZINVALID_POLICY)r>r<Z_policyr?r?r@ check_policys zFirewall.check_policycCs8|}| s|dkr|j}||jjkr4ttj||S)NrN)get_default_zoner8rzr"r!Z INVALID_ZONE)r>r8_zoner?r?r@r~s  zFirewall.check_zonecCstj|sttj|dS)N)rZcheckInterfacer"r!ZINVALID_INTERFACE)r> interfacer?r?r@check_interfaces zFirewall.check_interfacecCs|jj|dS)N)r7 check_service)r>r7r?r?r@rszFirewall.check_servicecCstj|sttj|dS)N)r check_portr"r!Z INVALID_PORT)r>Zportr?r?r@rs zFirewall.check_portcCs*|sttj|dkr&ttjd|dS)Ntcpudpsctpdccpz''%s' not in {'tcp'|'udp'|'sctp'|'dccp'})rrrr)r"r!ZMISSING_PROTOCOLZINVALID_PROTOCOL)r>Zprotocolr?r?r@ check_tcpudps  zFirewall.check_tcpudpcCstj|sttj|dS)N)rZcheckIPr"r! INVALID_ADDR)r>Zipr?r?r@check_ips zFirewall.check_ipcCsP|dkr tj|sLttj|n,|dkr@tj|sLttj|n ttjddS)Nr[r\z'%s' not in {'ipv4'|'ipv6'})rZ checkIPnMaskr"r!rZ checkIP6nMaskr)r>rsourcer?r?r@ check_addresss  zFirewall.check_addresscCs|jj|dS)N)r6check_icmptype)r>Zicmpr?r?r@rszFirewall.check_icmptypecCs>t|tstd|t|ft|dkr:ttjd|dS)Nz%s is %s, expected intrz#timeout '%d' is not positive number) isinstanceint TypeErrortyper"r! INVALID_VALUE)r>Ztimeoutr?r?r@ check_timeouts   zFirewall.check_timeoutc Cs`|j}|j}|sNi}x&|jjD]}|jj|d||<q W|jj}|j}g}x$|jj D]} |j |jj | q^W|s|j d|j |jd} y|jd|dWn&tk r} z | } WYdd} ~ XnX|r(xL|D]D} |jj| jsx0|jjD]"} | jdkrq| j| jqWqW|s|j}||kr||krRi||<xFt||jD]2\}}|drd||||||<|||=qdWxb|jjD]T}||krx.||D]"}|jj|||||dqW||=n tjd|qWt|d kr6x(t|jD]}tjd |||=qW~x|D]} |jj| jrxx| jD]R}y|jj| j|Wn6tk r}z|jt j!kr|WYdd}~XnXqZWn|jj"| |jj#| jq>W|jj$|t%}|r,x@|jjd gD],}x$t&|D]}|jj|||d q WqW||_|jsD|j d | rVd|_'| nd|_'dS)N interfacesZDROPT)rrr __default__senderzNew zone '%s'.rz(Lost zone '%s', zone interfaces dropped.rN)rrrr)(rDrPr8rz get_settingsr9Zget_runtime_configrrrrrrrrrrnZ query_ipsetrrZ set_destroyritemschange_zone_of_interfacerrVrwkeysZentriesZ add_entryr"rr!ALREADY_ENABLEDrZ apply_ipsetZ set_configrrrC)r>rrDZ flush_allZ_zone_interfacesr8Z_direct_config_old_dzZ _ipset_objs_nameZstart_exceptionrrrZ_new_dzifacesettingsZ interface_identryrZ nm_bus_namerr?r?r@rs                zFirewall.reloadcCs|jS)N)rC)r>r?r?r@ get_stateaszFirewall.get_statecCsZ|jrttjdy|jdWn.tk rN}zttj|WYdd}~XnXd|_dS)Nzpanic mode already enabledZPANICT)rDr"r!rrrnCOMMAND_FAILED)r>rr?r?r@enable_panic_modefszFirewall.enable_panic_modecCsZ|jsttjdy|jdWn.tk rN}zttj|WYdd}~XnXd|_dS)Nzpanic mode is not enabledrF)rDr"r!Z NOT_ENABLEDrrnr)r>rr?r?r@disable_panic_modeqszFirewall.disable_panic_modecCs|jS)N)rD)r>r?r?r@query_panic_mode|szFirewall.query_panic_modecCs|jS)N)rL)r>r?r?r@get_log_deniedszFirewall.get_log_deniedcCsb|tjkr&ttjd|djtjf||jkrR||_|jj d||jj n ttj |dS)Nz'%s', choose from '%s'z','rh) rZLOG_DENIED_VALUESr"r!rjoinrrLr$setwriteZ ALREADY_SET)r>rr?r?r@set_log_denieds   zFirewall.set_log_deniedcCs|jS)N)rE)r>r?r?r@rszFirewall.get_default_zonecCs|j|}||jkr|j}||_|jjd||jj|jj|||jj|}x@t|dj D]\}}|drd|jj d|qdWn t t j |dS)NrcrrrN)r~rEr$rrr8rrrrrr"r!ZZONE_ALREADY_SET)r>r8rrZ_old_dz_settingsrrr?r?r@set_default_zones    zFirewall.set_default_zonecCsH|j}x:|jD].\}}|s(t|tr2|||<q||kr||=qW|S)N)rqrrbool)r>Z permanentZruntimerkeyrr?r?r@'combine_runtime_with_permanent_settingss  z0Firewall.combine_runtime_with_permanent_settingscCsi}i}xt|jt|jBD]}||kr"t||trt||krN||ng}tt|||||<t|t||A|@||<q"t||tst||tr|| r||rd||<q||r|| rd||<q"ttjdj t |||q"W||fS)NTFz Unhandled setting type {} key {}) rrrrrrr"r!ZINVALID_SETTINGformatr)r>Z old_settingsZ new_settingsZ add_settingsZremove_settingsroldr?r?r@get_added_and_removed_settingss   z'Firewall.get_added_and_removed_settings)F)FF)F)N)N)N)N)N)F)4__name__ __module__ __qualname__rArMr=rZrbrrrvrrrrsrrrrrrrrrrrrrrrrrr~rrrrrrrrrrrrrrrrrrrr?r?r?r@rBsh ( ;                s  )A__all__Zos.pathr{rXrqrrZfirewallrrZ firewall.corerrrrr Zfirewall.core.fw_icmptyper Zfirewall.core.fw_servicer Zfirewall.core.fw_zoner Zfirewall.core.fw_directr Zfirewall.core.fw_configrZfirewall.core.fw_policiesrZfirewall.core.fw_ipsetrZfirewall.core.fw_transactionrZfirewall.core.fw_helperrZfirewall.core.fw_policyrZfirewall.core.fw_nmrrZfirewall.core.loggerrZfirewall.core.io.firewalld_confrZfirewall.core.io.directrZfirewall.core.io.servicerZfirewall.core.io.icmptyperZfirewall.core.io.zonerrZfirewall.core.io.ipsetrZfirewall.core.ipsetrZfirewall.core.io.helperrZfirewall.core.io.policyr r!Zfirewall.errorsr"objectrr?r?r?r@sH                            PK!gzz__pycache__/base.cpython-36.pycnu[3 Yj6@sdZdZdZd0ZdddedgZddddgZd d d d d ddZddddddddddddddddgd d!d"d#d$d%d&ddg d'Zd(d)d*d+d,d-d.gZd/S)1zBase firewall settingsz{chain}_{zone}ZCONTINUEZACCEPTz %%REJECT%%ZDROPdefaultZREJECTZPREZPOSTINZFWDIZFWDOZOUT)Z PREROUTINGZ POSTROUTINGZINPUTZ FORWARD_INZ FORWARD_OUTZOUTPUTzicmp-host-prohibitedz host-prohibzicmp-net-unreachablez net-unreachzicmp-host-unreachablez host-unreachzicmp-port-unreachablez port-unreachzicmp-proto-unreachablez proto-unreachzicmp-net-prohibitedz net-prohibz tcp-resetztcp-rstzicmp-admin-prohibitedz admin-prohibzicmp6-adm-prohibitedzadm-prohibitedzicmp6-no-routezno-routezicmp6-addr-unreachablez addr-unreachzicmp6-port-unreachable)Zipv4Zipv6zhash:ipz hash:ip,portz hash:ip,markzhash:netz hash:net,portzhash:net,ifacezhash:macN) __doc__ZDEFAULT_ZONE_TARGETZDEFAULT_POLICY_TARGETZDEFAULT_POLICY_PRIORITYZ ZONE_TARGETSZPOLICY_TARGETSZ SHORTCUTSZ REJECT_TYPESZSOURCE_IPSET_TYPESrr/usr/lib/python3.6/base.pys. PK!6 ;;)__pycache__/nftables.cpython-36.opt-1.pycnu[3 YjBu @s6ddlmZddlZddlZddlZddlmZddlmZm Z m Z m Z m Z ddl mZmZmZmZmZmZmZddlmZmZmZmZmZmZmZddlmZmZddl m!Z!d Z"e"d d Z#d Z$d Z%iddde%fidde%fdde%fddde%fdde%fdde%fdde%fddZ&Gddde'Z(dS))absolute_importN)log) check_mac getPortRange normalizeIP6check_single_address check_address) FirewallError UNKNOWN_ERROR INVALID_RULEINVALID_ICMPTYPE INVALID_TYPE INVALID_ENTRY INVALID_PORT) Rich_Accept Rich_Reject Rich_Drop Rich_MarkRich_MasqueradeRich_ForwardPortRich_IcmpBlock) ICMP_TYPES ICMPV6_TYPES)NftablesZ firewalld_Z policy_dropZpolicy_ PREROUTING preroutingdZ postrouting)r POSTROUTINGinputforwardoutput)rINPUTFORWARDOUTPUT)rawmanglenatfilterc@sjeZdZdZdZddZddZddZdd Zd d Z d d Z ddZ dddZ ddZ ddZddZddZdddZddZdd d!Zd"d#Zdd%d&Zdd(d)Zdd*d+Zdd,d-Zd.d/Zd0d1Zd2d3Zd4d5Zd6d7Zd8d9Zd:d;Zdd?Z!d@dAZ"dBdCZ#dDdEZ$dFdGZ%dHdIZ&ddJdKZ'dLdMZ(dNdOZ)dPdQZ*dRdSZ+ddTdUZ,ddVdWZ-ddXdYZ.dZd[Z/dd\d]Z0dd^d_Z1dd`daZ2ddbdcZ3ddddeZ4ddfdgZ5dhdiZ6ddjdkZ7dldmZ8ddndoZ9dpdqZ:drdsZ;dtduZddzd{Z?d|d}Z@dd~dZAddZBddZCddZDddZEddZFddZGddZHdddZIdS)nftablesTcCsb||_d|_g|_i|_i|_i|_i|_i|_gggd|_t |_ |j j d|j j ddS)NT)inetipip6) _fwZrestore_command_existsZavailable_tablesrule_to_handlerule_ref_countrich_rule_priority_countspolicy_priority_countszone_source_index_cachecreated_tablesrr+Zset_echo_outputZset_handle_output)selffwr8/usr/lib/python3.6/nftables.py__init__Xs znftables.__init__cCsxdD]}||krPqWd||dkr`||ddd||dddf}||dd=n(d||dkrd}||dd=ndS||dd }|r|dkr||kr|||kr||j|n|dkr||krg||<|r(|||kr||j|||jd d d ||j|}n|jjr8d }n t||}||}||=|d krf||d<n |d8}||d<||ddd<dS)Naddinsertdeletez%%ZONE_SOURCE%%rulezoneaddressz%%ZONE_INTERFACE%%familycSs|dS)Nrr8)xr8r8r9sz3nftables._run_replace_zone_source..)keyrindex)r;r<r=)removeappendsortrFr/_allow_zone_driftinglen)r6r>r4verbZ zone_sourcerArF _verb_snippetr8r8r9_run_replace_zone_sourcegsD        z!nftables._run_replace_zone_sourcecCsBd|krdtj|diSd|kr4dtj|diSttddS)Nr<r=r;zFailed to reverse rule)copydeepcopyr r )r6dictr8r8r9 reverse_rules znftables.reverse_rulec Csxd D]}||krPqW|||dkr||d|}||d|=t|tkr^ttd||dd||ddf}|dkr||ks|||ks|||dkrttd |||d 8<n||kri||<|||krd|||<d}xVt||jD]B}||kr"|dkr"P||||7}||kr|dkrPqW|||d 7<||} ||=|dkr| |d<n |d 8}| |d<||ddd <dS) Nr;r<r=r>z%priority must be followed by a numberrAchainrz*nonexistent or underflow of priority countrErF)r;r<r=)typeintr r r sortedkeys) r6r>Zpriority_countstokenrLpriorityrSrFprMr8r8r9_set_rule_replace_prioritysD          z#nftables._set_rule_replace_prioritycCsfx`d D]X}||krd||krtj||d}xd D]}||kr6||=q6Wtj|dd }|SqWdS) Nr;r<r=r>rFhandlepositionT)Z sort_keys)r;r<r=)rFr\r])rOrPjsondumps)r6r>rLrule_keyZnon_keyr8r8r9 _get_rule_keys   znftables._get_rule_keycCsLdddddg}dddg}g}g}tj|j}tj|j}tj|j} |jj} x|D]} t| tkrvtt d| x|D]} | | kr|Pq|W| | krtt d| |j | } | | krDt j d|j| | | | dkr| | d 7<qVnX| | d kr | | d 8<qVn6| | d kr,| | d 8<ntt d | | | fn| r\| dkr\d | | <|j| tj| }| rttd|| d d || d d <|j||d |j||d|j|| | dkrdd |dd d|dd d|dd d|j| dii}|j|qVWdddd iig|i}t jdkrVt jd|jtj||jj|\}}}|dkrtdd|tj|f||_||_| |_| |_d}x|D]} |d 7}|j | } | s̐qd| kr|j| =|j| =qx"|D]} | |d|krPqW| |d|kr$q|d|| d d|j| <qWdS)Nr;r<r=flushreplacez#rule must be a dictionary, rule: %szno valid verb found, rule: %sz%s: prev rule ref cnt %d, %srEz)rule ref count bug: rule_key '%s', cnt %dr>exprz%%RICH_RULE_PRIORITY%%z%%POLICY_PRIORITY%%rAtablerS)rArerSr\r+ZmetainfoZjson_schema_versionz.%s: calling python-nftables with JSON blob: %srz'%s' failed: %s JSON blob: %szpython-nftablesr\)rOrPr2r3r4r1rTrQr r r rarZdebug2 __class__rHlistr*r[rNr0ZgetDebugLogLevelZdebug3r^r_r+Zjson_cmd ValueError)r6rules log_deniedZ _valid_verbsZ_valid_add_verbsZ_deduplicated_rulesZ_executed_rulesr2r3r4r1r>rLr`Z_ruleZ json_blobZrcr#errorrFr8r8r9 set_ruless             &         znftables.set_rulescCs|j|g|dS)N)rm)r6r>rkr8r8r9set_rule:sznftables.set_ruleNcCs|r |gStjS)N)IPTABLES_TO_NFT_HOOKrW)r6rer8r8r9get_available_tables>sznftables.get_available_tablescCsFg}x policy_keyrAr8r8r9build_flush_rulesPs    znftables.build_flush_rulesc Cslddd|}g}xTdD]L}|j|ddtd d |fd d d diiddddgididdigdiiqW|S)Nr;r=)TFr!r"r#r>r,z%s_%sr*matchctrDstateinset establishedrelated)leftoprightaccept)rArerSrd)r!r"r#)rHTABLE_NAME_POLICY)r6enableadd_delrjhookr8r8r9rtgs    z)nftables._build_set_policy_rules_ct_rulesc Cstg}|dkrt|jdddtdii|jdjtx>dD]6}|jdddtd d |fd |dtd ddiiq:W|dkr|jdddtdii|jdjtx>dD]6}|jdddtd d |fd |dtd ddiiqW||jd7}nz|dkrfx4|jdD]&}|j|}||jkr |j|q W||jt7}t|jdkrp|jdjtn t t d|S)NZPANICr;rer,)rArrrr#rSz%s_%sr'r*i,rEdrop)rArerrrTrpriopolicyDROPr!r"rTACCEPTFznot implemented)rr#i)r!r"r#) rHrr5NFT_HOOK_OFFSETrtrar0rsrGr r )r6rrjrr>rvr8r8r9build_set_policy_rulestsH               znftables.build_set_policy_rulescCsJt}|dks|dkr$|jtj|dks4|dkrB|jtjt|S)Nipv4ipv6)r|updaterrWrrh)r6ipvZ supportedr8r8r9supported_icmp_typess znftables.supported_icmp_typescCs>g}x4dD],}|jdd|tdii|j|jtq W|S)Nr,r-r.r;re)rArr)r,r-r.)rHrur5)r6Zdefault_tablesrAr8r8r9build_default_tabless   znftables.build_default_tablesoffcCsg}xtdjD]}|jdddtd|ddtd|dtd|d d iixz|jjrld d d dgnd d dgD]X}|jdddtd||fdii|jdddtd|ddd||fiigdiiqvWqWxd?D]}xtdjD]}|jdd|td|ddtd|dtd|d d iix~|jjrJd d d dgnd d dgD]Z}|jdd|td||fdii|jdd|td|ddd||fiigdiiqTWqWqWxVtdjD]F}|jdddtd|ddtd|dtd|d d iiqW|jdddtddddddiid d!d"d#gid$id%digdii|jdddtdddddd&iid d'd$id%digdii|jdddtdddd(dd)iid*d+d$id%digdiix~|jjrd d d dgnd d dgD]Z}|jdddtd,d|fdii|jdddtddddd,d|fiigdiiqW|d-kr|jdddtddddddiid d!d.gid$i|j|d/d0d1iigdii|jdddtddddddiid d!d.gid$id2digdii|d-kr$|jdddtdd|j|d/d0d3iigdii|jdddtddd4d5d6d7igdii|jdddtdd8ddddiid d!d"d#gid$id%digdii|jdddtdd8dddd&iid d'd$id%digdii|jdddtdd8dd(dd)iid*d+d$id%digdiixbd@D]Z}|jdddtd,d8|fdii|jdddtdd8ddd,d8|fiigdiiqWxdAD]}xz|jjrd d gnd gD]^}|jdddtd;d8||fdii|jdddtdd8ddd;d8||fiigdiiqWqvWxbdBD]Z}|jdddtd,d8|fdii|jdddtdd8ddd,d8|fiigdiiqW|d-kr|jdddtdd8ddddiid d!d.gid$i|j|d/d0d1iigdii|jdddtdd8ddddiid d!d.gid$id2digdii|d-kr6|jdddtdd8|j|d/d0d3iigdii|jdddtdd8d4d5d6d7igdii|jdddtdd<ddddiid d!d"d#gid$id%digdii|jdddtd=dd(dd>iid*d+d$id%digdiixbdCD]Z}|jdddtd,d<|fdii|jdddtdd<ddd,d<|fiigdiiqWxbdDD]Z}|jdddtd,d<|fdii|jdddtdd<ddd,d<|fiigdiiqHW|S)ENr(r;rSr,z mangle_%sr*z%srrE)rArerrrTrr POLICIES_preZ ZONES_SOURCEZZONES POLICIES_postz mangle_%s_%s)rArerrr>jumptarget)rArerSrdr-r.r)znat_%sz nat_%s_%sz filter_%sr$rxryrDrzr{r|r}r~)rrrrZstatusdnatmetaiifnamez==loz filter_%s_%srZinvalidrprefixzSTATE_INVALID_DROP: rzFINAL_REJECT: rejecticmpxzadmin-prohibited)rTrdr%INOUTzfilter_%s_%s_%sr& filter_OUTPUToifname)r-r.)r)rr)r)r)r)rprWrHrur/rJ_pkttype_match_fragment)r6rkZ default_rulesrSZdispatch_suffixrA directionr8r8r9build_default_ruless $  (  &  .        &  &                 &   .   &               &   &znftables.build_default_rulescCs4|dkrdddgS|dkr dgS|dkr0ddgSgS) Nr*r$ FORWARD_IN FORWARD_OUTr(rr)r r8)r6rer8r8r9get_zone_table_chainss znftables.get_zone_table_chainsr,c  sdkr\dkr\g} | jj|||||dd | jj|||||dd | Sjjj|jdkrxdnddkrd krd nd } jjj|t| g} g} |r| jd d ddiiddt |idi|r| jd d ddiiddt |ididdd}|rlxT|D]L}dkrTjj j |}||krT||krTq| jj d|qW|rxT|D]L}dkrjj j |}||kr||krqx| jj d|qxWfdd}g} | rHx| D]P}| rxB| D]}| j|||qWn"dkr0|r0n| j||dqWn\dkrZ|rZnJ| rxB| D]}| j|d|qfWn"dkr|rn| j|dd| S)Nr)r,r-)rAr.rprepostr TFrxrrDrz==r|)rrrr)rrsaddrdaddrcsg}|r|j||r |j||jdddfiitdf|d}|jjrrdd|iiSdd|iiSdS) Nrrz%s_%sz%s_%s_POLICIES_%s)rArerSrdr;r>r=)rHrur_policy_priority_fragment)ingress_fragmentegress_fragmentexpr_fragmentsr>)_policyrS chain_suffixrrAp_objr6rer8r9_generate_policy_dispatch_rules    zRnftables.build_policy_ingress_egress_rules.._generate_policy_dispatch_rule) extend!build_policy_ingress_egress_rulesr/rZ get_policyrYpolicy_base_chain_namePOLICY_CHAIN_PREFIXrHrhr?Z check_source_rule_addr_fragment)r6rrrerSZingress_interfacesZegress_interfacesZingress_sourcesZegress_sourcesrArjisSNATZingress_fragmentsZegress_fragmentsZ ipv_to_familysrcrdstrrrr8)rrSrrrArr6rer9rsv          z*nftables.build_policy_ingress_egress_rulesFc  Cs|dkrT|dkrTg} | j|j|||||||d| j|j|||||||d| S|dkrh|dkrhdnd} |jjj||t| d} d d d d d d d |} |t|d d kr|dt|d d}d} |dkr| dd|| fiig}n,ddd| iid|di| dd|| fiig}|rL| rLd}|td||f|d}|j|j nP|rnd}|td||f|d}n.d}|td||f|d}|s|j|j |d|iigS)Nr)r,r-r.r TF)rrr)rr r$rrr&rE+*gotorz%s_%srxrrDz==)rrrr<z %s_%s_ZONES)rArerSrdr;r=r>) r!build_zone_source_interface_rulesr/rrrrKrur_zone_interface_fragment)r6rr?r interfacererSrHrArjrroptactionrrLr>r8r8r9rs\     z*nftables.build_zone_source_interface_rulesc Csn|dkr|dkrg}|jdr6|j|tdd} nd} td|sTt|sT| dkrp|j|j||||||dtd|st|s| dkr|j|j||||||d|S|dkr|dkrd nd } |jjj ||t | d } d d d|} ddddddd|} |jj rd||f}n d||f}d}|t ||j | ||dd|| fiigd}|j|j||| d|iigS)Nr)r,zipset:rr-rr.r TF)rr<r=)TFrr)rr r$rrr&z%s_%s_ZONES_SOURCEz %s_%s_ZONESrrz%s_%s)rArerSrdr>) startswith_set_get_familyrKrrrbuild_zone_source_address_rulesr/rrrrJrurr_zone_source_fragment)r6rr?rr@rerSrArjZ ipset_familyrrrrZzone_dispatch_chainrr>r8r8r9r<sB    z(nftables.build_zone_source_address_rulesc Cs|dkrH|dkrHg}|j|j||||d|j|j||||d|Sddd|}|dkrj|dkrjd nd }|jjj||t|d } g}|j|d |td || fdiix0d!D](} |j|d |td|| | fdiiqWxDd"D]<} |j|d|td || fddd|| | fiigdiiqW|jjj|j } |jj dkr|dkr| d#kr| } | dkrhd} |j|d|td || f|j |jj ddd| | fiigdii|dkr| d$kr| d%kr|j } n | j di} |j|d|td || f| gdii|s|j|S)&Nr)r,r-r.r;r=)TFr TF)rrSz%s_%s)rArerrrrdenyallowrz%s_%s_%sr>rr)rArerSrdrr*REJECT %%REJECT%%rrz"filter_%s_%s: "r)rrrrr)rrrrr)rrr)rrrr)rr)rbuild_policy_chain_rulesr/rrrrHruZ _policiesrget_log_deniedr_reject_fragmentlowerreverse)r6rrrerSrArjrrrrrZ log_suffixtarget_fragmentr8r8r9rjsZ      &             z!nftables.build_policy_chain_rulescCs<|dkr iS|d kr,ddddiid |d iSttd |dS) Nallunicast broadcast multicastrxrrDpkttypez==)rrrzInvalid pkttype "%s")rrr)r r )r6rr8r8r9rs  z nftables._pkttype_match_fragmentcCsddddiddddiddddiddddiddddiddddiddddiddddiddddiddddiddd diddd diddd diddd didd d diddd diddd diddd diddd diddddiddddidddiidddiid}||S)Nricmpzhost-prohibited)rTrdznet-prohibitedzadmin-prohibitedicmpv6znet-unreachablezhost-unreachablezport-unreachablerzprot-unreachablezaddr-unreachablezno-routerTz tcp reset)zicmp-host-prohibitedz host-prohibzicmp-net-prohibitedz net-prohibzicmp-admin-prohibitedz admin-prohibzicmp6-adm-prohibitedzadm-prohibitedzicmp-net-unreachablez net-unreachzicmp-host-unreachablez host-unreachzicmp-port-unreachablezicmp6-port-unreachablez port-unreachzicmp-proto-unreachablez proto-unreachzicmp6-addr-unreachablez addr-unreachzicmp6-no-routezno-routez tcp-resetztcp-rstr8)r6Z reject_typeZfragsr8r8r9_reject_types_fragments0                      znftables._reject_types_fragmentcCsddddiS)Nrrzadmin-prohibited)rTrdr8)r6r8r8r9rsznftables._reject_fragmentcCs ddddiiddddgid iS) NrxrrDl4protoz==r|rr)rrrr8)r6r8r8r9_icmp_match_fragments znftables._icmp_match_fragmentcCsP|siSddddd}|j\}}|||d}|j}|dk rH||d<d|iS) NsecondZminuteZhourZday)smhd)rateZperburstlimit)Z value_parseZ burst_parse)r6rZ rich_to_nftrZdurationrrr8r8r9_rich_rule_limit_fragments  z"nftables._rich_rule_limit_fragmentcCst|jtttgkrn<|jrHt|jtttt gkrRt t dt|jn t t d|j dkrt|jttgkst|jtt gkrdSt|jtgkst|jttgkrdSn|j dkrdSdSdS)NzUnknown action %szNo rule action specified.rrrrr) rTelementrrrrrrrrr r rY)r6 rich_ruler8r8r9_rich_rule_chain_suffixs    z nftables._rich_rule_chain_suffixcCs>|j r|j rttd|jdkr(dS|jdkr6dSdSdS)NzNot log or auditrrrr)rauditr r rY)r6rr8r8r9 _rich_rule_chain_suffix_from_logs   z)nftables._rich_rule_chain_suffix_from_logcCsddiS)Nz%%ZONE_INTERFACE%%r8)r6r8r8r9rsz!nftables._zone_interface_fragmentcCsNtd|rt|}n,td|r@|jd}t|dd|d}d||diS)Nr/rrEz%%ZONE_SOURCE%%)r?r@)rrrsplit)r6r?r@Z addr_splitr8r8r9rs     znftables._zone_source_fragmentcCs d|jiS)Nz%%POLICY_PRIORITY%%)rY)r6rr8r8r9rsz"nftables._policy_priority_fragmentcCs| s|jdkriSd|jiS)Nrz%%RICH_RULE_PRIORITY%%)rY)r6rr8r8r9_rich_rule_priority_fragmentsz%nftables._rich_rule_priority_fragmentc Cs|js iS|jjj||t}ddd|}|j|}i} |jjrPd|jj| d<|jjr|d|jjkrhdn|jj} d| | d<d td |||f||j |jj d | igd } | j |j ||d | iiS)Nr;r=)TFz%srZwarningwarnlevelr,z%s_%s_%sr)rArerSrdr>) rr/rrrrrrrurrrr) r6rrrrerrrrZ log_optionsrr>r8r8r9_rich_rule_log"s&    znftables._rich_rule_logc Cs|js iS|jjj||t}ddd|}|j|}dtd|||f||j|jjdddiigd } | j |j ||d | iiS) Nr;r=)TFr,z%s_%s_%srrr)rArerSrdr>) rr/rrrrrurrrr) r6rrrrerrrrr>r8r8r9_rich_rule_audit<s   znftables._rich_rule_auditc Cs|js iS|jjj||t}ddd|}|j|}d|||f} t|jtkr\ddi} nt|jtkr|jjr|j |jj} nddi} nt|jt krddi} nt|jt krHd}|jjj||t}d|||f} |jj j d } t| d kr,dd d d iiddd d d ii| d gi| dgidi} ndd d d ii| ddi} nttdt|jdt| ||j|jj| gd} | j|j||d| iiS)Nr;r=)TFz%s_%s_%srrrr(rrErrDmark^&r)rDvaluezUnknown action %sr,)rArerSrdr>)rr/rrrrrTrrrrrr|rrKr r rurrrr) r6rrrrerrrrrSZ rule_actionrr>r8r8r9_rich_rule_actionNsB     , znftables._rich_rule_actioncCs|jdr0|j|tddd|kr(dnd|St|r>d}ntd|rNd}nvtd|rd}tj|dd}d |jj |j d i}nDtd |rd }t |}n,d }|j d }d t |dt |dd i}dd||di|rdnd|diSdS)Nzipset:rTFetherrr-)strictr)addrrKrr.rrrErxpayload)protocolfieldz!=z==)rrr)r_set_match_fragmentrKrrr ipaddressZ IPv4NetworkZnetwork_addressZ compressedZ prefixlenrrrU)r6Z addr_fieldr@invertrAZnormalized_addressZaddr_lenr8r8r9rys( &      znftables._rule_addr_fragmentcCs6|siS|d krttd|ddddiid|d iS) NrrzInvalid familyrxrrDnfprotoz==)rrr)rr)r r )r6Z rich_familyr8r8r9_rich_rule_family_fragments  z#nftables._rich_rule_family_fragmentcCs8|siS|jr|j}n|jr&d|j}|jd||jdS)Nzipset:r)r)ripsetrr)r6Z rich_destr@r8r8r9_rich_rule_destination_fragments z(nftables._rich_rule_destination_fragmentcCsZ|siS|jr|j}n2t|dr.|jr.|j}nt|drH|jrHd|j}|jd||jdS)Nmacrzipset:r)r)rhasattrrrrr)r6Z rich_sourcer@r8r8r9_rich_rule_source_fragments z#nftables._rich_rule_source_fragmentcCsPt|}t|tr$|dkr$ttn(t|dkr8|dSd|d|dgiSdS)NrrErange)r isinstancerUr rrK)r6portrr8r8r9_port_fragments   znftables._port_fragmentc Csbddd|}d}|jjj||t} g} |r>| j|j|j|rT| j|jd||r|| j|j|j | j|j |j | jdd|dd id |j |d i| st |jtkr| jdd d diiddddgid ig} |r0| j|j||||| | j|j||||| | j|j||||| n.| j|ddtd|| f| ddigdii| S)Nr;r=)TFr*rrxrdport)rrz==)rrrryrDrzr{r|new untrackedr>r,z %s_%s_allowr)rArerSrd)r/rrrrHrrArr destinationrsourcer rTrrrrrru) r6rrprotor rrrrerrrjr8r8r9build_policy_ports_ruless:   z!nftables.build_policy_ports_rulesc CsZddd|}d}|jjj||t}g} |r>| j|j|j|rT| j|jd||r|| j|j|j | j|j |j | jdddd iid |d i| st |j tkr| jdd dd iiddddgid ig} |r(| j|j||||| | j|j||||| | j|j||||| n.| j|ddtd||f| ddigdii| S)Nr;r=)TFr*rrxrrDrz==)rrrryrzr{r|r rr>r,z %s_%s_allowr)rArerSrd)r/rrrrHrrArrrrrrTrrrrrru) r6rrrrrrrerrrjr8r8r9build_policy_protocol_ruless8   z$nftables.build_policy_protocol_rulesc Csbddd|}d}|jjj||t} g} |r>| j|j|j|rT| j|jd||r|| j|j|j | j|j |j | jdd|dd id |j |d i| st |jtkr| jdd d diiddddgid ig} |r0| j|j||||| | j|j||||| | j|j||||| n.| j|ddtd|| f| ddigdii| S)Nr;r=)TFr*rrxrsport)rrz==)rrrryrDrzr{r|r rr>r,z %s_%s_allowr)rArerSrd)r/rrrrHrrArrrrrr rTrrrrrru) r6rrrr rrrrerrrjr8r8r9build_policy_source_ports_ruless:   z(nftables.build_policy_source_ports_rulesc Csd}|jjj||t} ddd|} g} |rR| jdddtd||f||diig} |rl| j|jd || jd d |d d id|j|di| jdd||fi| j| ddtd| | dii| S)Nr*r;r=)TFz ct helperr,z helper-%s-%s)rArerrrTrrrxrr )rrz==)rrrr>zfilter_%s_allow)rArerSrd)r/rrrrHrurr ) r6rrrr rZ helper_nameZmodule_short_namererrrjrr8r8r9build_policy_helper_ports_rules)s.    z(nftables.build_policy_helper_ports_rulesc Csddd|}|jjj||t}g} |rv|t|ddkrT|dt|dd}ddd d iid |d id dig} n|jd|d dig} dtd|| d} | j|d| ii| S)Nr;r=)TFrErrrxrrDrz==)rrrrrr,zfilter_%s_allow)rArerSrdr>)r/rrrrKrrurH) r6rr?rrerrrrrjrdr>r8r8r9build_zone_forward_rulesFs"  z!nftables.build_zone_forward_rulesc Csd}|jjj||tdd}ddd|}g}|r`|j|j|j|j|j|j|j |} nd} |t d|| f|d d d d iid ddiddigd} | j |j ||d| iigS)Nr)T)rr;r=)TFrz nat_%s_%srxrrDrz!=r)rrrZ masquerade)rArerSrdr>) r/rrrrHrrrrrrurr) r6rrrArrerrrrr>r8r8r9"_build_policy_masquerade_nat_rules_s&   z+nftables._build_policy_masquerade_nat_rulesc Cs^g}|rD|jr|jdks,|jrDtd|jjrD|j|j||d|nV|r|jrX|jdksl|jrtd|jjr|j|j||d|n|j|j||d|d}|jjj||t }ddd|}g}|r|j |j |j |j |j |j|j|} nd } d td || f|d d ddiiddddgididdigd} | j|j||j |d| ii|S)Nrr.rr-r*r;r=)TFrr,z filter_%s_%srxryrDrzr{r|r r)rrrr)rArerSrdr>)rArrrrrr/rrrrHrrrrrurr) r6rrrrjrerrrrr>r8r8r9build_policy_masquerade_rulesxs8   z&nftables.build_policy_masquerade_rulesc Cs$d} |jjj|| t} ddd|} g} |r\| j|j|j| j|j|j|j |} nd} | jdd|dd id |j |d i|rt d |rt |}|r|d kr| jd||j |diq| jdd|iin| jdd|j |ii|t d| | f| d}|j|j|| d|iigS)Nr)r;r=)TFrrxrr )rrz==)rrrrrnr)rr rZredirectr z nat_%s_%s)rArerSrdr>)r/rrrrHrrrrrr rrrurr)r6rrr rtoaddrtoportrArrerrrrr>r8r8r9$_build_policy_forward_port_nat_ruless4     z-nftables._build_policy_forward_port_nat_rulesc Csg}|rF|jr|jdks&|rFtd|rF|j|j||||||d|n|r|jrZ|jdksh|rtd|r|j|j||||||d|nL|rtd|r|j|j||||||d|n|j|j||||||d||S)Nrr.rr-)rArrr) r6rrr rrrrrjr8r8r9build_policy_forward_port_ruless    z(nftables.build_policy_forward_port_rulescCsHdd|ddid|dig}|dk rD|jdd|ddid|di|S)NrxrrT)rrz==)rrrcode)rH)r6rrTr fragmentsr8r8r9_icmp_types_fragmentss  znftables._icmp_types_fragmentscCs|dkr4|tkr4t|\}}}|jd||r.dn|S|dkrh|tkrht|\}}}|jd||rbdn|Sttd||j|fdS)Nrrrrz)ICMP type '%s' not supported by %s for %s)rr rr r rr)r6rZ icmp_typeZ_type_codeZ _omit_coder8r8r9_icmp_types_to_nft_fragmentssz%nftables._icmp_types_to_nft_fragmentscCsBd}|jjj||t}ddd|}|r6|jr6|j}n<|jrjg}d|jkrT|jdd|jkrr|jdnddg}g} x|D]} |jjj|rd||f} ddi} nd ||f} |j} g} |r| j|j |j | j|j |j| j|j |j | j|j| |j|r| j|j||||| | j|j||||| |jrf| j|j||||| nN|j|}d td |||f| |jgd }|j|j|| j|d |iiq~|jjdkr|jjj| r| j|d d t| | |j|jjddd||fiigd ii| j|d d t| | | gd iiq~W| S)Nr*r;r=)TFrrz %s_%s_allowrz %s_%s_denyr,z%s_%s_%s)rArerSrdr>rrrz"%s_%s_ICMP_BLOCK: ")r/rrripvsrrHquery_icmp_block_inversionrrrArrrrr"rrrrrrrrurrrr)r6rrZictrrerrr#rjrZ final_chainrrrr>r8r8r9build_policy_icmp_block_rulessb          " " z&nftables.build_policy_icmp_block_rulescCsd}|jjj||t}g}ddd|}|jjj|r@|j}nddi}|j|ddtd||fd |j|gd ii|jj d kr|jjj|r|j|ddtd||fd |j|j |jj d d d||fiigd ii|S)Nr*r;r=)TFrr>r,z%s_%s)rArerSrFrdrrrz%s_%s_ICMP_BLOCK: ) r/rrrr$rrHrurrr)r6rrrerrjrrr8r8r9'build_policy_icmp_block_inversion_rules(s,      z0nftables.build_policy_icmp_block_inversion_rulesc Csg}ddddiidddiddd d d gd d idddig}|dkrV|jdddii|jddi|jdddtd|dii|jdddtddddddiddddgidid digdii|S)!NrxrrDrz==r)rrrZfibrZiifrZoif)flagsresultFrrrzrpfilter_DROP: rr<r>r,Zfilter_PREROUTING)rArerSrdrrrT)rrr|znd-router-advertznd-neighbor-solicitr)rHru)r6rkrjrr8r8r9build_rpfilter_rulesGs0     znftables.build_rpfilter_rulesc Csddddddddd g }d d |D}d d dddidd|idig}|jjd"krb|jdddii|j|jdg}|jdddtdd|dii|jdddtd d!|dii|S)#Nz ::0.0.0.0/96z::ffff:0.0.0.0/96z2002:0000::/24z2002:0a00::/24z2002:7f00::/24z2002:ac10::/28z2002:c0a8::/32z2002:a9fe::/32z2002:e000::/19cSs2g|]*}d|jddt|jdddiqS)rrrrE)rrK)rrU).0rBr8r8r9 nsz5nftables.build_rfc3964_ipv4_rules..rxrr.r)rrz==r|)rrrrrrrzRFC3964_IPv4_REJECT: z addr-unreachr;r>r,rrE)rArerSrFrdZfilter_FORWARD)rr)r/Z _log_deniedrHrru)r6Z daddr_setrrjr8r8r9build_rfc3964_ipv4_rulescs:   z!nftables.build_rfc3964_ipv4_rulescCsd}g}|j|j|j|j|j|j|j|j|jg}|j|j||||||j|j||||||j|j ||||||S)Nr*) rHrrArrrrrrr)r6rrrrerrjr8r8r9*build_policy_rich_source_destination_rulessz3nftables.build_policy_rich_source_destination_rulescCs|dkr dSdS)NrrebTF)rrr0r8)r6rr8r8r9is_ipv_supportedsznftables.is_ipv_supportedc Csddd}||||ddg||dd||g||dd||g||dg||||||g||ddg||dd||g||dgdd }||kr||Sttd |dS) NZ ipv4_addrZ ipv6_addr)rrZ inet_protoZ inet_servicerZifnameZ ether_addr) zhash:ipz hash:ip,portzhash:ip,port,ipzhash:ip,port,netz hash:ip,markzhash:netz hash:net,netz hash:net,portzhash:net,port,netzhash:net,ifacezhash:macz!ipset type name '%s' is not valid)r r )r6rrTZipv_addrtypesr8r8r9_set_type_lists"    znftables._set_type_listc Cs|rd|kr|ddkrd}nd}t||j||d}x0|jddjdD]}|dkrLd g|d <PqLW|rd|kr|d|d<d|kr|d|d<g}x0dD](}d|i} | j||jdd| iiqW|S)NrAinet6rr)rerrrT:rE,r-netr Zintervalr(ZtimeoutZmaxelemsizer,r.r;r|)r-r7r )r,r-r.)rur3rrrH) r6rrrToptionsrZset_dicttrjrAZ rule_dictr8r8r9build_set_create_ruless*     znftables.build_set_create_rulescCs$|j|||}|j||jjdS)N)r;rmr/r)r6rrrTr9rjr8r8r9 set_createsznftables.set_createcCs8x2dD]*}dd|t|dii}|j||jjqWdS)Nr,r-r.r=r|)rArerr)r,r-r.)ruror/r)r6rrrAr>r8r8r9 set_destroys   znftables.set_destroycCs6|jjj|jjddjd}g}xtt|D]}||dkrr|jdddii|jdd |rdd nd d iq2||dkr|jd|j||rdndd iq2||dkr|jdd|rdndiiq2||dkr|jdddiiq2t d||q2Wdt|dkrd|in|d|r&dndd|diS)Nr5rEr6r rrDrrZthr r)rrr-r7rrrZifacerrrz-Unsupported ipset type for match fragment: %srxconcatrz!=z==@)rrr)r-r7r) r/r get_ipsetrTrrrKrHrr )r6rrZ match_destr type_formatrir8r8r9rs$      znftables._set_match_fragmentc CsN|jjj|}|jjddjd}|jd}t|t|krHttdg}xtt|D]}||dkr,y||j d}Wn&t k r|j d||} Yn,X|j ||d||||dd} y| j d}Wn t k r|j | Yn(X|j d| d|| |ddgiq\||dkr d||krb|j d||jdiny||j d }WnLt k r||} d |j kr|j d d krt | } |j | Yn^X||d|} d |j kr|j d d krt | } |j d| t|||dddiq\|j ||q\Wt|dkrJd|igS|S)Nr5rEr6z+Number of values does not match ipset type.r Ztcp-rr-r7rrAr4r)rrKr>)r-r7)r/rr@rTrrKr rrrFrirHr9rrU) r6rrentryobjrAZ entry_tokensZfragmentrBrFZport_strrr8r8r9_set_entry_fragmentsL  ("znftables._set_entry_fragmentc Csjg}g}t|ttfs|g}x|D]}|j|j||q"Wx(dD] }|jdd|t||diiqBW|S)Nr,r-r.r;r)rArerrelem)r,r-r.)r rhtuplerrFrHru)r6rrentriesrjelementsrrAr8r8r9build_set_add_rules(s   znftables.build_set_add_rulescCs"|j||}|j||jjdS)N)rKrmr/r)r6rrrDrjr8r8r9set_add7s znftables.set_addcCsF|j||}x4dD],}dd|t||dii}|j||jjqWdS)Nr,r-r.r=r)rArerrrG)r,r-r.)rFruror/r)r6rrrDrrAr>r8r8r9 set_delete;s   znftables.set_deletecCs4g}x*dD]"}dd|t|dii}|j|q W|S)Nr,r-r.rbr|)rArerr)r,r-r.)rurH)r6rrrjrAr>r8r8r9build_set_flush_rulesDs  znftables.build_set_flush_rulescCs |j|}|j||jjdS)N)rNrmr/r)r6rrrjr8r8r9 set_flushMs znftables.set_flushcCsJ|jjj|}|jdkrd}n(|jrBd|jkrB|jddkrBd}nd}|S)Nzhash:macrrAr4r.r-)r/rr@rTr9)r6rrrrAr8r8r9rQs znftables._set_get_familycCsg}|j|j||||j|j|xbtdt|dD]<}|j|j||||d|j||jj|j q:W|j||jjdS)Nri) rr;rNrrKrKrmr/rclear)r6Zset_nameZ type_namerIZcreate_optionsZ entry_optionsrjrBr8r8r9 set_restore^s znftables.set_restore)N)N)r)r,)Fr,)r,)r,)F)NN)NN)NN)NN)N)N)N)N)N)N)F)N)N)F)NN)J__name__ __module__ __qualname__rrZpolicies_supportedr:rNrRr[rarmrorqrsrwrtrrrrrrrrrrrrrrrrrrrrrrrrrrrr rrrrrrrrrr r"r%r'r*r.r/r1r3r;r<r=rrFrKrLrMrNrOrrQr8r8r8r9r+Ts/.`  4  R i ; - 9   +     $ $ $   ' $   < #   4  r+iji))Z __future__rrOr^rZfirewall.core.loggerrZfirewall.functionsrrrrrZfirewall.errorsr r r r r rrZfirewall.core.richrrrrrrrZfirewall.core.icmprrZnftables.nftablesrrurrrrpobjectr+r8r8r8r9s,  $$       PK!+0̋)__pycache__/fw_ifcfg.cpython-36.opt-1.pycnu[3 Yj @sTdZddgZddlZddlZddlmZddlmZddlm Z ddZ d dZ dS) z.Functions to search for and change ifcfg filessearch_ifcfg_of_interfaceifcfg_set_zone_of_interfaceN)config)log)ifcfgcCstjjtjsdSxtttjtjD]`}|jds4q$xd D]}|j|r:q:q:Wd |krXq$t d tj|f}|j |j d |kr$|Sq$Wd tj|f}tjj|rt |}|j |SdS)z6search ifcfg file for the interface in config.IFCFGDIRNzifcfg-.bak.orig.rpmnew.rpmorig.rpmsave-range.z%s/%sZDEVICEz %s/ifcfg-%s)rrr r r r ) ospathexistsrZIFCFGDIRsortedlistdir startswithendswithrreadget) interfacefilenameZignored ifcfg_filer/usr/lib/python3.6/fw_ifcfg.pyr!s*   cCsn|dkr d}t|}|dk rj|jd|krj|jddko>|dk rjtjd||jf|jd||jdS)zYSet zone (ZONE=) in the ifcfg file that uses the interface (DEVICE=)NZZONEzSetting ZONE=%s in '%s')rrrZdebug1rsetwrite)Zzonerrrrrr?s ) __doc____all__rZos.pathZfirewallrZfirewall.core.loggerrZfirewall.core.io.ifcfgrrrrrrrs   PK!gzz%__pycache__/base.cpython-36.opt-1.pycnu[3 Yj6@sdZdZdZd0ZdddedgZddddgZd d d d d ddZddddddddddddddddgd d!d"d#d$d%d&ddg d'Zd(d)d*d+d,d-d.gZd/S)1zBase firewall settingsz{chain}_{zone}ZCONTINUEZACCEPTz %%REJECT%%ZDROPdefaultZREJECTZPREZPOSTINZFWDIZFWDOZOUT)Z PREROUTINGZ POSTROUTINGZINPUTZ FORWARD_INZ FORWARD_OUTZOUTPUTzicmp-host-prohibitedz host-prohibzicmp-net-unreachablez net-unreachzicmp-host-unreachablez host-unreachzicmp-port-unreachablez port-unreachzicmp-proto-unreachablez proto-unreachzicmp-net-prohibitedz net-prohibz tcp-resetztcp-rstzicmp-admin-prohibitedz admin-prohibzicmp6-adm-prohibitedzadm-prohibitedzicmp6-no-routezno-routezicmp6-addr-unreachablez addr-unreachzicmp6-port-unreachable)Zipv4Zipv6zhash:ipz hash:ip,portz hash:ip,markzhash:netz hash:net,portzhash:net,ifacezhash:macN) __doc__ZDEFAULT_ZONE_TARGETZDEFAULT_POLICY_TARGETZDEFAULT_POLICY_PRIORITYZ ZONE_TARGETSZPOLICY_TARGETSZ SHORTCUTSZ REJECT_TYPESZSOURCE_IPSET_TYPESrr/usr/lib/python3.6/base.pys. PK!'*)__pycache__/ebtables.cpython-36.opt-1.pycnu[3 Yj$@s&dgZddlZddlmZddlmZddlmZm Z m Z ddl m Z ddl mZddlmZmZddlZd gd d d gd d dgdZiZiZiZxejD]tZgee<eee<x\eeD]PZeejdeeejdeefeejdeeejdeqWqWGdddeZdS)ebtablesN)runProg)log)tempFilereadfile splitArgs)COMMANDS) ipXtables) FirewallError INVALID_IPVZBROUTINGZ PREROUTINGZ POSTROUTINGZOUTPUTZINPUTZFORWARD)ZbrouteZnatfilterz -N %s_directz-I %s 1 -j %s_directz-I %s_direct 1 -j RETURNz %s_directc@seZdZdZdZdZddZddZddZd d Z d d Z d dZ ddZ ddZ ddZddZddZddZddZddZd/d d!Zd"d#Zd$d%Zd&d'Zd(d)Zd0d+d,Zd-d.ZdS)1rZebFcCsBt|j|_td|j|_|j|_|j|_|jg|_ dS)Nz %s-restore) ripv_command_restore_command_detect_restore_noflush_optionZrestore_noflush_option_detect_concurrent_optionconcurrent_option fill_existsavailable_tables)selfr/usr/lib/python3.6/ebtables.py__init__9s    zebtables.__init__cCs$tjj|j|_tjj|j|_dS)N)ospathexistsrZcommand_existsrZrestore_command_exists)rrrrrAszebtables.fill_existscCs(d}t|jddg}|ddkr$d}|S)Nz --concurrentz-Lr)rr)rrretrrrrEs  z"ebtables._detect_concurrent_optionc Cs.g}y|j|dWntk r(dSXdS)NoffFT) set_rules ValueError)rrulesrrrrOs z'ebtables._detect_restore_noflush_optioncCsg}|jr |j|kr |j|j|dd|D7}tjd|j|jdj|t|j|\}}|dkr~td|jdj||f|S)NcSsg|] }d|qS)z%sr).0itemrrr ^sz"ebtables.__run..z %s: %s %s rz'%s %s' failed: %s) rappendrdebug2 __class__rjoinrr )rargsZ_argsstatusrrrrZ__runYs zebtables.__runcCs(x"dD]}||krttd|qWdS)N %%REJECT%%%%ICMP%% %%LOGTYPE%%z'%s' invalid for ebtables)r,r-r.)r r )rrulestrrrr_rule_validatefs zebtables._rule_validatecCs|tko|t|kS)N)BUILT_IN_CHAINS)rr tablechainrrris_chain_builtinlszebtables.is_chain_builtincCsJg}|r4|jd|d|g|jd|d|dddgn|jd|d|g|S)Nz-tz-Nz-I1z-jZRETURNz-X)r&)raddr3r4r!rrrbuild_chain_rulesps zebtables.build_chain_rulescCs8d|g}|r |d|t|g7}n |d|g7}||7}|S)Nz-tz-Iz-D)r0)rr7r3r4indexr*r/rrr build_rule{s  zebtables.build_rulecCs tj|S)N)r Zcommon_reverse_rule)rr*rrr reverse_ruleszebtables.reverse_rulecCstj|dS)N)r Zcommon_check_passthrough)rr*rrrcheck_passthroughszebtables.check_passthroughcCs tj|S)N)r Zcommon_reverse_passthrough)rr*rrrreverse_passthroughszebtables.reverse_passthroughc Cs<t}d}i}x|D]}|dd}|j|xTdD]L}y|j|} Wntk rZYq4Xt|| dkr4|j| |j| }q4Wx^tt|D]N} xHtjD]>} | || kr|| j do|| j d rd|| || <qWqW|j |gj |qWxD|D]<}|j d|x&||D]}|j dj|d qWqW|jtj|j} tjd |j|jd |j| jfg} | j d t|j| |jd \} }tjdkrt|j}|dk rd} xH|D]@}tjd| |fddd|j d stjddd| d7} qWtj|j| dkr8td|jdj| |fdS)Nr -t--table"z"%s"z*%s r% z %s: %s %sz%s: %dz --noflush)stdinz%8d: %sr)nofmtnlr)rEz'%s %s' failed: %s)r>r?)rr1r9r lenpoprangestringZ whitespace startswithendswith setdefaultr&writer)closerstatnamerr'r(rst_sizerZgetDebugLogLevelrZdebug3unlink)rr! log_deniedZ temp_filer3Z table_rulesZ_ruler/opticrPr*r+rlineslinerrrrsZ                zebtables.set_rulescCs|j||j|S)N)r1_ebtables__run)rr/rTrrrset_rules zebtables.set_ruleNc Csg}|r|gntj}xp|D]h}||jkr6|j|qy*|jd|dg|jj||j|Wqtk rtjd|YqXqW|S)Nz-tz-Lz#ebtables table '%s' does not exist.)r2keysrr&rZr rZdebug1)rr3rZtablesrrrget_available_tabless    zebtables.get_available_tablescCsiS)Nr)rr3rrrget_zone_table_chainsszebtables.get_zone_table_chainscCsFg}xs.     PK![j/__pycache__/fw_transaction.cpython-36.opt-1.pycnu[3 Yj@sJdZdgZddlZddlmZddlmZddlmZGddde Z dS)z!Transaction classes for firewalldFirewallTransactionN)log)errors) FirewallErrorc@seZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ ddZ ddZ ddZddZddZddZdd Zd!d"Zd#S)$rcCs(||_i|_g|_g|_g|_g|_dS)N)fwrules pre_funcs post_funcs fail_funcsmodules)selfrr $/usr/lib/python3.6/fw_transaction.py__init__!s zFirewallTransaction.__init__cCs2|jj|jdd=|jdd=|jdd=dS)N)rclearrr r )r r r rr)s   zFirewallTransaction.clearcCs|jj|jgj|dS)N)r setdefaultnameappend)r backendruler r radd_rule/szFirewallTransaction.add_rulecCsx|D]}|j||qWdS)N)r)r rrrr r r add_rules2s zFirewallTransaction.add_rulescCs|j|jko||j|jkS)N)rr)r rrr r r query_rule6szFirewallTransaction.query_rulecCs2|j|jkr.||j|jkr.|j|jj|dS)N)rrremove)r rrr r r remove_rule9szFirewallTransaction.remove_rulecGs|jj||fdS)N)rr)r funcargsr r radd_pre=szFirewallTransaction.add_precGs|jj||fdS)N)r r)r rrr r radd_post@szFirewallTransaction.add_postcGs|jj||fdS)N)r r)r rrr r radd_failCszFirewallTransaction.add_failcCs||jkr|jj|dS)N)r r)r moduler r r add_moduleFs zFirewallTransaction.add_modulecCs||jkr|jj|dS)N)r r)r r r r r remove_moduleJs z!FirewallTransaction.remove_modulecCsx|D]}|j|qWdS)N)r!)r r r r r r add_modulesNs zFirewallTransaction.add_modulescCsx|D]}|j|qWdS)N)r")r r r r r rremove_modulesRs z"FirewallTransaction.remove_modulescCstjdt||dfi}|sjxp|jD]<}x6t|j|D]$}|j|gj|jj|j |qX|j |q>W|s|jj ||} | r| \} }| rtj||ri} xH|D]@}g| |<x2t||D]"} | |j |jj|j| qWqWxb| D]Z}y|jj|| |Wn<tk r}ztjt j tj |WYdd}~XnXq0Wxh|jD]^\} }y | |WnFtk r}z(tjt j tj d| ||fWYdd}~XnXqWttj||jdS)Nz%s.execute(%s)FTz#Calling fail func %s(%s) failed: %s)rr%r&r-prerr Exceptiondebug1 traceback format_excerrorrZhandle_modulesr'r(r)r rrZCOMMAND_FAILEDpost)r r+rr r4ZerrorMsgdoner,msgZ module_returnZstatusZ undo_rulesrrrr r rexecutefsV    " & zFirewallTransaction.executecCs|tjdt|xd|jD]Z\}}y ||Wqtk rr}z(tjtjtjd|||fWYdd}~XqXqWdS)Nz%s.pre()z"Calling pre func %s(%s) failed: %s) rr%r&rr0r1r2r3r4)r rrr7r r rr/s zFirewallTransaction.precCs|tjdt|xd|jD]Z\}}y ||Wqtk rr}z(tjtjtjd|||fWYdd}~XqXqWdS)Nz %s.post()z#Calling post func %s(%s) failed: %s) rr%r&r r0r1r2r3r4)r rrr7r r rr5s zFirewallTransaction.postN)__name__ __module__ __qualname__rrrrrrrrrr!r"r#r$r-r8r/r5r r r rr s"@ ) __doc____all__r2Zfirewall.core.loggerrZfirewallrZfirewall.errorsrobjectrr r r rs    PK!qq#__pycache__/__init__.cpython-36.pycnu[3 Yj@sdS)Nrrr/usr/lib/python3.6/__init__.pysPK!qq)__pycache__/__init__.cpython-36.opt-1.pycnu[3 Yj@sdS)Nrrr/usr/lib/python3.6/__init__.pysPK!l|q"q"&__pycache__/ipset.cpython-36.opt-1.pycnu[3 Yjq2 @sdZdddgZddlZddlZddlmZddlmZddl m Z dd l m Z dd l mZmZdd lmZd Zd ddddddddddg ZdddddZddddZGd ddeZd!dZd"dZd#d$Zd%d&Zd'd(ZdS))zThe ipset command wrapperipsetcheck_ipset_nameremove_default_create_optionsN)errors) FirewallError)runProg)log)tempFilereadfile)COMMANDS zhash:ipz hash:ip,portzhash:ip,port,ipzhash:ip,port,netz hash:ip,markzhash:netz hash:net,netz hash:net,portzhash:net,port,netzhash:net,ifacezhash:macz inet|inet6valuez value in secs)familyhashsizemaxelemtimeoutZinetZ1024Z65536)rrrc@seZdZdZddZddZddZdd Zd d Zd'd dZ ddZ ddZ ddZ d(ddZ d)ddZddZd*ddZd+ddZdd Zd!d"Zd#d$Zd%d&Zd S),rzipset command wrapper classcCstd|_d|_dS)Nr)r _commandname)selfr/usr/lib/python3.6/ipset.py__init__Ks zipset.__init__cCs^dd|D}tjd|j|jdj|t|j|\}}|dkrZtd|jdj||f|S)zCall ipset with argscSsg|] }d|qS)z%sr).0itemrrr Rszipset.__run..z %s: %s %s rz'%s %s' failed: %s)rdebug2 __class__rjoinr ValueError)rargsZ_argsstatusretrrrZ__runOsz ipset.__runcCs t|tkrttjd|dS)zCheck ipset namezipset name '%s' is not validN)lenIPSET_MAXNAMELENrrZ INVALID_NAME)rrrrr check_nameZs zipset.check_namecCsg}d}y|jdg}Wn0tk rH}ztjd|WYdd}~XnX|j}d}xT|D]L}|r|jjdd}|d|kr|dtkr|j|d|j dr\d }q\W|S) z?Return types that are supported by the ipset command and kernelz--helpzipset error: %sNFrzSupported set types:T) _ipset__runrrZdebug1 splitlinesstripsplit IPSET_TYPESappend startswith)rr"outputZexlinesZin_typeslinesplitsrrrset_supported_types`s    zipset.set_supported_typescCs(t|tks|tkr$ttjd|dS)zCheck ipset typez!ipset type name '%s' is not validN)r#r$r,rrZ INVALID_TYPE)r type_namerrr check_typeuszipset.check_typeNcCsd|j||j|d||g}t|trZx0|jD]$\}}|j||dkr2|j|q2W|j|S)z+Create an ipset with name, type and optionscreater&)r%r5 isinstancedictitemsr-r()rset_namer4optionsr keyvalrrr set_create{s     zipset.set_createcCs|j||jd|gS)NZdestroy)r%r()rr:rrr set_destroys zipset.set_destroycCsd||g}|j|S)Nadd)r()rr:entryr rrrset_adds z ipset.set_addcCsd||g}|j|S)Ndel)r()rr:rAr rrr set_deletes zipset.set_deletecCs,d||g}|r"|jddj||j|S)Ntestz%sr)r-rr()rr:rAr;r rrrrEs z ipset.testcCs2dg}|r|j||r"|j||j|jdS)Nlist )r-extendr(r+)rr:r;r rrrset_lists   zipset.set_listc Cs<|jdgd}i}d}}i}x|D] }t|dkr:q&dd|jddD}t|dkr`q&q&|d d krv|d}q&|d d kr|d}q&|d d kr&|dj}d } x^| t|kr|| } | dkrt|| kr| d7} || || <ntjd|iS| d7} qW|r$|r$|t|f||<d}}|jq&W|S)z" Get active ipsets (only headers) z-terse)r;NcSsg|] }|jqSr)r*)rxrrrrsz.ipset.set_get_active_terse..:r'rNameZTypeZHeaderrrrrnetmaskz&Malformed ipset list -terse output: %s)rrrrrN)rIr#r+rerrorrclear) rr0r"_nameZ_type_optionsr1Zpairr2ioptrrrset_get_active_tersesD            zipset.set_get_active_tersecCsdg}|r|j||j|S)Nsave)r-r()rr:r rrrrVs z ipset.savecCs|j||j|t}d|kr*d|}d||dg}|rlx0|jD]$\}} |j|| dkrD|j| qDW|jddj||jd|xN|D]F} d| krd| } |r|jd|| dj|fq|jd || fqW|jtj |j } t j d |j |jd |j | jfd g}t|j||j d \} } t jdkryt|j Wntk r`YnVXd}xNt|j D]@}t jd||fddd|jdst jddd|d7}qrWtj|j | dkrtd|jdj|| f| S)Nrz'%s'r6z-existr&z%s z flush %s z add %s %s %s z add %s %s z%s: %s restore %sz%s: %dZrestore)stdinr'rJz%8d: %sr)nofmtnlrG)rXz'%s %s' failed: %s)r%r5r r9r-writercloseosstatrrrrrst_sizerZgetDebugLogLevelr ExceptionZdebug3endswithunlinkr)rr:r4entriesZcreate_optionsZ entry_optionsZ temp_filer r<r=rAr]r!r"rSr1rrr set_restoresV         zipset.set_restorecCsdg}|r|j||j|S)Nflush)r-r()rr:r rrr set_flushs zipset.set_flushcCs|jd||gS)Nrename)r()rZ old_set_nameZ new_set_namerrrrf sz ipset.renamecCs|jd||gS)Nswap)r()rZ set_name_1Z set_name_2rrrrgsz ipset.swapcCs |jdgS)Nversion)r()rrrrrhsz ipset.version)N)N)NN)N)NN)__name__ __module__ __qualname____doc__rr(r%r3r5r>r?rBrDrErIrUrVrcrerfrgrhrrrrrHs&    '  7cCst|tkrdSdS)z"Return true if ipset name is validFT)r#r$)rrrrrs cCs8|j}x*tD]"}||krt|||kr||=qW|S)z( Return only non default create options )copyIPSET_DEFAULT_CREATE_OPTIONS)r;rRrTrrrrs   c Cshg}xX|jdD]J}y&|jd|jttj|ddWqtk rX|j|YqXqWdj|S)z! Normalize IP addresses in entry ,/F)strict)r+indexr-str ipaddress ip_networkrr)rAZ_entryZ_partrrrnormalize_ipset_entry&s rvcCsxt|jddkrdSytj|dd}Wntk r<dSXx4|D],}|jtj|ddrDttjdj ||qDWdS)z: Check if entry overlaps any entry in the list of entries rorJNF)rqz,Entry '{}' overlaps with existing entry '{}') r#r+rtruroverlapsrr INVALID_ENTRYformat)rArbZ entry_networkZitrrrrcheck_entry_overlaps_existing2s rzcCs~ydd|D}Wntk r&dSXt|dkr8dS|j|jd}x.|D]&}|j|rrttjdj|||}qPWdS)z> Check if any entry overlaps any entry in the list of entries cSsg|]}tj|ddqS)F)rq)rtru)rrKrrrrEsz1check_for_overlapping_entries..NrzEntry '{}' overlaps entry '{}') rr#sortpoprwrrrxry)rbZ prev_networkZcurrent_networkrrrcheck_for_overlapping_entriesBs 2   r})rl__all__Zos.pathr\rtZfirewallrZfirewall.errorsrZfirewall.core.progrZfirewall.core.loggerrZfirewall.functionsr r Zfirewall.configr r$r,ZIPSET_CREATE_OPTIONSrnobjectrrrrvrzr}rrrrsF      P  PK!ܥ]Z]Z'__pycache__/logger.cpython-36.opt-1.pycnu[3 Yj>y@sddddgZddlZddlZddlZddlZddlZddlZddlZddlZddl Z ddl Z Gddde Z Gddde Z Gd d d e ZGd d d e ZGd dde ZGddde ZeZdS) LogTargetFileLogLoggerlogNc@s2eZdZdZddZd ddZddZd d Zd S) rz% Abstract class for logging targets. cCs d|_dS)N)fd)selfr/usr/lib/python3.6/logger.py__init__(szLogTarget.__init__rcCs tddS)Nz%LogTarget.write is an abstract method)NotImplementedError)rdatalevelloggeris_debugrrr write+szLogTarget.writecCs tddS)Nz%LogTarget.flush is an abstract method)r )rrrr flush.szLogTarget.flushcCs tddS)Nz%LogTarget.close is an abstract method)r )rrrr close1szLogTarget.closeN)r)__name__ __module__ __qualname____doc__r rrrrrrr r&s  c@s.eZdZddZd ddZddZdd Zd S) _StdoutLogcCstj|tj|_dS)N)rr sysstdoutr)rrrr r 8s z_StdoutLog.__init__rcCs|jj||jdS)N)rrr)rr r rrrrr r<s z_StdoutLog.writecCs |jdS)N)r)rrrr rAsz_StdoutLog.closecCs|jjdS)N)rr)rrrr rDsz_StdoutLog.flushN)r)rrrr rrrrrrr r7s rc@seZdZddZdS) _StderrLogcCstj|tj|_dS)N)rr rstderrr)rrrr r Ks z_StderrLog.__init__N)rrrr rrrr rJsrc@s.eZdZddZd ddZddZdd Zd S) _SyslogLogcCs.tj|tjtjjtjdtj tj dS)Nr) rr syslogZopenlogospathbasenamerargvZLOG_PIDZ LOG_DAEMON)rrrr r Ss z_SyslogLog.__init__rcCsd}|rtj}nF||jkr"tj}n4||jkr4tj}n"||jkrFtj}n||jkrVtj }|j drt|dt |d}t |dkr|dkrtj|n tj||dS)N r) rZ LOG_DEBUGINFO1ZLOG_INFOWARNINGZ LOG_WARNINGERRORZLOG_ERRFATALZLOG_CRITendswithlen)rr r rrZpriorityrrr ras"       z_SyslogLog.writecCs tjdS)N)rZcloselog)rrrr rwsz_SyslogLog.closecCsdS)Nr)rrrr rzsz_SyslogLog.flushN)r)rrrr rrrrrrr rRs rc@s<eZdZdZdddZddZddd Zd d Zd d ZdS)rz< FileLog class. File will be opened on the first write. wcCstj|||_||_dS)N)rr filenamemode)rr+r,rrr r s zFileLog.__init__cCsv|jr dStjtjB}|jjdr,|tjO}tj|j|d|_tj |jdtj |j|j|_t j |jt j t j dS)Nai)rrO_CREATO_WRONLYr, startswithO_APPENDopenr+fchmodfdopenfcntlZF_SETFDZ FD_CLOEXEC)rflagsrrr r2s   z FileLog.openrcCs(|js|j|jj||jjdS)N)rr2rr)rr r rrrrr rs z FileLog.writecCs|js dS|jjd|_dS)N)rr)rrrr rs z FileLog.closecCs|js dS|jjdS)N)rr)rrrr rsz FileLog.flushN)r*)r) rrrrr r2rrrrrrr rs   c@seZdZdZd[Zd\Zd]Zd^Zd_ZdZ e Z e Z eZd`d d Zd d ZdaddZdbddZdcddZddddZddZddZddZddZddZd d!Zed"fd#d$Zed"fd%d&Zed"fd'd(Zed"fd)d*Zed"fd+d,Z ed"fd-d.Z!d/d0Z"d1d2Z#d3d4Z$d5d6Z%d7d8Z&d9d:Z'd;d<Z(d=d>Z)d?d@Z*dAdBZ+dCdDZ,dedEdFZ-dGdHZ.dfdIdJZ/ed"dfdKdLZ0ed"dfdMdNZ1ed"dfdOdPZ2dgdQdRZ3dSdTZ4dUdVZ5dWdXZ6dhdYdZZ7d"S)iraL Format string: %(class)s Calling class the function belongs to, else empty %(date)s Date using Logger.date_format, see time module %(domain)s Full Domain: %(module)s.%(class)s.%(function)s %(file)s Filename of the module %(function)s Function name, empty in __main__ %(label)s Label according to log function call from Logger.label %(level)d Internal logging level %(line)d Line number in module %(module)s Module name %(message)s Log message Standard levels: FATAL Fatal error messages ERROR Error messages WARNING Warning messages INFOx, x in [1..5] Information DEBUGy, y in [1..10] Debug messages NO_INFO No info output NO_DEBUG No debug output INFO_MAX Maximum info level DEBUG_MAX Maximum debug level x and y depend on info_max and debug_max from Logger class initialization. See __init__ function. Default logging targets: stdout Logs to stdout stderr Logs to stderr syslog Logs to syslog Additional arguments for logging functions (fatal, error, warning, info and debug): nl Disable newline at the end with nl=0, default is nl=1. fmt Format string for this logging entry, overloads global format string. Example: fmt="%(file)s:%(line)d %(message)s" nofmt Only output message with nofmt=1. The nofmt argument wins over the fmt argument. Example: from logger import log log.setInfoLogLevel(log.INFO1) log.setDebugLogLevel(log.DEBUG1) for i in range(1, log.INFO_MAX+1): log.setInfoLogLabel(i, "INFO%d: " % i) log.setFormat("%(date)s %(module)s:%(line)d [%(domain)s] %(label)s: " "%(level)d %(message)s") log.setDateFormat("%Y-%m-%d %H:%M:%S") fl = FileLog("/tmp/log", "a") log.addInfoLogging("*", fl) log.addDebugLogging("*", fl) log.addInfoLogging("*", log.syslog, fmt="%(label)s%(message)s") log.debug3("debug3") log.debug2("debug2") log.debug1("debug1") log.info2("info2") log.info1("info1") log.warning("warning\n", nl=0) log.error("error\n", nl=0) log.fatal("fatal") log.info(log.INFO1, "nofmt info", nofmt=1) r#r cCsi|_i|_d|_d|_i|_i|_i|_i|_i|_i|_ |dkrPt d||dkrdt d||j |_ ||_ d|_||_|j|jd|j|jd|j|jd|j|j dxNtd|j dD]:}t|d |||j|dt|d |d d ||qWxTtd|jdD]@}t|d |||j|d|t|d|dd ||qW|j|j|j|j|jd|jd|jd|j|j|j|j g|jd|jddt|j|j dD|jd|jddtd|jdDdS)z Logger class initialization r#zLogger: info_max %d is too lowrzLogger: debug_max %d is too lowz FATAL ERROR: zERROR: z WARNING: zINFO%dzinfo%dcsfddS)Ncsj|f||S)N)info)messageargskwargs)rxrr  sz3Logger.__init__....r)rrAr)rrAr rBsz!Logger.__init__..zDEBUG%dz DEBUG%d: zdebug%dcsfddS)Ncsj|f||S)N)debug)r>r?r@)rrArr rB)sz3Logger.__init__....r)rrAr)rrAr rB(sz%(label)s%(message)sz%d %b %Y %H:%M:%S*cSsg|]}|qSrr).0irrr 4sz#Logger.__init__..cSsg|]}|qSrr)rErFrrr rG6sN) _level _debug_level_format _date_format_label _debug_label_logging_debug_logging_domains_debug_domains ValueErrorr%NO_INFOINFO_MAXNO_DEBUG DEBUG_MAXsetInfoLogLabelr' TRACEBACKr&rangesetattrsetDebugLogLabelsetInfoLogLevelr$setDebugLogLevel setFormat setDateFormatsetInfoLoggingrrsetDebugLogging)rZinfo_maxZ debug_maxrHrrr r sX            zLogger.__init__cCsNxHt|j|jdD]2}||jkr$qx |j|D]\}}}|jq0WqWdS)z Close all logging targets r#N)rYr'rVrNr)rr dummytargetrrr r8s  z Logger.closerDcCs$|j|||jkr|j|S|jS)z Get info log level. ) _checkDomainrHNOTHING)rdomainrrr getInfoLogLevel@s   zLogger.getInfoLogLevelcCs8|j|||jkr|j}||jkr*|j}||j|<dS)z% Set log level [NOTHING .. INFO_MAX] N)rdrerTrH)rr rfrrr r\Gs    zLogger.setInfoLogLevelcCs*|j|||jkr$|j||jS|jS)z Get debug log level. )rdrIrU)rrfrrr getDebugLogLevelPs  zLogger.getDebugLogLevelcCs:|j||dkrd}||jkr&|j}||j|j|<dS)z- Set debug log level [NO_DEBUG .. DEBUG_MAX] rN)rdrVrUrI)rr rfrrr r]Ws   zLogger.setDebugLogLevelcCs|jS)N)rJ)rrrr getFormat`szLogger.getFormatcCs ||_dS)N)rJ)rrJrrr r^cszLogger.setFormatcCs|jS)N)rK)rrrr getDateFormatfszLogger.getDateFormatcCs ||_dS)N)rK)rrJrrr r_iszLogger.setDateFormatcCs:|j|}x*|D]"}|j||j|jd||j|<qWdS)zU Set log label for level. Level can be a single level or an array of levels. ) min_level max_levelN) _getLevels_checkLogLevelr'rTrL)rr labellevelsrrr rWls     zLogger.setInfoLogLabelcCs>|j|dd}x*|D]"}|j||j|jd||j|<qWdS)zU Set log label for level. Level can be a single level or an array of levels. r#)r)rkrlN)rmrnr$rVrM)rr rorprrr r[us    zLogger.setDebugLogLabelNcCs|j||||dddS)z Set info log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r)rN) _setLogging)rrfrcr fmtrrr r`~szLogger.setInfoLoggingcCs|j||||dddS)z Set debug log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r#)rN)rq)rrfrcr rrrrr raszLogger.setDebugLoggingcCs|j||||dddS)z Add info log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r)rN) _addLogging)rrfrcr rrrrr addInfoLoggingszLogger.addInfoLoggingcCs|j||||dddS)z Add debg log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r#)rN)rs)rrfrcr rrrrr addDebugLoggingszLogger.addDebugLoggingcCs|j||||dddS)z Delete info log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r)rN) _delLogging)rrfrcr rrrrr delInfoLoggingszLogger.delInfoLoggingcCs|j||||dddS)z Delete debug log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. r#)rN)rv)rrfrcr rrrrr delDebugLoggingszLogger.delDebugLoggingcCs|j|ddS)zN Is there currently any info logging for this log level (and domain)? r)r)_isLoggingHere)rr rrr isInfoLoggingHereszLogger.isInfoLoggingHerecCs|j|ddS)zO Is there currently any debug logging for this log level (and domain)? r#)r)ry)rr rrr isDebugLoggingHereszLogger.isDebugLoggingHerecOs,|j|d|d<|j|j|f||dS)z Fatal error log. rrN) _checkKWargs_logr')rrJr?r@rrr fatals z Logger.fatalcOs,|j|d|d<|j|j|f||dS)z Error log. rrN)r|r}r&)rrJr?r@rrr errors z Logger.errorcOs,|j|d|d<|j|j|f||dS)z Warning log. rrN)r|r}r%)rrJr?r@rrr warnings zLogger.warningcOsB|j|d|jd|j|d|d<|j||j|f||dS)z Information log using info level [1..info_max]. There are additional infox functions according to info_max from __init__r#)rkrlrrN)rnrTr|r}rS)rr rJr?r@rrr r=s z Logger.infocOs<|j|d|jd|j|d|d<|j||f||dS)z Debug log using debug level [1..debug_max]. There are additional debugx functions according to debug_max from __init__r#)rkrlrN)rnrVr|r})rr rJr?r@rrr rCs z Logger.debugcCs|j|jtjgiddS)N)r?r@)r}rX traceback format_exc)rrrr exceptionszLogger.exceptioncCs&||ks||kr"td|||fdS)Nz*Level %d out of range, should be [%d..%d].)rR)rr rkrlrrr rnszLogger._checkLogLevelcCs2|sdSx$|jD]}|dkrtd|qWdS)Nnlrrnofmtz0Key '%s' is not allowed as argument for logging.)rrrr)keysrR)rr@keyrrr r|s zLogger._checkKWargscCs| s|dkrtd|dS)Nr<zDomain '%s' is not valid.)rR)rrfrrr rdszLogger._checkDomaincCs||jkrft|tst|tr$|}n|g}xp|D]0}|rL|j|d|jdq0|j||j|jdq0Wn6|rddt|j |jD}nddt|j|jD}|S)z Generate log level array. r#)rkrlcSsg|]}|qSrr)rErFrrr rGsz%Logger._getLevels..cSsg|]}|qSrr)rErFrrr rGs) ALL isinstancelisttuplernrVr'rTrYZDEBUG1)rr rrprrr rms    zLogger._getLevelscCsNt|tst|tr|}n|g}x(|D] }t|jts&td|jjq&W|S)z Generate target array. z '%s' is no valid logging target.)rrr issubclass __class__rrRr)rrctargetsZ_targetrrr _getTargetss  zLogger._getTargetscCs|r |j}|j}d|jdf}n|j}|j}|j|jdf}t|dkrP|jxVt |d|dD]@}||krrqdx0||D]$\}}}||kr||j |gj |q|WqdWdS)z% Generate dict with domain by level. r#rN) rQrOrVrPrNr'rTr)clearrY setdefaultappend)rrrPrNZ_ranger rfrbrrr _genDomainss zLogger._genDomainsc Csl|j||j||}|j|}|r,|j}n|j}x*|D]"}x|D]}|||fg||<qBWq8W|j|dS)N)rdrmrrOrNr) rrfrcr rrrrprrNrrr rqs     zLogger._setLoggingc Cst|j||j||}|j|}|r,|j}n|j}x2|D]*}x$|D]}|j|gj|||fqBWq8W|j|dS)N)rdrmrrOrNrrr) rrfrcr rrrrprrNrrr rs-s      zLogger._addLoggingc Cs|j||j||}|j|}|r,|j}n|j}x|D]|} xv|D]n}| |krPqB|||f|| kr|| j|||ft|| dkr|| =qB||jkrBtd| ||j j |fqBWq8W|j |dS)NrzDNo mathing logging for level %d, domain %s, target %s and format %s.) rdrmrrOrNremover)rrRrrr) rrfrcr rrrrprrNrHrrr rv<s&      zLogger._delLoggingcCst|j||}|sdS|dd}|r,|j}n|j}x<||D]0\}}}|dksh|j|shtj|d|rrfrrDrrr") rr)rOrNr0rrrJrr)rr rJr?r@rrrrrrNZ used_targetsrfrcrrr r}sL     z Logger._logc Csg}d}|r |j}|j}|j}n|j}|j}|j}xN|D]F}|dkrh|||kr~d}t|dkrdg}Pq8|||kr8|j|q8W| rt|dkrdS||krdStj } x$| r| j r| j d|j kr| j } qW| st d| j d} | d } x|D]}| j|rg}PqW| j} t| } xx||D]l}|jd}|dkrDq&n|dkr\|d|}n|}| t|kr| j|sdSn|j| s&dSq&Wd }||kr||}| j| j| d | jd ||tj|jtjd }|d d krd |d <d}x&||D]}|dkrqd}PqW|jjddksR|jjddksR|sRt|dkrl|j| }|rl|j|d<d |d|d<|dd kr|dd |d7<|d d kr|dd |d 7<t|dkr|S|dd }x0|D](}|j|stj|d|r|SqWdS)z Internal function. FrDTrr#Nrz Frame information not available.rr<) filelinerclassfunctionrfror Zdater?z %(domain)z%(class)rrrf)rIrQrMrHrPrLr)rrZ currentframef_back f_globalsrrRr0rfind co_filenamef_linenortimeZstrftimerKZ localtimerJrrrr)rr rZ check_domainsZ simple_matchrrPrLrffZ module_nameZ point_moduleco_lenrFdZ level_strZ domain_neededrrrrr rs                      zLogger._genDict)r7r;)rD)rD)rD)rD)r)r)r)r)8rrrrrrer'rXr&r%rrrrrrr rrgr\rhr]rir^rjr_rWr[r`rartrurwrxrzr{r~rrr=rCrrnr|rdrmrrrqrsrvryrrr}rrrrr rsdG ;           4)__all__rrrrrrrr5Zos.pathrobjectrrrrrrrrrrr s. -(*4PK!T22*__pycache__/fw_direct.cpython-36.opt-1.pycnu[3 YjW@sndgZddlmZddlmZddlmZddlmZddlm Z ddl m Z ddl m Z Gd ddeZd S) FirewallDirect)LastUpdatedOrderedDict) ipXtables)ebtables)FirewallTransaction)log)errors) FirewallErrorc@sLeZdZddZddZddZddZd d Zd d Zd dZ ddZ dNddZ ddZ ddZ dOddZddZddZddZd d!ZdPd"d#ZdQd$d%Zd&d'Zd(d)Zd*d+ZdRd,d-ZdSd.d/Zd0d1Zd2d3Zd4d5Zd6d7Zd8d9Zd:d;ZdTdd?Z!d@dAZ"dBdCZ#dDdEZ$dFdGZ%dHdIZ&dJdKZ'dLdMZ(dS)VrcCs||_|jdS)N)_fw_FirewallDirect__init_vars)selffwr/usr/lib/python3.6/fw_direct.py__init__'szFirewallDirect.__init__cCsd|j|j|j|jfS)Nz%s(%r, %r, %r)) __class___chains_rules_rule_priority_positions)r rrr__repr__+szFirewallDirect.__repr__cCs"i|_i|_i|_i|_d|_dS)N)rrr _passthroughs_obj)r rrrZ __init_vars/s zFirewallDirect.__init_varscCs |jdS)N)r )r rrrcleanup6szFirewallDirect.cleanupcCs t|jS)N)rr )r rrrnew_transaction;szFirewallDirect.new_transactioncCs ||_dS)N)r)r objrrrset_permanent_config@sz#FirewallDirect.set_permanent_configcCs*t|jt|jt|jdkr&dSdS)NrTF)lenrrr)r rrrhas_runtime_configurationCs"z(FirewallDirect.has_runtime_configurationcCsB|jr dSt|jjt|jjt|jjdkr>dSdS)NTrF)rrrget_all_chains get_all_rulesget_all_passthroughs)r rrrhas_configurationHs z FirewallDirect.has_configurationNcCsP|dkr|j}n|}|j|jj|jj|jjf||dkrL|jddS)NT)r set_configrrrr execute)r use_transaction transactionrrr apply_directQs   zFirewallDirect.apply_directc Csi}i}i}xL|jD]B}|\}}x4|j|D]&}|jj|||s,|j|gj|q,WqWxf|jD]\}|\}}}xL|j|D]>\} } |jj|||| | s|||krt||<| ||| | f<q|WqbWxP|jD]F}x@|j|D]2} |jj || s||krg||<||j| qWqW|||fS)N) rr query_chain setdefaultappendr query_rulerrquery_passthrough) r ZchainsrulesZ passthroughstable_idipvtablechainchain_idpriorityargsrrrget_runtime_configbs,      z!FirewallDirect.get_runtime_configcCs|j|j|jfS)N)rrr)r rrr get_configszFirewallDirect.get_configcCs|dkr|j}n|}|\}}}x||D]t}|\}} xf||D]Z} |j|| | sr nftables_enabledget_direct_backend_by_ipv our_chainsrZ OUR_CHAINSr rZ BUILTIN_CHAINzoneZzone_from_chainZ INVALID_CHAIN)r r.r/r0Zbuilt_in_chainsrCrrr_check_builtin_chains"     z#FirewallDirect._check_builtin_chaincCsH|r|jj|gj|n*|j|j|t|j|dkrD|j|=dS)Nr)rr(r)remover)r r-r0addrrr_register_chains zFirewallDirect._register_chaincCsV|dkr|j}n|}|jjr.|j|jj|jd|||||dkrR|jddS)NT)rr may_skip_flush_direct_backendsadd_preflush_direct_backends_chainr#)r r.r/r0r$r%rrrr6s  zFirewallDirect.add_chaincCs>|dkr|j}n|}|jd|||||dkr:|jddS)NFT)rrLr#)r r.r/r0r$r%rrr remove_chains  zFirewallDirect.remove_chaincCs:|j|||j|||||f}||jko8||j|kS)N)r@rEr)r r.r/r0r-rrrr's   zFirewallDirect.query_chaincCs,|j||||f}||jkr(|j|SgS)N)r@r)r r.r/r-rrr get_chainss    zFirewallDirect.get_chainscCsDg}x:|jD]0}|\}}x"|j|D]}|j|||fq$Wq W|S)N)rr))r rkeyr.r/r0rrrrs  zFirewallDirect.get_all_chainscCsZ|dkr|j}n|}|jjr.|j|jj|jd|||||||dkrV|jddS)NT)rr rIrJrK_ruler#)r r.r/r0r2r3r$r%rrrr8 s  zFirewallDirect.add_rulecCsB|dkr|j}n|}|jd|||||||dkr>|jddS)NFT)rrQr#)r r.r/r0r2r3r$r%rrr remove_rules  zFirewallDirect.remove_rulecCs2|j|||||f}||jko0||f|j|kS)N)r@r)r r.r/r0r2r3r1rrrr*#s   zFirewallDirect.query_rulecCs6|j|||||f}||jkr2t|j|jSgS)N)r@rlistr?)r r.r/r0r1rrr get_rules)s    zFirewallDirect.get_rulesc CsRg}xH|jD]>}|\}}}x.|j|D] \}}|j||||t|fq&Wq W|S)N)rr)rS)r rOrPr.r/r0r2r3rrrr0s    zFirewallDirect.get_all_rulescCs|rr||jkrt|j|<||j||<||jkrg}x4|jD]*}x$|j|D]}|j|t|fqWq W|S)N)rr)rS)r rOr.r3rrrr {s  z#FirewallDirect.get_all_passthroughscCs4g}||jkr0x |j|D]}|jt|qW|S)N)rr)rS)r r.rOr3rrrget_passthroughss  zFirewallDirect.get_passthroughsc Csg}x|D]}d}x|D]}y|j|}Wntk r>YqXt||krd||dkrd}||djd}x.|D]&} |dd} | | |d<|j| qxWqW|s |j|q W|S)z5Split values combined with commas for options in optsF,TN)index ValueErrorrsplitr)) r r,ZoptsZ out_rulesrYZ processedoptiitemsitemrQrrr split_values$     zFirewallDirect.split_valuec Cs*|j|||jj r2|dkr2|jjj|||||}|jj|} |jj rd| j|||rdd|}n:|jjr|dddkr| j|||ddr|dd}|||f} ||f} |r| |jkr| |j| krtt j d||||fnB| |jks| |j| krtt j d||||f|j| | }d} d } | |j krt |j | j}d }x@|t|kr|||kr| |j | ||7} |d7}qTWt|g}|j|d d g}|j|d d g}x<|D]4}|j| | j|||| t|| d7} | d7} qW|j| | ||| |j|j| | || | dS)Nr;r<z %s_directZ_directz"rule '%s' already is in '%s:%s:%s'zrule '%s' is not in '%s:%s:%s'rdrz-sz--sourcez-dz --destination)r;r<iii)r@r rArDcreate_zone_base_by_chainrBZis_chain_builtinrr rALREADY_ENABLED NOT_ENABLEDrsortedr?rrSrlr8Z build_rulerarXadd_fail)r rVr.r/r0r2r3r%rLbackendr1rUrerWZ positionsjZ args_list_argsrrrrQsZ         (   zFirewallDirect._rulecCs|j|||j|||||f}|rV||jkr||j|krttjd|||fn.||jksn||j|krttjd|||f|jj|}|j ||j ||||j ||||j |j ||| dS)Nz chain '%s' already is in '%s:%s'zchain '%s' is not in '%s:%s') r@rErr rrorpr rBZ add_rulesZbuild_chain_rulesrHrr)r rGr.r/r0r%r-rsrrrrLs$    zFirewallDirect._chainc Cs|j|t|}|rD||jkrp||j|krpttjd||fn,||jks\||j|krpttjd||f|jj|}|r|j ||dkr|j |\}}|r|r|jj j ||||} n |j |} |j|| |j||||j|j||| dS)Nzpassthrough '%s', '%s'r;r<)r;r<)r=rarr rrorpr rBZcheck_passthroughZpassthrough_parse_table_chainrDrnZreverse_passthroughr8r^rr) r rVr.r3r%Z tuple_argsrsr/r0rurrrr_'s0        zFirewallDirect._passthrough)N)N)N)N)N)N)N)N))__name__ __module__ __qualname__rrr rrrrr!r&r4r5r"r=r@rErHr6rMr'rNrr8rRr*rTrrXr]r^r9r`r+r rbrlrQrLr_rrrrr&sL  '       jN)__all__Zfirewall.fw_typesrZ firewall.corerrZfirewall.core.fw_transactionrZfirewall.core.loggerrZfirewallrZfirewall.errorsr objectrrrrrs       PK![{ޣ$__pycache__/fw_helper.cpython-36.pycnu[3 Yj)@s6dZdgZddlmZddlmZGdddeZdS)zhelper backendFirewallHelper)errors) FirewallErrorc@s\eZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ ddZ dS)rcCs||_i|_dS)N)Z_fw_helpers)selffwr/usr/lib/python3.6/fw_helper.py__init__szFirewallHelper.__init__cCsd|j|jfS)Nz%s(%r)) __class__r)rrrr __repr__"szFirewallHelper.__repr__cCs|jjdS)N)rclear)rrrr cleanup'szFirewallHelper.cleanupcCs||jkrttj|dS)N) get_helpersrrINVALID_HELPER)rnamerrr check_helper*s zFirewallHelper.check_helpercCs ||jkS)N)r)rrrrr query_helper.szFirewallHelper.query_helpercCst|jjS)N)sortedrkeys)rrrr r1szFirewallHelper.get_helperscCst|jdkS)Nr)lenr)rrrr has_helpers4szFirewallHelper.has_helperscCs|j||j|S)N)rr)rrrrr get_helper7s zFirewallHelper.get_helpercCs||j|j<dS)N)rr)robjrrr add_helper;szFirewallHelper.add_helpercCs"||jkrttj||j|=dS)N)rrrr)rrrrr remove_helper>s  zFirewallHelper.remove_helperN) __name__ __module__ __qualname__r r rrrrrrrrrrrr rsN)__doc____all__ZfirewallrZfirewall.errorsrobjectrrrrr s  PK!$$ ebtables.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2010-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "ebtables" ] import os.path from firewall.core.prog import runProg from firewall.core.logger import log from firewall.functions import tempFile, readfile, splitArgs from firewall.config import COMMANDS from firewall.core import ipXtables # some common stuff lives there from firewall.errors import FirewallError, INVALID_IPV import string BUILT_IN_CHAINS = { "broute": [ "BROUTING" ], "nat": [ "PREROUTING", "POSTROUTING", "OUTPUT" ], "filter": [ "INPUT", "OUTPUT", "FORWARD" ], } DEFAULT_RULES = { } LOG_RULES = { } OUR_CHAINS = {} # chains created by firewalld for table in BUILT_IN_CHAINS.keys(): DEFAULT_RULES[table] = [ ] OUR_CHAINS[table] = set() for chain in BUILT_IN_CHAINS[table]: DEFAULT_RULES[table].append("-N %s_direct" % chain) DEFAULT_RULES[table].append("-I %s 1 -j %s_direct" % (chain, chain)) DEFAULT_RULES[table].append("-I %s_direct 1 -j RETURN" % chain) OUR_CHAINS[table].add("%s_direct" % chain) class ebtables(object): ipv = "eb" name = "ebtables" policies_supported = False # ebtables only supported with direct interface def __init__(self): self._command = COMMANDS[self.ipv] self._restore_command = COMMANDS["%s-restore" % self.ipv] self.restore_noflush_option = self._detect_restore_noflush_option() self.concurrent_option = self._detect_concurrent_option() self.fill_exists() self.available_tables = [] def fill_exists(self): self.command_exists = os.path.exists(self._command) self.restore_command_exists = os.path.exists(self._restore_command) def _detect_concurrent_option(self): # Do not change any rules, just try to use the --concurrent option # with -L concurrent_option = "" ret = runProg(self._command, ["--concurrent", "-L"]) if ret[0] == 0: concurrent_option = "--concurrent" # concurrent for ebtables lock return concurrent_option def _detect_restore_noflush_option(self): # Do not change any rules, just try to use the restore command # with --noflush rules = [ ] try: self.set_rules(rules, "off") except ValueError: return False return True def __run(self, args): # convert to string list _args = [ ] if self.concurrent_option and self.concurrent_option not in args: _args.append(self.concurrent_option) _args += ["%s" % item for item in args] log.debug2("%s: %s %s", self.__class__, self._command, " ".join(_args)) (status, ret) = runProg(self._command, _args) if status != 0: raise ValueError("'%s %s' failed: %s" % (self._command, " ".join(args), ret)) return ret def _rule_validate(self, rule): for str in ["%%REJECT%%", "%%ICMP%%", "%%LOGTYPE%%"]: if str in rule: raise FirewallError(INVALID_IPV, "'%s' invalid for ebtables" % str) def is_chain_builtin(self, ipv, table, chain): return table in BUILT_IN_CHAINS and \ chain in BUILT_IN_CHAINS[table] def build_chain_rules(self, add, table, chain): rules = [] if add: rules.append([ "-t", table, "-N", chain ]) rules.append([ "-t", table, "-I", chain, "1", "-j", "RETURN" ]) else: rules.append([ "-t", table, "-X", chain ]) return rules def build_rule(self, add, table, chain, index, args): rule = [ "-t", table ] if add: rule += [ "-I", chain, str(index) ] else: rule += [ "-D", chain ] rule += args return rule def reverse_rule(self, args): return ipXtables.common_reverse_rule(args) def check_passthrough(self, args): ipXtables.common_check_passthrough(args) def reverse_passthrough(self, args): return ipXtables.common_reverse_passthrough(args) def set_rules(self, rules, log_denied): temp_file = tempFile() table = "filter" table_rules = { } for _rule in rules: rule = _rule[:] self._rule_validate(rule) # get table form rule for opt in [ "-t", "--table" ]: try: i = rule.index(opt) except ValueError: pass else: if len(rule) >= i+1: rule.pop(i) table = rule.pop(i) # we can not use joinArgs here, because it would use "'" instead # of '"' for the start and end of the string, this breaks # iptables-restore for i in range(len(rule)): for c in string.whitespace: if c in rule[i] and not (rule[i].startswith('"') and rule[i].endswith('"')): rule[i] = '"%s"' % rule[i] table_rules.setdefault(table, []).append(rule) for table in table_rules: temp_file.write("*%s\n" % table) for rule in table_rules[table]: temp_file.write(" ".join(rule) + "\n") temp_file.close() stat = os.stat(temp_file.name) log.debug2("%s: %s %s", self.__class__, self._restore_command, "%s: %d" % (temp_file.name, stat.st_size)) args = [ ] args.append("--noflush") (status, ret) = runProg(self._restore_command, args, stdin=temp_file.name) if log.getDebugLogLevel() > 2: lines = readfile(temp_file.name) if lines is not None: i = 1 for line in lines: log.debug3("%8d: %s" % (i, line), nofmt=1, nl=0) if not line.endswith("\n"): log.debug3("", nofmt=1) i += 1 os.unlink(temp_file.name) if status != 0: raise ValueError("'%s %s' failed: %s" % (self._restore_command, " ".join(args), ret)) def set_rule(self, rule, log_denied): self._rule_validate(rule) return self.__run(rule) def get_available_tables(self, table=None): ret = [] tables = [ table ] if table else BUILT_IN_CHAINS.keys() for table in tables: if table in self.available_tables: ret.append(table) else: try: self.__run(["-t", table, "-L"]) self.available_tables.append(table) ret.append(table) except ValueError: log.debug1("ebtables table '%s' does not exist." % table) return ret def get_zone_table_chains(self, table): return {} def build_flush_rules(self): rules = [] for table in BUILT_IN_CHAINS.keys(): if table not in self.get_available_tables(): continue # Flush firewall rules: -F # Delete firewall chains: -X # Set counter to zero: -Z for flag in [ "-F", "-X", "-Z" ]: rules.append(["-t", table, flag]) return rules def build_set_policy_rules(self, policy): rules = [] _policy = "DROP" if policy == "PANIC" else policy for table in BUILT_IN_CHAINS.keys(): if table not in self.get_available_tables(): continue for chain in BUILT_IN_CHAINS[table]: rules.append(["-t", table, "-P", chain, _policy]) return rules def build_default_tables(self): # nothing to do, they always exist return [] def build_default_rules(self, log_denied="off"): default_rules = [] for table in DEFAULT_RULES: if table not in self.get_available_tables(): continue _default_rules = DEFAULT_RULES[table][:] if log_denied != "off" and table in LOG_RULES: _default_rules.extend(LOG_RULES[table]) prefix = [ "-t", table ] for rule in _default_rules: if type(rule) == list: default_rules.append(prefix + rule) else: default_rules.append(prefix + splitArgs(rule)) return default_rules def is_ipv_supported(self, ipv): return ipv == self.ipv PK!4C88rich.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2013-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "Rich_Source", "Rich_Destination", "Rich_Service", "Rich_Port", "Rich_Protocol", "Rich_Masquerade", "Rich_IcmpBlock", "Rich_IcmpType", "Rich_SourcePort", "Rich_ForwardPort", "Rich_Log", "Rich_Audit", "Rich_Accept", "Rich_Reject", "Rich_Drop", "Rich_Mark", "Rich_Limit", "Rich_Rule" ] from firewall import functions from firewall.core.ipset import check_ipset_name from firewall.core.base import REJECT_TYPES from firewall import errors from firewall.errors import FirewallError class Rich_Source(object): def __init__(self, addr, mac, ipset, invert=False): self.addr = addr if self.addr == "": self.addr = None self.mac = mac if self.mac == "" or self.mac is None: self.mac = None elif self.mac is not None: self.mac = self.mac.upper() self.ipset = ipset if self.ipset == "": self.ipset = None self.invert = invert if self.addr is None and self.mac is None and self.ipset is None: raise FirewallError(errors.INVALID_RULE, "no address, mac and ipset") def __str__(self): ret = 'source%s ' % (" NOT" if self.invert else "") if self.addr is not None: return ret + 'address="%s"' % self.addr elif self.mac is not None: return ret + 'mac="%s"' % self.mac elif self.ipset is not None: return ret + 'ipset="%s"' % self.ipset else: raise FirewallError(errors.INVALID_RULE, "no address, mac and ipset") class Rich_Destination(object): def __init__(self, addr, ipset, invert=False): self.addr = addr if self.addr == "": self.addr = None self.ipset = ipset if self.ipset == "": self.ipset = None self.invert = invert if self.addr is None and self.ipset is None: raise FirewallError(errors.INVALID_RULE, "no address and ipset") def __str__(self): ret = 'destination%s ' % (" NOT" if self.invert else "") if self.addr is not None: return ret + 'address="%s"' % self.addr elif self.ipset is not None: return ret + 'ipset="%s"' % self.ipset else: raise FirewallError(errors.INVALID_RULE, "no address and ipset") class Rich_Service(object): def __init__(self, name): self.name = name def __str__(self): return 'service name="%s"' % (self.name) class Rich_Port(object): def __init__(self, port, protocol): self.port = port self.protocol = protocol def __str__(self): return 'port port="%s" protocol="%s"' % (self.port, self.protocol) class Rich_SourcePort(Rich_Port): def __str__(self): return 'source-port port="%s" protocol="%s"' % (self.port, self.protocol) class Rich_Protocol(object): def __init__(self, value): self.value = value def __str__(self): return 'protocol value="%s"' % (self.value) class Rich_Masquerade(object): def __init__(self): pass def __str__(self): return 'masquerade' class Rich_IcmpBlock(object): def __init__(self, name): self.name = name def __str__(self): return 'icmp-block name="%s"' % (self.name) class Rich_IcmpType(object): def __init__(self, name): self.name = name def __str__(self): return 'icmp-type name="%s"' % (self.name) class Rich_ForwardPort(object): def __init__(self, port, protocol, to_port, to_address): self.port = port self.protocol = protocol self.to_port = to_port self.to_address = to_address # replace None with "" in to_port and/or to_address if self.to_port is None: self.to_port = "" if self.to_address is None: self.to_address = "" def __str__(self): return 'forward-port port="%s" protocol="%s"%s%s' % \ (self.port, self.protocol, ' to-port="%s"' % self.to_port if self.to_port != "" else '', ' to-addr="%s"' % self.to_address if self.to_address != "" else '') class Rich_Log(object): def __init__(self, prefix=None, level=None, limit=None): #TODO check default level in iptables self.prefix = prefix self.level = level self.limit = limit def __str__(self): return 'log%s%s%s' % \ (' prefix="%s"' % (self.prefix) if self.prefix else "", ' level="%s"' % (self.level) if self.level else "", " %s" % self.limit if self.limit else "") class Rich_Audit(object): def __init__(self, limit=None): #TODO check default level in iptables self.limit = limit def __str__(self): return 'audit%s' % (" %s" % self.limit if self.limit else "") class Rich_Accept(object): def __init__(self, limit=None): self.limit = limit def __str__(self): return "accept%s" % (" %s" % self.limit if self.limit else "") class Rich_Reject(object): def __init__(self, _type=None, limit=None): self.type = _type self.limit = limit def __str__(self): return "reject%s%s" % (' type="%s"' % self.type if self.type else "", " %s" % self.limit if self.limit else "") def check(self, family): if self.type: if not family: raise FirewallError(errors.INVALID_RULE, "When using reject type you must specify also rule family.") if family in ['ipv4', 'ipv6'] and \ self.type not in REJECT_TYPES[family]: valid_types = ", ".join(REJECT_TYPES[family]) raise FirewallError(errors.INVALID_RULE, "Wrong reject type %s.\nUse one of: %s." % (self.type, valid_types)) class Rich_Drop(Rich_Accept): def __str__(self): return "drop%s" % (" %s" % self.limit if self.limit else "") class Rich_Mark(object): def __init__(self, _set, limit=None): self.set = _set self.limit = limit def __str__(self): return "mark set=%s%s" % (self.set, " %s" % self.limit if self.limit else "") def check(self): if self.set is not None: x = self.set else: raise FirewallError(errors.INVALID_MARK, "no value set") if "/" in x: splits = x.split("/") if len(splits) != 2: raise FirewallError(errors.INVALID_MARK, x) if not functions.checkUINT32(splits[0]) or \ not functions.checkUINT32(splits[1]): # value and mask are uint32 raise FirewallError(errors.INVALID_MARK, x) else: if not functions.checkUINT32(x): # value is uint32 raise FirewallError(errors.INVALID_MARK, x) DURATION_TO_MULT = { "s": 1, "m": 60, "h": 60 * 60, "d": 24 * 60 * 60, } class Rich_Limit(object): def __init__(self, value, burst=None): self.value = value self.burst = burst def check(self): self.value_parse() self.burst_parse() @property def value(self): return self._value @value.setter def value(self, value): if value is None: self._value = None return try: rate, duration = self._value_parse(value) except FirewallError: # The value is invalid. We cannot normalize it. v = value else: v = f"{rate}/{duration}" if getattr(self, "_value", None) != v: self._value = v @property def burst(self): return self._burst @burst.setter def burst(self, burst): if burst is None: self._burst = None return try: b = self._burst_parse(burst) except FirewallError: b = burst else: b = str(burst) if getattr(self, "_burst", None) != b: self._burst = b @staticmethod def _value_parse(value): splits = None if "/" in value: splits = value.split("/") if not splits or len(splits) != 2: raise FirewallError(errors.INVALID_LIMIT, value) (rate, duration) = splits try: rate = int(rate) except: raise FirewallError(errors.INVALID_LIMIT, value) if duration in ["second", "minute", "hour", "day"]: duration = duration[:1] if rate < 1 or duration not in ["s", "m", "h", "d"]: raise FirewallError(errors.INVALID_LIMIT, value) if 10000 * DURATION_TO_MULT[duration] // rate == 0: raise FirewallError(errors.INVALID_LIMIT, "%s too fast" % (value,)) if rate == 1 and duration == "d": # iptables (v1.4.21) doesn't accept 1/d raise FirewallError(errors.INVALID_LIMIT, "%s too slow" % (value,)) return rate, duration def value_parse(self): return self._value_parse(self._value) @staticmethod def _burst_parse(burst): if burst is None: return None try: b = int(burst) except: raise FirewallError(errors.INVALID_LIMIT, burst) if b < 1 or b > 10_000_000: raise FirewallError(errors.INVALID_LIMIT, burst) return b def burst_parse(self): return self._burst_parse(self._burst) def __str__(self): s = f'limit value="{self._value}"' if self._burst is not None: s += f" burst={self._burst}" return s class Rich_Rule(object): priority_min = -32768 priority_max = 32767 def __init__(self, family=None, rule_str=None, priority=0): if family is not None: self.family = str(family) else: self.family = None self.priority = priority self.source = None self.destination = None self.element = None self.log = None self.audit = None self.action = None if rule_str: self._import_from_string(rule_str) def _lexer(self, rule_str): """ Lexical analysis """ tokens = [] for r in functions.splitArgs(rule_str): if "=" in r: attr = r.split('=') if len(attr) != 2 or not attr[0] or not attr[1]: raise FirewallError(errors.INVALID_RULE, 'internal error in _lexer(): %s' % r) tokens.append({'attr_name':attr[0], 'attr_value':attr[1]}) else: tokens.append({'element':r}) tokens.append({'element':'EOL'}) return tokens def _import_from_string(self, rule_str): if not rule_str: raise FirewallError(errors.INVALID_RULE, 'empty rule') rule_str = functions.stripNonPrintableCharacters(rule_str) self.priority = 0 self.family = None self.source = None self.destination = None self.element = None self.log = None self.audit = None self.action = None tokens = self._lexer(rule_str) if tokens and tokens[0].get('element') == 'EOL': raise FirewallError(errors.INVALID_RULE, 'empty rule') attrs = {} # attributes of elements in_elements = [] # stack with elements we are in index = 0 # index into tokens while not (tokens[index].get('element') == 'EOL' and in_elements == ['rule']): element = tokens[index].get('element') attr_name = tokens[index].get('attr_name') attr_value = tokens[index].get('attr_value') #print ("in_elements: ", in_elements) #print ("index: %s, element: %s, attribute: %s=%s" % (index, element, attr_name, attr_value)) if attr_name: # attribute if attr_name not in ['priority', 'family', 'address', 'mac', 'ipset', 'invert', 'value', 'port', 'protocol', 'to-port', 'to-addr', 'name', 'prefix', 'level', 'type', 'set', 'burst']: raise FirewallError(errors.INVALID_RULE, "bad attribute '%s'" % attr_name) else: # element if element in ['rule', 'source', 'destination', 'protocol', 'service', 'port', 'icmp-block', 'icmp-type', 'masquerade', 'forward-port', 'source-port', 'log', 'audit', 'accept', 'drop', 'reject', 'mark', 'limit', 'not', 'NOT', 'EOL']: if element == 'source' and self.source: raise FirewallError(errors.INVALID_RULE, "more than one 'source' element") elif element == 'destination' and self.destination: raise FirewallError(errors.INVALID_RULE, "more than one 'destination' element") elif element in ['protocol', 'service', 'port', 'icmp-block', 'icmp-type', 'masquerade', 'forward-port', 'source-port'] and self.element: raise FirewallError(errors.INVALID_RULE, "more than one element. There cannot be both '%s' and '%s' in one rule." % (element, self.element)) elif element == 'log' and self.log: raise FirewallError(errors.INVALID_RULE, "more than one 'log' element") elif element == 'audit' and self.audit: raise FirewallError(errors.INVALID_RULE, "more than one 'audit' element") elif element in ['accept', 'drop', 'reject', 'mark'] and self.action: raise FirewallError(errors.INVALID_RULE, "more than one 'action' element. There cannot be both '%s' and '%s' in one rule." % (element, self.action)) else: raise FirewallError(errors.INVALID_RULE, "unknown element %s" % element) in_element = in_elements[len(in_elements)-1] if len(in_elements) > 0 else '' if in_element == '': if not element and attr_name: if attr_name == 'family': raise FirewallError(errors.INVALID_RULE, "'family' outside of rule. Use 'rule family=...'.") elif attr_name == 'priority': raise FirewallError(errors.INVALID_RULE, "'priority' outside of rule. Use 'rule priority=...'.") else: raise FirewallError(errors.INVALID_RULE, "'%s' outside of any element. Use 'rule %s= ...'." % (attr_name, attr_name)) elif 'rule' not in element: raise FirewallError(errors.INVALID_RULE, "'%s' outside of rule. Use 'rule ... %s ...'." % (element, element)) else: in_elements.append('rule') # push into stack elif in_element == 'rule': if attr_name == 'family': if attr_value not in ['ipv4', 'ipv6']: raise FirewallError(errors.INVALID_RULE, "'family' attribute cannot have '%s' value. Use 'ipv4' or 'ipv6' instead." % attr_value) self.family = attr_value elif attr_name == 'priority': try: self.priority = int(attr_value) except ValueError: raise FirewallError(errors.INVALID_PRIORITY, "invalid 'priority' attribute value '%s'." % attr_value) elif attr_name: if attr_name == 'protocol': err_msg = "wrong 'protocol' usage. Use either 'rule protocol value=...' or 'rule [forward-]port protocol=...'." else: err_msg = "attribute '%s' outside of any element. Use 'rule %s= ...'." % (attr_name, attr_name) raise FirewallError(errors.INVALID_RULE, err_msg) else: in_elements.append(element) # push into stack elif in_element == 'source': if attr_name in ['address', 'mac', 'ipset', 'invert']: attrs[attr_name] = attr_value elif element in ['not', 'NOT']: attrs['invert'] = True else: self.source = Rich_Source(attrs.get('address'), attrs.get('mac'), attrs.get('ipset'), attrs.get('invert', False)) in_elements.pop() # source attrs.clear() index = index -1 # return token to input elif in_element == 'destination': if attr_name in ['address', 'ipset', 'invert']: attrs[attr_name] = attr_value elif element in ['not', 'NOT']: attrs['invert'] = True else: self.destination = Rich_Destination(attrs.get('address'), attrs.get('ipset'), attrs.get('invert', False)) in_elements.pop() # destination attrs.clear() index = index -1 # return token to input elif in_element == 'protocol': if attr_name == 'value': self.element = Rich_Protocol(attr_value) in_elements.pop() # protocol else: raise FirewallError(errors.INVALID_RULE, "invalid 'protocol' element") elif in_element == 'service': if attr_name == 'name': self.element = Rich_Service(attr_value) in_elements.pop() # service else: raise FirewallError(errors.INVALID_RULE, "invalid 'service' element") elif in_element == 'port': if attr_name in ['port', 'protocol']: attrs[attr_name] = attr_value else: self.element = Rich_Port(attrs.get('port'), attrs.get('protocol')) in_elements.pop() # port attrs.clear() index = index -1 # return token to input elif in_element == 'icmp-block': if attr_name == 'name': self.element = Rich_IcmpBlock(attr_value) in_elements.pop() # icmp-block else: raise FirewallError(errors.INVALID_RULE, "invalid 'icmp-block' element") elif in_element == 'icmp-type': if attr_name == 'name': self.element = Rich_IcmpType(attr_value) in_elements.pop() # icmp-type else: raise FirewallError(errors.INVALID_RULE, "invalid 'icmp-type' element") elif in_element == 'masquerade': self.element = Rich_Masquerade() in_elements.pop() attrs.clear() index = index -1 # return token to input elif in_element == 'forward-port': if attr_name in ['port', 'protocol', 'to-port', 'to-addr']: attrs[attr_name] = attr_value else: self.element = Rich_ForwardPort(attrs.get('port'), attrs.get('protocol'), attrs.get('to-port'), attrs.get('to-addr')) in_elements.pop() # forward-port attrs.clear() index = index -1 # return token to input elif in_element == 'source-port': if attr_name in ['port', 'protocol']: attrs[attr_name] = attr_value else: self.element = Rich_SourcePort(attrs.get('port'), attrs.get('protocol')) in_elements.pop() # source-port attrs.clear() index = index -1 # return token to input elif in_element == 'log': if attr_name in ['prefix', 'level']: attrs[attr_name] = attr_value elif element == 'limit': in_elements.append('limit') else: self.log = Rich_Log(attrs.get('prefix'), attrs.get('level'), attrs.get('limit')) in_elements.pop() # log attrs.clear() index = index -1 # return token to input elif in_element == 'audit': if element == 'limit': in_elements.append('limit') else: self.audit = Rich_Audit(attrs.get('limit')) in_elements.pop() # audit attrs.clear() index = index -1 # return token to input elif in_element == 'accept': if element == 'limit': in_elements.append('limit') else: self.action = Rich_Accept(attrs.get('limit')) in_elements.pop() # accept attrs.clear() index = index -1 # return token to input elif in_element == 'drop': if element == 'limit': in_elements.append('limit') else: self.action = Rich_Drop(attrs.get('limit')) in_elements.pop() # drop attrs.clear() index = index -1 # return token to input elif in_element == 'reject': if attr_name == 'type': attrs[attr_name] = attr_value elif element == 'limit': in_elements.append('limit') else: self.action = Rich_Reject(attrs.get('type'), attrs.get('limit')) in_elements.pop() # accept attrs.clear() index = index -1 # return token to input elif in_element == 'mark': if attr_name == 'set': attrs[attr_name] = attr_value elif element == 'limit': in_elements.append('limit') else: self.action = Rich_Mark(attrs.get('set'), attrs.get('limit')) in_elements.pop() # accept attrs.clear() index = index -1 # return token to input elif in_element == 'limit': if attr_name in ["value", "burst"]: attrs[f"limit.{attr_name}"] = attr_value else: if "limit.value" not in attrs: raise FirewallError( errors.INVALID_RULE, "invalid 'limit' element" ) attrs["limit"] = Rich_Limit( attrs["limit.value"], attrs.get("limit.burst") ) attrs.pop("limit.value", None) attrs.pop("limit.burst", None) in_elements.pop() # limit index = index - 1 # return token to input index = index + 1 self.check() def check(self): if self.family is not None and self.family not in [ "ipv4", "ipv6" ]: raise FirewallError(errors.INVALID_FAMILY, self.family) if self.family is None: if (self.source is not None and self.source.addr is not None) or \ self.destination is not None: raise FirewallError(errors.MISSING_FAMILY) if type(self.element) == Rich_ForwardPort: raise FirewallError(errors.MISSING_FAMILY) if self.priority < self.priority_min or self.priority > self.priority_max: raise FirewallError(errors.INVALID_PRIORITY, "'priority' attribute must be between %d and %d." \ % (self.priority_min, self.priority_max)) if self.element is None and \ (self.log is None or (self.log is not None and self.priority == 0)): if self.action is None: raise FirewallError(errors.INVALID_RULE, "no element, no action") if self.source is None and self.destination is None and self.priority == 0: raise FirewallError(errors.INVALID_RULE, "no element, no source, no destination") if type(self.element) not in [ Rich_IcmpBlock, Rich_ForwardPort, Rich_Masquerade ]: if self.log is None and self.audit is None and \ self.action is None: raise FirewallError(errors.INVALID_RULE, "no action, no log, no audit") # source if self.source is not None: if self.source.addr is not None: if self.family is None: raise FirewallError(errors.INVALID_FAMILY) if self.source.mac is not None: raise FirewallError(errors.INVALID_RULE, "address and mac") if self.source.ipset is not None: raise FirewallError(errors.INVALID_RULE, "address and ipset") if not functions.check_address(self.family, self.source.addr): raise FirewallError(errors.INVALID_ADDR, str(self.source.addr)) elif self.source.mac is not None: if self.source.ipset is not None: raise FirewallError(errors.INVALID_RULE, "mac and ipset") if not functions.check_mac(self.source.mac): raise FirewallError(errors.INVALID_MAC, str(self.source.mac)) elif self.source.ipset is not None: if not check_ipset_name(self.source.ipset): raise FirewallError(errors.INVALID_IPSET, str(self.source.ipset)) else: raise FirewallError(errors.INVALID_RULE, "invalid source") # destination if self.destination is not None: if self.destination.addr is not None: if self.family is None: raise FirewallError(errors.INVALID_FAMILY) if self.destination.ipset is not None: raise FirewallError(errors.INVALID_DESTINATION, "address and ipset") if not functions.check_address(self.family, self.destination.addr): raise FirewallError(errors.INVALID_ADDR, str(self.destination.addr)) elif self.destination.ipset is not None: if not check_ipset_name(self.destination.ipset): raise FirewallError(errors.INVALID_IPSET, str(self.destination.ipset)) else: raise FirewallError(errors.INVALID_RULE, "invalid destination") # service if type(self.element) == Rich_Service: # service availability needs to be checked in Firewall, here is no # knowledge about this, therefore only simple check if self.element.name is None or len(self.element.name) < 1: raise FirewallError(errors.INVALID_SERVICE, str(self.element.name)) # port elif type(self.element) == Rich_Port: if not functions.check_port(self.element.port): raise FirewallError(errors.INVALID_PORT, self.element.port) if self.element.protocol not in [ "tcp", "udp", "sctp", "dccp" ]: raise FirewallError(errors.INVALID_PROTOCOL, self.element.protocol) # protocol elif type(self.element) == Rich_Protocol: if not functions.checkProtocol(self.element.value): raise FirewallError(errors.INVALID_PROTOCOL, self.element.value) # masquerade elif type(self.element) == Rich_Masquerade: if self.action is not None: raise FirewallError(errors.INVALID_RULE, "masquerade and action") if self.source is not None and self.source.mac is not None: raise FirewallError(errors.INVALID_RULE, "masquerade and mac source") # icmp-block elif type(self.element) == Rich_IcmpBlock: # icmp type availability needs to be checked in Firewall, here is no # knowledge about this, therefore only simple check if self.element.name is None or len(self.element.name) < 1: raise FirewallError(errors.INVALID_ICMPTYPE, str(self.element.name)) if self.action: raise FirewallError(errors.INVALID_RULE, "icmp-block and action") # icmp-type elif type(self.element) == Rich_IcmpType: # icmp type availability needs to be checked in Firewall, here is no # knowledge about this, therefore only simple check if self.element.name is None or len(self.element.name) < 1: raise FirewallError(errors.INVALID_ICMPTYPE, str(self.element.name)) # forward-port elif type(self.element) == Rich_ForwardPort: if not functions.check_port(self.element.port): raise FirewallError(errors.INVALID_PORT, self.element.port) if self.element.protocol not in [ "tcp", "udp", "sctp", "dccp" ]: raise FirewallError(errors.INVALID_PROTOCOL, self.element.protocol) if self.element.to_port == "" and self.element.to_address == "": raise FirewallError(errors.INVALID_PORT, self.element.to_port) if self.element.to_port != "" and \ not functions.check_port(self.element.to_port): raise FirewallError(errors.INVALID_PORT, self.element.to_port) if self.element.to_address != "" and \ not functions.check_single_address(self.family, self.element.to_address): raise FirewallError(errors.INVALID_ADDR, self.element.to_address) if self.family is None: raise FirewallError(errors.INVALID_FAMILY) if self.action is not None: raise FirewallError(errors.INVALID_RULE, "forward-port and action") # source-port elif type(self.element) == Rich_SourcePort: if not functions.check_port(self.element.port): raise FirewallError(errors.INVALID_PORT, self.element.port) if self.element.protocol not in [ "tcp", "udp", "sctp", "dccp" ]: raise FirewallError(errors.INVALID_PROTOCOL, self.element.protocol) # other element and not empty? elif self.element is not None: raise FirewallError(errors.INVALID_RULE, "Unknown element %s" % type(self.element)) # log if self.log is not None: if self.log.level and \ self.log.level not in [ "emerg", "alert", "crit", "error", "warning", "notice", "info", "debug" ]: raise FirewallError(errors.INVALID_LOG_LEVEL, self.log.level) if self.log.limit is not None: self.log.limit.check() # audit if self.audit is not None: if type(self.action) not in [ Rich_Accept, Rich_Reject, Rich_Drop ]: raise FirewallError(errors.INVALID_AUDIT_TYPE, type(self.action)) if self.audit.limit is not None: self.audit.limit.check() # action if self.action is not None: if type(self.action) == Rich_Reject: self.action.check(self.family) elif type(self.action) == Rich_Mark: self.action.check() if self.action.limit is not None: self.action.limit.check() def __str__(self): ret = 'rule' if self.priority: ret += ' priority="%d"' % self.priority if self.family: ret += ' family="%s"' % self.family if self.source: ret += " %s" % self.source if self.destination: ret += " %s" % self.destination if self.element: ret += " %s" % self.element if self.log: ret += " %s" % self.log if self.audit: ret += " %s" % self.audit if self.action: ret += " %s" % self.action return (functions.u2b(ret)) if functions.PY2 else ret #class Rich_RawRule(object): #class Rich_RuleSet(object): #class Rich_AddressList(object): PK! fw_config.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "FirewallConfig" ] import copy import os import os.path import shutil from firewall import config from firewall.core.logger import log from firewall.core.io.icmptype import IcmpType, icmptype_reader, icmptype_writer from firewall.core.io.service import Service, service_reader, service_writer from firewall.core.io.zone import Zone, zone_reader, zone_writer from firewall.core.io.ipset import IPSet, ipset_reader, ipset_writer from firewall.core.io.helper import Helper, helper_reader, helper_writer from firewall.core.io.policy import Policy, policy_reader, policy_writer from firewall import errors from firewall.errors import FirewallError class FirewallConfig(object): def __init__(self, fw): self._fw = fw self.__init_vars() def __repr__(self): return '%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)' % \ (self.__class__, self._ipsets, self._icmptypes, self._services, self._zones, self._helpers, self.policy_objects, self._builtin_ipsets, self._builtin_icmptypes, self._builtin_services, self._builtin_zones, self._builtin_helpers, self._builtin_policy_objects, self._firewalld_conf, self._policies, self._direct) def __init_vars(self): self._ipsets = { } self._icmptypes = { } self._services = { } self._zones = { } self._helpers = { } self._policy_objects = { } self._builtin_ipsets = { } self._builtin_icmptypes = { } self._builtin_services = { } self._builtin_zones = { } self._builtin_helpers = { } self._builtin_policy_objects = { } self._firewalld_conf = None self._policies = None self._direct = None def cleanup(self): for x in list(self._builtin_ipsets.keys()): self._builtin_ipsets[x].cleanup() del self._builtin_ipsets[x] for x in list(self._ipsets.keys()): self._ipsets[x].cleanup() del self._ipsets[x] for x in list(self._builtin_icmptypes.keys()): self._builtin_icmptypes[x].cleanup() del self._builtin_icmptypes[x] for x in list(self._icmptypes.keys()): self._icmptypes[x].cleanup() del self._icmptypes[x] for x in list(self._builtin_services.keys()): self._builtin_services[x].cleanup() del self._builtin_services[x] for x in list(self._services.keys()): self._services[x].cleanup() del self._services[x] for x in list(self._builtin_zones.keys()): self._builtin_zones[x].cleanup() del self._builtin_zones[x] for x in list(self._zones.keys()): self._zones[x].cleanup() del self._zones[x] for x in list(self._builtin_helpers.keys()): self._builtin_helpers[x].cleanup() del self._builtin_helpers[x] for x in list(self._helpers.keys()): self._helpers[x].cleanup() del self._helpers[x] if self._firewalld_conf: self._firewalld_conf.cleanup() del self._firewalld_conf self._firewalld_conf = None if self._policies: self._policies.cleanup() del self._policies self._policies = None if self._direct: self._direct.cleanup() del self._direct self._direct = None self.__init_vars() # access check def lockdown_enabled(self): return self._fw.policies.query_lockdown() def access_check(self, key, value): return self._fw.policies.access_check(key, value) # firewalld_conf def set_firewalld_conf(self, conf): self._firewalld_conf = conf def get_firewalld_conf(self): return self._firewalld_conf def update_firewalld_conf(self): if not os.path.exists(config.FIREWALLD_CONF): self._firewalld_conf.clear() else: self._firewalld_conf.read() # policies def set_policies(self, policies): self._policies = policies def get_policies(self): return self._policies def update_lockdown_whitelist(self): if not os.path.exists(config.LOCKDOWN_WHITELIST): self._policies.lockdown_whitelist.cleanup() else: self._policies.lockdown_whitelist.read() # direct def set_direct(self, direct): self._direct = direct def get_direct(self): return self._direct def update_direct(self): if not os.path.exists(config.FIREWALLD_DIRECT): self._direct.cleanup() else: self._direct.read() # ipset def get_ipsets(self): return sorted(set(list(self._ipsets.keys()) + \ list(self._builtin_ipsets.keys()))) def add_ipset(self, obj): if obj.builtin: self._builtin_ipsets[obj.name] = obj else: self._ipsets[obj.name] = obj def get_ipset(self, name): if name in self._ipsets: return self._ipsets[name] elif name in self._builtin_ipsets: return self._builtin_ipsets[name] raise FirewallError(errors.INVALID_IPSET, name) def load_ipset_defaults(self, obj): if obj.name not in self._ipsets: raise FirewallError(errors.NO_DEFAULTS, obj.name) elif self._ipsets[obj.name] != obj: raise FirewallError(errors.NO_DEFAULTS, "self._ipsets[%s] != obj" % obj.name) elif obj.name not in self._builtin_ipsets: raise FirewallError(errors.NO_DEFAULTS, "'%s' not a built-in ipset" % obj.name) self._remove_ipset(obj) return self._builtin_ipsets[obj.name] def get_ipset_config(self, obj): return obj.export_config() def set_ipset_config(self, obj, conf): if obj.builtin: x = copy.copy(obj) x.import_config(conf) x.path = config.ETC_FIREWALLD_IPSETS x.builtin = False if obj.path != x.path: x.default = False self.add_ipset(x) ipset_writer(x) return x else: obj.import_config(conf) ipset_writer(obj) return obj def new_ipset(self, name, conf): if name in self._ipsets or name in self._builtin_ipsets: raise FirewallError(errors.NAME_CONFLICT, "new_ipset(): '%s'" % name) x = IPSet() x.check_name(name) x.import_config(conf) x.name = name x.filename = "%s.xml" % name x.path = config.ETC_FIREWALLD_IPSETS # It is not possible to add a new one with a name of a buitin x.builtin = False x.default = True ipset_writer(x) self.add_ipset(x) return x def update_ipset_from_path(self, name): filename = os.path.basename(name) path = os.path.dirname(name) if not os.path.exists(name): # removed file if path == config.ETC_FIREWALLD_IPSETS: # removed custom ipset for x in self._ipsets.keys(): obj = self._ipsets[x] if obj.filename == filename: del self._ipsets[x] if obj.name in self._builtin_ipsets: return ("update", self._builtin_ipsets[obj.name]) return ("remove", obj) else: # removed builtin ipset for x in self._builtin_ipsets.keys(): obj = self._builtin_ipsets[x] if obj.filename == filename: del self._builtin_ipsets[x] if obj.name not in self._ipsets: # update dbus ipset return ("remove", obj) else: # builtin hidden, no update needed return (None, None) # ipset not known to firewalld, yet (timeout, ..) return (None, None) # new or updated file log.debug1("Loading ipset file '%s'", name) try: obj = ipset_reader(filename, path) except Exception as msg: log.error("Failed to load ipset file '%s': %s", filename, msg) return (None, None) # new ipset if obj.name not in self._builtin_ipsets and obj.name not in self._ipsets: self.add_ipset(obj) return ("new", obj) # updated ipset if path == config.ETC_FIREWALLD_IPSETS: # custom ipset update if obj.name in self._ipsets: obj.default = self._ipsets[obj.name].default self._ipsets[obj.name] = obj return ("update", obj) else: if obj.name in self._builtin_ipsets: # builtin ipset update del self._builtin_ipsets[obj.name] self._builtin_ipsets[obj.name] = obj if obj.name not in self._ipsets: # update dbus ipset return ("update", obj) else: # builtin hidden, no update needed return (None, None) # ipset not known to firewalld, yet (timeout, ..) return (None, None) def _remove_ipset(self, obj): if obj.name not in self._ipsets: raise FirewallError(errors.INVALID_IPSET, obj.name) if obj.path != config.ETC_FIREWALLD_IPSETS: raise FirewallError(errors.INVALID_DIRECTORY, "'%s' != '%s'" % (obj.path, config.ETC_FIREWALLD_IPSETS)) name = "%s/%s.xml" % (obj.path, obj.name) try: shutil.move(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) os.remove(name) del self._ipsets[obj.name] def check_builtin_ipset(self, obj): if obj.builtin or not obj.default: raise FirewallError(errors.BUILTIN_IPSET, "'%s' is built-in ipset" % obj.name) def remove_ipset(self, obj): self.check_builtin_ipset(obj) self._remove_ipset(obj) def rename_ipset(self, obj, name): self.check_builtin_ipset(obj) new_ipset = self._copy_ipset(obj, name) self._remove_ipset(obj) return new_ipset def _copy_ipset(self, obj, name): return self.new_ipset(name, obj.export_config()) # icmptypes def get_icmptypes(self): return sorted(set(list(self._icmptypes.keys()) + \ list(self._builtin_icmptypes.keys()))) def add_icmptype(self, obj): if obj.builtin: self._builtin_icmptypes[obj.name] = obj else: self._icmptypes[obj.name] = obj def get_icmptype(self, name): if name in self._icmptypes: return self._icmptypes[name] elif name in self._builtin_icmptypes: return self._builtin_icmptypes[name] raise FirewallError(errors.INVALID_ICMPTYPE, name) def load_icmptype_defaults(self, obj): if obj.name not in self._icmptypes: raise FirewallError(errors.NO_DEFAULTS, obj.name) elif self._icmptypes[obj.name] != obj: raise FirewallError(errors.NO_DEFAULTS, "self._icmptypes[%s] != obj" % obj.name) elif obj.name not in self._builtin_icmptypes: raise FirewallError(errors.NO_DEFAULTS, "'%s' not a built-in icmptype" % obj.name) self._remove_icmptype(obj) return self._builtin_icmptypes[obj.name] def get_icmptype_config(self, obj): return obj.export_config() def set_icmptype_config(self, obj, conf): if obj.builtin: x = copy.copy(obj) x.import_config(conf) x.path = config.ETC_FIREWALLD_ICMPTYPES x.builtin = False if obj.path != x.path: x.default = False self.add_icmptype(x) icmptype_writer(x) return x else: obj.import_config(conf) icmptype_writer(obj) return obj def new_icmptype(self, name, conf): if name in self._icmptypes or name in self._builtin_icmptypes: raise FirewallError(errors.NAME_CONFLICT, "new_icmptype(): '%s'" % name) x = IcmpType() x.check_name(name) x.import_config(conf) x.name = name x.filename = "%s.xml" % name x.path = config.ETC_FIREWALLD_ICMPTYPES # It is not possible to add a new one with a name of a buitin x.builtin = False x.default = True icmptype_writer(x) self.add_icmptype(x) return x def update_icmptype_from_path(self, name): filename = os.path.basename(name) path = os.path.dirname(name) if not os.path.exists(name): # removed file if path == config.ETC_FIREWALLD_ICMPTYPES: # removed custom icmptype for x in self._icmptypes.keys(): obj = self._icmptypes[x] if obj.filename == filename: del self._icmptypes[x] if obj.name in self._builtin_icmptypes: return ("update", self._builtin_icmptypes[obj.name]) return ("remove", obj) else: # removed builtin icmptype for x in self._builtin_icmptypes.keys(): obj = self._builtin_icmptypes[x] if obj.filename == filename: del self._builtin_icmptypes[x] if obj.name not in self._icmptypes: # update dbus icmptype return ("remove", obj) else: # builtin hidden, no update needed return (None, None) # icmptype not known to firewalld, yet (timeout, ..) return (None, None) # new or updated file log.debug1("Loading icmptype file '%s'", name) try: obj = icmptype_reader(filename, path) except Exception as msg: log.error("Failed to load icmptype file '%s': %s", filename, msg) return (None, None) # new icmptype if obj.name not in self._builtin_icmptypes and obj.name not in self._icmptypes: self.add_icmptype(obj) return ("new", obj) # updated icmptype if path == config.ETC_FIREWALLD_ICMPTYPES: # custom icmptype update if obj.name in self._icmptypes: obj.default = self._icmptypes[obj.name].default self._icmptypes[obj.name] = obj return ("update", obj) else: if obj.name in self._builtin_icmptypes: # builtin icmptype update del self._builtin_icmptypes[obj.name] self._builtin_icmptypes[obj.name] = obj if obj.name not in self._icmptypes: # update dbus icmptype return ("update", obj) else: # builtin hidden, no update needed return (None, None) # icmptype not known to firewalld, yet (timeout, ..) return (None, None) def _remove_icmptype(self, obj): if obj.name not in self._icmptypes: raise FirewallError(errors.INVALID_ICMPTYPE, obj.name) if obj.path != config.ETC_FIREWALLD_ICMPTYPES: raise FirewallError(errors.INVALID_DIRECTORY, "'%s' != '%s'" % \ (obj.path, config.ETC_FIREWALLD_ICMPTYPES)) name = "%s/%s.xml" % (obj.path, obj.name) try: shutil.move(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) os.remove(name) del self._icmptypes[obj.name] def check_builtin_icmptype(self, obj): if obj.builtin or not obj.default: raise FirewallError(errors.BUILTIN_ICMPTYPE, "'%s' is built-in icmp type" % obj.name) def remove_icmptype(self, obj): self.check_builtin_icmptype(obj) self._remove_icmptype(obj) def rename_icmptype(self, obj, name): self.check_builtin_icmptype(obj) new_icmptype = self._copy_icmptype(obj, name) self._remove_icmptype(obj) return new_icmptype def _copy_icmptype(self, obj, name): return self.new_icmptype(name, obj.export_config()) # services def get_services(self): return sorted(set(list(self._services.keys()) + \ list(self._builtin_services.keys()))) def add_service(self, obj): if obj.builtin: self._builtin_services[obj.name] = obj else: self._services[obj.name] = obj def get_service(self, name): if name in self._services: return self._services[name] elif name in self._builtin_services: return self._builtin_services[name] raise FirewallError(errors.INVALID_SERVICE, "get_service(): '%s'" % name) def load_service_defaults(self, obj): if obj.name not in self._services: raise FirewallError(errors.NO_DEFAULTS, obj.name) elif self._services[obj.name] != obj: raise FirewallError(errors.NO_DEFAULTS, "self._services[%s] != obj" % obj.name) elif obj.name not in self._builtin_services: raise FirewallError(errors.NO_DEFAULTS, "'%s' not a built-in service" % obj.name) self._remove_service(obj) return self._builtin_services[obj.name] def get_service_config(self, obj): conf_dict = obj.export_config_dict() conf_list = [] for i in range(8): # tuple based dbus API has 8 elements if obj.IMPORT_EXPORT_STRUCTURE[i][0] not in conf_dict: # old API needs the empty elements as well. Grab it from the # object otherwise we don't know the type. conf_list.append(copy.deepcopy(getattr(obj, obj.IMPORT_EXPORT_STRUCTURE[i][0]))) else: conf_list.append(conf_dict[obj.IMPORT_EXPORT_STRUCTURE[i][0]]) return tuple(conf_list) def get_service_config_dict(self, obj): return obj.export_config_dict() def set_service_config(self, obj, conf): conf_dict = {} for i,value in enumerate(conf): conf_dict[obj.IMPORT_EXPORT_STRUCTURE[i][0]] = value if obj.builtin: x = copy.copy(obj) x.import_config_dict(conf_dict) x.path = config.ETC_FIREWALLD_SERVICES x.builtin = False if obj.path != x.path: x.default = False self.add_service(x) service_writer(x) return x else: obj.import_config_dict(conf_dict) service_writer(obj) return obj def set_service_config_dict(self, obj, conf): if obj.builtin: x = copy.copy(obj) x.import_config_dict(conf) x.path = config.ETC_FIREWALLD_SERVICES x.builtin = False if obj.path != x.path: x.default = False self.add_service(x) service_writer(x) return x else: obj.import_config_dict(conf) service_writer(obj) return obj def new_service(self, name, conf): if name in self._services or name in self._builtin_services: raise FirewallError(errors.NAME_CONFLICT, "new_service(): '%s'" % name) conf_dict = {} for i,value in enumerate(conf): conf_dict[Service.IMPORT_EXPORT_STRUCTURE[i][0]] = value x = Service() x.check_name(name) x.import_config_dict(conf_dict) x.name = name x.filename = "%s.xml" % name x.path = config.ETC_FIREWALLD_SERVICES # It is not possible to add a new one with a name of a buitin x.builtin = False x.default = True service_writer(x) self.add_service(x) return x def new_service_dict(self, name, conf): if name in self._services or name in self._builtin_services: raise FirewallError(errors.NAME_CONFLICT, "new_service(): '%s'" % name) x = Service() x.check_name(name) x.import_config_dict(conf) x.name = name x.filename = "%s.xml" % name x.path = config.ETC_FIREWALLD_SERVICES # It is not possible to add a new one with a name of a buitin x.builtin = False x.default = True service_writer(x) self.add_service(x) return x def update_service_from_path(self, name): filename = os.path.basename(name) path = os.path.dirname(name) if not os.path.exists(name): # removed file if path == config.ETC_FIREWALLD_SERVICES: # removed custom service for x in self._services.keys(): obj = self._services[x] if obj.filename == filename: del self._services[x] if obj.name in self._builtin_services: return ("update", self._builtin_services[obj.name]) return ("remove", obj) else: # removed builtin service for x in self._builtin_services.keys(): obj = self._builtin_services[x] if obj.filename == filename: del self._builtin_services[x] if obj.name not in self._services: # update dbus service return ("remove", obj) else: # builtin hidden, no update needed return (None, None) # service not known to firewalld, yet (timeout, ..) return (None, None) # new or updated file log.debug1("Loading service file '%s'", name) try: obj = service_reader(filename, path) except Exception as msg: log.error("Failed to load service file '%s': %s", filename, msg) return (None, None) # new service if obj.name not in self._builtin_services and obj.name not in self._services: self.add_service(obj) return ("new", obj) # updated service if path == config.ETC_FIREWALLD_SERVICES: # custom service update if obj.name in self._services: obj.default = self._services[obj.name].default self._services[obj.name] = obj return ("update", obj) else: if obj.name in self._builtin_services: # builtin service update del self._builtin_services[obj.name] self._builtin_services[obj.name] = obj if obj.name not in self._services: # update dbus service return ("update", obj) else: # builtin hidden, no update needed return (None, None) # service not known to firewalld, yet (timeout, ..) return (None, None) def _remove_service(self, obj): if obj.name not in self._services: raise FirewallError(errors.INVALID_SERVICE, obj.name) if obj.path != config.ETC_FIREWALLD_SERVICES: raise FirewallError(errors.INVALID_DIRECTORY, "'%s' != '%s'" % \ (obj.path, config.ETC_FIREWALLD_SERVICES)) name = "%s/%s.xml" % (obj.path, obj.name) try: shutil.move(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) os.remove(name) del self._services[obj.name] def check_builtin_service(self, obj): if obj.builtin or not obj.default: raise FirewallError(errors.BUILTIN_SERVICE, "'%s' is built-in service" % obj.name) def remove_service(self, obj): self.check_builtin_service(obj) self._remove_service(obj) def rename_service(self, obj, name): self.check_builtin_service(obj) new_service = self._copy_service(obj, name) self._remove_service(obj) return new_service def _copy_service(self, obj, name): return self.new_service_dict(name, obj.export_config_dict()) # zones def get_zones(self): return sorted(set(list(self._zones.keys()) + \ list(self._builtin_zones.keys()))) def add_zone(self, obj): if obj.builtin: self._builtin_zones[obj.name] = obj else: self._zones[obj.name] = obj def forget_zone(self, name): if name in self._builtin_zones: del self._builtin_zones[name] if name in self._zones: del self._zones[name] def get_zone(self, name): if name in self._zones: return self._zones[name] elif name in self._builtin_zones: return self._builtin_zones[name] raise FirewallError(errors.INVALID_ZONE, "get_zone(): %s" % name) def load_zone_defaults(self, obj): if obj.name not in self._zones: raise FirewallError(errors.NO_DEFAULTS, obj.name) elif self._zones[obj.name] != obj: raise FirewallError(errors.NO_DEFAULTS, "self._zones[%s] != obj" % obj.name) elif obj.name not in self._builtin_zones: raise FirewallError(errors.NO_DEFAULTS, "'%s' not a built-in zone" % obj.name) self._remove_zone(obj) return self._builtin_zones[obj.name] def get_zone_config(self, obj): conf_dict = obj.export_config_dict() conf_list = [] for i in range(16): # tuple based dbus API has 16 elements if obj.IMPORT_EXPORT_STRUCTURE[i][0] not in conf_dict: # old API needs the empty elements as well. Grab it from the # object otherwise we don't know the type. conf_list.append(copy.deepcopy(getattr(obj, obj.IMPORT_EXPORT_STRUCTURE[i][0]))) else: conf_list.append(conf_dict[obj.IMPORT_EXPORT_STRUCTURE[i][0]]) return tuple(conf_list) def get_zone_config_dict(self, obj): return obj.export_config_dict() def set_zone_config(self, obj, conf): conf_dict = {} for i,value in enumerate(conf): conf_dict[obj.IMPORT_EXPORT_STRUCTURE[i][0]] = value if obj.builtin: x = copy.copy(obj) x.fw_config = self x.import_config_dict(conf_dict) x.path = config.ETC_FIREWALLD_ZONES x.builtin = False if obj.path != x.path: x.default = False self.add_zone(x) zone_writer(x) return x else: obj.fw_config = self obj.import_config_dict(conf_dict) zone_writer(obj) return obj def set_zone_config_dict(self, obj, conf): if obj.builtin: x = copy.copy(obj) x.fw_config = self x.import_config_dict(conf) x.path = config.ETC_FIREWALLD_ZONES x.builtin = False if obj.path != x.path: x.default = False self.add_zone(x) zone_writer(x) return x else: obj.fw_config = self obj.import_config_dict(conf) zone_writer(obj) return obj def new_zone(self, name, conf): if name in self._zones or name in self._builtin_zones: raise FirewallError(errors.NAME_CONFLICT, "new_zone(): '%s'" % name) conf_dict = {} for i,value in enumerate(conf): conf_dict[Zone.IMPORT_EXPORT_STRUCTURE[i][0]] = value x = Zone() x.fw_config = self x.check_name(name) x.import_config_dict(conf_dict) x.name = name x.filename = "%s.xml" % name x.path = config.ETC_FIREWALLD_ZONES # It is not possible to add a new one with a name of a buitin x.builtin = False x.default = True zone_writer(x) self.add_zone(x) return x def new_zone_dict(self, name, conf): if name in self._zones or name in self._builtin_zones: raise FirewallError(errors.NAME_CONFLICT, "new_zone(): '%s'" % name) x = Zone() x.fw_config = self x.check_name(name) x.import_config_dict(conf) x.name = name x.filename = "%s.xml" % name x.path = config.ETC_FIREWALLD_ZONES # It is not possible to add a new one with a name of a buitin x.builtin = False x.default = True zone_writer(x) self.add_zone(x) return x def update_zone_from_path(self, name): filename = os.path.basename(name) path = os.path.dirname(name) if not os.path.exists(name): # removed file if path.startswith(config.ETC_FIREWALLD_ZONES): # removed custom zone for x in self._zones.keys(): obj = self._zones[x] if obj.filename == filename: del self._zones[x] if obj.name in self._builtin_zones: return ("update", self._builtin_zones[obj.name]) return ("remove", obj) else: # removed builtin zone for x in self._builtin_zones.keys(): obj = self._builtin_zones[x] if obj.filename == filename: del self._builtin_zones[x] if obj.name not in self._zones: # update dbus zone return ("remove", obj) else: # builtin hidden, no update needed return (None, None) # zone not known to firewalld, yet (timeout, ..) return (None, None) # new or updated file log.debug1("Loading zone file '%s'", name) try: obj = zone_reader(filename, path) except Exception as msg: log.error("Failed to load zone file '%s': %s", filename, msg) return (None, None) obj.fw_config = self if path.startswith(config.ETC_FIREWALLD_ZONES) and \ len(path) > len(config.ETC_FIREWALLD_ZONES): # custom combined zone part obj.name = "%s/%s" % (os.path.basename(path), os.path.basename(filename)[0:-4]) # new zone if obj.name not in self._builtin_zones and obj.name not in self._zones: self.add_zone(obj) return ("new", obj) # updated zone if path.startswith(config.ETC_FIREWALLD_ZONES): # custom zone update if obj.name in self._zones: obj.default = self._zones[obj.name].default self._zones[obj.name] = obj return ("update", obj) else: if obj.name in self._builtin_zones: # builtin zone update del self._builtin_zones[obj.name] self._builtin_zones[obj.name] = obj if obj.name not in self._zones: # update dbus zone return ("update", obj) else: # builtin hidden, no update needed return (None, None) # zone not known to firewalld, yet (timeout, ..) return (None, None) def _remove_zone(self, obj): if obj.name not in self._zones: raise FirewallError(errors.INVALID_ZONE, obj.name) if not obj.path.startswith(config.ETC_FIREWALLD_ZONES): raise FirewallError(errors.INVALID_DIRECTORY, "'%s' doesn't start with '%s'" % \ (obj.path, config.ETC_FIREWALLD_ZONES)) name = "%s/%s.xml" % (obj.path, obj.name) try: shutil.move(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) os.remove(name) del self._zones[obj.name] def check_builtin_zone(self, obj): if obj.builtin or not obj.default: raise FirewallError(errors.BUILTIN_ZONE, "'%s' is built-in zone" % obj.name) def remove_zone(self, obj): self.check_builtin_zone(obj) self._remove_zone(obj) def rename_zone(self, obj, name): self.check_builtin_zone(obj) obj_conf = obj.export_config_dict() self._remove_zone(obj) try: new_zone = self.new_zone_dict(name, obj_conf) except: # re-add original if rename failed self.new_zone_dict(obj.name, obj_conf) raise return new_zone # policy objects def get_policy_objects(self): return sorted(set(list(self._policy_objects.keys()) + \ list(self._builtin_policy_objects.keys()))) def add_policy_object(self, obj): if obj.builtin: self._builtin_policy_objects[obj.name] = obj else: self._policy_objects[obj.name] = obj def get_policy_object(self, name): if name in self._policy_objects: return self._policy_objects[name] elif name in self._builtin_policy_objects: return self._builtin_policy_objects[name] raise FirewallError(errors.INVALID_POLICY, "get_policy_object(): %s" % name) def load_policy_object_defaults(self, obj): if obj.name not in self._policy_objects: raise FirewallError(errors.NO_DEFAULTS, obj.name) elif self._policy_objects[obj.name] != obj: raise FirewallError(errors.NO_DEFAULTS, "self._policy_objects[%s] != obj" % obj.name) elif obj.name not in self._builtin_policy_objects: raise FirewallError(errors.NO_DEFAULTS, "'%s' not a built-in policy" % obj.name) self._remove_policy_object(obj) return self._builtin_policy_objects[obj.name] def get_policy_object_config_dict(self, obj): return obj.export_config_dict() def set_policy_object_config_dict(self, obj, conf): if obj.builtin: x = copy.copy(obj) x.fw_config = self x.import_config_dict(conf) x.path = config.ETC_FIREWALLD_POLICIES x.builtin = False if obj.path != x.path: x.default = False self.add_policy_object(x) policy_writer(x) return x else: obj.fw_config = self obj.import_config_dict(conf) policy_writer(obj) return obj def new_policy_object_dict(self, name, conf): if name in self._policy_objects or name in self._builtin_policy_objects: raise FirewallError(errors.NAME_CONFLICT, "new_policy_object(): '%s'" % name) x = Policy() x.fw_config = self x.check_name(name) x.import_config_dict(conf) x.name = name x.filename = "%s.xml" % name x.path = config.ETC_FIREWALLD_POLICIES # It is not possible to add a new one with a name of a buitin x.builtin = False x.default = True policy_writer(x) self.add_policy_object(x) return x def update_policy_object_from_path(self, name): filename = os.path.basename(name) path = os.path.dirname(name) if not os.path.exists(name): # removed file if path.startswith(config.ETC_FIREWALLD_POLICIES): # removed custom policy_object for x in self._policy_objects.keys(): obj = self._policy_objects[x] if obj.filename == filename: del self._policy_objects[x] if obj.name in self._builtin_policy_objects: return ("update", self._builtin_policy_objects[obj.name]) return ("remove", obj) else: # removed builtin policy_object for x in self._builtin_policy_objects.keys(): obj = self._builtin_policy_objects[x] if obj.filename == filename: del self._builtin_policy_objects[x] if obj.name not in self._policy_objects: # update dbus policy_object return ("remove", obj) else: # builtin hidden, no update needed return (None, None) # policy_object not known to firewalld, yet (timeout, ..) return (None, None) # new or updated file log.debug1("Loading policy file '%s'", name) try: obj = policy_reader(filename, path) except Exception as msg: log.error("Failed to load policy file '%s': %s", filename, msg) return (None, None) obj.fw_config = self if path.startswith(config.ETC_FIREWALLD_POLICIES) and \ len(path) > len(config.ETC_FIREWALLD_POLICIES): # custom combined policy_object part obj.name = "%s/%s" % (os.path.basename(path), os.path.basename(filename)[0:-4]) # new policy_object if obj.name not in self._builtin_policy_objects and obj.name not in self._policy_objects: self.add_policy_object(obj) return ("new", obj) # updated policy_object if path.startswith(config.ETC_FIREWALLD_POLICIES): # custom policy_object update if obj.name in self._policy_objects: obj.default = self._policy_objects[obj.name].default self._policy_objects[obj.name] = obj return ("update", obj) else: if obj.name in self._builtin_policy_objects: # builtin policy_object update del self._builtin_policy_objects[obj.name] self._builtin_policy_objects[obj.name] = obj if obj.name not in self._policy_objects: # update dbus policy_object return ("update", obj) else: # builtin hidden, no update needed return (None, None) # policy_object not known to firewalld, yet (timeout, ..) return (None, None) def _remove_policy_object(self, obj): if obj.name not in self._policy_objects: raise FirewallError(errors.INVALID_POLICY, obj.name) if not obj.path.startswith(config.ETC_FIREWALLD_POLICIES): raise FirewallError(errors.INVALID_DIRECTORY, "'%s' doesn't start with '%s'" % \ (obj.path, config.ETC_FIREWALLD_POLICIES)) name = "%s/%s.xml" % (obj.path, obj.name) try: shutil.move(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) os.remove(name) del self._policy_objects[obj.name] def check_builtin_policy_object(self, obj): if obj.builtin or not obj.default: raise FirewallError(errors.BUILTIN_POLICY, "'%s' is built-in policy" % obj.name) def remove_policy_object(self, obj): self.check_builtin_policy_object(obj) self._remove_policy_object(obj) def rename_policy_object(self, obj, name): self.check_builtin_policy_object(obj) new_policy_object = self._copy_policy_object(obj, name) self._remove_policy_object(obj) return new_policy_object def _copy_policy_object(self, obj, name): return self.new_policy_object_dict(name, obj.export_config_dict()) # helper def get_helpers(self): return sorted(set(list(self._helpers.keys()) + \ list(self._builtin_helpers.keys()))) def add_helper(self, obj): if obj.builtin: self._builtin_helpers[obj.name] = obj else: self._helpers[obj.name] = obj def get_helper(self, name): if name in self._helpers: return self._helpers[name] elif name in self._builtin_helpers: return self._builtin_helpers[name] raise FirewallError(errors.INVALID_HELPER, name) def load_helper_defaults(self, obj): if obj.name not in self._helpers: raise FirewallError(errors.NO_DEFAULTS, obj.name) elif self._helpers[obj.name] != obj: raise FirewallError(errors.NO_DEFAULTS, "self._helpers[%s] != obj" % obj.name) elif obj.name not in self._builtin_helpers: raise FirewallError(errors.NO_DEFAULTS, "'%s' not a built-in helper" % obj.name) self._remove_helper(obj) return self._builtin_helpers[obj.name] def get_helper_config(self, obj): return obj.export_config() def set_helper_config(self, obj, conf): if obj.builtin: x = copy.copy(obj) x.import_config(conf) x.path = config.ETC_FIREWALLD_HELPERS x.builtin = False if obj.path != x.path: x.default = False self.add_helper(x) helper_writer(x) return x else: obj.import_config(conf) helper_writer(obj) return obj def new_helper(self, name, conf): if name in self._helpers or name in self._builtin_helpers: raise FirewallError(errors.NAME_CONFLICT, "new_helper(): '%s'" % name) x = Helper() x.check_name(name) x.import_config(conf) x.name = name x.filename = "%s.xml" % name x.path = config.ETC_FIREWALLD_HELPERS # It is not possible to add a new one with a name of a buitin x.builtin = False x.default = True helper_writer(x) self.add_helper(x) return x def update_helper_from_path(self, name): filename = os.path.basename(name) path = os.path.dirname(name) if not os.path.exists(name): # removed file if path == config.ETC_FIREWALLD_HELPERS: # removed custom helper for x in self._helpers.keys(): obj = self._helpers[x] if obj.filename == filename: del self._helpers[x] if obj.name in self._builtin_helpers: return ("update", self._builtin_helpers[obj.name]) return ("remove", obj) else: # removed builtin helper for x in self._builtin_helpers.keys(): obj = self._builtin_helpers[x] if obj.filename == filename: del self._builtin_helpers[x] if obj.name not in self._helpers: # update dbus helper return ("remove", obj) else: # builtin hidden, no update needed return (None, None) # helper not known to firewalld, yet (timeout, ..) return (None, None) # new or updated file log.debug1("Loading helper file '%s'", name) try: obj = helper_reader(filename, path) except Exception as msg: log.error("Failed to load helper file '%s': %s", filename, msg) return (None, None) # new helper if obj.name not in self._builtin_helpers and obj.name not in self._helpers: self.add_helper(obj) return ("new", obj) # updated helper if path == config.ETC_FIREWALLD_HELPERS: # custom helper update if obj.name in self._helpers: obj.default = self._helpers[obj.name].default self._helpers[obj.name] = obj return ("update", obj) else: if obj.name in self._builtin_helpers: # builtin helper update del self._builtin_helpers[obj.name] self._builtin_helpers[obj.name] = obj if obj.name not in self._helpers: # update dbus helper return ("update", obj) else: # builtin hidden, no update needed return (None, None) # helper not known to firewalld, yet (timeout, ..) return (None, None) def _remove_helper(self, obj): if obj.name not in self._helpers: raise FirewallError(errors.INVALID_HELPER, obj.name) if obj.path != config.ETC_FIREWALLD_HELPERS: raise FirewallError(errors.INVALID_DIRECTORY, "'%s' != '%s'" % (obj.path, config.ETC_FIREWALLD_HELPERS)) name = "%s/%s.xml" % (obj.path, obj.name) try: shutil.move(name, "%s.old" % name) except Exception as msg: log.error("Backup of file '%s' failed: %s", name, msg) os.remove(name) del self._helpers[obj.name] def check_builtin_helper(self, obj): if obj.builtin or not obj.default: raise FirewallError(errors.BUILTIN_HELPER, "'%s' is built-in helper" % obj.name) def remove_helper(self, obj): self.check_builtin_helper(obj) self._remove_helper(obj) def rename_helper(self, obj, name): self.check_builtin_helper(obj) new_helper = self._copy_helper(obj, name) self._remove_helper(obj) return new_helper def _copy_helper(self, obj, name): return self.new_helper(name, obj.export_config()) PK!^ܪ>y>y logger.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2005-2007,2012 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "LogTarget", "FileLog", "Logger", "log" ] import sys import types import time import inspect import fnmatch import syslog import traceback import fcntl import os.path import os # --------------------------------------------------------------------------- # abstract class for logging targets class LogTarget(object): """ Abstract class for logging targets. """ def __init__(self): self.fd = None def write(self, data, level, logger, is_debug=0): raise NotImplementedError("LogTarget.write is an abstract method") def flush(self): raise NotImplementedError("LogTarget.flush is an abstract method") def close(self): raise NotImplementedError("LogTarget.close is an abstract method") # --------------------------------------------------------------------------- # private class for stdout class _StdoutLog(LogTarget): def __init__(self): LogTarget.__init__(self) self.fd = sys.stdout def write(self, data, level, logger, is_debug=0): # ignore level self.fd.write(data) self.flush() def close(self): self.flush() def flush(self): self.fd.flush() # --------------------------------------------------------------------------- # private class for stderr class _StderrLog(_StdoutLog): def __init__(self): _StdoutLog.__init__(self) self.fd = sys.stderr # --------------------------------------------------------------------------- # private class for syslog class _SyslogLog(LogTarget): def __init__(self): # Only initialize LogTarget here as fs should be None LogTarget.__init__(self) # # Derived from: https://github.com/canvon/firewalld/commit/af0edfee1cc1891b7b13f302ca5911b24e9b0f13 # # Work around Python issue 27875, "Syslogs /usr/sbin/foo as /foo # instead of as foo" # (but using openlog explicitly might be better anyway) # # Set ident to basename, log PID as well, and log to facility "daemon". syslog.openlog(os.path.basename(sys.argv[0]), syslog.LOG_PID, syslog.LOG_DAEMON) def write(self, data, level, logger, is_debug=0): priority = None if is_debug: priority = syslog.LOG_DEBUG else: if level >= logger.INFO1: priority = syslog.LOG_INFO elif level == logger.WARNING: priority = syslog.LOG_WARNING elif level == logger.ERROR: priority = syslog.LOG_ERR elif level == logger.FATAL: priority = syslog.LOG_CRIT if data.endswith("\n"): data = data[:len(data)-1] if len(data) > 0: if priority is None: syslog.syslog(data) else: syslog.syslog(priority, data) def close(self): syslog.closelog() def flush(self): pass # --------------------------------------------------------------------------- class FileLog(LogTarget): """ FileLog class. File will be opened on the first write. """ def __init__(self, filename, mode="w"): LogTarget.__init__(self) self.filename = filename self.mode = mode def open(self): if self.fd: return flags = os.O_CREAT | os.O_WRONLY if self.mode.startswith('a'): flags |= os.O_APPEND self.fd = os.open(self.filename, flags, 0o640) # Make sure that existing file has correct perms os.fchmod(self.fd, 0o640) # Make it an object self.fd = os.fdopen(self.fd, self.mode) fcntl.fcntl(self.fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC) def write(self, data, level, logger, is_debug=0): if not self.fd: self.open() self.fd.write(data) self.fd.flush() def close(self): if not self.fd: return self.fd.close() self.fd = None def flush(self): if not self.fd: return self.fd.flush() # --------------------------------------------------------------------------- class Logger(object): r""" Format string: %(class)s Calling class the function belongs to, else empty %(date)s Date using Logger.date_format, see time module %(domain)s Full Domain: %(module)s.%(class)s.%(function)s %(file)s Filename of the module %(function)s Function name, empty in __main__ %(label)s Label according to log function call from Logger.label %(level)d Internal logging level %(line)d Line number in module %(module)s Module name %(message)s Log message Standard levels: FATAL Fatal error messages ERROR Error messages WARNING Warning messages INFOx, x in [1..5] Information DEBUGy, y in [1..10] Debug messages NO_INFO No info output NO_DEBUG No debug output INFO_MAX Maximum info level DEBUG_MAX Maximum debug level x and y depend on info_max and debug_max from Logger class initialization. See __init__ function. Default logging targets: stdout Logs to stdout stderr Logs to stderr syslog Logs to syslog Additional arguments for logging functions (fatal, error, warning, info and debug): nl Disable newline at the end with nl=0, default is nl=1. fmt Format string for this logging entry, overloads global format string. Example: fmt="%(file)s:%(line)d %(message)s" nofmt Only output message with nofmt=1. The nofmt argument wins over the fmt argument. Example: from logger import log log.setInfoLogLevel(log.INFO1) log.setDebugLogLevel(log.DEBUG1) for i in range(1, log.INFO_MAX+1): log.setInfoLogLabel(i, "INFO%d: " % i) log.setFormat("%(date)s %(module)s:%(line)d [%(domain)s] %(label)s: " "%(level)d %(message)s") log.setDateFormat("%Y-%m-%d %H:%M:%S") fl = FileLog("/tmp/log", "a") log.addInfoLogging("*", fl) log.addDebugLogging("*", fl) log.addInfoLogging("*", log.syslog, fmt="%(label)s%(message)s") log.debug3("debug3") log.debug2("debug2") log.debug1("debug1") log.info2("info2") log.info1("info1") log.warning("warning\n", nl=0) log.error("error\n", nl=0) log.fatal("fatal") log.info(log.INFO1, "nofmt info", nofmt=1) """ ALL = -5 NOTHING = -4 FATAL = -3 TRACEBACK = -2 ERROR = -1 WARNING = 0 # Additional levels are generated in class initilization stdout = _StdoutLog() stderr = _StderrLog() syslog = _SyslogLog() def __init__(self, info_max=5, debug_max=10): """ Logger class initialization """ self._level = { } self._debug_level = { } self._format = "" self._date_format = "" self._label = { } self._debug_label = { } self._logging = { } self._debug_logging = { } self._domains = { } self._debug_domains = { } # INFO1 is required for standard log level if info_max < 1: raise ValueError("Logger: info_max %d is too low" % info_max) if debug_max < 0: raise ValueError("Logger: debug_max %d is too low" % debug_max) self.NO_INFO = self.WARNING # = 0 self.INFO_MAX = info_max self.NO_DEBUG = 0 self.DEBUG_MAX = debug_max self.setInfoLogLabel(self.FATAL, "FATAL ERROR: ") self.setInfoLogLabel(self.TRACEBACK, "") self.setInfoLogLabel(self.ERROR, "ERROR: ") self.setInfoLogLabel(self.WARNING, "WARNING: ") # generate info levels and infox functions for _level in range(1, self.INFO_MAX+1): setattr(self, "INFO%d" % _level, _level) self.setInfoLogLabel(_level, "") setattr(self, "info%d" % (_level), (lambda self, x: lambda message, *args, **kwargs: self.info(x, message, *args, **kwargs))(self, _level)) # pylint: disable=E0602 # generate debug levels and debugx functions for _level in range(1, self.DEBUG_MAX+1): setattr(self, "DEBUG%d" % _level, _level) self.setDebugLogLabel(_level, "DEBUG%d: " % _level) setattr(self, "debug%d" % (_level), (lambda self, x: lambda message, *args, **kwargs: self.debug(x, message, *args, **kwargs))(self, _level)) # pylint: disable=E0602 # set initial log levels, formats and targets self.setInfoLogLevel(self.INFO1) self.setDebugLogLevel(self.NO_DEBUG) self.setFormat("%(label)s%(message)s") self.setDateFormat("%d %b %Y %H:%M:%S") self.setInfoLogging("*", self.stderr, [ self.FATAL, self.ERROR, self.WARNING ]) self.setInfoLogging("*", self.stdout, [ i for i in range(self.INFO1, self.INFO_MAX+1) ]) self.setDebugLogging("*", self.stdout, [ i for i in range(1, self.DEBUG_MAX+1) ]) def close(self): """ Close all logging targets """ for level in range(self.FATAL, self.DEBUG_MAX+1): if level not in self._logging: continue for (dummy, target, dummy) in self._logging[level]: target.close() def getInfoLogLevel(self, domain="*"): """ Get info log level. """ self._checkDomain(domain) if domain in self._level: return self._level[domain] return self.NOTHING def setInfoLogLevel(self, level, domain="*"): """ Set log level [NOTHING .. INFO_MAX] """ self._checkDomain(domain) if level < self.NOTHING: level = self.NOTHING if level > self.INFO_MAX: level = self.INFO_MAX self._level[domain] = level def getDebugLogLevel(self, domain="*"): """ Get debug log level. """ self._checkDomain(domain) if domain in self._debug_level: return self._debug_level[domain] + self.NO_DEBUG return self.NO_DEBUG def setDebugLogLevel(self, level, domain="*"): """ Set debug log level [NO_DEBUG .. DEBUG_MAX] """ self._checkDomain(domain) if level < 0: level = 0 if level > self.DEBUG_MAX: level = self.DEBUG_MAX self._debug_level[domain] = level - self.NO_DEBUG def getFormat(self): return self._format def setFormat(self, _format): self._format = _format def getDateFormat(self): return self._date_format def setDateFormat(self, _format): self._date_format = _format def setInfoLogLabel(self, level, label): """ Set log label for level. Level can be a single level or an array of levels. """ levels = self._getLevels(level) for level in levels: self._checkLogLevel(level, min_level=self.FATAL, max_level=self.INFO_MAX) self._label[level] = label def setDebugLogLabel(self, level, label): """ Set log label for level. Level can be a single level or an array of levels. """ levels = self._getLevels(level, is_debug=1) for level in levels: self._checkLogLevel(level, min_level=self.INFO1, max_level=self.DEBUG_MAX) self._debug_label[level] = label def setInfoLogging(self, domain, target, level=ALL, fmt=None): """ Set info log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. """ self._setLogging(domain, target, level, fmt, is_debug=0) def setDebugLogging(self, domain, target, level=ALL, fmt=None): """ Set debug log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. """ self._setLogging(domain, target, level, fmt, is_debug=1) def addInfoLogging(self, domain, target, level=ALL, fmt=None): """ Add info log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. """ self._addLogging(domain, target, level, fmt, is_debug=0) def addDebugLogging(self, domain, target, level=ALL, fmt=None): """ Add debg log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. """ self._addLogging(domain, target, level, fmt, is_debug=1) def delInfoLogging(self, domain, target, level=ALL, fmt=None): """ Delete info log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. """ self._delLogging(domain, target, level, fmt, is_debug=0) def delDebugLogging(self, domain, target, level=ALL, fmt=None): """ Delete debug log target for domain and level. Level can be a single level or an array of levels. Use level ALL to set for all levels. If no format is specified, the default format will be used. """ self._delLogging(domain, target, level, fmt, is_debug=1) def isInfoLoggingHere(self, level): """ Is there currently any info logging for this log level (and domain)? """ return self._isLoggingHere(level, is_debug=0) def isDebugLoggingHere(self, level): """ Is there currently any debug logging for this log level (and domain)? """ return self._isLoggingHere(level, is_debug=1) ### log functions def fatal(self, _format, *args, **kwargs): """ Fatal error log. """ self._checkKWargs(kwargs) kwargs["is_debug"] = 0 self._log(self.FATAL, _format, *args, **kwargs) def error(self, _format, *args, **kwargs): """ Error log. """ self._checkKWargs(kwargs) kwargs["is_debug"] = 0 self._log(self.ERROR, _format, *args, **kwargs) def warning(self, _format, *args, **kwargs): """ Warning log. """ self._checkKWargs(kwargs) kwargs["is_debug"] = 0 self._log(self.WARNING, _format, *args, **kwargs) def info(self, level, _format, *args, **kwargs): """ Information log using info level [1..info_max]. There are additional infox functions according to info_max from __init__""" self._checkLogLevel(level, min_level=1, max_level=self.INFO_MAX) self._checkKWargs(kwargs) kwargs["is_debug"] = 0 self._log(level+self.NO_INFO, _format, *args, **kwargs) def debug(self, level, _format, *args, **kwargs): """ Debug log using debug level [1..debug_max]. There are additional debugx functions according to debug_max from __init__""" self._checkLogLevel(level, min_level=1, max_level=self.DEBUG_MAX) self._checkKWargs(kwargs) kwargs["is_debug"] = 1 self._log(level, _format, *args, **kwargs) def exception(self): self._log(self.TRACEBACK, traceback.format_exc(), args=[], kwargs={}) ### internal functions def _checkLogLevel(self, level, min_level, max_level): if level < min_level or level > max_level: raise ValueError("Level %d out of range, should be [%d..%d]." % \ (level, min_level, max_level)) def _checkKWargs(self, kwargs): if not kwargs: return for key in kwargs.keys(): if key not in [ "nl", "fmt", "nofmt" ]: raise ValueError("Key '%s' is not allowed as argument for logging." % key) def _checkDomain(self, domain): if not domain or domain == "": raise ValueError("Domain '%s' is not valid." % domain) def _getLevels(self, level, is_debug=0): """ Generate log level array. """ if level != self.ALL: if isinstance(level, list) or isinstance(level, tuple): levels = level else: levels = [ level ] for level in levels: if is_debug: self._checkLogLevel(level, min_level=1, max_level=self.DEBUG_MAX) else: self._checkLogLevel(level, min_level=self.FATAL, max_level=self.INFO_MAX) else: if is_debug: levels = [ i for i in range(self.DEBUG1, self.DEBUG_MAX) ] else: levels = [ i for i in range(self.FATAL, self.INFO_MAX) ] return levels def _getTargets(self, target): """ Generate target array. """ if isinstance(target, list) or isinstance(target, tuple): targets = target else: targets = [ target ] for _target in targets: if not issubclass(_target.__class__, LogTarget): raise ValueError("'%s' is no valid logging target." % \ _target.__class__.__name__) return targets def _genDomains(self, is_debug=0): # private method for self._domains array creation, speeds up """ Generate dict with domain by level. """ if is_debug: _domains = self._debug_domains _logging = self._debug_logging _range = ( 1, self.DEBUG_MAX+1 ) else: _domains = self._domains _logging = self._logging _range = ( self.FATAL, self.INFO_MAX+1 ) if len(_domains) > 0: _domains.clear() for level in range(_range[0], _range[1]): if level not in _logging: continue for (domain, dummy, dummy) in _logging[level]: if domain not in _domains: _domains.setdefault(level, [ ]).append(domain) def _setLogging(self, domain, target, level=ALL, fmt=None, is_debug=0): self._checkDomain(domain) levels = self._getLevels(level, is_debug) targets = self._getTargets(target) if is_debug: _logging = self._debug_logging else: _logging = self._logging for level in levels: for target in targets: _logging[level] = [ (domain, target, fmt) ] self._genDomains(is_debug) def _addLogging(self, domain, target, level=ALL, fmt=None, is_debug=0): self._checkDomain(domain) levels = self._getLevels(level, is_debug) targets = self._getTargets(target) if is_debug: _logging = self._debug_logging else: _logging = self._logging for level in levels: for target in targets: _logging.setdefault(level, [ ]).append((domain, target, fmt)) self._genDomains(is_debug) def _delLogging(self, domain, target, level=ALL, fmt=None, is_debug=0): self._checkDomain(domain) levels = self._getLevels(level, is_debug) targets = self._getTargets(target) if is_debug: _logging = self._debug_logging else: _logging = self._logging for _level in levels: for target in targets: if _level not in _logging: continue if (domain, target, fmt) in _logging[_level]: _logging[_level].remove( (domain, target, fmt) ) if len(_logging[_level]) == 0: del _logging[_level] continue if level != self.ALL: raise ValueError("No mathing logging for " \ "level %d, domain %s, target %s and format %s." % \ (_level, domain, target.__class__.__name__, fmt)) self._genDomains(is_debug) def _isLoggingHere(self, level, is_debug=0): _dict = self._genDict(level, is_debug) if not _dict: return False point_domain = _dict["domain"] + "." if is_debug: _logging = self._debug_logging else: _logging = self._logging # do we need to log? for (domain, dummy, dummy) in _logging[level]: if domain == "*" or \ point_domain.startswith(domain) or \ fnmatch.fnmatchcase(_dict["domain"], domain): return True return False def _getClass(self, frame): """ Function to get calling class. Returns class or None. """ # get class by first function argument, if there are any if frame.f_code.co_argcount > 0: selfname = frame.f_code.co_varnames[0] if selfname in frame.f_locals: _self = frame.f_locals[selfname] obj = self._getClass2(_self.__class__, frame.f_code) if obj: return obj module = inspect.getmodule(frame.f_code) code = frame.f_code # function in module? if code.co_name in module.__dict__: if hasattr(module.__dict__[code.co_name], "func_code") and \ module.__dict__[code.co_name].__code__ == code: return None # class in module for (dummy, obj) in module.__dict__.items(): if isinstance(obj, types.ClassType): if hasattr(obj, code.co_name): value = getattr(obj, code.co_name) if isinstance(value, types.FunctionType): if value.__code__ == code: return obj # nothing found return None def _getClass2(self, obj, code): """ Internal function to get calling class. Returns class or None. """ for value in obj.__dict__.values(): if isinstance(value, types.FunctionType): if value.__code__ == code: return obj for base in obj.__bases__: _obj = self._getClass2(base, code) if _obj: return _obj return None # internal log class def _log(self, level, _format, *args, **kwargs): is_debug = 0 if "is_debug" in kwargs: is_debug = kwargs["is_debug"] nl = 1 if "nl" in kwargs: nl = kwargs["nl"] nofmt = 0 if "nofmt" in kwargs: nofmt = kwargs["nofmt"] _dict = self._genDict(level, is_debug) if not _dict: return if len(args) > 1: _dict['message'] = _format % args elif len(args) == 1: # needed for _format % _dict _dict['message'] = _format % args[0] else: _dict['message'] = _format point_domain = _dict["domain"] + "." if is_debug: _logging = self._debug_logging else: _logging = self._logging used_targets = [ ] # log to target(s) for (domain, target, _format) in _logging[level]: if target in used_targets: continue if domain == "*" \ or point_domain.startswith(domain+".") \ or fnmatch.fnmatchcase(_dict["domain"], domain): if not _format: _format = self._format if "fmt" in kwargs: _format = kwargs["fmt"] if nofmt: target.write(_dict["message"], level, self, is_debug) else: target.write(_format % _dict, level, self, is_debug) if nl: # newline target.write("\n", level, self, is_debug) used_targets.append(target) # internal function to generate the dict, needed for logging def _genDict(self, level, is_debug=0): """ Internal function. """ check_domains = [ ] simple_match = False if is_debug: _dict = self._debug_level _domains = self._debug_domains _label = self._debug_label else: _dict = self._level _domains = self._domains _label = self._label # no debug for domain in _dict: if domain == "*": # '*' matches everything: simple match if _dict[domain] >= level: simple_match = True if len(check_domains) > 0: check_domains = [ ] break else: if _dict[domain] >= level: check_domains.append(domain) if not simple_match and len(check_domains) < 1: return None if level not in _domains: return None f = inspect.currentframe() # go outside of logger module as long as there is a lower frame while f and f.f_back and f.f_globals["__name__"] == self.__module__: f = f.f_back if not f: raise ValueError("Frame information not available.") # get module name module_name = f.f_globals["__name__"] # simple module match test for all entries of check_domain point_module = module_name + "." for domain in check_domains: if point_module.startswith(domain): # found domain in module name check_domains = [ ] break # get code co = f.f_code # optimization: bail out early if domain can not match at all _len = len(module_name) for domain in _domains[level]: i = domain.find("*") if i == 0: continue elif i > 0: d = domain[:i] else: d = domain if _len >= len(d): if not module_name.startswith(d): return None else: if not d.startswith(module_name): return None # generate _dict for format output level_str = "" if level in _label: level_str = _label[level] _dict = { 'file': co.co_filename, 'line': f.f_lineno, 'module': module_name, 'class': '', 'function': co.co_name, 'domain': '', 'label' : level_str, 'level' : level, 'date' : time.strftime(self._date_format, time.localtime()) } if _dict["function"] == "?": _dict["function"] = "" # domain match needed? domain_needed = False for domain in _domains[level]: # standard domain, matches everything if domain == "*": continue # domain is needed domain_needed = True break # do we need to get the class object? if self._format.find("%(domain)") >= 0 or \ self._format.find("%(class)") >= 0 or \ domain_needed or \ len(check_domains) > 0: obj = self._getClass(f) if obj: _dict["class"] = obj.__name__ # build domain string _dict["domain"] = "" + _dict["module"] if _dict["class"] != "": _dict["domain"] += "." + _dict["class"] if _dict["function"] != "": _dict["domain"] += "." + _dict["function"] if len(check_domains) < 1: return _dict point_domain = _dict["domain"] + "." for domain in check_domains: if point_domain.startswith(domain) or \ fnmatch.fnmatchcase(_dict["domain"], domain): return _dict return None # --------------------------------------------------------------------------- # Global logging object. log = Logger() # --------------------------------------------------------------------------- """ # Example if __name__ == '__main__': log.setInfoLogLevel(log.INFO2) log.setDebugLogLevel(log.DEBUG5) for i in range(log.INFO1, log.INFO_MAX+1): log.setInfoLogLabel(i, "INFO%d: " % i) for i in range(log.DEBUG1, log.DEBUG_MAX+1): log.setDebugLogLabel(i, "DEBUG%d: " % i) log.setFormat("%(date)s %(module)s:%(line)d %(label)s" "%(message)s") log.setDateFormat("%Y-%m-%d %H:%M:%S") fl = FileLog("/tmp/log", "a") log.addInfoLogging("*", fl) log.delDebugLogging("*", log.stdout) log.setDebugLogging("*", log.stdout, [ log.DEBUG1, log.DEBUG2 ] ) log.addDebugLogging("*", fl) # log.addInfoLogging("*", log.syslog, fmt="%(label)s%(message)s") # log.addDebugLogging("*", log.syslog, fmt="%(label)s%(message)s") log.debug10("debug10") log.debug9("debug9") log.debug8("debug8") log.debug7("debug7") log.debug6("debug6") log.debug5("debug5") log.debug4("debug4") log.debug3("debug3") log.debug2("debug2", fmt="%(file)s:%(line)d %(message)s") log.debug1("debug1", nofmt=1) log.info5("info5") log.info4("info4") log.info3("info3") log.info2("info2") log.info1("info1") log.warning("warning\n", nl=0) log.error("error ", nl=0) log.error("error", nofmt=1) log.fatal("fatal") log.info(log.INFO1, "nofmt info", nofmt=1) log.info(log.INFO2, "info2 fmt", fmt="%(file)s:%(line)d %(message)s") try: a = b except Exception as e: log.exception() """ # vim:ts=4:sw=4:showmatch:expandtab PK! Pq2q2ipset.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2015-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """The ipset command wrapper""" __all__ = [ "ipset", "check_ipset_name", "remove_default_create_options" ] import os.path import ipaddress from firewall import errors from firewall.errors import FirewallError from firewall.core.prog import runProg from firewall.core.logger import log from firewall.functions import tempFile, readfile from firewall.config import COMMANDS IPSET_MAXNAMELEN = 32 IPSET_TYPES = [ # bitmap and set types are currently not supported # "bitmap:ip", # "bitmap:ip,mac", # "bitmap:port", # "list:set", "hash:ip", "hash:ip,port", "hash:ip,port,ip", "hash:ip,port,net", "hash:ip,mark", "hash:net", "hash:net,net", "hash:net,port", "hash:net,port,net", "hash:net,iface", "hash:mac", ] IPSET_CREATE_OPTIONS = { "family": "inet|inet6", "hashsize": "value", "maxelem": "value", "timeout": "value in secs", #"counters": None, #"comment": None, } IPSET_DEFAULT_CREATE_OPTIONS = { "family": "inet", "hashsize": "1024", "maxelem": "65536", } class ipset(object): """ipset command wrapper class""" def __init__(self): self._command = COMMANDS["ipset"] self.name = "ipset" def __run(self, args): """Call ipset with args""" # convert to string list _args = ["%s" % item for item in args] log.debug2("%s: %s %s", self.__class__, self._command, " ".join(_args)) (status, ret) = runProg(self._command, _args) if status != 0: raise ValueError("'%s %s' failed: %s" % (self._command, " ".join(_args), ret)) return ret def check_name(self, name): """Check ipset name""" if len(name) > IPSET_MAXNAMELEN: raise FirewallError(errors.INVALID_NAME, "ipset name '%s' is not valid" % name) def set_supported_types(self): """Return types that are supported by the ipset command and kernel""" ret = [ ] output = "" try: output = self.__run(["--help"]) except ValueError as ex: log.debug1("ipset error: %s" % ex) lines = output.splitlines() in_types = False for line in lines: #print(line) if in_types: splits = line.strip().split(None, 2) if splits[0] not in ret and splits[0] in IPSET_TYPES: ret.append(splits[0]) if line.startswith("Supported set types:"): in_types = True return ret def check_type(self, type_name): """Check ipset type""" if len(type_name) > IPSET_MAXNAMELEN or type_name not in IPSET_TYPES: raise FirewallError(errors.INVALID_TYPE, "ipset type name '%s' is not valid" % type_name) def set_create(self, set_name, type_name, options=None): """Create an ipset with name, type and options""" self.check_name(set_name) self.check_type(type_name) args = [ "create", set_name, type_name ] if isinstance(options, dict): for key, val in options.items(): args.append(key) if val != "": args.append(val) return self.__run(args) def set_destroy(self, set_name): self.check_name(set_name) return self.__run([ "destroy", set_name ]) def set_add(self, set_name, entry): args = [ "add", set_name, entry ] return self.__run(args) def set_delete(self, set_name, entry): args = [ "del", set_name, entry ] return self.__run(args) def test(self, set_name, entry, options=None): args = [ "test", set_name, entry ] if options: args.append("%s" % " ".join(options)) return self.__run(args) def set_list(self, set_name=None, options=None): args = [ "list" ] if set_name: args.append(set_name) if options: args.extend(options) return self.__run(args).split("\n") def set_get_active_terse(self): """ Get active ipsets (only headers) """ lines = self.set_list(options=["-terse"]) ret = { } _name = _type = None _options = { } for line in lines: if len(line) < 1: continue pair = [ x.strip() for x in line.split(":", 1) ] if len(pair) != 2: continue elif pair[0] == "Name": _name = pair[1] elif pair[0] == "Type": _type = pair[1] elif pair[0] == "Header": splits = pair[1].split() i = 0 while i < len(splits): opt = splits[i] if opt in [ "family", "hashsize", "maxelem", "timeout", "netmask" ]: if len(splits) > i: i += 1 _options[opt] = splits[i] else: log.error("Malformed ipset list -terse output: %s", line) return { } i += 1 if _name and _type: ret[_name] = (_type, remove_default_create_options(_options)) _name = _type = None _options.clear() return ret def save(self, set_name=None): args = [ "save" ] if set_name: args.append(set_name) return self.__run(args) def set_restore(self, set_name, type_name, entries, create_options=None, entry_options=None): self.check_name(set_name) self.check_type(type_name) temp_file = tempFile() if ' ' in set_name: set_name = "'%s'" % set_name args = [ "create", set_name, type_name, "-exist" ] if create_options: for key, val in create_options.items(): args.append(key) if val != "": args.append(val) temp_file.write("%s\n" % " ".join(args)) temp_file.write("flush %s\n" % set_name) for entry in entries: if ' ' in entry: entry = "'%s'" % entry if entry_options: temp_file.write("add %s %s %s\n" % \ (set_name, entry, " ".join(entry_options))) else: temp_file.write("add %s %s\n" % (set_name, entry)) temp_file.close() stat = os.stat(temp_file.name) log.debug2("%s: %s restore %s", self.__class__, self._command, "%s: %d" % (temp_file.name, stat.st_size)) args = [ "restore" ] (status, ret) = runProg(self._command, args, stdin=temp_file.name) if log.getDebugLogLevel() > 2: try: readfile(temp_file.name) except Exception: pass else: i = 1 for line in readfile(temp_file.name): log.debug3("%8d: %s" % (i, line), nofmt=1, nl=0) if not line.endswith("\n"): log.debug3("", nofmt=1) i += 1 os.unlink(temp_file.name) if status != 0: raise ValueError("'%s %s' failed: %s" % (self._command, " ".join(args), ret)) return ret def set_flush(self, set_name): args = [ "flush" ] if set_name: args.append(set_name) return self.__run(args) def rename(self, old_set_name, new_set_name): return self.__run([ "rename", old_set_name, new_set_name ]) def swap(self, set_name_1, set_name_2): return self.__run([ "swap", set_name_1, set_name_2 ]) def version(self): return self.__run([ "version" ]) def check_ipset_name(name): """Return true if ipset name is valid""" if len(name) > IPSET_MAXNAMELEN: return False return True def remove_default_create_options(options): """ Return only non default create options """ _options = options.copy() for opt in IPSET_DEFAULT_CREATE_OPTIONS: if opt in _options and \ IPSET_DEFAULT_CREATE_OPTIONS[opt] == _options[opt]: del _options[opt] return _options def normalize_ipset_entry(entry): """ Normalize IP addresses in entry """ _entry = [] for _part in entry.split(","): try: _part.index("/") _entry.append(str(ipaddress.ip_network(_part, strict=False))) except ValueError: _entry.append(_part) return ",".join(_entry) def check_entry_overlaps_existing(entry, entries): """ Check if entry overlaps any entry in the list of entries """ # Only check simple types if len(entry.split(",")) > 1: return try: entry_network = ipaddress.ip_network(entry, strict=False) except ValueError: # could not parse the new IP address, maybe a MAC return for itr in entries: if entry_network.overlaps(ipaddress.ip_network(itr, strict=False)): raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps with existing entry '{}'".format(entry, itr)) def check_for_overlapping_entries(entries): """ Check if any entry overlaps any entry in the list of entries """ try: entries = [ipaddress.ip_network(x, strict=False) for x in entries] except ValueError: # at least one entry can not be parsed return if len(entries) == 0: return # We can take advantage of some facts of IPv4Network/IPv6Network and # how Python sorts the networks to quickly detect overlaps. # # Facts: # # 1. IPv{4,6}Network are normalized to remove host bits, e.g. # 10.1.1.0/16 will become 10.1.0.0/16. # # 2. IPv{4,6}Network objects are sorted by: # a. IP address (network bits) # then # b. netmask (significant bits count) # # Because of the above we have these properties: # # 1. big networks (netA) are sorted before smaller networks (netB) # that overlap the big network (netA) # - e.g. 10.1.128.0/17 (netA) sorts before 10.1.129.0/24 (netB) # 2. same value addresses (network bits) are grouped together even # if the number of network bits vary. e.g. /16 vs /24 # - recall that address are normalized to remove host bits # - e.g. 10.1.128.0/17 (netA) sorts before 10.1.128.0/24 (netC) # 3. non-overlapping networks (netD, netE) are always sorted before or # after networks that overlap (netB, netC) the current one (netA) # - e.g. 10.1.128.0/17 (netA) sorts before 10.2.128.0/16 (netD) # - e.g. 10.1.128.0/17 (netA) sorts after 9.1.128.0/17 (netE) # - e.g. 9.1.128.0/17 (netE) sorts before 10.1.129.0/24 (netB) # # With this we know the sorted list looks like: # # list: [ netE, netA, netB, netC, netD ] # # netE = non-overlapping network # netA = big network # netB = smaller network that overlaps netA (subnet) # netC = smaller network that overlaps netA (subnet) # netD = non-overlapping network # # If networks netB and netC exist in the list, they overlap and are # adjacent to netA. # # Checking for overlaps on a sorted list is thus: # # 1. compare adjacent elements in the list for overlaps # # Recall that we only need to detect a single overlap. We do not need to # detect them all. # entries.sort() prev_network = entries.pop(0) for current_network in entries: if prev_network.overlaps(current_network): raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps entry '{}'".format(prev_network, current_network)) prev_network = current_network PK!OXWW fw_direct.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2010-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "FirewallDirect" ] from firewall.fw_types import LastUpdatedOrderedDict from firewall.core import ipXtables from firewall.core import ebtables from firewall.core.fw_transaction import FirewallTransaction from firewall.core.logger import log from firewall import errors from firewall.errors import FirewallError ############################################################################ # # class Firewall # ############################################################################ class FirewallDirect(object): def __init__(self, fw): self._fw = fw self.__init_vars() def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__, self._chains, self._rules, self._rule_priority_positions) def __init_vars(self): self._chains = { } self._rules = { } self._rule_priority_positions = { } self._passthroughs = { } self._obj = None def cleanup(self): self.__init_vars() # transaction def new_transaction(self): return FirewallTransaction(self._fw) # configuration def set_permanent_config(self, obj): self._obj = obj def has_runtime_configuration(self): if len(self._chains) + len(self._rules) + len(self._passthroughs) > 0: return True return False def has_configuration(self): if self.has_runtime_configuration(): return True if len(self._obj.get_all_chains()) + \ len(self._obj.get_all_rules()) + \ len(self._obj.get_all_passthroughs()) > 0: return True return False def apply_direct(self, use_transaction=None): if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction # Apply permanent configuration and save the obj to be able to # remove permanent configuration settings within get_runtime_config # for use in firewalld reload. self.set_config((self._obj.get_all_chains(), self._obj.get_all_rules(), self._obj.get_all_passthroughs()), transaction) if use_transaction is None: transaction.execute(True) def get_runtime_config(self): # Return only runtime changes # Remove all chains, rules and passthroughs that are in self._obj # (permanent config applied in firewalld _start. chains = { } rules = { } passthroughs = { } for table_id in self._chains: (ipv, table) = table_id for chain in self._chains[table_id]: if not self._obj.query_chain(ipv, table, chain): chains.setdefault(table_id, [ ]).append(chain) for chain_id in self._rules: (ipv, table, chain) = chain_id for (priority, args) in self._rules[chain_id]: if not self._obj.query_rule(ipv, table, chain, priority, args): if chain_id not in rules: rules[chain_id] = LastUpdatedOrderedDict() rules[chain_id][(priority, args)] = priority for ipv in self._passthroughs: for args in self._passthroughs[ipv]: if not self._obj.query_passthrough(ipv, args): if ipv not in passthroughs: passthroughs[ipv] = [ ] passthroughs[ipv].append(args) return (chains, rules, passthroughs) def get_config(self): return (self._chains, self._rules, self._passthroughs) def set_config(self, conf, use_transaction=None): if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction (_chains, _rules, _passthroughs) = conf for table_id in _chains: (ipv, table) = table_id for chain in _chains[table_id]: if not self.query_chain(ipv, table, chain): try: self.add_chain(ipv, table, chain, use_transaction=transaction) except FirewallError as error: log.warning(str(error)) for chain_id in _rules: (ipv, table, chain) = chain_id for (priority, args) in _rules[chain_id]: if not self.query_rule(ipv, table, chain, priority, args): try: self.add_rule(ipv, table, chain, priority, args, use_transaction=transaction) except FirewallError as error: log.warning(str(error)) for ipv in _passthroughs: for args in _passthroughs[ipv]: if not self.query_passthrough(ipv, args): try: self.add_passthrough(ipv, args, use_transaction=transaction) except FirewallError as error: log.warning(str(error)) if use_transaction is None: transaction.execute(True) def _check_ipv(self, ipv): ipvs = ['ipv4', 'ipv6', 'eb'] if ipv not in ipvs: raise FirewallError(errors.INVALID_IPV, "'%s' not in '%s'" % (ipv, ipvs)) def _check_ipv_table(self, ipv, table): self._check_ipv(ipv) tables = ipXtables.BUILT_IN_CHAINS.keys() if ipv in [ 'ipv4', 'ipv6' ] \ else ebtables.BUILT_IN_CHAINS.keys() if table not in tables: raise FirewallError(errors.INVALID_TABLE, "'%s' not in '%s'" % (table, tables)) def _check_builtin_chain(self, ipv, table, chain): if ipv in ['ipv4', 'ipv6']: built_in_chains = ipXtables.BUILT_IN_CHAINS[table] if self._fw.nftables_enabled: our_chains = {} else: our_chains = self._fw.get_direct_backend_by_ipv(ipv).our_chains[table] else: built_in_chains = ebtables.BUILT_IN_CHAINS[table] our_chains = ebtables.OUR_CHAINS[table] if chain in built_in_chains: raise FirewallError(errors.BUILTIN_CHAIN, "chain '%s' is built-in chain" % chain) if chain in our_chains: raise FirewallError(errors.BUILTIN_CHAIN, "chain '%s' is reserved" % chain) if ipv in [ "ipv4", "ipv6" ]: if self._fw.zone.zone_from_chain(chain) is not None: raise FirewallError(errors.INVALID_CHAIN, "Chain '%s' is reserved" % chain) def _register_chain(self, table_id, chain, add): if add: self._chains.setdefault(table_id, [ ]).append(chain) else: self._chains[table_id].remove(chain) if len(self._chains[table_id]) == 0: del self._chains[table_id] def add_chain(self, ipv, table, chain, use_transaction=None): if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if self._fw.may_skip_flush_direct_backends(): transaction.add_pre(self._fw.flush_direct_backends) #TODO: policy="ACCEPT" self._chain(True, ipv, table, chain, transaction) if use_transaction is None: transaction.execute(True) def remove_chain(self, ipv, table, chain, use_transaction=None): if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction self._chain(False, ipv, table, chain, transaction) if use_transaction is None: transaction.execute(True) def query_chain(self, ipv, table, chain): self._check_ipv_table(ipv, table) self._check_builtin_chain(ipv, table, chain) table_id = (ipv, table) return (table_id in self._chains and chain in self._chains[table_id]) def get_chains(self, ipv, table): self._check_ipv_table(ipv, table) table_id = (ipv, table) if table_id in self._chains: return self._chains[table_id] return [ ] def get_all_chains(self): r = [ ] for key in self._chains: (ipv, table) = key for chain in self._chains[key]: r.append((ipv, table, chain)) return r def add_rule(self, ipv, table, chain, priority, args, use_transaction=None): if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if self._fw.may_skip_flush_direct_backends(): transaction.add_pre(self._fw.flush_direct_backends) self._rule(True, ipv, table, chain, priority, args, transaction) if use_transaction is None: transaction.execute(True) def remove_rule(self, ipv, table, chain, priority, args, use_transaction=None): if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction self._rule(False, ipv, table, chain, priority, args, transaction) if use_transaction is None: transaction.execute(True) def query_rule(self, ipv, table, chain, priority, args): self._check_ipv_table(ipv, table) chain_id = (ipv, table, chain) return chain_id in self._rules and \ (priority, args) in self._rules[chain_id] def get_rules(self, ipv, table, chain): self._check_ipv_table(ipv, table) chain_id = (ipv, table, chain) if chain_id in self._rules: return list(self._rules[chain_id].keys()) return [ ] def get_all_rules(self): r = [ ] for key in self._rules: (ipv, table, chain) = key for (priority, args) in self._rules[key]: r.append((ipv, table, chain, priority, list(args))) return r def _register_rule(self, rule_id, chain_id, priority, enable, count): if enable: if chain_id not in self._rules: self._rules[chain_id] = LastUpdatedOrderedDict() self._rules[chain_id][rule_id] = priority if chain_id not in self._rule_priority_positions: self._rule_priority_positions[chain_id] = { } if priority in self._rule_priority_positions[chain_id]: self._rule_priority_positions[chain_id][priority] += count else: self._rule_priority_positions[chain_id][priority] = count else: del self._rules[chain_id][rule_id] if len(self._rules[chain_id]) == 0: del self._rules[chain_id] self._rule_priority_positions[chain_id][priority] -= count # DIRECT PASSTHROUGH (untracked) def passthrough(self, ipv, args): try: return self._fw.rule(self._fw.get_direct_backend_by_ipv(ipv).name, args) except Exception as msg: log.debug2(msg) raise FirewallError(errors.COMMAND_FAILED, msg) def _register_passthrough(self, ipv, args, enable): if enable: if ipv not in self._passthroughs: self._passthroughs[ipv] = [ ] self._passthroughs[ipv].append(args) else: self._passthroughs[ipv].remove(args) if len(self._passthroughs[ipv]) == 0: del self._passthroughs[ipv] def add_passthrough(self, ipv, args, use_transaction=None): if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if self._fw.may_skip_flush_direct_backends(): transaction.add_pre(self._fw.flush_direct_backends) self._passthrough(True, ipv, list(args), transaction) if use_transaction is None: transaction.execute(True) def remove_passthrough(self, ipv, args, use_transaction=None): if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction self._passthrough(False, ipv, list(args), transaction) if use_transaction is None: transaction.execute(True) def query_passthrough(self, ipv, args): return ipv in self._passthroughs and \ tuple(args) in self._passthroughs[ipv] def get_all_passthroughs(self): r = [ ] for ipv in self._passthroughs: for args in self._passthroughs[ipv]: r.append((ipv, list(args))) return r def get_passthroughs(self, ipv): r = [ ] if ipv in self._passthroughs: for args in self._passthroughs[ipv]: r.append(list(args)) return r def split_value(self, rules, opts): """Split values combined with commas for options in opts""" out_rules = [ ] for rule in rules: processed = False for opt in opts: try: i = rule.index(opt) except ValueError: pass else: if len(rule) > i and "," in rule[i+1]: # For all items in the comma separated list in index # i of the rule, a new rule is created with a single # item from this list processed = True items = rule[i+1].split(",") for item in items: _rule = rule[:] _rule[i+1] = item out_rules.append(_rule) if not processed: out_rules.append(rule) return out_rules def _rule(self, enable, ipv, table, chain, priority, args, transaction): self._check_ipv_table(ipv, table) # Do not create zone chains if we're using nftables. Only allow direct # rules in the built in chains. if not self._fw.nftables_enabled \ and ipv in [ "ipv4", "ipv6" ]: self._fw.zone.create_zone_base_by_chain(ipv, table, chain, transaction) _chain = chain backend = self._fw.get_direct_backend_by_ipv(ipv) # if nftables is in use, just put the direct rules in the chain # specified by the user. i.e. don't append _direct. if not self._fw.nftables_enabled \ and backend.is_chain_builtin(ipv, table, chain): _chain = "%s_direct" % (chain) elif self._fw.nftables_enabled and chain[-7:] == "_direct" \ and backend.is_chain_builtin(ipv, table, chain[:-7]): # strip _direct suffix. If we're using nftables we don't bother # creating the *_direct chains for builtin chains. _chain = chain[:-7] chain_id = (ipv, table, chain) rule_id = (priority, args) if enable: if chain_id in self._rules and \ rule_id in self._rules[chain_id]: raise FirewallError(errors.ALREADY_ENABLED, "rule '%s' already is in '%s:%s:%s'" % \ (args, ipv, table, chain)) else: if chain_id not in self._rules or \ rule_id not in self._rules[chain_id]: raise FirewallError(errors.NOT_ENABLED, "rule '%s' is not in '%s:%s:%s'" % \ (args, ipv, table, chain)) # get priority of rule priority = self._rules[chain_id][rule_id] # If a rule gets added, the initial rule index position within the # ipv, table and chain combination (chain_id) is 1. # Tf the chain_id exists in _rule_priority_positions, there are already # other rules for this chain_id. The number of rules for a priority # less or equal to the priority of the new rule will increase the # index of the new rule. The index is the ip*tables -I insert rule # number. # # Example: We have the following rules for chain_id (ipv4, filter, # INPUT) already: # ipv4, filter, INPUT, 1, -i, foo1, -j, ACCEPT # ipv4, filter, INPUT, 2, -i, foo2, -j, ACCEPT # ipv4, filter, INPUT, 2, -i, foo2_1, -j, ACCEPT # ipv4, filter, INPUT, 3, -i, foo3, -j, ACCEPT # This results in the following _rule_priority_positions structure: # _rule_priority_positions[(ipv4,filter,INPUT)][1] = 1 # _rule_priority_positions[(ipv4,filter,INPUT)][2] = 2 # _rule_priority_positions[(ipv4,filter,INPUT)][3] = 1 # The new rule # ipv4, filter, INPUT, 2, -i, foo2_2, -j, ACCEPT # has the same pritority as the second rule before and will be added # right after it. # The initial index is 1 and the chain_id is already in # _rule_priority_positions. Therefore the index will increase for # the number of rules in every rule position in # _rule_priority_positions[(ipv4,filter,INPUT)].keys() # where position is smaller or equal to the entry in keys. # With the example from above: # The priority of the new rule is 2. Therefore for all keys in # _rule_priority_positions[chain_id] where priority is 1 or 2, the # number of the rules will increase the index of the rule. # For _rule_priority_positions[chain_id][1]: index += 1 # _rule_priority_positions[chain_id][2]: index += 2 # index will be 4 in the end and the rule in the table chain # combination will be added at index 4. # If there are no rules in the table chain combination, a new rule # has index 1. index = 1 count = 0 if chain_id in self._rule_priority_positions: positions = sorted(self._rule_priority_positions[chain_id].keys()) j = 0 while j < len(positions) and priority >= positions[j]: index += self._rule_priority_positions[chain_id][positions[j]] j += 1 # split the direct rule in some cases as iptables-restore can't handle # compound args. # args_list = [list(args)] args_list = self.split_value(args_list, [ "-s", "--source" ]) args_list = self.split_value(args_list, [ "-d", "--destination" ]) for _args in args_list: transaction.add_rule(backend, backend.build_rule(enable, table, _chain, index, tuple(_args))) index += 1 count += 1 self._register_rule(rule_id, chain_id, priority, enable, count) transaction.add_fail(self._register_rule, rule_id, chain_id, priority, not enable, count) def _chain(self, add, ipv, table, chain, transaction): self._check_ipv_table(ipv, table) self._check_builtin_chain(ipv, table, chain) table_id = (ipv, table) if add: if table_id in self._chains and \ chain in self._chains[table_id]: raise FirewallError(errors.ALREADY_ENABLED, "chain '%s' already is in '%s:%s'" % \ (chain, ipv, table)) else: if table_id not in self._chains or \ chain not in self._chains[table_id]: raise FirewallError(errors.NOT_ENABLED, "chain '%s' is not in '%s:%s'" % \ (chain, ipv, table)) backend = self._fw.get_direct_backend_by_ipv(ipv) transaction.add_rules(backend, backend.build_chain_rules(add, table, chain)) self._register_chain(table_id, chain, add) transaction.add_fail(self._register_chain, table_id, chain, not add) def _passthrough(self, enable, ipv, args, transaction): self._check_ipv(ipv) tuple_args = tuple(args) if enable: if ipv in self._passthroughs and \ tuple_args in self._passthroughs[ipv]: raise FirewallError(errors.ALREADY_ENABLED, "passthrough '%s', '%s'" % (ipv, args)) else: if ipv not in self._passthroughs or \ tuple_args not in self._passthroughs[ipv]: raise FirewallError(errors.NOT_ENABLED, "passthrough '%s', '%s'" % (ipv, args)) backend = self._fw.get_direct_backend_by_ipv(ipv) if enable: backend.check_passthrough(args) # try to find out if a zone chain should be used if ipv in [ "ipv4", "ipv6" ]: table, chain = backend.passthrough_parse_table_chain(args) if table and chain: self._fw.zone.create_zone_base_by_chain(ipv, table, chain) _args = args else: _args = backend.reverse_passthrough(args) transaction.add_rule(backend, _args) self._register_passthrough(ipv, tuple_args, enable) transaction.add_fail(self._register_passthrough, ipv, tuple_args, not enable) PK!E-kofw_transaction.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """Transaction classes for firewalld""" __all__ = [ "FirewallTransaction" ] import traceback from firewall.core.logger import log from firewall import errors from firewall.errors import FirewallError class FirewallTransaction(object): def __init__(self, fw): self.fw = fw self.rules = { } # [ ( backend.name, [ rule,.. ] ),.. ] self.pre_funcs = [ ] # [ (func, args),.. ] self.post_funcs = [ ] # [ (func, args),.. ] self.fail_funcs = [ ] # [ (func, args),.. ] self.modules = [ ] # [ module,.. ] def clear(self): self.rules.clear() del self.pre_funcs[:] del self.post_funcs[:] del self.fail_funcs[:] def add_rule(self, backend, rule): self.rules.setdefault(backend.name, [ ]).append(rule) def add_rules(self, backend, rules): for rule in rules: self.add_rule(backend, rule) def query_rule(self, backend, rule): return backend.name in self.rules and rule in self.rules[backend.name] def remove_rule(self, backend, rule): if backend.name in self.rules and rule in self.rules[backend.name]: self.rules[backend.name].remove(rule) def add_pre(self, func, *args): self.pre_funcs.append((func, args)) def add_post(self, func, *args): self.post_funcs.append((func, args)) def add_fail(self, func, *args): self.fail_funcs.append((func, args)) def add_module(self, module): if module not in self.modules: self.modules.append(module) def remove_module(self, module): if module in self.modules: self.modules.remove(module) def add_modules(self, modules): for module in modules: self.add_module(module) def remove_modules(self, modules): for module in modules: self.remove_module(module) def prepare(self, enable): log.debug4("%s.prepare(%s, %s)" % (type(self), enable, "...")) rules = { } if not enable: # reverse rule order for cleanup for backend_name in self.rules: for rule in reversed(self.rules[backend_name]): rules.setdefault(backend_name, [ ]).append( self.fw.get_backend_by_name(backend_name).reverse_rule(rule)) else: for backend_name in self.rules: rules.setdefault(backend_name, [ ]).extend(self.rules[backend_name]) return rules, self.modules def execute(self, enable): log.debug4("%s.execute(%s)" % (type(self), enable)) rules, modules = self.prepare(enable) # pre self.pre() # stage 1: apply rules error = False errorMsg = "" done = [ ] for backend_name in rules: try: self.fw.rules(backend_name, rules[backend_name]) except Exception as msg: error = True errorMsg = msg log.debug1(traceback.format_exc()) log.error(msg) else: done.append(backend_name) # stage 2: load modules if not error: module_return = self.fw.handle_modules(modules, enable) if module_return: # Debug log about issues loading modules, but don't error. The # modules may be builtin or CONFIG_MODULES=n, in which case # modprobe will fail. Or we may be running inside a container # that doesn't have sufficient privileges. Unfortunately there # is no way for us to know. (status, msg) = module_return if status: log.debug1(msg) # error case: revert rules if error: undo_rules = { } for backend_name in done: undo_rules[backend_name] = [ ] for rule in reversed(rules[backend_name]): undo_rules[backend_name].append( self.fw.get_backend_by_name(backend_name).reverse_rule(rule)) for backend_name in undo_rules: try: self.fw.rules(backend_name, undo_rules[backend_name]) except Exception as msg: log.debug1(traceback.format_exc()) log.error(msg) # call failure functions for (func, args) in self.fail_funcs: try: func(*args) except Exception as msg: log.debug1(traceback.format_exc()) log.error("Calling fail func %s(%s) failed: %s" % \ (func, args, msg)) raise FirewallError(errors.COMMAND_FAILED, errorMsg) # post self.post() def pre(self): log.debug4("%s.pre()" % type(self)) for (func, args) in self.pre_funcs: try: func(*args) except Exception as msg: log.debug1(traceback.format_exc()) log.error("Calling pre func %s(%s) failed: %s" % \ (func, args, msg)) def post(self): log.debug4("%s.post()" % type(self)) for (func, args) in self.post_funcs: try: func(*args) except Exception as msg: log.debug1(traceback.format_exc()) log.error("Calling post func %s(%s) failed: %s" % \ (func, args, msg)) PK!~ watcher.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2012-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "Watcher" ] from gi.repository import Gio, GLib class Watcher(object): def __init__(self, callback, timeout): self._callback = callback self._timeout = timeout self._monitors = { } self._timeouts = { } self._blocked = [ ] def add_watch_dir(self, directory): gfile = Gio.File.new_for_path(directory) self._monitors[directory] = gfile.monitor_directory(\ Gio.FileMonitorFlags.NONE, None) self._monitors[directory].connect("changed", self._file_changed_cb) def add_watch_file(self, filename): gfile = Gio.File.new_for_path(filename) self._monitors[filename] = gfile.monitor_file(\ Gio.FileMonitorFlags.NONE, None) self._monitors[filename].connect("changed", self._file_changed_cb) def get_watches(self): return self._monitors.keys() def has_watch(self, filename): return filename in self._monitors def remove_watch(self, filename): del self._monitors[filename] def block_source(self, filename): if filename not in self._blocked: self._blocked.append(filename) def unblock_source(self, filename): if filename in self._blocked: self._blocked.remove(filename) def clear_timeouts(self): for filename in list(self._timeouts.keys()): GLib.source_remove(self._timeouts[filename]) del self._timeouts[filename] def _call_callback(self, filename): if filename not in self._blocked: self._callback(filename) del self._timeouts[filename] def _file_changed_cb(self, monitor, gio_file, gio_other_file, event): filename = gio_file.get_parse_name() if filename in self._blocked: if filename in self._timeouts: GLib.source_remove(self._timeouts[filename]) del self._timeouts[filename] return if event == Gio.FileMonitorEvent.CHANGED or \ event == Gio.FileMonitorEvent.CREATED or \ event == Gio.FileMonitorEvent.DELETED or \ event == Gio.FileMonitorEvent.ATTRIBUTE_CHANGED: if filename in self._timeouts: GLib.source_remove(self._timeouts[filename]) del self._timeouts[filename] self._timeouts[filename] = GLib.timeout_add_seconds(\ self._timeout, self._call_callback, filename) PK!1 fw_policies.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "FirewallPolicies" ] from firewall import config from firewall.core.logger import log from firewall.core.io.lockdown_whitelist import LockdownWhitelist from firewall import errors from firewall.errors import FirewallError class FirewallPolicies(object): def __init__(self): self._lockdown = False self.lockdown_whitelist = LockdownWhitelist(config.LOCKDOWN_WHITELIST) def __repr__(self): return '%s(%r, %r)' % (self.__class__, self._lockdown, self.lockdown_whitelist) def cleanup(self): self._lockdown = False self.lockdown_whitelist.cleanup() # lockdown def access_check(self, key, value): if key == "context": log.debug2('Doing access check for context "%s"' % value) if self.lockdown_whitelist.match_context(value): log.debug3('context matches.') return True elif key == "uid": log.debug2('Doing access check for uid %d' % value) if self.lockdown_whitelist.match_uid(value): log.debug3('uid matches.') return True elif key == "user": log.debug2('Doing access check for user "%s"' % value) if self.lockdown_whitelist.match_user(value): log.debug3('user matches.') return True elif key == "command": log.debug2('Doing access check for command "%s"' % value) if self.lockdown_whitelist.match_command(value): log.debug3('command matches.') return True return False def enable_lockdown(self): if self._lockdown: raise FirewallError(errors.ALREADY_ENABLED, "enable_lockdown()") self._lockdown = True def disable_lockdown(self): if not self._lockdown: raise FirewallError(errors.NOT_ENABLED, "disable_lockdown()") self._lockdown = False def query_lockdown(self): return self._lockdown PK!*fw_nm.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2010-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """Functions for NetworkManager interaction""" __all__ = [ "check_nm_imported", "nm_is_imported", "nm_get_zone_of_connection", "nm_set_zone_of_connection", "nm_get_connections", "nm_get_connection_of_interface", "nm_get_bus_name", "nm_get_dbus_interface" ] import gi from gi.repository import GLib try: gi.require_version('NM', '1.0') except ValueError: _nm_imported = False else: try: from gi.repository import NM _nm_imported = True except (ImportError, ValueError, GLib.Error): _nm_imported = False _nm_client = None from firewall import errors from firewall.errors import FirewallError from firewall.core.logger import log import dbus def check_nm_imported(): """Check function to raise a MISSING_IMPORT error if the import of NM failed """ if not _nm_imported: raise FirewallError(errors.MISSING_IMPORT, "gi.repository.NM = 1.0") def nm_is_imported(): """Returns true if NM has been properly imported @return True if import was successful, False otherwirse """ return _nm_imported def nm_get_client(): """Returns the NM client object or None if the import of NM failed @return NM.Client instance if import was successful, None otherwise """ global _nm_client if not _nm_client: _nm_client = NM.Client.new(None) return _nm_client def nm_get_zone_of_connection(connection): """Get zone of connection from NM @param connection name @return zone string setting of connection, empty string if not set, None if connection is unknown """ check_nm_imported() con = nm_get_client().get_connection_by_uuid(connection) if con is None: return None setting_con = con.get_setting_connection() if setting_con is None: return None try: if con.get_flags() & (NM.SettingsConnectionFlags.NM_GENERATED | NM.SettingsConnectionFlags.NM_VOLATILE): return "" except AttributeError: # Prior to NetworkManager 1.12, we can only guess # that a connection was generated/volatile. if con.get_unsaved(): return "" zone = setting_con.get_zone() if zone is None: zone = "" return zone def nm_set_zone_of_connection(zone, connection): """Set the zone for a connection @param zone name @param connection name @return True if zone was set, else False """ check_nm_imported() con = nm_get_client().get_connection_by_uuid(connection) if con is None: return False setting_con = con.get_setting_connection() if setting_con is None: return False if zone == "": zone = None setting_con.set_property("zone", zone) return con.commit_changes(True, None) def nm_get_connections(connections, connections_name): """Get active connections from NM @param connections return dict @param connections_name return dict """ connections.clear() connections_name.clear() check_nm_imported() active_connections = nm_get_client().get_active_connections() for active_con in active_connections: # ignore vpn devices for now if active_con.get_vpn(): continue name = active_con.get_id() uuid = active_con.get_uuid() devices = active_con.get_devices() connections_name[uuid] = name for dev in devices: ip_iface = dev.get_ip_iface() if ip_iface: connections[ip_iface] = uuid def nm_get_interfaces(): """Get active interfaces from NM @returns list of interface names """ check_nm_imported() active_interfaces = [] for active_con in nm_get_client().get_active_connections(): # ignore vpn devices for now if active_con.get_vpn(): continue try: con = active_con.get_connection() if con.get_flags() & (NM.SettingsConnectionFlags.NM_GENERATED | NM.SettingsConnectionFlags.NM_VOLATILE): continue except AttributeError: # Prior to NetworkManager 1.12, we can only guess # that a connection was generated/volatile. if con.get_unsaved(): continue for dev in active_con.get_devices(): ip_iface = dev.get_ip_iface() if ip_iface: active_interfaces.append(ip_iface) return active_interfaces def nm_get_interfaces_in_zone(zone): interfaces = [] for interface in nm_get_interfaces(): conn = nm_get_connection_of_interface(interface) if zone == nm_get_zone_of_connection(conn): interfaces.append(interface) return interfaces def nm_get_device_by_ip_iface(interface): """Get device from NM which has the given IP interface @param interface name @returns NM.Device instance or None """ check_nm_imported() for device in nm_get_client().get_devices(): ip_iface = device.get_ip_iface() if ip_iface is None: continue if ip_iface == interface: return device return None def nm_get_connection_of_interface(interface): """Get connection from NM that is using the interface @param interface name @returns connection that is using interface or None """ check_nm_imported() device = nm_get_device_by_ip_iface(interface) if device is None: return None active_con = device.get_active_connection() if active_con is None: return None try: con = active_con.get_connection() if con.get_flags() & NM.SettingsConnectionFlags.NM_GENERATED: return None except AttributeError: # Prior to NetworkManager 1.12, we can only guess # that a connection was generated. if con.get_unsaved(): return None return active_con.get_uuid() def nm_get_bus_name(): if not _nm_imported: return None try: bus = dbus.SystemBus() obj = bus.get_object(NM.DBUS_INTERFACE, NM.DBUS_PATH) name = obj.bus_name del obj, bus return name except Exception: log.debug2("Failed to get bus name of NetworkManager") return None def nm_get_dbus_interface(): if not _nm_imported: return "" return NM.DBUS_INTERFACE PK!Uе fw_icmptype.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "FirewallIcmpType" ] from firewall.core.logger import log from firewall import errors from firewall.errors import FirewallError class FirewallIcmpType(object): def __init__(self, fw): self._fw = fw self._icmptypes = { } def __repr__(self): return '%s(%r)' % (self.__class__, self._icmptypes) def cleanup(self): self._icmptypes.clear() # zones def get_icmptypes(self): return sorted(self._icmptypes.keys()) def check_icmptype(self, icmptype): if icmptype not in self._icmptypes: raise FirewallError(errors.INVALID_ICMPTYPE, icmptype) def get_icmptype(self, icmptype): self.check_icmptype(icmptype) return self._icmptypes[icmptype] def add_icmptype(self, obj): orig_ipvs = obj.destination if len(orig_ipvs) == 0: orig_ipvs = [ "ipv4", "ipv6" ] for ipv in orig_ipvs: if ipv == "ipv4": if not self._fw.ip4tables_enabled and not self._fw.nftables_enabled: continue supported_icmps = self._fw.ipv4_supported_icmp_types elif ipv == "ipv6": if not self._fw.ip6tables_enabled and not self._fw.nftables_enabled: continue supported_icmps = self._fw.ipv6_supported_icmp_types else: supported_icmps = [ ] if obj.name.lower() not in supported_icmps: log.info1("ICMP type '%s' is not supported by the kernel for %s." % (obj.name, ipv)) self._icmptypes[obj.name] = obj def remove_icmptype(self, icmptype): self.check_icmptype(icmptype) del self._icmptypes[icmptype] PK!#$icmp.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2017 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "ICMP_TYPES", "ICMPV6_TYPES", "check_icmp_type_code", "check_icmpv6_type_code" ] ICMP_TYPES = { # "type": (type, code, backend omit code) "echo-reply": (0, 0, True), "pong": (0, 0, True), "destination-unreachable": (3, 0, True), "network-unreachable": (3, 0, False), "tos-network-unreachable": (3, 0, False), "host-unreachable": (3, 1, False), "tos-host-unreachable": (3, 1, False), "protocol-unreachable": (3, 2, False), "port-unreachable": (3, 3, False), "fragmentation-needed": (3, 4, False), "source-route-failed": (3, 5, False), # RFC-1112 Section 3.2.2.1 defines type 3, code 6-12 "network-unknown": (3, 6, False), "host-unknown": (3, 7, False), "network-prohibited": (3, 9, False), "host-prohibited": (3, 10, False), "TOS-network-unreachable": (3, 11, False), "TOS-host-unreachable": (3, 12, False), # RFC-1812 Section 5.2.7.1 defines type 3, code 13-15 "communication-prohibited": (3, 13, False), "host-precedence-violation": (3, 14, False), "precedence-cutoff": (3, 15, False), "source-quench": (4, 0, True), "network-redirect": (5, 0, False), "redirect": (5, 0, True), "host-redirect": (5, 1, False), "tos-host-redirect": (5, 1, False), "TOS-network-redirect": (5, 2, False), "tos-network-redirect": (5, 2, False), "TOS-host-redirect": (5, 3, False), "echo-request": (8, 0, True), "ping": (8, 0, True), "router-advertisement": (9, 0, True), "router-solicitation": (10, 0, True), "time-exceeded": (11, 0, True), "ttl-zero-during-transit": (11, 0, False), "ttl-zero-during-reassembly": (11, 1, False), "parameter-problem": (12, 0, True), "ip-header-bad": (12, 0, False), "required-option-missing": (12, 1, False), "timestamp-request": (13, 0, True), "timestamp-reply": (14, 0, True), "address-mask-request": (17, 0, False), "address-mask-reply": (18, 0, False), } ICMPV6_TYPES = { # "type": (type, code, backend omit code) "destination-unreachable": (1, 0, True), "no-route": (1, 0, False), "communication-prohibited": (1, 1, False), "beyond-scope": (1, 2, False), "address-unreachable": (1, 3, False), "port-unreachable": (1, 4, False), "failed-policy": (1, 5, False), "reject-route": (1, 6, False), "packet-too-big": (2, 0, True), "time-exceeded": (3, 0, True), "ttl-zero-during-transit": (3, 0, False), "ttl-zero-during-reassembly": (3, 1, False), "parameter-problem": (4, 0, True), "bad-header": (4, 0, False), "unknown-header-type": (4, 1, False), "unknown-option": (4, 2, False), "echo-request": (128, 0, True), "ping": (128, 0, True), "echo-reply": (129, 0, True), "pong": (129, 0, True), "router-solicitation": (133, 0, True), "router-advertisement": (134, 0, True), "neighbour-solicitation": (135, 0, True), "neigbour-solicitation": (135, 0, True), "neighbour-advertisement": (136, 0, True), "neigbour-advertisement": (136, 0, True), "redirect": (137, 0, True), # MLD is RFC-2710 "mld-listener-query": (130, 0, True), "mld-listener-report": (131, 0, True), "mld-listener-done": (132, 0, True), # MLDv2 is RFC-9777 "mld2-listener-report": (143, 0, True), } def check_icmp_name(_name): if _name in ICMP_TYPES: return True return False def check_icmp_type_code(_type, _code): if (_type, _code) in ICMP_TYPES.values(): return True return False def check_icmpv6_name(_name): if _name in ICMP_TYPES: return True return False def check_icmpv6_type_code(_type, _code): if (_type, _code) in ICMPV6_TYPES.values(): return True return False PK!gfw.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2010-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # __all__ = [ "Firewall" ] import os.path import sys import copy import time import traceback from firewall import config from firewall import functions from firewall.core import ipXtables from firewall.core import ebtables from firewall.core import nftables from firewall.core import ipset from firewall.core import modules from firewall.core.fw_icmptype import FirewallIcmpType from firewall.core.fw_service import FirewallService from firewall.core.fw_zone import FirewallZone from firewall.core.fw_direct import FirewallDirect from firewall.core.fw_config import FirewallConfig from firewall.core.fw_policies import FirewallPolicies from firewall.core.fw_ipset import FirewallIPSet from firewall.core.fw_transaction import FirewallTransaction from firewall.core.fw_helper import FirewallHelper from firewall.core.fw_policy import FirewallPolicy from firewall.core.fw_nm import nm_get_bus_name, nm_get_interfaces_in_zone from firewall.core.logger import log from firewall.core.io.firewalld_conf import firewalld_conf from firewall.core.io.direct import Direct from firewall.core.io.service import service_reader from firewall.core.io.icmptype import icmptype_reader from firewall.core.io.zone import zone_reader, Zone from firewall.core.io.ipset import ipset_reader from firewall.core.ipset import IPSET_TYPES from firewall.core.io.helper import helper_reader from firewall.core.io.policy import policy_reader from firewall import errors from firewall.errors import FirewallError ############################################################################ # # class Firewall # ############################################################################ class Firewall(object): def __init__(self, offline=False): self._firewalld_conf = firewalld_conf(config.FIREWALLD_CONF) self._offline = offline if self._offline: self.ip4tables_enabled = False self.ip6tables_enabled = False self.ebtables_enabled = False self.ipset_enabled = False self.ipset_supported_types = IPSET_TYPES self.nftables_enabled = False else: self.ip4tables_backend = ipXtables.ip4tables(self) self.ip4tables_enabled = True self.ipv4_supported_icmp_types = [ ] self.ip6tables_backend = ipXtables.ip6tables(self) self.ip6tables_enabled = True self.ipv6_supported_icmp_types = [ ] self.ebtables_backend = ebtables.ebtables() self.ebtables_enabled = True self.ipset_backend = ipset.ipset() self.ipset_enabled = True self.ipset_supported_types = [ ] self.nftables_backend = nftables.nftables(self) self.nftables_enabled = True self.modules_backend = modules.modules() self.icmptype = FirewallIcmpType(self) self.service = FirewallService(self) self.zone = FirewallZone(self) self.direct = FirewallDirect(self) self.config = FirewallConfig(self) self.policies = FirewallPolicies() self.ipset = FirewallIPSet(self) self.helper = FirewallHelper(self) self.policy = FirewallPolicy(self) self.__init_vars() def __repr__(self): return '%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)' % \ (self.__class__, self.ip4tables_enabled, self.ip6tables_enabled, self.ebtables_enabled, self._state, self._panic, self._default_zone, self._module_refcount, self._marks, self.cleanup_on_exit, self.cleanup_modules_on_exit, self.ipv6_rpfilter_enabled, self.ipset_enabled, self._individual_calls, self._log_denied) def __init_vars(self): self._state = "INIT" self._panic = False self._default_zone = "" self._module_refcount = { } self._marks = [ ] # fallback settings will be overloaded by firewalld.conf self.cleanup_on_exit = config.FALLBACK_CLEANUP_ON_EXIT self.cleanup_modules_on_exit = config.FALLBACK_CLEANUP_MODULES_ON_EXIT self.ipv6_rpfilter_enabled = config.FALLBACK_IPV6_RPFILTER self._individual_calls = config.FALLBACK_INDIVIDUAL_CALLS self._log_denied = config.FALLBACK_LOG_DENIED self._firewall_backend = config.FALLBACK_FIREWALL_BACKEND self._flush_all_on_reload = config.FALLBACK_FLUSH_ALL_ON_RELOAD self._rfc3964_ipv4 = config.FALLBACK_RFC3964_IPV4 self._allow_zone_drifting = config.FALLBACK_ALLOW_ZONE_DRIFTING def _check_tables(self): # check if iptables, ip6tables and ebtables are usable, else disable if self.ip4tables_enabled and \ "filter" not in self.ip4tables_backend.get_available_tables(): log.info1("iptables is not usable.") self.ip4tables_enabled = False if self.ip6tables_enabled and \ "filter" not in self.ip6tables_backend.get_available_tables(): log.info1("ip6tables is not usable.") self.ip6tables_enabled = False if self.ebtables_enabled and \ "filter" not in self.ebtables_backend.get_available_tables(): log.info1("ebtables is not usable.") self.ebtables_enabled = False # is there at least support for ipv4 or ipv6 if not self.ip4tables_enabled and not self.ip6tables_enabled \ and not self.nftables_enabled: log.fatal("No IPv4 and IPv6 firewall.") sys.exit(1) def _start_check(self): try: self.ipset_backend.set_list() except ValueError: log.warning("ipset not usable, disabling ipset usage in firewall.") # ipset is not usable, no supported types self.ipset_enabled = False self.ipset_supported_types = [ ] else: # ipset is usable, get all supported types self.ipset_supported_types = self.ipset_backend.set_supported_types() self.ip4tables_backend.fill_exists() if not self.ip4tables_backend.restore_command_exists: if self.ip4tables_backend.command_exists: log.warning("iptables-restore is missing, using " "individual calls for IPv4 firewall.") else: log.warning("iptables-restore and iptables are missing, " "disabling IPv4 firewall.") self.ip4tables_enabled = False if self.nftables_enabled: self.ipv4_supported_icmp_types = self.nftables_backend.supported_icmp_types("ipv4") else: if self.ip4tables_enabled: self.ipv4_supported_icmp_types = self.ip4tables_backend.supported_icmp_types() else: self.ipv4_supported_icmp_types = [ ] self.ip6tables_backend.fill_exists() if not self.ip6tables_backend.restore_command_exists: if self.ip6tables_backend.command_exists: log.warning("ip6tables-restore is missing, using " "individual calls for IPv6 firewall.") else: log.warning("ip6tables-restore and ip6tables are missing, " "disabling IPv6 firewall.") self.ip6tables_enabled = False if self.nftables_enabled: self.ipv6_supported_icmp_types = self.nftables_backend.supported_icmp_types("ipv6") else: if self.ip6tables_enabled: self.ipv6_supported_icmp_types = self.ip6tables_backend.supported_icmp_types() else: self.ipv6_supported_icmp_types = [ ] self.ebtables_backend.fill_exists() if not self.ebtables_backend.restore_command_exists: if self.ebtables_backend.command_exists: log.warning("ebtables-restore is missing, using " "individual calls for bridge firewall.") else: log.warning("ebtables-restore and ebtables are missing, " "disabling bridge firewall.") self.ebtables_enabled = False if self.ebtables_enabled and not self._individual_calls and \ not self.ebtables_backend.restore_noflush_option: log.debug1("ebtables-restore is not supporting the --noflush " "option, will therefore not be used") def _start(self, reload=False, complete_reload=False): # initialize firewall default_zone = config.FALLBACK_ZONE # load firewalld config log.debug1("Loading firewalld config file '%s'", config.FIREWALLD_CONF) try: self._firewalld_conf.read() except Exception as msg: log.warning(msg) log.warning("Using fallback firewalld configuration settings.") else: if self._firewalld_conf.get("DefaultZone"): default_zone = self._firewalld_conf.get("DefaultZone") if self._firewalld_conf.get("CleanupOnExit"): value = self._firewalld_conf.get("CleanupOnExit") if value is not None and value.lower() in [ "no", "false" ]: self.cleanup_on_exit = False log.debug1("CleanupOnExit is set to '%s'", self.cleanup_on_exit) if self._firewalld_conf.get("CleanupModulesOnExit"): value = self._firewalld_conf.get("CleanupModulesOnExit") if value is not None and value.lower() in [ "yes", "true" ]: self.cleanup_modules_on_exit = True if value is not None and value.lower() in [ "no", "false" ]: self.cleanup_modules_on_exit = False log.debug1("CleanupModulesOnExit is set to '%s'", self.cleanup_modules_on_exit) if self._firewalld_conf.get("Lockdown"): value = self._firewalld_conf.get("Lockdown") if value is not None and value.lower() in [ "yes", "true" ]: log.debug1("Lockdown is enabled") try: self.policies.enable_lockdown() except FirewallError: # already enabled, this is probably reload pass if self._firewalld_conf.get("IPv6_rpfilter"): value = self._firewalld_conf.get("IPv6_rpfilter") if value is not None: if value.lower() in [ "no", "false" ]: self.ipv6_rpfilter_enabled = False if value.lower() in [ "yes", "true" ]: self.ipv6_rpfilter_enabled = True if self.ipv6_rpfilter_enabled: log.debug1("IPv6 rpfilter is enabled") else: log.debug1("IPV6 rpfilter is disabled") if self._firewalld_conf.get("IndividualCalls"): value = self._firewalld_conf.get("IndividualCalls") if value is not None and value.lower() in [ "yes", "true" ]: log.debug1("IndividualCalls is enabled") self._individual_calls = True if self._firewalld_conf.get("LogDenied"): value = self._firewalld_conf.get("LogDenied") if value is None or value.lower() == "no": self._log_denied = "off" else: self._log_denied = value.lower() log.debug1("LogDenied is set to '%s'", self._log_denied) if self._firewalld_conf.get("FirewallBackend"): self._firewall_backend = self._firewalld_conf.get("FirewallBackend") log.debug1("FirewallBackend is set to '%s'", self._firewall_backend) if self._firewalld_conf.get("FlushAllOnReload"): value = self._firewalld_conf.get("FlushAllOnReload") if value.lower() in [ "no", "false" ]: self._flush_all_on_reload = False else: self._flush_all_on_reload = True log.debug1("FlushAllOnReload is set to '%s'", self._flush_all_on_reload) if self._firewalld_conf.get("RFC3964_IPv4"): value = self._firewalld_conf.get("RFC3964_IPv4") if value.lower() in [ "no", "false" ]: self._rfc3964_ipv4 = False else: self._rfc3964_ipv4 = True log.debug1("RFC3964_IPv4 is set to '%s'", self._rfc3964_ipv4) if self._firewalld_conf.get("AllowZoneDrifting"): value = self._firewalld_conf.get("AllowZoneDrifting") if value.lower() in [ "no", "false" ]: self._allow_zone_drifting = False else: self._allow_zone_drifting = True if not self._offline: log.warning("AllowZoneDrifting is enabled. This is considered " "an insecure configuration option. It will be " "removed in a future release. Please consider " "disabling it now.") log.debug1("AllowZoneDrifting is set to '%s'", self._allow_zone_drifting) self.config.set_firewalld_conf(copy.deepcopy(self._firewalld_conf)) self._select_firewall_backend(self._firewall_backend) if not self._offline: self._start_check() # load lockdown whitelist log.debug1("Loading lockdown whitelist") try: self.policies.lockdown_whitelist.read() except Exception as msg: if self.policies.query_lockdown(): log.error("Failed to load lockdown whitelist '%s': %s", self.policies.lockdown_whitelist.filename, msg) else: log.debug1("Failed to load lockdown whitelist '%s': %s", self.policies.lockdown_whitelist.filename, msg) # copy policies to config interface self.config.set_policies(copy.deepcopy(self.policies)) # load ipset files self._loader(config.FIREWALLD_IPSETS, "ipset") self._loader(config.ETC_FIREWALLD_IPSETS, "ipset") # load icmptype files self._loader(config.FIREWALLD_ICMPTYPES, "icmptype") self._loader(config.ETC_FIREWALLD_ICMPTYPES, "icmptype") if len(self.icmptype.get_icmptypes()) == 0: log.error("No icmptypes found.") # load helper files self._loader(config.FIREWALLD_HELPERS, "helper") self._loader(config.ETC_FIREWALLD_HELPERS, "helper") # load service files self._loader(config.FIREWALLD_SERVICES, "service") self._loader(config.ETC_FIREWALLD_SERVICES, "service") if len(self.service.get_services()) == 0: log.error("No services found.") # load zone files self._loader(config.FIREWALLD_ZONES, "zone") self._loader(config.ETC_FIREWALLD_ZONES, "zone") if len(self.zone.get_zones()) == 0: log.fatal("No zones found.") sys.exit(1) # load policy files self._loader(config.FIREWALLD_POLICIES, "policy") self._loader(config.ETC_FIREWALLD_POLICIES, "policy") # check minimum required zones error = False for z in [ "block", "drop", "trusted" ]: if z not in self.zone.get_zones(): log.fatal("Zone '%s' is not available.", z) error = True if error: sys.exit(1) # check if default_zone is a valid zone if default_zone not in self.zone.get_zones(): if "public" in self.zone.get_zones(): zone = "public" elif "external" in self.zone.get_zones(): zone = "external" else: zone = "block" # block is a base zone, therefore it has to exist log.error("Default zone '%s' is not valid. Using '%s'.", default_zone, zone) default_zone = zone else: log.debug1("Using default zone '%s'", default_zone) # load direct rules obj = Direct(config.FIREWALLD_DIRECT) if os.path.exists(config.FIREWALLD_DIRECT): log.debug1("Loading direct rules file '%s'" % \ config.FIREWALLD_DIRECT) try: obj.read() except Exception as msg: log.error("Failed to load direct rules file '%s': %s", config.FIREWALLD_DIRECT, msg) self.direct.set_permanent_config(obj) self.config.set_direct(copy.deepcopy(obj)) self._default_zone = self.check_zone(default_zone) if self._offline: return # check if needed tables are there self._check_tables() if log.getDebugLogLevel() > 0: # get time before flushing and applying tm1 = time.time() # Start transaction transaction = FirewallTransaction(self) # flush rules if not reload: self.flush(use_transaction=transaction) # If modules need to be unloaded in complete reload or if there are # ipsets to get applied, limit the transaction to flush. # # Future optimization for the ipset case in reload: The transaction # only needs to be split here if there are conflicting ipset types in # exsting ipsets and the configuration in firewalld. if (reload and complete_reload) or \ (self.ipset_enabled and self.ipset.has_ipsets()): transaction.execute(True) transaction.clear() # complete reload: unload modules also if reload and complete_reload: log.debug1("Unloading firewall modules") self.modules_backend.unload_firewall_modules() self.apply_default_tables(use_transaction=transaction) transaction.execute(True) transaction.clear() # apply settings for loaded ipsets while reloading here if self.ipset_enabled and self.ipset.has_ipsets(): log.debug1("Applying ipsets") self.ipset.apply_ipsets() # Start or continue with transaction # apply default rules log.debug1("Applying default rule set") self.apply_default_rules(use_transaction=transaction) # apply settings for loaded zones log.debug1("Applying used zones") self.zone.apply_zones(use_transaction=transaction) self.zone.change_default_zone(None, self._default_zone, use_transaction=transaction) # apply policies log.debug1("Applying used policies") self.policy.apply_policies(use_transaction=transaction) # Execute transaction transaction.execute(True) # Start new transaction for direct rules transaction.clear() # apply direct chains, rules and passthrough rules if self.direct.has_configuration(): log.debug1("Applying direct chains rules and passthrough rules") self.direct.apply_direct(transaction) # since direct rules are easy to make syntax errors lets highlight # the cause if the transaction fails. try: transaction.execute(True) transaction.clear() except FirewallError as e: raise FirewallError(e.code, "Direct: %s" % (e.msg if e.msg else "")) except Exception: raise del transaction if log.getDebugLogLevel() > 1: # get time after flushing and applying tm2 = time.time() log.debug2("Flushing and applying took %f seconds" % (tm2 - tm1)) def start(self): try: self._start() except Exception: self._state = "FAILED" self.set_policy("ACCEPT") raise else: self._state = "RUNNING" self.set_policy("ACCEPT") def _loader(self, path, reader_type, combine=False): # combine: several zone files are getting combined into one obj if not os.path.isdir(path): return if combine: if path.startswith(config.ETC_FIREWALLD) and reader_type == "zone": combined_zone = Zone() combined_zone.name = os.path.basename(path) combined_zone.check_name(combined_zone.name) combined_zone.path = path combined_zone.default = False else: combine = False for filename in sorted(os.listdir(path)): if not filename.endswith(".xml"): if path.startswith(config.ETC_FIREWALLD) and \ reader_type == "zone" and \ os.path.isdir("%s/%s" % (path, filename)): self._loader("%s/%s" % (path, filename), reader_type, combine=True) continue name = "%s/%s" % (path, filename) log.debug1("Loading %s file '%s'", reader_type, name) try: if reader_type == "icmptype": obj = icmptype_reader(filename, path) if obj.name in self.icmptype.get_icmptypes(): orig_obj = self.icmptype.get_icmptype(obj.name) log.debug1(" Overloads %s '%s' ('%s/%s')", reader_type, orig_obj.name, orig_obj.path, orig_obj.filename) self.icmptype.remove_icmptype(orig_obj.name) elif obj.path.startswith(config.ETC_FIREWALLD): obj.default = True try: self.icmptype.add_icmptype(obj) except FirewallError as error: log.info1("%s: %s, ignoring for run-time." % \ (obj.name, str(error))) # add a deep copy to the configuration interface self.config.add_icmptype(copy.deepcopy(obj)) elif reader_type == "service": obj = service_reader(filename, path) if obj.name in self.service.get_services(): orig_obj = self.service.get_service(obj.name) log.debug1(" Overloads %s '%s' ('%s/%s')", reader_type, orig_obj.name, orig_obj.path, orig_obj.filename) self.service.remove_service(orig_obj.name) elif obj.path.startswith(config.ETC_FIREWALLD): obj.default = True self.service.add_service(obj) # add a deep copy to the configuration interface self.config.add_service(copy.deepcopy(obj)) elif reader_type == "zone": obj = zone_reader(filename, path, no_check_name=combine) if combine: # Change name for permanent configuration obj.name = "%s/%s" % ( os.path.basename(path), os.path.basename(filename)[0:-4]) obj.check_name(obj.name) # Copy object before combine config_obj = copy.deepcopy(obj) if obj.name in self.zone.get_zones(): orig_obj = self.zone.get_zone(obj.name) self.zone.remove_zone(orig_obj.name) if orig_obj.combined: log.debug1(" Combining %s '%s' ('%s/%s')", reader_type, obj.name, path, filename) obj.combine(orig_obj) else: log.debug1(" Overloads %s '%s' ('%s/%s')", reader_type, orig_obj.name, orig_obj.path, orig_obj.filename) elif obj.path.startswith(config.ETC_FIREWALLD): obj.default = True config_obj.default = True self.config.add_zone(config_obj) if combine: log.debug1(" Combining %s '%s' ('%s/%s')", reader_type, combined_zone.name, path, filename) combined_zone.combine(obj) else: self.zone.add_zone(obj) elif reader_type == "ipset": obj = ipset_reader(filename, path) if obj.name in self.ipset.get_ipsets(): orig_obj = self.ipset.get_ipset(obj.name) log.debug1(" Overloads %s '%s' ('%s/%s')", reader_type, orig_obj.name, orig_obj.path, orig_obj.filename) self.ipset.remove_ipset(orig_obj.name) elif obj.path.startswith(config.ETC_FIREWALLD): obj.default = True try: self.ipset.add_ipset(obj) except FirewallError as error: log.warning("%s: %s, ignoring for run-time." % \ (obj.name, str(error))) # add a deep copy to the configuration interface self.config.add_ipset(copy.deepcopy(obj)) elif reader_type == "helper": obj = helper_reader(filename, path) if obj.name in self.helper.get_helpers(): orig_obj = self.helper.get_helper(obj.name) log.debug1(" Overloads %s '%s' ('%s/%s')", reader_type, orig_obj.name, orig_obj.path, orig_obj.filename) self.helper.remove_helper(orig_obj.name) elif obj.path.startswith(config.ETC_FIREWALLD): obj.default = True self.helper.add_helper(obj) # add a deep copy to the configuration interface self.config.add_helper(copy.deepcopy(obj)) elif reader_type == "policy": obj = policy_reader(filename, path) if obj.name in self.policy.get_policies(): orig_obj = self.policy.get_policy(obj.name) log.debug1(" Overloads %s '%s' ('%s/%s')", reader_type, orig_obj.name, orig_obj.path, orig_obj.filename) self.policy.remove_policy(orig_obj.name) elif obj.path.startswith(config.ETC_FIREWALLD): obj.default = True self.policy.add_policy(obj) # add a deep copy to the configuration interface self.config.add_policy_object(copy.deepcopy(obj)) else: log.fatal("Unknown reader type %s", reader_type) except FirewallError as msg: log.error("Failed to load %s file '%s': %s", reader_type, name, msg) except Exception: log.error("Failed to load %s file '%s':", reader_type, name) log.exception() if combine and combined_zone.combined: if combined_zone.name in self.zone.get_zones(): orig_obj = self.zone.get_zone(combined_zone.name) log.debug1(" Overloading and deactivating %s '%s' ('%s/%s')", reader_type, orig_obj.name, orig_obj.path, orig_obj.filename) try: self.zone.remove_zone(combined_zone.name) except Exception: pass self.config.forget_zone(combined_zone.name) self.zone.add_zone(combined_zone) def cleanup(self): self.icmptype.cleanup() self.service.cleanup() self.zone.cleanup() self.ipset.cleanup() self.helper.cleanup() self.config.cleanup() self.direct.cleanup() self.policies.cleanup() self.policy.cleanup() self._firewalld_conf.cleanup() self.__init_vars() def stop(self): if not self._offline: if self.cleanup_on_exit: self.flush() self.ipset.flush() self.set_policy("ACCEPT") if self.cleanup_modules_on_exit: log.debug1('Unloading firewall kernel modules') self.modules_backend.unload_firewall_modules() self.cleanup() # handle modules def handle_modules(self, _modules, enable): num_failed = 0 error_msgs = "" for i,module in enumerate(_modules): if enable: (status, msg) = self.modules_backend.load_module(module) else: if self._module_refcount[module] > 1: status = 0 # module referenced more then one, do not unload else: (status, msg) = self.modules_backend.unload_module(module) if status != 0: num_failed += 1 error_msgs += msg continue if enable: self._module_refcount.setdefault(module, 0) self._module_refcount[module] += 1 else: if module in self._module_refcount: self._module_refcount[module] -= 1 if self._module_refcount[module] == 0: del self._module_refcount[module] return (num_failed, error_msgs) def _select_firewall_backend(self, backend): if backend != "nftables": self.nftables_enabled = False # even if using nftables, the other backends are enabled for use with # the direct interface. nftables is used for the firewalld primitives. def get_backend_by_name(self, name): for backend in self.all_backends(): if backend.name == name: return backend raise FirewallError(errors.UNKNOWN_ERROR, "'%s' backend does not exist" % name) def get_backend_by_ipv(self, ipv): if self.nftables_enabled: return self.nftables_backend if ipv == "ipv4" and self.ip4tables_enabled: return self.ip4tables_backend elif ipv == "ipv6" and self.ip6tables_enabled: return self.ip6tables_backend elif ipv == "eb" and self.ebtables_enabled: return self.ebtables_backend raise FirewallError(errors.INVALID_IPV, "'%s' is not a valid backend or is unavailable" % ipv) def get_direct_backend_by_ipv(self, ipv): if ipv == "ipv4" and self.ip4tables_enabled: return self.ip4tables_backend elif ipv == "ipv6" and self.ip6tables_enabled: return self.ip6tables_backend elif ipv == "eb" and self.ebtables_enabled: return self.ebtables_backend raise FirewallError(errors.INVALID_IPV, "'%s' is not a valid backend or is unavailable" % ipv) def is_backend_enabled(self, name): if name == "ip4tables": return self.ip4tables_enabled elif name == "ip6tables": return self.ip6tables_enabled elif name == "ebtables": return self.ebtables_enabled elif name == "nftables": return self.nftables_enabled return False def is_ipv_enabled(self, ipv): if self.nftables_enabled: return True if ipv == "ipv4": return self.ip4tables_enabled elif ipv == "ipv6": return self.ip6tables_enabled elif ipv == "eb": return self.ebtables_enabled return False def enabled_backends(self): backends = [] if self.nftables_enabled: backends.append(self.nftables_backend) else: if self.ip4tables_enabled: backends.append(self.ip4tables_backend) if self.ip6tables_enabled: backends.append(self.ip6tables_backend) if self.ebtables_enabled: backends.append(self.ebtables_backend) return backends def all_backends(self): backends = [] if self.ip4tables_enabled: backends.append(self.ip4tables_backend) if self.ip6tables_enabled: backends.append(self.ip6tables_backend) if self.ebtables_enabled: backends.append(self.ebtables_backend) if self.nftables_enabled: backends.append(self.nftables_backend) return backends def apply_default_tables(self, use_transaction=None): if use_transaction is None: transaction = FirewallTransaction(self) else: transaction = use_transaction for backend in self.enabled_backends(): transaction.add_rules(backend, backend.build_default_tables()) if use_transaction is None: transaction.execute(True) def apply_default_rules(self, use_transaction=None): if use_transaction is None: transaction = FirewallTransaction(self) else: transaction = use_transaction for backend in self.enabled_backends(): rules = backend.build_default_rules(self._log_denied) transaction.add_rules(backend, rules) if self.is_ipv_enabled("ipv6"): ipv6_backend = self.get_backend_by_ipv("ipv6") if "raw" in ipv6_backend.get_available_tables(): if self.ipv6_rpfilter_enabled: rules = ipv6_backend.build_rpfilter_rules(self._log_denied) transaction.add_rules(ipv6_backend, rules) if self.is_ipv_enabled("ipv6") and self._rfc3964_ipv4: rules = ipv6_backend.build_rfc3964_ipv4_rules() transaction.add_rules(ipv6_backend, rules) if use_transaction is None: transaction.execute(True) def may_skip_flush_direct_backends(self): if self.nftables_enabled and not self.direct.has_runtime_configuration(): return True return False def flush_direct_backends(self, use_transaction=None): if use_transaction is None: transaction = FirewallTransaction(self) else: transaction = use_transaction for backend in self.all_backends(): if backend in self.enabled_backends(): continue rules = backend.build_flush_rules() transaction.add_rules(backend, rules) if use_transaction is None: transaction.execute(True) def flush(self, use_transaction=None): if use_transaction is None: transaction = FirewallTransaction(self) else: transaction = use_transaction log.debug1("Flushing rule set") if not self.may_skip_flush_direct_backends(): self.flush_direct_backends(use_transaction=transaction) for backend in self.enabled_backends(): rules = backend.build_flush_rules() transaction.add_rules(backend, rules) if use_transaction is None: transaction.execute(True) def set_policy(self, policy, use_transaction=None): if use_transaction is None: transaction = FirewallTransaction(self) else: transaction = use_transaction log.debug1("Setting policy to '%s'", policy) for backend in self.enabled_backends(): rules = backend.build_set_policy_rules(policy) transaction.add_rules(backend, rules) if use_transaction is None: transaction.execute(True) # rule function used in handle_ functions def rule(self, backend_name, rule): if not rule: return "" backend = self.get_backend_by_name(backend_name) if not backend: raise FirewallError(errors.INVALID_IPV, "'%s' is not a valid backend" % backend_name) if not self.is_backend_enabled(backend_name): return "" return backend.set_rule(rule, self._log_denied) def rules(self, backend_name, rules): _rules = list(filter(None, rules)) backend = self.get_backend_by_name(backend_name) if not backend: raise FirewallError(errors.INVALID_IPV, "'%s' is not a valid backend" % backend_name) if not self.is_backend_enabled(backend_name): return if self._individual_calls or \ not backend.restore_command_exists or \ (backend_name == "ebtables" and not self.ebtables_backend.restore_noflush_option): for i,rule in enumerate(_rules): try: backend.set_rule(rule, self._log_denied) except Exception as msg: log.debug1(traceback.format_exc()) log.error(msg) for rule in reversed(_rules[:i]): try: backend.set_rule(backend.reverse_rule(rule), self._log_denied) except Exception: # ignore errors here pass raise msg else: backend.set_rules(_rules, self._log_denied) # check functions def check_panic(self): if self._panic: raise FirewallError(errors.PANIC_MODE) def check_policy(self, policy): _policy = policy if _policy not in self.policy.get_policies(): raise FirewallError(errors.INVALID_POLICY, _policy) return _policy def check_zone(self, zone): _zone = zone if not _zone or _zone == "": _zone = self.get_default_zone() if _zone not in self.zone.get_zones(): raise FirewallError(errors.INVALID_ZONE, _zone) return _zone def check_interface(self, interface): if not functions.checkInterface(interface): raise FirewallError(errors.INVALID_INTERFACE, interface) def check_service(self, service): self.service.check_service(service) def check_port(self, port): if not functions.check_port(port): raise FirewallError(errors.INVALID_PORT, port) def check_tcpudp(self, protocol): if not protocol: raise FirewallError(errors.MISSING_PROTOCOL) if protocol not in [ "tcp", "udp", "sctp", "dccp" ]: raise FirewallError(errors.INVALID_PROTOCOL, "'%s' not in {'tcp'|'udp'|'sctp'|'dccp'}" % \ protocol) def check_ip(self, ip): if not functions.checkIP(ip): raise FirewallError(errors.INVALID_ADDR, ip) def check_address(self, ipv, source): if ipv == "ipv4": if not functions.checkIPnMask(source): raise FirewallError(errors.INVALID_ADDR, source) elif ipv == "ipv6": if not functions.checkIP6nMask(source): raise FirewallError(errors.INVALID_ADDR, source) else: raise FirewallError(errors.INVALID_IPV, "'%s' not in {'ipv4'|'ipv6'}") def check_icmptype(self, icmp): self.icmptype.check_icmptype(icmp) def check_timeout(self, timeout): if not isinstance(timeout, int): raise TypeError("%s is %s, expected int" % (timeout, type(timeout))) if int(timeout) < 0: raise FirewallError(errors.INVALID_VALUE, "timeout '%d' is not positive number" % timeout) # RELOAD def reload(self, stop=False): _panic = self._panic # must stash this. The value may change after _start() flush_all = self._flush_all_on_reload if not flush_all: # save zone interfaces _zone_interfaces = { } for zone in self.zone.get_zones(): _zone_interfaces[zone] = self.zone.get_settings(zone)["interfaces"] # save direct config _direct_config = self.direct.get_runtime_config() _old_dz = self.get_default_zone() _ipset_objs = [] for _name in self.ipset.get_ipsets(): _ipset_objs.append(self.ipset.get_ipset(_name)) if not _panic: self.set_policy("DROP") self.flush() self.cleanup() start_exception = None try: self._start(reload=True, complete_reload=stop) except Exception as e: # save the exception for later, but continue restoring interfaces, # etc. We'll re-raise it at the end. start_exception = e # destroy ipsets no longer in the permanent configuration if flush_all: for obj in _ipset_objs: if not self.ipset.query_ipset(obj.name): for backend in self.ipset.backends(): # nftables sets are part of the normal firewall ruleset. if backend.name == "nftables": continue backend.set_destroy(obj.name) if not flush_all: # handle interfaces in the default zone and move them to the new # default zone if it changed _new_dz = self.get_default_zone() if _new_dz != _old_dz: # if_new_dz has been introduced with the reload, we need to add it # https://github.com/firewalld/firewalld/issues/53 if _new_dz not in _zone_interfaces: _zone_interfaces[_new_dz] = { } # default zone changed. Move interfaces from old default zone to # the new one. for iface, settings in list(_zone_interfaces[_old_dz].items()): if settings["__default__"]: # move only those that were added to default zone # (not those that were added to specific zone same as # default) _zone_interfaces[_new_dz][iface] = \ _zone_interfaces[_old_dz][iface] del _zone_interfaces[_old_dz][iface] # add interfaces to zones again for zone in self.zone.get_zones(): if zone in _zone_interfaces: for interface_id in _zone_interfaces[zone]: self.zone.change_zone_of_interface(zone, interface_id, _zone_interfaces[zone][interface_id]["sender"]) del _zone_interfaces[zone] else: log.info1("New zone '%s'.", zone) if len(_zone_interfaces) > 0: for zone in list(_zone_interfaces.keys()): log.info1("Lost zone '%s', zone interfaces dropped.", zone) del _zone_interfaces[zone] del _zone_interfaces # restore runtime-only ipsets for obj in _ipset_objs: if self.ipset.query_ipset(obj.name): for entry in obj.entries: try: self.ipset.add_entry(obj.name, entry) except FirewallError as msg: if msg.code != errors.ALREADY_ENABLED: raise msg else: self.ipset.add_ipset(obj) self.ipset.apply_ipset(obj.name) # restore direct config self.direct.set_config(_direct_config) # Restore permanent interfaces from NetworkManager nm_bus_name = nm_get_bus_name() if nm_bus_name: for zone in self.zone.get_zones() + [""]: for interface in nm_get_interfaces_in_zone(zone): self.zone.change_zone_of_interface(zone, interface, sender=nm_bus_name) self._panic = _panic if not self._panic: self.set_policy("ACCEPT") if start_exception: self._state = "FAILED" raise start_exception else: self._state = "RUNNING" # STATE def get_state(self): return self._state # PANIC MODE def enable_panic_mode(self): if self._panic: raise FirewallError(errors.ALREADY_ENABLED, "panic mode already enabled") try: self.set_policy("PANIC") except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) self._panic = True def disable_panic_mode(self): if not self._panic: raise FirewallError(errors.NOT_ENABLED, "panic mode is not enabled") try: self.set_policy("ACCEPT") except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) self._panic = False def query_panic_mode(self): return self._panic # LOG DENIED def get_log_denied(self): return self._log_denied def set_log_denied(self, value): if value not in config.LOG_DENIED_VALUES: raise FirewallError(errors.INVALID_VALUE, "'%s', choose from '%s'" % \ (value, "','".join(config.LOG_DENIED_VALUES))) if value != self.get_log_denied(): self._log_denied = value self._firewalld_conf.set("LogDenied", value) self._firewalld_conf.write() else: raise FirewallError(errors.ALREADY_SET, value) # DEFAULT ZONE def get_default_zone(self): return self._default_zone def set_default_zone(self, zone): _zone = self.check_zone(zone) if _zone != self._default_zone: _old_dz = self._default_zone self._default_zone = _zone self._firewalld_conf.set("DefaultZone", _zone) self._firewalld_conf.write() # remove old default zone from ZONES and add new default zone self.zone.change_default_zone(_old_dz, _zone) # Move interfaces from old default zone to the new one. _old_dz_settings = self.zone.get_settings(_old_dz) for iface, settings in list(_old_dz_settings["interfaces"].items()): if settings["__default__"]: # move only those that were added to default zone # (not those that were added to specific zone same as default) self.zone.change_zone_of_interface("", iface) else: raise FirewallError(errors.ZONE_ALREADY_SET, _zone) def combine_runtime_with_permanent_settings(self, permanent, runtime): combined = permanent.copy() for key,value in runtime.items(): # omit empty entries if value or isinstance(value, bool): combined[key] = value # make sure to remove values that were in permanent, but no # longer in runtime. elif key in combined: del combined[key] return combined def get_added_and_removed_settings(self, old_settings, new_settings): add_settings = {} remove_settings = {} for key in (set(old_settings.keys()) | set(new_settings.keys())): if key in new_settings: if isinstance(new_settings[key], list): old = set(old_settings[key] if key in old_settings else []) add_settings[key] = list(set(new_settings[key]) - old) remove_settings[key] = list((old ^ set(new_settings[key])) & old) # check for bool or int because dbus.Boolean is a subclass of # int (because bool can't be subclassed). elif isinstance(new_settings[key], bool) or isinstance(new_settings[key], int): if not old_settings[key] and new_settings[key]: add_settings[key] = True elif old_settings[key] and not new_settings[key]: remove_settings[key] = False else: raise FirewallError(errors.INVALID_SETTING, "Unhandled setting type {} key {}".format(type(new_settings[key]), key)) return (add_settings, remove_settings) PK!vg=V=V fw_policy.pynu[# -*- coding: utf-8 -*- # # SPDX-License-Identifier: GPL-2.0-or-later import time import copy from firewall.core.logger import log from firewall.functions import portStr, checkIPnMask, checkIP6nMask, \ checkProtocol, enable_ip_forwarding, check_single_address, \ portInPortRange, get_nf_conntrack_short_name, coalescePortRange, breakPortRange from firewall.core.rich import Rich_Rule, Rich_Accept, \ Rich_Service, Rich_Port, Rich_Protocol, \ Rich_Masquerade, Rich_ForwardPort, Rich_SourcePort, Rich_IcmpBlock, \ Rich_IcmpType, Rich_Mark from firewall.core.fw_transaction import FirewallTransaction from firewall import errors from firewall.errors import FirewallError from firewall.fw_types import LastUpdatedOrderedDict from firewall.core.base import SOURCE_IPSET_TYPES class FirewallPolicy(object): def __init__(self, fw): self._fw = fw self._chains = { } self._policies = { } def __repr__(self): return '%s(%r, %r)' % (self.__class__, self._chains, self._policies) def cleanup(self): self._chains.clear() self._policies.clear() # transaction def new_transaction(self): return FirewallTransaction(self._fw) # policies def get_policies(self): return sorted(self._policies.keys()) def get_policies_not_derived_from_zone(self): policies = [] for p in self.get_policies(): p_obj = self.get_policy(p) if not p_obj.derived_from_zone: policies.append(p) return sorted(policies) def get_active_policies_not_derived_from_zone(self): active_policies = [] for policy in self.get_policies_not_derived_from_zone(): settings = self.get_settings(policy) if (set(settings["ingress_zones"]) & (set(self._fw.zone.get_active_zones()) | set(["HOST", "ANY"]))) and \ (set(settings["egress_zones"]) & (set(self._fw.zone.get_active_zones()) | set(["HOST", "ANY"]))): active_policies.append(policy) return active_policies def get_policy(self, policy): p = self._fw.check_policy(policy) return self._policies[p] def add_policy(self, obj): obj.settings = { x : LastUpdatedOrderedDict() for x in [ "services", "ports", "masquerade", "forward_ports", "source_ports", "icmp_blocks", "rules", "protocols", "icmp_block_inversion", "ingress_zones", "egress_zones" ] } self._policies[obj.name] = obj self.copy_permanent_to_runtime(obj.name) def remove_policy(self, policy): obj = self._policies[policy] if obj.applied: self.unapply_policy_settings(policy) obj.settings.clear() del self._policies[policy] def copy_permanent_to_runtime(self, policy): obj = self._policies[policy] if obj.applied: return for args in obj.ingress_zones: self.add_ingress_zone(policy, args, allow_apply=False) for args in obj.egress_zones: self.add_egress_zone(policy, args, allow_apply=False) for args in obj.icmp_blocks: self.add_icmp_block(policy, args) for args in obj.forward_ports: self.add_forward_port(policy, *args) for args in obj.services: self.add_service(policy, args) for args in obj.ports: try: self.add_port(policy, *args) except FirewallError as error: if error.code in [errors.ALREADY_ENABLED]: log.warning(error) else: raise error for args in obj.protocols: self.add_protocol(policy, args) for args in obj.source_ports: try: self.add_source_port(policy, *args) except FirewallError as error: if error.code in [errors.ALREADY_ENABLED]: log.warning(error) else: raise error for args in obj.rules: self.add_rule(policy, args) if obj.masquerade: self.add_masquerade(policy) def apply_policies(self, use_transaction=None): for policy in self.get_policies(): p_obj = self._policies[policy] if p_obj.derived_from_zone: continue if policy in self.get_active_policies_not_derived_from_zone(): log.debug1("Applying policy '%s'", policy) self.apply_policy_settings(policy, use_transaction=use_transaction) def set_policy_applied(self, policy, applied): obj = self._policies[policy] obj.applied = applied # settings # generate settings record with sender, timeout def __gen_settings(self, timeout, sender): ret = { "date": time.time(), "sender": sender, "timeout": timeout, } return ret def get_settings(self, policy): return self.get_policy(policy).settings def _policy_settings(self, enable, policy, use_transaction=None): _policy = self._fw.check_policy(policy) obj = self._policies[_policy] if (enable and obj.applied) or (not enable and not obj.applied): return if enable: obj.applied = True if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if enable: # build the base chain layout of the policy for (table, chain) in self._get_table_chains_for_policy_dispatch(policy) if not obj.derived_from_zone \ else self._get_table_chains_for_zone_dispatch(policy): self.gen_chain_rules(policy, True, table, chain, transaction) settings = self.get_settings(policy) if not obj.derived_from_zone: self._ingress_egress_zones(enable, _policy, transaction) for key in settings: for args in settings[key]: if key == "icmp_blocks": self._icmp_block(enable, _policy, args, transaction) elif key == "icmp_block_inversion": continue elif key == "forward_ports": self._forward_port(enable, _policy, transaction, *args) elif key == "services": self._service(enable, _policy, args, transaction) elif key == "ports": self._port(enable, _policy, args[0], args[1], transaction) elif key == "protocols": self._protocol(enable, _policy, args, transaction) elif key == "source_ports": self._source_port(enable, _policy, args[0], args[1], transaction) elif key == "masquerade": self._masquerade(enable, _policy, transaction) elif key == "rules": self.__rule(enable, _policy, Rich_Rule(rule_str=args), transaction) elif key == "ingress_zones": continue elif key == "egress_zones": continue else: log.warning("Policy '%s': Unknown setting '%s:%s', " "unable to apply", policy, key, args) if not enable: for (table, chain) in self._get_table_chains_for_policy_dispatch(policy) if not obj.derived_from_zone \ else self._get_table_chains_for_zone_dispatch(policy): self.gen_chain_rules(policy, False, table, chain, transaction) obj.applied = False if use_transaction is None: transaction.execute(enable) def apply_policy_settings(self, policy, use_transaction=None): self._policy_settings(True, policy, use_transaction=use_transaction) def unapply_policy_settings(self, policy, use_transaction=None): self._policy_settings(False, policy, use_transaction=use_transaction) def get_config_with_settings_dict(self, policy): """ :return: exported config updated with runtime settings """ permanent = self.get_policy(policy).export_config_dict() runtime = { "services": self.list_services(policy), "ports": self.list_ports(policy), "icmp_blocks": self.list_icmp_blocks(policy), "masquerade": self.query_masquerade(policy), "forward_ports": self.list_forward_ports(policy), "rich_rules": self.list_rules(policy), "protocols": self.list_protocols(policy), "source_ports": self.list_source_ports(policy), "ingress_zones": self.list_ingress_zones(policy), "egress_zones": self.list_egress_zones(policy), } return self._fw.combine_runtime_with_permanent_settings(permanent, runtime) def set_config_with_settings_dict(self, policy, settings, sender): # stupid wrappers to convert rich rule string to rich rule object from firewall.core.rich import Rich_Rule def add_rule_wrapper(policy, rule_str, timeout=0, sender=None): self.add_rule(policy, Rich_Rule(rule_str=rule_str), timeout=0, sender=sender) def remove_rule_wrapper(policy, rule_str): self.remove_rule(policy, Rich_Rule(rule_str=rule_str)) setting_to_fn = { "services": (self.add_service, self.remove_service), "ports": (self.add_port, self.remove_port), "icmp_blocks": (self.add_icmp_block, self.remove_icmp_block), "masquerade": (self.add_masquerade, self.remove_masquerade), "forward_ports": (self.add_forward_port, self.remove_forward_port), "rich_rules": (add_rule_wrapper, remove_rule_wrapper), "protocols": (self.add_protocol, self.remove_protocol), "source_ports": (self.add_source_port, self.remove_source_port), "ingress_zones": (self.add_ingress_zone, self.remove_ingress_zone), "egress_zones": (self.add_egress_zone, self.remove_egress_zone), } old_settings = self.get_config_with_settings_dict(policy) (add_settings, remove_settings) = self._fw.get_added_and_removed_settings(old_settings, settings) for key in remove_settings: if isinstance(remove_settings[key], list): for args in remove_settings[key]: if isinstance(args, tuple): setting_to_fn[key][1](policy, *args) else: setting_to_fn[key][1](policy, args) else: # bool setting_to_fn[key][1](policy) for key in add_settings: if isinstance(add_settings[key], list): for args in add_settings[key]: if isinstance(args, tuple): setting_to_fn[key][0](policy, *args, timeout=0, sender=sender) else: setting_to_fn[key][0](policy, args, timeout=0, sender=sender) else: # bool setting_to_fn[key][0](policy, timeout=0, sender=sender) # ingress zones def check_ingress_zone(self, zone): if not zone: raise FirewallError(errors.INVALID_ZONE) if zone not in ["HOST", "ANY"]: self._fw.check_zone(zone) def __ingress_zone_id(self, zone): self.check_ingress_zone(zone) return zone def add_ingress_zone(self, policy, zone, timeout=0, sender=None, use_transaction=None, allow_apply=True): _policy = self._fw.check_policy(policy) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._policies[_policy] zone_id = self.__ingress_zone_id(zone) if zone_id in _obj.settings["ingress_zones"]: raise FirewallError(errors.ALREADY_ENABLED, "'%s' already in '%s'" % (zone, _policy)) if "ANY" in _obj.settings["ingress_zones"] or "HOST" in _obj.settings["ingress_zones"] or \ zone in ["ANY", "HOST"] and _obj.settings["ingress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'ingress-zones' may only contain one of: many regular zones, ANY, or HOST") if zone == "HOST" and "HOST" in _obj.settings["egress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'HOST' can only appear in either ingress or egress zones, but not both") if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if allow_apply: if _obj.applied: self._ingress_egress_zones(False, _policy, transaction) # register early so backends can access updated zone list self.__register_ingress_zone(_obj, zone_id, timeout, sender) transaction.add_fail(self.__unregister_ingress_zone, _obj, zone_id) if not _obj.applied: if _policy in self.get_active_policies_not_derived_from_zone(): self.apply_policy_settings(_policy, use_transaction=transaction) transaction.add_fail(self.set_policy_applied, _policy, False) else: self._ingress_egress_zones(True, _policy, transaction) else: self.__register_ingress_zone(_obj, zone_id, timeout, sender) transaction.add_fail(self.__unregister_ingress_zone, _obj, zone_id) if use_transaction is None: transaction.execute(True) def __register_ingress_zone(self, _obj, zone_id, timeout, sender): _obj.settings["ingress_zones"][zone_id] = self.__gen_settings(timeout, sender) def remove_ingress_zone(self, policy, zone, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] zone_id = self.__ingress_zone_id(zone) if zone_id not in _obj.settings["ingress_zones"]: raise FirewallError(errors.NOT_ENABLED, "'%s' not in '%s'" % (zone, _policy)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: if len(_obj.settings["ingress_zones"]) == 1: self.unapply_policy_settings(_policy, transaction) else: self._ingress_egress_zones(False, _policy, transaction) # unregister early so backends have updated zone list self.__unregister_ingress_zone(_obj, zone_id) transaction.add_fail(self.__register_ingress_zone, _obj, zone_id, None, None) if _policy in self.get_active_policies_not_derived_from_zone(): self._ingress_egress_zones(True, _policy, transaction) else: transaction.add_post(self.__unregister_ingress_zone, _obj, zone_id) if use_transaction is None: transaction.execute(True) return _policy def __unregister_ingress_zone(self, _obj, zone_id): if zone_id in _obj.settings["ingress_zones"]: del _obj.settings["ingress_zones"][zone_id] def query_ingress_zone(self, policy, zone): return self.__ingress_zone_id(zone) in self.get_settings(policy)["ingress_zones"] def list_ingress_zones(self, policy): return list(self.get_settings(policy)["ingress_zones"].keys()) # egress zones def check_egress_zone(self, zone): if not zone: raise FirewallError(errors.INVALID_ZONE) if zone not in ["HOST", "ANY"]: self._fw.check_zone(zone) def __egress_zone_id(self, zone): self.check_egress_zone(zone) return zone def add_egress_zone(self, policy, zone, timeout=0, sender=None, use_transaction=None, allow_apply=True): _policy = self._fw.check_policy(policy) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._policies[_policy] zone_id = self.__egress_zone_id(zone) if zone_id in _obj.settings["egress_zones"]: raise FirewallError(errors.ALREADY_ENABLED, "'%s' already in '%s'" % (zone, _policy)) if "ANY" in _obj.settings["egress_zones"] or "HOST" in _obj.settings["egress_zones"] or \ zone in ["ANY", "HOST"] and _obj.settings["egress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'egress-zones' may only contain one of: many regular zones, ANY, or HOST") if zone == "HOST" and "HOST" in _obj.settings["ingress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'HOST' can only appear in either ingress or egress zones, but not both") if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if allow_apply: if _obj.applied: self._ingress_egress_zones(False, _policy, transaction) # register early so backends can access updated zone list self.__register_egress_zone(_obj, zone_id, timeout, sender) transaction.add_fail(self.__unregister_egress_zone, _obj, zone_id) if not _obj.applied: if _policy in self.get_active_policies_not_derived_from_zone(): self.apply_policy_settings(_policy, use_transaction=transaction) transaction.add_fail(self.set_policy_applied, _policy, False) else: self._ingress_egress_zones(True, _policy, transaction) else: self.__register_egress_zone(_obj, zone_id, timeout, sender) transaction.add_fail(self.__unregister_egress_zone, _obj, zone_id) if use_transaction is None: transaction.execute(True) def __register_egress_zone(self, _obj, zone_id, timeout, sender): _obj.settings["egress_zones"][zone_id] = self.__gen_settings(timeout, sender) def remove_egress_zone(self, policy, zone, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] zone_id = self.__egress_zone_id(zone) if zone_id not in _obj.settings["egress_zones"]: raise FirewallError(errors.NOT_ENABLED, "'%s' not in '%s'" % (zone, _policy)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: if len(_obj.settings["egress_zones"]) == 1: self.unapply_policy_settings(_policy, transaction) else: self._ingress_egress_zones(False, _policy, transaction) # unregister early so backends have updated zone list self.__unregister_egress_zone(_obj, zone_id) transaction.add_fail(self.__register_egress_zone, _obj, zone_id, None, None) if _policy in self.get_active_policies_not_derived_from_zone(): self._ingress_egress_zones(True, _policy, transaction) else: transaction.add_post(self.__unregister_egress_zone, _obj, zone_id) if use_transaction is None: transaction.execute(True) return _policy def __unregister_egress_zone(self, _obj, zone_id): if zone_id in _obj.settings["egress_zones"]: del _obj.settings["egress_zones"][zone_id] def query_egress_zone(self, policy, zone): return self.__egress_zone_id(zone) in self.get_settings(policy)["egress_zones"] def list_egress_zones(self, policy): return list(self.get_settings(policy)["egress_zones"].keys()) # RICH LANGUAGE def check_rule(self, rule): rule.check() def __rule_id(self, rule): self.check_rule(rule) return str(rule) def _rule_source_ipv(self, source): if not source: return None if source.addr: if checkIPnMask(source.addr): return "ipv4" elif checkIP6nMask(source.addr): return "ipv6" elif hasattr(source, "mac") and source.mac: return "" elif hasattr(source, "ipset") and source.ipset: self._check_ipset_type_for_source(source.ipset) self._check_ipset_applied(source.ipset) return self._ipset_family(source.ipset) return None def __rule(self, enable, policy, rule, transaction): self._rule_prepare(enable, policy, rule, transaction) def add_rule(self, policy, rule, timeout=0, sender=None, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._policies[_policy] rule_id = self.__rule_id(rule) if rule_id in _obj.settings["rules"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.ALREADY_ENABLED, "'%s' already in '%s'" % (rule, _name)) if not _obj.derived_from_zone: if rule.element and isinstance(rule.element, Rich_Masquerade): if "HOST" in _obj.settings["egress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'masquerade' is invalid for egress zone 'HOST'") if "HOST" in _obj.settings["ingress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'masquerade' is invalid for ingress zone 'HOST'") for zone in _obj.settings["ingress_zones"]: if zone == "ANY": continue if self._fw.zone.list_interfaces(zone): raise FirewallError(errors.INVALID_ZONE, "'masquerade' cannot be used in a policy if an ingress zone has assigned interfaces") if rule.element and isinstance(rule.element, Rich_ForwardPort): if "HOST" in _obj.settings["egress_zones"]: if rule.element.to_address: raise FirewallError(errors.INVALID_FORWARD, "A 'forward-port' with 'to-addr' is invalid for egress zone 'HOST'") elif _obj.settings["egress_zones"]: if not rule.element.to_address: raise FirewallError(errors.INVALID_FORWARD, "'forward-port' requires 'to-addr' if egress zone is 'ANY' or a zone") for zone in _obj.settings["egress_zones"]: if zone == "ANY": continue if self._fw.zone.list_interfaces(zone): raise FirewallError(errors.INVALID_ZONE, "'forward-port' cannot be used in a policy if an egress zone has assigned interfaces") if rule.action and isinstance(rule.action, Rich_Mark): for zone in _obj.settings["egress_zones"]: if zone in ["ANY", "HOST"]: continue if self._fw.zone.list_interfaces(zone): raise FirewallError(errors.INVALID_ZONE, "'mark' action cannot be used in a policy if an egress zone has assigned interfaces") if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self.__rule(True, _policy, rule, transaction) self.__register_rule(_obj, rule_id, timeout, sender) transaction.add_fail(self.__unregister_rule, _obj, rule_id) if use_transaction is None: transaction.execute(True) return _policy def __register_rule(self, _obj, rule_id, timeout, sender): _obj.settings["rules"][rule_id] = self.__gen_settings( timeout, sender) def remove_rule(self, policy, rule, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] rule_id = self.__rule_id(rule) if rule_id not in _obj.settings["rules"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.NOT_ENABLED, "'%s' not in '%s'" % (rule, _name)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self.__rule(False, _policy, rule, transaction) transaction.add_post(self.__unregister_rule, _obj, rule_id) if use_transaction is None: transaction.execute(True) return _policy def __unregister_rule(self, _obj, rule_id): if rule_id in _obj.settings["rules"]: del _obj.settings["rules"][rule_id] def query_rule(self, policy, rule): return self.__rule_id(rule) in self.get_settings(policy)["rules"] def list_rules(self, policy): return list(self.get_settings(policy)["rules"].keys()) # SERVICES def check_service(self, service): self._fw.check_service(service) def __service_id(self, service): self.check_service(service) return service def add_service(self, policy, service, timeout=0, sender=None, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._policies[_policy] service_id = self.__service_id(service) if service_id in _obj.settings["services"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.ALREADY_ENABLED, "'%s' already in '%s'" % (service, _name)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._service(True, _policy, service, transaction) self.__register_service(_obj, service_id, timeout, sender) transaction.add_fail(self.__unregister_service, _obj, service_id) if use_transaction is None: transaction.execute(True) return _policy def __register_service(self, _obj, service_id, timeout, sender): _obj.settings["services"][service_id] = \ self.__gen_settings(timeout, sender) def remove_service(self, policy, service, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] service_id = self.__service_id(service) if service_id not in _obj.settings["services"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.NOT_ENABLED, "'%s' not in '%s'" % (service, _name)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._service(False, _policy, service, transaction) transaction.add_post(self.__unregister_service, _obj, service_id) if use_transaction is None: transaction.execute(True) return _policy def __unregister_service(self, _obj, service_id): if service_id in _obj.settings["services"]: del _obj.settings["services"][service_id] def query_service(self, policy, service): return self.__service_id(service) in self.get_settings(policy)["services"] def list_services(self, policy): return self.get_settings(policy)["services"].keys() def get_helpers_for_service_helpers(self, helpers): _helpers = [ ] for helper in helpers: try: _helper = self._fw.helper.get_helper(helper) except FirewallError: raise FirewallError(errors.INVALID_HELPER, helper) _helpers.append(_helper) return _helpers def get_helpers_for_service_modules(self, modules, enable): # If automatic helper assignment is turned off, helpers that # do not have ports defined will be replaced by the helpers # that the helper.module defines. _helpers = [ ] for module in modules: try: helper = self._fw.helper.get_helper(module) except FirewallError: raise FirewallError(errors.INVALID_HELPER, module) if len(helper.ports) < 1: _module_short_name = get_nf_conntrack_short_name(helper.module) try: _helper = self._fw.helper.get_helper(_module_short_name) _helpers.append(_helper) except FirewallError: if enable: log.warning("Helper '%s' is not available" % _module_short_name) continue else: _helpers.append(helper) return _helpers # PORTS def check_port(self, port, protocol): self._fw.check_port(port) self._fw.check_tcpudp(protocol) def __port_id(self, port, protocol): self.check_port(port, protocol) return (portStr(port, "-"), protocol) def add_port(self, policy, port, protocol, timeout=0, sender=None, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._policies[_policy] existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"])) for port_id in existing_port_ids: if portInPortRange(port, port_id[0]): _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.ALREADY_ENABLED, "'%s:%s' already in '%s'" % (port, protocol, _name)) added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids]) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: for range in added_ranges: self._port(True, _policy, portStr(range, "-"), protocol, transaction) for range in removed_ranges: self._port(False, _policy, portStr(range, "-"), protocol, transaction) for range in added_ranges: port_id = self.__port_id(range, protocol) self.__register_port(_obj, port_id, timeout, sender) transaction.add_fail(self.__unregister_port, _obj, port_id) for range in removed_ranges: port_id = self.__port_id(range, protocol) transaction.add_post(self.__unregister_port, _obj, port_id) if use_transaction is None: transaction.execute(True) return _policy def __register_port(self, _obj, port_id, timeout, sender): _obj.settings["ports"][port_id] = \ self.__gen_settings(timeout, sender) def remove_port(self, policy, port, protocol, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"])) for port_id in existing_port_ids: if portInPortRange(port, port_id[0]): break else: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.NOT_ENABLED, "'%s:%s' not in '%s'" % (port, protocol, _name)) added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids]) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: for range in added_ranges: self._port(True, _policy, portStr(range, "-"), protocol, transaction) for range in removed_ranges: self._port(False, _policy, portStr(range, "-"), protocol, transaction) for range in added_ranges: port_id = self.__port_id(range, protocol) self.__register_port(_obj, port_id, 0, None) transaction.add_fail(self.__unregister_port, _obj, port_id) for range in removed_ranges: port_id = self.__port_id(range, protocol) transaction.add_post(self.__unregister_port, _obj, port_id) if use_transaction is None: transaction.execute(True) return _policy def __unregister_port(self, _obj, port_id): if port_id in _obj.settings["ports"]: del _obj.settings["ports"][port_id] def query_port(self, policy, port, protocol): for (_port, _protocol) in self.get_settings(policy)["ports"]: if portInPortRange(port, _port) and protocol == _protocol: return True return False def list_ports(self, policy): return list(self.get_settings(policy)["ports"].keys()) # PROTOCOLS def check_protocol(self, protocol): if not checkProtocol(protocol): raise FirewallError(errors.INVALID_PROTOCOL, protocol) def __protocol_id(self, protocol): self.check_protocol(protocol) return protocol def add_protocol(self, policy, protocol, timeout=0, sender=None, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._policies[_policy] protocol_id = self.__protocol_id(protocol) if protocol_id in _obj.settings["protocols"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.ALREADY_ENABLED, "'%s' already in '%s'" % (protocol, _name)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._protocol(True, _policy, protocol, transaction) self.__register_protocol(_obj, protocol_id, timeout, sender) transaction.add_fail(self.__unregister_protocol, _obj, protocol_id) if use_transaction is None: transaction.execute(True) return _policy def __register_protocol(self, _obj, protocol_id, timeout, sender): _obj.settings["protocols"][protocol_id] = \ self.__gen_settings(timeout, sender) def remove_protocol(self, policy, protocol, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] protocol_id = self.__protocol_id(protocol) if protocol_id not in _obj.settings["protocols"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.NOT_ENABLED, "'%s' not in '%s'" % (protocol, _name)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._protocol(False, _policy, protocol, transaction) transaction.add_post(self.__unregister_protocol, _obj, protocol_id) if use_transaction is None: transaction.execute(True) return _policy def __unregister_protocol(self, _obj, protocol_id): if protocol_id in _obj.settings["protocols"]: del _obj.settings["protocols"][protocol_id] def query_protocol(self, policy, protocol): return self.__protocol_id(protocol) in self.get_settings(policy)["protocols"] def list_protocols(self, policy): return list(self.get_settings(policy)["protocols"].keys()) # SOURCE PORTS def __source_port_id(self, port, protocol): self.check_port(port, protocol) return (portStr(port, "-"), protocol) def add_source_port(self, policy, port, protocol, timeout=0, sender=None, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._policies[_policy] existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"])) for port_id in existing_port_ids: if portInPortRange(port, port_id[0]): _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.ALREADY_ENABLED, "'%s:%s' already in '%s'" % (port, protocol, _name)) added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids]) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: for range in added_ranges: self._source_port(True, _policy, portStr(range, "-"), protocol, transaction) for range in removed_ranges: self._source_port(False, _policy, portStr(range, "-"), protocol, transaction) for range in added_ranges: port_id = self.__source_port_id(range, protocol) self.__register_source_port(_obj, port_id, timeout, sender) transaction.add_fail(self.__unregister_source_port, _obj, port_id) for range in removed_ranges: port_id = self.__source_port_id(range, protocol) transaction.add_post(self.__unregister_source_port, _obj, port_id) if use_transaction is None: transaction.execute(True) return _policy def __register_source_port(self, _obj, port_id, timeout, sender): _obj.settings["source_ports"][port_id] = \ self.__gen_settings(timeout, sender) def remove_source_port(self, policy, port, protocol, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"])) for port_id in existing_port_ids: if portInPortRange(port, port_id[0]): break else: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.NOT_ENABLED, "'%s:%s' not in '%s'" % (port, protocol, _name)) added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids]) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: for range in added_ranges: self._source_port(True, _policy, portStr(range, "-"), protocol, transaction) for range in removed_ranges: self._source_port(False, _policy, portStr(range, "-"), protocol, transaction) for range in added_ranges: port_id = self.__source_port_id(range, protocol) self.__register_source_port(_obj, port_id, 0, None) transaction.add_fail(self.__unregister_source_port, _obj, port_id) for range in removed_ranges: port_id = self.__source_port_id(range, protocol) transaction.add_post(self.__unregister_source_port, _obj, port_id) if use_transaction is None: transaction.execute(True) return _policy def __unregister_source_port(self, _obj, port_id): if port_id in _obj.settings["source_ports"]: del _obj.settings["source_ports"][port_id] def query_source_port(self, policy, port, protocol): for (_port, _protocol) in self.get_settings(policy)["source_ports"]: if portInPortRange(port, _port) and protocol == _protocol: return True return False def list_source_ports(self, policy): return list(self.get_settings(policy)["source_ports"].keys()) # MASQUERADE def __masquerade_id(self): return True def add_masquerade(self, policy, timeout=0, sender=None, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._policies[_policy] masquerade_id = self.__masquerade_id() if masquerade_id in _obj.settings["masquerade"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.ALREADY_ENABLED, "masquerade already enabled in '%s'" % _name) if not _obj.derived_from_zone: if "HOST" in _obj.settings["egress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'masquerade' is invalid for egress zone 'HOST'") if "HOST" in _obj.settings["ingress_zones"]: raise FirewallError(errors.INVALID_ZONE, "'masquerade' is invalid for ingress zone 'HOST'") for zone in _obj.settings["ingress_zones"]: if zone == "ANY": continue if self._fw.zone.list_interfaces(zone): raise FirewallError(errors.INVALID_ZONE, "'masquerade' cannot be used in a policy if an ingress zone has assigned interfaces") if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._masquerade(True, _policy, transaction) self.__register_masquerade(_obj, masquerade_id, timeout, sender) transaction.add_fail(self.__unregister_masquerade, _obj, masquerade_id) if use_transaction is None: transaction.execute(True) return _policy def __register_masquerade(self, _obj, masquerade_id, timeout, sender): _obj.settings["masquerade"][masquerade_id] = \ self.__gen_settings(timeout, sender) def remove_masquerade(self, policy, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] masquerade_id = self.__masquerade_id() if masquerade_id not in _obj.settings["masquerade"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.NOT_ENABLED, "masquerade not enabled in '%s'" % _name) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._masquerade(False, _policy, transaction) transaction.add_post(self.__unregister_masquerade, _obj, masquerade_id) if use_transaction is None: transaction.execute(True) return _policy def __unregister_masquerade(self, _obj, masquerade_id): if masquerade_id in _obj.settings["masquerade"]: del _obj.settings["masquerade"][masquerade_id] def query_masquerade(self, policy): return self.__masquerade_id() in self.get_settings(policy)["masquerade"] # PORT FORWARDING def check_forward_port(self, ipv, port, protocol, toport=None, toaddr=None): self._fw.check_port(port) self._fw.check_tcpudp(protocol) if toport: self._fw.check_port(toport) if toaddr: if not check_single_address(ipv, toaddr): raise FirewallError(errors.INVALID_ADDR, toaddr) if not toport and not toaddr: raise FirewallError( errors.INVALID_FORWARD, "port-forwarding is missing to-port AND to-addr") def __forward_port_id(self, port, protocol, toport=None, toaddr=None): if check_single_address("ipv6", toaddr): self.check_forward_port("ipv6", port, protocol, toport, toaddr) else: self.check_forward_port("ipv4", port, protocol, toport, toaddr) return (portStr(port, "-"), protocol, portStr(toport, "-"), str(toaddr)) def add_forward_port(self, policy, port, protocol, toport=None, toaddr=None, timeout=0, sender=None, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._policies[_policy] forward_id = self.__forward_port_id(port, protocol, toport, toaddr) if forward_id in _obj.settings["forward_ports"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.ALREADY_ENABLED, "'%s:%s:%s:%s' already in '%s'" % \ (port, protocol, toport, toaddr, _name)) if not _obj.derived_from_zone: if "HOST" in _obj.settings["egress_zones"]: if toaddr: raise FirewallError(errors.INVALID_FORWARD, "A 'forward-port' with 'to-addr' is invalid for egress zone 'HOST'") elif _obj.settings["egress_zones"]: if not toaddr: raise FirewallError(errors.INVALID_FORWARD, "'forward-port' requires 'to-addr' if egress zone is 'ANY' or a zone") for zone in _obj.settings["egress_zones"]: if zone == "ANY": continue if self._fw.zone.list_interfaces(zone): raise FirewallError(errors.INVALID_ZONE, "'forward-port' cannot be used in a policy if an egress zone has assigned interfaces") if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._forward_port(True, _policy, transaction, port, protocol, toport, toaddr) self.__register_forward_port(_obj, forward_id, timeout, sender) transaction.add_fail(self.__unregister_forward_port, _obj, forward_id) if use_transaction is None: transaction.execute(True) return _policy def __register_forward_port(self, _obj, forward_id, timeout, sender): _obj.settings["forward_ports"][forward_id] = \ self.__gen_settings(timeout, sender) def remove_forward_port(self, policy, port, protocol, toport=None, toaddr=None, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] forward_id = self.__forward_port_id(port, protocol, toport, toaddr) if forward_id not in _obj.settings["forward_ports"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.NOT_ENABLED, "'%s:%s:%s:%s' not in '%s'" % \ (port, protocol, toport, toaddr, _name)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._forward_port(False, _policy, transaction, port, protocol, toport, toaddr) transaction.add_post(self.__unregister_forward_port, _obj, forward_id) if use_transaction is None: transaction.execute(True) return _policy def __unregister_forward_port(self, _obj, forward_id): if forward_id in _obj.settings["forward_ports"]: del _obj.settings["forward_ports"][forward_id] def query_forward_port(self, policy, port, protocol, toport=None, toaddr=None): forward_id = self.__forward_port_id(port, protocol, toport, toaddr) return forward_id in self.get_settings(policy)["forward_ports"] def list_forward_ports(self, policy): return list(self.get_settings(policy)["forward_ports"].keys()) # ICMP BLOCK def check_icmp_block(self, icmp): self._fw.check_icmptype(icmp) def __icmp_block_id(self, icmp): self.check_icmp_block(icmp) return icmp def add_icmp_block(self, policy, icmp, timeout=0, sender=None, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._policies[_policy] icmp_id = self.__icmp_block_id(icmp) if icmp_id in _obj.settings["icmp_blocks"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.ALREADY_ENABLED, "'%s' already in '%s'" % (icmp, _name)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._icmp_block(True, _policy, icmp, transaction) self.__register_icmp_block(_obj, icmp_id, timeout, sender) transaction.add_fail(self.__unregister_icmp_block, _obj, icmp_id) if use_transaction is None: transaction.execute(True) return _policy def __register_icmp_block(self, _obj, icmp_id, timeout, sender): _obj.settings["icmp_blocks"][icmp_id] = \ self.__gen_settings(timeout, sender) def remove_icmp_block(self, policy, icmp, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] icmp_id = self.__icmp_block_id(icmp) if icmp_id not in _obj.settings["icmp_blocks"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError(errors.NOT_ENABLED, "'%s' not in '%s'" % (icmp, _name)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._icmp_block(False, _policy, icmp, transaction) transaction.add_post(self.__unregister_icmp_block, _obj, icmp_id) if use_transaction is None: transaction.execute(True) return _policy def __unregister_icmp_block(self, _obj, icmp_id): if icmp_id in _obj.settings["icmp_blocks"]: del _obj.settings["icmp_blocks"][icmp_id] def query_icmp_block(self, policy, icmp): return self.__icmp_block_id(icmp) in self.get_settings(policy)["icmp_blocks"] def list_icmp_blocks(self, policy): return self.get_settings(policy)["icmp_blocks"].keys() # ICMP BLOCK INVERSION def __icmp_block_inversion_id(self): return True def add_icmp_block_inversion(self, policy, sender=None, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] icmp_block_inversion_id = self.__icmp_block_inversion_id() if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError( errors.ALREADY_ENABLED, "icmp-block-inversion already enabled in '%s'" % _name) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: # undo icmp blocks for args in self.get_settings(_policy)["icmp_blocks"]: self._icmp_block(False, _policy, args, transaction) self._icmp_block_inversion(False, _policy, transaction) self.__register_icmp_block_inversion(_obj, icmp_block_inversion_id, sender) transaction.add_fail(self.__undo_icmp_block_inversion, _policy, _obj, icmp_block_inversion_id) # redo icmp blocks if _obj.applied: for args in self.get_settings(_policy)["icmp_blocks"]: self._icmp_block(True, _policy, args, transaction) self._icmp_block_inversion(True, _policy, transaction) if use_transaction is None: transaction.execute(True) return _policy def __register_icmp_block_inversion(self, _obj, icmp_block_inversion_id, sender): _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] = \ self.__gen_settings(0, sender) def __undo_icmp_block_inversion(self, _policy, _obj, icmp_block_inversion_id): transaction = self.new_transaction() # undo icmp blocks if _obj.applied: for args in self.get_settings(_policy)["icmp_blocks"]: self._icmp_block(False, _policy, args, transaction) if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]: del _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] # redo icmp blocks if _obj.applied: for args in self.get_settings(_policy)["icmp_blocks"]: self._icmp_block(True, _policy, args, transaction) transaction.execute(True) def remove_icmp_block_inversion(self, policy, use_transaction=None): _policy = self._fw.check_policy(policy) self._fw.check_panic() _obj = self._policies[_policy] icmp_block_inversion_id = self.__icmp_block_inversion_id() if icmp_block_inversion_id not in _obj.settings["icmp_block_inversion"]: _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy raise FirewallError( errors.NOT_ENABLED, "icmp-block-inversion not enabled in '%s'" % _name) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: # undo icmp blocks for args in self.get_settings(_policy)["icmp_blocks"]: self._icmp_block(False, _policy, args, transaction) self._icmp_block_inversion(False, _policy, transaction) self.__unregister_icmp_block_inversion(_obj, icmp_block_inversion_id) transaction.add_fail(self.__register_icmp_block_inversion, _obj, icmp_block_inversion_id, None) # redo icmp blocks if _obj.applied: for args in self.get_settings(_policy)["icmp_blocks"]: self._icmp_block(True, _policy, args, transaction) self._icmp_block_inversion(True, _policy, transaction) if use_transaction is None: transaction.execute(True) return _policy def __unregister_icmp_block_inversion(self, _obj, icmp_block_inversion_id): if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]: del _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] def query_icmp_block_inversion(self, policy): return self.__icmp_block_inversion_id() in \ self.get_settings(policy)["icmp_block_inversion"] def gen_chain_rules(self, policy, create, table, chain, transaction): obj = self._fw.policy.get_policy(policy) if obj.derived_from_zone: # For policies derived from zones, use only the first policy in the # list to track chain creation. The chain names are converted to # zone-based names as such they're "global" for all zone derived # policies. tracking_policy = self._fw.zone._zone_policies[obj.derived_from_zone][0] else: tracking_policy = policy if create: if tracking_policy in self._chains and \ (table, chain) in self._chains[tracking_policy]: return else: if tracking_policy not in self._chains or \ (table, chain) not in self._chains[tracking_policy]: return for backend in self._fw.enabled_backends(): if backend.policies_supported and \ table in backend.get_available_tables(): rules = backend.build_policy_chain_rules(create, policy, table, chain) transaction.add_rules(backend, rules) self._register_chains(tracking_policy, create, [(table, chain)]) transaction.add_fail(self._register_chains, tracking_policy, not create, [(table, chain)]) def _register_chains(self, policy, create, tables): for (table, chain) in tables: if create: self._chains.setdefault(policy, []).append((table, chain)) else: self._chains[policy].remove((table, chain)) if len(self._chains[policy]) == 0: del self._chains[policy] # IPSETS def _ipset_family(self, name): if self._fw.ipset.get_type(name) == "hash:mac": return None return self._fw.ipset.get_family(name) def __ipset_type(self, name): return self._fw.ipset.get_type(name) def _ipset_match_flags(self, name, flag): return ",".join([flag] * self._fw.ipset.get_dimension(name)) def _check_ipset_applied(self, name): return self._fw.ipset.check_applied(name) def _check_ipset_type_for_source(self, name): _type = self.__ipset_type(name) if _type not in SOURCE_IPSET_TYPES: raise FirewallError( errors.INVALID_IPSET, "ipset '%s' with type '%s' not usable as source" % \ (name, _type)) def _rule_prepare(self, enable, policy, rule, transaction, included_services=None): # First apply any services this service may include if type(rule.element) == Rich_Service: svc = self._fw.service.get_service(rule.element.name) if included_services is None: included_services = [rule.element.name] for include in svc.includes: if include in included_services: continue self.check_service(include) included_services.append(include) _rule = copy.deepcopy(rule) _rule.element.name = include self._rule_prepare(enable, policy, _rule, transaction, included_services=included_services) ipvs = [] if rule.family: ipvs = [ rule.family ] elif rule.element and (isinstance(rule.element, Rich_IcmpBlock) or isinstance(rule.element, Rich_IcmpType)): ict = self._fw.config.get_icmptype(rule.element.name) if ict.destination: ipvs = [ipv for ipv in ["ipv4", "ipv6"] if ipv in ict.destination] source_ipv = self._rule_source_ipv(rule.source) if source_ipv: if rule.family: # rule family is defined by user, no way to change it if rule.family != source_ipv: raise FirewallError(errors.INVALID_RULE, "Source address family '%s' conflicts with rule family '%s'." % (source_ipv, rule.family)) else: # use the source family as rule family ipvs = [ source_ipv ] if not ipvs: ipvs = ["ipv4", "ipv6"] # clamp ipvs to those that are actually enabled. ipvs = [ipv for ipv in ipvs if self._fw.is_ipv_enabled(ipv)] # add an element to object to allow backends to know what ipvs this applies to rule.ipvs = ipvs for backend in set([self._fw.get_backend_by_ipv(x) for x in ipvs]): # SERVICE if type(rule.element) == Rich_Service: svc = self._fw.service.get_service(rule.element.name) destinations = [] if len(svc.destination) > 0: if rule.destination: # we can not use two destinations at the same time raise FirewallError(errors.INVALID_RULE, "Destination conflict with service.") for ipv in ipvs: if ipv in svc.destination and backend.is_ipv_supported(ipv): destinations.append(svc.destination[ipv]) else: # dummy for the following for loop destinations.append(None) for destination in destinations: if type(rule.action) == Rich_Accept: # only load modules for accept action helpers = self.get_helpers_for_service_modules(svc.modules, enable) helpers += self.get_helpers_for_service_helpers(svc.helpers) helpers = sorted(set(helpers), key=lambda x: x.name) modules = [ ] for helper in helpers: module = helper.module _module_short_name = get_nf_conntrack_short_name(module) nat_module = module.replace("conntrack", "nat") modules.append(nat_module) if helper.family != "" and not backend.is_ipv_supported(helper.family): # no support for family ipv, continue continue if len(helper.ports) < 1: modules.append(module) else: for (port,proto) in helper.ports: rules = backend.build_policy_helper_ports_rules( enable, policy, proto, port, destination, helper.name, _module_short_name) transaction.add_rules(backend, rules) transaction.add_modules(modules) # create rules for (port,proto) in svc.ports: rules = backend.build_policy_ports_rules( enable, policy, proto, port, destination, rule) transaction.add_rules(backend, rules) for proto in svc.protocols: rules = backend.build_policy_protocol_rules( enable, policy, proto, destination, rule) transaction.add_rules(backend, rules) # create rules for (port,proto) in svc.source_ports: rules = backend.build_policy_source_ports_rules( enable, policy, proto, port, destination, rule) transaction.add_rules(backend, rules) # PORT elif type(rule.element) == Rich_Port: port = rule.element.port protocol = rule.element.protocol self.check_port(port, protocol) rules = backend.build_policy_ports_rules( enable, policy, protocol, port, None, rule) transaction.add_rules(backend, rules) # PROTOCOL elif type(rule.element) == Rich_Protocol: protocol = rule.element.value self.check_protocol(protocol) rules = backend.build_policy_protocol_rules( enable, policy, protocol, None, rule) transaction.add_rules(backend, rules) # MASQUERADE elif type(rule.element) == Rich_Masquerade: if enable: for ipv in ipvs: if backend.is_ipv_supported(ipv): transaction.add_post(enable_ip_forwarding, ipv) rules = backend.build_policy_masquerade_rules(enable, policy, rule) transaction.add_rules(backend, rules) # FORWARD PORT elif type(rule.element) == Rich_ForwardPort: port = rule.element.port protocol = rule.element.protocol toport = rule.element.to_port toaddr = rule.element.to_address for ipv in ipvs: if backend.is_ipv_supported(ipv): self.check_forward_port(ipv, port, protocol, toport, toaddr) if toaddr and enable: transaction.add_post(enable_ip_forwarding, ipv) rules = backend.build_policy_forward_port_rules( enable, policy, port, protocol, toport, toaddr, rule) transaction.add_rules(backend, rules) # SOURCE PORT elif type(rule.element) == Rich_SourcePort: port = rule.element.port protocol = rule.element.protocol self.check_port(port, protocol) rules = backend.build_policy_source_ports_rules( enable, policy, protocol, port, None, rule) transaction.add_rules(backend, rules) # ICMP BLOCK and ICMP TYPE elif type(rule.element) == Rich_IcmpBlock or \ type(rule.element) == Rich_IcmpType: ict = self._fw.config.get_icmptype(rule.element.name) if rule.family and ict.destination and \ rule.family not in ict.destination: raise FirewallError(errors.INVALID_ICMPTYPE, "rich rule family '%s' conflicts with icmp type '%s'" % \ (rule.family, rule.element.name)) if type(rule.element) == Rich_IcmpBlock and \ rule.action and type(rule.action) == Rich_Accept: # icmp block might have reject or drop action, but not accept raise FirewallError(errors.INVALID_RULE, "IcmpBlock not usable with accept action") rules = backend.build_policy_icmp_block_rules(enable, policy, ict, rule) transaction.add_rules(backend, rules) elif rule.element is None: rules = backend.build_policy_rich_source_destination_rules( enable, policy, rule) transaction.add_rules(backend, rules) # EVERYTHING ELSE else: raise FirewallError(errors.INVALID_RULE, "Unknown element %s" % type(rule.element)) def _service(self, enable, policy, service, transaction, included_services=None): svc = self._fw.service.get_service(service) helpers = self.get_helpers_for_service_modules(svc.modules, enable) helpers += self.get_helpers_for_service_helpers(svc.helpers) helpers = sorted(set(helpers), key=lambda x: x.name) # First apply any services this service may include if included_services is None: included_services = [service] for include in svc.includes: if include in included_services: continue self.check_service(include) included_services.append(include) self._service(enable, policy, include, transaction, included_services=included_services) # build a list of (backend, destination). The destination may be ipv4, # ipv6 or None # backends_ipv = [] for ipv in ["ipv4", "ipv6"]: if not self._fw.is_ipv_enabled(ipv): continue backend = self._fw.get_backend_by_ipv(ipv) if len(svc.destination) > 0: if ipv in svc.destination: backends_ipv.append((backend, svc.destination[ipv])) else: if (backend, None) not in backends_ipv: backends_ipv.append((backend, None)) for (backend,destination) in backends_ipv: for helper in helpers: module = helper.module _module_short_name = get_nf_conntrack_short_name(module) nat_module = helper.module.replace("conntrack", "nat") transaction.add_module(nat_module) if helper.family != "" and not backend.is_ipv_supported(helper.family): # no support for family ipv, continue continue if len(helper.ports) < 1: transaction.add_module(module) else: for (port,proto) in helper.ports: rules = backend.build_policy_helper_ports_rules( enable, policy, proto, port, destination, helper.name, _module_short_name) transaction.add_rules(backend, rules) for (port,proto) in svc.ports: rules = backend.build_policy_ports_rules(enable, policy, proto, port, destination) transaction.add_rules(backend, rules) for protocol in svc.protocols: rules = backend.build_policy_protocol_rules( enable, policy, protocol, destination) transaction.add_rules(backend, rules) for (port,proto) in svc.source_ports: rules = backend.build_policy_source_ports_rules( enable, policy, proto, port, destination) transaction.add_rules(backend, rules) def _port(self, enable, policy, port, protocol, transaction): for backend in self._fw.enabled_backends(): if not backend.policies_supported: continue rules = backend.build_policy_ports_rules(enable, policy, protocol, port) transaction.add_rules(backend, rules) def _protocol(self, enable, policy, protocol, transaction): for backend in self._fw.enabled_backends(): if not backend.policies_supported: continue rules = backend.build_policy_protocol_rules(enable, policy, protocol) transaction.add_rules(backend, rules) def _source_port(self, enable, policy, port, protocol, transaction): for backend in self._fw.enabled_backends(): if not backend.policies_supported: continue rules = backend.build_policy_source_ports_rules(enable, policy, protocol, port) transaction.add_rules(backend, rules) def _masquerade(self, enable, policy, transaction): ipv = "ipv4" transaction.add_post(enable_ip_forwarding, ipv) backend = self._fw.get_backend_by_ipv(ipv) rules = backend.build_policy_masquerade_rules(enable, policy) transaction.add_rules(backend, rules) def _forward_port(self, enable, policy, transaction, port, protocol, toport=None, toaddr=None): if check_single_address("ipv6", toaddr): ipv = "ipv6" else: ipv = "ipv4" if toaddr and enable: transaction.add_post(enable_ip_forwarding, ipv) backend = self._fw.get_backend_by_ipv(ipv) rules = backend.build_policy_forward_port_rules( enable, policy, port, protocol, toport, toaddr) transaction.add_rules(backend, rules) def _icmp_block(self, enable, policy, icmp, transaction): ict = self._fw.config.get_icmptype(icmp) for backend in self._fw.enabled_backends(): if not backend.policies_supported: continue skip_backend = False if ict.destination: for ipv in ["ipv4", "ipv6"]: if ipv in ict.destination: if not backend.is_ipv_supported(ipv): skip_backend = True break if skip_backend: continue rules = backend.build_policy_icmp_block_rules(enable, policy, ict) transaction.add_rules(backend, rules) def _icmp_block_inversion(self, enable, policy, transaction): target = self._policies[policy].target # Do not add general icmp accept rules into a trusted, block or drop # policy. if target in [ "DROP", "%%REJECT%%", "REJECT" ]: return if not self.query_icmp_block_inversion(policy) and target == "ACCEPT": # ibi target and policy target are ACCEPT, no need to add an extra # rule return for backend in self._fw.enabled_backends(): if not backend.policies_supported: continue rules = backend.build_policy_icmp_block_inversion_rules(enable, policy) transaction.add_rules(backend, rules) def check_ingress_egress(self, policy, ingress_zones, egress_zones, ingress_interfaces, egress_interfaces, ingress_sources, egress_sources): for zone in ingress_zones: self.check_ingress_zone(zone) for zone in egress_zones: self.check_egress_zone(zone) if ("ANY" in ingress_zones or "HOST" in ingress_zones) and \ len(ingress_zones) > 1: raise FirewallError(errors.INVALID_ZONE, "'ingress-zones' may only contain one of: many regular zones, ANY, or HOST") if ("ANY" in egress_zones or "HOST" in egress_zones) and \ len(egress_zones) > 1: raise FirewallError(errors.INVALID_ZONE, "'egress-zones' may only contain one of: many regular zones, ANY, or HOST") if (egress_interfaces or egress_sources) and \ not ingress_interfaces and not ingress_sources and \ "HOST" not in ingress_zones and "ANY" not in ingress_zones: raise FirewallError(errors.INVALID_ZONE, "policy \"%s\" has no ingress" % (policy)) if (ingress_interfaces or ingress_sources) and \ not egress_interfaces and not egress_sources and \ "HOST" not in egress_zones and "ANY" not in egress_zones: raise FirewallError(errors.INVALID_ZONE, "policy \"%s\" has no egress" % (policy)) def check_ingress_egress_chain(self, policy, table, chain, ingress_zones, egress_zones, ingress_interfaces, egress_interfaces, ingress_sources, egress_sources): if chain == "PREROUTING": # raw,prerouting is used for conntrack helpers (services), so we # need to allow it if egress-zones contains an actual zone if table != "raw": if egress_interfaces: raise FirewallError(errors.INVALID_ZONE, "policy \"%s\" egress-zones may not include a zone with added interfaces." % (policy)) elif chain == "POSTROUTING": if "HOST" in ingress_zones: raise FirewallError(errors.INVALID_ZONE, "policy \"%s\" ingress-zones may not include HOST." % (policy)) if "HOST" in egress_zones: raise FirewallError(errors.INVALID_ZONE, "policy \"%s\" egress-zones may not include HOST." % (policy)) if ingress_interfaces: raise FirewallError(errors.INVALID_ZONE, "policy \"%s\" ingress-zones may not include a zone with added interfaces." % (policy)) elif chain == "FORWARD": if "HOST" in ingress_zones: raise FirewallError(errors.INVALID_ZONE, "policy \"%s\" ingress-zones may not include HOST." % (policy)) if "HOST" in egress_zones: raise FirewallError(errors.INVALID_ZONE, "policy \"%s\" egress-zones may not include HOST." % (policy)) elif chain == "INPUT": if "HOST" not in egress_zones: raise FirewallError(errors.INVALID_ZONE, "policy \"%s\" egress-zones must include only HOST." % (policy)) elif chain == "OUTPUT": if "HOST" not in ingress_zones: raise FirewallError(errors.INVALID_ZONE, "policy \"%s\" ingress-zones must include only HOST." % (policy)) def _ingress_egress_zones_transaction(self, enable, policy): transaction = self.new_transaction() self._ingress_egress_zones(enable, policy, transaction) transaction.execute(True) def _ingress_egress_zones(self, enable, policy, transaction): obj = self._policies[policy] ingress_zones = obj.settings["ingress_zones"] egress_zones = obj.settings["egress_zones"] ingress_interfaces = set() egress_interfaces = set() ingress_sources = set() egress_sources = set() for zone in ingress_zones: if zone in ["ANY", "HOST"]: continue ingress_interfaces |= set(self._fw.zone.list_interfaces(zone)) ingress_sources |= set(self._fw.zone.list_sources(zone)) for zone in egress_zones: if zone in ["ANY", "HOST"]: continue egress_interfaces |= set(self._fw.zone.list_interfaces(zone)) egress_sources |= set(self._fw.zone.list_sources(zone)) self.check_ingress_egress(policy, ingress_zones, egress_zones, ingress_interfaces, egress_interfaces, ingress_sources, egress_sources) for backend in self._fw.enabled_backends(): if not backend.policies_supported: continue for (table, chain) in self._get_table_chains_for_policy_dispatch(policy): self.check_ingress_egress_chain(policy, table, chain, ingress_zones, egress_zones, ingress_interfaces, egress_interfaces, ingress_sources, egress_sources) rules = backend.build_policy_ingress_egress_rules(enable, policy, table, chain, ingress_interfaces, egress_interfaces, ingress_sources, egress_sources) transaction.add_rules(backend, rules) def _get_table_chains_for_policy_dispatch(self, policy): """Create a list of (table, chain) needed for policy dispatch""" obj = self._policies[policy] if "ANY" in obj.settings["ingress_zones"] and "HOST" in obj.settings["egress_zones"]: # any --> HOST tc = [("filter", "INPUT"), ("nat", "PREROUTING"), ("mangle", "PREROUTING")] # iptables backend needs to put conntrack helper rules in raw # prerouting. if not self._fw.nftables_enabled: tc.append(("raw", "PREROUTING")) return tc elif "HOST" in obj.settings["egress_zones"]: # zone --> HOST tc = [("filter", "INPUT")] # iptables backend needs to put conntrack helper rules in raw # prerouting. if not self._fw.nftables_enabled: tc.append(("raw", "PREROUTING")) return tc elif "HOST" in obj.settings["ingress_zones"]: # HOST --> zone/any return [("filter", "OUTPUT")] elif "ANY" in obj.settings["ingress_zones"] and "ANY" in obj.settings["egress_zones"]: # any --> any tc = [("filter", "FORWARD"), ("nat", "PREROUTING"), ("nat", "POSTROUTING"), ("mangle", "PREROUTING")] # iptables backend needs to put conntrack helper rules in raw # prerouting. if not self._fw.nftables_enabled: tc.append(("raw", "PREROUTING")) return tc elif "ANY" in obj.settings["egress_zones"]: # zone --> any tc = [("filter", "FORWARD"), ("nat", "PREROUTING"), ("mangle", "PREROUTING")] # iptables backend needs to put conntrack helper rules in raw # prerouting. if not self._fw.nftables_enabled: tc.append(("raw", "PREROUTING")) for zone in obj.settings["ingress_zones"]: if self._fw.zone.get_settings(zone)["interfaces"]: break else: tc.append(("nat", "POSTROUTING")) return tc elif "ANY" in obj.settings["ingress_zones"]: # any --> zone tc = [("filter", "FORWARD"), ("nat", "POSTROUTING")] # iptables backend needs to put conntrack helper rules in raw # prerouting. if not self._fw.nftables_enabled: tc.append(("raw", "PREROUTING")) for zone in obj.settings["egress_zones"]: if self._fw.zone.get_settings(zone)["interfaces"]: break else: tc.append(("nat", "PREROUTING")) tc.append(("mangle", "PREROUTING")) return tc else: # zone -> zone tc = [("filter", "FORWARD")] # iptables backend needs to put conntrack helper rules in raw # prerouting. if not self._fw.nftables_enabled: tc.append(("raw", "PREROUTING")) for zone in obj.settings["ingress_zones"]: if self._fw.zone.get_settings(zone)["interfaces"]: break else: tc.append(("nat", "POSTROUTING")) for zone in obj.settings["egress_zones"]: if self._fw.zone.get_settings(zone)["interfaces"]: break else: tc.append(("nat", "PREROUTING")) tc.append(("mangle", "PREROUTING")) return tc def _get_table_chains_for_zone_dispatch(self, policy): """Create a list of (table, chain) needed for zone dispatch""" obj = self._policies[policy] if "HOST" in obj.settings["egress_zones"]: # zone --> Host tc = [("filter", "INPUT")] # iptables backend needs to put conntrack helper rules in raw # prerouting. if not self._fw.nftables_enabled: tc.append(("raw", "PREROUTING")) return tc elif "ANY" in obj.settings["egress_zones"]: # zone --> any return [("filter", "FORWARD_IN"), ("nat", "PREROUTING"), ("mangle", "PREROUTING")] elif "ANY" in obj.settings["ingress_zones"]: # any --> zone return [("filter", "FORWARD_OUT"), ("nat", "POSTROUTING")] else: return FirewallError("Invalid policy: %s" % (policy)) def policy_base_chain_name(self, policy, table, policy_prefix, isSNAT=False): obj = self._fw.policy.get_policy(policy) if obj.derived_from_zone: suffix = obj.derived_from_zone else: suffix = policy_prefix + policy if "HOST" in obj.settings["egress_zones"]: # zone/any --> Host if table == "filter": return "IN_" + suffix if table == "raw": # NOTE: nftables doesn't actually use this. Only iptables return "PRE_" + suffix if not obj.derived_from_zone: if table in ["mangle", "nat"]: return "PRE_" + suffix elif "HOST" in obj.settings["ingress_zones"]: # HOST --> zone/any if not obj.derived_from_zone: if table == "filter": return "OUT_" + suffix elif "ANY" in obj.settings["egress_zones"]: # zone/any --> any if table == "filter": if obj.derived_from_zone: return "FWDI_" + suffix else: return "FWD_" + suffix elif table == "nat": if isSNAT: return "POST_" + suffix else: return "PRE_" + suffix elif table in ["mangle", "raw"]: return "PRE_" + suffix elif "ANY" in obj.settings["ingress_zones"]: # any --> zone if table == "filter": if obj.derived_from_zone: return "FWDO_" + suffix else: return "FWD_" + suffix elif table == "nat": if isSNAT: return "POST_" + suffix else: return "PRE_" + suffix elif table in ["mangle", "raw"]: if not obj.derived_from_zone: return "PRE_" + suffix elif not obj.derived_from_zone: # zone --> zone if table == "filter": return "FWD_" + suffix elif table == "nat": if isSNAT: return "POST_" + suffix else: return "PRE_" + suffix elif table in ["mangle", "raw"]: return "PRE_" + suffix return FirewallError("Can't convert policy to chain name: %s, %s, %s" % (policy, table, isSNAT)) PK!V_P)) fw_helper.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2015-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """helper backend""" __all__ = [ "FirewallHelper" ] from firewall import errors from firewall.errors import FirewallError class FirewallHelper(object): def __init__(self, fw): self._fw = fw self._helpers = { } def __repr__(self): return '%s(%r)' % (self.__class__, self._helpers) # helpers def cleanup(self): self._helpers.clear() def check_helper(self, name): if name not in self.get_helpers(): raise FirewallError(errors.INVALID_HELPER, name) def query_helper(self, name): return name in self.get_helpers() def get_helpers(self): return sorted(self._helpers.keys()) def has_helpers(self): return len(self._helpers) > 0 def get_helper(self, name): self.check_helper(name) return self._helpers[name] def add_helper(self, obj): self._helpers[obj.name] = obj def remove_helper(self, name): if name not in self._helpers: raise FirewallError(errors.INVALID_HELPER, name) del self._helpers[name] PK!%% fw_ipset.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2015-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """ipset backend""" __all__ = [ "FirewallIPSet" ] from firewall.core.logger import log from firewall.core.ipset import remove_default_create_options as rm_def_cr_opts, \ normalize_ipset_entry, check_entry_overlaps_existing, \ check_for_overlapping_entries from firewall.core.io.ipset import IPSet from firewall import errors from firewall.errors import FirewallError class FirewallIPSet(object): def __init__(self, fw): self._fw = fw self._ipsets = { } def __repr__(self): return '%s(%r)' % (self.__class__, self._ipsets) # ipsets def cleanup(self): self._ipsets.clear() def check_ipset(self, name): if name not in self.get_ipsets(): raise FirewallError(errors.INVALID_IPSET, name) def query_ipset(self, name): return name in self.get_ipsets() def get_ipsets(self): return sorted(self._ipsets.keys()) def has_ipsets(self): return len(self._ipsets) > 0 def get_ipset(self, name, applied=False): self.check_ipset(name) obj = self._ipsets[name] if applied: self.check_applied_obj(obj) return obj def backends(self): backends = [] if self._fw.nftables_enabled: backends.append(self._fw.nftables_backend) if self._fw.ipset_enabled: backends.append(self._fw.ipset_backend) return backends def add_ipset(self, obj): if obj.type not in self._fw.ipset_supported_types: raise FirewallError(errors.INVALID_TYPE, "'%s' is not supported by ipset." % obj.type) self._ipsets[obj.name] = obj def remove_ipset(self, name, keep=False): obj = self._ipsets[name] if obj.applied and not keep: try: for backend in self.backends(): backend.set_destroy(name) except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) else: log.debug1("Keeping ipset '%s' because of timeout option", name) del self._ipsets[name] def apply_ipset(self, name): obj = self._ipsets[name] for backend in self.backends(): if backend.name == "ipset": active = backend.set_get_active_terse() if name in active and ("timeout" not in obj.options or \ obj.options["timeout"] == "0" or \ obj.type != active[name][0] or \ rm_def_cr_opts(obj.options) != \ active[name][1]): try: backend.set_destroy(name) except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) if self._fw._individual_calls: try: backend.set_create(obj.name, obj.type, obj.options) except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) else: obj.applied = True if "timeout" in obj.options and \ obj.options["timeout"] != "0": # no entries visible for ipsets with timeout continue try: backend.set_flush(obj.name) except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) for entry in obj.entries: try: backend.set_add(obj.name, entry) except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) else: try: backend.set_restore(obj.name, obj.type, obj.entries, obj.options, None) except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) else: obj.applied = True def apply_ipsets(self): for name in self.get_ipsets(): obj = self._ipsets[name] obj.applied = False log.debug1("Applying ipset '%s'" % name) self.apply_ipset(name) def flush(self): for backend in self.backends(): # nftables sets are part of the normal firewall ruleset. if backend.name == "nftables": continue for ipset in self.get_ipsets(): try: self.check_applied(ipset) backend.set_destroy(ipset) except FirewallError as msg: if msg.code != errors.NOT_APPLIED: raise msg # TYPE def get_type(self, name, applied=True): return self.get_ipset(name, applied=applied).type # DIMENSION def get_dimension(self, name): return len(self.get_ipset(name, applied=True).type.split(",")) def check_applied(self, name): obj = self.get_ipset(name) self.check_applied_obj(obj) def check_applied_obj(self, obj): if not obj.applied: raise FirewallError( errors.NOT_APPLIED, obj.name) # OPTIONS def get_family(self, name, applied=True): obj = self.get_ipset(name, applied=applied) if "family" in obj.options: if obj.options["family"] == "inet6": return "ipv6" return "ipv4" # ENTRIES def add_entry(self, name, entry): obj = self.get_ipset(name, applied=True) entry = normalize_ipset_entry(entry) IPSet.check_entry(entry, obj.options, obj.type) if entry in obj.entries: raise FirewallError(errors.ALREADY_ENABLED, "'%s' already is in '%s'" % (entry, name)) check_entry_overlaps_existing(entry, obj.entries) try: for backend in self.backends(): backend.set_add(obj.name, entry) except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) else: if "timeout" not in obj.options or obj.options["timeout"] == "0": # no entries visible for ipsets with timeout obj.entries.append(entry) def remove_entry(self, name, entry): obj = self.get_ipset(name, applied=True) entry = normalize_ipset_entry(entry) # no entry check for removal if entry not in obj.entries: raise FirewallError(errors.NOT_ENABLED, "'%s' not in '%s'" % (entry, name)) try: for backend in self.backends(): backend.set_delete(obj.name, entry) except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) else: if "timeout" not in obj.options or obj.options["timeout"] == "0": # no entries visible for ipsets with timeout obj.entries.remove(entry) def query_entry(self, name, entry): obj = self.get_ipset(name, applied=True) entry = normalize_ipset_entry(entry) if "timeout" in obj.options and obj.options["timeout"] != "0": # no entries visible for ipsets with timeout raise FirewallError(errors.IPSET_WITH_TIMEOUT, name) return entry in obj.entries def get_entries(self, name): obj = self.get_ipset(name, applied=True) return obj.entries def set_entries(self, name, entries): obj = self.get_ipset(name, applied=True) check_for_overlapping_entries(entries) for entry in entries: IPSet.check_entry(entry, obj.options, obj.type) if "timeout" not in obj.options or obj.options["timeout"] == "0": # no entries visible for ipsets with timeout obj.entries = entries try: for backend in self.backends(): backend.set_flush(obj.name) except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) else: obj.applied = True try: for backend in self.backends(): if self._fw._individual_calls: for entry in obj.entries: backend.set_add(obj.name, entry) else: backend.set_restore(obj.name, obj.type, obj.entries, obj.options, None) except Exception as msg: raise FirewallError(errors.COMMAND_FAILED, msg) else: obj.applied = True return PK!266base.pynu[# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """Base firewall settings""" DEFAULT_ZONE_TARGET = "{chain}_{zone}" DEFAULT_POLICY_TARGET = "CONTINUE" DEFAULT_POLICY_PRIORITY = -1 ZONE_TARGETS = [ "ACCEPT", "%%REJECT%%", "DROP", DEFAULT_ZONE_TARGET, "default" ] POLICY_TARGETS = [ "ACCEPT", "REJECT", "DROP", "CONTINUE" ] SHORTCUTS = { "PREROUTING": "PRE", "POSTROUTING": "POST", "INPUT": "IN", "FORWARD_IN": "FWDI", "FORWARD_OUT": "FWDO", "OUTPUT": "OUT", } REJECT_TYPES = { "ipv4": [ "icmp-host-prohibited", "host-prohib", "icmp-net-unreachable", "net-unreach", "icmp-host-unreachable", "host-unreach", "icmp-port-unreachable", "port-unreach", "icmp-proto-unreachable", "proto-unreach", "icmp-net-prohibited", "net-prohib", "tcp-reset", "tcp-rst", "icmp-admin-prohibited", "admin-prohib" ], "ipv6": [ "icmp6-adm-prohibited", "adm-prohibited", "icmp6-no-route", "no-route", "icmp6-addr-unreachable", "addr-unreach", "icmp6-port-unreachable", "port-unreach", "tcp-reset" ] } # ipset types that can be used as a source in zones # The match-set option will be src or src,src according to the # dimension of the ipset. SOURCE_IPSET_TYPES = [ "hash:ip", "hash:ip,port", "hash:ip,mark", "hash:net", "hash:net,port", "hash:net,iface", "hash:mac" ] PK!4HHmmc_core.ko.xznu[7zXZִF!t/_<]?Eh=ڜ.++h5 u~lN[L(̆N_(YeEQ# }>4&鴻,p,(:}:kyZZw1 ci."ZzTz*۩ kQOWyodL'zzկy]JX;Mgr[xt2q9FO_ׯoPO 5;/~b bqR/i9sC^jG1@avyeWX.vKbm=Υu"JHC@`#VY( =ه]*9\~4h^ޣuNYvkusp M%5`}׽$q1Bc\PN&Q 9 d/R{h`Uu2^[2';Ҭ8e];%I7+g0-Nrt'7&vסj\i3][WY&/[^*hM=n:UauUct7 qT ,5ux}0ѪFQ19ل%OUC6PvbQd0(@SXa3g!{xGªL狇H|7ZJ{6fEwX,ܘ<1JP DFO2-\`lOkek10GA}L4wcbtLw[UKyU /Whk;,ʝuPG0Wqٌ`buM;]Ӊ\{- .p͓Mm6q?#@j%̉<w2u2!U%b-j⁷<22% `M?US,UDet@yPi{ ʌbf oj*L(a)-?jـ&3GDc=M;M. ]t7!D+}0z#4:#g07t`[] q TUq^&#쭅-NSX@>~+_?rb?{Snl ^k 3qRpf$,-:u;;Q"wi|֘WᮼWHq5Ygxo>yU}+aSESzqW-4; l4UfrFܖ=tYCm[Yk/=SBXgޱpx_Ȟ{0v4I0ihEfdӍ - q[!t%bd\_YGɓ6iB*`7+o#ɂC= MS9t=:7;88s*W79a*qs8 I].edqhF j8 {a7#tvܵ1GV7UuyG)~I%s|¦Dn6v5 |U"h I_RG8J{ (d.U\sүټZ0}7h{tiFzevN 6BуH. _l "Qtx4w⪃`x 3! %c/6vFօ] v֚+[4 DtX?[ZL0 &ΥQ4g0X-@gL:pLӔo( |>'EP^a0zWR-#SGhVo(&"F!=U%JH9>Cĉ"GC)/% s%&c"픞T8><}, ,;]6vm9[2 n0'?!y}KZ}sHe11$9U$0 ZL T o0ڕXU>kɵYiJy;ZɁU/V1i㨌d&{@f CT<\j)pͶXȰچB# 4/YR[[Ti#9€xȢQgT\% v},<}@)VJAp{'NUN8.q_c>odg Y`@80>Ǔ$sa z`y>eX6e)KJvK} HɆ:bNqu(*(.\hv)\VHH?ܗDM׶@nA2c oTzEh>IRԡVC$P4=sm>/"IP)wvLA q yf'Wӆ-OH 6@g>{I? `1z"<{|֤KUzyǵ117&ƸU5qS̠ R?ye_DO{9\uxpTwG b4d1V~>)^ˀ*óA Fe&ܸ wmB{_hQx:7X{I՘+h-_PvO,)K0Yyf*#ŠfBx#p}.? |Wt@[%g *U?ii .aTob@xf/Z GY&0v /bbv52;},%k꽔}!5Su1Cm)>Q,2e#<9[.~3zEġpY 5ud ykRU>\̴}V2fS|(?#dQ#PaZr(S+:%Mƌ\J  \,~CɍlȢtjx8lx+ 75Tb e}y bش{ de|>#Mh'XB"̼%.O>´ҚKx:ؐEx :PSll9ݺ#Xȏ횇 ņ2,ʠ.ֽd@_͉-X#e_ĩDS˘'G L*w2mzqD?<W1bqOۗv wNH+8Ið @99f9a- XjI܈s8pRZ$rOdIFBs 1Ӂ]V)>-n{ Lf}[q`l['&ҦRsVyDw C̪H˙}[A0Bއ[t\ֳ^^5R6sC̨}v.ZtQqGzW:$[/1*R1Zc1HFs' d.iG ^߱ɸϐ@ӧ"r$REAT+Ly<*t"l(^idW*f#&Y.:%G}~Šv=H)E@K&R_ZtMHtr<η9vTI,p\ÕUt;VL[@gH˩W@ B֘km}5iSD|F6R3'S`6!^OR ă .RO:/T#)@~q^F| <8{ϯ?E/ {*Yo8HZ`>[F~] W<;{0Sנ03 ˱]ql /c` x ebcYĿ{ݼwc@$AP)adE Χ:/78Hx/;uv+U\Ԍiz@rH2d6B5KCP2fQ Ƞ`QK >or'ٹbj4(L`c>n᱁@kO#7!T." fc5mGIϿx>;CVF\q߯TKv !1rYkx0̺o&!4>tL7|~Y'j/^p-A$.$ UTH~T<3SKJxF" =C?qJh__mq&no,ۉg7'#3ƐC}by&"C Ypn8ڽ8o;??Pz'j>ɟG;$gu00\4U3}p hh:=GFuDg1S(']:$9}V_FFh.Iv)JZ.d(΃w߳Q@6Sr^ALŌ. #_3 Wj/a?F.| ~? 7!.˪e6`jF.poŏ P*|$)g[nVawwuEw^*ֲKiHd)vKvf?T$nl2C^? 4|,\D&/4bC[ou':H҉L#43Y=5>(Hڤ˦m %5; ]C K:`Pw8?>g_#=ltA@Kc?m`0R;T!݌%ϧPVGvBPb EZ]S@K%Do tmObڗWP4ϳH("? QnʴV]'ڷҦ΀>[`O++1NK*G F]z!PvK`GN0tddb (!i䙓P!8ISX4ÄۆꧫTeץ׈W%5bD$ƪQ8w_ȉ-LIa_م Y\eˬ O$b:lX6`ڈtqH'H,ͩVn4} J0bzi29e$.X8 , jH/N$G).=23?![O*,*#B3wD%4)pg&jrT}%8 ԑ_[q|b^~@e3/[!RQAƦ F)e47zJW*a:xGCB-7"4fӲ T]aBpx*ӭDu{v\۫H̶$)WYEa=?3jqHJ. Xn!/ȺJϹ`^ "Dcd㞧rX/, tلsot:OQ~{麰\3mKE3\0޺_L9RË/K3MTb~ SXfG$5;k=4d$5;7Y^ n%jKhQ5j>3#$F%! i> y=7!Q{bԇ 4@bTDh 2SU9BV^A 3MZlMIjJ(R2 mj~`XDHZtFsd=LIiǮKBACZ\' +WOb$$0ftùhDĥ5B'߇f!|`I ZbNaU$#i,}!TV5TYN[wlWsa3$7˴'dJ=TzQ9(Y6%"gc.m\O+.~?Lb}VZJ}$ ~or r#NWH˧urk%Ko~E<[N|`WoO>6{T艹-az"TXQ psEH B7 ldp]4ؓu)cYt+%qȡhy%{CW*E7ׅ[}Ͻi7mC26. DȎ465M0LGK1ϕ?"#'I=m@KkoV7%8yJd#]'/& 7liL;K$!쩜c<6b+ؚx jqĦ]ðkF2A_: <(fEH7ˉ T& }P[%G~G0V=-IOV/;ɹVmnAOVd>1^K{;;3cBkY[ːryL[SL[yǟS("M{Wb/5B嬻 F̴',dqo*@)ίnpL݊eܽ'M u%HjMJh,)M2$7<}Ա|5J@M17[=̯ˠ'tdT T ,a 89f`q ϬvAQ `+hY\>EjY|O\[^ٽJհʹפƳ &1t)tiI-X'߄۩tu^'aO,Êm~Dޭh+I!+3oQQGcœyz_aA abCm$m^X+1l]+5!>x)0Mj3 7;7~}ޤpS+vlz,a!{~9*gȸ_]B-'=fAܫ7,ϙކLV E֜UV7 l BlC"˼is^`?q?)鸍dN?+\88. \U&3-7pٺ; ;)w Й?}Wvs>׵2|T3Y߃:zDcl]OtUͣEW>2y6]QpϷ/kDh@*Lp.@$ʋnjIa0{-b/ӆ2P X3p5h侲^h:(#ݕhӂDG8/v֔f20ܸ]V'E%iXt<|}yM698s00C[񶛧`f1Tx {6Z/Ax65,ۨ}˶8pL'%eT@wɾ_֨R; ; 7p˓DیY2U_&@f筞ߩv؂ cm?b;L3 cK$u``Ÿ8~QOD*>O@$A's4Oho,U<(C+0 з}ZTv,*X4v#;x;5 ARDŽN_Fqnv3h:. % ^ k( Inq8}{M…rW;TМF@q=*{p~^f)2v?ؽĥ4c /&1M<.IIk Uwi@Wq>6+L3d(ըD8 rZ.,6IC|9'\P=kk.D 8ɸ"Ͷy!*dO9 h"BK0ЍOD# =-r\rædנj>s4h) ;TBG!ޕӴǐ`a&=Ue@Lk#'E#'DICՓ8 詙j_-/L::Éf3h DR0kNb+U'<~wv،&dˎ2m %nbv'f>S bʩղb&QP]MTb9;^$H}v TEK}4Ѓ#n#cC3aǍwHjzN{fm-IOGS @}dϾ6\lya_cF%Fq0;Q>V ڲoM:BTU]`KR`,.|; rP[ phAuLn羾;"kZu)ƙB5iib\C?i@,w>cEهl u6a#]$\':6 ƒA:"ՙ$.B-\te Hbߞ;6酓 ヘtoeW#d&H։SRJgr> K?}7;g/n$H$DBd)X`ƶr%4; ܭ0C3/# FSO`q{Q;uSpRC18KKMZS T1'a|oRJNA1) 3 MQ׻!rxO{ ΏlW#hiC?(. HhǏ{wf@Dd^S[oi$ӏpqiȰLny~s3D @$e*"%WLgB|tkܧSĕ_H @1騣ӜA" YK"""` *(̶0tV QUX,0Fǭ ̒ ]cFqC("eZ|ZЭfvrc vUBf+p`?"M6>JH+FI',y#O  *&>PF;0&<i0WahiZKN+悏rI2"Bᶐk:%fv, B$ƥ7Q?q@8˰mjFzk/ğ9HL~4 R6fqck5HNhmƫ](|jTv&m ^S+|Jпi[92͊Etym՝@"3O%u՟<yrDbkx\j~ KYsiE=K2QkETQdᦗ4bVi?P=]/Q&$A6*q8zX^}ֈX-{qz|Z(/NRkן6[ l^K{XCb4)'B1nlrr3b.7bصa0 hdj"@5E+|L5쳩F/깄s#W_[F|02 vTr p j&EԷ!3b#HGp?\ yJPl7tvr"Ypk?GUɴ>$ǙhQ&gqLpNPDwX6PRdںmNBzB싪Jc89|ܻ|_C3pbɲjY_l 6m4Z 6(6Ao8n}JRCFn頪?110}=W[5p{ZouMr/]:@ܨh %+ٞoD*[al|Ri.1aaOn=UmUF$ёqA4BIP{=g? ZіeB|(M &P0P ~z6 1KW}$ꝒPeGWjhVL15Xn & P\̓&|6fX?yN>%CwKt 5)hj] 6iSvAyTT\a4~@"rj#oc︙@:J t;n#Y`U2Q@_PPz9u1Ox-? 3 [y%u 6C*gl|1WJUS b߱+A[t-$L/UBٴt :`F[Ql~N~tg/AE[9i~RNDTLGԣūLϚzp1!C)dT3lk8K1.19 1Pbq$M. d3jg/a5NHfu2L~,2 s'4 ^ycUw3R|~Ÿ:PBg7[EQsz1E + +ImE>(1Vn&j8W BsѽW RbzT.J#uJLY-7,9vWr2PKnbৌs5B"8E<GBftFQ>?$nkl\v`xJwdB53/ޛ9~WRp2۲+{!"ٜ=·\auÇ?H}K|Oe6Cñ6D;)f!T%ƳlcYwI)gsk2`f?`+d^ WΰĔ lPrsti`0cޡtDaI Q[gm!~[g!6p3XIyAoEe{DRNoPI]I+bVg. $ۄ F;.˰S'׎ϺA T OP̌XotjNlH+pWPOm:N:4fS  e`hy64"Λ{ I go1mz5PQD!{w4 t`Sˤei fm* {d (OęIq>&}TEy˛pJ!>4濆?`!' &ڿrbPx}!L^= oܬ0\"䨍8Wºp`juP`Ț#-tBKCv=Cٽ)ipRKؔХBӉ3i9(X֫@X戫™ۼ![cn:ІHʽlЛ4ShzaJ}`m[g8Mh )7侥La$!W_&oH No_s%rv*whl51Wpf̭#4\>^yCGE1TwC/az9K i5[j*?8lgZ7Sgj)qg6M4n#_[p飭  l"@F9)_v5sJk8Iez m;HE!"'?5IdNbc}+@A?ڼ ϐQ&zc젤Iv|V &+xq䭖$A67ŵ0آ:#*ԧ(?IJNG}àN, fO7Pd㳁j88e`̇qf Q|V+-*D^Gs\QWrZײKӇ9LI~)⍌&>3nng2ة83]Yu #-[+OwsTp=W@aL+ࣖdwf0qH8$`;wf*Ha%V\JrQ*3x"-W^J.!(8!D:XѱrKL2{Ͷfc~V"Ћ5ymfZA5r(}L"p'qun{oSɃ^66po*u3uQ9LL:\`ŐOL@ &w v#`g%q8܉sL]3+\w>1z`K y5X E5 avN6mm呺Z$?I{ˀK2Ů-&Bo:8^, w,qj:H@Q&ǒB e.g$H#`iR|q4cE=jL:ǹ:qp;hRDo'H75%MIjNX.x)An ITh:Ah/yT Puv= [xywYKz@UWtO&.…}LfO"vЖ+W\fM( Tf !2Ax|kKBJTRJfI<*l אp)HMQ$vBӆ)W2=3/Qlc>㽙_O,XqL/xlCM8V)"Djڤ.z?'1VrSeXr]̐=5*aHW3i U?\ Hn}$4`6RZh Fλ-CIgLL~ qΉM9yiTtH;jmFuֶҁIqzb V'oB GG?0UD+( 9?`_p9iR#._/Mt⤲ID2%ڄb{=S~$fi/)*R'pk'@1Ll$6MW'iRYxك4rf—ePw)(tk݋hŤ.KYaG{\7L>|9$ߤ4mVt>=t)mNrjS=./NZ|폢SWOiJoII> R%#;6,o_f SF]֫rPButPSm].4)i2P\<漕 q/U5uxyd҅2g/lFH]m6%?䆣\䡔m9aj$l\RcB*$3#CG8uC" 7SVJx:V˴ɴe[Jݜ;ZAp6[E:k_i{'A&޳0>3+&Bb mH@t!#=0l>HK)'G KmU#"D*.SFŞtPRaAy2 C*چ8$n}?lz6J\*!p*ex]\N,:Ǘ2NGԄ?iU6D9 E`ΟU}nodZ3.+fzڢl|KH&\hY%l-PR:k  s^]TLkfeh *G<#*^}٨tml#6]%d&ӀO4KpA?p$ǜ%f9GL67']5!ȧf\ד,Θr.OHFIT,&.G)y_*n8#%HwͪkV\7!2yR?ETdmʒ[>~w*; X8 }oWSJ!&ڽGw>1Ft)ldIo[h*xiŦS ^.w {B)*[Pl!JtoLU_V )43\ QYe84]J`IHt'r#`$<]|A#BordmBVb^pUv<[&}lx/oю8QX!2*;|]RMe R(* Cfm5E b5)E֌N[E2\ ̌MYdtJ5Dcw9[y*9J  nX_ֶ{lwi-^wSdgqobpl]W_!~F:+wޢW {q=6[֣7QO;xXUO#Tm³9jɜmX]Ni[~:˫:&? Zh8\^8kl;@+pzQ\ЖeLYy EhxN{`3=T1wG ,}ĺʿRju"bs8f,JN&CU k 7b697I=ٺ~έqjz!,8&ⳀUmaJ]=@gkˤ0rhң`=(Aw8oMS.6&9G!-:cp8A#y9ś2 c/*J#}FK8wc $:/C̤_ :+D™ϙ RU"aRўHվ_+n>INHvha\.6{q$Εj ,g-QSmaSIwTMES41d t(@cgy\NJEݮL8v Vc˘'|Ud@֤5+|O | :qcxb)vƈ\K1s-д.Anu$;وj Cu""4L .*2˛ )RTs2曧ӛ )ZK`> O s}v=qۮ)$JH1oc\DfYVq!sΤve rj XY>(͏ܲRrIA :9a_\.1+Fެ]n/' ; ˤ|}P2"}n# 'Z7[~OW\/:p~ (C1]ګk`!tH~?'V,9"(gDC[X1;iq'1}I_X\N~ IrF'9>|ڙ8 nn5uZ@)E=qޘ5!q⽭%(ԮyHA?p#{ae1js )à1lD/NfzM4& flr?53LӱZ3ojɠtԝ0h!wB`_}5~KbPe>W]iFG! kqpUW);:CKD wE :_f[TԳkP4+[3.cD~7]9?^F Q5HMp#dF1c-4`hePd4T9 rX:Ij,fZL]NA9T.8 rP40)9Řkן$4X[Y'( FrK&71ϙU}Gh}`K&TBp2;g@&G𧪑5Hm -_C+v>ׇ:ɎH]`h!B.0&8|{S054ѪAn[Ҍeϯ.l3;iާ/6kѭ_ ;״wjuoT0\U$aj'M9;G4NHR_!T%\0>չu[C|ici,\E$Y܇P`8N vd1WҩԟQC1qTZ3F`{e¼rc3rk`PP8b3"Eӓ 4v9!䋌Z4Y1ŧBse@%é;8٧lAp3|N"(iUEgu4VxTYa_}h4p̊ퟍI9;y!h|b3нiȁ=ШPlqE](_`?!4Q"n贯QQ,03xofY`,ҺϾ,GiZgmYPwfrԪaM q6׀ɴi mEz_T1c!Y%`\mLVDښ멃^9EMȵ/ Pcԋ eFq@eWʯ5\WDkxʐ! "!iTwn\ >86F ZPi{vkxy ULx& dRE=%a;X!%U<(~BL8P^W63+ry]"}BPtI[A{(}0MEZ(T bCKa-}ƹ7#N\u 68<äƍhYG;ɧ)S;!븞 si~-`kI:GUm٘jV&<{!O[Pa6܊Tܤt qbm3{ PǕNW6Pcڿ^h&TFq /|`tNlI"z<%-::^y]%Qzp'8c?V"#}]hj wfW)5I |[a"K=zu=t`!}06m#ُm[+~NZ,Fn MYI)xݽ[Z1_Hw|ِoGdhhkvƣ3|.= ~J׶z?bL16Ã9n%QS4c*zpkZa5wOȳK>ҕR?y#0a/PcI.v#x \r!|b8Lyf'PBnw-tR`3!ظN v;(cn'd/olZ3 EP[Fpxv>Z,ydSHE@fcmymk}Tuc( 4fԓ/C'X% % SOTqkL^ %5U8É.u__xcnvcΤ"C" 5FB!̀竔=jٍxdgPΉ5Ѷz@YyS;G}>.xJvNk_iG|Di$%AƊOE(6Fr= @fȠ|hMhQ7\۪b+,np(?T*.#^MwCX |9yl҆$$9;$r#DS#Xh; </~W72>Kra,eC3+`?eVs o"N\"RF2d ;z?2;5UWmv"ԭ3ͣ)Jf'n; s F;A`{5祝2 hD,o̱8Dm&(9S7X^АMThe^|Au? j D#(Nh(a#b7&_8B+ wׯ!R̐GKE%4^m@S|BϘ ZV:YP '5R);.)W"G=bnFcvYt'Eu"Hzf92#TrIՋCns! ^M/.$#Y8gq2ۺVEpgUM߁oȷRhv]HK|U'vڦYDi M ͌8|IR^[(e+a7Yˮ<8 A c2I{~_rQj'ሒ S 6<( r""* BKt2%Hbqp;ϝaVV^rpӒ8,NEւ)p.^<<1 ͦ_vESZ%7cp;<|<^A͊EHJeKq"䝉;vO!NVBX3<8G׫+Lӆ^|eD-*$ %w2kD :tBD^=R/I/`{CuݔC|5<_̫{F\y:l# R0篥GEtXT~3zej%Lx&PEI3l(>M5Vq'pn1{EPr⿿aЀPr!Bwg/poG#VIm44`"#,$ cי&$$7K%Hmw$PSLt-9g+Y Zo4ՙ5p j@\k0k15d1B.Nш*$6uY1ź06Dn4קqhO.qgg'1˼j!Ի1|$<vwY۰m'cFOr<였|cf$3Fys ']DdB:x_rdWYCЊF3q(WsTzoӮey,n\D˰uCKuxU+\mֹ]^hb{^ H^ض;b"үR,$0c]ʸG$NY_OJ,dē?6{h?WMءL z1* l2eeU;dK{sOm"V:{ԽjŬ0>Ju(cAկi+o"8!zJ5?$H(5rtac߯dKV\:my֣"?}!wfviP-%]r̻zь)A@ F jJrʄ)وOVŎV= dN-sRn;BFF3Oktt=م 2`BJ$>%MF4JGW^rRfۖZk7O뻔G`-Nmx :t.*P\Ej(SbzިIFKxb+DT9^$"\bK,?iگ +Z8b~/~`7dN@#|R- n)vI3HL^o_XJ%E6&ǔ z**'a~H=\L|؄+! A =uPMPK|6 *M9% Mc ;`jY2# z4i(O@x+[Va²BѼ?<~9JChkׯ2+x A!m,!Pm4zCbMtcZ1'z $NtH0Ԯo{3 KԵ84EkyS3Ú(7TD~ӘQd!=cj,7. @؀%[ҒuoP}c(>tgpn[ ׯ0XltsJ ^8d8qO j/랿xjH6F.@}:p%VmAVo{[NcFTIjnj >I y|I4ɷ_L Kvq{ČyWD%S%E >)<<]n ߏye M^MHɿO9$~9b @(ZgVwXpmKn $95]Zgs釟Dok5ǿZN\>DrJ뉺oyL:!D9murCrgrˀEܢT\fsp$Jݑ,xXpR:,aFipF}d€Bz;/?Ҵg -ubϺa+=l}zqW -EU1YtJ79cgslN$jWjh B$١SN|O{ol-kW?d<%{,Z1~&8}M걞-VN!Saeďx97Ƚ5*B3;:U ?r@`I=x*MD03>OU,OGfEb2& '= V(. ͠X.mh= 褴Pv`Ȁ 2*YZCH/lY7hqPfLsf7u~4,s]݊7ڜ5h%ʴDoɶ&w5t)P\\v7f,5S19/_s8{7yezsŖOdszBYZZӥ'[MU9֡3[# ѷҩ i_IS-S¾o >oT}hCreg mcz[#?p/ 2Z[_:( |K'J*9V{kq-Bm;4-Y'R.2]Yyrnqv1{2/C-4a{B$jq{YlS}-^G!#2p]~,\! fCG\aYvdK_?\QoB?>3]g=(䱙ҌFw LKjRqg`( َ |ޛecg̔W53x:yGJ+Mnb3rE RAayݥu5R *845G>Îdl}ȷNh,@:yKl|7<9?@pHEފ/=uN.%اa4iA̐J?ϜoNt/h ?&_JAzɗ.Ʋ)<,DmYb/XG0/$Qh`¥PaZ/]}|vW0" E"eqC^I=ZȤ` sA] ?'IH7nۄÍ IdgQt[|ڷ=$)N\ /nF3DXka}֘lšal7?#&W _߬yZ!?=֞>J@a!{rBAzL)o[8 wy7?36wHmt8we&["u ѭ ;=Nc5)S95"L A9N}ֱSF@RP-)<t oNhH(1uoвd!#~v?Cjm>CtOZǏ8_h!v j+h.PpHt;*uJbHG=dЕT-oAn Ja%';ui"Hpg`{pc;U`$KpzT슰I3N0|a0(\ͦ~l> xYܬJplHoA Lrf)?-ՄnDɮnfغb dI袨ٔ9rΞ%oY'}/WrmMp#nU-FUZ-tnޅxO8aƋhhDGzO8QN'G< AmjX̕eE$RhB#np#> bucH(pn nm(c#hXrWF2ĎnGV@VD-v.+x9dfeOf"<ߗ:*s_`Zw`9^᪓@#=#Qcb% Ie.n23DZlWYÞ#6/Vv4$& Pbk r姟a,QfDjݷ ]&(#N0M/w"(@m`j?Y`wT\m8&yɁEkq2%Sf aroi,"uE6-Cj2Y-/jRpXq[* %(3ng쀝fJZFTŃgA (~к\RAw*Rem^_k indK!Qd /xDn?.ʲEFs9>PaqǶ*r"轀 z%]ӳ _rӓ N~t#$ 7+Jbz08jE]$de"hۅIZ+~XTgZQJ|,K^+9UXipo!,L1MZ Ƹ%>d؇W=9O:k:u C<̟^ اjypJP?vF]u5Iuo\hd4%+tȝʚë*b5S'9#3KVFn#0OMFUkWdnY >R.6RCpAX+&94ܡNeJөܕB?.cⳆli7a\\ۏ םv 5ybaٻ4d-ܮo@KV #CxH! V#[4YSAu"4JWc榉URB -;_fJlxg@hd?C/ 9V.07SXاO#pI]h: $ ș4&xvoa8f̿75cSmBAG%k/ǏK\JTF+{W;W3k$l5膦2Jty`/ts6VfJ/ߋt&n4ψؿLS'Xk?0rDԉGH{Ⱥgq(UY#=FE=]#(Q9nx iA 1B1Y;. 苩CO\ʰ3J41^ ;ƲO}LxΊ  eW۬jue`G! )J9x$)>(\*v"79=\!JO'=% 3hgmީX9I@z-Lik xr<ؖ>s8`K~r *ו o4<]87@ Y VJ Ĥ[I*}N;=(oX1prJP CvUDkM8ޢ{`+#M9l*[Pa̳blx )=y7E8GZV1هH%r_0:O>"IQ<śRo(ZgJF4{)`f g&glR_1@]Ƞbr$w(#&*y,$%Va|J[K^u;fn.|H; R@$2isL)6+=<0RIwظIa&$=Ī@Ѫ"ArV [eA3P,1B`K21iOu7(`3dl%\޻x)5U|`eHZ~1}[݃Y%n7%!aԫ^ \ڮ=BDT}FTM>|\~X]umyLΣ9bH){0jns^o A.o?GVŰ<{ r v` hLVޡoӍFki2A댌(b#֎a޵tsŒ")G+t@?.Wmr[lӽv-!'}x!e_+]U!gh/cg`k {b:6~uUUPO au. F6Ȑ|刂mԭƫW$.b`@=f߼dp<,u+$7PH&%h}PH]}wn٧b(PJe>zG$ǰuoT s~If \5(DVnM6E;4>pX!m5#)P-řTگOb#/۱(o* ǣSHP'yYNC+P"OVPb+a¹ aRC0mF7?W_i;XYg(Z٢8jTa# QaxfcbnVFz[܁Ytq(%.by2MX{%vAejשw^Qw]~X#Ǽ Cn ;9̤ĕ@޺Rs>-iٿ$-D }>Db%lQ Iƒ;cpC>cZ -킯 6`<''2~qN$%%s?$ UM/$i?»2šV2օ{$4fwg StMOU:Y [Jd#1/&xL,@oG N$p!/Ǣ?G295^H8`!m)z#`؟֥-:A:E¨Hٵ~B~_OΝnp- UgTw !p h/wAI-~Wdf0.6g2t0PèR9"-_˄lCQIV=&K6ϱʦ6E^tgJKXU*nDÀxt1:(" u9%á0aL:];;D8ci$rý$ʚ&ϭ#QsH\@n#E^^>kzu1O3hdk$%e0w=t`"?Yj[V8|zg=)gު偁(4 E:,<@`@v{˔cd<oKP0*W, NgK P ~u2AK# Ouqӻ"K,^1EvD%/&j M8[gpMi9cCT͎,2|P$ƊQ|QsжaOԸ-* A}|n ̾u@.sXCCMdtUu h eaYcٟұX½u \ۡ ©۸W/vNo6EZ W{c!lDfgRj'8 QmlT ׸`|i+p]dW+r?OW.N},I^[՝HIQ=V{ Ne:Ѥ!ci(I?rz k\{q1 RӬ\eק wtg֓X#huܙȤœіseN*&P ?IB b[H'O;~ b#ލ|<aHK=5[)+Y͇^RJy-Ia6;-P*-]KN-X IIYYR~ "p!=l'=e^b^͒;u Y{i1D? %Q8jԥ fDcÕ=Ҧ̑1W'ğz >68-uf/8t" rwo`4;YxFhoo<(lt0yP yWb{CtFv<(em6GLsʀ.6 AMwYjW QpZ[P!1h9JNEpY'6(_۶?e7.l-U!*G|g0$jwC/M5i> 4^A0*A!;k6:>Žh8O]0KaIwMC/_s߳#?׳.ڀUc?vO,\\;|nώQC%u9'25 sv,v=t~5QLV^S#v"]Vd#ID4:(?H'uOTo&݀eXXgnɖN7T>ϫ@oYx5 eKf1:T"ʫ+y ,ɮzs<trKOধ2]*uͲTm2vls%quztaN,c1#LYx է.IGC[leJ' kO혝]YKTam_.¤tao:2-g}~`tX5&LbFǕb+p3RLF,殡X7'(rҏ@y,17Չ>Vjbr>//Q[ &1 ]ڳv:%T] `Nh,fn]|Ub8@: .71SnuP?|"^{tg|+7$b :m@`dVfOQ;ZbUZ3q. wm=d,oQLx%} V*dܙ#2SM8fjt5>;ymOC'.A^&7m9_[V'JY+ TcY<ROb+Em"GV.c&{#׃*wi&LR3L_urR_s {llww N{cY!>;g3RƽnlZ+ڑȥ⣍Ď#< %N =lexsܺ Kz1Gǜm*R}ńKϵ,3( x zbŹ )ykPŏG,YQ->n& "'mvpBkUfē%Lւ. cϗic9B岝(񴖢ui_jod]WNFw)6śi_[jB$}fY-BJme,;m,7>Ept]kKd&<}!R!-Qh[N"NB Q?O,"Qtz3 BVIVHn-oUt>XD7%ޅ̊nIb8އʌ@5Ck}.E}~Nϫs>*?3xSӅZ\COlߜ#PT)-[.@!ze|=pa.(OZ(FN^Up0 !]"YR1¾6Iu 8@Ka٘P?Z+u=k Ϳ}hH̒_s&;%tmԝe 5М4ǮGpw.;LA4o^Hko٧)ܲ^k9=T%6w0zU [u\= uZ8X*9(#C%*LJ 09NQ$^Rh PNEhd3*nH4VLS.`IT*Y%>R/% 4j[w%83ֈ2܅grI! W*OΰMhT՞#2bQMQ1_P7c e2^j@p7*~tUpUnTgiڤEM5g7\i,3huiˏ!yKys i$T9I;y^|2eljiC\\!DS9۟_:B6IoY#i5!", mkC2)'ڧh˳TszrvʬG5AQ66P=X.,7ym-gϧKu̘\- )In[x/\x] ѿlbk*o>ԡ7j V'ndh.^K&|zRN‚A}y~Y6YFŴ *\0F2PŞZoVE% sb>޺ܺtBS"/Sz.Z(Zpڨ=gF]uPsA'ĢL% B8a(NkfHK{qMfX#"=zNQԠ,Rr6SHU".7ΝF &4Nᇠ#aP^aXVr-)~Ns P ֽ2& N_?lW<cq rڋi[Bd_LUiXzM'8U>M31W9\?c{7:íײۡx*"Ctf鱙`tXY* h %PSԃ^+"jZc1LM?:x0 AG7g1yb%{"܄> įgU^t7LiΏ2N ܛǭ|vsLOW;:n{UZv и `|Ȧ${i9J_^(M*Zbzw3tPJG}+ _qfH [75ALr1 c4K0:]stby~9 L/a\ᨀ`  v/0MPj.W~5Ƿvstcqk &0&Kq1wC]SvZȦ.Q!: @L_@_˻{Wo3%!NRTNQvt>:uCsM.pA[N#·/#wWա٣9hD]}aS:9Cwd TcQV {Y'6q1v&"_W8M7NeH\\­.!ZKZp%!M>`ƶSϤ$/L$c=U픬/ܗBINǬŶ_]< B-ҌW٠ ttP0ҾꅫX`&6=#mzE*XNceĸV̱/lƑ q`idE՚;ug*."^trtȈY0݈aB.?ā~U6VF}j㿗ktC?%|K< B8N_7Q8˛W ]T`F/1&;;0-J+~lsこCqYs*x_5m@߭fk[|D)LP̣}N5T/rLϿ #EC6(qT^"cOzx, bdH9Z# fq@^[3~W0$ A~SCQHP,IL zojjػC d(Mɮ7 :ӼDZ u3iʖQk|\5( z=/]NyRhJ9VվiWT`!6O:Xڎ.Yt!㢌c<ǻwh!3ӪXYMj}F}+fPOS@!6̱TgEp 9Ďv˽x(=x7nqp$`@6*6rζyl) \VMcj>]++Cn[HMXs(&V6w%MDu ,CaHbUº %fz  jErY Đutl Sű 9lUV/4FQ4DKQ rCwm T pE-.c 9L??$H/"Ȱݚ IEOZX|n(i_ :oˠNl UT'ǘ–U'fF^*ŻrQݜ/hwR=3D Yڦ' Xl2mdɩ5?xkx{cc]F. (Ċ8&>Y(~U0xbEثzA}n6DɌZҏz>ht!ybV&]XZ_>F?%Wz+K'gN哟J Đ,@{j:J#9h]Kx߰Yޝcɝ"x (*ݮ~6"[\9KԆT?P>g1arSͻ_k|>%utEh}z99^᜼^>h 7~ `E*PݶG/{4~NaO\/Qe^>aaKǝ>Ur,唎X:P8Rŕ˒vu}?FD6}Bkپ1h2>pNOeÑ˘٨ **{u ;mFIro|?lHwx5I@fN7\ѤA3@Tb~ٜ4 suܮ#UڑQH[0ֹwRc*Ho4* >.Jz+[{aq@a^ ] 7pS?5B0鄬(G[<4(ëD>FD&cO'[ko| m6PE`qFB9`zcc`Gb oE \GRsODds?lQ:@ZlLyąaF~(lEE]w@oXTCʝϜg{? H}3Y,cj~V){q'Sp<3b`w6Gp^kxtFlX+nrٹ`:Lp5jau ,JNfdݍLI5~:WiJUS;Nse7HbQǖO+S,.kV` N5p{Xh3C;CSkeE 0yРM~ڞhr6PКzb.( GouV(eel-s<Xdt1elJ+Hq *П7JT{'<; E){ॉH&è-"=Q(ue&OG?}$ 9q^ j\a|6W-^odzԃzmr6.CGOxxo QjsFF4 x9y$wp@w{0qsqgZ OuI^1safEP%K|j AUBԹ]2-`a=oW?k] T!=( b$(֎]ۺ޵l_p W}8* cY`r|0E8`lD}dL}r\2)+Q!L:{Sl(lτ2zw\iɄh += 2T! \eK-aSX&`{جC4<~6 ḺҨ+~&eM2򇺯34BIy #@@usfI;*^C֜&W͜FZⓝ>l5"eR;Ƚ)ۯ7!T| D:OT19TgTd`FB)DE9?CQPRH<ӜQ],!aU0!vqZКpWkM;x>w@O!`v%5Zs|r);c;R!<_J'#ۃˮUAp1r%Z7BkX6G6գ+R.%<#mtaj Ya^zd:@V??F-U\f!B@rE{ Nbm]u,_FY;7B첱Dpiml9wVv5 2~650<^A[[yD)@D| 20=+h4e=R.Gr?!~R)c5c swD>xKb!1j/CC¼k.NngYէH;{mED6 ҷydEaDLud/qQkT*[Xs AkFBmQlmnrV:r1urZm` h@狁r lk7-?EЉ! 99s^ 13 a:r@_1?rQ5:rА9I GY+eW\ @g0TuSi:&ksF|MHS`CDK(Iu?̮ UKEr{|*:(jZM/Eլܪre/De EMw9KM{Ӱ9q50R^FtSIR ՠ2 1어6yK*22T=#ǓΌރs0V` - w(1̷9bo]-9p=$_..3kjگh!Byz@[[w^"trǐc!n׹ \ ZEtŇY&CuTUxy#O`5Oج/fm;mGnn[Ys0rܔ­%FmKНTt[]]sʮ\,._>YFce4LTJLe*T3ʏ+"9fbZWUZ^E["󿟁(0~(^t $h^TcYZ=&ezv8GWᏘ)rc<>4v6{d[֠h^xnZPnSKϢ {3K'W^)?!Rˆ1ɘxJ X8M*_5kq߮Xd@ϽkP)]K*t\ 3\[Sd9<*^Тj`<,"Y oߩ"gH+vNo-pD#>12ksY Dm' _:wڇ/hݫ\=k GMb \ w@pY>6|`?(Uj#, ~x@ 9WȽJC;f$ᇃ-lΆi2^A.{8\(F2>QRy]9&NPmnro CDV1Z`3BA'(Mpl R(=s]!89L]s/]dfC8 B-cCJ 4)ϔsVcJݜ2JԡzT\[|1hTܪi=V$NQ5FnVG ˼Y}A$\gZkiH(i}V$xWtsn?2{HshrF5ZB|ffecY(ja57 q.9Li X6Mwi!/rEq-'#0^yLJk\ sU`|wBui5ɣ3 s<ۅf} }x\7G1qߒJBj3xBY_&(M k2((NQ[4&Lw97m>5ӝ>#~i , cjlTCF_dH/e= /!J< 4æ`mͫ'%8iYb Uw 嗲~ |}&5iŇm1򃶙0PDdm"-v(M yș2KfVŎg xQ *&m1X`pLrX#HV 41NNAEj{ZoO:-&ha :Z Ue|KIKWYU/cNwA P\4W;r0=~Aj`V:O}tp H Ny΢T0(#>[xEӾ PN UL*(=BdBśfUElWݽtXMf UPpcy*0}Rk# ScXCJ@hCy:ݕ.f?-j*V×1ƻ=\r8@HpK2.k ٕUd0s4V9]dﯱ{,>p %kW{ ?n*Ec]dB_~Dh'RXM:iXSAhj8n_9s:cp}tVEZ-y?| pb1E\NU\JBIJ n{U`H~ ގ y`F|73fE6`Sæ,S~8ψ&k3[dKJ> ELwi0GYpm)v ܩVH~6_,$3( "&GJ*͖jW-;WJżQܡa#q/ة:(y/?[h =wMB?30 yuÁ kxKK&0l]D{{` ݧ}zv02ˇ+D>FbEOG&z5aѺ>q}q/: r {hͮ^}{#{N%.)1+Hd{9kZ]r,1A:_"'*qrZ K yrI.BTb:0]@5%Ur x l'fzPw x0yKBkwEFW#K5y;B -m:зJu4p{2/IUx5Gmun#'řur0>ӻD̴~Ж!\:*j%R$njgt #g6RR D}n20$ ,$*Մ $ @W +a-t #rcݽ?SIK%=Y zp88Nr׆O+k^WQa)a/ _b ]xp-K05R7 *'!fzh㋦YV-Mcы5.}>NY/:TzhW7bn*M%JfE`x#=d1sT`SMzṾi-8Aؚԭn2k\15΃+T)MuSJy( =c8Gv(^q( A, @C+a{V`pALX!L|xH[Yr"=Enf)-#&,Vb^p;B)]6~ *D@g}/Q iި$3Waf6FwՈ$g5rwwJE`El:!\VnS' J2bGBpti%EǪו5O3ɥH݇~b?.^9GܝIp\ŒK"jfBفJ!#=Bo^fgA}: lCs٨͗ifz/IŠ.6+$3xdo"Us/o<Ӎ=jkaK5#o݋=< 8#*rNٶ$~q=obHe|zd1z- A m#e#|xnШZ(o&m] -B+B#+5g db{SD@ mBvF2=0뼂gA긘_8y|i_n忑v i+@.PHdC{|XL5MN#8]/|:h7bC8o O_{UPø ֪%#H=ۍ>'d={@Y,Jd`'DibpJTJ򒹏-iQRvxr3q$K a,n))w08h;<Yķb(o$mɫݗh5ʩ7`jAdd=vRt}ǶBWLlyύy96oo5%Uun$]^o~{0O`mDQ"mN_/S yEa9WEK9m!bV .0-&m&9&V1@΋Lj@=բ66v{o;zU&Ad8w[t88)\}4V\G@wdhr tP"PQVZ1T5(>\g mUހoƛ(tn:Cax hOOw̰.6ع2L/gf`C֕FGHE'ߴT+%%c\^+{ICPg@kW55|*E;YY !7#{mFRÚ_@ҁTx;̙ˎ>R*KUų͟: |%$юٸGr{Ѝνjb#U kUsvS^q@9m?<)+Oԯ'ڬ 6_=kZ J\q: (Y74ZAFpRn`îGŅ)_К5,ڳZl qiTۥ%9#?~&H f$W%qfeAкӲ[7+pS 2# X.<xXAVG]CX<āv_?WܝK5O9DݽO+A ?OQ3f6S,Z痼0 A7% ?F P.g&mq?Ee43:$Y{XI}jzs l}ShP*-"^˷BLB2׃!_3Z*Hbk{kZvpxz*+Ôo~Q3.C6- i"E~1[*AJ;yRQ0,8 &d(M!'$BTUEG8HDӁϿF?o]gsۈP}3j+7ߏ 떓4&Sb&<_gU95ȭ]l|-T)g`T`49UӻyuƖyu7pqI\4zBm5'HsB;zeACU ."ptzH59{1~+ "Zꗒ˯vwo fgQ Vfg~\4IEx2N/R0}jzgf0 u}l%m?n4f ]fY8 9>d)S pAzl7 A^uI zbU5:^ѫd;Qd$W,;y['T^E ̾L,En8HOϏ߄Xn9{@Gl@gTrT6܆Uy${E1[KRќV\hʤ)ĸNBc_AO+ GS{t {kڲ BY93۹<3ލ§璡byk6r{'s5|N* Eh8\&c&5<3BWJ5ug)qEtIHhKh&_/̣' H[$ł{9"Y뀿O=v Ns+Qm6R|Ix >@,|a6AH."(0;[=P`,YHʑ##9璍cp×mAY  ^Bv|fD)vd#ޒKp,c B& V}K("I;9GsFO$f2mL]ϝb8&R`c 3bd,js̾,r(1-?;._fȵ 64_o3IG/ETM@ [E d23L Êт筐Ҹ͈sX>ؽOil SNZHG]?8g7}1"p'-`\ -=Hv@v zC"q>d誌G0t@ϯzw%:,R[")YG)aNJj ',]F1%qxq<{OiM+] w|l{88FW đŅvSBpɋ_pd-R{C'0qYьy<&vH$\3ʕ|lBj:M_-&,!!Џ%3~9pk(#>vs0I-u(BMylِ9PF+-бpt6͂HSZn/~MCKTJ}q|麇<.8H;*:ˇJ wrEXj[ ړaEw^6X"}ng&ŗ';408J$*)*3']>ޤU$ q\T ND_JMcω\q\3Z6:0OI{йbx)faC[WP/b]C\:-ʧ9_g4!}.ֺ5*u[?6iHt+.b^4Cy.A1p{\9/z#H)T Ј䏳*.ӌ]ƒ@ 1ш7"P#tX^\O؎_(,|^Ѣ^=+Lٚs3$g)E EBMLbFx5u>ښ[Ȑ _J\D[NEyc`.lcs˟?BvVff Vffbj)9[×):Wr<*P1Pq1e1Av+!]efntAHU"#Kl.wz*)h/X -> LV4QDOyDKS_>$mz v i1$S휑oѤsD*FVo^)],7]!r{{,; to#Ftkq%˜4}^-񖷼wyYY8i?uODʒj7Χ:! LL-G((c0}1yX[4&7e_^1\4j6ҷC:J|p+*^zrXÚȃ<@=@ ^[hG?<UrB %8 k߉ΕfLsce^ɡ6+qm&c(K;=%w i5xrzyB-]`&ƇP[*fB꥾;Dr?y.j:y(?YO)_]nQ8T{4We0-nl #3r7S6ӧ . Ú¹)tjdF/ء Or̢*yQ%܎z`ݫ]ѐFނ {(H_0Ƚ3ol3Ԗ6e (,_k| ZPC5[)ar7?+L7?+Q4BKa¤~A'+ُ]C8V .7Px:3*l>5{k@h#dᚪ6EN`Xڠ$-6!~}Vi>9# 3HAԑeZGȻx{Xvɧ&ITajX^]F󻌘i.Đ`>QX,BcHB-|}܁\!NoSՓFȲH>f _duܿq;@!&#d$G;O]47iK,˹G=3֕KtB?epˡʤhf֢$m)Fz8qu$1ji/8iyG!5 _\ɤ>"S&9@@&K\0ܪ=so?䉬R-!oqtRV); )KbjE׀\W_{28^ _'@Q,$~Pmd!`UCEu] &g.g5t[?0(C!ʲg8?ή8.ڶY0:4^۸;y),8ztxÝ`6e9RjFo%+`q]Ɵ꣌7]š旲vfd͞o),]}RCXg_ 4b_1Ƿ'HH~g:|^ܟq%s+>rg->^,23`xyl=[_6H#$.My@lp HNDT.Y+ j0!]9ƫ(r"`~"Nߌ. @ʩJQs3;t#ѿR)'e^(D9i֜qV0鉋*6Dc3E&7 MspV=^'Vz9..94MbDn4 gA-12n夦.QN%А_ oR&cΒaF b^l'5&cxӛc=^]#q oW>ma!eh4*XP_o7E߿ZylD~BUHΉx &Sydu b7n2'tFo=LhWJzfndgmcZ@#͞jz^$@^ln/R`YjN(y\k}z{ɵ?X7[ GQ**TWy;W ˹r:2 _h:ÓƴZ d鉫K5]5!V4u'_6 wǔPEQ,ё-#QLYE&ho,>.Wȥ8z &ް]XܕHGE Zn u@d9s{ar;-:{߮.Y7H'pR{fPب7Hq[ K cw2v]'Q壖2QiP/#-CGKsz֏T:pȸ礗U" er䔢ܑəTaEB ꀓq9CրqRfh:]ۙ6#l8M)d*朿v-h|74]̝a*,Z=dd)77_ҕqHD VRL8p@@b4q<^߅һ,ĩCqm#[O7[[.`zA{̨Cѽo#|Y7I[ ÙG&]aLE r=]0 !Cmw?ߕZBыι՟෰JHe(ϭ Z {#_/pV̏ +o A~ ٩Q$nIq;#I޾!n֧{8T?K&^JHJ{u@7Tټ:)%NpuYBNI0veS*fI$5%5dro!8fkhq+8oCȝȼ)O%M_ӫ /ZF PLɉ[A1Lnji?N' #*;wU⮦o y}PߖVLxb6mz԰ AUV,Hݰr߱82I Jz eՉ˜>~Rub2ңV@wZⵍ'B,䨢=Ho1OPK~RW X}p%?'bo, 9Fb(Cju§rNݔ`N"bi8E Q|Pq j27^H-Z3h]4QHitP4a;'$F˥5<ڞץ~$66JZ p'[7׷37 _aRT|bQsd|W/9#_G&:;}9s3K!K.u>F@mHP)I6oBz9r9Ѿtl~M헎=s`k@&vG}PɚJܪád1^IS]WLh.\E'_+Y^,R&R<)nZJG0G "~9f݊x}۪#)5JQ.,i-!.O/X0PROuИ 38TFАo.$-aUɂ:D:Oŏ*ǩ|OvtuBxwL_<.~r8a=)yy?&0L"P33zRZTQcs>zZ[ tq{/! bUR ;fsAg*&5z.- nT[󍏊C&þRNґ҅vbnzlTS /fs(jg ;? pcOox g>b"PJϱ!]^V%$KC58px^bQD ml+Ba).ԉу+ Q{^)̷ 9|c'|5mVJkR2Fs%nYЍêFKLpY۷/\M'i>F@C.!֍S^&(mɻlъԙ:glr1sQծXי үGRU*E~ic  h5fay,vX±>ǘqb9<eQэ!XĚE?"ls ;]~k=|ͅ{ІEZ|Ngv#}\Cp72x7J1zdENw)]P2̝J)"5aD<蠳~>o4E EB \߈P>QOش҂f[xHV5\IQ+g5aeD +Hz+t3_.}ҰV J7eOL؊K6Jeyq2_Vy(g 2會HwX YutގLP\pi"5ZNxZ"2NV [1mSQS|`hxc SkFZɕ5xG&j"E롪  =փH(jޮ_0sדۼ7<Df &%Ez*4oJ-8}x M"$ ~ۍ@pg7]qRFjzOU!q0r6xtLdO3U@[3{&92#n˄CtE*.`(?3΀`fNJ|w\G+]7p)'0C"TȣsZOh- yO:L5O.N6L@UqF̦&D؄I+d ~ )=my P^b؛NyQ FJ>D;ē{B$L؞iYG<Um"DEz4Y8ִFCߌE)1,KY|`R%QDڙd!oImˁ!= 4[= B- .CE$d)/]^S$C E{;ȪcZ#G)y? 3eRrHڨ{\1."WQE9E-OqB@5|js?cɹ'woq)52 GՖ -D j]ht'^LcL1{ zP5Ŝ6_XcjJ6|#a"fq~{+N#H2p*8xQL<}IJ̉M'D-bY'jۇB.Xu]&3CYAY 1YBO;{}o6qݡ`F]59oץIl%8vY.94#sv"Km_?3>/uXlM~!Hh 3=cR]=P3Gve9IjuGS%Ac;@YI[MQ7͸RvVPP &fI #/ԐO,W: xAT<>YwakU~i0 Xd`jou#Bw3ޮX _3ɚO=AZ>2OcwLߢYGf9UCO_Ʋhq<_: (X(ҬK h m|Uww8߃&\,Pbڠ2v d]PTVŜmZ%u tH$X|nHo ΡӅEVm=UrX8:lrץ)] ~>@J+D@MͲ,FMMwQUfVMKEÅ1 #9!byWF 7( w.zNps,DtJMe6-m;ݮ!oa@76u[h/3u[wQj=M[MH r20as]f9&"b6S;N^<0BC`o炘_wh~4@v^8ZJw4* c봘Y!\3ةʝbcfnNTt4 4+%Mi? co0K޼1zOi5 9)/I hV v%V7SL[Pf⸂iM"n_*t!XbP 3GDݔ' 6 &|[ Q H\Oǟ ~q8&n$I&5ƕ_5JѲou% )qFwh!I$ OjUsl=ȼ"v0Zbxy\㧵\U*@ϰ~MБ~'^mD]rkc`bK5dz1~gA_G4cDy[_z ճ~qVn9s+#v c^;\9?JQωK? "34itɼy2tهhmywm[Ӱw݋ @B` nRi,qA9A$do?i|D31}Z򷽟vUIU7&-5>v3J>*i2?6(*dNܑAbW|J&?UXN=)] PyY+#mxOabx9@׎=;>n|/f g@WC# vPiF"HLrVL~xNasH#f` 5j gKs'uՋ*Т R)0ɕxvWAaFRVC/5^xu5^&%%53@v#9)Vle@KGSxoxSPw}N3u/X$dAӎYL\ ~fe 8MN{;_z~9hCB\OCڋ@=a8?$1 7 r0C$+YÂ?kW5')6dM$NxmisPFy Msܢ^ukeTP# >Hkېts04sc׿'{­5'zA⇹9wԚ̺G_H}E 7j6;yt[RtҦs~;Si,\#>c%!#bRV Ql@=/Pӣa(\7qk9B@NJ;<]2 F3V*+45&H5q;.B{[ %y"Nxטk)cj&uuB+0E RsLNӅ7N #؉e7#̇+I01oL4F_p3GcVqUK6iޅ BR6Zt9kNG^婎o[xrY) {Cށfb*8QSX v\}bxӝfZ%A>ǤI& "1 v8Ϩs7~Q#.֭:ߓQ%|j8v\z" NG7LaT'G-V4NA?;X?Uh6]rmZԱU3CI><,sGW PvL˒<&ؼvMV?V-IcqiD3ܿ*3t&J]C/q%a*Z7|(W1X 9:ʼnhL U$rrHHiwЋu[`ݛ38[Caaq}7l+kHet]*e"ĎplTs ڼs,XڞV) Y̼3 a\k+ڨkǔ ٴd .uv+(Zp?Q3m^'g.3S"??P Kw-43rx, h[w7|Q>(GF 7E>ov蠖|P27a@N h#]L"6uCtӱd-ꀵ6 w q5-#X^"L0w6,N:&' N;KNClP/x*RC@ĴCr%CmlB-3G3UGUfAwDbb6]5 i!z0S:*,nY x&թ\Ȇ9/Z:1-Qlfv~jǖY(RZ^M3q=o{Fg#ӯUz]a:/u}R%U$4Uv5!P ]NGSw,ggb/bڨW"-߰UGٓ11ZOtKKꎶfLQd/Hw86'H$q|V9I/ZN[-QrNU E.ߐsɼ \M*,O%u9>b0 m|c g|һϷ`:J]=Lin|c:n~B 4sru.)hGw2Es  &K[~qpyÁ {"4^D洕v}H  }}U+C3 )d8nܠD G!3щ3>pZ=M0Q,@ )J:`5lXVdX\ ,qro9tu_|UZ5_;Ӑ"mR^rci{ )_MHEk_^\fCZ~:ځY>x?Qd-&!pm:|l-SiŏVPn&.46 @{@ &~ٝ{q#fQ(RTM (SL)FRV7PYݙ(KŇu]\B?(@Hz8`v)>W ^3g|F/V=TƴM<րj^^ 9M5e=+)_ }t'ճ[1O2qpkie*Xcn1O#; }P[4oMEޠĦcYFtb6a<Xq7ٻwm*d7 Z6|;f{![dv^d:r6Do ~M %:=5s:|hR/~4C,x[c3PϥqYQJڽ*Bq'$]?dQue^˙82TH-w}u{y="Fgrts0]KN2Q"U2BWЩݸʓj 3CKey1/rMӼ|gA c/^_ZwVwcq!hgZ^<7 IAZ.֣$1!// }~rOmxLuv~g;.Gz#4BET54tN3l9x0 P3G\ ,V?k-cN%8펨wyG*kHg' |pRrsk%urO0hzy|J©n贮SEwdُ惹o=0ծǠpO+\&fz&⇔r>,/[ هU(#\sN:oD3 W nfc#11/˾t9C [n71D6C nϪA }MƢ=7?5#:_{k گ3Wo*0Q2G\,!h;flX'ߒmgu瘀V#GE.-+~Õ lz 7HZ=1N$?w/0=ǃ #ѥ-ɤxt"BVzæQʚj9m|4^k}̷`q},xeLK\wd.Aٓ7 pJ+|}7k~O|EHt P͍*0U|)s3:4mJy[{0:}O0u֔:ܰLƵj 1B\ȄqWt˝ȸ{C-ZeaX<*b?7fցbD=mSQ4ҎT۳Qh jP;-kt77۩(BZ2(ވ YWc?fq_K^L,dlg߅ [MIeOuX! χhitU5eJ:{H^Fȥz)Gy6L[gM- ?9=uXPN,fdcHu'(mQ<{aɞ(uphnAhRNJX"񇼥6f~To}^ɰqF]4͘TԨ0 Т85]@nnXIc DNa46 %mr]E ._SLםvա<j\1t|E94DŹc:~@C+S #%닍bUxj|?@>EVƚk}1tgvPbIƵ]>-XEKC}[܅?K9E]$ҔN@MJk4?62.zڟ$c->àҨlM$!qG7ukVN ?%.CڊJ)>r$Aڄ>_ B bsR'HqHJF8Q~ V~u 9O+%@Ҁ2 +f񿒨/r"2kmbpbj2C!ț[pjDwE]j%e]DI䑣K&e(tbo*E +.Y}<z󡱥Z÷Ko/t{e:Ǡx^^ü7/sH(O0"X?:BZ|Qx$[Qu7_ Ȃ8Lm 3Zym.] <ȑ+^j9GxQ<Fs8#^\~K>QUeV9>k>/ ^h9]zcݡ#'7QIYaVHE6#4[zT$_vA*.ӯf.6i3s' Q\9 ƃv$D/ԧ<$LZ) (ѱv̇svkS_s_]k!=>*=Us?fL]I'ȣtžV' v;ZGWz؞C6-j~r@,oHM# jgzTX* H|Sz7% Gg^Tt,Hƶ{]I t^$N&e 5wϕO1ȫBSUŸnzUD#=[kcmvܓϰke؉`4*qY_lpQ AN_ c/'*h0ýP- 5>^{V5VEJ}2o|?A&9-'OMtUOU'h5ri31YMv4d#0%NS"s,B"2ply כǞ ,Mc;Z#U.,_q=| sd K"GSPW4-N%F~ vj y)* #K>**zp 0U*: ċoM>Ɍ"{tZfd *2(SSC2[@xPld%+שmR7a^ܥ)L 5(wsES.!пEid!i7X&&)jᭌAu@U6d=-uM]3*=}FGNu=xIs3fM}k56 a(q0xe[5JesBr!7`` L9'YxvȺihx'K&&e?3z#٧#&z|1HLLJ j9@'ZXw@3ᑡ 6U;ݱ9J ݡ(bMz)N@>Rp7N{DRw&>z'R[xpӲ@(I8ar~}WȘ”"R [(yp'1iJ`X藫kvۚOEz!¸k` )ӽR&˾6'~t *,M~R<ƌ2%i(7 9 ^]7u_UZEH ?s}xsBVC{9'j=&&Q([P#`T,l beDUk­xqI Dt5$omICOnBmsH0<6=Ö(1!}]l׀XVS}n؇ +ɣL ;䫩@9w?:i>WdpFtT d@}/48J<XMutx?]sS_%,&8eb=5)>TS%.wըߞ|2v\p f!(-yDӨ))"}O0-F¤M/t#] \ T Gg./DQa!.ޟXpe6~*F(tXm]ʳ0EDv@~J4W6Ϊl1c ΁qH75ozt1i^A,كU!B4߅d,j9G@pA Uhc8Tx d M Rz;G p"HWɿ.ktcPVzNӴb8c:FDŽ2a=襟׉)$yjQ)E+DXK%gsϜ!}R8fS"4})uNklͺ0dXemx:?¤ @l"ܙNX(NO%xM ErPn;?DY]1-pOV؞9@<؏m|:uզa,x8[k犁JS #$H .|sPLо,^);ahB.T(Jwa~~fv=B!`*YaP0AZi~ۑhxPeâ?eʒ[V՟)fTy3$[J U;bZkiMTǟ0`J׏Ֆ|-[˙[doV3:&4$Au=0N!! ;5-ˆ~wl&W_eZZCD;p7h\Mu~v)Zʈ_~hlR9a+gޢm([E_5}_5i!Z1+n";ZH_J$E{VS@lOچ]@bc198e ]*UTN8@PMV'H:9B}SVuH+ ,dl#iNP6r5ւjz#wjdYrrC=QGdtNb7iKMB5DV慇h%OLN#!'{V{n5vwZ2 ~Dx,MXtb>؋=k2#}[c7CX(V }!^O(?KlVA-b`nb]T\lJwt5mB9l[D@KـE>e(֪<%\sC m(yX w Yw5{bpuU=C振F;|_T(???%R.=͇ zXB jn aoJE' ڗ0 7XIhǔ_]tXBfG.Ͽ6U%Gž{bX_0gYJ;tU6~|jE5{JCJi%UPVAؖ'9VVMњ.R@aPW1_[VΊe9_*/&3SFϽӻz|uc;(2Fc#$_ &PViklF4C-i6! ގrCfbM )oU3@FWY==AM 6dI 4c@wM׊'X’l7AFZ180æF @lSp nZ&B#dGo ",ߋrL dS%f` PdaJwfK̇BPI7ݸ,b9-KZR?i"Gio^%jJ m/>,U@ Js<=D*'=]En)UKTbYՄ\;ʳ7sah^B7TByq%~1q e_#_l!ie #<& qjdiO<,45?w10.B%}I"WBoAu;9A6ɦinDX[I_6Pvdf8:d Bp/%͉">h1Z B*L_\UVW9\B)P,BJb7^-< T[0; :8k<-*^7IT!J3GOI,~daI"N:{CN8BxPg jL%y$XW%L?7QzØ㋑W4Jcq)xP$Ѽ)uծq0,9&*ZJu/pj0`(K@S 7ڍ;N2*3m..%]qLTՎkH1:\u#TZ]AV`KPYv:؟1 Se\nF!|{ pcð}zXU2zNݳSv7vMgyg\ND1,_ɌڸH\tIz䰭ux0rucS&9h0Lvƪ ųGyD (soDnq߻G!l؍lʸs)% UDJg,W9c0"W$Hd>q.>|w}OBJN֯D ;k@gP'/ HHPʿY;s@ xx{+Ys@B<(~hylbU8s?Q䊩 T?$n~Tj=* 9o>#%% = |O,5ޔ):2*nmeiº8ov ;'`7b5uGP2-!!7k?@wѻ * ߥi<6]駶:NQ*10#(nҴE)$Op 7*6}Bj^gu])|-jvynu,-%`1e}X !Jga\J]Tk݉ m`"{V79K b':!n(ެ/a(Ƅwh}1/s-B,ciB)*H[w-,_?N  :ks>\-*.HfފuhhrЉ/t c@@xܭU$VzU,32Չ'D Γ2XA Fjct5mh!EJ5BDa4wзen^#Jj+'sl%F+ַ4+`E3H%:u}a=oƘӄl^oqW#zVNcާ3 * td!w֥vI'>x,xΜ4Qаܕ3X̵]"6uRV_b ڤ.Z1F=_X(B="s*u:ĮEvYԟK:m;-iW@IIgtv1Ysmrp^i Y-jsn, NHTEm12ag$gcNi&DS<ݔo;}lb'E1( >J&%Ɛl#y`y}+S(-\恫7\7mwdh;ǧ ;ifC>$O4 # *ùZML"qABe`5}-aY5y6 eBK=ku-OVLMyviS$;%{ ko{NgR?0H1CECgwP@ oTyYU< 9[IA tݳ1ֽI1z7"m'6yqNcY7eS3PC5b@r6cIdE؞h}>|0g,,vDu(%Գ (b0prSʛ35gFK>$6 ) 64^ǖ7yGud~i\M3b֏.BhglksC{.czDUC9u'Q ^?:uҼl"~hK+O}[IYKD m/5V-j*q`cXr W'k㎄A?9)-jdy OB A] ǩ%cXQ[W=WXt4[+2,G:?ztʟA˃+]dQ_D]Ko=ײ]tq] X}?H⎺EэA#ѦR'8rGq-AdiR7Q?%K=M=Ĭ0, C;k6PS`$dʷc5k4318eY)0ՒtmķE&43_е%fibY:~;Δ5;jnO80ʾ4L 'lZ@fT?x,bP9 BQt+>1GKF@S'2KAk3$F [~'d= %AphWR(/ (FfSi&@DT%֓%ҵ?[^٬j^|%4L lˈidCg썟"bDbg:%B ]DchEW{#cPۋ˟(Ѥnеh/N'8^ [mH^?<:W5;]m'vD8"YTŌ~=fh+S2]"Ap} \\M>/{_ _׀\X:ƴ;7O ^&CR r(Gȿ: Ϗ 2]<}MtÆHߖIx}Hc:puj6GSZ|۞4>-+L珂-U<⇩o"چBnV5۰5Ʉ/d/ez7a1WR:S! '% 7t l\.J+^}\u޷dĂ #&H*ڲ#iuK~9$u{ݦ(_ohd\kэ=+zV^E "{綣^f.@D"RIUyTiPHz= '4TNF uUե^WB0viXQѫ.r\4}OwQ<Bvy<K`1AXf(I%kKaϯגhV{8`{qM#5-2ar:~7߂akل9 n‚V ;JxW9N6@J=ӏi*xȏߥC'JqXAUWy֠di%Ž\obTX׫e&jm}1w[]馕i]_:Y0^qT ibkR&7ƳM!v vgb?ޏjsey\0z,((  aN:~k@{WGިƙȨٽl]+5Qq Od(OK'Y:a:iEnLmQf#aldC]%$?¹ ?L?gczm0m>FuKZE^varN$0~W jxEcbE}.|P"+\o5 2r\JFBLtV7f޹ߠY aO,*M&RiqHܠq¶t>_< j,{L͢H QAﻂ)~i,=~=4C>?hzswmW:^zqn ֶI];IovcdP[@΀⧫PM~Bs!z!8V[^8Ϻ~-$e8qH}nϤ<7tͽ4JpB+zDR9ήN?9!Tv>$sAψލx[`޳Lz*Yr+شTN5W<*$Y{ìWډ=<@rkCi]؆|*̗=x'UpX$VzQdl)3lR ʾ7Qt9̑ E8 _Q/)+nRk^.!`7^Q9cDg/UqZӺnc|%'&v#ǒ[ 65ibZsx:UStt-A=<+{C2fQ[tȪPV =F#;}3ILe 5Gb%+?A.r峡,r-H fj^oP+s@uΉ e/xIx1@|œI EPxiP)#J J%q%BΣܪ3$NytdU^\qT(mrc㺥a^˝w 8+f *w"5 Zٹt/X-IRw: \ agDUm)NFp ?.+|c **O%"B7^WV-`O"JQgZ]VPDuBu~uQDL%).dWrR :"xXx@^rsx5zgZ[ 0һ[;+Aӿѯ,3u_ iPdLpF\$<YQA*Ax 157!15k)d BEIƃ-b.n-ap<ǘf31[owy y+JzfaH5Qw1N:;ji#~et<wgEI%Mc i 5jQ灑c%B/&LBSCD]|s_2khg1 oLf̋(dMҔA?+)%/<7dkGE @lF/5(bnnߗd Lw1GĴ'.o˕F1! 3esG^J'Sul;WZ 7+dQ|>U;4ZC`w~ MV*]t?cSzH/n3xJB\/r /7޷.r[T,y3(lSUZ̷{8uOAuC<.O\W*:Zߝ+vY'e >GYJoi Kc/Miyu^Q8X ֥}Sja.|Ac_7#0܆Eײ4ue2r%͗/ݾ nWW#< kIt/4]& PNÍRV$ŝzmsz)-Zkpt&1(Rrssdx1j а%I2mtC;#6Y?֟p֥>v`r>Cf4aU;5Ew N@F+):(ܢ?Z屓&m΍uhDy&ƎKNIEgV7ժPis"4p1Md\5n3oeyXO8 cʎ +mqoi '׋e 4Mz.aEŎ,2-tTpCVdu_bJg"<1!$= p, _B2(xa;drQ 1kA=]?_![?r`e] QIHas$vNv D :4oXC Z}X|v C *f|-lm\BR82cntGzlM[E,3^6Ij:amkFIA8NlNg\P/6g=cXBV@k$rԇhcY%"K5~Y|zDEKI-IRȝj&H;cזȩa+<1ɍ>G46Rqa8x1|8i"\м(J[=wf-|JGvOC|p<*K,-!@lಪ]D!7=u!S Xx+mѽ =Fߏ`& on~2`xewVK]~sIRp E *.;,j7ʯl}ob`((,Gv91]`75 kXU8iqYq 2ƽɌ_BV8:a4}@,KCbI1(wʄn8C4%G!DH8qK|:>?^+?"fyLӳlO?20ܺ=o.L2!Ǥz}z4ST"sXY8XpFd9 AL W[E5Z=ȷ>\RA2U9X^ݵX|Dp4Js*ŻS |Cdzzd4YaSf+fUx]|G*}A&SgN-l!HJH:Z?9]o"ۗbiztC2d>󱧮CWmȣiS~;(؄_uI|F_-CkGv'6q>I%H9h720Ei32t.*SB]_S>#^/@$ZVÕXFm@KO$kZZB?P{S"Bk_Ơ$`#) tC9J|b]#*|r$?ݠ( 5jPtkXnG)M`HLp$Vd2VJgRN}#e[uZ!i}\tN`r0gp?aXdh-xAԛKZ;ݿ}+'8UҒ0V0vb/K_0t155 @G}ypT ~MqK:;f4SMq24%}7y޶eCJE7K[0:DJQ[I7qʯ}Sտ k-]bF5M,f*ME~3VF<79ֳ}*uMMgX]I(m1$U['zPqвKkjl5r~"0~.'2%JSB)w <,K/ZqyA]#ENպhs;~Kb6"2WTaQjb+yk0*t 8{+k` vcm|TLèGc ]נ`¯z֙:@ fH_)ÀTP`{@>ލH y.^X8JaRB0N.4A=@:It;> scVzsbԞ'IpoM>QW䥹ccg Dt>-j/"ԉ_*O Ix8t:'s= AOA0o?GQpJ蝘{ *yJc'ݠQрϮἿ'ܻ[ )UExg`ҮU^u3縘Bڲ; *;Z+n{nڀُE³R")/\Vq~;@3}!=*q?^R WaP3M]0nAA*ߺeE&0t#g&d >8.%k.WlėP{G b)YrfA`_9O.WFpe lXA`h`vqz>(svx1?2Xǧ%^m) ѷ[L6F1HǓ 2G*)Uن5,=,cxm@ !v6q*|ۯb^nc- $9ņfa9w3gUr=b2!Z\x3YHw TyAkAmL1:?=F Bk+sDi,\q~MѺX!NSU$0^FKy?d f#0Oh7xzA@p@i hDےQlGS~N0q2Hzm[ oRw2r., %~<.r>IH@x/S(LW9pҀ(mf9Ieu=oǡ{BP@@h8XM6!;;IE? C E銯EC»Wk>C|f^TZL]SEaɊ?:‰ñ9IzWDbAxZfRt &EˢSA4+;*XYvQj򦳸O~<[ &_:Rj/|1nPhRC7G`C"קhcz k>Y*>tNJǢ$g¶ʋz ySo\[S!PNkvwhgBjU  @WsZP Xrh.Xjj/}22R4LޛIzJٰ -[$EVT%uÍ0hMez b kD U;-K?" .s@ݙV (?`B QMce_ոuhڳp6nߵpNǒr@v%$*#) (@C},Lqiɱ@~p* 6ir.];O搘'u"b^,/#>X`AsQxtr=(-,4ZLEV׹Xɯl&4 ' .CmwU| _/eSk2ٝG^ei/-Ŕ8Ơl\ѫPٗөvopO4Eo"jީjղ`̗(5OP:pۿH0f0yG ?%^ !izԝW욠F|]J Nr*P8Ä Q *b+%mjJf}}o Gg`~z Eag8m& ,{mtr0t B ]}g**.c)jyOg嗓ߺ$';Ȟ2ho=t)$E,#.H<М][?sGHv66:?5t=9 %WՐȂGˁ g+fX;8԰Tc\o5ܹ"C 8M?j4Y :m-tZĦ}bz< l`&@C -SHK_Vb~P.ceDc2A,|Eniz%ܯ r S]6K_T}l#54%z}h|5:Yr$~0K6j-&` Pb'e8C_Ⱥ1÷'B{%UlbPmT9#X( Sn͸t/_Col$m]_g#H1DmͤVbo8-;n#lh^Z~^C X&o>ǾNGWF@p>Wn?;j1GL29P3dD_Sr\q ǝBM Oߓ{:l4T3fU#%u4E򷥝7Y"*KL (Y^$pBew>f|6;(mz+Wb<|7@W4aKFDdX:K5^orziX O_Z"oD0|F 4OhPn3:֊.AߍR&4nZ؟=.45xG$sz *"4Ta؏nJòAapX^y}׷kP̗|5 &,wzQ e%%6YA}g˛(}wjm`:@,fOu= FWRBJs5R9i2 pt qkCa*.WucvqNchN CeT= a8GJ#D+üĢ_}xhe$#*چ,5-X-X[(m^KT 8;5ݶtnAy{lf C=p;vgC˕ҽ]id +Rd WR87~Hr[f`wN+~B_H߭9=6Sc|c9 ң=s>w,c5St9/ǞR'4 TZٌ~ܧA^GHPvFHpCQ=c ~MۅTa.qyfg#s6Pc9 x˗75ʣI'ݮA8PA1! lG: |;#%(b@wPHޠY_R =~9}FLiye1\N.bf ufST%f5~pzN2 _ (*&1M widNzkFRWn}!+')% We̋œAɍL&Jo50<F Bn
X`7/Sgr7Nx>gLAdZ,T& xn*b *:]7 boͬQ$@'퍜} #Wԣ1!tG=#-!`a*dx{\wP\Dy5T<"QӄBQT|vZ(&3.n/cdQ`zL;<|:C/̢)7/$"|{Hц<8NI( M;@^>d%gE 1{D[H z0J/m*JЄhxA+lK1>J]Vd!a?e?c!C_^D05KPq+^g|{ulW3n Q[e˝wipȃ[_U&*Gs5D m?X.5Ju%2}|Oe d'lGǰui +gחaj]w]2E8]H"i&|WD5̰H}+p'Ft˩(}Pȧ}Moo>EB(!`w'_ct혘r~GU/gvEI2Ev`גq#G}DP8}AؾH<1' Ax p_ٍ|Am 15!y WvsBٲwᘻ*9^82 1goD7sOy{ȏg^I>gJUNER,V[ 8,L&ϝ@6x3#rmFFãͅ %*jGBtfR9fެrf}T2s HcY 2y?jA)(l^^.m;譨|JjEgPybpr.<5_\X (d}X2FI)kZN/A6dWR1TAHRlWS33 ;`ӓ/1˞S &̷5LS!D_)5Qk/͔)t78R˅\6YA`,7TN1{ƫ^t qd<15()[ABS-9{+_UPUu XfP_H;C۷ϸ[w_~9hC|b{ls1R_ b ̌oMaCm [bp5, 1f4tyGЂfZ_AXV/džNJ:m9K6> {d1q~}ϫuOlLaJlޱwoiGy@!/h+ОhdX]3;8OoReτMp3Rc?n{8lk̘/%kTj} ؓW~ 9$?qhlJS6{{==NZNZ;J|>&Wç^T@f/>Ӿ1A I=skNwŃ}o%ե)\ҥSMG^gIHe 2[E~nps]Xo ~`⻽_zؾdbfER;ӲP~&/5߂DhU] 3ɻ<ْ2,[Ql= ; 7$pWEO JrN~*,*i&i)PKn-`4jeח@ XYԪCѫi'8Λ%A$ 1$Jeċ X<V>Gfi=RXP ^s%2vO+w^lڛp V ?|sd^Kk:1rQ4Rǥth,8H];ђG+*컝^p`o}>6H0fǡt S. P6@'&/nJԱrns<1ugu v4iBe0R(^G.Sn˷+KH͇;G[_PϠF;ڕ7sB.] o8 U%d~'hk|x0cAff{$b6x3sT?ȟ&-5s\ᓯ%*;[yˋL &u?I-|mb,<7]KJ( \ZuFC`=_8w:^e6r+j=).6ASkǏ0p>"یdz@c5rjEUĜCN#W¯V=Xďj>~[v ={7%L5Y٣TG1!hg~$F\IM4TW }kb.|"BQ8fҿP ^syN+ޒVW~uPr'#DG[Ļ~*Έ(WSȅ㟕5R= G # S7iʆX<1*F98%8 .BZ za`O+ת1w˱j ۟]δ.D_ӹ`Pk*r"t FXilz~j910gݰ܂H˟_LuTZI⻣*9":ɧ W웈1${8b1yeM? Bl2PX72\[HaВpԘkF0$Tiq1(؅W-SJlV1K)'&)q4 OsH ]?GL˅Sj0{=WB?>Y]G ӓiTBzJ@29RU;|y`FcE{Yń3?fq43#Ū#R8 >G ay)7oB'LWƇڅ':fd 0' YCS)AKӻ O29+i;C㸹2*0ՉGTkMVB,evtG(쏌NDל42g>Hh)' Sg .&P|V FamS_kl$"2Yj-4K૑/46sy*e%IhVDCRx?%vb?i43W ÍGj#5qsײ~WWUw5QrpСDN<,&T48- #T!bk*˿pP2FOt2 m xZ& ;@HD5V&b нnY\[W~ȶ}R)a9|*,&o6^TT+U# G.zmN6 @)k̻B|{=Ij697sc+:.l?>JAؘegYZPK!3=CCsnd-rawmidi.ko.xznu[7zXZִF!t/JOC]?Eh=ڜ.+ pHq*/O[Qo; 6X*L\)SAXyԳ+5=xrFO4&݂[sw48=#fs~`'Q/zStl9k7lݝr/cUh ! _bm)v/H[7ǹ;m~t)kkCIɅ=โž6WIJ $WՂ7dkDNOWtP9-9)is!Y,)!1#\ Ś XDv0~3A}%ԗh2 :f&q'Q6M)U aB|ښG5<ƈlgb`'鷁݅)Sdr1gK^3l2<ݚsѳJ%40&7yaDHT4?|er!I*DLjݎ3 _;!PG[ U1?* & yVk`51 #SD-.h(<lX?T6&&kpW./k^i\3)Oܼ ~%t|A #=i?E-K9׆C_ݯ8 "˅lZ4= ܫ͎`ݯ0j$r(E+P-p']++M 5B(1ݹOEB!ON' V?}꘨$(XCJ8I$"-UU〔ϫod淓MwBP@x?VwEk@2sa7[^Z|[ƫ۶k%H/S10!ҚL3qq$~Y뼠a+z]/X<8=a $"$Jΐg&q;YiPcQ6^u)p 3.ٷ$^P4!53DBu׳~5|`_í< 1AV^a?鮦d fY^Cj(d`یeeAu|J!|޶G'yp3=bF'Ybջۄ&A.ly_*6SP28L=XmhL0j;bV|M$އ=wե|KǣhP;&>d"[@gM0}$wt•=ǨvgagߘN2-* 5D [ӱ۞[BLc)m_A#2 )~!4?r %ABJq aʬg`1 Pf}K( рGzzK;)j(_]ar%)nOWP1Mh}+Wm _RB3p>SrcK= 뺁 4;X7•5iB@y¥IvpY ~9P Lp)̲܃0i, Ix/%,?|pC/Or95%[I?o_bi)_ah% 3z?͏ Z.Xz9ž?VR2f.ttʭ!#ՙ_u* C&m:rjӈ&ꙖM-ڋBMQW HNM*$ jT2Cl%&*whՀFtvk#M{4ѱ3 C=d{ˊ!V%T͐:?A'%\A}BhN F <@X{J9P0 l*CO%_B)D"rp7_G8P`(B~m0g ?g6#ԗۯռۧe1n6ZFC*R oqoŕ3,8=:,캁,Z*\8x|PW- X{IQɆˮ2ȉbx0+O)EgHl wDָlaDL@,8+`)$f]{5`s(M,JE܌Q92 s/NZ__jZ? ~bBܴxd Plkvw!_RL#?;nLKv,}KWX [lISAcتJdL$'JK[L_#o#45*>)`!pcR ˹H>!3h3 ?|?{2kED(Vt;|Ήp>n*>U<]OYxݢGpj1eFjHi=A$I9ȣW|8 󅤪lCڄ;;>6K.L ;1lJV.fUۮ:Zt()s)4@2 (`cMO2Bh1]FB $橀B5uМ rKep4{UДƄSoxC؜P jh;qB@ k >F($As CN"n|xS |u=IJM b?P;ɤ `_%opxlpG%nrIA/dE]HDrMS _HuE VWDⷻ"hڣf _k_`7 Cg\H%`Te)ԑ)4yuRu5<-h)rS;k_nӼ ١Yi3_٧u>i SXZ͢St|_+89I?;d9{6*3sln'X ;ԓq=9gJÚί1 Du_*.4HxfZW*gN?娏PFH Դ0ka.+I`okaxYD~Cn N_$5Qao N@plh1 2-ۮGҤeH=єf5s)KЭpU?/S#Ơ2yZ/6 s벌Ȯ#xSrqfjS)-0p0#ёtH~ʁy}iahpYq('#LCFܲh[ŦoXݝd3BXףpz m`MUlkee=T )RJqlcGqvRkpza'$ ZbB||HZ5?Bx5̮e;C@&Y>0rѯͫ8KbwV`D(re|?l7'my7yGezjy3-aqlYY1idѤ1‰e0!̹bgZ|=W6n: %<.o鍉r"eh{TOb]n9ŦͪP RJů{hrxJ25ahQ3~ 9T#k2B( Ž9^Ҡ:&n/'m*rBrs\+>H9 o"Xr+ Z줱 Rٸtp_h(t$`=B>RmnYDۨګpXFZqV x3GZ=Hs.3|%pñ!fgf @fr]}EGGNV DVY'z/ {p$Mَ-D;%%>BDz85T *x=6 CBDYmOYfyn mœLq^cvx0Z+'g+kw=434zhX /}H!&EZ4`Պג125D[r1綈5y|j#ERk^0!ΔY/59Sp9΀cٱ@&f yheV 61YGضC͏AV b.qa46Onȸ1laݨ21$ɸd x3ך;K 2PͰt i!4hwÂY,"{#ǺJr{IYF{!sw9\;wAr@)L.Vd_J[-(jC]/\Gx˹wΟE.xB<Ť;pasCWLVfk+,ٳ|bƮP_L.;([%C`|36#Z0b߼x ?` =g|8߉FHHa&3> *[WA?ʥrS% qC^<% i9˷v8QY 8i˸/H! %07ce;Բo(=N(NgdaL8eU 7}"w7Zd@dLƭoF elsZ!0`a ڴ D }'!.\˒c ?Ŗz5x{_kj(6xj.11 b. J1l4H)~}Qp.pCh.M,hy)XS-Hkm*WƆ2\f|ha?Y:Dcpm5x!N;F&Oe)ى8z"{\*m:HjH\(Iތ$ڕ/rك9zF~+(2Io7_}<9t)Eh[o* Tt2>z>-'q@ 7mTJ S,L럣yX;DV |Y? mN$ЭneyӇaf,umVIv#7uø&g9$^ooDl`|FCFˋJsf }91 bDm|ҟ$[svW ~\45 GQ;YS Z5V9* :#:'>lb O!&@="!R:5ުJ#$mHV׶AsX4ھXOeQ(Zl}BeR҃D憁E ^ п\)dͩT%*?1$ojb:Q|Iz,D!Oj݀:*|{-y nvЋz(s7s}.Hqк40kBOz P#,: R/7M[ao5bi Da7 ]"cq wޢ#}Kʀ`tp@}# =ȩ<>!,2?(z:tq l[& }j^M )rODU֎_(Ź*x"kI`2DbHΖWhge#ꄆ֢F*CcM6 {N]]na0-_TOOKZ,ns8b^L)ӯ%ތr3e[M'i?ŸlU ?և-x DM&6c]z_ b6k0mёqBCw]#X觳E}D@/MapJt2Mcқfu`V-fM%GZ):6Is<(k'~(15!4)myby؏E}#5MnJ6I3nF->gW|ήqK%+v <䏜ܹiԺ1F3˰jcG%=Jڠ/NoGR}KdL j5l5 U1WZ3eh6L ]\u'o_پz 365j:mi=w0gU6 -Ԉq7G?qw1V󢧕w;AU@Vpo<܊yoFSy *YӉ.o*Q߱Bx &=a o9{ljiܲ-8$9rDsbYmxY$X?f匼wmE?zfO5o Tt3Hx!WƭZZ/J>p_{9z- $*"`ч=ݪNH=E!ehRj:ddsuSCU'P~$%p62⬡Bei rkfEfKYò/Y\A;('-Bk: B18CK{ibRՎQ_a9vu#KGD2L.vAx?˒&]߯sSss3HUW3}Nq'liE$UtDÎm%lL;v?|^GYTƳhɧȓnChp/IW^/(zDt 8Hy&uRϕ2YЎ6 #a"h7S+*'@ﳟvbZ!\܅E-b.'l5| m3XYUj!WcNXyڡg*@ְѵp@ S\%N6FcM M4f 9l<49Q`c%chHs |A7}^W(fV5=apq#RR`(q?&HːXhL2NsR^O6ɏh6ܥ8Qv^# 8 ݉RQIJEWF?[R-o_$g*G ,[jh=T=j9M8q#멃l]R`ϭijNҠrf2I6N/ٽ'> zs1 n>lBouhk,x29TfoOA%ӱ$S£8.Ԗ+./^.JR3<ieɔ۝yDWoni(w9%+, G~>=cTfCG;E*I*wY'@!tW|-W2/WWם ï)%*ޘ!} f+4F~%Va& !\,1,՘n]׽>s:!;f<_4K2%ݍ&Jn-\ 9S@rr!`@6d> *'A(S;ZR:K (rLooiFўb!=:;S2 dWohkոY8lY)UL+>Y-M_coEe_d,ZSh͐3.jrnXXd5Ka7Q "]G -U M?Juc Ǡ8-J<as`ц q\DŽ-C8n7wva`%-7/-! M%PJ(5{ XG|N HKZ >. pS{Q/ y;*rXC+4An[B rZCֳ2S[)'OL6!*#}rb7Ly9"M-Xa_,-=[ @cC#|.9=Hnrmyz[=ZeGm{N|Y&vdH=7- `F1R1Djs ݒVyO@)s,}[e|kZ]Ws~|:"2<0 E֡]V[Ҟ|٦I73N{y[۸D|tV9}H){\iƢS>Μ,`zGhUNf_ 5^exᑐٽ_oR)aJ2O7#M5/Pv[G@L=DYczF-BLCx-ʵl=(afޓ[kOA:$sɱ̓Ѽ</'2/][/'X)Ul*g<* YASU. 86p\5\;QbjG U4k Ic0S]DV N ,hc#A֔pLQ[sh;BG&wk`jfkf`ch$!fe0JՉ$)\iʒGz`Pa590˟7A )z4 4G ߀cft|VmLq?zE5z a^F<'RY]&ymm[rҗs):ɼQzs/P=1}wιC1\ky\ݽv>=DxQqr$OVScI1"-V&[Ī:6q/4?c(t:6lxy[3@'.H# +ՐSϏ8rk!aR(I>k( xS% G -X/R)h2SK#P"4QkVa (5LK ވ<񸴍1ZƆMEݖFbY!BV8uw ]>(ꈰx! @4sͅچbC>@SYyP>C]q0X̙X;|쎑"Y 8S2m."WiBBXqPT%ZL++ nAnP!OP0aBʁl| B>"Dr_tZ%$DØɄT]/Te,^5⟑TɣZxgN3_xŋ\7%X; &`;Fq^GP 2BT?E9i&,"yUD$]cjC&٪!{V`S/W֗HizoTd#_?(ۚPYlhVTOǑBq}3;<=,91]~S_Hs6aQ94͚դwc,ڐ /z w6G;/zJkTdZGhqQ7%SȦco,MSNst{/DmČ}^ڶ0⊨4cDũ],L]y]n4CS#d߷_%]8YUlRב) ж'&ZҾ-JZ`g:ɇДgYZPK!)iCCsnd-timer.ko.xznu[7zXZִF!t/>C]?Eh=ڜ.+ofܡk?ZzeѼzH}wD wT?4GoĮxu$nUp>K}${(.p㠱V=c ROX/vߠSl{&wyăZe,Tx7ЏQΈ \>UлjnMOr환v|5N1ECR!CY?5 `&h/IA9{nSYʩ>/檬,O;mzp)xrq5BOelmzQ>@ K/w0HC$>uAI&GܿIOs,EŨUzQ$twSI'uh N;25f:o^jG]t͉7wmegφiB:Sq{a=UEcc=AĿt2&X?ol>}KWs',uķ9 NЪ U~S_1tdJ)$dxq%94*\/jmFLrqDv9tR|wk? c&C9 i,' S8O*cO@ڑυ8GMg9PP*k`Հ'ݍ}I#Bn/aUϰ4O2UqΰgiU`דSSKi ˘`*|0s]bBݰDn6f u bj"4;ŶGq& M:?sWxaHPi ܜkR>[ʪABI`yŸZWC+~hN eiG͊q;&/tKohP]9t"7yxzKS]2eX l30ʢ\sV88ǺdxisŇND-HЭH`Ҏ3E#^(*TI<bʸv."rlITզR~% fN0j% !s&UWR ,jh5>ϋ, Xg~>>4,+L@t uSq)4 C$0I?yR57>ZsJsu_Do <]H.w,w!+2HYvWJ[z>{kAmN*IP^RRXڲf>/stZ^-}=ģYфLJ^ Ry,ʼnӐǎ̜1hmFՒ]k=yl̞%_yM(bZ|Ý(y\DnցħvbSMTM$,* 1Ftp1܂Ş6CH[=zs1'~/_QpړJ6.({wVI͚Q5@3, ř%H=܃c0b lf֮'n.(<_2z{ ۮGwru QYOZ=,/Ǧzv'<cBNd-yB YNj+jA%2n;ub)Eޘ W'zvi51)+Jß2.IU1fo`u^x1(#v16G)hU4q8w2 kBksnLBRFAP6au'V_㦪6fb 6/BUjc`GYۜҏSM;YӈvMdrd@x_Vg ۾0kْ\y;nݏJ{-%4@2S;ZJ]^_;J#"⺨kyH(O1n~:#&JeBLG$Ɗ\Qm>dpwҫv_nl7w[P㒔Nz@1 EΤ_ Vwȁ)-g ER.aufSaWgлs:`äXs5'U)?4jwʗ3m.+zohgɦ#^ y#ۇ/]sn&mU1NuQTrI9!]DNrկ OPn'Fʄa^?8)Mr9J"mZJWX3ҥڴA= `, pg)/xj(~=՛W0Yh>w]IOhث1h,Oƛgrm] ʶNk7n؋KHVDCg1K $w( -/Bjs3MsBK`筇Fۇqcf=emb޿b\f^gߎOt>}N3~G1ߴGaP(x,im/xdkq{]5Y BU1ɻ'MF~U֜a7bQS鑁m&jYNړ%Y6b1e41 d)+k 2*?hKw.>^ߞyg3y_gT3;`McPP9486[F16BNW=F@ő?67;;E#LV&Tsڹ gBʸ/HF2Q a*P^ %Lckt&@p/M5*geo,6/@{"mۼ`l֚4J:qYxR6>/ eAg !5m\⼀EH1X(eՓ-M Ny1T:/^O9).d-!(zs+Q8́'llL 8iTղh4sCCC#8[[-R?4xWe O7)DMVWf4@kmS --EÄ<j"GYT Gډ)8HsG1r%B E8Dn*Bz/9-PV4=KzN2_ ,FdkL$=DgMOeϪ,3Pzsz nup@[ )K~A;o'c8 eG*4lS E7;3mVx 9wݴAJL[55 ןtԪݙ d5 D@/W\~|GIL -'˜^qpb{B9mIr]Z-Fc^v(~n. Ov7>Il@ݤB=d@đ +0vwQM,7c&&84nqsڻ` {  n8F*=a_]e5),jie7 *@&G "R5Jzk/u_sxF@C+@0\GCzFƘx Ý5d%ppU9 `p0Ljm׼iZ@fѝ5~}`4կHB|siohl;8\ُyZ;oX]$OTImJשsJˢIޒ0i0i~&slA͆USܠ{ *HJ8GVp cD 6W0z>C[9= (`(0D)+$57DGav\=K?iUDRRQW]aIGǕ/ӆvL6 vC헿(.@?pBLҬàIyXG>݋d |У~ , y>x::yG7aXJM`e3jیLnn9M e!lNXw7nX.ݘ:ę^ح ͛et qR\cr-Ț%;#݂4U!rQE)뵑A5Ch򲚑vJ^ aw)Aw[$~dIK7b\m|ɷfF<@{ L.v}=xK]9hr-u]9Ew.crIR:&9^{a<NV ɦvrZrʇ~^@7i0~vTYդJv3[N1q@\A.̷~ºT}d<('fIc6[W -`e(vYnؑݍE<4X'|[adag~1,)3/s/J)t$t+9B-p\ ?țR톹W>RFT-%&MA^]:YRTwp$s0YeWP_fdE=cU .H- {/ >M^w6uhnboxT';a(n#R\e(閃[]5)]![Z7coIĕdm"<QItR{.ɜ]<}; ) 7&CpN}; rwDsg(Mi*8>x ^xhF_y@Kޗ6"p?W u ͧR[ -:e:BG""RQj \V<tvin4zu@d~q@SI=O*]j~O*/.3{gbS^o^7x-@p; 9f0X]ZXqCt (VT3m'dXbϿV=D|?gZ.d/>Co f[޲yuԩ("i*᪆=\C nj6|,.Pn=?_CcG./ޒ ՂKQ>X99f 0Ks;#.ح)Z#ʇ FM8ĶG૩x!} {myia.s,cdT7JuTGl +] 0Ntw]*!rJiS!$;* @-L0U9P0f¦hg|IXR_ţgd.VcQpo+=EhŲ  CB#L5XNf`$BxijւOoWW6u^,Ah +.`>o\yސZD {S̳V"ʧX{m3[5KP/Dž3 r!jYCmR5Ii%3eESuH0[;\ů6Fz̘U:gFԳs5NQ! Y SKWv СoF3M$D|A&,Ϡ?~lS32H : >4kadcׂ5J r< W 1TDiOVNcĤ]pS$sZkL܊?\"Rdfv8nQUm*_5_f3WWDgdV[u(&} *(dc"\h|@]v/ԋ'@wcaa/ D#8u6ZsAFa('8>܆owxӴp[EqwWD{ƴ JYjKXǜ Zb/&7q87̭GZ_L1&g?>y*Mv;,9&jJ=SK~yEAUZ gJ056i;X. pN}/9rcx|b3iI"fӉ@Q.я4u6#m0ڰ?dN"Zpch ~&uy;kW4ٿsYsmKG岼Z`o y|c.ׄ,7PWbSZb- A}~̺J+g׶C\\rVÊ>cfCUWE/3u|yAB,GaCzߔqIJOX %0&*k\mz-_26gsgI&É 68#p:dM)?=X/*Z [9"[zk]Run'H}\h`1ŭ^!#Wc{B }|m L`.fAM5"/Ni4)CAd!Ĺ,Zd.tr`{tZLGXZ8eܖWz%>AAu`*B/uofE~EØآ{S kuD{q x P  "y{iQ7!nÍy]\Z逋;of)/a@9r.b0Yq5*vMyo2,Gm|)KO9i!wGʈFGUߊmF]GǘDzط͠in^cUZ<* L^,٭!>3/y V=hPDD!j1i~fƹkwd `k#+! :E^zSӊʅ]Y_]IEWa=-E뗹Iܰel2KnQ n&ű_l{_ <;q7}zwڇ[n[v9<>3×/p)tZ],>M@0e5![7uE+vj,~ 'JsWNo+EJk)+ ] ""ol*IT{/v?ߠ/ŸXe6^p 3B\1mK Y%p̃oثd 1񌙼b3j3"|mby2>Qte̴/  ZZv_h4/ fT~Y` ,qL', ,kheڤU3OGc(`U)6T-VPݷJ5RGh5M~rc!i/|r_.|]&UcuǕ!$p[Wܥ Pԇr]@ dՁ?sٮ.PbK6mgK!dDHEi㜸%bѷڊsj4Φewmgvvã$R?*/WA?'cąr߼|@_"hA!@5hHLO^y3Oa,nAW1`e`\r6bP.2}լ?3=3VڹS7{ ۖ}=Le|YUi ofzZɮC7?]="f#ϼP5' ƆZhI{1~T55Z@~/ gKYc1 .~; *#'Q.ֳm~Y,]urٳz -姧ÊX/@,PC:Y=ZZ}O~zpYxW0 4Z5>̱CV&!ӡ;pc0?o0a&R!ǝJ~)o. /w*t Uq49!7!kzj ${}# *JXPֽDdjtmF:<`іic*T}CDͫϫno#P<"Z9׌{挞S~)T>J.M3jkNjb׎v{߮L\Ů )$Pw}GD0{(vSɴŖFUM +$$X[|f&2o[y4vk iB3,E##:\:xna!v<W9[szvS/$ϫ{kaAL,I`} Pb2疽W?_bP@^ ΝsR,n oA٩\ju2Aiwyli$;f۱1Q K~@8JL_)yyķdP AE7{b?/- !oe2+}ټ!)r.~(+S`Qo^SsNX{D?ʦ+##LvzvMj׮g(MffuĺPx@'|3$4?,29^@0434pT6Ӡ։+l) FQ#˚J _mH_gfTt}8sܼ|S ?.erԔ %X}or8 58GnT20exs+̖r5Ëx/*+J{ 8ZC%U SXnd<1^w58> R6sTpF'כyx ʚw8ɴ4p0s{нYFf~Muk\+r1*%;<͘%0ۘIQF{"֝hX?mv5 ` YqQ _\TRzrca;BOb7E{%6`rzӆh$2t{h~\VBhKLWRz!U0g ÷zakl窦l/`7?7"v#,7Ӷ)oKojBt,7X~5\h~L@h{jާ")S- ].c0&xA \s!s$6ŌEqbط0"pk+'ܴQ#DHf-'`Ta6ekL0^>GOTDSa!<٫dP"-蓃^(62yKߡmr_IorD3I$N ګrhfUJOuJGXoM;@oS4a$eM$sple*N/,=eJsubMNl>Y^}=9tz-ˆ-"rbA!DwBy*w =V' 37bh\}KJ,=oE<| 1cs%SZ P(bIU[۳~|S.]ml%ׯ:YSVx$ϸM}ɢ@X',PŖ~V@_vx;w6Ƶ4t Q ˦<<9\Yw@u C!=հP7l dk.H  'Ȗ^'5a~-O$DJ /NL*%2੣Sǡ;W|˪hW7pc/_VHI#q,XEY9?YF˨6fsW8H ꚅPf\ yЪd,QIsA+{Gry>uq9@=^ S0b~_#>k"/;]99kb a$c~ X:9,;w;4ib?rB'o vXV rr&[2YJ)qWMR5YC'vI0(QtSK6XB D %qkʋࣸYvzYE;+?9qgD/%fIʪՇq2~y6.YJ%:q_Pw-~u7}،A:liA*%EJK^`y~Cr'Ayz\$E$߭=xvF_5]ahDse ?A-Y# e1~B$|UiP X|h'wBdI&7 ixjwbj/xjC y K|J K,98 |=/!/|!ZyC}?llPV![yxl=B56;BKնhrf :u]KcT5Y ʘN_M LE`ˆao8.-ʳ} 6(r^ϐ( CotCbq3X0la"#4`){GP%[AYͺ _’&rDž*B5<ߟYIbWS.M`FC|VrT݋y(weTh:<0CU Z!eg"rB}֝wАk~u)M-Ǵ -0QK~j_|jJӃƽyf1n͍EtkOE{ed}Wx &NF \TsAALT^ǐ7ETYDC~1iuM4h/6*1f)%Rz'y폓l!?q T85kttRlzD葧,~Cd t[F] ±ȵm ÍFͶFPz*kq$JfPWQƮ%iP2fe]@,RtR3x~p|?vRH eN r%KB1| a+E6v StQ~Ƌ+ڔ;f+ ,^nD+w\֭PسE(f'hUKF  t9 Ft/['e[Z`O3[t+Ι4>bGݘ9ӽ=vfN̷m=y~/%ޞ覽E.W ?P GnGQ`%rkf2G $>86 ZbFgij+סH lUjWʢ22 pMm= L/^ z_jH_+AC4*}}-s>FQ]i2Fgw='Ԉ(oiz?ȌV bLj)Z:57{Q=QF, }8Ev.Ȳ~a,37!7pН5ik`9PWgON>ńJkRFImlLo9lX`GDWA{hjg\.<Ċ>:hPs ֬h{9e?5tH99,2z⿥Pۗ۽Ƥa6l~rƳLI);O6Wq&1ڴ%/gVFKWcJ_[,DjkQ.ٯ ƣ5i͔r $[*Ԧ4NGFsA]m5< |j\4azhuLuu.wgr gR.D#Fߋ$Aׂ2xuPq)U4T'6%Y_ԗ& uL]6vlJߌa4F,Rަ9D e)67;MBe ul>cȢj/I߳X'T쯭eawߙ,1%'CYCqϺh--q*8Rt$l،:b>8$0Bz ͨp*v|=(d_S bG)a[P qM|a%j+}|--;R wJ@,;V ~ÑiT5^ ʄg%&?N<.n5 V_[ /@z1uϱ2VV,`poGcmɅD }CdQ/L ƎP@ns}9Xg@B=LkU/ Fw{+ 7w桬CS^aN;Re#oJ Ȝ:(@ĵڱ #RW¡R>s"9ȼp=#.@` ya!0pzKƐ s ZjtK6eY"RN62}[z?ߑA Wx):0|/_p+[ʒ2L\|ltwbRaMI45]LtJY  Db2̶֝GH;27S4p%w!}Bn!iJyqvZ„=LUփ z}fA`a)ˁ8־H;d|&(m#_QQ|?5Zz֥o h"$'b5Q?u)eӵ+;IM۰ eLR%WԭΣk]sc(jE3PIVmOt'ZxhyO6ޏm%̭_ b7GH;'[/.} &4Zжؖ],x5un䨝r!kN,o(z$bnJR'" 0}@gc{n :b30y|lPI7Ko#7D&14ʀ0NuDlvn!9@;́.6l|1Hz|dEmTXKk-^…*f0NW` +AugazR`0ogt$pi[2tԨ02ՐR~K=%Khrt\NB0n7g2L.!6_IgŽ *WISzpc0·C{s sydWHn?bD4->nF2,+=WܫUe 9@'5[0}R$ W؞;y*,_Jf3hM#z?gؗŨ:ia% D]OxY3b,ߙZE9ZPFҧJixVnMr䬫,R96'L: Mw GDCqYP3M̧iOm"h6XiR"}zG~9I঳W$U@Qf50A!q1>.wq55,2/3,w *6KOx1 M4d/źOhaSbu6&ޢ GVݳ=AsM\)Bc2 Fת;_ jnNKrDg0/ |՞'] saЃviaFH^J5ٰZ?GfifͳT@Ll2C-v!d^H֟n >2%] 俙rbHAZ|}])SZ9r/='M5:zjwm!pTY44ʺ.aCv?|-=CAR Ej~(;sn 5ݧ#d9z|%^RZ b{хrF$2(If[+ٻ¹/ycɀR>C q]C6w?V$@)pZ0 ?QiS|stw&'"KD$RE ʚIKc'8ai bNQ}ͽyYs560L~ƱaႤmLw)1TڹV s yWg!CM?= 8((}+jB8 J,"CQ@hBuLȫ-Ʃ#-$ݺyL[5*,9.*:lak$mWZ a= ¡ܵ~>.\IajRZZ8=U_o8`#ۘy`-cIA/NjC广NiQ;L6h^3nqCz4P&@1 rgRRmVPuq-"7]h>(}V@Þ"AQ<ک'EհV9مh'$B48 7jr|FM!;mDwoP %ʴ҉<ʎ SZɷM!g]o򏢩ϽtO4R԰t?1B[Gac"}ya׽J̓A~j?3 d @$PCso'<kE#BN.iSC}j4=#.JAy-kA)%fʚ܇ު}M~U4p٦fczGlR rvrYI~Պٶsմ)ZkPaTrfn'> !4Rdƾ"}v;' FP񓽨6*YQgpo 賟{kQfh?gKwmQe0y] fnGE KnYdK4FLuda*M=ǝ2<ӊy-m~ i{4m !e̱!t{S7_v۶W"-*o}X"L11|hb4;Ç$A\֡Ag;_n0OC kñӣ1`DRC&cL,[6Eq}1#qGPn}3&[rJ] dEQFq9T锖ڼQÂz ј`4߾qPtͩg]w:Ϝ9m~> `@DU{]~'t?)vK_[מʏPaR}sCmFpJf(`{-׆{&<n"2WfӾ|eDw#_WYfIБ7ac&ABHL[CZ; 'A!ʀ(m${0 0Iv)G{~z 1D-Bg׷ ϴGJ-[^}s3zSu0rG4)k& srkۊׯ7؝nT(T+ە!>Ȩi6#@)'ܓ[iˀ4Kx~G|-Xpݢ YwdY:lPZ\ wV[T[g7J|[s[P~Jǩ]q)ޖtTQ!hjD QPq7WXg;K9 V{V6syl}[Nb.kڈCr(vC'+k^YnxK &-RفAfWϷ;Юk zBa<i.kC6@Wk?4fmJ*;eNf-j{԰kQc|3 Ee2hMƆc^d FHG`)1/Kq^ j#nVR. iQa= Z xBݨtqk>'#Xdm)տMWLvIRڎFѤuu5h3sp4HvyAKiJ&i5Ҏ&kW5A9n@A5K_t4(>pЖHMgYZPK!L L snd-hrtimer.ko.xznu[7zXZִF!t/.o ]?Eh=ڜ.+ˢGS(rz9vqx'a\ۨCn$?:UDe o!VyGsq`ve1 ²Ty}dXKY;R.&j% 8!. wyJO]qn}|19HUo*vuYzbvؘ%2\{fy20\R*V!b%'$UE0{z( I^V0(BfK1)\:;1N r^z{8 2xy{;ޥ-GjlBr6g@l{r) U>Q6h`ѹ7BiItHžP?A5X-()-*M Tr&ٽ jmwuNz -L9#*-uO56E]aflAه6d6H QmjG!&dZm?(u2;_ѕm^NNn8Ȉoy[BCZl_s<2э) 8*4MRdJʶ4Tf'+x7- p../>kfvᡤ-$d#@2m.-@g1.|UaXRo9 TjϠ %,$n=y>psK*$fȕo|P>jIdM"Si\g98zf@_^ 濶n#}_w3pMe Ls|D^0H^_^TǏ)S{Y&1sKIWvX#3,ڠ41Q/Iޥ旞Xvq%=Rj(bMmI8eԴD7Bub$ ٺ,1q)WV-ߡ^wɯo<Ȧ MKw#?+= f-o5tȨylpV~N2j?H8ta@Mu=…p j3Kަ3E淚-aZlGP>poȴ!2A_+cVZ*;¢#>i}K`Y"m2K;#:Mo ԑ!A dHy7϶4!yԉ]I?ﱞd7pi^,8o: VTfqvnNgԿ[&`ESU+nIA(’j5˧FUuɌA&&wiH DrM_9B2AZJya9pJ9hSVy,iД*"@݃z2ʉ2ڐ8T9" *i<ϼ-U'ݟhR R~Ots3+k.o-WJESCrCiGLd{)@?f!ڐ =tP:ȝ~ߖ_ ZkkkZeɓȘ^.[3`٢aC۔.mE4 ǀA (c-]64rZńuGOO!tr cLW/?o:Izn QoU'a;6]ۺ=4؍к1k1&XR ɓvu rU ,8,ށUOH@ K- wr ]n>pƉț08``;rh1 8NysTe|dJ`$R஗Ĕ#|f=aŁ sƂW H23LLyssm6W o%\w}`fO!jf b<[ŏxΡඪ(ME|>6Nz]"] UoDe~oxS7BGtޮYI4x,rǔ#SsHxTepPq8󻨾򘗸n*A))I߬հ}γbsg<,M+InFc2l$`BUsʪ^s,%bt #om@RقcQ6`yz I 2[D١rx '{{@SL&= W7@7 X_\v`,z#vGc<mb[֦ɡC@MYQm~oܛxn^/[~x6%[qS؃;&; T{TvdJzoJQ|/j _l"nD]L_$j=hybz-J aCS̜Q+^EES!9hW0}K~e|"<(ʕBtXA2)Q.@p9^۟8#>S"(9+.tCL`BCVݐp˶(jl*iȱ aC26&f% 5v[Xᇴqa9VU9%)NS%ҠLj"3/qv4acˠ %?Þ 쵘.I ̴q3hP&oL*}VJ0` P(Eu>çzw.cS%]ΝU6DLmZ@ Eae@!"8%9:<-HK]'\WPQB*Xң"qu4?&4d).އ)]9bez ќyI/v[\'+!qMdzF Dʹj_MAATs$.زJp{[JUQE cLe@j^XLIr?X99݊Mx5ʐ?bJ%' O@uldQ=@lCAaEt;˲=Ԩ(n|nɡ7 !1ȸpab%W (Hэ *Cg{D1,y !F:%Na`oE`X1Unr&#<݌F<w P7Q݌G;Jd奜6Ga͆ IolhzDt&{a/6j/̅^nԊRxrڜ$o0Kؼ;R Lǚ˝HVOvz b2 |zUQ̿1Pyz ɦEK߬)rN !M^~]?J&5SUQpme}buS񻌈\_.GU hbzx` }[c W$“JSV@/u:bm9b?DՔL27H |T\l_<RӚg*gEhM%tE!:m$+=p)UKϑlE?"oU%}@Ȁ@$) [ p\yAfL]d~Xk+S~r;6N?mbGDYc"J;]~˫Gtk(0Zї{iv"}x&hh`b ,b)ijqiGZKɱeh5G\Bf;vQ$E.!HtCP60:eئ?ƖU`r %BQ 1eChI^WqqΤ-J%r|r]E\fwH. k23 NhQMf|wwjD&#gkb^v͖ EŐjo(r K~ (K38Wv~!")k@8 %(Y;ˣupq(5RBJ i ̜[#ڻ]fB1&偉~j|J%505gt3H#8DzKca-dW#eShr֧a\- a G HɟkH>sP"|CwECpISɆ4tǨtk_O_k|* ibyݓb&R.Pvfh\ 1!i. c "rHzso܌_U-P(Ce"ϼy>t"EOQf\?2mdhoicL{sJ&WDtZpWC.GٚwyIjM䐝UΩ짅W* d *ŏ1%v)69{"O@ d3Pgj:ҕa7݅0l&6pNzH>$Z~1Xs005By?XwX.wfGRkcFαrR]NVȋhM'tq(j:1i+B˔hl6-zW^Kц0~}6A=;)=0QeSExĒ$A u#UτK0Vb7-wǹU2}Duh)v(hl _`Z5X"S%^!F~za,Ώ,9&UD뛈wguȕ?nT׸)󁎽6K2p0n#ac \MFSoa_EbshV+2?̊ԃITH#!pN2eC>V T#i2aE9EV+ɿgyZVU^y3YVش+qep9GI]px~J$>$R4xx BQf$,Mz?ݦ H#mRT^ZpP@AuVH.mo3OI x ssYSVCЋ\!AAi O^[fR=GG/m&)ͭdwυs-dY)Qd^lBEDkL=ȐF̫cYR$yK"pc86%aIw7' -ofY29nלBj ?*!)cxW.y/ 8T&qi>R:L(G "ڐS@ՙ>DHt%3$rvɄ1y[bW;qA`8+*6MmQO-0Z:ۈDN+M #6I ^˴I%n0{c+J=R+G}Sp=1)}T/\~$GԵ J1:vȲ)ᄽ>Xx9d蒹嵶J@_d3v8p%Lb9d P!衿8ӢsDx2?#c .g'K>B(]+s "+;I jsh ׸g3SLyeuQ cG> n!g`%֩*zBf^}QçR(ˤpF)bު& )%ԪԕA 5Y;@aCF3o `dp7_'"fJ?wNlxK:Qm >NWO 9s'F`eW,n 9U@}Rf$^;:3dw'xmoz,]g)8YExz tfh_mi6҆mDLW{[5m+MۑjEb `{&mbD_ Z: ۫uB.e'H#J e m]b$2qjke pXůkn|u\ K_C:+S)JBhg[wױa8_&(*GN\LF]FU C=py@(M__^ɠ[Q ܧ SЭ)tj/>![x}fb1C>yF>_ɨ8$6F`qg`aTyalOV}>, wk#eﱸW y=_=}pkEG2  {ՃlTy$LL%H{Q! QViY4u|_/Ԃ4 R4NتVQȏx3:uͧP'ߒ3^+o&6u@<Ԛ>c$nۊƗT'vųʕR܊4[xZ;4oiA)~?Կ,ef2dEN+`817Zl@C7'?EyW,N@R(ZܙnLɢ& -( gukyej5.fB΁ȗ*0ٚŠz= vĜ~\է1 l/v۱gYZPK!c;, (uMKyvm?.fs:w`.@ZTnTPplBY5BsBV7g#G.T0ǙSRh Z6Zx<6eU2Hi̓mi˶;̬3^6 <9*@Zpc pp[6QƵG%|os[\^ct^$ރVRRxIC#Ƙ=VפgTVy%2dҞh{։skM<'Mf57qK ~pslzqP6Q6ü۴E!҈lrxAV5;zZHqjWs. F[\H6}Ilи0) š L]obG4L}D7Z9=`$TD %ʁ_HdZ N<8%6g5;OqO΅56wN01@I'F FmCzDūǢjԭzh̳SOm}x> s+&ޫ3T=8X*X|iYXr3eIގ֏,[+mzQ^#jM}?KyKgQ[)Ǒ=sh" cM} yn>yRAJ;[O,֯x7N=GnT>#a {#ՎOfקԪf{PT}n]NYHt}7|עW j5Qblv]>@wg,\=4Ը$_DW{I@ES 5u%pA\p;DQ Dqؓa~ɥL#(;mFg ÇnLQU5"˹vP2Iݓ:(X^q7ؚ8MM!46ʈicl>5bp{qā+OlbP' )j7mS)-k` y4Svw yzbu/tMѺFCy @}*>aiI!>V4iX!J&PC Sc#("BqRJYb~}L2#[|)Ss`cGBXgذDecMESP0q $+5+63Vԣnah7Ud8@mxuݼf= z28`ɝlfsQ{a' q(I# &|KhI*p b;QgUtvg_8L1n%㥺=s), M<*\R3@;rgp*7n "Chwq9Cߍjs 6=›#B5V#fޡ Q#iD(ghGe3'ڏq1RwT?h[ԓX1pK #6cDc`E]$(ƒ.^BR$v@OPɀ6Yft^_m}a!%p =D]D\Z ҕ鿬 M8: owG*{:igB%}M74U)j!1i$}jlcaH'SBp6.٨a Ko|?;!#)RޡZΓB:ev= E.W7MzDtA_+DC; b|X^ Ȼ>s'Bkcb9 kf>ĒJn`"scpo-&QKWP ؃PFƼ8I/%6jpElk D7rp3mg1Gk?kZWt{E>8Sq~/ߓNtՄІBV6v X$/mNKєbw .ryAeFn&rc#hjMO[: Lx,-˷`DB Ř[W씭3P`k;1P Md4;m~b`[`6 e(Ђgo=&pvB@l kfՕQ?]L9b8-UKd :!V;!h\eƣ5K9֊]/ wɋ!5<٬A>ar^(OJ ǖs-S›Cf28}q"4rnt~#yexu4B!MsyE\'cb+NW( <0Gnn*E8 P:gMz=;ӡ):-5F;lқjEC}#j9V+y; ǴŋvqrdQ̩82b1+JJǝ3zk`OH*( "q }"%E>R?u?ʉHӘe8Np43cFy}kk<PR;NRAEQD6;l xʊ4wYtT&TʆK_?x@k%>g)☢-d>)'w%ZUO@KTݲS8  Mt5Wl(S$m3$j)`S,d8dt$b~쥦JS^aneH?T-zy9.VO!މ%l81$cäx9J0#(!krP7C{ν &(t!&fs>x0Cn/kcSvj2{=0RYtxƓ)TQK>NႿi1KVw$w\^μxL>Avt/@cI~07(F}sF\%p#{/?)OxͶfMa*Y=~nK"9_(1bOmV"527e7$Q6th>]5nT W3}n{C=rkPHg =M}; A}zRzn`d=76BzxoK6F3FE]šLل!+z$rzJ /.2xʏ9|ET#/: ͷO״/)[߶c$0to_spzhء$ZLBfx&g T\bD/f\n2XIhݮ1 kO3x{}0)A(P; PLe_ĬE5,&ϭe,NWa 嫯m%pgTGH WƑ&ae YoHm j{&'ur0nLxȸ=ipj\S6Zva1]^ӕ3igмyhNAqK{@1$gn ߺMZ'6Q!J;$_E`4bCr{ !0BkZB4glH˲~UEK^p*-_\Vn~ no A 43400Niډf" 6j,"&[ .g%rp/UYLadyŬ$)ׂ /i׻W4_"oMfFkh>Pؓ(L%j#m4JGHW;TC-LK`7£[>_TqO ~iAGq-.3U0?y$n#@q7$ `_bGא أ1'aYʞzcXhh)1dցBL!9=S %a ЫXѹ2t1KF ȊvGz f;4?Jn ɲjsv:5=܋ !$xfzf(+OC;܍Pw e?\cgCoH^&%O6o]4b ?P:j%#!WSI{Tb^e 5=OJ#5KJk:Q0'"{~t[G񭱒a/h5j̀Yg|wU=ztx`Ѡg.8H$vIֺccr?@v\g -+5n /yojQ(q_5b $: i{i6ŗ[|x]u' 1 :Hӈ//Kkj3aAc#. Qs"˙dbTM8]w6H=zwk5YeFd4wK8=8( BD=]FW]daYw2嬥W} EJP`xVkzԾBzRWvK]u[ lAaI9l$R5 fTx #p!(z \,'GD2'̖$wi~~ҷ$OX6*T܆6`Yc,;UA4jѿ9^y(5vW.{>PЉLo2t!ɼʵҫA#uaS"~_ 8B!@r`c%)x=^OQjO1ŷ>n |GX)p_f+Vì}{8ާ]b rhc\{4_DΞ v 3Wk_ͼ5jx)02i+ Odg/*ԤwFe}>3Hɯ4 ?Se71*h+>>>Y Vx0bN%2MmyE @\&:yљNbԐ S-l<3Tow;Eͩ%"8v"/ad$@;znOKu!";vs7 !U%ZG 1AR+ᚂSbR/rHt>l2S(=bԂ ilPC"sf-^yaXp 蒡tnJ9EAIT ó7UjYGu?0#"$R[\(l(G׌?EaL)glZV(kY6q,>婢l`~x:ݹ~.=BIx߻gܩAK"}EiOc`'Z&RԽpw8C~]tGt:,s h%4tU*)1W ca8T:+q(×U3$7T j8- iw))G]*W\kePuċ Hm+o#sPB[OpÖ\L/"0~xU)_X9CgF.P^=bR힐S,-s8$C awsa>mTkxL}Ǣ \[ e7bMo6$#XM)2S"P;7rXFܥt?:Ɋgơ-Il[zF- ]rDv՝sD6\Ljj~ s`2G}u'Lvj"W!UvI Yf>/c~P|bM-*nVzA7id2A(9}ᷱ:łQ !0*KY5xg-;ByLTDX!$XLX.=PS6NK-$ %^ t~ݾLM/Pv yg$P?ת,bT@3ha_ 7?-UIuI2TDqA;v}ґп†; ,Yߚ„F@CU\*u `-@rZ ^T> RG!ϝW*@cS9[ <'3uA\/Mx>YQxK- 3,`bx2- =^}5j4ѳl,߳ @"+}? lvcr= Ii8,{1,KF &".z &]H _ksY-q&+HUo}AoXX\+@RGÇy1)=z>\2&N"0__7B3G΍פ.Tl:#_Lo ?t8&4m޷ۛnD}EJo&׏1 Bb^JsWhD`/ y7cn"|O_^] %OUMY螃Tirc.Ł@p$HIKxU0 5Bd~ފB7y5wN W(s-A@aܦ{azr^vʺa vsf'[<.!V8LC&K>6Vm |5cDpfwl6)U3K޾Ѕ[ٛ'|$\/Qu[PBM qS޻4$FY QQE֣a9=> eQO?2H*UOUX P8eS?D3q6L;`ɬ,X58u!gQ0 tX'|V&zߍ6D@wSODt*+Mɝ #fiҪ8^TwB ӥ(X_(˰k.oe=\P"7qe+"pY>K ͓Ql0H=E.r:f$wYvCNsӢT2G%4jQ+JI̖5%9qab`'N~03BOT ptZo0H5a?bPBAF6>:#m{KJz @OQL'2kz v&TWTXnDkSO@@00*S ?~A r]?R+ޥMl -@=' Ǫ/D'!Q% r1l m{`g6 { 5D1z(}}YLmE'@ղv;(9=cBͣ:...ok ;ww3I-H|;Pg7Ch#frwd&_\uC2ށH/9vj;lg?ٺ{ClQwvا,A^EE%?mAqyl`;JJ눊e0bk!/\ ClP5 q-eG|\EVsT]LVPd/:Bf B4]Tۻ{;{P\0A')v\[ۜ@95<)wlp:&[5T K,\~AJ# Xx-!JlbRWF 7ί0k®uF/aQ,=bQ`bl=瑕vlhק.DZ{䇹ǖ:Bl ܄[ @a@ 7G *uĂ]}6UUDi~!OpXTF^dx;8!I4M :a|]~.ՕH1$ȽRC* JAS{& AgU޸ \?ryGm7&2s$M`F=?`ο@TR Yz밄U^Fx+ZMLpL'.v+Bg>}#¦M{en~3Py]4xq z 1j)oѪy]kQʉ\IHThVJȜL  uhn츎Vufut`ӆ.8Bi43 1ڕI~wsGաx0k{HVSɓ/wPٖ9Ԁ!z$K8bC1 ٝ"rW?3*$bdfbV_:]&L~@I6HQ8>RRJƴeDx 3i单gȫmy*Z(=xdIqrS,Z_Ёi}V^QP}Ѽn7\zd{-F1Mk׫[B#:IKXCB'l /Pf?'}iiSvɵcr&'"'[So\R|W ?hl/qF1KmU؆brq8h I9!.l:Dhx) k`..@U=Ȋ{LJ݃GgI`.&L^Esc]m?2]_#2Oda(o5 ΐ0 /}Bs~o6d n@-0]9G'ʊ@{Fy5*dJ=Ќ,;y \:NS7*ӨgRDp-|BR)S}>v|9m&{,NQ@U41Āu]:Fy|D55]Ͼ VWbU,] ʜl޶l ʏ^I<'wz13;#:1& Q2RPTʆQ#iqSQc2KI&Ԏ{td^lFvG[1n*$7 険ojl5 ƌ\HOHVN< Ë IW#+I"< yq]e|DFZJ`X"D)f8t+:H#- >å mkk8`%W~KeU\CN蔞i]a y1GL5z)>ӬRY,ېA1_¶uC-p:bUu9hd f`9s!Q.Y:IƔN;3{>~Gwj9D5yn5p;vyc_}X*yټhe.ck| ``<;sXmم"݀;gVGn`i SK?p:5SYn!o RF𶋿5h,tLg=ҖJ]_YTi>θ% nk?|h!))@XuҼ ps&e:ܼ/@ΧLc@qDGf>|wmupqi#v@Ӛkם< e`WbvАv!)\-40uwe\p|Ko$ɹY Vab2V>įڷ y}!!DE0%\1]SXL j l!4XB͵6Fj ?8݊hr wdşX;":Bc5wzpoЦWBB׺W[O :R|^jNh0eBiU-67ˇ@HJ> \)^!JjÒ#@쨭6- OWJ"/M-d?.\=zK5 y5!LBsMp؎<3&K[JS4s nӯ=zUhg9DIZȼy@FFt[- a. T\ PK^ة8r\.Ag# a@Hl#\B{:A*R> >sdJ_X?]jOt ?"d+CnKD)`>/g{zE+< oǖFj(`ɞ&j/PO~Wf虸){N d`mDB Iu3atTU-( EUd!ۇ^C,4&:BȃiLEP7AL' T)8i4rUbhO clSP=SKM;X|[Js=Ar1n >H+9wa< v҃Py H[IjWeOtȸUG<*D6CAp5!qUnǓ(F]F&Y#+zS.5(iOx-.Rx5{vA3b',Owj9Lsh=wRl|8$h7gARG{P4+^ Ƿ$~n hW/ }t(Ya%v# '- P8-/BquУ}IG䧂6, 2Nlk}P5I/a$.NMj 4mR7k (3Mei H#T|905.6oީܜh?-x{G M} mȂ#X뒋ȱ"Ŷ4vH-}ا ?#R%>>j3AVʲ'B6 `tmؐ ( ;Z_Ж8xVci'm5z!&]bɮ>:N\:Q|(7aN<7z]3rl%$xCqUƳ0-ƣϏxo `ꡊTNG0c{=pw @bOBj ar pr^u|YCטA -w`cdB֞6:vx;U_=0cm}O?o_P4,5fk$C5&`У~!mĚJ$*UY>SA>-7/)"6vK+E:R0M~Az{Kߙ^u_(@:dMW7Қ_ N2/΃\^pk<19;}8OǒHfAR1EeS)]R8+mx3d`U YD=2O@Hk}|zC[Ȯ:*njٰ2,E?1Ȓ#SDi2 3x̸̤aM s1Ɯ*t=^;ꫜ".tVGO ̜̊tmqPce}m-N3-UxL@PД(٣ |tڝƑ*c'oFl-.43>Sew'uO70„Jx+;3Qyh`iʿ9d&xNϭ,]J[~[7sI#nҙoG$;'oTѣ|3)6H6qMvVN~bAYP46)7ND-~6T2)!:KD^(擃Y$h0G xy?]2ƿ%a{:``+nUAe:b\Wf{Qj15 ݟ3; UH.Gdo&:<ۺhY-(Ի0et8HK3`?p1VJeMu]2@JgJ~nOt"co>3 Sf2\jb><*/y㪛k)8huM1d1o븽o7M%mqxWe?6Ռ"oc# ^0`X%CA<4AA_N*nҟ Dˎ,^:8 83q/˝1.}a Xh j;qtĥJ4*z *PMvPƬ+T ?4IYO{a\pN yų9-gA<7t$"$8OBur͠KYF o~CAC&]]xe(lI:AvVrKF殫09 \ ws=ѣM,:a1{/z 2t^t37qY-nfgP~#hء8< q`ͺ1aqJ)Ќe89|vgrbA*?$2!~p䀍7la_:W-!czX4;d&ECI04E- %/;bYi2% RH)ECf{c?*Z@!<sX+˞R)qo1!؆f$:;"d"ܖސ}{>SG"XkvATMR?4ek-Nd1خh^|r/5( IS\nRi"v-`^iupY>ŹǍN|LGɗELaĆZ0V̤ȗZf# q9ܜ6 ERctkπ8[ [Lj՚ rf[avtç[6fHFYU2?nՅMc}Z3CժϿIIsU725!DިR3h 1tbi)9ImPѪl 1qG(+4'µ``ì*?gt-QAT >ftxa@ {,?OSNN( 8R+DE;EXWO5[qӪ~‵E?Q` {e -~|w6NYs-?Ka‡qY.zÚ 988'\q;>DzN8Wl^8lKŸDmDyyץ6vh^`HhПu4?gL?Ӭl1*}x10 dP&]U-ޟ Oy\rc+I^%jsȍyWrKg 8!H ˈkP\-I@9(tedjQzҠ:Lpï5m,3#9rs|HT)Z!.7zu''Ya/V#"yE+;≵%U ̮u%/@o1; !_73yS1|2nMۆ ‹dS*ֺrdٙL1B~Df 38gMը͌z?T#3/(muey+I2H .KRS{gV"?IҾ~g*~j%ߤ事fFoJ&Xk 7&>Nfd.1 ~#$D}:s{Yd! IqP5,0nsiI3Ju\PsjΧw -e k@>'H]z?C ǞP:%޶_\~GaМ\6R-*G:pm]Q313n;{N9ӫʪfhfDހ';vTA~`34&j JB =/Ԛ\fXA0#ͲRB̛!42Jyص4)Ր2V鉋` ʩYm!kFM]ƉG0P*J3pe3ه/qM ?C [< 27| .V b6mz`mB'}C5 $&;-v8';a]enl ?yO*<)bMCUqƜDX5efz;$vonwbƫ]0Ŭ=IȀꁏ~G)떣r4 iՅy65Ƕص`hzD>^Gm zwWE|m 6E9d%}iM w;#7Mi^rd3ZmȄtm7E#}4W-x٢2N@:[YvK層1sf'do~$Yh>F :%{e@ѥHk%cF`mŠ}pJaJˌ\7݁DOoo K/fk祱zek72W˅vl)c$F}8y G#wTV5 |1odJߗiM )N|/[TpJ3Z^`:$px!_ΓjjEAX|.(MQn5ձ:VBue olg3 . {x^ʨ=}@0[Ph;Vm]JE%`Z+cYSUa;ɞ O!)Έ8rf7Ch1Q:H{]"`?֚])5$3 5jx7FX!d[P)c 0W6-Ki4F vW(u8 ;":(Mf<<\pQ叮IRrq9Y>`t3U,^6΁?j!fsVd!Y>\I2pN = c; oeaM ev٦}Io1-9C0{&ŇzYǪHa)^eg|DpT~vh]p ($7#_wsg#*V!N"q vO HvJ8>b昈=,*d3̫x0w(U(x #o+<lbJLMgq\[qN;LCRrہPiϥ*vamQ8YCAJKy䵁q$ mfaZ2x(zkc.:C5C,&>Hs;3TP)J:;j+K_bksZ߬cCtp =Pj1Lk,8/3ooeMWvS C2ro"qF[(($$:-7kfaNAy?b6vrbepQlfe:y۶k@[FB}qo˹KȮeN]g7|<+z귰lx 1^ZrNuED O/r䱇]F}{][n G/EKjoQg޲V 4O6ޝE7^@9;gԡ9Y$Go<|)a)"H? &`Z;,IU1Ah5-~/.o# oe,= 0pE,>?gy7MFtDMCkԕ?>^A[j۷_]WP)l"ފ5 *T_4W˃b<˚cS#7j"85H?8`! ׳ yC2">$i1yO?0˪j'#q+Hew|xQEZrWCaE3nP';zrD*-I%(;-T'xdJ OsA<3㡋gt 0k%ܭJʞV?=]}f:-"k Myx;,9CR-<$q Iܨ[8rXM5!6K ;) F`ZW34h&%m1oK*4#1z%bA/M=.\2/è\g`EMŹp ~^F4 ma;@ ;U"pĈMf옕hr ddLua}jvVK ~5~)Vf69jЎA8ROSl[ϻR)@n9SiN%aq c۵tPޝ%)CPǴFaq+>!=P/S xc) ߉[M[ODS#K01^vR0 [Po RRxw}麌3[*'/W-qN+da6K_qvw᫑Sl_Ty+?=? _n*nڋ/!(蟶ؼ/xqK'?MI۪?M?aW3Hj7įgʀt&,Ҍ3$S?L! [2wChFex:W(q#sȒMPWb 1(&͝ >֋, hV-4\ [RB$e\ӇU?/AkIKdɜ<~|XaB_X C; 1f1/˹DKcݬgwgqiZ4p{0&{Nn9hA1NWs[θkԌ.H{?AMoXIv^>!>'#n+=:АmޛVG}=PsT?-YTT#J-\$ٝp_e=R̈%}b^?x33{{Cu>/ [$rK"e@@{Pg>ol'xS0w9Vz;+6z0KkNKûok-R#ku2.w"f 4'B;rQ-z[)<U/s%rjƪ[ʣMns!$Nӡ,-L2{T[aC h٪NJNI+Ӝii y>O 'u3Vx ?G9q71ܱ /.Y+[-U)5E |_,jΘK]ZARM[PwXP@_7NoNDv-Efԭ#p<̇p0<v݈C8]6 w M燐J2'E` ы֬\t{?+wM^eXxQ@//& +m࡛?S@9&cKv$GP{0Lvrb.P-i2.LU#H.H(bt-)ȉ? VkĈΠb$ HzBn4;^SB;ܟDu;pV ػ됟(r1;[xcKudksV־9^3#gm1YmyJgLw+†<nB6FYܭ@d= p[sB)^(jNYmlk|VPSe`~"o#s6:Aָv]ŁΔs<>"gYH$PtZQߗ3h;|a@'dQdr AkK[S 8~ 9tsgYNmC>s _& !{xzRO뫝AĂ$NC+@=d JSkT!KOgއ^/;lnG[]72jLTg6/7D Knsx7 з"F6J!jjیw&1UZ̈5ҸQD̶QZ;\ͽ+,CcY!+C>-00ZukfئoU~pKޜJYdZGYq%>ʼv$c´#"\fh#vX@>] 'Z nyj66Wx]oSzZGk-d1j͒Tqy6h'B;^˅_^f*ܫiۄY"@@c!ڑbv:ioGEI=_Da1ǁZP@CbЏE-))cI5i@E)R?IMc P(iԐJ؄7|CK e>-h=']<Ӱ".#eI=r]۱2] 1+E25pϳ' Cuy Lb:kd  7NXGkPe'ՕH= 3f/ 1߄j94,4s~݁gK']zn&z|, !"6H-'گ>·~zNfg4ʙ^8~+cB0vrbB^#L.itbѮGM\5%/Vb0~hGpҪP.Rc F0-Ҭ?_x= ;.*wa1an2rB`4}jk&(Y&DSfUN}:F }w23w~Qs6`=Í /ZηOCQ4Mx5nIe/R(z_O5.F N[/{viy8-܁Zu_#ǙXڨ q=4P2ܐp͕ɁN_q[Ďd)VOGW(υX>'*dc0=gFX:?[C#3/ .njcOoҿY8F5oNiqu)3o]y~e9a#(Ehmx솅΋y]V3*Q M⭺* m ,UmO KW{ԟnƝ>;kw je7r)⚖FrAJQ2y3+ )Zq_Vx;t6qJX+>LvyyRaҘu>ޕicSy] "{{ HY^Qg]2 W`|<;\ c1/MbB>U%D'?,΋Y:"9t"b.uxLԿP'ǂ57ƄE{mc:7iH\2i ee~*YבWŘh43-R,1k/to֩1\ݲ{\Zs-H ynAPSQZ:y nbW息YX[A,t!qFc#s<,+vp#io%S +)lI\Kv3(?;]':Eд!v3c`0;$K͎2nm٤6sD`X$fdn(GSG'LjQd(;u&,:Flװبt \]SD|d%йwY.>|$/[<++V)..dszCyIֈ*M+.eYkʼnܧW|VL7ƭ4w;u̞rF_h6ϋ?7]`}# J2T٬\hER.Y]LgIUlhPoXhThЙFM~7ZW?F ~_W lM?UG GrV9׊4jo@; RQd撷h' *Ntqh~`Æ~h: oly[h?X_]1 ]X팝oo]>ih#B;y&vf :61P`pnFBoهlmlVa=!LKJ W+1Xeܴ"~SvO&auK*Eyn6qR?ڍ56t?d_K.$㊚ÖH_یVM2=^O\m[ΔRn!_Ig М+ۗadpdO- |Nsq,!7|/4J&ׯJwױΊ<&gOPAxVB{=NC9ӷ352'zCJ^LgcceŏX2CkBPZ O8`jÜ>ʾybdb,ٿ?er. h `T=PGL4{Jm69i\38,<\nuX~;UO2\5{O(] !eY 4 Zz>q+l|vY0*\ ̙u&7+,N;8A;%ƐK V銸RAkƓ%Fi֔jt:q͝}TiN!Ne8jx~9:0tO8rr^FbcPwΦWg۳^DefVa]}Gtw[;|@3'?a ?:>j3- $Tg(hSH__*]|ƈPi߳ \b_~آTُZgr MU_:&3%>3ͥBz:N:*:6Rb"Mqwg`I>kV>xNm DYc%8 &.p4L1J#h0S b󒓻,2I@{( ' WJ+}cO|: 9>\BO1@d-~qey.Y8sfʙx<ݏ9H_1~1V^e<1|ɝ7fy[ݛ?[K2inn6Ȓ}Ο͠N.$<Z!b k Vd^ZgNK Ci墲CE\ cy|**vaۗئ5A/J]vR~F9N#?8q6p2G|JTmg\/x$ChZ9!ш1yA P4f^;7oeqe4Ƕ>/Wd$Tֱ͵n^kw?. F%e+SAyяqu!43_Le)9< k?J9Bi,cg-ϩ`S*w/L}F.53}@X>O>G,f$Ro!"`as#V#JĸsJT*зPk$ApP~v %pQLdi$;'}Dʭ]kQl%} pgmk 8Fҹfx Jmʸy W@}&2|΢sh 'e(C?.ǼOOwK>:|;hr-o{9XDȞ{#q!Z!v1 MWϛU$t{#f \Oj8AvWCJ/*eMg[ͳΊ )-O <5,fz#˳{YSPm :jJ U{1.S$ǝ;_yapԜWݩ[% }u*SբL(18Btk~pZr%T)>\Oڼ[ S´q kdj " 輿hYC>`iXC/ݭHгbk"5C Q2^hfd~!F?2ȠGSԼ)H^q̈́.$z(Mk@)@N!. fifY@Is4f{MJ2}DrjZ^{sQ;_+^J%͹B'ΣGEIl;ofOyʁ:R @Qgd[WJf@ <ۨ脾1((~^!_qY3*|fph +7/p8_ZDx- C?>k1]YG: T^5r;:q =l0*F,!#QsHK\()kEːT过nen ⶟;(È]WK:/qEcJsԭiNh}"P>&Acds#Wzg''~sI_ γܘO{_ h>r/1w;?j6Gz>^QJS>%\k8雟PP Man2 @ښy^ׯ]ۂA 1\ 7P`FUodД mTYgYZPK!ATOTOseq/oss/snd-seq-oss.ko.xznu[7zXZִF!t/?O]?Eh=ڜ.+i?cB8!X?0JYzGu278񆭼U?dY!O pjQ9" y8)q9vഃ.@녅sèEs$Oܖd~`JZ,eb`n檸\xjVǚDa}B1'lp\(|6>AMqbl{c5@J]Ǖ3j'UÃ"7҆DYW~hp;t 4B2E;246qL BF^| $'Pd,h5Ň.S0?av:ݚ霴-{^@uX[cthGIX$ Y\.LGǑ!c҄7BZigf뺨zB|I: Q9ӫ^Χ,@9~)!Ǝ>ٙ ٚ83{B#l,5gcm͟) ƵS*UmvYj+?h`UT :XZ#+eKQTlG?2XT06Gt(b!<0o,DOdYhSTiVvg9cɀqUu:RRc> \?Gwϡ/}oHqXĊ%'5\4 q7оM'[&lÐ>7-/F=& H\ LLc ?mMWA1,]@maj:F>\dʒ/~cDt;R4_Gͧ D2T?BZQ;n |BGEVm Hw{PsceO|ɧfa&9?O.16hO"+sijAxeGYUd^JFu8 puCntZ$W&\gS&4'sb-v޾j|g*JقPhX߻S;zދ4:Ld#䬐'٦8(6Uxٙ3]oxF^tEz䀎o` :K;ȂTm#2gj3/tdgLLV;U9dWR6)nHu.L`%%ĩ+uj` /YGz_hSuu#ͅQEt_ߵGtڢ:Syc|W,+q~%E#`Kņ,-T1UG{3?^pd#߂<-L1}~ơ: j.=5n’,YěRzncRHd iM<{A@(Ps{=<;4G 4H 8o,dMJoq%[_'uY_&)XW*'<Kxo/laK NF)ر̰rYgP-?p P:>WG :/rnaQٔaJ h?cQ/I3_Q];ñK!L tl׮4W$ C~l'\3֘}$!dt~½bCJ*:ZMgx;_t8 {F͎mol<ȬYv7E~řnjYZH_PֵT߶C)(#d[|x:p6lNI~Գl㛝HmS Xb~\i  ~t졬g 8/]$g)NCSHƺ8%v+~&_@](&^))`KIy{6@3oiђR;jFV&L$-`ݻf ί8U'XVB;h1^=)]O6IcgiB S1Me)"nòU`?)kMmo{Y֚f V*#Qe.02ӲugdR\? ,AGkw\:]O.l{0l(xcϑ2h*r~o*4&d}io:dX_Z?-Olc)ϓ;=;ҨX_U2>[ ]b7׃4&<ͺ*p;Rs!'|`'nSޝ5UR;{ !'4MѬSH(FyUZv@&`喻^f{X'T2C`8gInû}%mv[N񷂔K6uWs[1p;+ yF߉;=4AF=Uwy u:)v4(K2@6V&T 4?S̷aE:)) *Zgٕ꩝nfHH9I59O&fYzVR)H K 0DFǎš%V\{% ^43?;h58\&F@Q4 p]n/E#(! I#]cW'cԈ_:<@֏>DgF*C|9 w=\1> ,6_6 h1=]Vmt򪱾u]l"=]U180 RGZiEd$"r0e$Z*2;*ck(/v13lBy(Y<'O,!Y#>7$NҔmєinFE] XKoJ^̚G>0Wr $01c[6{ bi&B+XqQ18p\$5Z8Kt325DtߖbIa_%5' Qm7eMsp_R2e a?k>&,s^ACe}eF&~T5(m\`<|6IJߏǘ' |)rqlC 68e@#% 3$OLĮk)ٰiG:카F9yt.U!Wc!w)QmK5NmB@˕(5T3_5#+h\$>N9ܡ&J\ RVkOpZ\ s#o/p;H3;vc{U=`DeX4,»-y*#$DJ\s!Aii?+ޜJ9W; B|)5'zC&HI( ;R}lEu$@xOX4`\>(4 B&8^&_Q1BMIEz߼vP=q 9B8 p7|]] o+I :.j2E 8Mk$bMph@n++Ec 4m;lQЃf1z,w"c9Mv\)DH6,DݔMj?$~-#^?]励lL %$ A˝/LvnӲ;s䋿b[Z]d`AE kUq*LSܽY9{Y[&~2Ԟ;g ΁ǑaאnڇM$o0Y!Ro`X \(0&:HE(謜1G(6H0;8'ڰ[qk@Gs`EY9(hr8)2{[OeSF eVY05oY 0WfcďJz-ŔmwOTwRqPcb&tӜV;Iidi[J3mmrslll[)䅳p*-B?PU.ai6jԉ Er[Z Ff$[ &5n&5s]\-+o" W=n:s7P`NE |tGtTgmYj}1$3',ebTTYcl:@܆czMðvR] BgvK~( Z3Jv8Nzi_H0yNغ*usǷjlyN;{p~ #B>Λ^H ++23nEB#/@ɋi~p4{DoS@7R@߄jA:^:N~*9,8Lk'`xp(JPwvR$)t#7<\82x?)9َ5Fb|ŏ+Xvӏo6]n}<#;٢R J`a9-U߭6oУ;NΎqNtycG?Mpw#4>L5퍌z9i|:z_WHUR4aYj-6 FVq+ ++6_nd6+C;x fU81t{ovGAu!ZSA6H[]qѶbwq({lj azj><LH?felH/ՈRnYddv\R}WNنt;cydc _YH|Nn!T~{5Xd=ePMI{o۹~hzH{i <}UNkë6`<q"OO-ًMaQ(&ްnS*m(aQ^A;hYh)/e`ࣁ +Ruu6H 2 > aiByTM~ܗs= 7. 3v;O\d)Scv>}.T::7ʣyL;a,I'`\V!Xwe X GV0ԔHr{D>-@7lٖl5\c{kW|+PWlecTphӫdQ ?yW#Q}pf/7}m!*%tnsje(~E$9l_0 62`- l$TB' 0K2\#@I%FN;/p<¶OH;jIV։AkAx[kMO8,4l5J-) "W[k\T}1›MijδӒ}(ۖ'S}fqwa{(\=эӤ,f 6}%-;^ʩ; !r225f2d?Q=j *S MXt{`'3TOJ[6%Ƌ[W[ Kf^r85BV-kBW!rny5O9G9cQpۺ`s?e{/ E :ŜcۭG0+ނ]H ۊ`zim;_Kc3t={\C0F#!+p |&EDqOMOPҭ6L{.|KPCU2co^\Iύl9ꗪ'rI]4Z$kMu_T֖Ǥ/?4=)h",;z5zH bC_dPH{])h> X~po.EƧʾ0u;T;OJj UB< M!lMxli נ˅A`{ZMdD^SKĆh/R<)TN C%0afZ;A(I^1E+IxK1sUqpc+wA=g7ɋV}mcr ȝMς Tf7PQaD# lVWcbߣZ,YvmnӾYFcGDbW񶐩0It:,Ḵz@< +#%MW{v}c:ϛpgnQVhԛܕ JƨKgp3k Lӣ9[L>n+X43|SUKZ=\*%j˪~8άH3ާ@9 g>}QeXtȿz5`ꛙS/SL85+b|Ɲ5cjFס>";$"ԪQK(%IQ"*;WV9F\~|0u(R4A>]hk~b/+=~mCFzE 9oL1ZfDmN{y1;..4SitaEdL-6J]M_x+5I%9vjG(>4qp]6մAdGudJc"7kdՠ]_̷'.~%q4!D+q=6.OqF $K(\"9n8]nhd1'I= eOf>0uLj h>3N 1ͮhg _,*.Iٽ+zZKNmBX*(+]L VYHR[.뙿˫D&ƛQ+ood6䑼| {Լ"2C4%uKa>{&ٟ˻erL{n ʰtbɑW:şAuVz#BEOI 7ruҊ ̥xDVR3Rp$aǾ)Q@֎^ZaHZJB[B_OL6CUG5nZ|#h@x)SsqjWZPpf}sS>[DI/h3Djc xw`(^ Opڱc\#(V6NVDy+JfnօnщTE@ ]d#="G#Ҹet$QcnmEK pڢ޼G\0~mˮY>+ ;DV|!r')Ysݙ'Emf|1?4O7kN|lUHPv\rc6ЁSP9<羯t\Zb^2-Q=*\2&>o󎪏B__4SG~VZ4y֛9O߄ZJъFv. >S9>wyp3 . z?KGB;.~4hhK^ߢǎ8Ep_a c5.c.'_PtL`Q{"/`ӶX265>訦Q f_2RvCET3:P^9l.㣢@>ɲ f {UcOljg .S(|ʲ+KT{{m;ٰC`P b/A8sﯷV_78$.TS| E5q"C"8(NR)q篐9l jX>C/2)680e4Wǹ1C0R?5⾹dž\ `^v/.w.bQU9eC @ܲ͒D]F" rOiQK^IS`&`@qo*'5jl}߄={r}͜ єY_(b}=~KRP7خzCp@$+6s& csXU3ML<Г+X{}T昸TY +IpK=#\%@t\!jaEc+¢P!Ò%c?அxT#r^J6MH$pq?bB00IYp| }/[<1/GjLv&{+ ~_2Rz]7W#J~[t1PSQqՋ"~e[v"V<ҒLJT쭒ܻ'~ z`e7 Q55%Tњra!a޶_:XzJN P89ՇI% 9Ծitx/ :췇vO`x BNw8D&`5Rc({+f htƠcAPPP7Dv;v:{)ad`IUYq}aY3Tr =$A^o rݢ~Z\qǻ G[$o?ǔMnܳGA9=} rNJT]Up0EZu+#B~CɾͮPi*dOSKi$*t-/cvs*UfcɰU#D15w9?0V?O"I8xS Ȫ+(hOꮞSeYno¬*x䌉!%ߓ5l]y0"WUr+e\!#.@FP{謇PpZKL\M"'kؓ~eIx=`ְr 1>]kTQBAAa?QyGx=hដ֟nKɑҢ)b 5d5y\oo ! D++6LNRuQ._'%_A^*Suʡ3M5ў=Q:;խxE^ q#VŢ5oEڒT >Yx;\Ś^N% .qg~N? 'SGY`6s}&q?oJ ET3t"]쑨MFs_W!ϓQ.3b~*OowĩRZLJ`V.mmP!*f{6S*YPnr6I}al(;Y/)Q,yyߟ2i$OsLJy Nb5TEbyjϡt޲''DZ| %p.y6y4#"?lt@]ߡ~@ m(^Q.D3J.wl_ҙfy&B R_$ĺyӓ&i?EAR ?ҪJu+]Fw8\ YP+VcݗsJywYLͭnd|"E E{Gר&VLd|CS^ihɗS_0Cl<jrhQ H§@>Z>)h,w&`We ݺkW h=m#a;"C ӟ 9~ѡ'70N4RmR IW+]O ܖqn"4wUh7ąrWΫiʇ2VXΙ= `=5aL<3]W ~>Bk)t"$Nn:wgd m5\zR-d\wа+Bv2.VSS!&. =iI: Vt45yTHnC-CsgaEHMrEڮ&J"%;si#iM߳FEECcm-@mMV0$ n?>vnĀ 7VKC\{dh7c2EMhl[Enk KqVp'38=(AU{/Ǡ6GHî9B;}3aeЙ19"&A% G֮<'%nsEAhkTnD.3(r8sj$!aS\"dXp:PHW+tzEB(<8u-"0Ɍg54j7r+)y\)>ݮLOeD& Bgxf+{Džmդjz!b}?(T𾜬H:9ʦمJvxmX-0ڂKgg)f34w1TQ~gѴ-d<TyA`{ `~o$7"R!١630ը Y^{?&;ry T?Mw Tڋf"smd(xUW2TFSwos$8"ޕ7?Y1Vn ]f%,#䦷NЩ-PU1Fz;vfmyi~̩? 7.1`PGg̉u{3n?'e" {SJ_2VnE*<U@kCOJxr2=ƣK%WYFvW_Qg.7gEԆ<Ѵx8ސ@]5v?¨F!`o"Y+n>TwK&ms),#CjobdiV㌗%2CM*Q]Lk@52YmX*^@jdw0b,=!0ՌGPuiE>~M9Y'{"^&MKmGOѪqs+C޺,ʋ# Utf;eMCrar =)A3LK#-w1d5wՂu{M|H,'$6J)@%ã|=Kb ީSj}AY]S !$_(ZzQmn>8]~ouEBHCUk^t:(Jog@A#٠„46Ԕlt{!@SqG3/? 3y`|ءMr<ģޥN{hv>6$sua#01JzlUk$ͺ,A'A"gIEs\.0 ;}NSm"v\+"c9cCfBQiJ(X^b$$;_{EXMշd7MT 1u*:(özR0UOc`ȥɻ%h.@/l h+U6s8KV8E4W# \!gBH`B9j%EpCO6ֹт"m}Whީ}>%%-&.'IiXÇ ~Χhx$<@tEtULUηI7_N>%} Kw2୛j2'ONd@4gǚ^pn\G.a@IGY6oj RoP~f{Q|>6usyVuqqa=0gr^lh[ LQ̍L54FEm4Ґ=ǞWe_&5p={kZ 8W,L/^;yю)cr `".˼mDEttN| DWS2a;DՂ؂>ëN(>  czF6 _PTI"ȴ?3kHifc jzX㧰N{4<Ӆ:{1̈ |3;sXdܫ~b`&XBFec$8&"a+Rl)줇K`72j5 /:/+2Ky4gi"y]7ŝ \oJBJ̴ɿ]0W!%j!=ϣH-(?&{U[w}9'Zyq " C5Tj`-Q 9[PC|!GyI@/_Δ3f|RѰ&0jsg V!^4~4ZdR .~~B 0򪋘*Ȑ7k^ؽI٩LB`E0ni=f{i{\BO^baK{Rv2l~Ox&m-"(l]5nyʮ`@H-qrc Ғ_ZzV6yMaVNF)/'@4dD{{,F}NvHSj9!khp||&~ݨ|EU5u xG(cwc6;uǒ^ę_a\΁ q`1Ĺ36A::UsBDdUb*3(:=fiܪ.缬$ҙ2̙r=ѦvWA|UhG:= =XD#Lom͐_cjXTYF%J˪)IsoJne: y iR:%n(׭Lhdf3}Ka~G@o*TTW2٬.oP$e?Ai0j1Μ[](6wW\>!4`0ZC2 DG?ష)EgWg.k։=`WY]|x\NE\R+`W2M\ԥI~쬹,ڳ2؁EZ/C4vSmBvj>YPQMi(Dc~}9bFM^8|x;I"8璅y'ϸzú.ZHnl`4nܴ 'M4nM+LյJNs#_>WNh5qcO@Bu ;$kA_lyv&Gs=5IVNw o6Ě>~ Ϛ@[yJZnvBng]ňI$Wq}Ҍ(~Vz9Tk""ǕNЁƽ3'YQx%/l`ˌ%m]'(Fd@JNg=Ei=lqgMiG[ `m$uyWqM'&dM:OJ/"(?;-}SF:vQGL;}kgdXdO'VFr=qg.Cjeln_(*&_l`: \Y]Q.P/)[=ڍ5Zc;p9<+f_Mrb/z3%ӟU{o4x#[?"C6Y/b *?l-g,]Ekͤhc">сzd`Qf$03@6DΫoR &Ժ+/ .s[|F"ZiyxJq? ZXaS{˓E9߁4%==hG(Zf%a)?İcB@,ڊŸL<@ѱ>X}rm ڻ<]o~+oUjFD oo_`DL%jlPjw%%!r$m' M$0tcTlYl< 3,4'޳2eYlj/Yw_L\Đh-K}^,)J܊;O[m6v 1o7X`* x,>!RqY}bO d9&" HMŐb*?瞒H҈_vJ۟bX8oϾpxQ&ٳezOޟSNtiY%oj[v՟e2!:p[Eajj%H>!␇zط{$tGNn=VtT~ZV } ~y}ʎv[nCnD7SY%v>Ħ eg2FfV,ؚъj)8ڻ}E#N9|LkUJq") l&t5B5{˾c ^ݶ^>' @(Vu~rÑ=ޔ!e gq+E|;bu0Q$ rfB8TRti, zzE#Gn%:W}΍T- amB`CZ;1WI4#/@Ev$JLW렚 8|: L rDwK(p9hh<ُz~]}QgazN*ѷ4 +4M +~,Q@ЮѣEG\SsN$e;jL<"=9A 2ڀ2󪥲ƨ.'ta]Ln?T ȋƊQ~B0TQ]rs =pZ ȁAM&NEIBށWֳ>qkaIN&LUpwiOړ2_=;\{)dWyϽLdaKIWCT$j6R,ho˂^tһ5Q92ym ]\{دV(FW{Lղ{3lG)<|{js\;HeN$R2>1Wd )@%d:)xֳH^Jj|8W *.aE]RYiDIU!o.K˨̯wlP޴% 1g*Ufyq$OJ.ih(1}ioMe51av[ا! x;/#7@+ҝ1/vW;]iSlYJ<̓72I5A{mڬf0igYZPK!lvseq/snd-seq-midi-event.ko.xznu[7zXZִF!t/N]?Eh=ڜ.+ȝvv,F8&[\9}t‡!ˆx$ވf&v'2džÈdIH6iڹhNul{X"kcՁ!zP&,IAq!@v?{7.D2T ڗH_ЁNҏR0❠QRKHzӗ~Rƛإ]3Пd7/@ŹcBogU ' ˷+ßj8r;.A(mK̒^CH,y )z+0`gc;^Ƃ_,]ImV{ <!p>;TޖGZq,-dFhr/fOڹuī'nElxh_M =­.0 a,Jir/0ma 0 RdHJU8Ɂ%_)740?^=я?p``<UV- xpyEcsW߉a~}{:] 0x}Tt2w5qPʶņ0M8q*/{ ƼјjPvDJ47EȗR~z=-(ϑ}),|%/1љ,FyR#QdJ1kRUC6~o|Sãį 6CXc@?|cKےTt ? -%]P&)Z\*)#T-m,iWGm"\MV\Z.FoEfhq~5MpN_:8Y=%:?-ՍSS3E(V' xSl;:%!u4{h/+(n74r׬{ Jqqˑ6cXSgĒu#w+CX \RPy݆d!P| #:Ռ<SK>k)*,Cx -ºk:peFTI\@K iUr]#ۼaH[AڙRI\mCG:,B sX ? !2}h(zrx,0Ɵ|?.AHZ |=Xj?`3KQZ{!Wq˪fI;EW'[Q|ۓICQΎR YYrv;[ΜvDHׅNbD PM ,͹> ^ry_LG*"je+㟐 o&h\1D8Uglwof/h (Nj1jɢDYžRvڬ `d d~?mD*8G/{~ˡ&C PdN,k%KfD'ߜWƔyQˤp|G~*ԑ( %ö|ZgǸlW;';ƱC *}~ ͮgY 57@(Of8Q/LxucR\Mp/Gf)V373*5qߙBF\0%c9fIכ*3=%,Ę?8mm`K̼##-PV*}LvpuJhR- 8oc8 1O/Q_+xdDƸkEDݩe8=Z,֍^)[7J)ܙ!eIj6v%. h9%]7,; Y)x79,\L!@LpG6N8 3`LHdu9!`|휶*:OFoSA0*;~?=_,d(k%gu)-2B5ƛvv+n=OQ \q7J ?VUhT3= 8_ +&PyGDUl^QXU ͊h*ExaBbYW?FGW7`X = Y<~Pbv^ƴjCw@mfp<=$]p| Ѹ1-{R m5K.s?+“Ӌ_V/?-b1L"߁nSdj%ڐ^d8&o _pwg/w!CwAH3TGA@{7^V{caL8]j:8z,!-KG|%ƣc!;6g,HKYlBlWbS,Z>q¹2;a}$SH_ӫ+n2Qx9J-T܂:wwzbk{IĵWށ%UnE!\fۗ)Pk?㘇`pBރI$r*8,p9j;욚{7 ~i7+a!sYLmJ+?POJqUL]~BZ#/?kn@%}41Gؓڵel3(ϻDt!{JЙ]ptI37t{Mcz` Ol9SUN9_XRša/yA*Ad4D=7kz໡ItvHԯW0?2@P=W=tIt*?`[YOVН#]x\-6%(4A$@yJ J UЬ~ KaRPcq$bEzDD! \ ^ZO(gyǓ<!R`*|fߘKQ׮>K@s|M;nJ@yxLol> zԯ|b!:0&/gkDMgh~,k1a{樿KO,fC++b-'lr%O 7`ZM_'lmCg0H ;}+v9{w)`SyaɁzmgF, 8MQ%K u<J|3dvĭ9r1nQW a~I b؁tcaF;` [*Gn4@i3웥Kh5E]Ixb,YelMIT>ro..5N_Tbe h.)}qB_K^|eԝ roɵR f,sr&k/ϋrYt5M)L\ð-ѪDBGd1I]f8Yfmf:1Q4vJјezjHULSQgՕ 6>4WA?v<~U. TU/.zemO%WdӬټOϪh3Bް0lSR ykdxU[)kN++ &TZH{PdRIЙ/k(uܣJkIҡgxݳbF~&;=>Xoڔ*x3l׽b>B{)VIadJ%2@};4Lx*f{CeXnŪ0oF75Z1ҖM6)>ѿL̕9 T.7`,yLفw;+EI|:|Y/~bTo~wuJ_)b?B%񝔟j`3_,XGgcQ-?C;˺B5-4i4atF&8=eA/,.}?}2 \" "ngSb`tՓ/:~D h+@+V`^$9g3O*;40{iBql@Kׄ\J3W΋U\Ũ;ف"&hS\*WdJ(ݷS]uWOޒk>wR5S\pPLP9\*ތԬA^7\\=L\5ǥ ltVʫ1iOaeA֯S`f1g@}{Cy?ݨ~ubh$7ҦZByDYv4+%{5 r_`waֶ)AĠknR>S÷߷u9qLYRx%P wf!7Ly v>G w"oޕj,"@pyzҊȔު ؚ Ļsx]eKLsgȡl2pYwXo_~u-5Ph˛SMcrJNovFGw6/2f}PHف~ tSiVz{bt=j;eC 4.?+H4A_Ce@ԣUBB';I]_WI(kc9 %wgYZPK!>Z seq/snd-seq-dummy.ko.xznu[7zXZִF!t/* ]?Eh=ڜ.+ S;X~cL pۧe"QkJVܿ6&uNnY^]CM #'';"*zLTpҏaG#ζ)>'~x k&Z,\,*o멟 vTkU2/;ހa@>͌RZ4=R'7siKKNH.䆑{l}y ~7ݬk@^޽Mcn^TEwhМkAzr[υ#y ޵0ڧvFpl-@~ԯπ<]Jl_HzOs~)A";mZi<+}D `ٲODMŤ&d B:D{;.߈0bhntH|qK*uw`Sd⩊&p0(ιe, !}jn;4_w [yC?[뷹WBFt_/NeIi>xGZh08&o~יsV<]LK*g:mǢ1wiRpޯ{c'aP\p,-8iypoz>筵A[am7o=cA& O KeP_RIR_bӶD^!;N&tgѶ5M5sS*N0SA{X-@OX0!39nS'$ \bSv=X'\ސkt4$ GhA*[ޕ{ $G#տ<ނ?7I{4|g$WVI`&/,;֞;Кcr;LbP^;lQ}sֵ.27СBv *L_y W"G&HHHI?QpsLRF^M p\,03\I+0s%Yy{NNR 8QDHdYVEcH5ظ0ű02k:+bwl01ϬS;3`F7Duw@0Bmx$pц1ېZE B_t\5 8 ݅`'eWboJM~nm%6%Z4}&OB:`Y #B&-w}-[/.5}֙ñroD¡V>hy1"P|CA]ۏўJ @1`ӮVA OOtӖ39뮐Ȥ߁@~Gmj;NkImy'mPF98G|%v͗~b b̽Cr'Re~^L8N%0)8,im=R?@C<%UZ`JKhTatڱuJzw㨒߳ȸPb-5c] ^+,8?'gӡF‡B䚑_3s(B{4ӄߨg*zyfVPM~8̽.ed{DX;(8BؿHëb0:Ŵ]}nmVIA.v9Ip=^wup3MAjn#ĐטGiS,IJE%x| _㸐 %zmW B?TZ$  70Ɲh=UB4-x-O9z>Ῑ'6GZWCAx"G !u!ѴayQЧȍ6WPh{$Ӳ{j%OE9銠q+H^'2#E'[U$W9*$m~oIE2Z^_`spȇFV1M-! F+4!o V+ӠpHRͲ\{DDM°Qy@dJ;mk)[Fom{( vkxh[" 2ڎęETx,sn87`d }ӌӷ!cN~f%.vO2C=u\MV}|yX"ՂaNG/,M5P*?6\k:weSd)Qz}=q_Umz&W,M]vzM.>5k0|3M}Ulz$gYZPK!TTseq/snd-seq-midi-emul.ko.xznu[7zXZִF!t/C]?Eh=ڜ.+<N)4џ¸<I,qx,f_MJ|+80>{ciн.r )p:m 6M6-I@Z+)aZ!f|kr ﲻ&]jV*Jx4͛ӝ.-zPJi"fL01~HL$;{B'wGe:졭m ȕ2$gdڤuܦ90>9cxg%M0ȳ^c*Ur6epQej0A:ġI<.)HNJvSc[d<.@|b ByZ]{{0u+3 MH v V7NOV:OG_{?"6Z+_,ؠkn}VN{ÐAsB#E;k^הl*We&Sٕ=dGeN_%UKa>0v&gQ1M``ӣF/{-R5\WkħPz MfπeB vG<1ݼ6 5彖JJL;|>~P!cq(:P؇*Z.T)3cLʦC('+zQ2d叝vyņI5 n-p,'O,v󦦭ɠ={4o<΃6zfֱiˬiX(cSAAK_LT9x,h}1Ul#lu>qD̥)¬l`cYT!lٞj!IA~V3kt*T_P<+@a1oP bmU|2Ξ뷕?!9BL̓٨B(rjMv=n,? ڈunSkSDǪU1f), |Z\5@,e[t;9<`%f|Y$4ꠎW} LF"ļ5t97ˊ]ԇwyR6akAC3؏Rg6C*}Kv)DGe/te:x>/ʽ~|'<5jf ? ${7yj!̒S r3NB05sE4/e,4]WtHMŃ-}xqEMag. VKf>8+1U F@|0Uecsx:lAYG4K-0SYʫge$qM UKM$,=_sBn@[79.gIerx9~4neEC%L~nF?XʌRo쩛N  Mc$u*MΣacG OFU"anٍ3Ѐr#>-_jTxd+dl zc%+|Q& Z_l=2nOhy;w=\,8S5V&@V1gĭ5O%zܠp^H=uPy#s[xI XEgDYTltMy|ʨ:H]oM'gP+'έv*/훘JT?_sY3WP4ۋ3j?%Ewc#qw(oxr +e^/yz;X%!VΎAv* ӷ󣎈R?8Z0mKΩDF>:zj##;TJGܧ=/C)r e3Cv#D9T2qH,IsFEɦ$%Ҧ$[Aw郻+롞N4Q`ە6sJpN76:V̒2Tb &v+9h{y+A(<է)Qvc'VN6X"ǁ;be+)xaיLV lⱈ3)4&kl3a,^0Zt}vL˞Ɍ͑B yFco߻yJ( ]G}Ծ$w3+70U~?9Po]jj đJ qUt +d5BY7=83NVY䫉kXT@nnE9~A icip-N]#tN ?z֩}ui(@U|#ń2tgHcp]1X4Q2PRrd˘~ Ỉ3ؒXI,ơ}-Qv~#{m*=8F'̌`Tj^~WR3iSa|: pu$G$7b"-f%(q澑J?U薚J_hK4X'u ,i׳  F N-ikc;N;g(+&<ܷpv2O?iRj&\ZoyQJ"EVw7 ܣ$zƅ4?guu 79Kws B2cMe"הvaڢhRPХ *yx_"|bEE'|Tn]#HPHEAsMyyuRyw|*$N\Dq~+dq4 Ԅyݵt;P<$0})!>&ۻ] v8;)$})ݹE+ש KP2 `w:ph>s6bdlԷkR&=G^kNU8/C7\zFnrTJao{NӂyY7,V;UX,wG_+qp"DMZJ/6"S:p},]FKFNa.87"0+wdK9r a\%FxTgf nZdPaORj918?0%,LJ86Ϭ@x"ḰXR(Xgiii4&Qog`T5>C # ,ۭؕ[ &l'7*^'ܡFQ BU_k6▲#С1St@RĴ] x"'#<9K \%Ea)0t5H̳I8]8^@s ,4, h%w't.~ 8%qb@hqLzj&ȇٱgYZPK!seq/snd-seq-virmidi.ko.xznu[7zXZִF!t/[g]?Eh=ڜ.+O;`8 +tl=;:SGKERsg}BMxɧroZ/Rn\dzs L»`odkN2c^# ٟԖ\0) ["O' t~i[ĭ5q>oF4)3:Y.쿫G[}%ާBf;Y-~\Ygh}Bjvϙp}U#)HBmˋð9ʾyNA ĹSFjM>~=oNQ@+pLr3ܐ2TH,V 꽨Z7Lϡn(bas"M8d؅!-33UXboIbV6/J['ěa?Q(jԭ(Tii+'%,ġv~ EW X7{*@jQՠ{pux|~a'.aghRGD8<˜@}26;e4+3! Ch3J K`v%S%Nii [ΐm %Ř-4\\zB 0wq,A FAMݫp^nB'%! K< ʣ3ltsj0JE L٢S GVs7V F]sh:Q[SfnED/KMv3[]gnGD+3.tu S*r 5=Sg.RcTd 1"iu[u:Ish``nee!|c F)!Ĕ47 =X>Zp >`mwX3 =u 9D =@" hPDQd"OˇA8ibsvw:]G XیG\\ uD-'7 v)4-!߁"1\׿yOR\U+Kf/$tdRwuR"[ b~ ˱hw;%zL/c{U. GcD5/dsŦx&g3~|\u XAXPBJ𽈬 y q[٭)pK.v|b-)<={u%&\6MF5w__\eNۋ&xI Fvei~"Kma[`?ۊ#I c_B%ʘDї EPk0v_$؝&i(Өޕ0-VBh p),@<שǦ"uW `hJOz}$hF|sэtp2\7`؄ IPOUTY#uw[չЃltae_KwE@28dI>R_R shvbЊ~=,) &Ic=ckx&6ޞƻ.Qm[2( "n:݅=*c} .%(1Doh@=P*m;9~'+<0,,<$ʴi>nKPI:ݎfGЉDҤCc"Af[2}.jZtiw':29vyr N2XŸ{Z iJYjD~bX~yg7?_OV\NoV#4N3\}s1T5W~??dh9؜S;}N=:B7=%|ĄH+/ 0zЧ|tf+]&0{[ 3 hؼ{3U2n%APg 67~wyR dQʱD(ӼZ6㆒TcV3O(r^j 2P|F M ƦιU3[geJIA?LeLeմfؼGo&5𜲩0jF'c|Cr @-'t'_z'[;aODco.X,7yOM(X;";(7{]qvEN@@>^W R16Fߗު-e:]tZ@C0̤[95I[7uZ?&x U@|(x=+_X3#n@=OS& kjs 42vh͎ }ZMH"is>Fk$qmZ;dYsQd;POr𘲊׀0Vّkcݧ{ Τ]DgJ`I+/$R@ٙM}02ͪ8:^X>;sIPx~.*c}1Q#Ěz Pv줃R)ԖhXI;)?K}v*=VՆKdNPW3=B%)%VT 0X.MP J'Z.ǻw~bqP/$iwK`!aw_Tkfho݇a(vjk=/"cWaE!c+5؅<GsU0C;nwhjĦԇ>>RAp{+mYmA$Z}_ ; Lɐt,MoTՕĄqn~ H F!`d;5)m .iJ/@Š}om28q=YGSwCM қHyC?_&^kyx`o1^d#P'*{N^wOHS)?]fB'jp.qj3;GSE_d(7zVޢ5滜cF^Ĥ~ 3*L9pZlŃ_{dgAT>F\#kI[&w' i ' Y?N#t j(=gGKNz/]^Qw,(\ex+w~2a pvkW h|6@A}?:[6L(<`Sr\.Һ=Mp$^K j+qԩ t)gYZPK!Z; snd-ctl-led.ko.xznu[7zXZִF!t/ P]?Eh=ڜ.+̊pO?mHjlqzbfu& (K)PX4ȹk?J'^[›8D~ 0v&6k;$ؓnf&D*9 ف&!kHᓉ`L t (յ(76CYxUu?edݦ0?c8ʼn3-_r ъĒe.6𣒲,mD$awۤ =Ƞ~?QkFc Ūr= ⽮O5E*q<% m@,=}}CT2kXyɿNc"h:bCO6k<ͳ("q)J{7BpE8c:ޙ1*S?2  vpcZ;@ +>0B UG)XKjSY/LpM#m!WA3$>0I@ƻ^ zrUPZf.L#uC2׳ڗT %D Mޕ P E!OHf O?VP?Znp肏Up\x.o g?UhLCЙ7əF(ZEB D,`x v;.ә7z5Dc0#M9&}ꤶRP^|cLE3f{n8XfAf$z*#V U¼89`ͳD_w%SgbZ׃G*Ƈ|7Ibc#rSzdeq4'BL_4T$*oo"OZ~|߅Qu~@: |BP(;MdƌSxaUt]t 'n>coa"!$׎?%"zTi:Cr@2{Ia6BϠA\B:?om{>4k< E&ocēzb k~9Z$ӕIۤ taYEȺCojg\R՜;Q|=r(2Baiq-f=NS tW%F~vaY&Oאٯ^֦/"}u.&+;+öչe] VZ5rE#hт<=/`4J9‡@+!8]^xA;4ͩ1ہ#P2f5l Z{:+aZc.m'JS5+1q#+Nj H/,,]Ob*J/6 +NݶQ "l8/<>qivb#nliwV9*X+f^"6aɫYSHDɤ8K1W7Pk%XC^sBAXޡPcKPbHȫtbΩ>{i GwYbo~0[w'WjD]j&cuެjpL{Wf e[ߍ  b2}jmDrаE20Z˔y(g-8t"QU,𓝨e~PN0fH%ٳ>dێE#G#\* 1 cFj]Y<*Uڜq @67$DY&T*ÁHbј_wMENTW4n[U h%N@?!]|%gM;0Wз]*`_.|t`UmDB^$ x%Ji~8 .~5I?} 5+a}FAo5vL tzO_S4\ 8QWuߟ]3ܿGwcg$^(QU6`3VӢ?D^\VHkP%x BTq6h %s * 'fUeֶw-輾DŽ >]Ba\$⑽Ň[W_B'8&mh eбUX;QQ(1&5X:`EXyA__r]*U`3-9v4.^yڧ+/xA)W cWʃ?l"*c]6m/iW)V9h}TՂO2WlbӳX#$ qD}qZ^9U#gOGSn';6f ^qZ,{S}UغoHڌ&;<=E"6]~@x,߂['DlF2ٻ+sAJdP{weլh.Ph#R3{`pkhq`h'fA錾 ]Ok1[gFk"դshg}uPqY1o6r1f!0%DW-|iYmdw&zu>R@8:KK+tUbrjSD`6ȂPf]{RUwP2\pQ0֑|EwJnBD$).t.7C1"z|Pfs7l(<_;&~2_HK=VlT[v{fc ʣG ߻pS-tʻ,:J bُ zxDz7=ݖZH4!0%N- j@icIRq#q6gVuRE;V+o3 ؕG&# eb$υ[z?q,+nv .R oiի##߲01ƑTO)$K/_,LpnbL+6!H v3Uŵ:e YPDPJI ] p gګN#y9 D Ax.Rֺk긆$֢?\Ƈ҃VR€s7Q( 7#A1KG[Kr#siv;ѐAie}R<,5(T@ny9ͤiw|J޶JD nœY~HKXtމNފ`_ gG2Q?.GK & :Ob*fʐe6?ۻ?>齜WN~"n_FC:1W/$ы2W\\>Izpy}ydP>9{HŗQӥv!:Ǖ,Z_螫MlKa0ww3?[\}b*䉱SnǨGȱ79sitaq#>xg`urݕ'כMHbW!X_3MnlSځ955Ȝ_,V[etTNRzSIfo/_ !IN:-G,>b>D>BKpW|SuDQ6 UEQ…&m hc$%gZ׿IGax~UAFo]R)NLؑ{]~? JLǁ}=q @cM^?3lB^iGm&壷Ǜy8d&,Z(-2_R里**Y)j5ϓI4<,)7ml|JZ_ xd.ZR8U=2jt#ʟLEPb4 wE4Q [q}mM0Aa&h&.#$ӟB\Pr@߂.[tD3 NÑ&Vl9C=Rqqeˇ=%~ E_z%ktT#O0 p]G{!9xϵ.f~]"5[Jk8,4,) mg G~XIxs ks$:0 ڭ_=.Mfp/*W@xbB-n\y)>T<6YR/->3حbZJ!ܰ9yX2t{dGӊ6RLx DD)0qm'/Z WLgW{؍QMOZͦM_c0 N +MhVpݎmBjy/o^v[0w/_SHƯ!OJ3MfcOS0)g?͕A龜/;+"\,گ[k3< G)27\6kH9~5eZ'u} [B ,އGM2MɁ%p8`G2{a@3CCꊟO*5fˎ?z!i^a< Y`,B{_U`DՀ~D!U(,pXŝEIms q9_Cf2l> 0`¨2%;{Ds1r> 4TT瀣^6%,ؔ̌14v z!5~+;@N8j :lOaHaA~ QR>!\}X$hU!Cd-uC$06C$ٺ=-EV穕NH5F\dҪn%qaW)؛}YdIFރƾzΕ'߷J u]&'.ߛQsxz5~[QIRvnX{^7ၸ,JGݭr7\ ^;DgIFxws#O\NH4c˜Zd[ndPd-/ Lgi9ŝӧa2 xhx`r3 $%n!QeN/rU@P P3=G6%2H.E_.hE8>zf3!hJ dֱ"̯Km.S5c滥?P23gwe%3W|q-\-pN5}cS6J>1J82DS /Bxz0%DLaKgi q-4[]kz>RXukԤBFp~y4k8)R!GXmj%}^oՕ6emJ1u$*Ɩbpîi#9gsX 9`i@?g$gYZPK!Xz,, snd.ko.xznu[7zXZִF!t/]?Eh=ڜ.+=)xV8 *W5j\Of M֩TUCأS!\Dbp^I_c 8tl*>; lzpE.u@*YQgU(Pq=bLy̾hn>UCa*A5\"?O&ܜN؟'_P!x%ԥbyu킵 Aes˳0ãRGY"}_ "※k"0rDgkSuؤչ*$_fr;}vIHib/E5egvgC~y*t?,/y3Lj@s,6\Pr=:l FVozǝ¼2wfq ѭr kξضr7(1X-8j Z)\YYN*nd r5[>"OպVzbRe9v3[6 g2=W©v+ùbt6s8g+v}Pw_ߍLjH#a'GPy Xu?cCegOAE2XF|AJR8Ow nCmQ Uw*D=;bhej.a`ylo(lPD)3_Th:0iWG*NS#B1Wj $^ dM+^;f\Fbc>GjqXwDYc=Ѡc:& Q/wǖ3=G}^"J1,fA\>$- "?16Ʈ^Ǥy)8rFKh ~7mڪu~nc16;:+¿5T72rJJ`^Ҋ] '0nRgꜾ]:e(¹V ?[﫛fG DZHmB\Kt[02x򛽹0,<ϾXFr1#o2GQggyL'sX*\.1)%$#s=`A@c)"' D,u [ASK;™[Za IVD=WޔQIR74 }⏠8=Csf?̛AOPEdjB4 łw=2Yil gXҊ)8Ec1~򦷰܈QԖsoJ<oneu1Ulp"GdY<(6IgVcZT,UΖNZ = O呰INҠWoW-nruA㠧w&`Cq07q!Ճ.R<|ƯoQR?u`:W6Hk9f܌pyKokg0ygtVCNE-:mB1}.p2+p9N:2|}@_:2T4`(QzASG}~ ګf,W]l C(ə ;ks)rn %V4O~3dUv5nbY*Α}e6ɦ"X}W)۟GFki"AwuԽFl=H?~Dmyn:T0(yGwV/B~z#\xm |&ϯBz5r(qRJ+k'-FR_:m_[rvG3p}.-+FX UtEwuB+G"/(Lkg*sO"ٸLt'O2&hcndeV l?HeVlʯ:!A򐩒و'M}_6K:?X4>GZl*1DQH@pܬfeó_ejsPMG{ʮ2tk"{afaEz 5 y_N]OYe4gE!w ռQb1 ]* γ!s& gRXT*챧#etԞXW P˶-%C?xe#nAE&Z~f4!g "k0ajڦez /yMFiS ҭc\~M1*67XjlE"sY{BvXB +PPWD\7FiJșQVhy aiI]zgir(mt^a+=aEn 3 CA tRk/}c9((a3:J#s;=B*HTN%EmXbx*9I] Gsb77M,m3z?Ĕp SJ8r5er"{>oDsd\շd#˞Aە=L"oe X'Z$X-5R6r@$Y N ѹ:j _ogc+՛5$LA3t48|f18՜L@(vu.P[‰@۷aV$).yN'YԓhMj ~]tp']x"7.%߼c&U H_#y‹ <H|Xu l'|a@oGlѭ) gޝg/)D&8g'd*bzFp/G][B9wím{X<EH*w@ǛB*Z?苂x> a=?aRd1K֠@P>(%!>\U%%D,uӿ6nr_M@r#R{8ʴ )jd{BsĐ8JS]f+*R VSI1f__j ֳk23Oi5M8P:"JH5RgzbWe]R S qk>S)gI^IMs11hhrg#W3cnÖk6tR}׎bp!} YZr=kȑOv4,a%dv[(] \T Al"BKe-\%sPcj&YaA3ۛiNm@>aP>Jc$C&N-4lafsbuKLۂkO/[Y. 2+TՀdS9Ԗt+YmX?=A^\_ \m J>ݙD`7ϙ NݏqVKqlG6:&T-WpнD6|_LP0O[.=^_N}3YjznĹBD,[Zlʁ%wA^iH"z8"uW!ڢ jc!uKiy²xy^mlF QI#)Sh%be, uYJ傶Ј`,B8<g/}X x5f Wx.M..D`0"^|V6F5+ 6oiɎ4VI0y66&WcV{" N7NYȷTUYk)PHgp@*[lOS4A=@smMHwC@HO1P 8>Y_3-zDDJJfg֚IP@_WIg NpWS|)Z[{+jj.5~bPO`DаohB2ȸ`pu|N~q32ۢ$e;U;GTf&\^έ9QU= ZS3Rҽk+v@!R)nբI+~Oo&Fڨ& fB&kj){a F iJa/q+B9>(3&ծ? 'HN\ˈ8u "DW݃ D KAv؏nDʧe`l"i<^^̋ EuA?w|fS)R;Pɬ2MXR'L[N|ްECɮ챍]/!#SZ#z~n*شڑYӣQb-bWio;~Sy%gI :|#r41|j%smYoKjW]啚'S5cB^傿:ows00CXe\@ 9`$PøyPnXM '/"iojU\k[re @ࣕv/Oȁ[~08F[ zLT vk{~S:GrfD3+H@teI] xj{+0 :_kSb|kS㮈̮" ̻*ģ fnv~l_o9a46 R/T!W AqsAzwB,VXw?n(㰹3*I(-t!mM">{zLJ߫ڎ.-@|ґK]NtP&`o U,09AY-R`\[Aaɠ܈?J:.: ;8p0Ҭ \`Mz'ǙH^ _A|,u`Ȉ]&jd$u O!tjw6}۰2}M[-bD2e2r<+x 2.eNՠEvm˦q ]v/zYe34!s h@v#G;N渜̥F@~ &-GI`GE9bDYhh1 o#M?gV-2RB~o q9|iƯ[^ȭRMM5hZBA+o0#wKzhBj 5!>e3]m)Sxw<$  3ۀ&)u٘''i>T+\wkQ*Ո}XdU۶=J!t}aO ˒).tO77,aM Y-eYQ9&s`V.ȻQkȘ9'Α+7b`% B9ƦNP'k^Rո"i]*a YtiC3&Xuᠳ*ozdP63-`z??Bpe"_pӟE:#~.!Pm\=oj^|0 qinK0 cq%JjLah#Uܢ3)8`îxd!| p<&U~M/])GVEƺ=h&7}du;nA ?1T0 NdGf 1'CB㼦= $LjP)|.1HwcPg8[$Uٶ.cbE ϊtղy4.+FOsSwfėXm oťEb~bx]M)2`ڶ^dw0lu:-hit4+=%}RH[. B0ۚRX9_&Scj.2g?'xy*.o1xX>iu>pLți>LŐũj#lM7t%aBEKӍ˟6QȾHioNV%,͸WmS2'0lٌx]:D҈jjV@Ʒ+.  LC[F0uaO".0 A3DBS B=={vE8.YyqP9h<Q8\S2z}pko]~M!2uC=e2_r@qenv9Spu;N!+^j:/p+LZ J,G$w:-vZF_op^ R*\갏416*A BB4bla*wEӦM1Uum9 ѭZoWTI-r"3uMJÕrӏ"먄U[)OtGq3;so@磳m0vZ :.㿕q+ƱEԉByFd<^t=`ifN a9c2K\v?}#g2 ;B15]2Q9mC`_ny*ފֈ)_ĒF qr+tleRA)9sg6z #νkoHN<29}(z2`YD5Oڝu(uBa8mIev/2,wK'ဤܻY \nB<@`|b۠.!>%x"Hu8X_³(㌛ymHh&t>I/A thc H7zvKmޝ,.H[T|^|K(.ݗp>&F ^.ףK6rjȅ{>SV]{,~<#+\JzT+EKb뷣L/m tNK)R6c׳{HIШK =G| p\K88sݶ&J>. uqqxH|d% RȚWLxOL L𨌷`2> =Ȉfs':[5vVmaB6(MZ^8$񺅏 <{`opL'׌ҩ=@HZH]]lh5 QV6S[DRH5);j =5h6:fUT7,?*,o+Ë/wiǴC^A8&6z*4LCj9uzpNY/-O|y(NuSEnLB-h8L=ҩ,8dWKrN_LK @4=^"Ëo.HkCp}xPnNNcƍ'8Kq {/M= mnw5T_("2D?40;,!^?.ꢠRa$bƺvؕ!:($i>wSK X ? 6+J+!Y)5 DcGj}"k3b -h)@w_o<9]G2*ЦH05M B)خI@L-_xaިz7Xn_ę??!au5lZĔi8,:ƚ؂˳tg=(6a8v~Z."㨋B!̑CyD wy= 6^ b2M, yk7F_vw3Sau (]נM54^c=$FqgҀzc7,^͒~6p+ZK.w`W1\),3~"k97/k| qX'1_yn |$A; ᯩtnq $taBBy&_׃ =Ҭ։ǢQ[:A}*Z()k.=w3O=(!oު3&iJj ה F-6KB%cuQg`e\3g# Q|ITmHJ#5@bsiv @K=KsI杼SriP|32b0ssvӒ+ u:'dyuӢENEQb.G,Z6 ŽVo7TL\UWVtz]'5(\ڃz>SblΚCY4KrzE7ai^N0Md&?u&hPLE2V={5cl yq͊i[Qi|"yHp_*ʹLGO-rC,x{ JvSP!%0Nk%:/Lsh5^`dc$|eioҬu("|xӚ k?@6CTS?%/П1=_R 3gXrs/1]YD-} / wLdܹn8M~؜ Mw7(tV)1}]: W])hs;3 45g\i\{]p+׭50H E/Dv)>W➊drU$˨$iGͮ(X(ǚna>]YIνx(;Ċ&}NWx"Dmϓqv?4 V0 a_ \6s!3%&8eYi65D~Z]N@h7mڟm/<}>Ǣ_ g;XM\>@QWkȥtzSc`)GmPqq 3W|I9Fy%`*ih؏BwB[4W|d9oH ?Jj^(plrw.\ 05ҡ-N;A$hjr׍h]nYݱvw)i#i,υke6]l Ք5S^88e%oȈڕ[)CY쬐96,p~[!{f 50ɡٹI N+N0M \D7v~VZNh>]rN&Vhy?)_GQֿwn l)H$SQccåͨ zOxJ*2OgF-Ө[XkHb렪8]S@}UuZ!6w7;W>2{0o3X8|H!tF7M{<&Qe96-.ת̢Ѻ߾Q3Itҫ?=}\A ZX[aPkemfaL=]OOU3'3ԗdz>ƽ{@>cX#;}KA͛Ԍ!XȪUO/: %3U킗0 2(%|`N|p:1QZ&^Q2¥gLE텲 uemm}Ax`3/frvne"yo8U&pu iԭ+ SegUMq᭯u !cޟ{,pb ȌK< 'پ3nD]M.Sb9ЕOr/ `5IY=?h`VKk9A$F'58:?tM[#PYlJ22,q+@vs$E 3/ShiKn.[B; '3d4; WsqD#2IGDX+M' Ȇjq$NzzCH'sY`ㄵAA?OzxVNx.sΉ@KǕ-g\VWĤJzLQxذ E!¦ ukk 0E_E)]ο71wpu笴#E>J?`zz;mrY4AޢڅO2" H}iEz'>A8e&d JR$L+mD>jrկg6[Q΋a 5fS!QոU-/Ȣ&d4oM@}),CU? C͚"~]-{E~?gM?DሔC a( "$sz%ǩ)f~ٔ`ՏE.4B+F^K;,Kp$%\g+(`sV{b\OGI_POP,Z,-TQ6vUBs9I3o8JBTvc&*)WK? ]?  @ .#3#&2 6GC[۠4csPn@R '(>k")XJ:h-i9J9IHL Ņ\:.V+6r |AܣN?HMNyf#3iAJF^ʇa6ɐJީLD?{oh"-Kj-#Nĕ [ua~HpbO3[(ʐyi}B5 ,&=5 {T&aꌖ.B3&ݬlr㎤ (9E:"KΌ;nupet)3Ӻ[1b~QSa4(XK m:WpZY7I\NCV+?=$-* WK5qIj䊆 Ֆɑ<4q׃9 ?6i ώD0' >S|pً܊@BHNDNh|\J / RCh#IKfٻ{/~4.J^ N.R K%;L^#X QbM3}ho6c=fxHӕHѸG efH W*戸Z-nS0_c/]CuK3hK/qy|simNun~s5+^V$I0"y=Au4ty3&~Aiy{:!7Ap`7(˚S'z 2~&F$0z*:uL}m"H$tx,x1#'*?NX(Y/ ymJL$JB:hBOGrU5<_BOƬώ,OQ;Vն$ 0ְGI4ܞ%aX0%uuݟco]T!eP$Jv?h?ؐ9:EּBVr3(A!sU (ay͒`v2-w[| MdC" Y?#q.x:Md?Z &urIG㻈p#:E^r3bE0K36!W_R&T$U49k@E6Mq/#T{^(cb/倍ĕk R-woP]bW&EżťUCO|֢L  "y.>2ԇ(JJb!%jajXgx߬eF˃{d)?2E5U 6\3mLRTgOEs}Hz~)rѺD>.&蛴awvpdr݂e 9)) ,}ܥwѨEXΊU6nbiMrzk֫#yBe݊_{x;!/D@Bo|gfdܭ||%Xv Yg2 Q"+%(ygfSÐ}$%p:Sntaw1Ŭ-}a((TDcqs/x^VNCE] Si`88n'ȕG``rP= I-p:[A$7n}F pU\zpf@ZjEZMzn 6O7@'A)~Hj=?,G7ap~"3&ڝmבO F 0!ʪ2Vzϫ7B-u,eJٗ?MbR{NIJd+F#!h6gNK>4 ӧ,h=+@:Hd6$>P]$P*¢KP^Q8“Sƹ2;ޭ+kE|e<+U ! lA/ylzz}B:1Ks3M}1Ki{/'k_Ryxx^'*6;X1EcrZ#N\:K7=A_4s !&vy`K]1n#=@B3LnTW-Ⱥ0.)y:L`)Q%L*| (OKF)9t/"#B&<:bc6Km~Eޛ=F >`vl8Hb}c dX'bpLYFWRrScbr՜7}jw>W'djUm<>mҏi=%: k-` ӀWaɒ9PHQCRCZ_Le Q,Y"~̐bTfFǫ];` }]BB[km] \zcXN4>  @LG8SAͨEOv1?oE8.Yui{L$'x?"0~E=yX|%Ŭ:LfѬN%+S麫f=s\2;Tt{+zLя'!ц$->q.!c}p?O\zU€52E gH;3sȁKZ7 S{/;lߺL4wi@/CXKAB%927 hF%^9lM> 4='m.BDMbEi+l`Dh?"E|q Ji;*} X%cInWMRaEѷ|rm%6s 3x4ia˫P>gW1k M`\9Z+ž]R2H, $;L t6 }DYg͑!®VA;۱އz!kG>$-)˓rYl ޔޒ"jfodXoHt$Pl2V.:d5>B^=V"G[c2 f_#AŌLۀ̹*HWJ_#G+Z Ukct.qnULnUK0~1\P#𽁎ǔ}n*X%l0 !&w媏n<5DEV PգB wDWr/d=k)C<зЦ7:]m,l˞CTU`Ro~sİwRf֙Q|NԎbI#NK-zZ4= 5ًyI1ֹgᅙ̐S4Pu^~,8P_;ɡ:ǂdNޙRJ/A:_CuWX]d%AHw_ȫłdkwO[YE>3JZɢk[!)^#Ѕ`1}OdJKAKK3L$ӆouA1Ùߵkm*JTd>9s:_T h,ICb8&73bg Plr&'/#w9l7 E"Y#~0/ -@&b.x\(~~{o: H?xdWɭt'?RPH37qÈ_P١lTzEvK5O AS<74!λ]X᰻?m ܝƒC_F%)4HAqP*VuA%y:6]Z3[rLGͷ3Ǫp2U\#qfgxP݂cGR!` "}84ؽS/Q]2.)(vqܷ%=]Tzz;gsvˈ>H | 3NA8 '5K7[v!BW1փOZSmg9 0U4*^"iRI5fb|ĭ*6>qt7bi1UL95j52̵AgvxS NA1,] 8 1{x9.v } js7CvQ95p}=Yڍ7&#fAZf!޳ NxDD 7򞋚D "ӿnUgw(~&.RPb`]`:02) %U@)#wⵛg]Lߩ%bn(w.b,]M`Whm)ztv^d\*x#٭#p"W{ݪw_瑣[kUiJåmg r/0ʈKe:! Wuv8kĖR+h2~ur-F{)챛ŃF2'W |ŕi{EJ[D'9XոF9e/z_R?8 aXx kg%H%lp& &PH2|$^) GxJjZ~M/[˂ѵm=>)V %Wɏ7yI'Z*99vuKev=wcVao 2AW 6GʪRgONgK U㧀 sale7c2 I;QK\`)Z=K-,\B~HSw=) FaG, RKMt{#\2٤syaF_?T_0<KgQ3X.9:LJ2_xfޘ9oߺ~.M G"l##%%1Cߖ-A^n&Tʬ?mm=!ztdgG CҬVtH_})J`+–w0Ag1l;;t #k>g 6X%K'u*Y\rsWqYCe9oNԯ,^2"'`/FB&:$oNY}|h[ժY8 Ňke~W@ҋ{b47.bpJd~ 7+2r! nzQ5R}(=$pkCD;#DQFϰD's=a,|a) r^b YPBP4hPXv^xWؙ΃];AI²_<+w%֫i021[8f$A-`/S"45[+_HqG 9u~j(ES٪ajr:L "6nր@c0LBOUc! s[6tfSȂw's7i!՚_s' Ц|h׈BN{|WVj:%O <SOs-'o5ψ><{h+5/^Õ1O!i: 0sRx4!wNqfj:6y|qfQGfʷ jH5xM].6$lI \Q%ŦzJs\Uƴ ?IQV`Z')$~GRj8==x^wȤ141[^SV4=,.Z#NnUڛuA9WOdZTv0+ҩ"~f HcNA ٹ/T@3HV" #*n Ƅa"]yoxY-:Wa7Y4Gv/foқ#gۘ͌_P٪pj4Kqw䈐оh\wG )6ćoc.{f#9[ X~_z`A0E5& ͢^;YxO$k-Wo CKwzhG];#3ҍBb ,{|ng0 CpH=E| 0>9'B)&n9[8gyR ~Dڊ2/Aӛ=.L̛)roj]P `'Fk&Sv h?T(ƉG6]>]徹/;`ۺA ko\FbeߐP+ ؗ:0B+lW$=I5TEk9=Ģ&f~:ߤJ`U%{Ki.قz}[:|a8r]),~w: 9ƺ>igC*, a%7B8u٦, yMō%fK޶/T΍Wk>s,JAuxVn 5ECZAPWl 3U0Q@ݜmt\16?4$ȚiƞZ4A7@\BFF+ O\ܹGwVm8X*#䕨ig[.c*q= `7djFQ)a$q6)-CxD'|X"WfY(op4.$m7@9xTzBZ_Y? ~ `wxk 6u?yV?r[v`\-@:gJwnk7/.:5=C0vd٪NC\AQm)?{{ڙSnC:AyX_Iϐ'wʬE;<"P<I/m_wbFjg̪(6H+ rqXqdƒSur-)j"*ɾ iP`7N E`"+tϼE5"Ho?v:etΟ;q `f="5|?~MA{iKp&,??L9??@Uu(żll;EOshEJa5Yͽ1990#G pAL$_Q/K\4!29]Re〃e/:\hXF؇?zet;ھT?8_hSD;^4~G̉X#-<(/A'ׯ+b>4ihi8uGGܫܫTݜ Ū|jѕl&_nKco4RNQ \w[Dnctvi60VNlaoixw^U˿mRz+ ΕHZ?i߲\T4{y.L;vUe2nYb-;.j9Sw7jp1k]8UxҶR;բWgDDi :=jXt6w;ϴJ.Q虧157@ A2 j B2{O !Xn$ԯ7$gwR).>hZۂoz-RGƑ0He b![V_-qmKs64TcW왑R穰FtC=_Ҹ^Rmj#6 lle{N=YA IoDET0^ k)=ƉwnNm $Q3ٯBtn]yۈطypp1Dzo!#kRNʠ ֮pcȄ+k"j̭,ȠYwʍcD@'+mvL>I3ڷLG`#9# !P'bpQ+j48vMҢPKKA]MDH =^Uo®F:mbEL?8ukŤԬSSEo)gPNEX`Y2y5?ӫ|_jhML8Vz/^A8!mb' rRF8WP+$g~MɅd;NJYO' D錓yи \E,Tfו],:LDEti&S**rP=<.i55=kGHN7QB޸ŎxHр7`8C\o,TH8.upzZA~ \0~N/!kv AP(_Ďj?K]%M]6ȾTB;wl膪wT ¶^Q S)Vt!5-'_{S1L}}Ftz~Ju("Za *k+3&0Ⱥ+ Jb B+):o U*0]b#5Qn?|Ծ] P6l$fm >֬!Zו8tytc12 E 땅@Ղg&t{©C|):oF~-=k@|޿?P:*f3y>,W;-wK hogN]^妍^W ‡y, t m }?1QWd(CqLQ) ]?}GsHp ܍fᕼ,*ùfpՒolXc[Nkkn[Աk~l: u5xd1kD[%5!(uy}oUz'VB4,|AZ>ĝ%_&6{DԑS0P~OXhZ?a=^xATmjtQΌg"O93-|G{wm4}MCRXo֞nt/2b]>Gm[bCjrԁ& #HJ 518NN8cp2f…<Ͳ:^WFh-(Q$Ki ,R} v.!̶Gt\Q_R *pK\x$UdG`<dI (iYMPQ:0`^`-bEU 8x4FZviK8jo#[e-2c΍_)QKJQE& GïY\Y/vgpcATQa1ԁ8mД6Gᰁcr&=&9ZHe:T]a׆?6O 86DTSVrI V]3Y^Fbf]{T|./릿v0I톮a n_mqv(2V*΋]g;G|E}]pCT(HP-ֿu7e~Rbk2iKKb4zٯm=|2=CY+]. $~׀>}q L. ʚ'Wg 9⬓fZ`F >O]?u)T-ܖNk(OuTqHe 6ex?흰 P]D+%'CRN !<;L#F]6d"=|"5J}o3Zvoɯhѻ,(N 6\<Or+u;އEXw>7hΔ4&ƭ:*Z>RSi֘2@CN %Vz/R n"nMjgֲLa+ohi3t%7˖P)n*Y :Ќ2 fD3 +}Č*4?xk\f ~ɸȦ#]*] G-n9_ݵÔRĎ3B,br Qۛ|/m7 0ÿNX υw:5Z_ TW6"C;X*uPcST,ǞVjc(bW#HSX!L-+Jp{LhOI̵T9q$s|+ն@K_L) S}i sGF3:qF[;*}cmA?~ǖ)s&)8G}{p/E 8綜k XnFf/ ʕe=[]UD}uꏴ2ۗI'=.78׃(}Y+Ø隢OC7&vZPnqN̬y+X}cfխiZ/`Y._f võN(>> x DfJ mkӭ3CmR`܊_D.)z ]&JN.AXۧ1A'n_IisÃ肺Am'k[7Ž%h0̺A\?WW;.r58 "q| $kck,Շ惡c#;)P խGӹȍ?1 yMs7,qtQ3dE?Y졳t Q-U,\ݑB-(I@@ iNF޺ze<^"OGs/zt<#R8GI)O,|U:riN-e#4q8M>& a<#$VXB|F8eJK3W+>jf ТĿ H=7r0*"jsTAl)&wIvQs//4UE>U~ĻTS&d"i?RD1V[">es.(tR1oydSu0?ctESoXND=Y_{O]VtdUt&h5>&鋦 ȞeӞim^Uaj,՞FƓeEoHvH/׵7\i ,>NM԰;!(G=0uomyCAiFf\y2B49AA= 4KP"e,@FxΆ|I-a{ p,Ǥcb]\%z;)#z}ő °)嘤79~ݫg|o7:1:hӆCQCtN@Mare0ZBԂfC픔k=DZCPfoR0O\P/~y!9 U++L@/-NLW=Z/\W 3o҄Kv|êf_wfZG2W׿K_s+s)5²`r 6zQμD;gUKuPsvadөnVz|; f5Qe6acKdYO6">(t_ C1(}1˺uN-KJEaq#^CAYh>󞊱w,оOx$eÃMDQ@oUϩ}- r7|  ĘdeSA3"@wmmB3ݓK15bbJ'el0/c ~e&Y>0|c:*驼o겶Bgw(-_һBeTX?=N-?(ؐ`7ٲGde)L+g ;6+X|bL}9\P;JXf"TΌ7g6ʑ@T@CZjٯ0Ӻ}]jZaTQ$'HOH4"#̚St >߻뭥sda ǝ>EHq$GOR_A?{!MF 3[5.\*fZW_ݢPϣ43- }UQq2g ~i؍ТFb6 ,J C3n#ZYUY3T_"F|l*4Tl+`>H݆fZk /d (sb#|_!D{¨_IƁ?x3ؾvG;bi)D j,.#ǕȻlYIRi)u ysUX݃noxChPZF c)P@ٿN㽂ڠz-gNAr1f qoF//Om( J WWљr(>`ߞ_79AGϔMP1ժSl3Ls`%V+cS~W퉗>^qoqkThXQGŔ[k%R+TnWrHAN*NakSެLxA8Kbب.Y^c(Ae@ '\w]encgv(o;,OiTߟ}?أa%`Ę C P*H.ɩ"D]gV ^xW%yAw?Dӝ#"<~[6@s,n: O~Vnܾ޾&NKiRtiVG2"'PE?'PƘ匸*3e"RRY޾uK" ˂LHzLڸ9jFSg<ٮ@$c^L)c㰂7nKtoİ2bq(k˦| &/>_%$ `]rp2&rz 'S%p^eHjzp04Vukg'rEۄ%rճ;%^[$D؉:@e|*/{6v(:!ո5KuX1^ ?zvP'FfU,9<{p7Lإ3H|e1ypkvϨ.³|@'[!ܛS^!Ogj"@gSՏz," ЯH[Hme(avD!# wY?8;ǔ1<)NX|S2ny$ُkf i"8ۻ`7!OYAaZ]_kk2͢ M(&mnA>ȸ|W5u| q⎁c:Z5Wxo'rcS0(wcZ#/@܆*3PnwU~_&\hMA殺+hV2^E$Di_LgaO9r 7p$yǮ/s[fb f$M0B^0\$PY}~* 9l8vzz$׉%۹k9) 2&D-CI[Q`=t%F-j?&@z0n;N= U Wt.ֆ/ЯT͢G`>F#{#|0$~Uǘ֧JcO4x)Φ5p o$afVTq3\2'YJcUFlyztbGU/i@m]*Au2HJ(ΡP#uxA1 _ p:0^9ubCr4Mm'X_#D[UD -a~ u`X""_EŹD$r{~\M BB;t5y[-ԫ( V#F@!n/MRbyot?$$%]v{D=Cn+D!5'lx]ϋ^@M8``$1ZZR #1oJ s} p0" ?!:$tyτ \͓Z 2+?%D=eKaLB7i;GG*1h$}B!*u.,9 .!y5z 3hvHΚˁ,G QPPqc 2خo^|:c@. a0b<HT/չΝ͍pM@S\8To&h5^y@PA?n#?&[洪Hje1ho''HFG>Ѥ zB /VYAZ| Wپ'T?&̡>0ohv43o%dD` z?5ˍByA&6H.!57-(Fs? w'$#Rs}P6O \/^ѶBNյ6*$`!JE٦]',`合,BTka4uUC2Oc)䃒F5kآ>.zs 1Q֒Gn+#f[uшvTks:z =0&wDrxٱ(N#Xz$bA֟L<l͉.c\1U}M#wœ'`a&BÖ)s%אW'e\h3kX2Tx1#mCzZ ȗ^לHqu 6`_Uzn w5ň^jlzHw'y({-jkbx oo.JB.4w=čۂPC0OJZ^~tw>2B  gӨ'#i+Q1(3[8i#{ a d+N`F; rEkAb{ި$N3LR\NʠtЬwrO!0*ݫ-~D1G!~*,uOY[%U+z*kŠw.#IkAQcXjoj ѡ'.uBˑĘGZ39 3&c8 {5̵pqb 3'ʤ-:)K^ȝ)s;Mz߱ǵ1RtosބӚDD}LDǏ3@$o{9-80,M 88cGB(E~'IɎ~n0'i. 4HIy)A{fV[0 ?baor]gC厺%Jv1>Ef*rGyH4 Y&ዳgG̗1s!%O~(4Sߙ( | !5[fjJf {Sf;U+0_jq.B fi?W "w-ݵSCSV➢I/NO`G1נf5:bϿ]?Ȱ]|/pV.mSU<{4}_L) ŅeO{_w$%T]TZ&;o;ǻ wO lx`IC~^tw׮J G {F.B9rB܅]B1wٔ=嵅n5Ky-].LH[Xiϗo@kD$6)> Qr"K YK̰^(͏Ϲ͏ vK~(= N6;0֥Drbs}Jhޮr ?i]­!7y*Ɋj]o"&#&oA o[فC:"T)\dv͚6Uu,dw*Ĉ!'ޢKӁkvv>_ ,`ٽmEnB,ݖg^jjDS pb&sY?Kٗ雍bt;0 h?(@%\E꼠 %LA5Ζ,+G#9SdXg *CR9Qw@cѵ.6"YB'ތ9x#!Zޝ~| [A lp0FUm`:_?eftO0L]-h|T"NVOamtvaR8sbDn)ߏto ިjZ&9ډz=ɏ4|nAӀtOeuV+^o)l~fXˏ 6CJYu%<6L7>e8ݞX )tB)^jM`f-?&YSɽѧ{Gݱug I~XͨShOfE]%[cvQGT[sI:\Xʆ­#Zt(v\ΓOĆ"uEHIw'M֔ueSˊBuØRt/h5lP7“԰|Bus 2h G|M 1369h刏n&} zMqC P-S\Ֆ`fІ,Vҹ^7Mh$lQD0v; 2TXY ,q ey_2Qư1КY<9? ׽uWLD cYF@u$-3~N6պӓ,HL+TJG ']WTxgOf[_qh` bŅa?=MqSܗi< YT<ԽP<>nzр@mcO7b (Q+ mK|dnP@RgZ ;}ݢ&ޥ/$v ЦW.<Pq!4 -_u}V O8[jcaknֱEbħ.^ ~'@<^ȣS?eͳK:j'NDgdJ0ü)IF9#Ұ}taYie q2F'(Qa2gLW(%oDM~FüvD qPjz&v"-:DeJ BHKYy݊[a8y3UY#$gISf$EgövK7rbZw`Z<&N$>D;I|Cξ3W0ypMI]g$Ia4E#ȞV?YpbzgVD3_:j ,HQ5 ognچz*e4ՠ(R@,AzA}^'V`y?5*H;I{۲M#[ITGrSKS M³3+dqAM@>I{[y hfil6Auы yiBmC|Gl5j(}sd\ia{SE{jr3 hn-h6XsBr&ݶ A{`dWiڄϧ!3&*rTz@|h`w =q4̕U)U1,K;xO˱}\8EGkA(0Mzk/{"h0&uYx@JF)q *me\D2)Y-W![`wg#!Bs) :AtܧGFxX V3Xns2pOuF׭Xɵ-83 GY@M Zy~>OP c ]TQwL0- "L0@Vem6)qz'3ҧNC@!2㯃s`l-Sb] ÒFmqgNy&4H4"$>zCqqFv˩6rd?եs;7j}1agih~  >w%iehoC{Cɉ0%!D:2Tɣ"Z3{BxΈ()]mk dŇ_/Ax{;#ޫv C< !ncB[%!<鑟2Rۭ HEJ po5=t ]QTO%QM c*!6Դԝ}ƲLfz48_G 1ᝦo4:IXq2;9|v6أ< NTA$#TRT'JMװ9:.d(XL%@kGѠ'- ?ѫva*OO!$G&*$PD4x3\r{Jq'S-BBSy# rgI'iYts N4=b."I õ 5nPhm*p}hJlZ)SFZ/r k&oS'ËKcp/$ʫ*lKH}1gffwm9^Xv8ePP/EϬJW 7v|Jbce춵6izE7 +[XƘf>W0H"qvܵm8S>vx vhsNo+dj`nimwNr?61A/ |nv8G}b?hs-b4w0!'#ڝ;ӍYIxs)&AjAX\p-~!=ԋrwdXD}: b 2ꢰ%Bлh&e5>7[m|VNCCO61dOz_ȁp27t<:Ox,\0$jNg#|/"6"}u,"CX ߹Lj\-vCQV,z! r},QA.+W5oNQ1ȊYSRmZ83X$gxIO^wڏe`Mݭ݈. z;{yƍ`iP=)-^ϩy "p,$,($.s-1(v zzމq*?h x\eU(H|强Qqyx[V1!V`p@ݶ@L)>QZx*puAn~X7h>䎥3Y9jT9h#Lm=Ƀ58flol6 QH^ߓi,G*ק[?Fq\Pdԍ&2u(?:oF67_ljzu5 pCo8C&"u%]Pz[!Jo)O\ f"^ͬۮаqeRş|pJ͒6LWJ͡Cv%|#ȉ ٽ)F0Œ}j@C~nnV́g&BL^| z HdyDp:y{AJl1P.w" ,a(  O}X+E㘊|3o&ΉXk}D؆~dQa#sXXF(]NKȗZ`z{U^"PF(U!b#p$ *%tUG;ǛC_Ƚ[ue _G*bUD!o U41M3n>#ԯ'n*Gw͜L/J,;6X,Έhwgϩ@7,䠌@6&g\4\ $|V `[a<{z淾`wͣa+O Gx%󖿔-6k,#|Ѐ7kPƒd)BB*s MĈ=NngBOCkoEIjjs>LvD<䊌D05QN \\,pT8K|"m1mzMVǓַbRK RQF-FMtUPm@]lPiEݜL@}<]%sGi&.i8@#'4\귿I%Wĝ:CwIi}az ONbW4&Y'nrݒOʘf8/Glb]ڤ44T3."La"eo|G q|8DMb_mAmI2/zr5 tH2Էsc<,w3\Twv,*_X*2Q;WAF&LXy寥 VP%my\l\2pVe)Ivjf7KQ\+4<5yt48ǥZ%7d͉å&~ʓwt0;/S i~ KQZO*)Ioƶ:cсiN.uq# vO٭x/ȰdKaVFQ)7#u-~r_ Bxa>P@̏F:mFOEl͔N0\dF\rn 92 -R>XS2-wrR!P"/l _̫kU Ok<܂e^NiP>pJ2ZAb&0qVxYߠF5wJ$HDtP@<_ATͤfkY! IV~FxF^Yq]|&{oيxVcg Uu(3vbrM{~@to@%k~mݦ;pxVaӉ*oOXtlA̢yt:IMU}jb?>r,cR`d^Zu⇫=bz?Kr![иH\x@D~o2&%ᨙ=pJec@LW4`Oᒷ_Xa^T2U!ssyCӪx~ \axk)"[k%wYeITSļǸRj\,һ =pi<d;0-Z˩O&xƏ@::䇽GA6b>W1i!K^ѵYf+s;c"?!PʩlFũytŷ8omuNg-XrUvAX|)0d;-d$ N#vkI_$*frN  NJb_F8}L>K!t2J2vW"σ]IהUa&b@nJ5^pZ\J ]\ctmxFZCפbKY=YB7i2)-ASH]IK ,KDC8;.3ߕ4c(SQ-gd{Sh \l)EՖ|"bƁھL Jp_4m5Se}'_zP*Zm_C }j&P6NHvv2)wk[ǫz//9ewww?m,3EOb&()cJlԙHƆ5zwSq]&TOLo7bo Jcv2r{_ԋ/hĺ}_PjDj1WןIcc%'ݠ_R寻}M4h'6^w>6*DQua$*Pf'J<Ț:Jk=;`4]4vl|z LC`Cqx6`)b༟ ̉,,zsžOD.D0)wX,;n/O2aibUOV)?}'xZ?2Zi,lcLr| `Ѯ8סH\f4B2N QL+Z9  k}hu /F8_ߢ-{4c0~͘50e6);=Ba{2'4>`^hoDqgAf09[;b,doYkNթЪ^K)VoTw 3냞˧&!N|Qj %X$(؉`ê[ݻJ ]2J^dK.KƽM +Aʒ Vlg9QƈJeYB+%b5sLE_ɱk,k&!{M$yAO?.ϣ!kGpL3 )Nv$~^ Vviuڝ}<)@F|4c6vXobd@O߼Q}zA +x0|˾pޠmƹ><$O۷+BogwV(U?Vi[;L†R$]~"}q}.+v:caamQS 0nL䷠Ps~>'BBT1Ȱ}h6RN/-x,/Q]=̙faѣ) ZYIt7E~ nQ̚)oj2~wV C8eK ٟG}əD$M~N*Gta;<ߗI\/G3’V4fyp^Xb3|'Dn(6)T[esJ$ZbI\%Ͻczւ=0m 3tkxnl+ًۣ~&KN20[+gPy,af}J-#lH%QbR$Ϲa$moº՛pqeY^F%_(`DG//^qdǜT!Eaj1=7dF7/O/ -_nTz4썝uSn:T {yÃ^>Q)c^2*iB+g=r A__OM?{EYrDɊXVS"Di+"[$0I7RqNCx9NN_kg Ag"w*ZSH0rŚ#Ѱ,_-qn3VIzWqqPBlGx.g$ ά Z%r :7S-^R" H]WR8LO|:@讱^D~D!J~&jHLZm!#A"0{MX,5/:n>4ė#~E$d뷚.Sl!PPqypb}4VSх+lg!ڑ9ERoS fL0b/q:du P*\GalZY^y?W0 c(IIA[87c>ME($؁~ܝiNEg7]lGp_I#:=f .aK1/ a_%Bo#"Y|_;7_% K֒)ax!?Qxh-Հ0g2SjbRcg=7,O%ἄ3zpd_7"[ܬ((w#B&!< -Ct!g?up"I=} Seҽ3ׯiY;PqXWw]`LJw%F -Tl`~7Ej1O=ks&@A,WKaآ$.VP[8v5޽ho1W/@rwv+Ѳ + 4}r53^+z斉f#Y^2h-/.&C&_ ~ė%AZPz33/>n T4TuЄYfݿ& ||.NzUfΦibP-wN/kH@lq16ۡG妖jY5&:756E՝^mp.h\"$d?ϛ.ZsEU./yot'a!J́rL;7gy[Vi?ȮdnoGiL xFNʚlM):Chäs#a>; u^,@hl\UQwQ P; kBP-j}iemyP+mJ9g8iîi,ԭa:++O_dpbn&VaV"ո.ɘ8YAwp9\N`yY\(dh1:i4SG_n/1\Ma6돖Bm OUP[e'MX{ iAB'BM2ZqaVʥ+!vS ƴ}&? $BDZֈ+cxE {:E%yC߹:]y Q+h{kd5:n Z0 d_-`9< II(ſ)!GG&=4m? 5HWX9QMv,B.*q Äka]q54ti7$׈!4X[PGt9iAKqq&$X64`Hvl]}d)w6 5)F]W#)? z PQMYg^c_Y\54GV_!#mQ o6a[E.\' K"}ҟ!%^0+waÂ+b fP 0\5TAhW 9߱}T Vp&rgu(>$ mw63Q{Qͥ~6ď2>K2"v :U>va6w&4^S=QJBN/dTjboeeۑvijm7?;YT#z >c|JO.4 ֿK稅@%l )9UNZ~:IVB,uKǶO B23ڽWORg+x6%,ctL1b|Gv2]YG5hA9)n"Ҟ-a_ ֥(:}.mnGwq;jrѫXͅbpaCdP6TBVqk!+bp"K'!Sw;^xyV1tK25"yf^Hi=X c"\&A3OMGsv¹(.M9>Ԝm^4b" *ɤ6x"$_ҸvE- 3"8Z^iOSsHjw1j^-5ySVAL>ʪ0liqSθr[@F)cL%Jf~Ա! "(>?0Oh+n)?lU# eau46a,7Dvr|ñ% '$R[)X#O?Iuˀr&e{fodQYKegAr(M;Qt(^Fq뒍{?K+yF!7u%c,#v%{h}W Jbﻻ^#)ZOAYqmB2+6h87rx616(Iâ0r=7= ZAbbooe۞59#hX)-[MU~SA!ݭ٘V(ƃx둀I^4+2Gz6(-).ۏ{*8H`Gpdhq)aߥhZmյyQD/zAZOjߌs, Yy#n!w6J2)9)O_~+&hejf dhK.Fܰ*_7Ktgf6h^j@GWT;TS=Cluc8ebN5/zG> c"".@/xn֥C3k쬬gqNo*Fm 8$upD|7#l*=6&H{m@ }lT'-OPr6=ɱh x m7`̓/+P&fkb:,ʏ57z#a*H#o i͇~Ìĉb`L=g c,}k׷[pX)7ilOmRPuyI<z4KHe ߩ,=v~~wP 0_7")HTZbEx߆#5wޞ%/QMRoA)fgFO 4H/Zfmޅ-*kkkfn1G4ěYV심pmHZc.{QS6 ܝxGww_|9Fzr^uD!Pb>Piɺi2 )?E Eft=%4.&` t9ݥ\ )CT OS/TŔ[ (a.+RD&#]9 vW{`UA \Mҹ3ҿJdЅQRdw>J?A =0t[ qJCHxЃݫI;owڈp4d%|*nL*pKiQ  Js7砪xJ>[poKA\Ղ֎.oqT,̆iTp5~Х}/18v3 (=MG^c8$EiyC=u*0h?6ٞQ rv.FC=]`iF.M.ը?j+#[ ٭zE(cXDvR(\_c hT5M %vE4x4dOrND5m=0O;[Rp 'Ɏ]1WMyѡ֔RVKd|їbH0Pqyu'?O]!{Xʹ]珿.eϐ2EnؼZmQ>e-}#R*Z@꯽\VRd|kOVB|UgBCkyh؁ɀu$&0snTPoާgΚ٢oj5B[lWȢi+VUR45_k-mVPо4([U@q'Z 噸V,9^ͼ}+hH!Pp[N Q񥇡DbeQS2~%#)W?rϋ8 FV[p$n7 ukuJ\?c?KjՖ<~/!-12 b]H#?W#7ee*&fCvf7&\,=RҀqʏC ½1r.C]ݥ(Cܢx g*q}^qT)) IO M 9Ut8ISQ4z_x.1xhӒn0KY5|[0藊%űed48ZiםԏJ1ɵ߲# t8r1h0M6k \v9= K\PniUgF+mpRW|=r`Ub?Da6pi; FHɶևtv ħ߉IrC'4Ej2Cn!Kvk<ʺb%jj&P<هm@*@7?T EJtԱt9£9 ?-$\'~ǾQߨb$ UViIS"uhpҊbk 5̷+RgUG${sҺ {ia v}X2s+NLhB|O6CւamIhps @D-֯IsfdiS@Ɖ?~ȸ_>ȹ>2ܿ(C+G]no~IgZP݉DΤ3CxCM7g(F5 1@QP%;$vV`ەJ"AQ.I r]Ʀ*z ůF*ٻݴ}ojCٚu4+)y+;Ljb%t*o8~u%5m_ɘv_J٬gƋ2..ZARbV"㦝j4姴e^ pD.ָ&͆HY|?j :e•м jKe2Bmp-J~`l5h+~{V窵+lGDY[䄓iW.1H t5W ۷ *2yjSݪdՈUywTlpV2LOeN OÐ@ol8lI0E58=~i@@^uqkFjeI&Un_%ǦhfpY VWo`C痦 {٦mܐEl^ly~oYI;{$z~B \;s6Ir-m0| rۜ|O%|8#,ٿidҊ7}2Fd?\/-7dhPOI8^Nj_aH=\PnjF[5m˾CUI8qƲ}x]R3myP\r)u"Jľ5]Zz;%;W G5r`LF!/+6lGuyR̜+2s20–5:Q@R^E\?'jݏQŝ.}B Լ=&>_Ȍ=*Aqu69*Nsr[$:.IN/ɗ{!k܋P\ !x31 Ś}Cj{_Q.@#7,Ȭ<]B|;hrFޙ^xvP{>uYn};/KAT)_yKFK r,_ DQFRF`".UV`]5vB|JKh nWǠV7Aia*zT'/Zh{9㢄R7}ҡUlyp@. $5TZމC•dj y 똨:ؽ.t^oBwrC `FCa9_AZШQ2eь9 +8KZ3Z ꜞT'EL5B14ͣix%{X,8QIaɛe)H̑(v&cfF{qHq@q{R1b6Y:7!+֜@<GqP1q)[Ә'sÔءpxZH?t#)Q/E#mS75#ñ?+F$t7?dKgqqxN6pM津Rʘ(,5R}{R-uUW ?|$;Qv۬1Wh,;Bh:zE: ų<ײ`=  FG,3`XL2:B nW"kKiFfBۻDk%Ƞ[me^7]md04,Ǫ΍BHo]t{F Qhui|N⽿+'-蔶mKtP*lgkǮ{G?%ˌoeox%-iۂ&?4(~LVk~6UR?ʈկVxe(POM w ?hc d,PCB,Rؗ,m-[IղvN$ɚ4_Cj ȁy$M6upTF6=tc8am@Nc){,O oV@6Ng@u1I-Cjm$|tClG/x0D*3_P: Fv)¤ޫS̍WhO=]RbI 07l?SjƝK@|mrԭJxgVc0'Y i.FJEv g_J:&+{4f;Zio!&~f^!Õr_1t&k9fťFO?md$ VB$q|ed"]J;{`5j3~HCy4a? X:P( M۹ >ұl&\=vwN`Q /LDVa5 1ЇxB{ns=kOj-#h P8Rjwo9%UfyZDI\x[Z~^9ۥcĥ?B!m|xz!]a ? qTMsNύ9-}(M-u=:{5h]]K@E~XI@~yr==TvߙҖ0̮h?0xbAo20fm]eiyx!>:.."%Y~zce 'S` &l\aOY#9=`3VfbǸҦ+ wnsiZMx'.ۻb <^Xd05VqsŅ],:2wL@6 寖މ4G ODE0^؍=} ^^'QTf>*QE#Mt*%šԇҶ.Mr>VM8IuANmmVWT_q~N+!-ƌڻ= *CGљ/îT `H_b`Y8enO ^ :mkMњ'0 RJ+lM³ua0mliHD.vw>N"QWj '8!'*zd7Y-qJV\/f`"]5 E@^ 4\Bڟ-)2T2[ޛaߡ0b-(3hWƏh mrSbdF3UδD~ z5qM6 D$nuux3b7\cHm댩gm1ݏU4h>zlx!5Ww1i R\gPuqt,5Xc'5Z.R_y,X#ZEXF7>? 6l[eo  22F\V5K_IL; ^<ږ8~jf_^BU4ܦJwk jg&LdI墅uW\ o@G:5M,xGs<%g'vdp4G[ǫvb\L:wEl砇W҂4`.L]fAiWE쒓r\%)bf ׉&DޗAʢc j1c"FU)a2Vs.sPn (p~l 0qVf.%}z:7W vJZ4:cQVj_q V+Q>!߽+D; Eۯ;Z?rNE|.NQ!x.K]"9w }Xp,DdꥥЊXRTig]vTⴔmKp|W&—gU-hJ_(6re䟕a%/tdؾQ%+0n̋zAf=[a== )2bV٘U0 p#.sb f\xQ>`#J.R6?U&! 3!aa0TG{J0}+ı U% ڪ~BSnb8Maf٘m7T)g2/dR\mJkpmF@[J[rPxI(j-F~(1vo#/ \_׃sA/&zu(PZ@q~lfE;"30(]>uUh(e`,#źL"~cʙdp䮓c ʟbGV 灭sT@0ZJC_Ejホ۽ƯHSFȴw?wϜwAsZ! Nxp%KeHP-C5l @Y&tQ JˣqbI3dؼ~͈EZ;cT#>{qԄxu/i(.ca+6^j8@ \z>\%T)lfXI7WVc0x| K<%P=KI4iC~qt/kX* 8[pnYR#TRBb~x2Qɪ+΁bNr܂|l8%7 Zӕ(4Pu9@M$>x&ˡ2nυ.9# 0K 36 )~8@R#$5TCOBcԡKDŽȡ=vYϳ{dgpV}lɞAyoLCs s1V:р ؆'4 w{A$.P1养VqOhyxye\y%"j`$䇟U]ԩ@BOƀd=)EN`\a{P7;vQۣ,)0J0޽s#څ0gx) /ȲIEi6l%˝lb ^žVԓEv j1W[-<廉,ƾE/k&X,7 ~m,~LÍJa0$uיf qdUIl+]|ئ 3P#T6 vu,+7\u0XNp2CpwjjW+j^~cs(xF8p& e(y>7|LMVSIJ noEHcɏ +=RT-daBc"/=3%,C]͚^YDL#h I5Ԝ!0v՗7ijU*&DB mI.sabQueX ĹΨWs+y޴vb:DUN{ @I~Q?HVꮛ̶fV̌f$Ɂ8T9W5Gehe)^VyvE0Iwl-%+^S<ȨK|Kh ՌL2 60ӮASu| Ɉhj|@Vyu'Ƹ*4~#uiuI =:z6.wYSgzYhhE.N˿BCM%u"Յ2>9}ln`fIϰȌfyC~$dl$Z-t{Y;uderms Y4h0AiBa3)*MQhH*بJV戫"=:+?m)71ӵ=ُ90μ6t(FJ+q,)ʊxR!VxS+N鍬,N|r2nOƄ%*7&9KKXwn;XKtШ&.u4 Ia N SgǬl8i*)H6yq h?Fb ޟ&m?N~,Z%5.>Ē7K6h70[:CRy疅?XZQfƲ~*w_wJMSx}|K^ZxQUyM2..rinl:J-{5x0Vji ldsqw$k*.FOJwÃf}eCM#+(Sm?2  ; ՏUbfӒ6),DUFQOWCmWҸT9%Ds X{sQZz~Y-# f$5hH)eSZW+Y+TL &C^gPSwX 񈜓+1 J 0=J# a هd*‘7%rwv1Ӽw嗀" t[OTओӌ HKUji$vgRn㶠FB_/=/}`d~yʼ~m[JQ&#*P1rDI^GQ@m03-b}Po$Q7IP˗0 `:du4&߽џrRf^-_I͇ ~ZY"MwA[Ts$Hbڄ5 t􄕙$f T{K!gX#')1:DhBeFF^6".^'}qC'DBmwPiCL:1 hw@~vdK&?fw' % cB X;d:߇8IaThVM/?JxnWMY|.l%HO ?ima )xB "7q> &^Ib=Is?raV݀k;@[GJ.rV塶Cz0i7)?Zs"{isǴ7LW"->xq97k=S%F!rJSTPYɹ'J䣲;BݐRP >gz-h-hb1d |aKQO^^veodrĊlg9ܗcGXQl eŭi%\ɯz-v͘46 w-a@nR)%< .w+Q*9W=\mG1\YG!h (^bIgUP{-O_X^Vp\ߤ{>gtx{B׶Yp{*:Y# 臰"q𐰐` _io|"J5H/sk8BKˤ i])R/MǶP5, FΡ32 ɥ*`Ne%V\[uL6 hY@k@" Ȇ8Vڏm0 ES;LZwDώ+|r'_|>/k`{3߃i&Ɠx* ,nV>MA j(AL>^4S{bIQ^Z>aH hx #cOf7OzJ;X*mC](c-i,zVCn/S\_.يe9ĜFXl٪Oes\f\<[[TB9"8:Z!L/Zp%D9@ S/)>\Wqxs׏vlG`}˱ bf-Dab3x!-TKYFs;gMDvMj}K3s~o|n(E{ރ\#zI.SF083pLROxmڪx;G Du /fH ]sx@jvq>0S.x_h{:aɭ*:F Vc7^O02iwxv&'%3/{Ճ0zι;Ph2=_fびBm0\Q r51j Fld>8¶̾UHʖ&Q4:L'fΫߘ 6#]ʏ~ [V*!Ei<:|jZUJT4HؿoQf[ ^3K5<*}a.ݤJZb*ԔԙTɈEpmydDr}u D@ UaFI͞ՄdFR)a5Y<'i(5y e?a>mdDDiSsseZrCi`ިeuՈTDo/.Ф?@aIaV!Jfո0DJC#@݀紡 MTr3#WJ+w rFNeG /iĭt;nz4\!6rnxO.^Vף׌T{7[u BGm"!p*3{V[4j;[@v-#줛4 MW8 vpxzc*zni]-I4TCpUikzq;*ަnP˃m.-J XyJ*ƨb=c2w T[3'"Th`oj$F WʡYiNnaKIeЧ=Y;j@5ONȌ(G 2^@*cZu꾐-EMO=_wWK?^vI yr [իb(A,^cxvܡn.O_6Y់{ĵ[wLٙZrTҭ VT3ørā* ٔosԢ6'4w#(lORsun=Mr Ú"gMgȍfP%xe+ B6W|~c={+C[$>!awX0iO Bhp0Xgڔ,͹K/t/ `If㟍^ܙ2QD۝L P 7UpG.w|:v۴iPrIP5D5T1B]W[ z5%YB!- bI"/JbuD(D_jGl 2sv0!Y7|E6?`H82OZY ǩ%8ny\sCP?%)]8hHjם\FqenL]+j߁2/P3즼I i`nvnv#́RBΞdA!Ed'%i۟-%/tiH4~HТtA4qhh(K&'`eGh׶[iڛ~_Y{<(d%sto͛#~GfW{e'Z6B| o΄LC4PgHLXVI%hsr\+IC?}b.TGϼ1ђk(TJ\-=l u)Ho=in {fvq8j)&ҁdO*+Rż2_ށǂLVx'M3Ш?9x)  ю@9Tz'V\c(Oވf> "U" Z3+K3jJTc=l>\Y-Hq@=*_%*2vzdFئ)~~8cg$Ɨь8_jެ*m<ᔽ WźB hOFf1BdPX&uËFd^VQOʨz[Q%T(%>jM %v3 Kp+J_OFI\QѭmP.cfő ݙHԎlIJ'PoxUkHGA:\+ulkX{{5m%y>mXeŻ=S$f P ڣvBvU7#_ir 69d'N[ qUZVO2m^*d/F:B.njc.s2ɀnϡTgOm 6= $}gA}˕p"۟p 88[*i5u3VcF&k{v6\c;jTISN.D\B)fo 7v='H?J Am@sңKZtՆш82pkecgqH|r_HvbHy]%TFV[w\ng㿵{%|=0&[T'}ipIJ@Ƃ/F魤0LV?fvLQ6/dh?yO/J8ޭd :]sR3&˗cha.`}4h%N+?5$W Meom(@8Gơ}ՠm[Θ(a t|*",>Pvc 2/i:<`k^ ȡUwmˁ,p=H=KaL=}!-p[GWE \59"n=-gN/`Tǔ2tV|\sv"ܚw H+[iێaw6:8_Ӓ`è %wY &r C:ͤ%̙/l#e4;!Ut&@UpUˠ*rfǃ8]ۣ7'cth,Jͥ$(T#IS[a1jϢAS׮+@oZZoqmT$㛨+.li dҔpjY?̖[{`'N5=S/ᶇ~kLq4-3Ƨ['zIEf>;?KF l%?>YSu5P9ũ.zl"h;7=qE}`H]-'cD{+'’?tYѷJ+rDL0; 蹑f  Q"d ӑiJ7cL^ B6lyzg^!F*Ld-A[)4!Ov|E|2UQI2 0l f_}nbVao=C a&u#Xp}mN NWC/N o QDù]H;un,"d7Ū˿ i+eNsq'sqW V;]o '3(18&_\~2 _i6p09/5M`ٕiG(=Oo!=ȳec6 D_R΄" $}Dl~sŘSLK(r1>$(p{SNߊ~䑶.2S@)5OȲ)\*O ?4wHdBKؚo6=`C^ O"Dȶ͋ ۀ,% At\laLOu"LǙ$NsѹNǶtDqUZ`q^Tvj9D]PU4oކ0f` QQVPy[YQ!%'d]ľ$t[.\nGeKh78sR tdkdH~Sun9f*<:fzp ]Ń^F#sW[SCE,H@4-Zbi90}A#?݂RkI}u<(Oͧ,5;vxB)2sVc IJG"20_]+G\"|/.&?Wqm>R_;^WG=k5139%YB bcW ~=WN˃7Ђ/ATe"&}c>G^% ϭ@f9j@hgVDH 'GK-gEx$XKPIXJ V}!VMjjmƑ,J&[/﫶"_3SyRyyQȨx88f!fmsF yBaAZY{\OCg\gOQ2{)8|# ̈́ ٗQ[nF aCd}E毌N֝f}!=JWI8,LCpЎdHyx<ͯb6:LOrHEwgRNAp7ڶ*N-bm^g|'?"G! jsI1PK{E)iH4 )$MYd3.8xRnZKg7gL.QL#T ńLd6ߨVCH3Iug}Tag']HɈ%+3P EM J!N/`;F6_ChUūhIPTSY\i[?K-q"(v Ij)v.MfGef{ׄ_Iz҂yֿ4iIzurC/VExztbF]J}V8j,0mHS7?B+&ǐ|X)9ǦԔS3, Tנ_E lX 3{F K$V_%Kbc1*(9 ĉAb.>tly#d lymbO/u-T%[NL_HH⿪Ok\-5>i: ?#ܟnFeUɢ;bǁ7HFRwnR0}zj My7shdz5ld /?Zä)֘_1cJWH>ASo^Yi @U'̃0P֠q:h̏§f˃"a+qZ/5̠^w5lNUA*ߐ~]@k5f7m#5/NrU4 &:)uVǾ)0[;$})B/^e8 r`e"*]õAϳ9M%37$A"AxE@}lj%ceA(:aN'c:{~͎c3qr4Dމg),ܫHTE[tμ"Ws̕9UɡbܱHUB] ~+2>Ś3C _|1}D4kOp֛3y͕c$VPOۦ\.$ܜ/Ƒ >&gg|R }X[Uݾo1h(SK:*Dg W83c_@Zhg"w~!V 1 텲#(XFܽIna>}BM ͏-#0'=߼ƤRh>^^f"8~F&T^@ Meg=-zA}ހgi#1fpϾ7+U-Ee!UG W9o5Ur/+[6R)x)>ܥv512b,Rğܮp ;͋=̀lhj<A 'r뙫u0[+cA(|ȕ^=Nm~kPeS)EqՆrcGc%NBa&Y:IG汃I5fK)+ka8:?ͳQ7 *X"%\yo.Jvɫ3 /%|OLJeՖ80Jx¬0AHUf^uGb:_b<]ڮI%S1ZTB0F^Dک GxZg ETo vv.GD@:gbPjXMB#jL@ѹ<~P3>#վko8pTb\l@G A%u@UK0؇&b .6dÞZA*WM*Hk[Ơ@(8ױ,H&wXRK+}y_|Sغq/5v~imqPL-^GYb8tt\˳jtϫtsx\S7.U U<yk:npU`# ϭyF77s DkiWu;#'*c3SKϊg&gJ+=M/܇FG=7-DbJ}ϵ2aiѝ3%yCՕ+,AM};r֟k]S@/[cj"zSE!>Fjw+p{Ӄ .1Yc:EѕGOtv'/l:1l!_gƯHXiS+19a`eCULNیo0ZՍF1)KҔNuWsIcun:$]̟?КXK-<,XF @1BX6Y.^`a'uCFʯ]b. x::Gpn˱ꩽ˼%:,PzXT1ux<DhR$Gr BrtJpG3X fw;,HxHvY ;-2xIhO^TQeH/u>"E^o3B)>xn*I4Ϸȴ1%Jc˟>2 :rdHrԘdJO[jT]pvDW|ԊrmvdVn0Fc;TI,xW"W.ėpOA%tSW tn!ອy*즄ku:.fg ց?+7E({*~( X5DG܅e<)4/s>GWJM9nеh(;沮5#).*}$BI# Ē)v }Ebik )Hq!}geeAJk}/q0>-`S8E Jfik]--9ྎ+ne[* c R-oSK <j5Uxo~hb5G$Aq%'!YM"EKg34Y'=_۬xtAt*;?&H/WC t%kV;m eD:AՒ,4&`8] bVȶk8h%);eUZ?YC'?]a:Q7YW{2Qeiԅ>[oP#Q0cȦ>3jT=Av$WiިVҗ$j,u'FGE޺_756F}ۡ7?]&f/HT$$"EaZY$/G3%.(9);(5g9vj v7zD.)'{BUnWh֭GR!eL)@M'EgA|vz;7kT 7#q׋㊤/^q0}~FKCPET4Ԁ8T>,Rxj!P:04]*xN LUMM. #M2II^IhlEZI *DIuH禤Au84fMnd9>Vh_]sBq~#ٙ^n D2)PM[=2 $s nRabPNsDh!cIO^ >!/! 1ePˑJ9]:" k/ oB =!EW [@|p1+Rᘱ?,,9F_W5U3T5gރ.]`j4lفC=n9N證h:J -.by5zB[AIڕU`\CpܕCȆBv\G:zIzX&la~^zinÝUL4~K\b y] F"%] =F7":{ʙGKNͦ++$7bx? u5amS=+֦{ dnބG%}m |$ Wby|* یbν>EE eeuv٧6ƐPM12u+V,sC k楄?DBĽNnRDc= U+c©:Y#6,bMn e1ޘ-{"e } g†|q"鏮;t%}IKwZ,`EwB~ %5 .xqr4M<rJ?_gFi/q[cM˖E.MnTs8K/"o;i2b dв yNj`CJ LpfF@qbCp wل \텺2Ŧ2O̼awZ5SNeU&w*@`6D._ 0mNjCZПWT5f%F>w϶fМGҤQÑ*jLrpA$7u%Ա;Bgnް;$>J~Oe֐ C S(y`d'RG@ǣq \=LPEw4:U :NV:! -1r.;gA>u)_tw~ 'L~B3h_wAqYD$O)qWs $ z:f=z4<,FEHwj}SBܜRٖ7I!պKmf$_}*քyD[c'qa%ѤoCa-D+2O!88T}!,rFW%WQM/SĔo>8}GBn# 417,a%*Sj)gH)qw켫ptZ;̋?:Z8_V(8GЙBW")q=V5̛$-C]o+n=(bNa_1 Ԑ,HXc8sn =UVIy申In4}# j ~zN̿O6f [[ m/Dn(\f fO5Ѣ›eCOapuIA0̄lz- CNFKq#Ez}F-a'ʳxP\խKf+DZGq:L"BEG*tdd8Wv9zy{z|(RKdf7&?ZfVVl:Fn˃#,8A\mj1RR.p/v@?!}(|,rqU{@.J+ =.ImtMO01 }ΙBfU \02>h|QKK$m)>E46^ueCQ-K71x¾fdxTkaۜu| 嚔 kfQzX9oEr% jʀ$Wj&s.7U7x-k-"!gY @w(%"\ g8a(5&ZH\+;07>RXk[S_TNwrg/xۆt6Ѵw.8GSV >~km|Ypx'P}S//78bv}Ѫ1L[߭*:.wZM- <73[1.,=eELR$ϢŞPQ*@+^!/w#;Kz]&486J,~me{h0`%S`8Yk1K,쿪z.L{ YF|_X?˿H]y#4 2Vna"w kh qsh^myeOp?`5HZw.-k U'+&G ptBYt$=?l(#BvS {noG dM:ݶE9e E@DF`Nv3SwIgJw\w5lY+@4G(hrW莶~ᆿ$]67 0J8 1 2dT4z시 Q'\EϳhSkH-n,ozY' zrwFHcs;ŵ15iUMW\D,Yن*;%b>pa'XӱYx^ؽ()#bˣrN:cіtτ k< ǣu#kMý=|Vѡ^ r='Va`KV1 o#=gޘj4?d~ş7]]|me^$o]^U $ Ռ1(]T1x ();8ei]ɾŷRPД<_gU Wn.+Zk")P.SᑱP# 1H qbk=Is)cLNzoMs)# eʥו-$湗|7bM_=>gҷݻ[<Q9+Z=[@``y0̫B@nwTzHWC fIͲKOoVp:dʛtMf;GT:{xRIǮDؼMEw%a_܇ω5 #OJӖ?[mbCqxb`b|#ڬ$Na%|AGMQNlٌy4~[sCj ̓nn])zDJah"@n$`R$Yr-~zy0nJ͠nC.o7|h7֐z4:: ~)F=…c}#SN˽y;>iZpfn6}P JN3!GdZ)r+M_* {K3-QF=~ `3,p)&<.h?wtT]үf*h)MH^-DP4UAO\=!x /ӶK#7!>M@X` Ux_x8 ,{6 +b %>H!C~nh-r8UINJ75m2E4r([( @k.{ޑ'bi/0bhW_:d-Щ7d[P׏B+YʞF iBym[!:IJPgjN!G9s"ŎFn8VnHKAu/}/0tr54~}b2e16$y{l)() Rʤ,U3& ۖW^5 ψW]s/{fbXW&bu 8[ *4cL(H׏ 5M"N}7芨W+eI7z7Na~fX~GOoЯ\ݏt*ʁS*P53v5K5TE)]',-,moA?ZNlH&CjG*Uz'۳!ң@?iY-Itæ@Sn9 3\_Uh[ޥ`x M&$ʄHYeW~٦tT`Lv LujB&vƝ}xO4?*vdR04=j5g+wп}|oMA ԧTKop12=%"H2 l>uHȑX*^t7W5 \DZq+I(tw%G遀lENm?K004Kl{ OEL l{i;m0̯Mn.Kko40HCh9cR<K{-j/cZZԈzLUuo2Y:VqnЩ:82, 9R'~J]X;V1AjZ1/я`b'w:o /=օr0dП+,:땍s9Ėcƀ =Qs"ѸL[/#3,KMhմ_Z(<1%y[9KY5d=.1'5RIBt4<Γ}{ a%;8ξ͸MZF+F3hbE}[}Se >y:UiS1$'(['k9 tjv ؿ}*:2:-n' y TEü"F;O /[߁< y'8Vc T"C?qdS0y| koEge(%f`f@/YSSOe(V4p ($ ,?cuїrꆍi'8rԐj3ˡՖ)5#wĒgI=qOP?0#OA?X25s3*פ mM OH뫶/mfSJ;l\ӻLܺǼ;+UB$A^75ť l,,l~GS*g]R<*sa,=^JZYՌU1F"՚8?>. xKeg5ڪV Z|d+s5DWg\1M9.Q^z, gT G]IuD?MadMIwL|HD/ 6948;ŐA>RA-簎H9P]aPj\eZ{ImS}}pԧjY[ ‚5#2@:Y `6,b(ş\Ԍe9ybzo#"6v~jlKu"XU>hKኮ^EfZ?d!r[ivWE_ߖ\k疬ҫe%+.1Cc}eSn\R|uFr©qwa2A.hUz.G˳.9"If8`$Յ#+= 4V4T ͥ)YJRDlc5_ms$ $c }*6Q:Mb$ H]}#덻#4bjPE'i^9/IrAb EΓh66TcE*b_ P[pY}Un:V0C;w.P|Ioz5A,5zv>a .)Mm{.ٍ鴶BQzuk`5gf Xrpxjїӕ`Ll ;-M\xL-eDX̥2~ .֡^k |;k%S# =MH5\?0 b%RÝMaNV%3ӡ'p] K&{z::vwgNnq ."Enzl}5nX N*Qjpc3IvaG6k*!bCJOn[s͏jvGF*셣yw\PXV: &0i ǑBA^[-גc9OA)a>(oR4G,q(%/߭  FD^=nG2u;7wxGRJHCu <;%îE, wRxy[lU~j  [Э uRN>ޏhi"v^# ՙ:@`:TRz*e-S1/2R!~'fR& xaU2nx]2un3:x`WM˕~"6_[=)ip+ës{Ej(qМ`xf*Dl*^66sbGFB^ eJ|6zweߴ菹I3(MC4,F{{D@[t\ELvE>=8]Ha^ORau/r4DՄ^oKaYW&H\g|'p+]f{Y |/>?8vڶX1暆##Q{!&H9v.(cs ꍐ͇_A cr VdL mW8{Tc[|MiqʼL^WOp+[HҚb9|d_~d&¯_)s ¶ -r^:dZ΂ŰՓrJW3iG38¡:z1N޷'uq= 48n/q9鄗s~.44+_ی!{@s腓7P=1Cѭfv3VYl0HjA4䆟rqOi{)mZ<`퇅aɳG-6npS{}Hu G.`&nN.>ȧq+BB>g_;K%1r3٪Mx ?g!av:{g= p5X 8vKԶ)nDK[= 6ۋ,n="c$[Eˮ*T b!xY)tS_2$9&Wl Yv Ε+]pMZ,w /#B˚Q%uS_:`-ZViJ`gQw s nnuy!G}~%N%` ߚN".J^Dx: i%%6h*be@Qp Rh-$u`^[2J"މO9`ꌁE_NZDy2NmYڢ{]mfO9sP Ԣl Vlæj}{> i= %؛!yM<{t4B^ÿuR*d}k>g/>IZyJ-+FU= 5175YjVe_Sb:<G43;)vn*94:h7c8!j`(|JHO0fr"PhMߕ"܁yUNGs{ݸ CY" Q8Nw~Ձ]8FIskMY诅 7ULs⋄3PCWvSAT\}Zt~6f83)j oD܎;g:B#Eހ5v; csO_>5.B0 F;[r wo\ aA3E< uC&Pv,~1lyc/L=\(;aJ$b(`oիJd$8r8놉 p7n;w?mAҞ o#a `͗R]39?;^+py0#[؁*IBb~z!p٨1pDO榥f\b0Y>ggɬ=uKw'$f62jl'K<Ҡީ?.T hgF_Rc<+Vt|i |HX"{,|!vYÛ4jNj;K"*KӅ~㾲`?͟yf(F7(?n/B+G+ et(AEdRF~5D>%O7wmbn({ l|4Zy eݦn1=C%ۨ{t N?4XJZm2(Ji6eD48J,QUWǝC:B 4y.Q5t S`49I1'"Gu/7(jnŤ 'YΕAÐ*='?zF{R2( "5u%qC[yfu'U$C[^;nYRe}D-ȦAʪ) |쉬eJQDUb]qCJ{RRm4oOL:$Ӫ,A$EpI;lu?pQ\RYy1 Dbv<#|`b-MmG^D2&:]qep<0AI6cm*l\׽-[82;{?Ð]IK0YNȸbVw/”.poQ벵9R#x%lT$+C: DKjngٴBg JM^.&6vwZc|:n0Zۍ͉6vJ@2۟  5B{խGH "oQ?A1c/m=B^\P7"/vŰ g˩ ^p)2Zve nMd heNM AکiKinZ7:SLf*sj4#W{0f9KF~*&|˓`4i\w8Y !Qw`lt*>͝oZ96LofC3زu ,.`Cr} q:r*dj7vH(eZ yFйNϙ"[3 TƑ~TgkO6/嶘7@LUP@4LcR ̪ 8a ʷK~!Aǡ];-3װpFm$E (ZE Up=i9:ƎL"2R#ePl=;#ϴ r22hn5S[mZ|ϭf ZYsg#X`-52U)riuM@= C`V/v9wa< &uN@{(y.z\@+ӧ4yQAdP>FzPݠU Z:)ѽxChAr-^M>{/V%[b[{kȸ 2vO~dvyڵIݦ`B^_N⥱j."ɚ#Ǩ^ljg(>z%:ֈMaBwX*Lt#Ŋ5aވ==,~w~Fnsœg|EWBWGx/#}[!ڶ57'D [ w)0躮!^(u䳯bP++M6AB^6Lw;]׼_L'SR*9!DcJBM29Y@)+ o#e|_'7VCC J~CgzBT.zk}N=\*'*xU b ({ǚ`3~ @@rl^2YF#g`h!{EmF&#"/cFJ?4B2XY/8=ΆG=]!h|K޺sB?J\}uYP6r3D.YY5NMۨu3&RN-~0cJcCiyK]Ǥ=Lgt}8'=5R77hZ N9rdvA8)?Ǔ]t{b!A /SL^$c\H"w8 m1cFj[9ey7"3EL`&Β&jf/p.XhE]f`}K %Ƞj&KvX;x02w[8*#V[^Ƭ:$tu0>ϐ i T ցYŐI.s:[pZOsBYIL7ykW-uYx.1jt5t썸܃/`C|4N,qu⦴Ml{k@_Ki&arW aRзe'c ֮ϰ _f&uf,F؇Fj+(UQʘn JSǭQ3#sHaN!SmK_13|ժqM?XIHfᆤu8!toDF N/D>6]` Ʒl72C0~cVf{SG#ZR<􋪀vi'A&,Qy0'"Yغ9SQ6AKU,cʣLLIv|եUu<\ASk篬92[֞Բ-ʊyE Ⱦ@ 0DmdP\HL&c,0G~kMf0wV0>SH[jnܔoY36RD"S쟈| ⒭no7 aM9GeR~ȗQ~Mz ֈ^l1Q2_[(" ƕB]ŌKg̙+ཤI_˻q|E^7> i_oΏ)s?Egy?X^sb!rE9$~, ,GO14+y2LRm0N+=X)mEGu&a; K=`2Lwc; P~:vj$ef,qh;rx r8-#fFj9fauL\XI" <>h)XqG'}%NEdhYH0nc=IeUꪽ>pX'>]y͹)z!}iLY_C<&Gsn -4F)(?&HɕY J|} qcQapQ#DvyWuU UҬ]b5? v`eZ݉ eIbݕS0qHTXe(jχjm@63'!I`yJ)m`"(ʪ')xk9kZ*cJ1JNA@^B3=}@ 9|K rE!AGp._b6SOSKRYò.lGݧQ{%\"iG!$d&@يhH!T=-/يڪRN>cqQA Ɠ% +:^RhɁ-(ϟлI2[I} ⲛ7!ql>2$FG1nf m}]s$u!+?S]xź̚$j8Ԅg&E)tyҙ=) $`CFmG3{o?Rx"3,,Ӎ͐ Md>qTSۇWN Cm)5XrIr;Nj^V P}'^ xaOqK]@`u/Gaq[e1B_Lm F~ilqF1NJ߸L|&CѩJ)A,5mEϘ@9+ xdVs[yރ3ҕC[54sBx]AsYDFTxZvbl:@]5H7Hah V8als]אp"7v,ߗXmDbx(CɿG@N(f#%D\a 1A%s՞[p͐[,Q!FpEs Edm#Et>c1%%FG* 'zH=o[thYaײ얀-$*3ޒIl}S4Q&œڔy5c0;ҹ ȥwѓ Nа20Nу}cLI6tv'gA%ϖe)Bl0ǒxcd EK5%g^W91!Y^s}֬1m8x!J:l5XעhdYeM#&܂6>A,E*R"u2!@j4E?U=!sB7M-]%gxdDOgp 7Do!e36}'>olHdSUfdܖ:aA>T^w"T`G=zFmlHPn@\[W{CJve-{e}|Qie%eJYdPɚh3φ# Jׇec ѤXnxKMg־& pr PaD'oMywC ~KZp'vf(V_ELPoDS»״CA)fp@_<0䥚{/LQpW\ ~U˫⃤8|uP^ByEuQE"Vj(CCM?NeYçDA WGcq{0BgQzar[ijmC0%qb6-blе(g]=~gf(b Eı]6hanѬιե#Jr&^6\9Ln]T(1RCLi6r6t@PdG^I!zbb0U"K)5 X#uRIM(ۡȘ6Wn )V-JͲS2(M*Ĺ{#9XYքx2i]˶lYh\5Y#N7h"'=Vhss[=x-7)Tw:xƣ3 ;B>qd%Lu~GV\sk.NĠw]s}{H fuc1i}j[vi2u'Nr)|xo͇ uV8i0ҨHLʼnYߣD{@|o'I:iJaO9wpPW|[NbYuWSu2{j-oIse uXpIRs)ȧ7o;ORma==37v{p-i憅^s/< HHcO H`@GAg_V-P JN>ύ8;e~v[ԭjN 渝,4OWt.ұ bPJ7n97;ZPcCZʃzӘf)M.mC[~ /7-#ٽG*gU-B؂ǿM*{a6(ԏ~M]WL?msnWb#dVp*/)W^Ls3h{1MX`ky 更' lympx>1e D'Q@S<-|GN3wɝ O&zO(S;.Bq_Xf%k{ee }&@'KJC{ |>`Ihb觙k`hsn~0V{P 51w?lS )zh1\l|qWq{$yu_Qy܅-㊜ͷ̍Ż`Y5i}º_ :=3) #'Y̘SdP|@ 7q>AhCI%T P\{|QK|n)`u4>Lgm9`8\wH c'lh TmHl1-#')&Xkșzg=:T2_\bJogW66פJDIFky ^2V?"ԭ 0Nf_J*[1Oa3i!~0ᵋPjNiH%vhg%7@(8&_ʹ5 ,ō٢ro/Jf6ӎ7KfZf؉/=Z. ze{&UpvШaKe9NTOJDzBW~RRܛ\TYLsFC"LA^Ga# C:0Dz>|!:yc#ݤɓ)Iܞ?W1 :6+PW>`%ߘڈ rV/XP1wn9ZIxX}/F,|L _ѩhfTTp2Y4!S Jk90^" sae#JoJ%c,oq{h9}NĂ25|{x7^DmftC v>UT"3C4mV}!UP DJ%PU?֟f_4Yn z)'ת Ȱ_rragip,Z3./=h97G2}L+3]7{RhWɻlvI`U15b-6 gฮJJDRB6K+$؅=6+PG||4DV)?"}xF^UxMSX G.٫Yw\NjmgB>ѧr&E.Nn2:Ћ`F>[ђ<ƈ_!3l}FMS9pD^zmݲXĽgc6Yf-ƍUz*Ж|]ՙU 7uD\aN,kgeèVSM;'ݙBj2u/HW~Ȋ1G$+{B6 LnbS4X?&T0jY1ϼp5 @}HAmz~sf*$.W݁چɃG: zCKh60BDtqa{ׁvR oA[jϜºG;-g;o-pz%CJEu.M"lN18 ۺ7Pٙb-P1(hy,MAss#לGm[:0R9/[gM~i3.'V1*}n_(mAhDGs:KD3"骪˰\XΜƘ qJ MgYZPK!snd-hwdep.ko.xznu[7zXZִF!t/vF]?Eh=ڜ.+φ sU1@WFf^0d r[H]3Rmrs'V}) =6Z\9#v?EB/"G`*{XE :1l#` wWeÌ\sxc \/Db+hMC_2/"6 }&썒"%ڹ̗%SF΢2,A +tscmPe3*ǥvj)Dl".40_B)# ilfU0 AR2>WM~h Ϯz^T=IQ73Le8!%tEfDP;_m W$ ȵA#{u"Cj8Jac%Ÿ5ۤ2.SXkqW Dqhoyӻow$$a }<֐?a HU<aGV.;j^]~ۨ6] byxLН_ SΔxG59htt.HM`bu<2go3Q"dç8zCz-//`) ,H_ 'ёaҢŪq@uO2!1ֈ g d2`$*nftAڮKKwh' #8GhԦoS+-)k_ӣVUȂ~R)m(݀b\u -IH x1P3b5ճr"DE( ?\qK ݡ>H͊DPϝu&94..7vb o)PZLltD0)Hsf  ؜ j!PF- .ޕ{1+ZE9*b]/ǘ/O2IIUB!%ҰYd&W4NJb(*89SNpz"$JuZZkM:pmSQQ8;ƽMܾ2FP:U_qxy1ذ3E'΁T읨W'ZPydKpXpXpW j }1n!9'i5]D N'5I+^NvC"wY`ͅ% YjP6SQZ.#HAz~#M:vk}{F+GhKpC%/O yYd[i.jC3@ NTj:]AnD"HYW<i5ԜkXB % ־r!˯(K*]]KD…(XlRbD1趦RvK ~1&$N~=!"-QkĄ bUm횭0Ԃpxcτ_bG>чƒb:qȚ"ӃV<Xc ^̿⒗ W7̄b0.`m[T03[|Xs!dpcc dN%Cl4hx)Nj(p KF_%wǖyT\NWUz1N_ ӻ8ErMpQ;*GBUZSzzZnFScwȑɈ|,zJzgH/A  O:'uz{@u7NuyԶKaMM]&.VټCάLB} OmUIpL ʮD04x c֜|"I)q4YW10*a9{M#wݯaK Ӂ(u/:ef7O@} #֚":0%TMo51&LWhV yt u_^wd ~ WBqņ&PMba?WP* (Cq`ٕaPꮹ9:?a%P,{k8.)Йþˀ-뮓{=GԷ2ᨱ1qQNf.çjIBN&-2u$1*sC W.˴POf2.75X߸-bvb ƤxMޢ%MQ'(Ke6/)~e6]m{%ñRMeSmShUaV@'&*b~5DK ZLrRqҏK&p`s(RIX dvѮ6Ss (` !>|WΑra3fXs&8WɭVSCΜLzɊ1ٙ 5ؗyxTT{IKRe|Z1gX`Aggfk41-n1z C0wZ(ƪ0(&6i3]F(џs6=:^Ii@2s#†'cm*f#FѶjbc%{lq #omO*zneuCD԰oA//tcKXcxrkfDџ}Wѱ.KhTy] &&ηlhX {xadZ@1o bY9ܜvðą7OVW}ꚑ&a2[2 b ʗ]01\T5FTC3h9Sc׈-ڹ8 k"Y­7~nϲ{=EUv3lA^'*+]yKhFs~YR^~Si$ L+Wy~Fc}$J:lH- =S#$ޜY/!]3522Ci3WÊ>Ī7Ie]9,֧A~]#+uߚ?"+_oC#D C v:\;9ez[ѓma]ۼwQ) &m7ы0V>ZU`NHZ\]ʩf!)>0Dȭ9hL'|$3Š ?S p+b%*vTBqس6XJ_Ğ `Sq>XT@`3dI"qrf:ΉyRmFRǜNԂb݃sL2Z [kʳNpÓ>-ԝ,3*wZyCĨJoD 0v>r]MG.5\K%H'J3@'%ѣ,ApvA=J%qX)ޢ'|5tilC 1(c`o! D1#;,9}3)R.CXq;./nҐ'd2`&F3 ~[ӽ2a˂jcJ!+ر${dr7ާIX.q1sjƢꜛ؇o kh0?m6= ڔԓO7X8ypY9٢wً]~dD/xI."eXe);U @Z*sZzLp0G(t&[tQͅf>ؑqŁP Qǹ*TrP&œ}w%=lW6"Eހ:qL]1kI"WVLWCt )@Y."WS1ݏ?ekvgz;0bWRAܔ;],NLZ͵d7;Ó}hWaY<T, 7S}yԢ) }[`1dgW.P_c Bc:CmZ5@Pzv܎"G/w'>_"JoOi9v?1 &01)^hNYumI]l0 #cM%~J}*?>}!(= lq36Hx W8|PD4SEZNtl ej.~PYH5ڤt: R:B,\.2 %a^F(NQLBd&cN^>_FYÈӌ;Cpsuʕ掜65+s%v2`XRPGFo)bT\1PWn]$@TM<)3gϛH<&[?}m'۰`D@D m ַR&gbu2rgYZPK!w(,(,mspro_block.ko.xznu[7zXZִF!t/G+]?Eh=ڜ.+>l=Viŀ7)6[?;&[LeS=U;FeQߧOiDe\l =a7\Tp{mUTFeӚ3(s'B!b<$O]:RM0^.mζf?*?{0*X 2\2Skߢqʥ}u.栦vƦe<2yx9rW]ag ]0(3Vt f$8y;7\5?fHI5 7b eڝkp(5x-Oх|&g82¸ջҕ)2nL6ii⩜drS m^ !lQ^SǠ\'l xuzj}f5֍H}Ʈ0ސEY ӗ*fG犩쇦L%clKSdy_~dDN3['A NZ|q$\s T@eR40q'+O-r[doQ{ŵHUV30r{|T.8ۗzr;a^w# ?@7Iaݟ@{APCJ4 n˪S=[.IC\vT->Vʶ=3.8vIjUpUeL‰y_Blxtm71O*+ hG߮\[:Ԛ$Pu)LrF $ LJ@"+ <]2q(,1N NV30ng\j7}Hycv[9Sd ߲]tg"kKлwDzadO2P#gHha鵂?e-7Z2C0z&cr.*/}48^m+Yz@ jOwĎ1IӟkA)Rlb2>?Pvc6vg :b8l.dٳkRnNVE+Î6=5A .b+gA,.K U%|؛хR[!Kt&\:hz<AV)@G,Շer0l/gOs%z׆e5?vfh7˱ztg`3am(,3^]m6P!pGi(qA @Oo)F6fH/^#W=* {e&U$gXSKUnjCռ69Y\h çJI!$xc/й$_=ϣ(4{h̽hkxXLL_Z^}Fn-;-5D*|crCbUȧM>JWXǒ=8o?GbsP;{_Ib;\MռÐe87 \5Z`[LzZ7cس=k<4)Vczt8tB/RNy♖` ̻jpgt`},y6Ula*EH o9\Zi4J|f%:$]hнjlS:v e:x #бkb֠~o֭]rmqV0w[v99 zμؽ\sfn\]iL,4!|Xwuq+CUB:jxVeUS ˆV,=w[bvf (PL vYz5%O Hn!WDž ͮHn(Tu lJ̾`Cj„d";:5$M:1i.W%H՟x [}?ppsďk,{Q۫uO<& aȷ$ u04qJs(lq n.xx%2C uD~!ԫ TtBh3Ka<9 Yl#jՁJ\쁟LkdJ.1"C8 c%$/!x%34!nNXM5Uw=x&V{z)3s 1j p횋+( m o`:NLPo`Kld"D&?g*\X޼qMf_n!OV Cme_E*lퟲIһe*Sj* ]j<)E*)cخ*i[=%YA)= ٸmj)" ANsXc': 0kV`L%STˣ$%NT}$/2Kle# 1KQ S|Kpwc{c]շg8,EwZ4c5_=i&l!@>s`4D¡,!̎ ÚbvbLo Lq^FĜ2FjJd KۮYM/ X$=0R Q+"fuX++lϡM Np!a#/4۠Mxaz-cm[UDA(BjJd_nz6=rFY E2bJ7 *.:vV-ai7*d=0(>-GʳfYO,Xa/Drךk ~wdly'`k0xDK&NR=r4G{N| 7Ut1=Lyd܅FJkfcU =+_J0/}y6Ib / tt;k.'LG#[pX{ F%ZOw9N={rEO=4k&e]mGfJ2j4>AَYr7@3(U/hz&Y 9t5˾Mq̊e|?[CQpBd>obB=nRkAMyqN~)(%$@ėdm1(: Y1z|!Xk&LD-( GR"P fEWlXgNw}BƊ`}YM?".JztrϛQګsik^@ۏ{>q5ߧ'V\qvd&V<7ޝ'W>7'l>Օqcu\ͳ"q=@P$oT)%Jr#{0儭~ ct x];KBִL;Z/dLaoz1 =%OՈuO46E؅d?Y}g(CAA1Wf +Ϡ7֋c_E.I`q )9G-"a=?\n|93e `:9pM 8׹KvANMRFA;^. 39LAw]rŻ"z_\ L)IqR*,7ԩӵyJ& W{Է;#ⱗlwg/(t$3_?$@8 c&Q<([7?""D%/+%Z!-z kJ1w5vt=4֥6bU pxx읺m9drb5L,AoUɣc,(^dPMRav5E&\Zi33?Hm8]f 0쉜Z3B]-vSyh~@#d9P]g}h>t'MFF09քվ4986DmxHԫ"eO+ӭ8ĤAxέƊ>Iw-Ṿ@+ǛP)LLߩZx/@Ȩv8MHެE7](s'3cm*yq_.hѷO\;*d$IU=ޖcKx =iGh`bhvڏ&-azRxO8hXFf?y*_(~bn1u3u9nfѢX ~EZ72P8Y|{M&|Je*~R=y>jZEyHOeađ-~|9fab $qsAk}ŷ󚌳JV,h[ #D$ce?@vFJnyC0Atfy}5ֲk T^;HU-@d|#;h># 3gaYKgv 5'| =Q(-<S #wB T>t򫌏YVKxBgX' Q DÈf0(2vexZVu8FɰIm!JL)I`Mu0712+u~;"hXf_8o2S^MSe:(_Wmnv5.M4µC d)ޢbSaEdoD &?4Yo&?n8tw3oXާL{MoH׻JĹaѸ&(YQ)袰􈫤T%ԋv{7`r lzIw6GŻ twq[4[uHvـ2voĨ''g`]qA0lg9[;~b7ręrh{I j#, EOBb[פ []Dd aƵ⛤; cΩɶߩsOTvaBSr]~J&7Ȓixm{e?7LڼS|j\q+tU ]^a5ѡ;LKlߕ sC~^8wVKL?3QGf<[ܼ#Z1*"C;f.I `_Oy%c>2{%+̉DvdŢL ۦ"%#uZN 5uLI1^ s mva81ܗJ3~wC4 3k5fWSߚLjrjp|=I;JRN)y59<1l7fOsΞv_Ac%YbUON{ c7YK=T IQz3 q}*F <-۲+@@Òj *AI.hlꌢ$[\-n22bCDWusDmV?}cLMdO\16ƶ#]HsrŪ (hjm@}20J45cEK#gʊN)q5M答WF3ձ_=lQ%{k;@e{m 0ƘTi;`Ţ8Z,[D8퇱4djݼ–VrƵ7LMM gD5@/0pnC=$%7˸ŝ .k CHB|̦|r hxM: rE @[*,_q#XӋXJ&u< f{`J٬ 9͐g J[;7 #?r^ ˵«*bV1o$1sw OՍsyZ?עoLoMf O21sr/V:ų .EJ?Gt$WOQuџj6gGE M7^5p}sY!$XJ;G2jT(8#c-uJ?3-m{},P}nj"IMsaPjJz8ޏ}zS J\﷒Y\$q[ ȿ|sg<}'-")D͙͜q$ KĞ^m##Q;)jǀv,N6=sVC6M c8̭- sX2Y!+3΄%U Tg1ɂ+t;&& 3]vshF3eYn)IܑLyS0spџg+ Wimjc10Vx]"s[dGIzJ°74yӯg4I;yuQcXo^ֻet#rU$8Qn*E4^ט惨9zj#j#H,|qW_8ඒ+90(7/b$~ G'A">R˚f! _ kCH'%8V^# I 5ˤ(Yj^thwj'>/]u/* _Hrg7@*D\P%=7M2;ofd4ν~ abF Ɛ,AgU7L{xKTڻ3O60wڈ"ܕ4$}oVѢٴ E~%y.Bak#?r>?&8ꖔoJqTz65s>bTWƂRbxM6XfN'XOy|n=d {mwṵStY{\_ӞK~Ifu˓ FnO-k]pv򌃏ͤjO;$ՆIW'nnghY+X~̀/g0H6V:Cl2wg{Uv֮K{QˏWh 5H䢡REcZKK#XB`Z{"qKKvUG?]\_|q,7tI $DmRG?h8ⓥu ^^S t\zuuX:TL/EFLS EQHL>"6Ўo}j蜦 O%دɒ01E҈MQe0F$%2z^#I.U9r l,#/ y6ϰuScDQ۩st!rݯeԹzB"P@ ''reEzj4rXAQ>#$]!V:%x[6A@s-g0}Eؚj\;@>A}\#Ln>.5E}G$A#K$/ND=y Ͻ3M`=3<ۀ$W Nk1#4{toG|TK !_ʄ  3RZ^pkY$uI7-Q"4ަ wz_únV8|82m㗍?o#dQuCWgwe)BTY~uy? ~崌zs)!KSvDp~oiG ] \%l:eg9mq?g9R` okx+2?|4At;eר8]#crKZu2^ܣ_ ☱ 9z>,bLHvA{6omœ sZ?uJ`I:gɏEO}sC 1{Q=S sR*:*g Q%o6$oDiK(z3B(bxEv6G3ݫr,ܽ^e^"c-=,r,qW=1sH:x `yQzJSU$]Zc#μ垦~Z!c1xT`׆j/[Y| RyOЈk_}*n; ^:vf6莧l?wl% PbhV%;lC*&.|}s'$?ɇ|c`\ 6*)Ph8@vHHQ?cT*JSPUS:`1 փȯ#b^ow ]Q*St$(ĺ(y(e ; {9X8ctyA ^'fk d>SsO̿Ft; qZ?BG͍7^[(ܨ`mM:DENdCY8'ݢ"衁%NQEKQ~Mrlk9&xvr^Mzo=֕7yyЃiOWl]0"tPnrȞ䧩6 H! Q"86V{:z'ׅaR[J5o9Tbksjg!"LPt ЀIt^Az f&[oQ<,~Ie~`%/8m-/]T?MS}'`[K.B2cPqyPCf3ї a A;!KfB tDnNZaP@='g%Yz ǽ|{d}0aZ q'#s+ 0I4a/b6  [žUddA>tWoid@2ܱw%WaR!SqM 5Lن5*D5 ڽOBrܟ(qWUPAMπ/ǁ{Jզ1DosK(+lut70@/e x#gg4mD“Pdc}UJbmM #w AErt%b;QY=眂8 ޓUDZJPpjÈ ;FFX([̡) kyFk:DȨFPG 'фiRΙ r` x}-TTAg p@(7IU6N5> lbլ hۡ5.@a>#?<.h-x9?|WJF; ʼnCvgY/2۴dIpEqBmIo#% %]H '%~]zs<6<;峈݃mթC׬~Υ.8´> +rP >E9ɝjM4A.Eb%UOT |SN2 %6\ki83.Ueʙ!WYӚM_1N5uw PZܠXn!ب^Izuq( l2%d׷?;lV5{0}۸^gPx6VAۗYD݇Jy]2PEh ۴{a &w:7L!}it E(ϖd{oD < EF„f$&.eڪiٗ6Gg(9Z5%ΫrriFkm3o"EF!cAS`00J}f`%I@sHP}HF'~)Vpc>oQ%{)B-Nv.b XRu j>~ewjen/G0 6fȌkU +q6RXTSzz䳲.uKmU6,¥Hpڐ4fzt"GV Ul^"vw3zho$ECOc ^jϱD3^%{JKax3Ö G'—玳y u@SiW%UNjRΐKE\@ibۂ\]=9*A!X#Ʌ| v߰r9=*)%K*mD:%aZ'O$@R|aj*;usE.sa?bn c'q,y m!VHxA 3;TA`:S3q| ?: ڷ}oR;bb&ae\>/I;U7>r]Q! Bd|?>2^u­TIIk!߶KggQ&))%CEYM( H]$:FɚcT4ȠGl~?9m}2wӡuE?R}!BŏKC랢?.{>xskaCa>B-$1γs'(Y=V43χGycj\n?ju 4`7%c\SH us8w;WLc/r _jf@WYCNט(gd&s0ɓLqS__[Xsxׄe>{l]qր30KSSdZS'϶*:CZ¹G 16~ <3OqLŹlRrB!Bfj0ì͎SR%Lu4h*s.=Fݞ_ٽI+ Y&^$\W9(θL}]R 6:A-iu!muR8a؂L<U4 ba+@H_+VzWP!R$VEZ{DG ;^)3ug_#x1={H!+Wjf؟R L `B$Zڼiwz׮)9;O|0N#Ul fζ˓٦tcPnsQ*Ц)PJ]>ݏNvw6j?r_RPM5XI"%еS?Yd;^mi]c,e^@Bp-.MkǓ;L~Q!+V3Cnlz<9.j'}qe'%#ͭ㭀5)wsb; ByEo dw+ՂYw*SCWP/R{ cDDB|gy-*W`90 rK8NRI45$V]1)ԅH۔*W\.ጚ)y_zhK"ɒƷ /џ+Kz#Vғ/mwIOcq.2!A~ c\\u ΚAj&>BsgVr '>V{=gyylp1~ 5*}ڋClt}EVAD9̍5ZᴮwܮB [])x  WҝBe 2 4 ΤVluVz`E]#|f9F1ExwK?eE}hE0\5y:QCԡ:NסIYwƏtw>`9!dW<]l6/~׿y[.,-ݟ;UjQ$G1y o.Qdn3$p e<7(45a+D<M:qz1,U}ɃTe_%-bEjexۛGab'SXر#q xBtM9Nu/DNf8T`Hr7nlu-ueFWefA:{|6L6XjiLj9@jSROjMQ:^ s S(Z$Ϳprbd%0yVz#%~59USPսԠHɹCFHAW20CzX7 ]lm3V=]0=adzɆIݶ$S0G+/ͧq<Ex"+!aYW:H#|1F܀H>:6^qF8eEӁpz[GuھZB/-57`"LSg LƖ*!+1r쬞WU.PԐ`m5d2l66}Ъϭ P1ˆ3 Kiť~ 5Lnb6+ gYZPK!`qf6,,ledtrig-usbport.ko.xznu[7zXZִF!t/;]?Eh=ڜ.+υ)p Zno_0R^~_:V3 p|!ԢZW9d\C8Cr7MI{)Vr݊Dϩ$ %t͌'%LE4\Et8ZA]+j=u[9^oyslJ5 x#- ӿD+*f\tE|uS 0 %dBC| {I P |7 U5Sz? m(6x"Uje_\hSZ0хiY,$Gb=œ?1Rj\NK4I69#×bG9z$O'tj dw)qxFL,.Ƚ =Jд{C+Ba'pUwYv1$$0Է]&Rt<֖0>jk()/h;0P5e=o}PڮPeODDZ7WJH+hsbnޒD2]GiW:̖p"CUG9&|8|b | PSgPy#\Z>_A%J)hPOu6#] s|nan]jݕdy\7"nG^!ry(noDn (Y0l}>=W Wk.Yȷ!<\SM`m^J! ֔`VPлL%PɂP[NX 4gmdUl|[*f$W'1;^w5řcFբSޏɝ5J-{;TD$ 2.0{"zWWh:ޑ+ f5իoGU22Dn2mF&#oZW⬙_qKņpƀk0qY o\ e`%8ӓ6B 7_`»QN3>( kP M1#H30pFaOZ_OL)66i_!Xs;gN%@yVrع Q@uv1?P>z|ۖՑ\^7uh}m8"o"}Uuf쬤r(v,a5@n+1§-X SK v\Ar'9H=@[ NC%Ki1<]_4-z='2nJ Y3sZ,$8Q`{hC|k>×T>Rfʚ4PL Tj6hM{8Ѕr⋒@lyњ+ߍ9HW Y5C/"z `i'9%6N@eMRIO8H;,`GNahރ"m,l)g} 0gۢ$v8d_` ?izj4?>Af >X[&xIDӅ Q~_h@Ŵ ]-c8>(H۰J}sb[ʎ iU*O5@sּCO^݂vx)%ƚ`SUJ%".D?CnQz1IX rTT/bs畳|_0.·-Z-YaIU}  ,ekӈl[NwjLZDb)$ v= caY'2'\S~x-[@X)1Z1N|FRH4̷HO4,?/ʁWu)4y}R::#fAƪ`|w..dz S$S%X F@|lք;=t76H[;eSz,ϩ= Bf!a3J0W-&;g}wA9>q5^'?B[VM8ĕi%,>Ӄ0 Ͼŀ.Y}kz gs+Ūwa HcǺ/V?`cګvBvu/<91RX-𖱩ې_d:Gs5|#i@<ɻB6XMgs/'gAUO|2AB1ǎEdR֬ W#cȚ6['!S+yZ[7C?,i JWΏ/5:;m(4*pjpkE?*&N{'m "31KWw8\ǷhQpjdG1jJ{&! >8m`+˦D涰;8s [Dm(miZq.ٻGHܝlP{|dCSSP]u)D„Kރ[7_O+uc7VVi3ޅBK5T:y+0M,SnaRNy`qh!$oLY½_D[0qWki#"w(@B-Q\|^eO"#aGccn YVFB}T 1_"?  Sy.(D Xρev5f_c!D{׏y|~G\ŭ;0g@_cư!I[$Sb2,>C(E׼B&֡,h;VU!`Y_b~-4ȖV(w-NgYZPK!ghhfailover.ko.xznu[7zXZִF!t/D(]?Eh=ڜ.+̉гX!HjlŮCkn\|ݾ\Ha?;wJx*h]POUZ3vT%܏.ŧzު1nc:ox\OswH ilBw*dחՓ|Zp;}XWo.T9q2XoZ>}R8^KaC納(`C暿q1@#.z#LLfo*.pGcע2BHiq2Ur`x:Ju!! n_\JpM=S2߶%LhjZ6wz̸ q(cNyye%*fݸYH\zxȵYZ/ hbF@}Y{B1H;*U ZRUX:.vvw̘ f>D(̑gD`Iav<_Vu_7*͹?J}ڍ}/lGC͡G- zevG36`IU%k.|ǿy GCl`pF" mY!ŗ:Ө#]Xy&aM% !zX ,mG-J\0lZ{LNM^cmRlZ<刺hdj֌yðr*qDϲjB>",B 89! b֧%H"ؐʱ.x~2aq* elga]$ 1a2Z]'N{vޝ˽tڈgђj $(;Ri +JnҰT/7|z^>HEv`3?1f/W&zy VC pKâm?n,^ twZ"5|Zx8oDɄil(:;Q/ѹLZݓߟÈHx [ȬEiO_Lzt#0Ei'4LEo;<9MѵP-*Ƶ3s)-L\+GS~g !{o\SxB/kgpo0?84"Q!H6N,nV1lk - B{[`2#@-$Č8[iYFNl%qֳP=2sRw9XZTMDP. nNhuUpY)ڄC5-5 f*QN>O v> I7.ntKeGl3`rNT'5x+o]]vX zv7 I~ /~ƖB꛽D s;!@tvʾS4M$YٳmKl.aaj^_`Gb;GƝf1{l˒V K+V禲nghpE(6SCb(UqœsDD$ȼ-.@C y~L*uL8"=ҼWpJ2Y\f~İϕ3#66ّEX/l"U]oiGuDa@捷]5[Q]YteJ^| $%byó:Iܾ;n]?f3ٌD#X́{D bq l4m# `SCX4VS^GqA%Pd3hYPX?)ܽ\!KkU5b(lP\&$hJʗK kt};̝q p gEut.a~JHZfd34B Fb [ ZC_Az)مNPUS@ZȁWD~P|;;IvNڣ"I㭭? 8w=ӼfÀ0y뵝y{p#1SWjP]k&!0\6E4),cs7o('.nb)㷸oϣxJza/ fvO li:k)%bhT=,s&[5IP.r8H4D-t(H!1,$P-ȅr~2D:zq3pF2)/̛:Bv= 6|kjW,rhޡM6d܀*0UzFW`y4j8 ql|Y %},r'pkr) a>Ŵ <G^?/`^k% D?[uu|%a IthXl"w10qsTU 4܅x䡶%nE'n^jl٥ d_(ݜ@!ѤwlHd9!O6> Y1/:2\7A#ݫB|l'ϯ.!9"+XWn].oTH֤UGʥvOֹnϛ!8z.- ӵHꉁ;L`;/S-(V7Z~aĝ[DaϷJdќp{<=IQTћ: TKKM-O~Q H͏]F05nt3?"{\!>GZ[$J nNL;٭+;S;us# Rlr+B  \#yEV1B%w'2g. {x9瞊Կ^oQUK.ʅ%a:g])p; $ˏ5{Kv}Z>CngɵvՏNOYHخ ?heXYmY K(zXҲAƲ,Y>`X!k~_Rk'~UAm[샪5!oק 8.5$h\O.gj'vK~ {{;.$ߜ0(iVztͪ#-C|A13{꒩lnX$fw!@_(hΌxԳ+! bz ;^v+wA-Ѐqכ'imUz]mC0Wjѭ8H^4 :r_vC͢p-)"uV|ϫ(]0H;Ỷ,gS!_jvHUy~GK@~9@T1&=۳pi F^ڥGw<ΞZ(T+y[fQxm).^da#67roDn+h d`0צ2'{`bt쀕?XPd򕝲:aZHaF=sܺU{$Oa ]Oz""/Ң-יIO:?'hX_' 3= (WgYZPK!( index.htmlnu[PK!bL_** Loader.phpnu[PK!S S !Controller.phpnu[PK!s~~Log.phpnu[PK!UX_3_3 gRouter.phpnu[PK! oo Security.phpnu[PK!o~<$<$ _Config.phpnu[PK!Λ>>>Utf8.phpnu[PK!bA>>CodeIgniter.phpnu[PK!V IIcompat/mbstring.phpnu[PK!PojZcompat/standard.phpnu[PK!cc"compat/password.phpnu[PK!(compat/index.htmlnu[PK!%'compat/hash.phpnu[PK!Ӝ8RR 1Output.phpnu[PK!9`;`;ڄURI.phpnu[PK!REXX qInput.phpnu[PK!YYLang.phpnu[PK!).jjQ.Exceptions.phpnu[PK!.: JBenchmark.phpnu[PK!\6  ZModel.phpnu[PK!gIUU  eCommon.phpnu[PK! kk Hooks.phpnu[PK!-8%y7y7rdata.hnu[PK!]_ss O robject.hnu[PK!SV"rbasic.hnu[PK!Y 8rclass.hnu[PK!JjpAAFrmatch.hnu[PK!}"NN 6\rstring.hnu[PK!H 66rhash.hnu[PK!5} rbignum.hnu[PK!kk rstruct.hnu[PK!SmC \rregexp.hnu[PK!%OcGGrarray.hnu[PK!ޓ1W1W 8rtypeddata.hnu[PK!xִZZrfile.hnu[PK!4;{g only.pmnu[PK! f__init__.pynu[PK![sBuBu nftables.pynu[PK!Ɵ8C$$ helper.pynu[PK!َ:  {fw_ifcfg.pynu[PK!gg (fw_service.pynu[PK!,:<<\/io/__init__.pynu[PK!/ 5io/helper.pynu[PK!Cc=== Vio/direct.pynu[PK!m io/ifcfg.pynu[PK!5io/icmptype.pynu[PK!z! -io/__pycache__/functions.cpython-36.opt-1.pycnu[PK!?xR`/`/'io/__pycache__/io_object.cpython-36.pycnu[PK!g22(io/__pycache__/zone.cpython-36.opt-1.pycnu[PK!qޝ&6io/__pycache__/icmptype.cpython-36.pycnu[PK!@*2Jio/__pycache__/firewalld_conf.cpython-36.opt-1.pycnu[PK!qޝ,hio/__pycache__/icmptype.cpython-36.opt-1.pycnu[PK!(# +|io/__pycache__/service.cpython-36.opt-1.pycnu[PK!))*io/__pycache__/helper.cpython-36.opt-1.pycnu[PK! h%h%6}io/__pycache__/lockdown_whitelist.cpython-36.opt-1.pycnu[PK!FmRR$Kio/__pycache__/policy.cpython-36.pycnu[PK!@*,). io/__pycache__/firewalld_conf.cpython-36.pycnu[PK!))$L io/__pycache__/helper.cpython-36.pycnu[PK!Vq/)c io/__pycache__/ifcfg.cpython-36.opt-1.pycnu[PK!9),),#s io/__pycache__/ipset.cpython-36.pycnu[PK!z! '6 io/__pycache__/functions.cpython-36.pycnu[PK!?xR`/`/-; io/__pycache__/io_object.cpython-36.opt-1.pycnu[PK! h%h%0 io/__pycache__/lockdown_whitelist.cpython-36.pycnu[PK!(# % io/__pycache__/service.cpython-36.pycnu[PK!g22"" io/__pycache__/zone.cpython-36.pycnu[PK!Vq/#-U io/__pycache__/ifcfg.cpython-36.pycnu[PK!I>..*Qe io/__pycache__/direct.cpython-36.opt-1.pycnu[PK!r&^ io/__pycache__/__init__.cpython-36.pycnu[PK!r,K io/__pycache__/__init__.cpython-36.opt-1.pycnu[PK!9),),)> io/__pycache__/ipset.cpython-36.opt-1.pycnu[PK!I>..$ io/__pycache__/direct.cpython-36.pycnu[PK!FmRR* io/__pycache__/policy.cpython-36.opt-1.pycnu[PK!oUU H io/ipset.pynu[PK! ϢϢ  io/policy.pynu[PK!γ11A io/lockdown_whitelist.pynu[PK!(55s io/io_object.pynu[PK!*yy io/functions.pynu[PK!8MM  io/zone.pynu[PK!9155T io/firewalld_conf.pynu[PK!mL22 ? io/service.pynu[PK!Dr prog.pynu[PK!\yy x fw_zone.pynu[PK!iI%% v+ipXtables.pynu[PK!  modules.pynu[PK!}mSq~q~*+__pycache__/fw_config.cpython-36.opt-1.pycnu[PK!jyy*ʪ__pycache__/ipXtables.cpython-36.opt-1.pycnu[PK!}mSq~q~$3__pycache__/fw_config.cpython-36.pycnu[PK!pnWMlMlb__pycache__/fw.cpython-36.pycnu[PK!;.?&__pycache__/fw_icmptype.cpython-36.pycnu[PK![{ޣ*'__pycache__/fw_helper.cpython-36.opt-1.pycnu[PK!(L+CC#/__pycache__/fw_ipset.cpython-36.pycnu[PK!ܥ]Z]Z!M__pycache__/logger.cpython-36.pycnu[PK![j):__pycache__/fw_transaction.cpython-36.pycnu[PK!ܧ ;__pycache__/icmp.cpython-36.pycnu[PK!QQ%:__pycache__/rich.cpython-36.opt-1.pycnu[PK!P@ @ (/__pycache__/modules.cpython-36.opt-1.pycnu[PK!{kS''__pycache__/helper.cpython-36.opt-1.pycnu[PK!;.?,(__pycache__/fw_icmptype.cpython-36.opt-1.pycnu[PK!{0z%1__pycache__/prog.cpython-36.opt-1.pycnu[PK!&4__pycache__/fw_nm.cpython-36.opt-1.pycnu[PK!+0̋#H__pycache__/fw_ifcfg.cpython-36.pycnu[PK!Xm "N__pycache__/watcher.cpython-36.pycnu[PK!(L+CC)Y__pycache__/fw_ipset.cpython-36.opt-1.pycnu[PK!jyy$Sw__pycache__/ipXtables.cpython-36.pycnu[PK!Ѩ=II$ __pycache__/fw_policy.cpython-36.pycnu[PK!ܧ %__pycache__/icmp.cpython-36.opt-1.pycnu[PK!{kS!__pycache__/helper.cpython-36.pycnu[PK!J|%__pycache__/fw_service.cpython-36.pycnu[PK!l|q"q" __pycache__/ipset.cpython-36.pycnu[PK!6 ;;#X__pycache__/nftables.cpython-36.pycnu[PK!5n  &__pycache__/fw_policies.cpython-36.pycnu[PK!4)c RR(V__pycache__/fw_zone.cpython-36.opt-1.pycnu[PK!'*#D__pycache__/ebtables.cpython-36.pycnu[PK!Ѩ=II*1a__pycache__/fw_policy.cpython-36.opt-1.pycnu[PK! 9__pycache__/fw_nm.cpython-36.pycnu[PK!4)c RR"M__pycache__/fw_zone.cpython-36.pycnu[PK!T22$__pycache__/fw_direct.cpython-36.pycnu[PK!P@ @ "__pycache__/modules.cpython-36.pycnu[PK!J|+ __pycache__/fw_service.cpython-36.opt-1.pycnu[PK!5n  ,__pycache__/fw_policies.cpython-36.opt-1.pycnu[PK!QQc__pycache__/rich.cpython-36.pycnu[PK!Xm (Rn__pycache__/watcher.cpython-36.opt-1.pycnu[PK!{0zXy__pycache__/prog.cpython-36.pycnu[PK!pnWMlMl#]|__pycache__/fw.cpython-36.opt-1.pycnu[PK!gzz__pycache__/base.cpython-36.pycnu[PK!6 ;;)__pycache__/nftables.cpython-36.opt-1.pycnu[PK!+0̋)Z__pycache__/fw_ifcfg.cpython-36.opt-1.pycnu[PK!gzz%>__pycache__/base.cpython-36.opt-1.pycnu[PK!'*) __pycache__/ebtables.cpython-36.opt-1.pycnu[PK![j/D__pycache__/fw_transaction.cpython-36.opt-1.pycnu[PK!qq#K__pycache__/__init__.cpython-36.pycnu[PK!qq)__pycache__/__init__.cpython-36.opt-1.pycnu[PK!l|q"q"&__pycache__/ipset.cpython-36.opt-1.pycnu[PK!ܥ]Z]Z'__pycache__/logger.cpython-36.opt-1.pycnu[PK!T22*TR__pycache__/fw_direct.cpython-36.opt-1.pycnu[PK![{ޣ$F__pycache__/fw_helper.cpython-36.pycnu[PK!$$ _ebtables.pynu[PK!4C88Hrich.pynu[PK! 6fw_config.pynu[PK!^ܪ>y>y  logger.pynu[PK! Pq2q2mipset.pynu[PK!OXWW +fw_direct.pynu[PK!E-ko]fw_transaction.pynu[PK!~ Dwatcher.pynu[PK!1 fw_policies.pynu[PK!*K)fw_nm.pynu[PK!Uе Efw_icmptype.pynu[PK!#$Oicmp.pynu[PK!gafw.pynu[PK!vg=V=V &fw_policy.pynu[PK!V_P)) }fw_helper.pynu[PK!%% fw_ipset.pynu[PK!266base.pynu[PK!4HHmmc_core.ko.xznu[PK!w!!ysdio_uart.ko.xznu[PK!W:UUmmc_block.ko.xznu[PK!3=CC) snd-rawmidi.ko.xznu[PK!)iCCn snd-timer.ko.xznu[PK!}? snd-seq-device.ko.xznu[PK!L L  snd-hrtimer.ko.xznu[PK!v$ seq/snd-seq-midi.ko.xznu[PK!Z !seq/snd-seq-dummy.ko.xznu[PK!TT!seq/snd-seq-midi-emul.ko.xznu[PK!^!seq/snd-seq-virmidi.ko.xznu[PK!Z; w!snd-ctl-led.ko.xznu[PK!Xz,, H "snd.ko.xznu[PK!c9 "snd-pcm.ko.xznu[PK!|4%4%n#snd-compress.ko.xznu[PK!#snd-hwdep.ko.xznu[PK!w(,(,#mspro_block.ko.xznu[PK!j;$memstick.ko.xznu[PK!`qf6,,$ledtrig-usbport.ko.xznu[PK!ghhK/$failover.ko.xznu[PK<?$