diff --git a/README.md b/README.md index 7128fd6..389dc05 100644 --- a/README.md +++ b/README.md @@ -88,15 +88,17 @@ This will also download the latest GeoIP database (IPLocation-lite). ### Usage ```bash -udig [-h|--help] [-v|--version] [-V|--verbose] [--json] [-d|--domain ""] +udig [-h|--help] [-v|--version] [-V|--verbose] [-s|--strict] [--json] + [-d|--domain "" [-d|--domain "" ...]] - ÜberDig - dig on steroids v1.3 by stuchl4n3k + ÜberDig - dig on steroids v1.4 by stuchl4n3k Arguments: -h --help Print help information -v --version Print version and exit -V --verbose Be more verbose + -s --strict Strict domain relation (TLD match) --json Output payloads as JSON objects -d --domain Domain to resolve ``` diff --git a/cmd/udig/main.go b/cmd/udig/main.go index da25b48..5563a0f 100644 --- a/cmd/udig/main.go +++ b/cmd/udig/main.go @@ -3,16 +3,17 @@ package main import ( "encoding/json" "fmt" + "net/url" + "os" + "github.com/akamensky/argparse" "github.com/miekg/dns" "github.com/netrixone/udig" - "net/url" - "os" ) const ( prog = "udig" - version = "1.3" + version = "1.4" author = "stuchl4n3k" description = "ÜberDig - dig on steroids v" + version + " by " + author ) @@ -103,6 +104,7 @@ func main() { parser := argparse.NewParser(prog, description) printVersion := parser.Flag("v", "version", &argparse.Options{Required: false, Help: "Print version and exit"}) beVerbose := parser.Flag("V", "verbose", &argparse.Options{Required: false, Help: "Be more verbose"}) + beStrict := parser.Flag("s", "strict", &argparse.Options{Required: false, Help: "Strict domain relation (TLD match)"}) jsonOutput := parser.Flag("", "json", &argparse.Options{Required: false, Help: "Output payloads as JSON objects"}) domain := parser.String("d", "domain", &argparse.Options{Required: false, Help: "Domain to resolve"}) @@ -126,6 +128,10 @@ func main() { udig.LogLevel = udig.LogLevelInfo } + if *beStrict { + udig.IsDomainRelated = udig.StrictDomainRelation + } + outputJson = *jsonOutput fmt.Println(banner) diff --git a/dns_test.go b/dns_test.go index c11c4ae..b43f79b 100644 --- a/dns_test.go +++ b/dns_test.go @@ -294,74 +294,6 @@ func Test_parentDomainOf_By_TLD(t *testing.T) { assert.Empty(t, parent) } -func Test_isDomainRelated_By_same_domain(t *testing.T) { - // Setup. - domainA := "example.com" - domainB := domainA - - // Execute. - res1 := isDomainRelated(domainA, domainB) - res2 := isDomainRelated(domainB, domainA) - - // Assert. - assert.Equal(t, true, res1) - assert.Equal(t, true, res2) -} - -func Test_isDomainRelated_By_subdomain(t *testing.T) { - // Setup. - domainA := "example.com" - domainB := "sub.example.com" - - // Execute. - res1 := isDomainRelated(domainA, domainB) - res2 := isDomainRelated(domainB, domainA) - - // Assert. - assert.Equal(t, true, res1) - assert.Equal(t, true, res2) -} - -func Test_isDomainRelated_By_domain_with_different_TLD(t *testing.T) { - // Setup. - domainA := "example.com" - domainB := "sub.example.net" - - // Execute. - res1 := isDomainRelated(domainA, domainB) - res2 := isDomainRelated(domainB, domainA) - - // Assert. - assert.Equal(t, true, res1) - assert.Equal(t, true, res2) -} - -func Test_isDomainRelated_By_TLDs(t *testing.T) { - // Setup. - domainA := "com" - domainB := "com" - - // Execute. - res := isDomainRelated(domainA, domainB) - - // Assert. - assert.Equal(t, false, res) -} - -func Test_isDomainRelated_By_invalid_domain(t *testing.T) { - // Setup. - domainA := "." - domainB := "example.com" - - // Execute. - res1 := isDomainRelated(domainA, domainB) - res2 := isDomainRelated(domainB, domainA) - - // Assert. - assert.Equal(t, false, res1) - assert.Equal(t, false, res2) -} - func mockDNSResponse(qType uint16, numRecords int) *dns.Msg { msg := &dns.Msg{} diff --git a/udig.go b/udig.go index 750de77..4494a91 100644 --- a/udig.go +++ b/udig.go @@ -161,7 +161,7 @@ func (udig *udigImpl) isCnameOrRelated(nextDomain string, resolution Resolution) } // Otherwise try heuristics. - return isDomainRelated(nextDomain, resolution.Query()) + return IsDomainRelated(nextDomain, resolution.Query()) } func (udig *udigImpl) getRelatedDomains(resolutions []Resolution) (domains []string) { diff --git a/utils.go b/utils.go index 4ed2e27..2b31ea6 100644 --- a/utils.go +++ b/utils.go @@ -27,10 +27,19 @@ const ( ) var ( - domainPattern = regexp.MustCompile(_domain) - ipPattern = regexp.MustCompile(_ip) + DefaultDomainRelation DomainRelationFn = func(domainA string, domainB string) bool { + return isDomainRelated(domainA, domainB, false) + } + StrictDomainRelation DomainRelationFn = func(domainA string, domainB string) bool { + return isDomainRelated(domainA, domainB, true) + } + IsDomainRelated = DefaultDomainRelation + domainPattern = regexp.MustCompile(_domain) + ipPattern = regexp.MustCompile(_ip) ) +type DomainRelationFn func(domainA string, domainB string) bool + func init() { domainPattern.Longest() } @@ -81,7 +90,7 @@ func cleanDomain(domain string) string { return strings.ToLower(domain) } -func isDomainRelated(domainA string, domainB string) bool { +func isDomainRelated(domainA string, domainB string, strict bool) bool { labelsA := dns.SplitDomainName(domainA) labelsB := dns.SplitDomainName(domainB) @@ -90,7 +99,10 @@ func isDomainRelated(domainA string, domainB string) bool { return false } - if len(labelsA) < 2 || len(labelsB) < 2 { + labelsALen := len(labelsA) + labelsBLen := len(labelsB) + + if labelsALen < 2 || labelsBLen < 2 { // Ignore TLDs. return false } @@ -103,10 +115,12 @@ func isDomainRelated(domainA string, domainB string) bool { // // => Therefor we say the domains are related iff at least 2nd order domains are the same. - labelsALen := len(labelsA) - labelsBLen := len(labelsB) - - return labelsA[labelsALen-2] == labelsB[labelsBLen-2] + related := labelsA[labelsALen-2] == labelsB[labelsBLen-2] + if related && strict { + // In strict mode we also require TLD match. + related = labelsA[labelsALen-1] == labelsB[labelsBLen-1] + } + return related } // reverseIPv4 returns a given IPv4 address in ARPA-like rDNS form. diff --git a/utils_test.go b/utils_test.go index 5608672..846f6e1 100644 --- a/utils_test.go +++ b/utils_test.go @@ -77,3 +77,85 @@ func Test_DissectDomainsFrom_By_invalid_domain(t *testing.T) { assert.Len(t, domains, 1) assert.Equal(t, "example.com", domains[0]) } + +func Test_isDomainRelated_By_same_domain(t *testing.T) { + // Setup. + domainA := "example.com" + domainB := domainA + + // Execute. + res1 := isDomainRelated(domainA, domainB, false) + res2 := isDomainRelated(domainB, domainA, false) + + // Assert. + assert.Equal(t, true, res1) + assert.Equal(t, true, res2) +} + +func Test_isDomainRelated_By_subdomain(t *testing.T) { + // Setup. + domainA := "example.com" + domainB := "sub.example.com" + + // Execute. + res1 := isDomainRelated(domainA, domainB, false) + res2 := isDomainRelated(domainB, domainA, false) + + // Assert. + assert.Equal(t, true, res1) + assert.Equal(t, true, res2) +} + +func Test_isDomainRelated_By_domain_with_different_TLD(t *testing.T) { + // Setup. + domainA := "example.com" + domainB := "sub.example.net" + + // Execute. + res1 := isDomainRelated(domainA, domainB, false) + res2 := isDomainRelated(domainB, domainA, false) + + // Assert. + assert.Equal(t, true, res1) + assert.Equal(t, true, res2) +} + +func Test_isDomainRelated_By_domain_with_different_TLD_strict(t *testing.T) { + // Setup. + domainA := "example.com" + domainB := "sub.example.net" + + // Execute. + res1 := isDomainRelated(domainA, domainB, true) + res2 := isDomainRelated(domainB, domainA, true) + + // Assert. + assert.Equal(t, false, res1) + assert.Equal(t, false, res2) +} + +func Test_isDomainRelated_By_TLDs(t *testing.T) { + // Setup. + domainA := "com" + domainB := "com" + + // Execute. + res := isDomainRelated(domainA, domainB, false) + + // Assert. + assert.Equal(t, false, res) +} + +func Test_isDomainRelated_By_invalid_domain(t *testing.T) { + // Setup. + domainA := "." + domainB := "example.com" + + // Execute. + res1 := isDomainRelated(domainA, domainB, false) + res2 := isDomainRelated(domainB, domainA, false) + + // Assert. + assert.Equal(t, false, res1) + assert.Equal(t, false, res2) +}