How to Develop a Domain Verifier CLI  using Golang.

How to Develop a Domain Verifier CLI using Golang.

#gopher #golang

In this blog post, we would be going through the process of building a domain verifier command line tool in Go. This tool look-ups a domain and verify its authenticity, and also provides the Txt records, and DMARC records.

Install Go run time

Visit the official Go website (https://golang.org/) and download the appropriate installation package for your operating system.

Install on Windows:

  • For Windows, download the MSI installer and double-click it to run the installation wizard. Follow the prompts to complete the installation.

Install on macOS:

  • For macOS, download the macOS package (.pkg) and double-click it to run the installation. Follow the instructions to complete the installation.

Install on Linux:

If you're using a Linux distribution, you can either download the tarball archive or use your package manager to install Go.

You can verify if it was successfully installed by running

go version

Now that the installation was successful let's start with the build

Now you can cd into your desired directory, and create a main.go file

package main

import (
   "bufio"
    "fmt"
    "log"
    "net"
     "os"
     "strings"
)

The first step would be to import all the packages, and as you can see all the packages needed for this build were provided to us by the Go standard library this just displays how powerful and robust the Go programming language is. I would be explaining the uses of all the packages along the way.

func main(){
   // Create a new scanner to read from the standard input(stdin)
   scanner := bufio.NewScanner(os.Stdin)
   fmt.Printf("domain,hasMX,hasSPF,sprRecord,hasDMARC,dmarcRecord\n")

    for scanner.Scan() {
        checkDomain(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal("Error : could not read from input :%v\n", err)
    }

}

The main() function is the entry point of this code, it serves as the main function that calls other functions like the checkDomain() function.

In the first line, we are creating a new scanner to read from the standard input using the "bufio" package directly imported from the go standard library, The bufio package in Go is used to provide buffered I/O operations and provides buffering for improved efficiency when reading or writing data. Buffering means that the bufio package reads or writes data in chunks, reducing the number of actual I/O system calls.

The scanner.Scan() Read input line by line and on the press of the "Enter Key", it calls the checkDomain() function.

func checkDomain(domain string) {
    var hasMX, hasSPF, hasDMARC bool
    var sprRecord, dmarcRecord string

    mxRecord, err := net.LookupMX(domain)
    if err != nil {
        log.Printf("Error:%v\n", err)
    }
    if len(mxRecord) > 0 {
        hasMX = true
    }
    txt_records, err := net.LookupTXT(domain)

    if err != nil {
        log.Printf("Error:%v\n", err)
    }

    for _, record := range txt_records {
        if strings.HasPrefix(record, "v=spfi") {
            hasSPF = true
            sprRecord = record
            break
        }
    }

    dmarcRecords, err := net.LookupTXT("_dmarc." + domain)
    if err != nil {
        log.Printf("Error:%v\n", err)
    }

    for _, records := range dmarcRecords {
        if strings.HasPrefix(records, "V=DMARC1") {
            hasDMARC = true
            dmarcRecord = records
            break
        }
    }

    fmt.Printf("%v,%v,%v,%v,%v,%v", domain, hasMX, hasSPF, sprRecord, hasDMARC, dmarcRecord)
}

The checkDomain() function is responsible for validating the domain and retrieving important details about the domain.

var hasMX, hasSPF, hasDMARC bool
var sprRecord, dmarcRecord string

mxRecord, err := net.LookupMX(domain)

We declared some variables in the first two lines, while we used the "net" package from the Go standard library on the third line of code. In Go (Golang), the net package is a standard library package that provides support for network programming. It offers functionality for working with network connections, both for the TCP and UDP protocols, as well as other network-related operations. net.LookupMX is a function provided by the net package. It is used to perform DNS (Domain Name System) and MX (Mail Exchange) lookups, The MX record is a type of DNS resource record that specifies the mail server responsible for receiving emails on behalf of a domain. When you send an email to someone, your email client or server looks up the MX records of the recipient's domain to find the appropriate mail server to deliver the email.

The net.LookupMX function takes a domain name as its argument and returns a slice of MX structs representing the MX records for that domain. The MX struct contains two fields: Host, which is the mail server's hostname, and Pref, which is the preference value for that mail server. The preference value is used to determine the priority of mail servers when there are multiple MX records for a domain.

txt_records, err := net.LookupTXT(domain)

In Go (Golang), net.LookupTXT is another function provided by the net package. It is used to perform DNS TXT (Text) record lookups. The TXT record is a type of DNS resource record that allows you to associate arbitrary text with a domain. TXT records are often used for various purposes, such as adding SPF (Sender Policy Framework) records for email validation, verifying domain ownership for services like Google Webmaster Tools, or adding verification tokens for third-party services. The net.LookupTXT function takes a domain name as its argument and returns a slice of strings containing the text entries associated with the domain's TXT records.

if err != nil {
        log.Printf("Error:%v\n", err)
}

When performing the look-ups it is very important to check for possible errors, that may occur

for _, record := range txt_records {
        if strings.HasPrefix(record, "v=spfi") {
            hasSPF = true
            sprRecord = record
            break
        }
}

In the For loop above we are looping over the txt-records and checking if it has a prefix "v=spfi", and then assigning a boolean to the hasSPF variable.

We used the "net" package to check for the MxRecord, DmarcRecord, and TxtRecord of any domain inputted by the user

fmt.Printf("%v,%v,%v,%v,%v,%v", domain, hasMX, hasSPF, sprRecord, hasDMARC, dmarcRecord)

After successfully looking up the domain and retrieving the records, we then print out the domain, hasMX, hasSPF, sprRecord, hasDMARC, dmarcRecord

Lastly, we run the Program using go run main.go