Recently I had to implement a IP Address whois service in Golang, and instead of fiddling around with using a database of WHOIS servers and trying to query the right server, I figured out I would take a little different route.
First off, for those who do not know how WHOIS servers work, basically to get WHOIS information about any IP or Domain, you open a socket to the WHOIS server on port 43, and send bytes of the string of the IP or Domain with a newline at the end. Then the WHOIS server with return data with information you need. There are a lot of things to worry about when using this approach, first is setting up the socket and dealing with all of that, second is finding the right WHOIS server for the specific IP or Domain name. The problem is that there is no single WHOIS server that has WHOIS information on all domain extensions and IP’s from all network information centers that I know of, so you would need a database of the WHOIS servers and depending on which domain extension you want to use or which NIC the IP belongs to, and you would have to query the right one for every IP or Domain.
Now the approach I ended up using is a little different. The whois linux command is a great little tool that will automatically open a socket, connect, send the ip or domain and return the data to stdout. We can leverage the power of that tool instead of reinventing the wheel and doing sockets manually. Now the second issue is finding the right WHOIS server for the right IP or Domain. There is a solution to this as well, IANA (Internet Assigned Numbers Authority) runs a WHOIS server that will basically tell you which whois server to query for the specific IP or Domain. Here is what the response looks like when we use the -h flag of whois command to connect to IANA WHOIS server and get info about an IP:
kaushal@Kaushals-MacBook-Pro:~$ whois -h whois.iana.org 8.8.8.8 % IANA WHOIS server % for more information on IANA, visit http://www.iana.org % This query returned 1 object refer: whois.arin.net inetnum: 8.0.0.0 - 8.255.255.255 organisation: Level 3 Communications, Inc. status: LEGACY whois: whois.arin.net changed: 1992-12 source: IANA
As you can see, the refer: line will tell you the WHOIS server you can connect to in order to get the information on the IP. So lets try to connect and see what we get:
kaushal@Kaushals-MacBook-Pro:~$ whois -h whois.arin.net 8.8.8.8
#
# ARIN WHOIS data and services are subject to the Terms of Use
# available at: https://www.arin.net/whois_tou.html
#
# If you see inaccuracies in the results, please report at
# https://www.arin.net/public/whoisinaccuracy/index.xhtml
#
#
# Query terms are ambiguous. The query is assumed to be:
# "n 8.8.8.8"
#
# Use "?" to get help.
#
#
# The following results may also be obtained via:
# https://whois.arin.net/rest/nets;q=8.8.8.8?showDetails=true&showARIN=false&showNonArinTopLevelNet=false&ext=netref2
#
Level 3 Communications, Inc. LVLT-ORG-8-8 (NET-8-0-0-0-1) 8.0.0.0 - 8.255.255.255
Google Inc. LVLT-GOGL-8-8-8 (NET-8-8-8-0-1) 8.8.8.0 - 8.8.8.255
#
# ARIN WHOIS data and services are subject to the Terms of Use
# available at: https://www.arin.net/whois_tou.html
#
# If you see inaccuracies in the results, please report at
# https://www.arin.net/public/whoisinaccuracy/index.xhtml
#
This time, we get the right WHOIS information from ARIN’s WHOIS server that we were referred to from the IANA’s WHOIS server. So basically, this is a two step process where you first connect to IANA’s server and get the refer address, then you connect to that and get the WHOIS. Here is what the implementation looks like on Golang:
package utils
import (
"bufio"
"bytes"
"net"
"os/exec"
"strings"
)
func RunWHOIS(ipAddr string) string {
// Parse IP to make sure it is valid
ipObj := net.ParseIP(ipAddr)
if ipObj == nil {
return "Invalid IP Address!"
}
// Use parsed IP for security reasons
ipAddr = ipObj.String()
// IANA WHOIS Server
ianaServer := "whois.iana.org"
// Run whois on IANA Server and get response
ianaResponse := runWhoisCommand("-h", ianaServer, ipAddr)
/**
Try to get the whois server to query from IANA Response
**/
// Default whois server in case we cannot find another one IANA
whoisServer := "whois.arin.net"
// Create a scanner to scan through IANA Response
reader := bytes.NewReader(ianaResponse.Bytes())
scanner := bufio.NewScanner(reader)
// Scan lines
scanner.Split(bufio.ScanLines)
// Scan through lines and find refer server
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "refer:") {
// Trim the refer: on left
whoisServer = strings.TrimPrefix(line, "refer:")
// Trim whitespace
whoisServer = strings.TrimSpace(whoisServer)
}
}
// Finally, run the actual whois command with the right whois servers
whois := runWhoisCommand("-h", whoisServer, ipAddr)
return whois.String()
}
// Run whois command and return buffer
func runWhoisCommand(args ...string) bytes.Buffer {
// Store output on buffer
var out bytes.Buffer
// Execute command
cmd := exec.Command("whois", args...)
cmd.Stdout = &out
cmd.Stderr = &out
cmd.Run()
return out
}
This same logic can be used on other languages as well. Just make sure you are not directly executing the user input, since that is a security nightmare waiting to happen. Cheers!