Improve readability and add documentation for GetPublicAddress::asynchronousUpdate()

This commit is contained in:
konstin
2015-08-08 13:58:21 +02:00
parent fa1ed0240f
commit ee06cd3ba2

View File

@@ -68,8 +68,14 @@ void GetPublicAddress::setup()
m_state = NOTHING_DONE;
}
/** Detects public IP-address and port by first sending a request to a randomly
* selected STUN server and then parsing and validating the response */
void GetPublicAddress::asynchronousUpdate()
{
// Creates a request and sends it to a random STUN server randomly slected
// from the list saved in the config file
// The request is send through m_transaction_host, from which the answer
// can be retrieved
if (m_state == NOTHING_DONE)
{
// format : 00MMMMMCMMMCMMMM (cf rfc 5389)
@@ -129,7 +135,7 @@ void GetPublicAddress::asynchronousUpdate()
Log::error("getaddrinfo", gai_strerror(status));
return;
}
for(p = res;p != NULL; p = p->ai_next)
for (p = res; p != NULL; p = p->ai_next)
{
struct sockaddr_in* current_interface = (struct sockaddr_in*)(p->ai_addr);
@@ -145,6 +151,8 @@ void GetPublicAddress::asynchronousUpdate()
freeaddrinfo(res); // free the linked list
}
// Gets the response from the STUN server, checks it for its validity and
// then parses the answer into address and port
if (m_state == TEST_SENT)
{
uint8_t* data = m_transaction_host->receiveRawPacket(TransportAddress(m_stun_server_ip, 3478), 2000);
@@ -153,96 +161,95 @@ void GetPublicAddress::asynchronousUpdate()
m_state = NOTHING_DONE; // will send the test again to an other server
return;
}
assert(data);
// check that the stun response is a response, contains the magic cookie and the transaction ID
if ( data[0] == 0x01 &&
data[1] == 0x01 &&
data[4] == (uint8_t)(m_stun_magic_cookie>>24) &&
data[5] == (uint8_t)(m_stun_magic_cookie>>16) &&
data[6] == (uint8_t)(m_stun_magic_cookie>>8) &&
data[7] == (uint8_t)(m_stun_magic_cookie) )
if (data[0] != 0x01 ||
data[1] != 0x01 ||
data[4] != (uint8_t)(m_stun_magic_cookie>>24) ||
data[5] != (uint8_t)(m_stun_magic_cookie>>16) ||
data[6] != (uint8_t)(m_stun_magic_cookie>>8) ||
data[7] != (uint8_t)(m_stun_magic_cookie))
{
if(
data[8] == (uint8_t)(m_stun_tansaction_id[0]>>24) &&
data[9] == (uint8_t)(m_stun_tansaction_id[0]>>16) &&
data[10] == (uint8_t)(m_stun_tansaction_id[0]>>8 ) &&
data[11] == (uint8_t)(m_stun_tansaction_id[0] ) &&
data[12] == (uint8_t)(m_stun_tansaction_id[1]>>24) &&
data[13] == (uint8_t)(m_stun_tansaction_id[1]>>16) &&
data[14] == (uint8_t)(m_stun_tansaction_id[1]>>8 ) &&
data[15] == (uint8_t)(m_stun_tansaction_id[1] ) &&
data[16] == (uint8_t)(m_stun_tansaction_id[2]>>24) &&
data[17] == (uint8_t)(m_stun_tansaction_id[2]>>16) &&
data[18] == (uint8_t)(m_stun_tansaction_id[2]>>8 ) &&
data[19] == (uint8_t)(m_stun_tansaction_id[2] ))
{
Log::verbose("GetPublicAddress", "The STUN server responded with a valid answer");
int message_size = data[2]*256+data[3];
return; // invalid data -> try again
}
// parse the stun message now:
bool finish = false;
uint8_t* attributes = data+20;
if (message_size == 0)
{
Log::error("GetPublicAddress", "STUN answer does not contain any information.");
finish = true;
}
if (message_size < 4) // cannot even read the size
{
Log::error("GetPublicAddress", "STUN message is not valid.");
finish = true;
}
uint16_t port;
uint32_t address;
bool valid = false;
while(!finish)
{
int type = attributes[0]*256+attributes[1];
int size = attributes[2]*256+attributes[3];
switch(type)
{
case 0:
case 1:
assert(size == 8);
assert(attributes[5] == 0x01); // IPv4 only
port = attributes[6]*256+attributes[7];
address = (attributes[8]<<24 & 0xFF000000)+(attributes[9]<<16 & 0x00FF0000)+(attributes[10]<<8 & 0x0000FF00)+(attributes[11] & 0x000000FF);
finish = true;
valid = true;
continue;
break;
default:
break;
}
attributes = attributes + 4 + size;
message_size -= 4 + size;
if (message_size == 0)
finish = true;
if (message_size < 4) // cannot even read the size
{
Log::error("GetPublicAddress", "STUN message is not valid.");
finish = true;
}
}
// finished parsing, we know our public transport address
if (valid)
{
Log::debug("GetPublicAddress", "The public address has been found : %i.%i.%i.%i:%i", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port);
m_state = ADDRESS_KNOWN;
TransportAddress* addr = static_cast<TransportAddress*>(m_callback_object);
addr->ip = address;
addr->port = port;
}
else
m_state = NOTHING_DONE; // need to re-send the stun request
}
else
if (data[8] != (uint8_t)(m_stun_tansaction_id[0]>>24) ||
data[9] != (uint8_t)(m_stun_tansaction_id[0]>>16) ||
data[10] != (uint8_t)(m_stun_tansaction_id[0]>>8 ) ||
data[11] != (uint8_t)(m_stun_tansaction_id[0] ) ||
data[12] != (uint8_t)(m_stun_tansaction_id[1]>>24) ||
data[13] != (uint8_t)(m_stun_tansaction_id[1]>>16) ||
data[14] != (uint8_t)(m_stun_tansaction_id[1]>>8 ) ||
data[15] != (uint8_t)(m_stun_tansaction_id[1] ) ||
data[16] != (uint8_t)(m_stun_tansaction_id[2]>>24) ||
data[17] != (uint8_t)(m_stun_tansaction_id[2]>>16) ||
data[18] != (uint8_t)(m_stun_tansaction_id[2]>>8 ) ||
data[19] != (uint8_t)(m_stun_tansaction_id[2] ))
{
m_state = NOTHING_DONE; // need to re-send the stun request
Log::warn("GetPublicAddress", "The STUN server responded with a invalid answer");
return;
}
Log::verbose("GetPublicAddress", "The STUN server responded with a valid answer");
int message_size = data[2]*256+data[3];
// The stun message is valid, so we parse it now:
bool finish = false;
uint8_t* attributes = data+20;
if (message_size == 0)
{
Log::error("GetPublicAddress", "STUN answer does not contain any information.");
finish = true;
}
if (message_size < 4) // cannot even read the size
{
Log::error("GetPublicAddress", "STUN message is not valid.");
finish = true;
}
// This are the address and port that is wanted
uint16_t port;
uint32_t address;
bool valid = false;
while(!finish)
{
int type = attributes[0]*256+attributes[1];
int size = attributes[2]*256+attributes[3];
if (type == 0 || type == 1)
{
m_state = NOTHING_DONE; // need to re-send the stun request
assert(size == 8);
assert(attributes[5] == 0x01); // IPv4 only
port = attributes[6]*256+attributes[7];
// The (IPv4) address was sent as 4 distinct bytes, but needs to be pack into one 4-byte int
address = (attributes[8]<<24 & 0xFF000000) + (attributes[9]<<16 & 0x00FF0000) + (attributes[10]<<8 & 0x0000FF00) + (attributes[11] & 0x000000FF);
finish = true;
valid = true;
continue;
}
attributes = attributes + 4 + size;
message_size -= 4 + size;
if (message_size == 0)
finish = true;
if (message_size < 4) // cannot even read the size
finish = true;
}
// finished parsing, we know our public transport address
if (valid)
{
Log::debug("GetPublicAddress", "The public address has been found: %i.%i.%i.%i:%i", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port);
m_state = ADDRESS_KNOWN;
TransportAddress* addr = static_cast<TransportAddress*>(m_callback_object);
addr->ip = address;
addr->port = port;
}
else
{
Log::error("GetPublicAddress", "STUN message is not valid.");
m_state = NOTHING_DONE; // need to re-send the stun request
}
}
// The address and the port are known, so the connection can be closed
if (m_state == ADDRESS_KNOWN)
{
m_state = EXITING;