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,6 +299,9 @@ int dns_encode_ns_response(char *buf, size_t buflen, struct query *q,
putbyte(&p, 's'); putbyte(&p, 's');
putshort(&p, topname); /* Name Server */ putshort(&p, topname); /* Name Server */
/* 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) */ /* Additional data (A-record of NS server) */
CHECKLEN(12); CHECKLEN(12);
putshort(&p, nsname); /* Name Server */ putshort(&p, nsname); /* Name Server */
@ -308,12 +311,13 @@ int dns_encode_ns_response(char *buf, size_t buflen, struct query *q,
putshort(&p, 4); /* Data length */ putshort(&p, 4); /* Data length */
/* ugly hack to output IP address */ /* ugly hack to output IP address */
ipp = (char *) &q->destination; ipp = (char *) &dest->sin_addr.s_addr;
CHECKLEN(4); CHECKLEN(4);
putbyte(&p, *(ipp++)); putbyte(&p, *(ipp++));
putbyte(&p, *(ipp++)); putbyte(&p, *(ipp++));
putbyte(&p, *(ipp++)); putbyte(&p, *(ipp++));
putbyte(&p, *ipp); putbyte(&p, *ipp);
}
len = p - buf; len = p - buf;
return len; 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 */ * www.topdomain . Mostly same as dns_encode_ns_response() above */
int dns_encode_a_response(char *buf, size_t buflen, struct query *q) int dns_encode_a_response(char *buf, size_t buflen, struct query *q)
{ {
struct sockaddr_in *dest = (struct sockaddr_in *) &q->destination;
HEADER *header; HEADER *header;
int len; int len;
short name; short name;
char *ipp; char *ipp;
char *p; char *p;
/* Check if we have an IPv4 address to send */
if (q->destination.ss_family != AF_INET)
return -1;
if (buflen < sizeof(HEADER)) if (buflen < sizeof(HEADER))
return 0; return 0;
@ -367,7 +376,7 @@ int dns_encode_a_response(char *buf, size_t buflen, struct query *q)
putshort(&p, 4); /* Data length */ putshort(&p, 4); /* Data length */
/* ugly hack to output IP address */ /* ugly hack to output IP address */
ipp = (char *) &q->destination; ipp = (char *) &dest->sin_addr.s_addr;
CHECKLEN(4); CHECKLEN(4);
putbyte(&p, *(ipp++)); putbyte(&p, *(ipp++));
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. /* If ns_ip set, overwrite destination addr with it.
* Destination addr will be sent as additional record (A, IN) */ * Destination addr will be sent as additional record (A, IN) */
struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination; struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination;
addr->sin_family = AF_INET;
memcpy(&addr->sin_addr, &ns_ip, sizeof(ns_ip)); memcpy(&addr->sin_addr, &ns_ip, sizeof(ns_ip));
q->dest_len = sizeof(*addr);
} }
len = dns_encode_ns_response(buf, sizeof(buf), q, topdomain); 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) { if (fakeip) {
in_addr_t ip = inet_addr("127.0.0.1"); in_addr_t ip = inet_addr("127.0.0.1");
struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination; struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination;
addr->sin_family = AF_INET;
memcpy(&addr->sin_addr, &ip, sizeof(ip)); memcpy(&addr->sin_addr, &ip, sizeof(ip));
q->dest_len = sizeof(*addr);
} else if (ns_ip != INADDR_ANY) { } else if (ns_ip != INADDR_ANY) {
/* If ns_ip set, overwrite destination addr with it. /* If ns_ip set, overwrite destination addr with it.
* Destination addr will be sent as additional record (A, IN) */ * Destination addr will be sent as additional record (A, IN) */
struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination; struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination;
addr->sin_family = AF_INET;
memcpy(&addr->sin_addr, &ns_ip, sizeof(ns_ip)); 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); len = dns_encode_a_response(buf, sizeof(buf), q);
if (len < 1) { if (len < 1) {
warnx("dns_encode_a_response doesn't fit"); warnx("dns_encode_a_response doesn't fit");