async def render_post(self, request): payload = request.payload.decode() print(f'LED {self.pin}: POST {payload}') if payload in ['0', 'off']: self.resource.off() elif payload in ['1', 'on']: self.resource.on() elif payload in ['-1', 'toggle']: self.resource.toggle() elif 'blink' in payload: p = payload.split(" ") if p[0] != 'blink': return Message(code=Code.BAD_REQUEST) on_time, off_time, n = 1, 1, None if len(p) > 1 and p[1].isdigit(): on_time = int(p[1]) if len(p) > 2 and p[2].isdigit(): off_time = int(p[2]) if len(p) > 3 and p[3].isdigit(): n = int(p[3]) self.resource.blink(on_time, off_time, n) else: return Message(code=Code.BAD_REQUEST) return Message(code=Code.CHANGED)
async def render_get(self, request): if request.opt.accept == 41: return Message(payload=("<data>%s</data>" % self.text).encode('utf8'), content_format=41) else: return Message(payload=self.text.encode('utf8'), content_format=0)
async def render_post(self, request): payload = request.payload.decode() print(f'SERVO {self.pin}: POST {payload}') if self.min_angle <= int(payload) <= self.max_angle: self.resource.angle = int(payload) return Message(code=Code.CHANGED) return Message(code=Code.BAD_REQUEST)
def abort(self, errormessage=None, bad_csm_option=None): self.log.warning("Aborting connection: %s", errormessage) abort_msg = Message(code=ABORT) if errormessage is not None: abort_msg.payload = errormessage.encode('utf8') if bad_csm_option is not None: bad_csm_option_option = optiontypes.UintOption(2, bad_csm_option) abort_msg.opt.add_option(bad_csm_option_option) self._abort_with(abort_msg)
def wraps(self: BaseResource, request: Message): try: request.payload = gzdecompress(request.payload) except OSError: # Since the received message was not properly compressed, return a non compressed response just in case return Message(code=Code.BAD_REQUEST, payload=b'{"error":"Bad GZIP compression"}') response = func(self, request) response.payload = gzcompress(response.payload) return response
async def render_post(self, request): payload = request.payload.decode() print(f'GPIO {self.pin}: POST {payload}') if payload in ['0', 'off']: self.resource.off() elif payload in ['1', 'on']: self.resource.on() else: return Message(code=Code.BAD_REQUEST) return Message(code=Code.CHANGED)
def _decode_message(data: bytes) -> Message: tokenoffset, tkl, _ = _extract_message_size(data) if tkl > 8: raise error.UnparsableMessage("Overly long token") code = data[tokenoffset - 1] token = data[tokenoffset:tokenoffset + tkl] msg = Message(code=code, token=token) msg.payload = msg.opt.decode(data[tokenoffset + tkl:]) return msg
def _decode_message(data: bytes) -> Message: codeoffset = 1 tokenoffset = 2 tkl = data[0] if tkl > 8: raise error.UnparsableMessage("Overly long token") code = data[codeoffset] token = data[tokenoffset:tokenoffset + tkl] msg = Message(code=code, token=token) msg.payload = msg.opt.decode(data[tokenoffset + tkl:]) return msg
async def start(self): p = argparse.ArgumentParser() p.add_argument("rd_uri", help="Preconfigured address of the resource" " directory", nargs='?', default='coap://[ff05::fd]') p.add_argument("--simple", help="Run simple registration rather than" " full (incomplete: Never renews)", action="store_true") p.add_argument("--register-as", help="Endpoint name to register as (default: node$SITE", default=None) p.add_argument("--site", help="Use a different resource / link layout", default=1, type=int) add_server_arguments(p) opts = p.parse_args() if opts.site == 1: site = build_site_node1() elif opts.site == 2: site = build_site_node2() elif opts.site == 3: site = build_site_node3() else: raise p.error("Invalid site value") if opts.register_as is None: opts.register_as = "node%s" % opts.site self.context = await server_context_from_arguments(site, opts) if opts.simple: rd_wkc = urljoin(opts.rd_uri, '/.well-known/core?ep=%s<=6000' % opts.register_as) await self.context.request(Message(code=POST, uri=rd_wkc)).response else: self.registerer = Registerer(self.context, opts.rd_uri, lt=120, registration_parameters={'ep': opts.register_as})
async def _observe(self, api_command: Command[T], timeout: float | None) -> None: """Observe an endpoint.""" duration = api_command.observe_duration url = api_command.url(self._host) err_callback = api_command.err_callback msg = Message(code=Code.GET, uri=url, observe=duration) # Note that this is necessary to start observing pr_req, pr_rsp = await self._get_response(msg, timeout) api_command.process_result(_process_output(pr_rsp)) def success_callback(res: Message) -> None: api_command.process_result(_process_output(res)) def error_callback(exc: Exception) -> None: if isinstance(exc, LibraryShutdown): _LOGGER.debug("Protocol is shutdown, stopping observation") return if err_callback: err_callback(exc) observation = pr_req.observation # The observation is set on the request # since we pass a Message with observe set above. assert observation is not None observation.register_callback(success_callback) observation.register_errback(error_callback) self._observations_err_callbacks.append(observation.error)
async def start_rd_observation(uri): """ decode Message cbor payload to object """ def decode_linkformat_payload(payload): link = payload.decode('ascii') myJson = Parse(link).as_json_string() return json.loads(myJson) """ start observation of resource """ def process_resource(resource): uris[resource['title']] = resource['href'] handle = asyncio.create_task(start_node_observation(resource['href'], resource['title'])) observation_handles.add(handle) get_obs_req = Message(code=GET, uri=uri, observe=0) request = protocol.request(get_obs_req) initResponse = await request.response resources = decode_linkformat_payload(initResponse.payload) for resource in resources: process_resource(resource) async for response in request.observation: newResources = decode_linkformat_payload(response.payload) for resource in newResources: process_resource(resource)
def test_oscore_filebased(self): from aiocoap.oscore import FilesystemSecurityContext raw = { 'coap://some-oscore-host/*': { 'oscore': { 'contextfile': __file__.replace('test_credentials.py', 'test_credentials_oscore_context/'), 'role': 'client' } }, 'coaps://some-dtls-host/*': { 'dtls': { 'psk': { 'hex': '73-65-63-72-65-74-50-53-4b' }, 'client-identity': b'Client_identity' } } } m = CredentialsMap() m.load_from_dict(raw) message = Message(code=GET, uri='coap://some-oscore-host/.well-known/core') secmatch = m.credentials_from_request(message) self.assertEqual(type(secmatch), FilesystemSecurityContext)
def _process_signaling(self, msg): if msg.code == CSM: if self._remote_settings is None: self._remote_settings = {} for opt in msg.opt.option_list(): # FIXME: this relies on the relevant option numbers to be # opaque; message parsing should already use the appropriate # option types, or re-think the way options are parsed if opt.number == 2: self._remote_settings['max-message-size'] = int.from_bytes(opt.value, 'big') elif opt.number == 4: self._remote_settings['block-wise-transfer'] = True elif opt.number.is_critical(): self.abort("Option not supported", bad_csm_option=opt.number) else: pass # ignoring elective CSM options elif msg.code in (PING, PONG, RELEASE, ABORT): # not expecting data in any of them as long as Custody is not implemented for opt in msg.opt.option_list(): if opt.number.is_critical(): self.abort("Unknown critical option") else: pass if msg.code == PING: pong = Message(code=PONG, token=msg.token) self._send_message(pong) elif msg.code == PONG: pass elif msg.code == RELEASE: raise NotImplementedError elif msg.code == ABORT: raise NotImplementedError else: self.abort("Unknown signalling code")
async def send_to_coap(payload): """ POST request to: localhost port 5683 (official IANA assigned CoAP port), URI "/other/sensor-values". """ from aiocoap import Context, Message from aiocoap.numbers.codes import Code from cbor2 import dumps context = await Context.create_client_context() request = Message(payload=dumps(payload), code=Code.POST) request.opt.uri_host = arg_host request.opt.uri_port = arg_port request.opt.uri_path = ("other", "sensor-values") response = await context.request(request).response str_res = str(response.code) status_code = str_res[:4] # or str_res.split()[0] if status_code == "4.00" or status_code == "5.00": print("Error: ", str_res) return False return True
async def _observe(self, api_command): """Observe an endpoint.""" duration = api_command.observe_duration url = api_command.url(self._host) err_callback = api_command.err_callback msg = Message(code=Code.GET, uri=url, observe=duration) # Note that this is necessary to start observing pr, r = await self._get_response(msg) api_command.result = _process_output(r) def success_callback(res): api_command.result = _process_output(res) def error_callback(ex): if isinstance(ex, LibraryShutdown): _LOGGER.debug("Protocol is shutdown, stopping observation") return err_callback(ex) ob = pr.observation ob.register_callback(success_callback) ob.register_errback(error_callback) self._observations_err_callbacks.append(ob.error)
def _execute(api_command): """Execute the command.""" if api_command.observe: yield from _observe(api_command) return method = api_command.method path = api_command.path data = api_command.data parse_json = api_command.parse_json url = api_command.url(host) kwargs = {} if data is not None: kwargs['payload'] = json.dumps(data).encode('utf-8') _LOGGER.debug('Executing %s %s %s: %s', host, method, path, data) else: _LOGGER.debug('Executing %s %s %s', host, method, path) api_method = Code.GET if method == 'put': api_method = Code.PUT msg = Message(code=api_method, uri=url, **kwargs) try: protocol = yield from _get_protocol() res = yield from protocol.request(msg).response except RequestTimedOut: raise RequestTimeout('Request timed out.') api_command.result = _process_output(res, parse_json) return api_command.result
async def set_control_values(self, data: dict, retry_count=5, resync=True) -> None: state_desired = { "state": { "desired": { "CommandType": "app", "DeviceId": "", "EnduserId": "", **data, } } } payload = json.dumps(state_desired) logger.debug("REQUEST: %s", payload) payload_encrypted = self._encryption_context.encrypt(payload) request = Message( code=POST, mtype=NON, uri=f"coap://{self.host}:{self.port}{self.CONTROL_PATH}", payload=payload_encrypted.encode(), ) response = await self._client_context.request(request).response logger.debug("RESPONSE: %s", response.payload) result = json.loads(response.payload) if result.get("status") == "success": return True else: if resync: logger.debug("set_control_value failed. resyncing...") await self._sync() if retry_count > 0: logger.debug("set_control_value failed. retrying...") return await self.set_control_values(data, retry_count - 1, resync) logger.error("set_control_value failed: %s", data) return False
def abort(self, errormessage=None, bad_csm_option=None): self.log.warning("Aborting connection: %s", errormessage) abort_msg = Message(code=ABORT) if errormessage is not None: abort_msg.payload = errormessage.encode('utf8') if bad_csm_option is not None: bad_csm_option_option = optiontypes.UintOption(2, bad_csm_option) abort_msg.opt.add_option(bad_csm_option_option) if self._transport is not None: self._send_message(abort_msg) self._transport.close() else: # FIXME: find out how this happens; i've only seen it after nmap # runs against an aiocoap server and then shutting it down. # "poisoning" the object to make sure this can not be exploited to # bypass the server shutdown. self._ctx = None
def start_server(): protocol = yield from Context.create_client_context() request = Message(code = Code.GET, mtype=aiocoap.CON) request.set_request_uri('coap://127.0.0.1/button') # set observe bit from None to 0 request.opt.observe = 0 def handle_notification(response): print("asdsadsa") print(response) import code; code.interact(local=dict(globals(), **locals())) protocol_request = protocol.request(request) protocol_request.observation.register_callback(handle_notification) protocol_request.observation.register_errback(handle_notification) response = yield from protocol_request.response
async def _get_file(url, method=GET, payload=b''): protocol = await Context.create_client_context(loop=None) request = Message(code=method, payload=payload) request.set_request_uri(url) try: response = await protocol.request(request).response except Exception as e: code = "Failed to fetch resource" payload = '{}'.format(e) else: code = response.code payload = response.payload finally: await protocol.shutdown() LOGGER.debug('{}: {}'.format(code, payload)) return code, payload
def _received_datagram(self, address, datagram): try: message = Message.decode(datagram, remote=address) except error.UnparsableMessage: self._log.warning("Ignoring unparsable message from %s", address) return self._mman.dispatch_message(message)
async def render(self, request): cf = request.opt.content_format acc = request.opt.accept raise_class = UnallowedMethod method_would_have_worked = False # FIXME: manually walking the MRO is not a nice way to go about this; # is there no other way to query the registered handlers according to # the regular inheritance patterns? for cls in type(self).mro(): if not issubclass( cls, ContenttypeRendered) or cls is ContenttypeRendered: continue for_method = cls.__handlers.get(request.code, None) if for_method is None: continue raise_class = UnsupportedContentFormat handler, responseformat = for_method.get((acc, cf), (None, None)) if handler is not None: break else: raise raise_class() sig = inspect.signature(handler) parameters = set(sig.parameters.keys()) parameters.remove("self") kwargs = {} if request.payload and "payload" not in parameters: raise BadRequest("Unexpected payload") if request.opt.uri_query and "query" not in parameters: raise BadRequest("Unexepcted query arguments") for p in parameters: if p == "payload": kwargs['payload'] = request.payload elif p == "request_uri": # BIG FIXME: This does not give the expected results due to the # URI path stripping in Site, and because Message gets the # requested authority wrong on the server side. kwargs["request_uri"] = request.get_request_uri() else: raise RuntimeError("Unexpected argument requested: %s" % p) payload = handler(self, **kwargs) if payload is None: payload = b"" elif isinstance(payload, str): payload = payload.encode('utf8') return Message( code={ GET: CONTENT, PUT: CHANGED }[request.code], payload=payload, content_format=responseformat, no_response=request.opt.no_response, )
def _coap_resource(url, method=GET, payload=b''): protocol = yield from Context.create_client_context() request = Message(code=method, payload=payload) request.set_request_uri(url) try: response = yield from protocol.request(request).response except Exception as e: code = "Failed to fetch resource" payload = '{0}'.format(e) else: code = response.code payload = response.payload.decode('utf-8') finally: yield from protocol.shutdown() logger.debug('Code: {0} - Payload: {1}'.format(code, payload)) return code, payload
def _build_msg(code: Code = None, data=None) -> Message: """ Prepares a `Message` object, adding code and dumping the json data """ if data is not None: payload = json.dumps(data, separators=(',', ':'), ensure_ascii=True).encode('ascii') else: payload = b'' return Message(code=code, payload=payload)
def _received_datagram(self, address, datagram): try: message = Message.decode(datagram, remote=address) except error.UnparsableMessage: self._log.warning("Ignoring unparsable message from %s" % (address, )) return self._new_message_callback(message)
async def send_trigger_request(ctx, target_addr, trigger): request = Message(code=Code.POST, mtype=NON, uri="coap://[" + target_addr + "]/t", payload=trigger) try: response = await ctx.request(request).response except Exception as e: log("Failed to fetch resource:") log(e) else: log("Trigger result: %s\n%r" % (response.code, response.payload))
async def coap_post(uri, payload): """ Takes in a String URI and a String payload. The string is posted to the uri provided. (full uri expected e.g. coap://localhost/) """ context = await Context.create_client_context() request = Message(code=POST, payload=bytes(payload, 'utf-8'), uri=uri) response = await context.request(request).response return response
async def _execute(self, api_command): data = api_command.data url = api_command.url(self._host) kwargs = {} kwargs['payload'] = json.dumps(data).encode('utf-8') api_method = Code.POST msg = Message(code=api_method, uri=url, **kwargs) _, res = await self._get_response(msg) api_command.result = _process_output(res) return api_command.result
async def get_temperature(): response = await client.request( Message(code=GET, uri=urljoin(args.coap_server, args.temperature_resource))).response try: return float(response.payload.decode()) except ValueError: return None
def _send_initial_csm(self): my_csm = Message(code=CSM) # this is a tad awkward in construction because the options objects # were designed under the assumption that the option space is constant # for all message codes. block_length = optiontypes.UintOption(2, self._my_max_message_size) my_csm.opt.add_option(block_length) supports_block = optiontypes.UintOption(4, 0) my_csm.opt.add_option(supports_block) self._send_message(my_csm)