diff --git a/lib/common/const.go b/lib/common/const.go index ffb2fa60..f7097e0b 100644 --- a/lib/common/const.go +++ b/lib/common/const.go @@ -27,11 +27,15 @@ const ( CONN_TCP = "tcp" CONN_UDP = "udp" CONN_TEST = "TST" - UnauthorizedBytes = `HTTP/1.1 401 Unauthorized -Content-Type: text/plain; charset=utf-8 -WWW-Authenticate: Basic realm="easyProxy" + UnauthorizedBytes = `HTTP/1.1 407 Proxy Authentication Required +Server: Proxy +Proxy-Authenticate: Basic realm="easyProxy Authentication" +Connection: Close +Proxy-Connection: Close +Content-Length: 0 + +` -401 Unauthorized` ConnectionFailBytes = `HTTP/1.1 404 Not Found ` diff --git a/lib/common/util.go b/lib/common/util.go index cb87daa1..895316fc 100755 --- a/lib/common/util.go +++ b/lib/common/util.go @@ -7,6 +7,7 @@ import ( "encoding/binary" "errors" "fmt" + "github.com/astaxie/beego/logs" "html/template" "io" "io/ioutil" @@ -49,8 +50,79 @@ func DomainCheck(domain string) bool { return match } +// 判断是否有有效的账号 +func hasValidAccount(accountMap map[string]string) bool { + if accountMap == nil { + return false + } + + for u, p := range accountMap { + if u != "" && p != "" { + return true + } + } + return false +} + +// 判断是否需要验证 +// user global user +// passwd global passwd +// accountMap enable multi user auth +func HasValid(user, passwd string, accountMap map[string]string) bool { + return hasValidAccount(accountMap) || (user != "" && passwd != "") +} + +// CheckAuthWithAccountMap +// u current login user +// p current login passwd +// user global user +// passwd global passwd +// accountMap enable multi user auth +func checkAuthWithAccountMap(u, p, user, passwd string, accountMap map[string]string) bool { + // 是否需要验证 + if !HasValid(user, passwd, accountMap) { + return true + } + + // invalid user or passwd + if u == "" || p == "" { + return false + } + + // global user auth + if u == user && p == passwd { + return true + } + + // multi user auth + if accountMap == nil { + return false + } + + return accountMap[u] == p +} + +// CheckAuthWithAccountMap +// u current login user +// p current login passwd +// user global user +// passwd global passwd +// accountMap enable multi user auth +func CheckAuthWithAccountMap(u, p, user, passwd string, accountMap map[string]string) bool { + isValid := checkAuthWithAccountMap(u, p, user, passwd, accountMap) + if !isValid { + logs.Info("账号验证失败") + } + return isValid +} + //Check if the Request request is validated -func CheckAuth(r *http.Request, user, passwd string) bool { +func CheckAuth(r *http.Request, user, passwd string, accountMap map[string]string) bool { + // 是否需要验证 + if !HasValid(user, passwd, accountMap) { + return true + } + s := strings.SplitN(r.Header.Get("Authorization"), " ", 2) if len(s) != 2 { s = strings.SplitN(r.Header.Get("Proxy-Authorization"), " ", 2) @@ -68,7 +140,8 @@ func CheckAuth(r *http.Request, user, passwd string) bool { if len(pair) != 2 { return false } - return pair[0] == user && pair[1] == passwd + + return CheckAuthWithAccountMap(pair[0], pair[1], user, passwd, accountMap) } //get bool by str diff --git a/server/proxy/base.go b/server/proxy/base.go index 7df5921c..58e240ff 100644 --- a/server/proxy/base.go +++ b/server/proxy/base.go @@ -4,6 +4,7 @@ import ( "errors" "net" "net/http" + "strings" "sync" "ehang.io/nps/bridge" @@ -63,11 +64,13 @@ func (s *BaseServer) writeConnFail(c net.Conn) { } //auth check -func (s *BaseServer) auth(r *http.Request, c *conn.Conn, u, p string) error { - if u != "" && p != "" && !common.CheckAuth(r, u, p) { - c.Write([]byte(common.UnauthorizedBytes)) +func (s *BaseServer) auth(r *http.Request, c *conn.Conn, u, p string, AccountMap map[string]string) error { + if !common.CheckAuth(r, u, p, AccountMap) { + var resp = common.UnauthorizedBytes + resp = strings.ReplaceAll(resp, "\n", "\r\n") + c.Write([]byte(resp)) c.Close() - return errors.New("401 Unauthorized") + return errors.New("407 Unauthorized") } return nil } diff --git a/server/proxy/http.go b/server/proxy/http.go index 3bc1e6ed..b26aed7c 100644 --- a/server/proxy/http.go +++ b/server/proxy/http.go @@ -150,7 +150,7 @@ reset: if !isReset { defer host.Client.AddConn() } - if err = s.auth(r, c, host.Client.Cnf.U, host.Client.Cnf.P); err != nil { + if err = s.auth(r, c, host.Client.Cnf.U, host.Client.Cnf.P, s.task.MultiAccount.AccountMap); err != nil { logs.Warn("auth error", err, r.RemoteAddr) return } diff --git a/server/proxy/https.go b/server/proxy/https.go index 3f0be1d9..764e0e73 100644 --- a/server/proxy/https.go +++ b/server/proxy/https.go @@ -117,7 +117,7 @@ func (https *HttpsServer) handleHttps(c net.Conn) { return } defer host.Client.AddConn() - if err = https.auth(r, conn.NewConn(c), host.Client.Cnf.U, host.Client.Cnf.P); err != nil { + if err = https.auth(r, conn.NewConn(c), host.Client.Cnf.U, host.Client.Cnf.P, https.task.MultiAccount.AccountMap); err != nil { logs.Warn("auth error", err, r.RemoteAddr) return } diff --git a/server/proxy/socks5.go b/server/proxy/socks5.go index 3faefe54..52d1c76c 100755 --- a/server/proxy/socks5.go +++ b/server/proxy/socks5.go @@ -302,7 +302,13 @@ func (s *Sock5ModeServer) handleConn(c net.Conn) { c.Close() return } - if (s.task.Client.Cnf.U != "" && s.task.Client.Cnf.P != "") || (s.task.MultiAccount != nil && len(s.task.MultiAccount.AccountMap) > 0) { + + var accountMap map[string]string = nil + if s.task.MultiAccount != nil { + accountMap = s.task.MultiAccount.AccountMap + } + + if common.HasValid(s.task.Client.Cnf.U, s.task.Client.Cnf.P, accountMap) { buf[1] = UserPassAuth c.Write(buf) if err := s.Auth(c); err != nil { @@ -340,21 +346,7 @@ func (s *Sock5ModeServer) Auth(c net.Conn) error { return err } - var U, P string - if s.task.MultiAccount != nil { - // enable multi user auth - U = string(user) - var ok bool - P, ok = s.task.MultiAccount.AccountMap[U] - if !ok { - return errors.New("验证不通过") - } - } else { - U = s.task.Client.Cnf.U - P = s.task.Client.Cnf.P - } - - if string(user) == U && string(pass) == P { + if common.CheckAuthWithAccountMap(string(user), string(pass), s.task.Client.Cnf.U, s.task.Client.Cnf.P, s.task.MultiAccount.AccountMap) { if _, err := c.Write([]byte{userAuthVersion, authSuccess}); err != nil { return err } diff --git a/server/proxy/tcp.go b/server/proxy/tcp.go index 58ce3e0c..d7992017 100755 --- a/server/proxy/tcp.go +++ b/server/proxy/tcp.go @@ -40,8 +40,11 @@ func (s *TunnelModeServer) Start() error { return } logs.Trace("new tcp connection,local port %d,client %d,remote address %s", s.task.Port, s.task.Client.Id, c.RemoteAddr()) - s.process(conn.NewConn(c), s) - s.task.Client.AddConn() + err := s.process(conn.NewConn(c), s) + if err == nil { + s.task.Client.AddConn() + } + }, &s.listener) } @@ -114,12 +117,14 @@ func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error { logs.Info(err) return err } + + if err := s.auth(r, c, s.task.Client.Cnf.U, s.task.Client.Cnf.P, s.task.MultiAccount.AccountMap); err != nil { + return err + } + if r.Method == "CONNECT" { c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) rb = nil } - if err := s.auth(r, c, s.task.Client.Cnf.U, s.task.Client.Cnf.P); err != nil { - return err - } return s.DealClient(c, s.task.Client, addr, rb, common.CONN_TCP, nil, s.task.Flow, s.task.Target.LocalProxy) }