Improve: config convergent and add log-level

This commit is contained in:
Dreamacro
2018-07-26 00:04:59 +08:00
parent 7357d2d0c2
commit 8389150318
31 changed files with 757 additions and 484 deletions

45
adapters/remote/direct.go Normal file
View File

@ -0,0 +1,45 @@
package adapters
import (
"net"
C "github.com/Dreamacro/clash/constant"
)
// DirectAdapter is a directly connected adapter
type DirectAdapter struct {
conn net.Conn
}
// Close is used to close connection
func (d *DirectAdapter) Close() {
d.conn.Close()
}
// Conn is used to http request
func (d *DirectAdapter) Conn() net.Conn {
return d.conn
}
type Direct struct{}
func (d *Direct) Name() string {
return "Direct"
}
func (d *Direct) Type() C.AdapterType {
return C.Direct
}
func (d *Direct) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) {
c, err := net.Dial("tcp", net.JoinHostPort(addr.String(), addr.Port))
if err != nil {
return
}
c.(*net.TCPConn).SetKeepAlive(true)
return &DirectAdapter{conn: c}, nil
}
func NewDirect() *Direct {
return &Direct{}
}

49
adapters/remote/reject.go Normal file
View File

@ -0,0 +1,49 @@
package adapters
import (
"io"
"net"
C "github.com/Dreamacro/clash/constant"
)
// RejectAdapter is a reject connected adapter
type RejectAdapter struct {
}
// Close is used to close connection
func (r *RejectAdapter) Close() {}
// Conn is used to http request
func (r *RejectAdapter) Conn() net.Conn {
return nil
}
type Reject struct {
}
func (r *Reject) Name() string {
return "Reject"
}
func (r *Reject) Type() C.AdapterType {
return C.Reject
}
func (r *Reject) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) {
return &RejectAdapter{}, nil
}
func NewReject() *Reject {
return &Reject{}
}
type NopRW struct{}
func (rw *NopRW) Read(b []byte) (int, error) {
return len(b), nil
}
func (rw *NopRW) Write(b []byte) (int, error) {
return 0, io.EOF
}

View File

@ -0,0 +1,65 @@
package adapters
import (
"errors"
C "github.com/Dreamacro/clash/constant"
)
type Selector struct {
name string
selected C.Proxy
proxies 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.proxies {
all = append(all, k)
}
return all
}
func (s *Selector) Set(name string) error {
proxy, exist := s.proxies[name]
if !exist {
return errors.New("Proxy does not exist")
}
s.selected = proxy
return nil
}
func NewSelector(name string, proxies map[string]C.Proxy) (*Selector, error) {
if len(proxies) == 0 {
return nil, errors.New("Provide at least one proxy")
}
mapping := make(map[string]C.Proxy)
var init string
for k, v := range proxies {
mapping[k] = v
init = k
}
s := &Selector{
name: name,
proxies: mapping,
selected: proxies[init],
}
return s, nil
}

View File

@ -0,0 +1,101 @@
package adapters
import (
"bytes"
"fmt"
"net"
"net/url"
"strconv"
C "github.com/Dreamacro/clash/constant"
"github.com/riobard/go-shadowsocks2/core"
"github.com/riobard/go-shadowsocks2/socks"
)
// ShadowsocksAdapter is a shadowsocks adapter
type ShadowsocksAdapter struct {
conn net.Conn
}
// Close is used to close connection
func (ss *ShadowsocksAdapter) Close() {
ss.conn.Close()
}
func (ss *ShadowsocksAdapter) Conn() net.Conn {
return ss.conn
}
type ShadowSocks struct {
server string
name string
cipher core.Cipher
}
func (ss *ShadowSocks) Name() string {
return ss.name
}
func (ss *ShadowSocks) Type() C.AdapterType {
return C.Shadowsocks
}
func (ss *ShadowSocks) Generator(addr *C.Addr) (adapter C.ProxyAdapter, err error) {
c, err := net.Dial("tcp", ss.server)
if err != nil {
return nil, fmt.Errorf("%s connect error", ss.server)
}
c.(*net.TCPConn).SetKeepAlive(true)
c = ss.cipher.StreamConn(c)
_, err = c.Write(serializesSocksAddr(addr))
return &ShadowsocksAdapter{conn: c}, err
}
func NewShadowSocks(name string, ssURL string) (*ShadowSocks, error) {
var key []byte
server, cipher, password, _ := parseURL(ssURL)
ciph, err := core.PickCipher(cipher, key, password)
if err != nil {
return nil, fmt.Errorf("ss %s initialize error: %s", server, err.Error())
}
return &ShadowSocks{
server: server,
name: name,
cipher: ciph,
}, nil
}
func parseURL(s string) (addr, cipher, password string, err error) {
u, err := url.Parse(s)
if err != nil {
return
}
addr = u.Host
if u.User != nil {
cipher = u.User.Username()
password, _ = u.User.Password()
}
return
}
func serializesSocksAddr(addr *C.Addr) []byte {
var buf [][]byte
aType := uint8(addr.AddrType)
p, _ := strconv.Atoi(addr.Port)
port := []byte{uint8(p >> 8), uint8(p & 0xff)}
switch addr.AddrType {
case socks.AtypDomainName:
len := uint8(len(addr.Host))
host := []byte(addr.Host)
buf = [][]byte{{aType, len}, host, port}
case socks.AtypIPv4:
host := addr.IP.To4()
buf = [][]byte{{aType}, host, port}
case socks.AtypIPv6:
host := addr.IP.To16()
buf = [][]byte{{aType}, host, port}
}
return bytes.Join(buf, []byte(""))
}

168
adapters/remote/urltest.go Normal file
View File

@ -0,0 +1,168 @@
package adapters
import (
"fmt"
"net"
"net/http"
"net/url"
"sync"
"time"
C "github.com/Dreamacro/clash/constant"
)
type URLTest struct {
name string
proxies []C.Proxy
url *url.URL
rawURL string
addr *C.Addr
fast C.Proxy
delay time.Duration
done chan struct{}
}
func (u *URLTest) Name() string {
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) {
return u.fast.Generator(addr)
}
func (u *URLTest) Close() {
u.done <- struct{}{}
}
func (u *URLTest) loop() {
tick := time.NewTicker(u.delay)
go u.speedTest()
Loop:
for {
select {
case <-tick.C:
go u.speedTest()
case <-u.done:
break Loop
}
}
}
func (u *URLTest) speedTest() {
wg := sync.WaitGroup{}
wg.Add(len(u.proxies))
c := make(chan interface{})
fast := selectFast(c)
timer := time.NewTimer(u.delay)
for _, p := range u.proxies {
go func(p C.Proxy) {
err := getUrl(p, u.addr, u.rawURL)
if err == nil {
c <- p
}
wg.Done()
}(p)
}
go func() {
wg.Wait()
close(c)
}()
select {
case <-timer.C:
// Wait for fast to return or close.
<-fast
case p, open := <-fast:
if open {
u.fast = p.(C.Proxy)
}
}
}
func getUrl(proxy C.Proxy, addr *C.Addr, rawURL string) (err error) {
instance, err := proxy.Generator(addr)
if err != nil {
return
}
defer instance.Close()
transport := &http.Transport{
Dial: func(string, string) (net.Conn, error) {
return instance.Conn(), nil
},
// from http.DefaultTransport
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
client := http.Client{Transport: transport}
req, err := client.Get(rawURL)
if err != nil {
return
}
req.Body.Close()
return nil
}
func selectFast(in chan interface{}) chan interface{} {
out := make(chan interface{})
go func() {
p, open := <-in
if open {
out <- p
}
close(out)
for range in {
}
}()
return out
}
func NewURLTest(name string, proxies []C.Proxy, rawURL string, delay time.Duration) (*URLTest, error) {
u, err := url.Parse(rawURL)
if err != nil {
return nil, err
}
port := u.Port()
if port == "" {
if u.Scheme == "https" {
port = "443"
} else if u.Scheme == "http" {
port = "80"
} else {
return nil, fmt.Errorf("%s scheme not Support", rawURL)
}
}
addr := &C.Addr{
AddrType: C.AtypDomainName,
Host: u.Hostname(),
IP: nil,
Port: port,
}
urlTest := &URLTest{
name: name,
proxies: proxies[:],
rawURL: rawURL,
url: u,
addr: addr,
fast: proxies[0],
delay: delay,
done: make(chan struct{}),
}
go urlTest.loop()
return urlTest, nil
}