diff --git a/src/dns.c b/src/dns.c index dd6e313..86bb3c2 100644 --- a/src/dns.c +++ b/src/dns.c @@ -227,6 +227,72 @@ int dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, return len; } +/* Only used when iodined gets a CAA type query */ +/* Mostly same as dns_encode_ns_response() below */ +int dns_encode_caa_response(char *buf, size_t buflen, struct query *q, + char *topdomain) +{ + HEADER *header; + int len; + short name; + int domain_len; + char *p; + + if (buflen < sizeof(HEADER)) + return 0; + + memset(buf, 0, buflen); + + header = (HEADER*)buf; + + header->id = htons(q->id); + header->qr = 1; + header->opcode = 0; + header->aa = 1; + header->tc = 0; + header->rd = 0; + header->ra = 0; + + p = buf + sizeof(HEADER); + + header->qdcount = htons(1); + header->ancount = htons(1); + + /* pointer to start of name */ + name = 0xc000 | ((p - buf) & 0x3fff); + + domain_len = strlen(q->name) - strlen(topdomain); + if (domain_len < 0 || domain_len == 1) + return -1; + if (strcasecmp(q->name + domain_len, topdomain)) + return -1; + if (domain_len >= 1 && q->name[domain_len - 1] != '.') + return -1; + + /* Query section */ + putname(&p, buflen - (p - buf), q->name); /* Name */ + CHECKLEN(4); + 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, 22); /* Data length */ + + /* caa record */ + CHECKLEN(22); + memcpy(p, "\x00\x05\x69\x73\x73\x75\x65\x6c\x65\x74\x73\x65\x6e\x63\x72\x79\x70" \ + "\x74\x2e\x6f\x72\x67", 22); + p += 22; + + len = p - buf; + return len; +} + /* Only used when iodined gets an NS type query */ /* Mostly same as dns_encode_a_response() below */ int dns_encode_ns_response(char *buf, size_t buflen, struct query *q, diff --git a/src/dns.h b/src/dns.h index 660f610..80ac985 100644 --- a/src/dns.h +++ b/src/dns.h @@ -28,6 +28,8 @@ typedef enum { extern int dnsc_use_edns0; int dns_encode(char *, size_t, struct query *, qr_t, const char *, size_t); +int dns_encode_caa_response(char *buf, size_t buflen, struct query *q, + char *topdomain); int dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain); int dns_encode_a_response(char *buf, size_t buflen, struct query *q); diff --git a/src/iodined.c b/src/iodined.c index b3230cf..8dc361a 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -1522,6 +1522,30 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query } } +static void +handle_caa_request(int dns_fd, struct query *q, int topdomain_offset) +/* Mostly identical to handle_caa_request() below */ +{ + char buf[64*1024]; + int len; + /* Use possibly dynamic top domain in reply */ + char *resp_domain = q->name + topdomain_offset; + + len = dns_encode_caa_response(buf, sizeof(buf), q, resp_domain); + if (len < 1) { + warnx("dns_encode_caa_response doesn't fit"); + return; + } + + if (debug >= 2) { + fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes NS reply\n", + format_addr(&q->from, q->fromlen), q->type, q->name, len); + } + if (sendto(dns_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) { + warn("ns reply send error"); + } +} + static void handle_ns_request(int dns_fd, struct query *q, int topdomain_offset) /* Mostly identical to handle_a_request() below */ @@ -1689,12 +1713,13 @@ tunnel_dns(int tun_fd, int dns_fd, struct dnsfd *dns_fds, int bind_fd) if ((read = read_dns(dns_fd, dns_fds, tun_fd, &q)) <= 0) return 0; + domain_len = query_datalen(q.name, topdomain); + if (debug >= 2) { - fprintf(stderr, "RX: client %s, type %d, name %s\n", - format_addr(&q.from, q.fromlen), q.type, q.name); + fprintf(stderr, "RX: client %s, type %d, name %s, domain_len=%d\n", + format_addr(&q.from, q.fromlen), q.type, q.name, domain_len); } - domain_len = query_datalen(q.name, topdomain); if (domain_len >= 0) { /* This is a query we can handle */ @@ -1740,10 +1765,19 @@ tunnel_dns(int tun_fd, int dns_fd, struct dnsfd *dns_fds, int bind_fd) case T_NS: handle_ns_request(dns_fd, &q, domain_len); break; + case T_CAA: + handle_caa_request(dns_fd, &q, domain_len); + break; default: + if (debug >= 0) { + fprintf(stderr, "WARN: can't handle request of type: %d\n", q.type); + } break; } } else { + if (debug >= 0) { + fprintf(stderr, "WARN: can't handle non-authoritiative domain request"); + } /* Forward query to other port ? */ if (bind_fd) { forward_query(bind_fd, &q);