123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- #include "simple/io/udp_socket.h"
- #include <iostream>
- #include <chrono>
- #include <thread>
- #include <random>
- #include <cassert>
- using namespace std::literals;
- int main(int argc, char const* argv[]) try
- {
- auto address_result = argc == 3
- ? simple::io::make_address(argv[1], argv[2])
- : simple::io::make_address("stun1.l.google.com", "19302")
- ;
- if(std::holds_alternative<simple::io::name_unresolved>(address_result))
- {
- std::cout << "Name unresolved!" << '\n';
- return 0;
- }
- const auto& address = get<0>(address_result);
- std::cout << to_string(address) << '\n';
- auto s = get<0>(simple::io::open_udp_socket());
- std::vector<std::byte> request;
- // i hate htonl and the likes and the whole mental baggage that comes with
- // em. the protocol is big endian, which means it's just the way you would
- // write it, nothing special, little endian is the special backwards one, so
- // if you just shift yer bytes into yer registers you shouldn't need to
- // give a single damn of what the endianess of your machine is or whatever
- // and if you want things to be fast, just define a protocol that matches the
- // endianess of your machine, like duh, why do you have to ruin everythin
- // binding request
- request.push_back(std::byte(0));
- request.push_back(std::byte(1));
- // message length 0?
- request.push_back(std::byte(0));
- request.push_back(std::byte(0));
- // magic cookie ?
- request.push_back(std::byte(0x21));
- request.push_back(std::byte(0x12));
- request.push_back(std::byte(0xa4));
- request.push_back(std::byte(0x42));
- // random transaction id
- auto rand = std::random_device{};
- std::uniform_int_distribution byte_dist(0,255);
- for(int i = 12; i --> 0;)
- request.push_back(std::byte(byte_dist(rand)));
- get<0>(simple::io::send(s, address, simple::support::as_byte_view(request)));
- std::vector<std::byte> response;
- response.resize(2048);
- auto frame_limit = 200;
- while(frame_limit --> 0)
- {
- auto br = simple::support::as_byte_range(response);
- auto received = get<0>(simple::io::receive(s, br));
- if(received.end != br.begin())
- {
- auto total_length = received.end - br.begin();
- std::cout << "Received " << total_length << " bytes" << '\n';
- auto reader = br.begin();
- // binding response type
- assert(*reader++ == std::byte(1));
- assert(*reader++ == std::byte(1));
- unsigned int message_length = 0;
- message_length |= (unsigned int)(*reader++);
- message_length <<= 8;
- message_length |= (unsigned int)(*reader++);
- std::cout << "Response message lenght: " << message_length << '\n';
- // magic cookie and transaction id repeat
- assert(std::equal(request.end() - 16, request.end(), reader));
- reader += 16;
- // TODO: handle plain address and jump over other attributes
- unsigned int attribute_type = 0;
- attribute_type |= (unsigned int)(*reader++);
- attribute_type <<= 8;
- attribute_type |= (unsigned int)(*reader++);
- std::cout << "Attribute type: "
- << std::hex << std::showbase << attribute_type
- << std::dec << '\n';
- unsigned int attribute_length = 0;
- attribute_length |= (unsigned int)(*reader++);
- attribute_length <<= 8;
- attribute_length |= (unsigned int)(*reader++);
- std::cout << "Attribute length: " << attribute_length << '\n';
- ++reader; // skip unused byte
- assert(*reader++ == std::byte(1)); // address family is IPv4
- // port is xored with higher word of magic cookie
- unsigned int port = 0;
- port |= (unsigned int)(*reader++) ^ 0x21;
- port <<= 8;
- port |= (unsigned int)(*reader++) ^ 0x12;
- // ip is xored with the whole of magic cookie for v4
- std::array<unsigned int, 4> ip{};
- ip[0] |= (unsigned int)(*reader++) ^ 0x21;
- ip[1] |= (unsigned int)(*reader++) ^ 0x12;
- ip[2] |= (unsigned int)(*reader++) ^ 0xa4;
- ip[3] |= (unsigned int)(*reader++) ^ 0x42;
- std::cout << "Finally our public address is: " <<
- ip[0] << '.' << ip[1] << '.' << ip[2] << '.' << ip[3]
- << ':' << port << '\n';
- break;
- }
- std::this_thread::sleep_for(16ms);
- }
- return 0;
- }
- catch(...)
- {
- perror("omaaaagooot");
- throw;
- }
|