<< Prev | Beej's Guide to Network Programming | Next >> |
Get information about a host name and/or service and load up a
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res); void freeaddrinfo(struct addrinfo *ai); const char *gai_strerror(int ecode); struct addrinfo { int ai_flags; // AI_PASSIVE, AI_CANONNAME, ... int ai_family; // AF_xxx int ai_socktype; // SOCK_xxx int ai_protocol; // 0 (auto) or IPPROTO_TCP, IPPROTO_UDP socklen_t ai_addrlen; // length of ai_addr char *ai_canonname; // canonical name for nodename struct sockaddr *ai_addr; // binary address struct addrinfo *ai_next; // next structure in linked list };
getaddrinfo() is an excellent function that will return
information on a particular host name (such as its IP address) and load
up a
The host name that you're interested in goes in the nodename parameter. The address can be either a host name, like "www.example.com", or an IPv4 or IPv6 address (passed as a string). This parameter can also be NULL if you're using the AI_PASSIVE flag (see below.)
The servname parameter is basically the port number. It can be a port number (passed as a string, like "80"), or it can be a service name, like "http" or "tftp" or "smtp" or "pop", etc. Well-known service names can be found in the IANA Port List or in your /etc/services file.
Lastly, for input parameters, we have hints. This is really where you get to define what the getaddinfo() function is going to do. Zero the whole structure before use with memset(). Let's take a look at the fields you need to set up before use.
The ai_flags can be set to a variety of things, but here are a couple important ones. (Multiple flags can be specified by bitwise-ORing them together with the | operator.) Check your man page for the complete list of flags.
AI_CANONNAME causes the ai_canonname
of the result to the filled out with the host's canonical (real) name.
AI_PASSIVE causes the result's IP address to
be filled out with INADDR_ANY (IPv4)or
in6addr_any (IPv6); this causes a subsequent call to
bind() to auto-fill the IP address of the
If you do use the AI_PASSIVE, flag, then you can pass NULL in the nodename (since bind() will fill it in for you later.)
Continuing on with the input paramters, you'll likely want to set ai_family to AF_UNSPEC which tells getaddrinfo() to look for both IPv4 and IPv6 addresses. You can also restrict yourself to one or the other with AF_INET or AF_INET6.
Next, the socktype field should be set to SOCK_STREAM or SOCK_DGRAM, depending on which type of socket you want.
Finally, just leave ai_protocol at 0 to automatically choose your protocol type.
Now, after you get all that stuff in there, you can finally make the call to getaddrinfo()!
Of course, this is where the fun begins. The res will
now point to a linked list of
Now, it's possible to get some addresses that don't work for one reason or another, so what the Linux man page does is loops through the list doing a call to socket() and connect() (or bind() if you're setting up a server with the AI_PASSIVE flag) until it succeeds.
Finally, when you're done with the linked list, you need to call freeaddrinfo() to free up the memory (or it will be leaked, and Some People will get upset.)
Returns zero on success, or nonzero on error. If it returns nonzero, you can use the function gai_strerror() to get a printable version of the error code in the return value.
// code for a client connecting to a server // namely a stream socket to www.example.com on port 80 (http) // either IPv4 or IPv6 int sockfd; struct addrinfo hints, *servinfo, *p; int rv; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6 hints.ai_socktype = SOCK_STREAM; if ((rv = getaddrinfo("www.example.com", "http", &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); exit(1); } // loop through all the results and connect to the first we can for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("socket"); continue; } if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); perror("connect"); continue; } break; // if we get here, we must have connected successfully } if (p == NULL) { // looped off the end of the list with no connection fprintf(stderr, "failed to connect\n"); exit(2); } freeaddrinfo(servinfo); // all done with this structure
// code for a server waiting for connections // namely a stream socket on port 3490, on this host's IP // either IPv4 or IPv6. int sockfd; struct addrinfo hints, *servinfo, *p; int rv; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6 hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // use my IP address if ((rv = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); exit(1); } // loop through all the results and bind to the first we can for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("socket"); continue; } if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); perror("bind"); continue; } break; // if we get here, we must have connected successfully } if (p == NULL) { // looped off the end of the list with no successful bind fprintf(stderr, "failed to bind socket\n"); exit(2); } freeaddrinfo(servinfo); // all done with this structure
gethostbyname(), getnameinfo()
<< Prev | Beej's Guide to Network Programming | Next >> |