From 2eafa64308040e1f28f683c1e881463dbe7fac88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Freise?= Date: Tue, 23 Apr 2019 11:43:02 +0200 Subject: [PATCH] Token stored as bytes and not limited to ascii chars anymore --- coapthon/layers/blocklayer.py | 14 ++++++----- coapthon/layers/messagelayer.py | 41 ++++++++++++++------------------- coapthon/layers/observelayer.py | 18 ++++++++------- coapthon/messages/message.py | 26 +++++++++++++-------- coapthon/serializer.py | 13 ++++------- coapthon/utils.py | 17 +++++++++++++- coverage_test.py | 9 +++++++- 7 files changed, 81 insertions(+), 57 deletions(-) diff --git a/coapthon/layers/blocklayer.py b/coapthon/layers/blocklayer.py index 28f2ffa..dd96142 100644 --- a/coapthon/layers/blocklayer.py +++ b/coapthon/layers/blocklayer.py @@ -1,5 +1,7 @@ import logging + from coapthon import defines +from coapthon import utils from coapthon.messages.request import Request from coapthon.messages.response import Response @@ -49,7 +51,7 @@ class BlockLayer(object): """ if transaction.request.block2 is not None: host, port = transaction.request.source - key_token = hash(str(host) + str(port) + str(transaction.request.token)) + key_token = utils.str_append_hash(host, port, transaction.request.token) num, m, size = transaction.request.block2 if key_token in self._block2_receive: self._block2_receive[key_token].num = num @@ -65,7 +67,7 @@ class BlockLayer(object): elif transaction.request.block1 is not None: # POST or PUT host, port = transaction.request.source - key_token = hash(str(host) + str(port) + str(transaction.request.token)) + key_token = utils.str_append_hash(host, port, transaction.request.token) num, m, size = transaction.request.block1 if transaction.request.size1 is not None: # What to do if the size1 is larger than the maximum resource size or the maxium server buffer @@ -122,7 +124,7 @@ class BlockLayer(object): :return: the edited transaction """ host, port = transaction.response.source - key_token = hash(str(host) + str(port) + str(transaction.response.token)) + key_token = utils.str_append_hash(host, port, transaction.response.token) if key_token in self._block1_sent and transaction.response.block1 is not None: item = self._block1_sent[key_token] transaction.block_transfer = True @@ -214,7 +216,7 @@ class BlockLayer(object): :return: the edited transaction """ host, port = transaction.request.source - key_token = hash(str(host) + str(port) + str(transaction.request.token)) + key_token = utils.str_append_hash(host, port, transaction.request.token) if (key_token in self._block2_receive and transaction.response.payload is not None) or \ (transaction.response.payload is not None and len(transaction.response.payload) > defines.MAX_PAYLOAD): if key_token in self._block2_receive: @@ -260,7 +262,7 @@ class BlockLayer(object): assert isinstance(request, Request) if request.block1 or (request.payload is not None and len(request.payload) > defines.MAX_PAYLOAD): host, port = request.destination - key_token = hash(str(host) + str(port) + str(request.token)) + key_token = utils.str_append_hash(host, port, request.token) if request.block1: num, m, size = request.block1 else: @@ -277,7 +279,7 @@ class BlockLayer(object): request.block1 = (num, m, size) elif request.block2: host, port = request.destination - key_token = hash(str(host) + str(port) + str(request.token)) + key_token = utils.str_append_hash(host, port, request.token) num, m, size = request.block2 item = BlockItem(size, num, m, size, "", None) self._block2_sent[key_token] = item diff --git a/coapthon/layers/messagelayer.py b/coapthon/layers/messagelayer.py index a1b72cc..c46ea96 100644 --- a/coapthon/layers/messagelayer.py +++ b/coapthon/layers/messagelayer.py @@ -2,6 +2,8 @@ import logging import random import time import socket + +from coapthon import utils from coapthon.messages.message import Message from coapthon import defines from coapthon.messages.request import Request @@ -12,15 +14,6 @@ __author__ = 'Giacomo Tanganelli' logger = logging.getLogger(__name__) -def str_append_hash(*args): - """ Convert each argument to a lower case string, appended, then hash """ - ret_hash = "" - for i in args: - ret_hash += str(i).lower() - - return hash(ret_hash) - - class MessageLayer(object): """ Handles matching between messages (Message ID) and request/response (Token) @@ -77,8 +70,8 @@ class MessageLayer(object): host, port = request.source except AttributeError: return - key_mid = str_append_hash(host, port, request.mid) - key_token = str_append_hash(host, port, request.token) + key_mid = utils.str_append_hash(host, port, request.mid) + key_token = utils.str_append_hash(host, port, request.token) if key_mid in list(self._transactions.keys()): # Duplicated @@ -107,10 +100,10 @@ class MessageLayer(object): except AttributeError: return all_coap_nodes = defines.ALL_COAP_NODES_IPV6 if socket.getaddrinfo(host, None)[0][0] == socket.AF_INET6 else defines.ALL_COAP_NODES - key_mid = str_append_hash(host, port, response.mid) - key_mid_multicast = str_append_hash(all_coap_nodes, port, response.mid) - key_token = str_append_hash(host, port, response.token) - key_token_multicast = str_append_hash(all_coap_nodes, port, response.token) + key_mid = utils.str_append_hash(host, port, response.mid) + key_mid_multicast = utils.str_append_hash(all_coap_nodes, port, response.mid) + key_token = utils.str_append_hash(host, port, response.token) + key_token_multicast = utils.str_append_hash(all_coap_nodes, port, response.token) if key_mid in list(self._transactions.keys()): transaction = self._transactions[key_mid] if response.token != transaction.request.token: @@ -154,10 +147,10 @@ class MessageLayer(object): except AttributeError: return all_coap_nodes = defines.ALL_COAP_NODES_IPV6 if socket.getaddrinfo(host, None)[0][0] == socket.AF_INET6 else defines.ALL_COAP_NODES - key_mid = str_append_hash(host, port, message.mid) - key_mid_multicast = str_append_hash(all_coap_nodes, port, message.mid) - key_token = str_append_hash(host, port, message.token) - key_token_multicast = str_append_hash(all_coap_nodes, port, message.token) + key_mid = utils.str_append_hash(host, port, message.mid) + key_mid_multicast = utils.str_append_hash(all_coap_nodes, port, message.mid) + key_token = utils.str_append_hash(host, port, message.token) + key_token_multicast = utils.str_append_hash(all_coap_nodes, port, message.token) if key_mid in list(self._transactions.keys()): transaction = self._transactions[key_mid] elif key_token in self._transactions_token: @@ -216,10 +209,10 @@ class MessageLayer(object): if transaction.request.mid is None: transaction.request.mid = self.fetch_mid() - key_mid = str_append_hash(host, port, request.mid) + key_mid = utils.str_append_hash(host, port, request.mid) self._transactions[key_mid] = transaction - key_token = str_append_hash(host, port, request.token) + key_token = utils.str_append_hash(host, port, request.token) self._transactions_token[key_token] = transaction return self._transactions[key_mid] @@ -252,7 +245,7 @@ class MessageLayer(object): host, port = transaction.response.destination except AttributeError: return - key_mid = str_append_hash(host, port, transaction.response.mid) + key_mid = utils.str_append_hash(host, port, transaction.response.mid) self._transactions[key_mid] = transaction transaction.request.acknowledged = True @@ -274,8 +267,8 @@ class MessageLayer(object): host, port = message.destination except AttributeError: return - key_mid = str_append_hash(host, port, message.mid) - key_token = str_append_hash(host, port, message.token) + key_mid = utils.str_append_hash(host, port, message.mid) + key_token = utils.str_append_hash(host, port, message.token) if key_mid in self._transactions: transaction = self._transactions[key_mid] related = transaction.response diff --git a/coapthon/layers/observelayer.py b/coapthon/layers/observelayer.py index 5df6a39..2820765 100644 --- a/coapthon/layers/observelayer.py +++ b/coapthon/layers/observelayer.py @@ -1,6 +1,8 @@ import logging import time + from coapthon import defines +from coapthon import utils __author__ = 'Giacomo Tanganelli' @@ -40,7 +42,7 @@ class ObserveLayer(object): if request.observe == 0: # Observe request host, port = request.destination - key_token = hash(str(host) + str(port) + str(request.token)) + key_token = utils.str_append_hash(host, port, request.token) self._relations[key_token] = ObserveItem(time.time(), None, True, None) @@ -56,7 +58,7 @@ class ObserveLayer(object): :return: the modified transaction """ host, port = transaction.response.source - key_token = hash(str(host) + str(port) + str(transaction.response.token)) + key_token = utils.str_append_hash(host, port, transaction.response.token) if key_token in self._relations and transaction.response.type == defines.Types["CON"]: transaction.notification = True return transaction @@ -70,7 +72,7 @@ class ObserveLayer(object): :return: the message unmodified """ host, port = message.destination - key_token = hash(str(host) + str(port) + str(message.token)) + key_token = utils.str_append_hash(host, port, message.token) if key_token in self._relations and message.type == defines.Types["RST"]: del self._relations[key_token] return message @@ -88,7 +90,7 @@ class ObserveLayer(object): if transaction.request.observe == 0: # Observe request host, port = transaction.request.source - key_token = hash(str(host) + str(port) + str(transaction.request.token)) + key_token = utils.str_append_hash(host, port, transaction.request.token) non_counter = 0 if key_token in self._relations: # Renew registration @@ -98,7 +100,7 @@ class ObserveLayer(object): self._relations[key_token] = ObserveItem(time.time(), non_counter, allowed, transaction) elif transaction.request.observe == 1: host, port = transaction.request.source - key_token = hash(str(host) + str(port) + str(transaction.request.token)) + key_token = utils.str_append_hash(host, port, transaction.request.token) logger.info("Remove Subscriber") try: del self._relations[key_token] @@ -120,7 +122,7 @@ class ObserveLayer(object): """ if empty.type == defines.Types["RST"]: host, port = transaction.request.source - key_token = hash(str(host) + str(port) + str(transaction.request.token)) + key_token = utils.str_append_hash(host, port, transaction.request.token) logger.info("Remove Subscriber") try: del self._relations[key_token] @@ -138,7 +140,7 @@ class ObserveLayer(object): :return: the transaction unmodified """ host, port = transaction.request.source - key_token = hash(str(host) + str(port) + str(transaction.request.token)) + key_token = utils.str_append_hash(host, port, transaction.request.token) if key_token in self._relations: if transaction.response.code == defines.Codes.CONTENT.number: if transaction.resource is not None and transaction.resource.observable: @@ -190,7 +192,7 @@ class ObserveLayer(object): """ logger.info("Remove Subcriber") host, port = message.destination - key_token = hash(str(host) + str(port) + str(message.token)) + key_token = utils.str_append_hash(host, port, message.token) try: self._relations[key_token].transaction.completed = True del self._relations[key_token] diff --git a/coapthon/messages/message.py b/coapthon/messages/message.py index 2bd8c5e..6364bf8 100644 --- a/coapthon/messages/message.py +++ b/coapthon/messages/message.py @@ -1,5 +1,9 @@ -from coapthon.utils import parse_blockwise +# -*- coding: utf-8 -*- + +import binascii + from coapthon import defines +from coapthon import utils from coapthon.messages.option import Option __author__ = 'Giacomo Tanganelli' @@ -122,8 +126,10 @@ class Message(object): if value is None: self._token = value return - if not isinstance(value, str): - value = str(value) + if isinstance(value, int): + value = bytes([value]) + if not isinstance(value, bytes): + value = bytes(value, "utf-8") if len(value) > 256: raise AttributeError self._token = value @@ -547,7 +553,7 @@ class Message(object): value = None for option in self.options: if option.number == defines.OptionRegistry.BLOCK1.number: - value = parse_blockwise(option.value) + value = utils.parse_blockwise(option.value) return value @block1.setter @@ -599,7 +605,7 @@ class Message(object): value = None for option in self.options: if option.number == defines.OptionRegistry.BLOCK2.number: - value = parse_blockwise(option.value) + value = utils.parse_blockwise(option.value) return value @block2.setter @@ -691,12 +697,14 @@ class Message(object): if self._code is None: self._code = defines.Codes.EMPTY.number + token = binascii.hexlify(self._token).decode("utf-8") if self._token is not None else str(None) + msg = "From {source}, To {destination}, {type}-{mid}, {code}-{token}, ["\ .format(source=self._source, destination=self._destination, type=inv_types[self._type], mid=self._mid, - code=defines.Codes.LIST[self._code].name, token=self._token) + code=defines.Codes.LIST[self._code].name, token=token) for opt in self._options: if 'Block' in opt.name: - msg += "{name}: {value}, ".format(name=opt.name, value=parse_blockwise(opt.value)) + msg += "{name}: {value}, ".format(name=opt.name, value=utils.parse_blockwise(opt.value)) else: msg += "{name}: {value}, ".format(name=opt.name, value=opt.value) msg += "]" @@ -726,9 +734,9 @@ class Message(object): msg += "MID: " + str(self._mid) + "\n" if self._code is None: self._code = 0 - + token = binascii.hexlify(self._token).decode("utf-8") if self._token is not None else str(None) msg += "Code: " + str(defines.Codes.LIST[self._code].name) + "\n" - msg += "Token: " + str(self._token) + "\n" + msg += "Token: " + token + "\n" for opt in self._options: msg += str(opt) msg += "Payload: " + "\n" diff --git a/coapthon/serializer.py b/coapthon/serializer.py index df11a87..2d167d5 100644 --- a/coapthon/serializer.py +++ b/coapthon/serializer.py @@ -51,10 +51,7 @@ class Serializer(object): message.type = message_type message.mid = mid if token_length > 0: - fmt = "%ss" % token_length - s = struct.Struct(fmt) - token_value = s.unpack_from(datagram[pos:])[0] - message.token = token_value.decode("utf-8") + message.token = datagram[pos:pos+token_length] else: message.token = None @@ -144,7 +141,7 @@ class Serializer(object): """ fmt = "!BBH" - if message.token is None or message.token == "": + if message.token is None: tkl = 0 else: tkl = len(message.token) @@ -157,9 +154,9 @@ class Serializer(object): if message.token is not None and tkl > 0: - for b in str(message.token): - fmt += "c" - values.append(bytes(b, "utf-8")) + for b in message.token: + fmt += "B" + values.append(b) options = Serializer.as_sorted_list(message.options) # already sorted lastoptionnumber = 0 diff --git a/coapthon/utils.py b/coapthon/utils.py index 505054c..dd99122 100644 --- a/coapthon/utils.py +++ b/coapthon/utils.py @@ -1,9 +1,24 @@ +# -*- coding: utf-8 -*- + +import binascii import random import string __author__ = 'Giacomo Tanganelli' +def str_append_hash(*args): + """ Convert each argument to a lower case string, appended, then hash """ + ret_hash = "" + for i in args: + if isinstance(i, (str, int)): + ret_hash += str(i).lower() + elif isinstance(i, bytes): + ret_hash += binascii.hexlify(i).decode("utf-8") + + return hash(ret_hash) + + def check_nocachekey(option): """ checks if an option is a NoCacheKey option or Etag @@ -51,7 +66,7 @@ def is_uri_option(number): def generate_random_token(size): - return ''.join(random.choice(string.ascii_letters) for _ in range(size)) + return bytes([random.randint(0, 255) for _ in range(size)]) def parse_blockwise(value): diff --git a/coverage_test.py b/coverage_test.py index 36e7323..94e8c1c 100644 --- a/coverage_test.py +++ b/coverage_test.py @@ -1,3 +1,5 @@ +import os +import logging.config from queue import Queue import random import socket @@ -6,6 +8,7 @@ import unittest from coapclient import HelperClient from coapserver import CoAPServer from coapthon import defines +from coapthon.utils import create_logging from coapthon.messages.message import Message from coapthon.messages.option import Option from coapthon.messages.request import Request @@ -57,6 +60,10 @@ PAYLOAD = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam no "erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd " \ "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." +if not os.path.isfile("logging.conf"): + create_logging() +logging.config.fileConfig("logging.conf", disable_existing_loggers=False) + class Tests(unittest.TestCase): @@ -1397,7 +1404,7 @@ class Tests(unittest.TestCase): expected.type = defines.Types["ACK"] expected._mid = self.current_mid expected.code = defines.Codes.NOT_FOUND.number - expected.token = "100" + expected.token = 100 expected.payload = None exchange1 = (req, expected)