github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/dnsproxy/resolvefile_unix.go (about) 1 //go:build !windows 2 3 package dnsproxy 4 5 import ( 6 "bufio" 7 "bytes" 8 "fmt" 9 "io" 10 "os" 11 "slices" 12 "strconv" 13 "strings" 14 15 "github.com/telepresenceio/telepresence/v2/pkg/ioutil" 16 ) 17 18 type ResolveFile struct { 19 Port int 20 Domain string 21 Nameservers []string 22 Search []string 23 Options []string 24 } 25 26 func ReadResolveFile(fileName string) (*ResolveFile, error) { 27 fl, err := os.Open(fileName) 28 if err != nil { 29 return nil, err 30 } 31 defer fl.Close() 32 sc := bufio.NewScanner(fl) 33 rf := ResolveFile{} 34 line := 0 35 36 onlyOne := func(key string) error { 37 return fmt.Errorf("%q must have a value at %s line %d", key, fileName, line) 38 } 39 40 for sc.Scan() { 41 line++ 42 txt := strings.TrimSpace(sc.Text()) 43 if len(txt) == 0 || strings.HasPrefix(txt, "#") { 44 continue 45 } 46 fields := strings.Fields(txt) 47 fc := len(fields) 48 if fc == 0 { 49 continue 50 } 51 key := fields[0] 52 if fc == 1 { 53 return nil, fmt.Errorf("%q must have a value at %s line %d", key, fileName, line) 54 } 55 value := fields[1] 56 switch key { 57 case "port": 58 if fc != 2 { 59 return nil, onlyOne(key) 60 } 61 rf.Port, err = strconv.Atoi(value) 62 if err != nil { 63 return nil, fmt.Errorf("%q is not a valid integer at %s line %d", key, fileName, line) 64 } 65 case "domain": 66 if fc != 2 { 67 return nil, onlyOne(key) 68 } 69 rf.Domain = value 70 case "nameserver": 71 if fc != 2 { 72 return nil, onlyOne(key) 73 } 74 rf.Nameservers = append(rf.Nameservers, value) 75 case "search": 76 rf.Search = fields[1:] 77 case "options": 78 rf.Options = fields[1:] 79 default: 80 // This reader doesn't do options just yet 81 return nil, fmt.Errorf("%q is not a recognized key at %s line %d", key, fileName, line) 82 } 83 } 84 return &rf, nil 85 } 86 87 func (r *ResolveFile) String() string { 88 var buf strings.Builder 89 _, _ = r.WriteTo(&buf) 90 return buf.String() 91 } 92 93 func (r *ResolveFile) Equals(o *ResolveFile) bool { 94 if r == nil || o == nil { 95 return r == o 96 } 97 return r.Port == o.Port && 98 r.Domain == o.Domain && 99 slices.Equal(r.Nameservers, o.Nameservers) && 100 slices.Equal(r.Search, o.Search) && 101 slices.Equal(r.Options, o.Options) 102 } 103 104 func (r *ResolveFile) Write(fileName string) error { 105 var buf bytes.Buffer 106 _, _ = r.WriteTo(&buf) 107 return os.WriteFile(fileName, buf.Bytes(), 0o644) 108 } 109 110 func (r *ResolveFile) WriteTo(buf io.Writer) (int64, error) { 111 n := ioutil.Println(buf, "# Generated by telepresence\n") 112 if r.Port > 0 { 113 n += ioutil.Printf(buf, "port %d\n", r.Port) 114 } 115 if r.Domain != "" { 116 n += ioutil.Printf(buf, "domain %s\n", r.Domain) 117 } 118 for _, ns := range r.Nameservers { 119 n += ioutil.Printf(buf, "nameserver %s\n", ns) 120 } 121 if len(r.Search) > 0 { 122 n += ioutil.Printf(buf, "search %s\n", strings.Join(r.Search, " ")) 123 } 124 if len(r.Options) > 0 { 125 n += ioutil.Printf(buf, "options %s\n", strings.Join(r.Options, " ")) 126 } 127 return int64(n), nil 128 }