]> Cypherpunks repositories - gostls13.git/commitdiff
split ipsock.go, sock.go, and unixsock.go out of net.go
authorRuss Cox <rsc@golang.org>
Sun, 1 Nov 2009 19:15:34 +0000 (11:15 -0800)
committerRuss Cox <rsc@golang.org>
Sun, 1 Nov 2009 19:15:34 +0000 (11:15 -0800)
prior to cleanup.  no changes, only moving.

remove dependencies on strconv and strings

R=r
http://go/go-review/1017010

src/pkg/Make.deps
src/pkg/net/Makefile
src/pkg/net/dnsclient.go
src/pkg/net/ip.go
src/pkg/net/ipsock.go [new file with mode: 0644]
src/pkg/net/net.go
src/pkg/net/parse.go
src/pkg/net/sock.go [new file with mode: 0644]
src/pkg/net/unixsock.go [new file with mode: 0644]

index d12040344d47ba5703ec7d0824803fcfec0e9eea..5ef36f5e2a9a59090647a80331b87245be85902c 100644 (file)
@@ -5,8 +5,8 @@ bignum.install: fmt.install
 bufio.install: io.install os.install strconv.install utf8.install
 bytes.install: os.install unicode.install utf8.install
 compress/flate.install: bufio.install bytes.install io.install math.install os.install sort.install strconv.install
-compress/gzip.install: bufio.install compress/flate.install hash/crc32.install hash.install io.install os.install
-compress/zlib.install: bufio.install compress/flate.install hash/adler32.install hash.install io.install os.install
+compress/gzip.install: bufio.install compress/flate.install hash.install hash/crc32.install io.install os.install
+compress/zlib.install: bufio.install compress/flate.install hash.install hash/adler32.install io.install os.install
 container/heap.install: sort.install
 container/list.install:
 container/ring.install:
@@ -19,7 +19,7 @@ crypto/rc4.install: os.install strconv.install
 crypto/sha1.install: hash.install os.install
 debug/dwarf.install: encoding/binary.install os.install strconv.install
 debug/macho.install: bytes.install debug/dwarf.install encoding/binary.install fmt.install io.install os.install strconv.install
-debug/elf.install: bytes.install debug/dwarf.install encoding/binary.install fmt.install io.install os.install strconv.install
+debug/elf.install: debug/dwarf.install encoding/binary.install fmt.install io.install os.install strconv.install
 debug/gosym.install: encoding/binary.install fmt.install os.install strconv.install strings.install
 debug/proc.install: container/vector.install fmt.install io.install os.install runtime.install strconv.install strings.install sync.install syscall.install
 ebnf.install: container/vector.install go/scanner.install go/token.install os.install strconv.install unicode.install utf8.install
@@ -47,13 +47,13 @@ hash/adler32.install: hash.install os.install
 hash/crc32.install: hash.install os.install
 http.install: bufio.install bytes.install container/vector.install fmt.install io.install log.install net.install os.install path.install strconv.install strings.install utf8.install
 image.install:
-image/png.install: bufio.install compress/zlib.install hash/crc32.install hash.install image.install io.install os.install strconv.install
+image/png.install: bufio.install compress/zlib.install hash.install hash/crc32.install image.install io.install os.install strconv.install
 io.install: bytes.install os.install sort.install strings.install sync.install
 json.install: bytes.install container/vector.install fmt.install math.install reflect.install strconv.install strings.install utf8.install
 log.install: fmt.install io.install os.install runtime.install time.install
 malloc.install:
 math.install:
-net.install: fmt.install io.install once.install os.install reflect.install strconv.install strings.install sync.install syscall.install
+net.install: fmt.install io.install once.install os.install reflect.install sync.install syscall.install
 once.install: sync.install
 os.install: once.install syscall.install
 path.install: io.install os.install strings.install
@@ -63,14 +63,13 @@ regexp.install: bytes.install container/vector.install io.install os.install run
 rpc.install: bufio.install fmt.install gob.install http.install io.install log.install net.install os.install reflect.install sort.install strings.install sync.install template.install unicode.install utf8.install
 runtime.install:
 sort.install:
-strconv.install: bytes.install math.install os.install unicode.install utf8.install
+strconv.install: bytes.install math.install os.install strings.install unicode.install utf8.install
 strings.install: os.install unicode.install utf8.install
 sync.install: runtime.install
 syscall.install: sync.install
 tabwriter.install: bytes.install container/vector.install io.install os.install utf8.install
 template.install: bytes.install container/vector.install fmt.install io.install os.install reflect.install runtime.install strings.install
 testing.install: flag.install fmt.install os.install runtime.install utf8.install
-testing/expect.install: fmt.install os.install rand.install reflect.install strings.install
 testing/iotest.install: bytes.install io.install log.install os.install
 testing/quick.install: flag.install fmt.install math.install os.install rand.install reflect.install strings.install
 time.install: io.install once.install os.install syscall.install
index a56f7c9b3678236b913adc4d26041188d87c717e..4c8ec38231d565e92bc55b0a8ecae8ca73c16a7d 100644 (file)
@@ -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
index a97f75cd503e069cfc2c411344bfe1cce3ad53cf..228d481cebc9166ee2864224d457d879abff6324 100644 (file)
 //     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 += ".";
index 8efdb577804c4fb1d99cc828ea2112bd290a7aef..6f1e07882003ef76ad6ad049ca25089819533f02 100644 (file)
@@ -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 (file)
index 0000000..6406952
--- /dev/null
@@ -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;
+}
+
index 5fb49d4bb98053ceeda0552d99d79ea73eabe20d..8669ed3aec43384957ecceae9d2bbcc0cf532ca6 100644 (file)
@@ -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);
+}
+
index ef5f1153aaf060fba8d1bc1f9653b712cb9d3520..4afe1dafb9668e1722064346c4df288c3ffbc9a4 100644 (file)
@@ -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 (file)
index 0000000..857a228
--- /dev/null
@@ -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 (file)
index 0000000..e02658a
--- /dev/null
@@ -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();
+}