ac04a33a04cac1f791533f551e08c1558c91c1d3
[platform/upstream/iotivity.git] / csdk / occoap / src / occoap.c
1 //******************************************************************
2 //
3 // Copyright 2014 Intel Corporation All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
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
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
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.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21
22 //=============================================================================
23 // Includes
24 //=============================================================================
25 #include "occoap.h"
26 #include "occlientcb.h"
27 #include "ocobserve.h"
28 #include <coap.h>
29
30 #ifndef WITH_ARDUINO
31 #include <unistd.h>
32 #endif
33 #include <limits.h>
34 #include <ctype.h>
35
36 //-----------------------------------------------------------------------------
37 // Macros
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;} }
43
44 #define BUF_SIZE (64)
45 #define BUF_SIZE_ENCODE_OPTION (3)
46 #define BUF_SIZE_PORT (2)
47
48 //=============================================================================
49 // Private Variables
50 //=============================================================================
51
52 static uint8_t coapWKIpAddr[] = { 224, 0, 1, 187 };
53 static coap_context_t *gCoAPCtx = NULL;
54
55 //=============================================================================
56 // Helper Functions
57 //=============================================================================
58
59 //generate a coap token
60 OCCoAPToken * OCGenerateCoAPToken()
61 {
62     OCCoAPToken *token = NULL;
63     // Generate token here, it will be deleted when the transaction is deleted
64     token = (OCCoAPToken *) malloc(sizeof(OCCoAPToken));
65     if (token)
66     {
67         token->tokenLength = MAX_TOKEN_LENGTH;
68         OCFillRandomMem((uint8_t*)token->token, token->tokenLength);
69     }
70
71     return token;
72 }
73
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)
77 {
78     // silence warnings
79     (void) ctx;
80
81     OCStackResult result;
82     OCRequest * request = NULL;
83     OCEntityHandlerRequest * entityHandlerRequest = NULL;
84     OCCoAPToken * rcvdToken = NULL;
85
86     unsigned char rcvdUri[MAX_URI_LENGTH] = { 0 };
87     unsigned char rcvdQuery[MAX_QUERY_LENGTH] = { 0 };
88
89     unsigned char bufRes[MAX_RESPONSE_LENGTH] = { 0 };
90
91     coap_list_t *optList = NULL;
92     unsigned char tempBuf[BUF_SIZE_ENCODE_OPTION];
93     coap_pdu_t *pdu;
94     coap_tid_t tid = COAP_INVALID_TID;
95
96     // fill OCRequest structure
97     result = FormOCRequest(rcvdRequest, &request, rcvdUri, rcvdQuery);
98     VERIFY_SUCCESS(result, OC_STACK_OK);
99
100     // fill OCEntityHandlerRequest structure
101     result = FormOCEntityHandlerRequest(rcvdRequest, &entityHandlerRequest,
102             bufRes, rcvdQuery);
103     VERIFY_SUCCESS(result, OC_STACK_OK);
104
105     // fill OCCoAPToken structure
106     result = RetrieveOCCoAPToken(rcvdRequest, &rcvdToken);
107     VERIFY_SUCCESS(result, OC_STACK_OK);
108
109     request->entityHandlerRequest = entityHandlerRequest;
110
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);
118
119     // process the request
120     result = HandleStackRequests(request);
121
122     if (result == OC_STACK_OK)
123     {
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),
131                     OrderOptions);
132     }
133
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);
142     coap_show_pdu(pdu);
143
144     if (pdu->hdr->type != COAP_MESSAGE_NON
145             || (pdu->hdr->code >= 64 && !coap_is_mcast(&rcvdRequest->local)))
146     {
147         tid = coap_send(gCoAPCtx, &rcvdRequest->remote, pdu);
148     }
149
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)
154     {
155         OC_LOG(INFO, TAG, "Deleting PDU");
156         coap_delete_pdu(pdu);
157     }
158
159 exit:
160     coap_delete_list(optList);
161     OCFree(rcvdToken);
162     OCFree(entityHandlerRequest);
163     OCFree(request);
164 }
165
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;
174
175     VERIFY_NON_NULL(ctx);
176     VERIFY_NON_NULL(rcvdResponse);
177
178     // TODO: we should check if we are interested in the token
179     // Now, just accept NON packets
180
181     if (rcvdResponse->pdu->hdr->type == COAP_MESSAGE_NON)
182     {
183         // fill OCResponse structure
184         result = FormOCResponse(rcvdResponse, &response);
185         VERIFY_SUCCESS(result, OC_STACK_OK);
186
187         // fill OCCoAPToken structure
188         result = RetrieveOCCoAPToken(rcvdResponse, &token);
189         VERIFY_SUCCESS(result, OC_STACK_OK);
190
191         // fill OCClientResponse structure
192         result = FormOCClientResponse(rcvdResponse, &clientResponse);
193         VERIFY_SUCCESS(result, OC_STACK_OK);
194
195         // put everything together
196         response->clientResponse = clientResponse;
197
198         cbNode = GetClientCB(token, NULL);
199
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,
204                       token->tokenLength);
205
206         if(cbNode && (cbNode->method == OC_REST_OBSERVE || cbNode->method == OC_REST_OBSERVE_ALL))
207         {
208             if(clientResponse->sequenceNumber != 0)
209             {
210                 if(cbNode->method == OC_REST_OBSERVE && (clientResponse->sequenceNumber <= cbNode->sequenceNumber))
211                 {
212                     OC_LOG_V(DEBUG, TAG, "Observe notification came out of order. \
213                              Ignoring Incoming:%d  Against Current:%d.",
214                              clientResponse->sequenceNumber, cbNode->sequenceNumber);
215                     return;
216                 }
217                 else
218                 {
219                     cbNode->sequenceNumber = clientResponse->sequenceNumber;
220                 }
221             }
222         }
223         else if(!cbNode && clientResponse  && clientResponse->sequenceNumber != 0) // Ensure that this is an observe notification.
224         {
225
226             coap_pdu_t *pdu;
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);
230
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);
235             coap_tid_t tid;
236             tid = coap_send(gCoAPCtx, (coap_address_t*) &rcvdResponse->remote, pdu);
237
238             OC_LOG_V(INFO, TAG, "TID %d", tid);
239             if (tid != COAP_INVALID_TID)
240             {
241                 OC_LOG(INFO, TAG, "Deleting PDU");
242                 coap_delete_pdu(pdu);
243             }
244             else
245             {
246                 OC_LOG(INFO, TAG, "Keeping PDU, we should handle the retry of this pdu");
247             }
248             goto exit;
249         }
250         response->cbNode = cbNode;
251         response->clientResponse->result = CoAPToOCResponseCode(rcvdResponse->pdu->hdr->code);
252         HandleStackResponses(response);
253     }
254     else
255     {
256         OC_LOG(DEBUG, TAG, "Do not accept other than NON in HandleCoAPResponses");
257     }
258
259 exit:
260     OCFree(response);
261     OCFree(token);
262     OCFree(clientResponse);
263 }
264
265 //=============================================================================
266 // Functions
267 //=============================================================================
268
269 /**
270  * Initialize the CoAP client or server with its IPv4 address and CoAP port
271  *
272  * @param ipAddr
273  *     IP Address of host device
274  * @param port
275  *     Port of host device
276  * @param mode
277  *     Host device is client, server, or client-server
278  *
279  * @return
280  *   0   - success
281  *   TBD - TBD error
282  */
283 int OCInitCoAP(const char *address, uint16_t port, OCMode mode) {
284
285     int ret = OC_COAP_ERR;
286
287     TODO ("Below should go away and be replaced by OC_LOG");
288     coap_log_t log_level = LOG_DEBUG + 1;
289     OCDevAddr devAddr;
290     OCDevAddr mcastAddr;
291     uint8_t ipAddr[4] = { 0 };
292
293     OC_LOG(INFO, TAG, PCF("Entering OCInitCoAP"));
294
295     coap_set_log_level(log_level);
296
297     if (address)
298     {
299         if (!ParseIPv4Address((unsigned char *) address, ipAddr))
300         {
301             return OC_COAP_ERR;
302         }
303         OC_LOG_V(INFO, TAG, "Parsed IP Address %d.%d.%d.%d",ipAddr[0],ipAddr[1],ipAddr[2],ipAddr[3]);
304     }
305
306     OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], port,
307             &devAddr);
308
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);
314         VERIFY_SUCCESS(
315                 coap_join_wellknown_group(gCoAPCtx,
316                         (coap_address_t* )&mcastAddr), 0);
317     }
318
319     coap_register_request_handler(gCoAPCtx, HandleCoAPRequests);
320     coap_register_response_handler(gCoAPCtx, HandleCoAPResponses);
321
322     ret = OC_COAP_OK;
323
324 exit:
325     if (ret != OC_COAP_OK)
326     {
327         OCStopCoAP(gCoAPCtx);
328     }
329     return ret;
330 }
331
332 /**
333  * Discover OC resources
334  *
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
341  * @return
342  *   0   - success
343  *   TBD - TBD error
344  */
345 int OCDoCoAPResource(OCMethod method, OCQualityOfService qos, OCCoAPToken * token,
346                      const char *Uri, const char *payload)
347 {
348
349     int ret = OC_COAP_ERR;
350     coap_pdu_t *pdu = NULL;
351     coap_uri_t uri;
352     OCDevAddr dst;
353     uint8_t ipAddr[4] = { 0 };
354     coap_list_t *optList = NULL;
355     unsigned char portBuf[BUF_SIZE_PORT];
356     size_t buflen;
357     unsigned char _buf[BUF_SIZE];
358     unsigned char *buf = _buf;
359     int res;
360     uint8_t coapMsgType;
361     uint8_t coapMethod;
362
363     OC_LOG(INFO, TAG, PCF("Entering OCDoCoAPResource"));
364
365     if (Uri) {
366         OC_LOG_V(INFO, TAG, "URI = %s", Uri);
367         VERIFY_SUCCESS(coap_split_uri((unsigned char * )Uri, strlen(Uri), &uri), 0);
368
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,
372                     &dst);
373         } else {
374             goto exit;
375         }
376
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),
382                     OrderOptions);
383         }
384
385         if (uri.path.length) {
386             buflen = BUF_SIZE;
387             res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
388
389             while (res--) {
390                 coap_insert(&optList,
391                         CreateNewOptionNode(COAP_OPTION_URI_PATH,
392                                 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
393                         OrderOptions);
394
395                 buf += COAP_OPT_SIZE(buf);
396             }
397         }
398
399         if (uri.query.length) {
400             buflen = BUF_SIZE;
401             buf = _buf;
402             res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
403
404             while (res--) {
405                 coap_insert(&optList,
406                         CreateNewOptionNode(COAP_OPTION_URI_QUERY,
407                                 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
408                         OrderOptions);
409
410                 buf += COAP_OPT_SIZE(buf);
411             }
412         }
413
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);
418     }
419     coapMsgType = COAP_MESSAGE_NON;
420
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");
425     }
426     // Decide method type
427     switch (method) {
428         case OC_REST_GET:
429             coapMethod = COAP_REQUEST_GET;
430             break;
431         case OC_REST_PUT:
432             coapMethod = COAP_REQUEST_PUT;
433             break;
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);
439
440             break;
441         default:
442             coapMethod = 0;
443             OC_LOG(FATAL, TAG, "OCDoCoAPResource only supports GET, PUT, & OBSERVE methods");
444             break;
445     }
446
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);
452
453     coap_send(gCoAPCtx, (coap_address_t*) &dst, pdu);
454
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)
458     {
459         OC_LOG(INFO, TAG, "Deleting PDU");
460         coap_delete_pdu(pdu);
461         pdu = NULL;
462     }
463
464     ret = OC_COAP_OK;
465
466 exit:
467     coap_delete_list(optList);
468     if (ret != OC_COAP_OK)
469     {
470         coap_delete_pdu(pdu);
471     }
472     return ret;
473 }
474
475 int OCCoAPSendMessage (OCDevAddr *dstAddr, OCStackResult msgCode,
476                        OCQualityOfService qos, OCCoAPToken * token,
477                        const char *payload, uint32_t seqNum)
478 {
479     coap_list_t *optList = NULL;
480     coap_pdu_t *pdu;
481     unsigned char tempBuf[BUF_SIZE_ENCODE_OPTION];
482     uint8_t coapMsgType = COAP_MESSAGE_NON;
483     coap_tid_t tid = COAP_INVALID_TID;
484
485     OC_LOG(INFO, TAG, PCF("Entering OCCoAPSendMessage"));
486
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);
495
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);
500     coap_show_pdu(pdu);
501
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)
505     {
506         OC_LOG(INFO, TAG, "Deleting PDU");
507         coap_delete_pdu(pdu);
508         pdu = NULL;
509     }
510     return OC_COAP_OK;
511
512 exit:
513     coap_delete_list(optList);
514     return OC_COAP_ERR;
515 }
516
517 /**
518  * Stop the CoAP client or server processing
519  *
520  * @return 0 - success, else - TBD error
521  */
522 int OCStopCoAP() {
523     OC_LOG(INFO, TAG, PCF("Entering OCStopCoAP"));
524     coap_free_context(gCoAPCtx);
525     gCoAPCtx = NULL;
526     return 0;
527 }
528
529 /**
530  * Called in main loop of CoAP client or server.  Allows low-level CoAP processing of
531  * send, receive, timeout, discovery, callbacks, etc.
532  *
533  * @return 0 - success, else - TBD error
534  */
535 int OCProcessCoAP() {
536     OC_LOG(INFO, TAG, PCF("Entering OCProcessCoAP"));
537     int read = 0;
538     read = coap_read(gCoAPCtx, gCoAPCtx->sockfd);
539     if(read > 0)
540     {
541         OC_LOG(INFO, TAG, "This is a Unicast<============");
542     }
543     if (-1 != gCoAPCtx->sockfd_wellknown) {
544         read = coap_read(gCoAPCtx, gCoAPCtx->sockfd_wellknown);
545         if(read > 0)
546         {
547             OC_LOG(INFO, TAG, "This is a Multicast<===========");
548         }
549     }
550     coap_dispatch(gCoAPCtx);
551     return 0;
552 }
553