2023-01-23 18:54:21 +07:00
|
|
|
package ws
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"fmt"
|
|
|
|
"github.com/gorilla/websocket"
|
2023-02-17 23:49:35 +07:00
|
|
|
"github.com/mzz2017/softwind/netproxy"
|
2023-01-23 18:54:21 +07:00
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2023-02-12 16:17:51 +07:00
|
|
|
"strconv"
|
2023-01-23 18:54:21 +07:00
|
|
|
)
|
|
|
|
|
|
|
|
// Ws is a base Ws struct
|
|
|
|
type Ws struct {
|
2023-06-04 10:38:05 +07:00
|
|
|
dialer netproxy.Dialer
|
|
|
|
wsAddr string
|
|
|
|
header http.Header
|
|
|
|
tlsClientConfig *tls.Config
|
2023-01-23 18:54:21 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewWs returns a Ws infra.
|
2023-02-17 23:49:35 +07:00
|
|
|
func NewWs(s string, d netproxy.Dialer) (*Ws, error) {
|
2023-01-23 18:54:21 +07:00
|
|
|
u, err := url.Parse(s)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("NewWs: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
t := &Ws{
|
|
|
|
dialer: d,
|
|
|
|
}
|
|
|
|
|
|
|
|
query := u.Query()
|
|
|
|
host := query.Get("host")
|
|
|
|
if host == "" {
|
|
|
|
host = u.Hostname()
|
|
|
|
}
|
|
|
|
t.header = http.Header{}
|
|
|
|
t.header.Set("Host", host)
|
|
|
|
|
|
|
|
wsUrl := url.URL{
|
|
|
|
Scheme: u.Scheme,
|
|
|
|
Host: u.Host,
|
|
|
|
}
|
2023-02-12 16:17:51 +07:00
|
|
|
t.wsAddr = wsUrl.String() + u.Path
|
2023-01-23 18:54:21 +07:00
|
|
|
if u.Scheme == "wss" {
|
2023-02-12 16:17:51 +07:00
|
|
|
skipVerify, _ := strconv.ParseBool(u.Query().Get("allowInsecure"))
|
2023-06-04 10:38:05 +07:00
|
|
|
t.tlsClientConfig = &tls.Config{
|
2023-02-12 16:17:51 +07:00
|
|
|
ServerName: u.Query().Get("sni"),
|
|
|
|
InsecureSkipVerify: skipVerify,
|
2023-01-23 18:54:21 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return t, nil
|
|
|
|
}
|
|
|
|
|
2023-02-20 17:06:54 +07:00
|
|
|
func (s *Ws) Dial(network, addr string) (c netproxy.Conn, err error) {
|
|
|
|
magicNetwork, err := netproxy.ParseMagicNetwork(network)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
switch magicNetwork.Network {
|
|
|
|
case "tcp":
|
2023-06-04 10:38:05 +07:00
|
|
|
wsDialer := &websocket.Dialer{
|
|
|
|
NetDial: func(_, addr string) (net.Conn, error) {
|
|
|
|
c, err := s.dialer.Dial(network, addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &netproxy.FakeNetConn{
|
|
|
|
Conn: c,
|
|
|
|
LAddr: nil,
|
|
|
|
RAddr: nil,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
//Subprotocols: []string{"binary"},
|
|
|
|
}
|
|
|
|
rc, _, err := wsDialer.Dial(s.wsAddr, s.header)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("[Ws]: dial to %s: %w", s.wsAddr, err)
|
|
|
|
}
|
|
|
|
return newConn(rc), err
|
2023-02-20 17:06:54 +07:00
|
|
|
case "udp":
|
2023-06-04 10:38:05 +07:00
|
|
|
return nil, fmt.Errorf("%w: ws+udp", netproxy.UnsupportedTunnelTypeError)
|
2023-02-20 17:06:54 +07:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("%w: %v", netproxy.UnsupportedTunnelTypeError, network)
|
|
|
|
}
|
|
|
|
}
|