mirror of https://github.com/ginuerzh/gost
10 changed files with 731 additions and 35 deletions
@ -0,0 +1,167 @@ |
|||||
|
package socks |
||||
|
|
||||
|
import ( |
||||
|
"crypto/tls" |
||||
|
"net" |
||||
|
"net/url" |
||||
|
|
||||
|
"github.com/ginuerzh/gosocks5" |
||||
|
"github.com/golang/glog" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
MethodTLS uint8 = 0x80 // extended method for tls
|
||||
|
MethodTLSAuth uint8 = 0x82 // extended method for tls+auth
|
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp
|
||||
|
) |
||||
|
|
||||
|
type ClientSelector struct { |
||||
|
methods []uint8 |
||||
|
User *url.Userinfo |
||||
|
TLSConfig *tls.Config |
||||
|
} |
||||
|
|
||||
|
func (selector *ClientSelector) Methods() []uint8 { |
||||
|
return selector.methods |
||||
|
} |
||||
|
|
||||
|
func (selector *ClientSelector) AddMethod(methods ...uint8) { |
||||
|
selector.methods = append(selector.methods, methods...) |
||||
|
} |
||||
|
|
||||
|
func (selector *ClientSelector) Select(methods ...uint8) (method uint8) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (selector *ClientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { |
||||
|
switch method { |
||||
|
case MethodTLS: |
||||
|
conn = tls.Client(conn, selector.TLSConfig) |
||||
|
|
||||
|
case gosocks5.MethodUserPass, MethodTLSAuth: |
||||
|
if method == MethodTLSAuth { |
||||
|
conn = tls.Client(conn, selector.TLSConfig) |
||||
|
} |
||||
|
|
||||
|
var username, password string |
||||
|
if selector.User != nil { |
||||
|
username = selector.User.Username() |
||||
|
password, _ = selector.User.Password() |
||||
|
} |
||||
|
|
||||
|
req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password) |
||||
|
if err := req.Write(conn); err != nil { |
||||
|
glog.Infoln("socks5 auth:", err) |
||||
|
return nil, err |
||||
|
} |
||||
|
glog.Infoln(req) |
||||
|
|
||||
|
resp, err := gosocks5.ReadUserPassResponse(conn) |
||||
|
if err != nil { |
||||
|
glog.Infoln("socks5 auth:", err) |
||||
|
return nil, err |
||||
|
} |
||||
|
glog.Infoln(resp) |
||||
|
|
||||
|
if resp.Status != gosocks5.Succeeded { |
||||
|
return nil, gosocks5.ErrAuthFailure |
||||
|
} |
||||
|
case gosocks5.MethodNoAcceptable: |
||||
|
return nil, gosocks5.ErrBadMethod |
||||
|
} |
||||
|
|
||||
|
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) Select(methods ...uint8) (method uint8) { |
||||
|
glog.Infof("%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 selector.users != nil { |
||||
|
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) { |
||||
|
glog.Infof("%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 { |
||||
|
glog.Infoln("[socks5-auth]", err) |
||||
|
return nil, err |
||||
|
} |
||||
|
glog.Infoln("[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 { |
||||
|
glog.Infoln("[socks5-auth]", err) |
||||
|
return nil, err |
||||
|
} |
||||
|
glog.Infoln("[socks5]", resp) |
||||
|
glog.Infoln("[socks5-auth] proxy authentication required") |
||||
|
|
||||
|
return nil, gosocks5.ErrAuthFailure |
||||
|
} |
||||
|
|
||||
|
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded) |
||||
|
if err := resp.Write(conn); err != nil { |
||||
|
glog.Infoln("[socks5-auth]", err) |
||||
|
return nil, err |
||||
|
} |
||||
|
glog.Infoln(resp) |
||||
|
|
||||
|
case gosocks5.MethodNoAcceptable: |
||||
|
return nil, gosocks5.ErrBadMethod |
||||
|
} |
||||
|
|
||||
|
return conn, nil |
||||
|
} |
||||
@ -0,0 +1,165 @@ |
|||||
|
package ssocks |
||||
|
|
||||
|
import ( |
||||
|
"encoding/binary" |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"net" |
||||
|
"net/url" |
||||
|
"strconv" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/go-log/log" |
||||
|
|
||||
|
"github.com/ginuerzh/gost" |
||||
|
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" |
||||
|
) |
||||
|
|
||||
|
// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,
|
||||
|
// we wrap around it to make io.Copy happy
|
||||
|
type shadowConn struct { |
||||
|
conn net.Conn |
||||
|
} |
||||
|
|
||||
|
func NewConn(conn net.Conn) net.Conn { |
||||
|
return &shadowConn{conn: conn} |
||||
|
} |
||||
|
|
||||
|
func (c *shadowConn) Read(b []byte) (n int, err error) { |
||||
|
return c.conn.Read(b) |
||||
|
} |
||||
|
|
||||
|
func (c *shadowConn) Write(b []byte) (n int, err error) { |
||||
|
n = len(b) // force byte length consistent
|
||||
|
_, err = c.conn.Write(b) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (c *shadowConn) Close() error { |
||||
|
return c.conn.Close() |
||||
|
} |
||||
|
|
||||
|
func (c *shadowConn) LocalAddr() net.Addr { |
||||
|
return c.conn.LocalAddr() |
||||
|
} |
||||
|
|
||||
|
func (c *shadowConn) RemoteAddr() net.Addr { |
||||
|
return c.conn.RemoteAddr() |
||||
|
} |
||||
|
|
||||
|
func (c *shadowConn) SetDeadline(t time.Time) error { |
||||
|
return c.conn.SetDeadline(t) |
||||
|
} |
||||
|
|
||||
|
func (c *shadowConn) SetReadDeadline(t time.Time) error { |
||||
|
return c.conn.SetReadDeadline(t) |
||||
|
} |
||||
|
|
||||
|
func (c *shadowConn) SetWriteDeadline(t time.Time) error { |
||||
|
return c.conn.SetWriteDeadline(t) |
||||
|
} |
||||
|
|
||||
|
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 |
||||
|
) |
||||
|
|
||||
|
type ShadowServer struct { |
||||
|
conn net.Conn |
||||
|
base gost.Server |
||||
|
} |
||||
|
|
||||
|
func NewServer(conn net.Conn, cipher *url.Userinfo, base gost.Server) (*ShadowServer, error) { |
||||
|
method := cipher.Username() |
||||
|
password, _ := cipher.Password() |
||||
|
cp, err := ss.NewCipher(method, password) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return &ShadowServer{conn: ss.NewConn(conn, cp), base: base}, nil |
||||
|
} |
||||
|
|
||||
|
func (s *ShadowServer) Serve() error { |
||||
|
log.Logf("[ss] %s - %s", s.conn.RemoteAddr(), s.conn.LocalAddr()) |
||||
|
|
||||
|
addr, err := s.getRequest() |
||||
|
if err != nil { |
||||
|
log.Logf("[ss] %s - %s : %s", s.conn.RemoteAddr(), s.conn.LocalAddr(), err) |
||||
|
return err |
||||
|
} |
||||
|
log.Logf("[ss] %s -> %s", s.conn.RemoteAddr(), addr) |
||||
|
|
||||
|
cc, err := s.base.Chain().Dial(addr) |
||||
|
if err != nil { |
||||
|
log.Logf("[ss] %s -> %s : %s", s.conn.RemoteAddr(), addr, err) |
||||
|
return err |
||||
|
} |
||||
|
defer cc.Close() |
||||
|
|
||||
|
log.Logf("[ss] %s <-> %s", s.conn.RemoteAddr(), addr) |
||||
|
defer log.Logf("[ss] %s >-< %s", s.conn.RemoteAddr(), addr) |
||||
|
|
||||
|
return gost.Transport(&shadowConn{conn: s.conn}, cc) |
||||
|
} |
||||
|
|
||||
|
// This function is copied from shadowsocks library with some modification.
|
||||
|
func (s *ShadowServer) getRequest() (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, gost.SmallBufferSize) |
||||
|
|
||||
|
// read till we get possible domain length field
|
||||
|
s.conn.SetReadDeadline(time.Now().Add(30 * time.Second)) |
||||
|
if _, err = io.ReadFull(s.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(s.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(s.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 |
||||
|
} |
||||
@ -1,8 +1,137 @@ |
|||||
package tcp |
package tcp |
||||
|
|
||||
|
import ( |
||||
|
"bufio" |
||||
|
"net" |
||||
|
"net/http" |
||||
|
"net/url" |
||||
|
"weed-fs/go/glog" |
||||
|
|
||||
|
"github.com/ginuerzh/gosocks4" |
||||
|
"github.com/ginuerzh/gosocks5" |
||||
|
"github.com/ginuerzh/gost" |
||||
|
"github.com/ginuerzh/gost/ssocks" |
||||
|
) |
||||
|
|
||||
type nodeServer struct { |
type nodeServer struct { |
||||
|
options *nodeOptions |
||||
|
chain *gost.Chain |
||||
|
} |
||||
|
|
||||
|
func (s *nodeServer) Chain() *gost.Chain { |
||||
|
return s.chain |
||||
|
} |
||||
|
|
||||
|
func (s *nodeServer) SetChain(chain *gost.Chain) { |
||||
|
s.chain = chain |
||||
|
} |
||||
|
|
||||
|
func (s *nodeServer) Options() gost.Options { |
||||
|
return s.options |
||||
} |
} |
||||
|
|
||||
func (s *nodeServer) Run() error { |
func (s *nodeServer) Run() error { |
||||
return nil |
ln, err := net.Listen("tcp", s.options.BaseOptions().Addr) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
defer ln.Close() |
||||
|
|
||||
|
for { |
||||
|
conn, err := ln.Accept() |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
go s.handleConn(conn) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (s *nodeServer) handleConn(conn net.Conn) { |
||||
|
defer conn.Close() |
||||
|
|
||||
|
switch s.options.BaseOptions().Protocol { |
||||
|
case "ss": // shadowsocks
|
||||
|
var cipher url.Userinfo |
||||
|
if len(s.options.users) > 0 { |
||||
|
cipher = s.options.users[0] |
||||
|
} |
||||
|
server := ssocks.NewServer(conn, &cipher, s) |
||||
|
server.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 |
||||
|
case "socks4", "socks4a": |
||||
|
req, err := gosocks4.ReadRequest(conn) |
||||
|
if err != nil { |
||||
|
glog.V(LWARNING).Infoln("[socks4]", err) |
||||
|
return |
||||
|
} |
||||
|
NewSocks4Server(conn, s).HandleRequest(req) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
br := bufio.NewReader(conn) |
||||
|
b, err := br.Peek(1) |
||||
|
if err != nil { |
||||
|
glog.V(LWARNING).Infoln(err) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
switch b[0] { |
||||
|
case gosocks4.Ver4: |
||||
|
req, err := gosocks4.ReadRequest(br) |
||||
|
if err != nil { |
||||
|
glog.V(LWARNING).Infoln("[socks4]", err) |
||||
|
return |
||||
|
} |
||||
|
NewSocks4Server(conn, s).HandleRequest(req) |
||||
|
|
||||
|
case gosocks5.Ver5: |
||||
|
methods, err := gosocks5.ReadMethods(br) |
||||
|
if err != nil { |
||||
|
glog.V(LWARNING).Infoln("[socks5]", err) |
||||
|
return |
||||
|
} |
||||
|
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) |
||||
|
|
||||
|
default: // http
|
||||
|
req, err := http.ReadRequest(br) |
||||
|
if err != nil { |
||||
|
glog.V(LWARNING).Infoln("[http]", err) |
||||
|
return |
||||
|
} |
||||
|
NewHttpServer(conn, s).HandleRequest(req) |
||||
|
} |
||||
} |
} |
||||
|
|||||
Loading…
Reference in new issue