Skip to content

Commit

Permalink
Merge branch 'feature/suggest-level'
Browse files Browse the repository at this point in the history
  • Loading branch information
wweir committed Mar 14, 2019
2 parents b3ad9f6 + 199e4cf commit f136875
Show file tree
Hide file tree
Showing 12 changed files with 183 additions and 96 deletions.
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ SERVER:=127.0.0.1:5533
default: test build

generate:
ifeq ("", "$(shell which stringer)")
@echo installing generator
go get -v golang.org/x/tools/cmd/stringer
endif
go generate ./...

test:
go vet ./...
go test ./...
Expand Down
105 changes: 62 additions & 43 deletions conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (
"os/exec"
"runtime"
"strconv"
"sync"
"time"

"github.com/pkg/errors"

"github.com/golang/glog"
toml "github.com/pelletier/go-toml"
"github.com/wweir/sower/util"
Expand All @@ -28,6 +29,7 @@ var Conf = struct {

DNSServer string `toml:"dns_server"`
ClientIP string `toml:"client_ip"`
SuggestLevel string `toml:"suggest_level"`
ClearDNSCache string `toml:"clear_dns_cache"`

BlockList []string `toml:"blocklist"`
Expand All @@ -36,8 +38,24 @@ var Conf = struct {
Verbose int `toml:"verbose"`
}{}

// OnRefreash will be executed while init and write new config
var OnRefreash = []func() (string, error){
func init() {
initArgs()

if _, err := os.Stat(Conf.ConfigFile); os.IsNotExist(err) {
glog.Warningln("no config file has been load:", Conf.ConfigFile)
return
}
for i := range refreshFns {
if action, err := refreshFns[i](); err != nil {
glog.Fatalln(action+":", err)
}
}

go addSuggestions()
}

// refreshFns will be executed while init and write new config
var refreshFns = []func() (string, error){
func() (string, error) {
action := "load config"
f, err := os.OpenFile(Conf.ConfigFile, os.O_RDONLY, 0644)
Expand All @@ -63,64 +81,65 @@ var OnRefreash = []func() (string, error){

switch runtime.GOOS {
case "windows":
return action, exec.CommandContext(ctx, "cmd", "/c", Conf.ClearDNSCache).Run()
if out, err := exec.CommandContext(ctx, "cmd", "/c", Conf.ClearDNSCache).CombinedOutput(); err != nil {
return action, errors.Wrapf(err, "cmd: %s, output: %s, error", Conf.ClearDNSCache, out)
}
default:
return action, exec.CommandContext(ctx, "sh", "-c", Conf.ClearDNSCache).Run()
if out, err := exec.CommandContext(ctx, "sh", "-c", Conf.ClearDNSCache).CombinedOutput(); err != nil {
return action, errors.Wrapf(err, "cmd: %s, output: %s, error", Conf.ClearDNSCache, out)
}
}
}
return action, nil
},
}

func init() {
initArgs()

if _, err := os.Stat(Conf.ConfigFile); os.IsNotExist(err) {
glog.Warningln("no config file has been load:", Conf.ConfigFile)
return
}
for i := range OnRefreash {
if action, err := OnRefreash[i](); err != nil {
glog.Fatalln(action+":", err)
// AddRefreshFn add refreshh function for reload config
func AddRefreshFn(init bool, fn func() (string, error)) error {
if init {
if _, err := fn(); err != nil {
return err
}
}
}

// mu keep synchronized add rule(write), do not care read while write
var mu = &sync.Mutex{}
refreshFns = append(refreshFns, fn)
return nil
}

// AddSuggestion add new domain into suggest rules
func AddSuggestion(domain string) {
mu.Lock()
defer mu.Unlock()
// SuggestCh add domain into suggestios
var SuggestCh = make(chan string)

Conf.Suggestions = append(Conf.Suggestions, domain)
Conf.Suggestions = util.NewReverseSecSlice(Conf.Suggestions).Sort().Uniq()
// addSuggestions add new domain into suggest rules
func addSuggestions() {
for domain := range SuggestCh {
Conf.Suggestions = append(Conf.Suggestions, domain)
Conf.Suggestions = util.NewReverseSecSlice(Conf.Suggestions).Sort().Uniq()

{ // safe write
f, err := os.OpenFile(Conf.ConfigFile+"~", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
glog.Errorln(err)
return
}
{ // safe write
f, err := os.OpenFile(Conf.ConfigFile+"~", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
glog.Errorln(err)
continue
}

if err := toml.NewEncoder(f).ArraysWithOneElementPerLine(true).Encode(Conf); err != nil {
glog.Errorln(err)
if err := toml.NewEncoder(f).ArraysWithOneElementPerLine(true).Encode(Conf); err != nil {
glog.Errorln(err)
f.Close()
continue
}
f.Close()
return
}
f.Close()

if err = os.Rename(Conf.ConfigFile+"~", Conf.ConfigFile); err != nil {
glog.Errorln(err)
return
if err = os.Rename(Conf.ConfigFile+"~", Conf.ConfigFile); err != nil {
glog.Errorln(err)
continue
}
}
}

// reload config
for i := range OnRefreash {
if action, err := OnRefreash[i](); err != nil {
glog.Errorln(action+":", err)
// reload config
for i := range refreshFns {
if action, err := refreshFns[i](); err != nil {
glog.Errorln(action+":", err)
}
}
}
}
9 changes: 6 additions & 3 deletions conf/conf_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ import (
"path/filepath"
"strings"

"github.com/wweir/sower/dns"
"github.com/wweir/sower/proxy/shadow"
"github.com/wweir/sower/proxy/transport"
)

func initArgs() {
flag.StringVar(&Conf.ConfigFile, "f", filepath.Dir(os.Args[0])+"/sower.toml", "config file location")
flag.StringVar(&Conf.NetType, "n", "TCP", "proxy net type ("+strings.Join(transport.ListTransports(), "|")+")")
flag.StringVar(&Conf.Cipher, "C", "AES_128_GCM", "cipher type (AES_128_GCM|AES_192_GCM|AES_256_GCM|CHACHA20_IETF_POLY1305|XCHACHA20_IETF_POLY1305)")
flag.StringVar(&Conf.NetType, "n", "TCP", "proxy net type: "+strings.Join(transport.ListTransports(), ","))
flag.StringVar(&Conf.Cipher, "C", "AES_128_GCM", "cipher type: "+strings.Join(shadow.ListCiphers(), ","))
flag.StringVar(&Conf.Password, "p", "12345678", "password")
flag.StringVar(&Conf.ServerPort, "P", "5533", "server mode listen port")
flag.StringVar(&Conf.ServerAddr, "s", "", "server IP (run in client mode if set)")
flag.StringVar(&Conf.ServerAddr, "s", "", "server IP (run in CLIENT MODE if set)")
flag.StringVar(&Conf.HTTPProxy, "H", "", "http proxy listen addr")
flag.StringVar(&Conf.DNSServer, "d", "114.114.114.114", "client dns server")
flag.StringVar(&Conf.ClientIP, "c", "127.0.0.1", "client dns service redirect IP")
flag.StringVar(&Conf.SuggestLevel, "S", "SPEEDUP", "suggest level setting: "+strings.Join(dns.ListSuggestLevels(), ","))

if !flag.Parsed() {
flag.Set("logtostderr", "true")
Expand Down
7 changes: 4 additions & 3 deletions conf/sower.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
net_type="TCP"
cipher="AES_128_GCM"
net_type="TCP" # TCP, KCP, QUIC
cipher="AES_128_GCM" # AES_128_GCM, AES_192_GCM, AES_256_GCM, CHACHA20_IETF_POLY1305, XCHACHA20_IETF_POLY1305
password="12345678"
server_port="5533"
# server_addr="remote-server:5533" # replce it to remote server
http_proxy=":8080" # eg: 192.168.0.2:8080
dns_server="223.5.5.5" # Keep empty for dynamic setting from net env
dns_server="114.114.114.114" # Keep empty for dynamic setting from net env
client_ip="127.0.0.1" # listen the IP, dns target is the IP
# clear_dns_cache="pkill mDNSResponder" # Windows: "ipconfig /flushdnss"
suggest_level="SPEEDUP" # DISABLE, BLOCK, SPEEDUP
blocklist=[
"**.google.*", # google
"**.goo.gl",
Expand Down
23 changes: 15 additions & 8 deletions dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import (
"github.com/golang/glog"
"github.com/miekg/dns"
mem "github.com/wweir/mem-go"
"github.com/wweir/sower/conf"
)

const colon = byte(':')

func StartDNS(dnsServer, listenIP string) {
func StartDNS(dnsServer, listenIP string, suggestCh chan<- string, suggestLevel string) {
ip := net.ParseIP(listenIP)

suggest := &intelliSuggest{listenIP, time.Second}
suggest := &intelliSuggest{suggestCh, parseSuggestLevel(suggestLevel), listenIP, time.Second}
mem.DefaultCache = mem.New(time.Hour)

dhcpCh := make(chan struct{})
Expand Down Expand Up @@ -97,12 +96,17 @@ func matchAndServe(w dns.ResponseWriter, r *dns.Msg, domain, listenIP, dnsServer
}

type intelliSuggest struct {
listenIP string
timeout time.Duration
suggestCh chan<- string
suggestLevel suggestLevel
listenIP string
timeout time.Duration
}

func (i *intelliSuggest) GetOne(domain interface{}) (iface interface{}, e error) {
iface, e = struct{}{}, nil
if i.suggestLevel == DISABLE {
return
}

// kill deadloop, for ugly wildcard setting dns setting
addr := strings.TrimSuffix(domain.(string), ".")
Expand Down Expand Up @@ -139,11 +143,14 @@ func (i *intelliSuggest) GetOne(domain interface{}) (iface interface{}, e error)
}

// remote ping faster
} else if atomic.CompareAndSwapInt32(protos[idx/2], 0, 1) && pings[idx].viaAddr == i.listenIP {
atomic.AddInt32(score, 1)
} else if pings[idx].viaAddr == i.listenIP {
if atomic.CompareAndSwapInt32(protos[idx/2], 0, 1) && i.suggestLevel == SPEEDUP {
atomic.AddInt32(score, 1)
}
glog.V(1).Infof("remote ping %s faster", addr)

} else {
atomic.CompareAndSwapInt32(protos[idx/2], 0, 2)
return // score change trigger add suggestion
}

Expand All @@ -160,7 +167,7 @@ func (i *intelliSuggest) GetOne(domain interface{}) (iface interface{}, e error)
// 2. all remote pings are faster
if atomic.LoadInt32(score) >= int32(len(protos)) {
old := atomic.SwapInt32(score, -1) // avoid readd the suggestion
conf.AddSuggestion(addr)
i.suggestCh <- addr
glog.Infof("suggested domain: %s with score: %d", addr, old)
}
}(idx)
Expand Down
16 changes: 16 additions & 0 deletions dns/suggestlevel_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 36 additions & 19 deletions dns/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,22 @@ import (

"github.com/golang/glog"
"github.com/miekg/dns"
"github.com/wweir/sower/conf"
"github.com/wweir/sower/util"
)

var (
blockList *util.Node
whiteList *util.Node
suggestList *util.Node
whiteList *util.Node
)

func init() {
host, _, _ := net.SplitHostPort(conf.Conf.ServerAddr)

//first init
blockList = loadRules("block", conf.Conf.BlockList)
suggestList = loadRules("suggest", conf.Conf.Suggestions)
whiteList = loadRules("white", conf.Conf.WhiteList)
// LoadRules init rules from config
func LoadRules(blocklist, suggestions, whitelist []string, host string) {
blockList = loadRules("block", blocklist)
suggestList = loadRules("suggest", suggestions)
whiteList = loadRules("white", whitelist)
whiteList.Add(host)
glog.V(1).Infoln("load config")

conf.OnRefreash = append(conf.OnRefreash, func() (string, error) {
blockList = loadRules("block", conf.Conf.BlockList)
suggestList = loadRules("suggest", conf.Conf.Suggestions)
whiteList = loadRules("white", conf.Conf.WhiteList)
whiteList.Add(host)
glog.V(1).Infoln("reload config")
return "reload config", nil
})
glog.V(1).Infoln("reloaded config")
}

func loadRules(name string, list []string) *util.Node {
Expand All @@ -57,3 +45,32 @@ func localA(r *dns.Msg, domain string, localIP net.IP) *dns.Msg {
}
return m
}

//go:generate stringer -type=suggestLevel $GOFILE
type suggestLevel int32

const (
DISABLE suggestLevel = iota
BLOCK
SPEEDUP
levelEnd
)

func ListSuggestLevels() []string {
list := make([]string, 0, int(levelEnd))
for i := suggestLevel(0); i < levelEnd; i++ {
list = append(list, i.String())
}
return list
}

func parseSuggestLevel(level string) suggestLevel {
for i := suggestLevel(0); i < levelEnd; i++ {
if level == i.String() {
return i
}
}

glog.Exitln("invalid suggest level: " + level)
return levelEnd
}
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f // indirect
github.com/lucas-clemente/quic-go v0.10.1
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced // indirect
github.com/miekg/dns v1.1.5
github.com/miekg/dns v1.1.6
github.com/pelletier/go-toml v1.2.0
github.com/pkg/errors v0.8.1
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
Expand All @@ -23,7 +23,7 @@ require (
github.com/ulule/deepcopier v0.0.0-20171107155558-ca99b135e50f // indirect
github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296
github.com/xtaci/kcp-go v5.0.7+incompatible
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
golang.org/x/net v0.0.0-20190310014029-b774fd8d5c0f // indirect
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a
golang.org/x/net v0.0.0-20190313220215-9f648a60d977 // indirect
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e
)
Loading

0 comments on commit f136875

Please sign in to comment.