This series covers Internet domain sockets from TLPI Chapter 59. Each part focuses on one concept so you can learn step by step.
When you type www.google.com in a browser, your computer needs to find the actual IP address (like 142.250.183.4) behind that name. This process is called host name resolution.
In C network programming, the traditional function to do this is gethostbyname(). It takes a hostname string and returns a structure packed with all the information about that host — its real (canonical) name, any aliases, and its IP addresses.
Note: gethostbyname() is old and considered obsolete today. The modern replacement is getaddrinfo(). But you will see gethostbyname() in older codebases, so it is important to understand it.
When your program calls gethostbyname("www.example.com"), here is what happens behind the scenes:
calls gethostbyname()
checks /etc/hosts first
queries if not cached
struct hostent*
The C library first checks /etc/hosts for a local mapping, then checks /etc/resolv.conf to find the DNS server, and finally sends a UDP query to the DNS server to resolve the name.
Include <netdb.h> to use these functions:
#include <netdb.h>
struct hostent *gethostbyname(const char *name);
/* Error reporting helpers */
void herror(const char *str);
const char *hstrerror(int err);
/* On success: returns pointer to a statically allocated hostent structure
On failure: returns NULL and sets h_errno (not errno) */
Important: Unlike most C functions that set errno on failure, gethostbyname() sets a separate global variable called h_errno. You must use hstrerror(h_errno) or herror() to print the error — not strerror(errno).
When gethostbyname() succeeds it returns a pointer to this structure:
struct hostent {
char *h_name; /* Official (canonical) name of the host */
char **h_aliases; /* NULL-terminated array of alias name */
int h_addrtype; /* Address type: AF_INET or AF_INET6 */
int h_length; /* Length of each address in bytes */
char **h_addr_list; /* NULL-terminated array of IP addresses */
/* (in network byte order) */
};
/* Convenience macro (for compatibility with old code) */
#define h_addr h_addr_list[0] /* First address in the list */
Key point: Both h_aliases and h_addr_list are arrays of pointers that end with a NULL pointer. You loop through them until you hit NULL.
This program takes hostnames as command-line arguments and prints all information returned by gethostbyname():
/* t_gethostbyname.c
* Usage: ./t_gethostbyname www.google.com www.example.com
*
* Compile: gcc -o t_gethostbyname t_gethostbyname.c
*/
#define _BSD_SOURCE /* Needed to expose hstrerror() in <netdb.h> */
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h> /* gethostbyname, struct hostent, h_errno */
#include <netinet/in.h> /* struct in_addr, struct in6_addr */
#include <arpa/inet.h> /* inet_ntop() */
int main(int argc, char *argv[])
{
struct hostent *h;
char **pp;
char str[INET6_ADDRSTRLEN]; /* Big enough for IPv6 address string */
if (argc < 2) {
fprintf(stderr, "Usage: %s hostname...\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Loop through each hostname given on command line */
for (argv++; *argv != NULL; argv++) {
h = gethostbyname(*argv);
if (h == NULL) {
/* h_errno is set, NOT errno — use hstrerror() */
fprintf(stderr, "gethostbyname() failed for '%s': %s\n",
*argv, hstrerror(h_errno));
continue; /* Try next hostname */
}
/* Print canonical name (official DNS name) */
printf("Canonical name: %s\n", h->h_name);
/* Print all aliases (e.g. www.example.com -> example.com) */
printf(" alias(es): ");
for (pp = h->h_aliases; *pp != NULL; pp++)
printf(" %s", *pp);
printf("\n");
/* Print address family */
printf(" address type: %s\n",
(h->h_addrtype == AF_INET) ? "AF_INET" :
(h->h_addrtype == AF_INET6) ? "AF_INET6" : "???");
/* Print all IP addresses */
if (h->h_addrtype == AF_INET || h->h_addrtype == AF_INET6) {
printf(" address(es): ");
for (pp = h->h_addr_list; *pp != NULL; pp++) {
/* inet_ntop converts binary IP to printable string */
printf(" %s",
inet_ntop(h->h_addrtype, *pp,
str, INET6_ADDRSTRLEN));
}
printf("\n");
}
}
exit(EXIT_SUCCESS);
}
Sample output:
$ ./t_gethostbyname www.jambit.com
Canonical name: jamjam1.jambit.com
alias(es): www.jambit.com
address type: AF_INET
address(es): 62.245.207.90
gethostbyname() returns a pointer to a statically allocated structure. Each call overwrites the previous result. Never call it from two threads simultaneously. Use getaddrinfo() in multi-threaded programs.On failure,
gethostbyname() sets h_errno. Common values: HOST_NOT_FOUND, TRY_AGAIN, NO_RECOVERY, NO_ADDRESS. Use hstrerror(h_errno) to get a string.The addresses in
h_addr_list are raw binary in network byte order. Use inet_ntop() to convert to a readable string. Do not try to print them directly.gethostbyname() does not support IPv6 properly in a single call. The modern API is getaddrinfo(), which is reentrant, protocol-independent, and handles both IPv4 and IPv6.A focused example showing how to get just the first IP address:
#define _BSD_SOURCE
#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(void)
{
const char *hostname = "www.example.com";
struct hostent *h;
char buf[INET_ADDRSTRLEN];
h = gethostbyname(hostname);
if (h == NULL) {
herror("gethostbyname"); /* herror() prints to stderr automatically */
return 1;
}
/* h_addr is a macro for h_addr_list[0] */
printf("First IP of %s: %s\n",
hostname,
inet_ntop(AF_INET, h->h_addr, buf, sizeof(buf)));
return 0;
}
/* Expected output (actual IP may differ):
First IP of www.example.com: 93.184.216.34
*/
These are set in h_errno when gethostbyname() returns NULL:
| h_errno Value | Meaning |
|---|---|
HOST_NOT_FOUND |
The hostname does not exist in DNS |
TRY_AGAIN |
Temporary DNS failure — try again later |
NO_RECOVERY |
Permanent DNS server error |
NO_ADDRESS |
Host exists in DNS but has no IP address record |
It returns NULL and sets the global variable h_errno. To get the error string, call hstrerror(h_errno) or call herror("message") which prints to stderr automatically.
It returns a pointer to a statically allocated struct hostent that is shared across all threads. A second thread calling gethostbyname() overwrites the same buffer, corrupting the result in the first thread. Use getaddrinfo() instead, which allocates new memory per call and is thread-safe (reentrant).
h_name is the official canonical DNS name of the host (e.g., jamjam1.jambit.com). h_aliases is a NULL-terminated array of alternative names (aliases) for the same host (e.g., www.jambit.com). When you query by an alias, the canonical name may differ from what you searched for.
The addresses in h_addr_list are raw binary in network byte order. Use inet_ntop(h->h_addrtype, *pp, str, sizeof(str)). Pass h_addrtype (either AF_INET or AF_INET6) so inet_ntop knows the format.
getaddrinfo() is the modern replacement. It is preferred because it is thread-safe (allocates memory per call), works for both IPv4 and IPv6 in a single call, is protocol-independent, and also integrates service (port) name resolution in the same call.
It expands to h_addr_list[0], which is the first IP address in the list. It was provided as a backward-compatible convenience macro since older code often assumed a host had only one address.
Learn how getservbyname() and getservbyport() map service names like “http” or “ftp” to port numbers.
