From: Marcel Holtmann Date: Sun, 14 Dec 2008 22:53:44 +0000 (+0100) Subject: Add DNS proxy/multiplexer implementation X-Git-Tag: accepted/2.0alpha-wayland/20121110.002834~4441 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e0abb552e9b2759f0ec7324d1d040a83a1249f47;p=profile%2Fivi%2Fconnman.git Add DNS proxy/multiplexer implementation --- diff --git a/plugins/dnsproxy.c b/plugins/dnsproxy.c index 419d33d..bc61d51 100644 --- a/plugins/dnsproxy.c +++ b/plugins/dnsproxy.c @@ -23,22 +23,206 @@ #include #endif +#include +#include +#include +#include +#include + #include #include #include +#include + +struct server_data { + char *interface; + char *server; + GIOChannel *channel; + guint watch; +}; + +struct request_data { + struct sockaddr_in sin; + socklen_t len; + guint16 id; +}; + +static GSList *server_list = NULL; +static GSList *request_list = NULL; + +static GIOChannel *listener_channel = NULL; +static guint listener_watch = 0; + +static struct request_data *find_request(guint16 id) +{ + GSList *list; + + for (list = request_list; list; list = list->next) { + struct request_data *data = list->data; + + if (data->id == id) + return data; + } + + return NULL; +} + +static struct server_data *find_server(const char *interface, + const char *server) +{ + GSList *list; + + DBG("interface %s server %s", interface, server); + + for (list = server_list; list; list = list->next) { + struct server_data *data = list->data; + + if (data->interface == NULL || data->server == NULL) + continue; + + if (g_str_equal(data->interface, interface) == TRUE && + g_str_equal(data->server, server) == TRUE) + return data; + } + + return NULL; +} + +static gboolean server_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + struct request_data *req; + unsigned char buf[768]; + int sk, err, len; + + sk = g_io_channel_unix_get_fd(channel); + + len = recv(sk, buf, sizeof(buf), 0); + if (len < 2) + return TRUE; + + DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8); + + req = find_request(buf[0] | buf[1] << 8); + if (req == NULL) + return TRUE; + + request_list = g_slist_remove(request_list, req); + + sk = g_io_channel_unix_get_fd(listener_channel); + + err = sendto(sk, buf, len, 0, (struct sockaddr *) &req->sin, req->len); + + g_free(req); + + return TRUE; +} + +static struct server_data *create_server(const char *interface, + const char *server) +{ + struct server_data *data; + struct sockaddr_in sin; + int sk; + + DBG("interface %s server %s", interface, server); + + sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sk < 0) { + connman_error("Failed to create server %s socket", server); + return NULL; + } + + if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, + interface, strlen(interface) + 1) < 0) { + connman_error("Failed to bind server %s to interface %s", + server, interface); + close(sk); + return NULL; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(53); + sin.sin_addr.s_addr = inet_addr(server); + + if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + connman_error("Failed to connect server %s", server); + close(sk); + return NULL; + } + + data = g_try_new0(struct server_data, 1); + if (data == NULL) { + connman_error("Failed to allocate server %s data", server); + close(sk); + return NULL; + } + + data->channel = g_io_channel_unix_new(sk); + if (data->channel == NULL) { + connman_error("Failed to create server %s channel", server); + close(sk); + g_free(data); + return NULL; + } + + g_io_channel_set_close_on_unref(data->channel, TRUE); + + data->watch = g_io_add_watch(data->channel, G_IO_IN, + server_event, data); + + data->interface = g_strdup(interface); + data->server = g_strdup(server); + + return data; +} + +static void destroy_server(struct server_data *data) +{ + DBG("interface %s server %s", data->interface, data->server); + + if (data->watch > 0) + g_source_remove(data->watch); + + g_io_channel_unref(data->channel); + + g_free(data->interface); + g_free(data->server); + g_free(data); +} + static int dnsproxy_append(const char *interface, const char *domain, const char *server) { - DBG("server %s", server); + struct server_data *data; + + DBG("interface %s server %s", interface, server); + + data = create_server(interface, server); + if (data == NULL) + return -EIO; - return -1; + server_list = g_slist_append(server_list, data); + + return 0; } static int dnsproxy_remove(const char *interface, const char *domain, const char *server) { - DBG("server %s", server); + struct server_data *data; + + DBG("interface %s server %s", interface, server); + + data = find_server(interface, server); + if (data == NULL) + return 0; + + server_list = g_slist_remove(server_list, data); + + destroy_server(data); return 0; } @@ -50,15 +234,154 @@ static struct connman_resolver dnsproxy_resolver = { .remove = dnsproxy_remove, }; +static gboolean listener_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + GSList *list; + unsigned char buf[768]; + struct request_data *req; + struct sockaddr_in sin; + socklen_t size = sizeof(sin); + int sk, err, len; + + sk = g_io_channel_unix_get_fd(channel); + + memset(&sin, 0, sizeof(sin)); + len = recvfrom(sk, buf, sizeof(buf), 0, + (struct sockaddr *) &sin, &size); + if (len < 2) + return TRUE; + + DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8); + + if (g_slist_length(server_list) == 0) + return TRUE; + + req = find_request(buf[0] | (buf[1] << 8)); + if (req == NULL) { + req = g_try_new0(struct request_data, 1); + if (req == NULL) + return TRUE; + + memcpy(&req->sin, &sin, sizeof(sin)); + req->len = size; + req->id = buf[0] | (buf[1] << 8); + + request_list = g_slist_append(request_list, req); + } else { + memcpy(&req->sin, &sin, sizeof(sin)); + req->len = size; + } + + for (list = server_list; list; list = list->next) { + struct server_data *data = list->data; + + sk = g_io_channel_unix_get_fd(data->channel); + + err = send(sk, buf, len, 0); + } + + return TRUE; +} + +static int create_listener(void) +{ + const char *ifname = "lo"; + struct sockaddr_in sin; + int sk; + + DBG(""); + + sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sk < 0) { + connman_error("Failed to create listener socket"); + return -EIO; + } + + //setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + //setsockopt(sk, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)); + + if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, + ifname, strlen(ifname) + 1) < 0) { + connman_error("Failed to bind listener interface"); + close(sk); + return -EIO; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(53); + sin.sin_addr.s_addr = inet_addr("127.0.0.1"); + //sin.sin_addr.s_addr = INADDR_ANY; + + if (bind(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + connman_error("Failed to bind listener socket"); + close(sk); + return -EIO; + } + + listener_channel = g_io_channel_unix_new(sk); + if (listener_channel == NULL) { + connman_error("Failed to create listener channel"); + close(sk); + return -EIO; + } + + g_io_channel_set_close_on_unref(listener_channel, TRUE); + + listener_watch = g_io_add_watch(listener_channel, G_IO_IN, + listener_event, NULL); + + return 0; +} + +static void destroy_listener(void) +{ + GSList *list; + + DBG(""); + + if (listener_watch > 0) + g_source_remove(listener_watch); + + for (list = request_list; list; list = list->next) { + struct request_data *data = list->data; + + DBG("Dropping request (id 0x%04x)", data->id); + + g_free(data); + list->data = NULL; + } + + g_slist_free(request_list); + request_list = NULL; + + g_io_channel_unref(listener_channel); +} + static int dnsproxy_init(void) { - return connman_resolver_register(&dnsproxy_resolver); + int err; + + err = create_listener(); + if (err < 0) + return err; + + err = connman_resolver_register(&dnsproxy_resolver); + if (err < 0) { + destroy_listener(); + return err; + } + + return 0; } static void dnsproxy_exit(void) { + destroy_listener(); + connman_resolver_unregister(&dnsproxy_resolver); } CONNMAN_PLUGIN_DEFINE(dnsproxy, "DNS proxy resolver plugin", VERSION, - dnsproxy_init, dnsproxy_exit) + dnsproxy_init, dnsproxy_exit)