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