Server-side changes to support observe functionality.
[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 <coap.h>
28
29 #ifndef WITH_ARDUINO
30 #include <unistd.h>
31 #endif
32 #include <limits.h>
33 #include <ctype.h>
34
35 //-----------------------------------------------------------------------------
36 // Macros
37 //-----------------------------------------------------------------------------
38 #define TAG    PCF("OCCoAP")
39 #define VERIFY_SUCCESS(op, successCode) { if (op != successCode) \
40             {OC_LOG(FATAL, TAG, #op " failed !!"); goto exit;} }
41 #define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG(FATAL, TAG, #arg " is NULL"); goto exit;} }
42
43 #define BUF_SIZE (64)
44 #define BUF_SIZE_ENCODE_OPTION (3)
45 #define BUF_SIZE_PORT (2)
46
47 //=============================================================================
48 // Private Variables
49 //=============================================================================
50
51 static uint8_t coapWKIpAddr[] = { 224, 0, 1, 187 };
52 static coap_context_t *gCoAPCtx = NULL;
53
54 //=============================================================================
55 // Helper Functions
56 //=============================================================================
57
58 //generate a coap token
59 OCCoAPToken * OCGenerateCoAPToken() {
60     OCCoAPToken *token;
61     // Generate token here, it will be deleted when the transaction is deleted
62     token = (OCCoAPToken *) malloc(sizeof(OCCoAPToken));
63     token->tokenLength = MAX_TOKEN_LENGTH;
64     OCFillRandomMem((uint8_t*)token->token, token->tokenLength);
65
66     //OC_LOG_V(INFO,TAG,"Toke n generated %d bytes..........%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
67     //          token->tokenLength,token->token[0],token->token[1],token->token[2],token->token[3],
68     //          token->token[4],token->token[5],token->token[6],token->token[7]);
69     return token;
70 }
71 //This function is called back by libcoap when a request is received
72 static void HandleCoAPRequests(struct coap_context_t *ctx,
73         const coap_queue_t * rcvdRequest)
74 {
75     // silence warnings
76     (void) ctx;
77
78     OCStackResult result;
79     OCRequest * request = NULL;
80     OCEntityHandlerRequest * entityHandlerRequest = NULL;
81     OCCoAPToken * rcvdToken = NULL;
82
83     unsigned char rcvdUri[MAX_URI_LENGTH] = { 0 };
84     unsigned char rcvdQuery[MAX_QUERY_LENGTH] = { 0 };
85
86     unsigned char bufRes[MAX_RESPONSE_LENGTH] = { 0 };
87
88     coap_list_t *optList = NULL;
89     unsigned char tempBuf[BUF_SIZE_ENCODE_OPTION];
90     coap_pdu_t *pdu;
91     coap_tid_t tid = COAP_INVALID_TID;
92
93     // fill OCRequest structure
94     result = FormOCRequest(rcvdRequest, &request, rcvdUri, rcvdQuery);
95     VERIFY_SUCCESS(result, OC_STACK_OK);
96
97     // fill OCEntityHandlerRequest structure
98     result = FormOCEntityHandlerRequest(rcvdRequest, &entityHandlerRequest,
99             bufRes, rcvdQuery);
100     VERIFY_SUCCESS(result, OC_STACK_OK);
101
102     // fill OCCoAPToken structure
103     result = RetrieveOCCoAPToken(rcvdRequest, &rcvdToken);
104     VERIFY_SUCCESS(result, OC_STACK_OK);
105
106     request->entityHandlerRequest = entityHandlerRequest;
107
108     OC_LOG_V(INFO, TAG, " Receveid uri:     %s", request->resourceUrl);
109     OC_LOG_V(INFO, TAG, " Receveid query:   %s", entityHandlerRequest->query);
110     OC_LOG_V(INFO, TAG, " Receveid payload: %s",
111             request->entityHandlerRequest->reqJSONPayload);
112
113     if(GetClientCB(rcvdToken, 0)) { //True if the token was found in the list of clientCBs
114         OC_LOG_V(INFO, TAG, " Token received %d bytes",
115                 rcvdToken->tokenLength);
116         OC_LOG_BUFFER(INFO, TAG, rcvdToken->token, rcvdToken->tokenLength);
117     }
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     if(rcvdToken) {
162         OCFree(rcvdToken);
163         rcvdToken = NULL;
164     }
165     OCFree(entityHandlerRequest);
166     OCFree(request);
167 }
168
169 //This function is called back by libcoap when a response is received
170 static void HandleCoAPResponses(struct coap_context_t *ctx,
171         const coap_queue_t * rcvdResponse) {
172     OCResponse * response = NULL;
173     OCCoAPToken * _token = NULL;
174     OCClientResponse * clientResponse = NULL;
175     OCStackResult result;
176
177     VERIFY_NON_NULL(ctx);
178     VERIFY_NON_NULL(rcvdResponse);
179
180     // TODO: we should check if we are interested in the token
181     // Now, just accept NON packets
182
183     if (rcvdResponse->pdu->hdr->type == COAP_MESSAGE_NON) {
184         // fill OCResponse structure
185         result = FormOCResponse(rcvdResponse, &response);
186         VERIFY_SUCCESS(result, OC_STACK_OK);
187
188         // fill OCCoAPToken structure
189         result = RetrieveOCCoAPToken(rcvdResponse, &_token);
190         VERIFY_SUCCESS(result, OC_STACK_OK);
191
192         // fill OCClientResponse structure
193         result = FormOCClientResponse(rcvdResponse, &clientResponse);
194         VERIFY_SUCCESS(result, OC_STACK_OK);
195
196         // put everything together
197         ClientCB * clientCB = GetClientCB(_token, NULL);
198         OCDoHandle *handle = NULL;
199         if(clientCB) {
200             handle = clientCB->handle;
201             response->handle = handle;
202         }
203         response->clientResponse = clientResponse;
204
205         OC_LOG_V(INFO, TAG, " Received a response HandleCoAPResponses in occoap: %s",
206                  response->clientResponse->resJSONPayload);
207         OC_LOG_V(INFO, TAG,"Token received %d bytes", _token->tokenLength);
208         OC_LOG_BUFFER(INFO, TAG, _token->token,
209                       _token->tokenLength);
210
211         response->clientResponse->result = OC_STACK_OK;
212         HandleStackResponses(response);
213         /*if (rcvdResponse->pdu->hdr->code == COAP_RESPONSE_CODE(205))
214         {
215             response->clientResponse->result = OC_STACK_OK;
216             HandleStackResponses(response);
217         }
218         else
219         {
220             OC_LOG(DEBUG, TAG,
221                    "No other response codes are supported in HandleCoAPResponses");
222         }*/
223     }
224     else
225     {
226         OC_LOG(DEBUG, TAG, "Do not accept other than NON in HandleCoAPResponses");
227     }
228
229 exit:
230     OCFree(response);
231     if(_token) {
232         OCFree(_token);
233         _token = NULL;
234     }
235     OCFree(clientResponse);
236 }
237
238 //=============================================================================
239 // Functions
240 //=============================================================================
241
242 /**
243  * Initialize the CoAP client or server with its IPv4 address and CoAP port
244  *
245  * @param ipAddr
246  *     IP Address of host device
247  * @param port
248  *     Port of host device
249  * @param mode
250  *     Host device is client, server, or client-server
251  *
252  * @return
253  *   0   - success
254  *   TBD - TBD error
255  */
256 int OCInitCoAP(const char *address, uint16_t port, OCMode mode) {
257
258     int ret = OC_COAP_ERR;
259
260     TODO ("Below should go away and be replaced by OC_LOG");
261     coap_log_t log_level = LOG_DEBUG + 1;
262     OCDevAddr devAddr;
263     OCDevAddr mcastAddr;
264     uint8_t ipAddr[4] = { 0 };
265
266     OC_LOG(INFO, TAG, PCF("Entering OCInitCoAP"));
267
268     coap_set_log_level(log_level);
269
270     if (address)
271     {
272         if (!ParseIPv4Address((unsigned char *) address, ipAddr))
273         {
274             return OC_COAP_ERR;
275         }
276         OC_LOG_V(INFO, TAG, "Parsed IP Address %d.%d.%d.%d",ipAddr[0],ipAddr[1],ipAddr[2],ipAddr[3]);
277     }
278
279     OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], port,
280             &devAddr);
281
282     gCoAPCtx = coap_new_context((coap_address_t*) &devAddr);
283     VERIFY_NON_NULL(gCoAPCtx);
284     if (mode != OC_CLIENT) {
285         OCBuildIPv4Address(coapWKIpAddr[0], coapWKIpAddr[1], coapWKIpAddr[2],
286                 coapWKIpAddr[3], COAP_DEFAULT_PORT, &mcastAddr);
287         VERIFY_SUCCESS(
288                 coap_join_wellknown_group(gCoAPCtx,
289                         (coap_address_t* )&mcastAddr), 0);
290     }
291
292     coap_register_request_handler(gCoAPCtx, HandleCoAPRequests);
293     coap_register_response_handler(gCoAPCtx, HandleCoAPResponses);
294
295     ret = OC_COAP_OK;
296
297 exit:
298     return ret;
299 }
300
301 /**
302  * Discover OC resources
303  *
304  * @param method          - method to perform on the resource
305  * @param qos             - Quality of Service the request will be sent on
306  * @param token           - token which will added to the request
307  * @param Uri             - URI of the resource to interact with
308  * @param payload         - the request payload to be added to the request before sending
309  *                          by the stack when discovery or resource interaction is complete
310  * @return
311  *   0   - success
312  *   TBD - TBD error
313  */
314 int OCDoCoAPResource(OCMethod method, OCQualityOfService qos, OCCoAPToken * token,
315                      const char *Uri, const char *payload)
316 {
317
318     int ret = OC_COAP_ERR;
319     coap_pdu_t *pdu = NULL;
320     coap_uri_t uri;
321     coap_tid_t tid = COAP_INVALID_TID;
322     OCDevAddr dst;
323     uint8_t ipAddr[4] = { 0 };
324     coap_list_t *optList = NULL;
325     unsigned char portBuf[BUF_SIZE_PORT];
326     size_t buflen;
327     unsigned char _buf[BUF_SIZE];
328     unsigned char *buf = _buf;
329     int res;
330     uint8_t coapMsgType;
331     uint8_t coapMethod;
332     // Vijay: TODO Observation registration is hardcoded here - change
333     unsigned char obs[] = "0";
334
335     OC_LOG(INFO, TAG, PCF("Entering OCDoCoAPResource"));
336
337     if (Uri) {
338         OC_LOG_V(INFO, TAG, "URI = %s", Uri);
339     }
340
341     VERIFY_SUCCESS(coap_split_uri((unsigned char * )Uri, strlen(Uri), &uri), 0);
342
343     // Generate the destination address
344     if (uri.host.length && ParseIPv4Address(uri.host.s, ipAddr)) {
345         OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], uri.port,
346                 &dst);
347     } else {
348         goto exit;
349     }
350
351     //create appropriate coap options
352     if (uri.port != COAP_DEFAULT_PORT) {
353         coap_insert(&optList,
354                 CreateNewOptionNode(COAP_OPTION_URI_PORT,
355                         coap_encode_var_bytes(portBuf, uri.port), portBuf),
356                 OrderOptions);
357     }
358
359     if (uri.path.length) {
360         buflen = BUF_SIZE;
361         res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
362
363         while (res--) {
364             coap_insert(&optList,
365                     CreateNewOptionNode(COAP_OPTION_URI_PATH,
366                             COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
367                     OrderOptions);
368
369             buf += COAP_OPT_SIZE(buf);
370         }
371     }
372
373     if (uri.query.length) {
374         buflen = BUF_SIZE;
375         buf = _buf;
376         res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
377
378         while (res--) {
379             coap_insert(&optList,
380                     CreateNewOptionNode(COAP_OPTION_URI_QUERY,
381                             COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
382                     OrderOptions);
383
384             buf += COAP_OPT_SIZE(buf);
385         }
386     }
387
388     OC_LOG_V(DEBUG, TAG, "uri.host.s %s", uri.host.s);
389     OC_LOG_V(DEBUG, TAG, "uri.path.s %s", uri.path.s);
390     OC_LOG_V(DEBUG, TAG, "uri.port %d", uri.port);
391     OC_LOG_V(DEBUG, TAG, "uri.query.s %s", uri.query.s);
392
393     coapMsgType = COAP_MESSAGE_NON;
394
395     // Decide message type
396     if (qos == OC_CONFIRMABLE) {
397         coapMsgType = COAP_MESSAGE_CON;
398         OC_LOG(FATAL, TAG, "qos == OC_CONFIRMABLE is not supported in OCDoCoAPResource");
399     }
400
401     // Decide method type
402     switch (method) {
403         case OC_REST_GET:
404             coapMethod = COAP_REQUEST_GET;
405             break;
406         case OC_REST_PUT:
407             coapMethod = COAP_REQUEST_PUT;
408             break;
409         case OC_REST_OBSERVE_ALL:
410         case OC_REST_OBSERVE:
411             coapMethod = COAP_REQUEST_GET;
412 #if 0
413             // Joey's add for observation registration: not working.
414             // Vijay's change below
415             buflen = BUF_SIZE;
416             buf = _buf;
417             res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
418             //Set observe option flag.
419             while (res--) {
420                 coap_insert(&optList,
421                         CreateNewOptionNode(COAP_OPTION_SUBSCRIPTION,
422                                 COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf)),
423                         OrderOptions);
424
425                 buf += COAP_OPT_SIZE(buf);
426             }
427 #endif
428             coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_OBSERVE,
429                         strlen((const char *)obs), (obs)), OrderOptions);
430
431             break;
432         default:
433             coapMethod = 0;
434             OC_LOG(FATAL, TAG, "OCDoCoAPResource only supports GET, PUT, & OBSERVE methods");
435             break;
436     }
437
438     VERIFY_NON_NULL(gCoAPCtx);
439     pdu = GenerateCoAPPdu(coapMsgType, coapMethod,
440             coap_new_message_id(gCoAPCtx), token->tokenLength, token->token,
441             (unsigned char*) payload, optList);
442     VERIFY_NON_NULL(pdu);
443
444     TODO ("Post Sprint 1 -- Send Confirmed requests for non-discovery requests");
445     tid = coap_send(gCoAPCtx, (coap_address_t*) &dst, pdu);
446
447     OC_LOG_V(INFO, TAG, "TID %d", tid);
448     if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
449     {
450         OC_LOG(INFO, TAG, "Deleting PDU");
451         coap_delete_pdu(pdu);
452         pdu = NULL;
453     }
454     else
455     {
456         OC_LOG(INFO, TAG, "Keeping PDU, we should handle the retry of this pdu");
457     }
458
459     ret = OC_COAP_OK;
460
461 exit:
462     coap_delete_list(optList);
463     if (ret != OC_COAP_OK)
464     {
465         coap_delete_pdu(pdu);
466     }
467     return ret;
468 }
469
470 int OCCoAPSendMessage (OCDevAddr *dstAddr, OCStackResult msgCode, 
471                        OCQualityOfService qos, OCCoAPToken * token,
472                        const char *payload, uint32_t seqNum)
473 {
474     coap_list_t *optList = NULL;
475     coap_pdu_t *pdu;
476     unsigned char tempBuf[BUF_SIZE_ENCODE_OPTION];
477     uint8_t coapMsgType = COAP_MESSAGE_NON;
478     coap_tid_t tid = COAP_INVALID_TID;
479
480     OC_LOG(INFO, TAG, PCF("Entering OCCoAPSendMessage"));
481
482     printf ("Payload: %s\n", payload);
483     OC_LOG_V(INFO, TAG, "OCStack payload: %s", payload);
484     coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_CONTENT_TYPE,
485                 coap_encode_var_bytes(tempBuf, COAP_MEDIATYPE_APPLICATION_JSON),
486                 tempBuf), OrderOptions);
487     coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_MAXAGE,
488                 coap_encode_var_bytes(tempBuf, 0x2ffff), tempBuf), OrderOptions);
489     coap_insert(&optList, CreateNewOptionNode(COAP_OPTION_OBSERVE,
490                 coap_encode_var_bytes(tempBuf, seqNum), tempBuf), OrderOptions);
491
492     pdu = GenerateCoAPPdu (coapMsgType, OCToCoAPResponseCode(msgCode),
493                            coap_new_message_id(gCoAPCtx), token->tokenLength, token->token,
494                            (unsigned char*) payload, optList);
495     VERIFY_NON_NULL(pdu);
496     coap_show_pdu(pdu);
497
498     tid = coap_send(gCoAPCtx, (coap_address_t*)dstAddr, pdu);
499     OC_LOG_V(INFO, TAG, "TID %d", tid);
500     if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
501     {
502         OC_LOG(INFO, TAG, "Deleting PDU");
503         coap_delete_pdu(pdu);
504         pdu = NULL;
505     }
506     return OC_COAP_OK;
507
508 exit:
509     coap_delete_list(optList);
510     return OC_COAP_ERR;
511 }
512
513 /**
514  * Stop the CoAP client or server processing
515  *
516  * @return 0 - success, else - TBD error
517  */
518 int OCStopCoAP() {
519     OC_LOG(INFO, TAG, PCF("Entering OCStopCoAP"));
520     coap_free_context(gCoAPCtx);
521     gCoAPCtx = NULL;
522     return 0;
523 }
524
525 /**
526  * Called in main loop of CoAP client or server.  Allows low-level CoAP processing of
527  * send, receive, timeout, discovery, callbacks, etc.
528  *
529  * @return 0 - success, else - TBD error
530  */
531 int OCProcessCoAP() {
532     OC_LOG(INFO, TAG, PCF("Entering OCProcessCoAP"));
533
534     coap_read(gCoAPCtx, gCoAPCtx->sockfd);
535     if (-1 != gCoAPCtx->sockfd_wellknown) {
536         coap_read(gCoAPCtx, gCoAPCtx->sockfd_wellknown);
537     }
538     coap_dispatch(gCoAPCtx);
539     return 0;
540 }
541