update gost library

This commit is contained in:
rui.zheng 2016-10-04 16:41:44 +08:00
parent a15bf73742
commit f903771282
12 changed files with 1498 additions and 134 deletions

View File

@ -32,11 +32,26 @@ func (c *ProxyChain) AddProxyNode(node ...ProxyNode) {
c.nodes = append(c.nodes, node...)
}
// Initialize proxy nodes, mainly check for http2 feature.
// Should be called immediately when proxy nodes are ready.
func (c *ProxyChain) AddProxyNodeString(snode ...string) error {
for _, sn := range snode {
node, err := ParseProxyNode(sn)
if err != nil {
return err
}
c.AddProxyNode(node)
}
return nil
}
func (c *ProxyChain) Nodes() []ProxyNode {
return c.nodes
}
// TryEnableHttp2 initialize HTTP2 if available.
// HTTP2 will be enabled when at least one HTTP2 proxy node (scheme == http2) is present.
//
// NOTE: http2 will not be enabled if not called.
func (c *ProxyChain) Init() {
// NOTE: Should be called immediately when proxy nodes are ready, HTTP2 will not be enabled if this function not be called.
func (c *ProxyChain) TryEnableHttp2() {
length := len(c.nodes)
if length == 0 {
return
@ -44,7 +59,7 @@ func (c *ProxyChain) Init() {
c.lastNode = &c.nodes[length-1]
// http2 restrict: http2 will be enabled when at least one http2 proxy node present
// HTTP2 restrict: HTTP2 will be enabled when at least one HTTP2 proxy node is present.
for i, node := range c.nodes {
if node.Transport == "http2" {
glog.V(LINFO).Infoln("http2 enabled")
@ -54,11 +69,15 @@ func (c *ProxyChain) Init() {
}
c.initHttp2Client(node.Addr, cfg, c.nodes[:i]...)
c.http2NodeIndex = i
break // shortest chain for http2
break // shortest chain for HTTP2
}
}
}
func (c *ProxyChain) Http2Enabled() bool {
return c.http2Enabled
}
func (c *ProxyChain) initHttp2Client(addr string, config *tls.Config, nodes ...ProxyNode) {
tr := http2.Transport{
TLSClientConfig: config,
@ -76,10 +95,6 @@ func (c *ProxyChain) initHttp2Client(addr string, config *tls.Config, nodes ...P
}
func (c *ProxyChain) Http2Enabled() bool {
return c.http2Enabled
}
// Connect to addr through proxy chain
func (c *ProxyChain) Dial(addr string) (net.Conn, error) {
if !strings.Contains(addr, ":") {
@ -88,20 +103,35 @@ func (c *ProxyChain) Dial(addr string) (net.Conn, error) {
return c.dialWithNodes(addr, c.nodes...)
}
// GetConn initializes a proxy chain connection,
// if no proxy nodes on this chain, it will return error
func (c *ProxyChain) GetConn() (net.Conn, error) {
nodes := c.nodes
if len(nodes) == 0 {
return nil, ErrEmptyChain
}
if c.Http2Enabled() {
nodes = nodes[c.http2NodeIndex+1:]
if len(nodes) == 0 {
return c.getHttp2Conn()
}
}
return c.travelNodes(nodes...)
}
func (c *ProxyChain) dialWithNodes(addr string, nodes ...ProxyNode) (conn net.Conn, err error) {
if len(nodes) == 0 {
return net.DialTimeout("tcp", addr, DialTimeout)
}
var pc *ProxyConn
if c.Http2Enabled() {
nodes = nodes[c.http2NodeIndex+1:]
if len(nodes) == 0 {
return c.http2Connect("http", addr)
return c.http2Connect(addr)
}
}
pc, err = c.travelNodes(nodes...)
pc, err := c.travelNodes(nodes...)
if err != nil {
return
}
@ -109,8 +139,8 @@ func (c *ProxyChain) dialWithNodes(addr string, nodes ...ProxyNode) (conn net.Co
pc.Close()
return
}
return pc, nil
conn = pc
return
}
func (c *ProxyChain) travelNodes(nodes ...ProxyNode) (conn *ProxyConn, err error) {
@ -125,7 +155,7 @@ func (c *ProxyChain) travelNodes(nodes ...ProxyNode) (conn *ProxyConn, err error
node := nodes[0]
if c.Http2Enabled() {
cc, err = c.http2Connect("http", node.Addr)
cc, err = c.http2Connect(node.Addr)
} else {
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
}
@ -152,13 +182,14 @@ func (c *ProxyChain) travelNodes(nodes ...ProxyNode) (conn *ProxyConn, err error
return
}
func (c *ProxyChain) http2Connect(protocol, addr string) (net.Conn, error) {
// Initialize an HTTP2 transport if HTTP2 is enabled.
func (c *ProxyChain) getHttp2Conn() (net.Conn, error) {
if !c.Http2Enabled() {
return nil, errors.New("http2 not enabled")
return nil, errors.New("HTTP2 not enabled")
}
http2Node := c.nodes[c.http2NodeIndex]
pr, pw := io.Pipe()
req := http.Request{
Method: http.MethodConnect,
URL: &url.URL{Scheme: "https", Host: http2Node.Addr},
@ -170,11 +201,7 @@ func (c *ProxyChain) http2Connect(protocol, addr string) (net.Conn, error) {
Host: http2Node.Addr,
ContentLength: -1,
}
req.Header.Set("gost-target", addr)
if protocol != "" {
req.Header.Set("gost-protocol", protocol)
}
req.Header.Set("Proxy-Switch", "gost") // Flag header to indicate server to switch to HTTP2 transport mode
if glog.V(LDEBUG) {
dump, _ := httputil.DumpRequest(&req, false)
glog.Infoln(string(dump))
@ -187,7 +214,19 @@ func (c *ProxyChain) http2Connect(protocol, addr string) (net.Conn, error) {
resp.Body.Close()
return nil, errors.New(resp.Status)
}
conn := &Http2ClientConn{r: resp.Body, w: pw}
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", addr)
return &http2Conn{r: resp.Body, w: pw}, nil
}
// Use HTTP2 as transport to connect target addr
func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) {
conn, err := c.getHttp2Conn()
if err != nil {
return nil, err
}
pc := NewProxyConn(conn, c.nodes[c.http2NodeIndex])
if err = pc.Connect(addr); err != nil {
pc.Close()
return nil, err
}
return conn, nil
}

View File

@ -263,8 +263,6 @@ func handlerHttp2Request(w http.ResponseWriter, req *http.Request) {
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target)
}
//func processSocks5OverHttp2()
func handleHttp2Transport(w http.ResponseWriter, req *http.Request) {
glog.V(LINFO).Infof("[http2] %s - %s", req.RemoteAddr, req.Host)
if glog.V(LDEBUG) {

View File

@ -25,7 +25,7 @@ func init() {
log.Fatal("please specific at least one request URL")
}
urls = flag.Args()
if glog.V(gost.LVDEBUG) {
if glog.V(5) {
http2.VerboseLogs = true
}
}
@ -42,14 +42,10 @@ func (list *stringlist) Set(value string) error {
func main() {
chain := gost.NewProxyChain()
for _, s := range proxyNodes {
node, err := gost.ParseProxyNode(s)
if err != nil {
log.Fatal(err)
}
chain.AddProxyNode(*node)
if err := chain.AddProxyNodeString(proxyNodes...); err != nil {
log.Fatal(err)
}
chain.Init()
chain.TryEnableHttp2()
for _, u := range urls {
url, err := url.Parse(u)

53
cmd/tools/gost_server.go Normal file
View File

@ -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()
}

46
conn.go
View File

@ -20,7 +20,7 @@ import (
type ProxyConn struct {
conn net.Conn
node ProxyNode
Node ProxyNode
handshaked bool
handshakeMutex sync.Mutex
handshakeErr error
@ -29,13 +29,13 @@ type ProxyConn struct {
func NewProxyConn(conn net.Conn, node ProxyNode) *ProxyConn {
return &ProxyConn{
conn: conn,
node: node,
Node: node,
}
}
// Handshake based on the proxy node info: transport, protocol, authentication, etc.
// Handshake handshake with this proxy node based on the proxy node info: transport, protocol, authentication, etc.
//
// NOTE: http2 will be downgrade to http (for protocol) and tls (for transport).
// NOTE: any HTTP2 scheme will be treated as http (for protocol) or tls (for transport).
func (c *ProxyConn) Handshake() error {
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
@ -53,16 +53,22 @@ func (c *ProxyConn) Handshake() error {
func (c *ProxyConn) handshake() error {
var tlsUsed bool
switch c.node.Transport {
switch c.Node.Transport {
case "ws": // websocket connection
conn, err := wsClient("ws", c.conn, c.node.Addr)
u := url.URL{Scheme: "ws", Host: c.Node.Addr, Path: "/ws"}
conn, err := WebsocketClientConn(u.String(), c.conn, nil)
if err != nil {
return err
}
c.conn = conn
case "wss": // websocket security
tlsUsed = true
conn, err := wsClient("wss", c.conn, c.node.Addr)
u := url.URL{Scheme: "wss", Host: c.Node.Addr, Path: "/ws"}
config := &tls.Config{
InsecureSkipVerify: c.Node.insecureSkipVerify(),
ServerName: c.Node.serverName,
}
conn, err := WebsocketClientConn(u.String(), c.conn, config)
if err != nil {
return err
}
@ -70,14 +76,14 @@ func (c *ProxyConn) handshake() error {
case "tls", "http2": // tls connection
tlsUsed = true
cfg := &tls.Config{
InsecureSkipVerify: c.node.insecureSkipVerify(),
ServerName: c.node.serverName,
InsecureSkipVerify: c.Node.insecureSkipVerify(),
ServerName: c.Node.serverName,
}
c.conn = tls.Client(c.conn, cfg)
default:
}
switch c.node.Protocol {
switch c.Node.Protocol {
case "socks", "socks5": // socks5 handshake with auth and tls supported
selector := &clientSelector{
methods: []uint8{
@ -85,11 +91,15 @@ func (c *ProxyConn) handshake() error {
gosocks5.MethodUserPass,
//MethodTLS,
},
user: c.node.User,
user: c.Node.User,
}
if !tlsUsed { // if transport is not security, enable security socks5
selector.methods = append(selector.methods, MethodTLS)
selector.tlsConfig = &tls.Config{
InsecureSkipVerify: c.Node.insecureSkipVerify(),
ServerName: c.Node.serverName,
}
}
conn := gosocks5.ClientConn(c.conn, selector)
@ -98,9 +108,9 @@ func (c *ProxyConn) handshake() error {
}
c.conn = conn
case "ss": // shadowsocks
if c.node.User != nil {
method := c.node.User.Username()
password, _ := c.node.User.Password()
if c.Node.User != nil {
method := c.Node.User.Username()
password, _ := c.Node.User.Password()
cipher, err := shadowsocks.NewCipher(method, password)
if err != nil {
return err
@ -117,9 +127,9 @@ func (c *ProxyConn) handshake() error {
return nil
}
// Connect to addr through this proxy node
// Connect connect to addr through this proxy node
func (c *ProxyConn) Connect(addr string) error {
switch c.node.Protocol {
switch c.Node.Protocol {
case "ss": // shadowsocks
host, port, err := net.SplitHostPort(addr)
if err != nil {
@ -177,9 +187,9 @@ func (c *ProxyConn) Connect(addr string) error {
Header: make(http.Header),
}
req.Header.Set("Proxy-Connection", "keep-alive")
if c.node.User != nil {
if c.Node.User != nil {
req.Header.Set("Proxy-Authorization",
"Basic "+base64.StdEncoding.EncodeToString([]byte(c.node.User.String())))
"Basic "+base64.StdEncoding.EncodeToString([]byte(c.Node.User.String())))
}
if err := req.Write(c); err != nil {
return err

62
gost.go
View File

@ -2,24 +2,38 @@ package gost
import (
"crypto/tls"
"encoding/base64"
"errors"
"github.com/golang/glog"
"net"
"strings"
"time"
)
const (
Version = "2.2-dev"
)
// Log level for glog
const (
LFATAL = iota
LERROR
LWARNING
LINFO
LDEBUG
LVDEBUG // verbose debug for http2
)
var (
DialTimeout = 30 * time.Second
KeepAliveTime = 180 * time.Second
DialTimeout = 30 * time.Second
ReadTimeout = 90 * time.Second
WriteTimeout = 90 * time.Second
)
var (
SmallBufferSize = 1 * 1024 // 1KB small buffer
MediumBufferSize = 8 * 1024 // 8KB medium buffer
LargeBufferSize = 16 * 1024 // 16KB large buffer
)
var (
@ -27,7 +41,7 @@ var (
DefaultKeyFile = "key.pem"
// This is the default cert and key data for convenience, providing your own cert is recommended.
DefaultRawCert = `-----BEGIN CERTIFICATE-----
defaultRawCert = []byte(`-----BEGIN CERTIFICATE-----
MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj
bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf
@ -44,8 +58,8 @@ UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO
RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/
BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS
uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA=
-----END CERTIFICATE-----`
DefaultRawKey = `-----BEGIN RSA PRIVATE KEY-----
-----END CERTIFICATE-----`)
defaultRawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ
y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0
znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg
@ -71,7 +85,11 @@ kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB
nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X
2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH
85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz
-----END RSA PRIVATE KEY-----`
-----END RSA PRIVATE KEY-----`)
)
var (
ErrEmptyChain = errors.New("empty chain")
)
func setKeepAlive(conn net.Conn, d time.Duration) error {
@ -88,11 +106,39 @@ func setKeepAlive(conn net.Conn, d time.Duration) error {
return nil
}
func loadCertificate(certFile, keyFile string) (tls.Certificate, error) {
// Load the certificate from cert and key files, will use the default certificate if the provided info are invalid.
func LoadCertificate(certFile, keyFile string) (tls.Certificate, error) {
tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err == nil {
return tlsCert, nil
}
glog.V(LWARNING).Infoln(err)
return tls.X509KeyPair([]byte(DefaultRawCert), []byte(DefaultRawKey))
return tls.X509KeyPair(defaultRawCert, defaultRawKey)
}
// Replace the default certificate by your own
func SetDefaultCertificate(rawCert, rawKey []byte) {
defaultRawCert = rawCert
defaultRawKey = rawKey
}
func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
if proxyAuth == "" {
return
}
if !strings.HasPrefix(proxyAuth, "Basic ") {
return
}
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
if err != nil {
return
}
cs := string(c)
s := strings.IndexByte(cs, ':')
if s < 0 {
return
}
return cs[:s], cs[s+1:], true
}

314
http.go
View File

@ -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 (c *Http2ClientConn) Read(b []byte) (n int, err error) {
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
}
// 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 *Http2ClientConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
func (c *Http2ClientConn) SetDeadline(t time.Time) error {
func (c *http2Conn) LocalAddr() net.Addr {
return nil
}
func (c *Http2ClientConn) SetReadDeadline(t time.Time) error {
func (c *http2Conn) RemoteAddr() net.Addr {
return nil
}
func (c *Http2ClientConn) SetWriteDeadline(t time.Time) error {
func (c *http2Conn) SetDeadline(t time.Time) error {
return nil
}
func (c *http2Conn) SetReadDeadline(t time.Time) error {
return nil
}
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
}

34
node.go
View File

@ -9,9 +9,9 @@ import (
// Proxy node represent a proxy
type ProxyNode struct {
Addr string // host:port
Protocol string // protocol: http/http2/socks5/ss
Transport string // transport: ws/wss/tls/tcp/udp/rtcp/rudp
Addr string // [host]:port
Protocol string // protocol: http/socks5/ss
Transport string // transport: ws/wss/tls/http2/tcp/udp/rtcp/rudp
Remote string // remote address, used by tcp/udp port forwarding
User *url.Userinfo // authentication for proxy
values url.Values
@ -19,8 +19,10 @@ type ProxyNode struct {
conn net.Conn
}
// the format is [scheme://][user:pass@host]:port
func ParseProxyNode(s string) (node *ProxyNode, err error) {
// The proxy node string pattern is [scheme://][user:pass@host]:port.
//
// Scheme can be devided into two parts by character '+', such as: http+tls.
func ParseProxyNode(s string) (node ProxyNode, err error) {
if !strings.Contains(s, "://") {
s = "gost://" + s
}
@ -29,7 +31,7 @@ func ParseProxyNode(s string) (node *ProxyNode, err error) {
return
}
node = &ProxyNode{
node = ProxyNode{
Addr: u.Host,
User: u.User,
values: u.Query(),
@ -38,6 +40,9 @@ func ParseProxyNode(s string) (node *ProxyNode, err error) {
if strings.Contains(u.Host, ":") {
node.serverName, _, _ = net.SplitHostPort(u.Host)
if node.serverName == "" {
node.serverName = "localhost" // default server name
}
}
schemes := strings.Split(u.Scheme, "+")
@ -66,7 +71,7 @@ func ParseProxyNode(s string) (node *ProxyNode, err error) {
}
switch node.Protocol {
case "http", "http2", "socks", "socks5", "ss":
case "http", "socks", "socks5", "ss", "http2":
default:
node.Protocol = ""
}
@ -74,8 +79,17 @@ func ParseProxyNode(s string) (node *ProxyNode, err error) {
return
}
// Get get node parameter by key
func (node *ProxyNode) Get(key string) string {
return node.values.Get(key)
}
func (node *ProxyNode) Set(key, value string) {
node.values.Set(key, value)
}
func (node *ProxyNode) insecureSkipVerify() bool {
s := node.values.Get("secure")
s := node.Get("secure")
if secure, _ := strconv.ParseBool(s); secure {
return !secure
}
@ -86,14 +100,14 @@ func (node *ProxyNode) insecureSkipVerify() bool {
}
func (node *ProxyNode) certFile() string {
if cert := node.values.Get("cert"); cert != "" {
if cert := node.Get("cert"); cert != "" {
return cert
}
return DefaultCertFile
}
func (node *ProxyNode) keyFile() string {
if key := node.values.Get("key"); key != "" {
if key := node.Get("key"); key != "" {
return key
}
return DefaultKeyFile

203
server.go Normal file
View File

@ -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
}

580
socks.go
View File

@ -1,9 +1,9 @@
package gost
import (
//"bytes"
"bytes"
"crypto/tls"
//"errors"
"errors"
"github.com/ginuerzh/gosocks5"
"github.com/golang/glog"
//"os/exec"
@ -11,8 +11,8 @@ import (
//"io/ioutil"
"net"
"net/url"
//"strconv"
//"time"
"strconv"
"time"
)
const (
@ -26,8 +26,9 @@ const (
)
type clientSelector struct {
methods []uint8
user *url.Userinfo
methods []uint8
user *url.Userinfo
tlsConfig *tls.Config
}
func (selector *clientSelector) Methods() []uint8 {
@ -41,11 +42,11 @@ func (selector *clientSelector) Select(methods ...uint8) (method uint8) {
func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
switch method {
case MethodTLS:
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
conn = tls.Client(conn, selector.tlsConfig)
case gosocks5.MethodUserPass, MethodTLSAuth:
if method == MethodTLSAuth {
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
conn = tls.Client(conn, selector.tlsConfig)
}
var username, password string
@ -79,9 +80,9 @@ func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Con
}
type serverSelector struct {
methods []uint8
user *url.Userinfo
cert tls.Certificate
methods []uint8
user *url.Userinfo
tlsConfig *tls.Config
}
func (selector *serverSelector) Methods() []uint8 {
@ -117,11 +118,11 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con
switch method {
case MethodTLS:
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.cert}})
conn = tls.Server(conn, selector.tlsConfig)
case gosocks5.MethodUserPass, MethodTLSAuth:
if method == MethodTLSAuth {
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.cert}})
conn = tls.Server(conn, selector.tlsConfig)
}
req, err := gosocks5.ReadUserPassRequest(conn)
@ -162,3 +163,556 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con
return conn, nil
}
type Socks5Server struct {
conn net.Conn
Base *ProxyServer
}
func NewSocks5Server(conn net.Conn, base *ProxyServer) *Socks5Server {
return &Socks5Server{conn: conn, Base: base}
}
func (s *Socks5Server) HandleRequest(req *gosocks5.Request) {
glog.V(LDEBUG).Infof("[socks5] %s - %s\n%s", s.conn.RemoteAddr(), req.Addr, req)
switch req.Cmd {
case gosocks5.CmdConnect:
glog.V(LINFO).Infof("[socks5-connect] %s -> %s", s.conn.RemoteAddr(), req.Addr)
s.handleConnect(req)
case gosocks5.CmdBind:
glog.V(LINFO).Infof("[socks5-bind] %s - %s", s.conn.RemoteAddr(), req.Addr)
s.handleBind(req)
case CmdUdpConnect:
glog.V(LINFO).Infof("[udp] %s - %s", s.conn.RemoteAddr(), req.Addr)
s.handleUDPConnect(req)
case gosocks5.CmdUdp:
glog.V(LINFO).Infof("[socks5-udp] %s - %s", s.conn.RemoteAddr(), req.Addr)
s.handleUDPRelay(req)
case CmdUdpTun:
glog.V(LINFO).Infof("[socks5-udp] %s - %s", s.conn.RemoteAddr(), req.Addr)
s.handleUDPTunnel(req)
default:
glog.V(LWARNING).Infoln("[socks5] Unrecognized request:", req.Cmd)
}
}
func (s *Socks5Server) handleConnect(req *gosocks5.Request) {
cc, err := s.Base.Chain.Dial(req.Addr.String())
if err != nil {
glog.V(LWARNING).Infof("[socks5-connect] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)
rep.Write(s.conn)
glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep)
return
}
defer cc.Close()
rep := gosocks5.NewReply(gosocks5.Succeeded, nil)
if err := rep.Write(s.conn); err != nil {
glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
return
}
glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep)
glog.V(LINFO).Infof("[socks5-connect] %s <-> %s", s.conn.RemoteAddr(), req.Addr)
//Transport(conn, cc)
s.Base.transport(s.conn, cc)
glog.V(LINFO).Infof("[socks5-connect] %s >-< %s", s.conn.RemoteAddr(), req.Addr)
}
func (s *Socks5Server) handleBind(req *gosocks5.Request) {
cc, err := s.Base.Chain.GetConn()
// forward request
if err == nil {
req.Write(cc)
}
// connection error
if err != nil && err != ErrEmptyChain {
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
reply.Write(s.conn)
glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
return
}
// serve socks5 bind
if err == ErrEmptyChain {
cc, err = s.bind(req.Addr.String())
if err != nil {
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
reply.Write(s.conn)
glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
return
}
}
defer cc.Close()
glog.V(LINFO).Infof("[socks5-bind] %s <-> %s", s.conn.RemoteAddr(), cc.RemoteAddr())
s.Base.transport(s.conn, cc)
glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", s.conn.RemoteAddr(), cc.RemoteAddr())
}
func (s *Socks5Server) handleUDPConnect(req *gosocks5.Request) {
cc, err := s.Base.Chain.GetConn()
// connection error
if err != nil && err != ErrEmptyChain {
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
reply.Write(s.conn)
glog.V(LDEBUG).Infof("[udp] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
return
}
// serve udp connect
if err == ErrEmptyChain {
s.udpConnect(req.Addr.String())
return
}
defer cc.Close()
// forward request
if err := req.Write(cc); err != nil {
glog.V(LINFO).Infof("[udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
gosocks5.NewReply(gosocks5.Failure, nil).Write(s.conn)
return
}
glog.V(LINFO).Infof("[udp] %s <-> %s", s.conn.RemoteAddr(), req.Addr)
s.Base.transport(s.conn, cc)
glog.V(LINFO).Infof("[udp] %s >-< %s", s.conn.RemoteAddr(), req.Addr)
}
func (s *Socks5Server) handleUDPRelay(req *gosocks5.Request) {
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", s.conn.RemoteAddr(), req.Addr, err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
reply.Write(s.conn)
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
return
}
defer relay.Close()
socksAddr := ToSocksAddr(relay.LocalAddr())
socksAddr.Host, _, _ = net.SplitHostPort(s.conn.LocalAddr().String())
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
if err := reply.Write(s.conn); err != nil {
glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
return
}
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), reply.Addr, reply)
glog.V(LINFO).Infof("[socks5-udp] %s - %s BIND ON %s OK", s.conn.RemoteAddr(), req.Addr, socksAddr)
cc, err := s.Base.Chain.GetConn()
// connection error
if err != nil && err != ErrEmptyChain {
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
return
}
// serve as standard socks5 udp relay
if err == ErrEmptyChain {
peer, err := net.ListenUDP("udp", nil)
if err != nil {
glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
return
}
defer peer.Close()
go s.transportUDP(relay, peer)
}
if err == nil {
defer cc.Close()
cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
if err := gosocks5.NewRequest(CmdUdpTun, nil).Write(cc); err != nil {
glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
return
}
cc.SetWriteDeadline(time.Time{})
cc.SetReadDeadline(time.Now().Add(ReadTimeout))
reply, err = gosocks5.ReadReply(cc)
if err != nil {
glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
return
}
if reply.Rep != gosocks5.Succeeded {
glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : udp associate error", s.conn.RemoteAddr(), req.Addr)
return
}
cc.SetReadDeadline(time.Time{})
go s.tunnelUDP(relay, cc, true)
}
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", s.conn.RemoteAddr(), req.Addr)
b := make([]byte, SmallBufferSize)
for {
_, err := s.conn.Read(b) // discard any data from tcp connection
if err != nil {
break // client disconnected
}
}
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", s.conn.RemoteAddr(), req.Addr)
}
func (s *Socks5Server) handleUDPTunnel(req *gosocks5.Request) {
cc, err := s.Base.Chain.GetConn()
// connection error
if err != nil && err != ErrEmptyChain {
glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
reply.Write(s.conn)
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
return
}
// serve tunnel udp, tunnel <-> remote, handle tunnel udp request
if err == ErrEmptyChain {
bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String())
uc, err := net.ListenUDP("udp", bindAddr)
if err != nil {
glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
return
}
defer uc.Close()
socksAddr := ToSocksAddr(uc.LocalAddr())
socksAddr.Host, _, _ = net.SplitHostPort(s.conn.LocalAddr().String())
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
if err := reply.Write(s.conn); err != nil {
glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
return
}
glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), uc.LocalAddr(), reply)
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", s.conn.RemoteAddr(), uc.LocalAddr())
s.tunnelUDP(uc, s.conn, false)
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", s.conn.RemoteAddr(), uc.LocalAddr())
return
}
defer cc.Close()
// tunnel <-> tunnel, direct forwarding
req.Write(cc)
glog.V(LINFO).Infof("[socks5-udp] %s <-> %s[tun]", s.conn.RemoteAddr(), cc.RemoteAddr())
s.Base.transport(s.conn, cc)
glog.V(LINFO).Infof("[socks5-udp] %s >-< %s[tun]", s.conn.RemoteAddr(), cc.RemoteAddr())
}
func (s *Socks5Server) bind(addr string) (net.Conn, error) {
bindAddr, _ := net.ResolveTCPAddr("tcp", addr)
ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error
if err != nil {
return nil, err
}
socksAddr := ToSocksAddr(ln.Addr())
// Issue: may not reachable when host has multi-interface
socksAddr.Host, _, _ = net.SplitHostPort(s.conn.LocalAddr().String())
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
if err := reply.Write(s.conn); err != nil {
glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), addr, err)
ln.Close()
return nil, err
}
glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", s.conn.RemoteAddr(), addr, reply)
glog.V(LINFO).Infof("[socks5-bind] %s - %s BIND ON %s OK", s.conn.RemoteAddr(), addr, socksAddr)
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 := make([]byte, SmallBufferSize)
_, err := s.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 nil, errors.New("accept error")
}
pconn = c
lnChan = nil
ln = nil
s.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, err
}
goto out
}
}
out:
s.conn.SetReadDeadline(time.Time{})
glog.V(LINFO).Infof("[socks5-bind] %s <- %s PEER %s ACCEPTED", s.conn.RemoteAddr(), socksAddr, pconn.RemoteAddr())
return pconn, nil
}
func (s *Socks5Server) udpConnect(addr string) {
raddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
glog.V(LINFO).Infof("[udp] %s -> %s : %s", s.conn.RemoteAddr(), addr, err)
gosocks5.NewReply(gosocks5.Failure, nil).Write(s.conn)
return
}
if err := gosocks5.NewReply(gosocks5.Succeeded, nil).Write(s.conn); err != nil {
glog.V(LINFO).Infof("[udp] %s <- %s : %s", s.conn.RemoteAddr(), addr, err)
return
}
glog.V(LINFO).Infof("[udp] %s <-> %s", s.conn.RemoteAddr(), raddr)
defer glog.V(LINFO).Infof("[udp] %s >-< %s", s.conn.RemoteAddr(), raddr)
for {
dgram, err := gosocks5.ReadUDPDatagram(s.conn)
if err != nil {
glog.V(LWARNING).Infof("[udp] %s -> %s : %s", s.conn.RemoteAddr(), addr, err)
return
}
go func() {
b := make([]byte, LargeBufferSize)
relay, err := net.DialUDP("udp", nil, raddr)
if err != nil {
glog.V(LWARNING).Infof("[udp] %s -> %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", s.conn.RemoteAddr(), raddr, err)
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", s.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", s.conn.RemoteAddr(), raddr, err)
return
}
relay.SetReadDeadline(time.Time{})
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", s.conn.RemoteAddr(), raddr, n)
s.conn.SetWriteDeadline(time.Now().Add(time.Second * 90))
if err := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, dgram.Header.Addr), b[:n]).Write(s.conn); err != nil {
glog.V(LWARNING).Infof("[udp] %s <- %s : %s", s.conn.RemoteAddr(), raddr, err)
return
}
s.conn.SetWriteDeadline(time.Time{})
}()
}
}
func (s *Socks5Server) transportUDP(relay, peer *net.UDPConn) (err error) {
errc := make(chan error, 2)
var clientAddr *net.UDPAddr
go func() {
b := make([]byte, LargeBufferSize)
for {
n, laddr, err := relay.ReadFromUDP(b)
if err != nil {
errc <- err
return
}
if clientAddr == nil {
clientAddr = laddr
}
dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))
if err != nil {
errc <- 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 {
errc <- err
return
}
glog.V(LDEBUG).Infof("[socks5-udp] %s >>> %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data))
}
}()
go func() {
b := make([]byte, LargeBufferSize)
for {
n, raddr, err := peer.ReadFromUDP(b)
if err != nil {
errc <- 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 {
errc <- err
return
}
glog.V(LDEBUG).Infof("[socks5-udp] %s <<< %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data))
}
}()
select {
case err = <-errc:
//log.Println("w exit", err)
}
return
}
func (s *Socks5Server) tunnelUDP(uc *net.UDPConn, cc net.Conn, client bool) (err error) {
errc := make(chan error, 2)
var clientAddr *net.UDPAddr
go func() {
b := make([]byte, LargeBufferSize)
for {
n, addr, err := uc.ReadFromUDP(b)
if err != nil {
errc <- err
return
}
var dgram *gosocks5.UDPDatagram
if client { // pipe from relay to tunnel
dgram, err = gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))
if err != nil {
errc <- err
return
}
if clientAddr == nil {
clientAddr = addr
}
dgram.Header.Rsv = uint16(len(dgram.Data))
if err := dgram.Write(cc); err != nil {
errc <- err
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", uc.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(cc); err != nil {
errc <- err
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", cc.RemoteAddr(), dgram.Header.Addr, len(dgram.Data))
}
}
}()
go func() {
for {
dgram, err := gosocks5.ReadUDPDatagram(cc)
if err != nil {
errc <- 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 := uc.WriteToUDP(buf.Bytes(), clientAddr); err != nil {
errc <- err
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", uc.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 := uc.WriteToUDP(dgram.Data, addr); err != nil {
errc <- err
return
}
glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", cc.RemoteAddr(), addr, len(dgram.Data))
}
}
}()
select {
case err = <-errc:
}
return
}
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),
}
}

134
ss.go Normal file
View File

@ -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
}

99
ws.go
View File

@ -3,47 +3,104 @@ package gost
import (
//"github.com/ginuerzh/gosocks5"
"crypto/tls"
//"github.com/golang/glog"
"github.com/golang/glog"
"github.com/gorilla/websocket"
"net"
//"net/http"
//"net/http/httputil"
"net/url"
"net/http"
"net/http/httputil"
//"net/url"
"time"
)
type wsConn struct {
type WebsocketServer struct {
Addr string
Base *ProxyServer
Handler http.Handler
upgrader websocket.Upgrader
}
func NewWebsocketServer(base *ProxyServer) *WebsocketServer {
return &WebsocketServer{
Addr: base.Node.Addr,
Base: base,
upgrader: websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
},
}
}
// Default websocket server handler
func (s *WebsocketServer) HandleRequest(w http.ResponseWriter, r *http.Request) {
glog.V(LINFO).Infof("[ws] %s - %s", r.RemoteAddr, s.Addr)
if glog.V(LDEBUG) {
dump, _ := httputil.DumpRequest(r, false)
glog.V(LDEBUG).Infof("[ws] %s - %s\n%s", r.RemoteAddr, s.Addr, string(dump))
}
conn, err := s.upgrader.Upgrade(w, r, nil)
if err != nil {
glog.V(LERROR).Infof("[ws] %s - %s : %s", r.RemoteAddr, s.Addr, err)
return
}
s.Base.handleConn(WebsocketServerConn(conn))
}
func (s *WebsocketServer) ListenAndServe() error {
mux := http.NewServeMux()
if s.Handler == nil {
s.Handler = http.HandlerFunc(s.HandleRequest)
}
mux.Handle("/ws", s.Handler)
return http.ListenAndServe(s.Addr, mux)
}
func (s *WebsocketServer) ListenAndServeTLS(config *tls.Config) error {
mux := http.NewServeMux()
if s.Handler == nil {
s.Handler = http.HandlerFunc(s.HandleRequest)
}
mux.Handle("/ws", s.Handler)
server := &http.Server{
Addr: s.Addr,
Handler: mux,
TLSConfig: config,
}
return server.ListenAndServeTLS("", "")
}
type WebsocketConn struct {
conn *websocket.Conn
rb []byte
}
func wsClient(scheme string, conn net.Conn, host string) (*wsConn, error) {
func WebsocketClientConn(url string, conn net.Conn, config *tls.Config) (*WebsocketConn, error) {
dialer := websocket.Dialer{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
HandshakeTimeout: time.Second * 90,
TLSClientConfig: config,
HandshakeTimeout: DialTimeout,
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)
c, resp, err := dialer.Dial(url, nil)
if err != nil {
return nil, err
}
resp.Body.Close()
return &wsConn{conn: c}, nil
return &WebsocketConn{conn: c}, nil
}
func wsServer(conn *websocket.Conn) *wsConn {
return &wsConn{
func WebsocketServerConn(conn *websocket.Conn) *WebsocketConn {
return &WebsocketConn{
conn: conn,
}
}
func (c *wsConn) Read(b []byte) (n int, err error) {
func (c *WebsocketConn) Read(b []byte) (n int, err error) {
if len(c.rb) == 0 {
_, c.rb, err = c.conn.ReadMessage()
}
@ -55,7 +112,7 @@ func (c *wsConn) Read(b []byte) (n int, err error) {
return
}
func (c *wsConn) Write(b []byte) (n int, err error) {
func (c *WebsocketConn) Write(b []byte) (n int, err error) {
err = c.conn.WriteMessage(websocket.BinaryMessage, b)
n = len(b)
//log.Println("ws w:", n)
@ -63,28 +120,28 @@ func (c *wsConn) Write(b []byte) (n int, err error) {
return
}
func (c *wsConn) Close() error {
func (c *WebsocketConn) Close() error {
return c.conn.Close()
}
func (c *wsConn) LocalAddr() net.Addr {
func (c *WebsocketConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *wsConn) RemoteAddr() net.Addr {
func (c *WebsocketConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (conn *wsConn) SetDeadline(t time.Time) error {
func (conn *WebsocketConn) 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 {
func (c *WebsocketConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *wsConn) SetWriteDeadline(t time.Time) error {
func (c *WebsocketConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}