diff options
author | Samuel Ortiz <sameo@linux.intel.com> | 2010-09-24 16:09:31 +0200 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2010-09-24 16:10:46 +0200 |
commit | d8071434ce6e22833eab381e13b09cfcc1d04522 (patch) | |
tree | a2fce101dd6ffb7051cee7576df76510f7b3a61c /plugins | |
parent | 3cf717c530a89cfd2ce50fe46e020fdf57ee7715 (diff) | |
download | connman-d8071434ce6e22833eab381e13b09cfcc1d04522.tar.gz connman-d8071434ce6e22833eab381e13b09cfcc1d04522.tar.bz2 connman-d8071434ce6e22833eab381e13b09cfcc1d04522.zip |
Initial DNS over TCP implementation for dnsproxy
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/dnsproxy.c | 817 |
1 files changed, 604 insertions, 213 deletions
diff --git a/plugins/dnsproxy.c b/plugins/dnsproxy.c index 04cb2ac2..a7f9be6b 100644 --- a/plugins/dnsproxy.c +++ b/plugins/dnsproxy.c @@ -79,18 +79,23 @@ struct server_data { char *interface; char *domain; char *server; + int protocol; GIOChannel *channel; guint watch; + guint timeout; gboolean enabled; + gboolean connected; }; struct request_data { struct sockaddr_in sin; + int client_sk; socklen_t len; guint16 srcid; guint16 dstid; guint16 altid; guint timeout; + guint watch; guint numserv; guint numresp; gpointer request; @@ -105,8 +110,10 @@ static GSList *request_list = NULL; static GSList *request_pending_list = NULL; static guint16 request_id = 0x0000; -static GIOChannel *listener_channel = NULL; -static guint listener_watch = 0; +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 struct request_data *find_request(guint16 id) { @@ -123,7 +130,8 @@ static struct request_data *find_request(guint16 id) } static struct server_data *find_server(const char *interface, - const char *domain, const char *server) + const char *domain, const char *server, + int protocol) { GSList *list; @@ -136,7 +144,8 @@ static struct server_data *find_server(const char *interface, continue; if (g_str_equal(data->interface, interface) == TRUE && - g_str_equal(data->server, server) == TRUE) { + g_str_equal(data->server, server) == TRUE && + data->protocol == protocol) { if (domain == NULL) { if (data->domain == NULL) return data; @@ -151,37 +160,161 @@ static struct server_data *find_server(const char *interface, return NULL; } -static gboolean server_event(GIOChannel *channel, GIOCondition condition, - gpointer user_data) +static gboolean request_timeout(gpointer user_data) { - struct server_data *data = user_data; - struct request_data *req; - unsigned char buf[4096]; - struct domain_hdr *hdr = (void *) &buf; - int sk, err, len; + struct request_data *req = user_data; - if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - connman_error("Error with server channel"); - data->watch = 0; - return FALSE; + DBG("id 0x%04x", req->srcid); + + request_list = g_slist_remove(request_list, req); + + if (req->resplen > 0 && req->resp != NULL) { + int sk, err; + + sk = g_io_channel_unix_get_fd(udp_listener_channel); + + err = sendto(sk, req->resp, req->resplen, 0, + (struct sockaddr *) &req->sin, req->len); } - sk = g_io_channel_unix_get_fd(channel); + g_free(req->resp); + g_free(req); - len = recv(sk, buf, sizeof(buf), 0); - if (len < 12) - return TRUE; + return FALSE; +} - DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8); +static int append_query(unsigned char *buf, unsigned int size, + const char *query, const char *domain) +{ + unsigned char *ptr = buf; + char *offset; + + DBG("query %s domain %s", query, domain); - req = find_request(buf[0] | buf[1] << 8); + offset = (char *) query; + while (offset != NULL) { + char *tmp; + + tmp = strchr(offset, '.'); + if (tmp == NULL) { + if (strlen(offset) == 0) + break; + *ptr = strlen(offset); + memcpy(ptr + 1, offset, strlen(offset)); + ptr += strlen(offset) + 1; + break; + } + + *ptr = tmp - offset; + memcpy(ptr + 1, offset, tmp - offset); + ptr += tmp - offset + 1; + + offset = tmp + 1; + } + + offset = (char *) domain; + while (offset != NULL) { + char *tmp; + + tmp = strchr(offset, '.'); + if (tmp == NULL) { + if (strlen(offset) == 0) + break; + *ptr = strlen(offset); + memcpy(ptr + 1, offset, strlen(offset)); + ptr += strlen(offset) + 1; + break; + } + + *ptr = tmp - offset; + memcpy(ptr + 1, offset, tmp - offset); + ptr += tmp - offset + 1; + + offset = tmp + 1; + } + + *ptr++ = 0x00; + + return ptr - buf; +} + +static int ns_resolv(struct server_data *server, struct request_data *req, + gpointer request, gpointer name) +{ + int sk, err; + + sk = g_io_channel_unix_get_fd(server->channel); + + err = send(sk, request, req->request_len, 0); + + req->numserv++; + + if (server->domain != NULL && server->protocol == IPPROTO_UDP) { + unsigned char alt[1024]; + struct domain_hdr *hdr = (void *) &alt; + int altlen, domlen; + + domlen = strlen(server->domain) + 1; + if (domlen < 5) + return -EINVAL; + + alt[0] = req->altid & 0xff; + alt[1] = req->altid >> 8; + + memcpy(alt + 2, request + 2, 10); + hdr->qdcount = htons(1); + + altlen = append_query(alt + 12, sizeof(alt) - 12, + name, server->domain); + if (altlen < 0) + return -EINVAL; + + altlen += 12; + + memcpy(alt + altlen, request + altlen - domlen, + req->request_len - altlen + domlen); + + err = send(sk, alt, req->request_len + domlen + 1, 0); + + req->numserv++; + } + + return 0; +} + +static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol) +{ + struct domain_hdr *hdr; + struct request_data *req; + unsigned char offset; + int dns_id, sk, err; + + switch (protocol) { + case IPPROTO_UDP: + offset = 0; + break; + + case IPPROTO_TCP: + offset = 2; + break; + + default: + return -EINVAL; + } + + hdr = (void *)(reply + offset); + dns_id = reply[offset] | reply[offset + 1] << 8; + + DBG("Received %d bytes (id 0x%04x)", reply_len, dns_id); + + req = find_request(dns_id); if (req == NULL) - return TRUE; + return -EINVAL; DBG("id 0x%04x rcode %d", hdr->id, hdr->rcode); - buf[0] = req->srcid & 0xff; - buf[1] = req->srcid >> 8; + reply[offset] = req->srcid & 0xff; + reply[offset + 1] = req->srcid >> 8; req->numresp++; @@ -189,43 +322,209 @@ static gboolean server_event(GIOChannel *channel, GIOCondition condition, g_free(req->resp); req->resplen = 0; - req->resp = g_try_malloc(len); + req->resp = g_try_malloc(reply_len); if (req->resp == NULL) - return TRUE; + return -ENOMEM; - memcpy(req->resp, buf, len); - req->resplen = len; + memcpy(req->resp, reply, reply_len); + req->resplen = reply_len; } if (hdr->rcode > 0 && req->numresp < req->numserv) - return TRUE; + return -EINVAL; if (req->timeout > 0) g_source_remove(req->timeout); request_list = g_slist_remove(request_list, req); - sk = g_io_channel_unix_get_fd(listener_channel); - - err = sendto(sk, req->resp, req->resplen, 0, + if (protocol == IPPROTO_UDP) { + sk = g_io_channel_unix_get_fd(udp_listener_channel); + err = sendto(sk, req->resp, req->resplen, 0, (struct sockaddr *) &req->sin, req->len); + } else { + sk = req->client_sk; + err = send(sk, req->resp, req->resplen, 0); + close(sk); + } g_free(req->resp); g_free(req); + return err; +} + + +static void destroy_server(struct server_data *server) +{ + DBG("interface %s server %s", server->interface, server->server); + + if (server->watch > 0) + g_source_remove(server->watch); + + if (server->timeout > 0) + g_source_remove(server->timeout); + + g_io_channel_unref(server->channel); + + connman_info("Removing DNS server %s", server->server); + + g_free(server->server); + g_free(server->domain); + g_free(server->interface); + g_free(server); +} + +static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + struct server_data *data = user_data; + unsigned char buf[4096]; + int sk, err, len; + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + connman_error("Error with server channel"); + data->watch = 0; + return FALSE; + } + + sk = g_io_channel_unix_get_fd(channel); + + len = recv(sk, buf, sizeof(buf), 0); + if (len < 12) + return TRUE; + + err = forward_dns_reply(buf, len, IPPROTO_UDP); + return TRUE; } +static gboolean tcp_server_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + int sk; + struct server_data *server = user_data; + + sk = g_io_channel_unix_get_fd(channel); + if (sk == 0) + return FALSE; + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + DBG("TCP server channel closed"); + + server_list = g_slist_remove(server_list, server); + destroy_server(server); + + return FALSE; + } + + if ((condition & G_IO_OUT) && !server->connected) { + GSList *list; + + server->connected = TRUE; + server_list = g_slist_append(server_list, server); + + if (server->timeout > 0) { + g_source_remove(server->timeout); + server->timeout = 0; + } + + for (list = request_list; list; list = list->next) { + struct request_data *req = list->data; + + if (!req->client_sk) + continue; + + DBG("Sending req %s over TCP", (char *)req->name); + + if (req->timeout > 0) + g_source_remove(req->timeout); + + req->timeout = g_timeout_add_seconds(30, + request_timeout, req); + ns_resolv(server, req, req->request, req->name); + } + + } else if (condition & G_IO_IN) { + int len, bytes_recv, total_bytes_recv; + unsigned char reply_len_buf[2]; + uint16_t reply_len; + unsigned char *reply; + + len = recv(sk, reply_len_buf, 2, 0); + if (len < 2) + return TRUE; + + reply_len = reply_len_buf[1] | reply_len_buf[0] << 8; + + DBG("TCP reply %d bytes", reply_len); + + reply = g_try_malloc(reply_len + 2); + if (reply == NULL) + return TRUE; + + reply[0] = reply_len_buf[0]; + reply[1] = reply_len_buf[1]; + + total_bytes_recv = bytes_recv = 0; + while (total_bytes_recv < reply_len) { + bytes_recv = recv(sk, reply + 2, reply_len, 0); + if (bytes_recv < 0) + return TRUE; + + total_bytes_recv += bytes_recv; + } + + forward_dns_reply(reply, reply_len + 2, IPPROTO_TCP); + + g_free(reply); + } + + return TRUE; +} + +static gboolean tcp_idle_timeout(gpointer user_data) +{ + struct server_data *server = user_data; + + DBG(""); + + if (server == NULL) + return FALSE; + + destroy_server(server); + + return FALSE; +} + static struct server_data *create_server(const char *interface, - const char *domain, const char *server) + const char *domain, const char *server, + int protocol) { struct server_data *data; struct sockaddr_in sin; - int sk; + int sk, type, ret; DBG("interface %s server %s", interface, server); - sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + switch (protocol) { + case IPPROTO_UDP: + type = SOCK_DGRAM; + break; + + case IPPROTO_TCP: + type = SOCK_STREAM; + break; + + default: + return NULL; + } + + data = find_server(interface, domain, server, protocol); + if (data) + return data; + + sk = socket(AF_INET, type, protocol); if (sk < 0) { connman_error("Failed to create server %s socket", server); return NULL; @@ -242,17 +541,6 @@ static struct server_data *create_server(const char *interface, } } - 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); @@ -270,127 +558,56 @@ static struct server_data *create_server(const char *interface, g_io_channel_set_close_on_unref(data->channel, TRUE); - data->watch = g_io_add_watch(data->channel, G_IO_IN, - server_event, data); + if (protocol == IPPROTO_TCP) { + g_io_channel_set_flags(data->channel, G_IO_FLAG_NONBLOCK, NULL); + data->watch = g_io_add_watch(data->channel, + G_IO_OUT | G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR, + tcp_server_event, data); + data->timeout = g_timeout_add_seconds(30, tcp_idle_timeout, + data); + } else + data->watch = g_io_add_watch(data->channel, G_IO_IN, + udp_server_event, data); data->interface = g_strdup(interface); data->domain = g_strdup(domain); data->server = g_strdup(server); + data->protocol = protocol; - /* Enable new servers by default */ - data->enabled = TRUE; - - connman_info("Adding DNS server %s", data->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); - - connman_info("Removing DNS server %s", data->server); - - g_free(data->server); - g_free(data->domain); - g_free(data->interface); - g_free(data); -} - -static int append_query(unsigned char *buf, unsigned int size, - const char *query, const char *domain) -{ - unsigned char *ptr = buf; - char *offset; - - DBG("query %s domain %s", query, domain); - - offset = (char *) query; - while (offset != NULL) { - char *tmp; - - tmp = strchr(offset, '.'); - if (tmp == NULL) { - if (strlen(offset) == 0) - break; - *ptr = strlen(offset); - memcpy(ptr + 1, offset, strlen(offset)); - ptr += strlen(offset) + 1; - break; - } - - *ptr = tmp - offset; - memcpy(ptr + 1, offset, tmp - offset); - ptr += tmp - offset + 1; - - offset = tmp + 1; - } - - offset = (char *) domain; - while (offset != NULL) { - char *tmp; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(53); + sin.sin_addr.s_addr = inet_addr(server); - tmp = strchr(offset, '.'); - if (tmp == NULL) { - if (strlen(offset) == 0) - break; - *ptr = strlen(offset); - memcpy(ptr + 1, offset, strlen(offset)); - ptr += strlen(offset) + 1; - break; + ret = connect(sk, (struct sockaddr *) &sin, sizeof(sin)); + if (ret < 0) { + if ((protocol == IPPROTO_TCP && errno != EINPROGRESS) || + protocol == IPPROTO_UDP) { + connman_error("Failed to connect to server %s", server); + close(sk); + g_free(data); + return NULL; } - - *ptr = tmp - offset; - memcpy(ptr + 1, offset, tmp - offset); - ptr += tmp - offset + 1; - - offset = tmp + 1; } - *ptr++ = 0x00; + if (protocol == IPPROTO_UDP) { + /* Enable new servers by default */ + data->enabled = TRUE; + connman_info("Adding DNS server %s", data->server); - return ptr - buf; -} + server_list = g_slist_append(server_list, data); -static gboolean request_timeout(gpointer user_data) -{ - struct request_data *req = user_data; - - DBG("id 0x%04x", req->srcid); - - request_list = g_slist_remove(request_list, req); - - if (req->resplen > 0 && req->resp != NULL) { - int sk, err; - - sk = g_io_channel_unix_get_fd(listener_channel); - - err = sendto(sk, req->resp, req->resplen, 0, - (struct sockaddr *) &req->sin, req->len); + return data; } - g_free(req->resp); - g_free(req); - - return FALSE; + return NULL; } static gboolean resolv(struct request_data *req, gpointer request, gpointer name) { - int sk, err; GSList *list; - request_list = g_slist_append(request_list, req); - - req->numserv = 0; - req->timeout = g_timeout_add_seconds(5, request_timeout, req); - for (list = server_list; list; list = list->next) { struct server_data *data = list->data; @@ -400,41 +617,8 @@ static gboolean resolv(struct request_data *req, if (data->enabled == FALSE) continue; - sk = g_io_channel_unix_get_fd(data->channel); - - err = send(sk, request, req->request_len, 0); - - req->numserv++; - - if (data->domain != NULL) { - unsigned char alt[1024]; - struct domain_hdr *hdr = (void *) &alt; - int altlen, domlen; - - domlen = strlen(data->domain) + 1; - if (domlen < 5) - continue; - - alt[0] = req->altid & 0xff; - alt[1] = req->altid >> 8; - - memcpy(alt + 2, request + 2, 10); - hdr->qdcount = htons(1); - - altlen = append_query(alt + 12, sizeof(alt) - 12, - name, data->domain); - if (altlen < 0) - continue; - - altlen += 12; - - memcpy(alt + altlen, request + altlen - domlen, - req->request_len - altlen + domlen); - - err = send(sk, alt, req->request_len + domlen + 1, 0); - - req->numserv++; - } + if (ns_resolv(data, req, request, name) < 0) + continue; } return TRUE; @@ -450,32 +634,37 @@ static int dnsproxy_append(const char *interface, const char *domain, if (g_str_equal(server, "127.0.0.1") == TRUE) return -ENODEV; - data = create_server(interface, domain, server); + data = create_server(interface, domain, server, IPPROTO_UDP); if (data == NULL) return -EIO; - server_list = g_slist_append(server_list, data); - return 0; } -static int dnsproxy_remove(const char *interface, const char *domain, - const char *server) +static void remove_server(const char *interface, const char *domain, + const char *server, int protocol) { struct server_data *data; - DBG("interface %s server %s", interface, server); - - if (g_str_equal(server, "127.0.0.1") == TRUE) - return -ENODEV; - - data = find_server(interface, domain, server); + data = find_server(interface, domain, server, protocol); if (data == NULL) - return 0; + return; server_list = g_slist_remove(server_list, data); destroy_server(data); +} + +static int dnsproxy_remove(const char *interface, const char *domain, + const char *server) +{ + DBG("interface %s server %s", interface, server); + + if (g_str_equal(server, "127.0.0.1") == TRUE) + return -ENODEV; + + remove_server(interface, domain, server, IPPROTO_UDP); + remove_server(interface, domain, server, IPPROTO_TCP); return 0; } @@ -658,7 +847,116 @@ static void send_response(int sk, unsigned char *buf, int len, err = sendto(sk, buf, len, 0, to, tolen); } -static gboolean listener_event(GIOChannel *channel, GIOCondition condition, +static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + unsigned char buf[768]; + char query[512]; + struct request_data *req; + struct server_data *server; + int sk, client_sk, len, err; + struct sockaddr client_addr; + socklen_t client_addr_len; + GSList *list; + + DBG("condition 0x%x", condition); + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + connman_error("Error with TCP listener channel"); + tcp_listener_watch = 0; + return FALSE; + } + + sk = g_io_channel_unix_get_fd(channel); + + client_addr_len = sizeof(struct sockaddr); + client_sk = accept(sk, &client_addr, &client_addr_len); + if (client_sk < 0) { + connman_error("Accept failure on TCP listener"); + tcp_listener_watch = 0; + return FALSE; + } + + len = recv(client_sk, buf, sizeof(buf), 0); + if (len < 2) + return TRUE; + + DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8); + + err = parse_request(buf + 2, len - 2, query, sizeof(query)); + if (err < 0 || (g_slist_length(server_list) == 0 && + connman_ondemand_connected())) { + send_response(client_sk, buf, len, NULL, 0); + return TRUE; + } + + req = g_try_new0(struct request_data, 1); + if (req == NULL) + return TRUE; + + memcpy(&req->sin, (struct sockaddr_in *)&client_addr, sizeof(req->sin)); + req->client_sk = client_sk; + req->len = client_addr_len; + + request_id += 2; + if (request_id == 0x0000 || request_id == 0xffff) + request_id += 2; + + req->srcid = buf[2] | (buf[3] << 8); + req->dstid = request_id; + req->altid = request_id + 1; + req->request_len = len; + + buf[2] = req->dstid & 0xff; + buf[3] = req->dstid >> 8; + + req->numserv = 0; + request_list = g_slist_append(request_list, req); + + for (list = server_list; list; list = list->next) { + struct server_data *data = list->data; + + if (data->protocol != IPPROTO_UDP || data->enabled == FALSE) + continue; + + server = create_server(data->interface, data->domain, + data->server, IPPROTO_TCP); + + /* + * If server is NULL, we're not connected yet. + * Copy the relevant buffers and continue with + * the next nameserver. + * The request will actually be sent once we're + * properly connected over TCP to this nameserver. + */ + if (server == NULL) { + req->request = g_try_malloc0(req->request_len); + if (req->request == NULL) + return TRUE; + + memcpy(req->request, buf, req->request_len); + + req->name = g_try_malloc0(sizeof(query)); + if (req->name == NULL) { + g_free(req->request); + return TRUE; + } + memcpy(req->name, query, sizeof(query)); + + continue; + } + + if (req->timeout > 0) + g_source_remove(req->timeout); + + req->timeout = g_timeout_add_seconds(30, request_timeout, req); + ns_resolv(server, req, buf, query); + } + + return TRUE; +} + +static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition, gpointer user_data) { unsigned char buf[768]; @@ -669,8 +967,8 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, int sk, err, len; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - connman_error("Error with listener channel"); - listener_watch = 0; + connman_error("Error with UDP listener channel"); + udp_listener_watch = 0; return FALSE; } @@ -696,6 +994,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, return TRUE; memcpy(&req->sin, &sin, sizeof(sin)); + req->client_sk = 0; req->len = size; request_id += 2; @@ -737,10 +1036,69 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, return TRUE; } + + req->numserv = 0; + 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_listener(void) +static int create_tcp_listener(void) +{ + const char *ifname = "lo"; + struct sockaddr_in sin; + int sk; + + DBG(""); + + sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sk < 0) { + connman_error("Failed to create TCP listener socket"); + return -EIO; + } + + if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, + ifname, strlen(ifname) + 1) < 0) { + connman_error("Failed to bind TCP 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 = htonl(INADDR_ANY); + + if (bind(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + connman_error("Failed to bind TCP listener socket"); + close(sk); + return -EIO; + } + + if (listen(sk, 10) < 0) { + connman_error("Failed to listen on TCP socket"); + close(sk); + return -EIO; + } + + tcp_listener_channel = g_io_channel_unix_new(sk); + if (tcp_listener_channel == NULL) { + connman_error("Failed to create listener channel"); + close(sk); + return -EIO; + } + + g_io_channel_set_close_on_unref(tcp_listener_channel, TRUE); + + tcp_listener_watch = g_io_add_watch(tcp_listener_channel, G_IO_IN, + tcp_listener_event, NULL); + + return 0; +} + +static int create_udp_listener(void) { const char *ifname = "lo"; struct sockaddr_in sin; @@ -750,7 +1108,7 @@ static int create_listener(void) sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sk < 0) { - connman_error("Failed to create listener socket"); + connman_error("Failed to create UDP listener socket"); return -EIO; } @@ -759,7 +1117,7 @@ static int create_listener(void) if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname) + 1) < 0) { - connman_error("Failed to bind listener interface"); + connman_error("Failed to bind UDP listener interface"); close(sk); return -EIO; } @@ -771,22 +1129,59 @@ static int create_listener(void) //sin.sin_addr.s_addr = INADDR_ANY; if (bind(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - connman_error("Failed to bind listener socket"); + connman_error("Failed to bind UDP 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"); + udp_listener_channel = g_io_channel_unix_new(sk); + if (udp_listener_channel == NULL) { + connman_error("Failed to create UDP listener channel"); close(sk); return -EIO; } - g_io_channel_set_close_on_unref(listener_channel, TRUE); + g_io_channel_set_close_on_unref(udp_listener_channel, TRUE); + + udp_listener_watch = g_io_add_watch(udp_listener_channel, G_IO_IN, + udp_listener_event, NULL); + + return 0; +} + +static void destroy_udp_listener(void) +{ + DBG(""); + + if (udp_listener_watch > 0) + g_source_remove(udp_listener_watch); + + g_io_channel_unref(udp_listener_channel); +} + +static void destroy_tcp_listener(void) +{ + DBG(""); + + if (tcp_listener_watch > 0) + g_source_remove(tcp_listener_watch); + + g_io_channel_unref(tcp_listener_channel); +} - listener_watch = g_io_add_watch(listener_channel, G_IO_IN, - listener_event, NULL); +static int create_listener(void) +{ + int err; + + err = create_udp_listener(); + if (err < 0) + return err; + + err = create_tcp_listener(); + if (err < 0) { + destroy_udp_listener(); + return err; + } connman_resolver_append("lo", NULL, "127.0.0.1"); @@ -797,13 +1192,8 @@ static void destroy_listener(void) { GSList *list; - DBG(""); - connman_resolver_remove_all("lo"); - if (listener_watch > 0) - g_source_remove(listener_watch); - for (list = request_pending_list; list; list = list->next) { struct request_data *req = list->data; @@ -836,7 +1226,8 @@ static void destroy_listener(void) g_slist_free(request_list); request_list = NULL; - g_io_channel_unref(listener_channel); + destroy_tcp_listener(); + destroy_udp_listener(); } static int dnsproxy_init(void) |