Check if ckmc_get_data() provides NULL value
[platform/core/connectivity/net-config.git] / src / utils / network-accessibility.c
1 /*
2  *  Internet-accessibility check
3  *
4  * Copyright 2012  Samsung Electronics Co., Ltd
5  *
6  * Licensed under the Flora License, Version 1.1 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.tizenopensource.org/license
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 #include <net/if.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <sys/ioctl.h>
24 #include <sys/socket.h>
25 #include <sys/inotify.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <netinet/if_ether.h>
29 #include <net/if_arp.h>
30 #include <netinet/ip.h>
31 #include <netinet/tcp.h>
32 #include <netinet/udp.h>
33 #include <net/route.h>
34 #include <glib.h>
35 #include <gio/gio.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <string.h>
39
40 #include "log.h"
41 #include "util.h"
42 #include "wifi-agent.h"
43 #include "netsupplicant.h"
44 #include "network-state.h"
45 #include "network-accessibility.h"
46
47 #define BUF_SIZE 2048
48 #define NETCONFIG_INTERNET_CHECK_TIMEOUT        3
49
50 enum netconfig_internet_check_state {
51         INTERNET_CHECK_STATE_NONE                       = 0,
52         INTERNET_CHECK_STATE_DNS_CHECK          = 1,
53         INTERNET_CHECK_STATE_PACKET_CHECK       = 2
54 };
55
56 struct internet_params {
57         int fd;
58         char *addr;
59         int port;
60         guint transport_watch;
61         guint send_watch;
62         gboolean header_done;
63         gboolean request_started;
64 };
65
66 static const char* url_list[] = {
67          "www.google.com",
68          "www.msn.com",
69          "www.yahoo.com",
70          "m.google.com",
71          "www.amazon.com",
72          "www.youtube.com"
73 };
74
75 #define URL_LIST_NUM            6
76
77 static guint timer_id = 0;
78 static const char *proxy_addr = NULL;
79 static gboolean perform_recheck = TRUE;
80 struct internet_params *net_params = NULL;
81 static gboolean is_internet_available = FALSE;
82 static int url_index = 0;
83 static char * redirect_url1 = NULL;
84 static char * redirect_url2 = NULL;
85 static enum netconfig_internet_check_state check_state = INTERNET_CHECK_STATE_NONE;
86
87 static GCancellable *cancellable;
88
89 static void __netconfig_connect_sockets(void);
90 static void __internet_check_state(enum netconfig_internet_check_state state);
91
92 gboolean netconfig_get_internet_status()
93 {
94         return is_internet_available;
95 }
96
97 static void __netconfig_update_internet_status(unsigned char *reply)
98 {
99         /* If the HTTP response is either 302 or 200 with redirection,
100          * then no Internet is available */
101         char *temp = NULL;
102         is_internet_available = FALSE;
103         char *saveptr = NULL;
104
105         if (NULL != reply) {
106                 if ((NULL != g_strrstr((char*)reply, "HTTP/1.1 200")) &&
107                                 (NULL != g_strrstr((char*)reply, "auth action"))) {
108                         DBG("200 OK but redirection found so:: Internet is un-available");
109                 } else if (NULL != g_strrstr((char*)reply, "HTTP/1.1 302")) {
110                         DBG("302:: Internet is un-available");
111                 } else if ((temp = g_strrstr((char*)reply, "Location:")) != NULL) {
112                         char * location = strtok_r(temp, "\r", &saveptr);
113                         if (location != NULL) {
114                                 DBG("%s", location);
115                                 if (redirect_url1 == NULL)
116                                         redirect_url1 = g_strdup(location + strlen("Location: "));
117                                 else if (redirect_url2 == NULL)
118                                         redirect_url2 = g_strdup(location + strlen("Location: "));
119
120                                 if (redirect_url1 != NULL && redirect_url2 != NULL) {
121                                         DBG("[%s] [%s]", redirect_url1, redirect_url2);
122                                         if (g_strcmp0(redirect_url1, redirect_url2) == 0) {
123                                                 DBG("Internet is un-available(Redirection to Error page)");
124                                                 is_internet_available = FALSE;
125                                         } else
126                                                 is_internet_available = TRUE;
127
128                                         g_free(redirect_url1);
129                                         g_free(redirect_url2);
130                                         redirect_url1 = NULL;
131                                         redirect_url2 = NULL;
132                                 }
133                         }
134                 } else {
135                         is_internet_available = TRUE;
136                         DBG("Internet is available");
137                 }
138         }
139
140         if (is_internet_available == TRUE)
141                 netconfig_send_notification_to_net_popup(NETCONFIG_DEL_PORTAL_NOTI, NULL);
142 }
143
144 static gboolean __netconfig_data_activity_timeout(gpointer data)
145 {
146         DBG("Timer timed-out");
147         enum netconfig_internet_check_state prev_state = (enum netconfig_internet_check_state)GPOINTER_TO_INT(data);
148         INFO("Prev_state: state=%d (1:dns check / 2:packet check)", prev_state);
149
150         if (net_params == NULL)
151                 return FALSE;
152
153         if (TRUE == perform_recheck && prev_state != INTERNET_CHECK_STATE_NONE) {
154                 perform_recheck = FALSE;
155                 if (prev_state == INTERNET_CHECK_STATE_DNS_CHECK) {
156                         net_params->request_started = FALSE;
157                         netconfig_check_internet_accessibility();
158                 } else /* (state == NETCONFIG_DATA_ACTIVITY_STATE_PACKET_CHECK) */
159                         __netconfig_connect_sockets();
160         } else {
161                 perform_recheck = TRUE;
162                 __internet_check_state(INTERNET_CHECK_STATE_NONE);
163         }
164
165         return FALSE;
166 }
167
168 static void __netconfig_internet_check_timer_stop(void)
169 {
170         if (timer_id != 0)
171                 netconfig_stop_timer(&timer_id);
172 }
173
174 static void __netconfig_internet_check_timer_start(enum netconfig_internet_check_state state)
175 {
176         static guint timeout = 0;
177         if (timer_id != 0) {
178                 DBG("netconfig_data_activity_timer is already running, so stop it");
179                 __netconfig_internet_check_timer_stop();
180         }
181
182         if (state == INTERNET_CHECK_STATE_NONE)
183                 return;
184         else if (state == INTERNET_CHECK_STATE_DNS_CHECK)
185                 timeout = NETCONFIG_INTERNET_CHECK_TIMEOUT;
186         else if (state == INTERNET_CHECK_STATE_PACKET_CHECK)
187                 timeout = NETCONFIG_INTERNET_CHECK_TIMEOUT;
188
189         netconfig_start_timer_seconds(timeout,
190                         __netconfig_data_activity_timeout,
191                         GINT_TO_POINTER(state),
192                         &timer_id);
193 }
194
195 static void __internet_check_state(enum netconfig_internet_check_state state)
196 {
197         enum netconfig_internet_check_state prev_state = check_state;
198
199         if (prev_state == state)
200                 return;
201
202         ERR("state change (%d) -> (%d)", prev_state, state);
203         check_state = state;
204
205         switch (state) {
206         case INTERNET_CHECK_STATE_DNS_CHECK:
207                 __netconfig_internet_check_timer_start(state);
208                 break;
209         case INTERNET_CHECK_STATE_PACKET_CHECK:
210                 if (prev_state == INTERNET_CHECK_STATE_DNS_CHECK)
211                         __netconfig_internet_check_timer_stop();
212
213                 __netconfig_internet_check_timer_start(state);
214                 break;
215         case INTERNET_CHECK_STATE_NONE:
216                 switch (prev_state) {
217                 case INTERNET_CHECK_STATE_DNS_CHECK:
218                 case INTERNET_CHECK_STATE_PACKET_CHECK:
219                         __netconfig_internet_check_timer_stop();
220                         netconfig_stop_internet_check();
221                         break;
222                 default:
223                         break;
224                 }
225                 break;
226         }
227 }
228
229 static gboolean __received_data_event(GIOChannel *channel,
230                 GIOCondition condition, gpointer data)
231 {
232         int n, fd;
233         unsigned char buf[BUF_SIZE] = { 0, };
234         char error_buf[MAX_SIZE_ERROR_BUFFER] = {0, };
235
236         if (net_params == NULL)
237                 return FALSE;
238
239         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
240                 goto cleanup;
241
242         fd = g_io_channel_unix_get_fd(channel);
243         if (fd < 0)
244                 goto cleanup;
245
246         n = read(fd, buf, BUF_SIZE - 1);
247         DBG("Received %d bytes[%s]", n, buf);
248         buf[BUF_SIZE - 1] = '\0';
249
250         if (n < 0) {
251                 ERR("read failed. %s",
252                         strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER));
253
254                 goto cleanup;
255         } else if (n == 0) {
256                 INFO("connection closed");
257
258                 goto cleanup;
259         }
260
261         /* We got data from server successfully */
262         __netconfig_update_internet_status(buf);
263         __internet_check_state(INTERNET_CHECK_STATE_NONE);
264
265         return TRUE;
266
267 cleanup:
268         /* Fail to get data from server */
269         __internet_check_state(INTERNET_CHECK_STATE_NONE);
270
271         return FALSE;
272 }
273
274 static gboolean __send_data_event(GIOChannel *channel,
275                 GIOCondition condition, gpointer data)
276 {
277         int n, fd;
278         char error_buf[MAX_SIZE_ERROR_BUFFER] = {0, };
279         const char *request_data =
280                         "GET /index.html HTTP/1.1\r\nHost: connman.net\r\n\r\n";
281
282         if (net_params == NULL)
283                 return FALSE;
284
285         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
286                 goto cleanup;
287
288         fd = g_io_channel_unix_get_fd(channel);
289         if (fd < 0)
290                 goto cleanup;
291
292         /* We don't need to send anymore. Just return here.*/
293         /* Socket will be closed received part*/
294         if (net_params->header_done == TRUE)
295                 return FALSE;
296
297         n = send(fd, request_data, strlen(request_data), MSG_NOSIGNAL);
298         DBG("Sent %d bytes", n);
299
300         if (n < 0) {
301                 ERR("send failed. %s",
302                         strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER));
303
304                 goto cleanup;
305         } else if (n == 0) {
306                 INFO("connection closed");
307
308                 goto cleanup;
309         }
310
311         net_params->header_done = TRUE;
312         return TRUE;
313
314 cleanup:
315         __internet_check_state(INTERNET_CHECK_STATE_NONE);
316
317         return FALSE;
318 }
319
320 static void __netconfig_connect_sockets(void)
321 {
322         GIOFlags flags;
323         struct sockaddr_in addr;
324         GIOChannel *channel = NULL;
325         const char *ifname = NULL;
326         int sock;
327
328         if (net_params == NULL || net_params->addr == NULL)
329                 return;
330
331         sock = socket(PF_INET, SOCK_STREAM, 0);
332         if (sock < 0)
333                 goto cleanup;
334
335         ifname = netconfig_get_default_ifname();
336         if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
337                         ifname, strlen(ifname) + 1) < 0) {
338                 ERR("Bind to device error");
339                 close(sock);
340                 goto cleanup;
341         }
342
343         memset(&addr, 0, sizeof(struct sockaddr_in));
344         addr.sin_family = AF_INET;
345         addr.sin_addr.s_addr = inet_addr(net_params->addr);
346         addr.sin_port = htons(net_params->port);
347
348         /* Register Watch */
349         channel = g_io_channel_unix_new(sock);
350
351         flags = g_io_channel_get_flags(channel);
352         g_io_channel_set_flags(channel, flags | G_IO_FLAG_NONBLOCK, NULL);
353         g_io_channel_set_encoding(channel, NULL, NULL);
354         g_io_channel_set_buffered(channel, FALSE);
355
356         if (connect(sock, (struct sockaddr *)&addr,
357                         sizeof(struct sockaddr_in)) < 0) {
358                 if (errno != EINPROGRESS) {
359                         INFO("connect fail");
360                         close(sock);
361                         goto cleanup;
362                 }
363         }
364
365         DBG("Connect successful");
366
367         net_params->fd = sock;
368         net_params->transport_watch = g_io_add_watch(channel,
369                         (GIOCondition) (G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR),
370                         (GIOFunc) __received_data_event, NULL);
371         net_params->send_watch = g_io_add_watch(channel,
372                         (GIOCondition) (G_IO_OUT | G_IO_HUP | G_IO_NVAL | G_IO_ERR),
373                         (GIOFunc) __send_data_event, NULL);
374
375         __internet_check_state(INTERNET_CHECK_STATE_PACKET_CHECK);
376         return;
377
378 cleanup:
379         __internet_check_state(INTERNET_CHECK_STATE_NONE);
380 }
381
382 static void __netconfig_obtain_host_ip_addr_cb(GObject *src,
383                 GAsyncResult *res,
384                 gpointer user_data)
385 {
386         GList *list, *cur;
387         GInetAddress *addr;
388         gchar *str_addr;
389         GError *error = NULL;
390
391         if (net_params == NULL)
392                 return;
393
394         if (check_state == INTERNET_CHECK_STATE_NONE)
395                 return;
396
397         list = g_resolver_lookup_by_name_finish((GResolver *)src, res, &error);
398         if (error != NULL) {
399                 if (error->code == G_IO_ERROR_CANCELLED)
400                         ERR("G_IO_ERROR_CANCELLED is called[%s]", error->message);
401                 g_error_free(error);
402         }
403
404         if (!list) {
405                 INFO("no data");
406                 goto cleanup;
407         }
408
409         for (cur = list; cur; cur = cur->next) {
410                 addr = cur->data;
411                 str_addr = g_inet_address_to_string(addr);
412                 if (!str_addr)
413                         continue;
414
415                 if (net_params != NULL) {
416                         g_free(net_params->addr);
417                         net_params->addr = str_addr;
418                 }
419
420                 g_object_unref(cur->data);
421                 break;
422         }
423
424         g_list_free(list);
425
426         if (net_params->addr == NULL)
427                 goto cleanup;
428
429         net_params->port = 80;
430         __netconfig_connect_sockets();
431
432         return;
433
434 cleanup:
435         __internet_check_state(INTERNET_CHECK_STATE_NONE);
436 }
437
438 gboolean __netconfig_obtain_host_ip_addr(void)
439 {
440         char *host, *addr, *port;
441         char *saveptr = NULL;
442
443         if (net_params == NULL)
444                 return FALSE;
445
446         if (net_params->request_started == TRUE)
447                 return FALSE;
448         else
449                 net_params->request_started = TRUE;
450
451         if (net_params->addr != NULL)
452                 return TRUE;
453
454         proxy_addr = netconfig_get_default_proxy();
455         DBG("Proxy(%s)", proxy_addr);
456
457         if (++url_index >= URL_LIST_NUM)
458                 url_index = 0;
459
460         DBG("addr (%s)", url_list[url_index]);
461
462         /* FIXME: domain proxy should be resolved */
463         if (proxy_addr == NULL) {
464                 GResolver *r = NULL;
465                 r = g_resolver_get_default();
466
467                 g_resolver_lookup_by_name_async(r,
468                                 url_list[url_index],
469                                 cancellable,
470                                 __netconfig_obtain_host_ip_addr_cb,
471                                 NULL);
472                 __internet_check_state(INTERNET_CHECK_STATE_DNS_CHECK);
473
474                 g_object_unref(r);
475                 return FALSE;
476         } else {
477                 host = g_strdup(proxy_addr);
478                 if (host == NULL)
479                         goto cleanup;
480
481                 addr = strtok_r(host, ":", &saveptr);
482                 if (addr == NULL)
483                         goto cleanup;
484
485                 port = strrchr(proxy_addr, ':');
486                 if (port == NULL)
487                         goto cleanup;
488                 else {
489                         char *end;
490                         int tmp = strtol(port + 1, &end, 10);
491
492                         if (*end == '\0') {
493                                 *port = '\0';
494                                 net_params->port = tmp;
495                         }
496                 }
497                 g_free(net_params->addr);
498                 net_params->addr = g_strdup(addr);
499
500                 g_free(host);
501         }
502         return TRUE;
503
504 cleanup:
505         g_free(host);
506         netconfig_stop_internet_check();
507
508         return FALSE;
509 }
510
511 void netconfig_check_internet_accessibility(void)
512 {
513         ERR("::Entry");
514
515         if (net_params == NULL) {
516                 net_params = g_try_malloc0(sizeof(struct internet_params));
517                 if (net_params == NULL)
518                         return;
519                 net_params->fd = -1;
520         }
521
522         if ((check_state != INTERNET_CHECK_STATE_NONE) || (net_params->request_started == TRUE)) {
523                 DBG("Older query in progress");
524                 return;
525         }
526
527         is_internet_available = FALSE;
528
529         /* If the host IP is resolved, directly go for connecting to sockets*/
530         if (__netconfig_obtain_host_ip_addr() == TRUE)
531                 __netconfig_connect_sockets();
532 }
533
534 void netconfig_stop_internet_check(void)
535 {
536         if (net_params == NULL)
537                 return;
538
539         net_params->header_done = FALSE;
540         net_params->request_started = FALSE;
541
542         if (g_cancellable_is_cancelled(cancellable) == FALSE) {
543                 g_cancellable_cancel(cancellable);
544                 ERR("g_cancellable_cancel is called and return stop_internet_check");
545                 return;
546         }
547
548         if (net_params->transport_watch > 0) {
549                 g_source_remove(net_params->transport_watch);
550                 net_params->transport_watch = 0;
551         }
552
553         if (net_params->send_watch > 0) {
554                 g_source_remove(net_params->send_watch);
555                 net_params->send_watch = 0;
556         }
557
558         if (net_params->fd > 0) {
559                 close(net_params->fd);
560                 net_params->fd = -1;
561         }
562
563         if (net_params->addr != NULL) {
564                 g_free(net_params->addr);
565                 net_params->addr = NULL;
566         }
567
568         g_free(net_params);
569         net_params = NULL;
570
571         if (redirect_url1) {
572                 g_free(redirect_url1);
573                 redirect_url1 = NULL;
574         }
575
576         if (redirect_url2) {
577                 g_free(redirect_url2);
578                 redirect_url2 = NULL;
579         }
580 }
581
582 void netconfig_internet_accessibility_init(void)
583 {
584         cancellable = g_cancellable_new();
585 }
586
587 void netconfig_internet_accessibility_deinit(void)
588 {
589         g_object_unref(cancellable);
590 }