github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/net/lookup_plan9.go (about) 1 // Copyright 2011 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 package net 6 7 import ( 8 "context" 9 "errors" 10 "io" 11 "os" 12 ) 13 14 func query(ctx context.Context, filename, query string, bufSize int) (res []string, err error) { 15 file, err := os.OpenFile(filename, os.O_RDWR, 0) 16 if err != nil { 17 return 18 } 19 defer file.Close() 20 21 _, err = file.Seek(0, io.SeekStart) 22 if err != nil { 23 return 24 } 25 _, err = file.WriteString(query) 26 if err != nil { 27 return 28 } 29 _, err = file.Seek(0, io.SeekStart) 30 if err != nil { 31 return 32 } 33 buf := make([]byte, bufSize) 34 for { 35 n, _ := file.Read(buf) 36 if n <= 0 { 37 break 38 } 39 res = append(res, string(buf[:n])) 40 } 41 return 42 } 43 44 func queryCS(ctx context.Context, net, host, service string) (res []string, err error) { 45 switch net { 46 case "tcp4", "tcp6": 47 net = "tcp" 48 case "udp4", "udp6": 49 net = "udp" 50 } 51 if host == "" { 52 host = "*" 53 } 54 return query(ctx, netdir+"/cs", net+"!"+host+"!"+service, 128) 55 } 56 57 func queryCS1(ctx context.Context, net string, ip IP, port int) (clone, dest string, err error) { 58 ips := "*" 59 if len(ip) != 0 && !ip.IsUnspecified() { 60 ips = ip.String() 61 } 62 lines, err := queryCS(ctx, net, ips, itoa(port)) 63 if err != nil { 64 return 65 } 66 f := getFields(lines[0]) 67 if len(f) < 2 { 68 return "", "", errors.New("bad response from ndb/cs") 69 } 70 clone, dest = f[0], f[1] 71 return 72 } 73 74 func queryDNS(ctx context.Context, addr string, typ string) (res []string, err error) { 75 return query(ctx, netdir+"/dns", addr+" "+typ, 1024) 76 } 77 78 // toLower returns a lower-case version of in. Restricting us to 79 // ASCII is sufficient to handle the IP protocol names and allow 80 // us to not depend on the strings and unicode packages. 81 func toLower(in string) string { 82 for _, c := range in { 83 if 'A' <= c && c <= 'Z' { 84 // Has upper case; need to fix. 85 out := []byte(in) 86 for i := 0; i < len(in); i++ { 87 c := in[i] 88 if 'A' <= c && c <= 'Z' { 89 c += 'a' - 'A' 90 } 91 out[i] = c 92 } 93 return string(out) 94 } 95 } 96 return in 97 } 98 99 // lookupProtocol looks up IP protocol name and returns 100 // the corresponding protocol number. 101 func lookupProtocol(ctx context.Context, name string) (proto int, err error) { 102 lines, err := query(ctx, netdir+"/cs", "!protocol="+toLower(name), 128) 103 if err != nil { 104 return 0, err 105 } 106 if len(lines) == 0 { 107 return 0, UnknownNetworkError(name) 108 } 109 f := getFields(lines[0]) 110 if len(f) < 2 { 111 return 0, UnknownNetworkError(name) 112 } 113 s := f[1] 114 if n, _, ok := dtoi(s, byteIndex(s, '=')+1); ok { 115 return n, nil 116 } 117 return 0, UnknownNetworkError(name) 118 } 119 120 func lookupHost(ctx context.Context, host string) (addrs []string, err error) { 121 // Use netdir/cs instead of netdir/dns because cs knows about 122 // host names in local network (e.g. from /lib/ndb/local) 123 lines, err := queryCS(ctx, "net", host, "1") 124 if err != nil { 125 return 126 } 127 loop: 128 for _, line := range lines { 129 f := getFields(line) 130 if len(f) < 2 { 131 continue 132 } 133 addr := f[1] 134 if i := byteIndex(addr, '!'); i >= 0 { 135 addr = addr[:i] // remove port 136 } 137 if ParseIP(addr) == nil { 138 continue 139 } 140 // only return unique addresses 141 for _, a := range addrs { 142 if a == addr { 143 continue loop 144 } 145 } 146 addrs = append(addrs, addr) 147 } 148 return 149 } 150 151 func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) { 152 lits, err := lookupHost(ctx, host) 153 if err != nil { 154 return 155 } 156 for _, lit := range lits { 157 host, zone := splitHostZone(lit) 158 if ip := ParseIP(host); ip != nil { 159 addr := IPAddr{IP: ip, Zone: zone} 160 addrs = append(addrs, addr) 161 } 162 } 163 return 164 } 165 166 func lookupPort(ctx context.Context, network, service string) (port int, err error) { 167 switch network { 168 case "tcp4", "tcp6": 169 network = "tcp" 170 case "udp4", "udp6": 171 network = "udp" 172 } 173 lines, err := queryCS(ctx, network, "127.0.0.1", service) 174 if err != nil { 175 return 176 } 177 unknownPortError := &AddrError{Err: "unknown port", Addr: network + "/" + service} 178 if len(lines) == 0 { 179 return 0, unknownPortError 180 } 181 f := getFields(lines[0]) 182 if len(f) < 2 { 183 return 0, unknownPortError 184 } 185 s := f[1] 186 if i := byteIndex(s, '!'); i >= 0 { 187 s = s[i+1:] // remove address 188 } 189 if n, _, ok := dtoi(s, 0); ok { 190 return n, nil 191 } 192 return 0, unknownPortError 193 } 194 195 func lookupCNAME(ctx context.Context, name string) (cname string, err error) { 196 lines, err := queryDNS(ctx, name, "cname") 197 if err != nil { 198 return 199 } 200 if len(lines) > 0 { 201 if f := getFields(lines[0]); len(f) >= 3 { 202 return f[2] + ".", nil 203 } 204 } 205 return "", errors.New("bad response from ndb/dns") 206 } 207 208 func lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) { 209 var target string 210 if service == "" && proto == "" { 211 target = name 212 } else { 213 target = "_" + service + "._" + proto + "." + name 214 } 215 lines, err := queryDNS(ctx, target, "srv") 216 if err != nil { 217 return 218 } 219 for _, line := range lines { 220 f := getFields(line) 221 if len(f) < 6 { 222 continue 223 } 224 port, _, portOk := dtoi(f[4], 0) 225 priority, _, priorityOk := dtoi(f[3], 0) 226 weight, _, weightOk := dtoi(f[2], 0) 227 if !(portOk && priorityOk && weightOk) { 228 continue 229 } 230 addrs = append(addrs, &SRV{absDomainName([]byte(f[5])), uint16(port), uint16(priority), uint16(weight)}) 231 cname = absDomainName([]byte(f[0])) 232 } 233 byPriorityWeight(addrs).sort() 234 return 235 } 236 237 func lookupMX(ctx context.Context, name string) (mx []*MX, err error) { 238 lines, err := queryDNS(ctx, name, "mx") 239 if err != nil { 240 return 241 } 242 for _, line := range lines { 243 f := getFields(line) 244 if len(f) < 4 { 245 continue 246 } 247 if pref, _, ok := dtoi(f[2], 0); ok { 248 mx = append(mx, &MX{absDomainName([]byte(f[3])), uint16(pref)}) 249 } 250 } 251 byPref(mx).sort() 252 return 253 } 254 255 func lookupNS(ctx context.Context, name string) (ns []*NS, err error) { 256 lines, err := queryDNS(ctx, name, "ns") 257 if err != nil { 258 return 259 } 260 for _, line := range lines { 261 f := getFields(line) 262 if len(f) < 3 { 263 continue 264 } 265 ns = append(ns, &NS{absDomainName([]byte(f[2]))}) 266 } 267 return 268 } 269 270 func lookupTXT(ctx context.Context, name string) (txt []string, err error) { 271 lines, err := queryDNS(ctx, name, "txt") 272 if err != nil { 273 return 274 } 275 for _, line := range lines { 276 if i := byteIndex(line, '\t'); i >= 0 { 277 txt = append(txt, absDomainName([]byte(line[i+1:]))) 278 } 279 } 280 return 281 } 282 283 func lookupAddr(ctx context.Context, addr string) (name []string, err error) { 284 arpa, err := reverseaddr(addr) 285 if err != nil { 286 return 287 } 288 lines, err := queryDNS(ctx, arpa, "ptr") 289 if err != nil { 290 return 291 } 292 for _, line := range lines { 293 f := getFields(line) 294 if len(f) < 3 { 295 continue 296 } 297 name = append(name, absDomainName([]byte(f[2]))) 298 } 299 return 300 }