--- Query.pm.orig Mon Oct 27 13:33:23 2003 +++ Query.pm Mon Nov 3 20:45:24 2003 @@ -39,6 +39,18 @@ my %SEVERITY = (pass => 00, softfail => 05, fail => 10, error => 20, unknown => 50); +my %allowed_default = ( + unknown => 0, + deny => 0, + softdeny => 0, + allow => 0, + '!' => 'deny', + '-' => 'softdeny', + '?' => 'unknown', + '+' => 'allow', +); + + $VERSION = "1.7"; $CACHE_TIMEOUT = 120; @@ -404,6 +416,88 @@ delete $Domains_Queried->{$query->cache_point}; } +sub get_ptr_domain { + my ($query) = shift; + + return $query->{ptr_domain} if ($query->{ptr_domain}); + + foreach my $ptrdname ($query->myquery(reverse_in_addr($query->{ipv4}) . ".in-addr.arpa", "PTR", "ptrdname")) { + $query->debuglog(" get_ptr_domain: $query->{ipv4} is $ptrdname"); + + $query->debuglog(" get_ptr_domain: checking hostname $ptrdname for legitimacy."); + + # check for legitimacy --- PTR -> hostname A -> PTR + foreach my $ptr_to_a ($query->myquery($ptrdname, "A", "address")) { + + $query->debuglog(" get_ptr_domain: hostname $ptrdname -> $ptr_to_a"); + + if ($ptr_to_a eq $query->{ipv4}) { + return $query->{ptr_domain} = $ptrdname; + } + } + } + + return undef; +} + +sub macro_substitute_item { + my $query = shift; + my $arg = shift; + + my ($field, $modifier) = split(//, $arg, 2); + my ($commands, $splitpattern) = $modifier =~ /([\dr]+)(.*)/; + my @modifier = split(//, $commands); + $splitpattern ||= '.'; + + my $newval = $arg; + my $timestamp = time; + + $newval = $query->{local_part} if ($field eq 'u'); + $newval = $query->{sender} if ($field eq 's'); + $newval = $query->{domain} if ($field eq 'd'); + $newval = $timestamp if ($field eq 't'); + $newval = $query->{helo} if ($field eq 'h'); + $newval = $query->ip if ($field eq 'i'); + $newval = $query->get_ptr_domain if ($field eq 'p'); + $newval = $query->{ipv4} ? 'in-addr' : 'ip6' + if ($field eq 'v'); + + while ($#modifier >= 0) { + if ($modifier[0] =~ /\d/) { + my @components = split(/\Q$splitpattern\E/, $newval); + + if ($#components + 1 > $modifier[0]) { + splice(@components, 0, $#components + 1 - $modifier[0]); + } + + $newval = join('.', @components); + } elsif ($modifier[0] eq 'r') { + $newval = join('.', reverse(split(/[\Q$splitpattern\E]/i, $newval))); + } + shift @modifier; + } + + $newval = uri_escape($newval) if ($field ne lc $field); + + return $newval; +} + +sub macro_substitute { + my $query = shift; + my $arg = shift; + my $original = $arg; + my $maxlen = shift; + + $arg =~ s/%((%)|(\w)|{(\w+)})/$2 || $query->macro_substitute_item($3 || $4)/ge; + if ($maxlen && length $arg > $maxlen) { + $arg = substr($arg, -$maxlen); + # now remove leading component cos we probably stomped it + $arg =~ s/[^.]*\.//; + } + $query->debuglog(" macro_substitute: $original -> $arg") if ($original ne $arg); + return $arg; +} + # ---------------------------------------------------------- # evaluate_mechanism # ---------------------------------------------------------- @@ -420,7 +514,7 @@ if ($mechanism =~ s/^([-?+!])//) { $modifier = $1 } if ({ map { $_=>1 } @KNOWN_MECHANISMS }->{$mechanism}) { - my ($hit, $text) = $query->$mechanism($argument); + my ($hit, $text) = $query->$mechanism($query->macro_substitute($argument, 255)); return if not $hit; @@ -720,9 +814,13 @@ $query->debuglog(" localpart: rlp delimiters = $delimiters; rlp_domain = $rlp_domain"); - if ($delimiters =~ s/-//) { $delimiters .= "-" } # move - to end of [...-] - - my @localpart = split /[$delimiters]/i, $localpart; + my @localpart; + + if ($delimiters ne '') { + @localpart = split /[\Q$delimiters\E]/i, $localpart; + } else { + @localpart = ($localpart); + } $query->debuglog(" localpart: splitting $localpart produces @localpart"); @@ -810,19 +908,7 @@ sub interpolate_explanation { my $query = shift; my $comment = shift; - my $exp = $query->{directive_set}->explanation; - - my $timestamp = time; - - for ($exp) { - s(%u)($query->{local_part})g; s(%U)(uri_escape($query->{local_part}))eg; - s(%s)($query->{sender})g; s(%S)(uri_escape($query->{sender}))eg; - s(%d)($query->{domain})g; s(%D)(uri_escape($query->{domain}))eg; - s(%t)($timestamp)g; s(%T)(uri_escape($timestamp))eg; - s(%h)($query->{helo})g; s(%H)(uri_escape($query->{helo}))eg; - s(%i)($query->ip)eg; s(%I)(uri_escape($query->ip))eg; - s(%%)(%)g; - } + my $exp = $query->macro_substitute($query->{directive_set}->explanation); if (not $exp) { return $comment } if (not $comment) { return $exp } @@ -937,8 +1023,8 @@ if ($lhs eq "scope") { push @{$directive_set->{scope}}, split /,/, $rhs, -1; } if ($lhs eq "default") { - if ($rhs =~ /^(unknown|deny|softdeny|allow)$/i) { - $directive_set->{default} = $rhs; + if ($rhs =~ /^(unknown|deny|softdeny|allow)$/i || defined($allowed_default{$rhs})) { + $directive_set->{default} = $allowed_default{$rhs} || $rhs; } else { $directive_set->{soft_syntax_error} = "spf error: \"$lhs=$rhs\" not understood";