about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/base/system.c492
-rw-r--r--src/base/system.h13
-rw-r--r--src/engine/client/client.cpp28
-rw-r--r--src/engine/client/serverbrowser.cpp6
-rw-r--r--src/engine/server.h2
-rw-r--r--src/engine/server/server.cpp51
-rw-r--r--src/engine/server/server.h2
-rw-r--r--src/engine/serverbrowser.h2
-rw-r--r--src/engine/shared/network_client.cpp10
-rw-r--r--src/engine/shared/network_server.cpp2
-rw-r--r--src/game/server/gamecontext.cpp17
11 files changed, 477 insertions, 148 deletions
diff --git a/src/base/system.c b/src/base/system.c
index c0b8edec..d4c81cf4 100644
--- a/src/base/system.c
+++ b/src/base/system.c
@@ -25,6 +25,7 @@
 	#include <netinet/in.h>
 	#include <fcntl.h>
 	#include <pthread.h>
+	#include <arpa/inet.h>
 
 	#include <dirent.h>
 	
@@ -63,6 +64,8 @@ static int num_loggers = 0;
 static NETSTATS network_stats = {0};
 static MEMSTATS memory_stats = {0};
 
+static NETSOCKET invalid_socket = {NETTYPE_INVALID, -1, -1};
+
 void dbg_logger(DBG_LOGGER logger)
 {
 	loggers[num_loggers++] = logger;
@@ -509,27 +512,55 @@ int64 time_freq()
 }
 
 /* -----  network ----- */
-static void netaddr_to_sockaddr(const NETADDR *src, struct sockaddr *dest)
+static void netaddr_to_sockaddr_in(const NETADDR *src, struct sockaddr_in *dest)
 {
-	/* TODO: IPv6 support */
-	struct sockaddr_in *p = (struct sockaddr_in *)dest;
-	mem_zero(p, sizeof(struct sockaddr_in));
-	p->sin_family = AF_INET;
-	p->sin_port = htons(src->port);
-	p->sin_addr.s_addr = htonl(src->ip[0]<<24|src->ip[1]<<16|src->ip[2]<<8|src->ip[3]);
+	mem_zero(dest, sizeof(struct sockaddr_in));
+	if(src->type != NETTYPE_IPV4)
+	{
+		dbg_msg("system", "couldn't convert NETADDR of type %d to ipv4", src->type);
+		return;
+	}
+
+	dest->sin_family = AF_INET;
+	dest->sin_port = htons(src->port);
+	mem_copy(&dest->sin_addr.s_addr, src->ip, 4);
+}
+
+static void netaddr_to_sockaddr_in6(const NETADDR *src, struct sockaddr_in6 *dest)
+{
+	mem_zero(dest, sizeof(struct sockaddr_in6));
+	if(src->type != NETTYPE_IPV6)
+	{
+		dbg_msg("system", "couldn't not convert NETADDR of type %d to ipv6", src->type);
+		return;
+	}
+
+	dest->sin6_family = AF_INET6;
+	dest->sin6_port = htons(src->port);
+	mem_copy(&dest->sin6_addr.s6_addr, src->ip, 16);
 }
 
 static void sockaddr_to_netaddr(const struct sockaddr *src, NETADDR *dst)
 {
-	/* TODO: IPv6 support */
-	unsigned int ip = htonl(((struct sockaddr_in*)src)->sin_addr.s_addr);
-	mem_zero(dst, sizeof(NETADDR));
-	dst->type = NETTYPE_IPV4;
-	dst->port = htons(((struct sockaddr_in*)src)->sin_port);
-	dst->ip[0] = (unsigned char)((ip>>24)&0xFF);
-	dst->ip[1] = (unsigned char)((ip>>16)&0xFF);
-	dst->ip[2] = (unsigned char)((ip>>8)&0xFF);
-	dst->ip[3] = (unsigned char)(ip&0xFF);
+	if(src->sa_family == AF_INET)
+	{
+		mem_zero(dst, sizeof(NETADDR));
+		dst->type = NETTYPE_IPV4;
+		dst->port = htons(((struct sockaddr_in*)src)->sin_port);
+		mem_copy(dst->ip, &((struct sockaddr_in*)src)->sin_addr.s_addr, 4);
+	}
+	else if(src->sa_family == AF_INET6)
+	{
+		mem_zero(dst, sizeof(NETADDR));
+		dst->type = NETTYPE_IPV6;
+		dst->port = htons(((struct sockaddr_in6*)src)->sin6_port);
+		mem_copy(dst->ip, &((struct sockaddr_in6*)src)->sin6_addr.s6_addr, 16);
+	}
+	else
+	{
+		mem_zero(dst, sizeof(struct sockaddr));
+		dbg_msg("system", "couldn't convert sockaddr of family %d", src->sa_family);
+	}
 }
 
 int net_addr_comp(const NETADDR *a, const NETADDR *b)
@@ -552,23 +583,70 @@ void net_addr_str(const NETADDR *addr, char *string, int max_length)
 		str_format(string, max_length, "unknown type %d", addr->type);
 }
 
+static int priv_net_extract(const char *hostname, char *host, int max_host, int *port)
+{
+	int i;
+
+	*port = 0;
+	host[0] = 0;
+
+	if(hostname[0] == '[')
+	{
+		// ipv6 mode
+		for(i = 1; i < max_host-1 && hostname[i] && hostname[i] != ']'; i++)
+			host[i-1] = hostname[i];
+		host[i-1] = 0;
+		if(hostname[i] != ']') // malformatted
+			return -1;
+
+		i++;
+		if(hostname[i] == ':')
+			*port = atol(hostname+i+1);
+	}
+	else
+	{
+		// generic mode (ipv4, hostname etc)
+		for(i = 0; i < max_host-1 && hostname[i] && hostname[i] != ':'; i++)
+			host[i] = hostname[i];
+		host[i] = 0;
+
+		if(hostname[i] == ':')
+			*port = atol(hostname+i+1);
+	}
+
+	return 0;
+}
+
 int net_host_lookup(const char *hostname, NETADDR *addr, int types)
 {
-	/* TODO: IPv6 support */
 	struct addrinfo hints;
 	struct addrinfo *result;
 	int e;
+	char host[256];
+	int port = 0;
+
+	if(priv_net_extract(hostname, host, sizeof(host), &port))
+		return -1;
+	/*
+	dbg_msg("host lookup", "host='%s' port=%d %d", host, port, types);
+	*/
 	
 	mem_zero(&hints, sizeof(hints));
-	hints.ai_family = AF_INET;
+	
+	hints.ai_family = AF_UNSPEC;
+
+	if(types == NETTYPE_IPV4)
+		hints.ai_family = AF_INET;
+	else if(types == NETTYPE_IPV6)
+		hints.ai_family = AF_INET6;
 
-	e = getaddrinfo(hostname, NULL, &hints, &result);
+	e = getaddrinfo(host, NULL, &hints, &result);
 	if(e != 0 || !result)
 		return -1;
 
 	sockaddr_to_netaddr(result->ai_addr, addr);
 	freeaddrinfo(result);
-	addr->port = 0;
+	addr->port = port;
 	return 0;
 }
 
@@ -629,7 +707,39 @@ int net_addr_from_str(NETADDR *addr, const char *string)
 	
 	if(str[0] == '[')
 	{
-		/* TODO: ipv6 */
+		/* ipv6 */
+		struct sockaddr_in6 sa6;
+		char buf[128];
+		int i, size;
+		for(i = 0; i < 127 && str[i] && str[i] != ']'; i++)
+			buf[i] = str[i];
+		buf[i] = 0;
+		str += i;
+#if defined(CONF_FAMILY_WINDOWS)
+		sa6.sin6_family = AF_INET6;
+		size = (int)sizeof(sa6);
+		if(WSAStringToAddress(buf, AF_INET6, NULL, (struct sockaddr *)&sa6, &size) != 0)
+			return -1;
+#else
+		if(inet_pton(AF_INET6, buf, &sa6) != 1)
+			return -1;
+#endif
+		sockaddr_to_netaddr((struct sockaddr *)&sa6, addr);
+
+		if(*str == ']')
+		{
+			str++;
+			if(*str == ':')
+			{
+				str++;
+				if(parse_uint16(&addr->port, &str))
+					return -1;
+			}
+		}
+		else
+			return -1;
+
+		return 0;
 	}
 	else
 	{
@@ -653,25 +763,63 @@ int net_addr_from_str(NETADDR *addr, const char *string)
 	return 0;
 }
 
+static void priv_net_close_socket(int sock)
+{
+#if defined(CONF_FAMILY_WINDOWS)
+	closesocket(sock);
+#else
+	close(sock);
+#endif
+}
 
-NETSOCKET net_udp_create(NETADDR bindaddr)
+static int priv_net_close_all_sockets(NETSOCKET sock)
 {
-	/* TODO: IPv6 support */
-	struct sockaddr addr;
+	/* close down ipv4 */
+	if(sock.ipv4sock >= 0)
+	{
+		priv_net_close_socket(sock.ipv4sock);
+		sock.ipv4sock = -1;
+		sock.type &= ~NETTYPE_IPV4;
+	}
+
+	/* close down ipv6 */
+	if(sock.ipv6sock >= 0)
+	{
+		priv_net_close_socket(sock.ipv6sock);
+		sock.ipv6sock = -1;
+		sock.type &= ~NETTYPE_IPV6;
+	}
+	return 0;
+}
+
+static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, int sockaddrlen)
+{
+	int sock, e;
 	unsigned long mode = 1;
 	int broadcast = 1;
 
 	/* create socket */
-	int sock = socket(AF_INET, SOCK_DGRAM, 0);
+	sock = socket(domain, type, 0);
 	if(sock < 0)
-		return NETSOCKET_INVALID;
-	
-	/* bind, we should check for error */
-	netaddr_to_sockaddr(&bindaddr, &addr);
-	if(bind(sock, &addr, sizeof(addr)) != 0)
 	{
-		net_udp_close(sock);
-		return NETSOCKET_INVALID;
+		dbg_msg("net", "failed to create socket with domain %d and type %d (%d '%s')", domain, type, errno, strerror(errno));
+		return -1;
+	}
+
+	/* set to IPv6 only if thats what we are creating */
+	if(domain == AF_INET6)
+	{
+		int ipv6only = 1;
+		setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&ipv6only, sizeof(ipv6only));
+	}
+
+	/* bind the socket */
+	e = bind(sock, addr, sockaddrlen);
+	if(e != 0)
+	{
+		dbg_msg("net", "failed to bind socket with domain %d and type %d (%d '%s')", domain, type, errno, strerror(errno));
+		priv_net_close_socket(sock);
+		return -1;
 	}
 	
 	/* set non-blocking */
@@ -684,23 +832,109 @@ NETSOCKET net_udp_create(NETADDR bindaddr)
 	/* set boardcast */
 	setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast));
 	
+	/* return the newly created socket */
+	return sock;
+}
+
+NETSOCKET net_udp_create(NETADDR bindaddr)
+{
+	NETSOCKET sock = invalid_socket;
+	NETADDR tmpbindaddr = bindaddr;
+
+	if(bindaddr.type&NETTYPE_IPV4)
+	{
+		struct sockaddr_in addr;
+		int socket = -1;
+
+		/* bind, we should check for error */
+		tmpbindaddr.type = NETTYPE_IPV4;
+		netaddr_to_sockaddr_in(&tmpbindaddr, &addr);
+		socket = priv_net_create_socket(AF_INET, SOCK_DGRAM, (struct sockaddr *)&addr, sizeof(addr));
+		if(socket >= 0)
+		{
+			sock.type |= NETTYPE_IPV4;
+			sock.ipv4sock = socket;
+		}
+	}
+
+	if(bindaddr.type&NETTYPE_IPV6)
+	{
+		struct sockaddr_in6 addr;
+		int socket = -1;
+
+		/* bind, we should check for error */
+		tmpbindaddr.type = NETTYPE_IPV6;
+		netaddr_to_sockaddr_in6(&tmpbindaddr, &addr);
+		socket = priv_net_create_socket(AF_INET6, SOCK_DGRAM, (struct sockaddr *)&addr, sizeof(addr));
+		if(socket >= 0)
+		{
+			sock.type |= NETTYPE_IPV6;
+			sock.ipv6sock = socket;
+		}
+	}
+
 	/* return */
 	return sock;
 }
 
 int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size)
 {
-	struct sockaddr sa;
-	int d;
-	mem_zero(&sa, sizeof(sa));
-	netaddr_to_sockaddr(addr, &sa);
-	d = sendto((int)sock, (const char*)data, size, 0, &sa, sizeof(sa));
+	int d = -1;
+
+	if(addr->type&NETTYPE_IPV4)
+	{
+		if(sock.ipv4sock >= 0)
+		{
+			struct sockaddr_in sa;
+			if(addr->type&NETTYPE_LINK_BROADCAST)
+			{
+				mem_zero(&sa, sizeof(sa));
+				sa.sin_port = htons(addr->port);
+				sa.sin_family = AF_INET;
+				sa.sin_addr.s_addr = INADDR_BROADCAST;
+			}
+			else
+				netaddr_to_sockaddr_in(addr, &sa);
+
+			d = sendto((int)sock.ipv4sock, (const char*)data, size, 0, (struct sockaddr *)&sa, sizeof(sa));
+		}
+		else
+			dbg_msg("net", "can't sent ipv4 traffic to this socket");
+	}
+
+	if(addr->type&NETTYPE_IPV6)
+	{
+		if(sock.ipv6sock >= 0)
+		{
+			struct sockaddr_in6 sa;
+			if(addr->type&NETTYPE_LINK_BROADCAST)
+			{
+				mem_zero(&sa, sizeof(sa));
+				sa.sin6_port = htons(addr->port);
+				sa.sin6_family = AF_INET6;
+				sa.sin6_addr.s6_addr[0] = 0xff; /* multicast */
+				sa.sin6_addr.s6_addr[1] = 0x02; /* link local scope */
+				sa.sin6_addr.s6_addr[15] = 1; /* all nodes */
+			}
+			else
+				netaddr_to_sockaddr_in6(addr, &sa);
+
+			d = sendto((int)sock.ipv6sock, (const char*)data, size, 0, (struct sockaddr *)&sa, sizeof(sa));
+		}
+		else
+			dbg_msg("net", "can't sent ipv6 traffic to this socket");
+	}
+	/*
+	else
+		dbg_msg("net", "can't sent to network of type %d", addr->type);
+		*/
+
 	/*if(d < 0)
 	{
 		char addrstr[256];
 		net_addr_str(addr, addrstr, sizeof(addrstr));
 		
-		dbg_msg("net", "sendto error %d %x", d, d);
+		dbg_msg("net", "sendto error (%d '%s')", errno, strerror(errno));
 		dbg_msg("net", "\tsock = %d %x", sock, sock);
 		dbg_msg("net", "\tsize = %d %x", size, size);
 		dbg_msg("net", "\taddr = %s", addrstr);
@@ -713,13 +947,25 @@ int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size
 
 int net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize)
 {
-	struct sockaddr from;
-	int bytes;
-	socklen_t fromlen = sizeof(struct sockaddr);
-	bytes = recvfrom(sock, (char*)data, maxsize, 0, &from, &fromlen);
+	char sockaddrbuf[128];
+	socklen_t fromlen;// = sizeof(sockaddrbuf);
+	int bytes = 0;
+
+	if(bytes == 0 && sock.ipv4sock >= 0)
+	{
+		fromlen = sizeof(struct sockaddr_in);
+		bytes = recvfrom(sock.ipv4sock, (char*)data, maxsize, 0, (struct sockaddr *)&sockaddrbuf, &fromlen);
+	}
+
+	if(bytes <= 0 && sock.ipv6sock >= 0)
+	{
+		fromlen = sizeof(struct sockaddr_in6);
+		bytes = recvfrom(sock.ipv6sock, (char*)data, maxsize, 0, (struct sockaddr *)&sockaddrbuf, &fromlen);
+	}
+
 	if(bytes > 0)
 	{
-		sockaddr_to_netaddr(&from, addr);
+		sockaddr_to_netaddr((struct sockaddr *)&sockaddrbuf, addr);
 		network_stats.recv_bytes += bytes;
 		network_stats.recv_packets++;
 		return bytes;
@@ -731,27 +977,29 @@ int net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize)
 
 int net_udp_close(NETSOCKET sock)
 {
-#if defined(CONF_FAMILY_WINDOWS)
-	closesocket(sock);
-#else
-	close((int)sock);
-#endif
-	return 0;
+	return priv_net_close_all_sockets(sock);
 }
 
+// TODO: make TCP stuff work again
 NETSOCKET net_tcp_create(const NETADDR *a)
 {
 	/* TODO: IPv6 support */
-    struct sockaddr addr;
+	NETSOCKET sock = invalid_socket;
 
-    /* create socket */
-    int sock = socket(AF_INET, SOCK_STREAM, 0);
-    if(sock < 0)
-        return NETSOCKET_INVALID;
+	if(a->type&NETTYPE_IPV4)
+	{
+		struct sockaddr_in addr;
+
+		/* create socket */
+		sock.type |= NETTYPE_IPV4;
+		sock.ipv4sock = socket(AF_INET, SOCK_STREAM, 0);
+		if(sock.ipv4sock < 0)
+			return invalid_socket;
 
-    /* bind, we should check for error */
-    netaddr_to_sockaddr(a, &addr);
-    bind(sock, &addr, sizeof(addr));
+		/* bind, we should check for error */
+		netaddr_to_sockaddr_in(a, &addr);
+		bind(sock.ipv4sock, (struct sockaddr *)&addr, sizeof(addr));
+	}
 
     /* return */
     return sock;
@@ -760,26 +1008,58 @@ NETSOCKET net_tcp_create(const NETADDR *a)
 int net_tcp_set_non_blocking(NETSOCKET sock)
 {
 	unsigned long mode = 1;
+	if(sock.ipv4sock >= 0)
+	{
 #if defined(CONF_FAMILY_WINDOWS)
-	return ioctlsocket(sock, FIONBIO, &mode);
+		ioctlsocket(sock.ipv4sock, FIONBIO, (unsigned long *)&mode);
 #else
-	return ioctl(sock, FIONBIO, &mode);
+		ioctl(sock.ipv4sock, FIONBIO, (unsigned long *)&mode);
 #endif
+	}
+
+	if(sock.ipv6sock >= 0)
+	{
+#if defined(CONF_FAMILY_WINDOWS)
+		ioctlsocket(sock.ipv6sock, FIONBIO, (unsigned long *)&mode);
+#else
+		ioctl(sock.ipv6sock, FIONBIO, (unsigned long *)&mode);
+#endif
+	}
+
+	return 0;
 }
 
 int net_tcp_set_blocking(NETSOCKET sock)
 {
 	unsigned long mode = 0;
+	if(sock.ipv4sock >= 0)
+	{
+#if defined(CONF_FAMILY_WINDOWS)
+		ioctlsocket(sock.ipv4sock, FIONBIO, (unsigned long *)&mode);
+#else
+		ioctl(sock.ipv4sock, FIONBIO, (unsigned long *)&mode);
+#endif
+	}
+
+	if(sock.ipv6sock >= 0)
+	{
 #if defined(CONF_FAMILY_WINDOWS)
-	return ioctlsocket(sock, FIONBIO, &mode);
+		ioctlsocket(sock.ipv6sock, FIONBIO, (unsigned long *)&mode);
 #else
-	return ioctl(sock, FIONBIO, &mode);
+		ioctl(sock.ipv6sock, FIONBIO, (unsigned long *)&mode);
 #endif
+	}
+
+	return 0;
 }
 
 int net_tcp_listen(NETSOCKET sock, int backlog)
 {
-	return listen(sock, backlog);
+	if(sock.ipv4sock >= 0)
+		listen(sock.ipv4sock, backlog);
+	if(sock.ipv6sock >= 0)
+		listen(sock.ipv6sock, backlog);
+	return 0;
 }
 
 int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *a)
@@ -788,61 +1068,80 @@ int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *a)
 	socklen_t sockaddr_len;
 	struct sockaddr addr;
 
+	*new_sock = invalid_socket;
+
 	sockaddr_len = sizeof(addr);
 
-	s = accept(sock, &addr, &sockaddr_len);
+	if(sock.ipv4sock >= 0)
+	{
+		s = accept(sock.ipv4sock, &addr, &sockaddr_len);
+
+		if (s != -1)
+		{
+			sockaddr_to_netaddr(&addr, a);
+			new_sock->type = NETTYPE_IPV4;
+			new_sock->ipv4sock = s;
+			return s;
+		}
+	}
 
-	if (s != -1)
+	if(sock.ipv6sock >= 0)
 	{
-		sockaddr_to_netaddr(&addr, a);
-		*new_sock = s;
+		s = accept(sock.ipv6sock, &addr, &sockaddr_len);
+
+		if (s != -1)
+		{
+			sockaddr_to_netaddr(&addr, a);
+			new_sock->type = NETTYPE_IPV6;
+			new_sock->ipv6sock = s;
+			return s;
+		}
 	}
-	return s;
+
+	return 0;
 }
 
 int net_tcp_connect(NETSOCKET sock, const NETADDR *a)
 {
-  struct sockaddr addr;
-
-  netaddr_to_sockaddr(a, &addr);
-  return connect(sock, &addr, sizeof(addr)); 
+	/*struct sockaddr addr;
+	netaddr_to_sockaddr(a, &addr);
+	return connect(sock, &addr, sizeof(addr));
+	*/
+	return 0;
 }
 
 int net_tcp_connect_non_blocking(NETSOCKET sock, const NETADDR *a)
 {
 	struct sockaddr addr;
-	int res;
+	int res = 0;
 
+	/*
 	netaddr_to_sockaddr(a, &addr);
 	net_tcp_set_non_blocking(sock);
   	res = connect(sock, &addr, sizeof(addr));
 	net_tcp_set_blocking(sock);
+	*/
 
 	return res;
 }
 
 int net_tcp_send(NETSOCKET sock, const void *data, int size)
 {
-  int d;
-  d = send((int)sock, (const char*)data, size, 0);
-  return d;
+	int bytes = 0;
+	/* bytes = send((int)sock, (const char*)data, size, 0); */
+	return bytes;
 }
 
 int net_tcp_recv(NETSOCKET sock, void *data, int maxsize)
 {
-  int bytes;
-  bytes = recv((int)sock, (char*)data, maxsize, 0);
-  return bytes;
+	int bytes = 0;
+	/* bytes = recv((int)sock, (char*)data, maxsize, 0); */
+	return bytes;
 }
 
 int net_tcp_close(NETSOCKET sock)
 {
-#if defined(CONF_FAMILY_WINDOWS)
-	closesocket(sock);
-#else
-	close((int)sock);
-#endif
-	return 0;
+	return priv_net_close_all_sockets(sock);
 }
 
 int net_errno()
@@ -1077,17 +1376,34 @@ int net_socket_read_wait(NETSOCKET sock, int time)
 {
     struct timeval tv;
     fd_set readfds;
+	int sockid;
 
     tv.tv_sec = 0;
     tv.tv_usec = 1000*time;
+	sockid = 0;
 
     FD_ZERO(&readfds);
-    FD_SET(sock, &readfds);
+    if(sock.ipv4sock >= 0)
+	{
+		FD_SET(sock.ipv4sock, &readfds);
+		sockid = sock.ipv4sock;
+	}
+	if(sock.ipv6sock >= 0)
+	{
+		FD_SET(sock.ipv6sock, &readfds);
+		if(sock.ipv6sock > sockid)
+			sockid = sock.ipv6sock;
+	}
 
     /* don't care about writefds and exceptfds */
-    select(sock+1, &readfds, NULL, NULL, &tv);
-    if(FD_ISSET(sock, &readfds))
-    	return 1;
+    select(sockid+1, &readfds, NULL, NULL, &tv);
+
+	if(sock.ipv4sock >= 0 && FD_ISSET(sock.ipv4sock, &readfds))
+		return 1;
+
+	if(sock.ipv6sock >= 0 && FD_ISSET(sock.ipv6sock, &readfds))
+		return 1;
+
     return 0;
 }
 
diff --git a/src/base/system.h b/src/base/system.h
index ed46cef7..fea9e452 100644
--- a/src/base/system.h
+++ b/src/base/system.h
@@ -418,15 +418,22 @@ int64 time_freq();
 unsigned time_timestamp();
 
 /* Group: Network General */
-typedef int NETSOCKET;
+typedef struct
+{
+	int type;
+	int ipv4sock;
+	int ipv6sock;
+} NETSOCKET;
+
 enum
 {
-	NETSOCKET_INVALID = -1,
+	NETADDR_MAXSTRSIZE = 1+(8*4+7)+1+1+5+1, // [XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX]:XXXXX
 	
 	NETTYPE_INVALID = 0,
 	NETTYPE_IPV4 = 1,
 	NETTYPE_IPV6 = 2,
-	NETTYPE_ALL = ~0
+	NETTYPE_LINK_BROADCAST = 4,
+	NETTYPE_ALL = NETTYPE_IPV4|NETTYPE_IPV6
 };
 
 typedef struct
diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp
index 198f5f1a..a39bfb5b 100644
--- a/src/engine/client/client.cpp
+++ b/src/engine/client/client.cpp
@@ -692,20 +692,8 @@ void CClient::Connect(const char *pAddress)
 	m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf);
 
 	ServerInfoRequest();
-	str_copy(aBuf, m_aServerAddressStr, sizeof(aBuf));
 
-	for(int k = 0; aBuf[k]; k++)
-	{
-		if(aBuf[k] == ':')
-		{
-			Port = str_toint(aBuf+k+1);
-			aBuf[k] = 0;
-			break;
-		}
-	}
-
-	// TODO: IPv6 support
-	if(net_host_lookup(aBuf, &m_ServerAddress, NETTYPE_IPV4) != 0)
+	if(net_host_lookup(m_aServerAddressStr, &m_ServerAddress, NETTYPE_ALL) != 0)
 	{
 		char aBufMsg[256];
 		str_format(aBufMsg, sizeof(aBufMsg), "could not find the address of %s, connecting to localhost", aBuf);
@@ -714,7 +702,8 @@ void CClient::Connect(const char *pAddress)
 	}
 
 	m_RconAuthed = 0;
-	m_ServerAddress.port = Port;
+	if(m_ServerAddress.port == 0)
+		m_ServerAddress.port = Port;
 	m_NetClient.Connect(&m_ServerAddress);
 	SetState(IClient::STATE_CONNECTING);
 
@@ -1119,9 +1108,7 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket)
 			Info.m_NumPlayers < 0 || Info.m_NumPlayers > Info.m_NumClients || Info.m_MaxPlayers < 0 || Info.m_MaxPlayers > Info.m_MaxClients)
 			return;
 
-		str_format(Info.m_aAddress, sizeof(Info.m_aAddress), "%d.%d.%d.%d:%d",
-			pPacket->m_Address.ip[0], pPacket->m_Address.ip[1], pPacket->m_Address.ip[2],
-			pPacket->m_Address.ip[3], pPacket->m_Address.port);
+		net_addr_str(&pPacket->m_Address, Info.m_aAddress, sizeof(Info.m_aAddress));
 
 		for(int i = 0; i < Info.m_NumClients; i++)
 		{
@@ -1868,7 +1855,12 @@ void CClient::Run()
 	{
 		NETADDR BindAddr;
 		mem_zero(&BindAddr, sizeof(BindAddr));
-		m_NetClient.Open(BindAddr, 0);
+		BindAddr.type = NETTYPE_ALL;
+		if(!m_NetClient.Open(BindAddr, 0))
+		{
+			dbg_msg("client", "couldn't start network");
+			return;
+		}
 	}
 
 	// connect to the server if wanted
diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp
index d4efe975..55d25f8a 100644
--- a/src/engine/client/serverbrowser.cpp
+++ b/src/engine/client/serverbrowser.cpp
@@ -492,12 +492,10 @@ void CServerBrowser::Refresh(int Type)
 		mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
 		Buffer[sizeof(SERVERBROWSE_GETINFO)] = m_CurrentToken;
 
+		/* do the broadcast version */
 		Packet.m_ClientID = -1;
 		mem_zero(&Packet, sizeof(Packet));
-		Packet.m_Address.ip[0] = 255;
-		Packet.m_Address.ip[1] = 255;
-		Packet.m_Address.ip[2] = 255;
-		Packet.m_Address.ip[3] = 255;
+		Packet.m_Address.type = NETTYPE_ALL|NETTYPE_LINK_BROADCAST;
 		Packet.m_Flags = NETSENDFLAG_CONNLESS;
 		Packet.m_DataSize = sizeof(Buffer);
 		Packet.m_pData = Buffer;
diff --git a/src/engine/server.h b/src/engine/server.h
index 7e11d865..c8e55110 100644
--- a/src/engine/server.h
+++ b/src/engine/server.h
@@ -30,7 +30,7 @@ public:
 	virtual int ClientCountry(int ClientID) = 0;
 	virtual bool ClientIngame(int ClientID) = 0;
 	virtual int GetClientInfo(int ClientID, CClientInfo *pInfo) = 0;
-	virtual void GetClientIP(int ClientID, char *pIPString, int Size) = 0;
+	virtual void GetClientAddr(int ClientID, char *pAddrStr, int Size) = 0;
 	virtual int *LatestInput(int ClientID, int *pSize) = 0;
 	
 	virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID) = 0;
diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
index c3ba583b..f1d2e6e1 100644
--- a/src/engine/server/server.cpp
+++ b/src/engine/server/server.cpp
@@ -333,12 +333,13 @@ int CServer::GetClientInfo(int ClientID, CClientInfo *pInfo)
 	return 0;
 }
 
-void CServer::GetClientIP(int ClientID, char *pIPString, int Size)
+void CServer::GetClientAddr(int ClientID, char *pAddrStr, int Size)
 {
 	if(ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_INGAME)
 	{
 		NETADDR Addr = m_NetServer.ClientAddr(ClientID);
-		str_format(pIPString, Size, "%d.%d.%d.%d", Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
+		Addr.port = 0;
+		net_addr_str(&Addr, pAddrStr, Size);
 	}
 }
 	
@@ -587,12 +588,10 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
 	CServer *pThis = (CServer *)pUser;
 	
 	NETADDR Addr = pThis->m_NetServer.ClientAddr(ClientID);
+	char aAddrStr[NETADDR_MAXSTRSIZE];
+	net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
 	char aBuf[256];
-	str_format(aBuf, sizeof(aBuf), "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"",
-		ClientID,
-		Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3],
-		pReason
-	);
+	str_format(aBuf, sizeof(aBuf), "client dropped. cid=%d addr=%s reason='%s'", ClientID, aAddrStr,	pReason);
 	pThis->Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
 
 	// notify the mod about the drop
@@ -732,10 +731,11 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 			if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTING)
 			{
 				Addr = m_NetServer.ClientAddr(ClientID);
+				char aAddrStr[NETADDR_MAXSTRSIZE];
+				net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
 					
 				char aBuf[256];
-				str_format(aBuf, sizeof(aBuf), "player is ready. ClientID=%x ip=%d.%d.%d.%d",
-					ClientID, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
+				str_format(aBuf, sizeof(aBuf), "player is ready. ClientID=%x addr=%s", ClientID, aAddrStr);
 				Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
 				m_aClients[ClientID].m_State = CClient::STATE_READY;
 				GameServer()->OnClientConnected(ClientID);
@@ -747,10 +747,11 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 			if(m_aClients[ClientID].m_State == CClient::STATE_READY && GameServer()->IsClientReady(ClientID))
 			{
 				Addr = m_NetServer.ClientAddr(ClientID);
+				char aAddrStr[NETADDR_MAXSTRSIZE];
+				net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
 					
 				char aBuf[256];
-				str_format(aBuf, sizeof(aBuf), "player has entered the game. ClientID=%x ip=%d.%d.%d.%d",
-					ClientID, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
+				str_format(aBuf, sizeof(aBuf), "player has entered the game. ClientID=%x addr=%s", ClientID, aAddrStr);
 				Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
 				m_aClients[ClientID].m_State = CClient::STATE_INGAME;
 				GameServer()->OnClientEnter(ClientID);
@@ -1102,9 +1103,8 @@ int CServer::Run()
 	}
 	
 	// start server
-	// TODO: IPv6 support
 	NETADDR BindAddr;
-	if(g_Config.m_SvBindaddr[0] && net_host_lookup(g_Config.m_SvBindaddr, &BindAddr, NETTYPE_IPV4) == 0)
+	if(g_Config.m_SvBindaddr[0] && net_host_lookup(g_Config.m_SvBindaddr, &BindAddr, NETTYPE_ALL) == 0)
 	{
 		// sweet!
 		BindAddr.port = g_Config.m_SvPort;
@@ -1112,6 +1112,7 @@ int CServer::Run()
 	else
 	{
 		mem_zero(&BindAddr, sizeof(BindAddr));
+		BindAddr.type = NETTYPE_ALL;
 		BindAddr.port = g_Config.m_SvPort;
 	}
 	
@@ -1342,8 +1343,11 @@ void CServer::ConUnban(IConsole::IResult *pResult, void *pUser)
 	
 	if(net_addr_from_str(&Addr, pStr) == 0 && !pServer->BanRemove(Addr))
 	{
+		char aAddrStr[NETADDR_MAXSTRSIZE];
+		net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
+
 		char aBuf[256];
-		str_format(aBuf, sizeof(aBuf), "unbanned %d.%d.%d.%d", Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
+		str_format(aBuf, sizeof(aBuf), "unbanned %s", aAddrStr);
 		pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
 	}
 	else if(StrAllnum(pStr))
@@ -1354,8 +1358,11 @@ void CServer::ConUnban(IConsole::IResult *pResult, void *pUser)
 			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "invalid ban index");
 		else if(!pServer->BanRemove(Info.m_Addr))
 		{
+			char aAddrStr[NETADDR_MAXSTRSIZE];
+			net_addr_str(&Info.m_Addr, aAddrStr, sizeof(aAddrStr));
+
 			char aBuf[256];
-			str_format(aBuf, sizeof(aBuf), "unbanned %d.%d.%d.%d", Info.m_Addr.ip[0], Info.m_Addr.ip[1], Info.m_Addr.ip[2], Info.m_Addr.ip[3]);
+			str_format(aBuf, sizeof(aBuf), "unbanned %s", aAddrStr);
 			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
 		}
 	}
@@ -1367,6 +1374,7 @@ void CServer::ConBans(IConsole::IResult *pResult, void *pUser)
 {
 	unsigned Now = time_timestamp();
 	char aBuf[1024];
+	char aAddrStr[NETADDR_MAXSTRSIZE];
 	CServer* pServer = (CServer *)pUser;
 	
 	int Num = pServer->m_NetServer.BanNum();
@@ -1375,15 +1383,16 @@ void CServer::ConBans(IConsole::IResult *pResult, void *pUser)
 		CNetServer::CBanInfo Info;
 		pServer->m_NetServer.BanGet(i, &Info);
 		NETADDR Addr = Info.m_Addr;
+		net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
 		
 		if(Info.m_Expires == -1)
 		{
-			str_format(aBuf, sizeof(aBuf), "#%d %d.%d.%d.%d for life", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]);
+			str_format(aBuf, sizeof(aBuf), "#%s for life", i, aAddrStr);
 		}
 		else
 		{
 			unsigned t = Info.m_Expires - Now;
-			str_format(aBuf, sizeof(aBuf), "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], t/60, t%60);
+			str_format(aBuf, sizeof(aBuf), "#%s for %d minutes and %d seconds", i, aAddrStr, t/60, t%60);
 		}
 		pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
 	}
@@ -1396,6 +1405,7 @@ void CServer::ConStatus(IConsole::IResult *pResult, void *pUser)
 	int i;
 	NETADDR Addr;
 	char aBuf[1024];
+	char aAddrStr[NETADDR_MAXSTRSIZE];
 	CServer* pServer = (CServer *)pUser;
 
 	for(i = 0; i < MAX_CLIENTS; i++)
@@ -1403,13 +1413,12 @@ void CServer::ConStatus(IConsole::IResult *pResult, void *pUser)
 		if(pServer->m_aClients[i].m_State != CClient::STATE_EMPTY)
 		{
 			Addr = pServer->m_NetServer.ClientAddr(i);
+			net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
 			if(pServer->m_aClients[i].m_State == CClient::STATE_INGAME)
-				str_format(aBuf, sizeof(aBuf), "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d",
-					i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], Addr.port,
+				str_format(aBuf, sizeof(aBuf), "id=%d addr=%s name='%s' score=%d", i, aAddrStr,
 					pServer->m_aClients[i].m_aName, pServer->m_aClients[i].m_Score);
 			else
-				str_format(aBuf, sizeof(aBuf), "id=%d addr=%d.%d.%d.%d:%d connecting",
-					i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], Addr.port);
+				str_format(aBuf, sizeof(aBuf), "id=%d addr=%s connecting", i, aAddrStr);
 			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "Server", aBuf);
 		}
 	}
diff --git a/src/engine/server/server.h b/src/engine/server/server.h
index 848b485a..00c1c648 100644
--- a/src/engine/server/server.h
+++ b/src/engine/server/server.h
@@ -141,7 +141,7 @@ public:
 
 	bool IsAuthed(int ClientID);
 	int GetClientInfo(int ClientID, CClientInfo *pInfo);
-	void GetClientIP(int ClientID, char *pIPString, int Size);
+	void GetClientAddr(int ClientID, char *pAddrStr, int Size);
 	const char *ClientName(int ClientID);
 	const char *ClientClan(int ClientID);
 	int ClientCountry(int ClientID);
diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h
index f0310421..1ac22056 100644
--- a/src/engine/serverbrowser.h
+++ b/src/engine/serverbrowser.h
@@ -44,7 +44,7 @@ public:
 	char m_aName[64];
 	char m_aMap[32];
 	char m_aVersion[32];
-	char m_aAddress[24];
+	char m_aAddress[NETADDR_MAXSTRSIZE];
 	CClient m_aClients[MAX_CLIENTS];
 };
 
diff --git a/src/engine/shared/network_client.cpp b/src/engine/shared/network_client.cpp
index d3adfd20..82a09474 100644
--- a/src/engine/shared/network_client.cpp
+++ b/src/engine/shared/network_client.cpp
@@ -5,11 +5,17 @@
 
 bool CNetClient::Open(NETADDR BindAddr, int Flags)
 {
+	// open socket
+	NETSOCKET Socket;
+	Socket = net_udp_create(BindAddr);
+	if(!Socket.type)
+		return false;
+
 	// clean it
 	mem_zero(this, sizeof(*this));
 
-	// open socket
-	m_Socket = net_udp_create(BindAddr);
+	// init
+	m_Socket = Socket;
 	m_Connection.Init(m_Socket);
 	return true;
 }
diff --git a/src/engine/shared/network_server.cpp b/src/engine/shared/network_server.cpp
index 3b5ef317..88aac789 100644
--- a/src/engine/shared/network_server.cpp
+++ b/src/engine/shared/network_server.cpp
@@ -33,7 +33,7 @@ bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int MaxClientsPerIP, int
 	
 	// open socket
 	m_Socket = net_udp_create(BindAddr);
-	if(m_Socket == NETSOCKET_INVALID)
+	if(!m_Socket.type)
 		return false;
 	
 	// clamp clients
diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp
index 6a7cc1f9..d008787a 100644
--- a/src/game/server/gamecontext.cpp
+++ b/src/game/server/gamecontext.cpp
@@ -414,10 +414,10 @@ void CGameContext::OnTick()
 			if(m_VoteUpdate)
 			{
 				// count votes
-				char aaBuf[MAX_CLIENTS][64] = {{0}};
+				char aaBuf[MAX_CLIENTS][NETADDR_MAXSTRSIZE] = {{0}};
 				for(int i = 0; i < MAX_CLIENTS; i++)
 					if(m_apPlayers[i])
-						Server()->GetClientIP(i, aaBuf[i], 64);
+						Server()->GetClientAddr(i, aaBuf[i], NETADDR_MAXSTRSIZE);
 				bool aVoteChecked[MAX_CLIENTS] = {0};
 				for(int i = 0; i < MAX_CLIENTS; i++)
 				{
@@ -709,9 +709,10 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
 				str_format(aCmd, sizeof(aCmd), "kick %d Kicked by vote", KickID);
 			else
 			{
-				char aBuf[64] = {0};
-				Server()->GetClientIP(KickID, aBuf, sizeof(aBuf));
-				str_format(aCmd, sizeof(aCmd), "ban %s %d Banned by vote", aBuf, g_Config.m_SvVoteKickBantime);
+				char aAddrStr[NETADDR_MAXSTRSIZE] = {0};
+				Server()->GetClientAddr(KickID, aAddrStr, sizeof(aAddrStr));
+				str_format(aCmd, sizeof(aCmd), "ban %s %d Banned by vote", aAddrStr, g_Config.m_SvVoteKickBantime);
+				Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aCmd);
 			}
 		}
 		else if(str_comp_nocase(pMsg->m_Type, "spectate") == 0)
@@ -1229,9 +1230,9 @@ void CGameContext::ConForceVote(IConsole::IResult *pResult, void *pUserData)
 		}
 		else
 		{
-			char aIP[64] = {0};
-			pSelf->Server()->GetClientIP(KickID, aIP, sizeof(aIP));
-			str_format(aBuf, sizeof(aBuf), "ban %s %d %s", aIP, g_Config.m_SvVoteKickBantime, pReason);
+			char aAddrStr[NETADDR_MAXSTRSIZE] = {0};
+			pSelf->Server()->GetClientAddr(KickID, aAddrStr, sizeof(aAddrStr));
+			str_format(aBuf, sizeof(aBuf), "ban %s %d %s", aAddrStr, g_Config.m_SvVoteKickBantime, pReason);
 			pSelf->Console()->ExecuteLine(aBuf);
 		}
 	}