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