diff --git a/README.md b/README.md index c1f361c25dc9938b287826b2e1224e85a078f3f0..ab2535e23693a10d670ea78e47cc7910f680dce6 100644 --- a/README.md +++ b/README.md @@ -69,13 +69,18 @@ go mod tidy If go.mod and go.sum are missing from the cloned repository, then: ``` # go mod init [module-path] -go mod init main +go mod init 0xacab.org/leap/vpn-hole/vpnhole ``` ## Build & Run <a name="Build & Run"></a> - +To build the library and use it as a standalone: ``` +# navigate to app/vpnhole + +cd app/vpnhole + # build the library + go build ``` An executable binary file would be generated for the library. @@ -107,10 +112,10 @@ sudo systemctl disable systemd-resolved To disable it permanently, uncomment and change `DNSStubListener` to *no* in `/etc/systemd/resolved.conf`; you can revert back everything to default if you stop using the vpn-hole library as a standalone. Also stop any other service which prevents from listening on port 53. Then, you can simply execute the binary file. -In **Windows**, an executable named- main.exe would be created. +In **Windows**, an executable named- vpnhole.exe would be created. ``` # executing the binary -main.exe +vpnhole.exe ``` This would start the network wide DNS-level blocker on port 53. @@ -134,12 +139,30 @@ Addresses: :: ## Integration <a name="Integration"></a> To integrate the VPN-Hole Library with existing cross-platform applications, we will be using gomobile to compile and bind it. ``` +# For binding with Android Platforms: + +cd vpn-hole/vpnhole + +# use gomobile to generate .aar: + +gomobile bind -target='android' -o path/for/the/output/filename.aar -v path/for/vpnhole.go + +For example: +# Here pwd is path/vpn-hole/vpnhole + +gomobile bind -target='android' -o vpn-hole.aar -v + +# Similarly, GoMobile can be used to compile for other platforms like iOS (XCode is required): + +gomobile bind -target='ios' -o path/for/the/output/filename.framework -v path/for/vpnhole.go ``` ### Adapting to Bitmask_core <a name="Adapting to Bitmask_core"></a> ``` + + ``` ## License <a name="License"></a> diff --git a/blacklist.go b/app/vpnhole/blocklist.go similarity index 50% rename from blacklist.go rename to app/vpnhole/blocklist.go index 1c2ae6fc0a9337486cabfe7aadbb4f63fc5bc6af..5da5f8d914c3f024d6d0feb8a6c032337f6b8845 100644 --- a/blacklist.go +++ b/app/vpnhole/blocklist.go @@ -1,14 +1,18 @@ package main import ( + "net/http" "strings" - "main/blacklist" + "time" + + "0xacab.org/leap/vpn-hole/blacklist" "github.com/miekg/dns" ) -var privBlacklist = blacklist.New(httpClient) +var httpClient = &http.Client{Timeout: 30 * time.Second} +var PrivBlacklist = blacklist.New(httpClient) -func isBlacklisted(req *dns.Msg) bool { +func IsBlacklisted(req *dns.Msg) bool { if req.Opcode != dns.OpcodeQuery { return false } @@ -26,5 +30,5 @@ func isBlacklisted(req *dns.Msg) bool { return false } - return privBlacklist.Contains(strings.TrimRight(q.Name, ".")) + return PrivBlacklist.Contains(strings.TrimRight(q.Name, ".")) } diff --git a/handler.go b/app/vpnhole/handler.go similarity index 60% rename from handler.go rename to app/vpnhole/handler.go index 1d214ee5e3ddd95b1d4282faecb771a9fb17b7f7..20c1d8683aca38e8b7745126ba0e44ff78f96a2a 100644 --- a/handler.go +++ b/app/vpnhole/handler.go @@ -5,41 +5,41 @@ import ( "log" "net" + "0xacab.org/leap/vpn-hole/vpnhole/vpnhole" "github.com/miekg/dns" ) var ( - client dns.Client - upstream string + client dns.Client blockIPv4 = net.ParseIP("0.0.0.0") blockIPv6 = net.ParseIP("0:0:0:0:0:0:0:0") blockTTL = uint32(60) ) -func handler(rw dns.ResponseWriter, req *dns.Msg) { +func Handler(rw dns.ResponseWriter, req *dns.Msg) { defer rw.Close() - if isBlacklisted(req) { - if err := block(rw, req); err != nil { - log.Println(fmt.Errorf("Failed to block request: %w", err)) + if IsBlacklisted(req) { + if err := Block(rw, req); err != nil { + log.Println(fmt.Errorf("failed to block request: %w", err)) } return } - - resp, _, err := client.Exchange(req, upstream) + c := alter.ParseFlags() + resp, _, err := client.Exchange(req, c.Upstream) if err != nil { - log.Fatalln(fmt.Errorf("Failed to exchange: %w", err)) + log.Fatalln(fmt.Errorf("failed to exchange: %w", err)) return } if err = rw.WriteMsg(resp); err != nil { - log.Println(fmt.Errorf("Failed to reply: %w", err)) + log.Println(fmt.Errorf("failed to reply: %w", err)) } } -func block(rw dns.ResponseWriter, req *dns.Msg) error { +func Block(rw dns.ResponseWriter, req *dns.Msg) error { resp := &dns.Msg{} resp.SetReply(req) diff --git a/app/vpnhole/main.go b/app/vpnhole/main.go new file mode 100644 index 0000000000000000000000000000000000000000..8af4c67c70d6e3ed62ab923d2bd382b3bcbdfc50 --- /dev/null +++ b/app/vpnhole/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "context" + "fmt" + "log" + + "0xacab.org/leap/vpn-hole/vpnhole/vpnhole" + "github.com/miekg/dns" + + "time" +) + +func init() { + // start the cmdline parser + alter.ParseFlags() + +} + +func main() { + + // call ParseFlags() to get the config struct with the values + c := alter.ParseFlags() + fmt.Println(c) + + // start the vpnhole + if err := c.Start(); err != nil { + log.Fatalln(fmt.Errorf("failed to start vpnhole: %w", err)) + } + defer func() { + if err := c.Stop(); err != nil { + log.Println(fmt.Errorf("failed to stop vpnhole: %w", err)) + } + }() + log.Printf("vpnhole started: %s", c) + + subscriptions, err := ReadSubscriptions(c.SubscriptionsFilename) + if err != nil { + log.Fatalln(fmt.Errorf("failed to read subscriptions list: %w", err)) + } + + for _, blacklistURL := range subscriptions { + PrivBlacklist.Subscribe(blacklistURL) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go PrivBlacklist.Watch(ctx, time.Minute*10) + + dns.HandleFunc(".", Handler) + + if err = dns.ListenAndServe(c.Addr, "udp", nil); err != nil { + log.Println(fmt.Errorf("failed to serve DNS server: %w", err)) + } + +} diff --git a/subscription.go b/app/vpnhole/sublist.go similarity index 88% rename from subscription.go rename to app/vpnhole/sublist.go index 4e29e724e711f7e21cacfea6f2bce6e572ec7c05..e4315cdee5511c2adad45a661ca2f16b2e425d8b 100644 --- a/subscription.go +++ b/app/vpnhole/sublist.go @@ -6,7 +6,7 @@ import ( "os" ) -func readSubscriptions(filename string) ([]string, error) { +func ReadSubscriptions(filename string) ([]string, error) { f, err := os.Open(filename) if err != nil { return nil, fmt.Errorf("failed to open: %w", err) diff --git a/subs.list b/app/vpnhole/subs.list similarity index 100% rename from subs.list rename to app/vpnhole/subs.list diff --git a/blacklist/blacklist.go b/blacklist/blacklist.go index dcc88053dac3f546da3854677dc369d8c0b5d760..6a43279d9588a9f3f0c0687107b73a684bc17b3b 100644 --- a/blacklist/blacklist.go +++ b/blacklist/blacklist.go @@ -56,7 +56,7 @@ func (b *Blacklist) Watch(ctx context.Context, interval time.Duration) { hosts, err := b.updateList(ctx, blacklistURL, sum) if err != nil { - log.Println(fmt.Errorf("Failed to update subscription (%s): %w", blacklistURL, err)) + log.Println(fmt.Errorf("failed to update subscription (%s): %w", blacklistURL, err)) return } @@ -98,7 +98,7 @@ func (b *Blacklist) fetch(ctx context.Context, blacklistURL string) ([]string, s if resp.StatusCode != http.StatusOK { io.Copy(ioutil.Discard, resp.Body) - return nil, "", fmt.Errorf("Unexpected response status code: %d", resp.StatusCode) + return nil, "", fmt.Errorf("unexpected response status code: %d", resp.StatusCode) } var ( @@ -125,7 +125,7 @@ func (b *Blacklist) fetch(ctx context.Context, blacklistURL string) ([]string, s if err = scanner.Err(); err != nil { io.Copy(ioutil.Discard, resp.Body) - return nil, "", fmt.Errorf("Failed to scan response body: %w", err) + return nil, "", fmt.Errorf("failed to scan response body: %w", err) } return hosts, hex.EncodeToString(hasher.Sum(nil)), nil diff --git a/go.mod b/go.mod deleted file mode 100644 index a7b3abc06608d53d0767ad5ccfc9641916b4f627..0000000000000000000000000000000000000000 --- a/go.mod +++ /dev/null @@ -1,13 +0,0 @@ -module main - -go 1.18 - -require github.com/miekg/dns v1.1.50 - -require ( - golang.org/x/mod v0.4.2 // indirect - golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 // indirect - golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect - golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index b7751ee9b68f84e081057669d416501d0cc373cc..0000000000000000000000000000000000000000 --- a/go.sum +++ /dev/null @@ -1,35 +0,0 @@ -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go deleted file mode 100644 index c43a73a6699cace4d868d3eac09204905f5ba264..0000000000000000000000000000000000000000 --- a/main.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "log" - "net/http" - "time" - - "github.com/miekg/dns" -) - -var httpClient = &http.Client{Timeout: 30 * time.Second} - -func main() { - var ( - addr string - subscriptionsFilename string - ) - - flag.StringVar(&addr, "addr", ":53", "addr") - flag.StringVar(&upstream, "upstream", "1.1.1.1:53", "upstream") - flag.StringVar(&subscriptionsFilename, "subs", "subs.list", "subscriptions filename") - flag.Parse() - - subscriptions, err := readSubscriptions(subscriptionsFilename) - if err != nil { - log.Fatalln(fmt.Errorf("Failed to read subscriptions list: %w", err)) - } - - for _, blacklistURL := range subscriptions { - privBlacklist.Subscribe(blacklistURL) - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - go privBlacklist.Watch(ctx, time.Minute*10) - - dns.HandleFunc(".", handler) - - if err = dns.ListenAndServe(addr, "udp", nil); err != nil { - log.Println(fmt.Errorf("Failed to serve DNS server: %w", err)) - } -} diff --git a/vpnhole/vpnhole.go b/vpnhole/vpnhole.go new file mode 100644 index 0000000000000000000000000000000000000000..3acba5f5b333eebcbddb4199dc6f627490b34e54 --- /dev/null +++ b/vpnhole/vpnhole.go @@ -0,0 +1,32 @@ +package vpnhole + +import ( + "fmt" +) + +var ErrShutdown = fmt.Errorf("vpnhole was shutdown gracefully") + +type vpnholeclient struct { + Addr string + SubscriptionsFilename string + Upstream string +} + +// parse the flags and return the config struct with the values +func ParseFlags() vpnholeclient { + return vpnholeclient{ + Addr: ":53", + SubscriptionsFilename: "subs.list", + Upstream: "1.1.1.1:53", + } +} + +// start the vpnhole +func (c *vpnholeclient) Start() error { + return nil +} + +// stop the vpnhole +func (c *vpnholeclient) Stop() error { + return nil +}