/** Creates a STUN request and sends it to a random STUN server selected from * the list stored in the config file. See * https://tools.ietf.org/html/rfc5389#section-6 * for details on the message structure. * The request is send through m_transaction_host, from which the answer * will be retrieved by parseStunResponse() */ void GetPublicAddress::createStunRequest() { // Pick a random stun server std::vector<std::string> stun_servers = UserConfigParams::m_stun_servers; const char* server_name = stun_servers[rand() % stun_servers.size()].c_str(); Log::debug("GetPublicAddress", "Using STUN server %s", server_name); struct addrinfo hints, *res; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version hints.ai_socktype = SOCK_STREAM; // Resolve the stun server name so we can send it a STUN request int status = getaddrinfo(server_name, NULL, &hints, &res); if (status != 0) { Log::error("GetPublicAddress", "Error in getaddrinfo: %s", gai_strerror(status)); return; } // documentation says it points to "one or more addrinfo structures" assert(res != NULL); struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr); m_stun_server_ip = ntohl(current_interface->sin_addr.s_addr); // Create a new socket for the stun server. m_transaction_host = new Network(1, 1, 0, 0); // Assemble the message for the stun server BareNetworkString s(21); // bytes 0-1: the type of the message // bytes 2-3: message length added to header (attributes) uint16_t message_type = 0x0001; // binding request uint16_t message_length = 0x0000; s.addUInt16(message_type).addUInt16(message_length) .addUInt32(0x2112A442); // bytes 8-19: the transaction id for (int i = 0; i < 12; i++) { uint8_t random_byte = rand() % 256; s.addUInt8(random_byte); m_stun_tansaction_id[i] = random_byte; } s.addChar(0); m_transaction_host->sendRawPacket(s, TransportAddress(m_stun_server_ip, m_stun_server_port) ); freeaddrinfo(res); m_state = STUN_REQUEST_SENT; } // createStunRequest
void GetPublicAddress::asynchronousUpdate() { if (m_state == NOTHING_DONE) { // format : 00MMMMMCMMMCMMMM (cf rfc 5389) uint16_t message_type = 0x0001; // binding request m_stun_tansaction_id[0] = stunRand(); m_stun_tansaction_id[1] = stunRand(); m_stun_tansaction_id[2] = stunRand(); uint16_t message_length = 0x0000; uint8_t bytes[21]; // the message to be sent // bytes 0-1 : the type of the message, bytes[0] = (uint8_t)(message_type>>8); bytes[1] = (uint8_t)(message_type); // bytes 2-3 : message length added to header (attributes) bytes[2] = (uint8_t)(message_length>>8); bytes[3] = (uint8_t)(message_length); // bytes 4-7 : magic cookie to recognize the stun protocol bytes[4] = (uint8_t)(m_stun_magic_cookie>>24); bytes[5] = (uint8_t)(m_stun_magic_cookie>>16); bytes[6] = (uint8_t)(m_stun_magic_cookie>>8); bytes[7] = (uint8_t)(m_stun_magic_cookie); // bytes 8-19 : the transaction id bytes[8] = (uint8_t)(m_stun_tansaction_id[0]>>24); bytes[9] = (uint8_t)(m_stun_tansaction_id[0]>>16); bytes[10] = (uint8_t)(m_stun_tansaction_id[0]>>8); bytes[11] = (uint8_t)(m_stun_tansaction_id[0]); bytes[12] = (uint8_t)(m_stun_tansaction_id[1]>>24); bytes[13] = (uint8_t)(m_stun_tansaction_id[1]>>16); bytes[14] = (uint8_t)(m_stun_tansaction_id[1]>>8); bytes[15] = (uint8_t)(m_stun_tansaction_id[1]); bytes[16] = (uint8_t)(m_stun_tansaction_id[2]>>24); bytes[17] = (uint8_t)(m_stun_tansaction_id[2]>>16); bytes[18] = (uint8_t)(m_stun_tansaction_id[2]>>8); bytes[19] = (uint8_t)(m_stun_tansaction_id[2]); bytes[20] = '\0'; // time to pick a random stun server std::vector<std::string> stun_servers = UserConfigParams::m_stun_servers; RandomGenerator random_gen; int rand_result = random_gen.get((int)stun_servers.size()); Log::verbose("GetPublicAddress", "Using STUN server %s", stun_servers[rand_result].c_str()); // resolve the name into an IP address struct addrinfo hints, *res, *p; int status; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version hints.ai_socktype = SOCK_STREAM; if ((status = getaddrinfo(stun_servers[rand_result].c_str(), NULL, &hints, &res)) != 0) { Log::error("getaddrinfo", gai_strerror(status)); return; } for(p = res;p != NULL; p = p->ai_next) { struct sockaddr_in* current_interface = (struct sockaddr_in*)(p->ai_addr); m_stun_server_ip = ntohl(current_interface->sin_addr.s_addr); m_transaction_host = new STKHost(); m_transaction_host->setupClient(1,1,0,0); m_transaction_host->sendRawPacket(bytes, 20, TransportAddress(m_stun_server_ip, 3478)); m_state = TEST_SENT; freeaddrinfo(res); // free the linked list return; } freeaddrinfo(res); // free the linked list }