| // 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 | |
| } | |
| old, ok := net.listener[netAddr{f.proto, f.sotype, f.addr.key()}] | |
| if ok && !old.listenerClosed() { | |
| 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 || l.listenerClosed() { | |
| 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) listenerClosed() bool { | |
| f.listener.Lock() | |
| defer f.listener.Unlock() | |
| return f.listener.closed | |
| } | |
| 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 } |