ares_gethostbyaddr.c (7507B)
1 /* MIT License 2 * 3 * Copyright (c) 1998 Massachusetts Institute of Technology 4 * Copyright (c) The c-ares project and its contributors 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 * 25 * SPDX-License-Identifier: MIT 26 */ 27 28 #include "ares_private.h" 29 30 #ifdef HAVE_NETINET_IN_H 31 # include <netinet/in.h> 32 #endif 33 #ifdef HAVE_NETDB_H 34 # include <netdb.h> 35 #endif 36 #ifdef HAVE_ARPA_INET_H 37 # include <arpa/inet.h> 38 #endif 39 40 #include "ares_nameser.h" 41 #include "ares_inet_net_pton.h" 42 43 struct addr_query { 44 /* Arguments passed to ares_gethostbyaddr() */ 45 ares_channel_t *channel; 46 struct ares_addr addr; 47 ares_host_callback callback; 48 void *arg; 49 char *lookups; /* duplicate memory from channel for ares_reinit() */ 50 const char *remaining_lookups; 51 size_t timeouts; 52 }; 53 54 static void next_lookup(struct addr_query *aquery); 55 static void addr_callback(void *arg, ares_status_t status, size_t timeouts, 56 const ares_dns_record_t *dnsrec); 57 static void end_aquery(struct addr_query *aquery, ares_status_t status, 58 struct hostent *host); 59 static ares_status_t file_lookup(ares_channel_t *channel, 60 const struct ares_addr *addr, 61 struct hostent **host); 62 63 void ares_gethostbyaddr_nolock(ares_channel_t *channel, const void *addr, 64 int addrlen, int family, 65 ares_host_callback callback, void *arg) 66 { 67 struct addr_query *aquery; 68 69 if (family != AF_INET && family != AF_INET6) { 70 callback(arg, ARES_ENOTIMP, 0, NULL); 71 return; 72 } 73 74 if ((family == AF_INET && addrlen != sizeof(aquery->addr.addr.addr4)) || 75 (family == AF_INET6 && addrlen != sizeof(aquery->addr.addr.addr6))) { 76 callback(arg, ARES_ENOTIMP, 0, NULL); 77 return; 78 } 79 80 aquery = ares_malloc(sizeof(struct addr_query)); 81 if (!aquery) { 82 callback(arg, ARES_ENOMEM, 0, NULL); 83 return; 84 } 85 aquery->lookups = ares_strdup(channel->lookups); 86 if (aquery->lookups == NULL) { 87 /* LCOV_EXCL_START: OutOfMemory */ 88 ares_free(aquery); 89 callback(arg, ARES_ENOMEM, 0, NULL); 90 return; 91 /* LCOV_EXCL_STOP */ 92 } 93 aquery->channel = channel; 94 if (family == AF_INET) { 95 memcpy(&aquery->addr.addr.addr4, addr, sizeof(aquery->addr.addr.addr4)); 96 } else { 97 memcpy(&aquery->addr.addr.addr6, addr, sizeof(aquery->addr.addr.addr6)); 98 } 99 aquery->addr.family = family; 100 aquery->callback = callback; 101 aquery->arg = arg; 102 aquery->remaining_lookups = aquery->lookups; 103 aquery->timeouts = 0; 104 105 next_lookup(aquery); 106 } 107 108 void ares_gethostbyaddr(ares_channel_t *channel, const void *addr, int addrlen, 109 int family, ares_host_callback callback, void *arg) 110 { 111 if (channel == NULL) { 112 return; 113 } 114 ares_channel_lock(channel); 115 ares_gethostbyaddr_nolock(channel, addr, addrlen, family, callback, arg); 116 ares_channel_unlock(channel); 117 } 118 119 static void next_lookup(struct addr_query *aquery) 120 { 121 const char *p; 122 ares_status_t status; 123 struct hostent *host = NULL; 124 char *name; 125 126 for (p = aquery->remaining_lookups; *p; p++) { 127 switch (*p) { 128 case 'b': 129 name = ares_dns_addr_to_ptr(&aquery->addr); 130 if (name == NULL) { 131 end_aquery(aquery, ARES_ENOMEM, 132 NULL); /* LCOV_EXCL_LINE: OutOfMemory */ 133 return; /* LCOV_EXCL_LINE: OutOfMemory */ 134 } 135 aquery->remaining_lookups = p + 1; 136 ares_query_nolock(aquery->channel, name, ARES_CLASS_IN, 137 ARES_REC_TYPE_PTR, addr_callback, aquery, NULL); 138 ares_free(name); 139 return; 140 case 'f': 141 status = file_lookup(aquery->channel, &aquery->addr, &host); 142 143 /* this status check below previously checked for !ARES_ENOTFOUND, 144 but we should not assume that this single error code is the one 145 that can occur, as that is in fact no longer the case */ 146 if (status == ARES_SUCCESS) { 147 end_aquery(aquery, status, host); 148 return; 149 } 150 break; 151 default: 152 break; 153 } 154 } 155 end_aquery(aquery, ARES_ENOTFOUND, NULL); 156 } 157 158 static void addr_callback(void *arg, ares_status_t status, size_t timeouts, 159 const ares_dns_record_t *dnsrec) 160 { 161 struct addr_query *aquery = (struct addr_query *)arg; 162 struct hostent *host; 163 size_t addrlen; 164 165 aquery->timeouts += timeouts; 166 if (status == ARES_SUCCESS) { 167 if (aquery->addr.family == AF_INET) { 168 addrlen = sizeof(aquery->addr.addr.addr4); 169 status = ares_parse_ptr_reply_dnsrec(dnsrec, &aquery->addr.addr.addr4, 170 (int)addrlen, AF_INET, &host); 171 } else { 172 addrlen = sizeof(aquery->addr.addr.addr6); 173 status = ares_parse_ptr_reply_dnsrec(dnsrec, &aquery->addr.addr.addr6, 174 (int)addrlen, AF_INET6, &host); 175 } 176 end_aquery(aquery, status, host); 177 } else if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) { 178 end_aquery(aquery, status, NULL); 179 } else { 180 next_lookup(aquery); 181 } 182 } 183 184 static void end_aquery(struct addr_query *aquery, ares_status_t status, 185 struct hostent *host) 186 { 187 aquery->callback(aquery->arg, (int)status, (int)aquery->timeouts, host); 188 if (host) { 189 ares_free_hostent(host); 190 } 191 ares_free(aquery->lookups); 192 ares_free(aquery); 193 } 194 195 static ares_status_t file_lookup(ares_channel_t *channel, 196 const struct ares_addr *addr, 197 struct hostent **host) 198 { 199 char ipaddr[INET6_ADDRSTRLEN]; 200 const void *ptr = NULL; 201 const ares_hosts_entry_t *entry; 202 ares_status_t status; 203 204 if (addr->family == AF_INET) { 205 ptr = &addr->addr.addr4; 206 } else if (addr->family == AF_INET6) { 207 ptr = &addr->addr.addr6; 208 } 209 210 if (ptr == NULL) { 211 return ARES_ENOTFOUND; 212 } 213 214 if (!ares_inet_ntop(addr->family, ptr, ipaddr, sizeof(ipaddr))) { 215 return ARES_ENOTFOUND; 216 } 217 218 status = ares_hosts_search_ipaddr(channel, ARES_FALSE, ipaddr, &entry); 219 if (status != ARES_SUCCESS) { 220 return status; 221 } 222 223 status = ares_hosts_entry_to_hostent(entry, addr->family, host); 224 if (status != ARES_SUCCESS) { 225 return status; /* LCOV_EXCL_LINE: OutOfMemory */ 226 } 227 228 return ARES_SUCCESS; 229 }