"Fossies" - the Fresh Open Source Software Archive

Member "Mail-SPF-Query-1.999.1/bin/spfquery" (22 Jan 2006, 13315 Bytes) of package /linux/privat/old/Mail-SPF-Query-1.999.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 #!/usr/bin/perl
    2 
    3 # spfquery - Sender Permitted From command line utility
    4 #
    5 #  Author: Wayne Schlitt <wayne@midwestcs.com>
    6 #
    7 #  File:   spfquery
    8 #  Desc:   SPF command line utility
    9 #
   10 # $Id: spfquery 138 2006-01-22 18:00:34Z julian $
   11 #
   12 # This program is free software; you can redistribute it and/or modify
   13 # it under the terms of either:
   14 #
   15 #   a) The GNU Lesser General Public License as published by the Free
   16 #      Software Foundation; either version 2.1, or (at your option) any
   17 #      later version,
   18 #
   19 #   OR
   20 #
   21 #   b) The two-clause BSD license.
   22 #
   23 #
   24 # The two-clause BSD license:
   25 #
   26 #
   27 # Redistribution and use in source and binary forms, with or without
   28 # modification, are permitted provided that the following conditions
   29 # are met:
   30 #
   31 # 1. Redistributions of source code must retain the above copyright
   32 #    notice, this list of conditions and the following disclaimer.
   33 # 2. Redistributions in binary form must reproduce the above copyright
   34 #    notice, this list of conditions and the following disclaimer in the
   35 #    documentation and/or other materials provided with the distribution.
   36 #
   37 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   38 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   39 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   40 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   41 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   42 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   43 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   44 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   45 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   46 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   47 
   48 =head1 NAME
   49 
   50 spfquery - checks if an IP address is an SPF-authorized SMTP sender for a
   51 domain
   52 
   53 =head1 VERSION
   54 
   55 2.3
   56 
   57 =head1 SYNOPSIS
   58 
   59 B<spfquery> B<--mail-from>|B<-m>|B<--sender>|B<-s> I<email-address>|I<domain>
   60 B<--helo>|B<-h> I<hostname> B<--ip>|B<-i> I<ip-address> [I<OPTIONS>]
   61 
   62 B<spfquery> B<--helo>|B<-h> I<hostname> B<--ip>|B<-i> I<ip-address> [I<OPTIONS>]
   63 
   64 B<spfquery> B<--file>|B<-f> I<filename>|B<-> [I<OPTIONS>]
   65 
   66 B<spfquery> B<--version>|B<-V>
   67 
   68 B<spfquery> B<--help>
   69 
   70 =head1 DESCRIPTION
   71 
   72 B<spfquery> performs Sender Policy Framework (SPF) authorization checks based
   73 on the command-line arguments or data given in a file or on standard input.
   74 For information on SPF see L<http://www.openspf.org>.
   75 
   76 The B<--mail-from> form checks if the given I<ip-address> is an authorized SMTP
   77 sender for the given envelope sender I<domain> or I<email-address> and C<HELO>
   78 I<hostname> (so-called C<MAIL FROM> check).  If a I<domain> is given,
   79 C<postmaster> will be substituted for the localpart.
   80 
   81 The B<--helo> form checks if the given I<ip-address> is an authorized SMTP
   82 sender for the given C<HELO> I<hostname> (so-called C<HELO> check).
   83 
   84 The B<--file> form reads "I<ip-address> I<sender-address> I<helo-hostname>"
   85 tuples from the file with the specified I<filename>, or from standard input if
   86 I<filename> is B<->.
   87 
   88 The B<--version> form prints version information of spfquery.  The B<--help>
   89 form prints usage information for spfquery.
   90 
   91 =head1 OPTIONS
   92 
   93 The B<--mail-from>, B<--helo>, and B<--file> forms optionally take any of the
   94 following additional I<OPTIONS>:
   95 
   96 =over
   97 
   98 =item B<--debug>
   99 
  100 Print out debug information.
  101 
  102 =item B<--default-explanation> I<string>
  103 
  104 Use the specified I<string> as the default explanation if the SPF record does
  105 not specify an explanation string itself.
  106 
  107 =item B<--guess> I<spf-terms>
  108 
  109 Use I<spf-terms> as a default record if no SPF record is found.  B<This is a
  110 non-standard feature.>
  111 
  112 =item B<--keep-comments>
  113 
  114 =item B<--no-keep-comments>
  115 
  116 Do (not) print any comments found when reading from a file or from standard
  117 input.
  118 
  119 =item B<--local> I<spf-terms>
  120 
  121 Process I<spf-terms> as local policy before resorting to a default result
  122 (the implicit or explicit C<all> mechanism at the end of the domain's SPF
  123 record).  For example, this could be used for white-listing one's secondary
  124 MXes: C<mx:mydomain.example.org>.
  125 
  126 =item B<--max-lookup-count> I<n>
  127 
  128 Perform a maximum of I<n> SPF record lookups.  Defaults to B<10>.
  129 
  130 =item B<--name> I<hostname>
  131 
  132 Use I<hostname> as the hostname of the local system instead of auto-detecting
  133 it.
  134 
  135 =item B<--override> I<domain>B<=>I<spf-record>
  136 
  137 =item B<--fallback> I<domain>B<=>I<spf-record>
  138 
  139 Set overrides and fallbacks.  Each option can be specified multiple times.  For
  140 example: C<--override example.org='v=spf1 -all' --override
  141 '*.example.net'='v=spf1 a mx -all' --fallback example.com='v=spf1 -all'>.
  142 B<This is a non-standard feature.>
  143 
  144 =item B<--rcpt-to> I<email-addresses>
  145 
  146 Automatically allow the secondary MXes of the comma-separated list of
  147 I<email-addresses>.
  148 
  149 =item B<--sanitize>
  150 
  151 =item B<--no-sanitize>
  152 
  153 Do (not) sanitize the output by condensing consecutive white-space into a
  154 single space and replacing non-printable characters with question marks.
  155 Enabled by default.
  156 
  157 =item B<--trusted>
  158 
  159 =item B<--no-trusted>
  160 
  161 Do (not) perform C<trusted-forwarder.org> accreditation checking.  Disabled by
  162 default.  B<This is a non-standard feature.>
  163 
  164 =back
  165 
  166 =head1 RESULT CODES
  167 
  168 =over 10
  169 
  170 =item B<pass>
  171 
  172 The specified IP address is an authorized mailer for the sender domain/address.
  173 
  174 =item B<fail>
  175 
  176 The specified IP address is not an authorized mailer for the sender
  177 domain/address.
  178 
  179 =item B<softfail>
  180 
  181 The specified IP address is not an authorized mailer for the sender
  182 domain/address, however the domain is still in the process of transitioning to
  183 SPF.
  184 
  185 =item B<neutral>
  186 
  187 The sender domain makes no assertion about the status of the IP address.
  188 
  189 =item B<unknown>
  190 
  191 The sender domain has a syntax error in its SPF record.
  192 
  193 =item B<error>
  194 
  195 A temporary DNS error occurred while resolving the sender policy.  Try again
  196 later.
  197 
  198 =item B<none>
  199 
  200 There is no SPF record for the sender domain.
  201 
  202 =back
  203 
  204 =head1 EXIT CODES
  205 
  206 =over
  207 
  208 =item B<0>
  209 
  210 pass
  211 
  212 =item B<1>
  213 
  214 fail
  215 
  216 =item B<2>
  217 
  218 softfail
  219 
  220 =item B<3>
  221 
  222 neutral
  223 
  224 =item B<4>
  225 
  226 unknown
  227 
  228 =item B<5>
  229 
  230 error
  231 
  232 =item B<6>
  233 
  234 none
  235 
  236 =back
  237 
  238 =head1 EXAMPLES
  239 
  240     spfquery -i 11.22.33.44 -m user@example.com -h spammer.example.net
  241     spfquery -f test_data
  242     echo "127.0.0.1 user@example.com helohost.example.com" | spfquery -f -
  243 
  244 =head1 SEE ALSO
  245 
  246 L<Mail::SPF::Query>, L<spfd>
  247 
  248 =head1 AUTHORS
  249 
  250 This version of B<spfquery> was written by Wayne Schlitt <wayne@midwestcs.com>.
  251 
  252 This man-page was written by Julian Mehnle <julian@mehnle.net>, based on a
  253 man-page written by S. Zachariah Sprackett for an older version of B<spfquery>.
  254 
  255 =cut
  256 
  257 our $VERSION = '2.3';
  258 
  259 use warnings;
  260 use strict;
  261 
  262 use Mail::SPF::Query;
  263 use Getopt::Long qw(:config gnu_compat);
  264 
  265 sub usage()
  266 {
  267   printf STDERR <<'EOT';
  268 Usage:
  269     spfquery --mail-from|-m <email-address>|<domain> --helo|-h <hostname>
  270         --ip|-i <ip-address> [OPTIONS]
  271     spfquery --helo|-h <hostname> --ip|-i <ip-address> [OPTIONS]
  272     spfquery --file|-f <filename>|- [OPTIONS]
  273     spfquery --version|-V
  274 
  275 See `spfquery --help` for more information.
  276 EOT
  277 }
  278 
  279 sub help()
  280 {
  281   print STDERR <<'EOT';
  282 Usage:
  283     spfquery --mail-from|-m <email-address>|<domain> --helo|-h <hostname>
  284         --ip|-i <ip-address> [OPTIONS]
  285     spfquery --helo|-h <hostname> --ip|-i <ip-address> [OPTIONS]
  286     spfquery --file|-f <filename>|- [OPTIONS]
  287     spfquery --version|-V
  288 
  289 spfquery performs SPF checks based on the command-line arguments or data given
  290 in a file or on standard input.
  291 
  292 The "--mail-from" form checks if the given <ip-address> is an authorized SMTP
  293 sender for the given envelope sender <domain> or <email-address> and HELO
  294 <hostname> (so-called MAIL FROM check).  If a <domain> is given, "postmaster"
  295 will be substituted for the localpart.
  296 
  297 The "--helo" form checks if the given <ip-address> is an authorized SMTP sender
  298 for the given HELO <hostname> (so-called HELO check).
  299 
  300 The "--file" form reads "<ip-address> <sender-address> <helo-hostname>" tuples
  301 from the file with the specified <filename>, or from standard input if
  302 <filename> is "-".
  303 
  304 The "--version" form prints version information of spfquery.
  305 
  306 Valid OPTIONS are:
  307     --debug             Output debugging information.
  308     --default-explanation <string>
  309                         Default explanation string to use.
  310     --guess <spf-terms> Default checks if no SPF record is found.
  311     --keep-comments     Print comments found when reading from a file.
  312     --local <spf-terms> Local policy for whitelisting.
  313     --max-lookup-count <n>
  314                         Maximum number of DNS lookups to allow.
  315     --name <hostname>   The name of the system doing the SPF checking.
  316     --override <domain>=<spf-record>
  317     --fallback <domain>=<spf-record>
  318                         Set override and fallback SPF records for domains.
  319     --rcpt-to <email-addresses>
  320                         A comma-separated lists of email addresses that will
  321                         have email from their secondary MXes automatically
  322                         allowed.
  323     --no-sanitize       Do not clean up invalid characters in output.
  324     --trusted           Check trusted-forwarder.org white-list.
  325 
  326 Examples:
  327     spfquery -i 11.22.33.44 -m user@example.com -h spammer.example.net
  328     spfquery -f test_data
  329     echo "127.0.0.1 user@example.com helohost.example.com" | spfquery -f -
  330 EOT
  331 }
  332 
  333 my %opt;
  334 
  335 my $result = GetOptions(
  336   \%opt,
  337   
  338   'file|f=s',
  339   'ip|ipv4|i=s',
  340   'mail-from|mfrom|m|sender|s=s',
  341   'helo|h=s',
  342   'rcpt-to|r=s',
  343   
  344   'debug!',
  345   'local=s',
  346   'trusted!',
  347   'guess=s',
  348   'default-explanation=s',
  349   'max-lookup-count|max-lookup=i',
  350   'sanitize!',
  351   'name=s',
  352   'override=s%',
  353   'fallback=s%',
  354   'keep-comments!',
  355   
  356   'version|V!',
  357   'help!'
  358 );
  359 
  360 if (!$result) {
  361   usage();
  362   exit 255;
  363 }
  364 
  365 if ($opt{help}) {
  366   help();
  367   exit 255;
  368 }
  369 
  370 if ($opt{version}) {
  371   printf STDERR "spfquery version %s\n\n", $VERSION;
  372   exit 0;
  373 }
  374 
  375 $opt{name} = 'spfquery' if not defined($opt{name});
  376 
  377 #
  378 # Process the SPF request
  379 #
  380 
  381 my $res;
  382 
  383 if (
  384   defined($opt{'mail-from'}) and defined($opt{helo}) and defined($opt{ip}) and
  385   not defined($opt{file})
  386 ) {
  387   # --mail-from form:
  388   $res = do_query();
  389 }
  390 elsif (
  391   defined($opt{helo}) and defined($opt{ip}) and
  392   not defined($opt{'mail-from'}) and not defined($opt{file})
  393 ) {
  394   # --helo form:
  395   $res = do_query();
  396 }
  397 elsif (
  398   defined($opt{file}) and
  399   not defined($opt{'mail-from'}) and not defined($opt{helo}) and not defined($opt{ip})
  400 ) {
  401   # --file form:
  402   local *FIN;
  403   if ($opt{file} eq '-') {
  404     *FIN = \*STDIN;
  405   }
  406   else {
  407     open(FIN, $opt{file}) || die("Could not open: $opt{file}\n");
  408   }
  409   while (<FIN>) {
  410     chomp;
  411     if ( /^\s*$/ || /^\s*#/ ) {
  412       print("$_\n") if $opt{'keep-comments'};
  413       next;
  414     }
  415     s/^\s*//;
  416     @opt{'ip', 'mail-from', 'helo', 'rcpt-to'} = split;
  417     $res = do_query();
  418   }
  419 }
  420 else {
  421   # Invalid usage.
  422   usage();
  423   exit 255;
  424 }
  425 
  426 exit $res;
  427 
  428 
  429 #
  430 # Process the SPF request and print the results
  431 #
  432 sub do_query {
  433   $opt{'mail-from'} = '' if not defined($opt{'mail-from'});
  434   $opt{helo}        = '' if not defined($opt{helo});
  435 
  436   my $query = new Mail::SPF::Query (ipv4       => $opt{ip},
  437                                     sender     => $opt{'mail-from'},
  438                                     helo       => $opt{helo},
  439                                     local      => $opt{local},
  440                                     trusted    => $opt{trusted},
  441                                     guess      => $opt{guess},
  442                                     default_explanation => $opt{exp},
  443                                     max_lookup_count    => $opt{'max-lookup-count'},
  444                                     sanitize   => $opt{sanitize},
  445                                     myhostname => $opt{name},
  446                                     fallback   => $opt{fallback},
  447                                     override   => $opt{override},
  448                                     debug      => $opt{debug}
  449                                    );
  450 
  451   my ($result, $smtp_comment, $header_comment);
  452   my $per_result;
  453   if (!defined($opt{'rcpt-to'}) || $opt{'rcpt-to'} eq '') {
  454     ($result, $smtp_comment, $header_comment) = $query->result;
  455     $per_result = $result;
  456   }
  457   else {
  458     $result = "";
  459     foreach my $recip (split(',', $opt{'rcpt-to'})) {
  460 
  461       ($per_result, $smtp_comment, $header_comment) = $query->result2( split(';', $recip));
  462       if ($result eq "" ) {
  463         $result = $per_result;
  464       }
  465       else {
  466         $result .= ",".$per_result;
  467       }
  468     }
  469     ($per_result, $smtp_comment, $header_comment) = $query->message_result2;
  470 
  471     if ($result eq "" ) {
  472       $result = $per_result;
  473     }
  474     else {
  475       $result .= ",".$per_result;
  476     }
  477   }
  478         
  479   my $received_spf;
  480   $received_spf = "Received-SPF: $per_result ($header_comment) client-ip=$opt{ip};";
  481   $received_spf .= " envelope-from=$opt{'mail-from'};" if defined($opt{'mail-from'});
  482   $received_spf .= " helo=$opt{helo};" if defined($opt{helo});
  483   { no warnings 'uninitialized';
  484     print "$result\n$smtp_comment\n$header_comment\n$received_spf\n";
  485   }
  486 
  487   return 0 if $result eq "pass";
  488   return 1 if $result eq "fail";
  489   return 2 if $result eq "softfail";
  490   return 3 if $result eq "neutral";
  491   return 4 if $result eq "unknown";
  492   return 5 if $result eq "error";
  493   return 6 if $result eq "none";
  494 
  495   return 255;
  496 }