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