From 46aeef6bc70424979ca4afa32dc1330d67b47065 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Thu, 11 May 2023 10:59:57 -0500 Subject: [PATCH 01/17] meta: upgrade to Go 1.20, switch to Github Actions Issue: https://github.com/openrdap/rdap/issues/21 --- .github/workflows/go.yml | 32 ++++++++++++++++++++++++++++++++ .travis.yml | 8 -------- README.md | 7 +++---- go.mod | 14 ++++++++------ go.sum | 25 ++++++++++++------------- 5 files changed, 55 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/go.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..8704165 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,32 @@ +name: Go + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + name: Go Build + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: '>= 1.20.4' + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v3 + + - name: Linters + run: | + go vet ./... + + - name: Test + run: | + go test ./... diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 622d6b5..0000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: go - -go: - - 1.7 - - master - -script: - - go test -v ./... diff --git a/README.md b/README.md index 288aa1f..3ba6068 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ This program uses Go. The Go compiler is available from https://golang.org/. To install: - go get -u github.com/openrdap/rdap/cmd/rdap + go install github.com/openrdap/rdap/cmd/rdap This will install the "rdap" binary in your $GOPATH/go/bin directory. Try running: @@ -64,8 +64,8 @@ See https://www.openrdap.org/docs. ## Go docs [![godoc](https://godoc.org/github.com/openrdap/rdap?status.png)](https://godoc.org/github.com/openrdap/rdap) -## Requires -Go 1.7+ +## Uses +Go 1.20+ ## Links - Wikipedia - [Registration Data Access Protocol](https://en.wikipedia.org/wiki/Registration_Data_Access_Protocol) @@ -81,4 +81,3 @@ Go 1.7+ - [RFC 7482 Registration Data Access Protocol (RDAP) Query Format](https://tools.ietf.org/html/rfc7482) - [RFC 7483 JSON Responses for the Registration Data Access Protocol (RDAP)](https://tools.ietf.org/html/rfc7483) - [RFC 7484 Finding the Authoritative Registration Data (RDAP) Service](https://tools.ietf.org/html/rfc7484) - diff --git a/go.mod b/go.mod index 5e7c596..be97c4f 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,16 @@ module github.com/openrdap/rdap -go 1.12 +go 1.20 require ( - github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect - github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 // indirect github.com/davecgh/go-spew v1.1.1 - github.com/jarcoal/httpmock v1.0.4 + github.com/jarcoal/httpmock v1.3.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/stretchr/testify v1.3.0 // indirect - golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 + golang.org/x/crypto v0.9.0 gopkg.in/alecthomas/kingpin.v2 v2.2.6 ) + +require ( + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect +) diff --git a/go.sum b/go.sum index 6ea77af..55ef396 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,24 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/jarcoal/httpmock v1.0.4 h1:jp+dy/+nonJE4g4xbVtl9QdrUNbn6/3hDT5R4nDIZnA= -github.com/jarcoal/httpmock v1.0.4/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= +github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= +github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= +github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From dee144e144cab8abd34ff84485cdee046dbb52a9 Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Mon, 15 May 2023 19:10:29 +0100 Subject: [PATCH 02/17] Update alecthomas/kingpin import path to prevent "module declares its path as...but was required as" error. --- cli.go | 2 +- go.mod | 6 +++--- go.sum | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cli.go b/cli.go index 3038fe2..f104c9f 100644 --- a/cli.go +++ b/cli.go @@ -23,7 +23,7 @@ import ( "golang.org/x/crypto/pkcs12" - kingpin "gopkg.in/alecthomas/kingpin.v2" + kingpin "github.com/alecthomas/kingpin/v2" ) var ( diff --git a/go.mod b/go.mod index be97c4f..beaf0b4 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,16 @@ module github.com/openrdap/rdap -go 1.20 +go 1.19 require ( + github.com/alecthomas/kingpin/v2 v2.3.2 github.com/davecgh/go-spew v1.1.1 github.com/jarcoal/httpmock v1.3.0 github.com/mitchellh/go-homedir v1.1.0 golang.org/x/crypto v0.9.0 - gopkg.in/alecthomas/kingpin.v2 v2.2.6 ) require ( - github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/xhit/go-str2duration/v2 v2.1.0 // indirect ) diff --git a/go.sum b/go.sum index 55ef396..bed1ac4 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU= +github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -13,12 +13,12 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From 5e89bfd232d021b042fbda4b3aeef87c9ad76d38 Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Mon, 15 May 2023 20:52:45 +0100 Subject: [PATCH 03/17] README.md: Fix install command text --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ba6068..493ce89 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ This program uses Go. The Go compiler is available from https://golang.org/. To install: - go install github.com/openrdap/rdap/cmd/rdap + go install github.com/openrdap/rdap/cmd/rdap@master This will install the "rdap" binary in your $GOPATH/go/bin directory. Try running: From d51797acc00cbbd288b5b0ce842f992c287e7897 Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Mon, 15 May 2023 21:25:08 +0100 Subject: [PATCH 04/17] Release v0.9.1. The only significant change is the -j/--json flag is implemented. --- cli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli.go b/cli.go index f104c9f..5599283 100644 --- a/cli.go +++ b/cli.go @@ -27,7 +27,7 @@ import ( ) var ( - version = "OpenRDAP v0.0.1" + version = "OpenRDAP v0.9.1" usageText = version + ` (www.openrdap.org) From d0be03e68e211d33b0d3b6aab0d347c668801fc4 Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Tue, 16 May 2023 19:54:46 +0100 Subject: [PATCH 05/17] Add -V/--version flag to print version string. --- cli.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cli.go b/cli.go index 5599283..7336241 100644 --- a/cli.go +++ b/cli.go @@ -44,6 +44,7 @@ Usage: rdap [OPTIONS] DOMAIN|IP|ASN|ENTITY|NAMESERVER|RDAP-URL Options: -h, --help Show help message. + -V, --version Print version and quit. -v, --verbose Print verbose messages on STDERR. -T, --timeout=SECS Timeout after SECS seconds (default: 30). @@ -142,6 +143,7 @@ func RunCLI(args []string, stdout io.Writer, stderr io.Writer, options CLIOption // Command line options. verboseFlag := app.Flag("verbose", "").Short('v').Bool() + versionFlag := app.Flag("version", "").Short('V').Bool() timeoutFlag := app.Flag("timeout", "").Short('T').Default("30").Uint16() insecureFlag := app.Flag("insecure", "").Short('k').Bool() @@ -179,6 +181,12 @@ func RunCLI(args []string, stdout io.Writer, stderr io.Writer, options CLIOption return 1 } + // Print version string? + if *versionFlag { + fmt.Fprintln(stdout, version) + return 0 + } + var verbose func(text string) if *verboseFlag { verbose = func(text string) { From de29e80b738fb8fc95427bd5a08afdd7b0e36bc1 Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Tue, 16 May 2023 19:55:45 +0100 Subject: [PATCH 06/17] go fmt (reformatting affects comments only) --- client.go | 68 +++++++++++++++++++----------------- decode_data.go | 1 - decoder.go | 78 +++++++++++++++++++++-------------------- doc.go | 37 +++++++++++--------- request.go | 94 ++++++++++++++++++++++++++------------------------ vcard.go | 44 ++++++++++++----------- 6 files changed, 167 insertions(+), 155 deletions(-) diff --git a/client.go b/client.go index c3319c6..b9248d1 100644 --- a/client.go +++ b/client.go @@ -20,54 +20,58 @@ import ( // This client executes RDAP requests, and returns the responses as Go values. // // Quick usage: -// client := &rdap.Client{} -// domain, err := client.QueryDomain("example.cz") // -// if err == nil { -// fmt.Printf("Handle=%s Domain=%s\n", domain.Handle, domain.LDHName) -// } +// client := &rdap.Client{} +// domain, err := client.QueryDomain("example.cz") +// +// if err == nil { +// fmt.Printf("Handle=%s Domain=%s\n", domain.Handle, domain.LDHName) +// } +// // The QueryDomain(), QueryAutnum(), and QueryIP() methods all provide full contact information, and timeout after 30s. // // Normal usage: -// // Query example.cz. -// req := &rdap.Request{ -// Type: rdap.DomainRequest, -// Query: "example.cz", -// } // -// client := &rdap.Client{} -// resp, err := client.Do(req) +// // Query example.cz. +// req := &rdap.Request{ +// Type: rdap.DomainRequest, +// Query: "example.cz", +// } // -// if domain, ok := resp.Object.(*rdap.Domain); ok { -// fmt.Printf("Handle=%s Domain=%s\n", domain.Handle, domain.LDHName) -// } +// client := &rdap.Client{} +// resp, err := client.Do(req) +// +// if domain, ok := resp.Object.(*rdap.Domain); ok { +// fmt.Printf("Handle=%s Domain=%s\n", domain.Handle, domain.LDHName) +// } // // Advanced usage: // // This demonstrates custom FetchRoles, a custom Context, a custom HTTP client, // a custom Bootstrapper, and a custom timeout. -// // Nameserver query on rdap.nic.cz. -// server, _ := url.Parse("https://rdap.nic.cz") -// req := &rdap.Request{ -// Type: rdap.NameserverRequest, -// Query: "a.ns.nic.cz", -// FetchRoles: []string{"all"}, -// Timeout: time.Second * 45, // Custom timeout. // -// Server: server, -// } +// // Nameserver query on rdap.nic.cz. +// server, _ := url.Parse("https://rdap.nic.cz") +// req := &rdap.Request{ +// Type: rdap.NameserverRequest, +// Query: "a.ns.nic.cz", +// FetchRoles: []string{"all"}, +// Timeout: time.Second * 45, // Custom timeout. +// +// Server: server, +// } // -// req = req.WithContext(ctx) // Custom context (see https://blog.golang.org/context). +// req = req.WithContext(ctx) // Custom context (see https://blog.golang.org/context). // -// client := &rdap.Client{} -// client.HTTP = &http.Client{} // Custom HTTP client. -// client.Bootstrap = &bootstrap.Client{} // Custom bootstapper. +// client := &rdap.Client{} +// client.HTTP = &http.Client{} // Custom HTTP client. +// client.Bootstrap = &bootstrap.Client{} // Custom bootstapper. // -// resp, err := client.Do(req) +// resp, err := client.Do(req) // -// if ns, ok := resp.Object.(*rdap.Nameserver); ok { -// fmt.Printf("Handle=%s Domain=%s\n", ns.Handle, ns.LDHName) -// } +// if ns, ok := resp.Object.(*rdap.Nameserver); ok { +// fmt.Printf("Handle=%s Domain=%s\n", ns.Handle, ns.LDHName) +// } type Client struct { HTTP *http.Client Bootstrap *bootstrap.Client diff --git a/decode_data.go b/decode_data.go index 2cb1554..9f05a60 100644 --- a/decode_data.go +++ b/decode_data.go @@ -60,7 +60,6 @@ func (r DecodeData) Notes(name string) []string { // // |name| is the RDAP field name (not the Go field name), so "port43", not // "Port43". For a full list of decoded field names, use Fields(). -// func (r DecodeData) Value(name string) interface{} { if v, ok := r.values[name]; ok { return v diff --git a/decoder.go b/decoder.go index 6dba633..826a2da 100644 --- a/decoder.go +++ b/decoder.go @@ -20,36 +20,37 @@ import ( // // To decode an RDAP response: // -// jsonBlob := []byte(` -// { -// "objectClassName": "domain", -// "rdapConformance": ["rdap_level_0"], -// "handle": "EXAMPLECOM", -// "ldhName": "example.com", -// "entities": [] -// } -// `) -// -// d := rdap.NewDecoder(jsonBlob) -// result, err := d.Decode() -// -// if err != nil { -// if domain, ok := result.(*rdap.Domain); ok { -// fmt.Printf("Domain name = %s\n", domain.LDHName) -// } -// } +// jsonBlob := []byte(` +// { +// "objectClassName": "domain", +// "rdapConformance": ["rdap_level_0"], +// "handle": "EXAMPLECOM", +// "ldhName": "example.com", +// "entities": [] +// } +// `) +// +// d := rdap.NewDecoder(jsonBlob) +// result, err := d.Decode() +// +// if err != nil { +// if domain, ok := result.(*rdap.Domain); ok { +// fmt.Printf("Domain name = %s\n", domain.LDHName) +// } +// } // // RDAP responses are decoded into the following types: -// &rdap.Error{} - Responses with an errorCode value. -// &rdap.Autnum{} - Responses with objectClassName="autnum". -// &rdap.Domain{} - Responses with objectClassName="domain". -// &rdap.Entity{} - Responses with objectClassName="entity". -// &rdap.IPNetwork{} - Responses with objectClassName="ip network". -// &rdap.Nameserver{} - Responses with objectClassName="nameserver". -// &rdap.DomainSearchResults{} - Responses with a domainSearchResults array. -// &rdap.EntitySearchResults{} - Responses with a entitySearchResults array. -// &rdap.NameserverSearchResults{} - Responses with a nameserverSearchResults array. -// &rdap.Help{} - All other valid JSON responses. +// +// &rdap.Error{} - Responses with an errorCode value. +// &rdap.Autnum{} - Responses with objectClassName="autnum". +// &rdap.Domain{} - Responses with objectClassName="domain". +// &rdap.Entity{} - Responses with objectClassName="entity". +// &rdap.IPNetwork{} - Responses with objectClassName="ip network". +// &rdap.Nameserver{} - Responses with objectClassName="nameserver". +// &rdap.DomainSearchResults{} - Responses with a domainSearchResults array. +// &rdap.EntitySearchResults{} - Responses with a entitySearchResults array. +// &rdap.NameserverSearchResults{} - Responses with a nameserverSearchResults array. +// &rdap.Help{} - All other valid JSON responses. // // Note that an RDAP server may return a different response type than expected. // @@ -95,16 +96,17 @@ func NewDecoder(jsonBlob []byte, opts ...DecoderOption) *Decoder { // returned. // // The possible results are: -// &rdap.Error{} - Responses with an errorCode value. -// &rdap.Autnum{} - Responses with objectClassName="autnum". -// &rdap.Domain{} - Responses with objectClassName="domain". -// &rdap.Entity{} - Responses with objectClassName="entity". -// &rdap.IPNetwork{} - Responses with objectClassName="ip network". -// &rdap.Nameserver{} - Responses with objectClassName="nameserver". -// &rdap.DomainSearchResults{} - Responses with a domainSearchResults array. -// &rdap.EntitySearchResults{} - Responses with a entitySearchResults array. -// &rdap.NameserverSearchResults{} - Responses with a nameserverSearchResults array. -// &rdap.Help{} - All other valid JSON responses. +// +// &rdap.Error{} - Responses with an errorCode value. +// &rdap.Autnum{} - Responses with objectClassName="autnum". +// &rdap.Domain{} - Responses with objectClassName="domain". +// &rdap.Entity{} - Responses with objectClassName="entity". +// &rdap.IPNetwork{} - Responses with objectClassName="ip network". +// &rdap.Nameserver{} - Responses with objectClassName="nameserver". +// &rdap.DomainSearchResults{} - Responses with a domainSearchResults array. +// &rdap.EntitySearchResults{} - Responses with a entitySearchResults array. +// &rdap.NameserverSearchResults{} - Responses with a nameserverSearchResults array. +// &rdap.Help{} - All other valid JSON responses. // // On serious errors (e.g. JSON syntax error) an error is returned. Otherwise, // decoding is performed on a best-effort basis, and "minor errors" (such as diff --git a/doc.go b/doc.go index bfb07c4..1814c81 100644 --- a/doc.go +++ b/doc.go @@ -9,27 +9,30 @@ // This client executes RDAP queries and returns the responses as Go values. // // Quick usage: -// client := &rdap.Client{} -// domain, err := client.QueryDomain("example.cz") // -// if err == nil { -// fmt.Printf("Handle=%s Domain=%s\n", domain.Handle, domain.LDHName) -// } +// client := &rdap.Client{} +// domain, err := client.QueryDomain("example.cz") +// +// if err == nil { +// fmt.Printf("Handle=%s Domain=%s\n", domain.Handle, domain.LDHName) +// } +// // The QueryDomain(), QueryAutnum(), and QueryIP() methods all provide full contact information, and timeout after 30s. // // Normal usage: -// // Query example.cz. -// req := &rdap.Request{ -// Type: rdap.DomainRequest, -// Query: "example.cz", -// } -// -// client := &rdap.Client{} -// resp, err := client.Do(req) -// -// if domain, ok := resp.Object.(*rdap.Domain); ok { -// fmt.Printf("Handle=%s Domain=%s\n", domain.Handle, domain.LDHName) -// } +// +// // Query example.cz. +// req := &rdap.Request{ +// Type: rdap.DomainRequest, +// Query: "example.cz", +// } +// +// client := &rdap.Client{} +// resp, err := client.Do(req) +// +// if domain, ok := resp.Object.(*rdap.Domain); ok { +// fmt.Printf("Handle=%s Domain=%s\n", domain.Handle, domain.LDHName) +// } // // As of June 2017, all five number registries (AFRINIC, ARIN, APNIC, LANIC, // RIPE) run RDAP servers. A small number of TLDs (top level domains) support diff --git a/request.go b/request.go index 20cd2c7..275467e 100644 --- a/request.go +++ b/request.go @@ -77,31 +77,31 @@ func (r RequestType) String() string { // A Request represents an RDAP request. // -// req := &rdap.Request{ -// Type: rdap.DomainRequest, -// Query: "example.cz", -// } +// req := &rdap.Request{ +// Type: rdap.DomainRequest, +// Query: "example.cz", +// } // // RDAP supports many request types. These are: // -// RequestType | Bootstrapped? | HTTP request path | Example Query -// -------------------------------------------+---------------+-------------------------+---------------- -// rdap.AutnumRequest | Yes | autnum/QUERY | AS2846 -// rdap.DomainRequest | Yes | domain/QUERY | example.cz -// rdap.EntityRequest | Experimental | entity/QUERY | 86860670-VRSN -// rdap.HelpRequest | No | help | N/A -// rdap.IPRequest | Yes | ip/QUERY | 2001:db8::1 -// rdap.NameserverRequest | No | nameserver/QUERY | ns1.skip.org -// | | | -// rdap.DomainSearchRequest | No | domains?name=QUERY | exampl*.com -// rdap.DomainSearchByNameserverRequest | No | domains?nsLdhName=QUERY | ns1.exampl*.com -// rdap.DomainSearchByNameserverIPRequest | No | domains?nsIp=QUERY | 192.0.2.0 -// rdap.NameserverSearchRequest | No | nameservers?name=QUERY | ns1.exampl*.com -// rdap.NameserverSearchByNameserverIPRequest | No | nameservers?ip=QUERY | 192.0.2.0 -// rdap.EntitySearchRequest | No | entities?fn=QUERY | ABC*-VRSN -// rdap.EntitySearchByHandleRequest | No | entities?handle=QUERY | ABC*-VRSN -// | | | -// rdap.RawRequest | N/A | N/A | N/A +// RequestType | Bootstrapped? | HTTP request path | Example Query +// -------------------------------------------+---------------+-------------------------+---------------- +// rdap.AutnumRequest | Yes | autnum/QUERY | AS2846 +// rdap.DomainRequest | Yes | domain/QUERY | example.cz +// rdap.EntityRequest | Experimental | entity/QUERY | 86860670-VRSN +// rdap.HelpRequest | No | help | N/A +// rdap.IPRequest | Yes | ip/QUERY | 2001:db8::1 +// rdap.NameserverRequest | No | nameserver/QUERY | ns1.skip.org +// | | | +// rdap.DomainSearchRequest | No | domains?name=QUERY | exampl*.com +// rdap.DomainSearchByNameserverRequest | No | domains?nsLdhName=QUERY | ns1.exampl*.com +// rdap.DomainSearchByNameserverIPRequest | No | domains?nsIp=QUERY | 192.0.2.0 +// rdap.NameserverSearchRequest | No | nameservers?name=QUERY | ns1.exampl*.com +// rdap.NameserverSearchByNameserverIPRequest | No | nameservers?ip=QUERY | 192.0.2.0 +// rdap.EntitySearchRequest | No | entities?fn=QUERY | ABC*-VRSN +// rdap.EntitySearchByHandleRequest | No | entities?handle=QUERY | ABC*-VRSN +// | | | +// rdap.RawRequest | N/A | N/A | N/A // // See https://tools.ietf.org/html/rfc7482 for more information on RDAP request // types. @@ -112,21 +112,22 @@ func (r RequestType) String() string { // // For other Request types, you must specify the RDAP server: // -// // Nameserver query on rdap.nic.cz. -// server, _ := url.Parse("https://rdap.nic.cz") -// req := &rdap.Request{ -// Type: rdap.NameserverRequest, -// Query: "a.ns.nic.cz", +// // Nameserver query on rdap.nic.cz. +// server, _ := url.Parse("https://rdap.nic.cz") +// req := &rdap.Request{ +// Type: rdap.NameserverRequest, +// Query: "a.ns.nic.cz", // -// Server: server, -// } +// Server: server, +// } // // RawRequest is a special case for existing RDAP request URLs: -// rdapURL, _ := url.Parse("https://rdap.example/mystery/query?ip=192.0.2.0") -// req := &rdap.Request{ -// Type: rdap.RawRequest, -// Server: rdapURL, -// } +// +// rdapURL, _ := url.Parse("https://rdap.example/mystery/query?ip=192.0.2.0") +// req := &rdap.Request{ +// Type: rdap.RawRequest, +// Server: rdapURL, +// } type Request struct { // Request type. Type RequestType @@ -227,15 +228,16 @@ func (r *Request) pathAndValues() (string, url.Values) { // URL constructs and returns the RDAP Request URL. // // As an example: -// server, _ := url.Parse("https://rdap.nic.cz") -// req := &rdap.Request{ -// Type: rdap.NameserverRequest, -// Query: "a.ns.nic.cz", // -// Server: server, -// } +// server, _ := url.Parse("https://rdap.nic.cz") +// req := &rdap.Request{ +// Type: rdap.NameserverRequest, +// Query: "a.ns.nic.cz", +// +// Server: server, +// } // -// fmt.Println(req.URL()) // Prints https://rdap.nic.cz/nameserver/a.ns.nic.cz. +// fmt.Println(req.URL()) // Prints https://rdap.nic.cz/nameserver/a.ns.nic.cz. // // Returns nil if the Server field is nil. // @@ -431,11 +433,11 @@ func NewRequest(requestType RequestType, query string) *Request { // NewAutoRequest creates a Request by guessing the type required for |queryText|. // // The following types are suppported: -// - RawRequest - e.g. https://example.com/domain/example2.com -// - DomainRequest - e.g. example.com, https://example.com, http://example.com/ -// - IPRequest - e.g. 192.0.2.0, 2001:db8::, 192.0.2.0/24, 2001:db8::/128 -// - AutnumRequest - e.g. AS2856, 5400 -// - EntityRequest - all other queries. +// - RawRequest - e.g. https://example.com/domain/example2.com +// - DomainRequest - e.g. example.com, https://example.com, http://example.com/ +// - IPRequest - e.g. 192.0.2.0, 2001:db8::, 192.0.2.0/24, 2001:db8::/128 +// - AutnumRequest - e.g. AS2856, 5400 +// - EntityRequest - all other queries. // // Returns a Request. Use r.Type to find the RequestType chosen. func NewAutoRequest(queryText string) *Request { diff --git a/vcard.go b/vcard.go index 78a80e2..4d22214 100644 --- a/vcard.go +++ b/vcard.go @@ -24,19 +24,20 @@ import ( // telephone numbers. RFC6350 documents a set of standard properties. // // RFC7095 describes the JSON document format, which looks like: -// ["vcard", [ -// [ -// ["version", {}, "text", "4.0"], -// ["fn", {}, "text", "Joe Appleseed"], -// ["tel", { -// "type":["work", "voice"], -// }, -// "uri", -// "tel:+1-555-555-1234;ext=555" -// ], -// ... -// ] -// ] +// +// ["vcard", [ +// [ +// ["version", {}, "text", "4.0"], +// ["fn", {}, "text", "Joe Appleseed"], +// ["tel", { +// "type":["work", "voice"], +// }, +// "uri", +// "tel:+1-555-555-1234;ext=555" +// ], +// ... +// ] +// ] type VCard struct { Properties []*VCardProperty } @@ -44,9 +45,10 @@ type VCard struct { // VCardProperty represents a single vCard property. // // Each vCard property has four fields, these are: -// Name Parameters Type Value -// ----- -------------------------- ----- ----------------------------- -// ["tel", {"type":["work", "voice"]}, "uri", "tel:+1-555-555-1234;ext=555"] +// +// Name Parameters Type Value +// ----- -------------------------- ----- ----------------------------- +// ["tel", {"type":["work", "voice"]}, "uri", "tel:+1-555-555-1234;ext=555"] type VCardProperty struct { Name string @@ -107,10 +109,10 @@ func (p *VCardProperty) appendValueStrings(v interface{}, strings *[]string) { // String returns the vCard as a multiline human readable string. For example: // -// vCard[ -// version (type=text, parameters=map[]): [4.0] -// mixed (type=text, parameters=map[]): [abc true 42 [def false 43]] -// ] +// vCard[ +// version (type=text, parameters=map[]): [4.0] +// mixed (type=text, parameters=map[]): [abc true 42 [def false 43]] +// ] // // This is intended for debugging only, and is not machine parsable. func (v *VCard) String() string { @@ -125,7 +127,7 @@ func (v *VCard) String() string { // String returns the VCardProperty as a human readable string. For example: // -// mixed (type=text, parameters=map[]): [abc true 42 [def false 43]] +// mixed (type=text, parameters=map[]): [abc true 42 [def false 43]] // // This is intended for debugging only, and is not machine parsable. func (p *VCardProperty) String() string { From ab90a9585039ae7c2fdd77a08be8fb0a985fa06d Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Tue, 16 May 2023 21:03:39 +0100 Subject: [PATCH 07/17] Enable RFC8521 (Registration Data Access Protocol (RDAP) Object Tagging) support by default. Run "rdap -v OPS4-RIPE" to see an entity query on RIPE's RDAP server. Object tags allow entity strings like OPS4-RIPE to be directed to the correct RDAP server using the -SUFFIX. There is a registry mapping suffixes to RDAP servers at https://data.iana.org/rdap/object-tags.json. --- bootstrap/client.go | 5 +- bootstrap/client_test.go | 15 +---- bootstrap/file.go | 17 +++-- bootstrap/service_provider_registry.go | 8 +-- bootstrap/service_provider_registry_test.go | 58 +++++------------ cli.go | 13 +--- client.go | 7 ++- test/http.go | 5 +- test/testdata/bootstrap/object-tags.json | 63 +++++++++++++++++++ .../service_provider.json | 14 ----- 10 files changed, 105 insertions(+), 100 deletions(-) create mode 100644 test/testdata/bootstrap/object-tags.json delete mode 100644 test/testdata/bootstrap_experimental/service_provider.json diff --git a/bootstrap/client.go b/bootstrap/client.go index a1955a0..4daadbe 100644 --- a/bootstrap/client.go +++ b/bootstrap/client.go @@ -430,7 +430,7 @@ func (c *Client) filenameFor(r RegistryType) string { return filename } -// Filename returns the JSON document filename: One of {asn,dns,ipv4,ipv6,service_provider}.json. +// Filename returns the JSON document filename: One of {asn,dns,ipv4,ipv6,object-tags}.json. func (r RegistryType) Filename() string { switch r { case ASN: @@ -442,8 +442,7 @@ func (r RegistryType) Filename() string { case IPv6: return "ipv6.json" case ServiceProvider: - // This is a guess and will need fixing to match whatever IANA chooses. - return "serviceprovider-draft-03.json" + return "object-tags.json" default: panic("Unknown RegistryType") } diff --git a/bootstrap/client_test.go b/bootstrap/client_test.go index e4983e9..b767ce6 100644 --- a/bootstrap/client_test.go +++ b/bootstrap/client_test.go @@ -5,7 +5,6 @@ package bootstrap import ( - "net/url" "testing" "github.com/openrdap/rdap/test" @@ -66,15 +65,9 @@ func TestLookups(t *testing.T) { }, { ServiceProvider, - "12345~VRSN", + "12345-FRNIC", true, - []string{"https://rdap.verisignlabs.com/rdap/v1"}, - }, - { - ServiceProvider, - "12345-VRSN", - true, - []string{"https://rdap.verisignlabs.com/rdap/v1"}, + []string{"https://rdap.nic.fr/"}, }, } @@ -87,10 +80,6 @@ func TestLookups(t *testing.T) { for _, test := range tests { var r *Answer - if test.Registry == ServiceProvider { - c.BaseURL, _ = url.Parse("https://test.rdap.net/rdap/") - } - question := &Question{ RegistryType: test.Registry, Query: test.Input, diff --git a/bootstrap/file.go b/bootstrap/file.go index d6e05c4..5b20a1d 100644 --- a/bootstrap/file.go +++ b/bootstrap/file.go @@ -52,13 +52,22 @@ func NewFile(jsonDocument []byte) (*File, error) { f.Entries = make(map[string][]*url.URL) for _, s := range doc.Services { - if len(s) != 2 { + var entries []string + var rawURLs []string + + switch len(s) { + case 2: + // {asn,dns,ipv4,ipv6}.json + entries = s[0] + rawURLs = s[1] + case 3: + // object-tags.json + entries = s[1] + rawURLs = s[2] + default: return nil, errors.New("Malformed bootstrap (bad services array)") } - entries := s[0] - rawURLs := s[1] - var urls []*url.URL for _, rawURL := range rawURLs { diff --git a/bootstrap/service_provider_registry.go b/bootstrap/service_provider_registry.go index 89f3b01..74077dc 100644 --- a/bootstrap/service_provider_registry.go +++ b/bootstrap/service_provider_registry.go @@ -22,7 +22,7 @@ type ServiceProviderRegistry struct { // Provider JSON document. // // The document format is specified in -// https://datatracker.ietf.org/doc/draft-hollenbeck-regext-rdap-object-tag/. +// https://datatracker.ietf.org/doc/rfc8521/. func NewServiceProviderRegistry(json []byte) (*ServiceProviderRegistry, error) { var r *File r, err := NewFile(json) @@ -50,11 +50,7 @@ func (s *ServiceProviderRegistry) Lookup(question *Question) (*Answer, error) { input := question.Query // Valid input looks like 12345-VRSN. - offset := strings.LastIndexByte(input, '~') - - if offset == -1 { - offset = strings.LastIndexByte(input, '-') - } + offset := strings.LastIndexByte(input, '-') if offset == -1 || offset == len(input)-1 { return &Answer{ diff --git a/bootstrap/service_provider_registry_test.go b/bootstrap/service_provider_registry_test.go index ca32261..a3ddf9a 100644 --- a/bootstrap/service_provider_registry_test.go +++ b/bootstrap/service_provider_registry_test.go @@ -11,10 +11,10 @@ import ( ) func TestServiceProviderRegistryLookups(t *testing.T) { - test.Start(test.BootstrapExperimental) + test.Start(test.Bootstrap) defer test.Finish() - var bytes []byte = test.Get("https://test.rdap.net/rdap/serviceprovider-draft-03.json") + var bytes []byte = test.Get("https://data.iana.org/rdap/object-tags.json") var s *ServiceProviderRegistry s, err := NewServiceProviderRegistry(bytes) @@ -31,58 +31,28 @@ func TestServiceProviderRegistryLookups(t *testing.T) { []string{}, }, { - "~", + "12345-FRNIC", false, - "", - []string{}, - }, - { - "X~VRSN~", - false, - "", - []string{}, - }, - { - "12345~VRSN", - false, - "VRSN", - []string{"https://rdap.verisignlabs.com/rdap/v1"}, - }, - { - "*~VRSN", - false, - "VRSN", - []string{"https://rdap.verisignlabs.com/rdap/v1"}, - }, - { - "~VRSN", - false, - "VRSN", - []string{"https://rdap.verisignlabs.com/rdap/v1"}, - }, - { - "12345-VRSN", - false, - "VRSN", - []string{"https://rdap.verisignlabs.com/rdap/v1"}, + "FRNIC", + []string{"https://rdap.nic.fr/"}, }, { - "*-VRSN", + "*-FRNIC", false, - "VRSN", - []string{"https://rdap.verisignlabs.com/rdap/v1"}, + "FRNIC", + []string{"https://rdap.nic.fr/"}, }, { - "-VRSN", + "-FRNIC", false, - "VRSN", - []string{"https://rdap.verisignlabs.com/rdap/v1"}, + "FRNIC", + []string{"https://rdap.nic.fr/"}, }, { - "A-B-VRSN", + "A-B-FRNIC", false, - "VRSN", - []string{"https://rdap.verisignlabs.com/rdap/v1"}, + "FRNIC", + []string{"https://rdap.nic.fr/"}, }, } diff --git a/cli.go b/cli.go index 7336241..527313e 100644 --- a/cli.go +++ b/cli.go @@ -93,11 +93,6 @@ Advanced options (bootstrapping): automatically as needed. (default: $HOME/.openrdap). --bs-url=URL Bootstrap service URL (default: https://data.iana.org/rdap) --bs-ttl=SECS Bootstrap cache time in seconds (default: 3600) - -Advanced options (experiments): - --exp=test_rdap_net Use the bootstrap service https://test.rdap.net/rdap - --exp=object_tag Enable object tag support - (draft-hollenbeck-regext-rdap-object-tag) ` ) @@ -222,9 +217,8 @@ func RunCLI(args []string, stdout io.Writer, stderr io.Writer, options CLIOption // Enable the -e selection of experiments? if *experimentalFlag { - verbose("rdap: Enabled -e/--experiments: test_rdap_net, object_tag") + verbose("rdap: Enabled -e/--experiments: test_rdap_net") experiments["test_rdap_net"] = true - experiments["object_tag"] = true } // Forced sandbox mode? @@ -484,9 +478,8 @@ func RunCLI(args []string, stdout io.Writer, stderr io.Writer, options CLIOption HTTP: httpClient, Bootstrap: bs, - Verbose: verbose, - UserAgent: version, - ServiceProviderExperiment: experiments["object_tag"], + Verbose: verbose, + UserAgent: version, } if *insecureFlag { diff --git a/client.go b/client.go index b9248d1..5b45cf3 100644 --- a/client.go +++ b/client.go @@ -79,8 +79,11 @@ type Client struct { // Optional callback function for verbose messages. Verbose func(text string) + UserAgent string + + // Service Provider support is now always enabled. + // This field is ignored. ServiceProviderExperiment bool - UserAgent string } func (c *Client) Do(req *Request) (*Response, error) { @@ -127,7 +130,7 @@ func (c *Client) Do(req *Request) (*Response, error) { var bootstrapType *bootstrap.RegistryType = bootstrapTypeFor(req) - if bootstrapType == nil || (*bootstrapType == bootstrap.ServiceProvider && !c.ServiceProviderExperiment) { + if bootstrapType == nil { return nil, &ClientError{ Type: BootstrapNotSupported, Text: fmt.Sprintf("Cannot run query type '%s' without a server URL, "+ diff --git a/test/http.go b/test/http.go index dd9a91f..c8ca93b 100644 --- a/test/http.go +++ b/test/http.go @@ -82,10 +82,7 @@ func loadTestDatasets() { load(Bootstrap, 200, "https://data.iana.org/rdap/dns.json", "bootstrap/dns.json") load(Bootstrap, 200, "https://data.iana.org/rdap/ipv4.json", "bootstrap/ipv4.json") load(Bootstrap, 200, "https://data.iana.org/rdap/ipv6.json", "bootstrap/ipv6.json") - - // Experimental bootstrap file for service providers. - // https://datatracker.ietf.org/doc/draft-hollenbeck-regext-rdap-object-tag/ . - load(BootstrapExperimental, 200, "https://test.rdap.net/rdap/serviceprovider-draft-03.json", "bootstrap_experimental/service_provider.json") + load(Bootstrap, 200, "https://data.iana.org/rdap/object-tags.json", "bootstrap/object-tags.json") // Malformed bootstrap files. load(BootstrapMalformed, 200, "https://www.example.org/dns_bad_services.json", "bootstrap_malformed/dns_bad_services.json") diff --git a/test/testdata/bootstrap/object-tags.json b/test/testdata/bootstrap/object-tags.json new file mode 100644 index 0000000..8024d76 --- /dev/null +++ b/test/testdata/bootstrap/object-tags.json @@ -0,0 +1,63 @@ +{ + "description": "RDAP bootstrap file for service provider object tags", + "publication": "2022-12-29T04:00:02Z", + "services": [ + [ + [ + "info@arin.net" + ], + [ + "ARIN" + ], + [ + "https://rdap.arin.net/registry/", + "http://rdap.arin.net/registry/" + ] + ], + [ + [ + "carlos@lacnic.net" + ], + [ + "LACNIC" + ], + [ + "https://rdap.lacnic.net/rdap/" + ] + ], + [ + [ + "bje@apnic.net" + ], + [ + "APNIC" + ], + [ + "https://rdap.apnic.net/" + ] + ], + [ + [ + "kranjbar@ripe.net" + ], + [ + "RIPE" + ], + [ + "https://rdap.db.ripe.net/" + ] + ], + [ + [ + "tld-tech@nic.fr" + ], + [ + "FRNIC" + ], + [ + "https://rdap.nic.fr/" + ] + ] + ], + "version": "1.0" +} \ No newline at end of file diff --git a/test/testdata/bootstrap_experimental/service_provider.json b/test/testdata/bootstrap_experimental/service_provider.json deleted file mode 100644 index c16177b..0000000 --- a/test/testdata/bootstrap_experimental/service_provider.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": "1.0", - "publication": "2017-04-26T00:00:00Z", - "description": "RDAP service provider bootstrap values (experimental, hosted on openrdap.org)", - "services": [ - [ - ["VRSN"], - [ - "https://rdap.verisignlabs.com/rdap/v1" - ] - ] - ] -} - From cdec80a3785cd9fb3c86cc1cb496db133df132af Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Tue, 16 May 2023 21:39:46 +0100 Subject: [PATCH 08/17] Cleanup/update documentation --- README.md | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- cli.go | 21 +++--- 2 files changed, 198 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 493ce89..2798589 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ https://www.openrdap.org/demo - live demo ## Features * Command line RDAP client +* Output formats: text, JSON, WHOIS style * Query types supported: * ip * domain @@ -24,11 +25,10 @@ https://www.openrdap.org/demo - live demo * nameserver-search-by-ip * entity-search * entity-search-by-handle -* Query bootstrapping (automatic RDAP server URL detection for ip/domain/autnum/(experimental) entity queries) +* Automatic server detection for ip/domain/autnum/entities +* Object tags support * Bootstrap cache (optional, uses ~/.openrdap by default) * X.509 client authentication -* Output formats: text, JSON, WHOIS style -* Experimental [object tagging](https://datatracker.ietf.org/doc/draft-ietf-regext-rdap-object-tag/) support ## Installation @@ -45,10 +45,16 @@ This will install the "rdap" binary in your $GOPATH/go/bin directory. Try runnin ## Usage | Query type | Usage | -| --- | --- | +| ------------------------- | ------------------------------------------------------------------------ | | Domain (.com) | rdap -v example.com | -| Network | rdap -v 2001:db8:: | -| Autnum | rdap -v AS15169 | +| IPv4 Address | rdap -v 192.0.2.0 | +| IPv6 Address | rdap -v 2001:db8:: | +| Autonomous System (ASN) | rdap -v AS15169 | +| Entity (with object tag) | rdap -v OPS4-RIPE | + +## Advanced Usage (server must be specified using -s; not all servers support all query types) +| Query type | Usage | +| ------------------------- | ------------------------------------------------------------------------ | | Nameserver | rdap -v -t nameserver -s https://rdap.verisign.com/com/v1 ns1.google.com | | Help | rdap -v -t help -s https://rdap.verisign.com/com/v1 | | Domain Search | rdap -v -t domain-search -s $SERVER_URL example*.gtld | @@ -61,6 +67,182 @@ This will install the "rdap" binary in your $GOPATH/go/bin directory. Try runnin See https://www.openrdap.org/docs. +## Example output + +Click the examples to see the output: + +
+rdap example.com +``` +rdap example.com +Domain: + Domain Name: EXAMPLE.COM + Handle: 2336799_DOMAIN_COM-VRSN + Status: client delete prohibited + Status: client transfer prohibited + Status: client update prohibited + Conformance: rdap_level_0 + Conformance: icann_rdap_technical_implementation_guide_0 + Conformance: icann_rdap_response_profile_0 + Notice: + Title: Terms of Use + Description: Service subject to Terms of Use. + Link: https://www.verisign.com/domain-names/registration-data-access-protocol/terms-service/index.xhtml + Notice: + Title: Status Codes + Description: For more information on domain status codes, please visit https://icann.org/epp + Link: https://icann.org/epp + Notice: + Title: RDDS Inaccuracy Complaint Form + Description: URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf + Link: https://icann.org/wicf + Link: https://rdap.verisign.com/com/v1/domain/EXAMPLE.COM + Event: + Action: registration + Date: 1995-08-14T04:00:00Z + Event: + Action: expiration + Date: 2023-08-13T04:00:00Z + Event: + Action: last changed + Date: 2023-05-12T15:13:35Z + Event: + Action: last update of RDAP database + Date: 2023-05-16T20:36:06Z + Secure DNS: + Delegation Signed: true + DSData: + Key Tag: 370 + Algorithm: 13 + Digest: BE74359954660069D5C63D200C39F5603827D7DD02B56F120EE9F3A86764247C + DigestType: 2 + Entity: + Handle: 376 + Public ID: + Type: IANA Registrar ID + Identifier: 376 + Role: registrar + vCard version: 4.0 + vCard fn: RESERVED-Internet Assigned Numbers Authority + Entity: + Role: abuse + vCard version: 4.0 + Nameserver: + Nameserver: A.IANA-SERVERS.NET + Nameserver: + Nameserver: B.IANA-SERVERS.NET +``` +
+ +
+rdap 8.8.8.8 +``` +$ rdap 8.8.8.8 +IP Network: + Handle: NET-8-8-8-0-1 + Start Address: 8.8.8.0 + End Address: 8.8.8.255 + IP Version: v4 + Name: LVLT-GOGL-8-8-8 + Type: ALLOCATION + ParentHandle: NET-8-0-0-0-1 + Status: active + Port43: whois.arin.net + Notice: + Title: Terms of Service + Description: By using the ARIN RDAP/Whois service, you are agreeing to the RDAP/Whois Terms of Use + Link: https://www.arin.net/resources/registry/whois/tou/ + Notice: + Title: Whois Inaccuracy Reporting + Description: If you see inaccuracies in the results, please visit: + Link: https://www.arin.net/resources/registry/whois/inaccuracy_reporting/ + Notice: + Title: Copyright Notice + Description: Copyright 1997-2023, American Registry for Internet Numbers, Ltd. + Entity: + Handle: GOGL + Port43: whois.arin.net + Remark: + Title: Registration Comments + Description: Please note that the recommended way to file abuse complaints are located in the following links. + Description: To report abuse and illegal activity: https://www.google.com/contact/ + Description: For legal requests: http://support.google.com/legal + Description: Regards, + Description: The Google Team + Link: https://rdap.arin.net/registry/entity/GOGL + Link: https://whois.arin.net/rest/org/GOGL + Event: + Action: last changed + Date: 2019-10-31T15:45:45-04:00 + Event: + Action: registration + Date: 2000-03-30T00:00:00-05:00 + Role: registrant + vCard version: 4.0 + vCard fn: Google LLC + vCard kind: org + Entity: + Handle: ABUSE5250-ARIN + Status: validated + Port43: whois.arin.net + Remark: + Title: Registration Comments + Description: Please note that the recommended way to file abuse complaints are located in the following links. + Description: To report abuse and illegal activity: https://www.google.com/contact/ + Description: For legal requests: http://support.google.com/legal + Description: Regards, + Description: The Google Team + Link: https://rdap.arin.net/registry/entity/ABUSE5250-ARIN + Link: https://whois.arin.net/rest/poc/ABUSE5250-ARIN + Event: + Action: last changed + Date: 2022-10-24T08:43:11-04:00 + Event: + Action: registration + Date: 2015-11-06T15:36:35-05:00 + Role: abuse + vCard version: 4.0 + vCard fn: Abuse + vCard org: Abuse + vCard kind: group + vCard email: network-abuse@google.com + vCard tel: +1-650-253-0000 + Entity: + Handle: ZG39-ARIN + Status: validated + Port43: whois.arin.net + Link: https://rdap.arin.net/registry/entity/ZG39-ARIN + Link: https://whois.arin.net/rest/poc/ZG39-ARIN + Event: + Action: last changed + Date: 2022-11-10T07:12:44-05:00 + Event: + Action: registration + Date: 2000-11-30T13:54:08-05:00 + Role: technical + Role: administrative + vCard version: 4.0 + vCard fn: Google LLC + vCard org: Google LLC + vCard kind: group + vCard email: arin-contact@google.com + vCard tel: +1-650-253-0000 + Link: https://rdap.arin.net/registry/ip/8.8.8.0 + Link: https://whois.arin.net/rest/net/NET-8-8-8-0-1 + Link: https://rdap.arin.net/registry/ip/8.0.0.0/9 + Event: + Action: last changed + Date: 2014-03-14T16:52:05-04:00 + Event: + Action: registration + Date: 2014-03-14T16:52:05-04:00 + cidr0_cidrs: + v4prefix: 8.8.8.0 + length: 24 +``` +
+ + ## Go docs [![godoc](https://godoc.org/github.com/openrdap/rdap?status.png)](https://godoc.org/github.com/openrdap/rdap) @@ -74,10 +256,10 @@ Go 1.20+ - [OpenRDAP](https://www.openrdap.org) - https://data.iana.org/rdap/ - Official IANA bootstrap information -- https://test.rdap.net/rdap/ - Test alternate bootstrap service with more experimental RDAP servers - [RFC 7480 HTTP Usage in the Registration Data Access Protocol (RDAP)](https://tools.ietf.org/html/rfc7480) - [RFC 7481 Security Services for the Registration Data Access Protocol (RDAP)](https://tools.ietf.org/html/rfc7481) - [RFC 7482 Registration Data Access Protocol (RDAP) Query Format](https://tools.ietf.org/html/rfc7482) - [RFC 7483 JSON Responses for the Registration Data Access Protocol (RDAP)](https://tools.ietf.org/html/rfc7483) - [RFC 7484 Finding the Authoritative Registration Data (RDAP) Service](https://tools.ietf.org/html/rfc7484) +- [RFC 8521 Registration Data Access Protocol (RDAP) Object Tagging] (https://datatracker.ietf.org/doc/rfc8521/) diff --git a/cli.go b/cli.go index 527313e..c5df888 100644 --- a/cli.go +++ b/cli.go @@ -32,13 +32,13 @@ var ( (www.openrdap.org) Usage: rdap [OPTIONS] DOMAIN|IP|ASN|ENTITY|NAMESERVER|RDAP-URL - e.g. rdap example.cz + e.g. rdap example.com rdap 192.0.2.0 rdap 2001:db8:: rdap AS2856 + rdap OPS4-RIPE rdap https://rdap.nic.cz/domain/example.cz - rdap -f registrant -f administrative -f billing amazon.com.br rdap --json https://rdap.nic.cz/domain/example.cz rdap -s https://rdap.nic.cz -t help @@ -50,16 +50,6 @@ Options: -T, --timeout=SECS Timeout after SECS seconds (default: 30). -k, --insecure Disable SSL certificate verification. - -e, --experimental Enable some experimental options: - - Use the bootstrap service https://test.rdap.net/rdap - - Enable object tag support - -Authentication options: - -P, --p12=cert.p12[:password] Use client certificate & private key (PKCS#12 format) -or: - -C, --cert=cert.pem Use client certificate (PEM format) - -K, --key=cert.key Use client private key (PEM format) - Output Options: --text Output RDAP, plain text "tree" format (default). -w, --whois Output WHOIS style (domain queries only). @@ -93,6 +83,13 @@ Advanced options (bootstrapping): automatically as needed. (default: $HOME/.openrdap). --bs-url=URL Bootstrap service URL (default: https://data.iana.org/rdap) --bs-ttl=SECS Bootstrap cache time in seconds (default: 3600) + +Advanced options (authentication): + -P, --p12=cert.p12[:password] Use client certificate & private key (PKCS#12 format) +or: + -C, --cert=cert.pem Use client certificate (PEM format) + -K, --key=cert.key Use client private key (PEM format) + ` ) From de999c6b3c18ed38157e66dc1de77b8bd04d9720 Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Tue, 16 May 2023 21:46:57 +0100 Subject: [PATCH 09/17] Add example output to README.md --- README.md | 494 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 487 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2798589..eb58cfe 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ This will install the "rdap" binary in your $GOPATH/go/bin directory. Try runnin | Autonomous System (ASN) | rdap -v AS15169 | | Entity (with object tag) | rdap -v OPS4-RIPE | -## Advanced Usage (server must be specified using -s; not all servers support all query types) +## Advanced usage (server must be specified using -s; not all servers support all query types) | Query type | Usage | | ------------------------- | ------------------------------------------------------------------------ | | Nameserver | rdap -v -t nameserver -s https://rdap.verisign.com/com/v1 ns1.google.com | @@ -73,9 +73,8 @@ Click the examples to see the output:
rdap example.com -``` -rdap example.com -Domain: + +```Domain: Domain Name: EXAMPLE.COM Handle: 2336799_DOMAIN_COM-VRSN Status: client delete prohibited @@ -132,13 +131,13 @@ Domain: Nameserver: Nameserver: B.IANA-SERVERS.NET ``` +
rdap 8.8.8.8 -``` -$ rdap 8.8.8.8 -IP Network: + +```IP Network: Handle: NET-8-8-8-0-1 Start Address: 8.8.8.0 End Address: 8.8.8.255 @@ -240,8 +239,489 @@ IP Network: v4prefix: 8.8.8.0 length: 24 ``` +
+
+rdap --json AS15169 + +``` +{ + "rdapConformance": [ + "nro_rdap_profile_0", + "rdap_level_0", + "nro_rdap_profile_asn_flat_0" + ], + "notices": [ + { + "title": "Terms of Service", + "description": [ + "By using the ARIN RDAP/Whois service, you are agreeing to the RDAP/Whois Terms of Use" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "terms-of-service", + "type": "text/html", + "href": "https://www.arin.net/resources/registry/whois/tou/" + } + ] + }, + { + "title": "Whois Inaccuracy Reporting", + "description": [ + "If you see inaccuracies in the results, please visit: " + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "inaccuracy-report", + "type": "text/html", + "href": "https://www.arin.net/resources/registry/whois/inaccuracy_reporting/" + } + ] + }, + { + "title": "Copyright Notice", + "description": [ + "Copyright 1997-2023, American Registry for Internet Numbers, Ltd." + ] + } + ], + "handle": "AS15169", + "startAutnum": 15169, + "endAutnum": 15169, + "name": "GOOGLE", + "events": [ + { + "eventAction": "last changed", + "eventDate": "2012-02-24T09:44:34-05:00" + }, + { + "eventAction": "registration", + "eventDate": "2000-03-30T00:00:00-05:00" + } + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/autnum/15169" + }, + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/asn/AS15169" + } + ], + "entities": [ + { + "handle": "GOGL", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "fn", + {}, + "text", + "Google LLC" + ], + [ + "adr", + { + "label": "1600 Amphitheatre Parkway\nMountain View\nCA\n94043\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "kind", + {}, + "text", + "org" + ] + ] + ], + "roles": [ + "registrant" + ], + "remarks": [ + { + "title": "Registration Comments", + "description": [ + "Please note that the recommended way to file abuse complaints are located in the following links. ", + "", + "To report abuse and illegal activity: https://www.google.com/contact/", + "", + "For legal requests: http://support.google.com/legal ", + "", + "Regards, ", + "The Google Team" + ] + } + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/GOGL" + }, + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/org/GOGL" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2019-10-31T15:45:45-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2000-03-30T00:00:00-05:00" + } + ], + "entities": [ + { + "handle": "ABUSE5250-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "1600 Amphitheatre Parkway\nMountain View\nCA\n94043\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "Abuse" + ], + [ + "org", + {}, + "text", + "Abuse" + ], + [ + "kind", + {}, + "text", + "group" + ], + [ + "email", + {}, + "text", + "network-abuse@google.com" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-650-253-0000" + ] + ] + ], + "roles": [ + "abuse" + ], + "remarks": [ + { + "title": "Registration Comments", + "description": [ + "Please note that the recommended way to file abuse complaints are located in the following links.", + "", + "To report abuse and illegal activity: https://www.google.com/contact/", + "", + "For legal requests: http://support.google.com/legal ", + "", + "Regards,", + "The Google Team" + ] + } + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/ABUSE5250-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/ABUSE5250-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2022-10-24T08:43:11-04:00" + }, + { + "eventAction": "registration", + "eventDate": "2015-11-06T15:36:35-05:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity" + }, + { + "handle": "ZG39-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "1600 Amphitheatre Parkway\nMountain View\nCA\n94043\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "Google LLC" + ], + [ + "org", + {}, + "text", + "Google LLC" + ], + [ + "kind", + {}, + "text", + "group" + ], + [ + "email", + {}, + "text", + "arin-contact@google.com" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-650-253-0000" + ] + ] + ], + "roles": [ + "technical", + "administrative" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/ZG39-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/ZG39-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2022-11-10T07:12:44-05:00" + }, + { + "eventAction": "registration", + "eventDate": "2000-11-30T13:54:08-05:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity" + } + ], + "port43": "whois.arin.net", + "objectClassName": "entity" + }, + { + "handle": "ZG39-ARIN", + "vcardArray": [ + "vcard", + [ + [ + "version", + {}, + "text", + "4.0" + ], + [ + "adr", + { + "label": "1600 Amphitheatre Parkway\nMountain View\nCA\n94043\nUnited States" + }, + "text", + [ + "", + "", + "", + "", + "", + "", + "" + ] + ], + [ + "fn", + {}, + "text", + "Google LLC" + ], + [ + "org", + {}, + "text", + "Google LLC" + ], + [ + "kind", + {}, + "text", + "group" + ], + [ + "email", + {}, + "text", + "arin-contact@google.com" + ], + [ + "tel", + { + "type": [ + "work", + "voice" + ] + }, + "text", + "+1-650-253-0000" + ] + ] + ], + "roles": [ + "technical" + ], + "links": [ + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "self", + "type": "application/rdap+json", + "href": "https://rdap.arin.net/registry/entity/ZG39-ARIN" + }, + { + "value": "https://rdap.arin.net/registry/autnum/15169", + "rel": "alternate", + "type": "application/xml", + "href": "https://whois.arin.net/rest/poc/ZG39-ARIN" + } + ], + "events": [ + { + "eventAction": "last changed", + "eventDate": "2022-11-10T07:12:44-05:00" + }, + { + "eventAction": "registration", + "eventDate": "2000-11-30T13:54:08-05:00" + } + ], + "status": [ + "validated" + ], + "port43": "whois.arin.net", + "objectClassName": "entity" + } + ], + "port43": "whois.arin.net", + "status": [ + "active" + ], + "objectClassName": "autnum" +} +``` + +
## Go docs [![godoc](https://godoc.org/github.com/openrdap/rdap?status.png)](https://godoc.org/github.com/openrdap/rdap) From eca9110348584be257268d383c1bef959f8b4c4d Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Tue, 16 May 2023 21:49:28 +0100 Subject: [PATCH 10/17] Tidy README.md further --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eb58cfe..b8d1889 100644 --- a/README.md +++ b/README.md @@ -731,7 +731,7 @@ Go 1.20+ ## Links - Wikipedia - [Registration Data Access Protocol](https://en.wikipedia.org/wiki/Registration_Data_Access_Protocol) -- [ICANN RDAP pilot](https://www.icann.org/rdap) +- ICANN - [RDAP](https://www.icann.org/rdap) - [OpenRDAP](https://www.openrdap.org) @@ -742,4 +742,4 @@ Go 1.20+ - [RFC 7482 Registration Data Access Protocol (RDAP) Query Format](https://tools.ietf.org/html/rfc7482) - [RFC 7483 JSON Responses for the Registration Data Access Protocol (RDAP)](https://tools.ietf.org/html/rfc7483) - [RFC 7484 Finding the Authoritative Registration Data (RDAP) Service](https://tools.ietf.org/html/rfc7484) -- [RFC 8521 Registration Data Access Protocol (RDAP) Object Tagging] (https://datatracker.ietf.org/doc/rfc8521/) +- [RFC 8521 Registration Data Access Protocol (RDAP) Object Tagging](https://datatracker.ietf.org/doc/rfc8521/) From 8261950e657dbdff759365709e0a6793e4719770 Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Mon, 29 May 2023 19:36:51 +0100 Subject: [PATCH 11/17] Use http.ProxyFromEnvironment to select a proxy server via the HTTP_PROXY, HTTPS_PROXY, NO_PROXY (or lowercase) environment variables. --- cli.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cli.go b/cli.go index c5df888..04dfd0b 100644 --- a/cli.go +++ b/cli.go @@ -460,6 +460,7 @@ func RunCLI(args []string, stdout io.Writer, stderr io.Writer, options CLIOption // Custom HTTP client. Used to disable TLS certificate verification. transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, TLSClientConfig: tlsConfig, } From 0254fcd317a48f5bce3f9859cd57d9aebe80d7db Mon Sep 17 00:00:00 2001 From: "Keith B. Perry" Date: Fri, 12 Nov 2021 15:07:53 -0600 Subject: [PATCH 12/17] small change to include "Organization" --- vcard.go | 7 +++++++ vcard_test.go | 2 ++ 2 files changed, 9 insertions(+) diff --git a/vcard.go b/vcard.go index 4d22214..813cec4 100644 --- a/vcard.go +++ b/vcard.go @@ -433,6 +433,13 @@ func (v *VCard) Email() string { return v.getFirstPropertySingleString("email") } +// Org returns the VCard's org. +// +// Returns empty string if the VCard contains no organization. +func (v *VCard) Org() string { + return v.getFirstPropertySingleString("org") +} + func (v *VCard) getFirstAddressField(index int) string { adr := v.GetFirst("adr") if adr == nil { diff --git a/vcard_test.go b/vcard_test.go index 754cfa7..0cd0cf3 100644 --- a/vcard_test.go +++ b/vcard_test.go @@ -143,6 +143,7 @@ func TestVCardQuickAccessors(t *testing.T) { j.Tel(), j.Fax(), j.Email(), + j.Org(), } expected := []string{ @@ -157,6 +158,7 @@ func TestVCardQuickAccessors(t *testing.T) { "tel:+1-418-656-9254;ext=102", "", "simon.perreault@viagenie.ca", + "Viagenie", } if !reflect.DeepEqual(got, expected) { From 79e3d521a14aa904d862e11c3da19e2d342b966a Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Mon, 19 Feb 2024 00:28:42 +0000 Subject: [PATCH 13/17] Add NewVCardWithOptions(): The only implemented option is IgnoreInvalidProperties, which causes invalid vCard options to be silently ignored, rather than cause the vCard decode to fail entirely. #25 --- decoder.go | 2 +- .../jcard/error_invalid_properties.json | 10 ++ vcard.go | 112 +++++++++++------- vcard_test.go | 14 +++ 4 files changed, 96 insertions(+), 42 deletions(-) create mode 100644 test/testdata/jcard/error_invalid_properties.json diff --git a/decoder.go b/decoder.go index 826a2da..1d57d34 100644 --- a/decoder.go +++ b/decoder.go @@ -739,7 +739,7 @@ func (d *Decoder) decodePtr(keyName string, src interface{}, dst reflect.Value, var err error if dst.Type().Elem().Name() == "VCard" { - vcard, vcardError := newVCardImpl(src) + vcard, vcardError := newVCardImpl(src, VCardOptions{}) if vcardError == nil { dst.Set(reflect.ValueOf(vcard)) diff --git a/test/testdata/jcard/error_invalid_properties.json b/test/testdata/jcard/error_invalid_properties.json new file mode 100644 index 0000000..aa418f8 --- /dev/null +++ b/test/testdata/jcard/error_invalid_properties.json @@ -0,0 +1,10 @@ +["vcard", + [ + ["lang", { "pref": "1" }, "language-tag", "fr"], + ["lang", { "pref": "2" }, "language-tag", "pt"], + ["lang", { "pref": "3" }, "language-tag", "de"], + ["lang", { "pref": "4" }, "language-tag", "es"], + + ["lang", {"type": "language-tag"}, "en"] + ] +] diff --git a/vcard.go b/vcard.go index 813cec4..e131e3e 100644 --- a/vcard.go +++ b/vcard.go @@ -73,6 +73,14 @@ type VCardProperty struct { Value interface{} } +// VCardOptions specifies options for the VCard decoder routine. +type VCardOptions struct { + // By default, any invalid VCard property causes the entire VCard decode to fail. + // + // Set IgnoreInvalidProperties to true to silently skip any invalid properties. + IgnoreInvalidProperties bool +} + // Values returns a simplified representation of the VCardProperty value. // // This is convenient for accessing simple unstructured data (e.g. "fn", "tel"). @@ -135,7 +143,15 @@ func (p *VCardProperty) String() string { } // NewVCard creates a VCard from jsonBlob. +// +// Default options are used for the VCard decoder (see NewVCardWithOptions). func NewVCard(jsonBlob []byte) (*VCard, error) { + vcard, err := NewVCardWithOptions(jsonBlob, VCardOptions{}) + return vcard, err +} + +// NewVCardWithOptions creates a VCard from jsonBlob. +func NewVCardWithOptions(jsonBlob []byte, options VCardOptions) (*VCard, error) { var top []interface{} err := json.Unmarshal(jsonBlob, &top) @@ -144,12 +160,12 @@ func NewVCard(jsonBlob []byte) (*VCard, error) { } var vcard *VCard - vcard, err = newVCardImpl(top) + vcard, err = newVCardImpl(top, options) return vcard, err } -func newVCardImpl(src interface{}) (*VCard, error) { +func newVCardImpl(src interface{}, options VCardOptions) (*VCard, error) { top, ok := src.([]interface{}) if !ok || len(top) != 2 { @@ -171,58 +187,72 @@ func newVCardImpl(src interface{}) (*VCard, error) { var p interface{} for _, p = range top[1].([]interface{}) { - var a []interface{} - var ok bool - a, ok = p.([]interface{}) - - if !ok { - return nil, vCardError("jCard property was not an array") - } else if len(a) < 4 { - return nil, vCardError("jCard property too short (>=4 array elements required)") + property, err := decodeVCardProperty(p) + + if err != nil { + if options.IgnoreInvalidProperties { + continue + } else { + return nil, err + } } - name, ok := a[0].(string) + v.Properties = append(v.Properties, property) + } - if !ok { - return nil, vCardError("jCard property name invalid") - } + return v, nil +} - var parameters map[string][]string - var err error - parameters, err = readParameters(a[1]) +func decodeVCardProperty(p interface{}) (*VCardProperty, error) { + var a []interface{} + var ok bool + a, ok = p.([]interface{}) - if err != nil { - return nil, err - } + if !ok { + return nil, vCardError("jCard property was not an array") + } else if len(a) < 4 { + return nil, vCardError("jCard property too short (>=4 array elements required)") + } - propertyType, ok := a[2].(string) + name, ok := a[0].(string) - if !ok { - return nil, vCardError("jCard property type invalid") - } + if !ok { + return nil, vCardError("jCard property name invalid") + } - var value interface{} - if len(a) == 4 { - value, err = readValue(a[3], 0) - } else { - value, err = readValue(a[3:], 0) - } + var parameters map[string][]string + var err error + parameters, err = readParameters(a[1]) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } - property := &VCardProperty{ - Name: name, - Type: propertyType, - Parameters: parameters, - Value: value, - } + propertyType, ok := a[2].(string) - v.Properties = append(v.Properties, property) + if !ok { + return nil, vCardError("jCard property type invalid") } - return v, nil + var value interface{} + if len(a) == 4 { + value, err = readValue(a[3], 0) + } else { + value, err = readValue(a[3:], 0) + } + + if err != nil { + return nil, err + } + + property := &VCardProperty{ + Name: name, + Type: propertyType, + Parameters: parameters, + Value: value, + } + + return property, nil } // Get returns a list of the vCard Properties with VCardProperty name |name|. diff --git a/vcard_test.go b/vcard_test.go index 0cd0cf3..d395524 100644 --- a/vcard_test.go +++ b/vcard_test.go @@ -34,6 +34,20 @@ func TestVCardErrors(t *testing.T) { } } +func TestVCardIgnoreInvalidProperties(t *testing.T) { + json := test.LoadFile("jcard/error_invalid_properties.json") + + j1, err1 := NewVCardWithOptions(json, VCardOptions{IgnoreInvalidProperties: true}) + if j1 == nil || len(j1.Properties) != 4 || err1 != nil { + t.Errorf("jCard with ignored errors not parsed correctly\n") + } + + j2, err2 := NewVCardWithOptions(json, VCardOptions{IgnoreInvalidProperties: false}) + if j2 != nil || err2 == nil { + t.Errorf("jCard with errors unexpectedly parsed\n") + } +} + func TestVCardExample(t *testing.T) { j, err := NewVCard(test.LoadFile("jcard/example.json")) if j == nil || err != nil { From b416f22afb6f429ade710fa41dc951a027ce8737 Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Mon, 19 Feb 2024 19:16:56 +0000 Subject: [PATCH 14/17] Improve NewVCardWithOptions() documentation --- vcard.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/vcard.go b/vcard.go index e131e3e..a1f3695 100644 --- a/vcard.go +++ b/vcard.go @@ -150,7 +150,12 @@ func NewVCard(jsonBlob []byte) (*VCard, error) { return vcard, err } -// NewVCardWithOptions creates a VCard from jsonBlob. +// NewVCardWithOptions creates a VCard from jsonBlob. options specifies options for +// the VCard decoder. +// +// Example usage: +// +// vcard, err := NewVCardWithOptions(jsonBlob, VCardOptions{IgnoreInvalidProperties: true}) func NewVCardWithOptions(jsonBlob []byte, options VCardOptions) (*VCard, error) { var top []interface{} err := json.Unmarshal(jsonBlob, &top) From 44bb7e1f0659c774ab11fb1c7c0da305251ea9e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 19:26:00 +0000 Subject: [PATCH 15/17] Bump golang.org/x/crypto from 0.9.0 to 0.17.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.9.0 to 0.17.0. - [Commits](https://github.com/golang/crypto/compare/v0.9.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index beaf0b4..ed0e5fe 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/davecgh/go-spew v1.1.1 github.com/jarcoal/httpmock v1.3.0 github.com/mitchellh/go-homedir v1.1.0 - golang.org/x/crypto v0.9.0 + golang.org/x/crypto v0.17.0 ) require ( diff --git a/go.sum b/go.sum index bed1ac4..941e832 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From c8ee6e0ca4889a824f2bad085605ed7ae090f16e Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Fri, 17 May 2024 21:17:23 +0100 Subject: [PATCH 16/17] Write JSON & Raw responses to the correct filehandle (custom stdout is specified for testing & use on openrdap.org's demo page) --- cli.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cli.go b/cli.go index 04dfd0b..767e33f 100644 --- a/cli.go +++ b/cli.go @@ -12,7 +12,6 @@ import ( "net" "net/http" "net/url" - "os" "strconv" "strings" "time" @@ -525,14 +524,14 @@ func RunCLI(args []string, stdout io.Writer, stderr io.Writer, options CLIOption // Print the raw response out? if *outputFormatRaw { - fmt.Printf("%s", resp.HTTP[0].Body) + fmt.Fprintf(stdout, "%s", resp.HTTP[0].Body) } // Print the response, JSON pretty-printed? if *outputFormatJSON { var out bytes.Buffer json.Indent(&out, resp.HTTP[0].Body, "", " ") - out.WriteTo(os.Stdout) + out.WriteTo(stdout) } // Print WHOIS style response out? From eb57b3a8dedd49e95dcff7e5fcb0a836e5fed7be Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Fri, 17 May 2024 21:31:39 +0100 Subject: [PATCH 17/17] Fix indenting in --help message --- cli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli.go b/cli.go index 767e33f..b874b97 100644 --- a/cli.go +++ b/cli.go @@ -35,7 +35,7 @@ Usage: rdap [OPTIONS] DOMAIN|IP|ASN|ENTITY|NAMESERVER|RDAP-URL rdap 192.0.2.0 rdap 2001:db8:: rdap AS2856 - rdap OPS4-RIPE + rdap OPS4-RIPE rdap https://rdap.nic.cz/domain/example.cz rdap --json https://rdap.nic.cz/domain/example.cz