add server and listeners

This commit is contained in:
rui.zheng 2017-07-22 14:02:41 +08:00
parent 289acfd058
commit 7402fb2407
13 changed files with 1113 additions and 58 deletions

View File

@ -1,7 +1,6 @@
package gost
import (
"context"
"net"
)
@ -15,18 +14,18 @@ func NewChain(nodes ...Node) *Chain {
}
}
func (c *Chain) Dial(ctx context.Context, addr string) (net.Conn, error) {
func (c *Chain) Dial(addr string) (net.Conn, error) {
if len(c.Nodes) == 0 {
return net.Dial("tcp", addr)
}
nodes := c.Nodes
conn, err := nodes[0].Client.Dial(ctx, nodes[0].Addr)
conn, err := nodes[0].Client.Dial(nodes[0].Addr)
if err != nil {
return nil, err
}
conn, err = nodes[0].Client.Handshake(ctx, conn)
conn, err = nodes[0].Client.Handshake(conn)
if err != nil {
return nil, err
}
@ -37,12 +36,12 @@ func (c *Chain) Dial(ctx context.Context, addr string) (net.Conn, error) {
}
next := nodes[i+1]
cc, err := node.Client.Connect(ctx, conn, next.Addr)
cc, err := node.Client.Connect(conn, next.Addr)
if err != nil {
conn.Close()
return nil, err
}
cc, err = next.Client.Handshake(ctx, cc)
cc, err = next.Client.Handshake(cc)
if err != nil {
conn.Close()
return nil, err
@ -51,7 +50,7 @@ func (c *Chain) Dial(ctx context.Context, addr string) (net.Conn, error) {
conn = cc
}
cc, err := nodes[len(nodes)-1].Client.Connect(ctx, conn, addr)
cc, err := nodes[len(nodes)-1].Client.Connect( conn, addr)
if err != nil {
conn.Close()
return nil, err

View File

@ -2,11 +2,11 @@ package main
import (
"bufio"
"context"
"crypto/tls"
"log"
"net/http"
"net/http/httputil"
"net/url"
"github.com/ginuerzh/gost/gost"
@ -19,6 +19,30 @@ func init() {
func main() {
chain := gost.NewChain(
/*
// http+ws
gost.Node{
Addr: "127.0.0.1:8000",
Client: gost.NewClient(
gost.HTTPConnector(url.UserPassword("admin", "123456")),
gost.WSTransporter("127.0.0.1:8000", nil),
),
},
*/
// http+wss
gost.Node{
Addr: "127.0.0.1:8443",
Client: gost.NewClient(
gost.HTTPConnector(url.UserPassword("admin", "123456")),
gost.WSSTransporter(
"127.0.0.1:8443",
&gost.WSOptions{TLSConfig: &tls.Config{InsecureSkipVerify: true}},
),
),
},
/*
// http+tcp
gost.Node{
Addr: "127.0.0.1:1080",
Client: gost.NewClient(
@ -26,27 +50,36 @@ func main() {
gost.TCPTransporter(),
),
},
*/
/*
// http+tls
gost.Node{
Addr: "172.24.222.54:8338",
Addr: "127.0.0.1:1443",
Client: gost.NewClient(
gost.HTTPConnector(url.UserPassword("admin", "123456")),
gost.TLSTransporter(&tls.Config{InsecureSkipVerify: true}),
),
},
*/
/*
// ss+tcp
gost.Node{
Addr: "127.0.0.1:8338",
Client: gost.NewClient(
gost.ShadowConnector(url.UserPassword("chacha20", "123456")),
gost.TCPTransporter(),
),
},
gost.Node{
Addr: "172.24.222.54:8080",
Client: gost.NewClient(
gost.SOCKS5Connector(url.UserPassword("cmdsh", "cmdsh123456")),
gost.TCPTransporter(),
),
},
*/
)
conn, err := chain.Dial(context.Background(), "baidu.com:443")
conn, err := chain.Dial("localhost:10000")
if err != nil {
log.Fatal(err)
}
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
req, err := http.NewRequest(http.MethodGet, "https://www.baidu.com", nil)
//conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
req, err := http.NewRequest(http.MethodGet, "http://localhost:10000/pkg", nil)
if err != nil {
log.Fatal(err)
}

View File

@ -1,7 +1,6 @@
package gost
import (
"context"
"net"
)
@ -10,9 +9,6 @@ type Client struct {
Transporter Transporter
}
// DefaultClient is a standard HTTP proxy
var DefaultClient = NewClient(HTTPConnector(nil), TCPTransporter())
func NewClient(c Connector, tr Transporter) *Client {
return &Client{
Connector: c,
@ -21,25 +17,40 @@ func NewClient(c Connector, tr Transporter) *Client {
}
// Dial connects to the target address
func (c *Client) Dial(ctx context.Context, addr string) (net.Conn, error) {
func (c *Client) Dial(addr string) (net.Conn, error) {
return net.Dial(c.Transporter.Network(), addr)
}
func (c *Client) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
return c.Transporter.Handshake(ctx, conn)
func (c *Client) Handshake(conn net.Conn) (net.Conn, error) {
return c.Transporter.Handshake(conn)
}
func (c *Client) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) {
return c.Connector.Connect(ctx, conn, addr)
func (c *Client) Connect(conn net.Conn, addr string) (net.Conn, error) {
return c.Connector.Connect(conn, addr)
}
// DefaultClient is a standard HTTP proxy client
var DefaultClient = NewClient(HTTPConnector(nil), TCPTransporter())
func Dial(addr string) (net.Conn, error) {
return DefaultClient.Dial(addr)
}
func Handshake(conn net.Conn) (net.Conn, error) {
return DefaultClient.Handshake(conn)
}
func Connect(conn net.Conn, addr string) (net.Conn, error) {
return DefaultClient.Connect(conn, addr)
}
type Connector interface {
Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error)
Connect(conn net.Conn, addr string) (net.Conn, error)
}
type Transporter interface {
Network() string
Handshake(ctx context.Context, conn net.Conn) (net.Conn, error)
Handshake(conn net.Conn) (net.Conn, error)
}
type tcpTransporter struct {
@ -53,6 +64,6 @@ func (tr *tcpTransporter) Network() string {
return "tcp"
}
func (tr *tcpTransporter) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
func (tr *tcpTransporter) Handshake(conn net.Conn) (net.Conn, error) {
return conn, nil
}

View File

@ -4,8 +4,16 @@ import (
"github.com/go-log/log"
)
const Version = "2.4-dev20170722"
var Debug bool
var (
SmallBufferSize = 1 * 1024 // 1KB small buffer
MediumBufferSize = 8 * 1024 // 8KB medium buffer
LargeBufferSize = 32 * 1024 // 32KB large buffer
)
func init() {
log.DefaultLogger = &logger{}
}

View File

@ -2,13 +2,13 @@ package gost
import (
"bufio"
"context"
"encoding/base64"
"fmt"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"github.com/go-log/log"
)
@ -21,7 +21,7 @@ func HTTPConnector(user *url.Userinfo) Connector {
return &httpConnector{User: user}
}
func (c *httpConnector) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) {
func (c *httpConnector) Connect(conn net.Conn, addr string) (net.Conn, error) {
req := &http.Request{
Method: http.MethodConnect,
URL: &url.URL{Host: addr},
@ -66,3 +66,134 @@ func (c *httpConnector) Connect(ctx context.Context, conn net.Conn, addr string)
return conn, nil
}
type httpHandler struct {
options *HandlerOptions
}
func HTTPHandler(opts ...HandlerOption) Handler {
h := &httpHandler{
options: &HandlerOptions{
Chain: new(Chain),
},
}
for _, opt := range opts {
opt(h.options)
}
return h
}
func (h *httpHandler) Handle(conn net.Conn) {
req, err := http.ReadRequest(bufio.NewReader(conn))
if err != nil {
log.Log("[http]", err)
return
}
log.Logf("[http] %s %s -> %s %s", req.Method, conn.RemoteAddr(), req.Host, req.Proto)
if Debug {
dump, _ := httputil.DumpRequest(req, false)
log.Logf(string(dump))
}
if req.Method == "PRI" && req.ProtoMajor == 2 {
log.Logf("[http] %s <- %s : Not an HTTP2 server", conn.RemoteAddr(), req.Host)
resp := "HTTP/1.1 400 Bad Request\r\n" +
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
conn.Write([]byte(resp))
return
}
valid := false
u, p, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization"))
users := h.options.Users
for _, user := range users {
username := user.Username()
password, _ := user.Password()
if (u == username && p == password) ||
(u == username && password == "") ||
(username == "" && p == password) {
valid = true
break
}
}
if len(users) > 0 && !valid {
log.Logf("[http] %s <- %s : proxy authentication required", 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"
conn.Write([]byte(resp))
return
}
req.Header.Del("Proxy-Authorization")
// forward http request
//lastNode := s.Base.Chain.lastNode
//if lastNode != nil && lastNode.Transport == "" && (lastNode.Protocol == "http" || lastNode.Protocol == "") {
// s.forwardRequest(req)
// return
//}
// if !s.Base.Node.Can("tcp", req.Host) {
// glog.Errorf("Unauthorized to tcp connect to %s", req.Host)
// return
// }
cc, err := h.options.Chain.Dial(req.Host)
if err != nil {
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
if Debug {
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
}
conn.Write(b)
return
}
defer cc.Close()
if req.Method == http.MethodConnect {
b := []byte("HTTP/1.1 200 Connection established\r\n" +
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
if Debug {
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
}
conn.Write(b)
} else {
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
return
}
}
log.Logf("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
transport(conn, cc)
log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
}
func (h *httpHandler) 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
}

View File

@ -2,17 +2,127 @@ package gost
import (
"crypto/tls"
"io"
"net"
"time"
"net/url"
"github.com/go-log/log"
)
type Server struct {
Addr string `opt:"addr"` // [host]:port
Protocol string `opt:"protocol"` // protocol: http/socks5/ss
TLSConfig *tls.Config
Chain *Chain
Users []url.Userinfo `opt:"user"` // authentication for proxy
l net.Listener
handler Handler
}
func (s *Server) Run() error {
return nil
func (s *Server) Handle(h Handler) {
s.handler = h
}
func (s *Server) Serve(l net.Listener) error {
defer l.Close()
var tempDelay time.Duration
for {
conn, e := l.Accept()
if e != nil {
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
log.Logf("server: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
go s.handler.Handle(conn)
}
}
type Listener interface {
net.Listener
}
type tcpListener struct {
net.Listener
}
func TCPListener(addr string) (Listener, error) {
ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
return &tcpListener{Listener: &tcpKeepAliveListener{ln.(*net.TCPListener)}}, nil
}
type Handler interface {
Handle(net.Conn)
}
type HandlerOptions struct {
Chain *Chain
Users []*url.Userinfo
TLSConfig *tls.Config
}
type HandlerOption func(opts *HandlerOptions)
func ChainHandlerOption(chain *Chain) HandlerOption {
return func(opts *HandlerOptions) {
opts.Chain = chain
}
}
func UsersHandlerOption(users ...*url.Userinfo) HandlerOption {
return func(opts *HandlerOptions) {
opts.Users = users
}
}
func TLSConfigHandlerOption(config *tls.Config) HandlerOption {
return func(opts *HandlerOptions) {
opts.TLSConfig = config
}
}
func transport(rw1, rw2 io.ReadWriter) error {
errc := make(chan error, 1)
go func() {
_, err := io.Copy(rw1, rw2)
errc <- err
}()
go func() {
_, err := io.Copy(rw2, rw1)
errc <- err
}()
return <-errc
}
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
*net.TCPListener
}
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}

View File

@ -1,7 +1,6 @@
package gost
import (
"context"
"crypto/tls"
"errors"
"fmt"
@ -23,25 +22,25 @@ const (
CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp
)
type ClientSelector struct {
type clientSelector struct {
methods []uint8
User *url.Userinfo
TLSConfig *tls.Config
}
func (selector *ClientSelector) Methods() []uint8 {
func (selector *clientSelector) Methods() []uint8 {
return selector.methods
}
func (selector *ClientSelector) AddMethod(methods ...uint8) {
func (selector *clientSelector) AddMethod(methods ...uint8) {
selector.methods = append(selector.methods, methods...)
}
func (selector *ClientSelector) Select(methods ...uint8) (method uint8) {
func (selector *clientSelector) Select(methods ...uint8) (method uint8) {
return
}
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 {
case MethodTLS:
conn = tls.Client(conn, selector.TLSConfig)
@ -83,6 +82,105 @@ func (selector *ClientSelector) OnSelected(method uint8, conn net.Conn) (net.Con
return conn, nil
}
type serverSelector struct {
methods []uint8
Users []*url.Userinfo
TLSConfig *tls.Config
}
func (selector *serverSelector) Methods() []uint8 {
return selector.methods
}
func (selector *serverSelector) AddMethod(methods ...uint8) {
selector.methods = append(selector.methods, methods...)
}
func (selector *serverSelector) Select(methods ...uint8) (method uint8) {
if Debug {
log.Logf("[socks5] %d %d %v", gosocks5.Ver5, len(methods), methods)
}
method = gosocks5.MethodNoAuth
for _, m := range methods {
if m == MethodTLS {
method = m
break
}
}
// when user/pass is set, auth is mandatory
if len(selector.Users) > 0 {
if method == gosocks5.MethodNoAuth {
method = gosocks5.MethodUserPass
}
if method == MethodTLS {
method = MethodTLSAuth
}
}
return
}
func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
if Debug {
log.Logf("[socks5] %d %d", gosocks5.Ver5, method)
}
switch method {
case MethodTLS:
conn = tls.Server(conn, selector.TLSConfig)
case gosocks5.MethodUserPass, MethodTLSAuth:
if method == MethodTLSAuth {
conn = tls.Server(conn, selector.TLSConfig)
}
req, err := gosocks5.ReadUserPassRequest(conn)
if err != nil {
log.Log("[socks5]", err)
return nil, err
}
if Debug {
log.Log("[socks5]", req.String())
}
valid := false
for _, user := range selector.Users {
username := user.Username()
password, _ := user.Password()
if (req.Username == username && req.Password == password) ||
(req.Username == username && password == "") ||
(username == "" && req.Password == password) {
valid = true
break
}
}
if len(selector.Users) > 0 && !valid {
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)
if err := resp.Write(conn); err != nil {
log.Log("[socks5]", err)
return nil, err
}
if Debug {
log.Log("[socks5]", resp)
}
log.Log("[socks5] proxy authentication required")
return nil, gosocks5.ErrAuthFailure
}
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded)
if err := resp.Write(conn); err != nil {
log.Log("[socks5]", err)
return nil, err
}
if Debug {
log.Log("[socks5]", resp)
}
case gosocks5.MethodNoAcceptable:
return nil, gosocks5.ErrBadMethod
}
return conn, nil
}
type socks5Connector struct {
User *url.Userinfo
}
@ -91,8 +189,8 @@ func SOCKS5Connector(user *url.Userinfo) Connector {
return &socks5Connector{User: user}
}
func (c *socks5Connector) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) {
selector := &ClientSelector{
func (c *socks5Connector) Connect(conn net.Conn, addr string) (net.Conn, error) {
selector := &clientSelector{
TLSConfig: &tls.Config{InsecureSkipVerify: true},
User: c.User,
}
@ -148,7 +246,7 @@ func SOCKS4Connector() Connector {
return &socks4Connector{}
}
func (c *socks4Connector) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) {
func (c *socks4Connector) Connect(conn net.Conn, addr string) (net.Conn, error) {
taddr, err := net.ResolveTCPAddr("tcp4", addr)
if err != nil {
return nil, err
@ -191,7 +289,7 @@ func SOCKS4AConnector() Connector {
return &socks4aConnector{}
}
func (c *socks4aConnector) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) {
func (c *socks4aConnector) Connect(conn net.Conn, addr string) (net.Conn, error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
@ -223,3 +321,105 @@ func (c *socks4aConnector) Connect(ctx context.Context, conn net.Conn, addr stri
return conn, nil
}
type socks5Handler struct {
selector *serverSelector
options *HandlerOptions
}
func SOCKS5Handler(opts ...HandlerOption) Handler {
options := &HandlerOptions{
Chain: new(Chain),
}
for _, opt := range opts {
opt(options)
}
selector := &serverSelector{ // socks5 server selector
Users: options.Users,
TLSConfig: options.TLSConfig,
}
// methods that socks5 server supported
selector.AddMethod(
gosocks5.MethodNoAuth,
gosocks5.MethodUserPass,
MethodTLS,
MethodTLSAuth,
)
return &socks5Handler{
options: options,
selector: selector,
}
}
func (h *socks5Handler) Handle(conn net.Conn) {
conn = gosocks5.ServerConn(conn, h.selector)
req, err := gosocks5.ReadRequest(conn)
if err != nil {
log.Log("[socks5]", err)
return
}
if Debug {
log.Logf("[socks5] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, req)
}
switch req.Cmd {
case gosocks5.CmdConnect:
log.Logf("[socks5-connect] %s -> %s", conn.RemoteAddr(), req.Addr)
h.handleConnect(conn, req)
case gosocks5.CmdBind:
log.Logf("[socks5-bind] %s - %s", conn.RemoteAddr(), req.Addr)
h.handleBind(conn, req)
case gosocks5.CmdUdp:
log.Logf("[socks5-udp] %s - %s", conn.RemoteAddr(), req.Addr)
//s.handleUDPRelay(req)
case CmdUdpTun:
log.Logf("[socks5-rudp] %s - %s", conn.RemoteAddr(), req.Addr)
//s.handleUDPTunnel(req)
default:
log.Log("[socks5] Unrecognized request:", req.Cmd)
}
}
func (h *socks5Handler) handleConnect(conn net.Conn, req *gosocks5.Request) {
addr := req.Addr.String()
//! if !s.Base.Node.Can("tcp", addr) {
//! glog.Errorf("Unauthorized to tcp connect to %s", addr)
//! rep := gosocks5.NewReply(gosocks5.NotAllowed, nil)
//! rep.Write(s.conn)
//! return
//! }
cc, err := h.options.Chain.Dial(addr)
if err != nil {
log.Logf("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)
rep.Write(conn)
if Debug {
log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
}
return
}
defer cc.Close()
rep := gosocks5.NewReply(gosocks5.Succeeded, nil)
if err := rep.Write(conn); err != nil {
log.Logf("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
return
}
if Debug {
log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
}
log.Logf("[socks5-connect] %s <-> %s", conn.RemoteAddr(), req.Addr)
transport(conn, cc)
log.Logf("[socks5-connect] %s >-< %s", conn.RemoteAddr(), req.Addr)
}
func (h *socks5Handler) handleBind(conn net.Conn, req *gosocks5.Request) {
// TODO: socks5 bind
}

18
gost/srv/cert.pem Normal file
View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==
-----END CERTIFICATE-----

27
gost/srv/key.pem Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=
-----END RSA PRIVATE KEY-----

111
gost/srv/srv.go Normal file
View File

@ -0,0 +1,111 @@
package main
import (
"crypto/tls"
"log"
"net/url"
"sync"
"github.com/ginuerzh/gost/gost"
)
func init() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
gost.Debug = true
}
func main() {
wg := sync.WaitGroup{}
wg.Add(1)
go httpServer(&wg)
wg.Add(1)
go tlsServer(&wg)
wg.Add(1)
go shadowServer(&wg)
wg.Add(1)
go wsServer(&wg)
wg.Add(1)
go wssServer(&wg)
wg.Wait()
}
func httpServer(wg *sync.WaitGroup) {
defer wg.Done()
s := &gost.Server{}
s.Handle(gost.HTTPHandler(
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
))
ln, err := gost.TCPListener(":1080")
if err != nil {
log.Fatal(err)
}
log.Fatal(s.Serve(ln))
}
func tlsServer(wg *sync.WaitGroup) {
defer wg.Done()
s := &gost.Server{}
s.Handle(gost.HTTPHandler(
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
))
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
if err != nil {
log.Fatal(err)
}
ln, err := gost.TLSListener(":1443", &tls.Config{Certificates: []tls.Certificate{cert}})
if err != nil {
log.Fatal(err)
}
log.Fatal(s.Serve(ln))
}
func wsServer(wg *sync.WaitGroup) {
defer wg.Done()
s := &gost.Server{}
s.Handle(gost.HTTPHandler(
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
))
ln, err := gost.WSListener(":8000", nil)
if err != nil {
log.Fatal(err)
}
log.Fatal(s.Serve(ln))
}
func wssServer(wg *sync.WaitGroup) {
defer wg.Done()
s := &gost.Server{}
s.Handle(gost.HTTPHandler(
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
))
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
if err != nil {
log.Fatal(err)
}
ln, err := gost.WSSListener(":8443", &gost.WSOptions{TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}}})
if err != nil {
log.Fatal(err)
}
log.Fatal(s.Serve(ln))
}
func shadowServer(wg *sync.WaitGroup) {
defer wg.Done()
s := &gost.Server{}
s.Handle(gost.ShadowHandler(
gost.UsersHandlerOption(url.UserPassword("chacha20", "123456")),
))
ln, err := gost.TCPListener(":8338")
if err != nil {
log.Fatal(err)
}
log.Fatal(s.Serve(ln))
}

View File

@ -1,11 +1,15 @@
package gost
import (
"context"
"encoding/binary"
"fmt"
"io"
"net"
"net/url"
"strconv"
"time"
"github.com/go-log/log"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
)
@ -15,10 +19,6 @@ type shadowConn struct {
conn net.Conn
}
func ShadowConn(conn net.Conn) net.Conn {
return &shadowConn{conn: conn}
}
func (c *shadowConn) Read(b []byte) (n int, err error) {
return c.conn.Read(b)
}
@ -61,7 +61,7 @@ func ShadowConnector(cipher *url.Userinfo) Connector {
return &shadowConnector{Cipher: cipher}
}
func (c *shadowConnector) Connect(ctx context.Context, conn net.Conn, addr string) (net.Conn, error) {
func (c *shadowConnector) Connect(conn net.Conn, addr string) (net.Conn, error) {
rawaddr, err := ss.RawAddr(addr)
if err != nil {
return nil, err
@ -84,3 +84,122 @@ func (c *shadowConnector) Connect(ctx context.Context, conn net.Conn, addr strin
}
return &shadowConn{conn: sc}, nil
}
type shadowHandler struct {
options *HandlerOptions
}
func ShadowHandler(opts ...HandlerOption) Handler {
h := &shadowHandler{
options: &HandlerOptions{
Chain: new(Chain),
},
}
for _, opt := range opts {
opt(h.options)
}
return h
}
func (h *shadowHandler) Handle(conn net.Conn) {
var method, password string
users := h.options.Users
if len(users) > 0 {
method = users[0].Username()
password, _ = users[0].Password()
}
cipher, err := ss.NewCipher(method, password)
if err != nil {
log.Log("[ss]", err)
return
}
conn = &shadowConn{conn: ss.NewConn(conn, cipher)}
log.Logf("[ss] %s - %s", conn.RemoteAddr(), conn.LocalAddr())
addr, err := h.getRequest(conn)
if err != nil {
log.Logf("[ss] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
return
}
log.Logf("[ss] %s -> %s", conn.RemoteAddr(), addr)
cc, err := h.options.Chain.Dial(addr)
if err != nil {
log.Logf("[ss] %s -> %s : %s", conn.RemoteAddr(), addr, err)
return
}
defer cc.Close()
log.Logf("[ss] %s <-> %s", conn.RemoteAddr(), addr)
transport(conn, cc)
log.Logf("[ss] %s >-< %s", conn.RemoteAddr(), addr)
}
const (
idType = 0 // address type index
idIP0 = 1 // ip addres start index
idDmLen = 1 // domain address length index
idDm0 = 2 // domain address start index
typeIPv4 = 1 // type is ipv4 address
typeDm = 3 // type is domain address
typeIPv6 = 4 // type is ipv6 address
lenIPv4 = net.IPv4len + 2 // ipv4 + 2port
lenIPv6 = net.IPv6len + 2 // ipv6 + 2port
lenDmBase = 2 // 1addrLen + 2port, plus addrLen
lenHmacSha1 = 10
)
// This function is copied from shadowsocks library with some modification.
func (h *shadowHandler) getRequest(conn net.Conn) (host string, err error) {
// buf size should at least have the same size with the largest possible
// request size (when addrType is 3, domain name has at most 256 bytes)
// 1(addrType) + 1(lenByte) + 256(max length address) + 2(port)
buf := make([]byte, SmallBufferSize)
// read till we get possible domain length field
conn.SetReadDeadline(time.Now().Add(30 * time.Second))
if _, err = io.ReadFull(conn, buf[:idType+1]); err != nil {
return
}
var reqStart, reqEnd int
addrType := buf[idType]
switch addrType & ss.AddrMask {
case typeIPv4:
reqStart, reqEnd = idIP0, idIP0+lenIPv4
case typeIPv6:
reqStart, reqEnd = idIP0, idIP0+lenIPv6
case typeDm:
if _, err = io.ReadFull(conn, buf[idType+1:idDmLen+1]); err != nil {
return
}
reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase)
default:
err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask)
return
}
if _, err = io.ReadFull(conn, buf[reqStart:reqEnd]); err != nil {
return
}
// Return string for typeIP is not most efficient, but browsers (Chrome,
// Safari, Firefox) all seems using typeDm exclusively. So this is not a
// big problem.
switch addrType & ss.AddrMask {
case typeIPv4:
host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String()
case typeIPv6:
host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String()
case typeDm:
host = string(buf[idDm0 : idDm0+buf[idDmLen]])
}
// parse port
port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd])
host = net.JoinHostPort(host, strconv.Itoa(int(port)))
return
}

View File

@ -1,7 +1,6 @@
package gost
import (
"context"
"crypto/tls"
"net"
)
@ -18,6 +17,18 @@ func (tr *tlsTransporter) Network() string {
return "tcp"
}
func (tr *tlsTransporter) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
func (tr *tlsTransporter) Handshake(conn net.Conn) (net.Conn, error) {
return tls.Client(conn, tr.TLSClientConfig), nil
}
type tlsListener struct {
net.Listener
}
func TLSListener(addr string, config *tls.Config) (Listener, error) {
ln, err := tls.Listen("tcp", addr, config)
if err != nil {
return nil, err
}
return &tlsListener{Listener: ln}, nil
}

277
gost/ws.go Normal file
View File

@ -0,0 +1,277 @@
package gost
import (
"crypto/tls"
"net"
"net/http"
"net/http/httputil"
"time"
"net/url"
"github.com/go-log/log"
"gopkg.in/gorilla/websocket.v1"
)
type WSOptions struct {
ReadBufferSize int
WriteBufferSize int
HandshakeTimeout time.Duration
EnableCompression bool
TLSConfig *tls.Config
}
type websocketConn struct {
conn *websocket.Conn
rb []byte
}
func websocketClientConn(url string, conn net.Conn, options *WSOptions) (net.Conn, error) {
if options == nil {
options = &WSOptions{}
}
dialer := websocket.Dialer{
ReadBufferSize: options.ReadBufferSize,
WriteBufferSize: options.WriteBufferSize,
TLSClientConfig: options.TLSConfig,
HandshakeTimeout: options.HandshakeTimeout,
EnableCompression: options.EnableCompression,
NetDial: func(net, addr string) (net.Conn, error) {
return conn, nil
},
}
c, resp, err := dialer.Dial(url, nil)
if err != nil {
return nil, err
}
resp.Body.Close()
return &websocketConn{conn: c}, nil
}
func websocketServerConn(conn *websocket.Conn) net.Conn {
// conn.EnableWriteCompression(true)
return &websocketConn{
conn: conn,
}
}
func (c *websocketConn) Read(b []byte) (n int, err error) {
if len(c.rb) == 0 {
_, c.rb, err = c.conn.ReadMessage()
}
n = copy(b, c.rb)
c.rb = c.rb[n:]
return
}
func (c *websocketConn) Write(b []byte) (n int, err error) {
err = c.conn.WriteMessage(websocket.BinaryMessage, b)
n = len(b)
return
}
func (c *websocketConn) Close() error {
return c.conn.Close()
}
func (c *websocketConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *websocketConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (conn *websocketConn) SetDeadline(t time.Time) error {
if err := conn.SetReadDeadline(t); err != nil {
return err
}
return conn.SetWriteDeadline(t)
}
func (c *websocketConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *websocketConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
type wsTransporter struct {
addr string
options *WSOptions
}
func WSTransporter(addr string, opts *WSOptions) Transporter {
return &wsTransporter{
addr: addr,
options: opts,
}
}
func (tr *wsTransporter) Network() string {
return "tcp"
}
func (tr *wsTransporter) Handshake(conn net.Conn) (net.Conn, error) {
url := url.URL{Scheme: "ws", Host: tr.addr, Path: "/ws"}
return websocketClientConn(url.String(), conn, tr.options)
}
type wssTransporter struct {
addr string
options *WSOptions
}
func WSSTransporter(addr string, opts *WSOptions) Transporter {
return &wssTransporter{
addr: addr,
options: opts,
}
}
func (tr *wssTransporter) Network() string {
return "tcp"
}
func (tr *wssTransporter) Handshake(conn net.Conn) (net.Conn, error) {
url := url.URL{Scheme: "wss", Host: tr.addr, Path: "/ws"}
return websocketClientConn(url.String(), conn, tr.options)
}
type wsListener struct {
addr net.Addr
upgrader *websocket.Upgrader
srv *http.Server
connChan chan net.Conn
errChan chan error
}
func WSListener(addr string, options *WSOptions) (Listener, error) {
tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
}
if options == nil {
options = &WSOptions{}
}
l := &wsListener{
addr: tcpAddr,
upgrader: &websocket.Upgrader{
ReadBufferSize: options.ReadBufferSize,
WriteBufferSize: options.WriteBufferSize,
CheckOrigin: func(r *http.Request) bool { return true },
EnableCompression: options.EnableCompression,
},
connChan: make(chan net.Conn, 32),
errChan: make(chan error, 1),
}
mux := http.NewServeMux()
mux.Handle("/ws", http.HandlerFunc(l.upgrade))
l.srv = &http.Server{Addr: addr, Handler: mux}
ln, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
return nil, err
}
go func() {
err := l.srv.Serve(tcpKeepAliveListener{ln})
if err != nil {
l.errChan <- err
}
close(l.errChan)
}()
select {
case err := <-l.errChan:
return nil, err
default:
}
return l, nil
}
func (l *wsListener) upgrade(w http.ResponseWriter, r *http.Request) {
log.Logf("[ws] %s -> %s", r.RemoteAddr, l.addr)
if Debug {
dump, _ := httputil.DumpRequest(r, false)
log.Log(string(dump))
}
conn, err := l.upgrader.Upgrade(w, r, nil)
if err != nil {
log.Logf("[ws] %s - %s : %s", r.RemoteAddr, l.addr, err)
return
}
l.connChan <- websocketServerConn(conn)
}
func (l *wsListener) Accept() (conn net.Conn, err error) {
select {
case conn = <-l.connChan:
case err = <-l.errChan:
}
return
}
func (l *wsListener) Close() error {
return l.srv.Close()
}
func (l *wsListener) Addr() net.Addr {
return l.addr
}
type wssListener struct {
*wsListener
}
func WSSListener(addr string, options *WSOptions) (Listener, error) {
tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
}
if options == nil {
options = &WSOptions{}
}
l := &wssListener{
wsListener: &wsListener{
addr: tcpAddr,
upgrader: &websocket.Upgrader{
ReadBufferSize: options.ReadBufferSize,
WriteBufferSize: options.WriteBufferSize,
CheckOrigin: func(r *http.Request) bool { return true },
EnableCompression: options.EnableCompression,
},
connChan: make(chan net.Conn, 32),
errChan: make(chan error, 1),
},
}
mux := http.NewServeMux()
mux.Handle("/ws", http.HandlerFunc(l.upgrade))
l.srv = &http.Server{
Addr: addr,
TLSConfig: options.TLSConfig,
Handler: mux,
}
ln, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
return nil, err
}
go func() {
err := l.srv.Serve(tls.NewListener(tcpKeepAliveListener{ln}, options.TLSConfig))
if err != nil {
l.errChan <- err
}
close(l.errChan)
}()
select {
case err := <-l.errChan:
return nil, err
default:
}
return l, nil
}