-
Notifications
You must be signed in to change notification settings - Fork 1
/
serverutil.go
162 lines (130 loc) · 3.93 KB
/
serverutil.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package sunrpc
import (
"bytes"
"errors"
"io"
"reflect"
"github.com/rasky/go-xdr/xdr2"
)
type Server interface {
Register(proc uint32, rcvr interface{})
RegisterWithName(proc uint32, rcvr interface{}, name string)
SetAuth(authFun func(proc uint32, cred interface{}) bool)
Serve(string) error
}
// ReadProcedureCall reads an RPC "call" message from the given reader, ensuring the RPC message is
// of the "call" type and specifies version '2' of the RPC protocol.
func ReadProcedureCall(r io.Reader) (*ProcedureCall, error) {
// Read RPC message header
message := ProcedureCall{}
if _, err := xdr.Unmarshal(r, &message); err != nil {
return nil, errors.New("")
}
// Make sure this is a "Call" message
if message.Header.Type != Call {
return nil, errors.New("Expected a call message")
}
// We can only read RPCv2 messages
if message.Body.RPCVersion != 2 {
return nil, errors.New("Expected an RPC version 2 message")
}
return &message, nil
}
// WriteReplyMessage writes an "Accepted" RPC reply of type "Success", indicating that the procedure
// call was successful. The given return data is written right after the RPC response header.
func (s *server) WriteReplyMessage(w io.Writer, xid uint32, acceptType AcceptType, ret interface{}) error {
var buf bytes.Buffer
// Header
header := Message{
Xid: xid,
Type: Reply,
}
if _, err := xdr.Marshal(&buf, header); err != nil {
return err
}
// "Accepted"
if _, err := xdr.Marshal(&buf, ReplyBody{Type: Accepted}); err != nil {
return err
}
// "Success"
if _, err := xdr.Marshal(&buf, AcceptedReply{Type: acceptType}); err != nil {
return err
}
// Return data
if ret != nil {
if _, err := xdr.Marshal(&buf, ret); err != nil {
return err
}
}
_, err := w.Write(buf.Bytes())
return err
}
func (s *server) WriteReplyMessageRejectedAuth(w io.Writer, xid uint32, auth AuthStat) error {
var buf bytes.Buffer
// Header
header := Message{
Xid: xid,
Type: Reply,
}
if _, err := xdr.Marshal(&buf, header); err != nil {
return err
}
// "Denied"
if _, err := xdr.Marshal(&buf, ReplyBody{Type: Denied}); err != nil {
return err
}
// "AuthError"
if _, err := xdr.Marshal(&buf, RejectedReply{Stat: AuthError}); err != nil {
return err
}
if _, err := xdr.Marshal(&buf, &auth); err != nil {
return err
}
return nil
}
// callFunc Resolves and calls a real Go function given a procedure ID. The method must look
// schematically like this (but no conformance checks are performed at runtime):
//
// func (t *T) MethodName(argType T1, replyType *T2) error
func (s *server) callFunc(r io.Reader, receiverFunc interface{}) (interface{}, error) {
// Resolve function's type
funcType := reflect.TypeOf(receiverFunc)
// Deserialize arguments read from procedure call body
funcArg := reflect.New(funcType.In(0)).Interface()
if _, err := xdr.Unmarshal(r, &funcArg); err != nil {
return nil, err
}
// Call function
funcValue := reflect.ValueOf(receiverFunc)
funcArgValue := reflect.Indirect(reflect.ValueOf(funcArg))
funcRetValue := reflect.New(funcType.In(1).Elem())
s.log.Debugf("-> %+v", funcArgValue)
funcRetError := funcValue.Call([]reflect.Value{funcArgValue, funcRetValue})[0]
s.log.Debugf("<- %+v", funcRetValue)
if !funcRetError.IsNil() {
return nil, funcRetError.Interface().(error)
}
// Return result computed by the actual function. This is what should be sent back to the remote
// caller.
return funcRetValue.Interface(), nil
}
func (o *OpaqueAuth) Decode() (interface{}, error) {
switch o.Flavor {
case AuthFlavorNone:
return AuthNone{}, nil
case AuthFlavorUnix:
auth := AuthUnix{}
_, err := xdr.Unmarshal(bytes.NewReader(o.Body), &auth)
if err != nil {
return nil, err
}
return auth, nil
case AuthFlavorDes:
return nil, errors.New("unsupported DES authentication")
default:
return nil, errors.New("unsupported authentication flavor")
}
}
func (s *server) SetAuth(authFun func(uint32, interface{}) bool) {
s.authFun = authFun
}