Skip to content

Commit

Permalink
support client via proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
wwqgtxx committed Mar 18, 2022
1 parent 134f0fd commit 2c9130d
Show file tree
Hide file tree
Showing 5 changed files with 663 additions and 26 deletions.
104 changes: 80 additions & 24 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ type Client interface {
Addr() string
GetClientImpl() ClientImpl
SetClientImpl(impl ClientImpl)
GetServerWSPath() string
}

type client struct {
ClientImpl
bindAddress string
bindAddress string
serverWSPath string
}

func (c *client) Start() {
Expand Down Expand Up @@ -56,12 +58,18 @@ func (c *client) Addr() string {
func (c *client) GetClientImpl() ClientImpl {
return c.ClientImpl
}

func (c *client) SetClientImpl(impl ClientImpl) {
c.ClientImpl = impl
}

func (c *client) GetServerWSPath() string {
return c.serverWSPath
}

type ClientImpl interface {
Target() string
Proxy() string
Handle(tcp net.Conn)
Dial(edBuf []byte, inHeader http.Header) (io.Closer, error)
ToRawConn(conn io.Closer) net.Conn
Expand All @@ -73,20 +81,26 @@ type wsClientImpl struct {
wsUrl string
wsDialer *websocket.Dialer
ed uint32
proxy string
}

type tcpClientImpl struct {
targetAddress string
tcpDialer *net.Dialer
netDial netDialerFunc
proxy string
}

func (c *wsClientImpl) Target() string {
return c.wsUrl
}

func (c *wsClientImpl) Proxy() string {
return c.proxy
}

func (c *wsClientImpl) Handle(tcp net.Conn) {
defer tcp.Close()
log.Println("Incoming --> ", tcp.RemoteAddr(), " --> ", c.Target())
log.Println("Incoming --> ", tcp.RemoteAddr(), " --> ", c.Target(), c.Proxy())
header, edBuf, err := encodeXray0rtt(tcp, c)
if err != nil {
log.Println(err)
Expand Down Expand Up @@ -127,10 +141,10 @@ func (c *wsClientImpl) Dial(edBuf []byte, inHeader http.Header) (io.Closer, erro
header.Set("Sec-WebSocket-Protocol", secProtocol)
}
}
log.Println("Dial to", c.Target(), "with", header)
log.Println("Dial to", c.Target(), c.Proxy(), "with", header)
ws, resp, err := c.wsDialer.Dial(c.Target(), header)
if resp != nil {
log.Println("Dial", c.Target(), "get response", resp.Header)
log.Println("Dial", c.Target(), c.Proxy(), "get response", resp.Header)
}
return ws, err
}
Expand All @@ -148,9 +162,13 @@ func (c *tcpClientImpl) Target() string {
return c.targetAddress
}

func (c *tcpClientImpl) Proxy() string {
return c.proxy
}

func (c *tcpClientImpl) Handle(tcp net.Conn) {
defer tcp.Close()
log.Println("Incoming --> ", tcp.RemoteAddr(), " --> ", c.Target())
log.Println("Incoming --> ", tcp.RemoteAddr(), " --> ", c.Target(), c.Proxy())
conn, err := c.Dial(nil, nil)
if err != nil {
log.Println(err)
Expand All @@ -161,7 +179,7 @@ func (c *tcpClientImpl) Handle(tcp net.Conn) {
}

func (c *tcpClientImpl) Dial(edBuf []byte, inHeader http.Header) (io.Closer, error) {
tcp, err := c.tcpDialer.Dial("tcp", c.Target())
tcp, err := c.netDial("tcp", c.Target())
if err == nil && len(edBuf) > 0 {
_, err = tcp.Write(edBuf)
}
Expand All @@ -177,27 +195,59 @@ func (c *tcpClientImpl) Tunnel(tcp net.Conn, conn io.Closer) {
}

func BuildClient(config ClientConfig) {
var c Client
var cImpl ClientImpl
var proxyUrl *url.URL
var proxyStr string
if len(config.Proxy) > 0 {
u, err := url.Parse(config.Proxy)
if err != nil {
log.Println(err)
}
proxyUrl = u

ru := *u
ru.User = nil
proxyStr = ru.String()
}
if len(config.TargetAddress) > 0 {
var netDial netDialerFunc
tcpDialer := &net.Dialer{
Timeout: 45 * time.Second,
}
c = &client{
ClientImpl: &tcpClientImpl{
targetAddress: config.TargetAddress,
tcpDialer: tcpDialer,
},
bindAddress: config.BindAddress,
netDial = tcpDialer.Dial

proxyDialer := proxy_FromEnvironment()
if proxyUrl != nil {
dialer, err := proxy_FromURL(proxyUrl, netDial)
if err != nil {
log.Println(err)
} else {
proxyDialer = dialer
}
}
if proxyDialer != proxy_Direct {
netDial = proxyDialer.Dial
}

cImpl = &tcpClientImpl{
targetAddress: config.TargetAddress,
netDial: netDial,
proxy: proxyStr,
}
} else {
proxy := http.ProxyFromEnvironment
if proxyUrl != nil {
proxy = http.ProxyURL(proxyUrl)
}

header := http.Header{}
if len(config.WSHeaders) != 0 {
for key, value := range config.WSHeaders {
header.Add(key, value)
}
}
wsDialer := &websocket.Dialer{
Proxy: http.ProxyFromEnvironment,
Proxy: proxy,
HandshakeTimeout: 45 * time.Second,
ReadBufferSize: BufSize,
WriteBufferSize: BufSize,
Expand All @@ -217,21 +267,27 @@ func BuildClient(config ClientConfig) {
config.WSUrl = u.String()
}
}
c = &client{
ClientImpl: &wsClientImpl{
header: header,
wsUrl: config.WSUrl,
wsDialer: wsDialer,
ed: ed,
},
bindAddress: config.BindAddress,
cImpl = &wsClientImpl{
header: header,
wsUrl: config.WSUrl,
wsDialer: wsDialer,
ed: ed,
proxy: proxyStr,
}
}

_, port, err := net.SplitHostPort(config.BindAddress)
if err != nil {
log.Println(err)
}

serverWSPath := strings.ReplaceAll(config.ServerWSPath, "{port}", port)

c := &client{
ClientImpl: cImpl,
bindAddress: config.BindAddress,
serverWSPath: serverWSPath,
}

PortToClient[port] = c
}

Expand Down
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type ClientConfig struct {
WSHeaders map[string]string `yaml:"ws-headers"`
SkipCertVerify bool `yaml:"skip-cert-verify"`
ServerName string `yaml:"servername"`
Proxy string `yaml:"proxy"`
ServerWSPath string `yaml:"server-ws-path"`
}

type ServerConfig struct {
Expand Down
95 changes: 95 additions & 0 deletions proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
"bufio"
"encoding/base64"
"errors"
"net"
"net/http"
"net/url"
"strings"
)

type netDialerFunc func(network, addr string) (net.Conn, error)

func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
return fn(network, addr)
}

func init() {
proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {
return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil
})
}

func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
hostPort = u.Host
hostNoPort = u.Host
if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
hostNoPort = hostNoPort[:i]
} else {
switch u.Scheme {
case "wss":
hostPort += ":443"
case "https":
hostPort += ":443"
default:
hostPort += ":80"
}
}
return hostPort, hostNoPort
}

type httpProxyDialer struct {
proxyURL *url.URL
forwardDial func(network, addr string) (net.Conn, error)
}

func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {
hostPort, _ := hostPortNoPort(hpd.proxyURL)
conn, err := hpd.forwardDial(network, hostPort)
if err != nil {
return nil, err
}

connectHeader := make(http.Header)
if user := hpd.proxyURL.User; user != nil {
proxyUser := user.Username()
if proxyPassword, passwordSet := user.Password(); passwordSet {
credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
connectHeader.Set("Proxy-Authorization", "Basic "+credential)
}
}

connectReq := &http.Request{
Method: http.MethodConnect,
URL: &url.URL{Opaque: addr},
Host: addr,
Header: connectHeader,
}

if err := connectReq.Write(conn); err != nil {
conn.Close()
return nil, err
}

// Read response. It's OK to use and discard buffered reader here becaue
// the remote server does not speak until spoken to.
br := bufio.NewReader(conn)
resp, err := http.ReadResponse(br, connectReq)
if err != nil {
conn.Close()
return nil, err
}

if resp.StatusCode != 200 {
conn.Close()
f := strings.SplitN(resp.Status, " ", 2)
return nil, errors.New(f[1])
}
return conn, nil
}
15 changes: 13 additions & 2 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func (s *normalServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

type internalServerHandler struct {
DestAddress string
Proxy string
Client Client
}

Expand All @@ -117,7 +118,7 @@ func (s *internalServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
return
}

log.Println("Incoming --> ", r.RemoteAddr, r.Header, " --> ( [Client]", s.DestAddress, ") --> ", s.Client.Target())
log.Println("Incoming --> ", r.RemoteAddr, r.Header, " --> ( [Client]", s.DestAddress, s.Proxy, ") --> ", s.Client.Target())

ch := make(chan io.Closer)
defer func() {
Expand Down Expand Up @@ -172,6 +173,15 @@ func closeTcpHandle(writer http.ResponseWriter, request *http.Request) {
func BuildServer(config ServerConfig) {
mux := http.NewServeMux()
hadRoot := false
for port, client := range PortToClient {
wsPath := client.GetServerWSPath()
if len(wsPath) > 0 {
config.Target = append(config.Target, ServerTargetConfig{
WSPath: wsPath,
TargetAddress: net.JoinHostPort("127.0.0.1", port),
})
}
}
for _, target := range config.Target {
if len(target.WSPath) == 0 {
target.WSPath = "/"
Expand All @@ -186,10 +196,11 @@ func BuildServer(config ServerConfig) {
log.Println("Short circuit replace (",
target.WSPath, "<->", target.TargetAddress,
") to (",
target.WSPath, "<->", client.Target(),
target.WSPath, "<->", client.Target(), client.Proxy(),
")")
sh = &internalServerHandler{
DestAddress: target.TargetAddress,
Proxy: client.Proxy(),
Client: client,
}
} else {
Expand Down
Loading

0 comments on commit 2c9130d

Please sign in to comment.