add proxy auth feature
This commit is contained in:
parent
8e568b6451
commit
6b8d16042e
114
client.go
114
client.go
@ -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 {
|
||||
methods := b[2 : 2+mn]
|
||||
handleSocks5(conn, methods)
|
||||
return
|
||||
}
|
||||
|
||||
handleSocks5(conn)
|
||||
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
|
||||
|
13
main.go
13
main.go
@ -3,7 +3,6 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
//"github.com/ginuerzh/gosocks5"
|
||||
"log"
|
||||
"net/url"
|
||||
"time"
|
||||
@ -19,6 +18,7 @@ var (
|
||||
PrintVersion bool
|
||||
|
||||
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))
|
||||
}
|
||||
|
50
socks5.go
50
socks5.go
@ -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()
|
||||
|
||||
|
78
util.go
78
util.go
@ -29,10 +29,11 @@ const (
|
||||
MethodTLSAuth
|
||||
)
|
||||
|
||||
var ErrEmptyPassword = errors.New("empty key")
|
||||
var ErrEmptyAuth = errors.New("empty auth")
|
||||
|
||||
var Methods = map[uint8]string{
|
||||
gosocks5.MethodNoAuth: "", // 0x00
|
||||
//gosocks5.MethodNoAuth: "", // 0x00
|
||||
gosocks5.MethodUserPass: "userpass", // 0x02
|
||||
MethodTLS: "tls", // 0x80
|
||||
MethodAES128: "aes-128-cfb", // 0x81
|
||||
MethodAES192: "aes-192-cfb", // 0x82
|
||||
@ -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",
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "1.6"
|
||||
Version = "1.7"
|
||||
)
|
||||
|
||||
func printVersion() {
|
||||
|
Loading…
Reference in New Issue
Block a user