Code

569782083d6a9d0b6273d2d890feea2188b708ee
[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  *
26  * $Id$
27  */
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
33 /* Need to turn off Posix features in glibc to build this */
34 #undef _POSIX_C_SOURCE
35 #undef _XOPEN_SOURCE
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <errno.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
46 #include <netdb.h>
48 #include "gethostbyname.h"
49 #include "getaddrinfo.h"
51 static struct addrinfo *
52 dup_addrinfo (struct addrinfo *info, void *addr, size_t addrlen)
53 {
54   struct addrinfo *ret;
56   ret = malloc (sizeof (struct addrinfo));
57   if (ret == NULL)
58     return NULL;
59   memcpy (ret, info, sizeof (struct addrinfo));
60   ret->ai_addr = malloc (addrlen);
61   if (ret->ai_addr == NULL)
62     {
63       free (ret);
64       return NULL;
65     }
66   memcpy (ret->ai_addr, addr, addrlen);
67   ret->ai_addrlen = addrlen;
68   return ret;
69 }
71 int
72 getaddrinfo (const char *nodename, const char *servname,
73              const struct addrinfo *hints, struct addrinfo **res)
74 {
75   struct hostent *hp;
76   struct servent *servent;
77   const char *socktype;
78   int port;
79   struct addrinfo hint, result;
80   struct addrinfo *ai, *sai, *eai;
81   struct ghbnctx ghbnctx;
82   char **addrs;
83   int code;
85   memset (&result, 0, sizeof result);
87   /* default for hints */
88   if (hints == NULL)
89     {
90       memset (&hint, 0, sizeof hint);
91       hint.ai_family = PF_UNSPEC;
92       hints = &hint;
93     }
95   result.ai_socktype = hints->ai_socktype;
97   /* Note: maintain port in host byte order to make debugging easier */
98   if (servname != NULL) {
99     if (isdigit (*servname))
100       port = strtol (servname, NULL, 10);
101     else if ((servent = getservbyname (servname, socktype)) != NULL)
102       port = ntohs (servent->s_port);
103     else
104       return EAI_NONAME;
105   }
107   /* if nodename == NULL refer to the local host for a client or any
108      for a server */
109   if (nodename == NULL)
110     {
111       struct sockaddr_in sin;
113       /* check protocol family is PF_UNSPEC or PF_INET - could try harder
114          for IPv6 but that's more code than I'm prepared to write */
115       if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
116         result.ai_family = AF_INET;
117       else
118         return EAI_FAMILY;
120       sin.sin_family = result.ai_family;
121       sin.sin_port = htons (port);
122       if (hints->ai_flags & AI_PASSIVE)
123         sin.sin_addr.s_addr = htonl (INADDR_ANY);
124       else
125         sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
126       /* Duplicate result and addr and return */
127       *res = dup_addrinfo (&result, &sin, sizeof sin);
128       return (*res == NULL) ? EAI_MEMORY : 0;
129     }
131   /* If AI_NUMERIC is specified, use inet_addr to translate numbers and
132      dots notation. */
133   if (hints->ai_flags & AI_NUMERICHOST)
134     {
135       struct sockaddr_in sin;
137       /* check protocol family is PF_UNSPEC or PF_INET */
138       if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)
139         result.ai_family = AF_INET;
140       else
141         return EAI_FAMILY;
143       sin.sin_family = result.ai_family;
144       sin.sin_port = htons (port);
145       sin.sin_addr.s_addr = inet_addr (nodename);
146       /* Duplicate result and addr and return */
147       *res = dup_addrinfo (&result, &sin, sizeof sin);
148       return (*res == NULL) ? EAI_MEMORY : 0;
149     }
151   errno = 0;
152   hp = gethostbyname_ctx (nodename, &ghbnctx);
153   if (hp == NULL)
154     {
155       if (errno != 0)
156         {
157           free_ghbnctx (&ghbnctx);
158           return EAI_SYSTEM;
159         }
160       code = h_error_ctx (&ghbnctx);
161       switch (code)
162         {
163         case HOST_NOT_FOUND: code = EAI_NODATA; break;
164         case NO_DATA: code = EAI_NODATA; break;
165 #if defined(NO_ADDRESS) && NO_ADDRESS != NO_DATA
166         case NO_ADDRESS: code = EAI_NODATA; break;
167 #endif
168         case NO_RECOVERY: code = EAI_FAIL; break;
169         case TRY_AGAIN: code = EAI_AGAIN; break;
170         default: code = EAI_FAIL; break;
171         }
172       free_ghbnctx (&ghbnctx);
173       return code;
174     }
176   /* Check that the address family is acceptable.
177    */
178   switch (hp->h_addrtype)
179     {
180     case AF_INET:
181       if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET))
182         goto eai_family;
183       break;
184 #ifdef USE_IPV6
185     case AF_INET6:
186       if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET6))
187         goto eai_family;
188       break;
189 #endif
190     default:
191     eai_family:
192       free_ghbnctx (&ghbnctx);
193       return EAI_FAMILY;
194     }
196   /* For each element pointed to by hp, create an element in the
197      result linked list. */
198   sai = eai = NULL;
199   for (addrs = hp->h_addr_list; *addrs != NULL; addrs++)
200     {
201       struct sockaddr sa;
202       size_t addrlen;
204       sa.sa_family = hp->h_addrtype;
205       switch (hp->h_addrtype)
206         {
207         case AF_INET:
208           ((struct sockaddr_in *) &sa)->sin_port = htons (port);
209           memcpy (&((struct sockaddr_in *) &sa)->sin_addr,
210                   *addrs, hp->h_length);
211           addrlen = sizeof (struct sockaddr_in);
212           break;
213 #ifdef USE_IPV6
214         case AF_INET6:
215 # if SIN6_LEN
216           ((struct sockaddr_in6 *) &sa)->sin6_len = hp->h_length;
217 # endif
218           ((struct sockaddr_in6 *) &sa)->sin6_port = htons (port);
219           memcpy (&((struct sockaddr_in6 *) &sa)->sin6_addr,
220                   *addrs, hp->h_length);
221           addrlen = sizeof (struct sockaddr_in6);
222           break;
223 #endif
224         default:
225           continue;
226         }
228       result.ai_family = hp->h_addrtype;
229       ai = dup_addrinfo (&result, &sa, addrlen);
230       if (ai == NULL)
231         {
232           free_ghbnctx (&ghbnctx);
233           freeaddrinfo (sai);
234           return EAI_MEMORY;
235         }
236       if (sai == NULL)
237         sai = ai;
238       else
239         eai->ai_next = ai;
240       eai = ai;
241     }
243   if (sai == NULL)
244     {
245       free_ghbnctx (&ghbnctx);
246       return EAI_NODATA;
247     }
248   
249   if (hints->ai_flags & AI_CANONNAME) 
250     {
251       sai->ai_canonname = malloc (strlen (hp->h_name) + 1);
252       if (sai->ai_canonname == NULL)
253         {
254           free_ghbnctx (&ghbnctx);
255           freeaddrinfo (sai);
256           return EAI_MEMORY;
257         }
258       strcpy (sai->ai_canonname, hp->h_name);
259     }
261   free_ghbnctx (&ghbnctx);
262   *res = sai;
263   return 0;
266 void
267 freeaddrinfo (struct addrinfo *ai)
269   struct addrinfo *next;
271   while (ai != NULL)
272     {
273       next = ai->ai_next;
274       if (ai->ai_canonname != NULL)
275         free (ai->ai_canonname);
276       if (ai->ai_addr != NULL)
277         free (ai->ai_addr);
278       free (ai);
279       ai = next;
280     }
283 const char *
284 gai_strerror (int ecode)
286   static const char *eai_descr[] =
287     {
288       "no error",
289       "address family for nodename not supported",      /* EAI_ADDRFAMILY */
290       "temporary failure in name resolution",           /* EAI_AGAIN */
291       "invalid value for ai_flags",                     /* EAI_BADFLAGS */
292       "non-recoverable failure in name resolution",     /* EAI_FAIL */
293       "ai_family not supported",                        /* EAI_FAMILY */
294       "memory allocation failure",                      /* EAI_MEMORY */
295       "no address associated with nodename",            /* EAI_NODATA */
296       "nodename nor servname provided, or not known",   /* EAI_NONAME */
297       "servname not supported for ai_socktype",         /* EAI_SERVICE */
298       "ai_socktype not supported",                      /* EAI_SOCKTYPE */
299       "system error returned in errno",                 /* EAI_SYSTEM */
300     };
302   if (ecode < 0 || ecode > (int) (sizeof eai_descr/ sizeof eai_descr[0]))
303     return "unknown error";
304   return eai_descr[ecode];