1
2 /* Copyright 2005 by Dominick Meglio
3 *
4 * Permission to use, copy, modify, and distribute this
5 * software and its documentation for any purpose and without
6 * fee is hereby granted, provided that the above copyright
7 * notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting
9 * documentation, and that the name of M.I.T. not be used in
10 * advertising or publicity pertaining to distribution of the
11 * software without specific, written prior permission.
12 * M.I.T. makes no representations about the suitability of
13 * this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
15 */
16 #include "ares_setup.h"
17
18 #ifdef HAVE_GETSERVBYPORT_R
19 # if !defined(GETSERVBYPORT_R_ARGS) || \
20 (GETSERVBYPORT_R_ARGS < 4) || (GETSERVBYPORT_R_ARGS > 6)
21 # error "you MUST specifiy a valid number of arguments for getservbyport_r"
22 # endif
23 #endif
24
25 #ifdef HAVE_NETINET_IN_H
26 # include <netinet/in.h>
27 #endif
28 #ifdef HAVE_NETDB_H
29 # include <netdb.h>
30 #endif
31 #ifdef HAVE_ARPA_INET_H
32 # include <arpa/inet.h>
33 #endif
34 #ifdef HAVE_ARPA_NAMESER_H
35 # include <arpa/nameser.h>
36 #else
37 # include "nameser.h"
38 #endif
39 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
40 # include <arpa/nameser_compat.h>
41 #endif
42
43 #ifdef HAVE_NET_IF_H
44 #include <net/if.h>
45 #endif
46
47 #include "ares.h"
48 #include "ares_ipv6.h"
49 #include "ares_nowarn.h"
50 #include "ares_private.h"
51
52 struct nameinfo_query {
53 ares_nameinfo_callback callback;
54 void *arg;
55 union {
56 struct sockaddr_in addr4;
57 struct sockaddr_in6 addr6;
58 } addr;
59 int family;
60 int flags;
61 int timeouts;
62 };
63
64 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
65 #define IPBUFSIZ \
66 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE)
67 #else
68 #define IPBUFSIZ \
69 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
70 #endif
71
72 static void nameinfo_callback(void *arg, int status, int timeouts,
73 struct hostent *host);
74 static char *lookup_service(unsigned short port, int flags,
75 char *buf, size_t buflen);
76 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
77 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid,
78 char *buf, size_t buflen);
79 #endif
80 STATIC_TESTABLE char *ares_striendstr(const char *s1, const char *s2);
81
ares_getnameinfo(ares_channel channel,const struct sockaddr * sa,ares_socklen_t salen,int flags,ares_nameinfo_callback callback,void * arg)82 void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa,
83 ares_socklen_t salen,
84 int flags, ares_nameinfo_callback callback, void *arg)
85 {
86 struct sockaddr_in *addr = NULL;
87 struct sockaddr_in6 *addr6 = NULL;
88 struct nameinfo_query *niquery;
89 unsigned int port = 0;
90
91 /* Validate socket address family and length */
92 if ((sa->sa_family == AF_INET) &&
93 (salen == sizeof(struct sockaddr_in)))
94 {
95 addr = (struct sockaddr_in *)sa;
96 port = addr->sin_port;
97 }
98 else if ((sa->sa_family == AF_INET6) &&
99 (salen == sizeof(struct sockaddr_in6)))
100 {
101 addr6 = (struct sockaddr_in6 *)sa;
102 port = addr6->sin6_port;
103 }
104 else
105 {
106 callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
107 return;
108 }
109
110 /* If neither, assume they want a host */
111 if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
112 flags |= ARES_NI_LOOKUPHOST;
113
114 /* All they want is a service, no need for DNS */
115 if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
116 {
117 char buf[33], *service;
118
119 service = lookup_service((unsigned short)(port & 0xffff),
120 flags, buf, sizeof(buf));
121 callback(arg, ARES_SUCCESS, 0, NULL, service);
122 return;
123 }
124
125 /* They want a host lookup */
126 if ((flags & ARES_NI_LOOKUPHOST))
127 {
128 /* A numeric host can be handled without DNS */
129 if ((flags & ARES_NI_NUMERICHOST))
130 {
131 char ipbuf[IPBUFSIZ];
132 char srvbuf[33];
133 char *service = NULL;
134 ipbuf[0] = 0;
135
136 /* Specifying not to lookup a host, but then saying a host
137 * is required has to be illegal.
138 */
139 if (flags & ARES_NI_NAMEREQD)
140 {
141 callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
142 return;
143 }
144 if (salen == sizeof(struct sockaddr_in6))
145 {
146 ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ);
147 /* If the system supports scope IDs, use it */
148 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
149 append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf));
150 #endif
151 }
152 else
153 {
154 ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
155 }
156 /* They also want a service */
157 if (flags & ARES_NI_LOOKUPSERVICE)
158 service = lookup_service((unsigned short)(port & 0xffff),
159 flags, srvbuf, sizeof(srvbuf));
160 callback(arg, ARES_SUCCESS, 0, ipbuf, service);
161 return;
162 }
163 /* This is where a DNS lookup becomes necessary */
164 else
165 {
166 niquery = ares_malloc(sizeof(struct nameinfo_query));
167 if (!niquery)
168 {
169 callback(arg, ARES_ENOMEM, 0, NULL, NULL);
170 return;
171 }
172 niquery->callback = callback;
173 niquery->arg = arg;
174 niquery->flags = flags;
175 niquery->timeouts = 0;
176 if (sa->sa_family == AF_INET)
177 {
178 niquery->family = AF_INET;
179 memcpy(&niquery->addr.addr4, addr, sizeof(niquery->addr.addr4));
180 ares_gethostbyaddr(channel, &addr->sin_addr,
181 sizeof(struct in_addr), AF_INET,
182 nameinfo_callback, niquery);
183 }
184 else
185 {
186 niquery->family = AF_INET6;
187 memcpy(&niquery->addr.addr6, addr6, sizeof(niquery->addr.addr6));
188 ares_gethostbyaddr(channel, &addr6->sin6_addr,
189 sizeof(struct ares_in6_addr), AF_INET6,
190 nameinfo_callback, niquery);
191 }
192 }
193 }
194 }
195
nameinfo_callback(void * arg,int status,int timeouts,struct hostent * host)196 static void nameinfo_callback(void *arg, int status, int timeouts,
197 struct hostent *host)
198 {
199 struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
200 char srvbuf[33];
201 char *service = NULL;
202
203 niquery->timeouts += timeouts;
204 if (status == ARES_SUCCESS)
205 {
206 /* They want a service too */
207 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
208 {
209 if (niquery->family == AF_INET)
210 service = lookup_service(niquery->addr.addr4.sin_port,
211 niquery->flags, srvbuf, sizeof(srvbuf));
212 else
213 service = lookup_service(niquery->addr.addr6.sin6_port,
214 niquery->flags, srvbuf, sizeof(srvbuf));
215 }
216 /* NOFQDN means we have to strip off the domain name portion. We do
217 this by determining our own domain name, then searching the string
218 for this domain name and removing it.
219 */
220 #ifdef HAVE_GETHOSTNAME
221 if (niquery->flags & ARES_NI_NOFQDN)
222 {
223 char buf[255];
224 char *domain;
225 gethostname(buf, 255);
226 if ((domain = strchr(buf, '.')) != NULL)
227 {
228 char *end = ares_striendstr(host->h_name, domain);
229 if (end)
230 *end = 0;
231 }
232 }
233 #endif
234 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts,
235 (char *)(host->h_name),
236 service);
237 ares_free(niquery);
238 return;
239 }
240 /* We couldn't find the host, but it's OK, we can use the IP */
241 else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD))
242 {
243 char ipbuf[IPBUFSIZ];
244 if (niquery->family == AF_INET)
245 ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf,
246 IPBUFSIZ);
247 else
248 {
249 ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf,
250 IPBUFSIZ);
251 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
252 append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf,
253 sizeof(ipbuf));
254 #endif
255 }
256 /* They want a service too */
257 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
258 {
259 if (niquery->family == AF_INET)
260 service = lookup_service(niquery->addr.addr4.sin_port,
261 niquery->flags, srvbuf, sizeof(srvbuf));
262 else
263 service = lookup_service(niquery->addr.addr6.sin6_port,
264 niquery->flags, srvbuf, sizeof(srvbuf));
265 }
266 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf,
267 service);
268 ares_free(niquery);
269 return;
270 }
271 niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL);
272 ares_free(niquery);
273 }
274
lookup_service(unsigned short port,int flags,char * buf,size_t buflen)275 static char *lookup_service(unsigned short port, int flags,
276 char *buf, size_t buflen)
277 {
278 const char *proto;
279 struct servent *sep;
280 #ifdef HAVE_GETSERVBYPORT_R
281 struct servent se;
282 #endif
283 char tmpbuf[4096];
284 char *name;
285 size_t name_len;
286
287 if (port)
288 {
289 if (flags & ARES_NI_NUMERICSERV)
290 sep = NULL;
291 else
292 {
293 if (flags & ARES_NI_UDP)
294 proto = "udp";
295 else if (flags & ARES_NI_SCTP)
296 proto = "sctp";
297 else if (flags & ARES_NI_DCCP)
298 proto = "dccp";
299 else
300 proto = "tcp";
301 #ifdef HAVE_GETSERVBYPORT_R
302 memset(&se, 0, sizeof(se));
303 sep = &se;
304 memset(tmpbuf, 0, sizeof(tmpbuf));
305 #if GETSERVBYPORT_R_ARGS == 6
306 if (getservbyport_r(port, proto, &se, (void *)tmpbuf,
307 sizeof(tmpbuf), &sep) != 0)
308 sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */
309 #elif GETSERVBYPORT_R_ARGS == 5
310 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf,
311 sizeof(tmpbuf));
312 #elif GETSERVBYPORT_R_ARGS == 4
313 if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0)
314 sep = NULL;
315 #else
316 /* Lets just hope the OS uses TLS! */
317 sep = getservbyport(port, proto);
318 #endif
319 #else
320 /* Lets just hope the OS uses TLS! */
321 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
322 sep = getservbyport(port, (char*)proto);
323 #else
324 sep = getservbyport(port, proto);
325 #endif
326 #endif
327 }
328 if (sep && sep->s_name)
329 {
330 /* get service name */
331 name = sep->s_name;
332 }
333 else
334 {
335 /* get port as a string */
336 sprintf(tmpbuf, "%u", (unsigned int)ntohs(port));
337 name = tmpbuf;
338 }
339 name_len = strlen(name);
340 if (name_len < buflen)
341 /* return it if buffer big enough */
342 memcpy(buf, name, name_len + 1);
343 else
344 /* avoid reusing previous one */
345 buf[0] = '\0'; /* LCOV_EXCL_LINE: no real service names are too big */
346 return buf;
347 }
348 buf[0] = '\0';
349 return NULL;
350 }
351
352 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
append_scopeid(struct sockaddr_in6 * addr6,unsigned int flags,char * buf,size_t buflen)353 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags,
354 char *buf, size_t buflen)
355 {
356 #ifdef HAVE_IF_INDEXTONAME
357 int is_ll, is_mcll;
358 #endif
359 char tmpbuf[IF_NAMESIZE + 2];
360 size_t bufl;
361 int is_scope_long = sizeof(addr6->sin6_scope_id) > sizeof(unsigned int);
362
363 tmpbuf[0] = '%';
364
365 #ifdef HAVE_IF_INDEXTONAME
366 is_ll = IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr);
367 is_mcll = IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr);
368 if ((flags & ARES_NI_NUMERICSCOPE) ||
369 (!is_ll && !is_mcll))
370 {
371 if (is_scope_long)
372 {
373 sprintf(&tmpbuf[1], "%lu", (unsigned long)addr6->sin6_scope_id);
374 }
375 else
376 {
377 sprintf(&tmpbuf[1], "%u", (unsigned int)addr6->sin6_scope_id);
378 }
379 }
380 else
381 {
382 if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
383 {
384 if (is_scope_long)
385 {
386 sprintf(&tmpbuf[1], "%lu", (unsigned long)addr6->sin6_scope_id);
387 }
388 else
389 {
390 sprintf(&tmpbuf[1], "%u", (unsigned int)addr6->sin6_scope_id);
391 }
392 }
393 }
394 #else
395 if (is_scope_long)
396 {
397 sprintf(&tmpbuf[1], "%lu", (unsigned long)addr6->sin6_scope_id);
398 }
399 else
400 {
401 sprintf(&tmpbuf[1], "%u", (unsigned int)addr6->sin6_scope_id);
402 }
403 (void) flags;
404 #endif
405 tmpbuf[IF_NAMESIZE + 1] = '\0';
406 bufl = strlen(buf);
407
408 if(bufl + strlen(tmpbuf) < buflen)
409 /* only append the scopeid string if it fits in the target buffer */
410 strcpy(&buf[bufl], tmpbuf);
411 }
412 #endif
413
414 /* Determines if s1 ends with the string in s2 (case-insensitive) */
ares_striendstr(const char * s1,const char * s2)415 STATIC_TESTABLE char *ares_striendstr(const char *s1, const char *s2)
416 {
417 const char *c1, *c2, *c1_begin;
418 int lo1, lo2;
419 size_t s1_len = strlen(s1), s2_len = strlen(s2);
420
421 /* If the substr is longer than the full str, it can't match */
422 if (s2_len > s1_len)
423 return NULL;
424
425 /* Jump to the end of s1 minus the length of s2 */
426 c1_begin = s1+s1_len-s2_len;
427 c1 = (const char *)c1_begin;
428 c2 = s2;
429 while (c2 < s2+s2_len)
430 {
431 lo1 = TOLOWER(*c1);
432 lo2 = TOLOWER(*c2);
433 if (lo1 != lo2)
434 return NULL;
435 else
436 {
437 c1++;
438 c2++;
439 }
440 }
441 return (char *)c1_begin;
442 }
443
ares__is_onion_domain(const char * name)444 int ares__is_onion_domain(const char *name)
445 {
446 if (ares_striendstr(name, ".onion"))
447 return 1;
448
449 if (ares_striendstr(name, ".onion."))
450 return 1;
451
452 return 0;
453 }
454