-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathheader.go
357 lines (321 loc) · 10.9 KB
/
header.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
package systemd
import (
"encoding/binary"
"fmt"
)
// Message types that can appear in the second byte of the header.
const (
// msgTypeMethodCall is a method call.
// This message type may prompt a reply.
msgTypeMethodCall byte = 1 + iota
// msgTypeMethodReply is a method reply with returned data.
msgTypeMethodReply
// msgTypeError is an error reply.
// If the first argument exists and is a string, it is an error message.
msgTypeError
// msgTypeSignal is a signal emission.
msgTypeSignal
)
// header represents a message header.
type header struct {
// ByteOrder is an endianness flag;
// ASCII 'l' for little-endian or ASCII 'B' for big-endian.
// Both header and body are in this endianness.
ByteOrder byte
// Type is a message type.
Type byte
// Flags is a bitwise OR of message flags.
Flags byte
// Proto is a major protocol version of the sending application.
Proto byte
// BodyLen is a length in bytes of the message body,
// starting from the end of the header.
// The header ends after its alignment padding to an 8-boundary.
BodyLen uint32
// Serial is the serial of this message,
// used as a cookie by the sender to identify the reply corresponding to this request.
// This must not be zero.
Serial uint32
// FieldsLen is a length of the header fields array in bytes
// excluding a padding at the end.
// The array contains structs of header fields (code, variant).
FieldsLen uint32
// Fields contain header fields if a caller chose to decode them.
// A header must contain the required header fields for its message type,
// and zero or more of any optional header fields.
// Note, the order of header fields in the message is preserved.
Fields []headerField
}
const (
littleEndian = 'l'
bigEndian = 'B'
)
// ByteOrder specifies how to convert byte slices into 32-bit unsigned integers.
// Both header and body are in this endianness.
func (h *header) Order() binary.ByteOrder {
switch h.ByteOrder {
case littleEndian:
return binary.LittleEndian
case bigEndian:
return binary.BigEndian
default:
return nil
}
}
// Len returns the length of the message header including padding at the end.
func (h *header) Len() uint32 {
wantHdrLen := msgPrologueSize + h.FieldsLen
_, padding := nextOffset(wantHdrLen, 8)
return wantHdrLen + padding
}
const (
// msgPrologueSize is the length of the fixed part of a message header,
// i.e., from the beginning until the header fields.
msgPrologueSize = 16
// maxMsgSize is the maximum length of a message (128 MiB),
// including header, header alignment padding, and body.
maxMsgSize = 134217728
)
// decodeHeader decodes a message header from conn into h.
// The string converter conv helps to reduce allocs when decoding header fields.
// A caller can ignore the header fields with the skipFields flag.
// Note, all fields of h must be overwritten because h is reused.
//
// The signature of the header is "yyyyuua(yv)" which is
// BYTE, BYTE, BYTE, BYTE, UINT32, UINT32, ARRAY of STRUCT of (BYTE, VARIANT).
// Here only the fixed portion "yyyyuua" of the entire header is decoded
// where "a" is the length of the header array in bytes.
// The caller can later decode "(yv)" structs knowing how many bytes to process
// based on the header length.
func decodeHeader(dec *decoder, conv *stringConverter, h *header, skipFields bool) error {
// Read the fixed portion of the message header (16 bytes),
// and set the position of the next byte we should be reading from.
b, err := dec.ReadN(msgPrologueSize)
if err != nil {
return err
}
h.ByteOrder = b[0]
order := h.Order()
if order == nil {
return fmt.Errorf("unknown byte order: %d", h.ByteOrder)
}
dec.SetOrder(order)
h.Type = b[1]
h.Flags = b[2]
h.Proto = b[3]
h.BodyLen = order.Uint32(b[4:8])
h.Serial = order.Uint32(b[8:12])
h.FieldsLen = order.Uint32(b[12:])
if h.BodyLen > maxMsgSize {
return fmt.Errorf("message exceeded the maximum length: %d/%d bytes", h.BodyLen, maxMsgSize)
}
// Clean the fields from a previous header use.
h.Fields = h.Fields[:0]
// Read the header fields where the body signature is stored.
// A caller might already know the signature from the spec
// and choose not to decode the fields as an optimization.
if skipFields {
if _, err = dec.ReadN(h.FieldsLen); err != nil {
return fmt.Errorf("message header: %w", err)
}
} else {
var (
f headerField
hdrArrEnd = dec.offset + h.FieldsLen
)
for dec.offset < hdrArrEnd {
if f, err = decodeHeaderField(dec, conv); err != nil {
break
}
h.Fields = append(h.Fields, f)
}
}
// The length of the header must be a multiple of 8,
// allowing the body to begin on an 8-byte boundary.
// If the header does not naturally end on an 8-byte boundary,
// up to 7 bytes of alignment padding is added.
// Here we're discarding the header padding.
if err = dec.Align(8); err != nil {
return fmt.Errorf("discard header padding: %w", err)
}
return nil
}
// Header fields.
const (
// fieldPath is the object to send a call to,
// or the object a signal is emitted from.
// This header field is controlled by the message sender.
fieldPath byte = 1 + iota
// fieldInterface is the interface to invoke a method call on,
// or that a signal is emitted from.
// Optional for method calls, required for signals.
// This header field is controlled by the message sender.
fieldInterface
// fieldMember is the member, either the method name or signal name.
// This header field is controlled by the message sender.
fieldMember
// fieldErrorName is the name of the error that occurred.
fieldErrorName
// fieldReplySerial is the serial number of the message this message is a reply to.
// The serial number is the second UINT32 in the header.
// This header field is controlled by the message sender.
fieldReplySerial
// fieldDestination represents the name of the connection the message is intended for.
// This field is usually only meaningful in combination with the message bus,
// but other servers may define their own meanings for it.
// This header field is controlled by the message sender.
fieldDestination
// fieldSender is a unique name of the sending connection.
// This field is usually only meaningful in combination with the message bus,
// but other servers may define their own meanings for it.
// On a message bus, this header field is controlled by the message bus,
// so it is as reliable and trustworthy as the message bus itself.
// Otherwise, this header field is controlled by the message sender,
// unless there is out-of-band information that indicates otherwise.
fieldSender
// fieldSignature is the signature of the message body.
// If omitted, it is assumed to be the empty signature "",
// i.e., the body must be 0-length.
// This header field is controlled by the message sender.
fieldSignature
)
// D-Bus types,
// see https://dbus.freedesktop.org/doc/dbus-specification.html#id-1.3.8.
const (
typeByte = 'y'
typeUint32 = 'u'
typeString = 's'
typeObjectPath = 'o'
typeSignature = 'g'
)
// headerField represents a header field.
// The array at the end of the header contains header fields,
// where each field is a 1-byte field code followed by a field value.
// For example, REPLY_SERIAL code and UINT32 value 3
// which is the serial number of the message this message is a reply to.
type headerField struct {
// Signature is a signature (single complete type) of the value.
Signature string
// The following fields contain a header field value
// depending on signature.
// The decision was made against an interface{} to reduce allocs.
U uint64
S string
// Code is a header field code, e.g., fieldPath.
Code byte
}
// decodeHeaderField decodes a header field.
func decodeHeaderField(d *decoder, conv *stringConverter) (headerField, error) {
var f headerField
// Since "(yv)" struct is being decoded, an alignment must be discarded.
err := d.Align(8)
if err != nil {
return f, err
}
// Decode "y" (a byte) which is a field code.
if f.Code, err = d.Byte(); err != nil {
return f, err
}
// Decode "v" (variant) which is a field value.
// Variants are marshaled as the SIGNATURE of the contents
// (which must be a single complete type),
// followed by a marshaled value with the type given by that signature.
var sign []byte
if sign, err = d.Signature(); err != nil {
return f, err
}
// Container types are not supported yet.
// Because there is no need in the scope of this library.
if len(sign) != 1 {
return f, fmt.Errorf("container type is not supported: %s", sign)
}
f.Signature = conv.String(sign)
var (
u uint32
s []byte
)
switch sign[0] {
case typeUint32:
if u, err = d.Uint32(); err != nil {
return f, err
}
f.U = uint64(u)
case typeString, typeObjectPath:
if s, err = d.String(); err != nil {
return f, err
}
f.S = conv.String(s)
case typeSignature:
if s, err = d.Signature(); err != nil {
return f, err
}
f.S = conv.String(s)
default:
return f, fmt.Errorf("unknown type: %s", sign)
}
return f, err
}
// encodeHeader encodes the message header h.
// FieldsLen can be arbitrary, because it will be overwritten.
func encodeHeader(enc *encoder, h *header) error {
if h.BodyLen > maxMsgSize {
return fmt.Errorf("message exceeded the maximum length: %d/%d bytes", h.BodyLen, maxMsgSize)
}
// Write the fixed portion of the message header (16 bytes).
enc.Byte(h.ByteOrder)
enc.Byte(h.Type)
enc.Byte(h.Flags)
enc.Byte(h.Proto)
enc.Uint32(h.BodyLen)
enc.Uint32(h.Serial)
// The length of the header fields array
// gets overwritten after the array is encoded.
const headerFieldsLenOffset = 12
enc.Uint32(h.FieldsLen)
// Encode header fields.
var (
err error
f headerField
fieldsOffset = enc.Offset()
)
for _, f = range h.Fields {
if err = encodeHeaderField(enc, f); err != nil {
return err
}
}
// Overwrite the h.FieldsLen with an actual length of fields array.
fieldsLen := enc.Offset() - fieldsOffset
if err = enc.Uint32At(fieldsLen, headerFieldsLenOffset); err != nil {
return fmt.Errorf("encode header FieldsLen: %w", err)
}
// The length of the header must be a multiple of 8,
// allowing the body to begin on an 8-byte boundary.
enc.Align(8)
return nil
}
// encodeHeaderField encodes a header field.
func encodeHeaderField(e *encoder, f headerField) error {
// Container types are not supported yet.
// Because there is no need in the scope of this library.
if len(f.Signature) != 1 {
return fmt.Errorf("container type is not supported: %s", f.Signature)
}
// Since "(yv)" struct is being encoded, a padding should be added.
e.Align(8)
// Encode "y" (a byte) which is a field code.
e.Byte(f.Code)
// Encode v (variant) which is a field value
// (signature of the type and value itself).
e.Signature(f.Signature)
switch f.Signature[0] {
case typeUint32:
e.Uint32(uint32(f.U))
case typeString, typeObjectPath:
e.String(f.S)
case typeSignature:
e.Signature(f.S)
default:
return fmt.Errorf("unknown type: %s", f.Signature)
}
return nil
}