Kaynağa Gözat

* Add dns_lookup_block()

Bryan Drewery 16 yıl önce
ebeveyn
işleme
a0b48b021e
2 değiştirilmiş dosya ile 118 ekleme ve 49 silme
  1. 117 49
      src/adns.c
  2. 1 0
      src/adns.h

+ 117 - 49
src/adns.c

@@ -170,10 +170,11 @@ static int get_dns_idx();
 static int cache_find(const char *);
 //static int dns_on_read(void *client_data, int idx, char *buf, int len);
 //static int dns_on_eof(void *client_data, int idx, int err, const char *errmsg);
+static void dns_read(int idx, char*, int&, bool blocking = 0);
 static void dns_on_read(int idx, char *buf, int atr);
 static void dns_on_eof(int idx);
 static const char *dns_next_server();
-static int parse_reply(char *response, size_t nbytes, const char* server_ip);
+static int parse_reply(char *response, size_t nbytes, const char* server_ip, bool blocking = 0);
 
 interval_t async_lookup_timeout = 10;
 interval_t async_server_timeout = 40;
@@ -285,6 +286,17 @@ static void async_timeout(void *client_data)
 
 char s1_7[3] = "",s2_3[3] = "",s2_2[3] = "";
 
+static void free_query(dns_query_t*& q) {
+	if (q->ip)
+		free(q->ip);
+	if (q->answer)
+		delete q->answer;
+	if (q->query)
+		free(q->query);
+	free(q);
+	q = NULL;
+}
+
 static dns_query_t *alloc_query(void *client_data, dns_callback_t callback, const char *query)
 {
 	dns_query_t *q = (dns_query_t *) my_calloc(1, sizeof(*q));
@@ -345,7 +357,7 @@ static int get_dns_idx()
 	return 1;
 }
 
-void egg_dns_send(char *query, int len)
+void egg_dns_send(char *query, int len, bool blocking)
 {
         if (dns_idx >= 0 && dcc[dns_idx].sock == -1) {
           lostdcc(dns_idx);
@@ -362,6 +374,9 @@ void egg_dns_send(char *query, int len)
           sdprintf("SETTING TIMEOUT to %d", async_server_timeout);
           dcc[dns_idx].timeval = now;
         }
+
+		if (blocking)
+			socket_set_nonblock(dcc[dns_idx].sock, 0);
         if (write(dcc[dns_idx].sock, query, len) == -1) {
 	  ;
 	}
@@ -378,7 +393,8 @@ dns_query_t *find_query(const char *host)
   return NULL;
 }
 
-static void dns_send_query(dns_query_t *q, int type = (DNS_LOOKUP_A|DNS_LOOKUP_AAAA))
+#define DEFAULT_DNS_TYPE (DNS_LOOKUP_A|DNS_LOOKUP_AAAA)
+static void dns_send_query(dns_query_t *q, int type = DEFAULT_DNS_TYPE, bool blocking = 0)
 {
   char buf[512] = "";
   int len;
@@ -394,7 +410,7 @@ static void dns_send_query(dns_query_t *q, int type = (DNS_LOOKUP_A|DNS_LOOKUP_A
       buf[len] = 0; len++; buf[len] = DNS_A; len++;
       buf[len] = 0; len++; buf[len] = 1; len++;
 
-      egg_dns_send(buf, len);
+      egg_dns_send(buf, len, blocking);
     }
 
 #ifdef USE_IPV6
@@ -406,7 +422,7 @@ static void dns_send_query(dns_query_t *q, int type = (DNS_LOOKUP_A|DNS_LOOKUP_A
       buf[len] = 0; len++; buf[len] = DNS_AAAA; len++;
       buf[len] = 0; len++; buf[len] = 1; len++;
 
-      egg_dns_send(buf, len);
+      egg_dns_send(buf, len, blocking);
     }
 #endif
   } else if (q->ip) {
@@ -416,10 +432,15 @@ static void dns_send_query(dns_query_t *q, int type = (DNS_LOOKUP_A|DNS_LOOKUP_A
     buf[len] = 0; len++; buf[len] = DNS_PTR; len++;
     buf[len] = 0; len++; buf[len] = 1; len++;
 
-    egg_dns_send(buf, len);
+    egg_dns_send(buf, len, blocking);
   }
 }
 
+static void dns_send_query_blocking(dns_query_t* q, int type = DEFAULT_DNS_TYPE) {
+	dns_send_query(q, type, 1);
+}
+
+
 /*
 void dns_resend_queries()
 {
@@ -505,11 +526,12 @@ int egg_dns_lookup(const char *host, interval_t timeout, dns_callback_t callback
 	return(q->id);
 }
 
-bd::Array<bd::String> dns_lookup_block(const char *host, interval_t timeout, void *client_data, int type)
+bd::Array<bd::String> dns_lookup_block(const char *host, interval_t timeout, int type)
 {
+	bd::Array<bd::String> answer;
+
 	if (is_dotted_ip(host)) {
 		/* If it's already an ip, we're done. */
-		bd::Array<bd::String> answer;
 		sdprintf("egg_dns_lookup(%s, %d): Already an ip.", host, timeout);
                 answer << host;
 		return answer;
@@ -518,7 +540,6 @@ bd::Array<bd::String> dns_lookup_block(const char *host, interval_t timeout, voi
 	/* Ok, now see if it's in our host cache. */
 	for (int i = 0; i < nhosts; i++) {
 		if (!strcasecmp(host, hosts[i].host)) {
-			bd::Array<bd::String> answer;
 			sdprintf("egg_dns_lookup(%s, %d): Found in hosts -> %s", host, timeout, hosts[i].ip);
                         answer << hosts[i].ip;
                         return answer;
@@ -531,14 +552,46 @@ bd::Array<bd::String> dns_lookup_block(const char *host, interval_t timeout, voi
 		sdprintf("egg_dns_lookup(%s, %d): Found in cache -> %s", host, timeout, cache[cache_id].answer->join(',').c_str());
 		return *(cache[cache_id].answer);
 	}
-#ifdef incomplete
 	/* Allocate our query struct. */
-	dns_query_t *q = alloc_query(client_data, callback, host);
+	dns_query_t *q = alloc_query(NULL, NULL, host);
 
-	sdprintf("egg_dns_lookup(%s, %d) -> %d", host, timeout, q->id);
-        dns_send_query(q, type);
-	return(q->id);
-#endif
+	sdprintf("dns_lookup_block(%s, %d) -> %d", host, timeout, q->id);
+	dns_send_query_blocking(q, type);
+
+	char buf[512] = "";
+	int atr;
+	bool read_error = 0;
+
+
+	/* Now the key difference, call read() on the socket to block until it returns */
+	if (!setjmp(alarmret)) {
+read_more:
+		alarm(timeout);
+
+		dns_read(dns_idx, buf, atr, 1);
+		read_error = errno ? 1 : 0;
+
+		alarm(0);
+
+		if (!read_error) {
+			dcc[dns_idx].timeval = now;
+			if (parse_reply(buf, atr, dns_ip, 1)) {
+				dns_on_eof(dns_idx);
+			} else if (errno == EAGAIN) {
+				goto read_more;
+			} else if (q->answer) {
+				answer = *(q->answer);
+			}
+		}
+	}
+
+	if (q)
+		free_query(q);
+
+	// Disable blocking on the dns socket.
+	socket_set_nonblock(dcc[dns_idx].sock, 1);
+
+	return answer;
 }
 
 /* Perform an async dns reverse lookup. This does ip -> host. For host -> ip
@@ -614,10 +667,30 @@ sdprintf("reversed ipv6 ip: %s", q->ip);
 	return(q->id);
 }
 
-//static int dns_on_read(void *client_data, int idx, char *buf, int len)
-static void dns_on_read(int idx, char *buf, int atr)
+static void dns_read(int idx, char* buf, int& atr, bool blocking) {
+	atr = read(dcc[idx].sock, buf, 512);
+
+	if (atr == -1) {
+		if (errno == EAGAIN) {
+			do {
+				atr = read(dcc[idx].sock, buf, 512);
+			} while (errno == EAGAIN && blocking);
+		}
+		if (atr == -1) {
+			dns_on_eof(idx);
+			errno = EPIPE;
+			return;
+		}
+	}
+	sdprintf("SETTING TIMEOUT to 0: Received reply.");
+	dns_handler.timeout_val = 0;
+	errno = 0;
+	return;
+}
+
+static void dns_on_read(int idx, char* buf, int atr)
 {
-        dcc[idx].timeval = now;
+	dcc[idx].timeval = now;
 
 //	if (resend_on_read) {
 //		resend_on_read = 0;
@@ -625,23 +698,13 @@ static void dns_on_read(int idx, char *buf, int atr)
 //		return;
 //	}
 
-        atr = read(dcc[idx].sock, buf, 512);
-
-        if (atr == -1) {
-          if (errno == EAGAIN)
-            atr = read(dcc[idx].sock, buf, 512);
-          if (atr == -1) {
-            dns_on_eof(idx);
-            return;
-          }
-        }
-        sdprintf("SETTING TIMEOUT to 0");
-        dns_handler.timeout_val = 0;
+	dns_read(idx, buf, atr);
 	if (parse_reply(buf, atr, dns_ip))
-          dns_on_eof(idx);
+		dns_on_eof(idx);
 	return;
 }
 
+
 static void dns_on_eof(int idx)
 {
         sdprintf("EOF on dns idx: %d sock: %d (%s)", idx, dcc[idx].sock, dcc[idx].host);
@@ -871,7 +934,7 @@ int egg_dns_cancel(int id, int issue_callback)
 	if (prev) prev->next = q->next;
 	else query_head = q->next;
 	sdprintf("Cancelling query: %s", q->query);
-	if (issue_callback) {
+	if (issue_callback && q->callback) {
 		if (q->answer->size() > 0) {
 			cache_add(q->query, *(q->answer), q->lowest_ttl);
 
@@ -881,10 +944,7 @@ int egg_dns_cancel(int id, int issue_callback)
 			q->callback(q->id, q->client_data, q->query, empty);
 		}
 	}
-	if (q->ip)
-		free(q->ip);
-	free(q->query);
-	free(q);
+	free_query(q);
 	return(0);
 }
 
@@ -928,14 +988,13 @@ void print_reply(dns_rr_t &reply)
 }
 */
 
-static int parse_reply(char *response, size_t nbytes, const char* server_ip)
+static int parse_reply(char *response, size_t nbytes, const char* server_ip, bool blocking)
 {
 	dns_header_t header;
 	dns_query_t *q = NULL, *prev = NULL;
-	dns_rr_t reply;
 	char result[512] = "";
 	short rr;
-	int r = -1;
+	dns_rr_t reply;
 	unsigned const char *eop = (unsigned char *) response + nbytes;
 	unsigned char *ptr = (unsigned char *) response;
         int return_code = 0;
@@ -984,6 +1043,7 @@ static int parse_reply(char *response, size_t nbytes, const char* server_ip)
                 sdprintf("Ignoring reply(%d) from %s: no recusion available.", header.id, server_ip);
                 return_code = 1;		/* get a new server */
 		q->remaining = 0;		/* Force this query to be removed, any further answers are ignored */
+				  errno = EACCES;
                 goto callback;
         }
 
@@ -992,9 +1052,11 @@ static int parse_reply(char *response, size_t nbytes, const char* server_ip)
           switch (GET_RCODE(header.flags)) {
             case 1:   /* Format error */
                   sdprintf("Ignoring reply(%d) from %s: Format error.", header.id, server_ip);
+				  errno = EINVAL;
                   break;
             case 2:   /* Server error */
                   sdprintf("Ignoring reply(%d) from %s: Server error.", header.id, server_ip);
+				  errno = EINVAL;
                   return_code = 1;		/* get a new server */
 		  q->remaining = 0;		/* Force this query to be removed, any further answers are ignored */
                   break;
@@ -1002,14 +1064,17 @@ static int parse_reply(char *response, size_t nbytes, const char* server_ip)
                   sdprintf("Ignoring reply(%d) from %s: NXDOMAIN.", header.id, server_ip);
                   /* Ignore the incoming AAAA or A reply as it will still be NXDOMAIN */
 		  q->remaining = 0;		/* Force this query to be removed, any further answers are ignored */
+				  errno = ENOENT;
                   break;
             case 4:
                   sdprintf("Ignoring reply(%d) from %s: Query not supported", header.id, server_ip);
+				  errno = EINVAL;
                   break;
             case 5:
                   sdprintf("Ignoring reply(%d) from %s: REFUSED", header.id, server_ip);
                   return_code = 1;		/* get a new server */
 		  q->remaining = 0;		/* Force this query to be removed, any further answers are ignored */
+				  errno = EACCES;
                   break;
           }
 
@@ -1062,9 +1127,7 @@ static int parse_reply(char *response, size_t nbytes, const char* server_ip)
 #endif /* USE_IPV6 */
 			break;
 		case DNS_PTR:
-			r = my_dn_expand((const unsigned char *) response, eop, ptr, result, sizeof(result));
-
-			if (r != -1 && result[0]) {
+			if ((my_dn_expand((const unsigned char *) response, eop, ptr, result, sizeof(result)) != -1) && result[0]) {
 				*(q->answer) << result;
 				sdprintf("Reply(%d): %s. \t %d \t IN PTR \t %s", header.id, q->query, reply.ttl, result);
 			}
@@ -1084,7 +1147,13 @@ static int parse_reply(char *response, size_t nbytes, const char* server_ip)
 
 callback:
 	/* Don't continue if we haven't gotten all expected replies. */
-	if (--q->remaining > 0) return 0;
+	if (--q->remaining > 0) {
+		sdprintf("Waiting on query(%d) for more replies.", header.id);
+		errno = EAGAIN;
+		return 0;
+	}
+
+	errno = 0;
 
 	/* Ok, we have, so now issue the callback with the answers. */
 	if (prev) prev->next = q->next;
@@ -1093,16 +1162,15 @@ callback:
         if (q->answer->size() > 0) {
 		cache_add(q->query, *(q->answer), q->lowest_ttl);
 
-		q->callback(q->id, q->client_data, q->query, *(q->answer));
-        } else {
+		if (q->callback)
+			q->callback(q->id, q->client_data, q->query, *(q->answer));
+        } else if (q->callback) {
 		bd::Array<bd::String> empty;
 		q->callback(q->id, q->client_data, q->query, empty);
         }
 
-	free(q->query);
-        if (q->ip)
-          free(q->ip);
-	free(q);
+		if (!blocking)
+			free_query(q);
 
 	return return_code;
 }

+ 1 - 0
src/adns.h

@@ -50,6 +50,7 @@ int egg_dns_init(void);
 
 void egg_dns_send(char *query, int len);
 int egg_dns_lookup(const char *host, interval_t timeout, dns_callback_t callback, void *client_data, int type = (DNS_LOOKUP_A|DNS_LOOKUP_AAAA));
+bd::Array<bd::String> dns_lookup_block(const char *host, interval_t timeout, int type = (DNS_LOOKUP_A|DNS_LOOKUP_AAAA));
 int egg_dns_reverse(const char *ip, interval_t timeout, dns_callback_t callback, void *client_data);
 int egg_dns_cancel(int id, int issue_callback);
 void tell_dnsdebug(int);