about summary refs log tree commit diff
path: root/src/base/system.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/base/system.c')
-rw-r--r--src/base/system.c492
1 files changed, 404 insertions, 88 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;
 }