#!/usr/bin/perl
#
# Module: vyatta-update-sslh.pl
#
# **** License ****
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# This code was originally developed by Ubiquiti, Inc.
# Portions created by Ubiquiti are Copyright (C) 2011 Ubiquiti, Inc.
# All Rights Reserved.
#
# This script is derived from ubnt-igmpproxy configuration script by:
#   Author: Stig Thormodsrud <eng@ubnt.com>
#   Date: July 2011
#   Description: Script to configure igmpproxy
#
# Author: Dominik Malis <d.malis@sh.cvut.cz>
# Author: Bronislav Robenek <brona@robenek.me>
# Date: July 2014
# Description: Script to configure sslh
#
# **** End License ****

use Getopt::Long;
use POSIX;
use File::Basename;
use File::Compare;
use NetAddr::IP;

use lib '/opt/vyatta/share/perl5';
use Vyatta::Config;
use Vyatta::Interface;

use warnings;
use strict;

my $daemon   = '/usr/sbin/sslh';
my $pidfile  = '/var/run/sslh.pid';
my $conffile = '/etc/sslh.conf';

sub is_running {
    my ($pid_file) = @_;

    if (defined $pid_file and -f $pid_file) {
	my $pid = `cat $pid_file`;
	chomp $pid;
	my $ps = `ps -p $pid -o comm=`;

	if (defined($ps) && $ps ne "") {
	    return 1;
	}
    }
    return 0;
}

sub is_disabled {
    my $config = new Vyatta::Config;
    $config->setLevel('service ssl-port-sharing');
    return 1 if $config->exists('disable');
    return;
}

sub is_same_as_file {
    my ($file, $value) = @_;

    return if ! -e $file;

    my $mem_file = '';
    open my $MF, '+<', \$mem_file or die "couldn't open memfile $!\n";
    print $MF $value;
    seek($MF, 0, 0);

    my $rc = compare($file, $MF);
    return 1 if $rc == 0;
    return;
}

sub write_conf_file {
    my ($file, $config) = @_;

    # Avoid unnecessary writes.  At boot the file will be the
    # regenerated with the same content.
    return if is_same_as_file($file, $config);

    open(my $fh, '>', $file) || die "Couldn't open $file - $!";
    print $fh $config;
    close $fh;
    return 1;
}

sub validate_config {
    my $config = new Vyatta::Config;

    my $path = 'service ssl-port-sharing';
    $config->setLevel($path);

    my @protocols = $config->listNodes('protocol');
    if (scalar(@protocols) < 1) {
        print "Error: must define at least 1 protocol\n";
        exit 1;
    }
    my $upstream = 0;
    foreach my $prot (@protocols) {
        $config->setLevel("$path protocol $prot");
        if (! $config->exists('address')) {
            print "Error: must define address for protocol $prot\n";
            exit 1;
        }
        if (! $config->exists('port')) {
            print "Error: must define port for protocol $prot\n";
            exit 1;
        }
    }
}

sub get_config_static {
    my $output = '';

    $output .= "#\n# autogenerated by $0\n#\n";
    $output .= "\n";
    $output .= "user: \"nobody\";\n";
}

sub get_config {

    my $config = new Vyatta::Config;
    my $output = get_config_static();

    my $path = 'service ssl-port-sharing';
    $config->setLevel($path);

    $output .= "\n";
    $output .= "listen:\n";
    $output .= "(\n";
    if ($config->exists('port')) {
        my $port = $config->returnValue('port');
        $output .= "    { host: \"0.0.0.0\"; port: \"$port\"; }\n";
    } else {
        $output .= "    { host: \"0.0.0.0\"; port: \"443\"; }\n";
    }
    $output .= ");\n\n";

    $output .= "protocols:\n";
    $output .= "(\n";
    my @protocols = $config->listNodes('protocol');
    my $n = $#protocols;
    foreach my $prot (@protocols) {
        $config->setLevel("$path protocol $prot");
        my $address = $config->returnValue('address');
        my $port    = $config->returnValue('port');

        $output .= "    { name: \"$prot\"; host: \"$address\"; port: \"$port\"; probe: \"builtin\"; }";
        if ($n--) {
            $output .= ",";
        }
        $output .= "\n";
    }
    $output .= ");\n";

    return $output;
}

sub load_mode {
    my $config = new Vyatta::Config;
    my $path = 'service ssl-port-sharing';
    $config->setLevel($path);
    if ($config->exists('mode') && ($config->returnValue('mode') eq "fork")) {
        # In debian sslh -> sslh-fork
        if ( -x "$daemon-fork" ) {
            $daemon .= "-fork";
        }
    } else {
        $daemon .= "-select";
    }
}

sub enable_sslh {

    if (is_disabled()) {
        if (is_running($pidfile)) {
            disable_sslh();
        }
        exit 0;
    }

    validate_config();
    my $output = get_config();
    write_conf_file($conffile, $output);

    my ($cmd, $rc) = ('', '');
    if (is_running($pidfile)) {
        disable_sslh();
    }

    load_mode();
    $cmd  = "/sbin/start-stop-daemon --start --startas $daemon --make-pidfile ";
    $cmd .= "--pidfile $pidfile --background -- -f -F $conffile";
    #print $cmd;
    print "Starting sslh\n";
    $rc = system($cmd);

    return $rc;
}

sub disable_sslh {
    my ($intf) = @_;

    if (! is_running($pidfile)) {
        print "Warning: sslh not running.\n";
        exit 0;
    }

    my ($cmd, $rc) = ('', '');
    $cmd  = "/sbin/start-stop-daemon --stop --pidfile $pidfile";
    #print $cmd;
    print "Stopping sslh\n";
    $rc = system($cmd);

    return $rc;
}


#
# main
#

my ($action);

GetOptions("action=s"    => \$action,
) or usage();

die "Must define action\n" if ! defined $action;

my $rc = 1;
$rc =  enable_sslh()  if $action eq 'enable';
$rc =  disable_sslh() if $action eq 'disable';

exit $rc;

# end of file
