]> Pileus Git - ~andy/fetchmail/blob - KAME/getnameinfo.c
Sign .xz; upload to sf.net; upload .xz to local site.
[~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 #ifdef HAVE_INTTYPES_H
75 #include <inttypes.h>
76 #endif
77 #include "fetchmail.h"
78 #include "getaddrinfo.h"
79
80 static const struct afd {
81         int a_af;
82         int a_addrlen;
83         int a_socklen;
84         int a_off;
85         int a_portoff;
86 } afdl [] = {
87 #ifdef INET6
88         {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
89          offsetof(struct sockaddr_in6, sin6_addr),
90          offsetof(struct sockaddr_in6, sin6_port)},
91 #endif
92         {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
93          offsetof(struct sockaddr_in, sin_addr),
94          offsetof(struct sockaddr_in, sin_port)},
95         {0, 0, 0, 0, 0},
96 };
97
98 #ifdef INET6
99 static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
100                                  size_t, int));
101 static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int));
102 #endif
103
104 int
105 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
106         const struct sockaddr *sa;
107 #ifdef HAVE_SOCKLEN_T
108         socklen_t salen;
109 #else
110         unsigned int salen;
111 #endif
112         char *host;
113         size_t hostlen;
114         char *serv;
115         size_t servlen;
116         int flags;
117 {
118         const struct afd *afd;
119         struct servent *sp;
120         struct hostent *hp;
121         u_short port;
122         int family, i;
123         const char *addr;
124         u_int32_t v4a;
125         int h_error;
126         char numserv[512];
127         char numaddr[512];
128
129         if (sa == NULL)
130                 return EAI_FAIL;
131
132 #ifdef HAVE_SA_LEN      /*XXX*/
133         if (sa->sa_len != salen)
134                 return EAI_FAIL;
135 #endif
136
137         family = sa->sa_family;
138         for (i = 0; afdl[i].a_af; i++)
139                 if (afdl[i].a_af == family) {
140                         afd = &afdl[i];
141                         goto found;
142                 }
143         return EAI_FAMILY;
144
145  found:
146         if (salen != afd->a_socklen)
147                 return EAI_FAIL;
148
149         /* network byte order */
150         memcpy(&port, (const char *)sa + afd->a_portoff, sizeof(port));
151         addr = (const char *)sa + afd->a_off;
152
153         if (serv == NULL || servlen == 0) {
154                 /*
155                  * do nothing in this case.
156                  * in case you are wondering if "&&" is more correct than
157                  * "||" here: RFC3493 says that serv == NULL OR servlen == 0
158                  * means that the caller does not want the result.
159                  */
160         } else {
161                 if (flags & NI_NUMERICSERV)
162                         sp = NULL;
163                 else {
164                         sp = getservbyport(port,
165                                 (flags & NI_DGRAM) ? "udp" : "tcp");
166                 }
167                 if (sp) {
168                         if (strlen(sp->s_name) + 1 > servlen)
169                                 return EAI_OVERFLOW;
170                         strlcpy(serv, sp->s_name, servlen);
171                 } else {
172                         snprintf(numserv, sizeof(numserv), "%u", ntohs(port));
173                         if (strlen(numserv) + 1 > servlen)
174                                 return EAI_OVERFLOW;
175                         strlcpy(serv, numserv, servlen);
176                 }
177         }
178
179         switch (sa->sa_family) {
180         case AF_INET:
181                 v4a = (u_int32_t)
182                     ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr);
183                 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
184                         flags |= NI_NUMERICHOST;
185                 v4a >>= IN_CLASSA_NSHIFT;
186                 if (v4a == 0)
187                         flags |= NI_NUMERICHOST;
188                 break;
189 #ifdef INET6
190         case AF_INET6:
191             {
192                 const struct sockaddr_in6 *sin6;
193                 sin6 = (const struct sockaddr_in6 *)sa;
194                 switch (sin6->sin6_addr.s6_addr[0]) {
195                 case 0x00:
196                         if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
197                                 ;
198                         else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
199                                 ;
200                         else
201                                 flags |= NI_NUMERICHOST;
202                         break;
203                 default:
204                         if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
205                                 flags |= NI_NUMERICHOST;
206                         else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
207                                 flags |= NI_NUMERICHOST;
208                         break;
209                 }
210             }
211                 break;
212 #endif
213         }
214         if (host == NULL || hostlen == 0) {
215                 /*
216                  * do nothing in this case.
217                  * in case you are wondering if "&&" is more correct than
218                  * "||" here: RFC3493 says that host == NULL or hostlen == 0
219                  * means that the caller does not want the result.
220                  */
221         } else if (flags & NI_NUMERICHOST) {
222                 /* NUMERICHOST and NAMEREQD conflicts with each other */
223                 if (flags & NI_NAMEREQD)
224                         return EAI_NONAME;
225
226                 goto numeric;
227         } else {
228 #ifdef USE_GETIPNODEBY
229                 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
230 #else
231                 hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
232 #ifdef HAVE_H_ERRNO
233                 h_error = h_errno;
234 #else
235                 h_error = EINVAL;
236 #endif
237 #endif
238
239                 if (hp) {
240 #if 0
241                         if (flags & NI_NOFQDN) {
242                                 /*
243                                  * According to RFC3493 section 6.2, NI_NOFQDN
244                                  * means "node name portion of the FQDN shall
245                                  * be returned for local hosts."  The following
246                                  * code tries to implement it by returning the
247                                  * first label (the part before the first
248                                  * period) of the FQDN.  However, it is not
249                                  * clear if this always makes sense, since the
250                                  * given address may be outside of "local
251                                  * hosts."  Due to the unclear description, we
252                                  * disable the code in this implementation.
253                                  */
254                                 char *p;
255                                 p = strchr(hp->h_name, '.');
256                                 if (p)
257                                         *p = '\0';
258                         }
259 #endif
260                         if (strlen(hp->h_name) + 1 > hostlen) {
261 #ifdef USE_GETIPNODEBY
262                                 freehostent(hp);
263 #endif
264                                 return EAI_OVERFLOW;
265                         }
266                         strlcpy(host, hp->h_name, hostlen);
267 #ifdef USE_GETIPNODEBY
268                         freehostent(hp);
269 #endif
270                 } else {
271                         if (flags & NI_NAMEREQD)
272                                 return EAI_NONAME;
273
274                   numeric:
275                         switch(afd->a_af) {
276 #ifdef INET6
277                         case AF_INET6:
278                         {
279                                 int error;
280
281                                 if ((error = ip6_parsenumeric(sa, addr, host,
282                                                               hostlen,
283                                                               flags)) != 0)
284                                         return(error);
285                                 break;
286                         }
287 #endif
288                         default:
289 #ifdef HAVE_INET_NTOP
290                                 if (inet_ntop(afd->a_af, addr, host,
291                                     hostlen) == NULL)
292                                         return EAI_SYSTEM;
293 #else
294                                 if (afd->a_af == AF_INET) {
295                                     struct in_addr addr_tmp;
296                                     addr_tmp.s_addr = addr;
297                                     strlcpy(host, inet_ntoa(addr_tmp), hostlen);
298                                 }
299                                 else
300                                     return EAI_FAMILY;
301 #endif
302                                 break;
303                         }
304                 }
305         }
306         return(0);
307 }
308
309 #ifdef INET6
310 static int
311 ip6_parsenumeric(sa, addr, host, hostlen, flags)
312         const struct sockaddr *sa;
313         const char *addr;
314         char *host;
315         size_t hostlen;
316         int flags;
317 {
318         int numaddrlen;
319         char numaddr[512];
320
321         if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL)
322                 return EAI_SYSTEM;
323
324         numaddrlen = strlen(numaddr);
325         if (numaddrlen + 1 > hostlen) /* don't forget terminator */
326                 return EAI_OVERFLOW;
327         strlcpy(host, numaddr, hostlen);
328
329         if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
330                 char zonebuf[MAXHOSTNAMELEN];
331                 int zonelen;
332
333                 zonelen = ip6_sa2str(
334                     (const struct sockaddr_in6 *)(const void *)sa,
335                     zonebuf, sizeof(zonebuf), flags);
336                 if (zonelen < 0)
337                         return EAI_OVERFLOW;
338                 if (zonelen + 1 + numaddrlen + 1 > hostlen)
339                         return EAI_OVERFLOW;
340
341                 /* construct <numeric-addr><delim><zoneid> */
342                 memcpy(host + numaddrlen + 1, zonebuf,
343                     (size_t)zonelen);
344                 host[numaddrlen] = SCOPE_DELIMITER;
345                 host[numaddrlen + 1 + zonelen] = '\0';
346         }
347
348         return 0;
349 }
350
351 /* ARGSUSED */
352 static int
353 ip6_sa2str(sa6, buf, bufsiz, flags)
354         const struct sockaddr_in6 *sa6;
355         char *buf;
356         size_t bufsiz;
357         int flags;
358 {
359         unsigned int ifindex;
360         const struct in6_addr *a6;
361         int n;
362
363         ifindex = (unsigned int)sa6->sin6_scope_id;
364         a6 = &sa6->sin6_addr;
365
366 #ifdef NI_NUMERICSCOPE
367         if ((flags & NI_NUMERICSCOPE) != 0) {
368                 n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
369                 if (n < 0 || n >= bufsiz)
370                         return -1;
371                 else
372                         return n;
373         }
374 #endif
375
376         /* if_indextoname() does not take buffer size.  not a good api... */
377         if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) ||
378              IN6_IS_ADDR_MC_NODELOCAL(a6)) && bufsiz >= IF_NAMESIZE) {
379                 char *p = if_indextoname(ifindex, buf);
380                 if (p)
381                         return (strlen(p));
382         }
383
384         /* last resort */
385         n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
386         if (n < 0 || n >= bufsiz)
387                 return -1;
388         else
389                 return n;
390 }
391 #endif /* INET6 */
392 #endif