Merge tag 'upstream/1.40' into tizen.
[platform/upstream/connman.git] / src / dnsproxy.c
index b244a02..18dc648 100755 (executable)
@@ -472,7 +472,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
@@ -1819,6 +1819,7 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                        char **uncompressed_ptr)
 {
        char *uptr = *uncompressed_ptr; /* position in result buffer */
+       char * const uncomp_end = uncompressed + uncomp_len - 1;
 
        debug("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr);
 
@@ -1839,14 +1840,15 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                 * tmp buffer.
                 */
 
-               ulen = strlen(name);
-               strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
+               ulen = strlen(name) + 1;
+               if ((uptr + ulen) > uncomp_end)
+                       goto out;
+               strncpy(uptr, name, ulen);
 
                debug("pos %d ulen %d left %d name %s", pos, ulen,
-                       (int)(uncomp_len - (uptr - uncompressed)), uptr);
+                       (int)(uncomp_end - (uptr + ulen)), uptr);
 
                uptr += ulen;
-               *uptr++ = '\0';
 
                ptr += pos;
 
@@ -1854,6 +1856,10 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                 * We copy also the fixed portion of the result (type, class,
                 * ttl, address length and the address)
                 */
+               if ((uptr + NS_RRFIXEDSZ) > uncomp_end) {
+                       debug("uncompressed data too large for buffer");
+                       goto out;
+               }
                memcpy(uptr, ptr, NS_RRFIXEDSZ);
 
                dns_type = uptr[0] << 8 | uptr[1];
@@ -1885,7 +1891,7 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                } else if (dns_type == ns_t_a || dns_type == ns_t_aaaa) {
                        dlen = uptr[-2] << 8 | uptr[-1];
 
-                       if (ptr + dlen > end) {
+                       if ((ptr + dlen) > end || (uptr + dlen) > uncomp_end) {
                                debug("data len %d too long", dlen);
                                goto out;
                        }
@@ -1924,6 +1930,10 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                         * refresh interval, retry interval, expiration
                         * limit and minimum ttl). They are 20 bytes long.
                         */
+                       if ((uptr + 20) > uncomp_end || (ptr + 20) > end) {
+                               debug("soa record too long");
+                               goto out;
+                       }
                        memcpy(uptr, ptr, 20);
                        uptr += 20;
                        ptr += 20;
@@ -2177,8 +2187,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;
@@ -3117,6 +3134,7 @@ static void dnsproxy_default_changed(struct connman_service *service)
        bool server_enabled = false;
        GSList *list;
        int index;
+       int vpn_index;
 
        DBG("service %p", service);
 
@@ -3133,6 +3151,13 @@ static void dnsproxy_default_changed(struct connman_service *service)
        if (index < 0)
                return;
 
+       /*
+        * In case non-split-routed VPN is set as split routed the DNS servers
+        * the VPN must be enabled as well, when the transport becomes the
+        * default service.
+        */
+       vpn_index = __connman_connection_get_vpn_index(index);
+
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
 
@@ -3140,6 +3165,9 @@ static void dnsproxy_default_changed(struct connman_service *service)
                        DBG("Enabling DNS server %s", data->server);
                        data->enabled = true;
                        server_enabled = true;
+               } else if (data->index == vpn_index) {
+                       DBG("Enabling DNS server of VPN %s", data->server);
+                       data->enabled = true;
                } else {
                        DBG("Disabling DNS server %s", data->server);
                        data->enabled = false;
@@ -3152,10 +3180,46 @@ static void dnsproxy_default_changed(struct connman_service *service)
        cache_refresh();
 }
 
+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 const unsigned char opt_edns0_type[2] = { 0x00, 0x29 };
@@ -3739,6 +3803,39 @@ static gboolean tcp6_listener_event(GIOChannel *channel, GIOCondition condition,
                                &ifdata->tcp6_listener_watch);
 }
 
+#if defined TIZEN_EXT
+struct request_data * create_request(int sk, unsigned char *buf, size_t len,
+                                const struct sockaddr *to, socklen_t tolen,
+                                int protocol)
+{
+       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;
+
+}
+static gboolean send_response_timeout (gpointer user_data)
+{
+       struct request_data *req = user_data;
+
+       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);
+
+       return FALSE;
+
+}
+#endif
+
 static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
                                struct listener_data *ifdata, int family,
                                guint *listener_watch)
@@ -3785,8 +3882,22 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
 
        err = parse_request(buf, len, query, sizeof(query));
        if (err < 0 || (g_slist_length(server_list) == 0)) {
+#if defined TIZEN_EXT
+               /** 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;
        }