-
Notifications
You must be signed in to change notification settings - Fork 3
/
message.go
138 lines (120 loc) · 2.6 KB
/
message.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
package api
import (
"crypto/rand"
"encoding/base64"
"encoding/binary"
"errors"
"io"
)
const (
// TokenLength is the length of the message token in bytes.
TokenLength = 16
)
var (
// token is the message token.
token [TokenLength]byte
)
// Message types.
const (
TypeNone = iota
TypeOK
TypeError
TypeVPNConfigUpdate
TypeUndefined
)
// Header is a message header.
type Header struct {
Type uint16
Length uint32
Token [TokenLength]byte
}
// Message is an API message.
type Message struct {
Header
Value []byte
}
// NewMessage returns a new message with type t and payload p.
func NewMessage(t uint16, p []byte) *Message {
return &Message{
Header: Header{
Type: t,
Length: uint32(len(p)),
Token: token,
},
Value: p,
}
}
// NewOK returns a new OK message with payload p.
func NewOK(p []byte) *Message {
return NewMessage(TypeOK, p)
}
// NewError returns a new error message with payload p.
func NewError(p []byte) *Message {
return NewMessage(TypeError, p)
}
// ReadMessage returns the next message from r.
func ReadMessage(r io.Reader) (*Message, error) {
// read header
h := &Header{}
err := binary.Read(r, binary.LittleEndian, h)
if err != nil {
return nil, err
}
// check if message is valid
if h.Type == TypeNone || h.Type >= TypeUndefined {
return nil, errors.New("invalid message type")
}
if h.Token != token {
return nil, errors.New("invalid message token")
}
// read payload
b := make([]byte, h.Length)
_, err = io.ReadFull(r, b)
if err != nil {
return nil, err
}
// return message
m := &Message{
Header: *h,
Value: b,
}
return m, nil
}
// WriteMessage writes message m to r.
func WriteMessage(w io.Writer, m *Message) error {
// write header
err := binary.Write(w, binary.LittleEndian, m.Header)
if err != nil {
return err
}
// write payload
if len(m.Value) == 0 {
return nil
}
err = binary.Write(w, binary.LittleEndian, m.Value)
if err != nil {
return err
}
return nil
}
// GetToken generates and returns the message token as string. This should be
// used once on the server side before the server is started. Token must be
// passed to the client side.
func GetToken() (string, error) {
_, err := rand.Read(token[:])
if err != nil {
return "", err
}
return base64.RawURLEncoding.EncodeToString(token[:]), nil
}
// SetToken sets the message token from string. This should be used on the
// client side before sending requests to the server. Token must match token on
// the server side.
func SetToken(s string) error {
b, err := base64.RawURLEncoding.DecodeString(s)
if err != nil {
return err
}
copy(token[:], b)
return nil
}