]> Cypherpunks repositories - gostls13.git/commitdiff
add network listening & tests
authorRuss Cox <rsc@golang.org>
Wed, 17 Sep 2008 20:49:23 +0000 (13:49 -0700)
committerRuss Cox <rsc@golang.org>
Wed, 17 Sep 2008 20:49:23 +0000 (13:49 -0700)
R=r,presotto
OCL=15410
CL=15440

src/lib/io.go
src/lib/make.bash
src/lib/net/ip.go
src/lib/net/net.go
src/lib/net/socket_darwin.go
src/lib/net/socket_linux.go
src/runtime/proc.c
src/syscall/syscall_amd64_linux.s
test/dialgoogle.go [new file with mode: 0644]
test/tcpserver.go [new file with mode: 0644]

index d7770ebe7d3f33d97ac8fc562317b790eb8c578c..266d948fc4e6e8890ab15e9cb6d298bc4adad92d 100644 (file)
@@ -4,17 +4,7 @@
 
 package io
 import os "os"
-
-export func StringToBytes(b *[]byte, s string) bool {
-       if len(s) >= len(b) {
-               return false
-       }
-       for i := 0; i < len(s); i++ {
-               b[i] = s[i]
-       }
-       b[len(s)] = '\000';     // not necessary - memory is zeroed - but be explicit
-       return true
-}
+import syscall "syscall"
 
 export type Read interface {
        Read(p *[]byte) (n int, err *os.Error);
@@ -24,13 +14,17 @@ export type Write interface {
        Write(p *[]byte) (n int, err *os.Error);
 }
 
+export type ReadWrite interface {
+       Read(p *[]byte) (n int, err *os.Error);
+       Write(p *[]byte) (n int, err *os.Error);
+}
+
 export func WriteString(w Write, s string) (n int, err *os.Error) {
        b := new([]byte, len(s)+1)
-       if !StringToBytes(b, s) {
+       if !syscall.StringToBytes(b, s) {
                return -1, os.EINVAL
        }
        // BUG return w.Write(b[0:len(s)])
        r, e := w.Write(b[0:len(s)])
        return r, e
 }
-
index 3786f7fa4f58448774141c6e3527156f366acfa5..f2e23247dd3ec517023124164456ebabd0edcf37 100755 (executable)
@@ -22,3 +22,10 @@ do
        6g -o $GOROOT/pkg/$base.6 $i
 done
 
+for i in net
+do
+       echo; echo; echo %%%% making lib/$i %%%%; echo
+       cd $i
+       make install
+       cd ..
+done
index ddb5114c50211190d2b304082e981d445974c1ef..a96ae6709bbb0291df203055bff14bc1a4d59733 100644 (file)
@@ -336,6 +336,10 @@ func ParseIPv6(s string) *[]byte {
        if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
                ellipsis = 0;
                i = 2
+               // Might be only ellipsis
+               if i == len(s) {
+                       return p
+               }
        }
 
        // Loop, parsing hex numbers followed by colon.
@@ -343,12 +347,12 @@ func ParseIPv6(s string) *[]byte {
 L:     for j < IPv6len {
                // Hex number.
                n, i1, ok := xtoi(s, i)
-               if !ok || n >= 0xFFFF {
+               if !ok || n > 0xFFFF {
                        return nil
                }
 
                // If followed by dot, might be in trailing IPv4.
-               if s[i1] == '.' {
+               if i1 < len(s) && s[i1] == '.' {
                        if ellipsis < 0 && j != IPv6len - IPv4len {
                                // Not the right place.
                                return nil
@@ -389,7 +393,7 @@ L:  for j < IPv6len {
                i++
 
                // Look for ellipsis.
-               if s[i+1] == ':' {
+               if s[i] == ':' {
                        if ellipsis >= 0 {      // already have one
                                return nil
                        }
@@ -411,7 +415,7 @@ L:  for j < IPv6len {
                        return nil
                }
                n := IPv6len - j
-               for k := j; k >= ellipsis; k-- {
+               for k := j-1; k >= ellipsis; k-- {
                        p[k+n] = p[k]
                }
                for k := ellipsis+n-1; k>=ellipsis; k-- {
index d44f2d3050b6697fc074314a9f3dd565b68b5f91..cfd34bbab0ea7d438a7e10db959c915559b09aee 100644 (file)
@@ -19,7 +19,8 @@ func NewError(s string) *os.Error {
 }
 
 export var (
-       BadAddress = NewError("malformed addres");
+       BadAddress = NewError("malformed address");
+       MissingAddress = NewError("missing address");
        UnknownNetwork = NewError("unknown network");
        UnknownHost = NewError("unknown host");
        UnknownPort = NewError("unknown port");
@@ -39,10 +40,10 @@ func SplitHostPort(hostport string) (host, port string, err *os.Error) {
        if i < 0 {
                return "", "", BadAddress
        }
-       
+
        host = hostport[0:i];
        port = hostport[i+1:len(hostport)];
-       
+
        // Can put brackets around host ...
        if host[0] == '[' && host[len(host)-1] == ']' {
                host = host[1:len(host)-1]
@@ -69,6 +70,20 @@ func JoinHostPort(host, port string) string {
        return host + ":" + port
 }
 
+func dtoi(s string) (n int, ok bool) {
+       if s == "" || s[0] < '0' || s[0] > '9' {
+               return 0, false
+       }
+       n = 0;
+       for i := 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
+               n = n*10 + int(s[i] - '0')
+               if n >= 1000000 {       // bigger than we need
+                       return 0, false
+               }
+       }
+       return n, true
+}
+
 // Convert "host:port" into IP address and port.
 // For now, host and port must be numeric literals.
 // Eventually, we'll have name resolution.
@@ -78,22 +93,21 @@ func HostPortToIP(net string, hostport string) (ip *[]byte, iport int, err *os.E
        if err != nil {
                return nil, 0, err
        }
-       
+
        // TODO: Resolve host.
-       
+
        addr := ip.ParseIP(host);
        if addr == nil {
-print("Failed to parse: ", host, "\n");
                return nil, 0, UnknownHost
        }
-       
+
        // TODO: Resolve port.
-       
-       p, ok := strings.atoi(port);
+
+       p, ok := dtoi(port);
        if !ok || p < 0 || p > 0xFFFF {
                return nil, 0, UnknownPort
        }
-       
+
        return addr, p, nil
 }
 
@@ -117,7 +131,7 @@ func SockaddrToHostPort(sa *socket.Sockaddr) (hostport string, err *os.Error) {
 func boolint(b bool) int {
        if b {
                return 1
-       } 
+       }
        return 0
 }
 
@@ -127,7 +141,10 @@ func Socket(f, p, t int64, la, ra *socket.Sockaddr) (fd int64, err *os.Error) {
        if e != nil {
                return -1, e
        }
-       
+
+       // Allow reuse of recently-used addresses.
+       socket.setsockopt_int(s, socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+
        var r int64
        if la != nil {
                r, e = socket.bind(s, la)
@@ -136,7 +153,7 @@ func Socket(f, p, t int64, la, ra *socket.Sockaddr) (fd int64, err *os.Error) {
                        return -1, e
                }
        }
-       
+
        if ra != nil {
                r, e = socket.connect(s, ra)
                if e != nil {
@@ -144,7 +161,7 @@ func Socket(f, p, t int64, la, ra *socket.Sockaddr) (fd int64, err *os.Error) {
                        return -1, e
                }
        }
-       
+
        return s, nil
 }
 
@@ -256,11 +273,18 @@ func (c *ConnBase) SetLinger(sec int) *os.Error {
 // PreferIPv4 here should fall back to the IPv4 socket interface when possible.
 const PreferIPv4 = false
 
-func DialInternet(net, laddr, raddr string, proto int64) (fd int64, err *os.Error) {
+func InternetSocket(net, laddr, raddr string, proto int64) (fd int64, err *os.Error) {
        // Parse addresses (unless they are empty).
        var lip, rip *[]byte
        var lport, rport int
        var lerr, rerr *os.Error
+// BUG 6g doesn't zero var lists
+lip = nil;
+rip = nil;
+lport = 0;
+rport = 0;
+lerr = nil;
+rerr = nil
        if laddr != "" {
                lip, lport, lerr = HostPortToIP(net, laddr)
                if lerr != nil {
@@ -274,7 +298,7 @@ func DialInternet(net, laddr, raddr string, proto int64) (fd int64, err *os.Erro
                }
        }
 
-       // Figure out IP version.  
+       // Figure out IP version.
        // If network has a suffix like "tcp4", obey it.
        vers := 0;
        switch net[len(net)-1] {
@@ -303,8 +327,11 @@ func DialInternet(net, laddr, raddr string, proto int64) (fd int64, err *os.Erro
                cvt = &socket.IPv6ToSockaddr;
                family = socket.AF_INET6
        }
-       
+
        var la, ra *socket.Sockaddr;
+// BUG
+la = nil;
+ra = nil
        if lip != nil {
                la, lerr = cvt(lip, lport);
                if lerr != nil {
@@ -388,15 +415,23 @@ func (c *ConnTCP) SetKeepAlive(keepalive bool) *os.Error {
        return (&c.base).SetKeepAlive(keepalive)
 }
 
+func NewConnTCP(fd int64, raddr string) *ConnTCP {
+       c := new(ConnTCP);
+       c.base.fd = os.NewFD(fd);
+       c.base.raddr = raddr;
+       c.SetNoDelay(true);
+       return c
+}
+
 export func DialTCP(net, laddr, raddr string) (c *ConnTCP, err *os.Error) {
-       fd, e := DialInternet(net, laddr, raddr, socket.SOCK_STREAM)
+       if raddr == "" {
+               return nil, MissingAddress
+       }
+       fd, e := InternetSocket(net, laddr, raddr, socket.SOCK_STREAM)
        if e != nil {
                return nil, e
        }
-       c = new(ConnTCP);
-       c.base.fd = os.NewFD(fd);
-       c.SetNoDelay(true)
-       return c, nil
+       return NewConnTCP(fd, raddr), nil
 }
 
 
@@ -481,3 +516,83 @@ export func Dial(net, laddr, raddr string) (c Conn, err *os.Error) {
        return nil, UnknownNetwork
 }
 
+
+export type Listener interface {
+       Accept() (c Conn, raddr string, err *os.Error);
+       Close() *os.Error;
+}
+
+type NoListener struct { unused int }
+func (l *NoListener) Accept() (c Conn, raddr string, err *os.Error) {
+       return &noconn, "", os.EINVAL
+}
+func (l *NoListener) Close() *os.Error { return os.EINVAL }
+
+var nolistener NoListener
+
+export type ListenerTCP struct {
+       fd *os.FD;
+       laddr string
+}
+
+export func ListenTCP(net, laddr string) (l *ListenerTCP, err *os.Error) {
+       fd, e := InternetSocket(net, laddr, "", socket.SOCK_STREAM)
+       if e != nil {
+               return nil, e
+       }
+       r, e1 := socket.listen(fd, socket.ListenBacklog())
+       if e1 != nil {
+               syscall.close(fd)
+               return nil, e1
+       }
+       l = new(ListenerTCP);
+       l.fd = os.NewFD(fd);
+       return l, nil
+}
+
+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
+       }
+       var sa socket.Sockaddr;
+       fd, e := socket.accept(l.fd.fd, &sa)
+       if e != nil {
+               return nil, "", e
+       }
+       raddr, e = SockaddrToHostPort(&sa)
+       if e != nil {
+               syscall.close(fd)
+               return nil, "", e
+       }
+       return NewConnTCP(fd, raddr), raddr, nil
+}
+
+func (l *ListenerTCP) Accept() (c Conn, raddr string, err *os.Error) {
+       c1, r1, e1 := l.AcceptTCP()
+       if e1 != nil {
+               return &noconn, "", e1
+       }
+       return c1, r1, nil
+}
+
+func (l *ListenerTCP) Close() *os.Error {
+       if l == nil || l.fd == nil {
+               return os.EINVAL
+       }
+       return l.fd.Close()
+}
+
+export func Listen(net, laddr string) (l Listener, err *os.Error) {
+       switch net {
+       case "tcp", "tcp4", "tcp6":
+               l, err := ListenTCP(net, laddr)
+               if err != nil {
+                       return &nolistener, err
+               }
+               return l, nil
+/*
+       more here
+*/
+       }
+       return nil, UnknownNetwork
+}
index a114002eaba02fd65d12177fdefbafbe85a5f586..815fc6fa3762d7f99dad4beb080aef552a5caffe 100644 (file)
@@ -53,6 +53,8 @@ export const (
        IPPROTO_UDP = 17;
 
        TCP_NODELAY = 0x01;
+
+       SOMAXCONN = 128;
 )
 
 export type SockaddrUnix struct {
@@ -127,7 +129,7 @@ export func listen(fd, n int64) (ret int64, err *os.Error) {
 }
 
 export func accept(fd int64, sa *Sockaddr) (ret int64, err *os.Error) {
-       n := int32(sa.len);
+       n := SizeofSockaddr;
        r1, r2, e := syscall.Syscall(ACCEPT, fd, SockaddrPtr(sa), Int32Ptr(&n));
        return r1, os.ErrnoToError(e)
 }
@@ -229,3 +231,6 @@ export func SockaddrToIP(sa1 *Sockaddr) (p *[]byte, port int, err *os.Error) {
        return nil, 0, nil      // not reached
 }
 
+export func ListenBacklog() int64 {
+       return SOMAXCONN
+}
index 5dacaf58e5f3e7cab9e57028fa6f0584b4451290..650a753f16d2053766302ca7f05abe88967ed017 100644 (file)
@@ -63,6 +63,8 @@ export const (
        IPPROTO_UDP = 17;
 
        TCP_NODELAY = 0x01;
+
+       SOMAXCONN = 128;
 )
 
 export type SockaddrUnix struct {
@@ -145,7 +147,7 @@ export func listen(fd, n int64) (ret int64, err *os.Error) {
 }
 
 export func accept(fd int64, sa *Sockaddr) (ret int64, err *os.Error) {
-       n := int32(sa.Len());
+       n := SizeofSockaddr;
        r1, r2, e := syscall.Syscall(ACCEPT, fd, SockaddrPtr(sa), Int32Ptr(&n));
        return r1, os.ErrnoToError(e)
 }
@@ -208,11 +210,21 @@ export func IPv4ToSockaddr(p *[]byte, port int) (sa1 *Sockaddr, err *os.Error) {
        return SockaddrInet4ToSockaddr(sa), nil
 }
 
+var IPv6zero [ip.IPv6len]byte;
+
 export func IPv6ToSockaddr(p *[]byte, port int) (sa1 *Sockaddr, err *os.Error) {
        p = ip.ToIPv6(p)
        if p == nil || port < 0 || port > 0xFFFF {
                return nil, os.EINVAL
        }
+
+       // 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.ToIPv4(p); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 {
+               p = &IPv6zero;
+       }
+
        sa := new(SockaddrInet6);
        sa.family = AF_INET6;
        sa.port[0] = byte(port>>8);
@@ -245,3 +257,10 @@ export func SockaddrToIP(sa1 *Sockaddr) (p *[]byte, port int, err *os.Error) {
        return nil, 0, nil      // not reached
 }
 
+export func ListenBacklog() int64 {
+       // TODO: maybe /proc/sys/net/core/somaxconn
+       // and read the limit out of there, to take advantage of kernels
+       // that have increased the limit
+
+       return SOMAXCONN
+}
index 6a741f8822ba9a0ac3b11c63988065a60da06ae9..62efd4569103230c52138e47fe64de862097d027 100644 (file)
@@ -78,7 +78,7 @@ schedinit(void)
        byte *p;
 
        sched.mmax = 1;
-       p = getenv("gomaxprocs");
+       p = getenv("GOMAXPROCS");
        if(p != nil && (n = atoi(p)) != 0)
                sched.mmax = n;
        sched.mcount = 1;
index a0b72ceedb72a3ef9a78df7d0f23b40909550dc2..c279ff8bf416d30ea53e8014ca513530ae2a7b03 100644 (file)
@@ -37,7 +37,6 @@ TEXT syscall·Syscall6(SB),7,$-8
        MOVQ    48(SP), R8
        MOVQ    56(SP), R9
        MOVQ    8(SP), AX       // syscall entry
-       ADDQ    $0x2000000, AX
        SYSCALL
        JLS     6(PC)
        MOVQ    $-1, 64(SP)     // r1
diff --git a/test/dialgoogle.go b/test/dialgoogle.go
new file mode 100644 (file)
index 0000000..56ef2de
--- /dev/null
@@ -0,0 +1,96 @@
+// 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.
+
+// $G $F.go && $L $F.$A && ./$A.out
+
+package main
+
+import (
+       "net";
+       "flag";
+       "os";
+       "syscall"
+)
+
+// If an IPv6 tunnel is running (see go/stubl), we can try dialing a real IPv6 address.
+var ipv6 = false
+var ipv6_flag = flag.Bool("ipv6", false, &ipv6, "assume ipv6 tunnel is present")
+
+func StringToBuf(s string) *[]byte
+{
+       l := len(s);
+       b := new([]byte, l);
+       for i := 0; i < l; i++ {
+               b[i] = s[i];
+       }
+       return b;
+}
+
+
+// fd is already connected to www.google.com port 80.
+// Run an HTTP request to fetch the main page.
+func FetchGoogle(fd net.Conn) {
+       req := StringToBuf("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
+       n, errno := fd.Write(req);
+
+       buf := new([1000]byte);
+       n, errno = fd.Read(buf);
+
+       fd.Close();
+       if n < 1000 {
+               panic("short http read");
+       }
+}
+
+func TestDial(network, addr string) {
+       fd, err := net.Dial(network, "", addr)
+       if err != nil {
+               panic("net.Dial ", network, " ", addr, ": ", err.String())
+       }
+       FetchGoogle(fd)
+}
+
+func TestDialTCP(network, addr string) {
+       fd, err := net.DialTCP(network, "", addr)
+       if err != nil {
+               panic("net.DialTCP ", network, " ", addr, ": ", err.String())
+       }
+       FetchGoogle(fd)
+}
+
+var addrs = []string {
+       "74.125.19.99:80",
+       "074.125.019.099:0080",
+       "[::ffff:74.125.19.99]:80",
+       "[::ffff:4a7d:1363]:80",
+       "[0:0:0:0:0000:ffff:74.125.19.99]:80",
+       "[0:0:0:0:000000:ffff:74.125.19.99]:80",
+       "[0:0:0:0:0:ffff::74.125.19.99]:80",
+       "[2001:4860:0:2001::68]:80"     // ipv6.google.com; removed if ipv6 flag not set
+}
+
+func main()
+{
+       flag.Parse()
+       // If no ipv6 tunnel, don't try the last address.
+       if !ipv6 {
+               addrs[len(addrs)-1] = ""
+       }
+
+       for i := 0; i < len(addrs); i++ {
+               addr := addrs[i]
+               if addr == "" {
+                       continue
+               }
+       //      print(addr, "\n");
+               TestDial("tcp", addr);
+               TestDialTCP("tcp", addr)
+               if addr[0] != '[' {
+                       TestDial("tcp4", addr);
+                       TestDialTCP("tcp4", addr)
+               }
+               TestDial("tcp6", addr);
+               TestDialTCP("tcp6", addr)
+       }
+}
diff --git a/test/tcpserver.go b/test/tcpserver.go
new file mode 100644 (file)
index 0000000..b4de505
--- /dev/null
@@ -0,0 +1,99 @@
+// 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.
+
+// $G $F.go && $L $F.$A && GOMAXPROCS=3 ./$A.out
+// # TODO(rsc): GOMAXPROCS will go away eventually.
+// # 3 is one for Echo, one for Serve, one for Connect.
+
+package main
+import (
+       "os";
+       "io";
+       "net";
+       "syscall"
+)
+
+func StringToBuf(s string) *[]byte  {
+       l := len(s);
+       b := new([]byte, l);
+       for i := 0; i < l; i++ {
+               b[i] = s[i];
+       }
+       return b;
+}
+
+func Echo(fd io.ReadWrite, done *chan<- int) {
+       var buf [1024]byte;
+
+       for {
+               n, err := fd.Read(&buf);
+               if err != nil || n == 0 {
+                       break;
+               }
+               fd.Write((&buf)[0:n])
+       }
+       done <- 1
+}
+
+func Serve(network, addr string, listening, done *chan<- int) {
+       l, err := net.Listen(network, addr);
+       if err != nil {
+               panic("listen: "+err.String());
+       }
+       listening <- 1;
+
+       for {
+               fd, addr, err := l.Accept();
+               if err != nil {
+                       break;
+               }
+               echodone := new(chan int)
+               go Echo(fd, echodone);
+               <-echodone;     // make sure Echo stops
+               l.Close();
+       }
+       done <- 1
+}
+
+func Connect(network, addr string) {
+       fd, err := net.Dial(network, "", addr);
+       if err != nil {
+               panic("connect: "+err.String());
+       }
+
+       b := StringToBuf("hello, world\n");
+       var b1 [100]byte;
+
+       n, errno := fd.Write(b);
+       if n != len(b) {
+               panic("syscall.write in connect");
+       }
+
+       n, errno = fd.Read(&b1);
+       if n != len(b) {
+               panic("syscall.read in connect");
+       }
+
+//     os.Stdout.Write((&b1)[0:n]);
+       fd.Close();
+}
+
+func Test(network, listenaddr, dialaddr string) {
+//     print("Test ", network, " ", listenaddr, " ", dialaddr, "\n");
+       listening := new(chan int);
+       done := new(chan int);
+       go Serve(network, listenaddr, listening, done);
+       <-listening;    // wait for server to start
+       Connect(network, dialaddr);
+       <-done; // make sure server stopped
+}
+
+func main() {
+       Test("tcp", "0.0.0.0:9999", "127.0.0.1:9999");
+       Test("tcp", "[::]:9999", "[::ffff:127.0.0.1]:9999");
+       Test("tcp", "[::]:9999", "127.0.0.1:9999");
+       Test("tcp", "0.0.0.0:9999", "[::ffff:127.0.0.1]:9999");
+       sys.exit(0);    // supposed to happen on return, doesn't
+}
+