def test_ipv6AddrString2Bytes(logFixture, validIpv6AddrStr2Bytes): (ipv6String, ipv6Bytes) = validIpv6AddrStr2Bytes ipv6Bytes = list(ipv6Bytes) log.debug('ipv6String: {0}'.format(ipv6String)) log.debug('ipv6Bytes: {0}'.format(coapUtils.formatBuf(ipv6Bytes))) result = coapUtils.ipv6AddrString2Bytes(ipv6String) log.debug('result: {0}'.format(coapUtils.formatBuf(result))) assert result == ipv6Bytes
def test_ipv6AddrString2Bytes(logFixture, validIpv6AddrStr2Bytes): (ipv6String,ipv6Bytes) = validIpv6AddrStr2Bytes ipv6Bytes = list(ipv6Bytes) log.debug('ipv6String: {0}'.format(ipv6String)) log.debug('ipv6Bytes: {0}'.format(coapUtils.formatBuf(ipv6Bytes))) result = coapUtils.ipv6AddrString2Bytes(ipv6String) log.debug('result: {0}'.format(coapUtils.formatBuf(result))) assert result==ipv6Bytes
def test_buildMessage(logFixture, messageAndBytes): (msg,bytes) = messageAndBytes log.debug('msg: {0}'.format(msg)) log.debug('bytes: {0}'.format(u.formatBuf(bytes))) result = m.buildMessage( type = msg[0], token = msg[1], code = msg[2], messageId = msg[3], options = msg[4], payload = list(msg[5]), ) log.debug('result: {0}'.format(u.formatBuf(result))) assert tuple(result)==bytes
def test_buildMessage(logFixture, messageAndBytes): (msg, bytes) = messageAndBytes log.debug('msg: {0}'.format(msg)) log.debug('bytes: {0}'.format(u.formatBuf(bytes))) result = m.buildMessage( msgtype=msg[0], token=msg[1], code=msg[2], messageId=msg[3], options=msg[4], payload=list(msg[5]), ) log.debug('result: {0}'.format(u.formatBuf(result))) assert tuple(result) == bytes
def _messageNotification(self, signal, sender, data): # get reception time timestamp = time.time() # log log.debug("got {2} from {1} at {0}".format(timestamp, sender, u.formatBuf(data))) # call the callback self.callback(timestamp, sender, data) # update stats self._incrementRx() # release the lock self.gotMsgSem.release()
def test_parseMessage(logFixture, messageAndBytes): (msg,bytes) = messageAndBytes bytes = list(bytes) log.debug('bytes: {0}'.format(u.formatBuf(bytes))) log.debug('msg: {0}'.format(msg)) result = m.parseMessage(bytes) log.debug('result: {0}'.format(result)) assert result['type'] == msg[0] assert result['token'] == msg[1] assert result['code'] == msg[2] assert result['messageId'] == msg[3] assert len(result['options']) == len(msg[4]) for (o1,o2) in zip(result['options'],msg[4]): assert repr(o1)==repr(o2) assert result['payload'] == list(msg[5])
def test_parseMessage(logFixture, messageAndBytes): (msg, bytes) = messageAndBytes bytes = list(bytes) log.debug('bytes: {0}'.format(u.formatBuf(bytes))) log.debug('msg: {0}'.format(msg)) result = m.parseMessage(bytes) log.debug('result: {0}'.format(result)) assert result['type'] == msg[0] assert result['token'] == msg[1] assert result['code'] == msg[2] assert result['messageId'] == msg[3] assert len(result['options']) == len(msg[4]) for (o1, o2) in zip(result['options'], msg[4]): assert repr(o1) == repr(o2) assert result['payload'] == list(msg[5])
def _messageNotification(self, signal, sender, data): timestamp = time.time() # log log.debug("{0}:{1}->{2}:{3}: {4}".format( sender[0], sender[1], signal[0], signal[1], u.formatBuf(data), )) srcIp = u.ipv6AddrString2Bytes(sender[0]) srcPort = sender[1] destIp = u.ipv6AddrString2Bytes(signal[0]) destPort = signal[1] # log in pcap self._writePcapMessage(timestamp, srcIp, destIp, srcPort, destPort, data) # release the lock self.gotMsgSem.release()
def _messageNotification(self,signal,sender,data): timestamp = time.time() # log log.debug("{0}:{1}->{2}:{3}: {4}".format( sender[0], sender[1], signal[0], signal[1], u.formatBuf(data), ) ) srcIp = u.ipv6AddrString2Bytes(sender[0]) srcPort = sender[1] destIp = u.ipv6AddrString2Bytes(signal[0]) destPort = signal[1] # log in pcap self._writePcapMessage(timestamp,srcIp,destIp,srcPort,destPort,data) # release the lock self.gotMsgSem.release()
def _receive(self, timestamp, sender, rawbytes): # all UDP packets are received here output = [] output += ['\n{0} _receive message:'.format(self.name)] output += ['- timestamp: {0}'.format(timestamp)] output += ['- sender: {0}'.format(sender)] output += ['- bytes: {0}'.format(u.formatBuf(rawbytes))] output = '\n'.join(output) log.debug(output) srcIp = sender[0] srcIp = u.trimAddress(srcIp) srcPort = sender[1] # parse messages try: message = m.parseMessage(rawbytes) options = message['options'] except e.messageFormatError as err: log.warning('malformed message {0}: {1}'.format( u.formatBuf(rawbytes), str(err))) return # dispatch message try: if message['code'] in d.METHOD_ALL: # this is meant for a resource (request) #==== decrypt message if encrypted innerOptions = [] foundContext = None requestPartialIV = None if 'ciphertext' in message.keys(): # retrieve security context # before decrypting we don't know what resource this request is meant for # so we take the first binding with the correct context (recipientID) blindContext = self._securityContextLookup( u.buf2str(message['kid'])) if not blindContext: if self.secContextHandler: appContext = self.secContextHandler( u.buf2str(message['kid'])) if not appContext: raise e.coapRcUnauthorized( 'Security context not found.') else: raise e.coapRcUnauthorized( 'Security context not found.') foundContext = blindContext if blindContext != None else appContext requestPartialIV = u.zeroPadString( u.buf2str(message['partialIV']), foundContext.getIVLength()) # decrypt the message try: (innerOptions, plaintext) = oscoap.unprotectMessage( foundContext, version=message['version'], code=message['code'], options=message['options'], ciphertext=message['ciphertext'], partialIV=requestPartialIV) except e.oscoapError as err: raise e.coapRcBadRequest( 'OSCOAP unprotect failed: {0}'.format(str(err))) payload = plaintext else: # message not encrypted payload = message['payload'] options += innerOptions #==== find right resource # retrieve path path = coapUri.options2path(options) log.debug('path="{0}"'.format(path)) # find resource that matches this path resource = None with self.resourceLock: for r in self.resources: if r.matchesPath(path): resource = r break log.debug('resource={0}'.format(resource)) if not resource: raise e.coapRcNotFound() #==== check if appropriate security context was used for the resource (context, authorizedMethods) = resource.getSecurityBinding() if context is not None: if context != foundContext: raise e.coapRcUnauthorized( 'Unauthorized security context for the given resource' ) objectSecurity = oscoap.objectSecurityOptionLookUp(options) if objectSecurity: objectSecurity.setContext(foundContext) #==== get a response # call the right resource's method try: if message[ 'code'] == d.METHOD_GET and d.METHOD_GET in authorizedMethods: (respCode, respOptions, respPayload) = resource.GET(options=options) elif message[ 'code'] == d.METHOD_POST and d.METHOD_POST in authorizedMethods: (respCode, respOptions, respPayload) = resource.POST(options=options, payload=payload) elif message[ 'code'] == d.METHOD_PUT and d.METHOD_PUT in authorizedMethods: (respCode, respOptions, respPayload) = resource.PUT(options=options, payload=payload) elif message[ 'code'] == d.METHOD_DELETE and d.METHOD_DELETE in authorizedMethods: (respCode, respOptions, respPayload) = resource.DELETE(options=options) elif message['code'] not in d.METHOD_ALL: raise SystemError('unexpected code {0}'.format( message['code'])) else: raise e.coapRcUnauthorized( 'Unauthorized method for the given resource') except Exception as err: if isinstance(err, e.coapRc): raise else: raise e.coapRcInternalServerError() #==== send back response # determine type of response packet if message['type'] == d.TYPE_CON: responseType = d.TYPE_ACK elif message['type'] == d.TYPE_NON: responseType = d.TYPE_NON else: raise SystemError('unexpected type {0}'.format( message['type'])) # if resource is protected with a security context, add Object-Security option if foundContext: # verify that the Object-Security option was not set by the resource handler assert not any( isinstance(option, o.ObjectSecurity) for option in respOptions) objectSecurity = o.ObjectSecurity(context=foundContext) respOptions += [objectSecurity] # if Stateless-Proxy option was present in the request echo it for option in options: if isinstance(option, o.StatelessProxy): respOptions += [option] break # build response packets and pass partialIV from the request for OSCOAP's processing response = m.buildMessage(msgtype=responseType, token=message['token'], code=respCode, messageId=message['messageId'], options=respOptions, payload=respPayload, securityContext=foundContext, partialIV=requestPartialIV) # send self.socketUdp.sendUdp( destIp=srcIp, destPort=srcPort, msg=response, ) elif message['code'] in d.COAP_RC_ALL: # this is meant for a transmitter (response) # find transmitter msgkey = (srcIp, srcPort, message['token'], message['messageId']) found = False with self.transmittersLock: self._cleanupTransmitter() for (k, v) in self.transmitters.items(): # try matching if (msgkey[0] == k[0] and msgkey[1] == k[1] and (msgkey[2] == k[2] or msgkey[3] == k[3])): found = True v.receiveMessage(timestamp, srcIp, srcPort, message) break if found == False: raise e.coapRcBadRequest( 'could not find transmitter corresponding to {0}, transmitters are {1}' .format( msgkey, ','.join( [str(k) for k in self.transmitters.keys()]))) else: raise NotImplementedError() except e.coapRc as err: # log log.warning(err) # determine type of response packet if message['type'] == d.TYPE_CON: responseType = d.TYPE_ACK elif message['type'] == d.TYPE_NON: responseType = d.TYPE_NON else: raise SystemError('unexpected type {0}'.format( message['type'])) # if Stateless-Proxy option was present in the request echo it errorOptions = [] for option in options: if isinstance(option, o.StatelessProxy): errorOptions += [option] break # build response packets response = m.buildMessage( msgtype=responseType, token=message['token'], code=err.rc, messageId=message['messageId'], options=errorOptions, ) # send self.socketUdp.sendUdp( destIp=srcIp, destPort=srcPort, msg=response, ) except Exception as err: log.critical(traceback.format_exc())
def parseOption(message, previousOptionNumber): ''' \brief Extract an option from the beginning of a message. \param[in] message A list of bytes. \param[in] previousOptionNumber The option number from the previous option in the message; set to 0 if this is the first option. \return A tuple with the following elements: - element 0 is the option that was extracted. If no option was found (end of the options or end of the packet), None is returned. - element 1 is the message without the option. ''' log.debug('parseOption message={0} previousOptionNumber={1}'.format( u.formatBuf(message), previousOptionNumber, )) #==== detect end of packet if len(message) == 0: message = message[1:] return (None, message) #==== detect payload marker if message[0] == d.COAP_PAYLOAD_MARKER: message = message[1:] return (None, message) #==== parse option # header optionDelta = (message[0] >> 4) & 0x0f optionLength = (message[0] >> 0) & 0x0f message = message[1:] # optionDelta if optionDelta <= 12: pass elif optionDelta == 13: if len(message) < 1: raise e.messageFormatError( 'message too short, {0} bytes: no space for 1B optionDelta'. format(len(message))) optionDelta = u.buf2int(message[0:1]) + 13 message = message[1:] elif optionDelta == 14: if len(message) < 2: raise e.messageFormatError( 'message too short, {0} bytes: no space for 2B optionDelta'. format(len(message))) optionDelta = u.buf2int(message[0:2]) + 269 message = message[2:] else: raise e.messageFormatError( 'invalid optionDelta={0}'.format(optionDelta)) log.debug('optionDelta = {0}'.format(optionDelta)) # optionLength if optionLength <= 12: pass elif optionLength == 13: if len(message) < 1: raise e.messageFormatError( 'message too short, {0} bytes: no space for 1B optionLength'. format(len(message))) optionLength = u.buf2int(message[0:1]) + 13 message = message[1:] elif optionLength == 14: if len(message) < 2: raise e.messageFormatError( 'message too short, {0} bytes: no space for 2B optionLength'. format(len(message))) optionLength = u.buf2int(message[0:2]) + 269 message = message[2:] else: raise e.messageFormatError( 'invalid optionLength={0}'.format(optionLength)) log.debug('optionLength = {0}'.format(optionLength)) # optionValue if len(message) < optionLength: raise e.messageFormatError( 'message too short, {0} bytes: no space for optionValue'.format( len(message))) optionValue = message[:optionLength] message = message[optionLength:] log.debug('optionValue = {0}'.format(u.formatBuf(optionValue))) #===== create option optionNumber = previousOptionNumber + optionDelta log.debug('optionNumber = {0}'.format(optionNumber)) if optionNumber not in d.OPTION_NUM_ALL: raise e.messageFormatError( 'invalid option number {0} (0x{0:x})'.format(optionNumber)) if optionNumber == d.OPTION_NUM_URIHOST: option = UriHost(host=''.join([chr(b) for b in optionValue])) elif optionNumber == d.OPTION_NUM_URIPATH: option = UriPath(path=''.join([chr(b) for b in optionValue])) elif optionNumber == d.OPTION_NUM_CONTENTFORMAT: option = ContentFormat(cformat=optionValue) elif optionNumber == d.OPTION_NUM_BLOCK2: option = Block2(rawbytes=optionValue) elif optionNumber == d.OPTION_NUM_OBJECT_SECURITY: option = ObjectSecurity(payload=optionValue) elif optionNumber == d.OPTION_NUM_PROXYSCHEME: option = ProxyScheme(scheme=''.join([chr(b) for b in optionValue])) elif optionNumber == d.OPTION_NUM_STATELESSPROXY: option = StatelessProxy(value=optionValue) else: raise NotImplementedError( 'option {0} not implemented'.format(optionNumber)) return (option, message)