Browse Source

Merge pull request #6925 from godotengine/ipv6

Adding IPv6 support
Rémi Verschelde 8 years ago
parent
commit
c67e3a485d

+ 58 - 0
bin/tests/test_string.cpp

@@ -32,6 +32,7 @@
 #include <stdio.h>
 #include "os/os.h"
 #include "drivers/nrex/regex.h"
+#include "core/io/ip_address.h"
 
 #include "test_string.h"
 
@@ -843,6 +844,62 @@ bool test_28() {
 	return state;
 }
 
+bool test_29() {
+
+	bool error = false;
+	bool state = true;
+	bool success = false;
+
+	IP_Address ip0("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+	OS::get_singleton()->print("ip0 is %ls\n", String(ip0).c_str());
+
+	IP_Address ip(0x0123, 0x4567, 0x89ab, 0xcdef, IP_Address::TYPE_IPV6);
+	OS::get_singleton()->print("ip6 is %ls\n", String(ip).c_str());
+
+	IP_Address ip2("fe80::52e5:49ff:fe93:1baf");
+	OS::get_singleton()->print("ip6 is %ls\n", String(ip2).c_str());
+
+	IP_Address ip3("::ffff:192.168.0.1");
+	OS::get_singleton()->print("ip6 is %ls\n", String(ip3).c_str());
+
+	String ip4 = "192.168.0.1";
+	success = ip4.is_valid_ip_address();
+	OS::get_singleton()->print("Is valid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	ip4 = "192.368.0.1";
+	success = (!ip4.is_valid_ip_address());
+	OS::get_singleton()->print("Is invalid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	String ip6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+	success = ip6.is_valid_ip_address();
+	OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	ip6 = "2001:0db8:85j3:0000:0000:8a2e:0370:7334";
+	success = (!ip6.is_valid_ip_address());
+	OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	ip6 = "2001:0db8:85f345:0000:0000:8a2e:0370:7334";
+	success = (!ip6.is_valid_ip_address());
+	OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	ip6 = "2001:0db8::0:8a2e:370:7334";
+	success = (ip6.is_valid_ip_address());
+	OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	ip6 = "::ffff:192.168.0.1";
+	success = (ip6.is_valid_ip_address());
+	OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	return state;
+};
+
 typedef bool (*TestFunc)(void);
 
 TestFunc test_funcs[] = {
@@ -875,6 +932,7 @@ TestFunc test_funcs[] = {
 	test_26,
 	test_27,
 	test_28,
+	test_29,
 	0
 
 };

+ 4 - 3
core/io/http_client.cpp

@@ -29,8 +29,9 @@
 #include "http_client.h"
 #include "io/stream_peer_ssl.h"
 
+VARIANT_ENUM_CAST(IP_Address::AddrType);
 
-Error HTTPClient::connect(const String &p_host, int p_port, bool p_ssl,bool p_verify_host){
+Error HTTPClient::connect(const String &p_host, int p_port, bool p_ssl,bool p_verify_host, IP_Address::AddrType p_addr_type){
 
 	close();
 	conn_port=p_port;
@@ -62,7 +63,7 @@ Error HTTPClient::connect(const String &p_host, int p_port, bool p_ssl,bool p_ve
 		status=STATUS_CONNECTING;
 	} else {
 		//is hostname
-		resolving=IP::get_singleton()->resolve_hostname_queue_item(conn_host);
+		resolving=IP::get_singleton()->resolve_hostname_queue_item(conn_host, p_addr_type);
 		status=STATUS_RESOLVING;
 
 	}
@@ -635,7 +636,7 @@ Error HTTPClient::_get_http_data(uint8_t* p_buffer, int p_bytes,int &r_received)
 
 void HTTPClient::_bind_methods() {
 
-	ObjectTypeDB::bind_method(_MD("connect:Error","host","port","use_ssl","verify_host"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true));
+	ObjectTypeDB::bind_method(_MD("connect:Error","host","port","use_ssl","verify_host"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true),DEFVAL(IP_Address::TYPE_ANY));
 	ObjectTypeDB::bind_method(_MD("set_connection","connection:StreamPeer"),&HTTPClient::set_connection);
 	ObjectTypeDB::bind_method(_MD("get_connection:StreamPeer"),&HTTPClient::get_connection);
 	ObjectTypeDB::bind_method(_MD("request_raw","method","url","headers","body"),&HTTPClient::request_raw);

+ 1 - 1
core/io/http_client.h

@@ -165,7 +165,7 @@ public:
 
 
 	//Error connect_and_get(const String& p_url,bool p_verify_host=true); //connects to a full url and perform request
-	Error connect(const String &p_host,int p_port,bool p_ssl=false,bool p_verify_host=true);
+	Error connect(const String &p_host,int p_port,bool p_ssl=false,bool p_verify_host=true, IP_Address::AddrType p_addr_type = IP_Address::TYPE_ANY);
 
 	void set_connection(const Ref<StreamPeer>& p_connection);
 	Ref<StreamPeer> get_connection() const;

+ 24 - 13
core/io/ip.cpp

@@ -32,6 +32,7 @@
 #include "hash_map.h"
 
 VARIANT_ENUM_CAST(IP::ResolverStatus);
+VARIANT_ENUM_CAST(IP_Address::AddrType);
 
 /************* RESOLVER ******************/
 
@@ -43,10 +44,12 @@ struct _IP_ResolverPrivate {
 		volatile IP::ResolverStatus status;
 		IP_Address response;
 		String hostname;
+		IP_Address::AddrType type;
 
 		void clear() {
 			status = IP::RESOLVER_STATUS_NONE;
 			response = IP_Address();
+			type = IP_Address::TYPE_NONE;
 			hostname="";
 		};
 
@@ -78,9 +81,9 @@ struct _IP_ResolverPrivate {
 
 			if (queue[i].status!=IP::RESOLVER_STATUS_WAITING)
 				continue;
-			queue[i].response=IP::get_singleton()->resolve_hostname(queue[i].hostname);
+			queue[i].response=IP::get_singleton()->resolve_hostname(queue[i].hostname, queue[i].type);
 
-			if (queue[i].response.host==0)
+			if (queue[i].response.type==IP_Address::TYPE_NONE)
 				queue[i].status=IP::RESOLVER_STATUS_ERROR;
 			else
 				queue[i].status=IP::RESOLVER_STATUS_DONE;
@@ -109,21 +112,23 @@ struct _IP_ResolverPrivate {
 
 
 
-IP_Address IP::resolve_hostname(const String& p_hostname) {
+IP_Address IP::resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type) {
 
-	GLOBAL_LOCK_FUNCTION
+	GLOBAL_LOCK_FUNCTION;
 
 	if (resolver->cache.has(p_hostname))
-		return resolver->cache[p_hostname];
+		if (resolver->cache[p_hostname].type & p_type != 0)
+			return resolver->cache[p_hostname];
+		// requested type is different from type in cache. continue resolution, if successful it'll overwrite cache
 
-	IP_Address res = _resolve_hostname(p_hostname);
+	IP_Address res = _resolve_hostname(p_hostname, p_type);
 	resolver->cache[p_hostname]=res;
 	return res;
 
 }
-IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
+IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname, IP_Address::AddrType p_type) {
 
-	GLOBAL_LOCK_FUNCTION
+	GLOBAL_LOCK_FUNCTION;
 
 	ResolverID id = resolver->find_empty_id();
 
@@ -133,7 +138,8 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
 	}
 
 	resolver->queue[id].hostname=p_hostname;
-	if (resolver->cache.has(p_hostname)) {
+	resolver->queue[id].type = p_type;
+	if (resolver->cache.has(p_hostname) && (resolver->cache[p_hostname].type & p_type) != 0) {
 		resolver->queue[id].response=resolver->cache[p_hostname];
 		resolver->queue[id].status=IP::RESOLVER_STATUS_DONE;
 	} else {
@@ -145,10 +151,6 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
 			resolver->resolve_queues();
 	}
 
-
-
-
-
 	return id;
 }
 
@@ -187,6 +189,14 @@ void IP::erase_resolve_item(ResolverID p_id) {
 
 }
 
+void IP::clear_cache(const String &p_hostname) {
+
+	if (p_hostname.empty()) {
+		resolver->cache.clear();
+	} else {
+		resolver->cache.erase(p_hostname);
+	}
+};
 
 Array IP::_get_local_addresses() const {
 
@@ -208,6 +218,7 @@ void IP::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("get_resolve_item_address","id"),&IP::get_resolve_item_address);
 	ObjectTypeDB::bind_method(_MD("erase_resolve_item","id"),&IP::erase_resolve_item);
 	ObjectTypeDB::bind_method(_MD("get_local_addresses"),&IP::_get_local_addresses);
+	ObjectTypeDB::bind_method(_MD("clear_cache"),&IP::clear_cache, DEFVAL(""));
 
 	BIND_CONSTANT( RESOLVER_STATUS_NONE );
 	BIND_CONSTANT( RESOLVER_STATUS_WAITING );

+ 13 - 3
core/io/ip.h

@@ -48,6 +48,14 @@ public:
 		RESOLVER_STATUS_ERROR,
 	};
 
+	enum AddressType {
+
+		ADDRESS_IPV4 = 1,
+		ADDRESS_IPV6 = 2,
+
+		ADDRESS_ANY = 3,
+	};
+
 	enum {
 		RESOLVER_MAX_QUERIES = 32,
 		RESOLVER_INVALID_ID=-1
@@ -65,7 +73,7 @@ protected:
 	static IP*singleton;
 	static void _bind_methods();
 
-	virtual IP_Address _resolve_hostname(const String& p_hostname)=0;
+	virtual IP_Address _resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type = IP_Address::TYPE_ANY)=0;
 	Array _get_local_addresses() const;
 
 	static IP* (*_create)();
@@ -73,14 +81,16 @@ public:
 
 
 
-	IP_Address resolve_hostname(const String& p_hostname);
+	IP_Address resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type = IP_Address::TYPE_ANY);
 	// async resolver hostname
-	ResolverID resolve_hostname_queue_item(const String& p_hostname);
+	ResolverID resolve_hostname_queue_item(const String& p_hostname, IP_Address::AddrType p_type = IP_Address::TYPE_ANY);
 	ResolverStatus get_resolve_item_status(ResolverID p_id) const;
 	IP_Address get_resolve_item_address(ResolverID p_id) const;
 	virtual void get_local_addresses(List<IP_Address> *r_addresses) const=0;
 	void erase_resolve_item(ResolverID p_id);
 
+	void clear_cache(const String& p_hostname = "");
+
 	static IP* get_singleton();
 
 	static IP* create();

+ 174 - 12
core/io/ip_address.cpp

@@ -32,29 +32,191 @@ IP_Address::operator Variant() const {
 
 	return operator String();
 }*/
+
+#include <string.h>
+#include <stdio.h>
+
 IP_Address::operator String() const {
 
-	return itos(field[0])+"."+itos(field[1])+"."+itos(field[2])+"."+itos(field[3]);
+	if (type == TYPE_NONE)
+		return "0.0.0.0";
+	if (type == TYPE_IPV4)
+		return itos(field8[0])+"."+itos(field8[1])+"."+itos(field8[2])+"."+itos(field8[3]);
+	else {
+		String ret;
+		for (int i=0; i<8; i++) {
+			if (i > 0)
+				ret = ret + ":";
+			uint16_t num = (field8[i*2] << 8) + field8[i*2+1];
+			ret = ret + String::num_int64(num, 16);
+		};
+
+		return ret;
+	};
 }
 
-IP_Address::IP_Address(const String& p_string) {
+static void _parse_hex(const String& p_string, int p_start, uint8_t* p_dst) {
+
+	uint16_t ret = 0;
+	for (int i=p_start; i<p_start + 4; i++) {
+
+		if (i >= p_string.length()) {
+			break;
+		};
+
+		int n = 0;
+		CharType c = p_string[i];
+		if (c >= '0' && c <= '9') {
+
+			n = c - '0';
+		} else if (c >= 'a' && c <= 'f') {
+			n = 10 + (c - 'a');
+		} else if (c >= 'A' && c <= 'F') {
+			n = 10 + (c - 'A');
+		} else if (c == ':') {
+			break;
+		} else {
+			ERR_EXPLAIN("Invalid character in ipv6 address: " + p_string);
+			ERR_FAIL();
+		};
+		ret = ret << 4;
+		ret += n;
+	};
+
+	p_dst[0] = ret >> 8;
+	p_dst[1] = ret & 0xff;
+};
+
+void IP_Address::_parse_ipv6(const String& p_string) {
+
+	static const int parts_total = 8;
+	int parts[parts_total] = {0};
+	int parts_count = 0;
+	bool part_found = false;
+	bool part_skip = false;
+	bool part_ipv4 = false;
+	int parts_idx = 0;
+
+	for (int i=0; i<p_string.length(); i++) {
+
+		CharType c = p_string[i];
+		if (c == ':') {
+
+			if (i == 0) {
+				continue; // next must be a ":"
+			};
+			if (!part_found) {
+				part_skip = true;
+				parts[parts_idx++] = -1;
+			};
+			part_found = false;
+		} else if (c == '.') {
+
+			part_ipv4 = true;
+
+		} else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+			if (!part_found) {
+				parts[parts_idx++] = i;
+				part_found = true;
+				++parts_count;
+			};
+		} else {
+
+			ERR_EXPLAIN("Invalid character in IPv6 address: " + p_string);
+			ERR_FAIL();
+		};
+	};
+
+	int parts_extra = 0;
+	if (part_skip) {
+		parts_extra = parts_total - parts_count;
+	};
+
+	int idx = 0;
+	for (int i=0; i<parts_idx; i++) {
 
-	host=0;
-	int slices = p_string.get_slice_count(".");
+		if (parts[i] == -1) {
+
+			for (int j=0; j<parts_extra; j++) {
+				field16[idx++] = 0;
+			};
+			continue;
+		};
+
+		if (part_ipv4 && i == parts_idx - 1) {
+			_parse_ipv4(p_string, parts[i], (uint8_t*)&field16[idx]); // should be the last one
+		} else {
+			_parse_hex(p_string, parts[i], (uint8_t*)&(field16[idx++]));
+		};
+	};
+
+};
+
+void IP_Address::_parse_ipv4(const String& p_string, int p_start, uint8_t* p_ret) {
+
+	String ip;
+	if (p_start != 0) {
+		ip = p_string.substr(p_start, p_string.length() - p_start);
+	} else {
+		ip = p_string;
+	};
+
+	int slices = ip.get_slice_count(".");
 	if (slices!=4) {
-		ERR_EXPLAIN("Invalid IP Address String: "+p_string);
+		ERR_EXPLAIN("Invalid IP Address String: "+ip);
 		ERR_FAIL();
 	}
 	for(int i=0;i<4;i++) {
-
-		field[i]=p_string.get_slicec('.',i).to_int();
+		p_ret[i]=ip.get_slicec('.',i).to_int();
 	}
+};
+
+void IP_Address::clear() {
+
+	memset(&field8[0], 0, sizeof(field8));
+};
+
+IP_Address::IP_Address(const String& p_string) {
+
+	clear();
+	if (p_string.find(":") >= 0) {
+
+		_parse_ipv6(p_string);
+		type = TYPE_IPV6;
+	} else {
+
+		_parse_ipv4(p_string, 0, &field8[0]);
+		type = TYPE_IPV4;
+	};
 }
 
-IP_Address::IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d) {
+_FORCE_INLINE_ static void _32_to_buf(uint8_t* p_dst, uint32_t p_n) {
+
+	p_dst[0] = (p_n >> 24) & 0xff;
+	p_dst[1] = (p_n >> 16) & 0xff;
+	p_dst[2] = (p_n >> 8) & 0xff;
+	p_dst[3] = (p_n >> 0) & 0xff;
+};
+
+IP_Address::IP_Address(uint32_t p_a,uint32_t p_b,uint32_t p_c,uint32_t p_d, IP_Address::AddrType p_type) {
+
+	type = p_type;
+	memset(&field8[0], 0, sizeof(field8));
+	if (p_type == TYPE_IPV4) {
+		field8[0]=p_a;
+		field8[1]=p_b;
+		field8[2]=p_c;
+		field8[3]=p_d;
+	} else if (type == TYPE_IPV6) {
+
+		_32_to_buf(&field8[0], p_a);
+		_32_to_buf(&field8[4], p_b);
+		_32_to_buf(&field8[8], p_c);
+		_32_to_buf(&field8[12], p_d);
+	} else {
+		type = TYPE_NONE;
+		ERR_EXPLAIN("Invalid type specified for IP_Address (use TYPE_IPV4 or TYPE_IPV6");
+		ERR_FAIL();
+	};
 
-	field[0]=p_a;
-	field[1]=p_b;
-	field[2]=p_c;
-	field[3]=p_d;
 }

+ 32 - 6
core/io/ip_address.h

@@ -33,22 +33,48 @@
 
 struct IP_Address {
 
+public:
+	enum AddrType {
+		TYPE_NONE = 0,
+		TYPE_IPV4 = 1,
+		TYPE_IPV6 = 2,
+
+		TYPE_ANY = 3,
+	};
+
+	AddrType type;
+
 	union {
-		uint8_t field[4];
-		uint32_t host;
+		uint8_t field8[16];
+		uint16_t field16[8];
+		uint32_t field32[4];
 	};
 
+protected:
+	void _parse_ipv6(const String& p_string);
+	void _parse_ipv4(const String& p_string, int p_start, uint8_t* p_ret);
+
+public:
 	//operator Variant() const;
 	bool operator==(const IP_Address& p_ip) const {
-		return host==p_ip.host;
+		for (int i=0; i<4; i++)
+			if (field32[i] != p_ip.field32[i])
+				return false;
+		return true;
 	}
 	bool operator!=(const IP_Address& p_ip) const {
-		return host!=p_ip.host;
+		for (int i=0; i<4; i++)
+			if (field32[i] != p_ip.field32[i])
+				return true;
+		return false;
 	}
+
+	void clear();
+
 	operator String() const;
 	IP_Address(const String& p_string);
-	IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d);
-	IP_Address() { host=0; }
+	IP_Address(uint32_t p_a,uint32_t p_b,uint32_t p_c,uint32_t p_d, AddrType p_type=TYPE_IPV4);
+	IP_Address() { clear(); type=TYPE_NONE; }
 };
 
 

+ 2 - 7
core/io/packet_peer_udp.cpp

@@ -29,14 +29,9 @@
 #include "packet_peer_udp.h"
 #include "io/ip.h"
 
-
 PacketPeerUDP* (*PacketPeerUDP::_create)()=NULL;
 
-int PacketPeerUDP::_get_packet_address() const {
-
-	IP_Address ip = get_packet_address();
-	return ip.host;
-}
+VARIANT_ENUM_CAST(IP_Address::AddrType);
 
 String PacketPeerUDP::_get_packet_ip() const {
 
@@ -65,7 +60,7 @@ void PacketPeerUDP::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("wait:Error"),&PacketPeerUDP::wait);
 	ObjectTypeDB::bind_method(_MD("is_listening"),&PacketPeerUDP::is_listening);
 	ObjectTypeDB::bind_method(_MD("get_packet_ip"),&PacketPeerUDP::_get_packet_ip);
-	ObjectTypeDB::bind_method(_MD("get_packet_address"),&PacketPeerUDP::_get_packet_address);
+	//ObjectTypeDB::bind_method(_MD("get_packet_address"),&PacketPeerUDP::_get_packet_address);
 	ObjectTypeDB::bind_method(_MD("get_packet_port"),&PacketPeerUDP::get_packet_port);
 	ObjectTypeDB::bind_method(_MD("set_send_address","host","port"),&PacketPeerUDP::_set_send_address);
 

+ 1 - 2
core/io/packet_peer_udp.h

@@ -40,14 +40,13 @@ protected:
 	static PacketPeerUDP* (*_create)();
 	static void _bind_methods();
 
-	int _get_packet_address() const;
 	String _get_packet_ip() const;
 
 	virtual Error _set_send_address(const String& p_address,int p_port);
 
 public:
 
-	virtual Error listen(int p_port,int p_recv_buffer_size=65536)=0;
+	virtual Error listen(int p_port, IP_Address::AddrType p_address_type = IP_Address::TYPE_IPV4, int p_recv_buffer_size=65536)=0;
 	virtual void close()=0;
 	virtual Error wait()=0;
 	virtual bool is_listening() const=0;

+ 5 - 3
core/io/tcp_server.cpp

@@ -30,6 +30,8 @@
 
 TCP_Server* (*TCP_Server::_create)()=NULL;
 
+VARIANT_ENUM_CAST(IP_Address::AddrType);
+
 Ref<TCP_Server> TCP_Server::create_ref() {
 
 	if (!_create)
@@ -44,19 +46,19 @@ TCP_Server* TCP_Server::create() {
 	return _create();
 }
 
-Error TCP_Server::_listen(uint16_t p_port,DVector<String> p_accepted_hosts) {
+Error TCP_Server::_listen(uint16_t p_port, IP_Address::AddrType p_type, DVector<String> p_accepted_hosts) {
 
 	List<String> hosts;
 	for(int i=0;i<p_accepted_hosts.size();i++)
 		hosts.push_back(p_accepted_hosts.get(i));
 
-	return listen(p_port,hosts.size()?&hosts:NULL);
+	return listen(p_port,p_type, hosts.size()?&hosts:NULL);
 
 }
 
 void TCP_Server::_bind_methods() {
 
-	ObjectTypeDB::bind_method(_MD("listen","port","accepted_hosts"),&TCP_Server::_listen,DEFVAL(DVector<String>()));
+	ObjectTypeDB::bind_method(_MD("listen","port","accepted_hosts"),&TCP_Server::_listen,DEFVAL(IP_Address::TYPE_IPV4), DEFVAL(DVector<String>()));
 	ObjectTypeDB::bind_method(_MD("is_connection_available"),&TCP_Server::is_connection_available);
 	ObjectTypeDB::bind_method(_MD("take_connection"),&TCP_Server::take_connection);
 	ObjectTypeDB::bind_method(_MD("stop"),&TCP_Server::stop);

+ 2 - 2
core/io/tcp_server.h

@@ -41,11 +41,11 @@ protected:
 	static TCP_Server* (*_create)();
 
 	//bind helper
-	Error _listen(uint16_t p_port,DVector<String> p_accepted_hosts=DVector<String>());
+	Error _listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4 ,DVector<String> p_accepted_hosts=DVector<String>());
 	static void _bind_methods();
 public:
 
-	virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL)=0;
+	virtual Error listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4, const List<String> *p_accepted_hosts=NULL)=0;
 	virtual bool is_connection_available() const=0;
 	virtual Ref<StreamPeerTCP> take_connection()=0;
 

+ 71 - 19
core/ustring.cpp

@@ -1543,11 +1543,11 @@ String::String(const StrRange& p_range) {
 	copy_from(p_range.c_str,p_range.len);
 }
 
-int String::hex_to_int() const {
+int String::hex_to_int(bool p_with_prefix) const {
 
     int l = length();
-    if (l<3)
-           return 0;
+	if (p_with_prefix && l<3)
+		return 0;
 
     const CharType *s=ptr();
 
@@ -1556,15 +1556,16 @@ int String::hex_to_int() const {
     if (sign<0) {
         s++;
         l--;
-        if (l<2)
+		if (p_with_prefix && l<2)
             return 0;
     }
 
-    if (s[0]!='0' || s[1]!='x')
-           return 0;
-
-    s+=2;
-    l-=2;
+	if (p_with_prefix) {
+		if (s[0]!='0' || s[1]!='x')
+			return 0;
+		s+=2;
+		l-=2;
+	};
 
     int hex=0;
 
@@ -3510,6 +3511,36 @@ bool String::is_valid_integer() const {
 
 }
 
+bool String::is_valid_hex_number(bool p_with_prefix) const {
+
+	int from = 0;
+	int len = length();
+
+	if (len!=1 && (operator[](0)=='+' || operator[](0)=='-'))
+		from++;
+
+	if (p_with_prefix) {
+
+		if (len < 2)
+			return false;
+		if (operator[](from) != '0' || operator[](from+1) != 'x') {
+			return false;
+		};
+		from += 2;
+	};
+
+	for (int i=from; i<len; i++) {
+
+		CharType c = operator[](i);
+		if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
+			continue;
+		return false;
+	};
+
+	return true;
+};
+
+
 bool String::is_valid_float() const {
 
 	int len = length();
@@ -3646,20 +3677,41 @@ bool String::is_valid_html_color() const {
 
 }
 
+
 bool String::is_valid_ip_address() const {
 
-	Vector<String> ip = split(".");
-	if (ip.size()!=4)
-		return false;
-	for(int i=0;i<ip.size();i++) {
+	if (find(":") >= 0) {
 
-		String n = ip[i];
-		if (!n.is_valid_integer())
-			return false;
-		int val = n.to_int();
-		if (val<0 || val>255)
+		Vector<String> ip = split(":");
+		for (int i=0; i<ip.size(); i++) {
+
+			String n = ip[i];
+			if (n.empty())
+				continue;
+			if (n.is_valid_hex_number(false)) {
+				int nint = n.hex_to_int(false);
+				if (nint < 0 || nint > 0xffff)
+					return false;
+				continue;
+			};
+			if (!n.is_valid_ip_address())
+				return false;
+		};
+
+	} else {
+		Vector<String> ip = split(".");
+		if (ip.size()!=4)
 			return false;
-	}
+		for(int i=0;i<ip.size();i++) {
+
+			String n = ip[i];
+			if (!n.is_valid_integer())
+				return false;
+			int val = n.to_int();
+			if (val<0 || val>255)
+				return false;
+		}
+	};
 
 	return true;
 }

+ 2 - 1
core/ustring.h

@@ -144,7 +144,7 @@ public:
 	bool is_numeric() const;
 	double to_double() const;
 	float to_float() const;
-	int hex_to_int() const;
+	int hex_to_int(bool p_with_prefix = true) const;
 	int to_int() const;
 
 	int64_t to_int64() const;
@@ -226,6 +226,7 @@ public:
 	bool is_valid_identifier() const;
 	bool is_valid_integer() const;
 	bool is_valid_float() const;
+	bool is_valid_hex_number(bool p_with_prefix) const;
 	bool is_valid_html_color() const;
 	bool is_valid_ip_address() const;
 

+ 5 - 0
core/variant_call.cpp

@@ -1820,6 +1820,11 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl
 	_VariantCall::add_constant(Variant::IMAGE,"INTERPOLATE_NEAREST",Image::INTERPOLATE_NEAREST);
 	_VariantCall::add_constant(Variant::IMAGE,"INTERPOLATE_BILINEAR",Image::INTERPOLATE_BILINEAR);
 	_VariantCall::add_constant(Variant::IMAGE,"INTERPOLATE_CUBIC",Image::INTERPOLATE_CUBIC);
+
+	_VariantCall::add_constant(Variant::INT, "IP_TYPE_NONE", IP_Address::TYPE_NONE);
+	_VariantCall::add_constant(Variant::INT, "IP_TYPE_IPV4", IP_Address::TYPE_IPV4);
+	_VariantCall::add_constant(Variant::INT, "IP_TYPE_IPV6", IP_Address::TYPE_IPV6);
+	_VariantCall::add_constant(Variant::INT, "IP_TYPE_ANY", IP_Address::TYPE_ANY);
 }
 
 void unregister_variant_methods() {

+ 61 - 28
drivers/unix/ip_unix.cpp

@@ -30,6 +30,7 @@
 
 #if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
 
+#include <string.h>
 
 #ifdef WINDOWS_ENABLED
  #ifdef WINRT_ENABLED
@@ -62,16 +63,51 @@
  #endif
 #endif
 
-IP_Address IP_Unix::_resolve_hostname(const String& p_hostname) {
+static IP_Address _sockaddr2ip(struct sockaddr* p_addr) {
 
-	struct hostent *he;
-	if ((he=gethostbyname(p_hostname.utf8().get_data())) == NULL) {  // get the host info
-		ERR_PRINT("gethostbyname failed!");
-		return IP_Address();
-	}
 	IP_Address ip;
+	if (p_addr->sa_family == AF_INET) {
+		struct sockaddr_in* addr = (struct sockaddr_in*)p_addr;
+		ip.field32[0] = *((unsigned long*)&addr->sin_addr);
+		ip.type = IP_Address::TYPE_IPV4;
+	} else {
+		struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+		for (int i=0; i<16; i++)
+			ip.field8[i] = addr6->sin6_addr.s6_addr[i];
+		ip.type = IP_Address::TYPE_IPV6;
+	};
+
+	return ip;
+};
+
+IP_Address IP_Unix::_resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type) {
 
-	ip.host= *((unsigned long*)he->h_addr);
+	struct addrinfo hints;
+	struct addrinfo* result;
+
+	memset(&hints, 0, sizeof(struct addrinfo));
+	if (p_type == IP_Address::TYPE_IPV4) {
+		hints.ai_family = AF_INET;
+	} else if (p_type == IP_Address::TYPE_IPV6) {
+		hints.ai_family = AF_INET6;
+	} else {
+		hints.ai_family = AF_UNSPEC;
+	};
+
+	int s = getaddrinfo(p_hostname.utf8().get_data(), NULL, &hints, &result);
+	if (s != 0) {
+		ERR_PRINT("getaddrinfo failed!");
+		return IP_Address();
+	};
+
+	if (result == NULL || result->ai_addr == NULL) {
+		ERR_PRINT("Invalid response from getaddrinfo");
+		return IP_Address();
+	};
+
+	IP_Address ip = _sockaddr2ip(result->ai_addr);
+
+	freeaddrinfo(result);
 
 	return ip;
 
@@ -90,10 +126,9 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
 
 	for (int i = 0; i < hostnames->Size; i++) {
 
-		if (hostnames->GetAt(i)->Type == HostNameType::Ipv4 && hostnames->GetAt(i)->IPInformation != nullptr) {
+		if (hostnames->GetAt(i)->Type == HostNameType::Ipv4 || hostnames->GetAt(i)->Type == HostNameType::Ipv6 && hostnames->GetAt(i)->IPInformation != nullptr) {
 
 			r_addresses->push_back(IP_Address(String(hostnames->GetAt(i)->CanonicalName->Data())));
-
 		}
 	}
 
@@ -108,7 +143,7 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
 	while (true) {
 
 		addrs = (IP_ADAPTER_ADDRESSES*)memalloc(buf_size);
-		int err = GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_ANYCAST |
+		int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST |
 									   GAA_FLAG_SKIP_MULTICAST |
 									   GAA_FLAG_SKIP_DNS_SERVER |
 									   GAA_FLAG_SKIP_FRIENDLY_NAME,
@@ -134,14 +169,23 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
 		IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
 		while (address != NULL) {
 
-			char addr_chr[INET_ADDRSTRLEN];
-			SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
-
 			IP_Address ip;
-			ip.host= *((unsigned long*)&ipv4->sin_addr);
 
+			if (address->Address.lpSockaddr->sa_family == AF_INET) {
+
+				SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
+
+				ip.field32[0] = *((unsigned long*)&ipv4->sin_addr);
+				ip.type = IP_Address::TYPE_IPV4;
+			} else { // ipv6
+
+				SOCKADDR_IN6* ipv6 = reinterpret_cast<SOCKADDR_IN6*>(address->Address.lpSockaddr);
+				for (int i=0; i<16; i++) {
+					ip.field8[i] = ipv6->sin6_addr.s6_addr[i];
+				};
+				ip.type = IP_Address::TYPE_IPV6;
+			};
 
-			//inet_ntop(AF_INET, &ipv4->sin_addr, addr_chr, INET_ADDRSTRLEN);
 
 			r_addresses->push_back(ip);
 
@@ -167,20 +211,9 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
 	for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
 		if (!ifa->ifa_addr)
 			continue;
-		if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4
-			// is a valid IP4 Address
 
-			IP_Address ip;
-			ip.host= *((unsigned long*)&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr);
-
-			r_addresses->push_back(ip);
-		}/* else if (ifa->ifa_addr->sa_family==AF_INET6) { // check it is IP6
-			// is a valid IP6 Address
-			tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
-			char addressBuffer[INET6_ADDRSTRLEN];
-			inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
-			printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
-		} */
+		IP_Address ip = _sockaddr2ip(ifa->ifa_addr);
+		r_addresses->push_back(ip);
 	}
 
 	if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);

+ 1 - 1
drivers/unix/ip_unix.h

@@ -36,7 +36,7 @@
 class IP_Unix : public IP {
 	OBJ_TYPE(IP_Unix, IP);
 
-	virtual IP_Address _resolve_hostname(const String& p_hostname);
+	virtual IP_Address _resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type);
 
 	static IP* _create_unix();
 public:

+ 59 - 21
drivers/unix/packet_peer_udp_posix.cpp

@@ -54,6 +54,7 @@
 #include <arpa/inet.h>
 #endif
 
+#include "drivers/unix/socket_helpers.h"
 
 int PacketPeerUDPPosix::get_available_packet_count() const {
 
@@ -73,7 +74,15 @@ Error PacketPeerUDPPosix::get_packet(const uint8_t **r_buffer,int &r_buffer_size
 		return ERR_UNAVAILABLE;
 
 	uint32_t size;
-	rb.read((uint8_t*)&packet_ip.host,4,true);
+	uint8_t type;
+	rb.read(&type, 1, true);
+	if (type == IP_Address::TYPE_IPV4) {
+		rb.read((uint8_t*)&packet_ip.field8,4,true);
+		packet_ip.type = IP_Address::TYPE_IPV4;
+	} else {
+		rb.read((uint8_t*)&packet_ip.field8,16,true);
+		packet_ip.type = IP_Address::TYPE_IPV6;
+	};
 	rb.read((uint8_t*)&packet_port,4,true);
 	rb.read((uint8_t*)&size,4,true);
 	rb.read(packet_buffer,size,true);
@@ -85,17 +94,17 @@ Error PacketPeerUDPPosix::get_packet(const uint8_t **r_buffer,int &r_buffer_size
 }
 Error PacketPeerUDPPosix::put_packet(const uint8_t *p_buffer,int p_buffer_size){
 
-	int sock = _get_socket();
+	ERR_FAIL_COND_V(peer_addr.type == IP_Address::TYPE_NONE, ERR_UNCONFIGURED);
+
+	int sock = _get_socket(peer_addr.type);
 	ERR_FAIL_COND_V( sock == -1, FAILED );
-	struct sockaddr_in addr;
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(peer_port);
-	addr.sin_addr = *((struct in_addr*)&peer_addr.host);
+	struct sockaddr_storage addr;
+	size_t addr_size = _set_sockaddr(&addr, peer_addr, peer_port);
 
 	errno = 0;
 	int err;
 
-	while ( (err = sendto(sock, p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, sizeof(addr))) != p_buffer_size) {
+	while ( (err = sendto(sock, p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, addr_size)) != p_buffer_size) {
 
 		if (errno != EAGAIN) {
 			return FAILED;
@@ -110,17 +119,19 @@ int PacketPeerUDPPosix::get_max_packet_size() const{
 	return 512; // uhm maybe not
 }
 
-Error PacketPeerUDPPosix::listen(int p_port, int p_recv_buffer_size){
+Error PacketPeerUDPPosix::listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size) {
+
+	ERR_FAIL_COND_V(p_address_type != IP_Address::TYPE_IPV4 && p_address_type != IP_Address::TYPE_IPV6, ERR_INVALID_PARAMETER);
 
 	close();
-	int sock = _get_socket();
+	int sock = _get_socket(p_address_type);
 	if (sock == -1 )
 		return ERR_CANT_CREATE;
-	sockaddr_in addr = {0};
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(p_port);
-	addr.sin_addr.s_addr = INADDR_ANY;
-	if (bind(sock, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == -1 ) {
+
+	sockaddr_storage addr = {0};
+	size_t addr_size = _set_listen_sockaddr(&addr, p_port, p_address_type, NULL);
+
+	if (bind(sock, (struct sockaddr*)&addr, addr_size) == -1 ) {
 		close();
 		return ERR_UNAVAILABLE;
 	}
@@ -145,16 +156,41 @@ Error PacketPeerUDPPosix::wait() {
 
 Error PacketPeerUDPPosix::_poll(bool p_wait) {
 
-	struct sockaddr_in from = {0};
-	socklen_t len = sizeof(struct sockaddr_in);
+	struct sockaddr_storage from = {0};
+	socklen_t len = sizeof(struct sockaddr_storage);
 	int ret;
 	while ( (ret = recvfrom(sockfd, recv_buffer, MIN((int)sizeof(recv_buffer),MAX(rb.space_left()-12, 0)), p_wait?0:MSG_DONTWAIT, (struct sockaddr*)&from, &len)) > 0) {
-		rb.write((uint8_t*)&from.sin_addr, 4);
-		uint32_t port = ntohs(from.sin_port);
+
+		uint32_t port = 0;
+
+		if (from.ss_family == AF_INET) {
+			uint8_t type = (uint8_t)IP_Address::TYPE_IPV4;
+			rb.write(&type, 1);
+			struct sockaddr_in* sin_from = (struct sockaddr_in*)&from;
+			rb.write((uint8_t*)&sin_from->sin_addr, 4);
+			port = sin_from->sin_port;
+
+		} else if (from.ss_family == AF_INET6) {
+
+			uint8_t type = (uint8_t)IP_Address::TYPE_IPV6;
+			rb.write(&type, 1);
+
+			struct sockaddr_in6* s6_from = (struct sockaddr_in6*)&from;
+			rb.write((uint8_t*)&s6_from->sin6_addr, 16);
+
+			port = s6_from->sin6_port;
+
+		} else {
+			// WARN_PRINT("Ignoring packet with unknown address family");
+			uint8_t type = (uint8_t)IP_Address::TYPE_NONE;
+			rb.write(&type, 1);
+		};
+
 		rb.write((uint8_t*)&port, 4);
 		rb.write((uint8_t*)&ret, 4);
 		rb.write(recv_buffer, ret);
-		len = sizeof(struct sockaddr_in);
+
+		len = sizeof(struct sockaddr_storage);
 		++queue_count;
 	};
 
@@ -182,12 +218,14 @@ int PacketPeerUDPPosix::get_packet_port() const{
 	return packet_port;
 }
 
-int PacketPeerUDPPosix::_get_socket() {
+int PacketPeerUDPPosix::_get_socket(IP_Address::AddrType p_type) {
 
 	if (sockfd != -1)
 		return sockfd;
 
-	sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	int family = p_type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+
+	sockfd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
 	ERR_FAIL_COND_V( sockfd == -1, -1 );
 	//fcntl(sockfd, F_SETFL, O_NONBLOCK);
 

+ 4 - 4
drivers/unix/packet_peer_udp_posix.h

@@ -44,15 +44,15 @@ class PacketPeerUDPPosix : public PacketPeerUDP {
 	mutable RingBuffer<uint8_t> rb;
 	uint8_t recv_buffer[PACKET_BUFFER_SIZE];
 	mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE];
-	IP_Address packet_ip;
-	int packet_port;
+	mutable IP_Address packet_ip;
+	mutable int packet_port;
 	mutable int queue_count;
 	int sockfd;
 
 	IP_Address peer_addr;
 	int peer_port;
 
-	_FORCE_INLINE_ int _get_socket();
+	_FORCE_INLINE_ int _get_socket(IP_Address::AddrType p_type);
 
 	static PacketPeerUDP* _create();
 	virtual Error _poll(bool p_block);
@@ -65,7 +65,7 @@ public:
 
 	virtual int get_max_packet_size() const;
 
-	virtual Error listen(int p_port,int p_recv_buffer_size=65536);
+	virtual Error listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size=65536);
 	virtual void close();
 	virtual Error wait();
 	virtual bool is_listening() const;

+ 70 - 0
drivers/unix/socket_helpers.h

@@ -0,0 +1,70 @@
+#ifndef SOCKET_HELPERS_H
+#define SOCKET_HELPERS_H
+
+#include <string.h>
+
+// helpers for sockaddr -> IP_Address and back, should work for posix and winsock. All implementations should use this
+
+static size_t _set_sockaddr(struct sockaddr_storage* p_addr, const IP_Address& p_ip, int p_port) {
+
+	memset(p_addr, 0, sizeof(struct sockaddr_storage));
+	if (p_ip.type == IP_Address::TYPE_IPV6) {
+
+		struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+		addr6->sin6_family = AF_INET6;
+		addr6->sin6_port = htons(p_port);
+		copymem(&addr6->sin6_addr.s6_addr, p_ip.field8, 16);
+		return sizeof(sockaddr_in6);
+
+	} else {
+
+		struct sockaddr_in* addr4 = (struct sockaddr_in*)p_addr;
+		addr4->sin_family = AF_INET;    // host byte order
+		addr4->sin_port = htons(p_port);  // short, network byte order
+		addr4->sin_addr = *((struct in_addr*)&p_ip.field32[0]);
+		return sizeof(sockaddr_in);
+	};
+};
+
+static size_t _set_listen_sockaddr(struct sockaddr_storage* p_addr, int p_port, IP_Address::AddrType p_address_type, const List<String> *p_accepted_hosts) {
+
+	memset(p_addr, 0, sizeof(struct sockaddr_storage));
+	if (p_address_type == IP_Address::TYPE_IPV4) {
+		struct sockaddr_in* addr4 = (struct sockaddr_in*)p_addr;
+		addr4->sin_family = AF_INET;
+		addr4->sin_port = htons(p_port);
+		addr4->sin_addr.s_addr = INADDR_ANY; // TODO: use accepted hosts list
+		return sizeof(sockaddr_in);
+	} else {
+		struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+
+		addr6->sin6_family = AF_INET6;
+		addr6->sin6_port = htons(p_port);
+		addr6->sin6_addr = in6addr_any; // TODO: use accepted hosts list
+		return sizeof(sockaddr_in6);
+	};
+};
+
+static void _set_ip_addr_port(IP_Address& r_ip, int& r_port, struct sockaddr_storage* p_addr) {
+
+	if (p_addr->ss_family == AF_INET) {
+		r_ip.type = IP_Address::TYPE_IPV4;
+
+		struct sockaddr_in* addr4 = (struct sockaddr_in*)p_addr;
+		r_ip.field32[0] = (uint32_t)addr4->sin_addr.s_addr;
+
+		r_port = ntohs(addr4->sin_port);
+
+	} else if (p_addr->ss_family == AF_INET6) {
+
+		r_ip.type = IP_Address::TYPE_IPV6;
+
+		struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+		copymem(&addr6->sin6_addr.s6_addr, r_ip.field8, 16);
+
+		r_port = ntohs(addr6->sin6_port);
+	};
+};
+
+
+#endif

+ 11 - 15
drivers/unix/stream_peer_tcp_posix.cpp

@@ -61,13 +61,7 @@
 	#define MSG_NOSIGNAL    SO_NOSIGPIPE
 #endif
 
-static void set_addr_in(struct sockaddr_in& their_addr, const IP_Address& p_host, uint16_t p_port) {
-
-	their_addr.sin_family = AF_INET;    // host byte order
-	their_addr.sin_port = htons(p_port);  // short, network byte order
-	their_addr.sin_addr = *((struct in_addr*)&p_host.host);
-	memset(&(their_addr.sin_zero), '\0', 8);
-};
+#include "drivers/unix/socket_helpers.h"
 
 StreamPeerTCP* StreamPeerTCPPosix::_create() {
 
@@ -103,9 +97,10 @@ Error StreamPeerTCPPosix::_poll_connection(bool p_block) const {
 		_block(sockfd, false, true);
 	};
 
-	struct sockaddr_in their_addr;
-	set_addr_in(their_addr, peer_host, peer_port);
-	if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1) {
+	struct sockaddr_storage their_addr;
+	size_t addr_size = _set_sockaddr(&their_addr, peer_host, peer_port);
+
+	if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == -1) {
 
 		if (errno == EISCONN) {
 			status = STATUS_CONNECTED;
@@ -140,9 +135,10 @@ void StreamPeerTCPPosix::set_socket(int p_sockfd, IP_Address p_host, int p_port)
 
 Error StreamPeerTCPPosix::connect(const IP_Address& p_host, uint16_t p_port) {
 
-	ERR_FAIL_COND_V( p_host.host == 0, ERR_INVALID_PARAMETER);
+	ERR_FAIL_COND_V( p_host.type == IP_Address::TYPE_NONE, ERR_INVALID_PARAMETER);
 
-	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+	int family = p_host.type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+	if ((sockfd = socket(family, SOCK_STREAM, 0)) == -1) {
 		ERR_PRINT("Socket creation failed!");
 		disconnect();
 		//perror("socket");
@@ -156,11 +152,11 @@ Error StreamPeerTCPPosix::connect(const IP_Address& p_host, uint16_t p_port) {
 	ioctl(sockfd, FIONBIO, &bval);
 #endif
 
-	struct sockaddr_in their_addr;
-	set_addr_in(their_addr, p_host, p_port);
+	struct sockaddr_storage their_addr;
+	size_t addr_size = _set_sockaddr(&their_addr, p_host, p_port);
 
 	errno = 0;
-	if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1 && errno != EINPROGRESS) {
+	if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == -1 && errno != EINPROGRESS) {
 
 		ERR_PRINT("Connection to remote host failed!");
 		disconnect();

+ 19 - 13
drivers/unix/tcp_server_posix.cpp

@@ -55,6 +55,9 @@
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <assert.h>
+
+#include "drivers/unix/socket_helpers.h"
+
 TCP_Server* TCPServerPosix::_create() {
 
 	return memnew(TCPServerPosix);
@@ -65,10 +68,11 @@ void TCPServerPosix::make_default() {
 	TCP_Server::_create = TCPServerPosix::_create;
 };
 
-Error TCPServerPosix::listen(uint16_t p_port,const List<String> *p_accepted_hosts) {
+Error TCPServerPosix::listen(uint16_t p_port, IP_Address::AddrType p_type, const List<String> *p_accepted_hosts) {
 
 	int sockfd;
-	sockfd = socket(AF_INET, SOCK_STREAM, 0);
+	int family = p_type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+	sockfd = socket(family, SOCK_STREAM, 0);
 	ERR_FAIL_COND_V(sockfd == -1, FAILED);
 #ifndef NO_FCNTL
 	fcntl(sockfd, F_SETFL, O_NONBLOCK);
@@ -82,13 +86,12 @@ Error TCPServerPosix::listen(uint16_t p_port,const List<String> *p_accepted_host
 		WARN_PRINT("REUSEADDR failed!")
 	}
 
-	struct sockaddr_in my_addr;
-	my_addr.sin_family = AF_INET;         // host byte order
-	my_addr.sin_port = htons(p_port);     // short, network byte order
-	my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP TODO: use p_accepted_hosts
-	memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
+	struct sockaddr_storage addr;
+	size_t addr_size = _set_listen_sockaddr(&addr, p_port, p_type, p_accepted_hosts);
+
+	// automatically fill with my IP TODO: use p_accepted_hosts
 
-	if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) != -1) {
+	if (bind(sockfd, (struct sockaddr *)&addr, addr_size) != -1) {
 
 		if (::listen(sockfd, 1) == -1) {
 
@@ -136,9 +139,9 @@ Ref<StreamPeerTCP> TCPServerPosix::take_connection() {
 		return Ref<StreamPeerTCP>();
 	};
 
-	struct sockaddr_in their_addr;
-	socklen_t sin_size = sizeof(their_addr);
-	int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &sin_size);
+	struct sockaddr_storage their_addr;
+	socklen_t size = sizeof(their_addr);
+	int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &size);
 	ERR_FAIL_COND_V(fd == -1, Ref<StreamPeerTCP>());
 #ifndef NO_FCNTL
 	fcntl(fd, F_SETFL, O_NONBLOCK);
@@ -149,8 +152,11 @@ Ref<StreamPeerTCP> TCPServerPosix::take_connection() {
 
 	Ref<StreamPeerTCPPosix> conn = memnew(StreamPeerTCPPosix);
 	IP_Address ip;
-	ip.host = (uint32_t)their_addr.sin_addr.s_addr;
-	conn->set_socket(fd, ip, ntohs(their_addr.sin_port));
+
+	int port;
+	_set_ip_addr_port(ip, port, &their_addr);
+
+	conn->set_socket(fd, ip, port);
 
 	return conn;
 };

+ 1 - 1
drivers/unix/tcp_server_posix.h

@@ -40,7 +40,7 @@ class TCPServerPosix : public TCP_Server {
 
 public:
 
-	virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL);
+	virtual Error listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4, const List<String> *p_accepted_hosts=NULL);
 	virtual bool is_connection_available() const;
 	virtual Ref<StreamPeerTCP> take_connection();
 

+ 6 - 3
modules/enet/networked_multiplayer_enet.cpp

@@ -77,6 +77,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
 Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port, int p_in_bandwidth, int p_out_bandwidth){
 
 	ERR_FAIL_COND_V(active,ERR_ALREADY_IN_USE);
+	ERR_FAIL_COND_V(p_ip.type != IP_Address::TYPE_IPV4, ERR_INVALID_PARAMETER);
 
 	host = enet_host_create (NULL /* create a client host */,
 		    1 /* only allow 1 outgoing connection */,
@@ -90,7 +91,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port
 	_setup_compressor();
 
 	ENetAddress address;
-	address.host=p_ip.host;
+	address.host=p_ip.field32[0];
 	address.port=p_port;
 
 	//enet_address_set_host (& address, "localhost");
@@ -149,7 +150,8 @@ void NetworkedMultiplayerENet::poll(){
 				}
 
 				IP_Address ip;
-				ip.host=event.peer -> address.host;
+				ip.type = IP_Address::TYPE_IPV4;
+				ip.field32[0]=event.peer -> address.host;
 
 				int *new_id = memnew( int );
 				*new_id = event.data;
@@ -683,5 +685,6 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet(){
 // sets IP for ENet to bind when using create_server
 // if no IP is set, then ENet bind to ENET_HOST_ANY
 void NetworkedMultiplayerENet::set_bind_ip(const IP_Address& p_ip){
-	bind_ip=p_ip.host;
+	ERR_FAIL_COND(p_ip.type != IP_Address::TYPE_IPV4);
+	bind_ip=p_ip.field32[0];
 }

+ 56 - 22
platform/windows/packet_peer_udp_winsock.cpp

@@ -29,6 +29,9 @@
 #include "packet_peer_udp_winsock.h"
 
 #include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include "drivers/unix/socket_helpers.h"
 
 int PacketPeerUDPWinsock::get_available_packet_count() const {
 
@@ -48,7 +51,15 @@ Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer,int &r_buffer_si
 		return ERR_UNAVAILABLE;
 
 	uint32_t size;
-	rb.read((uint8_t*)&packet_ip.host,4,true);
+	uint8_t type;
+	rb.read(&type, 1, true);
+	if (type == IP_Address::TYPE_IPV4) {
+		rb.read((uint8_t*)&packet_ip.field8,4,true);
+		packet_ip.type = IP_Address::TYPE_IPV4;
+	} else {
+		rb.read((uint8_t*)&packet_ip.field8,16,true);
+		packet_ip.type = IP_Address::TYPE_IPV6;
+	};
 	rb.read((uint8_t*)&packet_port,4,true);
 	rb.read((uint8_t*)&size,4,true);
 	rb.read(packet_buffer,size,true);
@@ -60,19 +71,16 @@ Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer,int &r_buffer_si
 }
 Error PacketPeerUDPWinsock::put_packet(const uint8_t *p_buffer,int p_buffer_size){
 
-	int sock = _get_socket();
+	int sock = _get_socket(peer_addr.type);
 	ERR_FAIL_COND_V( sock == -1, FAILED );
-	struct sockaddr_in addr;
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(peer_port);
-	addr.sin_addr = *((struct in_addr*)&peer_addr.host);
-
+	struct sockaddr_storage addr;
+	size_t addr_size = _set_sockaddr(&addr, peer_addr, peer_port);
 
 	_set_blocking(true);
 
 	errno = 0;
 	int err;
-	while ( (err = sendto(sock, (const char*)p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, sizeof(addr))) != p_buffer_size) {
+	while ( (err = sendto(sock, (const char*)p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, addr_size)) != p_buffer_size) {
 
 		if (WSAGetLastError() != WSAEWOULDBLOCK) {
 			return FAILED;
@@ -104,17 +112,17 @@ void PacketPeerUDPWinsock::_set_blocking(bool p_blocking) {
 	};
 }
 
-Error PacketPeerUDPWinsock::listen(int p_port, int p_recv_buffer_size){
+Error PacketPeerUDPWinsock::listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size) {
 
 	close();
-	int sock = _get_socket();
+	int sock = _get_socket(p_address_type);
 	if (sock == -1 )
 		return ERR_CANT_CREATE;
-	sockaddr_in addr = {0};
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(p_port);
-	addr.sin_addr.s_addr = INADDR_ANY;
-	if (bind(sock, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == -1 ) {
+
+	struct sockaddr_storage addr = {0};
+	size_t addr_size = _set_listen_sockaddr(&addr, p_port, p_address_type, NULL);
+
+	if (bind(sock, (struct sockaddr*)&addr, addr_size) == -1 ) {
 		close();
 		return ERR_UNAVAILABLE;
 	}
@@ -146,17 +154,41 @@ Error PacketPeerUDPWinsock::_poll(bool p_wait) {
 	_set_blocking(p_wait);
 
 
-	struct sockaddr_in from = {0};
-	int len = sizeof(struct sockaddr_in);
+	struct sockaddr_storage from = {0};
+	int len = sizeof(struct sockaddr_storage);
 	int ret;
 	while ( (ret = recvfrom(sockfd, (char*)recv_buffer, MIN((int)sizeof(recv_buffer),MAX(rb.space_left()-12, 0)), 0, (struct sockaddr*)&from, &len)) > 0) {
-		rb.write((uint8_t*)&from.sin_addr, 4);
-		uint32_t port = ntohs(from.sin_port);
+
+		uint32_t port = 0;
+
+		if (from.ss_family == AF_INET) {
+			uint8_t type = (uint8_t)IP_Address::TYPE_IPV4;
+			rb.write(&type, 1);
+			struct sockaddr_in* sin_from = (struct sockaddr_in*)&from;
+			rb.write((uint8_t*)&sin_from->sin_addr, 4);
+			port = sin_from->sin_port;
+
+		} else if (from.ss_family == AF_INET6) {
+
+			uint8_t type = (uint8_t)IP_Address::TYPE_IPV6;
+			rb.write(&type, 1);
+
+			struct sockaddr_in6* s6_from = (struct sockaddr_in6*)&from;
+			rb.write((uint8_t*)&s6_from->sin6_addr, 16);
+
+			port = s6_from->sin6_port;
+
+		} else {
+			// WARN_PRINT("Ignoring packet with unknown address family");
+			uint8_t type = (uint8_t)IP_Address::TYPE_NONE;
+			rb.write(&type, 1);
+		};
+
 		rb.write((uint8_t*)&port, 4);
 		rb.write((uint8_t*)&ret, 4);
 		rb.write(recv_buffer, ret);
 
-		len = sizeof(struct sockaddr_in);
+		len = sizeof(struct sockaddr_storage);
 		++queue_count;
 	};
 
@@ -202,12 +234,14 @@ int PacketPeerUDPWinsock::get_packet_port() const{
 	return packet_port;
 }
 
-int PacketPeerUDPWinsock::_get_socket() {
+int PacketPeerUDPWinsock::_get_socket(IP_Address::AddrType p_type) {
 
 	if (sockfd != -1)
 		return sockfd;
 
-	sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	int family = p_type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+
+	sockfd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
 	ERR_FAIL_COND_V( sockfd == -1, -1 );
 	//fcntl(sockfd, F_SETFL, O_NONBLOCK);
 

+ 4 - 4
platform/windows/packet_peer_udp_winsock.h

@@ -42,15 +42,15 @@ class PacketPeerUDPWinsock : public PacketPeerUDP {
 	mutable RingBuffer<uint8_t> rb;
 	uint8_t recv_buffer[PACKET_BUFFER_SIZE];
 	mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE];
-	IP_Address packet_ip;
-	int packet_port;
+	mutable IP_Address packet_ip;
+	mutable int packet_port;
 	mutable int queue_count;
 	int sockfd;
 
 	IP_Address peer_addr;
 	int peer_port;
 
-	_FORCE_INLINE_ int _get_socket();
+	_FORCE_INLINE_ int _get_socket(IP_Address::AddrType p_type);
 
 	static PacketPeerUDP* _create();
 
@@ -67,7 +67,7 @@ public:
 
 	virtual int get_max_packet_size() const;
 
-	virtual Error listen(int p_port,int p_recv_buffer_size=65536);
+	virtual Error listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size=65536);
 	virtual void close();
 	virtual Error wait();
 	virtual bool is_listening() const;

+ 12 - 16
platform/windows/stream_peer_winsock.cpp

@@ -31,16 +31,11 @@
 #include "stream_peer_winsock.h"
 
 #include <winsock2.h>
+#include <ws2tcpip.h>
 
-int winsock_refcount = 0;
-
-static void set_addr_in(struct sockaddr_in& their_addr, const IP_Address& p_host, uint16_t p_port) {
+#include "drivers/unix/socket_helpers.h"
 
-	their_addr.sin_family = AF_INET;    // host byte order
-	their_addr.sin_port = htons(p_port);  // short, network byte order
-	their_addr.sin_addr = *((struct in_addr*)&p_host.host);
-	memset(&(their_addr.sin_zero), '\0', 8);
-};
+int winsock_refcount = 0;
 
 StreamPeerTCP* StreamPeerWinsock::_create() {
 
@@ -92,10 +87,10 @@ Error StreamPeerWinsock::_poll_connection(bool p_block) const {
 		_block(sockfd, false, true);
 	};
 
-	struct sockaddr_in their_addr;
-	set_addr_in(their_addr, peer_host, peer_port);
+	struct sockaddr_storage their_addr;
+	size_t addr_size = _set_sockaddr(&their_addr, peer_host, peer_port);
 
-	if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) {
+	if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == SOCKET_ERROR) {
 
 		int err = WSAGetLastError();
 		if (err == WSAEISCONN) {
@@ -299,9 +294,10 @@ void StreamPeerWinsock::set_socket(int p_sockfd, IP_Address p_host, int p_port)
 
 Error StreamPeerWinsock::connect(const IP_Address& p_host, uint16_t p_port) {
 
-	ERR_FAIL_COND_V( p_host.host == 0, ERR_INVALID_PARAMETER);
+	ERR_FAIL_COND_V( p_host.type == IP_Address::TYPE_NONE, ERR_INVALID_PARAMETER);
 
-	if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
+	int family = p_host.type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+	if ((sockfd = socket(family, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
 		ERR_PRINT("Socket creation failed!");
 		disconnect();
 		//perror("socket");
@@ -315,10 +311,10 @@ Error StreamPeerWinsock::connect(const IP_Address& p_host, uint16_t p_port) {
 		return FAILED;
 	};
 
-	struct sockaddr_in their_addr;
-	set_addr_in(their_addr, p_host, p_port);
+	struct sockaddr_storage their_addr;
+	size_t addr_size = _set_sockaddr(&their_addr, p_host, p_port);
 
-	if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) {
+	if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == SOCKET_ERROR) {
 
 		if (WSAGetLastError() != WSAEWOULDBLOCK) {
 			ERR_PRINT("Connection to remote host failed!");

+ 11 - 10
platform/windows/tcp_server_winsock.cpp

@@ -31,6 +31,9 @@
 #include "stream_peer_winsock.h"
 
 #include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include "drivers/unix/socket_helpers.h"
 
 extern int winsock_refcount;
 
@@ -60,7 +63,7 @@ void TCPServerWinsock::cleanup() {
 };
 
 
-Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_hosts) {
+Error TCPServerWinsock::listen(uint16_t p_port, IP_Address::AddrType p_type,const List<String> *p_accepted_hosts) {
 
 	int sockfd;
 	sockfd = socket(AF_INET, SOCK_STREAM, 0);
@@ -73,11 +76,8 @@ Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_ho
 		return FAILED;
 	};
 
-	struct sockaddr_in my_addr;
-	my_addr.sin_family = AF_INET;         // host byte order
-	my_addr.sin_port = htons(p_port);     // short, network byte order
-	my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP TODO: use p_accepted_hosts
-	memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
+	struct sockaddr_storage my_addr;
+	size_t addr_size = _set_listen_sockaddr(&my_addr, p_port, p_type, p_accepted_hosts);
 
 	int reuse=1;
 	if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) {
@@ -86,7 +86,7 @@ Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_ho
 	}
 
 
-	if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) != SOCKET_ERROR) {
+	if (bind(sockfd, (struct sockaddr *)&my_addr, addr_size) != SOCKET_ERROR) {
 
 		if (::listen(sockfd, SOMAXCONN) == SOCKET_ERROR) {
 
@@ -140,16 +140,17 @@ Ref<StreamPeerTCP> TCPServerWinsock::take_connection() {
 		return NULL;
 	};
 
-	struct sockaddr_in their_addr;
+	struct sockaddr_storage their_addr;
 	int sin_size = sizeof(their_addr);
 	int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &sin_size);
 	ERR_FAIL_COND_V(fd == INVALID_SOCKET, NULL);
 
 	Ref<StreamPeerWinsock> conn = memnew(StreamPeerWinsock);
 	IP_Address ip;
-	ip.host = (uint32_t)their_addr.sin_addr.s_addr;
+	int port;
+	_set_ip_addr_port(ip, port, &their_addr);
 
-	conn->set_socket(fd, ip, ntohs(their_addr.sin_port));
+	conn->set_socket(fd, ip, port);
 
 	return conn;
 };

+ 1 - 1
platform/windows/tcp_server_winsock.h

@@ -39,7 +39,7 @@ class TCPServerWinsock : public TCP_Server {
 
 public:
 
-	virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL);
+	virtual Error listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4,const List<String> *p_accepted_hosts=NULL);
 	virtual bool is_connection_available() const;
 	virtual Ref<StreamPeerTCP> take_connection();