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_setup.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
42#include "ares.h"
43#include "ares_inet_net_pton.h"
44#include "ares_platform.h"
45#include "ares_private.h"
46
47#ifdef WATT32
48#  undef WIN32
49#endif
50
51struct addr_query {
52  /* Arguments passed to ares_gethostbyaddr() */
53  ares_channel_t    *channel;
54  struct ares_addr   addr;
55  ares_host_callback callback;
56  void              *arg;
57  char       *lookups; /* duplicate memory from channel for ares_reinit() */
58  const char *remaining_lookups;
59  size_t      timeouts;
60};
61
62static void          next_lookup(struct addr_query *aquery);
63static void          addr_callback(void *arg, int status, int timeouts,
64                                   unsigned char *abuf, int alen);
65static void          end_aquery(struct addr_query *aquery, ares_status_t status,
66                                struct hostent *host);
67static ares_status_t file_lookup(ares_channel_t         *channel,
68                                 const struct ares_addr *addr,
69                                 struct hostent        **host);
70
71static void ares_gethostbyaddr_int(ares_channel_t *channel, const void *addr,
72                                   int addrlen, int family,
73                                   ares_host_callback callback, void *arg)
74{
75  struct addr_query *aquery;
76
77  if (family != AF_INET && family != AF_INET6) {
78    callback(arg, ARES_ENOTIMP, 0, NULL);
79    return;
80  }
81
82  if ((family == AF_INET && addrlen != sizeof(aquery->addr.addr.addr4)) ||
83      (family == AF_INET6 && addrlen != sizeof(aquery->addr.addr.addr6))) {
84    callback(arg, ARES_ENOTIMP, 0, NULL);
85    return;
86  }
87
88  aquery = ares_malloc(sizeof(struct addr_query));
89  if (!aquery) {
90    callback(arg, ARES_ENOMEM, 0, NULL);
91    return;
92  }
93  aquery->lookups = ares_strdup(channel->lookups);
94  if (aquery->lookups == NULL) {
95    ares_free(aquery);
96    callback(arg, ARES_ENOMEM, 0, NULL);
97    return;
98  }
99  aquery->channel = channel;
100  if (family == AF_INET) {
101    memcpy(&aquery->addr.addr.addr4, addr, sizeof(aquery->addr.addr.addr4));
102  } else {
103    memcpy(&aquery->addr.addr.addr6, addr, sizeof(aquery->addr.addr.addr6));
104  }
105  aquery->addr.family       = family;
106  aquery->callback          = callback;
107  aquery->arg               = arg;
108  aquery->remaining_lookups = aquery->lookups;
109  aquery->timeouts          = 0;
110
111  next_lookup(aquery);
112}
113
114void ares_gethostbyaddr(ares_channel_t *channel, const void *addr, int addrlen,
115                        int family, ares_host_callback callback, void *arg)
116{
117  if (channel == NULL) {
118    return;
119  }
120  ares__channel_lock(channel);
121  ares_gethostbyaddr_int(channel, addr, addrlen, family, callback, arg);
122  ares__channel_unlock(channel);
123}
124
125static void next_lookup(struct addr_query *aquery)
126{
127  const char     *p;
128  ares_status_t   status;
129  struct hostent *host;
130  char           *name;
131
132  for (p = aquery->remaining_lookups; *p; p++) {
133    switch (*p) {
134      case 'b':
135        name = ares_dns_addr_to_ptr(&aquery->addr);
136        if (name == NULL) {
137          end_aquery(aquery, ARES_ENOMEM, NULL);
138          return;
139        }
140        aquery->remaining_lookups = p + 1;
141        ares_query(aquery->channel, name, C_IN, T_PTR, addr_callback, aquery);
142        ares_free(name);
143        return;
144      case 'f':
145        status = file_lookup(aquery->channel, &aquery->addr, &host);
146
147        /* this status check below previously checked for !ARES_ENOTFOUND,
148           but we should not assume that this single error code is the one
149           that can occur, as that is in fact no longer the case */
150        if (status == ARES_SUCCESS) {
151          end_aquery(aquery, status, host);
152          return;
153        }
154        break;
155      default:
156        break;
157    }
158  }
159  end_aquery(aquery, ARES_ENOTFOUND, NULL);
160}
161
162static void addr_callback(void *arg, int status, int timeouts,
163                          unsigned char *abuf, int alen)
164{
165  struct addr_query *aquery = (struct addr_query *)arg;
166  struct hostent    *host;
167  size_t             addrlen;
168
169  aquery->timeouts += (size_t)timeouts;
170  if (status == ARES_SUCCESS) {
171    if (aquery->addr.family == AF_INET) {
172      addrlen = sizeof(aquery->addr.addr.addr4);
173      status  = ares_parse_ptr_reply(abuf, alen, &aquery->addr.addr.addr4,
174                                     (int)addrlen, AF_INET, &host);
175    } else {
176      addrlen = sizeof(aquery->addr.addr.addr6);
177      status  = ares_parse_ptr_reply(abuf, alen, &aquery->addr.addr.addr6,
178                                     (int)addrlen, AF_INET6, &host);
179    }
180    end_aquery(aquery, (ares_status_t)status, host);
181  } else if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) {
182    end_aquery(aquery, (ares_status_t)status, NULL);
183  } else {
184    next_lookup(aquery);
185  }
186}
187
188static void end_aquery(struct addr_query *aquery, ares_status_t status,
189                       struct hostent *host)
190{
191  aquery->callback(aquery->arg, (int)status, (int)aquery->timeouts, host);
192  if (host) {
193    ares_free_hostent(host);
194  }
195  ares_free(aquery->lookups);
196  ares_free(aquery);
197}
198
199static ares_status_t file_lookup(ares_channel_t         *channel,
200                                 const struct ares_addr *addr,
201                                 struct hostent        **host)
202{
203  char                      ipaddr[INET6_ADDRSTRLEN];
204  const void               *ptr = NULL;
205  const ares_hosts_entry_t *entry;
206  ares_status_t             status;
207
208  if (addr->family == AF_INET) {
209    ptr = &addr->addr.addr4;
210  } else if (addr->family == AF_INET6) {
211    ptr = &addr->addr.addr6;
212  }
213
214  if (ptr == NULL) {
215    return ARES_ENOTFOUND;
216  }
217
218  if (!ares_inet_ntop(addr->family, ptr, ipaddr, sizeof(ipaddr))) {
219    return ARES_ENOTFOUND;
220  }
221
222  status = ares__hosts_search_ipaddr(channel, ARES_FALSE, ipaddr, &entry);
223  if (status != ARES_SUCCESS) {
224    return status;
225  }
226
227  status = ares__hosts_entry_to_hostent(entry, addr->family, host);
228  if (status != ARES_SUCCESS) {
229    return status;
230  }
231
232  return ARES_SUCCESS;
233}
234