stun.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #include "simple/io/udp_socket.h"
  2. #include <iostream>
  3. #include <chrono>
  4. #include <thread>
  5. #include <random>
  6. #include <cassert>
  7. using namespace std::literals;
  8. int main(int argc, char const* argv[]) try
  9. {
  10. auto address_result = argc == 3
  11. ? simple::io::make_address(argv[1], argv[2])
  12. : simple::io::make_address("stun1.l.google.com", "19302")
  13. ;
  14. if(std::holds_alternative<simple::io::name_unresolved>(address_result))
  15. {
  16. std::cout << "Name unresolved!" << '\n';
  17. return 0;
  18. }
  19. const auto& address = get<0>(address_result);
  20. std::cout << to_string(address) << '\n';
  21. auto s = get<0>(simple::io::open_udp_socket());
  22. std::vector<std::byte> request;
  23. // i hate htonl and the likes and the whole mental baggage that comes with
  24. // em. the protocol is big endian, which means it's just the way you would
  25. // write it, nothing special, little endian is the special backwards one, so
  26. // if you just shift yer bytes into yer registers you shouldn't need to
  27. // give a single damn of what the endianess of your machine is or whatever
  28. // and if you want things to be fast, just define a protocol that matches the
  29. // endianess of your machine, like duh, why do you have to ruin everythin
  30. // binding request
  31. request.push_back(std::byte(0));
  32. request.push_back(std::byte(1));
  33. // message length 0?
  34. request.push_back(std::byte(0));
  35. request.push_back(std::byte(0));
  36. // magic cookie ?
  37. request.push_back(std::byte(0x21));
  38. request.push_back(std::byte(0x12));
  39. request.push_back(std::byte(0xa4));
  40. request.push_back(std::byte(0x42));
  41. // random transaction id
  42. auto rand = std::random_device{};
  43. std::uniform_int_distribution byte_dist(0,255);
  44. for(int i = 12; i --> 0;)
  45. request.push_back(std::byte(byte_dist(rand)));
  46. get<0>(simple::io::send(s, address, simple::support::as_byte_view(request)));
  47. std::vector<std::byte> response;
  48. response.resize(2048);
  49. auto frame_limit = 200;
  50. while(frame_limit --> 0)
  51. {
  52. auto br = simple::support::as_byte_range(response);
  53. auto received = get<0>(simple::io::receive(s, br));
  54. if(received.end != br.begin())
  55. {
  56. auto total_length = received.end - br.begin();
  57. std::cout << "Received " << total_length << " bytes" << '\n';
  58. auto reader = br.begin();
  59. // binding response type
  60. assert(*reader++ == std::byte(1));
  61. assert(*reader++ == std::byte(1));
  62. unsigned int message_length = 0;
  63. message_length |= (unsigned int)(*reader++);
  64. message_length <<= 8;
  65. message_length |= (unsigned int)(*reader++);
  66. std::cout << "Response message lenght: " << message_length << '\n';
  67. // magic cookie and transaction id repeat
  68. assert(std::equal(request.end() - 16, request.end(), reader));
  69. reader += 16;
  70. // TODO: handle plain address and jump over other attributes
  71. unsigned int attribute_type = 0;
  72. attribute_type |= (unsigned int)(*reader++);
  73. attribute_type <<= 8;
  74. attribute_type |= (unsigned int)(*reader++);
  75. std::cout << "Attribute type: "
  76. << std::hex << std::showbase << attribute_type
  77. << std::dec << '\n';
  78. unsigned int attribute_length = 0;
  79. attribute_length |= (unsigned int)(*reader++);
  80. attribute_length <<= 8;
  81. attribute_length |= (unsigned int)(*reader++);
  82. std::cout << "Attribute length: " << attribute_length << '\n';
  83. ++reader; // skip unused byte
  84. assert(*reader++ == std::byte(1)); // address family is IPv4
  85. // port is xored with higher word of magic cookie
  86. unsigned int port = 0;
  87. port |= (unsigned int)(*reader++) ^ 0x21;
  88. port <<= 8;
  89. port |= (unsigned int)(*reader++) ^ 0x12;
  90. // ip is xored with the whole of magic cookie for v4
  91. std::array<unsigned int, 4> ip{};
  92. ip[0] |= (unsigned int)(*reader++) ^ 0x21;
  93. ip[1] |= (unsigned int)(*reader++) ^ 0x12;
  94. ip[2] |= (unsigned int)(*reader++) ^ 0xa4;
  95. ip[3] |= (unsigned int)(*reader++) ^ 0x42;
  96. std::cout << "Finally our public address is: " <<
  97. ip[0] << '.' << ip[1] << '.' << ip[2] << '.' << ip[3]
  98. << ':' << port << '\n';
  99. break;
  100. }
  101. std::this_thread::sleep_for(16ms);
  102. }
  103. return 0;
  104. }
  105. catch(...)
  106. {
  107. perror("omaaaagooot");
  108. throw;
  109. }