简体   繁体   中英

sendto Address family not supported by protocol family for raw socket icmp on OpenBSD

I'm trying to write a ping function for an ICMP library. Everything seems to work up until sendto , when it returns Address family not supported by protocol family . I don't understand the error. What am I doing wrong?

#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 is a pointer to an addrinfo structure, not a sockaddr . The sockaddr is in the ai_addr member, and its length is in the ai_addrlen member. These should be passed to sendto() .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM