Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added internal range check option to retryabledns #40

Merged
merged 2 commits into from Jan 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions client.go
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/gob"
"encoding/json"
"errors"
"fmt"
"math/rand"
"net"
"strings"
Expand All @@ -18,8 +19,16 @@ import (
"github.com/projectdiscovery/retryabledns/hostsfile"
)

var internalRangeCheckerInstance *internalRangeChecker

func init() {
rand.Seed(time.Now().UnixNano())

var err error
internalRangeCheckerInstance, err = newInternalRangeChecker()
if err != nil {
fmt.Printf("could not initialize range checker: %s\n", err)
}
}

// Client is a DNS resolver client to resolve hostnames.
Expand Down Expand Up @@ -391,20 +400,29 @@ type DNSData struct {
NS []string `json:"ns,omitempty"`
TXT []string `json:"txt,omitempty"`
Raw string `json:"raw,omitempty"`
Internal bool `json:"internal,omitempty"`
StatusCode string `json:"status_code,omitempty"`
StatusCodeRaw int `json:"status_code_raw,omitempty"`
TraceData *TraceData `json:"trace,omitempty"`
RawResp *dns.Msg `json:"raw_resp,omitempty"`
Timestamp time.Time `json:"timestamp,omitempty"`
}

// CheckInternalIPs when set to true returns if DNS response IPs
// belong to internal IP ranges.
var CheckInternalIPs = false

// ParseFromMsg and enrich data
func (d *DNSData) ParseFromMsg(msg *dns.Msg) error {
allRecords := append(msg.Answer, msg.Extra...)
allRecords = append(allRecords, msg.Ns...)

for _, record := range allRecords {
switch recordType := record.(type) {
case *dns.A:
if CheckInternalIPs && internalRangeCheckerInstance != nil && internalRangeCheckerInstance.ContainsIPv4(recordType.A) {
d.Internal = true
}
d.A = append(d.A, trimChars(recordType.A.String()))
case *dns.NS:
d.NS = append(d.NS, trimChars(recordType.Ns))
Expand All @@ -422,9 +440,13 @@ func (d *DNSData) ParseFromMsg(msg *dns.Msg) error {
d.TXT = append(d.TXT, trimChars(txt))
}
case *dns.AAAA:
if CheckInternalIPs && internalRangeCheckerInstance.ContainsIPv6(recordType.AAAA) {
d.Internal = true
}
d.AAAA = append(d.AAAA, trimChars(recordType.AAAA.String()))
}
}

return nil
}

Expand Down
104 changes: 104 additions & 0 deletions validate.go
@@ -0,0 +1,104 @@
package retryabledns

import "net"

// ipv4InternalRanges contains the IP ranges internal in IPv4 range.
var ipv4InternalRanges = []string{
"0.0.0.0/8", // Current network (only valid as source address)
"10.0.0.0/8", // Private network
"100.64.0.0/10", // Shared Address Space
"127.0.0.0/8", // Loopback
"169.254.0.0/16", // Link-local (Also many cloud providers Metadata endpoint)
"172.16.0.0/12", // Private network
"192.0.0.0/24", // IETF Protocol Assignments
"192.0.2.0/24", // TEST-NET-1, documentation and examples
"192.88.99.0/24", // IPv6 to IPv4 relay (includes 2002::/16)
"192.168.0.0/16", // Private network
"198.18.0.0/15", // Network benchmark tests
"198.51.100.0/24", // TEST-NET-2, documentation and examples
"203.0.113.0/24", // TEST-NET-3, documentation and examples
"224.0.0.0/4", // IP multicast (former Class D network)
"240.0.0.0/4", // Reserved (former Class E network)
}

// ipv6InternalRanges contains the IP ranges internal in IPv6 range.
var ipv6InternalRanges = []string{
"::1/128", // Loopback
"64:ff9b::/96", // IPv4/IPv6 translation (RFC 6052)
"100::/64", // Discard prefix (RFC 6666)
"2001::/32", // Teredo tunneling
"2001:10::/28", // Deprecated (previously ORCHID)
"2001:20::/28", // ORCHIDv2
"2001:db8::/32", // Addresses used in documentation and example source code
"2002::/16", // 6to4
"fc00::/7", // Unique local address
"fe80::/10", // Link-local address
"ff00::/8", // Multicast
}

// internalRangeChecker contains a list of internal IP ranges.
type internalRangeChecker struct {
ipv4 []*net.IPNet
ipv6 []*net.IPNet
}

// newInternalRangeChecker creates a structure for checking if a host is from
// a internal IP range whether its ipv4 or ipv6.
func newInternalRangeChecker() (*internalRangeChecker, error) {
rangeChecker := internalRangeChecker{}

err := rangeChecker.appendIPv4Ranges(ipv4InternalRanges)
if err != nil {
return nil, err
}

err = rangeChecker.appendIPv6Ranges(ipv6InternalRanges)
if err != nil {
return nil, err
}
return &rangeChecker, nil
}

// appendIPv4Ranges adds a list of IPv4 Ranges to the list.
func (r *internalRangeChecker) appendIPv4Ranges(ranges []string) error {
for _, ip := range ranges {
_, rangeNet, err := net.ParseCIDR(ip)
if err != nil {
return err
}
r.ipv4 = append(r.ipv4, rangeNet)
}
return nil
}

// appendIPv6Ranges adds a list of IPv6 Ranges to the list.
func (r *internalRangeChecker) appendIPv6Ranges(ranges []string) error {
for _, ip := range ranges {
_, rangeNet, err := net.ParseCIDR(ip)
if err != nil {
return err
}
r.ipv6 = append(r.ipv6, rangeNet)
}
return nil
}

// ContainsIPv4 checks whether a given IP address exists in the internal IPv4 ranges.
func (r *internalRangeChecker) ContainsIPv4(IP net.IP) bool {
for _, net := range r.ipv4 {
if net.Contains(IP) {
return true
}
}
return false
}

// ContainsIPv6 checks whether a given IP address exists in the internal IPv6 ranges.
func (r *internalRangeChecker) ContainsIPv6(IP net.IP) bool {
for _, net := range r.ipv6 {
if net.Contains(IP) {
return true
}
}
return false
}