add network listening & tests
R=r,presotto
OCL=15410
CL=15440
diff --git a/src/lib/io.go b/src/lib/io.go
index d7770eb..266d948 100644
--- a/src/lib/io.go
+++ b/src/lib/io.go
@@ -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 @@
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
}
-
diff --git a/src/lib/make.bash b/src/lib/make.bash
index 3786f7f..f2e2324 100755
--- a/src/lib/make.bash
+++ b/src/lib/make.bash
@@ -22,3 +22,10 @@
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
diff --git a/src/lib/net/ip.go b/src/lib/net/ip.go
index ddb5114..a96ae67 100644
--- a/src/lib/net/ip.go
+++ b/src/lib/net/ip.go
@@ -336,6 +336,10 @@
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 @@
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 @@
i++
// Look for ellipsis.
- if s[i+1] == ':' {
+ if s[i] == ':' {
if ellipsis >= 0 { // already have one
return nil
}
@@ -411,7 +415,7 @@
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-- {
diff --git a/src/lib/net/net.go b/src/lib/net/net.go
index d44f2d3..cfd34bb 100644
--- a/src/lib/net/net.go
+++ b/src/lib/net/net.go
@@ -19,7 +19,8 @@
}
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 @@
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 @@
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 @@
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 boolint(b bool) int {
if b {
return 1
- }
+ }
return 0
}
@@ -127,7 +141,10 @@
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 @@
return -1, e
}
}
-
+
if ra != nil {
r, e = socket.connect(s, ra)
if e != nil {
@@ -144,7 +161,7 @@
return -1, e
}
}
-
+
return s, nil
}
@@ -256,11 +273,18 @@
// 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 @@
}
}
- // 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 @@
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 @@
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 @@
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
+}
diff --git a/src/lib/net/socket_darwin.go b/src/lib/net/socket_darwin.go
index a114002..815fc6f 100644
--- a/src/lib/net/socket_darwin.go
+++ b/src/lib/net/socket_darwin.go
@@ -53,6 +53,8 @@
IPPROTO_UDP = 17;
TCP_NODELAY = 0x01;
+
+ SOMAXCONN = 128;
)
export type SockaddrUnix struct {
@@ -127,7 +129,7 @@
}
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 @@
return nil, 0, nil // not reached
}
+export func ListenBacklog() int64 {
+ return SOMAXCONN
+}
diff --git a/src/lib/net/socket_linux.go b/src/lib/net/socket_linux.go
index 5dacaf5..650a753 100644
--- a/src/lib/net/socket_linux.go
+++ b/src/lib/net/socket_linux.go
@@ -63,6 +63,8 @@
IPPROTO_UDP = 17;
TCP_NODELAY = 0x01;
+
+ SOMAXCONN = 128;
)
export type SockaddrUnix struct {
@@ -145,7 +147,7 @@
}
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 @@
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 @@
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
+}
diff --git a/src/runtime/proc.c b/src/runtime/proc.c
index 6a741f8..62efd45 100644
--- a/src/runtime/proc.c
+++ b/src/runtime/proc.c
@@ -78,7 +78,7 @@
byte *p;
sched.mmax = 1;
- p = getenv("gomaxprocs");
+ p = getenv("GOMAXPROCS");
if(p != nil && (n = atoi(p)) != 0)
sched.mmax = n;
sched.mcount = 1;
diff --git a/src/syscall/syscall_amd64_linux.s b/src/syscall/syscall_amd64_linux.s
index a0b72ce..c279ff8 100644
--- a/src/syscall/syscall_amd64_linux.s
+++ b/src/syscall/syscall_amd64_linux.s
@@ -37,7 +37,6 @@
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
index 0000000..56ef2de
--- /dev/null
+++ b/test/dialgoogle.go
@@ -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
index 0000000..b4de505
--- /dev/null
+++ b/test/tcpserver.go
@@ -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
+}
+