blob: 2bd6d823aee396fa936feacf4cfb89d1da735ece [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*******************************************************************************
2 *
3 * Copyright (c) 2015 Sierra Wireless and others.
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * and Eclipse Distribution License v1.0 which accompany this distribution.
7 *
8 * The Eclipse Public License is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 * The Eclipse Distribution License is available at
11 * http://www.eclipse.org/org/documents/edl-v10.php.
12 *
13 * Contributors:
14 * Pascal Rieux - Please refer to git log
15 * Bosch Software Innovations GmbH - Please refer to git log
16 * David Navarro, Intel Corporation - Please refer to git log
17 *
18 *******************************************************************************/
19
20#include "internals.h"
21
22#include <stdlib.h>
23#include <string.h>
24#include <stdio.h>
25
26#ifdef LWM2M_BOOTSTRAP
27#ifdef LWM2M_CLIENT_MODE
28
29#define PRV_QUERY_BUFFER_LENGTH 200
30
31static void prv_handleBootstrapReply(lwm2m_transaction_t * transaction, void * message)
32{
33 LOG("[BOOTSTRAP] Handling bootstrap reply...\r\n");
34 lwm2m_context_t * context = (lwm2m_context_t *)transaction->userData;
35 coap_packet_t * coapMessage = (coap_packet_t *)message;
36 if (NULL != coapMessage && COAP_TYPE_RST != coapMessage->type)
37 {
38 handle_bootstrap_response(context, coapMessage, NULL);
39 }
40 else
41 {
42 bootstrap_failed(context);
43 }
44}
45
46// start a device initiated bootstrap
47int bootstrap_initiating_request(lwm2m_context_t * context)
48{
49 char query[PRV_QUERY_BUFFER_LENGTH];
50 int query_length = 0;
51 lwm2m_transaction_t * transaction = NULL;
52
53 query_length = snprintf(query, sizeof(query), "?ep=%s", context->endpointName);
54 if (query_length <= 1)
55 {
56 return INTERNAL_SERVER_ERROR_5_00;
57 }
58
59 // find the first bootstrap server
60 lwm2m_server_t * bootstrapServer = context->bootstrapServerList;
61 while (bootstrapServer != NULL)
62 {
63 if (bootstrapServer->sessionH == NULL)
64 {
65 bootstrapServer->sessionH = context->connectCallback(bootstrapServer->secObjInstID, context->userData);
66 }
67 if (bootstrapServer->sessionH != NULL)
68 {
69 LOG("[BOOTSTRAP] Bootstrap session starting...\r\n");
70 transaction = transaction_new(COAP_TYPE_CON, COAP_POST, NULL, NULL, context->nextMID++, 4, NULL, ENDPOINT_SERVER, (void *)bootstrapServer);
71 if (transaction == NULL)
72 {
73 return INTERNAL_SERVER_ERROR_5_00;
74 }
75 coap_set_header_uri_path(transaction->message, "/"URI_BOOTSTRAP_SEGMENT);
76 coap_set_header_uri_query(transaction->message, query);
77 transaction->callback = prv_handleBootstrapReply;
78 transaction->userData = (void *)context;
79 context->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(context->transactionList, transaction);
80 if (transaction_send(context, transaction) == 0)
81 {
82 LOG("[BOOTSTRAP] DI bootstrap requested to BS server\r\n");
83 context->bsState = BOOTSTRAP_INITIATED;
84 reset_bootstrap_timer(context);
85 }
86 }
87 else
88 {
89 LOG("No bootstrap session handler found\r\n");
90 }
91 bootstrapServer = bootstrapServer->next;
92 }
93 return NO_ERROR;
94}
95
96void handle_bootstrap_response(lwm2m_context_t * context,
97 coap_packet_t * message,
98 void * fromSessionH)
99{
100 if (COAP_204_CHANGED == message->code)
101 {
102 context->bsState = BOOTSTRAP_PENDING;
103 LOG("[BOOTSTRAP] Received ACK/2.04, Bootstrap pending, waiting for DEL/PUT from BS server...\r\n");
104 reset_bootstrap_timer(context);
105 }
106 else
107 {
108 bootstrap_failed(context);
109 }
110}
111
112void bootstrap_failed(lwm2m_context_t * context)
113{
114 reset_bootstrap_timer(context);
115 context->bsState = BOOTSTRAP_FAILED;
116 LOG("[BOOTSTRAP] Bootstrap failed\r\n");
117}
118
119void reset_bootstrap_timer(lwm2m_context_t * context)
120{
121 context->bsStart = lwm2m_gettime();
122}
123
124void update_bootstrap_state(lwm2m_context_t * context,
125 uint32_t currentTime,
126 time_t* timeoutP)
127{
128 if (context->bsState == BOOTSTRAP_REQUESTED)
129 {
130 context->bsState = BOOTSTRAP_CLIENT_HOLD_OFF;
131 context->bsStart = currentTime;
132 LOG("[BOOTSTRAP] Bootstrap requested at: %lu, now waiting during ClientHoldOffTime...\r\n",
133 (unsigned long)context->bsStart);
134 }
135 if (context->bsState == BOOTSTRAP_CLIENT_HOLD_OFF)
136 {
137 lwm2m_server_t * bootstrapServer = context->bootstrapServerList;
138 if (bootstrapServer != NULL)
139 {
140 // get ClientHoldOffTime from bootstrapServer->lifetime
141 // (see objects.c => object_getServers())
142 int32_t timeToBootstrap = (context->bsStart + bootstrapServer->lifetime) - currentTime;
143 LOG("[BOOTSTRAP] ClientHoldOffTime %ld\r\n", (long)timeToBootstrap);
144 if (0 >= timeToBootstrap)
145 {
146 bootstrap_initiating_request(context);
147 }
148 else if (timeToBootstrap < *timeoutP)
149 {
150 *timeoutP = timeToBootstrap;
151 }
152 }
153 else
154 {
155 bootstrap_failed(context);
156 }
157 }
158 if (context->bsState == BOOTSTRAP_PENDING)
159 {
160 // Use COAP_DEFAULT_MAX_AGE according proposal in
161 // https://github.com/OpenMobileAlliance/OMA-LwM2M-Public-Review/issues/35
162 int32_t timeToBootstrap = (context->bsStart + COAP_DEFAULT_MAX_AGE) - currentTime;
163 LOG("[BOOTSTRAP] Pending %ld\r\n", (long)timeToBootstrap);
164 if (0 >= timeToBootstrap)
165 {
166 // Time out and no error => bootstrap OK
167 // TODO: add smarter condition for bootstrap success:
168 // 1) security object contains at least one bootstrap server
169 // 2) there are coherent configurations for provisioned DM servers
170 // if these conditions are not met, then bootstrap has failed and previous security
171 // and server object configurations might be restored by client
172 LOG("\r\n[BOOTSTRAP] Bootstrap finished at: %lu (difftime: %lu s)\r\n",
173 (unsigned long)currentTime, (unsigned long)(currentTime - context->bsStart));
174 context->bsState = BOOTSTRAP_FINISHED;
175 context->bsStart = currentTime;
176 *timeoutP = 1;
177 }
178 else if (timeToBootstrap < *timeoutP)
179 {
180 *timeoutP = timeToBootstrap;
181 }
182 }
183 else if (context->bsState == BOOTSTRAP_FINISHED)
184 {
185 context->bsStart = currentTime;
186 if (0 <= lwm2m_start(context))
187 {
188 context->bsState = BOOTSTRAPPED;
189 }
190 else
191 {
192 bootstrap_failed(context);
193 }
194 // during next step, lwm2m_update_registrations will connect the client to DM server
195 }
196 if (BOOTSTRAP_FAILED == context->bsState)
197 {
198 lwm2m_server_t * bootstrapServer = context->bootstrapServerList;
199 if (bootstrapServer != NULL)
200 {
201 // get ClientHoldOffTime from bootstrapServer->lifetime
202 // (see objects.c => object_getServers())
203 int32_t timeToBootstrap = (context->bsStart + bootstrapServer->lifetime) - currentTime;
204 LOG("[BOOTSTRAP] Bootstrap failed: %lu, now waiting during ClientHoldOffTime %ld ...\r\n",
205 (unsigned long)context->bsStart, (long)timeToBootstrap);
206 if (0 >= timeToBootstrap)
207 {
208 context->bsState = NOT_BOOTSTRAPPED;
209 context->bsStart = currentTime;
210 LOG("[BOOTSTRAP] Bootstrap failed: retry ...\r\n");
211 *timeoutP = 1;
212 }
213 else if (timeToBootstrap < *timeoutP)
214 {
215 *timeoutP = timeToBootstrap;
216 }
217 }
218 }
219}
220
221coap_status_t handle_bootstrap_finish(lwm2m_context_t * context,
222 void * fromSessionH)
223{
224 if (context->bsState == BOOTSTRAP_PENDING)
225 {
226 context->bsState = BOOTSTRAP_FINISHED;
227 return COAP_204_CHANGED;
228 }
229
230 return COAP_IGNORE;
231}
232#endif
233
234#endif
235
236#ifdef LWM2M_BOOTSTRAP_SERVER_MODE
237uint8_t handle_bootstrap_request(lwm2m_context_t * contextP,
238 lwm2m_uri_t * uriP,
239 void * fromSessionH,
240 coap_packet_t * message,
241 coap_packet_t * response)
242{
243 uint8_t result;
244 char * name;
245
246 if (contextP->bootstrapCallback == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
247 if (message->code != COAP_POST) return COAP_400_BAD_REQUEST;
248 if (message->uri_query == NULL) return COAP_400_BAD_REQUEST;
249 if (message->payload != NULL) return COAP_400_BAD_REQUEST;
250
251 if (lwm2m_strncmp((char *)message->uri_query->data, QUERY_TEMPLATE, QUERY_LENGTH) != 0)
252 {
253 return COAP_400_BAD_REQUEST;
254 }
255
256 if (message->uri_query->len == QUERY_LENGTH) return COAP_400_BAD_REQUEST;
257 if (message->uri_query->next != NULL) return COAP_400_BAD_REQUEST;
258
259 name = (char *)lwm2m_malloc(message->uri_query->len - QUERY_LENGTH + 1);
260 if (name == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
261
262 memcpy(name, message->uri_query->data + QUERY_LENGTH, message->uri_query->len - QUERY_LENGTH);
263 name[message->uri_query->len - QUERY_LENGTH] = 0;
264
265 result = contextP->bootstrapCallback(fromSessionH, COAP_NO_ERROR, NULL, name, contextP->bootstrapUserData);
266
267 lwm2m_free(name);
268
269 return result;
270}
271
272void lwm2m_set_bootstrap_callback(lwm2m_context_t * contextP,
273 lwm2m_bootstrap_callback_t callback,
274 void * userData)
275{
276 contextP->bootstrapCallback = callback;
277 contextP->bootstrapUserData = userData;
278}
279
280static void bs_result_callback(lwm2m_transaction_t * transacP,
281 void * message)
282{
283 bs_data_t * dataP = (bs_data_t *)transacP->userData;
284 lwm2m_uri_t * uriP;
285
286 if (dataP->isUri == true)
287 {
288 uriP = &dataP->uri;
289 }
290 else
291 {
292 uriP = NULL;
293 }
294
295 if (message == NULL)
296 {
297 dataP->callback(transacP->peerP,
298 COAP_503_SERVICE_UNAVAILABLE,
299 uriP,
300 NULL,
301 dataP->userData);
302 }
303 else
304 {
305 coap_packet_t * packet = (coap_packet_t *)message;
306
307 dataP->callback(transacP->peerP,
308 packet->code,
309 uriP,
310 NULL,
311 dataP->userData);
312 }
313 lwm2m_free(dataP);
314}
315
316int lwm2m_bootstrap_delete(lwm2m_context_t * contextP,
317 void * sessionH,
318 lwm2m_uri_t * uriP)
319{
320 lwm2m_transaction_t * transaction;
321 bs_data_t * dataP;
322
323 transaction = transaction_new(COAP_TYPE_CON, COAP_DELETE, NULL, uriP, contextP->nextMID++, 4, NULL, ENDPOINT_UNKNOWN, sessionH);
324 if (transaction == NULL) return INTERNAL_SERVER_ERROR_5_00;
325
326 dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t));
327 if (dataP == NULL)
328 {
329 transaction_free(transaction);
330 return COAP_500_INTERNAL_SERVER_ERROR;
331 }
332 if (uriP == NULL)
333 {
334 dataP->isUri = false;
335 }
336 else
337 {
338 dataP->isUri = true;
339 memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t));
340 }
341 dataP->callback = contextP->bootstrapCallback;
342 dataP->userData = contextP->bootstrapUserData;
343
344 transaction->callback = bs_result_callback;
345 transaction->userData = (void *)dataP;
346
347 contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
348
349 return transaction_send(contextP, transaction);
350}
351
352int lwm2m_bootstrap_write(lwm2m_context_t * contextP,
353 void * sessionH,
354 lwm2m_uri_t * uriP,
355 uint8_t * buffer,
356 size_t length)
357{
358 lwm2m_transaction_t * transaction;
359 bs_data_t * dataP;
360
361 if (uriP == NULL
362 || buffer == NULL
363 || length == 0)
364 {
365 return COAP_400_BAD_REQUEST;
366 }
367
368 transaction = transaction_new(COAP_TYPE_CON, COAP_PUT, NULL, uriP, contextP->nextMID++, 4, NULL, ENDPOINT_UNKNOWN, sessionH);
369 if (transaction == NULL) return INTERNAL_SERVER_ERROR_5_00;
370
371 coap_set_payload(transaction->message, buffer, length);
372
373 dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t));
374 if (dataP == NULL)
375 {
376 transaction_free(transaction);
377 return COAP_500_INTERNAL_SERVER_ERROR;
378 }
379 dataP->isUri = true;
380 memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t));
381 dataP->callback = contextP->bootstrapCallback;
382 dataP->userData = contextP->bootstrapUserData;
383
384 transaction->callback = bs_result_callback;
385 transaction->userData = (void *)dataP;
386
387 contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
388
389 return transaction_send(contextP, transaction);
390}
391
392int lwm2m_bootstrap_finish(lwm2m_context_t * contextP,
393 void * sessionH)
394{
395 lwm2m_transaction_t * transaction;
396 bs_data_t * dataP;
397
398 transaction = transaction_new(COAP_TYPE_CON, COAP_PUT, NULL, NULL, contextP->nextMID++, 4, NULL, ENDPOINT_UNKNOWN, sessionH);
399 if (transaction == NULL) return INTERNAL_SERVER_ERROR_5_00;
400
401 coap_set_header_uri_path(transaction->message, "/"URI_BOOTSTRAP_SEGMENT);
402
403 dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t));
404 if (dataP == NULL)
405 {
406 transaction_free(transaction);
407 return COAP_500_INTERNAL_SERVER_ERROR;
408 }
409 dataP->isUri = false;
410 dataP->callback = contextP->bootstrapCallback;
411 dataP->userData = contextP->bootstrapUserData;
412
413 transaction->callback = bs_result_callback;
414 transaction->userData = (void *)dataP;
415
416 contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
417
418 return transaction_send(contextP, transaction);
419}
420
421#endif