]> git.ipfire.org Git - thirdparty/public-inbox.git/commitdiff
daemon: allow per-listener servername= and serverport=
authorEric Wong <e@80x24.org>
Fri, 28 Mar 2025 10:34:10 +0000 (10:34 +0000)
committerEric Wong <e@80x24.org>
Sun, 30 Mar 2025 18:19:36 +0000 (18:19 +0000)
Being able to specify per-listener servername= with the
`--listen' CLI arg is helpful for running an Tor .onion NNTP
server with the same config file (and thus
publicinbox.nntpserver) as a public-facing NNTP endpoint.

For HTTP, servername= and serverport= allow overriding the
SERVER_NAME and SERVER_PORT PSGI environment variables,
respectively.  These are useful for generating full URLs
for clients which don't send the HTTP `Host:' header.

These currently have no effect aside from wasting memory for
POP3 and IMAP listeners.  serverport= isn't used by NNTP,
either.

Documentation/public-inbox-config.pod
Documentation/public-inbox-daemon.pod
Documentation/public-inbox-netd.pod
lib/PublicInbox/Daemon.pm
lib/PublicInbox/HTTPD.pm
lib/PublicInbox/NNTPD.pm
t/httpd.t
t/nntpd.t

index faf35d673a1e9882a7d4e41db870edc8444ca051..0e35696140f25e2c3b3b7ae07b029d11c2efd97b 100644 (file)
@@ -330,7 +330,10 @@ Default: none
 =item publicinbox.nntpserver
 
 Same as C<publicinbox.imapserver>, but for the hostname(s) of the
-L<public-inbox-nntpd(1)> instance.
+L<public-inbox-nntpd(1)> instance.  This affects C<Xref:> headers
+in responses.  As of public-inbox 2.0, this can be overriden on
+the C<public-inbox-netd(1)> command-line via
+C<--listen nntp://$ADDRESS:$PORT/?servername=example.com>
 
 Default: none
 
index 092be667bb53eadb8b70771e165b703ece8a76f8..7496cfca4ff81224f41b763e5e5c76ab175763a5 100644 (file)
@@ -51,7 +51,8 @@ all if relying on L<systemd.socket(5)> or similar,
 Per-listener options may be specified after C<?> as C<KEY=VALUE>
 pairs delimited by C<,>.  See L<public-inbox-netd(1)> for
 documentation on the C<cert=>, C<key=>, C<env.NAME=VALUE>,
-C<out=>, C<err=>, and C<psgi=> options available.
+C<out=>, C<err=>, C<psgi=>, C<servername=>, and C<serverport=>
+options available.
 
 Default: server-dependent unless socket activation is used with
 L<systemd(1)> or similar (see L<systemd.socket(5)>).
index 71425e3cba15e53242101cc65a9dab87127f78bc..bbf0eb448fb1f7a5e218eb96cae7cd34db1d0fa4 100644 (file)
@@ -50,6 +50,14 @@ C<stdout>.  HTTP(S) users are encouraged to configure
 L<Plack::Middleware::AccessLog> or
 L<Plack::Middleware::AccessLog::Timed>, instead.
 
+As of public-inbox 2.0, C<servername=> and C<serverport=> may
+also be specified on a per listener basis.  For HTTP(S),
+these ensure full URLs are generated properly for HTTP clients
+which omit the HTTP C<Host:> header.  For NNTP(S), C<servername=>
+is printed in the the greeting and used for generating C<Xref:>
+headers, overriding the L<public-inbox-config(5)/publicinbox.nntpserver>
+directive.
+
 =item --cert /path/to/cert
 
 See L<public-inbox-daemon(1)>.
index 17abf01d4dbad7ffb630dc223bb09ef44d35b155..cb13a4cc00250a262c80e5d4a8399b77d28aa2d0 100644 (file)
@@ -149,6 +149,15 @@ sub load_mod ($;$$) {
                $tlsd->{$f} = $logs{$p} //= open_log_path(my $fh, $p);
                warn "# $scheme://$addr $f=$p\n";
        }
+       for my $f (qw(servername serverport)) {
+               my $p = $opt->{$f} or next;
+               die "multiple $f= options specified\n" if @$p > 1;
+               if ($p->[0] eq '') {
+                       warn "W: empty $f= ignored\n";
+               } else {
+                       $tlsd->{$f} = $p->[0];
+               }
+       }
        # for per-listener $SIG{__WARN__}:
        my $err = $tlsd->{err};
        $tlsd->{warn_cb} = sub {
index 38b2e4f9a662ce2380af6ba75228e7a4f111c0a8..55d728cbd4bafba82f503526234e63d8e237fa46 100644 (file)
@@ -21,8 +21,8 @@ sub env_for ($$$) {
        my $n = getsockname($srv) or die "not a socket: $srv $!\n";
        my ($host, $port) = PublicInbox::Daemon::host_with_port($n);
        {
-               SERVER_NAME => $host,
-               SERVER_PORT => $port,
+               SERVER_NAME => $self->{servername} // $host,
+               SERVER_PORT => ($self->{serverport} // $port) + 0,
                SCRIPT_NAME => '',
                'psgi.version' => [ 1, 1 ],
                'psgi.errors' => $self->{err},
index 4401a29b963fed06abcb670a93a8cc16f55defc9..7e0459beb0eb3e24ddaf361e5eb6ecc64ba41b68 100644 (file)
@@ -18,6 +18,7 @@ sub new {
                out => \*STDOUT,
                # pi_cfg => $pi_cfg,
                # ssl_ctx_opt => { SSL_cert_file => ..., SSL_key_file => ... }
+               # servername => str
                # idler => PublicInbox::InboxIdle
        }, $class;
 }
@@ -25,16 +26,11 @@ sub new {
 sub refresh_groups {
        my ($self, $sig) = @_;
        my $pi_cfg = PublicInbox::Config->new;
-       my $name = $pi_cfg->{'publicinbox.nntpserver'};
-       if (!defined($name) or $name eq '') {
-               $name = hostname;
-       } elsif (ref($name) eq 'ARRAY') {
-               $name = $name->[0];
-       }
-       if ($name ne ($self->{servername} // '')) {
-               $self->{servername} = $name;
-               $self->{greet} = \"201 $name ready - post via email\r\n";
-       }
+       $self->{servername} //= $pi_cfg->{'publicinbox.nntpserver'} // '';
+       ref($self->{servername}) eq 'ARRAY' and
+               $self->{servername} = $self->{servername}->[0];
+       $self->{servername} = hostname if $self->{servername} eq '';
+       $self->{greet} = \"201 $self->{servername} ready - post via email\r\n";
        my $groups = $pi_cfg->{-by_newsgroup}; # filled during each_inbox
        my $cache = eval { $pi_cfg->ALL->misc->nntpd_cache_load } // {};
        $pi_cfg->each_inbox(sub {
index 39382b85f61de644600eabfcf67931f93a3ef13a..36b076adee362e8328fe9da580adc3d615230762 100644 (file)
--- a/t/httpd.t
+++ b/t/httpd.t
@@ -5,6 +5,7 @@ use strict;
 use v5.10.1;
 use PublicInbox::TestCommon;
 use PublicInbox::Eml;
+use PublicInbox::IO;
 use Socket qw(IPPROTO_TCP SOL_SOCKET);
 require_mods '-httpd';
 require_git_http_backend;
@@ -18,7 +19,9 @@ my $inboxdir = "$tmpdir/i.git";
 my $group = 'test-httpd';
 my $addr = $group . '@example.com';
 my $sock = tcp_server();
+my $hostname = 'bogus-test.example.com';
 my $td;
+my $msgid = 'httpd-test@example.com';
 {
        create_inbox 'test', tmpdir => $inboxdir, sub {
                my ($im, $ibx) = @_;
@@ -26,7 +29,7 @@ my $td;
 From: Me <me\@example.com>
 To: You <you\@example.com>
 Cc: $addr
-Message-Id: <nntp\@example.com>
+Message-Id: <$msgid>
 Subject: hihi
 Date: Thu, 01 Jan 1970 06:06:06 +0000
 
@@ -40,9 +43,10 @@ EOF
        local $ENV{HOME} = $home;
        my $cmd = [ '-init', $group, $inboxdir, 'http://example.com/', $addr ];
        ok(run_script($cmd), 'init ran properly');
-       $cmd = [ '-httpd', '-W0', "--stdout=$out", "--stderr=$err" ];
-       $td = start_script($cmd, undef, { 3 => $sock });
        my $http_pfx = 'http://'.tcp_host_port($sock);
+       $cmd = [ '-httpd', '-W0', "--stdout=$out", "--stderr=$err",
+               "-l$http_pfx?servername=$hostname&serverport=80" ];
+       $td = start_script($cmd, undef, { 3 => $sock });
        {
                my $bad = tcp_connect($sock);
                print $bad "GETT / HTTP/1.0\r\n\r\n" or die;
@@ -57,6 +61,12 @@ EOF
                is($conn->read($buf, 1), 0, "EOF");
        }
 
+       $conn = tcp_connect($sock);
+       print $conn "GET /$group/$msgid HTTP/1.0\r\n\r\n" or xbail $!;
+       my $buf = PublicInbox::IO::read_all($conn);
+       like $buf, qr!\nLocation:\x20http://\Q$hostname\E/$group/$msgid/\r\n!s,
+               'redirect used SERVER_{NAME,PORT} from CLI overrides';
+
        is(xsys(qw(git clone -q --mirror),
                        "$http_pfx/$group", "$tmpdir/clone.git"),
                0, 'smart clone successful');
@@ -81,7 +91,7 @@ EOM
        close $fh or xbail "close $!";
        $td->kill('HUP') or BAIL_OUT "failed to kill -httpd: $!";
        tick; # wait for HUP to take effect
-       my $buf = do {
+       $buf = do {
                my $c2 = tcp_connect($sock);
                $c2->write("GET /test-2/qp\@example.com/raw HTTP/1.0\r\n\r\n")
                                        or xbail "c2 write: $!";
index 1b2a9fa9bbd59d2e39dd7962018f77d4d6a4dfa0..220003ffb79889c353286cc828001685b469520b 100644 (file)
--- a/t/nntpd.t
+++ b/t/nntpd.t
@@ -5,10 +5,10 @@ use strict; use v5.10.1; use PublicInbox::TestCommon;
 require_mods(qw(DBD::SQLite Net::NNTP));
 use PublicInbox::Eml;
 use Socket qw(IPPROTO_TCP TCP_NODELAY);
-use Sys::Hostname;
 use POSIX qw(_exit);
 use PublicInbox::SHA;
 use PublicInbox::DS;
+my $hostname = 'bogus-test.example.com';
 
 # t/nntpd-v2.t wraps this for v2
 my $version = $ENV{PI_TEST_VERSION} || 1;
@@ -84,7 +84,8 @@ EOF
 close $cfgfh or BAIL_OUT;
 
 {
-       my $cmd = [ '-nntpd', '-W0', "--stdout=$out", "--stderr=$err" ];
+       my $cmd = [ '-nntpd', '-W0', "--stdout=$out", "--stderr=$err",
+               "-lnntp://$host_port?servername=$hostname" ];
        $td = start_script($cmd, undef, { 3 => $sock });
        my $n = Net::NNTP->new($host_port);
        my $list = $n->list;
@@ -95,7 +96,7 @@ close $cfgfh or BAIL_OUT;
        # TODO: Net::NNTP::listgroup does not support range at the moment
        my $s = tcp_connect($sock);
        sysread($s, my $buf, 4096);
-       is($buf, "201 " . hostname . " ready - post via email\r\n",
+       is($buf, "201 " . $hostname . " ready - post via email\r\n",
                'got greeting');
        syswrite($s, "LISTGROUP $group 1-1\r\n");
        $buf = read_til_dot($s);
@@ -123,13 +124,13 @@ close $cfgfh or BAIL_OUT;
                'from' => "El\xc3\xa9anor <me\@example.com>",
                'to' => "El\xc3\xa9anor <you\@example.com>",
                'cc' => $addr,
-               'xref' => hostname . " $group:1",
+               'xref' => $hostname . " $group:1",
                'references' => '<reftabsqueezed>',
        );
 
        $s = tcp_connect($sock);
        sysread($s, $buf, 4096);
-       is($buf, "201 " . hostname . " ready - post via email\r\n",
+       is($buf, "201 " . $hostname . " ready - post via email\r\n",
                'got greeting');
 
        ok(syswrite($s, "   \r\n"), 'wrote spaces');
@@ -149,7 +150,7 @@ close $cfgfh or BAIL_OUT;
 
        $s = tcp_connect($sock);
        sysread($s, $buf, 4096);
-       is($buf, "201 " . hostname . " ready - post via email\r\n",
+       is($buf, "201 " . $hostname . " ready - post via email\r\n",
                'got greeting');
 
        syswrite($s, "CAPABILITIES\r\n");
@@ -199,7 +200,7 @@ close $cfgfh or BAIL_OUT;
                        '<reftabsqueezed>',
                        $len,
                        '1',
-                       'Xref: '. hostname . ' test-nntpd:1'] },
+                       'Xref: '. $hostname . ' test-nntpd:1'] },
                "XOVER range works");
 
        is_deeply($n->xover('1'), {
@@ -210,7 +211,7 @@ close $cfgfh or BAIL_OUT;
                        '<reftabsqueezed>',
                        $len,
                        '1',
-                       'Xref: '. hostname . ' test-nntpd:1'] },
+                       'Xref: '. $hostname . ' test-nntpd:1'] },
                "XOVER by article works");
 
        is_deeply($n->head(1), $n->head('<nntp@example.com>'), 'HEAD OK');
@@ -233,7 +234,7 @@ close $cfgfh or BAIL_OUT;
                        "El\xc3\xa9anor <me\@example.com>\t" .
                        "Thu, 01 Jan 1970 06:06:06 +0000\t" .
                        "$mid\t<reftabsqueezed>\t$len\t1" .
-                       "\tXref: " . hostname . " test-nntpd:0",
+                       "\tXref: " . $hostname . " test-nntpd:0",
                        'OVER by Message-ID works');
                is($r[2], '.', 'correctly terminated response');
        }