#!/bin/sh /etc/rc.common
#
# Copyright (C) 2010-2015 OpenWrt.org
# Copyright (C) 2010 segal.di.ubi.pt
#

START=99
STOP=01
USE_PROCD=1

PROG=/usr/bin/ssh

_log() {
	logger -p daemon.info -t sshtunnel "$@"
}

_err() {
	logger -p daemon.err -t sshtunnel "$@"
}

append_string() {
	local varname="$1"; local add="$2"; local separator="${3:- }"; local actual new
	eval "actual=\$$varname"

	new="${actual:+$actual$separator}$add"
	eval "$varname=\$new"
}

validate_server_section() {
	uci_load_validate sshtunnel server "$1" "$2" \
		'user:string(1)' \
		'hostname:host' \
		'port:port' \
		'retrydelay:min(1):60' \
		'PKCS11Provider:file' \
		'CheckHostIP:or("yes", "no")' \
		'Compression:or("yes", "no")' \
		'IdentityFile:file' \
		'LogLevel:or("QUIET", "FATAL", "ERROR", "INFO", "VERBOSE", "DEBUG", "DEBUG1", "DEBUG2", "DEBUG3")' \
		'ServerAliveCountMax:min(1)' \
		'ServerAliveInterval:min(0)' \
		'StrictHostKeyChecking:or("yes", "no", "accept-new"):accept-new' \
		'TCPKeepAlive:or("yes", "no")' \
		'VerifyHostKeyDNS:or("yes", "no")' \
		'ProxyCommand:string(1)'
}

validate_tunnelR_section() {
	uci_load_validate sshtunnel tunnelR "$1" "$2" \
		'enabled:bool:1' \
		'remoteaddress:or(host, "*")' \
		'remoteport:port' \
		'localaddress:host' \
		'localport:port'
}

validate_tunnelL_section() {
	uci_load_validate sshtunnel tunnelL "$1" "$2" \
		'enabled:bool:1' \
		'remoteaddress:host' \
		'remoteport:port' \
		'localaddress:or(host, "*")' \
		'localport:port'
}

validate_tunnelD_section() {
	uci_load_validate sshtunnel tunnelD "$1" "$2" \
		'enabled:bool:1' \
		'localaddress:or(host, "*")' \
		'localport:port'
}

validate_tunnelW_section() {
	uci_load_validate sshtunnel tunnelW "$1" "$2" \
		'enabled:bool:1' \
		'vpntype:or("ethernet", "point-to-point"):point-to-point' \
		'localdev:or("any", min(0))' \
		'remotedev:or("any", min(0))'
}

load_tunnelR() {
	config_get section_server "$1" "server"
	[ "$enabled" = 0 ] && return 0

	# continue to read next section if this is not for the current server
	[ "$server" = "$section_server" ] || return 0

	# validate and load this remote tunnel config
	[ "$2" = 0 ] || { _err "tunnelR $1: validation failed"; return 1; }

	[ -n "$remoteport" -a -n "$localport" ] || { _err "tunnelR $1: missing required options"; return 1; }

	# count nr of valid sections to make sure there are at least one
	count=$((count+=1))

	_log "tunnelR at $server: -R $remoteaddress:$remoteport:$localaddress:$localport"
	append_string "ARGS_tunnels" "-R $remoteaddress:$remoteport:$localaddress:$localport"
}

load_tunnelL() {
	config_get section_server "$1" "server"
	[ "$enabled" = 0 ] && return 0

	# continue to read next section if this is not for the current server
	[ "$server" = "$section_server" ] || return 0

	# validate and load this remote tunnel config
	[ "$2" = 0 ] || { _err "tunnelL $1: validation failed"; return 1; }

	[ -n "$remoteport" -a -n "$localport" ] || { _err "tunnelL $1: missing required options"; return 1; }

	# count nr of valid sections to make sure there are at least one
	count=$((count+=1))

	_log "tunnelL at $server: -L $localaddress:$localport:$remoteaddress:$remoteport"
	append_string "ARGS_tunnels" "-L $localaddress:$localport:$remoteaddress:$remoteport"
}

load_tunnelD() {
	config_get section_server "$1" "server"
	[ "$enabled" = 0 ] && return 0

	# continue to read next section if this is not for the current server
	[ "$server" = "$section_server" ] || return 0

	# validate and load this remote tunnel config
	[ "$2" = 0 ] || { _err "tunnelD $1: validation failed"; return 1; }

	[ -n "$localport" ] || { _err "tunnelD $1: missing localport"; return 1; }

	# count nr of valid sections to make sure there are at least one
	count=$((count+=1))

	_log "proxy via $server: -D $localaddress:$localport"
	append_string "ARGS_tunnels" "-D $localaddress:$localport"
}

load_tunnelW() {
	config_get section_server "$1" "server"
	[ "$enabled" = 0 ] && return 0

	# continue to read next section if this is not for the current server
	[ "$server" = "$section_server" ] || return 0

	# validate and load this remote tunnel config
	[ "$2" = 0 ] || { _err "tunnelW $1: validation failed"; return 1; }

	[ -n "$vpntype" -a -n "$localdev" -a -n "$remotedev" ] || { _err "tunnelW $1: missing or bad options"; return 1; }

	[ "$user" = "root" ] || { _err "tunnelW $1: root is required for tun"; return 1; }

	# count nr of valid sections to make sure there are at least one
	count=$((count+=1))

	_log "tunnelW to $server: -o Tunnel=$vpntype -w $localdev:$remotedev"
	append_string "ARGS_tunnels" "-o Tunnel=$vpntype -w $localdev:$remotedev"
}

load_server() {
	local server="$1"

	[ "$2" = 0 ] || { _err "server $server: validation failed"; return 1; }

	local ARGS_tunnels=""
	local count=0

	config_foreach validate_tunnelR_section "tunnelR" load_tunnelR
	config_foreach validate_tunnelL_section "tunnelL" load_tunnelL
	config_foreach validate_tunnelD_section "tunnelD" load_tunnelD
	config_foreach validate_tunnelW_section "tunnelW" load_tunnelW
	[ "$count" -eq 0 ] && { _err "tunnels to $server not started - no tunnels defined"; return 1; }

	# old dbclient use -y for StrictHostKeyChecking.
	# The -y for OpenSSH means to use syslog but that's ok
	local db_StrictHostKeyChecking=""
	[ "$StrictHostKeyChecking" = "accept-new" ] && db_StrictHostKeyChecking="-y"
	[ "$StrictHostKeyChecking" = "no" ] && db_StrictHostKeyChecking="-yy"

	local ARGS="$hostname $ARGS_tunnels \
	${port:+-p $port} \
	${user:+-l $user} \
	${IdentityFile:+-i $IdentityFile} \
	${CheckHostIP:+-o CheckHostIP=$CheckHostIP} \
	${VerifyHostKeyDNS:+-o VerifyHostKeyDNS=$VerifyHostKeyDNS} \
	${Compression:+-o Compression=$Compression} \
	${LogLevel:+-o LogLevel=$LogLevel} \
	${PKCS11Provider:+-o PKCS11Provider=$PKCS11Provider} \
	${TCPKeepAlive:+-o TCPKeepAlive=$TCPKeepAlive} \
	${ServerAliveCountMax:+-o ServerAliveCountMax=$ServerAliveCountMax} \
	${ServerAliveInterval:+-o ServerAliveInterval=$ServerAliveInterval} \
	${StrictHostKeyChecking:+-o StrictHostKeyChecking=$StrictHostKeyChecking $db_StrictHostKeyChecking} \
	-o ExitOnForwardFailure=yes -o BatchMode=yes -nN \
	"

	procd_open_instance "$server"
	procd_set_param command "$PROG" $ARGS
	# ProxyCommand must be quoted
	[ -n "$ProxyCommand" ] && procd_append_param command -o "ProxyCommand=$ProxyCommand"

	procd_set_param stdout 1
	procd_set_param stderr 1
	procd_set_param respawn 0 "$retrydelay" 1
	procd_close_instance
}

start_service() {
	config_load "sshtunnel"
	config_foreach validate_server_section "server" load_server
}

service_triggers() {
	procd_add_reload_trigger "sshtunnel"

	procd_open_validate
	validate_server_section
	validate_tunnelR_section
	validate_tunnelL_section
	validate_tunnelD_section
	validate_tunnelW_section
	procd_close_validate
}