Fix IPv4 address in replies to A or NS queries (github issue #38)

The destination field in struct query was changed from in_addr_t to
struct sockaddr_storage, wihtout updating the functions sending it
in src/dns.c.

Only add extra A answer for NS queries if destination refers to an
IPv4 address, and fail if trying to encode a reply to an A query
if destination is not IPv4.

This means NS requests received over IPv6 will not contain an address
and A requests will be ignored, unless the -n option is used, or the
www subdomain is requested which sets a fixed address (127.0.0.1).
This commit is contained in:
Erik Ekman 2020-07-23 21:36:41 +02:00
parent d09c3e4f0b
commit ec6a1ac308
2 changed files with 42 additions and 23 deletions

View File

@ -299,21 +299,25 @@ int dns_encode_ns_response(char *buf, size_t buflen, struct query *q,
putbyte(&p, 's');
putshort(&p, topname); /* Name Server */
/* Additional data (A-record of NS server) */
CHECKLEN(12);
putshort(&p, nsname); /* Name Server */
putshort(&p, T_A); /* Type */
putshort(&p, C_IN); /* Class */
putlong(&p, 3600); /* TTL */
putshort(&p, 4); /* Data length */
/* Do we have an IPv4 address to send? */
if (q->destination.ss_family == AF_INET) {
struct sockaddr_in *dest = (struct sockaddr_in *) &q->destination;
/* Additional data (A-record of NS server) */
CHECKLEN(12);
putshort(&p, nsname); /* Name Server */
putshort(&p, T_A); /* Type */
putshort(&p, C_IN); /* Class */
putlong(&p, 3600); /* TTL */
putshort(&p, 4); /* Data length */
/* ugly hack to output IP address */
ipp = (char *) &q->destination;
CHECKLEN(4);
putbyte(&p, *(ipp++));
putbyte(&p, *(ipp++));
putbyte(&p, *(ipp++));
putbyte(&p, *ipp);
/* ugly hack to output IP address */
ipp = (char *) &dest->sin_addr.s_addr;
CHECKLEN(4);
putbyte(&p, *(ipp++));
putbyte(&p, *(ipp++));
putbyte(&p, *(ipp++));
putbyte(&p, *ipp);
}
len = p - buf;
return len;
@ -323,12 +327,17 @@ int dns_encode_ns_response(char *buf, size_t buflen, struct query *q,
* www.topdomain . Mostly same as dns_encode_ns_response() above */
int dns_encode_a_response(char *buf, size_t buflen, struct query *q)
{
struct sockaddr_in *dest = (struct sockaddr_in *) &q->destination;
HEADER *header;
int len;
short name;
char *ipp;
char *p;
/* Check if we have an IPv4 address to send */
if (q->destination.ss_family != AF_INET)
return -1;
if (buflen < sizeof(HEADER))
return 0;
@ -355,19 +364,19 @@ int dns_encode_a_response(char *buf, size_t buflen, struct query *q)
/* Query section */
putname(&p, buflen - (p - buf), q->name); /* Name */
CHECKLEN(4);
putshort(&p, q->type); /* Type */
putshort(&p, C_IN); /* Class */
putshort(&p, q->type); /* Type */
putshort(&p, C_IN); /* Class */
/* Answer section */
CHECKLEN(12);
putshort(&p, name); /* Name */
putshort(&p, q->type); /* Type */
putshort(&p, C_IN); /* Class */
putlong(&p, 3600); /* TTL */
putshort(&p, 4); /* Data length */
putshort(&p, name); /* Name */
putshort(&p, q->type); /* Type */
putshort(&p, C_IN); /* Class */
putlong(&p, 3600); /* TTL */
putshort(&p, 4); /* Data length */
/* ugly hack to output IP address */
ipp = (char *) &q->destination;
ipp = (char *) &dest->sin_addr.s_addr;
CHECKLEN(4);
putbyte(&p, *(ipp++));
putbyte(&p, *(ipp++));

View File

@ -1549,7 +1549,9 @@ handle_ns_request(int dns_fd, struct query *q)
/* If ns_ip set, overwrite destination addr with it.
* Destination addr will be sent as additional record (A, IN) */
struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination;
addr->sin_family = AF_INET;
memcpy(&addr->sin_addr, &ns_ip, sizeof(ns_ip));
q->dest_len = sizeof(*addr);
}
len = dns_encode_ns_response(buf, sizeof(buf), q, topdomain);
@ -1577,15 +1579,23 @@ handle_a_request(int dns_fd, struct query *q, int fakeip)
if (fakeip) {
in_addr_t ip = inet_addr("127.0.0.1");
struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination;
addr->sin_family = AF_INET;
memcpy(&addr->sin_addr, &ip, sizeof(ip));
q->dest_len = sizeof(*addr);
} else if (ns_ip != INADDR_ANY) {
/* If ns_ip set, overwrite destination addr with it.
* Destination addr will be sent as additional record (A, IN) */
struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination;
addr->sin_family = AF_INET;
memcpy(&addr->sin_addr, &ns_ip, sizeof(ns_ip));
q->dest_len = sizeof(*addr);
}
/* Give up if no IPv4 address known (when A request received over IPv6
* and destination was not overwritten above) */
if (q->destination.ss_family != AF_INET)
return;
len = dns_encode_a_response(buf, sizeof(buf), q);
if (len < 1) {
warnx("dns_encode_a_response doesn't fit");