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 _receive(self, timestamp, sender, bytes): # 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(bytes))] output = '\n'.join(output) log.debug(output) srcIp = sender[0] srcIp = u.trimAddress(srcIp) srcPort = sender[1] # parse messages try: message = m.parseMessage(bytes) except e.messageFormatError as err: log.warning('malformed message {0}: {1}'.format( u.formatBuf(bytes), str(err))) return # dispatch message try: if message['code'] in d.METHOD_ALL: # this is meant for a resource #==== find right resource # retrieve path path = coapUri.options2path(message['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() #==== get a response # call the right resource's method try: if message['code'] == d.METHOD_GET: (respCode, respOptions, respPayload) = resource.GET( options=message['options']) elif message['code'] == d.METHOD_POST: (respCode, respOptions, respPayload) = resource.POST( options=message['options'], payload=message['payload']) elif message['code'] == d.METHOD_PUT: (respCode, respOptions, respPayload) = resource.PUT( options=message['options'], payload=message['payload']) elif message['code'] == d.METHOD_DELETE: (respCode, respOptions, respPayload) = resource.DELETE( options=message['options']) else: raise SystemError('unexpected code {0}'.format( message['code'])) 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'])) # build response packets response = m.buildMessage( type=responseType, token=message['token'], code=respCode, messageId=message['messageId'], options=respOptions, payload=respPayload, ) # send self.socketUdp.sendUdp( destIp=srcIp, destPort=srcPort, msg=response, ) elif message['code'] in d.COAP_RC_ALL: # this is meant for a transmitter # 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() else: raise NotImplementedError() except e.coapRc as 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'])) # build response packets response = m.buildMessage( type=responseType, token=message['token'], code=err.rc, messageId=message['messageId'], ) # send self.socketUdp.sendUdp( destIp=srcIp, destPort=srcPort, msg=response, )
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 to short, {0} bytes: not space for 1B optionDelta'.format(len(message))) optionDelta = u.buf2int(message[0])+13 message = message[1:] elif optionDelta==14: if len(message)<2: raise e.messageFormatError('message to short, {0} bytes: not space for 2B optionDelta'.format(len(message))) optionDelta = u.buf2int(message[0:1])+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 to short, {0} bytes: not space for 1B optionLength'.format(len(message))) optionLength = u.buf2int(message[0])+13 message = message[1:] elif optionLength==14: if len(message)<2: raise e.messageFormatError('message to short, {0} bytes: not space for 2B optionLength'.format(len(message))) optionLength = u.buf2int(message[0:1])+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 to short, {0} bytes: not 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_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) else: raise NotImplementedError('option {0} not implemented'.format(optionNumber)) return (option,message)
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] options = [] # parse messages try: message = m.parseMessage(rawbytes) 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 = message['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)
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) 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 #==== find right resource # retrieve path path = coapUri.options2path(message['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() #==== get a response # call the right resource's method try: if message['code']==d.METHOD_GET: (respCode,respOptions,respPayload) = resource.GET( options=message['options'] ) elif message['code']==d.METHOD_POST: (respCode,respOptions,respPayload) = resource.POST( options=message['options'], payload=message['payload'] ) elif message['code']==d.METHOD_PUT: (respCode,respOptions,respPayload) = resource.PUT( options=message['options'], payload=message['payload'] ) elif message['code']==d.METHOD_DELETE: (respCode,respOptions,respPayload) = resource.DELETE( options=message['options'] ) else: raise SystemError('unexpected code {0}'.format(message['code'])) 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'])) # build response packets response = m.buildMessage( msgtype = responseType, token = message['token'], code = respCode, messageId = message['messageId'], options = respOptions, payload = respPayload, ) # send self.socketUdp.sendUdp( destIp = srcIp, destPort = srcPort, msg = response, ) elif message['code'] in d.COAP_RC_ALL: # this is meant for a transmitter # 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'])) # build response packets response = m.buildMessage( msgtype = responseType, token = message['token'], code = err.rc, messageId = message['messageId'], ) # 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 to short, {0} bytes: not space for 1B optionDelta'. format(len(message))) optionDelta = u.buf2int(messsage[0]) + 13 message = message[1:] elif optionDelta == 14: if len(message) < 2: raise e.messageFormatError( 'message to short, {0} bytes: not space for 2B optionDelta'. format(len(message))) optionDelta = u.buf2int(messsage[0:1]) + 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 to short, {0} bytes: not space for 1B optionLength'. format(len(message))) optionLength = u.buf2int(messsage[0]) + 13 message = message[1:] elif optionLength == 14: if len(message) < 2: raise e.messageFormatError( 'message to short, {0} bytes: not space for 2B optionLength'. format(len(message))) optionLength = u.buf2int(messsage[0:1]) + 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 to short, {0} bytes: not 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 if optionNumber not in d.OPTION_NUM_ALL: raise e.messageFormatError( 'invalid option number {0}'.format(optionNumber)) if optionNumber == d.OPTION_NUM_URIPATH: option = UriPath(path=''.join([chr(b) for b in optionValue])) else: raise NotImplementedError() return (option, message)