mirror of https://github.com/ginuerzh/gost
20 changed files with 536 additions and 2469 deletions
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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() |
|||
} |
|||
@ -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), |
|||
} |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
) |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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("", "") |
|||
} |
|||
@ -1,134 +1,237 @@ |
|||
package gost |
|||
|
|||
import ( |
|||
"bytes" |
|||
"encoding/binary" |
|||
"errors" |
|||
"fmt" |
|||
"github.com/ginuerzh/gosocks5" |
|||
"github.com/golang/glog" |
|||
"github.com/shadowsocks/shadowsocks-go/shadowsocks" |
|||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" |
|||
"io" |
|||
"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 { |
|||
conn net.Conn |
|||
conn *ss.Conn |
|||
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} |
|||
} |
|||
|
|||
func (s *ShadowServer) Serve() { |
|||
glog.V(LINFO).Infof("[ss] %s -> %s", s.conn.RemoteAddr(), s.conn.LocalAddr()) |
|||
|
|||
var conn net.Conn |
|||
|
|||
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) |
|||
} |
|||
glog.V(LINFO).Infof("[ss] %s - %s", s.conn.RemoteAddr(), s.conn.LocalAddr()) |
|||
|
|||
addr, extra, err := getShadowRequest(conn) |
|||
addr, ota, err := s.getRequest() |
|||
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 |
|||
} |
|||
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 { |
|||
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 |
|||
} |
|||
defer cc.Close() |
|||
|
|||
if extra != nil { |
|||
if _, err := cc.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", s.conn.RemoteAddr(), addr) |
|||
if ota { |
|||
s.transportOTA(s.conn, cc) |
|||
} else { |
|||
s.Base.transport(&shadowConn{conn: s.conn}, cc) |
|||
} |
|||
|
|||
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()) |
|||
glog.V(LINFO).Infof("[ss] %s >-< %s", s.conn.RemoteAddr(), addr) |
|||
} |
|||
|
|||
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
|
|||
) |
|||
|
|||
// This func are copied from shadowsocks library with some modification.
|
|||
func (s *ShadowServer) getRequest() (host string, ota bool, err error) { |
|||
// 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, SmallBufferSize) |
|||
|
|||
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 { |
|||
s.conn.SetReadDeadline(time.Now().Add(ReadTimeout)) |
|||
if _, err = io.ReadFull(s.conn, buf[:idType+1]); err != nil { |
|||
return |
|||
} |
|||
|
|||
addr = &gosocks5.Addr{ |
|||
Type: buf[idType], |
|||
} |
|||
|
|||
reqLen := -1 |
|||
switch buf[idType] { |
|||
var reqStart, reqEnd int |
|||
addrType := buf[idType] |
|||
switch addrType & ss.AddrMask { |
|||
case typeIPv4: |
|||
reqLen = lenIPv4 |
|||
reqStart, reqEnd = idIP0, idIP0+lenIPv4 |
|||
case typeIPv6: |
|||
reqLen = lenIPv6 |
|||
reqStart, reqEnd = idIP0, idIP0+lenIPv6 |
|||
case typeDm: |
|||
reqLen = int(buf[idDmLen]) + lenDmBase |
|||
if _, err = io.ReadFull(s.conn, buf[idType+1:idDmLen+1]); err != nil { |
|||
return |
|||
} |
|||
reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase) |
|||
default: |
|||
err = fmt.Errorf("addr type %d not supported", buf[idType]) |
|||
err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask) |
|||
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] |
|||
if _, err = io.ReadFull(s.conn, buf[reqStart:reqEnd]); err != nil { |
|||
return |
|||
} |
|||
|
|||
// 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] { |
|||
switch addrType & ss.AddrMask { |
|||
case typeIPv4: |
|||
addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() |
|||
host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() |
|||
case typeIPv6: |
|||
addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() |
|||
host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() |
|||
case typeDm: |
|||
addr.Host = string(buf[idDm0 : idDm0+buf[idDmLen]]) |
|||
host = string(buf[idDm0 : idDm0+buf[idDmLen]]) |
|||
} |
|||
// 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 |
|||
} |
|||
|
|||
// 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) |
|||
} |
|||
|
|||
Loading…
Reference in new issue