switch to IPC::Run, add logging
[undersmtpd.git] / _smtpd
1 #!/usr/bin/perl -w
2
3 use strict;
4 use Getopt::Std;
5 use vars qw(%aliases $opt_a);
6 use subs qw(validate_recipient process_message read_aliases);
7 use Net::Server::Mail::ESMTP;
8 use IPC::Run qw( run );
9 use Sys::Syslog;
10
11 getopt('a:v');
12
13 my $smtp = new Net::Server::Mail::ESMTP;
14 $smtp->set_callback(RCPT => \&validate_recipient);
15 $smtp->set_callback(DATA => \&process_message);
16
17 openlog('[_smtpd]', '', 'mail');
18 read_aliases;
19 $smtp->process;
20 closelog();
21 #--
22
23 sub validate_recipient {
24   my($session, $recipient) = @_;
25   $recipient =~ s/^<//;
26   $recipient =~ s/\@.*$//;
27   return(0, 550, "Unknown user $recipient") unless exists $aliases{$recipient};
28   return(1);
29 }
30
31 sub process_message {
32   my($session, $data) = @_;
33
34   #warn "DATA: ". $$data;
35
36   my @recipients = $session->get_recipients();
37   return(0, 554, 'Error: no valid recipients')
38     unless(@recipients);
39
40   syslog('info',
41          'received '.length($$data).' bytes for '.join(',', @recipients)
42   );
43   foreach my $recipient ( @recipients ) {
44     $recipient =~ s/^<//;
45     $recipient =~ s/\@.*$//;
46
47     my $result = eval { run $aliases{$recipient}, $data };
48     if (!$result) {
49       syslog('info', "pipe command failed: $@");
50       return(0, 451, "pipe command failed: $@");
51     }
52   }
53
54   syslog('info', 'message piped');
55   return(1, 250, 'message piped');
56
57 }
58
59 #--
60
61 sub read_aliases {
62   open(ALIASES, $opt_a || '/etc/aliases' ) or die $!;
63
64   while(<ALIASES>) {
65     chomp;
66     s/^\s+//; s/\s+$//;
67     next if /^$/ or /^#/;
68     /^([\w\-\+\.]+):\s*("?)\|(.*)\2\s*$/ or next;
69     #$aliases{$1} = [ split(/\s+/, $3) ];
70     $aliases{$1} = [ qw(/bin/sh -c), $3 ];
71   }
72
73 }
74
75 =head1 NAME
76
77 _smtpd - UnderSMTPD, the underscore SMTP daemon
78
79 =head1 SYNOPSIS
80
81   #make some aliases
82   echo 'username: "|someprogram and args"' > /etc/aliases
83
84   #inetd setup
85   echo "smtp stream tcp nowait mail /usr/local/bin/_smtpd" >>/etc/inetd.conf
86   echo "_smtpd: my.mail.server.ip"                         >>/etc/hosts.allow
87   echo "_smtpd: ALL"                                       >>/etc/hosts.deny
88
89   #or add an smtp file to /etc/xinetd.d/
90   service smtp
91   {
92         socket_type = stream
93         protocol    = tcp
94         wait        = no
95         user        = mail
96         server      = /usr/local/bin/_smtpd
97   }
98
99 =head1 DESCRIPTION
100
101 This is a minimal SMTP server which only forwards mail to pipe destinations
102 in /etc/aliases.  It does nothing else.  Its intended function is on an
103 internal mail server that forwards mail to other programs on a per address
104 basis.
105
106 UnderSMTPD reads /etc/aliases for usernames; if a match is identified
107 the message is piped to the given program.  Any problems executing the program
108 will cause a temporary SMTP error to be returned to the connecting client.
109
110 Other kinds of aliases are not recognized and cause a permanent SMTP error
111 to be returned to the connecting client, as do usernames not found in
112 /etc/aliases
113
114 UnderSMTPD was originally written to be used with the Request Tracker ticketing
115 system.
116
117 UnderSMTPD uses Net::Server::Mail to do all the hard work.
118
119 =head1 OPTIONS
120
121 =over 4
122
123 =item -a filename: Alternate aliases file
124
125 =back
126
127 =head1 ALIASES FORMAT
128
129   username: |program and args
130   username: "|program and args"
131
132 Quotes are not necessary around the pipe symbol, program and arguments but are
133 stripped if present.  Line continuations are not supported.
134
135 =head1 RT ALIASES EXAMPLE
136
137   support: |/opt/rt3/bin/rt-mailgate --queue support --action correspond --url http://rt.example.com/
138   billing: |/opt/rt3/bin/rt-mailgate --queue billing --action correspond --url http://rt.example.com/
139
140 =head1 BUGS
141
142 Yes.
143
144 =head1 AUTHOR
145
146 Ivan Kohler <ivan-undersmtpd@420.am>
147
148 =head1 SEE ALSO
149
150 L<Net::Server::Mail>
151
152 =cut
153
154 1;
155