summaryrefslogtreecommitdiffstats
path: root/dmarc_milter.pl
blob: bdb6e0b553ac5e37d6aff6095bd26d7430ddc76f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use lib '.';

use Sendmail::Milter;
use Spline::DMARC qw(check_addresses);
use Spline::Log qw(set_verbose debug info);
use Spline::Data;

use Data::Dumper;

my %milter_callbacks = (
    'envfrom' => \&from_callback,
    'envrcpt' => \&rcpt_callback,
    'header' => \&header_callback,
    'eoh' => \&eom_callback,
    'abort' => \&abort_callback,
    'close' => \&close_callback,
);

sub from_callback($$@) {
    my $ctx = shift;
    my $from = shift;

    my $data = Spline::Data->new($ctx);
    $data->set('counter', 0);

    debug "MAIL FROM: $from";
    return SMFIS_CONTINUE;
}

sub rcpt_callback($$@) {
    my $ctx = shift;
    my $rcpt_to = shift;

    my $data = Spline::Data->load($ctx);
    debug "RCPT TO: $rcpt_to";

    my $next_hop = $ctx->getsymval('{rcpt_host}');
    if ($next_hop eq '[lists.spline.inf.fu-berlin.de]') {
        info "Mailinglist address: $rcpt_to";
        $data->set('counter', 1);
    }

    return SMFIS_CONTINUE;
}

sub header_callback($$$) {
    my $ctx = shift;
    my ($field, $value) = @_;
 
    my $data = Spline::Data->load($ctx);
    debug "HEADER '$field': $value";

    if (lc($field) eq 'from') {
        return SMFIS_CONTINUE if $data->get('counter') == 0;

        my $reject = check_addresses($value);
        if ($reject) {
            info 'Rejecting mail';
            $ctx->setreply('550', '5.7.2', 'Your provider does not permit sending to mailing lists (DMARC policy)');
            return SMFIS_REJECT;
        }
    }

    # We cannot SMFIS_ACCEPT here, because there could
    # be multiple From headers.
    return SMFIS_CONTINUE; 
}

sub eoh_callback($) {
    my $ctx = shift;

    my $data = Spline::Data->load($ctx);
    $data->set('counter', 0);

    debug 'END OF HEADER';
    return SMFIS_ACCEPT;
}

sub abort_callback($) {
    my $ctx = shift;

    my $data = Spline::Data->load($ctx);
    $data->set('counter', 0);

    debug 'ABORT';
    return SMFIS_CONTINUE;
}

sub close_callback($) {
    my $ctx = shift;
    
    Spline::Data->load($ctx);
    $ctx->setpriv(undef);

    debug 'CLOSE';
    return SMFIS_CONTINUE;
}

sub main($) {
    my $listen = shift;

    Sendmail::Milter::setconn($listen);
    Sendmail::Milter::register("dmarc_lists_filter",
        \%milter_callbacks, SMFI_CURR_ACTS);
    Sendmail::Milter::main();
}

main('inet:12345@localhost');

# vim: set et tabstop=4 tw=70: