replace : iotivity -> iotivity-sec
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / ip_adapter / ios / caipnwmonitor.m
1 /******************************************************************
2 *
3 * Copyright 2014 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 "caipinterface.h"
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/select.h>
28 #include <ifaddrs.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <arpa/inet.h>
32 #include <netinet/in.h>
33 #include <net/if.h>
34 #include <netdb.h>
35 #include <errno.h>
36
37 #include "octhread.h"
38 #include "caipnwmonitor.h"
39 #include "caadapterutils.h"
40 #include "logger.h"
41 #include "oic_malloc.h"
42 #include "oic_string.h"
43 #include <coap/utlist.h>
44
45 #import <Foundation/Foundation.h>
46 #import <SystemConfiguration/CaptiveNetwork.h>
47
48 #define TAG "OIC_CA_IP_MONITOR"
49
50 /**
51  * Mutex for synchronizing access to cached interface and IP address information.
52  */
53 static oc_mutex g_networkMonitorContextMutex = NULL;
54
55 /**
56  * Used to storing network interface.
57  */
58 static u_arraylist_t *g_netInterfaceList = NULL;
59
60 /**
61  * Used to storing initial AP Name.
62  */
63 NSString *initialAPName = nil;
64
65 /**
66  * Used to storing adapter changes callback interface.
67  */
68 static struct CAIPCBData_t *g_adapterCallbackList = NULL;
69
70 /**
71  * Initialize the network interface monitoring list.
72  */
73 static CAResult_t CAIPInitializeNetworkMonitorList();
74
75 /**
76  * Destroy the network interface monitoring list.
77  */
78 static void CAIPDestroyNetworkMonitorList();
79
80 /**
81  * Compare the interface with the already added interface in list.
82  */
83 static bool CACmpNetworkList(size_t ifiindex);
84
85 /**
86  * Add new network interface in list.
87  */
88 static CAResult_t CAAddNetworkMonitorList(CAInterface_t *ifitem);
89
90 /**
91  * Remove network interace from list.
92  */
93 static void CARemoveNetworkMonitorList(int ifiindex);
94
95 /**
96  * Pass the changed network status through the stored callback.
97  */
98 static void CAIPPassNetworkChangesToAdapter(CANetworkStatus_t status);
99
100 /**
101  * Create new interface item.
102  */
103 static CAInterface_t *CANewInterfaceItem(int index, const char *name, int family,
104                                          const char *addr, int flags);
105
106 /**
107   * Callback function invoked for each observer of a notification
108   * when the notification is posted.
109   * @param center The notification center handling the notification
110   * @param observer An arbitrary value, other than NULL, that identifies the observer
111   * @param name The name of the notification being posted
112   * @param object An arbitrary value that identifies the object posting the notification
113   * @param userInfo A dictionary containing additional information regarding
114   *        the notification
115   */
116 static void onNotifyCallback(CFNotificationCenterRef center, void *observer,
117                     CFStringRef name, const void *object, CFDictionaryRef userInfo);
118
119 /**
120  * Check whether Celluler data is On/Off
121  */
122 bool isCellulerDataEnabled();
123
124 static CAResult_t CAIPInitializeNetworkMonitorList()
125 {
126     if (!g_networkMonitorContextMutex)
127     {
128         g_networkMonitorContextMutex = oc_mutex_new();
129         if (!g_networkMonitorContextMutex)
130         {
131             OIC_LOG(ERROR, TAG, "oc_mutex_new has failed");
132             return CA_STATUS_FAILED;
133         }
134     }
135
136     if (!g_netInterfaceList)
137     {
138         g_netInterfaceList = u_arraylist_create();
139         if (!g_netInterfaceList)
140         {
141             OIC_LOG(ERROR, TAG, "u_arraylist_create has failed");
142             CAIPDestroyNetworkMonitorList();
143             return CA_STATUS_FAILED;
144         }
145     }
146     return CA_STATUS_OK;
147 }
148
149 static void CAIPDestroyNetworkMonitorList()
150 {
151     if (g_netInterfaceList)
152     {
153         u_arraylist_destroy(g_netInterfaceList);
154         g_netInterfaceList = NULL;
155     }
156
157     if (g_networkMonitorContextMutex)
158     {
159         oc_mutex_free(g_networkMonitorContextMutex);
160         g_networkMonitorContextMutex = NULL;
161     }
162 }
163
164 static bool CACmpNetworkList(size_t ifiindex)
165 {
166     if (!g_netInterfaceList)
167     {
168         OIC_LOG(ERROR, TAG, "g_netInterfaceList is NULL");
169         return false;
170     }
171
172     oc_mutex_lock(g_networkMonitorContextMutex);
173
174     size_t list_length = u_arraylist_length(g_netInterfaceList);
175     for (size_t list_index = 0; list_index < list_length; list_index++)
176     {
177         CAInterface_t *currItem = (CAInterface_t *) u_arraylist_get(g_netInterfaceList,
178                                                                     list_index);
179         if (currItem->index == ifiindex)
180         {
181             oc_mutex_unlock(g_networkMonitorContextMutex);
182             return true;
183         }
184     }
185     oc_mutex_unlock(g_networkMonitorContextMutex);
186     return false;
187 }
188
189 static CAResult_t CAAddNetworkMonitorList(CAInterface_t *ifitem)
190 {
191     VERIFY_NON_NULL(g_netInterfaceList, TAG, "g_netInterfaceList is NULL");
192     VERIFY_NON_NULL(ifitem, TAG, "ifitem is NULL");
193
194     oc_mutex_lock(g_networkMonitorContextMutex);
195     bool result = u_arraylist_add(g_netInterfaceList, (void *) ifitem);
196     if (!result)
197     {
198         OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
199         oc_mutex_unlock(g_networkMonitorContextMutex);
200         return CA_STATUS_FAILED;
201     }
202     oc_mutex_unlock(g_networkMonitorContextMutex);
203     return CA_STATUS_OK;
204 }
205
206 static void CARemoveNetworkMonitorList(int ifiindex)
207 {
208     VERIFY_NON_NULL_VOID(g_netInterfaceList, TAG, "g_netInterfaceList is NULL");
209
210     oc_mutex_lock(g_networkMonitorContextMutex);
211
212     size_t list_length = u_arraylist_length(g_netInterfaceList);
213     for (size_t list_index = 0; list_index < list_length; list_index++)
214     {
215         CAInterface_t *removedifitem = (CAInterface_t *) u_arraylist_get(
216                 g_netInterfaceList, list_index);
217         if (removedifitem && ((int)removedifitem->index) == ifiindex)
218         {
219             if (u_arraylist_remove(g_netInterfaceList, list_index))
220             {
221                 OICFree(removedifitem);
222                 oc_mutex_unlock(g_networkMonitorContextMutex);
223                 return;
224             }
225             continue;
226         }
227     }
228     oc_mutex_unlock(g_networkMonitorContextMutex);
229     return;
230 }
231
232 CAResult_t CAIPStartNetworkMonitor(CAIPAdapterStateChangeCallback callback,
233                                    CATransportAdapter_t adapter)
234 {
235     NSDictionary *connectionDetails = [NSDictionary dictionary];
236     NSArray *myArray = (id) CNCopySupportedInterfaces();
237     if (myArray) {
238         CFDictionaryRef myDict = CNCopyCurrentNetworkInfo((__bridge CFStringRef)myArray[0]);
239         connectionDetails = (NSDictionary*) myDict;
240     }
241
242     initialAPName = connectionDetails[@"SSID"];
243     OIC_LOG_V(INFO, TAG, "Initial AP Name = %s", [initialAPName UTF8String]);
244
245     CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
246                                     NULL,
247                                     onNotifyCallback,
248                                     CFSTR("com.apple.system.config.network_change"),
249                                     NULL,
250                                     CFNotificationSuspensionBehaviorDeliverImmediately);
251
252     OIC_LOG_V(INFO, TAG, "Celluler Data Enabled = %d", isCellulerDataEnabled());
253
254     CAResult_t res = CAIPInitializeNetworkMonitorList();
255     if (CA_STATUS_OK == res)
256     {
257         return CAIPSetNetworkMonitorCallback(callback, adapter);
258     }
259     return res;
260 }
261
262 CAResult_t CAIPStopNetworkMonitor(CATransportAdapter_t adapter)
263 {
264     CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(),
265             NULL, CFSTR("com.apple.system.config.network_change"), NULL);
266     CAIPDestroyNetworkMonitorList();
267     return CAIPUnSetNetworkMonitorCallback(adapter);
268 }
269
270 int CAGetPollingInterval(int interval)
271 {
272     return interval;
273 }
274
275 static void CAIPPassNetworkChangesToAdapter(CANetworkStatus_t status)
276 {
277     CAIPCBData_t *cbitem = NULL;
278     LL_FOREACH(g_adapterCallbackList, cbitem)
279     {
280         if (cbitem && cbitem->adapter)
281         {
282             cbitem->callback(cbitem->adapter, status);
283         }
284     }
285 }
286
287 CAResult_t CAIPSetNetworkMonitorCallback(CAIPAdapterStateChangeCallback callback,
288                                          CATransportAdapter_t adapter)
289 {
290     if (!callback)
291     {
292         OIC_LOG(ERROR, TAG, "callback is null");
293         return CA_STATUS_INVALID_PARAM;
294     }
295
296     CAIPCBData_t *cbitem = NULL;
297     LL_FOREACH(g_adapterCallbackList, cbitem)
298     {
299         if (cbitem && adapter == cbitem->adapter && callback == cbitem->callback)
300         {
301             OIC_LOG(DEBUG, TAG, "this callback is already added");
302             return CA_STATUS_OK;
303         }
304     }
305
306     cbitem = (CAIPCBData_t *)OICCalloc(1, sizeof(*cbitem));
307     if (!cbitem)
308     {
309         OIC_LOG(ERROR, TAG, "Malloc failed");
310         return CA_STATUS_FAILED;
311     }
312
313     cbitem->adapter = adapter;
314     cbitem->callback = callback;
315     LL_APPEND(g_adapterCallbackList, cbitem);
316
317     return CA_STATUS_OK;
318 }
319
320 CAResult_t CAIPUnSetNetworkMonitorCallback(CATransportAdapter_t adapter)
321 {
322     CAIPCBData_t *cbitem = NULL;
323     CAIPCBData_t *tmpCbitem = NULL;
324     LL_FOREACH_SAFE(g_adapterCallbackList, cbitem, tmpCbitem)
325     {
326         if (cbitem && adapter == cbitem->adapter)
327         {
328             OIC_LOG(DEBUG, TAG, "remove specific callback");
329             LL_DELETE(g_adapterCallbackList, cbitem);
330             OICFree(cbitem);
331             return CA_STATUS_OK;
332         }
333     }
334     return CA_STATUS_OK;
335 }
336
337 static CAInterface_t *CANewInterfaceItem(int index, const char *name, int family,
338                                          const char *addr, int flags)
339 {
340     CAInterface_t *ifitem = (CAInterface_t *)OICCalloc(1, sizeof (CAInterface_t));
341     if (!ifitem)
342     {
343         OIC_LOG(ERROR, TAG, "Malloc failed");
344         return NULL;
345     }
346
347     OICStrcpy(ifitem->name, sizeof (ifitem->name), name);
348     ifitem->index = index;
349     ifitem->family = family;
350     OICStrcpy(ifitem->addr, sizeof (ifitem->addr), addr);
351     ifitem->flags = flags;
352
353     return ifitem;
354 }
355
356 u_arraylist_t *CAFindInterfaceChange()
357 {
358     u_arraylist_t *iflist = NULL;
359     char buf[4096] = { 0 };
360     struct nlmsghdr *nh = NULL;
361     struct iovec iov = { .iov_base = buf,
362                          .iov_len = sizeof (buf) };
363     struct msghdr msg = { .msg_name = (void *)0,
364                           .msg_namelen = 0,
365                           .msg_iov = &iov,
366                           .msg_iovlen = 1 };
367
368     ssize_t len = recvmsg(caglobals.ip.netlinkFd, &msg, 0);
369     return iflist;
370 }
371
372 u_arraylist_t *CAIPGetInterfaceInformation(int desiredIndex)
373 {
374     if (desiredIndex < 0)
375     {
376         OIC_LOG_V(ERROR, TAG, "invalid index : %d", desiredIndex);
377         return NULL;
378     }
379
380     u_arraylist_t *iflist = u_arraylist_create();
381     if (!iflist)
382     {
383         OIC_LOG_V(ERROR, TAG, "Failed to create iflist: %s", strerror(errno));
384         return NULL;
385     }
386
387     struct ifaddrs *ifp = NULL;
388     if (-1 == getifaddrs(&ifp))
389     {
390         OIC_LOG_V(ERROR, TAG, "Failed to get ifaddrs: %s", strerror(errno));
391         u_arraylist_destroy(iflist);
392         return NULL;
393     }
394
395     struct ifaddrs *ifa = NULL;
396     for (ifa = ifp; ifa; ifa = ifa->ifa_next)
397     {
398         if (!ifa->ifa_addr)
399         {
400             continue;
401         }
402         int family = ifa->ifa_addr->sa_family;
403         if ((ifa->ifa_flags & IFF_LOOPBACK) || (AF_INET != family && AF_INET6 != family))
404         {
405             continue;
406         }
407
408         int ifindex = if_nametoindex(ifa->ifa_name);
409         if (desiredIndex && (ifindex != desiredIndex))
410         {
411             continue;
412         }
413
414         int length = u_arraylist_length(iflist);
415         int already = false;
416         for (int i = length-1; i >= 0; i--)
417         {
418             CAInterface_t *ifitem = (CAInterface_t *)u_arraylist_get(iflist, i);
419
420             if (ifitem
421                 && (int)ifitem->index == ifindex
422                 && ifitem->family == (uint16_t)family)
423             {
424                 already = true;
425                 break;
426             }
427         }
428         if (already)
429         {
430             continue;
431         }
432
433         CAInterface_t *ifitem = (CAInterface_t *)OICCalloc(1, sizeof(CAInterface_t));
434         if (!ifitem)
435         {
436             OIC_LOG(ERROR, TAG, "Malloc failed");
437             goto exit;
438         }
439
440         OICStrcpy(ifitem->name, INTERFACE_NAME_MAX, ifa->ifa_name);
441         ifitem->index = ifindex;
442         ifitem->family = family;
443         ifitem->flags = ifa->ifa_flags;
444
445         if (ifitem->family == AF_INET6)
446         {
447             struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa->ifa_addr;
448             inet_ntop(ifitem->family, (void *)&(in6->sin6_addr), ifitem->addr,
449                       sizeof(ifitem->addr));
450         }
451         else if (ifitem->family == AF_INET)
452         {
453             struct sockaddr_in *in = (struct sockaddr_in*) ifa->ifa_addr;
454             inet_ntop(ifitem->family, (void *)&(in->sin_addr), ifitem->addr,
455                       sizeof(ifitem->addr));
456         }
457
458         bool result = u_arraylist_add(iflist, ifitem);
459         if (!result)
460         {
461             OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
462             goto exit;
463         }
464
465         bool isFound = CACmpNetworkList(ifitem->index);
466         if (!isFound)
467         {
468             CAInterface_t *newifitem = CANewInterfaceItem(ifitem->index, ifitem->name,
469                                           ifitem->family, ifitem->addr, ifitem->flags);
470             CAResult_t ret = CAAddNetworkMonitorList(newifitem);
471             if (CA_STATUS_OK != ret)
472             {
473                 OICFree(newifitem);
474                 goto exit;
475             }
476             CAIPPassNetworkChangesToAdapter(CA_INTERFACE_UP);
477             OIC_LOG_V(DEBUG, TAG, "Added interface: %s (%d)", ifitem->name, ifitem->family);
478         }
479     }
480     freeifaddrs(ifp);
481     return iflist;
482
483 exit:
484     freeifaddrs(ifp);
485     u_arraylist_destroy(iflist);
486     return NULL;
487 }
488
489
490 void CAIpStateEnabled()
491 {
492     OIC_LOG(DEBUG, TAG, "Wifi is in Activated State");
493     CAIPPassNetworkChangesToAdapter(CA_INTERFACE_UP);
494
495     u_arraylist_t *iflist = CAIPGetInterfaceInformation(0);
496     if (!iflist)
497     {
498         OIC_LOG_V(ERROR, TAG, "get interface info failed: %s", strerror(errno));
499         return;
500     }
501
502     size_t listLength = u_arraylist_length(iflist);
503     for (size_t i = 0; i < listLength; i++)
504     {
505         CAInterface_t *ifitem = (CAInterface_t *)u_arraylist_get(iflist, i);
506         if (!ifitem)
507         {
508             continue;
509         }
510         CAProcessNewInterface(ifitem);
511     }
512     u_arraylist_destroy(iflist);
513 }
514
515 void CAIpStateDisabled()
516 {
517     OIC_LOG(DEBUG, TAG, "Wifi or Celluler Data is in Deactivated State");
518     CAIPPassNetworkChangesToAdapter(CA_INTERFACE_DOWN);
519 }
520
521 bool isCellulerDataEnabled()
522 {
523     struct ifaddrs* interfaces = NULL;
524     struct ifaddrs* temp_addr = NULL;
525     int success = 0;
526     bool isEnabled = false;
527
528     success = getifaddrs(&interfaces);
529
530     if (success == 0)
531     {
532         temp_addr = interfaces;
533
534         while (temp_addr != NULL)
535         {
536             if ((temp_addr->ifa_addr->sa_family == AF_INET) || (temp_addr->ifa_addr->sa_family == AF_INET6))
537             {
538                 NSString* ifa_name = [NSString stringWithUTF8String: temp_addr->ifa_name];
539                 if ([ifa_name isEqualToString:@"pdp_ip0"])
540                 {
541                     isEnabled = true;
542                     break;
543                 }
544             }
545             temp_addr = temp_addr->ifa_next;
546         }
547     }
548     freeifaddrs(interfaces);
549
550     return isEnabled;
551 }
552
553 static void onNotifyCallback(CFNotificationCenterRef center, void *observer, CFStringRef name,
554                                             const void *object, CFDictionaryRef userInfo)
555 {
556     NSString* notifyName = (__bridge NSString*) name;
557     if ([notifyName isEqualToString:@"com.apple.system.config.network_change"]) {
558         NSDictionary *connectionDetails = [NSDictionary dictionary];
559         NSArray *myArray = (id) CNCopySupportedInterfaces();
560         if (myArray) {
561             CFDictionaryRef myDict = CNCopyCurrentNetworkInfo((__bridge CFStringRef)myArray[0]);
562             connectionDetails = (NSDictionary*)myDict;
563         }
564
565         NSString *apName = connectionDetails[@"SSID"];
566         if (apName == nil && !isCellulerDataEnabled())
567         {
568             OIC_LOG(INFO, TAG, "WiFi AP or Celluler Data is OFF!");
569             initialAPName = nil;
570             CAIpStateDisabled();
571         }
572         else if(![apName isEqualToString:initialAPName] || isCellulerDataEnabled())
573         {
574             initialAPName = apName;
575             OIC_LOG_V(INFO, TAG, "Current AP Name = %s", [initialAPName UTF8String]);
576             OIC_LOG_V(INFO, TAG, "Celluler Data Enabled = %d", isCellulerDataEnabled());
577             CAIpStateEnabled();
578         }
579     }
580 }