public inbox for development@lists.ipfire.org
 help / color / mirror / Atom feed
From: Michael Tremer <michael.tremer@ipfire.org>
To: development@lists.ipfire.org
Subject: Re: [PATCH] New addon: Portredirect 1.0
Date: Thu, 01 Jul 2021 16:24:11 +0100	[thread overview]
Message-ID: <56118375-8C9D-4553-A919-6696E7944E8D@ipfire.org> (raw)
In-Reply-To: <ADA23788-C972-417C-8F55-DD9D3B335BE9@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 78729 bytes --]

@Stefan: There should be an exception in the UI that these rules can be created when REDIRECT is being used.

> On 1 Jul 2021, at 16:04, Jon Murphy <jcmurphy26(a)gmail.com> wrote:
> 
>> You probably want “any” as destination.
> 
> Those are the only choices that allow a Save.  When I enter an IP address I get this error:
> 
> 
> Error messages
> 
> Source and destination IP addresses are from the same subnet.  
> 
> 
> 
> Jon
> 
>> On Jul 1, 2021, at 3:08 AM, Michael Tremer <michael.tremer(a)ipfire.org> wrote:
>> 
>> Hey Jon,
>> 
>> You probably want “any” as destination.
>> 
>> -Michael
>> 
>>> On 1 Jul 2021, at 04:08, Jon Murphy <jcmurphy26(a)gmail.com> wrote:
>>> 
>>> Hi Stefan,
>>> 
>>> Thank you for taking this on!
>>> 
>>> I applied the patchwork.ipfire patch.
>>> 
>>> I think I entered something wrong since I cannot get things to work.  I tried both with Destination Firewall GREEN & Firewall RED.
>>> 
>>> 
>>> Does the Firewall Rule seem right?
>>> 
>>> Best regards,
>>> Jon
>>> 
>>> 
>>> Here is the rule I set up:
>>> 
>>> 
>>> <Screen Shot 2021-06-30 at 9.53.52 PM.png>
>>> 
>>> <Screen Shot 2021-06-30 at 9.53.10 PM.png>
>>> 
>>> 
>>> And this is what I see with conntrack:
>>> 
>>> conntrack -E -e NEW,UPDATE | grep -e "=53 "
>>> 
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=51169 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=51169
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=54168 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=54168
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=56094 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=56094
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=52964 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=52964
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=53279 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=53279
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=61657 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=61657
>>>    [NEW] udp      17 30 src=192.168.1.102 dst=1.2.3.4 sport=57723 dport=53 [UNREPLIED] src=1.2.3.4 dst=10.7.4.10 sport=53 dport=57723
>>> 
>>> 
>>> 
>>> 
>>>> On Jun 30, 2021, at 2:14 PM, Stefan Schantl <stefan.schantl(a)ipfire.org> wrote:
>>>> 
>>>> Hello Matthias, Hello Michael, Hello Jon, Hello *,
>>>> 
>>>> I've followed the conversation on this list since the first mail and
>>>> thoughts about forcing DNS traffic to use the local resolver.
>>>> 
>>>> It was a very long journey and lot of time and work has been spent to
>>>> get to the present point.
>>>> 
>>>> As Michael requested here, I've digged through the lines of the perl
>>>> script which is responsible for creating the firewall rules and
>>>> surprisingly found that everyting which is needed to create generic
>>>> REDIRECT rules already was written in the past - it just did not work
>>>> as designed/expected.
>>>> 
>>>> Finaly I was able to adjust these lines of code and to repair that
>>>> feature.
>>>> 
>>>> A redirect rule can be created by picking a single host or group of
>>>> hosts or entire network(s) as source, selecting NAT (DNAT) and choosing
>>>> the Firewall itself as target.
>>>> 
>>>> The protocol or service or service group which should be redirected has
>>>> to be selected afterwards. If you want to redirect a given port to
>>>> another one it can be specified as "Target port".
>>>> 
>>>> All created redirect rules are displayed as "input rules".
>>>> 
>>>> 
>>>> The patch directly can be accessed here:
>>>> 
>>>> https://patchwork.ipfire.org/project/ipfire/patch/20210630184031.7726-1-stefan.schantl(a)ipfire.org/
>>>> 
>>>> Best regards,
>>>> 
>>>> -Stefan
>>>> 
>>>>> Hello,
>>>>> 
>>>>>> On 28 Jun 2021, at 18:53, Jon Murphy <jcmurphy26(a)gmail.com> wrote:
>>>>>> 
>>>>>> Hi Michael!  Happy Monday!
>>>>>> 
>>>>>> 
>>>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>>>> of code instead of adding many hundreds of lines?
>>>>>>> 
>>>>>>> Please can someone elaborate on this more?
>>>>>> 
>>>>>> Doing a DNS redirect, via the WegBUI, has been an issue since
>>>>>> 2015.  I found this quote in the old forum:
>>>>>> 
>>>>>> "Having investigated a bit more I have concluded that it's not
>>>>>> currently possible to create such rules through the WUI.
>>>>>> 
>>>>>> There are a number of obstacles:
>>>>>> 1. It is not allowed to create a rule where source IP and
>>>>>> destination nat IP is on the same subnetwork (e.g. GREEN), WUI
>>>>>> error message: "Source and destination IP addresses are from the
>>>>>> same subnet."
>>>>>> 
>>>>>> 2. WUI will not allow you to create a rule without a destination
>>>>>> (the filtered packet must adhere to a destination, not only a port)
>>>>>> and the destination MUST be an IP address of one of the IPFire
>>>>>> interfaces, which limits whats possible a great deal." 
>>>>> 
>>>>> And these cannot be changed?
>>>>> 
>>>>>> And I found this from 2016:
>>>>>> https://bugzilla.ipfire.org/show_bug.cgi?id=11168
>>>>>> 
>>>>>> So I am guessing that no one has been able to determine a way to
>>>>>> extend the WebGUI.  
>>>>> 
>>>>> Has anyone tried? I do not see any obvious reasons why this should
>>>>> not be possible.
>>>>> 
>>>>>> I am curious - Who created the 
>>>>>> https://ipfire:444/cgi-bin/firewall.cgi page?  And could they help?
>>>>> 
>>>>> -Michael
>>>>> 
>>>>>> Jon
>>>>>> 
>>>>>> 
>>>>>>> On Jun 28, 2021, at 11:04 AM, Michael Tremer <
>>>>>>> michael.tremer(a)ipfire.org> wrote:
>>>>>>> 
>>>>>>> Hello Matthias,
>>>>>>> 
>>>>>>>> On 27 Jun 2021, at 14:48, Matthias Fischer <
>>>>>>>> matthias.fischer(a)ipfire.org> wrote:
>>>>>>>> 
>>>>>>>> From: Marcel Lorenz <marcel.lorenz(a)ipfire.org>
>>>>>>> 
>>>>>>> Thank you for sending this patch on Marcel’s behalf, but I would
>>>>>>> much more prefer if he would submit his patches on his own. I do
>>>>>>> not see why that isn’t possible.
>>>>>>> 
>>>>>>>> Please note:
>>>>>>>> This is a new addon written by Marcel Lorenz <
>>>>>>>> marcel.lorenz(a)ipfire.org>.
>>>>>>>> 
>>>>>>>> It adds a new GUI to IPFire for DNS/NTP *and* user specific
>>>>>>>> port redirections.
>>>>>>>> 
>>>>>>>> How its working:
>>>>>>>> It has exactly the same functionalities as "Forcing
>>>>>>>> DNS/NTP..."  - and some more.
>>>>>>>> 
>>>>>>>> By setting switches, DNS/NTP requests are automatically
>>>>>>>> redirected to the local IPFire DNS/NTP servers.
>>>>>>>> 
>>>>>>>> Additionally, the user can specify custom redirections.
>>>>>>>> 
>>>>>>>> These rules are added to a new chain in PREROUTING =>
>>>>>>>> PORT_REDIRECT.
>>>>>>>> 
>>>>>>>> To avoid problems with (e.g.) transparent 'squid'
>>>>>>>> configurations,
>>>>>>>> redirection rules are added automatically before existing
>>>>>>>> 'squid' rules.
>>>>>>> 
>>>>>>> This message does unfortunately not say why this add-on would be
>>>>>>> useful. I am emphasising this again and again that it is not very
>>>>>>> important how something is done specially. That should be
>>>>>>> commented in the code and other implementation details should
>>>>>>> also be documented there.
>>>>>>> 
>>>>>>> As I have stated on this functionality many times before, I do
>>>>>>> not see why this is necessary at all.
>>>>>>> 
>>>>>>> Why is this an add-on?
>>>>>>> 
>>>>>>> Why do we not extend the firewall UI probably by about 20 lines
>>>>>>> of code instead of adding many hundreds of lines?
>>>>>>> 
>>>>>>> Please can someone elaborate on this more?
>>>>>>> 
>>>>>>> -Michael
>>>>>>> 
>>>>>>>> Signed-off-by: Matthias Fischer <matthias.fischer(a)ipfire.org>
>>>>>>>> ---
>>>>>>>> config/portredir/EX-portredir.menu    |   6 +
>>>>>>>> config/portredir/lang/portredir.de.pl |  19 +
>>>>>>>> config/portredir/lang/portredir.en.pl |  19 +
>>>>>>>> config/portredir/portredir-backup     |   1 +
>>>>>>>> config/portredir/portredir.cgi        | 525
>>>>>>>> ++++++++++++++++++++++++++
>>>>>>>> config/rootfiles/common/misc-progs    |   1 +
>>>>>>>> config/rootfiles/packages/portredir   |  11 +
>>>>>>>> lfs/portredir                         |  85 +++++
>>>>>>>> make.sh                               |   1 +
>>>>>>>> src/initscripts/packages/portredir    | 191 ++++++++++
>>>>>>>> src/misc-progs/Makefile               |   2 +-
>>>>>>>> src/misc-progs/portredirctrl.c        |  47 +++
>>>>>>>> src/paks/portredir/install.sh         |  32 ++
>>>>>>>> src/paks/portredir/uninstall.sh       |  28 ++
>>>>>>>> src/paks/portredir/update.sh          |  26 ++
>>>>>>>> 15 files changed, 993 insertions(+), 1 deletion(-)
>>>>>>>> create mode 100644 config/portredir/EX-portredir.menu
>>>>>>>> create mode 100644 config/portredir/lang/portredir.de.pl
>>>>>>>> create mode 100644 config/portredir/lang/portredir.en.pl
>>>>>>>> create mode 100644 config/portredir/portredir-backup
>>>>>>>> create mode 100644 config/portredir/portredir.cgi
>>>>>>>> create mode 100644 config/rootfiles/packages/portredir
>>>>>>>> create mode 100644 lfs/portredir
>>>>>>>> create mode 100644 src/initscripts/packages/portredir
>>>>>>>> create mode 100644 src/misc-progs/portredirctrl.c
>>>>>>>> create mode 100644 src/paks/portredir/install.sh
>>>>>>>> create mode 100644 src/paks/portredir/uninstall.sh
>>>>>>>> create mode 100644 src/paks/portredir/update.sh
>>>>>>>> 
>>>>>>>> diff --git a/config/portredir/EX-portredir.menu
>>>>>>>> b/config/portredir/EX-portredir.menu
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..8376e8053
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/EX-portredir.menu
>>>>>>>> @@ -0,0 +1,6 @@
>>>>>>>> +    $subfirewall->{'95.portredir'} = {
>>>>>>>> +                               'caption' =>
>>>>>>>> $Lang::tr{'portredir port redirections'},
>>>>>>>> +                               'uri' => '/cgi-
>>>>>>>> bin/portredir.cgi',
>>>>>>>> +                               'title' =>
>>>>>>>> "$Lang::tr{'portredir port redirections'}",
>>>>>>>> +                               'enabled' => 1
>>>>>>>> +                               };
>>>>>>>> diff --git a/config/portredir/lang/portredir.de.pl
>>>>>>>> b/config/portredir/lang/portredir.de.pl
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..b932d4a85
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/lang/portredir.de.pl
>>>>>>>> @@ -0,0 +1,19 @@
>>>>>>>> +%tr = (
>>>>>>>> +%tr,
>>>>>>>> +'portredir enable addon' => 'Addon aktivieren',
>>>>>>>> +'portredir common settings' => 'Allgemeine Einstellungen',
>>>>>>>> +'portredir port redirections' => 'Portumleitungen',
>>>>>>>> +'portredir fw for interface' => 'Firewalloptionen für das
>>>>>>>> Interface',
>>>>>>>> +'portredir enable user redirections' => 'Aktiviere
>>>>>>>> benutzerdefinierte Portumleitungen',
>>>>>>>> +'portredir force local dns' => 'Erzwinge lokale DNS-Server',
>>>>>>>> +'portredir force local ntp' => 'Erzwinge lokale NTP-Server',
>>>>>>>> +'portredir custom redirections' => 'Benutzerdefinierte
>>>>>>>> Portumleitungen',
>>>>>>>> +'portredir remove rule' => 'Entferne Regel',
>>>>>>>> +'portredir add rule' => 'Hinzufügen',
>>>>>>>> +'portredir no entries' => 'Keine Einträge vorhanden.',
>>>>>>>> +'portredir invalid address' => 'Ungültige Host-Addresse.',
>>>>>>>> +'portredir empty input' => 'Fehlende Angabe: Bitte geben Sie
>>>>>>>> einen gültigen Host an.',
>>>>>>>> +'portredir save to activate' => 'Speichern, um Änderungen zu
>>>>>>>> aktivieren',
>>>>>>>> +);
>>>>>>>> +
>>>>>>>> +#EOF
>>>>>>>> diff --git a/config/portredir/lang/portredir.en.pl
>>>>>>>> b/config/portredir/lang/portredir.en.pl
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..f442f3eaa
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/lang/portredir.en.pl
>>>>>>>> @@ -0,0 +1,19 @@
>>>>>>>> +%tr = (
>>>>>>>> +%tr,
>>>>>>>> +'portredir enable addon' => 'Enable addon',
>>>>>>>> +'portredir common settings' => 'Common settings',
>>>>>>>> +'portredir port redirections' => 'Port redirections',
>>>>>>>> +'portredir fw for interface' => 'Firewall options for
>>>>>>>> interface',
>>>>>>>> +'portredir enable user redirections' => 'Enable user port
>>>>>>>> redirections',
>>>>>>>> +'portredir force local dns' => 'Enforce local DNS servers',
>>>>>>>> +'portredir force local ntp' => 'Enforce local NTP servers',
>>>>>>>> +'portredir custom redirections' => 'Custom port redirections',
>>>>>>>> +'portredir remove rule' => 'Remove rule',
>>>>>>>> +'portredir add rule' => 'Add new',
>>>>>>>> +'portredir no entries' => 'No entries at the moment.',
>>>>>>>> +'portredir invalid address' => 'Invalid host address.',
>>>>>>>> +'portredir empty input' => 'Empty input: Please enter a valid
>>>>>>>> host.',
>>>>>>>> +'portredir save to activate' => 'Save to activate changes',
>>>>>>>> +);
>>>>>>>> +
>>>>>>>> +#EOF
>>>>>>>> diff --git a/config/portredir/portredir-backup
>>>>>>>> b/config/portredir/portredir-backup
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..bd2ada742
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/portredir-backup
>>>>>>>> @@ -0,0 +1 @@
>>>>>>>> +/var/ipfire/portredir
>>>>>>>> diff --git a/config/portredir/portredir.cgi
>>>>>>>> b/config/portredir/portredir.cgi
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..4913dda3f
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/portredir/portredir.cgi
>>>>>>>> @@ -0,0 +1,525 @@
>>>>>>>> +#!/usr/bin/perl
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# IPFire.org - A linux based
>>>>>>>> firewall                                         #
>>>>>>>> +# Copyright (C) 2021  IPFire Team 
>>>>>>>> <info(a)ipfire.org>                          #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# 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 3 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 <
>>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +use strict;
>>>>>>>> +
>>>>>>>> +# enable only the following on debugging purpose
>>>>>>>> +use warnings;
>>>>>>>> +use CGI::Carp 'fatalsToBrowser';
>>>>>>>> +
>>>>>>>> +require '/var/ipfire/general-functions.pl';
>>>>>>>> +require "${General::swroot}/lang.pl";
>>>>>>>> +require "${General::swroot}/header.pl";
>>>>>>>> +
>>>>>>>> +# File declarations
>>>>>>>> +my $settingsfile = "${General::swroot}/portredir/settings";
>>>>>>>> +my $redirectsfile = "${General::swroot}/portredir/redirects";
>>>>>>>> +
>>>>>>>> +# Create empty settingsfiles if they does not exist yet
>>>>>>>> +unless (-e "$settingsfile") { system ("touch $settingsfile");
>>>>>>>> }
>>>>>>>> +unless (-e "$redirectsfile") { system ("touch
>>>>>>>> $redirectsfile"); }
>>>>>>>> +
>>>>>>>> +# load ipfire settings
>>>>>>>> +our %netsettings = ();
>>>>>>>> +our %color = ();
>>>>>>>> +&General::readhash("${General::swroot}/ethernet/settings",
>>>>>>>> \%netsettings);
>>>>>>>> +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include
>>>>>>>> /colors.txt", \%color);
>>>>>>>> +
>>>>>>>> +my %settings=();
>>>>>>>> +my %portredirs=();
>>>>>>>> +my %checked=(); # Checkbox manipulations
>>>>>>>> +my $errormessage='';
>>>>>>>> +my %selected=();
>>>>>>>> +our %redirects=();
>>>>>>>> +
>>>>>>>> +$settings{'ACTION'} = '';
>>>>>>>> +$settings{'REDIR_ENABLE_ADDON'}="off";
>>>>>>>> +$settings{'REDIR_CUSTOM_GREEN'}="off";
>>>>>>>> +$settings{'REDIR_CUSTOM_BLUE'}="off";
>>>>>>>> +$settings{'REDIR_CUSTOM_ORANGE'}="off";
>>>>>>>> +$settings{'REDIR_DNS_GREEN'}="off";
>>>>>>>> +$settings{'REDIR_NTP_GREEN'}="off";
>>>>>>>> +$settings{'REDIR_DNS_BLUE'}="off";
>>>>>>>> +$settings{'REDIR_NTP_BLUE'}="off";
>>>>>>>> +$settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>>> +$settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>>> +
>>>>>>>> +&Header::showhttpheaders();
>>>>>>>> +
>>>>>>>> +# Get GUI values
>>>>>>>> +&Header::getcgihash(\%settings);
>>>>>>>> +
>>>>>>>> +# Save action
>>>>>>>> +if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
>>>>>>>> +
>>>>>>>> +       # If custom rules enabled, deactivate default rules on
>>>>>>>> interface
>>>>>>>> +       if ($settings{'REDIR_CUSTOM_GREEN'} eq "on" ) {
>>>>>>>> +               $settings{'REDIR_DNS_GREEN'}="off";
>>>>>>>> +               $settings{'REDIR_NTP_GREEN'}="off";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       if ($settings{'REDIR_CUSTOM_BLUE'} eq "on" ) {
>>>>>>>> +               $settings{'REDIR_DNS_BLUE'}="off";
>>>>>>>> +               $settings{'REDIR_NTP_BLUE'}="off";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       if ($settings{'REDIR_CUSTOM_ORANGE'} eq "on" ) {
>>>>>>>> +               $settings{'REDIR_DNS_ORANGE'}="off";
>>>>>>>> +               $settings{'REDIR_NTP_ORANGE'}="off";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       &General::writehash($settingsfile, \%settings);
>>>>>>>> +
>>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "on") {
>>>>>>>> +               system ('/usr/local/bin/portredirctrl restart
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               system ('/usr/local/bin/portredirctrl enable
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               &General::log('portredir addon: port
>>>>>>>> redirections enabled');
>>>>>>>> +       }
>>>>>>>> +       if ($settings{'REDIR_ENABLE_ADDON'} eq "off") {
>>>>>>>> +               system ('/usr/local/bin/portredirctrl disable
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               system ('/usr/local/bin/portredirctrl stop
>>>>>>>>> /dev/null 2>&1');
>>>>>>>> +               &General::log('portredir addon: port
>>>>>>>> redirections disabled');
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +# Add/edit an entry to the redirectsfile.
>>>>>>>> +
>>>>>>>> +} elsif (($settings{'ACTION'} eq $Lang::tr{'add'}) ||
>>>>>>>> ($settings{'ACTION'} eq $Lang::tr{'update'})) {
>>>>>>>> +
>>>>>>>> +       # Check if any input has been performed.
>>>>>>>> +       if ($settings{'REDIR_ENTRY_ADDRESS'} ne '') {
>>>>>>>> +
>>>>>>>> +               # Check if the given input is no valid IP-
>>>>>>>> address, display an error message.
>>>>>>>> +               if
>>>>>>>> (!&General::validip($settings{'REDIR_ENTRY_ADDRESS'}))  {
>>>>>>>> +                       $errormessage = "$Lang::tr{'portredir
>>>>>>>> invalid address'}";
>>>>>>>> +               }
>>>>>>>> +       } else {
>>>>>>>> +               $errormessage = "$Lang::tr{'portredir empty
>>>>>>>> input'}";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       # Go further if there was no error.
>>>>>>>> +       if ($errormessage eq '') {
>>>>>>>> +               my %redirects = ();
>>>>>>>> +               my $id;
>>>>>>>> +               my $status;
>>>>>>>> +
>>>>>>>> +               # Assign hash values.
>>>>>>>> +               my $new_entry_interface =
>>>>>>>> $settings{'REDIR_ENTRY_INTERFACE'};
>>>>>>>> +               my $new_entry_protocol =
>>>>>>>> $settings{'REDIR_ENTRY_PROTOCOL'};
>>>>>>>> +               my $new_entry_port =
>>>>>>>> $settings{'REDIR_ENTRY_PORT'};
>>>>>>>> +               my $new_entry_address =
>>>>>>>> $settings{'REDIR_ENTRY_ADDRESS'};
>>>>>>>> +               my $new_entry_remark =
>>>>>>>> $settings{'REDIR_ENTRY_REMARK'};
>>>>>>>> +
>>>>>>>> +               # Read-in redirectsfile.
>>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +
>>>>>>>> +               # Check if we should edit an existing entry and
>>>>>>>> got an ID.
>>>>>>>> +               if (($settings{'ACTION'} eq
>>>>>>>> $Lang::tr{'update'}) && ($settings{'ID'})) {
>>>>>>>> +                       # Assin the provided id.
>>>>>>>> +                       $id = $settings{'ID'};
>>>>>>>> +
>>>>>>>> +                       # Undef the given ID.
>>>>>>>> +                       undef($settings{'ID'});
>>>>>>>> +
>>>>>>>> +                       # Grab the configured status of the
>>>>>>>> corresponding entry.
>>>>>>>> +                       $status = $redirects{$id}[4];
>>>>>>>> +               } else {
>>>>>>>> +                       # Each newly added entry automatically
>>>>>>>> should be enabled.
>>>>>>>> +                       $status = "enabled";
>>>>>>>> +
>>>>>>>> +                       # Generate the ID for the new entry.
>>>>>>>> +                       #
>>>>>>>> +                       # Sort the keys by their ID and store
>>>>>>>> them in an array.
>>>>>>>> +                       my @keys = sort { $a <=> $b } keys
>>>>>>>> %redirects;
>>>>>>>> +
>>>>>>>> +                       # Reverse the key array.
>>>>>>>> +                       my @reversed = reverse(@keys);
>>>>>>>> +
>>>>>>>> +                       # Obtain the last used id.
>>>>>>>> +                       my $last_id = @reversed[0];
>>>>>>>> +
>>>>>>>> +                       # Increase the last id by one and use
>>>>>>>> it as id for the new entry.
>>>>>>>> +                       $id = ++$last_id;
>>>>>>>> +               }
>>>>>>>> +
>>>>>>>> +               # Add/Modify the entry to/in the redirects
>>>>>>>> hash.
>>>>>>>> +               $redirects{$id} = ["$new_entry_interface",
>>>>>>>> "$new_entry_protocol", "$new_entry_port",
>>>>>>>> "$new_entry_address","$status", "$new_entry_remark"];
>>>>>>>> +
>>>>>>>> +               # Write the changed redirects hash to the
>>>>>>>> redirects file.
>>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +# Toggle Enabled/Disabled for an existing entry on the
>>>>>>>> redirects list.
>>>>>>>> +
>>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'toggle enable
>>>>>>>> disable'}) {
>>>>>>>> +       my %redirects = ();
>>>>>>>> +
>>>>>>>> +       # Only go further, if an ID has been passed.
>>>>>>>> +       if ($settings{'ID'}) {
>>>>>>>> +               # Assign the given ID.
>>>>>>>> +               my $id = $settings{'ID'};
>>>>>>>> +
>>>>>>>> +               # Undef the given ID.
>>>>>>>> +               undef($settings{'ID'});
>>>>>>>> +
>>>>>>>> +               # Read-in ignoredfile.
>>>>>>>> +               &General::readhasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +
>>>>>>>> +               # Grab the configured status of the
>>>>>>>> corresponding entry.
>>>>>>>> +               my $status = $redirects{$id}[4];
>>>>>>>> +
>>>>>>>> +               # Switch the status.
>>>>>>>> +               if ($status eq "disabled") {
>>>>>>>> +                       $status = "enabled";
>>>>>>>> +               } else {
>>>>>>>> +                       $status = "disabled";
>>>>>>>> +               }
>>>>>>>> +
>>>>>>>> +               # Modify the status of the existing entry.
>>>>>>>> +               $redirects{$id} = ["$redirects{$id}[0]",
>>>>>>>> "$redirects{$id}[1]", "$redirects{$id}[2]",
>>>>>>>> "$redirects{$id}[3]","$status", "$redirects{$id}[5]"];
>>>>>>>> +
>>>>>>>> +               # Write the changed ignored hash to the
>>>>>>>> redirects file.
>>>>>>>> +               &General::writehasharray($redirectsfile,
>>>>>>>> \%redirects);
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +# Remove entry from redirects list.
>>>>>>>> +
>>>>>>>> +} elsif ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
>>>>>>>> +       my %redirects = ();
>>>>>>>> +
>>>>>>>> +       # Read-in redirectsfile.
>>>>>>>> +       &General::readhasharray($redirectsfile, \%redirects);
>>>>>>>> +
>>>>>>>> +       # move data on key up
>>>>>>>> +       foreach my $key (sort keys %redirects) {
>>>>>>>> +               if ($key >= $settings{'ID'}) {
>>>>>>>> +                       my $next = $key + 1;
>>>>>>>> +                       if (exists $redirects{$next}) {
>>>>>>>> +                               foreach my $i (0 ..
>>>>>>>> $#{$redirects{$next}}) { $redirects{$key}[$i] =
>>>>>>>> $redirects{$next}[$i]; }
>>>>>>>> +                       }
>>>>>>>> +               }
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       my $last_key = (sort {$a <=> $b} keys %redirects)[-1];
>>>>>>>> +       delete $redirects{$last_key};
>>>>>>>> +
>>>>>>>> +       # Undef the given ID.
>>>>>>>> +       undef($settings{'ID'});
>>>>>>>> +
>>>>>>>> +       # Write the changed redirects hash to file.
>>>>>>>> +       &General::writehasharray($redirectsfile, \%redirects);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +# Load settings from file
>>>>>>>> +&General::readhash($settingsfile, \%settings);
>>>>>>>> +&General::readhasharray($redirectsfile, \%redirects);
>>>>>>>> +
>>>>>>>> +# Call functions to generate whole page.
>>>>>>>> +&Header::openpage($Lang::tr{'portredir port redirections'}, 1,
>>>>>>>> '');
>>>>>>>> +&Header::openbigbox('100%', 'left', '', $errormessage);
>>>>>>>> +
>>>>>>>> +if ($errormessage) {
>>>>>>>> +        &Header::openbox('100%', 'left', $Lang::tr{'warning
>>>>>>>> messages'});
>>>>>>>> +        print "<font color='red'>$errormessage&nbsp;</font>";
>>>>>>>> +        &Header::closebox();
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_ENABLE_ADDON'}{$settings{'REDIR_ENABLE_ADDON'}
>>>>>>>> } = "checked='checked'";
>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_GREEN'}{$settings{'REDIR_CUSTOM_GREEN'}
>>>>>>>> } = "checked='checked'";
>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_BLUE'}{$settings{'REDIR_CUSTOM_BLUE'}}
>>>>>>>> = "checked='checked'";
>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_CUSTOM_ORANGE'}{$settings{'REDIR_CUSTOM_ORANGE
>>>>>>>> '}} = "checked='checked'";
>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_DNS_GREEN'}{$settings{'REDIR_DNS_GREEN'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_NTP_GREEN'}{$settings{'REDIR_NTP_GREEN'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_DNS_BLUE'}{$settings{'REDIR_DNS_BLUE'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_NTP_BLUE'}{$settings{'REDIR_NTP_BLUE'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_DNS_ORANGE'}{$settings{'REDIR_DNS_ORANGE'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'off'} = '';
>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{'on'} = '';
>>>>>>>> +$checked{'REDIR_NTP_ORANGE'}{$settings{'REDIR_NTP_ORANGE'}} =
>>>>>>>> "checked='checked'";
>>>>>>>> +
>>>>>>>> +$selected{'REDIR_ENTRY_INTERFACE'}{$settings{'REDIR_ENTRY_INTE
>>>>>>>> RFACE'}} = 'selected';
>>>>>>>> +$selected{'REDIR_ENTRY_PROTOCOL'}{$settings{'REDIR_ENTRY_PROTO
>>>>>>>> COL'}} = 'selected';
>>>>>>>> +
>>>>>>>> +&showMainBox();
>>>>>>>> +&showRedirectsBox();
>>>>>>>> +
>>>>>>>> +&Header::closebigbox();
>>>>>>>> +&Header::closepage();
>>>>>>>> +
>>>>>>>> +# Function to show main settings and options.
>>>>>>>> +sub showMainBox() {
>>>>>>>> +
>>>>>>>> +       &Header::openbox('100%', 'center',
>>>>>>>> "$Lang::tr{'settings'}");
>>>>>>>> +       print "<form method='post'
>>>>>>>> action='$ENV{'SCRIPT_NAME'}'>";
>>>>>>>> +
>>>>>>>> +print <<END;
>>>>>>>> +<table width='80%' cellspacing='0' border='0'>
>>>>>>>> +       <tr><td colspan='2' class='base'
>>>>>>>> bgcolor='$color{'color20'}'><b>$Lang::tr{'portredir common
>>>>>>>> settings'}</b></td></tr
>>>>>>>> +       <tr>
>>>>>>>> +               <td width='25%'
>>>>>>>> class='base'>$Lang::tr{'portredir enable addon'}:</td>
>>>>>>>> +               <td><input type='checkbox'
>>>>>>>> name='REDIR_ENABLE_ADDON' $checked{'REDIR_ENABLE_ADDON'}{'on'}
>>>>>>>> /></td>
>>>>>>>> +       </tr>
>>>>>>>> +       <tr><td colspan='2'></td></tr>
>>>>>>>> +       <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>>> +END
>>>>>>>> +
>>>>>>>> +       # create html table with header line 1
>>>>>>>> +       print "<table width='80%' cellspacing='0'
>>>>>>>> border='0'><tr>";
>>>>>>>> +       print "<th class='base' width='40%'
>>>>>>>> align='left'><b>$Lang::tr{'portredir fw for interface'}</th>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<th
>>>>>>>> class='base' width='10%'><b><font
>>>>>>>> color=green>$Lang::tr{'green'}</font></th>";
>>>>>>>> +               } else {                 print "<th
>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<th
>>>>>>>> class='base' width='10%'><b><font
>>>>>>>> color=blue>$Lang::tr{'blue'}</font></th>";
>>>>>>>> +               } else {                 print "<th
>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<th
>>>>>>>> class='base' width='10%'><b><font
>>>>>>>> color=orange>$Lang::tr{'orange'}</font></th>";
>>>>>>>> +               } else {                 print "<th
>>>>>>>> class='base' width='10%'></font></th>"; }
>>>>>>>> +
>>>>>>>> +       # the empty right row
>>>>>>>> +       print "<th class='base'
>>>>>>>> width='30%'><td></td></th></tr>";
>>>>>>>> +
>>>>>>>> +       # line 2
>>>>>>>> +       print "<tr><td>$Lang::tr{'portredir force local
>>>>>>>> dns'}</td>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_DNS_GREEN'
>>>>>>>> $checked{'REDIR_DNS_GREEN'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_DNS_BLUE' $checked{'REDIR_DNS_BLUE'}{'on'}></td>";}
>>>>>>>> else { print "<td></td>";}
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_DNS_ORANGE'
>>>>>>>> $checked{'REDIR_DNS_ORANGE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +
>>>>>>>> +       # line 3
>>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir force local
>>>>>>>> ntp'}</td>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_NTP_GREEN'
>>>>>>>> $checked{'REDIR_NTP_GREEN'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_NTP_BLUE' $checked{'REDIR_NTP_BLUE'}{'on'}></td>";}
>>>>>>>> else { print "<td></td>";}
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_NTP_ORANGE'
>>>>>>>> $checked{'REDIR_NTP_ORANGE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +
>>>>>>>> +       # line 4
>>>>>>>> +       print "</tr><tr><td>$Lang::tr{'portredir enable user
>>>>>>>> redirections'}</td>";
>>>>>>>> +       if ($netsettings{'GREEN_DEV'})  {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_CUSTOM_GREEN'
>>>>>>>> $checked{'REDIR_CUSTOM_GREEN'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'BLUE_DEV'})   {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_CUSTOM_BLUE'
>>>>>>>> $checked{'REDIR_CUSTOM_BLUE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +       if ($netsettings{'ORANGE_DEV'}) {print "<td
>>>>>>>> class='base' align='center'><input type='checkbox'
>>>>>>>> name='REDIR_CUSTOM_ORANGE'
>>>>>>>> $checked{'REDIR_CUSTOM_ORANGE'}{'on'}></td>";} else { print
>>>>>>>> "<td></td>";}
>>>>>>>> +
>>>>>>>> +       print <<END;
>>>>>>>> +       </tr></table>
>>>>>>>> +       <table width='80%' cellspacing='0' border='0'>
>>>>>>>> +               <tr><td colspan='2'>&nbsp;</td></tr>
>>>>>>>> +               <tr><td align='left'><b>$Lang::tr{'portredir
>>>>>>>> save to activate'}</b></td><td width='5%' align='center'><input
>>>>>>>> type='submit' name='ACTION' value='  $Lang::tr{'save'} 
>>>>>>>> '></td></tr>
>>>>>>>> +       </table></form>
>>>>>>>> +END
>>>>>>>> +
>>>>>>>> +&Header::closebox();
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +# Function to show elements of the redirects file and allow to
>>>>>>>> add or remove single members of it.
>>>>>>>> +sub showRedirectsBox() {
>>>>>>>> +        &Header::openbox('100%', 'center',
>>>>>>>> "$Lang::tr{'portredir custom redirections'}");
>>>>>>>> +
>>>>>>>> +       print <<END;
>>>>>>>> +               <table width='80%' cellspacing='1' border='0'>
>>>>>>>> +                       <tr>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'interface'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'port'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}' align='center'><b>$Lang::tr{'ip
>>>>>>>> address'}</b></td>
>>>>>>>> +                               <td class='base'
>>>>>>>> bgcolor='$color{'color20'}'
>>>>>>>> align='center'><b>$Lang::tr{'remark'}</b></td>
>>>>>>>> +                               <td class='base' colspan='3'
>>>>>>>> bgcolor='$color{'color20'}'></td>
>>>>>>>> +                       </tr>
>>>>>>>> +END
>>>>>>>> +                       # Check if some rules have been added
>>>>>>>> to be redirects.
>>>>>>>> +                       if (keys (%redirects)) {
>>>>>>>> +                               my $col = "";
>>>>>>>> +
>>>>>>>> +                               # List all entries of the hash.
>>>>>>>> +                               foreach my $key (sort keys
>>>>>>>> %redirects){
>>>>>>>> +
>>>>>>>> +                                       # Assign data array
>>>>>>>> positions to some nice variable names.
>>>>>>>> +                                       my $interface =
>>>>>>>> $redirects{$key}[0];
>>>>>>>> +                                       my $protocol =
>>>>>>>> $redirects{$key}[1];
>>>>>>>> +                                       my $port  =
>>>>>>>> $redirects{$key}[2];
>>>>>>>> +                                       my $address =
>>>>>>>> $redirects{$key}[3];
>>>>>>>> +                                       my $status  =
>>>>>>>> $redirects{$key}[4];
>>>>>>>> +                                       my $remark  =
>>>>>>>> $redirects{$key}[5];
>>>>>>>> +
>>>>>>>> +                                       # Check if the key (id)
>>>>>>>> number is even or not.
>>>>>>>> +                                       if ($settings{'ID'} eq
>>>>>>>> $key) {
>>>>>>>> +                                               $col="bgcolor='
>>>>>>>> ${Header::colouryellow}'";
>>>>>>>> +                                       } elsif ($key % 2) {
>>>>>>>> +                                               $col="bgcolor='
>>>>>>>> $color{'color22'}'";
>>>>>>>> +                                       } else {
>>>>>>>> +                                               $col="bgcolor='
>>>>>>>> $color{'color20'}'";
>>>>>>>> +                                       }
>>>>>>>> +
>>>>>>>> +                                       # Choose icon for the
>>>>>>>> checkbox.
>>>>>>>> +                                       my $gif;
>>>>>>>> +                                       my $gdesc;
>>>>>>>> +
>>>>>>>> +                                       # Check if the status
>>>>>>>> is enabled and select the correct image and description.
>>>>>>>> +                                       if ($status eq
>>>>>>>> 'enabled' ) {
>>>>>>>> +                                               $gif =
>>>>>>>> 'on.gif';
>>>>>>>> +                                               $gdesc =
>>>>>>>> $Lang::tr{'click to disable'};
>>>>>>>> +                                       } else {
>>>>>>>> +                                               $gif =
>>>>>>>> 'off.gif';
>>>>>>>> +                                               $gdesc =
>>>>>>>> $Lang::tr{'click to enable'};
>>>>>>>> +                                       }
>>>>>>>> +
>>>>>>>> +                                       print <<END;
>>>>>>>> +                                       <tr>
>>>>>>>> +                                               <td width='15%'
>>>>>>>> class='base' align='center' $col><b><font
>>>>>>>> color=$interface>$Lang::tr{$interface}</font></b></td>
>>>>>>>> +                                               <td width='10%'
>>>>>>>> class='base' align='center' $col>$protocol</td>
>>>>>>>> +                                               <td width='10%'
>>>>>>>> class='base' align='center' $col>$port</td>
>>>>>>>> +                                               <td width='15%'
>>>>>>>> class='base' align='center' $col>&nbsp;$address</td>
>>>>>>>> +                                               <td width='40%'
>>>>>>>> class='base' align='center' $col>&nbsp;$remark</td>
>>>>>>>> +                                               <td
>>>>>>>> align='center' $col>
>>>>>>>> +                                                       <form
>>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ACTION' value='$Lang::tr{'toggle
>>>>>>>> enable disable'}' />
>>>>>>>> +                                                              
>>>>>>>> <input type='image' name='$Lang::tr{'toggle enable disable'}'
>>>>>>>> src='/images/$gif' alt='$gdesc' title='$gdesc' />
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ID' value='$key' />
>>>>>>>> +                                                       </form>
>>>>>>>> +                                               </td>
>>>>>>>> +                                               <td
>>>>>>>> align='center' $col>
>>>>>>>> +                                                       <form
>>>>>>>> method='post' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}'
>>>>>>>> />
>>>>>>>> +                                                              
>>>>>>>> <input type='image' name='$Lang::tr{'edit'}'
>>>>>>>> src='/images/edit.gif' alt='$Lang::tr{'edit'}'
>>>>>>>> title='$Lang::tr{'edit'}' />
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ID' value='$key' />
>>>>>>>> +                                                       </form>
>>>>>>>> +                                               </td>
>>>>>>>> +                                               <td
>>>>>>>> align='center' $col>
>>>>>>>> +                                                       <form
>>>>>>>> method='post' name='$key' action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                                                              
>>>>>>>> <input type='image' name='$Lang::tr{'remove'}'
>>>>>>>> src='/images/delete.gif' title='$Lang::tr{'remove'}'
>>>>>>>> alt='$Lang::tr{'remove'}'>
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ID' value='$key'>
>>>>>>>> +                                                              
>>>>>>>> <input type='hidden' name='ACTION'
>>>>>>>> value='$Lang::tr{'remove'}'>
>>>>>>>> +                                                       </form>
>>>>>>>> +                                               </td>
>>>>>>>> +                                       </tr>
>>>>>>>> +END
>>>>>>>> +                               }
>>>>>>>> +                       } else {
>>>>>>>> +                               # Print notice that currently
>>>>>>>> no ports are redirected.
>>>>>>>> +                               print "<tr>\n";
>>>>>>>> +                               print "<td class='base'
>>>>>>>> colspan='2'>$Lang::tr{'portredir no entries'}</td>\n";
>>>>>>>> +                               print "</tr>\n";
>>>>>>>> +                       }
>>>>>>>> +
>>>>>>>> +               print "</table>\n";
>>>>>>>> +
>>>>>>>> +       # Section to add new elements or edit existing ones.
>>>>>>>> +       print <<END;
>>>>>>>> +       <br>
>>>>>>>> +       <hr>
>>>>>>>> +       <br>
>>>>>>>> +       <div align='center'>
>>>>>>>> +               <table width='100%' cellspacing='0' border='0'>
>>>>>>>> +END
>>>>>>>> +
>>>>>>>> +       # Assign correct headline and button text.
>>>>>>>> +       my $buttontext;
>>>>>>>> +       my $entry_interface;
>>>>>>>> +       my $entry_protocol;
>>>>>>>> +       my $entry_port;
>>>>>>>> +       my $entry_address;
>>>>>>>> +       my $entry_remark;
>>>>>>>> +
>>>>>>>> +       # Check if an ID (key) has been given, in this case an
>>>>>>>> existing entry should be edited.
>>>>>>>> +       if ($settings{'ID'} ne '') {
>>>>>>>> +               $buttontext = $Lang::tr{'update'};
>>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>>> colspan='6'><b>$Lang::tr{'update'}</b></td></tr>\n";
>>>>>>>> +
>>>>>>>> +               # Grab address and remark for the given key.
>>>>>>>> +               $entry_interface =
>>>>>>>> $redirects{$settings{'ID'}}[0];
>>>>>>>> +               $entry_protocol =
>>>>>>>> $redirects{$settings{'ID'}}[1];
>>>>>>>> +               $entry_port = $redirects{$settings{'ID'}}[2];
>>>>>>>> +               $entry_address =
>>>>>>>> $redirects{$settings{'ID'}}[3];
>>>>>>>> +               $entry_remark = $redirects{$settings{'ID'}}[5];
>>>>>>>> +
>>>>>>>> +       } else {
>>>>>>>> +               $buttontext = $Lang::tr{'add'};
>>>>>>>> +               print "<tr><td class='boldbase'
>>>>>>>> colspan='11'><b>$Lang::tr{'dnsforward add a new
>>>>>>>> entry'}</b></td></tr>\n";
>>>>>>>> +               print "<tr><td>&nbsp</td></tr>\n";
>>>>>>>> +       }
>>>>>>>> +
>>>>>>>> +       print <<END;
>>>>>>>> +                       <tr>
>>>>>>>> +                               <td class='base' width='1%' 
>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>> +                               <td class='base' width='15%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'><b>$Lang::tr{'interface'}</b></td>
>>>>>>>> +                               <td class='base' width='10%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'><b>$Lang::tr{'protocol'}</b></td>
>>>>>>>> +                               <td class='base' width='10%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'port'}</b></td>
>>>>>>>> +                               <td class='base' width='13%'
>>>>>>>> bgcolor='$color{'color22'}' align='left'>&nbsp;<b>$Lang::tr{'ip
>>>>>>>> address'}</b></td>
>>>>>>>> +                               <td class='base' width='30%'
>>>>>>>> bgcolor='$color{'color22'}'
>>>>>>>> align='left'>&nbsp;<b>$Lang::tr{'remark'}</b></td>
>>>>>>>> +                               <td class='base' width='15%'
>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>> +                               <td class='base' width='1%' 
>>>>>>>> bgcolor='$color{'color22'}'></td>
>>>>>>>> +                       </tr>
>>>>>>>> +
>>>>>>>> +                       <form method='post'
>>>>>>>> action='$ENV{'SCRIPT_NAME'}'>
>>>>>>>> +                       <input type='hidden' name='ID'
>>>>>>>> value='$settings{'ID'}'>
>>>>>>>> +                       <tr>
>>>>>>>> +                               <td class='base'></td>
>>>>>>>> +                               <td><select style='width:90px;'
>>>>>>>> id='interface' name='REDIR_ENTRY_INTERFACE'>
>>>>>>>> +END
>>>>>>>> +                               if ($netsettings{'GREEN_DEV'})
>>>>>>>> {
>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>> "green") {
>>>>>>>> +                                               print "<option
>>>>>>>> value='green' selected='selected'
>>>>>>>> {'green'}>$Lang::tr{'green'}</option>";
>>>>>>>> +                                       } else { print "<option
>>>>>>>> value='green' {'green'}>$Lang::tr{'green'}</option>";}
>>>>>>>> +                               }
>>>>>>>> +                               if ($netsettings{'BLUE_DEV'}) {
>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>> "blue") { 
>>>>>>>> +                                               print "<option
>>>>>>>> value='blue' selected='selected'
>>>>>>>> {'blue'}>$Lang::tr{'blue'}</option>";
>>>>>>>> +                                       } else { print "<option
>>>>>>>> value='blue' {'blue'}>$Lang::tr{'blue'}</option>";}
>>>>>>>> +                               }
>>>>>>>> +                               if ($netsettings{'ORANGE_DEV'})
>>>>>>>> {
>>>>>>>> +                                       if ($entry_interface eq
>>>>>>>> "orange") { 
>>>>>>>> +                                               print "<option
>>>>>>>> value='orange' selected='selected'
>>>>>>>> {'orange'}>$Lang::tr{'orange'}</option>";
>>>>>>>> +                                       } else { print "<option
>>>>>>>> value='orange' {'orange'}>$Lang::tr{'orange'}</option>";}
>>>>>>>> +                               }
>>>>>>>> +
>>>>>>>> +                       print "</select><td><select
>>>>>>>> style='width:50px;' name='REDIR_ENTRY_PROTOCOL'>";
>>>>>>>> +                       if ((!$entry_protocol) ||
>>>>>>>> ($entry_protocol eq "tcp")) {
>>>>>>>> +                               print "<option
>>>>>>>> selected='selected' id='protocol' value='tcp'
>>>>>>>> {'tcp'}>tcp</option>";
>>>>>>>> +                               print "<option value='udp'
>>>>>>>> {'udp'}>udp</option>";
>>>>>>>> +                       } elsif ($entry_protocol eq "udp") {
>>>>>>>> +                               print "<option value='tcp'
>>>>>>>> {'tcp'}>tcp</option>";
>>>>>>>> +                               print "<option
>>>>>>>> selected='selected' value='udp' {'udp'}>udp</option>";
>>>>>>>> +                       }
>>>>>>>> +       print <<END;
>>>>>>>> +                               </select></td>
>>>>>>>> +                               <td><input type='text'
>>>>>>>> name='REDIR_ENTRY_PORT'    value='$entry_port'   
>>>>>>>> size='4'></td>
>>>>>>>> +                               <td><input type='text'
>>>>>>>> name='REDIR_ENTRY_ADDRESS' value='$entry_address'
>>>>>>>> size='14'></td>
>>>>>>>> +                               <td><input type='text'
>>>>>>>> name='REDIR_ENTRY_REMARK'  value='$entry_remark' 
>>>>>>>> size='35'></td>
>>>>>>>> +                               <td width='10%'
>>>>>>>> align='center'><input type='submit' name='ACTION' value=' 
>>>>>>>> $buttontext  '></td>
>>>>>>>> +                               <td class='base'></td>
>>>>>>>> +                       </tr>
>>>>>>>> +                       </form>
>>>>>>>> +               </table>
>>>>>>>> +       </div>
>>>>>>>> +END
>>>>>>>> +       &Header::closebox();
>>>>>>>> +}
>>>>>>>> diff --git a/config/rootfiles/common/misc-progs
>>>>>>>> b/config/rootfiles/common/misc-progs
>>>>>>>> index d6594b3f8..fbad2af8b 100644
>>>>>>>> --- a/config/rootfiles/common/misc-progs
>>>>>>>> +++ b/config/rootfiles/common/misc-progs
>>>>>>>> @@ -17,6 +17,7 @@ usr/local/bin/logwatch
>>>>>>>> #usr/local/bin/mpfirectrl
>>>>>>>> usr/local/bin/openvpnctrl
>>>>>>>> usr/local/bin/pakfire
>>>>>>>> +#usr/local/bin/portredirctrl
>>>>>>>> usr/local/bin/qosctrl
>>>>>>>> usr/local/bin/rebuildhosts
>>>>>>>> usr/local/bin/rebuildroutes
>>>>>>>> diff --git a/config/rootfiles/packages/portredir
>>>>>>>> b/config/rootfiles/packages/portredir
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..4b4ba8366
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/config/rootfiles/packages/portredir
>>>>>>>> @@ -0,0 +1,11 @@
>>>>>>>> +etc/rc.d/init.d/portredir
>>>>>>>> +etc/rc.d/rc0.d/K77portredir
>>>>>>>> +etc/rc.d/rc3.d/S23portredir
>>>>>>>> +etc/rc.d/rc6.d/K77portredir
>>>>>>>> +srv/web/ipfire/cgi-bin/portredir.cgi
>>>>>>>> +usr/local/bin/portredirctrl
>>>>>>>> +var/ipfire/addon-lang/portredir.de.pl
>>>>>>>> +var/ipfire/addon-lang/portredir.en.pl
>>>>>>>> +var/ipfire/backup/addons/includes/portredir
>>>>>>>> +var/ipfire/menu.d/EX-portredir.menu
>>>>>>>> +var/ipfire/portredir
>>>>>>>> diff --git a/lfs/portredir b/lfs/portredir
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..a4911f71f
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/lfs/portredir
>>>>>>>> @@ -0,0 +1,85 @@
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# IPFire.org - A linux based
>>>>>>>> firewall                                         #
>>>>>>>> +# Copyright (C) 2007-2021  IPFire Team 
>>>>>>>> <info(a)ipfire.org>                     #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +# 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 3 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 <
>>>>>>>> http://www.gnu.org/licenses/>.       #
>>>>>>>> +#                                                             
>>>>>>>>                #
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +# Definitions
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +include Config
>>>>>>>> +
>>>>>>>> +VER        = 1.0
>>>>>>>> +
>>>>>>>> +THISAPP    = portredir-$(VER)
>>>>>>>> +DIR_APP    = $(DIR_SRC)/$(THISAPP)
>>>>>>>> +TARGET     = $(DIR_INFO)/$(THISAPP)
>>>>>>>> +PROG       = portredir
>>>>>>>> +PAK_VER    = 1
>>>>>>>> +
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +# Top-level Rules
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +install : $(TARGET)
>>>>>>>> +
>>>>>>>> +check :
>>>>>>>> +
>>>>>>>> +download :
>>>>>>>> +
>>>>>>>> +md5 :
>>>>>>>> +
>>>>>>>> +dist: 
>>>>>>>> +       @$(PAK)
>>>>>>>> +
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +# Installation Details
>>>>>>>> +##############################################################
>>>>>>>> #################
>>>>>>>> +
>>>>>>>> +$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>>>>>>>> +       @$(PREBUILD)
>>>>>>>> +       @rm -rf $(DIR_APP) && cd $(DIR_SRC)
>>>>>>>> +
>>>>>>>> +       #install cgi 
>>>>>>>> +       install -v -m 755 $(DIR_CONF)/portredir/portredir.cgi
>>>>>>>> /srv/web/ipfire/cgi-bin/
>>>>>>>> +
>>>>>>>> +       #create configuration dir 
>>>>>>>> +       -mkdir -pv /var/ipfire/portredir/
>>>>>>>> +       chown -R nobody:nobody /var/ipfire/portredir/
>>>>>>>> +
>>>>>>>> +       # Install include file for backup
>>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/portredir-
>>>>>>>> backup /var/ipfire/backup/addons/includes/portredir
>>>>>>>> +
>>>>>>>> +       # Install menu file
>>>>>>>> +       install -v -m 644 $(DIR_CONF)/portredir/EX-
>>>>>>>> portredir.menu /var/ipfire/menu.d/
>>>>>>>> +       chown nobody:nobody /var/ipfire/menu.d/EX-
>>>>>>>> portredir.menu
>>>>>>>> +
>>>>>>>> +       # Install addon-specific language-files
>>>>>>>> +       install -v -m 644
>>>>>>>> $(DIR_CONF)/portredir/lang/portredir.*.pl /var/ipfire/addon-
>>>>>>>> lang/
>>>>>>>> +
>>>>>>>> +       #install initscripts
>>>>>>>> +       $(call INSTALL_INITSCRIPT,portredir)
>>>>>>>> +
>>>>>>>> +       # Create symlinks for runlevel interaction.
>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc3.d/S23portredir
>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc0.d/K77portredir
>>>>>>>> +       ln -svf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc6.d/K77portredir
>>>>>>>> +
>>>>>>>> +       @rm -rf $(DIR_APP)
>>>>>>>> +       @$(POSTBUILD)
>>>>>>>> +
>>>>>>>> diff --git a/make.sh b/make.sh
>>>>>>>> index fc03ebcd5..ab9fe881a 100755
>>>>>>>> --- a/make.sh
>>>>>>>> +++ b/make.sh
>>>>>>>> @@ -1623,6 +1623,7 @@ buildipfire() {
>>>>>>>> lfsmake2 socat
>>>>>>>> lfsmake2 libcdada
>>>>>>>> lfsmake2 pmacct
>>>>>>>> +  lfsmake2 portredir
>>>>>>>> }
>>>>>>>> 
>>>>>>>> buildinstaller() {
>>>>>>>> diff --git a/src/initscripts/packages/portredir
>>>>>>>> b/src/initscripts/packages/portredir
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..cc57fb9cc
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/initscripts/packages/portredir
>>>>>>>> @@ -0,0 +1,191 @@
>>>>>>>> +#!/bin/sh
>>>>>>>> +##############################################################
>>>>>>>> ##########
>>>>>>>> +# Begin $rc_base/init.d/portredir
>>>>>>>> +#
>>>>>>>> +# Description : portredir init script for DNS/NTP and custom 
>>>>>>>> +#              port redirection rules
>>>>>>>> +#
>>>>>>>> +##############################################################
>>>>>>>> ##########
>>>>>>>> +
>>>>>>>> +. /etc/sysconfig/rc
>>>>>>>> +. ${rc_functions}
>>>>>>>> +
>>>>>>>> +IPT="/sbin/iptables";
>>>>>>>> +parent_chain="PREROUTING";
>>>>>>>> +chain="PORT_REDIRECT";
>>>>>>>> +
>>>>>>>> +confdir="/var/ipfire/portredir";
>>>>>>>> +settingsfile="${confdir}/settings";
>>>>>>>> +redirectsfile="${confdir}/redirects";
>>>>>>>> +SYSLOG="NO";
>>>>>>>> +VERBOSE="NO";
>>>>>>>> +
>>>>>>>> +eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings);
>>>>>>>> +eval $(/usr/local/bin/readhash ${settingsfile});
>>>>>>>> +
>>>>>>>> +logtext() {
>>>>>>>> +       if [ "${SYSLOG}" = "YES" ]; then logger -t "portredir"
>>>>>>>> ${1}; fi;
>>>>>>>> +       if [ "${VERBOSE}" = "YES" ]; then echo ${1}; fi;}
>>>>>>>> +
>>>>>>>> +create_chain() {
>>>>>>>> +
>>>>>>>> +       local line=$(${IPT} -t nat -L ${parent_chain} --line-
>>>>>>>> numbers |grep "SQUID" |awk '{printf($1)}');
>>>>>>>> +
>>>>>>>> +       if [[ "${REDIR_ENABLE_ADDON}" == "off" || -z
>>>>>>>> "${REDIR_ENABLE_ADDON}" ]]; then
>>>>>>>> +               logtext "addon not enabled in web
>>>>>>>> interface...";
>>>>>>>> +               echo "Portredir addon not enabled in web
>>>>>>>> interface...";
>>>>>>>> +               exit 0;
>>>>>>>> +       fi;
>>>>>>>> +
>>>>>>>> +       if [ -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>>> ${chain})" ]; then
>>>>>>>> +               ${IPT} -t nat -N ${chain};
>>>>>>>> +
>>>>>>>> +               if [ ! -z "${line}" ]; then
>>>>>>>> +                       logtext "create chain ${chain} and link
>>>>>>>> in ${parent_chain} at position ${line}...";
>>>>>>>> +                       ${IPT} -t nat -I ${parent_chain}
>>>>>>>> ${line} -j ${chain};
>>>>>>>> +               else
>>>>>>>> +                       logtext "create chain ${chain} and link
>>>>>>>> in ${parent_chain} at last position...";
>>>>>>>> +                       ${IPT} -t nat -A ${parent_chain} -j
>>>>>>>> ${chain};
>>>>>>>> +               fi
>>>>>>>> +       else
>>>>>>>> +               return 1;
>>>>>>>> +       fi;
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +remove_chain() {
>>>>>>>> +       if [ ! -z "$(${IPT} -t nat -L ${parent_chain} |grep
>>>>>>>> ${chain})" ]; then
>>>>>>>> +               logtext "remove chain ${chain} and link in
>>>>>>>> ${parent_chain} from system...";
>>>>>>>> +               ${IPT} -t nat -D "${parent_chain}" -j ${chain};
>>>>>>>> +               ${IPT} -t nat -F ${chain};
>>>>>>>> +               ${IPT} -t nat -X ${chain};
>>>>>>>> +       else
>>>>>>>> +               return 1;
>>>>>>>> +       fi;
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +activate_custom_redirections() {
>>>>>>>> +       
>>>>>>>> +       local array=();
>>>>>>>> +       local redirects=();
>>>>>>>> +       local i;
>>>>>>>> +       index=();
>>>>>>>> +       iface=();
>>>>>>>> +       protocol=();
>>>>>>>> +       port=();
>>>>>>>> +       targetip=();
>>>>>>>> +       enabled=();
>>>>>>>> +
>>>>>>>> +       IFS=$'\n' read -d '' -ra redirects < ${redirectsfile};
>>>>>>>> +
>>>>>>>> +       for i in "${!redirects[@]}"
>>>>>>>> +       do
>>>>>>>> +               IFS=$',' read -ra array <<< ${redirects[i]};
>>>>>>>> +               index[i]=${array[0]};
>>>>>>>> +               iface[i]=${array[1]};
>>>>>>>> +               protocol[i]=${array[2]};
>>>>>>>> +               port[i]=${array[3]};
>>>>>>>> +               targetip[i]=${array[4]};
>>>>>>>> +               enabled[i]=${array[5]};
>>>>>>>> +       done
>>>>>>>> +
>>>>>>>> +       for i in "${!index[@]}"
>>>>>>>> +       do
>>>>>>>> +               if [[ ! -z "${GREEN_DEV}" &&  "${iface[i]}" =
>>>>>>>> "green" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>> +
>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>> ${GREEN_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>> ${port[i]} ";
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${GREEN_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${GREEN_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>> +               fi
>>>>>>>> +               if [[ ! -z "${BLUE_DEV}" &&  "${iface[i]}" =
>>>>>>>> "blue" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>> ${BLUE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>> ${port[i]} ";
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${BLUE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${BLUE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>> +               fi
>>>>>>>> +               if [[ ! -z "${ORANGE_DEV}" &&  "${iface[i]}" =
>>>>>>>> "orange" && "${enabled[i]}" = "enabled" ]]; then
>>>>>>>> +                       logtext "add redirect in ${chain} on
>>>>>>>> ${ORANGE_DEV} ip ${targetip[i]} protocol ${protocol[i]} port
>>>>>>>> ${port[i]} ";
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -d ${targetip[i]} -p ${protocol[i]} -m
>>>>>>>> ${protocol[i]} --dport ${port[i]} -j RETURN;
>>>>>>>> +                       ${IPT} -t nat -A ${chain} -i
>>>>>>>> ${ORANGE_DEV} -p ${protocol[i]} -m ${protocol[i]} --dport
>>>>>>>> ${port[i]} -j REDIRECT;
>>>>>>>> +               fi
>>>>>>>> +       done
>>>>>>>> +       unset array redirects i index iface protocol port
>>>>>>>> targetip enabled;
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +activate_redirections() {
>>>>>>>> +
>>>>>>>> +       if ! create_chain; then return 1; fi;
>>>>>>>> +       
>>>>>>>> +       # Force DNS REDIRECTs on GREEN (udp, tcp, 53)
>>>>>>>> +       if [[ "${REDIR_DNS_GREEN}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport domain -j RETURN;
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>> udp -m udp --dport domain -j REDIRECT;
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>> ${GREEN_ADDRESS} -p tcp -m tcp --dport domain -j RETURN;
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>> tcp -m tcp --dport domain -j REDIRECT;
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force DNS REDIRECTs on BLUE (udp, tcp, 53)
>>>>>>>> +       if [[ "${REDIR_DNS_BLUE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>>>> -m udp --dport domain -j REDIRECT
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>> ${BLUE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p tcp
>>>>>>>> -m tcp --dport domain -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force DNS REDIRECTs on ORANGE (udp, tcp, 53)
>>>>>>>> +       if [[ "${REDIR_DNS_ORANGE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>> udp -m udp --dport domain -j REDIRECT
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>> ${ORANGE_ADDRESS} -p tcp -m tcp --dport domain -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>> tcp -m tcp --dport domain -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force NTP REDIRECTs on GREEN (udp, 123)
>>>>>>>> +       if [[ "${REDIR_NTP_GREEN}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_GREEN}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -d
>>>>>>>> ${GREEN_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${GREEN_DEV} -p
>>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force NTP REDIRECTs on BLUE (udp, 123)
>>>>>>>> +       if [[ "${REDIR_NTP_BLUE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_BLUE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -d
>>>>>>>> ${BLUE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${BLUE_DEV} -p udp
>>>>>>>> -m udp --dport ntp -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       # Force NTP REDIRECTs on ORANGE (udp, 123)
>>>>>>>> +       if [[ "${REDIR_NTP_ORANGE}" == "on" && 
>>>>>>>> "${REDIR_CUSTOM_ORANGE}" = "off" ]]; then
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -d
>>>>>>>> ${ORANGE_ADDRESS} -p udp -m udp --dport ntp -j RETURN
>>>>>>>> +               ${IPT} -t nat -A ${chain} -i ${ORANGE_DEV} -p
>>>>>>>> udp -m udp --dport ntp -j REDIRECT
>>>>>>>> +       fi
>>>>>>>> +
>>>>>>>> +       if ! activate_custom_redirections; then return 1; fi;
>>>>>>>> +
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +case "${1}" in
>>>>>>>> +       start)
>>>>>>>> +               boot_mesg "Loading port redirections..."
>>>>>>>> +               activate_redirections;
>>>>>>>> +               evaluate_retval;
>>>>>>>> +               ;;
>>>>>>>> +
>>>>>>>> +       stop)   
>>>>>>>> +               boot_mesg "Removing port redirections..."
>>>>>>>> +               remove_chain;
>>>>>>>> +               evaluate_retval;
>>>>>>>> +               ;;
>>>>>>>> +
>>>>>>>> +       restart)
>>>>>>>> +               ${0} stop
>>>>>>>> +               ${0} start
>>>>>>>> +               ;;
>>>>>>>> +
>>>>>>>> +       *)
>>>>>>>> +               echo "Usage: ${0} {start|stop|restart}"
>>>>>>>> +               exit 1
>>>>>>>> +               ;;
>>>>>>>> +esac
>>>>>>>> +
>>>>>>>> +# End $rc_base/init.d/portredir
>>>>>>>> diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
>>>>>>>> index 7c3ef7529..850f8fdcc 100644
>>>>>>>> --- a/src/misc-progs/Makefile
>>>>>>>> +++ b/src/misc-progs/Makefile
>>>>>>>> @@ -30,7 +30,7 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
>>>>>>>>        wirelessctrl getipstat qosctrl \
>>>>>>>>        redctrl syslogdctrl extrahdctrl sambactrl \
>>>>>>>>        smartctrl clamavctrl addonctrl pakfire mpfirectrl
>>>>>>>> wlanapctrl \
>>>>>>>> -       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>>>> rebuildroutes \
>>>>>>>> +       setaliases urlfilterctrl updxlratorctrl fireinfoctrl
>>>>>>>> rebuildroutes portredirctrl \
>>>>>>>>        getconntracktable wirelessclient torctrl ddnsctrl
>>>>>>>> unboundctrl \
>>>>>>>>        captivectrl
>>>>>>>> 
>>>>>>>> diff --git a/src/misc-progs/portredirctrl.c b/src/misc-
>>>>>>>> progs/portredirctrl.c
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..7897d711c
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/misc-progs/portredirctrl.c
>>>>>>>> @@ -0,0 +1,47 @@
>>>>>>>> +/* This file is part of the IPFire Firewall.
>>>>>>>> + *
>>>>>>>> + * This program is distributed under the terms of the GNU
>>>>>>>> General Public
>>>>>>>> + * Licence.  See the file COPYING for details.
>>>>>>>> + *
>>>>>>>> + */
>>>>>>>> +
>>>>>>>> +#include <stdlib.h>
>>>>>>>> +#include <stdio.h>
>>>>>>>> +#include <string.h>
>>>>>>>> +#include <unistd.h>
>>>>>>>> +#include <sys/types.h>
>>>>>>>> +#include <fcntl.h>
>>>>>>>> +#include "setuid.h"
>>>>>>>> +
>>>>>>>> +int main(int argc, char *argv[]) {
>>>>>>>> +       if (!(initsetuid()))
>>>>>>>> +               exit(1);
>>>>>>>> +
>>>>>>>> +       // Check what command is asked
>>>>>>>> +        if (argc < 2) {
>>>>>>>> +                fprintf(stderr, "\nNo argument
>>>>>>>> given.\n\nportredirctrl
>>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>>> +                exit(1);
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        if (strcmp(argv[1], "start") == 0) {
>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>> start");
>>>>>>>> +        } else if (strcmp(argv[1], "stop") == 0) {
>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>> stop");
>>>>>>>> +        } else if (strcmp(argv[1], "restart") == 0) {
>>>>>>>> +                safe_system("/etc/rc.d/init.d/portredir
>>>>>>>> restart");
>>>>>>>> +       } else if (strcmp(argv[1], "enable") == 0) {
>>>>>>>> +               safe_system("touch
>>>>>>>> /var/ipfire/portredir/enable");
>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc3.d/S23portredir >/dev/null 2>&1");
>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc0.d/K77portredir >/dev/null 2>&1");
>>>>>>>> +               safe_system("ln -snf /etc/rc.d/init.d/portredir
>>>>>>>> /etc/rc.d/rc6.d/K77portredir >/dev/null 2>&1");
>>>>>>>> +       } else if (strcmp(argv[1], "disable") == 0) {
>>>>>>>> +               safe_system("/etc/rc.d/init.d/portredir stop");
>>>>>>>> +               safe_system("unlink
>>>>>>>> /var/ipfire/portredir/enable");
>>>>>>>> +               safe_system("rm -rf /etc/rc.d/rc*.d/*portredir
>>>>>>>>> /dev/null 2>&1");
>>>>>>>> +        } else {
>>>>>>>> +                fprintf(stderr, "\nBad argument
>>>>>>>> given.\n\nportredirctrl
>>>>>>>> (start|stop|restart|enable|disable)\n\n");
>>>>>>>> +                exit(1);
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +       return 0;
>>>>>>>> +}
>>>>>>>> diff --git a/src/paks/portredir/install.sh
>>>>>>>> b/src/paks/portredir/install.sh
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..9f69aeae2
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/paks/portredir/install.sh
>>>>>>>> @@ -0,0 +1,32 @@
>>>>>>>> +#!/bin/bash
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# This file is part of the IPFire
>>>>>>>> Firewall.                                #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire 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.                                      #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire 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 IPFire; if not, write to the Free
>>>>>>>> Software                    #
>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>> 02111-1307 USA #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# Copyright (C) 2021 IPFire-Team
>>>>>>>> <info(a)ipfire.org>.                        #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#
>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>> +extract_files
>>>>>>>> +restore_backup ${NAME}
>>>>>>>> +
>>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>>> +
>>>>>>>> +chown root:nobody /usr/local/bin/portredirctrl
>>>>>>>> +chmod 4750 /usr/local/bin/portredirctrl
>>>>>>>> +chmod u+s /usr/local/bin/portredirctrl
>>>>>>>> diff --git a/src/paks/portredir/uninstall.sh
>>>>>>>> b/src/paks/portredir/uninstall.sh
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..df9270125
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/paks/portredir/uninstall.sh
>>>>>>>> @@ -0,0 +1,28 @@
>>>>>>>> +#!/bin/bash
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# This file is part of the IPFire
>>>>>>>> Firewall.                                #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire 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.                                      #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire 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 IPFire; if not, write to the Free
>>>>>>>> Software                    #
>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>> 02111-1307 USA #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>>> <info(a)ipfire.org>.                        #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#
>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>> +make_backup ${NAME}
>>>>>>>> +remove_files
>>>>>>>> +
>>>>>>>> +/usr/local/bin/update-lang-cache
>>>>>>>> diff --git a/src/paks/portredir/update.sh
>>>>>>>> b/src/paks/portredir/update.sh
>>>>>>>> new file mode 100644
>>>>>>>> index 000000000..89c40d0d7
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/src/paks/portredir/update.sh
>>>>>>>> @@ -0,0 +1,26 @@
>>>>>>>> +#!/bin/bash
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# This file is part of the IPFire
>>>>>>>> Firewall.                                #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire 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.                                      #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# IPFire 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 IPFire; if not, write to the Free
>>>>>>>> Software                    #
>>>>>>>> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
>>>>>>>> 02111-1307 USA #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +# Copyright (C) 2007 IPFire-Team
>>>>>>>> <info(a)ipfire.org>.                        #
>>>>>>>> +#                                                             
>>>>>>>>             #
>>>>>>>> +##############################################################
>>>>>>>> ##############
>>>>>>>> +#
>>>>>>>> +. /opt/pakfire/lib/functions.sh
>>>>>>>> +./uninstall.sh
>>>>>>>> +./install.sh
>>>>>>>> -- 
>>>>>>>> 2.18.0
>>>>>>>> 
>>>>>>> 
>>>>>> 
>>>>> 
>>>> 
>>> 
>> 
> 


       reply	other threads:[~2021-07-01 15:24 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <ADA23788-C972-417C-8F55-DD9D3B335BE9@gmail.com>
2021-07-01 15:24 ` Michael Tremer [this message]
     [not found] <CD95E831-4AE9-40B7-BB91-27B3F3164270@gmail.com>
2021-08-05 20:33 ` Michael Tremer
2021-08-06 15:57   ` Stefan Schantl
     [not found] <6EBF1B5C-79B2-4258-B117-C2A4EB16CCFD@gmail.com>
2021-07-05 12:08 ` Stefan Schantl
     [not found] <E41194E1-4002-4960-BF73-F57D6BCCD152@gmail.com>
2021-07-05 12:07 ` Stefan Schantl
     [not found] <A699ABA7-74D3-4551-A981-E2E3A0935F8E@gmail.com>
2021-07-01  8:08 ` Michael Tremer
     [not found] <305D680D-4D10-4D99-9F1D-3589BDF08FA9@gmail.com>
2021-06-28 17:56 ` Michael Tremer
2021-06-27 13:48 Matthias Fischer
2021-06-28 16:04 ` Michael Tremer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=56118375-8C9D-4553-A919-6696E7944E8D@ipfire.org \
    --to=michael.tremer@ipfire.org \
    --cc=development@lists.ipfire.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox