39
# Big Ugly Hack; purpose: don't force requirement on IO::Socket::INET6
41
use vars qw($have_getaddrinfo_in_core $have_getaddrinfo_legacy
42
$io_socket_module_name $have_inet4 $have_inet6
44
# don't force requirement on IO::Socket::IP or IO::Socket::INET6
43
require IO::Socket::INET6;
46
use Socket qw(:DEFAULT IPPROTO_TCP);
48
# AUTOLOADing 'constants' here enables inlining - see Exporter man page
49
&SOCK_STREAM; &IPPROTO_TCP; &SOMAXCONN;
51
$have_getaddrinfo_in_core = eval {
52
# The Socket module (1.94) bundled with Perl 5.14.* provides
53
# new affordances for IPv6, including implementations of the
54
# Socket::getaddrinfo() and Socket::getnameinfo() functions,
55
# along with related constants and a handful of new functions.
56
# Perl 5.16.0 upgrades the core Socket module to version 2.001.
57
# Socket->VERSION(1.94); # provides getaddrinfo() and getnameinfo()
58
# Socket->VERSION(1.95); # provides AI_ADDRCONFIG
59
Socket->VERSION(1.96); # provides NIx_NOSERV, and Exporter tag :addrinfo
60
# Socket->VERSION(1.97); # IO::Socket::IP depends on Socket 1.97
61
import Socket qw(/^(?:AI|NI|NIx|EAI)_/);
62
&AI_ADDRCONFIG; &AI_PASSIVE; # enable inlining
63
&NI_NUMERICHOST, &NI_NUMERICSERV; &NIx_NOSERV; 1;
66
$have_getaddrinfo_legacy = !$have_getaddrinfo_in_core && eval {
68
# Socket6->VERSION(0.13); # provides NI_NAMEREQD
69
Socket6->VERSION(0.18); # provides AI_NUMERICSERV
70
import Socket6 qw(/^(?:AI|NI|NIx|EAI)_/);
71
&AI_ADDRCONFIG; &AI_PASSIVE; # enable inlining
72
&NI_NUMERICHOST; &NI_NUMERICSERV; &NI_NAMEREQD; 1;
47
require IO::Socket::INET;
48
*new_io_socket_inetx = sub { IO::Socket::INET->new(@_); };
49
*ip_or_name_to_ip = sub {
50
my $in_addr = (gethostbyname(shift))[4] or return undef;
51
Socket::inet_ntoa($in_addr);
53
*peer_info_from_socket = sub {
55
my ($port, $in_addr) = Socket::sockaddr_in($sock->peername)
75
&AF_UNSPEC; &AF_INET; &AF_INET6; # enable inlining
77
$ai_addrconfig_flag = 0;
79
if ($have_getaddrinfo_in_core) {
80
# using a modern Socket module
82
eval { # does the operating system recognize an AI_ADDRCONFIG flag?
83
if (&AI_ADDRCONFIG && &EAI_BADFLAGS) {
84
my($err, @res) = Socket::getaddrinfo("localhost", 0,
85
{ family => &AF_UNSPEC, flags => &AI_ADDRCONFIG });
86
$ai_addrconfig_flag = &AI_ADDRCONFIG if !$err || $err != &EAI_BADFLAGS;
90
*ip_or_name_to_ip_addresses = sub {
91
my($addr, $ai_family) = @_;
92
# Socket::getaddrinfo returns a list of hashrefs
94
Socket::getaddrinfo($addr, 0,
95
{ family => $ai_family, flags => $ai_addrconfig_flag | &AI_PASSIVE,
96
socktype => &SOCK_STREAM, protocol => &IPPROTO_TCP });
101
Socket::getnameinfo($a->{addr},
102
&NI_NUMERICHOST | &NI_NUMERICSERV, &NIx_NOSERV);
103
if (!$err) { push(@ip_addrs, $ip_addr) }
104
elsif (!$error) { $error = $err }
107
return ($error, @ip_addrs);
110
*peer_info_from_socket = sub {
112
my $peer_addr = $sock->peerhost; # textual representation of an IP addr
113
$peer_addr or return;
115
if ($sock->UNIVERSAL::can('peerhostname')) {
116
$peer_hostname = $sock->peerhostname; # provided by IO::Socket::IP
118
my($err, $host) = Socket::getnameinfo($sock->peername,
119
&NI_NAMEREQD, &NIx_NOSERV);
120
$peer_hostname = $host if !$err;
122
return ($sock->peerport, $peer_addr, $peer_hostname||$peer_addr,
126
} elsif ($have_getaddrinfo_legacy) {
127
# using a legacy Socket6 module; somewhat different API on getaddrinfo()
128
# and getnameinfo() compared to these functions in a module Socket
130
eval { # does the operating system recognize an AI_ADDRCONFIG flag?
131
if (&AI_ADDRCONFIG && &EAI_BADFLAGS) {
132
my @res = Socket6::getaddrinfo("localhost", "", 0, &SOCK_STREAM,
133
&IPPROTO_TCP, &AI_ADDRCONFIG);
134
my $err = @res >= 5 ? 0 : $res[0];
135
$ai_addrconfig_flag = &AI_ADDRCONFIG if !$err || $err != &EAI_BADFLAGS;
139
*ip_or_name_to_ip_addresses = sub {
140
my($addr, $ai_family) = @_;
141
# Socket6::getaddrinfo returns a list of quintuples
142
my @res = Socket6::getaddrinfo($addr, '',
143
$ai_family, &SOCK_STREAM, &IPPROTO_TCP,
144
$ai_addrconfig_flag | &AI_PASSIVE);
145
my($error, @ip_addrs);
149
my($family, $socktype, $proto, $saddr, $canonname);
151
($family, $socktype, $proto, $saddr, $canonname, @res) = @res;
153
Socket6::getnameinfo($saddr, &NI_NUMERICHOST | &NI_NUMERICSERV);
154
if (@resinfo >= 2) { push(@ip_addrs, $resinfo[0]) }
155
elsif (!$error) { $error = $resinfo[0] }
158
return ($error, @ip_addrs);
161
*peer_info_from_socket = sub {
163
my $peer_addr = $sock->peerhost;
164
$peer_addr or return;
165
my @resinfo = (Socket6::getnameinfo($sock->peername, &NI_NAMEREQD))[0];
166
my $peer_hostname = @resinfo > 1 ? $resinfo[0] : undef;
167
return ($sock->peerport, $peer_addr, $peer_hostname||$peer_addr,
171
} else { # IPv4 only, no getaddrinfo() available
173
*ip_or_name_to_ip_addresses = sub {
174
my($addr, $ai_family) = @_;
175
$ai_family == &AF_UNSPEC || $ai_family == &AF_INET
176
or die "Protocol family $ai_family not supported on this platform";
177
my($error, @ip_addrs, @binaddr);
178
$! = 0; my @res = gethostbyname($addr);
180
$error = "no results from gethostbyname $!";
182
my($name,$aliases,$addrtype,$length);
183
($name,$aliases,$addrtype,$length,@binaddr) = @res;
186
$error = "no such host";
189
my $ip_addr = Socket::inet_ntoa($_);
190
push(@ip_addrs, $ip_addr) if $ip_addr;
193
return ($error, @ip_addrs);
196
*peer_info_from_socket = sub {
198
my ($peer_port, $in_addr) = Socket::sockaddr_in($sock->peername)
57
my $addr = Socket::inet_ntoa($in_addr) or return;
58
my $host = gethostbyaddr($in_addr, Socket::AF_INET()) || $addr;
59
return ($port, $addr, $host);
63
*new_io_socket_inetx = sub { IO::Socket::INET6->new(@_); };
64
*ip_or_name_to_ip = sub {
67
shift, '', Socket::AF_UNSPEC(), Socket::SOCK_STREAM()
71
$addr = (Socket6::getnameinfo($addr, Socket6::NI_NUMERICHOST()))[0];
73
*peer_info_from_socket = sub {
75
my $addr = $sock->peerhost or return;
77
(Socket6::getnameinfo($sock->peername, Socket6::NI_NAMEREQD()))[0]
79
return ($sock->peerport(), $addr, $host);
200
my $peer_addr = Socket::inet_ntoa($in_addr) or return;
201
my $peer_hostname = gethostbyaddr($in_addr, &AF_INET);
202
return ($peer_port, $peer_addr, $peer_hostname||$peer_addr,
208
if (eval { require IO::Socket::IP }) { # handles IPv6 and IPv4
209
IO::Socket::IP->VERSION(0.09); # implements IPV6_V6ONLY
210
$io_socket_module_name = 'IO::Socket::IP';
212
} elsif (eval { require IO::Socket::INET6 }) { # handles IPv6 and IPv4
213
$io_socket_module_name = 'IO::Socket::INET6';
215
} elsif (eval { require IO::Socket::INET }) { # IPv4 only
216
$io_socket_module_name = 'IO::Socket::INET';
219
$have_inet4 = # can we create a PF_INET socket?
220
defined $io_socket_module_name && eval {
222
$io_socket_module_name->new(LocalAddr => '0.0.0.0', Proto => 'tcp');
223
$sock->close or die "error closing socket: $!" if $sock;
227
$have_inet6 = # can we create a PF_INET6 socket?
228
defined $io_socket_module_name &&
229
$io_socket_module_name ne 'IO::Socket::INET' &&
232
$io_socket_module_name->new(LocalAddr => '::', Proto => 'tcp');
233
$sock->close or die "error closing socket: $!" if $sock;
87
243
use Mail::SpamAssassin;
88
244
use Mail::SpamAssassin::NetSet;
89
245
use Mail::SpamAssassin::SubProcBackChannel;
90
246
use Mail::SpamAssassin::SpamdForkScaling qw(:pfstates);
91
247
use Mail::SpamAssassin::Logger qw(:DEFAULT log_message);
92
use Mail::SpamAssassin::Util qw(untaint_var exit_status_str
93
am_running_on_windows);
248
use Mail::SpamAssassin::Util qw(untaint_var untaint_file_path
249
exit_status_str am_running_on_windows);
94
250
use Mail::SpamAssassin::Timeout;
97
253
use POSIX qw(:sys_wait_h);
98
use POSIX qw(setsid sigprocmask _exit);
254
use POSIX qw(locale_h setsid sigprocmask _exit);
256
use Fcntl qw(:flock);
102
259
use File::Spec 0.8;
335
$opt{$opt} = Mail::SpamAssassin::Util::untaint_file_path(
336
File::Spec->rel2abs( $opt{$opt} ) # rel2abs taints the new value!
340
# sanity checking on parameters: if --socketpath is used, and --port or
341
# --ssl-port is NOT specified, it means that we're using UNIX domain sockets,
342
# none of the IP params are allowed. The code would probably work ok if we
343
# didn't check it, but it's better if we detect the error and report it lest
344
# the admin find surprises.
349
if (!defined $opt{'socketpath'}) {
356
( @{ $opt{'allowed-ip'} } > 0 )
357
or defined $opt{'auth-ident'}
358
or defined $opt{'port'}
366
or defined $opt{'ssl-port'}
367
or defined $opt{'ssl-version'}
371
if (!defined $opt{'ssl-port'}) {
378
and ( $opt{'socketowner'}
379
or $opt{'socketgroup'}
380
or $opt{'socketmode'})
383
print_usage_and_exit("ERROR: --socketowner/group/mode requires --socketpath param");
485
# rel2abs taints the new value!
487
untaint_file_path(File::Spec->rel2abs( $opt{$opt} )) if $opt{$opt};
386
490
# These can be changed on command line with -A flag
387
# but only if we're not using UNIX domain sockets
388
491
my $allowed_nets = Mail::SpamAssassin::NetSet->new();
389
if ( not defined $opt{'socketpath'} ) {
390
if ( @{ $opt{'allowed-ip'} } ) {
391
set_allowed_ip( grep length, map { split /,/ } @{ $opt{'allowed-ip'} } );
394
set_allowed_ip('127.0.0.1'); #, '::1'); M::SA::NetSet needs fixing for IPv6
492
if ( @{ $opt{'allowed-ip'} } ) {
493
set_allowed_ip( grep length, map { split /,/ } @{ $opt{'allowed-ip'} } );
495
set_allowed_ip('127.0.0.1', '::1');
398
498
# ident-based spamc user authentication
399
499
if ( $opt{'auth-ident'} ) {
400
eval { require Net::Ident };
401
die "spamd: ident-based authentication requested, but Net::Ident is unavailable ($@)\n"
500
eval { require Net::Ident }
501
or die "spamd: ident-based authentication requested, ".
502
"but Net::Ident is unavailable: $@\n";
404
504
$opt{'ident-timeout'} = undef if $opt{'ident-timeout'} <= 0.0;
405
505
import Net::Ident qw(ident_lookup);
408
# Check for server certs
409
$opt{'server-key'} ||= "$LOCAL_RULES_DIR/certs/server-key.pem";
410
$opt{'server-cert'} ||= "$LOCAL_RULES_DIR/certs/server-cert.pem";
412
eval { require IO::Socket::SSL };
413
die "spamd: SSL encryption requested, but IO::Socket::SSL is unavailable ($@)\n"
416
if ( !-e $opt{'server-key'} ) {
417
die "spamd: server key file $opt{'server-key'} does not exist\n";
419
if ( !-e $opt{'server-cert'} ) {
420
die "spamd: server certificate file $opt{'server-cert'} does not exist\n";
424
508
### Begin initialization of logging ########################
426
510
# The syslog facility can be changed on the command line with the
658
743
# Do whitelist later in tmp dir. Side effect: this will be done as -u user.
660
my ( $sslport, $sslversion, $inetport, $addr, $proto );
662
if ( $listen_inet || $listen_ssl ) {
663
$proto = getprotobyname('tcp') or die "getprotobyname(tcp): $!";
665
$addr = $opt{'listen-ip'};
668
$addr = ip_or_name_to_ip($addr);
669
die "spamd: invalid address: $opt{'listen-ip'}\n" unless $addr;
672
$addr = '0.0.0.0'; # FIXME: this won't bind to IPv6 sockets
745
my $sslversion = $opt{'ssl-version'} || 'sslv3';
746
if ($sslversion !~ /^(?:sslv3|tlsv1)$/) {
747
die "spamd: invalid ssl-version: $opt{'ssl-version'}\n";
750
$opt{'server-key'} ||= "$LOCAL_RULES_DIR/certs/server-key.pem";
751
$opt{'server-cert'} ||= "$LOCAL_RULES_DIR/certs/server-cert.pem";
753
# ---------------------------------------------------------------------------
754
# Server (listening) socket setup for the various supported types
756
dbg("spamd: socket module of choice: %s %s, Socket %s".
757
", %s PF_INET, %s PF_INET6, %s, AI_ADDRCONFIG %s",
758
$io_socket_module_name,
759
$io_socket_module_name->VERSION,
761
$have_inet4 ? 'have' : 'no',
762
$have_inet6 ? 'have' : 'no',
763
$have_getaddrinfo_in_core ? 'using Socket::getaddrinfo'
764
: $have_getaddrinfo_legacy ? 'using legacy Socket6::getaddrinfo'
765
: 'no getaddrinfo, using gethostbyname, IPv4-only',
766
$ai_addrconfig_flag ? "is supported" : "not supported",
770
my @listen_sockets; # list of hashrefs, contains info on all listen sockets
771
my $server_select_mask;
773
my @listen_socket_specs = @{$opt{'listen-sockets'}};
775
{ # merge legacy option --socketpath into @listen_socket_specs
776
my $socketpath = $opt{'socketpath'};
777
if (defined $socketpath && $socketpath ne '') {
779
or die "socketpath option should specify an absolute path: $socketpath";
780
push(@listen_socket_specs, $socketpath);
784
# supply a default socket (loopback IP address) if none specified
785
push(@listen_socket_specs, 'localhost') if !@listen_socket_specs;
787
for (@listen_socket_specs) {
788
my $socket_specs = $_;
790
$socket_specs = '*' if $socket_specs eq ''; # empty implies all interfaces
792
local($1,$2,$3,$4,$5,$6);
795
( / .* ) \z }xsi) { # unix socket - absolute path
796
my($proto,$path) = ($1, $2);
797
# $proto = 'ssl' if defined $opt{'ssl'} || defined $opt{'ssl-port'};
798
$proto = !defined($proto) ? '' : lc($proto);
799
# abstracted out the setup-retry code
800
dbg("spamd: unix socket: %s", $path);
801
server_sock_setup(\&server_sock_setup_unix, $socket_specs, $path);
803
} elsif ($socket_specs =~
807
| ( [a-f0-9]* : [a-f0-9]* : [a-f0-9:]* \z )
810
(?: : ( [a-z0-9-]* ) )? \z }xsi) {
811
my($proto,$addr,$port) = ($1, $2||$3||$4||$5, $6);
812
$addr = 'localhost' if !defined $addr;
813
$proto = 'ssl' if defined $opt{'ssl'} || defined $opt{'ssl-port'};
814
$proto = !defined($proto) ? '' : lc($proto);
815
$port = $opt{'ssl-port'} if !defined $port && $proto eq 'ssl';
816
$port = $opt{'port'} if !defined $port || $port eq '';
817
$port = '783' if !defined $port || $port eq '';
818
if ($port ne '' && $port !~ /^(\d+)\z/) {
819
$port = ( getservbyname($port,'tcp') )[2];
820
$port or die "spamd: invalid port: $port, socket: $socket_specs\n";
822
# abstracted out the setup-retry code
823
dbg('spamd: %s socket specification: "%s", IP address: %s, port: %s',
824
$proto, $socket_specs, $addr, $port);
825
server_sock_setup(\&server_sock_setup_inet,
826
$socket_specs, $addr, $port, $proto eq 'ssl' ? 1 : 0);
828
die "Invalid socket specification syntax: $socket_specs\n";
832
@listen_sockets or die "No listen sockets specified, aborting\n";
834
# ---------------------------------------------------------------------------
836
# Check for server certs
837
if ( $have_ssl_module ) {
838
if ( !-e $opt{'server-key'} ) {
839
die "spamd: server key file $opt{'server-key'} does not exist\n";
841
if ( !-e $opt{'server-cert'} ) {
842
die "spamd: server certificate file $opt{'server-cert'} does not exist\n";
846
# ---------------------------------------------------------------------------
848
my $sockets_access_lock_tempfile; # a File::Temp object, if locking is needed
849
my $sockets_access_lock_fh; # per-child file handle on a lock file
680
851
my $backchannel = Mail::SpamAssassin::SubProcBackChannel->new();
682
854
if (!$opt{'round-robin'})
684
856
my $max_children = $childlimit;
703
878
# ---------------------------------------------------------------------------
705
my $listeninfo = compose_listen_info_string();
707
880
sub compose_listen_info_string {
710
if ( $listen_unix ) {
711
push @listeninfo, "UNIX domain socket " . $opt{'socketpath'};
715
$sslport = $opt{'ssl-port'} || $opt{'port'} || 783;
716
if ($sslport !~ /^(\d+)$/ ) {
717
$sslport = ( getservbyname($sslport, 'tcp') )[2];
718
die "spamd: invalid ssl-port: $opt{'port'}\n" unless $sslport;
720
$sslversion = $opt{'ssl-version'} || 'sslv23';
721
if ($sslversion !~ /^(?:sslv([23]|23)|(tlsv1))$/) {
722
die "spamd: invalid ssl-version: $opt{'ssl-version'}\n";
725
push @listeninfo, "SSL port $sslport/tcp";
726
push @listeninfo, "SSL version $sslversion";
729
if ( $listen_inet ) {
730
$inetport = $opt{'port'} || 783;
731
if ($inetport !~ /^(\d+)$/ ) {
732
$inetport = ( getservbyname($inetport, 'tcp') )[2];
733
die "spamd: invalid port: $opt{'port'}\n" unless $inetport;
736
push @listeninfo, "port $inetport/tcp";
883
for my $socket_info (@listen_sockets) {
884
next if !$socket_info;
885
my $socket = $socket_info->{socket};
887
my $socket_specs = $socket_info->{specs};
889
if ($socket->isa('IO::Socket::UNIX')) {
890
push(@listeninfo, "UNIX domain socket " . $socket_info->{path});
892
} elsif ( $socket->isa('IO::Socket::INET') ||
893
$socket->isa('IO::Socket::INET6') ||
894
$socket->isa('IO::Socket::IP') ) {
895
push(@listeninfo, sprintf("%s [%s]:%s", ref $socket,
896
$socket_info->{ip_addr}, $socket_info->{port}));
898
} elsif ($socket->isa('IO::Socket::SSL')) {
899
push(@listeninfo, sprintf("SSL [%s]:%s, ssl version %s",
900
$socket_info->{ip_addr}, $socket_info->{port},
901
$opt{'ssl-version'}||'sslv3'));
739
905
# just for reporting at startup
740
$listeninfo = join ', ', @listeninfo;
743
# ---------------------------------------------------------------------------
744
# Server (listening) socket setup for the various supported types
746
my ( $server_inet, $server_unix, $server_ssl );
747
my ( $fd_inet, $fd_unix, $fd_ssl );
748
my $have_multiple_server_socks;
749
my $server_select_mask;
751
# abstract out the setup-retry code
752
if ( $listen_unix ) {
753
server_sock_setup(sub { server_sock_setup_unix(); });
756
server_sock_setup(sub { server_sock_setup_ssl(); });
758
if ( $listen_inet ) {
759
server_sock_setup(sub { server_sock_setup_inet(); });
906
return join(', ', @listeninfo);
762
909
sub server_sock_setup {
910
my($sub, @args) = @_;
765
912
# retry 3 times to bind to the listening socket; 3 seconds delay,
766
913
# max, but should allow a little time for any existing shutting-down
868
1014
if (!chmod $mode, $path) { # make sure everybody can talk to it
869
1015
die "spamd: could not chmod $path to $mode: $!";
873
sub server_sock_setup_ssl {
876
LocalPort => $sslport,
881
SSL_version => $sslversion,
882
SSL_verify_mode => 0x00,
883
SSL_key_file => $opt{'server-key'},
884
SSL_cert_file => $opt{'server-cert'}
886
dbg("spamd: creating SSL socket:\n" . join("\n", map { " $_: " . (defined $socket{$_} ? $socket{$_} : "(undef)") } sort keys %socket));
887
$server_ssl = new IO::Socket::SSL(%socket)
888
|| die "spamd: could not create SSL socket on $addr:$sslport: $!\n";
1018
push(@listen_sockets, { specs => $socket_specs,
1020
socket => $server_unix,
1021
fd => $server_unix->fileno }) if $server_unix;
891
1025
sub server_sock_setup_inet {
894
LocalPort => $inetport,
900
dbg("spamd: creating INET socket:\n" . join("\n", map { " $_: " . (defined $socket{$_} ? $socket{$_} : "(undef)") } sort keys %socket));
901
$server_inet = new_io_socket_inetx(%socket)
902
|| die "spamd: could not create INET socket on $addr:$inetport: $!\n";
1026
my($socket_specs, $addr, $port, $ssl) = @_;
1028
$have_inet4 || $have_inet6
1029
or warn "spamd: neither the PF_INET (IPv4) nor the PF_INET6 (IPv6) ".
1030
"protocol families seem to be available, pushing our luck anyway\n";
1032
my $ai_family = &AF_UNSPEC; # defaults to any address family (i.e. both)
1033
if ($have_inet6 && (!$have_inet4 || $opt{'force_ipv6'})) {
1034
$ai_family = &AF_INET6;
1035
} elsif ($have_inet4 && (!$have_inet6 || $opt{'force_ipv4'})) {
1036
$ai_family = &AF_INET;
1038
my($error, @addresses);
1039
if (!defined $addr || lc $addr eq 'localhost') { # loopback interface
1040
push(@addresses, '127.0.0.1')
1041
if $ai_family == &AF_UNSPEC || $ai_family == &AF_INET;
1042
push(@addresses, '::1')
1043
if $ai_family == &AF_UNSPEC || $ai_family == &AF_INET6;
1044
} elsif ($addr eq '*' || $addr eq '') { # any address
1045
push(@addresses, '0.0.0.0')
1046
if $ai_family == &AF_UNSPEC || $ai_family == &AF_INET;
1047
push(@addresses, '::')
1048
if $ai_family == &AF_UNSPEC || $ai_family == &AF_INET6;
1050
($error, @addresses) = ip_or_name_to_ip_addresses($addr, $ai_family);
1052
die "spamd: invalid address for a listen socket: \"$socket_specs\": $error\n"
1054
die "spamd: no valid address for a listen socket: \"$socket_specs\"\n"
1057
dbg("spamd: attempting to listen on IP addresses: %s, port %d",
1058
join(', ',@addresses), $port);
1059
for my $adr (@addresses) {
1063
Type => &SOCK_STREAM,
1066
Listen => &SOMAXCONN,
1068
$sockopt{V6Only} = 1 if $io_socket_module_name eq 'IO::Socket::IP'
1069
&& IO::Socket::IP->VERSION >= 0.09;
1070
%sockopt = (%sockopt, (
1071
SSL_version => $sslversion,
1072
SSL_verify_mode => 0x00,
1073
SSL_key_file => $opt{'server-key'},
1074
SSL_cert_file => $opt{'server-cert'},
1076
dbg("spamd: creating %s socket: %s",
1077
$ssl ? 'IO::Socket::SSL' : $io_socket_module_name,
1078
join(', ', map("$_: ".(defined $sockopt{$_} ? $sockopt{$_} : "(undef)"),
1079
sort keys %sockopt)));
1080
if ($ssl && !$have_ssl_module) {
1081
eval { require IO::Socket::SSL }
1082
or die "spamd: SSL encryption requested, ".
1083
"but IO::Socket::SSL is unavailable ($@)\n";
1084
$have_ssl_module = 1;
1086
my $server_inet = $ssl ? IO::Socket::SSL->new(%sockopt)
1087
: $io_socket_module_name->new(%sockopt);
1089
or die sprintf("spamd: could not create %s socket on [%s]:%s: %s\n",
1090
$ssl ? 'IO::Socket::SSL' : $io_socket_module_name,
1092
push(@listen_sockets, { specs => $socket_specs,
1093
ip_addr => $adr, port => $port,
1094
socket => $server_inet,
1095
fd => $server_inet->fileno }) if $server_inet;
905
1100
# ---------------------------------------------------------------------------
1196
1444
sub accept_from_any_server_socket {
1198
my $fdvec = $server_select_mask;
1200
if ($have_multiple_server_socks) {
1201
# determine which of our server FDs is ready using select().
1202
# We only need to do this if we have more than one server
1203
# socket supported, since otherwise there can only be one socket
1204
# with a client waiting.
1205
# (TODO: we could extend the prefork protocol to pass this data)
1206
my $nfound = select($fdvec, undef, undef, 0.1);
1207
die "oops? accept_a_conn: no fds ready" unless $nfound;
1210
if ($fd_inet && vec $fdvec, $fd_inet, 1) {
1211
$client = $server_inet->accept;
1213
elsif ($fd_unix && vec $fdvec, $fd_unix, 1) {
1214
$client = $server_unix->accept;
1216
elsif ($fd_ssl && vec $fdvec, $fd_ssl, 1) {
1217
$client = $server_ssl->accept;
1220
die "accept_a_conn: no fds ready by vec: $fdvec";
1446
my($client, $selected_socket_info, $socket, $locked);
1449
if (!@listen_sockets) {
1453
} elsif (@listen_sockets == 1) {
1454
$selected_socket_info = $listen_sockets[0];
1457
# determine which of our server FDs is ready using select().
1458
# We only need to do this if we have more than one server
1459
# socket supported, since otherwise there can only be one socket
1460
# with a client waiting.
1461
# (TODO: we could extend the prefork protocol to pass this data)
1463
if ($sockets_access_lock_fh) {
1464
dbg("spamd: acquiring a lock over select+accept");
1465
# with multipe sockets a lock across select+accept is needed, Bug 6996
1466
flock($sockets_access_lock_fh, LOCK_EX)
1467
or die "Can't acquire lock access to sockets: $!";
1471
my $sel_mask_str = unpack('b*', $server_select_mask);
1472
dbg("spamd: select() on fd bit field %s, %s, %s",
1473
$sel_mask_str, defined $timeout ? "timeout $timeout" : "no timeout",
1474
$locked ? "locked" : "not locked");
1476
my $fdvec = $server_select_mask;
1477
my $nfound = select($fdvec, undef, undef, $timeout);
1479
if (!defined $nfound || $nfound < 0) {
1480
die "select failed on fd bit field $sel_mask_str: $!";
1481
} elsif (!$nfound) {
1482
die "no fd ready, fd bit field $sel_mask_str";
1485
my(@ready_fd) = # list of file desciptors ready for read
1486
grep(defined $_->{fd} && vec($fdvec, $_->{fd}, 1), @listen_sockets);
1488
die "no file descriptors matching a bit field " . unpack('b*',$fdvec);
1489
} elsif (@ready_fd == 1) { # easy, just one is ready
1490
$selected_socket_info = $ready_fd[0];
1491
} else { # give equal opportunity to each ready socket
1492
my $j = int rand(@ready_fd);
1493
$selected_socket_info = $ready_fd[$j];
1494
dbg("spamd: requests ready on multiple sockets, picking #%d out of %d",
1495
$j+1, scalar @ready_fd);
1498
} # end multiple sockets case
1500
if ($selected_socket_info) {
1501
my $socket = $selected_socket_info->{socket};
1502
$socket or die "no socket???, impossible";
1503
dbg("spamd: accept() on fd %d", $selected_socket_info->{fd});
1504
$client = $socket->accept;
1506
1; # end eval with success
1509
my $err = $@ ne '' ? $@ : "errno=$!"; chomp $err;
1511
dbg("spamd: releasing a lock over select+accept");
1512
flock($sockets_access_lock_fh, LOCK_UN)
1513
or die "Can't release sockets-access lock: $!";
1516
die "accept_a_conn: $err";
1520
dbg("spamd: releasing a lock over select+accept");
1521
flock($sockets_access_lock_fh, LOCK_UN)
1522
or die "Can't release sockets-access lock: $!";
1524
$client or die sprintf("accept_a_conn: %s accept failed: %s",
1526
!$socket->isa('IO::Socket::SSL') ? $!
1527
: $socket->errstr.", $!");
1528
return ($client, $selected_socket_info);
1225
1531
sub accept_a_conn {
1226
$client = accept_from_any_server_socket();
1535
# $client is a global variable
1536
($client, $socket_info) = accept_from_any_server_socket($timeout);
1228
1538
if ($scaling) {
1229
1539
$scaling->update_child_status_busy();
2875
3195
-g groupname, --groupname=groupname Run as groupname
2876
3196
-v, --vpopmail Enable vpopmail config
2877
3197
-x, --nouser-config Disable user config files
2878
--auth-ident Use ident to authenticate spamc user
3198
--auth-ident Use ident to identify spamc user (deprecated)
2879
3199
--ident-timeout=timeout Timeout for ident connections
2880
-A host,..., --allowed-ips=..,.. Limit ip addresses which can connect
2881
3200
-D, --debug[=areas] Print debugging messages (for areas)
2882
3201
-L, --local Use local tests only (no DNS)
2883
3202
-P, --paranoid Die upon user errors
2884
-H [dir], --helper-home-dir[=dir] Specify a different HOME directory
2885
--ssl Run an SSL server
2886
--ssl-port port Listen on port for SSL connections
3203
-H [dir], --helper-home-dir[=dir] Specify a different HOME directory
3204
--ssl Enable SSL on TCP connections
3205
--ssl-port port Override --port setting for SSL connections
2887
3206
--ssl-version sslversion Specify SSL protocol version to use
2888
3207
--server-key keyfile Specify an SSL keyfile
2889
3208
--server-cert certfile Specify an SSL certificate
2890
--socketpath=path Listen on given UNIX domain socket
3209
--socketpath=path Listen on a given UNIX domain socket
2891
3210
--socketowner=name Set UNIX domain socket file's owner
2892
3211
--socketgroup=name Set UNIX domain socket file's group
2893
3212
--socketmode=mode Set UNIX domain socket file's mode
2894
3213
-V, --version Print version and exit
3215
The --listen option (or -i) may be specified multiple times, its syntax
3216
is: [ ssl: ] [ host-name-or-IP-address ] [ : port ] or an absolute path
3217
(filename) of a Unix socket. If port is omitted it defaults to --port or
3218
to 783. Option --ssl implies a prefix 'ssl:'. An IPv6 address should be
3219
enclosed in square brackets, e.g. [::1]:783, an IPv4 address may be but
3220
need not be enclosed in square brackets. An asterisk '*' in place of a
3221
hostname implies an unspecified address, ('0.0.0.0' or '::'), i.e. it
3222
binds to all interfaces. An empty option value implies '*'. A default
3223
is '--listen localhost', which binds to a loopback interface only.
2896
3226
=head1 DESCRIPTION
2898
3228
The purpose of this program is to provide a daemonized version of the
3186
3524
immediately if authentication fails. In this case, spamc will pass
3187
3525
the mail through unchecked. Failure to connect to an ident server,
3188
3526
and response timeouts are considered authentication failures. This
3189
requires that Net::Ident be installed.
3527
requires that Net::Ident be installed. Deprecated.
3191
3529
=item B<--ident-timeout>=I<timeout>
3193
3531
Wait at most I<timeout> seconds for a response to ident queries.
3194
Authentication that takes long that I<timeout> seconds will fail, and
3532
Ident query that takes longer that I<timeout> seconds will fail, and
3195
3533
mail will not be processed. Setting this to 0.0 or less results in no
3196
3534
timeout, which is STRONGLY discouraged. The default is 5 seconds.
3198
3536
=item B<-A> I<host,...>, B<--allowed-ips>=I<host,...>
3200
Specify a list of authorized hosts or networks which can connect to this spamd
3201
instance. Single IP addresses can be given, ranges of IP addresses in
3202
address/masklength CIDR format, or ranges of IP addresses by listing 3 or less
3203
octets with a trailing dot. Hostnames are not supported, only IP addresses.
3538
Specify a comma-separated list of authorized hosts or networks which
3539
can connect to this spamd instance. Each element of the list is either a
3540
single IP addresses, or a range of IP addresses in address/masklength CIDR
3541
notation, or ranges of IPv4 addresses by specifying 3 or less octets with
3542
a trailing dot. Hostnames are not supported, only IPv4 or IPv6 addresses.
3204
3543
This option can be specified multiple times, or can take a list of addresses
3205
separated by commas. Examples:
3544
separated by commas. IPv6 addresses may be (but need not be) enclosed
3545
in square brackets for consistency with option B<--listen>. Examples:
3207
3547
B<-A 10.11.12.13> -- only allow connections from C<10.11.12.13>.