home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
boot
/
i386
/
root
/
usr
/
share
/
YaST2
/
modules
/
PortRanges.ycp
< prev
next >
Wrap
Text File
|
2006-11-29
|
17KB
|
498 lines
/**
* 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 */
}