Add: selector and proxys & rules router

This commit is contained in:
Dreamacro 2018-07-12 23:28:38 +08:00
parent 39b45513af
commit 0eef9bbf5d
12 changed files with 390 additions and 47 deletions

View File

@ -35,6 +35,10 @@ func (d *Direct) Name() string {
return "Direct" return "Direct"
} }
func (d *Direct) Type() C.AdapterType {
return C.Direct
}
func (d *Direct) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) { func (d *Direct) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) {
c, err := net.Dial("tcp", net.JoinHostPort(addr.String(), addr.Port)) c, err := net.Dial("tcp", net.JoinHostPort(addr.String(), addr.Port))
if err != nil { if err != nil {

View File

@ -31,6 +31,10 @@ func (r *Reject) Name() string {
return "Reject" return "Reject"
} }
func (r *Reject) Type() C.AdapterType {
return C.Reject
}
func (r *Reject) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) { func (r *Reject) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) {
return &RejectAdapter{}, nil return &RejectAdapter{}, nil
} }

65
adapters/selector.go Normal file
View File

@ -0,0 +1,65 @@
package adapters
import (
"errors"
C "github.com/Dreamacro/clash/constant"
)
type Selector struct {
name string
selected C.Proxy
proxys map[string]C.Proxy
}
func (s *Selector) Name() string {
return s.name
}
func (s *Selector) Type() C.AdapterType {
return C.Selector
}
func (s *Selector) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) {
return s.selected.Generator(addr)
}
func (s *Selector) Now() string {
return s.selected.Name()
}
func (s *Selector) All() []string {
var all []string
for k, _ := range s.proxys {
all = append(all, k)
}
return all
}
func (s *Selector) Set(name string) error {
proxy, exist := s.proxys[name]
if !exist {
return errors.New("Proxy does not exist")
}
s.selected = proxy
return nil
}
func NewSelector(name string, proxys map[string]C.Proxy) (*Selector, error) {
if len(proxys) == 0 {
return nil, errors.New("Provide at least one proxy")
}
mapping := make(map[string]C.Proxy)
var init string
for k, v := range proxys {
mapping[k] = v
init = k
}
s := &Selector{
name: name,
proxys: mapping,
selected: proxys[init],
}
return s, nil
}

View File

@ -44,6 +44,10 @@ func (ss *ShadowSocks) Name() string {
return ss.name return ss.name
} }
func (ss *ShadowSocks) Type() C.AdapterType {
return C.Shadowsocks
}
func (ss *ShadowSocks) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) { func (ss *ShadowSocks) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) {
c, err := net.Dial("tcp", ss.server) c, err := net.Dial("tcp", ss.server)
if err != nil { if err != nil {

View File

@ -26,6 +26,14 @@ func (u *URLTest) Name() string {
return u.name return u.name
} }
func (u *URLTest) Type() C.AdapterType {
return C.URLTest
}
func (u *URLTest) Now() string {
return u.fast.Name()
}
func (u *URLTest) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) { func (u *URLTest) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) {
return u.fast.Generator(addr) return u.fast.Generator(addr)
} }

View File

@ -5,6 +5,15 @@ import (
"net" "net"
) )
// Adapter Type
const (
Direct AdapterType = iota
Reject
Selector
Shadowsocks
URLTest
)
type ProxyAdapter interface { type ProxyAdapter interface {
ReadWriter() io.ReadWriter ReadWriter() io.ReadWriter
Conn() net.Conn Conn() net.Conn
@ -19,5 +28,26 @@ type ServerAdapter interface {
type Proxy interface { type Proxy interface {
Name() string Name() string
Type() AdapterType
Generator(addr *Addr) (ProxyAdapter, error) Generator(addr *Addr) (ProxyAdapter, error)
} }
// AdapterType is enum of adapter type
type AdapterType int
func (at AdapterType) String() string {
switch at {
case Direct:
return "Direct"
case Reject:
return "Reject"
case Selector:
return "Selector"
case Shadowsocks:
return "Shadowsocks"
case URLTest:
return "URLTest"
default:
return "Unknow"
}
}

View File

@ -3,65 +3,47 @@ package hub
import ( import (
"net/http" "net/http"
"github.com/Dreamacro/clash/tunnel"
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/go-chi/render" "github.com/go-chi/render"
) )
type Configs struct {
Proxys []Proxy `json:"proxys"`
Rules []Rule `json:"rules"`
}
type Proxy struct {
Name string `json:"name"`
}
type Rule struct {
Name string `json:"name"`
Payload string `json:"type"`
}
func configRouter() http.Handler { func configRouter() http.Handler {
r := chi.NewRouter() r := chi.NewRouter()
r.Get("/", getConfig)
r.Put("/", updateConfig) r.Put("/", updateConfig)
return r return r
} }
func getConfig(w http.ResponseWriter, r *http.Request) { type General struct {
rulesCfg, proxysCfg := tun.Config() Mode string `json:mode`
}
var ( var modeMapping = map[string]tunnel.Mode{
rules []Rule "global": tunnel.Global,
proxys []Proxy "rule": tunnel.Rule,
) "direct": tunnel.Direct,
for _, rule := range rulesCfg {
rules = append(rules, Rule{
Name: rule.RuleType().String(),
Payload: rule.Payload(),
})
}
for _, proxy := range proxysCfg {
proxys = append(proxys, Proxy{Name: proxy.Name()})
}
w.WriteHeader(http.StatusOK)
render.JSON(w, r, Configs{
Rules: rules,
Proxys: proxys,
})
} }
func updateConfig(w http.ResponseWriter, r *http.Request) { func updateConfig(w http.ResponseWriter, r *http.Request) {
err := tun.UpdateConfig() general := &General{}
err := render.DecodeJSON(r.Body, general)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusBadRequest)
render.JSON(w, r, Error{ render.JSON(w, r, Error{
Error: err.Error(), Error: "Format error",
}) })
return return
} }
mode, ok := modeMapping[general.Mode]
if !ok {
w.WriteHeader(http.StatusBadRequest)
render.JSON(w, r, Error{
Error: "Mode error",
})
return
}
tun.SetMode(mode)
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
} }

129
hub/proxys.go Normal file
View File

@ -0,0 +1,129 @@
package hub
import (
"fmt"
"net/http"
A "github.com/Dreamacro/clash/adapters"
C "github.com/Dreamacro/clash/constant"
"github.com/go-chi/chi"
"github.com/go-chi/render"
)
func proxyRouter() http.Handler {
r := chi.NewRouter()
r.Get("/", getProxys)
r.Get("/{name}", getProxy)
r.Put("/{name}", updateProxy)
return r
}
type SampleProxy struct {
Type string `json:"type"`
}
type Selector struct {
Type string `json:"type"`
Now string `json:"now"`
All []string `json:"all"`
}
type URLTest struct {
Type string `json:"type"`
Now string `json:"now"`
}
func transformProxy(proxy C.Proxy) interface{} {
t := proxy.Type()
switch t {
case C.Selector:
selector := proxy.(*A.Selector)
return Selector{
Type: t.String(),
Now: selector.Now(),
All: selector.All(),
}
case C.URLTest:
return URLTest{
Type: t.String(),
Now: proxy.(*A.URLTest).Now(),
}
default:
return SampleProxy{
Type: proxy.Type().String(),
}
}
}
type GetProxysResponse struct {
Proxys map[string]interface{} `json:"proxys"`
}
func getProxys(w http.ResponseWriter, r *http.Request) {
_, rawProxys := tun.Config()
proxys := make(map[string]interface{})
for name, proxy := range rawProxys {
proxys[name] = transformProxy(proxy)
}
render.JSON(w, r, GetProxysResponse{Proxys: proxys})
}
func getProxy(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
_, proxys := tun.Config()
proxy, exist := proxys[name]
if !exist {
w.WriteHeader(http.StatusNotFound)
render.JSON(w, r, Error{
Error: "Proxy not found",
})
return
}
render.JSON(w, r, transformProxy(proxy))
}
type UpdateProxyRequest struct {
Name string `json:"name"`
}
func updateProxy(w http.ResponseWriter, r *http.Request) {
req := UpdateProxyRequest{}
if err := render.DecodeJSON(r.Body, &req); err != nil {
w.WriteHeader(http.StatusBadRequest)
render.JSON(w, r, Error{
Error: "Format error",
})
return
}
name := chi.URLParam(r, "name")
_, proxys := tun.Config()
proxy, exist := proxys[name]
if !exist {
w.WriteHeader(http.StatusNotFound)
render.JSON(w, r, Error{
Error: "Proxy not found",
})
return
}
selector, ok := proxy.(*A.Selector)
if !ok {
w.WriteHeader(http.StatusBadRequest)
render.JSON(w, r, Error{
Error: "Proxy can't update",
})
return
}
if err := selector.Set(req.Name); err != nil {
w.WriteHeader(http.StatusBadRequest)
render.JSON(w, r, Error{
Error: fmt.Sprintf("Selector update error: %s", err.Error()),
})
return
}
w.WriteHeader(http.StatusNoContent)
}

53
hub/rules.go Normal file
View File

@ -0,0 +1,53 @@
package hub
import (
"net/http"
"github.com/go-chi/chi"
"github.com/go-chi/render"
)
func ruleRouter() http.Handler {
r := chi.NewRouter()
r.Get("/", getRules)
r.Put("/", updateRules)
return r
}
type Rule struct {
Name string `json:"name"`
Payload string `json:"type"`
}
type GetRulesResponse struct {
Rules []Rule `json:"rules"`
}
func getRules(w http.ResponseWriter, r *http.Request) {
rulesCfg, _ := tun.Config()
var rules []Rule
for _, rule := range rulesCfg {
rules = append(rules, Rule{
Name: rule.RuleType().String(),
Payload: rule.Payload(),
})
}
w.WriteHeader(http.StatusOK)
render.JSON(w, r, GetRulesResponse{
Rules: rules,
})
}
func updateRules(w http.ResponseWriter, r *http.Request) {
err := tun.UpdateConfig()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
render.JSON(w, r, Error{
Error: err.Error(),
})
return
}
w.WriteHeader(http.StatusNoContent)
}

View File

@ -31,6 +31,8 @@ func NewHub(addr string) {
r.Get("/traffic", traffic) r.Get("/traffic", traffic)
r.Get("/logs", getLogs) r.Get("/logs", getLogs)
r.Mount("/configs", configRouter()) r.Mount("/configs", configRouter())
r.Mount("/proxys", proxyRouter())
r.Mount("/rules", ruleRouter())
err := http.ListenAndServe(addr, r) err := http.ListenAndServe(addr, r)
if err != nil { if err != nil {

22
tunnel/mode.go Normal file
View File

@ -0,0 +1,22 @@
package tunnel
type Mode int
const (
Global Mode = iota
Rule
Direct
)
func (m Mode) String() string {
switch m {
case Global:
return "Global"
case Rule:
return "Rule"
case Direct:
return "Direct"
default:
return "Unknow"
}
}

View File

@ -28,6 +28,8 @@ type Tunnel struct {
logCh chan interface{} logCh chan interface{}
configLock *sync.RWMutex configLock *sync.RWMutex
traffic *C.Traffic traffic *C.Traffic
mode Mode
selector *adapters.Selector
} }
func (t *Tunnel) Add(req C.ServerAdapter) { func (t *Tunnel) Add(req C.ServerAdapter) {
@ -46,6 +48,10 @@ func (t *Tunnel) Log() *observable.Observable {
return t.observable return t.observable
} }
func (t *Tunnel) SetMode(mode Mode) {
t.mode = mode
}
func (t *Tunnel) UpdateConfig() (err error) { func (t *Tunnel) UpdateConfig() (err error) {
cfg, err := C.GetConfig() cfg, err := C.GetConfig()
if err != nil { if err != nil {
@ -62,11 +68,10 @@ func (t *Tunnel) UpdateConfig() (err error) {
// parse proxy // parse proxy
for _, key := range proxysConfig.Keys() { for _, key := range proxysConfig.Keys() {
proxy := strings.Split(key.Value(), ",") proxy := key.Strings(",")
if len(proxy) == 0 { if len(proxy) == 0 {
continue continue
} }
proxy = trimArr(proxy)
switch proxy[0] { switch proxy[0] {
// ss, server, port, cipter, password // ss, server, port, cipter, password
case "ss": case "ss":
@ -106,12 +111,12 @@ func (t *Tunnel) UpdateConfig() (err error) {
// parse proxy groups // parse proxy groups
for _, key := range groupsConfig.Keys() { for _, key := range groupsConfig.Keys() {
rule := strings.Split(key.Value(), ",") rule := strings.Split(key.Value(), ",")
if len(rule) < 4 {
continue
}
rule = trimArr(rule) rule = trimArr(rule)
switch rule[0] { switch rule[0] {
case "url-test": case "url-test":
if len(rule) < 4 {
return fmt.Errorf("URLTest need more than 4 param")
}
proxyNames := rule[1 : len(rule)-2] proxyNames := rule[1 : len(rule)-2]
delay, _ := strconv.Atoi(rule[len(rule)-1]) delay, _ := strconv.Atoi(rule[len(rule)-1])
url := rule[len(rule)-2] url := rule[len(rule)-2]
@ -127,6 +132,24 @@ func (t *Tunnel) UpdateConfig() (err error) {
return fmt.Errorf("Config error: %s", err.Error()) return fmt.Errorf("Config error: %s", err.Error())
} }
proxys[key.Name()] = adapter proxys[key.Name()] = adapter
case "select":
if len(rule) < 3 {
return fmt.Errorf("Selector need more than 3 param")
}
proxyNames := rule[1:]
selectProxy := make(map[string]C.Proxy)
for _, name := range proxyNames {
proxy, exist := proxys[name]
if !exist {
return fmt.Errorf("Proxy %s not exist", name)
}
selectProxy[name] = proxy
}
selector, err := adapters.NewSelector(key.Name(), selectProxy)
if err != nil {
return fmt.Errorf("Selector create error: %s", err.Error())
}
proxys[key.Name()] = selector
} }
} }
@ -145,8 +168,14 @@ func (t *Tunnel) UpdateConfig() (err error) {
} }
} }
s, err := adapters.NewSelector("Proxy", proxys)
if err != nil {
return err
}
t.proxys = proxys t.proxys = proxys
t.rules = rules t.rules = rules
t.selector = s
return nil return nil
} }
@ -163,7 +192,17 @@ func (t *Tunnel) process() {
func (t *Tunnel) handleConn(localConn C.ServerAdapter) { func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
defer localConn.Close() defer localConn.Close()
addr := localConn.Addr() addr := localConn.Addr()
proxy := t.match(addr)
var proxy C.Proxy
switch t.mode {
case Direct:
proxy = t.proxys["DIRECT"]
case Global:
proxy = t.selector
// Rule
default:
proxy = t.match(addr)
}
remoConn, err := proxy.Generator(addr) remoConn, err := proxy.Generator(addr)
if err != nil { if err != nil {
t.logCh <- newLog(WARNING, "Proxy connect error: %s", err.Error()) t.logCh <- newLog(WARNING, "Proxy connect error: %s", err.Error())
@ -201,6 +240,7 @@ func newTunnel() *Tunnel {
logCh: logCh, logCh: logCh,
configLock: &sync.RWMutex{}, configLock: &sync.RWMutex{},
traffic: C.NewTraffic(time.Second), traffic: C.NewTraffic(time.Second),
mode: Rule,
} }
go tunnel.process() go tunnel.process()
go tunnel.subscribeLogs() go tunnel.subscribeLogs()