| // 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. |
| |
| // Native Client SRPC message passing. |
| // This code is needed to invoke SecureRandom, the NaCl equivalent of /dev/random. |
| |
| package syscall |
| |
| import ( |
| "errors" |
| "sync" |
| "unsafe" |
| ) |
| |
| // An srpcClient represents the client side of an SRPC connection. |
| type srpcClient struct { |
| fd int // to server |
| r msgReceiver |
| s msgSender |
| service map[string]srpcService // services by name |
| |
| outMu sync.Mutex // protects writing to connection |
| |
| mu sync.Mutex // protects following fields |
| muxer bool // is someone reading and muxing responses |
| pending map[uint32]*srpc |
| idGen uint32 // generator for request IDs |
| } |
| |
| // An srpcService is a single method that the server offers. |
| type srpcService struct { |
| num uint32 // method number |
| fmt string // argument format; see "parsing of RPC messages" below |
| } |
| |
| // An srpc represents a single srpc issued by a client. |
| type srpc struct { |
| Ret []interface{} |
| Done chan *srpc |
| Err error |
| c *srpcClient |
| id uint32 |
| } |
| |
| // newClient allocates a new SRPC client using the file descriptor fd. |
| func newClient(fd int) (*srpcClient, error) { |
| c := new(srpcClient) |
| c.fd = fd |
| c.r.fd = fd |
| c.s.fd = fd |
| c.service = make(map[string]srpcService) |
| c.pending = make(map[uint32]*srpc) |
| |
| // service discovery request |
| m := &msg{ |
| isRequest: 1, |
| template: []interface{}{[]byte(nil)}, |
| size: []int{4000}, // max size to accept for returned byte slice |
| } |
| if err := m.pack(); err != nil { |
| return nil, errors.New("Native Client SRPC service_discovery: preparing request: " + err.Error()) |
| } |
| c.s.send(m) |
| m, err := c.r.recv() |
| if err != nil { |
| return nil, err |
| } |
| m.unpack() |
| if m.status != uint32(srpcOK) { |
| return nil, errors.New("Native Client SRPC service_discovery: " + srpcErrno(m.status).Error()) |
| } |
| list := m.value[0].([]byte) |
| var n uint32 |
| for len(list) > 0 { |
| var line []byte |
| i := byteIndex(list, '\n') |
| if i < 0 { |
| line, list = list, nil |
| } else { |
| line, list = list[:i], list[i+1:] |
| } |
| i = byteIndex(line, ':') |
| if i >= 0 { |
| c.service[string(line)] = srpcService{n, string(line[i+1:])} |
| } |
| n++ |
| } |
| |
| return c, nil |
| } |
| |
| func byteIndex(b []byte, c byte) int { |
| for i, bi := range b { |
| if bi == c { |
| return i |
| } |
| } |
| return -1 |
| } |
| |
| var yourTurn srpc |
| |
| func (c *srpcClient) wait(r *srpc) { |
| var rx *srpc |
| for rx = range r.Done { |
| if rx != &yourTurn { |
| break |
| } |
| c.input() |
| } |
| return |
| } |
| |
| func (c *srpcClient) input() { |
| // read message |
| m, err := c.r.recv() |
| if err != nil { |
| println("Native Client SRPC receive error:", err.Error()) |
| return |
| } |
| if m.unpack(); m.status != uint32(srpcOK) { |
| println("Native Client SRPC receive error: invalid message: ", srpcErrno(m.status).Error()) |
| return |
| } |
| |
| // deliver to intended recipient |
| c.mu.Lock() |
| rpc, ok := c.pending[m.id] |
| if ok { |
| delete(c.pending, m.id) |
| } |
| |
| // wake a new muxer if there are more RPCs to read |
| c.muxer = false |
| for _, rpc := range c.pending { |
| c.muxer = true |
| rpc.Done <- &yourTurn |
| break |
| } |
| c.mu.Unlock() |
| if !ok { |
| println("Native Client: unexpected response for ID", m.id) |
| return |
| } |
| rpc.Ret = m.value |
| rpc.Done <- rpc |
| } |
| |
| // Wait blocks until the RPC has finished. |
| func (r *srpc) Wait() { |
| r.c.wait(r) |
| } |
| |
| // Start issues an RPC request for method name with the given arguments. |
| // The RPC r must not be in use for another pending request. |
| // To wait for the RPC to finish, receive from r.Done and then |
| // inspect r.Ret and r.Errno. |
| func (r *srpc) Start(name string, arg []interface{}) { |
| r.Err = nil |
| r.c.mu.Lock() |
| srv, ok := r.c.service[name] |
| if !ok { |
| r.c.mu.Unlock() |
| r.Err = srpcErrBadRPCNumber |
| r.Done <- r |
| return |
| } |
| r.c.pending[r.id] = r |
| if !r.c.muxer { |
| r.c.muxer = true |
| r.Done <- &yourTurn |
| } |
| r.c.mu.Unlock() |
| |
| var m msg |
| m.id = r.id |
| m.isRequest = 1 |
| m.rpc = srv.num |
| m.value = arg |
| |
| // Fill in the return values and sizes to generate |
| // the right type chars. We'll take most any size. |
| |
| // Skip over input arguments. |
| // We could check them against arg, but the server |
| // will do that anyway. |
| i := 0 |
| for srv.fmt[i] != ':' { |
| i++ |
| } |
| format := srv.fmt[i+1:] |
| |
| // Now the return prototypes. |
| m.template = make([]interface{}, len(format)) |
| m.size = make([]int, len(format)) |
| for i := 0; i < len(format); i++ { |
| switch format[i] { |
| default: |
| println("Native Client SRPC: unexpected service type " + string(format[i])) |
| r.Err = srpcErrBadRPCNumber |
| r.Done <- r |
| return |
| case 'b': |
| m.template[i] = false |
| case 'C': |
| m.template[i] = []byte(nil) |
| m.size[i] = 1 << 30 |
| case 'd': |
| m.template[i] = float64(0) |
| case 'D': |
| m.template[i] = []float64(nil) |
| m.size[i] = 1 << 30 |
| case 'h': |
| m.template[i] = int(-1) |
| case 'i': |
| m.template[i] = int32(0) |
| case 'I': |
| m.template[i] = []int32(nil) |
| m.size[i] = 1 << 30 |
| case 's': |
| m.template[i] = "" |
| m.size[i] = 1 << 30 |
| } |
| } |
| |
| if err := m.pack(); err != nil { |
| r.Err = errors.New("Native Client RPC Start " + name + ": preparing request: " + err.Error()) |
| r.Done <- r |
| return |
| } |
| |
| r.c.outMu.Lock() |
| r.c.s.send(&m) |
| r.c.outMu.Unlock() |
| } |
| |
| // Call is a convenience wrapper that starts the RPC request, |
| // waits for it to finish, and then returns the results. |
| // Its implementation is: |
| // |
| // r.Start(name, arg) |
| // r.Wait() |
| // return r.Ret, r.Errno |
| // |
| func (c *srpcClient) Call(name string, arg ...interface{}) (ret []interface{}, err error) { |
| r := c.NewRPC(nil) |
| r.Start(name, arg) |
| r.Wait() |
| return r.Ret, r.Err |
| } |
| |
| // NewRPC creates a new RPC on the client connection. |
| func (c *srpcClient) NewRPC(done chan *srpc) *srpc { |
| if done == nil { |
| done = make(chan *srpc, 1) |
| } |
| c.mu.Lock() |
| id := c.idGen |
| c.idGen++ |
| c.mu.Unlock() |
| return &srpc{Done: done, c: c, id: id} |
| } |
| |
| // The current protocol number. |
| // Kind of useless, since there have been backwards-incompatible changes |
| // to the wire protocol that did not update the protocol number. |
| // At this point it's really just a sanity check. |
| const protocol = 0xc0da0002 |
| |
| // An srpcErrno is an SRPC status code. |
| type srpcErrno uint32 |
| |
| const ( |
| srpcOK srpcErrno = 256 + iota |
| srpcErrBreak |
| srpcErrMessageTruncated |
| srpcErrNoMemory |
| srpcErrProtocolMismatch |
| srpcErrBadRPCNumber |
| srpcErrBadArgType |
| srpcErrTooFewArgs |
| srpcErrTooManyArgs |
| srpcErrInArgTypeMismatch |
| srpcErrOutArgTypeMismatch |
| srpcErrInternalError |
| srpcErrAppError |
| ) |
| |
| var srpcErrstr = [...]string{ |
| srpcOK - srpcOK: "ok", |
| srpcErrBreak - srpcOK: "break", |
| srpcErrMessageTruncated - srpcOK: "message truncated", |
| srpcErrNoMemory - srpcOK: "out of memory", |
| srpcErrProtocolMismatch - srpcOK: "protocol mismatch", |
| srpcErrBadRPCNumber - srpcOK: "invalid RPC method number", |
| srpcErrBadArgType - srpcOK: "unexpected argument type", |
| srpcErrTooFewArgs - srpcOK: "too few arguments", |
| srpcErrTooManyArgs - srpcOK: "too many arguments", |
| srpcErrInArgTypeMismatch - srpcOK: "input argument type mismatch", |
| srpcErrOutArgTypeMismatch - srpcOK: "output argument type mismatch", |
| srpcErrInternalError - srpcOK: "internal error", |
| srpcErrAppError - srpcOK: "application error", |
| } |
| |
| func (e srpcErrno) Error() string { |
| if e < srpcOK || int(e-srpcOK) >= len(srpcErrstr) { |
| return "srpcErrno(" + itoa(int(e)) + ")" |
| } |
| return srpcErrstr[e-srpcOK] |
| } |
| |
| // A msgHdr is the data argument to the imc_recvmsg |
| // and imc_sendmsg system calls. |
| type msgHdr struct { |
| iov *iov |
| niov int32 |
| desc *int32 |
| ndesc int32 |
| flags uint32 |
| } |
| |
| // A single region for I/O. |
| type iov struct { |
| base *byte |
| len int32 |
| } |
| |
| const maxMsgSize = 1<<16 - 4*4 |
| |
| // A msgReceiver receives messages from a file descriptor. |
| type msgReceiver struct { |
| fd int |
| data [maxMsgSize]byte |
| desc [8]int32 |
| hdr msgHdr |
| iov iov |
| } |
| |
| func (r *msgReceiver) recv() (*msg, error) { |
| // Init pointers to buffers where syscall recvmsg can write. |
| r.iov.base = &r.data[0] |
| r.iov.len = int32(len(r.data)) |
| r.hdr.iov = &r.iov |
| r.hdr.niov = 1 |
| r.hdr.desc = &r.desc[0] |
| r.hdr.ndesc = int32(len(r.desc)) |
| n, _, e := Syscall(sys_imc_recvmsg, uintptr(r.fd), uintptr(unsafe.Pointer(&r.hdr)), 0) |
| if e != 0 { |
| println("Native Client imc_recvmsg: ", e.Error()) |
| return nil, e |
| } |
| |
| // Make a copy of the data so that the next recvmsg doesn't |
| // smash it. The system call did not update r.iov.len. Instead it |
| // returned the total byte count as n. |
| m := new(msg) |
| m.data = make([]byte, n) |
| copy(m.data, r.data[0:]) |
| |
| // Make a copy of the desc too. |
| // The system call *did* update r.hdr.ndesc. |
| if r.hdr.ndesc > 0 { |
| m.desc = make([]int32, r.hdr.ndesc) |
| copy(m.desc, r.desc[:]) |
| } |
| |
| return m, nil |
| } |
| |
| // A msgSender sends messages on a file descriptor. |
| type msgSender struct { |
| fd int |
| hdr msgHdr |
| iov iov |
| } |
| |
| func (s *msgSender) send(m *msg) error { |
| if len(m.data) > 0 { |
| s.iov.base = &m.data[0] |
| } |
| s.iov.len = int32(len(m.data)) |
| s.hdr.iov = &s.iov |
| s.hdr.niov = 1 |
| s.hdr.desc = nil |
| s.hdr.ndesc = 0 |
| _, _, e := Syscall(sys_imc_sendmsg, uintptr(s.fd), uintptr(unsafe.Pointer(&s.hdr)), 0) |
| if e != 0 { |
| println("Native Client imc_sendmsg: ", e.Error()) |
| return e |
| } |
| return nil |
| } |
| |
| // A msg is the Go representation of an SRPC message. |
| type msg struct { |
| data []byte // message data |
| desc []int32 // message file descriptors |
| |
| // parsed version of message |
| id uint32 |
| isRequest uint32 |
| rpc uint32 |
| status uint32 |
| value []interface{} |
| template []interface{} |
| size []int |
| format string |
| broken bool |
| } |
| |
| // reading from a msg |
| |
| func (m *msg) uint32() uint32 { |
| if m.broken { |
| return 0 |
| } |
| if len(m.data) < 4 { |
| m.broken = true |
| return 0 |
| } |
| b := m.data[:4] |
| x := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 |
| m.data = m.data[4:] |
| return x |
| } |
| |
| func (m *msg) uint64() uint64 { |
| x := uint64(m.uint32()) | uint64(m.uint32())<<32 |
| if m.broken { |
| return 0 |
| } |
| return x |
| } |
| |
| func (m *msg) bytes(n int) []byte { |
| if m.broken { |
| return nil |
| } |
| if len(m.data) < n { |
| m.broken = true |
| return nil |
| } |
| x := m.data[0:n] |
| m.data = m.data[n:] |
| return x |
| } |
| |
| // writing to a msg |
| |
| func (m *msg) wuint32(x uint32) { |
| m.data = append(m.data, byte(x), byte(x>>8), byte(x>>16), byte(x>>24)) |
| } |
| |
| func (m *msg) wuint64(x uint64) { |
| lo := uint32(x) |
| hi := uint32(x >> 32) |
| m.data = append(m.data, byte(lo), byte(lo>>8), byte(lo>>16), byte(lo>>24), byte(hi), byte(hi>>8), byte(hi>>16), byte(hi>>24)) |
| } |
| |
| func (m *msg) wbytes(p []byte) { |
| m.data = append(m.data, p...) |
| } |
| |
| func (m *msg) wstring(s string) { |
| m.data = append(m.data, s...) |
| } |
| |
| // Parsing of RPC messages. |
| // |
| // Each message begins with |
| // total_size uint32 |
| // total_descs uint32 |
| // fragment_size uint32 |
| // fragment_descs uint32 |
| // |
| // If fragment_size < total_size or fragment_descs < total_descs, the actual |
| // message is broken up in multiple messages; follow-up messages omit |
| // the "total" fields and begin with the "fragment" fields. |
| // We do not support putting fragmented messages back together. |
| // To do this we would need to change the message receiver. |
| // |
| // After that size information, the message header follows: |
| // protocol uint32 |
| // requestID uint32 |
| // isRequest uint32 |
| // rpcNumber uint32 |
| // status uint32 |
| // numValue uint32 |
| // numTemplate uint32 |
| // |
| // After the header come numTemplate fixed-size arguments, |
| // numValue fixed-size arguments, and then the variable-sized |
| // part of the values. The templates describe the expected results |
| // and have no associated variable sized data in the request. |
| // |
| // Each fixed-size argument has the form: |
| // tag uint32 // really a char, like 'b' or 'C' |
| // pad uint32 // unused |
| // val1 uint32 |
| // val2 uint32 |
| // |
| // The tags are: |
| // 'b': bool; val1 == 0 or 1 |
| // 'C': []byte; val1 == len, data in variable-sized section |
| // 'd': float64; (val1, val2) is data |
| // 'D': []float64; val1 == len, data in variable-sized section |
| // 'h': int; val1 == file descriptor |
| // 'i': int32; descriptor in next entry in m.desc |
| // 'I': []int; val1 == len, data in variable-sized section |
| // 's': string; val1 == len, data in variable-sized section |
| // |
| |
| func (m *msg) pack() error { |
| m.data = m.data[:0] |
| m.desc = m.desc[:0] |
| |
| // sizes, to fill in later |
| m.wuint32(0) |
| m.wuint32(0) |
| m.wuint32(0) |
| m.wuint32(0) |
| |
| // message header |
| m.wuint32(protocol) |
| m.wuint32(m.id) |
| m.wuint32(m.isRequest) |
| m.wuint32(m.rpc) |
| m.wuint32(m.status) |
| m.wuint32(uint32(len(m.value))) |
| m.wuint32(uint32(len(m.template))) |
| |
| // fixed-size templates |
| for i, x := range m.template { |
| var tag, val1, val2 uint32 |
| switch x.(type) { |
| default: |
| return errors.New("unexpected template type") |
| case bool: |
| tag = 'b' |
| case []byte: |
| tag = 'C' |
| val1 = uint32(m.size[i]) |
| case float64: |
| tag = 'd' |
| case []float64: |
| tag = 'D' |
| val1 = uint32(m.size[i]) |
| case int: |
| tag = 'h' |
| case int32: |
| tag = 'i' |
| case []int32: |
| tag = 'I' |
| val1 = uint32(m.size[i]) |
| case string: |
| tag = 's' |
| val1 = uint32(m.size[i]) |
| } |
| m.wuint32(tag) |
| m.wuint32(0) |
| m.wuint32(val1) |
| m.wuint32(val2) |
| } |
| |
| // fixed-size values |
| for _, x := range m.value { |
| var tag, val1, val2 uint32 |
| switch x := x.(type) { |
| default: |
| return errors.New("unexpected value type") |
| case bool: |
| tag = 'b' |
| if x { |
| val1 = 1 |
| } |
| case []byte: |
| tag = 'C' |
| val1 = uint32(len(x)) |
| case float64: |
| tag = 'd' |
| v := float64bits(x) |
| val1 = uint32(v) |
| val2 = uint32(v >> 32) |
| case []float64: |
| tag = 'D' |
| val1 = uint32(len(x)) |
| case int32: |
| tag = 'i' |
| m.desc = append(m.desc, x) |
| case []int32: |
| tag = 'I' |
| val1 = uint32(len(x)) |
| case string: |
| tag = 's' |
| val1 = uint32(len(x) + 1) |
| } |
| m.wuint32(tag) |
| m.wuint32(0) |
| m.wuint32(val1) |
| m.wuint32(val2) |
| } |
| |
| // variable-length data for values |
| for _, x := range m.value { |
| switch x := x.(type) { |
| case []byte: |
| m.wbytes(x) |
| case []float64: |
| for _, f := range x { |
| m.wuint64(float64bits(f)) |
| } |
| case []int32: |
| for _, j := range x { |
| m.wuint32(uint32(j)) |
| } |
| case string: |
| m.wstring(x) |
| m.wstring("\x00") |
| } |
| } |
| |
| // fill in sizes |
| data := m.data |
| m.data = m.data[:0] |
| m.wuint32(uint32(len(data))) |
| m.wuint32(uint32(len(m.desc))) |
| m.wuint32(uint32(len(data))) |
| m.wuint32(uint32(len(m.desc))) |
| m.data = data |
| |
| return nil |
| } |
| |
| func (m *msg) unpack() error { |
| totalSize := m.uint32() |
| totalDesc := m.uint32() |
| fragSize := m.uint32() |
| fragDesc := m.uint32() |
| if totalSize != fragSize || totalDesc != fragDesc { |
| return errors.New("Native Client: fragmented RPC messages not supported") |
| } |
| if m.uint32() != protocol { |
| return errors.New("Native Client: RPC protocol mismatch") |
| } |
| |
| // message header |
| m.id = m.uint32() |
| m.isRequest = m.uint32() |
| m.rpc = m.uint32() |
| m.status = m.uint32() |
| m.value = make([]interface{}, m.uint32()) |
| m.template = make([]interface{}, m.uint32()) |
| m.size = make([]int, len(m.template)) |
| if m.broken { |
| return errors.New("Native Client: malformed message") |
| } |
| |
| // fixed-size templates |
| for i := range m.template { |
| tag := m.uint32() |
| m.uint32() // padding |
| val1 := m.uint32() |
| m.uint32() // val2 |
| switch tag { |
| default: |
| return errors.New("Native Client: unexpected template type " + string(rune(tag))) |
| case 'b': |
| m.template[i] = false |
| case 'C': |
| m.template[i] = []byte(nil) |
| m.size[i] = int(val1) |
| case 'd': |
| m.template[i] = float64(0) |
| case 'D': |
| m.template[i] = []float64(nil) |
| m.size[i] = int(val1) |
| case 'i': |
| m.template[i] = int32(0) |
| case 'I': |
| m.template[i] = []int32(nil) |
| m.size[i] = int(val1) |
| case 'h': |
| m.template[i] = int(0) |
| case 's': |
| m.template[i] = "" |
| m.size[i] = int(val1) |
| } |
| } |
| |
| // fixed-size values |
| var ( |
| strsize []uint32 |
| d int |
| ) |
| for i := range m.value { |
| tag := m.uint32() |
| m.uint32() // padding |
| val1 := m.uint32() |
| val2 := m.uint32() |
| switch tag { |
| default: |
| return errors.New("Native Client: unexpected value type " + string(rune(tag))) |
| case 'b': |
| m.value[i] = val1 > 0 |
| case 'C': |
| m.value[i] = []byte(nil) |
| strsize = append(strsize, val1) |
| case 'd': |
| m.value[i] = float64frombits(uint64(val1) | uint64(val2)<<32) |
| case 'D': |
| m.value[i] = make([]float64, val1) |
| case 'i': |
| m.value[i] = int32(val1) |
| case 'I': |
| m.value[i] = make([]int32, val1) |
| case 'h': |
| m.value[i] = int(m.desc[d]) |
| d++ |
| case 's': |
| m.value[i] = "" |
| strsize = append(strsize, val1) |
| } |
| } |
| |
| // variable-sized parts of values |
| for i, x := range m.value { |
| switch x := x.(type) { |
| case []byte: |
| m.value[i] = m.bytes(int(strsize[0])) |
| strsize = strsize[1:] |
| case []float64: |
| for i := range x { |
| x[i] = float64frombits(m.uint64()) |
| } |
| case []int32: |
| for i := range x { |
| x[i] = int32(m.uint32()) |
| } |
| case string: |
| m.value[i] = string(m.bytes(int(strsize[0]))) |
| strsize = strsize[1:] |
| } |
| } |
| |
| if len(m.data) > 0 { |
| return errors.New("Native Client: junk at end of message") |
| } |
| return nil |
| } |
| |
| func float64bits(x float64) uint64 { |
| return *(*uint64)(unsafe.Pointer(&x)) |
| } |
| |
| func float64frombits(x uint64) float64 { |
| return *(*float64)(unsafe.Pointer(&x)) |
| } |
| |
| // At startup, connect to the name service. |
| var nsClient = nsConnect() |
| |
| func nsConnect() *srpcClient { |
| var ns int32 = -1 |
| _, _, errno := Syscall(sys_nameservice, uintptr(unsafe.Pointer(&ns)), 0, 0) |
| if errno != 0 { |
| println("Native Client nameservice:", errno.Error()) |
| return nil |
| } |
| |
| sock, _, errno := Syscall(sys_imc_connect, uintptr(ns), 0, 0) |
| if errno != 0 { |
| println("Native Client nameservice connect:", errno.Error()) |
| return nil |
| } |
| |
| c, err := newClient(int(sock)) |
| if err != nil { |
| println("Native Client nameservice init:", err.Error()) |
| return nil |
| } |
| |
| return c |
| } |
| |
| const ( |
| nsSuccess = 0 |
| nsNameNotFound = 1 |
| nsDuplicateName = 2 |
| nsInsufficientResources = 3 |
| nsPermissionDenied = 4 |
| nsInvalidArgument = 5 |
| ) |
| |
| func openNamedService(name string, mode int32) (fd int, err error) { |
| if nsClient == nil { |
| return 0, errors.New("no name service") |
| } |
| ret, err := nsClient.Call("lookup:si:ih", name, int32(mode)) |
| if err != nil { |
| return 0, err |
| } |
| status := ret[0].(int32) |
| fd = ret[1].(int) |
| switch status { |
| case nsSuccess: |
| // ok |
| case nsNameNotFound: |
| return -1, ENOENT |
| case nsDuplicateName: |
| return -1, EEXIST |
| case nsInsufficientResources: |
| return -1, EWOULDBLOCK |
| case nsPermissionDenied: |
| return -1, EPERM |
| case nsInvalidArgument: |
| return -1, EINVAL |
| default: |
| return -1, EINVAL |
| } |
| return fd, nil |
| } |