home *** CD-ROM | disk | FTP | other *** search
- /**
- * Copyright 2004, Novell, Inc. All rights reserved.
- *
- * File: modules/PortRanges.ycp
- * Package: SuSEFirewall configuration
- * Summary: Checking and manipulation with port ranges (iptables).
- * Authors: Lukas Ocilka <locilka@suse.cz>
- *
- * $id$
- *
- * Module for handling port ranges.
- */
-
- {
- module "PortRanges";
- textdomain "base";
-
- import "PortAliases";
-
- // Local Helper Functions -->
-
- /**
- * Variable for ReportOnlyOnce() function
- */
- list <string> report_only_once = [];
-
- /**
- * Report the error, warning, message only once.
- * Stores the error, warning, message in memory.
- * This is just a helper function that could avoid from filling y2log up with
- * a lot of the very same messages - 'foreach()' is a very powerful builtin.
- *
- * @param string error, warning or message
- * @return boolean whether the message should be reported or not
- *
- * @example
- * string error = sformat("Port number %1 is invalid.", port_nr);
- * if (ReportOnlyOnce(error)) y2error(error);
- */
- boolean ReportOnlyOnce (string what_to_report) {
- if (contains(report_only_once, what_to_report)) {
- return false;
- } else {
- report_only_once = add (report_only_once, what_to_report);
- return true;
- }
- }
-
- // <-- Local Helper Functions
-
- /**
- * Maximal number of port number, they are in the interval 1-65535 included.
- * The very same value should appear in SuSEFirewall::max_port_number.
- */
- global integer max_port_number = 65535;
-
- // Port Ranges -->
-
- /**
- * Function returns where the string parameter is a port range.
- * Port ranges are defined by the syntax "min_port_number:max_port_number".
- * Port range means that these maximum and minimum ports define the range
- * of currency in Firewall. Ports defining the range are included in it.
- * This function doesn't check whether the port range is valid or not.
- *
- * @param string to be checked
- * @return boolean whether the checked string is a port range or not
- *
- * @see IsValidPortRange()
- *
- * @example
- * IsPortRange("34:38") -> true
- * IsPortRange("0:38") -> true
- * IsPortRange("port-range") -> false
- * IsPortRange("19-22") -> false
- */
- global boolean IsPortRange (string check_this) {
- if (regexpmatch(check_this,"^[0123456789]+:[0123456789]+$")) {
- return true;
- }
- return false;
- }
-
- /**
- * Checks whether the port range is valid.
- *
- * @param string port_range
- * @return boolean if it is valid
- *
- * @see IsPortRange()
- *
- * @example
- * IsValidPortRange("54:135") -> true // valid
- * IsValidPortRange("135:54") -> false // reverse order
- * IsValidPortRange("0:135") -> false // cannot be from 0
- * IsValidPortRange("135") -> false // cannot be one number
- * IsValidPortRange("54-135") -> false // wrong separator
- */
- global boolean IsValidPortRange (string port_range) {
- // not a port range
- if (! IsPortRange(port_range)) return false;
-
- integer min_pr = tointeger(regexpsub(port_range,"^([0123456789]+):.*$", "\\1"));
- integer max_pr = tointeger(regexpsub(port_range,"^.*:([0123456789]+)$", "\\1"));
-
- // couldn't extract two integers
- if (min_pr == nil && max_pr == nil) return false;
-
- // Checking the minimal port number in the port-range
- // wrong range
- if (min_pr < 1 || min_pr > max_port_number) {
- string warning = sformat ("Wrong port-range definition %1", port_range);
- if (ReportOnlyOnce(warning)) y2warning(warning);
-
- return false;
- }
-
- // Checking the maximal port number in the port-range
- // wrong range
- if (max_pr < 1 || max_pr > max_port_number) {
- string warning = sformat ("Wrong port-range definition %1", port_range);
- if (ReportOnlyOnce(warning)) y2warning(warning);
-
- return false;
- }
-
- // wrong range
- if (min_pr >= max_pr) {
- string warning = sformat ("Wrong port-range definition %1", port_range);
- if (ReportOnlyOnce(warning)) y2warning(warning);
-
- return false;
- }
-
- return true;
- }
-
- /**
- * Function returns where the port name or port number is included in the
- * list of port ranges. Port ranges must be defined as a string with format
- * "min_port_number:max_port_number".
- *
- * @param string port_number_or_port_name
- * @param list <string> port_ranges
- *
- * @example
- * PortIsInPortranges ("130", ["100:150","10:30"]) -> true
- * PortIsInPortranges ("30", ["100:150","10:20"]) -> false
- * PortIsInPortranges ("pop3", ["100:150","10:30"]) -> true
- * PortIsInPortranges ("http", ["100:150","10:20"]) -> false
- */
- global boolean PortIsInPortranges (string port, list <string> port_ranges) {
- if (size(port_ranges) == 0) return false;
-
- boolean ret = false;
-
- integer port_number = PortAliases::GetPortNumber(port);
-
- if (port_number != nil) {
- foreach (string port_range, port_ranges, {
- // is portrange really a port range?
- if (IsValidPortRange(port_range)) {
- integer min_pr = tointeger(regexpsub(port_range,"^([0123456789]+):.*$", "\\1"));
- integer max_pr = tointeger(regexpsub(port_range,"^.*:([0123456789]+)$", "\\1"));
-
- // is the port inside?
- if (min_pr <= max_pr && min_pr <= port_number && port_number <= max_pr) {
- ret = true;
-
- break; // break the loop, match found
- }
- }
- });
- }
-
- return ret;
- }
-
- /**
- * Function divides list of ports to the map of ports and port ranges.
- * If with_aliases is 'true' it also returns ports wit their port aliases.
- * Port ranges are not affected with it.
- *
- * @struct Returns $[
- * "ports" : [ list of ports ],
- * "port_ranges" : [ list of port ranges ],
- * ]
- *
- * @param list <string> unsorted_ports
- * @param boolean with port aliases
- * @return <map <string, list <string> > > of divided ports
- */
- global map <string, list <string> > DividePortsAndPortRanges (list <string> unsorted_ports, boolean with_aliases) {
- map <string, list <string> > ret = $[];
-
- foreach (string port, unsorted_ports, {
- // port range
- if (IsPortRange(port)) {
- ret["port_ranges"] = add (ret["port_ranges"]:[], port);
- // is a normal port
- } else {
- // find also aliases
- if (with_aliases) {
- ret["ports"] = (list<string>) union (
- ret["ports"]:[], PortAliases::GetListOfServiceAliases(port)
- );
- // only add the port itself
- } else {
- ret["ports"] = add (ret["ports"]:[], port);
- }
- }
- });
-
- return ret;
- }
-
- /**
- * Function creates a port range from min and max params. Max must be bigger than min.
- * If something is wrong, it returns an empty string.
- *
- * @param integer min_port
- * @param integer max_port
- * @return string new port range
- */
- global string CreateNewPortRange (integer min_pr, integer max_pr) {
- if (min_pr == nil || min_pr == 0) {
- y2error("Wrong definition of the starting port '%1', it must be between 1 and 65535", min_pr);
- return "";
- } else if (max_pr == nil || max_pr == 0 || max_pr > 65535) {
- y2error("Wrong definition of the ending port '%1', it must be between 1 and 65535", max_pr);
- return "";
- }
-
- // max and min are the same, this is not a port range
- if (min_pr == max_pr) {
- return tostring(min_pr);
- // right port range
- } else if (min_pr < max_pr) {
- return tostring(min_pr) + ":" + tostring(max_pr);
- // min is bigger than max
- } else {
- y2error("Starting port '%1' cannot be bigger than ending port '%2'", min_pr, max_pr);
- return "";
- }
- }
-
- /**
- * Function removes port number from all port ranges. Port must be in its numeric
- * form.
- *
- * @see PortAliases::GetPortNumber()
- * @param integer port_number to be removed
- * @param list <string> of all current port_ranges
- * @return list <string> of filtered port_ranges
- *
- * @example
- * RemovePortFromPortRanges(25, ["19-88", "152-160"]) -> ["19-24", "26-88", "152-160"]
- */
- global list <string> RemovePortFromPortRanges (integer port_number, list <string> port_ranges) {
- // Checking necessarity of filtering and params
- if (port_ranges == nil || port_ranges == []) return port_ranges;
- if (port_number == nil || port_number == 0) return port_ranges;
-
- y2milestone("Removing port %1 from port ranges %2", port_number, port_ranges);
-
- list <string> ret = [];
- // Checking every port range alone
- foreach (string port_range, port_ranges, {
- // Port range might be now only "port"
- if (!IsPortRange(port_range)) {
- // If the port doesn't match the ~port_range...
- if (tostring(port_number) != port_range)
- ret = add (ret, port_range);
- // If matches, it isn't added (it is filtered)
- // Modify the port range when the port is included
- } else if (PortIsInPortranges(tostring(port_number), [port_range])) {
- integer min_pr = tointeger(regexpsub(port_range,"^([0123456789]+):.*$", "\\1"));
- integer max_pr = tointeger(regexpsub(port_range,"^.*:([0123456789]+)$", "\\1"));
-
- // Port matches the min. value of port range
- if (port_number == min_pr) {
- ret = add (ret, CreateNewPortRange(port_number + 1, max_pr));
- // Port matches the max. value of port range
- } else if (port_number == max_pr) {
- ret = add (ret, CreateNewPortRange(min_pr, port_number - 1));
- // Port is inside the port range, split it up
- } else {
- ret = add (ret, CreateNewPortRange(port_number + 1, max_pr));
- ret = add (ret, CreateNewPortRange(min_pr, port_number - 1));
- }
- // Port isn't in the port range, adding the current port range
- } else {
- ret = add (ret, port_range);
- }
- });
-
- y2milestone("Result: %1", ret);
-
- return ret;
- }
-
- /**
- * Function tries to flatten services into the minimal list.
- * If ports are already mentioned inside port ranges, they are dropped.
- *
- * @param list <string> of services and port ranges
- * @return list <string> of flattened services and port ranges
- */
- global list <string> FlattenServices (list <string> old_list, string protocol) {
- if (! contains(["TCP", "UDP"], protocol)) {
- string message = sformat("Protocol %1 doesn't support port ranges, skipping...", protocol);
- if (ReportOnlyOnce(message)) y2milestone(message);
- return old_list;
- }
-
- list <string> new_list = [];
- list <string> list_of_ports = [];
- list <string> list_of_ranges = [];
- // Using port number, we can remove ports mentioned in port ranges
- map <string, integer> ports_to_port_numbers = $[];
- // Using this we can remove ports mentioned more than once
- map <integer, list <string> > port_numbers_to_port_names = $[];
-
- foreach (string one_item, old_list, {
- // Port range
- if (IsPortRange(one_item)) list_of_ranges = add (list_of_ranges, one_item);
- // Port number or name
- else {
- integer port_number = PortAliases::GetPortNumber(one_item);
- // Cannot find port number for this port, it is en error of the configuration
- if (port_number == nil) {
- y2warning("Unknown port %1 but leaving it in the configuration.", one_item);
- new_list = add (new_list, one_item);
- // skip the 'nil' port number
- return;
- }
- ports_to_port_numbers[one_item] = port_number;
- port_numbers_to_port_names[port_number] = add (port_numbers_to_port_names[port_number]:[], one_item);
- }
- });
-
- foreach (integer port_number, list <string> port_names, port_numbers_to_port_names, {
- // Port is not in any defined port range
- if (!PortIsInPortranges(tostring(port_number), list_of_ranges)) {
- // Port - 1 IS in some port range
- if (PortIsInPortranges(tostring(port_number - 1), list_of_ranges)) {
- // Creating fake port range, to be joined with another one
- list_of_ranges = add (list_of_ranges, CreateNewPortRange(port_number - 1, port_number));
- // Port + 1 IS in some port range
- } else if (PortIsInPortranges(tostring(port_number + 1), list_of_ranges)) {
- // Creating fake port range, to be joined with another one
- list_of_ranges = add (list_of_ranges, CreateNewPortRange(port_number, port_number + 1));
- // Port is not in any port range and also it cannot be joined with any one
- } else {
- // Port names of this port
- list <string> used_port_names = port_numbers_to_port_names[port_number]:[];
- if (size(used_port_names)>0) {
- new_list = add (new_list, used_port_names[0]:"");
- } else {
- y2milestone("No port name for port number %1. Adding %1...", port_number);
- // There are no port names (hmm?), adding port number
- new_list = add (new_list, tostring(port_number));
- }
- }
- // Port is in a port range
- } else {
- y2milestone("Removing port %1 mentioned in port ranges %2", port_number, list_of_ranges);
- }
- });
-
- list_of_ranges = toset(list_of_ranges);
- // maximal count of steps
- integer max_loops = 5000;
-
- // Joining port ranges together
- // this is a bit dangerous!
- y2milestone("Joining list of ranges %1", list_of_ranges);
- while (true && max_loops>0) {
- // if something goes wrong
- max_loops = max_loops - 1;
-
- boolean any_change_during_this_loop = false;
-
- list <string> try_all_these_ranges = list_of_ranges;
- foreach (string port_range, try_all_these_ranges, {
- if (! IsValidPortRange(port_range)) {
- string warning = sformat("Wrong port-range definition %1, cannot join", port_range);
- if (ReportOnlyOnce(warning)) y2warning(warning);
- return;
- }
-
- integer min_pr = tointeger(regexpsub(port_range,"^([0123456789]+):.*$", "\\1"));
- integer max_pr = tointeger(regexpsub(port_range,"^.*:([0123456789]+)$", "\\1"));
-
- if (min_pr == nil || max_pr == nil) {
- y2error("Not a port range %1", port_range);
- return;
- }
-
- // try to join it with another port ranges
- // -->
- foreach (string try_this_pr, try_all_these_ranges, {
- // Exact match means the same port range
- if (try_this_pr == port_range) return;
-
- string this_min = regexpsub(try_this_pr,"^([0123456789]+):.*$", "\\1");
- string this_max = regexpsub(try_this_pr,"^.*:([0123456789]+)$", "\\1");
-
- if (this_min == nil || this_max == nil) {
- y2error("Wrong port range %1, %2 > %3", port_range, this_min, this_max);
- // skip it
- return;
- }
-
- integer this_min_pr = tointeger(this_min);
- integer this_max_pr = tointeger(this_max);
-
- // // wrong definition of the port range
- if (this_min_pr < 1 || this_max_pr > max_port_number) {
- string warning = sformat("Wrong port-range definition %1, cannot join", port_range);
- if (ReportOnlyOnce(warning)) y2warning(warning);
- // skip it
- return;
- }
-
- // If new port range should be created
- integer new_min = nil;
- integer new_max = nil;
-
- // the second one is inside the first one
- if (min_pr <= this_min_pr && max_pr >= this_max_pr) {
- // take min_pr & max_pr
- any_change_during_this_loop = true;
- new_min = min_pr;
- new_max = max_pr;
- // the fist one is inside the second one
- } else if (min_pr >= this_min_pr && max_pr <= this_max_pr) {
- // take this_min_pr & this_max_pr
- any_change_during_this_loop = true;
- new_min = this_min_pr;
- new_max = this_max_pr;
- // the fist one partly covers the second one (by its right side)
- } else if (min_pr <= this_min_pr && max_pr >= this_min_pr) {
- // take min_pr & this_max_pr
- any_change_during_this_loop = true;
- new_min = min_pr;
- new_max = this_max_pr;
- // the second one partly covers the first one (by its left side)
- } else if (min_pr >= this_min_pr && max_pr <= this_max_pr) {
- // take this_min_pr & max_pr
- any_change_during_this_loop = true;
- new_min = this_min_pr;
- new_max = max_pr;
- // the first one has the second one just next on the right
- } else if ((max_pr + 1) == this_min_pr) {
- // take min_pr & this_max_pr
- any_change_during_this_loop = true;
- new_min = min_pr;
- new_max = this_max_pr;
- // the first one has the second one just next on the left side
- } else if ((min_pr - 1) == this_max_pr) {
- // take this_min_pr & max_pr
- any_change_during_this_loop = true;
- new_min = this_min_pr;
- new_max = max_pr;
- }
-
- if (any_change_during_this_loop && new_min != nil && new_max != nil) {
- string new_port_range = CreateNewPortRange(new_min, new_max);
- y2milestone("Joining %1 and %2 into %3", port_range, try_this_pr, new_port_range);
- // Remove old port ranges
- list_of_ranges = filter (string filter_pr, list_of_ranges, {
- return (filter_pr != port_range && filter_pr != try_this_pr);
- });
- // Create a new one
- list_of_ranges = add (list_of_ranges, new_port_range);
- }
- });
- // <--
-
- // renew list of current port ranges, they have changed
- if (any_change_during_this_loop) break;
- });
-
- if (!any_change_during_this_loop) break;
- }
- y2milestone("Result of joining: %1", list_of_ranges);
-
- new_list = (list <string>) union (new_list, list_of_ranges);
-
- return new_list;
- }
-
- // <-- Port Ranges
-
- /* EOF */
- }
-