github.com/pritambaral/docker@v1.4.2-0.20150120174542-b2fe1b3dd952/pkg/networkfs/resolvconf/resolvconf.go (about)

     1  package resolvconf
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"regexp"
     7  	"strings"
     8  	"sync"
     9  
    10  	log "github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/utils"
    12  )
    13  
    14  var (
    15  	defaultDns      = []string{"8.8.8.8", "8.8.4.4"}
    16  	localHostRegexp = regexp.MustCompile(`(?m)^nameserver 127[^\n]+\n*`)
    17  	nsRegexp        = regexp.MustCompile(`^\s*nameserver\s*(([0-9]+\.){3}([0-9]+))\s*$`)
    18  	searchRegexp    = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`)
    19  )
    20  
    21  var lastModified struct {
    22  	sync.Mutex
    23  	sha256   string
    24  	contents []byte
    25  }
    26  
    27  func Get() ([]byte, error) {
    28  	resolv, err := ioutil.ReadFile("/etc/resolv.conf")
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	return resolv, nil
    33  }
    34  
    35  // Retrieves the host /etc/resolv.conf file, checks against the last hash
    36  // and, if modified since last check, returns the bytes and new hash.
    37  // This feature is used by the resolv.conf updater for containers
    38  func GetIfChanged() ([]byte, string, error) {
    39  	lastModified.Lock()
    40  	defer lastModified.Unlock()
    41  
    42  	resolv, err := ioutil.ReadFile("/etc/resolv.conf")
    43  	if err != nil {
    44  		return nil, "", err
    45  	}
    46  	newHash, err := utils.HashData(bytes.NewReader(resolv))
    47  	if err != nil {
    48  		return nil, "", err
    49  	}
    50  	if lastModified.sha256 != newHash {
    51  		lastModified.sha256 = newHash
    52  		lastModified.contents = resolv
    53  		return resolv, newHash, nil
    54  	}
    55  	// nothing changed, so return no data
    56  	return nil, "", nil
    57  }
    58  
    59  // retrieve the last used contents and hash of the host resolv.conf
    60  // Used by containers updating on restart
    61  func GetLastModified() ([]byte, string) {
    62  	lastModified.Lock()
    63  	defer lastModified.Unlock()
    64  
    65  	return lastModified.contents, lastModified.sha256
    66  }
    67  
    68  // RemoveReplaceLocalDns looks for localhost (127.*) entries in the provided
    69  // resolv.conf, removing local nameserver entries, and, if the resulting
    70  // cleaned config has no defined nameservers left, adds default DNS entries
    71  // It also returns a boolean to notify the caller if changes were made at all
    72  func RemoveReplaceLocalDns(resolvConf []byte) ([]byte, bool) {
    73  	changed := false
    74  	cleanedResolvConf := localHostRegexp.ReplaceAll(resolvConf, []byte{})
    75  	// if the resulting resolvConf is empty, use defaultDns
    76  	if !bytes.Contains(cleanedResolvConf, []byte("nameserver")) {
    77  		log.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers : %v", defaultDns)
    78  		cleanedResolvConf = append(cleanedResolvConf, []byte("\nnameserver "+strings.Join(defaultDns, "\nnameserver "))...)
    79  	}
    80  	if !bytes.Equal(resolvConf, cleanedResolvConf) {
    81  		changed = true
    82  	}
    83  	return cleanedResolvConf, changed
    84  }
    85  
    86  // getLines parses input into lines and strips away comments.
    87  func getLines(input []byte, commentMarker []byte) [][]byte {
    88  	lines := bytes.Split(input, []byte("\n"))
    89  	var output [][]byte
    90  	for _, currentLine := range lines {
    91  		var commentIndex = bytes.Index(currentLine, commentMarker)
    92  		if commentIndex == -1 {
    93  			output = append(output, currentLine)
    94  		} else {
    95  			output = append(output, currentLine[:commentIndex])
    96  		}
    97  	}
    98  	return output
    99  }
   100  
   101  // GetNameservers returns nameservers (if any) listed in /etc/resolv.conf
   102  func GetNameservers(resolvConf []byte) []string {
   103  	nameservers := []string{}
   104  	for _, line := range getLines(resolvConf, []byte("#")) {
   105  		var ns = nsRegexp.FindSubmatch(line)
   106  		if len(ns) > 0 {
   107  			nameservers = append(nameservers, string(ns[1]))
   108  		}
   109  	}
   110  	return nameservers
   111  }
   112  
   113  // GetNameserversAsCIDR returns nameservers (if any) listed in
   114  // /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32")
   115  // This function's output is intended for net.ParseCIDR
   116  func GetNameserversAsCIDR(resolvConf []byte) []string {
   117  	nameservers := []string{}
   118  	for _, nameserver := range GetNameservers(resolvConf) {
   119  		nameservers = append(nameservers, nameserver+"/32")
   120  	}
   121  	return nameservers
   122  }
   123  
   124  // GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf
   125  // If more than one search line is encountered, only the contents of the last
   126  // one is returned.
   127  func GetSearchDomains(resolvConf []byte) []string {
   128  	domains := []string{}
   129  	for _, line := range getLines(resolvConf, []byte("#")) {
   130  		match := searchRegexp.FindSubmatch(line)
   131  		if match == nil {
   132  			continue
   133  		}
   134  		domains = strings.Fields(string(match[1]))
   135  	}
   136  	return domains
   137  }
   138  
   139  func Build(path string, dns, dnsSearch []string) error {
   140  	content := bytes.NewBuffer(nil)
   141  	for _, dns := range dns {
   142  		if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil {
   143  			return err
   144  		}
   145  	}
   146  	if len(dnsSearch) > 0 {
   147  		if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." {
   148  			if _, err := content.WriteString("search " + searchString + "\n"); err != nil {
   149  				return err
   150  			}
   151  		}
   152  	}
   153  
   154  	return ioutil.WriteFile(path, content.Bytes(), 0644)
   155  }