Code

Standardising translation texts
[nagiosplug.git] / plugins / getaddrinfo.c
1 /*
2  *  This file is part of libESMTP, a library for submission of RFC 2822
3  *  formatted electronic mail messages using the SMTP protocol described
4  *  in RFC 2821.
5  *  Modified by Jeremy T. Bouse for use in Nagios plugins
6  *
7  *  Copyright (C) 2001,2002  Brian Stafford  <brian@stafford.uklinux.net>
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
24 /* An emulation of the RFC 2553 / Posix getaddrinfo resolver interface.
25  */
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
31 /* Need to turn off Posix features in glibc to build this */
32 #undef _POSIX_C_SOURCE
33 #undef _XOPEN_SOURCE
35 #include <stdlib.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <errno.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
44 #include <netdb.h>
46 #include "gethostbyname.h"
47 #include "getaddrinfo.h"
49 static struct addrinfo *
50 dup_addrinfo (struct addrinfo *info, void *addr, size_t addrlen)
51 {
52   struct addrinfo *ret;
54   ret = malloc (sizeof (struct addrinfo));
55   if (ret == NULL)
56     return NULL;
57   memcpy (ret, info, sizeof (struct addrinfo));
58   ret->ai_addr = malloc (addrlen);
59   if (ret->ai_addr == NULL)
60     {
61       free (ret);
62       return NULL;
63     }
64   memcpy (ret->ai_addr, addr, addrlen);
65   ret->ai_addrlen = addrlen;
66   return ret;
67 }
69 int
70 getaddrinfo (const char *nodename, const char *servname,
71              const struct addrinfo *hints, struct addrinfo **res)
72 {
73   struct hostent *hp;
74   struct servent *servent;
75   const char *socktype;
76   int port;
77   struct addrinfo hint, result;
78   struct addrinfo *ai, *sai, *eai;
79   struct ghbnctx ghbnctx;
80   char **addrs;
81   int code;
83   memset (&result, 0, sizeof result);
85   /* default for hints */
86   if (hints == NULL)
87     {
88       memset (&hint, 0, sizeof hint);
89       hint.ai_family = PF_UNSPEC;
90       hints = &hint;
91     }
93   result.ai_socktype = hints->ai_socktype;
95   /* Note: maintain port in host byte order to make debugging easier */
96   if (servname != NULL) {
97     if (isdigit (*servname))
98       port = strtol (servname, NULL, 10);
99     else if ((servent = getservbyname (servname, socktype)) != NULL)
100       port = ntohs (servent->s_port);
101     else
102       return EAI_NONAME;
103   }
105   /* if nodename == NULL refer to the local host for a client or any
106      for a server */
107   if (nodename == NULL)
108     {
109       struct sockaddr_in sin;
111       /* check protocol family is PF_UNSPEC or PF_INET - could try harder
112          for IPv6 but that's more code than I'm prepared to write */
113       if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
114         result.ai_family = AF_INET;
115       else
116         return EAI_FAMILY;
118       sin.sin_family = result.ai_family;
119       sin.sin_port = htons (port);
120       if (hints->ai_flags & AI_PASSIVE)
121         sin.sin_addr.s_addr = htonl (INADDR_ANY);
122       else
123         sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
124       /* Duplicate result and addr and return */
125       *res = dup_addrinfo (&result, &sin, sizeof sin);
126       return (*res == NULL) ? EAI_MEMORY : 0;
127     }
129   /* If AI_NUMERIC is specified, use inet_addr to translate numbers and
130      dots notation. */
131   if (hints->ai_flags & AI_NUMERICHOST)
132     {
133       struct sockaddr_in sin;
135       /* check protocol family is PF_UNSPEC or PF_INET */
136       if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
137         result.ai_family = AF_INET;
138       else
139         return EAI_FAMILY;
141       sin.sin_family = result.ai_family;
142       sin.sin_port = htons (port);
143       sin.sin_addr.s_addr = inet_addr (nodename);
144       /* Duplicate result and addr and return */
145       *res = dup_addrinfo (&result, &sin, sizeof sin);
146       return (*res == NULL) ? EAI_MEMORY : 0;
147     }
149   errno = 0;
150   hp = gethostbyname_ctx (nodename, &ghbnctx);
151   if (hp == NULL)
152     {
153       if (errno != 0)
154         {
155           free_ghbnctx (&ghbnctx);
156           return EAI_SYSTEM;
157         }
158       code = h_error_ctx (&ghbnctx);
159       switch (code)
160         {
161         case HOST_NOT_FOUND: code = EAI_NODATA; break;
162         case NO_DATA: code = EAI_NODATA; break;
163 #if defined(NO_ADDRESS) && NO_ADDRESS != NO_DATA
164         case NO_ADDRESS: code = EAI_NODATA; break;
165 #endif
166         case NO_RECOVERY: code = EAI_FAIL; break;
167         case TRY_AGAIN: code = EAI_AGAIN; break;
168         default: code = EAI_FAIL; break;
169         }
170       free_ghbnctx (&ghbnctx);
171       return code;
172     }
174   /* Check that the address family is acceptable.
175    */
176   switch (hp->h_addrtype)
177     {
178     case AF_INET:
179       if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET))
180         goto eai_family;
181       break;
182 #ifdef USE_IPV6
183     case AF_INET6:
184       if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET6))
185         goto eai_family;
186       break;
187 #endif
188     default:
189     eai_family:
190       free_ghbnctx (&ghbnctx);
191       return EAI_FAMILY;
192     }
194   /* For each element pointed to by hp, create an element in the
195      result linked list. */
196   sai = eai = NULL;
197   for (addrs = hp->h_addr_list; *addrs != NULL; addrs++)
198     {
199       struct sockaddr sa;
200       size_t addrlen;
202       sa.sa_family = hp->h_addrtype;
203       switch (hp->h_addrtype)
204         {
205         case AF_INET:
206           ((struct sockaddr_in *) &sa)->sin_port = htons (port);
207           memcpy (&((struct sockaddr_in *) &sa)->sin_addr,
208                   *addrs, hp->h_length);
209           addrlen = sizeof (struct sockaddr_in);
210           break;
211 #ifdef USE_IPV6
212         case AF_INET6:
213 # if SIN6_LEN
214           ((struct sockaddr_in6 *) &sa)->sin6_len = hp->h_length;
215 # endif
216           ((struct sockaddr_in6 *) &sa)->sin6_port = htons (port);
217           memcpy (&((struct sockaddr_in6 *) &sa)->sin6_addr,
218                   *addrs, hp->h_length);
219           addrlen = sizeof (struct sockaddr_in6);
220           break;
221 #endif
222         default:
223           continue;
224         }
226       result.ai_family = hp->h_addrtype;
227       ai = dup_addrinfo (&result, &sa, addrlen);
228       if (ai == NULL)
229         {
230           free_ghbnctx (&ghbnctx);
231           freeaddrinfo (sai);
232           return EAI_MEMORY;
233         }
234       if (sai == NULL)
235         sai = ai;
236       else
237         eai->ai_next = ai;
238       eai = ai;
239     }
241   if (sai == NULL)
242     {
243       free_ghbnctx (&ghbnctx);
244       return EAI_NODATA;
245     }
246   
247   if (hints->ai_flags & AI_CANONNAME) 
248     {
249       sai->ai_canonname = malloc (strlen (hp->h_name) + 1);
250       if (sai->ai_canonname == NULL)
251         {
252           free_ghbnctx (&ghbnctx);
253           freeaddrinfo (sai);
254           return EAI_MEMORY;
255         }
256       strcpy (sai->ai_canonname, hp->h_name);
257     }
259   free_ghbnctx (&ghbnctx);
260   *res = sai;
261   return 0;
264 void
265 freeaddrinfo (struct addrinfo *ai)
267   struct addrinfo *next;
269   while (ai != NULL)
270     {
271       next = ai->ai_next;
272       if (ai->ai_canonname != NULL)
273         free (ai->ai_canonname);
274       if (ai->ai_addr != NULL)
275         free (ai->ai_addr);
276       free (ai);
277       ai = next;
278     }
281 const char *
282 gai_strerror (int ecode)
284   static const char *eai_descr[] =
285     {
286       "no error",
287       "address family for nodename not supported",      /* EAI_ADDRFAMILY */
288       "temporary failure in name resolution",           /* EAI_AGAIN */
289       "invalid value for ai_flags",                     /* EAI_BADFLAGS */
290       "non-recoverable failure in name resolution",     /* EAI_FAIL */
291       "ai_family not supported",                        /* EAI_FAMILY */
292       "memory allocation failure",                      /* EAI_MEMORY */
293       "no address associated with nodename",            /* EAI_NODATA */
294       "nodename nor servname provided, or not known",   /* EAI_NONAME */
295       "servname not supported for ai_socktype",         /* EAI_SERVICE */
296       "ai_socktype not supported",                      /* EAI_SOCKTYPE */
297       "system error returned in errno",                 /* EAI_SYSTEM */
298     };
300   if (ecode < 0 || ecode > (int) (sizeof eai_descr/ sizeof eai_descr[0]))
301     return "unknown error";
302   return eai_descr[ecode];