Sync with Tizen 2.4(v1.1.38)
[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 const static 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
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 = (enum netconfig_internet_check_state)GPOINTER_TO_INT(data);
147         INFO("Prev_state: state=%d (1:dns check / 2:packet check)",prev_state);
148
149         if (net_params == NULL)
150                 return FALSE;
151
152         if (TRUE == perform_recheck && prev_state != INTERNET_CHECK_STATE_NONE) {
153                 perform_recheck = FALSE;
154                 if (prev_state == INTERNET_CHECK_STATE_DNS_CHECK) {
155                         net_params->request_started = FALSE;
156                         netconfig_check_internet_accessibility();
157                 } else /* (state == NETCONFIG_DATA_ACTIVITY_STATE_PACKET_CHECK) */
158                         __netconfig_connect_sockets();
159         } else {
160                 perform_recheck = TRUE;
161                 __internet_check_state(INTERNET_CHECK_STATE_NONE);
162         }
163
164         return FALSE;
165 }
166
167 static void __netconfig_internet_check_timer_stop(void)
168 {
169         if (timer_id != 0)
170                 netconfig_stop_timer(&timer_id);
171 }
172
173 static void __netconfig_internet_check_timer_start(enum netconfig_internet_check_state state)
174 {
175         static guint timeout = 0;
176         if (timer_id != 0) {
177                 DBG("netconfig_data_activity_timer is already running, so stop it");
178                 __netconfig_internet_check_timer_stop();
179         }
180
181         if (state == INTERNET_CHECK_STATE_NONE)
182                 return;
183         else if (state == INTERNET_CHECK_STATE_DNS_CHECK)
184                 timeout = NETCONFIG_INTERNET_CHECK_TIMEOUT;
185         else if (state == INTERNET_CHECK_STATE_PACKET_CHECK)
186                 timeout = NETCONFIG_INTERNET_CHECK_TIMEOUT;
187
188         netconfig_start_timer_seconds(timeout,
189                         __netconfig_data_activity_timeout,
190                         GINT_TO_POINTER(state),
191                         &timer_id);
192 }
193
194 static void __internet_check_state(enum netconfig_internet_check_state state)
195 {
196         enum netconfig_internet_check_state prev_state = check_state;
197
198         if (prev_state == state)
199                 return;
200
201         ERR("state change (%d) -> (%d)", prev_state, state);
202         check_state = state;
203
204         switch (state) {
205         case INTERNET_CHECK_STATE_DNS_CHECK:
206                 __netconfig_internet_check_timer_start(state);
207                 break;
208         case INTERNET_CHECK_STATE_PACKET_CHECK:
209                 if (prev_state == INTERNET_CHECK_STATE_DNS_CHECK)
210                         __netconfig_internet_check_timer_stop();
211
212                 __netconfig_internet_check_timer_start(state);
213                 break;
214         case INTERNET_CHECK_STATE_NONE:
215                 switch (prev_state) {
216                 case INTERNET_CHECK_STATE_DNS_CHECK:
217                 case INTERNET_CHECK_STATE_PACKET_CHECK:
218                         __netconfig_internet_check_timer_stop();
219                         netconfig_stop_internet_check();
220                         break;
221                 default:
222                         break;
223                 }
224                 break;
225         }
226 }
227
228 static gboolean __received_data_event(GIOChannel *channel,
229                 GIOCondition condition, gpointer data)
230 {
231         int n, fd;
232         unsigned char buf[BUF_SIZE] = { 0, };
233
234         if (net_params == NULL)
235                 return FALSE;
236
237         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
238                 goto cleanup;
239
240         fd = g_io_channel_unix_get_fd(channel);
241         if (fd < 0)
242                 goto cleanup;
243
244         n = read(fd, buf, BUF_SIZE - 1);
245         DBG("Received %d bytes[%s]", n, buf);
246         buf[BUF_SIZE - 1] = '\0';
247
248         if (n < 0) {
249                 ERR("read failed. %s", strerror(errno));
250
251                 goto cleanup;
252         } else if (n == 0) {
253                 INFO("connection closed");
254
255                 goto cleanup;
256         }
257
258         /* We got data from server successfully */
259         __netconfig_update_internet_status(buf);
260         __internet_check_state(INTERNET_CHECK_STATE_NONE);
261
262         return TRUE;
263
264 cleanup:
265         /* Fail to get data from server */
266         __internet_check_state(INTERNET_CHECK_STATE_NONE);
267
268         return FALSE;
269 }
270
271 static gboolean __send_data_event(GIOChannel *channel,
272                 GIOCondition condition, gpointer data)
273 {
274         int n, fd;
275         const char *request_data =
276                         "GET /index.html HTTP/1.1\r\nHost: connman.net\r\n\r\n";
277
278         if (net_params == NULL)
279                 return FALSE;
280
281         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
282                 goto cleanup;
283
284         fd = g_io_channel_unix_get_fd(channel);
285         if (fd < 0)
286                 goto cleanup;
287
288         /* We don't need to send anymore. Just return here.*/
289         /* Socket will be closed received part*/
290         if (net_params->header_done == TRUE)
291                 return FALSE;
292
293         n = send(fd, request_data, strlen(request_data), 0);
294         DBG("Sent %d bytes", n);
295
296         if (n < 0) {
297                 ERR("send failed. %s", strerror(errno));
298
299                 goto cleanup;
300         } else if (n == 0) {
301                 INFO("connection closed");
302
303                 goto cleanup;
304         }
305
306         net_params->header_done = TRUE;
307         return TRUE;
308
309 cleanup:
310         __internet_check_state(INTERNET_CHECK_STATE_NONE);
311
312         return FALSE;
313 }
314
315 static void __netconfig_connect_sockets(void)
316 {
317         GIOFlags flags;
318         struct sockaddr_in addr;
319         GIOChannel *channel = NULL;
320         int sock;
321
322         if (net_params == NULL || net_params->addr == NULL)
323                 return;
324
325         sock = socket(PF_INET, SOCK_STREAM, 0);
326         if (sock < 0)
327                 goto cleanup;
328
329         if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
330                         WIFI_IFNAME, strlen(WIFI_IFNAME) + 1) < 0) {
331                 ERR("Bind to device error");
332                 close(sock);
333                 goto cleanup;
334         }
335
336         memset(&addr, 0, sizeof(struct sockaddr_in));
337         addr.sin_family = AF_INET;
338         addr.sin_addr.s_addr = inet_addr(net_params->addr);
339         addr.sin_port = htons(net_params->port);
340
341         /* Register Watch */
342         channel = g_io_channel_unix_new(sock);
343
344         flags = g_io_channel_get_flags(channel);
345         g_io_channel_set_flags(channel, flags | G_IO_FLAG_NONBLOCK, NULL);
346         g_io_channel_set_encoding(channel, NULL, NULL);
347         g_io_channel_set_buffered(channel, FALSE);
348
349         if (connect(sock, (struct sockaddr *)&addr,
350                         sizeof(struct sockaddr_in)) < 0) {
351                 if (errno != EINPROGRESS) {
352                         INFO("connect fail");
353                         close(sock);
354                         goto cleanup;
355                 }
356         }
357
358         DBG("Connect successful");
359
360         net_params->fd = sock;
361         net_params->transport_watch = g_io_add_watch(channel,
362                         (GIOCondition) (G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR),
363                         (GIOFunc) __received_data_event, NULL);
364         net_params->send_watch = g_io_add_watch(channel,
365                         (GIOCondition) (G_IO_OUT | G_IO_HUP | G_IO_NVAL | G_IO_ERR),
366                         (GIOFunc) __send_data_event, NULL);
367
368         __internet_check_state(INTERNET_CHECK_STATE_PACKET_CHECK);
369         return;
370
371         cleanup:
372         __internet_check_state(INTERNET_CHECK_STATE_NONE);
373 }
374
375 static void __netconfig_obtain_host_ip_addr_cb(GObject *src,
376                 GAsyncResult *res,
377                 gpointer user_data)
378 {
379         GList *list, *cur;
380         GInetAddress *addr;
381         gchar *str_addr;
382         GError *error = NULL;
383
384         if (net_params == NULL)
385                 return;
386
387         if (check_state == INTERNET_CHECK_STATE_NONE)
388                 return;
389
390         list = g_resolver_lookup_by_name_finish((GResolver *)src, res, &error);
391         if (error != NULL) {
392                 if (error->code == G_IO_ERROR_CANCELLED) {
393                         ERR("G_IO_ERROR_CANCELLED is called[%s]", error->message);
394                 }
395                 g_error_free(error);
396         }
397
398         if (!list) {
399                 INFO("no data");
400                 goto cleanup;
401         }
402
403         for (cur = list; cur; cur = cur->next) {
404                 addr = cur->data;
405                 str_addr = g_inet_address_to_string(addr);
406                 if (!str_addr)
407                         continue;
408
409                 if (net_params != NULL) {
410                         g_free(net_params->addr);
411                         net_params->addr = str_addr;
412                 }
413
414                 g_object_unref(cur->data);
415                 break;
416         }
417
418         g_list_free(list);
419
420         if (net_params->addr == NULL)
421                 goto cleanup;
422
423         net_params->port = 80;
424         __netconfig_connect_sockets();
425
426         return;
427
428 cleanup:
429         __internet_check_state(INTERNET_CHECK_STATE_NONE);
430 }
431
432 gboolean __netconfig_obtain_host_ip_addr(void)
433 {
434         char *host, *addr, *port;
435
436         if (net_params == NULL)
437                 return FALSE;
438
439         if (net_params->request_started == TRUE)
440                 return FALSE;
441         else
442                 net_params->request_started = TRUE;
443
444         if (net_params->addr != NULL)
445                 return TRUE;
446
447         proxy_addr = netconfig_get_default_proxy();
448         DBG("Proxy(%s)", proxy_addr);
449
450         if (++url_index >= URL_LIST_NUM)
451                 url_index = 0;
452
453         DBG("addr (%s)", url_list[url_index]);
454
455         /* FIXME: domain proxy should be resolved */
456         if (proxy_addr == NULL) {
457                 GResolver *r = NULL;
458                 r = g_resolver_get_default();
459
460                 g_resolver_lookup_by_name_async(r,
461                                 url_list[url_index],
462                                 cancellable,
463                                 __netconfig_obtain_host_ip_addr_cb,
464                                 NULL);
465                 __internet_check_state(INTERNET_CHECK_STATE_DNS_CHECK);
466
467                 g_object_unref(r);
468                 return FALSE;
469         } else {
470                 host = g_strdup(proxy_addr);
471                 if (host == NULL)
472                         goto cleanup;
473
474                 addr = strtok(host, ":");
475                 if (addr == NULL)
476                         goto cleanup;
477
478                 port = strrchr(proxy_addr, ':');
479                 if (port == NULL)
480                         goto cleanup;
481                 else {
482                         char *end;
483                         int tmp = strtol(port + 1, &end, 10);
484
485                         if (*end == '\0') {
486                                 *port = '\0';
487                                 net_params->port = tmp;
488                         }
489                 }
490                 g_free(net_params->addr);
491                 net_params->addr = g_strdup(addr);
492
493                 g_free(host);
494         }
495         return TRUE;
496
497 cleanup:
498         g_free(host);
499         netconfig_stop_internet_check();
500
501         return FALSE;
502 }
503
504 void netconfig_check_internet_accessibility(void)
505 {
506         ERR("::Entry");
507
508         if (net_params == NULL) {
509                 net_params = g_try_malloc0(sizeof(struct internet_params));
510                 if (net_params == NULL)
511                         return;
512                 net_params->fd = -1;
513         }
514
515         if ((check_state != INTERNET_CHECK_STATE_NONE) || (net_params->request_started == TRUE)) {
516                 DBG("Older query in progress");
517                 return;
518         }
519
520         is_internet_available = FALSE;
521
522         /* If the host IP is resolved, directly go for connecting to sockets*/
523         if (__netconfig_obtain_host_ip_addr() == TRUE) {
524                 __netconfig_connect_sockets();
525         }
526 }
527
528 void netconfig_stop_internet_check(void)
529 {
530         if (net_params == NULL)
531                 return;
532
533         net_params->header_done = FALSE;
534         net_params->request_started = FALSE;
535
536         if (g_cancellable_is_cancelled(cancellable) == FALSE) {
537                 g_cancellable_cancel(cancellable);
538                 ERR("g_cancellable_cancel is called and return stop_internet_check");
539                 return;
540         }
541
542         if (net_params->transport_watch > 0) {
543                 g_source_remove(net_params->transport_watch);
544                 net_params->transport_watch = 0;
545         }
546
547         if (net_params->send_watch > 0) {
548                 g_source_remove(net_params->send_watch);
549                 net_params->send_watch = 0;
550         }
551
552         if (net_params->fd > 0) {
553                 close(net_params->fd);
554                 net_params->fd = -1;
555         }
556
557         if (net_params->addr != NULL) {
558                 g_free(net_params->addr);
559                 net_params->addr = NULL;
560         }
561
562         g_free(net_params);
563         net_params = NULL;
564
565         if (redirect_url1) {
566                 g_free(redirect_url1);
567                 redirect_url1 = NULL;
568         }
569
570         if (redirect_url2) {
571                 g_free(redirect_url2);
572                 redirect_url2 = NULL;
573         }
574 }
575
576 void netconfig_internet_accessibility_init(void)
577 {
578         cancellable = g_cancellable_new();
579 }
580
581 void netconfig_internet_accessibility_deinit(void)
582 {
583         g_object_unref(cancellable);
584 }