1 //******************************************************************
3 // Copyright 2014 Intel Corporation All Rights Reserved.
4 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7 //=============================================================================
9 //=============================================================================
16 //-----------------------------------------------------------------------------
18 //-----------------------------------------------------------------------------
19 #define TAG PCF("OCCoAP")
20 #define VERIFY_SUCCESS(op, successCode) { if (op != successCode) \
21 {OC_LOG(FATAL, TAG, #op " failed !!"); goto exit;} }
22 #define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG(FATAL, TAG, #arg " is NULL"); goto exit;} }
25 #define BUF_SIZE_ENCODE_OPTION (3)
26 #define BUF_SIZE_PORT (2)
28 //=============================================================================
30 //=============================================================================
32 static uint8_t coapWKIpAddr[] = { 224, 0, 1, 187 };
33 static coap_context_t *gCoAPCtx = NULL;
35 //=============================================================================
37 //=============================================================================
39 //generate a coap message
41 GenerateCoAPPdu(uint8_t msgType, uint8_t code, unsigned short id,
42 size_t tokenLength, uint8_t * token, coap_list_t *options) {
46 pdu = coap_pdu_init(msgType, code, id, COAP_MAX_PDU_SIZE);
49 pdu->hdr->token_length = tokenLength;
50 if (!coap_add_token(pdu, tokenLength, token)) {
51 OC_LOG(FATAL, TAG, "coap_add_token failed");
54 // display the pdu for debugging purposes
57 for (opt = options; opt; opt = opt->next) {
58 coap_add_option(pdu, COAP_OPTION_KEY(*(coap_option *)opt->data),
59 COAP_OPTION_LENGTH(*(coap_option *)opt->data),
60 COAP_OPTION_DATA(*(coap_option *)opt->data));
67 //a function to help in ordering coap options
68 static int OrderOptions(void *a, void *b) {
70 return a < b ? -1 : 1;
73 if (COAP_OPTION_KEY(*(coap_option *)a)
74 < COAP_OPTION_KEY(*(coap_option *)b) ) {
78 return COAP_OPTION_KEY(*(coap_option *)a)
79 == COAP_OPTION_KEY(*(coap_option *)b) ;
82 //a function to create a coap option
84 CreateNewOptionNode(unsigned short key, unsigned int length,
85 unsigned char *data) {
89 option = coap_malloc(sizeof(coap_option) + length);
94 COAP_OPTION_KEY(*option) = key;
95 COAP_OPTION_LENGTH(*option) = length;
96 VERIFY_NON_NULL(data);
97 memcpy(COAP_OPTION_DATA(*option), data, length);
99 /* we can pass NULL here as delete function since option is released automatically */
100 node = coap_new_listnode(option, NULL);
106 exit: OC_LOG(ERROR,TAG,"new_option_node: malloc: was not created");
111 //This function is called back by libcoap when a response is received
112 static void HandleCoAPRequests(struct coap_context_t *ctx,
113 const coap_queue_t * rcvdRequest) {
117 // fill OCRequest structure
119 OCEntityHandlerRequest * entityHandlerRequest;
120 unsigned char bufReq[MAX_REQUEST_LENGTH] = { 0 };
121 unsigned char bufRes[MAX_RESPONSE_LENGTH] = { 0 };
124 coap_opt_iterator_t opt_iter;
125 coap_opt_filter_t filter;
127 unsigned char rcvdUri[MAX_URI_LENGTH] = { 0 };
128 unsigned char rcvdQuery[MAX_QUERY_LENGTH] = { 0 };
129 OCStackResult result;
130 coap_list_t *optList = NULL;
131 unsigned char tempBuf[BUF_SIZE_ENCODE_OPTION];
133 coap_tid_t tid = COAP_INVALID_TID;
135 //TODO:will be deleted at the end
136 entityHandlerRequest = (OCEntityHandlerRequest *) malloc(
137 sizeof(OCEntityHandlerRequest));
139 entityHandlerRequest->method = OC_REST_NOMETHOD;
140 if (rcvdRequest->pdu->hdr->code == COAP_REQUEST_GET) {
141 entityHandlerRequest->method = OC_REST_GET;
143 OC_LOG(DEBUG, TAG, "!!!!!!!!!!!!!!!!!!!!!!!method not supported in HandleCoAPRequests");
146 coap_get_data(rcvdRequest->pdu, &bufLen, (unsigned char **)&bufReq);
148 OC_LOG_V(INFO, TAG, "received payload %d bytes : %s", bufLen, bufReq);
149 entityHandlerRequest->reqJSONPayload = bufReq;
151 entityHandlerRequest->resJSONPayload = bufRes;
152 entityHandlerRequest->resJSONPayloadLen = MAX_RESPONSE_LENGTH;
154 //TODO:will be deleted at the end
155 rcvdToken = (OCToken *) malloc(sizeof(OCToken));
156 rcvdToken->tokenLength = rcvdRequest->pdu->hdr->token_length;
157 memcpy(rcvdToken->token, rcvdRequest->pdu->hdr->token,
158 rcvdToken->tokenLength);
160 OC_LOG_V(INFO,TAG,"Token received %d bytes..........%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
161 rcvdToken->tokenLength,rcvdToken->token[0],rcvdToken->token[1],rcvdToken->token[2],rcvdToken->token[3],
162 rcvdToken->token[4],rcvdToken->token[5],rcvdToken->token[6],rcvdToken->token[7]);
163 entityHandlerRequest->token = rcvdToken;
165 //TODO:will be deleted at the end
166 request = (OCRequest *) malloc(sizeof(OCRequest));
167 request->entityHandlerRequest = entityHandlerRequest;
169 request->qos = OC_NON_CONFIRMABLE;
170 if (rcvdRequest->pdu->hdr->type == COAP_MESSAGE_CON) {
171 request->qos = OC_CONFIRMABLE;
175 request->resourceUrl = NULL;
177 coap_option_filter_clear(filter);
178 coap_option_setb(filter, COAP_OPTION_URI_PATH);
179 coap_option_iterator_init(rcvdRequest->pdu, &opt_iter, filter);
180 while ((option = coap_option_next(&opt_iter))) {
181 rcvdUri[bufLen++] = '/';
182 memcpy(rcvdUri + bufLen, COAP_OPT_VALUE(option),
183 COAP_OPT_LENGTH(option));
184 bufLen += COAP_OPT_LENGTH(option);
186 rcvdUri[bufLen] = '\0';
187 OC_LOG_V(INFO, TAG, " Receveid uri %s", rcvdUri);
188 request->resourceUrl = rcvdUri;
190 request->query = NULL;
192 coap_option_filter_clear(filter);
193 coap_option_setb(filter, COAP_OPTION_URI_QUERY);
194 coap_option_iterator_init(rcvdRequest->pdu, &opt_iter, filter);
195 while ((option = coap_option_next(&opt_iter))) {
196 memcpy(rcvdQuery + bufLen, COAP_OPT_VALUE(option),
197 COAP_OPT_LENGTH(option));
198 bufLen += COAP_OPT_LENGTH(option);
199 rcvdQuery[bufLen++] = '&';
202 rcvdQuery[bufLen - 1] = '\0';
203 OC_LOG_V(INFO, TAG, " Receveid query %s", rcvdQuery);
204 request->query = rcvdQuery;
206 result = OCStackHandleReceiveRequest(request);
208 if (result == OC_STACK_OK) {
209 OC_LOG_V(INFO, TAG, "Response from ocstack: %s", request->entityHandlerRequest->resJSONPayload);
210 // need to build the response PDU
211 coap_insert(&optList,
212 CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
213 coap_encode_var_bytes(tempBuf,
214 COAP_MEDIATYPE_APPLICATION_JSON), tempBuf),
216 coap_insert(&optList,
217 CreateNewOptionNode(COAP_OPTION_MAXAGE,
218 coap_encode_var_bytes(tempBuf, 0x2ffff), tempBuf),
221 // generate the pdu, if the request was CON, then the response is ACK, otherwire NON
222 pdu = GenerateCoAPPdu(
223 rcvdRequest->pdu->hdr->type == COAP_MESSAGE_CON ?
224 COAP_MESSAGE_ACK : COAP_MESSAGE_NON,
225 COAP_RESPONSE_CODE(205), rcvdRequest->pdu->hdr->id,
226 rcvdToken->tokenLength, rcvdToken->token, optList);
228 strlen((const char *)request->entityHandlerRequest->resJSONPayload),
229 (unsigned char*) request->entityHandlerRequest->resJSONPayload);
231 if (pdu->hdr->type != COAP_MESSAGE_NON
232 || (pdu->hdr->code >= 64 && !coap_is_mcast(&rcvdRequest->local))) {
233 tid = coap_send(gCoAPCtx, &rcvdRequest->remote, pdu);
234 }OC_LOG_V(INFO, TAG, "TID %d", tid);OC_LOG(INFO, TAG, "Deleting PDU");
235 // unlike stock libcoap (deletion in handle_request in net.c), we are deleting the response here
236 // in the future, the response might be queued for SLOW resources
237 coap_delete_pdu(pdu);
239 coap_delete_list(optList);
241 OC_LOG(INFO, TAG, "Done Cleaning up");
243 OC_LOG(DEBUG, TAG, "We not supporting other returns of OCServerRequestResult in HandleCoAPRequests/"
244 " ......................yet");
247 free(entityHandlerRequest);
251 //This function is called back by libcoap when a response is received
252 static void HandleCoAPResponses(struct coap_context_t *ctx,
253 const coap_queue_t * rcvdResponse) {
254 OCResponse * response;
256 OCClientResponse * clientResponse;
257 unsigned char bufRes[MAX_RESPONSE_LENGTH] = { 0 };
258 unsigned char * pRes = NULL;
261 VERIFY_NON_NULL(ctx);
262 VERIFY_NON_NULL(rcvdResponse);
264 // TODO: we should check if we are interested in the token
265 // Now, just accept NON packets
267 if (rcvdResponse->pdu->hdr->type == COAP_MESSAGE_NON) {
268 // These will be deleted at the end
269 response = (OCResponse *) malloc(sizeof(OCResponse));
270 token = (OCToken *) malloc(sizeof(OCToken));
271 clientResponse = (OCClientResponse *) malloc(sizeof(OCClientResponse));
273 token->tokenLength = rcvdResponse->pdu->hdr->token_length;
274 memcpy(token->token, rcvdResponse->pdu->hdr->token, token->tokenLength);
276 clientResponse->addr = (OCDevAddr *)&(rcvdResponse->remote);
278 coap_get_data(rcvdResponse->pdu, &bufLen, &pRes);
279 memcpy(bufRes, pRes, bufLen);
281 clientResponse->resJSONPayload = bufRes;
282 OC_LOG_V(INFO, TAG, "Received a response HandleCoAPResponses in occoap: %s ", bufRes);
284 clientResponse->result = OC_STACK_ERROR;
286 response->token = token;
287 response->clientResponse = clientResponse;
289 if (rcvdResponse->pdu->hdr->code == COAP_RESPONSE_CODE(205)) {
290 response->clientResponse->result = OC_STACK_OK;
291 OCStackHandleReceiveResponse(response);
293 OC_LOG(DEBUG, TAG, "No other response codes are supported in HandleCoAPResponsesooooooooooooooooooooooo");
298 free(clientResponse);
300 OC_LOG(DEBUG, TAG, "Do not accept other than NON in HandleCoAPResponsesooooooooooooooooooooooo");
304 unsigned char *dataBuf;
305 const void *addrPtr = NULL;
307 uint8_t remoteIpAddr[4];
308 uint16_t remotePortNu;
310 VERIFY_NON_NULL(received);
311 VERIFY_NON_NULL(ctx);
312 VERIFY_NON_NULL(remote);
314 /* check if this is a response to our original request */
315 if (!CheckToken(received)) {
319 switch (received->hdr->type) {
320 case COAP_MESSAGE_CON:
321 /* acknowledge received response if confirmable (TODO: check Token) */
322 coap_send_ack(ctx, remote, received);
324 case COAP_MESSAGE_RST:
330 if (received->hdr->code == COAP_RESPONSE_CODE(205)) {
331 coap_get_data(received, &len, &dataBuf);
332 OC_LOG_V(INFO, TAG, "This is a success COAP_RESPONSE_CODE(205):Payload data: %s", dataBuf);
335 // TODO: we should call OCHandleClientReceiveResponse
336 /*if (storedOCStackCB) {
337 OCDevAddrToIPv4Addr((OCDevAddr *) remote, remoteIpAddr, remoteIpAddr + 1, remoteIpAddr + 2, remoteIpAddr + 3);
338 OCDevAddrToPort((OCDevAddr *) remote, &remotePortNu);
339 sprintf(buf, "%d.%d.%d.%d:%d", remoteIpAddr[0], remoteIpAddr[1], remoteIpAddr[2], remoteIpAddr[3], remotePortNu);
340 OC_LOG_V(INFO, TAG, "Calling storedOCStackCB function with remote address %s",buf);
341 storedOCStackCB(buf);
348 static void parseIPv4Address(const unsigned char * ipAddrStr, uint8_t * ipAddr) {
352 if (isdigit((unsigned char) *ipAddrStr)) {
354 ipAddr[index] += *ipAddrStr - '0';
355 } else if ((unsigned char) *ipAddrStr == '.') {
363 //=============================================================================
365 //=============================================================================
368 * Initialize the CoAP client or server with its IPv4 address and CoAP port
371 * IP Address of host device
373 * Port of host device
375 * Host device is client, server, or client-server
381 int OCInitCoAP(const char *address, uint16_t port, OCMode mode) {
383 int ret = OC_COAP_ERR;
385 TODO ("Below should go away and be replaced by OC_LOG");
386 coap_log_t log_level = LOG_DEBUG + 1;
387 OCDevAddr devAddr, mcastAddr;
388 uint8_t ipAddr[4] = { 0 };
390 OC_LOG(INFO, TAG, PCF("Entering OCInitCoAP"));
392 coap_set_log_level(log_level);
395 parseIPv4Address((const unsigned char *) address, ipAddr);
396 OC_LOG_V(INFO, TAG, "Parsed IP Address %d.%d.%d.%d",ipAddr[0],ipAddr[1],ipAddr[2],ipAddr[3]);
399 OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], port,
402 gCoAPCtx = coap_new_context((coap_address_t*) &devAddr);
403 VERIFY_NON_NULL(gCoAPCtx);
404 if (mode != OC_CLIENT) {
405 OCBuildIPv4Address(coapWKIpAddr[0], coapWKIpAddr[1], coapWKIpAddr[2],
406 coapWKIpAddr[3], COAP_DEFAULT_PORT, &mcastAddr);
408 coap_join_wellknown_group(gCoAPCtx,
409 (coap_address_t* )&mcastAddr), 0);
412 coap_register_request_handler(gCoAPCtx, HandleCoAPRequests);
413 coap_register_response_handler(gCoAPCtx, HandleCoAPResponses);
421 * Discover OC resources
423 * @param method - method to perform on the resource
424 * @param Uri - URI of the resource to interact with
425 * @param asyncReturnFunc - asynchronous callback function that is invoked
426 * by the stack when discovery or resource interaction is complete
431 int OCDoCoAPResource(OCMethod method, OCQualityOfService qos, OCToken * token,
434 int ret = OC_COAP_ERR;
435 coap_pdu_t *pdu = NULL;
437 coap_tid_t tid = COAP_INVALID_TID;
439 uint8_t ipAddr[4] = { 0 };
440 coap_list_t *optList = NULL;
441 unsigned char portBuf[BUF_SIZE_PORT];
443 unsigned char _buf[BUF_SIZE];
444 unsigned char *buf = _buf;
449 OC_LOG(INFO, TAG, PCF("Entering OCDoCoAPResource"));
452 OC_LOG_V(INFO, TAG, "URI = %s", Uri);
455 VERIFY_SUCCESS(coap_split_uri((unsigned char * )Uri, strlen(Uri), &uri), 0);
457 // Generate the destination address
458 if (uri.host.length) {
459 parseIPv4Address(uri.host.s, ipAddr);
460 OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], uri.port,
466 //create appropriate coap options
467 if (uri.port != COAP_DEFAULT_PORT) {
468 coap_insert(&optList,
469 CreateNewOptionNode(COAP_OPTION_URI_PORT,
470 coap_encode_var_bytes(portBuf, uri.port), portBuf),
474 if (uri.path.length) {
476 res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
479 coap_insert(&optList,
480 CreateNewOptionNode(COAP_OPTION_URI_PATH,
481 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
484 buf += COAP_OPT_SIZE(buf);
488 if (uri.query.length) {
491 res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
494 coap_insert(&optList,
495 CreateNewOptionNode(COAP_OPTION_URI_QUERY,
496 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
499 buf += COAP_OPT_SIZE(buf);
503 OC_LOG_V(DEBUG, TAG, "uri.host.s %s", uri.host.s);OC_LOG_V(DEBUG, TAG, "uri.path.s %s", uri.path.s);OC_LOG_V(DEBUG, TAG, "uri.port %d", uri.port);OC_LOG_V(DEBUG, TAG, "uri.query.s %s", uri.query.s);
505 coapMsgType = COAP_MESSAGE_NON;
507 // Decide message type
508 if (qos == OC_CONFIRMABLE) {
509 coapMsgType = COAP_MESSAGE_CON;
510 OC_LOG(FATAL, TAG, "qos == OC_CONFIRMABLE is not supported in OCDoCoAPResource");
513 // Decide method type
514 if (method == OC_REST_GET) {
515 coapMethod = COAP_REQUEST_GET;
517 OC_LOG(FATAL, TAG, "OCDoCoAPResource only supports method == OC_REST_GET");
520 VERIFY_NON_NULL(gCoAPCtx);
521 pdu = GenerateCoAPPdu(coapMsgType, coapMethod,
522 coap_new_message_id(gCoAPCtx), token->tokenLength, token->token,
524 VERIFY_NON_NULL(pdu);
526 TODO ("Post Sprint 1 -- Send Confirmed requests for non-discovery requests");
527 tid = coap_send(gCoAPCtx, (coap_address_t*) &dst, pdu);
529 OC_LOG_V(INFO, TAG, "TID %d", tid);
530 if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID) {
531 OC_LOG(INFO, TAG, "Deleting PDU");
532 coap_delete_pdu(pdu);
533 OC_LOG(INFO, TAG, "Done Deleting PDU");
535 OC_LOG(INFO, TAG, "Keeping PDU, we should handle the retry of this pdu");
538 coap_delete_list(optList);
546 * Stop the CoAP client or server processing
548 * @return 0 - success, else - TBD error
551 OC_LOG(INFO, TAG, PCF("Entering OCStopCoAP"));
552 coap_free_context(gCoAPCtx);
558 * Called in main loop of CoAP client or server. Allows low-level CoAP processing of
559 * send, receive, timeout, discovery, callbacks, etc.
561 * @return 0 - success, else - TBD error
563 int OCProcessCoAP() {
564 OC_LOG(INFO, TAG, PCF("Entering OCProcessCoAP"));
566 coap_read(gCoAPCtx, gCoAPCtx->sockfd);
567 if (-1 != gCoAPCtx->sockfd_wellknown) {
568 coap_read(gCoAPCtx, gCoAPCtx->sockfd_wellknown);
570 coap_dispatch(gCoAPCtx);