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)
This commit is contained in:
Fabio Alessandrelli 2016-12-05 16:32:38 +01:00
parent a77a0118f6
commit 1aff508dd9
11 changed files with 112 additions and 101 deletions

View file

@ -842,7 +842,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");

View file

@ -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 {

View file

@ -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();
};
}
}

View file

@ -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(); }
};

View file

@ -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);
};

View file

@ -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);
};

View file

@ -20,31 +20,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);
};
};
@ -92,19 +91,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);
};

View file

@ -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) {

View file

@ -77,7 +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);
ERR_FAIL_COND_V(!p_ip.is_ipv4(), ERR_INVALID_PARAMETER);
host = enet_host_create (NULL /* create a client host */,
1 /* only allow 1 outgoing connection */,
@ -91,7 +91,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port
_setup_compressor();
ENetAddress address;
address.host=p_ip.field32[0];
address.host=*((uint32_t *)p_ip.get_ipv4());
address.port=p_port;
//enet_address_set_host (& address, "localhost");
@ -150,8 +150,7 @@ void NetworkedMultiplayerENet::poll(){
}
IP_Address ip;
ip.type = IP_Address::TYPE_IPV4;
ip.field32[0]=event.peer -> address.host;
ip.set_ipv4((uint8_t *)&(event.peer -> address.host));
int *new_id = memnew( int );
*new_id = event.data;
@ -685,6 +684,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){
ERR_FAIL_COND(p_ip.type != IP_Address::TYPE_IPV4);
bind_ip=p_ip.field32[0];
ERR_FAIL_COND(!p_ip.is_ipv4());
bind_ip=*(uint32_t *)p_ip.get_ipv4();
}

View file

@ -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);
};

View file

@ -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) {