add gost library
This commit is contained in:
parent
b3461ea527
commit
ff6d3e14d8
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 ginuerzh
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
193
chain.go
Normal file
193
chain.go
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
package gost
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Proxy chain holds a list of proxy nodes
|
||||||
|
type ProxyChain struct {
|
||||||
|
nodes []ProxyNode
|
||||||
|
lastNode *ProxyNode
|
||||||
|
http2NodeIndex int
|
||||||
|
http2Enabled bool
|
||||||
|
http2Client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyChain(nodes ...ProxyNode) *ProxyChain {
|
||||||
|
chain := &ProxyChain{nodes: nodes, http2NodeIndex: -1}
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyChain) AddProxyNode(node ...ProxyNode) {
|
||||||
|
c.nodes = append(c.nodes, node...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize proxy nodes, mainly check for http2 feature.
|
||||||
|
// Should be called immediately when proxy nodes are ready.
|
||||||
|
//
|
||||||
|
// NOTE: http2 will not be enabled if not called.
|
||||||
|
func (c *ProxyChain) Init() {
|
||||||
|
length := len(c.nodes)
|
||||||
|
if length == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.lastNode = &c.nodes[length-1]
|
||||||
|
|
||||||
|
// http2 restrict: http2 will be enabled when at least one http2 proxy node present
|
||||||
|
for i, node := range c.nodes {
|
||||||
|
if node.Transport == "http2" {
|
||||||
|
glog.V(LINFO).Infoln("http2 enabled")
|
||||||
|
cfg := &tls.Config{
|
||||||
|
InsecureSkipVerify: node.insecureSkipVerify(),
|
||||||
|
ServerName: node.serverName,
|
||||||
|
}
|
||||||
|
c.initHttp2Client(node.Addr, cfg, c.nodes[:i]...)
|
||||||
|
c.http2NodeIndex = i
|
||||||
|
break // shortest chain for http2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyChain) initHttp2Client(addr string, config *tls.Config, nodes ...ProxyNode) {
|
||||||
|
tr := http2.Transport{
|
||||||
|
TLSClientConfig: config,
|
||||||
|
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||||
|
// replace the default dialer with our proxy chain.
|
||||||
|
conn, err := c.dialWithNodes(addr, nodes...)
|
||||||
|
if err != nil {
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
return tls.Client(conn, cfg), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.http2Client = &http.Client{Transport: &tr}
|
||||||
|
c.http2Enabled = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyChain) Http2Enabled() bool {
|
||||||
|
return c.http2Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to addr through proxy chain
|
||||||
|
func (c *ProxyChain) Dial(addr string) (net.Conn, error) {
|
||||||
|
if !strings.Contains(addr, ":") {
|
||||||
|
addr += ":80"
|
||||||
|
}
|
||||||
|
return c.dialWithNodes(addr, c.nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyChain) dialWithNodes(addr string, nodes ...ProxyNode) (conn net.Conn, err error) {
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
return net.DialTimeout("tcp", addr, DialTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pc *ProxyConn
|
||||||
|
|
||||||
|
if c.Http2Enabled() {
|
||||||
|
nodes = nodes[c.http2NodeIndex+1:]
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
return c.http2Connect("http", addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pc, err = c.travelNodes(nodes...)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = pc.Connect(addr); err != nil {
|
||||||
|
pc.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return pc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyChain) travelNodes(nodes ...ProxyNode) (conn *ProxyConn, err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil && conn != nil {
|
||||||
|
conn.Close()
|
||||||
|
conn = nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var cc net.Conn
|
||||||
|
node := nodes[0]
|
||||||
|
|
||||||
|
if c.Http2Enabled() {
|
||||||
|
cc, err = c.http2Connect("http", node.Addr)
|
||||||
|
} else {
|
||||||
|
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setKeepAlive(cc, KeepAliveTime)
|
||||||
|
|
||||||
|
pc := NewProxyConn(cc, node)
|
||||||
|
if err = pc.Handshake(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn = pc
|
||||||
|
for _, node := range nodes[1:] {
|
||||||
|
if err = conn.Connect(node.Addr); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pc := NewProxyConn(conn, node)
|
||||||
|
if err = pc.Handshake(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn = pc
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyChain) http2Connect(protocol, addr string) (net.Conn, error) {
|
||||||
|
if !c.Http2Enabled() {
|
||||||
|
return nil, errors.New("http2 not enabled")
|
||||||
|
}
|
||||||
|
http2Node := c.nodes[c.http2NodeIndex]
|
||||||
|
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
req := http.Request{
|
||||||
|
Method: http.MethodConnect,
|
||||||
|
URL: &url.URL{Scheme: "https", Host: http2Node.Addr},
|
||||||
|
Header: make(http.Header),
|
||||||
|
Proto: "HTTP/2.0",
|
||||||
|
ProtoMajor: 2,
|
||||||
|
ProtoMinor: 0,
|
||||||
|
Body: ioutil.NopCloser(pr),
|
||||||
|
Host: http2Node.Addr,
|
||||||
|
ContentLength: -1,
|
||||||
|
}
|
||||||
|
req.Header.Set("gost-target", addr)
|
||||||
|
if protocol != "" {
|
||||||
|
req.Header.Set("gost-protocol", protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
dump, _ := httputil.DumpRequest(&req, false)
|
||||||
|
glog.Infoln(string(dump))
|
||||||
|
}
|
||||||
|
resp, err := c.http2Client.Do(&req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil, errors.New(resp.Status)
|
||||||
|
}
|
||||||
|
conn := &Http2ClientConn{r: resp.Body, w: pw}
|
||||||
|
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", addr)
|
||||||
|
return conn, nil
|
||||||
|
}
|
611
conn.go
611
conn.go
@ -1,4 +1,4 @@
|
|||||||
package main
|
package gost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@ -9,557 +9,117 @@ import (
|
|||||||
"github.com/ginuerzh/gosocks5"
|
"github.com/ginuerzh/gosocks5"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
"github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
//"sync/atomic"
|
|
||||||
"golang.org/x/net/http2"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type ProxyConn struct {
|
||||||
connCounter int32
|
conn net.Conn
|
||||||
)
|
node ProxyNode
|
||||||
|
handshaked bool
|
||||||
var (
|
handshakeMutex sync.Mutex
|
||||||
// tcp buffer pool
|
handshakeErr error
|
||||||
tcpPool = sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return make([]byte, 32*1024)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// udp buffer pool
|
|
||||||
udpPool = sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return make([]byte, 32*1024)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func listenAndServe(arg Args) error {
|
|
||||||
var ln net.Listener
|
|
||||||
var err error
|
|
||||||
|
|
||||||
switch arg.Transport {
|
|
||||||
case "ws": // websocket connection
|
|
||||||
return NewWs(arg).ListenAndServe()
|
|
||||||
case "wss": // websocket security connection
|
|
||||||
return NewWs(arg).listenAndServeTLS()
|
|
||||||
case "tls": // tls connection
|
|
||||||
ln, err = tls.Listen("tcp", arg.Addr,
|
|
||||||
&tls.Config{Certificates: []tls.Certificate{arg.Cert}})
|
|
||||||
case "http2": // http2 connetction
|
|
||||||
return listenAndServeHttp2(arg, http.HandlerFunc(handlerHttp2Request))
|
|
||||||
case "tcp": // Local TCP port forwarding
|
|
||||||
return listenAndServeTcpForward(arg)
|
|
||||||
case "udp": // Local UDP port forwarding
|
|
||||||
return listenAndServeUdpForward(arg)
|
|
||||||
case "rtcp": // Remote TCP port forwarding
|
|
||||||
return serveRTcpForward(arg)
|
|
||||||
case "rudp": // Remote UDP port forwarding
|
|
||||||
return serveRUdpForward(arg)
|
|
||||||
default:
|
|
||||||
ln, err = net.Listen("tcp", arg.Addr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
func NewProxyConn(conn net.Conn, node ProxyNode) *ProxyConn {
|
||||||
|
return &ProxyConn{
|
||||||
|
conn: conn,
|
||||||
|
node: node,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handshake based on the proxy node info: transport, protocol, authentication, etc.
|
||||||
|
//
|
||||||
|
// NOTE: http2 will be downgrade to http (for protocol) and tls (for transport).
|
||||||
|
func (c *ProxyConn) Handshake() error {
|
||||||
|
c.handshakeMutex.Lock()
|
||||||
|
defer c.handshakeMutex.Unlock()
|
||||||
|
|
||||||
|
if err := c.handshakeErr; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if c.handshaked {
|
||||||
defer ln.Close()
|
return nil
|
||||||
|
}
|
||||||
for {
|
c.handshakeErr = c.handshake()
|
||||||
conn, err := ln.Accept()
|
return c.handshakeErr
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
setKeepAlive(conn, keepAliveTime)
|
|
||||||
|
|
||||||
go handleConn(conn, arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func listenAndServeHttp2(arg Args, handler http.Handler) error {
|
|
||||||
srv := http.Server{
|
|
||||||
Addr: arg.Addr,
|
|
||||||
Handler: handler,
|
|
||||||
TLSConfig: &tls.Config{
|
|
||||||
Certificates: []tls.Certificate{arg.Cert},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
http2.ConfigureServer(&srv, nil)
|
|
||||||
return srv.ListenAndServeTLS("", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func listenAndServeTcpForward(arg Args) error {
|
|
||||||
raddr, err := net.ResolveTCPAddr("tcp", arg.Remote)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ln, err := net.Listen("tcp", arg.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer ln.Close()
|
|
||||||
|
|
||||||
for {
|
|
||||||
conn, err := ln.Accept()
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
setKeepAlive(conn, keepAliveTime)
|
|
||||||
|
|
||||||
go handleTcpForward(conn, raddr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func listenAndServeUdpForward(arg Args) error {
|
|
||||||
laddr, err := net.ResolveUDPAddr("udp", arg.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
raddr, err := net.ResolveUDPAddr("udp", arg.Remote)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := net.ListenUDP("udp", laddr)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
if len(forwardArgs) == 0 {
|
|
||||||
for {
|
|
||||||
b := udpPool.Get().([]byte)
|
|
||||||
|
|
||||||
n, addr, err := conn.ReadFromUDP(b)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
handleUdpForwardLocal(conn, addr, raddr, b[:n])
|
|
||||||
udpPool.Put(b)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rChan, wChan := make(chan *gosocks5.UDPDatagram, 32), make(chan *gosocks5.UDPDatagram, 32)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
b := make([]byte, 32*1024)
|
|
||||||
n, addr, err := conn.ReadFromUDP(b)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case rChan <- gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]):
|
|
||||||
default:
|
|
||||||
// glog.V(LWARNING).Infof("[udp-connect] %s -> %s : rbuf is full", laddr, raddr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
dgram := <-wChan
|
|
||||||
addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String())
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err)
|
|
||||||
continue // drop silently
|
|
||||||
}
|
|
||||||
if _, err = conn.WriteToUDP(dgram.Data, addr); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
handleUdpForwardTunnel(laddr, raddr, rChan, wChan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveRTcpForward(arg Args) error {
|
|
||||||
if len(forwardArgs) == 0 {
|
|
||||||
return errors.New("rtcp: at least one -F must be assigned")
|
|
||||||
}
|
|
||||||
|
|
||||||
laddr, err := net.ResolveTCPAddr("tcp", arg.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
raddr, err := net.ResolveTCPAddr("tcp", arg.Remote)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
retry := 0
|
|
||||||
for {
|
|
||||||
conn, _, err := forwardChain(forwardArgs...)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[rtcp] %s - %s : %s", arg.Addr, arg.Remote, err)
|
|
||||||
time.Sleep((1 << uint(retry)) * time.Second)
|
|
||||||
if retry < 5 {
|
|
||||||
retry++
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
retry = 0
|
|
||||||
|
|
||||||
if err := connectRTcpForward(conn, laddr, raddr); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
time.Sleep(6 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveRUdpForward(arg Args) error {
|
|
||||||
if len(forwardArgs) == 0 {
|
|
||||||
return errors.New("rudp: at least one -F must be assigned")
|
|
||||||
}
|
|
||||||
|
|
||||||
laddr, err := net.ResolveUDPAddr("udp", arg.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
raddr, err := net.ResolveUDPAddr("udp", arg.Remote)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
retry := 0
|
|
||||||
for {
|
|
||||||
conn, _, err := forwardChain(forwardArgs...)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[rudp] %s - %s : %s", arg.Addr, arg.Remote, err)
|
|
||||||
time.Sleep((1 << uint(retry)) * time.Second)
|
|
||||||
if retry < 5 {
|
|
||||||
retry++
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
retry = 0
|
|
||||||
|
|
||||||
if err := connectRUdpForward(conn, laddr, raddr); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
time.Sleep(6 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleConn(conn net.Conn, arg Args) {
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// socks5 server supported methods
|
|
||||||
selector := &serverSelector{
|
|
||||||
methods: []uint8{
|
|
||||||
gosocks5.MethodNoAuth,
|
|
||||||
gosocks5.MethodUserPass,
|
|
||||||
MethodTLS,
|
|
||||||
MethodTLSAuth,
|
|
||||||
},
|
|
||||||
user: arg.User,
|
|
||||||
cert: arg.Cert,
|
|
||||||
}
|
|
||||||
|
|
||||||
switch arg.Protocol {
|
|
||||||
case "ss": // shadowsocks
|
|
||||||
handleShadow(conn, arg)
|
|
||||||
return
|
|
||||||
case "http":
|
|
||||||
req, err := http.ReadRequest(bufio.NewReader(conn))
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("[http]", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
handleHttpRequest(req, conn, arg)
|
|
||||||
return
|
|
||||||
case "socks", "socks5":
|
|
||||||
conn = gosocks5.ServerConn(conn, selector)
|
|
||||||
req, err := gosocks5.ReadRequest(conn)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("[socks5]", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
handleSocks5Request(req, conn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// http or socks5
|
|
||||||
|
|
||||||
//b := make([]byte, 16*1024)
|
|
||||||
b := tcpPool.Get().([]byte)
|
|
||||||
defer tcpPool.Put(b)
|
|
||||||
|
|
||||||
n, err := io.ReadAtLeast(conn, b, 2)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("[client]", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if b[0] == gosocks5.Ver5 {
|
|
||||||
mn := int(b[1]) // methods count
|
|
||||||
length := 2 + mn
|
|
||||||
if n < length {
|
|
||||||
if _, err := io.ReadFull(conn, b[n:length]); err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("[socks5]", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
methods := b[2 : 2+mn]
|
|
||||||
method := selector.Select(methods...)
|
|
||||||
if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("[socks5] select:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c, err := selector.OnSelected(method, conn)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("[socks5] onselected:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
conn = c
|
|
||||||
|
|
||||||
req, err := gosocks5.ReadRequest(conn)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("[socks5] request:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
handleSocks5Request(req, conn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.ReadRequest(bufio.NewReader(newReqReader(b[:n], conn)))
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("[http]", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
handleHttpRequest(req, conn, arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
type reqReader struct {
|
|
||||||
b []byte
|
|
||||||
r io.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
func newReqReader(b []byte, r io.Reader) *reqReader {
|
|
||||||
return &reqReader{
|
|
||||||
b: b,
|
|
||||||
r: r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reqReader) Read(p []byte) (n int, err error) {
|
|
||||||
if len(r.b) == 0 {
|
|
||||||
return r.r.Read(p)
|
|
||||||
}
|
|
||||||
n = copy(p, r.b)
|
|
||||||
r.b = r.b[n:]
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func connect(addr string, prot string, chain ...Args) (conn net.Conn, err error) {
|
|
||||||
if !strings.Contains(addr, ":") {
|
|
||||||
addr += ":80"
|
|
||||||
}
|
|
||||||
|
|
||||||
if enabled, h2host := http2Enabled(); enabled {
|
|
||||||
return connectHttp2(http2Client, h2host, addr, prot)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(chain) == 0 {
|
|
||||||
return net.DialTimeout("tcp", addr, time.Second*90)
|
|
||||||
}
|
|
||||||
|
|
||||||
var end Args
|
|
||||||
conn, end, err = forwardChain(chain...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := establish(conn, addr, end); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func http2Enabled() (enabled bool, host string) {
|
|
||||||
length := len(forwardArgs)
|
|
||||||
if http2Client == nil || length == 0 || forwardArgs[length-1].Transport != "http2" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return true, forwardArgs[length-1].Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectHttp2(client *http.Client, host, target string, prot string) (net.Conn, error) {
|
|
||||||
pr, pw := io.Pipe()
|
|
||||||
req := http.Request{
|
|
||||||
Method: http.MethodConnect,
|
|
||||||
URL: &url.URL{Scheme: "https", Host: host},
|
|
||||||
Header: make(http.Header),
|
|
||||||
Proto: "HTTP/2.0",
|
|
||||||
ProtoMajor: 2,
|
|
||||||
ProtoMinor: 0,
|
|
||||||
Body: ioutil.NopCloser(pr),
|
|
||||||
Host: host,
|
|
||||||
ContentLength: -1,
|
|
||||||
}
|
|
||||||
req.Header.Set("gost-target", target)
|
|
||||||
if prot != "" {
|
|
||||||
req.Header.Set("gost-protocol", prot)
|
|
||||||
}
|
|
||||||
|
|
||||||
if glog.V(LDEBUG) {
|
|
||||||
dump, _ := httputil.DumpRequest(&req, false)
|
|
||||||
glog.Infoln(string(dump))
|
|
||||||
}
|
|
||||||
resp, err := client.Do(&req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
resp.Body.Close()
|
|
||||||
return nil, errors.New(resp.Status)
|
|
||||||
}
|
|
||||||
conn := &Http2ClientConn{r: resp.Body, w: pw}
|
|
||||||
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", target)
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// establish connection throughout the forward chain
|
|
||||||
func forwardChain(chain ...Args) (conn net.Conn, end Args, err error) {
|
|
||||||
defer func() {
|
|
||||||
if err != nil && conn != nil {
|
|
||||||
conn.Close()
|
|
||||||
conn = nil
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
end = chain[0]
|
|
||||||
if conn, err = net.DialTimeout("tcp", end.Addr, time.Second*90); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setKeepAlive(conn, keepAliveTime)
|
|
||||||
|
|
||||||
c, err := forward(conn, end)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
conn = c
|
|
||||||
|
|
||||||
chain = chain[1:]
|
|
||||||
for _, arg := range chain {
|
|
||||||
if err = establish(conn, arg.Addr, end); err != nil {
|
|
||||||
goto exit
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err = forward(conn, arg)
|
|
||||||
if err != nil {
|
|
||||||
goto exit
|
|
||||||
}
|
|
||||||
conn = c
|
|
||||||
end = arg
|
|
||||||
}
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func forward(conn net.Conn, arg Args) (net.Conn, error) {
|
|
||||||
var err error
|
|
||||||
if glog.V(LINFO) {
|
|
||||||
proto := arg.Protocol
|
|
||||||
trans := arg.Transport
|
|
||||||
if proto == "" {
|
|
||||||
proto = "http" // default is http
|
|
||||||
}
|
|
||||||
if trans == "" { // default is tcp
|
|
||||||
trans = "tcp"
|
|
||||||
}
|
|
||||||
glog.V(LDEBUG).Infof("forward: %s/%s %s", proto, trans, arg.Addr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ProxyConn) handshake() error {
|
||||||
var tlsUsed bool
|
var tlsUsed bool
|
||||||
|
|
||||||
switch arg.Transport {
|
switch c.node.Transport {
|
||||||
case "ws": // websocket connection
|
case "ws": // websocket connection
|
||||||
conn, err = wsClient("ws", conn, arg.Addr)
|
conn, err := wsClient("ws", c.conn, c.node.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
c.conn = conn
|
||||||
case "wss": // websocket security
|
case "wss": // websocket security
|
||||||
tlsUsed = true
|
tlsUsed = true
|
||||||
conn, err = wsClient("wss", conn, arg.Addr)
|
conn, err := wsClient("wss", c.conn, c.node.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
case "tls": // tls connection
|
c.conn = conn
|
||||||
|
case "tls", "http2": // tls connection
|
||||||
tlsUsed = true
|
tlsUsed = true
|
||||||
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
cfg := &tls.Config{
|
||||||
// conn = tls.Client(conn, &tls.Config{ServerName: "ice139.com"})
|
InsecureSkipVerify: c.node.insecureSkipVerify(),
|
||||||
case "tcp":
|
ServerName: c.node.serverName,
|
||||||
fallthrough
|
}
|
||||||
|
c.conn = tls.Client(c.conn, cfg)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
switch arg.Protocol {
|
switch c.node.Protocol {
|
||||||
case "socks", "socks5":
|
case "socks", "socks5": // socks5 handshake with auth and tls supported
|
||||||
selector := &clientSelector{
|
selector := &clientSelector{
|
||||||
methods: []uint8{
|
methods: []uint8{
|
||||||
gosocks5.MethodNoAuth,
|
gosocks5.MethodNoAuth,
|
||||||
gosocks5.MethodUserPass,
|
gosocks5.MethodUserPass,
|
||||||
//MethodTLS,
|
//MethodTLS,
|
||||||
},
|
},
|
||||||
user: arg.User,
|
user: c.node.User,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tlsUsed { // if transport is not security, enable security socks5
|
if !tlsUsed { // if transport is not security, enable security socks5
|
||||||
selector.methods = append(selector.methods, MethodTLS)
|
selector.methods = append(selector.methods, MethodTLS)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := gosocks5.ClientConn(conn, selector)
|
conn := gosocks5.ClientConn(c.conn, selector)
|
||||||
if err := c.Handleshake(); err != nil {
|
if err := conn.Handleshake(); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
conn = c
|
c.conn = conn
|
||||||
case "ss": // shadowsocks
|
case "ss": // shadowsocks
|
||||||
if arg.User != nil {
|
if c.node.User != nil {
|
||||||
method := arg.User.Username()
|
method := c.node.User.Username()
|
||||||
password, _ := arg.User.Password()
|
password, _ := c.node.User.Password()
|
||||||
cipher, err := shadowsocks.NewCipher(method, password)
|
cipher, err := shadowsocks.NewCipher(method, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
conn = shadowsocks.NewConn(conn, cipher)
|
c.conn = shadowsocks.NewConn(c.conn, cipher)
|
||||||
}
|
}
|
||||||
case "http":
|
case "http", "http2":
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn, nil
|
c.handshaked = true
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func establish(conn net.Conn, addr string, arg Args) error {
|
// Connect to addr through this proxy node
|
||||||
switch arg.Protocol {
|
func (c *ProxyConn) Connect(addr string) error {
|
||||||
|
switch c.node.Protocol {
|
||||||
case "ss": // shadowsocks
|
case "ss": // shadowsocks
|
||||||
host, port, err := net.SplitHostPort(addr)
|
host, port, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -576,9 +136,10 @@ func establish(conn net.Conn, addr string, arg Args) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b := buf.Bytes()
|
b := buf.Bytes()
|
||||||
if _, err := conn.Write(b[3:]); err != nil {
|
if _, err := c.Write(b[3:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.V(LDEBUG).Infoln(req)
|
glog.V(LDEBUG).Infoln(req)
|
||||||
case "socks", "socks5":
|
case "socks", "socks5":
|
||||||
host, port, err := net.SplitHostPort(addr)
|
host, port, err := net.SplitHostPort(addr)
|
||||||
@ -591,12 +152,12 @@ func establish(conn net.Conn, addr string, arg Args) error {
|
|||||||
Host: host,
|
Host: host,
|
||||||
Port: uint16(p),
|
Port: uint16(p),
|
||||||
})
|
})
|
||||||
if err := req.Write(conn); err != nil {
|
if err := req.Write(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
glog.V(LDEBUG).Infoln(req)
|
glog.V(LDEBUG).Infoln(req)
|
||||||
|
|
||||||
rep, err := gosocks5.ReadReply(conn)
|
rep, err := gosocks5.ReadReply(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -604,11 +165,11 @@ func establish(conn net.Conn, addr string, arg Args) error {
|
|||||||
if rep.Rep != gosocks5.Succeeded {
|
if rep.Rep != gosocks5.Succeeded {
|
||||||
return errors.New("Service unavailable")
|
return errors.New("Service unavailable")
|
||||||
}
|
}
|
||||||
case "http":
|
case "http", "http2":
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
req := &http.Request{
|
req := &http.Request{
|
||||||
Method: "CONNECT",
|
Method: http.MethodConnect,
|
||||||
URL: &url.URL{Host: addr},
|
URL: &url.URL{Host: addr},
|
||||||
Host: addr,
|
Host: addr,
|
||||||
ProtoMajor: 1,
|
ProtoMajor: 1,
|
||||||
@ -616,11 +177,11 @@ func establish(conn net.Conn, addr string, arg Args) error {
|
|||||||
Header: make(http.Header),
|
Header: make(http.Header),
|
||||||
}
|
}
|
||||||
req.Header.Set("Proxy-Connection", "keep-alive")
|
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||||
if arg.User != nil {
|
if c.node.User != nil {
|
||||||
req.Header.Set("Proxy-Authorization",
|
req.Header.Set("Proxy-Authorization",
|
||||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(arg.User.String())))
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(c.node.User.String())))
|
||||||
}
|
}
|
||||||
if err := req.Write(conn); err != nil {
|
if err := req.Write(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if glog.V(LDEBUG) {
|
if glog.V(LDEBUG) {
|
||||||
@ -628,7 +189,7 @@ func establish(conn net.Conn, addr string, arg Args) error {
|
|||||||
glog.Infoln(string(dump))
|
glog.Infoln(string(dump))
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
resp, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -643,3 +204,35 @@ func establish(conn net.Conn, addr string, arg Args) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ProxyConn) Read(b []byte) (n int, err error) {
|
||||||
|
return c.conn.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyConn) Write(b []byte) (n int, err error) {
|
||||||
|
return c.conn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyConn) RemoteAddr() net.Addr {
|
||||||
|
return c.conn.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
98
gost.go
Normal file
98
gost.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package gost
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LFATAL = iota
|
||||||
|
LERROR
|
||||||
|
LWARNING
|
||||||
|
LINFO
|
||||||
|
LDEBUG
|
||||||
|
LVDEBUG // verbose debug for http2
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DialTimeout = 30 * time.Second
|
||||||
|
KeepAliveTime = 180 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultCertFile = "cert.pem"
|
||||||
|
DefaultKeyFile = "key.pem"
|
||||||
|
|
||||||
|
// This is the default cert and key data for convenience, providing your own cert is recommended.
|
||||||
|
DefaultRawCert = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
|
||||||
|
bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj
|
||||||
|
bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf
|
||||||
|
d/0/QS3E93cYIWHw831i/IKxigdRD/XMZonLdEHywW6lOiXazaP8e6CqPGSmnl0x
|
||||||
|
5k/3dvGCMj2JCVxM6+z7NpL+AiwvXmvkj/TOciCgwqssCwYS2CiVwjfazRjx1ZUJ
|
||||||
|
VDC5qiyRsfktQ2fVHrpnJGVSRagmiQgwGWBilVG9B8QvRtpQKN/GQGq17oIQm8aK
|
||||||
|
kOdPt93g93ojMIg7YJpgDgOirvVz/hDn7YD4ryrtPos9CMafFkJprymKpRHyvz7P
|
||||||
|
8a3+OkuPjFjPnwOHQ5u1U3+8vC44vfb1ExWzDLoT8Xp8Gndx39k0f7MVOol3GnYu
|
||||||
|
MN/dvNUdAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEF
|
||||||
|
BQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDALBgkqhkiG
|
||||||
|
9w0BAQUDggEBAIG8CJqvTIgJnNOK+i5/IUc/3yF/mSCWuG8qP+Fmo2t6T0PVOtc0
|
||||||
|
8wiWH5iWtCAhjn0MRY9l/hIjWm6gUZGHCGuEgsOPpJDYGoNLjH9Xwokm4y3LFNRK
|
||||||
|
UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO
|
||||||
|
RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/
|
||||||
|
BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS
|
||||||
|
uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
DefaultRawKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ
|
||||||
|
y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0
|
||||||
|
znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg
|
||||||
|
YpVRvQfEL0baUCjfxkBqte6CEJvGipDnT7fd4Pd6IzCIO2CaYA4Doq71c/4Q5+2A
|
||||||
|
+K8q7T6LPQjGnxZCaa8piqUR8r8+z/Gt/jpLj4xYz58Dh0ObtVN/vLwuOL329RMV
|
||||||
|
swy6E/F6fBp3cd/ZNH+zFTqJdxp2LjDf3bzVHQIDAQABAoIBAHal26147nQ+pHwY
|
||||||
|
jxwers3XDCjWvup7g79lfcqlKi79UiUEA6KYHm7UogMYewt7p4nb2KwH+XycvDiB
|
||||||
|
aAUf5flXpTs+6IkWauUDiLZi4PlV7uiEexUq5FjirlL0U/6MjbudX4bK4WQ4uxDc
|
||||||
|
WaV07Kw2iJFOOHLDKT0en9JaX5jtJNc4ZnE9efFoQ5jfypPWtRw65G1rULEg6nvc
|
||||||
|
GDh+1ce+4foCkpLRC9c24xAwJONZG6x3UqrSS9qfAsb73nWRQrTfUcO3nhoN8VvL
|
||||||
|
kL9skn1+S06NyUN0KoEtyRBp+RcpXSsBWAo6qZmo/WqhB/gjzWrxVwn20+yJSm35
|
||||||
|
ZsMc6QECgYEA8GS+Mp9xfB2szWHz6YTOO1Uu4lHM1ccZMwS1G+dL0KO3uGAiPdvp
|
||||||
|
woVot6v6w88t7onXsLo5pgz7SYug0CpkF3K/MRd1Ar4lH7PK7IBQ6rFr9ppVxDbx
|
||||||
|
AEWRswUoPbKCr7W6HU8LbQHDavsDlEIwc6+DiwnL4BzlKjb7RpgQEz0CgYEA6sB5
|
||||||
|
uHvx3Y5FDcGk1n73leQSAcq14l3ZLNpjrs8msoREDil/j5WmuSN58/7PGMiMgHEi
|
||||||
|
1vLm3H796JmvGr9OBvspOjHyk07ui2/We/j9Hoxm1VWhyi8HkLNDj70HKalTTFMz
|
||||||
|
RHO4O+0xCva+h9mKZrRMVktXr2jjdFn/0MYIZ2ECgYAIIsC1IeRLWQ3CHbCNlKsO
|
||||||
|
IwHlMvOFwKk/qsceXKOaOhA7szU1dr3gkXdL0Aw6mEZrrkqYdpUA46uVf54/rU+Z
|
||||||
|
445I8QxKvXiwK/uQKX+TkdGflPWWIG3jnnch4ejMvb/ihnn4B/bRB6A/fKNQXzUY
|
||||||
|
lTYUfI5j1VaEKTwz1W2l2QKBgByFCcSp+jZqhGUpc3dDsZyaOr3Q/Mvlju7uEVI5
|
||||||
|
hIAHpaT60a6GBd1UPAqymEJwivFHzW3D0NxU6VAK68UaHMaoWNfjHY9b9YsnKS2i
|
||||||
|
kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB
|
||||||
|
nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X
|
||||||
|
2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH
|
||||||
|
85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
)
|
||||||
|
|
||||||
|
func setKeepAlive(conn net.Conn, d time.Duration) error {
|
||||||
|
c, ok := conn.(*net.TCPConn)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("Not a TCP connection")
|
||||||
|
}
|
||||||
|
if err := c.SetKeepAlive(true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.SetKeepAlivePeriod(d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadCertificate(certFile, keyFile string) (tls.Certificate, error) {
|
||||||
|
tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
|
if err == nil {
|
||||||
|
return tlsCert, nil
|
||||||
|
}
|
||||||
|
glog.V(LWARNING).Infoln(err)
|
||||||
|
return tls.X509KeyPair([]byte(DefaultRawCert), []byte(DefaultRawKey))
|
||||||
|
}
|
645
gost/conn.go
Normal file
645
gost/conn.go
Normal file
@ -0,0 +1,645 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"github.com/ginuerzh/gosocks5"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
//"sync/atomic"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
connCounter int32
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// tcp buffer pool
|
||||||
|
tcpPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, 32*1024)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// udp buffer pool
|
||||||
|
udpPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, 32*1024)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func listenAndServe(arg Args) error {
|
||||||
|
var ln net.Listener
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch arg.Transport {
|
||||||
|
case "ws": // websocket connection
|
||||||
|
return NewWs(arg).ListenAndServe()
|
||||||
|
case "wss": // websocket security connection
|
||||||
|
return NewWs(arg).listenAndServeTLS()
|
||||||
|
case "tls": // tls connection
|
||||||
|
ln, err = tls.Listen("tcp", arg.Addr,
|
||||||
|
&tls.Config{Certificates: []tls.Certificate{arg.Cert}})
|
||||||
|
case "http2": // http2 connetction
|
||||||
|
return listenAndServeHttp2(arg, http.HandlerFunc(handlerHttp2Request))
|
||||||
|
case "tcp": // Local TCP port forwarding
|
||||||
|
return listenAndServeTcpForward(arg)
|
||||||
|
case "udp": // Local UDP port forwarding
|
||||||
|
return listenAndServeUdpForward(arg)
|
||||||
|
case "rtcp": // Remote TCP port forwarding
|
||||||
|
return serveRTcpForward(arg)
|
||||||
|
case "rudp": // Remote UDP port forwarding
|
||||||
|
return serveRUdpForward(arg)
|
||||||
|
default:
|
||||||
|
ln, err = net.Listen("tcp", arg.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer ln.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
setKeepAlive(conn, keepAliveTime)
|
||||||
|
|
||||||
|
go handleConn(conn, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenAndServeHttp2(arg Args, handler http.Handler) error {
|
||||||
|
srv := http.Server{
|
||||||
|
Addr: arg.Addr,
|
||||||
|
Handler: handler,
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{arg.Cert},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
http2.ConfigureServer(&srv, nil)
|
||||||
|
return srv.ListenAndServeTLS("", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenAndServeTcpForward(arg Args) error {
|
||||||
|
raddr, err := net.ResolveTCPAddr("tcp", arg.Remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ln, err := net.Listen("tcp", arg.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer ln.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
setKeepAlive(conn, keepAliveTime)
|
||||||
|
|
||||||
|
go handleTcpForward(conn, raddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenAndServeUdpForward(arg Args) error {
|
||||||
|
laddr, err := net.ResolveUDPAddr("udp", arg.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
raddr, err := net.ResolveUDPAddr("udp", arg.Remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.ListenUDP("udp", laddr)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
if len(forwardArgs) == 0 {
|
||||||
|
for {
|
||||||
|
b := udpPool.Get().([]byte)
|
||||||
|
|
||||||
|
n, addr, err := conn.ReadFromUDP(b)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
handleUdpForwardLocal(conn, addr, raddr, b[:n])
|
||||||
|
udpPool.Put(b)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rChan, wChan := make(chan *gosocks5.UDPDatagram, 32), make(chan *gosocks5.UDPDatagram, 32)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
b := make([]byte, 32*1024)
|
||||||
|
n, addr, err := conn.ReadFromUDP(b)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case rChan <- gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]):
|
||||||
|
default:
|
||||||
|
// glog.V(LWARNING).Infof("[udp-connect] %s -> %s : rbuf is full", laddr, raddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
dgram := <-wChan
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String())
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err)
|
||||||
|
continue // drop silently
|
||||||
|
}
|
||||||
|
if _, err = conn.WriteToUDP(dgram.Data, addr); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
handleUdpForwardTunnel(laddr, raddr, rChan, wChan)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveRTcpForward(arg Args) error {
|
||||||
|
if len(forwardArgs) == 0 {
|
||||||
|
return errors.New("rtcp: at least one -F must be assigned")
|
||||||
|
}
|
||||||
|
|
||||||
|
laddr, err := net.ResolveTCPAddr("tcp", arg.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
raddr, err := net.ResolveTCPAddr("tcp", arg.Remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
retry := 0
|
||||||
|
for {
|
||||||
|
conn, _, err := forwardChain(forwardArgs...)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[rtcp] %s - %s : %s", arg.Addr, arg.Remote, err)
|
||||||
|
time.Sleep((1 << uint(retry)) * time.Second)
|
||||||
|
if retry < 5 {
|
||||||
|
retry++
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
retry = 0
|
||||||
|
|
||||||
|
if err := connectRTcpForward(conn, laddr, raddr); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
time.Sleep(6 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveRUdpForward(arg Args) error {
|
||||||
|
if len(forwardArgs) == 0 {
|
||||||
|
return errors.New("rudp: at least one -F must be assigned")
|
||||||
|
}
|
||||||
|
|
||||||
|
laddr, err := net.ResolveUDPAddr("udp", arg.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
raddr, err := net.ResolveUDPAddr("udp", arg.Remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
retry := 0
|
||||||
|
for {
|
||||||
|
conn, _, err := forwardChain(forwardArgs...)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[rudp] %s - %s : %s", arg.Addr, arg.Remote, err)
|
||||||
|
time.Sleep((1 << uint(retry)) * time.Second)
|
||||||
|
if retry < 5 {
|
||||||
|
retry++
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
retry = 0
|
||||||
|
|
||||||
|
if err := connectRUdpForward(conn, laddr, raddr); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
time.Sleep(6 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleConn(conn net.Conn, arg Args) {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// socks5 server supported methods
|
||||||
|
selector := &serverSelector{
|
||||||
|
methods: []uint8{
|
||||||
|
gosocks5.MethodNoAuth,
|
||||||
|
gosocks5.MethodUserPass,
|
||||||
|
MethodTLS,
|
||||||
|
MethodTLSAuth,
|
||||||
|
},
|
||||||
|
user: arg.User,
|
||||||
|
cert: arg.Cert,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch arg.Protocol {
|
||||||
|
case "ss": // shadowsocks
|
||||||
|
handleShadow(conn, arg)
|
||||||
|
return
|
||||||
|
case "http":
|
||||||
|
req, err := http.ReadRequest(bufio.NewReader(conn))
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[http]", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handleHttpRequest(req, conn, arg)
|
||||||
|
return
|
||||||
|
case "socks", "socks5":
|
||||||
|
conn = gosocks5.ServerConn(conn, selector)
|
||||||
|
req, err := gosocks5.ReadRequest(conn)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[socks5]", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handleSocks5Request(req, conn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// http or socks5
|
||||||
|
|
||||||
|
//b := make([]byte, 16*1024)
|
||||||
|
b := tcpPool.Get().([]byte)
|
||||||
|
defer tcpPool.Put(b)
|
||||||
|
|
||||||
|
n, err := io.ReadAtLeast(conn, b, 2)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[client]", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b[0] == gosocks5.Ver5 {
|
||||||
|
mn := int(b[1]) // methods count
|
||||||
|
length := 2 + mn
|
||||||
|
if n < length {
|
||||||
|
if _, err := io.ReadFull(conn, b[n:length]); err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[socks5]", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methods := b[2 : 2+mn]
|
||||||
|
method := selector.Select(methods...)
|
||||||
|
if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[socks5] select:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c, err := selector.OnSelected(method, conn)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[socks5] onselected:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn = c
|
||||||
|
|
||||||
|
req, err := gosocks5.ReadRequest(conn)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[socks5] request:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handleSocks5Request(req, conn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.ReadRequest(bufio.NewReader(newReqReader(b[:n], conn)))
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[http]", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handleHttpRequest(req, conn, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
type reqReader struct {
|
||||||
|
b []byte
|
||||||
|
r io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReqReader(b []byte, r io.Reader) *reqReader {
|
||||||
|
return &reqReader{
|
||||||
|
b: b,
|
||||||
|
r: r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reqReader) Read(p []byte) (n int, err error) {
|
||||||
|
if len(r.b) == 0 {
|
||||||
|
return r.r.Read(p)
|
||||||
|
}
|
||||||
|
n = copy(p, r.b)
|
||||||
|
r.b = r.b[n:]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func connect(addr string, prot string, chain ...Args) (conn net.Conn, err error) {
|
||||||
|
if !strings.Contains(addr, ":") {
|
||||||
|
addr += ":80"
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabled, h2host := http2Enabled(); enabled {
|
||||||
|
return connectHttp2(http2Client, h2host, addr, prot)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(chain) == 0 {
|
||||||
|
return net.DialTimeout("tcp", addr, time.Second*90)
|
||||||
|
}
|
||||||
|
|
||||||
|
var end Args
|
||||||
|
conn, end, err = forwardChain(chain...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := establish(conn, addr, end); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func http2Enabled() (enabled bool, host string) {
|
||||||
|
length := len(forwardArgs)
|
||||||
|
if http2Client == nil || length == 0 || forwardArgs[length-1].Transport != "http2" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return true, forwardArgs[length-1].Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectHttp2(client *http.Client, host, target string, prot string) (net.Conn, error) {
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
req := http.Request{
|
||||||
|
Method: http.MethodConnect,
|
||||||
|
URL: &url.URL{Scheme: "https", Host: host},
|
||||||
|
Header: make(http.Header),
|
||||||
|
Proto: "HTTP/2.0",
|
||||||
|
ProtoMajor: 2,
|
||||||
|
ProtoMinor: 0,
|
||||||
|
Body: ioutil.NopCloser(pr),
|
||||||
|
Host: host,
|
||||||
|
ContentLength: -1,
|
||||||
|
}
|
||||||
|
req.Header.Set("gost-target", target)
|
||||||
|
if prot != "" {
|
||||||
|
req.Header.Set("gost-protocol", prot)
|
||||||
|
}
|
||||||
|
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
dump, _ := httputil.DumpRequest(&req, false)
|
||||||
|
glog.Infoln(string(dump))
|
||||||
|
}
|
||||||
|
resp, err := client.Do(&req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil, errors.New(resp.Status)
|
||||||
|
}
|
||||||
|
conn := &Http2ClientConn{r: resp.Body, w: pw}
|
||||||
|
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", target)
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// establish connection throughout the forward chain
|
||||||
|
func forwardChain(chain ...Args) (conn net.Conn, end Args, err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil && conn != nil {
|
||||||
|
conn.Close()
|
||||||
|
conn = nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
end = chain[0]
|
||||||
|
if conn, err = net.DialTimeout("tcp", end.Addr, time.Second*90); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setKeepAlive(conn, keepAliveTime)
|
||||||
|
|
||||||
|
c, err := forward(conn, end)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn = c
|
||||||
|
|
||||||
|
chain = chain[1:]
|
||||||
|
for _, arg := range chain {
|
||||||
|
if err = establish(conn, arg.Addr, end); err != nil {
|
||||||
|
goto exit
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = forward(conn, arg)
|
||||||
|
if err != nil {
|
||||||
|
goto exit
|
||||||
|
}
|
||||||
|
conn = c
|
||||||
|
end = arg
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func forward(conn net.Conn, arg Args) (net.Conn, error) {
|
||||||
|
var err error
|
||||||
|
if glog.V(LINFO) {
|
||||||
|
proto := arg.Protocol
|
||||||
|
trans := arg.Transport
|
||||||
|
if proto == "" {
|
||||||
|
proto = "http" // default is http
|
||||||
|
}
|
||||||
|
if trans == "" { // default is tcp
|
||||||
|
trans = "tcp"
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infof("forward: %s/%s %s", proto, trans, arg.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tlsUsed bool
|
||||||
|
|
||||||
|
switch arg.Transport {
|
||||||
|
case "ws": // websocket connection
|
||||||
|
conn, err = wsClient("ws", conn, arg.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case "wss": // websocket security
|
||||||
|
tlsUsed = true
|
||||||
|
conn, err = wsClient("wss", conn, arg.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case "tls": // tls connection
|
||||||
|
tlsUsed = true
|
||||||
|
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
||||||
|
// conn = tls.Client(conn, &tls.Config{ServerName: "ice139.com"})
|
||||||
|
case "tcp":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
switch arg.Protocol {
|
||||||
|
case "socks", "socks5":
|
||||||
|
selector := &clientSelector{
|
||||||
|
methods: []uint8{
|
||||||
|
gosocks5.MethodNoAuth,
|
||||||
|
gosocks5.MethodUserPass,
|
||||||
|
//MethodTLS,
|
||||||
|
},
|
||||||
|
user: arg.User,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tlsUsed { // if transport is not security, enable security socks5
|
||||||
|
selector.methods = append(selector.methods, MethodTLS)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := gosocks5.ClientConn(conn, selector)
|
||||||
|
if err := c.Handleshake(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn = c
|
||||||
|
case "ss": // shadowsocks
|
||||||
|
if arg.User != nil {
|
||||||
|
method := arg.User.Username()
|
||||||
|
password, _ := arg.User.Password()
|
||||||
|
cipher, err := shadowsocks.NewCipher(method, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn = shadowsocks.NewConn(conn, cipher)
|
||||||
|
}
|
||||||
|
case "http":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func establish(conn net.Conn, addr string, arg Args) error {
|
||||||
|
switch arg.Protocol {
|
||||||
|
case "ss": // shadowsocks
|
||||||
|
host, port, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p, _ := strconv.Atoi(port)
|
||||||
|
req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{
|
||||||
|
Type: gosocks5.AddrDomain,
|
||||||
|
Host: host,
|
||||||
|
Port: uint16(p),
|
||||||
|
})
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
if err := req.Write(&buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b := buf.Bytes()
|
||||||
|
if _, err := conn.Write(b[3:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infoln(req)
|
||||||
|
case "socks", "socks5":
|
||||||
|
host, port, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p, _ := strconv.Atoi(port)
|
||||||
|
req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{
|
||||||
|
Type: gosocks5.AddrDomain,
|
||||||
|
Host: host,
|
||||||
|
Port: uint16(p),
|
||||||
|
})
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infoln(req)
|
||||||
|
|
||||||
|
rep, err := gosocks5.ReadReply(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infoln(rep)
|
||||||
|
if rep.Rep != gosocks5.Succeeded {
|
||||||
|
return errors.New("Service unavailable")
|
||||||
|
}
|
||||||
|
case "http":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
req := &http.Request{
|
||||||
|
Method: "CONNECT",
|
||||||
|
URL: &url.URL{Host: addr},
|
||||||
|
Host: addr,
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
Header: make(http.Header),
|
||||||
|
}
|
||||||
|
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||||
|
if arg.User != nil {
|
||||||
|
req.Header.Set("Proxy-Authorization",
|
||||||
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(arg.User.String())))
|
||||||
|
}
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
dump, _ := httputil.DumpRequest(req, false)
|
||||||
|
glog.Infoln(string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
glog.Infoln(string(dump))
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return errors.New(resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
310
gost/http.go
Normal file
310
gost/http.go
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
http2Client *http.Client
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) {
|
||||||
|
glog.V(LINFO).Infof("[http] %s %s - %s %s", req.Method, conn.RemoteAddr(), req.Host, req.Proto)
|
||||||
|
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
dump, _ := httputil.DumpRequest(req, false)
|
||||||
|
glog.Infoln(string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
var username, password string
|
||||||
|
if arg.User != nil {
|
||||||
|
username = arg.User.Username()
|
||||||
|
password, _ = arg.User.Password()
|
||||||
|
}
|
||||||
|
|
||||||
|
u, p, _ := basicAuth(req.Header.Get("Proxy-Authorization"))
|
||||||
|
req.Header.Del("Proxy-Authorization")
|
||||||
|
if (username != "" && u != username) || (password != "" && p != password) {
|
||||||
|
resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
||||||
|
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
|
||||||
|
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
|
||||||
|
|
||||||
|
if _, err := conn.Write([]byte(resp)); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[http] %s <- %s : %s", conn.RemoteAddr(), req.Host, err)
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, resp)
|
||||||
|
|
||||||
|
glog.V(LWARNING).Infof("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(forwardArgs) > 0 {
|
||||||
|
last := forwardArgs[len(forwardArgs)-1]
|
||||||
|
if last.Protocol == "http" || last.Protocol == "" {
|
||||||
|
forwardHttpRequest(req, conn, arg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := connect(req.Host, "http", forwardArgs...)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
||||||
|
|
||||||
|
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
||||||
|
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||||
|
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
|
||||||
|
conn.Write(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
if req.Method == http.MethodConnect {
|
||||||
|
b := []byte("HTTP/1.1 200 Connection established\r\n" +
|
||||||
|
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||||
|
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
|
||||||
|
conn.Write(b)
|
||||||
|
} else {
|
||||||
|
req.Header.Del("Proxy-Connection")
|
||||||
|
req.Header.Set("Connection", "Keep-Alive")
|
||||||
|
|
||||||
|
if err = req.Write(c); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
|
||||||
|
Transport(conn, c)
|
||||||
|
glog.V(LINFO).Infof("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func forwardHttpRequest(req *http.Request, conn net.Conn, arg Args) {
|
||||||
|
last := forwardArgs[len(forwardArgs)-1]
|
||||||
|
c, _, err := forwardChain(forwardArgs...)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), last.Addr, err)
|
||||||
|
|
||||||
|
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
||||||
|
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||||
|
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), last.Addr, string(b))
|
||||||
|
conn.Write(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
if last.User != nil {
|
||||||
|
req.Header.Set("Proxy-Authorization",
|
||||||
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(last.User.String())))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = req.Write(c); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
glog.V(LINFO).Infof("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
|
||||||
|
Transport(conn, c)
|
||||||
|
glog.V(LINFO).Infof("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Http2ClientConn struct {
|
||||||
|
r io.Reader
|
||||||
|
w io.Writer
|
||||||
|
localAddr net.Addr
|
||||||
|
remoteAddr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Http2ClientConn) Read(b []byte) (n int, err error) {
|
||||||
|
return c.r.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Http2ClientConn) Write(b []byte) (n int, err error) {
|
||||||
|
return c.w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Http2ClientConn) Close() error {
|
||||||
|
if rc, ok := c.r.(io.ReadCloser); ok {
|
||||||
|
return rc.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Http2ClientConn) LocalAddr() net.Addr {
|
||||||
|
return c.localAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Http2ClientConn) RemoteAddr() net.Addr {
|
||||||
|
return c.remoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Http2ClientConn) SetDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Http2ClientConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Http2ClientConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// init http2 client with target http2 proxy server addr, and forward chain chain
|
||||||
|
func initHttp2Client(host string, chain ...Args) {
|
||||||
|
tr := http2.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||||
|
// replace the default dialer with our forward chain.
|
||||||
|
conn, err := connect(host, "http2", chain...)
|
||||||
|
if err != nil {
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
return tls.Client(conn, cfg), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
http2Client = &http.Client{Transport: &tr}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlerHttp2Request(w http.ResponseWriter, req *http.Request) {
|
||||||
|
target := req.Header.Get("gost-target")
|
||||||
|
if target == "" {
|
||||||
|
target = req.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[http2] %s %s - %s %s", req.Method, req.RemoteAddr, target, req.Proto)
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
dump, _ := httputil.DumpRequest(req, false)
|
||||||
|
glog.Infoln(string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := connect(target, req.Header.Get("gost-protocol"), forwardArgs...)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||||
|
w.Header().Set("Proxy-Agent", "gost/"+Version)
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
if fw, ok := w.(http.Flusher); ok {
|
||||||
|
fw.Flush()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, target)
|
||||||
|
errc := make(chan error, 2)
|
||||||
|
|
||||||
|
if req.Method == http.MethodConnect {
|
||||||
|
w.Header().Set("Proxy-Agent", "gost/"+Version)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if fw, ok := w.(http.Flusher); ok {
|
||||||
|
fw.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// compatible with HTTP 1.x
|
||||||
|
if hj, ok := w.(http.Hijacker); ok && req.ProtoMajor == 1 {
|
||||||
|
// we take over the underly connection
|
||||||
|
conn, _, err := hj.Hijack()
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
go Pipe(conn, c, errc)
|
||||||
|
go Pipe(c, conn, errc)
|
||||||
|
} else {
|
||||||
|
go Pipe(req.Body, c, errc)
|
||||||
|
go Pipe(c, flushWriter{w}, errc)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-errc:
|
||||||
|
// glog.V(LWARNING).Infoln("exit", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req.Header.Set("Connection", "Keep-Alive")
|
||||||
|
if err = req.Write(c); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
for k, v := range resp.Header {
|
||||||
|
for _, vv := range v {
|
||||||
|
w.Header().Add(k, vv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.WriteHeader(resp.StatusCode)
|
||||||
|
if fw, ok := w.(http.Flusher); ok {
|
||||||
|
fw.Flush()
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[http2] %s <- %s : %s", req.RemoteAddr, target, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
//func processSocks5OverHttp2()
|
||||||
|
|
||||||
|
func handleHttp2Transport(w http.ResponseWriter, req *http.Request) {
|
||||||
|
glog.V(LINFO).Infof("[http2] %s - %s", req.RemoteAddr, req.Host)
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
dump, _ := httputil.DumpRequest(req, false)
|
||||||
|
glog.Infoln(string(dump))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func basicAuth(authInfo string) (username, password string, ok bool) {
|
||||||
|
if authInfo == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(authInfo, "Basic ") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authInfo, "Basic "))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cs := string(c)
|
||||||
|
s := strings.IndexByte(cs, ':')
|
||||||
|
if s < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return cs[:s], cs[s+1:], true
|
||||||
|
}
|
||||||
|
|
||||||
|
type flushWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fw flushWriter) Write(p []byte) (n int, err error) {
|
||||||
|
n, err = fw.w.Write(p)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("flush writer:", err)
|
||||||
|
}
|
||||||
|
if f, ok := fw.w.(http.Flusher); ok {
|
||||||
|
f.Flush()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
553
gost/socks.go
Normal file
553
gost/socks.go
Normal file
@ -0,0 +1,553 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
//"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"github.com/ginuerzh/gosocks5"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
//"os/exec"
|
||||||
|
//"io"
|
||||||
|
//"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MethodTLS uint8 = 0x80 // extended method for tls
|
||||||
|
MethodTLSAuth uint8 = 0x82 // extended method for tls+auth
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CmdUdpConnect uint8 = 0xF1 // extended method for udp local port forwarding
|
||||||
|
CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp
|
||||||
|
)
|
||||||
|
|
||||||
|
type clientSelector struct {
|
||||||
|
methods []uint8
|
||||||
|
user *url.Userinfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (selector *clientSelector) Methods() []uint8 {
|
||||||
|
return selector.methods
|
||||||
|
}
|
||||||
|
|
||||||
|
func (selector *clientSelector) Select(methods ...uint8) (method uint8) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
||||||
|
switch method {
|
||||||
|
case MethodTLS:
|
||||||
|
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
||||||
|
|
||||||
|
case gosocks5.MethodUserPass, MethodTLSAuth:
|
||||||
|
if method == MethodTLSAuth {
|
||||||
|
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
var username, password string
|
||||||
|
if selector.user != nil {
|
||||||
|
username = selector.user.Username()
|
||||||
|
password, _ = selector.user.Password()
|
||||||
|
}
|
||||||
|
|
||||||
|
req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password)
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("socks5 auth:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infoln(req)
|
||||||
|
|
||||||
|
resp, err := gosocks5.ReadUserPassResponse(conn)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("socks5 auth:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infoln(resp)
|
||||||
|
|
||||||
|
if resp.Status != gosocks5.Succeeded {
|
||||||
|
return nil, gosocks5.ErrAuthFailure
|
||||||
|
}
|
||||||
|
case gosocks5.MethodNoAcceptable:
|
||||||
|
return nil, gosocks5.ErrBadMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverSelector struct {
|
||||||
|
methods []uint8
|
||||||
|
user *url.Userinfo
|
||||||
|
cert tls.Certificate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (selector *serverSelector) Methods() []uint8 {
|
||||||
|
return selector.methods
|
||||||
|
}
|
||||||
|
|
||||||
|
func (selector *serverSelector) Select(methods ...uint8) (method uint8) {
|
||||||
|
glog.V(LDEBUG).Infof("%d %d %v", gosocks5.Ver5, len(methods), methods)
|
||||||
|
|
||||||
|
method = gosocks5.MethodNoAuth
|
||||||
|
for _, m := range methods {
|
||||||
|
if m == MethodTLS {
|
||||||
|
method = m
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// when user/pass is set, auth is mandatory
|
||||||
|
if selector.user != nil {
|
||||||
|
if method == gosocks5.MethodNoAuth {
|
||||||
|
method = gosocks5.MethodUserPass
|
||||||
|
}
|
||||||
|
if method == MethodTLS {
|
||||||
|
method = MethodTLSAuth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
||||||
|
glog.V(LDEBUG).Infof("%d %d", gosocks5.Ver5, method)
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case MethodTLS:
|
||||||
|
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.cert}})
|
||||||
|
|
||||||
|
case gosocks5.MethodUserPass, MethodTLSAuth:
|
||||||
|
if method == MethodTLSAuth {
|
||||||
|
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.cert}})
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := gosocks5.ReadUserPassRequest(conn)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[socks5-auth]", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infoln("[socks5]", req.String())
|
||||||
|
|
||||||
|
var username, password string
|
||||||
|
if selector.user != nil {
|
||||||
|
username = selector.user.Username()
|
||||||
|
password, _ = selector.user.Password()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (username != "" && req.Username != username) || (password != "" && req.Password != password) {
|
||||||
|
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)
|
||||||
|
if err := resp.Write(conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[socks5-auth]", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infoln("[socks5]", resp)
|
||||||
|
glog.V(LWARNING).Infoln("[socks5-auth] proxy authentication required")
|
||||||
|
|
||||||
|
return nil, gosocks5.ErrAuthFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded)
|
||||||
|
if err := resp.Write(conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[socks5-auth]", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infoln(resp)
|
||||||
|
|
||||||
|
case gosocks5.MethodNoAcceptable:
|
||||||
|
return nil, gosocks5.ErrBadMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSocks5Request(req *gosocks5.Request, conn net.Conn) {
|
||||||
|
glog.V(LDEBUG).Infof("[socks5] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, req)
|
||||||
|
|
||||||
|
switch req.Cmd {
|
||||||
|
case gosocks5.CmdConnect:
|
||||||
|
glog.V(LINFO).Infof("[socks5-connect] %s - %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
|
||||||
|
tconn, err := connect(req.Addr.String(), "socks5", forwardArgs...)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)
|
||||||
|
if err := rep.Write(conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
} else {
|
||||||
|
glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tconn.Close()
|
||||||
|
|
||||||
|
rep := gosocks5.NewReply(gosocks5.Succeeded, nil)
|
||||||
|
if err := rep.Write(conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[socks5-connect] %s <-> %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
Transport(conn, tconn)
|
||||||
|
glog.V(LINFO).Infof("[socks5-connect] %s >-< %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
|
||||||
|
case gosocks5.CmdBind:
|
||||||
|
glog.V(LINFO).Infof("[socks5-bind] %s - %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
|
||||||
|
reply, fconn, err := socks5Bind(req, conn)
|
||||||
|
if reply != nil {
|
||||||
|
if err := reply.Write(conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
if fconn != nil {
|
||||||
|
fconn.Close()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-bind] %s - %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fconn.Close()
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[socks5-bind] %s <-> %s", conn.RemoteAddr(), fconn.RemoteAddr())
|
||||||
|
Transport(conn, fconn)
|
||||||
|
glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", conn.RemoteAddr(), fconn.RemoteAddr())
|
||||||
|
|
||||||
|
case CmdUdpConnect:
|
||||||
|
glog.V(LINFO).Infof("[udp] %s - %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
udpConnect(req, conn)
|
||||||
|
|
||||||
|
case gosocks5.CmdUdp:
|
||||||
|
glog.V(LINFO).Infof("[socks5-udp] %s - %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
socks5UDP(req, conn)
|
||||||
|
|
||||||
|
case CmdUdpTun:
|
||||||
|
glog.V(LINFO).Infof("[socks5-udp] %s - %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
if err := socks5TunnelUDP(req, conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-udp] %s - %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
rep := gosocks5.NewReply(gosocks5.Failure, nil)
|
||||||
|
if err := rep.Write(conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
} else {
|
||||||
|
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
glog.V(LWARNING).Infoln("[socks5] Unrecognized request:", req.Cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func udpConnect(req *gosocks5.Request, conn net.Conn) error {
|
||||||
|
if len(forwardArgs) > 0 { // direct forwarding
|
||||||
|
fconn, _, err := forwardChain(forwardArgs...)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LINFO).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fconn.Close()
|
||||||
|
|
||||||
|
if err := req.Write(fconn); err != nil {
|
||||||
|
glog.V(LINFO).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[udp] %s <-> %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
err = Transport(conn, fconn)
|
||||||
|
glog.V(LINFO).Infof("[udp] %s >-< %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
raddr, err := net.ResolveUDPAddr("udp", req.Addr.String())
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LINFO).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gosocks5.NewReply(gosocks5.Succeeded, nil).Write(conn); err != nil {
|
||||||
|
glog.V(LINFO).Infof("[udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[udp] %s <-> %s", conn.RemoteAddr(), raddr)
|
||||||
|
defer glog.V(LINFO).Infof("[udp] %s >-< %s", conn.RemoteAddr(), raddr)
|
||||||
|
|
||||||
|
for {
|
||||||
|
dgram, err := gosocks5.ReadUDPDatagram(conn)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
b := udpPool.Get().([]byte)
|
||||||
|
defer udpPool.Put(b)
|
||||||
|
|
||||||
|
relay, err := net.DialUDP("udp", nil, raddr)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), raddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer relay.Close()
|
||||||
|
|
||||||
|
if _, err := relay.Write(dgram.Data); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), raddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", conn.RemoteAddr(), raddr, len(dgram.Data))
|
||||||
|
|
||||||
|
relay.SetReadDeadline(time.Now().Add(time.Second * 60))
|
||||||
|
n, err := relay.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", conn.RemoteAddr(), raddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
relay.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
|
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", conn.RemoteAddr(), raddr, n)
|
||||||
|
|
||||||
|
conn.SetWriteDeadline(time.Now().Add(time.Second * 90))
|
||||||
|
if err := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, dgram.Header.Addr), b[:n]).Write(conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", conn.RemoteAddr(), raddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn.SetWriteDeadline(time.Time{})
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func socks5UDP(req *gosocks5.Request, conn net.Conn) error {
|
||||||
|
bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String())
|
||||||
|
relay, err := net.ListenUDP("udp", bindAddr) // udp associate, strict mode: if the port already in use, it will return error
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
|
||||||
|
rep := gosocks5.NewReply(gosocks5.Failure, nil)
|
||||||
|
if err := rep.Write(conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
} else {
|
||||||
|
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer relay.Close()
|
||||||
|
|
||||||
|
addr := ToSocksAddr(relay.LocalAddr())
|
||||||
|
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
||||||
|
rep := gosocks5.NewReply(gosocks5.Succeeded, addr)
|
||||||
|
if err := rep.Write(conn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[socks5-udp] %s - %s BIND ON %s OK", conn.RemoteAddr(), req.Addr, addr)
|
||||||
|
|
||||||
|
if len(forwardArgs) > 0 { // client -> tunnel, tunnel udp over tcp
|
||||||
|
tun, _, err := forwardChain(forwardArgs...)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer tun.Close()
|
||||||
|
|
||||||
|
tun.SetWriteDeadline(time.Now().Add(time.Second * 90))
|
||||||
|
if err := gosocks5.NewRequest(CmdUdpTun, nil).Write(tun); err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tun.SetWriteDeadline(time.Time{})
|
||||||
|
|
||||||
|
tun.SetReadDeadline(time.Now().Add(time.Second * 90))
|
||||||
|
rep, err := gosocks5.ReadReply(tun)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rep.Rep != gosocks5.Succeeded {
|
||||||
|
return errors.New("udp associate error")
|
||||||
|
}
|
||||||
|
tun.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
go tunnelUDP(relay, tun, true)
|
||||||
|
} else { // standard socks5 udp relay
|
||||||
|
peer, err := net.ListenUDP("udp", nil)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer peer.Close()
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
go transportUDP(relay, peer)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := tcpPool.Get().([]byte)
|
||||||
|
defer tcpPool.Put(b)
|
||||||
|
for {
|
||||||
|
_, err := conn.Read(b) // discard any data from tcp connection
|
||||||
|
if err != nil {
|
||||||
|
break // client disconnected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", conn.RemoteAddr(), req.Addr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func socks5TunnelUDP(req *gosocks5.Request, conn net.Conn) error {
|
||||||
|
if len(forwardArgs) > 0 { // tunnel -> tunnel, direct forwarding
|
||||||
|
tun, _, err := forwardChain(forwardArgs...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer tun.Close()
|
||||||
|
|
||||||
|
if err := req.Write(tun); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s[tun]", conn.RemoteAddr(), tun.RemoteAddr())
|
||||||
|
Transport(conn, tun)
|
||||||
|
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s[tun]", conn.RemoteAddr(), tun.RemoteAddr())
|
||||||
|
} else { // tunnel -> remote, handle tunnel udp request
|
||||||
|
bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String())
|
||||||
|
uconn, err := net.ListenUDP("udp", bindAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer uconn.Close()
|
||||||
|
|
||||||
|
addr := ToSocksAddr(uconn.LocalAddr())
|
||||||
|
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
||||||
|
rep := gosocks5.NewReply(gosocks5.Succeeded, addr)
|
||||||
|
if err := rep.Write(conn); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), uconn.LocalAddr(), rep)
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", conn.RemoteAddr(), uconn.LocalAddr())
|
||||||
|
tunnelUDP(uconn, conn, false)
|
||||||
|
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", conn.RemoteAddr(), uconn.LocalAddr())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func socks5Bind(req *gosocks5.Request, conn net.Conn) (*gosocks5.Reply, net.Conn, error) {
|
||||||
|
if len(forwardArgs) > 0 {
|
||||||
|
fconn, _, err := forwardChain(forwardArgs...)
|
||||||
|
if err != nil {
|
||||||
|
return gosocks5.NewReply(gosocks5.Failure, nil), nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := req.Write(fconn); err != nil {
|
||||||
|
fconn.Close()
|
||||||
|
return gosocks5.NewReply(gosocks5.Failure, nil), nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fconn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bindAddr, _ := net.ResolveTCPAddr("tcp", req.Addr.String())
|
||||||
|
ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error
|
||||||
|
if err != nil {
|
||||||
|
return gosocks5.NewReply(gosocks5.Failure, nil), nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := ToSocksAddr(ln.Addr())
|
||||||
|
// Issue: may not reachable when host has multi-interface
|
||||||
|
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
||||||
|
rep := gosocks5.NewReply(gosocks5.Succeeded, addr)
|
||||||
|
if err := rep.Write(conn); err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
||||||
|
ln.Close()
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
||||||
|
glog.V(LINFO).Infof("[socks5-bind] %s - %s BIND ON %s OK", conn.RemoteAddr(), req.Addr, addr)
|
||||||
|
|
||||||
|
lnChan := make(chan net.Conn, 1)
|
||||||
|
go func() {
|
||||||
|
defer close(lnChan)
|
||||||
|
c, err := ln.AcceptTCP()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lnChan <- c
|
||||||
|
}()
|
||||||
|
|
||||||
|
peerChan := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
defer close(peerChan)
|
||||||
|
b := tcpPool.Get().([]byte)
|
||||||
|
defer tcpPool.Put(b)
|
||||||
|
_, err := conn.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
peerChan <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var pconn net.Conn
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case c := <-lnChan:
|
||||||
|
ln.Close() // only accept one peer
|
||||||
|
if c == nil {
|
||||||
|
return gosocks5.NewReply(gosocks5.Failure, nil), nil, errors.New("[socks5-bind] accept error")
|
||||||
|
}
|
||||||
|
pconn = c
|
||||||
|
lnChan = nil
|
||||||
|
ln = nil
|
||||||
|
conn.SetReadDeadline(time.Now()) // timeout right now ,so we can break out of blocking
|
||||||
|
case err := <-peerChan:
|
||||||
|
if err != nil || pconn == nil {
|
||||||
|
if ln != nil {
|
||||||
|
ln.Close()
|
||||||
|
}
|
||||||
|
if pconn != nil {
|
||||||
|
pconn.Close()
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = errors.New("Oops, some mysterious error!")
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
goto out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
conn.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
|
glog.V(LINFO).Infof("[socks5-bind] %s <- %s PEER %s ACCEPTED", conn.RemoteAddr(), addr, pconn.RemoteAddr())
|
||||||
|
rep = gosocks5.NewReply(gosocks5.Succeeded, ToSocksAddr(pconn.RemoteAddr()))
|
||||||
|
return rep, pconn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
|
||||||
|
host := "0.0.0.0"
|
||||||
|
port := 0
|
||||||
|
if addr != nil {
|
||||||
|
h, p, _ := net.SplitHostPort(addr.String())
|
||||||
|
host = h
|
||||||
|
port, _ = strconv.Atoi(p)
|
||||||
|
}
|
||||||
|
return &gosocks5.Addr{
|
||||||
|
Type: gosocks5.AddrIPv4,
|
||||||
|
Host: host,
|
||||||
|
Port: uint16(port),
|
||||||
|
}
|
||||||
|
}
|
141
gost/ws.go
Normal file
141
gost/ws.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
//"github.com/ginuerzh/gosocks5"
|
||||||
|
"crypto/tls"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wsConn struct {
|
||||||
|
conn *websocket.Conn
|
||||||
|
rb []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsClient(scheme string, conn net.Conn, host string) (*wsConn, error) {
|
||||||
|
dialer := websocket.Dialer{
|
||||||
|
ReadBufferSize: 4096,
|
||||||
|
WriteBufferSize: 4096,
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
HandshakeTimeout: time.Second * 90,
|
||||||
|
NetDial: func(net, addr string) (net.Conn, error) {
|
||||||
|
return conn, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
u := url.URL{Scheme: scheme, Host: host, Path: "/ws"}
|
||||||
|
c, resp, err := dialer.Dial(u.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
return &wsConn{conn: c}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsServer(conn *websocket.Conn) *wsConn {
|
||||||
|
return &wsConn{
|
||||||
|
conn: conn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wsConn) Read(b []byte) (n int, err error) {
|
||||||
|
if len(c.rb) == 0 {
|
||||||
|
_, c.rb, err = c.conn.ReadMessage()
|
||||||
|
}
|
||||||
|
n = copy(b, c.rb)
|
||||||
|
c.rb = c.rb[n:]
|
||||||
|
|
||||||
|
//log.Println("ws r:", n)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wsConn) Write(b []byte) (n int, err error) {
|
||||||
|
err = c.conn.WriteMessage(websocket.BinaryMessage, b)
|
||||||
|
n = len(b)
|
||||||
|
//log.Println("ws w:", n)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wsConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wsConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wsConn) RemoteAddr() net.Addr {
|
||||||
|
return c.conn.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *wsConn) SetDeadline(t time.Time) error {
|
||||||
|
if err := conn.SetReadDeadline(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
func (c *wsConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wsConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ws struct {
|
||||||
|
upgrader websocket.Upgrader
|
||||||
|
arg Args
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWs(arg Args) *ws {
|
||||||
|
return &ws{
|
||||||
|
arg: arg,
|
||||||
|
upgrader: websocket.Upgrader{
|
||||||
|
ReadBufferSize: 1024,
|
||||||
|
WriteBufferSize: 1024,
|
||||||
|
CheckOrigin: func(r *http.Request) bool { return true },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ws) handle(w http.ResponseWriter, r *http.Request) {
|
||||||
|
glog.V(LINFO).Infof("[ws] %s - %s", r.RemoteAddr, s.arg.Addr)
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
dump, err := httputil.DumpRequest(r, false)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infof("[ws] %s - %s : %s", r.RemoteAddr, s.arg.Addr, err)
|
||||||
|
} else {
|
||||||
|
glog.V(LDEBUG).Infof("[ws] %s - %s\n%s", r.RemoteAddr, s.arg.Addr, string(dump))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn, err := s.upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LERROR).Infoln(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handleConn(wsServer(conn), s.arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ws) ListenAndServe() error {
|
||||||
|
sm := http.NewServeMux()
|
||||||
|
sm.HandleFunc("/ws", s.handle)
|
||||||
|
return http.ListenAndServe(s.arg.Addr, sm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ws) listenAndServeTLS() error {
|
||||||
|
sm := http.NewServeMux()
|
||||||
|
sm.HandleFunc("/ws", s.handle)
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: s.arg.Addr,
|
||||||
|
TLSConfig: &tls.Config{Certificates: []tls.Certificate{s.arg.Cert}},
|
||||||
|
Handler: sm,
|
||||||
|
}
|
||||||
|
return server.ListenAndServeTLS("", "")
|
||||||
|
}
|
272
http.go
272
http.go
@ -1,122 +1,20 @@
|
|||||||
package main
|
package gost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
//"bufio"
|
||||||
"crypto/tls"
|
//"crypto/tls"
|
||||||
"encoding/base64"
|
//"encoding/base64"
|
||||||
"github.com/golang/glog"
|
//"github.com/golang/glog"
|
||||||
"golang.org/x/net/http2"
|
//"golang.org/x/net/http2"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
//"net/http"
|
||||||
"net/http/httputil"
|
//"net/http/httputil"
|
||||||
"strings"
|
//"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// http2 client connection, wrapped up just like a net.Conn
|
||||||
http2Client *http.Client
|
|
||||||
)
|
|
||||||
|
|
||||||
func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) {
|
|
||||||
glog.V(LINFO).Infof("[http] %s %s - %s %s", req.Method, conn.RemoteAddr(), req.Host, req.Proto)
|
|
||||||
|
|
||||||
if glog.V(LDEBUG) {
|
|
||||||
dump, _ := httputil.DumpRequest(req, false)
|
|
||||||
glog.Infoln(string(dump))
|
|
||||||
}
|
|
||||||
|
|
||||||
var username, password string
|
|
||||||
if arg.User != nil {
|
|
||||||
username = arg.User.Username()
|
|
||||||
password, _ = arg.User.Password()
|
|
||||||
}
|
|
||||||
|
|
||||||
u, p, _ := basicAuth(req.Header.Get("Proxy-Authorization"))
|
|
||||||
req.Header.Del("Proxy-Authorization")
|
|
||||||
if (username != "" && u != username) || (password != "" && p != password) {
|
|
||||||
resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
|
||||||
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
|
|
||||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
|
|
||||||
|
|
||||||
if _, err := conn.Write([]byte(resp)); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[http] %s <- %s : %s", conn.RemoteAddr(), req.Host, err)
|
|
||||||
}
|
|
||||||
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, resp)
|
|
||||||
|
|
||||||
glog.V(LWARNING).Infof("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(forwardArgs) > 0 {
|
|
||||||
last := forwardArgs[len(forwardArgs)-1]
|
|
||||||
if last.Protocol == "http" || last.Protocol == "" {
|
|
||||||
forwardHttpRequest(req, conn, arg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := connect(req.Host, "http", forwardArgs...)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
|
||||||
|
|
||||||
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
|
||||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
|
||||||
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
|
|
||||||
conn.Write(b)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
if req.Method == http.MethodConnect {
|
|
||||||
b := []byte("HTTP/1.1 200 Connection established\r\n" +
|
|
||||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
|
||||||
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
|
|
||||||
conn.Write(b)
|
|
||||||
} else {
|
|
||||||
req.Header.Del("Proxy-Connection")
|
|
||||||
req.Header.Set("Connection", "Keep-Alive")
|
|
||||||
|
|
||||||
if err = req.Write(c); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
|
|
||||||
Transport(conn, c)
|
|
||||||
glog.V(LINFO).Infof("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
|
|
||||||
}
|
|
||||||
|
|
||||||
func forwardHttpRequest(req *http.Request, conn net.Conn, arg Args) {
|
|
||||||
last := forwardArgs[len(forwardArgs)-1]
|
|
||||||
c, _, err := forwardChain(forwardArgs...)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), last.Addr, err)
|
|
||||||
|
|
||||||
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
|
||||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
|
||||||
glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), last.Addr, string(b))
|
|
||||||
conn.Write(b)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
if last.User != nil {
|
|
||||||
req.Header.Set("Proxy-Authorization",
|
|
||||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(last.User.String())))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = req.Write(c); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
glog.V(LINFO).Infof("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
|
|
||||||
Transport(conn, c)
|
|
||||||
glog.V(LINFO).Infof("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type Http2ClientConn struct {
|
type Http2ClientConn struct {
|
||||||
r io.Reader
|
r io.Reader
|
||||||
w io.Writer
|
w io.Writer
|
||||||
@ -158,153 +56,3 @@ func (c *Http2ClientConn) SetReadDeadline(t time.Time) error {
|
|||||||
func (c *Http2ClientConn) SetWriteDeadline(t time.Time) error {
|
func (c *Http2ClientConn) SetWriteDeadline(t time.Time) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// init http2 client with target http2 proxy server addr, and forward chain chain
|
|
||||||
func initHttp2Client(host string, chain ...Args) {
|
|
||||||
tr := http2.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
},
|
|
||||||
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
|
||||||
// replace the default dialer with our forward chain.
|
|
||||||
conn, err := connect(host, "http2", chain...)
|
|
||||||
if err != nil {
|
|
||||||
return conn, err
|
|
||||||
}
|
|
||||||
return tls.Client(conn, cfg), nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
http2Client = &http.Client{Transport: &tr}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerHttp2Request(w http.ResponseWriter, req *http.Request) {
|
|
||||||
target := req.Header.Get("gost-target")
|
|
||||||
if target == "" {
|
|
||||||
target = req.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[http2] %s %s - %s %s", req.Method, req.RemoteAddr, target, req.Proto)
|
|
||||||
if glog.V(LDEBUG) {
|
|
||||||
dump, _ := httputil.DumpRequest(req, false)
|
|
||||||
glog.Infoln(string(dump))
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := connect(target, req.Header.Get("gost-protocol"), forwardArgs...)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
|
||||||
w.Header().Set("Proxy-Agent", "gost/"+Version)
|
|
||||||
w.WriteHeader(http.StatusServiceUnavailable)
|
|
||||||
if fw, ok := w.(http.Flusher); ok {
|
|
||||||
fw.Flush()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, target)
|
|
||||||
errc := make(chan error, 2)
|
|
||||||
|
|
||||||
if req.Method == http.MethodConnect {
|
|
||||||
w.Header().Set("Proxy-Agent", "gost/"+Version)
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
if fw, ok := w.(http.Flusher); ok {
|
|
||||||
fw.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// compatible with HTTP 1.x
|
|
||||||
if hj, ok := w.(http.Hijacker); ok && req.ProtoMajor == 1 {
|
|
||||||
// we take over the underly connection
|
|
||||||
conn, _, err := hj.Hijack()
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
go Pipe(conn, c, errc)
|
|
||||||
go Pipe(c, conn, errc)
|
|
||||||
} else {
|
|
||||||
go Pipe(req.Body, c, errc)
|
|
||||||
go Pipe(c, flushWriter{w}, errc)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-errc:
|
|
||||||
// glog.V(LWARNING).Infoln("exit", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
req.Header.Set("Connection", "Keep-Alive")
|
|
||||||
if err = req.Write(c); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.ReadResponse(bufio.NewReader(c), req)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
for k, v := range resp.Header {
|
|
||||||
for _, vv := range v {
|
|
||||||
w.Header().Add(k, vv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.WriteHeader(resp.StatusCode)
|
|
||||||
if fw, ok := w.(http.Flusher); ok {
|
|
||||||
fw.Flush()
|
|
||||||
}
|
|
||||||
if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[http2] %s <- %s : %s", req.RemoteAddr, target, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target)
|
|
||||||
}
|
|
||||||
|
|
||||||
//func processSocks5OverHttp2()
|
|
||||||
|
|
||||||
func handleHttp2Transport(w http.ResponseWriter, req *http.Request) {
|
|
||||||
glog.V(LINFO).Infof("[http2] %s - %s", req.RemoteAddr, req.Host)
|
|
||||||
if glog.V(LDEBUG) {
|
|
||||||
dump, _ := httputil.DumpRequest(req, false)
|
|
||||||
glog.Infoln(string(dump))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func basicAuth(authInfo string) (username, password string, ok bool) {
|
|
||||||
if authInfo == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(authInfo, "Basic ") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authInfo, "Basic "))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cs := string(c)
|
|
||||||
s := strings.IndexByte(cs, ':')
|
|
||||||
if s < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return cs[:s], cs[s+1:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
type flushWriter struct {
|
|
||||||
w io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fw flushWriter) Write(p []byte) (n int, err error) {
|
|
||||||
n, err = fw.w.Write(p)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("flush writer:", err)
|
|
||||||
}
|
|
||||||
if f, ok := fw.w.(http.Flusher); ok {
|
|
||||||
f.Flush()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
100
node.go
Normal file
100
node.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package gost
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Proxy node represent a proxy
|
||||||
|
type ProxyNode struct {
|
||||||
|
Addr string // host:port
|
||||||
|
Protocol string // protocol: http/http2/socks5/ss
|
||||||
|
Transport string // transport: ws/wss/tls/tcp/udp/rtcp/rudp
|
||||||
|
Remote string // remote address, used by tcp/udp port forwarding
|
||||||
|
User *url.Userinfo // authentication for proxy
|
||||||
|
values url.Values
|
||||||
|
serverName string
|
||||||
|
conn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// the format is [scheme://][user:pass@host]:port
|
||||||
|
func ParseProxyNode(s string) (node *ProxyNode, err error) {
|
||||||
|
if !strings.Contains(s, "://") {
|
||||||
|
s = "gost://" + s
|
||||||
|
}
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node = &ProxyNode{
|
||||||
|
Addr: u.Host,
|
||||||
|
User: u.User,
|
||||||
|
values: u.Query(),
|
||||||
|
serverName: u.Host,
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(u.Host, ":") {
|
||||||
|
node.serverName, _, _ = net.SplitHostPort(u.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
schemes := strings.Split(u.Scheme, "+")
|
||||||
|
if len(schemes) == 1 {
|
||||||
|
node.Protocol = schemes[0]
|
||||||
|
node.Transport = schemes[0]
|
||||||
|
}
|
||||||
|
if len(schemes) == 2 {
|
||||||
|
node.Protocol = schemes[0]
|
||||||
|
node.Transport = schemes[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node.Transport {
|
||||||
|
case "ws", "wss", "tls":
|
||||||
|
case "https":
|
||||||
|
node.Protocol = "http"
|
||||||
|
node.Transport = "tls"
|
||||||
|
case "http2":
|
||||||
|
node.Protocol = "http2"
|
||||||
|
case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
|
||||||
|
node.Remote = strings.Trim(u.EscapedPath(), "/")
|
||||||
|
case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding
|
||||||
|
node.Remote = strings.Trim(u.EscapedPath(), "/")
|
||||||
|
default:
|
||||||
|
node.Transport = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node.Protocol {
|
||||||
|
case "http", "http2", "socks", "socks5", "ss":
|
||||||
|
default:
|
||||||
|
node.Protocol = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *ProxyNode) insecureSkipVerify() bool {
|
||||||
|
s := node.values.Get("secure")
|
||||||
|
if secure, _ := strconv.ParseBool(s); secure {
|
||||||
|
return !secure
|
||||||
|
}
|
||||||
|
if n, _ := strconv.Atoi(s); n > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *ProxyNode) certFile() string {
|
||||||
|
if cert := node.values.Get("cert"); cert != "" {
|
||||||
|
return cert
|
||||||
|
}
|
||||||
|
return DefaultCertFile
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *ProxyNode) keyFile() string {
|
||||||
|
if key := node.values.Get("key"); key != "" {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
return DefaultKeyFile
|
||||||
|
}
|
397
socks.go
397
socks.go
@ -1,9 +1,9 @@
|
|||||||
package main
|
package gost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"bytes"
|
//"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
//"errors"
|
||||||
"github.com/ginuerzh/gosocks5"
|
"github.com/ginuerzh/gosocks5"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
//"os/exec"
|
//"os/exec"
|
||||||
@ -11,8 +11,8 @@ import (
|
|||||||
//"io/ioutil"
|
//"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
//"strconv"
|
||||||
"time"
|
//"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -162,392 +162,3 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con
|
|||||||
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSocks5Request(req *gosocks5.Request, conn net.Conn) {
|
|
||||||
glog.V(LDEBUG).Infof("[socks5] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, req)
|
|
||||||
|
|
||||||
switch req.Cmd {
|
|
||||||
case gosocks5.CmdConnect:
|
|
||||||
glog.V(LINFO).Infof("[socks5-connect] %s - %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
|
|
||||||
tconn, err := connect(req.Addr.String(), "socks5", forwardArgs...)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)
|
|
||||||
if err := rep.Write(conn); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
} else {
|
|
||||||
glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer tconn.Close()
|
|
||||||
|
|
||||||
rep := gosocks5.NewReply(gosocks5.Succeeded, nil)
|
|
||||||
if err := rep.Write(conn); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[socks5-connect] %s <-> %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
Transport(conn, tconn)
|
|
||||||
glog.V(LINFO).Infof("[socks5-connect] %s >-< %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
|
|
||||||
case gosocks5.CmdBind:
|
|
||||||
glog.V(LINFO).Infof("[socks5-bind] %s - %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
|
|
||||||
reply, fconn, err := socks5Bind(req, conn)
|
|
||||||
if reply != nil {
|
|
||||||
if err := reply.Write(conn); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
if fconn != nil {
|
|
||||||
fconn.Close()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-bind] %s - %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer fconn.Close()
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[socks5-bind] %s <-> %s", conn.RemoteAddr(), fconn.RemoteAddr())
|
|
||||||
Transport(conn, fconn)
|
|
||||||
glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", conn.RemoteAddr(), fconn.RemoteAddr())
|
|
||||||
|
|
||||||
case CmdUdpConnect:
|
|
||||||
glog.V(LINFO).Infof("[udp] %s - %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
udpConnect(req, conn)
|
|
||||||
|
|
||||||
case gosocks5.CmdUdp:
|
|
||||||
glog.V(LINFO).Infof("[socks5-udp] %s - %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
socks5UDP(req, conn)
|
|
||||||
|
|
||||||
case CmdUdpTun:
|
|
||||||
glog.V(LINFO).Infof("[socks5-udp] %s - %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
if err := socks5TunnelUDP(req, conn); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-udp] %s - %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
rep := gosocks5.NewReply(gosocks5.Failure, nil)
|
|
||||||
if err := rep.Write(conn); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
} else {
|
|
||||||
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
glog.V(LWARNING).Infoln("[socks5] Unrecognized request:", req.Cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func udpConnect(req *gosocks5.Request, conn net.Conn) error {
|
|
||||||
if len(forwardArgs) > 0 { // direct forwarding
|
|
||||||
fconn, _, err := forwardChain(forwardArgs...)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LINFO).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer fconn.Close()
|
|
||||||
|
|
||||||
if err := req.Write(fconn); err != nil {
|
|
||||||
glog.V(LINFO).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[udp] %s <-> %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
err = Transport(conn, fconn)
|
|
||||||
glog.V(LINFO).Infof("[udp] %s >-< %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
raddr, err := net.ResolveUDPAddr("udp", req.Addr.String())
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LINFO).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gosocks5.NewReply(gosocks5.Succeeded, nil).Write(conn); err != nil {
|
|
||||||
glog.V(LINFO).Infof("[udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[udp] %s <-> %s", conn.RemoteAddr(), raddr)
|
|
||||||
defer glog.V(LINFO).Infof("[udp] %s >-< %s", conn.RemoteAddr(), raddr)
|
|
||||||
|
|
||||||
for {
|
|
||||||
dgram, err := gosocks5.ReadUDPDatagram(conn)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
b := udpPool.Get().([]byte)
|
|
||||||
defer udpPool.Put(b)
|
|
||||||
|
|
||||||
relay, err := net.DialUDP("udp", nil, raddr)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), raddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer relay.Close()
|
|
||||||
|
|
||||||
if _, err := relay.Write(dgram.Data); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), raddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", conn.RemoteAddr(), raddr, len(dgram.Data))
|
|
||||||
|
|
||||||
relay.SetReadDeadline(time.Now().Add(time.Second * 60))
|
|
||||||
n, err := relay.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", conn.RemoteAddr(), raddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
relay.SetReadDeadline(time.Time{})
|
|
||||||
|
|
||||||
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", conn.RemoteAddr(), raddr, n)
|
|
||||||
|
|
||||||
conn.SetWriteDeadline(time.Now().Add(time.Second * 90))
|
|
||||||
if err := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, dgram.Header.Addr), b[:n]).Write(conn); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", conn.RemoteAddr(), raddr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
conn.SetWriteDeadline(time.Time{})
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func socks5UDP(req *gosocks5.Request, conn net.Conn) error {
|
|
||||||
bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String())
|
|
||||||
relay, err := net.ListenUDP("udp", bindAddr) // udp associate, strict mode: if the port already in use, it will return error
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
|
|
||||||
rep := gosocks5.NewReply(gosocks5.Failure, nil)
|
|
||||||
if err := rep.Write(conn); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
} else {
|
|
||||||
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer relay.Close()
|
|
||||||
|
|
||||||
addr := ToSocksAddr(relay.LocalAddr())
|
|
||||||
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
|
||||||
rep := gosocks5.NewReply(gosocks5.Succeeded, addr)
|
|
||||||
if err := rep.Write(conn); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[socks5-udp] %s - %s BIND ON %s OK", conn.RemoteAddr(), req.Addr, addr)
|
|
||||||
|
|
||||||
if len(forwardArgs) > 0 { // client -> tunnel, tunnel udp over tcp
|
|
||||||
tun, _, err := forwardChain(forwardArgs...)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer tun.Close()
|
|
||||||
|
|
||||||
tun.SetWriteDeadline(time.Now().Add(time.Second * 90))
|
|
||||||
if err := gosocks5.NewRequest(CmdUdpTun, nil).Write(tun); err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tun.SetWriteDeadline(time.Time{})
|
|
||||||
|
|
||||||
tun.SetReadDeadline(time.Now().Add(time.Second * 90))
|
|
||||||
rep, err := gosocks5.ReadReply(tun)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rep.Rep != gosocks5.Succeeded {
|
|
||||||
return errors.New("udp associate error")
|
|
||||||
}
|
|
||||||
tun.SetReadDeadline(time.Time{})
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
go tunnelUDP(relay, tun, true)
|
|
||||||
} else { // standard socks5 udp relay
|
|
||||||
peer, err := net.ListenUDP("udp", nil)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer peer.Close()
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
go transportUDP(relay, peer)
|
|
||||||
}
|
|
||||||
|
|
||||||
b := tcpPool.Get().([]byte)
|
|
||||||
defer tcpPool.Put(b)
|
|
||||||
for {
|
|
||||||
_, err := conn.Read(b) // discard any data from tcp connection
|
|
||||||
if err != nil {
|
|
||||||
break // client disconnected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", conn.RemoteAddr(), req.Addr)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func socks5TunnelUDP(req *gosocks5.Request, conn net.Conn) error {
|
|
||||||
if len(forwardArgs) > 0 { // tunnel -> tunnel, direct forwarding
|
|
||||||
tun, _, err := forwardChain(forwardArgs...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer tun.Close()
|
|
||||||
|
|
||||||
if err := req.Write(tun); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s[tun]", conn.RemoteAddr(), tun.RemoteAddr())
|
|
||||||
Transport(conn, tun)
|
|
||||||
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s[tun]", conn.RemoteAddr(), tun.RemoteAddr())
|
|
||||||
} else { // tunnel -> remote, handle tunnel udp request
|
|
||||||
bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String())
|
|
||||||
uconn, err := net.ListenUDP("udp", bindAddr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer uconn.Close()
|
|
||||||
|
|
||||||
addr := ToSocksAddr(uconn.LocalAddr())
|
|
||||||
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
|
||||||
rep := gosocks5.NewReply(gosocks5.Succeeded, addr)
|
|
||||||
if err := rep.Write(conn); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), uconn.LocalAddr(), rep)
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", conn.RemoteAddr(), uconn.LocalAddr())
|
|
||||||
tunnelUDP(uconn, conn, false)
|
|
||||||
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", conn.RemoteAddr(), uconn.LocalAddr())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func socks5Bind(req *gosocks5.Request, conn net.Conn) (*gosocks5.Reply, net.Conn, error) {
|
|
||||||
if len(forwardArgs) > 0 {
|
|
||||||
fconn, _, err := forwardChain(forwardArgs...)
|
|
||||||
if err != nil {
|
|
||||||
return gosocks5.NewReply(gosocks5.Failure, nil), nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := req.Write(fconn); err != nil {
|
|
||||||
fconn.Close()
|
|
||||||
return gosocks5.NewReply(gosocks5.Failure, nil), nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fconn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
bindAddr, _ := net.ResolveTCPAddr("tcp", req.Addr.String())
|
|
||||||
ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error
|
|
||||||
if err != nil {
|
|
||||||
return gosocks5.NewReply(gosocks5.Failure, nil), nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := ToSocksAddr(ln.Addr())
|
|
||||||
// Issue: may not reachable when host has multi-interface
|
|
||||||
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
|
||||||
rep := gosocks5.NewReply(gosocks5.Succeeded, addr)
|
|
||||||
if err := rep.Write(conn); err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
|
|
||||||
ln.Close()
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
|
|
||||||
glog.V(LINFO).Infof("[socks5-bind] %s - %s BIND ON %s OK", conn.RemoteAddr(), req.Addr, addr)
|
|
||||||
|
|
||||||
lnChan := make(chan net.Conn, 1)
|
|
||||||
go func() {
|
|
||||||
defer close(lnChan)
|
|
||||||
c, err := ln.AcceptTCP()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
lnChan <- c
|
|
||||||
}()
|
|
||||||
|
|
||||||
peerChan := make(chan error, 1)
|
|
||||||
go func() {
|
|
||||||
defer close(peerChan)
|
|
||||||
b := tcpPool.Get().([]byte)
|
|
||||||
defer tcpPool.Put(b)
|
|
||||||
_, err := conn.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
peerChan <- err
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var pconn net.Conn
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case c := <-lnChan:
|
|
||||||
ln.Close() // only accept one peer
|
|
||||||
if c == nil {
|
|
||||||
return gosocks5.NewReply(gosocks5.Failure, nil), nil, errors.New("[socks5-bind] accept error")
|
|
||||||
}
|
|
||||||
pconn = c
|
|
||||||
lnChan = nil
|
|
||||||
ln = nil
|
|
||||||
conn.SetReadDeadline(time.Now()) // timeout right now ,so we can break out of blocking
|
|
||||||
case err := <-peerChan:
|
|
||||||
if err != nil || pconn == nil {
|
|
||||||
if ln != nil {
|
|
||||||
ln.Close()
|
|
||||||
}
|
|
||||||
if pconn != nil {
|
|
||||||
pconn.Close()
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
err = errors.New("Oops, some mysterious error!")
|
|
||||||
}
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
goto out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
conn.SetReadDeadline(time.Time{})
|
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[socks5-bind] %s <- %s PEER %s ACCEPTED", conn.RemoteAddr(), addr, pconn.RemoteAddr())
|
|
||||||
rep = gosocks5.NewReply(gosocks5.Succeeded, ToSocksAddr(pconn.RemoteAddr()))
|
|
||||||
return rep, pconn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
|
|
||||||
host := "0.0.0.0"
|
|
||||||
port := 0
|
|
||||||
if addr != nil {
|
|
||||||
h, p, _ := net.SplitHostPort(addr.String())
|
|
||||||
host = h
|
|
||||||
port, _ = strconv.Atoi(p)
|
|
||||||
}
|
|
||||||
return &gosocks5.Addr{
|
|
||||||
Type: gosocks5.AddrIPv4,
|
|
||||||
Host: host,
|
|
||||||
Port: uint16(port),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
84
tools/chain_request.go
Normal file
84
tools/chain_request.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ginuerzh/gost"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
proxyNodes stringlist
|
||||||
|
urls []string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.Var(&proxyNodes, "F", "forward address, can make a forward chain")
|
||||||
|
flag.Parse()
|
||||||
|
if flag.NArg() == 0 {
|
||||||
|
log.Fatal("please specific at least one request URL")
|
||||||
|
}
|
||||||
|
urls = flag.Args()
|
||||||
|
if glog.V(gost.LVDEBUG) {
|
||||||
|
http2.VerboseLogs = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringlist []string
|
||||||
|
|
||||||
|
func (list *stringlist) String() string {
|
||||||
|
return fmt.Sprintf("%s", *list)
|
||||||
|
}
|
||||||
|
func (list *stringlist) Set(value string) error {
|
||||||
|
*list = append(*list, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
chain := gost.NewProxyChain()
|
||||||
|
for _, s := range proxyNodes {
|
||||||
|
node, err := gost.ParseProxyNode(s)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
chain.AddProxyNode(*node)
|
||||||
|
}
|
||||||
|
chain.Init()
|
||||||
|
|
||||||
|
for _, u := range urls {
|
||||||
|
url, err := url.Parse(u)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Invalid url:", u)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("GET", u)
|
||||||
|
conn, err := chain.Dial(url.Host)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
header, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Println(string(header))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,63 +0,0 @@
|
|||||||
// +build http2client
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"golang.org/x/net/http2"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
//"net/http/httputil"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
tr := http2.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
},
|
|
||||||
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
|
||||||
return tls.DialWithDialer(&net.Dialer{Timeout: 30 * time.Second}, "tcp", "localhost:8080", cfg)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
client := http.Client{Transport: &tr}
|
|
||||||
|
|
||||||
pr, pw := io.Pipe()
|
|
||||||
|
|
||||||
req, err := http.NewRequest("CONNECT", "https://www.baidu.com", ioutil.NopCloser(pr))
|
|
||||||
req.ContentLength = -1
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
req := &http.Request{
|
|
||||||
Method: "CONNECT",
|
|
||||||
URL: &url.URL{Scheme: "https"},
|
|
||||||
Host: "www.baidu.com:443",
|
|
||||||
Header: make(http.Header),
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
r, err := http.NewRequest("GET", "https://www.baidu.com", nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
r.Write(pw)
|
|
||||||
|
|
||||||
n, err := io.Copy(os.Stdout, resp.Body)
|
|
||||||
log.Fatalf("copied %d, %v", n, err)
|
|
||||||
}
|
|
63
ws.go
63
ws.go
@ -1,13 +1,13 @@
|
|||||||
package main
|
package gost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"github.com/ginuerzh/gosocks5"
|
//"github.com/ginuerzh/gosocks5"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"github.com/golang/glog"
|
//"github.com/golang/glog"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
//"net/http"
|
||||||
"net/http/httputil"
|
//"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -19,8 +19,8 @@ type wsConn struct {
|
|||||||
|
|
||||||
func wsClient(scheme string, conn net.Conn, host string) (*wsConn, error) {
|
func wsClient(scheme string, conn net.Conn, host string) (*wsConn, error) {
|
||||||
dialer := websocket.Dialer{
|
dialer := websocket.Dialer{
|
||||||
ReadBufferSize: 4096,
|
ReadBufferSize: 1024,
|
||||||
WriteBufferSize: 4096,
|
WriteBufferSize: 1024,
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
HandshakeTimeout: time.Second * 90,
|
HandshakeTimeout: time.Second * 90,
|
||||||
NetDial: func(net, addr string) (net.Conn, error) {
|
NetDial: func(net, addr string) (net.Conn, error) {
|
||||||
@ -88,54 +88,3 @@ func (c *wsConn) SetReadDeadline(t time.Time) error {
|
|||||||
func (c *wsConn) SetWriteDeadline(t time.Time) error {
|
func (c *wsConn) SetWriteDeadline(t time.Time) error {
|
||||||
return c.conn.SetWriteDeadline(t)
|
return c.conn.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ws struct {
|
|
||||||
upgrader websocket.Upgrader
|
|
||||||
arg Args
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWs(arg Args) *ws {
|
|
||||||
return &ws{
|
|
||||||
arg: arg,
|
|
||||||
upgrader: websocket.Upgrader{
|
|
||||||
ReadBufferSize: 1024,
|
|
||||||
WriteBufferSize: 1024,
|
|
||||||
CheckOrigin: func(r *http.Request) bool { return true },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ws) handle(w http.ResponseWriter, r *http.Request) {
|
|
||||||
glog.V(LINFO).Infof("[ws] %s - %s", r.RemoteAddr, s.arg.Addr)
|
|
||||||
if glog.V(LDEBUG) {
|
|
||||||
dump, err := httputil.DumpRequest(r, false)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LWARNING).Infof("[ws] %s - %s : %s", r.RemoteAddr, s.arg.Addr, err)
|
|
||||||
} else {
|
|
||||||
glog.V(LDEBUG).Infof("[ws] %s - %s\n%s", r.RemoteAddr, s.arg.Addr, string(dump))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn, err := s.upgrader.Upgrade(w, r, nil)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(LERROR).Infoln(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
handleConn(wsServer(conn), s.arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ws) ListenAndServe() error {
|
|
||||||
sm := http.NewServeMux()
|
|
||||||
sm.HandleFunc("/ws", s.handle)
|
|
||||||
return http.ListenAndServe(s.arg.Addr, sm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ws) listenAndServeTLS() error {
|
|
||||||
sm := http.NewServeMux()
|
|
||||||
sm.HandleFunc("/ws", s.handle)
|
|
||||||
server := &http.Server{
|
|
||||||
Addr: s.arg.Addr,
|
|
||||||
TLSConfig: &tls.Config{Certificates: []tls.Certificate{s.arg.Cert}},
|
|
||||||
Handler: sm,
|
|
||||||
}
|
|
||||||
return server.ListenAndServeTLS("", "")
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user