1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
// Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf
package resolvconf
import (
"bytes"
"fmt"
"os"
"strings"
"github.com/docker/docker/libnetwork/internal/resolvconf"
"github.com/opencontainers/go-digest"
)
// constants for the IP address type
const (
IP = iota // IPv4 and IPv6
IPv4
IPv6
)
// File contains the resolv.conf content and its hash
type File struct {
Content []byte
Hash []byte
}
func Path() string {
return resolvconf.Path()
}
// Get returns the contents of /etc/resolv.conf and its hash
func Get() (*File, error) {
return GetSpecific(Path())
}
// GetSpecific returns the contents of the user specified resolv.conf file and its hash
func GetSpecific(path string) (*File, error) {
resolv, err := os.ReadFile(path)
if err != nil {
return nil, err
}
hash := digest.FromBytes(resolv)
return &File{Content: resolv, Hash: []byte(hash)}, nil
}
// FilterResolvDNS cleans up the config in resolvConf. It has two main jobs:
// 1. It looks for localhost (127.*|::1) entries in the provided
// resolv.conf, removing local nameserver entries, and, if the resulting
// cleaned config has no defined nameservers left, adds default DNS entries
// 2. Given the caller provides the enable/disable state of IPv6, the filter
// code will remove all IPv6 nameservers if it is not enabled for containers
func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "")
if err != nil {
return nil, err
}
rc.TransformForLegacyNw(ipv6Enabled)
content, err := rc.Generate(false)
if err != nil {
return nil, err
}
hash := digest.FromBytes(content)
return &File{Content: content, Hash: []byte(hash)}, nil
}
// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf
func GetNameservers(resolvConf []byte, kind int) []string {
rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "")
if err != nil {
return nil
}
nsAddrs := rc.NameServers()
var nameservers []string
for _, addr := range nsAddrs {
if kind == IP {
nameservers = append(nameservers, addr.String())
} else if kind == IPv4 && addr.Is4() {
nameservers = append(nameservers, addr.String())
} else if kind == IPv6 && addr.Is6() {
nameservers = append(nameservers, addr.String())
}
}
return nameservers
}
// GetNameserversAsCIDR returns nameservers (if any) listed in
// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32")
// This function's output is intended for net.ParseCIDR
func GetNameserversAsCIDR(resolvConf []byte) []string {
rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "")
if err != nil {
return nil
}
nsAddrs := rc.NameServers()
nameservers := make([]string, 0, len(nsAddrs))
for _, addr := range nsAddrs {
str := fmt.Sprintf("%s/%d", addr.WithZone("").String(), addr.BitLen())
nameservers = append(nameservers, str)
}
return nameservers
}
// GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf
// If more than one search line is encountered, only the contents of the last
// one is returned.
func GetSearchDomains(resolvConf []byte) []string {
rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "")
if err != nil {
return nil
}
return rc.Search()
}
// GetOptions returns options (if any) listed in /etc/resolv.conf
// If more than one options line is encountered, only the contents of the last
// one is returned.
func GetOptions(resolvConf []byte) []string {
rc, err := resolvconf.Parse(bytes.NewBuffer(resolvConf), "")
if err != nil {
return nil
}
return rc.Options()
}
// Build generates and writes a configuration file to path containing a nameserver
// entry for every element in nameservers, a "search" entry for every element in
// dnsSearch, and an "options" entry for every element in dnsOptions. It returns
// a File containing the generated content and its (sha256) hash.
//
// Note that the resolv.conf file is written, but the hash file is not.
func Build(path string, nameservers, dnsSearch, dnsOptions []string) (*File, error) {
content := bytes.NewBuffer(nil)
if len(dnsSearch) > 0 {
if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." {
if _, err := content.WriteString("search " + searchString + "\n"); err != nil {
return nil, err
}
}
}
for _, dns := range nameservers {
if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil {
return nil, err
}
}
if len(dnsOptions) > 0 {
if optsString := strings.Join(dnsOptions, " "); strings.Trim(optsString, " ") != "" {
if _, err := content.WriteString("options " + optsString + "\n"); err != nil {
return nil, err
}
}
}
if err := os.WriteFile(path, content.Bytes(), 0o644); err != nil {
return nil, err
}
hash := digest.FromBytes(content.Bytes())
return &File{Content: content.Bytes(), Hash: []byte(hash)}, nil
}
|