Skip to content
Plugins (183)

dns

stable

DNS lookup, IP address parsing, hostname validation, and DNS record formatting utilities for network-aware Zolo programs.

use plugin dns::{lookup, lookup_first, resolve, …}
11 functions Networking
Runs with the native Zolo host

Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.

/ filter jk navigate Esc clear
Functions (11)
  1. lookup Resolves a hostname to all its IP addresses
  2. lookup_first Resolves a hostname to its first IP address
  3. resolve Resolves a host:port string to socket addresses
  4. is_ipv4 Returns true if a string is a valid IPv4 address
  5. is_ipv6 Returns true if a string is a valid IPv6 address
  6. is_loopback Returns true if an IP is a loopback address
  7. is_private Returns true if an IP is a private network address
  8. parse_ip Parses an IP string into a detail table
  9. reverse_lookup_str Converts an IP to its reverse DNS ARPA form
  10. format_record Formats a DNS record in zone file syntax
  11. is_valid_hostname Validates a hostname against RFC rules

From import to result

  1. 01Import only what you use
  2. 02Copy the smallest example
  3. 03Run it with the Zolo CLI

Overview

The dns plugin gives Zolo programs network-awareness without pulling in an external resolver library. It wraps the operating system's name resolver to turn hostnames into IP addresses, and adds a toolkit of pure string utilities for classifying IPs (IPv4 vs IPv6, loopback, private), validating hostnames, building reverse-DNS ARPA names, and formatting zone-file records. Every function is stateless — there are no handles or connections to manage; you call a function with strings and integers and get a value back. Reach for it whenever you need to resolve a host, sanity-check user-supplied addresses, or emit DNS records.

Common patterns

Resolve a hostname and inspect the address it returned:

use plugin dns::{lookup_first, is_private, is_ipv6}

let ip = lookup_first("example.com")
if ip != nil {
  print("resolved to {ip}")
  print("private?  {is_private(ip)}")
  print("ipv6?     {is_ipv6(ip)}")
}

Validate a hostname before trying to resolve it, so a typo doesn't reach the resolver:

use plugin dns::{is_valid_hostname, lookup}

let host = "my-service.internal"
if is_valid_hostname(host) {
  let ips = lookup(host)
  print("first address: {ips[1]}")
} else {
  print("'{host}' is not a valid hostname")
}

Build a zone-file fragment and the matching reverse-DNS name for an address:

use plugin dns::{format_record, reverse_lookup_str}

let ip = "93.184.216.34"
print(format_record("A", "example.com.", ip))
print(reverse_lookup_str(ip))
// "34.216.184.93.in-addr.arpa"

Resolves a hostname to all its IP addresses

Resolves a hostname to all its IP addresses using the system resolver. Returns a table (array) of IP address strings. Duplicates are removed.

use plugin dns::{lookup}

let ips = lookup("example.com")
print(ips[1])  // e.g. "93.184.216.34"

Iterate over every address a host resolves to:

use plugin dns::{lookup}

let ips = lookup("localhost")
for ip in ips {
  print("address: {ip}")
}

Resolves a hostname to its first IP address

Resolves a hostname and returns only the first IP address, or nil if resolution fails. Useful when you only need one address.

use plugin dns::{lookup_first}

let ip = lookup_first("example.com")
if ip != nil {
  print("Resolved: {ip}")
}

Resolves a host:port string to socket addresses

Resolves a "host:port" string to a table of socket address strings (e.g. "93.184.216.34:80"). Use when you need the port preserved in the result.

use plugin dns::{resolve}

let addrs = resolve("example.com:80")
print(addrs[1])  // "93.184.216.34:80"

Returns true if a string is a valid IPv4 address

Returns true if the string is a valid IPv4 address, false otherwise.

use plugin dns::{is_ipv4}

print(is_ipv4("192.168.1.1"))  // true
print(is_ipv4("::1"))          // false
print(is_ipv4("not-an-ip"))    // false

Returns true if a string is a valid IPv6 address

Returns true if the string is a valid IPv6 address, false otherwise.

use plugin dns::{is_ipv6}

print(is_ipv6("::1"))           // true
print(is_ipv6("192.168.1.1"))   // false

Returns true if an IP is a loopback address

Returns true if the IP address is a loopback address (127.x.x.x for IPv4 or ::1 for IPv6).

use plugin dns::{is_loopback}

print(is_loopback("127.0.0.1"))  // true
print(is_loopback("::1"))        // true
print(is_loopback("8.8.8.8"))    // false

Returns true if an IP is a private network address

Returns true if the IP address is in a private network range: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 for IPv4, or fc00::/7 for IPv6.

use plugin dns::{is_private}

print(is_private("192.168.0.1"))  // true
print(is_private("10.0.0.5"))     // true
print(is_private("8.8.8.8"))      // false

Parses an IP string into a detail table

Parses an IP address string into a table with fields: address (normalized), kind ("v4" or "v6"), is_loopback, and is_unspecified. Errors if the string is not a valid IP.

use plugin dns::{parse_ip}

let info = parse_ip("192.168.1.100")
print(info["kind"])        // "v4"
print(info["is_loopback"]) // false

Use parse_ip to branch on address family and flag special addresses:

use plugin dns::{parse_ip}

let info = parse_ip("::1")
print("normalized: {info["address"]}")  // "::1"
print("kind: {info["kind"]}")            // "v6"
if info["is_loopback"] {
  print("this is a loopback address")
}

Converts an IP to its reverse DNS ARPA form

Returns the reverse DNS ARPA form of an IP address. For IPv4, this is d.c.b.a.in-addr.arpa; for IPv6, it is the full nibble-reversed .ip6.arpa form.

use plugin dns::{reverse_lookup_str}

print(reverse_lookup_str("93.184.216.34"))
// "34.216.184.93.in-addr.arpa"

IPv6 addresses produce the full nibble-reversed .ip6.arpa name:

use plugin dns::{reverse_lookup_str}

print(reverse_lookup_str("::1"))
// "1.0.0. ... .0.0.ip6.arpa"

Formats a DNS record in zone file syntax

Formats a DNS record in standard zone file syntax. Supports A, AAAA, CNAME, MX, TXT, NS, SOA, and SRV record types. ttl defaults to 3600.

use plugin dns::{format_record}

print(format_record("A", "example.com.", "93.184.216.34"))
// "example.com.  3600  IN  A  93.184.216.34"

print(format_record("MX", "example.com.", "mail.example.com.", 3600, 10))
// "example.com.  3600  IN  MX  10  mail.example.com."

print(format_record("TXT", "example.com.", "v=spf1 include:_spf.google.com ~all"))
// "example.com.  3600  IN  TXT  \"v=spf1 include:_spf.google.com ~all\""

SRV records take both a priority and a weight, and any record type accepts a custom ttl:

use plugin dns::{format_record}

print(format_record("SRV", "_sip._tcp.example.com.", "sip.example.com.", 7200, 10, 60))
// "_sip._tcp.example.com.  7200  IN  SRV  10  60  sip.example.com."

print(format_record("CNAME", "www.example.com.", "example.com.", 300))
// "www.example.com.  300  IN  CNAME  example.com."

Validates a hostname against RFC rules

Validates a hostname against RFC rules: max 253 characters total, each label max 63 characters, labels contain only alphanumeric characters and hyphens, and labels do not start or end with a hyphen.

use plugin dns::{is_valid_hostname}

print(is_valid_hostname("example.com"))       // true
print(is_valid_hostname("-bad.example.com"))  // false
print(is_valid_hostname("ok-label.test"))     // true

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

en