Optimize some uses of stpcpy away.
[kopensolaris-gnu/glibc.git] / nis / nss_nisplus / nisplus-hosts.c
1 /* Copyright (C) 1997 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1997.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <nss.h>
21 #include <netdb.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include <string.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <bits/libc-lock.h>
28 #include <rpcsvc/nis.h>
29 #include <rpcsvc/nislib.h>
30
31 #include "nss-nisplus.h"
32
33 __libc_lock_define_initialized (static, lock)
34
35 static nis_result *result = NULL;
36 static nis_name tablename_val = NULL;
37 static u_long tablename_len = 0;
38
39 #define NISENTRYVAL(idx,col,res) \
40         ((res)->objects.objects_val[(idx)].EN_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val)
41
42 #define NISENTRYLEN(idx,col,res) \
43         ((res)->objects.objects_val[(idx)].EN_data.en_cols.en_cols_val[(col)].ec_value.ec_value_len)
44
45 /* Get implementation for some internal functions. */
46 #include "../../resolv/mapv4v6addr.h"
47
48 static int
49 _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
50                             char *buffer, size_t buflen)
51 {
52   unsigned int i;
53   char *first_unused = buffer;
54   size_t room_left = buflen;
55   char *data, *p, *line;
56
57   if (result == NULL)
58     return 0;
59
60   if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) ||
61       __type_of (result->objects.objects_val) != ENTRY_OBJ ||
62       strcmp(result->objects.objects_val[0].EN_data.en_type,
63              "hosts_tbl") != 0 ||
64       result->objects.objects_val[0].EN_data.en_cols.en_cols_len < 4)
65     return 0;
66
67   if (room_left < NISENTRYLEN (0, 2, result) + 1)
68     {
69       __set_errno (ERANGE);
70       return 0;
71     }
72
73   data = first_unused;
74   if (inet_pton (af, NISENTRYVAL (0, 2, result), data) < 1)
75     /* Illegal address: ignore line.  */
76     return 0;
77
78   host->h_addrtype = af;
79   if (af == AF_INET6)
80     host->h_length = IN6ADDRSZ;
81   else
82     {
83       if (_res.options & RES_USE_INET6)
84         {
85           map_v4v6_address (data, data);
86           host->h_addrtype = AF_INET6;
87           host->h_length = IN6ADDRSZ;
88         }
89       else
90         {
91           host->h_addrtype = AF_INET;
92           host->h_length = INADDRSZ;
93         }
94     }
95   first_unused+=host->h_length;
96   room_left-=host->h_length;
97
98   if (NISENTRYLEN (0, 0, result) + 1 > room_left)
99     {
100       __set_errno (ERANGE);
101       return 0;
102     }
103   p = stpncpy (first_unused, NISENTRYVAL (0, 0, result),
104                NISENTRYLEN (0, 0, result));
105   *p = '\0';
106   room_left -= (NISENTRYLEN (0, 0, result) + 1);
107   host->h_name = first_unused;
108   first_unused += NISENTRYLEN (0, 0, result) +1;
109   p = first_unused;
110
111   line = p;
112   for (i = 0; i < result->objects.objects_len; i++)
113     {
114       if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
115         {
116           if (NISENTRYLEN (i, 1, result) + 2 > room_left)
117             {
118               __set_errno (ERANGE);
119               return 0;
120             }
121           *p++ = ' ';
122           p = stpncpy (p, NISENTRYVAL (i, 1, result),
123                        NISENTRYLEN (i, 1, result));
124           *p = '\0';
125           room_left -= (NISENTRYLEN (i, 1, result) + 1);
126         }
127     }
128   ++p;
129   first_unused = p;
130   /* Adjust the pointer so it is aligned for
131      storing pointers.  */
132   first_unused += __alignof__ (char *) - 1;
133   first_unused -= ((first_unused - (char *) 0) % __alignof__ (char *));
134   host->h_addr_list = (char **) first_unused;
135   if (room_left < 2 * sizeof (char *))
136     {
137       __set_errno (ERANGE);
138       return 0;
139     }
140   room_left -= (2 * sizeof (char *));
141   host->h_addr_list[0] = data;
142   host->h_addr_list[1] = NULL;
143   host->h_aliases = &host->h_addr_list[2];
144   host->h_aliases[0] = NULL;
145
146   i = 0;
147   while (*line != '\0')
148     {
149       /* Skip leading blanks.  */
150       while (isspace (*line))
151         line++;
152
153       if (*line == '\0')
154         break;
155
156       if (room_left < sizeof (char *))
157         {
158           __set_errno (ERANGE);
159           return 0;
160         }
161
162       room_left -= sizeof (char *);
163       host->h_aliases[i] = line;
164
165       while (*line != '\0' && *line != ' ')
166         ++line;
167
168       if (*line == ' ')
169         {
170           *line = '\0';
171           ++line;
172           ++i;
173         }
174       else
175         host->h_aliases[i+1] = NULL;
176     }
177
178   return 1;
179
180 }
181
182 static enum nss_status
183 _nss_create_tablename (void)
184 {
185   if (tablename_val == NULL)
186     {
187       char buf [40 + strlen (nis_local_directory ())];
188       char *p;
189
190       p = stpcpy (buf, "hosts.org_dir.");
191       p = stpcpy (p, nis_local_directory ());
192       tablename_val = strdup (buf);
193       if (tablename_val == NULL)
194         return NSS_STATUS_TRYAGAIN;
195       tablename_len = strlen (tablename_val);
196     }
197   return NSS_STATUS_SUCCESS;
198 }
199
200 enum nss_status
201 _nss_nisplus_sethostent (void)
202 {
203   enum nss_status status = NSS_STATUS_SUCCESS;
204
205   __libc_lock_lock (lock);
206
207   if (result)
208     nis_freeresult (result);
209   result = NULL;
210
211   if (tablename_val == NULL)
212     if (_nss_create_tablename() != NSS_STATUS_SUCCESS)
213       status = NSS_STATUS_UNAVAIL;
214
215   __libc_lock_unlock (lock);
216
217   return status;
218 }
219
220 enum nss_status
221 _nss_nisplus_endhostent (void)
222 {
223   __libc_lock_lock (lock);
224
225   if (result)
226     nis_freeresult (result);
227   result = NULL;
228
229   __libc_lock_unlock (lock);
230
231   return NSS_STATUS_SUCCESS;
232 }
233
234 static enum nss_status
235 internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
236                                size_t buflen, int *herrnop)
237 {
238   int parse_res;
239
240   /* Get the next entry until we found a correct one. */
241   do
242     {
243       if (result == NULL)
244         {
245           if (tablename_val == NULL)
246             if (_nss_create_tablename() != NSS_STATUS_SUCCESS)
247               return NSS_STATUS_UNAVAIL;
248
249           result = nis_first_entry(tablename_val);
250           if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
251             {
252               int retval;
253
254               retval = niserr2nss (result->status);
255               if (retval == NSS_STATUS_TRYAGAIN)
256                 {
257                   *herrnop = NETDB_INTERNAL;
258                   __set_errno (EAGAIN);
259                 }
260               return retval;
261             }
262
263         }
264       else
265         {
266           nis_result *res2;
267
268           res2 = nis_next_entry(tablename_val, &result->cookie);
269           nis_freeresult (result);
270           result = res2;
271           if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
272             {
273               int retval;
274
275               retval = niserr2nss (result->status);
276               if (retval == NSS_STATUS_TRYAGAIN)
277                 {
278                   *herrnop = NETDB_INTERNAL;
279                   __set_errno (EAGAIN);
280                 }
281               return retval;
282             }
283         }
284
285       parse_res = _nss_nisplus_parse_hostent (result, AF_INET6,
286                                               host, buffer, buflen);
287       if (!parse_res && errno != ERANGE)
288         parse_res = _nss_nisplus_parse_hostent (result, AF_INET, host,
289                                                 buffer, buflen);
290       if (!parse_res && errno == ERANGE)
291         {
292           *herrnop = NETDB_INTERNAL;
293           return NSS_STATUS_TRYAGAIN;
294         }
295
296     } while (!parse_res);
297
298   return NSS_STATUS_SUCCESS;
299 }
300
301 enum nss_status
302 _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
303                            size_t buflen, int *herrnop)
304 {
305   int status;
306
307   __libc_lock_lock (lock);
308
309   status = internal_nisplus_gethostent_r (result, buffer, buflen, herrnop);
310
311   __libc_lock_unlock (lock);
312
313   return status;
314 }
315
316 enum nss_status
317 _nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host,
318                               char *buffer, size_t buflen, int *herrnop)
319 {
320   int parse_res, retval;
321
322   if (tablename_val == NULL)
323     if (_nss_create_tablename() != NSS_STATUS_SUCCESS)
324       {
325         *herrnop = NETDB_INTERNAL;
326         return NSS_STATUS_UNAVAIL;
327       }
328
329   if (name == NULL)
330     {
331       __set_errno (EINVAL);
332       *herrnop = NETDB_INTERNAL;
333       return NSS_STATUS_NOTFOUND;
334     }
335   else
336     {
337       nis_result *result;
338       char buf[strlen (name) + 255 + tablename_len];
339
340       /* Search at first in the alias list, and use the correct name
341          for the next search */
342       sprintf(buf, "[name=%s],%s", name, tablename_val);
343       result = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
344
345       /* If we do not find it, try it as original name. But if the
346          database is correct, we should find it in the first case, too */
347       if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) ||
348           __type_of (result->objects.objects_val) != ENTRY_OBJ ||
349           strcmp(result->objects.objects_val->EN_data.en_type,
350                  "hosts_tbl") != 0 ||
351           result->objects.objects_val->EN_data.en_cols.en_cols_len < 3)
352         sprintf(buf, "[cname=%s],%s", name, tablename_val);
353       else
354         sprintf(buf, "[cname=%s],%s", NISENTRYVAL(0, 0, result),
355                 tablename_val);
356
357       nis_freeresult (result);
358       result = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
359
360       retval = niserr2nss (result->status);
361       if (retval != NSS_STATUS_SUCCESS)
362         {
363           if (retval == NSS_STATUS_TRYAGAIN)
364             {
365               __set_errno (EAGAIN);
366               *herrnop = NETDB_INTERNAL;
367             }
368           nis_freeresult (result);
369           return retval;
370         }
371
372       parse_res =
373         _nss_nisplus_parse_hostent (result, af, host, buffer, buflen);
374
375       nis_freeresult (result);
376
377       if (parse_res)
378         return NSS_STATUS_SUCCESS;
379
380       *herrnop = NETDB_INTERNAL;
381       if (!parse_res && errno == ERANGE)
382         return NSS_STATUS_TRYAGAIN;
383       else
384         return NSS_STATUS_NOTFOUND;
385     }
386 }
387
388 enum nss_status
389 _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
390                               char *buffer, size_t buflen, int *h_errnop)
391 {
392   if (_res.options & RES_USE_INET6)
393     {
394       enum nss_status status;
395
396       status = _nss_nisplus_gethostbyname2_r (name, AF_INET6, host, buffer,
397                                               buflen, h_errnop);
398       if (status == NSS_STATUS_SUCCESS)
399         return status;
400     }
401
402   return _nss_nisplus_gethostbyname2_r (name, AF_INET, host, buffer,
403                                         buflen, h_errnop);
404 }
405
406 enum nss_status
407 _nss_nisplus_gethostbyaddr_r (const char *addr, int addrlen, int type,
408                               struct hostent *host, char *buffer,
409                               size_t buflen, int *herrnop)
410 {
411   if (tablename_val == NULL)
412     if (_nss_create_tablename() != NSS_STATUS_SUCCESS)
413       return NSS_STATUS_UNAVAIL;
414
415   if (addr == NULL)
416     return NSS_STATUS_NOTFOUND;
417   else
418     {
419       nis_result *result;
420       char buf[255 + tablename_len];
421       int retval, parse_res;
422
423       snprintf(buf, sizeof (buf) -1, "[addr=%s],%s",
424                inet_ntoa (*(struct in_addr *)addr), tablename_val);
425       result = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
426
427       retval = niserr2nss (result->status);
428       if (retval != NSS_STATUS_SUCCESS)
429         {
430           if (retval == NSS_STATUS_TRYAGAIN)
431             {
432               __set_errno (EAGAIN);
433               *herrnop = NETDB_INTERNAL;
434             }
435           nis_freeresult (result);
436           return retval;
437         }
438
439       parse_res = _nss_nisplus_parse_hostent (result, type, host,
440                                               buffer, buflen);
441
442       nis_freeresult (result);
443
444       if (parse_res)
445         return NSS_STATUS_SUCCESS;
446
447       *herrnop = NETDB_INTERNAL;
448       if (!parse_res && errno == ERANGE)
449         return NSS_STATUS_TRYAGAIN;
450       else
451         return NSS_STATUS_NOTFOUND;
452     }
453 }