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 |
package gost |
||||
|
|
||||
import ( |
import ( |
||||
//"bufio"
|
"bufio" |
||||
//"crypto/tls"
|
"crypto/tls" |
||||
//"encoding/base64"
|
"github.com/golang/glog" |
||||
//"github.com/golang/glog"
|
"golang.org/x/net/http2" |
||||
//"golang.org/x/net/http2"
|
|
||||
"io" |
"io" |
||||
"net" |
"net" |
||||
//"net/http"
|
"net/http" |
||||
//"net/http/httputil"
|
"net/http/httputil" |
||||
|
//"encoding/base64"
|
||||
//"strings"
|
//"strings"
|
||||
|
"errors" |
||||
"time" |
"time" |
||||
) |
) |
||||
|
|
||||
// http2 client connection, wrapped up just like a net.Conn
|
type HttpServer struct { |
||||
type Http2ClientConn struct { |
conn net.Conn |
||||
r io.Reader |
Base *ProxyServer |
||||
w io.Writer |
} |
||||
localAddr net.Addr |
|
||||
remoteAddr net.Addr |
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) |
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) |
return c.w.Write(b) |
||||
} |
} |
||||
|
|
||||
func (c *Http2ClientConn) Close() error { |
func (c *http2Conn) Close() error { |
||||
if rc, ok := c.r.(io.ReadCloser); ok { |
if rc, ok := c.r.(io.ReadCloser); ok { |
||||
return rc.Close() |
return rc.Close() |
||||
} |
} |
||||
return nil |
return nil |
||||
} |
} |
||||
|
|
||||
func (c *Http2ClientConn) LocalAddr() net.Addr { |
func (c *http2Conn) LocalAddr() net.Addr { |
||||
return c.localAddr |
return nil |
||||
} |
} |
||||
|
|
||||
func (c *Http2ClientConn) RemoteAddr() net.Addr { |
func (c *http2Conn) RemoteAddr() net.Addr { |
||||
return c.remoteAddr |
return nil |
||||
} |
} |
||||
|
|
||||
func (c *Http2ClientConn) SetDeadline(t time.Time) error { |
func (c *http2Conn) SetDeadline(t time.Time) error { |
||||
return nil |
return nil |
||||
} |
} |
||||
|
|
||||
func (c *Http2ClientConn) SetReadDeadline(t time.Time) error { |
func (c *http2Conn) SetReadDeadline(t time.Time) error { |
||||
return nil |
return nil |
||||
} |
} |
||||
|
|
||||
func (c *Http2ClientConn) SetWriteDeadline(t time.Time) error { |
func (c *http2Conn) SetWriteDeadline(t time.Time) error { |
||||
return nil |
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