Merge pull request #7141 from Faless/2.1.x-ipv6

Cherry pick IPv6 patches to 2.1.x
This commit is contained in:
Rémi Verschelde 2017-01-04 08:38:28 +01:00 committed by GitHub
commit a86dae5761
33 changed files with 866 additions and 223 deletions

View File

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

View File

@ -29,10 +29,14 @@
#include "http_client.h"
#include "io/stream_peer_ssl.h"
void HTTPClient::set_ip_type(IP::Type p_type) {
ip_type = p_type;
}
Error HTTPClient::connect(const String &p_host, int p_port, bool p_ssl,bool p_verify_host){
close();
tcp_connection->set_ip_type(ip_type);
conn_port=p_port;
conn_host=p_host;
@ -62,7 +66,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, ip_type);
status=STATUS_RESOLVING;
}
@ -635,6 +639,7 @@ Error HTTPClient::_get_http_data(uint8_t* p_buffer, int p_bytes,int &r_received)
void HTTPClient::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_ip_type","ip_type"),&HTTPClient::set_ip_type);
ObjectTypeDB::bind_method(_MD("connect:Error","host","port","use_ssl","verify_host"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true));
ObjectTypeDB::bind_method(_MD("set_connection","connection:StreamPeer"),&HTTPClient::set_connection);
ObjectTypeDB::bind_method(_MD("get_connection:StreamPeer"),&HTTPClient::get_connection);
@ -761,6 +766,7 @@ String HTTPClient::query_string_from_dict(const Dictionary& p_dict) {
HTTPClient::HTTPClient(){
ip_type = IP::TYPE_ANY;
tcp_connection = StreamPeerTCP::create_ref();
resolving = IP::RESOLVER_INVALID_ID;
status=STATUS_DISCONNECTED;

View File

@ -132,6 +132,7 @@ public:
private:
IP::Type ip_type;
Status status;
IP::ResolverID resolving;
int conn_port;
@ -164,6 +165,7 @@ private:
public:
void set_ip_type(IP::Type p_type);
//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);

View File

@ -43,10 +43,12 @@ struct _IP_ResolverPrivate {
volatile IP::ResolverStatus status;
IP_Address response;
String hostname;
IP::Type type;
void clear() {
status = IP::RESOLVER_STATUS_NONE;
response = IP_Address();
type = IP::TYPE_NONE;
hostname="";
};
@ -78,9 +80,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==IP_Address())
queue[i].status=IP::RESOLVER_STATUS_ERROR;
else
queue[i].status=IP::RESOLVER_STATUS_DONE;
@ -105,25 +107,30 @@ struct _IP_ResolverPrivate {
HashMap<String, IP_Address> cache;
static String get_cache_key(String p_hostname, IP::Type p_type) {
return itos(p_type) + p_hostname;
}
};
IP_Address IP::resolve_hostname(const String& p_hostname) {
IP_Address IP::resolve_hostname(const String& p_hostname, IP::Type p_type) {
GLOBAL_LOCK_FUNCTION
GLOBAL_LOCK_FUNCTION;
if (resolver->cache.has(p_hostname))
return resolver->cache[p_hostname];
String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
if (resolver->cache.has(key))
return resolver->cache[key];
IP_Address res = _resolve_hostname(p_hostname);
resolver->cache[p_hostname]=res;
IP_Address res = _resolve_hostname(p_hostname, p_type);
resolver->cache[key]=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::Type p_type) {
GLOBAL_LOCK_FUNCTION
GLOBAL_LOCK_FUNCTION;
ResolverID id = resolver->find_empty_id();
@ -132,9 +139,11 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
return id;
}
String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
resolver->queue[id].hostname=p_hostname;
if (resolver->cache.has(p_hostname)) {
resolver->queue[id].response=resolver->cache[p_hostname];
resolver->queue[id].type = p_type;
if (resolver->cache.has(key)) {
resolver->queue[id].response=resolver->cache[key];
resolver->queue[id].status=IP::RESOLVER_STATUS_DONE;
} else {
resolver->queue[id].response=IP_Address();
@ -145,10 +154,6 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
resolver->resolve_queues();
}
return id;
}
@ -187,6 +192,17 @@ 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(_IP_ResolverPrivate::get_cache_key(p_hostname, IP::TYPE_NONE));
resolver->cache.erase(_IP_ResolverPrivate::get_cache_key(p_hostname, IP::TYPE_IPV4));
resolver->cache.erase(_IP_ResolverPrivate::get_cache_key(p_hostname, IP::TYPE_IPV6));
resolver->cache.erase(_IP_ResolverPrivate::get_cache_key(p_hostname, IP::TYPE_ANY));
}
};
Array IP::_get_local_addresses() const {
@ -202,12 +218,13 @@ Array IP::_get_local_addresses() const {
void IP::_bind_methods() {
ObjectTypeDB::bind_method(_MD("resolve_hostname","host"),&IP::resolve_hostname);
ObjectTypeDB::bind_method(_MD("resolve_hostname_queue_item","host"),&IP::resolve_hostname_queue_item);
ObjectTypeDB::bind_method(_MD("resolve_hostname","host","ip_type"),&IP::resolve_hostname,DEFVAL(IP::TYPE_ANY));
ObjectTypeDB::bind_method(_MD("resolve_hostname_queue_item","host","ip_type"),&IP::resolve_hostname_queue_item,DEFVAL(IP::TYPE_ANY));
ObjectTypeDB::bind_method(_MD("get_resolve_item_status","id"),&IP::get_resolve_item_status);
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 );
@ -217,6 +234,10 @@ void IP::_bind_methods() {
BIND_CONSTANT( RESOLVER_MAX_QUERIES );
BIND_CONSTANT( RESOLVER_INVALID_ID );
BIND_CONSTANT( TYPE_NONE );
BIND_CONSTANT( TYPE_IPV4 );
BIND_CONSTANT( TYPE_IPV6 );
BIND_CONSTANT( TYPE_ANY );
}

View File

@ -48,6 +48,14 @@ public:
RESOLVER_STATUS_ERROR,
};
enum Type {
TYPE_NONE = 0,
TYPE_IPV4 = 1,
TYPE_IPV6 = 2,
TYPE_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, Type p_type = 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, Type p_type = TYPE_ANY);
// async resolver hostname
ResolverID resolve_hostname_queue_item(const String& p_hostname);
ResolverID resolve_hostname_queue_item(const String& p_hostname, Type p_type = 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();
@ -91,4 +101,6 @@ public:
};
VARIANT_ENUM_CAST(IP::Type);
#endif // IP_H

View File

@ -32,29 +32,209 @@ 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(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) {
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++) {
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: "+ip);
ERR_FAIL();
}
for(int i=0;i<4;i++) {
p_ret[i]=ip.get_slicec('.',i).to_int();
}
};
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) {
host=0;
int slices = p_string.get_slice_count(".");
if (slices!=4) {
ERR_EXPLAIN("Invalid IP Address String: "+p_string);
ERR_FAIL();
}
for(int i=0;i<4;i++) {
clear();
if (p_string.find(":") >= 0) {
field[i]=p_string.get_slicec('.',i).to_int();
}
_parse_ipv6(p_string);
} else {
// Mapped to IPv6
field16[5] = 0xffff;
_parse_ipv4(p_string, 0, &field8[12]);
};
}
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, bool is_v6) {
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);
}
field[0]=p_a;
field[1]=p_b;
field[2]=p_c;
field[3]=p_d;
}

View File

@ -33,22 +33,45 @@
struct IP_Address {
private:
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();
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(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, bool is_v6=false);
IP_Address() { clear(); }
};

View File

@ -29,27 +29,20 @@
#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;
}
String PacketPeerUDP::_get_packet_ip() const {
return get_packet_address();
}
Error PacketPeerUDP::_set_send_address(const String& p_address,int p_port) {
Error PacketPeerUDP::_set_send_address(const String& p_address, int p_port) {
IP_Address ip;
if (p_address.is_valid_ip_address()) {
ip=p_address;
} else {
ip=IP::get_singleton()->resolve_hostname(p_address);
ip=IP::get_singleton()->resolve_hostname(p_address, ip_type);
if (ip==IP_Address())
return ERR_CANT_RESOLVE;
}
@ -58,14 +51,20 @@ Error PacketPeerUDP::_set_send_address(const String& p_address,int p_port) {
return OK;
}
void PacketPeerUDP::set_ip_type(IP::Type p_type) {
close();
ip_type = p_type;
}
void PacketPeerUDP::_bind_methods() {
ObjectTypeDB::bind_method(_MD("listen:Error","port","recv_buf_size"),&PacketPeerUDP::listen,DEFVAL(65536));
ObjectTypeDB::bind_method(_MD("set_ip_type","ip_type"),&PacketPeerUDP::set_ip_type);
ObjectTypeDB::bind_method(_MD("listen:Error","port", "recv_buf_size"),&PacketPeerUDP::listen,DEFVAL(65536));
ObjectTypeDB::bind_method(_MD("close"),&PacketPeerUDP::close);
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);
@ -88,4 +87,5 @@ PacketPeerUDP* PacketPeerUDP::create() {
PacketPeerUDP::PacketPeerUDP()
{
ip_type = IP::TYPE_ANY;
}

View File

@ -30,6 +30,7 @@
#define PACKET_PEER_UDP_H
#include "io/ip.h"
#include "io/packet_peer.h"
class PacketPeerUDP : public PacketPeer {
@ -37,17 +38,19 @@ class PacketPeerUDP : public PacketPeer {
protected:
IP::Type ip_type;
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 void set_ip_type(IP::Type p_type);
virtual Error listen(int p_port, int p_recv_buffer_size=65536)=0;
virtual void close()=0;
virtual Error wait()=0;
virtual bool is_listening() const=0;

View File

@ -30,9 +30,30 @@
StreamPeerTCP* (*StreamPeerTCP::_create)()=NULL;
Error StreamPeerTCP::_connect(const String& p_address,int p_port) {
IP_Address ip;
if (p_address.is_valid_ip_address()) {
ip=p_address;
} else {
ip=IP::get_singleton()->resolve_hostname(p_address, ip_type);
if (ip==IP_Address())
return ERR_CANT_RESOLVE;
}
connect(ip,p_port);
return OK;
}
void StreamPeerTCP::set_ip_type(IP::Type p_type) {
disconnect();
ip_type = p_type;
}
void StreamPeerTCP::_bind_methods() {
ObjectTypeDB::bind_method(_MD("connect","host","port"),&StreamPeerTCP::connect);
ObjectTypeDB::bind_method(_MD("set_ip_type","ip_type"),&StreamPeerTCP::set_ip_type);
ObjectTypeDB::bind_method(_MD("connect","host","port"),&StreamPeerTCP::_connect);
ObjectTypeDB::bind_method(_MD("is_connected"),&StreamPeerTCP::is_connected);
ObjectTypeDB::bind_method(_MD("get_status"),&StreamPeerTCP::get_status);
ObjectTypeDB::bind_method(_MD("get_connected_host"),&StreamPeerTCP::get_connected_host);
@ -62,6 +83,7 @@ StreamPeerTCP* StreamPeerTCP::create() {
StreamPeerTCP::StreamPeerTCP() {
ip_type = IP::TYPE_ANY;
}
StreamPeerTCP::~StreamPeerTCP() {

View File

@ -32,6 +32,7 @@
#include "stream_peer.h"
#include "ip_address.h"
#include "io/ip.h"
class StreamPeerTCP : public StreamPeer {
@ -50,11 +51,15 @@ public:
protected:
IP::Type ip_type;
virtual Error _connect(const String& p_address, int p_port);
static StreamPeerTCP* (*_create)();
static void _bind_methods();
public:
virtual void set_ip_type(IP::Type p_type);
virtual Error connect(const IP_Address& p_host, uint16_t p_port)=0;
//read/write from streampeer

View File

@ -44,18 +44,24 @@ 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, 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, hosts.size()?&hosts:NULL);
}
void TCP_Server::set_ip_type(IP::Type p_type) {
stop();
ip_type = p_type;
}
void TCP_Server::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_ip_type","ip_type"),&TCP_Server::set_ip_type);
ObjectTypeDB::bind_method(_MD("listen","port","accepted_hosts"),&TCP_Server::_listen,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);
@ -66,4 +72,5 @@ void TCP_Server::_bind_methods() {
TCP_Server::TCP_Server()
{
ip_type = IP::TYPE_ANY;
}

View File

@ -38,14 +38,17 @@ class TCP_Server : public Reference {
OBJ_TYPE( TCP_Server, Reference );
protected:
IP::Type ip_type;
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, 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 void set_ip_type(IP::Type p_type);
virtual Error listen(uint16_t p_port, const List<String> *p_accepted_hosts=NULL)=0;
virtual bool is_connection_available() const=0;
virtual Ref<StreamPeerTCP> take_connection()=0;

View File

@ -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())
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;
int val = n.to_int();
if (val<0 || val>255)
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;
}

View File

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

View File

@ -1760,6 +1760,7 @@ _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);
}
void unregister_variant_methods() {

View File

@ -15596,8 +15596,10 @@
</return>
<argument index="0" name="host" type="String">
</argument>
<argument index="1" name="ip_type" type="int" default="IP.TYPE_ANY">
</argument>
<description>
Resolve a given hostname, blocking. Resolved hostname is returned as an IP.
Resolve a given hostname, blocking. Resolved hostname is returned as an IPv4 or IPv6 depending on "ip_type".
</description>
</method>
<method name="resolve_hostname_queue_item">
@ -15605,8 +15607,10 @@
</return>
<argument index="0" name="host" type="String">
</argument>
<argument index="1" name="ip_type" type="int" default="IP.TYPE_ANY">
</argument>
<description>
Create a queue item for resolving a given hostname. The queue ID is returned, or RESOLVER_INVALID_ID on error.
Create a queue item for resolving a given hostname to an IPv4 or IPv6 depending on "ip_type". The queue ID is returned, or RESOLVER_INVALID_ID on error.
</description>
</method>
</methods>
@ -24543,7 +24547,7 @@
<argument index="1" name="recv_buf_size" type="int" default="65536">
</argument>
<description>
Make this [PacketPeerUDP] listen on the "port" using a buffer size "recv_buf_size". Listens on all available adresses.
Make this [PacketPeerUDP] listen on the "port" with a buffer size "recv_buf_size". Listens on all available addresses.
</description>
</method>
<method name="set_send_address">
@ -24554,7 +24558,7 @@
<argument index="1" name="port" type="int">
</argument>
<description>
Set the destination address and port for sending packets and variables, a hostname will be resolved if valid.
Set the destination address and port for sending packets and variables, a hostname will be resolved using if valid.
</description>
</method>
<method name="wait">
@ -38120,7 +38124,7 @@
<argument index="1" name="port" type="int">
</argument>
<description>
Connect to the specified IP:port pair. Returns [OK] on success or [FAILED] on failure.
Connect to the specified host:port pair. A hostname will be resolved if valid. Returns [OK] on success or [FAILED] on failure.
</description>
</method>
<method name="disconnect">
@ -39634,7 +39638,7 @@
<argument index="1" name="accepted_hosts" type="StringArray" default="StringArray([])">
</argument>
<description>
Listen on a port, alternatively give a white-list of accepted hosts.
Listen on a port using protocol, alternatively give a white-list of accepted hosts.
</description>
</method>
<method name="stop">

View File

@ -28,10 +28,15 @@
/*************************************************************************/
#include "ip_unix.h"
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED) && !defined(WINRT_ENABLED)
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
#include <string.h>
#ifdef WINDOWS_ENABLED
// Workaround mingw missing flags!
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0x00000400
#endif
#ifdef WINRT_ENABLED
#include <ws2tcpip.h>
#include <winsock2.h>
@ -62,16 +67,50 @@
#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.set_ipv4((uint8_t *)&(addr->sin_addr));
} else {
struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
ip.set_ipv6(addr6->sin6_addr.s6_addr);
};
ip.host= *((unsigned long*)he->h_addr);
return ip;
};
IP_Address IP_Unix::_resolve_hostname(const String& p_hostname, Type p_type) {
struct addrinfo hints;
struct addrinfo* result;
memset(&hints, 0, sizeof(struct addrinfo));
if (p_type == TYPE_IPV4) {
hints.ai_family = AF_INET;
} else if (p_type == TYPE_IPV6) {
hints.ai_family = AF_INET6;
hints.ai_flags = 0;
} else {
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_ADDRCONFIG;
};
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;
@ -83,6 +122,18 @@ IP_Address IP_Unix::_resolve_hostname(const String& p_hostname) {
void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
using namespace Windows::Networking;
using namespace Windows::Networking::Connectivity;
auto hostnames = NetworkInformation::GetHostNames();
for (int i = 0; i < hostnames->Size; i++) {
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())));
}
}
};
#else
@ -95,7 +146,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,
@ -121,14 +172,20 @@ 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.set_ipv4((uint8_t *)&(ipv4->sin_addr));
} else { // ipv6
SOCKADDR_IN6* ipv6 = reinterpret_cast<SOCKADDR_IN6*>(address->Address.lpSockaddr);
ip.set_ipv6(ipv6->sin6_addr.s6_addr);
};
//inet_ntop(AF_INET, &ipv4->sin_addr, addr_chr, INET_ADDRSTRLEN);
r_addresses->push_back(ip);
@ -154,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);

View File

@ -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::Type p_type);
static IP* _create_unix();
public:

View File

@ -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,17 @@ 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::TYPE_IPV4) {
uint8_t ip[4];
rb.read(ip,4,true);
packet_ip.set_ipv4(ip);
} else {
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);
rb.read(packet_buffer,size,true);
@ -85,17 +96,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){
ERR_FAIL_COND_V(peer_addr == IP_Address(), ERR_UNCONFIGURED);
int sock = _get_socket();
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, ip_type);
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 +121,18 @@ 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, int p_recv_buffer_size) {
close();
int sock = _get_socket();
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, ip_type, NULL);
if (bind(sock, (struct sockaddr*)&addr, addr_size) == -1 ) {
close();
return ERR_UNAVAILABLE;
}
@ -145,16 +157,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::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 = ntohs(sin_from->sin_port);
} else if (from.ss_family == AF_INET6) {
uint8_t type = (uint8_t)IP::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 = ntohs(s6_from->sin6_port);
} else {
// WARN_PRINT("Ignoring packet with unknown address family");
uint8_t type = (uint8_t)IP::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;
};
@ -187,9 +224,7 @@ int PacketPeerUDPPosix::_get_socket() {
if (sockfd != -1)
return sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
ERR_FAIL_COND_V( sockfd == -1, -1 );
//fcntl(sockfd, F_SETFL, O_NONBLOCK);
sockfd = _socket_create(ip_type, SOCK_DGRAM, IPPROTO_UDP);
return sockfd;
}
@ -218,6 +253,7 @@ PacketPeerUDPPosix::PacketPeerUDPPosix() {
packet_port=0;
queue_count=0;
peer_port=0;
ip_type = IP::TYPE_ANY;
}
PacketPeerUDPPosix::~PacketPeerUDPPosix() {

View File

@ -44,8 +44,8 @@ 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;
@ -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, int p_recv_buffer_size=65536);
virtual void close();
virtual Error wait();
virtual bool is_listening() const;

View File

@ -0,0 +1,106 @@
#ifndef SOCKET_HELPERS_H
#define SOCKET_HELPERS_H
#include <string.h>
#ifdef WINDOWS_ENABLED
// Workaround mingw missing flags!
#ifndef IPV6_V6ONLY
#define IPV6_V6ONLY 27
#endif
#endif
// 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, IP::Type p_sock_type = IP::TYPE_ANY) {
memset(p_addr, 0, sizeof(struct sockaddr_storage));
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);
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;
addr4->sin_port = htons(p_port); // short, network byte order
copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 16);
return sizeof(sockaddr_in);
};
};
static size_t _set_listen_sockaddr(struct sockaddr_storage* p_addr, int p_port, IP::Type p_sock_type, const List<String> *p_accepted_hosts) {
memset(p_addr, 0, sizeof(struct sockaddr_storage));
if (p_sock_type == IP::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 int _socket_create(IP::Type p_type, int type, int protocol) {
ERR_FAIL_COND_V(p_type > IP::TYPE_ANY || p_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
int family = p_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6;
int sockfd = socket(family, type, protocol);
ERR_FAIL_COND_V( sockfd == -1, -1 );
if(family == AF_INET6) {
// Select IPv4 over IPv6 mapping
int opt = p_type != IP::TYPE_ANY;
if(setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&opt, sizeof(opt)) != 0) {
WARN_PRINT("Unable to set/unset IPv4 address mapping over IPv6");
}
}
return sockfd;
}
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) {
struct sockaddr_in* addr4 = (struct sockaddr_in*)p_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) {
struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
r_ip.set_ipv6(addr6->sin6_addr.s6_addr);
r_port = ntohs(addr6->sin6_port);
};
};
#endif

View File

@ -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,16 +97,22 @@ 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, ip_type);
if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == -1) {
if (errno == EISCONN) {
status = STATUS_CONNECTED;
return OK;
};
return OK;
if (errno == EINPROGRESS || errno == EALREADY) {
return OK;
}
status = STATUS_ERROR;
return ERR_CONNECTION_ERROR;
} else {
status = STATUS_CONNECTED;
@ -122,8 +122,9 @@ Error StreamPeerTCPPosix::_poll_connection(bool p_block) const {
return OK;
};
void StreamPeerTCPPosix::set_socket(int p_sockfd, IP_Address p_host, int p_port) {
void StreamPeerTCPPosix::set_socket(int p_sockfd, IP_Address p_host, int p_port, IP::Type p_ip_type) {
ip_type = p_ip_type;
sockfd = p_sockfd;
#ifndef NO_FCNTL
fcntl(sockfd, F_SETFL, O_NONBLOCK);
@ -140,9 +141,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 == IP_Address(), ERR_INVALID_PARAMETER);
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
sockfd = _socket_create(ip_type, SOCK_STREAM, IPPROTO_TCP);
if (sockfd == -1) {
ERR_PRINT("Socket creation failed!");
disconnect();
//perror("socket");
@ -156,11 +158,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, ip_type);
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();
@ -391,6 +393,7 @@ StreamPeerTCPPosix::StreamPeerTCPPosix() {
sockfd = -1;
status = STATUS_NONE;
peer_port = 0;
ip_type = IP::TYPE_ANY;
};
StreamPeerTCPPosix::~StreamPeerTCPPosix() {

View File

@ -69,7 +69,7 @@ public:
virtual int get_available_bytes() const;
void set_socket(int p_sockfd, IP_Address p_host, int p_port);
void set_socket(int p_sockfd, IP_Address p_host, int p_port, IP::Type p_ip_type);
virtual IP_Address get_connected_host() const;
virtual uint16_t get_connected_port() const;

View File

@ -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);
@ -68,8 +71,10 @@ void TCPServerPosix::make_default() {
Error TCPServerPosix::listen(uint16_t p_port,const List<String> *p_accepted_hosts) {
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
sockfd = _socket_create(ip_type, SOCK_STREAM, IPPROTO_TCP);
ERR_FAIL_COND_V(sockfd == -1, FAILED);
#ifndef NO_FCNTL
fcntl(sockfd, F_SETFL, O_NONBLOCK);
#else
@ -82,13 +87,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, ip_type, p_accepted_hosts);
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) != -1) {
// automatically fill with my IP TODO: use p_accepted_hosts
if (bind(sockfd, (struct sockaddr *)&addr, addr_size) != -1) {
if (::listen(sockfd, 1) == -1) {
@ -136,9 +140,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 +153,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, ip_type);
return conn;
};
@ -169,6 +176,7 @@ void TCPServerPosix::stop() {
TCPServerPosix::TCPServerPosix() {
listen_sockfd = -1;
ip_type = IP::TYPE_ANY;
};
TCPServerPosix::~TCPServerPosix() {

View File

@ -239,7 +239,7 @@ def configure(env):
env.Append(CCFLAGS=['/DGLES2_ENABLED'])
LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32', 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'shell32', 'advapi32', 'dinput8', 'dxguid']
LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32', 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'ws2_32', 'shell32', 'advapi32', 'dinput8', 'dxguid']
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
env.Append(LIBPATH=[os.getenv("WindowsSdkDir") + "/Lib"])
@ -358,7 +358,7 @@ def configure(env):
env.Append(CCFLAGS=['-DWINDOWS_ENABLED', '-mwindows'])
env.Append(CPPFLAGS=['-DRTAUDIO_ENABLED'])
env.Append(CCFLAGS=['-DGLES2_ENABLED'])
env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid'])
env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid'])
# if (env["bits"]=="32"):
# env.Append(LIBS=['gcc_s'])

View File

@ -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,17 @@ 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::TYPE_IPV4) {
uint8_t ip[4];
rb.read(ip,4,true);
packet_ip.set_ipv4(ip);
} else {
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);
rb.read(packet_buffer,size,true);
@ -62,17 +75,14 @@ Error PacketPeerUDPWinsock::put_packet(const uint8_t *p_buffer,int p_buffer_size
int sock = _get_socket();
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, ip_type);
_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 +114,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, int p_recv_buffer_size) {
close();
int sock = _get_socket();
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, ip_type, NULL);
if (bind(sock, (struct sockaddr*)&addr, addr_size) == -1 ) {
close();
return ERR_UNAVAILABLE;
}
@ -146,17 +156,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::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 = ntohs(sin_from->sin_port);
} else if (from.ss_family == AF_INET6) {
uint8_t type = (uint8_t)IP::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 = ntohs(s6_from->sin6_port);
} else {
// WARN_PRINT("Ignoring packet with unknown address family");
uint8_t type = (uint8_t)IP::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;
};
@ -207,9 +241,7 @@ int PacketPeerUDPWinsock::_get_socket() {
if (sockfd != -1)
return sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
ERR_FAIL_COND_V( sockfd == -1, -1 );
//fcntl(sockfd, F_SETFL, O_NONBLOCK);
sockfd = _socket_create(ip_type, SOCK_DGRAM, IPPROTO_UDP);
return sockfd;
}
@ -239,6 +271,7 @@ PacketPeerUDPWinsock::PacketPeerUDPWinsock() {
packet_port=0;
queue_count=0;
peer_port=0;
ip_type = IP::TYPE_ANY;
}
PacketPeerUDPWinsock::~PacketPeerUDPWinsock() {

View File

@ -42,8 +42,8 @@ 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;
@ -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, int p_recv_buffer_size=65536);
virtual void close();
virtual Error wait();
virtual bool is_listening() const;

View File

@ -31,17 +31,12 @@
#include "stream_peer_winsock.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include "drivers/unix/socket_helpers.h"
int winsock_refcount = 0;
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);
};
StreamPeerTCP* StreamPeerWinsock::_create() {
return memnew(StreamPeerWinsock);
@ -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, ip_type);
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) {
@ -103,7 +98,12 @@ Error StreamPeerWinsock::_poll_connection(bool p_block) const {
return OK;
};
return OK;
if (errno == WSAEINPROGRESS || errno == WSAEALREADY) {
return OK;
}
status = STATUS_ERROR;
return ERR_CONNECTION_ERROR;
} else {
status = STATUS_CONNECTED;
@ -289,8 +289,9 @@ void StreamPeerWinsock::disconnect() {
peer_port = 0;
};
void StreamPeerWinsock::set_socket(int p_sockfd, IP_Address p_host, int p_port) {
void StreamPeerWinsock::set_socket(int p_sockfd, IP_Address p_host, int p_port, IP::Type p_ip_type) {
ip_type = p_ip_type;
sockfd = p_sockfd;
status = STATUS_CONNECTING;
peer_host = p_host;
@ -299,9 +300,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 == IP_Address(), ERR_INVALID_PARAMETER);
if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
sockfd = _socket_create(ip_type, SOCK_STREAM, IPPROTO_TCP);
if (sockfd == INVALID_SOCKET) {
ERR_PRINT("Socket creation failed!");
disconnect();
//perror("socket");
@ -315,10 +317,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, ip_type);
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!");
@ -366,6 +368,7 @@ StreamPeerWinsock::StreamPeerWinsock() {
sockfd = INVALID_SOCKET;
status = STATUS_NONE;
peer_port = 0;
ip_type = IP::TYPE_ANY;
};
StreamPeerWinsock::~StreamPeerWinsock() {

View File

@ -68,7 +68,7 @@ public:
virtual int get_available_bytes() const;
void set_socket(int p_sockfd, IP_Address p_host, int p_port);
void set_socket(int p_sockfd, IP_Address p_host, int p_port, IP::Type p_ip_type);
virtual IP_Address get_connected_host() const;
virtual uint16_t get_connected_port() const;

View File

@ -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;
@ -63,7 +66,7 @@ void TCPServerWinsock::cleanup() {
Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_hosts) {
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
sockfd = _socket_create(ip_type, SOCK_STREAM, IPPROTO_TCP);
ERR_FAIL_COND_V(sockfd == INVALID_SOCKET, FAILED);
unsigned long par = 1;
@ -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, ip_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, ip_type);
return conn;
};
@ -167,6 +168,7 @@ void TCPServerWinsock::stop() {
TCPServerWinsock::TCPServerWinsock() {
listen_sockfd = INVALID_SOCKET;
ip_type = IP::TYPE_ANY;
};
TCPServerWinsock::~TCPServerWinsock() {

View File

@ -28,6 +28,10 @@
/*************************************************************************/
#include "http_request.h"
void HTTPRequest::set_ip_type(IP::Type p_type) {
client->set_ip_type(p_type);
}
void HTTPRequest::_redirect_request(const String& p_new_url) {
@ -535,6 +539,7 @@ int HTTPRequest::get_body_size() const{
void HTTPRequest::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_ip_type","ip_type"),&HTTPRequest::set_ip_type);
ObjectTypeDB::bind_method(_MD("request","url","custom_headers","ssl_validate_domain","method","request_data"),&HTTPRequest::request,DEFVAL(StringArray()),DEFVAL(true),DEFVAL(HTTPClient::METHOD_GET),DEFVAL(String()));
ObjectTypeDB::bind_method(_MD("cancel_request"),&HTTPRequest::cancel_request);

View File

@ -116,6 +116,7 @@ protected:
static void _bind_methods();
public:
void set_ip_type(IP::Type p_type);
Error request(const String& p_url, const Vector<String>& p_custom_headers=Vector<String>(), bool p_ssl_validate_domain=true, HTTPClient::Method p_method=HTTPClient::METHOD_GET, const String& p_request_data=""); //connects to a full url and perform request
void cancel_request();
HTTPClient::Status get_http_client_status() const;