-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmessage_common.go
395 lines (371 loc) · 12.1 KB
/
message_common.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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
package eidc32proxy
import (
"bufio"
"bytes"
"errors"
"fmt"
"io/ioutil"
"net/http"
"strings"
"sync"
"time"
"github.com/logrusorgru/aurora"
)
const (
ua = "User-Agent"
ok200 = "200 OK"
contentType = "content-type"
ApplicationJSON = "application/json"
Northbound Direction = true
Southbound Direction = false
)
const (
MsgTypeUnknown MsgType = iota
MsgTypeConnectedRequest // Northbound via POST
MsgTypeConnectedResponse // Southbound
MsgTypeGetoutboundRequest // Southbound via GET
MsgTypeGetoutboundResponse // Northbound
MsgTypeGetPointStatusRequest // Southbound via POST
MsgTypeGetPointStatusResponse // Northbound
MsgTypeSetTimeRequest // Southbound via POST
MsgTypeSetTimeResponse // Northbound
MsgTypePointStatusRequest // Northbound via POST (no response)
MsgTypeEventRequest // Northbound via POST (no response)
MsgTypeDoor0x2fLockStatusRequest // Southbound via POST
MsgTypeDoor0x2fLockStatusResponse // Northbound
MsgTypeEnableEventsRequest // Southbound via GET
MsgTypeEnableEventsResponse // Northbound EIDCSimpleResponse
MsgTypeEventAckRequest // Southbound via POST
MsgTypeEventAckResponse // Northbound EIDCSimpleResponse
MsgTypeHeartbeatRequest // Southbound via GET
MsgTypeHeartbeatResponse // Northbound EIDCSimpleResponse
MsgTypeSetWebUserRequest // Southbound via POST
MsgTypeSetWebUserResponse // Northbound EIDCSimpleResponse
MsgTypeSetOutboundRequest // Southbound via POST
MsgTypeSetOutboundResponse // Northbound EIDCSimpleResponse
MsgTypeResetEventsRequest // Southbound via GET
MsgTypeResetEventsResponse // Northbound
MsgTypeClearPointsRequest // Southbound via GET
MsgTypeClearPointsResponse // Northbound
MsgTypeAddPointsRequest // Southbound via POST
MsgTypeAddPointsResponse // Northbound
MsgTypeResetPointEngineRequest // Southbound via GET
MsgTypeResetPointEngineResponse // Northbound
MsgTypeAddFormatsRequest // Southbound via POST
MsgTypeAddFormatsResponse // Northbound
MsgTypeAddPrivilegesRequest // Southbound via POST
MsgTypeAddPrivilegesResponse // Northbound
MsgTypeAddCardsRequest // Southbound via POST
MsgTypeAddCardsResponse // Northbound
MsgTypeSetConfigKeyRequest // Southbound via POST
MsgTypeSetConfigKeyResponse // Northbound
MsgTypeSetDeviceIDRequest // Southbound via POST
MsgTypeSetDeviceIDResponse // Northbound
MsgTypeClearSchedulesRequest // Southbound via GET
MsgTypeClearSchedulesResponse // Northbound
MsgTypeClearHolidaysRequest // Southbound via GET
MsgTypeClearHolidaysResponse // Northbound
MsgTypeAddSchedulesRequest // Southbound via POST
MsgTypeAddSchedulesResponse // Northbound
MsgTypeClearPrivilegesRequest // Southbound via GET
MsgTypeClearPrivilegesResponse // Northbound
MsgTypeClearCardsRequest // Southbound via GET
MsgTypeClearCardsResponse // Northbound
MsgTypeDownloadRequest // Southbound via POST
MsgTypeDownloadResponse // Northbound
MsgTypeReflashRequest // Southbound via GET
MsgTypeReflashResponse // Northbound
)
type MsgType int
type Direction bool
type Message struct {
direction Direction
Request *http.Request
Response *http.Response
Body []byte
Type MsgType
origBytes []byte
Injected bool
Dropped bool
lock *sync.Mutex
}
// Send sends a message in the passed session. It's really only safe to use with
// messages that don't provoke a response. Messages that *do* provoke a response
// should be sent using the session's Inject() method because it supports
// including manglers which can be used to intercept those responses.
func (o Message) Send(s *Session) {
s.Inject(o, nil)
}
// ReadMsg parses a byte slice containing an entire HTTP message including
// headers and body. It also takes a direction. ReadMsg returns a *Message
// with appropriate fields populated. Note that the Message structure contains
// both *http.Request and *http.Response elements. Exactly one of these will be
// populated, depending on what's found in the []byte.
func ReadMsg(in []byte, dir Direction) (*Message, error) {
var err error
msg := &Message{
direction: dir,
origBytes: in,
lock: &sync.Mutex{},
}
switch {
case isRequest(in):
msg.Request, err = http.ReadRequest(bufio.NewReader(bytes.NewReader(in)))
if err != nil {
return msg, err
}
if msg.Request.ContentLength > 0 {
msg.Body, err = ioutil.ReadAll(msg.Request.Body)
if err != nil {
return msg, err
}
}
case isResponse(in):
msg.Response, err = http.ReadResponse(bufio.NewReader(bytes.NewReader(in)), nil)
if err != nil {
return msg, err
}
if msg.Response.ContentLength > 0 {
msg.Body, err = ioutil.ReadAll(msg.Response.Body)
if err != nil {
return msg, err
}
}
default:
return msg, errors.New("data submitted to ReadMsg neither a request nor response")
}
msg.Type = msg.GetType()
return msg, nil
}
// Marshal renders a message into bytes suitable for transmission
func (o Message) Marshal() ([]byte, error) {
switch {
case o.Request != nil:
return o.marshalRequest()
case o.Response != nil:
return o.marshalResponse()
default:
return nil, errors.New("cannot Marshal message with neither request or response elements")
}
}
func (o Message) marshalRequest() ([]byte, error) {
o.lock.Lock()
defer o.lock.Unlock()
// Re-set the original user-agent string. If blank, we set
// it blank. This stops GO from using its own value here.
o.Request.Header.Set(ua, o.Request.Header.Get(ua))
o.Request.Body = ioutil.NopCloser(bytes.NewReader(o.Body))
out := bytes.Buffer{}
err := o.Request.Write(&out)
if err != nil {
return nil, err
}
return out.Bytes(), nil
}
func (o Message) marshalResponse() ([]byte, error) {
o.lock.Lock()
defer o.lock.Unlock()
o.Response.Body = ioutil.NopCloser(bytes.NewReader(o.Body))
out := bytes.Buffer{}
err := o.Response.Write(&out)
if err != nil {
return nil, err
}
return out.Bytes(), nil
}
func (o Direction) String() string {
switch o {
case true:
return "Northbound"
default:
return "Southbound"
}
}
func (o Message) GetType() MsgType {
if o.Type != 0 {
return o.Type
}
switch o.direction {
case Northbound:
return o.getNorthboundType()
case Southbound:
return o.getSouthboundMsgType()
}
// This should never happen
return o.Type
}
func (o Message) Direction() Direction {
return o.direction
}
func (o MsgType) String() string {
switch o {
case MsgTypeConnectedRequest:
return "Connected Request"
case MsgTypeConnectedResponse:
return "Connected Response"
case MsgTypeGetoutboundRequest:
return "Getoutbound Request"
case MsgTypeGetoutboundResponse:
return "Getoutbound Response"
case MsgTypeGetPointStatusRequest:
return "GetPointStatus Request"
case MsgTypeGetPointStatusResponse:
return "GetPointStatus Response"
case MsgTypeSetTimeRequest:
return "SetTime Request"
case MsgTypeSetTimeResponse:
return "SetTime Response"
case MsgTypePointStatusRequest:
return "PointStatus Request"
case MsgTypeEventRequest:
return "Event Request"
case MsgTypeDoor0x2fLockStatusRequest:
return "Door/LockStatus Request"
case MsgTypeDoor0x2fLockStatusResponse:
return "Door/LockStatus Response"
case MsgTypeEnableEventsRequest:
return "EnableEvents Request"
case MsgTypeEnableEventsResponse:
return "EnableEvents Response"
case MsgTypeEventAckRequest:
return "EventAck Request"
case MsgTypeEventAckResponse:
return "EventAck Response"
case MsgTypeHeartbeatRequest:
return "Heartbeat Request"
case MsgTypeHeartbeatResponse:
return "Heartbeat Response"
case MsgTypeSetWebUserRequest:
return "SetWebUser Request"
case MsgTypeSetWebUserResponse:
return "SetWebUser Response"
case MsgTypeSetOutboundRequest:
return "SetOutbound Request"
case MsgTypeSetOutboundResponse:
return "SetOutbound Response"
case MsgTypeResetEventsRequest:
return "ResetEvents Request"
case MsgTypeResetEventsResponse:
return "ResetEvents Response"
case MsgTypeClearPointsRequest:
return "ClearPoints Request"
case MsgTypeClearPointsResponse:
return "ClearPoints Response"
case MsgTypeAddPointsRequest:
return "AddPoints Request"
case MsgTypeAddPointsResponse:
return "AddPoints Response"
case MsgTypeResetPointEngineRequest:
return "ResetPointEngine Request"
case MsgTypeResetPointEngineResponse:
return "ResetPointEngine Response"
case MsgTypeAddFormatsRequest:
return "AddFormats Request"
case MsgTypeAddFormatsResponse:
return "AddFormats Response"
case MsgTypeAddPrivilegesRequest:
return "AddPrivileges Request"
case MsgTypeAddPrivilegesResponse:
return "AddPrivileges Response"
case MsgTypeAddCardsRequest:
return "AddCards Request"
case MsgTypeAddCardsResponse:
return "AddCards Response"
case MsgTypeSetConfigKeyRequest:
return "SetConfigKey Request"
case MsgTypeSetConfigKeyResponse:
return "SetConfigKey Response"
case MsgTypeSetDeviceIDRequest:
return "SetDeviceID Request"
case MsgTypeSetDeviceIDResponse:
return "SetDeviceID Response"
case MsgTypeClearSchedulesRequest:
return "ClearSchedules Request"
case MsgTypeClearSchedulesResponse:
return "ClearSchedules Response"
case MsgTypeClearHolidaysRequest:
return "ClearHolidays Request"
case MsgTypeClearHolidaysResponse:
return "ClearHolidays Response"
case MsgTypeAddSchedulesRequest:
return "AddSchedules Request"
case MsgTypeAddSchedulesResponse:
return "AddSchedules Response"
case MsgTypeClearPrivilegesRequest:
return "ClearPrivileges Request"
case MsgTypeClearPrivilegesResponse:
return "ClearPrivileges Response"
case MsgTypeClearCardsRequest:
return "ClearCards Request"
case MsgTypeClearCardsResponse:
return "ClearCards Response"
case MsgTypeDownloadRequest:
return "Download Request"
case MsgTypeDownloadResponse:
return "Download Response"
case MsgTypeReflashRequest:
return "Reflash Request"
case MsgTypeReflashResponse:
return "Reflash Response"
default:
return fmt.Sprintf("Event type %d has no string value", o)
}
}
func (o Message) contentType() string {
switch {
case o.Request != nil:
return o.Request.Header.Get(contentType)
case o.Response != nil:
return o.Response.Header.Get(contentType)
}
return ""
}
func (o Message) OrigBytes() []byte {
return o.origBytes
}
func (o Message) String() (string, error) {
b, err := o.Marshal()
if err != nil {
return "", err
}
_ = b
i, err := impersonate(b, o.direction)
if err != nil {
return "", err
}
return string(i), nil
}
func (o Message) PrintableLines() ([]string, error){
now := time.Now().Format("01/02 15:04:05")
str, err := o.String()
if err != nil {
return nil, err
}
// make carriage returns and newlines printable
str = strings.ReplaceAll((str), "\r", "\\r")
str = strings.ReplaceAll((str), "\n", "\\n\n")
lines := strings.Split(str, "\n")
for i, l := range lines {
switch o.direction {
case Northbound:
switch {
case o.Injected:
lines[i] = fmt.Sprintf("%s\t%s\n", aurora.White(now), aurora.Italic(aurora.Red(l)))
case o.Dropped:
lines[i] = fmt.Sprintf("%s\t%s\n", aurora.White(now), aurora.BgRed(l))
default:
lines[i] = fmt.Sprintf("%s\t%s\n", aurora.White(now), aurora.Red(l))
}
case Southbound:
switch {
case o.Injected:
lines[i] = fmt.Sprintf("%s\t%s\n", aurora.White(now), aurora.Italic(aurora.Blue(l)))
case o.Dropped:
lines[i] = fmt.Sprintf("%s\t%s\n", aurora.White(now), aurora.BgBlue(l))
default:
lines[i] = fmt.Sprintf("%s\t%s\n", aurora.White(now), aurora.Blue(l))
}
}
}
return lines, nil
}