649 lines
13 KiB
Go
649 lines
13 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/tls"
|
|
"errors"
|
|
"github.com/ginuerzh/gosocks5"
|
|
"github.com/golang/glog"
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
MethodTLS uint8 = 0x80 // extended method for tls
|
|
MethodTLSAuth uint8 = 0x82 // extended method for tls+auth
|
|
)
|
|
|
|
type clientSelector struct {
|
|
methods []uint8
|
|
arg Args
|
|
}
|
|
|
|
func (selector *clientSelector) Methods() []uint8 {
|
|
return selector.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, &tls.Config{InsecureSkipVerify: true})
|
|
|
|
case gosocks5.MethodUserPass, MethodTLSAuth:
|
|
if method == MethodTLSAuth {
|
|
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
|
}
|
|
|
|
var username, password string
|
|
if selector.arg.User != nil {
|
|
username = selector.arg.User.Username()
|
|
password, _ = selector.arg.User.Password()
|
|
}
|
|
|
|
req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password)
|
|
if err := req.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 auth:", err)
|
|
}
|
|
return nil, err
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(req)
|
|
}
|
|
|
|
res, err := gosocks5.ReadUserPassResponse(conn)
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 auth:", err)
|
|
}
|
|
return nil, err
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(res)
|
|
}
|
|
|
|
if res.Status != gosocks5.Succeeded {
|
|
return nil, gosocks5.ErrAuthFailure
|
|
}
|
|
case gosocks5.MethodNoAcceptable:
|
|
return nil, gosocks5.ErrBadMethod
|
|
}
|
|
|
|
return conn, nil
|
|
}
|
|
|
|
type serverSelector struct {
|
|
methods []uint8
|
|
arg Args
|
|
}
|
|
|
|
func (selector *serverSelector) Methods() []uint8 {
|
|
return selector.methods
|
|
}
|
|
|
|
func (selector *serverSelector) Select(methods ...uint8) (method uint8) {
|
|
if glog.V(LDEBUG) {
|
|
glog.Infof("%d %d % d", 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.arg.User != 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) {
|
|
if glog.V(LDEBUG) {
|
|
glog.Infof("%d %d", gosocks5.Ver5, method)
|
|
}
|
|
|
|
switch method {
|
|
case MethodTLS:
|
|
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.arg.Cert}})
|
|
|
|
case gosocks5.MethodUserPass, MethodTLSAuth:
|
|
if method == MethodTLSAuth {
|
|
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.arg.Cert}})
|
|
}
|
|
|
|
req, err := gosocks5.ReadUserPassRequest(conn)
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 auth:", err)
|
|
}
|
|
return nil, err
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(req.String())
|
|
}
|
|
|
|
var username, password string
|
|
if selector.arg.User != nil {
|
|
username = selector.arg.User.Username()
|
|
password, _ = selector.arg.User.Password()
|
|
}
|
|
|
|
if (username != "" && req.Username != username) || (password != "" && req.Password != password) {
|
|
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)
|
|
if err := resp.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 auth:", err)
|
|
}
|
|
return nil, err
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(resp)
|
|
}
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5: proxy authentication required")
|
|
}
|
|
return nil, gosocks5.ErrAuthFailure
|
|
}
|
|
|
|
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded)
|
|
if err := resp.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 auth:", err)
|
|
}
|
|
return nil, err
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(resp)
|
|
}
|
|
|
|
case gosocks5.MethodNoAcceptable:
|
|
return nil, gosocks5.ErrBadMethod
|
|
}
|
|
|
|
return conn, nil
|
|
}
|
|
|
|
func handleSocks5Request(req *gosocks5.Request, conn net.Conn) {
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(req)
|
|
}
|
|
|
|
switch req.Cmd {
|
|
case gosocks5.CmdConnect:
|
|
if glog.V(LINFO) {
|
|
glog.Infoln("socks5 connect:", req.Addr.String())
|
|
}
|
|
tconn, err := Connect(req.Addr.String())
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 connect:", err)
|
|
}
|
|
rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)
|
|
if err := rep.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 connect:", err)
|
|
}
|
|
} else {
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
defer tconn.Close()
|
|
|
|
rep := gosocks5.NewReply(gosocks5.Succeeded, nil)
|
|
if err := rep.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 connect:", err)
|
|
}
|
|
return
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
|
|
Transport(conn, tconn)
|
|
case gosocks5.CmdBind:
|
|
if glog.V(LINFO) {
|
|
glog.Infoln("socks5 bind:", req.Addr)
|
|
}
|
|
if len(forwardArgs) > 0 {
|
|
forwardBind(req, conn)
|
|
} else {
|
|
serveBind(conn)
|
|
}
|
|
case gosocks5.CmdUdp:
|
|
if glog.V(LINFO) {
|
|
glog.Infoln("socks5 udp associate:", req.Addr)
|
|
}
|
|
uconn, err := net.ListenUDP("udp", nil)
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 udp listen:", err)
|
|
}
|
|
rep := gosocks5.NewReply(gosocks5.Failure, nil)
|
|
if err := rep.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 udp listen:", err)
|
|
}
|
|
} else {
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
defer uconn.Close()
|
|
|
|
addr := ToSocksAddr(uconn.LocalAddr())
|
|
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
|
if glog.V(LINFO) {
|
|
glog.Infoln("socks5 udp listen:", addr)
|
|
}
|
|
rep := gosocks5.NewReply(gosocks5.Succeeded, addr)
|
|
if err := rep.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 udp:", err)
|
|
}
|
|
return
|
|
} else {
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
}
|
|
|
|
//clientConn, dgram, err := createClientConn(conn, uconn)
|
|
_, dgram, err := createClientConn(conn, uconn)
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 udp:", err)
|
|
}
|
|
return
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infof("[udp] length %d, to %s", len(dgram.Data), dgram.Header.Addr)
|
|
}
|
|
|
|
//serverConn, err := createServerConn(uconn)
|
|
_, err = createServerConn(uconn)
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 udp forward:", err)
|
|
}
|
|
}
|
|
default:
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("Unrecognized request: ", req)
|
|
}
|
|
}
|
|
}
|
|
|
|
func createClientConn(conn net.Conn, uconn *net.UDPConn) (c *UDPConn, dgram *gosocks5.UDPDatagram, err error) {
|
|
var raddr *net.UDPAddr
|
|
dgramChan := make(chan *gosocks5.UDPDatagram, 1)
|
|
errChan := make(chan error, 1)
|
|
go func() {
|
|
b := make([]byte, 64*1024+262)
|
|
|
|
n, addr, err := uconn.ReadFromUDP(b)
|
|
if err != nil {
|
|
errChan <- err
|
|
return
|
|
}
|
|
raddr = addr
|
|
|
|
dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))
|
|
if err != nil {
|
|
errChan <- err
|
|
return
|
|
}
|
|
dgramChan <- dgram
|
|
}()
|
|
|
|
go func() {
|
|
dgram, err := gosocks5.ReadUDPDatagram(conn)
|
|
if err != nil {
|
|
errChan <- err
|
|
return
|
|
}
|
|
dgramChan <- dgram
|
|
}()
|
|
|
|
select {
|
|
case dgram = <-dgramChan:
|
|
if raddr != nil {
|
|
c = Client(uconn, raddr)
|
|
} else {
|
|
c = Client(conn, nil)
|
|
}
|
|
case err = <-errChan:
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func createServerConn(uconn *net.UDPConn) (c *UDPConn, err error) {
|
|
if len(forwardArgs) == 0 {
|
|
c = Server(uconn)
|
|
return
|
|
}
|
|
|
|
fconn, _, err := forwardChain(forwardArgs...)
|
|
if err != nil {
|
|
if fconn != nil {
|
|
fconn.Close()
|
|
}
|
|
return
|
|
}
|
|
|
|
c = Server(fconn)
|
|
return
|
|
}
|
|
|
|
/*
|
|
func forwardUDP(req *gosocks5.Request) (conn net.Conn, err error) {
|
|
|
|
if err != nil {
|
|
if conn != nil {
|
|
conn.Close()
|
|
}
|
|
rep := gosocks5.NewReply(gosocks5.Failure, nil)
|
|
if err := rep.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 udp forward:", err)
|
|
}
|
|
} else {
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
if err = req.Write(fconn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 udp forward:", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
if err = peekReply(conn, fconn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 udp forward:", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
}
|
|
*/
|
|
func serveBind(conn net.Conn) error {
|
|
l, err := net.ListenTCP("tcp", nil)
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 bind listen:", err)
|
|
}
|
|
rep := gosocks5.NewReply(gosocks5.Failure, nil)
|
|
if err := rep.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 bind listen:", err)
|
|
}
|
|
} else {
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
addr := ToSocksAddr(l.Addr())
|
|
// Issue: may not reachable when host has two interfaces
|
|
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
|
if glog.V(LINFO) {
|
|
glog.Infoln("socks5 bind:", addr)
|
|
}
|
|
rep := gosocks5.NewReply(gosocks5.Succeeded, addr)
|
|
if err := rep.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 bind:", err)
|
|
}
|
|
l.Close()
|
|
return err
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
|
|
tconn, err := l.AcceptTCP()
|
|
l.Close() // only accept one peer
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 bind accept:", err)
|
|
}
|
|
rep = gosocks5.NewReply(gosocks5.Failure, nil)
|
|
if err := rep.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 bind accept:", err)
|
|
}
|
|
} else {
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
defer tconn.Close()
|
|
|
|
addr = ToSocksAddr(tconn.RemoteAddr())
|
|
if glog.V(LINFO) {
|
|
glog.Infoln("socks5 bind accept:", addr.String())
|
|
}
|
|
rep = gosocks5.NewReply(gosocks5.Succeeded, addr)
|
|
if err := rep.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 bind accept:", err)
|
|
}
|
|
return err
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
|
|
return Transport(conn, tconn)
|
|
}
|
|
|
|
func forwardBind(req *gosocks5.Request, conn net.Conn) error {
|
|
fconn, _, err := forwardChain(forwardArgs...)
|
|
if err != nil {
|
|
if fconn != nil {
|
|
fconn.Close()
|
|
}
|
|
rep := gosocks5.NewReply(gosocks5.Failure, nil)
|
|
if err := rep.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 bind forward:", err)
|
|
}
|
|
} else {
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
defer fconn.Close()
|
|
|
|
if err := req.Write(fconn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 bind forward:", err)
|
|
}
|
|
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)
|
|
return err
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(req)
|
|
}
|
|
|
|
// first reply
|
|
if err := peekReply(conn, fconn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 bind forward:", err)
|
|
}
|
|
return err
|
|
}
|
|
// second reply
|
|
if err := peekReply(conn, fconn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln("socks5 bind forward:", err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
return Transport(conn, fconn)
|
|
}
|
|
|
|
func peekReply(dst io.Writer, src io.Reader) error {
|
|
rep, err := gosocks5.ReadReply(src)
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln(err)
|
|
}
|
|
rep = gosocks5.NewReply(gosocks5.Failure, nil)
|
|
}
|
|
if err := rep.Write(dst); err != nil {
|
|
return err
|
|
}
|
|
if glog.V(LDEBUG) {
|
|
glog.Infoln(rep)
|
|
}
|
|
if rep.Rep != gosocks5.Succeeded {
|
|
return errors.New("Failure")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
func cliTunnelUDP(uconn *net.UDPConn, sconn net.Conn) {
|
|
var raddr *net.UDPAddr
|
|
|
|
go func() {
|
|
b := make([]byte, 16*1024)
|
|
for {
|
|
n, addr, err := uconn.ReadFromUDP(b)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
raddr = addr
|
|
r := bytes.NewBuffer(b[:n])
|
|
udp, err := gosocks5.ReadUDPDatagram(r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
udp.Header.Rsv = uint16(len(udp.Data))
|
|
//log.Println("r", raddr.String(), udp.Header)
|
|
|
|
if err := udp.Write(sconn); err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
for {
|
|
b := lpool.Take()
|
|
defer lpool.put(b)
|
|
|
|
udp, err := gosocks5.ReadUDPDatagram(sconn)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
//log.Println("w", udp.Header)
|
|
udp.Header.Rsv = 0
|
|
buf := bytes.NewBuffer(b[0:0])
|
|
udp.Write(buf)
|
|
if _, err := uconn.WriteTo(buf.Bytes(), raddr); err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func srvTunnelUDP(conn net.Conn, uconn *net.UDPConn) {
|
|
go func() {
|
|
b := make([]byte, 16*1024)
|
|
|
|
for {
|
|
n, addr, err := uconn.ReadFromUDP(b)
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln(err)
|
|
}
|
|
return
|
|
}
|
|
|
|
udp := gosocks5.NewUDPDatagram(
|
|
gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n])
|
|
//log.Println("r", udp.Header)
|
|
if err := udp.Write(conn); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln(err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
for {
|
|
udp, err := gosocks5.ReadUDPDatagram(conn)
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln(err)
|
|
}
|
|
return
|
|
}
|
|
//log.Println("w", udp.Header)
|
|
addr, err := net.ResolveUDPAddr("udp", udp.Header.Addr.String())
|
|
if err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln(err)
|
|
}
|
|
continue // drop silently
|
|
}
|
|
|
|
if _, err := uconn.WriteToUDP(udp.Data, addr); err != nil {
|
|
if glog.V(LWARNING) {
|
|
glog.Warningln(err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
|
|
host, port, _ := net.SplitHostPort(addr.String())
|
|
p, _ := strconv.Atoi(port)
|
|
|
|
return &gosocks5.Addr{
|
|
Type: gosocks5.AddrIPv4,
|
|
Host: host,
|
|
Port: uint16(p),
|
|
}
|
|
}
|