X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fdnsproxy.c;h=a7bf87a144356ec870df9b2f1f3bbb039d86d515;hb=dd3cccc5e67548dcc2dd6c6254ed6c97859085d5;hp=40b4f1594ebfa6594b95ace4bf31e9f4d8a714a0;hpb=9362752a471a5c892d679548fbf2828d5fc5684b;p=platform%2Fupstream%2Fconnman.git diff --git a/src/dnsproxy.c b/src/dnsproxy.c index 40b4f15..a7bf87a 100644 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -79,6 +79,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; @@ -448,7 +453,7 @@ 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 @@ -470,7 +475,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len, 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) { @@ -482,21 +487,26 @@ static void send_response(int sk, unsigned char *buf, int len, if (offset < 0) return; - if (len < 12) + if (len < sizeof(*hdr) + offset) return; hdr = (void *) (buf + offset); + if (offset) { + buf[0] = 0; + buf[1] = sizeof(*hdr); + } debug("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode); hdr->qr = 1; hdr->rcode = ns_r_servfail; + hdr->qdcount = 0; hdr->ancount = 0; hdr->nscount = 0; hdr->arcount = 0; - err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen); + err = sendto(sk, buf, sizeof(*hdr) + offset, MSG_NOSIGNAL, to, tolen); if (err < 0) { connman_error("Failed to send DNS response to %d: %s", sk, strerror(errno)); @@ -2221,7 +2231,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)) { @@ -2233,12 +2243,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); return TRUE; } @@ -2905,34 +2912,77 @@ static void dnsproxy_default_changed(struct connman_service *service) 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; + } + + 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); @@ -2942,7 +2992,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; } @@ -2958,26 +3024,13 @@ 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]; - - debug("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); } debug("query %s", name); @@ -3927,6 +3980,11 @@ destroy: return err; } +int __connman_dnsproxy_set_mdns(int index, bool enabled) +{ + return -ENOTSUP; +} + void __connman_dnsproxy_cleanup(void) { DBG(""); @@ -3948,4 +4006,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); }