C 语言 : gethostbyaddr() return : 不允许操作

C language : gethostbyaddr() return : Operation not permitted

目前我正在尝试制作一个类似 ping 的程序并且它工作得很好但是我在 DNS_lookup()

上遇到了问题

这是我的函数:

char *DNS_reverse_lookup(struct sockaddr_in *host_addr)
{
    if (host_addr == NULL)
    {
        printf("host_addr is empty.\n");
        return NULL;
    }

    struct hostent *host_info = NULL;
    host_info = gethostbyaddr(&host_addr->sin_addr.s_addr, sizeof(*host_addr), host_addr->sin_family);
    if (host_info == NULL)
    {
        printf("%s:%d : gethostbyaddr() failed : %s\n", __FILE__, __LINE__, strerror(h_errno));
        return NULL;
    }

    return host_info->h_name;
}

这里是 gethostbyaddr() return 某些域不允许操作,例如 google.com 但对于我的 ovh 服务器,它是 return 预期的结果,我不知道为什么

有人可以解释一下吗?

这是我的全部代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <netinet/ip_icmp.h>
#include <signal.h>
#include <ifaddrs.h>

#include "globaldata/gd.h"
#include "netsocket/netsocket.h"

// packet size
#define PING_PKT_S 64

// Automatic port number
#define PORT_NB 0

#define PING_SLEEP_RATE 1000000

// Give the timeout delay for receiving packets
// In seconds
#define RECV_TIMEOUT 2

struct icmp_pkt {
    struct icmphdr header;
    char msg[PING_PKT_S - sizeof(struct icmphdr)];
};

void sigintHandler(int sig_num);
unsigned short checksum(void *b, int len);
struct sockaddr_in *DNS_lookup(char *host_domain_name);
char *DNS_reverse_lookup(struct sockaddr_in *host_addr);
void send_ping(Socket_t *socket, char *ping_ip, char *host_domain_name);

GlobalData gd;

int main(int argc, char *argv[])
{
    /* Declaration */

    // information about the host
    struct sockaddr_in *host_addr = NULL;
    socklen_t host_addr_len = sizeof(*host_addr);
    Socket_t *socket = NULL;
    char *host_domain_name;
    int opt_value = 0, i = 0;
    FILE *socket_file = NULL;
    int arg_port = 0;
    int port = 0;
    struct ifaddrs *ifaces;
    struct ifaddrs *tmp = NULL;

    /* Allocation */

    socket = SOCKET_New();

    /* Initialisation */

    // Correct syntax
    if (argc < 2)
    {
        printf("\nFormat %s <address>\n", argv[0]);
        goto FatalError;
    }

    for (i = 0 ; argv[i] != 0 ; i++)
    {
        if (strcmp(argv[i], "-p") == 0)
        {
            arg_port = i;
        }
    }

    if (arg_port)
    {
        port = atoi(argv[arg_port+1]);
        printf("port = %d\n", port);
    }

    // loop while the ping is performed
    gd.ping_loop = 1;

    // Open socket
    if (SOCKET_OpenSocket(socket, AF_INET, SOCK_RAW, IPPROTO_ICMP) == NULL)
    {
        printf("SOCKET_OpenSocket() failed.\n");
        goto FatalError;
    }

    opt_value = 1;
    if (setsockopt(socket->sockfd, SOL_SOCKET, SO_REUSEADDR, &opt_value, sizeof(opt_value)) == -1)
    {
        printf("setsockopt() failed : %s\n", strerror(errno));
    }

    if (SOCKET_SetAddr(socket, inet_addr(argv[1]), AF_INET, port, 5) == NULL)
    {
        printf("SOCKET_SetAddr() failed?\n");
        goto FatalError;
    }



    // Get host domain name (if there is)
    host_domain_name = DNS_reverse_lookup(&socket->addr);
    if (host_domain_name == NULL)
    {
        printf("DNS_reverse_lookup() failed.\n");
        host_domain_name = 0;
        // goto FatalError;
    }
    printf("host domain name = %s (%s)\n", host_domain_name, argv[1]);

    // printf("\nsockaddr :\nIP = %x\nPort = %d\nFamily = %d\n", socket->addr.sin_addr.s_addr, socket->addr.sin_port, socket->addr.sin_family);
    // if (SOCKET_BindToSocket(socket) == NULL)
    // {
    //     printf("SOCKET_BindToSocket() failed.\n");
    //     goto FatalError;
    // }

    // handle interrupt signal
    signal(SIGINT, sigintHandler); 

    send_ping(socket, argv[1], host_domain_name);

    /* Leave */

    if (socket != NULL)
    {
        if (socket->sockfd > 0)
        {
            shutdown(socket->sockfd, SHUT_RDWR);
            close(socket->sockfd);
            printf("\nsocket closed.\n");
        }
        socket = SOCKET_Free(socket);
    }

    printf("Terminated : Without Error.\n");
    return EXIT_SUCCESS;

FatalError:

    if (socket != NULL)
    {
        if (socket->sockfd > 0)
        {
            shutdown(socket->sockfd, SHUT_RDWR);
            close(socket->sockfd);
            printf("\nsocket closed.\n");
        }
        socket = SOCKET_Free(socket);
    }


    printf("Terminated : Fatal Error.\n");
    return EXIT_FAILURE;
}

void sigintHandler(int sig_num)
{
    gd.ping_loop = 0;
}

unsigned short checksum(void *b, int len)
{
    unsigned short *buf = b;
    unsigned int sum = 0;
    unsigned short result;

    for (sum = 0 ; len > 1 ; len -= 2)
    {
        sum += *buf++;
    }

    if (len == 1)
    {
        sum += *(unsigned char *)buf;
    }
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    result = ~sum;

    return result;
}

// Return struct sockaddr_in *ptr on success or 0 on error
struct sockaddr_in *DNS_lookup(char *host_domain_name)
{
    printf("\nResolving DNS...\n");

    // store host info in struct hostent
    struct hostent *host_info = NULL;
    static struct sockaddr_in host_addr;
    memset(&host_addr, 0, sizeof(host_addr));

    if ((host_info = gethostbyname(host_domain_name)) == NULL)
    {
        printf("%s:%d : gethostbyname() failed %s\n", __FILE__, __LINE__, strerror(h_errno));
        return NULL;
    }

    host_addr.sin_addr.s_addr = inet_addr(host_info->h_addr_list[0]);
    host_addr.sin_family = host_info->h_addrtype;
    host_addr.sin_port = htons(PORT_NB);

    printf("Resolved.\n\n");

    return &host_addr;
}

// Return char *host_domain_name on success or 0 on error
char *DNS_reverse_lookup(struct sockaddr_in *host_addr)
{
    if (host_addr == NULL)
    {
        printf("host_addr is empty.\n");
        return NULL;
    }

    struct hostent *host_info = NULL;
    host_info = gethostbyaddr(&host_addr->sin_addr.s_addr, sizeof(*host_addr), host_addr->sin_family);
    if (host_info == NULL)
    {
        printf("%s:%d : gethostbyaddr() failed : %s\n", __FILE__, __LINE__, strerror(h_errno));
        return NULL;
    }

    return host_info->h_name;
}

void send_ping(Socket_t *socket, char *ping_ip, char *host_domain_name)
{
    if (socket == NULL)
    {
        printf("%s:%d : Bad socket.\n", __FILE__, __LINE__);
        return;
    }
    if (socket->sockfd <= 0)
    {
        printf("%s:%d : Bad socket file descriptor.\n", __FILE__, __LINE__);
    }

    int ttl_value, msg_count, i, addr_len, flag, msg_received_count;
    struct icmp_pkt packet;
    struct sockaddr_in r_addr;

    long double rtt_msec, total_msec;
    struct timespec time_start, time_end, tfs, tfe;
    struct timeval tv_out;
    tv_out.tv_sec = RECV_TIMEOUT;
    tv_out.tv_usec = 0;

    ttl_value = 64;
    msg_count = 0;
    i = 0;
    addr_len = 0;
    flag = 1;
    msg_received_count = 0;
    rtt_msec = total_msec = 0.0;

    // time start
    clock_gettime(CLOCK_MONOTONIC, &tfs);

    // SOL = Set Option Level
    // SO = Set Option
    // Set time to live value
    if (setsockopt(socket->sockfd, SOL_IP, IP_TTL, &ttl_value, sizeof(ttl_value)) == -1)
    {
        printf("%s:%d : setsockopt() failed : %s\n", __FILE__, __LINE__, strerror(errno));
        return;
    }
    printf("Socket set to TTL : %d\n", ttl_value);

    // Set timeout of receiving setting
    if (setsockopt(socket->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv_out, sizeof(tv_out)) == -1)
    {
        printf("%s:%d : setsockopt() failed : %s\n", __FILE__, __LINE__, strerror(errno));
        return;
    }

    printf("Ping to %s (%s).\n", host_domain_name, ping_ip);

    while (gd.ping_loop)
    {
        // flag is whether packet was sent or not
        flag = 1;

        // filling packet
        memset(&packet, 0, sizeof(packet));

        packet.header.type = ICMP_ECHO;
        packet.header.un.echo.id = getpid();

        for (i = 0 ; i < sizeof(packet.msg) ; i++)
        {
            packet.msg[i] = '[=12=]';
        }

        char message[] = "Anything";
        strncpy(packet.msg, message, strlen(message));

        packet.header.un.echo.sequence = msg_count++;
        packet.header.checksum = checksum(&packet, sizeof(packet));

        usleep(PING_SLEEP_RATE);

        // send packet
        clock_gettime(CLOCK_MONOTONIC, &time_start);
        // if (write(socket->sockfd, &packet, sizeof(packet)) == -1)
        // {
        //     printf("%s:%d : write() failed : %s\n", __FILE__, __LINE__, strerror(errno));
        //     flag = 0;
        // }

        if (sendto(socket->sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&socket->addr, socket->addr_len) == -1)
        {
            printf("%s:%d : sento() failed : %s\n", __FILE__, __LINE__, strerror(errno));
            return;
        }

        // receiving packet

        // printf("reading packet...\n");
        // if (read(socket->sockfd, &packet, sizeof(packet)) == -1 && msg_count > 1)
        // {
        //     printf("%s:%d : read() failed : %s\n", __FILE__, __LINE__, strerror(errno));
        //     flag = 0;
        // }
        if (recvfrom(socket->sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&socket->addr, &socket->addr_len) == -1)
        {
            printf("%s:%d : recvfrom() failed : %s\n", __FILE__, __LINE__, strerror(errno));
            return;
        }

        else
        {
            clock_gettime(CLOCK_MONOTONIC, &time_end);

            double time_elapsed = ((double)time_end.tv_nsec - (double)time_start.tv_nsec) / 1000000.0;
            printf("time elapsed = %.4f\n", time_elapsed);

            rtt_msec = (double)(time_end.tv_sec - time_start.tv_sec) * 1000.0 + time_elapsed;

            // case packet wasn't sent or received
            if (!flag)
            {
                printf("%s:%d : ERROR : Packet received with ICMP type %d code %d\n", __FILE__, __LINE__, packet.header.type, packet.header.code);
            }else{
                printf("%d bytes from %d (h: %s) (%s) msg_seq=%d ttl=%d rtt = %Lf ms.\n", PING_PKT_S, socket->addr.sin_family, host_domain_name, ping_ip, msg_count, ttl_value, rtt_msec);
                msg_received_count++;
            }
        }
    }

    clock_gettime(CLOCK_MONOTONIC, &tfe);
    double time_elapsed = ((double)(tfe.tv_nsec - tfs.tv_nsec)) / 1000000.0;
    total_msec = (tfe.tv_sec - tfs.tv_sec) * 1000 + time_elapsed;

    printf("\n===%s ping statistics===\n", ping_ip);
    printf("\n%d packets sent, %d packets received, %f percent packet loss. Total time: %Lf ms.\n\n", msg_count, msg_received_count, (double)((msg_count - msg_received_count)/msg_count) * 100.0, total_msec); 

}

终端执行:

kali@kali:~/Code/Network/Ping$ sudo ./pingto 151.101.65.69
main.c:232 : gethostbyaddr() failed : Operation not permitted
DNS_reverse_lookup() failed.
host domain name = (null) (151.101.65.69)
Socket set to TTL : 64
Ping to (null) (151.101.65.69).
time elapsed = 8.1787
64 bytes from 2 (h: (null)) (151.101.65.69) msg_seq=1 ttl=64 rtt = 8.178748 ms.
time elapsed = 7.9054
64 bytes from 2 (h: (null)) (151.101.65.69) msg_seq=2 ttl=64 rtt = 7.905438 ms.
time elapsed = 8.2425
64 bytes from 2 (h: (null)) (151.101.65.69) msg_seq=3 ttl=64 rtt = 8.242489 ms.
^Ctime elapsed = 7.9805
64 bytes from 2 (h: (null)) (151.101.65.69) msg_seq=4 ttl=64 rtt = 7.980547 ms.

===151.101.65.69 ping statistics===

4 packets sent, 4 packets received, 0.000000 percent packet loss. Total time: 3494.951433 ms.


socket closed.
Terminated : Without Error.


您没有正确解码错误代码。

h_errno的值与errno对应的值不对应,所以无法使用strerror获取错误文本。

有一个名为 hstrerror 的函数,其功能与 strerror 相同,但用于 gethostbyname 和其他相关函数返回的错误代码。

手册页中列出的错误代码如下:

   HOST_NOT_FOUND
          The specified host is unknown.

   NO_ADDRESS or NO_DATA
          The requested name is valid but does not have an IP address.

   NO_RECOVERY
          A nonrecoverable name server error occurred.

   TRY_AGAIN
          A temporary error occurred on an authoritative name server.  Try
          again later.

“不允许操作”代码是 EPERM,在我的系统上是 1。该代码在我的系统上对应于 HOST_NOT_FOUND,所以假设您的错误代码与实际的相同您看到的错误。