nss.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // Copyright 2015 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
  5. package net
  6. import (
  7. "errors"
  8. "internal/bytealg"
  9. "io"
  10. "os"
  11. )
  12. // nssConf represents the state of the machine's /etc/nsswitch.conf file.
  13. type nssConf struct {
  14. err error // any error encountered opening or parsing the file
  15. sources map[string][]nssSource // keyed by database (e.g. "hosts")
  16. }
  17. type nssSource struct {
  18. source string // e.g. "compat", "files", "mdns4_minimal"
  19. criteria []nssCriterion
  20. }
  21. // standardCriteria reports all specified criteria have the default
  22. // status actions.
  23. func (s nssSource) standardCriteria() bool {
  24. for i, crit := range s.criteria {
  25. if !crit.standardStatusAction(i == len(s.criteria)-1) {
  26. return false
  27. }
  28. }
  29. return true
  30. }
  31. // nssCriterion is the parsed structure of one of the criteria in brackets
  32. // after an NSS source name.
  33. type nssCriterion struct {
  34. negate bool // if "!" was present
  35. status string // e.g. "success", "unavail" (lowercase)
  36. action string // e.g. "return", "continue" (lowercase)
  37. }
  38. // standardStatusAction reports whether c is equivalent to not
  39. // specifying the criterion at all. last is whether this criteria is the
  40. // last in the list.
  41. func (c nssCriterion) standardStatusAction(last bool) bool {
  42. if c.negate {
  43. return false
  44. }
  45. var def string
  46. switch c.status {
  47. case "success":
  48. def = "return"
  49. case "notfound", "unavail", "tryagain":
  50. def = "continue"
  51. default:
  52. // Unknown status
  53. return false
  54. }
  55. if last && c.action == "return" {
  56. return true
  57. }
  58. return c.action == def
  59. }
  60. func parseNSSConfFile(file string) *nssConf {
  61. f, err := os.Open(file)
  62. if err != nil {
  63. return &nssConf{err: err}
  64. }
  65. defer f.Close()
  66. return parseNSSConf(f)
  67. }
  68. func parseNSSConf(r io.Reader) *nssConf {
  69. slurp, err := readFull(r)
  70. if err != nil {
  71. return &nssConf{err: err}
  72. }
  73. conf := new(nssConf)
  74. conf.err = foreachLine(slurp, func(line []byte) error {
  75. line = trimSpace(removeComment(line))
  76. if len(line) == 0 {
  77. return nil
  78. }
  79. colon := bytealg.IndexByte(line, ':')
  80. if colon == -1 {
  81. return errors.New("no colon on line")
  82. }
  83. db := string(trimSpace(line[:colon]))
  84. srcs := line[colon+1:]
  85. for {
  86. srcs = trimSpace(srcs)
  87. if len(srcs) == 0 {
  88. break
  89. }
  90. sp := bytealg.IndexByte(srcs, ' ')
  91. var src string
  92. if sp == -1 {
  93. src = string(srcs)
  94. srcs = nil // done
  95. } else {
  96. src = string(srcs[:sp])
  97. srcs = trimSpace(srcs[sp+1:])
  98. }
  99. var criteria []nssCriterion
  100. // See if there's a criteria block in brackets.
  101. if len(srcs) > 0 && srcs[0] == '[' {
  102. bclose := bytealg.IndexByte(srcs, ']')
  103. if bclose == -1 {
  104. return errors.New("unclosed criterion bracket")
  105. }
  106. var err error
  107. criteria, err = parseCriteria(srcs[1:bclose])
  108. if err != nil {
  109. return errors.New("invalid criteria: " + string(srcs[1:bclose]))
  110. }
  111. srcs = srcs[bclose+1:]
  112. }
  113. if conf.sources == nil {
  114. conf.sources = make(map[string][]nssSource)
  115. }
  116. conf.sources[db] = append(conf.sources[db], nssSource{
  117. source: src,
  118. criteria: criteria,
  119. })
  120. }
  121. return nil
  122. })
  123. return conf
  124. }
  125. // parses "foo=bar !foo=bar"
  126. func parseCriteria(x []byte) (c []nssCriterion, err error) {
  127. err = foreachField(x, func(f []byte) error {
  128. not := false
  129. if len(f) > 0 && f[0] == '!' {
  130. not = true
  131. f = f[1:]
  132. }
  133. if len(f) < 3 {
  134. return errors.New("criterion too short")
  135. }
  136. eq := bytealg.IndexByte(f, '=')
  137. if eq == -1 {
  138. return errors.New("criterion lacks equal sign")
  139. }
  140. lowerASCIIBytes(f)
  141. c = append(c, nssCriterion{
  142. negate: not,
  143. status: string(f[:eq]),
  144. action: string(f[eq+1:]),
  145. })
  146. return nil
  147. })
  148. return
  149. }