add gost library

This commit is contained in:
rui.zheng 2016-09-30 17:37:28 +08:00
parent b3461ea527
commit ff6d3e14d8
20 changed files with 2264 additions and 1281 deletions

21
LICENSE Normal file
View 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
View 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
}

605
conn.go
View File

@ -1,4 +1,4 @@
package main
package gost
import (
"bufio"
@ -9,557 +9,117 @@ import (
"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
)
type ProxyConn struct {
conn net.Conn
node ProxyNode
handshaked bool
handshakeMutex sync.Mutex
handshakeErr error
}
var (
// tcp buffer pool
tcpPool = sync.Pool{
New: func() interface{} {
return make([]byte, 32*1024)
},
func NewProxyConn(conn net.Conn, node ProxyNode) *ProxyConn {
return &ProxyConn{
conn: conn,
node: node,
}
// 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
// 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()
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 {
if err := c.handshakeErr; 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)
if c.handshaked {
return nil
}
c.handshakeErr = c.handshake()
return c.handshakeErr
}
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
switch arg.Transport {
switch c.node.Transport {
case "ws": // websocket connection
conn, err = wsClient("ws", conn, arg.Addr)
conn, err := wsClient("ws", c.conn, c.node.Addr)
if err != nil {
return nil, err
return err
}
c.conn = conn
case "wss": // websocket security
tlsUsed = true
conn, err = wsClient("wss", conn, arg.Addr)
conn, err := wsClient("wss", c.conn, c.node.Addr)
if err != nil {
return nil, err
return err
}
case "tls": // tls connection
c.conn = conn
case "tls", "http2": // tls connection
tlsUsed = true
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
// conn = tls.Client(conn, &tls.Config{ServerName: "ice139.com"})
case "tcp":
fallthrough
cfg := &tls.Config{
InsecureSkipVerify: c.node.insecureSkipVerify(),
ServerName: c.node.serverName,
}
c.conn = tls.Client(c.conn, cfg)
default:
}
switch arg.Protocol {
case "socks", "socks5":
switch c.node.Protocol {
case "socks", "socks5": // socks5 handshake with auth and tls supported
selector := &clientSelector{
methods: []uint8{
gosocks5.MethodNoAuth,
gosocks5.MethodUserPass,
//MethodTLS,
},
user: arg.User,
user: c.node.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 := gosocks5.ClientConn(c.conn, selector)
if err := conn.Handleshake(); err != nil {
return err
}
conn = c
c.conn = conn
case "ss": // shadowsocks
if arg.User != nil {
method := arg.User.Username()
password, _ := arg.User.Password()
if c.node.User != nil {
method := c.node.User.Username()
password, _ := c.node.User.Password()
cipher, err := shadowsocks.NewCipher(method, password)
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
default:
}
return conn, nil
c.handshaked = true
return nil
}
func establish(conn net.Conn, addr string, arg Args) error {
switch arg.Protocol {
// Connect to addr through this proxy node
func (c *ProxyConn) Connect(addr string) error {
switch c.node.Protocol {
case "ss": // shadowsocks
host, port, err := net.SplitHostPort(addr)
if err != nil {
@ -576,9 +136,10 @@ func establish(conn net.Conn, addr string, arg Args) error {
return err
}
b := buf.Bytes()
if _, err := conn.Write(b[3:]); err != nil {
if _, err := c.Write(b[3:]); err != nil {
return err
}
glog.V(LDEBUG).Infoln(req)
case "socks", "socks5":
host, port, err := net.SplitHostPort(addr)
@ -591,12 +152,12 @@ func establish(conn net.Conn, addr string, arg Args) error {
Host: host,
Port: uint16(p),
})
if err := req.Write(conn); err != nil {
if err := req.Write(c); err != nil {
return err
}
glog.V(LDEBUG).Infoln(req)
rep, err := gosocks5.ReadReply(conn)
rep, err := gosocks5.ReadReply(c)
if err != nil {
return err
}
@ -604,11 +165,11 @@ func establish(conn net.Conn, addr string, arg Args) error {
if rep.Rep != gosocks5.Succeeded {
return errors.New("Service unavailable")
}
case "http":
case "http", "http2":
fallthrough
default:
req := &http.Request{
Method: "CONNECT",
Method: http.MethodConnect,
URL: &url.URL{Host: addr},
Host: addr,
ProtoMajor: 1,
@ -616,11 +177,11 @@ func establish(conn net.Conn, addr string, arg Args) error {
Header: make(http.Header),
}
req.Header.Set("Proxy-Connection", "keep-alive")
if arg.User != nil {
if c.node.User != nil {
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
}
if glog.V(LDEBUG) {
@ -628,7 +189,7 @@ func establish(conn net.Conn, addr string, arg Args) error {
glog.Infoln(string(dump))
}
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
resp, err := http.ReadResponse(bufio.NewReader(c), req)
if err != nil {
return err
}
@ -643,3 +204,35 @@ func establish(conn net.Conn, addr string, arg Args) error {
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
View 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
View 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
View 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
View 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),
}
}

View File

View File

View File

141
gost/ws.go Normal file
View 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
View File

@ -1,122 +1,20 @@
package main
package gost
import (
"bufio"
"crypto/tls"
"encoding/base64"
"github.com/golang/glog"
"golang.org/x/net/http2"
//"bufio"
//"crypto/tls"
//"encoding/base64"
//"github.com/golang/glog"
//"golang.org/x/net/http2"
"io"
"net"
"net/http"
"net/http/httputil"
"strings"
//"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
}
// http2 client connection, wrapped up just like a net.Conn
type Http2ClientConn struct {
r io.Reader
w io.Writer
@ -158,153 +56,3 @@ func (c *Http2ClientConn) SetReadDeadline(t time.Time) error {
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
}

100
node.go Normal file
View 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
View File

@ -1,9 +1,9 @@
package main
package gost
import (
//"bytes"
"crypto/tls"
"errors"
//"errors"
"github.com/ginuerzh/gosocks5"
"github.com/golang/glog"
//"os/exec"
@ -11,8 +11,8 @@ import (
//"io/ioutil"
"net"
"net/url"
"strconv"
"time"
//"strconv"
//"time"
)
const (
@ -162,392 +162,3 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con
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
View 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))
}
}

View File

@ -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
View File

@ -1,13 +1,13 @@
package main
package gost
import (
//"github.com/ginuerzh/gosocks5"
"crypto/tls"
"github.com/golang/glog"
//"github.com/golang/glog"
"github.com/gorilla/websocket"
"net"
"net/http"
"net/http/httputil"
//"net/http"
//"net/http/httputil"
"net/url"
"time"
)
@ -19,8 +19,8 @@ type wsConn struct {
func wsClient(scheme string, conn net.Conn, host string) (*wsConn, error) {
dialer := websocket.Dialer{
ReadBufferSize: 4096,
WriteBufferSize: 4096,
ReadBufferSize: 1024,
WriteBufferSize: 1024,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
HandshakeTimeout: time.Second * 90,
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 {
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("", "")
}