add proxy auth feature

This commit is contained in:
rui.zheng 2015-06-26 18:10:26 +08:00
parent 8e568b6451
commit 6b8d16042e
5 changed files with 190 additions and 91 deletions

114
client.go
View File

@ -5,6 +5,7 @@ import (
"bytes"
"crypto/tls"
"encoding/binary"
"errors"
"fmt"
"github.com/ginuerzh/gosocks5"
"github.com/gorilla/websocket"
@ -58,10 +59,18 @@ func listenAndServe(addr string, handler func(net.Conn)) error {
func clientMethodSelected(method uint8, conn net.Conn) (net.Conn, error) {
switch method {
case gosocks5.MethodUserPass:
user, pass := parseUserPass(Password)
if err := clientSocksAuth(conn, user, pass); err != nil {
return nil, err
}
case MethodTLS, MethodTLSAuth:
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
if method == MethodTLSAuth {
if err := cliTLSAuth(conn); err != nil {
if len(Password) == 0 {
return nil, ErrEmptyAuth
}
if err := clientSocksAuth(conn, "", Password); err != nil {
return nil, err
}
}
@ -80,26 +89,6 @@ func clientMethodSelected(method uint8, conn net.Conn) (net.Conn, error) {
return conn, nil
}
func cliTLSAuth(conn net.Conn) error {
if len(Password) == 0 {
return ErrEmptyPassword
}
if err := gosocks5.NewUserPassRequest(
gosocks5.UserPassVer, "", Password).Write(conn); err != nil {
return err
}
res, err := gosocks5.ReadUserPassResponse(conn)
if err != nil {
return err
}
if res.Status != gosocks5.Succeeded {
return gosocks5.ErrAuthFailure
}
return nil
}
func makeTunnel() (c net.Conn, err error) {
if UseWebsocket || !UseHttp {
c, err = connect(Saddr)
@ -161,21 +150,19 @@ func cliHandle(conn net.Conn) {
}
if b[0] == gosocks5.Ver5 {
length := 2 + int(b[1])
mn := int(b[1]) // methods count
length := 2 + mn
if n < length {
if _, err := io.ReadFull(conn, b[n:length]); err != nil {
return
}
}
if err := gosocks5.WriteMethod(gosocks5.MethodNoAuth, conn); err != nil {
return
}
handleSocks5(conn)
methods := b[2 : 2+mn]
handleSocks5(conn, methods)
return
}
log.Println(string(b[:n]))
for {
if bytes.HasSuffix(b[:n], []byte("\r\n\r\n")) {
break
@ -197,7 +184,51 @@ func cliHandle(conn net.Conn) {
handleHttp(req, conn)
}
func handleSocks5(conn net.Conn) {
func selectMethod(conn net.Conn, methods ...uint8) error {
m := gosocks5.MethodNoAuth
if listenUrl.User != nil {
for _, method := range methods {
if method == gosocks5.MethodUserPass {
m = method
break
}
}
if m != gosocks5.MethodUserPass {
m = gosocks5.MethodNoAcceptable
}
}
if err := gosocks5.WriteMethod(m, conn); err != nil {
return err
}
log.Println(m)
switch m {
case gosocks5.MethodUserPass:
var username, password string
if listenUrl != nil && listenUrl.User != nil {
username = listenUrl.User.Username()
password, _ = listenUrl.User.Password()
}
if err := serverSocksAuth(conn, username, password); err != nil {
return err
}
case gosocks5.MethodNoAcceptable:
return gosocks5.ErrBadMethod
}
return nil
}
func handleSocks5(conn net.Conn, methods []uint8) {
if err := selectMethod(conn, methods...); err != nil {
log.Println(err)
return
}
req, err := gosocks5.ReadRequest(conn)
if err != nil {
return
@ -301,10 +332,35 @@ func cliTunnelUDP(uconn *net.UDPConn, sconn net.Conn) {
}
}
func clientHttpAuth(req *http.Request, conn net.Conn, username, password string) error {
u, p, ok := req.BasicAuth()
if !ok ||
(len(username) > 0 && u != username) ||
(len(password) > 0 && p != password) {
conn.Write([]byte("HTTP/1.1 401 Not Authorized\r\n" +
"WWW-Authenticate: Basic realm=\"Authorization Required\"\r\n" +
"Proxy-Agent: gost/" + Version + "\r\n\r\n"))
return errors.New("Not Authorized")
}
return nil
}
func handleHttp(req *http.Request, conn net.Conn) {
var host string
var port uint16
if listenUrl != nil && listenUrl.User != nil {
username := listenUrl.User.Username()
password, _ := listenUrl.User.Password()
if err := clientHttpAuth(req, conn, username, password); err != nil {
log.Println(err)
return
}
}
s := strings.Split(req.Host, ":")
host = s[0]
port = 80

15
main.go
View File

@ -3,7 +3,6 @@ package main
import (
"flag"
//"github.com/ginuerzh/gosocks5"
"log"
"net/url"
"time"
@ -18,7 +17,8 @@ var (
CertFile, KeyFile string
PrintVersion bool
proxyURL *url.URL
proxyURL *url.URL
listenUrl *url.URL
)
func init() {
@ -40,6 +40,7 @@ func init() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
proxyURL, _ = parseURL(Proxy)
listenUrl, _ = parseURL(Laddr)
}
var (
@ -54,18 +55,20 @@ func main() {
return
}
laddr := listenUrl.Host
if len(Saddr) == 0 {
var server Server
if UseWebsocket {
server = &WSServer{Addr: Laddr}
server = &WSServer{Addr: laddr}
} else if UseHttp {
server = &HttpServer{Addr: Laddr}
server = &HttpServer{Addr: laddr}
} else {
server = &Socks5Server{Addr: Laddr}
server = &Socks5Server{Addr: laddr}
}
log.Fatal(server.ListenAndServe())
return
}
log.Fatal(listenAndServe(Laddr, cliHandle))
log.Fatal(listenAndServe(laddr, cliHandle))
}

View File

@ -94,6 +94,7 @@ func (s *Socks5Server) ListenAndServe() error {
}
func serverSelectMethod(methods ...uint8) uint8 {
log.Println(methods)
m := gosocks5.MethodNoAuth
for _, method := range methods {
@ -102,6 +103,11 @@ func serverSelectMethod(methods ...uint8) uint8 {
}
}
// when user/pass is set for proxy auth, the NoAuth method is disabled
if len(Method) == 0 && m == gosocks5.MethodNoAuth && listenUrl.User != nil {
return gosocks5.MethodNoAcceptable
}
if len(Method) == 0 || Methods[m] == Method {
return m
}
@ -110,7 +116,19 @@ func serverSelectMethod(methods ...uint8) uint8 {
}
func serverMethodSelected(method uint8, conn net.Conn) (net.Conn, error) {
log.Println(method)
switch method {
case gosocks5.MethodUserPass:
var username, password string
if listenUrl != nil && listenUrl.User != nil {
username = listenUrl.User.Username()
password, _ = listenUrl.User.Password()
}
if err := serverSocksAuth(conn, username, password); err != nil {
return nil, err
}
case MethodTLS, MethodTLSAuth:
var cert tls.Certificate
var err error
@ -126,7 +144,11 @@ func serverMethodSelected(method uint8, conn net.Conn) (net.Conn, error) {
}
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{cert}})
if method == MethodTLSAuth {
if err := svrTLSAuth(conn); err != nil {
// password is mandatory
if len(Password) == 0 {
return nil, ErrEmptyAuth
}
if err := serverSocksAuth(conn, "", Password); err != nil {
return nil, err
}
}
@ -144,32 +166,6 @@ func serverMethodSelected(method uint8, conn net.Conn) (net.Conn, error) {
return conn, nil
}
func svrTLSAuth(conn net.Conn) error {
if len(Password) == 0 {
return ErrEmptyPassword
}
req, err := gosocks5.ReadUserPassRequest(conn)
if err != nil {
return err
}
if req.Password != Password {
if err := gosocks5.NewUserPassResponse(
gosocks5.UserPassVer, gosocks5.Failure).Write(conn); err != nil {
return err
}
return gosocks5.ErrAuthFailure
}
if err := gosocks5.NewUserPassResponse(
gosocks5.UserPassVer, gosocks5.Succeeded).Write(conn); err != nil {
return err
}
return nil
}
func serveSocks5(conn net.Conn) {
defer conn.Close()

100
util.go
View File

@ -29,21 +29,22 @@ const (
MethodTLSAuth
)
var ErrEmptyPassword = errors.New("empty key")
var ErrEmptyAuth = errors.New("empty auth")
var Methods = map[uint8]string{
gosocks5.MethodNoAuth: "", // 0x00
MethodTLS: "tls", // 0x80
MethodAES128: "aes-128-cfb", // 0x81
MethodAES192: "aes-192-cfb", // 0x82
MethodAES256: "aes-256-cfb", // 0x83
MethodDES: "des-cfb", // 0x84
MethodBF: "bf-cfb", // 0x85
MethodCAST5: "cast5-cfb", // 0x86
MethodRC4MD5: "rc4-md5", // 8x87
MethodRC4: "rc4", // 0x88
MethodTable: "table", // 0x89
MethodTLSAuth: "tls-auth", // 0x90
//gosocks5.MethodNoAuth: "", // 0x00
gosocks5.MethodUserPass: "userpass", // 0x02
MethodTLS: "tls", // 0x80
MethodAES128: "aes-128-cfb", // 0x81
MethodAES192: "aes-192-cfb", // 0x82
MethodAES256: "aes-256-cfb", // 0x83
MethodDES: "des-cfb", // 0x84
MethodBF: "bf-cfb", // 0x85
MethodCAST5: "cast5-cfb", // 0x86
MethodRC4MD5: "rc4-md5", // 8x87
MethodRC4: "rc4", // 0x88
MethodTable: "table", // 0x89
MethodTLSAuth: "tls-auth", // 0x90
}
func parseURL(rawurl string) (*url.URL, error) {
@ -57,6 +58,15 @@ func parseURL(rawurl string) (*url.URL, error) {
return url.Parse(rawurl)
}
func parseUserPass(key string) (username string, password string) {
sep := ":"
i := strings.Index(key, sep)
if i < 0 {
return key, ""
}
return key[0:i], key[i+len(sep):]
}
func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
host, port, _ := net.SplitHostPort(addr.String())
p, _ := strconv.Atoi(port)
@ -135,9 +145,12 @@ func connectSocks5Proxy(addr string) (conn net.Conn, err error) {
}
conf := &gosocks5.Config{
Methods: []uint8{gosocks5.MethodNoAuth, gosocks5.MethodUserPass},
// Methods: []uint8{gosocks5.MethodNoAuth, gosocks5.MethodUserPass},
MethodSelected: proxyMethodSelected,
}
if proxyURL.User != nil {
conf.Methods = []uint8{gosocks5.MethodUserPass}
}
c := gosocks5.ClientConn(conn, conf)
if err := c.Handleshake(); err != nil {
@ -178,30 +191,61 @@ func connectSocks5Proxy(addr string) (conn net.Conn, err error) {
func proxyMethodSelected(method uint8, conn net.Conn) (net.Conn, error) {
switch method {
case gosocks5.MethodUserPass:
if proxyURL == nil || proxyURL.User == nil {
return nil, gosocks5.ErrAuthFailure
var user, pass string
if proxyURL != nil && proxyURL.User != nil {
user = proxyURL.User.Username()
pass, _ = proxyURL.User.Password()
}
pwd, _ := proxyURL.User.Password()
if err := gosocks5.NewUserPassRequest(gosocks5.UserPassVer,
proxyURL.User.Username(), pwd).Write(conn); err != nil {
if err := clientSocksAuth(conn, user, pass); err != nil {
return nil, err
}
resp, err := gosocks5.ReadUserPassResponse(conn)
if err != nil {
return nil, err
}
if resp.Status != gosocks5.Succeeded {
return nil, gosocks5.ErrAuthFailure
}
case gosocks5.MethodNoAcceptable:
return nil, gosocks5.ErrBadMethod
//case gosocks5.MethodNoAuth:
}
return conn, nil
}
func clientSocksAuth(conn net.Conn, username, password string) error {
if err := gosocks5.NewUserPassRequest(
gosocks5.UserPassVer, username, password).Write(conn); err != nil {
return err
}
res, err := gosocks5.ReadUserPassResponse(conn)
if err != nil {
return err
}
if res.Status != gosocks5.Succeeded {
return gosocks5.ErrAuthFailure
}
return nil
}
func serverSocksAuth(conn net.Conn, username, password string) error {
req, err := gosocks5.ReadUserPassRequest(conn)
if err != nil {
return err
}
if (len(username) > 0 && req.Username != username) ||
(len(password) > 0 && req.Password != password) {
if err := gosocks5.NewUserPassResponse(
gosocks5.UserPassVer, gosocks5.Failure).Write(conn); err != nil {
return err
}
return gosocks5.ErrAuthFailure
}
if err := gosocks5.NewUserPassResponse(
gosocks5.UserPassVer, gosocks5.Succeeded).Write(conn); err != nil {
return err
}
return nil
}
func setBasicAuth(r *http.Request) {
if proxyURL != nil && proxyURL.User != nil {
r.Header.Set("Proxy-Authorization",

View File

@ -5,7 +5,7 @@ import (
)
const (
Version = "1.6"
Version = "1.7"
)
func printVersion() {