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 "ocobserve.h"
28 #include "coap_time.h"
31 //-----------------------------------------------------------------------------
33 //-----------------------------------------------------------------------------
34 #define TAG PCF("OCCoAPHelper")
35 #define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG_V(FATAL, TAG, "%s is NULL", #arg); goto exit;} }
37 //=============================================================================
39 //=============================================================================
41 OCStackResult isVendorSpecific(uint16_t optionID)
43 if(optionID >= COAP_VENDOR_OPT_START && optionID <= COAP_MAX_OPT)
47 return OC_STACK_INVALID_OPTION;
50 // Convert OCStack code to CoAP code
51 uint8_t OCToCoAPResponseCode(OCStackResult result)
57 ret = COAP_RESPONSE_200;
60 case OC_STACK_RESOURCE_CREATED:
61 ret = COAP_RESPONSE_201;
64 case OC_STACK_RESOURCE_DELETED:
65 ret = COAP_RESPONSE_202;
68 case OC_STACK_INVALID_QUERY :
69 ret = COAP_RESPONSE_400;
72 case OC_STACK_RESOURCE_ERROR:
73 return COAP_RESPONSE_403;
76 case OC_STACK_NO_RESOURCE :
77 ret = COAP_RESPONSE_404;
80 case OC_STACK_INVALID_METHOD :
81 ret = COAP_RESPONSE_405;
85 ret = COAP_RESPONSE_500;
90 uint8_t OCToCoAPQoS(OCQualityOfService qos)
95 return COAP_MESSAGE_CON;
101 return COAP_MESSAGE_NON;
105 // Convert CoAP code to OCStack code
106 OCStackResult CoAPToOCResponseCode(uint8_t coapCode)
112 case COAP_RESPONSE_200 :
116 case COAP_RESPONSE_201 :
117 ret = OC_STACK_RESOURCE_CREATED;
120 case COAP_RESPONSE_202 :
121 ret = OC_STACK_RESOURCE_DELETED;
124 case COAP_RESPONSE_400 :
125 ret = OC_STACK_INVALID_QUERY;
128 case COAP_RESPONSE_403 :
129 ret = OC_STACK_RESOURCE_ERROR;
132 case COAP_RESPONSE_404 :
133 ret = OC_STACK_NO_RESOURCE;
136 case COAP_RESPONSE_405 :
137 ret = OC_STACK_INVALID_METHOD;
141 decimal = ((coapCode >> 5) * 100) + (coapCode & 31);
142 if (decimal >= 200 && decimal <= 231)
148 ret = OC_STACK_ERROR;
154 // Retrieve Uri and Query from received coap pdu
155 OCStackResult ParseCoAPPdu(coap_pdu_t * pdu, unsigned char * uriBuf,
156 unsigned char * queryBuf, uint8_t * * observeOptionLoc,
157 uint8_t * * maxAgeOptionLoc, unsigned char * * payloadLoc,
158 OCHeaderOption * rcvVendorSpecificHeaderOptions,
159 uint8_t * numRcvVendorSpecificHeaderOptions)
161 coap_opt_filter_t filter;
162 coap_opt_iterator_t opt_iter;
163 coap_opt_t *option = NULL;
166 uint8_t * observeOption = NULL;
167 uint8_t * maxAgeOption = NULL;
168 uint8_t optionFound = 0;
173 coap_option_filter_clear(filter);
174 coap_option_setb(filter, COAP_OPTION_URI_PATH);
175 coap_option_iterator_init(pdu, &opt_iter, filter);
176 while ((option = coap_option_next(&opt_iter)))
178 optLen = COAP_OPT_LENGTH(option);
179 if (bufLen + 1 + optLen < MAX_URI_LENGTH)
181 //we still have room in the buffer
182 uriBuf[bufLen++] = '/';
183 memcpy(uriBuf + bufLen, COAP_OPT_VALUE(option), optLen);
188 // TODO: we should check that resources do not have long uri at the resource creation
189 return OC_STACK_NO_MEMORY;
192 uriBuf[bufLen] = '\0';
199 coap_option_filter_clear(filter);
200 coap_option_setb(filter, COAP_OPTION_URI_QUERY);
201 coap_option_iterator_init(pdu, &opt_iter, filter);
202 while ((option = coap_option_next(&opt_iter)))
204 optLen = COAP_OPT_LENGTH(option);
205 if (bufLen + 1 + optLen < MAX_QUERY_LENGTH)
207 //we still have room in the buffer
208 memcpy(queryBuf + bufLen, COAP_OPT_VALUE(option), optLen);
210 queryBuf[bufLen++] = '&';
214 // TODO: should it be OC_STACK_NO_MEMORY
215 return OC_STACK_NO_MEMORY;
219 queryBuf[bufLen ? (bufLen - 1) : (bufLen)] = '\0';
225 // parse the observe option
226 coap_option_filter_clear(filter);
227 coap_option_setb(filter, COAP_OPTION_OBSERVE);
228 coap_option_iterator_init(pdu, &opt_iter, filter);
229 while ((option = coap_option_next(&opt_iter)))
231 observeOption = (uint8_t *) OCMalloc(COAP_OPT_LENGTH(option));
234 return OC_STACK_NO_MEMORY;
236 memcpy(observeOption, COAP_OPT_VALUE(option),COAP_OPT_LENGTH(option));
242 *observeOptionLoc = observeOption;
246 OCFree(observeOption);
247 *observeOptionLoc = NULL;
255 // parse the observe option
256 coap_option_filter_clear(filter);
257 coap_option_setb(filter, COAP_OPTION_MAXAGE);
258 coap_option_iterator_init(pdu, &opt_iter, filter);
259 while ((option = coap_option_next(&opt_iter)))
261 maxAgeOption = (uint8_t *) OCMalloc(COAP_OPT_LENGTH(option));
264 return OC_STACK_NO_MEMORY;
266 memcpy(maxAgeOption, COAP_OPT_VALUE(option),COAP_OPT_LENGTH(option));
272 *maxAgeOptionLoc = maxAgeOption;
276 OCFree(maxAgeOption);
277 *maxAgeOptionLoc = NULL;
281 if(rcvVendorSpecificHeaderOptions)
283 coap_option_filter_clear(filter);
284 coap_option_setbVendor(filter);
285 coap_option_iterator_init(pdu, &opt_iter, filter);
287 while((option = coap_option_next(&opt_iter)))
289 if(i >= MAX_HEADER_OPTIONS ||
290 COAP_OPT_LENGTH(option) > MAX_HEADER_OPTION_DATA_LENGTH)
292 return OC_STACK_NO_MEMORY;
294 rcvVendorSpecificHeaderOptions[i].protocolID = OC_COAP_ID;
295 rcvVendorSpecificHeaderOptions[i].optionID = opt_iter.type;
296 rcvVendorSpecificHeaderOptions[i].optionLength = COAP_OPT_LENGTH(option);
297 memcpy(rcvVendorSpecificHeaderOptions[i].optionData, COAP_OPT_VALUE(option),
298 rcvVendorSpecificHeaderOptions[i].optionLength);
299 OC_LOG_V(INFO, TAG, " Parsing option %d with", rcvVendorSpecificHeaderOptions[i].optionID);
300 OC_LOG_BUFFER(INFO, TAG, rcvVendorSpecificHeaderOptions[i].optionData,
301 rcvVendorSpecificHeaderOptions[i].optionLength);
303 *numRcvVendorSpecificHeaderOptions = i;
310 coap_get_data(pdu, &bufLen, payloadLoc);
316 // Form the OCRequest struct
317 OCStackResult FormOCRequest(OCRequest * * requestLoc, OCQualityOfService qos,
318 unsigned char * uriBuf, OCObserveReq * observeReq,
319 OCEntityHandlerRequest * entityHandlerRequest)
321 OCRequest * request = NULL;
324 request = (OCRequest *) OCMalloc(sizeof(OCRequest));
327 return OC_STACK_NO_MEMORY;
334 request->resourceUrl = uriBuf;
337 request->observe = observeReq;
339 // add entityHandlerRequest
340 request->entityHandlerRequest = entityHandlerRequest;
342 //TODO: this needs to be filled in the future
343 request->sequenceNum = 0;
345 *requestLoc = request;
349 // Form the OCObserveReq struct
350 OCStackResult FormOCObserveReq(OCObserveReq ** observeReqLoc, uint32_t observeOption,
351 OCDevAddr * remote, OCCoAPToken * rcvdToken)
353 OCObserveReq * observeReq;
355 if(observeOption == OC_RESOURCE_NO_OBSERVE)
360 observeReq = (OCObserveReq *)OCMalloc(sizeof(OCObserveReq));
363 *observeReqLoc = NULL;
364 return OC_STACK_NO_MEMORY;
367 observeReq->option = observeOption;
368 observeReq->subAddr = remote;
369 observeReq->token = rcvdToken;
370 observeReq->result = OC_STACK_OK;
372 *observeReqLoc = observeReq;
376 // Form the OCEntityHandlerRequest struct
377 OCStackResult FormOCEntityHandlerRequest(OCEntityHandlerRequest * entityHandlerRequestLoc,
378 OCMethod method, unsigned char * resBuf, unsigned char * bufReqPayload,
379 unsigned char * queryBuf, unsigned char *newResUriBuf)
381 if (entityHandlerRequestLoc)
383 //set it to NULL for now, it will be modified in ocstack
384 entityHandlerRequestLoc->resource = NULL;
386 entityHandlerRequestLoc->method = method;
389 entityHandlerRequestLoc->query = queryBuf;
392 entityHandlerRequestLoc->reqJSONPayload = bufReqPayload;
394 entityHandlerRequestLoc->resJSONPayload = resBuf;
395 entityHandlerRequestLoc->resJSONPayloadLen = MAX_RESPONSE_LENGTH;
397 entityHandlerRequestLoc->obsInfo = NULL;
398 entityHandlerRequestLoc->newResourceUri = newResUriBuf;
400 entityHandlerRequestLoc->numRcvdVendorSpecificHeaderOptions = 0;
401 entityHandlerRequestLoc->numSendVendorSpecificHeaderOptions = 0;
405 return OC_STACK_INVALID_PARAM;
408 // Retrieve the token from the PDU
409 void RetrieveOCCoAPToken(const coap_pdu_t * pdu, OCCoAPToken * rcvdToken)
411 if (pdu && rcvdToken)
413 rcvdToken->tokenLength = pdu->hdr->token_length;
414 memcpy(rcvdToken->token, pdu->hdr->token,
415 rcvdToken->tokenLength);
419 OCStackResult FormOCResponse(OCResponse * * responseLoc, ClientCB * cbNode,
420 uint8_t TTL, OCClientResponse * clientResponse)
422 OCResponse * response = (OCResponse *) OCMalloc(sizeof(OCResponse));
425 return OC_STACK_NO_MEMORY;
427 response->cbNode = cbNode;
429 response->clientResponse = clientResponse;
431 *responseLoc = response;
435 OCStackResult FormOCClientResponse(OCClientResponse * clientResponse,
436 OCStackResult result, OCDevAddr * remote, uint32_t seqNum,
437 const unsigned char * resJSONPayload)
439 clientResponse->sequenceNumber = seqNum;
440 clientResponse->result = result;
441 clientResponse->addr = remote;
442 clientResponse->resJSONPayload = resJSONPayload;
447 OCStackResult FormOptionList(coap_list_t * * optListLoc, uint8_t * addMediaType,
448 uint32_t * addMaxAge, uint8_t observeOptionLength, uint32_t * observeOptionPtr,
449 uint16_t * addPortNumber, uint8_t uriLength, unsigned char * uri,
450 uint8_t queryLength, unsigned char * query,
451 OCHeaderOption * vendorSpecificHeaderOptions,
452 uint8_t numVendorSpecificHeaderOptions)
454 coap_list_t * optNode = NULL;
457 unsigned char _buf[BUF_SIZE];
458 unsigned char *buf = _buf;
462 optNode = CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
463 sizeof(*addMediaType), addMediaType);
464 VERIFY_NON_NULL(optNode);
465 coap_insert(optListLoc, optNode, OrderOptions);
470 optNode = CreateNewOptionNode(COAP_OPTION_MAXAGE,
471 sizeof(*addMaxAge), (uint8_t *)addMaxAge);
472 VERIFY_NON_NULL(optNode);
473 coap_insert(optListLoc, optNode, OrderOptions);
476 if(observeOptionLength && observeOptionPtr)
478 optNode = CreateNewOptionNode(COAP_OPTION_OBSERVE,
479 observeOptionLength, (uint8_t *)observeOptionPtr);
481 VERIFY_NON_NULL(optNode);
482 coap_insert(optListLoc, optNode, OrderOptions);
484 if(addPortNumber && *addPortNumber != COAP_DEFAULT_PORT)
486 optNode = CreateNewOptionNode(COAP_OPTION_URI_PORT,
487 sizeof(*addPortNumber), (uint8_t *)addPortNumber);
488 VERIFY_NON_NULL(optNode);
489 coap_insert(optListLoc, optNode, OrderOptions);
496 res = coap_split_path(uri, uriLength, buf, &buflen);
498 optNode = CreateNewOptionNode(COAP_OPTION_URI_PATH,
499 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf));
500 VERIFY_NON_NULL(optNode);
501 coap_insert(optListLoc, optNode, OrderOptions);
502 buf += COAP_OPT_SIZE(buf);
506 if(query && queryLength)
510 res = coap_split_query(query, queryLength, buf, &buflen);
512 optNode = CreateNewOptionNode(COAP_OPTION_URI_QUERY,
513 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf));
514 VERIFY_NON_NULL(optNode);
515 coap_insert(optListLoc, optNode, OrderOptions);
516 buf += COAP_OPT_SIZE(buf);
520 // make sure that options are valid
521 if(vendorSpecificHeaderOptions && numVendorSpecificHeaderOptions)
524 for( i = 0; i < numVendorSpecificHeaderOptions; i++)
526 if(vendorSpecificHeaderOptions[i].protocolID == OC_COAP_ID)
528 if(isVendorSpecific(vendorSpecificHeaderOptions[i].optionID)
530 vendorSpecificHeaderOptions[i].optionLength <=
531 MAX_HEADER_OPTION_DATA_LENGTH)
533 OC_LOG_V(INFO, TAG, " Adding option %d with",
534 vendorSpecificHeaderOptions[i].optionID);
535 OC_LOG_BUFFER(INFO, TAG, vendorSpecificHeaderOptions[i].optionData,
536 vendorSpecificHeaderOptions[i].optionLength);
537 optNode = CreateNewOptionNode(vendorSpecificHeaderOptions[i].optionID,
538 vendorSpecificHeaderOptions[i].optionLength,
539 vendorSpecificHeaderOptions[i].optionData);
540 VERIFY_NON_NULL(optNode);
541 coap_insert(optListLoc, optNode, OrderOptions);
545 coap_delete_list(*optListLoc);
546 return OC_STACK_INVALID_OPTION;
554 coap_delete_list(*optListLoc);
555 return OC_STACK_NO_MEMORY;
560 SendCoAPPdu(coap_context_t * gCoAPCtx, coap_address_t* dst, coap_pdu_t * pdu,
561 coap_send_flags_t flag)
563 coap_tid_t tid = COAP_INVALID_TID;
564 OCStackResult res = OC_STACK_COMM_ERROR;
566 if (!(flag & SEND_DELAYED))
568 flag = (coap_send_flags_t)( flag |
569 (pdu->hdr->type == COAP_MESSAGE_CON) ? SEND_NOW_CON : SEND_NOW);
572 tid = coap_send(gCoAPCtx, dst, pdu, flag);
573 OC_LOG_V(INFO, TAG, "TID %d", tid);
574 if(tid != COAP_INVALID_TID)
576 OC_LOG(INFO, TAG, PCF("Sending a pdu with Token:"));
577 OC_LOG_BUFFER(INFO,TAG, pdu->hdr->token, pdu->hdr->token_length);
581 if ((pdu->hdr->type != COAP_MESSAGE_CON && (!(flag & SEND_DELAYED))) || tid == COAP_INVALID_TID)
583 OC_LOG(INFO, TAG, PCF("Deleting PDU"));
584 coap_delete_pdu(pdu);
588 OC_LOG(INFO, TAG, PCF("Keeping PDU, we will handle the retry/delay of this pdu"));
594 //generate a coap message
596 GenerateCoAPPdu(uint8_t msgType, uint8_t code, unsigned short id,
597 OCCoAPToken * token, unsigned char * payloadJSON,
598 coap_list_t *options)
605 pdu = coap_pdu_init(msgType, code, id, COAP_MAX_PDU_SIZE);
606 VERIFY_NON_NULL(pdu);
607 pdu->hdr->token_length = token->tokenLength;
608 if (!coap_add_token(pdu, token->tokenLength, token->token))
610 OC_LOG(FATAL, TAG, PCF("coap_add_token failed"));
615 pdu = coap_pdu_init(msgType, code, id, sizeof(coap_pdu_t));
616 VERIFY_NON_NULL(pdu);
619 for (opt = options; opt; opt = opt->next)
621 coap_add_option(pdu, COAP_OPTION_KEY(*(coap_option *) opt->data),
622 COAP_OPTION_LENGTH(*(coap_option *) opt->data),
623 COAP_OPTION_DATA(*(coap_option *) opt->data));
628 coap_add_data(pdu, strlen((const char *) payloadJSON) + 1,
629 (unsigned char*) payloadJSON);
632 // display the pdu for debugging purposes
636 coap_delete_list(options);
640 coap_delete_list(options);
644 //a function to help in ordering coap options
645 int OrderOptions(void *a, void *b)
649 return a < b ? -1 : 1;
652 if (COAP_OPTION_KEY(*(coap_option *)a)
653 < COAP_OPTION_KEY(*(coap_option *)b) )
658 return COAP_OPTION_KEY(*(coap_option *)a)
659 == COAP_OPTION_KEY(*(coap_option *)b) ;
662 //a function to create a coap option
664 CreateNewOptionNode(unsigned short key, unsigned int length, unsigned char *data)
666 coap_option *option = NULL;
669 VERIFY_NON_NULL(data);
670 option = (coap_option *)coap_malloc(sizeof(coap_option) + length);
671 VERIFY_NON_NULL(option);
673 COAP_OPTION_KEY(*option) = key;
674 COAP_OPTION_LENGTH(*option) = length;
675 memcpy(COAP_OPTION_DATA(*option), data, length);
677 /* we can pass NULL here as delete function since option is released automatically */
678 node = coap_new_listnode(option, NULL);
686 OC_LOG(ERROR,TAG, PCF("new_option_node: malloc: was not created"));
691 OCStackResult ReTXCoAPQueue(coap_context_t * ctx, coap_queue_t * queue)
693 coap_tid_t tid = COAP_INVALID_TID;
694 OCStackResult result = OC_STACK_ERROR;
695 tid = coap_retransmit( ctx, queue);
696 if(tid == COAP_INVALID_TID)
698 OC_LOG_V(DEBUG, TAG, "Retransmission Failed TID %d",
700 result = OC_STACK_COMM_ERROR;
704 OC_LOG_V(DEBUG, TAG, "Retransmission TID %d, this is attempt %d",
705 queue->id, queue->retransmit_cnt);
706 result = OC_STACK_OK;
711 OCStackResult HandleFailedCommunication(coap_context_t * ctx, coap_queue_t * queue)
713 OCResponse * response = NULL;
714 ClientCB * cbNode = NULL;
715 ResourceObserver * observer = NULL;
716 OCClientResponse clientResponse;
718 OCStackResult result = OC_STACK_OK;
720 RetrieveOCCoAPToken(queue->pdu, &token);
722 cbNode = GetClientCB(&token, NULL, NULL);
727 result = FormOCClientResponse(&clientResponse, OC_STACK_COMM_ERROR,
728 (OCDevAddr *) &(queue->remote), 0, NULL);
729 if(result != OC_STACK_OK)
733 result = FormOCResponse(&response, cbNode, 0, &clientResponse);
734 if(result != OC_STACK_OK)
738 HandleStackResponses(response);
741 observer = GetObserverUsingToken (&token);
747 result = OCObserverStatus(&token, OC_OBSERVER_FAILED_COMM);
748 if(result == OC_STACK_OK)
750 coap_cancel_all_messages(ctx, &queue->remote, token.token, token.tokenLength);
759 // a function to handle the send queue in the passed context
760 void HandleSendQueue(coap_context_t * ctx)
763 coap_queue_t *nextQueue = NULL;
766 nextQueue = coap_peek_next( ctx );
767 while (nextQueue && nextQueue->t <= now - ctx->sendqueue_basetime)
769 nextQueue = coap_pop_next( ctx );
770 if((uint8_t)nextQueue->delayedResponse)
772 OC_LOG_V(DEBUG, TAG, "Sending Delayed response TID %d",
774 if(SendCoAPPdu(ctx, &nextQueue->remote, nextQueue->pdu,
775 (coap_send_flags_t)(nextQueue->secure ? SEND_SECURE_PORT : 0))
776 == OC_STACK_COMM_ERROR)
778 OC_LOG(DEBUG, TAG, PCF("A problem occurred in sending a pdu"));
779 HandleFailedCommunication(ctx, nextQueue);
781 nextQueue->pdu = NULL;
782 coap_delete_node(nextQueue);
786 OC_LOG_V(DEBUG, TAG, "Retrying a CON pdu TID %d",nextQueue->id);
787 if(ReTXCoAPQueue(ctx, nextQueue) == OC_STACK_COMM_ERROR)
789 OC_LOG(DEBUG, TAG, PCF("A problem occurred in retransmitting a pdu"));
790 HandleFailedCommunication(ctx, nextQueue);
791 coap_delete_node(nextQueue);
794 nextQueue = coap_peek_next( ctx );