Make DialTimeout configurable.
This commit is contained in:
@@ -36,6 +36,7 @@ RUN set -ex && \
|
|||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
ENV SSHWIFTY_HOSTNAME= \
|
ENV SSHWIFTY_HOSTNAME= \
|
||||||
SSHWIFTY_SHAREDKEY= \
|
SSHWIFTY_SHAREDKEY= \
|
||||||
|
SSHWIFTY_DIALTIMEOUT=10 \
|
||||||
SSHWIFTY_SOCKS5= \
|
SSHWIFTY_SOCKS5= \
|
||||||
SSHWIFTY_SOCKS5_USER= \
|
SSHWIFTY_SOCKS5_USER= \
|
||||||
SSHWIFTY_SOCKS5_PASSWORD= \
|
SSHWIFTY_SOCKS5_PASSWORD= \
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -114,6 +114,12 @@ Here is the options that can be used in a configuration file and what it for:
|
|||||||
// Web interface access password. Set to empty to allow public access
|
// Web interface access password. Set to empty to allow public access
|
||||||
"SharedKey": "WEB_ACCESS_PASSWORD",
|
"SharedKey": "WEB_ACCESS_PASSWORD",
|
||||||
|
|
||||||
|
// Remote dial timeout. This limits how long of time the backend can spend
|
||||||
|
// to connect to a remote host. The max timeout will be determined by
|
||||||
|
// server configuration (ReadTimeout).
|
||||||
|
// (In Second)
|
||||||
|
"DialTimeout": 10,
|
||||||
|
|
||||||
// Socks5 proxy. When set, we will try to connect remote through the given
|
// Socks5 proxy. When set, we will try to connect remote through the given
|
||||||
// proxy
|
// proxy
|
||||||
"Socks5": "localhost:1080",
|
"Socks5": "localhost:1080",
|
||||||
@@ -136,23 +142,29 @@ Here is the options that can be used in a configuration file and what it for:
|
|||||||
|
|
||||||
// Timeout of initial request. HTTP handshake must be finished within
|
// Timeout of initial request. HTTP handshake must be finished within
|
||||||
// this time
|
// this time
|
||||||
|
// (In Second)
|
||||||
"InitialTimeout": 3,
|
"InitialTimeout": 3,
|
||||||
|
|
||||||
// How long the connection can be idle before the server disconnects the
|
// How long the connection can be idle before the server disconnects the
|
||||||
// client
|
// client
|
||||||
|
// (In Second)
|
||||||
"ReadTimeout": 60,
|
"ReadTimeout": 60,
|
||||||
|
|
||||||
// How long the server will wait until the client connect is ready to
|
// How long the server will wait until the client connect is ready to
|
||||||
// recieve new data
|
// recieve new data
|
||||||
|
// (In Second)
|
||||||
"WriteTimeout": 60,
|
"WriteTimeout": 60,
|
||||||
|
|
||||||
// The interval between internal echo requests
|
// The interval between internal echo requests
|
||||||
|
// (In Second)
|
||||||
"HeartbeatTimeout": 20,
|
"HeartbeatTimeout": 20,
|
||||||
|
|
||||||
// Forced delay between each request
|
// Forced delay between each request
|
||||||
|
// (In Milisecond)
|
||||||
"ReadDelay": 10,
|
"ReadDelay": 10,
|
||||||
|
|
||||||
// Forced delay between each write
|
// Forced delay between each write
|
||||||
|
// (In Milisecond)
|
||||||
"WriteDelay": 10,
|
"WriteDelay": 10,
|
||||||
|
|
||||||
// Path to TLS certificate file. Set empty to use HTTP
|
// Path to TLS certificate file. Set empty to use HTTP
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ import (
|
|||||||
"github.com/niruix/sshwifty/application/rw"
|
"github.com/niruix/sshwifty/application/rw"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CommandConfiguration contains configuration data needed to run command
|
||||||
|
type CommandConfiguration struct {
|
||||||
|
Dial network.Dial
|
||||||
|
DialTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
// Commander command control
|
// Commander command control
|
||||||
type Commander struct {
|
type Commander struct {
|
||||||
commands Commands
|
commands Commands
|
||||||
@@ -41,7 +47,7 @@ func New(cs Commands) Commander {
|
|||||||
|
|
||||||
// New Adds a new client
|
// New Adds a new client
|
||||||
func (c Commander) New(
|
func (c Commander) New(
|
||||||
dialer network.Dial,
|
cfg CommandConfiguration,
|
||||||
receiver rw.FetchReader,
|
receiver rw.FetchReader,
|
||||||
sender io.Writer,
|
sender io.Writer,
|
||||||
senderLock *sync.Mutex,
|
senderLock *sync.Mutex,
|
||||||
@@ -50,7 +56,7 @@ func (c Commander) New(
|
|||||||
l log.Logger,
|
l log.Logger,
|
||||||
) (Handler, error) {
|
) (Handler, error) {
|
||||||
return newHandler(
|
return newHandler(
|
||||||
dialer,
|
cfg,
|
||||||
&c.commands,
|
&c.commands,
|
||||||
receiver,
|
receiver,
|
||||||
sender,
|
sender,
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/niruix/sshwifty/application/log"
|
"github.com/niruix/sshwifty/application/log"
|
||||||
"github.com/niruix/sshwifty/application/network"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Consts
|
// Consts
|
||||||
@@ -37,7 +36,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Command represents a command handler machine builder
|
// Command represents a command handler machine builder
|
||||||
type Command func(l log.Logger, w StreamResponder, d network.Dial) FSMMachine
|
type Command func(
|
||||||
|
l log.Logger,
|
||||||
|
w StreamResponder,
|
||||||
|
cfg CommandConfiguration,
|
||||||
|
) FSMMachine
|
||||||
|
|
||||||
// Commands contains data of all commands
|
// Commands contains data of all commands
|
||||||
type Commands [MaxCommandID + 1]Command
|
type Commands [MaxCommandID + 1]Command
|
||||||
@@ -57,7 +60,10 @@ func (c *Commands) Register(id byte, cb Command) {
|
|||||||
|
|
||||||
// Run creates command executer
|
// Run creates command executer
|
||||||
func (c Commands) Run(
|
func (c Commands) Run(
|
||||||
id byte, l log.Logger, w StreamResponder, dial network.Dial) (FSM, error) {
|
id byte,
|
||||||
|
l log.Logger,
|
||||||
|
w StreamResponder,
|
||||||
|
cfg CommandConfiguration) (FSM, error) {
|
||||||
if id > MaxCommandID {
|
if id > MaxCommandID {
|
||||||
return FSM{}, ErrCommandRunUndefinedCommand
|
return FSM{}, ErrCommandRunUndefinedCommand
|
||||||
}
|
}
|
||||||
@@ -68,5 +74,5 @@ func (c Commands) Run(
|
|||||||
return FSM{}, ErrCommandRunUndefinedCommand
|
return FSM{}, ErrCommandRunUndefinedCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
return newFSM(cc(l, w, dial)), nil
|
return newFSM(cc(l, w, cfg)), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/niruix/sshwifty/application/log"
|
"github.com/niruix/sshwifty/application/log"
|
||||||
"github.com/niruix/sshwifty/application/network"
|
|
||||||
"github.com/niruix/sshwifty/application/rw"
|
"github.com/niruix/sshwifty/application/rw"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -105,7 +104,7 @@ func (h streamHandlerSender) Write(b []byte) (int, error) {
|
|||||||
|
|
||||||
// Handler client stream control
|
// Handler client stream control
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
dialer network.Dial
|
cfg CommandConfiguration
|
||||||
commands *Commands
|
commands *Commands
|
||||||
receiver rw.FetchReader
|
receiver rw.FetchReader
|
||||||
sender handlerSender
|
sender handlerSender
|
||||||
@@ -118,7 +117,7 @@ type Handler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newHandler(
|
func newHandler(
|
||||||
dialer network.Dial,
|
cfg CommandConfiguration,
|
||||||
commands *Commands,
|
commands *Commands,
|
||||||
receiver rw.FetchReader,
|
receiver rw.FetchReader,
|
||||||
sender io.Writer,
|
sender io.Writer,
|
||||||
@@ -128,7 +127,7 @@ func newHandler(
|
|||||||
l log.Logger,
|
l log.Logger,
|
||||||
) Handler {
|
) Handler {
|
||||||
return Handler{
|
return Handler{
|
||||||
dialer: dialer,
|
cfg: cfg,
|
||||||
commands: commands,
|
commands: commands,
|
||||||
receiver: receiver,
|
receiver: receiver,
|
||||||
sender: handlerSender{writer: sender, lock: senderLock},
|
sender: handlerSender{writer: sender, lock: senderLock},
|
||||||
@@ -235,7 +234,7 @@ func (e *Handler) handleStream(h Header, d byte, l log.Logger) error {
|
|||||||
return st.reinit(h, &e.receiver, streamHandlerSender{
|
return st.reinit(h, &e.receiver, streamHandlerSender{
|
||||||
handlerSender: &e.sender,
|
handlerSender: &e.sender,
|
||||||
sendDelay: e.sendDelay,
|
sendDelay: e.sendDelay,
|
||||||
}, l, e.commands, e.dialer, e.rBuf[:])
|
}, l, e.commands, e.cfg, e.rBuf[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Handler) handleClose(h Header, d byte, l log.Logger) error {
|
func (e *Handler) handleClose(h Header, d byte, l log.Logger) error {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func TestHandlerHandleEcho(t *testing.T) {
|
|||||||
}
|
}
|
||||||
lock := sync.Mutex{}
|
lock := sync.Mutex{}
|
||||||
handler := newHandler(
|
handler := newHandler(
|
||||||
nil,
|
CommandConfiguration{},
|
||||||
nil,
|
nil,
|
||||||
rw.NewFetchReader(testDummyFetchGen(s)),
|
rw.NewFetchReader(testDummyFetchGen(s)),
|
||||||
&w,
|
&w,
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/niruix/sshwifty/application/log"
|
"github.com/niruix/sshwifty/application/log"
|
||||||
"github.com/niruix/sshwifty/application/network"
|
|
||||||
"github.com/niruix/sshwifty/application/rw"
|
"github.com/niruix/sshwifty/application/rw"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -67,9 +66,12 @@ type dummyStreamCommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newDummyStreamCommand(
|
func newDummyStreamCommand(
|
||||||
l log.Logger, w StreamResponder, d network.Dial) FSMMachine {
|
l log.Logger,
|
||||||
|
w StreamResponder,
|
||||||
|
cfg CommandConfiguration,
|
||||||
|
) FSMMachine {
|
||||||
return &dummyStreamCommand{
|
return &dummyStreamCommand{
|
||||||
lock:sync.Mutex{},
|
lock: sync.Mutex{},
|
||||||
l: l,
|
l: l,
|
||||||
w: w,
|
w: w,
|
||||||
downWait: sync.WaitGroup{},
|
downWait: sync.WaitGroup{},
|
||||||
@@ -84,7 +86,7 @@ func (d *dummyStreamCommand) Bootup(
|
|||||||
) (FSMState, FSMError) {
|
) (FSMState, FSMError) {
|
||||||
d.downWait.Add(1)
|
d.downWait.Add(1)
|
||||||
|
|
||||||
echoTrans:=d.echoTrans
|
echoTrans := d.echoTrans
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -178,7 +180,7 @@ func TestHandlerHandleStream(t *testing.T) {
|
|||||||
|
|
||||||
lock := sync.Mutex{}
|
lock := sync.Mutex{}
|
||||||
hhd := newHandler(
|
hhd := newHandler(
|
||||||
nil,
|
CommandConfiguration{},
|
||||||
&cmds,
|
&cmds,
|
||||||
rw.NewFetchReader(readerSource),
|
rw.NewFetchReader(readerSource),
|
||||||
wBuffer,
|
wBuffer,
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/niruix/sshwifty/application/log"
|
"github.com/niruix/sshwifty/application/log"
|
||||||
"github.com/niruix/sshwifty/application/network"
|
|
||||||
"github.com/niruix/sshwifty/application/rw"
|
"github.com/niruix/sshwifty/application/rw"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -342,7 +341,7 @@ func (c *stream) reinit(
|
|||||||
w streamHandlerSender,
|
w streamHandlerSender,
|
||||||
l log.Logger,
|
l log.Logger,
|
||||||
cc *Commands,
|
cc *Commands,
|
||||||
dialer network.Dial,
|
cfg CommandConfiguration,
|
||||||
b []byte,
|
b []byte,
|
||||||
) error {
|
) error {
|
||||||
hd := streamInitialHeader{}
|
hd := streamInitialHeader{}
|
||||||
@@ -355,7 +354,8 @@ func (c *stream) reinit(
|
|||||||
|
|
||||||
l = l.Context("Command (%d)", hd.command())
|
l = l.Context("Command (%d)", hd.command())
|
||||||
|
|
||||||
ccc, cccErr := cc.Run(hd.command(), l, newStreamResponder(w, h), dialer)
|
ccc, cccErr := cc.Run(
|
||||||
|
hd.command(), l, newStreamResponder(w, h), cfg)
|
||||||
|
|
||||||
if cccErr != nil {
|
if cccErr != nil {
|
||||||
hd.set(0, uint16(StreamErrorCommandUndefined), false)
|
hd.set(0, uint16(StreamErrorCommandUndefined), false)
|
||||||
|
|||||||
@@ -22,13 +22,11 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
"github.com/niruix/sshwifty/application/command"
|
"github.com/niruix/sshwifty/application/command"
|
||||||
"github.com/niruix/sshwifty/application/log"
|
"github.com/niruix/sshwifty/application/log"
|
||||||
"github.com/niruix/sshwifty/application/network"
|
|
||||||
"github.com/niruix/sshwifty/application/rw"
|
"github.com/niruix/sshwifty/application/rw"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -116,8 +114,7 @@ func (s sshRemoteConn) isValid() bool {
|
|||||||
type sshClient struct {
|
type sshClient struct {
|
||||||
w command.StreamResponder
|
w command.StreamResponder
|
||||||
l log.Logger
|
l log.Logger
|
||||||
dial network.Dial
|
cfg command.CommandConfiguration
|
||||||
dialTimeout time.Duration
|
|
||||||
remoteCloseWait sync.WaitGroup
|
remoteCloseWait sync.WaitGroup
|
||||||
credentialReceive chan []byte
|
credentialReceive chan []byte
|
||||||
credentialProcessed bool
|
credentialProcessed bool
|
||||||
@@ -132,13 +129,12 @@ type sshClient struct {
|
|||||||
func newSSH(
|
func newSSH(
|
||||||
l log.Logger,
|
l log.Logger,
|
||||||
w command.StreamResponder,
|
w command.StreamResponder,
|
||||||
dial network.Dial,
|
cfg command.CommandConfiguration,
|
||||||
) command.FSMMachine {
|
) command.FSMMachine {
|
||||||
return &sshClient{
|
return &sshClient{
|
||||||
w: w,
|
w: w,
|
||||||
l: l,
|
l: l,
|
||||||
dial: dial,
|
cfg: cfg,
|
||||||
dialTimeout: 10 * time.Second,
|
|
||||||
remoteCloseWait: sync.WaitGroup{},
|
remoteCloseWait: sync.WaitGroup{},
|
||||||
credentialReceive: make(chan []byte, 1),
|
credentialReceive: make(chan []byte, 1),
|
||||||
credentialProcessed: false,
|
credentialProcessed: false,
|
||||||
@@ -300,7 +296,7 @@ func (d *sshClient) comfirmRemoteFingerprint(
|
|||||||
|
|
||||||
func (d *sshClient) dialRemote(
|
func (d *sshClient) dialRemote(
|
||||||
network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
|
network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
|
||||||
conn, err := d.dial(network, addr, config.Timeout)
|
conn, err := d.cfg.Dial(network, addr, config.Timeout)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -332,7 +328,7 @@ func (d *sshClient) remote(
|
|||||||
HostKeyCallback: func(h string, r net.Addr, k ssh.PublicKey) error {
|
HostKeyCallback: func(h string, r net.Addr, k ssh.PublicKey) error {
|
||||||
return d.comfirmRemoteFingerprint(h, r, k, buf[:])
|
return d.comfirmRemoteFingerprint(h, r, k, buf[:])
|
||||||
},
|
},
|
||||||
Timeout: d.dialTimeout,
|
Timeout: d.cfg.DialTimeout,
|
||||||
})
|
})
|
||||||
|
|
||||||
if dErr != nil {
|
if dErr != nil {
|
||||||
|
|||||||
@@ -21,11 +21,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/niruix/sshwifty/application/command"
|
"github.com/niruix/sshwifty/application/command"
|
||||||
"github.com/niruix/sshwifty/application/log"
|
"github.com/niruix/sshwifty/application/log"
|
||||||
"github.com/niruix/sshwifty/application/network"
|
|
||||||
"github.com/niruix/sshwifty/application/rw"
|
"github.com/niruix/sshwifty/application/rw"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,26 +48,24 @@ const (
|
|||||||
type telnetClient struct {
|
type telnetClient struct {
|
||||||
l log.Logger
|
l log.Logger
|
||||||
w command.StreamResponder
|
w command.StreamResponder
|
||||||
dial network.Dial
|
cfg command.CommandConfiguration
|
||||||
remoteChan chan io.WriteCloser
|
remoteChan chan io.WriteCloser
|
||||||
remoteConn io.WriteCloser
|
remoteConn io.WriteCloser
|
||||||
closeWait sync.WaitGroup
|
closeWait sync.WaitGroup
|
||||||
dialTimeout time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTelnet(
|
func newTelnet(
|
||||||
l log.Logger,
|
l log.Logger,
|
||||||
w command.StreamResponder,
|
w command.StreamResponder,
|
||||||
dial network.Dial,
|
cfg command.CommandConfiguration,
|
||||||
) command.FSMMachine {
|
) command.FSMMachine {
|
||||||
return &telnetClient{
|
return &telnetClient{
|
||||||
l: l,
|
l: l,
|
||||||
w: w,
|
w: w,
|
||||||
dial: dial,
|
cfg: cfg,
|
||||||
remoteChan: make(chan io.WriteCloser, 1),
|
remoteChan: make(chan io.WriteCloser, 1),
|
||||||
remoteConn: nil,
|
remoteConn: nil,
|
||||||
closeWait: sync.WaitGroup{},
|
closeWait: sync.WaitGroup{},
|
||||||
dialTimeout: 10 * time.Second,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +97,7 @@ func (d *telnetClient) remote(addr string) {
|
|||||||
|
|
||||||
buf := [4096]byte{}
|
buf := [4096]byte{}
|
||||||
|
|
||||||
clientConn, clientConnErr := d.dial("tcp", addr, d.dialTimeout)
|
clientConn, clientConnErr := d.cfg.Dial("tcp", addr, d.cfg.DialTimeout)
|
||||||
|
|
||||||
if clientConnErr != nil {
|
if clientConnErr != nil {
|
||||||
errLen := copy(
|
errLen := copy(
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ type Configuration struct {
|
|||||||
HostName string
|
HostName string
|
||||||
SharedKey string
|
SharedKey string
|
||||||
Dialer network.Dial
|
Dialer network.Dial
|
||||||
|
DialTimeout time.Duration
|
||||||
Servers []Server
|
Servers []Server
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +133,7 @@ type Common struct {
|
|||||||
HostName string
|
HostName string
|
||||||
SharedKey string
|
SharedKey string
|
||||||
Dialer network.Dial
|
Dialer network.Dial
|
||||||
|
DialTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify verifies current setting
|
// Verify verifies current setting
|
||||||
@@ -155,24 +157,31 @@ func (c Configuration) Verify() error {
|
|||||||
|
|
||||||
// Common returns common settings
|
// Common returns common settings
|
||||||
func (c Configuration) Common() Common {
|
func (c Configuration) Common() Common {
|
||||||
return Common{
|
|
||||||
HostName: c.HostName,
|
|
||||||
SharedKey: c.SharedKey,
|
|
||||||
Dialer: c.Dialer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithDefault build the configuration and fill the blank with default values
|
|
||||||
func (c Common) WithDefault() Common {
|
|
||||||
dialer := c.Dialer
|
dialer := c.Dialer
|
||||||
|
|
||||||
if dialer == nil {
|
if dialer == nil {
|
||||||
dialer = network.TCPDial()
|
dialer = network.TCPDial()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialTimeout := c.DialTimeout
|
||||||
|
|
||||||
|
if dialTimeout <= 1*time.Second {
|
||||||
|
dialTimeout = 1 * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
return Common{
|
return Common{
|
||||||
HostName: c.HostName,
|
HostName: c.HostName,
|
||||||
SharedKey: c.SharedKey,
|
SharedKey: c.SharedKey,
|
||||||
Dialer: dialer,
|
Dialer: c.Dialer,
|
||||||
|
DialTimeout: c.DialTimeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DecideDialTimeout will return a reasonable timeout for dialing
|
||||||
|
func (c Common) DecideDialTimeout(max time.Duration) time.Duration {
|
||||||
|
if c.DialTimeout > max {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.DialTimeout
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/niruix/sshwifty/application/log"
|
"github.com/niruix/sshwifty/application/log"
|
||||||
"github.com/niruix/sshwifty/application/network"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -46,12 +46,21 @@ func Enviro() Loader {
|
|||||||
return func(log log.Logger) (string, Configuration, error) {
|
return func(log log.Logger) (string, Configuration, error) {
|
||||||
log.Info("Loading configuration from environment variables ...")
|
log.Info("Loading configuration from environment variables ...")
|
||||||
|
|
||||||
cfg := fileCfgCommon{
|
dialTimeout, _ := strconv.ParseUint(
|
||||||
|
parseEviro("SSHWIFTY_DIALTIMEOUT"), 10, 32)
|
||||||
|
|
||||||
|
cfg, dialer, cfgErr := fileCfgCommon{
|
||||||
HostName: parseEviro("SSHWIFTY_HOSTNAME"),
|
HostName: parseEviro("SSHWIFTY_HOSTNAME"),
|
||||||
SharedKey: parseEviro("SSHWIFTY_SHAREDKEY"),
|
SharedKey: parseEviro("SSHWIFTY_SHAREDKEY"),
|
||||||
|
DialTimeout: int(dialTimeout),
|
||||||
Socks5: parseEviro("SSHWIFTY_SOCKS5"),
|
Socks5: parseEviro("SSHWIFTY_SOCKS5"),
|
||||||
Socks5User: parseEviro("SSHWIFTY_SOCKS5_USER"),
|
Socks5User: parseEviro("SSHWIFTY_SOCKS5_USER"),
|
||||||
Socks5Password: parseEviro("SSHWIFTY_SOCKS5_PASSWORD"),
|
Socks5Password: parseEviro("SSHWIFTY_SOCKS5_PASSWORD"),
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
if cfgErr != nil {
|
||||||
|
return enviroTypeName, Configuration{}, fmt.Errorf(
|
||||||
|
"Failed to build the configuration: %s", cfgErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
listenPort, listenPortErr := strconv.ParseUint(
|
listenPort, listenPortErr := strconv.ParseUint(
|
||||||
@@ -93,25 +102,11 @@ func Enviro() Loader {
|
|||||||
TLSCertificateKeyFile: parseEviro("SSHWIFTY_TLSCERTIFICATEKEYFILE"),
|
TLSCertificateKeyFile: parseEviro("SSHWIFTY_TLSCERTIFICATEKEYFILE"),
|
||||||
}
|
}
|
||||||
|
|
||||||
var dialer network.Dial
|
|
||||||
|
|
||||||
if len(cfg.Socks5) <= 0 {
|
|
||||||
dialer = network.TCPDial()
|
|
||||||
} else {
|
|
||||||
sDial, sDialErr := network.BuildSocks5Dial(
|
|
||||||
cfg.Socks5, cfg.Socks5User, cfg.Socks5Password)
|
|
||||||
|
|
||||||
if sDialErr != nil {
|
|
||||||
return enviroTypeName, Configuration{}, sDialErr
|
|
||||||
}
|
|
||||||
|
|
||||||
dialer = sDial
|
|
||||||
}
|
|
||||||
|
|
||||||
return enviroTypeName, Configuration{
|
return enviroTypeName, Configuration{
|
||||||
HostName: cfg.HostName,
|
HostName: cfg.HostName,
|
||||||
SharedKey: cfg.SharedKey,
|
SharedKey: cfg.SharedKey,
|
||||||
Dialer: dialer,
|
Dialer: dialer,
|
||||||
|
DialTimeout: time.Duration(cfg.DialTimeout) * time.Second,
|
||||||
Servers: []Server{cfgSer.build()},
|
Servers: []Server{cfgSer.build()},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ type fileCfgServer struct {
|
|||||||
TLSCertificateKeyFile string // Location of TLS certificate key
|
TLSCertificateKeyFile string // Location of TLS certificate key
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f fileCfgServer) minDur(current, min int) int {
|
func (f fileCfgServer) durationAtLeast(current, min int) int {
|
||||||
if current > min {
|
if current > min {
|
||||||
return current
|
return current
|
||||||
}
|
}
|
||||||
@@ -62,17 +62,17 @@ func (f *fileCfgServer) build() Server {
|
|||||||
ListenInterface: f.ListenInterface,
|
ListenInterface: f.ListenInterface,
|
||||||
ListenPort: f.ListenPort,
|
ListenPort: f.ListenPort,
|
||||||
InitialTimeout: time.Duration(
|
InitialTimeout: time.Duration(
|
||||||
f.minDur(f.InitialTimeout, 5)) * time.Second,
|
f.durationAtLeast(f.InitialTimeout, 5)) * time.Second,
|
||||||
ReadTimeout: time.Duration(
|
ReadTimeout: time.Duration(
|
||||||
f.minDur(f.ReadTimeout, 30)) * time.Second,
|
f.durationAtLeast(f.ReadTimeout, 30)) * time.Second,
|
||||||
WriteTimeout: time.Duration(
|
WriteTimeout: time.Duration(
|
||||||
f.minDur(f.WriteTimeout, 30)) * time.Second,
|
f.durationAtLeast(f.WriteTimeout, 30)) * time.Second,
|
||||||
HeartbeatTimeout: time.Duration(
|
HeartbeatTimeout: time.Duration(
|
||||||
f.minDur(f.HeartbeatTimeout, 10)) * time.Second,
|
f.durationAtLeast(f.HeartbeatTimeout, 10)) * time.Second,
|
||||||
ReadDelay: time.Duration(
|
ReadDelay: time.Duration(
|
||||||
f.minDur(f.ReadDelay, 0)) * time.Millisecond,
|
f.durationAtLeast(f.ReadDelay, 0)) * time.Millisecond,
|
||||||
WriteDelay: time.Duration(
|
WriteDelay: time.Duration(
|
||||||
f.minDur(f.WriteDelay, 0)) * time.Millisecond,
|
f.durationAtLeast(f.WriteDelay, 0)) * time.Millisecond,
|
||||||
TLSCertificateFile: f.TLSCertificateFile,
|
TLSCertificateFile: f.TLSCertificateFile,
|
||||||
TLSCertificateKeyFile: f.TLSCertificateKeyFile,
|
TLSCertificateKeyFile: f.TLSCertificateKeyFile,
|
||||||
}
|
}
|
||||||
@@ -81,12 +81,46 @@ func (f *fileCfgServer) build() Server {
|
|||||||
type fileCfgCommon struct {
|
type fileCfgCommon struct {
|
||||||
HostName string // Host name
|
HostName string // Host name
|
||||||
SharedKey string // Shared key, empty to enable public access
|
SharedKey string // Shared key, empty to enable public access
|
||||||
|
DialTimeout int // DialTimeout, min 5s
|
||||||
Socks5 string // Socks5 server address, optional
|
Socks5 string // Socks5 server address, optional
|
||||||
Socks5User string // Login user for socks5 server, optional
|
Socks5User string // Login user for socks5 server, optional
|
||||||
Socks5Password string // Login pass for socks5 server, optional
|
Socks5Password string // Login pass for socks5 server, optional
|
||||||
Servers []*fileCfgServer // Servers
|
Servers []*fileCfgServer // Servers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f fileCfgCommon) build() (fileCfgCommon, network.Dial, error) {
|
||||||
|
dialTimeout := f.DialTimeout
|
||||||
|
|
||||||
|
if dialTimeout < 3 {
|
||||||
|
dialTimeout = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
var dialer network.Dial
|
||||||
|
|
||||||
|
if len(f.Socks5) <= 0 {
|
||||||
|
dialer = network.TCPDial()
|
||||||
|
} else {
|
||||||
|
sDial, sDialErr := network.BuildSocks5Dial(
|
||||||
|
f.Socks5, f.Socks5User, f.Socks5Password)
|
||||||
|
|
||||||
|
if sDialErr != nil {
|
||||||
|
return fileCfgCommon{}, nil, sDialErr
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer = sDial
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileCfgCommon{
|
||||||
|
HostName: f.HostName,
|
||||||
|
SharedKey: f.SharedKey,
|
||||||
|
DialTimeout: dialTimeout,
|
||||||
|
Socks5: f.Socks5,
|
||||||
|
Socks5User: f.Socks5User,
|
||||||
|
Socks5Password: f.Socks5Password,
|
||||||
|
Servers: f.Servers,
|
||||||
|
}, dialer, nil
|
||||||
|
}
|
||||||
|
|
||||||
func loadFile(filePath string) (string, Configuration, error) {
|
func loadFile(filePath string) (string, Configuration, error) {
|
||||||
f, fErr := os.Open(filePath)
|
f, fErr := os.Open(filePath)
|
||||||
|
|
||||||
@@ -105,31 +139,23 @@ func loadFile(filePath string) (string, Configuration, error) {
|
|||||||
return fileTypeName, Configuration{}, jDecodeErr
|
return fileTypeName, Configuration{}, jDecodeErr
|
||||||
}
|
}
|
||||||
|
|
||||||
servers := make([]Server, len(cfg.Servers))
|
finalCfg, dialer, cfgErr := cfg.build()
|
||||||
|
|
||||||
|
if cfgErr != nil {
|
||||||
|
return fileTypeName, Configuration{}, cfgErr
|
||||||
|
}
|
||||||
|
|
||||||
|
servers := make([]Server, len(finalCfg.Servers))
|
||||||
|
|
||||||
for i := range servers {
|
for i := range servers {
|
||||||
servers[i] = cfg.Servers[i].build()
|
servers[i] = finalCfg.Servers[i].build()
|
||||||
}
|
|
||||||
|
|
||||||
var dialer network.Dial
|
|
||||||
|
|
||||||
if len(cfg.Socks5) <= 0 {
|
|
||||||
dialer = network.TCPDial()
|
|
||||||
} else {
|
|
||||||
sDial, sDialErr := network.BuildSocks5Dial(
|
|
||||||
cfg.Socks5, cfg.Socks5User, cfg.Socks5Password)
|
|
||||||
|
|
||||||
if sDialErr != nil {
|
|
||||||
return fileTypeName, Configuration{}, sDialErr
|
|
||||||
}
|
|
||||||
|
|
||||||
dialer = sDial
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileTypeName, Configuration{
|
return fileTypeName, Configuration{
|
||||||
HostName: cfg.HostName,
|
HostName: finalCfg.HostName,
|
||||||
SharedKey: cfg.SharedKey,
|
SharedKey: finalCfg.SharedKey,
|
||||||
Dialer: dialer,
|
Dialer: dialer,
|
||||||
|
DialTimeout: time.Duration(finalCfg.DialTimeout) * time.Second,
|
||||||
Servers: servers,
|
Servers: servers,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,7 +358,11 @@ func (s socket) Get(
|
|||||||
|
|
||||||
senderLock := sync.Mutex{}
|
senderLock := sync.Mutex{}
|
||||||
cmdExec, cmdExecErr := s.commander.New(
|
cmdExec, cmdExecErr := s.commander.New(
|
||||||
s.commonCfg.Dialer, rw.NewFetchReader(func() ([]byte, error) {
|
command.CommandConfiguration{
|
||||||
|
Dial: s.commonCfg.Dialer,
|
||||||
|
DialTimeout: s.commonCfg.DecideDialTimeout(s.serverCfg.ReadTimeout),
|
||||||
|
},
|
||||||
|
rw.NewFetchReader(func() ([]byte, error) {
|
||||||
defer s.increaseNonce(readNonce[:])
|
defer s.increaseNonce(readNonce[:])
|
||||||
|
|
||||||
// Size is unencrypted
|
// Size is unencrypted
|
||||||
@@ -402,7 +406,8 @@ func (s socket) Get(
|
|||||||
readNonce[:],
|
readNonce[:],
|
||||||
cipherReadBuf[:packageSize],
|
cipherReadBuf[:packageSize],
|
||||||
nil)
|
nil)
|
||||||
}), socketPackageWriter{
|
}),
|
||||||
|
socketPackageWriter{
|
||||||
w: wsWriter,
|
w: wsWriter,
|
||||||
packager: func(w websocketWriter, b []byte) error {
|
packager: func(w websocketWriter, b []byte) error {
|
||||||
start := 0
|
start := 0
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ func BuildSocks5Dial(
|
|||||||
) (net.Conn, error) {
|
) (net.Conn, error) {
|
||||||
dial, dialErr := proxy.SOCKS5("tcp", socks5Address, auth, &net.Dialer{
|
dial, dialErr := proxy.SOCKS5("tcp", socks5Address, auth, &net.Dialer{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
|
Deadline: time.Now().Add(timeout),
|
||||||
})
|
})
|
||||||
|
|
||||||
if dialErr != nil {
|
if dialErr != nil {
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ func (s Server) Serve(
|
|||||||
closeCallback CloseCallback,
|
closeCallback CloseCallback,
|
||||||
handlerBuilder HandlerBuilder,
|
handlerBuilder HandlerBuilder,
|
||||||
) *Serving {
|
) *Serving {
|
||||||
ccCfg := commonCfg.WithDefault()
|
|
||||||
ssCfg := serverCfg.WithDefault()
|
ssCfg := serverCfg.WithDefault()
|
||||||
|
|
||||||
l := s.logger.Context(
|
l := s.logger.Context(
|
||||||
@@ -88,7 +87,7 @@ func (s Server) Serve(
|
|||||||
|
|
||||||
ss := &Serving{
|
ss := &Serving{
|
||||||
server: http.Server{
|
server: http.Server{
|
||||||
Handler: handlerBuilder(ccCfg, ssCfg, l),
|
Handler: handlerBuilder(commonCfg, ssCfg, l),
|
||||||
ReadTimeout: ssCfg.ReadTimeout,
|
ReadTimeout: ssCfg.ReadTimeout,
|
||||||
ReadHeaderTimeout: ssCfg.InitialTimeout,
|
ReadHeaderTimeout: ssCfg.InitialTimeout,
|
||||||
WriteTimeout: ssCfg.WriteTimeout,
|
WriteTimeout: ssCfg.WriteTimeout,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"HostName": "",
|
"HostName": "",
|
||||||
"SharedKey": "WEB_ACCESS_PASSWORD",
|
"SharedKey": "WEB_ACCESS_PASSWORD",
|
||||||
|
"DialTimeout": 5,
|
||||||
"Socks5": "",
|
"Socks5": "",
|
||||||
"Socks5User": "",
|
"Socks5User": "",
|
||||||
"Socks5Password": "",
|
"Socks5Password": "",
|
||||||
|
|||||||
Reference in New Issue
Block a user