Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Client in C++, use gethostbyname or getaddrinfo

Tags:

c++

sockets

I found the following code to open a connection in C:

int OpenConnection(const char *hostname, int port)
{
    int sd;
    struct hostent *host;
    struct sockaddr_in addr = {0};
    if ((host = gethostbyname(hostname)) == NULL)
    {
        perror(hostname);
        abort();
    }

    sd = socket(PF_INET, SOCK_STREAM, 0);

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = *(long *)(host->h_addr_list[0]);
    if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
    {
        close(sd);
        perror(hostname);
        abort();
    }
    return sd;
}

As I was rewriting this code in C++, I found out that I should be using getaddrinfo instead of gethostbyname, according to a comment in this post: C - What does *(long *)(host->h_addr); do?.

I was looking at Beej's guide to socket programming, and found the following example:

int status;
struct addrinfo hints;
struct addrinfo *servinfo;  // will point to the results

memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC;     // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
    exit(1);
}

// servinfo now points to a linked list of 1 or more struct addrinfos

// ... do everything until you don't need servinfo anymore ....

freeaddrinfo(servinfo); // free the linked-list

However, this example applies to creating a server, which makes me wonder

A] is getaddrinfosupposed to be used for clients

and

B] How would I modify the hints struct and the function parameters in order to make this code suitable for creating a client?

Currently, I have the following code but I

struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

struct addrinfo *client_info;

const int status = getaddrinfo(hostname, port, &hints, &client_info); //don't know if its correct

Update:

For anyone who may find it useful, here is my final code after reading the accepted answer:

int OpenConnection(const char *hostname, const char *port)
{
    struct hostent *host;
    if ((host = gethostbyname(hostname)) == nullptr)
    {
        //More descriptive error message?
        perror(hostname);
        exit(EXIT_FAILURE);
    }

    struct addrinfo hints = {0}, *addrs;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    const int status = getaddrinfo(hostname, port, &hints, &addrs);
    if (status != 0)
    {
        fprintf(stderr, "%s: %s\n", hostname, gai_strerror(status));
        exit(EXIT_FAILURE);
    }

    int sfd, err;
    for (struct addrinfo *addr = addrs; addr != nullptr; addr = addr->ai_next)
    {
        sfd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
        if (sfd == ERROR_STATUS)
        {
            err = errno;
            continue;
        }

        if (connect(sfd, addr->ai_addr, addr->ai_addrlen) == 0)
        {
            break;
        }

        err = errno;
        sfd = ERROR_STATUS;
        close(sfd);
    }

    freeaddrinfo(addrs);

    if (sfd == ERROR_STATUS)
    {
        fprintf(stderr, "%s: %s\n", hostname, strerror(err));
        exit(EXIT_FAILURE);
    }

    return sfd;
}
like image 652
Roymunson Avatar asked Oct 09 '18 18:10

Roymunson


People also ask

What is the use of Gethostbyname?

The gethostbyname function returns a pointer to a hostent structure—a structure allocated by Windows Sockets. The hostent structure contains the results of a successful search for the host specified in the name parameter.

Which service does Gethostbyname internally consult?

The domain name queries carried out by gethostbyname() and gethostbyaddr() rely on the Name Service Switch (nsswitch. conf(5)) configured sources or a local name server (named(8)). The default action is to query the Name Service Switch (nsswitch.

How does socket Gethostbyname work?

This program uses the socket call, gethostbyname to return an IP address that corresponds to the supplied hostname. gethostbyname will determine if a nameserver or local host tables are being used for name resolution. The answer is returned in the hostent structure, hp and then printed.

What is struct Hostent?

The hostent structure is used by functions to store information about a given host, such as host name, IPv4 address, and so forth. An application should never attempt to modify this structure or to free any of its components.


Video Answer


2 Answers

The gethostbyname() and gethostbyaddr() functions are deprecated on most platforms, and they don't implement support for IPv6. IPv4 has reached its limits, the world has been moving to IPv6 for awhile now. Use getaddrinfo() and getnameinfo() instead, respectively.

To answer your questions:

A. getaddrinfo() and getnameinfo() can be used for clients and servers alike, just as gethostbyname() and gethostbyaddr() can be. They are just host/address resolution functions, how the resolved values get used is up to the calling app to decide.

B. client code using getaddrinfo() would look something like this:

int OpenConnection(const char *hostname, int port)
{
    int sd, err;
    struct addrinfo hints = {}, *addrs;
    char port_str[16] = {};

    hints.ai_family = AF_INET; // Since your original code was using sockaddr_in and
                               // PF_INET, I'm using AF_INET here to match.  Use
                               // AF_UNSPEC instead if you want to allow getaddrinfo()
                               // to find both IPv4 and IPv6 addresses for the hostname.
                               // Just make sure the rest of your code is equally family-
                               // agnostic when dealing with the IP addresses associated
                               // with this connection. For instance, make sure any uses
                               // of sockaddr_in are changed to sockaddr_storage,
                               // and pay attention to its ss_family field, etc...
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    sprintf(port_str, "%d", port);

    err = getaddrinfo(hostname, port_str, &hints, &addrs);
    if (err != 0)
    {
        fprintf(stderr, "%s: %s\n", hostname, gai_strerror(err));
        abort();
    }

    for(struct addrinfo *addr = addrs; addr != NULL; addr = addr->ai_next)
    {
        sd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (sd == -1)
        {
            err = errno;
            break; // if using AF_UNSPEC above instead of AF_INET/6 specifically,
                   // replace this 'break' with 'continue' instead, as the 'ai_family'
                   // may be different on the next iteration...
        }

        if (connect(sd, addr->ai_addr, addr->ai_addrlen) == 0)
            break;

        err = errno;

        close(sd);
        sd = -1;
    }

    freeaddrinfo(addrs);

    if (sd == -1)
    {
        fprintf(stderr, "%s: %s\n", hostname, strerror(err));
        abort();
    }

    return sd;
}
like image 68
Remy Lebeau Avatar answered Sep 17 '22 16:09

Remy Lebeau


I've always used gethostbyname() since "forever". It's always worked, it continues to work, and it's "simpler".

getaddrinfo() is the newer function:

http://man7.org/linux/man-pages/man3/getaddrinfo.3.html

The getaddrinfo() function combines the functionality provided by the gethostbyname(3) and getservbyname(3) functions into a single interface, but unlike the latter functions, getaddrinfo() is reentrant and allows programs to eliminate IPv4-versus-IPv6 dependencies.

I understand that getaddrinfo() ismore robust, more efficient, and more secure: You shouldn't be using gethostbyname() anyway

ADDENDUM:

In reply to your specific questions:

A] getaddrinfo() is preferred over gethostbyname() to lookup the IP address of a hostname; either "client" or "server".

B] Q: How would I modify the hints struct and the function parameters?

A: The "hints" look OK, but I would probably modify the port to NULL.

Here's a complete example:

https://www.kutukupret.com/2009/09/28/gethostbyname-vs-getaddrinfo/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    struct addrinfo hints, *res, *p;
    int status;
    char ipstr[INET6_ADDRSTRLEN];

    if (argc != 2) {
       fprintf(stderr, "Usage: %s hostname\n", argv[0]);
       return 1;
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
    hints.ai_socktype = SOCK_STREAM;

    if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
        return 2;
    }

    for(p = res;p != NULL; p = p->ai_next) {
        void *addr;
        if (p->ai_family == AF_INET) {
            return 1;  
        } else {
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
            addr = &(ipv6->sin6_addr);

            /* convert the IP to a string and print it: */
            inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);             
            printf("Hostname: %s\n", argv[1]);
            printf("IP Address: %s\n", ipstr);
        }
    }

    freeaddrinfo(res); // free the linked list     
    return 0;
}
like image 32
paulsm4 Avatar answered Sep 17 '22 16:09

paulsm4