Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'blocking-dns'

* blocking-dns:
  * When looking up own IPs, only as A or AAAA depending on what is needed
  * Retry blocking dns call until it succeeds or all servers exhausted
  * Fix crash for query not being deallocated correctly after failed blocking-dns lookup
  * Lower DNS timeout to 20 seconds
  * Removed gethostbyname() dependency. This fixes Linux binary compatibility issues / crashes on startup.
  * Add dns_reverse_block()
  * Move some ip code
  * Add dns_lookup_block()
  * Being work on dns_lookup_block()
  • Loading branch information...
commit a5fff3c7f34df1ffa0066cb31ab7869ade00eb9f 2 parents c007acd + 0f9d6d5
@bdrewery bdrewery authored
Showing with 259 additions and 97 deletions.
  1. +1 −0  doc/UPDATES
  2. +238 −61 src/adns.c
  3. +2 −0  src/adns.h
  4. +18 −36 src/net.c
View
1  doc/UPDATES
@@ -8,6 +8,7 @@
* Uname checking (and associated emails) have been removed.
* Email functionality has been removed.
* Fix crash on OpenBSD due to mmap(2) bug.
+ * Removed gethostbyname() dependency. This fixes Linux binary compatibility issues / crashes on startup.
* Cookie op changes
* Cookie op algorithm changed again, they can no longer be easily reused (fixes #402) Thank you mulder.
* Cookies no longer fail if the opping bot changes nick after queueing the mode.
View
299 src/adns.c
@@ -170,13 +170,14 @@ 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;
+interval_t async_server_timeout = 20;
//int resend_on_read = 0;
static void
@@ -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));
@@ -303,6 +315,29 @@ static dns_query_t *alloc_query(void *client_data, dns_callback_t callback, cons
return q;
}
+static void query_transform_ip(dns_query_t* q, const char* ip) {
+ /* We need to transform the ip address into the proper form
+ * for reverse lookup. */
+ if (strchr(ip, ':')) {
+ char temp[128] = "";
+
+ socket_ipv6_to_dots(ip, temp);
+ sdprintf("dots: %s", temp);
+ size_t iplen = strlen(temp) + 9 + 1;
+ q->ip = (char *) my_calloc(1, iplen);
+ // reverse_ip(temp, q->ip);
+ strlcat(q->ip, temp, iplen);
+ strlcat(q->ip, "ip6.arpa", iplen);
+ sdprintf("reversed ipv6 ip: %s", q->ip);
+ }
+ else {
+ size_t iplen = strlen(ip) + 13 + 1;
+ q->ip = (char *) my_calloc(1, iplen);
+ reverse_ip(ip, q->ip);
+ strlcat(q->ip, ".in-addr.arpa", iplen);
+ }
+}
+
static int get_dns_idx()
{
int i, sock;
@@ -345,7 +380,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 +397,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 +416,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 +433,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 +445,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 +455,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,6 +549,143 @@ int egg_dns_lookup(const char *host, interval_t timeout, dns_callback_t callback
return(q->id);
}
+bd::Array<bd::String> dns_send_blocking(dns_query_t* q, interval_t timeout, int type = DEFAULT_DNS_TYPE) {
+ bd::Array<bd::String> answer;
+ 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) {
+ dns_query_t *prev = NULL, *cur = NULL;
+ for (cur = query_head; cur; cur = cur->next) {
+ if (cur->id == q->id) break;
+ prev = cur;
+ }
+ if (cur) {
+ if (prev) prev->next = cur->next;
+ else query_head = cur->next;
+ }
+ free_query(q);
+ }
+
+ // Disable blocking on the dns socket.
+ socket_set_nonblock(dcc[dns_idx].sock, 1);
+
+ return answer;
+}
+
+bd::Array<bd::String> dns_blocking_loop(const char* what, interval_t timeout, const char* from, int type = DEFAULT_DNS_TYPE, bool is_ip = 0) {
+ /* Allocate our query struct. */
+ dns_query_t *q = NULL;
+ bd::Array<bd::String> answer;
+ static int cnt = 0;
+try_again:
+ q = alloc_query(NULL, NULL, what);
+
+ if (is_ip)
+ query_transform_ip(q, what);
+
+ sdprintf("%s(%s, %d) -> %d", from, what, timeout, q->id);
+ if (cnt++ < nservers) {
+ answer = dns_send_blocking(q, timeout, type);
+ // No answers received?? Blocking DNS demands an answer, check the next server.
+ if (answer.size() == 0 && cnt < nservers) {
+ dns_on_eof(dns_idx);
+ goto try_again;
+ }
+ }
+ cnt = 0;
+ return answer;
+}
+
+
+bd::Array<bd::String> dns_lookup_block(const char *host, interval_t timeout, int type)
+{
+ if (is_dotted_ip(host)) {
+ bd::Array<bd::String> answer;
+ /* If it's already an ip, we're done. */
+ sdprintf("dns_lookup_block(%s, %d): Already an ip.", host, timeout);
+ answer << host;
+ return answer;
+ }
+
+ /* 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("dns_lookup_block(%s, %d): Found in hosts -> %s", host, timeout, hosts[i].ip);
+ answer << hosts[i].ip;
+ return answer;
+ }
+ }
+
+ int cache_id = cache_find(host);
+ if (cache_id >= 0) {
+// cache[cache_id].answer->shuffle();
+ sdprintf("dns_lookup_block(%s, %d): Found in cache -> %s", host, timeout, cache[cache_id].answer->join(',').c_str());
+ return *(cache[cache_id].answer);
+ }
+
+ return dns_blocking_loop(host, timeout, "dns_lookup_block", type);
+}
+
+/* Perform a blocking dns reverse lookup. This does ip -> host. For host -> ip
+ * use egg_dns_lookup(). */
+bd::Array<bd::String> dns_reverse_block(const char *ip, interval_t timeout)
+{
+
+ if (!is_dotted_ip(ip)) {
+ bd::Array<bd::String> answer;
+ /* If it's not a valid ip, don't even make the request. */
+ sdprintf("dns_reverse_block(%s, %d): Not an ip.", ip, timeout);
+ answer << ip;
+ return answer;
+ }
+
+ /* Ok, see if we have it in our host cache. */
+ for (int i = 0; i < nhosts; i++) {
+ if (!strcasecmp(hosts[i].ip, ip)) {
+ bd::Array<bd::String> answer;
+ sdprintf("dns_reverse_block(%s, %d): Found in hosts -> %s", ip, timeout, hosts[i].host);
+ answer << hosts[i].host;
+ return answer;
+ }
+ }
+
+ int cache_id = cache_find(ip);
+ if (cache_id >= 0) {
+// cache[cache_id].answer->shuffle();
+ sdprintf("dns_reverse_block(%s, %d): Found in cache -> %s", ip, timeout, cache[cache_id].answer->join(',').c_str());
+ return *(cache[cache_id].answer);
+ }
+
+ return dns_blocking_loop(ip, timeout, "dns_reverse_block", 0, 1);
+}
+
/* Perform an async dns reverse lookup. This does ip -> host. For host -> ip
* use egg_dns_lookup(). We return a dns id that you can use to cancel the
* lookup. */
@@ -549,26 +730,7 @@ int egg_dns_reverse(const char *ip, interval_t timeout, dns_callback_t callback,
q = alloc_query(client_data, callback, ip);
sdprintf("egg_dns_reverse(%s, %d) -> %d", ip, timeout, q->id);
- /* We need to transform the ip address into the proper form
- * for reverse lookup. */
- if (strchr(ip, ':')) {
- char temp[128] = "";
-
- socket_ipv6_to_dots(ip, temp);
-sdprintf("dots: %s", temp);
- size_t iplen = strlen(temp) + 9 + 1;
- q->ip = (char *) my_calloc(1, iplen);
-// reverse_ip(temp, q->ip);
- strlcat(q->ip, temp, iplen);
- strlcat(q->ip, "ip6.arpa", iplen);
-sdprintf("reversed ipv6 ip: %s", q->ip);
- }
- else {
- size_t iplen = strlen(ip) + 13 + 1;
- q->ip = (char *) my_calloc(1, iplen);
- reverse_ip(ip, q->ip);
- strlcat(q->ip, ".in-addr.arpa", iplen);
- }
+ query_transform_ip(q, ip);
dns_send_query(q);
@@ -578,10 +740,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;
@@ -589,23 +771,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);
@@ -835,7 +1007,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);
@@ -845,10 +1017,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);
}
@@ -892,14 +1061,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;
@@ -948,6 +1116,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;
}
@@ -956,9 +1125,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;
@@ -966,14 +1137,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;
}
@@ -1026,9 +1200,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);
}
@@ -1048,7 +1220,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;
@@ -1057,16 +1235,15 @@ static int parse_reply(char *response, size_t nbytes, const char* server_ip)
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;
}
View
2  src/adns.h
@@ -50,7 +50,9 @@ 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);
+bd::Array<bd::String> dns_reverse_block(const char *ip, interval_t timeout);
int egg_dns_cancel(int id, int issue_callback);
void tell_dnsdebug(int);
void dns_cache_flush();
View
54 src/net.c
@@ -112,7 +112,7 @@ int sockprotocol(int sock)
}
/* AF_INET-independent resolving routine */
-static int get_ip(char *hostname, union sockaddr_union *so)
+static int get_ip(char *hostname, union sockaddr_union *so, int dns_type)
{
if (!hostname || (hostname && !hostname[0]))
return 1;
@@ -120,41 +120,23 @@ static int get_ip(char *hostname, union sockaddr_union *so)
memset(so, 0, sizeof(union sockaddr_union));
debug1("get_ip(%s)", hostname);
-#ifdef USE_IPV6
- struct addrinfo hints, *ai = NULL, *res = NULL;
- int error = 0;
-
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_socktype = SOCK_STREAM;
+ bd::Array<bd::String> hosts = dns_lookup_block(hostname, 10, dns_type);
+ if (hosts.length() == 0)
+ return -1;
- if ((error = getaddrinfo(hostname, NULL, &hints, &res))) {
- if (res)
- freeaddrinfo(res);
- return error;
- }
+ my_addr_t addr;
+ get_addr(bd::String(hosts[0]).c_str(), &addr);
- error = 1;
- for (ai = res; ai != NULL; ai = ai->ai_next) {
- if ((ai->ai_family == AF_INET6) || (ai->ai_family == AF_INET)) {
- memcpy(so, ai->ai_addr, ai->ai_addrlen);
- error = 0;
- break;
- }
+ if (addr.family == AF_INET) {
+ so->sin.sin_family = AF_INET;
+ memcpy(&so->sin.sin_addr, &addr.u.addr, sizeof(addr.u.addr));
+#ifdef USE_IPV6
+ } else if (addr.family == AF_INET6) {
+ so->sin6.sin6_family = AF_INET6;
+ memcpy(&so->sin6.sin6_addr, &addr.u.addr6, sizeof(addr.u.addr6));
+#endif
}
-
- if (res)
- freeaddrinfo(res);
- return error;
-#else
- struct hostent *hp = NULL;
-
- if (!(hp = gethostbyname(hostname)))
- return -1;
-
- memcpy(&so->sin.sin_addr, hp->h_addr, 4);
- so->sin.sin_family = AF_INET;
return 0;
-#endif /* USE_IPV6 */
}
/* Initialize the socklist
@@ -228,10 +210,10 @@ void cache_my_ip()
memset(&cached_myip6_so, 0, sizeof(union sockaddr_union));
if (conf.bot->net.ip6) {
- if (get_ip(conf.bot->net.ip6, &cached_myip6_so))
+ if (get_ip(conf.bot->net.ip6, &cached_myip6_so, DNS_LOOKUP_AAAA))
any = 1;
} else if (conf.bot->net.host6) {
- if (get_ip(conf.bot->net.host6, &cached_myip6_so))
+ if (get_ip(conf.bot->net.host6, &cached_myip6_so, DNS_LOOKUP_AAAA))
any = 1;
} else
any = 1;
@@ -243,10 +225,10 @@ void cache_my_ip()
#endif /* USE_IPV6 */
if (conf.bot->net.ip) {
- if (get_ip(conf.bot->net.ip, &cached_myip4_so))
+ if (get_ip(conf.bot->net.ip, &cached_myip4_so, DNS_LOOKUP_A))
error = 1;
} else if (conf.bot->net.host) {
- if (get_ip(conf.bot->net.host, &cached_myip4_so))
+ if (get_ip(conf.bot->net.host, &cached_myip4_so, DNS_LOOKUP_A))
error = 2;
} else {
/*
Please sign in to comment.
Something went wrong with that request. Please try again.