Getaddrinfo
From Wikipedia, the free encyclopedia
- The correct title of this article is getaddrinfo. The initial letter is shown capitalized due to technical restrictions.
The getaddrinfo() function is part of the POSIX standard API in support for using it as a function to resolve a DNS hostname and IP addresses from their human-readable form into the computer format that the Operating System's Networking API's understand.
The reverse function is getnameinfo().
Contents |
[edit] Examples
[edit] Detailed Examples
In 1998 Jun-ichiro 'itojun' Itoh wrote the Implementing AF-independent application article. Additionally Eva M. Castro has written a very detailed article, including Porting applications to IPv6. These two articles describe how to implement an Address Family Independent application. The main goal here is to add support for IPv6 to existing IPv4 applications, directly making them future proof, and also enabling them to do load balancing and the other functions that getaddrinfo() provides.
[edit] Load Balanced Friendlyname Resolution
The following example will use getaddrinfo() to get a result, then use getnameinfo() to get it back. In most cases it should return the same name you give, unless you are using some sort of load balancing.
This particular example was created to resolve the hostname from a load balanced friendlyname.
#include <stdio.h> #include <netdb.h> #include <netinet/in.h> int main() { struct addrinfo *result; char hostname[NI_MAXHOST]; int error; if (error = getaddrinfo("www.example.com", NULL, NULL, &result)) { fprintf(stderr, "error using getaddrinfo: %s\n", gai_strerror(error)); } if (result) { if (error = getnameinfo(result->ai_addr, sizeof(struct sockaddr), hostname, sizeof(hostname), NULL,0,0)) { fprintf(stderr, "error using getnameinfo: %s\n", gai_strerror(error)); } } }
[edit] Protocol Independent Programming
The following example will use getaddrinfo() (and getnameinfo()) to program a client and a server in protocol-independent manner compatible with both IPv4 and IPv6, and potentially with any other network level layer, as proposed in IETF RFC 3493 (Basic Socket Interface Extensions for IPv6), in Sections 3.10 (struct sockaddr_storage), 6.1 (getaddrinfo()) and 6.2 (getnameinfo()). This client program simply sends the string "Hello, World!\n" to the server (Why make it simple, when you can make it really complicated !?). This software is poorly written: don't trust it; it's only show getaddrinfo usage and shouldn't be used for something else.
Client:
/* * Copyright (C) 2005 Olivier Aumage, Samuel Thibault * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ #include <errno.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netdb.h> #include <netinet/in.h> static char msg[] = "Hello, World!\n"; static void loop_write(int s, void *buf, size_t count) { int v; while (count && (v = write(s, buf, count)) < count) { if (!v) continue; if (v < 0) { if (errno == EINTR) continue; perror("write"); exit(1); } buf += v; count -= v; } } static void usage(void) { fprintf(stderr, "usage: prog [-h <hostname>] <port>\n"); exit(1); } int main(int argc, char **argv) { int hport = 0; int s = 0; size_t l = 0; unsigned char c = 0; unsigned int i = 0; char *hostname = NULL; struct addrinfo hints; struct addrinfo *res = NULL; struct addrinfo *ptr = NULL; char sport[NI_MAXSERV]; int v = 0; argc--;argv++; if (argc < 1) usage(); if (strcmp(*argv, "-h") == 0) { argc--;argv++; if (argc < 2) usage(); hostname = *argv; argc--;argv++; } if (argc < 1) usage(); hport = atoi(*argv); snprintf(sport, NI_MAXSERV, "%u", hport); hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; if ((v = getaddrinfo(hostname, sport, &hints, &res))) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(v)); if (v == EAI_SYSTEM) { perror("getaddrinfo"); } exit(1); } for (ptr = res; ptr; ptr = ptr->ai_next) { if (ptr->ai_socktype != hints.ai_socktype || ptr->ai_protocol != hints.ai_protocol) goto next_ai; s = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (s < 0) { switch (errno) { case EAFNOSUPPORT: case EINVAL: goto next_ai; default: perror("warning: socket"); goto next_ai; } } while ((v = connect(s, ptr->ai_addr, ptr->ai_addrlen)) < 0) { switch (errno) { case EAFNOSUPPORT: goto next_ai; case EINTR: case EAGAIN: continue; default: perror("warning: socket"); goto next_ai; } } break; next_ai: ; } if (!ptr) { fprintf(stderr, "no suitable address struct found\n"); exit(1); } freeaddrinfo(res); l = strlen(msg)+1; c = sizeof(l); loop_write(s, (void *)&c, 1); for (i = 0; i < c; i++) { unsigned char c2 = ((unsigned long)l >> (8*i)) & 255; loop_write(s, (void *)&c2, 1); } loop_write(s, msg, l); close(s); return 0; }
Server:
/* * Copyright (C) 2005 Olivier Aumage, Samuel Thibault * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ #include <errno.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/select.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netdb.h> #include <netinet/in.h> #define MAX_SERV 8 static void loop_read(int s, void *buf, size_t count) { int v; while (count && (v = read(s, buf, count)) < count) { if (!v) { fprintf(stderr, "connection unexpectedly closed\n"); exit(1); } if (v < 0) { if (errno == EINTR) continue; perror("read"); exit(1); } buf += v; count -= v; } } static int build_server_socket(struct addrinfo *ai) { struct sockaddr_storage sas; socklen_t sas_len = sizeof(sas); int server_s = -1; int v = 0; char shost[NI_MAXHOST]; char sport[NI_MAXSERV]; server_s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (server_s < 0) { perror("socket"); return -1; } if (bind(server_s, ai->ai_addr, ai->ai_addrlen) < 0) { perror("warning: bind"); close(server_s); return -1; } if (listen(server_s, 1) < 0) { perror("warning: listen"); close(server_s); return -1; } if (getsockname(server_s, (struct sockaddr *)&sas, &sas_len) < 0) { perror("warning: getsockname"); close(server_s); return -1; } if ((v = getnameinfo((struct sockaddr *)&sas, sas_len, shost, NI_MAXHOST, sport, NI_MAXSERV, 0))) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(v)); if (v == EAI_SYSTEM) { perror("warning: getaddrinfo"); } close(server_s); return -1; } printf("listening on %s:%s\n", shost, sport); return server_s; } static int multi_accept(int *server_s, int nb_server_s) { int select_server_s = -1; int s = -1; if (!nb_server_s) { fprintf(stderr, "invalid parameter\n"); exit(1); } while (1) { int i = 0; int m = 0; int v = 0; fd_set fds; FD_ZERO(&fds); for (i = 0; i < nb_server_s; i++) { redo: if (server_s[i] == -1) { nb_server_s--; if (i < nb_server_s) { server_s[i] = server_s[nb_server_s]; goto redo; } break; } FD_SET(server_s[i], &fds); if (m < server_s[i]) m = server_s[i]; } v = select(m+1, &fds, NULL, NULL, NULL); if (v < 1) { if (errno != EINTR) { perror("select"); exit(1); } continue; } for (i = 0; i < nb_server_s; i++) { if (FD_ISSET(server_s[i], &fds)) { select_server_s = server_s[i]; while ((s = accept(select_server_s, NULL, NULL)) < 0) { if (errno != EINTR) { perror("warning: accept"); server_s[i] = -1; goto next_fd; } } return s; } next_fd: ; } fprintf(stderr, "invalid state\n"); exit(1); } } int main(int argc, char **argv) { int nb_server_s = 0; int server_s[MAX_SERV]; int s = 0; size_t l = 0; unsigned char c = 0; unsigned int i = 0; char *msg = NULL; struct addrinfo hints; struct addrinfo *res = NULL; struct addrinfo *ai = NULL; char sport[NI_MAXSERV]; int v = 0; snprintf(sport, NI_MAXSERV, "%u", 0); hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; if ((v = getaddrinfo(NULL, sport, &hints, &res))) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(v)); if (v == EAI_SYSTEM) { perror("getaddrinfo"); } exit(1); } ai = res; while (ai && nb_server_s < MAX_SERV) { int _server_s = -1; if (ai->ai_socktype != hints.ai_socktype || ai->ai_protocol != hints.ai_protocol) goto next_res; if ((_server_s = build_server_socket(ai)) < 0) { goto next_res; } server_s[nb_server_s++] = _server_s; next_res: ai = ai->ai_next; } if (!nb_server_s) { fprintf(stderr, "no suitable address struct found\n"); exit(1); } freeaddrinfo(res); s = multi_accept(server_s, nb_server_s); loop_read(s, &c, 1); l = 0; for (i = 0; i < c; i++) { unsigned char c2; loop_read(s, &c2, 1); if (c2 && i >= sizeof (l)) { fprintf(stderr, "integer overflow during conversion\n"); exit(1); } l |= (size_t)c2 << (8 * i); } msg = malloc(l); if (!msg) { perror("malloc"); exit(1); } loop_read(s, msg, l); printf("msg = [%s]\n", msg); free(msg); close(s); while (nb_server_s) { close(server_s[--nb_server_s]); } return 0; }