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