1#!/usr/bin/env python3
2#
3#   Copyright 2020 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import bluetooth_packets_python3 as bt_packets
18from bluetooth_packets_python3 import hci_packets
19from bluetooth_packets_python3.hci_packets import EventCode
20from bluetooth_packets_python3 import l2cap_packets
21from bluetooth_packets_python3.l2cap_packets import CommandCode, LeCommandCode
22from bluetooth_packets_python3.l2cap_packets import ConfigurationResponseResult
23from bluetooth_packets_python3.l2cap_packets import ConnectionResponseResult
24from bluetooth_packets_python3.l2cap_packets import InformationRequestInfoType
25from bluetooth_packets_python3.l2cap_packets import LeCreditBasedConnectionResponseResult
26
27
28class HciMatchers(object):
29
30    @staticmethod
31    def CommandComplete(opcode=None, num_complete=1):
32        return lambda msg: HciMatchers._is_matching_command_complete(msg.event, opcode, num_complete)
33
34    @staticmethod
35    def _is_matching_command_complete(packet_bytes, opcode=None, num_complete=1):
36        hci_event = HciMatchers.extract_hci_event_with_code(packet_bytes, EventCode.COMMAND_COMPLETE)
37        if hci_event is None:
38            return False
39        frame = hci_packets.CommandCompleteView(hci_event)
40        return (opcode is None or frame.GetCommandOpCode() == opcode) and\
41               frame.GetNumHciCommandPackets() == num_complete
42
43    @staticmethod
44    def EventWithCode(event_code):
45        return lambda msg: HciMatchers.extract_hci_event_with_code(msg.event, event_code)
46
47    @staticmethod
48    def extract_hci_event_with_code(packet_bytes, event_code=None):
49        hci_event = hci_packets.EventPacketView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))
50        if hci_event is None:
51            return None
52        if event_code is not None and hci_event.GetEventCode() != event_code:
53            return None
54        return hci_event
55
56    @staticmethod
57    def LinkKeyRequest():
58        return lambda event: HciMatchers.EventWithCode(EventCode.LINK_KEY_REQUEST)
59
60    @staticmethod
61    def IoCapabilityRequest():
62        return lambda event: HciMatchers.EventWithCode(EventCode.IO_CAPABILITY_REQUEST)
63
64    @staticmethod
65    def IoCapabilityResponse():
66        return lambda event: HciMatchers.EventWithCode(EventCode.IO_CAPABILITY_RESPONSE)
67
68    @staticmethod
69    def UserPasskeyNotification():
70        return lambda event: HciMatchers.EventWithCode(EventCode.USER_PASSKEY_NOTIFICATION)
71
72    @staticmethod
73    def UserPasskeyRequest():
74        return lambda event: HciMatchers.EventWithCode(EventCode.USER_PASSKEY_REQUEST)
75
76    @staticmethod
77    def UserConfirmationRequest():
78        return lambda event: HciMatchers.EventWithCode(EventCode.USER_CONFIRMATION_REQUEST)
79
80    @staticmethod
81    def RemoteHostSupportedFeaturesNotification():
82        return lambda event: HciMatchers.EventWithCode(EventCode.REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION)
83
84    @staticmethod
85    def LinkKeyNotification():
86        return lambda event: HciMatchers.EventWithCode(EventCode.LINK_KEY_NOTIFICATION)
87
88    @staticmethod
89    def SimplePairingComplete():
90        return lambda event: HciMatchers.EventWithCode(EventCode.SIMPLE_PAIRING_COMPLETE)
91
92    @staticmethod
93    def Disconnect():
94        return lambda event: HciMatchers.EventWithCode(EventCode.DISCONNECT)
95
96
97class NeighborMatchers(object):
98
99    @staticmethod
100    def InquiryResult(address):
101        return lambda msg: NeighborMatchers._is_matching_inquiry_result(msg.packet, address)
102
103    @staticmethod
104    def _is_matching_inquiry_result(packet, address):
105        hci_event = HciMatchers.extract_hci_event_with_code(packet, EventCode.INQUIRY_RESULT)
106        if hci_event is None:
107            return False
108        inquiry_view = hci_packets.InquiryResultView(hci_event)
109        if inquiry_view is None:
110            return False
111        results = inquiry_view.GetInquiryResults()
112        return any((address == result.bd_addr for result in results))
113
114    @staticmethod
115    def InquiryResultwithRssi(address):
116        return lambda msg: NeighborMatchers._is_matching_inquiry_result_with_rssi(msg.packet, address)
117
118    @staticmethod
119    def _is_matching_inquiry_result_with_rssi(packet, address):
120        hci_event = HciMatchers.extract_hci_event_with_code(packet, EventCode.INQUIRY_RESULT_WITH_RSSI)
121        if hci_event is None:
122            return False
123        inquiry_view = hci_packets.InquiryResultWithRssiView(hci_event)
124        if inquiry_view is None:
125            return False
126        results = inquiry_view.GetInquiryResults()
127        return any((address == result.address for result in results))
128
129    @staticmethod
130    def ExtendedInquiryResult(address):
131        return lambda msg: NeighborMatchers._is_matching_extended_inquiry_result(msg.packet, address)
132
133    @staticmethod
134    def _is_matching_extended_inquiry_result(packet, address):
135        hci_event = HciMatchers.extract_hci_event_with_code(packet, EventCode.EXTENDED_INQUIRY_RESULT)
136        if hci_event is None:
137            return False
138        extended_view = hci_packets.ExtendedInquiryResultView(hci_event)
139        if extended_view is None:
140            return False
141        return address == extended_view.GetAddress()
142
143
144class L2capMatchers(object):
145
146    @staticmethod
147    def ConnectionRequest(psm):
148        return lambda packet: L2capMatchers._is_matching_connection_request(packet, psm)
149
150    @staticmethod
151    def ConnectionResponse(scid):
152        return lambda packet: L2capMatchers._is_matching_connection_response(packet, scid)
153
154    @staticmethod
155    def ConfigurationResponse(result=ConfigurationResponseResult.SUCCESS):
156        return lambda packet: L2capMatchers._is_matching_configuration_response(packet, result)
157
158    @staticmethod
159    def ConfigurationRequest(cid=None):
160        return lambda packet: L2capMatchers._is_matching_configuration_request_with_cid(packet, cid)
161
162    @staticmethod
163    def ConfigurationRequestWithErtm():
164        return lambda packet: L2capMatchers._is_matching_configuration_request_with_ertm(packet)
165
166    @staticmethod
167    def ConfigurationRequestView(dcid):
168        return lambda request_view: request_view.GetDestinationCid() == dcid
169
170    @staticmethod
171    def DisconnectionRequest(scid, dcid):
172        return lambda packet: L2capMatchers._is_matching_disconnection_request(packet, scid, dcid)
173
174    @staticmethod
175    def DisconnectionResponse(scid, dcid):
176        return lambda packet: L2capMatchers._is_matching_disconnection_response(packet, scid, dcid)
177
178    @staticmethod
179    def EchoResponse():
180        return lambda packet: L2capMatchers._is_control_frame_with_code(packet, CommandCode.ECHO_RESPONSE)
181
182    @staticmethod
183    def CommandReject():
184        return lambda packet: L2capMatchers._is_control_frame_with_code(packet, CommandCode.COMMAND_REJECT)
185
186    @staticmethod
187    def LeCommandReject():
188        return lambda packet: L2capMatchers._is_le_control_frame_with_code(packet, LeCommandCode.COMMAND_REJECT)
189
190    @staticmethod
191    def LeConnectionParameterUpdateRequest():
192        return lambda packet: L2capMatchers._is_le_control_frame_with_code(
193            packet, LeCommandCode.CONNECTION_PARAMETER_UPDATE_REQUEST)
194
195    @staticmethod
196    def LeConnectionParameterUpdateResponse(result=l2cap_packets.ConnectionParameterUpdateResponseResult.ACCEPTED):
197        return lambda packet: L2capMatchers._is_matching_connection_parameter_update_response(packet, result)
198
199    @staticmethod
200    def CreditBasedConnectionRequest(psm):
201        return lambda packet: L2capMatchers._is_matching_credit_based_connection_request(packet, psm)
202
203    @staticmethod
204    def CreditBasedConnectionResponse(result=LeCreditBasedConnectionResponseResult.SUCCESS):
205        return lambda packet: L2capMatchers._is_matching_credit_based_connection_response(packet, result)
206
207    @staticmethod
208    def CreditBasedConnectionResponseUsedCid():
209        return lambda packet: L2capMatchers._is_matching_credit_based_connection_response(
210            packet, LeCreditBasedConnectionResponseResult.SOURCE_CID_ALREADY_ALLOCATED
211        ) or L2capMatchers._is_le_control_frame_with_code(packet, LeCommandCode.COMMAND_REJECT)
212
213    @staticmethod
214    def LeDisconnectionRequest(scid, dcid):
215        return lambda packet: L2capMatchers._is_matching_le_disconnection_request(packet, scid, dcid)
216
217    @staticmethod
218    def LeDisconnectionResponse(scid, dcid):
219        return lambda packet: L2capMatchers._is_matching_le_disconnection_response(packet, scid, dcid)
220
221    @staticmethod
222    def LeFlowControlCredit(cid):
223        return lambda packet: L2capMatchers._is_matching_le_flow_control_credit(packet, cid)
224
225    @staticmethod
226    def SFrame(req_seq=None, f=None, s=None, p=None):
227        return lambda packet: L2capMatchers._is_matching_supervisory_frame(packet, req_seq, f, s, p)
228
229    @staticmethod
230    def IFrame(tx_seq=None, payload=None, f=None):
231        return lambda packet: L2capMatchers._is_matching_information_frame(packet, tx_seq, payload, f, fcs=False)
232
233    @staticmethod
234    def IFrameWithFcs(tx_seq=None, payload=None, f=None):
235        return lambda packet: L2capMatchers._is_matching_information_frame(packet, tx_seq, payload, f, fcs=True)
236
237    @staticmethod
238    def IFrameStart(tx_seq=None, payload=None, f=None):
239        return lambda packet: L2capMatchers._is_matching_information_start_frame(packet, tx_seq, payload, f, fcs=False)
240
241    @staticmethod
242    def Data(payload):
243        return lambda packet: packet.GetPayload().GetBytes() == payload
244
245    @staticmethod
246    def FirstLeIFrame(payload, sdu_size):
247        return lambda packet: L2capMatchers._is_matching_first_le_i_frame(packet, payload, sdu_size)
248
249    # this is a hack - should be removed
250    @staticmethod
251    def PartialData(payload):
252        return lambda packet: payload in packet.GetPayload().GetBytes()
253
254    # this is a hack - should be removed
255    @staticmethod
256    def PacketPayloadRawData(payload):
257        return lambda packet: payload in packet.payload
258
259    # this is a hack - should be removed
260    @staticmethod
261    def PacketPayloadWithMatchingPsm(psm):
262        return lambda packet: None if psm != packet.psm else packet
263
264    # this is a hack - should be removed
265    @staticmethod
266    def PacketPayloadWithMatchingCid(cid):
267        return lambda packet: None if cid != packet.fixed_cid else packet
268
269    @staticmethod
270    def ExtractBasicFrame(scid):
271        return lambda packet: L2capMatchers._basic_frame_for(packet, scid)
272
273    @staticmethod
274    def ExtractBasicFrameWithFcs(scid):
275        return lambda packet: L2capMatchers._basic_frame_with_fcs_for(packet, scid)
276
277    @staticmethod
278    def InformationRequestWithType(info_type):
279        return lambda packet: L2capMatchers._information_request_with_type(packet, info_type)
280
281    @staticmethod
282    def InformationResponseExtendedFeatures(supports_ertm=None,
283                                            supports_streaming=None,
284                                            supports_fcs=None,
285                                            supports_fixed_channels=None):
286        return lambda packet: L2capMatchers._is_matching_information_response_extended_features(
287            packet, supports_ertm, supports_streaming, supports_fcs, supports_fixed_channels)
288
289    @staticmethod
290    def _basic_frame(packet):
291        if packet is None:
292            return None
293        return l2cap_packets.BasicFrameView(bt_packets.PacketViewLittleEndian(list(packet.payload)))
294
295    @staticmethod
296    def _basic_frame_with_fcs(packet):
297        if packet is None:
298            return None
299        return l2cap_packets.BasicFrameWithFcsView(bt_packets.PacketViewLittleEndian(list(packet.payload)))
300
301    @staticmethod
302    def _basic_frame_for(packet, scid):
303        frame = L2capMatchers._basic_frame(packet)
304        if frame.GetChannelId() != scid:
305            return None
306        return frame
307
308    @staticmethod
309    def _basic_frame_with_fcs_for(packet, scid):
310        frame = L2capMatchers._basic_frame(packet)
311        if frame.GetChannelId() != scid:
312            return None
313        frame = L2capMatchers._basic_frame_with_fcs(packet)
314        if frame is None:
315            return None
316        return frame
317
318    @staticmethod
319    def _information_frame(packet):
320        standard_frame = l2cap_packets.StandardFrameView(packet)
321        if standard_frame.GetFrameType() != l2cap_packets.FrameType.I_FRAME:
322            return None
323        return l2cap_packets.EnhancedInformationFrameView(standard_frame)
324
325    @staticmethod
326    def _information_frame_with_fcs(packet):
327        standard_frame = l2cap_packets.StandardFrameWithFcsView(packet)
328        if standard_frame is None:
329            return None
330        if standard_frame.GetFrameType() != l2cap_packets.FrameType.I_FRAME:
331            return None
332        return l2cap_packets.EnhancedInformationFrameWithFcsView(standard_frame)
333
334    @staticmethod
335    def _information_start_frame(packet):
336        start_frame = L2capMatchers._information_frame(packet)
337        if start_frame is None:
338            return None
339        return l2cap_packets.EnhancedInformationStartFrameView(start_frame)
340
341    @staticmethod
342    def _information_start_frame_with_fcs(packet):
343        start_frame = L2capMatchers._information_frame_with_fcs(packet)
344        if start_frame is None:
345            return None
346        return l2cap_packets.EnhancedInformationStartFrameWithFcsView(start_frame)
347
348    @staticmethod
349    def _supervisory_frame(packet):
350        standard_frame = l2cap_packets.StandardFrameView(packet)
351        if standard_frame.GetFrameType() != l2cap_packets.FrameType.S_FRAME:
352            return None
353        return l2cap_packets.EnhancedSupervisoryFrameView(standard_frame)
354
355    @staticmethod
356    def _is_matching_information_frame(packet, tx_seq, payload, f, fcs=False):
357        if fcs:
358            frame = L2capMatchers._information_frame_with_fcs(packet)
359        else:
360            frame = L2capMatchers._information_frame(packet)
361        if frame is None:
362            return False
363        if tx_seq is not None and frame.GetTxSeq() != tx_seq:
364            return False
365        if payload is not None and frame.GetPayload().GetBytes() != payload:
366            return False
367        if f is not None and frame.GetF() != f:
368            return False
369        return True
370
371    @staticmethod
372    def _is_matching_information_start_frame(packet, tx_seq, payload, f, fcs=False):
373        if fcs:
374            frame = L2capMatchers._information_start_frame_with_fcs(packet)
375        else:
376            frame = L2capMatchers._information_start_frame(packet)
377        if frame is None:
378            return False
379        if tx_seq is not None and frame.GetTxSeq() != tx_seq:
380            return False
381        if payload is not None and frame.GetPayload().GetBytes() != payload:
382            return False
383        if f is not None and frame.GetF() != f:
384            return False
385        return True
386
387    @staticmethod
388    def _is_matching_supervisory_frame(packet, req_seq, f, s, p):
389        frame = L2capMatchers._supervisory_frame(packet)
390        if frame is None:
391            return False
392        if req_seq is not None and frame.GetReqSeq() != req_seq:
393            return False
394        if f is not None and frame.GetF() != f:
395            return False
396        if s is not None and frame.GetS() != s:
397            return False
398        if p is not None and frame.GetP() != p:
399            return False
400        return True
401
402    @staticmethod
403    def _is_matching_first_le_i_frame(packet, payload, sdu_size):
404        first_le_i_frame = l2cap_packets.FirstLeInformationFrameView(packet)
405        return first_le_i_frame.GetPayload().GetBytes() == payload and first_le_i_frame.GetL2capSduLength() == sdu_size
406
407    @staticmethod
408    def _control_frame(packet):
409        if packet.GetChannelId() != 1:
410            return None
411        return l2cap_packets.ControlView(packet.GetPayload())
412
413    @staticmethod
414    def _le_control_frame(packet):
415        if packet.GetChannelId() != 5:
416            return None
417        return l2cap_packets.LeControlView(packet.GetPayload())
418
419    @staticmethod
420    def control_frame_with_code(packet, code):
421        frame = L2capMatchers._control_frame(packet)
422        if frame is None or frame.GetCode() != code:
423            return None
424        return frame
425
426    @staticmethod
427    def le_control_frame_with_code(packet, code):
428        frame = L2capMatchers._le_control_frame(packet)
429        if frame is None or frame.GetCode() != code:
430            return None
431        return frame
432
433    @staticmethod
434    def _is_control_frame_with_code(packet, code):
435        return L2capMatchers.control_frame_with_code(packet, code) is not None
436
437    @staticmethod
438    def _is_le_control_frame_with_code(packet, code):
439        return L2capMatchers.le_control_frame_with_code(packet, code) is not None
440
441    @staticmethod
442    def _is_matching_connection_request(packet, psm):
443        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONNECTION_REQUEST)
444        if frame is None:
445            return False
446        request = l2cap_packets.ConnectionRequestView(frame)
447        return request.GetPsm() == psm
448
449    @staticmethod
450    def _is_matching_connection_response(packet, scid):
451        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONNECTION_RESPONSE)
452        if frame is None:
453            return False
454        response = l2cap_packets.ConnectionResponseView(frame)
455        return response.GetSourceCid() == scid and response.GetResult(
456        ) == ConnectionResponseResult.SUCCESS and response.GetDestinationCid() != 0
457
458    @staticmethod
459    def _is_matching_configuration_request_with_cid(packet, cid=None):
460        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONFIGURATION_REQUEST)
461        if frame is None:
462            return False
463        request = l2cap_packets.ConfigurationRequestView(frame)
464        dcid = request.GetDestinationCid()
465        return cid is None or cid == dcid
466
467    @staticmethod
468    def _is_matching_configuration_request_with_ertm(packet):
469        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONFIGURATION_REQUEST)
470        if frame is None:
471            return False
472        request = l2cap_packets.ConfigurationRequestView(frame)
473        config_bytes = request.GetBytes()
474        # TODO(b/153189503): Use packet struct parser.
475        return b"\x04\x09\x03" in config_bytes
476
477    @staticmethod
478    def _is_matching_configuration_response(packet, result=ConfigurationResponseResult.SUCCESS):
479        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONFIGURATION_RESPONSE)
480        if frame is None:
481            return False
482        response = l2cap_packets.ConfigurationResponseView(frame)
483        return response.GetResult() == result
484
485    @staticmethod
486    def _is_matching_disconnection_request(packet, scid, dcid):
487        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.DISCONNECTION_REQUEST)
488        if frame is None:
489            return False
490        request = l2cap_packets.DisconnectionRequestView(frame)
491        return request.GetSourceCid() == scid and request.GetDestinationCid() == dcid
492
493    @staticmethod
494    def _is_matching_disconnection_response(packet, scid, dcid):
495        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.DISCONNECTION_RESPONSE)
496        if frame is None:
497            return False
498        response = l2cap_packets.DisconnectionResponseView(frame)
499        return response.GetSourceCid() == scid and response.GetDestinationCid() == dcid
500
501    @staticmethod
502    def _is_matching_le_disconnection_response(packet, scid, dcid):
503        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.DISCONNECTION_RESPONSE)
504        if frame is None:
505            return False
506        response = l2cap_packets.LeDisconnectionResponseView(frame)
507        return response.GetSourceCid() == scid and response.GetDestinationCid() == dcid
508
509    @staticmethod
510    def _is_matching_le_disconnection_request(packet, scid, dcid):
511        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.DISCONNECTION_REQUEST)
512        if frame is None:
513            return False
514        request = l2cap_packets.LeDisconnectionRequestView(frame)
515        return request.GetSourceCid() == scid and request.GetDestinationCid() == dcid
516
517    @staticmethod
518    def _is_matching_le_flow_control_credit(packet, cid):
519        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_FLOW_CONTROL_CREDIT)
520        if frame is None:
521            return False
522        request = l2cap_packets.LeFlowControlCreditView(frame)
523        return request.GetCid() == cid
524
525    @staticmethod
526    def _information_request_with_type(packet, info_type):
527        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.INFORMATION_REQUEST)
528        if frame is None:
529            return None
530        request = l2cap_packets.InformationRequestView(frame)
531        if request.GetInfoType() != info_type:
532            return None
533        return request
534
535    @staticmethod
536    def _information_response_with_type(packet, info_type):
537        frame = L2capMatchers.control_frame_with_code(packet, CommandCode.INFORMATION_RESPONSE)
538        if frame is None:
539            return None
540        response = l2cap_packets.InformationResponseView(frame)
541        if response.GetInfoType() != info_type:
542            return None
543        return response
544
545    @staticmethod
546    def _is_matching_information_response_extended_features(packet, supports_ertm, supports_streaming, supports_fcs,
547                                                            supports_fixed_channels):
548        frame = L2capMatchers._information_response_with_type(packet,
549                                                              InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED)
550        if frame is None:
551            return False
552        features = l2cap_packets.InformationResponseExtendedFeaturesView(frame)
553        if supports_ertm is not None and features.GetEnhancedRetransmissionMode() != supports_ertm:
554            return False
555        if supports_streaming is not None and features.GetStreamingMode != supports_streaming:
556            return False
557        if supports_fcs is not None and features.GetFcsOption() != supports_fcs:
558            return False
559        if supports_fixed_channels is not None and features.GetFixedChannels() != supports_fixed_channels:
560            return False
561        return True
562
563    @staticmethod
564    def _is_matching_connection_parameter_update_response(packet, result):
565        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.CONNECTION_PARAMETER_UPDATE_RESPONSE)
566        if frame is None:
567            return False
568        return l2cap_packets.ConnectionParameterUpdateResponseView(frame).GetResult() == result
569
570    @staticmethod
571    def _is_matching_credit_based_connection_request(packet, psm):
572        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_REQUEST)
573        if frame is None:
574            return False
575        request = l2cap_packets.LeCreditBasedConnectionRequestView(frame)
576        return request.GetLePsm() == psm
577
578    @staticmethod
579    def _is_matching_credit_based_connection_response(packet, result):
580        frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_RESPONSE)
581        if frame is None:
582            return False
583        response = l2cap_packets.LeCreditBasedConnectionResponseView(frame)
584        return response.GetResult() == result and (result != LeCreditBasedConnectionResponseResult.SUCCESS or
585                                                   response.GetDestinationCid() != 0)
586
587
588class SecurityMatchers(object):
589
590    @staticmethod
591    def UiMsg(type):
592        return lambda event: True if event.message_type == type else False
593
594    @staticmethod
595    def BondMsg(type):
596        return lambda event: True if event.message_type == type else False
597