diff options
| author | Shulhan <ms@kilabit.info> | 2023-04-09 20:49:09 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2023-04-09 20:49:09 +0700 |
| commit | ace497bd8c22c718bedd18582db4ed07780d1a44 (patch) | |
| tree | 314bda69caba9176d0c3a5bc2d1e712ca30b293f | |
| parent | 1015cc4c739bed24999ad13c98b4c62f72cc11e3 (diff) | |
| download | pakakeh.go-ace497bd8c22c718bedd18582db4ed07780d1a44.tar.xz | |
lib/net: refactoring resolv.conf parser using lib/bytes#Parser
The lib/io#Reader will be deprecated and replaced with lib/bytes#Parser
in the future.
| -rw-r--r-- | lib/net/resolvconf.go | 235 | ||||
| -rw-r--r-- | lib/net/resolvconf_test.go | 21 | ||||
| -rw-r--r-- | lib/net/testdata/resolv.conf | 2 |
3 files changed, 135 insertions, 123 deletions
diff --git a/lib/net/resolvconf.go b/lib/net/resolvconf.go index d35dda38..cf03c8fd 100644 --- a/lib/net/resolvconf.go +++ b/lib/net/resolvconf.go @@ -14,7 +14,7 @@ import ( "strings" "github.com/shuLhan/share/lib/ascii" - libio "github.com/shuLhan/share/lib/io" + libbytes "github.com/shuLhan/share/lib/bytes" ) const ( @@ -22,8 +22,6 @@ const ( ) var ( - newLineTerms = []byte{'\n'} - // lambda to test os.Hostname. getHostname = os.Hostname ) @@ -102,33 +100,38 @@ type ResolvConf struct { } // NewResolvConf open resolv.conf file in path and return the parsed records. -func NewResolvConf(path string) (*ResolvConf, error) { - rc := &ResolvConf{ - OptMisc: make(map[string]bool), - } +func NewResolvConf(path string) (rc *ResolvConf, err error) { + var ( + logp = `NewResolvConf` - reader, err := libio.NewReader(path) + content []byte + ) + + content, err = os.ReadFile(path) if err != nil { - return nil, err + return nil, fmt.Errorf(`%s: %w`, logp, err) } - rc.parse(reader) + var parser = libbytes.NewParser(content, nil) + + rc = &ResolvConf{ + OptMisc: make(map[string]bool), + } + rc.parse(parser) return rc, nil } // Init parse resolv.conf from string. func (rc *ResolvConf) Init(src string) { - reader := new(libio.Reader) - reader.Init([]byte(src)) + var parser = libbytes.NewParser([]byte(src), nil) rc.reset() - - rc.parse(reader) + rc.parse(parser) } func (rc *ResolvConf) reset() { - rc.Domain = "" + rc.Domain = `` rc.Search = nil rc.NameServers = nil rc.OptMisc = make(map[string]bool) @@ -144,148 +147,156 @@ func (rc *ResolvConf) reset() { // by white space. // // See `man resolv.conf` -func (rc *ResolvConf) parse(reader *libio.Reader) { +func (rc *ResolvConf) parse(parser *libbytes.Parser) { + parser.SetDelimiters([]byte{' ', '\t', ';', '#', '\n'}) + + var ( + stok string + tok []byte + c byte + ) + for { - c := reader.SkipSpaces() - if c == 0 { - break - } - if c == ';' || c == '#' { - reader.SkipUntil(newLineTerms) + tok, c = parser.ReadNoSpace() + if len(tok) == 0 { + if c == 0 { + break + } + if c == ';' || c == '#' { + // Skip empty line or keyword without value. + parser.SkipLine() + } continue } - tok, isTerm, _ := reader.ReadUntil(ascii.Spaces, newLineTerms) - if isTerm { - // We found keyword without value. + _, c = parser.SkipHorizontalSpaces() + if c == '\n' || c == ';' || c == '#' { + // Skip empty line or keyword without value. + parser.SkipLine() continue } tok = ascii.ToLower(tok) - v := string(tok) - switch v { - case "domain": - rc.parseValue(reader, &rc.Domain) - case "search": - rc.parseSearch(reader) - case "nameserver": - v = "" - rc.parseValue(reader, &v) - if len(rc.NameServers) < 3 && len(v) > 0 { - rc.NameServers = append(rc.NameServers, v) + stok = string(tok) + + switch stok { + case `domain`: + tok, c = parser.ReadNoSpace() + if len(tok) != 0 { + rc.Domain = string(tok) + } + if c != '\n' { + parser.SkipLine() + } + + case `search`: + rc.parseSearch(parser) + + case `nameserver`: + tok, c = parser.ReadNoSpace() + if len(tok) != 0 { + if len(rc.NameServers) < 3 && len(tok) > 0 { + rc.NameServers = append(rc.NameServers, string(tok)) + } } - case "options": - rc.parseOptions(reader) + if c != '\n' { + parser.SkipLine() + } + + case `options`: + rc.parseOptions(parser) + default: - reader.SkipUntil(newLineTerms) + parser.SkipLine() } } rc.sanitize() } -func (rc *ResolvConf) parseValue(reader *libio.Reader, out *string) { - _, c := reader.SkipHorizontalSpace() - if c == '\n' || c == 0 { - return - } - - tok, isTerm, _ := reader.ReadUntil(ascii.Spaces, newLineTerms) - if len(tok) > 0 { - *out = string(tok) - } - - if !isTerm { - reader.SkipUntil(newLineTerms) - } -} +// parseSearch parse the "search" directive using the following format, +// +// search domain *(domain) +// +// The domain and search keywords are mutually exclusive. +// If more than one instance of these keywords is present, the last instance +// wins. +func (rc *ResolvConf) parseSearch(parser *libbytes.Parser) { + var ( + max = 6 + maxLen = 255 -// (1) The domain and search keywords are mutually exclusive. If more than -// one instance of these keywords is present, the last instance wins. -func (rc *ResolvConf) parseSearch(reader *libio.Reader) { - max := 6 - maxLen := 255 - var curLen int + curLen int + tok []byte + c byte + ) - // (1) rc.Search = nil for { - _, c := reader.SkipHorizontalSpace() - if c == '\n' || c == 0 { + tok, c = parser.ReadNoSpace() + + if curLen+len(tok) > maxLen { break } - tok, isTerm, _ := reader.ReadUntil(ascii.Spaces, newLineTerms) - if len(tok) > 0 { - if curLen+len(tok) > maxLen { - break - } + rc.Search = append(rc.Search, string(tok)) + if len(rc.Search) == max { + break + } - rc.Search = append(rc.Search, string(tok)) - if len(rc.Search) == max { - break - } + curLen += len(tok) - curLen += len(tok) - } - if isTerm { + if c == '\n' { break } } - - reader.SkipUntil(newLineTerms) + if c != '\n' { + parser.SkipLine() + } } -func (rc *ResolvConf) parseOptions(reader *libio.Reader) { +func (rc *ResolvConf) parseOptions(parser *libbytes.Parser) { var ( - c byte - isTerm bool - tok []byte + tok []byte + c byte ) + for { - _, c = reader.SkipHorizontalSpace() - if c == '\n' || c == 0 { + tok, c = parser.ReadNoSpace() + if len(tok) == 0 { break } - - tok, isTerm, _ = reader.ReadUntil(ascii.Spaces, newLineTerms) - if len(tok) > 0 { - rc.parseOptionsKV(tok) - } - if isTerm { + rc.parseOptionsKV(tok) + if c == '\n' { break } } + if c != '\n' { + parser.SkipLine() + } } func (rc *ResolvConf) parseOptionsKV(opt []byte) { - var k, v []byte - for x := 0; x < len(opt); x++ { - if opt[x] == ':' { - k = opt[:x] - if x+1 < len(opt) { - v = opt[x+1:] - } - break - } - } - if len(k) == 0 { - k = opt + var ( + kv = bytes.SplitN(opt, []byte{':'}, 2) + key = string(kv[0]) + + value string + ) + if len(kv) == 2 { + value = string(kv[1]) } - sk := string(k) - switch sk { - case "ndots": - rc.NDots, _ = strconv.Atoi(string(v)) - case "timeout": - rc.Timeout, _ = strconv.Atoi(string(v)) - case "attempts": - rc.Attempts, _ = strconv.Atoi(string(v)) + switch key { + case `ndots`: + rc.NDots, _ = strconv.Atoi(value) + case `timeout`: + rc.Timeout, _ = strconv.Atoi(value) + case `attempts`: + rc.Attempts, _ = strconv.Atoi(value) default: - if len(k) > 0 { - rc.OptMisc[sk] = true - } + rc.OptMisc[key] = true } } diff --git a/lib/net/resolvconf_test.go b/lib/net/resolvconf_test.go index 52d1bbf4..a9bb9a78 100644 --- a/lib/net/resolvconf_test.go +++ b/lib/net/resolvconf_test.go @@ -19,17 +19,17 @@ func TestNewResolvConf(t *testing.T) { exp *ResolvConf expErr string }{{ - desc: "With invalid file", - path: "", + desc: `With invalid file`, + path: ``, exp: nil, - expErr: "open : no such file or directory", + expErr: `NewResolvConf: open : no such file or directory`, }, { - desc: "From testdata/resolv.conf", - path: "testdata/resolv.conf", + desc: `From testdata/resolv.conf`, + path: `testdata/resolv.conf`, exp: &ResolvConf{ - Domain: "a", - Search: []string{"d", "e", "f", "g", "h", "i"}, - NameServers: []string{"127.0.0.1", "1.1.1.1", "2.2.2.2"}, + Domain: `a`, + Search: []string{`d`, `e`, `f`, `g`, `h`, `i`}, + NameServers: []string{`127.0.0.1`, `1.1.1.1`, `2.2.2.2`}, NDots: 1, Timeout: 5, Attempts: 2, @@ -42,10 +42,11 @@ func TestNewResolvConf(t *testing.T) { rc, err := NewResolvConf(c.path) if err != nil { - test.Assert(t, "expError", c.expErr, err.Error()) + test.Assert(t, `error`, c.expErr, err.Error()) + continue } - test.Assert(t, "exp", c.exp, rc) + test.Assert(t, `exp`, c.exp, rc) } } diff --git a/lib/net/testdata/resolv.conf b/lib/net/testdata/resolv.conf index 5776e055..ca92a48e 100644 --- a/lib/net/testdata/resolv.conf +++ b/lib/net/testdata/resolv.conf @@ -14,7 +14,7 @@ invalid keyword domain a b c search d e f g h i j -nameserver 127.0.0.1 +nameserver 127.0.0.1 # with comment. nameserver 1.1.1.1 |
