1 | /* Copyright (C) 2004-2021 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | Contributed by Ulrich Drepper <drepper@redhat.com>, 2004. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <assert.h> |
20 | #include <errno.h> |
21 | #include <netdb.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <unistd.h> |
25 | #include <not-cancel.h> |
26 | |
27 | #include "nscd-client.h" |
28 | #include "nscd_proto.h" |
29 | |
30 | |
31 | /* Define in nscd_gethst_r.c. */ |
32 | extern int __nss_not_use_nscd_hosts; |
33 | |
34 | |
35 | /* We use the mapping from nscd_gethst. */ |
36 | libc_locked_map_ptr (extern, __hst_map_handle) attribute_hidden; |
37 | |
38 | /* Defined in nscd_gethst_r.c. */ |
39 | extern int __nss_have_localdomain attribute_hidden; |
40 | |
41 | |
42 | int |
43 | __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop) |
44 | { |
45 | if (__glibc_unlikely (__nss_have_localdomain >= 0)) |
46 | { |
47 | if (__nss_have_localdomain == 0) |
48 | __nss_have_localdomain = getenv ("LOCALDOMAIN" ) != NULL ? 1 : -1; |
49 | if (__nss_have_localdomain > 0) |
50 | { |
51 | __nss_not_use_nscd_hosts = 1; |
52 | return -1; |
53 | } |
54 | } |
55 | |
56 | size_t keylen = strlen (key) + 1; |
57 | int gc_cycle; |
58 | int nretries = 0; |
59 | |
60 | /* If the mapping is available, try to search there instead of |
61 | communicating with the nscd. */ |
62 | struct mapped_database *mapped; |
63 | mapped = __nscd_get_map_ref (GETFDHST, "hosts" , &__hst_map_handle, |
64 | &gc_cycle); |
65 | |
66 | retry:; |
67 | struct nscd_ai_result *resultbuf = NULL; |
68 | const char *recend = (const char *) ~UINTMAX_C (0); |
69 | char *respdata = NULL; |
70 | int retval = -1; |
71 | int sock = -1; |
72 | ai_response_header ai_resp; |
73 | |
74 | if (mapped != NO_MAPPING) |
75 | { |
76 | struct datahead *found = __nscd_cache_search (GETAI, key, keylen, |
77 | mapped, sizeof ai_resp); |
78 | if (found != NULL) |
79 | { |
80 | respdata = (char *) (&found->data[0].aidata + 1); |
81 | ai_resp = found->data[0].aidata; |
82 | recend = (const char *) found->data + found->recsize; |
83 | /* Now check if we can trust ai_resp fields. If GC is |
84 | in progress, it can contain anything. */ |
85 | if (mapped->head->gc_cycle != gc_cycle) |
86 | { |
87 | retval = -2; |
88 | goto out; |
89 | } |
90 | } |
91 | } |
92 | |
93 | /* If we do not have the cache mapped, try to get the data over the |
94 | socket. */ |
95 | if (respdata == NULL) |
96 | { |
97 | sock = __nscd_open_socket (key, keylen, GETAI, &ai_resp, |
98 | sizeof (ai_resp)); |
99 | if (sock == -1) |
100 | { |
101 | /* nscd not running or wrong version. */ |
102 | __nss_not_use_nscd_hosts = 1; |
103 | goto out; |
104 | } |
105 | } |
106 | |
107 | if (ai_resp.found == 1) |
108 | { |
109 | size_t datalen = ai_resp.naddrs + ai_resp.addrslen + ai_resp.canonlen; |
110 | |
111 | /* This check really only affects the case where the data |
112 | comes from the mapped cache. */ |
113 | if (respdata + datalen > recend) |
114 | { |
115 | assert (sock == -1); |
116 | goto out; |
117 | } |
118 | |
119 | /* Create result. */ |
120 | resultbuf = (struct nscd_ai_result *) malloc (sizeof (*resultbuf) |
121 | + datalen); |
122 | if (resultbuf == NULL) |
123 | { |
124 | *h_errnop = NETDB_INTERNAL; |
125 | goto out_close; |
126 | } |
127 | |
128 | /* Set up the data structure, including pointers. */ |
129 | resultbuf->naddrs = ai_resp.naddrs; |
130 | resultbuf->addrs = (char *) (resultbuf + 1); |
131 | resultbuf->family = (uint8_t *) (resultbuf->addrs + ai_resp.addrslen); |
132 | if (ai_resp.canonlen != 0) |
133 | resultbuf->canon = (char *) (resultbuf->family + resultbuf->naddrs); |
134 | else |
135 | resultbuf->canon = NULL; |
136 | |
137 | if (respdata == NULL) |
138 | { |
139 | /* Read the data from the socket. */ |
140 | if ((size_t) __readall (sock, resultbuf + 1, datalen) == datalen) |
141 | { |
142 | retval = 0; |
143 | *result = resultbuf; |
144 | } |
145 | else |
146 | { |
147 | free (resultbuf); |
148 | *h_errnop = NETDB_INTERNAL; |
149 | } |
150 | } |
151 | else |
152 | { |
153 | /* Copy the data in the block. */ |
154 | memcpy (resultbuf + 1, respdata, datalen); |
155 | |
156 | /* Try to detect corrupt databases. */ |
157 | if (resultbuf->canon != NULL |
158 | && resultbuf->canon[ai_resp.canonlen - 1] != '\0') |
159 | /* We cannot use the database. */ |
160 | { |
161 | if (mapped->head->gc_cycle != gc_cycle) |
162 | retval = -2; |
163 | else |
164 | free (resultbuf); |
165 | goto out_close; |
166 | } |
167 | |
168 | retval = 0; |
169 | *result = resultbuf; |
170 | } |
171 | } |
172 | else |
173 | { |
174 | if (__glibc_unlikely (ai_resp.found == -1)) |
175 | { |
176 | /* The daemon does not cache this database. */ |
177 | __nss_not_use_nscd_hosts = 1; |
178 | goto out_close; |
179 | } |
180 | |
181 | /* Store the error number. */ |
182 | *h_errnop = ai_resp.error; |
183 | |
184 | /* Set errno to 0 to indicate no error, just no found record. */ |
185 | __set_errno (0); |
186 | /* Even though we have not found anything, the result is zero. */ |
187 | retval = 0; |
188 | } |
189 | |
190 | out_close: |
191 | if (sock != -1) |
192 | __close_nocancel_nostatus (sock); |
193 | out: |
194 | if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0) |
195 | { |
196 | /* When we come here this means there has been a GC cycle while we |
197 | were looking for the data. This means the data might have been |
198 | inconsistent. Retry if possible. */ |
199 | if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1) |
200 | { |
201 | /* nscd is just running gc now. Disable using the mapping. */ |
202 | if (atomic_decrement_val (&mapped->counter) == 0) |
203 | __nscd_unmap (mapped); |
204 | mapped = NO_MAPPING; |
205 | } |
206 | |
207 | if (retval != -1) |
208 | { |
209 | *result = NULL; |
210 | free (resultbuf); |
211 | goto retry; |
212 | } |
213 | } |
214 | |
215 | return retval; |
216 | } |
217 | |