mirror of
https://github.com/Luzifer/dns_check.git
synced 2024-12-22 10:51:18 +00:00
Initital version
This commit is contained in:
commit
bb35ac980a
8 changed files with 629 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
dns_check
|
4
.gobuilder.yml
Normal file
4
.gobuilder.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
build_matrix:
|
||||
general:
|
||||
ldflags:
|
||||
- "-X main.version $(git describe --tags)"
|
8
Makefile
Normal file
8
Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
default:
|
||||
|
||||
compile:
|
||||
go generate
|
||||
go build -ldflags "-X main.version=$(shell git describe --tags || git rev-parse --short HEAD)" .
|
||||
|
||||
bindata:
|
||||
go-bindata nameservers.yaml
|
60
README.md
Normal file
60
README.md
Normal file
|
@ -0,0 +1,60 @@
|
|||
# Luzifer / dns\_check
|
||||
|
||||
dns\_check is a small utility to check major DNS services for records of a FQDN without having to query them one-by-one.
|
||||
|
||||
Use cases:
|
||||
|
||||
- Check whether the IP of your domain is consistent on all services
|
||||
- Check whether your DNS change is already live for users of those services
|
||||
- Have an automated check which tells you when something is not right
|
||||
|
||||
You can see the current table of nameservers oncluded on the build by browsing the [nameservers.yaml](nameservers.yaml) file.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# ./dns_check --help
|
||||
Usage of ./dns_check:
|
||||
-a, --assert=[]: Exit with exit code 2 when these DNS entries were not found
|
||||
--assert-threshold=100: If used with -a fail when not at least N percent of the nameservers had the expected result
|
||||
-f, --full-scan[=false]: Scan all nameservers included in this build
|
||||
-q, --quiet[=false]: Do not communicate by text, use only exit codes
|
||||
-s, --short[=true]: Use short notation (only when using assertion)
|
||||
--version[=false]: Print version and exit
|
||||
```
|
||||
|
||||
Use case: I know the IP of my domain and want to check whether all services report that IP
|
||||
|
||||
```bash
|
||||
# ./dns_check -a "188.40.126.69" A luzifer.io
|
||||
[Level3] (209.244.0.3:53) ✓
|
||||
[Level3] (209.244.0.4:53) ✓
|
||||
[Verisign] (64.6.64.6:53) ✓
|
||||
[Verisign] (64.6.65.6:53) ✓
|
||||
[Google] (8.8.8.8:53) ✓
|
||||
[Google] (8.8.4.4:53) ✓
|
||||
[OpenDNS Home] (208.67.222.222:53) ✓
|
||||
[OpenDNS Home] (208.67.220.220:53) ✓
|
||||
```
|
||||
|
||||
Use case: Just tell me the IP of any domain
|
||||
|
||||
```bash
|
||||
# ./dns_check A luzifer.io
|
||||
[Google] (8.8.8.8:53)
|
||||
- 188.40.126.69
|
||||
[Google] (8.8.4.4:53)
|
||||
- 188.40.126.69
|
||||
[Level3] (209.244.0.3:53)
|
||||
- 188.40.126.69
|
||||
[Level3] (209.244.0.4:53)
|
||||
- 188.40.126.69
|
||||
[Verisign] (64.6.64.6:53)
|
||||
- 188.40.126.69
|
||||
[Verisign] (64.6.65.6:53)
|
||||
- 188.40.126.69
|
||||
[OpenDNS Home] (208.67.222.222:53)
|
||||
- 188.40.126.69
|
||||
[OpenDNS Home] (208.67.220.220:53)
|
||||
- 188.40.126.69
|
||||
```
|
235
bindata.go
Normal file
235
bindata.go
Normal file
|
@ -0,0 +1,235 @@
|
|||
// Code generated by go-bindata.
|
||||
// sources:
|
||||
// nameservers.yaml
|
||||
// DO NOT EDIT!
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, gz)
|
||||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info os.FileInfo
|
||||
}
|
||||
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _nameserversYaml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x64\x53\x4b\x6f\x9b\x40\x10\xbe\xfb\x57\x20\xf5\xcc\xc0\x3e\x78\xe5\x66\xb9\x6d\x52\x29\x4a\x0f\xb6\x52\xf5\x14\x11\x18\x3b\x28\xb0\x8b\x96\xc5\x6a\xff\x7d\x67\xcd\x6b\xad\x2a\xb3\x04\x3e\xcf\xeb\xfb\x66\x36\x0c\xc3\x5d\xa5\x0d\xbe\xf5\x46\x5f\x9b\x1a\xcd\xf0\xb0\x0b\x82\x30\x78\xc6\x2b\xb6\xe2\xf6\xfa\x8a\xa6\x19\x9a\x8b\xba\x7d\x3c\x6a\x7d\x69\xf1\xf6\xfa\xb3\x47\xf5\xf5\xe5\x18\x3c\xe9\x0e\x77\xfd\xf8\xde\x36\xd5\x9b\x2a\x3b\x1c\xd0\x5c\xe7\x3c\x5f\x82\xe7\x66\xb0\x41\xa5\xbb\xbe\x69\xb1\x0e\xce\x46\x77\xc1\x87\xb5\xfd\x43\x14\xf5\xd5\x30\xf6\xbd\x36\x16\xca\x77\x3d\x5a\x20\xa7\x48\xd7\x91\x6d\xfa\xc1\x9a\xa6\xfa\x1c\xa2\x32\x3a\x1b\xc4\x70\x4a\x1d\xd6\x6a\x08\xe7\xd4\xf0\x61\x3b\xca\x3e\xf5\xe8\xea\xb8\x76\x78\x5c\x00\x97\x12\x62\x10\x0f\x89\xf8\x0f\x94\x13\xb8\x90\x59\xa2\x52\x09\x29\xb8\xc7\x16\x33\x41\xc9\x02\x4d\x8c\x17\xff\x1c\x6e\x7f\x9b\xb7\xfb\x94\x4b\x76\x92\x03\x7e\xed\x4f\x87\xa7\xd5\x5d\x02\x8f\x63\x48\x0b\xc8\x63\x2f\x66\x42\x33\xea\x6a\x46\x0f\xba\xd3\xb5\x0e\x8e\x58\x8d\x06\x5d\x9a\xad\x1e\x4f\x21\x49\xe9\xe9\x97\xe4\x31\xb1\xca\xe8\xdf\x04\xfa\x93\xd8\xe4\xc8\x21\x25\x17\xce\xdd\xf1\x15\x99\xf1\xd8\x9d\xb5\xed\x60\x5f\x5f\x4b\x65\xcb\xcb\x9a\x80\x51\x55\x96\x48\xd7\x25\xdb\xc2\x57\x94\x2d\xe8\x0b\x8d\x50\x2b\x62\xa0\x14\x56\xf6\x58\x9e\xb7\x0c\x05\xd1\x4e\x80\x11\x03\xe6\xb1\x5f\xe1\x6c\x85\x1f\x69\xce\xea\x84\x65\xe7\x33\x67\xc0\x59\x0e\x8c\x15\x74\xee\x27\x9a\x13\x5c\xd0\x11\xf3\xa4\x5d\x51\x2f\x92\x15\x09\xc8\x14\x44\x41\xe6\x97\x5d\x50\xe9\xc9\xf6\xf2\xe3\xb0\x84\x25\x44\x94\xa5\xf4\x2b\x09\xe3\xa9\x3d\xc3\x5c\x50\x3b\x73\x1f\xc7\xae\x34\xf6\xb5\xe9\xd1\xf8\x6a\x67\x34\xa8\x98\xec\x5e\x6b\x87\x32\xb2\x59\xe9\xbf\xeb\xe6\x71\x4a\xca\x5c\x43\x09\x99\x17\xb3\xc0\xce\x26\xf8\x3b\xa9\xe3\xd1\x13\x34\x3d\x0a\xa2\x01\x64\x72\x8b\xf3\xd0\x6c\x42\xf7\xad\x45\xa3\x4a\x7b\xb7\x4f\x37\xdd\x62\x92\x56\x72\xc8\xfc\xb5\x20\x7a\x89\x00\x96\x0a\x48\x66\x55\x7f\x97\xaa\xc6\x3f\xe0\x05\x67\x99\x93\xfe\x6e\xfd\x17\x68\xe6\x57\xa1\x1a\x46\x73\x36\x0d\xdd\x56\xa8\x3f\xd7\x59\xd2\x2d\x14\x02\xa4\xa0\xbd\xd9\x62\x0b\x6a\x83\xc6\xc1\xe8\x2a\xd0\x99\xf0\xa7\xd1\xd0\xe5\x2f\x15\x06\xdf\x5a\x5a\x27\x7a\x5f\x8b\x4b\xc8\x39\x48\x67\x93\x6b\x3f\x2a\x7b\xd8\x9f\x56\x66\xb4\x18\xee\x9e\xcd\x5a\xef\xfe\x05\x00\x00\xff\xff\xa7\x60\x1b\x39\xda\x04\x00\x00")
|
||||
|
||||
func nameserversYamlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_nameserversYaml,
|
||||
"nameservers.yaml",
|
||||
)
|
||||
}
|
||||
|
||||
func nameserversYaml() (*asset, error) {
|
||||
bytes, err := nameserversYamlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "nameservers.yaml", size: 1242, mode: os.FileMode(420), modTime: time.Unix(1450103270, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func Asset(name string) ([]byte, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||
}
|
||||
|
||||
// AssetNames returns the names of the assets.
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
for name := range _bindata {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"nameservers.yaml": nameserversYaml,
|
||||
}
|
||||
|
||||
// AssetDir returns the file names below a certain
|
||||
// directory embedded in the file by go-bindata.
|
||||
// For example if you run go-bindata on data/... and data contains the
|
||||
// following hierarchy:
|
||||
// data/
|
||||
// foo.txt
|
||||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(cannonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.Func != nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"nameservers.yaml": &bintree{nameserversYaml, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||
}
|
||||
|
75
dns.go
Normal file
75
dns.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func getDNSQueryResponse(queryType, fqdn, dnsServer string) ([]string, error) {
|
||||
qt, ok := dns.StringToType[queryType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Query type '%s' is an unknown type.", queryType)
|
||||
}
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(dns.Fqdn(fqdn), qt)
|
||||
|
||||
in, err := dns.Exchange(m, dnsServer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responses := []string{}
|
||||
|
||||
switch dns.RcodeToString[in.Rcode] {
|
||||
// TODO: Catch more error codes (https://github.com/miekg/dns/blob/master/msg.go#L127)
|
||||
case "NXDOMAIN":
|
||||
return nil, fmt.Errorf("Domain was not found. (NXDOMAIN)")
|
||||
}
|
||||
|
||||
for _, a := range in.Answer {
|
||||
r, err := formatDNSAnswer(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rp := range r {
|
||||
responses = append(responses, rp)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(responses)
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func formatDNSAnswer(a interface{}) (r []string, err error) {
|
||||
switch a.(type) {
|
||||
case *dns.A:
|
||||
r = []string{a.(*dns.A).A.String()}
|
||||
case *dns.AAAA:
|
||||
r = []string{a.(*dns.AAAA).AAAA.String()}
|
||||
case *dns.CNAME:
|
||||
r = []string{a.(*dns.CNAME).Target}
|
||||
case *dns.MX:
|
||||
r = []string{fmt.Sprintf("%d %s", a.(*dns.MX).Preference, a.(*dns.MX).Mx)}
|
||||
case *dns.NS:
|
||||
r = []string{a.(*dns.NS).Ns}
|
||||
case *dns.PTR:
|
||||
r = []string{a.(*dns.PTR).Ptr}
|
||||
case *dns.TXT:
|
||||
r = a.(*dns.TXT).Txt
|
||||
case *dns.SRV:
|
||||
r = []string{fmt.Sprintf("%d %d %d %s",
|
||||
a.(*dns.SRV).Priority,
|
||||
a.(*dns.SRV).Weight,
|
||||
a.(*dns.SRV).Port,
|
||||
a.(*dns.SRV).Target,
|
||||
)}
|
||||
default:
|
||||
err = fmt.Errorf("Got an unexpected answer type: %s", a.(dns.RR).String())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
182
main.go
Normal file
182
main.go
Normal file
|
@ -0,0 +1,182 @@
|
|||
package main
|
||||
|
||||
//go:generate make bindata
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/Luzifer/rconfig"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
var (
|
||||
cfg = struct {
|
||||
FullScan bool `flag:"full-scan,f" default:"false" description:"Scan all nameservers included in this build"`
|
||||
Assert []string `flag:"assert,a" default:"" description:"Exit with exit code 2 when these DNS entries were not found"`
|
||||
AssertPercentage float64 `flag:"assert-threshold" default:"100.0" description:"If used with -a fail when not at least N percent of the nameservers had the expected result"`
|
||||
Quiet bool `flag:"quiet,q" default:"false" description:"Do not communicate by text, use only exit codes"`
|
||||
Short bool `flag:"short,s" default:"true" description:"Use short notation (only when using assertion)"`
|
||||
Version bool `flag:"version" default:"false" description:"Print version and exit"`
|
||||
}{}
|
||||
nameserverDirectory = struct {
|
||||
CoreProviders []string `yaml:"core_providers"`
|
||||
PublicNameservers map[string][]string `yaml:"public_nameservers"`
|
||||
}{}
|
||||
version = "dev"
|
||||
|
||||
// Color output helpers
|
||||
red = color.New(color.FgRed).SprintfFunc()
|
||||
green = color.New(color.FgGreen).SprintFunc()
|
||||
yellow = color.New(color.FgYellow).SprintFunc()
|
||||
providerOut = color.New(color.FgWhite).Add(color.BgBlue).SprintfFunc()
|
||||
serverOut = color.New(color.FgWhite).SprintfFunc()
|
||||
)
|
||||
|
||||
type checkResult struct {
|
||||
Provider string
|
||||
Server string
|
||||
Results []string
|
||||
QueryError error
|
||||
AssertSucceeded bool
|
||||
}
|
||||
|
||||
func (c checkResult) Print() {
|
||||
if c.QueryError != nil {
|
||||
fmt.Printf("%s %s %s\n",
|
||||
providerOut("[%s]", c.Provider),
|
||||
serverOut("(%s)", c.Server),
|
||||
red("Error: %s", c.QueryError),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var result string
|
||||
if len(cfg.Assert) > 0 {
|
||||
if c.AssertSucceeded {
|
||||
result = green("\u2713")
|
||||
} else {
|
||||
result = red("\u2717")
|
||||
}
|
||||
} else {
|
||||
result = ""
|
||||
}
|
||||
|
||||
srvBuf := bytes.NewBuffer([]byte{})
|
||||
fmt.Fprintf(srvBuf, "%s %s %s\n",
|
||||
providerOut("[%s]", c.Provider),
|
||||
serverOut("(%s)", c.Server),
|
||||
result,
|
||||
)
|
||||
if !cfg.Short {
|
||||
for _, r := range c.Results {
|
||||
fmt.Fprintf(srvBuf, " %s %s\n", yellow("-"), r)
|
||||
}
|
||||
}
|
||||
fmt.Print(srvBuf.String())
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := rconfig.Parse(&cfg); err != nil {
|
||||
log.Fatalf("Unable to parse arguments: %s", err)
|
||||
}
|
||||
|
||||
if cfg.Version {
|
||||
fmt.Printf("dns_check version %s\n", version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(cfg.Assert, []string{""}) {
|
||||
cfg.Short = false
|
||||
cfg.Assert = []string{}
|
||||
}
|
||||
|
||||
if err := loadNameservers(); err != nil {
|
||||
log.Fatalf("Unable to load nameserver list, probably your build is defect: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
args := rconfig.Args()
|
||||
if len(args) != 3 {
|
||||
fmt.Println("Usage: dns_check <type> <query>")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
queryType := args[1]
|
||||
queryFQDN := args[2]
|
||||
|
||||
// Correct ordering is required for DeepEqual
|
||||
sort.Strings(cfg.Assert)
|
||||
|
||||
wg := make(chan bool, 10)
|
||||
results := []*checkResult{}
|
||||
for provider, servers := range nameserverDirectory.PublicNameservers {
|
||||
if !cfg.FullScan && !isCoreProvider(provider) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, server := range servers {
|
||||
wg <- true
|
||||
r := &checkResult{
|
||||
Provider: provider,
|
||||
Server: server,
|
||||
}
|
||||
results = append(results, r)
|
||||
go checkProviderServer(wg, queryType, queryFQDN, provider, server, r)
|
||||
}
|
||||
}
|
||||
for len(wg) > 0 {
|
||||
time.Sleep(1)
|
||||
}
|
||||
|
||||
var failCount int
|
||||
for _, r := range results {
|
||||
if !r.AssertSucceeded {
|
||||
failCount++
|
||||
}
|
||||
if !cfg.Quiet {
|
||||
r.Print()
|
||||
}
|
||||
}
|
||||
|
||||
if (1.0-float64(failCount)/float64(len(results)))*100 < cfg.AssertPercentage {
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func checkProviderServer(wg chan bool, queryType, queryFQDN, provider, server string, r *checkResult) {
|
||||
r.Results, r.QueryError = getDNSQueryResponse(queryType, queryFQDN, server)
|
||||
|
||||
if len(cfg.Assert) > 0 {
|
||||
r.AssertSucceeded = reflect.DeepEqual(r.Results, cfg.Assert)
|
||||
} else {
|
||||
r.AssertSucceeded = true
|
||||
}
|
||||
|
||||
<-wg
|
||||
}
|
||||
|
||||
func isCoreProvider(s string) bool {
|
||||
for _, v := range nameserverDirectory.CoreProviders {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func loadNameservers() error {
|
||||
data, err := Asset("nameservers.yaml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return yaml.Unmarshal(data, &nameserverDirectory)
|
||||
}
|
64
nameservers.yaml
Normal file
64
nameservers.yaml
Normal file
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
core_providers:
|
||||
- Level3
|
||||
- Verisign
|
||||
- Google
|
||||
- OpenDNS Home
|
||||
public_nameservers:
|
||||
# List compiled from http://pcsupport.about.com/od/tipstricks/a/free-public-dns-servers.htm
|
||||
Level3:
|
||||
- 209.244.0.3:53
|
||||
- 209.244.0.4:53
|
||||
Verisign:
|
||||
- 64.6.64.6:53
|
||||
- 64.6.65.6:53
|
||||
Google:
|
||||
- 8.8.8.8:53
|
||||
- 8.8.4.4:53
|
||||
DNS.WATCH:
|
||||
- 84.200.69.80:53
|
||||
- 84.200.70.40:53
|
||||
Comodo Secure DNS:
|
||||
- 8.26.56.26:53
|
||||
- 8.20.247.20:53
|
||||
OpenDNS Home:
|
||||
- 208.67.222.222:53
|
||||
- 208.67.220.220:53
|
||||
DNS Advantage:
|
||||
- 156.154.70.1:53
|
||||
- 156.154.71.1:53
|
||||
Norton ConnectSafe:
|
||||
- 199.85.126.10:53
|
||||
- 199.85.127.10:53
|
||||
GreenTeamDNS:
|
||||
- 81.218.119.11:53
|
||||
- 209.88.198.133:53
|
||||
SafeDNS:
|
||||
- 195.46.39.39:53
|
||||
- 195.46.39.40:53
|
||||
OpenNIC:
|
||||
- 50.116.40.226:53
|
||||
- 50.116.23.211:53
|
||||
SmartViper:
|
||||
- 208.76.50.50:53
|
||||
- 208.76.51.51:53
|
||||
Dyn:
|
||||
- 216.146.35.35:53
|
||||
- 216.146.36.36:53
|
||||
FreeDNS:
|
||||
- 37.235.1.174:53
|
||||
- 37.235.1.177:53
|
||||
Alternate DNS:
|
||||
- 198.101.242.72:53
|
||||
- 23.253.163.53:53
|
||||
Yandex.DNS:
|
||||
- 77.88.8.8:53
|
||||
- 77.88.8.1:53
|
||||
censurfridns.dk:
|
||||
- 89.233.43.71:53
|
||||
- 91.239.100.100:53
|
||||
Hurricane Electric:
|
||||
- 74.82.42.42:53
|
||||
puntCAT:
|
||||
- 109.69.8.51:53
|
||||
|
Loading…
Reference in a new issue