def block2(self, value): """ Set the Block2 option. :param value: the Block2 value, (num, m, size) """ option = Option() option.number = defines.inv_options['Block2'] num, m, size = value if size > 1024: szx = 6 elif 512 < size <= 1024: szx = 6 elif 256 < size <= 512: szx = 5 elif 128 < size <= 256: szx = 4 elif 64 < size <= 128: szx = 3 elif 32 < size <= 64: szx = 2 elif 16 < size <= 32: szx = 1 else: szx = 0 value = (num << 4) value |= (m << 3) value |= szx option.value = value self.add_option(option)
def block2(self, value): """ Set the Block2 option. :param value: the Block2 value """ option = Option() option.number = defines.OptionRegistry.BLOCK2.number num, m, size = value if size > 512: szx = 6 elif 256 < size <= 512: szx = 5 elif 128 < size <= 256: szx = 4 elif 64 < size <= 128: szx = 3 elif 32 < size <= 64: szx = 2 elif 16 < size <= 32: szx = 1 else: szx = 0 value = (num << 4) value |= (m << 3) value |= szx option.value = value self.add_option(option)
def add_block2(self, num, m, size): """ Add and format a Block2 option to a request. :param num: num :param m: more blocks :param size: size in byte """ option = Option() option.number = defines.inv_options['Block2'] if size > 1024: szx = 6 elif 512 < size <= 1024: szx = 6 elif 256 < size <= 512: szx = 5 elif 128 < size <= 256: szx = 4 elif 64 < size <= 128: szx = 3 elif 32 < size <= 64: szx = 2 elif 16 < size <= 32: szx = 1 else: szx = 0 value = (num << 4) value |= (m << 3) value |= szx option.value = value self.add_option(option)
def test_get_separate(self): print "\nGET /separate\n" args = ("/separate",) kwargs = {} path = args[0] req = Request() for key in kwargs: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) req.code = defines.inv_codes['GET'] req.uri_path = path req.type = defines.inv_types["CON"] req._mid = self.current_mid req.destination = self.server_address expected = Response() expected.type = defines.inv_types["CON"] expected.code = defines.responses["CONTENT"] expected.token = None expected.payload = "Separate" self.current_mid += 1 self._test_separate(req, expected)
def block1(self, value): """ Set the Block1 option. :param value: the Block1 value """ option = Option() option.number = defines.OptionRegistry.BLOCK1.number num, m, size = value if size > 512: szx = 6 elif 256 < size <= 512: szx = 5 elif 128 < size <= 256: szx = 4 elif 64 < size <= 128: szx = 3 elif 32 < size <= 64: szx = 2 elif 16 < size <= 32: szx = 1 else: szx = 0 value = (num << 4) value |= (m << 3) value |= szx option.value = value self.add_option(option)
def test_get_not_found(self): print "\nGET /not_found\n" args = ("/not_found",) kwargs = {} path = args[0] req = Request() for key in kwargs: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) req.code = defines.inv_codes['GET'] req.uri_path = path req.type = defines.inv_types["CON"] req._mid = self.current_mid req.destination = self.server_address expected = Response() expected.type = defines.inv_types["ACK"] expected._mid = self.current_mid expected.code = defines.responses["NOT_FOUND"] expected.token = None expected.payload = None self.current_mid += 1 self._test(req, expected)
def test_long(self): print "\nGET /long\n" args = ("/long",) kwargs = {} path = args[0] req = Request() for key in kwargs: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) req.code = defines.inv_codes['GET'] req.uri_path = path req.type = defines.inv_types["CON"] req._mid = self.current_mid req.destination = self.server_address expected = Response() expected.type = defines.inv_types["ACK"] expected._mid = self.current_mid expected.code = None expected.token = None expected.payload = None expected2 = Response() expected2.type = defines.inv_types["CON"] expected2.code = defines.responses["CONTENT"] expected2.token = None expected2.payload = "Long Time" self.current_mid += 1 self._test_modular([(req, expected), (None, expected2)])
def block2(self, value): option = Option() option.number = defines.OptionRegistry.BLOCK2.number num, m, size = value if size > 1024: szx = 6 elif 512 < size <= 1024: szx = 6 elif 256 < size <= 512: szx = 5 elif 128 < size <= 256: szx = 4 elif 64 < size <= 128: szx = 3 elif 32 < size <= 64: szx = 2 elif 16 < size <= 32: szx = 1 else: szx = 0 value = (num << 4) value |= (m << 3) value |= szx option.value = value self.add_option(option)
def if_match(self, values): assert isinstance(values, list) for v in values: option = Option() option.number = defines.OptionRegistry.IF_MATCH.number option.value = v self.add_option(option)
def delete(self, client_callback, *args, **kwargs): if isinstance(args[0], str): path = str(args[0]) req = Request() req.uri_path = path if "Token" in kwargs.keys(): req.token = kwargs.get("Token") del kwargs["Token"] if "MID" in kwargs.keys(): req.mid = kwargs.get("MID") del kwargs["MID"] if "Server" in kwargs.keys(): req.destination = kwargs.get("Server") del kwargs["Server"] else: req = args[0] for key in kwargs: try: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) except KeyError: pass req.code = defines.inv_codes['DELETE'] req.type = defines.inv_types["CON"] self.send_callback(req, self.delete_results, client_callback)
def test_td_coap_core_07(self): print "TD_COAP_CORE_07" path = "/test" req = Request() req.code = defines.inv_codes['PUT'] req.uri_path = path req.type = defines.inv_types["NON"] o = Option() o.number = defines.inv_options["Content-Type"] o.value = defines.inv_content_types["application/xml"] req.add_option(o) req._mid = self.current_mid req.destination = self.server_address req.payload = "<value>test</value>" expected = Response() expected.type = defines.inv_types["NON"] expected._mid = None expected.code = defines.responses["CHANGED"] expected.token = None expected.payload = None self.current_mid += 1 self._test_plugtest(req, expected)
def add_observing(self, resource, request, response): """ Add an observer to a resource and sets the Observe option in the response. :param resource: the resource of interest :param request: the request :param response: the response :return: response """ host, port = response.destination key = hash(str(host) + str(port) + str(response.token)) observers = self._parent.relation.get(resource) now = int(round(time.time() * 1000)) observe_count = resource.observe_count if observers is None: # log.msg("Initiate an observe relation between " + str(host) + ":" + # str(port) + " and resource " + str(resource.path)) observers = {key: (now, request, response)} elif key not in observers: # log.msg("Initiate an observe relation between " + str(host) + ":" + # str(port) + " and resource " + str(resource.path)) observers[key] = (now, request, response) else: # log.msg("Update observe relation between " + str(host) + ":" + # str(port) + " and resource " + str(resource.path)) old, request, response = observers[key] observers[key] = (now, request, response) self._parent.relation[resource] = observers option = Option() option.number = defines.inv_options['Observe'] option.value = observe_count response.add_option(option) return response
def add_if_none_match(self): """ Add the if-none-match option to the request. """ option = Option() option.number = defines.OptionRegistry.IF_NONE_MATCH.number option.value = None self.add_option(option)
def location_query(self, value): del self.location_query queries = value.split("&") for q in queries: option = Option() option.number = defines.OptionRegistry.LOCATION_QUERY.number option.value = str(q) self.add_option(option)
def add_no_response(self): """ Add the no-response option to the request # https://tools.ietf.org/html/rfc7967#section-2.1 """ option = Option() option.number = defines.OptionRegistry.NO_RESPONSE.number option.value = 26 self.add_option(option)
def block1(self, value): option = Option() option.number = defines.inv_options["Block1"] num, m, size = value value = num << 4 value |= m << 3 value |= size option.value = value
def ocf_content_format_version(self, value): """ Add the ocf_content_format_version option to the request. """ option = Option() #print (" setting ocf_content_format_version :", int(value)) option.number = defines.OptionRegistry.OCF_CONTENT_FORMAT_VERSION.number option.value = int(value) print("request: ocf_content_format_version (setter)", option) self.add_option(option)
def max_age(self, value): """ Set the Max-Age option of a request. :param value: the Proxy-Schema value """ option = Option() option.number = defines.OptionRegistry.MAX_AGE.number option.value = int(value) self.add_option(option)
def location_path(self, path): path = path.strip("/") tmp = path.split("?") path = tmp[0] paths = path.split("/") for p in paths: option = Option() option.number = defines.OptionRegistry.LOCATION_PATH.number option.value = p self.add_option(option)
def etag(self, etag): """ Add an ETag option to the message. :param etag: the etag """ option = Option() option.number = defines.inv_options['ETag'] option.value = etag self.add_option(option)
def uri_query(self, value): """ Set the uri_query option of a request. :param value: the uri_query value """ option = Option() option.number = defines.OptionRegistry.URI_QUERY.number option.value = str(value) self.add_option(option)
def etag(self, etag): """ Add an ETag option to the message. :param etag: the etag """ option = Option() option.number = defines.OptionRegistry.ETAG.number option.value = etag self.add_option(option)
def max_age(self, max_age): """ Set the Max-Age option of a response. :param max_age: the Max-Age in seconds """ option = Option() option.number = defines.inv_options["Max-Age"] option.value = int(max_age) self.add_option(option)
def size2(self, value): """ Set the Size2 option. :param value: the Block2 value """ option = Option() option.number = defines.OptionRegistry.SIZE2.number option.value = value self.add_option(option)
def proxy_schema(self, value): """ Set the Proxy-Schema option of a request. :param value: the Proxy-Schema value """ option = Option() option.number = defines.OptionRegistry.PROXY_SCHEME.number option.value = str(value) self.add_option(option)
def post(self, path, payload, callback=None, timeout=None, *options): request = self.mk_request(defines.Codes.POST, path) request.token = generate_random_token(2) request.payload = payload for number, value in options: option = Option() option.number = number option.value = value request.add_option(option) return self.send_request(request, callback, timeout)
def proxy_uri(self, uri): """ Set the Proxy-Uri option of a request. :param uri: the Proxy-Uri values """ option = Option() option.number = defines.inv_options['Proxy-Uri'] option.value = str(uri) self.add_option(option)
def proxy_uri(self, value): """ Set the Proxy-Uri option of a request. :param value: the Proxy-Uri value """ option = Option() option.number = defines.OptionRegistry.PROXY_URI.number option.value = str(value) self.add_option(option)
def proxy_schema(self, value): """ Set the Proxy-Uri option of a request. :param value: the Proxy-Uri value """ option = Option() option.number = defines.OptionRegistry.PROXY_SCHEME.number option.value = str(value) self.add_option(option)
def add_no_response(self,value=26): """ Add the no-response option to the request # https://tools.ietf.org/html/rfc7967#section-2.1 """ option = Option() option.number = defines.OptionRegistry.NO_RESPONSE.number #26 means 16+8+2: not interested in all responses option.value = value self.add_option(option)
def accept(self, value): """ Add an Accept option to a request. :param value: the Accept value """ if value in defines.Content_types.values(): option = Option() option.number = defines.OptionRegistry.ACCEPT.number option.value = value self.add_option(option)
def observe(self, ob): """ Add the Observe option. :param ob: observe count """ option = Option() option.number = defines.inv_options['Observe'] option.value = ob self.del_option_name("Observe") self.add_option(option)
def content_type(self, content_type): """ Set the Content-Type option of a response. :type content_type: int :param content_type: the Content-Type """ option = Option() option.number = defines.inv_options['Content-Type'] option.value = int(content_type) self.add_option(option)
def observe(self, ob): """ Add the Observe option. :param ob: observe count """ option = Option() option.number = defines.OptionRegistry.OBSERVE.number option.value = ob self.del_option_by_number(defines.OptionRegistry.OBSERVE.number) self.add_option(option)
def add_query(self, q): """ Adds a query. :param q: the query """ queries = q.split("&") for q in queries: option = Option() option.number = defines.inv_options['Uri-Query'] option.value = str(q) self.add_option(option)
def content_type(self, content_type): """ Set the Content-Type option of a response. :type content_type: int :param content_type: the Content-Type """ option = Option() option.number = defines.OptionRegistry.CONTENT_TYPE.number option.value = int(content_type) self.add_option(option)
def if_none_match(self, value): """ Set the If-Match option of a request. :param value: True/False :type values : bool """ assert isinstance(value, bool) option = Option() option.number = defines.OptionRegistry.IF_NONE_MATCH.number option.value = None self.add_option(option)
def max_age(self, value): """ Set the MaxAge of the response. :type value: int :param value: the MaxAge option """ option = Option() option.number = defines.OptionRegistry.MAX_AGE.number option.value = int(value) self.del_option_by_number(defines.OptionRegistry.MAX_AGE.number) self.add_option(option)
def test_post_and_get_storage(self): print "\nPOST /storage/data1 - GET /storage/data1\n" args = ("/storage/data1",) kwargs = {} path = args[0] req = Request() for key in kwargs: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) req.code = defines.inv_codes['POST'] req.uri_path = path req.type = defines.inv_types["CON"] req._mid = self.current_mid req.payload = "Created" req.destination = self.server_address expected = Response() expected.type = defines.inv_types["ACK"] expected._mid = self.current_mid expected.code = defines.responses["CREATED"] expected.token = None expected.payload = None option = Option() option.number = defines.inv_options["Location-Path"] option.value = "/storage/data1" expected.add_option(option) self.current_mid += 1 self._test(req, expected) req = Request() for key in kwargs: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) req.code = defines.inv_codes['GET'] req.uri_path = path req.type = defines.inv_types["CON"] req._mid = self.current_mid req.destination = self.server_address expected = Response() expected.type = defines.inv_types["ACK"] expected._mid = self.current_mid expected.code = defines.responses["CONTENT"] expected.token = None expected.payload = "Created" self.current_mid += 1 self._test(req, expected)
def uri_query(self, value): """ Adds a query. :param value: the query """ del self.uri_query queries = value.split("&") for q in queries: option = Option() option.number = defines.OptionRegistry.URI_QUERY.number option.value = str(q) self.add_option(option)
def if_match(self, values): """ Set the If-Match option of a request. :param values: the If-Match values :type values : list """ assert isinstance(values, list) for v in values: option = Option() option.number = defines.OptionRegistry.IF_MATCH.number option.value = v self.add_option(option)
def etag(self, etag): """ Add an ETag option to the message. :param etag: the etag """ if not isinstance(etag, list): etag = [etag] for e in etag: option = Option() option.number = defines.OptionRegistry.ETAG.number option.value = e self.add_option(option)
def ocf_accept_content_format_version(self, value): """ Set the OCF_ACCEPT_CONTENT_FORMAT_VERSION of the response. :type value: int :param value: the OCF_ACCEPT_CONTENT_FORMAT_VERSION option """ option = Option() option.number = defines.OptionRegistry.OCF_ACCEPT_CONTENT_FORMAT_VERSION.number option.value = int(value) self.del_option_by_number(defines.OptionRegistry.OCF_ACCEPT_CONTENT_FORMAT_VERSION.number) print ("response: ocf_accept_content_format_version (setter)",option) self.add_option(option)
def build_request(request, headers, device): """ Helper method to organized required headers into the CoAP Request. :param request: Request being build :param headers: Data from AMQP message which contains data to forward OpenC2 Command. :param device: Device specific data from headers sent by O.I.F. """ orc_host, orc_port = headers["transport"]["socket"].split( ":", 1) # location of orchestrator-side CoAP server request.source = (orc_host, safe_cast(orc_port, int, 5683)) dev_host, dev_port = device["socket"].split( ":", 1) # location of device-side CoAP server request.destination = (dev_host, safe_cast(dev_port, int, 5683)) encoding = f"application/{device['encoding']}" # Content Serialization request.content_type = defines.Content_types[ encoding] # using application/json, TODO: add define to openc2+json request.mid = int("0x" + headers["correlationID"], 16) # 16-bit correlationID request.timestamp = headers[ "date"] # time message was sent from orchestrator # Add OIF-unique value used for routing to the desired actuator profile = Option() profile.number = 8 profile.value = device.get("profile", "")[0] request.add_option(profile) source_socket = Option() source_socket.number = 3 source_socket.value = headers["transport"]["socket"] request.add_option(source_socket) return request
def location_query(self, value): """ Set the Location-Query of the response. :type path: String :param path: the Location-Query as a string """ del self.location_query queries = value.split("&") for q in queries: option = Option() option.number = defines.OptionRegistry.LOCATION_QUERY.number option.value = str(q) self.add_option(option)
def location_path(self, path): """ Set the Location-Path of the response. :type path: String :param path: the Location-Path as a string """ path = path.strip("/") tmp = path.split("?") path = tmp[0] paths = path.split("/") for p in paths: option = Option() option.number = defines.OptionRegistry.LOCATION_PATH.number option.value = p self.add_option(option)
def etag(self, etag): """ Add an ETag option to the message. :param etag: the etag """ if not isinstance(etag, list): etag = [etag] for e in etag: option = Option() option.number = defines.OptionRegistry.ETAG.number if isinstance(e, str): # if etag was accidentally set to str, convert it to bytes e = bytes(e, "utf-8") option.value = e self.add_option(option)
def uri_path(self, path): """ Set the Uri-Path of a request. :param path: the Uri-Path """ path = path.strip("/") tmp = path.split("?") path = tmp[0] paths = path.split("/") for p in paths: option = Option() option.number = defines.OptionRegistry.URI_PATH.number option.value = p self.add_option(option) if len(tmp) > 1: query = tmp[1] self.uri_query = query
def deserialize(datagram, source): """ De-serialize a stream of byte to a message. :param datagram: the incoming udp message :param source: the source address and port (ip, port) :return: the message :rtype: Message """ try: fmt = "!BBH" pos = struct.calcsize(fmt) s = struct.Struct(fmt) values = s.unpack_from(datagram) first = values[0] code = values[1] mid = values[2] version = (first & 0xC0) >> 6 message_type = (first & 0x30) >> 4 token_length = (first & 0x0F) if Serializer.is_response(code): message = Response() message.code = code elif Serializer.is_request(code): message = Request() message.code = code else: message = Message() message.source = source message.destination = None message.version = version 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") else: message.token = None pos += token_length current_option = 0 values = datagram[pos:] length_packet = len(values) pos = 0 while pos < length_packet: next_byte = struct.unpack("B", values[pos].to_bytes(1, "big"))[0] pos += 1 if next_byte != int(defines.PAYLOAD_MARKER): # the first 4 bits of the byte represent the option delta # delta = self._reader.read(4).uint num, option_length, pos = Serializer.read_option_value_len_from_byte(next_byte, pos, values) current_option += num # read option try: option_item = defines.OptionRegistry.LIST[current_option] except KeyError: (opt_critical, _, _) = defines.OptionRegistry.get_option_flags(current_option) if opt_critical: raise AttributeError("Critical option %s unknown" % current_option) else: # If the non-critical option is unknown # (vendor-specific, proprietary) - just skip it #log.err("unrecognized option %d" % current_option) pass else: if option_length == 0: value = None elif option_item.value_type == defines.INTEGER: tmp = values[pos: pos + option_length] value = 0 for b in tmp: value = (value << 8) | struct.unpack("B", b.to_bytes(1, "big"))[0] elif option_item.value_type == defines.OPAQUE: tmp = values[pos: pos + option_length] value = tmp else: value = values[pos: pos + option_length] option = Option() option.number = current_option option.value = Serializer.convert_to_raw(current_option, value, option_length) message.add_option(option) if option.number == defines.OptionRegistry.CONTENT_TYPE.number: message.payload_type = option.value finally: pos += option_length else: if length_packet <= pos: # log.err("Payload Marker with no payload") raise AttributeError("Packet length %s, pos %s" % (length_packet, pos)) message.payload = "" payload = values[pos:] try: if message.payload_type == defines.Content_types["application/octet-stream"]: message.payload = payload else: message.payload = payload.decode("utf-8") except AttributeError: message.payload = payload.decode("utf-8") pos += len(payload) return message except AttributeError: return defines.Codes.BAD_REQUEST.number except struct.error: return defines.Codes.BAD_REQUEST.number
def max_age(self, value): option = Option() option.number = defines.OptionRegistry.MAX_AGE.number option.value = int(value) self.add_option(option)