From ec6a1ac308e07e037d3274c9381cdc69523ebf9e Mon Sep 17 00:00:00 2001 From: Erik Ekman Date: Thu, 23 Jul 2020 21:36:41 +0200 Subject: [PATCH] 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). --- src/dns.c | 53 ++++++++++++++++++++++++++++++--------------------- src/iodined.c | 12 +++++++++++- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/dns.c b/src/dns.c index f6aad55..9741606 100644 --- a/src/dns.c +++ b/src/dns.c @@ -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++)); diff --git a/src/iodined.c b/src/iodined.c index 647892d..21acbf7 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -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");