zergon321
@zergon321

Почему не работает самописный tracert?

Сделал свою реализацию traceroute. Ни один маршрутизатор не отвечает на эхо-запрос. Обычный tracert, встроенный в Windows, работает нормально. ping, часть кода которого я взял для tracert, также работает нормально. Убирал setsockopt() - все равно нет ответа, везде превышено время ожидания. Алгоритм использовал следующий: для каждого TTL по очереди посылается 3 ICMP-пакета типа echo request, для каждого ожидается ответ с маршрутизатора. Работа программы оканчивается, когда TTL превышает максимальное число прыжков или когда получено ICMP-сообщение типа echo reply. Просканировал все это при помощи Wireshark - echo request'ы посылаются, ответы time exceeded приходят, но программа считает, что их нет вообще, select() не дожидается (хотя должен бы), ICMP-ответы попросту не принимаются. Что нужно сделать, чтобы все заработало?

Код main()
#include "winsock_error.h"
#include "io_ext.h"
#include <cstdio>

#define SOCK_NUM 3

#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable : 4996)

int main()
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY);

	WSADATA filler;

	if (WSAStartup(WSA_VERSION, &filler))
	{
		output_error("Couldn't initialize WSA");
		return EXIT_FAILURE;
	}

	char host_name[MAXGETHOSTSTRUCT];

	enter_message("Please enter a host name: ", host_name);
	std::cout << std::endl;

	LPHOSTENT remote_host_info = gethostbyname(host_name);

	if (remote_host_info == NULL)
	{
		output_error("Couldn't get a host info");
		finalize();

		return EXIT_FAILURE;
	}

	if (remote_host_info->h_addrtype == AF_INET6)
	{
		output_error("Sorry, but R-Scan can't perform scanning of ipv6-hosts");
		finalize();

		return EXIT_SUCCESS;
	}

	//ALIASES SHOWING
	if (remote_host_info->h_aliases[0])
	{
		std::cout << "Server name aliases: " << std::endl;

		for (int i = 0; remote_host_info->h_aliases[i]; i++)
			std::cout << remote_host_info->h_aliases[i] << std::endl;

		std::cout << std::endl;
	}
	else
		std::cout << "Host has no aliases" << std::endl << std::endl;

	in_addr tmp;

	//ADDRESSES SHOWING
	if (remote_host_info->h_addr_list[1])
	{
		std::cout << "Server addresses: " << std::endl;

		for (int i = 0; remote_host_info->h_addr_list[i]; i++)
		{
			tmp.s_addr = *(LPWORD)remote_host_info->h_addr_list[i];
			std::cout << inet_ntoa(tmp) << std::endl;
		}

		std::cout << std::endl;
	}
	else
	{
		std::cout << "Server address: ";
		tmp.s_addr = *(u_long*)remote_host_info->h_addr;
		std::cout << inet_ntoa(tmp) << std::endl << std::endl;
	}

	WORD temp;
	BYTE max_hops_num;

	//MAX HOPS NUM INPUT
	while (true)
	{
		std::cout << "Please enter max number of hops: ";
		(std::cin >> temp).get();

		if (!std::cin || temp > TTL_MAX_VAL || temp == 0)
		{
			clear_istream(std::cin);
			std::cerr << "You entered wrong value; try again" << std::endl << std::endl;
		}
		else
			break;
	}

	__asm //firstly value is assigned to 16 bit temporary variable, then left octet of temporary variable is assigned to max_hops_num variable
	{
		mov ax, temp
		mov max_hops_num, al
	}

	fd_set read;
	SOCKET sock = ICMP_SOCKET;
	sockaddr_in server_data;
	ECHO_REQUEST echo_req;
	ECHO_REPLY echo_rep;
	timeval wait_time = { 2, 0 };

	//CONNECTION DATA FILLING
	server_data.sin_family = AF_INET;
	server_data.sin_port = 0;
	tmp.s_addr = *(LPWORD)&remote_host_info->h_addr;
	inet_pton(AF_INET, (PCSTR)inet_ntoa(tmp), &server_data.sin_addr);

	//ICMP HDR AND ECHO_REQUEST STRUCT FILLING
	echo_req.icmp_hdr.type = ICMP_ECHO_REQ;
	echo_req.icmp_hdr.code = 0;
	echo_req.icmp_hdr.id = 1;
	echo_req.icmp_hdr.seq = 0;
	echo_req.icmp_hdr.checksum = 0;
	echo_req.dw_time = GetTickCount();
	memset(echo_req.data, ECHO_FILLER, PACK_FRAME_SIZE);
	echo_req.icmp_hdr.checksum = checksum((LPWORD)&echo_req, sizeof(echo_req));

	//TRACERT LOOP
	for (BYTE TTL = 1; TTL <= max_hops_num; TTL++)
	{
		tmp.s_addr = 0;
		printf("%3d\t", TTL);

		//set TTL
		if (setsockopt(sock, IPPROTO_IP, IP_TTL, (PCSTR)&TTL, sizeof(TTL)) == SOCKET_ERROR)
		{
			error_to_close_socket("Couldn't set IP header TTL", sock);
			finalize();

			return EXIT_FAILURE;
		}

		//send 3 ICMP echo requests
		RANGE(0, SOCK_NUM)
		{
			echo_req.dw_time = GetTickCount();
			echo_req.icmp_hdr.checksum = checksum((LPWORD)&echo_req, sizeof(echo_req));

			if (sendto(sock, (LPSTR)&echo_req, sizeof(echo_req), 0, (LPSOCKADDR)&server_data, sizeof(server_data)) == SOCKET_ERROR)
			{
				error_to_close_socket("Couldn't perform a ping", sock);
				finalize();

				return EXIT_FAILURE;
			}

			FD_ZERO(&read);
			FD_SET(sock, &read);

			int ret = select(0, &read, NULL, NULL, &wait_time);

			if (ret == SOCKET_ERROR)
			{
				error_to_close_socket("Couldn't perform data awaiting", sock);
				finalize();

				return EXIT_FAILURE;
			}
			else if (ret == 0)
			{
				std::cout << " * \t";
			}
			else
			{
				if (recvfrom(sock, (LPSTR)&echo_rep, sizeof(ECHO_REPLY), 0, NULL, NULL) == SOCKET_ERROR)
				{
					error_to_close_socket("Couldn't receive ICMP", sock);
					finalize();

					return EXIT_FAILURE;
				}

				printf("%4d ms\t", GetTickCount() - echo_rep.echo_request.dw_time);
				tmp = echo_rep.ip_hdr.src_addr;
			}
		}

		if (tmp.s_addr == 0)
		{
			std::cout << "Waiting time exceeded" << std::endl;
			continue;
		}

		remote_host_info = gethostbyaddr((PCSTR)&echo_rep.ip_hdr.src_addr, AF_INET_ADDR_LEN, AF_INET);

		if (remote_host_info == NULL)
			std::cout << inet_ntoa(echo_rep.ip_hdr.src_addr) << std::endl;
		else
			printf("%s [%s]", remote_host_info->h_name, inet_ntoa(echo_rep.ip_hdr.src_addr));

		if (echo_rep.echo_request.icmp_hdr.type == 0 && echo_rep.echo_request.icmp_hdr.code == 0)
			break;
	}
}


Заголовочный файл
#pragma once
#ifndef WINSOCK_ERROR
#define WINSOCK_ERROR

#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <cstdlib>
#include <vector>
#include <cstring>
#include <ctime>
#include <WS2tcpip.h>

#define ECHO_PORT 32768
#define WSA_VERSION 0x0202
#define LOCALHOST "127.0.0.1"
#define BUFF_SIZE 8192
#define BACKLOG 4
#define WAIT_TIME 1
#define INPUT_BUFF 16
#define PACK_FRAME_SIZE 64
#define REPLY_DATA_SIZE 0x100
#define TTL_MAX_VAL 0xFF
#define ICMP_ECHO_REQ 8
#define ECHO_FILLER 87
#define EVERLAST_HOPS_PARAM 0
#define AF_INET_ADDR_LEN 4
#define TCP_SOCKET socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)
#define UDP_SOCKET socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)
#define ICMP_SOCKET socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)

namespace con_mode
{
	enum mode
	{
		write, //socket condition for sending data
		read, //socket condition for receiving data
		input, //socket condition for entering data by user
		timeout //socket condition for inactivity
	};
}

typedef struct ICMP_hdr //ICMP header representation
{
	BYTE type;
	BYTE code;
	WORD checksum;
	WORD id;
	WORD seq;
} ICMP_HDR;

typedef struct echo_request //packet that will be sent through the network
{
	ICMP_HDR icmp_hdr;
	DWORD dw_time;
	char data[PACK_FRAME_SIZE];
} ECHO_REQUEST;

typedef struct ip_hdr
{
	BYTE ver_ihl; //4 bit for version, 4 bit for internet header length (in DWORDs)
	BYTE dscp_ecn; //DiffServ: 6 bit for dscp (packet priority or ToS) and 2 bit ECN (congestion flag); if congestion exists, transmission rate reduces 
	WORD tot_len; //length of header + data, 16 bit field
	WORD id; //for packet fragments identifying (if a packet was fragmented)
	WORD flag_off; //flags of fragmentation and fragment offset; 1st bit - reserved; 2nd bit - if set, packet won't be fragmented if it need and will be dropped;
	//3rd bit - set for all fragmented packets; fragment offset - 13 bit field, defines a fragment offset relative to the beginnig of original IP datagram; measured in 64 bit blocks
	BYTE TTL; //time-to-live, 8 bit field; measured determined bu the number of gateways and hosts packet can traverse
	BYTE protocol; //8 bit field; protocol list: https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
	WORD checksum; //16 bit field; if it hasn't been computed yet, it must be equal to 0
	in_addr src_addr; //32 bit sender IP address
	in_addr dst_addr; //32 bit receiver IP address
} IP_HDR;

typedef struct echo_reply //ICMP echo response from server
{
	IP_HDR ip_hdr;
	ECHO_REQUEST echo_request;
	char c_filler[REPLY_DATA_SIZE];
} ECHO_REPLY;

inline USHORT checksum(USHORT *buffer, int size)
{
	unsigned long cksum = 0;

	while (size > 1)
	{
		cksum += *buffer++;
		size -= sizeof(USHORT);
	}
	if (size)
	{
		cksum += *(UCHAR*)buffer;
	}
	cksum = (cksum >> 16) + (cksum & 0xffff);
	cksum += (cksum >> 16);
	return (USHORT)(~cksum);
}

inline void output_error(const char* message)
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_INTENSITY);
	std::cerr << message << std::endl;
	std::cerr << "Error code: " << WSAGetLastError() << std::endl;
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY);
}

inline void finalize()
{
	WSACleanup();
	system("pause");
}

inline void close_socket(SOCKET sock)
{
	if (closesocket(sock) == SOCKET_ERROR)
	{
		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_INTENSITY);
		std::cerr << "Couldn't close socket: error " << WSAGetLastError() << std::endl;
		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY);
	}
}

inline void error_to_close_socket(const char* message, SOCKET sock)
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_INTENSITY);

	output_error(message);
	close_socket(sock);

	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY);
}

#endif


Скриншот сниффера:26fc208b251c4ab38b41998912a0e9f6.png
  • Вопрос задан
  • 557 просмотров
Пригласить эксперта
Ответы на вопрос 1
@res2001
Developer, ex-admin
Много кода, не осилил.
Возьмите любой сниффер (NetworkMonitor, WireShark) и смотрите им трафик, что от вас уходит, что к вам приходит.
И для начала тренируйтесь на ближайших узлах, которые в вашей сети.
Будет результат от сниффера, тогда можно дальше что-нибудь думать.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы