clientconfig.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package dns
  2. import (
  3. "bufio"
  4. "io"
  5. "os"
  6. "strconv"
  7. "strings"
  8. )
  9. // ClientConfig wraps the contents of the /etc/resolv.conf file.
  10. type ClientConfig struct {
  11. Servers []string // servers to use
  12. Search []string // suffixes to append to local name
  13. Port string // what port to use
  14. Ndots int // number of dots in name to trigger absolute lookup
  15. Timeout int // seconds before giving up on packet
  16. Attempts int // lost packets before giving up on server, not used in the package dns
  17. }
  18. // ClientConfigFromFile parses a resolv.conf(5) like file and returns
  19. // a *ClientConfig.
  20. func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {
  21. file, err := os.Open(resolvconf)
  22. if err != nil {
  23. return nil, err
  24. }
  25. defer file.Close()
  26. return ClientConfigFromReader(file)
  27. }
  28. // ClientConfigFromReader works like ClientConfigFromFile but takes an io.Reader as argument
  29. func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
  30. c := new(ClientConfig)
  31. scanner := bufio.NewScanner(resolvconf)
  32. c.Servers = make([]string, 0)
  33. c.Search = make([]string, 0)
  34. c.Port = "53"
  35. c.Ndots = 1
  36. c.Timeout = 5
  37. c.Attempts = 2
  38. for scanner.Scan() {
  39. if err := scanner.Err(); err != nil {
  40. return nil, err
  41. }
  42. line := scanner.Text()
  43. f := strings.Fields(line)
  44. if len(f) < 1 {
  45. continue
  46. }
  47. switch f[0] {
  48. case "nameserver": // add one name server
  49. if len(f) > 1 {
  50. // One more check: make sure server name is
  51. // just an IP address. Otherwise we need DNS
  52. // to look it up.
  53. name := f[1]
  54. c.Servers = append(c.Servers, name)
  55. }
  56. case "domain": // set search path to just this domain
  57. if len(f) > 1 {
  58. c.Search = make([]string, 1)
  59. c.Search[0] = f[1]
  60. } else {
  61. c.Search = make([]string, 0)
  62. }
  63. case "search": // set search path to given servers
  64. c.Search = append([]string(nil), f[1:]...)
  65. case "options": // magic options
  66. for _, s := range f[1:] {
  67. switch {
  68. case len(s) >= 6 && s[:6] == "ndots:":
  69. n, _ := strconv.Atoi(s[6:])
  70. if n < 0 {
  71. n = 0
  72. } else if n > 15 {
  73. n = 15
  74. }
  75. c.Ndots = n
  76. case len(s) >= 8 && s[:8] == "timeout:":
  77. n, _ := strconv.Atoi(s[8:])
  78. if n < 1 {
  79. n = 1
  80. }
  81. c.Timeout = n
  82. case len(s) >= 9 && s[:9] == "attempts:":
  83. n, _ := strconv.Atoi(s[9:])
  84. if n < 1 {
  85. n = 1
  86. }
  87. c.Attempts = n
  88. case s == "rotate":
  89. /* not imp */
  90. }
  91. }
  92. }
  93. }
  94. return c, nil
  95. }
  96. // NameList returns all of the names that should be queried based on the
  97. // config. It is based off of go's net/dns name building, but it does not
  98. // check the length of the resulting names.
  99. func (c *ClientConfig) NameList(name string) []string {
  100. // if this domain is already fully qualified, no append needed.
  101. if IsFqdn(name) {
  102. return []string{name}
  103. }
  104. // Check to see if the name has more labels than Ndots. Do this before making
  105. // the domain fully qualified.
  106. hasNdots := CountLabel(name) > c.Ndots
  107. // Make the domain fully qualified.
  108. name = Fqdn(name)
  109. // Make a list of names based off search.
  110. names := []string{}
  111. // If name has enough dots, try that first.
  112. if hasNdots {
  113. names = append(names, name)
  114. }
  115. for _, s := range c.Search {
  116. names = append(names, Fqdn(name+s))
  117. }
  118. // If we didn't have enough dots, try after suffixes.
  119. if !hasNdots {
  120. names = append(names, name)
  121. }
  122. return names
  123. }