1. Web Addresses

In the real world, we use physical addresses to help us find where a friend lives, where a business is located, or where a party is being thrown (well, I don't because I'm not invited to parties, but I digress).

In computing, web clients find other computers over the internet using Internet Protocolarrow-up-right (IP) addresses. Each device connected to the internet has a unique IP address.

Domain Names and IP Addresses

When we browse the internet, we type in a human readable domain name. That domain is converted into an IP address. The IP address tells our computer where the server is located on the internet.

Assignment

Cloudflare is a tech company that provides a cool HTTP server that we can use to look up the IP address of any domain.

I've provided a getIPAddress function that makes a request to Cloudflare. The function takes a domain namearrow-up-right as input and returns the IP address associated with that domain.

The function currently prints a string representation of the entire response we receive from Cloudflare.

1

Inspect the response

Run the code to see the structure of the response and what fields it contains.

2

Unmarshal the JSON

Import the "encoding/json" package and unmarshalarrow-up-right the response as you have done before.

3

Return the first IP address

Update the getIPAddress function to return just the first IP address found within (if it exists).

  • If there is no IP address, return an empty string and an error.

I've provided a DNSResponse struct in dns.go you might find useful.

If you encounter the error dial tcp 1.1.1.1:443: i/o timeout, switch to Google's DNS service instead. Change the url variable in getIPAddress function to utilize "https://dns.google/resolve?name=%s&type=A".

Solution

main.go
package main

import (
	"fmt"
	"io"
	"net/http"
	"encoding/json"
)

func getIPAddress(domain string) (string, error) {
	url := fmt.Sprintf("https://1.1.1.1/dns-query?name=%s&type=A", domain)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", fmt.Errorf("error creating request: %w", err)
	}
	req.Header.Set("accept", "application/dns-json")

	client := http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("error sending request: %w", err)
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		return "", fmt.Errorf("error reading response body: %w", err)
	}

	var dnsResponse DNSResponse
	err = json.Unmarshal(body, &dnsResponse)
	if err != nil {
		return "", err
	}

	if len(dnsResponse.Answer) == 0 {
		return "", fmt.Errorf("no IP address found")
	}

	return dnsResponse.Answer[0].Data, nil
}

dns.go structs

What This Does

  1. Unmarshal the JSON into DNSResponse struct:

  1. Check if there are any answers:

  1. Return the first IP address (the data field from the first Answer):

The Flow

JSON: {"Answer": [{"data": "104.26.1.86"}, ...]} ↓ Unmarshal into DNSResponse ↓ dnsResponse.Answer[0].Data ↓ Return: "104.26.1.86"