ares_getnameinfo.c 13 KB

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