Skip to content

Commit

Permalink
Change domain detect logic
Browse files Browse the repository at this point in the history
  • Loading branch information
wweir committed Apr 18, 2020
1 parent 396b08c commit 91582d6
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 170 deletions.
2 changes: 1 addition & 1 deletion conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func Init() (*Client, *Server, string) {
flag.StringVar(&conf.Server.KeyFile, "s_key", "", "tls key file, gen cert from letsencrypt if empty")
flag.StringVar(&conf.Client.Address, "c", "", "remote server domain, eg: aa.bb.cc, socks5h://127.0.0.1:1080")
flag.StringVar(&conf.Client.HTTPProxy, "http_proxy", ":8080", "http proxy, empty to disable")
flag.IntVar(&conf.Client.Router.DetectLevel, "level", 2, "dynamic rule detect level: 0~4")
flag.IntVar(&conf.Client.Router.DetectLevel, "level", 0, "dynamic rule detect level: -4~4")

if !flag.Parsed() {
flag.Parse()
Expand Down
2 changes: 1 addition & 1 deletion conf/sower.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ password="" # sower password
# eg: ":2222"="aa.bb.cc:22"

[client.router]
detect_level = 1 # 0~4, the bigger the harder to add
detect_level = 0 # [-4, 4], the bigger the harder to add
direct_list = [
"**.in-addr.arpa",
"imap.*.*",
Expand Down
10 changes: 4 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,18 @@ func main() {

if client.HTTPProxy != "" {
go proxy.StartHTTPProxy(client.HTTPProxy, client.Address,
[]byte(password), route.ShouldProxy)
[]byte(password), route.GenProxyCheck(true))
}

enableDNSSolution := client.DNSServeIP != ""
if enableDNSSolution {
if client.DNSUpstream != "" {
transport.SetDNS(client.DNSUpstream)
}
transport.SetDNS(nil, client.DNSUpstream)
go proxy.StartDNS(client.DNSServeIP, client.DNSUpstream,
route.ShouldProxy)
route.GenProxyCheck(false))
}

proxy.StartClient(client.Address, password, enableDNSSolution,
client.PortForward, route.ShouldProxy)
client.PortForward, route.GenProxyCheck(true))

default:
fmt.Println()
Expand Down
7 changes: 4 additions & 3 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,15 @@ func StartClient(serverAddr, password string, enableDNS bool,
}
}

rc, err := transport.Dial(target, func(host string) (string, []byte) {
if shouldProxy(host) {
rc, err := transport.Dial(target, func(domain string) (string, []byte) {
if shouldProxy(domain) {
return serverAddr, passwordData
}
return "", nil
})
if err != nil {
log.Warnw("dial", "addr", serverAddr, "err", err)
host, _, _ := net.SplitHostPort(target)
log.Warnw("dial", "addr", target, "proxy", shouldProxy(host), "err", err)
return
}
defer rc.Close()
Expand Down
16 changes: 7 additions & 9 deletions router/http_ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"crypto/tls"
"encoding/binary"
"io"
"net"
"strconv"
"time"
Expand All @@ -18,27 +19,24 @@ const (
)

// Ping try connect to a http(s) server with domain though the http addr
func (p Port) Ping(domain string, timeout time.Duration) error {
conn, err := net.DialTimeout("tcp", net.JoinHostPort(domain, p.String()), timeout)
func (p Port) Ping(domain string, dial func(string) (net.Conn, error)) error {
conn, err := dial(net.JoinHostPort(domain, p.String()))
if err != nil {
return err
}
defer conn.Close()

return p.PingWithConn(domain, conn, timeout)
}

// PingWithConn try connect to a http(s) server with domain though the http addr
func (p Port) PingWithConn(domain string, conn net.Conn, timeout time.Duration) error {
conn.SetDeadline(time.Now().Add(timeout))
conn.SetDeadline(time.Now().Add(5 * time.Second))
if _, err := conn.Write(p.PingMsg(domain)); err != nil {
return err
}

// err -> nil: read something succ
// err -> io.EOF: no such domain or connection refused
// err -> timeout: tcp package has been dropped
_, err := conn.Read(make([]byte, 10))
if _, err = conn.Read(make([]byte, 10)); err == io.EOF {
err = nil
}
return err
}

Expand Down
132 changes: 38 additions & 94 deletions router/http_ping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,124 +4,68 @@
package router_test

import (
"crypto/tls"
"net"
"strconv"
"testing"
"time"

"github.com/wweir/sower/router"
"github.com/wweir/sower/transport"
)

func TestPort_Ping(t *testing.T) {
type args struct {
domain string
timeout time.Duration
}
tests := []struct {
name string
p router.Port
args args
wantErr bool
}{
{
"baidu_80",
router.HTTP,
args{"baidu.com", time.Second},
false,
},
{
"baidu_443",
router.HTTPS,
args{"baidu.com", time.Second},
false,
},
{
"google_80",
router.HTTP,
args{"google.com", time.Second},
true,
},
{
"google_443",
router.HTTPS,
args{"google.com", time.Second},
true,
},
{
"mail_80",
router.HTTP,
args{"smtp.163.com", time.Second},
true,
},
{
"mail_443",
router.HTTPS,
args{"smtp.163.com", time.Second},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.p.Ping(tt.args.domain, tt.args.timeout); (err != nil) != tt.wantErr {
t.Errorf("Port.Ping() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

var (
proxyAddr string = ""
password []byte = []byte("")
proxyAddr string = "socks5://127.0.0.1:1080"
password []byte = nil
)

func TestPort_PingWithConn(t *testing.T) {
conn, err := transport.Dial(":443", func(string) (string, []byte) {
return proxyAddr, password
})
if err != nil {
t.Errorf("dial remote %s", err)
func TestPort_Ping(t *testing.T) {
direct := func(addr string) (net.Conn, error) {
return net.DialTimeout("tcp", addr, 5*time.Second)
}
proxy := func(addr string) (net.Conn, error) {
conn, err := tls.Dial("tcp", proxyAddr, &tls.Config{})
if err != nil {
return nil, err
}
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
p, err := strconv.Atoi(port)
if err != nil {
return nil, err
}
return transport.ToProxyConn(conn, host, uint16(p), password)
}

type args struct {
domain string
conn net.Conn
timeout time.Duration
domain string
dial func(string) (net.Conn, error)
}
tests := []struct {
name string
p router.Port
args args
wantErr bool
}{
{
"google_80",
router.HTTP,
args{"google.com", conn, 3 * time.Second},
false,
},
{
"google_443",
router.HTTPS,
args{"baidu.com", conn, 3 * time.Second},
false,
},
{
"mail_80",
router.HTTP,
args{"smtp.163.com", conn, 3 * time.Second},
true,
},
{
"mail_443",
router.HTTPS,
args{"smtp.163.com", conn, 3 * time.Second},
true,
},
{"", router.HTTP, args{"baidu.com", direct}, false},
{"", router.HTTP, args{"baidu.com", proxy}, false},
{"", router.HTTPS, args{"baidu.com", direct}, false},
{"", router.HTTPS, args{"baidu.com", proxy}, false},
{"", router.HTTP, args{"google.com", direct}, true},
{"", router.HTTP, args{"google.com", proxy}, false},
{"", router.HTTPS, args{"google.com", direct}, true},
{"", router.HTTPS, args{"google.com", proxy}, false},
{"", router.HTTP, args{"smtp.163.com", direct}, true},
{"", router.HTTP, args{"smtp.163.com", proxy}, true},
{"", router.HTTPS, args{"smtp.163.com", direct}, true},
{"", router.HTTPS, args{"smtp.163.com", proxy}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.p.PingWithConn(tt.args.domain, tt.args.conn, tt.args.timeout); (err != nil) != tt.wantErr {
t.Errorf("Port.PingWithConn() error = %v, wantErr %v", err, tt.wantErr)
if err := tt.p.Ping(tt.args.domain, tt.args.dial); (err != nil) != tt.wantErr {
t.Errorf("Port.Ping() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
Expand Down
66 changes: 37 additions & 29 deletions router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,44 @@ type Route struct {
}

// ShouldProxy check if the domain shoule request though proxy
func (r *Route) ShouldProxy(domain string) bool {
func (r *Route) GenProxyCheck(sync bool) func(string) bool {
r.once.Do(func() {
r.cache = mem.New(4 * time.Hour)
r.password = []byte(r.ProxyPassword)
r.directRule = util.NewNodeFromRules(r.DirectList...)
r.proxyRule = util.NewNodeFromRules(r.ProxyList...)
})

// break deadlook, for wildcard
if strings.Count(domain, ".") > 4 {
return false
}
domain = strings.TrimSuffix(domain, ".")

if domain == r.ProxyAddress {
return false
}

if r.proxyRule.Match(domain) {
detect := func(domain string) bool {
go r.cache.Remember(r, domain)
return true
}
if r.directRule.Match(domain) {
return false
if sync {
detect = func(domain string) bool {
r.cache.Remember(r, domain)
if r.proxyRule.Match(domain) {
return true
}
return !r.directRule.Match(domain)
}
}

go r.cache.Remember(r, domain)
return true
return func(domain string) bool {
domain = strings.TrimSuffix(domain, ".")
// break deadlook, for wildcard
if sepCount := strings.Count(domain, "."); sepCount == 0 || sepCount >= 5 {
return false
}

if r.proxyRule.Match(domain) {
return true
}
if r.directRule.Match(domain) {
return false
}

return detect(domain)
}
}

// Get implement for cache
Expand Down Expand Up @@ -93,19 +104,16 @@ func (r *Route) detect(domain string) (http, https int) {
go func(shouldProxy bool, port Port) {
defer wg.Done()

target := net.JoinHostPort(domain, port.String())
conn, err := transport.Dial(target, func(string) (string, []byte) {
if shouldProxy {
return r.ProxyAddress, r.password
}
return "", nil
})
if err != nil {
log.Warnw("sower dial", "proxy", shouldProxy, "address", target, "err", err)
return
}

if err := port.PingWithConn(domain, conn, 5*time.Second); err != nil {
if err := port.Ping(domain, func(domain string) (net.Conn, error) {
return transport.Dial(domain,
func(domain string) (proxyAddr string, password []byte) {
if shouldProxy {
return r.ProxyAddress, r.password
}
return "", nil
})
}); err != nil {
log.Warnw("sower dial", "proxy", shouldProxy, "host", domain, "port", port, "err", err)
return
}

Expand Down
5 changes: 2 additions & 3 deletions transport/proxy_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package transport

import (
"crypto/md5"
"crypto/tls"
"encoding/binary"
"io"
"net"
Expand All @@ -12,7 +11,7 @@ import (
)

// checksum(>=0x80) + port + target_length + target + data
// data(HTTP/HTTPS, first byte < 0x7F)
// data(HTTP, first byte < 0x7F)
type head struct {
Checksum byte
Port uint16
Expand Down Expand Up @@ -41,7 +40,7 @@ func ParseProxyConn(conn net.Conn, password []byte) (net.Conn, string) {
return teeConn, net.JoinHostPort(string(buf), strconv.Itoa(int(h.Port)))
}

func ToProxyConn(conn net.Conn, tgtHost string, tgtPort uint16, tlsCfg *tls.Config, password []byte) (net.Conn, error) {
func ToProxyConn(conn net.Conn, tgtHost string, tgtPort uint16, password []byte) (net.Conn, error) {
h := &head{
Checksum: sumChecksum([]byte(tgtHost), password),
Port: tgtPort,
Expand Down
Loading

0 comments on commit 91582d6

Please sign in to comment.