2 * Internet-accessibility check
4 * Copyright 2012 Samsung Electronics Co., Ltd
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
10 * http://www.tizenopensource.org/license
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.
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>
42 #include "wifi-agent.h"
43 #include "netsupplicant.h"
44 #include "network-state.h"
45 #include "network-accessibility.h"
48 #define NETCONFIG_INTERNET_CHECK_TIMEOUT 3
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
56 struct internet_params {
60 guint transport_watch;
63 gboolean request_started;
66 static const char* url_list[] = {
75 #define URL_LIST_NUM 6
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;
87 static GCancellable *cancellable;
89 static void __netconfig_connect_sockets(void);
90 static void __internet_check_state(enum netconfig_internet_check_state state);
92 gboolean netconfig_get_internet_status()
94 return is_internet_available;
97 static void __netconfig_update_internet_status(unsigned char *reply)
99 /* If the HTTP response is either 302 or 200 with redirection,
100 * then no Internet is available */
102 is_internet_available = FALSE;
103 char *saveptr = NULL;
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) {
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: "));
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;
126 is_internet_available = TRUE;
128 g_free(redirect_url1);
129 g_free(redirect_url2);
130 redirect_url1 = NULL;
131 redirect_url2 = NULL;
135 is_internet_available = TRUE;
136 DBG("Internet is available");
140 if (is_internet_available == TRUE)
141 netconfig_send_notification_to_net_popup(NETCONFIG_DEL_PORTAL_NOTI, NULL);
144 static gboolean __netconfig_data_activity_timeout(gpointer data)
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);
150 if (net_params == NULL)
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();
161 perform_recheck = TRUE;
162 __internet_check_state(INTERNET_CHECK_STATE_NONE);
168 static void __netconfig_internet_check_timer_stop(void)
171 netconfig_stop_timer(&timer_id);
174 static void __netconfig_internet_check_timer_start(enum netconfig_internet_check_state state)
176 static guint timeout = 0;
178 DBG("netconfig_data_activity_timer is already running, so stop it");
179 __netconfig_internet_check_timer_stop();
182 if (state == INTERNET_CHECK_STATE_NONE)
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;
189 netconfig_start_timer_seconds(timeout,
190 __netconfig_data_activity_timeout,
191 GINT_TO_POINTER(state),
195 static void __internet_check_state(enum netconfig_internet_check_state state)
197 enum netconfig_internet_check_state prev_state = check_state;
199 if (prev_state == state)
202 ERR("state change (%d) -> (%d)", prev_state, state);
206 case INTERNET_CHECK_STATE_DNS_CHECK:
207 __netconfig_internet_check_timer_start(state);
209 case INTERNET_CHECK_STATE_PACKET_CHECK:
210 if (prev_state == INTERNET_CHECK_STATE_DNS_CHECK)
211 __netconfig_internet_check_timer_stop();
213 __netconfig_internet_check_timer_start(state);
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();
229 static gboolean __received_data_event(GIOChannel *channel,
230 GIOCondition condition, gpointer data)
233 unsigned char buf[BUF_SIZE] = { 0, };
234 char error_buf[MAX_SIZE_ERROR_BUFFER] = {0, };
236 if (net_params == NULL)
239 if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
242 fd = g_io_channel_unix_get_fd(channel);
246 n = read(fd, buf, BUF_SIZE - 1);
247 DBG("Received %d bytes[%s]", n, buf);
248 buf[BUF_SIZE - 1] = '\0';
251 ERR("read failed. %s",
252 strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER));
256 INFO("connection closed");
261 /* We got data from server successfully */
262 __netconfig_update_internet_status(buf);
263 __internet_check_state(INTERNET_CHECK_STATE_NONE);
268 /* Fail to get data from server */
269 __internet_check_state(INTERNET_CHECK_STATE_NONE);
274 static gboolean __send_data_event(GIOChannel *channel,
275 GIOCondition condition, gpointer data)
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";
282 if (net_params == NULL)
285 if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
288 fd = g_io_channel_unix_get_fd(channel);
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)
297 n = send(fd, request_data, strlen(request_data), MSG_NOSIGNAL);
298 DBG("Sent %d bytes", n);
301 ERR("send failed. %s",
302 strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER));
306 INFO("connection closed");
311 net_params->header_done = TRUE;
315 __internet_check_state(INTERNET_CHECK_STATE_NONE);
320 static void __netconfig_connect_sockets(void)
323 struct sockaddr_in addr;
324 GIOChannel *channel = NULL;
325 const char *ifname = NULL;
328 if (net_params == NULL || net_params->addr == NULL)
331 sock = socket(PF_INET, SOCK_STREAM, 0);
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");
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);
349 channel = g_io_channel_unix_new(sock);
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);
356 if (connect(sock, (struct sockaddr *)&addr,
357 sizeof(struct sockaddr_in)) < 0) {
358 if (errno != EINPROGRESS) {
359 INFO("connect fail");
365 DBG("Connect successful");
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);
375 __internet_check_state(INTERNET_CHECK_STATE_PACKET_CHECK);
379 __internet_check_state(INTERNET_CHECK_STATE_NONE);
382 static void __netconfig_obtain_host_ip_addr_cb(GObject *src,
389 GError *error = NULL;
391 if (net_params == NULL)
394 if (check_state == INTERNET_CHECK_STATE_NONE)
397 list = g_resolver_lookup_by_name_finish((GResolver *)src, res, &error);
399 if (error->code == G_IO_ERROR_CANCELLED)
400 ERR("G_IO_ERROR_CANCELLED is called[%s]", error->message);
409 for (cur = list; cur; cur = cur->next) {
411 str_addr = g_inet_address_to_string(addr);
415 if (net_params != NULL) {
416 g_free(net_params->addr);
417 net_params->addr = str_addr;
420 g_object_unref(cur->data);
426 if (net_params->addr == NULL)
429 net_params->port = 80;
430 __netconfig_connect_sockets();
435 __internet_check_state(INTERNET_CHECK_STATE_NONE);
438 gboolean __netconfig_obtain_host_ip_addr(void)
440 char *host, *addr, *port;
441 char *saveptr = NULL;
443 if (net_params == NULL)
446 if (net_params->request_started == TRUE)
449 net_params->request_started = TRUE;
451 if (net_params->addr != NULL)
454 proxy_addr = netconfig_get_default_proxy();
455 DBG("Proxy(%s)", proxy_addr);
457 if (++url_index >= URL_LIST_NUM)
460 DBG("addr (%s)", url_list[url_index]);
462 /* FIXME: domain proxy should be resolved */
463 if (proxy_addr == NULL) {
465 r = g_resolver_get_default();
467 g_resolver_lookup_by_name_async(r,
470 __netconfig_obtain_host_ip_addr_cb,
472 __internet_check_state(INTERNET_CHECK_STATE_DNS_CHECK);
477 host = g_strdup(proxy_addr);
481 addr = strtok_r(host, ":", &saveptr);
485 port = strrchr(proxy_addr, ':');
490 int tmp = strtol(port + 1, &end, 10);
494 net_params->port = tmp;
497 g_free(net_params->addr);
498 net_params->addr = g_strdup(addr);
506 netconfig_stop_internet_check();
511 void netconfig_check_internet_accessibility(void)
515 if (net_params == NULL) {
516 net_params = g_try_malloc0(sizeof(struct internet_params));
517 if (net_params == NULL)
522 if ((check_state != INTERNET_CHECK_STATE_NONE) || (net_params->request_started == TRUE)) {
523 DBG("Older query in progress");
527 is_internet_available = FALSE;
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();
534 void netconfig_stop_internet_check(void)
536 if (net_params == NULL)
539 net_params->header_done = FALSE;
540 net_params->request_started = FALSE;
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");
548 if (net_params->transport_watch > 0) {
549 g_source_remove(net_params->transport_watch);
550 net_params->transport_watch = 0;
553 if (net_params->send_watch > 0) {
554 g_source_remove(net_params->send_watch);
555 net_params->send_watch = 0;
558 if (net_params->fd > 0) {
559 close(net_params->fd);
563 if (net_params->addr != NULL) {
564 g_free(net_params->addr);
565 net_params->addr = NULL;
572 g_free(redirect_url1);
573 redirect_url1 = NULL;
577 g_free(redirect_url2);
578 redirect_url2 = NULL;
582 void netconfig_internet_accessibility_init(void)
584 cancellable = g_cancellable_new();
587 void netconfig_internet_accessibility_deinit(void)
589 g_object_unref(cancellable);