7449aade8069b24a8b88d66ba474b6bebce1ff7f
[kopensolaris-gnu/glibc.git] / nscd / nscd_gethst_r.c
1 /* Copyright (C) 1998-2002, 2003, 2004 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
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, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <errno.h>
21 #include <netdb.h>
22 #include <resolv.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <arpa/nameser.h>
29 #include <sys/socket.h>
30 #include <sys/uio.h>
31 #include <sys/un.h>
32 #include <not-cancel.h>
33
34 #include "nscd-client.h"
35 #include "nscd_proto.h"
36
37 int __nss_not_use_nscd_hosts;
38
39 static int nscd_gethst_r (const char *key, size_t keylen, request_type type,
40                           struct hostent *resultbuf, char *buffer,
41                           size_t buflen, struct hostent **result,
42                           int *h_errnop) internal_function;
43
44
45 int
46 __nscd_gethostbyname_r (const char *name, struct hostent *resultbuf,
47                         char *buffer, size_t buflen, struct hostent **result,
48                         int *h_errnop)
49 {
50   request_type reqtype;
51
52   reqtype = (_res.options & RES_USE_INET6) ? GETHOSTBYNAMEv6 : GETHOSTBYNAME;
53
54   return nscd_gethst_r (name, strlen (name) + 1, reqtype, resultbuf,
55                         buffer, buflen, result, h_errnop);
56 }
57
58
59 int
60 __nscd_gethostbyname2_r (const char *name, int af, struct hostent *resultbuf,
61                          char *buffer, size_t buflen, struct hostent **result,
62                          int *h_errnop)
63 {
64   request_type reqtype;
65
66   reqtype = af == AF_INET6 ? GETHOSTBYNAMEv6 : GETHOSTBYNAME;
67
68   return nscd_gethst_r (name, strlen (name) + 1, reqtype, resultbuf,
69                         buffer, buflen, result, h_errnop);
70 }
71
72
73 int
74 __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
75                         struct hostent *resultbuf, char *buffer, size_t buflen,
76                         struct hostent **result, int *h_errnop)
77 {
78   request_type reqtype;
79
80   if (!((len == INADDRSZ && type == AF_INET)
81         || (len == IN6ADDRSZ && type == AF_INET6)))
82     /* LEN and TYPE do not match.  */
83     return -1;
84
85   reqtype = type == AF_INET6 ? GETHOSTBYADDRv6 : GETHOSTBYADDR;
86
87   return nscd_gethst_r (addr, len, reqtype, resultbuf, buffer, buflen, result,
88                         h_errnop);
89 }
90
91
92 /* Create a socket connected to a name. */
93 int
94 __nscd_open_socket (const char *key, size_t keylen, request_type type,
95                     void *response, size_t responselen)
96 {
97   struct sockaddr_un addr;
98   int sock;
99   int saved_errno = errno;
100
101   sock = __socket (PF_UNIX, SOCK_STREAM, 0);
102   if (sock < 0)
103     {
104       __set_errno (saved_errno);
105       return -1;
106     }
107
108   addr.sun_family = AF_UNIX;
109   strcpy (addr.sun_path, _PATH_NSCDSOCKET);
110   if (__connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
111     {
112       __set_errno (saved_errno);
113       goto out;
114     }
115
116   request_header req;
117   req.version = NSCD_VERSION;
118   req.type = type;
119   req.key_len = keylen;
120
121   struct iovec vec[2];
122   vec[0].iov_base = &req;
123   vec[0].iov_len = sizeof (request_header);
124   vec[1].iov_base = (void *) key;
125   vec[1].iov_len = keylen;
126
127   ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
128   if (nbytes != (ssize_t) (sizeof (request_header) + keylen))
129     {
130     out:
131       close_not_cancel_no_status (sock);
132       sock = -1;
133     }
134   else
135     {
136       nbytes = TEMP_FAILURE_RETRY (__read (sock, response, responselen));
137       if (nbytes != (ssize_t) responselen)
138         goto out;
139     }
140
141   return sock;
142 }
143
144
145 static int
146 internal_function
147 nscd_gethst_r (const char *key, size_t keylen, request_type type,
148                struct hostent *resultbuf, char *buffer, size_t buflen,
149                struct hostent **result, int *h_errnop)
150 {
151   hst_response_header hst_resp;
152   int sock = __nscd_open_socket (key, keylen, type, &hst_resp,
153                                  sizeof (hst_resp));
154   if (sock == -1)
155     {
156       __nss_not_use_nscd_hosts = 1;
157       return -1;
158     }
159
160   /* No value found so far.  */
161   int retval = -1;
162   *result = NULL;
163
164   if (hst_resp.found == -1)
165     {
166       /* The daemon does not cache this database.  */
167       __nss_not_use_nscd_hosts = 1;
168       goto out;
169     }
170
171   if (hst_resp.found == 1)
172     {
173       struct iovec vec[4];
174       uint32_t *aliases_len;
175       char *cp = buffer;
176       uintptr_t align1;
177       uintptr_t align2;
178       size_t total_len;
179       ssize_t cnt;
180       char *ignore;
181       int n;
182
183       /* A first check whether the buffer is sufficiently large is possible.  */
184       /* Now allocate the buffer the array for the group members.  We must
185          align the pointer and the base of the h_addr_list pointers.  */
186       align1 = ((__alignof__ (char *) - (cp - ((char *) 0)))
187                 & (__alignof__ (char *) - 1));
188       align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp.h_name_len)
189                                          - ((char *) 0)))
190                 & (__alignof__ (char *) - 1));
191       if (buflen < (align1 + hst_resp.h_name_len + align2
192                     + ((hst_resp.h_aliases_cnt + hst_resp.h_addr_list_cnt + 2)
193                        * sizeof (char *))
194                     + hst_resp.h_addr_list_cnt * (type == AF_INET
195                                                   ? INADDRSZ : IN6ADDRSZ)))
196         {
197         no_room:
198           __set_errno (ERANGE);
199           retval = ERANGE;
200           goto out;
201         }
202       cp += align1;
203
204       /* Prepare the result as far as we can.  */
205       resultbuf->h_aliases = (char **) cp;
206       cp += (hst_resp.h_aliases_cnt + 1) * sizeof (char *);
207       resultbuf->h_addr_list = (char **) cp;
208       cp += (hst_resp.h_addr_list_cnt + 1) * sizeof (char *);
209
210       resultbuf->h_name = cp;
211       cp += hst_resp.h_name_len + align2;
212       vec[0].iov_base = resultbuf->h_name;
213       vec[0].iov_len = hst_resp.h_name_len;
214
215       aliases_len = alloca (hst_resp.h_aliases_cnt * sizeof (uint32_t));
216       vec[1].iov_base = aliases_len;
217       vec[1].iov_len = hst_resp.h_aliases_cnt * sizeof (uint32_t);
218
219       total_len = (hst_resp.h_name_len
220                    + hst_resp.h_aliases_cnt * sizeof (uint32_t));
221
222       n = 2;
223       if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
224         {
225           vec[2].iov_base = cp;
226           vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
227
228           for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
229             {
230               resultbuf->h_addr_list[cnt] = cp;
231               cp += INADDRSZ;
232             }
233
234           resultbuf->h_addrtype = AF_INET;
235           resultbuf->h_length = INADDRSZ;
236
237           total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
238
239           n = 3;
240         }
241       else
242         {
243           if (hst_resp.h_length == INADDRSZ)
244             {
245               ignore = alloca (hst_resp.h_addr_list_cnt * INADDRSZ);
246               vec[2].iov_base = ignore;
247               vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
248
249               total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
250
251               n = 3;
252             }
253
254           vec[n].iov_base = cp;
255           vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
256
257           for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
258             {
259               resultbuf->h_addr_list[cnt] = cp;
260               cp += IN6ADDRSZ;
261             }
262
263           resultbuf->h_addrtype = AF_INET6;
264           resultbuf->h_length = IN6ADDRSZ;
265
266           total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ;
267
268           ++n;
269         }
270       resultbuf->h_addr_list[cnt] = NULL;
271
272       if ((size_t) TEMP_FAILURE_RETRY (__readv (sock, vec, n)) != total_len)
273         goto out;
274
275       /*  Now we also can read the aliases.  */
276       total_len = 0;
277       for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
278         {
279           resultbuf->h_aliases[cnt] = cp;
280           cp += aliases_len[cnt];
281           total_len += aliases_len[cnt];
282         }
283       resultbuf->h_aliases[cnt] = NULL;
284
285       /* See whether this would exceed the buffer capacity.  */
286       if (cp > buffer + buflen)
287         goto no_room;
288
289       /* And finally read the aliases.  */
290       if ((size_t) TEMP_FAILURE_RETRY (__read (sock, resultbuf->h_aliases[0],
291                                                total_len)) == total_len)
292         {
293           retval = 0;
294           *result = resultbuf;
295         }
296     }
297   else
298     {
299       /* Store the error number.  */
300       *h_errnop = hst_resp.error;
301
302       /* The `errno' to some value != ERANGE.  */
303       __set_errno (ENOENT);
304       /* Even though we have not found anything, the result is zero.  */
305       retval = 0;
306     }
307
308  out:
309   __close (sock);
310
311   return retval;
312 }