]> git.ipfire.org Git - thirdparty/ldns.git/commitdiff
Finalize source address binding from drill
authorWillem Toorop <willem@NLnetLabs.nl>
Tue, 19 Mar 2013 15:35:40 +0000 (15:35 +0000)
committerWillem Toorop <willem@NLnetLabs.nl>
Tue, 19 Mar 2013 15:35:40 +0000 (15:35 +0000)
drill/drill.c
ldns/resolver.h
net.c
resolver.c

index cec7730eeea9f7d47dc507117a89bd380ef22586..f7893996d99599695d47849efe4d52cb6a68aa0a 100644 (file)
@@ -627,10 +627,17 @@ main(int argc, char *argv[])
                        ldns_resolver_set_dnssec_cd(res, true);
                        /* set dnssec implies udp_size of 4096 */
                        ldns_resolver_set_edns_udp_size(res, 4096);
-                       pkt = ldns_resolver_query(res, qname, type, clas, qflags);
-                       
+                       pkt = NULL;
+                       status = ldns_resolver_query_status(
+                                       &pkt, res, qname, type, clas, qflags);
+                       if (status != LDNS_STATUS_OK) {
+                               error("error sending query: %s",
+                                       ldns_get_errorstr_by_id(status));
+                       }
                        if (!pkt) {
-                               error("%s", "error pkt sending");
+                               if (status == LDNS_STATUS_OK) {
+                                       error("%s", "error pkt sending");
+                               }
                                result = EXIT_FAILURE;
                        } else {
                                if (verbosity >= 3) {
@@ -756,9 +763,17 @@ main(int argc, char *argv[])
                        }
                        
                        /* create a packet and set the RD flag on it */
-                       pkt = ldns_resolver_query(res, qname, type, clas, qflags);
+                       pkt = NULL;
+                       status = ldns_resolver_query_status(
+                                       &pkt, res, qname, type, clas, qflags);
+                       if (status != LDNS_STATUS_OK) {
+                               error("error sending query: %s",
+                                       ldns_get_errorstr_by_id(status));
+                       }
                        if (!pkt)  {
-                               error("%s", "pkt sending");
+                               if (status == LDNS_STATUS_OK) {
+                                       error("%s", "pkt sending");
+                               }
                                result = EXIT_FAILURE;
                        } else {
                                if (verbosity != -1) {
@@ -829,7 +844,15 @@ main(int argc, char *argv[])
                                        goto exit;
                                } else {
                                        /* create a packet and set the RD flag on it */
-                                       pkt = ldns_resolver_query(res, qname, type, clas, qflags);
+                                       pkt = NULL;
+                                       status = ldns_resolver_query_status(
+                                                       &pkt, res, qname,
+                                                       type, clas, qflags);
+                                       if (status != LDNS_STATUS_OK) {
+                                               error("error sending query: %s"
+                                                    , ldns_get_errorstr_by_id(
+                                                            status));
+                                       }
                                }
                        }
                        
index 7423425f8d6c6bfd6f7c6c30f24671c8debdf511..b0e174e9fd35835674841d07b54eec53db1bf9b0 100644 (file)
@@ -600,6 +600,22 @@ ldns_status ldns_resolver_push_nameserver_rr_list(ldns_resolver *r, ldns_rr_list
  */
 ldns_pkt* ldns_resolver_search(const ldns_resolver *r, const ldns_rdf *rdf, ldns_rr_type t, ldns_rr_class c, uint16_t flags);
 
+
+/**
+ * Send the query for using the resolver and take the search list into account
+ * The search algorithm is as follows:
+ * If the name is absolute, try it as-is, otherwise apply the search list
+ * \param[out] pkt a packet with the reply from the nameserver
+ * \param[in] *r operate using this resolver
+ * \param[in] *rdf query for this name
+ * \param[in] t query for this type (may be 0, defaults to A)
+ * \param[in] c query for this class (may be 0, default to IN)
+ * \param[in] flags the query flags
+ *
+ * \return ldns_status LDNS_STATUS_OK on success
+ */
+ldns_status ldns_resolver_search_status(ldns_pkt** pkt, ldns_resolver *r, const ldns_rdf *rdf, ldns_rr_type t, ldns_rr_class c, uint16_t flags);
+
 /**
  * Form a query packet from a resolver and name/type/class combo
  * \param[out] **q a pointer to a ldns_pkt pointer (initialized by this function)
@@ -636,12 +652,29 @@ ldns_status ldns_resolver_send_pkt(ldns_pkt **answer, ldns_resolver *r, ldns_pkt
 
 /**
  * Send a query to a nameserver
+ * \param[out] pkt a packet with the reply from the nameserver
  * \param[in] *r operate using this resolver
  * \param[in] *name query for this name
  * \param[in] *t query for this type (may be 0, defaults to A)
  * \param[in] *c query for this class (may be 0, default to IN)
  * \param[in] flags the query flags
  *
+ * \return ldns_status LDNS_STATUS_OK on success
+ * if _defnames is true the default domain will be added
+ */
+ldns_status ldns_resolver_query_status(ldns_pkt** pkt, ldns_resolver *r, const ldns_rdf *name, ldns_rr_type t, ldns_rr_class c, uint16_t flags);
+
+
+/**
+ * Send a query to a nameserver
+ * \param[in] *r operate using this resolver 
+ *               (despite the const in the declaration,
+ *                the struct is altered as a side-effect)
+ * \param[in] *name query for this name
+ * \param[in] *t query for this type (may be 0, defaults to A)
+ * \param[in] *c query for this class (may be 0, default to IN)
+ * \param[in] flags the query flags
+ *
  * \return ldns_pkt* a packet with the reply from the nameserver
  * if _defnames is true the default domain will be added
  */
diff --git a/net.c b/net.c
index c55284493f5cea63bfc2d658dac4dc2ee7321ae5..78b35f00c5797c3c97ad74660e992da4f77c5410 100644 (file)
--- a/net.c
+++ b/net.c
@@ -56,185 +56,53 @@ ldns_send(ldns_pkt **result_packet, ldns_resolver *r, const ldns_pkt *query_pkt)
        return result;
 }
 
-ldns_status
-ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf *tsig_mac)
+/* code from rdata.c */
+static struct sockaddr_storage *
+ldns_rdf2native_sockaddr_storage_port(
+               const ldns_rdf *rd, uint16_t port, size_t *size)
 {
-       uint8_t i;
-
-       struct sockaddr_storage *src = NULL;
-       size_t src_len;
-       struct sockaddr_storage *ns;
-       size_t ns_len;
-       struct timeval tv_s;
-       struct timeval tv_e;
-
-       ldns_rdf **ns_array;
-       size_t *rtt;
-       ldns_pkt *reply;
-       bool all_servers_rtt_inf;
-       uint8_t retries;
-
-       uint8_t *reply_bytes = NULL;
-       size_t reply_size = 0;
-       ldns_status status, send_status;
-
-       assert(r != NULL);
-
-       status = LDNS_STATUS_OK;
-       rtt = ldns_resolver_rtt(r);
-       ns_array = ldns_resolver_nameservers(r);
-       reply = NULL; 
-       ns_len = 0;
-
-       all_servers_rtt_inf = true;
-
-       if (ldns_resolver_random(r)) {
-               ldns_resolver_nameservers_randomize(r);
-       }
-
-       if(ldns_resolver_source(r)) {
-               src = ldns_rdf2native_sockaddr_storage(ldns_resolver_source(r), 0, &src_len);
-       }
-
-       /* loop through all defined nameservers */
-       for (i = 0; i < ldns_resolver_nameserver_count(r); i++) {
-               if (rtt[i] == LDNS_RESOLV_RTT_INF) {
-                       /* not reachable nameserver! */
-                       continue;
-               }
-
-               /* maybe verbosity setting?
-               printf("Sending to ");
-               ldns_rdf_print(stdout, ns_array[i]);
-               printf("\n");
-               */
-               ns = ldns_rdf2native_sockaddr_storage(ns_array[i],
-                               ldns_resolver_port(r), &ns_len);
+        struct sockaddr_storage *data;
+        struct sockaddr_in  *data_in;
+        struct sockaddr_in6 *data_in6;
 
+        data = LDNS_MALLOC(struct sockaddr_storage);
+        if (!data) {
+                return NULL;
+        }
+       /* zero the structure for portability */
+       memset(data, 0, sizeof(struct sockaddr_storage));
 
+        switch(ldns_rdf_get_type(rd)) {
+                case LDNS_RDF_TYPE_A:
 #ifndef S_SPLINT_S
-               if ((ns->ss_family == AF_INET) &&
-                               (ldns_resolver_ip6(r) == LDNS_RESOLV_INET6)) {
-                       /* not reachable */
-                       LDNS_FREE(ns);
-                       continue;
-               }
-
-               if ((ns->ss_family == AF_INET6) &&
-                                (ldns_resolver_ip6(r) == LDNS_RESOLV_INET)) {
-                       /* not reachable */
-                       LDNS_FREE(ns);
-                       continue;
-               }
+                        data->ss_family = AF_INET;
 #endif
+                        data_in = (struct sockaddr_in*) data;
+                        data_in->sin_port = (in_port_t)htons(port);
+                        memcpy(&(data_in->sin_addr), ldns_rdf_data(rd), ldns_rdf_size(rd));
+                        *size = sizeof(struct sockaddr_in);
+                        return data;
+                case LDNS_RDF_TYPE_AAAA:
+#ifndef S_SPLINT_S
+                        data->ss_family = AF_INET6;
+#endif
+                        data_in6 = (struct sockaddr_in6*) data;
+                        data_in6->sin6_port = (in_port_t)htons(port);
+                        memcpy(&data_in6->sin6_addr, ldns_rdf_data(rd), ldns_rdf_size(rd));
+                        *size = sizeof(struct sockaddr_in6);
+                        return data;
+                default:
+                        LDNS_FREE(data);
+                        return NULL;
+        }
+}
 
-               all_servers_rtt_inf = false;
-
-               gettimeofday(&tv_s, NULL);
-
-               send_status = LDNS_STATUS_ERR;
-
-               /* reply_bytes implicitly handles our error */
-               if (1 == ldns_resolver_usevc(r)) {
-                       for (retries = ldns_resolver_retry(r); retries > 0; retries--) {
-                               send_status = 
-                                       ldns_tcp_send(&reply_bytes, qb, ns, 
-                                       (socklen_t)ns_len, ldns_resolver_timeout(r), 
-                                       &reply_size);
-                               if (send_status == LDNS_STATUS_OK) {
-                                       break;
-                               }
-                       }
-               } else {
-                       for (retries = ldns_resolver_retry(r); retries > 0; retries--) {
-                               /* ldns_rdf_print(stdout, ns_array[i]); */
-                               send_status = 
-                                       ldns_udp_send_from(&reply_bytes, qb,
-                                               ns,  (socklen_t)ns_len,
-                                               src, (socklen_t)src_len,
-                                               ldns_resolver_timeout(r),
-                                               &reply_size);
-                               if (send_status == LDNS_STATUS_OK) {
-                                       break;
-                               }
-                       }
-               }
-
-               if (send_status != LDNS_STATUS_OK) {
-                       ldns_resolver_set_nameserver_rtt(r, i, LDNS_RESOLV_RTT_INF);
-                       status = send_status;
-               }
-               
-               /* obey the fail directive */
-               if (!reply_bytes) {
-                       /* the current nameserver seems to have a problem, blacklist it */
-                       if (ldns_resolver_fail(r)) {
-                               LDNS_FREE(ns);
-                               return LDNS_STATUS_ERR;
-                       } else {
-                               LDNS_FREE(ns);
-                               continue;
-                       }
-               } 
-               
-               status = ldns_wire2pkt(&reply, reply_bytes, reply_size);
-               if (status != LDNS_STATUS_OK) {
-                       LDNS_FREE(reply_bytes);
-                       LDNS_FREE(ns);
-                       return status;
-               }
-               
-               LDNS_FREE(ns);
-               gettimeofday(&tv_e, NULL);
-
-               if (reply) {
-                       ldns_pkt_set_querytime(reply, (uint32_t)
-                               ((tv_e.tv_sec - tv_s.tv_sec) * 1000) +
-                               (tv_e.tv_usec - tv_s.tv_usec) / 1000);
-                       ldns_pkt_set_answerfrom(reply,
-                                       ldns_rdf_clone(ns_array[i]));
-                       ldns_pkt_set_timestamp(reply, tv_s);
-                       ldns_pkt_set_size(reply, reply_size);
-                       break;
-               } else {
-                       if (ldns_resolver_fail(r)) {
-                               /* if fail is set bail out, after the first
-                                * one */
-                               break;
-                       }
-               }
-
-               /* wait retrans seconds... */
-               sleep((unsigned int) ldns_resolver_retrans(r));
-       }
-
-       if(src) {
-               LDNS_FREE(src);
-       }
-       if (all_servers_rtt_inf) {
-               LDNS_FREE(reply_bytes);
-               return LDNS_STATUS_RES_NO_NS;
-       }
-#ifdef HAVE_SSL
-       if (tsig_mac && reply && reply_bytes) {
-               if (!ldns_pkt_tsig_verify(reply,
-                                         reply_bytes,
-                                         reply_size,
-                                         ldns_resolver_tsig_keyname(r),
-                                         ldns_resolver_tsig_keydata(r), tsig_mac)) {
-                       status = LDNS_STATUS_CRYPTO_TSIG_BOGUS;
-               }
-       }
-#else
-       (void)tsig_mac;
-#endif /* HAVE_SSL */
-
-       LDNS_FREE(reply_bytes);
-       if (result) {
-               *result = reply;
-       }
-
-       return status;
+struct sockaddr_storage *
+ldns_rdf2native_sockaddr_storage(
+               const ldns_rdf *rd, uint16_t port, size_t *size)
+{
+       return ldns_rdf2native_sockaddr_storage_port(
+                       rd, (port == 0 ? LDNS_PORT : port), size);
 }
 
 /** best effort to set nonblocking */
@@ -300,41 +168,166 @@ ldns_sock_wait(int sockfd, struct timeval timeout, int write)
        return 1;
 }
 
-ldns_status
-ldns_udp_send_from(uint8_t **result, ldns_buffer *qbin,
-               const struct sockaddr_storage *to  , socklen_t tolen,
-               const struct sockaddr_storage *from, socklen_t fromlen,
-               struct timeval timeout, size_t *answer_size)
+
+static int
+ldns_tcp_connect_from(const struct sockaddr_storage *to, socklen_t tolen, 
+               const struct sockaddr_storage *from, socklen_t fromlen,
+               struct timeval timeout)
 {
        int sockfd;
-       uint8_t *answer;
 
-       sockfd = ldns_udp_bgsend_from(qbin, to, tolen, from, fromlen, timeout);
+#ifndef S_SPLINT_S
+       if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_STREAM, 
+                                       IPPROTO_TCP)) == -1) {
+               return 0;
+       }
+#endif
+       if (from && bind(sockfd, (const struct sockaddr*)from, fromlen) == -1){
+               return 0;
+       }
 
-       if (sockfd == 0) {
-               return LDNS_STATUS_SOCKET_ERROR;
+       /* perform nonblocking connect, to be able to wait with select() */
+       ldns_sock_nonblock(sockfd);
+       if (connect(sockfd, (struct sockaddr*)to, tolen) == -1) {
+#ifndef USE_WINSOCK
+#ifdef EINPROGRESS
+               if(errno != EINPROGRESS) {
+#else
+               if(1) {
+#endif
+                       close(sockfd);
+                       return 0;
+               }
+#else /* USE_WINSOCK */
+               if(WSAGetLastError() != WSAEINPROGRESS &&
+                       WSAGetLastError() != WSAEWOULDBLOCK) {
+                       closesocket(sockfd);
+                       return 0;
+               }
+#endif
+               /* error was only telling us that it would block */
        }
 
-       /* wait for an response*/
-       if(!ldns_sock_wait(sockfd, timeout, 0)) {
+       /* wait(write) until connected or error */
+       while(1) {
+               int error = 0;
+               socklen_t len = (socklen_t)sizeof(error);
+
+               if(!ldns_sock_wait(sockfd, timeout, 1)) {
+#ifndef USE_WINSOCK
+                       close(sockfd);
+#else
+                       closesocket(sockfd);
+#endif
+                       return 0;
+               }
+
+               /* check if there is a pending error for nonblocking connect */
+               if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&error,
+                       &len) < 0) {
+#ifndef USE_WINSOCK
+                       error = errno; /* on solaris errno is error */
+#else
+                       error = WSAGetLastError();
+#endif
+               }
+#ifndef USE_WINSOCK
+#if defined(EINPROGRESS) && defined(EWOULDBLOCK)
+               if(error == EINPROGRESS || error == EWOULDBLOCK)
+                       continue; /* try again */
+#endif
+               else if(error != 0) {
+                       close(sockfd);
+                       /* error in errno for our user */
+                       errno = error;
+                       return 0;
+               }
+#else /* USE_WINSOCK */
+               if(error == WSAEINPROGRESS)
+                       continue;
+               else if(error == WSAEWOULDBLOCK)
+                       continue;
+               else if(error != 0) {
+                       closesocket(sockfd);
+                       errno = error;
+                       return 0;
+               }
+#endif /* USE_WINSOCK */
+               /* connected */
+               break;
+       }
+
+       /* set the socket blocking again */
+       ldns_sock_block(sockfd);
+
+       return sockfd;
+}
+
+int
+ldns_tcp_connect(const struct sockaddr_storage *to, socklen_t tolen, 
+               struct timeval timeout)
+{
+       return ldns_tcp_connect_from(to, tolen, NULL, 0, timeout);
+}
+
+static int
+ldns_tcp_bgsend_from(ldns_buffer *qbin,
+               const struct sockaddr_storage *to, socklen_t tolen, 
+               const struct sockaddr_storage *from, socklen_t fromlen,
+               struct timeval timeout)
+{
+       int sockfd;
+       
+       sockfd = ldns_tcp_connect_from(to, tolen, from, fromlen, timeout);
+       
+       if (sockfd == 0) {
+               return 0;
+       }
+       
+       if (ldns_tcp_send_query(qbin, sockfd, to, tolen) == 0) {
 #ifndef USE_WINSOCK
                close(sockfd);
 #else
-                closesocket(sockfd);
+               closesocket(sockfd);
 #endif
-               return LDNS_STATUS_NETWORK_ERR;
+               return 0;
        }
+       
+       return sockfd;
+}
 
-        /* set to nonblocking, so if the checksum is bad, it becomes
-         * an EGAIN error and the ldns_udp_send function does not block,
-         * but returns a 'NETWORK_ERROR' much like a timeout. */
-        ldns_sock_nonblock(sockfd);
+int
+ldns_tcp_bgsend(ldns_buffer *qbin,
+               const struct sockaddr_storage *to, socklen_t tolen, 
+               struct timeval timeout)
+{
+       return ldns_tcp_bgsend_from(qbin, to, tolen, NULL, 0, timeout);
+}
 
-       answer = ldns_udp_read_wire(sockfd, answer_size, NULL, NULL);
+
+/* keep in mind that in DNS tcp messages the first 2 bytes signal the
+ * amount data to expect
+ */
+static ldns_status
+ldns_tcp_send_from(uint8_t **result,  ldns_buffer *qbin,
+               const struct sockaddr_storage *to, socklen_t tolen,
+               const struct sockaddr_storage *from, socklen_t fromlen,
+               struct timeval timeout, size_t *answer_size)
+{
+       int sockfd;
+       uint8_t *answer;
+       
+       sockfd = ldns_tcp_bgsend_from(qbin, to, tolen, from, fromlen, timeout);
+       
+       if (sockfd == 0) {
+               return LDNS_STATUS_ERR;
+       }
+
+       answer = ldns_tcp_read_wire_timeout(sockfd, answer_size, timeout);
 #ifndef USE_WINSOCK
        close(sockfd);
 #else
-        closesocket(sockfd);
+       closesocket(sockfd);
 #endif
 
        if (*answer_size == 0) {
@@ -342,21 +335,40 @@ ldns_udp_send_from(uint8_t **result, ldns_buffer *qbin,
                return LDNS_STATUS_NETWORK_ERR;
        }
 
-       *result = answer;
+       /* resize accordingly */
+       *result = LDNS_XREALLOC(answer, uint8_t, (size_t)*answer_size);
+        if(!*result) {
+                LDNS_FREE(answer);
+                return LDNS_STATUS_MEM_ERR;
+        }
        return LDNS_STATUS_OK;
 }
 
 ldns_status
-ldns_udp_send(uint8_t **result, ldns_buffer *qbin,
-               const struct sockaddr_storage *to  , socklen_t tolen,
+ldns_tcp_send(uint8_t **result,  ldns_buffer *qbin,
+               const struct sockaddr_storage *to, socklen_t tolen,
                struct timeval timeout, size_t *answer_size)
 {
-       return ldns_udp_send_from(result, qbin, to, tolen, NULL, 0,
-                       timeout, answer_size);
+       return ldns_tcp_send_from(result, qbin,
+                       to, tolen, NULL, 0, timeout, answer_size);
 }
 
-
 int
+ldns_udp_connect(const struct sockaddr_storage *to, struct timeval ATTR_UNUSED(timeout))
+{
+       int sockfd;
+
+#ifndef S_SPLINT_S
+       if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_DGRAM, 
+                                       IPPROTO_UDP)) 
+                       == -1) {
+                return 0;
+        }
+#endif
+       return sockfd;
+}
+
+static int
 ldns_udp_bgsend_from(ldns_buffer *qbin,
                const struct sockaddr_storage *to  , socklen_t tolen, 
                const struct sockaddr_storage *from, socklen_t fromlen, 
@@ -370,10 +382,8 @@ ldns_udp_bgsend_from(ldns_buffer *qbin,
                return 0;
        }
 
-       if (from) {
-               if (bind(sockfd, (const struct sockaddr*)from, fromlen)) {
-                       return 0;
-               }
+       if (from && bind(sockfd, (const struct sockaddr*)from, fromlen) == -1){
+               return 0;
        }
 
        if (ldns_udp_send_query(qbin, sockfd, to, tolen) == 0) {
@@ -395,109 +405,243 @@ ldns_udp_bgsend(ldns_buffer *qbin,
        return ldns_udp_bgsend_from(qbin, to, tolen, NULL, 0, timeout);
 }
 
-int
-ldns_udp_connect(const struct sockaddr_storage *to, struct timeval ATTR_UNUSED(timeout))
+static ldns_status
+ldns_udp_send_from(uint8_t **result, ldns_buffer *qbin,
+               const struct sockaddr_storage *to  , socklen_t tolen,
+               const struct sockaddr_storage *from, socklen_t fromlen,
+               struct timeval timeout, size_t *answer_size)
 {
        int sockfd;
+       uint8_t *answer;
 
-#ifndef S_SPLINT_S
-       if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_DGRAM, 
-                                       IPPROTO_UDP)) 
-                       == -1) {
-                return 0;
-        }
-#endif
-       return sockfd;
-}
-
-int
-ldns_tcp_connect(const struct sockaddr_storage *to, socklen_t tolen, 
-               struct timeval timeout)
-{
-       int sockfd;
+       sockfd = ldns_udp_bgsend_from(qbin, to, tolen, from, fromlen, timeout);
 
-#ifndef S_SPLINT_S
-       if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_STREAM, 
-                                       IPPROTO_TCP)) == -1) {
-               return 0;
+       if (sockfd == 0) {
+               return LDNS_STATUS_SOCKET_ERROR;
        }
+
+       /* wait for an response*/
+       if(!ldns_sock_wait(sockfd, timeout, 0)) {
+#ifndef USE_WINSOCK
+               close(sockfd);
+#else
+                closesocket(sockfd);
 #endif
+               return LDNS_STATUS_NETWORK_ERR;
+       }
 
-       /* perform nonblocking connect, to be able to wait with select() */
-       ldns_sock_nonblock(sockfd);
-       if (connect(sockfd, (struct sockaddr*)to, tolen) == -1) {
+        /* set to nonblocking, so if the checksum is bad, it becomes
+         * an EGAIN error and the ldns_udp_send function does not block,
+         * but returns a 'NETWORK_ERROR' much like a timeout. */
+        ldns_sock_nonblock(sockfd);
+
+       answer = ldns_udp_read_wire(sockfd, answer_size, NULL, NULL);
 #ifndef USE_WINSOCK
-#ifdef EINPROGRESS
-               if(errno != EINPROGRESS) {
+       close(sockfd);
 #else
-               if(1) {
+        closesocket(sockfd);
 #endif
-                       close(sockfd);
-                       return 0;
+
+       if (*answer_size == 0) {
+               /* oops */
+               return LDNS_STATUS_NETWORK_ERR;
+       }
+
+       *result = answer;
+       return LDNS_STATUS_OK;
+}
+
+ldns_status
+ldns_udp_send(uint8_t **result, ldns_buffer *qbin,
+               const struct sockaddr_storage *to  , socklen_t tolen,
+               struct timeval timeout, size_t *answer_size)
+{
+       return ldns_udp_send_from(result, qbin, to, tolen, NULL, 0,
+                       timeout, answer_size);
+}
+
+ldns_status
+ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf *tsig_mac)
+{
+       uint8_t i;
+
+       struct sockaddr_storage *src = NULL;
+       size_t src_len;
+       struct sockaddr_storage *ns;
+       size_t ns_len;
+       struct timeval tv_s;
+       struct timeval tv_e;
+
+       ldns_rdf **ns_array;
+       size_t *rtt;
+       ldns_pkt *reply;
+       bool all_servers_rtt_inf;
+       uint8_t retries;
+
+       uint8_t *reply_bytes = NULL;
+       size_t reply_size = 0;
+       ldns_status status, send_status;
+
+       assert(r != NULL);
+
+       status = LDNS_STATUS_OK;
+       rtt = ldns_resolver_rtt(r);
+       ns_array = ldns_resolver_nameservers(r);
+       reply = NULL; 
+       ns_len = 0;
+
+       all_servers_rtt_inf = true;
+
+       if (ldns_resolver_random(r)) {
+               ldns_resolver_nameservers_randomize(r);
+       }
+
+       if(ldns_resolver_source(r)) {
+               src = ldns_rdf2native_sockaddr_storage_port(
+                               ldns_resolver_source(r), 0, &src_len);
+       }
+
+       /* loop through all defined nameservers */
+       for (i = 0; i < ldns_resolver_nameserver_count(r); i++) {
+               if (rtt[i] == LDNS_RESOLV_RTT_INF) {
+                       /* not reachable nameserver! */
+                       continue;
+               }
+
+               /* maybe verbosity setting?
+               printf("Sending to ");
+               ldns_rdf_print(stdout, ns_array[i]);
+               printf("\n");
+               */
+               ns = ldns_rdf2native_sockaddr_storage(ns_array[i],
+                               ldns_resolver_port(r), &ns_len);
+
+
+#ifndef S_SPLINT_S
+               if ((ns->ss_family == AF_INET) &&
+                               (ldns_resolver_ip6(r) == LDNS_RESOLV_INET6)) {
+                       /* not reachable */
+                       LDNS_FREE(ns);
+                       continue;
                }
-#else /* USE_WINSOCK */
-               if(WSAGetLastError() != WSAEINPROGRESS &&
-                       WSAGetLastError() != WSAEWOULDBLOCK) {
-                       closesocket(sockfd);
-                       return 0;
+
+               if ((ns->ss_family == AF_INET6) &&
+                                (ldns_resolver_ip6(r) == LDNS_RESOLV_INET)) {
+                       /* not reachable */
+                       LDNS_FREE(ns);
+                       continue;
                }
 #endif
-               /* error was only telling us that it would block */
-       }
 
-       /* wait(write) until connected or error */
-       while(1) {
-               int error = 0;
-               socklen_t len = (socklen_t)sizeof(error);
+               all_servers_rtt_inf = false;
 
-               if(!ldns_sock_wait(sockfd, timeout, 1)) {
-#ifndef USE_WINSOCK
-                       close(sockfd);
-#else
-                       closesocket(sockfd);
-#endif
-                       return 0;
+               gettimeofday(&tv_s, NULL);
+
+               send_status = LDNS_STATUS_ERR;
+
+               /* reply_bytes implicitly handles our error */
+               if (1 == ldns_resolver_usevc(r)) {
+                       for (retries = ldns_resolver_retry(r); retries > 0; retries--) {
+                               send_status = 
+                                       ldns_tcp_send_from(&reply_bytes, qb, 
+                                               ns, (socklen_t)ns_len,
+                                               src, (socklen_t)src_len,
+                                               ldns_resolver_timeout(r),
+                                               &reply_size);
+                               if (send_status == LDNS_STATUS_OK) {
+                                       break;
+                               }
+                       }
+               } else {
+                       for (retries = ldns_resolver_retry(r); retries > 0; retries--) {
+                               /* ldns_rdf_print(stdout, ns_array[i]); */
+                               send_status = 
+                                       ldns_udp_send_from(&reply_bytes, qb,
+                                               ns,  (socklen_t)ns_len,
+                                               src, (socklen_t)src_len,
+                                               ldns_resolver_timeout(r),
+                                               &reply_size);
+                               if (send_status == LDNS_STATUS_OK) {
+                                       break;
+                               }
+                       }
                }
 
-               /* check if there is a pending error for nonblocking connect */
-               if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&error,
-                       &len) < 0) {
-#ifndef USE_WINSOCK
-                       error = errno; /* on solaris errno is error */
-#else
-                       error = WSAGetLastError();
-#endif
+               if (send_status != LDNS_STATUS_OK) {
+                       ldns_resolver_set_nameserver_rtt(r, i, LDNS_RESOLV_RTT_INF);
+                       status = send_status;
                }
-#ifndef USE_WINSOCK
-#if defined(EINPROGRESS) && defined(EWOULDBLOCK)
-               if(error == EINPROGRESS || error == EWOULDBLOCK)
-                       continue; /* try again */
-#endif
-               else if(error != 0) {
-                       close(sockfd);
-                       /* error in errno for our user */
-                       errno = error;
-                       return 0;
+               
+               /* obey the fail directive */
+               if (!reply_bytes) {
+                       /* the current nameserver seems to have a problem, blacklist it */
+                       if (ldns_resolver_fail(r)) {
+                               LDNS_FREE(ns);
+                               return LDNS_STATUS_ERR;
+                       } else {
+                               LDNS_FREE(ns);
+                               continue;
+                       }
+               } 
+               
+               status = ldns_wire2pkt(&reply, reply_bytes, reply_size);
+               if (status != LDNS_STATUS_OK) {
+                       LDNS_FREE(reply_bytes);
+                       LDNS_FREE(ns);
+                       return status;
                }
-#else /* USE_WINSOCK */
-               if(error == WSAEINPROGRESS)
-                       continue;
-               else if(error == WSAEWOULDBLOCK)
-                       continue;
-               else if(error != 0) {
-                       closesocket(sockfd);
-                       errno = error;
-                       return 0;
+               
+               LDNS_FREE(ns);
+               gettimeofday(&tv_e, NULL);
+
+               if (reply) {
+                       ldns_pkt_set_querytime(reply, (uint32_t)
+                               ((tv_e.tv_sec - tv_s.tv_sec) * 1000) +
+                               (tv_e.tv_usec - tv_s.tv_usec) / 1000);
+                       ldns_pkt_set_answerfrom(reply,
+                                       ldns_rdf_clone(ns_array[i]));
+                       ldns_pkt_set_timestamp(reply, tv_s);
+                       ldns_pkt_set_size(reply, reply_size);
+                       break;
+               } else {
+                       if (ldns_resolver_fail(r)) {
+                               /* if fail is set bail out, after the first
+                                * one */
+                               break;
+                       }
                }
-#endif /* USE_WINSOCK */
-               /* connected */
-               break;
+
+               /* wait retrans seconds... */
+               sleep((unsigned int) ldns_resolver_retrans(r));
        }
 
-       /* set the socket blocking again */
-       ldns_sock_block(sockfd);
+       if(src) {
+               LDNS_FREE(src);
+       }
+       if (all_servers_rtt_inf) {
+               LDNS_FREE(reply_bytes);
+               return LDNS_STATUS_RES_NO_NS;
+       }
+#ifdef HAVE_SSL
+       if (tsig_mac && reply && reply_bytes) {
+               if (!ldns_pkt_tsig_verify(reply,
+                                         reply_bytes,
+                                         reply_size,
+                                         ldns_resolver_tsig_keyname(r),
+                                         ldns_resolver_tsig_keydata(r), tsig_mac)) {
+                       status = LDNS_STATUS_CRYPTO_TSIG_BOGUS;
+               }
+       }
+#else
+       (void)tsig_mac;
+#endif /* HAVE_SSL */
 
-       return sockfd;
+       LDNS_FREE(reply_bytes);
+       if (result) {
+               *result = reply;
+       }
+
+       return status;
 }
 
 ssize_t
@@ -681,110 +825,6 @@ ldns_tcp_read_wire(int sockfd, size_t *size)
        return wire;
 }
 
-/* keep in mind that in DNS tcp messages the first 2 bytes signal the
- * amount data to expect
- */
-ldns_status
-ldns_tcp_send(uint8_t **result,  ldns_buffer *qbin, const struct sockaddr_storage *to, 
-               socklen_t tolen, struct timeval timeout, size_t *answer_size)
-{
-       int sockfd;
-       uint8_t *answer;
-       
-       sockfd = ldns_tcp_bgsend(qbin, to, tolen, timeout);
-       
-       if (sockfd == 0) {
-               return LDNS_STATUS_ERR;
-       }
-
-       answer = ldns_tcp_read_wire_timeout(sockfd, answer_size, timeout);
-#ifndef USE_WINSOCK
-       close(sockfd);
-#else
-       closesocket(sockfd);
-#endif
-
-       if (*answer_size == 0) {
-               /* oops */
-               return LDNS_STATUS_NETWORK_ERR;
-       }
-
-       /* resize accordingly */
-       *result = LDNS_XREALLOC(answer, uint8_t, (size_t)*answer_size);
-        if(!*result) {
-                LDNS_FREE(answer);
-                return LDNS_STATUS_MEM_ERR;
-        }
-       return LDNS_STATUS_OK;
-}
-
-int
-ldns_tcp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, 
-               struct timeval timeout)
-{
-       int sockfd;
-       
-       sockfd = ldns_tcp_connect(to, tolen, timeout);
-       
-       if (sockfd == 0) {
-               return 0;
-       }
-       
-       if (ldns_tcp_send_query(qbin, sockfd, to, tolen) == 0) {
-#ifndef USE_WINSOCK
-               close(sockfd);
-#else
-               closesocket(sockfd);
-#endif
-               return 0;
-       }
-       
-       return sockfd;
-}
-
-/* code from rdata.c */
-struct sockaddr_storage *
-ldns_rdf2native_sockaddr_storage(const ldns_rdf *rd, uint16_t port, size_t *size)
-{
-        struct sockaddr_storage *data;
-        struct sockaddr_in  *data_in;
-        struct sockaddr_in6 *data_in6;
-
-        data = LDNS_MALLOC(struct sockaddr_storage);
-        if (!data) {
-                return NULL;
-        }
-               /* zero the structure for portability */
-               memset(data, 0, sizeof(struct sockaddr_storage));
-        if (port == 0) {
-                port =  LDNS_PORT;
-        }
-
-        switch(ldns_rdf_get_type(rd)) {
-                case LDNS_RDF_TYPE_A:
-#ifndef S_SPLINT_S
-                        data->ss_family = AF_INET;
-#endif
-                        data_in = (struct sockaddr_in*) data;
-                        data_in->sin_port = (in_port_t)htons(port);
-                        memcpy(&(data_in->sin_addr), ldns_rdf_data(rd), ldns_rdf_size(rd));
-                        *size = sizeof(struct sockaddr_in);
-                        return data;
-                case LDNS_RDF_TYPE_AAAA:
-#ifndef S_SPLINT_S
-                        data->ss_family = AF_INET6;
-#endif
-                        data_in6 = (struct sockaddr_in6*) data;
-                        data_in6->sin6_port = (in_port_t)htons(port);
-                        memcpy(&data_in6->sin6_addr, ldns_rdf_data(rd), ldns_rdf_size(rd));
-                        *size = sizeof(struct sockaddr_in6);
-                        return data;
-                default:
-                        LDNS_FREE(data);
-                        return NULL;
-        }
-}
-
 #ifndef S_SPLINT_S
 ldns_rdf *
 ldns_sockaddr_storage2rdf(struct sockaddr_storage *sock, uint16_t *port)
@@ -827,6 +867,8 @@ ldns_axfr_start(ldns_resolver *resolver, ldns_rdf *domain, ldns_rr_class class)
         ldns_pkt *query;
         ldns_buffer *query_wire;
 
+        struct sockaddr_storage *src = NULL;
+        size_t src_len = 0;
         struct sockaddr_storage *ns = NULL;
         size_t ns_len = 0;
         size_t ns_i;
@@ -841,6 +883,10 @@ ldns_axfr_start(ldns_resolver *resolver, ldns_rdf *domain, ldns_rr_class class)
         if (!query) {
                 return LDNS_STATUS_ADDRESS_ERR;
         }
+       if(ldns_resolver_source(resolver)) {
+               src = ldns_rdf2native_sockaddr_storage_port(
+                               ldns_resolver_source(resolver), 0, &src_len);
+       }
         /* For AXFR, we have to make the connection ourselves */
         /* try all nameservers (which usually would mean v4 fallback if
          * @hostname is used */
@@ -855,7 +901,9 @@ ldns_axfr_start(ldns_resolver *resolver, ldns_rdf *domain, ldns_rr_class class)
                        resolver->_nameservers[ns_i],
                        ldns_resolver_port(resolver), &ns_len);
 
-               resolver->_socket = ldns_tcp_connect(ns, (socklen_t)ns_len,
+               resolver->_socket = ldns_tcp_connect_from(
+                               ns, (socklen_t)ns_len,
+                               src, (socklen_t)src_len,
                                ldns_resolver_timeout(resolver));
        }
 
index 9fe14d9fe578719d81e60922d1cd516c3e37a286..a57157a3fe216290e69b624b9db5a077763485c0 100644 (file)
@@ -966,86 +966,82 @@ ldns_resolver_deep_free(ldns_resolver *res)
        }
 }
 
-ldns_pkt *
-ldns_resolver_search(const ldns_resolver *r,const  ldns_rdf *name,
-       ldns_rr_type t, ldns_rr_class c, uint16_t flags)
+ldns_status
+ldns_resolver_search_status(ldns_pkt** pkt,
+               ldns_resolver *r, const  ldns_rdf *name,
+               ldns_rr_type t, ldns_rr_class c, uint16_t flags)
 {
-
        ldns_rdf *new_name;
        ldns_rdf **search_list;
        size_t i;
-       ldns_pkt *p;
+       ldns_status s = LDNS_STATUS_OK;
 
        if (ldns_dname_absolute(name)) {
                /* query as-is */
-               return ldns_resolver_query(r, name, t, c, flags);
+               return ldns_resolver_query_status(pkt, r, name, t, c, flags);
        } else if (ldns_resolver_dnsrch(r)) {
                search_list = ldns_resolver_searchlist(r);
                for (i = 0; i < ldns_resolver_searchlist_count(r); i++) {
                        new_name = ldns_dname_cat_clone(name, search_list[i]);
 
-                       p = ldns_resolver_query(r, new_name, t, c, flags);
+                       s = ldns_resolver_query_status(pkt, r,
+                                       new_name, t, c, flags);
                        ldns_rdf_free(new_name);
-                       if (p) {
-                               if (ldns_pkt_get_rcode(p) == LDNS_RCODE_NOERROR) {
-                                       return p;
-                               } else {
-                                       ldns_pkt_free(p);
-                                       p = NULL;
+                       if (pkt) {
+                               if (s == LDNS_STATUS_OK && *pkt &&
+                                               ldns_pkt_get_rcode(*pkt) ==
+                                               LDNS_RCODE_NOERROR) {
+                                       return LDNS_STATUS_OK;
                                }
+                               ldns_pkt_free(*pkt);
                        }
                }
        }
-       return NULL;
+       return s;
 }
 
 ldns_pkt *
-ldns_resolver_query(const ldns_resolver *r, const ldns_rdf *name,
+ldns_resolver_search(const ldns_resolver *r,const  ldns_rdf *name,
        ldns_rr_type t, ldns_rr_class c, uint16_t flags)
+{
+       ldns_pkt* pkt = NULL;
+       if (ldns_resolver_search_status(&pkt, (ldns_resolver *)r,
+                               name, t, c, flags) != LDNS_STATUS_OK) {
+               ldns_pkt_free(pkt);
+       }
+       return pkt;
+}
+
+ldns_status
+ldns_resolver_query_status(ldns_pkt** pkt,
+               ldns_resolver *r, const ldns_rdf *name,
+               ldns_rr_type t, ldns_rr_class c, uint16_t flags)
 {
        ldns_rdf *newname;
-       ldns_pkt *pkt;
        ldns_status status;
 
-       pkt = NULL;
-
-       if (!ldns_resolver_defnames(r)) {
-               status = ldns_resolver_send(&pkt, (ldns_resolver *)r, name,
-                               t, c, flags);
-               if (status == LDNS_STATUS_OK) {
-                       return pkt;
-               } else {
-                       if (pkt) {
-                               ldns_pkt_free(pkt);
-                       }
-                       return NULL;
-               }
-       }
-
-       if (!ldns_resolver_domain(r)) {
-               /* _defnames is set, but the domain is not....?? */
-               status = ldns_resolver_send(&pkt, (ldns_resolver *)r, name,
-                               t, c, flags);
-               if (status == LDNS_STATUS_OK) {
-                       return pkt;
-               } else {
-                       if (pkt) {
-                               ldns_pkt_free(pkt);
-                       }
-                       return NULL;
-               }
+       if (!ldns_resolver_defnames(r) || !ldns_resolver_domain(r)) {
+               return ldns_resolver_send(pkt, r, name, t, c, flags);
        }
 
-       newname = ldns_dname_cat_clone((const ldns_rdf*)name, ldns_resolver_domain(r));
+       newname = ldns_dname_cat_clone(name, ldns_resolver_domain(r));
        if (!newname) {
-               return NULL;
+               return LDNS_STATUS_MEM_ERR;
        }
-
-       (void)ldns_resolver_send(&pkt, (ldns_resolver *)r, newname, t, c,
-                       flags);
-
+       status = ldns_resolver_send(pkt, r, newname, t, c, flags);
        ldns_rdf_free(newname);
+       return status;
+}
 
+ldns_pkt *
+ldns_resolver_query(const ldns_resolver *r, const ldns_rdf *name,
+       ldns_rr_type t, ldns_rr_class c, uint16_t flags)
+{
+       ldns_pkt* pkt = NULL;
+       if (ldns_resolver_query_status(&pkt, (ldns_resolver *)r,
+                               name, t, c, flags) != LDNS_STATUS_OK) {
+               ldns_pkt_free(pkt);
+       }
        return pkt;
 }