dnscache

package module
v0.12.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 4, 2025 License: GPL-3.0, MIT Imports: 15 Imported by: 0

README

dnscache

golang GoDoc Go Report Issues Size Tag View examples License


Purpose

The dnscache package provides a simple, thread-safe DNS caching mechanism for Go applications. It helps reduce DNS lookup latency and network overhead by caching resolved IP addresses and optionally refreshing them in the background.

Key features:

  • Thread-safe DNS resolution caching,
  • Optional background refresh of cached entries,
  • Simple API for fetching IPs (as arrays, single values, or strings),
  • Random IP selection for load balancing.

Installation

You can use Go to install this package for you:

go get github.com/mwat56/dnscache

Usage

The cache is thread safe. Create a new instance by specifying how long each entry should be cached (in minutes). IPs can be refreshed in the background; the default refresh interval is 0 (disabled). The default number of lookup retries is 3.

// Refresh items every 5 minutes
resolver := dnscache.New(5)

// get an array of net.IP
ips, _ := resolver.Fetch("api.google.de")

// get the first net.IP
ip, _ := resolver.FetchOne("api.google.de")

// get the first net.IP as string
ip, _ := resolver.FetchOneString("api.google.de")
Configuration Options

The dnscache package offers several configuration options through the TResolverOptions struct:

type TResolverOptions struct {
	DNSservers      []string
	CacheSize       int
	Resolver        *net.Resolver
	ExpireInterval  uint8
	MaxRetries      uint8
	RefreshInterval uint8
	TTL             uint8
}
Available Options
  • DNSservers: List of DNS servers to use, nil means use system default.
  • CacheSize: Initial size of the DNS cache, 0 means use default ( 64)
  • Resolver: Custom DNS resolver to use, nil means use default (net.DefaultResolver)
  • ExpireInterval: How often to remove expired entries in minutes (0 disables background expiration).
  • MaxRetries: Maximum number of retry attempts for DNS lookups, 0 means use default (3).
  • RefreshInterval: How often to refresh cached entries in minutes, 0 disables background refresh.
  • TTL: Time to live for cache entries in minutes, 0 means use default (64).

One may use any of the options or just a subset of them – every option has a default value to use if not explicitly specified.

Basic usage with default options except refresh interval:
// Refresh cached hosts every 5 minutes
resolver := dnscache.New(5)

This way is probably the most common practice to create a resolver. It uses all default values except for the refresh interval.

Advanced usage with custom options:
// Refresh items every 10 minutes, use a custom resolver, and retry each
// lookup up to 5 times, using 2 DNS servers while assigning each lookup
// result a TTL of 16 minutes:
resolver := dnscache.NewWithOptions(dnscache.TResolverOptions{
	DNSservers:      []string{"8.8.8.8", "8.8.4.4"},
	CacheSize:       128,
	Resolver:        myCustomResolver,
	MaxRetries:      5,
	RefreshInterval: 10,
	TTL:             16,
})
Example Use Cases
1. HTTP Client with DNS Caching

Improve HTTP client performance by caching DNS lookups:

// Create a DNS resolver with 10-minute refresh
resolver := dnscache.New(10)

// Create an HTTP client with custom transport
client := &http.Client{
	Transport: &http.Transport{
		MaxIdleConnsPerHost: 64,
		Dial: func(aNetwork string, aAddress string) (net.Conn, error) {
			separator := strings.LastIndex(aAddress, ":")
			host := aAddress[:separator]
			port := aAddress[separator:]

			ip, err := resolver.FetchRandomString(host)
			if nil != err {
				return nil, err
			}

			// Connect using the resolved IP
			return net.Dial("tcp", ip + port)
		}, // Dial
		TLSHandshakeTimeout: 10 * time.Second,
	}, // Transport
	Timeout: 30 * time.Second,
} // client

// Use the client
response, err := client.Get("https://example.com/")
if nil != err {
	log.Fatalf("Error: %v", err)
}
defer response.Body.Close()
2. Load Balancing by Random IP Selection

Distribute connections across multiple IPs for a single hostname:

// Create a DNS resolver with 15-minute refresh
resolver := dnscache.New(15)

func connectToService(aService string) (net.Conn, error) {
	// Get a random IP for the service (using Yoda-style comparison)
	ip, err := resolver.FetchRandomString(aService)
	if nil != err {
		return nil, fmt.Errorf("DNS resolution failed: %w", err)
	}

	// Connect to the randomly selected IP
	conn, err := net.Dial("tcp", ip + ":443")
	if nil != err {
		return nil, fmt.Errorf("connection failed: %w", err)
	}

	return conn, nil
} // connectToService()
3. Microservice Communication with DNS Caching

Improve inter-service communication in a microservice architecture:

// Create a DNS resolver with 3-minute refresh
resolver := dnscache.New(3)

// Service discovery function
func getServiceEndpoint(aServiceName string) (string, error) {
	ip, err := resolver.FetchOneString(aServiceName + ".internal")
	if nil != err {
		return "", fmt.Errorf("service discovery failed: %w", err)
	}

	return fmt.Sprintf("http://%s:8080", ip), nil
} // getServiceEndpoint()

// Call another service
func callUserService(aUserID string) (*User, error) {
	endpoint, err := getServiceEndpoint("user-service")
	if nil != err {
		return nil, err
	}

	response, err := http.Get(endpoint + "/users/" + aUserID)
	if nil != err {
		return nil, fmt.Errorf("service call failed: %w", err)
	}
	defer response.Body.Close()

	// Process response...
} // callUserService()
4. Graceful Shutdown

Properly close the resolver when your application shuts down:

// Create a DNS resolver with background refresh
resolver := dnscache.New(10)

// Use resolver throughout application lifetime...

// When shutting down
func shutdown() {
	// Stop background refresh goroutine
	resolver.StopRefresh()
	// Stop background expiration goroutine
	resolver.StopExpire()

	// Perform other cleanup...
} // shutdown()
Runtime Metrics

The dnscache package provides metrics for monitoring the performance and health of the DNS cache. The metrics can be accessed through the Metrics() method of the TResolver instance:

// Create a DNS resolver with background refresh
resolver := dnscache.New(10)

// … do some work …

// Get the current metrics data
metrics := resolver.Metrics()

The metrics are returned as a *TMetrics struct, which provides the following fields:

  • Lookups: Total number of DNS lookups,
  • Hits: Number of cache hits,
  • Misses: Number of cache misses,
  • Retries: Number of lookup retries,
  • Errors: Number of lookup errors,
  • Peak: Peak number of cached entries.

The field values are a snapshot of the current state at the time of requesting the metrics and get updated atomically as the resolver does its work. In other words, the metrics may change while you are reading them. Hence, in case some sort of statistics are to be calculated, it is recommended to request the metrics data at regular intervals and then work with the respective snapshot.

The metrics can be printed to the console using the String() method of the TMetrics struct:

metrics := resolver.Metrics()
// … do some analysing …
fmt.Println(metrics.String())

Libraries

The following external libraries were used building dnscache:

  • No external libraries were used to build this library.

Licence

    Copyright © 2025 M.Watermann, 10247 Berlin, Germany
                    All rights reserved
                EMail : <support@mwat.de>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

You should have received a copy of the GNU General Public License along with this program. If not, see the GNU General Public License for details.


GFDL

Documentation

Overview

Copyright © 2025 M.Watermann, 10247 Berlin, Germany

    All rights reserved
EMail : <support@mwat.de>

Package `dnscache` implements a DNS cache with optional background refresh and configurable allow/deny lists (to block SPAM/Ad sites).

Copyright © 2025 M.Watermann, 10247 Berlin, Germany
                All rights reserved
            EMail : <support@mwat.de>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

You should have received a copy of the GNU General Public License along with this program. If not, see the [GNU General Public License](http://www.gnu.org/licenses/gpl.html) for details.

This package was inSPIRED on the abandoned `dnscache` project at <https://github.com/viki-org/dnscache>.

Copyright © 2025 M.Watermann, 10247 Berlin, Germany

    All rights reserved
EMail : <support@mwat.de>

Copyright © 2025 M.Watermann, 10247 Berlin, Germany

    All rights reserved
EMail : <support@mwat.de>

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type TMetrics added in v0.5.0

type TMetrics struct {
	Lookups uint32
	Hits    uint32
	Misses  uint32
	Retries uint32
	Errors  uint32
	Peak    uint32
}

`TMetrics` contains the metrics data for the DNS cache.

These are the public fields to access the metrics data:

  • `Lookups`: Total number of lookups,
  • `Hits`: Number of cache hits,
  • `Misses`: Number of cache misses,
  • `Retries`: Number of lookup retries,
  • `Errors`: Number of lookup errors,
  • `Peak`: Peak number of cached entries.

func (*TMetrics) Equal added in v0.6.0

func (m *TMetrics) Equal(aMetrics *TMetrics) bool

`Equal()` checks whether the metrics data is equal to the given one.

Parameters:

  • `aMetrics`: Metrics data to compare with.

Returns:

  • `bool`: `true` if the metrics data is equal to the given one, `false` otherwise.

func (*TMetrics) String added in v0.5.0

func (m *TMetrics) String() string

`String()` implements the `fmt.Stringer` interface for the metrics data.

Returns:

  • `string`: String representation of the metrics data.

type TResolver

type TResolver struct {
	sync.RWMutex

	cache.ICacheList //list of DNS cache entries
	// contains filtered or unexported fields
}

`TResolver` is a DNS resolver with an optional background refresh.

It embeds a map of DNS cache entries to store the DNS cache entries and uses a Mutex to synchronise access to that cache.

func New

func New(aRefreshInterval uint8) *TResolver

`New()` returns a new DNS resolver with an optional background refresh.

If `aRefreshInterval` is greater than zero, cached DNS entries will be automatically refreshed at the specified interval.

Parameters:

  • `aRefreshInterval`: Optional interval in minutes to refresh the cache.

Returns:

  • `*TResolver`: A new `TResolver` instance.

func NewWithOptions added in v0.2.0

func NewWithOptions(aOptions TResolverOptions) *TResolver

`NewWithOptions()` returns a new DNS resolver with custom options.

Parameters:

  • `aOptions`: Options for the resolver.

Returns:

  • `*TResolver`: A new `TResolver` instance.

func (*TResolver) Fetch

func (r *TResolver) Fetch(aHostname string) ([]net.IP, error)

`Fetch()` returns the IP addresses for a given hostname.

Parameters:

  • `aHostname`: The hostname to resolve.

Returns:

  • `[]net.IP`: List of IP addresses for the given hostname.
  • `error`: `nil` if the hostname was resolved successfully, the error otherwise.

func (*TResolver) FetchFirst added in v0.11.0

func (r *TResolver) FetchFirst(aHostname string) (net.IP, error)

`FetchFirst()` returns the first IP address for a given hostname.

If the hostname has multiple IP addresses, the first one is returned; to get a random IP address, use [FetchRandom].

Parameters:

  • `aHostname`: The hostname to resolve.

Returns:

  • `net.IP`: First IP address for the given hostname.
  • `error`: `nil` if the hostname was resolved successfully, the error otherwise.

func (*TResolver) FetchFirstString added in v0.11.0

func (r *TResolver) FetchFirstString(aHostname string) (string, error)

`FetchFirstString()` returns the first IP address for a given hostname as a string.

Parameters:

  • `aHostname`: The hostname to resolve.

Returns:

  • `string`: First IP address for the given hostname as a string.
  • `error`: `nil` if the hostname was resolved successfully, the error otherwise.

func (*TResolver) FetchRandom

func (r *TResolver) FetchRandom(aHostname string) (net.IP, error)

`FetchRandom()` returns a random IP address for a given hostname.

If the hostname has multiple IP addresses, a random one is returned; to get always the first one, use [FetchFirst] instead.

Parameters:

  • `aHostname`: The hostname to resolve.

Returns:

  • `net.IP`: Random IP address for the given hostname.
  • `error`: `nil` if the hostname was resolved successfully, the error otherwise.

func (*TResolver) FetchRandomString

func (r *TResolver) FetchRandomString(aHostname string) (string, error)

`FetchRandomString()` returns a random IP address for a given hostname as a string.

If the hostname has multiple IP addresses, a random one is returned; to get always the first one, use [FetchFirstString] instead.

Parameters:

  • `aHostname`: The hostname to resolve.

Returns:

  • `string`: Random IP address for the given hostname as a string.
  • `error`: `nil` if the hostname was resolved successfully, the error otherwise.

func (*TResolver) LoadAllowlist added in v0.11.0

func (r *TResolver) LoadAllowlist(aFilename string) error

`LoadAllowlist()` loads the allowlist from the given file.

Parameters:

  • `aFilename`: The path/file name to read the 'allow' patterns from.

Returns:

  • `error`: An error in case of problems, or `nil` otherwise.

func (*TResolver) LoadBlocklists added in v0.11.0

func (r *TResolver) LoadBlocklists(aURLs []string) error

`LoadBlocklists()` loads the blocklists from the given URLs.

Parameters:

  • `aURLs`: The URLs to download the blocklists from.

Returns:

  • `error`: An error in case of problems, or `nil` otherwise.

func (*TResolver) LookupHost added in v0.11.1

func (r *TResolver) LookupHost(aCtx context.Context, aHostname string) ([]net.IP, error)

`LookupHost()` resolves a hostname with the given context and caches the result.

This method is called by `Fetch()` and `Refresh()` internally and not intended for public use because it bypasses both, the allow/deny lists and the internal cache.

Parameters:

  • `aCtx`: Context for the lookup operation.
  • `aHostname`: The hostname to resolve.

Returns:

  • `[]net.IP`: List of IP addresses for the given hostname.
  • `error`: `nil` if the hostname was resolved successfully, the error otherwise.

func (*TResolver) Metrics added in v0.5.0

func (r *TResolver) Metrics() (rMetrics *TMetrics)

`Metrics()` returns the current metrics data.

Returns:

  • `*TMetrics`: Current metrics data.

func (*TResolver) Refresh

func (r *TResolver) Refresh()

`Refresh()` resolves all cached hostnames and updates the cache.

This method is called automatically if a refresh interval was specified in the `New()` constructor.

func (*TResolver) StopExpire added in v0.6.0

func (r *TResolver) StopExpire() *TResolver

`StopExpire()` stops the background expiration goroutine if it's running.

This method should be called when the background expirations are no longer needed. The resolver remains usable after calling `StopExpire()“, but cached entries will no longer be automatically expired.

func (*TResolver) StopRefresh added in v0.6.0

func (r *TResolver) StopRefresh() *TResolver

`StopRefresh()` stops the background refresh goroutine if it's running.

This method should be called when the background updates are no longer needed. The resolver remains usable after calling `StopRefresh()“, but cached entries will no longer be automatically refreshed.

type TResolverOptions added in v0.2.0

type TResolverOptions struct {
	BlockLists      []string
	DNSservers      []string
	AllowList       string
	DataDir         string
	CacheSize       int
	Resolver        *net.Resolver
	ExpireInterval  uint8
	MaxRetries      uint8
	RefreshInterval uint8
	TTL             uint8
}

`TResolverOptions` contain configuration options for creating a resolver.

This are the public fields to configure a new `TResolver` instance:

  • `BlockLists`: List of URLs to download blocklists from.
  • `DNSservers`: List of DNS servers to use, `nil` means use system default.
  • `AllowList`: Path/file name to read the 'allow' patterns from.
  • `DataDir`: Directory to store local allow and deny lists.
  • `CacheSize`: Initial cache size, `0` means use default (`512`).
  • `Resolver`: Custom resolver, `nil` means use default.
  • `ExpireInterval`: Optional interval (in minutes) to remove expired cache entries.
  • `MaxRetries`: Maximum number of retries for DNS lookup, `0` means use default (`3`).
  • `RefreshInterval`: Optional interval (in minutes) to refresh the cache.
  • `TTL`: Optional time to live (in minutes) for cache entries.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL