X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fdnsproxy.c;h=7956e7fb17bdf335e665bcf441c39f5ffcc80ea0;hb=6d2cf90574c81cd30340116270e17af058703afb;hp=58ce44276602cd52ea27d988e6c82a01297fed0b;hpb=9c59ca5a99768d876fb5e12366f158bc2110ba9c;p=platform%2Fupstream%2Fconnman.git diff --git a/src/dnsproxy.c b/src/dnsproxy.c index 58ce442..7956e7f 100755 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -46,6 +46,8 @@ #include #endif +#define debug(fmt...) do { } while (0) + #if __BYTE_ORDER == __LITTLE_ENDIAN struct domain_hdr { uint16_t id; @@ -82,6 +84,11 @@ struct domain_hdr { #error "Unknown byte order" #endif +struct qtype_qclass { + uint16_t qtype; + uint16_t qclass; +} __attribute__ ((packed)); + struct partial_reply { uint16_t len; uint16_t received; @@ -301,7 +308,7 @@ static struct server_data *find_server(int index, { GSList *list; - DBG("index %d server %s proto %d", index, server, protocol); + debug("index %d server %s proto %d", index, server, protocol); for (list = server_list; list; list = list->next) { struct server_data *data = list->data; @@ -352,14 +359,14 @@ static void refresh_dns_entry(struct cache_entry *entry, char *name) } if (!entry->ipv4) { - DBG("Refresing A record for %s", name); + debug("Refreshing A record for %s", name); g_resolv_lookup_hostname(ipv4_resolve, name, dummy_resolve_func, NULL); age = 4; } if (!entry->ipv6) { - DBG("Refresing AAAA record for %s", name); + debug("Refreshing AAAA record for %s", name); g_resolv_lookup_hostname(ipv6_resolve, name, dummy_resolve_func, NULL); age = 4; @@ -374,7 +381,7 @@ static int dns_name_length(unsigned char *buf) { if ((buf[0] & NS_CMPRSFLGS) == NS_CMPRSFLGS) /* compressed name */ return 2; - return strlen((char *)buf); + return strlen((char *)buf) + 1; } static void update_cached_ttl(unsigned char *buf, int len, int new_ttl) @@ -465,13 +472,13 @@ static void send_cached_response(int sk, unsigned char *buf, int len, hdr->nscount = 0; hdr->arcount = 0; - /* if this is a negative reply, we are authorative */ + /* if this is a negative reply, we are authoritative */ if (answers == 0) hdr->aa = 1; else update_cached_ttl((unsigned char *)hdr, adj_len, ttl); - DBG("sk %d id 0x%04x answers %d ptr %p length %d dns %d", + debug("sk %d id 0x%04x answers %d ptr %p length %d dns %d", sk, hdr->id, answers, ptr, len, dns_len); err = sendto(sk, ptr, len, MSG_NOSIGNAL, to, tolen); @@ -483,37 +490,50 @@ static void send_cached_response(int sk, unsigned char *buf, int len, if (err != len || (dns_len != (len - 2) && protocol == IPPROTO_TCP) || (dns_len != len && protocol == IPPROTO_UDP)) - DBG("Packet length mismatch, sent %d wanted %d dns %d", + debug("Packet length mismatch, sent %d wanted %d dns %d", err, len, dns_len); } -static void send_response(int sk, unsigned char *buf, int len, +static void send_response(int sk, unsigned char *buf, size_t len, const struct sockaddr *to, socklen_t tolen, int protocol) { struct domain_hdr *hdr; int err, offset = protocol_offset(protocol); - DBG("sk %d", sk); + debug("sk %d", sk); if (offset < 0) return; - if (len < 12) + if (len < sizeof(*hdr) + offset) return; hdr = (void *) (buf + offset); +#if !defined TIZEN_EXT + if (offset) { + buf[0] = 0; + buf[1] = sizeof(*hdr); + } +#endif - DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode); + debug("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode); hdr->qr = 1; hdr->rcode = ns_r_servfail; +#if !defined TIZEN_EXT + hdr->qdcount = 0; +#endif hdr->ancount = 0; hdr->nscount = 0; hdr->arcount = 0; +#if defined TIZEN_EXT err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen); +#else + err = sendto(sk, buf, sizeof(*hdr) + offset, MSG_NOSIGNAL, to, tolen); +#endif if (err < 0) { connman_error("Failed to send DNS response to %d: %s", sk, strerror(errno)); @@ -556,7 +576,7 @@ static gboolean request_timeout(gpointer user_data) if (!req) return FALSE; - DBG("id 0x%04x", req->srcid); + debug("id 0x%04x", req->srcid); request_list = g_slist_remove(request_list, req); @@ -598,7 +618,7 @@ static gboolean request_timeout(gpointer user_data) * if we get a request timeout from server. */ if (req->protocol == IPPROTO_TCP) { - DBG("client %d removed", req->client_sk); + debug("client %d removed", req->client_sk); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(req->client_sk)); } @@ -616,7 +636,7 @@ static int append_query(unsigned char *buf, unsigned int size, unsigned char *ptr = buf; int len; - DBG("query %s domain %s", query, domain); + debug("query %s domain %s", query, domain); while (query) { const char *tmp; @@ -686,7 +706,7 @@ static void cache_enforce_validity(struct cache_entry *entry) if (!cache_check_is_valid(entry->ipv4, current_time) && entry->ipv4) { - DBG("cache timeout \"%s\" type A", entry->key); + debug("cache timeout \"%s\" type A", entry->key); g_free(entry->ipv4->data); g_free(entry->ipv4); entry->ipv4 = NULL; @@ -695,7 +715,7 @@ static void cache_enforce_validity(struct cache_entry *entry) if (!cache_check_is_valid(entry->ipv6, current_time) && entry->ipv6) { - DBG("cache timeout \"%s\" type AAAA", entry->key); + debug("cache timeout \"%s\" type AAAA", entry->key); g_free(entry->ipv6->data); g_free(entry->ipv6); entry->ipv6 = NULL; @@ -720,7 +740,7 @@ static uint16_t cache_check_validity(char *question, uint16_t type, switch (type) { case 1: /* IPv4 */ if (!cache_check_is_valid(entry->ipv4, current_time)) { - DBG("cache %s \"%s\" type A", entry->ipv4 ? + debug("cache %s \"%s\" type A", entry->ipv4 ? "timeout" : "entry missing", question); if (want_refresh) @@ -739,7 +759,7 @@ static uint16_t cache_check_validity(char *question, uint16_t type, case 28: /* IPv6 */ if (!cache_check_is_valid(entry->ipv6, current_time)) { - DBG("cache %s \"%s\" type AAAA", entry->ipv6 ? + debug("cache %s \"%s\" type AAAA", entry->ipv6 ? "timeout" : "entry missing", question); if (want_refresh) @@ -785,7 +805,7 @@ static gboolean try_remove_cache(gpointer user_data) cache_timer = 0; if (__sync_fetch_and_sub(&cache_refcount, 1) == 1) { - DBG("No cache users, removing it."); + debug("No cache users, removing it."); g_hash_table_destroy(cache); cache = NULL; @@ -855,7 +875,7 @@ static struct cache_entry *cache_check(gpointer request, int *qtype, int proto) static int get_name(int counter, unsigned char *pkt, unsigned char *start, unsigned char *max, unsigned char *output, int output_max, int *output_len, - unsigned char **end, char *name, int *name_len) + unsigned char **end, char *name, size_t max_name, int *name_len) { unsigned char *p; @@ -876,7 +896,7 @@ static int get_name(int counter, return get_name(counter + 1, pkt, pkt + offset, max, output, output_max, output_len, end, - name, name_len); + name, max_name, name_len); } else { unsigned label_len = *p; @@ -886,6 +906,9 @@ static int get_name(int counter, if (*output_len > output_max) return -ENOBUFS; + if ((*name_len + 1 + label_len + 1) > max_name) + return -ENOBUFS; + /* * We need the original name in order to check * if this answer is the correct one. @@ -917,14 +940,14 @@ static int parse_rr(unsigned char *buf, unsigned char *start, unsigned char *response, unsigned int *response_size, uint16_t *type, uint16_t *class, int *ttl, int *rdlen, unsigned char **end, - char *name) + char *name, size_t max_name) { struct domain_rr *rr; int err, offset; int name_len = 0, output_len = 0, max_rsp = *response_size; err = get_name(0, buf, start, max, response, max_rsp, - &output_len, end, name, &name_len); + &output_len, end, name, max_name, &name_len); if (err < 0) return err; @@ -999,7 +1022,7 @@ static int parse_response(unsigned char *buf, int buflen, if (buflen < 12) return -EINVAL; - DBG("qr %d qdcount %d", hdr->qr, qdcount); + debug("qr %d qdcount %d", hdr->qr, qdcount); /* We currently only cache responses where question count is 1 */ if (hdr->qr != 1 || qdcount != 1) @@ -1050,7 +1073,8 @@ static int parse_response(unsigned char *buf, int buflen, memset(rsp, 0, sizeof(rsp)); ret = parse_rr(buf, ptr, buf + buflen, rsp, &rsp_len, - type, class, ttl, &rdlen, &next, name); + type, class, ttl, &rdlen, &next, name, + sizeof(name) - 1); if (ret != 0) { err = ret; goto out; @@ -1116,7 +1140,7 @@ static int parse_response(unsigned char *buf, int buflen, */ ret = get_name(0, buf, next - rdlen, buf + buflen, rsp, rsp_len, &output_len, &end, - name, &name_len); + name, sizeof(name) - 1, &name_len); if (ret != 0) { /* just ignore the error at this point */ ptr = next; @@ -1244,7 +1268,7 @@ static void cache_cleanup(void) count = g_hash_table_foreach_remove(cache, cache_check_entry, &data); } - DBG("removed %d in the first pass", count); + debug("removed %d in the first pass", count); /* * In the second pass, if the first pass turned up blank, @@ -1308,7 +1332,7 @@ static gboolean cache_invalidate_entry(gpointer key, gpointer value, */ static void cache_invalidate(void) { - DBG("Invalidating the DNS cache %p", cache); + debug("Invalidating the DNS cache %p", cache); if (!cache) return; @@ -1340,7 +1364,7 @@ static void cache_refresh_entry(struct cache_entry *entry) *c = '.'; c += jump + 1; } - DBG("Refreshing %s\n", dns_name); + debug("Refreshing %s\n", dns_name); /* then refresh the hostname */ refresh_dns_entry(entry, &dns_name[1]); } @@ -1376,7 +1400,7 @@ static int reply_query_type(unsigned char *msg, int len) return 0; /* now the query, which is a name and 2 16 bit words */ - l = dns_name_length(c) + 1; + l = dns_name_length(c); c += l; type = c[0] << 8 | c[1]; @@ -1417,7 +1441,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg, if (offset < 0) return 0; - DBG("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode); + debug("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode); /* Continue only if response code is 0 (=ok) */ if (hdr->rcode != ns_r_noerror) @@ -1597,7 +1621,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg, cache_size++; } - DBG("cache %d %squestion \"%s\" type %d ttl %d size %zd packet %u " + debug("cache %d %squestion \"%s\" type %d ttl %d size %zd packet %u " "dns len %u", cache_size, new_entry ? "new " : "old ", question, type, ttl, @@ -1623,7 +1647,7 @@ static int ns_resolv(struct server_data *server, struct request_data *req, int ttl_left = 0; struct cache_data *data; - DBG("cache hit %s type %s", lookup, type == 1 ? "A" : "AAAA"); + debug("cache hit %s type %s", lookup, type == 1 ? "A" : "AAAA"); if (type == 1) data = entry->ipv4; else @@ -1685,7 +1709,7 @@ static int ns_resolv(struct server_data *server, struct request_data *req, err = sendto(sk, request, req->request_len, MSG_NOSIGNAL, server->server_addr, server->server_addr_len); if (err < 0) { - DBG("Cannot send message to server %s sock %d " + debug("Cannot send message to server %s sock %d " "protocol %d (%s/%d)", server->server, sk, server->protocol, strerror(errno), errno); @@ -1745,7 +1769,7 @@ static int ns_resolv(struct server_data *server, struct request_data *req, alt[1] = req_len & 0xff; } - DBG("req %p dstid 0x%04x altid 0x%04x", req, req->dstid, + debug("req %p dstid 0x%04x altid 0x%04x", req, req->dstid, req->altid); err = send(sk, alt, req->request_len + domlen, MSG_NOSIGNAL); @@ -1767,7 +1791,7 @@ static char *convert_label(char *start, char *end, char *ptr, char *uptr, pos = dn_expand((u_char *)start, (u_char *)end, (u_char *)ptr, name, NS_MAXLABEL); if (pos < 0) { - DBG("uncompress error [%d/%s]", errno, strerror(errno)); + debug("uncompress error [%d/%s]", errno, strerror(errno)); goto out; } @@ -1777,7 +1801,7 @@ static char *convert_label(char *start, char *end, char *ptr, char *uptr, */ comp_pos = dn_comp(name, (u_char *)uptr, remaining_len, NULL, NULL); if (comp_pos < 0) { - DBG("compress error [%d/%s]", errno, strerror(errno)); + debug("compress error [%d/%s]", errno, strerror(errno)); goto out; } @@ -1796,7 +1820,7 @@ static char *uncompress(int16_t field_count, char *start, char *end, { char *uptr = *uncompressed_ptr; /* position in result buffer */ - DBG("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr); + debug("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr); while (field_count-- > 0 && ptr < end) { int dlen; /* data field length */ @@ -1818,7 +1842,7 @@ static char *uncompress(int16_t field_count, char *start, char *end, ulen = strlen(name); strncpy(uptr, name, uncomp_len - (uptr - uncompressed)); - DBG("pos %d ulen %d left %d name %s", pos, ulen, + debug("pos %d ulen %d left %d name %s", pos, ulen, (int)(uncomp_len - (uptr - uncompressed)), uptr); uptr += ulen; @@ -1862,7 +1886,7 @@ static char *uncompress(int16_t field_count, char *start, char *end, dlen = uptr[-2] << 8 | uptr[-1]; if (ptr + dlen > end) { - DBG("data len %d too long", dlen); + debug("data len %d too long", dlen); goto out; } @@ -1973,13 +1997,13 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, hdr = (void *)(reply + offset); dns_id = reply[offset] | reply[offset + 1] << 8; - DBG("Received %d bytes (id 0x%04x)", reply_len, dns_id); + debug("Received %d bytes (id 0x%04x)", reply_len, dns_id); req = find_request(dns_id); if (!req) return -EINVAL; - DBG("req %p dstid 0x%04x altid 0x%04x rcode %d", + debug("req %p dstid 0x%04x altid 0x%04x rcode %d", req, req->dstid, req->altid, hdr->rcode); reply[offset] = req->srcid & 0xff; @@ -2035,7 +2059,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, ptr[dns_type_pos + 3]; if (dns_type != ns_t_a && dns_type != ns_t_aaaa && dns_class != ns_c_in) { - DBG("Pass msg dns type %d class %d", + debug("Pass msg dns type %d class %d", dns_type, dns_class); goto pass; } @@ -2094,21 +2118,21 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, (char *)reply + offset, eom, ptr, uncompressed, NS_MAXDNAME, &uptr); - if (ptr == NULL) + if (!ptr) goto out; ptr = uncompress(ntohs(hdr->nscount), (char *)reply + offset, eom, ptr, uncompressed, NS_MAXDNAME, &uptr); - if (ptr == NULL) + if (!ptr) goto out; ptr = uncompress(ntohs(hdr->arcount), (char *)reply + offset, eom, ptr, uncompressed, NS_MAXDNAME, &uptr); - if (ptr == NULL) + if (!ptr) goto out; /* @@ -2124,7 +2148,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, new_len = strip_domains(uncompressed, answers, uptr - answers); if (new_len < 0) { - DBG("Corrupted packet"); + debug("Corrupted packet"); return -EINVAL; } @@ -2153,8 +2177,15 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, req->resplen = 0; req->resp = g_try_malloc(reply_len); +#if defined TIZEN_EXT + if (!req->resp) { + g_free(new_reply); + return -ENOMEM; + } +#else if (!req->resp) return -ENOMEM; +#endif memcpy(req->resp, reply, reply_len); req->resplen = reply_len; @@ -2189,10 +2220,10 @@ out: } if (err < 0) - DBG("Cannot send msg, sk %d proto %d errno %d/%s", sk, + debug("Cannot send msg, sk %d proto %d errno %d/%s", sk, protocol, errno, strerror(errno)); else - DBG("proto %d sent %d bytes to %d", protocol, err, sk); + debug("proto %d sent %d bytes to %d", protocol, err, sk); destroy_request_data(req); @@ -2201,7 +2232,7 @@ out: static void server_destroy_socket(struct server_data *data) { - DBG("index %d server %s proto %d", data->index, + debug("index %d server %s proto %d", data->index, data->server, data->protocol); if (data->watch > 0) { @@ -2226,7 +2257,7 @@ static void server_destroy_socket(struct server_data *data) static void destroy_server(struct server_data *server) { - DBG("index %d server %s sock %d", server->index, server->server, + debug("index %d server %s sock %d", server->index, server->server, server->channel ? g_io_channel_unix_get_fd(server->channel): -1); @@ -2234,7 +2265,7 @@ static void destroy_server(struct server_data *server) server_destroy_socket(server); if (server->protocol == IPPROTO_UDP && server->enabled) - DBG("Removing DNS server %s", server->server); + debug("Removing DNS server %s", server->server); g_free(server->server); g_list_free_full(server->domains, g_free); @@ -2259,7 +2290,7 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition, gpointer user_data) { unsigned char buf[4096]; - int sk, err, len; + int sk, len; struct server_data *data = user_data; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { @@ -2271,12 +2302,9 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition, 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, data); - if (err < 0) - return TRUE; + if (len >= 12) + forward_dns_reply(buf, len, IPPROTO_UDP, data); #if defined TIZEN_EXT GSList *list; @@ -2307,7 +2335,7 @@ static gboolean tcp_server_event(GIOChannel *channel, GIOCondition condition, if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { GSList *list; hangup: - DBG("TCP server channel closed, sk %d", sk); + debug("TCP server channel closed, sk %d", sk); /* * Discard any partial response which is buffered; better @@ -2316,9 +2344,11 @@ hangup: g_free(server->incoming_reply); server->incoming_reply = NULL; - for (list = request_list; list; list = list->next) { + list = request_list; + while (list) { struct request_data *req = list->data; struct domain_hdr *hdr; + list = list->next; if (req->protocol == IPPROTO_UDP) continue; @@ -2360,7 +2390,7 @@ hangup: domains = domains->next) { char *dom = domains->data; - DBG("Adding domain %s to %s", + debug("Adding domain %s to %s", dom, server->server); server->domains = g_list_append(server->domains, @@ -2385,7 +2415,7 @@ hangup: continue; } - DBG("Sending req %s over TCP", (char *)req->name); + debug("Sending req %s over TCP", (char *)req->name); status = ns_resolv(server, req, req->request, req->name); @@ -2444,7 +2474,7 @@ hangup: reply_len = reply_len_buf[1] | reply_len_buf[0] << 8; reply_len += 2; - DBG("TCP reply %d bytes from %d", reply_len, sk); + debug("TCP reply %d bytes from %d", reply_len, sk); reply = g_try_malloc(sizeof(*reply) + reply_len + 2); if (!reply) @@ -2491,7 +2521,7 @@ static gboolean tcp_idle_timeout(gpointer user_data) { struct server_data *server = user_data; - DBG(""); + debug(""); if (!server) return FALSE; @@ -2506,7 +2536,7 @@ static int server_create_socket(struct server_data *data) int sk, err; char *interface; - DBG("index %d server %s proto %d", data->index, + debug("index %d server %s proto %d", data->index, data->server, data->protocol); sk = socket(data->server_addr->sa_family, @@ -2520,7 +2550,7 @@ static int server_create_socket(struct server_data *data) return -err; } - DBG("sk %d", sk); + debug("sk %d", sk); interface = connman_inet_ifname(data->index); if (interface) { @@ -2580,6 +2610,25 @@ static int server_create_socket(struct server_data *data) return 0; } +static void enable_fallback(bool enable) +{ + GSList *list; + + for (list = server_list; list; list = list->next) { + struct server_data *data = list->data; + + if (data->index != -1) + continue; + + if (enable) + DBG("Enabling fallback DNS server %s", data->server); + else + DBG("Disabling fallback DNS server %s", data->server); + + data->enabled = enable; + } +} + #if defined TIZEN_EXT static void destroy_server_sec(struct server_data *server) @@ -2837,6 +2886,8 @@ static struct server_data *create_server(int index, data->index)) { data->enabled = true; DBG("Adding DNS server %s", data->server); + + enable_fallback(false); } server_list = g_slist_append(server_list, data); @@ -2858,7 +2909,7 @@ static bool resolv(struct request_data *req, continue; } - DBG("server %s enabled %d", data->server, data->enabled); + debug("server %s enabled %d", data->server, data->enabled); if (!data->enabled) continue; @@ -2877,7 +2928,7 @@ static bool resolv(struct request_data *req, return false; } -static void append_domain(int index, const char *domain) +static void update_domain(int index, const char *domain, bool append) { GSList *list; @@ -2908,13 +2959,27 @@ static void append_domain(int index, const char *domain) } } - if (!dom_found) { + if (!dom_found && append) { data->domains = g_list_append(data->domains, g_strdup(domain)); + } else if (dom_found && !append) { + data->domains = + g_list_remove(data->domains, dom); + g_free(dom); } } } +static void append_domain(int index, const char *domain) +{ + update_domain(index, domain, true); +} + +static void remove_domain(int index, const char *domain) +{ + update_domain(index, domain, false); +} + static void flush_requests(struct server_data *server) { GSList *list; @@ -2984,12 +3049,22 @@ static void remove_server(int index, const char *domain, const char *server, int protocol) { struct server_data *data; + GSList *list; data = find_server(index, server, protocol); if (!data) return; destroy_server(data); + + for (list = server_list; list; list = list->next) { + struct server_data *data = list->data; + + if (data->index != -1 && data->enabled == true) + return; + } + + enable_fallback(true); } int __connman_dnsproxy_remove(int index, const char *domain, @@ -2997,9 +3072,15 @@ int __connman_dnsproxy_remove(int index, const char *domain, { DBG("index %d server %s", index, server); - if (!server) + if (!server && !domain) return -EINVAL; + if (!server) { + remove_domain(index, domain); + + return 0; + } + if (g_str_equal(server, "127.0.0.1")) return -ENODEV; @@ -3040,6 +3121,7 @@ static void dnsproxy_offline_mode(bool enabled) static void dnsproxy_default_changed(struct connman_service *service) { + bool server_enabled = false; GSList *list; int index; @@ -3064,43 +3146,90 @@ static void dnsproxy_default_changed(struct connman_service *service) if (data->index == index) { DBG("Enabling DNS server %s", data->server); data->enabled = true; + server_enabled = true; } else { DBG("Disabling DNS server %s", data->server); data->enabled = false; } } + if (!server_enabled) + enable_fallback(true); + cache_refresh(); } -static struct connman_notifier dnsproxy_notifier = { +static void dnsproxy_service_state_changed(struct connman_service *service, + enum connman_service_state state) +{ + GSList *list; + int index; + + switch (state) { + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_IDLE: + break; + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_FAILURE: + case CONNMAN_SERVICE_STATE_ONLINE: + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_UNKNOWN: + return; + } + + index = __connman_service_get_index(service); + list = server_list; + + while (list) { + struct server_data *data = list->data; + + /* Get next before the list is changed by destroy_server() */ + list = list->next; + + if (data->index == index) { + DBG("removing server data of index %d", index); + destroy_server(data); + } + } +} + +static const struct connman_notifier dnsproxy_notifier = { .name = "dnsproxy", .default_changed = dnsproxy_default_changed, .offline_mode = dnsproxy_offline_mode, + .service_state_changed = dnsproxy_service_state_changed, }; -static unsigned char opt_edns0_type[2] = { 0x00, 0x29 }; +static const unsigned char opt_edns0_type[2] = { 0x00, 0x29 }; -static int parse_request(unsigned char *buf, int len, +static int parse_request(unsigned char *buf, size_t len, char *name, unsigned int size) { struct domain_hdr *hdr = (void *) buf; uint16_t qdcount = ntohs(hdr->qdcount); + uint16_t ancount = ntohs(hdr->ancount); + uint16_t nscount = ntohs(hdr->nscount); uint16_t arcount = ntohs(hdr->arcount); unsigned char *ptr; - char *last_label = NULL; unsigned int remain, used = 0; - if (len < 12) + if (len < sizeof(*hdr) + sizeof(struct qtype_qclass) || + hdr->qr || qdcount != 1 || ancount || nscount) { + DBG("Dropped DNS request qr %d with len %zd qdcount %d " + "ancount %d nscount %d", hdr->qr, len, qdcount, ancount, + nscount); + return -EINVAL; + } - DBG("id 0x%04x qr %d opcode %d qdcount %d arcount %d", + if (!name || !size) + return -EINVAL; + + debug("id 0x%04x qr %d opcode %d qdcount %d arcount %d", hdr->id, hdr->qr, hdr->opcode, qdcount, arcount); - if (hdr->qr != 0 || qdcount != 1) - return -EINVAL; - name[0] = '\0'; ptr = buf + sizeof(struct domain_hdr); @@ -3110,7 +3239,23 @@ static int parse_request(unsigned char *buf, int len, uint8_t label_len = *ptr; if (label_len == 0x00) { - last_label = (char *) (ptr + 1); + uint8_t class; + struct qtype_qclass *q = + (struct qtype_qclass *)(ptr + 1); + + if (remain < sizeof(*q)) { + DBG("Dropped malformed DNS query"); + return -EINVAL; + } + + class = ntohs(q->qclass); + if (class != 1 && class != 255) { + DBG("Dropped non-IN DNS class %d", class); + return -EINVAL; + } + + ptr += sizeof(*q) + 1; + remain -= (sizeof(*q) + 1); break; } @@ -3126,44 +3271,16 @@ static int parse_request(unsigned char *buf, int len, remain -= label_len + 1; } -#if defined TIZEN_EXT - /* parse DNS query type either A or AAAA - * enforce to drop AAAA temporarily (IPv6 not supported) - */ - if (last_label != NULL) { - uint16_t *type_p = (uint16_t *)last_label; - uint16_t type = ntohs(*type_p); + if (arcount && remain >= sizeof(struct domain_rr) + 1 && !ptr[0] && + ptr[1] == opt_edns0_type[0] && ptr[2] == opt_edns0_type[1]) { + struct domain_rr *edns0 = (struct domain_rr *)(ptr + 1); - if (type == 0x1c) { - DBG("query %s is type AAAA(0x%x)", name, type); - return -ENOENT; - } + DBG("EDNS0 buffer size %u", ntohs(edns0->class)); + } else if (!arcount && remain) { + DBG("DNS request with %d garbage bytes", remain); } -#endif - if (last_label && arcount && remain >= 9 && last_label[4] == 0 && - !memcmp(last_label + 5, opt_edns0_type, 2)) { - uint16_t edns0_bufsize; - - edns0_bufsize = last_label[7] << 8 | last_label[8]; - - DBG("EDNS0 buffer size %u", edns0_bufsize); - - /* This is an evil hack until full TCP support has been - * implemented. - * - * Somtimes the EDNS0 request gets send with a too-small - * buffer size. Since glibc doesn't seem to crash when it - * gets a response biffer then it requested, just bump - * the buffer size up to 4KiB. - */ - if (edns0_bufsize < 0x1000) { - last_label[7] = 0x10; - last_label[8] = 0x00; - } - } - - DBG("query %s", name); + debug("query %s", name); return 0; } @@ -3174,7 +3291,7 @@ static void client_reset(struct tcp_partial_client_data *client) return; if (client->channel) { - DBG("client %d closing", + debug("client %d closing", g_io_channel_unix_get_fd(client->channel)); g_io_channel_unref(client->channel); @@ -3218,14 +3335,14 @@ static bool read_tcp_data(struct tcp_partial_client_data *client, client_sk = g_io_channel_unix_get_fd(client->channel); if (read_len == 0) { - DBG("client %d closed, pending %d bytes", + debug("client %d closed, pending %d bytes", client_sk, client->buf_end); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); return false; } - DBG("client %d received %d bytes", client_sk, read_len); + debug("client %d received %d bytes", client_sk, read_len); client->buf_end += read_len; @@ -3234,24 +3351,24 @@ static bool read_tcp_data(struct tcp_partial_client_data *client, msg_len = get_msg_len(client->buf); if (msg_len > TCP_MAX_BUF_LEN) { - DBG("client %d sent too much data %d", client_sk, msg_len); + debug("client %d sent too much data %d", client_sk, msg_len); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); return false; } read_another: - DBG("client %d msg len %d end %d past end %d", client_sk, msg_len, + debug("client %d msg len %d end %d past end %d", client_sk, msg_len, client->buf_end, client->buf_end - (msg_len + 2)); if (client->buf_end < (msg_len + 2)) { - DBG("client %d still missing %d bytes", + debug("client %d still missing %d bytes", client_sk, msg_len + 2 - client->buf_end); return true; } - DBG("client %d all data %d received", client_sk, msg_len); + debug("client %d all data %d received", client_sk, msg_len); err = parse_request(client->buf + 2, msg_len, query, sizeof(query)); @@ -3292,7 +3409,7 @@ read_another: int ttl_left = 0; struct cache_data *data; - DBG("cache hit %s type %s", query, qtype == 1 ? "A" : "AAAA"); + debug("cache hit %s type %s", query, qtype == 1 ? "A" : "AAAA"); if (qtype == 1) data = entry->ipv4; else @@ -3309,7 +3426,7 @@ read_another: g_free(req); goto out; } else - DBG("data missing, ignoring cache for this query"); + debug("data missing, ignoring cache for this query"); } for (list = server_list; list; list = list->next) { @@ -3364,7 +3481,7 @@ read_another: out: if (client->buf_end > (msg_len + 2)) { - DBG("client %d buf %p -> %p end %d len %d new %d", + debug("client %d buf %p -> %p end %d len %d new %d", client_sk, client->buf + msg_len + 2, client->buf, client->buf_end, @@ -3380,12 +3497,12 @@ out: */ msg_len = get_msg_len(client->buf); if ((msg_len + 2) == client->buf_end) { - DBG("client %d reading another %d bytes", client_sk, + debug("client %d reading another %d bytes", client_sk, msg_len + 2); goto read_another; } } else { - DBG("client %d clearing reading buffer", client_sk); + debug("client %d clearing reading buffer", client_sk); client->buf_end = 0; memset(client->buf, 0, TCP_MAX_BUF_LEN); @@ -3447,7 +3564,7 @@ static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition, if (errno == EAGAIN || errno == EWOULDBLOCK) return TRUE; - DBG("client %d cannot read errno %d/%s", client_sk, -errno, + debug("client %d cannot read errno %d/%s", client_sk, -errno, strerror(errno)); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); @@ -3464,7 +3581,7 @@ static gboolean client_timeout(gpointer user_data) sock = g_io_channel_unix_get_fd(client->channel); - DBG("client %d timeout pending %d bytes", sock, client->buf_end); + debug("client %d timeout pending %d bytes", sock, client->buf_end); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(sock)); @@ -3504,7 +3621,7 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, struct timeval tv; fd_set readfds; - DBG("condition 0x%02x channel %p ifdata %p family %d", + debug("condition 0x%02x channel %p ifdata %p family %d", condition, channel, ifdata, family); if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { @@ -3540,9 +3657,9 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, select(sk + 1, &readfds, NULL, NULL, &tv); if (FD_ISSET(sk, &readfds)) { client_sk = accept(sk, client_addr, client_addr_len); - DBG("client %d accepted", client_sk); + debug("client %d accepted", client_sk); } else { - DBG("No data to read from master %d, waiting.", sk); + debug("No data to read from master %d, waiting.", sk); return true; } @@ -3576,9 +3693,9 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, client->ifdata = ifdata; - DBG("client %d created %p", client_sk, client); + debug("client %d created %p", client_sk, client); } else { - DBG("client %d already exists %p", client_sk, client); + debug("client %d already exists %p", client_sk, client); } if (!client->buf) { @@ -3602,11 +3719,11 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, len = recv(client_sk, client->buf, TCP_MAX_BUF_LEN, 0); if (len < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { - DBG("client %d no data to read, waiting", client_sk); + debug("client %d no data to read, waiting", client_sk); return true; } - DBG("client %d cannot read errno %d/%s", client_sk, -errno, + debug("client %d cannot read errno %d/%s", client_sk, -errno, strerror(errno)); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); @@ -3614,14 +3731,14 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, } if (len < 2) { - DBG("client %d not enough data to read, waiting", client_sk); + debug("client %d not enough data to read, waiting", client_sk); client->buf_end += len; return true; } msg_len = get_msg_len(client->buf); if (msg_len > TCP_MAX_BUF_LEN) { - DBG("client %d invalid message length %u ignoring packet", + debug("client %d invalid message length %u ignoring packet", client_sk, msg_len); g_hash_table_remove(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); @@ -3637,7 +3754,7 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, #else if (msg_len != (unsigned int)(len - 2)) { #endif - DBG("client %d sent %d bytes but expecting %u pending %d", + debug("client %d sent %d bytes but expecting %u pending %d", client_sk, len, msg_len + 2, msg_len + 2 - len); client->buf_end += len; @@ -3666,37 +3783,35 @@ static gboolean tcp6_listener_event(GIOChannel *channel, GIOCondition condition, } #if defined TIZEN_EXT -/* Temporarily disable AAAA type to enhance performance (IPv6 not supported) */ -static void __send_response_not_implemented(int sk, unsigned char *buf, int len, - const struct sockaddr *to, socklen_t tolen, - int protocol) +struct request_data * create_request(int sk, unsigned char *buf, size_t len, + const struct sockaddr *to, socklen_t tolen, + int protocol) { - struct domain_hdr *hdr; - int err, offset = protocol_offset(protocol); - - DBG("sk %d", sk); - - if (offset < 0) - return; - - if (len < 12) - return; - - hdr = (void *) (buf + offset); + struct request_data *req; + req = g_try_new0(struct request_data, 1); + if (!req) + return NULL; + memcpy(&req->sa, to, tolen); + req->sa_len = tolen; + req->client_sk = sk; + req->protocol = protocol; + req->request_len = len; + req->request = g_malloc(len); + memcpy(req->request, buf, len); + return req; - DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode); +} +static gboolean send_response_timeout (gpointer user_data) +{ + struct request_data *req = user_data; - hdr->qr = 1; - hdr->rcode = 4; + send_response(req->client_sk, req->request,(size_t) req->request_len, (const struct sockaddr *)&req->sa, + (socklen_t)req->sa_len, req->protocol); + g_free(req->request); + g_free(req); - hdr->ancount = 0; - hdr->nscount = 0; - hdr->arcount = 0; + return FALSE; - err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen); - if (err < 0) - connman_error("Failed to send DNS response to %d: %s", - sk, strerror(errno)); } #endif @@ -3742,22 +3857,26 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition, if (len < 2) return true; - DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8); + debug("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8); err = parse_request(buf, len, query, sizeof(query)); if (err < 0 || (g_slist_length(server_list) == 0)) { #if defined TIZEN_EXT - if (err == -ENOENT) { - /* Temporarily disable AAAA type to enhance performance - * (IPv6 not supported) - */ - __send_response_not_implemented(sk, buf, len, client_addr, - *client_addr_len, IPPROTO_UDP); - return TRUE; - } -#endif + /** TEMP Fix + * Reason: In tizen6.0 same code working fine because it seems some delay in 6.0 platform + * where as in tizen6.5 this loop is continuously executing due to this unable to receive + * the response from telephony deamon and wpa_supplicant. To stop continuously execution + * of this code added 10ms delay. + */ + req = create_request(sk, buf, len, client_addr, + *client_addr_len, IPPROTO_UDP); + if(!req) + return true;; + g_timeout_add(10, send_response_timeout, req); +#else send_response(sk, buf, len, client_addr, *client_addr_len, IPPROTO_UDP); +#endif return true; } @@ -3841,9 +3960,10 @@ static GIOChannel *get_listener(int family, int protocol, int index) int option; int sd_num = 0; int rv; + int is_socket_inet = 0; #endif - DBG("family %d protocol %d index %d", family, protocol, index); + debug("family %d protocol %d index %d", family, protocol, index); switch (protocol) { case IPPROTO_UDP: @@ -3876,11 +3996,12 @@ static GIOChannel *get_listener(int family, int protocol, int index) rv = sd_is_socket_inet(sk, family, type, -1, 53); if(rv > 0){ DBG("socket fd (%d) is passed by systemd", sk); + is_socket_inet = 1; break; } } - if(sk >= SD_LISTEN_FDS_START+sd_num){ + if (!is_socket_inet) { DBG("socket fd is not matched what connman requests"); return NULL; } @@ -3896,9 +4017,6 @@ static GIOChannel *get_listener(int family, int protocol, int index) return NULL; } - /* ConnMan listens DNS from multiple interfaces - * E.g. various technology based and tethering interfaces - */ interface = connman_inet_ifname(index); if (!interface || setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, interface, @@ -3935,6 +4053,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) s.sin.sin_family = AF_INET; s.sin.sin_port = htons(53); slen = sizeof(s.sin); + if (__connman_inet_get_interface_address(index, AF_INET, &s.sin.sin_addr) < 0) { @@ -3946,11 +4065,16 @@ static GIOChannel *get_listener(int family, int protocol, int index) return NULL; } #endif + #if defined TIZEN_EXT /* When ConnMan crashed, * probably DNS listener cannot bind existing address */ option = 1; - setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); + if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) < 0) { + connman_error("Failed to set socket option SO_REUSEADDR"); + close(sk); + return NULL; + } #endif #if !defined TIZEN_EXT if (bind(sk, &s.sa, slen) < 0) { @@ -3961,6 +4085,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) #endif if (protocol == IPPROTO_TCP) { + #if !defined TIZEN_EXT if (listen(sk, 10) < 0) { connman_error("Failed to listen on TCP socket %d/%s", @@ -3968,6 +4093,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) close(sk); return NULL; } + #endif fcntl(sk, F_SETFL, O_NONBLOCK); } @@ -4141,7 +4267,7 @@ static void destroy_listener(struct listener_data *ifdata) for (list = request_list; list; list = list->next) { struct request_data *req = list->data; - DBG("Dropping request (id 0x%04x -> 0x%04x)", + debug("Dropping request (id 0x%04x -> 0x%04x)", req->srcid, req->dstid); destroy_request_data(req); list->data = NULL; @@ -4265,6 +4391,11 @@ destroy: return err; } +int __connman_dnsproxy_set_mdns(int index, bool enabled) +{ + return -ENOTSUP; +} + void __connman_dnsproxy_cleanup(void) { DBG(""); @@ -4286,4 +4417,9 @@ void __connman_dnsproxy_cleanup(void) g_hash_table_destroy(listener_table); g_hash_table_destroy(partial_tcp_req_table); + + if (ipv4_resolve) + g_resolv_unref(ipv4_resolve); + if (ipv6_resolve) + g_resolv_unref(ipv6_resolve); }