IP address order in gethostbyname function
Asked Answered
D

1

7

Since I got no answer on my previous question, I will rephrase it.

What order of IP addresses (in case when IP addresses are bound to one interface) is used when doing gethostbyname() using PC name (NetBIOS name)?

I have this code:

#include <iostream>
#include <winsock.h>
#pragma comment(lib, "Ws2_32.lib")

int main()
{
    char hostname[255];
    struct hostent *he;
    struct in_addr **addr_list;

    WSAData data;
    WSAStartup(MAKEWORD(2, 2), &data);

    gethostname(hostname, 255);
    std::cout << "Host name: " << hostname << std::endl;

    if ((he = gethostbyname(hostname)) == NULL) {
        std::cout << "gethostbyname error" << std::endl;
    } else {
        std::cout << "IP addresses: "  << std::endl;
        addr_list = (struct in_addr **)he->h_addr_list;
        for(int i = 0; addr_list[i] != NULL; i++) {
            std::cout << inet_ntoa(*addr_list[i]) << std::endl;
        }
    }
    std::cin.get();
}

And it gives me different results on Windows Server 2012 and Windows Server 2008 / Windows 7. On my home PC with Windows 7, ascending order is used:

Host name: SplattWin
IP addresses:
192.168.1.140
192.168.3.1
192.168.3.2
192.168.3.3
192.168.3.4

However, on Windows server 2012 it gives me IP addresses in descending order:

Host name: WinServ
IP addresses:
1.1.1.4
1.1.1.3
1.1.1.2
1.1.1.1

Is there any way to reorder it? I tried skipassource flag when I added these IP addresses but it doesn't seem to work in this case.

I have third party software that is using gethostname() followed by gethostbyname() to determine it's own IP address (it takes first from the list). And it's really frustrating that you need to change settings and client side every time you add new IP address to your system.

Decadent answered 12/11, 2014 at 14:3 Comment(0)
O
3

The order of the IPs is determined by Windows based on interface priorities and such. There is no standard rule that spans across machine boundaries or Windows version boundaries. You have to treat the output list as random, and re-order the IPs yourself to your particular needs. For example:

#include <iostream>
#include <vector>
#include <algorithm>
#include <winsock.h>

#pragma comment(lib, "Ws2_32.lib")

bool SortInAddr(const in_addr &a, const in_addr &b)
{
    return (a.S_un.S_addr < b.S_un.S_addr);
} 

int main()
{
    WSAData data;
    WSAStartup(MAKEWORD(2, 2), &data);

    char hostname[256] = {0};
    if (gethostname(hostname, 255) == SOCKET_ERROR)
    {
        std::cout << "gethostname error: " << WSAGetLastError() << std::endl;
    }
    else
    {
        std::cout << "Host name: " << hostname << std::endl;

        struct hostent *he = gethostbyname(hostname);
        if (he == NULL)
        {
            std::cout << "gethostbyname error: " << WSAGetLastError() << std::endl;
        }
        else if (he->h_length != sizeof(in_addr))
        {
            std::cout << "gethostbyname did not return IPv4 addresses" << std::endl;
        }
        else
        {
            std::vector<in_addr> addrs;

            struct in_addr **addr_list = (struct in_addr **)(he->h_addr_list);
            for(int i = 0; addr_list[i] != NULL; ++i)
            {
                addrs.push_back(*(addr_list[i]));
            }

            if (addrs.size() > 1)
            {
                std::sort(addrs.begin(), addrs.end(), SortInAddr);
            }

            std::cout << "IPv4 addresses: " << std::endl;
            for(std::vector<in_addr>::iterator iter = addrs.begin();
                iter != addrs.end();
                ++iter)
            {
                std::cout << inet_ntoa(addrs[i]) << std::endl;
            }
        }
    }

    WSACleanup();
    std::cin.get();
}

That being said, don't use gethostbyname() (or getaddrinfo()) to enumerate a machine's local interfaces. Such functions are not intended for that purpose. Use GetAdaptersInfo() or GetAdaptersAddresses() instead. They are specifically intended to enumerate local interfaces, and they give you much more detailed information about the interfaces.

Ostrich answered 12/11, 2014 at 18:10 Comment(4)
As I said, I have 3rd party software which I don't have control of, that is using first IP from the list returned by gethostbyname(). The question is how to reorder IPs on one particular machine. But thanks for your answer anyway.Decadent
You don't have much/any control over how Windows decides to order its IP addresses. It cares more about the order of the IPs in its routing tables and their priorities, then the order of the interfaces and the IP addresses within them. The best you can do is probably just contact the 3rd party and ask that they tweak their software to manually order the IPs before picking one, similar to what I showed.Ostrich
I think it's easier for me to write external dll with gethostbyname/gethostname hook and inject/patch it, than contacting manufacturer. This software is really old. Thank you anyway. It's just weird that they changed the behavior of such common function.Decadent
gethostbyname() was never intended for enumerating local IPs, it was intended for DNS lookups. And when dealing with DNS results, software should be attempting to connect to every IP that is reported until one succeeds. This is just another example of how people have abused gethostbyname() over the years, and why other enumeration functions exist to replace that abuse. But yes, in your case, it sounds like a detour hook is needed to re-order the IPs that are reported.Ostrich

© 2022 - 2024 — McMap. All rights reserved.