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