165 lines
3.9 KiB
Go
165 lines
3.9 KiB
Go
package gost
|
|
|
|
import (
|
|
//"bytes"
|
|
"crypto/tls"
|
|
//"errors"
|
|
"github.com/ginuerzh/gosocks5"
|
|
"github.com/golang/glog"
|
|
//"os/exec"
|
|
//"io"
|
|
//"io/ioutil"
|
|
"net"
|
|
"net/url"
|
|
//"strconv"
|
|
//"time"
|
|
)
|
|
|
|
const (
|
|
MethodTLS uint8 = 0x80 // extended method for tls
|
|
MethodTLSAuth uint8 = 0x82 // extended method for tls+auth
|
|
)
|
|
|
|
const (
|
|
CmdUdpConnect uint8 = 0xF1 // extended method for udp local port forwarding
|
|
CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp
|
|
)
|
|
|
|
type clientSelector struct {
|
|
methods []uint8
|
|
user *url.Userinfo
|
|
}
|
|
|
|
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.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.V(LWARNING).Infoln("socks5 auth:", err)
|
|
return nil, err
|
|
}
|
|
glog.V(LDEBUG).Infoln(req)
|
|
|
|
resp, err := gosocks5.ReadUserPassResponse(conn)
|
|
if err != nil {
|
|
glog.V(LWARNING).Infoln("socks5 auth:", err)
|
|
return nil, err
|
|
}
|
|
glog.V(LDEBUG).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
|
|
user *url.Userinfo
|
|
cert tls.Certificate
|
|
}
|
|
|
|
func (selector *serverSelector) Methods() []uint8 {
|
|
return selector.methods
|
|
}
|
|
|
|
func (selector *serverSelector) Select(methods ...uint8) (method uint8) {
|
|
glog.V(LDEBUG).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.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) {
|
|
glog.V(LDEBUG).Infof("%d %d", gosocks5.Ver5, method)
|
|
|
|
switch method {
|
|
case MethodTLS:
|
|
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.cert}})
|
|
|
|
case gosocks5.MethodUserPass, MethodTLSAuth:
|
|
if method == MethodTLSAuth {
|
|
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.cert}})
|
|
}
|
|
|
|
req, err := gosocks5.ReadUserPassRequest(conn)
|
|
if err != nil {
|
|
glog.V(LWARNING).Infoln("[socks5-auth]", err)
|
|
return nil, err
|
|
}
|
|
glog.V(LDEBUG).Infoln("[socks5]", req.String())
|
|
|
|
var username, password string
|
|
if selector.user != nil {
|
|
username = selector.user.Username()
|
|
password, _ = selector.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 {
|
|
glog.V(LWARNING).Infoln("[socks5-auth]", err)
|
|
return nil, err
|
|
}
|
|
glog.V(LDEBUG).Infoln("[socks5]", resp)
|
|
glog.V(LWARNING).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.V(LWARNING).Infoln("[socks5-auth]", err)
|
|
return nil, err
|
|
}
|
|
glog.V(LDEBUG).Infoln(resp)
|
|
|
|
case gosocks5.MethodNoAcceptable:
|
|
return nil, gosocks5.ErrBadMethod
|
|
}
|
|
|
|
return conn, nil
|
|
}
|