1 //******************************************************************
3 // Copyright 2014 Intel Corporation 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 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
22 //=============================================================================
24 //=============================================================================
26 #include "occlientcb.h"
27 #include "ocobserve.h"
36 //-----------------------------------------------------------------------------
38 //-----------------------------------------------------------------------------
39 #define TAG PCF("OCCoAP")
40 #define VERIFY_SUCCESS(op, successCode) { if (op != successCode) \
41 {OC_LOG(FATAL, TAG, #op " failed !!"); goto exit;} }
42 #define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG(FATAL, TAG, #arg " is NULL"); goto exit;} }
45 #define BUF_SIZE_ENCODE_OPTION (3)
46 #define BUF_SIZE_PORT (2)
48 //=============================================================================
50 //=============================================================================
52 static uint8_t coapWKIpAddr[] = { 224, 0, 1, 187 };
53 static coap_context_t *gCoAPCtx = NULL;
55 //=============================================================================
57 //=============================================================================
59 //generate a coap token
60 OCCoAPToken * OCGenerateCoAPToken()
62 OCCoAPToken *token = NULL;
63 // Generate token here, it will be deleted when the transaction is deleted
64 token = (OCCoAPToken *) malloc(sizeof(OCCoAPToken));
67 token->tokenLength = MAX_TOKEN_LENGTH;
68 OCFillRandomMem((uint8_t*)token->token, token->tokenLength);
74 //This function is called back by libcoap when a request is received
75 static void HandleCoAPRequests(struct coap_context_t *ctx,
76 const coap_queue_t * rcvdRequest)
82 OCRequest * request = NULL;
83 OCEntityHandlerRequest * entityHandlerRequest = NULL;
84 OCCoAPToken * rcvdToken = NULL;
86 unsigned char rcvdUri[MAX_URI_LENGTH] = { 0 };
87 unsigned char rcvdQuery[MAX_QUERY_LENGTH] = { 0 };
89 unsigned char bufRes[MAX_RESPONSE_LENGTH] = { 0 };
91 coap_list_t *optList = NULL;
92 unsigned char tempBuf[BUF_SIZE_ENCODE_OPTION];
94 coap_tid_t tid = COAP_INVALID_TID;
96 // fill OCRequest structure
97 result = FormOCRequest(rcvdRequest, &request, rcvdUri, rcvdQuery);
98 VERIFY_SUCCESS(result, OC_STACK_OK);
100 // fill OCEntityHandlerRequest structure
101 result = FormOCEntityHandlerRequest(rcvdRequest, &entityHandlerRequest,
103 VERIFY_SUCCESS(result, OC_STACK_OK);
105 // fill OCCoAPToken structure
106 result = RetrieveOCCoAPToken(rcvdRequest, &rcvdToken);
107 VERIFY_SUCCESS(result, OC_STACK_OK);
109 request->entityHandlerRequest = entityHandlerRequest;
111 OC_LOG_V(INFO, TAG, " Receveid uri: %s", request->resourceUrl);
112 OC_LOG_V(INFO, TAG, " Receveid query: %s", entityHandlerRequest->query);
113 OC_LOG_V(INFO, TAG, " Receveid payload: %s",
114 request->entityHandlerRequest->reqJSONPayload);
115 OC_LOG_V(INFO, TAG, " Token received %d bytes",
116 rcvdToken->tokenLength);
117 OC_LOG_BUFFER(INFO, TAG, rcvdToken->token, rcvdToken->tokenLength);
119 // process the request
120 result = HandleStackRequests(request);
122 if (result == OC_STACK_OK)
124 OC_LOG_V(INFO, TAG, "Response from ocstack: %s", request->entityHandlerRequest->resJSONPayload);
125 // need to build the response PDU
126 coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
127 coap_encode_var_bytes(tempBuf, COAP_MEDIATYPE_APPLICATION_JSON),
128 tempBuf), OrderOptions);
129 coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_MAXAGE,
130 coap_encode_var_bytes(tempBuf, 0x2ffff), tempBuf),
134 // generate the pdu, if the request was CON, then the response is ACK, otherwire NON
135 pdu = GenerateCoAPPdu(
136 rcvdRequest->pdu->hdr->type == COAP_MESSAGE_CON ?
137 COAP_MESSAGE_ACK : COAP_MESSAGE_NON,
138 OCToCoAPResponseCode(result), rcvdRequest->pdu->hdr->id,
139 rcvdToken->tokenLength, rcvdToken->token,
140 request->entityHandlerRequest->resJSONPayload, optList);
141 VERIFY_NON_NULL(pdu);
144 if (pdu->hdr->type != COAP_MESSAGE_NON
145 || (pdu->hdr->code >= 64 && !coap_is_mcast(&rcvdRequest->local)))
147 tid = coap_send(gCoAPCtx, &rcvdRequest->remote, pdu);
150 OC_LOG_V(INFO, TAG, "TID %d", tid);
151 // unlike stock libcoap (deletion in handle_request in net.c), we are deleting the response here
152 // in the future, the response might be queued for SLOW resources
153 if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
155 OC_LOG(INFO, TAG, "Deleting PDU");
156 coap_delete_pdu(pdu);
160 coap_delete_list(optList);
162 OCFree(entityHandlerRequest);
166 //This function is called back by libcoap when a response is received
167 static void HandleCoAPResponses(struct coap_context_t *ctx,
168 const coap_queue_t * rcvdResponse) {
169 OCResponse * response = NULL;
170 OCCoAPToken * token = NULL;
171 OCClientResponse * clientResponse = NULL;
172 ClientCB * cbNode = NULL;
173 OCStackResult result;
175 VERIFY_NON_NULL(ctx);
176 VERIFY_NON_NULL(rcvdResponse);
178 // TODO: we should check if we are interested in the token
179 // Now, just accept NON packets
181 if (rcvdResponse->pdu->hdr->type == COAP_MESSAGE_NON)
183 // fill OCResponse structure
184 result = FormOCResponse(rcvdResponse, &response);
185 VERIFY_SUCCESS(result, OC_STACK_OK);
187 // fill OCCoAPToken structure
188 result = RetrieveOCCoAPToken(rcvdResponse, &token);
189 VERIFY_SUCCESS(result, OC_STACK_OK);
191 // fill OCClientResponse structure
192 result = FormOCClientResponse(rcvdResponse, &clientResponse);
193 VERIFY_SUCCESS(result, OC_STACK_OK);
195 // put everything together
196 response->clientResponse = clientResponse;
198 cbNode = GetClientCB(token, NULL);
200 OC_LOG_V(INFO, TAG, " Received a response HandleCoAPResponses in occoap: %s",
201 response->clientResponse->resJSONPayload);
202 OC_LOG_V(INFO, TAG,"Token received %d bytes", token->tokenLength);
203 OC_LOG_BUFFER(INFO, TAG, token->token,
206 if(cbNode && (cbNode->method == OC_REST_OBSERVE || cbNode->method == OC_REST_OBSERVE_ALL))
208 if(clientResponse->sequenceNumber != 0)
210 if(cbNode->method == OC_REST_OBSERVE && (clientResponse->sequenceNumber <= cbNode->sequenceNumber))
212 OC_LOG_V(DEBUG, TAG, "Observe notification came out of order. \
213 Ignoring Incoming:%d Against Current:%d.",
214 clientResponse->sequenceNumber, cbNode->sequenceNumber);
219 cbNode->sequenceNumber = clientResponse->sequenceNumber;
223 else if(!cbNode && clientResponse && clientResponse->sequenceNumber != 0) // Ensure that this is an observe notification.
227 coap_list_t *optList = NULL;
228 coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_OBSERVE,
229 strlen(OC_RESOURCE_OBSERVE_DEREGISTER), (unsigned char *)OC_RESOURCE_OBSERVE_DEREGISTER), OrderOptions);
231 pdu = GenerateCoAPPdu(COAP_MESSAGE_NON, COAP_REQUEST_GET,
232 coap_new_message_id(gCoAPCtx), token->tokenLength, token->token,
233 (unsigned char*)"", optList);
234 VERIFY_NON_NULL(pdu);
236 tid = coap_send(gCoAPCtx, (coap_address_t*) &rcvdResponse->remote, pdu);
238 OC_LOG_V(INFO, TAG, "TID %d", tid);
239 if (tid != COAP_INVALID_TID)
241 OC_LOG(INFO, TAG, "Deleting PDU");
242 coap_delete_pdu(pdu);
246 OC_LOG(INFO, TAG, "Keeping PDU, we should handle the retry of this pdu");
250 response->cbNode = cbNode;
251 response->clientResponse->result = CoAPToOCResponseCode(rcvdResponse->pdu->hdr->code);
252 HandleStackResponses(response);
256 OC_LOG(DEBUG, TAG, "Do not accept other than NON in HandleCoAPResponses");
262 OCFree(clientResponse);
265 //=============================================================================
267 //=============================================================================
270 * Initialize the CoAP client or server with its IPv4 address and CoAP port
273 * IP Address of host device
275 * Port of host device
277 * Host device is client, server, or client-server
283 int OCInitCoAP(const char *address, uint16_t port, OCMode mode) {
285 int ret = OC_COAP_ERR;
287 TODO ("Below should go away and be replaced by OC_LOG");
288 coap_log_t log_level = LOG_DEBUG + 1;
291 uint8_t ipAddr[4] = { 0 };
293 OC_LOG(INFO, TAG, PCF("Entering OCInitCoAP"));
295 coap_set_log_level(log_level);
299 if (!ParseIPv4Address((unsigned char *) address, ipAddr))
303 OC_LOG_V(INFO, TAG, "Parsed IP Address %d.%d.%d.%d",ipAddr[0],ipAddr[1],ipAddr[2],ipAddr[3]);
306 OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], port,
309 gCoAPCtx = coap_new_context((coap_address_t*) &devAddr);
310 VERIFY_NON_NULL(gCoAPCtx);
311 if (mode != OC_CLIENT) {
312 OCBuildIPv4Address(coapWKIpAddr[0], coapWKIpAddr[1], coapWKIpAddr[2],
313 coapWKIpAddr[3], COAP_DEFAULT_PORT, &mcastAddr);
315 coap_join_wellknown_group(gCoAPCtx,
316 (coap_address_t* )&mcastAddr), 0);
319 coap_register_request_handler(gCoAPCtx, HandleCoAPRequests);
320 coap_register_response_handler(gCoAPCtx, HandleCoAPResponses);
325 if (ret != OC_COAP_OK)
327 OCStopCoAP(gCoAPCtx);
333 * Discover OC resources
335 * @param method - method to perform on the resource
336 * @param qos - Quality of Service the request will be sent on
337 * @param token - token which will added to the request
338 * @param Uri - URI of the resource to interact with
339 * @param payload - the request payload to be added to the request before sending
340 * by the stack when discovery or resource interaction is complete
345 int OCDoCoAPResource(OCMethod method, OCQualityOfService qos, OCCoAPToken * token,
346 const char *Uri, const char *payload)
349 int ret = OC_COAP_ERR;
350 coap_pdu_t *pdu = NULL;
353 uint8_t ipAddr[4] = { 0 };
354 coap_list_t *optList = NULL;
355 unsigned char portBuf[BUF_SIZE_PORT];
357 unsigned char _buf[BUF_SIZE];
358 unsigned char *buf = _buf;
363 OC_LOG(INFO, TAG, PCF("Entering OCDoCoAPResource"));
366 OC_LOG_V(INFO, TAG, "URI = %s", Uri);
367 VERIFY_SUCCESS(coap_split_uri((unsigned char * )Uri, strlen(Uri), &uri), 0);
369 // Generate the destination address
370 if (uri.host.length && ParseIPv4Address(uri.host.s, ipAddr)) {
371 OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], uri.port,
377 //create appropriate coap options
378 if (uri.port != COAP_DEFAULT_PORT) {
379 coap_insert(&optList,
380 CreateNewOptionNode(COAP_OPTION_URI_PORT,
381 coap_encode_var_bytes(portBuf, uri.port), portBuf),
385 if (uri.path.length) {
387 res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
390 coap_insert(&optList,
391 CreateNewOptionNode(COAP_OPTION_URI_PATH,
392 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
395 buf += COAP_OPT_SIZE(buf);
399 if (uri.query.length) {
402 res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
405 coap_insert(&optList,
406 CreateNewOptionNode(COAP_OPTION_URI_QUERY,
407 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
410 buf += COAP_OPT_SIZE(buf);
414 OC_LOG_V(DEBUG, TAG, "uri.host.s %s", uri.host.s);
415 OC_LOG_V(DEBUG, TAG, "uri.path.s %s", uri.path.s);
416 OC_LOG_V(DEBUG, TAG, "uri.port %d", uri.port);
417 OC_LOG_V(DEBUG, TAG, "uri.query.s %s", uri.query.s);
419 coapMsgType = COAP_MESSAGE_NON;
421 // Decide message type
422 if (qos == OC_CONFIRMABLE) {
423 coapMsgType = COAP_MESSAGE_CON;
424 OC_LOG(FATAL, TAG, "qos == OC_CONFIRMABLE is not supported in OCDoCoAPResource");
426 // Decide method type
429 coapMethod = COAP_REQUEST_GET;
432 coapMethod = COAP_REQUEST_PUT;
434 case OC_REST_OBSERVE_ALL:
435 case OC_REST_OBSERVE:
436 coapMethod = COAP_REQUEST_GET;
437 coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_OBSERVE,
438 strlen(OC_RESOURCE_OBSERVE_REGISTER), (unsigned char *)OC_RESOURCE_OBSERVE_REGISTER), OrderOptions);
443 OC_LOG(FATAL, TAG, "OCDoCoAPResource only supports GET, PUT, & OBSERVE methods");
447 VERIFY_NON_NULL(gCoAPCtx);
448 pdu = GenerateCoAPPdu(coapMsgType, coapMethod,
449 coap_new_message_id(gCoAPCtx), token->tokenLength, token->token,
450 (unsigned char*) payload, optList);
451 VERIFY_NON_NULL(pdu);
453 coap_send(gCoAPCtx, (coap_address_t*) &dst, pdu);
455 //OC_LOG_V(INFO, TAG, "TID %d", tid);
456 TODO ("Once CON implementation is available, pdu should be saved until ACK is received");
457 //if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
459 OC_LOG(INFO, TAG, "Deleting PDU");
460 coap_delete_pdu(pdu);
467 coap_delete_list(optList);
468 if (ret != OC_COAP_OK)
470 coap_delete_pdu(pdu);
475 int OCCoAPSendMessage (OCDevAddr *dstAddr, OCStackResult msgCode,
476 OCQualityOfService qos, OCCoAPToken * token,
477 const char *payload, uint32_t seqNum)
479 coap_list_t *optList = NULL;
481 unsigned char tempBuf[BUF_SIZE_ENCODE_OPTION];
482 uint8_t coapMsgType = COAP_MESSAGE_NON;
483 coap_tid_t tid = COAP_INVALID_TID;
485 OC_LOG(INFO, TAG, PCF("Entering OCCoAPSendMessage"));
487 OC_LOG_V(INFO, TAG, "OCStack payload: %s", payload);
488 coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
489 coap_encode_var_bytes(tempBuf, COAP_MEDIATYPE_APPLICATION_JSON),
490 tempBuf), OrderOptions);
491 coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_MAXAGE,
492 coap_encode_var_bytes(tempBuf, 0x2ffff), tempBuf), OrderOptions);
493 coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_OBSERVE,
494 coap_encode_var_bytes(tempBuf, seqNum), tempBuf), OrderOptions);
496 pdu = GenerateCoAPPdu (coapMsgType, OCToCoAPResponseCode(msgCode),
497 coap_new_message_id(gCoAPCtx), token->tokenLength, token->token,
498 (unsigned char*) payload, optList);
499 VERIFY_NON_NULL(pdu);
502 tid = coap_send(gCoAPCtx, (coap_address_t*)dstAddr, pdu);
503 OC_LOG_V(INFO, TAG, "TID %d", tid);
504 if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
506 OC_LOG(INFO, TAG, "Deleting PDU");
507 coap_delete_pdu(pdu);
513 coap_delete_list(optList);
518 * Stop the CoAP client or server processing
520 * @return 0 - success, else - TBD error
523 OC_LOG(INFO, TAG, PCF("Entering OCStopCoAP"));
524 coap_free_context(gCoAPCtx);
530 * Called in main loop of CoAP client or server. Allows low-level CoAP processing of
531 * send, receive, timeout, discovery, callbacks, etc.
533 * @return 0 - success, else - TBD error
535 int OCProcessCoAP() {
536 OC_LOG(INFO, TAG, PCF("Entering OCProcessCoAP"));
538 read = coap_read(gCoAPCtx, gCoAPCtx->sockfd);
541 OC_LOG(INFO, TAG, "This is a Unicast<============");
543 if (-1 != gCoAPCtx->sockfd_wellknown) {
544 read = coap_read(gCoAPCtx, gCoAPCtx->sockfd_wellknown);
547 OC_LOG(INFO, TAG, "This is a Multicast<===========");
550 coap_dispatch(gCoAPCtx);