D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Введение
В этой статье мы рассмотрим пример кода на C++, который использует библиотеку Winsock для выполнения STUN-теста. STUN (Session Traversal Utilities for NAT) - это протокол, который позволяет определить наличие и тип NAT (Network Address Translation) между вашим устройством и сервером. Это полезно для настройки соединений в сетях, использующих NAT.
Подготовка к работе
Для начала мы подключаем необходимые заголовочные файлы:
C++: Скопировать в буфер обмена
Также, мы указываем компилятору использовать библиотеку `ws2_32.lib`, которая содержит функции Winsock:
C++: Скопировать в буфер обмена
Основной код
Мы начинаем с определения макросов для обработки ошибок:
C++: Скопировать в буфер обмена
Далее, у нас есть структура `stunPack`, представляющая STUN-пакет:
C++: Скопировать в буфер обмена
Мы также имеем функции `BEbytes`, которые выполняют перекодировку чисел в сетевой порядок байт:
C++: Скопировать в буфер обмена
Функция `StunRequest` выполняет запрос к STUN-серверу:
C++: Скопировать в буфер обмена
Функция `xorAddr` используется для обработки ответа от STUN-сервера и извлечения IP-адреса и порта:
C++: Скопировать в буфер обмена
Основная функция
В функции `StunTest` мы инициализируем Winsock и создаем сокет:
C++: Скопировать в буфер обмена
Затем мы выполняем запрос к STUN-серверу (в данном случае, "stun.l.google.com") и ожидаем ответ:
C++: Скопировать в буфер обмена
Заключительные шаги включают закрытие сокета и очистку Winsock:
C++: Скопировать в буфер обмена
Весь код
C++: Скопировать в буфер обмена
Завершение
Этот пример кода демонстрирует, как использовать Winsock для выполнения STUN-теста и извлечения IP-адреса и порта. Вы можете адаптировать этот код для своих нужд и использовать его в своих проектах, связанных с сетевой коммуникацией.
В этой статье мы рассмотрим пример кода на C++, который использует библиотеку Winsock для выполнения STUN-теста. STUN (Session Traversal Utilities for NAT) - это протокол, который позволяет определить наличие и тип NAT (Network Address Translation) между вашим устройством и сервером. Это полезно для настройки соединений в сетях, использующих NAT.
Подготовка к работе
Для начала мы подключаем необходимые заголовочные файлы:
C++: Скопировать в буфер обмена
Код:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
Также, мы указываем компилятору использовать библиотеку `ws2_32.lib`, которая содержит функции Winsock:
C++: Скопировать в буфер обмена
#pragma comment(lib, "ws2_32.lib")
Основной код
Мы начинаем с определения макросов для обработки ошибок:
C++: Скопировать в буфер обмена
Код:
#define REPORT_ERROR(msg) \
do { \
std::cerr << msg << std::endl; \
return; \
} while (0)
// Другие макросы для обработки ошибок
Далее, у нас есть структура `stunPack`, представляющая STUN-пакет:
C++: Скопировать в буфер обмена
Код:
struct stunPack {
uint16_t MessageType;
uint16_t MessageLength;
uint32_t MessageCookie;
uint8_t MessageTransactionID[12];
};
Мы также имеем функции `BEbytes`, которые выполняют перекодировку чисел в сетевой порядок байт:
C++: Скопировать в буфер обмена
Код:
void BEbytes(void* buffer, uint16_t value);
void BEbytes(void* buffer, uint32_t value);
Функция `StunRequest` выполняет запрос к STUN-серверу:
C++: Скопировать в буфер обмена
void StunRequest(const std::string& server, SOCKET sock);
Функция `xorAddr` используется для обработки ответа от STUN-сервера и извлечения IP-адреса и порта:
C++: Скопировать в буфер обмена
void xorAddr(const uint8_t* b, std::string& result);
Основная функция
В функции `StunTest` мы инициализируем Winsock и создаем сокет:
C++: Скопировать в буфер обмена
Код:
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
REPORT_ERROR("Failed to initialize Winsock");
}
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
REPORT_ERROR("Failed to create socket");
WSACleanup();
return;
}
Затем мы выполняем запрос к STUN-серверу (в данном случае, "stun.l.google.com") и ожидаем ответ:
C++: Скопировать в буфер обмена
Код:
StunRequest("stun.l.google.com", sock);
char buffer[1024];
while (true) {
int bytesReceived = recv(sock, buffer, sizeof(buffer), 0);
if (bytesReceived == SOCKET_ERROR) {
REPORT_ERROR("Error receiving data");
}
if (bytesReceived >= 6) {
std::string s;
xorAddr(reinterpret_cast<uint8_t*>(buffer + bytesReceived - 6), s);
std::cout << s << std::endl;
}
}
Заключительные шаги включают закрытие сокета и очистку Winsock:
C++: Скопировать в буфер обмена
Код:
closesocket(sock);
WSACleanup();
Весь код
C++: Скопировать в буфер обмена
Код:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
#pragma comment(lib, "ws2_32.lib")
#define REPORT_ERROR(msg) \
do { \
std::cerr << msg << std::endl; \
return; \
} while (0)
#define REPORT_ERROR_NULL(msg) \
do { \
std::cerr << msg << std::endl; \
return nullptr; \
} while (0)
#define REPORT_ERROR_EMPTY_STRING(msg) \
do { \
std::cerr << msg << std::endl; \
return ""; \
} while (0)
#define REPORT_ERROR_NEGATIVE_INT(msg) \
do { \
std::cerr << msg << std::endl; \
return -1; \
} while (0)
struct stunPack {
uint16_t MessageType;
uint16_t MessageLength;
uint32_t MessageCookie;
uint8_t MessageTransactionID[12];
};
void BEbytes(void* buffer, uint16_t value) {
*(uint16_t*)buffer = htons(value);
}
void BEbytes(void* buffer, uint32_t value) {
*(uint32_t*)buffer = htonl(value);
}
void StunRequest(const std::string& server, SOCKET sock) {
addrinfo hints, * result = nullptr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo(server.c_str(), "19302", &hints, &result) != 0) {
REPORT_ERROR("Failed to resolve server address");
}
stunPack sp;
sp.MessageType = htons(1);
sp.MessageLength = htons(0);
sp.MessageCookie = htonl(0x2112A442);
uint8_t transactionID[12] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
memcpy(sp.MessageTransactionID, transactionID, sizeof(transactionID));
if (sendto(sock, (const char*)&sp, sizeof(sp), 0, result->ai_addr, (int)result->ai_addrlen) == SOCKET_ERROR) {
REPORT_ERROR("Failed to send data");
}
freeaddrinfo(result);
}
void xorAddr(const uint8_t* b, std::string& result) {
uint16_t port = ntohs(*(uint16_t*)b);
port ^= 0x2112;
struct in_addr addr;
addr.S_un.S_un_b.s_b1 = b[2] ^ 0x21;
addr.S_un.S_un_b.s_b2 = b[3] ^ 0x12;
addr.S_un.S_un_b.s_b3 = b[4] ^ 0xa4;
addr.S_un.S_un_b.s_b4 = b[5] ^ 0x42;
char ipAddress[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &addr, ipAddress, INET_ADDRSTRLEN) == nullptr) {
REPORT_ERROR("Failed to convert IP address");
}
result = ipAddress + std::string(":") + std::to_string(port);
}
void StunTest() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
REPORT_ERROR("Failed to initialize Winsock");
}
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
REPORT_ERROR("Failed to create socket");
WSACleanup();
return;
}
StunRequest("stun.l.google.com", sock);
//StunRequest("stun1.l.google.com", sock);
//StunRequest("stun2.l.google.com", sock);
//StunRequest("stun3.l.google.com", sock);
char buffer[1024];
while (true) {
int bytesReceived = recv(sock, buffer, sizeof(buffer), 0);
if (bytesReceived == SOCKET_ERROR) {
REPORT_ERROR("Error receiving data");
}
if (bytesReceived >= 6) {
std::string s;
xorAddr(reinterpret_cast<uint8_t*>(buffer + bytesReceived - 6), s);
std::cout << s << std::endl;
}
}
closesocket(sock);
WSACleanup();
}
int main() {
StunTest();
return 0;
}
Завершение
Этот пример кода демонстрирует, как использовать Winsock для выполнения STUN-теста и извлечения IP-адреса и порта. Вы можете адаптировать этот код для своих нужд и использовать его в своих проектах, связанных с сетевой коммуникацией.