From e41694929eaad433f9cb5af1a916af5b7a99eb98 Mon Sep 17 00:00:00 2001 From: Yu A Wang Date: Thu, 21 Apr 2011 03:26:05 -0400 Subject: [PATCH] dnsproxy: Add function __connman_dnsproxy_add/remove_listener() __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 | 2 + src/dnsproxy.c | 172 ++++++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 130 insertions(+), 44 deletions(-) diff --git a/src/connman.h b/src/connman.h index b79726f..aa18b3a 100644 --- a/src/connman.h +++ b/src/connman.h @@ -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); diff --git a/src/dnsproxy.c b/src/dnsproxy.c index 0b2ff22..1d32994 100644 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -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); } -- 2.7.4