lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 1 | |
| 2 | /* Copyright 1998 by the Massachusetts Institute of Technology. |
| 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_NETINET_IN_H |
| 19 | # include <netinet/in.h> |
| 20 | #endif |
| 21 | #ifdef HAVE_NETDB_H |
| 22 | # include <netdb.h> |
| 23 | #endif |
| 24 | #ifdef HAVE_ARPA_INET_H |
| 25 | # include <arpa/inet.h> |
| 26 | #endif |
| 27 | #ifdef HAVE_ARPA_NAMESER_H |
| 28 | # include <arpa/nameser.h> |
| 29 | #else |
| 30 | # include "nameser.h" |
| 31 | #endif |
| 32 | #ifdef HAVE_ARPA_NAMESER_COMPAT_H |
| 33 | # include <arpa/nameser_compat.h> |
| 34 | #endif |
| 35 | |
| 36 | #include "ares.h" |
| 37 | #include "ares_inet_net_pton.h" |
| 38 | #include "ares_platform.h" |
| 39 | #include "ares_private.h" |
| 40 | |
| 41 | #ifdef WATT32 |
| 42 | #undef WIN32 |
| 43 | #endif |
| 44 | |
| 45 | struct addr_query { |
| 46 | /* Arguments passed to ares_gethostbyaddr() */ |
| 47 | ares_channel channel; |
| 48 | struct ares_addr addr; |
| 49 | ares_host_callback callback; |
| 50 | void *arg; |
| 51 | |
| 52 | const char *remaining_lookups; |
| 53 | int timeouts; |
| 54 | }; |
| 55 | |
| 56 | static void next_lookup(struct addr_query *aquery); |
| 57 | static void addr_callback(void *arg, int status, int timeouts, |
| 58 | unsigned char *abuf, int alen); |
| 59 | static void end_aquery(struct addr_query *aquery, int status, |
| 60 | struct hostent *host); |
| 61 | static int file_lookup(struct ares_addr *addr, struct hostent **host); |
| 62 | static void ptr_rr_name(char *name, const struct ares_addr *addr); |
| 63 | |
| 64 | void ares_gethostbyaddr(ares_channel channel, const void *addr, int addrlen, |
| 65 | int family, ares_host_callback callback, void *arg) |
| 66 | { |
| 67 | struct addr_query *aquery; |
| 68 | |
| 69 | if (family != AF_INET && family != AF_INET6) |
| 70 | { |
| 71 | callback(arg, ARES_ENOTIMP, 0, NULL); |
| 72 | return; |
| 73 | } |
| 74 | |
| 75 | if ((family == AF_INET && addrlen != sizeof(aquery->addr.addrV4)) || |
| 76 | (family == AF_INET6 && addrlen != sizeof(aquery->addr.addrV6))) |
| 77 | { |
| 78 | callback(arg, ARES_ENOTIMP, 0, NULL); |
| 79 | return; |
| 80 | } |
| 81 | |
| 82 | aquery = ares_malloc(sizeof(struct addr_query)); |
| 83 | if (!aquery) |
| 84 | { |
| 85 | callback(arg, ARES_ENOMEM, 0, NULL); |
| 86 | return; |
| 87 | } |
| 88 | aquery->channel = channel; |
| 89 | if (family == AF_INET) |
| 90 | memcpy(&aquery->addr.addrV4, addr, sizeof(aquery->addr.addrV4)); |
| 91 | else |
| 92 | memcpy(&aquery->addr.addrV6, addr, sizeof(aquery->addr.addrV6)); |
| 93 | aquery->addr.family = family; |
| 94 | aquery->callback = callback; |
| 95 | aquery->arg = arg; |
| 96 | aquery->remaining_lookups = channel->lookups; |
| 97 | aquery->timeouts = 0; |
| 98 | |
| 99 | next_lookup(aquery); |
| 100 | } |
| 101 | |
| 102 | static void next_lookup(struct addr_query *aquery) |
| 103 | { |
| 104 | const char *p; |
| 105 | char name[128]; |
| 106 | int status; |
| 107 | struct hostent *host; |
| 108 | |
| 109 | for (p = aquery->remaining_lookups; *p; p++) |
| 110 | { |
| 111 | switch (*p) |
| 112 | { |
| 113 | case 'b': |
| 114 | ptr_rr_name(name, &aquery->addr); |
| 115 | aquery->remaining_lookups = p + 1; |
| 116 | ares_query(aquery->channel, name, C_IN, T_PTR, addr_callback, |
| 117 | aquery); |
| 118 | return; |
| 119 | case 'f': |
| 120 | status = file_lookup(&aquery->addr, &host); |
| 121 | |
| 122 | /* this status check below previously checked for !ARES_ENOTFOUND, |
| 123 | but we should not assume that this single error code is the one |
| 124 | that can occur, as that is in fact no longer the case */ |
| 125 | if (status == ARES_SUCCESS) |
| 126 | { |
| 127 | end_aquery(aquery, status, host); |
| 128 | return; |
| 129 | } |
| 130 | break; |
| 131 | } |
| 132 | } |
| 133 | end_aquery(aquery, ARES_ENOTFOUND, NULL); |
| 134 | } |
| 135 | |
| 136 | static void addr_callback(void *arg, int status, int timeouts, |
| 137 | unsigned char *abuf, int alen) |
| 138 | { |
| 139 | struct addr_query *aquery = (struct addr_query *) arg; |
| 140 | struct hostent *host; |
| 141 | size_t addrlen; |
| 142 | |
| 143 | aquery->timeouts += timeouts; |
| 144 | if (status == ARES_SUCCESS) |
| 145 | { |
| 146 | if (aquery->addr.family == AF_INET) |
| 147 | { |
| 148 | addrlen = sizeof(aquery->addr.addrV4); |
| 149 | status = ares_parse_ptr_reply(abuf, alen, &aquery->addr.addrV4, |
| 150 | (int)addrlen, AF_INET, &host); |
| 151 | } |
| 152 | else |
| 153 | { |
| 154 | addrlen = sizeof(aquery->addr.addrV6); |
| 155 | status = ares_parse_ptr_reply(abuf, alen, &aquery->addr.addrV6, |
| 156 | (int)addrlen, AF_INET6, &host); |
| 157 | } |
| 158 | end_aquery(aquery, status, host); |
| 159 | } |
| 160 | else if (status == ARES_EDESTRUCTION) |
| 161 | end_aquery(aquery, status, NULL); |
| 162 | else |
| 163 | next_lookup(aquery); |
| 164 | } |
| 165 | |
| 166 | static void end_aquery(struct addr_query *aquery, int status, |
| 167 | struct hostent *host) |
| 168 | { |
| 169 | aquery->callback(aquery->arg, status, aquery->timeouts, host); |
| 170 | if (host) |
| 171 | ares_free_hostent(host); |
| 172 | ares_free(aquery); |
| 173 | } |
| 174 | |
| 175 | static int file_lookup(struct ares_addr *addr, struct hostent **host) |
| 176 | { |
| 177 | FILE *fp; |
| 178 | int status; |
| 179 | int error; |
| 180 | |
| 181 | #ifdef WIN32 |
| 182 | char PATH_HOSTS[MAX_PATH]; |
| 183 | win_platform platform; |
| 184 | |
| 185 | PATH_HOSTS[0] = '\0'; |
| 186 | |
| 187 | platform = ares__getplatform(); |
| 188 | |
| 189 | if (platform == WIN_NT) { |
| 190 | char tmp[MAX_PATH]; |
| 191 | HKEY hkeyHosts; |
| 192 | |
| 193 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, |
| 194 | &hkeyHosts) == ERROR_SUCCESS) |
| 195 | { |
| 196 | DWORD dwLength = MAX_PATH; |
| 197 | RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, |
| 198 | &dwLength); |
| 199 | ExpandEnvironmentStrings(tmp, PATH_HOSTS, MAX_PATH); |
| 200 | RegCloseKey(hkeyHosts); |
| 201 | } |
| 202 | } |
| 203 | else if (platform == WIN_9X) |
| 204 | GetWindowsDirectory(PATH_HOSTS, MAX_PATH); |
| 205 | else |
| 206 | return ARES_ENOTFOUND; |
| 207 | |
| 208 | strcat(PATH_HOSTS, WIN_PATH_HOSTS); |
| 209 | |
| 210 | #elif defined(WATT32) |
| 211 | extern const char *_w32_GetHostsFile (void); |
| 212 | const char *PATH_HOSTS = _w32_GetHostsFile(); |
| 213 | |
| 214 | if (!PATH_HOSTS) |
| 215 | return ARES_ENOTFOUND; |
| 216 | #endif |
| 217 | |
| 218 | fp = fopen(PATH_HOSTS, "r"); |
| 219 | if (!fp) |
| 220 | { |
| 221 | error = ERRNO; |
| 222 | switch(error) |
| 223 | { |
| 224 | case ENOENT: |
| 225 | case ESRCH: |
| 226 | return ARES_ENOTFOUND; |
| 227 | default: |
| 228 | DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", |
| 229 | error, strerror(error))); |
| 230 | DEBUGF(fprintf(stderr, "Error opening file: %s\n", |
| 231 | PATH_HOSTS)); |
| 232 | *host = NULL; |
| 233 | return ARES_EFILE; |
| 234 | } |
| 235 | } |
| 236 | while ((status = ares__get_hostent(fp, addr->family, host)) == ARES_SUCCESS) |
| 237 | { |
| 238 | if (addr->family != (*host)->h_addrtype) |
| 239 | { |
| 240 | ares_free_hostent(*host); |
| 241 | continue; |
| 242 | } |
| 243 | if (addr->family == AF_INET) |
| 244 | { |
| 245 | if (memcmp((*host)->h_addr, &addr->addrV4, |
| 246 | sizeof(addr->addrV4)) == 0) |
| 247 | break; |
| 248 | } |
| 249 | else if (addr->family == AF_INET6) |
| 250 | { |
| 251 | if (memcmp((*host)->h_addr, &addr->addrV6, |
| 252 | sizeof(addr->addrV6)) == 0) |
| 253 | break; |
| 254 | } |
| 255 | ares_free_hostent(*host); |
| 256 | } |
| 257 | fclose(fp); |
| 258 | if (status == ARES_EOF) |
| 259 | status = ARES_ENOTFOUND; |
| 260 | if (status != ARES_SUCCESS) |
| 261 | *host = NULL; |
| 262 | return status; |
| 263 | } |
| 264 | |
| 265 | static void ptr_rr_name(char *name, const struct ares_addr *addr) |
| 266 | { |
| 267 | if (addr->family == AF_INET) |
| 268 | { |
| 269 | unsigned long laddr = ntohl(addr->addrV4.s_addr); |
| 270 | unsigned long a1 = (laddr >> 24UL) & 0xFFUL; |
| 271 | unsigned long a2 = (laddr >> 16UL) & 0xFFUL; |
| 272 | unsigned long a3 = (laddr >> 8UL) & 0xFFUL; |
| 273 | unsigned long a4 = laddr & 0xFFUL; |
| 274 | sprintf(name, "%lu.%lu.%lu.%lu.in-addr.arpa", a4, a3, a2, a1); |
| 275 | } |
| 276 | else |
| 277 | { |
| 278 | unsigned char *bytes = (unsigned char *)&addr->addrV6; |
| 279 | /* There are too many arguments to do this in one line using |
| 280 | * minimally C89-compliant compilers */ |
| 281 | sprintf(name, |
| 282 | "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.", |
| 283 | bytes[15]&0xf, bytes[15] >> 4, bytes[14]&0xf, bytes[14] >> 4, |
| 284 | bytes[13]&0xf, bytes[13] >> 4, bytes[12]&0xf, bytes[12] >> 4, |
| 285 | bytes[11]&0xf, bytes[11] >> 4, bytes[10]&0xf, bytes[10] >> 4, |
| 286 | bytes[9]&0xf, bytes[9] >> 4, bytes[8]&0xf, bytes[8] >> 4); |
| 287 | sprintf(name+strlen(name), |
| 288 | "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa", |
| 289 | bytes[7]&0xf, bytes[7] >> 4, bytes[6]&0xf, bytes[6] >> 4, |
| 290 | bytes[5]&0xf, bytes[5] >> 4, bytes[4]&0xf, bytes[4] >> 4, |
| 291 | bytes[3]&0xf, bytes[3] >> 4, bytes[2]&0xf, bytes[2] >> 4, |
| 292 | bytes[1]&0xf, bytes[1] >> 4, bytes[0]&0xf, bytes[0] >> 4); |
| 293 | } |
| 294 | } |