diff --git a/Usermode/Libraries/libnet.so_src/Makefile b/Usermode/Libraries/libnet.so_src/Makefile
index 29abaaa0e95d8b5223426b8d364fb89b4f6bd7ee..8bbf403524215b7e8b3d8f14414317c2c72a5124 100644
--- a/Usermode/Libraries/libnet.so_src/Makefile
+++ b/Usermode/Libraries/libnet.so_src/Makefile
@@ -6,7 +6,7 @@ CPPFLAGS +=
 CFLAGS   += -Wall
 LDFLAGS  += -lc -soname libnet.so
 
-OBJ = main.o address.o socket.o
+OBJ = main.o address.o socket.o dns.o
 BIN = libnet.so
 
 include ../Makefile.tpl
diff --git a/Usermode/Libraries/libnet.so_src/address.c b/Usermode/Libraries/libnet.so_src/address.c
index 8887362d69a52a765023e5228e5162d019713642..d103e64b04fee12d7951049fee38b6215e6a484e 100644
--- a/Usermode/Libraries/libnet.so_src/address.c
+++ b/Usermode/Libraries/libnet.so_src/address.c
@@ -7,7 +7,7 @@
  */
 #include <net.h>
 #include <stdint.h>
-//#include <stdio.h>
+#include <stdio.h>	// sprintf
 #include <stdlib.h>
 #define DEBUG	0
 
diff --git a/Usermode/Libraries/libnet.so_src/dns.c b/Usermode/Libraries/libnet.so_src/dns.c
new file mode 100644
index 0000000000000000000000000000000000000000..43213a485faaae52f1103d46307db8fea7d5c12e
--- /dev/null
+++ b/Usermode/Libraries/libnet.so_src/dns.c
@@ -0,0 +1,245 @@
+/*
+ * Acess2 Networking Toolkit
+ * By John Hodge (thePowersGang)
+ * 
+ * dns.c
+ * - Hostname<->Address resolution
+ */
+#include <stddef.h>	// size_t / NULL
+#include <stdint.h>	// uint*_t
+#include <string.h>	// memcpy, strchrnul
+#include <assert.h>
+#include <net.h>
+#include "include/dns.h"
+
+// === PROTOTYPES ===
+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);
+static uint16_t	get16(const void *buf);
+static size_t put16(void *buf, uint16_t val);
+
+
+// === CODE ===
+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 namelen = DNS_EncodeName(NULL, name);
+	assert(namelen < 256);
+	size_t	pos = 0;
+	char	packet[ 512 ];
+	assert( (6*2) + (namelen + 2*2) < 512 );
+	// - Header
+	pos += put16(packet + pos, 0xAC00);	// Identifier (arbitary)
+	pos += put16(packet + pos, (0 << 0) | (0 << 1) );	// Op : Query, Standard, no other flags
+	pos += put16(packet + pos, 1);	// QDCount
+	pos += put16(packet + pos, 0);	// ANCount
+	pos += put16(packet + pos, 0);	// NSCount
+	pos += put16(packet + pos, 0);	// ARCount
+	// - Question
+	pos += DNS_EncodeName(packet + pos, name);
+	pos += put16(packet + pos, type);	// QType
+	pos += put16(packet + pos, class);	// QClass
+	
+	assert(pos <= sizeof(packet));
+	
+	// Send and wait for reply
+	// - Lock
+	//  > TODO: Lock DNS queries
+	// - Send
+	int sock = Net_OpenSocket_UDP(ServerAType, ServerAddr, 53, 0);
+	if( sock < 0 ) {
+		// Connection failed
+		// TODO: Correctly report this failure with a useful error code
+		return 1;
+	}
+	int rv = _SysWrite(sock, packet, pos);
+	if( rv != pos ) {
+		// 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 ) {
+		// TODO: Error reporting
+		_SysClose(sock);
+		return 1;
+	}
+	_SysClose(sock);
+	// - Release
+	//  > TODO: Lock DNS queries
+	
+	// For each response in the answer (and additional) sections, call the passed callback
+	char	rr_name[256];
+	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;
+	// 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;
+	}
+	// - Answers, pass on to handler
+	for( unsigned int i = 0; i < an_count; i ++ )
+	{
+		enum eTypes	type;
+		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);
+		if( rv < 0 ) {
+			return 1;
+		}
+		pos += rv;
+		
+		handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos);
+	}
+	// 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);
+		if( rv < 0 ) {
+			return 1;
+		}
+		pos += rv;
+	}
+	// - Additional records, pass to handler
+	for( unsigned int i = 0; i < ar_count; i ++ )
+	{
+		enum eTypes	type;
+		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);
+		if( rv < 0 ) {
+			return 1;
+		}
+		pos += rv;
+		
+		handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos);
+	}
+
+	return 0;
+}
+
+/// Encode a dotted name as a DNS name
+size_t	DNS_EncodeName(void *buf, const char *dotted_name)
+{
+	size_t	ret = 0;
+	const char *str = dotted_name;
+	uint8_t	*buf8 = buf;
+	while( *str )
+	{
+		const char *next = strchr(str, '.');
+		size_t seg_len = (next ? next - str : strlen(str));
+		if( seg_len > 63 ) {
+			// Oops, too long (truncate)
+			seg_len = 63;
+		}
+		if( seg_len == 0 && next != NULL ) {
+			// '..' encountered, invalid (skip)
+			str = next+1;
+			continue ;
+		}
+		
+		if( buf8 )
+		{
+			buf8[ret] = seg_len;
+			memcpy(buf8+ret+1, str, seg_len);
+		}
+		ret += 1 + seg_len;
+		
+		if( next == NULL ) {
+			// No trailing '.', assume it's there? Yes, need to be NUL terminated
+			if(buf8)	buf8[ret] = 0;
+			ret ++;
+			break;
+		}
+		else {
+			str = next + 1;
+		}
+	}
+	return ret;
+}
+
+// Decode a name (including trailing . for root)
+int DNS_DecodeName(char dotted_name[256], const void *buf, size_t space)
+{
+	int consumed = 0;
+	int out_pos = 0;
+	const uint8_t *buf8 = buf;
+	while( *buf8 && space > 0 )
+	{
+		if( consumed + 1 > space )	return -1;
+		uint8_t	seg_len = *buf8;
+		buf8 ++;
+		consumed ++;
+		// Protocol violation (overflowed end of buffer)
+		if( consumed + seg_len > space )
+			return -1;
+		// Protocol violation (segment too long)
+		if( seg_len >= 64 )
+			return -1;
+		// Protocol violation (name was too long)
+		if( out_pos + seg_len + 1 > sizeof(dotted_name)-1 )
+			return -1;
+		
+		// Read segment
+		memcpy(dotted_name + out_pos, buf8, seg_len);
+		buf8 += seg_len;
+		consumed += seg_len;
+		
+		// Place '.'
+		dotted_name[out_pos+seg_len+1] = '.';
+		// Increment output counter
+		out_pos += seg_len + 1;
+	}
+	
+	dotted_name[out_pos] = '\0';
+	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)
+{
+	const uint8_t	*buf8 = buf;
+	size_t	consumed = 0;
+	
+	// 1. Name
+	int rv = DNS_DecodeName(name_p, buf8, space);
+	if(rv < 0)	return -1;
+	
+	buf8 += rv, consumed += rv;
+	
+	if( type_p )
+		*type_p = get16(buf8);
+	buf8 += 2, consumed += 2;
+	
+	if( class_p )
+		*class_p = get16(buf8);
+	buf8 += 2, consumed += 2;
+	
+	return consumed;
+}
+
+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;
+	return rv;
+}
+static size_t put16(void *buf, uint16_t val) {
+	uint8_t* buf8 = buf;
+	buf8[0] = val & 0xFF;
+	buf8[1] = val >> 8;
+	return 2;
+}
+
diff --git a/Usermode/Libraries/libnet.so_src/include/dns.h b/Usermode/Libraries/libnet.so_src/include/dns.h
new file mode 100644
index 0000000000000000000000000000000000000000..1f2dd61ddc7f47fb360cd839da8ccfbdc13584a1
--- /dev/null
+++ b/Usermode/Libraries/libnet.so_src/include/dns.h
@@ -0,0 +1,41 @@
+/*
+ */
+#ifndef _DNS_H_
+#define _DNS_H_
+
+enum eTypes
+{
+	TYPE_A = 1,
+	TYPE_NS = 2,
+	TYPE_CNAME = 5,
+	TYPE_SOA = 6,
+	TYPE_NULL = 10,
+	TYPE_PTR = 12,
+	TYPE_HINFO = 13,
+	TYPE_MX = 15,
+	TYPE_TXT = 16,
+	QTYPE_STAR = 255,
+};
+
+enum eClass
+{
+	CLASS_IN,
+	CLASS_CH,	// "Chaos"
+	CLASS_STAR = 255,
+};
+
+/**
+ * \brief Handler for a DNS record obtained by DNS_Query
+ * \param info	Value passed as the last argument to DNS_Query
+ * \param name	NUL-terminated name associated with the returned record
+ * \param type	Record type (may not be equal to requested)
+ * \param class	Record class (may not be equal to requested)
+ * \param rdlength	Length of data pointed to by 'rdata'
+ * \param rdata	Record data
+ */
+typedef void	handle_record_t(void *info, const char *name, enum eTypes type, enum eClass class, unsigned int ttl, size_t rdlength, const void *rdata);
+
+int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eTypes type, enum eClass class, handle_record_t* handle_record, void *info);
+
+#endif
+
diff --git a/Usermode/Libraries/libnet.so_src/include_exp/net.h b/Usermode/Libraries/libnet.so_src/include_exp/net.h
index 5fe4ba1058688e83365e6dc5df026a0108ce471d..f02c0a433ccecac117f39bad7a8e90194aa540ab 100644
--- a/Usermode/Libraries/libnet.so_src/include_exp/net.h
+++ b/Usermode/Libraries/libnet.so_src/include_exp/net.h
@@ -53,8 +53,31 @@ extern char	*Net_GetInterface(int AddrType, void *Addr);
  * Opens a file using /Devices/ip/routes/@<AddrType>:<Addr>/<SocketName>
  * 
  */
-extern int	Net_OpenSocket(int AddrType, void *Addr, const char *SocketName);
+extern int	Net_OpenSocket(int AddrType, const void *Addr, const char *SocketName);
 
-extern int	Net_OpenSocket_TCPC(int AddrType, void *Addr, int Port);
+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);
+
+
+/**
+ * \name Hostnames
+ * \brief Handling of hostname resolution
+ * \{
+ */
+
+/**
+ * \brief Returns an address for the specified hostname
+ * \note Picks randomly if multiple addresses are present
+ */
+extern int	Net_Lookup_AnyAddr(const char *Name, int AddrType, void *Addr);
+
+/**
+ */
+extern int	Net_Lookup_Name(int AddrType, const void *Addr, char *Dest[256]);
+
+/**
+ * \}
+ */
 
 #endif
diff --git a/Usermode/Libraries/libnet.so_src/socket.c b/Usermode/Libraries/libnet.so_src/socket.c
index 85f7e54f28944711b7f026a280625bf3ccd94db7..68335acc34828e13375334f8f57b2f779979fab3 100644
--- a/Usermode/Libraries/libnet.so_src/socket.c
+++ b/Usermode/Libraries/libnet.so_src/socket.c
@@ -10,15 +10,23 @@
 #include <stdint.h>
 #include <acess/sys.h>
 
-int Net_OpenSocket(int AddrType, void *Addr, const char *Filename)
+enum {
+	UDP_IOCTL_GETSETLPORT = 4,
+	UDP_IOCTL_GETSETRPORT,
+	UDP_IOCTL_GETSETRMASK,
+	UDP_IOCTL_SETRADDR,
+};
+
+int Net_OpenSocket(int AddrType, const void *Addr, const char *Filename)
 {
 	 int	addrLen = Net_GetAddressSize(AddrType);
-	 int	i;
-	uint8_t	*addrBuffer = Addr;
 	char	hexAddr[addrLen*2+1];
 	
-	for( i = 0; i < addrLen; i ++ )
-		sprintf(hexAddr+i*2, "%02x", addrBuffer[i]);
+	{
+		const uint8_t	*addrBuffer = Addr;
+		for( unsigned int i = 0; i < addrLen; i ++ )
+			sprintf(hexAddr+i*2, "%02x", addrBuffer[i]);
+	}
 	
 	if(Filename)
 	{
@@ -37,14 +45,27 @@ int Net_OpenSocket(int AddrType, void *Addr, const char *Filename)
 	}
 }
 
-int Net_OpenSocket_TCPC(int AddrType, void *Addr, int Port)
+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, Addr);	// Remote address
+        _SysIOCtl(fd, 6, (void*)Addr);	// Remote address (kernel shouldn't modify)
 	_SysIOCtl(fd, 7, NULL);	// connect
 	return fd;
 }
 
+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
+	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)
+	return fd;
+}
+