[IOT-1413,IOT-1415] Fixed request handling bugs for /oic/ping resource
[platform/upstream/iotivity.git] / resource / csdk / stack / src / oickeepalive.c
1 /* ****************************************************************
2  *
3  * Copyright 2015 Samsung Electronics 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 #include "oickeepalive.h"
22
23 #include <stdio.h>
24 #include <string.h>
25 #include "oic_malloc.h"
26 #include "oic_string.h"
27 #include "oic_time.h"
28 #include "ocrandom.h"
29 #include "uarraylist.h"
30 #include "ocstackinternal.h"
31 #include "ocpayloadcbor.h"
32 #include "ocpayload.h"
33 #include "ocresourcehandler.h"
34 #include "logger.h"
35
36 /**
37  * Logging tag for module name.
38  */
39 #define TAG "OIC_RI_KEEPALIVE"
40
41 static const uint64_t USECS_PER_SEC = 1000000;
42
43 //-----------------------------------------------------------------------------
44 // Macros
45 //-----------------------------------------------------------------------------
46 #define VERIFY_SUCCESS(op, successCode) { if ((op) != (successCode)) \
47             {OIC_LOG_V(FATAL, TAG, "%s failed!!", #op); goto exit;} }
48
49 #define VERIFY_NON_NULL(arg, logLevel, retVal) { if (!(arg)) { OIC_LOG((logLevel), \
50              TAG, #arg " is NULL"); return (retVal); } }
51
52 #define VERIFY_NON_NULL_NR(arg, logLevel) { if (!(arg)) { OIC_LOG((logLevel), \
53              TAG, #arg " is NULL"); return; } }
54
55 #define VERIFY_NON_NULL_V(arg) { if (!arg) {OIC_LOG_V(FATAL, TAG, "%s is NULL", #arg);\
56     goto exit;} }
57
58 /**
59  * The KeepAlive table entries are removed
60  * if it can't receive response message within 60 seconds.
61  */
62 #define KEEPALIVE_RESPONSE_TIMEOUT_SEC 60
63
64 /**
65  * The Min time interval value. (2 minutes)
66  * start from 2 minutes and increases in multiples of 2 up to a maximum of 64minutes.
67  */
68 #define KEEPALIVE_MIN_INTERVAL 2
69
70 /**
71  * The Max time interval value. (64 minutes)
72  */
73 #define KEEPALIVE_MAX_INTERVAL 64
74
75 /**
76  * Default counts of interval value.
77  */
78 #define DEFAULT_INTERVAL_COUNT  6
79
80 /**
81  * KeepAlive key to parser Payload Table.
82  */
83 static const char INTERVAL[] = "in";
84
85 /**
86  * KeepAlive key to get interval values from Payload Table.
87  */
88 static const char INTERVAL_ARRAY[] = "inarray";
89
90 /**
91  * To check if KeepAlive is initialized.
92  */
93 static bool g_isKeepAliveInitialized = false;
94
95 /**
96  * Pointer to handle of the newly created KeepAlive resource.
97  */
98 static OCResourceHandle g_keepAliveHandle = NULL;
99
100 /**
101  * KeepAlive table which holds connection interval.
102  */
103 static u_arraylist_t *g_keepAliveConnectionTable = NULL;
104
105 /**
106  * KeepAlive table entries.
107  */
108 typedef struct
109 {
110     OCMode mode;                    /**< host Mode of Operation. */
111     CAEndpoint_t remoteAddr;        /**< destination Address. */
112     uint32_t interval;              /**< time interval for KeepAlive. in seconds.*/
113     int32_t currIndex;              /**< current interval value index. */
114     size_t intervalSize;            /**< total interval counts. */
115     int64_t *intervalInfo;          /**< interval values for KeepAlive. */
116     bool sentPingMsg;               /**< if oic client already sent ping message. */
117     uint64_t timeStamp;             /**< last sent or received ping message. in microseconds. */
118 } KeepAliveEntry_t;
119
120 /**
121  * Send disconnect message to remove connection.
122  */
123 static OCStackResult SendDisconnectMessage(const KeepAliveEntry_t *entry);
124
125 /**
126  * Send ping message to remote endpoint.
127  */
128 static OCStackResult SendPingMessage(KeepAliveEntry_t *entry);
129
130 /**
131  * Increase interval value to send next ping message.
132  */
133 static void IncreaseInterval(KeepAliveEntry_t *entry);
134
135 /**
136  * Ping Message callback registered with RI for KeepAlive Request.
137  */
138 static OCStackApplicationResult PingRequestCallback(void* ctx, OCDoHandle handle,
139                                              OCClientResponse * clientResponse);
140
141 /**
142  * This function creates KeepAlive resource.
143  * @return  ::OC_STACK_OK or Appropriate error code.
144  */
145 static OCStackResult CreateKeepAliveResource();
146
147 /**
148  * This function deletes KeepAlive resource.
149  * @return  ::OC_STACK_OK or Appropriate error code.
150  */
151 static OCStackResult DeleteKeepAliveResource();
152
153 /**
154  * API to handle the GET request received for a KeepAlive resource.
155  * @param[in]   request     Request Received.
156  * @param[in]   resource    Resource handle used for sending the response.
157  * @return  ::OC_STACK_OK or Appropriate error code.
158  */
159 static OCStackResult HandleKeepAliveGETRequest(OCServerRequest *request,
160                                                const OCResource *resource);
161
162 /**
163  * API to handle the PUT request received for a KeepAlive resource.
164  * @param[in]   request     Request Received.
165  * @param[in]   resource    Resource handle used for sending the response.
166  * @return  ::OC_STACK_OK or Appropriate error code.
167  */
168 static OCStackResult HandleKeepAlivePUTRequest(OCServerRequest *request,
169                                                const OCResource *resource);
170
171 /**
172  * API to handle the Response payload.
173  * @param[in]   endpoint        RemoteEndpoint which sent the packet.
174  * @param[in]   responseCode    Received reseponse code.
175  * @param[in]   respPayload     Response payload.
176  * @return  ::OC_STACK_OK or Appropriate error code.
177  */
178 OCStackResult HandleKeepAliveResponse(const CAEndpoint_t *endPoint,
179                                       OCStackResult responseCode,
180                                       const OCRepPayload *respPayload);
181 /**
182  * Gets keepalive entry.
183  * @param[in]   endpoint    Remote Endpoint information (like ipaddress,
184  *                          port, reference uri and transport type) to
185  *                          which the ping message has to be sent.
186  * @param[out]  index       index of array list.
187  * @return  KeepAlive entry to send ping message.
188  */
189 static KeepAliveEntry_t *GetEntryFromEndpoint(const CAEndpoint_t *endpoint, uint32_t *index);
190
191 /**
192  * Add keepalive entry.
193  * @param[in]   endpoint    Remote Endpoint information (like ipaddress,
194  *                          port, reference uri and transport type).
195  * @param[in]   mode        Whether it is OIC Server or OIC Client.
196  * @param[in]   intervalArray   Received interval values from cloud server.
197  * @return  The KeepAlive entry added in KeepAlive Table.
198  */
199 KeepAliveEntry_t *AddKeepAliveEntry(const CAEndpoint_t *endpoint, OCMode mode,
200                                     int64_t *intervalArray);
201
202 /**
203  * Remove keepalive entry.
204  * @param[in]   endpoint    Remote Endpoint information (like ipaddress,
205  *                          port, reference uri and transport type).
206  * @return  The KeepAlive entry removed in KeepAlive Table.
207  */
208 static OCStackResult RemoveKeepAliveEntry(const CAEndpoint_t *endpoint);
209
210 OCStackResult InitializeKeepAlive(OCMode mode)
211 {
212     OIC_LOG(DEBUG, TAG, "InitializeKeepAlive IN");
213     if (g_isKeepAliveInitialized)
214     {
215         OIC_LOG(DEBUG, TAG, "KeepAlive already initialized");
216         return OC_STACK_OK;
217     }
218
219     if (OC_CLIENT != mode)
220     {
221         // Create the KeepAlive Resource[/oic/ping].
222         OCStackResult result = CreateKeepAliveResource();
223         if (OC_STACK_OK != result)
224         {
225             OIC_LOG_V(ERROR, TAG, "CreateKeepAliveResource failed[%d]", result);
226             return result;
227         }
228     }
229
230     if (!g_keepAliveConnectionTable)
231     {
232         g_keepAliveConnectionTable = u_arraylist_create();
233         if (NULL == g_keepAliveConnectionTable)
234         {
235             OIC_LOG(ERROR, TAG, "Creating KeepAlive Table failed");
236             TerminateKeepAlive(mode);
237             return OC_STACK_ERROR;
238         }
239     }
240
241     g_isKeepAliveInitialized = true;
242
243     OIC_LOG(DEBUG, TAG, "InitializeKeepAlive OUT");
244     return OC_STACK_OK;
245 }
246
247 OCStackResult TerminateKeepAlive(OCMode mode)
248 {
249     OIC_LOG(DEBUG, TAG, "TerminateKeepAlive IN");
250     if (!g_isKeepAliveInitialized)
251     {
252         OIC_LOG(ERROR, TAG, "KeepAlive not initialized");
253         return OC_STACK_ERROR;
254     }
255
256     if (OC_CLIENT != mode)
257     {
258         // Delete the KeepAlive Resource[/oic/ping].
259         OCStackResult result = DeleteKeepAliveResource();
260         if (OC_STACK_OK != result)
261         {
262             OIC_LOG_V(ERROR, TAG, "DeleteKeepAliveResource failed[%d]", result);
263             return result;
264         }
265     }
266
267     if (NULL != g_keepAliveConnectionTable)
268     {
269         u_arraylist_destroy(g_keepAliveConnectionTable);
270         g_keepAliveConnectionTable = NULL;
271     }
272
273     g_isKeepAliveInitialized = false;
274
275     OIC_LOG(DEBUG, TAG, "TerminateKeepAlive OUT");
276     return OC_STACK_OK;
277 }
278
279 OCStackResult CreateKeepAliveResource()
280 {
281     OIC_LOG(DEBUG, TAG, "InitKeepAliveResource IN");
282
283     // Create a KeepAlive resource
284     OCStackResult result = OCCreateResource(&g_keepAliveHandle,
285                                             KEEPALIVE_RESOURCE_TYPE_NAME,
286                                             KEEPALIVE_RESOURCE_INTF_NAME,
287                                             KEEPALIVE_RESOURCE_URI,
288                                             NULL,
289                                             NULL,
290                                             OC_DISCOVERABLE);
291
292     if (OC_STACK_OK != result)
293     {
294         OIC_LOG_V(ERROR, TAG, "Create resource for KeepAlive failed[%d]", result);
295     }
296
297     OIC_LOG(DEBUG, TAG, "InitKeepAliveResource OUT");
298     return result;
299 }
300
301 OCStackResult DeleteKeepAliveResource()
302 {
303     OIC_LOG(DEBUG, TAG, "DeleteKeepAliveResource IN");
304
305     // Create a KeepAlive resource
306     OCStackResult result = OCDeleteResource(g_keepAliveHandle);
307
308     if (OC_STACK_OK != result)
309     {
310         OIC_LOG_V(ERROR, TAG, "Delete resource for KeepAlive failed[%d]", result);
311     }
312
313     OIC_LOG(DEBUG, TAG, "DeleteKeepAliveResource OUT");
314     return result;
315 }
316
317 OCStackResult HandleKeepAliveRequest(OCServerRequest *request,
318                                      const OCResource *resource)
319 {
320     VERIFY_NON_NULL(request, FATAL, OC_STACK_INVALID_PARAM);
321     VERIFY_NON_NULL(resource, FATAL, OC_STACK_INVALID_PARAM);
322
323     OIC_LOG(DEBUG, TAG, "HandleKeepAliveRequest IN");
324
325     OCStackResult result = OC_STACK_ERROR;
326     if (OC_REST_GET == request->method)
327     {
328         switch ((OCObserveAction)request->observationOption)
329         {
330             case OC_OBSERVE_NO_OPTION:
331                 OIC_LOG(DEBUG, TAG, "Received GET request");
332                 result = HandleKeepAliveGETRequest(request, resource);
333                 break;
334             default:
335                 OIC_LOG(DEBUG, TAG, "Not Supported by KeepAlive");
336                 result = OC_STACK_UNAUTHORIZED_REQ;
337         }
338     }
339     else if (OC_REST_PUT == request->method)
340     {
341         OIC_LOG(DEBUG, TAG, "Received PUT request");
342         result = HandleKeepAlivePUTRequest(request, resource);
343     }
344     else
345     {
346         OIC_LOG(DEBUG, TAG, "Not Supported by KeepAlive");
347         result = OC_STACK_UNAUTHORIZED_REQ;
348     }
349
350     // convert OCStackResult to CAResponseResult_t.
351     CAResponseResult_t caResult = OCToCAStackResult(result, request->method);
352     CAEndpoint_t endpoint = {.adapter = CA_DEFAULT_ADAPTER};
353     CopyDevAddrToEndpoint(&request->devAddr, &endpoint);
354
355     // Send response message.
356     SendDirectStackResponse(&endpoint, request->coapID, caResult,
357                            qualityOfServiceToMessageType(request->qos),
358                            request->numRcvdVendorSpecificHeaderOptions,
359                            request->rcvdVendorSpecificHeaderOptions,
360                            request->requestToken, request->tokenLength,
361                            request->resourceUrl, CA_RESPONSE_DATA);
362
363     OIC_LOG(DEBUG, TAG, "HandleKeepAliveRequest OUT");
364     return OC_STACK_OK;
365 }
366
367 OCStackResult HandleKeepAliveGETRequest(OCServerRequest *request,
368                                         const OCResource *resource)
369 {
370     VERIFY_NON_NULL(request, FATAL, OC_STACK_INVALID_PARAM);
371     VERIFY_NON_NULL(resource, FATAL, OC_STACK_INVALID_PARAM);
372
373     OIC_LOG_V(DEBUG, TAG, "Find Ping resource [%s]", request->resourceUrl);
374
375     CAResponseResult_t result = CA_CONTENT;
376     OCResource *resourcePtr = FindResourceByUri(request->resourceUrl);
377     if (!resourcePtr)
378     {
379         // Resource URL not specified
380         OIC_LOG_V(DEBUG, TAG, "There is no Ping resource [%s]", request->resourceUrl);
381         return OC_STACK_NO_RESOURCE;
382     }
383
384     return OC_STACK_OK;
385 }
386
387 OCStackResult HandleKeepAlivePUTRequest(OCServerRequest *request,
388                                         const OCResource *resource)
389 {
390     VERIFY_NON_NULL(request, FATAL, OC_STACK_INVALID_PARAM);
391     VERIFY_NON_NULL(resource, FATAL, OC_STACK_INVALID_PARAM);
392
393     // Get entry from KeepAlive table.
394     CAEndpoint_t endpoint = { .adapter = CA_DEFAULT_ADAPTER };
395     CopyDevAddrToEndpoint(&request->devAddr, &endpoint);
396
397     uint32_t index = 0;
398     KeepAliveEntry_t *entry = GetEntryFromEndpoint(&endpoint, &index);
399     if (!entry)
400     {
401         OIC_LOG(ERROR, TAG, "Received the first keepalive message from client");
402         entry = AddKeepAliveEntry(&endpoint, OC_SERVER, NULL);
403         if (!entry)
404         {
405             OIC_LOG(ERROR, TAG, "Failed to add new keepalive entry");
406             return OC_STACK_ERROR;
407         }
408     }
409
410     OCPayload *ocPayload = NULL;
411     OCParsePayload(&ocPayload, PAYLOAD_TYPE_REPRESENTATION,
412                    request->payload, request->payloadSize);
413     OCRepPayload *repPayload = (OCRepPayload *)ocPayload;
414
415     int64_t interval = 0;
416     OCRepPayloadGetPropInt(repPayload, INTERVAL, &interval);
417     entry->interval = interval;
418     OIC_LOG_V(DEBUG, TAG, "Received interval is [%d]", entry->interval);
419     entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
420
421     OCPayloadDestroy(ocPayload);
422
423     return OC_STACK_OK;
424 }
425
426 OCStackResult HandleKeepAliveResponse(const CAEndpoint_t *endPoint,
427                                       OCStackResult responseCode,
428                                       const OCRepPayload *respPayload)
429 {
430     VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM);
431
432     OIC_LOG(DEBUG, TAG, "HandleKeepAliveResponse IN");
433
434     // Get entry from KeepAlive table.
435     uint32_t index = 0;
436     KeepAliveEntry_t *entry = GetEntryFromEndpoint(endPoint, &index);
437     if (!entry)
438     {
439         // Receive response message about find /oic/ping request.
440         OIC_LOG(ERROR, TAG, "There is no connection info in KeepAlive table");
441
442         if (OC_STACK_NO_RESOURCE == responseCode)
443         {
444             OIC_LOG(ERROR, TAG, "Server doesn't have a ping resource");
445             return OC_STACK_ERROR;
446         }
447         else if (OC_STACK_OK == responseCode)
448         {
449             int64_t *recvInterval = NULL;
450             size_t dimensions[MAX_REP_ARRAY_DEPTH] = { 0 };
451             OCRepPayloadGetIntArray(respPayload, INTERVAL_ARRAY, &recvInterval, dimensions);
452             size_t serverIntervalSize = calcDimTotal(dimensions);
453
454             entry = AddKeepAliveEntry(endPoint, OC_CLIENT, recvInterval);
455             if (!entry)
456             {
457                 OIC_LOG(ERROR, TAG, "Failed to add new KeepAlive entry");
458                 return OC_STACK_ERROR;
459             }
460
461             if (serverIntervalSize)
462             {
463                 // update interval size with received size of server.
464                 entry->intervalSize = serverIntervalSize;
465             }
466
467             // Send first ping message
468             return SendPingMessage(entry);
469         }
470     }
471     else
472     {
473         // Set sentPingMsg values with false.
474         entry->sentPingMsg = false;
475     }
476
477     OIC_LOG(DEBUG, TAG, "HandleKeepAliveResponse OUT");
478     return OC_STACK_OK;
479 }
480
481 void ProcessKeepAlive()
482 {
483     if (!g_isKeepAliveInitialized)
484     {
485         OIC_LOG(ERROR, TAG, "KeepAlive not initialized");
486         return;
487     }
488
489     uint32_t len = u_arraylist_length(g_keepAliveConnectionTable);
490
491     for (uint32_t i = 0; i < len; i++)
492     {
493         KeepAliveEntry_t *entry = (KeepAliveEntry_t *)u_arraylist_get(g_keepAliveConnectionTable,
494                                                                       i);
495         if (NULL == entry)
496         {
497             continue;
498         }
499
500         uint64_t currentTime = OICGetCurrentTime(TIME_IN_US);
501         if (OC_CLIENT == entry->mode)
502         {
503             if (entry->sentPingMsg)
504             {
505                 /*
506                  * If an OIC Client does not receive the response within 1 minutes,
507                  * terminate the connection.
508                  * In this case the timeStamp means last time sent ping message.
509                  */
510                 if ((KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC) <= currentTime - entry->timeStamp)
511                 {
512                     OIC_LOG(DEBUG, TAG, "Client does not receive the response within 1 minutes.");
513
514                     // Send message to disconnect session.
515                     SendDisconnectMessage(entry);
516                 }
517             }
518             else
519             {
520                 if ((entry->interval * KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC)
521                         <= currentTime - entry->timeStamp)
522                 {
523                     // Increase interval value.
524                     IncreaseInterval(entry);
525
526                     OCStackResult result = SendPingMessage(entry);
527                     if (OC_STACK_OK != result)
528                     {
529                         OIC_LOG(ERROR, TAG, "Failed to send ping request");
530                         continue;
531                     }
532                 }
533             }
534         }
535         else if (OC_SERVER == entry->mode)
536         {
537             /*
538              * If an OIC Server does not receive a PUT request to ping resource
539              * within the specified interval time, terminate the connection.
540              * In this case the timeStamp means last time received ping message.
541              */
542             if ((entry->interval * KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC)
543                     <= currentTime - entry->timeStamp)
544             {
545                 OIC_LOG(DEBUG, TAG, "Server does not receive a PUT request.");
546                 SendDisconnectMessage(entry);
547             }
548         }
549     }
550 }
551
552 void IncreaseInterval(KeepAliveEntry_t *entry)
553 {
554     VERIFY_NON_NULL_NR(entry, FATAL);
555
556     OIC_LOG_V(DEBUG, TAG, "Total interval counts: %zu", entry->intervalSize);
557     if (entry->intervalSize > (size_t)entry->currIndex + 1)
558     {
559         entry->currIndex++;
560         entry->interval = entry->intervalInfo[entry->currIndex];
561         OIC_LOG_V(DEBUG, TAG, "increase interval value [%d]", entry->interval);
562     }
563 }
564
565 OCStackResult SendDisconnectMessage(const KeepAliveEntry_t *entry)
566 {
567     VERIFY_NON_NULL(entry, FATAL, OC_STACK_INVALID_PARAM);
568
569     /*
570      * Send empty message to disconnect a connection.
571      * If CA get the empty message from RI, CA will disconnect a connection.
572      */
573
574     OCStackResult result = RemoveKeepAliveEntry(&entry->remoteAddr);
575     if (result != OC_STACK_OK)
576     {
577         return result;
578     }
579
580     CARequestInfo_t requestInfo = { .method = CA_PUT };
581     result = CASendRequest(&entry->remoteAddr, &requestInfo);
582     return CAResultToOCResult(result);
583 }
584
585 OCStackResult SendPingMessage(KeepAliveEntry_t *entry)
586 {
587     VERIFY_NON_NULL(entry, FATAL, OC_STACK_INVALID_PARAM);
588
589     // Send ping message.
590     OCCallbackData pingData = { .context = NULL, .cb = PingRequestCallback };
591     OCDevAddr devAddr = { .adapter = OC_ADAPTER_TCP };
592     CopyEndpointToDevAddr(&(entry->remoteAddr), &devAddr);
593
594     OCRepPayload *payload = OCRepPayloadCreate();
595     if (!payload)
596     {
597         OIC_LOG(ERROR, TAG, "Failed to allocate Payload");
598         return OC_STACK_ERROR;
599     }
600     payload->base.type = PAYLOAD_TYPE_REPRESENTATION;
601     OCRepPayloadSetPropInt(payload, INTERVAL, entry->interval);
602
603     OCStackResult result = OCDoResource(NULL, OC_REST_PUT, KEEPALIVE_RESOURCE_URI, &devAddr,
604                                         (OCPayload *) payload, CT_ADAPTER_TCP, OC_LOW_QOS,
605                                         &pingData, NULL, 0);
606     if (OC_STACK_OK != result)
607     {
608         OIC_LOG(ERROR, TAG, "OCDoResource has failed");
609         return result;
610     }
611
612     // Update timeStamp with time sent ping message for next ping message.
613     entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
614     entry->sentPingMsg = true;
615
616     OIC_LOG_V(DEBUG, TAG, "Client sent ping message, interval [%d]", entry->interval);
617
618     return OC_STACK_OK;
619 }
620
621 OCStackApplicationResult PingRequestCallback(void* ctx, OCDoHandle handle,
622                                              OCClientResponse *clientResponse)
623 {
624     OIC_LOG(DEBUG, TAG, "PingRequestCallback IN");
625     (void) ctx;
626     (void) handle;
627     if (NULL == clientResponse)
628     {
629         OIC_LOG(ERROR, TAG, "clientResponse is NULL");
630         return OC_STACK_DELETE_TRANSACTION;
631     }
632
633     CAEndpoint_t endpoint = { .adapter = CA_ADAPTER_TCP };
634     CopyDevAddrToEndpoint(&(clientResponse->devAddr), &endpoint);
635
636     HandleKeepAliveResponse(&endpoint, clientResponse->result,
637                             (OCRepPayload *)clientResponse->payload);
638
639     OIC_LOG(DEBUG, TAG, "PingRequestCallback OUT");
640     return OC_STACK_DELETE_TRANSACTION;
641 }
642
643 KeepAliveEntry_t *GetEntryFromEndpoint(const CAEndpoint_t *endpoint, uint32_t *index)
644 {
645     if (!g_keepAliveConnectionTable)
646     {
647         OIC_LOG(ERROR, TAG, "KeepAlive Table was not Created.");
648         return NULL;
649     }
650
651     uint32_t len = u_arraylist_length(g_keepAliveConnectionTable);
652
653     for (uint32_t i = 0; i < len; i++)
654     {
655         KeepAliveEntry_t *entry = (KeepAliveEntry_t *)u_arraylist_get(g_keepAliveConnectionTable,
656                                                                       i);
657         if (NULL == entry)
658         {
659             continue;
660         }
661
662         if (!strncmp(entry->remoteAddr.addr, endpoint->addr, sizeof(entry->remoteAddr.addr))
663                 && (entry->remoteAddr.port == endpoint->port))
664         {
665             OIC_LOG(DEBUG, TAG, "Connection Info found in KeepAlive table");
666             *index = i;
667             return entry;
668         }
669     }
670
671     return NULL;
672 }
673
674 KeepAliveEntry_t *AddKeepAliveEntry(const CAEndpoint_t *endpoint, OCMode mode,
675                                     int64_t *intervalInfo)
676 {
677     if (!endpoint)
678     {
679         OIC_LOG(ERROR, TAG, "endpoint is NULL");
680         return NULL;
681     }
682
683     if (!g_keepAliveConnectionTable)
684     {
685         OIC_LOG(ERROR, TAG, "KeepAlive Table was not Created.");
686         return NULL;
687     }
688
689     KeepAliveEntry_t *entry = (KeepAliveEntry_t *) OICCalloc(1, sizeof(KeepAliveEntry_t));
690     if (NULL == entry)
691     {
692         OIC_LOG(ERROR, TAG, "Failed to Calloc KeepAlive Entry");
693         return NULL;
694     }
695
696     entry->mode = mode;
697     entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
698     entry->remoteAddr.adapter = endpoint->adapter;
699     entry->remoteAddr.flags = endpoint->flags;
700     entry->remoteAddr.ifindex = endpoint->ifindex;
701     entry->remoteAddr.port = endpoint->port;
702     strncpy(entry->remoteAddr.addr, endpoint->addr, sizeof(entry->remoteAddr.addr));
703
704     entry->intervalSize = DEFAULT_INTERVAL_COUNT;
705     entry->intervalInfo = intervalInfo;
706     if (!entry->intervalInfo)
707     {
708         entry->intervalInfo = (int64_t*) OICMalloc(entry->intervalSize * sizeof(int64_t));
709         for (size_t i = 0; i < entry->intervalSize; i++)
710         {
711             entry->intervalInfo[i] = KEEPALIVE_MIN_INTERVAL << i;
712         }
713     }
714     entry->interval = entry->intervalInfo[0];
715
716     bool result = u_arraylist_add(g_keepAliveConnectionTable, (void *)entry);
717     if (!result)
718     {
719         OIC_LOG(ERROR, TAG, "Adding node to head failed");
720         OICFree(entry->intervalInfo);
721         OICFree(entry);
722         return NULL;
723     }
724
725     return entry;
726 }
727
728 OCStackResult RemoveKeepAliveEntry(const CAEndpoint_t *endpoint)
729 {
730     VERIFY_NON_NULL(endpoint, FATAL, OC_STACK_INVALID_PARAM);
731
732     uint32_t index = 0;
733     KeepAliveEntry_t *entry = GetEntryFromEndpoint(endpoint, &index);
734     if (!entry)
735     {
736         OIC_LOG(ERROR, TAG, "There is no entry in keepalive table.");
737         return OC_STACK_ERROR;
738     }
739
740     KeepAliveEntry_t *removedEntry = (KeepAliveEntry_t *)
741                                         u_arraylist_remove(g_keepAliveConnectionTable, index);
742     if (NULL == removedEntry)
743     {
744         OIC_LOG(ERROR, TAG, "Removed Entry is NULL");
745         return OC_STACK_ERROR;
746     }
747
748     OIC_LOG_V(DEBUG, TAG, "Remove Connection Info from KeepAlive table, "
749              "remote addr=%s port:%d", removedEntry->remoteAddr.addr,
750              removedEntry->remoteAddr.port);
751
752     OICFree(entry->intervalInfo);
753     OICFree(removedEntry);
754
755     return OC_STACK_OK;
756 }
757
758 void HandleKeepAliveConnCB(const CAEndpoint_t *endpoint, bool isConnected)
759 {
760     VERIFY_NON_NULL_NR(endpoint, FATAL);
761
762     if (isConnected)
763     {
764         OIC_LOG(DEBUG, TAG, "Received the connected device information from CA");
765
766         // Send discover message to find ping resource
767         OCCallbackData pingData = {.context = NULL, .cb = PingRequestCallback };
768         OCDevAddr devAddr = { .adapter = OC_ADAPTER_TCP };
769         CopyEndpointToDevAddr(endpoint, &devAddr);
770
771         OCStackResult result = OCDoResource(NULL, OC_REST_DISCOVER, KEEPALIVE_RESOURCE_URI,
772                                             &devAddr, NULL, CT_ADAPTER_TCP, OC_HIGH_QOS,
773                                             &pingData, NULL, 0);
774         if (OC_STACK_OK != result)
775         {
776             OIC_LOG(ERROR, TAG, "OCDoResource has failed");
777             return;
778         }
779     }
780     else
781     {
782         OIC_LOG(DEBUG, TAG, "Received the disconnected device information from CA");
783
784         OCStackResult result = RemoveKeepAliveEntry(endpoint);
785         if(result != OC_STACK_OK)
786         {
787             return;
788         }
789     }
790 }