/src/c-ares/src/lib/ares_gethostbyaddr.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
67 | 0 | struct addr_query *aquery; |
68 | |
|
69 | 0 | if (family != AF_INET && family != AF_INET6) { |
70 | 0 | callback(arg, ARES_ENOTIMP, 0, NULL); |
71 | 0 | return; |
72 | 0 | } |
73 | | |
74 | 0 | if ((family == AF_INET && addrlen != sizeof(aquery->addr.addr.addr4)) || |
75 | 0 | (family == AF_INET6 && addrlen != sizeof(aquery->addr.addr.addr6))) { |
76 | 0 | callback(arg, ARES_ENOTIMP, 0, NULL); |
77 | 0 | return; |
78 | 0 | } |
79 | | |
80 | 0 | aquery = ares_malloc(sizeof(struct addr_query)); |
81 | 0 | if (!aquery) { |
82 | 0 | callback(arg, ARES_ENOMEM, 0, NULL); |
83 | 0 | return; |
84 | 0 | } |
85 | 0 | aquery->lookups = ares_strdup(channel->lookups); |
86 | 0 | if (aquery->lookups == NULL) { |
87 | | /* LCOV_EXCL_START: OutOfMemory */ |
88 | 0 | ares_free(aquery); |
89 | 0 | callback(arg, ARES_ENOMEM, 0, NULL); |
90 | 0 | return; |
91 | | /* LCOV_EXCL_STOP */ |
92 | 0 | } |
93 | 0 | aquery->channel = channel; |
94 | 0 | if (family == AF_INET) { |
95 | 0 | memcpy(&aquery->addr.addr.addr4, addr, sizeof(aquery->addr.addr.addr4)); |
96 | 0 | } else { |
97 | 0 | memcpy(&aquery->addr.addr.addr6, addr, sizeof(aquery->addr.addr.addr6)); |
98 | 0 | } |
99 | 0 | aquery->addr.family = family; |
100 | 0 | aquery->callback = callback; |
101 | 0 | aquery->arg = arg; |
102 | 0 | aquery->remaining_lookups = aquery->lookups; |
103 | 0 | aquery->timeouts = 0; |
104 | |
|
105 | 0 | next_lookup(aquery); |
106 | 0 | } |
107 | | |
108 | | void ares_gethostbyaddr(ares_channel_t *channel, const void *addr, int addrlen, |
109 | | int family, ares_host_callback callback, void *arg) |
110 | 0 | { |
111 | 0 | if (channel == NULL) { |
112 | 0 | return; |
113 | 0 | } |
114 | 0 | ares_channel_lock(channel); |
115 | 0 | ares_gethostbyaddr_nolock(channel, addr, addrlen, family, callback, arg); |
116 | 0 | ares_channel_unlock(channel); |
117 | 0 | } |
118 | | |
119 | | static void next_lookup(struct addr_query *aquery) |
120 | 0 | { |
121 | 0 | const char *p; |
122 | 0 | ares_status_t status; |
123 | 0 | struct hostent *host; |
124 | 0 | char *name; |
125 | |
|
126 | 0 | for (p = aquery->remaining_lookups; *p; p++) { |
127 | 0 | switch (*p) { |
128 | 0 | case 'b': |
129 | 0 | name = ares_dns_addr_to_ptr(&aquery->addr); |
130 | 0 | if (name == NULL) { |
131 | 0 | end_aquery(aquery, ARES_ENOMEM, |
132 | 0 | NULL); /* LCOV_EXCL_LINE: OutOfMemory */ |
133 | 0 | return; /* LCOV_EXCL_LINE: OutOfMemory */ |
134 | 0 | } |
135 | 0 | aquery->remaining_lookups = p + 1; |
136 | 0 | ares_query_nolock(aquery->channel, name, ARES_CLASS_IN, |
137 | 0 | ARES_REC_TYPE_PTR, addr_callback, aquery, NULL); |
138 | 0 | ares_free(name); |
139 | 0 | return; |
140 | 0 | case 'f': |
141 | 0 | 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 | 0 | if (status == ARES_SUCCESS) { |
147 | 0 | end_aquery(aquery, status, host); |
148 | 0 | return; |
149 | 0 | } |
150 | 0 | break; |
151 | 0 | default: |
152 | 0 | break; |
153 | 0 | } |
154 | 0 | } |
155 | 0 | end_aquery(aquery, ARES_ENOTFOUND, NULL); |
156 | 0 | } |
157 | | |
158 | | static void addr_callback(void *arg, ares_status_t status, size_t timeouts, |
159 | | const ares_dns_record_t *dnsrec) |
160 | 0 | { |
161 | 0 | struct addr_query *aquery = (struct addr_query *)arg; |
162 | 0 | struct hostent *host; |
163 | 0 | size_t addrlen; |
164 | |
|
165 | 0 | aquery->timeouts += timeouts; |
166 | 0 | if (status == ARES_SUCCESS) { |
167 | 0 | if (aquery->addr.family == AF_INET) { |
168 | 0 | addrlen = sizeof(aquery->addr.addr.addr4); |
169 | 0 | status = ares_parse_ptr_reply_dnsrec(dnsrec, &aquery->addr.addr.addr4, |
170 | 0 | (int)addrlen, AF_INET, &host); |
171 | 0 | } else { |
172 | 0 | addrlen = sizeof(aquery->addr.addr.addr6); |
173 | 0 | status = ares_parse_ptr_reply_dnsrec(dnsrec, &aquery->addr.addr.addr6, |
174 | 0 | (int)addrlen, AF_INET6, &host); |
175 | 0 | } |
176 | 0 | end_aquery(aquery, status, host); |
177 | 0 | } else if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) { |
178 | 0 | end_aquery(aquery, status, NULL); |
179 | 0 | } else { |
180 | 0 | next_lookup(aquery); |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | | static void end_aquery(struct addr_query *aquery, ares_status_t status, |
185 | | struct hostent *host) |
186 | 0 | { |
187 | 0 | aquery->callback(aquery->arg, (int)status, (int)aquery->timeouts, host); |
188 | 0 | if (host) { |
189 | 0 | ares_free_hostent(host); |
190 | 0 | } |
191 | 0 | ares_free(aquery->lookups); |
192 | 0 | ares_free(aquery); |
193 | 0 | } |
194 | | |
195 | | static ares_status_t file_lookup(ares_channel_t *channel, |
196 | | const struct ares_addr *addr, |
197 | | struct hostent **host) |
198 | 0 | { |
199 | 0 | char ipaddr[INET6_ADDRSTRLEN]; |
200 | 0 | const void *ptr = NULL; |
201 | 0 | const ares_hosts_entry_t *entry; |
202 | 0 | ares_status_t status; |
203 | |
|
204 | 0 | if (addr->family == AF_INET) { |
205 | 0 | ptr = &addr->addr.addr4; |
206 | 0 | } else if (addr->family == AF_INET6) { |
207 | 0 | ptr = &addr->addr.addr6; |
208 | 0 | } |
209 | |
|
210 | 0 | if (ptr == NULL) { |
211 | 0 | return ARES_ENOTFOUND; |
212 | 0 | } |
213 | | |
214 | 0 | if (!ares_inet_ntop(addr->family, ptr, ipaddr, sizeof(ipaddr))) { |
215 | 0 | return ARES_ENOTFOUND; |
216 | 0 | } |
217 | | |
218 | 0 | status = ares_hosts_search_ipaddr(channel, ARES_FALSE, ipaddr, &entry); |
219 | 0 | if (status != ARES_SUCCESS) { |
220 | 0 | return status; |
221 | 0 | } |
222 | | |
223 | 0 | status = ares_hosts_entry_to_hostent(entry, addr->family, host); |
224 | 0 | if (status != ARES_SUCCESS) { |
225 | 0 | return status; /* LCOV_EXCL_LINE: OutOfMemory */ |
226 | 0 | } |
227 | | |
228 | 0 | return ARES_SUCCESS; |
229 | 0 | } |