NOLookup/bin/no_rdap.pl
#!/usr/bin/env perl
# This client is inspired by rdapper:
# https://metacpan.org/source/GBROWN/rdapper-0.3
#
use strict;
use warnings;
use NOLookup::RDAP::RDAPLookup qw / $sepln %RDAP_FIELDSETS/;
use NOLookup::RDAP::RDAPLookup::Whois;
use Encode;
use Getopt::Long qw(:config no_ignore_case bundling);
use Pod::Usage;
use Term::ANSIColor;
use JSON;
use Data::Dumper;
$Data::Dumper::Indent=1;
my ($service_url, $query, $check, $nameservers, $rawtype, $fieldset,
$cursor, $nopages, $entity, $help, $debug, $verbose, $expand,
$short, $use_cache, $referral_ip, $whois_fmt, $force_ipv,
$bauth_username, $bauth_password, $color);
my $BG_COL_GREY = 'on_grey23';
my $FG_COL_YELLOW = 'yellow';
my $FG_COL_RED = 'red';
# Reset any term colors
print color('reset'), "\n";
##
# Default test values unless overrided by their parameters
my $use_test_values = 1;
my $test_service_url = $ENV{RDAP_SERVICE_URL} || 'https://rdap.test.norid.no';
my $test_referral_ip = 0; # or an ip, like '1.2.3.4';
my $test_expand = 0;
GetOptions(
'service_url|s:s' => \$service_url,
'query|q:s' => \$query,
'check|c' => \$check,
'nameservers|n:i' => \$nameservers,
'entity|e' => \$entity,
'referral_ip|I:s' => \$referral_ip,
'expand|x' => \$expand,
'short|o' => \$short,
'use_cache|a' => \$use_cache,
'debug|d:i' => \$debug,
'help|h' => \$help,
'verbose|v' => \$verbose,
'whois_fmt|w' => \$whois_fmt,
'force_ipv|f:i' => \$force_ipv,
'bauth_username|U:s' => \$bauth_username,
'bauth_password|P:s' => \$bauth_password,
'color|l' => \$color,
'rawtype|r' => \$rawtype,
'fieldset|F:s' => \$fieldset,
'cursor|C:s' => \$cursor,
'nopages|p:i' => \$nopages,
) or pod2usage('-verbose' => 99, '-sections' => [qw(NAME DESCRIPTION USAGE)]);
pod2usage('-verbose' => 99, '-sections' => [qw(NAME DESCRIPTION USAGE)]) if ($help);
unless ($query) {
pod2usage("A query (-q) being a domainname, identity, nameserver name or a handle must be specified!\n");
}
if ($use_test_values) {
$service_url = $test_service_url unless $service_url;
$referral_ip = $test_referral_ip unless defined($referral_ip);
$expand = $test_expand unless $expand;
}
sub print_warnings {
my (@params) = @_;
print STDERR color($FG_COL_YELLOW) if $color;
print STDERR "Warnings:\n";
foreach my $el (@params) {
next unless $el;
my $str = encode('UTF-8', sprintf(" %s", $el));
print STDERR $str, "\n";
}
print color('reset'), "\n" if $color;
}
sub print_errors {
my (@params) = @_;
print STDERR color($FG_COL_RED) if $color;
print STDERR "Errors:\n";
foreach my $el (@params) {
next unless $el;
my $str = encode('UTF-8', sprintf(" %s", $el));
print STDERR $str, "\n";
}
print color('reset'), "\n" if $color;
}
sub print_verbose {
my $ro = shift;
if ($verbose) {
unless ($check) {
print $sepln;
print "\n--\nJSON raw data structure pretty: '", $ro->raw_json_decoded, "'\n--\n";
}
}
}
my $ro;
my %OPTIONS = (
service_url => $service_url ,
debug => $debug || 0,
use_cache => $use_cache,
norid_referral_ip => $referral_ip,
bauth_username => $bauth_username,
bauth_password => $bauth_password,
rawtype => $rawtype,
fieldset => $fieldset,
cursor => $cursor,
nopages => $nopages,
force_ipv => $force_ipv,
);
#print STDERR "OPTIONS: ", Dumper \%OPTIONS;
if ($force_ipv) {
# Try to connect on the requested protocol, if possible
unless ($force_ipv == 4 || $force_ipv == 6) {
pod2usage("The -f option must specify 4 or 6!\n");
}
}
if ($whois_fmt) {
$ro = NOLookup::RDAP::RDAPLookup::Whois->new({ %OPTIONS });
} else {
$ro = NOLookup::RDAP::RDAPLookup->new({ %OPTIONS });
}
##
# Validation and analyzing what type of query is done by the lookup
#
$ro->lookup($query, $check, $nameservers, $entity);
if ($ro->error) {
print_verbose($ro);
print_errors($ro->error, $ro->status, $ro->description);
exit 1;
}
if ($ro->warning) {
print_verbose($ro);
print_warnings($ro->warning, $ro->status, $ro->description);
} else {
print_verbose($ro);
}
if ($debug) {
print STDERR $sepln;
print STDERR "$0:\n";
print STDERR " (connecting over ipv $force_ipv since force_ipv option is set)\n" if ($force_ipv);
print STDERR " Looked up : ", $ro->_method, "/ ", $ro->_full_url, "\n";
print STDERR " Size of returned data : ", $ro->size || 0, "\n";
# paging data
if ($ro->is_a_search) {
# Cursors to pages
print STDERR " Is a search, page info :\n";
print STDERR " page_size : ", $ro->page_size || '-', "\n";
print STDERR " first_page: ", $ro->first_page || '-', "\n";
print STDERR " cur_page : ", $ro->cur_page || '-', "\n";
print STDERR " prev_page : ", $ro->prev_page || '-', "\n";
print STDERR " next_page : ", $ro->next_page || '-', "\n";
print STDERR " size : ", $ro->size || '-', "\n";
print STDERR " total_size: ", $ro->total_size || '-', "\n";
}
print STDERR $sepln;
}
if ($check) {
print "\n-- HEAD (check) operation OK, query '$query' found --\n";
exit 0;
}
if ($debug) {
print $sepln;
print " GET (lookup) operation OK, query '$query' found\n";
print " Use the -v option to see the raw JSON content\n" unless ($verbose);
}
my $result = $ro->result;
#print STDERR "ro result: ", Dumper $result;
#print "lookup up class: ", $result->class, "\n" if ($result->class);
## Print structured output
my ($rs, $errs);
print $sepln if ($debug);
if ($whois_fmt) {
# Make a whois string of the rdap result
$ro->{insert_page_info} = 1;
($rs, $errs) = $ro->result_as_norid_whois_string($check, $nameservers, $entity, $expand);
if ($rs) {
print color($BG_COL_GREY) if $color;
print "\n";
print uc("Result in whois text format:\n\n");
print encode('UTF-8', "$rs\n\n");
#print STDERR " from result: ", Dumper $result;
###
# DEBUG: Make whois objects of the whois string
#my ($wh, $do, $ho) = $ro->norid_whois_parse($rs);
#print "no_rdap.pl, wh: ", Dumper $wh if ($wh);
#print "no_rdap.pl, do: ", Dumper $do if ($do);
#print "no_rdap.pl, ho: ", Dumper $ho if ($ho);
}
} else {
($rs, $errs) = $ro->result_as_rdap_string($check, $nameservers, $entity, $short, $expand);
if ($rs) {
print color($BG_COL_GREY) if $color;
print "\n";
print uc("Result in RDAP text format:\n\n");
print encode('UTF-8', "$rs\n\n") if ($rs);
}
}
print color('reset'), "\n" if $color;
print $sepln if ($debug);
if ($errs && @$errs) {
print_warnings(@$errs);
}
=pod
=head1 NAME
no_rdap.pl
=head1 DESCRIPTION
Default behaviour:
Uses NOLookup::RDAP::RDAPLookup to perform RDAP lookup on domain
related queries and fetch and print the matching information in a
textual format inspired by rdapper.
With the -w option:
Uses NOLookup::RDAP::RDAPLookup::Whois to perform RDAP lookup on
domain related queries and fetch and print the matching information in
Norid old school whois format (rdap2whois behaviour).
=head1 USAGE
The lookup arguments can be specified by the user, and passed as a
raw argument. A raw query must be indicated with the -r option.
Otherwise the default behavious kicks in. The query type is
automagically guessed by analyzing the query, combined with
-n or -e options to resolve some ambiguous cases.
perl no_rdap.pl -q <query>
Examples:
* Lookup a single object:
- A domain:
no_rdap.pl -q norid.no
- A nameserver:
no_rdap.pl -q nn.uninett.no -n 1
- A role/host/registrar/registrant entity object:
no_rdap.pl -q UH9R-NORID
no_rdap.pl -q NN14H-NORID
no_rdap.pl -q reg2-NORID -w
no_rdap.pl -q UNA78O-NORID -e
no_rdap.pl -q XX18091P-NORID -e
* Search for matching objects:
- List of matching domains from a domain name or wildcard:
no_rdap.pl -q winter.no (same as lookup of one domain)
no_rdap.pl -q winter*.no
no_rdap.pl -q *apartment.no
- List of matching domains from a registrant identity:
no_rdap.pl -q 985821585
no_rdap.pl -q N.PRI.1234567
no_rdap.pl -q N.ORG.1234567
no_rdap.pl -q N.LEG.1234567
- List of matching domains from a registrant handle:
no_rdap.pl -q UNA78O-NORID
no_rdap.pl -q XX18091P-NORID
- List of matching domains from a nameserver name
or wildcard:
no_rdap.pl -q nn.uninett.no -n 2
no_rdap.pl -q nn.u*.no -n 2
no_rdap.pl -q *inett.no -n 2
- List of matching domains from a nameserver ip:
no_rdap.pl -q 158.38.0.181
no_rdap.pl -q 2001:700:0:503::aa:5302
- List of matching nameservers from a nameserver name
wildcard:
no_rdap.pl -q nn.uninett.no -n 1 (same as lookup of one
nameserver)
no_rdap.pl -q nn.u*.no -n 1
no_rdap.pl -q *inett.no -n 1
- List of matching nameservers from a nameserver ip:
no_rdap.pl -q 158.38.0.181 -n 1
no_rdap.pl -q 2001:700:0:503::aa:5302 -n 1
- List of matching entities from a registrant identity:
no_rdap.pl -q 985821585 -e
no_rdap.pl -q N.PRI.1234567 -e
no_rdap.pl -q N.ORG.1234567 -e
no_rdap.pl -q N.LEG.1234567 -e
- List of matching entities from a contact's full name or
wildcard:
no_rdap.pl -q 'trond haugen'
no_rdap.pl -q 'haugen'
no_rdap.pl -q 'trond hau*'
no_rdap.pl -q '* haugen'
Mandatory arguments:
-q: query, one of:
- domain name or wildcard
- nameserver name or wildcard (see use of -n)
- handle, if query matches a handle (P/O/R/H/REG (D not
supported, use domain name))
- identity: if query is a holder identity [ 985821585 | N.{PRI|ORG|LEG}.xxxxx ], a search is
performed to find matching domains.
- identity: if query is a O/P holder handle, a search is
performed to find matching domains
if -e is set, the handle object is looked up instead
- ip-address: lookup domains with nameservers using this ip,
or lookup nameservers using this ip if -n is set.
-r: The query is raw:
A raw query is complete, and shall be used as is towards the
RDAP backend. The http(s) part must not be included.
Optional arguments:
Query options:
-n: integer, one of:
1: The query is a nameserver name, and a nameservers
by nameserver name search shall be performed
2: The query is a nameserver name, and a domains
by nameserver name search shall be performed
-s: The full http(s)-address (URL) of the RDAP service (default is
https://rdap.test.norid.no)
-c: Do a HEAD instead of the default GET. HEAD returns no data, and
can be used to check existence of domain etc.
Authentication options:
Grants access to access layer with more functionality and data
(default is basic layer)
-U: Basic authentication username
For a Norid registrar RDAP user where:
- RDAP username is : 'rdapuser',
- Norid registrar id is: 'reg1234'
The basic username must be set as follows: 'rdapuser@reg1234'.
-P: Basic authentication password
Authentication options (for Norid usage only):
Grants access to access layer with more functionality and data
(default is basic layer)
-I: The ip address of the client UA for proper referral rate
limiting (default is none)
Page control for searches:
-F: Fieldset value, undef or one of the valid fieldSets
-C: Cursor to be used in first page lookup for a search, undef for
first page
-p: max number of pages from cursor (1..x, default
$RDAP_ENTITY_QUOTA_PAGES in rdap lib).
Note on page size:
The 'pageSize' parameter in RDAP lookup result tells the number of
hits per page and cannot be controlled by a client.
Various:
-e: lookup entity instead of doing a domain search
-o: present short result ( only when -w not set)
-x: expand result, do additional lookups if data is truncated
-f: force ipv4 (-f 4) or ipv6 (-f 6) connection, else use what your system picks
Note: option added to force mode if you experience DNS/connect problems
-l: Use terminal colors
Format for output of the result:
-w: dump result in old style Norid whois format (default is a
rudimentary text output inspired by rdapper)
Other:
-d: debug
0 : debug off
1 : debug output from this module
2 : headers output from LWP UA callback
3 : Debug output from Net::RDAP::UA (headers and contents)
4-8: Debug output using LWP::ConsoleLogger::Easy,
which then must be installed
-a: activate Net::RDAP library caching
-v: verbose: dump JSON result received from RDAP
-h: help
=cut
1;