• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ares_gethostbyaddr_nolock(ares_channel_t * channel,const void * addr,int addrlen,int family,ares_host_callback callback,void * arg)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 
ares_gethostbyaddr(ares_channel_t * channel,const void * addr,int addrlen,int family,ares_host_callback callback,void * arg)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 
next_lookup(struct addr_query * aquery)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 
addr_callback(void * arg,ares_status_t status,size_t timeouts,const ares_dns_record_t * dnsrec)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 
end_aquery(struct addr_query * aquery,ares_status_t status,struct hostent * host)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 
file_lookup(ares_channel_t * channel,const struct ares_addr * addr,struct hostent ** host)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 }
230