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