Skip to content

Commit

Permalink
allow PTR queries for domain names as well as IP addresses (#378)
Browse files Browse the repository at this point in the history
* Update Bill Herrin's patch from issue #348 to take account of the
ZDNS library and CLI tool split

* added a new util function to check for valid domain names and used it in reverse ptr lookups

* add license

---------

Co-authored-by: phillip-stephens <phillip@cs.stanford.edu>
Co-authored-by: Zakir Durumeric <zakird@gmail.com>
  • Loading branch information
3 people authored Jun 11, 2024
1 parent 79f8a8a commit 2b3e427
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 3 deletions.
7 changes: 7 additions & 0 deletions src/internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"
"fmt"
"net"
"regexp"
"strings"

"github.com/pkg/errors"
Expand Down Expand Up @@ -86,6 +87,12 @@ func GetDefaultResolvers() []string {
return []string{"8.8.8.8:53", "8.8.4.4:53", "1.1.1.1:53", "1.0.0.1:53"}
}

// IsStringValidDomainName checks if the given string is a valid domain name using regex
func IsStringValidDomainName(domain string) bool {
var domainRegex = regexp.MustCompile(`^(?i)[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*\.[a-z]{2,}$`)
return domainRegex.MatchString(domain)
}

// HasCtxExpired checks if the context has expired. Common function used in various places.
func HasCtxExpired(ctx *context.Context) bool {
select {
Expand Down
74 changes: 74 additions & 0 deletions src/internal/util/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* ZDNS Copyright 2024 Regents of the University of Michigan
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package util

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestIsStringValidDomainName(t *testing.T) {
tests := []struct {
domain string
expected bool
}{
// Valid domain names
{"example.com", true},
{"sub-domain.example.com", true},
{"example.co.uk", true},
{"a.com", true},
{"0-0.com", true},
{"example-domain.com", true},
{"EXAMPLE.COM", true},
{"subdomain.example.com", true},
{"subdomain1.subdomain0.zdns-testing.com", true},
{"subdomain4.subdomain3.subdomain2.subdomain1.zdns-testing.com", true},

// Invalid domain names
{"example", false}, // No TLD
{"ex@mple.com", false}, // Invalid character
{".example.com", false}, // Starts with dot
{"-example.com", false}, // Starts with hyphen
{"example-.com", false}, // Ends with hyphen
{"example..com", false}, // Consecutive dots
{"example.com-", false}, // Ends with hyphen
{"example..", false}, // Ends with dot
{"", false}, // Empty string
{"exa mple.com", false}, // Contains space
{"example.123", false}, // TLD with digits only
{"example.com/", false}, // Contains slash
{"example-.com", false}, // Hyphen at end of label
{"-example.com", false}, // Hyphen at start of label
{"example..com", false}, // Double dot
{"example.c", false}, // Single letter TLD
{"example.helloThisTLDIsOverThe63CharacterLimithelloThisTLDIsOverThe63Char", false}, // TLD too long, max 63 chars
{"exa$mple.com", false}, // Invalid character
{"a-very-long-domain-name-that-exceeds-the-maximum-length-of-253-characters-because-it-is-designed-to-" +
"test-the-upper-boundary-conditions-of-domain-name-validation-functionality-in-golang." +
"example.com", false}, // Domain too long, max domain name length is 253 characters
}

for _, test := range tests {
t.Run(test.domain, func(t *testing.T) {
result := IsStringValidDomainName(test.domain)
if test.expected {
require.True(t, result, "For domain %s, expected domain to be valid but got invalid", test.domain)
} else {
require.False(t, result, "For domain %s, expected domain to be invalid but got valid", test.domain)
}
})
}
}
12 changes: 9 additions & 3 deletions src/zdns/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,17 @@ func (r *Resolver) doSingleDstServerLookup(q Question, nameServer string, isIter

if q.Type == dns.TypePTR {
var err error
q.Name, err = dns.ReverseAddr(q.Name)
if err != nil {
var qname string
qname, err = dns.ReverseAddr(q.Name)
// might be an actual DNS name instead of an IP address
// if that looks likely, use it as is
if err != nil && !util.IsStringValidDomainName(q.Name) {
return nil, nil, StatusIllegalInput, err
// q.Name is a valid domain name, we can continue
} else {
// remove trailing "." added by dns.ReverseAddr
q.Name = qname[:len(qname)-1]
}
q.Name = q.Name[:len(q.Name)-1]
}
ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
defer cancel()
Expand Down

0 comments on commit 2b3e427

Please sign in to comment.