fix shadowsocks

This commit is contained in:
rui.zheng 2016-10-05 14:53:15 +08:00
parent f903771282
commit 518ca462c6
20 changed files with 553 additions and 2486 deletions

2
.gitignore vendored
View File

@ -23,3 +23,5 @@ _testmain.go
*.exe *.exe
*.test *.test
*.bak

View File

@ -10,8 +10,9 @@ gost - GO Simple Tunnel
* 兼容标准http(s)/socks5代理协议 * 兼容标准http(s)/socks5代理协议
* socks5代理支持tls协商加密 * socks5代理支持tls协商加密
* Tunnel UDP over TCP * Tunnel UDP over TCP
* 兼容shadowsocks协议 * 兼容shadowsocks协议支持OTA(OTA功能需2.2及以上版本)
* 支持端口转发(2.1及以上版本) * 支持端口转发(2.1及以上版本)
* 支持HTTP2.0(2.2及以上版本)
二进制文件下载https://github.com/ginuerzh/gost/releases 二进制文件下载https://github.com/ginuerzh/gost/releases

View File

@ -2,6 +2,7 @@ package gost
import ( import (
"crypto/tls" "crypto/tls"
"encoding/base64"
"errors" "errors"
"github.com/golang/glog" "github.com/golang/glog"
"golang.org/x/net/http2" "golang.org/x/net/http2"
@ -114,7 +115,22 @@ func (c *ProxyChain) GetConn() (net.Conn, error) {
if c.Http2Enabled() { if c.Http2Enabled() {
nodes = nodes[c.http2NodeIndex+1:] nodes = nodes[c.http2NodeIndex+1:]
if len(nodes) == 0 { if len(nodes) == 0 {
return c.getHttp2Conn() header := make(http.Header)
header.Set("Proxy-Switch", "gost") // Flag header to indicate server to switch to HTTP2 transport mode
conn, err := c.getHttp2Conn(header)
if err != nil {
return nil, err
}
http2Node := c.nodes[c.http2NodeIndex]
if http2Node.Protocol == "" {
http2Node.Protocol = "socks5" // assume it as socks5 protocol
}
pc := NewProxyConn(conn, http2Node)
if err := pc.Handshake(); err != nil {
conn.Close()
return nil, err
}
return pc, nil
} }
} }
return c.travelNodes(nodes...) return c.travelNodes(nodes...)
@ -183,17 +199,21 @@ func (c *ProxyChain) travelNodes(nodes ...ProxyNode) (conn *ProxyConn, err error
} }
// Initialize an HTTP2 transport if HTTP2 is enabled. // Initialize an HTTP2 transport if HTTP2 is enabled.
func (c *ProxyChain) getHttp2Conn() (net.Conn, error) { func (c *ProxyChain) getHttp2Conn(header http.Header) (net.Conn, error) {
if !c.Http2Enabled() { if !c.Http2Enabled() {
return nil, errors.New("HTTP2 not enabled") return nil, errors.New("HTTP2 not enabled")
} }
http2Node := c.nodes[c.http2NodeIndex] http2Node := c.nodes[c.http2NodeIndex]
pr, pw := io.Pipe() pr, pw := io.Pipe()
if header == nil {
header = make(http.Header)
}
req := http.Request{ req := http.Request{
Method: http.MethodConnect, Method: http.MethodConnect,
URL: &url.URL{Scheme: "https", Host: http2Node.Addr}, URL: &url.URL{Scheme: "https", Host: http2Node.Addr},
Header: make(http.Header), Header: header,
Proto: "HTTP/2.0", Proto: "HTTP/2.0",
ProtoMajor: 2, ProtoMajor: 2,
ProtoMinor: 0, ProtoMinor: 0,
@ -201,7 +221,6 @@ func (c *ProxyChain) getHttp2Conn() (net.Conn, error) {
Host: http2Node.Addr, Host: http2Node.Addr,
ContentLength: -1, ContentLength: -1,
} }
req.Header.Set("Proxy-Switch", "gost") // Flag header to indicate server to switch to HTTP2 transport mode
if glog.V(LDEBUG) { if glog.V(LDEBUG) {
dump, _ := httputil.DumpRequest(&req, false) dump, _ := httputil.DumpRequest(&req, false)
glog.Infoln(string(dump)) glog.Infoln(string(dump))
@ -214,19 +233,23 @@ func (c *ProxyChain) getHttp2Conn() (net.Conn, error) {
resp.Body.Close() resp.Body.Close()
return nil, errors.New(resp.Status) return nil, errors.New(resp.Status)
} }
return &http2Conn{r: resp.Body, w: pw}, nil conn := &http2Conn{r: resp.Body, w: pw}
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", http2Node.Addr)
return conn, nil
} }
// Use HTTP2 as transport to connect target addr // Use HTTP2 as transport to connect target addr
func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) { func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) {
conn, err := c.getHttp2Conn() if !c.Http2Enabled() {
if err != nil { return nil, errors.New("HTTP2 not enabled")
return nil, err
} }
pc := NewProxyConn(conn, c.nodes[c.http2NodeIndex]) http2Node := c.nodes[c.http2NodeIndex]
if err = pc.Connect(addr); err != nil {
pc.Close() header := make(http.Header)
return nil, err header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to
if http2Node.User != nil {
header.Set("Proxy-Authorization",
"Basic "+base64.StdEncoding.EncodeToString([]byte(http2Node.User.String())))
} }
return conn, nil return c.getHttp2Conn(header)
} }

View File

@ -1,645 +0,0 @@
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
}

View File

@ -1,308 +0,0 @@
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 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
}

View File

@ -1,76 +0,0 @@
// main
package main
import (
"flag"
"fmt"
"github.com/golang/glog"
"golang.org/x/net/http2"
"os"
"runtime"
"sync"
)
const (
LFATAL = iota
LERROR
LWARNING
LINFO
LDEBUG
LVDEBUG // verbose debug
)
const (
Version = "2.2-dev-http2"
)
var (
listenAddr, forwardAddr strSlice
pv bool // print version
listenArgs []Args
forwardArgs []Args
)
func init() {
flag.Var(&listenAddr, "L", "listen address, can listen on multiple ports")
flag.Var(&forwardAddr, "F", "forward address, can make a forward chain")
flag.BoolVar(&pv, "V", false, "print version")
flag.Parse()
if glog.V(LVDEBUG) {
http2.VerboseLogs = true
}
}
func main() {
defer glog.Flush()
if flag.NFlag() == 0 {
flag.PrintDefaults()
return
}
if pv {
fmt.Fprintf(os.Stderr, "gost %s (%s)\n", Version, runtime.Version())
return
}
listenArgs = parseArgs(listenAddr)
if len(listenArgs) == 0 {
fmt.Fprintln(os.Stderr, "no listen address, please specify at least one -L parameter")
return
}
forwardArgs = parseArgs(forwardAddr)
processForwardChain(forwardArgs...)
var wg sync.WaitGroup
for _, args := range listenArgs {
wg.Add(1)
go func(arg Args) {
defer wg.Done()
glog.V(LERROR).Infoln(listenAndServe(arg))
}(args)
}
wg.Wait()
}

View File

@ -1,553 +0,0 @@
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

@ -1,123 +0,0 @@
package main
import (
"encoding/binary"
"fmt"
"github.com/ginuerzh/gosocks5"
"github.com/golang/glog"
"github.com/shadowsocks/shadowsocks-go/shadowsocks"
"io"
"net"
)
func handleShadow(conn net.Conn, arg Args) {
glog.V(LINFO).Infof("[ss] %s -> %s", conn.RemoteAddr(), conn.LocalAddr())
if arg.User != nil {
method := arg.User.Username()
password, _ := arg.User.Password()
cipher, err := shadowsocks.NewCipher(method, password)
if err != nil {
glog.V(LWARNING).Infof("[ss] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
return
}
conn = shadowsocks.NewConn(conn, cipher)
}
addr, extra, err := getShadowRequest(conn)
if err != nil {
glog.V(LWARNING).Infof("[ss] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
return
}
glog.V(LINFO).Infof("[ss] %s -> %s", conn.RemoteAddr(), addr.String())
sconn, err := connect(addr.String(), "ss", forwardArgs...)
if err != nil {
glog.V(LWARNING).Infof("[ss] %s -> %s : %s", conn.RemoteAddr(), addr.String(), err)
return
}
defer sconn.Close()
if extra != nil {
if _, err := sconn.Write(extra); err != nil {
glog.V(LWARNING).Infof("[ss] %s - %s : %s", conn.RemoteAddr(), addr.String(), err)
return
}
}
glog.V(LINFO).Infof("[ss] %s <-> %s", conn.RemoteAddr(), addr.String())
Transport(conn, sconn)
glog.V(LINFO).Infof("[ss] %s >-< %s", conn.RemoteAddr(), addr.String())
}
func getShadowRequest(conn net.Conn) (addr *gosocks5.Addr, extra []byte, err error) {
const (
idType = 0 // address type index
idIP0 = 1 // ip addres start index
idDmLen = 1 // domain address length index
idDm0 = 2 // domain address start index
typeIPv4 = 1 // type is ipv4 address
typeDm = 3 // type is domain address
typeIPv6 = 4 // type is ipv6 address
lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port
lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port
lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen
)
// buf size should at least have the same size with the largest possible
// request size (when addrType is 3, domain name has at most 256 bytes)
// 1(addrType) + 1(lenByte) + 256(max length address) + 2(port)
buf := make([]byte, 1024)
var n int
// read till we get possible domain length field
//shadowsocks.SetReadTimeout(conn)
if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil {
return
}
addr = &gosocks5.Addr{
Type: buf[idType],
}
reqLen := -1
switch buf[idType] {
case typeIPv4:
reqLen = lenIPv4
case typeIPv6:
reqLen = lenIPv6
case typeDm:
reqLen = int(buf[idDmLen]) + lenDmBase
default:
err = fmt.Errorf("addr type %d not supported", buf[idType])
return
}
if n < reqLen { // rare case
//ss.SetReadTimeout(conn)
if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil {
return
}
} else if n > reqLen {
// it's possible to read more than just the request head
extra = buf[reqLen:n]
}
// Return string for typeIP is not most efficient, but browsers (Chrome,
// Safari, Firefox) all seems using typeDm exclusively. So this is not a
// big problem.
switch buf[idType] {
case typeIPv4:
addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String()
case typeIPv6:
addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String()
case typeDm:
addr.Host = string(buf[idDm0 : idDm0+buf[idDmLen]])
}
// parse port
addr.Port = binary.BigEndian.Uint16(buf[reqLen-2 : reqLen])
return
}

View File

@ -1,74 +0,0 @@
package main
import (
"crypto/tls"
"github.com/golang/glog"
)
const (
certFile = "cert.pem"
keyFile = "key.pem"
// This is the default cert file for convenience, providing your own cert is recommended.
rawCert = `-----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-----`
rawKey = `-----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 init() {
var err error
if tlsCert, err = tls.LoadX509KeyPair(certFile, keyFile); err != nil {
glog.V(LWARNING).Infoln(err)
tlsCert, err = tls.X509KeyPair([]byte(rawCert), []byte(rawKey))
if err != nil {
glog.Infoln(err)
}
}
}
var (
tlsCert tls.Certificate
)

View File

@ -1,170 +0,0 @@
package main
import (
"bytes"
"github.com/ginuerzh/gosocks5"
"github.com/golang/glog"
"net"
//"time"
)
func transportUDP(relay, peer *net.UDPConn) (err error) {
rChan := make(chan error, 1)
wChan := make(chan error, 1)
var clientAddr *net.UDPAddr
go func() {
b := udpPool.Get().([]byte)
defer udpPool.Put(b)
for {
n, laddr, err := relay.ReadFromUDP(b)
if err != nil {
rChan <- err
return
}
if clientAddr == nil {
clientAddr = laddr
}
dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))
if err != nil {
rChan <- err
return
}
raddr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String())
if err != nil {
continue // drop silently
}
if _, err := peer.WriteToUDP(dgram.Data, raddr); err != nil {
rChan <- err
return
}
glog.V(LDEBUG).Infof("[socks5-udp] %s >>> %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data))
}
}()
go func() {
b := udpPool.Get().([]byte)
defer udpPool.Put(b)
for {
n, raddr, err := peer.ReadFrom(b)
if err != nil {
wChan <- err
return
}
if clientAddr == nil {
continue
}
buf := bytes.Buffer{}
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, ToSocksAddr(raddr)), b[:n])
dgram.Write(&buf)
if _, err := relay.WriteToUDP(buf.Bytes(), clientAddr); err != nil {
wChan <- err
return
}
glog.V(LDEBUG).Infof("[socks5-udp] %s <<< %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data))
}
}()
select {
case err = <-wChan:
//log.Println("w exit", err)
case err = <-rChan:
//log.Println("r exit", err)
}
return
}
func tunnelUDP(conn *net.UDPConn, tun net.Conn, client bool) (err error) {
rChan := make(chan error, 1)
wChan := make(chan error, 1)
var clientAddr *net.UDPAddr
go func() {
b := udpPool.Get().([]byte)
defer udpPool.Put(b)
for {
n, addr, err := conn.ReadFromUDP(b)
if err != nil {
rChan <- err
return
}
var dgram *gosocks5.UDPDatagram
if client { // pipe from relay to tunnel
dgram, err = gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))
if err != nil {
rChan <- err
return
}
if clientAddr == nil {
clientAddr = addr
}
dgram.Header.Rsv = uint16(len(dgram.Data))
if err := dgram.Write(tun); err != nil {
rChan <- err
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", conn.LocalAddr(), dgram.Header.Addr, len(dgram.Data))
} else { // pipe from peer to tunnel
dgram = gosocks5.NewUDPDatagram(
gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n])
if err := dgram.Write(tun); err != nil {
rChan <- err
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", tun.RemoteAddr(), dgram.Header.Addr, len(dgram.Data))
}
}
}()
go func() {
for {
dgram, err := gosocks5.ReadUDPDatagram(tun)
if err != nil {
wChan <- err
return
}
if client { // pipe from tunnel to relay
if clientAddr == nil {
continue
}
dgram.Header.Rsv = 0
buf := bytes.Buffer{}
dgram.Write(&buf)
if _, err := conn.WriteToUDP(buf.Bytes(), clientAddr); err != nil {
wChan <- err
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", conn.LocalAddr(), dgram.Header.Addr, len(dgram.Data))
} else { // pipe from tunnel to peer
addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String())
if err != nil {
continue // drop silently
}
if _, err := conn.WriteToUDP(dgram.Data, addr); err != nil {
wChan <- err
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", tun.RemoteAddr(), addr, len(dgram.Data))
}
}
}()
select {
case err = <-wChan:
//log.Println("w exit", err)
case err = <-rChan:
//log.Println("r exit", err)
}
return
}

View File

@ -1,193 +0,0 @@
package main
import (
"crypto/tls"
"errors"
"fmt"
"github.com/golang/glog"
"io"
"net"
"net/url"
"strings"
"time"
)
const (
keepAliveTime = 180 * time.Second
)
type strSlice []string
func (ss *strSlice) String() string {
return fmt.Sprintf("%s", *ss)
}
func (ss *strSlice) Set(value string) error {
*ss = append(*ss, value)
return nil
}
// admin:123456@localhost:8080
type Args 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
Cert tls.Certificate // tls certificate
}
func (args Args) String() string {
var authUser, authPass string
if args.User != nil {
authUser = args.User.Username()
authPass, _ = args.User.Password()
}
return fmt.Sprintf("host: %s, protocol: %s, transport: %s, remote: %s, auth: %s/%s",
args.Addr, args.Protocol, args.Transport, args.Remote, authUser, authPass)
}
func parseArgs(ss []string) (args []Args) {
for _, s := range ss {
if !strings.Contains(s, "://") {
s = "auto://" + s
}
u, err := url.Parse(s)
if err != nil {
glog.V(LWARNING).Infoln(err)
continue
}
arg := Args{
Addr: u.Host,
User: u.User,
Cert: tlsCert,
}
schemes := strings.Split(u.Scheme, "+")
if len(schemes) == 1 {
arg.Protocol = schemes[0]
arg.Transport = schemes[0]
}
if len(schemes) == 2 {
arg.Protocol = schemes[0]
arg.Transport = schemes[1]
}
switch arg.Transport {
case "ws", "wss", "tls":
case "https":
arg.Protocol = "http"
arg.Transport = "tls"
case "http2":
arg.Protocol = "http2"
case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
arg.Remote = strings.Trim(u.EscapedPath(), "/")
case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding
arg.Remote = strings.Trim(u.EscapedPath(), "/")
default:
arg.Transport = ""
}
switch arg.Protocol {
case "http", "http2", "socks", "socks5", "ss":
default:
arg.Protocol = ""
}
args = append(args, arg)
}
return
}
func processForwardChain(chain ...Args) {
glog.V(LINFO).Infoln(chain)
if len(chain) == 0 {
return
}
length := len(chain)
c, last := chain[:length-1], chain[length-1]
// http2 restrict: only last proxy can enable http2
for i, _ := range c {
if c[i].Transport == "http2" {
c[i].Transport = ""
}
}
if last.Transport == "http2" {
initHttp2Client(last.Addr, c...)
}
}
// Based on io.Copy, but the io.ErrShortWrite is ignored (mainly for websocket)
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
// b := make([]byte, 32*1024)
b := tcpPool.Get().([]byte)
defer tcpPool.Put(b)
for {
nr, er := src.Read(b)
//log.Println("cp r", nr, er)
if nr > 0 {
nw, ew := dst.Write(b[:nr])
//log.Println("cp w", nw, ew)
if nw > 0 {
written += int64(nw)
}
if ew != nil {
err = ew
break
}
/*
if nr != nw {
err = io.ErrShortWrite
break
}
*/
}
if er == io.EOF {
break
}
if er != nil {
err = er
break
}
}
return
}
func Pipe(src io.Reader, dst io.Writer, ch chan<- error) {
_, err := Copy(dst, src)
ch <- err
}
func Transport(conn, conn2 net.Conn) (err error) {
rChan := make(chan error, 1)
wChan := make(chan error, 1)
go Pipe(conn, conn2, wChan)
go Pipe(conn2, conn, rChan)
select {
case err = <-wChan:
//log.Println("w exit", err)
case err = <-rChan:
//log.Println("r exit", err)
}
return
}
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
}

View File

@ -1,141 +0,0 @@
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("", "")
}

20
conn.go
View File

@ -73,13 +73,15 @@ func (c *ProxyConn) handshake() error {
return err return err
} }
c.conn = conn c.conn = conn
case "tls", "http2": // tls connection case "tls": // tls connection
tlsUsed = true tlsUsed = true
cfg := &tls.Config{ cfg := &tls.Config{
InsecureSkipVerify: c.Node.insecureSkipVerify(), InsecureSkipVerify: c.Node.insecureSkipVerify(),
ServerName: c.Node.serverName, ServerName: c.Node.serverName,
} }
c.conn = tls.Client(c.conn, cfg) c.conn = tls.Client(c.conn, cfg)
case "http2":
tlsUsed = true
default: default:
} }
@ -115,9 +117,9 @@ func (c *ProxyConn) handshake() error {
if err != nil { if err != nil {
return err return err
} }
c.conn = shadowsocks.NewConn(c.conn, cipher) c.conn = &shadowConn{conn: shadowsocks.NewConn(c.conn, cipher)}
} }
case "http", "http2": case "http":
fallthrough fallthrough
default: default:
} }
@ -150,7 +152,7 @@ func (c *ProxyConn) Connect(addr string) error {
return err return err
} }
glog.V(LDEBUG).Infoln(req) glog.V(LDEBUG).Infoln("[ss]", req)
case "socks", "socks5": case "socks", "socks5":
host, port, err := net.SplitHostPort(addr) host, port, err := net.SplitHostPort(addr)
if err != nil { if err != nil {
@ -165,17 +167,17 @@ func (c *ProxyConn) Connect(addr string) error {
if err := req.Write(c); err != nil { if err := req.Write(c); err != nil {
return err return err
} }
glog.V(LDEBUG).Infoln(req) glog.V(LDEBUG).Infoln("[socks5]", req)
rep, err := gosocks5.ReadReply(c) reply, err := gosocks5.ReadReply(c)
if err != nil { if err != nil {
return err return err
} }
glog.V(LDEBUG).Infoln(rep) glog.V(LDEBUG).Infoln("[socks5]", reply)
if rep.Rep != gosocks5.Succeeded { if reply.Rep != gosocks5.Succeeded {
return errors.New("Service unavailable") return errors.New("Service unavailable")
} }
case "http", "http2": case "http":
fallthrough fallthrough
default: default:
req := &http.Request{ req := &http.Request{

View File

@ -1,4 +1,4 @@
package main package gost
import ( import (
"errors" "errors"
@ -9,23 +9,139 @@ import (
"time" "time"
) )
func handleTcpForward(conn net.Conn, raddr net.Addr) { type TcpForwardServer struct {
Base *ProxyServer
Handler func(conn net.Conn, raddr net.Addr)
}
func NewTcpForwardServer(base *ProxyServer) *TcpForwardServer {
return &TcpForwardServer{Base: base}
}
func (s *TcpForwardServer) ListenAndServe() error {
raddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Remote)
if err != nil {
return err
}
ln, err := net.Listen("tcp", s.Base.Node.Addr)
if err != nil {
return err
}
defer ln.Close()
if s.Handler == nil {
s.Handler = s.handleTcpForward
}
for {
conn, err := ln.Accept()
if err != nil {
glog.V(LWARNING).Infoln(err)
continue
}
setKeepAlive(conn, KeepAliveTime)
go s.Handler(conn, raddr)
}
}
func (s *TcpForwardServer) handleTcpForward(conn net.Conn, raddr net.Addr) {
defer conn.Close() defer conn.Close()
glog.V(LINFO).Infof("[tcp] %s - %s", conn.RemoteAddr(), raddr) glog.V(LINFO).Infof("[tcp] %s - %s", conn.RemoteAddr(), raddr)
c, err := connect(raddr.String(), "", forwardArgs...) cc, err := s.Base.Chain.Dial(raddr.String())
if err != nil { if err != nil {
glog.V(LWARNING).Infof("[tcp] %s -> %s : %s", conn.RemoteAddr(), raddr, err) glog.V(LWARNING).Infof("[tcp] %s -> %s : %s", conn.RemoteAddr(), raddr, err)
return return
} }
defer c.Close() defer cc.Close()
glog.V(LINFO).Infof("[tcp] %s <-> %s", conn.RemoteAddr(), raddr) glog.V(LINFO).Infof("[tcp] %s <-> %s", conn.RemoteAddr(), raddr)
Transport(conn, c) s.Base.transport(conn, cc)
glog.V(LINFO).Infof("[tcp] %s >-< %s", conn.RemoteAddr(), raddr) glog.V(LINFO).Infof("[tcp] %s >-< %s", conn.RemoteAddr(), raddr)
} }
func handleUdpForwardLocal(conn *net.UDPConn, laddr, raddr *net.UDPAddr, data []byte) { type UdpForwardServer struct {
Base *ProxyServer
}
func NewUdpForwardServer(base *ProxyServer) *UdpForwardServer {
return &UdpForwardServer{Base: base}
}
func (s *UdpForwardServer) ListenAndServe() error {
laddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Addr)
if err != nil {
return err
}
raddr, err := net.ResolveUDPAddr("udp", s.Base.Node.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(s.Base.Chain.nodes) == 0 {
for {
b := make([]byte, MediumBufferSize)
n, addr, err := conn.ReadFromUDP(b)
if err != nil {
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
continue
}
go func() {
s.handleUdpForwardLocal(conn, addr, raddr, b[:n])
}()
}
}
rChan, wChan := make(chan *gosocks5.UDPDatagram, 32), make(chan *gosocks5.UDPDatagram, 32)
go func() {
for {
b := make([]byte, MediumBufferSize)
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 {
s.handleUdpForwardTunnel(laddr, raddr, rChan, wChan)
}
}
func (s *UdpForwardServer) handleUdpForwardLocal(conn *net.UDPConn, laddr, raddr *net.UDPAddr, data []byte) {
lconn, err := net.ListenUDP("udp", nil) lconn, err := net.ListenUDP("udp", nil)
if err != nil { if err != nil {
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
@ -39,9 +155,8 @@ func handleUdpForwardLocal(conn *net.UDPConn, laddr, raddr *net.UDPAddr, data []
} }
glog.V(LDEBUG).Infof("[udp] %s >>> %s length %d", laddr, raddr, len(data)) glog.V(LDEBUG).Infof("[udp] %s >>> %s length %d", laddr, raddr, len(data))
b := udpPool.Get().([]byte) b := make([]byte, MediumBufferSize)
defer udpPool.Put(b) lconn.SetReadDeadline(time.Now().Add(ReadTimeout))
lconn.SetReadDeadline(time.Now().Add(time.Second * 60))
n, addr, err := lconn.ReadFromUDP(b) n, addr, err := lconn.ReadFromUDP(b)
if err != nil { if err != nil {
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err) glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err)
@ -55,20 +170,85 @@ func handleUdpForwardLocal(conn *net.UDPConn, laddr, raddr *net.UDPAddr, data []
return return
} }
func prepareUdpConnectTunnel(addr net.Addr) (net.Conn, error) { func (s *UdpForwardServer) handleUdpForwardTunnel(laddr, raddr *net.UDPAddr, rChan, wChan chan *gosocks5.UDPDatagram) {
conn, _, err := forwardChain(forwardArgs...) var cc net.Conn
var err error
retry := 0
for {
cc, err = s.prepareUdpConnectTunnel(raddr)
if err != nil {
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
time.Sleep((1 << uint(retry)) * time.Second)
if retry < 5 {
retry++
}
continue
}
break
}
defer cc.Close()
glog.V(LINFO).Infof("[udp] %s <-> %s", laddr, raddr)
rExit := make(chan interface{})
errc := make(chan error, 2)
go func() {
for {
select {
case dgram := <-rChan:
if err := dgram.Write(cc); err != nil {
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
errc <- err
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", laddr, raddr, len(dgram.Data))
case <-rExit:
// glog.V(LDEBUG).Infof("[udp-connect] %s -> %s : exited", laddr, raddr)
return
}
}
}()
go func() {
for {
dgram, err := gosocks5.ReadUDPDatagram(cc)
if err != nil {
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err)
close(rExit)
errc <- err
return
}
select {
case wChan <- dgram:
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", laddr, raddr, len(dgram.Data))
default:
}
}
}()
select {
case <-errc:
//log.Println("w exit", err)
}
glog.V(LINFO).Infof("[udp] %s >-< %s", laddr, raddr)
}
func (s *UdpForwardServer) prepareUdpConnectTunnel(addr net.Addr) (net.Conn, error) {
conn, err := s.Base.Chain.GetConn()
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn.SetWriteDeadline(time.Now().Add(time.Second * 90)) conn.SetWriteDeadline(time.Now().Add(WriteTimeout))
if err = gosocks5.NewRequest(CmdUdpConnect, ToSocksAddr(addr)).Write(conn); err != nil { if err = gosocks5.NewRequest(CmdUdpConnect, ToSocksAddr(addr)).Write(conn); err != nil {
conn.Close() conn.Close()
return nil, err return nil, err
} }
conn.SetWriteDeadline(time.Time{}) conn.SetWriteDeadline(time.Time{})
conn.SetReadDeadline(time.Now().Add(90 * time.Second)) conn.SetReadDeadline(time.Now().Add(ReadTimeout))
reply, err := gosocks5.ReadReply(conn) reply, err := gosocks5.ReadReply(conn)
if err != nil { if err != nil {
conn.Close() conn.Close()
@ -84,73 +264,49 @@ func prepareUdpConnectTunnel(addr net.Addr) (net.Conn, error) {
return conn, nil return conn, nil
} }
func handleUdpForwardTunnel(laddr, raddr *net.UDPAddr, rChan, wChan chan *gosocks5.UDPDatagram) { type RTcpForwardServer struct {
var tun net.Conn Base *ProxyServer
var err error }
func NewRTcpForwardServer(base *ProxyServer) *RTcpForwardServer {
return &RTcpForwardServer{Base: base}
}
func (s *RTcpForwardServer) Serve() error {
if len(s.Base.Chain.nodes) == 0 {
return errors.New("rtcp: at least one -F must be assigned")
}
laddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Addr)
if err != nil {
return err
}
raddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Remote)
if err != nil {
return err
}
retry := 0 retry := 0
for { for {
tun, err = prepareUdpConnectTunnel(raddr) conn, err := s.Base.Chain.GetConn()
if err != nil { if err != nil {
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) glog.V(LWARNING).Infof("[rtcp] %s - %s : %s", laddr, raddr, err)
time.Sleep((1 << uint(retry)) * time.Second) time.Sleep((1 << uint(retry)) * time.Second)
if retry < 5 { if retry < 5 {
retry++ retry++
} }
continue continue
} }
break retry = 0
}
defer tun.Close()
glog.V(LINFO).Infof("[udp] %s <-> %s", laddr, raddr) if err := s.connectRTcpForward(conn, laddr, raddr); err != nil {
conn.Close()
rExit := make(chan interface{}) time.Sleep(6 * time.Second)
rErr, wErr := make(chan error, 1), make(chan error, 1)
go func() {
for {
select {
case dgram := <-rChan:
if err := dgram.Write(tun); err != nil {
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err)
rErr <- err
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", laddr, raddr, len(dgram.Data))
case <-rExit:
// glog.V(LDEBUG).Infof("[udp-connect] %s -> %s : exited", laddr, raddr)
return
}
} }
}()
go func() {
for {
dgram, err := gosocks5.ReadUDPDatagram(tun)
if err != nil {
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err)
close(rExit)
wErr <- err
return
}
select {
case wChan <- dgram:
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", laddr, raddr, len(dgram.Data))
default:
}
}
}()
select {
case <-rErr:
//log.Println("w exit", err)
case <-wErr:
//log.Println("r exit", err)
} }
glog.V(LINFO).Infof("[udp] %s >-< %s", laddr, raddr)
} }
func connectRTcpForward(conn net.Conn, laddr, raddr net.Addr) error { func (s *RTcpForwardServer) connectRTcpForward(conn net.Conn, laddr, raddr net.Addr) error {
glog.V(LINFO).Infof("[rtcp] %s - %s", laddr, raddr) glog.V(LINFO).Infof("[rtcp] %s - %s", laddr, raddr)
req := gosocks5.NewRequest(gosocks5.CmdBind, ToSocksAddr(laddr)) req := gosocks5.NewRequest(gosocks5.CmdBind, ToSocksAddr(laddr))
@ -160,7 +316,7 @@ func connectRTcpForward(conn net.Conn, laddr, raddr net.Addr) error {
} }
// first reply, bind status // first reply, bind status
conn.SetReadDeadline(time.Now().Add(90 * time.Second)) conn.SetReadDeadline(time.Now().Add(ReadTimeout))
rep, err := gosocks5.ReadReply(conn) rep, err := gosocks5.ReadReply(conn)
if err != nil { if err != nil {
glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", laddr, raddr, err) glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", laddr, raddr, err)
@ -197,25 +353,67 @@ func connectRTcpForward(conn net.Conn, laddr, raddr net.Addr) error {
defer lconn.Close() defer lconn.Close()
glog.V(LINFO).Infof("[rtcp] %s <-> %s", rep.Addr, lconn.RemoteAddr()) glog.V(LINFO).Infof("[rtcp] %s <-> %s", rep.Addr, lconn.RemoteAddr())
Transport(lconn, conn) s.Base.transport(lconn, conn)
glog.V(LINFO).Infof("[rtcp] %s >-< %s", rep.Addr, lconn.RemoteAddr()) glog.V(LINFO).Infof("[rtcp] %s >-< %s", rep.Addr, lconn.RemoteAddr())
}() }()
return nil return nil
} }
func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error { type RUdpForwardServer struct {
Base *ProxyServer
}
func NewRUdpForwardServer(base *ProxyServer) *RUdpForwardServer {
return &RUdpForwardServer{Base: base}
}
func (s *RUdpForwardServer) Serve() error {
if len(s.Base.Chain.nodes) == 0 {
return errors.New("rudp: at least one -F must be assigned")
}
laddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Addr)
if err != nil {
return err
}
raddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Remote)
if err != nil {
return err
}
retry := 0
for {
conn, err := s.Base.Chain.GetConn()
if err != nil {
glog.V(LWARNING).Infof("[rudp] %s - %s : %s", laddr, raddr, err)
time.Sleep((1 << uint(retry)) * time.Second)
if retry < 5 {
retry++
}
continue
}
retry = 0
if err := s.connectRUdpForward(conn, laddr, raddr); err != nil {
conn.Close()
time.Sleep(6 * time.Second)
}
}
}
func (s *RUdpForwardServer) connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error {
glog.V(LINFO).Infof("[rudp] %s - %s", laddr, raddr) glog.V(LINFO).Infof("[rudp] %s - %s", laddr, raddr)
req := gosocks5.NewRequest(CmdUdpTun, ToSocksAddr(laddr)) req := gosocks5.NewRequest(CmdUdpTun, ToSocksAddr(laddr))
conn.SetWriteDeadline(time.Now().Add(time.Second * 90)) conn.SetWriteDeadline(time.Now().Add(WriteTimeout))
if err := req.Write(conn); err != nil { if err := req.Write(conn); err != nil {
glog.V(LWARNING).Infof("[rudp] %s -> %s : %s", laddr, raddr, err) glog.V(LWARNING).Infof("[rudp] %s -> %s : %s", laddr, raddr, err)
return err return err
} }
conn.SetWriteDeadline(time.Time{}) conn.SetWriteDeadline(time.Time{})
conn.SetReadDeadline(time.Now().Add(90 * time.Second)) conn.SetReadDeadline(time.Now().Add(ReadTimeout))
rep, err := gosocks5.ReadReply(conn) rep, err := gosocks5.ReadReply(conn)
if err != nil { if err != nil {
glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err) glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err)
@ -225,7 +423,7 @@ func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error {
if rep.Rep != gosocks5.Succeeded { if rep.Rep != gosocks5.Succeeded {
glog.V(LWARNING).Infof("[rudp] %s <- %s : bind on %s failure", laddr, raddr, laddr) glog.V(LWARNING).Infof("[rudp] %s <- %s : bind on %s failure", laddr, raddr, laddr)
return errors.New(fmt.Sprintf("Bind on %s failure", laddr)) return errors.New(fmt.Sprintf("bind on %s failure", laddr))
} }
glog.V(LINFO).Infof("[rudp] %s - %s BIND ON %s OK", laddr, raddr, rep.Addr) glog.V(LINFO).Infof("[rudp] %s - %s BIND ON %s OK", laddr, raddr, rep.Addr)
@ -238,8 +436,7 @@ func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error {
} }
go func() { go func() {
b := udpPool.Get().([]byte) b := make([]byte, MediumBufferSize)
defer udpPool.Put(b)
relay, err := net.DialUDP("udp", nil, raddr) relay, err := net.DialUDP("udp", nil, raddr)
if err != nil { if err != nil {
@ -254,7 +451,7 @@ func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error {
} }
glog.V(LDEBUG).Infof("[rudp] %s >>> %s length: %d", laddr, raddr, len(dgram.Data)) glog.V(LDEBUG).Infof("[rudp] %s >>> %s length: %d", laddr, raddr, len(dgram.Data))
relay.SetReadDeadline(time.Now().Add(time.Second * 60)) relay.SetReadDeadline(time.Now().Add(ReadTimeout))
n, err := relay.Read(b) n, err := relay.Read(b)
if err != nil { if err != nil {
glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err) glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err)
@ -264,7 +461,7 @@ func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error {
glog.V(LDEBUG).Infof("[rudp] %s <<< %s length: %d", laddr, raddr, n) glog.V(LDEBUG).Infof("[rudp] %s <<< %s length: %d", laddr, raddr, n)
conn.SetWriteDeadline(time.Now().Add(time.Second * 90)) conn.SetWriteDeadline(time.Now().Add(WriteTimeout))
if err := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, dgram.Header.Addr), b[:n]).Write(conn); err != nil { if err := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, dgram.Header.Addr), b[:n]).Write(conn); err != nil {
glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err) glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err)
return return
@ -272,5 +469,4 @@ func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error {
conn.SetWriteDeadline(time.Time{}) conn.SetWriteDeadline(time.Time{})
}() }()
} }
} }

View File

@ -33,7 +33,7 @@ var (
var ( var (
SmallBufferSize = 1 * 1024 // 1KB small buffer SmallBufferSize = 1 * 1024 // 1KB small buffer
MediumBufferSize = 8 * 1024 // 8KB medium buffer MediumBufferSize = 8 * 1024 // 8KB medium buffer
LargeBufferSize = 16 * 1024 // 16KB large buffer LargeBufferSize = 32 * 1024 // 32KB large buffer
) )
var ( var (

22
http.go
View File

@ -148,6 +148,7 @@ func (s *Http2Server) HandleRequest(w http.ResponseWriter, req *http.Request) {
glog.V(LINFO).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err) glog.V(LINFO).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err)
return return
} }
glog.V(LINFO).Infof("[http2] %s - %s : switch to HTTP2 transport mode OK", req.RemoteAddr, target)
s.Base.handleConn(conn) s.Base.handleConn(conn)
return return
} }
@ -257,13 +258,18 @@ func (s *Http2Server) Upgrade(w http.ResponseWriter, r *http.Request) (net.Conn,
fw.Flush() fw.Flush()
} }
return &http2Conn{r: r.Body, w: flushWriter{w}}, nil conn := &http2Conn{r: r.Body, w: flushWriter{w}}
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", r.RemoteAddr)
conn.localAddr, _ = net.ResolveTCPAddr("tcp", r.Host)
return conn, nil
} }
// HTTP2 client connection, wrapped up just like a net.Conn // HTTP2 client connection, wrapped up just like a net.Conn
type http2Conn struct { type http2Conn struct {
r io.Reader r io.Reader
w io.Writer w io.Writer
remoteAddr net.Addr
localAddr net.Addr
} }
func (c *http2Conn) Read(b []byte) (n int, err error) { func (c *http2Conn) Read(b []byte) (n int, err error) {
@ -282,23 +288,23 @@ func (c *http2Conn) Close() error {
} }
func (c *http2Conn) LocalAddr() net.Addr { func (c *http2Conn) LocalAddr() net.Addr {
return nil return c.localAddr
} }
func (c *http2Conn) RemoteAddr() net.Addr { func (c *http2Conn) RemoteAddr() net.Addr {
return nil return c.remoteAddr
} }
func (c *http2Conn) SetDeadline(t time.Time) error { func (c *http2Conn) SetDeadline(t time.Time) error {
return nil return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
} }
func (c *http2Conn) SetReadDeadline(t time.Time) error { func (c *http2Conn) SetReadDeadline(t time.Time) error {
return nil return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
} }
func (c *http2Conn) SetWriteDeadline(t time.Time) error { func (c *http2Conn) SetWriteDeadline(t time.Time) error {
return nil return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
} }
type flushWriter struct { type flushWriter struct {

24
node.go
View File

@ -56,12 +56,10 @@ func ParseProxyNode(s string) (node ProxyNode, err error) {
} }
switch node.Transport { switch node.Transport {
case "ws", "wss", "tls": case "ws", "wss", "tls", "http2":
case "https": case "https":
node.Protocol = "http" node.Protocol = "http"
node.Transport = "tls" node.Transport = "tls"
case "http2":
node.Protocol = "http2"
case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
node.Remote = strings.Trim(u.EscapedPath(), "/") node.Remote = strings.Trim(u.EscapedPath(), "/")
case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding
@ -71,7 +69,7 @@ func ParseProxyNode(s string) (node ProxyNode, err error) {
} }
switch node.Protocol { switch node.Protocol {
case "http", "socks", "socks5", "ss", "http2": case "http", "socks", "socks5", "ss":
default: default:
node.Protocol = "" node.Protocol = ""
} }
@ -84,19 +82,21 @@ func (node *ProxyNode) Get(key string) string {
return node.values.Get(key) return node.values.Get(key)
} }
func (node *ProxyNode) getBool(key string) bool {
s := node.Get(key)
if b, _ := strconv.ParseBool(s); b {
return b
}
n, _ := strconv.Atoi(s)
return n > 0
}
func (node *ProxyNode) Set(key, value string) { func (node *ProxyNode) Set(key, value string) {
node.values.Set(key, value) node.values.Set(key, value)
} }
func (node *ProxyNode) insecureSkipVerify() bool { func (node *ProxyNode) insecureSkipVerify() bool {
s := node.Get("secure") return !node.getBool("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 { func (node *ProxyNode) certFile() string {

View File

@ -5,6 +5,7 @@ import (
"crypto/tls" "crypto/tls"
"github.com/ginuerzh/gosocks5" "github.com/ginuerzh/gosocks5"
"github.com/golang/glog" "github.com/golang/glog"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
"io" "io"
"net" "net"
"net/http" "net/http"
@ -15,6 +16,7 @@ type ProxyServer struct {
Chain *ProxyChain Chain *ProxyChain
TLSConfig *tls.Config TLSConfig *tls.Config
selector *serverSelector selector *serverSelector
cipher *ss.Cipher
} }
func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *ProxyServer { func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *ProxyServer {
@ -24,6 +26,17 @@ func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *Prox
if config == nil { if config == nil {
config = &tls.Config{} config = &tls.Config{}
} }
var cipher *ss.Cipher
if node.Protocol == "ss" && node.User != nil {
var err error
method := node.User.Username()
password, _ := node.User.Password()
cipher, err = ss.NewCipher(method, password)
if err != nil {
glog.Fatal(err)
}
}
return &ProxyServer{ return &ProxyServer{
Node: node, Node: node,
Chain: chain, Chain: chain,
@ -39,6 +52,7 @@ func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *Prox
user: node.User, user: node.User,
tlsConfig: config, tlsConfig: config,
}, },
cipher: cipher,
} }
} }
@ -59,13 +73,13 @@ func (s *ProxyServer) Serve() error {
server.Handler = http.HandlerFunc(server.HandleRequest) server.Handler = http.HandlerFunc(server.HandleRequest)
return server.ListenAndServeTLS(s.TLSConfig) return server.ListenAndServeTLS(s.TLSConfig)
case "tcp": // Local TCP port forwarding case "tcp": // Local TCP port forwarding
// return listenAndServeTcpForward(arg) return NewTcpForwardServer(s).ListenAndServe()
case "udp": // Local UDP port forwarding case "udp": // Local UDP port forwarding
// return listenAndServeUdpForward(arg) return NewUdpForwardServer(s).ListenAndServe()
case "rtcp": // Remote TCP port forwarding case "rtcp": // Remote TCP port forwarding
// return serveRTcpForward(arg) return NewRTcpForwardServer(s).Serve()
case "rudp": // Remote UDP port forwarding case "rudp": // Remote UDP port forwarding
// return serveRUdpForward(arg) return NewRUdpForwardServer(s).Serve()
default: default:
ln, err = net.Listen("tcp", node.Addr) ln, err = net.Listen("tcp", node.Addr)
} }
@ -94,7 +108,9 @@ func (s *ProxyServer) handleConn(conn net.Conn) {
switch s.Node.Protocol { switch s.Node.Protocol {
case "ss": // shadowsocks case "ss": // shadowsocks
NewShadowServer(conn, s).Serve() server := NewShadowServer(ss.NewConn(conn, s.cipher.Copy()), s)
server.OTA = s.Node.getBool("ota")
server.Serve()
return return
case "http": case "http":
req, err := http.ReadRequest(bufio.NewReader(conn)) req, err := http.ReadRequest(bufio.NewReader(conn))
@ -166,7 +182,7 @@ func (s *ProxyServer) handleConn(conn net.Conn) {
NewHttpServer(conn, s).HandleRequest(req) NewHttpServer(conn, s).HandleRequest(req)
} }
func (s *ProxyServer) transport(conn1, conn2 net.Conn) (err error) { func (_ *ProxyServer) transport(conn1, conn2 net.Conn) (err error) {
errc := make(chan error, 2) errc := make(chan error, 2)
go func() { go func() {

View File

@ -467,6 +467,7 @@ func (s *Socks5Server) bind(addr string) (net.Conn, error) {
pconn = c pconn = c
lnChan = nil lnChan = nil
ln = nil ln = nil
// TODO: implement deadline
s.conn.SetReadDeadline(time.Now()) // timeout right now ,so we can break out of blocking s.conn.SetReadDeadline(time.Now()) // timeout right now ,so we can break out of blocking
case err := <-peerChan: case err := <-peerChan:
if err != nil || pconn == nil { if err != nil || pconn == nil {

255
ss.go
View File

@ -1,134 +1,237 @@
package gost package gost
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"github.com/ginuerzh/gosocks5"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/shadowsocks/shadowsocks-go/shadowsocks" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
"io" "io"
"net" "net"
"strconv"
"time"
)
const (
idType = 0 // address type index
idIP0 = 1 // ip addres start index
idDmLen = 1 // domain address length index
idDm0 = 2 // domain address start index
typeIPv4 = 1 // type is ipv4 address
typeDm = 3 // type is domain address
typeIPv6 = 4 // type is ipv6 address
lenIPv4 = net.IPv4len + 2 // ipv4 + 2port
lenIPv6 = net.IPv6len + 2 // ipv6 + 2port
lenDmBase = 2 // 1addrLen + 2port, plus addrLen
lenHmacSha1 = 10
) )
type ShadowServer struct { type ShadowServer struct {
conn net.Conn conn *ss.Conn
Base *ProxyServer Base *ProxyServer
OTA bool // one time auth
} }
func NewShadowServer(conn net.Conn, base *ProxyServer) *ShadowServer { func NewShadowServer(conn *ss.Conn, base *ProxyServer) *ShadowServer {
return &ShadowServer{conn: conn, Base: base} return &ShadowServer{conn: conn, Base: base}
} }
func (s *ShadowServer) Serve() { func (s *ShadowServer) Serve() {
glog.V(LINFO).Infof("[ss] %s -> %s", s.conn.RemoteAddr(), s.conn.LocalAddr()) glog.V(LINFO).Infof("[ss] %s - %s", s.conn.RemoteAddr(), s.conn.LocalAddr())
var conn net.Conn addr, ota, err := s.getRequest()
if s.Base.Node.User != nil {
method := s.Base.Node.User.Username()
password, _ := s.Base.Node.User.Password()
cipher, err := shadowsocks.NewCipher(method, password)
if err != nil {
glog.V(LWARNING).Infof("[ss] %s - %s : %s", s.conn.RemoteAddr(), s.conn.LocalAddr(), err)
return
}
conn = shadowsocks.NewConn(s.conn, cipher)
}
addr, extra, err := getShadowRequest(conn)
if err != nil { if err != nil {
glog.V(LWARNING).Infof("[ss] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) glog.V(LWARNING).Infof("[ss] %s - %s : %s", s.conn.RemoteAddr(), s.conn.LocalAddr(), err)
return return
} }
glog.V(LINFO).Infof("[ss] %s -> %s", conn.RemoteAddr(), addr.String()) glog.V(LINFO).Infof("[ss] %s -> %s, ota: %v", s.conn.RemoteAddr(), addr, ota)
cc, err := s.Base.Chain.Dial(addr.String()) cc, err := s.Base.Chain.Dial(addr)
if err != nil { if err != nil {
glog.V(LWARNING).Infof("[ss] %s -> %s : %s", conn.RemoteAddr(), addr.String(), err) glog.V(LWARNING).Infof("[ss] %s -> %s : %s", s.conn.RemoteAddr(), addr, err)
return return
} }
defer cc.Close() defer cc.Close()
if extra != nil { glog.V(LINFO).Infof("[ss] %s <-> %s", s.conn.RemoteAddr(), addr)
if _, err := cc.Write(extra); err != nil { if ota {
glog.V(LWARNING).Infof("[ss] %s - %s : %s", conn.RemoteAddr(), addr.String(), err) s.transportOTA(s.conn, cc)
return } else {
} s.Base.transport(&shadowConn{conn: s.conn}, cc)
} }
glog.V(LINFO).Infof("[ss] %s >-< %s", s.conn.RemoteAddr(), addr)
glog.V(LINFO).Infof("[ss] %s <-> %s", conn.RemoteAddr(), addr.String())
s.Base.transport(conn, cc)
glog.V(LINFO).Infof("[ss] %s >-< %s", conn.RemoteAddr(), addr.String())
} }
func getShadowRequest(conn net.Conn) (addr *gosocks5.Addr, extra []byte, err error) { // This func are copied from shadowsocks library with some modification.
const ( func (s *ShadowServer) getRequest() (host string, ota bool, err error) {
idType = 0 // address type index
idIP0 = 1 // ip addres start index
idDmLen = 1 // domain address length index
idDm0 = 2 // domain address start index
typeIPv4 = 1 // type is ipv4 address
typeDm = 3 // type is domain address
typeIPv6 = 4 // type is ipv6 address
lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port
lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port
lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen
)
// buf size should at least have the same size with the largest possible // buf size should at least have the same size with the largest possible
// request size (when addrType is 3, domain name has at most 256 bytes) // request size (when addrType is 3, domain name has at most 256 bytes)
// 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) // 1(addrType) + 1(lenByte) + 256(max length address) + 2(port)
buf := make([]byte, SmallBufferSize) buf := make([]byte, SmallBufferSize)
var n int
// read till we get possible domain length field // read till we get possible domain length field
//shadowsocks.SetReadTimeout(conn) s.conn.SetReadDeadline(time.Now().Add(ReadTimeout))
if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { if _, err = io.ReadFull(s.conn, buf[:idType+1]); err != nil {
return return
} }
addr = &gosocks5.Addr{ var reqStart, reqEnd int
Type: buf[idType], addrType := buf[idType]
} switch addrType & ss.AddrMask {
reqLen := -1
switch buf[idType] {
case typeIPv4: case typeIPv4:
reqLen = lenIPv4 reqStart, reqEnd = idIP0, idIP0+lenIPv4
case typeIPv6: case typeIPv6:
reqLen = lenIPv6 reqStart, reqEnd = idIP0, idIP0+lenIPv6
case typeDm: case typeDm:
reqLen = int(buf[idDmLen]) + lenDmBase if _, err = io.ReadFull(s.conn, buf[idType+1:idDmLen+1]); err != nil {
default:
err = fmt.Errorf("addr type %d not supported", buf[idType])
return
}
if n < reqLen { // rare case
//ss.SetReadTimeout(conn)
if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil {
return return
} }
} else if n > reqLen { reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase)
// it's possible to read more than just the request head default:
extra = buf[reqLen:n] err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask)
return
}
if _, err = io.ReadFull(s.conn, buf[reqStart:reqEnd]); err != nil {
return
} }
// Return string for typeIP is not most efficient, but browsers (Chrome, // Return string for typeIP is not most efficient, but browsers (Chrome,
// Safari, Firefox) all seems using typeDm exclusively. So this is not a // Safari, Firefox) all seems using typeDm exclusively. So this is not a
// big problem. // big problem.
switch buf[idType] { switch addrType & ss.AddrMask {
case typeIPv4: case typeIPv4:
addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String()
case typeIPv6: case typeIPv6:
addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String()
case typeDm: case typeDm:
addr.Host = string(buf[idDm0 : idDm0+buf[idDmLen]]) host = string(buf[idDm0 : idDm0+buf[idDmLen]])
} }
// parse port // parse port
addr.Port = binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd])
host = net.JoinHostPort(host, strconv.Itoa(int(port)))
// if specified one time auth enabled, we should verify this
if s.OTA || addrType&ss.OneTimeAuthMask > 0 {
ota = true
if _, err = io.ReadFull(s.conn, buf[reqEnd:reqEnd+lenHmacSha1]); err != nil {
return
}
iv := s.conn.GetIv()
key := s.conn.GetKey()
actualHmacSha1Buf := ss.HmacSha1(append(iv, key...), buf[:reqEnd])
if !bytes.Equal(buf[reqEnd:reqEnd+lenHmacSha1], actualHmacSha1Buf) {
err = fmt.Errorf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, buf[:reqEnd])
return
}
}
return
}
const (
dataLenLen = 2
hmacSha1Len = 10
idxData0 = dataLenLen + hmacSha1Len
)
// copyOta copies data from src to dst with ota verification.
//
// This func are copied from shadowsocks library with some modification.
func (s *ShadowServer) copyOta(dst net.Conn, src *ss.Conn) (int64, error) {
// sometimes it have to fill large block
buf := make([]byte, LargeBufferSize)
for {
src.SetReadDeadline(time.Now().Add(ReadTimeout))
if n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]); err != nil {
return int64(n), err
}
src.SetReadDeadline(time.Time{})
dataLen := binary.BigEndian.Uint16(buf[:dataLenLen])
expectedHmacSha1 := buf[dataLenLen:idxData0]
var dataBuf []byte
if len(buf) < int(idxData0+dataLen) {
dataBuf = make([]byte, dataLen)
} else {
dataBuf = buf[idxData0 : idxData0+dataLen]
}
if n, err := io.ReadFull(src, dataBuf); err != nil {
return int64(n), err
}
chunkIdBytes := make([]byte, 4)
chunkId := src.GetAndIncrChunkId()
binary.BigEndian.PutUint32(chunkIdBytes, chunkId)
actualHmacSha1 := ss.HmacSha1(append(src.GetIv(), chunkIdBytes...), dataBuf)
if !bytes.Equal(expectedHmacSha1, actualHmacSha1) {
return 0, errors.New("ota error: mismatch")
}
if n, err := dst.Write(dataBuf); err != nil {
return int64(n), err
}
}
}
func (s *ShadowServer) transportOTA(sc *ss.Conn, cc net.Conn) (err error) {
errc := make(chan error, 2)
go func() {
_, err := io.Copy(&shadowConn{conn: sc}, cc)
errc <- err
}()
go func() {
_, err := s.copyOta(cc, sc)
errc <- err
}()
select {
case err = <-errc:
//glog.V(LWARNING).Infoln("transport exit", err)
}
return return
} }
// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,
// we wrap around it to make io.Copy happy
type shadowConn struct {
conn *ss.Conn
}
func (c *shadowConn) Read(b []byte) (n int, err error) {
return c.conn.Read(b)
}
func (c *shadowConn) Write(b []byte) (n int, err error) {
n = len(b) // force byte length consistent
_, err = c.conn.Write(b)
return
}
func (c *shadowConn) Close() error {
return c.conn.Close()
}
func (c *shadowConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *shadowConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *shadowConn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}
func (c *shadowConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *shadowConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}