diff options
Diffstat (limited to 'src/dnsproxy.c')
-rw-r--r-- | src/dnsproxy.c | 316 |
1 files changed, 225 insertions, 91 deletions
diff --git a/src/dnsproxy.c b/src/dnsproxy.c index 7a9ca910..60b15129 100644 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -105,6 +105,7 @@ struct request_data { socklen_t sa_len; int client_sk; int protocol; + int family; guint16 srcid; guint16 dstid; guint16 altid; @@ -123,10 +124,16 @@ struct request_data { struct listener_data { int index; - GIOChannel *udp_listener_channel; - guint udp_listener_watch; - GIOChannel *tcp_listener_channel; - guint tcp_listener_watch; + + GIOChannel *udp4_listener_channel; + GIOChannel *tcp4_listener_channel; + guint udp4_listener_watch; + guint tcp4_listener_watch; + + GIOChannel *udp6_listener_channel; + GIOChannel *tcp6_listener_channel; + guint udp6_listener_watch; + guint tcp6_listener_watch; }; struct cache_data { @@ -468,25 +475,39 @@ static void send_response(int sk, unsigned char *buf, int len, } } +static int get_req_udp_socket(struct request_data *req) +{ + GIOChannel *channel; + + if (req->family == AF_INET) + channel = req->ifdata->udp4_listener_channel; + else + channel = req->ifdata->udp6_listener_channel; + + if (channel == NULL) + return -1; + + return g_io_channel_unix_get_fd(channel); +} + 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(ifdata->udp_listener_channel); + sk = get_req_udp_socket(req); + if (sk < 0) + return FALSE; err = sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL, &req->sa, req->sa_len); @@ -506,10 +527,12 @@ static gboolean request_timeout(gpointer user_data) hdr = (void *) (req->request); hdr->id = req->srcid; - 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); + + sk = get_req_udp_socket(req); + if (sk >= 0) + send_response(sk, req->request, + req->request_len, &req->sa, + req->sa_len, IPPROTO_UDP); } } @@ -1494,8 +1517,7 @@ static int ns_resolv(struct server_data *server, struct request_data *req, } if (data != NULL && req->protocol == IPPROTO_UDP) { - int udp_sk = g_io_channel_unix_get_fd( - req->ifdata->udp_listener_channel); + int udp_sk = get_req_udp_socket(req); send_cached_response(udp_sk, data->data, data->data_len, &req->sa, req->sa_len, @@ -1600,7 +1622,6 @@ 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; @@ -1617,8 +1638,6 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, DBG("req %p dstid 0x%04x altid 0x%04x rcode %d", req, req->dstid, req->altid, hdr->rcode); - ifdata = req->ifdata; - reply[offset] = req->srcid & 0xff; reply[offset + 1] = req->srcid >> 8; @@ -1694,7 +1713,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(ifdata->udp_listener_channel); + sk = get_req_udp_socket(req); err = sendto(sk, req->resp, req->resplen, 0, &req->sa, req->sa_len); } else { @@ -2515,25 +2534,29 @@ static int parse_request(unsigned char *buf, int len, } static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition, - gpointer user_data) + struct listener_data *ifdata, int family, + guint *listener_watch) { unsigned char buf[768]; char query[512]; struct request_data *req; int sk, client_sk, len, err; - struct sockaddr_in6 client_addr; - socklen_t client_addr_len = sizeof(client_addr); + struct sockaddr_in6 client_addr6; + socklen_t client_addr6_len = sizeof(client_addr6); + struct sockaddr_in client_addr4; + socklen_t client_addr4_len = sizeof(client_addr4); + void *client_addr; + socklen_t *client_addr_len; GSList *list; - struct listener_data *ifdata = user_data; int waiting_for_connect = FALSE, qtype = 0; struct cache_entry *entry; DBG("condition 0x%x", condition); if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - if (ifdata->tcp_listener_watch > 0) - g_source_remove(ifdata->tcp_listener_watch); - ifdata->tcp_listener_watch = 0; + if (*listener_watch > 0) + g_source_remove(*listener_watch); + *listener_watch = 0; connman_error("Error with TCP listener channel"); @@ -2542,10 +2565,19 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition, sk = g_io_channel_unix_get_fd(channel); - client_sk = accept(sk, (void *)&client_addr, &client_addr_len); + if (family == AF_INET) { + client_addr = &client_addr4; + client_addr_len = &client_addr4_len; + } else { + client_addr = &client_addr6; + client_addr_len = &client_addr6_len; + } + + client_sk = accept(sk, client_addr, client_addr_len); + if (client_sk < 0) { connman_error("Accept failure on TCP listener"); - ifdata->tcp_listener_watch = 0; + *listener_watch = 0; return FALSE; } @@ -2566,10 +2598,11 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition, if (req == NULL) return TRUE; - memcpy(&req->sa, &client_addr, client_addr_len); - req->sa_len = client_addr_len; + memcpy(&req->sa, client_addr, *client_addr_len); + req->sa_len = *client_addr_len; req->client_sk = client_sk; req->protocol = IPPROTO_TCP; + req->family = family; req->srcid = buf[2] | (buf[3] << 8); req->dstid = get_id(); @@ -2580,7 +2613,7 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition, buf[3] = req->dstid >> 8; req->numserv = 0; - req->ifdata = (struct listener_data *) ifdata; + req->ifdata = ifdata; req->append_domain = FALSE; /* @@ -2662,28 +2695,57 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition, return TRUE; } +static gboolean tcp4_listener_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + struct listener_data *ifdata = user_data; + + return tcp_listener_event(channel, condition, ifdata, AF_INET, + &ifdata->tcp4_listener_watch); +} + +static gboolean tcp6_listener_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + struct listener_data *ifdata = user_data; + + return tcp_listener_event(channel, condition, user_data, AF_INET6, + &ifdata->tcp6_listener_watch); +} + static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition, - gpointer user_data) + struct listener_data *ifdata, int family, + guint *listener_watch) { unsigned char buf[768]; char query[512]; struct request_data *req; - struct sockaddr_in6 client_addr; - socklen_t client_addr_len = sizeof(client_addr); + struct sockaddr_in6 client_addr6; + socklen_t client_addr6_len = sizeof(client_addr6); + struct sockaddr_in client_addr4; + socklen_t client_addr4_len = sizeof(client_addr4); + void *client_addr; + socklen_t *client_addr_len; 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"); - ifdata->udp_listener_watch = 0; + *listener_watch = 0; return FALSE; } sk = g_io_channel_unix_get_fd(channel); - memset(&client_addr, 0, client_addr_len); - len = recvfrom(sk, buf, sizeof(buf), 0, (void *)&client_addr, - &client_addr_len); + if (family == AF_INET) { + client_addr = &client_addr4; + client_addr_len = &client_addr4_len; + } else { + client_addr = &client_addr6; + client_addr_len = &client_addr6_len; + } + + memset(client_addr, 0, *client_addr_len); + len = recvfrom(sk, buf, sizeof(buf), 0, client_addr, client_addr_len); if (len < 2) return TRUE; @@ -2691,8 +2753,8 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition, err = parse_request(buf, len, query, sizeof(query)); if (err < 0 || (g_slist_length(server_list) == 0)) { - send_response(sk, buf, len, (void *)&client_addr, - client_addr_len, IPPROTO_UDP); + send_response(sk, buf, len, client_addr, + *client_addr_len, IPPROTO_UDP); return TRUE; } @@ -2700,10 +2762,11 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition, if (req == NULL) return TRUE; - memcpy(&req->sa, &client_addr, client_addr_len); - req->sa_len = client_addr_len; + memcpy(&req->sa, client_addr, *client_addr_len); + req->sa_len = *client_addr_len; req->client_sk = 0; req->protocol = IPPROTO_UDP; + req->family = family; req->srcid = buf[0] | (buf[1] << 8); req->dstid = get_id(); @@ -2714,7 +2777,7 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition, buf[1] = req->dstid >> 8; req->numserv = 0; - req->ifdata = (struct listener_data *) ifdata; + req->ifdata = ifdata; req->append_domain = FALSE; if (resolv(req, buf, query) == TRUE) { @@ -2729,7 +2792,25 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition, return TRUE; } -static int create_dns_listener(int protocol, struct listener_data *ifdata) +static gboolean udp4_listener_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + struct listener_data *ifdata = user_data; + + return udp_listener_event(channel, condition, ifdata, AF_INET, + &ifdata->udp4_listener_watch); +} + +static gboolean udp6_listener_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + struct listener_data *ifdata = user_data; + + return udp_listener_event(channel, condition, user_data, AF_INET6, + &ifdata->udp6_listener_watch); +} + +static GIOChannel *get_listener(int family, int protocol, int index) { GIOChannel *channel; const char *proto; @@ -2739,11 +2820,10 @@ static int create_dns_listener(int protocol, struct listener_data *ifdata) struct sockaddr_in sin; } s; socklen_t slen; - int sk, type, v6only = 0; - int family = AF_INET6; + int sk, type; char *interface; - DBG("index %d", ifdata->index); + DBG("family %d protocol %d index %d", family, protocol, index); switch (protocol) { case IPPROTO_UDP: @@ -2757,84 +2837,127 @@ static int create_dns_listener(int protocol, struct listener_data *ifdata) break; default: - return -EINVAL; + return NULL; } sk = socket(family, type, protocol); if (sk < 0 && family == AF_INET6 && errno == EAFNOSUPPORT) { - connman_error("No IPv6 support; DNS proxy listening only on Legacy IP"); - family = AF_INET; - sk = socket(family, type, protocol); + connman_error("No IPv6 support"); + return NULL; } + if (sk < 0) { connman_error("Failed to create %s listener socket", proto); - return -EIO; + return NULL; } - interface = connman_inet_ifname(ifdata->index); + interface = connman_inet_ifname(index); if (interface == NULL || setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, interface, strlen(interface) + 1) < 0) { - connman_error("Failed to bind %s listener interface", proto); + connman_error("Failed to bind %s listener interface " + "for %s (%d/%s)", + proto, family == AF_INET ? "IPv4" : "IPv6", + -errno, strerror(errno)); close(sk); g_free(interface); - return -EIO; + return NULL; } g_free(interface); - /* Ensure it accepts Legacy IP connections too */ - if (family == AF_INET6 && - setsockopt(sk, SOL_IPV6, IPV6_V6ONLY, - &v6only, sizeof(v6only)) < 0) { - connman_error("Failed to clear V6ONLY on %s listener socket", - proto); - close(sk); - return -EIO; - } + if (family == AF_INET6) { + memset(&s.sin6, 0, sizeof(s.sin6)); + s.sin6.sin6_family = AF_INET6; + s.sin6.sin6_port = htons(53); + slen = sizeof(s.sin6); - if (family == AF_INET) { + if (__connman_inet_get_interface_address(index, + AF_INET6, + &s.sin6.sin6_addr) < 0) { + /* So we could not find suitable IPv6 address for + * the interface. This could happen if we have + * disabled IPv6 for the interface. + */ + close(sk); + return NULL; + } + + } else if (family == AF_INET) { memset(&s.sin, 0, sizeof(s.sin)); s.sin.sin_family = AF_INET; s.sin.sin_port = htons(53); - s.sin.sin_addr.s_addr = htonl(INADDR_ANY); slen = sizeof(s.sin); + + if (__connman_inet_get_interface_address(index, + AF_INET, + &s.sin.sin_addr) < 0) { + close(sk); + return NULL; + } } else { - memset(&s.sin6, 0, sizeof(s.sin6)); - s.sin6.sin6_family = AF_INET6; - s.sin6.sin6_port = htons(53); - s.sin6.sin6_addr = in6addr_any; - slen = sizeof(s.sin6); + close(sk); + return NULL; } if (bind(sk, &s.sa, slen) < 0) { connman_error("Failed to bind %s listener socket", proto); close(sk); - return -EIO; + return NULL; } if (protocol == IPPROTO_TCP && listen(sk, 10) < 0) { - connman_error("Failed to listen on TCP socket"); + connman_error("Failed to listen on TCP socket %d/%s", -errno, + strerror(errno)); close(sk); - return -EIO; + return NULL; } channel = g_io_channel_unix_new(sk); if (channel == NULL) { connman_error("Failed to create %s listener channel", proto); close(sk); - return -EIO; + return NULL; } g_io_channel_set_close_on_unref(channel, TRUE); + return channel; +} + +static int create_dns_listener(int protocol, struct listener_data *ifdata) +{ if (protocol == IPPROTO_TCP) { - ifdata->tcp_listener_channel = channel; - ifdata->tcp_listener_watch = g_io_add_watch(channel, - G_IO_IN, tcp_listener_event, (gpointer) ifdata); + ifdata->tcp4_listener_channel = get_listener(AF_INET, protocol, + ifdata->index); + if (ifdata->tcp4_listener_channel != NULL) + ifdata->tcp4_listener_watch = + g_io_add_watch(ifdata->tcp4_listener_channel, + G_IO_IN, tcp4_listener_event, + (gpointer)ifdata); + + ifdata->tcp6_listener_channel = get_listener(AF_INET6, protocol, + ifdata->index); + if (ifdata->tcp6_listener_channel != NULL) + ifdata->tcp6_listener_watch = + g_io_add_watch(ifdata->tcp6_listener_channel, + G_IO_IN, tcp6_listener_event, + (gpointer)ifdata); } else { - ifdata->udp_listener_channel = channel; - ifdata->udp_listener_watch = g_io_add_watch(channel, - G_IO_IN, udp_listener_event, (gpointer) ifdata); + ifdata->udp4_listener_channel = get_listener(AF_INET, protocol, + ifdata->index); + if (ifdata->udp4_listener_channel != NULL) + ifdata->udp4_listener_watch = + g_io_add_watch(ifdata->udp4_listener_channel, + G_IO_IN, udp4_listener_event, + (gpointer)ifdata); + + ifdata->udp6_listener_channel = get_listener(AF_INET6, protocol, + ifdata->index); + if (ifdata->udp6_listener_channel != NULL) + ifdata->udp6_listener_watch = + g_io_add_watch(ifdata->udp6_listener_channel, + G_IO_IN, udp6_listener_event, + (gpointer)ifdata); } return 0; @@ -2844,20 +2967,27 @@ static void destroy_udp_listener(struct listener_data *ifdata) { DBG("index %d", ifdata->index); - if (ifdata->udp_listener_watch > 0) - g_source_remove(ifdata->udp_listener_watch); + if (ifdata->udp4_listener_watch > 0) + g_source_remove(ifdata->udp4_listener_watch); + + if (ifdata->udp6_listener_watch > 0) + g_source_remove(ifdata->udp6_listener_watch); - g_io_channel_unref(ifdata->udp_listener_channel); + g_io_channel_unref(ifdata->udp4_listener_channel); + g_io_channel_unref(ifdata->udp6_listener_channel); } static void destroy_tcp_listener(struct listener_data *ifdata) { DBG("index %d", ifdata->index); - if (ifdata->tcp_listener_watch > 0) - g_source_remove(ifdata->tcp_listener_watch); + if (ifdata->tcp4_listener_watch > 0) + g_source_remove(ifdata->tcp4_listener_watch); + if (ifdata->tcp6_listener_watch > 0) + g_source_remove(ifdata->tcp6_listener_watch); - g_io_channel_unref(ifdata->tcp_listener_channel); + g_io_channel_unref(ifdata->tcp4_listener_channel); + g_io_channel_unref(ifdata->tcp6_listener_channel); } static int create_listener(struct listener_data *ifdata) @@ -2927,10 +3057,14 @@ int __connman_dnsproxy_add_listener(int index) return -ENOMEM; ifdata->index = index; - ifdata->udp_listener_channel = NULL; - ifdata->udp_listener_watch = 0; - ifdata->tcp_listener_channel = NULL; - ifdata->tcp_listener_watch = 0; + ifdata->udp4_listener_channel = NULL; + ifdata->udp4_listener_watch = 0; + ifdata->tcp4_listener_channel = NULL; + ifdata->tcp4_listener_watch = 0; + ifdata->udp6_listener_channel = NULL; + ifdata->udp6_listener_watch = 0; + ifdata->tcp6_listener_channel = NULL; + ifdata->tcp6_listener_watch = 0; err = create_listener(ifdata); if (err < 0) { |