]> Pileus Git - ~andy/fetchmail/blob - KAME/getnameinfo.c
Add getnameinfo.c.
[~andy/fetchmail] / KAME / getnameinfo.c
1 /*      $KAME: getnameinfo.c,v 1.72 2005/01/13 04:12:03 itojun Exp $    */
2
3 /*
4  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 /*
33  * Issues to be discussed:
34  * - RFC2553 says that we should raise error on short buffer.  X/Open says
35  *   we need to truncate the result.  We obey RFC2553 (and X/Open should be
36  *   modified).  ipngwg rough consensus seems to follow RFC2553.  RFC3493 says
37  *   nothing about it, but defines a new error code EAI_OVERFLOW which seems
38  *   to be intended the code for this case.
39  * - What is "local" in NI_NOFQDN?  (see comments in the code)
40  * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other.
41  * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if
42  *   sin6_scope_id is filled - standardization status?
43  * - what should we do if we should do getservbyport("sctp")?
44  */
45
46 /*
47  * Considerations about thread-safeness
48  *   The code in this file is thread-safe, and so the thread-safeness of
49  *   getnameinfo() depends on the property of backend functions.
50  *     - getservbyport() is not thread safe for most systems we are targeting.
51  *     - getipnodebyaddr() is thread safe.  However, many resolver libraries
52  *       used in the function are not thread safe.
53  *     - gethostbyaddr() is usually not thread safe.
54  */
55
56 #ifdef HAVE_CONFIG_H
57 #include <config.h>
58 #endif
59
60 #ifndef HAVE_GETNAMEINFO
61
62 #include <sys/types.h>
63 #include <sys/socket.h>
64 #include <net/if.h>
65 #include <netinet/in.h>
66 #include <arpa/inet.h>
67 #include <arpa/nameser.h>
68 #include <netdb.h>
69 #include <resolv.h>
70 #include <string.h>
71 #include <stddef.h>
72 #include <errno.h>
73
74 #include <inttypes.h>
75 #include "fetchmail.h"
76 #include "getaddrinfo.h"
77
78 static const struct afd {
79         int a_af;
80         int a_addrlen;
81         int a_socklen;
82         int a_off;
83         int a_portoff;
84 } afdl [] = {
85 #ifdef INET6
86         {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
87          offsetof(struct sockaddr_in6, sin6_addr),
88          offsetof(struct sockaddr_in6, sin6_port)},
89 #endif
90         {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
91          offsetof(struct sockaddr_in, sin_addr),
92          offsetof(struct sockaddr_in, sin_port)},
93         {0, 0, 0, 0, 0},
94 };
95
96 #ifdef INET6
97 static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
98                                  size_t, int));
99 static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int));
100 #endif
101
102 int
103 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
104         const struct sockaddr *sa;
105 #ifdef HAVE_SOCKLEN_T
106         socklen_t salen;
107 #else
108         unsigned int salen;
109 #endif
110         char *host;
111         size_t hostlen;
112         char *serv;
113         size_t servlen;
114         int flags;
115 {
116         const struct afd *afd;
117         struct servent *sp;
118         struct hostent *hp;
119         u_short port;
120         int family, i;
121         const char *addr;
122         u_int32_t v4a;
123         int h_error;
124         char numserv[512];
125         char numaddr[512];
126
127         if (sa == NULL)
128                 return EAI_FAIL;
129
130 #ifdef HAVE_SA_LEN      /*XXX*/
131         if (sa->sa_len != salen)
132                 return EAI_FAIL;
133 #endif
134
135         family = sa->sa_family;
136         for (i = 0; afdl[i].a_af; i++)
137                 if (afdl[i].a_af == family) {
138                         afd = &afdl[i];
139                         goto found;
140                 }
141         return EAI_FAMILY;
142
143  found:
144         if (salen != afd->a_socklen)
145                 return EAI_FAIL;
146
147         /* network byte order */
148         memcpy(&port, (const char *)sa + afd->a_portoff, sizeof(port));
149         addr = (const char *)sa + afd->a_off;
150
151         if (serv == NULL || servlen == 0) {
152                 /*
153                  * do nothing in this case.
154                  * in case you are wondering if "&&" is more correct than
155                  * "||" here: RFC3493 says that serv == NULL OR servlen == 0
156                  * means that the caller does not want the result.
157                  */
158         } else {
159                 if (flags & NI_NUMERICSERV)
160                         sp = NULL;
161                 else {
162                         sp = getservbyport(port,
163                                 (flags & NI_DGRAM) ? "udp" : "tcp");
164                 }
165                 if (sp) {
166                         if (strlen(sp->s_name) + 1 > servlen)
167                                 return EAI_OVERFLOW;
168                         strlcpy(serv, sp->s_name, servlen);
169                 } else {
170                         snprintf(numserv, sizeof(numserv), "%u", ntohs(port));
171                         if (strlen(numserv) + 1 > servlen)
172                                 return EAI_OVERFLOW;
173                         strlcpy(serv, numserv, servlen);
174                 }
175         }
176
177         switch (sa->sa_family) {
178         case AF_INET:
179                 v4a = (u_int32_t)
180                     ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr);
181                 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
182                         flags |= NI_NUMERICHOST;
183                 v4a >>= IN_CLASSA_NSHIFT;
184                 if (v4a == 0)
185                         flags |= NI_NUMERICHOST;
186                 break;
187 #ifdef INET6
188         case AF_INET6:
189             {
190                 const struct sockaddr_in6 *sin6;
191                 sin6 = (const struct sockaddr_in6 *)sa;
192                 switch (sin6->sin6_addr.s6_addr[0]) {
193                 case 0x00:
194                         if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
195                                 ;
196                         else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
197                                 ;
198                         else
199                                 flags |= NI_NUMERICHOST;
200                         break;
201                 default:
202                         if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
203                                 flags |= NI_NUMERICHOST;
204                         else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
205                                 flags |= NI_NUMERICHOST;
206                         break;
207                 }
208             }
209                 break;
210 #endif
211         }
212         if (host == NULL || hostlen == 0) {
213                 /*
214                  * do nothing in this case.
215                  * in case you are wondering if "&&" is more correct than
216                  * "||" here: RFC3493 says that host == NULL or hostlen == 0
217                  * means that the caller does not want the result.
218                  */
219         } else if (flags & NI_NUMERICHOST) {
220                 /* NUMERICHOST and NAMEREQD conflicts with each other */
221                 if (flags & NI_NAMEREQD)
222                         return EAI_NONAME;
223
224                 goto numeric;
225         } else {
226 #ifdef USE_GETIPNODEBY
227                 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
228 #else
229                 hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
230 #ifdef HAVE_H_ERRNO
231                 h_error = h_errno;
232 #else
233                 h_error = EINVAL;
234 #endif
235 #endif
236
237                 if (hp) {
238 #if 0
239                         if (flags & NI_NOFQDN) {
240                                 /*
241                                  * According to RFC3493 section 6.2, NI_NOFQDN
242                                  * means "node name portion of the FQDN shall
243                                  * be returned for local hosts."  The following
244                                  * code tries to implement it by returning the
245                                  * first label (the part before the first
246                                  * period) of the FQDN.  However, it is not
247                                  * clear if this always makes sense, since the
248                                  * given address may be outside of "local
249                                  * hosts."  Due to the unclear description, we
250                                  * disable the code in this implementation.
251                                  */
252                                 char *p;
253                                 p = strchr(hp->h_name, '.');
254                                 if (p)
255                                         *p = '\0';
256                         }
257 #endif
258                         if (strlen(hp->h_name) + 1 > hostlen) {
259 #ifdef USE_GETIPNODEBY
260                                 freehostent(hp);
261 #endif
262                                 return EAI_OVERFLOW;
263                         }
264                         strlcpy(host, hp->h_name, hostlen);
265 #ifdef USE_GETIPNODEBY
266                         freehostent(hp);
267 #endif
268                 } else {
269                         if (flags & NI_NAMEREQD)
270                                 return EAI_NONAME;
271
272                   numeric:
273                         switch(afd->a_af) {
274 #ifdef INET6
275                         case AF_INET6:
276                         {
277                                 int error;
278
279                                 if ((error = ip6_parsenumeric(sa, addr, host,
280                                                               hostlen,
281                                                               flags)) != 0)
282                                         return(error);
283                                 break;
284                         }
285 #endif
286                         default:
287                                 if (inet_ntop(afd->a_af, addr, host,
288                                     hostlen) == NULL)
289                                         return EAI_SYSTEM;
290                                 break;
291                         }
292                 }
293         }
294         return(0);
295 }
296
297 #ifdef INET6
298 static int
299 ip6_parsenumeric(sa, addr, host, hostlen, flags)
300         const struct sockaddr *sa;
301         const char *addr;
302         char *host;
303         size_t hostlen;
304         int flags;
305 {
306         int numaddrlen;
307         char numaddr[512];
308
309         if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL)
310                 return EAI_SYSTEM;
311
312         numaddrlen = strlen(numaddr);
313         if (numaddrlen + 1 > hostlen) /* don't forget terminator */
314                 return EAI_OVERFLOW;
315         strlcpy(host, numaddr, hostlen);
316
317         if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
318                 char zonebuf[MAXHOSTNAMELEN];
319                 int zonelen;
320
321                 zonelen = ip6_sa2str(
322                     (const struct sockaddr_in6 *)(const void *)sa,
323                     zonebuf, sizeof(zonebuf), flags);
324                 if (zonelen < 0)
325                         return EAI_OVERFLOW;
326                 if (zonelen + 1 + numaddrlen + 1 > hostlen)
327                         return EAI_OVERFLOW;
328
329                 /* construct <numeric-addr><delim><zoneid> */
330                 memcpy(host + numaddrlen + 1, zonebuf,
331                     (size_t)zonelen);
332                 host[numaddrlen] = SCOPE_DELIMITER;
333                 host[numaddrlen + 1 + zonelen] = '\0';
334         }
335
336         return 0;
337 }
338
339 /* ARGSUSED */
340 static int
341 ip6_sa2str(sa6, buf, bufsiz, flags)
342         const struct sockaddr_in6 *sa6;
343         char *buf;
344         size_t bufsiz;
345         int flags;
346 {
347         unsigned int ifindex;
348         const struct in6_addr *a6;
349         int n;
350
351         ifindex = (unsigned int)sa6->sin6_scope_id;
352         a6 = &sa6->sin6_addr;
353
354 #ifdef NI_NUMERICSCOPE
355         if ((flags & NI_NUMERICSCOPE) != 0) {
356                 n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
357                 if (n < 0 || n >= bufsiz)
358                         return -1;
359                 else
360                         return n;
361         }
362 #endif
363
364         /* if_indextoname() does not take buffer size.  not a good api... */
365         if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) ||
366              IN6_IS_ADDR_MC_NODELOCAL(a6)) && bufsiz >= IF_NAMESIZE) {
367                 char *p = if_indextoname(ifindex, buf);
368                 if (p)
369                         return (strlen(p));
370         }
371
372         /* last resort */
373         n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
374         if (n < 0 || n >= bufsiz)
375                 return -1;
376         else
377                 return n;
378 }
379 #endif /* INET6 */
380 #endif