preliminary network - just Dial for now
R=r,presotto
OCL=15393
CL=15399
diff --git a/src/lib/net/net.go b/src/lib/net/net.go
new file mode 100644
index 0000000..d44f2d3
--- /dev/null
+++ b/src/lib/net/net.go
@@ -0,0 +1,483 @@
+// 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.
+
+package net
+
+import (
+ "os";
+ "ip";
+ "socket";
+ "strings";
+ "syscall"
+)
+
+func NewError(s string) *os.Error {
+ e := new(os.Error);
+ e.s = s;
+ return e
+}
+
+export var (
+ BadAddress = NewError("malformed addres");
+ UnknownNetwork = NewError("unknown network");
+ UnknownHost = NewError("unknown host");
+ UnknownPort = NewError("unknown port");
+ UnknownSocketFamily = NewError("unknown socket family");
+)
+
+// 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.
+ var i int
+ for i = len(hostport)-1; i >= 0; i-- {
+ if hostport[i] == ':' {
+ break
+ }
+ }
+ 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]
+ } else {
+ // ... but if there are no brackets, no colons.
+ for i := 0; i < len(host); i++ {
+ if host[i] == ':' {
+ return "", "", BadAddress
+ }
+ }
+ }
+ return host, port, nil
+}
+
+// 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.
+ for i := 0; i < len(host); i++ {
+ if host[i] == ':' {
+ 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 string, hostport string) (ip *[]byte, iport int, err *os.Error) {
+ var host, port string;
+ host, port, err = SplitHostPort(hostport);
+ 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);
+ if !ok || p < 0 || p > 0xFFFF {
+ return nil, 0, UnknownPort
+ }
+
+ return addr, p, nil
+}
+
+// Convert socket address into "host:port".
+func SockaddrToHostPort(sa *socket.Sockaddr) (hostport string, err *os.Error) {
+ switch sa.family {
+ case socket.AF_INET, socket.AF_INET6:
+ addr, port, e := socket.SockaddrToIP(sa)
+ if e != nil {
+ return "", e
+ }
+ host := ip.IPToString(addr);
+ return JoinHostPort(host, strings.itoa(port)), nil
+ default:
+ return "", UnknownSocketFamily
+ }
+ return "", nil // not reached
+}
+
+// Boolean to int.
+func boolint(b bool) int {
+ if b {
+ return 1
+ }
+ return 0
+}
+
+// Generic Socket creation.
+func Socket(f, p, t int64, la, ra *socket.Sockaddr) (fd int64, err *os.Error) {
+ s, e := socket.socket(f, p, t);
+ if e != nil {
+ return -1, e
+ }
+
+ var r int64
+ if la != nil {
+ r, e = socket.bind(s, la)
+ if e != nil {
+ syscall.close(s)
+ return -1, e
+ }
+ }
+
+ if ra != nil {
+ r, e = socket.connect(s, ra)
+ if e != nil {
+ syscall.close(s)
+ return -1, e
+ }
+ }
+
+ return s, nil
+}
+
+
+// Generic implementation of Conn interface; not exported.
+
+type ConnBase struct {
+ fd *os.FD;
+ raddr string;
+}
+
+// Eventually, these will use epoll or some such.
+
+func (c *ConnBase) FD() int64 {
+ 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 (c *ConnBase) SetReadBuffer(bytes int) *os.Error {
+ return socket.setsockopt_int(c.FD(), socket.SOL_SOCKET, socket.SO_RCVBUF, bytes);
+}
+
+func (c *ConnBase) SetWriteBuffer(bytes int) *os.Error {
+ return socket.setsockopt_int(c.FD(), socket.SOL_SOCKET, socket.SO_SNDBUF, bytes);
+}
+
+func (c *ConnBase) SetReadTimeout(nsec int64) *os.Error {
+ return socket.setsockopt_tv(c.FD(), socket.SOL_SOCKET, socket.SO_RCVTIMEO, nsec);
+}
+
+func (c *ConnBase) SetWriteTimeout(nsec int64) *os.Error {
+ return socket.setsockopt_tv(c.FD(), socket.SOL_SOCKET, socket.SO_SNDTIMEO, nsec);
+}
+
+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 socket.setsockopt_int(c.FD(), socket.SOL_SOCKET, socket.SO_REUSEADDR, boolint(reuse));
+}
+
+func (c *ConnBase) BindToDevice(dev string) *os.Error {
+ // TODO: call setsockopt with null-terminated string pointer
+ return os.EINVAL
+}
+
+func (c *ConnBase) SetDontRoute(dontroute bool) *os.Error {
+ return socket.setsockopt_int(c.FD(), socket.SOL_SOCKET, socket.SO_DONTROUTE, boolint(dontroute));
+}
+
+func (c *ConnBase) SetKeepAlive(keepalive bool) *os.Error {
+ return socket.setsockopt_int(c.FD(), socket.SOL_SOCKET, socket.SO_KEEPALIVE, boolint(keepalive));
+}
+
+func (c *ConnBase) SetLinger(sec int) *os.Error {
+ return socket.setsockopt_linger(c.FD(), socket.SOL_SOCKET, socket.SO_LINGER, sec);
+}
+
+
+// Internet sockets (TCP, UDP)
+
+// 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.
+// If we need to build on a system without IPv6 support, setting
+// 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) {
+ // Parse addresses (unless they are empty).
+ var lip, rip *[]byte
+ var lport, rport int
+ var lerr, rerr *os.Error
+ if laddr != "" {
+ lip, lport, lerr = HostPortToIP(net, laddr)
+ if lerr != nil {
+ return -1, lerr
+ }
+ }
+ if raddr != "" {
+ rip, rport, rerr = HostPortToIP(net, raddr)
+ if rerr != nil {
+ return -1, rerr
+ }
+ }
+
+ // 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 || ip.ToIPv4(lip) != nil)
+ && (rip == nil || ip.ToIPv4(rip) != nil) {
+ vers = 4
+ } else {
+ vers = 6
+ }
+ }
+
+ var cvt *(addr *[]byte, port int) (sa *socket.Sockaddr, err *os.Error)
+ var family int64
+ if vers == 4 {
+ cvt = &socket.IPv4ToSockaddr;
+ family = socket.AF_INET
+ } else {
+ cvt = &socket.IPv6ToSockaddr;
+ family = socket.AF_INET6
+ }
+
+ var la, ra *socket.Sockaddr;
+ if lip != nil {
+ la, lerr = cvt(lip, lport);
+ if lerr != nil {
+ return -1, lerr
+ }
+ }
+ if rip != nil {
+ ra, rerr = cvt(rip, rport);
+ if rerr != nil {
+ return -1, rerr
+ }
+ }
+
+ fd, err = Socket(family, proto, 0, la, ra);
+ return fd, err
+}
+
+
+// TCP connections.
+
+export type ConnTCP struct {
+ base ConnBase
+}
+
+// New TCP methods
+func (c *ConnTCP) SetNoDelay(nodelay bool) *os.Error {
+ if c == nil {
+ return os.EINVAL
+ }
+ return socket.setsockopt_int(c.base.fd.fd, socket.IPPROTO_TCP, socket.TCP_NODELAY, boolint(nodelay))
+}
+
+// Wrappers
+func (c *ConnTCP) Read(b *[]byte) (n int, err *os.Error) {
+ n, err = (&c.base).Read(b)
+ return n, err
+}
+func (c *ConnTCP) Write(b *[]byte) (n int, err *os.Error) {
+ n, err = (&c.base).Write(b)
+ return n, err
+}
+func (c *ConnTCP) ReadFrom(b *[]byte) (n int, raddr string, err *os.Error) {
+ n, raddr, err = (&c.base).ReadFrom(b)
+ return n, raddr, err
+}
+func (c *ConnTCP) WriteTo(raddr string, b *[]byte) (n int, err *os.Error) {
+ n, err = (&c.base).WriteTo(raddr, b)
+ return n, err
+}
+func (c *ConnTCP) Close() *os.Error {
+ return (&c.base).Close()
+}
+func (c *ConnTCP) SetReadBuffer(bytes int) *os.Error {
+ return (&c.base).SetReadBuffer(bytes)
+}
+func (c *ConnTCP) SetWriteBuffer(bytes int) *os.Error {
+ return (&c.base).SetWriteBuffer(bytes)
+}
+func (c *ConnTCP) SetTimeout(nsec int64) *os.Error {
+ return (&c.base).SetTimeout(nsec)
+}
+func (c *ConnTCP) SetReadTimeout(nsec int64) *os.Error {
+ return (&c.base).SetReadTimeout(nsec)
+}
+func (c *ConnTCP) SetWriteTimeout(nsec int64) *os.Error {
+ return (&c.base).SetWriteTimeout(nsec)
+}
+func (c *ConnTCP) SetLinger(sec int) *os.Error {
+ return (&c.base).SetLinger(sec)
+}
+func (c *ConnTCP) SetReuseAddr(reuseaddr bool) *os.Error {
+ return (&c.base).SetReuseAddr(reuseaddr)
+}
+func (c *ConnTCP) BindToDevice(dev string) *os.Error {
+ return (&c.base).BindToDevice(dev)
+}
+func (c *ConnTCP) SetDontRoute(dontroute bool) *os.Error {
+ return (&c.base).SetDontRoute(dontroute)
+}
+func (c *ConnTCP) SetKeepAlive(keepalive bool) *os.Error {
+ return (&c.base).SetKeepAlive(keepalive)
+}
+
+export func DialTCP(net, laddr, raddr string) (c *ConnTCP, err *os.Error) {
+ fd, e := DialInternet(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
+}
+
+
+// TODO: UDP connections
+
+
+// TODO: raw IP connections
+
+
+// TODO: raw ethernet connections
+
+
+export type Conn interface {
+ Read(b *[]byte) (n int, err *os.Error);
+ Write(b *[]byte) (n int, err *os.Error);
+ ReadFrom(b *[]byte) (n int, addr string, err *os.Error);
+ WriteTo(addr string, b *[]byte) (n int, err *os.Error);
+ Close() *os.Error;
+ SetReadBuffer(bytes int) *os.Error;
+ SetWriteBuffer(bytes int) *os.Error;
+ SetTimeout(nsec int64) *os.Error;
+ SetReadTimeout(nsec int64) *os.Error;
+ SetWriteTimeout(nsec int64) *os.Error;
+ SetLinger(sec int) *os.Error;
+ SetReuseAddr(reuseaddr bool) *os.Error;
+ SetDontRoute(dontroute bool) *os.Error;
+ SetKeepAlive(keepalive bool) *os.Error;
+ BindToDevice(dev string) *os.Error;
+}
+
+type NoConn struct { unused int }
+func (c *NoConn) Read(b *[]byte) (n int, err *os.Error) { return -1, os.EINVAL }
+func (c *NoConn) Write(b *[]byte) (n int, err *os.Error) { return -1, os.EINVAL }
+func (c *NoConn) ReadFrom(b *[]byte) (n int, addr string, err *os.Error) { return -1, "", os.EINVAL }
+func (c *NoConn) WriteTo(addr string, b *[]byte) (n int, err *os.Error) { return -1, os.EINVAL }
+func (c *NoConn) Close() *os.Error { return nil }
+func (c *NoConn) SetReadBuffer(bytes int) *os.Error { return os.EINVAL }
+func (c *NoConn) SetWriteBuffer(bytes int) *os.Error { return os.EINVAL }
+func (c *NoConn) SetTimeout(nsec int64) *os.Error { return os.EINVAL }
+func (c *NoConn) SetReadTimeout(nsec int64) *os.Error { return os.EINVAL }
+func (c *NoConn) SetWriteTimeout(nsec int64) *os.Error { return os.EINVAL }
+func (c *NoConn) SetLinger(sec int) *os.Error { return os.EINVAL }
+func (c *NoConn) SetReuseAddr(reuseaddr bool) *os.Error { return os.EINVAL }
+func (c *NoConn) SetDontRoute(dontroute bool) *os.Error { return os.EINVAL }
+func (c *NoConn) SetKeepAlive(keepalive bool) *os.Error { return os.EINVAL }
+func (c *NoConn) BindToDevice(dev string) *os.Error { return os.EINVAL }
+
+var noconn NoConn
+
+// Dial's arguments are the network, local address, and remote address.
+// Examples:
+// Dial("tcp", "", "12.34.56.78:80")
+// Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80")
+// Dial("tcp", "127.0.0.1:123", "127.0.0.1:88")
+//
+// Eventually, we plan to allow names in addition to IP addresses,
+// but that requires writing a DNS library.
+
+export func Dial(net, laddr, raddr string) (c Conn, err *os.Error) {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ c, err := DialTCP(net, laddr, raddr)
+ if err != nil {
+ return &noconn, err
+ }
+ return c, nil
+/*
+ case "udp", "udp4", "upd6":
+ c, err := DialUDP(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, UnknownNetwork
+}
+