[BZ #2749]
[glibc.git] / nis / nis_call.c
blob928053daf5f87a0c85433bd025322ac085221a7d
1 /* Copyright (C) 1997,1998,2001,2004,2005,2006 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.
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.
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.
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. */
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <libintl.h>
24 #include <rpc/rpc.h>
25 #include <rpc/auth.h>
26 #include <rpcsvc/nis.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
31 #include "nis_xdr.h"
32 #include "nis_intern.h"
33 #include <libnsl.h>
35 static const struct timeval RPCTIMEOUT = {10, 0};
36 static const struct timeval UDPTIMEOUT = {5, 0};
38 extern u_short __pmap_getnisport (struct sockaddr_in *address, u_long program,
39 u_long version, u_int protocol);
41 unsigned long int
42 inetstr2int (const char *str)
44 size_t j = 0;
45 for (size_t i = 0; str[i] != '\0'; ++i)
46 if (str[i] == '.' && __builtin_expect (++j == 4, 0))
48 char buffer[i + 1];
49 buffer[i] = '\0';
50 return inet_addr (memcpy (buffer, str, i));
53 return inet_addr (str);
56 void
57 __nisbind_destroy (dir_binding *bind)
59 if (bind->clnt != NULL)
61 if (bind->use_auth)
62 auth_destroy (bind->clnt->cl_auth);
63 clnt_destroy (bind->clnt);
66 libnsl_hidden_def (__nisbind_destroy)
68 nis_error
69 __nisbind_next (dir_binding *bind)
71 if (bind->clnt != NULL)
73 if (bind->use_auth)
74 auth_destroy (bind->clnt->cl_auth);
75 clnt_destroy (bind->clnt);
76 bind->clnt = NULL;
79 if (bind->trys >= bind->server_len)
80 return NIS_FAIL;
82 for (u_int j = bind->current_ep + 1;
83 j < bind->server_val[bind->server_used].ep.ep_len; ++j)
84 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
85 "inet") == 0)
86 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
88 bind->current_ep = j;
89 return NIS_SUCCESS;
92 ++bind->trys;
93 ++bind->server_used;
94 if (bind->server_used >= bind->server_len)
95 bind->server_used = 0;
97 for (u_int j = 0; j < bind->server_val[bind->server_used].ep.ep_len; ++j)
98 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
99 "inet") == 0)
100 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
102 bind->current_ep = j;
103 return NIS_SUCCESS;
106 return NIS_FAIL;
108 libnsl_hidden_def (__nisbind_next)
110 nis_error
111 __nisbind_connect (dir_binding *dbp)
113 nis_server *serv;
115 if (dbp == NULL)
116 return NIS_FAIL;
118 serv = &dbp->server_val[dbp->server_used];
120 memset (&dbp->addr, '\0', sizeof (dbp->addr));
121 dbp->addr.sin_family = AF_INET;
123 dbp->addr.sin_addr.s_addr =
124 inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
126 if (dbp->addr.sin_addr.s_addr == INADDR_NONE)
127 return NIS_FAIL;
129 /* Check, if the host is online and rpc.nisd is running. Much faster
130 then the clnt*_create functions: */
131 if (__pmap_getnisport (&dbp->addr, NIS_PROG, NIS_VERSION, IPPROTO_UDP) == 0)
132 return NIS_RPCERROR;
134 dbp->socket = RPC_ANYSOCK;
135 if (dbp->use_udp)
136 dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
137 UDPTIMEOUT, &dbp->socket);
138 else
139 dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
140 &dbp->socket, 0, 0);
142 if (dbp->clnt == NULL)
143 return NIS_RPCERROR;
145 clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t) &RPCTIMEOUT);
146 /* If the program exists, close the socket */
147 if (fcntl (dbp->socket, F_SETFD, 1) == -1)
148 perror ("fcntl: F_SETFD");
150 if (dbp->use_auth)
152 if (serv->key_type == NIS_PK_DH)
154 char netname[MAXNETNAMELEN + 1];
155 char *p;
157 p = stpcpy (netname, "unix.");
158 strncpy (p, serv->name, MAXNETNAMELEN - 5);
159 netname[MAXNETNAMELEN] = '\0';
160 // XXX What is this supposed to do? If we really want to replace
161 // XXX the first dot, then we might as well use unix@ as the
162 // XXX prefix string. --drepper
163 p = strchr (netname, '.');
164 *p = '@';
165 dbp->clnt->cl_auth =
166 authdes_pk_create (netname, &serv->pkey, 300, NULL, NULL);
167 if (!dbp->clnt->cl_auth)
168 dbp->clnt->cl_auth = authunix_create_default ();
170 else
171 dbp->clnt->cl_auth = authunix_create_default ();
174 return NIS_SUCCESS;
176 libnsl_hidden_def (__nisbind_connect)
178 nis_error
179 __nisbind_create (dir_binding *dbp, const nis_server *serv_val,
180 unsigned int serv_len, unsigned int flags)
182 dbp->clnt = NULL;
184 dbp->server_len = serv_len;
185 dbp->server_val = (nis_server *)serv_val;
187 if (flags & USE_DGRAM)
188 dbp->use_udp = TRUE;
189 else
190 dbp->use_udp = FALSE;
192 if (flags & NO_AUTHINFO)
193 dbp->use_auth = FALSE;
194 else
195 dbp->use_auth = TRUE;
197 if (flags & MASTER_ONLY)
198 dbp->master_only = TRUE;
199 else
200 dbp->master_only = FALSE;
202 /* We try the first server */
203 dbp->trys = 1;
205 dbp->class = -1;
206 if (__nis_findfastest (dbp) < 1)
207 return NIS_NAMEUNREACHABLE;
209 return NIS_SUCCESS;
211 libnsl_hidden_def (__nisbind_create)
213 /* __nisbind_connect (dbp) must be run before calling this function !
214 So we could use the same binding twice */
215 nis_error
216 __do_niscall3 (dir_binding *dbp, u_long prog, xdrproc_t xargs, caddr_t req,
217 xdrproc_t xres, caddr_t resp, unsigned int flags, nis_cb *cb)
219 enum clnt_stat result;
220 nis_error retcode;
222 if (dbp == NULL)
223 return NIS_NAMEUNREACHABLE;
227 again:
228 result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
230 if (result != RPC_SUCCESS)
231 retcode = NIS_RPCERROR;
232 else
234 switch (prog)
236 case NIS_IBLIST:
237 if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
238 (cb != NULL))
240 __nis_do_callback (dbp, &((nis_result *) resp)->cookie, cb);
241 break;
243 /* Yes, the missing break is correct. If we doesn't have to
244 start a callback, look if we have to search another server */
245 case NIS_LOOKUP:
246 case NIS_ADD:
247 case NIS_MODIFY:
248 case NIS_REMOVE:
249 case NIS_IBADD:
250 case NIS_IBMODIFY:
251 case NIS_IBREMOVE:
252 case NIS_IBFIRST:
253 case NIS_IBNEXT:
254 if (((nis_result *)resp)->status == NIS_SYSTEMERROR
255 || ((nis_result *)resp)->status == NIS_NOSUCHNAME
256 || ((nis_result *)resp)->status == NIS_NOT_ME)
258 next_server:
259 if (__nisbind_next (dbp) == NIS_SUCCESS)
261 while (__nisbind_connect (dbp) != NIS_SUCCESS)
263 if (__nisbind_next (dbp) != NIS_SUCCESS)
264 return NIS_SUCCESS;
267 else
268 break; /* No more servers to search in */
269 goto again;
271 break;
272 case NIS_FINDDIRECTORY:
273 if (((fd_result *)resp)->status == NIS_SYSTEMERROR
274 || ((fd_result *)resp)->status == NIS_NOSUCHNAME
275 || ((fd_result *)resp)->status == NIS_NOT_ME)
276 goto next_server;
277 break;
278 case NIS_DUMPLOG: /* log_result */
279 case NIS_DUMP:
280 if (((log_result *)resp)->lr_status == NIS_SYSTEMERROR
281 || ((log_result *)resp)->lr_status == NIS_NOSUCHNAME
282 || ((log_result *)resp)->lr_status == NIS_NOT_ME)
283 goto next_server;
284 break;
285 default:
286 break;
288 retcode = NIS_SUCCESS;
291 while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
293 return retcode;
295 libnsl_hidden_def (__do_niscall3)
298 nis_error
299 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
300 xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
301 unsigned int flags, nis_cb *cb)
303 dir_binding dbp;
304 nis_error status;
306 if (flags & MASTER_ONLY)
307 server_len = 1;
309 status = __nisbind_create (&dbp, server, server_len, flags);
310 if (status != NIS_SUCCESS)
311 return status;
313 while (__nisbind_connect (&dbp) != NIS_SUCCESS)
314 if (__nisbind_next (&dbp) != NIS_SUCCESS)
315 return NIS_NAMEUNREACHABLE;
317 status = __do_niscall3 (&dbp, prog, xargs, req, xres, resp, flags, cb);
319 __nisbind_destroy (&dbp);
321 return status;
325 static directory_obj *
326 rec_dirsearch (const_nis_name name, directory_obj *dir, nis_error *status)
328 fd_result *fd_res;
329 XDR xdrs;
331 switch (nis_dir_cmp (name, dir->do_name))
333 case SAME_NAME:
334 *status = NIS_SUCCESS;
335 return dir;
336 case NOT_SEQUENTIAL:
337 /* NOT_SEQUENTIAL means, go one up and try it there ! */
338 case HIGHER_NAME:
339 { /* We need data from a parent domain */
340 directory_obj *obj;
341 char ndomain[strlen (dir->do_name) + 3];
343 nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
345 /* The root server of our domain is a replica of the parent
346 domain ! (Now I understand why a root server must be a
347 replica of the parent domain) */
348 fd_res = __nis_finddirectory (dir, ndomain);
349 if (fd_res == NULL)
351 nis_free_directory (dir);
352 *status = NIS_NOMEMORY;
353 return NULL;
355 *status = fd_res->status;
356 if (fd_res->status != NIS_SUCCESS)
358 /* Try the current directory obj, maybe it works */
359 __free_fdresult (fd_res);
360 return dir;
362 nis_free_directory (dir);
363 obj = calloc (1, sizeof (directory_obj));
364 if (obj == NULL)
366 __free_fdresult (fd_res);
367 *status = NIS_NOMEMORY;
368 return NULL;
370 xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
371 fd_res->dir_data.dir_data_len, XDR_DECODE);
372 _xdr_directory_obj (&xdrs, obj);
373 xdr_destroy (&xdrs);
374 __free_fdresult (fd_res);
376 /* We have found a NIS+ server serving ndomain, now
377 let us search for "name" */
378 return rec_dirsearch (name, obj, status);
380 break;
381 case LOWER_NAME:
383 directory_obj *obj;
384 size_t namelen = strlen (name);
385 char leaf[namelen + 3];
386 char domain[namelen + 3];
387 char ndomain[namelen + 3];
388 char *cp;
390 strcpy (domain, name);
394 if (domain[0] == '\0')
396 nis_free_directory (dir);
397 return NULL;
399 nis_leaf_of_r (domain, leaf, sizeof (leaf));
400 nis_domain_of_r (domain, ndomain, sizeof (ndomain));
401 strcpy (domain, ndomain);
403 while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
405 cp = rawmemchr (leaf, '\0');
406 *cp++ = '.';
407 strcpy (cp, domain);
409 fd_res = __nis_finddirectory (dir, leaf);
410 if (fd_res == NULL)
412 nis_free_directory (dir);
413 *status = NIS_NOMEMORY;
414 return NULL;
416 *status = fd_res->status;
417 if (fd_res->status != NIS_SUCCESS)
419 /* Try the current directory object, maybe it works */
420 __free_fdresult (fd_res);
421 return dir;
423 nis_free_directory (dir);
424 obj = calloc (1, sizeof(directory_obj));
425 if (obj == NULL)
427 __free_fdresult (fd_res);
428 *status = NIS_NOMEMORY;
429 return NULL;
431 xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
432 fd_res->dir_data.dir_data_len, XDR_DECODE);
433 _xdr_directory_obj (&xdrs, obj);
434 xdr_destroy (&xdrs);
435 __free_fdresult (fd_res);
436 /* We have found a NIS+ server serving ndomain, now
437 let us search for "name" */
438 return rec_dirsearch (name, obj, status);
440 break;
441 case BAD_NAME:
442 nis_free_directory (dir);
443 *status = NIS_BADNAME;
444 return NULL;
446 nis_free_directory (dir);
447 *status = NIS_FAIL;
448 return NULL;
451 /* We try to query the current server for the searched object,
452 maybe he know about it ? */
453 static directory_obj *
454 first_shoot (const_nis_name name, int search_parent_first, directory_obj *dir)
456 directory_obj *obj = NULL;
457 fd_result *fd_res;
458 XDR xdrs;
459 char domain[strlen (name) + 3];
461 #if 0
462 if (nis_dir_cmp (name, dir->do_name) == SAME_NAME)
463 return dir;
464 #endif
466 const char *search_name = name;
467 if (search_parent_first)
469 nis_domain_of_r (name, domain, sizeof (domain));
470 search_name = domain;
473 if (nis_dir_cmp (search_name, dir->do_name) == SAME_NAME)
474 return dir;
476 fd_res = __nis_finddirectory (dir, search_name);
477 if (fd_res == NULL)
478 return NULL;
479 if (fd_res->status == NIS_SUCCESS
480 && (obj = calloc (1, sizeof (directory_obj))) != NULL)
482 xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
483 fd_res->dir_data.dir_data_len, XDR_DECODE);
484 _xdr_directory_obj (&xdrs, obj);
485 xdr_destroy (&xdrs);
487 if (strcmp (dir->do_name, obj->do_name) != 0)
489 nis_free_directory (obj);
490 obj = NULL;
494 __free_fdresult (fd_res);
496 if (obj != NULL)
497 nis_free_directory (dir);
499 return obj;
502 nis_error
503 __nisfind_server (const_nis_name name, int search_parent_first,
504 directory_obj **dir)
506 if (name == NULL)
507 return NIS_BADNAME;
509 #if 0
510 /* Search in local cache. In the moment, we ignore the fastest server */
511 if (!(flags & NO_CACHE))
512 dir = __nis_cache_search (name, flags, &cinfo);
513 #endif
515 nis_error result = NIS_SUCCESS;
516 if (*dir == NULL)
518 nis_error status;
519 directory_obj *obj;
521 *dir = readColdStartFile ();
522 if (*dir == NULL)
523 /* No /var/nis/NIS_COLD_START->no NIS+ installed. */
524 return NIS_UNAVAIL;
526 /* Try at first, if servers in "dir" know our object */
527 obj = first_shoot (name, search_parent_first, *dir);
528 if (obj == NULL)
530 obj = rec_dirsearch (name, *dir, &status);
531 if (obj == NULL)
532 result = status;
535 *dir = obj;
538 return result;
542 nis_error
543 __prepare_niscall (const_nis_name name, directory_obj **dirp,
544 dir_binding *bptrp, unsigned int flags)
546 nis_error retcode = __nisfind_server (name, 1, dirp);
547 if (__builtin_expect (retcode != NIS_SUCCESS, 0))
548 return retcode;
550 nis_server *server;
551 u_int server_len;
553 if (flags & MASTER_ONLY)
555 server = (*dirp)->do_servers.do_servers_val;
556 server_len = 1;
558 else
560 server = (*dirp)->do_servers.do_servers_val;
561 server_len = (*dirp)->do_servers.do_servers_len;
564 retcode = __nisbind_create (bptrp, server, server_len, flags);
565 if (retcode == NIS_SUCCESS)
568 if (__nisbind_connect (bptrp) == NIS_SUCCESS)
569 return NIS_SUCCESS;
570 while (__nisbind_next (bptrp) == NIS_SUCCESS);
572 __nisbind_destroy (bptrp);
573 memset (bptrp, '\0', sizeof (*bptrp));
575 retcode = NIS_NAMEUNREACHABLE;
578 nis_free_directory (*dirp);
579 *dirp = NULL;
581 return retcode;
583 libnsl_hidden_def (__prepare_niscall)
586 nis_error
587 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
588 caddr_t req, xdrproc_t xres, caddr_t resp, unsigned int flags,
589 nis_cb *cb)
591 dir_binding bptr;
592 directory_obj *dir = NULL;
593 int saved_errno = errno;
595 nis_error retcode = __prepare_niscall (name, &dir, &bptr, flags);
596 if (retcode == NIS_SUCCESS)
598 retcode = __do_niscall3 (&bptr, prog, xargs, req, xres, resp, flags, cb);
600 __nisbind_destroy (&bptr);
602 nis_free_directory (dir);
605 __set_errno (saved_errno);
607 return retcode;