Feature: local socks5/http(s) auth (#216)

This commit is contained in:
bobo liu
2019-06-27 17:04:25 +08:00
committed by Dreamacro
parent 2417cfda12
commit 1c792b46c9
8 changed files with 215 additions and 19 deletions

17
proxy/auth/auth.go Normal file
View File

@ -0,0 +1,17 @@
package auth
import (
"github.com/Dreamacro/clash/component/auth"
)
var (
authenticator auth.Authenticator
)
func Authenticator() auth.Authenticator {
return authenticator
}
func SetAuthenticator(au auth.Authenticator) {
authenticator = au
}

View File

@ -2,11 +2,17 @@ package http
import (
"bufio"
"encoding/base64"
"net"
"net/http"
"strings"
"time"
adapters "github.com/Dreamacro/clash/adapters/inbound"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/component/auth"
"github.com/Dreamacro/clash/log"
authStore "github.com/Dreamacro/clash/proxy/auth"
"github.com/Dreamacro/clash/tunnel"
)
@ -18,6 +24,7 @@ type HttpListener struct {
net.Listener
address string
closed bool
cache *cache.Cache
}
func NewHttpProxy(addr string) (*HttpListener, error) {
@ -25,10 +32,11 @@ func NewHttpProxy(addr string) (*HttpListener, error) {
if err != nil {
return nil, err
}
hl := &HttpListener{l, addr, false}
hl := &HttpListener{l, addr, false, cache.New(30 * time.Second)}
go func() {
log.Infoln("HTTP proxy listening at: %s", addr)
for {
c, err := hl.Accept()
if err != nil {
@ -37,7 +45,7 @@ func NewHttpProxy(addr string) (*HttpListener, error) {
}
continue
}
go handleConn(c)
go handleConn(c, hl.cache)
}
}()
@ -53,7 +61,19 @@ func (l *HttpListener) Address() string {
return l.address
}
func handleConn(conn net.Conn) {
func canActivate(loginStr string, authenticator auth.Authenticator, cache *cache.Cache) (ret bool) {
if result := cache.Get(loginStr); result != nil {
ret = result.(bool)
}
loginData, err := base64.StdEncoding.DecodeString(loginStr)
login := strings.Split(string(loginData), ":")
ret = err == nil && len(login) == 2 && authenticator.Verify(login[0], login[1])
cache.Put(loginStr, ret, time.Minute)
return
}
func handleConn(conn net.Conn, cache *cache.Cache) {
br := bufio.NewReader(conn)
request, err := http.ReadRequest(br)
if err != nil || request.URL.Host == "" {
@ -61,6 +81,20 @@ func handleConn(conn net.Conn) {
return
}
authenticator := authStore.Authenticator()
if authenticator != nil {
if authStrings := strings.Split(request.Header.Get("Proxy-Authorization"), " "); len(authStrings) != 2 {
_, err = conn.Write([]byte("HTTP/1.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic\r\n\r\n"))
conn.Close()
return
} else if !canActivate(authStrings[1], authenticator, cache) {
conn.Write([]byte("HTTP/1.1 403 Forbidden\r\n\r\n"))
log.Infoln("Auth failed from %s", conn.RemoteAddr().String())
conn.Close()
return
}
}
if request.Method == http.MethodConnect {
_, err := conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
if err != nil {

View File

@ -7,6 +7,7 @@ import (
"github.com/Dreamacro/clash/component/socks5"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
authStore "github.com/Dreamacro/clash/proxy/auth"
"github.com/Dreamacro/clash/tunnel"
)
@ -54,7 +55,7 @@ func (l *SockListener) Address() string {
}
func handleSocks(conn net.Conn) {
target, command, err := socks5.ServerHandshake(conn)
target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator())
if err != nil {
conn.Close()
return