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