diff --git a/src/www/firewall_rules_edit.php b/src/www/firewall_rules_edit.php index 430304f9d..17b9b3c09 100644 --- a/src/www/firewall_rules_edit.php +++ b/src/www/firewall_rules_edit.php @@ -86,6 +86,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { } elseif (isset($_GET['id']) && isset($a_filter[$_GET['id']])) { $id = $_GET['id']; $configId = $id; + } elseif (isset($_GET['get_address_options'])) { + /* XXX: no beauty contest here, we need the same valid options as MVC, just dump them... */ + echo json_encode((new OPNsense\Firewall\Api\FilterController())->listNetworkSelectOptionsAction()); + exit(0); } // define form fields @@ -149,12 +153,26 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { $pconfig['category'] = !empty($pconfig['category']) ? explode(",", $pconfig['category']) : []; // process fields with some kind of logic - address_to_pconfig($a_filter[$configId]['source'], $pconfig['src'], - $pconfig['srcmask'], $pconfig['srcnot'], - $pconfig['srcbeginport'], $pconfig['srcendport']); - address_to_pconfig($a_filter[$configId]['destination'], $pconfig['dst'], - $pconfig['dstmask'], $pconfig['dstnot'], - $pconfig['dstbeginport'], $pconfig['dstendport']); + address_to_pconfig( + $a_filter[$configId]['source'], + $pconfig['src'], + $ignore, /* XXX: ignored */ + $pconfig['srcnot'], + $pconfig['srcbeginport'], + $pconfig['srcendport'], + true + ); + + address_to_pconfig( + $a_filter[$configId]['destination'], + $pconfig['dst'], + $ignore, /* XXX: ignored */ + $pconfig['dstnot'], + $pconfig['dstbeginport'], + $pconfig['dstendport'], + true + ); + if (isset($id) && isset($a_filter[$configId]['associated-rule-id'])) { // do not link on rule copy. $pconfig['associated-rule-id'] = $a_filter[$configId]['associated-rule-id']; @@ -294,18 +312,24 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { $input_errors[] = gettext('When selecting aliases for destination ports, both from and to fields must be the same'); } } - if (!is_specialnet($pconfig['src']) && !is_ipaddroralias($pconfig['src'])) { + if (strpos($pconfig['src'], ',') > 0) { + foreach (explode(',', $pconfig['src']) as $tmp) { + if (!is_specialnet($tmp) && !is_alias($tmp)) { + $input_errors[] = sprintf(gettext("%s is not a valid source alias."), $tmp); + } + } + } elseif (!is_specialnet($pconfig['src']) && !is_ipaddroralias($pconfig['src']) && !is_subnet($pconfig['src'])) { $input_errors[] = sprintf(gettext("%s is not a valid source IP address or alias."),$pconfig['src']); } - if (!empty($pconfig['srcmask']) && !is_numericint($pconfig['srcmask'])) { - $input_errors[] = gettext("A valid source bit count must be specified."); - } - if (!is_specialnet($pconfig['dst']) && !is_ipaddroralias($pconfig['dst'])) { + if (strpos($pconfig['dst'], ',') > 0) { + foreach (explode(',', $pconfig['dst']) as $tmp) { + if (!is_specialnet($tmp) && !is_alias($tmp)) { + $input_errors[] = sprintf(gettext("%s is not a valid destination alias."), $tmp); + } + } + } elseif (!is_specialnet($pconfig['dst']) && !is_ipaddroralias($pconfig['dst']) && !is_subnet($pconfig['dst'])) { $input_errors[] = sprintf(gettext("%s is not a valid destination IP address or alias."),$pconfig['dst']); } - if (!empty($pconfig['dstmask']) && !is_numericint($pconfig['dstmask'])) { - $input_errors[] = gettext("A valid destination bit count must be specified."); - } if (is_ipaddr($pconfig['src']) && is_ipaddr($pconfig['dst'])) { if ((is_ipaddrv4($pconfig['src']) && is_ipaddrv6($pconfig['dst'])) || (is_ipaddrv6($pconfig['src']) && is_ipaddrv4($pconfig['dst']))) { @@ -314,21 +338,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { } foreach (array('src', 'dst') as $fam) { if (is_ipaddr($pconfig[$fam])) { - if (is_ipaddrv6($pconfig[$fam]) && $pconfig['ipprotocol'] == "inet") { + if ((is_ipaddrv6($pconfig[$fam]) || is_subnetv6($pconfig[$fam])) && $pconfig['ipprotocol'] == "inet") { $input_errors[] = gettext("You can not use IPv6 addresses in IPv4 rules."); - } elseif (is_ipaddrv4($pconfig[$fam]) && $pconfig['ipprotocol'] == "inet6") { + } elseif ((is_ipaddrv4($pconfig[$fam]) || is_subnetv4($pconfig[$fam])) && $pconfig['ipprotocol'] == "inet6") { $input_errors[] = gettext("You can not use IPv4 addresses in IPv6 rules."); } } } - if (is_ipaddrv4($pconfig['src']) && $pconfig['srcmask'] > 32) { - $input_errors[] = gettext("Invalid subnet mask on IPv4 source"); - } - if (is_ipaddrv4($pconfig['dst']) && $pconfig['dstmask'] > 32) { - $input_errors[] = gettext("Invalid subnet mask on IPv4 destination"); - } - if ((is_ipaddr($pconfig['src']) || is_ipaddr($pconfig['dst'])) && ($pconfig['ipprotocol'] == "inet46")) { $input_errors[] = gettext('You can not use an IPv4 or IPv6 address in combined IPv4 + IPv6 rules.'); } @@ -395,7 +412,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { if (is_numeric($pconfig['adaptivestart']) || is_numeric($pconfig['adaptiveend'])) { $input_errors[] = gettext("You cannot specify the adaptive timeouts (advanced option) if statetype is none."); } - } if (!empty($pconfig['max']) && !is_posnumericint($pconfig['max'])) @@ -586,10 +602,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { } pconfig_to_address($filterent['source'], $pconfig['src'], - $pconfig['srcmask'] ?? '', !empty($pconfig['srcnot']), + '', !empty($pconfig['srcnot']), $pconfig['srcbeginport'], $pconfig['srcendport']); pconfig_to_address($filterent['destination'], $pconfig['dst'], - $pconfig['dstmask'] ?? '', !empty($pconfig['dstnot']), + '', !empty($pconfig['dstnot']), $pconfig['dstbeginport'], $pconfig['dstendport']); $filterent['updated'] = make_config_revision_entry(); @@ -652,10 +668,54 @@ include("head.inc"); $(".advanced_opt_src").toggleClass("hidden visible"); }); + $.ajax("/firewall_rules_edit.php?get_address_options",{ + type: 'get', + cache: false, + dataType: "json", + success: function(data) { + $(".net_selector_multi").each(function(){ + /* replaceInputWithSelector() replaces our input with a clean one, copy relevant attributes after construct */ + $(this).replaceInputWithSelector(data, true); + let new_input = $("#" + $(this).attr('id')); + new_input.attr('name', $(this).attr('id')); + if ($(this).is(':disabled')) { + $("select[for='" + $(this).attr('id') + "']").prop('disabled', true); + new_input.prop('disabled', true); + } + $("select[for='" + $(this).attr('id') + "']").on('shown.bs.select', function(){ + $(this).data('previousValue', $(this).val()); + }).change(function(){ + let prev = Array.isArray($(this).data('previousValue')) ? $(this).data('previousValue') : []; + let is_single = $(this).val().includes('') || $(this).val().includes('any'); + let was_single = prev.includes('') || prev.includes('any'); + let refresh = false; + if (was_single && is_single && $(this).val().length > 1) { + $(this).val($(this).val().filter(value => !prev.includes(value))); + refresh = true; + } else if (is_single && $(this).val().length > 1) { + if ($(this).val().includes('any') && !prev.includes('any')) { + $(this).val('any'); + } else{ + $(this).val(''); + } + refresh = true; + } + if (refresh) { + $(this).selectpicker('refresh'); + $(this).trigger('change'); + } + $(this).data('previousValue', $(this).val()); + }); + new_input.val($(this).val()).change(); + }); + } + }); + + // select / input combination, link behaviour // when the data attribute "data-other" is selected, display related input item(s) // push changes from input back to selected option value - $('[for!=""][for]').each(function(){ + $('.portselect').each(function(){ var refObj = $("#"+$(this).attr("for")); if (refObj.is("select")) { // connect on change event to select box (show/hide) @@ -689,7 +749,7 @@ include("head.inc"); if ($(this).attr("name") == undefined) { $(this).change(function(){ var otherOpt = $('#'+$(this).attr('for')+' > option[data-other="true"]') ; - otherOpt.attr("value",$(this).val()); + otherOpt.attr("value", $(this).val()); }); } } @@ -1064,51 +1124,8 @@ include("head.inc"); - - - - - - - -
- -
-
- - - - - - - -
- - type="text" id="src_address" for="src" value="" aria-label=""/> - - -
-
-
- + id="src" name="src" class="net_selector_multi" type="text" value="" /> + @@ -1168,10 +1185,10 @@ include("head.inc"); - type="text" value="" for="srcbeginport"> + type="text" value="" class="portselect" for="srcbeginport"> - type="text" value="" for="srcendport"> + type="text" value="" class="portselect" for="srcendport"> @@ -1192,48 +1209,7 @@ include("head.inc"); - - - - - - - -
- -
- - - - - - - -
- - type="text" id="dst_address" for="dst" value="" aria-label=""/> - - -
-
+ id="dst" name="dst" type="text" value="" class="net_selector_multi" /> @@ -1285,10 +1261,10 @@ include("head.inc"); - type="text" value="" for="dstbeginport"> + class="portselect" type="text" value="" for="dstbeginport"> - type="text" value="" for="dstendport"> + class="portselect" type="text" value="" for="dstendport"> diff --git a/src/www/guiconfig.inc b/src/www/guiconfig.inc index 96a36ec4e..b70fc0adf 100644 --- a/src/www/guiconfig.inc +++ b/src/www/guiconfig.inc @@ -385,7 +385,7 @@ function gentitle($breadcrumbs, $navlevelsep = ': ') return join($navlevelsep, $output) . "$gentitle_suffix"; } -function address_to_pconfig($adr, &$padr, &$pmask, &$pnot, &$pbeginport, &$pendport) +function address_to_pconfig($adr, &$padr, &$pmask, &$pnot, &$pbeginport, &$pendport, $merge_mask=false) { if (isset($adr['any'])) { $padr = "any"; @@ -402,6 +402,9 @@ function address_to_pconfig($adr, &$padr, &$pmask, &$pnot, &$pbeginport, &$pendp $pmask = 32; } } + if ($merge_mask && is_ipaddr($padr)) { + $padr = $padr . '/' . $pmask; + } } if (isset($adr['not'])) {