summaryrefslogtreecommitdiffstats
path: root/plugins/getaddrinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/getaddrinfo.c')
-rw-r--r--plugins/getaddrinfo.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/plugins/getaddrinfo.c b/plugins/getaddrinfo.c
new file mode 100644
index 00000000..12ac67d6
--- /dev/null
+++ b/plugins/getaddrinfo.c
@@ -0,0 +1,303 @@
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 */
23
24/* An emulation of the RFC 2553 / Posix getaddrinfo resolver interface.
25 */
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31/* Need to turn off Posix features in glibc to build this */
32#undef _POSIX_C_SOURCE
33#undef _XOPEN_SOURCE
34
35#include <stdlib.h>
36#include <string.h>
37#include <ctype.h>
38#include <errno.h>
39
40#include <sys/socket.h>
41#include <netinet/in.h>
42#include <arpa/inet.h>
43
44#include <netdb.h>
45
46#include "gethostbyname.h"
47#include "getaddrinfo.h"
48
49static struct addrinfo *
50dup_addrinfo (struct addrinfo *info, void *addr, size_t addrlen)
51{
52 struct addrinfo *ret;
53
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}
68
69int
70getaddrinfo (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;
82
83 memset (&result, 0, sizeof result);
84
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 }
92
93 result.ai_socktype = hints->ai_socktype;
94
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 }
104
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;
110
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;
117
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 }
128
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;
134
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;
140
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 }
148
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 }
173
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 }
193
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;
201
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 }
225
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 }
240
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 }
258
259 free_ghbnctx (&ghbnctx);
260 *res = sai;
261 return 0;
262}
263
264void
265freeaddrinfo (struct addrinfo *ai)
266{
267 struct addrinfo *next;
268
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 }
279}
280
281const char *
282gai_strerror (int ecode)
283{
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 };
299
300 if (ecode < 0 || ecode > (int) (sizeof eai_descr/ sizeof eai_descr[0]))
301 return "unknown error";
302 return eai_descr[ecode];
303}