diff --git a/KernelLand/Kernel/Makefile b/KernelLand/Kernel/Makefile
index 34e6bcfd24d7fc2470d2e921ff833df1d572a1be..895d90f9878e99f141b739b8e3ca368551e94e55 100644
--- a/KernelLand/Kernel/Makefile
+++ b/KernelLand/Kernel/Makefile
@@ -153,7 +153,7 @@ $(BUILDINFO_SRC): $(filter-out $(BUILDINFO_OBJ), $(OBJ)) $(MODS) arch/$(ARCHDIR)
 	@echo "const char gsGitHash[] = \"$(_GITHASH)\";" >> $@
 	@echo "const int giBuildNumber = $(BUILD_NUM);" >> $@
 	@echo "const char gsBuildInfo[] = \"Acess2 v$(KERNEL_VERSION) $(ARCH)-$(PLATFORM)\\\\r\\\\n\"" >> $@
-	@echo "                           \"Build $(shell hostname --fqdn):$(BUILD_NUM) Git $(_GITHASH) - $(_GITCHANGED) modified\";" >> $@
+	@echo "                           \"Build $(shell hostname --fqdn):$(BUILD_NUM) $(shell date +%FT%T%z) Git $(_GITHASH) - $(_GITCHANGED) modified\";" >> $@
 # Compile rule for buildinfo (needs a special one because it's not a general source file)
 $(BUILDINFO_OBJ): $(BUILDINFO_SRC)
 	@echo --- CC -o $@
diff --git a/KernelLand/Modules/IPStack/udp.c b/KernelLand/Modules/IPStack/udp.c
index 67f6056128718099cef4cddc6316ae11a1e5f9e2..b9dd3231442851e934daf8e0086bf8b21f00a54c 100644
--- a/KernelLand/Modules/IPStack/udp.c
+++ b/KernelLand/Modules/IPStack/udp.c
@@ -67,13 +67,15 @@ void UDP_Initialise()
 int UDP_int_ScanList(tUDPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer)
 {
 	tUDPHeader	*hdr = Buffer;
-	tUDPChannel	*chan;
-	tUDPPacket	*pack;
-	 int	len;
 	
-	for(chan = List; chan; chan = chan->Next)
+	for(tUDPChannel *chan = List; chan; chan = chan->Next)
 	{
 		// Match local endpoint
+		LOG("(%p):%i - %s/%i:%i",
+			chan->Interface, chan->LocalPort,
+			IPStack_PrintAddress(chan->Remote.AddrType, &chan->Remote.Addr), chan->RemoteMask,
+			chan->Remote.Port
+			);
 		if(chan->Interface && chan->Interface != Interface)	continue;
 		if(chan->LocalPort != ntohs(hdr->DestPort))	continue;
 		
@@ -93,8 +95,8 @@ int UDP_int_ScanList(tUDPChannel *List, tInterface *Interface, void *Address, in
 		
 		Log_Log("UDP", "Recieved packet for %p", chan);
 		// Create the cached packet
-		len = ntohs(hdr->Length);
-		pack = malloc(sizeof(tUDPPacket) + len);
+	 	int len = ntohs(hdr->Length);
+		tUDPPacket *pack = malloc(sizeof(tUDPPacket) + len);
 		pack->Next = NULL;
 		memcpy(&pack->Remote.Addr, Address, IPStack_GetAddressSize(Interface->Type));
 		pack->Remote.Port = ntohs(hdr->SourcePort);
@@ -159,7 +161,11 @@ void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, U
 {
 	tUDPHeader	hdr;
 
-	if(Channel->Interface && Channel->Interface->Type != AddrType)	return ;
+	if(Channel->Interface && Channel->Interface->Type != AddrType) {
+		LOG("Bad interface type for channel packet, IF is %i, but packet is %i",
+			Channel->Interface->Type, AddrType);
+		return ;
+	}
 	
 	// Create the packet
 	hdr.SourcePort = htons( Channel->LocalPort );
@@ -177,6 +183,7 @@ void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, U
 		IPStack_Buffer_AppendSubBuffer(buffer, Length, 0, Data, NULL, NULL);
 		IPStack_Buffer_AppendSubBuffer(buffer, sizeof(hdr), 0, &hdr, NULL, NULL);
 		// TODO: What if Channel->Interface is NULL here?
+		ASSERT(Channel->Interface);
 		IPv4_SendPacket(Channel->Interface, *(tIPv4*)Address, IP4PROT_UDP, 0, buffer);
 		break;
 	default:
@@ -205,49 +212,59 @@ tVFS_Node *UDP_Channel_Init(tInterface *Interface)
 	return &new->Node;
 }
 
-/**
- * \brief Read from the channel file (wait for a packet)
- */
-size_t UDP_Channel_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
+tUDPPacket *UDP_Channel_WaitForPacket(tUDPChannel *chan, Uint VFSFlags)
 {
-	tUDPChannel	*chan = Node->ImplPtr;
-	tUDPPacket	*pack;
-	tUDPEndpoint	*ep;
-	 int	ofs, addrlen;
-	
-	if(chan->LocalPort == 0) {
-		Log_Notice("UDP", "Channel %p sent with no local port", chan);
-		return 0;
-	}
-	
-	while(chan->Queue == NULL)	Threads_Yield();
+	// EVIL - Yield until queue is created (avoids races)
+	while(chan->Queue == NULL)
+		Threads_Yield();
 	
 	for(;;)
 	{
-		tTime	timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
-		int rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "UDP_Channel_Read");
-		if( rv ) {
-			errno = (Flags & VFS_IOFLAG_NOBLOCK) ? EWOULDBLOCK : EINTR;
+		tTime	timeout_z = 0, *timeout = (VFSFlags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
+		int rv = VFS_SelectNode(&chan->Node, VFS_SELECT_READ, timeout, "UDP_Channel_Read");
+		if( rv == 0 ) {
+			errno = (VFSFlags & VFS_IOFLAG_NOBLOCK) ? EWOULDBLOCK : EINTR;
+			return NULL;
 		}
 		SHORTLOCK(&chan->lQueue);
 		if(chan->Queue == NULL) {
 			SHORTREL(&chan->lQueue);
 			continue;
 		}
-		pack = chan->Queue;
+		tUDPPacket *pack = chan->Queue;
 		chan->Queue = pack->Next;
 		if(!chan->Queue) {
 			chan->QueueEnd = NULL;
-			VFS_MarkAvaliable(Node, 0);	// Nothing left
+			VFS_MarkAvaliable(&chan->Node, 0);	// Nothing left
 		}
 		SHORTREL(&chan->lQueue);
-		break;
+		return pack;
+	}
+	// Unreachable
+}
+
+/**
+ * \brief Read from the channel file (wait for a packet)
+ */
+size_t UDP_Channel_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
+{
+	tUDPChannel	*chan = Node->ImplPtr;
+	
+	if(chan->LocalPort == 0) {
+		Log_Notice("UDP", "Channel %p sent with no local port", chan);
+		return 0;
+	}
+	
+	tUDPPacket	*pack = UDP_Channel_WaitForPacket(chan, Flags);
+	if( !pack ) {
+		return 0;
 	}
 
+	size_t addrlen = IPStack_GetAddressSize(pack->Remote.AddrType);
+	tUDPEndpoint *ep = Buffer;
+	size_t ofs = 4 + addrlen;
+	
 	// Check that the header fits
-	addrlen = IPStack_GetAddressSize(pack->Remote.AddrType);
-	ep = Buffer;
-	ofs = 4 + addrlen;
 	if(Length < ofs) {
 		free(pack);
 		Log_Notice("UDP", "Insuficient space for header in buffer (%i < %i)", (int)Length, ofs);
@@ -303,6 +320,8 @@ static const char *casIOCtls_Channel[] = {
 	"getset_remoteport",
 	"getset_remotemask",
 	"set_remoteaddr",
+	"sendto",
+	"recvfrom",
 	NULL
 	};
 /**
@@ -354,14 +373,21 @@ int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data)
 	
 	case 6:	// getset_remotemask (returns bool success)
 		if(!Data)	LEAVE_RET('i', chan->RemoteMask);
-		if(!CheckMem(Data, sizeof(int)))	LEAVE_RET('i', -1);
+		if(!CheckMem(Data, sizeof(int))) {
+			LOG("Data pointer invalid");
+			LEAVE_RET('i', -1);
+		}
 		if( !chan->Interface ) {
 			LOG("Can't set remote mask on NULL interface");
 			LEAVE_RET('i', -1);
 		}
-		if( *(int*)Data > IPStack_GetAddressSize(chan->Interface->Type) )
+		 int	mask = *(int*)Data;
+		 int	addr_bits = IPStack_GetAddressSize(chan->Interface->Type) * 8;
+		if( mask > addr_bits ) {
+			LOG("Mask too large (%i > max %i)", mask, addr_bits);
 			LEAVE_RET('i', -1);
-		chan->RemoteMask = *(int*)Data;
+		}
+		chan->RemoteMask = mask;
 		LEAVE('i', chan->RemoteMask);
 		return chan->RemoteMask;	
 
@@ -374,9 +400,73 @@ int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data)
 			LOG("Invalid pointer");
 			LEAVE_RET('i', -1);
 		}
+		LOG("Set remote addr %s", IPStack_PrintAddress(chan->Interface->Type, Data));
+		chan->Remote.AddrType = chan->Interface->Type;
 		memcpy(&chan->Remote.Addr, Data, IPStack_GetAddressSize(chan->Interface->Type));
 		LEAVE('i', 0);
 		return 0;
+	case 8: {	// sendto
+		if(!CheckMem(Data, 2*sizeof(void*)+2)) {
+			LOG("Data pointer invalid");
+			LEAVE_RET('i', -1);
+		}
+		const struct sSendToArgs {
+			const tUDPEndpoint* ep;
+			const void* buf;
+			const Uint16 buflen;
+		} info = *(const struct sSendToArgs*)Data;
+		LOG("sendto(buf=%p + %u, ep=%p)", info.buf, info.buflen, info.ep);
+		if(!CheckMem(info.ep, 2+2) || !CheckMem(info.ep, 2+2+IPStack_GetAddressSize(info.ep->AddrType)) ) {
+			LEAVE_RET('i', -1);
+		}
+		if(!CheckMem(info.buf, info.buflen)) {
+			LEAVE_RET('i', -1);
+		}
+		
+		UDP_SendPacketTo(chan, info.ep->AddrType, &info.ep->Addr, info.ep->Port,
+			info.buf, (size_t)info.buflen);
+		
+		LEAVE_RET('i', info.buflen); }
+	case 9: {	// recvfrom
+		if(!CheckMem(Data, 2*sizeof(void*)+2)) {
+			LOG("Data pointer invalid");
+			LEAVE_RET('i', -1);
+		}
+		const struct sRecvFromArgs {
+			tUDPEndpoint* ep;
+			void* buf;
+			Uint16 buflen;
+		} info = *(const struct sRecvFromArgs*)Data;
+		LOG("recvfrom(buf=%p + %u, ep=%p)", info.buf, info.buflen, info.ep);
+		if(!CheckMem(info.ep, 2+2)) {
+			LEAVE_RET('i', -1);
+		}
+		if(!CheckMem(info.buf, info.buflen)) {
+			LEAVE_RET('i', -1);
+		}
+		
+		tUDPPacket	*pack = UDP_Channel_WaitForPacket(chan, 0);
+		if( pack == NULL ) {
+			LOG("No packet");
+			LEAVE_RET('i', 0);
+		}
+		
+		size_t	addrsize = IPStack_GetAddressSize(pack->Remote.AddrType);
+		if( !CheckMem(info.ep, 2+2+addrsize) ) {
+			LOG("Insufficient space for source address");
+			free(pack);
+			LEAVE_RET('i', -1);
+		}
+		info.ep->Port = pack->Remote.Port;
+		info.ep->AddrType = pack->Remote.AddrType;
+		memcpy(&info.ep->Addr, &pack->Remote.Addr, addrsize);
+		
+		size_t	retlen = (info.buflen < pack->Length ? info.buflen : pack->Length);
+		memcpy(info.buf, pack->Data, retlen);
+
+		free(pack);
+	
+		LEAVE_RET('i', retlen); }
 	}
 	LEAVE_RET('i', 0);
 }
diff --git a/Usermode/Libraries/Makefile.tpl b/Usermode/Libraries/Makefile.tpl
index 8f548bfa99adc20171b738395a9a4fb5e8548e36..3b8d2f55aca31fab14e0edfd2d57e3820ed6daa0 100644
--- a/Usermode/Libraries/Makefile.tpl
+++ b/Usermode/Libraries/Makefile.tpl
@@ -60,17 +60,15 @@ _libs: $(HEADERS)
 
 .PHONY: utest utest-build utest-run $(UTESTS:%=runtest-%)
 
-utest: utest-build generate_exp utest-run
-
-generate_exp: $(UTESTS:%=EXP_%.txt)
+utest: utest-build utest-run
 
 utest-build: $(UTESTS:%=TEST_%)
 
 utest-run: $(UTESTS:%=runtest-%)
 
-$(UTESTS:%=runtest-%): runtest-%: TEST_% EXP_%.txt
+$(UTESTS:%=runtest-%): runtest-%: TEST_%
 	@echo --- [TEST] $*
-	@./TEST_$* | diff EXP_$*.txt -
+	@./TEST_$*
 
 clean:
 	$(RM) $(_BIN) $(_XBIN) $(OBJ) $(_BIN).dsm $(DEPFILES) $(EXTRACLEAN)
diff --git a/Usermode/Libraries/libnet.so_src/Makefile b/Usermode/Libraries/libnet.so_src/Makefile
index 00bb8783a616e65847948cf9f1886b8b23795fd0..10d5b9620d45e8c01fc8fb6b77b5bc4f173a1640 100644
--- a/Usermode/Libraries/libnet.so_src/Makefile
+++ b/Usermode/Libraries/libnet.so_src/Makefile
@@ -10,4 +10,6 @@ OBJ = main.o address.o socket.o
 OBJ += hostnames.o dns.o
 BIN = libnet.so
 
+UTESTS = dns
+
 include ../Makefile.tpl
diff --git a/Usermode/Libraries/libnet.so_src/TEST_dns.c b/Usermode/Libraries/libnet.so_src/TEST_dns.c
new file mode 100644
index 0000000000000000000000000000000000000000..26b36bf50481273e33c8fca0febdd67c0f92d572
--- /dev/null
+++ b/Usermode/Libraries/libnet.so_src/TEST_dns.c
@@ -0,0 +1,52 @@
+/*
+ */
+#include "include/dns.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+extern int DNS_int_ParseResponse(const void* packet, size_t return_len, void *info, handle_record_t* handle_record_t);
+
+// Complex response from "Q ssh.ucc.asn.au A IN"
+const uint8_t test_packet_1[] = {
+	0xac, 0x00, 0x80, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x03, 0x73, 0x73, 0x68, 0x03, 0x75, 0x63, 0x63, 0x03, 0x61, 0x73, 0x6e, 0x02, 0x61, 0x75, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x77, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x78, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x79, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x61, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x7a, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x62, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x75, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x76, 0xc0, 0x18, 0xc0, 0x2c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x25, 0xd1, 0xc0, 0x05, 0xc0, 0x3c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x25, 0xd1, 0xc2, 0x05, 0xc0, 0x4c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x25, 0xd1, 0xc4, 0x05, 0xc0, 0x5c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x3a, 0x41, 0xfe, 0x49, 0xc0, 0x6c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x25, 0xd1, 0xc6, 0x05, 0xc0, 0x7c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x3a, 0x41, 0xfd, 0x49, 0xc0, 0x8c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0xd3, 0x1d, 0x85, 0x20, 0xc0, 0x9c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0xca, 0x0c, 0x1f, 0x8d, 
+	};
+
+#define TEST_REL_INT(exp, rel, have) do { \
+	int a = (exp);\
+	int b = (have);\
+	if( !(a rel b) ) { \
+		fprintf(stderr, "TEST_REL_INT("#exp" "#rel" "#exp") FAILED l=%i r=%i", \
+			a, b); \
+		return 1; \
+	} \
+} while(0)
+	
+int test_response_parse_1_cb(void *info, const char *name, enum eTypes type, enum eClass class, unsigned int ttl, size_t rdlength, const void *rdata)
+{
+	int* stagep = info;
+	switch( *stagep )
+	{
+	case 0:
+		TEST_REL_INT(0, ==, strcmp(name, "au."));
+		break;
+	}
+	(*stagep) += 1;
+	return 0;
+}
+int test_response_parse_1(void)
+{
+	int stage = 0;
+	TEST_REL_INT(0, ==, DNS_int_ParseResponse(test_packet_1, sizeof(test_packet_1), &stage, test_response_parse_1_cb) );
+	return 0;
+}
+
+int main(void)
+{
+	 int rv = 0;
+	// - Name Encode
+	// - Name Decode
+	// - Response parsing
+	rv |= test_response_parse_1();
+	return rv;
+}
diff --git a/Usermode/Libraries/libnet.so_src/dns.c b/Usermode/Libraries/libnet.so_src/dns.c
index 001ec0006162a7f76ce5c987525ab95266b5557a..518d4d47b4b6a425f6cc5f1a4dd2dacc2779a954 100644
--- a/Usermode/Libraries/libnet.so_src/dns.c
+++ b/Usermode/Libraries/libnet.so_src/dns.c
@@ -9,14 +9,20 @@
 #include <stdint.h>	// uint*_t
 #include <string.h>	// memcpy, strchr
 #include <assert.h>
+#include <acess/sys.h>	// for _SysSelect
+#include <acess/fd_set.h>	// FD_SET
 #include <net.h>
 #include "include/dns.h"
 
 // === PROTOTYPES ===
+//int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eTypes type, enum eClass class, handle_record_t* handle_record, void *info);
+int DNS_int_ParseResponse(const void* packet, size_t return_len, void *info, handle_record_t* handle_record_t);
 size_t	DNS_EncodeName(void *buf, const char *dotted_name);
-int DNS_DecodeName(char dotted_name[256], const void *buf, size_t space);
-int DNS_int_ParseRR(const void *buf, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p);
+int DNS_DecodeName(char dotted_name[256], const void *buf, size_t ofs, size_t space);
+int DNS_int_ParseRR(const void *buf, size_t ofs, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p);
+
 static uint16_t	get16(const void *buf);
+static uint32_t	get32(const void *buf);
 static size_t put16(void *buf, uint16_t val);
 
 
@@ -49,22 +55,42 @@ int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eT
 	int sock = Net_OpenSocket_UDP(ServerAType, ServerAddr, 53, 0);
 	if( sock < 0 ) {
 		// Connection failed
+		_SysDebug("DNS_Query - UDP open failed");
 		// TODO: Correctly report this failure with a useful error code
 		return 1;
 	}
-	int rv = _SysWrite(sock, packet, pos);
+	int rv = Net_UDP_SendTo(sock, 53, ServerAType, ServerAddr, pos, packet);
 	if( rv != pos ) {
+		_SysDebug("DNS_Query - Write failed");
 		// TODO: Error reporting
 		_SysClose(sock);
 		return 1;
 	}
 	// - Wait
-	int return_len = 0;
-	do {
-		return_len = _SysRead(sock, packet, sizeof(packet));
-	} while( return_len == 0 );
-	if( return_len < 0 ) {
+	{
+		 int	nfd = sock + 1;
+		fd_set	fds;
+		FD_ZERO(&fds);
+		FD_SET(sock, &fds);
+		int64_t	timeout = 2000;	// Give it two seconds, should be long enough
+		rv = _SysSelect(nfd, &fds, NULL, NULL, &timeout, 0);
+		if( rv == 0 ) {
+			// Timeout with no reply, give up
+			_SysDebug("DNS_Query - Timeout");
+			_SysClose(sock);
+			return 1;
+		}
+		if( rv < 0 ) {
+			// Oops, select failed
+			_SysDebug("DNS_Query - Select failure");
+			_SysClose(sock);
+			return 1;
+		}
+	}
+	int return_len = Net_UDP_RecvFrom(sock, NULL, NULL, NULL, sizeof(packet), packet);
+	if( return_len <= 0 ) {
 		// TODO: Error reporting
+		_SysDebug("DNS_Query - Read failure");
 		_SysClose(sock);
 		return 1;
 	}
@@ -73,17 +99,33 @@ int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eT
 	//  > TODO: Lock DNS queries
 	
 	// For each response in the answer (and additional) sections, call the passed callback
+	return DNS_int_ParseResponse(packet, return_len, info, handle_record);
+}
+
+int DNS_int_ParseResponse(const void* buf, size_t return_len, void *info, handle_record_t* handle_record)
+{
+	const uint8_t* packet = buf;
 	char	rr_name[256];
+	unsigned int id = get16(packet + 0);
+	if( id != 0xAC00 ) {
+		_SysDebug("DNS_Query - Packet ID mismatch");
+		return 2;
+	}
+	unsigned int flags = get16(packet + 2);
 	unsigned int qd_count = get16(packet + 4);
 	unsigned int an_count = get16(packet + 6);
 	unsigned int ns_count = get16(packet + 8);
 	unsigned int ar_count = get16(packet + 10);
-	pos = 6*2;
+	size_t pos = 6*2;
 	// TODO: Can I safely assert / fail if qd_count is non-zero?
 	// - Questions, ignored
 	for( unsigned int i = 0; i < qd_count; i ++ ) {
-		pos += DNS_DecodeName(NULL, packet + pos, return_len - pos);
-		pos += 2*2;
+		int rv = DNS_DecodeName(rr_name, packet, pos, return_len);
+		if( rv < 0 ) {
+			_SysDebug("DNS_Query - Parse error in QD");
+			return 1;
+		}
+		pos += rv + 2*2;
 	}
 	// - Answers, pass on to handler
 	for( unsigned int i = 0; i < an_count; i ++ )
@@ -92,20 +134,22 @@ int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eT
 		enum eClass	class;
 		uint32_t	ttl;
 		size_t	rdlength;
-		int rv = DNS_int_ParseRR(packet + pos, return_len - pos, rr_name, &type, &class, &ttl, &rdlength);
+		int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, &type, &class, &ttl, &rdlength);
 		if( rv < 0 ) {
+			_SysDebug("DNS_Query - Parse error in AN");
 			return 1;
 		}
 		pos += rv;
 		
-		handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos);
+		handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos - rdlength);
 	}
 	// Authority Records (should all be NS records)
 	for( unsigned int i = 0; i < ns_count; i ++ )
 	{
 		size_t	rdlength;
-		int rv = DNS_int_ParseRR(packet + pos, return_len - pos, rr_name, NULL, NULL, NULL, &rdlength);
+		int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, NULL, NULL, NULL, &rdlength);
 		if( rv < 0 ) {
+			_SysDebug("DNS_Query - Parse error in NS");
 			return 1;
 		}
 		pos += rv;
@@ -117,15 +161,16 @@ int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eT
 		enum eClass	class;
 		uint32_t	ttl;
 		size_t	rdlength;
-		int rv = DNS_int_ParseRR(packet + pos, return_len - pos, rr_name, &type, &class, &ttl, &rdlength);
+		int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, &type, &class, &ttl, &rdlength);
 		if( rv < 0 ) {
+			_SysDebug("DNS_Query - Parse error in AR");
 			return 1;
 		}
 		pos += rv;
 		
-		handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos);
+		handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos - rdlength);
 	}
-
+	
 	return 0;
 }
 
@@ -170,76 +215,124 @@ size_t	DNS_EncodeName(void *buf, const char *dotted_name)
 }
 
 // Decode a name (including trailing . for root)
-int DNS_DecodeName(char dotted_name[256], const void *buf, size_t space)
+int DNS_DecodeName(char dotted_name[256], const void *buf, size_t ofs, size_t space)
 {
 	int consumed = 0;
 	int out_pos = 0;
-	const uint8_t *buf8 = buf;
-	while( *buf8 && space > 0 )
+	const uint8_t *buf8 = (const uint8_t*)buf + ofs;
+	for( ;; )
 	{
-		if( consumed + 1 > space )	return -1;
+		if( ofs + consumed + 1 > space ) {
+			_SysDebug("DNS_DecodeName - Len byte OOR space=%i", space);
+			return -1;
+		}
 		uint8_t	seg_len = *buf8;
 		buf8 ++;
 		consumed ++;
-		// Protocol violation (overflowed end of buffer)
-		if( consumed + seg_len > space )
-			return -1;
+		// Done
+		if( seg_len == 0 )
+			break;
+		if( (seg_len & 0xC0) == 0xC0 )
+		{
+			// Backreference, the rest of the name is a backref
+			char tmp[256];
+			int ref_ofs = get16(buf8 - 1) & 0x3FFF;
+			consumed += 1, buf8 += 1;	// Only one, previous inc still applies
+			_SysDebug("DNS_DecodeName - Nested at %i", ref_ofs);
+			if( DNS_DecodeName(tmp, buf, ref_ofs, space) < 0 )
+				return -1;
+			memcpy(dotted_name+out_pos, tmp, strlen(tmp));
+			out_pos += strlen(tmp);
+			break;
+		}
 		// Protocol violation (segment too long)
-		if( seg_len >= 64 )
+		if( seg_len >= 64 ) {
+			_SysDebug("DNS_DecodeName - Seg too long %i", seg_len);
 			return -1;
+		}
+		// Protocol violation (overflowed end of buffer)
+		if( ofs + consumed + seg_len > space ) {
+			_SysDebug("DNS_DecodeName - Seg OOR %i+%i>%i", consumed, seg_len, space);
+			return -1;
+		}
 		// Protocol violation (name was too long)
-		if( out_pos + seg_len + 1 > sizeof(dotted_name)-1 )
+		if( out_pos + seg_len + 1 > 255 ) {
+			_SysDebug("DNS_DecodeName - Dotted name too long %i+%i+1 > %i",
+				out_pos, seg_len, 255);
 			return -1;
+		}
+		
+		_SysDebug("DNS_DecodeName : Seg %i '%.*s'", seg_len, seg_len, buf8);
 		
 		// Read segment
 		memcpy(dotted_name + out_pos, buf8, seg_len);
 		buf8 += seg_len;
 		consumed += seg_len;
+		out_pos += seg_len;
 		
 		// Place '.'
-		dotted_name[out_pos+seg_len+1] = '.';
-		// Increment output counter
-		out_pos += seg_len + 1;
+		dotted_name[out_pos] = '.';
+		out_pos ++;
 	}
-	
 	dotted_name[out_pos] = '\0';
+	_SysDebug("DNS_DecodeName - '%s', consumed = %i", dotted_name, consumed);
 	return consumed;
 }
 
 // Parse a Resource Record
-int DNS_int_ParseRR(const void *buf, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p)
+int DNS_int_ParseRR(const void *buf, size_t ofs, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p)
 {
 	const uint8_t	*buf8 = buf;
 	size_t	consumed = 0;
 	
 	// 1. Name
-	int rv = DNS_DecodeName(name_p, buf8, space);
+	int rv = DNS_DecodeName(name_p, buf, ofs, space);
 	if(rv < 0)	return -1;
 	
-	buf8 += rv, consumed += rv;
+	ofs += rv, consumed += rv;
 	
 	if( type_p )
-		*type_p = get16(buf8);
-	buf8 += 2, consumed += 2;
+		*type_p = get16(buf8 + ofs);
+	ofs += 2, consumed += 2;
 	
 	if( class_p )
-		*class_p = get16(buf8);
-	buf8 += 2, consumed += 2;
+		*class_p = get16(buf8 + ofs);
+	ofs += 2, consumed += 2;
 	
-	return consumed;
+	if( ttl_p )
+		*ttl_p = get32(buf + ofs);
+	ofs += 4, consumed += 4;
+	
+	size_t rdlength = get16(buf + ofs);
+	if( rdlength_p )
+		*rdlength_p = rdlength;
+	ofs += 2, consumed += 2;
+	
+	_SysDebug("DNS_int_ParseRR - name='%s', rdlength=%i", name_p, rdlength);
+	
+	return consumed + rdlength;
 }
 
 static uint16_t get16(const void *buf) {
 	const uint8_t* buf8 = buf;
 	uint16_t rv = 0;
-	rv |= buf8[0];
-	rv |= (uint16_t)buf8[1] << 8;
+	rv |= (uint16_t)buf8[0] << 8;
+	rv |= (uint16_t)buf8[1] << 0;
+	return rv;
+}
+static uint32_t get32(const void *buf) {
+	const uint8_t* buf8 = buf;
+	uint32_t rv = 0;
+	rv |= (uint32_t)buf8[0] << 24;
+	rv |= (uint32_t)buf8[1] << 16;
+	rv |= (uint32_t)buf8[2] << 8;
+	rv |= (uint32_t)buf8[3] << 0;
 	return rv;
 }
 static size_t put16(void *buf, uint16_t val) {
 	uint8_t* buf8 = buf;
-	buf8[0] = val & 0xFF;
-	buf8[1] = val >> 8;
+	buf8[0] = val >> 8;
+	buf8[1] = val & 0xFF;
 	return 2;
 }
 
diff --git a/Usermode/Libraries/libnet.so_src/hostnames.c b/Usermode/Libraries/libnet.so_src/hostnames.c
index 88093de37d1a3eb3a398564a22c1be784608c392..c3c962681164ac02dbc769c9718792cc107a6424 100644
--- a/Usermode/Libraries/libnet.so_src/hostnames.c
+++ b/Usermode/Libraries/libnet.so_src/hostnames.c
@@ -8,7 +8,9 @@
 #include <net.h>
 #include "include/dns.h"
 #include <string.h>
+#include <stdlib.h>	// malloc (for loading config)
 #include <stdbool.h>
+#include <acess/sys.h>	// _SysDebug
 
 // === TYPES ===
 struct sDNSServer
@@ -74,9 +76,16 @@ int int_lookupany_callback(void *info_v, int AddrType, const void *Addr)
 
 int Net_Lookup_Addrs(const char *Name, void *cb_info, tNet_LookupAddrs_Callback *callback)
 {
+	_SysDebug("Net_Lookup_Addrs(Name='%s')", Name);
 	// 1. Load (if not loaded) the DNS config from "/Acess/Conf/dns"
 	// - "* <ip> <ip>" for DNS server(s)
 	// - "127.0.0.1 localhost localhost.localdomain"
+	if( !gaDNSServers )
+	{
+		giNumDNSServers = 1;
+		gaDNSServers = malloc( 1 * sizeof(gaDNSServers[0]) );
+		gaDNSServers[0].AddrType = Net_ParseAddress("192.168.1.1", gaDNSServers[0].AddrData);
+	}
 	
 	// 2. Check the hosts list
 	for( int i = 0; i < giNumHostEntries; i ++ )
@@ -121,6 +130,7 @@ int Net_Lookup_Addrs(const char *Name, void *cb_info, tNet_LookupAddrs_Callback
 void int_DNS_callback(void *info_v, const char *name, enum eTypes type, enum eClass class, unsigned int ttl, size_t rdlength, const void *rdata)
 {
 	struct sDNSCallbackInfo	*info = info_v;
+	_SysDebug("int_DNS_callback(name='%s', type=%i, class=%i)", name, type, class);
 	
 	// Check type matches (if pattern was provided)
 	if( info->desired_type != QTYPE_STAR && type != info->desired_type )
diff --git a/Usermode/Libraries/libnet.so_src/include/dns.h b/Usermode/Libraries/libnet.so_src/include/dns.h
index 62204f0513d2399b6dd8ae5e0f738dc6cab79f4c..dc7a58b4f220440e9c6d5bb355240f9a944a2327 100644
--- a/Usermode/Libraries/libnet.so_src/include/dns.h
+++ b/Usermode/Libraries/libnet.so_src/include/dns.h
@@ -26,8 +26,8 @@ enum eTypes
 
 enum eClass
 {
-	CLASS_IN,
-	CLASS_CH,	// "Chaos"
+	CLASS_IN = 1,
+	CLASS_CH = 3,	// "Chaos"
 	QCLASS_STAR = 255,
 };
 
diff --git a/Usermode/Libraries/libnet.so_src/include_exp/net.h b/Usermode/Libraries/libnet.so_src/include_exp/net.h
index cd2cb6c8e646a4fe899c9b806bef22481bef8e8c..3d33da0337c7a700ed2b1d470bae068145309b48 100644
--- a/Usermode/Libraries/libnet.so_src/include_exp/net.h
+++ b/Usermode/Libraries/libnet.so_src/include_exp/net.h
@@ -6,6 +6,8 @@
 #ifndef __LIBNET_H_
 #define __LIBNET_H_
 
+#include <stddef.h>
+
 enum {
 	NET_ADDRTYPE_NULL = 0,
 	NET_ADDRTYPE_IPV4 = 4,
@@ -58,6 +60,8 @@ extern int	Net_OpenSocket(int AddrType, const void *Addr, const char *SocketName
 extern int	Net_OpenSocket_TCPC(int AddrType, const void *Addr, int Port);
 
 extern int	Net_OpenSocket_UDP(int AddrType, const void *Addr, int RAddr, int LAddr);
+extern int Net_UDP_SendTo  (int FD, int Port, int AddrType, const void *Addr, size_t Length, const void *Data);
+extern int Net_UDP_RecvFrom(int FD, int* Port, int* AddrType, void *Addr, size_t Length, void *Data);
 
 
 /**
diff --git a/Usermode/Libraries/libnet.so_src/socket.c b/Usermode/Libraries/libnet.so_src/socket.c
index 68335acc34828e13375334f8f57b2f779979fab3..825dc6fe274a2011bd56f61d01748205799db0d5 100644
--- a/Usermode/Libraries/libnet.so_src/socket.c
+++ b/Usermode/Libraries/libnet.so_src/socket.c
@@ -8,6 +8,7 @@
 #include <net.h>
 #include <stdio.h>
 #include <stdint.h>
+#include <string.h>	// memcpy
 #include <acess/sys.h>
 
 enum {
@@ -15,6 +16,8 @@ enum {
 	UDP_IOCTL_GETSETRPORT,
 	UDP_IOCTL_GETSETRMASK,
 	UDP_IOCTL_SETRADDR,
+	UDP_IOCTL_SENDTO,
+	UDP_IOCTL_RECVFROM,
 };
 
 int Net_OpenSocket(int AddrType, const void *Addr, const char *Filename)
@@ -50,10 +53,16 @@ int Net_OpenSocket_TCPC(int AddrType, const void *Addr, int Port)
 	int fd = Net_OpenSocket(AddrType, Addr, "tcpc");
 	if( fd == -1 )	return -1;
 	
-	_SysIOCtl(fd, 5, &Port);	// Remote Port
-        _SysIOCtl(fd, 6, (void*)Addr);	// Remote address (kernel shouldn't modify)
-	_SysIOCtl(fd, 7, NULL);	// connect
+	if( _SysIOCtl(fd, 5, &Port) < 0 )	// Remote Port
+		goto err;
+	if( _SysIOCtl(fd, 6, (void*)Addr) < 0 )	// Remote address (kernel shouldn't modify)
+		goto err;
+	if( _SysIOCtl(fd, 7, NULL) < 0)	// connect
+		goto err;
 	return fd;
+err:
+	_SysClose(fd);
+	return -1;
 }
 
 int Net_OpenSocket_UDP(int AddrType, const void *Addr, int RPort, int LPort)
@@ -61,11 +70,60 @@ int Net_OpenSocket_UDP(int AddrType, const void *Addr, int RPort, int LPort)
 	int fd = Net_OpenSocket(AddrType, Addr, "udp");
 	if( fd == -1 )	return -1;
 	
-	_SysIOCtl(fd, UDP_IOCTL_GETSETLPORT, &LPort);	// Remote Port
+	if( _SysIOCtl(fd, UDP_IOCTL_GETSETLPORT, &LPort) < 0 )
+		goto err;
+	if( _SysIOCtl(fd, UDP_IOCTL_GETSETRPORT, &RPort) < 0 )
+		goto err;
 	int maskbits = Net_GetAddressSize(AddrType) * 8;
-	_SysIOCtl(fd, UDP_IOCTL_GETSETRPORT, &RPort);
-	_SysIOCtl(fd, UDP_IOCTL_GETSETRMASK, &maskbits);
-        _SysIOCtl(fd, UDP_IOCTL_SETRADDR, (void*)Addr);	// Remote address (kernel shouldn't modify)
+	if( _SysIOCtl(fd, UDP_IOCTL_GETSETRMASK, &maskbits) < 0 )
+		goto err;
+	if( _SysIOCtl(fd, UDP_IOCTL_SETRADDR, (void*)Addr) < 0 )	// Remote address (kernel shouldn't modify)
+		goto err;
 	return fd;
+err:
+	_SysClose(fd);
+	return -1;
+}
+
+int Net_UDP_SendTo(int FD, int Port, int AddrType, const void *Addr, size_t Length, const void *Data)
+{
+	struct {
+		uint16_t port;
+		uint16_t addr_type;
+		char	addr[16];
+	} ep;
+	ep.port = Port;
+	ep.addr_type = AddrType;
+	memcpy(ep.addr, Addr, Net_GetAddressSize(AddrType));
+	struct {
+		const void *ep;
+		const void *buf;
+		uint16_t len;
+	} info = { .ep = &ep, .buf = Data, .len = Length };
+	
+	return _SysIOCtl(FD, UDP_IOCTL_SENDTO, &info);
+}
+
+int Net_UDP_RecvFrom(int FD, int* Port, int* AddrType, void *Addr, size_t Length, void *Data)
+{
+	struct {
+		uint16_t port;
+		uint16_t addr_type;
+		char	addr[16];
+	} ep;
+	struct {
+		void *ep;
+		void *buf;
+		uint16_t len;
+	} info = { .ep = &ep, .buf = Data, .len = Length };
+	
+	int rv = _SysIOCtl(FD, UDP_IOCTL_RECVFROM, &info);
+	if( rv > 0 )
+	{
+		if(Port)	*Port = ep.port;
+		if(AddrType)	*AddrType = ep.addr_type;
+		if(Addr)	memcpy(Addr, ep.addr, Net_GetAddressSize(ep.addr_type));
+	}
+	return rv;
 }