ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/services/lwm2m/core/bootstrap.c b/marvell/services/lwm2m/core/bootstrap.c
new file mode 100644
index 0000000..2bd6d82
--- /dev/null
+++ b/marvell/services/lwm2m/core/bootstrap.c
@@ -0,0 +1,421 @@
+/*******************************************************************************
+ *
+ * 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_BOOTSTRAP
+#ifdef LWM2M_CLIENT_MODE
+
+#define PRV_QUERY_BUFFER_LENGTH 200
+
+static void prv_handleBootstrapReply(lwm2m_transaction_t * transaction, void * message)
+{
+    LOG("[BOOTSTRAP] Handling bootstrap reply...\r\n");
+    lwm2m_context_t * context = (lwm2m_context_t *)transaction->userData;
+    coap_packet_t * coapMessage = (coap_packet_t *)message;
+    if (NULL != coapMessage && COAP_TYPE_RST != coapMessage->type)
+    {
+        handle_bootstrap_response(context, coapMessage, NULL);
+    }
+    else
+    {
+        bootstrap_failed(context);
+    }
+}
+
+// start a device initiated bootstrap
+int bootstrap_initiating_request(lwm2m_context_t * context)
+{
+    char query[PRV_QUERY_BUFFER_LENGTH];
+    int query_length = 0;
+    lwm2m_transaction_t * transaction = NULL;
+
+    query_length = snprintf(query, sizeof(query), "?ep=%s", context->endpointName);
+    if (query_length <= 1)
+    {
+        return INTERNAL_SERVER_ERROR_5_00;
+    }
+
+    // find the first bootstrap server
+    lwm2m_server_t * bootstrapServer = context->bootstrapServerList;
+    while (bootstrapServer != NULL)
+    {
+        if (bootstrapServer->sessionH == NULL)
+        {
+            bootstrapServer->sessionH = context->connectCallback(bootstrapServer->secObjInstID, context->userData);
+        }
+        if (bootstrapServer->sessionH != NULL)
+        {
+            LOG("[BOOTSTRAP] Bootstrap session starting...\r\n");
+            transaction = transaction_new(COAP_TYPE_CON, COAP_POST, NULL, NULL, context->nextMID++, 4, NULL, ENDPOINT_SERVER, (void *)bootstrapServer);
+            if (transaction == NULL)
+            {
+                return INTERNAL_SERVER_ERROR_5_00;
+            }
+            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 *)context;
+            context->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(context->transactionList, transaction);
+            if (transaction_send(context, transaction) == 0)
+            {
+                LOG("[BOOTSTRAP] DI bootstrap requested to BS server\r\n");
+                context->bsState = BOOTSTRAP_INITIATED;
+                reset_bootstrap_timer(context);
+            }
+        }
+        else
+        {
+            LOG("No bootstrap session handler found\r\n");
+        }
+        bootstrapServer = bootstrapServer->next;
+    }
+    return NO_ERROR;
+}
+
+void handle_bootstrap_response(lwm2m_context_t * context,
+        coap_packet_t * message,
+        void * fromSessionH)
+{
+    if (COAP_204_CHANGED == message->code)
+    {
+        context->bsState = BOOTSTRAP_PENDING;
+        LOG("[BOOTSTRAP] Received ACK/2.04, Bootstrap pending, waiting for DEL/PUT from BS server...\r\n");
+        reset_bootstrap_timer(context);
+    }
+    else
+    {
+        bootstrap_failed(context);
+    }
+}
+
+void bootstrap_failed(lwm2m_context_t * context)
+{
+    reset_bootstrap_timer(context);
+    context->bsState = BOOTSTRAP_FAILED;
+    LOG("[BOOTSTRAP] Bootstrap failed\r\n");
+}
+
+void reset_bootstrap_timer(lwm2m_context_t * context)
+{
+    context->bsStart = lwm2m_gettime();
+}
+
+void update_bootstrap_state(lwm2m_context_t * context,
+        uint32_t currentTime,
+        time_t* timeoutP)
+{
+    if (context->bsState == BOOTSTRAP_REQUESTED)
+    {
+        context->bsState = BOOTSTRAP_CLIENT_HOLD_OFF;
+        context->bsStart = currentTime;
+        LOG("[BOOTSTRAP] Bootstrap requested at: %lu, now waiting during ClientHoldOffTime...\r\n",
+                (unsigned long)context->bsStart);
+    }
+    if (context->bsState == BOOTSTRAP_CLIENT_HOLD_OFF)
+    {
+        lwm2m_server_t * bootstrapServer = context->bootstrapServerList;
+        if (bootstrapServer != NULL)
+        {
+            // get ClientHoldOffTime from bootstrapServer->lifetime
+            // (see objects.c => object_getServers())
+            int32_t timeToBootstrap = (context->bsStart + bootstrapServer->lifetime) - currentTime;
+            LOG("[BOOTSTRAP] ClientHoldOffTime %ld\r\n", (long)timeToBootstrap);
+            if (0 >= timeToBootstrap)
+            {
+                bootstrap_initiating_request(context);
+            }
+            else if (timeToBootstrap < *timeoutP)
+            {
+                *timeoutP = timeToBootstrap;
+            }
+        }
+        else
+        {
+            bootstrap_failed(context);
+        }
+    }
+    if (context->bsState == BOOTSTRAP_PENDING)
+    {
+        // Use COAP_DEFAULT_MAX_AGE according proposal in
+        // https://github.com/OpenMobileAlliance/OMA-LwM2M-Public-Review/issues/35
+        int32_t timeToBootstrap = (context->bsStart + COAP_DEFAULT_MAX_AGE) - currentTime;
+        LOG("[BOOTSTRAP] Pending %ld\r\n", (long)timeToBootstrap);
+        if (0 >= timeToBootstrap)
+        {
+            // Time out and no error => bootstrap OK
+            // TODO: add smarter condition for bootstrap success:
+            // 1) security object contains at least one bootstrap server
+            // 2) there are coherent configurations for provisioned DM servers
+            // if these conditions are not met, then bootstrap has failed and previous security
+            // and server object configurations might be restored by client
+            LOG("\r\n[BOOTSTRAP] Bootstrap finished at: %lu (difftime: %lu s)\r\n",
+                    (unsigned long)currentTime, (unsigned long)(currentTime - context->bsStart));
+            context->bsState = BOOTSTRAP_FINISHED;
+            context->bsStart = currentTime;
+            *timeoutP = 1;
+        }
+        else if (timeToBootstrap < *timeoutP)
+        {
+            *timeoutP = timeToBootstrap;
+        }
+    }
+    else if (context->bsState == BOOTSTRAP_FINISHED)
+    {
+        context->bsStart = currentTime;
+        if (0 <= lwm2m_start(context))
+        {
+            context->bsState = BOOTSTRAPPED;
+        }
+        else
+        {
+            bootstrap_failed(context);
+        }
+        // during next step, lwm2m_update_registrations will connect the client to DM server
+    }
+    if (BOOTSTRAP_FAILED == context->bsState)
+    {
+        lwm2m_server_t * bootstrapServer = context->bootstrapServerList;
+        if (bootstrapServer != NULL)
+        {
+            // get ClientHoldOffTime from bootstrapServer->lifetime
+            // (see objects.c => object_getServers())
+            int32_t timeToBootstrap = (context->bsStart + bootstrapServer->lifetime) - currentTime;
+            LOG("[BOOTSTRAP] Bootstrap failed: %lu, now waiting during ClientHoldOffTime %ld ...\r\n",
+                    (unsigned long)context->bsStart, (long)timeToBootstrap);
+            if (0 >= timeToBootstrap)
+            {
+                context->bsState = NOT_BOOTSTRAPPED;
+                context->bsStart = currentTime;
+                LOG("[BOOTSTRAP] Bootstrap failed: retry ...\r\n");
+                *timeoutP = 1;
+            }
+            else if (timeToBootstrap < *timeoutP)
+            {
+                *timeoutP = timeToBootstrap;
+            }
+        }
+    }
+}
+
+coap_status_t handle_bootstrap_finish(lwm2m_context_t * context,
+                                      void * fromSessionH)
+{
+    if (context->bsState == BOOTSTRAP_PENDING)
+    {
+        context->bsState = BOOTSTRAP_FINISHED;
+        return COAP_204_CHANGED;
+    }
+
+    return COAP_IGNORE;
+}
+#endif
+
+#endif
+
+#ifdef LWM2M_BOOTSTRAP_SERVER_MODE
+uint8_t handle_bootstrap_request(lwm2m_context_t * contextP,
+                                 lwm2m_uri_t * uriP,
+                                 void * fromSessionH,
+                                 coap_packet_t * message,
+                                 coap_packet_t * response)
+{
+    uint8_t result;
+    char * name;
+
+    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_TEMPLATE, QUERY_LENGTH) != 0)
+    {
+        return COAP_400_BAD_REQUEST;
+    }
+
+    if (message->uri_query->len == QUERY_LENGTH) 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_LENGTH + 1);
+    if (name == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    memcpy(name, message->uri_query->data + QUERY_LENGTH, message->uri_query->len - QUERY_LENGTH);
+    name[message->uri_query->len - QUERY_LENGTH] = 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)
+{
+    contextP->bootstrapCallback = callback;
+    contextP->bootstrapUserData = userData;
+}
+
+static void bs_result_callback(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->peerP,
+                        COAP_503_SERVICE_UNAVAILABLE,
+                        uriP,
+                        NULL,
+                        dataP->userData);
+    }
+    else
+    {
+        coap_packet_t * packet = (coap_packet_t *)message;
+
+        dataP->callback(transacP->peerP,
+                        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;
+
+    transaction = transaction_new(COAP_TYPE_CON, COAP_DELETE, NULL, uriP, contextP->nextMID++, 4, NULL, ENDPOINT_UNKNOWN, sessionH);
+    if (transaction == NULL) return INTERNAL_SERVER_ERROR_5_00;
+
+    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 = bs_result_callback;
+    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,
+                          uint8_t * buffer,
+                          size_t length)
+{
+    lwm2m_transaction_t * transaction;
+    bs_data_t * dataP;
+
+    if (uriP == NULL
+    || buffer == NULL
+    || length == 0)
+    {
+        return COAP_400_BAD_REQUEST;
+    }
+
+    transaction = transaction_new(COAP_TYPE_CON, COAP_PUT, NULL, uriP, contextP->nextMID++, 4, NULL, ENDPOINT_UNKNOWN, sessionH);
+    if (transaction == NULL) return INTERNAL_SERVER_ERROR_5_00;
+
+    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 = bs_result_callback;
+    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;
+
+    transaction = transaction_new(COAP_TYPE_CON, COAP_PUT, NULL, NULL, contextP->nextMID++, 4, NULL, ENDPOINT_UNKNOWN, sessionH);
+    if (transaction == NULL) return INTERNAL_SERVER_ERROR_5_00;
+
+    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 = bs_result_callback;
+    transaction->userData = (void *)dataP;
+
+    contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
+
+    return transaction_send(contextP, transaction);
+}
+
+#endif