mirror of
https://github.com/wweir/sower.git
synced 2024-04-21 12:42:15 +00:00
Refactor to proxy router
This commit is contained in:
+105
-80
@@ -3,128 +3,153 @@ package conf
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
toml "github.com/pelletier/go-toml"
|
||||
"github.com/wweir/sower/util"
|
||||
"github.com/wweir/utils/log"
|
||||
)
|
||||
|
||||
// Conf define the config items
|
||||
var Conf = struct {
|
||||
ConfigFile string
|
||||
NetType string `toml:"net_type"`
|
||||
Cipher string `toml:"cipher"`
|
||||
Password string `toml:"password"`
|
||||
var (
|
||||
version, date string
|
||||
|
||||
ServerPort string `toml:"server_port"`
|
||||
ServerAddr string `toml:"server_addr"`
|
||||
HTTPProxy string `toml:"http_proxy"`
|
||||
flushOnce = sync.Once{}
|
||||
flushMu = sync.Mutex{}
|
||||
flushCh = make(chan struct{})
|
||||
|
||||
DNSServer string `toml:"dns_server"`
|
||||
ClientIP string `toml:"client_ip"`
|
||||
SuggestLevel string `toml:"suggest_level"`
|
||||
ClearDNSCache string `toml:"clear_dns_cache"`
|
||||
// Conf define the config items
|
||||
Conf = struct {
|
||||
ConfigFile string
|
||||
|
||||
BlockList []string `toml:"blocklist"`
|
||||
WhiteList []string `toml:"whitelist"`
|
||||
Suggestions []string `toml:"suggestions"`
|
||||
Verbose int `toml:"verbose"`
|
||||
VersionOnly bool `toml:"-"`
|
||||
}{}
|
||||
Upstream struct {
|
||||
Socks5 string `toml:"socks5"`
|
||||
DNS string `toml:"dns"`
|
||||
} `toml:"upstream"`
|
||||
|
||||
Downstream struct {
|
||||
ServeIP string `toml:"serve_ip"`
|
||||
HTTPProxy string `toml:"http_proxy"`
|
||||
} `toml:"downstream"`
|
||||
|
||||
Router struct {
|
||||
FlushDNSCmd string `toml:"flush_dns_cmd"`
|
||||
ProxyLevel int `toml:"proxy_level"`
|
||||
|
||||
PortMapping map[string]string `toml:"port_mapping"`
|
||||
ProxyList []string `toml:"proxy_list"`
|
||||
DirectList []string `toml:"direct_list"`
|
||||
DynamicList []string `toml:"dynamic_list"`
|
||||
} `toml:"router"`
|
||||
}{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
initArgs()
|
||||
if Conf.VersionOnly {
|
||||
return
|
||||
flag.StringVar(&Conf.ConfigFile, "f", "", "config file, keep empty for dynamic detect proxy rule")
|
||||
flag.StringVar(&Conf.Upstream.Socks5, "socks5", "127.0.0.1:1080", "upstream socks5 address")
|
||||
flag.StringVar(&Conf.Upstream.DNS, "dns", "", "upstream dns ip, keep empty to dynamic detect")
|
||||
flag.StringVar(&Conf.Downstream.ServeIP, "serve", "127.0.0.1", "serve on address")
|
||||
flag.StringVar(&Conf.Downstream.HTTPProxy, "http_proxy", "", "serve http proxy, eg: 127.0.0.1:8080")
|
||||
flag.IntVar(&Conf.Router.ProxyLevel, "level", 2, "dynamic proxy level: 0~4")
|
||||
|
||||
Init() // execute platform init logic
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
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)
|
||||
if _, err := os.Stat(Conf.ConfigFile); err == nil {
|
||||
for i := range loadConfigFns {
|
||||
if err := loadConfigFns[i].fn(); err != nil {
|
||||
log.Fatalw("load config", "config", Conf.ConfigFile, "step", loadConfigFns[i].step, "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go addSuggestions()
|
||||
log.Infow("start", "version", version, "date", date, "config", Conf)
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return action, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
//safe refresh config
|
||||
file := Conf.ConfigFile
|
||||
if err = toml.NewDecoder(f).Decode(&Conf); err != nil {
|
||||
return action, err
|
||||
}
|
||||
Conf.ConfigFile = file
|
||||
|
||||
return action, flag.Set("v", strconv.Itoa(Conf.Verbose))
|
||||
},
|
||||
func() (string, error) {
|
||||
action := "clear dns cache"
|
||||
if Conf.ClearDNSCache != "" {
|
||||
return action, execute(Conf.ClearDNSCache)
|
||||
}
|
||||
return action, nil
|
||||
},
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
var loadConfigFns = []struct {
|
||||
step string
|
||||
fn func() error
|
||||
}{{"load_config", func() error {
|
||||
f, err := os.OpenFile(Conf.ConfigFile, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
refreshFns = append(refreshFns, fn)
|
||||
//safe refresh config
|
||||
file := Conf.ConfigFile
|
||||
if err = toml.NewDecoder(f).Decode(&Conf); err != nil {
|
||||
return err
|
||||
}
|
||||
Conf.ConfigFile = file
|
||||
return nil
|
||||
|
||||
}}, {"flush_dns", func() error {
|
||||
if Conf.Router.FlushDNSCmd != "" {
|
||||
return execute(Conf.Router.FlushDNSCmd)
|
||||
}
|
||||
return nil
|
||||
}}}
|
||||
|
||||
// AddReloadConfigHook add hook function for reload config
|
||||
func AddReloadConfigHook(step string, fn func() error) {
|
||||
loadConfigFns = append(loadConfigFns, struct {
|
||||
step string
|
||||
fn func() error
|
||||
}{step, fn})
|
||||
}
|
||||
|
||||
// SuggestCh add domain into suggestios
|
||||
var SuggestCh = make(chan string)
|
||||
// AddDynamic add new domain into dynamic list
|
||||
func AddDynamic(domain string) {
|
||||
flushMu.Lock()
|
||||
Conf.Router.DynamicList = append(Conf.Router.DynamicList, domain)
|
||||
Conf.Router.DynamicList = util.NewReverseSecSlice(Conf.Router.DynamicList).Sort().Uniq()
|
||||
flushMu.Unlock()
|
||||
|
||||
// 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()
|
||||
flushOnce.Do(func() {
|
||||
if Conf.ConfigFile != "" {
|
||||
go flushConf()
|
||||
}
|
||||
})
|
||||
|
||||
{ // safe write
|
||||
select {
|
||||
case flushCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func flushConf() {
|
||||
for range flushCh {
|
||||
// safe write
|
||||
if Conf.ConfigFile != "" {
|
||||
f, err := os.OpenFile(Conf.ConfigFile+"~", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
log.Errorw("flush config", "step", "flush", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
flushMu.Lock()
|
||||
if err := toml.NewEncoder(f).ArraysWithOneElementPerLine(true).Encode(Conf); err != nil {
|
||||
glog.Errorln(err)
|
||||
log.Errorw("flush config", "step", "flush", "err", err)
|
||||
flushMu.Unlock()
|
||||
f.Close()
|
||||
continue
|
||||
}
|
||||
flushMu.Unlock()
|
||||
f.Close()
|
||||
|
||||
if err = os.Rename(Conf.ConfigFile+"~", Conf.ConfigFile); err != nil {
|
||||
glog.Errorln(err)
|
||||
log.Errorw("flush config", "step", "flush", "err", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// reload config
|
||||
for i := range refreshFns {
|
||||
if action, err := refreshFns[i](); err != nil {
|
||||
glog.Errorln(action+":", err)
|
||||
for i := range loadConfigFns {
|
||||
if err := loadConfigFns[i].fn(); err != nil {
|
||||
log.Errorw("flush config", "step", loadConfigFns[i].step, "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// +build darwin
|
||||
|
||||
package conf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
flag.StringVar(&Conf.Router.FlushDNSCmd, "flush_dns", "pkill mDNSResponder || true", "flush dns command")
|
||||
}
|
||||
|
||||
func execute(cmd string) error {
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
out, err := exec.CommandContext(ctx, "sh", "-c", Conf.Router.FlushDNSCmd).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmd: %s, err: %s, output: %s", Conf.Router.FlushDNSCmd, err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// +build linux
|
||||
|
||||
package conf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
flag.StringVar(&Conf.Router.FlushDNSCmd, "flush_dns", "", "flush dns command")
|
||||
}
|
||||
|
||||
func execute(cmd string) error {
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
out, err := exec.CommandContext(ctx, "sh", "-c", Conf.Router.FlushDNSCmd).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmd: %s, err: %s, output: %s", Conf.Router.FlushDNSCmd, err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package conf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wweir/sower/dns"
|
||||
"github.com/wweir/sower/proxy/shadow"
|
||||
"github.com/wweir/sower/proxy/transport"
|
||||
)
|
||||
|
||||
func initArgs() {
|
||||
cfgFile, _ := filepath.Abs(filepath.Join(filepath.Dir(os.Args[0]), "sower.toml"))
|
||||
flag.StringVar(&Conf.ConfigFile, "f", cfgFile, "config file location")
|
||||
flag.StringVar(&Conf.NetType, "n", "TCP", "net type (socks5 client only): "+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.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, "l", "SPEEDUP", "suggest level setting: "+strings.Join(dns.ListSuggestLevels(), ","))
|
||||
flag.BoolVar(&Conf.VersionOnly, "V", false, "print sower version")
|
||||
|
||||
if !flag.Parsed() {
|
||||
flag.Set("logtostderr", "true")
|
||||
flag.Parse()
|
||||
}
|
||||
}
|
||||
|
||||
func execute(cmd string) error {
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
out, err := exec.CommandContext(ctx, "sh", "-c", Conf.ClearDNSCache).CombinedOutput()
|
||||
return errors.Wrapf(err, "cmd: %s, output: %s, error", Conf.ClearDNSCache, out)
|
||||
}
|
||||
+20
-23
@@ -13,8 +13,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wweir/utils/log"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
@@ -24,19 +23,15 @@ import (
|
||||
const name = "sower"
|
||||
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
|
||||
|
||||
func initArgs() {
|
||||
cfgFile, _ := filepath.Abs(filepath.Join(filepath.Dir(os.Args[0]), "sower.toml"))
|
||||
flag.StringVar(&Conf.ConfigFile, "f", cfgFile, "config file location")
|
||||
flag.BoolVar(&Conf.VersionOnly, "V", false, "print sower version")
|
||||
install := flag.Bool("install", false, "install sower as a service")
|
||||
uninstall := flag.Bool("uninstall", false, "uninstall sower from service list")
|
||||
exePath,_:=filepath.Abs(os.Args[0])
|
||||
func Init() {
|
||||
exePath, _ := filepath.Abs(os.Args[0])
|
||||
logFile := filepath.Join(filepath.Dir(exePath), name+".log")
|
||||
|
||||
if !flag.Parsed() {
|
||||
os.Mkdir("log", 0755)
|
||||
flag.Set("log_dir", filepath.Dir(os.Args[0])+"/log")
|
||||
flag.Parse()
|
||||
}
|
||||
install := flag.Bool("i", false, "install sower as a service")
|
||||
uninstall := flag.Bool("u", false, "uninstall sower from service list")
|
||||
logFile := flag.String("log", logFile, name+" log file path")
|
||||
flag.StringVar(&Conf.Router.FlushDNSCmd, "flush_dns", "ipconfig /flushdnss", "flush dns command")
|
||||
flag.Parse()
|
||||
|
||||
switch {
|
||||
case *install:
|
||||
@@ -46,7 +41,7 @@ func initArgs() {
|
||||
s.Close()
|
||||
return fmt.Errorf("service %s already exists", name)
|
||||
}
|
||||
s, err = m.CreateService(name, exePath, mgr.Config{
|
||||
s, err = m.CreateService(name, exePath, mgr.Config{
|
||||
DisplayName: "Sower Proxy",
|
||||
StartType: windows.SERVICE_AUTO_START,
|
||||
})
|
||||
@@ -77,18 +72,18 @@ func initArgs() {
|
||||
default:
|
||||
os.Chdir(filepath.Dir(os.Args[0]))
|
||||
if active, err := svc.IsAnInteractiveSession(); err != nil {
|
||||
glog.Exitf("failed to determine if we are running in an interactive session: %v", err)
|
||||
log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
|
||||
} else if !active {
|
||||
go func() {
|
||||
elog, err := eventlog.Open(name)
|
||||
if err != nil {
|
||||
glog.Exitln(err)
|
||||
log.Fatalw("install service", "err", err)
|
||||
}
|
||||
defer elog.Close()
|
||||
|
||||
if err := svc.Run(name, &myservice{}); err != nil {
|
||||
elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err))
|
||||
glog.Exitln(err)
|
||||
log.Fatalw("install service", "err", err)
|
||||
}
|
||||
elog.Info(1, fmt.Sprintf("winsvc.RunAsService: %s service stopped", name))
|
||||
os.Exit(0)
|
||||
@@ -110,12 +105,12 @@ func serviceDo(fn func(*mgr.Service) error) {
|
||||
func mgrDo(fn func(m *mgr.Mgr) error) {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
glog.Exitln(err)
|
||||
log.Fatalw("install service", "err", err)
|
||||
}
|
||||
defer m.Disconnect()
|
||||
|
||||
if err := fn(m); err != nil {
|
||||
glog.Fatalln(err)
|
||||
log.Fatalw("install service", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +119,7 @@ type myservice struct{}
|
||||
func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
||||
elog, err := eventlog.Open(name)
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
log.Errorw("install service", "err", err)
|
||||
return
|
||||
}
|
||||
defer elog.Close()
|
||||
@@ -174,6 +169,8 @@ func execute(cmd string) error {
|
||||
|
||||
command := exec.CommandContext(ctx, cmds[0], cmds[1:]...)
|
||||
command.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
out, err := command.CombinedOutput()
|
||||
return errors.Wrapf(err, "cmd: %s, output: %s, error", Conf.ClearDNSCache, out)
|
||||
if out, err := command.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("cmd: %s, output: %s, err: %w", Conf.ClearDNSCache, out, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+16
-13
@@ -1,14 +1,18 @@
|
||||
net_type="TCP" # TCP, KCP, QUIC, SOCKS5(client only)
|
||||
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" # replce it to remote server
|
||||
http_proxy=":8080" # eg: 192.168.0.2:8080
|
||||
dns_server="" # eg: 223.5.5.5:53, 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 || true" # Windows: "ipconfig /flushdnss"
|
||||
suggest_level="SPEEDUP" # DISABLE, BLOCK, SPEEDUP
|
||||
blocklist=[
|
||||
[upstream]
|
||||
socks5="127.0.0.1:1080"
|
||||
dns="" # eg: 223.5.5.5, keep empty to get it from network environment
|
||||
|
||||
[downstream]
|
||||
serve_ip="127.0.0.1"
|
||||
http_proxy="" # eg: :8080, 127.0.0.1:8080, keep empty to disable it
|
||||
|
||||
[router.port_mapping]
|
||||
# 2222="aaa.bbb.cc:22"
|
||||
|
||||
[router]
|
||||
flush_dns_cmd="" # Platform-related, keep empty to use default command
|
||||
proxy_level=2 # dynamic detect level, (0~4), more bigger more harder to be proxy
|
||||
proxy_list=[
|
||||
"**.google.*", # google
|
||||
"**.goo.gl",
|
||||
"**.googleusercontent.com",
|
||||
@@ -31,7 +35,7 @@ blocklist=[
|
||||
"*.githubusercontent.com",
|
||||
"*.github.*",
|
||||
]
|
||||
whitelist=[
|
||||
direct_list=[
|
||||
"**.in-addr.arpa",
|
||||
"imap.*.*",
|
||||
"imap.*.*.*",
|
||||
@@ -48,4 +52,3 @@ whitelist=[
|
||||
"**.163.com",
|
||||
"**.weiyun.com",
|
||||
]
|
||||
verbose=0
|
||||
|
||||
Reference in New Issue
Block a user