Skip to content

Commit

Permalink
add rdap support
Browse files Browse the repository at this point in the history
  • Loading branch information
TomK committed Apr 7, 2020
1 parent bfd7500 commit 2ffd416
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 10 deletions.
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/lucidcube/whois

go 1.13

require github.com/zonedb/zonedb v1.0.2641
23 changes: 23 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
github.com/zonedb/zonedb v1.0.2641 h1:m9omAmaMqdmPI/sc8s1+/gz/dEusyzslJ3Mr0fS4lLM=
github.com/zonedb/zonedb v1.0.2641/go.mod h1:0sNeQCB5BUCvkKDJQbDhQdzuUlbKgPaUMGRA6dcZX3Q=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
10 changes: 10 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package whois

func IsAvailable(domain string) (bool, error) {
rdapResult, err := IsAvailableFromRdap(domain)
if err != nil {
return rdapResult, err
}

return IsAvailableFromWhois(domain)
}
69 changes: 69 additions & 0 deletions rdap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package whois

import (
"encoding/json"
"errors"
"io/ioutil"
"log"
"math/rand"
"net/http"
"strings"
"time"
)

type rdapBootstrap struct {
Version string `json:"version"`
Description string `json:"description"`
Publication time.Time `json:"publication"`
Services [][][]string `json:"services"`
}

var (
rdapDns = map[string][]string{}
)

func RefreshMap() {
response, err := http.Get("http://data.iana.org/rdap/dns.json")
if err == nil {
body, err := ioutil.ReadAll(response.Body)
if err == nil {
bootstrap := rdapBootstrap{}
err := json.Unmarshal(body, &bootstrap)
if err == nil {
rdapDns = map[string][]string{}
for _, svc := range bootstrap.Services {
for _, tld := range svc[0] {
for _, endpoint := range svc[1] {
rdapDns[tld] = append(rdapDns[tld], endpoint)
}
}
}
}
}
}
}

func IsAvailableFromRdap(domain string) (bool, error) {
split := strings.SplitN(domain, ".", 2)
if services, ok := rdapDns[split[1]]; ok && len(services) > 0 {
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(services), func(i, j int) { services[i], services[j] = services[j], services[i] })
for len(services) > 0 {
service := services[0]
services = services[1:]

response, err := http.Get(service + "/domain/" + domain)
if err == nil {
if response.StatusCode == 404 {
return true, nil
}
log.Print(domain, " ", response.StatusCode)
if response.StatusCode == 200 {
return false, nil
}
}
}
return false, errors.New("no valid response from rdap endpoint")
}
return false, errors.New("not an rdap enabled tld")
}
34 changes: 34 additions & 0 deletions rdap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package whois_test

import "github.com/lucidcube/whois"
import "testing"

func TestRdap(t *testing.T) {
whois.RefreshMap()

result, err := whois.IsAvailableFromRdap("test.invalid")
if err == nil {
t.Errorf("invalid domain returned no error")
} else if err.Error() != "not an rdap enabled tld" {
t.Errorf("invalid domain returned wrong error; should be `not an rdap enabled tld`")
}
if result == true {
t.Errorf("invalid domain returned true; should be false")
}

result, err = whois.IsAvailableFromRdap("test.com")
if err != nil {
t.Errorf("taken domain returned an error %s", err.Error())
}
if result == true {
t.Errorf("taken domain returned true; should be false")
}

result, err = whois.IsAvailableFromRdap("available-lucidcube.com")
if err != nil {
t.Errorf("available domain returned an error %s", err.Error())
}
if result == false {
t.Errorf("available domain returned false; should be true")
}
}
14 changes: 4 additions & 10 deletions whois.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,25 +61,21 @@ func ConvertRecord(result string) map[string]string {
return res
}

func IsAvailable(domain string) (bool, error) {
result, err := GetRecord(domain)
func IsAvailableFromWhois(domain string) (bool, error) {
whoisResult, err := GetRecordWithTimeout(domain, 5*time.Second)
if err != nil {
return true, err
return false, err
}
return IsAvailableFromWhois(domain, result), nil
}

func IsAvailableFromWhois(domain string, whoisResult string) bool {
uppercaseResult := strings.ToUpper(whoisResult)
return strings.Contains(uppercaseResult, "NO MATCH FOR") || strings.Contains(uppercaseResult, "DOMAIN NOT FOUND")
return strings.Contains(uppercaseResult, "NO MATCH FOR") || strings.Contains(uppercaseResult, "DOMAIN NOT FOUND"), nil
}

func GetRecord(domain string) (string, error) {
return GetRecordWithTimeout(domain, 5*time.Second)
}

func GetRecordWithTimeout(domain string, timeout time.Duration) (string, error) {

server, err := getServer(domain)
if err != nil {
return "", err
Expand All @@ -88,7 +84,6 @@ func GetRecordWithTimeout(domain string, timeout time.Duration) (string, error)
primaryWhois, err := getWhoisResult(server, domain, timeout)

if strings.Contains(primaryWhois, "To single out one record") {

primaryWhois, err = getWhoisResult(server, "="+domain, timeout)
}

Expand All @@ -111,7 +106,6 @@ func GetRecordWithTimeout(domain string, timeout time.Duration) (string, error)
}

return fullWhois, nil

}

func getWhoisResult(server string, domain string, timeout time.Duration) (string, error) {
Expand Down
34 changes: 34 additions & 0 deletions whois_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package whois_test

import (
"github.com/lucidcube/whois"
"testing"
)

func TestWhois(t *testing.T) {
result, err := whois.IsAvailableFromWhois("test.invalid")
if err == nil {
t.Errorf("invalid domain returned no error")
} else if err.Error() != "no public zone found for test.invalid" {
t.Errorf("invalid domain returned wrong error `%s`; should be `not an rdap enabled tld`", err)
}
if result == true {
t.Errorf("invalid domain returned true; should be false")
}

result, err = whois.IsAvailableFromWhois("test.com")
if err != nil {
t.Errorf("taken domain returned an error %s", err)
}
if result == true {
t.Errorf("taken domain returned true; should be false")
}

result, err = whois.IsAvailableFromWhois("available-lucidcube.com")
if err != nil {
t.Errorf("available domain returned an error %s", err)
}
if result == false {
t.Errorf("available domain returned false; should be true")
}
}

0 comments on commit 2ffd416

Please sign in to comment.