diff --git a/CHANGELOG b/CHANGELOG index 057ef9a..2335411 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,8 @@ CHANGES: - Added capability to forward DNS queries outside tunnel domain to a nameserver on localhost. Use -b port to enable. - iodine client now shuts down if it detects a server restart. + - iodined now replies to NS request on its own domain, fixes issue #33. + The destination IP address is sent as reply. 2008-08-06: 0.4.2 "Opened Zone" - Applied a few small patches from Maxim Bourmistrov and Gregor Herrmann diff --git a/src/dns.c b/src/dns.c index 94ee906..3f05f37 100644 --- a/src/dns.c +++ b/src/dns.c @@ -98,6 +98,85 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ return len; } +int +dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain) +{ + HEADER *header; + int len; + short name; + short topname; + short nsname; + char *domain; + int domain_len; + char *p; + + 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); + header->arcount = htons(1); + + /* pointer to start of name */ + name = 0xc000 | ((p - buf) & 0x3fff); + + domain = strstr(q->name, topdomain); + if (domain) { + domain_len = (int) (domain - q->name); + } else { + return -1; + } + /* pointer to start of topdomain */ + topname = 0xc000 | ((p - buf + domain_len) & 0x3fff); + + /* Query section */ + putname(&p, sizeof(q->name), q->name); /* Name */ + putshort(&p, q->type); /* Type */ + putshort(&p, C_IN); /* Class */ + + /* Answer section */ + putshort(&p, name); /* Name */ + putshort(&p, q->type); /* Type */ + putshort(&p, C_IN); /* Class */ + putlong(&p, 0x3ea7d011); /* TTL */ + putshort(&p, 5); /* Data length */ + + /* pointer to ns.topdomain */ + nsname = 0xc000 | ((p - buf) & 0x3fff); + putbyte(&p, 2); + putbyte(&p, 'n'); + putbyte(&p, 's'); + putshort(&p, topname); /* Name Server */ + + /* Additional data (A-record of NS server) */ + putshort(&p, nsname); /* Name Server */ + putshort(&p, T_A); /* Type */ + putshort(&p, C_IN); /* Class */ + putlong(&p, 0x3ea7d011); /* TTL */ + putshort(&p, 4); /* Data length */ + + /* ugly hack to output IP address */ + domain = (char *) &q->destination; + putbyte(&p, *domain++); + putbyte(&p, *domain++); + putbyte(&p, *domain++); + putbyte(&p, *domain); + + len = p - buf; + return len; +} + short dns_get_id(char *packet, size_t packetlen) { diff --git a/src/dns.h b/src/dns.h index 56c15d3..92ebc46 100644 --- a/src/dns.h +++ b/src/dns.h @@ -25,6 +25,7 @@ typedef enum { } qr_t; int dns_encode(char *, size_t, struct query *, qr_t, char *, size_t); +int dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain); short dns_get_id(char *packet, size_t packetlen); int dns_decode(char *, size_t, struct query *, qr_t, char *, size_t); diff --git a/src/iodined.c b/src/iodined.c index 4881a81..77d5002 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -333,6 +333,19 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) } } +static void +handle_ns_request(int dns_fd, struct query *q) +{ + char buf[64*1024]; + int len; + + len = dns_encode_ns_response(buf, sizeof(buf), q, topdomain); + + if (sendto(dns_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) { + warn("ns reply send error"); + } +} + static void forward_query(int bind_fd, struct query *q) { @@ -443,6 +456,9 @@ tunnel_dns(int tun_fd, int dns_fd, int bind_fd) case T_NULL: handle_null_request(tun_fd, dns_fd, &q, domain_len); break; + case T_NS: + handle_ns_request(dns_fd, &q); + break; default: break; }