diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-02-14 13:23:51 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-02-14 13:23:51 +0100 |
commit | 758ff64c69e34965f8af5b2d6ffd65e8d7ab2150 (patch) | |
tree | 6d6b34f8c678862fe9b56c945a7b63f68502c245 /src/pkg/net | |
parent | 3e45412327a2654a77944249962b3652e6142299 (diff) | |
download | golang-758ff64c69e34965f8af5b2d6ffd65e8d7ab2150.tar.gz |
Imported Upstream version 2011-02-01.1upstream/2011-02-01.1
Diffstat (limited to 'src/pkg/net')
-rw-r--r-- | src/pkg/net/dial.go | 6 | ||||
-rw-r--r-- | src/pkg/net/dialgoogle_test.go | 38 | ||||
-rw-r--r-- | src/pkg/net/dnsclient.go | 81 | ||||
-rw-r--r-- | src/pkg/net/dnsname_test.go | 14 | ||||
-rw-r--r-- | src/pkg/net/fd.go | 17 | ||||
-rw-r--r-- | src/pkg/net/fd_windows.go | 139 | ||||
-rw-r--r-- | src/pkg/net/hosts.go | 31 | ||||
-rw-r--r-- | src/pkg/net/net_test.go | 49 | ||||
-rw-r--r-- | src/pkg/net/port.go | 4 | ||||
-rw-r--r-- | src/pkg/net/resolv_windows.go | 29 | ||||
-rw-r--r-- | src/pkg/net/server_test.go | 21 | ||||
-rw-r--r-- | src/pkg/net/timeout_test.go | 17 | ||||
-rw-r--r-- | src/pkg/net/unixsock.go | 61 |
13 files changed, 411 insertions, 96 deletions
diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go index 9a4c8f688..03b9d87be 100644 --- a/src/pkg/net/dial.go +++ b/src/pkg/net/dial.go @@ -59,7 +59,7 @@ func Dial(net, laddr, raddr string) (c Conn, err os.Error) { return nil, err } return c, nil - case "unix", "unixgram": + case "unix", "unixgram", "unixpacket": var la, ra *UnixAddr if raddr != "" { if ra, err = ResolveUnixAddr(net, raddr); err != nil { @@ -102,7 +102,7 @@ Error: // Listen announces on the local network address laddr. // The network string net must be a stream-oriented -// network: "tcp", "tcp4", "tcp6", or "unix". +// network: "tcp", "tcp4", "tcp6", or "unix", or "unixpacket". func Listen(net, laddr string) (l Listener, err os.Error) { switch net { case "tcp", "tcp4", "tcp6": @@ -117,7 +117,7 @@ func Listen(net, laddr string) (l Listener, err os.Error) { return nil, err } return l, nil - case "unix": + case "unix", "unixpacket": var la *UnixAddr if laddr != "" { if la, err = ResolveUnixAddr(net, laddr); err != nil { diff --git a/src/pkg/net/dialgoogle_test.go b/src/pkg/net/dialgoogle_test.go index 47a478a8f..a432800cf 100644 --- a/src/pkg/net/dialgoogle_test.go +++ b/src/pkg/net/dialgoogle_test.go @@ -6,12 +6,14 @@ package net import ( "flag" + "fmt" "io" + "strings" "syscall" "testing" ) -// If an IPv6 tunnel is running (see go/stubl), we can try dialing a real IPv6 address. +// If an IPv6 tunnel is running, we can try dialing a real IPv6 address. var ipv6 = flag.Bool("ipv6", false, "assume ipv6 tunnel is present") // fd is already connected to the destination, port 80. @@ -40,16 +42,16 @@ func doDial(t *testing.T, network, addr string) { } var googleaddrs = []string{ - "74.125.19.99:80", + "%d.%d.%d.%d:80", "www.google.com:80", - "74.125.19.99:http", + "%d.%d.%d.%d:http", "www.google.com:http", - "074.125.019.099:0080", - "[::ffff:74.125.19.99]:80", - "[::ffff:4a7d:1363]:80", - "[0:0:0:0:0000:ffff:74.125.19.99]:80", - "[0:0:0:0:000000:ffff:74.125.19.99]:80", - "[0:0:0:0:0:ffff::74.125.19.99]:80", + "%03d.%03d.%03d.%03d:0080", + "[::ffff:%d.%d.%d.%d]:80", + "[::ffff:%02x%02x:%02x%02x]:80", + "[0:0:0:0:0000:ffff:%d.%d.%d.%d]:80", + "[0:0:0:0:000000:ffff:%d.%d.%d.%d]:80", + "[0:0:0:0:0:ffff::%d.%d.%d.%d]:80", "[2001:4860:0:2001::68]:80", // ipv6.google.com; removed if ipv6 flag not set } @@ -59,6 +61,24 @@ func TestDialGoogle(t *testing.T) { googleaddrs[len(googleaddrs)-1] = "" } + // Insert an actual IP address for google.com + // into the table. + + _, addrs, err := LookupHost("www.google.com") + if err != nil { + t.Fatalf("lookup www.google.com: %v", err) + } + if len(addrs) == 0 { + t.Fatalf("no addresses for www.google.com") + } + ip := ParseIP(addrs[0]).To4() + + for i, s := range googleaddrs { + if strings.Contains(s, "%") { + googleaddrs[i] = fmt.Sprintf(s, ip[0], ip[1], ip[2], ip[3]) + } + } + for i := 0; i < len(googleaddrs); i++ { addr := googleaddrs[i] if addr == "" { diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go index f1cd47bb1..3252dd454 100644 --- a/src/pkg/net/dnsclient.go +++ b/src/pkg/net/dnsclient.go @@ -15,6 +15,8 @@ package net import ( + "bytes" + "fmt" "os" "rand" "sync" @@ -96,18 +98,18 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, os.Er // Find answer for name in dns message. // On return, if err == nil, addrs != nil. -func answer(name, server string, dns *dnsMsg, qtype uint16) (addrs []dnsRR, err os.Error) { +func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { addrs = make([]dnsRR, 0, len(dns.answer)) if dns.rcode == dnsRcodeNameError && dns.recursion_available { - return nil, &DNSError{Error: noSuchHost, Name: name} + return "", nil, &DNSError{Error: noSuchHost, Name: name} } if dns.rcode != dnsRcodeSuccess { // None of the error codes make sense // for the query we sent. If we didn't get // a name error and we didn't get success, // the server is behaving incorrectly. - return nil, &DNSError{Error: "server misbehaving", Name: name, Server: server} + return "", nil, &DNSError{Error: "server misbehaving", Name: name, Server: server} } // Look for the name. @@ -135,19 +137,19 @@ Cname: } } if len(addrs) == 0 { - return nil, &DNSError{Error: noSuchHost, Name: name, Server: server} + return "", nil, &DNSError{Error: noSuchHost, Name: name, Server: server} } - return addrs, nil + return name, addrs, nil } - return nil, &DNSError{Error: "too many redirects", Name: name, Server: server} + return "", nil, &DNSError{Error: "too many redirects", Name: name, Server: server} } // Do a lookup for a single name, which must be rooted // (otherwise answer will not find the answers). -func tryOneName(cfg *dnsConfig, name string, qtype uint16) (addrs []dnsRR, err os.Error) { +func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { if len(cfg.servers) == 0 { - return nil, &DNSError{Error: "no DNS servers", Name: name} + return "", nil, &DNSError{Error: "no DNS servers", Name: name} } for i := 0; i < len(cfg.servers); i++ { // Calling Dial here is scary -- we have to be sure @@ -168,7 +170,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (addrs []dnsRR, err o err = merr continue } - addrs, err = answer(name, server, msg, qtype) + cname, addrs, err = answer(name, server, msg, qtype) if err == nil || err.(*DNSError).Error == noSuchHost { break } @@ -259,9 +261,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro rname += "." } // Can try as ordinary name. - addrs, err = tryOneName(cfg, rname, qtype) + cname, addrs, err = tryOneName(cfg, rname, qtype) if err == nil { - cname = rname return } } @@ -275,9 +276,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro if rname[len(rname)-1] != '.' { rname += "." } - addrs, err = tryOneName(cfg, rname, qtype) + cname, addrs, err = tryOneName(cfg, rname, qtype) if err == nil { - cname = rname return } } @@ -287,9 +287,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro if !rooted { rname += "." } - addrs, err = tryOneName(cfg, rname, qtype) + cname, addrs, err = tryOneName(cfg, rname, qtype) if err == nil { - cname = rname return } return @@ -357,9 +356,59 @@ func LookupMX(name string) (entries []*MX, err os.Error) { return } entries = make([]*MX, len(records)) - for i := 0; i < len(records); i++ { + for i := range records { r := records[i].(*dnsRR_MX) entries[i] = &MX{r.Mx, r.Pref} } return } + +// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP +// address addr suitable for rDNS (PTR) record lookup or an error if it fails +// to parse the IP address. +func reverseaddr(addr string) (arpa string, err os.Error) { + ip := ParseIP(addr) + if ip == nil { + return "", &DNSError{Error: "unrecognized address", Name: addr} + } + if ip.To4() != nil { + return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil + } + // Must be IPv6 + var buf bytes.Buffer + // Add it, in reverse, to the buffer + for i := len(ip) - 1; i >= 0; i-- { + s := fmt.Sprintf("%02x", ip[i]) + buf.WriteByte(s[1]) + buf.WriteByte('.') + buf.WriteByte(s[0]) + buf.WriteByte('.') + } + // Append "ip6.arpa." and return (buf already has the final .) + return buf.String() + "ip6.arpa.", nil +} + +// LookupAddr performs a reverse lookup for the given address, returning a list +// of names mapping to that address. +func LookupAddr(addr string) (name []string, err os.Error) { + name = lookupStaticAddr(addr) + if len(name) > 0 { + return + } + var arpa string + arpa, err = reverseaddr(addr) + if err != nil { + return + } + var records []dnsRR + _, records, err = lookup(arpa, dnsTypePTR) + if err != nil { + return + } + name = make([]string, len(records)) + for i := range records { + r := records[i].(*dnsRR_PTR) + name[i] = r.Ptr + } + return +} diff --git a/src/pkg/net/dnsname_test.go b/src/pkg/net/dnsname_test.go index f4089c5db..0c1a62518 100644 --- a/src/pkg/net/dnsname_test.go +++ b/src/pkg/net/dnsname_test.go @@ -27,7 +27,7 @@ var tests = []testCase{ {"a.b..com", false}, } -func getTestCases(ch chan<- *testCase) { +func getTestCases(ch chan<- testCase) { defer close(ch) var char59 = "" var char63 = "" @@ -39,17 +39,17 @@ func getTestCases(ch chan<- *testCase) { char64 = char63 + "a" for _, tc := range tests { - ch <- &tc + ch <- tc } - ch <- &testCase{char63 + ".com", true} - ch <- &testCase{char64 + ".com", false} + ch <- testCase{char63 + ".com", true} + ch <- testCase{char64 + ".com", false} // 255 char name is fine: - ch <- &testCase{char59 + "." + char63 + "." + char63 + "." + + ch <- testCase{char59 + "." + char63 + "." + char63 + "." + char63 + ".com", true} // 256 char name is bad: - ch <- &testCase{char59 + "a." + char63 + "." + char63 + "." + + ch <- testCase{char59 + "a." + char63 + "." + char63 + "." + char63 + ".com", false} } @@ -58,7 +58,7 @@ func TestDNSNames(t *testing.T) { if runtime.GOOS == "windows" { return } - ch := make(chan *testCase) + ch := make(chan testCase) go getTestCases(ch) for tc := range ch { if isDomainName(tc.name) != tc.result { diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go index 5ec91845d..2ba9296f3 100644 --- a/src/pkg/net/fd.go +++ b/src/pkg/net/fd.go @@ -220,11 +220,16 @@ func (s *pollServer) Run() { nn, _ = s.pr.Read(scratch[0:]) } // Read from channels - for fd, ok := <-s.cr; ok; fd, ok = <-s.cr { - s.AddFD(fd, 'r') - } - for fd, ok := <-s.cw; ok; fd, ok = <-s.cw { - s.AddFD(fd, 'w') + Update: + for { + select { + case fd := <-s.cr: + s.AddFD(fd, 'r') + case fd := <-s.cw: + s.AddFD(fd, 'w') + default: + break Update + } } } else { netfd := s.LookupFD(fd, mode) @@ -417,7 +422,7 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S var oserr os.Error for { var errno int - n, oobn, flags, errno = syscall.Recvmsg(fd.sysfd, p, oob, sa, 0) + n, oobn, flags, sa, errno = syscall.Recvmsg(fd.sysfd, p, oob, 0) if errno == syscall.EAGAIN && fd.rdeadline >= 0 { pollserver.WaitRead(fd) continue diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go index 72685d612..9b91eb398 100644 --- a/src/pkg/net/fd_windows.go +++ b/src/pkg/net/fd_windows.go @@ -6,14 +6,13 @@ package net import ( "os" + "runtime" "sync" "syscall" + "time" "unsafe" - "runtime" ) -// BUG(brainman): The Windows implementation does not implement SetTimeout. - // IO completion result parameters. type ioResult struct { key uint32 @@ -79,6 +78,8 @@ type ioPacket struct { // Link to the io owner. c chan *ioResult + + w *syscall.WSABuf } func (s *pollServer) getCompletedIO() (ov *syscall.Overlapped, result *ioResult, err os.Error) { @@ -126,6 +127,8 @@ func startServer() { panic("Start pollServer: " + err.String() + "\n") } pollserver = p + + go timeoutIO() } var initErr os.Error @@ -143,8 +146,8 @@ func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err sysfd: fd, family: family, proto: proto, - cr: make(chan *ioResult), - cw: make(chan *ioResult), + cr: make(chan *ioResult, 1), + cw: make(chan *ioResult, 1), net: net, laddr: laddr, raddr: raddr, @@ -199,6 +202,80 @@ func newWSABuf(p []byte) *syscall.WSABuf { return &syscall.WSABuf{uint32(len(p)), p0} } +func waitPacket(fd *netFD, pckt *ioPacket, mode int) (r *ioResult) { + var delta int64 + if mode == 'r' { + delta = fd.rdeadline_delta + } + if mode == 'w' { + delta = fd.wdeadline_delta + } + if delta <= 0 { + return <-pckt.c + } + + select { + case r = <-pckt.c: + case <-time.After(delta): + a := &arg{f: cancel, fd: fd, pckt: pckt, c: make(chan int)} + ioChan <- a + <-a.c + r = <-pckt.c + if r.errno == 995 { // IO Canceled + r.errno = syscall.EWOULDBLOCK + } + } + return r +} + +const ( + read = iota + readfrom + write + writeto + cancel +) + +type arg struct { + f int + fd *netFD + pckt *ioPacket + done *uint32 + flags *uint32 + rsa *syscall.RawSockaddrAny + size *int32 + sa *syscall.Sockaddr + c chan int +} + +var ioChan chan *arg = make(chan *arg) + +func timeoutIO() { + // CancelIO only cancels all pending input and output (I/O) operations that are + // issued by the calling thread for the specified file, does not cancel I/O + // operations that other threads issue for a file handle. So we need do all timeout + // I/O in single OS thread. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + for { + o := <-ioChan + var e int + switch o.f { + case read: + e = syscall.WSARecv(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, o.flags, &o.pckt.o, nil) + case readfrom: + e = syscall.WSARecvFrom(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, o.flags, o.rsa, o.size, &o.pckt.o, nil) + case write: + e = syscall.WSASend(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, uint32(0), &o.pckt.o, nil) + case writeto: + e = syscall.WSASendto(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, 0, *o.sa, &o.pckt.o, nil) + case cancel: + _, e = syscall.CancelIo(uint32(o.fd.sysfd)) + } + o.c <- e + } +} + func (fd *netFD) Read(p []byte) (n int, err os.Error) { if fd == nil { return 0, os.EINVAL @@ -213,9 +290,17 @@ func (fd *netFD) Read(p []byte) (n int, err os.Error) { // Submit receive request. var pckt ioPacket pckt.c = fd.cr + pckt.w = newWSABuf(p) var done uint32 flags := uint32(0) - e := syscall.WSARecv(uint32(fd.sysfd), newWSABuf(p), 1, &done, &flags, &pckt.o, nil) + var e int + if fd.rdeadline_delta > 0 { + a := &arg{f: read, fd: fd, pckt: &pckt, done: &done, flags: &flags, c: make(chan int)} + ioChan <- a + e = <-a.c + } else { + e = syscall.WSARecv(uint32(fd.sysfd), pckt.w, 1, &done, &flags, &pckt.o, nil) + } switch e { case 0: // IO completed immediately, but we need to get our completion message anyway. @@ -225,7 +310,7 @@ func (fd *netFD) Read(p []byte) (n int, err os.Error) { return 0, &OpError{"WSARecv", fd.net, fd.laddr, os.Errno(e)} } // Wait for our request to complete. - r := <-pckt.c + r := waitPacket(fd, &pckt, 'r') if r.errno != 0 { err = &OpError{"WSARecv", fd.net, fd.laddr, os.Errno(r.errno)} } @@ -253,11 +338,19 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err os.Error) { // Submit receive request. var pckt ioPacket pckt.c = fd.cr + pckt.w = newWSABuf(p) var done uint32 flags := uint32(0) var rsa syscall.RawSockaddrAny l := int32(unsafe.Sizeof(rsa)) - e := syscall.WSARecvFrom(uint32(fd.sysfd), newWSABuf(p), 1, &done, &flags, &rsa, &l, &pckt.o, nil) + var e int + if fd.rdeadline_delta > 0 { + a := &arg{f: readfrom, fd: fd, pckt: &pckt, done: &done, flags: &flags, rsa: &rsa, size: &l, c: make(chan int)} + ioChan <- a + e = <-a.c + } else { + e = syscall.WSARecvFrom(uint32(fd.sysfd), pckt.w, 1, &done, &flags, &rsa, &l, &pckt.o, nil) + } switch e { case 0: // IO completed immediately, but we need to get our completion message anyway. @@ -267,7 +360,7 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err os.Error) { return 0, nil, &OpError{"WSARecvFrom", fd.net, fd.laddr, os.Errno(e)} } // Wait for our request to complete. - r := <-pckt.c + r := waitPacket(fd, &pckt, 'r') if r.errno != 0 { err = &OpError{"WSARecvFrom", fd.net, fd.laddr, os.Errno(r.errno)} } @@ -290,8 +383,16 @@ func (fd *netFD) Write(p []byte) (n int, err os.Error) { // Submit send request. var pckt ioPacket pckt.c = fd.cw + pckt.w = newWSABuf(p) var done uint32 - e := syscall.WSASend(uint32(fd.sysfd), newWSABuf(p), 1, &done, uint32(0), &pckt.o, nil) + var e int + if fd.wdeadline_delta > 0 { + a := &arg{f: write, fd: fd, pckt: &pckt, done: &done, c: make(chan int)} + ioChan <- a + e = <-a.c + } else { + e = syscall.WSASend(uint32(fd.sysfd), pckt.w, 1, &done, uint32(0), &pckt.o, nil) + } switch e { case 0: // IO completed immediately, but we need to get our completion message anyway. @@ -301,7 +402,7 @@ func (fd *netFD) Write(p []byte) (n int, err os.Error) { return 0, &OpError{"WSASend", fd.net, fd.laddr, os.Errno(e)} } // Wait for our request to complete. - r := <-pckt.c + r := waitPacket(fd, &pckt, 'w') if r.errno != 0 { err = &OpError{"WSASend", fd.net, fd.laddr, os.Errno(r.errno)} } @@ -326,8 +427,16 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err os.Error) { // Submit send request. var pckt ioPacket pckt.c = fd.cw + pckt.w = newWSABuf(p) var done uint32 - e := syscall.WSASendto(uint32(fd.sysfd), newWSABuf(p), 1, &done, 0, sa, &pckt.o, nil) + var e int + if fd.wdeadline_delta > 0 { + a := &arg{f: writeto, fd: fd, pckt: &pckt, done: &done, sa: &sa, c: make(chan int)} + ioChan <- a + e = <-a.c + } else { + e = syscall.WSASendto(uint32(fd.sysfd), pckt.w, 1, &done, 0, sa, &pckt.o, nil) + } switch e { case 0: // IO completed immediately, but we need to get our completion message anyway. @@ -337,7 +446,7 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err os.Error) { return 0, &OpError{"WSASendTo", fd.net, fd.laddr, os.Errno(e)} } // Wait for our request to complete. - r := <-pckt.c + r := waitPacket(fd, &pckt, 'w') if r.errno != 0 { err = &OpError{"WSASendTo", fd.net, fd.laddr, os.Errno(r.errno)} } @@ -410,8 +519,8 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. sysfd: s, family: fd.family, proto: fd.proto, - cr: make(chan *ioResult), - cw: make(chan *ioResult), + cr: make(chan *ioResult, 1), + cw: make(chan *ioResult, 1), net: fd.net, laddr: laddr, raddr: raddr, diff --git a/src/pkg/net/hosts.go b/src/pkg/net/hosts.go index 556d57f11..8525f578d 100644 --- a/src/pkg/net/hosts.go +++ b/src/pkg/net/hosts.go @@ -19,16 +19,18 @@ var hostsPath = "/etc/hosts" // Simple cache. var hosts struct { sync.Mutex - data map[string][]string - time int64 - path string + byName map[string][]string + byAddr map[string][]string + time int64 + path string } func readHosts() { now, _, _ := os.Time() hp := hostsPath - if len(hosts.data) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp { + if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp { hs := make(map[string][]string) + is := make(map[string][]string) var file *file if file, _ = open(hp); file == nil { return @@ -45,12 +47,14 @@ func readHosts() { for i := 1; i < len(f); i++ { h := f[i] hs[h] = append(hs[h], f[0]) + is[f[0]] = append(is[f[0]], h) } } // Update the data cache. hosts.time, _, _ = os.Time() hosts.path = hp - hosts.data = hs + hosts.byName = hs + hosts.byAddr = is file.close() } } @@ -60,10 +64,23 @@ func lookupStaticHost(host string) []string { hosts.Lock() defer hosts.Unlock() readHosts() - if len(hosts.data) != 0 { - if ips, ok := hosts.data[host]; ok { + if len(hosts.byName) != 0 { + if ips, ok := hosts.byName[host]; ok { return ips } } return nil } + +// rlookupStaticHosts looks up the hosts for the given address from /etc/hosts. +func lookupStaticAddr(addr string) []string { + hosts.Lock() + defer hosts.Unlock() + readHosts() + if len(hosts.byAddr) != 0 { + if hosts, ok := hosts.byAddr[addr]; ok { + return hosts + } + } + return nil +} diff --git a/src/pkg/net/net_test.go b/src/pkg/net/net_test.go index b303254c6..1e6e99eec 100644 --- a/src/pkg/net/net_test.go +++ b/src/pkg/net/net_test.go @@ -7,6 +7,7 @@ package net import ( "flag" "regexp" + "runtime" "testing" ) @@ -52,6 +53,14 @@ var dialErrorTests = []DialErrorTest{ "unix", "", "/etc/", "dial unix /etc/: (permission denied|socket operation on non-socket|connection refused)", }, + { + "unixpacket", "", "/etc/file-not-found", + "dial unixpacket /etc/file-not-found: no such file or directory", + }, + { + "unixpacket", "", "/etc/", + "dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)", + }, } func TestDialError(t *testing.T) { @@ -75,3 +84,43 @@ func TestDialError(t *testing.T) { } } } + +var revAddrTests = []struct { + Addr string + Reverse string + ErrPrefix string +}{ + {"1.2.3.4", "4.3.2.1.in-addr.arpa.", ""}, + {"245.110.36.114", "114.36.110.245.in-addr.arpa.", ""}, + {"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa.", ""}, + {"::1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", ""}, + {"1::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.ip6.arpa.", ""}, + {"1234:567::89a:bcde", "e.d.c.b.a.9.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.6.5.0.4.3.2.1.ip6.arpa.", ""}, + {"1234:567:fefe:bcbc:adad:9e4a:89a:bcde", "e.d.c.b.a.9.8.0.a.4.e.9.d.a.d.a.c.b.c.b.e.f.e.f.7.6.5.0.4.3.2.1.ip6.arpa.", ""}, + {"1.2.3", "", "unrecognized address"}, + {"1.2.3.4.5", "", "unrecognized address"}, + {"1234:567:bcbca::89a:bcde", "", "unrecognized address"}, + {"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"}, +} + +func TestReverseAddress(t *testing.T) { + if runtime.GOOS == "windows" { + return + } + for i, tt := range revAddrTests { + a, e := reverseaddr(tt.Addr) + if len(tt.ErrPrefix) > 0 && e == nil { + t.Errorf("#%d: expected %q, got <nil> (error)", i, tt.ErrPrefix) + continue + } + if len(tt.ErrPrefix) == 0 && e != nil { + t.Errorf("#%d: expected <nil>, got %q (error)", i, e) + } + if e != nil && e.(*DNSError).Error != tt.ErrPrefix { + t.Errorf("#%d: expected %q, got %q (mismatched error)", i, tt.ErrPrefix, e.(*DNSError).Error) + } + if a != tt.Reverse { + t.Errorf("#%d: expected %q, got %q (reverse address)", i, tt.Reverse, a) + } + } +} diff --git a/src/pkg/net/port.go b/src/pkg/net/port.go index cd18d2b42..7d25058b2 100644 --- a/src/pkg/net/port.go +++ b/src/pkg/net/port.go @@ -18,7 +18,9 @@ var onceReadServices sync.Once func readServices() { services = make(map[string]map[string]int) var file *file - file, servicesError = open("/etc/services") + if file, servicesError = open("/etc/services"); servicesError != nil { + return + } for line, ok := file.readLine(); ok; line, ok = file.readLine() { // "http 80/tcp www www-http # World Wide Web HTTP" if i := byteIndex(line, '#'); i >= 0 { diff --git a/src/pkg/net/resolv_windows.go b/src/pkg/net/resolv_windows.go index d5292b8be..f3d854ff2 100644 --- a/src/pkg/net/resolv_windows.go +++ b/src/pkg/net/resolv_windows.go @@ -78,6 +78,35 @@ func LookupPort(network, service string) (port int, err os.Error) { return int(syscall.Ntohs(s.Port)), nil } +// TODO(brainman): Following code is only to get tests running. + func isDomainName(s string) bool { panic("unimplemented") } + +func reverseaddr(addr string) (arpa string, err os.Error) { + panic("unimplemented") +} + +// DNSError represents a DNS lookup error. +type DNSError struct { + Error string // description of the error + Name string // name looked for + Server string // server used + IsTimeout bool +} + +func (e *DNSError) String() string { + if e == nil { + return "<nil>" + } + s := "lookup " + e.Name + if e.Server != "" { + s += " on " + e.Server + } + s += ": " + e.Error + return s +} + +func (e *DNSError) Timeout() bool { return e.IsTimeout } +func (e *DNSError) Temporary() bool { return e.IsTimeout } diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go index 46bedaa5b..3dda500e5 100644 --- a/src/pkg/net/server_test.go +++ b/src/pkg/net/server_test.go @@ -25,7 +25,7 @@ func runEcho(fd io.ReadWriter, done chan<- int) { for { n, err := fd.Read(buf[0:]) - if err != nil || n == 0 { + if err != nil || n == 0 || string(buf[:n]) == "END" { break } fd.Write(buf[0:n]) @@ -79,6 +79,13 @@ func connect(t *testing.T, network, addr string, isEmpty bool) { if n != len(b) || err1 != nil { t.Fatalf("fd.Read() = %d, %v (want %d, nil)", n, err1, len(b)) } + + // Send explicit ending for unixpacket. + // Older Linux kernels do stop reads on close. + if network == "unixpacket" { + fd.Write([]byte("END")) + } + fd.Close() } @@ -117,8 +124,11 @@ func TestUnixServer(t *testing.T) { doTest(t, "unix", "/tmp/gotest.net", "/tmp/gotest.net") os.Remove("/tmp/gotest.net") if syscall.OS == "linux" { + doTest(t, "unixpacket", "/tmp/gotest.net", "/tmp/gotest.net") + os.Remove("/tmp/gotest.net") // Test abstract unix domain socket, a Linux-ism doTest(t, "unix", "@gotest/net", "@gotest/net") + doTest(t, "unixpacket", "@gotest/net", "@gotest/net") } } @@ -130,13 +140,16 @@ func runPacket(t *testing.T, network, addr string, listening chan<- string, done listening <- c.LocalAddr().String() c.SetReadTimeout(10e6) // 10ms var buf [1000]byte +Run: for { n, addr, err := c.ReadFrom(buf[0:]) if e, ok := err.(Error); ok && e.Timeout() { - if done <- 1 { - break + select { + case done <- 1: + break Run + default: + continue Run } - continue } if err != nil { break diff --git a/src/pkg/net/timeout_test.go b/src/pkg/net/timeout_test.go index 092781685..09a257dc8 100644 --- a/src/pkg/net/timeout_test.go +++ b/src/pkg/net/timeout_test.go @@ -8,14 +8,9 @@ import ( "os" "testing" "time" - "runtime" ) func testTimeout(t *testing.T, network, addr string, readFrom bool) { - // Timeouts are not implemented on windows. - if runtime.GOOS == "windows" { - return - } fd, err := Dial(network, "", addr) if err != nil { t.Errorf("dial %s %s failed: %v", network, addr, err) @@ -51,8 +46,12 @@ func TestTimeoutUDP(t *testing.T) { } func TestTimeoutTCP(t *testing.T) { - // 74.125.19.99 is www.google.com. - // could use dns, but dns depends on - // timeouts and this is the timeout test. - testTimeout(t, "tcp", "74.125.19.99:80", false) + // set up a listener that won't talk back + listening := make(chan string) + done := make(chan int) + go runServe(t, "tcp", "127.0.0.1:0", listening, done) + addr := <-listening + + testTimeout(t, "tcp", addr, false) + <-done } diff --git a/src/pkg/net/unixsock.go b/src/pkg/net/unixsock.go index 2521969eb..8c26a7baf 100644 --- a/src/pkg/net/unixsock.go +++ b/src/pkg/net/unixsock.go @@ -20,6 +20,8 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err proto = syscall.SOCK_STREAM case "unixgram": proto = syscall.SOCK_DGRAM + case "unixpacket": + proto = syscall.SOCK_SEQPACKET } var la, ra syscall.Sockaddr @@ -48,9 +50,12 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err } f := sockaddrToUnix - if proto != syscall.SOCK_STREAM { + if proto == syscall.SOCK_DGRAM { f = sockaddrToUnixgram + } else if proto == syscall.SOCK_SEQPACKET { + f = sockaddrToUnixpacket } + fd, oserr := socket(net, syscall.AF_UNIX, proto, 0, la, ra, f) if oserr != nil { goto Error @@ -67,30 +72,48 @@ Error: // UnixAddr represents the address of a Unix domain socket end point. type UnixAddr struct { - Name string - Datagram bool + Name string + Net string } func sockaddrToUnix(sa syscall.Sockaddr) Addr { if s, ok := sa.(*syscall.SockaddrUnix); ok { - return &UnixAddr{s.Name, false} + return &UnixAddr{s.Name, "unix"} } return nil } func sockaddrToUnixgram(sa syscall.Sockaddr) Addr { if s, ok := sa.(*syscall.SockaddrUnix); ok { - return &UnixAddr{s.Name, true} + return &UnixAddr{s.Name, "unixgram"} } return nil } -// Network returns the address's network name, "unix" or "unixgram". -func (a *UnixAddr) Network() string { - if a == nil || !a.Datagram { +func sockaddrToUnixpacket(sa syscall.Sockaddr) Addr { + if s, ok := sa.(*syscall.SockaddrUnix); ok { + return &UnixAddr{s.Name, "unixpacket"} + } + return nil +} + +func protoToNet(proto int) string { + switch proto { + case syscall.SOCK_STREAM: return "unix" + case syscall.SOCK_SEQPACKET: + return "unixpacket" + case syscall.SOCK_DGRAM: + return "unixgram" + default: + panic("protoToNet unknown protocol") } - return "unixgram" + return "" +} + +// Network returns the address's network name, "unix" or "unixgram". +func (a *UnixAddr) Network() string { + return a.Net } func (a *UnixAddr) String() string { @@ -108,17 +131,17 @@ func (a *UnixAddr) toAddr() Addr { } // ResolveUnixAddr parses addr as a Unix domain socket address. -// The string net gives the network name, "unix" or "unixgram". +// The string net gives the network name, "unix", "unixgram" or +// "unixpacket". func ResolveUnixAddr(net, addr string) (*UnixAddr, os.Error) { - var datagram bool switch net { case "unix": + case "unixpacket": case "unixgram": - datagram = true default: return nil, UnknownNetworkError(net) } - return &UnixAddr{addr, datagram}, nil + return &UnixAddr{addr, net}, nil } // UnixConn is an implementation of the Conn interface @@ -234,7 +257,7 @@ func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error) n, sa, err := c.fd.ReadFrom(b) switch sa := sa.(type) { case *syscall.SockaddrUnix: - addr = &UnixAddr{sa.Name, c.fd.proto == syscall.SOCK_DGRAM} + addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} } return } @@ -258,7 +281,7 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err os.Error) { if !c.ok() { return 0, os.EINVAL } - if addr.Datagram != (c.fd.proto == syscall.SOCK_DGRAM) { + if addr.Net != protoToNet(c.fd.proto) { return 0, os.EAFNOSUPPORT } sa := &syscall.SockaddrUnix{Name: addr.Name} @@ -284,7 +307,7 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob) switch sa := sa.(type) { case *syscall.SockaddrUnix: - addr = &UnixAddr{sa.Name, c.fd.proto == syscall.SOCK_DGRAM} + addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} } return } @@ -294,7 +317,7 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err return 0, 0, os.EINVAL } if addr != nil { - if addr.Datagram != (c.fd.proto == syscall.SOCK_DGRAM) { + if addr.Net != protoToNet(c.fd.proto) { return 0, 0, os.EAFNOSUPPORT } sa := &syscall.SockaddrUnix{Name: addr.Name} @@ -330,11 +353,11 @@ type UnixListener struct { // ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. // Net must be "unix" (stream sockets). func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) { - if net != "unix" && net != "unixgram" { + if net != "unix" && net != "unixgram" && net != "unixpacket" { return nil, UnknownNetworkError(net) } if laddr != nil { - laddr = &UnixAddr{laddr.Name, net == "unixgram"} // make our own copy + laddr = &UnixAddr{laddr.Name, net} // make our own copy } fd, err := unixSocket(net, laddr, nil, "listen") if err != nil { |