94fc1607fd6adb688e6eeb3244d704262c8ca0ea
[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,
52         INTERNET_CHECK_STATE_DNS_CHECK,
53         INTERNET_CHECK_STATE_PACKET_CHECK
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         GCancellable *resolv_cancel;
65 };
66
67 const static char* url_list[] = {
68          "www.google.com",
69          "www.msn.com",
70          "www.yahoo.com",
71          "m.google.com",
72          "www.amazon.com",
73          "www.youtube.com"
74  };
75
76 static guint timer_id = 0;
77 static const char *proxy_addr = NULL;
78 static gboolean perform_recheck = TRUE;
79 struct internet_params *net_params = NULL;
80 static gboolean is_internet_available = FALSE;
81 const static int url_list_num = 6;
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 =
86                 INTERNET_CHECK_STATE_NONE;
87
88 static void __netconfig_connect_sockets(void);
89 static void __internet_check_state(
90                 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
104         if (NULL != reply) {
105                 if ((NULL != g_strrstr((char*)reply, "HTTP/1.1 200")) &&
106                                 (NULL != g_strrstr((char*)reply, "auth action"))) {
107                         DBG("200 OK but redirection found so:: Internet is un-available");
108                 } else if (NULL != g_strrstr((char*)reply, "HTTP/1.1 302")) {
109                         DBG("302:: Internet is un-available");
110                 } else if ((temp = g_strrstr((char*)reply, "Location:")) != NULL) {
111                         char * location = strtok(temp, "\r");
112                         if (location != NULL) {
113                                 DBG("%s", location);
114                                 if (redirect_url1 == NULL)
115                                         redirect_url1 = g_strdup(location + strlen("Location: "));
116                                 else if (redirect_url2 == NULL)
117                                         redirect_url2 = g_strdup(location + strlen("Location: "));
118
119                                 if (redirect_url1 != NULL && redirect_url2 != NULL) {
120                                         DBG("[%s] [%s]", redirect_url1, redirect_url2);
121                                         if (g_strcmp0(redirect_url1, redirect_url2) == 0) {
122                                                 DBG("Internet is un-available(Redirection to Error page)");
123                                                 is_internet_available = FALSE;
124                                         } else
125                                                 is_internet_available = TRUE;
126
127                                         g_free(redirect_url1);
128                                         g_free(redirect_url2);
129                                         redirect_url1 = NULL;
130                                         redirect_url2 = NULL;
131                                 }
132                         }
133                 } else {
134                         is_internet_available = TRUE;
135                         DBG("Internet is available");
136                 }
137         }
138
139         if (is_internet_available == TRUE)
140                 netconfig_send_notification_to_net_popup(NETCONFIG_DEL_PORTAL_NOTI, NULL);
141 }
142
143 static gboolean __netconfig_data_activity_timeout(gpointer data)
144 {
145         DBG("Timer timed-out");
146         enum netconfig_internet_check_state prev_state =
147                                         (enum netconfig_internet_check_state)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(
175                 enum netconfig_internet_check_state state)
176 {
177         static guint timeout = 0;
178         if (timer_id != 0) {
179                 DBG("netconfig_data_activity_timer is already running, so stop it");
180                 __netconfig_internet_check_timer_stop();
181         }
182
183         if (state == INTERNET_CHECK_STATE_NONE)
184                 return;
185         else if (state == INTERNET_CHECK_STATE_DNS_CHECK)
186                 timeout = NETCONFIG_INTERNET_CHECK_TIMEOUT;
187         else if (state == INTERNET_CHECK_STATE_PACKET_CHECK)
188                 timeout = NETCONFIG_INTERNET_CHECK_TIMEOUT;
189
190         netconfig_start_timer_seconds(timeout,
191                         __netconfig_data_activity_timeout,
192                         (void *)state,
193                         &timer_id);
194 }
195
196 static void __internet_check_state(
197                 enum netconfig_internet_check_state state)
198 {
199         if (check_state == state)
200                 return;
201
202         INFO("state change (%d) -> (%d)", check_state, state);
203         switch (state) {
204         case INTERNET_CHECK_STATE_DNS_CHECK:
205                 __netconfig_internet_check_timer_start(state);
206                 break;
207         case INTERNET_CHECK_STATE_PACKET_CHECK:
208                 if (check_state == INTERNET_CHECK_STATE_DNS_CHECK)
209                         __netconfig_internet_check_timer_stop();
210
211                 __netconfig_internet_check_timer_start(state);
212                 break;
213         case INTERNET_CHECK_STATE_NONE:
214                 switch (check_state) {
215                 case INTERNET_CHECK_STATE_DNS_CHECK:
216                 case INTERNET_CHECK_STATE_PACKET_CHECK:
217                         __netconfig_internet_check_timer_stop();
218                         netconfig_stop_internet_check();
219                         break;
220                 default:
221                         break;
222                 }
223                 break;
224         }
225         check_state = state;
226 }
227
228 void netconfig_stop_internet_check(void)
229 {
230         if (net_params == NULL)
231                 return;
232
233         net_params->header_done = FALSE;
234         net_params->request_started = FALSE;
235
236         if (net_params->resolv_cancel != NULL) {
237                 g_cancellable_cancel(net_params->resolv_cancel);
238                 g_object_unref(net_params->resolv_cancel);
239                 net_params->resolv_cancel = NULL;
240         }
241
242         if (net_params->transport_watch > 0) {
243                 g_source_remove(net_params->transport_watch);
244                 net_params->transport_watch = 0;
245         }
246
247         if (net_params->send_watch > 0) {
248                 g_source_remove(net_params->send_watch);
249                 net_params->send_watch = 0;
250         }
251
252         if (net_params->fd > 0) {
253                 close(net_params->fd);
254                 net_params->fd = -1;
255         }
256
257         if (net_params->addr != NULL) {
258                 g_free(net_params->addr);
259                 net_params->addr = NULL;
260         }
261
262         g_free(net_params);
263         net_params = NULL;
264
265         if (redirect_url1) {
266                 g_free(redirect_url1);
267                 redirect_url1 = NULL;
268         }
269
270         if (redirect_url2) {
271                 g_free(redirect_url2);
272                 redirect_url2 = NULL;
273         }
274 }
275
276 static gboolean __received_data_event(GIOChannel *channel,
277                 GIOCondition condition, gpointer data)
278 {
279         int n, fd;
280         unsigned char buf[BUF_SIZE] = { 0, };
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         n = read(fd, buf, BUF_SIZE - 1);
293         DBG("Received %d bytes[%s]", n, buf);
294         buf[BUF_SIZE - 1] = '\0';
295
296         if (n < 0) {
297                 ERR("read failed. %s", strerror(errno));
298
299                 goto cleanup;
300         } else if (n == 0) {
301                 INFO("connection closed");
302
303                 goto cleanup;
304         }
305
306         /* We got data from server successfully */
307         __netconfig_update_internet_status(buf);
308         __internet_check_state(INTERNET_CHECK_STATE_NONE);
309
310         return TRUE;
311
312 cleanup:
313         /* Fail to get data from server */
314         __internet_check_state(INTERNET_CHECK_STATE_NONE);
315
316         return FALSE;
317 }
318
319 static gboolean __send_data_event(GIOChannel *channel,
320                 GIOCondition condition, gpointer data)
321 {
322         int n, fd;
323         const char *request_data =
324                         "GET /index.html HTTP/1.1\r\nHost: connman.net\r\n\r\n";
325
326         if (net_params == NULL)
327                 return FALSE;
328
329         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
330                 goto cleanup;
331
332         fd = g_io_channel_unix_get_fd(channel);
333         if (fd < 0)
334                 goto cleanup;
335
336         /* We don't need to send anymore. Just return here.*/
337         /* Socket will be closed received part*/
338         if (net_params->header_done == TRUE)
339                 return FALSE;
340
341         n = send(fd, request_data, strlen(request_data), 0);
342         DBG("Sent %d bytes", n);
343
344         if (n < 0) {
345                 ERR("send failed. %s", strerror(errno));
346
347                 goto cleanup;
348         } else if (n == 0) {
349                 INFO("connection closed");
350
351                 goto cleanup;
352         }
353
354         net_params->header_done = TRUE;
355         return TRUE;
356
357 cleanup:
358         __internet_check_state(INTERNET_CHECK_STATE_NONE);
359
360         return FALSE;
361 }
362
363 static void __netconfig_connect_sockets(void)
364 {
365         GIOFlags flags;
366         struct sockaddr_in addr;
367         GIOChannel *channel = NULL;
368         int sock;
369
370         if (net_params == NULL || net_params->addr == NULL)
371                 return;
372
373         sock = socket(PF_INET, SOCK_STREAM, 0);
374         if (sock < 0)
375                 goto cleanup;
376
377         if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
378                         WIFI_IFNAME, strlen(WIFI_IFNAME) + 1) < 0) {
379                 ERR("Bind to device error");
380                 close(sock);
381                 goto cleanup;
382         }
383
384         memset(&addr, 0, sizeof(struct sockaddr_in));
385         addr.sin_family = AF_INET;
386         addr.sin_addr.s_addr = inet_addr(net_params->addr);
387         addr.sin_port = htons(net_params->port);
388
389         /* Register Watch */
390         channel = g_io_channel_unix_new(sock);
391
392         flags = g_io_channel_get_flags(channel);
393         g_io_channel_set_flags(channel, flags | G_IO_FLAG_NONBLOCK, NULL);
394         g_io_channel_set_encoding(channel, NULL, NULL);
395         g_io_channel_set_buffered(channel, FALSE);
396
397         if (connect(sock, (struct sockaddr *)&addr,
398                         sizeof(struct sockaddr_in)) < 0) {
399                 if (errno != EINPROGRESS) {
400                         INFO("connect fail");
401                         close(sock);
402                         goto cleanup;
403                 }
404         }
405
406         DBG("Connect successful");
407
408         net_params->fd = sock;
409         net_params->transport_watch = g_io_add_watch(channel,
410                         (GIOCondition) (G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR),
411                         (GIOFunc) __received_data_event, NULL);
412         net_params->send_watch = g_io_add_watch(channel,
413                         (GIOCondition) (G_IO_OUT | G_IO_HUP | G_IO_NVAL | G_IO_ERR),
414                         (GIOFunc) __send_data_event, NULL);
415
416         __internet_check_state(INTERNET_CHECK_STATE_PACKET_CHECK);
417         return;
418
419         cleanup:
420         __internet_check_state(INTERNET_CHECK_STATE_NONE);
421 }
422
423 static void __netconfig_obtain_host_ip_addr_cb(GObject *src,
424                 GAsyncResult *res,
425                 gpointer user_data)
426 {
427         GList *list, *cur;
428         GInetAddress *addr;
429         gchar *str_addr;
430
431         if (net_params == NULL)
432                 return;
433
434         if (check_state == INTERNET_CHECK_STATE_NONE)
435                 return;
436
437         list = g_resolver_lookup_by_name_finish((GResolver *)src, res, NULL);
438         if (!list) {
439                 INFO("no data");
440                 goto cleanup;
441         }
442
443         for (cur = list; cur; cur = cur->next) {
444                 addr = cur->data;
445                 str_addr = g_inet_address_to_string(addr);
446                 if (!str_addr)
447                         continue;
448
449                 if (net_params != NULL) {
450                         g_free(net_params->addr);
451                         net_params->addr = str_addr;
452                 }
453
454                 g_object_unref(cur->data);
455                 break;
456         }
457
458         g_list_free(list);
459
460         if (net_params->addr == NULL)
461                 goto cleanup;
462
463         net_params->port = 80;
464         __netconfig_connect_sockets();
465
466         return;
467
468 cleanup:
469         __internet_check_state(INTERNET_CHECK_STATE_NONE);
470 }
471
472 gboolean __netconfig_obtain_host_ip_addr(void)
473 {
474         char *host, *addr, *port;
475
476         if (net_params == NULL)
477                 return FALSE;
478
479         if (net_params->request_started == TRUE)
480                 return FALSE;
481         else
482                 net_params->request_started = TRUE;
483
484         if (net_params->addr != NULL)
485                 return TRUE;
486
487         proxy_addr = netconfig_get_default_proxy();
488         DBG("Proxy(%s)", proxy_addr);
489
490         if (++url_index >= url_list_num)
491                 url_index = 0;
492
493         DBG("addr (%s)", url_list[url_index]);
494
495         /* FIXME: domain proxy should be resolved */
496         if (proxy_addr == NULL) {
497                 GResolver *r = NULL;
498                 net_params->resolv_cancel = g_cancellable_new();
499                 r = g_resolver_get_default();
500
501                 g_resolver_lookup_by_name_async(r,
502                                 url_list[url_index],
503                                 net_params->resolv_cancel,
504                                 __netconfig_obtain_host_ip_addr_cb,
505                                 NULL);
506                 __internet_check_state(INTERNET_CHECK_STATE_DNS_CHECK);
507
508                 g_object_unref(r);
509                 return FALSE;
510         } else {
511                 host = g_strdup(proxy_addr);
512                 if (host == NULL)
513                         goto cleanup;
514
515                 addr = strtok(host, ":");
516                 if (addr == NULL)
517                         goto cleanup;
518
519                 port = strrchr(proxy_addr, ':');
520                 if (port == NULL)
521                         goto cleanup;
522                 else {
523                         char *end;
524                         int tmp = strtol(port + 1, &end, 10);
525
526                         if (*end == '\0') {
527                                 *port = '\0';
528                                 net_params->port = tmp;
529                         }
530                 }
531                 g_free(net_params->addr);
532                 net_params->addr = g_strdup(addr);
533
534                 g_free(host);
535         }
536         return TRUE;
537
538 cleanup:
539         g_free(host);
540         netconfig_stop_internet_check();
541
542         return FALSE;
543 }
544
545 void netconfig_check_internet_accessibility(void)
546 {
547         DBG("::Entry");
548
549         if (net_params == NULL) {
550                 net_params = g_try_malloc0(sizeof(struct internet_params));
551                 if (net_params == NULL)
552                         return;
553                 net_params->fd = -1;
554         }
555
556         if ((check_state != INTERNET_CHECK_STATE_NONE) ||
557                         (net_params->request_started == TRUE)) {
558                 DBG("Older query in progress");
559                 return;
560         }
561
562         is_internet_available = FALSE;
563
564         /* If the host IP is resolved, directly go for connecting to sockets*/
565         if (__netconfig_obtain_host_ip_addr() == TRUE) {
566                 __netconfig_connect_sockets();
567         }
568 }