/* This function is an adaptation of function coap_receive() from Erbium's er-coap-13-engine.c. * Erbium is Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich * All rights reserved. */ void lwm2m_handle_packet(lwm2m_context_t * contextP, uint8_t * buffer, int length, void * fromSessionH) { uint8_t coap_error_code = NO_ERROR; static coap_packet_t message[1]; static coap_packet_t response[1]; LOG("Entering"); coap_error_code = coap_parse_message(message, buffer, (uint16_t)length); if (coap_error_code == NO_ERROR) { LOG_ARG("Parsed: ver %u, type %u, tkl %u, code %u.%.2u, mid %u, Content type: %d", message->version, message->type, message->token_len, message->code >> 5, message->code & 0x1F, message->mid, message->content_type); LOG_ARG("Payload: %.*s", message->payload_len, message->payload); if (message->code >= COAP_GET && message->code <= COAP_DELETE) { uint32_t block_num = 0; uint16_t block_size = REST_MAX_CHUNK_SIZE; uint32_t block_offset = 0; int64_t new_offset = 0; /* prepare response */ if (message->type == COAP_TYPE_CON) { /* Reliable CON requests are answered with an ACK. */ coap_init_message(response, COAP_TYPE_ACK, COAP_205_CONTENT, message->mid); } else { /* Unreliable NON requests are answered with a NON as well. */ coap_init_message(response, COAP_TYPE_NON, COAP_205_CONTENT, contextP->nextMID++); } /* mirror token */ if (message->token_len) { coap_set_header_token(response, message->token, message->token_len); } /* get offset for blockwise transfers */ if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset)) { LOG_ARG("Blockwise: block request %u (%u/%u) @ %u bytes", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset); block_size = MIN(block_size, REST_MAX_CHUNK_SIZE); new_offset = block_offset; } /* handle block1 option */ if (IS_OPTION(message, COAP_OPTION_BLOCK1)) { #ifdef LWM2M_CLIENT_MODE // get server lwm2m_server_t * serverP; serverP = utils_findServer(contextP, fromSessionH); #ifdef LWM2M_BOOTSTRAP if (serverP == NULL) { serverP = utils_findBootstrapServer(contextP, fromSessionH); } #endif if (serverP == NULL) { coap_error_code = COAP_500_INTERNAL_SERVER_ERROR; } else { uint32_t block1_num; uint8_t block1_more; uint16_t block1_size; uint8_t * complete_buffer = NULL; size_t complete_buffer_size; // parse block1 header coap_get_header_block1(message, &block1_num, &block1_more, &block1_size, NULL); LOG_ARG("Blockwise: block1 request NUM %u (SZX %u/ SZX Max%u) MORE %u", block1_num, block1_size, REST_MAX_CHUNK_SIZE, block1_more); // handle block 1 coap_error_code = coap_block1_handler(&serverP->block1Data, message->mid, message->payload, message->payload_len, block1_size, block1_num, block1_more, &complete_buffer, &complete_buffer_size); // if payload is complete, replace it in the coap message. if (coap_error_code == NO_ERROR) { message->payload = complete_buffer; message->payload_len = complete_buffer_size; } else if (coap_error_code == COAP_231_CONTINUE) { block1_size = MIN(block1_size, REST_MAX_CHUNK_SIZE); coap_set_header_block1(response,block1_num, block1_more,block1_size); } } #else coap_error_code = COAP_501_NOT_IMPLEMENTED; #endif } if (coap_error_code == NO_ERROR) { coap_error_code = handle_request(contextP, fromSessionH, message, response); } if (coap_error_code==NO_ERROR) { /* Save original payload pointer for later freeing. Payload in response may be updated. */ uint8_t *payload = response->payload; if ( IS_OPTION(message, COAP_OPTION_BLOCK2) ) { /* unchanged new_offset indicates that resource is unaware of blockwise transfer */ if (new_offset==block_offset) { LOG_ARG("Blockwise: unaware resource with payload length %u/%u", response->payload_len, block_size); if (block_offset >= response->payload_len) { LOG("handle_incoming_data(): block_offset >= response->payload_len"); response->code = COAP_402_BAD_OPTION; coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */ } else { coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size); coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size)); } /* if (valid offset) */ } else { /* resource provides chunk-wise data */ LOG_ARG("Blockwise: blockwise resource, new offset %d", (int) new_offset); coap_set_header_block2(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size); if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size); } /* if (resource aware of blockwise) */ } else if (new_offset!=0) { LOG_ARG("Blockwise: no block option for blockwise resource, using block size %u", REST_MAX_CHUNK_SIZE); coap_set_header_block2(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE); coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE)); } /* if (blockwise request) */ coap_error_code = message_send(contextP, response, fromSessionH); lwm2m_free(payload); response->payload = NULL; response->payload_len = 0; } else if (coap_error_code != COAP_IGNORE) { if (1 == coap_set_status_code(response, coap_error_code)) { coap_error_code = message_send(contextP, response, fromSessionH); } } } else { /* Responses */ switch (message->type) { case COAP_TYPE_NON: case COAP_TYPE_CON: { bool done = transaction_handleResponse(contextP, fromSessionH, message, response); #ifdef LWM2M_SERVER_MODE if (!done && IS_OPTION(message, COAP_OPTION_OBSERVE) && ((message->code == COAP_204_CHANGED) || (message->code == COAP_205_CONTENT))) { done = observe_handleNotify(contextP, fromSessionH, message, response); } #endif if (!done && message->type == COAP_TYPE_CON ) { coap_init_message(response, COAP_TYPE_ACK, 0, message->mid); coap_error_code = message_send(contextP, response, fromSessionH); } } break; case COAP_TYPE_RST: /* Cancel possible subscriptions. */ handle_reset(contextP, fromSessionH, message); transaction_handleResponse(contextP, fromSessionH, message, NULL); break; case COAP_TYPE_ACK: transaction_handleResponse(contextP, fromSessionH, message, NULL); break; default: break; } } /* Request or Response */ coap_free_header(message); } /* if (parsed correctly) */
/*----------------------------------------------------------------------------*/ void tasks_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { const char *path; char name[TRES_TASK_NAME_MAX_LEN]; char subres[SUBRES_LEN]; int len; int retv; #if TRES_COPPER_WORKAROUND uint32_t num = 0; uint8_t more = 0; uint16_t size = 0; uint32_t b1_offset = 0; int i; tres_res_t *task; int32_t strpos = 0; int16_t bufpos = 0; if(coap_get_header_block1(request, &num, &more, &size, &b1_offset) && more == 0) { coap_set_header_block1(response, num, more, size); } #endif len = REST.get_url(request, &path); retv = parse_tasks_path(path, len, name, subres); if(retv < 0) { PRINTF("Invalid path: %d\n", retv); REST.set_response_status(response, REST.status.BAD_REQUEST); return; } if(*name) { PRINTF("name: %s\n", name); task_name_handler(request, response, buffer, preferred_size, offset, name, subres); return; } for(i = 0; i < tasks_mem.num; i++) { task = &tasks_mem_memb_mem[i]; if(task->name[0]) { /* </tasks/[task_name] */ if(strpos == 0) { BLOCK_SPRINTF("</" TRES_BASE_PATH "/", strpos, bufpos, offset, preferred_size, buffer); } else { BLOCK_SPRINTF(",</" TRES_BASE_PATH "/", strpos, bufpos, offset, preferred_size, buffer); } BLOCK_SPRINTF(task->name, strpos, bufpos, offset, preferred_size, buffer); BLOCK_SPRINTF(">;ct=40", strpos, bufpos, offset, preferred_size, buffer); } } REST.set_response_payload(response, buffer, bufpos); REST.set_header_content_type(response, APPLICATION_LINK_FORMAT); if(bufpos >= preferred_size || *offset > 0) { if(i == tasks_mem.num) { *offset = -1; } else { *offset += preferred_size; } } }
/*----------------------------------------------------------------------------*/ void pf_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset, tres_res_t *task) { rest_resource_flags_t method; uint32_t num = 0; uint8_t more = 0; uint16_t size = 0; uint32_t b1_offset = 0; uint16_t plen; const uint8_t *payload; method = REST.get_method_type(request); PRINTF("Method %d\n", method); switch (method) { case METHOD_GET: //TODO: retrieve python bytecode REST.set_response_status(response, REST.status.NOT_IMPLEMENTED); break; case METHOD_PUT: if(coap_get_header_block1(request, &num, &more, &size, &b1_offset)) { coap_set_header_block1(response, num, more, size); } PRINTF("Request on /pf: num = %"PRIu32", more = %"PRIu8", size = %"PRIu16 ", offset = %"PRIu32"\n", num, more, size, b1_offset); // if it's the first packet, stop input resource monitoring if(b1_offset == 0) { tres_stop_monitoring(task); // if the user is actually updating the PF, clear it first if(task->pf_img) { tres_mem_pf_clear(task->pf_img); task_reset_state(task); } } plen = coap_get_payload(request, &payload); PRINTF("Payload len: %d\n", plen); #if TRES_COPPER_WORKAROUND // if no block-wise transfer and payload len equal to maximum size, then // we probably need block wise transfer. FIXME: that may be not true if(size == 0 && plen == REST_MAX_CHUNK_SIZE) { more = 1; size = REST_MAX_CHUNK_SIZE; coap_set_header_block1(response, num, more, size); } #endif // if it's the first packet, allocate a memory slot if(b1_offset == 0) { // ... but, if the first byte is not 0x0a than it is not python bytecode if(payload[0] != 0x0a) { REST.set_response_status(response, REST.status.BAD_REQUEST); return; } task->sid = tres_mem_pf_store_start(); if(task->sid < 0) { PRINTF("No memory slot available\n"); // TODO: we should return a server error code return; } } // store the current block if(tres_mem_pf_store_step(task->sid, payload, plen) < 0) { PRINTF("PF too big\n"); return; } // if it's the final block, finalize the memory slot if(more == 0) { task->pf_img = tres_mem_pf_store_done(task->sid); PRINTF("task->pf_img: %p\n", task->pf_img); REST.set_response_status(response, REST.status.CHANGED); } break; case METHOD_POST: REST.set_response_status(response, REST.status.METHOD_NOT_ALLOWED); break; case METHOD_DELETE: if(task->pf_img) { tres_stop_monitoring(task); tres_mem_pf_clear(task->pf_img); task_reset_state(task); task->pf_img = NULL; } REST.set_response_status(response, REST.status.DELETED); break; default: break; } }
void mirror_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { /* The ETag and Token is copied to the header. */ uint8_t opaque[] = {0x0A, 0xBC, 0xDE}; /* Strings are not copied and should be static or in program memory (char *str = "string in .text";). * They must be '\0'-terminated as the setters use strlen(). */ static char location[] = {'/','f','/','a','?','k','&','e', 0}; /* Getter for the header option Content-Type. If the option is not set, text/plain is returned by default. */ unsigned int content_type = REST.get_header_content_type(request); /* The other getters copy the value (or string/array pointer) to the given pointers and return 1 for success or the length of strings/arrays. */ uint32_t max_age = 0; const char *str = ""; uint32_t observe = 0; const uint8_t *bytes = NULL; uint32_t block_num = 0; uint8_t block_more = 0; uint16_t block_size = 0; const char *query = ""; int len = 0; /* Mirror the received header options in the response payload. Unsupported getters (e.g., rest_get_header_observe() with HTTP) will return 0. */ int strpos = 0; /* snprintf() counts the terminating '\0' to the size parameter. * Add +1 to fill the complete buffer. * The additional byte is taken care of by allocating REST_MAX_CHUNK_SIZE+1 bytes in the REST framework. */ strpos += snprintf((char *)buffer, REST_MAX_CHUNK_SIZE+1, "CT %u\n", content_type); /* Some getters such as for ETag or Location are omitted, as these options should not appear in a request. * Max-Age might appear in HTTP requests or used for special purposes in CoAP. */ if (REST.get_header_max_age(request, &max_age)) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "MA %lu\n", max_age); } if ((len = REST.get_header_host(request, &str))) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "UH %.*s\n", len, str); } /* CoAP-specific example: actions not required for normal RESTful Web service. */ #if WITH_COAP > 1 if (coap_get_header_observe(request, &observe)) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "Ob %lu\n", observe); } if ((len = coap_get_header_token(request, &bytes))) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "To 0x"); int index = 0; for (index = 0; index<len; ++index) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "%02X", bytes[index]); } strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "\n"); } if ((len = coap_get_header_etag(request, &bytes))) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "ET 0x"); int index = 0; for (index = 0; index<len; ++index) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "%02X", bytes[index]); } strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "\n"); } if ((len = coap_get_header_uri_path(request, &str))) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "UP "); strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "%.*s\n", len, str); } #if WITH_COAP == 3 if ((len = coap_get_header_location(request, &str))) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "Lo %.*s\n", len, str); } if (coap_get_header_block(request, &block_num, &block_more, &block_size, NULL)) /* This getter allows NULL pointers to get only a subset of the block parameters. */ { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "Bl %lu%s (%u)\n", block_num, block_more ? "+" : "", block_size); } #elif WITH_COAP >= 5 if ((len = coap_get_header_location_path(request, &str))) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "LP %.*s\n", len, str); } if ((len = coap_get_header_location_query(request, &str))) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "LQ %.*s\n", len, str); } if (coap_get_header_block2(request, &block_num, &block_more, &block_size, NULL)) /* This getter allows NULL pointers to get only a subset of the block parameters. */ { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "B2 %lu%s (%u)\n", block_num, block_more ? "+" : "", block_size); } if (coap_get_header_block1(request, &block_num, &block_more, &block_size, NULL)) /* This getter allows NULL pointers to get only a subset of the block parameters. */ { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "B1 %lu%s (%u)\n", block_num, block_more ? "+" : "", block_size); } #if WITH_COAP >= 7 #endif #endif #endif /* CoAP-specific example */ if ((len = REST.get_query(request, &query))) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "Qu %.*s\n", len, query); } if ((len = REST.get_request_payload(request, &bytes))) { strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "%.*s", len, bytes); } if (strpos == REST_MAX_CHUNK_SIZE) { buffer[REST_MAX_CHUNK_SIZE-1] = 0xBB; /* '»' to indicate truncation */ } REST.set_response_payload(response, buffer, strpos); PRINTF("/mirror options received: %s\n", buffer); /* Set dummy header options for response. Like getters, some setters are not implemented for HTTP and have no effect. */ REST.set_header_content_type(response, REST.type.TEXT_PLAIN); REST.set_header_max_age(response, 10); /* For HTTP, browsers will not re-request the page for 10 seconds. CoAP action depends on the client. */ REST.set_header_etag(response, opaque, 2); REST.set_header_location(response, location); /* Initial slash is omitted by framework */ /* CoAP-specific example: actions not required for normal RESTful Web service. */ #if WITH_COAP > 1 coap_set_header_uri_host(response, "tiki"); coap_set_header_observe(response, 10); #if WITH_COAP == 3 coap_set_header_block(response, 42, 0, 64); /* The block option might be overwritten by the framework when blockwise transfer is requested. */ #elif WITH_COAP >= 5 coap_set_header_proxy_uri(response, "ftp://x"); coap_set_header_block2(response, 42, 0, 64); /* The block option might be overwritten by the framework when blockwise transfer is requested. */ coap_set_header_block1(response, 23, 0, 16); #if WITH_COAP >= 7 coap_set_header_accept(response, TEXT_PLAIN); coap_set_header_if_none_match(response); #endif #endif #endif /* CoAP-specific example */ }