c - 发送 ICMP 消息但没有收到任何消息?

标签 c sockets

我想发送 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”没有任何结果,我花了两天时间,在这里谢谢。

最佳答案

首先,您需要确保编译器不会尝试在 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;

}

关于c - 发送 ICMP 消息但没有收到任何消息?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49102067/

相关文章:

python - 从函数返回控制台对象后,paramiko/Socket 关闭

java - Java猜数字游戏(客户端和服务器)。输入非整数时,会发生SocketException错误

c - 为什么 emacs 会显示这些警告和错误?

java - 允许一定数量的用户连接到服务器[JAVA]

Android TCP 通过互联网(非 LAN)进行通信。

c++ - 如何在C/C++中使用源代码级多线程?

java - Ruby(服务器)- Java(客户端)连接错误

c - 用 calloc 初始化的随机数填充指针数组的问题

c - 查找头文件包含路径

c - typedef 指针常量怪异