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