To open a socket client-server, we need some important information about the server that we want to connect to such as the domain address, the address family it uses, etc.
This connection process requires the use of several functions, and the call to each of these has a specific order that must be strictly followed. Many of these functions are used to retrieve the data from the server that you want to connect to. Their results are some of the input arguments for the subsequent function.
These arguments are descriptors and data structures that contain the client and server-specific information about some of the layers that make up a network connection.
In this Linux Hint article, you will learn how to use the getaddrinfo() function to resolve the IP address of a host name and obtain the necessary information in the data structures that the various C functions use to connect to a remote server.
We will see a description of the syntax, arguments, and input/output structures that make up this function as well as a theoretical explanation of how it works. Then, we will apply what we learned in a practical example that includes code snippets and images that show how to use the getaddrinfo() function in C.
Using this function requires an understanding of the data structures that make up its input arguments. For this reason, we included a special section in this article that describes its composition, the type of data of its members, the parameter that is set by each of them, and the modification of this parameter.
Syntax of Getaddrinfo() Function in C Language
const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
Description of the Getaddrinfo() Function in the C Language
The getaddrinfo() function resolves the IP address of a domain and performs an information crossing with the server for the basic data that is needed to open a socket and establish a communication with it.
The IP address that is resolved by this function is stored in structures of sockaddr type, the same type that is used by the bind() and connect() functions to establish the connection.
To better understand how getaddrinfo() works, we need to know what its input arguments are. The following is a list of these arguments along with an explanation of the function that each of them performs within getaddrinfo():
node: A string, or a pointer to it, that specifies the domain name or IP address that we want to get the information from. If node is a hostname, getaddrinfo() resolves to its IP address. This input argument accepts an IPv4, IPv6, or domain name.
service: A string or a pointer to it which specifies the type of service or the port number through which the socket should be connected. This input argument can be passed as null as long as the node specifies a valid hostname or address.
hints: This input argument is a structure of addrinfo type where each of its members specifies the domain name and the type of connection to be made to the server. These parameters provide information about, for example, the transport protocol, the IP version we intend to use, etc. In this way, the information that is provided by the server is targeted to this type of connection, if possible. Later, we will see a section about this structure, the individual parameters that are established by its members, and how to configure the connection through them.
res: This argument is a structure of addrinfo type in which the information that is returned by the server is stored. Res contains a structure and a pointer to this structure or a list of structures of sockaddr type which store the address of the hostname which is sent in node and resolved by the server. This structure contains the same information type of hints in its members, but with the data from the server. This information is then used as an input argument to the bind() and connect() functions to establish the connection.
In cases where the server returns more than one address, each can be accessed with the ai_next pointer which is a member of the res structure.
If the getaddrinfo() function successfully obtains the information, it returns 0. If an error occurs, this function returns the code from a list which is defined in the “netdb.h” header.
Later, you will find a section that deals with errors when using this function and the definitions with their respective numeric representations which are returned by getaddrinfo().
The getaddrinfo() function is defined in the “netdb.h” header. To use it, it must be included in our code as follows:
How to Resolve the IP Address of a Domain Using the Getaddrinfo() Function in C
In this example, we will explain how to resolve the IP address of a domain and get the necessary parameters from the server in the addrinfo and sockaddr structures to establish a client-server connection to it.
The first step is to define the structures and variables that are needed for the input and output arguments of getaddrinfo(). Next, let us look at the definition of the variables that we will use in this example and the correct way to declare the addrinfo structures:
char hostname[100] = "www.linuxhints.com";
struct addrinfo hints_1, *res_1;
The error variable is the output argument of getaddrinfo(). We use it to detect the possible errors. The hostname character array contains the string with the domain name and is the input argument, node.
The hints_1 “suggests” to the server the type of connection that we want to make. Res_1 is the structure that getaddrinfo() returns with the server data and domain address.
It is important that you set all the parameters of the hints_1 structure to null characters. In this example, we use the memset() function to set the space that is allocated to this structure to null character.
Once the variables and structures are defined, we call the getaddrinfo() function and pass the hostname array as an input argument, node, with the domain name whose IP address we want to get. In this case, we use the address of our website – “www .linuxhint.com”
In the input argument service, we can specify that the requested service is “http” or the port number 80 which is the same.
The third and fourth input arguments are the structures hints_1 and res_1, respectively.
The output argument is error which we use to determine whether getaddrinfo() returns with errors or performs the operation successfully.
If getaddrinfo() returns successfully, the structures that are pointed to by res_1 contain the IP address corresponding to the specified domain and any server parameters needed by the bind() and connect() functions in the sockaddr type structures to connect to it.
To find out if an error occurs, we output the result in the command console. Next, we see the full code for this example which includes the headers, defines the variables and structures, and calls the getaddrinfo() function.
#include <string.h>
#include <netdb.h>
int main ()
{
int error;
char hostname [100] = "www.linuxhints.com";
struct addrinfo hints_1, *res_1;
memset(&hints_1, '\0', sizeof(hints_1));
error = getaddrinfo( hostname, "80", &hints_1, &res_1);
printf ("\nError: %i\n", error);
}
As we can see in the following image, the getaddrinfo function returns without errors:
How to Convert the IP Address Returned by the Server in an Addrinfo Struct Structure to String
In many cases, it is useful to convert the IP address that is returned by the server when the getaddrinf() function is called to a string. This address is stored in the 14-element character array, sa_data, in the sockaddr structure which is a member of the res structure of addrinfo type.
Although the address is stored in an array of elements of char type, they have no character format, but a numeric representation of integers from 0 to 255.
In the IP v4 family, the first value of the sa_data array is always zero and the second value is the port number. So, the elements that we need to convert are 2, 3, 4, and 5.
We perform the conversion using the inet_ntop() function. This function converts the binary numbers in the sa_data array to characters and returns them in the ip array.
Since the bytes that specifies the IP number start with byte number 2, we refer to this address as a pointer to sa_data in the src input argument of the inet_ntop() function.
unsigned char ip[50]= "";
if (error ==0){
inet_ntop(AF_INET, &res_1->ai_addr->sa_data[2], ip, sizeof(ip));
printf ("IP address: %s\n\n", ip);
}
If we insert this fragment at the end of the code of the previous example, we see the IP corresponding to the hostname that we send to the server to resolve its address.
#include <string.h>
#include <netdb.h>
int main ()
{
int a;
unsigned char b[5]= "";
unsigned char ip[50]= "";
int error;
char hostname [100] = "www.linuxhints.com";
struct addrinfo hints_1, *res_1;
memset(&hints_1, '\0', sizeof(hints_1));
error = getaddrinfo( hostname, "80", &hints_1, &res_1);
printf ("\nError: %i\n", error);
#include <arpa/inet.h>
unsigned char ip[50]= "";
if (error ==0){
inet_ntop(AF_INET, &res_1->ai_addr->sa_data[2], ip, sizeof(ip));
printf ("IP address: %s\n\n", ip);
}
}
In the following figure, we see the IP address corresponding to the hostname that we retrieve from the addrinfo res_1 structure and convert to a string for inet_ntop():
Addrinfo Structure
The members of the addrinfo structure are responsible for informing the server about certain parameters of the socket to be opened. These parameters are specified in the structure which is sent as input arguments in hints. The server responds by sending the information that is stored in a similar structure whose pointer is sent as an input argument in res.
The returned information is a configuration that matches or is closest to the configuration that is suggested in hints based on the server’s capabilities. For example, if you want to create a configuration for IPv6 addresses, not all servers can handle this type of family.
Next, we look in detail at the individual elements of the structure and what parameters they set.
{
int ai_flags; /* Input flags. */
int ai_family; /* Protocol family for socket. */
int ai_socktype; /* Socket type. */
int ai_protocol; /* Protocol for socket. */
socklen_t ai_addrlen; /* Length of socket address. */
struct sockaddr *ai_addr; /* Socket address for socket. */
char *ai_cannonname; /* Canonical name for service location. */
struct addrinfo *ai_next; /* Pointer to next in list. */
};
Let’s take a detailed look at the following individual fields of the addrinfo structure and the parameter options for each field:
ai_flags: These are control flags which are grouped into an integer. To set one of them to 0 or 1, you must perform a Bitwise logical operation between the integer and a mask that changes only the selected bits. In our article, “Bitwise Operators in C”, we explain step by step on how to perform the logical operations with masks.
Next, let us look at the fragment of the “netdb.h” header where these flags are defined with a short description of each one:
# define AI_PASSIVE 0x0001 // Address is intended for `bind'.
# define AI_CANONNAME 0x0002 // Request for canonical name.
# define AI_NUMERICHOST 0x0004 // Don't use name resolution.
# define AI_V4MAPPED 0x0008 // IPv4 mapped addresses.
# define AI_ALL 0x0010 // Return IPv4 mapped and IPv6
# define AI_ADDRCONFIG 0x0020 // Use configuration of this host
// to chose
// returned address type.
# ifdef __USE_GNU
# define AI_IDN 0x0040 // IDN encode input // in the current locale
// character set (colation)
// before looking it up.
# define AI_CANOIDN 0x0080 // Translate canonical name from
// IDN format.
# define AI_IDN_ALLOW_UNASSIGNED 0x0100 // Don't reject unassigned
// Unicode code points.
# define AI_IDN_USE_STD3_ASCII_RULES 0x0200 // Validate strings
// according to
// STD3 rules.
Manipulating the flags without full knowledge can lead to errors that are difficult to diagnose. Therefore, it is advisable to use this field in its default configuration.
ai_family: This integer specifies one of two address family options that the server should return. The address family options are:
AF_INET for IPv4 addresses.
AF_INET6 for IPv6 addresses.
AF_UNSPEC can return either of the two families.
ai_socktype: This integer sets the transport protocol for the socket. The type options are:
SOCK_STREAM for TCP protocol
SOCK_DGRAM for UDP protocol
ai_addrlen: This field indicates the size of the socket address.
Ai_addr: In this sockaddr type structure, the address of the socket is stored.
ai_next: If the server has more than one address, it returns multiple structures of sockaddr type, each containing a different address. The ai_next is the pointer to the list of structures which you can use to access each structure.
How to Set the Socket Parameters in the Fields of the Addrinfo Struct
The parameters of the addrinfo structure must be set before calling the getaddrinfo(). You can change them in the following way:
For example, if we want to select the family of IPv6 addresses in the res_1 structure of the previous example, we need to do the following:
Errors Returned by the Getaddrinfo() Function in C Language
If an error occurs when the getaddrinfo() function is called, it returns a numeric value indicating what the error is. Next, we see a fragment of the “netdb.h” header where these errors and their numerical representation are defined.
# define EAI_BADFLAGS -1 // Invalid value for `ai_flags' field.
# define EAI_NONAME -2 // NAME or SERVICE is unknown.
# define EAI_AGAIN -3 // Temporary failure in name resolution.
# define EAI_FAIL -4 // Non-recoverable failure in name res.
# define EAI_FAMILY -6 // `ai_family' not supported.
# define EAI_SOCKTYPE -7 // `ai_socktype' not supported.
# define EAI_SERVICE -8 // SERVICE not supported ai_socktype'
# define EAI_MEMORY -10 // Memory allocation failure.
# define EAI_SYSTEM -11 // System error returned in `errno'.
# define EAI_OVERFLOW -12 // Argument buffer overflow.
# ifdef __USE_GNU
# define EAI_NODATA -5 // No address associated with NAME.
# define EAI_ADDRFAMILY -9 // Address family for NAME not supported.
# define EAI_INPROGRESS -100 // Processing request in progress.
# define EAI_CANCELED -101 // Request canceled.
# define EAI_NOTCANCELED -102 // Request not canceled.
# define EAI_ALLDONE -103 // All requests done.
# define EAI_INTR -104 // Interrupted by a signal.
# define EAI_IDN_ENCODE -105 // IDN encoding failed.
Conclusion
In this Linux Hints article, we explained how to use the getaddrinfo() function to resolve the IP address of a domain and get the necessary information from the server to open a socket and connect to it.
We looked at the syntax of this function and described each of its input arguments, the type of data used in them, and their purpose within the function in detail. To help you understand how to handle this function, we created two special sections explaining how the addrinfo structures are built, what parameters do each of its members handles, and how you can change them to configure the connection.
We hope that you found this article useful. For more articles about the C language and Linux Hints, please use the search engine on our website.