summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2023-04-09 20:49:09 +0700
committerShulhan <ms@kilabit.info>2023-04-09 20:49:09 +0700
commitace497bd8c22c718bedd18582db4ed07780d1a44 (patch)
tree314bda69caba9176d0c3a5bc2d1e712ca30b293f
parent1015cc4c739bed24999ad13c98b4c62f72cc11e3 (diff)
downloadpakakeh.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.go235
-rw-r--r--lib/net/resolvconf_test.go21
-rw-r--r--lib/net/testdata/resolv.conf2
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