summaryrefslogtreecommitdiff
path: root/src/syscall/net_nacl.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/syscall/net_nacl.go')
-rw-r--r--src/syscall/net_nacl.go913
1 files changed, 913 insertions, 0 deletions
diff --git a/src/syscall/net_nacl.go b/src/syscall/net_nacl.go
new file mode 100644
index 000000000..b5cb53030
--- /dev/null
+++ b/src/syscall/net_nacl.go
@@ -0,0 +1,913 @@
+// 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 int
+ when int64
+ period int64
+ f func(interface{}, uintptr) // NOTE: must not be closure
+ arg interface{}
+ seq uintptr
+}
+
+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(i interface{}, seq uintptr) {
+ 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 }