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