You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
3.2 KiB
Go
124 lines
3.2 KiB
Go
2 years ago
|
package obfssh
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"context"
|
||
|
"crypto/tls"
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
log "github.com/fangdingjun/go-log/v5"
|
||
|
"github.com/gorilla/websocket"
|
||
|
"golang.org/x/crypto/ssh"
|
||
|
)
|
||
|
|
||
|
type Dialer struct {
|
||
|
// NetDial specifies the dial function for creating TCP connections. If
|
||
|
// NetDial is nil, net.Dial is used.
|
||
|
NetDial func(network, addr string) (net.Conn, error)
|
||
|
|
||
|
Proxy func() (*url.URL, error)
|
||
|
|
||
|
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
|
||
|
// If nil, the default configuration is used.
|
||
|
// If either NetDialTLS or NetDialTLSContext are set, Dial assumes the TLS handshake
|
||
|
// is done there and TLSClientConfig is ignored.
|
||
|
TLSClientConfig *tls.Config
|
||
|
|
||
|
NetConf *Conf
|
||
|
}
|
||
|
|
||
|
func (d *Dialer) Dial(addr string, conf *ssh.ClientConfig) (*Client, error) {
|
||
|
if d.NetConf.Timeout == 0 {
|
||
|
d.NetConf.Timeout = 15 * time.Second
|
||
|
}
|
||
|
if d.NetConf.KeepAliveInterval == 0 {
|
||
|
d.NetConf.KeepAliveInterval = 10
|
||
|
}
|
||
|
if d.NetConf.KeepAliveMax == 0 {
|
||
|
d.NetConf.KeepAliveMax = 3
|
||
|
}
|
||
|
var dialFunc func(network, addr string) (net.Conn, error)
|
||
|
if d.NetDial == nil {
|
||
|
dialFunc = dialer.Dial
|
||
|
}
|
||
|
|
||
|
u, err := url.Parse(addr)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if d.Proxy != nil {
|
||
|
dialFunc = func(network, addr string) (net.Conn, error) {
|
||
|
var conn net.Conn
|
||
|
var err error
|
||
|
u1, _ := d.Proxy()
|
||
|
if u1 == nil {
|
||
|
return dialer.Dial(network, addr)
|
||
|
}
|
||
|
log.Debugf("connect to proxy %s", u1.String())
|
||
|
switch u1.Scheme {
|
||
|
case "http":
|
||
|
conn, err = dialHTTPProxy(addr, u1)
|
||
|
case "https":
|
||
|
conn, err = dialHTTPSProxy(addr, u1)
|
||
|
case "socks5":
|
||
|
conn, err = dialSocks5Proxy(addr, u1)
|
||
|
default:
|
||
|
return nil, fmt.Errorf("unknown proxy scheme %s", u1.Scheme)
|
||
|
}
|
||
|
if err != nil {
|
||
|
log.Errorf("connect to proxy error %s", err)
|
||
|
}
|
||
|
return conn, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch u.Scheme {
|
||
|
case "":
|
||
|
conn, err := dialFunc("tcp", u.Host)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return NewClient(&TimedOutConn{Conn: conn, Timeout: d.NetConf.Timeout}, conf, u.Host, d.NetConf)
|
||
|
case "tls":
|
||
|
conn, err := dialFunc("tcp", u.Host)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
conn = tls.Client(&TimedOutConn{Conn: conn, Timeout: d.NetConf.Timeout}, d.TLSClientConfig)
|
||
|
return NewClient(conn, conf, u.Host, d.NetConf)
|
||
|
case "ws":
|
||
|
fallthrough
|
||
|
case "wss":
|
||
|
_addr := fmt.Sprintf("%s://%s%s", u.Scheme, u.Host, u.Path)
|
||
|
_dailer := websocket.Dialer{
|
||
|
NetDial: func(network, addr string) (net.Conn, error) {
|
||
|
c, err := dialFunc(network, addr)
|
||
|
return &TimedOutConn{Conn: c, Timeout: d.NetConf.Timeout}, err
|
||
|
},
|
||
|
TLSClientConfig: d.TLSClientConfig,
|
||
|
}
|
||
|
wsconn, res, err := _dailer.Dial(_addr, nil)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if res.StatusCode != http.StatusSwitchingProtocols {
|
||
|
return nil, fmt.Errorf("websocket connect failed, http code %d", res.StatusCode)
|
||
|
}
|
||
|
_conn := &wsConn{Conn: wsconn, buf: new(bytes.Buffer), mu: new(sync.Mutex), ch: make(chan struct{})}
|
||
|
go _conn.readLoop()
|
||
|
return NewClient(_conn, conf, u.Host, d.NetConf)
|
||
|
default:
|
||
|
return nil, fmt.Errorf("unknow scheme %s", u.Scheme)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (d *Dialer) DialContext(ctx context.Context, addr string, conf *ssh.ClientConfig) (*Client, error) {
|
||
|
return nil, nil
|
||
|
}
|