Server IP : 85.214.239.14 / Your IP : 18.117.94.221 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/2/cwd/usr/share/perl5/Amavis/Lookup/ |
Upload File : |
# SPDX-License-Identifier: GPL-2.0-or-later package Amavis::Lookup::DNSxL; use strict; use re 'taint'; BEGIN { use vars qw($dns_resolver); # implicit persistent Net::DNS::Resolver object } use Amavis::Conf qw(:platform); use Amavis::Util qw(ll do_log); sub new { my($class, $zone, $expected, $resolver) = @_; # $zone is either a DNSxL zone name, or a template where an %a is a # place-holder for the IP address to be queried. # The result of a type-A DNS query is matched against $expected, which is # either a scalar string, or a ref to an array of strings, or a regexp obj. require NetAddr::IP or die "Can't load module NetAddr::IP"; NetAddr::IP->VERSION(4.010); # need a method full6() if ($resolver) { # DNS resolver object provided by a caller, use that } elsif ($dns_resolver) { # reuse previously created internal resolver object $resolver = $dns_resolver; } else { # create a new internal resolver object with some sensible defaults require Net::DNS or die "Can't load module Net::DNS"; $dns_resolver = Net::DNS::Resolver->new( config_file => '/etc/resolv.conf', force_v4 => !$have_inet6, defnames => 0, retry => 1, persistent_udp => 1, tcp_timeout => 2, udp_timeout => 2, retrans => 1); # seconds $dns_resolver or die "Failed to create a Net::DNS::Resolver object"; $dns_resolver->udppacketsize(1220); $resolver = $dns_resolver; } defined $zone && $zone ne '' or die "DNS zone name must not be empty, in Amavis::Lookup::DNSxL"; $expected = '127.0.0.2' if !defined $expected; # an RFC 5782 convention my $self = { zone => $zone, # DNSxL zone name (a base DNS domain name) resolver => $resolver, # a Net::DNS::Resolver object or equivalent expected => $expected, # a set of replies that qualify as a match }; bless $self, $class; } # Query a DNSxL list given an IPv4 or IPv6 address, according to RFC 5782. # Returns an IPv4 address in the 127.0.0.0/8 subnet as returned by a DNS # type-A query when the result matches the provided expected value, or a # zero when a query succeeded (NOERROR or NXDOMAIN) but there was no match. # The argument $expected may be a string, a ref to array, or a regexp object. # Returns undef on DNS failures (like a timeout, or no Net::DNS module). # sub lookup_ip { my($self, $ipaddr) = @_; my $result; # result of a DNS query, undef indicates a lookup failure my $fullkey; # matching (expected) key return ($result,$fullkey) if !$self->{resolver}; my $revip; local($1,$2,$3,$4); if ($ipaddr =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\z/) { $revip = "$4.$3.$2.$1"; } elsif ($ipaddr =~ /:[0-9a-f]*:/i) { # triage # looks like an IPv6 address, let NetAddr::IP check the details my $ip_obj = NetAddr::IP->new6($ipaddr); if (defined $ip_obj) { # a valid IPv6 address, apply RFC 5782 section 2.4 $revip = lc $ip_obj->network->full6; # string in a canonical form $revip =~ s/://gs; $revip = join('.', reverse split(//,$revip)); } } if (!defined $revip) { do_log(4,'invalid IP address for a DNSxL query: %s', $ipaddr); return ($result,$fullkey); } my $query = $self->{zone}; $query =~ s/%a/$revip/gs or ($query = $revip . '.' .$query); my $pkt = $self->{resolver}->send($query, 'A'); my $ll5 = ll(5); $result = 0; # defined but false if (!$pkt || !$pkt->header) { undef $result; $ll5 && do_log(5,'DNSxL query %s, no result', $query); } elsif ($pkt->header->rcode eq 'NXDOMAIN') { $ll5 && do_log(5,'DNSxL query %s, domain does not exist', $query); } elsif ($pkt->header->rcode ne 'NOERROR') { $ll5 && do_log(5,'DNSxL query %s, rcode %s', $query, $pkt->header->rcode); } elsif ($pkt->header->ancount) { my $expected = $self->{expected}; $expected = [ $expected ] if !ref $expected; for my $rr ($pkt->answer) { next if $rr->type ne 'A'; my $returned_addr = $rr->address; $ll5 && do_log(5,'DNSxL query %s, DNS answer: %s',$query,$returned_addr); # RFC 5782 section 2.3: values SHOULD be in the 127.0.0.0/8 range next if $returned_addr !~ /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/s; if (ref $expected eq 'ARRAY') { # $expected is an array of strings: IPv4 addresses in dotted-quad # form, with bytes possibly omitted from the left for (@$expected) { if ( ( /^\d+\z/ ? "127.0.0.$_" : /^\d+\.\d+\z/ ? "127.0.$_" : /^\d+\.\d+\.\d+\z/ ? "127.$_" : $_) eq $returned_addr) { $fullkey = $_; $result = $returned_addr; last; } } last if defined $result; } elsif (ref $expected eq 'Regexp') { # $expected is a regular expresion if ($returned_addr =~ /$expected/s) { $fullkey = "$expected"; # stringified regexp object $result = $returned_addr; last; } } } } do_log(5,'DNSxL result: %s, matches %s',$result,$fullkey) if $ll5 && $result; ($result, $fullkey); } 1;