diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/net/nss.go | |
parent | Initial commit. (diff) | |
download | golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/net/nss.go')
-rw-r--r-- | src/net/nss.go | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/src/net/nss.go b/src/net/nss.go new file mode 100644 index 0000000..092b515 --- /dev/null +++ b/src/net/nss.go @@ -0,0 +1,249 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "errors" + "internal/bytealg" + "os" + "sync" + "time" +) + +const ( + nssConfigPath = "/etc/nsswitch.conf" +) + +var nssConfig nsswitchConfig + +type nsswitchConfig struct { + initOnce sync.Once // guards init of nsswitchConfig + + // ch is used as a semaphore that only allows one lookup at a + // time to recheck nsswitch.conf + ch chan struct{} // guards lastChecked and modTime + lastChecked time.Time // last time nsswitch.conf was checked + + mu sync.Mutex // protects nssConf + nssConf *nssConf +} + +func getSystemNSS() *nssConf { + nssConfig.tryUpdate() + nssConfig.mu.Lock() + conf := nssConfig.nssConf + nssConfig.mu.Unlock() + return conf +} + +// init initializes conf and is only called via conf.initOnce. +func (conf *nsswitchConfig) init() { + conf.nssConf = parseNSSConfFile("/etc/nsswitch.conf") + conf.lastChecked = time.Now() + conf.ch = make(chan struct{}, 1) +} + +// tryUpdate tries to update conf. +func (conf *nsswitchConfig) tryUpdate() { + conf.initOnce.Do(conf.init) + + // Ensure only one update at a time checks nsswitch.conf + if !conf.tryAcquireSema() { + return + } + defer conf.releaseSema() + + now := time.Now() + if conf.lastChecked.After(now.Add(-5 * time.Second)) { + return + } + conf.lastChecked = now + + var mtime time.Time + if fi, err := os.Stat(nssConfigPath); err == nil { + mtime = fi.ModTime() + } + if mtime.Equal(conf.nssConf.mtime) { + return + } + + nssConf := parseNSSConfFile(nssConfigPath) + conf.mu.Lock() + conf.nssConf = nssConf + conf.mu.Unlock() +} + +func (conf *nsswitchConfig) acquireSema() { + conf.ch <- struct{}{} +} + +func (conf *nsswitchConfig) tryAcquireSema() bool { + select { + case conf.ch <- struct{}{}: + return true + default: + return false + } +} + +func (conf *nsswitchConfig) releaseSema() { + <-conf.ch +} + +// nssConf represents the state of the machine's /etc/nsswitch.conf file. +type nssConf struct { + mtime time.Time // time of nsswitch.conf modification + err error // any error encountered opening or parsing the file + sources map[string][]nssSource // keyed by database (e.g. "hosts") +} + +type nssSource struct { + source string // e.g. "compat", "files", "mdns4_minimal" + criteria []nssCriterion +} + +// standardCriteria reports all specified criteria have the default +// status actions. +func (s nssSource) standardCriteria() bool { + for i, crit := range s.criteria { + if !crit.standardStatusAction(i == len(s.criteria)-1) { + return false + } + } + return true +} + +// nssCriterion is the parsed structure of one of the criteria in brackets +// after an NSS source name. +type nssCriterion struct { + negate bool // if "!" was present + status string // e.g. "success", "unavail" (lowercase) + action string // e.g. "return", "continue" (lowercase) +} + +// standardStatusAction reports whether c is equivalent to not +// specifying the criterion at all. last is whether this criteria is the +// last in the list. +func (c nssCriterion) standardStatusAction(last bool) bool { + if c.negate { + return false + } + var def string + switch c.status { + case "success": + def = "return" + case "notfound", "unavail", "tryagain": + def = "continue" + default: + // Unknown status + return false + } + if last && c.action == "return" { + return true + } + return c.action == def +} + +func parseNSSConfFile(file string) *nssConf { + f, err := open(file) + if err != nil { + return &nssConf{err: err} + } + defer f.close() + mtime, _, err := f.stat() + if err != nil { + return &nssConf{err: err} + } + + conf := parseNSSConf(f) + conf.mtime = mtime + return conf +} + +func parseNSSConf(f *file) *nssConf { + conf := new(nssConf) + for line, ok := f.readLine(); ok; line, ok = f.readLine() { + line = trimSpace(removeComment(line)) + if len(line) == 0 { + continue + } + colon := bytealg.IndexByteString(line, ':') + if colon == -1 { + conf.err = errors.New("no colon on line") + return conf + } + db := trimSpace(line[:colon]) + srcs := line[colon+1:] + for { + srcs = trimSpace(srcs) + if len(srcs) == 0 { + break + } + sp := bytealg.IndexByteString(srcs, ' ') + var src string + if sp == -1 { + src = srcs + srcs = "" // done + } else { + src = srcs[:sp] + srcs = trimSpace(srcs[sp+1:]) + } + var criteria []nssCriterion + // See if there's a criteria block in brackets. + if len(srcs) > 0 && srcs[0] == '[' { + bclose := bytealg.IndexByteString(srcs, ']') + if bclose == -1 { + conf.err = errors.New("unclosed criterion bracket") + return conf + } + var err error + criteria, err = parseCriteria(srcs[1:bclose]) + if err != nil { + conf.err = errors.New("invalid criteria: " + srcs[1:bclose]) + return conf + } + srcs = srcs[bclose+1:] + } + if conf.sources == nil { + conf.sources = make(map[string][]nssSource) + } + conf.sources[db] = append(conf.sources[db], nssSource{ + source: src, + criteria: criteria, + }) + } + } + return conf +} + +// parses "foo=bar !foo=bar" +func parseCriteria(x string) (c []nssCriterion, err error) { + err = foreachField(x, func(f string) error { + not := false + if len(f) > 0 && f[0] == '!' { + not = true + f = f[1:] + } + if len(f) < 3 { + return errors.New("criterion too short") + } + eq := bytealg.IndexByteString(f, '=') + if eq == -1 { + return errors.New("criterion lacks equal sign") + } + if hasUpperCase(f) { + lower := []byte(f) + lowerASCIIBytes(lower) + f = string(lower) + } + c = append(c, nssCriterion{ + negate: not, + status: f[:eq], + action: f[eq+1:], + }) + return nil + }) + return +} |