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,
322 OCRequest * request = NULL;
325 request = (OCRequest *) OCMalloc(sizeof(OCRequest));
328 return OC_STACK_NO_MEMORY;
335 request->resourceUrl = uriBuf;
337 request->secure = secure;
340 request->observe = observeReq;
342 // add entityHandlerRequest
343 request->entityHandlerRequest = entityHandlerRequest;
345 //TODO: this needs to be filled in the future
346 request->sequenceNum = 0;
348 *requestLoc = request;
352 // Form the OCObserveReq struct
353 OCStackResult FormOCObserveReq(OCObserveReq ** observeReqLoc, uint32_t observeOption,
354 OCDevAddr * remote, OCCoAPToken * rcvdToken)
356 OCObserveReq * observeReq;
358 if(observeOption == OC_RESOURCE_NO_OBSERVE)
363 observeReq = (OCObserveReq *)OCMalloc(sizeof(OCObserveReq));
366 *observeReqLoc = NULL;
367 return OC_STACK_NO_MEMORY;
370 observeReq->option = observeOption;
371 observeReq->subAddr = remote;
372 observeReq->token = rcvdToken;
373 observeReq->result = OC_STACK_OK;
375 *observeReqLoc = observeReq;
379 // Form the OCEntityHandlerRequest struct
380 OCStackResult FormOCEntityHandlerRequest(OCEntityHandlerRequest * entityHandlerRequestLoc,
381 OCMethod method, unsigned char * resBuf, unsigned char * bufReqPayload,
382 unsigned char * queryBuf, unsigned char *newResUriBuf)
384 if (entityHandlerRequestLoc)
386 //set it to NULL for now, it will be modified in ocstack
387 entityHandlerRequestLoc->resource = NULL;
389 entityHandlerRequestLoc->method = method;
392 entityHandlerRequestLoc->query = queryBuf;
395 entityHandlerRequestLoc->reqJSONPayload = bufReqPayload;
397 entityHandlerRequestLoc->resJSONPayload = resBuf;
398 entityHandlerRequestLoc->resJSONPayloadLen = MAX_RESPONSE_LENGTH;
400 entityHandlerRequestLoc->obsInfo = NULL;
401 entityHandlerRequestLoc->newResourceUri = newResUriBuf;
403 entityHandlerRequestLoc->numRcvdVendorSpecificHeaderOptions = 0;
404 entityHandlerRequestLoc->numSendVendorSpecificHeaderOptions = 0;
408 return OC_STACK_INVALID_PARAM;
411 // Retrieve the token from the PDU
412 void RetrieveOCCoAPToken(const coap_pdu_t * pdu, OCCoAPToken * rcvdToken)
414 if (pdu && rcvdToken)
416 rcvdToken->tokenLength = pdu->hdr->token_length;
417 memcpy(rcvdToken->token, pdu->hdr->token,
418 rcvdToken->tokenLength);
422 OCStackResult FormOCResponse(OCResponse * * responseLoc, ClientCB * cbNode,
423 uint8_t TTL, OCClientResponse * clientResponse)
425 OCResponse * response = (OCResponse *) OCMalloc(sizeof(OCResponse));
428 return OC_STACK_NO_MEMORY;
430 response->cbNode = cbNode;
432 response->clientResponse = clientResponse;
434 *responseLoc = response;
438 OCStackResult FormOCClientResponse(OCClientResponse * clientResponse,
439 OCStackResult result, OCDevAddr * remote, uint32_t seqNum,
440 const unsigned char * resJSONPayload)
442 clientResponse->sequenceNumber = seqNum;
443 clientResponse->result = result;
444 clientResponse->addr = remote;
445 clientResponse->resJSONPayload = resJSONPayload;
450 OCStackResult FormOptionList(coap_list_t * * optListLoc, uint8_t * addMediaType,
451 uint32_t * addMaxAge, uint8_t observeOptionLength, uint32_t * observeOptionPtr,
452 uint16_t * addPortNumber, uint8_t uriLength, unsigned char * uri,
453 uint8_t queryLength, unsigned char * query,
454 OCHeaderOption * vendorSpecificHeaderOptions,
455 uint8_t numVendorSpecificHeaderOptions)
457 coap_list_t * optNode = NULL;
460 unsigned char _buf[BUF_SIZE];
461 unsigned char *buf = _buf;
465 optNode = CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
466 sizeof(*addMediaType), addMediaType);
467 VERIFY_NON_NULL(optNode);
468 coap_insert(optListLoc, optNode, OrderOptions);
473 optNode = CreateNewOptionNode(COAP_OPTION_MAXAGE,
474 sizeof(*addMaxAge), (uint8_t *)addMaxAge);
475 VERIFY_NON_NULL(optNode);
476 coap_insert(optListLoc, optNode, OrderOptions);
479 if(observeOptionLength && observeOptionPtr)
481 optNode = CreateNewOptionNode(COAP_OPTION_OBSERVE,
482 observeOptionLength, (uint8_t *)observeOptionPtr);
484 VERIFY_NON_NULL(optNode);
485 coap_insert(optListLoc, optNode, OrderOptions);
487 if(addPortNumber && *addPortNumber != COAP_DEFAULT_PORT)
489 optNode = CreateNewOptionNode(COAP_OPTION_URI_PORT,
490 sizeof(*addPortNumber), (uint8_t *)addPortNumber);
491 VERIFY_NON_NULL(optNode);
492 coap_insert(optListLoc, optNode, OrderOptions);
499 res = coap_split_path(uri, uriLength, buf, &buflen);
501 optNode = CreateNewOptionNode(COAP_OPTION_URI_PATH,
502 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf));
503 VERIFY_NON_NULL(optNode);
504 coap_insert(optListLoc, optNode, OrderOptions);
505 buf += COAP_OPT_SIZE(buf);
509 if(query && queryLength)
513 res = coap_split_query(query, queryLength, buf, &buflen);
515 optNode = CreateNewOptionNode(COAP_OPTION_URI_QUERY,
516 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf));
517 VERIFY_NON_NULL(optNode);
518 coap_insert(optListLoc, optNode, OrderOptions);
519 buf += COAP_OPT_SIZE(buf);
523 // make sure that options are valid
524 if(vendorSpecificHeaderOptions && numVendorSpecificHeaderOptions)
527 for( i = 0; i < numVendorSpecificHeaderOptions; i++)
529 if(vendorSpecificHeaderOptions[i].protocolID == OC_COAP_ID)
531 if(isVendorSpecific(vendorSpecificHeaderOptions[i].optionID)
533 vendorSpecificHeaderOptions[i].optionLength <=
534 MAX_HEADER_OPTION_DATA_LENGTH)
536 OC_LOG_V(INFO, TAG, " Adding option %d with",
537 vendorSpecificHeaderOptions[i].optionID);
538 OC_LOG_BUFFER(INFO, TAG, vendorSpecificHeaderOptions[i].optionData,
539 vendorSpecificHeaderOptions[i].optionLength);
540 optNode = CreateNewOptionNode(vendorSpecificHeaderOptions[i].optionID,
541 vendorSpecificHeaderOptions[i].optionLength,
542 vendorSpecificHeaderOptions[i].optionData);
543 VERIFY_NON_NULL(optNode);
544 coap_insert(optListLoc, optNode, OrderOptions);
548 coap_delete_list(*optListLoc);
549 return OC_STACK_INVALID_OPTION;
557 coap_delete_list(*optListLoc);
558 return OC_STACK_NO_MEMORY;
563 SendCoAPPdu(coap_context_t * gCoAPCtx, coap_address_t* dst, coap_pdu_t * pdu,
564 coap_send_flags_t flag)
566 coap_tid_t tid = COAP_INVALID_TID;
567 OCStackResult res = OC_STACK_COMM_ERROR;
570 if (!(flag & SEND_DELAYED))
572 flag = (coap_send_flags_t)( flag |
573 ((pdu->hdr->type == COAP_MESSAGE_CON) ? SEND_NOW_CON : SEND_NOW));
576 tid = coap_send(gCoAPCtx, dst, pdu, flag, &cache);
577 OC_LOG_V(INFO, TAG, "TID %d", tid);
578 if(tid != COAP_INVALID_TID)
580 OC_LOG(INFO, TAG, PCF("Sending a pdu with Token:"));
581 OC_LOG_BUFFER(INFO,TAG, pdu->hdr->token, pdu->hdr->token_length);
585 if (( (pdu->hdr->type != COAP_MESSAGE_CON) && (!(flag & SEND_DELAYED)) && (!cache))
586 || (tid == COAP_INVALID_TID))
588 OC_LOG(INFO, TAG, PCF("Deleting PDU"));
589 coap_delete_pdu(pdu);
593 OC_LOG(INFO, TAG, PCF("Keeping PDU, we will handle the retry/delay of this pdu"));
599 //generate a coap message
601 GenerateCoAPPdu(uint8_t msgType, uint8_t code, unsigned short id,
602 OCCoAPToken * token, unsigned char * payloadJSON,
603 coap_list_t *options)
610 pdu = coap_pdu_init(msgType, code, id, COAP_MAX_PDU_SIZE);
611 VERIFY_NON_NULL(pdu);
612 pdu->hdr->token_length = token->tokenLength;
613 if (!coap_add_token(pdu, token->tokenLength, token->token))
615 OC_LOG(FATAL, TAG, PCF("coap_add_token failed"));
620 pdu = coap_pdu_init(msgType, code, id, sizeof(coap_pdu_t));
621 VERIFY_NON_NULL(pdu);
624 for (opt = options; opt; opt = opt->next)
626 coap_add_option(pdu, COAP_OPTION_KEY(*(coap_option *) opt->data),
627 COAP_OPTION_LENGTH(*(coap_option *) opt->data),
628 COAP_OPTION_DATA(*(coap_option *) opt->data));
633 coap_add_data(pdu, strlen((const char *) payloadJSON) + 1,
634 (unsigned char*) payloadJSON);
637 // display the pdu for debugging purposes
641 coap_delete_list(options);
645 coap_delete_list(options);
649 //a function to help in ordering coap options
650 int OrderOptions(void *a, void *b)
654 return a < b ? -1 : 1;
657 if (COAP_OPTION_KEY(*(coap_option *)a)
658 < COAP_OPTION_KEY(*(coap_option *)b) )
663 return COAP_OPTION_KEY(*(coap_option *)a)
664 == COAP_OPTION_KEY(*(coap_option *)b) ;
667 //a function to create a coap option
669 CreateNewOptionNode(unsigned short key, unsigned int length, unsigned char *data)
671 coap_option *option = NULL;
674 VERIFY_NON_NULL(data);
675 option = (coap_option *)coap_malloc(sizeof(coap_option) + length);
676 VERIFY_NON_NULL(option);
678 COAP_OPTION_KEY(*option) = key;
679 COAP_OPTION_LENGTH(*option) = length;
680 memcpy(COAP_OPTION_DATA(*option), data, length);
682 /* we can pass NULL here as delete function since option is released automatically */
683 node = coap_new_listnode(option, NULL);
691 OC_LOG(ERROR,TAG, PCF("new_option_node: malloc: was not created"));
696 OCStackResult ReTXCoAPQueue(coap_context_t * ctx, coap_queue_t * queue)
698 coap_tid_t tid = COAP_INVALID_TID;
699 OCStackResult result = OC_STACK_ERROR;
700 tid = coap_retransmit( ctx, queue);
701 if(tid == COAP_INVALID_TID)
703 OC_LOG_V(DEBUG, TAG, "Retransmission Failed TID %d",
705 result = OC_STACK_COMM_ERROR;
709 OC_LOG_V(DEBUG, TAG, "Retransmission TID %d, this is attempt %d",
710 queue->id, queue->retransmit_cnt);
711 result = OC_STACK_OK;
716 OCStackResult HandleFailedCommunication(coap_context_t * ctx, coap_queue_t * queue)
718 OCResponse * response = NULL;
719 ClientCB * cbNode = NULL;
720 ResourceObserver * observer = NULL;
721 OCClientResponse clientResponse;
723 OCStackResult result = OC_STACK_OK;
725 RetrieveOCCoAPToken(queue->pdu, &token);
727 cbNode = GetClientCB(&token, NULL, NULL);
732 result = FormOCClientResponse(&clientResponse, OC_STACK_COMM_ERROR,
733 (OCDevAddr *) &(queue->remote), 0, NULL);
734 if(result != OC_STACK_OK)
738 result = FormOCResponse(&response, cbNode, 0, &clientResponse);
739 if(result != OC_STACK_OK)
743 HandleStackResponses(response);
746 observer = GetObserverUsingToken (&token);
752 result = OCObserverStatus(&token, OC_OBSERVER_FAILED_COMM);
753 if(result == OC_STACK_OK)
755 coap_cancel_all_messages(ctx, &queue->remote, token.token, token.tokenLength);
764 // a function to handle the send queue in the passed context
765 void HandleSendQueue(coap_context_t * ctx)
768 coap_queue_t *nextQueue = NULL;
771 nextQueue = coap_peek_next( ctx );
772 while (nextQueue && nextQueue->t <= now - ctx->sendqueue_basetime)
774 nextQueue = coap_pop_next( ctx );
775 if((uint8_t)nextQueue->delayedResponse)
777 OC_LOG_V(DEBUG, TAG, "Sending Delayed response TID %d",
779 if(SendCoAPPdu(ctx, &nextQueue->remote, nextQueue->pdu,
780 (coap_send_flags_t)(nextQueue->secure ? SEND_SECURE_PORT : 0))
781 == OC_STACK_COMM_ERROR)
783 OC_LOG(DEBUG, TAG, PCF("A problem occurred in sending a pdu"));
784 HandleFailedCommunication(ctx, nextQueue);
786 nextQueue->pdu = NULL;
787 coap_delete_node(nextQueue);
791 OC_LOG_V(DEBUG, TAG, "Retrying a CON pdu TID %d",nextQueue->id);
792 if(ReTXCoAPQueue(ctx, nextQueue) == OC_STACK_COMM_ERROR)
794 OC_LOG(DEBUG, TAG, PCF("A problem occurred in retransmitting a pdu"));
795 HandleFailedCommunication(ctx, nextQueue);
796 coap_delete_node(nextQueue);
799 nextQueue = coap_peek_next( ctx );