summaryrefslogtreecommitdiff
path: root/src/pkg/net
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-02-14 13:23:51 +0100
committerOndřej Surý <ondrej@sury.org>2011-02-14 13:23:51 +0100
commit758ff64c69e34965f8af5b2d6ffd65e8d7ab2150 (patch)
tree6d6b34f8c678862fe9b56c945a7b63f68502c245 /src/pkg/net
parent3e45412327a2654a77944249962b3652e6142299 (diff)
downloadgolang-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.go6
-rw-r--r--src/pkg/net/dialgoogle_test.go38
-rw-r--r--src/pkg/net/dnsclient.go81
-rw-r--r--src/pkg/net/dnsname_test.go14
-rw-r--r--src/pkg/net/fd.go17
-rw-r--r--src/pkg/net/fd_windows.go139
-rw-r--r--src/pkg/net/hosts.go31
-rw-r--r--src/pkg/net/net_test.go49
-rw-r--r--src/pkg/net/port.go4
-rw-r--r--src/pkg/net/resolv_windows.go29
-rw-r--r--src/pkg/net/server_test.go21
-rw-r--r--src/pkg/net/timeout_test.go17
-rw-r--r--src/pkg/net/unixsock.go61
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 {