1package zerobus
2
3import "encoding/binary"
4
5// Type is the kind of a message.
6type Type byte
7
8// Message types.
9const (
10 TypeMethodCall Type = 1
11 TypeMethodReturn Type = 2
12 TypeError Type = 3
13 TypeSignal Type = 4
14)
15
16// Flags are the header flags of a message.
17type Flags byte
18
19// Message flags.
20const (
21 // FlagNoReplyExpected tells the receiver not to answer a method call.
22 FlagNoReplyExpected Flags = 0x1
23 // FlagNoAutoStart tells the bus not to start the destination service.
24 FlagNoAutoStart Flags = 0x2
25 // FlagAllowInteractiveAuth allows the receiver to prompt the user.
26 FlagAllowInteractiveAuth Flags = 0x4
27)
28
29// Header field codes.
30const (
31 fieldPath = 1
32 fieldInterface = 2
33 fieldMember = 3
34 fieldErrorName = 4
35 fieldReplySerial = 5
36 fieldDestination = 6
37 fieldSender = 7
38 fieldSignature = 8
39 fieldUnixFDs = 9
40)
41
42const headerLen = 16 // the fixed part, up to the length of the field array
43
44// Message is a received message.
45//
46// Its strings point into the connection's receive buffer: they and the
47// message itself are valid only until the next read on the connection.
48type Message struct {
49 Type Type
50 Flags Flags
51 Serial uint32
52 ReplySerial uint32
53 Path string
54 Interface string
55 Member string
56 ErrorName string
57 Destination string
58 Sender string
59 Signature string // the signature of the body
60
61 body []byte
62 big bool
63 r Reader
64}
65
66// Body returns a reader at the start of the message body. Each call starts
67// over from the beginning.
68func (m *Message) Body() *Reader {
69 m.r = Reader{buf: m.body, big: m.big}
70 return &m.r
71}
72
73// frameLen returns the full length of the message whose fixed header is b,
74// which must hold at least headerLen bytes.
75func frameLen(b []byte) (int, error) {
76 var order binary.ByteOrder
77 switch b[0] {
78 case 'l':
79 order = binary.LittleEndian
80 case 'B':
81 order = binary.BigEndian
82 default:
83 return 0, ErrMalformed
84 }
85 if b[3] != 1 {
86 return 0, ErrMalformed // protocol version
87 }
88 body := uint64(order.Uint32(b[4:]))
89 fields := uint64(order.Uint32(b[12:]))
90 if body > maxMessage || fields > maxArray {
91 return 0, ErrTooLarge
92 }
93 n := uint64(pad(headerLen+int(fields), 8)) + body
94 if n > maxMessage {
95 return 0, ErrTooLarge
96 }
97 return int(n), nil
98}
99
100// parse decodes the message b, whose length frameLen returned, into m. The
101// strings of m point into b.
102func parse(b []byte, m *Message) error {
103 *m = Message{big: b[0] == 'B', Type: Type(b[1]), Flags: Flags(b[2])}
104 r := Reader{buf: b, big: m.big, off: 4}
105 bodyLen := int(r.Uint32())
106 m.Serial = r.Uint32()
107 if m.Serial == 0 {
108 return ErrMalformed
109 }
110 var fds uint32
111 end := r.Array('(')
112 for r.More(end) {
113 r.Struct()
114 code := r.Byte()
115 sig := r.Variant()
116 if r.err != nil {
117 break
118 }
119 want := byte('s')
120 var dst *string
121 switch code {
122 case fieldPath:
123 want, dst = 'o', &m.Path
124 case fieldInterface:
125 dst = &m.Interface
126 case fieldMember:
127 dst = &m.Member
128 case fieldErrorName:
129 dst = &m.ErrorName
130 case fieldDestination:
131 dst = &m.Destination
132 case fieldSender:
133 dst = &m.Sender
134 case fieldSignature:
135 want, dst = 'g', &m.Signature
136 case fieldReplySerial, fieldUnixFDs:
137 if sig != "u" {
138 return ErrMalformed
139 }
140 if code == fieldReplySerial {
141 m.ReplySerial = r.Uint32()
142 } else {
143 fds = r.Uint32()
144 }
145 continue
146 default:
147 r.Skip(sig) // unknown fields must be ignored
148 continue
149 }
150 if len(sig) != 1 || sig[0] != want {
151 return ErrMalformed
152 }
153 if want == 'g' {
154 *dst = r.Signature()
155 } else {
156 *dst = r.Str()
157 }
158 }
159 if r.err != nil {
160 return r.err
161 }
162 if fds != 0 {
163 return ErrMalformed // fd passing is not negotiated, so none may come
164 }
165 start := pad(r.off, 8)
166 if start+bodyLen != len(b) || start > len(b) {
167 return ErrMalformed
168 }
169 for _, c := range b[r.off:start] {
170 if c != 0 {
171 return ErrMalformed
172 }
173 }
174 m.body = b[start:]
175 // No signature means an empty body.
176 if m.Signature == "" && bodyLen != 0 || !validSignature(m.Signature) || m.Path != "" && !validPath(m.Path) {
177 return ErrMalformed
178 }
179 switch m.Type {
180 case TypeMethodCall:
181 if m.Path == "" || m.Member == "" {
182 return ErrMalformed
183 }
184 case TypeSignal:
185 if m.Path == "" || m.Interface == "" || m.Member == "" {
186 return ErrMalformed
187 }
188 case TypeError:
189 if m.ErrorName == "" || m.ReplySerial == 0 {
190 return ErrMalformed
191 }
192 case TypeMethodReturn:
193 if m.ReplySerial == 0 {
194 return ErrMalformed
195 }
196 }
197 return nil
198}
199
200// begin starts a new message in e: the fixed header and the field array, up
201// to the body. Empty strings are left out of the header.
202func (e *Encoder) begin(t Type, serial, replySerial uint32, dest, path, iface, member, errName, sig string) {
203 e.buf = append(e.buf[:0], 'l', byte(t), 0, 1, 0, 0, 0, 0)
204 e.buf = binary.LittleEndian.AppendUint32(e.buf, serial)
205 e.body, e.sig, e.err = 0, sig, nil
206 // A bus disconnects a client that sends an invalid name or leaves out a
207 // field its message type requires.
208 if dest != "" && !validBusName(dest) || iface != "" && !validInterface(iface) ||
209 member != "" && !validMember(member) || errName != "" && !validInterface(errName) {
210 e.invalid()
211 }
212 switch t {
213 case TypeMethodCall:
214 if path == "" || member == "" {
215 e.invalid()
216 }
217 case TypeSignal:
218 if path == "" || iface == "" || member == "" {
219 e.invalid()
220 }
221 case TypeError:
222 if errName == "" {
223 e.invalid()
224 }
225 }
226 fields := e.BeginArray('(')
227 str := func(code byte, typ, v string) {
228 if v == "" {
229 return
230 }
231 e.Struct()
232 e.Byte(code)
233 e.Signature(typ)
234 switch typ {
235 case "o":
236 e.ObjectPath(v)
237 case "g":
238 e.Signature(v)
239 default:
240 e.Str(v)
241 }
242 }
243 str(fieldPath, "o", path)
244 str(fieldInterface, "s", iface)
245 str(fieldMember, "s", member)
246 str(fieldErrorName, "s", errName)
247 if replySerial != 0 {
248 e.Struct()
249 e.Byte(fieldReplySerial)
250 e.Signature("u")
251 e.Uint32(replySerial)
252 }
253 str(fieldDestination, "s", dest)
254 str(fieldSignature, "g", sig)
255 e.EndArray(fields)
256 e.pad(8)
257 e.body = len(e.buf)
258}
259
260// finish writes the body length and checks the body against the signature.
261// It returns the bytes to send.
262func (e *Encoder) finish() ([]byte, error) {
263 if e.body == 0 {
264 return nil, ErrInvalid
265 }
266 if e.err != nil {
267 return nil, e.err
268 }
269 if len(e.buf) > maxMessage {
270 return nil, ErrTooLarge
271 }
272 binary.LittleEndian.PutUint32(e.buf[4:], uint32(len(e.buf)-e.body))
273 // Reading the whole body back with the signature proves it matches: a
274 // bus disconnects a client that sends a body that does not.
275 r := Reader{buf: e.buf[e.body:]}
276 r.check(e.sig)
277 if !r.Done() {
278 return nil, ErrInvalid
279 }
280 return e.buf, nil
281}