Richard Bagshaw am Samstag, 13. Mai 2006 15.56:
> Peeps,
>
> I'm very new to perl and I have been trying to solve a problem for a few
> days now, I am reading a file that I use to setup my firewall rules on a
> Linux box, the file contains many lines, but as an example I will show
> just two here :-
>
> iptables -A INPUT -p tcp -s 123.45.678.90 --dport 22 -j ACCEPT
>
> and ...
>
> iptables -A INPUT -p tcp --dport 25 -j ACCEPT
>
> As you can see, the main difference is that line one has the "source ip"
> listed, where as line 2 doesn't.  I am writing a regex to basically pull
> all of the pertinent parts from the lines.  The below expression works
> perfectly for the first line, but for the 2nd line it does not work.  I
> know "why" it doesn't work, but cannot find the correct syntax for
> alternation to make the 2nd line work.
>
> if (/(INPUT) -p (...) -s ([0-9\.]*) --dport ([0-9]+) -j ([A-Za-z].*)/gi) {
>   ... code here ...
> }
>
> $1 = INPUT
> $2 = UDP or TCP
> $3 = <IP of SOURCE>
> $4 = <PORT>
> $5 = ACCEPT or REJECT or DROP

Hello Richard

Within [], a "." has literal meaning, so the backslash is not necessary.

([0-9.]*) matches not only IPs: It also matches the empty string, '2...3.', 
etc.

[A-Za-z] and the i modifier are the same as [A-Z] 
(and the last .* looks a bit strange, it matches nothing or anything).

The if/regex construct only matches the first rule.

What about the use of \s+ instead of the space character in the regex?
If somebody edited the rule file and had put two spaces somewhere, 
the original regex would not match. 

What if the order is different than expected by the regex?

Anyway, a first version that does what you want could be:

===
#!/usr/bin/perl
use strict; use warnings;

while (<DATA>) { # handle line after line of the input file
  if (/^iptables\s+-A\s+(INPUT)\s+
       -p\s+(...)\s+
       (?:-s\s+([0-9.]*)\s+)? # the optional source IP
       --dport\s+([0-9]+)\s+
       -j\s+([A-Za-z]+)
       /gix)
  {
    print map "<$_>", $1, $2, $3, $4, $5; 
    print "\n";
  }
}

__DATA__
iptables -A INPUT -p tcp -s 123.45.678.90 --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 25 -j ACCEPT
===

#output:

<INPUT><tcp><123.45.678.90><22><ACCEPT>
Use of uninitialized value in concatenation (.) or string at ./script.pl line 
37, <DATA> line 2.
<INPUT><tcp><><25><ACCEPT>


You would have to check if the values in at least $2 and $3 are valid 
(if it's not sure they are valid in the input file).

Another approach would be to extract the parts one by one. 
The following code is easy to extend/adapt (say, you want to match 
more rules later on, or handle syntax errors, or handle 
the case where (shell) variables are used in the rules, etc.)

===
#!/usr/bin/perl
use strict; use warnings;

while (<DATA>) {
  chomp; # remove ending newline

  my %parts; # the contents of one rule

  $parts{chain} =$1 if s/^iptables\s+-A\s+([A-Z]+)//i;
  next unless $parts{chain}; # skip empty lines;

  $parts{proto} =$1 if s/-p\s+((?:tcp)|(?:udp))//i;
  $parts{srcip} =$1 if s/-s\s+((?:\d{1,3}\.){3}\d{1,3})//;
  $parts{dport} =$1 if s/--dport\s+([0-9]+)//;
  $parts{target}=$1 if s/-j\s+([A-Z]+)//i;
  # other parts here

  $parts{unmatched}=$_ unless /^\s*$/ ;

  print map "<$_=$parts{$_}>", keys %parts;
  print "\n";
}

__DATA__
iptables -A INPUT -p tcp -s 123.45.678.90 --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 25 -j ACCEPT
iptables -A INPUT -p tcp -s 123.45.678.90 --sport 22 -j ACCEPT
===
# output:

<target=ACCEPT><proto=tcp><chain=INPUT><srcip=123.45.678.90><dport=22>
<target=ACCEPT><proto=tcp><chain=INPUT><dport=25>
<target=ACCEPT><proto=tcp><chain=INPUT><srcip=123.45.678.90><unmatched=   
--sport 22 >


There are improvements possible, I'm sure, but maybe it can give you an 
idea :-)


Dani

-- 
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
<http://learn.perl.org/> <http://learn.perl.org/first-response>


Reply via email to