mirror of
https://github.com/wweir/sower.git
synced 2026-10-20 00:15:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52c7bd19f9 | ||
|
|
c761ed6de6 | ||
|
|
4883e240cc | ||
|
|
18ee9ca993 | ||
|
|
4fff2a1f3b | ||
|
|
3e82890c33 | ||
|
|
01d66d737b | ||
|
|
e342dadaf1 | ||
|
|
7ba9f1a23c | ||
|
|
7240208cb3 | ||
|
|
a27748d835 | ||
|
|
39797d077b | ||
|
|
b6df69358a | ||
|
|
ae525f9b40 | ||
|
|
29eb0e6e1c | ||
|
|
aef941b17e | ||
|
|
9bc5a32074 | ||
|
|
8194083f54 |
@@ -13,17 +13,21 @@ jobs:
|
||||
cp deploy/* .
|
||||
mkdir artifacts
|
||||
- run:
|
||||
name: build linux Binary
|
||||
name: Run Unit Test
|
||||
command: |
|
||||
make test
|
||||
- run:
|
||||
name: Build linux Binary
|
||||
command: |
|
||||
make build
|
||||
tar czvf artifacts/sower_linux_amd64.tar.gz sower sower.toml sower-client.service sower-server.service
|
||||
- run:
|
||||
name: build macOS Binary
|
||||
name: Build macOS Binary
|
||||
command: |
|
||||
GOOS=darwin GOARCH=amd64 make build
|
||||
tar czvf artifacts/sower_darwin_amd64.tar.gz sower sower.toml cc.wweir.sower.plist
|
||||
- run:
|
||||
name: build Windows Binary
|
||||
name: Build Windows Binary
|
||||
command: |
|
||||
GOOS=windows GOARCH=amd64 make build
|
||||
tar czvf artifacts/sower_windows_amd64.tar.gz sower sower.toml
|
||||
@@ -38,7 +42,7 @@ jobs:
|
||||
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION%-*-*} artifacts/
|
||||
fi
|
||||
- run:
|
||||
name: build docker image
|
||||
name: Build Docker Image
|
||||
command: |
|
||||
if git log -1 --pretty='%s' | grep -oE '(^|\s)@release(\s|$)'; then
|
||||
docker login -u $DOCKER_USER -p $DOCKER_PASS
|
||||
|
||||
+3
-5
@@ -12,12 +12,10 @@
|
||||
*.out
|
||||
|
||||
.DS_Store
|
||||
**/.DS_Store
|
||||
._*
|
||||
*.swp
|
||||
*.swo
|
||||
.vscode
|
||||
.idea
|
||||
sower
|
||||
cert.pem
|
||||
**/cert.pem
|
||||
privkey.pem
|
||||
**/privkey.pem
|
||||
/sower.toml
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
SERVER:=127.0.0.1:5533
|
||||
|
||||
default: build
|
||||
default: test build
|
||||
|
||||
generate:
|
||||
go generate ./...
|
||||
|
||||
test:
|
||||
go test -race ./...
|
||||
build:
|
||||
GO111MODULE=on go build -v -ldflags \
|
||||
"-X main.version=$(shell git describe --tags) \
|
||||
@@ -16,14 +17,14 @@ kill:
|
||||
sudo pkill -9 sower || true
|
||||
|
||||
client: build kill
|
||||
sudo $(PWD)/sower -f conf/sower.toml
|
||||
sudo $(PWD)/sower
|
||||
|
||||
server: build kill
|
||||
$(PWD)/sower -n TCP -v 1
|
||||
|
||||
run: build kill
|
||||
$(PWD)/sower -n TCP -v 1 &
|
||||
sudo $(PWD)/sower -f conf/sower.toml &
|
||||
sudo $(PWD)/sower &
|
||||
@sleep 1
|
||||
curl 127.0.0.1
|
||||
@sleep 1
|
||||
|
||||
@@ -17,6 +17,8 @@ Sower also provides an http(s) proxy listening on `:8080` by default. You can tu
|
||||
|
||||
|
||||
## Installation
|
||||
After Deployed, please check your config file, it is placed in `/usr/local/etc/sower.toml` by default. Here is the example config file [**conf/sower.toml**](https://github.com/wweir/sower/blob/master/conf/sower.toml)
|
||||
|
||||
### Auto deploy
|
||||
Auto deploy script support Linux server side and masOS/Linux client side.
|
||||
|
||||
@@ -39,7 +41,7 @@ If you wanna uninstall sower, change `install` into `uninstall` and rerun the co
|
||||
### Docker deploy
|
||||
The auto build docker images are [wweir/sower](https://hub.docker.com/r/wweir/sower).
|
||||
|
||||
It is very simple to use it on the server side. Map the port and run it directly.
|
||||
It is very simple to use it on the server side. Export the port(5533) and run it directly.
|
||||
|
||||
But the client is more troublesome and needs some understanding of the working mechanism of the sower.
|
||||
|
||||
@@ -88,4 +90,4 @@ For more detail, see [透明代理 Sower 技术剖析](https://wweir.cc/post/%E9
|
||||
- [x] CI/CD
|
||||
- [ ] relay optimization
|
||||
- [ ] deploy script for all normal platform
|
||||
- [x] dns rule intelligent suggestions
|
||||
- [x] dns rule intelligent suggestions
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ func watchConfigFile() {
|
||||
}
|
||||
}
|
||||
|
||||
glog.Infof("watch %s event: %v", Conf.ConfigFile, event)
|
||||
glog.V(1).Infof("watch %s event: %v", Conf.ConfigFile, event)
|
||||
for i := range OnRefreash {
|
||||
if err := OnRefreash[i](); err != nil {
|
||||
glog.Errorln(err)
|
||||
|
||||
+33
-15
@@ -4,23 +4,41 @@ 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" # Alibaba public dns
|
||||
dns_server="" # 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="killall -HUP mDNSResponder"
|
||||
blocklist=[
|
||||
"*.google.com", # google
|
||||
"*.goo.gl",
|
||||
"*.googleusercontent.com",
|
||||
"*.googleapis.com",
|
||||
"*.youtube.com", # youtube
|
||||
"*.ytimg.com",
|
||||
"*.ggpht.com",
|
||||
"*.googlevideo.com",
|
||||
"*.facebook.com", # facebook
|
||||
"*.xx.fbcdn.net",
|
||||
"*.twitter.com", # twitter
|
||||
"*.twimg.com",
|
||||
"*.blogspot.com", # blogspot
|
||||
"*.wikipedia.org", # wikipeida
|
||||
"**.google.com", # google
|
||||
"**.goo.gl",
|
||||
"**.googleusercontent.com",
|
||||
"**.googleapis.com",
|
||||
"*.googlesource.com",
|
||||
"**.youtube.com", # youtube
|
||||
"**.ytimg.com",
|
||||
"**.ggpht.com",
|
||||
"**.googlevideo.com",
|
||||
"**.facebook.com", # facebook
|
||||
"**.fbcdn.net",
|
||||
"**.twitter.com", # twitter
|
||||
"**.twimg.com",
|
||||
"**.blogspot.com", # blogspot
|
||||
"**.appspot.com",
|
||||
"**.wikipedia.org", # wikipeida
|
||||
"*.cloudfront.net",
|
||||
"**.amazon.com",
|
||||
"**.amazonaws.com",
|
||||
"*.githubusercontent.com",
|
||||
"*.github.com",
|
||||
"*.github.io",
|
||||
"**.mozilla.com",
|
||||
"**.mozilla.net",
|
||||
"**.akadns.net",
|
||||
]
|
||||
whitelist=[
|
||||
"iamp.*.*.*",
|
||||
"iamp.*.*",
|
||||
"imap-mail.outlook.com",
|
||||
"**.qq.com",
|
||||
"**.baidu.com",
|
||||
]
|
||||
verbose=0
|
||||
|
||||
+70
-34
@@ -3,6 +3,7 @@ package dns
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
@@ -16,9 +17,28 @@ const colon = byte(':')
|
||||
|
||||
func StartDNS(dnsServer, listenIP string) {
|
||||
ip := net.ParseIP(listenIP)
|
||||
suggest := &intelliSuggest{listenIP, 2 * time.Second, []string{"80", "443"}}
|
||||
|
||||
suggest := &intelliSuggest{listenIP, time.Second}
|
||||
mem.DefaultCache = mem.New(time.Hour)
|
||||
dnsServer = net.JoinHostPort(dnsServer, "53")
|
||||
|
||||
dhcpCh := make(chan struct{})
|
||||
if dnsServer != "" {
|
||||
dnsServer = net.JoinHostPort(dnsServer, "53")
|
||||
} else {
|
||||
go func() {
|
||||
for {
|
||||
<-dhcpCh
|
||||
host := util.GetDefaultDNSServer()
|
||||
if host == "" {
|
||||
continue
|
||||
}
|
||||
// atomic action
|
||||
dnsServer = net.JoinHostPort(host, "53")
|
||||
glog.Infoln("set dns server to", host)
|
||||
}
|
||||
}()
|
||||
dhcpCh <- struct{}{}
|
||||
}
|
||||
|
||||
dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
// *Msg r has an TSIG record and it was validated
|
||||
@@ -37,15 +57,15 @@ func StartDNS(dnsServer, listenIP string) {
|
||||
domain = domain[:idx]
|
||||
}
|
||||
|
||||
matchAndServe(w, r, domain, listenIP, dnsServer, ip, suggest)
|
||||
matchAndServe(w, r, domain, listenIP, dnsServer, dhcpCh, ip, suggest)
|
||||
})
|
||||
|
||||
server := &dns.Server{Addr: net.JoinHostPort(listenIP, "53"), Net: "udp"}
|
||||
glog.Fatalln(server.ListenAndServe())
|
||||
}
|
||||
|
||||
func matchAndServe(w dns.ResponseWriter, r *dns.Msg, domain, listenIP,
|
||||
dnsServer string, ipNet net.IP, suggest *intelliSuggest) {
|
||||
func matchAndServe(w dns.ResponseWriter, r *dns.Msg, domain, listenIP, dnsServer string,
|
||||
dhcpCh chan struct{}, ipNet net.IP, suggest *intelliSuggest) {
|
||||
|
||||
inWriteList := whiteList.Match(domain)
|
||||
|
||||
@@ -60,6 +80,12 @@ func matchAndServe(w dns.ResponseWriter, r *dns.Msg, domain, listenIP,
|
||||
}
|
||||
|
||||
msg, err := dns.Exchange(r, dnsServer)
|
||||
if err != nil && dhcpCh != nil {
|
||||
select {
|
||||
case dhcpCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
if msg == nil { // expose any response except nil
|
||||
glog.V(1).Infof("get dns of %s fail: %s", domain, err)
|
||||
return
|
||||
@@ -70,11 +96,10 @@ func matchAndServe(w dns.ResponseWriter, r *dns.Msg, domain, listenIP,
|
||||
type intelliSuggest struct {
|
||||
listenIP string
|
||||
timeout time.Duration
|
||||
ports []string
|
||||
}
|
||||
|
||||
func (i *intelliSuggest) GetOne(domain interface{}) (iface interface{}, e error) {
|
||||
iface = struct{}{}
|
||||
iface, e = struct{}{}, nil
|
||||
|
||||
// kill deadloop, for ugly wildcard setting dns setting
|
||||
addr := strings.TrimSuffix(domain.(string), ".")
|
||||
@@ -82,36 +107,47 @@ func (i *intelliSuggest) GetOne(domain interface{}) (iface interface{}, e error)
|
||||
return
|
||||
}
|
||||
|
||||
for _, port := range i.ports {
|
||||
// give local dial a hand, make it not so easy to be added into suggestions
|
||||
util.HTTPPing(addr+port, addr, i.timeout/5)
|
||||
localCh := util.HTTPPing(addr+port, addr, i.timeout)
|
||||
remoteCh := util.HTTPPing(i.listenIP+port, addr, i.timeout)
|
||||
// give local dial a hand, make it not so easy to be added into suggestions
|
||||
util.HTTPPing(addr, addr, util.Http, i.timeout/50)
|
||||
|
||||
select {
|
||||
case err := <-localCh:
|
||||
if err == nil {
|
||||
glog.V(2).Infoln(addr, "local first, succ")
|
||||
continue
|
||||
}
|
||||
if err = <-remoteCh; err != nil {
|
||||
glog.V(2).Infoln(addr, "local first, all fail")
|
||||
continue
|
||||
}
|
||||
glog.V(2).Infoln(addr, "local first, remote succ")
|
||||
conf.AddSuggest(addr)
|
||||
|
||||
case err := <-remoteCh:
|
||||
if err != nil {
|
||||
glog.V(2).Infoln(addr, "remote first, fail")
|
||||
continue
|
||||
}
|
||||
glog.V(2).Infoln(addr, "remote first, succ")
|
||||
conf.AddSuggest(addr)
|
||||
var (
|
||||
pings = []struct {
|
||||
viaAddr string
|
||||
port util.Port
|
||||
}{
|
||||
{addr, util.Http},
|
||||
{addr, util.Https},
|
||||
{i.listenIP, util.Http},
|
||||
{i.listenIP, util.Https},
|
||||
}
|
||||
half = int32(len(pings) / 2)
|
||||
score = new(int32)
|
||||
)
|
||||
for idx := range pings {
|
||||
go func(idx int) {
|
||||
err := util.HTTPPing(pings[idx].viaAddr, addr, pings[idx].port, i.timeout)
|
||||
if err != nil {
|
||||
// local ping fail
|
||||
if pings[idx].viaAddr == addr {
|
||||
atomic.AddInt32(score, 1)
|
||||
}
|
||||
} else {
|
||||
// remote ping succ
|
||||
if pings[idx].viaAddr == i.listenIP {
|
||||
atomic.AddInt32(score, 1)
|
||||
|
||||
glog.Infof("added suggest domain: %s", addr)
|
||||
return
|
||||
// local ping took longger than remote
|
||||
} else if atomic.LoadInt32(score) >= half {
|
||||
atomic.AddInt32(score, 1)
|
||||
}
|
||||
}
|
||||
|
||||
if atomic.LoadInt32(score) > half {
|
||||
atomic.StoreInt32(score, 0) // avoid redo add action
|
||||
conf.AddSuggest(addr)
|
||||
glog.Infof("added suggest domain: %s", addr)
|
||||
}
|
||||
}(idx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ func init() {
|
||||
|
||||
//first init
|
||||
blockList = loadRules("block", conf.Conf.BlockList)
|
||||
suggestList = loadRules("suggest", conf.Conf.BlockList)
|
||||
suggestList = loadRules("suggest", conf.Conf.Suggestions)
|
||||
whiteList = loadRules("white", conf.Conf.WhiteList)
|
||||
whiteList.Add(host)
|
||||
|
||||
@@ -35,7 +35,7 @@ func init() {
|
||||
|
||||
func loadRules(name string, list []string) *util.Node {
|
||||
rule := util.NewNodeFromRules(".", list...)
|
||||
glog.V(2).Infof("load %s rule:\n%s", name, rule)
|
||||
glog.V(3).Infof("load %s rule:\n%s", name, rule)
|
||||
return rule
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ require (
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
||||
github.com/hashicorp/golang-lru v0.5.0 // indirect
|
||||
github.com/klauspost/cpuid v1.2.0 // indirect
|
||||
github.com/klauspost/reedsolomon v1.8.0 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.0 // indirect
|
||||
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.3
|
||||
github.com/miekg/dns v1.1.4
|
||||
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
|
||||
@@ -19,8 +19,9 @@ require (
|
||||
github.com/ulule/deepcopier v0.0.0-20171107155558-ca99b135e50f // indirect
|
||||
github.com/wweir/fsnotify v1.4.8
|
||||
github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296
|
||||
github.com/wweir/netboot v0.0.2
|
||||
github.com/xtaci/kcp-go v5.0.7+incompatible
|
||||
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect
|
||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e // indirect
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd // indirect
|
||||
golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3 // indirect
|
||||
)
|
||||
|
||||
@@ -8,16 +8,16 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/reedsolomon v1.8.0 h1:lvvOkvk64cE1EGbBIgFk7WSOOsI1GexpuLiT7zjab6g=
|
||||
github.com/klauspost/reedsolomon v1.8.0/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
||||
github.com/klauspost/reedsolomon v1.9.0 h1:usyTY5K7D2B6WOHn2jmpB7ky8Qom96mShZmmq3OW4JU=
|
||||
github.com/klauspost/reedsolomon v1.9.0/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
||||
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f h1:sSeNEkJrs+0F9TUau0CgWTTNEwF23HST3Eq0A+QIx+A=
|
||||
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04=
|
||||
github.com/lucas-clemente/quic-go v0.10.1 h1:ipcMmYP9RT+b1YytOKGUY1qndxPGOczVEQkAVz3CZrs=
|
||||
github.com/lucas-clemente/quic-go v0.10.1/go.mod h1:wuD+2XqEx8G9jtwx5ou2BEYBsE+whgQmlj0Vz/77PrY=
|
||||
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced h1:zqEC1GJZFbGZA0tRyNZqRjep92K5fujFtFsu5ZW7Aug=
|
||||
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58=
|
||||
github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM=
|
||||
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.4 h1:rCMZsU2ScVSYcAsOXgmC6+AKOK+6pmQTOcw03nfwYV0=
|
||||
github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
@@ -34,11 +34,13 @@ github.com/wweir/fsnotify v1.4.8 h1:zxOoqwE5p91F3rmcYLQheoSeTlIVqwi2b5T7UlpElWE=
|
||||
github.com/wweir/fsnotify v1.4.8/go.mod h1:4GuxIc23DXfJI1/LhBXG5eE/wJ4CzL+kk5M7woxVprc=
|
||||
github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296 h1:/HkUfg+ZMx/tNdnyJdVlhyv+xO3A7ZlpfL9nFLWLYcc=
|
||||
github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296/go.mod h1:k7rjBGWoJ+JKwvfe8juAX0zgybjo/Yo3JGkca5f/06s=
|
||||
github.com/wweir/netboot v0.0.2 h1:DFuNd97kxzgblExEKn4Nqf1du0m9qUnaTQ70rFgIjLU=
|
||||
github.com/wweir/netboot v0.0.2/go.mod h1:p/WOnPHdqwgAI6YLEfdmwjwutbc/zDZ7EojAHlXLi4s=
|
||||
github.com/xtaci/kcp-go v5.0.7+incompatible h1:zs9tc8XRID0m+aetu3qPWZFyRt2UIMqbXIBgw+vcnlE=
|
||||
github.com/xtaci/kcp-go v5.0.7+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b h1:Elez2XeF2p9uyVj0yEUDqQ56NFcDtcBNkYP7yv8YbUE=
|
||||
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e h1:3GIlrlVLfkoipSReOMNAgApI0ajnalyLa/EZHHca/XI=
|
||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3 h1:+KlxhGbYkFs8lMfwKn+2ojry1ID5eBSMXprS2u/wqCE=
|
||||
golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
||||
+10
-5
@@ -31,17 +31,22 @@ func NewClient(netType string) Client {
|
||||
func StartClient(netType, server, cipher, password, listenIP string) {
|
||||
connCh := listenLocal(listenIP, []string{"80", "443"})
|
||||
client := NewClient(netType)
|
||||
if addr, err := net.ResolveTCPAddr("tcp", server); err != nil {
|
||||
glog.Fatalln(err)
|
||||
} else {
|
||||
server = addr.String()
|
||||
}
|
||||
resolved := false
|
||||
|
||||
glog.Infoln("Client started.")
|
||||
for {
|
||||
conn := <-connCh
|
||||
glog.V(1).Infof("new conn from (%s) to (%s)", conn.RemoteAddr(), server)
|
||||
|
||||
if !resolved {
|
||||
if addr, err := net.ResolveTCPAddr("tcp", server); err != nil {
|
||||
glog.Fatalln(err)
|
||||
} else {
|
||||
server = addr.String()
|
||||
resolved = true
|
||||
}
|
||||
}
|
||||
|
||||
rc, err := client.Dial(server)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"runtime"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/wweir/netboot/dhcp4"
|
||||
)
|
||||
|
||||
var xid = make([]byte, 4)
|
||||
|
||||
func GetDefaultDNSServer() string {
|
||||
pack := &dhcp4.Packet{
|
||||
Type: dhcp4.MsgDiscover,
|
||||
Broadcast: true,
|
||||
}
|
||||
options := map[dhcp4.Option][]byte{
|
||||
dhcp4.OptRequestedOptions: []byte{byte(dhcp4.OptDNSServers)},
|
||||
}
|
||||
|
||||
NewConn := dhcp4.NewConn
|
||||
if runtime.GOOS == "linux" {
|
||||
NewConn = dhcp4.NewSnooperConn
|
||||
}
|
||||
|
||||
ifaces := mustGetInterfaces()
|
||||
for _, iface := range ifaces {
|
||||
conn, err := NewConn(iface.IP.String() + ":68")
|
||||
if err != nil { // maybe in use
|
||||
glog.V(1).Infoln(err)
|
||||
continue
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
rand.Read(xid)
|
||||
pack.TransactionID = xid
|
||||
pack.HardwareAddr = iface.Interface.HardwareAddr
|
||||
options[dhcp4.OptClientIdentifier] = iface.Interface.HardwareAddr
|
||||
pack.Options = dhcp4.Options(options)
|
||||
|
||||
if err := conn.SendDHCP(pack, iface.Interface); err != nil {
|
||||
glog.Errorln(err)
|
||||
continue
|
||||
}
|
||||
|
||||
pack, _, err = conn.RecvDHCP()
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
continue
|
||||
}
|
||||
|
||||
ips, err := pack.Options.IPs(dhcp4.OptDNSServers)
|
||||
if err != nil {
|
||||
glog.Errorln(err)
|
||||
continue
|
||||
}
|
||||
return ips[0].String() // if len(ips) == 0, err should not be wrong size
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type netIface struct {
|
||||
*net.Interface
|
||||
net.Addr
|
||||
net.IP
|
||||
}
|
||||
|
||||
func mustGetInterfaces() []*netIface {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
glog.Fatalln(err)
|
||||
}
|
||||
|
||||
v4Iface := make([]*netIface, 0, len(ifaces))
|
||||
for i := range ifaces {
|
||||
if len(ifaces[i].HardwareAddr) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
addrs, _ := ifaces[i].Addrs()
|
||||
for _, addr := range addrs {
|
||||
if ip := addr.(*net.IPNet).IP.To4(); ip != nil {
|
||||
v4Iface = append(v4Iface, &netIface{&ifaces[i], addr, ip})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return v4Iface
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package util
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetDefaultDNSServer(t *testing.T) {
|
||||
t.Skip("skip for some enviroment not support dhcp")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
}{{
|
||||
"",
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := GetDefaultDNSServer(); got == "" {
|
||||
t.Errorf("GetDefaultDNSServer() return empty")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+35
-25
@@ -10,36 +10,15 @@ import (
|
||||
)
|
||||
|
||||
// HTTPPing try connect to a http(s) server with domain though the http addr
|
||||
func HTTPPing(tcpAddr, domain string, timeout time.Duration) <-chan error {
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
errCh <- httpPing(tcpAddr, domain, timeout)
|
||||
}()
|
||||
return errCh
|
||||
}
|
||||
|
||||
func httpPing(tcpAddr, domain string, timeout time.Duration) error {
|
||||
conn, err := net.DialTimeout("tcp", tcpAddr, timeout)
|
||||
func HTTPPing(viaHost, domain string, port Port, timeout time.Duration) (err error) {
|
||||
conn, err := net.DialTimeout("tcp", port.JoinAddr(viaHost), timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
host, port, err := net.SplitHostPort(tcpAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var msg []byte
|
||||
switch port {
|
||||
case ":80":
|
||||
msg = []byte("TRACE / HTTP/1.1\r\nHost: " + domain + "\r\n\r\n")
|
||||
case ":443":
|
||||
msg = NewClientHelloSNIMsg(domain)
|
||||
}
|
||||
|
||||
conn.SetDeadline(time.Now().Add(timeout))
|
||||
if _, err = conn.Write(msg); err != nil {
|
||||
if _, err = conn.Write(port.pingMsg(domain)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -47,12 +26,43 @@ func httpPing(tcpAddr, domain string, timeout time.Duration) error {
|
||||
// err -> io.EOF: no such domain or connection refused
|
||||
// err -> timeout: tcp package has been dropped
|
||||
_, err = conn.Read(make([]byte, 1))
|
||||
if err == io.EOF && host == domain {
|
||||
if err == io.EOF && viaHost == domain {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Port ==========================
|
||||
type Port uint16
|
||||
|
||||
const (
|
||||
Http Port = iota
|
||||
Https
|
||||
)
|
||||
|
||||
func (p Port) JoinAddr(addr string) string {
|
||||
switch p {
|
||||
case Http:
|
||||
return addr + ":80"
|
||||
case Https:
|
||||
return addr + ":443"
|
||||
default:
|
||||
panic("invalid port")
|
||||
}
|
||||
}
|
||||
|
||||
func (p Port) pingMsg(domain string) []byte {
|
||||
switch p {
|
||||
case Http:
|
||||
return []byte("TRACE / HTTP/1.1\r\nHost: " + domain + "\r\n\r\n")
|
||||
case Https:
|
||||
return NewClientHelloSNIMsg(domain)
|
||||
default:
|
||||
panic("invalid port")
|
||||
}
|
||||
}
|
||||
|
||||
// SNI ==========================
|
||||
type clientHelloSNI struct {
|
||||
ContentType uint8
|
||||
Version uint16
|
||||
|
||||
+24
-9
@@ -54,33 +54,48 @@ func (n *node) add(secs []string) {
|
||||
case 1:
|
||||
n.node[secs[length-1]] = &node{node: map[string]*node{"": &node{}}}
|
||||
default:
|
||||
subNode, ok := n.node[secs[length-1]]
|
||||
sec := secs[length-1]
|
||||
if sec == "**" {
|
||||
sec = "*"
|
||||
}
|
||||
|
||||
subNode, ok := n.node[sec]
|
||||
if !ok {
|
||||
subNode = &node{node: map[string]*node{}}
|
||||
n.node[secs[length-1]] = subNode
|
||||
n.node[sec] = subNode
|
||||
}
|
||||
subNode.add(secs[:length-1])
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) Match(item string) bool {
|
||||
return n.matchSecs(strings.Split(n.trim(item), n.sep))
|
||||
return n.matchSecs(strings.Split(n.trim(item), n.sep), false)
|
||||
}
|
||||
|
||||
func (n *node) matchSecs(secs []string) bool {
|
||||
func (n *node) matchSecs(secs []string, fuzzNode bool) bool {
|
||||
length := len(secs)
|
||||
if length == 0 {
|
||||
if _, ok := n.node[""]; ok {
|
||||
return true
|
||||
}
|
||||
_, ok := n.node["*"]
|
||||
return ok
|
||||
if _, ok := n.node["**"]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := n.node["*"]; ok {
|
||||
return !fuzzNode
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if n, ok := n.node[secs[length-1]]; ok {
|
||||
return n.matchSecs(secs[:length-1])
|
||||
return n.matchSecs(secs[:length-1], false)
|
||||
}
|
||||
if n, ok := n.node["*"]; ok {
|
||||
return n.matchSecs(secs[:length-1], true)
|
||||
}
|
||||
if _, ok := n.node["**"]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
_, ok := n.node["*"]
|
||||
return ok
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -30,18 +30,39 @@ func TestNode_Match(t *testing.T) {
|
||||
},
|
||||
}, {
|
||||
"fuzz1",
|
||||
NewNodeFromRules(".", "wweir.cc", "*.wweir.cc"),
|
||||
[]test{
|
||||
{"wweir.cc", true},
|
||||
{"a.wweir.cc", true},
|
||||
},
|
||||
}, {
|
||||
"fuzz2",
|
||||
NewNodeFromRules(".", "a.wweir.cc", "*.wweir.cc"),
|
||||
NewNodeFromRules(".", "wweir.cc", "a.wweir.cc", "*.wweir.cc"),
|
||||
[]test{
|
||||
{"wweir.cc", true},
|
||||
{"a.wweir.cc", true},
|
||||
{"b.wweir.cc", true},
|
||||
{"a.b.wweir.cc", false},
|
||||
},
|
||||
}, {
|
||||
"fuzz2",
|
||||
NewNodeFromRules(".", "a.*.cc"),
|
||||
[]test{
|
||||
{"wweir.cc", false},
|
||||
{"a.wweir.cc", true},
|
||||
{"b.wweir.cc", false},
|
||||
},
|
||||
}, {
|
||||
"fuzz3",
|
||||
NewNodeFromRules(".", "*.*.cc", "iamp.*.*"),
|
||||
[]test{
|
||||
{"wweir.cc", false},
|
||||
{"a.wweir.cc", true},
|
||||
{"b.wweir.cc", true},
|
||||
{"iamp.wweir.cc", true},
|
||||
},
|
||||
}, {
|
||||
"fuzz4",
|
||||
NewNodeFromRules(".", "**.cc", "a.**.com"),
|
||||
[]test{
|
||||
{"wweir.cc", true},
|
||||
{"a.wweir.cc", true},
|
||||
{"a.b.wweir.cc", true},
|
||||
{"a.wweir.com", true},
|
||||
{"b.wweir.com", false},
|
||||
},
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
|
||||
Reference in New Issue
Block a user