diff options
author | Russ Cox <rsc@golang.org> | 2009-11-01 11:15:34 -0800 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2009-11-01 11:15:34 -0800 |
commit | dafb7d902fb108c6d7e67934490f7ea20c3fa29a (patch) | |
tree | a62a5df9fd7fc124c57200bb168c11e4723bfb36 /src/pkg/net | |
parent | e553412f7d28715525607dcd86f48723bc28423b (diff) | |
download | golang-dafb7d902fb108c6d7e67934490f7ea20c3fa29a.tar.gz |
split ipsock.go, sock.go, and unixsock.go out of net.go
prior to cleanup. no changes, only moving.
remove dependencies on strconv and strings
R=r
http://go/go-review/1017010
Diffstat (limited to 'src/pkg/net')
-rw-r--r-- | src/pkg/net/Makefile | 3 | ||||
-rw-r--r-- | src/pkg/net/dnsclient.go | 4 | ||||
-rw-r--r-- | src/pkg/net/ip.go | 3 | ||||
-rw-r--r-- | src/pkg/net/ipsock.go | 362 | ||||
-rw-r--r-- | src/pkg/net/net.go | 854 | ||||
-rw-r--r-- | src/pkg/net/parse.go | 45 | ||||
-rw-r--r-- | src/pkg/net/sock.go | 219 | ||||
-rw-r--r-- | src/pkg/net/unixsock.go | 190 |
8 files changed, 868 insertions, 812 deletions
diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile index a56f7c9b3..4c8ec3823 100644 --- a/src/pkg/net/Makefile +++ b/src/pkg/net/Makefile @@ -12,8 +12,11 @@ GOFILES=\ fd.go\ fd_$(GOOS).go\ ip.go\ + ipsock.go\ net.go\ parse.go\ port.go\ + sock.go\ + unixsock.go\ include $(GOROOT)/src/Make.pkg diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go index a97f75cd5..228d481ce 100644 --- a/src/pkg/net/dnsclient.go +++ b/src/pkg/net/dnsclient.go @@ -11,14 +11,12 @@ // Could have a small cache. // Random UDP source port (net.Dial should do that for us). // Random request IDs. -// More substantial error reporting. package net import ( "once"; "os"; - "strings"; ) // DNSError represents a DNS lookup error. @@ -240,7 +238,7 @@ func LookupHost(name string) (cname string, addrs []string, err os.Error) { // If name is rooted (trailing dot) or has enough dots, // try it by itself first. rooted := len(name) > 0 && name[len(name)-1] == '.'; - if rooted || strings.Count(name, ".") >= cfg.ndots { + if rooted || count(name, '.') >= cfg.ndots { rname := name; if !rooted { rname += "."; diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go index 8efdb5778..6f1e07882 100644 --- a/src/pkg/net/ip.go +++ b/src/pkg/net/ip.go @@ -12,9 +12,6 @@ package net -import ( -) - // IP address lengths (bytes). const ( IPv4len = 4; diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go new file mode 100644 index 000000000..640695256 --- /dev/null +++ b/src/pkg/net/ipsock.go @@ -0,0 +1,362 @@ +// 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. + +// IP sockets + +package net + +import ( + "os"; + "syscall"; +) + +// Should we try to use the IPv4 socket interface if we're +// only dealing with IPv4 sockets? As long as the host system +// understands IPv6, it's okay to pass IPv4 addresses to the IPv6 +// interface. That simplifies our code and is most general. +// Unfortunately, we need to run on kernels built without IPv6 support too. +// So probe the kernel to figure it out. +func kernelSupportsIPv6() bool { + fd, e := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); + if fd >= 0 { + syscall.Close(fd) + } + return e == 0 +} + +var preferIPv4 = !kernelSupportsIPv6() + +// TODO(rsc): if syscall.OS == "linux", we're supposd to read +// /proc/sys/net/core/somaxconn, +// to take advantage of kernels that have raised the limit. +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; + } + } + + // Figure out IP version. + // If network has a suffix like "tcp4", obey it. + vers := 0; + switch net[len(net)-1] { + case '4': + vers = 4; + case '6': + vers = 6; + 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 + } + } + + 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 { + goto Error; + } + } + if rip != nil { + if ra, err = ipToSockaddr(family, rip, rport); err != nil { + goto Error; + } + } + + fd, err = socket(net, laddr, raddr, family, proto, 0, la, ra); + if err != nil { + goto Error; + } + return fd, nil; + +Error: + addr := raddr; + if mode == "listen" { + addr = laddr; + } + 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} + } + fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_DGRAM, "dial"); + if e != nil { + return nil, e + } + return newConnUDP(fd, raddr), nil +} + +func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { + switch family { + case syscall.AF_INET: + if ip = ip.To4(); ip == nil { + return nil, os.EINVAL + } + s := new(syscall.SockaddrInet4); + for i := 0; i < IPv4len; i++ { + s.Addr[i] = ip[i]; + } + s.Port = port; + return s, nil; + case syscall.AF_INET6: + // 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. + if p4 := ip.To4(); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 { + ip = IPzero; + } + if ip = ip.To16(); ip == nil { + return nil, os.EINVAL + } + s := new(syscall.SockaddrInet6); + for i := 0; i < IPv6len; i++ { + s.Addr[i] = ip[i]; + } + s.Port = port; + return s, nil; + } + return nil, os.EINVAL; +} + +// Split "host:port" into "host" and "port". +// Host cannot contain colons unless it is bracketed. +func splitHostPort(hostport string) (host, port string, err os.Error) { + // The port starts after the last colon. + i := last(hostport, ':'); + if i < 0 { + err = &AddrError{"missing port in address", hostport}; + return; + } + + host, port = hostport[0:i], hostport[i+1:len(hostport)]; + + // Can put brackets around host ... + if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { + host = host[1:len(host)-1] + } else { + // ... but if there are no brackets, no colons. + if byteIndex(host, ':') >= 0 { + err = &AddrError{"too many colons in address", hostport}; + return; + } + } + return; +} + +// Join "host" and "port" into "host:port". +// If host contains colons, will join into "[host]:port". +func joinHostPort(host, port string) string { + // If host has colons, have to bracket it. + if byteIndex(host, ':') >= 0 { + return "[" + host + "]:" + port + } + return host + ":" + port +} + +// 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) { + 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 { + 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; + } + } + + p, i, ok := dtoi(port, 0); + if !ok || i != len(port) { + p, err = LookupPort(net, port); + if err != nil { + goto Error; + } + } + if p < 0 || p > 0xFFFF { + err = &AddrError{"invalid port", port}; + goto Error; + } + + return addr, p, nil; + +Error: + return nil, 0, err; +} + diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go index 5fb49d4bb..8669ed3ae 100644 --- a/src/pkg/net/net.go +++ b/src/pkg/net/net.go @@ -4,52 +4,11 @@ package net -import ( - "os"; - "reflect"; - "strconv"; - "strings"; - "syscall"; -) +// TODO(rsc): +// support for raw IP sockets +// support for raw ethernet sockets -var errMissingAddress = os.ErrorString("missing address") - -type OpError struct { - Op string; - Net string; - Addr string; - Error os.Error; -} - -func (e *OpError) String() string { - s := e.Op; - if e.Net != "" { - s += " " + e.Net; - } - if e.Addr != "" { - s += " " + e.Addr; - } - s += ": " + e.Error.String(); - return s; -} - -type AddrError struct { - Error string; - Addr string; -} - -func (e *AddrError) String() string { - s := e.Error; - if e.Addr != "" { - s += " " + e.Addr; - } - return s; -} - -type UnknownNetworkError string -func (e UnknownNetworkError) String() string { - return "unknown network " + string(e); -} +import "os" // Conn is a generic network connection. type Conn interface { @@ -134,681 +93,12 @@ type Conn interface { BindToDevice(dev string) os.Error; } -// Should we try to use the IPv4 socket interface if we're -// only dealing with IPv4 sockets? As long as the host system -// understands IPv6, it's okay to pass IPv4 addresses to the IPv6 -// interface. That simplifies our code and is most general. -// Unfortunately, we need to run on kernels built without IPv6 support too. -// So probe the kernel to figure it out. -func kernelSupportsIPv6() bool { - fd, e := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); - if fd >= 0 { - syscall.Close(fd) - } - return e == 0 -} - -var preferIPv4 = !kernelSupportsIPv6() - -// TODO(rsc): if syscall.OS == "linux", we're supposd to read -// /proc/sys/net/core/somaxconn, -// to take advantage of kernels that have raised the limit. -func listenBacklog() int { - return syscall.SOMAXCONN -} - -// Split "host:port" into "host" and "port". -// Host cannot contain colons unless it is bracketed. -func splitHostPort(hostport string) (host, port string, err os.Error) { - // The port starts after the last colon. - i := strings.LastIndex(hostport, ":"); - if i < 0 { - err = &AddrError{"missing port in address", hostport}; - return; - } - - host, port = hostport[0:i], hostport[i+1:len(hostport)]; - - // Can put brackets around host ... - if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { - host = host[1:len(host)-1] - } else { - // ... but if there are no brackets, no colons. - if byteIndex(host, ':') >= 0 { - err = &AddrError{"too many colons in address", hostport}; - return; - } - } - return; -} - -// Join "host" and "port" into "host:port". -// If host contains colons, will join into "[host]:port". -func joinHostPort(host, port string) string { - // If host has colons, have to bracket it. - if byteIndex(host, ':') >= 0 { - return "[" + host + "]:" + port - } - return host + ":" + port -} - -// 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) { - 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 { - 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; - } - } - - p, i, ok := dtoi(port, 0); - if !ok || i != len(port) { - p, err = LookupPort(net, port); - if err != nil { - goto Error; - } - } - if p < 0 || p > 0xFFFF { - err = &AddrError{"invalid port", port}; - goto Error; - } - - return addr, p, nil; - -Error: - return nil, 0, err; -} - -type UnknownSocketError struct { - sa syscall.Sockaddr; -} -func (e *UnknownSocketError) String() string { - return "unknown socket address type " + reflect.Typeof(e.sa).String() -} - -func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) { - switch a := sa.(type) { - case *syscall.SockaddrInet4: - return joinHostPort(IP(&a.Addr).String(), strconv.Itoa(a.Port)), nil; - case *syscall.SockaddrInet6: - return joinHostPort(IP(&a.Addr).String(), strconv.Itoa(a.Port)), nil; - case *syscall.SockaddrUnix: - return a.Name, nil; - } - - return "", &UnknownSocketError{sa}; -} - -func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { - switch family { - case syscall.AF_INET: - if ip = ip.To4(); ip == nil { - return nil, os.EINVAL - } - s := new(syscall.SockaddrInet4); - for i := 0; i < IPv4len; i++ { - s.Addr[i] = ip[i]; - } - s.Port = port; - return s, nil; - case syscall.AF_INET6: - // 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. - if p4 := ip.To4(); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 { - ip = IPzero; - } - if ip = ip.To16(); ip == nil { - return nil, os.EINVAL - } - s := new(syscall.SockaddrInet6); - for i := 0; i < IPv6len; i++ { - s.Addr[i] = ip[i]; - } - s.Port = port; - return s, nil; - } - return nil, os.EINVAL; -} - -// Boolean to int. -func boolint(b bool) int { - if b { - return 1 - } - return 0 -} - -// Generic socket creation. -func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd *netFD, err os.Error) { - // See ../syscall/exec.go for description of ForkLock. - syscall.ForkLock.RLock(); - s, e := syscall.Socket(f, p, t); - if e != 0 { - syscall.ForkLock.RUnlock(); - return nil, os.Errno(e) - } - syscall.CloseOnExec(s); - syscall.ForkLock.RUnlock(); - - // Allow reuse of recently-used addresses. - syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); - - if la != nil { - e = syscall.Bind(s, la); - if e != 0 { - syscall.Close(s); - return nil, os.Errno(e) - } - } - - if ra != nil { - e = syscall.Connect(s, ra); - if e != 0 { - syscall.Close(s); - return nil, os.Errno(e) - } - } - - fd, err = newFD(s, net, laddr, raddr); - if err != nil { - syscall.Close(s); - return nil, err - } - - 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)); -} - -func setsockoptNsec(fd, level, opt int, nsec int64) os.Error { - var tv = syscall.NsecToTimeval(nsec); - 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 (c *connBase) SetWriteBuffer(bytes int) os.Error { - return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes); -} - -func (c *connBase) SetReadTimeout(nsec int64) os.Error { - c.fd.rdeadline_delta = nsec; - return nil; -} - -func (c *connBase) SetWriteTimeout(nsec int64) os.Error { - c.fd.wdeadline_delta = nsec; - return nil; -} - -func (c *connBase) SetTimeout(nsec int64) os.Error { - if e := c.SetReadTimeout(nsec); e != nil { - return e - } - return c.SetWriteTimeout(nsec) -} - -func (c *connBase) SetReuseAddr(reuse bool) os.Error { - return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)); -} - -func (c *connBase) BindToDevice(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 (c *connBase) SetKeepAlive(keepalive bool) os.Error { - return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)); -} - -func (c *connBase) SetLinger(sec int) os.Error { - var l syscall.Linger; - if sec >= 0 { - l.Onoff = 1; - l.Linger = int32(sec); - } else { - l.Onoff = 0; - l.Linger = 0; - } - e := syscall.SetsockoptLinger(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_LINGER, &l); - return os.NewSyscallError("setsockopt", e); -} - - -// 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; - } - } - - // Figure out IP version. - // If network has a suffix like "tcp4", obey it. - vers := 0; - switch net[len(net)-1] { - case '4': - vers = 4; - case '6': - vers = 6; - 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 - } - } - - 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 { - goto Error; - } - } - if rip != nil { - if ra, err = ipToSockaddr(family, rip, rport); err != nil { - goto Error; - } - } - - fd, err = socket(net, laddr, raddr, family, proto, 0, la, ra); - if err != nil { - goto Error; - } - return fd, nil; - -Error: - addr := raddr; - if mode == "listen" { - addr = laddr; - } - 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} - } - fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_DGRAM, "dial"); - if e != nil { - return nil, e - } - return newConnUDP(fd, raddr), nil -} - - -// TODO: raw IP connections - -// TODO: raw ethernet connections - - -// Unix domain sockets - -func unixSocket(net, laddr, raddr string, 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": - proto = syscall.SOCK_DGRAM; - } - - var la, ra syscall.Sockaddr; - switch mode { - default: - panic("unixSocket", mode); - - case "dial": - if laddr != "" { - return nil, &OpError{mode, net, raddr, &AddrError{"unexpected local address", laddr}} - } - if raddr == "" { - return nil, &OpError{mode, net, "", errMissingAddress} - } - ra = &syscall.SockaddrUnix{Name: raddr}; - - case "listen": - if laddr == "" { - return nil, &OpError{mode, net, "", errMissingAddress} - } - la = &syscall.SockaddrUnix{Name: laddr}; - if raddr != "" { - return nil, &OpError{mode, net, laddr, &AddrError{"unexpected remote address", raddr}} - } - } - - fd, err = socket(net, laddr, raddr, syscall.AF_UNIX, proto, 0, la, ra); - if err != nil { - goto Error; - } - return fd, nil; - -Error: - addr := raddr; - if mode == "listen" { - addr = laddr; - } - return nil, &OpError{mode, net, addr, err}; -} - -// ConnUnix is an implementation of the Conn interface -// for connections to Unix domain sockets. -type ConnUnix struct { - connBase -} - -func newConnUnix(fd *netFD, raddr string) *ConnUnix { - c := new(ConnUnix); - c.fd = fd; - c.raddr = raddr; - return c; -} - -// 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) { - fd, e := unixSocket(net, laddr, raddr, "dial"); - if e != nil { - return nil, e - } - return newConnUnix(fd, raddr), nil; -} - -// ListenerUnix is a Unix domain socket listener. -// Clients should typically use variables of type Listener -// instead of assuming Unix domain sockets. -type ListenerUnix struct { - fd *netFD; - laddr 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"); - 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; - } - 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; -} - -// 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) { - 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 newConnUnix(fd, fd.raddr), raddr, 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; -} - - -// Close stops listening on the Unix address. -// Already accepted connections are not closed. -func (l *ListenerUnix) Close() os.Error { - if l == nil || l.fd == nil { - return os.EINVAL - } - - // The operating system doesn't clean up - // the file that announcing created, so - // we have to clean it up ourselves. - // There's a race here--we can't know for - // sure whether someone else has come along - // and replaced our socket name already-- - // but this sequence (remove then close) - // 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); - } - err := l.fd.Close(); - l.fd = nil; - return err; -} - -// Addr returns the listener's network address. -func (l *ListenerUnix) Addr() string { - return l.fd.addr(); +// A Listener is a generic network listener. +// Accept waits for the next connection and Close closes the connection. +type Listener interface { + Accept() (c Conn, raddr string, err os.Error); + Close() os.Error; + Addr() string; // Listener's network address } // Dial connects to the remote address raddr on the network net. @@ -840,92 +130,10 @@ func Dial(net, laddr, raddr string) (c Conn, err os.Error) { case "unix", "unix-dgram": c, err := DialUnix(net, laddr, raddr); return c, err; -/* - case "ether": - c, err := DialEther(net, laddr, raddr); - return c, err; - case "ipv4": - c, err := DialIPv4(net, laddr, raddr); - return c, err; - case "ipv6": - c, err := DialIPv6(net, laddr, raddr); - return c, err -*/ } return nil, &OpError{"dial", net, raddr, UnknownNetworkError(net)}; } -// A Listener is a generic network listener. -// Accept waits for the next connection and Close closes the connection. -type Listener interface { - Accept() (c Conn, raddr string, err os.Error); - Close() os.Error; - Addr() string; // Listener's network address -} - -// 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(); -} - // Listen announces on the local network address laddr. // The network string net must be "tcp", "tcp4", "tcp6", // "unix", or "unix-dgram". @@ -943,11 +151,47 @@ func Listen(net, laddr string) (l Listener, err os.Error) { return nil, err; } return l, nil; -/* - more here -*/ // BUG(rsc): Listen should support UDP. } return nil, UnknownNetworkError(net); } +var errMissingAddress = os.ErrorString("missing address") + +type OpError struct { + Op string; + Net string; + Addr string; + Error os.Error; +} + +func (e *OpError) String() string { + s := e.Op; + if e.Net != "" { + s += " " + e.Net; + } + if e.Addr != "" { + s += " " + e.Addr; + } + s += ": " + e.Error.String(); + return s; +} + +type AddrError struct { + Error string; + Addr string; +} + +func (e *AddrError) String() string { + s := e.Error; + if e.Addr != "" { + s += " " + e.Addr; + } + return s; +} + +type UnknownNetworkError string +func (e UnknownNetworkError) String() string { + return "unknown network " + string(e); +} + diff --git a/src/pkg/net/parse.go b/src/pkg/net/parse.go index ef5f1153a..4afe1dafb 100644 --- a/src/pkg/net/parse.go +++ b/src/pkg/net/parse.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Simple file i/o and string manipulation, to avoid -// depending on strconv and bufio. +// depending on strconv and bufio and strings. package net @@ -154,3 +154,46 @@ func xtoi(s string, i0 int) (n int, i int, ok bool) { return n, i, true } +// Integer to decimal. +func itoa(i int) string { + var buf [30]byte; + n := len(buf); + neg := false; + if i < 0 { + i = -i; + neg = true; + } + ui := uint(i); + for ui > 0 || n == len(buf) { + n--; + buf[n] = byte('0' + ui%10); + ui /= 10; + } + if neg { + n--; + buf[n] = '-'; + } + return string(buf[n:len(buf)]); +} + +// Number of occurrences of b in s. +func count(s string, b byte) int { + n := 0; + for i := 0; i < len(s); i++ { + if s[i] == b { + n++; + } + } + return n; +} + +// Index of rightmost occurrence of b in s. +func last(s string, b byte) int { + i := len(s); + for i--; i >= 0; i-- { + if s[i] == b { + break; + } + } + return i; +} diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go new file mode 100644 index 000000000..857a228bd --- /dev/null +++ b/src/pkg/net/sock.go @@ -0,0 +1,219 @@ +// 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. + +// Sockets + +package net + +import ( + "os"; + "reflect"; + "syscall"; +) + +// Boolean to int. +func boolint(b bool) int { + if b { + return 1 + } + return 0 +} + +// Generic socket creation. +func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd *netFD, err os.Error) { + // See ../syscall/exec.go for description of ForkLock. + syscall.ForkLock.RLock(); + s, e := syscall.Socket(f, p, t); + if e != 0 { + syscall.ForkLock.RUnlock(); + return nil, os.Errno(e) + } + syscall.CloseOnExec(s); + syscall.ForkLock.RUnlock(); + + // Allow reuse of recently-used addresses. + syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); + + if la != nil { + e = syscall.Bind(s, la); + if e != 0 { + syscall.Close(s); + return nil, os.Errno(e) + } + } + + if ra != nil { + e = syscall.Connect(s, ra); + if e != 0 { + syscall.Close(s); + return nil, os.Errno(e) + } + } + + fd, err = newFD(s, net, laddr, raddr); + if err != nil { + syscall.Close(s); + return nil, err + } + + 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)); +} + +func setsockoptNsec(fd, level, opt int, nsec int64) os.Error { + var tv = syscall.NsecToTimeval(nsec); + 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 (c *connBase) SetWriteBuffer(bytes int) os.Error { + return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes); +} + +func (c *connBase) SetReadTimeout(nsec int64) os.Error { + c.fd.rdeadline_delta = nsec; + return nil; +} + +func (c *connBase) SetWriteTimeout(nsec int64) os.Error { + c.fd.wdeadline_delta = nsec; + return nil; +} + +func (c *connBase) SetTimeout(nsec int64) os.Error { + if e := c.SetReadTimeout(nsec); e != nil { + return e + } + return c.SetWriteTimeout(nsec) +} + +func (c *connBase) SetReuseAddr(reuse bool) os.Error { + return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)); +} + +func (c *connBase) BindToDevice(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 (c *connBase) SetKeepAlive(keepalive bool) os.Error { + return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)); +} + +func (c *connBase) SetLinger(sec int) os.Error { + var l syscall.Linger; + if sec >= 0 { + l.Onoff = 1; + l.Linger = int32(sec); + } else { + l.Onoff = 0; + l.Linger = 0; + } + e := syscall.SetsockoptLinger(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_LINGER, &l); + return os.NewSyscallError("setsockopt", e); +} + + +type UnknownSocketError struct { + sa syscall.Sockaddr; +} +func (e *UnknownSocketError) String() string { + return "unknown socket address type " + reflect.Typeof(e.sa).String() +} + +func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) { + switch a := sa.(type) { + case *syscall.SockaddrInet4: + return joinHostPort(IP(&a.Addr).String(), itoa(a.Port)), nil; + case *syscall.SockaddrInet6: + return joinHostPort(IP(&a.Addr).String(), itoa(a.Port)), nil; + case *syscall.SockaddrUnix: + return a.Name, nil; + } + + return "", &UnknownSocketError{sa}; +} + diff --git a/src/pkg/net/unixsock.go b/src/pkg/net/unixsock.go new file mode 100644 index 000000000..e02658a3f --- /dev/null +++ b/src/pkg/net/unixsock.go @@ -0,0 +1,190 @@ +// 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. + +// Unix domain sockets + +package net + +import ( + "os"; + "syscall"; +) + +func unixSocket(net, laddr, raddr string, 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": + proto = syscall.SOCK_DGRAM; + } + + var la, ra syscall.Sockaddr; + switch mode { + default: + panic("unixSocket", mode); + + case "dial": + if laddr != "" { + return nil, &OpError{mode, net, raddr, &AddrError{"unexpected local address", laddr}} + } + if raddr == "" { + return nil, &OpError{mode, net, "", errMissingAddress} + } + ra = &syscall.SockaddrUnix{Name: raddr}; + + case "listen": + if laddr == "" { + return nil, &OpError{mode, net, "", errMissingAddress} + } + la = &syscall.SockaddrUnix{Name: laddr}; + if raddr != "" { + return nil, &OpError{mode, net, laddr, &AddrError{"unexpected remote address", raddr}} + } + } + + fd, err = socket(net, laddr, raddr, syscall.AF_UNIX, proto, 0, la, ra); + if err != nil { + goto Error; + } + return fd, nil; + +Error: + addr := raddr; + if mode == "listen" { + addr = laddr; + } + return nil, &OpError{mode, net, addr, err}; +} + +// ConnUnix is an implementation of the Conn interface +// for connections to Unix domain sockets. +type ConnUnix struct { + connBase +} + +func newConnUnix(fd *netFD, raddr string) *ConnUnix { + c := new(ConnUnix); + c.fd = fd; + c.raddr = raddr; + return c; +} + +// 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) { + fd, e := unixSocket(net, laddr, raddr, "dial"); + if e != nil { + return nil, e + } + return newConnUnix(fd, raddr), nil; +} + +// ListenerUnix is a Unix domain socket listener. +// Clients should typically use variables of type Listener +// instead of assuming Unix domain sockets. +type ListenerUnix struct { + fd *netFD; + laddr 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"); + 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; + } + 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; +} + +// 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) { + 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 newConnUnix(fd, fd.raddr), raddr, 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; +} + + +// Close stops listening on the Unix address. +// Already accepted connections are not closed. +func (l *ListenerUnix) Close() os.Error { + if l == nil || l.fd == nil { + return os.EINVAL + } + + // The operating system doesn't clean up + // the file that announcing created, so + // we have to clean it up ourselves. + // There's a race here--we can't know for + // sure whether someone else has come along + // and replaced our socket name already-- + // but this sequence (remove then close) + // 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); + } + err := l.fd.Close(); + l.fd = nil; + return err; +} + +// Addr returns the listener's network address. +func (l *ListenerUnix) Addr() string { + return l.fd.addr(); +} |