Browse Source

IP_Address now handle IPv4 and IPv6 transparently

IP_Address changes:
- Converts to and from String transparently while handling IPv4 as IPv6
  mapped (::ffff:[IP]) address internally.
- Completely remove AddrType enum.
- Setting/Getting of ip array is now only possible through dedicated functions
  (ie. set_ipv4, get_ipv4, set_ipv6, get_ipv6)
- Add function to know if the address is a valid IPv4 (for IP implementation and enet)

(cherry picked from commit 1aff508dd9713abf0db0d0436fa7f7c4788c5a4a)
Fabio Alessandrelli 8 years ago
parent
commit
00fdcf3cd0

+ 1 - 1
bin/tests/test_string.cpp

@@ -853,7 +853,7 @@ bool test_29() {
 	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);
+	IP_Address ip(0x0123, 0x4567, 0x89ab, 0xcdef, true);
 	OS::get_singleton()->print("ip6 is %ls\n", String(ip).c_str());
 
 	IP_Address ip2("fe80::52e5:49ff:fe93:1baf");

+ 6 - 3
core/io/ip.cpp

@@ -82,7 +82,7 @@ struct _IP_ResolverPrivate {
 				continue;
 			queue[i].response=IP::get_singleton()->resolve_hostname(queue[i].hostname, queue[i].type);
 
-			if (queue[i].response.type==IP_Address::TYPE_NONE)
+			if (queue[i].response==IP_Address())
 				queue[i].status=IP::RESOLVER_STATUS_ERROR;
 			else
 				queue[i].status=IP::RESOLVER_STATUS_DONE;
@@ -116,7 +116,8 @@ IP_Address IP::resolve_hostname(const String& p_hostname, IP::Type p_type) {
 	GLOBAL_LOCK_FUNCTION;
 
 	if (resolver->cache.has(p_hostname))
-		if (resolver->cache[p_hostname].type & p_type != 0)
+		if ((resolver->cache[p_hostname].is_ipv4() && p_type != IP::TYPE_IPV6) ||
+			(!resolver->cache[p_hostname].is_ipv4() && p_type != IP::TYPE_IPV4))
 			return resolver->cache[p_hostname];
 		// requested type is different from type in cache. continue resolution, if successful it'll overwrite cache
 
@@ -138,7 +139,9 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname, IP::Typ
 
 	resolver->queue[id].hostname=p_hostname;
 	resolver->queue[id].type = p_type;
-	if (resolver->cache.has(p_hostname) && (resolver->cache[p_hostname].type & p_type) != 0) {
+	if (resolver->cache.has(p_hostname) &&
+		((resolver->cache[p_hostname].is_ipv4() && p_type != IP::TYPE_IPV6) ||
+		(!resolver->cache[p_hostname].is_ipv4() && p_type != IP::TYPE_IPV4))) {
 		resolver->queue[id].response=resolver->cache[p_hostname];
 		resolver->queue[id].status=IP::RESOLVER_STATUS_DONE;
 	} else {

+ 50 - 32
core/io/ip_address.cpp

@@ -38,21 +38,18 @@ IP_Address::operator Variant() const {
 
 IP_Address::operator String() const {
 
-	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;
+	if(is_ipv4())
+		// IPv4 address mapped to IPv6
+		return itos(field8[12])+"."+itos(field8[13])+"."+itos(field8[14])+"."+itos(field8[15]);
+	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;
 }
 
 static void _parse_hex(const String& p_string, int p_start, uint8_t* p_dst) {
@@ -176,17 +173,41 @@ void IP_Address::clear() {
 	memset(&field8[0], 0, sizeof(field8));
 };
 
+bool IP_Address::is_ipv4() const{
+	return (field32[0]==0 && field32[1]==0 && field16[4]==0 && field16[5]==0xffff);
+}
+
+const uint8_t *IP_Address::get_ipv4() const{
+	ERR_FAIL_COND_V(!is_ipv4(),0);
+	return &(field8[12]);
+}
+
+void IP_Address::set_ipv4(const uint8_t *p_ip) {
+	clear();
+	field16[5]=0xffff;
+	field32[3]=*((const uint32_t *)p_ip);
+}
+
+const uint8_t *IP_Address::get_ipv6() const{
+	return field8;
+}
+
+void IP_Address::set_ipv6(const uint8_t *p_buf) {
+	clear();
+	for (int i=0; i<16; i++)
+		field8[i] = p_buf[i];
+}
+
 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;
+		// Mapped to IPv6
+		field16[5] = 0xffff;
+		_parse_ipv4(p_string, 0, &field8[12]);
 	};
 }
 
@@ -198,25 +219,22 @@ _FORCE_INLINE_ static void _32_to_buf(uint8_t* p_dst, uint32_t p_n) {
 	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) {
+IP_Address::IP_Address(uint32_t p_a,uint32_t p_b,uint32_t p_c,uint32_t p_d, bool is_v6) {
 
-	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) {
+	clear();
+	if (!is_v6) {
+		// Mapped to IPv6
+		field16[5]=0xffff;
+		field8[12]=p_a;
+		field8[13]=p_b;
+		field8[14]=p_c;
+		field8[15]=p_d;
+	} else {
 
 		_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();
-	};
+	}
 
 }

+ 9 - 12
core/io/ip_address.h

@@ -33,16 +33,7 @@
 
 struct IP_Address {
 
-public:
-	enum AddrType {
-		TYPE_NONE = 0,
-		TYPE_IPV4 = 1,
-		TYPE_IPV6 = 2,
-
-		TYPE_ANY = 3,
-	};
-
-	AddrType type;
+private:
 
 	union {
 		uint8_t field8[16];
@@ -70,11 +61,17 @@ public:
 	}
 
 	void clear();
+	bool is_ipv4() const;
+	const uint8_t *get_ipv4() const;
+	void set_ipv4(const uint8_t *p_ip);
+
+	const uint8_t *get_ipv6() const;
+	void set_ipv6(const uint8_t *buf);
 
 	operator String() const;
 	IP_Address(const String& p_string);
-	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; }
+	IP_Address(uint32_t p_a,uint32_t p_b,uint32_t p_c,uint32_t p_d, bool is_v6=false);
+	IP_Address() { clear(); }
 };
 
 

+ 5 - 11
drivers/unix/ip_unix.cpp

@@ -75,13 +75,10 @@ static IP_Address _sockaddr2ip(struct sockaddr* p_addr) {
 	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;
+		ip.set_ipv4((uint8_t *)&(addr->sin_addr));
 	} 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;
+		ip.set_ipv6(addr6->sin6_addr.s6_addr);
 	};
 
 	return ip;
@@ -189,15 +186,12 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
 
 				SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
 
-				ip.field32[0] = *((unsigned long*)&ipv4->sin_addr);
-				ip.type = IP_Address::TYPE_IPV4;
+				ip.set_ipv4((uint8_t *)&(ipv4->sin_addr));
 			} 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;
+
+				ip.set_ipv6(ipv6->sin6_addr.s6_addr);
 			};
 
 

+ 11 - 9
drivers/unix/packet_peer_udp_posix.cpp

@@ -76,12 +76,14 @@ Error PacketPeerUDPPosix::get_packet(const uint8_t **r_buffer,int &r_buffer_size
 	uint32_t size;
 	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;
+	if (type == IP::TYPE_IPV4) {
+		uint8_t ip[4];
+		rb.read(ip,4,true);
+		packet_ip.set_ipv4(ip);
 	} else {
-		rb.read((uint8_t*)&packet_ip.field8,16,true);
-		packet_ip.type = IP_Address::TYPE_IPV6;
+		uint8_t ipv6[16];
+		rb.read(ipv6,16,true);
+		packet_ip.set_ipv6(ipv6);
 	};
 	rb.read((uint8_t*)&packet_port,4,true);
 	rb.read((uint8_t*)&size,4,true);
@@ -94,7 +96,7 @@ 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){
 
-	ERR_FAIL_COND_V(peer_addr.type == IP_Address::TYPE_NONE, ERR_UNCONFIGURED);
+	ERR_FAIL_COND_V(peer_addr == IP_Address(), ERR_UNCONFIGURED);
 
 	int sock = _get_socket();
 	ERR_FAIL_COND_V( sock == -1, FAILED );
@@ -163,7 +165,7 @@ Error PacketPeerUDPPosix::_poll(bool p_wait) {
 		uint32_t port = 0;
 
 		if (from.ss_family == AF_INET) {
-			uint8_t type = (uint8_t)IP_Address::TYPE_IPV4;
+			uint8_t type = (uint8_t)IP::TYPE_IPV4;
 			rb.write(&type, 1);
 			struct sockaddr_in* sin_from = (struct sockaddr_in*)&from;
 			rb.write((uint8_t*)&sin_from->sin_addr, 4);
@@ -171,7 +173,7 @@ Error PacketPeerUDPPosix::_poll(bool p_wait) {
 
 		} else if (from.ss_family == AF_INET6) {
 
-			uint8_t type = (uint8_t)IP_Address::TYPE_IPV6;
+			uint8_t type = (uint8_t)IP::TYPE_IPV6;
 			rb.write(&type, 1);
 
 			struct sockaddr_in6* s6_from = (struct sockaddr_in6*)&from;
@@ -181,7 +183,7 @@ Error PacketPeerUDPPosix::_poll(bool p_wait) {
 
 		} else {
 			// WARN_PRINT("Ignoring packet with unknown address family");
-			uint8_t type = (uint8_t)IP_Address::TYPE_NONE;
+			uint8_t type = (uint8_t)IP::TYPE_NONE;
 			rb.write(&type, 1);
 		};
 

+ 13 - 17
drivers/unix/socket_helpers.h

@@ -16,31 +16,30 @@ static size_t _set_sockaddr(struct sockaddr_storage* p_addr, const IP_Address& p
 
 	memset(p_addr, 0, sizeof(struct sockaddr_storage));
 
-	// Dual stack (ANY) or matching ip type is required
-	ERR_FAIL_COND_V(p_sock_type != IP::TYPE_ANY && p_sock_type != p_ip.type,0);
+	ERR_FAIL_COND_V(p_ip==IP_Address(),0);
 
 	// IPv6 socket
 	if (p_sock_type == IP::TYPE_IPV6 || p_sock_type == IP::TYPE_ANY) {
 
+		// IPv6 only socket with IPv4 address
+		ERR_FAIL_COND_V(p_sock_type == IP::TYPE_IPV6 && p_ip.is_ipv4(),0);
+
 		struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
 		addr6->sin6_family = AF_INET6;
 		addr6->sin6_port = htons(p_port);
-		if(p_ip.type == IP_Address::TYPE_IPV4) {
-			// Remapping needed
-			uint16_t base[8] = {0x0, 0x0, 0x0, 0x0, 0x0, 0xffff, p_ip.field16[0], p_ip.field16[1]};
-			copymem(&addr6->sin6_addr.s6_addr, base, 16);
-
-		} else {
-			copymem(&addr6->sin6_addr.s6_addr, p_ip.field8, 16);
-		}
+		copymem(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16);
 		return sizeof(sockaddr_in6);
 
 	} else { // IPv4 socket
 
+		// IPv4 socket with IPv6 address
+		ERR_FAIL_COND_V(!p_ip.is_ipv4(),0);
+
+		uint32_t ipv4 = *((uint32_t *)p_ip.get_ipv4());
 		struct sockaddr_in* addr4 = (struct sockaddr_in*)p_addr;
-		addr4->sin_family = AF_INET;    // host byte order
+		addr4->sin_family = AF_INET;
 		addr4->sin_port = htons(p_port);  // short, network byte order
-		addr4->sin_addr = *((struct in_addr*)&p_ip.field32[0]);
+		copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 16);
 		return sizeof(sockaddr_in);
 	};
 };
@@ -88,19 +87,16 @@ static int _socket_create(IP::Type p_type, int type, int protocol) {
 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_ip.set_ipv4((uint8_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(&r_ip.field8, addr6->sin6_addr.s6_addr, 16);
+		r_ip.set_ipv6(addr6->sin6_addr.s6_addr);
 
 		r_port = ntohs(addr6->sin6_port);
 	};

+ 1 - 1
drivers/unix/stream_peer_tcp_posix.cpp

@@ -141,7 +141,7 @@ 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.type == IP_Address::TYPE_NONE, ERR_INVALID_PARAMETER);
+	ERR_FAIL_COND_V( p_host == IP_Address(), ERR_INVALID_PARAMETER);
 
 	sockfd = _socket_create(ip_type, SOCK_STREAM, IPPROTO_TCP);
 	if (sockfd == -1) {

+ 10 - 8
platform/windows/packet_peer_udp_winsock.cpp

@@ -53,12 +53,14 @@ Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer,int &r_buffer_si
 	uint32_t size;
 	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;
+	if (type == IP::TYPE_IPV4) {
+		uint8_t ip[4];
+		rb.read(ip,4,true);
+		packet_ip.set_ipv4(ip);
 	} else {
-		rb.read((uint8_t*)&packet_ip.field8,16,true);
-		packet_ip.type = IP_Address::TYPE_IPV6;
+		uint8_t ip[16];
+		rb.read(ip,16,true);
+		packet_ip.set_ipv6(ip);
 	};
 	rb.read((uint8_t*)&packet_port,4,true);
 	rb.read((uint8_t*)&size,4,true);
@@ -162,7 +164,7 @@ Error PacketPeerUDPWinsock::_poll(bool p_wait) {
 		uint32_t port = 0;
 
 		if (from.ss_family == AF_INET) {
-			uint8_t type = (uint8_t)IP_Address::TYPE_IPV4;
+			uint8_t type = (uint8_t)IP::TYPE_IPV4;
 			rb.write(&type, 1);
 			struct sockaddr_in* sin_from = (struct sockaddr_in*)&from;
 			rb.write((uint8_t*)&sin_from->sin_addr, 4);
@@ -170,7 +172,7 @@ Error PacketPeerUDPWinsock::_poll(bool p_wait) {
 
 		} else if (from.ss_family == AF_INET6) {
 
-			uint8_t type = (uint8_t)IP_Address::TYPE_IPV6;
+			uint8_t type = (uint8_t)IP::TYPE_IPV6;
 			rb.write(&type, 1);
 
 			struct sockaddr_in6* s6_from = (struct sockaddr_in6*)&from;
@@ -180,7 +182,7 @@ Error PacketPeerUDPWinsock::_poll(bool p_wait) {
 
 		} else {
 			// WARN_PRINT("Ignoring packet with unknown address family");
-			uint8_t type = (uint8_t)IP_Address::TYPE_NONE;
+			uint8_t type = (uint8_t)IP::TYPE_NONE;
 			rb.write(&type, 1);
 		};
 

+ 1 - 1
platform/windows/stream_peer_winsock.cpp

@@ -300,7 +300,7 @@ 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.type == IP_Address::TYPE_NONE, ERR_INVALID_PARAMETER);
+	ERR_FAIL_COND_V( p_host == IP_Address(), ERR_INVALID_PARAMETER);
 
 	sockfd = _socket_create(ip_type, SOCK_STREAM, IPPROTO_TCP);
 	if (sockfd  == INVALID_SOCKET) {