diff options
Diffstat (limited to 'src/pkg/syscall/net_nacl.go')
-rw-r--r-- | src/pkg/syscall/net_nacl.go | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/src/pkg/syscall/net_nacl.go b/src/pkg/syscall/net_nacl.go new file mode 100644 index 000000000..b9488f48d --- /dev/null +++ b/src/pkg/syscall/net_nacl.go @@ -0,0 +1,912 @@ +// Copyright 2013 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. + +// A simulated network for use within NaCl. +// The simulation is not particularly tied to NaCl, +// but other systems have real networks. + +package syscall + +import ( + "sync" + "sync/atomic" +) + +// Interface to timers implemented in package runtime. +// Must be in sync with ../runtime/runtime.h:/^struct.Timer$ +// Really for use by package time, but we cannot import time here. + +type runtimeTimer struct { + i int32 + when int64 + period int64 + f func(int64, interface{}) // NOTE: must not be closure + arg interface{} +} + +func startTimer(*runtimeTimer) +func stopTimer(*runtimeTimer) bool + +type timer struct { + expired bool + q *queue + r runtimeTimer +} + +func (t *timer) start(q *queue, deadline int64) { + if deadline == 0 { + return + } + t.q = q + t.r.when = deadline + t.r.f = timerExpired + t.r.arg = t + startTimer(&t.r) +} + +func (t *timer) stop() { + stopTimer(&t.r) +} + +func timerExpired(now int64, i interface{}) { + t := i.(*timer) + go func() { + t.q.Lock() + defer t.q.Unlock() + t.expired = true + t.q.canRead.Broadcast() + t.q.canWrite.Broadcast() + }() +} + +// Network constants and data structures. These match the traditional values. + +const ( + AF_UNSPEC = iota + AF_UNIX + AF_INET + AF_INET6 +) + +const ( + SHUT_RD = iota + SHUT_WR + SHUT_RDWR +) + +const ( + SOCK_STREAM = 1 + iota + SOCK_DGRAM + SOCK_RAW + SOCK_SEQPACKET +) + +const ( + IPPROTO_IP = 0 + IPPROTO_IPV4 = 4 + IPPROTO_IPV6 = 0x29 + IPPROTO_TCP = 6 + IPPROTO_UDP = 0x11 +) + +// Misc constants expected by package net but not supported. +const ( + _ = iota + SOL_SOCKET + SO_TYPE + NET_RT_IFLIST + IFNAMSIZ + IFF_UP + IFF_BROADCAST + IFF_LOOPBACK + IFF_POINTOPOINT + IFF_MULTICAST + IPV6_V6ONLY + SOMAXCONN + F_DUPFD_CLOEXEC + SO_BROADCAST + SO_REUSEADDR + SO_REUSEPORT + SO_RCVBUF + SO_SNDBUF + SO_KEEPALIVE + SO_LINGER + SO_ERROR + IP_PORTRANGE + IP_PORTRANGE_DEFAULT + IP_PORTRANGE_LOW + IP_PORTRANGE_HIGH + IP_MULTICAST_IF + IP_MULTICAST_LOOP + IP_ADD_MEMBERSHIP + IPV6_PORTRANGE + IPV6_PORTRANGE_DEFAULT + IPV6_PORTRANGE_LOW + IPV6_PORTRANGE_HIGH + IPV6_MULTICAST_IF + IPV6_MULTICAST_LOOP + IPV6_JOIN_GROUP + TCP_NODELAY + TCP_KEEPINTVL + TCP_KEEPIDLE + + SYS_FCNTL = 500 // unsupported +) + +var SocketDisableIPv6 bool + +// A Sockaddr is one of the SockaddrXxx structs. +type Sockaddr interface { + // copy returns a copy of the underlying data. + copy() Sockaddr + + // key returns the value of the underlying data, + // for comparison as a map key. + key() interface{} +} + +type SockaddrInet4 struct { + Port int + Addr [4]byte +} + +func (sa *SockaddrInet4) copy() Sockaddr { + sa1 := *sa + return &sa1 +} + +func (sa *SockaddrInet4) key() interface{} { return *sa } + +type SockaddrInet6 struct { + Port int + ZoneId uint32 + Addr [16]byte +} + +func (sa *SockaddrInet6) copy() Sockaddr { + sa1 := *sa + return &sa1 +} + +func (sa *SockaddrInet6) key() interface{} { return *sa } + +type SockaddrUnix struct { + Name string +} + +func (sa *SockaddrUnix) copy() Sockaddr { + sa1 := *sa + return &sa1 +} + +func (sa *SockaddrUnix) key() interface{} { return *sa } + +type SockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [12]int8 +} + +func (sa *SockaddrDatalink) copy() Sockaddr { + sa1 := *sa + return &sa1 +} + +func (sa *SockaddrDatalink) key() interface{} { return *sa } + +// RoutingMessage represents a routing message. +type RoutingMessage interface { + unimplemented() +} + +type IPMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type IPv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type Linger struct { + Onoff int32 + Linger int32 +} + +type ICMPv6Filter struct { + Filt [8]uint32 +} + +// A queue is the bookkeeping for a synchronized buffered queue. +// We do not use channels because we need to be able to handle +// writes after and during close, and because a chan byte would +// require too many send and receive operations in real use. +type queue struct { + sync.Mutex + canRead sync.Cond + canWrite sync.Cond + r int // total read index + w int // total write index + m int // index mask + closed bool +} + +func (q *queue) init(size int) { + if size&(size-1) != 0 { + panic("invalid queue size - must be power of two") + } + q.canRead.L = &q.Mutex + q.canWrite.L = &q.Mutex + q.m = size - 1 +} + +func past(deadline int64) bool { + sec, nsec := now() + return deadline > 0 && deadline < sec*1e9+int64(nsec) +} + +func (q *queue) waitRead(n int, deadline int64) (int, error) { + if past(deadline) { + return 0, EAGAIN + } + var t timer + t.start(q, deadline) + for q.w-q.r == 0 && !q.closed && !t.expired { + q.canRead.Wait() + } + t.stop() + m := q.w - q.r + if m == 0 && t.expired { + return 0, EAGAIN + } + if m > n { + m = n + q.canRead.Signal() // wake up next reader too + } + q.canWrite.Signal() + return m, nil +} + +func (q *queue) waitWrite(n int, deadline int64) (int, error) { + if past(deadline) { + return 0, EAGAIN + } + var t timer + t.start(q, deadline) + for q.w-q.r > q.m && !q.closed && !t.expired { + q.canWrite.Wait() + } + t.stop() + m := q.m + 1 - (q.w - q.r) + if m == 0 && t.expired { + return 0, EAGAIN + } + if m == 0 { + return 0, EAGAIN + } + if m > n { + m = n + q.canWrite.Signal() // wake up next writer too + } + q.canRead.Signal() + return m, nil +} + +func (q *queue) close() { + q.Lock() + defer q.Unlock() + q.closed = true + q.canRead.Broadcast() + q.canWrite.Broadcast() +} + +// A byteq is a byte queue. +type byteq struct { + queue + data []byte +} + +func newByteq() *byteq { + q := &byteq{ + data: make([]byte, 4096), + } + q.init(len(q.data)) + return q +} + +func (q *byteq) read(b []byte, deadline int64) (int, error) { + q.Lock() + defer q.Unlock() + n, err := q.waitRead(len(b), deadline) + if err != nil { + return 0, err + } + b = b[:n] + for len(b) > 0 { + m := copy(b, q.data[q.r&q.m:]) + q.r += m + b = b[m:] + } + return n, nil +} + +func (q *byteq) write(b []byte, deadline int64) (n int, err error) { + q.Lock() + defer q.Unlock() + for n < len(b) { + nn, err := q.waitWrite(len(b[n:]), deadline) + if err != nil { + return n, err + } + bb := b[n : n+nn] + n += nn + for len(bb) > 0 { + m := copy(q.data[q.w&q.m:], bb) + q.w += m + bb = bb[m:] + } + } + return n, nil +} + +// A msgq is a queue of messages. +type msgq struct { + queue + data []interface{} +} + +func newMsgq() *msgq { + q := &msgq{ + data: make([]interface{}, 32), + } + q.init(len(q.data)) + return q +} + +func (q *msgq) read(deadline int64) (interface{}, error) { + q.Lock() + defer q.Unlock() + n, err := q.waitRead(1, deadline) + if err != nil { + return nil, err + } + if n == 0 { + return nil, nil + } + m := q.data[q.r&q.m] + q.r++ + return m, nil +} + +func (q *msgq) write(m interface{}, deadline int64) error { + q.Lock() + defer q.Unlock() + _, err := q.waitWrite(1, deadline) + if err != nil { + return err + } + q.data[q.w&q.m] = m + q.w++ + return nil +} + +// An addr is a sequence of bytes uniquely identifying a network address. +// It is not human-readable. +type addr string + +// A conn is one side of a stream-based network connection. +// That is, a stream-based network connection is a pair of cross-connected conns. +type conn struct { + rd *byteq + wr *byteq + local addr + remote addr +} + +// A pktconn is one side of a packet-based network connection. +// That is, a packet-based network connection is a pair of cross-connected pktconns. +type pktconn struct { + rd *msgq + wr *msgq + local addr + remote addr +} + +// A listener accepts incoming stream-based network connections. +type listener struct { + rd *msgq + local addr +} + +// A netFile is an open network file. +type netFile struct { + defaultFileImpl + proto *netproto + sotype int + listener *msgq + packet *msgq + rd *byteq + wr *byteq + rddeadline int64 + wrdeadline int64 + addr Sockaddr + raddr Sockaddr +} + +// A netAddr is a network address in the global listener map. +// All the fields must have defined == operations. +type netAddr struct { + proto *netproto + sotype int + addr interface{} +} + +// net records the state of the network. +// It maps a network address to the listener on that address. +var net = struct { + sync.Mutex + listener map[netAddr]*netFile +}{ + listener: make(map[netAddr]*netFile), +} + +// TODO(rsc): Some day, do a better job with port allocation. +// For playground programs, incrementing is fine. +var nextport = 2 + +// A netproto contains protocol-specific functionality +// (one for AF_INET, one for AF_INET6 and so on). +// It is a struct instead of an interface because the +// implementation needs no state, and I expect to +// add some data fields at some point. +type netproto struct { + bind func(*netFile, Sockaddr) error +} + +var netprotoAF_INET = &netproto{ + bind: func(f *netFile, sa Sockaddr) error { + if sa == nil { + f.addr = &SockaddrInet4{ + Port: nextport, + Addr: [4]byte{127, 0, 0, 1}, + } + nextport++ + return nil + } + addr, ok := sa.(*SockaddrInet4) + if !ok { + return EINVAL + } + addr = addr.copy().(*SockaddrInet4) + if addr.Port == 0 { + addr.Port = nextport + nextport++ + } + f.addr = addr + return nil + }, +} + +var netprotos = map[int]*netproto{ + AF_INET: netprotoAF_INET, +} + +// These functions implement the usual BSD socket operations. + +func (f *netFile) bind(sa Sockaddr) error { + if f.addr != nil { + return EISCONN + } + if err := f.proto.bind(f, sa); err != nil { + return err + } + if f.sotype == SOCK_DGRAM { + _, ok := net.listener[netAddr{f.proto, f.sotype, f.addr.key()}] + if ok { + f.addr = nil + return EADDRINUSE + } + net.listener[netAddr{f.proto, f.sotype, f.addr.key()}] = f + f.packet = newMsgq() + } + return nil +} + +func (f *netFile) listen(backlog int) error { + net.Lock() + defer net.Unlock() + if f.listener != nil { + return EINVAL + } + _, ok := net.listener[netAddr{f.proto, f.sotype, f.addr.key()}] + if ok { + return EADDRINUSE + } + net.listener[netAddr{f.proto, f.sotype, f.addr.key()}] = f + f.listener = newMsgq() + return nil +} + +func (f *netFile) accept() (fd int, sa Sockaddr, err error) { + msg, err := f.listener.read(f.readDeadline()) + if err != nil { + return -1, nil, err + } + newf, ok := msg.(*netFile) + if !ok { + // must be eof + return -1, nil, EAGAIN + } + return newFD(newf), newf.raddr.copy(), nil +} + +func (f *netFile) connect(sa Sockaddr) error { + if past(f.writeDeadline()) { + return EAGAIN + } + if f.addr == nil { + if err := f.bind(nil); err != nil { + return err + } + } + net.Lock() + if sa == nil { + net.Unlock() + return EINVAL + } + sa = sa.copy() + if f.raddr != nil { + net.Unlock() + return EISCONN + } + if f.sotype == SOCK_DGRAM { + net.Unlock() + f.raddr = sa + return nil + } + if f.listener != nil { + net.Unlock() + return EISCONN + } + l, ok := net.listener[netAddr{f.proto, f.sotype, sa.key()}] + if !ok { + net.Unlock() + return ECONNREFUSED + } + f.raddr = sa + f.rd = newByteq() + f.wr = newByteq() + newf := &netFile{ + proto: f.proto, + sotype: f.sotype, + addr: f.raddr, + raddr: f.addr, + rd: f.wr, + wr: f.rd, + } + net.Unlock() + l.listener.write(newf, f.writeDeadline()) + return nil +} + +func (f *netFile) read(b []byte) (int, error) { + if f.rd == nil { + if f.raddr != nil { + n, _, err := f.recvfrom(b, 0) + return n, err + } + return 0, ENOTCONN + } + return f.rd.read(b, f.readDeadline()) +} + +func (f *netFile) write(b []byte) (int, error) { + if f.wr == nil { + if f.raddr != nil { + err := f.sendto(b, 0, f.raddr) + var n int + if err == nil { + n = len(b) + } + return n, err + } + return 0, ENOTCONN + } + return f.wr.write(b, f.writeDeadline()) +} + +type pktmsg struct { + buf []byte + addr Sockaddr +} + +func (f *netFile) recvfrom(p []byte, flags int) (n int, from Sockaddr, err error) { + if f.sotype != SOCK_DGRAM { + return 0, nil, EINVAL + } + if f.packet == nil { + return 0, nil, ENOTCONN + } + msg1, err := f.packet.read(f.readDeadline()) + if err != nil { + return 0, nil, err + } + msg, ok := msg1.(*pktmsg) + if !ok { + return 0, nil, EAGAIN + } + return copy(p, msg.buf), msg.addr, nil +} + +func (f *netFile) sendto(p []byte, flags int, to Sockaddr) error { + if f.sotype != SOCK_DGRAM { + return EINVAL + } + if f.packet == nil { + if err := f.bind(nil); err != nil { + return err + } + } + net.Lock() + if to == nil { + net.Unlock() + return EINVAL + } + to = to.copy() + l, ok := net.listener[netAddr{f.proto, f.sotype, to.key()}] + if !ok || l.packet == nil { + net.Unlock() + return ECONNREFUSED + } + net.Unlock() + msg := &pktmsg{ + buf: make([]byte, len(p)), + addr: f.addr, + } + copy(msg.buf, p) + l.packet.write(msg, f.writeDeadline()) + return nil +} + +func (f *netFile) close() error { + if f.listener != nil { + f.listener.close() + } + if f.packet != nil { + f.packet.close() + } + if f.rd != nil { + f.rd.close() + } + if f.wr != nil { + f.wr.close() + } + return nil +} + +func fdToNetFile(fd int) (*netFile, error) { + f, err := fdToFile(fd) + if err != nil { + return nil, err + } + impl := f.impl + netf, ok := impl.(*netFile) + if !ok { + return nil, EINVAL + } + return netf, nil +} + +func Socket(proto, sotype, unused int) (fd int, err error) { + p := netprotos[proto] + if p == nil { + return -1, EPROTONOSUPPORT + } + if sotype != SOCK_STREAM && sotype != SOCK_DGRAM { + return -1, ESOCKTNOSUPPORT + } + f := &netFile{ + proto: p, + sotype: sotype, + } + return newFD(f), nil +} + +func Bind(fd int, sa Sockaddr) error { + f, err := fdToNetFile(fd) + if err != nil { + return err + } + return f.bind(sa) +} + +func StopIO(fd int) error { + f, err := fdToNetFile(fd) + if err != nil { + return err + } + f.close() + return nil +} + +func Listen(fd int, backlog int) error { + f, err := fdToNetFile(fd) + if err != nil { + return err + } + return f.listen(backlog) +} + +func Accept(fd int) (newfd int, sa Sockaddr, err error) { + f, err := fdToNetFile(fd) + if err != nil { + return 0, nil, err + } + return f.accept() +} + +func Getsockname(fd int) (sa Sockaddr, err error) { + f, err := fdToNetFile(fd) + if err != nil { + return nil, err + } + if f.addr == nil { + return nil, ENOTCONN + } + return f.addr.copy(), nil +} + +func Getpeername(fd int) (sa Sockaddr, err error) { + f, err := fdToNetFile(fd) + if err != nil { + return nil, err + } + if f.raddr == nil { + return nil, ENOTCONN + } + return f.raddr.copy(), nil +} + +func Connect(fd int, sa Sockaddr) error { + f, err := fdToNetFile(fd) + if err != nil { + return err + } + return f.connect(sa) +} + +func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { + f, err := fdToNetFile(fd) + if err != nil { + return 0, nil, err + } + return f.recvfrom(p, flags) +} + +func Sendto(fd int, p []byte, flags int, to Sockaddr) error { + f, err := fdToNetFile(fd) + if err != nil { + return err + } + return f.sendto(p, flags, to) +} + +func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn, recvflags int, from Sockaddr, err error) { + f, err := fdToNetFile(fd) + if err != nil { + return + } + n, from, err = f.recvfrom(p, flags) + return +} + +func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) error { + _, err := SendmsgN(fd, p, oob, to, flags) + return err +} + +func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { + f, err := fdToNetFile(fd) + if err != nil { + return 0, err + } + switch f.sotype { + case SOCK_STREAM: + n, err = f.write(p) + case SOCK_DGRAM: + n = len(p) + err = f.sendto(p, flags, to) + } + if err != nil { + return 0, err + } + return n, nil +} + +func GetsockoptInt(fd, level, opt int) (value int, err error) { + f, err := fdToNetFile(fd) + if err != nil { + return 0, err + } + switch { + case level == SOL_SOCKET && opt == SO_TYPE: + return f.sotype, nil + } + return 0, ENOTSUP +} + +func SetsockoptInt(fd, level, opt int, value int) error { + return nil +} + +func SetsockoptByte(fd, level, opt int, value byte) error { + _, err := fdToNetFile(fd) + if err != nil { + return err + } + return ENOTSUP +} + +func SetsockoptLinger(fd, level, opt int, l *Linger) error { + return nil +} + +func SetReadDeadline(fd int, t int64) error { + f, err := fdToNetFile(fd) + if err != nil { + return err + } + atomic.StoreInt64(&f.rddeadline, t) + return nil +} + +func (f *netFile) readDeadline() int64 { + return atomic.LoadInt64(&f.rddeadline) +} + +func SetWriteDeadline(fd int, t int64) error { + f, err := fdToNetFile(fd) + if err != nil { + return err + } + atomic.StoreInt64(&f.wrdeadline, t) + return nil +} + +func (f *netFile) writeDeadline() int64 { + return atomic.LoadInt64(&f.wrdeadline) +} + +func Shutdown(fd int, how int) error { + f, err := fdToNetFile(fd) + if err != nil { + return err + } + switch how { + case SHUT_RD: + f.rd.close() + case SHUT_WR: + f.wr.close() + case SHUT_RDWR: + f.rd.close() + f.wr.close() + } + return nil +} + +func SetsockoptICMPv6Filter(fd, level, opt int, filter *ICMPv6Filter) error { panic("SetsockoptICMPv") } +func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) error { panic("SetsockoptIPMreq") } +func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) error { panic("SetsockoptIPv") } +func SetsockoptInet4Addr(fd, level, opt int, value [4]byte) error { panic("SetsockoptInet") } +func SetsockoptString(fd, level, opt int, s string) error { panic("SetsockoptString") } +func SetsockoptTimeval(fd, level, opt int, tv *Timeval) error { panic("SetsockoptTimeval") } +func Socketpair(domain, typ, proto int) (fd [2]int, err error) { panic("Socketpair") } + +func SetNonblock(fd int, nonblocking bool) error { return nil } |