mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-20 07:13:33 +01:00
179 lines
3.3 KiB
Go
179 lines
3.3 KiB
Go
// Copyright © 2017 VMware, Inc. All Rights Reserved.
|
|
// SPDX-License-Identifier: BSD-2-Clause
|
|
//
|
|
package rpc
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"net"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/vmware/go-nfs-client/nfs/util"
|
|
"github.com/vmware/go-nfs-client/nfs/xdr"
|
|
)
|
|
|
|
const (
|
|
MsgAccepted = iota
|
|
MsgDenied
|
|
)
|
|
|
|
const (
|
|
Success = iota
|
|
ProgUnavail
|
|
ProgMismatch
|
|
ProcUnavail
|
|
GarbageArgs
|
|
SystemErr
|
|
)
|
|
|
|
const (
|
|
RpcMismatch = iota
|
|
)
|
|
|
|
var xid uint32
|
|
|
|
func init() {
|
|
// seed the XID (which is set by the client)
|
|
xid = rand.New(rand.NewSource(time.Now().UnixNano())).Uint32()
|
|
}
|
|
|
|
type Client struct {
|
|
*tcpTransport
|
|
}
|
|
|
|
func DialTCP(network string, ldr *net.TCPAddr, addr string) (*Client, error) {
|
|
a, err := net.ResolveTCPAddr(network, addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn, err := net.DialTCP(a.Network(), ldr, a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
t := &tcpTransport{
|
|
r: bufio.NewReader(conn),
|
|
wc: conn,
|
|
}
|
|
|
|
return &Client{t}, nil
|
|
}
|
|
|
|
type message struct {
|
|
Xid uint32
|
|
Msgtype uint32
|
|
Body interface{}
|
|
}
|
|
|
|
func (c *Client) Call(call interface{}) (io.ReadSeeker, error) {
|
|
retries := 1
|
|
|
|
msg := &message{
|
|
Xid: atomic.AddUint32(&xid, 1),
|
|
Body: call,
|
|
}
|
|
|
|
retry:
|
|
w := new(bytes.Buffer)
|
|
if err := xdr.Write(w, msg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if _, err := c.Write(w.Bytes()); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res, err := c.recv()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
xid, err := xdr.ReadUint32(res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if xid != msg.Xid {
|
|
return nil, fmt.Errorf("xid did not match, expected: %x, received: %x", msg.Xid, xid)
|
|
}
|
|
|
|
mtype, err := xdr.ReadUint32(res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if mtype != 1 {
|
|
return nil, fmt.Errorf("message as not a reply: %d", mtype)
|
|
}
|
|
|
|
status, err := xdr.ReadUint32(res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch status {
|
|
case MsgAccepted:
|
|
|
|
// padding
|
|
_, err = xdr.ReadUint32(res)
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
|
|
opaque_len, err := xdr.ReadUint32(res)
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
|
|
_, err = res.Seek(int64(opaque_len), io.SeekCurrent)
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
|
|
acceptStatus, _ := xdr.ReadUint32(res)
|
|
|
|
switch acceptStatus {
|
|
case Success:
|
|
return res, nil
|
|
case ProgUnavail:
|
|
return nil, fmt.Errorf("rpc: PROG_UNAVAIL - server does not recognize the program number")
|
|
case ProgMismatch:
|
|
return nil, fmt.Errorf("rpc: PROG_MISMATCH - program version does not exist on the server")
|
|
case ProcUnavail:
|
|
return nil, fmt.Errorf("rpc: PROC_UNAVAIL - unrecognized procedure number")
|
|
case GarbageArgs:
|
|
// emulate Linux behaviour for GARBAGE_ARGS
|
|
if retries > 0 {
|
|
util.Debugf("Retrying on GARBAGE_ARGS per linux semantics")
|
|
retries--
|
|
goto retry
|
|
}
|
|
|
|
return nil, fmt.Errorf("rpc: GARBAGE_ARGS - rpc arguments cannot be XDR decoded")
|
|
case SystemErr:
|
|
return nil, fmt.Errorf("rpc: SYSTEM_ERR - unknown error on server")
|
|
default:
|
|
return nil, fmt.Errorf("rpc: unknown accepted status error: %d", acceptStatus)
|
|
}
|
|
|
|
case MsgDenied:
|
|
rejectStatus, _ := xdr.ReadUint32(res)
|
|
switch rejectStatus {
|
|
case RpcMismatch:
|
|
|
|
default:
|
|
return nil, fmt.Errorf("rejectedStatus was not valid: %d", rejectStatus)
|
|
}
|
|
|
|
default:
|
|
return nil, fmt.Errorf("rejectedStatus was not valid: %d", status)
|
|
}
|
|
|
|
panic("unreachable")
|
|
}
|