mirror of https://github.com/ginuerzh/gost
12 changed files with 1494 additions and 130 deletions
@ -0,0 +1,53 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"crypto/tls" |
|||
"flag" |
|||
"fmt" |
|||
"github.com/ginuerzh/gost" |
|||
"log" |
|||
"sync" |
|||
) |
|||
|
|||
var ( |
|||
proxyNodes stringlist |
|||
) |
|||
|
|||
func init() { |
|||
flag.Var(&proxyNodes, "L", "proxy server node") |
|||
flag.Parse() |
|||
} |
|||
|
|||
type stringlist []string |
|||
|
|||
func (list *stringlist) String() string { |
|||
return fmt.Sprintf("%s", *list) |
|||
} |
|||
func (list *stringlist) Set(value string) error { |
|||
*list = append(*list, value) |
|||
return nil |
|||
} |
|||
|
|||
func main() { |
|||
chain := gost.NewProxyChain() |
|||
var wg sync.WaitGroup |
|||
for _, ns := range proxyNodes { |
|||
serverNode, err := gost.ParseProxyNode(ns) |
|||
if err != nil { |
|||
log.Println(err) |
|||
continue |
|||
} |
|||
wg.Add(1) |
|||
go func(node gost.ProxyNode) { |
|||
defer wg.Done() |
|||
cert, err := gost.LoadCertificate(node.Get("cert"), node.Get("key")) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
server := gost.NewProxyServer(node, chain, &tls.Config{Certificates: []tls.Certificate{cert}}) |
|||
log.Fatal(server.Serve()) |
|||
}(serverNode) |
|||
} |
|||
wg.Wait() |
|||
} |
|||
@ -1,58 +1,318 @@ |
|||
package gost |
|||
|
|||
import ( |
|||
//"bufio"
|
|||
//"crypto/tls"
|
|||
//"encoding/base64"
|
|||
//"github.com/golang/glog"
|
|||
//"golang.org/x/net/http2"
|
|||
"bufio" |
|||
"crypto/tls" |
|||
"github.com/golang/glog" |
|||
"golang.org/x/net/http2" |
|||
"io" |
|||
"net" |
|||
//"net/http"
|
|||
//"net/http/httputil"
|
|||
"net/http" |
|||
"net/http/httputil" |
|||
//"encoding/base64"
|
|||
//"strings"
|
|||
"errors" |
|||
"time" |
|||
) |
|||
|
|||
// http2 client connection, wrapped up just like a net.Conn
|
|||
type Http2ClientConn struct { |
|||
r io.Reader |
|||
w io.Writer |
|||
localAddr net.Addr |
|||
remoteAddr net.Addr |
|||
type HttpServer struct { |
|||
conn net.Conn |
|||
Base *ProxyServer |
|||
} |
|||
|
|||
func NewHttpServer(conn net.Conn, base *ProxyServer) *HttpServer { |
|||
return &HttpServer{ |
|||
conn: conn, |
|||
Base: base, |
|||
} |
|||
} |
|||
|
|||
// Default HTTP server handler
|
|||
func (s *HttpServer) HandleRequest(req *http.Request) { |
|||
glog.V(LINFO).Infof("[http] %s %s - %s %s", req.Method, s.conn.RemoteAddr(), req.Host, req.Proto) |
|||
|
|||
if glog.V(LDEBUG) { |
|||
dump, _ := httputil.DumpRequest(req, false) |
|||
glog.Infoln(string(dump)) |
|||
} |
|||
|
|||
if req.Method == "PRI" && req.ProtoMajor == 2 { |
|||
glog.V(LWARNING).Infof("[http] %s <- %s : Not an HTTP2 server", s.conn.RemoteAddr(), req.Host) |
|||
resp := "HTTP/1.1 400 Bad Request\r\n" + |
|||
"Proxy-Agent: gost/" + Version + "\r\n\r\n" |
|||
s.conn.Write([]byte(resp)) |
|||
return |
|||
} |
|||
|
|||
var username, password string |
|||
if s.Base.Node.User != nil { |
|||
username = s.Base.Node.User.Username() |
|||
password, _ = s.Base.Node.User.Password() |
|||
} |
|||
|
|||
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization")) |
|||
req.Header.Del("Proxy-Authorization") |
|||
if (username != "" && u != username) || (password != "" && p != password) { |
|||
glog.V(LWARNING).Infof("[http] %s <- %s : proxy authentication required", s.conn.RemoteAddr(), req.Host) |
|||
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" |
|||
s.conn.Write([]byte(resp)) |
|||
return |
|||
} |
|||
|
|||
// TODO: forward http request
|
|||
/* |
|||
if len(forwardArgs) > 0 { |
|||
last := forwardArgs[len(forwardArgs)-1] |
|||
if last.Protocol == "http" || last.Protocol == "" { |
|||
forwardHttpRequest(req, conn, arg) |
|||
return |
|||
} |
|||
} |
|||
*/ |
|||
|
|||
c, err := s.Base.Chain.Dial(req.Host) |
|||
if err != nil { |
|||
glog.V(LWARNING).Infof("[http] %s -> %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", s.conn.RemoteAddr(), req.Host, string(b)) |
|||
s.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", s.conn.RemoteAddr(), req.Host, string(b)) |
|||
s.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", s.conn.RemoteAddr(), req.Host, err) |
|||
return |
|||
} |
|||
} |
|||
|
|||
glog.V(LINFO).Infof("[http] %s <-> %s", s.conn.RemoteAddr(), req.Host) |
|||
s.Base.transport(s.conn, c) |
|||
glog.V(LINFO).Infof("[http] %s >-< %s", s.conn.RemoteAddr(), req.Host) |
|||
} |
|||
|
|||
type Http2Server struct { |
|||
Base *ProxyServer |
|||
Handler http.Handler |
|||
TLSConfig *tls.Config |
|||
} |
|||
|
|||
func NewHttp2Server(base *ProxyServer) *Http2Server { |
|||
return &Http2Server{Base: base} |
|||
} |
|||
|
|||
func (s *Http2Server) ListenAndServeTLS(config *tls.Config) error { |
|||
srv := http.Server{ |
|||
Addr: s.Base.Node.Addr, |
|||
Handler: s.Handler, |
|||
TLSConfig: config, |
|||
} |
|||
if srv.Handler == nil { |
|||
srv.Handler = http.HandlerFunc(s.HandleRequest) |
|||
} |
|||
http2.ConfigureServer(&srv, nil) |
|||
return srv.ListenAndServeTLS("", "") |
|||
} |
|||
|
|||
// Default HTTP2 server handler
|
|||
func (s *Http2Server) HandleRequest(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)) |
|||
} |
|||
|
|||
w.Header().Set("Proxy-Agent", "gost/"+Version) |
|||
|
|||
// HTTP2 as transport
|
|||
if req.Header.Get("Proxy-Switch") == "gost" { |
|||
conn, err := s.Upgrade(w, req) |
|||
if err != nil { |
|||
glog.V(LINFO).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err) |
|||
return |
|||
} |
|||
s.Base.handleConn(conn) |
|||
return |
|||
} |
|||
|
|||
var username, password string |
|||
if s.Base.Node.User != nil { |
|||
username = s.Base.Node.User.Username() |
|||
password, _ = s.Base.Node.User.Password() |
|||
} |
|||
|
|||
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization")) |
|||
req.Header.Del("Proxy-Authorization") |
|||
if (username != "" && u != username) || (password != "" && p != password) { |
|||
glog.V(LWARNING).Infof("[http2] %s <- %s : proxy authentication required", req.RemoteAddr, target) |
|||
w.WriteHeader(http.StatusProxyAuthRequired) |
|||
return |
|||
} |
|||
|
|||
c, err := s.Base.Chain.Dial(target) |
|||
if err != nil { |
|||
glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err) |
|||
w.WriteHeader(http.StatusServiceUnavailable) |
|||
return |
|||
} |
|||
defer c.Close() |
|||
|
|||
glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, target) |
|||
|
|||
if req.Method == http.MethodConnect { |
|||
w.WriteHeader(http.StatusOK) |
|||
if fw, ok := w.(http.Flusher); ok { |
|||
fw.Flush() |
|||
} |
|||
|
|||
// compatible with HTTP1.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) |
|||
w.WriteHeader(http.StatusInternalServerError) |
|||
return |
|||
} |
|||
defer conn.Close() |
|||
|
|||
s.Base.transport(conn, c) |
|||
return |
|||
} |
|||
|
|||
errc := make(chan error, 2) |
|||
|
|||
go func() { |
|||
_, err := io.Copy(c, req.Body) |
|||
errc <- err |
|||
}() |
|||
go func() { |
|||
_, err := io.Copy(flushWriter{w}, c) |
|||
errc <- err |
|||
}() |
|||
|
|||
select { |
|||
case <-errc: |
|||
// glog.V(LWARNING).Infoln("exit", err)
|
|||
} |
|||
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target) |
|||
return |
|||
} |
|||
|
|||
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 _, 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) |
|||
} |
|||
|
|||
// Upgrade upgrade an HTTP2 request to a bidirectional connection that preparing for tunneling other protocol, just like a websocket connection.
|
|||
func (s *Http2Server) Upgrade(w http.ResponseWriter, r *http.Request) (net.Conn, error) { |
|||
w.Header().Set("Proxy-Agent", "gost/"+Version) |
|||
|
|||
if r.Method != http.MethodConnect { |
|||
w.WriteHeader(http.StatusMethodNotAllowed) |
|||
return nil, errors.New("Method not allowed") |
|||
} |
|||
|
|||
w.WriteHeader(http.StatusOK) |
|||
|
|||
if fw, ok := w.(http.Flusher); ok { |
|||
fw.Flush() |
|||
} |
|||
|
|||
return &http2Conn{r: r.Body, w: flushWriter{w}}, nil |
|||
} |
|||
|
|||
func (c *Http2ClientConn) Read(b []byte) (n int, err error) { |
|||
// HTTP2 client connection, wrapped up just like a net.Conn
|
|||
type http2Conn struct { |
|||
r io.Reader |
|||
w io.Writer |
|||
} |
|||
|
|||
func (c *http2Conn) Read(b []byte) (n int, err error) { |
|||
return c.r.Read(b) |
|||
} |
|||
|
|||
func (c *Http2ClientConn) Write(b []byte) (n int, err error) { |
|||
func (c *http2Conn) Write(b []byte) (n int, err error) { |
|||
return c.w.Write(b) |
|||
} |
|||
|
|||
func (c *Http2ClientConn) Close() error { |
|||
func (c *http2Conn) 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 *http2Conn) LocalAddr() net.Addr { |
|||
return nil |
|||
} |
|||
|
|||
func (c *Http2ClientConn) RemoteAddr() net.Addr { |
|||
return c.remoteAddr |
|||
func (c *http2Conn) RemoteAddr() net.Addr { |
|||
return nil |
|||
} |
|||
|
|||
func (c *Http2ClientConn) SetDeadline(t time.Time) error { |
|||
func (c *http2Conn) SetDeadline(t time.Time) error { |
|||
return nil |
|||
} |
|||
|
|||
func (c *Http2ClientConn) SetReadDeadline(t time.Time) error { |
|||
func (c *http2Conn) SetReadDeadline(t time.Time) error { |
|||
return nil |
|||
} |
|||
|
|||
func (c *Http2ClientConn) SetWriteDeadline(t time.Time) error { |
|||
func (c *http2Conn) SetWriteDeadline(t time.Time) error { |
|||
return nil |
|||
} |
|||
|
|||
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) |
|||
return |
|||
} |
|||
if f, ok := fw.w.(http.Flusher); ok { |
|||
f.Flush() |
|||
} |
|||
return |
|||
} |
|||
|
|||
@ -0,0 +1,203 @@ |
|||
package gost |
|||
|
|||
import ( |
|||
"bufio" |
|||
"crypto/tls" |
|||
"github.com/ginuerzh/gosocks5" |
|||
"github.com/golang/glog" |
|||
"io" |
|||
"net" |
|||
"net/http" |
|||
) |
|||
|
|||
type ProxyServer struct { |
|||
Node ProxyNode |
|||
Chain *ProxyChain |
|||
TLSConfig *tls.Config |
|||
selector *serverSelector |
|||
} |
|||
|
|||
func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *ProxyServer { |
|||
if chain == nil { |
|||
chain = NewProxyChain() |
|||
} |
|||
if config == nil { |
|||
config = &tls.Config{} |
|||
} |
|||
return &ProxyServer{ |
|||
Node: node, |
|||
Chain: chain, |
|||
TLSConfig: config, |
|||
selector: &serverSelector{ // socks5 server selector
|
|||
// methods that socks5 server supported
|
|||
methods: []uint8{ |
|||
gosocks5.MethodNoAuth, |
|||
gosocks5.MethodUserPass, |
|||
MethodTLS, |
|||
MethodTLSAuth, |
|||
}, |
|||
user: node.User, |
|||
tlsConfig: config, |
|||
}, |
|||
} |
|||
} |
|||
|
|||
func (s *ProxyServer) Serve() error { |
|||
var ln net.Listener |
|||
var err error |
|||
node := s.Node |
|||
|
|||
switch node.Transport { |
|||
case "ws": // websocket connection
|
|||
return NewWebsocketServer(s).ListenAndServe() |
|||
case "wss": // websocket security connection
|
|||
return NewWebsocketServer(s).ListenAndServeTLS(s.TLSConfig) |
|||
case "tls": // tls connection
|
|||
ln, err = tls.Listen("tcp", node.Addr, s.TLSConfig) |
|||
case "http2": // Standard HTTP2 proxy server, compatible with HTTP1.x.
|
|||
server := NewHttp2Server(s) |
|||
server.Handler = http.HandlerFunc(server.HandleRequest) |
|||
return server.ListenAndServeTLS(s.TLSConfig) |
|||
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", node.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 s.handleConn(conn) |
|||
} |
|||
} |
|||
|
|||
func (s *ProxyServer) handleConn(conn net.Conn) { |
|||
defer conn.Close() |
|||
|
|||
switch s.Node.Protocol { |
|||
case "ss": // shadowsocks
|
|||
NewShadowServer(conn, s).Serve() |
|||
return |
|||
case "http": |
|||
req, err := http.ReadRequest(bufio.NewReader(conn)) |
|||
if err != nil { |
|||
glog.V(LWARNING).Infoln("[http]", err) |
|||
return |
|||
} |
|||
NewHttpServer(conn, s).HandleRequest(req) |
|||
return |
|||
case "socks", "socks5": |
|||
conn = gosocks5.ServerConn(conn, s.selector) |
|||
req, err := gosocks5.ReadRequest(conn) |
|||
if err != nil { |
|||
glog.V(LWARNING).Infoln("[socks5]", err) |
|||
return |
|||
} |
|||
NewSocks5Server(conn, s).HandleRequest(req) |
|||
return |
|||
} |
|||
|
|||
glog.V(LINFO).Infof("%s - %s", conn.RemoteAddr(), s.Node.Addr) |
|||
// http or socks5
|
|||
b := make([]byte, MediumBufferSize) |
|||
|
|||
n, err := io.ReadAtLeast(conn, b, 2) |
|||
if err != nil { |
|||
glog.V(LWARNING).Infoln(err) |
|||
return |
|||
} |
|||
|
|||
// TODO: use bufio.Reader
|
|||
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 |
|||
} |
|||
} |
|||
// TODO: use gosocks5.ServerConn
|
|||
methods := b[2 : 2+mn] |
|||
method := s.selector.Select(methods...) |
|||
if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil { |
|||
glog.V(LWARNING).Infoln("[socks5] select:", err) |
|||
return |
|||
} |
|||
c, err := s.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 |
|||
} |
|||
NewSocks5Server(conn, s).HandleRequest(req) |
|||
return |
|||
} |
|||
|
|||
req, err := http.ReadRequest(bufio.NewReader(&reqReader{b: b[:n], r: conn})) |
|||
if err != nil { |
|||
glog.V(LWARNING).Infoln("[http]", err) |
|||
return |
|||
} |
|||
NewHttpServer(conn, s).HandleRequest(req) |
|||
} |
|||
|
|||
func (s *ProxyServer) transport(conn1, conn2 net.Conn) (err error) { |
|||
errc := make(chan error, 2) |
|||
|
|||
go func() { |
|||
_, err := io.Copy(conn1, conn2) |
|||
errc <- err |
|||
}() |
|||
|
|||
go func() { |
|||
_, err := io.Copy(conn2, conn1) |
|||
errc <- err |
|||
}() |
|||
|
|||
select { |
|||
case err = <-errc: |
|||
//glog.V(LWARNING).Infoln("transport exit", err)
|
|||
} |
|||
|
|||
return |
|||
} |
|||
|
|||
type reqReader struct { |
|||
b []byte |
|||
r io.Reader |
|||
} |
|||
|
|||
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 |
|||
} |
|||
@ -0,0 +1,134 @@ |
|||
package gost |
|||
|
|||
import ( |
|||
"encoding/binary" |
|||
"fmt" |
|||
"github.com/ginuerzh/gosocks5" |
|||
"github.com/golang/glog" |
|||
"github.com/shadowsocks/shadowsocks-go/shadowsocks" |
|||
"io" |
|||
"net" |
|||
) |
|||
|
|||
type ShadowServer struct { |
|||
conn net.Conn |
|||
Base *ProxyServer |
|||
} |
|||
|
|||
func NewShadowServer(conn net.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) |
|||
} |
|||
|
|||
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()) |
|||
|
|||
cc, err := s.Base.Chain.Dial(addr.String()) |
|||
if err != nil { |
|||
glog.V(LWARNING).Infof("[ss] %s -> %s : %s", conn.RemoteAddr(), addr.String(), 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", 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) { |
|||
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, 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 { |
|||
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 |
|||
} |
|||
Loading…
Reference in new issue