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