1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
package scan
import (
"bufio"
"crypto/tls"
"errors"
"fmt"
"io"
"net"
)
// Connectivity contains scanners testing basic connectivity to the host
var Connectivity = &Family{
Description: "Scans for basic connectivity with the host through DNS and TCP/TLS dials",
Scanners: map[string]*Scanner{
"DNSLookup": {
"Host can be resolved through DNS",
dnsLookupScan,
},
"CloudFlareStatus": {
"Host is on CloudFlare",
onCloudFlareScan,
},
"TCPDial": {
"Host accepts TCP connection",
tcpDialScan,
},
"TLSDial": {
"Host can perform TLS handshake",
tlsDialScan,
},
},
}
// dnsLookupScan tests that DNS resolution of the host returns at least one address
func dnsLookupScan(addr, hostname string) (grade Grade, output Output, err error) {
addrs, err := net.LookupHost(hostname)
if err != nil {
return
}
if len(addrs) == 0 {
err = errors.New("no addresses found for host")
}
grade, output = Good, addrs
return
}
var (
cfNets []*net.IPNet
cfNetsErr error
)
func initOnCloudFlareScan() ([]*net.IPNet, error) {
// Propogate previous errors and don't attempt to re-download.
if cfNetsErr != nil {
return nil, cfNetsErr
}
// Don't re-download ranges if we already have them.
if len(cfNets) > 0 {
return cfNets, nil
}
// Download CloudFlare CIDR ranges and parse them.
v4resp, err := Client.Get("https://www.cloudflare.com/ips-v4")
if err != nil {
cfNetsErr = fmt.Errorf("Couldn't download CloudFlare IPs: %v", err)
return nil, cfNetsErr
}
defer v4resp.Body.Close()
v6resp, err := Client.Get("https://www.cloudflare.com/ips-v6")
if err != nil {
cfNetsErr = fmt.Errorf("Couldn't download CloudFlare IPs: %v", err)
return nil, cfNetsErr
}
defer v6resp.Body.Close()
scanner := bufio.NewScanner(io.MultiReader(v4resp.Body, v6resp.Body))
for scanner.Scan() {
_, ipnet, err := net.ParseCIDR(scanner.Text())
if err != nil {
cfNetsErr = fmt.Errorf("Couldn't parse CIDR range: %v", err)
return nil, cfNetsErr
}
cfNets = append(cfNets, ipnet)
}
if err := scanner.Err(); err != nil {
cfNetsErr = fmt.Errorf("Couldn't read IP bodies: %v", err)
return nil, cfNetsErr
}
return cfNets, nil
}
func onCloudFlareScan(addr, hostname string) (grade Grade, output Output, err error) {
var cloudflareNets []*net.IPNet
if cloudflareNets, err = initOnCloudFlareScan(); err != nil {
grade = Skipped
return
}
_, addrs, err := dnsLookupScan(addr, hostname)
if err != nil {
return
}
cfStatus := make(map[string]bool)
grade = Good
for _, addr := range addrs.([]string) {
ip := net.ParseIP(addr)
for _, cfNet := range cloudflareNets {
if cfNet.Contains(ip) {
cfStatus[addr] = true
break
}
}
if !cfStatus[addr] {
cfStatus[addr] = false
grade = Bad
}
}
output = cfStatus
return
}
// tcpDialScan tests that the host can be connected to through TCP.
func tcpDialScan(addr, hostname string) (grade Grade, output Output, err error) {
conn, err := Dialer.Dial(Network, addr)
if err != nil {
return
}
conn.Close()
grade = Good
return
}
// tlsDialScan tests that the host can perform a TLS Handshake
// and warns if the server's certificate can't be verified.
func tlsDialScan(addr, hostname string) (grade Grade, output Output, err error) {
var conn *tls.Conn
config := defaultTLSConfig(hostname)
if conn, err = tls.DialWithDialer(Dialer, Network, addr, config); err != nil {
return
}
conn.Close()
config.InsecureSkipVerify = false
if conn, err = tls.DialWithDialer(Dialer, Network, addr, config); err != nil {
grade = Warning
return
}
conn.Close()
grade = Good
return
}
|