| #!/bin/sh /etc/rc.common |
| # Copyright (C) 2006-2008 OpenWrt.org |
| # Copyright (C) 2019 Jeffery To |
| |
| START=90 |
| USE_PROCD=1 |
| |
| PID_FILE="/var/run/stunnel/stunnel.pid" |
| CONF_FILE="/var/etc/stunnel.conf" |
| BIN="/usr/bin/stunnel" |
| CONF_FILE_CREATED= |
| HAVE_ALT_CONF_FILE= |
| SERVICE_SECTION_FOUND= |
| |
| validate_globals_section() { |
| uci_load_validate stunnel globals "$1" "$2" \ |
| 'alt_config_file:file' \ |
| \ |
| 'compression:or("deflate","zlib")' \ |
| 'EGD:string' \ |
| 'engine:string' \ |
| 'engineCtrl:string' \ |
| 'engineDefault:list(or("ALL","CIPHERS","DH","DIGESTS","DSA","ECDH","ECDSA","PKEY","PKEY_ASN1","PKEY_CRYPTO","RAND","RSA"))' \ |
| 'log:or("append","overwrite")' \ |
| 'output:string' \ |
| 'RNDbytes:uinteger' \ |
| 'RNDfile:string' \ |
| 'RNDoverwrite:bool' \ |
| 'setgid:or(string,uinteger)' \ |
| 'setuid:or(string,uinteger)' \ |
| 'syslog:bool' \ |
| ; |
| } |
| |
| validate_service_section() { |
| uci_load_validate stunnel service "$1" "$2" \ |
| 'enabled:bool:1' \ |
| \ |
| 'setgid:or(string,uinteger)' \ |
| 'setuid:or(string,uinteger)' \ |
| ; |
| } |
| |
| validate_service_options() { |
| uci_load_validate stunnel "$1" "$2" "$3" \ |
| 'accept_host:host' \ |
| 'accept_port:port' \ |
| 'CAfile:string' \ |
| 'CApath:string' \ |
| 'cert:string' \ |
| 'checkEmail:list(string)' \ |
| 'checkHost:list(host)' \ |
| 'checkIP:list(ipaddr)' \ |
| 'ciphers:list(string)' \ |
| 'ciphersuites:list(string)' \ |
| 'client:bool' \ |
| 'config:list(string)' \ |
| 'connect:list(string)' \ |
| 'CRLfile:string' \ |
| 'CRLpath:string' \ |
| 'curves:list(string)' \ |
| 'debug:or(range(0,7),string)' \ |
| 'delay:bool' \ |
| 'engineId:string' \ |
| 'engineNum:and(uinteger,min(1))' \ |
| 'exec:string' \ |
| 'execArgs:string' \ |
| 'failover:or("prio","rr")' \ |
| 'ident:string' \ |
| 'include:directory' \ |
| 'key:string' \ |
| 'local:host' \ |
| 'logId:or("process","sequential","thread","unique")' \ |
| 'OCSP:string' \ |
| 'OCSPaia:bool' \ |
| 'OCSPflag:list(or("NOCASIGN","NOCERTS","NOCHAIN","NOCHECKS","NODELEGATED","NOEXPLICIT","NOINTERN","NOSIGS","NOTIME","NOVERIFY","RESPID_KEY","TRUSTOTHER"))' \ |
| 'OCSPnonce:bool' \ |
| 'options:list(string) ' \ |
| 'protocol:or("cifs","connect","imap","nntp","pgsql","pop3","proxy","smtp","socks")' \ |
| 'protocolAuthentication:or("basic","login","ntlm","plain")' \ |
| 'protocolDomain:hostname' \ |
| 'protocolHost_host:host' \ |
| 'protocolHost_port:port' \ |
| 'protocolPassword:string' \ |
| 'protocolUsername:string' \ |
| 'PSKidentity:string' \ |
| 'PSKsecrets:string' \ |
| 'pty:bool' \ |
| 'redirect_host:host' \ |
| 'redirect_port:port' \ |
| 'renegotiation:bool' \ |
| 'requireCert:bool' \ |
| 'reset:bool' \ |
| 'retry:bool' \ |
| 'service:string' \ |
| 'sessionCacheSize:uinteger' \ |
| 'sessionCacheTimeout:uinteger' \ |
| 'sessiond_host:host' \ |
| 'sessiond_port:port' \ |
| 'sni:list(string)' \ |
| 'socket:list(string)' \ |
| 'sslVersion:or("all","SSLv2","SSLv3","TLSv1","TLSv1.1","TLSv1.2")' \ |
| 'stack:uinteger' \ |
| 'ticketKeySecret:string' \ |
| 'ticketMacSecret:string' \ |
| 'TIMEOUTbusy:uinteger' \ |
| 'TIMEOUTclose:uinteger' \ |
| 'TIMEOUTconnect:uinteger' \ |
| 'TIMEOUTidle:uinteger' \ |
| 'transparent:or("both","destination","none","source")' \ |
| 'verifyChain:bool' \ |
| 'verifyPeer:bool' \ |
| ; |
| } |
| |
| validate_globals_section_service_options() { |
| validate_service_options globals "$@" |
| } |
| |
| validate_service_section_service_options() { |
| validate_service_options service "$@" |
| } |
| |
| print_options() { |
| local _opt |
| local _value |
| for _opt in "$@"; do |
| eval "_value=\$$_opt" |
| [ -z "$_value" ] || echo "$_opt = $_value" >> "$CONF_FILE" |
| done |
| } |
| |
| print_bool_options() { |
| local _opt |
| local _bool |
| local _value |
| for _opt in "$@"; do |
| eval "_bool=\$$_opt" |
| [ -z "$_bool" ] || { |
| _value=no |
| [ "$_bool" != 1 ] || _value=yes |
| echo "$_opt = $_value" >> "$CONF_FILE" |
| } |
| done |
| } |
| |
| print_lists_map() { |
| local _opt |
| local _values |
| local _value |
| for _opt in "$@"; do |
| eval "_values=\$$_opt" |
| for _value in $_values; do |
| echo "$_opt = $_value" >> "$CONF_FILE" |
| done |
| done |
| } |
| |
| print_lists_reduce() { |
| local _delim="$1" |
| local _opt |
| local _value |
| local _values |
| local _v |
| shift |
| for _opt in "$@"; do |
| _value= |
| eval "_values=\$$_opt" |
| for _v in $_values; do |
| _value=$_value$_delim$_v |
| done |
| _value=${_value#$_delim} |
| [ -z "$_value" ] || echo "$_opt = $_value" >> "$CONF_FILE" |
| done |
| } |
| |
| print_host_port() { |
| local _opt |
| local _host |
| local _port |
| for _opt in "$@"; do |
| eval "_host=\${${_opt}_host}" |
| eval "_port=\${${_opt}_port}" |
| [ -z "$_host" ] || [ -z "$_port" ] || echo "$_opt = $_host:$_port" >> "$CONF_FILE" |
| done |
| } |
| |
| print_optional_host_port() { |
| local _opt |
| local _host |
| local _port |
| local _value |
| for _opt in "$@"; do |
| eval "_host=\${${_opt}_host}" |
| eval "_port=\${${_opt}_port}" |
| [ -z "$_port" ] || { |
| _value=$_port |
| [ -z "$_host" ] || _value=$_host:$_port |
| echo "$_opt = $_value" >> "$CONF_FILE" |
| } |
| done |
| } |
| |
| print_global_options() { |
| print_options \ |
| compression \ |
| EGD \ |
| engine \ |
| engineCtrl \ |
| log \ |
| output \ |
| RNDbytes \ |
| RNDfile \ |
| RNDoverwrite \ |
| ; |
| |
| print_bool_options \ |
| syslog \ |
| ; |
| |
| print_lists_reduce , \ |
| engineDefault \ |
| ; |
| } |
| |
| print_service_options() { |
| [ "$2" = 0 ] || { |
| echo "validation failed" |
| return 1 |
| } |
| |
| print_options \ |
| CAfile \ |
| CApath \ |
| cert \ |
| CRLfile \ |
| CRLpath \ |
| debug \ |
| logId \ |
| engineId \ |
| engineNum \ |
| exec \ |
| execArgs \ |
| failover \ |
| ident \ |
| include \ |
| key \ |
| local \ |
| OCSP \ |
| protocol \ |
| protocolAuthentication \ |
| protocolDomain \ |
| protocolPassword \ |
| protocolUsername \ |
| PSKidentity \ |
| PSKsecrets \ |
| service \ |
| sessionCacheSize \ |
| sessionCacheTimeout \ |
| setgid \ |
| setuid \ |
| sslVersion \ |
| stack \ |
| ticketKeySecret \ |
| ticketMacSecret \ |
| TIMEOUTbusy \ |
| TIMEOUTclose \ |
| TIMEOUTconnect \ |
| TIMEOUTidle \ |
| transparent \ |
| ; |
| |
| print_bool_options \ |
| client \ |
| delay \ |
| OCSPaia \ |
| OCSPnonce \ |
| pty \ |
| renegotiation \ |
| requireCert \ |
| reset \ |
| retry \ |
| verifyChain \ |
| verifyPeer \ |
| ; |
| |
| print_lists_map \ |
| checkEmail \ |
| checkHost \ |
| checkIP \ |
| config \ |
| connect \ |
| OCSPflag \ |
| options \ |
| sni \ |
| socket \ |
| ; |
| |
| print_lists_reduce : \ |
| ciphers \ |
| curves \ |
| ciphersuites \ |
| ; |
| |
| print_host_port \ |
| protocolHost \ |
| sessiond \ |
| ; |
| |
| print_optional_host_port \ |
| accept \ |
| redirect \ |
| ; |
| } |
| |
| create_conf_file() { |
| [ -n "$CONF_FILE_CREATED" ] || { |
| mkdir -p "$(dirname "$CONF_FILE")" |
| echo "; STunnel configuration file generated by uci" > "$CONF_FILE" |
| echo "; Written $(date +'%c')" >> "$CONF_FILE" |
| echo >> "$CONF_FILE" |
| echo "foreground = quiet" >> "$CONF_FILE" |
| echo "pid = $PID_FILE" >> "$CONF_FILE" |
| CONF_FILE_CREATED=1 |
| } |
| } |
| |
| global_defs() { |
| local pid_dir |
| |
| [ "$2" = 0 ] || { |
| echo "validation failed" |
| return 1 |
| } |
| |
| # If the first globals section has alt_config_file, don't process any more globals |
| [ -z "$HAVE_ALT_CONF_FILE" ] || return 0 |
| |
| # If "alt_config_file" specified in the first globals section, use that instead |
| [ -z "$alt_config_file" ] || [ -n "$CONF_FILE_CREATED" ] || { |
| # Symlink "alt_config_file" since it's a bit easier and safer |
| ln -s "$alt_config_file" "$CONF_FILE" |
| # Set section found to start service, user hopefully knows what they are doing |
| SERVICE_SECTION_FOUND=1 |
| CONF_FILE_CREATED=1 |
| HAVE_ALT_CONF_FILE=1 |
| return 0 |
| } |
| |
| pid_dir="$(dirname "$PID_FILE")" |
| mkdir -p "$pid_dir" |
| [ -z "$setuid" ] || chown "$setuid" "$pid_dir" |
| [ -z "$setgid" ] || chown ":$setgid" "$pid_dir" |
| |
| create_conf_file |
| print_global_options |
| validate_service_options globals "$1" print_service_options |
| } |
| |
| service_section() { |
| [ "$2" = 0 ] || { |
| echo "validation failed" |
| return 1 |
| } |
| |
| [ "$enabled" = 1 ] || return 0 |
| |
| SERVICE_SECTION_FOUND=1 |
| echo >> "$CONF_FILE" |
| echo "[$1]" >> "$CONF_FILE" |
| |
| validate_service_options service "$1" print_service_options |
| } |
| |
| service_triggers() { |
| procd_add_reload_trigger stunnel |
| |
| procd_open_validate |
| validate_globals_section "$@" |
| validate_globals_section_service_options "$@" |
| validate_service_section "$@" |
| validate_service_section_service_options "$@" |
| procd_close_validate |
| } |
| |
| start_service() { |
| rm -f "$CONF_FILE" |
| config_load stunnel |
| |
| config_foreach validate_globals_section globals global_defs |
| |
| [ -n "$HAVE_ALT_CONF_FILE" ] || { |
| create_conf_file |
| config_foreach validate_service_section service service_section |
| } |
| |
| [ -n "$SERVICE_SECTION_FOUND" ] || { |
| logger -t stunnel -p daemon.info "No uci service section enabled or found!" |
| return 1 |
| } |
| |
| procd_open_instance |
| procd_set_param command "$BIN" |
| procd_append_param command "$CONF_FILE" |
| procd_set_param respawn |
| procd_set_param file "$CONF_FILE" |
| procd_close_instance |
| } |