Computing FoundationsHow the Internet Works › Day 16

Hands-on lab — Day 16: IP Addresses, DNS, and Routing

Commands

Setup

cd labs/sections/computing-foundations/day-016-ip-addresses-dns-and-routing

Run

bash examples/explore_dns.sh
bash starter/explore_dns.sh

Test

bash tests/run_tests.sh

File tree

examples/explore_dns.sh
expected-output/sample-macos.txt
metadata.yml
README.md
requirements/README.md
security.md
starter/dns-worksheet.md
starter/explore_dns.sh
tests/run_tests.sh
troubleshooting.md

Lab README

Day 016 lab — Explore DNS and Routing

Lesson

Purpose

Day 16's lesson explains how a name becomes an address and how packets reach it. This lab makes it concrete: you query the real Domain Name System and trace the real route to a host from your own machine, using the same command-line tools professionals reach for first. You resolve example.com to its IPv4 and IPv6 addresses, read its mail record, and trace the path across routers to it — then classify each address as public or private.

Learning objectives

  • Resolve a name to its A (IPv4) and AAAA (IPv6) addresses with dig.
  • Read a domain's MX (mail-exchange) record and interpret a null MX.
  • Trace the route to a host and count the hops with traceroute.
  • Classify an IPv4 address as public, private, or loopback by its range.
  • Complete a shell script by filling in four well-specified exercises.
  • Run an automated test that degrades gracefully when offline.

Prerequisites

  • The Day 16 lesson (read it first — it explains every concept this lab uses).
  • A terminal and comfort running commands (Days 8–14).
  • A working network connection for the live queries. Offline, the scripts and tests still run and report clearly — see "How to run".

Supported operating systems

  • macOS — fully supported (tested on macOS with Apple Silicon; dig, traceroute, whois, and ping ship with the system or via one package).
  • Linux — fully supported (dig from dnsutils/bind-utils, traceroute from its own package; installation covered in requirements/).
  • Windows — use nslookup for lookups and tracert for the path (both built in), or run the scripts unmodified inside WSL.

Hardware requirements

Any computer able to reach the network. The lab only reads DNS answers and sends small trace probes; it needs no particular RAM, disk, or GPU.

Required software

  • bash (3.2 or newer — preinstalled on macOS and Linux).
  • dig (from BIND/dnsutils) — or nslookup as a fallback.
  • traceroute (or tracert on Windows).
  • Optional: whois and ping for the extension exercises.

See requirements/README.md for install commands.

Free and open-source options

Everything here is free and open source or ships with your OS: bash, dig, nslookup, traceroute, whois, and ping. No account, API key, or purchase is needed. Commercial DNS-monitoring and IP-intelligence services exist, but they are built on exactly these queries, which you run yourself here for free.

Installation

Usually nothing to install on macOS. On minimal Linux images you may need the DNS and traceroute tools:

## Debian / Ubuntu
sudo apt install dnsutils traceroute
## Fedora
sudo dnf install bind-utils traceroute

File structure

day-016-ip-addresses-dns-and-routing/
├── README.md                       ← you are here
├── metadata.yml                    ← machine-readable lab metadata
├── starter/
│   ├── explore_dns.sh              ← YOUR working file (4 exercises)
│   └── dns-worksheet.md            ← worksheet for the practice assignment
├── examples/
│   └── explore_dns.sh              ← completed reference implementation
├── tests/
│   └── run_tests.sh                ← automated checks (online and offline)
├── expected-output/
│   └── sample-macos.txt            ← a real captured run (macOS, online)
├── requirements/
│   └── README.md                   ← dependency and install notes
├── troubleshooting.md
└── security.md

How to run

From this directory:

## 1. See the finished result first
bash examples/explore_dns.sh

## 2. Your task: complete the four exercises in the starter, then run it
bash starter/explore_dns.sh

## 3. Check your work
bash tests/run_tests.sh

You may pass a different domain as an argument, e.g. bash examples/explore_dns.sh wikipedia.org.

What the commands do

  • bash examples/explore_dns.sh — the reference script: resolves the A and AAAA records with dig +short, reads the MX record, notes how to run dig +trace to watch the hierarchy walk, then runs a short, timeout-bounded traceroute and classifies each IPv4 address as public, private, or loopback. If the machine is offline (or dig is missing) it says so on each line and still exits 0.
  • bash starter/explore_dns.sh — the same report skeleton with four values set to REPLACE_ME; each exercise comment names the exact command to use. Edit the file and replace each assignment with the command in $(...) form.
  • bash tests/run_tests.sh — checks that the reference script exits cleanly and prints a well-formed report; when online, that dig returns a real IPv4; when offline, it SKIPS the network checks with a message and still passes. Exit code is 0 on success, non-zero on any structural failure.

Expected output

See expected-output/sample-macos.txt — a real captured run (macOS, online, 2026-07-12). Your addresses and hops will differ; that is the point. The traceroute middle hops depend on your ISP and location, and some routers answer with * * * (silence) — both are normal. When offline, every live line is replaced by a plain "(offline / no dig …)" note and the script still completes.

Validation steps

  1. Run bash examples/explore_dns.sh — it must complete and exit without errors, online or offline.
  2. Complete the four exercises in starter/explore_dns.sh and run it; confirm the A, AAAA, and MX sections show real values (when online).
  3. Confirm you can say, for each IPv4 address shown, whether it is public or private (does it start with 10., 192.168., 172.16172.31, or 127.?).
  4. Run the tests (next section) — all checks must pass.

Tests

bash tests/run_tests.sh

Expected final line online: 12 checks, 0 failure(s), 0 skipped. (Offline it reads 10 checks, 0 failure(s), 2 skipped. — the two network checks are skipped, not failed.) The command exits 0 on success and non-zero on any structural failure, so it is safe to run in CI.

Cleanup

Nothing to clean up: the scripts make DNS queries and short trace probes and write nothing outside their own console output. To reset your work, restore the starter from git: git checkout -- starter/explore_dns.sh.

Troubleshooting

See troubleshooting.md for the full list (missing dig or traceroute, silent * * * hops, empty MX answers, caching surprises, Windows notes).

Security notes

See security.md. Short version: the scripts query public DNS and trace a route to a public test domain; they make no other connections, need no elevated privileges, and collect nothing sensitive — but a traceroute does reveal your ISP and rough location, so think before pasting one publicly.

Extension exercises

  1. Run dig +trace example.com and find the line where a root server hands off to the .com servers and where a .com server hands off to the authoritative servers.
  2. Run dig NS example.com (authoritative name servers) and whois example.com (who is registered as responsible for the name).
  3. Pick any address you saw and classify it fully — IPv4 or IPv6, public or private — and run whois <address> to see which organization's block it belongs to.
  • Previous day: Day 15 — What Happens When You Load a Web Page.
  • Next day: Day 17 — TCP, UDP, and Ports.

Expected output

sample-macos.txt

=== DNS and Routing Report ===
Generated on: 2026-07-12
Target domain: example.com

1. A record (IPv4 addresses)
   104.20.23.154   -> public
   172.66.147.243   -> public
   A record IP (first): 104.20.23.154

2. AAAA record (IPv6 addresses)
   2606:4700:10::ac42:93f3
   2606:4700:10::6814:179a
   (IPv6 present)

3. MX record (mail exchange)
   0 .

4. Walking the hierarchy
   To watch the resolver walk root -> TLD -> authoritative yourself, run:
       dig +trace example.com
   It prints every referral from the root servers down to the
   authoritative name servers. (Not run here to keep output short.)

5. Route to example.com (first few hops)
    1  192.168.1.1 (192.168.1.1)  4.141 ms
    2  10.240.12.18 (10.240.12.18)  6.278 ms
    3  *
    4  nsg-corporate-117.212.187.122.airtel.in (122.187.212.117)  9.142 ms
    5  182.79.141.56 (182.79.141.56)  14.056 ms
    6  182.79.223.13 (182.79.223.13)  18.235 ms
    7  162.158.226.81 (162.158.226.81)  23.423 ms
    8  104.20.23.154 (104.20.23.154)  27.655 ms
   Hops shown: 8 (capped at 8; '* * *' means a silent router)

=== End of report ===

Source files

examples/explore_dns.sh (4428 bytes)
#!/usr/bin/env bash
# Day 016 lab — completed reference: explore DNS and routing.
#
# Resolves a name to its IPv4/IPv6 addresses, reads its MX record, notes how
# to walk the full DNS hierarchy, traces the route to it (a few hops, with a
# timeout so it never hangs), and classifies each resolved address as public
# or private. Uses example.com — a domain reserved for documentation and
# testing (RFC 2606) — so the lab probes nobody's private systems.
#
# Requires network access. If offline (or dig is missing) it says so plainly
# and still exits cleanly, so you can read the structure before you are online.
set -uo pipefail

DOMAIN="${1:-example.com}"

echo "=== DNS and Routing Report ==="
echo "Generated on: $(date '+%Y-%m-%d')"
echo "Target domain: ${DOMAIN}"
echo

# --- helper: is this IPv4 address in a private / loopback range? -------------
classify_ipv4() {
  local ip="$1"
  case "${ip}" in
    10.*) echo "private (10.0.0.0/8)" ;;
    192.168.*) echo "private (192.168.0.0/16)" ;;
    127.*) echo "loopback (127.0.0.0/8)" ;;
    172.1[6-9].* | 172.2[0-9].* | 172.3[0-1].*) echo "private (172.16.0.0/12)" ;;
    *) echo "public" ;;
  esac
}

# --- preflight: do we have a DNS tool, and are we online? --------------------
have_dig="no"
command -v dig >/dev/null 2>&1 && have_dig="yes"

online="no"
if [ "${have_dig}" = "yes" ]; then
  if dig +time=3 +tries=1 +short A "${DOMAIN}" >/dev/null 2>&1; then
    # A successful command does not guarantee an answer; check for one below.
    online="yes"
  fi
fi

if [ "${have_dig}" != "yes" ]; then
  echo "NOTE: 'dig' is not installed — install it (see requirements/README.md)"
  echo "      or use 'nslookup ${DOMAIN}' by hand. Skipping live DNS queries."
fi

# --- 1. A record (IPv4) ------------------------------------------------------
echo "1. A record (IPv4 addresses)"
a_records=""
if [ "${have_dig}" = "yes" ]; then
  a_records="$(dig +time=3 +tries=1 +short A "${DOMAIN}" 2>/dev/null | grep -E '^[0-9]+\.' || true)"
fi
if [ -n "${a_records}" ]; then
  while IFS= read -r ip; do
    [ -z "${ip}" ] && continue
    echo "   ${ip}   -> $(classify_ipv4 "${ip}")"
  done <<< "${a_records}"
  first_a="$(echo "${a_records}" | head -n 1)"
  echo "   A record IP (first): ${first_a}"
else
  echo "   (no A record retrieved — offline, no dig, or no such record)"
fi
echo

# --- 2. AAAA record (IPv6) ---------------------------------------------------
echo "2. AAAA record (IPv6 addresses)"
aaaa_records=""
if [ "${have_dig}" = "yes" ]; then
  aaaa_records="$(dig +time=3 +tries=1 +short AAAA "${DOMAIN}" 2>/dev/null | grep -E ':' || true)"
fi
if [ -n "${aaaa_records}" ]; then
  echo "${aaaa_records}" | sed 's/^/   /'
  echo "   (IPv6 present)"
else
  echo "   (no AAAA record retrieved — this name may have none, or you are offline)"
fi
echo

# --- 3. MX record (mail exchange) --------------------------------------------
echo "3. MX record (mail exchange)"
mx_records=""
if [ "${have_dig}" = "yes" ]; then
  mx_records="$(dig +time=3 +tries=1 +short MX "${DOMAIN}" 2>/dev/null || true)"
fi
if [ -n "${mx_records}" ]; then
  echo "${mx_records}" | sed 's/^/   /'
else
  echo "   (none — the domain may have no MX record, or a null MX '0 .')"
fi
echo

# --- 4. Walking the full hierarchy (note) ------------------------------------
echo "4. Walking the hierarchy"
echo "   To watch the resolver walk root -> TLD -> authoritative yourself, run:"
echo "       dig +trace ${DOMAIN}"
echo "   It prints every referral from the root servers down to the"
echo "   authoritative name servers. (Not run here to keep output short.)"
echo

# --- 5. traceroute (a few hops, with a timeout) ------------------------------
echo "5. Route to ${DOMAIN} (first few hops)"
if command -v traceroute >/dev/null 2>&1; then
  # -m 8: stop after 8 hops.  -w 2: wait at most 2s per hop.  -q 1: one probe.
  # Wrapped so a slow/blocked network never hangs the whole script.
  if hops="$(traceroute -m 8 -w 2 -q 1 "${DOMAIN}" 2>/dev/null)"; then
    echo "${hops}" | sed 's/^/   /'
    hop_count="$(echo "${hops}" | grep -Ec '^[[:space:]]*[0-9]+')"
    echo "   Hops shown: ${hop_count} (capped at 8; '* * *' means a silent router)"
  else
    echo "   (traceroute did not complete — network may block probes; that is normal)"
  fi
else
  echo "   (traceroute not installed — see requirements/README.md; on Windows use 'tracert')"
fi
echo

echo "=== End of report ==="
metadata.yml (591 bytes)
lesson_id: D016
day: 16
kind: command-line-inspection
languages: [bash]
setup_commands:
  - cd labs/sections/computing-foundations/day-016-ip-addresses-dns-and-routing
run_commands:
  - bash examples/explore_dns.sh
  - bash starter/explore_dns.sh
test_commands:
  - bash tests/run_tests.sh
cleanup_commands:
  - 'git checkout -- starter/explore_dns.sh  # optional: reset your work'
requires_network: true
requires_api_key: false
estimated_minutes: 30
last_executed: '2026-07-12'
executed_on: 'macOS (Apple Silicon), online — bash tests/run_tests.sh → 12 checks, 0 failure(s), 0 skipped'
requirements/README.md (1704 bytes)
# Dependencies — Day 016 lab

This lab needs a POSIX shell and two small network tools. Both are free and
open source.

## `dig` (DNS lookup)

- **macOS:** ships with the system on most versions. If missing,
  `brew install bind` provides it.
- **Debian / Ubuntu:** `sudo apt install dnsutils`
- **Fedora / RHEL:** `sudo dnf install bind-utils`
- **Fallback:** `nslookup` is preinstalled almost everywhere (including
  Windows) and answers the same basic questions, just with less detail. The
  scripts prefer `dig`; if it is absent they tell you to use `nslookup`.

## `traceroute` (route tracing)

- **macOS:** preinstalled (`/usr/sbin/traceroute`).
- **Debian / Ubuntu:** `sudo apt install traceroute`
- **Fedora / RHEL:** `sudo dnf install traceroute`
- **Windows:** use the built-in `tracert` command instead.

> Note: `traceroute` can be **slow** (it waits for each hop) and is often
> **rate-limited or blocked** by intermediate networks, which shows up as
> `* * *` lines. That is expected, not a failure. The lab's scripts cap it at
> a few hops with a short per-hop timeout so it never hangs.

## Optional, for the extension exercises

- `whois` — domain and IP registration lookups (`sudo apt install whois` on
  Debian/Ubuntu; preinstalled on macOS).
- `ping` — reachability checks (preinstalled on macOS, Linux, and Windows).

## Network

This lab **requires network access** for its live queries. Run offline, every
script and the test suite still execute and report clearly (network checks are
skipped, not failed) — but you will not see real addresses or hops until you
are online.

There is deliberately no `requirements.txt`/`package.json`: everything here is
a system utility.
starter/dns-worksheet.md (1270 bytes)
# DNS and Routing worksheet — Day 016

Fill in every value using `bash starter/explore_dns.sh` (once you have
completed the four exercises) or the individual commands from the lesson's
hands-on section. Use `example.com` for the first column; optionally repeat
for one more site of your choice in the second.

| Field | example.com | (your choice) |
| --- | --- | --- |
| Date measured | | |
| A record — first IPv4 address | | |
| Is that A-record address public or private? | | |
| AAAA record present? (yes / no) | | |
| One AAAA (IPv6) address, if any | | |
| MX record (address, `0 .` null MX, or "none") | | |
| Number of hops from traceroute to the host | | |
| Your default gateway (hop 1 of traceroute) | | |

## How the lookup happened, in my own words

Write 4–6 sentences narrating the journey for `example.com`: your request to
the resolver, the resolver walking root → TLD → authoritative, the address
coming back, and your packets hopping to it. Use your own captured numbers,
and state explicitly whether the final address is public or private and how
you can tell.

## One thing that surprised me

One or two sentences (for example: a hop that stayed silent as `* * *`, a
domain with no MX record, or how few hops it took to cross the world).
starter/explore_dns.sh (2118 bytes)
#!/usr/bin/env bash
# Day 016 lab — STARTER: explore DNS and routing.
#
# Your task: complete the four exercises below by replacing each `REPLACE_ME`
# with the exact command named in its comment (keep the surrounding "$( )" so
# the command's output is captured into the variable). Then run:
#
#     bash starter/explore_dns.sh
#
# A completed reference version is in examples/explore_dns.sh — try it first.
# This uses example.com, a domain reserved for documentation and testing.
set -uo pipefail

DOMAIN="${1:-example.com}"

echo "=== DNS and Routing Report ==="
echo "Generated on: $(date '+%Y-%m-%d')"
echo "Target domain: ${DOMAIN}"
echo

# --- Exercise 1: resolve the A record (IPv4 address) -------------------------
# Command:  dig +short A "${DOMAIN}"
# Replace REPLACE_ME below with:  $(dig +short A "${DOMAIN}")
a_records="REPLACE_ME"
echo "1. A record (IPv4 addresses)"
echo "${a_records}" | sed 's/^/   /'
echo

# --- Exercise 2: resolve the AAAA record (IPv6 address) ---------------------
# Command:  dig +short AAAA "${DOMAIN}"
# Replace REPLACE_ME below with:  $(dig +short AAAA "${DOMAIN}")
aaaa_records="REPLACE_ME"
echo "2. AAAA record (IPv6 addresses)"
echo "${aaaa_records}" | sed 's/^/   /'
echo

# --- Exercise 3: read the MX record (mail exchange) -------------------------
# Command:  dig +short MX "${DOMAIN}"
# Replace REPLACE_ME below with:  $(dig +short MX "${DOMAIN}")
mx_records="REPLACE_ME"
echo "3. MX record (mail exchange)"
echo "${mx_records}" | sed 's/^/   /'
echo

# --- Exercise 4: trace the route to the domain (first few hops) -------------
# Command:  traceroute -m 8 -w 2 -q 1 "${DOMAIN}"
#   -m 8 stops after 8 hops, -w 2 waits at most 2s per hop, -q 1 sends one
#   probe per hop — so it finishes quickly and never hangs.
# Replace REPLACE_ME below with:  $(traceroute -m 8 -w 2 -q 1 "${DOMAIN}" 2>&1)
route_output="REPLACE_ME"
echo "4. Route to ${DOMAIN} (first few hops)"
echo "${route_output}" | sed 's/^/   /'
echo

echo "Tip: to watch the DNS hierarchy walk from root to authoritative, run:"
echo "     dig +trace ${DOMAIN}"
echo
echo "=== End of report ==="
tests/run_tests.sh (4628 bytes)
#!/usr/bin/env bash
# Tests for the Day 016 lab. Run from the lab directory:
#   bash tests/run_tests.sh
#
# Structural checks always run (the reference script must produce a
# well-formed report and exit cleanly). Network checks (dig returns a real
# IP) run only when the machine is online; offline they are SKIPPED with a
# message and the suite still passes. Exit status is 0 when all run checks
# pass, non-zero on any structural failure — so it is CI-safe either way.
set -u

lab_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
failures=0
checks=0
skips=0

check() {
  local label="$1" ok="$2"
  checks=$((checks + 1))
  if [ "${ok}" = "yes" ]; then
    echo "  ok: ${label}"
  else
    echo "  FAIL: ${label}"
    failures=$((failures + 1))
  fi
}

skip() {
  skips=$((skips + 1))
  echo "  SKIP: $1"
}

# --- Are we online with a working DNS tool? ---------------------------------
network_ok="no"
if command -v dig >/dev/null 2>&1; then
  if dig +time=3 +tries=1 +short A example.com 2>/dev/null | grep -qE '^[0-9]+\.'; then
    network_ok="yes"
  fi
fi
if [ "${network_ok}" = "yes" ]; then
  echo "Network: online (dig resolved example.com) — running full checks."
else
  echo "Network: offline or 'dig' unavailable — network checks will be SKIPPED."
fi
echo

# --- Structural checks against the reference script -------------------------
ref="${lab_dir}/examples/explore_dns.sh"
echo "Testing ${ref} ..."
if output="$(bash "${ref}" 2>&1)"; then
  check "reference script exits successfully" "yes"
else
  check "reference script exits successfully" "no"
  echo "${output}" | sed 's/^/    /'
fi

echo "${output}" | grep -q '^=== DNS and Routing Report ===$' && check "prints report header" "yes" || check "prints report header" "no"
echo "${output}" | grep -q '^=== End of report ===$' && check "prints report footer" "yes" || check "prints report footer" "no"
echo "${output}" | grep -q 'A record (IPv4' && check "has A-record section" "yes" || check "has A-record section" "no"
echo "${output}" | grep -q 'AAAA record (IPv6' && check "has AAAA-record section" "yes" || check "has AAAA-record section" "no"
echo "${output}" | grep -q 'MX record' && check "has MX-record section" "yes" || check "has MX-record section" "no"
echo "${output}" | grep -q 'Route to' && check "has route section" "yes" || check "has route section" "no"
echo "${output}" | grep -q 'dig +trace' && check "mentions dig +trace for the hierarchy" "yes" || check "mentions dig +trace for the hierarchy" "no"

# --- Network-dependent checks -----------------------------------------------
if [ "${network_ok}" = "yes" ]; then
  if echo "${output}" | grep -qE 'A record IP \(first\): [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'; then
    check "reference script resolved a real IPv4 address" "yes"
  else
    check "reference script resolved a real IPv4 address" "no"
  fi
  if dig +time=3 +tries=1 +short A example.com 2>/dev/null | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then
    check "dig independently returns an IPv4 for example.com" "yes"
  else
    check "dig independently returns an IPv4 for example.com" "no"
  fi
else
  skip "network check: reference script resolves an IPv4 (offline)"
  skip "network check: dig returns an IPv4 for example.com (offline)"
fi

# --- Structural checks against the starter ----------------------------------
starter="${lab_dir}/starter/explore_dns.sh"
echo "Testing ${starter} ..."
if bash "${starter}" >/dev/null 2>&1; then
  check "starter script exits successfully" "yes"
else
  check "starter script exits successfully" "no"
fi
if grep -q '"REPLACE_ME"' "${starter}"; then
  echo "  Note: starter still has unfinished exercises (REPLACE_ME) — structure only."
  check "starter names the four required commands in comments" \
    "$(grep -qE 'dig \+short A' "${starter}" && grep -qE 'dig \+short AAAA' "${starter}" && grep -qE 'dig \+short MX' "${starter}" && grep -qE 'traceroute' "${starter}" && echo yes || echo no)"
else
  # Learner has completed it: hold it to the same output shape.
  sout="$(bash "${starter}" 2>&1 || true)"
  echo "${sout}" | grep -q '^=== DNS and Routing Report ===$' && check "completed starter prints header" "yes" || check "completed starter prints header" "no"
  if [ "${network_ok}" = "yes" ]; then
    echo "${sout}" | grep -qE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' && check "completed starter shows a resolved IPv4" "yes" || check "completed starter shows a resolved IPv4" "no"
  else
    skip "network check: completed starter shows a resolved IPv4 (offline)"
  fi
fi

echo
echo "${checks} checks, ${failures} failure(s), ${skips} skipped."
[ "${failures}" -eq 0 ]

Troubleshooting

Troubleshooting — Day 016 lab

dig: command not found

Your system does not have BIND's tools installed. Install them (brew install bind on macOS if missing; sudo apt install dnsutils on Debian/Ubuntu; sudo dnf install bind-utils on Fedora), or fall back to the preinstalled nslookup: nslookup example.com. The scripts detect a missing dig and tell you this on each line rather than crashing.

traceroute: command not found (Linux)

Install it: sudo apt install traceroute (Debian/Ubuntu) or sudo dnf install traceroute (Fedora). On Windows the equivalent command is tracert, which is already present.

traceroute shows only * * *, or seems to hang

Many networks rate-limit or block the probes traceroute uses, so some (or all) hops decline to answer and print * * *. This is normal and does not mean the path is broken — if the final line reaches the destination, routing works. The lab caps traceroute at 8 hops with a 2-second per-hop timeout so it always finishes; you can also press Ctrl+C to stop early.

dig MX returns nothing, or 0 .

That is a real, valid answer. A domain may have no MX record, or it may publish a "null MX" (0 .) meaning it accepts no email. Record it as "none" or "null MX" on your worksheet rather than treating it as an error.

The AAAA lookup is empty

Not every name has an IPv6 address. An empty AAAA answer means the domain has no AAAA record (or you are offline), not that something is wrong. Note "no AAAA" and move on.

I changed a DNS record elsewhere and still see the old value

You are seeing a cached answer whose TTL has not expired. Wait for the TTL to pass, or query an authoritative server directly (dig @<authoritative-ns> name) to bypass the cache. This caching behavior is exactly what the lesson describes.

The script prints a private address in the traceroute

That is expected for the first hop or two: your home router and your ISP's internal routers use private ranges (192.168.x, 10.x). Only the public hops in the middle and the destination are globally routable.

Windows: bash is not recognized

Use WSL (wsl --install, then open your Linux distribution and follow the Linux path), or run the commands by hand: nslookup example.com for the lookups and tracert example.com for the path.

Tests report checks were "skipped"

That means the test could not reach the network (no dig, or offline), so it skipped the live-query checks and validated structure only. This is by design and still counts as a pass — reconnect and rerun to exercise the network checks.

Security notes

Security notes — Day 016 lab

  • What the scripts do: they send DNS queries for a public test domain (example.com, reserved for documentation and testing) and a short traceroute toward it, then print and classify the results. They make no other network connections, write no files, and change no settings.
  • Privileges: everything runs as your normal user. Nothing here needs sudo — installing the tools may, but running the lab does not. If any tutorial ever tells you to sudo a script you have not read, stop and read it first; this course reinforces that habit.
  • Privacy — the one thing to think about: a traceroute reveals the path out of your network, which includes your ISP's routers and can indicate your rough geographic location and your provider. This is only mildly identifying (it does not expose your identity or credentials), but avoid pasting a full traceroute into a public forum without a glance at what it shows. Your DNS queries also travel to your resolver, which can log them — a reason some people choose a privacy-focused resolver or encrypted DNS, as the lesson discusses.
  • Only public, reserved targets: the lab deliberately targets example.com, a domain the standards reserve for exactly this kind of testing, so you are never probing someone else's private systems. If you point the script at another host, use one you own or a well-known public site — running scans or traces against systems you do not control can violate acceptable-use policies.
  • Reading before running: both scripts are short and commented — read them first. Running unread shell scripts is a common way developers get compromised; every lab script in this course is small enough to read and understand before executing.