Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
88c7ccf749 | |||
3cacfb8a7f | |||
0eef9bbf5d | |||
39b45513af | |||
283e4e1f4f | |||
bd0a1ae1a7 | |||
018a6ba041 | |||
9c2ace1f91 | |||
17224a32cb | |||
05ab653103 | |||
c2c8f82f96 | |||
330a3391e3 |
@ -11,7 +11,7 @@ env:
|
|||||||
- NAME=clash
|
- NAME=clash
|
||||||
- BINDIR=bin
|
- BINDIR=bin
|
||||||
script:
|
script:
|
||||||
- go test
|
- go test ./...
|
||||||
before_deploy: make -j releases
|
before_deploy: make -j releases
|
||||||
deploy:
|
deploy:
|
||||||
provider: releases
|
provider: releases
|
||||||
|
@ -13,7 +13,7 @@ RUN dep ensure && \
|
|||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
RUN apk --no-cache add ca-certificates && \
|
RUN apk --no-cache add ca-certificates && \
|
||||||
mkdir -p /root/.config/clash
|
mkdir -p /root/.config/clash
|
||||||
COPY --from=builder /clash .
|
|
||||||
COPY --from=builder /Country.mmdb /root/.config/clash/
|
COPY --from=builder /Country.mmdb /root/.config/clash/
|
||||||
|
COPY --from=builder /clash .
|
||||||
EXPOSE 7890 7891
|
EXPOSE 7890 7891
|
||||||
ENTRYPOINT ["/clash"]
|
ENTRYPOINT ["/clash"]
|
||||||
|
16
Gopkg.lock
generated
16
Gopkg.lock
generated
@ -13,6 +13,18 @@
|
|||||||
revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98"
|
revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/go-chi/chi"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "e83ac2304db3c50cf03d96a2fcd39009d458bc35"
|
||||||
|
version = "v3.3.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/go-chi/render"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "3215478343fbc559bd3fc08f7031bb134d6bdad5"
|
||||||
|
version = "v1.0.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/oschwald/geoip2-golang"
|
name = "github.com/oschwald/geoip2-golang"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@ -53,7 +65,7 @@
|
|||||||
"poly1305",
|
"poly1305",
|
||||||
"ssh/terminal"
|
"ssh/terminal"
|
||||||
]
|
]
|
||||||
revision = "a8fb68e7206f8c78be19b432c58eb52a6aa34462"
|
revision = "027cca12c2d63e3d62b670d901e8a2c95854feec"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@ -80,6 +92,6 @@
|
|||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "4297c505508c6cdd8c94fd4ef29bc6940c65fea81e125fcf871a316b6e671a71"
|
inputs-digest = "c3c901e4e393a2df9e421924d3a4d85ec73642e36dcbc1ddca5fc13159220e86"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
@ -25,6 +25,14 @@
|
|||||||
# unused-packages = true
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/go-chi/chi"
|
||||||
|
version = "3.3.2"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/go-chi/render"
|
||||||
|
version = "1.0.1"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/oschwald/geoip2-golang"
|
name = "github.com/oschwald/geoip2-golang"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
6
Makefile
6
Makefile
@ -10,10 +10,14 @@ linux:
|
|||||||
macos:
|
macos:
|
||||||
GOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
|
GOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
|
||||||
|
|
||||||
releases: linux macos
|
win64:
|
||||||
|
GOARCH=amd64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe
|
||||||
|
|
||||||
|
releases: linux macos win64
|
||||||
chmod +x $(BINDIR)/$(NAME)-*
|
chmod +x $(BINDIR)/$(NAME)-*
|
||||||
gzip $(BINDIR)/$(NAME)-linux
|
gzip $(BINDIR)/$(NAME)-linux
|
||||||
gzip $(BINDIR)/$(NAME)-macos
|
gzip $(BINDIR)/$(NAME)-macos
|
||||||
|
zip -m -j $(BINDIR)/$(NAME)-win64.zip $(BINDIR)/$(NAME)-win64.exe
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm $(BINDIR)/*
|
rm $(BINDIR)/*
|
||||||
|
26
README.md
26
README.md
@ -1,8 +1,25 @@
|
|||||||
# Clash
|
<h1 align="center">
|
||||||
|
<img src="https://github.com/Dreamacro/clash/raw/master/docs/logo.png" alt="Clash" width="200">
|
||||||
|
<br>
|
||||||
|
Clash
|
||||||
|
<br>
|
||||||
|
</h1>
|
||||||
|
|
||||||
[](https://travis-ci.org/Dreamacro/clash)
|
<h4 align="center">A rule based proxy in Go.</h4>
|
||||||
|
|
||||||
A rule based proxy in Go.
|
<p align="center">
|
||||||
|
<a href="https://travis-ci.org/Dreamacro/clash">
|
||||||
|
<img src="https://img.shields.io/travis/Dreamacro/clash.svg?style=flat-square"
|
||||||
|
alt="Travis-CI">
|
||||||
|
</a>
|
||||||
|
<a href="https://goreportcard.com/report/github.com/Dreamacro/clash">
|
||||||
|
<img src="https://goreportcard.com/badge/github.com/Dreamacro/clash?style=flat-square">
|
||||||
|
</a>
|
||||||
|
<a href="https://app.fossa.io/projects/git%2Bgithub.com%2FDreamacro%2Fclash?ref=badge_shield" alt="FOSSA Status"><img src="https://app.fossa.io/api/projects/git%2Bgithub.com%2FDreamacro%2Fclash.svg?type=shield"/></a>
|
||||||
|
<a href="https://github.com/Dreamacro/clash/releases">
|
||||||
|
<img src="https://img.shields.io/github/release/Dreamacro/clash/all.svg?style=flat-square">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@ -72,6 +89,9 @@ GEOIP,CN,DIRECT
|
|||||||
FINAL,,Proxy # note: there is two ","
|
FINAL,,Proxy # note: there is two ","
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2FDreamacro%2Fclash?ref=badge_large)
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [ ] Complementing the necessary rule operators
|
- [ ] Complementing the necessary rule operators
|
||||||
|
@ -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 {
|
||||||
|
@ -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
65
adapters/selector.go
Normal 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
|
||||||
|
}
|
@ -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 {
|
||||||
@ -93,13 +97,13 @@ func serializesSocksAddr(addr *C.Addr) []byte {
|
|||||||
case socks.AtypDomainName:
|
case socks.AtypDomainName:
|
||||||
len := uint8(len(addr.Host))
|
len := uint8(len(addr.Host))
|
||||||
host := []byte(addr.Host)
|
host := []byte(addr.Host)
|
||||||
buf = [][]byte{[]byte{aType, len}, host, port}
|
buf = [][]byte{{aType, len}, host, port}
|
||||||
case socks.AtypIPv4:
|
case socks.AtypIPv4:
|
||||||
host := addr.IP.To4()
|
host := addr.IP.To4()
|
||||||
buf = [][]byte{[]byte{aType}, host, port}
|
buf = [][]byte{{aType}, host, port}
|
||||||
case socks.AtypIPv6:
|
case socks.AtypIPv6:
|
||||||
host := addr.IP.To16()
|
host := addr.IP.To16()
|
||||||
buf = [][]byte{[]byte{aType}, host, port}
|
buf = [][]byte{{aType}, host, port}
|
||||||
}
|
}
|
||||||
return bytes.Join(buf, []byte(""))
|
return bytes.Join(buf, []byte(""))
|
||||||
}
|
}
|
||||||
|
@ -19,21 +19,40 @@ type URLTest struct {
|
|||||||
addr *C.Addr
|
addr *C.Addr
|
||||||
fast C.Proxy
|
fast C.Proxy
|
||||||
delay time.Duration
|
delay time.Duration
|
||||||
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *URLTest) Name() string {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *URLTest) Close() {
|
||||||
|
u.done <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
func (u *URLTest) loop() {
|
func (u *URLTest) loop() {
|
||||||
tick := time.Tick(u.delay)
|
tick := time.NewTicker(u.delay)
|
||||||
go u.speedTest()
|
go u.speedTest()
|
||||||
for range tick {
|
Loop:
|
||||||
go u.speedTest()
|
for {
|
||||||
|
select {
|
||||||
|
case <-tick.C:
|
||||||
|
go u.speedTest()
|
||||||
|
case <-u.done:
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +161,7 @@ func NewURLTest(name string, proxys []C.Proxy, rawURL string, delay time.Duratio
|
|||||||
addr: addr,
|
addr: addr,
|
||||||
fast: proxys[0],
|
fast: proxys[0],
|
||||||
delay: delay,
|
delay: delay,
|
||||||
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
go urlTest.loop()
|
go urlTest.loop()
|
||||||
return urlTest, nil
|
return urlTest, nil
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,8 +16,8 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
Name = "clash"
|
Name = "clash"
|
||||||
DefalutHTTPPort = "7890"
|
DefalutHTTPPort = 7890
|
||||||
DefalutSOCKSPort = "7891"
|
DefalutSOCKSPort = 7891
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -26,6 +26,13 @@ var (
|
|||||||
MMDBPath string
|
MMDBPath string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type General struct {
|
||||||
|
Mode *string `json:"mode,omitempty"`
|
||||||
|
AllowLan *bool `json:"allow-lan,omitempty"`
|
||||||
|
Port *int `json:"port,omitempty"`
|
||||||
|
SocksPort *int `json:"socks-port,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
currentUser, err := user.Current()
|
currentUser, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
7
constant/proxy.go
Normal file
7
constant/proxy.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
// ProxySignal is used to handle graceful shutdown of proxy
|
||||||
|
type ProxySignal struct {
|
||||||
|
Done chan<- struct{}
|
||||||
|
Closed <-chan struct{}
|
||||||
|
}
|
@ -11,8 +11,26 @@ const (
|
|||||||
|
|
||||||
type RuleType int
|
type RuleType int
|
||||||
|
|
||||||
|
func (rt RuleType) String() string {
|
||||||
|
switch rt {
|
||||||
|
case DomainSuffix:
|
||||||
|
return "DomainSuffix"
|
||||||
|
case DomainKeyword:
|
||||||
|
return "DomainKeyword"
|
||||||
|
case GEOIP:
|
||||||
|
return "GEOIP"
|
||||||
|
case IPCIDR:
|
||||||
|
return "IPCIDR"
|
||||||
|
case FINAL:
|
||||||
|
return "FINAL"
|
||||||
|
default:
|
||||||
|
return "Unknow"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Rule interface {
|
type Rule interface {
|
||||||
RuleType() RuleType
|
RuleType() RuleType
|
||||||
IsMatch(addr *Addr) bool
|
IsMatch(addr *Addr) bool
|
||||||
Adapter() string
|
Adapter() string
|
||||||
|
Payload() string
|
||||||
}
|
}
|
||||||
|
BIN
docs/logo.png
Executable file
BIN
docs/logo.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
31
hub/common.go
Normal file
31
hub/common.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package hub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Dreamacro/clash/proxy"
|
||||||
|
T "github.com/Dreamacro/clash/tunnel"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
tunnel = T.GetInstance()
|
||||||
|
listener = proxy.Instance()
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Errors struct {
|
||||||
|
Errors map[string]string `json:"errors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatErrors(errorsMap map[string]error) (bool, Errors) {
|
||||||
|
errors := make(map[string]string)
|
||||||
|
hasError := false
|
||||||
|
for key, err := range errorsMap {
|
||||||
|
if err != nil {
|
||||||
|
errors[key] = err.Error()
|
||||||
|
hasError = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasError, Errors{Errors: errors}
|
||||||
|
}
|
74
hub/configs.go
Normal file
74
hub/configs.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package hub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/proxy"
|
||||||
|
T "github.com/Dreamacro/clash/tunnel"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
func configRouter() http.Handler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Get("/", getConfigs)
|
||||||
|
r.Put("/", updateConfigs)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
var modeMapping = map[string]T.Mode{
|
||||||
|
"Global": T.Global,
|
||||||
|
"Rule": T.Rule,
|
||||||
|
"Direct": T.Direct,
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfigs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info := listener.Info()
|
||||||
|
mode := tunnel.GetMode().String()
|
||||||
|
info.Mode = &mode
|
||||||
|
render.JSON(w, r, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateConfigs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
general := &C.General{}
|
||||||
|
err := render.DecodeJSON(r.Body, general)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
render.JSON(w, r, Error{
|
||||||
|
Error: "Format error",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// update errors
|
||||||
|
var proxyErr, modeErr error
|
||||||
|
|
||||||
|
// update proxy
|
||||||
|
listener := proxy.Instance()
|
||||||
|
proxyErr = listener.Update(general.AllowLan, general.Port, general.SocksPort)
|
||||||
|
|
||||||
|
// update mode
|
||||||
|
if general.Mode != nil {
|
||||||
|
mode, ok := modeMapping[*general.Mode]
|
||||||
|
if !ok {
|
||||||
|
modeErr = fmt.Errorf("Mode error")
|
||||||
|
} else {
|
||||||
|
tunnel.SetMode(mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasError, errors := formatErrors(map[string]error{
|
||||||
|
"proxy": proxyErr,
|
||||||
|
"mode": modeErr,
|
||||||
|
})
|
||||||
|
|
||||||
|
if hasError {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
render.JSON(w, r, errors)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
129
hub/proxys.go
Normal file
129
hub/proxys.go
Normal 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 := tunnel.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 := tunnel.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 := tunnel.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
53
hub/rules.go
Normal 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, _ := tunnel.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 := tunnel.UpdateConfig()
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
render.JSON(w, r, Error{
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
@ -5,36 +5,26 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/tunnel"
|
T "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"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
tun = tunnel.GetInstance()
|
|
||||||
)
|
|
||||||
|
|
||||||
type Traffic struct {
|
type Traffic struct {
|
||||||
Up int64 `json:"up"`
|
Up int64 `json:"up"`
|
||||||
Down int64 `json:"down"`
|
Down int64 `json:"down"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Log struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Payload string `json:"payload"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Error struct {
|
|
||||||
Error string `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHub(addr string) {
|
func NewHub(addr string) {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
r.Get("/traffic", traffic)
|
r.Get("/traffic", traffic)
|
||||||
r.Get("/logs", getLogs)
|
r.Get("/logs", getLogs)
|
||||||
|
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 {
|
||||||
@ -43,10 +33,10 @@ func NewHub(addr string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func traffic(w http.ResponseWriter, r *http.Request) {
|
func traffic(w http.ResponseWriter, r *http.Request) {
|
||||||
render.Status(r, http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
tick := time.NewTicker(time.Second)
|
tick := time.NewTicker(time.Second)
|
||||||
t := tun.Traffic()
|
t := tunnel.Traffic()
|
||||||
for range tick.C {
|
for range tick.C {
|
||||||
up, down := t.Now()
|
up, down := t.Now()
|
||||||
if err := json.NewEncoder(w).Encode(Traffic{
|
if err := json.NewEncoder(w).Encode(Traffic{
|
||||||
@ -59,19 +49,55 @@ func traffic(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetLogs struct {
|
||||||
|
Level string `json:"level"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Log struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
func getLogs(w http.ResponseWriter, r *http.Request) {
|
func getLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
src := tun.Log()
|
req := &GetLogs{}
|
||||||
|
render.DecodeJSON(r.Body, req)
|
||||||
|
if req.Level == "" {
|
||||||
|
req.Level = "info"
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping := map[string]T.LogLevel{
|
||||||
|
"info": T.INFO,
|
||||||
|
"debug": T.DEBUG,
|
||||||
|
"error": T.ERROR,
|
||||||
|
"warning": T.WARNING,
|
||||||
|
}
|
||||||
|
|
||||||
|
level, ok := mapping[req.Level]
|
||||||
|
if !ok {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
render.JSON(w, r, Error{
|
||||||
|
Error: "Level error",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
src := tunnel.Log()
|
||||||
sub, err := src.Subscribe()
|
sub, err := src.Subscribe()
|
||||||
defer src.UnSubscribe(sub)
|
defer src.UnSubscribe(sub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Status(r, http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
render.JSON(w, r, Error{
|
render.JSON(w, r, Error{
|
||||||
Error: err.Error(),
|
Error: err.Error(),
|
||||||
})
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
render.Status(r, http.StatusOK)
|
render.Status(r, http.StatusOK)
|
||||||
for elm := range sub {
|
for elm := range sub {
|
||||||
log := elm.(tunnel.Log)
|
log := elm.(T.Log)
|
||||||
|
if log.LogLevel > level {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(Log{
|
if err := json.NewEncoder(w).Encode(Log{
|
||||||
Type: log.Type(),
|
Type: log.Type(),
|
||||||
Payload: log.Payload,
|
Payload: log.Payload,
|
||||||
|
30
main.go
30
main.go
@ -7,38 +7,28 @@ import (
|
|||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/hub"
|
"github.com/Dreamacro/clash/hub"
|
||||||
"github.com/Dreamacro/clash/proxy/http"
|
"github.com/Dreamacro/clash/proxy"
|
||||||
"github.com/Dreamacro/clash/proxy/socks"
|
|
||||||
"github.com/Dreamacro/clash/tunnel"
|
"github.com/Dreamacro/clash/tunnel"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
if err := tunnel.GetInstance().UpdateConfig(); err != nil {
|
||||||
|
log.Fatalf("Parse config error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := proxy.Instance().Run(); err != nil {
|
||||||
|
log.Fatalf("Proxy listen error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hub
|
||||||
cfg, err := C.GetConfig()
|
cfg, err := C.GetConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Read config error: %s", err.Error())
|
log.Fatalf("Read config error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
port, socksPort := C.DefalutHTTPPort, C.DefalutSOCKSPort
|
|
||||||
section := cfg.Section("General")
|
section := cfg.Section("General")
|
||||||
if key, err := section.GetKey("port"); err == nil {
|
|
||||||
port = key.Value()
|
|
||||||
}
|
|
||||||
|
|
||||||
if key, err := section.GetKey("socks-port"); err == nil {
|
|
||||||
socksPort = key.Value()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tunnel.GetInstance().UpdateConfig()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Parse config error: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
go http.NewHttpProxy(port)
|
|
||||||
go socks.NewSocksProxy(socksPort)
|
|
||||||
|
|
||||||
// Hub
|
|
||||||
if key, err := section.GetKey("external-controller"); err == nil {
|
if key, err := section.GetKey("external-controller"); err == nil {
|
||||||
go hub.NewHub(key.Value())
|
go hub.NewHub(key.Value())
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,3 @@
|
|||||||
package observable
|
package observable
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Iterable <-chan interface{}
|
type Iterable <-chan interface{}
|
||||||
|
|
||||||
func NewIterable(any interface{}) (Iterable, error) {
|
|
||||||
switch any := any.(type) {
|
|
||||||
case chan interface{}:
|
|
||||||
return Iterable(any), nil
|
|
||||||
case <-chan interface{}:
|
|
||||||
return Iterable(any), nil
|
|
||||||
default:
|
|
||||||
return nil, errors.New("type error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -50,7 +50,6 @@ func (o *Observable) Subscribe() (Subscription, error) {
|
|||||||
func (o *Observable) UnSubscribe(sub Subscription) {
|
func (o *Observable) UnSubscribe(sub Subscription) {
|
||||||
elm, exist := o.listener.Load(sub)
|
elm, exist := o.listener.Load(sub)
|
||||||
if !exist {
|
if !exist {
|
||||||
println("not exist")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
subscriber := elm.(*Subscriber)
|
subscriber := elm.(*Subscriber)
|
||||||
|
@ -27,11 +27,7 @@ func TestObservable(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
count := 0
|
count := 0
|
||||||
for {
|
for range data {
|
||||||
_, open := <-data
|
|
||||||
if !open {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
count = count + 1
|
count = count + 1
|
||||||
}
|
}
|
||||||
if count != 5 {
|
if count != 5 {
|
||||||
@ -49,11 +45,7 @@ func TestObservable_MutilSubscribe(t *testing.T) {
|
|||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
waitCh := func(ch <-chan interface{}) {
|
waitCh := func(ch <-chan interface{}) {
|
||||||
for {
|
for range ch {
|
||||||
_, open := <-ch
|
|
||||||
if !open {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
count = count + 1
|
count = count + 1
|
||||||
}
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
@ -80,6 +72,25 @@ func TestObservable_UnSubscribe(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestObservable_SubscribeClosedSource(t *testing.T) {
|
||||||
|
iter := iterator([]interface{}{1})
|
||||||
|
src := NewObservable(iter)
|
||||||
|
data, _ := src.Subscribe()
|
||||||
|
<-data
|
||||||
|
|
||||||
|
_, closed := src.Subscribe()
|
||||||
|
if closed == nil {
|
||||||
|
t.Error("Observable should be closed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestObservable_UnSubscribeWithNotExistSubscription(t *testing.T) {
|
||||||
|
sub := Subscription(make(chan interface{}))
|
||||||
|
iter := iterator([]interface{}{1})
|
||||||
|
src := NewObservable(iter)
|
||||||
|
src.UnSubscribe(sub)
|
||||||
|
}
|
||||||
|
|
||||||
func TestObservable_SubscribeGoroutineLeak(t *testing.T) {
|
func TestObservable_SubscribeGoroutineLeak(t *testing.T) {
|
||||||
// waiting for other goroutine recycle
|
// waiting for other goroutine recycle
|
||||||
time.Sleep(120 * time.Millisecond)
|
time.Sleep(120 * time.Millisecond)
|
||||||
@ -97,11 +108,7 @@ func TestObservable_SubscribeGoroutineLeak(t *testing.T) {
|
|||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(max)
|
wg.Add(max)
|
||||||
waitCh := func(ch <-chan interface{}) {
|
waitCh := func(ch <-chan interface{}) {
|
||||||
for {
|
for range ch {
|
||||||
_, open := <-ch
|
|
||||||
if !open {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package observable
|
|
||||||
|
|
||||||
func mergeWithBytes(ch <-chan interface{}, buf []byte) chan interface{} {
|
|
||||||
out := make(chan interface{})
|
|
||||||
go func() {
|
|
||||||
defer close(out)
|
|
||||||
if len(buf) != 0 {
|
|
||||||
out <- buf
|
|
||||||
}
|
|
||||||
for elm := range ch {
|
|
||||||
out <- elm
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return out
|
|
||||||
}
|
|
@ -32,7 +32,6 @@ func (h *HttpAdapter) Connect(proxy C.ProxyAdapter) {
|
|||||||
// from http.DefaultTransport
|
// from http.DefaultTransport
|
||||||
MaxIdleConns: 100,
|
MaxIdleConns: 100,
|
||||||
IdleConnTimeout: 90 * time.Second,
|
IdleConnTimeout: 90 * time.Second,
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
}
|
}
|
||||||
resp, err := req.RoundTrip(h.r)
|
resp, err := req.RoundTrip(h.r)
|
||||||
@ -48,7 +47,23 @@ func (h *HttpAdapter) Connect(proxy C.ProxyAdapter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.w.WriteHeader(resp.StatusCode)
|
h.w.WriteHeader(resp.StatusCode)
|
||||||
io.Copy(h.w, resp.Body)
|
var writer io.Writer = h.w
|
||||||
|
if len(resp.TransferEncoding) > 0 && resp.TransferEncoding[0] == "chunked" {
|
||||||
|
writer = ChunkWriter{Writer: h.w}
|
||||||
|
}
|
||||||
|
io.Copy(writer, resp.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChunkWriter struct {
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw ChunkWriter) Write(b []byte) (int, error) {
|
||||||
|
n, err := cw.Writer.Write(b)
|
||||||
|
if err == nil {
|
||||||
|
cw.Writer.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHttp(host string, w http.ResponseWriter, r *http.Request) (*HttpAdapter, chan struct{}) {
|
func NewHttp(host string, w http.ResponseWriter, r *http.Request) (*HttpAdapter, chan struct{}) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -17,9 +17,20 @@ var (
|
|||||||
tun = tunnel.GetInstance()
|
tun = tunnel.GetInstance()
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewHttpProxy(port string) {
|
func NewHttpProxy(addr string) (*C.ProxySignal, error) {
|
||||||
|
l, err := net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
closed := make(chan struct{})
|
||||||
|
signal := &C.ProxySignal{
|
||||||
|
Done: done,
|
||||||
|
Closed: closed,
|
||||||
|
}
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: fmt.Sprintf(":%s", port),
|
|
||||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == http.MethodConnect {
|
if r.Method == http.MethodConnect {
|
||||||
handleTunneling(w, r)
|
handleTunneling(w, r)
|
||||||
@ -28,8 +39,20 @@ func NewHttpProxy(port string) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
log.Infof("HTTP proxy :%s", port)
|
|
||||||
server.ListenAndServe()
|
go func() {
|
||||||
|
log.Infof("HTTP proxy listening at: %s", addr)
|
||||||
|
server.Serve(l)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-done
|
||||||
|
server.Shutdown(context.Background())
|
||||||
|
l.Close()
|
||||||
|
closed <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return signal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleHTTP(w http.ResponseWriter, r *http.Request) {
|
func handleHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
143
proxy/listener.go
Normal file
143
proxy/listener.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/proxy/http"
|
||||||
|
"github.com/Dreamacro/clash/proxy/socks"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
listener *Listener
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
type Listener struct {
|
||||||
|
httpPort int
|
||||||
|
socksPort int
|
||||||
|
allowLan bool
|
||||||
|
|
||||||
|
// signal for update
|
||||||
|
httpSignal *C.ProxySignal
|
||||||
|
socksSignal *C.ProxySignal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info returns the proxys's current configuration
|
||||||
|
func (l *Listener) Info() (info C.General) {
|
||||||
|
return C.General{
|
||||||
|
Port: &l.httpPort,
|
||||||
|
SocksPort: &l.socksPort,
|
||||||
|
AllowLan: &l.allowLan,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Update(allowLan *bool, httpPort *int, socksPort *int) error {
|
||||||
|
if allowLan != nil {
|
||||||
|
l.allowLan = *allowLan
|
||||||
|
}
|
||||||
|
|
||||||
|
var socksErr, httpErr error
|
||||||
|
if allowLan != nil || httpPort != nil {
|
||||||
|
newHTTPPort := l.httpPort
|
||||||
|
if httpPort != nil {
|
||||||
|
newHTTPPort = *httpPort
|
||||||
|
}
|
||||||
|
httpErr = l.updateHTTP(newHTTPPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowLan != nil || socksPort != nil {
|
||||||
|
newSocksPort := l.socksPort
|
||||||
|
if socksPort != nil {
|
||||||
|
newSocksPort = *socksPort
|
||||||
|
}
|
||||||
|
socksErr = l.updateSocks(newSocksPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
if socksErr != nil && httpErr != nil {
|
||||||
|
return fmt.Errorf("%s\n%s", socksErr.Error(), httpErr.Error())
|
||||||
|
} else if socksErr != nil {
|
||||||
|
return socksErr
|
||||||
|
} else if httpErr != nil {
|
||||||
|
return httpErr
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) updateHTTP(port int) error {
|
||||||
|
if l.httpSignal != nil {
|
||||||
|
signal := l.httpSignal
|
||||||
|
signal.Done <- struct{}{}
|
||||||
|
<-signal.Closed
|
||||||
|
l.httpSignal = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
signal, err := http.NewHttpProxy(l.genAddr(port))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.httpSignal = signal
|
||||||
|
l.httpPort = port
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) updateSocks(port int) error {
|
||||||
|
if l.socksSignal != nil {
|
||||||
|
signal := l.socksSignal
|
||||||
|
signal.Done <- struct{}{}
|
||||||
|
<-signal.Closed
|
||||||
|
l.socksSignal = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
signal, err := socks.NewSocksProxy(l.genAddr(port))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.socksSignal = signal
|
||||||
|
l.socksPort = port
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) genAddr(port int) string {
|
||||||
|
host := "127.0.0.1"
|
||||||
|
if l.allowLan {
|
||||||
|
host = ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s:%d", host, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Run() error {
|
||||||
|
return l.Update(&l.allowLan, &l.httpPort, &l.socksPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newListener() *Listener {
|
||||||
|
cfg, err := C.GetConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Read config error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
general := cfg.Section("General")
|
||||||
|
|
||||||
|
port := general.Key("port").RangeInt(C.DefalutHTTPPort, 1, 65535)
|
||||||
|
socksPort := general.Key("socks-port").RangeInt(C.DefalutSOCKSPort, 1, 65535)
|
||||||
|
allowLan := general.Key("allow-lan").MustBool()
|
||||||
|
|
||||||
|
return &Listener{
|
||||||
|
httpPort: port,
|
||||||
|
socksPort: socksPort,
|
||||||
|
allowLan: allowLan,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Instance() *Listener {
|
||||||
|
once.Do(func() {
|
||||||
|
listener = newListener()
|
||||||
|
})
|
||||||
|
return listener
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package socks
|
package socks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -17,20 +16,41 @@ var (
|
|||||||
tun = tunnel.GetInstance()
|
tun = tunnel.GetInstance()
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewSocksProxy(port string) {
|
func NewSocksProxy(addr string) (*C.ProxySignal, error) {
|
||||||
l, err := net.Listen("tcp", fmt.Sprintf(":%s", port))
|
l, err := net.Listen("tcp", addr)
|
||||||
defer l.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Infof("SOCKS proxy :%s", port)
|
|
||||||
for {
|
done := make(chan struct{})
|
||||||
c, err := l.Accept()
|
closed := make(chan struct{})
|
||||||
if err != nil {
|
signal := &C.ProxySignal{
|
||||||
continue
|
Done: done,
|
||||||
|
Closed: closed,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Infof("SOCKS proxy listening at: %s", addr)
|
||||||
|
for {
|
||||||
|
c, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
if _, open := <-done; !open {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go handleSocks(c)
|
||||||
}
|
}
|
||||||
go handleSocks(c)
|
}()
|
||||||
}
|
|
||||||
|
go func() {
|
||||||
|
<-done
|
||||||
|
close(done)
|
||||||
|
l.Close()
|
||||||
|
closed <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return signal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSocks(conn net.Conn) {
|
func handleSocks(conn net.Conn) {
|
||||||
|
@ -27,6 +27,10 @@ func (dk *DomainKeyword) Adapter() string {
|
|||||||
return dk.adapter
|
return dk.adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dk *DomainKeyword) Payload() string {
|
||||||
|
return dk.keyword
|
||||||
|
}
|
||||||
|
|
||||||
func NewDomainKeyword(keyword string, adapter string) *DomainKeyword {
|
func NewDomainKeyword(keyword string, adapter string) *DomainKeyword {
|
||||||
return &DomainKeyword{
|
return &DomainKeyword{
|
||||||
keyword: keyword,
|
keyword: keyword,
|
||||||
|
@ -27,6 +27,10 @@ func (ds *DomainSuffix) Adapter() string {
|
|||||||
return ds.adapter
|
return ds.adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ds *DomainSuffix) Payload() string {
|
||||||
|
return ds.suffix
|
||||||
|
}
|
||||||
|
|
||||||
func NewDomainSuffix(suffix string, adapter string) *DomainSuffix {
|
func NewDomainSuffix(suffix string, adapter string) *DomainSuffix {
|
||||||
return &DomainSuffix{
|
return &DomainSuffix{
|
||||||
suffix: suffix,
|
suffix: suffix,
|
||||||
|
@ -20,6 +20,10 @@ func (f *Final) Adapter() string {
|
|||||||
return f.adapter
|
return f.adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Final) Payload() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func NewFinal(adapter string) *Final {
|
func NewFinal(adapter string) *Final {
|
||||||
return &Final{
|
return &Final{
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
|
@ -38,6 +38,10 @@ func (g *GEOIP) Adapter() string {
|
|||||||
return g.adapter
|
return g.adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GEOIP) Payload() string {
|
||||||
|
return g.country
|
||||||
|
}
|
||||||
|
|
||||||
func NewGEOIP(country string, adapter string) *GEOIP {
|
func NewGEOIP(country string, adapter string) *GEOIP {
|
||||||
return &GEOIP{
|
return &GEOIP{
|
||||||
country: country,
|
country: country,
|
||||||
|
@ -23,8 +23,12 @@ func (i *IPCIDR) IsMatch(addr *C.Addr) bool {
|
|||||||
return i.ipnet.Contains(*addr.IP)
|
return i.ipnet.Contains(*addr.IP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *IPCIDR) Adapter() string {
|
func (i *IPCIDR) Adapter() string {
|
||||||
return g.adapter
|
return i.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IPCIDR) Payload() string {
|
||||||
|
return i.ipnet.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIPCIDR(s string, adapter string) *IPCIDR {
|
func NewIPCIDR(s string, adapter string) *IPCIDR {
|
||||||
|
@ -7,21 +7,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
INFO LogType = iota
|
ERROR LogLevel = iota
|
||||||
WARNING
|
WARNING
|
||||||
ERROR
|
INFO
|
||||||
DEBUG
|
DEBUG
|
||||||
)
|
)
|
||||||
|
|
||||||
type LogType int
|
type LogLevel int
|
||||||
|
|
||||||
type Log struct {
|
type Log struct {
|
||||||
LogType LogType
|
LogLevel LogLevel
|
||||||
Payload string
|
Payload string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) Type() string {
|
func (l *Log) Type() string {
|
||||||
switch l.LogType {
|
switch l.LogLevel {
|
||||||
case INFO:
|
case INFO:
|
||||||
return "Info"
|
return "Info"
|
||||||
case WARNING:
|
case WARNING:
|
||||||
@ -36,7 +36,7 @@ func (l *Log) Type() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func print(data Log) {
|
func print(data Log) {
|
||||||
switch data.LogType {
|
switch data.LogLevel {
|
||||||
case INFO:
|
case INFO:
|
||||||
log.Infoln(data.Payload)
|
log.Infoln(data.Payload)
|
||||||
case WARNING:
|
case WARNING:
|
||||||
@ -59,9 +59,9 @@ func (t *Tunnel) subscribeLogs() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLog(logType LogType, format string, v ...interface{}) Log {
|
func newLog(logLevel LogLevel, format string, v ...interface{}) Log {
|
||||||
return Log{
|
return Log{
|
||||||
LogType: logType,
|
LogLevel: logLevel,
|
||||||
Payload: fmt.Sprintf(format, v...),
|
Payload: fmt.Sprintf(format, v...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
tunnel/mode.go
Normal file
22
tunnel/mode.go
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
@ -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) {
|
||||||
@ -38,17 +40,29 @@ func (t *Tunnel) Traffic() *C.Traffic {
|
|||||||
return t.traffic
|
return t.traffic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tunnel) Config() ([]C.Rule, map[string]C.Proxy) {
|
||||||
|
return t.rules, t.proxys
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Tunnel) Log() *observable.Observable {
|
func (t *Tunnel) Log() *observable.Observable {
|
||||||
return t.observable
|
return t.observable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tunnel) SetMode(mode Mode) {
|
||||||
|
t.mode = mode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tunnel) GetMode() Mode {
|
||||||
|
return t.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 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear proxys and rules
|
// empty proxys and rules
|
||||||
proxys := make(map[string]C.Proxy)
|
proxys := make(map[string]C.Proxy)
|
||||||
rules := []C.Rule{}
|
rules := []C.Rule{}
|
||||||
|
|
||||||
@ -58,11 +72,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":
|
||||||
@ -78,10 +91,6 @@ func (t *Tunnel) UpdateConfig() (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// init proxy
|
|
||||||
proxys["DIRECT"] = adapters.NewDirect(t.traffic)
|
|
||||||
proxys["REJECT"] = adapters.NewReject()
|
|
||||||
|
|
||||||
// parse rules
|
// parse rules
|
||||||
for _, key := range rulesConfig.Keys() {
|
for _, key := range rulesConfig.Keys() {
|
||||||
rule := strings.Split(key.Name(), ",")
|
rule := strings.Split(key.Name(), ",")
|
||||||
@ -106,12 +115,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,14 +136,50 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init proxy
|
||||||
|
proxys["DIRECT"] = adapters.NewDirect(t.traffic)
|
||||||
|
proxys["REJECT"] = adapters.NewReject()
|
||||||
|
|
||||||
t.configLock.Lock()
|
t.configLock.Lock()
|
||||||
defer t.configLock.Unlock()
|
defer t.configLock.Unlock()
|
||||||
|
|
||||||
|
// stop url-test
|
||||||
|
for _, elm := range t.proxys {
|
||||||
|
urlTest, ok := elm.(*adapters.URLTest)
|
||||||
|
if ok {
|
||||||
|
urlTest.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
@ -151,7 +196,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())
|
||||||
@ -172,7 +227,7 @@ func (t *Tunnel) match(addr *C.Addr) C.Proxy {
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.logCh <- newLog(INFO, "%v match %d using %s", addr.String(), rule.RuleType(), rule.Adapter())
|
t.logCh <- newLog(INFO, "%v match %s using %s", addr.String(), rule.RuleType().String(), rule.Adapter())
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,6 +244,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()
|
||||||
|
Reference in New Issue
Block a user