pick up wakaama files from https://github.com/eclipse/wakaama
Diff: core/bootstrap.c
- Revision:
- 0:3f48af28ebcd
diff -r 000000000000 -r 3f48af28ebcd core/bootstrap.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/bootstrap.c Wed Apr 19 11:28:00 2017 +0000 @@ -0,0 +1,751 @@ +/******************************************************************************* + * + * Copyright (c) 2015 Sierra Wireless and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Pascal Rieux - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * David Navarro, Intel Corporation - Please refer to git log + * + *******************************************************************************/ + +#include "internals.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#ifdef LWM2M_CLIENT_MODE +#ifdef LWM2M_BOOTSTRAP + +#define PRV_QUERY_BUFFER_LENGTH 200 + + +static void prv_handleResponse(lwm2m_server_t * bootstrapServer, + coap_packet_t * message) +{ + if (COAP_204_CHANGED == message->code) + { + LOG("Received ACK/2.04, Bootstrap pending, waiting for DEL/PUT from BS server..."); + bootstrapServer->status = STATE_BS_PENDING; + } + else + { + bootstrapServer->status = STATE_BS_FAILING; + } +} + +static void prv_handleBootstrapReply(lwm2m_transaction_t * transaction, + void * message) +{ + lwm2m_server_t * bootstrapServer = (lwm2m_server_t *)transaction->userData; + coap_packet_t * coapMessage = (coap_packet_t *)message; + + LOG("Entering"); + + if (bootstrapServer->status == STATE_BS_INITIATED) + { + if (NULL != coapMessage && COAP_TYPE_RST != coapMessage->type) + { + prv_handleResponse(bootstrapServer, coapMessage); + } + else + { + bootstrapServer->status = STATE_BS_FAILING; + } + } +} + +// start a device initiated bootstrap +static void prv_requestBootstrap(lwm2m_context_t * context, + lwm2m_server_t * bootstrapServer) +{ + char query[PRV_QUERY_BUFFER_LENGTH]; + int query_length = 0; + int res; + + LOG("Entering"); + + query_length = utils_stringCopy(query, PRV_QUERY_BUFFER_LENGTH, QUERY_STARTER QUERY_NAME); + if (query_length < 0) + { + bootstrapServer->status = STATE_BS_FAILING; + return; + } + res = utils_stringCopy(query + query_length, PRV_QUERY_BUFFER_LENGTH - query_length, context->endpointName); + if (res < 0) + { + bootstrapServer->status = STATE_BS_FAILING; + return; + } + query_length += res; + + if (bootstrapServer->sessionH == NULL) + { + bootstrapServer->sessionH = lwm2m_connect_server(bootstrapServer->secObjInstID, context->userData); + } + + if (bootstrapServer->sessionH != NULL) + { + lwm2m_transaction_t * transaction = NULL; + + LOG("Bootstrap server connection opened"); + + transaction = transaction_new(bootstrapServer->sessionH, COAP_POST, NULL, NULL, context->nextMID++, 4, NULL); + if (transaction == NULL) + { + bootstrapServer->status = STATE_BS_FAILING; + return; + } + + coap_set_header_uri_path(transaction->message, "/"URI_BOOTSTRAP_SEGMENT); + coap_set_header_uri_query(transaction->message, query); + transaction->callback = prv_handleBootstrapReply; + transaction->userData = (void *)bootstrapServer; + context->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(context->transactionList, transaction); + if (transaction_send(context, transaction) == 0) + { + LOG("CI bootstrap requested to BS server"); + bootstrapServer->status = STATE_BS_INITIATED; + } + } + else + { + LOG("Connecting bootstrap server failed"); + bootstrapServer->status = STATE_BS_FAILED; + } +} + +void bootstrap_step(lwm2m_context_t * contextP, + uint32_t currentTime, + time_t * timeoutP) +{ + lwm2m_server_t * targetP; + + LOG("entering"); + targetP = contextP->bootstrapServerList; + while (targetP != NULL) + { + LOG_ARG("Initial status: %s", STR_STATUS(targetP->status)); + switch (targetP->status) + { + case STATE_DEREGISTERED: + targetP->registration = currentTime + targetP->lifetime; + targetP->status = STATE_BS_HOLD_OFF; + if (*timeoutP > targetP->lifetime) + { + *timeoutP = targetP->lifetime; + } + break; + + case STATE_BS_HOLD_OFF: + if (targetP->registration <= currentTime) + { + prv_requestBootstrap(contextP, targetP); + } + else if (*timeoutP > targetP->registration - currentTime) + { + *timeoutP = targetP->registration - currentTime; + } + break; + + case STATE_BS_INITIATED: + // waiting + break; + + case STATE_BS_PENDING: + // waiting + break; + + case STATE_BS_FINISHING: + if (targetP->sessionH != NULL) + { + lwm2m_close_connection(targetP->sessionH, contextP->userData); + targetP->sessionH = NULL; + } + targetP->status = STATE_BS_FINISHED; + *timeoutP = 0; + break; + + case STATE_BS_FAILING: + if (targetP->sessionH != NULL) + { + lwm2m_close_connection(targetP->sessionH, contextP->userData); + targetP->sessionH = NULL; + } + targetP->status = STATE_BS_FAILED; + *timeoutP = 0; + break; + + default: + break; + } + LOG_ARG("Finalal status: %s", STR_STATUS(targetP->status)); + targetP = targetP->next; + } +} + +coap_status_t bootstrap_handleFinish(lwm2m_context_t * context, + void * fromSessionH) +{ + lwm2m_server_t * bootstrapServer; + + LOG("Entering"); + bootstrapServer = utils_findBootstrapServer(context, fromSessionH); + if (bootstrapServer != NULL + && bootstrapServer->status == STATE_BS_PENDING) + { + LOG("Bootstrap server status changed to STATE_BS_FINISHING"); + bootstrapServer->status = STATE_BS_FINISHING; + return COAP_204_CHANGED; + } + + return COAP_IGNORE; +} + +/* + * Reset the bootstrap servers statuses + * + * TODO: handle LWM2M Servers the client is registered to ? + * + */ +void bootstrap_start(lwm2m_context_t * contextP) +{ + lwm2m_server_t * targetP; + + LOG("Entering"); + targetP = contextP->bootstrapServerList; + while (targetP != NULL) + { + targetP->status = STATE_DEREGISTERED; + if (targetP->sessionH == NULL) + { + targetP->sessionH = lwm2m_connect_server(targetP->secObjInstID, contextP->userData); + } + targetP = targetP->next; + } +} + +/* + * Returns STATE_BS_PENDING if at least one bootstrap is still pending + * Returns STATE_BS_FINISHED if at least one bootstrap succeeded and no bootstrap is pending + * Returns STATE_BS_FAILED if all bootstrap failed. + */ +lwm2m_status_t bootstrap_getStatus(lwm2m_context_t * contextP) +{ + lwm2m_server_t * targetP; + lwm2m_status_t bs_status; + + LOG("Entering"); + targetP = contextP->bootstrapServerList; + bs_status = STATE_BS_FAILED; + + while (targetP != NULL) + { + switch (targetP->status) + { + case STATE_BS_FINISHED: + if (bs_status == STATE_BS_FAILED) + { + bs_status = STATE_BS_FINISHED; + } + break; + + case STATE_BS_HOLD_OFF: + case STATE_BS_INITIATED: + case STATE_BS_PENDING: + case STATE_BS_FINISHING: + bs_status = STATE_BS_PENDING; + break; + + default: + break; + } + targetP = targetP->next; + } + + LOG_ARG("Returned status: %s", STR_STATUS(bs_status)); + + return bs_status; +} + +static coap_status_t prv_checkServerStatus(lwm2m_server_t * serverP) +{ + LOG_ARG("Initial status: %s", STR_STATUS(serverP->status)); + + switch (serverP->status) + { + case STATE_BS_HOLD_OFF: + serverP->status = STATE_BS_PENDING; + LOG_ARG("Status changed to: %s", STR_STATUS(serverP->status)); + break; + + case STATE_BS_INITIATED: + // The ACK was probably lost + serverP->status = STATE_BS_PENDING; + LOG_ARG("Status changed to: %s", STR_STATUS(serverP->status)); + break; + + case STATE_DEREGISTERED: + // server initiated bootstrap + case STATE_BS_PENDING: + // do nothing + break; + + case STATE_BS_FINISHED: + case STATE_BS_FINISHING: + case STATE_BS_FAILING: + case STATE_BS_FAILED: + default: + LOG("Returning COAP_IGNORE"); + return COAP_IGNORE; + } + + return COAP_NO_ERROR; +} + +static void prv_tagServer(lwm2m_context_t * contextP, + uint16_t id) +{ + lwm2m_server_t * targetP; + + targetP = (lwm2m_server_t *)LWM2M_LIST_FIND(contextP->bootstrapServerList, id); + if (targetP == NULL) + { + targetP = (lwm2m_server_t *)LWM2M_LIST_FIND(contextP->serverList, id); + } + if (targetP != NULL) + { + targetP->dirty = true; + } +} + +static void prv_tagAllServer(lwm2m_context_t * contextP, + lwm2m_server_t * serverP) +{ + lwm2m_server_t * targetP; + + targetP = contextP->bootstrapServerList; + while (targetP != NULL) + { + if (targetP != serverP) + { + targetP->dirty = true; + } + targetP = targetP->next; + } + targetP = contextP->serverList; + while (targetP != NULL) + { + targetP->dirty = true; + targetP = targetP->next; + } +} + +coap_status_t bootstrap_handleCommand(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_server_t * serverP, + coap_packet_t * message, + coap_packet_t * response) +{ + coap_status_t result; + lwm2m_media_type_t format; + + LOG_ARG("Code: %02X", message->code); + LOG_URI(uriP); + format = utils_convertMediaType(message->content_type); + + result = prv_checkServerStatus(serverP); + if (result != COAP_NO_ERROR) return result; + + switch (message->code) + { + case COAP_PUT: + { + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + if (object_isInstanceNew(contextP, uriP->objectId, uriP->instanceId)) + { + result = object_create(contextP, uriP, format, message->payload, message->payload_len); + if (COAP_201_CREATED == result) + { + result = COAP_204_CHANGED; + } + } + else + { + result = object_write(contextP, uriP, format, message->payload, message->payload_len); + if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID + && result == COAP_204_CHANGED) + { + prv_tagServer(contextP, uriP->instanceId); + } + } + } + else + { + lwm2m_data_t * dataP = NULL; + int size = 0; + int i; + + if (message->payload_len == 0 || message->payload == 0) + { + result = COAP_400_BAD_REQUEST; + } + else + { + size = lwm2m_data_parse(uriP, message->payload, message->payload_len, format, &dataP); + if (size == 0) + { + result = COAP_500_INTERNAL_SERVER_ERROR; + break; + } + + for (i = 0 ; i < size ; i++) + { + if(dataP[i].type == LWM2M_TYPE_OBJECT_INSTANCE) + { + if (object_isInstanceNew(contextP, uriP->objectId, dataP[i].id)) + { + result = object_createInstance(contextP, uriP, &dataP[i]); + if (COAP_201_CREATED == result) + { + result = COAP_204_CHANGED; + } + } + else + { + result = object_writeInstance(contextP, uriP, &dataP[i]); + if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID + && result == COAP_204_CHANGED) + { + prv_tagServer(contextP, dataP[i].id); + } + } + + if(result != COAP_204_CHANGED) // Stop object create or write when result is error + { + break; + } + } + else + { + result = COAP_400_BAD_REQUEST; + } + } + lwm2m_data_free(size, dataP); + } + } + } + break; + + case COAP_DELETE: + { + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + result = COAP_400_BAD_REQUEST; + } + else + { + result = object_delete(contextP, uriP); + if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID + && result == COAP_202_DELETED) + { + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + prv_tagServer(contextP, uriP->instanceId); + } + else + { + prv_tagAllServer(contextP, NULL); + } + } + } + } + break; + + case COAP_GET: + case COAP_POST: + default: + result = COAP_400_BAD_REQUEST; + break; + } + + if (result == COAP_202_DELETED + || result == COAP_204_CHANGED) + { + if (serverP->status != STATE_BS_PENDING) + { + serverP->status = STATE_BS_PENDING; + contextP->state = STATE_BOOTSTRAPPING; + } + } + LOG_ARG("Server status: %s", STR_STATUS(serverP->status)); + + return result; +} + +coap_status_t bootstrap_handleDeleteAll(lwm2m_context_t * contextP, + void * fromSessionH) +{ + lwm2m_server_t * serverP; + coap_status_t result; + lwm2m_object_t * objectP; + + LOG("Entering"); + serverP = utils_findBootstrapServer(contextP, fromSessionH); + if (serverP == NULL) return COAP_IGNORE; + result = prv_checkServerStatus(serverP); + if (result != COAP_NO_ERROR) return result; + + result = COAP_202_DELETED; + for (objectP = contextP->objectList; objectP != NULL; objectP = objectP->next) + { + lwm2m_uri_t uri; + + memset(&uri, 0, sizeof(lwm2m_uri_t)); + uri.flag = LWM2M_URI_FLAG_OBJECT_ID; + uri.objectId = objectP->objID; + + if (objectP->objID == LWM2M_SECURITY_OBJECT_ID) + { + lwm2m_list_t * instanceP; + + instanceP = objectP->instanceList; + while (NULL != instanceP + && result == COAP_202_DELETED) + { + if (instanceP->id == serverP->secObjInstID) + { + instanceP = instanceP->next; + } + else + { + uri.flag = LWM2M_URI_FLAG_OBJECT_ID | LWM2M_URI_FLAG_INSTANCE_ID; + uri.instanceId = instanceP->id; + result = object_delete(contextP, &uri); + instanceP = objectP->instanceList; + } + } + if (result == COAP_202_DELETED) + { + prv_tagAllServer(contextP, serverP); + } + } + else + { + result = object_delete(contextP, &uri); + if (result == COAP_405_METHOD_NOT_ALLOWED) + { + // Fake a successful deletion for static objects like the Device object. + result = COAP_202_DELETED; + } + } + } + + return result; +} +#endif +#endif + +#ifdef LWM2M_BOOTSTRAP_SERVER_MODE +uint8_t bootstrap_handleRequest(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + void * fromSessionH, + coap_packet_t * message, + coap_packet_t * response) +{ + uint8_t result; + char * name; + + LOG_URI(uriP); + if (contextP->bootstrapCallback == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + if (message->code != COAP_POST) return COAP_400_BAD_REQUEST; + if (message->uri_query == NULL) return COAP_400_BAD_REQUEST; + if (message->payload != NULL) return COAP_400_BAD_REQUEST; + + if (lwm2m_strncmp((char *)message->uri_query->data, QUERY_NAME, QUERY_NAME_LEN) != 0) + { + return COAP_400_BAD_REQUEST; + } + + if (message->uri_query->len == QUERY_NAME_LEN) return COAP_400_BAD_REQUEST; + if (message->uri_query->next != NULL) return COAP_400_BAD_REQUEST; + + name = (char *)lwm2m_malloc(message->uri_query->len - QUERY_NAME_LEN + 1); + if (name == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + memcpy(name, message->uri_query->data + QUERY_NAME_LEN, message->uri_query->len - QUERY_NAME_LEN); + name[message->uri_query->len - QUERY_NAME_LEN] = 0; + + result = contextP->bootstrapCallback(fromSessionH, COAP_NO_ERROR, NULL, name, contextP->bootstrapUserData); + + lwm2m_free(name); + + return result; +} + +void lwm2m_set_bootstrap_callback(lwm2m_context_t * contextP, + lwm2m_bootstrap_callback_t callback, + void * userData) +{ + LOG("Entering"); + contextP->bootstrapCallback = callback; + contextP->bootstrapUserData = userData; +} + +static void prv_resultCallback(lwm2m_transaction_t * transacP, + void * message) +{ + bs_data_t * dataP = (bs_data_t *)transacP->userData; + lwm2m_uri_t * uriP; + + if (dataP->isUri == true) + { + uriP = &dataP->uri; + } + else + { + uriP = NULL; + } + + if (message == NULL) + { + dataP->callback(transacP->peerH, + COAP_503_SERVICE_UNAVAILABLE, + uriP, + NULL, + dataP->userData); + } + else + { + coap_packet_t * packet = (coap_packet_t *)message; + + dataP->callback(transacP->peerH, + packet->code, + uriP, + NULL, + dataP->userData); + } + lwm2m_free(dataP); +} + +int lwm2m_bootstrap_delete(lwm2m_context_t * contextP, + void * sessionH, + lwm2m_uri_t * uriP) +{ + lwm2m_transaction_t * transaction; + bs_data_t * dataP; + + LOG_URI(uriP); + transaction = transaction_new(sessionH, COAP_DELETE, NULL, uriP, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t)); + if (dataP == NULL) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + if (uriP == NULL) + { + dataP->isUri = false; + } + else + { + dataP->isUri = true; + memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t)); + } + dataP->callback = contextP->bootstrapCallback; + dataP->userData = contextP->bootstrapUserData; + + transaction->callback = prv_resultCallback; + transaction->userData = (void *)dataP; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + return transaction_send(contextP, transaction); +} + +int lwm2m_bootstrap_write(lwm2m_context_t * contextP, + void * sessionH, + lwm2m_uri_t * uriP, + lwm2m_media_type_t format, + uint8_t * buffer, + size_t length) +{ + lwm2m_transaction_t * transaction; + bs_data_t * dataP; + + LOG_URI(uriP); + if (uriP == NULL + || buffer == NULL + || length == 0) + { + return COAP_400_BAD_REQUEST; + } + + transaction = transaction_new(sessionH, COAP_PUT, NULL, uriP, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + coap_set_header_content_type(transaction->message, format); + coap_set_payload(transaction->message, buffer, length); + + dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t)); + if (dataP == NULL) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + dataP->isUri = true; + memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t)); + dataP->callback = contextP->bootstrapCallback; + dataP->userData = contextP->bootstrapUserData; + + transaction->callback = prv_resultCallback; + transaction->userData = (void *)dataP; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + return transaction_send(contextP, transaction); +} + +int lwm2m_bootstrap_finish(lwm2m_context_t * contextP, + void * sessionH) +{ + lwm2m_transaction_t * transaction; + bs_data_t * dataP; + + LOG("Entering"); + transaction = transaction_new(sessionH, COAP_POST, NULL, NULL, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + coap_set_header_uri_path(transaction->message, "/"URI_BOOTSTRAP_SEGMENT); + + dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t)); + if (dataP == NULL) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + dataP->isUri = false; + dataP->callback = contextP->bootstrapCallback; + dataP->userData = contextP->bootstrapUserData; + + transaction->callback = prv_resultCallback; + transaction->userData = (void *)dataP; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + return transaction_send(contextP, transaction); +} + +#endif