update gost library
This commit is contained in:
parent
a15bf73742
commit
f903771282
93
chain.go
93
chain.go
@ -32,11 +32,26 @@ func (c *ProxyChain) AddProxyNode(node ...ProxyNode) {
|
|||||||
c.nodes = append(c.nodes, node...)
|
c.nodes = append(c.nodes, node...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize proxy nodes, mainly check for http2 feature.
|
func (c *ProxyChain) AddProxyNodeString(snode ...string) error {
|
||||||
// Should be called immediately when proxy nodes are ready.
|
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.
|
// NOTE: Should be called immediately when proxy nodes are ready, HTTP2 will not be enabled if this function not be called.
|
||||||
func (c *ProxyChain) Init() {
|
func (c *ProxyChain) TryEnableHttp2() {
|
||||||
length := len(c.nodes)
|
length := len(c.nodes)
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
return
|
return
|
||||||
@ -44,7 +59,7 @@ func (c *ProxyChain) Init() {
|
|||||||
|
|
||||||
c.lastNode = &c.nodes[length-1]
|
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 {
|
for i, node := range c.nodes {
|
||||||
if node.Transport == "http2" {
|
if node.Transport == "http2" {
|
||||||
glog.V(LINFO).Infoln("http2 enabled")
|
glog.V(LINFO).Infoln("http2 enabled")
|
||||||
@ -54,11 +69,15 @@ func (c *ProxyChain) Init() {
|
|||||||
}
|
}
|
||||||
c.initHttp2Client(node.Addr, cfg, c.nodes[:i]...)
|
c.initHttp2Client(node.Addr, cfg, c.nodes[:i]...)
|
||||||
c.http2NodeIndex = 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) {
|
func (c *ProxyChain) initHttp2Client(addr string, config *tls.Config, nodes ...ProxyNode) {
|
||||||
tr := http2.Transport{
|
tr := http2.Transport{
|
||||||
TLSClientConfig: config,
|
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
|
// Connect to addr through proxy chain
|
||||||
func (c *ProxyChain) Dial(addr string) (net.Conn, error) {
|
func (c *ProxyChain) Dial(addr string) (net.Conn, error) {
|
||||||
if !strings.Contains(addr, ":") {
|
if !strings.Contains(addr, ":") {
|
||||||
@ -88,20 +103,35 @@ func (c *ProxyChain) Dial(addr string) (net.Conn, error) {
|
|||||||
return c.dialWithNodes(addr, c.nodes...)
|
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) {
|
func (c *ProxyChain) dialWithNodes(addr string, nodes ...ProxyNode) (conn net.Conn, err error) {
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
return net.DialTimeout("tcp", addr, DialTimeout)
|
return net.DialTimeout("tcp", addr, DialTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pc *ProxyConn
|
|
||||||
|
|
||||||
if c.Http2Enabled() {
|
if c.Http2Enabled() {
|
||||||
nodes = nodes[c.http2NodeIndex+1:]
|
nodes = nodes[c.http2NodeIndex+1:]
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
return c.http2Connect("http", addr)
|
return c.http2Connect(addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pc, err = c.travelNodes(nodes...)
|
pc, err := c.travelNodes(nodes...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -109,8 +139,8 @@ func (c *ProxyChain) dialWithNodes(addr string, nodes ...ProxyNode) (conn net.Co
|
|||||||
pc.Close()
|
pc.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
conn = pc
|
||||||
return pc, nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ProxyChain) travelNodes(nodes ...ProxyNode) (conn *ProxyConn, err error) {
|
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]
|
node := nodes[0]
|
||||||
|
|
||||||
if c.Http2Enabled() {
|
if c.Http2Enabled() {
|
||||||
cc, err = c.http2Connect("http", node.Addr)
|
cc, err = c.http2Connect(node.Addr)
|
||||||
} else {
|
} else {
|
||||||
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
|
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
|
||||||
}
|
}
|
||||||
@ -152,13 +182,14 @@ func (c *ProxyChain) travelNodes(nodes ...ProxyNode) (conn *ProxyConn, err error
|
|||||||
return
|
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() {
|
if !c.Http2Enabled() {
|
||||||
return nil, errors.New("http2 not enabled")
|
return nil, errors.New("HTTP2 not enabled")
|
||||||
}
|
}
|
||||||
http2Node := c.nodes[c.http2NodeIndex]
|
http2Node := c.nodes[c.http2NodeIndex]
|
||||||
|
|
||||||
pr, pw := io.Pipe()
|
pr, pw := io.Pipe()
|
||||||
|
|
||||||
req := http.Request{
|
req := http.Request{
|
||||||
Method: http.MethodConnect,
|
Method: http.MethodConnect,
|
||||||
URL: &url.URL{Scheme: "https", Host: http2Node.Addr},
|
URL: &url.URL{Scheme: "https", Host: http2Node.Addr},
|
||||||
@ -170,11 +201,7 @@ func (c *ProxyChain) http2Connect(protocol, addr string) (net.Conn, error) {
|
|||||||
Host: http2Node.Addr,
|
Host: http2Node.Addr,
|
||||||
ContentLength: -1,
|
ContentLength: -1,
|
||||||
}
|
}
|
||||||
req.Header.Set("gost-target", addr)
|
req.Header.Set("Proxy-Switch", "gost") // Flag header to indicate server to switch to HTTP2 transport mode
|
||||||
if protocol != "" {
|
|
||||||
req.Header.Set("gost-protocol", protocol)
|
|
||||||
}
|
|
||||||
|
|
||||||
if glog.V(LDEBUG) {
|
if glog.V(LDEBUG) {
|
||||||
dump, _ := httputil.DumpRequest(&req, false)
|
dump, _ := httputil.DumpRequest(&req, false)
|
||||||
glog.Infoln(string(dump))
|
glog.Infoln(string(dump))
|
||||||
@ -187,7 +214,19 @@ func (c *ProxyChain) http2Connect(protocol, addr string) (net.Conn, error) {
|
|||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
return nil, errors.New(resp.Status)
|
return nil, errors.New(resp.Status)
|
||||||
}
|
}
|
||||||
conn := &Http2ClientConn{r: resp.Body, w: pw}
|
return &http2Conn{r: resp.Body, w: pw}, nil
|
||||||
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", addr)
|
}
|
||||||
|
|
||||||
|
// 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
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
@ -263,8 +263,6 @@ func handlerHttp2Request(w http.ResponseWriter, req *http.Request) {
|
|||||||
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target)
|
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
//func processSocks5OverHttp2()
|
|
||||||
|
|
||||||
func handleHttp2Transport(w http.ResponseWriter, req *http.Request) {
|
func handleHttp2Transport(w http.ResponseWriter, req *http.Request) {
|
||||||
glog.V(LINFO).Infof("[http2] %s - %s", req.RemoteAddr, req.Host)
|
glog.V(LINFO).Infof("[http2] %s - %s", req.RemoteAddr, req.Host)
|
||||||
if glog.V(LDEBUG) {
|
if glog.V(LDEBUG) {
|
||||||
|
@ -25,7 +25,7 @@ func init() {
|
|||||||
log.Fatal("please specific at least one request URL")
|
log.Fatal("please specific at least one request URL")
|
||||||
}
|
}
|
||||||
urls = flag.Args()
|
urls = flag.Args()
|
||||||
if glog.V(gost.LVDEBUG) {
|
if glog.V(5) {
|
||||||
http2.VerboseLogs = true
|
http2.VerboseLogs = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,14 +42,10 @@ func (list *stringlist) Set(value string) error {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
chain := gost.NewProxyChain()
|
chain := gost.NewProxyChain()
|
||||||
for _, s := range proxyNodes {
|
if err := chain.AddProxyNodeString(proxyNodes...); err != nil {
|
||||||
node, err := gost.ParseProxyNode(s)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
chain.AddProxyNode(*node)
|
chain.TryEnableHttp2()
|
||||||
}
|
|
||||||
chain.Init()
|
|
||||||
|
|
||||||
for _, u := range urls {
|
for _, u := range urls {
|
||||||
url, err := url.Parse(u)
|
url, err := url.Parse(u)
|
53
cmd/tools/gost_server.go
Normal file
53
cmd/tools/gost_server.go
Normal 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
46
conn.go
@ -20,7 +20,7 @@ import (
|
|||||||
|
|
||||||
type ProxyConn struct {
|
type ProxyConn struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
node ProxyNode
|
Node ProxyNode
|
||||||
handshaked bool
|
handshaked bool
|
||||||
handshakeMutex sync.Mutex
|
handshakeMutex sync.Mutex
|
||||||
handshakeErr error
|
handshakeErr error
|
||||||
@ -29,13 +29,13 @@ type ProxyConn struct {
|
|||||||
func NewProxyConn(conn net.Conn, node ProxyNode) *ProxyConn {
|
func NewProxyConn(conn net.Conn, node ProxyNode) *ProxyConn {
|
||||||
return &ProxyConn{
|
return &ProxyConn{
|
||||||
conn: conn,
|
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 {
|
func (c *ProxyConn) Handshake() error {
|
||||||
c.handshakeMutex.Lock()
|
c.handshakeMutex.Lock()
|
||||||
defer c.handshakeMutex.Unlock()
|
defer c.handshakeMutex.Unlock()
|
||||||
@ -53,16 +53,22 @@ func (c *ProxyConn) Handshake() error {
|
|||||||
func (c *ProxyConn) handshake() error {
|
func (c *ProxyConn) handshake() error {
|
||||||
var tlsUsed bool
|
var tlsUsed bool
|
||||||
|
|
||||||
switch c.node.Transport {
|
switch c.Node.Transport {
|
||||||
case "ws": // websocket connection
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.conn = conn
|
c.conn = conn
|
||||||
case "wss": // websocket security
|
case "wss": // websocket security
|
||||||
tlsUsed = true
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -70,14 +76,14 @@ func (c *ProxyConn) handshake() error {
|
|||||||
case "tls", "http2": // tls connection
|
case "tls", "http2": // tls connection
|
||||||
tlsUsed = true
|
tlsUsed = true
|
||||||
cfg := &tls.Config{
|
cfg := &tls.Config{
|
||||||
InsecureSkipVerify: c.node.insecureSkipVerify(),
|
InsecureSkipVerify: c.Node.insecureSkipVerify(),
|
||||||
ServerName: c.node.serverName,
|
ServerName: c.Node.serverName,
|
||||||
}
|
}
|
||||||
c.conn = tls.Client(c.conn, cfg)
|
c.conn = tls.Client(c.conn, cfg)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.node.Protocol {
|
switch c.Node.Protocol {
|
||||||
case "socks", "socks5": // socks5 handshake with auth and tls supported
|
case "socks", "socks5": // socks5 handshake with auth and tls supported
|
||||||
selector := &clientSelector{
|
selector := &clientSelector{
|
||||||
methods: []uint8{
|
methods: []uint8{
|
||||||
@ -85,11 +91,15 @@ func (c *ProxyConn) handshake() error {
|
|||||||
gosocks5.MethodUserPass,
|
gosocks5.MethodUserPass,
|
||||||
//MethodTLS,
|
//MethodTLS,
|
||||||
},
|
},
|
||||||
user: c.node.User,
|
user: c.Node.User,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tlsUsed { // if transport is not security, enable security socks5
|
if !tlsUsed { // if transport is not security, enable security socks5
|
||||||
selector.methods = append(selector.methods, MethodTLS)
|
selector.methods = append(selector.methods, MethodTLS)
|
||||||
|
selector.tlsConfig = &tls.Config{
|
||||||
|
InsecureSkipVerify: c.Node.insecureSkipVerify(),
|
||||||
|
ServerName: c.Node.serverName,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conn := gosocks5.ClientConn(c.conn, selector)
|
conn := gosocks5.ClientConn(c.conn, selector)
|
||||||
@ -98,9 +108,9 @@ func (c *ProxyConn) handshake() error {
|
|||||||
}
|
}
|
||||||
c.conn = conn
|
c.conn = conn
|
||||||
case "ss": // shadowsocks
|
case "ss": // shadowsocks
|
||||||
if c.node.User != nil {
|
if c.Node.User != nil {
|
||||||
method := c.node.User.Username()
|
method := c.Node.User.Username()
|
||||||
password, _ := c.node.User.Password()
|
password, _ := c.Node.User.Password()
|
||||||
cipher, err := shadowsocks.NewCipher(method, password)
|
cipher, err := shadowsocks.NewCipher(method, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -117,9 +127,9 @@ func (c *ProxyConn) handshake() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to addr through this proxy node
|
// Connect connect to addr through this proxy node
|
||||||
func (c *ProxyConn) Connect(addr string) error {
|
func (c *ProxyConn) Connect(addr string) error {
|
||||||
switch c.node.Protocol {
|
switch c.Node.Protocol {
|
||||||
case "ss": // shadowsocks
|
case "ss": // shadowsocks
|
||||||
host, port, err := net.SplitHostPort(addr)
|
host, port, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -177,9 +187,9 @@ func (c *ProxyConn) Connect(addr string) error {
|
|||||||
Header: make(http.Header),
|
Header: make(http.Header),
|
||||||
}
|
}
|
||||||
req.Header.Set("Proxy-Connection", "keep-alive")
|
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||||
if c.node.User != nil {
|
if c.Node.User != nil {
|
||||||
req.Header.Set("Proxy-Authorization",
|
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 {
|
if err := req.Write(c); err != nil {
|
||||||
return err
|
return err
|
||||||
|
62
gost.go
62
gost.go
@ -2,24 +2,38 @@ package gost
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Version = "2.2-dev"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Log level for glog
|
||||||
const (
|
const (
|
||||||
LFATAL = iota
|
LFATAL = iota
|
||||||
LERROR
|
LERROR
|
||||||
LWARNING
|
LWARNING
|
||||||
LINFO
|
LINFO
|
||||||
LDEBUG
|
LDEBUG
|
||||||
LVDEBUG // verbose debug for http2
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DialTimeout = 30 * time.Second
|
|
||||||
KeepAliveTime = 180 * 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 (
|
var (
|
||||||
@ -27,7 +41,7 @@ var (
|
|||||||
DefaultKeyFile = "key.pem"
|
DefaultKeyFile = "key.pem"
|
||||||
|
|
||||||
// This is the default cert and key data for convenience, providing your own cert is recommended.
|
// This is the default cert and key data for convenience, providing your own cert is recommended.
|
||||||
DefaultRawCert = `-----BEGIN CERTIFICATE-----
|
defaultRawCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||||
MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
|
MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
|
||||||
bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj
|
bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj
|
||||||
bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf
|
bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf
|
||||||
@ -44,8 +58,8 @@ UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO
|
|||||||
RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/
|
RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/
|
||||||
BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS
|
BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS
|
||||||
uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA=
|
uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA=
|
||||||
-----END CERTIFICATE-----`
|
-----END CERTIFICATE-----`)
|
||||||
DefaultRawKey = `-----BEGIN RSA PRIVATE KEY-----
|
defaultRawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||||
MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ
|
MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ
|
||||||
y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0
|
y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0
|
||||||
znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg
|
znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg
|
||||||
@ -71,7 +85,11 @@ kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB
|
|||||||
nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X
|
nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X
|
||||||
2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH
|
2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH
|
||||||
85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz
|
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 {
|
func setKeepAlive(conn net.Conn, d time.Duration) error {
|
||||||
@ -88,11 +106,39 @@ func setKeepAlive(conn net.Conn, d time.Duration) error {
|
|||||||
return nil
|
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)
|
tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return tlsCert, nil
|
return tlsCert, nil
|
||||||
}
|
}
|
||||||
glog.V(LWARNING).Infoln(err)
|
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
314
http.go
@ -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 (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)
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Http2ClientConn) RemoteAddr() net.Addr {
|
|
||||||
return c.remoteAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Http2ClientConn) SetDeadline(t time.Time) error {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Http2ClientConn) SetReadDeadline(t time.Time) error {
|
func (c *http2Conn) RemoteAddr() net.Addr {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Http2ClientConn) SetWriteDeadline(t time.Time) error {
|
func (c *http2Conn) SetDeadline(t time.Time) error {
|
||||||
return nil
|
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
34
node.go
@ -9,9 +9,9 @@ import (
|
|||||||
|
|
||||||
// Proxy node represent a proxy
|
// Proxy node represent a proxy
|
||||||
type ProxyNode struct {
|
type ProxyNode struct {
|
||||||
Addr string // host:port
|
Addr string // [host]:port
|
||||||
Protocol string // protocol: http/http2/socks5/ss
|
Protocol string // protocol: http/socks5/ss
|
||||||
Transport string // transport: ws/wss/tls/tcp/udp/rtcp/rudp
|
Transport string // transport: ws/wss/tls/http2/tcp/udp/rtcp/rudp
|
||||||
Remote string // remote address, used by tcp/udp port forwarding
|
Remote string // remote address, used by tcp/udp port forwarding
|
||||||
User *url.Userinfo // authentication for proxy
|
User *url.Userinfo // authentication for proxy
|
||||||
values url.Values
|
values url.Values
|
||||||
@ -19,8 +19,10 @@ type ProxyNode struct {
|
|||||||
conn net.Conn
|
conn net.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// the format is [scheme://][user:pass@host]:port
|
// The proxy node string pattern is [scheme://][user:pass@host]:port.
|
||||||
func ParseProxyNode(s string) (node *ProxyNode, err error) {
|
//
|
||||||
|
// 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, "://") {
|
if !strings.Contains(s, "://") {
|
||||||
s = "gost://" + s
|
s = "gost://" + s
|
||||||
}
|
}
|
||||||
@ -29,7 +31,7 @@ func ParseProxyNode(s string) (node *ProxyNode, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
node = &ProxyNode{
|
node = ProxyNode{
|
||||||
Addr: u.Host,
|
Addr: u.Host,
|
||||||
User: u.User,
|
User: u.User,
|
||||||
values: u.Query(),
|
values: u.Query(),
|
||||||
@ -38,6 +40,9 @@ func ParseProxyNode(s string) (node *ProxyNode, err error) {
|
|||||||
|
|
||||||
if strings.Contains(u.Host, ":") {
|
if strings.Contains(u.Host, ":") {
|
||||||
node.serverName, _, _ = net.SplitHostPort(u.Host)
|
node.serverName, _, _ = net.SplitHostPort(u.Host)
|
||||||
|
if node.serverName == "" {
|
||||||
|
node.serverName = "localhost" // default server name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
schemes := strings.Split(u.Scheme, "+")
|
schemes := strings.Split(u.Scheme, "+")
|
||||||
@ -66,7 +71,7 @@ func ParseProxyNode(s string) (node *ProxyNode, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch node.Protocol {
|
switch node.Protocol {
|
||||||
case "http", "http2", "socks", "socks5", "ss":
|
case "http", "socks", "socks5", "ss", "http2":
|
||||||
default:
|
default:
|
||||||
node.Protocol = ""
|
node.Protocol = ""
|
||||||
}
|
}
|
||||||
@ -74,8 +79,17 @@ func ParseProxyNode(s string) (node *ProxyNode, err error) {
|
|||||||
return
|
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 {
|
func (node *ProxyNode) insecureSkipVerify() bool {
|
||||||
s := node.values.Get("secure")
|
s := node.Get("secure")
|
||||||
if secure, _ := strconv.ParseBool(s); secure {
|
if secure, _ := strconv.ParseBool(s); secure {
|
||||||
return !secure
|
return !secure
|
||||||
}
|
}
|
||||||
@ -86,14 +100,14 @@ func (node *ProxyNode) insecureSkipVerify() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (node *ProxyNode) certFile() string {
|
func (node *ProxyNode) certFile() string {
|
||||||
if cert := node.values.Get("cert"); cert != "" {
|
if cert := node.Get("cert"); cert != "" {
|
||||||
return cert
|
return cert
|
||||||
}
|
}
|
||||||
return DefaultCertFile
|
return DefaultCertFile
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *ProxyNode) keyFile() string {
|
func (node *ProxyNode) keyFile() string {
|
||||||
if key := node.values.Get("key"); key != "" {
|
if key := node.Get("key"); key != "" {
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
return DefaultKeyFile
|
return DefaultKeyFile
|
||||||
|
203
server.go
Normal file
203
server.go
Normal 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
|
||||||
|
}
|
572
socks.go
572
socks.go
@ -1,9 +1,9 @@
|
|||||||
package gost
|
package gost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
//"errors"
|
"errors"
|
||||||
"github.com/ginuerzh/gosocks5"
|
"github.com/ginuerzh/gosocks5"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
//"os/exec"
|
//"os/exec"
|
||||||
@ -11,8 +11,8 @@ import (
|
|||||||
//"io/ioutil"
|
//"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
//"strconv"
|
"strconv"
|
||||||
//"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -28,6 +28,7 @@ const (
|
|||||||
type clientSelector struct {
|
type clientSelector struct {
|
||||||
methods []uint8
|
methods []uint8
|
||||||
user *url.Userinfo
|
user *url.Userinfo
|
||||||
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (selector *clientSelector) Methods() []uint8 {
|
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) {
|
func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
||||||
switch method {
|
switch method {
|
||||||
case MethodTLS:
|
case MethodTLS:
|
||||||
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
conn = tls.Client(conn, selector.tlsConfig)
|
||||||
|
|
||||||
case gosocks5.MethodUserPass, MethodTLSAuth:
|
case gosocks5.MethodUserPass, MethodTLSAuth:
|
||||||
if method == MethodTLSAuth {
|
if method == MethodTLSAuth {
|
||||||
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
conn = tls.Client(conn, selector.tlsConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
var username, password string
|
var username, password string
|
||||||
@ -81,7 +82,7 @@ func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Con
|
|||||||
type serverSelector struct {
|
type serverSelector struct {
|
||||||
methods []uint8
|
methods []uint8
|
||||||
user *url.Userinfo
|
user *url.Userinfo
|
||||||
cert tls.Certificate
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (selector *serverSelector) Methods() []uint8 {
|
func (selector *serverSelector) Methods() []uint8 {
|
||||||
@ -117,11 +118,11 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con
|
|||||||
|
|
||||||
switch method {
|
switch method {
|
||||||
case MethodTLS:
|
case MethodTLS:
|
||||||
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.cert}})
|
conn = tls.Server(conn, selector.tlsConfig)
|
||||||
|
|
||||||
case gosocks5.MethodUserPass, MethodTLSAuth:
|
case gosocks5.MethodUserPass, MethodTLSAuth:
|
||||||
if method == 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)
|
req, err := gosocks5.ReadUserPassRequest(conn)
|
||||||
@ -162,3 +163,556 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con
|
|||||||
|
|
||||||
return conn, nil
|
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
134
ss.go
Normal 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
99
ws.go
@ -3,47 +3,104 @@ package gost
|
|||||||
import (
|
import (
|
||||||
//"github.com/ginuerzh/gosocks5"
|
//"github.com/ginuerzh/gosocks5"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
//"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"net"
|
"net"
|
||||||
//"net/http"
|
"net/http"
|
||||||
//"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
//"net/url"
|
||||||
"time"
|
"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
|
conn *websocket.Conn
|
||||||
rb []byte
|
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{
|
dialer := websocket.Dialer{
|
||||||
ReadBufferSize: 1024,
|
ReadBufferSize: 1024,
|
||||||
WriteBufferSize: 1024,
|
WriteBufferSize: 1024,
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: config,
|
||||||
HandshakeTimeout: time.Second * 90,
|
HandshakeTimeout: DialTimeout,
|
||||||
NetDial: func(net, addr string) (net.Conn, error) {
|
NetDial: func(net, addr string) (net.Conn, error) {
|
||||||
return conn, nil
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
|
|
||||||
return &wsConn{conn: c}, nil
|
return &WebsocketConn{conn: c}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func wsServer(conn *websocket.Conn) *wsConn {
|
func WebsocketServerConn(conn *websocket.Conn) *WebsocketConn {
|
||||||
return &wsConn{
|
return &WebsocketConn{
|
||||||
conn: conn,
|
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 {
|
if len(c.rb) == 0 {
|
||||||
_, c.rb, err = c.conn.ReadMessage()
|
_, c.rb, err = c.conn.ReadMessage()
|
||||||
}
|
}
|
||||||
@ -55,7 +112,7 @@ func (c *wsConn) Read(b []byte) (n int, err error) {
|
|||||||
return
|
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)
|
err = c.conn.WriteMessage(websocket.BinaryMessage, b)
|
||||||
n = len(b)
|
n = len(b)
|
||||||
//log.Println("ws w:", n)
|
//log.Println("ws w:", n)
|
||||||
@ -63,28 +120,28 @@ func (c *wsConn) Write(b []byte) (n int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wsConn) Close() error {
|
func (c *WebsocketConn) Close() error {
|
||||||
return c.conn.Close()
|
return c.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wsConn) LocalAddr() net.Addr {
|
func (c *WebsocketConn) LocalAddr() net.Addr {
|
||||||
return c.conn.LocalAddr()
|
return c.conn.LocalAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wsConn) RemoteAddr() net.Addr {
|
func (c *WebsocketConn) RemoteAddr() net.Addr {
|
||||||
return c.conn.RemoteAddr()
|
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 {
|
if err := conn.SetReadDeadline(t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return conn.SetWriteDeadline(t)
|
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)
|
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)
|
return c.conn.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user