正则表达式解析DNS答案

时间:2017-12-19 17:12:36

标签: regex perl sed

我想解析以下几行

8.8.19.12.53 > 125.15.15.9.40583: [udp sum ok] 62639 q: A? mp.microsoft.com. 6/5/9 mp.microsoft.com. CNAME .mp.microsoft.com.c.footprint.net., mp.microsoft.com.c.footprint.net. A 8.250.143.254, mp.microsoft.com.c.footprint.net. A 8.250.157.254 ns: c.footprint.net. NS d.ns.c.footprint.net. ar: d.ns.c.footprint.net. A 4.26.235.155 (439)
8.8.19.12.53 > 125.15.15.9.42091: [udp sum ok] 46555 q: A? www.toto.net. 1/0/0 www.toto.net. A 120.33.1.11 (47)

并获得以下输出

125.15.15.9 mp.microsoft.com A 8.250.143.254 A 8.250.157.254
125.15.15.9 www.toto.net A 120.33.1.11

我成功用命令

解析前两个字段
sed -Eun 's/[^>]+> ([0-9.]+)\.[0-9]+:.+q: A\? ([a-z0-9.-]+)\.([^:]+).*/\1:\2:\3/pg

`

但我无法获得已解析的IP(A xx.xx.xx.xx)。事实上可能有几个。

是否可以使用sed或Perl获得此类输出?

修改: 正如我在评论中添加的,解析更大的输入样本,我还需要在输出中丢弃几行。这条线的特征可以是:

  • A记录的数量(“A xx.xx.xx.xx”)为非空
  • 或该行不得包含NXDomain\*?-

我成功地满足了新的第一个需求,但不是第二个。

在@ikegami回复之后,这是我的尝试:

  perl -nle '
     my $field_value_re = qr/(?![^\s:]++:(?!\S)) \S++ (?: (?! \s++ [^\s:]++:(?!\S) ) \s++ \S++ )*+/x;

     my ($id, $rest) = /^ \s+ ( [^:]++ ) : \s++ $field_value_re ( .* ) /sx
        or next;

     my ($ip) = $id =~ /^ \S++ \s++ \S++ \s++ ( [^\s\.]++\.[^\s\.]++\.[^\s\.]++\.[^\s\.]++ )\.[^\s\.]++ \z /x
        or next;

     my %fields = $rest =~ /\G \s++ ( [^\s:]++ ) :(?!\S) \s++ ( $field_value_re ) /gsx;

     my ($query, $answers) = $fields{q} =~ /^ A\? \s++ ( \S++ ) \s++ \S++ \s++ ( .* ) /sx
        or next;

     $query =~ s/\.\z//;

     my @answers = split(/\s*+,\s*+/, $answers);
     my ($afield) = join " ", map { /^\S++\s++A\s++(\S++)/ } @answers;
     if ( length($afield) != 0)
     {
             print join " ", $ip, $query, $afield;
     }
  ' dns.sample

3 个答案:

答案 0 :(得分:3)

这与您提出的样本数据

有关

我首先构建一个匹配数字URL的正则表达式模式$url_re,以使下面的代码更简洁。然后,我会在>之后立即搜索第一个网址,在A?之后立即搜索指定的网址,并在A

之后搜索以下所有网址

它们都存储在数组@urls中并打印

use strict;
use warnings 'all';
use 5.010;

my $url_re = qr/(?:\d+\.){3}\d+/;

while ( <DATA> ) {

    my @urls = ( />\s+($url_re)/, /A\?\s+([-\w.]+\w)/, /(A\s+$url_re)/g );

    say "@urls";
}

__DATA__
8.8.19.12.53 > 125.15.15.9.40583: [udp sum ok] 62639 q: A? mp.microsoft.com. 6/5/9 mp.microsoft.com. CNAME .mp.microsoft.com.c.footprint.net., mp.microsoft.com.c.footprint.net. A 8.250.143.254, mp.microsoft.com.c.footprint.net. A 8.250.157.254 ns: c.footprint.net. NS d.ns.c.footprint.net. ar: d.ns.c.footprint.net. A 4.26.235.155 (439)
8.8.19.12.53 > 125.15.15.9.42091: [udp sum ok] 46555 q: A? www.toto.net. 1/0/0 www.toto.net. A 120.33.1.11 (47)

输出

125.15.15.9 mp.microsoft.com A 8.250.143.254 A 8.250.157.254 A 4.26.235.155
125.15.15.9 www.toto.net A 120.33.1.11

答案 1 :(得分:2)

每一行似乎都是

形式
{"id" with spaces}: {stuff} [ {key}: {stuff} ]*

您似乎对&#34; id&#34;内部以及名为q的字段内的信息感兴趣。 q字段的值似乎是

形式
A? {word} {word} {ns_return} [, {ns_return} ]*

这是一个处理上述格式的强大解决方案。

perl -nle'
   my $field_value_re = qr/(?![^\s:]++:(?!\S)) \S++ (?: (?! \s++ [^\s:]++:(?!\S) ) \s++ \S++ )*+/x;

   my ($id, $id_val, $rest) = /^ ( [^:]++ ) : \s++ ( $field_value_re ) ( .* ) /sx
      or next;

   next if $id_val =~ /\bNXDomain\b/;

   my ($ip) = $id =~ /^ \S++ \s++ \S++ \s++ ( [^\s\.]++\.[^\s\.]++\.[^\s\.]++\.[^\s\.]++ )\.[^\s\.]++ \z /x
      or next;

   my %fields = $rest =~ /\G \s++ ( [^\s:]++ ) :(?!\S) \s++ ( $field_value_re ) /gsx;

   my ($query, $answers) = $fields{q} =~ /^ A\? \s++ ( \S++ ) \s++ \S++ \s++ ( .* ) /sx
      or next;

   $query =~ s/\.\z//;

   my @answers =
      map { /^\S++\s++A\s++(\S++)/ }
         split(/\s*+,\s*+/, $answers);

   next if !@answers;

   print join " ", $ip, $query, map { "A $_" } @answers;
' log
125.15.15.9 mp.microsoft.com A 8.250.143.254 A 8.250.157.254
125.15.15.9 www.toto.net A 120.33.1.11

答案 2 :(得分:1)

这会使用map函数打印所需的输出,以某种非正统的方式忽略q:之后的任何字段

perl -lne 'print join qq/\t/, m/> ([\d\.]+)\./, map {/A\? ([^\s]+)\./, /(A [\d\.]+)/g} / q:([^:]+)/' log.txt