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 |
|||
|
|||
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 { |
|||
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 { |
|||
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