c - OpenBSD 上原始套接字 icmp 的协议(protocol)族不支持 sendto 地址族

标签 c sockets icmp openbsd

我正在尝试为 ICMP 库编写一个 ping 函数。一切似乎都正常,直到 sendto 返回 Address family not supported by protocol family。我不明白这个错误。我做错了什么?

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <netdb.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static u_int16_t
checksum(u_int16_t *arr, size_t bytes)
{
    u_int32_t  sum = 0;
    u_int16_t *ptr = arr;
    while (bytes > 1) {
        sum += *ptr++;
        bytes -= 2;
    }

    if (bytes == 1) {
        *(u_int8_t *)&sum += *(u_int8_t *)ptr;
    }

    sum  = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    return (u_int16_t)(~sum);
}

ssize_t
icmp_send(const char *host, const char *data, const size_t datalen)
{
    int s, error;
    struct addrinfo hints;
    struct addrinfo *res = NULL;

    bzero(&hints, sizeof(hints));
    hints.ai_flags    = AI_CANONNAME;
    hints.ai_family   = AF_INET;
    hints.ai_socktype = SOCK_RAW;
    hints.ai_protocol = IPPROTO_ICMP;

    if ((error = getaddrinfo(host, NULL, &hints, &res))) {
        fprintf(stderr, "ping: getaddrinfo: %s\n", gai_strerror(error));
        return -1;
    }

    struct protoent *proto = getprotobyname("icmp");
    if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) == -1) {
        fprintf(stderr, "ping: socket: %s\n", strerror(errno));
        return -1;
    }

    int on = 1;
    if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
        fprintf(stderr, "ping: setsockopt: %s\n", strerror(errno));
        return -1;
    }

    struct sockaddr_in to;
    bzero(&to, sizeof(to));
    to.sin_family = AF_INET;

    struct ip ip;
    bzero(&ip, sizeof(ip));
    ip.ip_v   = IPVERSION;
    ip.ip_hl  = sizeof(struct ip) << 2;
    ip.ip_id  = 0;
    ip.ip_ttl = 255;
    ip.ip_p   = IPPROTO_ICMP;
    ip.ip_src.s_addr = INADDR_ANY;
    ip.ip_dst = ((struct sockaddr_in *)res)->sin_addr;
    ip.ip_sum = checksum((u_int16_t *)&ip, sizeof(ip));

    struct icmp icp;
    bzero(&icp, sizeof(icp));
    icp.icmp_type = ICMP_ECHOREPLY;
    icp.icmp_code = 0;
    icp.icmp_cksum = checksum((u_int16_t *)&icp, sizeof(icp));

    size_t packetlen = sizeof(ip) + sizeof(icp) + datalen;
    char packet[packetlen];
    memset(packet, 0, packetlen);
    memcpy((char *)packet, &ip, sizeof(ip));
    memcpy((char *)packet + sizeof(ip), &icp, sizeof(icp));
    memcpy((char *)packet + sizeof(ip) + sizeof(icp), data, packetlen);

    ssize_t snd_ret = sendto(s, packet, packetlen, 0, (const struct sockaddr *)&to, sizeof(to));
    if (errno) {
        fprintf(stderr, "ping: sendto: %s\n", strerror(errno));
        return -1;
    }

    return snd_ret;
}

最佳答案

res 是指向 addrinfo 结构的指针,而不是 sockaddrsockaddrai_addr 成员中,它的长度在ai_addrlen 成员中。这些应该传递给 sendto()

ssize_t snd_ret = sendto(s, packet, packetlen, 0, res->ai_addr, res->ai_addrlen);

关于c - OpenBSD 上原始套接字 icmp 的协议(protocol)族不支持 sendto 地址族,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51903321/

相关文章:

c++ - 计算 ICMP 数据包校验和

C 指向结构的双指针

c++ - 条件中的 Switch 语句和 & 号

c - 在C中用树初始化结构的全局数组

C套接字仅使用二进制数据造成内存泄漏

windows - 在 Windows 上,ICMPv6 回显回复缺少 IP header

c - OpenMP:错误: ‘w.13’ 未在封闭并行中指定

Java ServerSocket 和 Android LocalServerSocket

c++ - pselect() 与循环中的 accept()