Adding upstream version 1.1.10.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
0935a35f2a
commit
504a5578e5
12 changed files with 537 additions and 0 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* text=auto eol=lf
|
12
.github/dependabot.yml
vendored
Normal file
12
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
day: "monday"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
day: "monday"
|
20
.github/workflows/test.yml
vendored
Normal file
20
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
name: test
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '*.md'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths-ignore:
|
||||||
|
- '*.md'
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 3
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 1.22.2
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: go test ./... --cover
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.idea
|
||||||
|
bin
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022-2023 TwiN
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
4
Makefile
Normal file
4
Makefile
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.PHONY: build-binaries
|
||||||
|
|
||||||
|
build-binaries:
|
||||||
|
./scripts/build.sh
|
83
README.md
Normal file
83
README.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# whois
|
||||||
|

|
||||||
|
|
||||||
|
Lightweight library for retrieving WHOIS information on a domain.
|
||||||
|
|
||||||
|
It automatically retrieves the appropriate WHOIS server based on the domain's TLD by first querying IANA.
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
### As an executable
|
||||||
|
To install it:
|
||||||
|
```console
|
||||||
|
go install github.com/TwiN/whois/cmd/whois@latest
|
||||||
|
```
|
||||||
|
Alternatively, you can download whois from the [release](https://github.com/TwiN/whois/releases) section, though you'll have to unzip the file first.
|
||||||
|
|
||||||
|
To run it:
|
||||||
|
```console
|
||||||
|
whois example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### As a library
|
||||||
|
```console
|
||||||
|
go get github.com/TwiN/whois
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Query
|
||||||
|
If all you want is the text a WHOIS server would return you, you can use the `Query` method of the `whois.Client` type:
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/TwiN/whois"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
client := whois.NewClient()
|
||||||
|
output, err := client.Query("example.com")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
println(output)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### QueryAndParse
|
||||||
|
If you want specific pieces of information, you can use the `QueryAndParse` method of the `whois.Client` type:
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/TwiN/whois"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
client := whois.NewClient()
|
||||||
|
response, err := client.QueryAndParse("example.com")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
println(response.ExpirationDate.String())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Note that because there is no standardized format for WHOIS responses, this parsing may not be successful for every single TLD.
|
||||||
|
|
||||||
|
Currently, the only fields parsed are:
|
||||||
|
- `ExpirationDate`: The time.Time at which the domain will expire
|
||||||
|
- `DomainStatuses`: The statuses that the domain currently has (e.g. `clientTransferProhibited`)
|
||||||
|
- `NameServers`: The nameservers currently tied to the domain
|
||||||
|
|
||||||
|
If you'd like one or more other fields to be parsed, please don't be shy and create an issue or a pull request.
|
||||||
|
|
||||||
|
#### Caching referral WHOIS servers
|
||||||
|
The way that WHOIS scales is by having one "main" WHOIS server, namely `whois.iana.org:43`, refer to other WHOIS server
|
||||||
|
on a per-TLD basis.
|
||||||
|
|
||||||
|
In other word, let's say that you wanted to have the WHOIS information for `example.com`.
|
||||||
|
The first step would be to query `whois.iana.org:43` with `com`, which would return `whois.verisign-grs.com`.
|
||||||
|
Then, you would query `whois.verisign-grs.com:43` for the WHOIS information on `example.com`.
|
||||||
|
|
||||||
|
If you're querying a lot of servers, making two queries instead of one can be a little wasteful, hence `WithReferralCache(true)`:
|
||||||
|
```go
|
||||||
|
client := whois.NewClient().WithReferralCache(true)
|
||||||
|
```
|
||||||
|
The above will cache the referral WHOIS server for each TLD, so that you can directly query the appropriate WHOIS server
|
||||||
|
instead of first querying `whois.iana.org:43` for the referral.
|
||||||
|
|
22
cmd/whois/main.go
Normal file
22
cmd/whois/main.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/TwiN/whois"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) != 2 {
|
||||||
|
_, _ = fmt.Fprintln(os.Stderr, "you must provide exactly one domain")
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
output, err := whois.NewClient().Query(os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
_, _ = fmt.Fprintln(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println(output)
|
||||||
|
}
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/TwiN/whois
|
||||||
|
|
||||||
|
go 1.22.2
|
30
scripts/build.sh
Normal file
30
scripts/build.sh
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# NOTE: You are expected to call this file from the Makefile at the root of this project #
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
APPLICATION_NAME="whois"
|
||||||
|
|
||||||
|
mkdir bin
|
||||||
|
rm -rf bin/${APPLICATION_NAME}-*.zip
|
||||||
|
|
||||||
|
env GOOS=darwin GOARCH=amd64 go build -o ${APPLICATION_NAME} ./cmd/whois/main.go
|
||||||
|
chmod +x ${APPLICATION_NAME}
|
||||||
|
zip bin/${APPLICATION_NAME}-darwin-amd64.zip ${APPLICATION_NAME} -m
|
||||||
|
|
||||||
|
env GOOS=darwin GOARCH=arm64 go build -o ${APPLICATION_NAME} ./cmd/whois/main.go
|
||||||
|
chmod +x ${APPLICATION_NAME}
|
||||||
|
zip bin/${APPLICATION_NAME}-darwin-arm64.zip ${APPLICATION_NAME} -m
|
||||||
|
|
||||||
|
env GOOS=linux GOARCH=amd64 go build -o ${APPLICATION_NAME} ./cmd/whois/main.go
|
||||||
|
chmod +x ${APPLICATION_NAME}
|
||||||
|
zip bin/${APPLICATION_NAME}-linux-amd64.zip ${APPLICATION_NAME} -m
|
||||||
|
|
||||||
|
env GOOS=linux GOARCH=arm64 go build -o ${APPLICATION_NAME} ./cmd/whois/main.go
|
||||||
|
chmod +x ${APPLICATION_NAME}
|
||||||
|
zip bin/${APPLICATION_NAME}-linux-arm64.zip ${APPLICATION_NAME} -m
|
||||||
|
|
||||||
|
env GOOS=windows GOARCH=amd64 go build -o ${APPLICATION_NAME}.exe ./cmd/whois/main.go
|
||||||
|
chmod +x ${APPLICATION_NAME}.exe
|
||||||
|
zip bin/${APPLICATION_NAME}-windows-amd64.zip ${APPLICATION_NAME}.exe -m
|
191
whois.go
Normal file
191
whois.go
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
package whois
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ianaWHOISServerAddress = "whois.iana.org:43"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tldWithoutExpirationDate = []string{"at", "be", "ch", "co.at", "com.br", "or.at", "de", "fr", "nl"}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
whoisServerAddress string
|
||||||
|
|
||||||
|
isCachingReferralWHOISServers bool
|
||||||
|
referralWHOISServersCache map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient() *Client {
|
||||||
|
return &Client{
|
||||||
|
whoisServerAddress: ianaWHOISServerAddress,
|
||||||
|
referralWHOISServersCache: make(map[string]string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithReferralCache allows you to enable or disable the referral WHOIS server cache.
|
||||||
|
// While ianaWHOISServerAddress is the "entry point" for WHOIS queries, it sometimes has
|
||||||
|
// availability issues. One way to mitigate this is to cache the referral WHOIS server.
|
||||||
|
//
|
||||||
|
// This is disabled by default
|
||||||
|
func (c *Client) WithReferralCache(enabled bool) *Client {
|
||||||
|
c.isCachingReferralWHOISServers = enabled
|
||||||
|
if enabled {
|
||||||
|
// We'll set a couple of common ones right away to avoid unnecessary queries
|
||||||
|
c.referralWHOISServersCache = map[string]string{
|
||||||
|
"com": "whois.verisign-grs.com",
|
||||||
|
"black": "whois.nic.black",
|
||||||
|
"dev": "whois.nic.google",
|
||||||
|
"green": "whois.nic.green",
|
||||||
|
"io": "whois.nic.io",
|
||||||
|
"net": "whois.verisign-grs.com",
|
||||||
|
"org": "whois.publicinterestregistry.org",
|
||||||
|
"red": "whois.nic.red",
|
||||||
|
"sh": "whois.nic.sh",
|
||||||
|
"uk": "whois.nic.uk",
|
||||||
|
"mx": "whois.nic.mx",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func doesTLDHaveExpirationDate(e string) bool {
|
||||||
|
for _, a := range tldWithoutExpirationDate {
|
||||||
|
if a == e {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Query(domain string) (string, error) {
|
||||||
|
parts := strings.Split(domain, ".")
|
||||||
|
domainExtension := parts[len(parts)-1]
|
||||||
|
if doesTLDHaveExpirationDate(domainExtension) {
|
||||||
|
return "", errors.New("domain extension " + domainExtension + " does not have a grace period.")
|
||||||
|
}
|
||||||
|
if c.isCachingReferralWHOISServers {
|
||||||
|
if cachedWHOISServer, ok := c.referralWHOISServersCache[domain]; ok {
|
||||||
|
return c.query(cachedWHOISServer, domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var output string
|
||||||
|
var err error
|
||||||
|
switch domainExtension {
|
||||||
|
case "ua":
|
||||||
|
if len(parts) > 2 && len(parts[len(parts)-2]) < 4 {
|
||||||
|
domainExtension = parts[len(parts)-2] + "." + domainExtension
|
||||||
|
}
|
||||||
|
output, err = c.query("whois."+domainExtension+":43", domain)
|
||||||
|
default:
|
||||||
|
output, err = c.query(c.whoisServerAddress, domainExtension)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if strings.Contains(output, "whois:") {
|
||||||
|
startIndex := strings.Index(output, "whois:") + 6
|
||||||
|
endIndex := strings.Index(output[startIndex:], "\n") + startIndex
|
||||||
|
whois := strings.TrimSpace(output[startIndex:endIndex])
|
||||||
|
if referOutput, err := c.query(whois+":43", domain); err == nil {
|
||||||
|
if c.isCachingReferralWHOISServers {
|
||||||
|
c.referralWHOISServersCache[domain] = whois + ":43"
|
||||||
|
}
|
||||||
|
return referOutput, nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) query(whoisServerAddress, domain string) (string, error) {
|
||||||
|
connection, err := net.DialTimeout("tcp", whoisServerAddress, 10*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer connection.Close()
|
||||||
|
_ = connection.SetDeadline(time.Now().Add(5 * time.Second))
|
||||||
|
_, err = connection.Write([]byte(domain + "\r\n"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
output, err := io.ReadAll(connection)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
ExpirationDate time.Time
|
||||||
|
DomainStatuses []string
|
||||||
|
NameServers []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryAndParse tries to parse the response from the WHOIS server
|
||||||
|
// There is no standardized format for WHOIS responses, so this is an attempt at best.
|
||||||
|
//
|
||||||
|
// Being the selfish person that I am, I also only parse the fields that I need.
|
||||||
|
// If you need more fields, please open an issue or pull request.
|
||||||
|
func (c *Client) QueryAndParse(domain string) (*Response, error) {
|
||||||
|
text, err := c.Query(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response := Response{}
|
||||||
|
for _, line := range strings.Split(text, "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
valueStartIndex := strings.Index(line, ":")
|
||||||
|
if valueStartIndex == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := strings.ToLower(strings.TrimSpace(line[:valueStartIndex]))
|
||||||
|
value := strings.TrimSpace(line[valueStartIndex+1:])
|
||||||
|
if strings.Contains(key, "expir") {
|
||||||
|
switch {
|
||||||
|
case strings.HasSuffix(domain, ".pp.ua"):
|
||||||
|
response.ExpirationDate, _ = time.Parse("02-Jan-2006 15:04:05 MST", strings.ToUpper(value))
|
||||||
|
case strings.HasSuffix(domain, ".ua"):
|
||||||
|
response.ExpirationDate, _ = time.Parse("2006-01-02 15:04:05Z07", strings.ToUpper(value))
|
||||||
|
case strings.HasSuffix(domain, ".uk"):
|
||||||
|
response.ExpirationDate, _ = time.Parse("02-Jan-2006", strings.ToUpper(value))
|
||||||
|
case strings.HasSuffix(domain, ".cz"):
|
||||||
|
response.ExpirationDate, _ = time.Parse("02.01.2006", strings.ToUpper(value))
|
||||||
|
case strings.HasSuffix(domain, ".im"):
|
||||||
|
response.ExpirationDate, _ = time.Parse("02/01/2006 15:04:05", strings.ToUpper(value))
|
||||||
|
case strings.HasSuffix(domain, ".scot"):
|
||||||
|
if !strings.Contains(key, "registrar") {
|
||||||
|
response.ExpirationDate, _ = time.Parse(time.RFC3339, strings.ToUpper(value))
|
||||||
|
}
|
||||||
|
case strings.HasSuffix(domain, ".br"):
|
||||||
|
response.ExpirationDate, _ = time.Parse("20060102", strings.ToUpper(value))
|
||||||
|
case strings.HasSuffix(domain, ".cn"):
|
||||||
|
response.ExpirationDate, _ = time.Parse("2006-01-02 15:04:05", strings.ToUpper(value))
|
||||||
|
case strings.HasSuffix(domain, ".mx"):
|
||||||
|
response.ExpirationDate, _ = time.Parse(time.DateOnly, strings.ToUpper(value))
|
||||||
|
default:
|
||||||
|
response.ExpirationDate, _ = time.Parse(time.RFC3339, strings.ToUpper(value))
|
||||||
|
}
|
||||||
|
} else if key == "paid-till" {
|
||||||
|
// example for ru/su domains -> paid-till: 2024-05-26T21:00:00Z
|
||||||
|
if strings.HasSuffix(domain, ".ru") || strings.HasSuffix(domain, ".su") {
|
||||||
|
response.ExpirationDate, _ = time.Parse(time.RFC3339, strings.ToUpper(value))
|
||||||
|
}
|
||||||
|
} else if strings.Contains(key, "status") {
|
||||||
|
response.DomainStatuses = append(response.DomainStatuses, value)
|
||||||
|
} else if key == "state" {
|
||||||
|
// example for ru/su domains -> state: DELEGATED, VERIFIED
|
||||||
|
if strings.HasSuffix(domain, ".ru") || strings.HasSuffix(domain, ".su") {
|
||||||
|
response.DomainStatuses = strings.Split(value, ", ")
|
||||||
|
}
|
||||||
|
} else if strings.Contains(key, "name server") || strings.Contains(key, "nserver") {
|
||||||
|
response.NameServers = append(response.NameServers, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &response, nil
|
||||||
|
}
|
148
whois_test.go
Normal file
148
whois_test.go
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package whois
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClient(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
domain string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
domain: "name.com",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.org",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.net",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.sh",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.io",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "get.dev",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.red",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.green",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "color.black",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.cn",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.de",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "google.com.br", // name.com.br is handled weirdly by whois.registro.br, so we'll use this instead
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.ua",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.pp.ua",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.co.uk",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.cz",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.me",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.im",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.uk",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "dot.scot", // name.scot not registered
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "name.ru", // expiration date in `paid-till` field
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "register.su", // expiration date in `paid-till` field
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "nic.mx",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
client := NewClient().WithReferralCache(true)
|
||||||
|
for _, scenario := range scenarios {
|
||||||
|
t.Run(scenario.domain+"_Query", func(t *testing.T) {
|
||||||
|
output, err := client.Query(scenario.domain)
|
||||||
|
if scenario.wantErr && err == nil {
|
||||||
|
t.Error("expected error, got none")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !scenario.wantErr {
|
||||||
|
if err != nil {
|
||||||
|
t.Error("expected no error, got", err.Error())
|
||||||
|
}
|
||||||
|
if !strings.Contains(strings.ToLower(output), scenario.domain) {
|
||||||
|
t.Errorf("expected %s in output, got %s", scenario.domain, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
time.Sleep(50 * time.Millisecond) // Give the WHOIS servers some breathing room
|
||||||
|
t.Run(scenario.domain+"_QueryAndParse", func(t *testing.T) {
|
||||||
|
response, err := client.QueryAndParse(scenario.domain)
|
||||||
|
if scenario.wantErr && err == nil {
|
||||||
|
t.Error("expected error, got none")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !scenario.wantErr {
|
||||||
|
if err != nil {
|
||||||
|
t.Error("expected no error, got", err.Error(), "for domain", scenario.domain)
|
||||||
|
}
|
||||||
|
if response.ExpirationDate.Unix() <= 0 {
|
||||||
|
t.Error("expected to have a valid expiry date, got", response.ExpirationDate.Unix(), "for domain", scenario.domain)
|
||||||
|
}
|
||||||
|
if len(response.NameServers) == 0 {
|
||||||
|
t.Errorf("expected to have at least one name server for domain %s", scenario.domain)
|
||||||
|
}
|
||||||
|
if len(response.DomainStatuses) == 0 && !strings.HasSuffix(scenario.domain, ".im") && !strings.HasSuffix(scenario.domain, ".mx") && !strings.HasSuffix(scenario.domain, ".cz") {
|
||||||
|
t.Errorf("expected to have at least one domain status for domain %s", scenario.domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
time.Sleep(50 * time.Millisecond) // Give the WHOIS servers some breathing room
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue