diff options
author | Russ Cox <rsc@golang.org> | 2009-11-02 18:37:30 -0800 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2009-11-02 18:37:30 -0800 |
commit | 05958a8e858c5c710c9a962fb3a9f7ed28b7c8ac (patch) | |
tree | 34addcd47401db47aac916688b90b8be55850c40 /src | |
parent | 303c11471ec30cbf06ae0f228fd201e3af0c3552 (diff) | |
download | golang-05958a8e858c5c710c9a962fb3a9f7ed28b7c8ac.tar.gz |
package net cleanup
added ReadFrom/WriteTo for packet protocols like UDP.
simplified the net.Conn interface.
added new net.PacketConn interface for packet protocols.
implemented proper UDP listener.
cleaned up LocalAddr/RemoteAddr methods - cache in netFD.
threw away various unused methods.
an interface change:
introduced net.Addr as a network address interface,
to avoid conversion of UDP host:port to string and
back for every ReadFrom/WriteTo sequence.
another interface change:
since signature of Listener.Accept was changing anyway,
dropped the middle return value, because it is available
as c.RemoteAddr(). (the Accept signature predates the
existence of that method.)
Dial and Listen still accept strings, but the proto-specific
versions DialTCP, ListenUDP, etc. take net.Addr instead.
because the generic Dial didn't change and because
no one calls Accept directly (only indirectly via the http
server), very little code will be affected by these interface
changes.
design comments welcome.
R=p
CC=go-dev, r
http://go/go-review/1018017
Diffstat (limited to 'src')
-rw-r--r-- | src/pkg/http/server.go | 10 | ||||
-rw-r--r-- | src/pkg/net/Makefile | 2 | ||||
-rw-r--r-- | src/pkg/net/dialgoogle_test.go | 14 | ||||
-rw-r--r-- | src/pkg/net/dnsclient.go | 6 | ||||
-rw-r--r-- | src/pkg/net/fd.go | 68 | ||||
-rw-r--r-- | src/pkg/net/ip.go | 4 | ||||
-rw-r--r-- | src/pkg/net/ipsock.go | 247 | ||||
-rw-r--r-- | src/pkg/net/net.go | 218 | ||||
-rw-r--r-- | src/pkg/net/server_test.go | 81 | ||||
-rw-r--r-- | src/pkg/net/sock.go | 122 | ||||
-rw-r--r-- | src/pkg/net/tcpsock.go | 289 | ||||
-rw-r--r-- | src/pkg/net/udpsock.go | 299 | ||||
-rw-r--r-- | src/pkg/net/unixsock.go | 387 | ||||
-rw-r--r-- | src/pkg/os/error.go | 1 | ||||
-rw-r--r-- | src/pkg/rpc/client.go | 2 | ||||
-rw-r--r-- | src/pkg/rpc/server.go | 2 | ||||
-rw-r--r-- | src/pkg/rpc/server_test.go | 4 |
17 files changed, 1254 insertions, 502 deletions
diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go index fb6b3bd4d..3aa5c072f 100644 --- a/src/pkg/http/server.go +++ b/src/pkg/http/server.go @@ -56,9 +56,11 @@ type Conn struct { } // Create new connection from rwc. -func newConn(rwc io.ReadWriteCloser, raddr string, handler Handler) (c *Conn, err os.Error) { +func newConn(rwc net.Conn, handler Handler) (c *Conn, err os.Error) { c = new(Conn); - c.RemoteAddr = raddr; + if a := rwc.RemoteAddr(); a != nil { + c.RemoteAddr = a.String(); + } c.handler = handler; c.rwc = rwc; br := bufio.NewReader(rwc); @@ -527,11 +529,11 @@ func Serve(l net.Listener, handler Handler) os.Error { handler = DefaultServeMux; } for { - rw, raddr, e := l.Accept(); + rw, e := l.Accept(); if e != nil { return e; } - c, err := newConn(rw, raddr, handler); + c, err := newConn(rw, handler); if err != nil { continue; } diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile index 4c8ec3823..a11b0009a 100644 --- a/src/pkg/net/Makefile +++ b/src/pkg/net/Makefile @@ -17,6 +17,8 @@ GOFILES=\ parse.go\ port.go\ sock.go\ + tcpsock.go\ + udpsock.go\ unixsock.go\ include $(GOROOT)/src/Make.pkg diff --git a/src/pkg/net/dialgoogle_test.go b/src/pkg/net/dialgoogle_test.go index 810277713..81c65c9d1 100644 --- a/src/pkg/net/dialgoogle_test.go +++ b/src/pkg/net/dialgoogle_test.go @@ -40,16 +40,6 @@ func doDial(t *testing.T, network, addr string) { fd.Close() } -func doDialTCP(t *testing.T, network, addr string) { - fd, err := DialTCP(network, "", addr); - if err != nil { - t.Errorf("DialTCP(%q, %q, %q) = _, %v", network, "", addr, err); - } else { - fetchGoogle(t, fd, network, addr); - } - fd.Close() -} - var googleaddrs = []string { "74.125.19.99:80", "www.google.com:80", @@ -77,22 +67,18 @@ func TestDialGoogle(t *testing.T) { } t.Logf("-- %s --", addr); doDial(t, "tcp", addr); - doDialTCP(t, "tcp", addr); if addr[0] != '[' { doDial(t, "tcp4", addr); - doDialTCP(t, "tcp4", addr); if !preferIPv4 { // make sure preferIPv4 flag works. preferIPv4 = true; syscall.SocketDisableIPv6 = true; doDial(t, "tcp4", addr); - doDialTCP(t, "tcp4", addr); syscall.SocketDisableIPv6 = false; preferIPv4 = false; } } doDial(t, "tcp6", addr); - doDialTCP(t, "tcp6", addr) } } diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go index 228d481ce..46b8218a4 100644 --- a/src/pkg/net/dnsclient.go +++ b/src/pkg/net/dnsclient.go @@ -78,7 +78,11 @@ func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error } return in, nil } - return nil, &DNSError{"no answer from server", name, c.RemoteAddr()} + var server string; + if a := c.RemoteAddr(); a != nil { + server = a.String(); + } + return nil, &DNSError{"no answer from server", name, server} } diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go index 0f422396e..64d505ae3 100644 --- a/src/pkg/net/fd.go +++ b/src/pkg/net/fd.go @@ -17,12 +17,14 @@ import ( type netFD struct { // immutable until Close fd int; + family int; + proto int; file *os.File; cr chan *netFD; cw chan *netFD; net string; - laddr string; - raddr string; + laddr Addr; + raddr Addr; // owned by client rdeadline_delta int64; @@ -289,7 +291,7 @@ func (s *pollServer) WaitWrite(fd *netFD) { var pollserver *pollServer -func _StartServer() { +func startServer() { p, err := newPollServer(); if err != nil { print("Start pollServer: ", err.String(), "\n") @@ -297,19 +299,27 @@ func _StartServer() { pollserver = p } -func newFD(fd int, net, laddr, raddr string) (f *netFD, err os.Error) { - if pollserver == nil { - once.Do(_StartServer); - } +func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) { + once.Do(startServer); if e := syscall.SetNonblock(fd, true); e != 0 { - return nil, &os.PathError{"setnonblock", laddr, os.Errno(e)}; - } - f = new(netFD); - f.fd = fd; - f.net = net; - f.laddr = laddr; - f.raddr = raddr; - f.file = os.NewFile(fd, net + "!" + laddr + "->" + raddr); + return nil, &OpError{"setnonblock", net, laddr, os.Errno(e)}; + } + f = &netFD{ + fd: fd, + family: family, + proto: proto, + net: net, + laddr: laddr, + raddr: raddr, + }; + var ls, rs string; + if laddr != nil { + ls = laddr.String(); + } + if raddr != nil { + rs = raddr.String(); + } + f.file = os.NewFile(fd, net + ":" + ls + "->" + rs); f.cr = make(chan *netFD, 1); f.cw = make(chan *netFD, 1); return f, nil @@ -322,24 +332,6 @@ func isEAGAIN(e os.Error) bool { return e == os.EAGAIN; } -func (fd *netFD) addr() string { - sa, e := syscall.Getsockname(fd.fd); - if e != 0 { - return ""; - } - addr, _ := sockaddrToString(sa); - return addr; -} - -func (fd *netFD) remoteAddr() string { - sa, e := syscall.Getpeername(fd.fd); - if e != 0 { - return ""; - } - addr, _ := sockaddrToString(sa); - return addr; -} - func (fd *netFD) Close() os.Error { if fd == nil || fd.file == nil { return os.EINVAL @@ -413,7 +405,7 @@ func (fd *netFD) Write(p []byte) (n int, err os.Error) { return nn, err } -func (fd *netFD) accept() (nfd *netFD, err os.Error) { +func (fd *netFD) accept(toAddr func(syscall.Sockaddr)Addr) (nfd *netFD, err os.Error) { if fd == nil || fd.file == nil { return nil, os.EINVAL } @@ -435,16 +427,12 @@ func (fd *netFD) accept() (nfd *netFD, err os.Error) { } if e != 0 { syscall.ForkLock.RUnlock(); - return nil, &os.PathError{"accept", fd.addr(), os.Errno(e)} + return nil, &OpError{"accept", fd.net, fd.laddr, os.Errno(e)} } syscall.CloseOnExec(s); syscall.ForkLock.RUnlock(); - raddr, err1 := sockaddrToString(sa); - if err1 != nil { - raddr = "invalid-address"; - } - if nfd, err = newFD(s, fd.net, fd.laddr, raddr); err != nil { + if nfd, err = newFD(s, fd.family, fd.proto, fd.net, fd.laddr, toAddr(sa)); err != nil { syscall.Close(s); return nil, err } diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go index 6f1e07882..b6f5ef49e 100644 --- a/src/pkg/net/ip.go +++ b/src/pkg/net/ip.go @@ -180,6 +180,10 @@ func itox(i uint) string { func (ip IP) String() string { p := ip; + if len(ip) == 0 { + return ""; + } + // If IPv4, use dotted notation. if p4 := p.To4(); len(p4) == 4 { return itod(uint(p4[0]))+"." diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go index 640695256..705fc075a 100644 --- a/src/pkg/net/ipsock.go +++ b/src/pkg/net/ipsock.go @@ -34,125 +34,47 @@ func listenBacklog() int { return syscall.SOMAXCONN } -// ListenerTCP is a TCP network listener. -// Clients should typically use variables of type Listener -// instead of assuming TCP. -type ListenerTCP struct { - fd *netFD; -} - -// ListenTCP announces on the TCP address laddr and returns a TCP listener. -// Net must be "tcp", "tcp4", or "tcp6". -// If laddr has a port of 0, it means to listen on some available port. -// The caller can use l.Addr() to retrieve the chosen address. -func ListenTCP(net, laddr string) (l *ListenerTCP, err os.Error) { - fd, e := internetSocket(net, laddr, "", syscall.SOCK_STREAM, "listen"); - if e != nil { - return nil, e - } - e1 := syscall.Listen(fd.fd, listenBacklog()); - if e1 != 0 { - syscall.Close(fd.fd); - return nil, &OpError{"listen", "tcp", laddr, os.Errno(e1)}; - } - l = new(ListenerTCP); - l.fd = fd; - return l, nil -} - -// AcceptTCP accepts the next incoming call and returns the new connection -// and the remote address. -func (l *ListenerTCP) AcceptTCP() (c *ConnTCP, raddr string, err os.Error) { - if l == nil || l.fd == nil || l.fd.fd < 0 { - return nil, "", os.EINVAL - } - fd, e := l.fd.accept(); - if e != nil { - return nil, "", e - } - return newConnTCP(fd, fd.raddr), fd.raddr, nil -} - -// Accept implements the Accept method in the Listener interface; -// it waits for the next call and returns a generic Conn. -func (l *ListenerTCP) Accept() (c Conn, raddr string, err os.Error) { - c1, r1, e1 := l.AcceptTCP(); - if e1 != nil { - return nil, "", e1 - } - return c1, r1, nil -} - -// Close stops listening on the TCP address. -// Already Accepted connections are not closed. -func (l *ListenerTCP) Close() os.Error { - if l == nil || l.fd == nil { - return os.EINVAL - } - return l.fd.Close() -} - -// Addr returns the listener's network address. -func (l *ListenerTCP) Addr() string { - return l.fd.addr(); -} - // Internet sockets (TCP, UDP) -func internetSocket(net, laddr, raddr string, proto int, mode string) (fd *netFD, err os.Error) { - // Parse addresses (unless they are empty). - var lip, rip IP; - var lport, rport int; - - if laddr != "" { - if lip, lport, err = hostPortToIP(net, laddr, mode); err != nil { - goto Error; - } - } - if raddr != "" { - if rip, rport, err = hostPortToIP(net, raddr, mode); err != nil { - goto Error; - } - } +// A sockaddr represents a TCP or UDP network address that can +// be converted into a syscall.Sockaddr. +type sockaddr interface { + Addr; + sockaddr(family int) (syscall.Sockaddr, os.Error); + family() int; +} +func internetSocket(net string, laddr, raddr sockaddr, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) { // Figure out IP version. // If network has a suffix like "tcp4", obey it. - vers := 0; + family := syscall.AF_INET6; switch net[len(net)-1] { case '4': - vers = 4; + family = syscall.AF_INET case '6': - vers = 6; + // nothing to do default: // Otherwise, guess. // If the addresses are IPv4 and we prefer IPv4, use 4; else 6. - if preferIPv4 && (lip == nil || lip.To4() != nil) && (rip == nil || rip.To4() != nil) { - vers = 4 - } else { - vers = 6 + if preferIPv4 && + (laddr == nil || laddr.family() == syscall.AF_INET) && + (raddr == nil || raddr.family() == syscall.AF_INET) { + family = syscall.AF_INET; } } - var family int; - if vers == 4 { - family = syscall.AF_INET - } else { - family = syscall.AF_INET6 - } - var la, ra syscall.Sockaddr; - if lip != nil { - if la, err = ipToSockaddr(family, lip, lport); err != nil { + if laddr != nil { + if la, err = laddr.sockaddr(family); err != nil { goto Error; } } - if rip != nil { - if ra, err = ipToSockaddr(family, rip, rport); err != nil { + if raddr != nil { + if ra, err = raddr.sockaddr(family); err != nil { goto Error; } } - - fd, err = socket(net, laddr, raddr, family, proto, 0, la, ra); + fd, err = socket(net, family, proto, 0, la, ra, toAddr); if err != nil { goto Error; } @@ -166,77 +88,31 @@ Error: return nil, &OpError{mode, net, addr, err}; } - -// TCP connections. - -// ConnTCP is an implementation of the Conn interface -// for TCP network connections. -type ConnTCP struct { - connBase -} - -func (c *ConnTCP) SetNoDelay(nodelay bool) os.Error { - if c == nil { - return os.EINVAL - } - return setsockoptInt(c.sysFD(), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(nodelay)) -} - -func newConnTCP(fd *netFD, raddr string) *ConnTCP { - c := new(ConnTCP); - c.fd = fd; - c.raddr = raddr; - c.SetNoDelay(true); - return c -} - -// DialTCP is like Dial but can only connect to TCP networks -// and returns a ConnTCP structure. -func DialTCP(net, laddr, raddr string) (c *ConnTCP, err os.Error) { - if raddr == "" { - return nil, &OpError{"dial", "tcp", "", errMissingAddress} - } - fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_STREAM, "dial"); - if e != nil { - return nil, e - } - return newConnTCP(fd, raddr), nil -} - - -// UDP connections. - -// TODO(rsc): UDP headers mode - -// ConnUDP is an implementation of the Conn interface -// for UDP network connections. -type ConnUDP struct { - connBase -} - -func newConnUDP(fd *netFD, raddr string) *ConnUDP { - c := new(ConnUDP); - c.fd = fd; - c.raddr = raddr; - return c -} - -// DialUDP is like Dial but can only connect to UDP networks -// and returns a ConnUDP structure. -func DialUDP(net, laddr, raddr string) (c *ConnUDP, err os.Error) { - if raddr == "" { - return nil, &OpError{"dial", "udp", "", errMissingAddress} +func getip(fd int, remote bool) (ip []byte, port int, ok bool) { + // No attempt at error reporting because + // there are no possible errors, and the + // caller won't report them anyway. + var sa syscall.Sockaddr; + if remote { + sa, _ = syscall.Getpeername(fd); + } else { + sa, _ = syscall.Getsockname(fd); } - fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_DGRAM, "dial"); - if e != nil { - return nil, e + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + return &sa.Addr, sa.Port, true; + case *syscall.SockaddrInet6: + return &sa.Addr, sa.Port, true; } - return newConnUDP(fd, raddr), nil + return; } func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { switch family { case syscall.AF_INET: + if len(ip) == 0 { + ip = IPv4zero; + } if ip = ip.To4(); ip == nil { return nil, os.EINVAL } @@ -247,6 +123,9 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { s.Port = port; return s, nil; case syscall.AF_INET6: + if len(ip) == 0 { + ip = IPzero; + } // IPv4 callers use 0.0.0.0 to mean "announce on any available address". // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0", // which it refuses to do. Rewrite to the IPv6 all zeros. @@ -302,43 +181,29 @@ func joinHostPort(host, port string) string { } // Convert "host:port" into IP address and port. -// For now, host and port must be numeric literals. -// Eventually, we'll have name resolution. -func hostPortToIP(net, hostport, mode string) (ip IP, iport int, err os.Error) { +func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) { host, port, err := splitHostPort(hostport); if err != nil { goto Error; } var addr IP; - if host == "" { - if mode != "listen" { - err = &AddrError{"no host in address", hostport}; - goto Error; - } - if preferIPv4 { - addr = IPv4zero; - } else { - addr = IPzero; // wildcard - listen to all - } - } - - // Try as an IP address. - if addr == nil { + if host != "" { + // Try as an IP address. addr = ParseIP(host); - } - if addr == nil { - // Not an IP address. Try as a DNS name. - _, addrs, err1 := LookupHost(host); - if err1 != nil { - err = err1; - goto Error; - } - addr = ParseIP(addrs[0]); if addr == nil { - // should not happen - err = &AddrError{"LookupHost returned invalid address", addrs[0]}; - goto Error; + // Not an IP address. Try as a DNS name. + _, addrs, err1 := LookupHost(host); + if err1 != nil { + err = err1; + goto Error; + } + addr = ParseIP(addrs[0]); + if addr == nil { + // should not happen + err = &AddrError{"LookupHost returned invalid address", addrs[0]}; + goto Error; + } } } diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go index 8669ed3ae..1532fc0a7 100644 --- a/src/pkg/net/net.go +++ b/src/pkg/net/net.go @@ -10,43 +10,71 @@ package net import "os" -// Conn is a generic network connection. +// Addr represents a network end point address. +type Addr interface { + Network() string; // name of the network + String() string; // string form of address +} + +// Conn is a generic stream-oriented network connection. type Conn interface { - // Read blocks until data is ready from the connection - // and then reads into b. It returns the number - // of bytes read, or 0 if the connection has been closed. + // Read reads data from the connection. + // Read can be made to time out and return err == os.EAGAIN + // after a fixed time limit; see SetTimeout and SetReadTimeout. Read(b []byte) (n int, err os.Error); - // Write writes the data in b to the connection. + // Write writes data to the connection. + // Write can be made to time out and return err == os.EAGAIN + // after a fixed time limit; see SetTimeout and SetReadTimeout. Write(b []byte) (n int, err os.Error); // Close closes the connection. Close() os.Error; // LocalAddr returns the local network address. - LocalAddr() string; + LocalAddr() Addr; // RemoteAddr returns the remote network address. - RemoteAddr() string; + RemoteAddr() Addr; + + // SetTimeout sets the read and write deadlines associated + // with the connection. + SetTimeout(nsec int64) os.Error; + + // SetReadTimeout sets the time (in nanoseconds) that + // Read will wait for data before returning os.EAGAIN. + // Setting nsec == 0 (the default) disables the deadline. + SetReadTimeout(nsec int64) os.Error; + + // SetWriteTimeout sets the time (in nanoseconds) that + // Write will wait to send its data before returning os.EAGAIN. + // Setting nsec == 0 (the default) disables the deadline. + // Even if write times out, it may return n > 0, indicating that + // some of the data was successfully written. + SetWriteTimeout(nsec int64) os.Error; +} - // For packet-based protocols such as UDP, - // ReadFrom reads the next packet from the network, - // returning the number of bytes read and the remote - // address that sent them. - ReadFrom(b []byte) (n int, addr string, err os.Error); +// PacketConn is a generic packet-oriented network connection. +type PacketConn interface { + // ReadFrom reads a packet from the connection, + // copying the payload into b. It returns the number of + // bytes copied into b and the return address that + // was on the packet. + // ReadFrom can be made to time out and return err == os.EAGAIN + // after a fixed time limit; see SetTimeout and SetReadTimeout. + ReadFrom(b []byte) (n int, addr Addr, err os.Error); - // For packet-based protocols such as UDP, - // WriteTo writes the byte buffer b to the network - // as a single payload, sending it to the target address. - WriteTo(addr string, b []byte) (n int, err os.Error); + // WriteTo writes a packet with payload b to addr. + // WriteTo can be made to time out and return err == os.EAGAIN + // after a fixed time limit; see SetTimeout and SetWriteTimeout. + // On packet-oriented connections, write timeouts are rare. + WriteTo(b []byte, addr Addr) (n int, err os.Error); - // SetReadBuffer sets the size of the operating system's - // receive buffer associated with the connection. - SetReadBuffer(bytes int) os.Error; + // Close closes the connection. + Close() os.Error; - // SetReadBuffer sets the size of the operating system's - // transmit buffer associated with the connection. - SetWriteBuffer(bytes int) os.Error; + // LocalAddr returns the local network address. + LocalAddr() Addr; // SetTimeout sets the read and write deadlines associated // with the connection. @@ -63,42 +91,14 @@ type Conn interface { // Even if write times out, it may return n > 0, indicating that // some of the data was successfully written. SetWriteTimeout(nsec int64) os.Error; - - // SetLinger sets the behavior of Close() on a connection - // which still has data waiting to be sent or to be acknowledged. - // - // If sec < 0 (the default), Close returns immediately and - // the operating system finishes sending the data in the background. - // - // If sec == 0, Close returns immediately and the operating system - // discards any unsent or unacknowledged data. - // - // If sec > 0, Close blocks for at most sec seconds waiting for - // data to be sent and acknowledged. - SetLinger(sec int) os.Error; - - // SetReuseAddr sets whether it is okay to reuse addresses - // from recent connections that were not properly closed. - SetReuseAddr(reuseaddr bool) os.Error; - - // SetDontRoute sets whether outgoing messages should - // bypass the system routing tables. - SetDontRoute(dontroute bool) os.Error; - - // SetKeepAlive sets whether the operating system should send - // keepalive messages on the connection. - SetKeepAlive(keepalive bool) os.Error; - - // BindToDevice binds a connection to a particular network device. - BindToDevice(dev string) os.Error; } -// A Listener is a generic network listener. +// A Listener is a generic network listener for stream-oriented protocols. // Accept waits for the next connection and Close closes the connection. type Listener interface { - Accept() (c Conn, raddr string, err os.Error); + Accept() (c Conn, err os.Error); Close() os.Error; - Addr() string; // Listener's network address + Addr() Addr; // Listener's network address } // Dial connects to the remote address raddr on the network net. @@ -116,42 +116,116 @@ type Listener interface { // Dial("tcp", "", "google.com:80") // Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80") // Dial("tcp", "127.0.0.1:123", "127.0.0.1:88") +// func Dial(net, laddr, raddr string) (c Conn, err os.Error) { switch net { case "tcp", "tcp4", "tcp6": - c, err := DialTCP(net, laddr, raddr); - if err != nil { - return nil, err + var la, ra *TCPAddr; + if laddr != "" { + if la, err = ResolveTCPAddr(laddr); err != nil { + goto Error; + } } - return c, nil; + if raddr != "" { + if ra, err = ResolveTCPAddr(raddr); err != nil { + goto Error; + } + } + return DialTCP(net, la, ra); case "udp", "udp4", "upd6": - c, err := DialUDP(net, laddr, raddr); - return c, err; - case "unix", "unix-dgram": - c, err := DialUnix(net, laddr, raddr); - return c, err; + var la, ra *UDPAddr; + if laddr != "" { + if la, err = ResolveUDPAddr(laddr); err != nil { + goto Error; + } + } + if raddr != "" { + if ra, err = ResolveUDPAddr(raddr); err != nil { + goto Error; + } + } + return DialUDP(net, la, ra); + case "unix", "unixgram": + var la, ra *UnixAddr; + if raddr != "" { + if ra, err = ResolveUnixAddr(net, raddr); err != nil { + goto Error; + } + } + if laddr != "" { + if la, err = ResolveUnixAddr(net, laddr); err != nil { + goto Error; + } + } + return DialUnix(net, la, ra); } - return nil, &OpError{"dial", net, raddr, UnknownNetworkError(net)}; + err = UnknownNetworkError(net); +Error: + return nil, &OpError{"dial", net+" "+raddr, nil, err}; } // Listen announces on the local network address laddr. -// The network string net must be "tcp", "tcp4", "tcp6", -// "unix", or "unix-dgram". +// The network string net must be a stream-oriented +// network: "tcp", "tcp4", "tcp6", or "unix". func Listen(net, laddr string) (l Listener, err os.Error) { switch net { case "tcp", "tcp4", "tcp6": - l, err := ListenTCP(net, laddr); + var la *TCPAddr; + if laddr != "" { + if la, err = ResolveTCPAddr(laddr); err != nil { + return nil, err; + } + } + l, err := ListenTCP(net, la); if err != nil { return nil, err; } return l, nil; - case "unix", "unix-dgram": - l, err := ListenUnix(net, laddr); + case "unix": + var la *UnixAddr; + if laddr != "" { + if la, err = ResolveUnixAddr(net, laddr); err != nil { + return nil, err; + } + } + l, err := ListenUnix(net, la); if err != nil { return nil, err; } return l, nil; - // BUG(rsc): Listen should support UDP. + } + return nil, UnknownNetworkError(net); +} + +// ListenPacket announces on the local network address laddr. +// The network string net must be a packet-oriented network: +// "udp", "udp4", "udp6", or "unixgram". +func ListenPacket(net, laddr string) (c PacketConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + var la *UDPAddr; + if laddr != "" { + if la, err = ResolveUDPAddr(laddr); err != nil { + return nil, err; + } + } + c, err := ListenUDP(net, la); + if err != nil { + return nil, err; + } + return c, nil; + case "unixgram": + var la *UnixAddr; + if laddr != "" { + if la, err = ResolveUnixAddr(net, laddr); err != nil { + return nil, err; + } + } + c, err := DialUnix(net, la, nil); + if err != nil { + return nil, err; + } + return c, nil; } return nil, UnknownNetworkError(net); } @@ -161,7 +235,7 @@ var errMissingAddress = os.ErrorString("missing address") type OpError struct { Op string; Net string; - Addr string; + Addr Addr; Error os.Error; } @@ -170,8 +244,8 @@ func (e *OpError) String() string { if e.Net != "" { s += " " + e.Net; } - if e.Addr != "" { - s += " " + e.Addr; + if e.Addr != nil { + s += " " + e.Addr.String(); } s += ": " + e.Error.String(); return s; diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go index d961ae068..cdcc99e31 100644 --- a/src/pkg/net/server_test.go +++ b/src/pkg/net/server_test.go @@ -6,6 +6,7 @@ package net import ( "io"; + "os"; "strings"; "syscall"; "testing"; @@ -29,10 +30,10 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done if err != nil { t.Fatalf("net.Listen(%q, %q) = _, %v", network, addr, err); } - listening <- l.Addr(); + listening <- l.Addr().String(); for { - fd, _, err := l.Accept(); + fd, err := l.Accept(); if err != nil { break; } @@ -45,9 +46,13 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done } func connect(t *testing.T, network, addr string) { - fd, err := Dial(network, "", addr); + var laddr string; + if network == "unixgram" { + laddr = addr + ".local"; + } + fd, err := Dial(network, laddr, addr); if err != nil { - t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, "", addr, err); + t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, laddr, addr, err); } b := strings.Bytes("hello, world\n"); @@ -81,7 +86,7 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) { <-done; // make sure server stopped } -func TestTcpServer(t *testing.T) { +func TestTCPServer(t *testing.T) { doTest(t, "tcp", "0.0.0.0", "127.0.0.1"); doTest(t, "tcp", "[::]", "[::ffff:127.0.0.1]"); doTest(t, "tcp", "[::]", "127.0.0.1"); @@ -90,9 +95,75 @@ func TestTcpServer(t *testing.T) { } func TestUnixServer(t *testing.T) { + os.Remove("/tmp/gotest.net"); doTest(t, "unix", "/tmp/gotest.net", "/tmp/gotest.net"); + os.Remove("/tmp/gotest.net"); if syscall.OS == "linux" { // Test abstract unix domain socket, a Linux-ism doTest(t, "unix", "@gotest/net", "@gotest/net"); } } + +func runPacket(t *testing.T, network, addr string, listening chan<- string, done chan<- int) { + c, err := ListenPacket(network, addr); + if err != nil { + t.Fatalf("net.ListenPacket(%q, %q) = _, %v", network, addr, err); + } + listening <- c.LocalAddr().String(); + c.SetReadTimeout(10e6); // 10ms + var buf [1000]byte; + for { + n, addr, err := c.ReadFrom(&buf); + if err == os.EAGAIN { + if done <- 1 { + break; + } + continue; + } + if err != nil { + break; + } + if _, err = c.WriteTo(buf[0:n], addr); err != nil { + t.Fatalf("WriteTo %v: %v", addr, err); + } + } + c.Close(); + done <- 1; +} + +func doTestPacket(t *testing.T, network, listenaddr, dialaddr string) { + t.Logf("TestPacket %s %s %s\n", network, listenaddr, dialaddr); + listening := make(chan string); + done := make(chan int); + if network == "udp" { + listenaddr += ":0"; // any available port + } + go runPacket(t, network, listenaddr, listening, done); + addr := <-listening; // wait for server to start + if network == "udp" { + dialaddr += addr[strings.LastIndex(addr, ":"):len(addr)]; + } + connect(t, network, dialaddr); + <-done; // tell server to stop + <-done; // wait for stop +} + +func TestUDPServer(t *testing.T) { + doTestPacket(t, "udp", "0.0.0.0", "127.0.0.1"); + doTestPacket(t, "udp", "[::]", "[::ffff:127.0.0.1]"); + doTestPacket(t, "udp", "[::]", "127.0.0.1"); + doTestPacket(t, "udp", "", "127.0.0.1"); + doTestPacket(t, "udp", "0.0.0.0", "[::ffff:127.0.0.1]"); +} + +func TestUnixDatagramServer(t *testing.T) { + os.Remove("/tmp/gotest1.net"); + os.Remove("/tmp/gotest1.net.local"); + doTestPacket(t, "unixgram", "/tmp/gotest1.net", "/tmp/gotest1.net"); + os.Remove("/tmp/gotest1.net"); + os.Remove("/tmp/gotest1.net.local"); + if syscall.OS == "linux" { + // Test abstract unix domain socket, a Linux-ism + doTestPacket(t, "unixgram", "@gotest1/net", "@gotest1/net"); + } +} diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go index 857a228bd..8b4018458 100644 --- a/src/pkg/net/sock.go +++ b/src/pkg/net/sock.go @@ -21,7 +21,7 @@ func boolint(b bool) int { } // Generic socket creation. -func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd *netFD, err os.Error) { +func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) { // See ../syscall/exec.go for description of ForkLock. syscall.ForkLock.RLock(); s, e := syscall.Socket(f, p, t); @@ -51,7 +51,12 @@ func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd } } - fd, err = newFD(s, net, laddr, raddr); + sa, _ := syscall.Getsockname(s); + laddr := toAddr(sa); + sa, _ = syscall.Getpeername(s); + raddr := toAddr(sa); + + fd, err = newFD(s, f, p, net, laddr, raddr); if err != nil { syscall.Close(s); return nil, err @@ -60,78 +65,6 @@ func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd return fd, nil } - -// Generic implementation of Conn interface; not exported. -type connBase struct { - fd *netFD; - raddr string; -} - -func (c *connBase) LocalAddr() string { - if c == nil { - return "" - } - return c.fd.addr(); -} - -func (c *connBase) RemoteAddr() string { - if c == nil { - return "" - } - return c.fd.remoteAddr(); -} - -func (c *connBase) File() *os.File { - if c == nil { - return nil - } - return c.fd.file; -} - -func (c *connBase) sysFD() int { - if c == nil || c.fd == nil { - return -1; - } - return c.fd.fd; -} - -func (c *connBase) Read(b []byte) (n int, err os.Error) { - n, err = c.fd.Read(b); - return n, err -} - -func (c *connBase) Write(b []byte) (n int, err os.Error) { - n, err = c.fd.Write(b); - return n, err -} - -func (c *connBase) ReadFrom(b []byte) (n int, raddr string, err os.Error) { - if c == nil { - return -1, "", os.EINVAL - } - n, err = c.Read(b); - return n, c.raddr, err -} - -func (c *connBase) WriteTo(raddr string, b []byte) (n int, err os.Error) { - if c == nil { - return -1, os.EINVAL - } - if raddr != c.raddr { - return -1, os.EINVAL - } - n, err = c.Write(b); - return n, err -} - -func (c *connBase) Close() os.Error { - if c == nil { - return os.EINVAL - } - return c.fd.Close() -} - - func setsockoptInt(fd, level, opt int, value int) os.Error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, opt, value)); } @@ -141,49 +74,49 @@ func setsockoptNsec(fd, level, opt int, nsec int64) os.Error { return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd, level, opt, &tv)); } -func (c *connBase) SetReadBuffer(bytes int) os.Error { - return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes); +func setReadBuffer(fd *netFD, bytes int) os.Error { + return setsockoptInt(fd.fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes); } -func (c *connBase) SetWriteBuffer(bytes int) os.Error { - return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes); +func setWriteBuffer(fd *netFD, bytes int) os.Error { + return setsockoptInt(fd.fd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes); } -func (c *connBase) SetReadTimeout(nsec int64) os.Error { - c.fd.rdeadline_delta = nsec; +func setReadTimeout(fd *netFD, nsec int64) os.Error { + fd.rdeadline_delta = nsec; return nil; } -func (c *connBase) SetWriteTimeout(nsec int64) os.Error { - c.fd.wdeadline_delta = nsec; +func setWriteTimeout(fd *netFD, nsec int64) os.Error { + fd.wdeadline_delta = nsec; return nil; } -func (c *connBase) SetTimeout(nsec int64) os.Error { - if e := c.SetReadTimeout(nsec); e != nil { +func setTimeout(fd *netFD, nsec int64) os.Error { + if e := setReadTimeout(fd, nsec); e != nil { return e } - return c.SetWriteTimeout(nsec) + return setWriteTimeout(fd, nsec) } -func (c *connBase) SetReuseAddr(reuse bool) os.Error { - return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)); +func setReuseAddr(fd *netFD, reuse bool) os.Error { + return setsockoptInt(fd.fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)); } -func (c *connBase) BindToDevice(dev string) os.Error { +func bindToDevice(fd *netFD, dev string) os.Error { // TODO(rsc): call setsockopt with null-terminated string pointer return os.EINVAL } -func (c *connBase) SetDontRoute(dontroute bool) os.Error { - return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute)); +func setDontRoute(fd *netFD, dontroute bool) os.Error { + return setsockoptInt(fd.fd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute)); } -func (c *connBase) SetKeepAlive(keepalive bool) os.Error { - return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)); +func setKeepAlive(fd *netFD, keepalive bool) os.Error { + return setsockoptInt(fd.fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)); } -func (c *connBase) SetLinger(sec int) os.Error { +func setLinger(fd *netFD, sec int) os.Error { var l syscall.Linger; if sec >= 0 { l.Onoff = 1; @@ -192,11 +125,10 @@ func (c *connBase) SetLinger(sec int) os.Error { l.Onoff = 0; l.Linger = 0; } - e := syscall.SetsockoptLinger(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_LINGER, &l); + e := syscall.SetsockoptLinger(fd.fd, syscall.SOL_SOCKET, syscall.SO_LINGER, &l); return os.NewSyscallError("setsockopt", e); } - type UnknownSocketError struct { sa syscall.Sockaddr; } diff --git a/src/pkg/net/tcpsock.go b/src/pkg/net/tcpsock.go new file mode 100644 index 000000000..cfe1a4bd4 --- /dev/null +++ b/src/pkg/net/tcpsock.go @@ -0,0 +1,289 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TCP sockets + +package net + +import ( + "os"; + "syscall"; +) + +func sockaddrToTCP(sa syscall.Sockaddr) Addr { + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + return &TCPAddr{&sa.Addr, sa.Port}; + case *syscall.SockaddrInet6: + return &TCPAddr{&sa.Addr, sa.Port}; + } + return nil; +} + +// TCPAddr represents the address of a TCP end point. +type TCPAddr struct { + IP IP; + Port int; +} + +// Network returns the address's network name, "tcp". +func (a *TCPAddr) Network() string { + return "tcp"; +} + +func (a *TCPAddr) String() string { + return joinHostPort(a.IP.String(), itoa(a.Port)); +} + +func (a *TCPAddr) family() int { + if a == nil || len(a.IP) <= 4 { + return syscall.AF_INET; + } + if ip := a.IP.To4(); ip != nil { + return syscall.AF_INET; + } + return syscall.AF_INET6; +} + +func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { + return ipToSockaddr(family, a.IP, a.Port); +} + +func (a *TCPAddr) toAddr() sockaddr { + if a == nil { // nil *TCPAddr + return nil; // nil interface + } + return a; +} + +// ResolveTCPAddr parses addr as a TCP address of the form +// host:port and resolves domain names or port names to +// numeric addresses. A literal IPv6 host address must be +// enclosed in square brackets, as in "[::]:80". +func ResolveTCPAddr(addr string) (*TCPAddr, os.Error) { + ip, port, err := hostPortToIP("tcp", addr); + if err != nil { + return nil, err; + } + return &TCPAddr{ip, port}, nil; +} + +// TCPConn is an implementation of the Conn interface +// for TCP network connections. +type TCPConn struct { + fd *netFD; +} + +func newTCPConn(fd *netFD) *TCPConn { + c := &TCPConn{fd}; + setsockoptInt(fd.fd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1); + return c +} + +func (c *TCPConn) ok() bool { +if c == nil || c.fd == nil { panic() } + return c != nil && c.fd != nil; +} + +// Implementation of the Conn interface - see Conn for documentation. + +// Read reads data from the TCP connection. +// +// Read can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *TCPConn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Read(b); +} + +// Write writes data to the TCP connection. +// +// Write can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *TCPConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b); +} + +// Close closes the TCP connection. +func (c *TCPConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close(); + c.fd = nil; + return err; +} + +// LocalAddr returns the local network address, a *TCPAddr. +func (c *TCPConn) LocalAddr() Addr { + if !c.ok() { + return nil; + } + return c.fd.laddr; +} + +// RemoteAddr returns the remote network address, a *TCPAddr. +func (c *TCPConn) RemoteAddr() Addr { + if !c.ok() { + return nil; + } + return c.fd.raddr; +} + +// SetTimeout sets the read and write deadlines associated +// with the connection. +func (c *TCPConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec); +} + +// SetReadTimeout sets the time (in nanoseconds) that +// Read will wait for data before returning os.EAGAIN. +// Setting nsec == 0 (the default) disables the deadline. +func (c *TCPConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec); +} + +// SetWriteTimeout sets the time (in nanoseconds) that +// Write will wait to send its data before returning os.EAGAIN. +// Setting nsec == 0 (the default) disables the deadline. +// Even if write times out, it may return n > 0, indicating that +// some of the data was successfully written. +func (c *TCPConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec); +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *TCPConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes); +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *TCPConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes); +} + +// SetLinger sets the behavior of Close() on a connection +// which still has data waiting to be sent or to be acknowledged. +// +// If sec < 0 (the default), Close returns immediately and +// the operating system finishes sending the data in the background. +// +// If sec == 0, Close returns immediately and the operating system +// discards any unsent or unacknowledged data. +// +// If sec > 0, Close blocks for at most sec seconds waiting for +// data to be sent and acknowledged. +func (c *TCPConn) SetLinger(sec int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setLinger(c.fd, sec); +} + +// SetKeepAlive sets whether the operating system should send +// keepalive messages on the connection. +func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error { + if !c.ok() { + return os.EINVAL + } + return setKeepAlive(c.fd, keepalive); +} + +// DialTCP is like Dial but can only connect to TCP networks +// and returns a TCPConn structure. +func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) { + if raddr == nil { + return nil, &OpError{"dial", "tcp", nil, errMissingAddress} + } + fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, "dial", sockaddrToTCP); + if e != nil { + return nil, e + } + return newTCPConn(fd), nil +} + +// TCPListener is a TCP network listener. +// Clients should typically use variables of type Listener +// instead of assuming TCP. +type TCPListener struct { + fd *netFD; +} + +// ListenTCP announces on the TCP address laddr and returns a TCP listener. +// Net must be "tcp", "tcp4", or "tcp6". +// If laddr has a port of 0, it means to listen on some available port. +// The caller can use l.Addr() to retrieve the chosen address. +func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error) { + fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, "listen", sockaddrToTCP); + if err != nil { + return nil, err + } + errno := syscall.Listen(fd.fd, listenBacklog()); + if errno != 0 { + syscall.Close(fd.fd); + return nil, &OpError{"listen", "tcp", laddr, os.Errno(errno)}; + } + l = new(TCPListener); + l.fd = fd; + return l, nil +} + +// AcceptTCP accepts the next incoming call and returns the new connection +// and the remote address. +func (l *TCPListener) AcceptTCP() (c *TCPConn, err os.Error) { + if l == nil || l.fd == nil || l.fd.fd < 0 { + return nil, os.EINVAL + } + fd, err := l.fd.accept(sockaddrToTCP); + if err != nil { + return nil, err + } + return newTCPConn(fd), nil +} + +// Accept implements the Accept method in the Listener interface; +// it waits for the next call and returns a generic Conn. +func (l *TCPListener) Accept() (c Conn, err os.Error) { + c1, err := l.AcceptTCP(); + if err != nil { + return nil, err + } + return c1, nil; +} + +// Close stops listening on the TCP address. +// Already Accepted connections are not closed. +func (l *TCPListener) Close() os.Error { + if l == nil || l.fd == nil { + return os.EINVAL + } + return l.fd.Close() +} + +// Addr returns the listener's network address, a *TCPAddr. +func (l *TCPListener) Addr() Addr { + return l.fd.laddr; +} diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go new file mode 100644 index 000000000..03549f536 --- /dev/null +++ b/src/pkg/net/udpsock.go @@ -0,0 +1,299 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// UDP sockets + +package net + +import ( + "os"; + "syscall"; +) + +func sockaddrToUDP(sa syscall.Sockaddr) Addr { + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + return &UDPAddr{&sa.Addr, sa.Port}; + case *syscall.SockaddrInet6: + return &UDPAddr{&sa.Addr, sa.Port}; + } + return nil; +} + +// UDPAddr represents the address of a UDP end point. +type UDPAddr struct { + IP IP; + Port int; +} + +// Network returns the address's network name, "udp". +func (a *UDPAddr) Network() string { + return "udp"; +} + +func (a *UDPAddr) String() string { + return joinHostPort(a.IP.String(), itoa(a.Port)); + +} + +func (a *UDPAddr) family() int { + if a == nil || len(a.IP) <= 4 { + return syscall.AF_INET; + } + if ip := a.IP.To4(); ip != nil { + return syscall.AF_INET; + } + return syscall.AF_INET6; +} + +func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { + return ipToSockaddr(family, a.IP, a.Port); +} + +func (a *UDPAddr) toAddr() sockaddr { + if a == nil { // nil *UDPAddr + return nil; // nil interface + } + return a; +} + +// ResolveUDPAddr parses addr as a UDP address of the form +// host:port and resolves domain names or port names to +// numeric addresses. A literal IPv6 host address must be +// enclosed in square brackets, as in "[::]:80". +func ResolveUDPAddr(addr string) (*UDPAddr, os.Error) { + ip, port, err := hostPortToIP("udp", addr); + if err != nil { + return nil, err; + } + return &UDPAddr{ip, port}, nil; +} + +// UDPConn is the implementation of the Conn and PacketConn +// interfaces for UDP network connections. +type UDPConn struct { + fd *netFD; +} + +func newUDPConn(fd *netFD) *UDPConn { + return &UDPConn{fd}; +} + +func (c *UDPConn) ok() bool { + return c != nil && c.fd != nil; +} + +// Implementation of the Conn interface - see Conn for documentation. + +// Read reads data from a single UDP packet on the connection. +// If the slice b is smaller than the arriving packet, +// the excess packet data may be discarded. +// +// Read can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UDPConn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Read(b); +} + +// Write writes data to the connection as a single UDP packet. +// +// Write can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UDPConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b); +} + +// Close closes the UDP connection. +func (c *UDPConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close(); + c.fd = nil; + return err; +} + +// LocalAddr returns the local network address. +func (c *UDPConn) LocalAddr() Addr { + if !c.ok() { + return nil; + } + return c.fd.laddr; +} + +// RemoteAddr returns the remote network address, a *UDPAddr. +func (c *UDPConn) RemoteAddr() Addr { + if !c.ok() { + return nil; + } + return c.fd.raddr; +} + +// SetTimeout sets the read and write deadlines associated +// with the connection. +func (c *UDPConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec); +} + +// SetReadTimeout sets the time (in nanoseconds) that +// Read will wait for data before returning os.EAGAIN. +// Setting nsec == 0 (the default) disables the deadline. +func (c *UDPConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec); +} + +// SetWriteTimeout sets the time (in nanoseconds) that +// Write will wait to send its data before returning os.EAGAIN. +// Setting nsec == 0 (the default) disables the deadline. +// Even if write times out, it may return n > 0, indicating that +// some of the data was successfully written. +func (c *UDPConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec); +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *UDPConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes); +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *UDPConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes); +} + +// UDP-specific methods. + +// ReadFromUDP reads a UDP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFromUDP can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, sa, errno := syscall.Recvfrom(c.fd.fd, b, 0); + if errno != 0 { + err = os.Errno(errno); + } + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + addr = &UDPAddr{&sa.Addr, sa.Port}; + case *syscall.SockaddrInet6: + addr = &UDPAddr{&sa.Addr, sa.Port}; + } + return; +} + +// ReadFrom reads a UDP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFrom can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, uaddr, err := c.ReadFromUDP(b); + return n, uaddr.toAddr(), err; +} + +// WriteToUDP writes a UDP packet to addr via c, copying the payload from b. +// +// WriteToUDP can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetWriteTimeout. +// On packet-oriented connections such as UDP, write timeouts are rare. +func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + sa, err := addr.sockaddr(c.fd.family); + if err != nil { + return 0, err; + } + if errno := syscall.Sendto(c.fd.fd, b, 0, sa); errno != 0 { + return 0, os.Errno(errno); + } + return len(b), nil; +} + +// WriteTo writes a UDP packet with payload b to addr via c. +// +// WriteTo can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetWriteTimeout. +// On packet-oriented connections such as UDP, write timeouts are rare. +func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + a, ok := addr.(*UDPAddr); + if !ok { + return 0, &OpError{"writeto", "udp", addr, os.EINVAL}; + } + return c.WriteToUDP(b, a); +} + +// DialUDP connects to the remote address raddr on the network net, +// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used +// as the local address for the connection. +func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) + } + if raddr == nil { + return nil, &OpError{"dial", "udp", nil, errMissingAddress} + } + fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, "dial", sockaddrToUDP); + if e != nil { + return nil, e + } + return newUDPConn(fd), nil +} + +// ListenUDP listens for incoming UDP packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send UDP +// packets with per-packet addressing. +func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + return nil, &OpError{"listen", "udp", nil, errMissingAddress} + } + fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, "dial", sockaddrToUDP); + if e != nil { + return nil, e + } + return newUDPConn(fd), nil +} diff --git a/src/pkg/net/unixsock.go b/src/pkg/net/unixsock.go index e02658a3f..5c61f1437 100644 --- a/src/pkg/net/unixsock.go +++ b/src/pkg/net/unixsock.go @@ -11,14 +11,14 @@ import ( "syscall"; ) -func unixSocket(net, laddr, raddr string, mode string) (fd *netFD, err os.Error) { +func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err os.Error) { var proto int; switch net { default: return nil, UnknownNetworkError(net); case "unix": proto = syscall.SOCK_STREAM; - case "unix-dgram": + case "unixgram": proto = syscall.SOCK_DGRAM; } @@ -28,25 +28,30 @@ func unixSocket(net, laddr, raddr string, mode string) (fd *netFD, err os.Error) panic("unixSocket", mode); case "dial": - if laddr != "" { - return nil, &OpError{mode, net, raddr, &AddrError{"unexpected local address", laddr}} + if laddr != nil { + la = &syscall.SockaddrUnix{Name: laddr.Name}; } - if raddr == "" { - return nil, &OpError{mode, net, "", errMissingAddress} + if raddr != nil { + ra = &syscall.SockaddrUnix{Name: raddr.Name}; + } else if proto != syscall.SOCK_DGRAM || laddr == nil { + return nil, &OpError{mode, net, nil, errMissingAddress} } - ra = &syscall.SockaddrUnix{Name: raddr}; case "listen": - if laddr == "" { - return nil, &OpError{mode, net, "", errMissingAddress} + if laddr == nil { + return nil, &OpError{mode, net, nil, errMissingAddress} } - la = &syscall.SockaddrUnix{Name: laddr}; - if raddr != "" { - return nil, &OpError{mode, net, laddr, &AddrError{"unexpected remote address", raddr}} + la = &syscall.SockaddrUnix{Name: laddr.Name}; + if raddr != nil { + return nil, &OpError{mode, net, raddr, &AddrError{"unexpected remote address", raddr.String()}} } } - fd, err = socket(net, laddr, raddr, syscall.AF_UNIX, proto, 0, la, ra); + f := sockaddrToUnix; + if proto != syscall.SOCK_STREAM { + f = sockaddrToUnixgram; + } + fd, err = socket(net, syscall.AF_UNIX, proto, 0, la, ra, f); if err != nil { goto Error; } @@ -60,108 +65,318 @@ Error: return nil, &OpError{mode, net, addr, err}; } -// ConnUnix is an implementation of the Conn interface +// UnixAddr represents the address of a Unix domain socket end point. +type UnixAddr struct { + Name string; + Datagram bool; +} + +func sockaddrToUnix(sa syscall.Sockaddr) Addr { + if s, ok := sa.(*syscall.SockaddrUnix); ok { + return &UnixAddr{s.Name, false}; + } + return nil; +} + +func sockaddrToUnixgram(sa syscall.Sockaddr) Addr { + if s, ok := sa.(*syscall.SockaddrUnix); ok { + return &UnixAddr{s.Name, true}; + } + return nil; +} + +// Network returns the address's network name, "unix" or "unixgram". +func (a *UnixAddr) Network() string { + if a == nil || !a.Datagram { + return "unix"; + } + return "unixgram"; +} + +func (a *UnixAddr) String() string { + if a == nil { + return "<nil>" + } + return a.Name; +} + +func (a *UnixAddr) toAddr() Addr { + if a == nil { // nil *UnixAddr + return nil; // nil interface + } + return a; +} + +// ResolveUnixAddr parses addr as a Unix domain socket address. +// The string net gives the network name, "unix" or "unixgram". +func ResolveUnixAddr(net, addr string) (*UnixAddr, os.Error) { + var datagram bool; + switch net { + case "unix": + case "unixgram": + datagram = true; + default: + return nil, UnknownNetworkError(net); + } + return &UnixAddr{addr, datagram}, nil; +} + +// UnixConn is an implementation of the Conn interface // for connections to Unix domain sockets. -type ConnUnix struct { - connBase +type UnixConn struct { + fd *netFD; +} + +func newUnixConn(fd *netFD) *UnixConn { + return &UnixConn{fd} +} + +func (c *UnixConn) ok() bool { + return c != nil && c.fd != nil; +} + +// Implementation of the Conn interface - see Conn for documentation. + +// Read reads data from the Unix domain connection. +// +// Read can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UnixConn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Read(b); +} + +// Write writes data to the Unix domain connection. +// +// Write can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UnixConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b); +} + +// Close closes the Unix domain connection. +func (c *UnixConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close(); + c.fd = nil; + return err; +} + +// LocalAddr returns the local network address, a *UnixAddr. +// Unlike in other protocols, LocalAddr is usually nil for dialed connections. +func (c *UnixConn) LocalAddr() Addr { + if !c.ok() { + return nil; + } + return c.fd.laddr; +} + +// RemoteAddr returns the remote network address, a *UnixAddr. +// Unlike in other protocols, RemoteAddr is usually nil for connections +// accepted by a listener. +func (c *UnixConn) RemoteAddr() Addr { + if !c.ok() { + return nil; + } + return c.fd.raddr; +} + +// SetTimeout sets the read and write deadlines associated +// with the connection. +func (c *UnixConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec); +} + +// SetReadTimeout sets the time (in nanoseconds) that +// Read will wait for data before returning os.EAGAIN. +// Setting nsec == 0 (the default) disables the deadline. +func (c *UnixConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec); +} + +// SetWriteTimeout sets the time (in nanoseconds) that +// Write will wait to send its data before returning os.EAGAIN. +// Setting nsec == 0 (the default) disables the deadline. +// Even if write times out, it may return n > 0, indicating that +// some of the data was successfully written. +func (c *UnixConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec); } -func newConnUnix(fd *netFD, raddr string) *ConnUnix { - c := new(ConnUnix); - c.fd = fd; - c.raddr = raddr; - return c; +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *UnixConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes); +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *UnixConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes); +} + +// ReadFromUnix reads a packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFromUnix can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, sa, errno := syscall.Recvfrom(c.fd.fd, b, 0); + if errno != 0 { + err = os.Errno(errno); + } + switch sa := sa.(type) { + case *syscall.SockaddrUnix: + addr = &UnixAddr{sa.Name, c.fd.proto == syscall.SOCK_DGRAM}; + } + return; +} + +// ReadFrom reads a packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFrom can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, uaddr, err := c.ReadFromUnix(b); + return n, uaddr.toAddr(), err; +} + +// WriteToUnix writes a packet to addr via c, copying the payload from b. +// +// WriteToUnix can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetWriteTimeout. +// On packet-oriented connections such as UDP, write timeouts are rare. +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) { + return 0, os.EAFNOSUPPORT; + } + sa := &syscall.SockaddrUnix{Name: addr.Name}; + if errno := syscall.Sendto(c.fd.fd, b, 0, sa); errno != 0 { + return 0, os.Errno(errno); + } + return len(b), nil; } -// DialUnix is like Dial but can only connect to Unix domain sockets -// and returns a ConnUnix structure. The laddr argument must be -// the empty string; it is included only to match the signature of -// the other dial routines. -func DialUnix(net, laddr, raddr string) (c *ConnUnix, err os.Error) { +// WriteTo writes a packet to addr via c, copying the payload from b. +// +// WriteTo can be made to time out and return err == os.EAGAIN +// after a fixed time limit; see SetTimeout and SetWriteTimeout. +// On packet-oriented connections such as UDP, write timeouts are rare. +func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + a, ok := addr.(*UnixAddr); + if !ok { + return 0, &OpError{"writeto", "unix", addr, os.EINVAL}; + } + return c.WriteToUnix(b, a); +} + +// DialUDP connects to the remote address raddr on the network net, +// which must be "unix" or "unixdgram". If laddr is not nil, it is used +// as the local address for the connection. +func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err os.Error) { fd, e := unixSocket(net, laddr, raddr, "dial"); if e != nil { return nil, e } - return newConnUnix(fd, raddr), nil; + return newUnixConn(fd), nil; } -// ListenerUnix is a Unix domain socket listener. +// UnixListener is a Unix domain socket listener. // Clients should typically use variables of type Listener // instead of assuming Unix domain sockets. -type ListenerUnix struct { +type UnixListener struct { fd *netFD; - laddr string + path string; } // ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. -// Net can be either "unix" (stream sockets) or "unix-dgram" (datagram sockets). -func ListenUnix(net, laddr string) (l *ListenerUnix, err os.Error) { - fd, e := unixSocket(net, laddr, "", "listen"); +// Net must be "unix" (stream sockets). +func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) { + if net != "unix" && net != "unixgram" { + return nil, UnknownNetworkError(net); + } + if laddr != nil { + laddr = &UnixAddr{laddr.Name, net == "unixgram"}; // make our own copy + } + fd, e := unixSocket(net, laddr, nil, "listen"); if e != nil { if pe, ok := e.(*os.PathError); ok { e = pe.Error; } - // Check for socket ``in use'' but ``refusing connections,'' - // which means some program created it and exited - // without unlinking it from the file system. - // Clean up on that program's behalf and try again. - // Don't do this for Linux's ``abstract'' sockets, which begin with @. - if e != os.EADDRINUSE || laddr[0] == '@' { - return nil, e; - } - fd1, e1 := unixSocket(net, "", laddr, "dial"); - if e1 == nil { - fd1.Close(); - } - if pe, ok := e1.(*os.PathError); ok { - e1 = pe.Error; - } - if e1 != os.ECONNREFUSED { - return nil, e; - } - syscall.Unlink(laddr); - fd1, e1 = unixSocket(net, laddr, "", "listen"); - if e1 != nil { - return nil, e; - } - fd = fd1; + return nil, e; } e1 := syscall.Listen(fd.fd, 8); // listenBacklog()); if e1 != 0 { syscall.Close(fd.fd); return nil, &OpError{"listen", "unix", laddr, os.Errno(e1)}; } - return &ListenerUnix{fd, laddr}, nil; + return &UnixListener{fd, laddr.Name}, nil; } // AcceptUnix accepts the next incoming call and returns the new connection // and the remote address. -func (l *ListenerUnix) AcceptUnix() (c *ConnUnix, raddr string, err os.Error) { +func (l *UnixListener) AcceptUnix() (c *UnixConn, err os.Error) { if l == nil || l.fd == nil || l.fd.fd < 0 { - return nil, "", os.EINVAL + return nil, os.EINVAL } - fd, e := l.fd.accept(); + fd, e := l.fd.accept(sockaddrToUnix); if e != nil { - return nil, "", e + return nil, e } - return newConnUnix(fd, fd.raddr), raddr, nil + c = newUnixConn(fd); + return c, nil } // Accept implements the Accept method in the Listener interface; // it waits for the next call and returns a generic Conn. -func (l *ListenerUnix) Accept() (c Conn, raddr string, err os.Error) { - // TODO(rsc): Should return l.AcceptUnix() be okay here? - // There is a type conversion -- the first return arg of - // l.AcceptUnix() is *ConnUnix and it gets converted to Conn - // in the explicit assignment. - c, raddr, err = l.AcceptUnix(); - return; +func (l *UnixListener) Accept() (c Conn, err os.Error) { + c1, err := l.AcceptUnix(); + if err != nil { + return nil, err; + } + return c1, nil; } - // Close stops listening on the Unix address. // Already accepted connections are not closed. -func (l *ListenerUnix) Close() os.Error { +func (l *UnixListener) Close() os.Error { if l == nil || l.fd == nil { return os.EINVAL } @@ -176,8 +391,8 @@ func (l *ListenerUnix) Close() os.Error { // is at least compatible with the auto-remove // sequence in ListenUnix. It's only non-Go // programs that can mess us up. - if l.laddr[0] != '@' { - syscall.Unlink(l.laddr); + if l.path[0] != '@' { + syscall.Unlink(l.path); } err := l.fd.Close(); l.fd = nil; @@ -185,6 +400,26 @@ func (l *ListenerUnix) Close() os.Error { } // Addr returns the listener's network address. -func (l *ListenerUnix) Addr() string { - return l.fd.addr(); +func (l *UnixListener) Addr() Addr { + return l.fd.laddr; +} + +// ListenUnixgram listens for incoming Unix datagram packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send UDP +// packets with per-packet addressing. The network net must be "unixgram". +func ListenUnixgram(net string, laddr *UnixAddr) (c *UDPConn, err os.Error) { + switch net { + case "unixgram": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + return nil, &OpError{"listen", "unixgram", nil, errMissingAddress} + } + fd, e := unixSocket(net, laddr, nil, "listen"); + if e != nil { + return nil, e + } + return newUDPConn(fd), nil } diff --git a/src/pkg/os/error.go b/src/pkg/os/error.go index 963cca355..89c06d59d 100644 --- a/src/pkg/os/error.go +++ b/src/pkg/os/error.go @@ -74,6 +74,7 @@ var ( EADDRINUSE Error = Errno(syscall.EADDRINUSE); ECONNREFUSED Error = Errno(syscall.ECONNREFUSED); ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG); + EAFNOSUPPORT Error = Errno(syscall.EAFNOSUPPORT); ) // PathError records an error and the operation and file path that caused it. diff --git a/src/pkg/rpc/client.go b/src/pkg/rpc/client.go index 216ea5d5c..9b7eb5e40 100644 --- a/src/pkg/rpc/client.go +++ b/src/pkg/rpc/client.go @@ -129,7 +129,7 @@ func DialHTTP(network, address string) (*Client, os.Error) { err = os.ErrorString("unexpected HTTP response: " + resp.Status); } conn.Close(); - return nil, &net.OpError{"dial-http", network, address, err}; + return nil, &net.OpError{"dial-http", network+" "+address, nil, err}; } // Dial connects to an RPC server at the specified network address. diff --git a/src/pkg/rpc/server.go b/src/pkg/rpc/server.go index d4d4687d8..c0fd9373b 100644 --- a/src/pkg/rpc/server.go +++ b/src/pkg/rpc/server.go @@ -355,7 +355,7 @@ func (server *serverType) input(conn io.ReadWriteCloser) { func (server *serverType) accept(lis net.Listener) { for { - conn, _, err := lis.Accept(); + conn, err := lis.Accept(); if err != nil { log.Exit("rpc.Serve: accept:", err.String()); // TODO(r): exit? } diff --git a/src/pkg/rpc/server_test.go b/src/pkg/rpc/server_test.go index ce8e13e2e..00b348dbc 100644 --- a/src/pkg/rpc/server_test.go +++ b/src/pkg/rpc/server_test.go @@ -59,7 +59,7 @@ func startServer() { if e != nil { log.Exitf("net.Listen tcp :0: %v", e); } - serverAddr = l.Addr(); + serverAddr = l.Addr().String(); log.Stderr("Test RPC server listening on ", serverAddr); go Accept(l); @@ -69,7 +69,7 @@ func startServer() { log.Stderrf("net.Listen tcp :0: %v", e); os.Exit(1); } - httpServerAddr = l.Addr(); + httpServerAddr = l.Addr().String(); log.Stderr("Test HTTP RPC server listening on ", httpServerAddr); go http.Serve(l, nil); } |