发送Icmp消息但什么都没有收到?

时间:2018-03-05 00:46:40

标签: 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'没什么,我需要两天时间,谢谢你。

1 个答案:

答案 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;

}