#!/usr/bin/perl
#
# SSL port forwarder for Penn by Raevnos. Allows SSL connections to
# persist over a @shutdown/reboot.
#
# Requires socat.
#
# Invoke like so: perl ssl_proxy.pl mush.cnf ssl-port
#
# Configuration
# In mush.cnf, set ssl_port to 0 so that the mush doesn't try to set
# up a ssl listener, but fill in the ssl_private_key_file,
# ssl_ca_file, and ssl_require_client_cert options.  ssl_ip_addr can
# be blank.
#
#
# This is mostly a proof of concept, as it works but has a major
# issue: all SSL connections end up appearing to come from localhost,
# which makes @sitelock restrictions useless. The proxy that will be
# someday be part of penn will include a way to pass on the original
# host, but the principal will be the same: A second process listens
# for SSL connections and for each one, makes a plain connection to
# the mush and relays between the two. The implementation will be
# different.

use v5.10.0;
use strict;
use feature qw/switch say/;

my $USAGE = "$0 MUSH-CNF-FILE SSL-PORT\n";

my $cnffile = shift;
die $USAGE unless defined $cnffile;

my $sslport = shift;
die $USAGE unless defined $sslport;

my ($ssl_private_key_file, $ssl_ca_file, $ssl_ip_addr, $ssl_require_client_cert) = (undef, undef, undef, 0);
my ($mush_ip_addr, $mush_port) = ("localhost", 4201);

open CNF, "<", $cnffile or die "Unable to open ${cnffile}: $!\n";
while (<CNF>) {
    chomp;
    given ($_) {
	when (/^port\s+(\d+)/) { $mush_port = $1; }
	when (/^ip_addr\s+(\S+)/) { $mush_ip_addr = $1; } 
	when (/^ssl_ip_addr\s+(\S+)/) { $ssl_ip_addr = $1; }
	when (/^ssl_private_key_file\s+(.+)/) { $ssl_private_key_file = $1; }
	when (/^ssl_ca_file\s+(.+)/) { $ssl_ca_file = $1; }
	when (/^ssl_require_client_cert\s+no/) { $ssl_require_client_cert = 0; }
	when (/^ssl_require_client_cert\s+yes/) { $ssl_require_client_cert = 1; }
    }
}
close CNF;
    
$mush_ip_addr =~ s/:/\\:/g;
$ssl_ip_addr =~ s/:/\\:/g if defined $ssl_ip_addr;

my $mush_side = "TCP:${mush_ip_addr}:${mush_port}";

my $ssl_side = "OPENSSL-LISTEN:${sslport},verify=${ssl_require_client_cert}";
$ssl_side .= ",cert=${ssl_private_key_file}" if defined $ssl_private_key_file;
$ssl_side .= ",cafile=${ssl_ca_file}" if defined $ssl_ca_file;
$ssl_side .= ",fork,reuseaddr";
$ssl_side .= ",bind=$ssl_ip_addr" if defined $ssl_ip_addr;

say "Listening on port $sslport for SSL connections.";
say "Using cert file $ssl_private_key_file and ca file $ssl_ca_file";
say "Forwarding to ${mush_ip_addr}:${mush_port}";

# say "Executing: socat \"$ssl_side\" \"$mush_side\"";

exec "socat", $ssl_side, $mush_side or
    die "Couldn't run socat: $!\n";
