我想发送ICMP消息并接收回声。我花了一周研究原始套接字和搜索引用。然后我编写下面的代码,我保证发送ICMP消息成功,因为'socket'和'send'的返回是可以的(不是减1)。不幸的是,'recvfrom'一无所获,下面是我在Windows上的简洁代码:
#define _WIN32_WINNT 0x501
#include <stdio.h>
#include <ws2tcpip.h>
#include <winsock2.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#pragma comment(lib,"Ws2_32.lib")
WSADATA wsaData;
int main(int args, char *argv[]){
WSAStartup(MAKEWORD(2,2),&wsaData);
//**************icmp book******************/
typedef struct icmp_hdr {
unsigned char icmp_type;
unsigned char icmp_code;
unsigned short icmp_checksum;
unsigned short icmp_id;
unsigned short icmp_sequence;
unsigned long icmp_timestamp;
} ICMP_HDR;
ICMP_HDR *icmp = NULL;
char buffer[sizeof(ICMP_HDR) + 32];
icmp = (ICMP_HDR *)buffer; //
icmp->icmp_type = 8;
icmp->icmp_code = 0;
icmp->icmp_id = 1314;
icmp->icmp_checksum = sizeof(ICMP_HDR) + 32;
icmp->icmp_sequence = 0;
icmp->icmp_timestamp = GetTickCount();
memset(&buffer[sizeof(ICMP_HDR)],'@',32);
int my_socket = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
struct in_addr my_addr;
my_addr.s_addr = inet_addr("127.0.0.1");
// inet_aton(argv[1],&my_addr);
struct sockaddr_in sa_t;
sa_t.sin_family = AF_INET;
sa_t.sin_port = htons(0);
sa_t.sin_addr = my_addr;
int p = sendto(my_socket,buffer,sizeof(ICMP_HDR)+32,0,(struct sockaddr *)&sa_t, sizeof(sa_t));
printf("return : %d\n ------s: %d\n",p,my_socket);
int tmp_add = sizeof(sa_t);
char buf[1024];
recvfrom(my_socket,buf,1024,0,(struct sockaddr *)&sa_t,&tmp_add);
printf("receive is: %s\n",buf);
return 1;
}
当我编译并运行此代码时,我的期望是'recvfrom'函数将收到任何东西。
但是'recvfrom'没什么,我需要两天时间,谢谢你。
答案 0 :(得分:1)
首先,你需要确保编译器没有填充你的struct试图对齐4,8或16字节地址边界(#pragma pack
)上的所有内容,然后你必须正确对齐结构并正确计算校验和。在响应方面,您不能将缓冲区视为字符串,它可能包含非ASCII数据。此外,网络字节顺序并不总是与任一主机架构上的字节顺序相同,因此您应始终在发送/接收之前进行转换。
这很有效。我会留给你研究响应中额外的20个左右的字节是什么。
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <ws2tcpip.h>
#include <winsock2.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#pragma comment(lib,"Ws2_32.lib")
#define STRING_LENGTH 32
//**************icmp book******************/
#pragma pack(push, 1)
typedef struct icmp_msg {
uint8_t type;
uint8_t code;
uint16_t checksum;
uint16_t id;
uint16_t sequence;
uint32_t timestamp;
uint8_t tail[STRING_LENGTH + 2];
} ICMP_MSG;
#pragma pack(pop)
// malloc() is gauranteed to provide suitable alignement.
ICMP_MSG *ping = NULL;
void PrintMsg(ICMP_MSG *msg)
{
printf("Ping:\n\tId: %u16\n\tSequence: %u16\n\tTimestamp: %lu\n\tString: ", ntohs(msg->id), ntohs(msg->sequence), ntohl(msg->timestamp));
char *it = (char*)&(msg->tail[0]);
char *it_end = (char*)msg + sizeof(ICMP_MSG);
while (it < it_end)
{
char *hexString[65] = { 0 };
_itoa_s(*it, (char*)hexString, 32, 16);
printf("0x%s,", (char*)hexString);
it++;
}
printf("\n\n");
}
// Ripped from https://github.com/pocoproject/poco/blob/develop/Net/src/ICMPPacketImpl.cpp#L98 and cleaned-up.
uint16_t Checksum(uint16_t *addr, size_t len)
{
size_t nleft = len;
uint16_t* w = addr;
uint16_t answer;
int32_t sum = 0;
while (nleft > 1)
{
sum += *w++;
nleft -= sizeof(uint16_t);
}
if (nleft == 1)
{
uint16_t u = 0;
*(uint8_t*)(&u) = *(uint8_t*)w;
sum += u;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum & 0xffff;
return answer;
}
int main(void) {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
ping = malloc(sizeof(ICMP_MSG));
ping->type = 8;
ping->code = 0;
ping->id = htons(1314);
ping->checksum = 0; // Must be zero prior to checksum calculation.
ping->sequence = 0;
ping->timestamp = htonl(GetTickCount());
// 0x40 == '@' in ASCII
memset(ping->tail, 0x40, STRING_LENGTH);
// NUL pad the last two bytes fo the string.
ping->tail[STRING_LENGTH] = '\0';
ping->tail[STRING_LENGTH + 1] = '\0';
ping->checksum = Checksum((uint16_t*)ping, sizeof(ICMP_MSG));
int res;
int my_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
struct in_addr my_addr;
my_addr.s_addr = inet_addr("127.0.0.1");
//res = inet_pton(AF_INET, "127.0.0.1", &my_addr.s_addr);
struct sockaddr_in sa_t;
sa_t.sin_family = AF_INET;
sa_t.sin_port = htons(0);
sa_t.sin_addr = my_addr;
PrintMsg(ping);
res = sendto(my_socket, (const char*)ping, sizeof(ICMP_MSG), 0, (struct sockaddr *)&sa_t, sizeof(sa_t));
printf("Socket: %d\nsendto returned: %d\n", my_socket, res);
int tmp_add = sizeof(sa_t);
ICMP_MSG *resp = calloc(1, 2048 /*sizeof(ICMP_MSG)*/);
res = recvfrom(my_socket, (char*)resp, 2048, 0, (struct sockaddr *)&sa_t, &tmp_add);
printf("recvfrom returned: %d\n", res);
if (SOCKET_ERROR == res)
{
res = WSAGetLastError();
printf("WSAGetLastError() returned: %d (", res);
switch (res)
{
case WSAEMSGSIZE:
printf("WSAMSGSIZE)\n");
break;
default:
printf("Unexpected)\n");
break;
}
}
PrintMsg(resp);
system("pause");
free(ping);
free(resp);
return 1;
}