dnsproxy: Add function __connman_dnsproxy_add/remove_listener()
authorYu A Wang <yu.a.wang@intel.com>
Thu, 21 Apr 2011 07:26:05 +0000 (03:26 -0400)
committerSamuel Ortiz <sameo@linux.intel.com>
Wed, 27 Apr 2011 08:33:29 +0000 (10:33 +0200)
__connman_dnsproxy_add_listener() can be used to add a listener
to DNS proxy, so, besides the listener bound to lo device, listener
bound to tether device can also be added. And tethering client can
use tether as DNS server.

src/connman.h
src/dnsproxy.c

index b79726f..aa18b3a 100644 (file)
@@ -681,6 +681,8 @@ int __connman_iptables_commit(const char *table_name);
 
 int __connman_dnsproxy_init(void);
 void __connman_dnsproxy_cleanup(void);
+int __connman_dnsproxy_add_listener(const char *interface);
+void __connman_dnsproxy_remove_listener(const char *interface);
 int __connman_dnsproxy_append(const char *interface, const char *domain, const char *server);
 int __connman_dnsproxy_remove(const char *interface, const char *domain, const char *server);
 void __connman_dnsproxy_flush(void);
index 0b2ff22..1d32994 100644 (file)
@@ -112,17 +112,22 @@ struct request_data {
        gpointer name;
        gpointer resp;
        gsize resplen;
+       struct listener_data *ifdata;
+};
+
+struct listener_data {
+       char *ifname;
+       GIOChannel *udp_listener_channel;
+       guint udp_listener_watch;
+       GIOChannel *tcp_listener_channel;
+       guint tcp_listener_watch;
 };
 
 static GSList *server_list = NULL;
 static GSList *request_list = NULL;
 static GSList *request_pending_list = NULL;
 static guint16 request_id = 0x0000;
-
-static GIOChannel *udp_listener_channel = NULL;
-static guint udp_listener_watch = 0;
-static GIOChannel *tcp_listener_channel = NULL;
-static guint tcp_listener_watch = 0;
+static GHashTable *listener_table = NULL;
 
 static int protocol_offset(int protocol)
 {
@@ -215,19 +220,22 @@ static void send_response(int sk, unsigned char *buf, int len,
 static gboolean request_timeout(gpointer user_data)
 {
        struct request_data *req = user_data;
+       struct listener_data *ifdata;
 
        DBG("id 0x%04x", req->srcid);
 
        if (req == NULL)
                return FALSE;
 
+       ifdata = req->ifdata;
+
        request_list = g_slist_remove(request_list, req);
        req->numserv--;
 
        if (req->resplen > 0 && req->resp != NULL) {
                int sk, err;
 
-               sk = g_io_channel_unix_get_fd(udp_listener_channel);
+               sk = g_io_channel_unix_get_fd(ifdata->udp_listener_channel);
 
                err = sendto(sk, req->resp, req->resplen, 0,
                             &req->sa, req->sa_len);
@@ -245,7 +253,8 @@ static gboolean request_timeout(gpointer user_data)
 
                        hdr = (void *) (req->request);
                        hdr->id = req->srcid;
-                       sk = g_io_channel_unix_get_fd(udp_listener_channel);
+                       sk = g_io_channel_unix_get_fd(
+                                               ifdata->udp_listener_channel);
                        send_response(sk, req->request, req->request_len,
                                      &req->sa, req->sa_len, IPPROTO_UDP);
                }
@@ -386,6 +395,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol)
        struct domain_hdr *hdr;
        struct request_data *req;
        int dns_id, sk, err, offset = protocol_offset(protocol);
+       struct listener_data *ifdata;
 
        if (offset < 0)
                return offset;
@@ -401,6 +411,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol)
 
        DBG("id 0x%04x rcode %d", hdr->id, hdr->rcode);
 
+       ifdata = req->ifdata;
+
        reply[offset] = req->srcid & 0xff;
        reply[offset + 1] = req->srcid >> 8;
 
@@ -427,7 +439,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol)
        request_list = g_slist_remove(request_list, req);
 
        if (protocol == IPPROTO_UDP) {
-               sk = g_io_channel_unix_get_fd(udp_listener_channel);
+               sk = g_io_channel_unix_get_fd(ifdata->udp_listener_channel);
                err = sendto(sk, req->resp, req->resplen, 0,
                             &req->sa, req->sa_len);
        } else {
@@ -1095,13 +1107,14 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
        struct sockaddr_in6 client_addr;
        socklen_t client_addr_len = sizeof(client_addr);
        GSList *list;
+       struct listener_data *ifdata = user_data;
 
        DBG("condition 0x%x", condition);
 
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               if (tcp_listener_watch > 0)
-                       g_source_remove(tcp_listener_watch);
-               tcp_listener_watch = 0;
+               if (ifdata->tcp_listener_watch > 0)
+                       g_source_remove(ifdata->tcp_listener_watch);
+               ifdata->tcp_listener_watch = 0;
 
                connman_error("Error with TCP listener channel");
 
@@ -1113,7 +1126,7 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
        client_sk = accept(sk, (void *)&client_addr, &client_addr_len);
        if (client_sk < 0) {
                connman_error("Accept failure on TCP listener");
-               tcp_listener_watch = 0;
+               ifdata->tcp_listener_watch = 0;
                return FALSE;
        }
 
@@ -1151,6 +1164,7 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
        buf[3] = req->dstid >> 8;
 
        req->numserv = 0;
+       req->ifdata = (struct listener_data *) ifdata;
        request_list = g_slist_append(request_list, req);
 
        for (list = server_list; list; list = list->next) {
@@ -1216,10 +1230,11 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition,
        struct sockaddr_in6 client_addr;
        socklen_t client_addr_len = sizeof(client_addr);
        int sk, err, len;
+       struct listener_data *ifdata = user_data;
 
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
                connman_error("Error with UDP listener channel");
-               udp_listener_watch = 0;
+               ifdata->udp_listener_watch = 0;
                return FALSE;
        }
 
@@ -1262,16 +1277,17 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition,
        buf[1] = req->dstid >> 8;
 
        req->numserv = 0;
+       req->ifdata = (struct listener_data *) ifdata;
        req->timeout = g_timeout_add_seconds(5, request_timeout, req);
        request_list = g_slist_append(request_list, req);
 
        return resolv(req, buf, query);
 }
 
-static int create_dns_listener(int protocol)
+static int create_dns_listener(int protocol, const char *ifname)
 {
        GIOChannel *channel;
-       const char *ifname = "lo", *proto;
+       const char *proto;
        union {
                struct sockaddr sa;
                struct sockaddr_in6 sin6;
@@ -1280,8 +1296,13 @@ static int create_dns_listener(int protocol)
        socklen_t slen;
        int sk, type, v6only = 0;
        int family = AF_INET6;
+       struct listener_data *ifdata;
 
-       DBG("");
+       DBG("interface %s", ifname);
+
+       ifdata = g_hash_table_lookup(listener_table, ifname);
+       if (ifdata == NULL)
+               return -ENODEV;
 
        switch (protocol) {
        case IPPROTO_UDP:
@@ -1361,62 +1382,79 @@ static int create_dns_listener(int protocol)
        g_io_channel_set_close_on_unref(channel, TRUE);
 
        if (protocol == IPPROTO_TCP) {
-               tcp_listener_channel = channel;
-               tcp_listener_watch = g_io_add_watch(channel,
-                                       G_IO_IN, tcp_listener_event, NULL);
+               ifdata->tcp_listener_channel = channel;
+               ifdata->tcp_listener_watch = g_io_add_watch(channel,
+                               G_IO_IN, tcp_listener_event, (gpointer) ifdata);
        } else {
-               udp_listener_channel = channel;
-               udp_listener_watch = g_io_add_watch(channel,
-                                       G_IO_IN, udp_listener_event, NULL);
+               ifdata->udp_listener_channel = channel;
+               ifdata->udp_listener_watch = g_io_add_watch(channel,
+                               G_IO_IN, udp_listener_event, (gpointer) ifdata);
        }
 
        return 0;
 }
 
-static void destroy_udp_listener(void)
+static void destroy_udp_listener(const char *interface)
 {
-       DBG("");
+       struct listener_data *ifdata;
 
-       if (udp_listener_watch > 0)
-               g_source_remove(udp_listener_watch);
+       DBG("interface %s", interface);
+
+       ifdata = g_hash_table_lookup(listener_table, interface);
+       if (ifdata == NULL)
+               return;
 
-       g_io_channel_unref(udp_listener_channel);
+       if (ifdata->udp_listener_watch > 0)
+               g_source_remove(ifdata->udp_listener_watch);
+
+       g_io_channel_unref(ifdata->udp_listener_channel);
 }
 
-static void destroy_tcp_listener(void)
+static void destroy_tcp_listener(const char *interface)
 {
-       DBG("");
+       struct listener_data *ifdata;
+
+       DBG("interface %s", interface);
+
+       ifdata = g_hash_table_lookup(listener_table, interface);
+       if (ifdata == NULL)
+               return;
 
-       if (tcp_listener_watch > 0)
-               g_source_remove(tcp_listener_watch);
+       if (ifdata->tcp_listener_watch > 0)
+               g_source_remove(ifdata->tcp_listener_watch);
 
-       g_io_channel_unref(tcp_listener_channel);
+       g_io_channel_unref(ifdata->tcp_listener_channel);
 }
 
-static int create_listener(void)
+static int create_listener(const char *interface)
 {
        int err;
 
-       err = create_dns_listener(IPPROTO_UDP);
+       err = create_dns_listener(IPPROTO_UDP, interface);
        if (err < 0)
                return err;
 
-       err = create_dns_listener(IPPROTO_TCP);
+       err = create_dns_listener(IPPROTO_TCP, interface);
        if (err < 0) {
-               destroy_udp_listener();
+               destroy_udp_listener(interface);
                return err;
        }
 
-       __connman_resolvfile_append("lo", NULL, "127.0.0.1");
+       if (g_strcmp0(interface, "lo") == 0)
+               __connman_resolvfile_append("lo", NULL, "127.0.0.1");
 
        return 0;
 }
 
-static void destroy_listener(void)
+static void destroy_listener(const char *interface)
 {
        GSList *list;
 
-       __connman_resolvfile_remove("lo", NULL, "127.0.0.1");
+       if (interface == NULL)
+               return;
+
+       if (g_strcmp0(interface, "lo") == 0)
+               __connman_resolvfile_remove("lo", NULL, "127.0.0.1");
 
        for (list = request_pending_list; list; list = list->next) {
                struct request_data *req = list->data;
@@ -1450,8 +1488,49 @@ static void destroy_listener(void)
        g_slist_free(request_list);
        request_list = NULL;
 
-       destroy_tcp_listener();
-       destroy_udp_listener();
+       destroy_tcp_listener(interface);
+       destroy_udp_listener(interface);
+}
+
+int __connman_dnsproxy_add_listener(const char *interface)
+{
+       struct listener_data *ifdata;
+       int err;
+
+       DBG("interface %s", interface);
+
+       if (g_hash_table_lookup(listener_table, interface) != NULL)
+               return 0;
+
+       ifdata = g_try_new0(struct listener_data, 1);
+       if (ifdata == NULL)
+               return -ENOMEM;
+
+       ifdata->ifname = g_strdup(interface);
+       ifdata->udp_listener_channel = NULL;
+       ifdata->udp_listener_watch = 0;
+       ifdata->tcp_listener_channel = NULL;
+       ifdata->tcp_listener_watch = 0;
+       g_hash_table_insert(listener_table, ifdata->ifname, ifdata);
+
+       err = create_listener(interface);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+void __connman_dnsproxy_remove_listener(const char *interface)
+{
+       DBG("interface %s", interface);
+
+       destroy_listener(interface);
+
+       g_hash_table_remove(listener_table, interface);
+}
+
+static void remove_listener(gpointer key, gpointer value, gpointer user_data)
+{
+       __connman_dnsproxy_remove_listener(key);
 }
 
 int __connman_dnsproxy_init(void)
@@ -1460,7 +1539,9 @@ int __connman_dnsproxy_init(void)
 
        DBG("");
 
-       err = create_listener();
+       listener_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, g_free);
+       err = __connman_dnsproxy_add_listener("lo");
        if (err < 0)
                return err;
 
@@ -1471,7 +1552,8 @@ int __connman_dnsproxy_init(void)
        return 0;
 
 destroy:
-       destroy_listener();
+       __connman_dnsproxy_remove_listener("lo");
+       g_hash_table_destroy(listener_table);
 
        return err;
 }
@@ -1482,5 +1564,7 @@ void __connman_dnsproxy_cleanup(void)
 
        connman_notifier_unregister(&dnsproxy_notifier);
 
-       destroy_listener();
+       g_hash_table_foreach(listener_table, remove_listener, NULL);
+
+       g_hash_table_destroy(listener_table);
 }