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;
114 OCQualityOfService CoAPToOCQoS(uint8_t qos)
118 case COAP_MESSAGE_CON:
120 case COAP_MESSAGE_NON:
126 // Convert CoAP code to OCStack code
127 OCStackResult CoAPToOCResponseCode(uint8_t coapCode)
133 case COAP_RESPONSE_200 :
137 case COAP_RESPONSE_201 :
138 ret = OC_STACK_RESOURCE_CREATED;
141 case COAP_RESPONSE_202 :
142 ret = OC_STACK_RESOURCE_DELETED;
145 case COAP_RESPONSE_400 :
146 ret = OC_STACK_INVALID_QUERY;
149 case COAP_RESPONSE_403 :
150 ret = OC_STACK_RESOURCE_ERROR;
153 case COAP_RESPONSE_404 :
154 ret = OC_STACK_NO_RESOURCE;
157 case COAP_RESPONSE_405 :
158 ret = OC_STACK_INVALID_METHOD;
161 case COAP_RESPONSE_501 :
162 ret = OC_STACK_NOTIMPL;
166 decimal = ((coapCode >> 5) * 100) + (coapCode & 31);
167 if (decimal >= 200 && decimal <= 231)
173 ret = OC_STACK_ERROR;
179 // Retrieve Uri and Query from received coap pdu
180 OCStackResult ParseCoAPPdu(coap_pdu_t * pdu, unsigned char * uriBuf,
181 unsigned char * queryBuf, uint32_t * observeOption,
182 uint32_t * maxAgeOption,
183 uint8_t * numVendorSpecificHeaderOptions,
184 OCHeaderOption * vendorSpecificHeaderOptions,
185 coap_block_t * block1, coap_block_t * block2,
186 uint16_t * size1, uint16_t * size2,
187 unsigned char * payload)
189 coap_opt_filter_t filter;
190 coap_opt_iterator_t opt_iter;
191 coap_opt_t *option = NULL;
193 unsigned char * optVal = NULL;
194 size_t uriBufLen = 0;
195 size_t queryBufLen = 0;
196 unsigned char * payloadLoc = NULL;
197 size_t payloadLength = 0;
199 coap_option_filter_clear(filter);
202 coap_option_setb(filter, COAP_OPTION_URI_PATH);
206 coap_option_setb(filter, COAP_OPTION_URI_QUERY);
210 coap_option_setb(filter, COAP_OPTION_OBSERVE);
214 coap_option_setb(filter, COAP_OPTION_MAXAGE);
218 coap_option_setb(filter, COAP_OPTION_BLOCK1);
222 coap_option_setb(filter, COAP_OPTION_BLOCK2);
226 coap_option_setb(filter, COAP_OPTION_SIZE1);
230 coap_option_setb(filter, COAP_OPTION_SIZE2);
232 if(vendorSpecificHeaderOptions)
234 coap_option_setbVendor(filter);
238 coap_get_data(pdu, &payloadLength, &payloadLoc);
239 memcpy(payload, payloadLoc, payloadLength);
241 coap_option_iterator_init(pdu, &opt_iter, filter);
243 while ((option = coap_option_next(&opt_iter)))
245 optLen = COAP_OPT_LENGTH(option);
246 optVal = COAP_OPT_VALUE(option);
247 switch(opt_iter.type)
249 case COAP_OPTION_URI_PATH:
250 if (uriBufLen + 1 + optLen < MAX_URI_LENGTH)
252 //we still have room in the buffer
253 uriBuf[uriBufLen++] = '/';
254 memcpy(uriBuf + uriBufLen, optVal, optLen);
259 return OC_STACK_NO_MEMORY;
262 case COAP_OPTION_URI_QUERY:
263 if (queryBufLen + 1 + optLen < MAX_QUERY_LENGTH)
265 //we still have room in the buffer
266 memcpy(queryBuf + queryBufLen, optVal, optLen);
267 queryBufLen += optLen;
268 queryBuf[queryBufLen++] = '&';
272 // TODO: we should check that resources do not have long uri
273 // at the resource creation
274 return OC_STACK_NO_MEMORY;
277 case COAP_OPTION_OBSERVE:
278 memcpy(observeOption, optVal, optLen);
279 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^Parsing the observe option %u",
282 case COAP_OPTION_MAXAGE:
283 memcpy(maxAgeOption, optVal, optLen);
284 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^Parsing the max age option %u",
287 case COAP_OPTION_BLOCK1:
288 block1->szx = COAP_OPT_BLOCK_SZX(option);
289 block1->num = coap_opt_block_num(option);
291 if(COAP_OPT_BLOCK_MORE(option))
295 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^Parsing block1 %u:%u:%u",
296 block1->num, block1->m, block1->szx);
298 case COAP_OPTION_BLOCK2:
299 block2->szx = COAP_OPT_BLOCK_SZX(option);
300 block2->num = coap_opt_block_num(option);
302 if(COAP_OPT_BLOCK_MORE(option))
306 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^Parsing block2 %u:%u:%u",
307 block1->num, block1->m, block1->szx);
309 case COAP_OPTION_SIZE1:
311 case COAP_OPTION_SIZE2:
314 if(*numVendorSpecificHeaderOptions >= MAX_HEADER_OPTIONS ||
315 optLen > MAX_HEADER_OPTION_DATA_LENGTH)
317 return OC_STACK_NO_MEMORY;
319 vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].protocolID = OC_COAP_ID;
320 vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionID = opt_iter.type;
321 vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionLength = optLen;
322 memcpy(vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionData, optVal, optLen);
323 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^Parsing vendor specific option %u",
324 vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionID);
325 OC_LOG_BUFFER(DEBUG, TAG, vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionData,
326 vendorSpecificHeaderOptions[*numVendorSpecificHeaderOptions].optionLength);
327 (*numVendorSpecificHeaderOptions)++;
333 uriBuf[uriBufLen] = '\0';
335 // delete last '&' in the query
338 queryBuf[queryBufLen?queryBufLen-1:queryBufLen] = '\0';
341 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^The final parsed uri is %s", uriBuf);
342 OC_LOG_V(DEBUG, TAG, "^^^^^^^^^^^^The final parsed query is %s", queryBuf);
346 // Retrieve the token from the PDU
347 void RetrieveOCCoAPToken(const coap_pdu_t * pdu, OCCoAPToken * rcvdToken)
349 if (pdu && rcvdToken)
351 rcvdToken->tokenLength = pdu->hdr->token_length;
352 memcpy(rcvdToken->token, pdu->hdr->token,
353 rcvdToken->tokenLength);
357 OCStackResult FormOCResponse(OCResponse * * responseLoc, ClientCB * cbNode, uint32_t maxAge,
358 unsigned char * fullUri, unsigned char * rcvdUri, OCCoAPToken * rcvdToken,
359 OCClientResponse * clientResponse, unsigned char * bufRes)
361 OCResponse * response = (OCResponse *) OCMalloc(sizeof(OCResponse));
364 return OC_STACK_NO_MEMORY;
366 response->cbNode = cbNode;
367 response->maxAge = maxAge;
368 response->fullUri = fullUri;
369 response->rcvdUri = rcvdUri;
370 response->rcvdToken = rcvdToken;
371 response->clientResponse = clientResponse;
372 response->bufRes = bufRes;
374 *responseLoc = response;
378 OCStackResult FormOCClientResponse(OCClientResponse * clientResponse,
379 OCStackResult result, OCDevAddr * remote, uint32_t seqNum,
380 const unsigned char * resJSONPayload)
382 clientResponse->sequenceNumber = seqNum;
383 clientResponse->result = result;
384 clientResponse->addr = remote;
385 clientResponse->resJSONPayload = resJSONPayload;
390 OCStackResult FormOptionList(coap_list_t * * optListLoc, uint8_t * addMediaType,
391 uint32_t * addMaxAge, uint32_t * observeOptionPtr,
392 uint16_t * addPortNumber, uint8_t uriLength, unsigned char * uri,
393 uint8_t queryLength, unsigned char * query,
394 OCHeaderOption * vendorSpecificHeaderOptions,
395 uint8_t numVendorSpecificHeaderOptions)
397 coap_list_t * optNode = NULL;
400 unsigned char _buf[MAX_URI_QUERY_BUF_SIZE];
401 unsigned char *buf = _buf;
405 optNode = CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
406 sizeof(*addMediaType), addMediaType);
407 VERIFY_NON_NULL(optNode);
408 coap_insert(optListLoc, optNode, OrderOptions);
413 optNode = CreateNewOptionNode(COAP_OPTION_MAXAGE,
414 sizeof(*addMaxAge), (uint8_t *)addMaxAge);
415 VERIFY_NON_NULL(optNode);
416 coap_insert(optListLoc, optNode, OrderOptions);
421 optNode = CreateNewOptionNode(COAP_OPTION_OBSERVE,
422 sizeof(*observeOptionPtr), (uint8_t *)observeOptionPtr);
424 VERIFY_NON_NULL(optNode);
425 coap_insert(optListLoc, optNode, OrderOptions);
427 if(addPortNumber && *addPortNumber != COAP_DEFAULT_PORT)
429 optNode = CreateNewOptionNode(COAP_OPTION_URI_PORT,
430 sizeof(*addPortNumber), (uint8_t *)addPortNumber);
431 VERIFY_NON_NULL(optNode);
432 coap_insert(optListLoc, optNode, OrderOptions);
438 buflen = MAX_URI_QUERY_BUF_SIZE;
439 res = coap_split_path(uri, uriLength, buf, &buflen);
441 optNode = CreateNewOptionNode(COAP_OPTION_URI_PATH,
442 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf));
443 VERIFY_NON_NULL(optNode);
444 coap_insert(optListLoc, optNode, OrderOptions);
445 buf += COAP_OPT_SIZE(buf);
449 if(query && queryLength)
452 buflen = MAX_URI_QUERY_BUF_SIZE;
453 res = coap_split_query(query, queryLength, buf, &buflen);
455 optNode = CreateNewOptionNode(COAP_OPTION_URI_QUERY,
456 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf));
457 VERIFY_NON_NULL(optNode);
458 coap_insert(optListLoc, optNode, OrderOptions);
459 buf += COAP_OPT_SIZE(buf);
463 // make sure that options are valid
464 if(vendorSpecificHeaderOptions && numVendorSpecificHeaderOptions)
467 for( i = 0; i < numVendorSpecificHeaderOptions; i++)
469 if(vendorSpecificHeaderOptions[i].protocolID == OC_COAP_ID)
471 if(isVendorSpecific(vendorSpecificHeaderOptions[i].optionID)
473 vendorSpecificHeaderOptions[i].optionLength <=
474 MAX_HEADER_OPTION_DATA_LENGTH)
476 OC_LOG_V(INFO, TAG, " Adding option %d with",
477 vendorSpecificHeaderOptions[i].optionID);
478 OC_LOG_BUFFER(INFO, TAG, vendorSpecificHeaderOptions[i].optionData,
479 vendorSpecificHeaderOptions[i].optionLength);
480 optNode = CreateNewOptionNode(vendorSpecificHeaderOptions[i].optionID,
481 vendorSpecificHeaderOptions[i].optionLength,
482 vendorSpecificHeaderOptions[i].optionData);
483 VERIFY_NON_NULL(optNode);
484 coap_insert(optListLoc, optNode, OrderOptions);
488 coap_delete_list(*optListLoc);
489 return OC_STACK_INVALID_OPTION;
497 coap_delete_list(*optListLoc);
498 return OC_STACK_NO_MEMORY;
503 SendCoAPPdu(coap_context_t * gCoAPCtx, coap_address_t* dst, coap_pdu_t * pdu,
504 coap_send_flags_t flag)
506 coap_tid_t tid = COAP_INVALID_TID;
507 OCStackResult res = OC_STACK_COMM_ERROR;
510 if (!(flag & SEND_DELAYED))
512 flag = (coap_send_flags_t)( flag |
513 ((pdu->hdr->type == COAP_MESSAGE_CON) ? SEND_NOW_CON : SEND_NOW));
516 tid = coap_send(gCoAPCtx, dst, pdu, flag, &cache);
517 OC_LOG_V(INFO, TAG, "TID %d", tid);
518 if(tid != COAP_INVALID_TID)
520 OC_LOG(INFO, TAG, PCF("Sending a pdu with Token:"));
521 OC_LOG_BUFFER(INFO,TAG, pdu->hdr->token, pdu->hdr->token_length);
525 if (( (pdu->hdr->type != COAP_MESSAGE_CON) && (!(flag & SEND_DELAYED)) && (!cache))
526 || (tid == COAP_INVALID_TID))
528 OC_LOG(INFO, TAG, PCF("Deleting PDU"));
529 coap_delete_pdu(pdu);
533 OC_LOG(INFO, TAG, PCF("Keeping PDU, we will handle the retry/delay of this pdu"));
539 //generate a coap message
541 GenerateCoAPPdu(uint8_t msgType, uint8_t code, unsigned short id,
542 OCCoAPToken * token, unsigned char * payloadJSON,
543 coap_list_t *options)
550 pdu = coap_pdu_init(msgType, code, id, COAP_MAX_PDU_SIZE);
551 VERIFY_NON_NULL(pdu);
552 pdu->hdr->token_length = token->tokenLength;
553 if (!coap_add_token(pdu, token->tokenLength, token->token))
555 OC_LOG(FATAL, TAG, PCF("coap_add_token failed"));
560 pdu = coap_pdu_init(msgType, code, id, sizeof(coap_pdu_t));
561 VERIFY_NON_NULL(pdu);
564 for (opt = options; opt; opt = opt->next)
566 coap_add_option(pdu, COAP_OPTION_KEY(*(coap_option *) opt->data),
567 COAP_OPTION_LENGTH(*(coap_option *) opt->data),
568 COAP_OPTION_DATA(*(coap_option *) opt->data));
573 coap_add_data(pdu, strlen((const char *) payloadJSON) + 1,
574 (unsigned char*) payloadJSON);
577 // display the pdu for debugging purposes
581 coap_delete_list(options);
585 coap_delete_list(options);
589 //a function to help in ordering coap options
590 int OrderOptions(void *a, void *b)
594 return a < b ? -1 : 1;
597 if (COAP_OPTION_KEY(*(coap_option *)a)
598 < COAP_OPTION_KEY(*(coap_option *)b) )
603 return COAP_OPTION_KEY(*(coap_option *)a)
604 == COAP_OPTION_KEY(*(coap_option *)b) ;
607 //a function to create a coap option
609 CreateNewOptionNode(unsigned short key, unsigned int length, unsigned char *data)
611 coap_option *option = NULL;
614 VERIFY_NON_NULL(data);
615 option = (coap_option *)coap_malloc(sizeof(coap_option) + length);
616 VERIFY_NON_NULL(option);
618 COAP_OPTION_KEY(*option) = key;
619 COAP_OPTION_LENGTH(*option) = length;
620 memcpy(COAP_OPTION_DATA(*option), data, length);
622 /* we can pass NULL here as delete function since option is released automatically */
623 node = coap_new_listnode(option, NULL);
631 OC_LOG(ERROR,TAG, PCF("new_option_node: malloc: was not created"));
636 OCStackResult ReTXCoAPQueue(coap_context_t * ctx, coap_queue_t * queue)
638 coap_tid_t tid = COAP_INVALID_TID;
639 OCStackResult result = OC_STACK_ERROR;
640 tid = coap_retransmit( ctx, queue);
641 if(tid == COAP_INVALID_TID)
643 OC_LOG_V(DEBUG, TAG, "Retransmission Failed TID %d",
645 result = OC_STACK_COMM_ERROR;
649 OC_LOG_V(DEBUG, TAG, "Retransmission TID %d, this is attempt %d",
650 queue->id, queue->retransmit_cnt);
651 result = OC_STACK_OK;
656 OCStackResult HandleFailedCommunication(coap_context_t * ctx, coap_queue_t * queue)
658 //TODO: this function should change to only use OCStackFeedBack
659 OCResponse * response = NULL;
660 ClientCB * cbNode = NULL;
661 OCClientResponse clientResponse;
663 OCStackResult result = OC_STACK_OK;
665 RetrieveOCCoAPToken(queue->pdu, &token);
667 cbNode = GetClientCB(&token, NULL, NULL);
672 result = FormOCClientResponse(&clientResponse, OC_STACK_COMM_ERROR,
673 (OCDevAddr *) &(queue->remote), 0, NULL);
674 if(result != OC_STACK_OK)
678 result = FormOCResponse(&response, cbNode, 0, NULL, NULL, &token, &clientResponse, NULL);
679 if(result != OC_STACK_OK)
683 HandleStackResponses(response);
686 result = OCStackFeedBack(&token, OC_OBSERVER_FAILED_COMM);
687 if(result == OC_STACK_OK)
689 coap_cancel_all_messages(ctx, &queue->remote, token.token, token.tokenLength);
695 // a function to handle the send queue in the passed context
696 void HandleSendQueue(coap_context_t * ctx)
699 coap_queue_t *nextQueue = NULL;
702 nextQueue = coap_peek_next( ctx );
703 while (nextQueue && nextQueue->t <= now - ctx->sendqueue_basetime)
705 nextQueue = coap_pop_next( ctx );
706 if((uint8_t)nextQueue->delayedResNeeded)
708 OC_LOG_V(DEBUG, TAG, "Sending Delayed response TID %d",
710 if(SendCoAPPdu(ctx, &nextQueue->remote, nextQueue->pdu,
711 (coap_send_flags_t)(nextQueue->secure ? SEND_SECURE_PORT : 0))
712 == OC_STACK_COMM_ERROR)
714 OC_LOG(DEBUG, TAG, PCF("A problem occurred in sending a pdu"));
715 HandleFailedCommunication(ctx, nextQueue);
717 nextQueue->pdu = NULL;
718 coap_delete_node(nextQueue);
722 OC_LOG_V(DEBUG, TAG, "Retrying a CON pdu TID %d",nextQueue->id);
723 if(ReTXCoAPQueue(ctx, nextQueue) == OC_STACK_COMM_ERROR)
725 OC_LOG(DEBUG, TAG, PCF("A problem occurred in retransmitting a pdu"));
726 HandleFailedCommunication(ctx, nextQueue);
727 coap_delete_node(nextQueue);
730 nextQueue = coap_peek_next( ctx );