From 4a9887c9df4756eabb018351cc66abbdd14c77a7 Mon Sep 17 00:00:00 2001
From: Bjorn Andersson <flex@kryo.se>
Date: Wed, 16 Aug 2006 18:12:15 +0000
Subject: [PATCH] rewrote READ* and PUT*; in read.c, handeling malformed
 compression of names

---
 dns.c  |  88 +++++++++++++++++++++-------------------
 dns.h  |   1 -
 read.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 read.h |  21 ++++------
 4 files changed, 162 insertions(+), 73 deletions(-)

diff --git a/dns.c b/dns.c
index 051412a..cada4a1 100644
--- a/dns.c
+++ b/dns.c
@@ -32,18 +32,20 @@
 #include <string.h>
 #include <ctype.h>
 
-#include "read.h"
 #include "structs.h"
 #include "dns.h"
 #include "encoding.h"
+#include "read.h"
 
 // For FreeBSD
 #ifndef MIN
 #define MIN(a,b) ((a)<(b)?(a):(b))
 #endif
 
+
 static int host2dns(const char *, char *, int);
 static int dns_write(int, int, char *, int, char);
+static void dns_query(int, int, char *, int);
 
 struct sockaddr_in peer;
 char topdomain[256];
@@ -68,7 +70,8 @@ open_dns(const char *domain, int localport, in_addr_t listen_ip)
 	bzero(&addr, sizeof(addr));
 	addr.sin_family = AF_INET;
 	addr.sin_port = htons(localport);
-	addr.sin_addr.s_addr = listen_ip; // This is already network byte order, inet_addr() or constant INADDR_ANY (==0)
+	/* listen_ip already in network byte order from inet_addr, or 0 */
+	addr.sin_addr.s_addr = listen_ip; 
 
 	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 	if(fd < 0) {
@@ -88,10 +91,11 @@ open_dns(const char *domain, int localport, in_addr_t listen_ip)
 	}
 
 	// Save top domain used
-	strncpy(topdomain, domain, sizeof(topdomain) - 2);
-	topdomain[sizeof(topdomain) - 1] = 0;
+	strncpy(topdomain, domain, sizeof(topdomain) - 1);
+	topdomain[sizeof(topdomain) - 1] = '\0';
 
 	printf("Opened UDP socket\n");
+
 	return fd;
 }
 
@@ -177,7 +181,7 @@ dns_handshake(int dns_fd)
 	dns_write(dns_fd, ++pingid, data, 2, 'H');
 }
 
-void 
+static void 
 dns_query(int fd, int id, char *host, int type)
 {
 	char *p;
@@ -205,16 +209,16 @@ dns_query(int fd, int id, char *host, int type)
 	p = buf + sizeof(HEADER);
 	p += host2dns(host, p, strlen(host));	
 
-	PUTSHORT(type, p);
-	PUTSHORT(C_IN, p);
+	putshort(&p, type);
+	putshort(&p, C_IN);
 
 	// EDNS0
-	*p++ = 0x00; //Root
-	PUTSHORT(0x0029, p); // OPT
-	PUTSHORT(0x1000, p); // Payload size: 4096
-	PUTSHORT(0x0000, p); // Higher bits/ edns version
-	PUTSHORT(0x8000, p); // Z
-	PUTSHORT(0x0000, p); // Data length
+	putbyte(&p, 0x00); //Root
+	putshort(&p, 0x0029); // OPT
+	putshort(&p, 0x1000); // Payload size: 4096
+	putshort(&p, 0x0000); // Higher bits/edns version
+	putshort(&p, 0x8000); // Z
+	putshort(&p, 0x0000); // Data length
 
 	peerlen = sizeof(peer);
 
@@ -250,6 +254,7 @@ int
 dns_read(int fd, char *buf, int buflen)
 {
 	int r;
+	int rv;
 	long ttl;
 	short rlen;
 	short type;
@@ -267,6 +272,7 @@ dns_read(int fd, char *buf, int buflen)
 	addrlen = sizeof(struct sockaddr);
 	r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen);
 
+	rv = 0;
 	if(r == -1) {
 		perror("recvfrom");
 	} else {
@@ -281,17 +287,17 @@ dns_read(int fd, char *buf, int buflen)
 			rlen = 0;
 
 			if(qdcount == 1) {
-				READNAME(packet, name, data);
-				READSHORT(type, data);
-				READSHORT(class, data);
+				readname(packet, &data, name, sizeof(name));
+				readshort(packet, &data, &type);
+				readshort(packet, &data, &class);
 			}
 			if(ancount == 1) {
-				READNAME(packet, name, data);
-				READSHORT(type, data);
-				READSHORT(class, data);
-				READLONG(ttl, data);
-				READSHORT(rlen, data);
-				READDATA(rdata, data, rlen);
+				readname(packet, &data, name, sizeof(name));
+				readshort(packet, &data, &type);
+				readshort(packet, &data, &class);
+				readlong(packet, &data, &ttl);
+				readshort(packet, &data, &rlen);
+				readdata(packet, &data, rdata, rlen);
 			}
 			if (dns_sending() && chunkid == ntohs(header->id)) {
 				// Got ACK on sent packet
@@ -309,14 +315,12 @@ dns_read(int fd, char *buf, int buflen)
 
 			if(type == T_NULL && rlen > 2) {
 				memcpy(buf, rdata, rlen);
-				return rlen;
-			} else {
-				return 0;
+				rv = rlen;
 			}
 		}
 	}
 
-	return 0;
+	return rv;
 }
 
 static int
@@ -375,19 +379,18 @@ dnsd_send(int fd, struct query *q, char *data, int datalen)
 	
 	name = 0xc000 | ((p - buf) & 0x3fff);
 	p += host2dns(q->name, p, strlen(q->name));
-	PUTSHORT(q->type, p);
-	PUTSHORT(C_IN, p);
-	
-	PUTSHORT(name, p);
-	PUTSHORT(q->type, p);
-	PUTSHORT(C_IN, p);
-	PUTLONG(0, p);
+	putshort(&p, q->type);
+	putshort(&p, C_IN);
+
+	putshort(&p, name);	
+	putshort(&p, q->type);
+	putshort(&p, C_IN);
+	putlong(&p, 0);
 
 	q->id = 0;
 
-	PUTSHORT(datalen, p);
-	memcpy(p, data, datalen);
-	p += datalen;
+	putshort(&p, datalen);
+	putdata(&p, data, datalen);
 
 	len = p - buf;
 	sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen);
@@ -411,6 +414,7 @@ int
 dnsd_read(int fd, struct query *q, char *buf, int buflen)
 {
 	int r;
+	int rv;
 	short id;
 	short type;
 	short class;
@@ -436,10 +440,9 @@ dnsd_read(int fd, struct query *q, char *buf, int buflen)
 			qdcount = ntohs(header->qdcount);
 
 			if(qdcount == 1) {
-				bzero(name, sizeof(name));
-				READNAME(packet, name, data);
-				READSHORT(type, data);
-				READSHORT(class, data);
+				readname(packet, &data, name, sizeof(name));
+				readshort(packet, &data, &type);
+				readshort(packet, &data, &class);
 
 				strncpy(q->name, name, 256);
 				q->type = type;
@@ -447,12 +450,13 @@ dnsd_read(int fd, struct query *q, char *buf, int buflen)
 				q->fromlen = addrlen;
 				memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen);
 
-				return decodepacket(name, buf, buflen);
+				rv = decodepacket(name, buf, buflen);
 			}
 		}
 	} else {
 		perror("recvfrom");
+		rv = 0;
 	}
 
-	return 0;
+	return rv;
 }
diff --git a/dns.h b/dns.h
index 62f6ca1..c64f58c 100644
--- a/dns.h
+++ b/dns.h
@@ -25,7 +25,6 @@ int dns_sending();
 void dns_handle_tun(int, char *, int);
 void dns_ping(int);
 void dns_handshake(int);
-void dns_query(int, int, char *, int);
 int dns_read(int, char *, int);
 
 extern struct sockaddr_in peer;
diff --git a/read.c b/read.c
index 4007cf9..2cc5ddf 100644
--- a/read.c
+++ b/read.c
@@ -17,38 +17,129 @@
 #include <string.h>
 
 int
-readname(char *packet, char *dst, char *src)
+readname(char *packet, char **src, char *dst, size_t length)
 {
-	char l;
+	char *dummy;
 	int len;
-	int offset;
+	char *p;
+	char c;
 
 	len = 0;
 
-	while(*src) {
-		l = *src++;
-		len++;
+	p = *src;
 
-		if(l & 0x80 && l & 0x40) {
-			offset = ((src[-1] & 0x3f) << 8) | src[0];		
-			readname(packet, dst, packet + offset);
-			dst += strlen(dst);
+	p = *src;
+	while(*p && len < length) {
+		c = *p++;
+
+		/* is this a compressed label? */
+		if((c & 0xc0) == 0xc0) {
+			dummy = packet + (((p[-1] & 0x3f) << 8) | p[0]);
+			readname(packet, &dummy, dst, length - len);
 			break;
 		}
 
-		while(l) {
-			*dst++ = *src++;
-			l--;
+		while(c && len < length) {
+			*dst++ = *p++;
 			len++;
+
+			c--;
 		}
 
-		*dst++ = '.';
+		if (*p != 0)
+			*dst = '.';
+		else 
+			*dst = '\0';
 	}
+	(*src) = p+1;
 
-	*dst = '\0';
-	src++;
-	len++;
+	return strlen(dst);
+}
+
+int
+readshort(char *packet, char **src, short *dst)
+{
+	char *p;
+
+	p = *src;
+	*dst = ((short)p[0] << 8) 
+		 | ((short)p[1]);
+
+	(*src) += sizeof(short);
+	return sizeof(short);
+}
+
+int
+readlong(char *packet, char **src, long *dst)
+{
+	char *p;
+
+	p = *src;
+
+	*dst = ((long)p[0] << 24) 
+		 | ((long)p[1] << 16) 
+		 | ((long)p[2] << 8)
+		 | ((long)p[3]);
+
+	(*src) += sizeof(long);
+	return sizeof(long);
+}
+
+int
+readdata(char *packet, char **src, char *dst, size_t len)
+{
+	memcpy(dst, *src, len);
+
+	(*src) += len;
 
 	return len;
 }
 
+int
+putbyte(char **dst, char value)
+{
+	**dst = value;
+	(*dst)++;
+
+	return sizeof(char);
+}
+
+int
+putshort(char **dst, short value)
+{
+	char *p;
+
+	p = *dst;
+
+	*p++ = (value >> 8);
+	*p++ = value;
+
+	(*dst) = p;
+	return sizeof(short);
+}
+
+int
+putlong(char **dst, long value)
+{
+	char *p;
+
+	p = *dst;
+
+	*p++ = (value >> 24);
+	*p++ = (value >> 16);
+	*p++ = (value >> 8);
+	*p++ = (value);
+
+	(*dst) = p;
+	return sizeof(long);
+}
+
+int
+putdata(char **dst, char *data, size_t len)
+{
+	memcpy(*dst, data, len);
+	
+	(*dst) += len;
+	return len;
+}
+
diff --git a/read.h b/read.h
index a4075ec..6117b7b 100644
--- a/read.h
+++ b/read.h
@@ -17,19 +17,14 @@
 #ifndef _READ_H_
 #define _READ_H_
 
-int readname(char *, char *, char *);
+int readname(char *, char **, char *, size_t);
+int readshort(char *, char **, short *);
+int readlong(char *, char **, long *);
+int readdata(char *, char **, char *, size_t);
 
-#define READNAME(packet, dst, src) (src) += readname((packet), (dst), (src));
-
-#define READSHORT(dst, src) \
-	memcpy(&dst, src, 2); \
-        (dst) = ntohs(dst); (src)+=2;
-
-#define READLONG(dst, src) \
-	memcpy(&dst, src, 2); \
-	(dst) = ntohl(dst); (src)+=4; 
-
-#define READDATA(dst, src, len) \
-	memcpy((dst), (src), (len)); (src)+=(len);
+int putbyte(char **, char);
+int putshort(char **, short);
+int putlong(char **, long);
+int putdata(char **, char *, size_t);
 
 #endif