X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fdnsproxy.c;h=178e98f75d6f2bc2995ee49edc81fd0e4fbf5dbe;hb=4e1d941e94590b2a41996b477f2563a9c860e1e5;hp=4ad4eb666af99cc100857eff16bec471a8223615;hpb=a5a030ecc15f2cfdcabc45238832f0b9e41097f7;p=platform%2Fupstream%2Fconnman.git diff --git a/src/dnsproxy.c b/src/dnsproxy.c old mode 100644 new mode 100755 index 4ad4eb6..178e98f --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -41,6 +41,13 @@ #include "connman.h" +#if defined TIZEN_EXT +#include +#include +#endif + +#define debug(fmt...) do { } while (0) + #if __BYTE_ORDER == __LITTLE_ENDIAN struct domain_hdr { uint16_t id; @@ -77,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; @@ -93,8 +105,8 @@ struct server_data { GIOChannel *channel; guint watch; guint timeout; - gboolean enabled; - gboolean connected; + bool enabled; + bool connected; struct partial_reply *incoming_reply; }; @@ -120,7 +132,7 @@ struct request_data { gpointer resp; gsize resplen; struct listener_data *ifdata; - gboolean append_domain; + bool append_domain; }; struct listener_data { @@ -164,7 +176,7 @@ struct cache_data { struct cache_entry { char *key; - int want_refresh; + bool want_refresh; int hits; struct cache_data *ipv4; struct cache_data *ipv6; @@ -192,7 +204,11 @@ struct domain_rr { * By default the TTL (time-to-live) of the DNS response is used * when setting the cache entry life time. The value is in seconds. */ +#if defined TIZEN_EXT +#define MAX_CACHE_TTL (60 * 60) +#else #define MAX_CACHE_TTL (60 * 30) +#endif /* * Also limit the other end, cache at least for 30 seconds. */ @@ -211,14 +227,29 @@ static int cache_size; static GHashTable *cache; static int cache_refcount; static GSList *server_list = NULL; +#if defined TIZEN_EXT +static GSList *server_list_sec = NULL; +#endif static GSList *request_list = NULL; static GHashTable *listener_table = NULL; static time_t next_refresh; static GHashTable *partial_tcp_req_table; +static guint cache_timer = 0; -static guint16 get_id() +#if defined TIZEN_EXT +static void destroy_server_sec(struct server_data *server); +static struct server_data *create_server_sec(int index, + const char *domain, const char *server, + int protocol); +#endif + +static guint16 get_id(void) { - return random(); + uint64_t rand; + + __connman_util_get_random(&rand); + + return rand; } static int protocol_offset(int protocol) @@ -277,22 +308,22 @@ 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; if (index < 0 && data->index < 0 && - g_str_equal(data->server, server) == TRUE && + g_str_equal(data->server, server) && data->protocol == protocol) return data; if (index < 0 || - data->index < 0 || data->server == NULL) + data->index < 0 || !data->server) continue; if (data->index == index && - g_str_equal(data->server, server) == TRUE && + g_str_equal(data->server, server) && data->protocol == protocol) return data; } @@ -315,27 +346,27 @@ static void refresh_dns_entry(struct cache_entry *entry, char *name) { int age = 1; - if (ipv4_resolve == NULL) { + if (!ipv4_resolve) { ipv4_resolve = g_resolv_new(0); g_resolv_set_address_family(ipv4_resolve, AF_INET); g_resolv_add_nameserver(ipv4_resolve, "127.0.0.1", 53, 0); } - if (ipv6_resolve == NULL) { + if (!ipv6_resolve) { ipv6_resolve = g_resolv_new(0); g_resolv_set_address_family(ipv6_resolve, AF_INET6); g_resolv_add_nameserver(ipv6_resolve, "::1", 53, 0); } - if (entry->ipv4 == NULL) { - DBG("Refresing A record for %s", name); + if (!entry->ipv4) { + debug("Refreshing A record for %s", name); g_resolv_lookup_hostname(ipv4_resolve, name, dummy_resolve_func, NULL); age = 4; } - if (entry->ipv6 == NULL) { - DBG("Refresing AAAA record for %s", name); + if (!entry->ipv6) { + debug("Refreshing AAAA record for %s", name); g_resolv_lookup_hostname(ipv6_resolve, name, dummy_resolve_func, NULL); age = 4; @@ -350,14 +381,13 @@ 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) { unsigned char *c; - uint32_t *i; - uint16_t *w; + uint16_t w; int l; /* skip the header */ @@ -387,17 +417,19 @@ static void update_cached_ttl(unsigned char *buf, int len, int new_ttl) break; /* now the 4 byte TTL field */ - i = (uint32_t *)c; - *i = htonl(new_ttl); + c[0] = new_ttl >> 24 & 0xff; + c[1] = new_ttl >> 16 & 0xff; + c[2] = new_ttl >> 8 & 0xff; + c[3] = new_ttl & 0xff; c += 4; len -= 4; if (len < 0) break; /* now the 2 byte rdlen field */ - w = (uint16_t *)c; - c += ntohs(*w) + 2; - len -= ntohs(*w) + 2; + w = c[0] << 8 | c[1]; + c += w + 2; + len -= w + 2; } } @@ -435,7 +467,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len, hdr->id = id; hdr->qr = 1; - hdr->rcode = 0; + hdr->rcode = ns_r_noerror; hdr->ancount = htons(answers); hdr->nscount = 0; hdr->arcount = 0; @@ -446,7 +478,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len, 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); @@ -458,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 = 2; + 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)); @@ -505,7 +550,7 @@ static int get_req_udp_socket(struct request_data *req) else channel = req->ifdata->udp6_listener_channel; - if (channel == NULL) + if (!channel) return -1; return g_io_channel_unix_get_fd(channel); @@ -525,54 +570,47 @@ static void destroy_request_data(struct request_data *req) static gboolean request_timeout(gpointer user_data) { struct request_data *req = user_data; + struct sockaddr *sa; + int sk; - if (req == NULL) + if (!req) return FALSE; - DBG("id 0x%04x", req->srcid); + debug("id 0x%04x", req->srcid); request_list = g_slist_remove(request_list, req); - req->numserv--; - if (req->resplen > 0 && req->resp != NULL) { - int sk, err; + if (req->protocol == IPPROTO_UDP) { + sk = get_req_udp_socket(req); + sa = &req->sa; + } else if (req->protocol == IPPROTO_TCP) { + sk = req->client_sk; + sa = NULL; + } else + goto out; - if (req->protocol == IPPROTO_UDP) { - sk = get_req_udp_socket(req); - if (sk < 0) - return FALSE; + if (req->resplen > 0 && req->resp) { + /* + * Here we have received at least one reply (probably telling + * "not found" result), so send that back to client instead + * of more fatal server failed error. + */ + if (sk >= 0) + sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL, + sa, req->sa_len); - err = sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL, - &req->sa, req->sa_len); - } else { - sk = req->client_sk; - err = send(sk, req->resp, req->resplen, MSG_NOSIGNAL); - if (err < 0) - close(sk); - } - if (err < 0) - return FALSE; - } else if (req->request && req->numserv == 0) { + } else if (req->request) { + /* + * There was not reply from server at all. + */ struct domain_hdr *hdr; - if (req->protocol == IPPROTO_TCP) { - hdr = (void *) (req->request + 2); - hdr->id = req->srcid; - send_response(req->client_sk, req->request, - req->request_len, NULL, 0, IPPROTO_TCP); + hdr = (void *)(req->request + protocol_offset(req->protocol)); + hdr->id = req->srcid; - } else if (req->protocol == IPPROTO_UDP) { - int sk; - - hdr = (void *) (req->request); - hdr->id = req->srcid; - - sk = get_req_udp_socket(req); - if (sk >= 0) - send_response(sk, req->request, - req->request_len, &req->sa, - req->sa_len, IPPROTO_UDP); - } + if (sk >= 0) + send_response(sk, req->request, req->request_len, + sa, req->sa_len, req->protocol); } /* @@ -580,11 +618,12 @@ 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)); } +out: req->timeout = 0; destroy_request_data(req); @@ -597,13 +636,13 @@ 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 != NULL) { + while (query) { const char *tmp; tmp = strchr(query, '.'); - if (tmp == NULL) { + if (!tmp) { len = strlen(query); if (len == 0) break; @@ -620,11 +659,11 @@ static int append_query(unsigned char *buf, unsigned int size, query = tmp + 1; } - while (domain != NULL) { + while (domain) { const char *tmp; tmp = strchr(domain, '.'); - if (tmp == NULL) { + if (!tmp) { len = strlen(domain); if (len == 0) break; @@ -646,16 +685,16 @@ static int append_query(unsigned char *buf, unsigned int size, return ptr - buf; } -static gboolean cache_check_is_valid(struct cache_data *data, +static bool cache_check_is_valid(struct cache_data *data, time_t current_time) { - if (data == NULL) - return FALSE; + if (!data) + return false; if (data->cache_until < current_time) - return FALSE; + return false; - return TRUE; + return true; } /* @@ -665,18 +704,18 @@ static void cache_enforce_validity(struct cache_entry *entry) { time_t current_time = time(NULL); - if (cache_check_is_valid(entry->ipv4, current_time) == FALSE + 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; } - if (cache_check_is_valid(entry->ipv6, current_time) == FALSE + 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; @@ -687,32 +726,31 @@ static uint16_t cache_check_validity(char *question, uint16_t type, struct cache_entry *entry) { time_t current_time = time(NULL); - int want_refresh = 0; + bool want_refresh = false; /* * if we have a popular entry, we want a refresh instead of * total destruction of the entry. */ if (entry->hits > 2) - want_refresh = 1; + want_refresh = true; cache_enforce_validity(entry); switch (type) { case 1: /* IPv4 */ - if (cache_check_is_valid(entry->ipv4, current_time) == FALSE) { - DBG("cache %s \"%s\" type A", entry->ipv4 ? + if (!cache_check_is_valid(entry->ipv4, current_time)) { + debug("cache %s \"%s\" type A", entry->ipv4 ? "timeout" : "entry missing", question); if (want_refresh) - entry->want_refresh = 1; + entry->want_refresh = true; /* * We do not remove cache entry if there is still * valid IPv6 entry found in the cache. */ - if (cache_check_is_valid(entry->ipv6, current_time) - == FALSE && want_refresh == FALSE) { + if (!cache_check_is_valid(entry->ipv6, current_time) && !want_refresh) { g_hash_table_remove(cache, question); type = 0; } @@ -720,15 +758,14 @@ static uint16_t cache_check_validity(char *question, uint16_t type, break; case 28: /* IPv6 */ - if (cache_check_is_valid(entry->ipv6, current_time) == FALSE) { - DBG("cache %s \"%s\" type AAAA", entry->ipv6 ? + if (!cache_check_is_valid(entry->ipv6, current_time)) { + debug("cache %s \"%s\" type AAAA", entry->ipv6 ? "timeout" : "entry missing", question); if (want_refresh) - entry->want_refresh = 1; + entry->want_refresh = true; - if (cache_check_is_valid(entry->ipv4, current_time) - == FALSE && want_refresh == FALSE) { + if (!cache_check_is_valid(entry->ipv4, current_time) && !want_refresh) { g_hash_table_remove(cache, question); type = 0; } @@ -743,15 +780,15 @@ static void cache_element_destroy(gpointer value) { struct cache_entry *entry = value; - if (entry == NULL) + if (!entry) return; - if (entry->ipv4 != NULL) { + if (entry->ipv4) { g_free(entry->ipv4->data); g_free(entry->ipv4); } - if (entry->ipv6 != NULL) { + if (entry->ipv6) { g_free(entry->ipv6->data); g_free(entry->ipv6); } @@ -765,8 +802,10 @@ static void cache_element_destroy(gpointer value) 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; @@ -775,7 +814,7 @@ static gboolean try_remove_cache(gpointer user_data) return FALSE; } -static void create_cache() +static void create_cache(void) { if (__sync_fetch_and_add(&cache_refcount, 1) == 0) cache = g_hash_table_new_full(g_str_hash, @@ -792,7 +831,7 @@ static struct cache_entry *cache_check(gpointer request, int *qtype, int proto) uint16_t type; int offset, proto_offset; - if (request == NULL) + if (!request) return NULL; proto_offset = protocol_offset(proto); @@ -809,13 +848,13 @@ static struct cache_entry *cache_check(gpointer request, int *qtype, int proto) if (type != 1 && type != 28) return NULL; - if (cache == NULL) { + if (!cache) { create_cache(); return NULL; } entry = g_hash_table_lookup(cache, question); - if (entry == NULL) + if (!entry) return NULL; type = cache_check_validity(question, type, entry); @@ -836,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; @@ -852,12 +891,12 @@ static int get_name(int counter, if (offset >= max - pkt) return -ENOBUFS; - if (*end == NULL) + if (!*end) *end = p + 2; 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; @@ -867,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. @@ -882,7 +924,7 @@ static int get_name(int counter, p += label_len + 1; - if (*end == NULL) + if (!*end) *end = p; if (p >= max) @@ -898,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; @@ -916,7 +958,7 @@ static int parse_rr(unsigned char *buf, unsigned char *start, rr = (void *) (*end); - if (rr == NULL) + if (!rr) return -EINVAL; *type = ntohs(rr->type); @@ -944,19 +986,19 @@ static int parse_rr(unsigned char *buf, unsigned char *start, return 0; } -static gboolean check_alias(GSList *aliases, char *name) +static bool check_alias(GSList *aliases, char *name) { GSList *list; - if (aliases != NULL) { + if (aliases) { for (list = aliases; list; list = list->next) { int len = strlen((char *)list->data); if (strncmp((char *)list->data, name, len) == 0) - return TRUE; + return true; } } - return FALSE; + return false; } static int parse_response(unsigned char *buf, int buflen, @@ -980,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) @@ -1007,6 +1049,8 @@ static int parse_response(unsigned char *buf, int buflen, *response_len = 0; *answers = 0; + memset(name, 0, sizeof(name)); + /* * We have a bunch of answers (like A, AAAA, CNAME etc) to * A or AAAA question. We traverse the answers and parse the @@ -1029,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; @@ -1095,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; @@ -1120,8 +1165,8 @@ static int parse_response(unsigned char *buf, int buflen, /* * We found correct type (A or AAAA) */ - if (check_alias(aliases, name) == TRUE || - (aliases == NULL && strncmp(question, name, + if (check_alias(aliases, name) || + (!aliases && strncmp(question, name, qlen) == 0)) { /* * We found an alias or the name of the rr @@ -1176,7 +1221,7 @@ static gboolean cache_check_entry(gpointer key, gpointer value, * remove both from the cache. */ - if (entry->ipv4 != NULL && entry->ipv4->timeout > 0) { + if (entry->ipv4 && entry->ipv4->timeout > 0) { max_timeout = entry->ipv4->cache_until; if (max_timeout > data->max_timeout) data->max_timeout = max_timeout; @@ -1185,7 +1230,7 @@ static gboolean cache_check_entry(gpointer key, gpointer value, return TRUE; } - if (entry->ipv6 != NULL && entry->ipv6->timeout > 0) { + if (entry->ipv6 && entry->ipv6->timeout > 0) { max_timeout = entry->ipv6->cache_until; if (max_timeout > data->max_timeout) data->max_timeout = max_timeout; @@ -1223,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, @@ -1257,7 +1302,7 @@ static gboolean cache_invalidate_entry(gpointer key, gpointer value, /* if anything is not expired, mark the entry for refresh */ if (entry->hits > 0 && (entry->ipv4 || entry->ipv6)) - entry->want_refresh = 1; + entry->want_refresh = true; /* delete the cached data */ if (entry->ipv4) { @@ -1287,9 +1332,9 @@ 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 == NULL) + if (!cache) return; g_hash_table_foreach_remove(cache, cache_invalidate_entry, NULL); @@ -1300,15 +1345,15 @@ static void cache_refresh_entry(struct cache_entry *entry) cache_enforce_validity(entry); - if (entry->hits > 2 && entry->ipv4 == NULL) - entry->want_refresh = 1; - if (entry->hits > 2 && entry->ipv6 == NULL) - entry->want_refresh = 1; + if (entry->hits > 2 && !entry->ipv4) + entry->want_refresh = true; + if (entry->hits > 2 && !entry->ipv6) + entry->want_refresh = true; if (entry->want_refresh) { char *c; char dns_name[NS_MAXDNAME + 1]; - entry->want_refresh = 0; + entry->want_refresh = false; /* turn a DNS name into a hostname with dots */ strncpy(dns_name, entry->key, NS_MAXDNAME); @@ -1319,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]); } @@ -1335,7 +1380,7 @@ static void cache_refresh_iterator(gpointer key, gpointer value, static void cache_refresh(void) { - if (cache == NULL) + if (!cache) return; g_hash_table_foreach(cache, cache_refresh_iterator, NULL); @@ -1344,7 +1389,6 @@ static void cache_refresh(void) static int reply_query_type(unsigned char *msg, int len) { unsigned char *c; - uint16_t *w; int l; int type; @@ -1356,10 +1400,9 @@ 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; - w = (uint16_t *) c; - type = ntohs(*w); + type = c[0] << 8 | c[1]; return type; } @@ -1378,7 +1421,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg, unsigned char response[NS_MAXDNAME + 1]; unsigned char *ptr; unsigned int rsplen; - gboolean new_entry = TRUE; + bool new_entry = true; time_t current_time; if (cache_size >= MAX_CACHE_SIZE) { @@ -1398,12 +1441,15 @@ 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 != 0) + if (hdr->rcode != ns_r_noerror) return 0; + if (!cache) + create_cache(); + rsplen = sizeof(response) - 1; question[sizeof(question) - 1] = '\0'; @@ -1420,16 +1466,12 @@ static int cache_update(struct server_data *srv, unsigned char *msg, if ((err == -ENOMSG || err == -ENOBUFS) && reply_query_type(msg + offset, msg_len - offset) == 28) { - if (cache == NULL) { - create_cache(); - entry = NULL; - } else - entry = g_hash_table_lookup(cache, question); - if (entry && entry->ipv4 && entry->ipv6 == NULL) { + entry = g_hash_table_lookup(cache, question); + if (entry && entry->ipv4 && !entry->ipv6) { int cache_offset = 0; data = g_try_new(struct cache_data, 1); - if (data == NULL) + if (!data) return -ENOMEM; data->inserted = entry->ipv4->inserted; data->type = type; @@ -1471,20 +1513,20 @@ static int cache_update(struct server_data *srv, unsigned char *msg, * records for the same name. */ entry = g_hash_table_lookup(cache, question); - if (entry == NULL) { + if (!entry) { entry = g_try_new(struct cache_entry, 1); - if (entry == NULL) + if (!entry) return -ENOMEM; data = g_try_new(struct cache_data, 1); - if (data == NULL) { + if (!data) { g_free(entry); return -ENOMEM; } entry->key = g_strdup(question); entry->ipv4 = entry->ipv6 = NULL; - entry->want_refresh = 0; + entry->want_refresh = false; entry->hits = 0; if (type == 1) @@ -1492,14 +1534,14 @@ static int cache_update(struct server_data *srv, unsigned char *msg, else entry->ipv6 = data; } else { - if (type == 1 && entry->ipv4 != NULL) + if (type == 1 && entry->ipv4) return 0; - if (type == 28 && entry->ipv6 != NULL) + if (type == 28 && entry->ipv6) return 0; data = g_try_new(struct cache_data, 1); - if (data == NULL) + if (!data) return -ENOMEM; if (type == 1) @@ -1515,7 +1557,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg, if (entry->hits < 0) entry->hits = 0; - new_entry = FALSE; + new_entry = false; } if (ttl < MIN_CACHE_TTL) @@ -1543,7 +1585,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg, data->cache_until = round_down_ttl(current_time + ttl, ttl); - if (data->data == NULL) { + if (!data->data) { g_free(entry->key); g_free(data); g_free(entry); @@ -1574,12 +1616,12 @@ static int cache_update(struct server_data *srv, unsigned char *msg, memcpy(ptr + offset + 12 + qlen + 1 + sizeof(struct domain_question), response, rsplen); - if (new_entry == TRUE) { + if (new_entry) { g_hash_table_replace(cache, entry->key, entry); 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, @@ -1601,11 +1643,11 @@ static int ns_resolv(struct server_data *server, struct request_data *req, struct cache_entry *entry; entry = cache_check(request, &type, req->protocol); - if (entry != NULL) { + if (entry) { 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 @@ -1616,16 +1658,19 @@ static int ns_resolv(struct server_data *server, struct request_data *req, entry->hits++; } - if (data != NULL && req->protocol == IPPROTO_TCP) { + if (data && req->protocol == IPPROTO_TCP) { send_cached_response(req->client_sk, data->data, data->data_len, NULL, 0, IPPROTO_TCP, req->srcid, data->answers, ttl_left); return 1; } - if (data != NULL && req->protocol == IPPROTO_UDP) { + if (data && req->protocol == IPPROTO_UDP) { int udp_sk = get_req_udp_socket(req); + if (udp_sk < 0) + return -EIO; + send_cached_response(udp_sk, data->data, data->data_len, &req->sa, req->sa_len, IPPROTO_UDP, req->srcid, data->answers, @@ -1634,12 +1679,37 @@ static int ns_resolv(struct server_data *server, struct request_data *req, } } +#if defined TIZEN_EXT + if (server->protocol == IPPROTO_UDP) { + GList *domains; + struct server_data *new_server = NULL; + + new_server = create_server_sec(server->index, NULL, + server->server, IPPROTO_UDP); + + if (new_server != NULL) { + for (domains = server->domains; domains; + domains = domains->next) { + char *dom = domains->data; + + DBG("Adding domain %s to %s", + dom, new_server->server); + + new_server->domains = g_list_append( + new_server->domains, + g_strdup(dom)); + } + + server = new_server; + } + } +#endif sk = g_io_channel_unix_get_fd(server->channel); 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); @@ -1650,11 +1720,11 @@ static int ns_resolv(struct server_data *server, struct request_data *req, /* If we have more than one dot, we don't add domains */ dot = strchr(lookup, '.'); - if (dot != NULL && dot != lookup + strlen(lookup) - 1) + if (dot && dot != lookup + strlen(lookup) - 1) return 0; - if (server->domains != NULL && server->domains->data != NULL) - req->append_domain = TRUE; + if (server->domains && server->domains->data) + req->append_domain = true; for (list = server->domains; list; list = list->next) { char *domain; @@ -1664,7 +1734,7 @@ static int ns_resolv(struct server_data *server, struct request_data *req, domain = list->data; - if (domain == NULL) + if (!domain) continue; offset = protocol_offset(server->protocol); @@ -1699,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); @@ -1712,6 +1782,208 @@ static int ns_resolv(struct server_data *server, struct request_data *req, return 0; } +static char *convert_label(char *start, char *end, char *ptr, char *uptr, + int remaining_len, int *used_comp, int *used_uncomp) +{ + int pos, comp_pos; + char name[NS_MAXLABEL]; + + pos = dn_expand((u_char *)start, (u_char *)end, (u_char *)ptr, + name, NS_MAXLABEL); + if (pos < 0) { + debug("uncompress error [%d/%s]", errno, strerror(errno)); + goto out; + } + + /* + * We need to compress back the name so that we get back to internal + * label presentation. + */ + comp_pos = dn_comp(name, (u_char *)uptr, remaining_len, NULL, NULL); + if (comp_pos < 0) { + debug("compress error [%d/%s]", errno, strerror(errno)); + goto out; + } + + *used_comp = pos; + *used_uncomp = comp_pos; + + return ptr; + +out: + return NULL; +} + +static char *uncompress(int16_t field_count, char *start, char *end, + char *ptr, char *uncompressed, int uncomp_len, + char **uncompressed_ptr) +{ + char *uptr = *uncompressed_ptr; /* position in result buffer */ + + 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 */ + int ulen; /* uncompress length */ + int pos; /* position in compressed string */ + char name[NS_MAXLABEL]; /* tmp label */ + uint16_t dns_type, dns_class; + int comp_pos; + + if (!convert_label(start, end, ptr, name, NS_MAXLABEL, + &pos, &comp_pos)) + goto out; + + /* + * Copy the uncompressed resource record, type, class and \0 to + * tmp buffer. + */ + + ulen = strlen(name); + strncpy(uptr, name, uncomp_len - (uptr - uncompressed)); + + debug("pos %d ulen %d left %d name %s", pos, ulen, + (int)(uncomp_len - (uptr - uncompressed)), uptr); + + uptr += ulen; + *uptr++ = '\0'; + + ptr += pos; + + /* + * We copy also the fixed portion of the result (type, class, + * ttl, address length and the address) + */ + memcpy(uptr, ptr, NS_RRFIXEDSZ); + + dns_type = uptr[0] << 8 | uptr[1]; + dns_class = uptr[2] << 8 | uptr[3]; + + if (dns_class != ns_c_in) + goto out; + + ptr += NS_RRFIXEDSZ; + uptr += NS_RRFIXEDSZ; + + /* + * Then the variable portion of the result (data length). + * Typically this portion is also compressed + * so we need to uncompress it also when necessary. + */ + if (dns_type == ns_t_cname) { + if (!convert_label(start, end, ptr, uptr, + uncomp_len - (uptr - uncompressed), + &pos, &comp_pos)) + goto out; + + uptr[-2] = comp_pos << 8; + uptr[-1] = comp_pos & 0xff; + + uptr += comp_pos; + ptr += pos; + + } else if (dns_type == ns_t_a || dns_type == ns_t_aaaa) { + dlen = uptr[-2] << 8 | uptr[-1]; + + if (ptr + dlen > end) { + debug("data len %d too long", dlen); + goto out; + } + + memcpy(uptr, ptr, dlen); + uptr += dlen; + ptr += dlen; + + } else if (dns_type == ns_t_soa) { + int total_len = 0; + char *len_ptr; + + /* Primary name server expansion */ + if (!convert_label(start, end, ptr, uptr, + uncomp_len - (uptr - uncompressed), + &pos, &comp_pos)) + goto out; + + total_len += comp_pos; + len_ptr = &uptr[-2]; + ptr += pos; + uptr += comp_pos; + + /* Responsible authority's mailbox */ + if (!convert_label(start, end, ptr, uptr, + uncomp_len - (uptr - uncompressed), + &pos, &comp_pos)) + goto out; + + total_len += comp_pos; + ptr += pos; + uptr += comp_pos; + + /* + * Copy rest of the soa fields (serial number, + * refresh interval, retry interval, expiration + * limit and minimum ttl). They are 20 bytes long. + */ + memcpy(uptr, ptr, 20); + uptr += 20; + ptr += 20; + total_len += 20; + + /* + * Finally fix the length of the data part + */ + len_ptr[0] = total_len << 8; + len_ptr[1] = total_len & 0xff; + } + + *uncompressed_ptr = uptr; + } + + return ptr; + +out: + return NULL; +} + +static int strip_domains(char *name, char *answers, int maxlen) +{ + uint16_t data_len; + int name_len = strlen(name); + char *ptr, *start = answers, *end = answers + maxlen; + + while (maxlen > 0) { + ptr = strstr(answers, name); + if (ptr) { + char *domain = ptr + name_len; + + if (*domain) { + int domain_len = strlen(domain); + + memmove(answers + name_len, + domain + domain_len, + end - (domain + domain_len)); + + end -= domain_len; + maxlen -= domain_len; + } + } + + answers += strlen(answers) + 1; + answers += 2 + 2 + 4; /* skip type, class and ttl fields */ + + data_len = answers[0] << 8 | answers[1]; + answers += 2; /* skip the length field */ + + if (answers + data_len > end) + return -EINVAL; + + answers += data_len; + maxlen -= answers - ptr; + } + + return end - start; +} + static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, struct server_data *data) { @@ -1725,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 == NULL) + 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; @@ -1739,89 +2011,219 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, req->numresp++; - if (hdr->rcode == 0 || req->resp == NULL) { + if (hdr->rcode == ns_r_noerror || !req->resp) { + unsigned char *new_reply = NULL; /* * If the domain name was append * remove it before forwarding the reply. + * If there were more than one question, then this + * domain name ripping can be hairy so avoid that + * and bail out in that that case. + * + * The reason we are doing this magic is that if the + * user's DNS client tries to resolv hostname without + * domain part, it also expects to get the result without + * a domain name part. */ - if (req->append_domain == TRUE) { - unsigned int domain_len = 0; - unsigned char *ptr; - uint8_t host_len; - unsigned int header_len; + if (req->append_domain && ntohs(hdr->qdcount) == 1) { + uint16_t domain_len = 0; + uint16_t header_len; + uint16_t dns_type, dns_class; + uint8_t host_len, dns_type_pos; + char uncompressed[NS_MAXDNAME], *uptr; + char *ptr, *eom = (char *)reply + reply_len; /* * ptr points to the first char of the hostname. * ->hostname.domain.net */ header_len = offset + sizeof(struct domain_hdr); - ptr = reply + header_len; + ptr = (char *)reply + header_len; + host_len = *ptr; if (host_len > 0) - domain_len = strnlen((const char *)ptr + 1 + - host_len, + domain_len = strnlen(ptr + 1 + host_len, reply_len - header_len); - - DBG("host len %d domain len %d", host_len, domain_len); + /* + * If the query type is anything other than A or AAAA, + * then bail out and pass the message as is. + * We only want to deal with IPv4 or IPv6 addresses. + */ + dns_type_pos = host_len + 1 + domain_len + 1; + + dns_type = ptr[dns_type_pos] << 8 | + ptr[dns_type_pos + 1]; + dns_class = ptr[dns_type_pos + 2] << 8 | + ptr[dns_type_pos + 3]; + if (dns_type != ns_t_a && dns_type != ns_t_aaaa && + dns_class != ns_c_in) { + debug("Pass msg dns type %d class %d", + dns_type, dns_class); + goto pass; + } /* * Remove the domain name and replace it by the end * of reply. Check if the domain is really there - * before trying to copy the data. The domain_len can - * be 0 because if the original query did not contain - * a domain name, then we are sending two packets, - * first without the domain name and the second packet - * with domain name. The append_domain is set to true - * even if we sent the first packet without domain - * name. In this case we end up in this branch. + * before trying to copy the data. We also need to + * uncompress the answers if necessary. + * The domain_len can be 0 because if the original + * query did not contain a domain name, then we are + * sending two packets, first without the domain name + * and the second packet with domain name. + * The append_domain is set to true even if we sent + * the first packet without domain name. In this + * case we end up in this branch. */ if (domain_len > 0) { + int len = host_len + 1; + int new_len, fixed_len; + char *answers; + + /* + * First copy host (without domain name) into + * tmp buffer. + */ + uptr = &uncompressed[0]; + memcpy(uptr, ptr, len); + + uptr[len] = '\0'; /* host termination */ + uptr += len + 1; + + /* + * Copy type and class fields of the question. + */ + ptr += len + domain_len + 1; + memcpy(uptr, ptr, NS_QFIXEDSZ); + + /* + * ptr points to answers after this + */ + ptr += NS_QFIXEDSZ; + uptr += NS_QFIXEDSZ; + answers = uptr; + fixed_len = answers - uncompressed; + + /* + * We then uncompress the result to buffer + * so that we can rip off the domain name + * part from the question. First answers, + * then name server (authority) information, + * and finally additional record info. + */ + + ptr = uncompress(ntohs(hdr->ancount), + (char *)reply + offset, eom, + ptr, uncompressed, NS_MAXDNAME, + &uptr); + if (!ptr) + goto out; + + ptr = uncompress(ntohs(hdr->nscount), + (char *)reply + offset, eom, + ptr, uncompressed, NS_MAXDNAME, + &uptr); + if (!ptr) + goto out; + + ptr = uncompress(ntohs(hdr->arcount), + (char *)reply + offset, eom, + ptr, uncompressed, NS_MAXDNAME, + &uptr); + if (!ptr) + goto out; + /* - * Note that we must use memmove() here, - * because the memory areas can overlap. + * The uncompressed buffer now contains almost + * valid response. Final step is to get rid of + * the domain name because at least glibc + * gethostbyname() implementation does extra + * checks and expects to find an answer without + * domain name if we asked a query without + * domain part. Note that glibc getaddrinfo() + * works differently and accepts FQDN in answer + */ + new_len = strip_domains(uncompressed, answers, + uptr - answers); + if (new_len < 0) { + debug("Corrupted packet"); + return -EINVAL; + } + + /* + * Because we have now uncompressed the answers + * we might have to create a bigger buffer to + * hold all that data. */ - memmove(ptr + host_len + 1, - ptr + host_len + domain_len + 1, - reply_len - header_len - domain_len); - reply_len = reply_len - domain_len; + reply_len = header_len + new_len + fixed_len; + + new_reply = g_try_malloc(reply_len); + if (!new_reply) + return -ENOMEM; + + memcpy(new_reply, reply, header_len); + memcpy(new_reply + header_len, uncompressed, + new_len + fixed_len); + + reply = new_reply; } } + pass: g_free(req->resp); req->resplen = 0; req->resp = g_try_malloc(reply_len); - if (req->resp == NULL) +#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; cache_update(data, reply, reply_len); + + g_free(new_reply); } - if (hdr->rcode > 0 && req->numresp < req->numserv) - return -EINVAL; +out: + if (req->numresp < req->numserv) { + if (hdr->rcode > ns_r_noerror) { + return -EINVAL; + } else if (hdr->ancount == 0 && req->append_domain) { + return -EINVAL; + } + } request_list = g_slist_remove(request_list, req); if (protocol == IPPROTO_UDP) { sk = get_req_udp_socket(req); - err = sendto(sk, req->resp, req->resplen, 0, - &req->sa, req->sa_len); + if (sk < 0) { + errno = -EIO; + err = -EIO; + } else + err = sendto(sk, req->resp, req->resplen, 0, + &req->sa, req->sa_len); } else { sk = req->client_sk; err = send(sk, req->resp, req->resplen, MSG_NOSIGNAL); } 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); @@ -1830,7 +2232,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol, 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) { @@ -1843,7 +2245,7 @@ static void server_destroy_socket(struct server_data *data) data->timeout = 0; } - if (data->channel != NULL) { + if (data->channel) { g_io_channel_shutdown(data->channel, TRUE, NULL); g_io_channel_unref(data->channel); data->channel = NULL; @@ -1855,25 +2257,18 @@ static void server_destroy_socket(struct server_data *data) static void destroy_server(struct server_data *server) { - GList *list; - - DBG("index %d server %s sock %d", server->index, server->server, - server->channel != NULL ? + debug("index %d server %s sock %d", server->index, server->server, + server->channel ? g_io_channel_unix_get_fd(server->channel): -1); server_list = g_slist_remove(server_list, 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); - for (list = server->domains; list; list = list->next) { - char *domain = list->data; - - server->domains = g_list_remove(server->domains, domain); - g_free(domain); - } + g_list_free_full(server->domains, g_free); g_free(server->server_addr); /* @@ -1885,7 +2280,8 @@ static void destroy_server(struct server_data *server) * without any good reason. The small delay allows the new RDNSS to * create a new DNS server instance and the refcount does not go to 0. */ - g_timeout_add_seconds(3, try_remove_cache, NULL); + if (cache && !cache_timer) + cache_timer = g_timeout_add_seconds(3, try_remove_cache, NULL); g_free(server); } @@ -1894,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)) { @@ -1906,12 +2302,22 @@ 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; + + for (list = server_list_sec; list; list = list->next) { + struct server_data *new_data = list->data; + + if (new_data == data) { + destroy_server_sec(data); + return TRUE; + } + } +#endif return TRUE; } @@ -1929,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 @@ -1938,14 +2344,16 @@ 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; - if (req->request == NULL) + if (!req->request) continue; /* @@ -1972,17 +2380,17 @@ hangup: if ((condition & G_IO_OUT) && !server->connected) { GSList *list; GList *domains; - int no_request_sent = TRUE; + bool no_request_sent = true; struct server_data *udp_server; udp_server = find_server(server->index, server->server, IPPROTO_UDP); - if (udp_server != NULL) { + if (udp_server) { for (domains = udp_server->domains; domains; 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, @@ -1990,7 +2398,7 @@ hangup: } } - server->connected = TRUE; + server->connected = true; server_list = g_slist_append(server_list, server); if (server->timeout > 0) { @@ -2007,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); @@ -2027,7 +2435,7 @@ hangup: continue; } - no_request_sent = FALSE; + no_request_sent = false; if (req->timeout > 0) g_source_remove(req->timeout); @@ -2037,7 +2445,7 @@ hangup: list = list->next; } - if (no_request_sent == TRUE) { + if (no_request_sent) { destroy_server(server); return FALSE; } @@ -2066,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) @@ -2113,9 +2521,9 @@ static gboolean tcp_idle_timeout(gpointer user_data) { struct server_data *server = user_data; - DBG(""); + debug(""); - if (server == NULL) + if (!server) return FALSE; destroy_server(server); @@ -2128,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, @@ -2142,10 +2550,10 @@ 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 != NULL) { + if (interface) { if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, interface, strlen(interface) + 1) < 0) { @@ -2162,7 +2570,7 @@ static int server_create_socket(struct server_data *data) } data->channel = g_io_channel_unix_new(sk); - if (data->channel == NULL) { + if (!data->channel) { connman_error("Failed to create server %s channel", data->server); close(sk); @@ -2202,38 +2610,228 @@ static int server_create_socket(struct server_data *data) return 0; } -static struct server_data *create_server(int index, - const char *domain, const char *server, - int protocol) +static void enable_fallback(bool enable) { - struct server_data *data; - struct addrinfo hints, *rp; - int ret; + GSList *list; - DBG("index %d server %s", index, server); + for (list = server_list; list; list = list->next) { + struct server_data *data = list->data; - data = g_try_new0(struct server_data, 1); - if (data == NULL) { - connman_error("Failed to allocate server %s data", server); - return NULL; - } + if (data->index != -1) + continue; - data->index = index; - if (domain) - data->domains = g_list_append(data->domains, g_strdup(domain)); - data->server = g_strdup(server); - data->protocol = protocol; + if (enable) + DBG("Enabling fallback DNS server %s", data->server); + else + DBG("Disabling fallback DNS server %s", data->server); - memset(&hints, 0, sizeof(hints)); + data->enabled = enable; + } +} - switch (protocol) { - case IPPROTO_UDP: - hints.ai_socktype = SOCK_DGRAM; - break; +#if defined TIZEN_EXT - case IPPROTO_TCP: - hints.ai_socktype = SOCK_STREAM; - break; +static void destroy_server_sec(struct server_data *server) +{ + GList *list; + int fd; + + if (server->channel) + fd = g_io_channel_unix_get_fd(server->channel); + else + fd = -1; + + DBG("index %d server %s sock %d", server->index, server->server, fd); + + server_list_sec = g_slist_remove(server_list_sec, server); + + if (fd > 0) + close(fd); + + server_destroy_socket(server); + + if (server->protocol == IPPROTO_UDP && server->enabled) + DBG("Removing DNS server %s", server->server); + + g_free(server->server); + for (list = server->domains; list; list = list->next) { + char *domain = list->data; + + server->domains = g_list_remove(server->domains, domain); + g_free(domain); + } + g_free(server->server_addr); + + /* + * We do not remove cache right away but delay it few seconds. + * The idea is that when IPv6 DNS server is added via RDNSS, it has a + * lifetime. When the lifetime expires we decrease the refcount so it + * is possible that the cache is then removed. Because a new DNS server + * is usually created almost immediately we would then loose the cache + * without any good reason. The small delay allows the new RDNSS to + * create a new DNS server instance and the refcount does not go to 0. + */ + /* TODO: Need to check this */ + /* g_timeout_add_seconds(3, try_remove_cache, NULL); */ + + g_free(server); +} + +static void destroy_all_server_sec() +{ + GSList *list; + + DBG("remove all dns server"); + + for (list = server_list_sec; list; list = list->next) { + struct server_data *server = list->data; + destroy_server_sec(server); + } + server_list_sec = NULL; +} + +static gboolean sec_udp_idle_timeout(gpointer user_data) +{ + struct server_data *server = user_data; + + DBG(""); + + if (server == NULL) + return FALSE; + + destroy_server_sec(server); + + return FALSE; +} + +static struct server_data *create_server_sec(int index, + const char *domain, const char *server, + int protocol) +{ + struct server_data *data; + struct addrinfo hints, *rp; + int ret; + + DBG("index %d server %s", index, server); + + data = g_try_new0(struct server_data, 1); + if (data == NULL) { + connman_error("Failed to allocate server %s data", server); + return NULL; + } + + data->index = index; + if (domain) + data->domains = g_list_append(data->domains, g_strdup(domain)); + data->server = g_strdup(server); + data->protocol = protocol; + + memset(&hints, 0, sizeof(hints)); + + switch (protocol) { + case IPPROTO_UDP: + hints.ai_socktype = SOCK_DGRAM; + break; + + case IPPROTO_TCP: + hints.ai_socktype = SOCK_STREAM; + break; + + default: + destroy_server_sec(data); + return NULL; + } + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST; + + ret = getaddrinfo(data->server, "53", &hints, &rp); + if (ret) { + connman_error("Failed to parse server %s address: %s\n", + data->server, gai_strerror(ret)); + freeaddrinfo(rp); + destroy_server_sec(data); + return NULL; + } + + /* Do not blindly copy this code elsewhere; it doesn't loop over the + results using ->ai_next as it should. That's OK in *this* case + because it was a numeric lookup; we *know* there's only one. */ + + data->server_addr_len = rp->ai_addrlen; + + switch (rp->ai_family) { + case AF_INET: + data->server_addr = (struct sockaddr *) + g_try_new0(struct sockaddr_in, 1); + break; + case AF_INET6: + data->server_addr = (struct sockaddr *) + g_try_new0(struct sockaddr_in6, 1); + break; + default: + connman_error("Wrong address family %d", rp->ai_family); + break; + } + if (data->server_addr == NULL) { + freeaddrinfo(rp); + destroy_server_sec(data); + return NULL; + } + memcpy(data->server_addr, rp->ai_addr, rp->ai_addrlen); + freeaddrinfo(rp); + + if (server_create_socket(data) != 0) { + destroy_server_sec(data); + return NULL; + } + + if (protocol == IPPROTO_UDP) { + /* Enable new servers by default */ + data->enabled = TRUE; + DBG("Adding DNS server %s", data->server); + + data->timeout = g_timeout_add_seconds(30, sec_udp_idle_timeout, + data); + + server_list_sec = g_slist_append(server_list_sec, data); + } + + return data; +} +#endif + +static struct server_data *create_server(int index, + const char *domain, const char *server, + int protocol) +{ + struct server_data *data; + struct addrinfo hints, *rp; + int ret; + + DBG("index %d server %s", index, server); + + data = g_try_new0(struct server_data, 1); + if (!data) { + connman_error("Failed to allocate server %s data", server); + return NULL; + } + + data->index = index; + if (domain) + data->domains = g_list_append(data->domains, g_strdup(domain)); + data->server = g_strdup(server); + data->protocol = protocol; + + memset(&hints, 0, sizeof(hints)); + + switch (protocol) { + case IPPROTO_UDP: + hints.ai_socktype = SOCK_DGRAM; + break; + + case IPPROTO_TCP: + hints.ai_socktype = SOCK_STREAM; + break; default: destroy_server(data); @@ -2269,7 +2867,7 @@ static struct server_data *create_server(int index, connman_error("Wrong address family %d", rp->ai_family); break; } - if (data->server_addr == NULL) { + if (!data->server_addr) { freeaddrinfo(rp); destroy_server(data); return NULL; @@ -2283,9 +2881,14 @@ static struct server_data *create_server(int index, } if (protocol == IPPROTO_UDP) { - /* Enable new servers by default */ - data->enabled = TRUE; - DBG("Adding DNS server %s", data->server); + if (__connman_service_index_is_default(data->index) || + __connman_service_index_is_split_routing( + data->index)) { + data->enabled = true; + DBG("Adding DNS server %s", data->server); + + enable_fallback(false); + } server_list = g_slist_append(server_list, data); } @@ -2293,7 +2896,7 @@ static struct server_data *create_server(int index, return data; } -static gboolean resolv(struct request_data *req, +static bool resolv(struct request_data *req, gpointer request, gpointer name) { GSList *list; @@ -2306,12 +2909,12 @@ static gboolean 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 == FALSE) + if (!data->enabled) continue; - if (data->channel == NULL && data->protocol == IPPROTO_UDP) { + if (!data->channel && data->protocol == IPPROTO_UDP) { if (server_create_socket(data) < 0) { DBG("socket creation failed while resolving"); continue; @@ -2319,26 +2922,26 @@ static gboolean resolv(struct request_data *req, } if (ns_resolv(data, req, request, name) > 0) - return TRUE; + return true; } - return FALSE; + return false; } -static void append_domain(int index, const char *domain) +static void update_domain(int index, const char *domain, bool append) { GSList *list; DBG("index %d domain %s", index, domain); - if (domain == NULL) + if (!domain) return; for (list = server_list; list; list = list->next) { struct server_data *data = list->data; GList *dom_list; char *dom; - gboolean dom_found = FALSE; + bool dom_found = false; if (data->index < 0) continue; @@ -2351,15 +2954,57 @@ static void append_domain(int index, const char *domain) dom = dom_list->data; if (g_str_equal(dom, domain)) { - dom_found = TRUE; + dom_found = true; break; } } - if (dom_found == FALSE) { + 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; + + list = request_list; + while (list) { + struct request_data *req = list->data; + + list = list->next; + + if (ns_resolv(server, req, req->request, req->name)) { + /* + * A cached result was sent, + * so the request can be released + */ + request_list = + g_slist_remove(request_list, req); + destroy_request_data(req); + continue; } + + if (req->timeout > 0) + g_source_remove(req->timeout); + + req->timeout = g_timeout_add_seconds(5, request_timeout, req); } } @@ -2370,31 +3015,33 @@ int __connman_dnsproxy_append(int index, const char *domain, DBG("index %d server %s", index, server); - if (server == NULL && domain == NULL) + if (!server && !domain) return -EINVAL; - if (server == NULL) { + if (!server) { append_domain(index, domain); return 0; } - if (g_str_equal(server, "127.0.0.1") == TRUE) + if (g_str_equal(server, "127.0.0.1")) return -ENODEV; - if (g_str_equal(server, "::1") == TRUE) + if (g_str_equal(server, "::1")) return -ENODEV; data = find_server(index, server, IPPROTO_UDP); - if (data != NULL) { + if (data) { append_domain(index, domain); return 0; } data = create_server(index, domain, server, IPPROTO_UDP); - if (data == NULL) + if (!data) return -EIO; + flush_requests(data); + return 0; } @@ -2402,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 == NULL) + 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, @@ -2415,49 +3072,32 @@ int __connman_dnsproxy_remove(int index, const char *domain, { DBG("index %d server %s", index, server); - if (server == NULL) + if (!server && !domain) return -EINVAL; - if (g_str_equal(server, "127.0.0.1") == TRUE) + if (!server) { + remove_domain(index, domain); + + return 0; + } + + if (g_str_equal(server, "127.0.0.1")) return -ENODEV; - if (g_str_equal(server, "::1") == TRUE) + if (g_str_equal(server, "::1")) return -ENODEV; remove_server(index, domain, server, IPPROTO_UDP); remove_server(index, domain, server, IPPROTO_TCP); - return 0; -} - -void __connman_dnsproxy_flush(void) -{ - GSList *list; - - list = request_list; - while (list) { - struct request_data *req = list->data; - - list = list->next; - - if (resolv(req, req->request, req->name) == TRUE) { - /* - * A cached result was sent, - * so the request can be released - */ - request_list = - g_slist_remove(request_list, req); - destroy_request_data(req); - continue; - } +#if defined TIZEN_EXT + destroy_all_server_sec(); +#endif - if (req->timeout > 0) - g_source_remove(req->timeout); - req->timeout = g_timeout_add_seconds(5, request_timeout, req); - } + return 0; } -static void dnsproxy_offline_mode(connman_bool_t enabled) +static void dnsproxy_offline_mode(bool enabled) { GSList *list; @@ -2466,14 +3106,14 @@ static void dnsproxy_offline_mode(connman_bool_t enabled) for (list = server_list; list; list = list->next) { struct server_data *data = list->data; - if (enabled == FALSE) { + if (!enabled) { DBG("Enabling DNS server %s", data->server); - data->enabled = TRUE; + data->enabled = true; cache_invalidate(); cache_refresh(); } else { DBG("Disabling DNS server %s", data->server); - data->enabled = FALSE; + data->enabled = false; cache_invalidate(); } } @@ -2481,6 +3121,7 @@ static void dnsproxy_offline_mode(connman_bool_t enabled) static void dnsproxy_default_changed(struct connman_service *service) { + bool server_enabled = false; GSList *list; int index; @@ -2489,9 +3130,9 @@ static void dnsproxy_default_changed(struct connman_service *service) /* DNS has changed, invalidate the cache */ cache_invalidate(); - if (service == NULL) { + if (!service) { /* When no services are active, then disable DNS proxying */ - dnsproxy_offline_mode(TRUE); + dnsproxy_offline_mode(true); return; } @@ -2504,44 +3145,55 @@ static void dnsproxy_default_changed(struct connman_service *service) if (data->index == index) { DBG("Enabling DNS server %s", data->server); - data->enabled = TRUE; + data->enabled = true; + server_enabled = true; } else { DBG("Disabling DNS server %s", data->server); - data->enabled = FALSE; + data->enabled = false; } } + if (!server_enabled) + enable_fallback(true); + cache_refresh(); } -static struct connman_notifier dnsproxy_notifier = { +static const struct connman_notifier dnsproxy_notifier = { .name = "dnsproxy", .default_changed = dnsproxy_default_changed, .offline_mode = dnsproxy_offline_mode, }; -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; + } + + if (!name || !size) return -EINVAL; - DBG("id 0x%04x qr %d opcode %d qdcount %d arcount %d", + 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); @@ -2551,7 +3203,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; } @@ -2567,40 +3235,27 @@ static int parse_request(unsigned char *buf, int len, remain -= label_len + 1; } - if (last_label && arcount && remain >= 9 && last_label[4] == 0 && - !memcmp(last_label + 5, opt_edns0_type, 2)) { - uint16_t edns0_bufsize; + 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); - 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("EDNS0 buffer size %u", ntohs(edns0->class)); + } else if (!arcount && remain) { + DBG("DNS request with %d garbage bytes", remain); } - DBG("query %s", name); + debug("query %s", name); return 0; } static void client_reset(struct tcp_partial_client_data *client) { - if (client == NULL) + if (!client) return; - if (client->channel != NULL) { - DBG("client %d closing", + if (client->channel) { + debug("client %d closing", g_io_channel_unix_get_fd(client->channel)); g_io_channel_unref(client->channel); @@ -2628,7 +3283,7 @@ static unsigned int get_msg_len(unsigned char *buf) return buf[0]<<8 | buf[1]; } -static gboolean read_tcp_data(struct tcp_partial_client_data *client, +static bool read_tcp_data(struct tcp_partial_client_data *client, void *client_addr, socklen_t client_addr_len, int read_len) { @@ -2637,58 +3292,59 @@ static gboolean read_tcp_data(struct tcp_partial_client_data *client, int client_sk, err; unsigned int msg_len; GSList *list; - int waiting_for_connect = FALSE, qtype = 0; + bool waiting_for_connect = false; + int qtype = 0; struct cache_entry *entry; 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; + 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; if (client->buf_end < 2) - return TRUE; + return true; 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; + 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; + 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)); if (err < 0 || (g_slist_length(server_list) == 0)) { send_response(client_sk, client->buf, msg_len + 2, NULL, 0, IPPROTO_TCP); - return TRUE; + return true; } req = g_try_new0(struct request_data, 1); - if (req == NULL) - return TRUE; + if (!req) + return true; memcpy(&req->sa, client_addr, client_addr_len); req->sa_len = client_addr_len; @@ -2706,24 +3362,24 @@ read_another: req->numserv = 0; req->ifdata = client->ifdata; - req->append_domain = FALSE; + req->append_domain = false; /* * Check if the answer is found in the cache before * creating sockets to the server. */ entry = cache_check(client->buf, &qtype, IPPROTO_TCP); - if (entry != NULL) { + if (entry) { 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 data = entry->ipv6; - if (data != NULL) { + if (data) { ttl_left = data->valid_until - time(NULL); entry->hits++; @@ -2734,28 +3390,28 @@ 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) { struct server_data *data = list->data; - if (data->protocol != IPPROTO_UDP || data->enabled == FALSE) + if (data->protocol != IPPROTO_UDP || !data->enabled) continue; - if(create_server(data->index, NULL, - data->server, IPPROTO_TCP) == NULL) + if (!create_server(data->index, NULL, data->server, + IPPROTO_TCP)) continue; - waiting_for_connect = TRUE; + waiting_for_connect = true; } - if (waiting_for_connect == FALSE) { + if (!waiting_for_connect) { /* No server is waiting for connect */ send_response(client_sk, client->buf, req->request_len, NULL, 0, IPPROTO_TCP); g_free(req); - return TRUE; + return true; } /* @@ -2765,7 +3421,7 @@ read_another: * properly connected over TCP to the nameserver. */ req->request = g_try_malloc0(req->request_len); - if (req->request == NULL) { + if (!req->request) { send_response(client_sk, client->buf, req->request_len, NULL, 0, IPPROTO_TCP); g_free(req); @@ -2774,7 +3430,7 @@ read_another: memcpy(req->request, client->buf, req->request_len); req->name = g_try_malloc0(sizeof(query)); - if (req->name == NULL) { + if (!req->name) { send_response(client_sk, client->buf, req->request_len, NULL, 0, IPPROTO_TCP); g_free(req->request); @@ -2789,7 +3445,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, @@ -2805,12 +3461,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); @@ -2824,7 +3480,7 @@ out: client->timeout = 0; } - return TRUE; + return true; } static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition, @@ -2872,7 +3528,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)); @@ -2889,14 +3545,31 @@ 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)); return FALSE; } -static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition, +#if defined TIZEN_EXT +static void recover_listener(GIOChannel *channel, struct listener_data *ifdata) +{ + int sk, index; + + index = ifdata->index; + + sk = g_io_channel_unix_get_fd(channel); + close(sk); + + __connman_dnsproxy_remove_listener(index); + + if (__connman_dnsproxy_add_listener(index) == 0) + DBG("listener %d successfully recovered", index); +} +#endif + +static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition, struct listener_data *ifdata, int family, guint *listener_watch) { @@ -2912,17 +3585,23 @@ static gboolean 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)) { +#if defined TIZEN_EXT + connman_error("Error %d with TCP listener channel", condition); + + recover_listener(channel, ifdata); +#else if (*listener_watch > 0) g_source_remove(*listener_watch); *listener_watch = 0; connman_error("Error with TCP listener channel"); +#endif - return FALSE; + return false; } sk = g_io_channel_unix_get_fd(channel); @@ -2942,26 +3621,28 @@ static gboolean 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); - return TRUE; + debug("No data to read from master %d, waiting.", sk); + return true; } if (client_sk < 0) { connman_error("Accept failure on TCP listener"); *listener_watch = 0; - return FALSE; + return false; } fcntl(client_sk, F_SETFL, O_NONBLOCK); client = g_hash_table_lookup(partial_tcp_req_table, GINT_TO_POINTER(client_sk)); - if (client == NULL) { + if (!client) { client = g_try_new0(struct tcp_partial_client_data, 1); - if (client == NULL) - return FALSE; + if (!client) { + close(client_sk); + return false; + } g_hash_table_insert(partial_tcp_req_table, GINT_TO_POINTER(client_sk), @@ -2976,15 +3657,15 @@ static gboolean 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 == NULL) { + if (!client->buf) { client->buf = g_try_malloc(TCP_MAX_BUF_LEN); - if (client->buf == NULL) - return FALSE; + if (!client->buf) + return false; } memset(client->buf, 0, TCP_MAX_BUF_LEN); client->buf_end = 0; @@ -3002,42 +3683,46 @@ static gboolean 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); - return TRUE; + 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)); - return TRUE; + return true; } 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; + 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)); - return TRUE; + return true; } /* * The packet length bytes do not contain the total message length, * that is the reason to -2 below. */ +#if defined TIZEN_EXT + if (msg_len > (unsigned int)(len - 2)) { +#else if (msg_len != (unsigned int)(len - 2)) { - DBG("client %d sent %d bytes but expecting %u pending %d", +#endif + 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; - return TRUE; + return true; } return read_tcp_data(client, client_addr, *client_addr_len, len); @@ -3061,7 +3746,7 @@ static gboolean tcp6_listener_event(GIOChannel *channel, GIOCondition condition, &ifdata->tcp6_listener_watch); } -static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition, +static bool udp_listener_event(GIOChannel *channel, GIOCondition condition, struct listener_data *ifdata, int family, guint *listener_watch) { @@ -3077,9 +3762,15 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition, int sk, err, len; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { +#if defined TIZEN_EXT + connman_error("Error %d with UDP listener channel", condition); + + recover_listener(channel, ifdata); +#else connman_error("Error with UDP listener channel"); *listener_watch = 0; - return FALSE; +#endif + return false; } sk = g_io_channel_unix_get_fd(channel); @@ -3095,20 +3786,20 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition, memset(client_addr, 0, *client_addr_len); len = recvfrom(sk, buf, sizeof(buf), 0, client_addr, client_addr_len); if (len < 2) - return TRUE; + 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)) { send_response(sk, buf, len, client_addr, *client_addr_len, IPPROTO_UDP); - return TRUE; + return true; } req = g_try_new0(struct request_data, 1); - if (req == NULL) - return TRUE; + if (!req) + return true; memcpy(&req->sa, client_addr, *client_addr_len); req->sa_len = *client_addr_len; @@ -3126,18 +3817,26 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition, req->numserv = 0; req->ifdata = ifdata; - req->append_domain = FALSE; + req->append_domain = false; - if (resolv(req, buf, query) == TRUE) { + if (resolv(req, buf, query)) { /* a cached result was sent, so the request can be released */ g_free(req); - return TRUE; + return true; } + req->name = g_strdup(query); + req->request = g_malloc(len); + memcpy(req->request, buf, len); +#if defined TIZEN_EXT + DBG("req %p dstid 0x%04x altid 0x%04x", req, req->dstid, req->altid); + req->timeout = g_timeout_add_seconds(30, request_timeout, req); +#else req->timeout = g_timeout_add_seconds(5, request_timeout, req); +#endif request_list = g_slist_append(request_list, req); - return TRUE; + return true; } static gboolean udp4_listener_event(GIOChannel *channel, GIOCondition condition, @@ -3162,16 +3861,26 @@ static GIOChannel *get_listener(int family, int protocol, int index) { GIOChannel *channel; const char *proto; +#if !defined TIZEN_EXT union { struct sockaddr sa; struct sockaddr_in6 sin6; struct sockaddr_in sin; } s; socklen_t slen; +#endif int sk, type; +#if !defined TIZEN_EXT char *interface; +#endif +#if defined TIZEN_EXT + 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: @@ -3187,7 +3896,33 @@ static GIOChannel *get_listener(int family, int protocol, int index) default: return NULL; } +#if defined TIZEN_EXT + sd_num = sd_listen_fds(0); + DBG("socket type(%s) systemd number of fds(%d)", proto, sd_num); + if(sd_num < 1){ + DBG("fail to get the fd from systemd"); + return NULL; + } + + if(protocol == IPPROTO_TCP) + type = SOCK_STREAM; + else + type = SOCK_DGRAM; + + for(sk = SD_LISTEN_FDS_START; sk < SD_LISTEN_FDS_START+sd_num; ++sk){ + 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 (!is_socket_inet) { + DBG("socket fd is not matched what connman requests"); + return NULL; + } +#else sk = socket(family, type, protocol); if (sk < 0 && family == AF_INET6 && errno == EAFNOSUPPORT) { connman_error("No IPv6 support"); @@ -3200,7 +3935,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) } interface = connman_inet_ifname(index); - if (interface == NULL || setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, + if (!interface || setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, interface, strlen(interface) + 1) < 0) { connman_error("Failed to bind %s listener interface " @@ -3246,15 +3981,29 @@ static GIOChannel *get_listener(int family, int protocol, int index) close(sk); return NULL; } +#endif +#if defined TIZEN_EXT + /* When ConnMan crashed, + * probably DNS listener cannot bind existing address */ + option = 1; + 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) { connman_error("Failed to bind %s listener socket", proto); close(sk); return NULL; } +#endif if (protocol == IPPROTO_TCP) { +#if !defined TIZEN_EXT if (listen(sk, 10) < 0) { connman_error("Failed to listen on TCP socket %d/%s", -errno, strerror(errno)); @@ -3262,11 +4011,12 @@ static GIOChannel *get_listener(int family, int protocol, int index) return NULL; } +#endif fcntl(sk, F_SETFL, O_NONBLOCK); } channel = g_io_channel_unix_new(sk); - if (channel == NULL) { + if (!channel) { connman_error("Failed to create %s listener channel", proto); close(sk); return NULL; @@ -3293,41 +4043,69 @@ static int create_dns_listener(int protocol, struct listener_data *ifdata) if (protocol == IPPROTO_TCP) { ifdata->tcp4_listener_channel = get_listener(AF_INET, protocol, ifdata->index); - if (ifdata->tcp4_listener_channel != NULL) + if (ifdata->tcp4_listener_channel) +#if defined TIZEN_EXT + ifdata->tcp4_listener_watch = + g_io_add_watch(ifdata->tcp4_listener_channel, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + tcp4_listener_event, (gpointer)ifdata); +#else ifdata->tcp4_listener_watch = g_io_add_watch(ifdata->tcp4_listener_channel, G_IO_IN, tcp4_listener_event, (gpointer)ifdata); +#endif else ret |= TCP_IPv4_FAILED; ifdata->tcp6_listener_channel = get_listener(AF_INET6, protocol, ifdata->index); - if (ifdata->tcp6_listener_channel != NULL) + if (ifdata->tcp6_listener_channel) +#if defined TIZEN_EXT + ifdata->tcp6_listener_watch = + g_io_add_watch(ifdata->tcp6_listener_channel, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + tcp6_listener_event, (gpointer)ifdata); +#else ifdata->tcp6_listener_watch = g_io_add_watch(ifdata->tcp6_listener_channel, G_IO_IN, tcp6_listener_event, (gpointer)ifdata); +#endif else ret |= TCP_IPv6_FAILED; } else { ifdata->udp4_listener_channel = get_listener(AF_INET, protocol, ifdata->index); - if (ifdata->udp4_listener_channel != NULL) + if (ifdata->udp4_listener_channel) +#if defined TIZEN_EXT + ifdata->udp4_listener_watch = + g_io_add_watch(ifdata->udp4_listener_channel, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + udp4_listener_event, (gpointer)ifdata); +#else ifdata->udp4_listener_watch = g_io_add_watch(ifdata->udp4_listener_channel, G_IO_IN, udp4_listener_event, (gpointer)ifdata); +#endif else ret |= UDP_IPv4_FAILED; ifdata->udp6_listener_channel = get_listener(AF_INET6, protocol, ifdata->index); - if (ifdata->udp6_listener_channel != NULL) + if (ifdata->udp6_listener_channel) +#if defined TIZEN_EXT + ifdata->udp6_listener_watch = + g_io_add_watch(ifdata->udp6_listener_channel, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + udp6_listener_event, (gpointer)ifdata); +#else ifdata->udp6_listener_watch = g_io_add_watch(ifdata->udp6_listener_channel, G_IO_IN, udp6_listener_event, (gpointer)ifdata); +#endif else ret |= UDP_IPv6_FAILED; } @@ -3345,8 +4123,10 @@ static void destroy_udp_listener(struct listener_data *ifdata) if (ifdata->udp6_listener_watch > 0) g_source_remove(ifdata->udp6_listener_watch); - g_io_channel_unref(ifdata->udp4_listener_channel); - g_io_channel_unref(ifdata->udp6_listener_channel); + if (ifdata->udp4_listener_channel) + g_io_channel_unref(ifdata->udp4_listener_channel); + if (ifdata->udp6_listener_channel) + g_io_channel_unref(ifdata->udp6_listener_channel); } static void destroy_tcp_listener(struct listener_data *ifdata) @@ -3358,8 +4138,10 @@ static void destroy_tcp_listener(struct listener_data *ifdata) if (ifdata->tcp6_listener_watch > 0) g_source_remove(ifdata->tcp6_listener_watch); - g_io_channel_unref(ifdata->tcp4_listener_channel); - g_io_channel_unref(ifdata->tcp6_listener_channel); + if (ifdata->tcp4_listener_channel) + g_io_channel_unref(ifdata->tcp4_listener_channel); + if (ifdata->tcp6_listener_channel) + g_io_channel_unref(ifdata->tcp6_listener_channel); } static int create_listener(struct listener_data *ifdata) @@ -3402,7 +4184,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; @@ -3425,14 +4207,14 @@ int __connman_dnsproxy_add_listener(int index) if (index < 0) return -EINVAL; - if (listener_table == NULL) + if (!listener_table) return -ENOENT; - if (g_hash_table_lookup(listener_table, GINT_TO_POINTER(index)) != NULL) + if (g_hash_table_lookup(listener_table, GINT_TO_POINTER(index))) return 0; ifdata = g_try_new0(struct listener_data, 1); - if (ifdata == NULL) + if (!ifdata) return -ENOMEM; ifdata->index = index; @@ -3463,11 +4245,11 @@ void __connman_dnsproxy_remove_listener(int index) DBG("index %d", index); - if (listener_table == NULL) + if (!listener_table) return; ifdata = g_hash_table_lookup(listener_table, GINT_TO_POINTER(index)); - if (ifdata == NULL) + if (!ifdata) return; destroy_listener(ifdata); @@ -3499,8 +4281,6 @@ int __connman_dnsproxy_init(void) DBG(""); - srandom(time(NULL)); - listener_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); @@ -3528,10 +4308,25 @@ destroy: return err; } +int __connman_dnsproxy_set_mdns(int index, bool enabled) +{ + return -ENOTSUP; +} + void __connman_dnsproxy_cleanup(void) { DBG(""); + if (cache_timer) { + g_source_remove(cache_timer); + cache_timer = 0; + } + + if (cache) { + g_hash_table_destroy(cache); + cache = NULL; + } + connman_notifier_unregister(&dnsproxy_notifier); g_hash_table_foreach(listener_table, remove_listener, NULL); @@ -3539,4 +4334,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); }