1 //******************************************************************
3 // Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved.
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
21 //-----------------------------------------------------------------------------
23 //-----------------------------------------------------------------------------
24 #include "occoaphelper.h"
25 #include "ocstackconfig.h"
27 #include "coap_time.h"
30 //-----------------------------------------------------------------------------
32 //-----------------------------------------------------------------------------
33 #define TAG PCF("OCCoAPHelper")
34 #define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG_V(FATAL, TAG, "%s is NULL", #arg); goto exit;} }
36 //=============================================================================
38 //=============================================================================
40 OCStackResult isVendorSpecific(uint16_t optionID)
42 if(optionID >= COAP_VENDOR_OPT_START && optionID <= COAP_MAX_OPT)
46 return OC_STACK_INVALID_OPTION;
49 // Convert OCStack code to CoAP code
50 uint8_t OCToCoAPResponseCode(OCStackResult result)
56 ret = COAP_RESPONSE_200;
59 case OC_STACK_RESOURCE_CREATED:
60 ret = COAP_RESPONSE_201;
63 case OC_STACK_RESOURCE_DELETED:
64 ret = COAP_RESPONSE_202;
67 case OC_STACK_INVALID_QUERY :
68 ret = COAP_RESPONSE_400;
71 case OC_STACK_RESOURCE_ERROR:
72 return COAP_RESPONSE_403;
75 case OC_STACK_NO_RESOURCE :
76 ret = COAP_RESPONSE_404;
79 case OC_STACK_INVALID_METHOD :
80 ret = COAP_RESPONSE_405;
83 case OC_STACK_NOTIMPL :
84 ret = COAP_RESPONSE_501;
88 ret = COAP_RESPONSE_500;
93 uint8_t OCToCoAPQoS(OCQualityOfService qos, uint8_t * ipAddr)
95 if(ipAddr[0] == COAP_WK_IPAddr_0 && ipAddr[1] == COAP_WK_IPAddr_1 &&
96 ipAddr[2] == COAP_WK_IPAddr_2 && ipAddr[3] == COAP_WK_IPAddr_3)
98 return COAP_MESSAGE_NON;
103 return COAP_MESSAGE_CON;
109 return COAP_MESSAGE_NON;
113 // Convert CoAP code to OCStack code
114 OCStackResult CoAPToOCResponseCode(uint8_t coapCode)
120 case COAP_RESPONSE_200 :
124 case COAP_RESPONSE_201 :
125 ret = OC_STACK_RESOURCE_CREATED;
128 case COAP_RESPONSE_202 :
129 ret = OC_STACK_RESOURCE_DELETED;
132 case COAP_RESPONSE_400 :
133 ret = OC_STACK_INVALID_QUERY;
136 case COAP_RESPONSE_403 :
137 ret = OC_STACK_RESOURCE_ERROR;
140 case COAP_RESPONSE_404 :
141 ret = OC_STACK_NO_RESOURCE;
144 case COAP_RESPONSE_405 :
145 ret = OC_STACK_INVALID_METHOD;
148 case COAP_RESPONSE_501 :
149 ret = OC_STACK_NOTIMPL;
153 decimal = ((coapCode >> 5) * 100) + (coapCode & 31);
154 if (decimal >= 200 && decimal <= 231)
160 ret = OC_STACK_ERROR;
166 // Retrieve Uri and Query from received coap pdu
167 OCStackResult ParseCoAPPdu(coap_pdu_t * pdu, unsigned char * uriBuf,
168 unsigned char * queryBuf, uint32_t * observeOption,
169 uint32_t * maxAgeOption,
170 uint8_t * numVendorSpecificHeaderOptions,
171 OCHeaderOption * vendorSpecificHeaderOptions,
172 coap_block_t * block1, coap_block_t * block2,
173 uint16_t * size1, uint16_t * size2,
174 unsigned char * payload)
176 coap_opt_filter_t filter;
177 coap_opt_iterator_t opt_iter;
178 coap_opt_t *option = NULL;
180 unsigned char * optVal = NULL;
181 size_t uriBufLen = 0;
182 size_t queryBufLen = 0;
183 unsigned char * payloadLoc = NULL;
184 size_t payloadLength = 0;
186 coap_option_filter_clear(filter);
189 coap_option_setb(filter, COAP_OPTION_URI_PATH);
193 coap_option_setb(filter, COAP_OPTION_URI_QUERY);
197 coap_option_setb(filter, COAP_OPTION_OBSERVE);
201 coap_option_setb(filter, COAP_OPTION_MAXAGE);
205 coap_option_setb(filter, COAP_OPTION_BLOCK1);
209 coap_option_setb(filter, COAP_OPTION_BLOCK2);
213 coap_option_setb(filter, COAP_OPTION_SIZE1);
217 coap_option_setb(filter, COAP_OPTION_SIZE2);
219 if(vendorSpecificHeaderOptions)
221 coap_option_setbVendor(filter);
225 coap_get_data(pdu, &payloadLength, &payloadLoc);
226 memcpy(payload, payloadLoc, payloadLength);
228 coap_option_iterator_init(pdu, &opt_iter, filter);
230 while ((option = coap_option_next(&opt_iter)))
232 optLen = COAP_OPT_LENGTH(option);
233 optVal = COAP_OPT_VALUE(option);
234 switch(opt_iter.type)
236 case COAP_OPTION_URI_PATH:
237 if (uriBufLen + 1 + optLen < MAX_URI_LENGTH)
239 //we still have room in the buffer
240 uriBuf[uriBufLen++] = '/';
241 memcpy(uriBuf + uriBufLen, optVal, optLen);
246 return OC_STACK_NO_MEMORY;
249 case COAP_OPTION_URI_QUERY:
250 if (queryBufLen + 1 + optLen < MAX_QUERY_LENGTH)
252 //we still have room in the buffer
253 memcpy(queryBuf + queryBufLen, optVal, optLen);
254 queryBufLen += optLen;
255 queryBuf[queryBufLen++] = '&';
259 // TODO: we should check that resources do not have long uri
260 // at the resource creation
261 return OC_STACK_NO_MEMORY;
264 case COAP_OPTION_OBSERVE:
265 memcpy(observeOption, optVal, optLen);
266 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^Parsing the observe option %u",
269 case COAP_OPTION_MAXAGE:
270 memcpy(maxAgeOption, optVal, optLen);
271 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^Parsing the max age option %u",
274 case COAP_OPTION_BLOCK1:
275 block1->szx = COAP_OPT_BLOCK_SZX(option);
276 block1->num = coap_opt_block_num(option);
278 if(COAP_OPT_BLOCK_MORE(option))
282 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^Parsing block1 %u:%u:%u",
283 block1->num, block1->m, block1->szx);
285 case COAP_OPTION_BLOCK2:
286 block2->szx = COAP_OPT_BLOCK_SZX(option);
287 block2->num = coap_opt_block_num(option);
289 if(COAP_OPT_BLOCK_MORE(option))
293 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^Parsing block2 %u:%u:%u",
294 block1->num, block1->m, block1->szx);
296 case COAP_OPTION_SIZE1:
298 case COAP_OPTION_SIZE2:
301 if(*numVendorSpecificHeaderOptions >= MAX_HEADER_OPTIONS ||
302 optLen > MAX_HEADER_OPTION_DATA_LENGTH)
304 return OC_STACK_NO_MEMORY;
306 vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].protocolID = OC_COAP_ID;
307 vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionID = opt_iter.type;
308 vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionLength = optLen;
309 memcpy(vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionData, optVal, optLen);
310 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^Parsing vendor specific option %u",
311 vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionID);
312 OC_LOG_BUFFER(DEBUG, TAG, vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionData,
313 vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionLength);
314 (*numVendorSpecificHeaderOptions)++;
320 uriBuf[uriBufLen] = '\0';
322 // delete last '&' in the query
325 queryBuf[queryBufLen?queryBufLen-1:queryBufLen] = '\0';
328 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^The final parsed uri is %s", uriBuf);
329 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^The final parsed query is %s", queryBuf);
333 // Retrieve the token from the PDU
334 void RetrieveOCCoAPToken(const coap_pdu_t * pdu, OCCoAPToken * rcvdToken)
336 if (pdu && rcvdToken)
338 rcvdToken->tokenLength = pdu->hdr->token_length;
339 memcpy(rcvdToken->token, pdu->hdr->token,
340 rcvdToken->tokenLength);
344 OCStackResult FormOCResponse(OCResponse * * responseLoc, ClientCB * cbNode,
345 uint8_t TTL, OCClientResponse * clientResponse)
347 OCResponse * response = (OCResponse *) OCMalloc(sizeof(OCResponse));
350 return OC_STACK_NO_MEMORY;
352 response->cbNode = cbNode;
354 response->clientResponse = clientResponse;
356 *responseLoc = response;
360 OCStackResult FormOCClientResponse(OCClientResponse * clientResponse,
361 OCStackResult result, OCDevAddr * remote, uint32_t seqNum,
362 const unsigned char * resJSONPayload)
364 clientResponse->sequenceNumber = seqNum;
365 clientResponse->result = result;
366 clientResponse->addr = remote;
367 clientResponse->resJSONPayload = resJSONPayload;
372 OCStackResult FormOptionList(coap_list_t * * optListLoc, uint8_t * addMediaType,
373 uint32_t * addMaxAge, uint32_t * observeOptionPtr,
374 uint16_t * addPortNumber, uint8_t uriLength, unsigned char * uri,
375 uint8_t queryLength, unsigned char * query,
376 OCHeaderOption * vendorSpecificHeaderOptions,
377 uint8_t numVendorSpecificHeaderOptions)
379 coap_list_t * optNode = NULL;
382 unsigned char _buf[MAX_URI_QUERY_BUF_SIZE];
383 unsigned char *buf = _buf;
387 optNode = CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
388 sizeof(*addMediaType), addMediaType);
389 VERIFY_NON_NULL(optNode);
390 coap_insert(optListLoc, optNode, OrderOptions);
395 optNode = CreateNewOptionNode(COAP_OPTION_MAXAGE,
396 sizeof(*addMaxAge), (uint8_t *)addMaxAge);
397 VERIFY_NON_NULL(optNode);
398 coap_insert(optListLoc, optNode, OrderOptions);
403 optNode = CreateNewOptionNode(COAP_OPTION_OBSERVE,
404 sizeof(*observeOptionPtr), (uint8_t *)observeOptionPtr);
406 VERIFY_NON_NULL(optNode);
407 coap_insert(optListLoc, optNode, OrderOptions);
409 if(addPortNumber && *addPortNumber != COAP_DEFAULT_PORT)
411 optNode = CreateNewOptionNode(COAP_OPTION_URI_PORT,
412 sizeof(*addPortNumber), (uint8_t *)addPortNumber);
413 VERIFY_NON_NULL(optNode);
414 coap_insert(optListLoc, optNode, OrderOptions);
420 buflen = MAX_URI_QUERY_BUF_SIZE;
421 res = coap_split_path(uri, uriLength, buf, &buflen);
423 optNode = CreateNewOptionNode(COAP_OPTION_URI_PATH,
424 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf));
425 VERIFY_NON_NULL(optNode);
426 coap_insert(optListLoc, optNode, OrderOptions);
427 buf += COAP_OPT_SIZE(buf);
431 if(query && queryLength)
434 buflen = MAX_URI_QUERY_BUF_SIZE;
435 res = coap_split_query(query, queryLength, buf, &buflen);
437 optNode = CreateNewOptionNode(COAP_OPTION_URI_QUERY,
438 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf));
439 VERIFY_NON_NULL(optNode);
440 coap_insert(optListLoc, optNode, OrderOptions);
441 buf += COAP_OPT_SIZE(buf);
445 // make sure that options are valid
446 if(vendorSpecificHeaderOptions && numVendorSpecificHeaderOptions)
449 for( i = 0; i < numVendorSpecificHeaderOptions; i++)
451 if(vendorSpecificHeaderOptions[i].protocolID == OC_COAP_ID)
453 if(isVendorSpecific(vendorSpecificHeaderOptions[i].optionID)
455 vendorSpecificHeaderOptions[i].optionLength <=
456 MAX_HEADER_OPTION_DATA_LENGTH)
458 OC_LOG_V(INFO, TAG, " Adding option %d with",
459 vendorSpecificHeaderOptions[i].optionID);
460 OC_LOG_BUFFER(INFO, TAG, vendorSpecificHeaderOptions[i].optionData,
461 vendorSpecificHeaderOptions[i].optionLength);
462 optNode = CreateNewOptionNode(vendorSpecificHeaderOptions[i].optionID,
463 vendorSpecificHeaderOptions[i].optionLength,
464 vendorSpecificHeaderOptions[i].optionData);
465 VERIFY_NON_NULL(optNode);
466 coap_insert(optListLoc, optNode, OrderOptions);
470 coap_delete_list(*optListLoc);
471 return OC_STACK_INVALID_OPTION;
479 coap_delete_list(*optListLoc);
480 return OC_STACK_NO_MEMORY;
485 SendCoAPPdu(coap_context_t * gCoAPCtx, coap_address_t* dst, coap_pdu_t * pdu,
486 coap_send_flags_t flag)
488 coap_tid_t tid = COAP_INVALID_TID;
489 OCStackResult res = OC_STACK_COMM_ERROR;
492 if (!(flag & SEND_DELAYED))
494 flag = (coap_send_flags_t)( flag |
495 ((pdu->hdr->type == COAP_MESSAGE_CON) ? SEND_NOW_CON : SEND_NOW));
498 tid = coap_send(gCoAPCtx, dst, pdu, flag, &cache);
499 OC_LOG_V(INFO, TAG, "TID %d", tid);
500 if(tid != COAP_INVALID_TID)
502 OC_LOG(INFO, TAG, PCF("Sending a pdu with Token:"));
503 OC_LOG_BUFFER(INFO,TAG, pdu->hdr->token, pdu->hdr->token_length);
507 if (( (pdu->hdr->type != COAP_MESSAGE_CON) && (!(flag & SEND_DELAYED)) && (!cache))
508 || (tid == COAP_INVALID_TID))
510 OC_LOG(INFO, TAG, PCF("Deleting PDU"));
511 coap_delete_pdu(pdu);
515 OC_LOG(INFO, TAG, PCF("Keeping PDU, we will handle the retry/delay of this pdu"));
521 //generate a coap message
523 GenerateCoAPPdu(uint8_t msgType, uint8_t code, unsigned short id,
524 OCCoAPToken * token, unsigned char * payloadJSON,
525 coap_list_t *options)
532 pdu = coap_pdu_init(msgType, code, id, COAP_MAX_PDU_SIZE);
533 VERIFY_NON_NULL(pdu);
534 pdu->hdr->token_length = token->tokenLength;
535 if (!coap_add_token(pdu, token->tokenLength, token->token))
537 OC_LOG(FATAL, TAG, PCF("coap_add_token failed"));
542 pdu = coap_pdu_init(msgType, code, id, sizeof(coap_pdu_t));
543 VERIFY_NON_NULL(pdu);
546 for (opt = options; opt; opt = opt->next)
548 coap_add_option(pdu, COAP_OPTION_KEY(*(coap_option *) opt->data),
549 COAP_OPTION_LENGTH(*(coap_option *) opt->data),
550 COAP_OPTION_DATA(*(coap_option *) opt->data));
555 coap_add_data(pdu, strlen((const char *) payloadJSON) + 1,
556 (unsigned char*) payloadJSON);
559 // display the pdu for debugging purposes
563 coap_delete_list(options);
567 coap_delete_list(options);
571 //a function to help in ordering coap options
572 int OrderOptions(void *a, void *b)
576 return a < b ? -1 : 1;
579 if (COAP_OPTION_KEY(*(coap_option *)a)
580 < COAP_OPTION_KEY(*(coap_option *)b) )
585 return COAP_OPTION_KEY(*(coap_option *)a)
586 == COAP_OPTION_KEY(*(coap_option *)b) ;
589 //a function to create a coap option
591 CreateNewOptionNode(unsigned short key, unsigned int length, unsigned char *data)
593 coap_option *option = NULL;
596 VERIFY_NON_NULL(data);
597 option = (coap_option *)coap_malloc(sizeof(coap_option) + length);
598 VERIFY_NON_NULL(option);
600 COAP_OPTION_KEY(*option) = key;
601 COAP_OPTION_LENGTH(*option) = length;
602 memcpy(COAP_OPTION_DATA(*option), data, length);
604 /* we can pass NULL here as delete function since option is released automatically */
605 node = coap_new_listnode(option, NULL);
613 OC_LOG(ERROR,TAG, PCF("new_option_node: malloc: was not created"));
618 OCStackResult ReTXCoAPQueue(coap_context_t * ctx, coap_queue_t * queue)
620 coap_tid_t tid = COAP_INVALID_TID;
621 OCStackResult result = OC_STACK_ERROR;
622 tid = coap_retransmit( ctx, queue);
623 if(tid == COAP_INVALID_TID)
625 OC_LOG_V(DEBUG, TAG, "Retransmission Failed TID %d",
627 result = OC_STACK_COMM_ERROR;
631 OC_LOG_V(DEBUG, TAG, "Retransmission TID %d, this is attempt %d",
632 queue->id, queue->retransmit_cnt);
633 result = OC_STACK_OK;
638 OCStackResult HandleFailedCommunication(coap_context_t * ctx, coap_queue_t * queue)
640 //TODO: this function should change to only use OCStackFeedBack
641 OCResponse * response = NULL;
642 ClientCB * cbNode = NULL;
643 OCClientResponse clientResponse;
645 OCStackResult result = OC_STACK_OK;
647 RetrieveOCCoAPToken(queue->pdu, &token);
649 cbNode = GetClientCB(&token, NULL, NULL);
654 result = FormOCClientResponse(&clientResponse, OC_STACK_COMM_ERROR,
655 (OCDevAddr *) &(queue->remote), 0, NULL);
656 if(result != OC_STACK_OK)
660 result = FormOCResponse(&response, cbNode, 0, &clientResponse);
661 if(result != OC_STACK_OK)
665 HandleStackResponses(response);
668 result = OCStackFeedBack(&token, OC_OBSERVER_FAILED_COMM);
669 if(result == OC_STACK_OK)
671 coap_cancel_all_messages(ctx, &queue->remote, token.token, token.tokenLength);
677 // a function to handle the send queue in the passed context
678 void HandleSendQueue(coap_context_t * ctx)
681 coap_queue_t *nextQueue = NULL;
684 nextQueue = coap_peek_next( ctx );
685 while (nextQueue && nextQueue->t <= now - ctx->sendqueue_basetime)
687 nextQueue = coap_pop_next( ctx );
688 if((uint8_t)nextQueue->delayedResNeeded)
690 OC_LOG_V(DEBUG, TAG, "Sending Delayed response TID %d",
692 if(SendCoAPPdu(ctx, &nextQueue->remote, nextQueue->pdu,
693 (coap_send_flags_t)(nextQueue->secure ? SEND_SECURE_PORT : 0))
694 == OC_STACK_COMM_ERROR)
696 OC_LOG(DEBUG, TAG, PCF("A problem occurred in sending a pdu"));
697 HandleFailedCommunication(ctx, nextQueue);
699 nextQueue->pdu = NULL;
700 coap_delete_node(nextQueue);
704 OC_LOG_V(DEBUG, TAG, "Retrying a CON pdu TID %d",nextQueue->id);
705 if(ReTXCoAPQueue(ctx, nextQueue) == OC_STACK_COMM_ERROR)
707 OC_LOG(DEBUG, TAG, PCF("A problem occurred in retransmitting a pdu"));
708 HandleFailedCommunication(ctx, nextQueue);
709 coap_delete_node(nextQueue);
712 nextQueue = coap_peek_next( ctx );