开发者

Validate IP Address (host) exists prior to connecting (ping)

开发者 https://www.devze.com 2023-02-22 14:54 出处:网络
I have a problem where I need to determine if the host exists prior to connecting to it. This host does not work with the function gethostbyaddr() because it is not PC-based and does not return host i

I have a problem where I need to determine if the host exists prior to connecting to it. This host does not work with the function gethostbyaddr() because it is not PC-based and does not return host information. It is IP-based only. Whenever I try to call gethostbyaddr() on the IP address, WinSock returns 11004 (WSANODATA).

Is there a similar function (besides ping) to determine if an IP is valid before trying to c开发者_如何转开发onnect?


If you have some kind of control over the destination host, one way you could periodically check if the host is present without using up ephemeral ports would be to send a UDP datagram, and wait for the ICMP response to tell you that the datagram was refused by the host.

You do this by creating a SOCK_DGRAM socket, binding to a local port, and calling sendto() to send to a known remote port which is not listening. You can then poll and call recvfrom() which should give an error if your host got the ICMP response back. If the host is not up then you will not get the response. You can reuse the same socket with the same port to send as many datagrams as are required periodically.

Sending ICMP echo request requires high privileges on most system, so is hard to do directly from your code.

Here is some sample code which does roughly what I describe:

struct sockaddr_in local_address;
struct sockaddr_in remote_address;
int sfd;
char * remote_host;
int s;
fd_set fds;
struct timeval timeout;

remote_host = argv[1];

sfd = socket(AF_INET, SOCK_DGRAM, 0);


if (sfd < 0) {
    perror("socket");
}

memset(&local_address, 0, sizeof(struct sockaddr_in));
local_address.sin_family = AF_INET;
local_address.sin_addr.s_addr = INADDR_ANY;
local_address.sin_port = htons(6799);

s = bind(sfd,
         (struct sockaddr*)&local_address,
         sizeof(local_address));

if (s != 0) {
    perror("bind");
    exit(1);
}

memset(&remote_address, 0, sizeof(struct sockaddr_in));
remote_address.sin_family = AF_INET;
remote_address.sin_addr.s_addr = inet_addr(remote_host);
remote_address.sin_port = htons(6799);

s = sendto(sfd,
           "MSG",
           3,
           0,
           (struct sockaddr*)&remote_address,
           sizeof(remote_address));

if (s != 3) {
    perror("sento");
    exit(1);
}

FD_ZERO(&fds);
FD_SET(sfd, &fds);

timeout.tv_sec = 5;
timeout.tv_usec = 0;

s = select(sfd + 1, &fds, 0, 0, &timeout);

if (s == 1) {
    char buf[512];

    printf("Got data, host is up\n");

    s = recvfrom(sfd, &buf[0], 512, 0, 0, 0);

    perror("recvfrom");
} else {
    printf("Timeout, host is down\n");
}


I solved the problem by using the built-in Windows API for PING. I changed the gethostbyname() to inet_addr.

shown here: ICMP.DLL Method

dllping.cpp

// Borland C++ 5.0: bcc32.cpp ping.cpp
// Visual C++ 5.0:  cl ping.cpp wsock32.lib
//
// This sample program is hereby placed in the public domain.

#include <iostream.h>
#include <winsock.h>
#include <windowsx.h>
#include "icmpdefs.h"

int doit(int argc, char* argv[])
{
    // Check for correct command-line args
    if (argc < 2) {
        cerr << "usage: ping <host>" << endl;
        return 1;
    }

    // Load the ICMP.DLL
    HINSTANCE hIcmp = LoadLibrary("ICMP.DLL");
    if (hIcmp == 0) {
        cerr << "Unable to locate ICMP.DLL!" << endl;
        return 2;
    }

    // Look up an IP address for the given host name
    struct hostent* phe;
    if ((phe = gethostbyname(argv[1])) == 0) {
        cerr << "Could not find IP address for " << argv[1] << endl;
        return 3;
    }

    // Get handles to the functions inside ICMP.DLL that we'll need
    typedef HANDLE (WINAPI* pfnHV)(VOID);
    typedef BOOL (WINAPI* pfnBH)(HANDLE);
    typedef DWORD (WINAPI* pfnDHDPWPipPDD)(HANDLE, DWORD, LPVOID, WORD,
            PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD); // evil, no?
    pfnHV pIcmpCreateFile;
    pfnBH pIcmpCloseHandle;
    pfnDHDPWPipPDD pIcmpSendEcho;
    pIcmpCreateFile = (pfnHV)GetProcAddress(hIcmp,
            "IcmpCreateFile");
    pIcmpCloseHandle = (pfnBH)GetProcAddress(hIcmp,
            "IcmpCloseHandle");
    pIcmpSendEcho = (pfnDHDPWPipPDD)GetProcAddress(hIcmp,
            "IcmpSendEcho");
    if ((pIcmpCreateFile == 0) || (pIcmpCloseHandle == 0) || 
            (pIcmpSendEcho == 0)) {
        cerr << "Failed to get proc addr for function." << endl;
        return 4;
    }

    // Open the ping service
    HANDLE hIP = pIcmpCreateFile();
    if (hIP == INVALID_HANDLE_VALUE) {
        cerr << "Unable to open ping service." << endl;
        return 5;
    }

    // Build ping packet
    char acPingBuffer[64];
    memset(acPingBuffer, '\xAA', sizeof(acPingBuffer));
    PIP_ECHO_REPLY pIpe = (PIP_ECHO_REPLY)GlobalAlloc(
            GMEM_FIXED | GMEM_ZEROINIT,
            sizeof(IP_ECHO_REPLY) + sizeof(acPingBuffer));
    if (pIpe == 0) {
        cerr << "Failed to allocate global ping packet buffer." << endl;
        return 6;
    }
    pIpe->Data = acPingBuffer;
    pIpe->DataSize = sizeof(acPingBuffer);      

    // Send the ping packet
    DWORD dwStatus = pIcmpSendEcho(hIP, *((DWORD*)phe->h_addr_list[0]), 
            acPingBuffer, sizeof(acPingBuffer), NULL, pIpe, 
            sizeof(IP_ECHO_REPLY) + sizeof(acPingBuffer), 5000);
    if (dwStatus != 0) {
        cout << "Addr: " <<
                int(LOBYTE(LOWORD(pIpe->Address))) << "." <<
                int(HIBYTE(LOWORD(pIpe->Address))) << "." <<
                int(LOBYTE(HIWORD(pIpe->Address))) << "." <<
                int(HIBYTE(HIWORD(pIpe->Address))) << ", " <<
                "RTT: " << int(pIpe->RoundTripTime) << "ms, " <<
                "TTL: " << int(pIpe->Options.Ttl) << endl;
    }
    else {
        cerr << "Error obtaining info from ping packet." << endl;
    }

    // Shut down...
    GlobalFree(pIpe);
    FreeLibrary(hIcmp);
    return 0;
}

int main(int argc, char* argv[])
{
    WSAData wsaData;
    if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
        return 255;
    }

    int retval = doit(argc, argv);

    WSACleanup();
    return retval;
}

icmpdefs.h

// Structures required to use functions in ICMP.DLL

typedef struct {
    unsigned char Ttl;                         // Time To Live
    unsigned char Tos;                         // Type Of Service
    unsigned char Flags;                       // IP header flags
    unsigned char OptionsSize;                 // Size in bytes of options data
    unsigned char *OptionsData;                // Pointer to options data
} IP_OPTION_INFORMATION, * PIP_OPTION_INFORMATION;

typedef struct {
    DWORD Address;                             // Replying address
    unsigned long  Status;                     // Reply status
    unsigned long  RoundTripTime;              // RTT in milliseconds
    unsigned short DataSize;                   // Echo data size
    unsigned short Reserved;                   // Reserved for system use
    void *Data;                                // Pointer to the echo data
    IP_OPTION_INFORMATION Options;             // Reply options
} IP_ECHO_REPLY, * PIP_ECHO_REPLY;


Here you can find the source of a short DNS resolver in C++.


DNS queries are not going to help you to establish whether the box is up (which is what you seem to be trying to do).

If you can run a process on the target box, you could run a heartbeat service of some sort, which would accept a TCP connection from the monitoring app, and send an "I'm alive" message every 2.5 seconds. The inability to connect or the lack of heartbeats would tell your monitoring app that there's a problem.

Alternatively (and perhaps more straightforwardly), why not use ICMP ping?


If you're only allowed a certain number of ephemeral ports, stop using ephemeral ports. Bind the source socket to a known port number before using it to attempt to connect to the other machine.

Alternatively, you don't say why you want to avoid ping. If it's just about doing it in code, you can generate an ICMP packet yourself and use that.

0

精彩评论

暂无评论...
验证码 换一张
取 消