def read_sent(self): """Read the first sent message in the queue""" lm, raw = ber_decode(self._incoming_queue.popleft(), asn1Spec=rfc4511.LDAPMessage()) if raw: raise Exception('unexpected leftover bits') return lm
def encode_decode(lm): """Simulate transmission over the network""" raw = ber_encode(lm) response, raw = ber_decode(raw, asn1Spec=rfc4511.LDAPMessage()) if raw: raise Exception('Unexpected leftover bits') return response
def test_unpack(self): message_id = 1 proto_op = 'compareResponse' test_lm = rfc4511.LDAPMessage() test_lm.setComponentByName('messageID', rfc4511.MessageID(message_id)) test_cr = rfc4511.CompareResponse() test_cr.setComponentByName('resultCode', protoutils.RESULT_compareTrue) test_cr.setComponentByName('matchedDN', rfc4511.LDAPDN('cn=testing,o=foo')) test_cr.setComponentByName('diagnosticMessage', rfc4511.LDAPString('')) test_po = rfc4511.ProtocolOp() test_po.setComponentByName(proto_op, test_cr) test_lm.setComponentByName('protocolOp', test_po) # simulate network transmission test_lm = encode_decode(test_lm) # ensure we successfully unpack the message ID and get back a compareResult actual_message_id, actual_cr, actual_controls = protoutils.unpack(proto_op, test_lm) self.assertEqual(actual_message_id, message_id) self.assertEqual(actual_cr.getComponentByName('resultCode'), protoutils.RESULT_compareTrue) # handling of optional controls varies by pyasn1 version # should either be None or length 0 if actual_controls is not None: self.assertEqual(len(actual_controls), 0) # ensure unpacking another type raises an exception with self.assertRaises(exceptions.UnexpectedResponseType): protoutils.unpack('bindResponse', test_lm)
def pack(message_id, op, controls=None): """Instantiate rfc4511.LDAPMessage using existing rfc4511.ProtocolOp and optional rfc4511.Controls""" lm = rfc4511.LDAPMessage() lm.setComponentByName('messageID', rfc4511.MessageID(int(message_id))) lm.setComponentByName('protocolOp', op) if controls: lm.setComponentByName('controls', controls) return lm
def recv_messages(self, want_message_id): while self._outgoing_queue: raw = self._outgoing_queue.popleft() lm, raw = ber_decode(raw, asn1Spec=rfc4511.LDAPMessage()) if raw: raise Exception('Unexpected leftover bits') have_message_id = lm.getComponentByName('messageID') if have_message_id != want_message_id: raise Exception( 'Unexpected message ID in mock queue (have={0} want={1})'. format(have_message_id, want_message_id)) yield lm raise Exception('No messages in mock queue')
async def run(self): """Handle the client's requests forever""" self.log.debug('Started new client') buffer = b'' while True: try: data = await self.reader.read(self.RECV_BUFFER) if not data: self.log.info('Client has exited') return buffer += data while len(buffer) > 0: _request, buffer = ber_decode( buffer, asn1Spec=rfc4511.LDAPMessage()) req = Request(_request) self.log.info( f'Received message_id={req.id} operation={req.operation}' ) if req.operation == 'unbindRequest': self.authenticated_name = None self.log.info('Client has unbound') return elif req.operation == 'abandonRequest': # As of right now I can't fathom a way to actually get abandon to work with asyncio # # Unfortunately RFC4511 specifies that interrupting result entries is the one thing we MUST # do, but it also says clients MUST NOT care if the abandon worked, so ... ? self.log.warning('Received abandon request - ignoring') else: await self._respond_to_request(req) except SubstrateUnderrunError: continue except (PyAsn1Error, DisconnectionProtocolError) as e: self.log.exception('Caught fatal disconnect error', e) xr = ldap_result(rfc4511.ExtendedResponse, 'protocolError', message=str(e)) xr.setComponentByName('responseName', constants.OID_NOTICE_OF_DISCONNECTION) op = protocol_op('extendedResp', xr) lm = pack(0, op) await self.send(lm) return