diff options
author | Eric Wong <e@80x24.org> | 2019-06-24 02:52:38 +0000 |
---|---|---|
committer | Eric Wong <e@80x24.org> | 2019-06-24 05:26:26 +0000 |
commit | b70cf61f0c1f70621b88fe6420083a576d47f19f (patch) | |
tree | 88b4cd6fb40e983d7b9a536456067e42b7d890ba /lib/PublicInbox/Daemon.pm | |
parent | 93fc0336d39ba3ef07b479877e64371f07c86eab (diff) | |
download | public-inbox-b70cf61f0c1f70621b88fe6420083a576d47f19f.tar.gz |
It kinda, barely works, and I'm most happy I got it working without any modifications to the main NNTP::event_step callback thanks to the DS->write(CODE) support we inherited from Danga::Socket.
Diffstat (limited to 'lib/PublicInbox/Daemon.pm')
-rw-r--r-- | lib/PublicInbox/Daemon.pm | 82 |
1 files changed, 75 insertions, 7 deletions
diff --git a/lib/PublicInbox/Daemon.pm b/lib/PublicInbox/Daemon.pm index b8d6b572..24c13ad2 100644 --- a/lib/PublicInbox/Daemon.pm +++ b/lib/PublicInbox/Daemon.pm @@ -22,12 +22,48 @@ my (@cfg_listen, $stdout, $stderr, $group, $user, $pid_file, $daemonize); my $worker_processes = 1; my @listeners; my %pids; -my %listener_names; +my %listener_names; # sockname => IO::Handle +my %tls_opt; # scheme://sockname => args for IO::Socket::SSL->start_SSL my $reexec_pid; my $cleanup; my ($uid, $gid); +my ($default_cert, $default_key); END { $cleanup->() if $cleanup }; +sub tls_listen ($$$) { + my ($scheme, $sockname, $opt_str) = @_; + # opt_str: opt1=val1,opt2=val2 (opt may repeat for multi-value) + require PublicInbox::TLS; + my $o = {}; + # allow ',' as delimiter since '&' is shell-unfriendly + foreach (split(/[,&]/, $opt_str)) { + my ($k, $v) = split(/=/, $_, 2); + push @{$o->{$k} ||= []}, $v; + } + + # key may be a part of cert. At least + # p5-io-socket-ssl/example/ssl_server.pl has this fallback: + $o->{cert} //= [ $default_cert ]; + $o->{key} //= defined($default_key) ? [ $default_key ] : $o->{cert}; + my %ctx_opt = (SSL_server => 1); + # parse out hostname:/path/to/ mappings: + foreach my $k (qw(cert key)) { + my $x = $ctx_opt{'SSL_'.$k.'_file'} = {}; + foreach my $path (@{$o->{$k}}) { + my $host = ''; + $path =~ s/\A([^:]+):// and $host = $1; + $x->{$host} = $path; + } + } + my $ctx = IO::Socket::SSL::SSL_Context->new(%ctx_opt) or + die 'SSL_Context->new: '.PublicInbox::TLS::err(); + $tls_opt{"$scheme://$sockname"} = { + SSL_server => 1, + SSL_startHandshake => 0, + SSL_reuse_ctx => $ctx + }; +} + sub daemon_prepare ($) { my ($default_listen) = @_; @CMD = ($0, @ARGV); @@ -42,6 +78,8 @@ sub daemon_prepare ($) { 'u|user=s' => \$user, 'g|group=s' => \$group, 'D|daemonize' => \$daemonize, + 'cert=s' => \$default_cert, + 'key=s' => \$default_key, ); GetOptions(%opts) or die "bad command-line args\n"; @@ -55,6 +93,18 @@ sub daemon_prepare ($) { push @cfg_listen, $default_listen unless (@listeners || @cfg_listen); foreach my $l (@cfg_listen) { + my $orig = $l; + my $scheme = ''; + $l =~ s!\A([^:]+)://!! and $scheme = $1; + if ($l =~ s!/?\?(.+)\z!!) { + tls_listen($scheme, $l, $1); + } elsif (defined($default_cert)) { + tls_listen($scheme, $l, ''); + } elsif ($scheme =~ /\A(?:nntps|https)\z/) { + die "$orig specified w/o cert=\n"; + } + # TODO: use scheme to load either NNTP.pm or HTTP.pm + next if $listener_names{$l}; # already inherited my (%o, $sock_pkg); if (index($l, '/') == 0) { @@ -461,9 +511,26 @@ sub master_loop { exit # never gets here, just for documentation } -sub daemon_loop ($$) { - my ($refresh, $post_accept) = @_; +sub tls_start_cb ($$) { + my ($opt, $orig_post_accept) = @_; + sub { + my ($io, $addr, $srv) = @_; + my $ssl = IO::Socket::SSL->start_SSL($io, %$opt); + $orig_post_accept->($ssl, $addr, $srv); + } +} + +sub daemon_loop ($$$) { + my ($refresh, $post_accept, $nntpd) = @_; PublicInbox::EvCleanup::enable(); # early for $refresh + my %post_accept; + while (my ($k, $v) = each %tls_opt) { + if ($k =~ s!\A(?:nntps|https)://!!) { + $post_accept{$k} = tls_start_cb($v, $post_accept); + } elsif ($nntpd) { # STARTTLS, $k eq '' is OK + $nntpd->{accept_tls} = $v; + } + } my $parent_pipe; if ($worker_processes > 0) { $refresh->(); # preload by default @@ -484,18 +551,19 @@ sub daemon_loop ($$) { $SIG{$_} = 'IGNORE' for qw(USR2 TTIN TTOU WINCH); # this calls epoll_create: @listeners = map { - PublicInbox::Listener->new($_, $post_accept) + PublicInbox::Listener->new($_, + $post_accept{sockname($_)} || $post_accept) } @listeners; PublicInbox::DS->EventLoop; $parent_pipe = undef; } -sub run ($$$) { - my ($default, $refresh, $post_accept) = @_; +sub run ($$$;$) { + my ($default, $refresh, $post_accept, $nntpd) = @_; daemon_prepare($default); daemonize(); - daemon_loop($refresh, $post_accept); + daemon_loop($refresh, $post_accept, $nntpd); } sub do_chown ($) { |