Initial merge-commit of the OIC code. Should successfully do discovery for single...
[platform/upstream/iotivity.git] / csdk / occoap / src / occoap.c
1 //******************************************************************
2 //
3 // Copyright 2014 Intel Corporation All Rights Reserved.
4 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
5
6
7 //=============================================================================
8 // Includes
9 //=============================================================================
10 #include "occoap.h"
11 #include <coap.h>
12 #include <unistd.h>
13 #include <limits.h>
14 #include <ctype.h>
15
16 //-----------------------------------------------------------------------------
17 // Macros
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;} }
23
24 #define BUF_SIZE (64)
25 #define BUF_SIZE_ENCODE_OPTION (3)
26 #define BUF_SIZE_PORT (2)
27
28 //=============================================================================
29 // Private Variables
30 //=============================================================================
31
32 static uint8_t coapWKIpAddr[] = { 224, 0, 1, 187 };
33 static coap_context_t *gCoAPCtx = NULL;
34
35 //=============================================================================
36 // Helper Functions
37 //=============================================================================
38
39 //generate a coap message
40 static coap_pdu_t *
41 GenerateCoAPPdu(uint8_t msgType, uint8_t code, unsigned short id,
42         size_t tokenLength, uint8_t * token, coap_list_t *options) {
43     coap_pdu_t *pdu;
44     coap_list_t *opt;
45
46     pdu = coap_pdu_init(msgType, code, id, COAP_MAX_PDU_SIZE);
47     VERIFY_NON_NULL(pdu);
48
49     pdu->hdr->token_length = tokenLength;
50     if (!coap_add_token(pdu, tokenLength, token)) {
51         OC_LOG(FATAL, TAG, "coap_add_token failed");
52     }
53
54     // display the pdu for debugging purposes
55     coap_show_pdu(pdu);
56
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));
61     }
62     return pdu;
63
64     exit: return NULL;
65 }
66
67 //a function to help in ordering coap options
68 static int OrderOptions(void *a, void *b) {
69     if (!a || !b) {
70         return a < b ? -1 : 1;
71     }
72
73     if (COAP_OPTION_KEY(*(coap_option *)a)
74             < COAP_OPTION_KEY(*(coap_option *)b) ) {
75         return -1;
76     }
77
78     return COAP_OPTION_KEY(*(coap_option *)a)
79             == COAP_OPTION_KEY(*(coap_option *)b) ;
80 }
81
82 //a function to create a coap option
83 static coap_list_t *
84 CreateNewOptionNode(unsigned short key, unsigned int length,
85         unsigned char *data) {
86     coap_option *option;
87     coap_list_t *node;
88
89     option = coap_malloc(sizeof(coap_option) + length);
90     if (!option) {
91         goto exit;
92     }
93
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);
98
99     /* we can pass NULL here as delete function since option is released automatically  */
100     node = coap_new_listnode(option, NULL);
101
102     if (node) {
103         return node;
104     }
105
106     exit: OC_LOG(ERROR,TAG,"new_option_node: malloc: was not created");
107     coap_free(option);
108     return NULL;
109 }
110
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) {
114
115     // silence warnings
116     (void) ctx;
117     // fill OCRequest structure
118     OCRequest * request;
119     OCEntityHandlerRequest * entityHandlerRequest;
120     unsigned char bufReq[MAX_REQUEST_LENGTH] = { 0 };
121     unsigned char bufRes[MAX_RESPONSE_LENGTH] = { 0 };
122     size_t bufLen = 0;
123     OCToken * rcvdToken;
124     coap_opt_iterator_t opt_iter;
125     coap_opt_filter_t filter;
126     coap_opt_t *option;
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];
132     coap_pdu_t *pdu;
133     coap_tid_t tid = COAP_INVALID_TID;
134
135     //TODO:will be deleted at the end
136     entityHandlerRequest = (OCEntityHandlerRequest *) malloc(
137             sizeof(OCEntityHandlerRequest));
138
139     entityHandlerRequest->method = OC_REST_NOMETHOD;
140     if (rcvdRequest->pdu->hdr->code == COAP_REQUEST_GET) {
141         entityHandlerRequest->method = OC_REST_GET;
142     } else {
143         OC_LOG(DEBUG, TAG, "!!!!!!!!!!!!!!!!!!!!!!!method not supported in HandleCoAPRequests");
144     }
145
146     coap_get_data(rcvdRequest->pdu, &bufLen, (unsigned char **)&bufReq);
147     bufReq[bufLen] = 0;
148     OC_LOG_V(INFO, TAG, "received payload %d bytes : %s", bufLen, bufReq);
149     entityHandlerRequest->reqJSONPayload = bufReq;
150
151     entityHandlerRequest->resJSONPayload = bufRes;
152     entityHandlerRequest->resJSONPayloadLen = MAX_RESPONSE_LENGTH;
153
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);
159
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;
164
165     //TODO:will be deleted at the end
166     request = (OCRequest *) malloc(sizeof(OCRequest));
167     request->entityHandlerRequest = entityHandlerRequest;
168
169     request->qos = OC_NON_CONFIRMABLE;
170     if (rcvdRequest->pdu->hdr->type == COAP_MESSAGE_CON) {
171         request->qos = OC_CONFIRMABLE;
172     }
173
174     //getting the uri
175     request->resourceUrl = NULL;
176     bufLen = 0;
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);
185     }
186     rcvdUri[bufLen] = '\0';
187     OC_LOG_V(INFO, TAG, " Receveid uri %s", rcvdUri);
188     request->resourceUrl = rcvdUri;
189
190     request->query = NULL;
191     bufLen = 0;
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++] = '&';
200     }
201     // delete last '&'
202     rcvdQuery[bufLen - 1] = '\0';
203     OC_LOG_V(INFO, TAG, " Receveid query %s", rcvdQuery);
204     request->query = rcvdQuery;
205
206     result = OCStackHandleReceiveRequest(request);
207
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),
215                 OrderOptions);
216         coap_insert(&optList,
217                 CreateNewOptionNode(COAP_OPTION_MAXAGE,
218                         coap_encode_var_bytes(tempBuf, 0x2ffff), tempBuf),
219                 OrderOptions);
220
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);
227         coap_add_data(pdu,
228                 strlen((const char *)request->entityHandlerRequest->resJSONPayload),
229                 (unsigned char*) request->entityHandlerRequest->resJSONPayload);
230
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);
238
239         coap_delete_list(optList);
240
241         OC_LOG(INFO, TAG, "Done Cleaning up");
242     } else {
243         OC_LOG(DEBUG, TAG, "We not supporting other returns of OCServerRequestResult in HandleCoAPRequests/"
244                 " ......................yet");
245     }
246     free(rcvdToken);
247     free(entityHandlerRequest);
248     free(request);
249 }
250
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;
255     OCToken * token;
256     OCClientResponse * clientResponse;
257     unsigned char bufRes[MAX_RESPONSE_LENGTH] = { 0 };
258     unsigned char * pRes = NULL;
259     size_t bufLen = 0;
260
261     VERIFY_NON_NULL(ctx);
262     VERIFY_NON_NULL(rcvdResponse);
263
264     // TODO: we should check if we are interested in the token
265     // Now, just accept NON packets
266
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));
272
273         token->tokenLength = rcvdResponse->pdu->hdr->token_length;
274         memcpy(token->token, rcvdResponse->pdu->hdr->token, token->tokenLength);
275
276         clientResponse->addr = (OCDevAddr *)&(rcvdResponse->remote);
277
278         coap_get_data(rcvdResponse->pdu, &bufLen, &pRes);
279         memcpy(bufRes, pRes, bufLen);
280         bufRes[bufLen] = 0;
281         clientResponse->resJSONPayload = bufRes;
282         OC_LOG_V(INFO, TAG, "Received a response HandleCoAPResponses in occoap: %s ", bufRes);
283
284         clientResponse->result = OC_STACK_ERROR;
285
286         response->token = token;
287         response->clientResponse = clientResponse;
288
289         if (rcvdResponse->pdu->hdr->code == COAP_RESPONSE_CODE(205)) {
290             response->clientResponse->result = OC_STACK_OK;
291             OCStackHandleReceiveResponse(response);
292         } else {
293             OC_LOG(DEBUG, TAG, "No other response codes are supported in HandleCoAPResponsesooooooooooooooooooooooo");
294         }
295
296         free(response);
297         free(token);
298         free(clientResponse);
299     } else {
300         OC_LOG(DEBUG, TAG, "Do not accept other than NON in HandleCoAPResponsesooooooooooooooooooooooo");
301     }
302 #if 0
303     size_t len;
304     unsigned char *dataBuf;
305     const void *addrPtr = NULL;
306     char buf[BUF_SIZE];
307     uint8_t remoteIpAddr[4];
308     uint16_t remotePortNu;
309
310     VERIFY_NON_NULL(received);
311     VERIFY_NON_NULL(ctx);
312     VERIFY_NON_NULL(remote);
313
314     /* check if this is a response to our original request */
315     if (!CheckToken(received)) {
316         return;
317     }
318
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);
323         break;
324         case COAP_MESSAGE_RST:
325         return;
326         default:
327         ;
328     }
329
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);
333     }
334
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);
342      }*/
343
344 #endif
345     exit: return;
346 }
347
348 static void parseIPv4Address(const unsigned char * ipAddrStr, uint8_t * ipAddr) {
349     size_t index = 0;
350
351     while (*ipAddrStr) {
352         if (isdigit((unsigned char) *ipAddrStr)) {
353             ipAddr[index] *= 10;
354             ipAddr[index] += *ipAddrStr - '0';
355         } else if ((unsigned char) *ipAddrStr == '.') {
356             index++;
357         } else {
358             return;
359         }
360         ipAddrStr++;
361     }
362 }
363 //=============================================================================
364 // Functions
365 //=============================================================================
366
367 /**
368  * Initialize the CoAP client or server with its IPv4 address and CoAP port
369  *
370  * @param ipAddr
371  *     IP Address of host device
372  * @param port
373  *     Port of host device
374  * @param mode
375  *     Host device is client, server, or client-server
376  *
377  * @return
378  *   0   - success
379  *   TBD - TBD error
380  */
381 int OCInitCoAP(const char *address, uint16_t port, OCMode mode) {
382
383     int ret = OC_COAP_ERR;
384
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 };
389
390     OC_LOG(INFO, TAG, PCF("Entering OCInitCoAP"));
391
392     coap_set_log_level(log_level);
393
394     if (address) {
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]);
397     }
398
399     OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], port,
400             &devAddr);
401
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);
407         VERIFY_SUCCESS(
408                 coap_join_wellknown_group(gCoAPCtx,
409                         (coap_address_t* )&mcastAddr), 0);
410     }
411
412     coap_register_request_handler(gCoAPCtx, HandleCoAPRequests);
413     coap_register_response_handler(gCoAPCtx, HandleCoAPResponses);
414
415     ret = OC_COAP_OK;
416
417     exit: return ret;
418 }
419
420 /**
421  * Discover OC resources
422  *
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
427  * @return
428  *   0   - success
429  *   TBD - TBD error
430  */
431 int OCDoCoAPResource(OCMethod method, OCQualityOfService qos, OCToken * token,
432         const char *Uri) {
433
434     int ret = OC_COAP_ERR;
435     coap_pdu_t *pdu = NULL;
436     coap_uri_t uri;
437     coap_tid_t tid = COAP_INVALID_TID;
438     OCDevAddr dst;
439     uint8_t ipAddr[4] = { 0 };
440     coap_list_t *optList = NULL;
441     unsigned char portBuf[BUF_SIZE_PORT];
442     size_t buflen;
443     unsigned char _buf[BUF_SIZE];
444     unsigned char *buf = _buf;
445     int res;
446     uint8_t coapMsgType;
447     uint8_t coapMethod;
448
449     OC_LOG(INFO, TAG, PCF("Entering OCDoCoAPResource"));
450
451     if (Uri) {
452         OC_LOG_V(INFO, TAG, "URI = %s", Uri);
453     }
454
455     VERIFY_SUCCESS(coap_split_uri((unsigned char * )Uri, strlen(Uri), &uri), 0);
456
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,
461                 &dst);
462     } else {
463         goto exit;
464     }
465
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),
471                 OrderOptions);
472     }
473
474     if (uri.path.length) {
475         buflen = BUF_SIZE;
476         res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
477
478         while (res--) {
479             coap_insert(&optList,
480                     CreateNewOptionNode(COAP_OPTION_URI_PATH,
481                             COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
482                     OrderOptions);
483
484             buf += COAP_OPT_SIZE(buf);
485         }
486     }
487
488     if (uri.query.length) {
489         buflen = BUF_SIZE;
490         buf = _buf;
491         res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
492
493         while (res--) {
494             coap_insert(&optList,
495                     CreateNewOptionNode(COAP_OPTION_URI_QUERY,
496                             COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
497                     OrderOptions);
498
499             buf += COAP_OPT_SIZE(buf);
500         }
501     }
502
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);
504
505     coapMsgType = COAP_MESSAGE_NON;
506
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");
511     }
512
513     // Decide method type
514     if (method == OC_REST_GET) {
515         coapMethod = COAP_REQUEST_GET;
516     } else {
517         OC_LOG(FATAL, TAG, "OCDoCoAPResource only supports method == OC_REST_GET");
518     }
519
520     VERIFY_NON_NULL(gCoAPCtx);
521     pdu = GenerateCoAPPdu(coapMsgType, coapMethod,
522             coap_new_message_id(gCoAPCtx), token->tokenLength, token->token,
523             optList);
524     VERIFY_NON_NULL(pdu);
525
526     TODO ("Post Sprint 1 -- Send Confirmed requests for non-discovery requests");
527     tid = coap_send(gCoAPCtx, (coap_address_t*) &dst, pdu);
528
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");
534     } else {
535         OC_LOG(INFO, TAG, "Keeping PDU, we should handle the retry of this pdu");
536     }
537
538     coap_delete_list(optList);
539
540     ret = OC_COAP_OK;
541
542     exit: return ret;
543 }
544
545 /**
546  * Stop the CoAP client or server processing
547  *
548  * @return 0 - success, else - TBD error
549  */
550 int OCStopCoAP() {
551     OC_LOG(INFO, TAG, PCF("Entering OCStopCoAP"));
552     coap_free_context(gCoAPCtx);
553     gCoAPCtx = NULL;
554     return 0;
555 }
556
557 /**
558  * Called in main loop of CoAP client or server.  Allows low-level CoAP processing of
559  * send, receive, timeout, discovery, callbacks, etc.
560  *
561  * @return 0 - success, else - TBD error
562  */
563 int OCProcessCoAP() {
564     OC_LOG(INFO, TAG, PCF("Entering OCProcessCoAP"));
565
566     coap_read(gCoAPCtx, gCoAPCtx->sockfd);
567     if (-1 != gCoAPCtx->sockfd_wellknown) {
568         coap_read(gCoAPCtx, gCoAPCtx->sockfd_wellknown);
569     }
570     coap_dispatch(gCoAPCtx);
571     return 0;
572 }
573