[BZ #2766]
[glibc.git] / nis / nis_call.c
blobf8f00d8c82bf95a3329b197f864f691a078f92f3
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
42 inetstr2int (const char *str)
44 size_t j = 0;
45 for (size_t i = 0; str[i] != '\0'; ++i)
46 if (str[i] == '.' && ++j == 4)
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 u_int j;
73 if (bind->clnt != NULL)
75 if (bind->use_auth)
76 auth_destroy (bind->clnt->cl_auth);
77 clnt_destroy (bind->clnt);
78 bind->clnt = NULL;
81 if (bind->trys >= bind->server_len)
82 return NIS_FAIL;
84 for (j = bind->current_ep + 1;
85 j < bind->server_val[bind->server_used].ep.ep_len; ++j)
86 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
87 "inet") == 0)
88 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
90 bind->current_ep = j;
91 return NIS_SUCCESS;
94 ++bind->trys;
95 ++bind->server_used;
96 if (bind->server_used >= bind->server_len)
97 bind->server_used = 0;
99 for (j = 0; j < bind->server_val[bind->server_used].ep.ep_len; ++j)
100 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
101 "inet") == 0)
102 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
104 bind->current_ep = j;
105 return NIS_SUCCESS;
108 return NIS_FAIL;
110 libnsl_hidden_def (__nisbind_next)
112 nis_error
113 __nisbind_connect (dir_binding *dbp)
115 nis_server *serv;
117 if (dbp == NULL)
118 return NIS_FAIL;
120 serv = &dbp->server_val[dbp->server_used];
122 memset (&dbp->addr, '\0', sizeof (dbp->addr));
123 dbp->addr.sin_family = AF_INET;
125 dbp->addr.sin_addr.s_addr =
126 inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
128 if (dbp->addr.sin_addr.s_addr == 0)
129 return NIS_FAIL;
131 /* Check, if the host is online and rpc.nisd is running. Much faster
132 then the clnt*_create functions: */
133 if (__pmap_getnisport (&dbp->addr, NIS_PROG, NIS_VERSION, IPPROTO_UDP) == 0)
134 return NIS_RPCERROR;
136 dbp->socket = RPC_ANYSOCK;
137 if (dbp->use_udp)
138 dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
139 UDPTIMEOUT, &dbp->socket);
140 else
141 dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
142 &dbp->socket, 0, 0);
144 if (dbp->clnt == NULL)
145 return NIS_RPCERROR;
147 clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t) &RPCTIMEOUT);
148 /* If the program exists, close the socket */
149 if (fcntl (dbp->socket, F_SETFD, 1) == -1)
150 perror ("fcntl: F_SETFD");
152 if (dbp->use_auth)
154 if (serv->key_type == NIS_PK_DH)
156 char netname[MAXNETNAMELEN + 1];
157 char *p;
159 p = stpcpy (netname, "unix.");
160 strncpy (p, serv->name, MAXNETNAMELEN - 5);
161 netname[MAXNETNAMELEN] = '\0';
162 // XXX What is this supposed to do? If we really want to replace
163 // XXX the first dot, then we might as well use unix@ as the
164 // XXX prefix string. --drepper
165 p = strchr (netname, '.');
166 *p = '@';
167 dbp->clnt->cl_auth =
168 authdes_pk_create (netname, &serv->pkey, 300, NULL, NULL);
169 if (!dbp->clnt->cl_auth)
170 dbp->clnt->cl_auth = authunix_create_default ();
172 else
173 dbp->clnt->cl_auth = authunix_create_default ();
176 return NIS_SUCCESS;
178 libnsl_hidden_def (__nisbind_connect)
180 nis_error
181 __nisbind_create (dir_binding *dbp, const nis_server *serv_val,
182 unsigned int serv_len, unsigned int flags)
184 dbp->clnt = NULL;
186 dbp->server_len = serv_len;
187 dbp->server_val = (nis_server *)serv_val;
189 if (flags & USE_DGRAM)
190 dbp->use_udp = TRUE;
191 else
192 dbp->use_udp = FALSE;
194 if (flags & NO_AUTHINFO)
195 dbp->use_auth = FALSE;
196 else
197 dbp->use_auth = TRUE;
199 if (flags & MASTER_ONLY)
200 dbp->master_only = TRUE;
201 else
202 dbp->master_only = FALSE;
204 /* We try the first server */
205 dbp->trys = 1;
207 dbp->class = -1;
208 if (__nis_findfastest (dbp) < 1)
209 return NIS_NAMEUNREACHABLE;
211 return NIS_SUCCESS;
213 libnsl_hidden_def (__nisbind_create)
215 /* __nisbind_connect (dbp) must be run before calling this function !
216 So we could use the same binding twice */
217 nis_error
218 __do_niscall3 (dir_binding *dbp, u_long prog, xdrproc_t xargs, caddr_t req,
219 xdrproc_t xres, caddr_t resp, unsigned int flags, nis_cb *cb)
221 enum clnt_stat result;
222 nis_error retcode;
224 if (dbp == NULL)
225 return NIS_NAMEUNREACHABLE;
229 again:
230 result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
232 if (result != RPC_SUCCESS)
233 retcode = NIS_RPCERROR;
234 else
236 switch (prog)
238 case NIS_IBLIST:
239 if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
240 (cb != NULL))
242 __nis_do_callback (dbp, &((nis_result *) resp)->cookie, cb);
243 break;
245 /* Yes, the missing break is correct. If we doesn't have to
246 start a callback, look if we have to search another server */
247 case NIS_LOOKUP:
248 case NIS_ADD:
249 case NIS_MODIFY:
250 case NIS_REMOVE:
251 case NIS_IBADD:
252 case NIS_IBMODIFY:
253 case NIS_IBREMOVE:
254 case NIS_IBFIRST:
255 case NIS_IBNEXT:
256 if (((nis_result *)resp)->status == NIS_SYSTEMERROR
257 || ((nis_result *)resp)->status == NIS_NOSUCHNAME
258 || ((nis_result *)resp)->status == NIS_NOT_ME)
260 next_server:
261 if (__nisbind_next (dbp) == NIS_SUCCESS)
263 while (__nisbind_connect (dbp) != NIS_SUCCESS)
265 if (__nisbind_next (dbp) != NIS_SUCCESS)
266 return NIS_SUCCESS;
269 else
270 break; /* No more servers to search in */
271 goto again;
273 break;
274 case NIS_FINDDIRECTORY:
275 if (((fd_result *)resp)->status == NIS_SYSTEMERROR
276 || ((fd_result *)resp)->status == NIS_NOSUCHNAME
277 || ((fd_result *)resp)->status == NIS_NOT_ME)
278 goto next_server;
279 break;
280 case NIS_DUMPLOG: /* log_result */
281 case NIS_DUMP:
282 if (((log_result *)resp)->lr_status == NIS_SYSTEMERROR
283 || ((log_result *)resp)->lr_status == NIS_NOSUCHNAME
284 || ((log_result *)resp)->lr_status == NIS_NOT_ME)
285 goto next_server;
286 break;
287 default:
288 break;
290 retcode = NIS_SUCCESS;
293 while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
295 return retcode;
297 libnsl_hidden_def (__do_niscall3)
300 nis_error
301 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
302 xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
303 unsigned int flags, nis_cb *cb)
305 dir_binding dbp;
306 nis_error status;
308 if (flags & MASTER_ONLY)
309 server_len = 1;
311 status = __nisbind_create (&dbp, server, server_len, flags);
312 if (status != NIS_SUCCESS)
313 return status;
315 while (__nisbind_connect (&dbp) != NIS_SUCCESS)
316 if (__nisbind_next (&dbp) != NIS_SUCCESS)
317 return NIS_NAMEUNREACHABLE;
319 status = __do_niscall3 (&dbp, prog, xargs, req, xres, resp, flags, cb);
321 __nisbind_destroy (&dbp);
323 return status;
327 static directory_obj *
328 rec_dirsearch (const_nis_name name, directory_obj *dir, nis_error *status)
330 fd_result *fd_res;
331 XDR xdrs;
333 switch (nis_dir_cmp (name, dir->do_name))
335 case SAME_NAME:
336 *status = NIS_SUCCESS;
337 return dir;
338 case NOT_SEQUENTIAL:
339 /* NOT_SEQUENTIAL means, go one up and try it there ! */
340 case HIGHER_NAME:
341 { /* We need data from a parent domain */
342 directory_obj *obj;
343 char ndomain [strlen (name) + 3];
345 nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
347 /* The root server of our domain is a replica of the parent
348 domain ! (Now I understand why a root server must be a
349 replica of the parent domain) */
350 fd_res = __nis_finddirectory (dir, ndomain);
351 if (fd_res == NULL)
353 nis_free_directory (dir);
354 *status = NIS_NOMEMORY;
355 return NULL;
357 *status = fd_res->status;
358 if (fd_res->status != NIS_SUCCESS)
360 /* Try the current directory obj, maybe it works */
361 __free_fdresult (fd_res);
362 return dir;
364 nis_free_directory (dir);
365 obj = calloc (1, sizeof (directory_obj));
366 if (obj == NULL)
368 __free_fdresult (fd_res);
369 *status = NIS_NOMEMORY;
370 return NULL;
372 xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
373 fd_res->dir_data.dir_data_len, XDR_DECODE);
374 _xdr_directory_obj (&xdrs, obj);
375 xdr_destroy (&xdrs);
376 __free_fdresult (fd_res);
378 /* We have found a NIS+ server serving ndomain, now
379 let us search for "name" */
380 return rec_dirsearch (name, obj, status);
382 break;
383 case LOWER_NAME:
385 directory_obj *obj;
386 size_t namelen = strlen (name);
387 char leaf[namelen + 3];
388 char domain[namelen + 3];
389 char ndomain[namelen + 3];
390 char *cp;
391 u_int run = 0;
393 strcpy (domain, name);
397 if (domain[0] == '\0')
399 nis_free_directory (dir);
400 return NULL;
402 nis_leaf_of_r (domain, leaf, sizeof (leaf));
403 nis_domain_of_r (domain, ndomain, sizeof (ndomain));
404 strcpy (domain, ndomain);
405 ++run;
407 while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
409 if (run == 1)
411 /* We have found the directory above. Use it. */
412 return dir;
415 cp = rawmemchr (leaf, '\0');
416 *cp++ = '.';
417 strcpy (cp, domain);
419 fd_res = __nis_finddirectory (dir, leaf);
420 if (fd_res == NULL)
422 nis_free_directory (dir);
423 *status = NIS_NOMEMORY;
424 return NULL;
426 *status = fd_res->status;
427 if (fd_res->status != NIS_SUCCESS)
429 /* Try the current directory object, maybe it works */
430 __free_fdresult (fd_res);
431 return dir;
433 nis_free_directory (dir);
434 obj = calloc (1, sizeof(directory_obj));
435 if (obj == NULL)
437 __free_fdresult (fd_res);
438 *status = NIS_NOMEMORY;
439 return NULL;
441 xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
442 fd_res->dir_data.dir_data_len, XDR_DECODE);
443 _xdr_directory_obj (&xdrs, obj);
444 xdr_destroy (&xdrs);
445 __free_fdresult (fd_res);
446 /* We have found a NIS+ server serving ndomain, now
447 let us search for "name" */
448 return rec_dirsearch (name, obj, status);
450 break;
451 case BAD_NAME:
452 nis_free_directory (dir);
453 *status = NIS_BADNAME;
454 return NULL;
456 nis_free_directory (dir);
457 *status = NIS_FAIL;
458 return NULL;
461 /* We try to query the current server for the searched object,
462 maybe he know about it ? */
463 static directory_obj *
464 first_shoot (const_nis_name name, directory_obj *dir)
466 directory_obj *obj = NULL;
467 fd_result *fd_res;
468 XDR xdrs;
469 char domain[strlen (name) + 3];
471 if (nis_dir_cmp (name, dir->do_name) == SAME_NAME)
472 return dir;
474 nis_domain_of_r (name, domain, sizeof (domain));
476 if (nis_dir_cmp (domain, dir->do_name) == SAME_NAME)
477 return dir;
479 fd_res = __nis_finddirectory (dir, domain);
480 if (fd_res == NULL)
481 return NULL;
482 if (fd_res->status == NIS_SUCCESS
483 && (obj = calloc (1, sizeof (directory_obj))) != NULL)
485 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
486 fd_res->dir_data.dir_data_len, XDR_DECODE);
487 _xdr_directory_obj (&xdrs, obj);
488 xdr_destroy (&xdrs);
491 __free_fdresult (fd_res);
493 if (obj != NULL)
494 nis_free_directory (dir);
496 return obj;
499 nis_error
500 __nisfind_server (const_nis_name name, directory_obj **dir)
502 if (name == NULL)
503 return NIS_BADNAME;
505 #if 0
506 /* Search in local cache. In the moment, we ignore the fastest server */
507 if (!(flags & NO_CACHE))
508 dir = __nis_cache_search (name, flags, &cinfo);
509 #endif
511 nis_error result = NIS_SUCCESS;
512 if (*dir == NULL)
514 nis_error status;
515 directory_obj *obj;
517 *dir = readColdStartFile ();
518 if (*dir == NULL)
519 /* No /var/nis/NIS_COLD_START->no NIS+ installed. */
520 return NIS_UNAVAIL;
522 /* Try at first, if servers in "dir" know our object */
523 obj = first_shoot (name, *dir);
524 if (obj == NULL)
526 obj = rec_dirsearch (name, *dir, &status);
527 if (obj == NULL)
528 result = status;
531 *dir = obj;
534 return result;
538 nis_error
539 __prepare_niscall (const_nis_name name, directory_obj **dirp,
540 dir_binding *bptrp, unsigned int flags)
542 nis_error retcode = __nisfind_server (name, dirp);
543 if (__builtin_expect (retcode != NIS_SUCCESS, 0))
544 return retcode;
546 nis_server *server;
547 u_int server_len;
549 if (flags & MASTER_ONLY)
551 server = (*dirp)->do_servers.do_servers_val;
552 server_len = 1;
554 else
556 server = (*dirp)->do_servers.do_servers_val;
557 server_len = (*dirp)->do_servers.do_servers_len;
560 retcode = __nisbind_create (bptrp, server, server_len, flags);
561 if (retcode == NIS_SUCCESS)
564 if (__nisbind_connect (bptrp) == NIS_SUCCESS)
565 return NIS_SUCCESS;
566 while (__nisbind_next (bptrp) == NIS_SUCCESS);
568 __nisbind_destroy (bptrp);
569 memset (bptrp, '\0', sizeof (*bptrp));
571 retcode = NIS_NAMEUNREACHABLE;
574 nis_free_directory (*dirp);
575 *dirp = NULL;
577 return retcode;
579 libnsl_hidden_def (__prepare_niscall)
582 nis_error
583 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
584 caddr_t req, xdrproc_t xres, caddr_t resp, unsigned int flags,
585 nis_cb *cb)
587 dir_binding bptr;
588 directory_obj *dir = NULL;
589 int saved_errno = errno;
591 nis_error retcode = __prepare_niscall (name, &dir, &bptr, flags);
592 if (retcode == NIS_SUCCESS)
594 retcode = __do_niscall3 (&bptr, prog, xargs, req, xres, resp, flags, cb);
596 __nisbind_destroy (&bptr);
598 nis_free_directory (dir);
601 __set_errno (saved_errno);
603 return retcode;