getnameinfo.c revision 1.9 1 /* $NetBSD: getnameinfo.c,v 1.9 2000/01/22 23:59:46 mycroft 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 * - Thread safe-ness must be checked
35 * - Return values. There seems to be no standard for return value (RFC2553)
36 * but INRIA implementation returns EAI_xxx defined for getaddrinfo().
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).
40 */
41
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <net/if.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <arpa/nameser.h>
48 #include <netdb.h>
49 #include <resolv.h>
50 #include <string.h>
51 #include <stddef.h>
52
53 #define SUCCESS 0
54 #define ANY 0
55 #define YES 1
56 #define NO 0
57
58 static struct afd {
59 int a_af;
60 int a_addrlen;
61 int a_socklen;
62 int a_off;
63 } afdl [] = {
64 #ifdef INET6
65 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
66 offsetof(struct sockaddr_in6, sin6_addr)},
67 #endif
68 {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
69 offsetof(struct sockaddr_in, sin_addr)},
70 {0, 0, 0},
71 };
72
73 struct sockinet {
74 u_char si_len;
75 u_char si_family;
76 u_short si_port;
77 };
78
79 #define ENI_NOSOCKET 0
80 #define ENI_NOSERVNAME 1
81 #define ENI_NOHOSTNAME 2
82 #define ENI_MEMORY 3
83 #define ENI_SYSTEM 4
84 #define ENI_FAMILY 5
85 #define ENI_SALEN 6
86
87 int
88 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
89 const struct sockaddr *sa;
90 size_t salen;
91 char *host;
92 size_t hostlen;
93 char *serv;
94 size_t servlen;
95 int flags;
96 {
97 struct afd *afd;
98 struct servent *sp;
99 struct hostent *hp;
100 u_short port;
101 int family, i;
102 const char *addr;
103 char *p;
104 u_int32_t v4a;
105 #ifdef USE_GETIPNODEBY
106 int h_error;
107 #endif
108 char numserv[512];
109 char numaddr[512];
110
111 if (sa == NULL)
112 return ENI_NOSOCKET;
113
114 if (sa->sa_len != salen)
115 return ENI_SALEN;
116
117 family = sa->sa_family;
118 for (i = 0; afdl[i].a_af; i++)
119 if (afdl[i].a_af == family) {
120 afd = &afdl[i];
121 goto found;
122 }
123 return ENI_FAMILY;
124
125 found:
126 if (salen != afd->a_socklen)
127 return ENI_SALEN;
128
129 port = ((const struct sockinet *)sa)->si_port; /* network byte order */
130 addr = (const char *)sa + afd->a_off;
131
132 if (serv == NULL || servlen == 0) {
133 /*
134 * do nothing in this case.
135 * in case you are wondering if "&&" is more correct than
136 * "||" here: RFC2553 says that serv == NULL OR servlen == 0
137 * means that the caller does not want the result.
138 */
139 } else {
140 if (flags & NI_NUMERICSERV)
141 sp = NULL;
142 else {
143 sp = getservbyport(port,
144 (flags & NI_DGRAM) ? "udp" : "tcp");
145 }
146 if (sp) {
147 if (strlen(sp->s_name) > servlen)
148 return ENI_MEMORY;
149 strcpy(serv, sp->s_name);
150 } else {
151 snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
152 if (strlen(numserv) > servlen)
153 return ENI_MEMORY;
154 strcpy(serv, numserv);
155 }
156 }
157
158 switch (sa->sa_family) {
159 case AF_INET:
160 v4a = (u_int32_t)
161 ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr);
162 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
163 flags |= NI_NUMERICHOST;
164 v4a >>= IN_CLASSA_NSHIFT;
165 if (v4a == 0)
166 flags |= NI_NUMERICHOST;
167 break;
168 #ifdef INET6
169 case AF_INET6:
170 {
171 const struct sockaddr_in6 *sin6;
172 sin6 = (const struct sockaddr_in6 *)sa;
173 switch (sin6->sin6_addr.s6_addr[0]) {
174 case 0x00:
175 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
176 ;
177 else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
178 ;
179 else
180 flags |= NI_NUMERICHOST;
181 break;
182 default:
183 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
184 flags |= NI_NUMERICHOST;
185 }
186 else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
187 flags |= NI_NUMERICHOST;
188 break;
189 }
190 }
191 break;
192 #endif
193 }
194 if (host == NULL || hostlen == 0) {
195 /*
196 * do nothing in this case.
197 * in case you are wondering if "&&" is more correct than
198 * "||" here: RFC2553 says that host == NULL OR hostlen == 0
199 * means that the caller does not want the result.
200 */
201 } else if (flags & NI_NUMERICHOST) {
202 /* NUMERICHOST and NAMEREQD conflicts with each other */
203 if (flags & NI_NAMEREQD)
204 return ENI_NOHOSTNAME;
205 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
206 == NULL)
207 return ENI_SYSTEM;
208 if (strlen(numaddr) > hostlen)
209 return ENI_MEMORY;
210 strcpy(host, numaddr);
211 #if defined(INET6) && defined(NI_WITHSCOPEID)
212 if (afd->a_af == AF_INET6 &&
213 (IN6_IS_ADDR_LINKLOCAL((const struct in6_addr *)addr) ||
214 IN6_IS_ADDR_MULTICAST((const struct in6_addr *)addr)) &&
215 ((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
216 #ifndef ALWAYS_WITHSCOPE
217 if (flags & NI_WITHSCOPEID)
218 #endif /* !ALWAYS_WITHSCOPE */
219 {
220 char *ep = strchr(host, '\0');
221 unsigned int ifindex =
222 ((const struct sockaddr_in6 *)sa)->sin6_scope_id;
223
224 *ep = SCOPE_DELIMITER;
225 if ((if_indextoname(ifindex, ep + 1)) == NULL)
226 /* XXX what should we do? */
227 strncpy(ep + 1, "???", 3);
228 }
229 }
230 #endif /* INET6 */
231 } else {
232 #ifdef USE_GETIPNODEBY
233 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
234 #else
235 hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
236 #endif
237
238 if (hp) {
239 if (flags & NI_NOFQDN) {
240 p = strchr(hp->h_name, '.');
241 if (p) *p = '\0';
242 }
243 if (strlen(hp->h_name) > hostlen) {
244 #ifdef USE_GETIPNODEBY
245 freehostent(hp);
246 #endif
247 return ENI_MEMORY;
248 }
249 strcpy(host, hp->h_name);
250 #ifdef USE_GETIPNODEBY
251 freehostent(hp);
252 #endif
253 } else {
254 if (flags & NI_NAMEREQD)
255 return ENI_NOHOSTNAME;
256 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
257 == NULL)
258 return ENI_NOHOSTNAME;
259 if (strlen(numaddr) > hostlen)
260 return ENI_MEMORY;
261 strcpy(host, numaddr);
262 }
263 }
264 return SUCCESS;
265 }
266