Update.
[glibc.git] / nis / nis_call.c
blob73bd2d7de2b455009d9ac853d58583caf72695dc
1 /* Copyright (C) 1997, 1998 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 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.
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.
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. */
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <rpc/rpc.h>
24 #include <rpc/auth.h>
25 #include <rpcsvc/nis.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
30 #include "nis_xdr.h"
31 #include "nis_intern.h"
33 static struct timeval RPCTIMEOUT = {10, 0};
34 static struct timeval UDPTIMEOUT = {5, 0};
36 extern u_short __pmap_getnisport (struct sockaddr_in *address, u_long program,
37 u_long version, u_int protocol);
39 unsigned long
40 inetstr2int (const char *str)
42 char buffer[strlen (str) + 3];
43 size_t buflen;
44 size_t i, j;
46 buflen = stpcpy (buffer, str) - buffer;
48 j = 0;
49 for (i = 0; i < buflen; ++i)
50 if (buffer[i] == '.')
52 ++j;
53 if (j == 4)
55 buffer[i] = '\0';
56 break;
60 return inet_addr (buffer);
63 void
64 __nisbind_destroy (dir_binding *bind)
66 if (bind->clnt != NULL)
68 if (bind->use_auth)
69 auth_destroy (bind->clnt->cl_auth);
70 clnt_destroy (bind->clnt);
74 nis_error
75 __nisbind_next (dir_binding *bind)
77 u_int j;
79 if (bind->clnt != NULL)
81 if (bind->use_auth)
82 auth_destroy (bind->clnt->cl_auth);
83 clnt_destroy (bind->clnt);
84 bind->clnt = NULL;
87 if (bind->trys >= bind->server_len)
88 return NIS_FAIL;
90 for (j = bind->current_ep + 1;
91 j < bind->server_val[bind->server_used].ep.ep_len; ++j)
92 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
93 "inet") == 0)
94 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
96 bind->current_ep = j;
97 return NIS_SUCCESS;
100 ++bind->trys;
101 ++bind->server_used;
102 if (bind->server_used >= bind->server_len)
103 bind->server_used = 0;
105 for (j = 0; j < bind->server_val[bind->server_used].ep.ep_len; ++j)
106 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
107 "inet") == 0)
108 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
110 bind->current_ep = j;
111 return NIS_SUCCESS;
114 return NIS_FAIL;
117 nis_error
118 __nisbind_connect (dir_binding *dbp)
120 nis_server *serv;
122 if (dbp == NULL)
123 return NIS_FAIL;
125 serv = &dbp->server_val[dbp->server_used];
127 memset (&dbp->addr, '\0', sizeof (dbp->addr));
128 dbp->addr.sin_family = AF_INET;
130 dbp->addr.sin_addr.s_addr =
131 inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
133 if (dbp->addr.sin_addr.s_addr == 0)
134 return NIS_FAIL;
136 /* Check, if the host is online and rpc.nisd is running. Much faster
137 then the clnt*_create functions: */
138 if (__pmap_getnisport (&dbp->addr, NIS_PROG, NIS_VERSION, IPPROTO_UDP) == 0)
139 return NIS_RPCERROR;
141 dbp->socket = RPC_ANYSOCK;
142 if (dbp->use_udp)
143 dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
144 UDPTIMEOUT, &dbp->socket);
145 else
146 dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
147 &dbp->socket, 0, 0);
149 if (dbp->clnt == NULL)
150 return NIS_RPCERROR;
152 clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t)&RPCTIMEOUT);
153 /* If the program exists, close the socket */
154 if (fcntl (dbp->socket, F_SETFD, 1) == -1)
155 perror (_("fcntl: F_SETFD"));
157 if (dbp->use_auth)
159 if (serv->key_type == NIS_PK_DH)
161 char netname[MAXNETNAMELEN+1];
162 char *p;
164 p = stpcpy (netname, "unix.");
165 strncpy (p, serv->name,MAXNETNAMELEN-5);
166 netname[MAXNETNAMELEN] = '\0';
167 p = strchr (netname, '.');
168 *p = '@';
169 dbp->clnt->cl_auth =
170 authdes_pk_create (netname, &serv->pkey, 300, NULL, NULL);
171 if (!dbp->clnt->cl_auth)
172 dbp->clnt->cl_auth = authunix_create_default ();
174 else
175 dbp->clnt->cl_auth = authunix_create_default ();
176 dbp->use_auth = TRUE;
179 return NIS_SUCCESS;
182 nis_error
183 __nisbind_create (dir_binding *dbp, const nis_server *serv_val,
184 unsigned int serv_len, unsigned int flags)
186 dbp->clnt = NULL;
188 dbp->server_len = serv_len;
189 dbp->server_val = (nis_server *)serv_val;
191 if (flags & USE_DGRAM)
192 dbp->use_udp = TRUE;
193 else
194 dbp->use_udp = FALSE;
196 if (flags & NO_AUTHINFO)
197 dbp->use_auth = FALSE;
198 else
199 dbp->use_auth = TRUE;
201 if (flags & MASTER_ONLY)
202 dbp->master_only = TRUE;
203 else
204 dbp->master_only = FALSE;
206 /* We try the first server */
207 dbp->trys = 1;
209 dbp->class = -1;
210 if (__nis_findfastest (dbp) < 1)
212 __nisbind_destroy (dbp);
213 return NIS_NAMEUNREACHABLE;
216 return NIS_SUCCESS;
219 /* __nisbind_connect (dbp) must be run before calling this function !
220 So we could use the same binding twice */
221 nis_error
222 __do_niscall3 (dir_binding *dbp, u_long prog, xdrproc_t xargs, caddr_t req,
223 xdrproc_t xres, caddr_t resp, unsigned int flags, nis_cb *cb)
225 enum clnt_stat result;
226 nis_error retcode;
228 if (dbp == NULL)
229 return NIS_NAMEUNREACHABLE;
233 again:
234 result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
236 if (result != RPC_SUCCESS)
237 retcode = NIS_RPCERROR;
238 else
240 switch (prog)
242 case NIS_IBLIST:
243 if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
244 (cb != NULL))
246 __nis_do_callback(dbp, &((nis_result *)resp)->cookie, cb);
247 break;
249 /* Yes, the missing break is correct. If we doesn't have to
250 start a callback, look if we have to search another server */
251 case NIS_LOOKUP:
252 case NIS_ADD:
253 case NIS_MODIFY:
254 case NIS_REMOVE:
255 case NIS_IBADD:
256 case NIS_IBMODIFY:
257 case NIS_IBREMOVE:
258 case NIS_IBFIRST:
259 case NIS_IBNEXT:
260 if ((((nis_result *)resp)->status == NIS_SYSTEMERROR) ||
261 (((nis_result *)resp)->status == NIS_NOSUCHNAME) ||
262 (((nis_result *)resp)->status == NIS_NOT_ME))
264 if (__nisbind_next (dbp) == NIS_SUCCESS)
266 while (__nisbind_connect (dbp) != NIS_SUCCESS)
268 if (__nisbind_next (dbp) != NIS_SUCCESS)
269 return NIS_SUCCESS;
272 else
273 break; /* No more servers to search in */
274 goto again;
276 break;
277 case NIS_FINDDIRECTORY:
278 if ((((fd_result *)resp)->status == NIS_SYSTEMERROR) ||
279 (((fd_result *)resp)->status == NIS_NOSUCHNAME) ||
280 (((fd_result *)resp)->status == NIS_NOT_ME))
282 if (__nisbind_next (dbp) == NIS_SUCCESS)
284 while (__nisbind_connect (dbp) != NIS_SUCCESS)
286 if (__nisbind_next (dbp) != NIS_SUCCESS)
287 return NIS_SUCCESS;
290 else
291 break; /* No more servers to search in */
292 goto again;
294 break;
295 case NIS_DUMPLOG: /* log_result */
296 case NIS_DUMP:
297 if ((((log_result *)resp)->lr_status == NIS_SYSTEMERROR) ||
298 (((log_result *)resp)->lr_status == NIS_NOSUCHNAME) ||
299 (((log_result *)resp)->lr_status == NIS_NOT_ME))
301 if (__nisbind_next (dbp) == NIS_SUCCESS)
303 while (__nisbind_connect (dbp) != NIS_SUCCESS)
305 if (__nisbind_next (dbp) != NIS_SUCCESS)
306 return NIS_SUCCESS;
309 else
310 break; /* No more servers to search in */
311 goto again;
313 break;
314 default:
315 break;
317 retcode = NIS_SUCCESS;
320 while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
322 return retcode;
325 nis_error
326 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
327 xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
328 unsigned int flags, nis_cb *cb)
330 dir_binding dbp;
331 nis_error status;
333 if (flags & MASTER_ONLY)
334 server_len = 1;
336 status = __nisbind_create (&dbp, server, server_len, flags);
337 if (status != NIS_SUCCESS)
338 return status;
340 while (__nisbind_connect (&dbp) != NIS_SUCCESS)
342 if (__nisbind_next (&dbp) != NIS_SUCCESS)
343 return NIS_NAMEUNREACHABLE;
346 status = __do_niscall3 (&dbp, prog, xargs, req, xres, resp, flags, cb);
348 __nisbind_destroy (&dbp);
350 return status;
354 static directory_obj *
355 rec_dirsearch (const_nis_name name, directory_obj *dir, nis_error *status)
357 fd_result *fd_res;
358 XDR xdrs;
360 switch (nis_dir_cmp (name, dir->do_name))
362 case SAME_NAME:
363 *status = NIS_SUCCESS;
364 return dir;
365 case NOT_SEQUENTIAL:
366 /* NOT_SEQUENTIAL means, go one up and try it there ! */
367 case HIGHER_NAME:
368 { /* We need data from a parent domain */
369 directory_obj *obj;
370 char ndomain [strlen (name) + 3];
372 nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
374 /* The root server of our domain is a replica of the parent
375 domain ! (Now I understand why a root server must be a
376 replica of the parent domain) */
377 fd_res = __nis_finddirectory (dir, ndomain);
378 *status = fd_res->status;
379 if (fd_res->status != NIS_SUCCESS)
381 /* Try the current directory obj, maybe it works */
382 __free_fdresult (fd_res);
383 return dir;
385 obj = calloc(1, sizeof(directory_obj));
386 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
387 fd_res->dir_data.dir_data_len, XDR_DECODE);
388 _xdr_directory_obj(&xdrs, obj);
389 xdr_destroy(&xdrs);
390 __free_fdresult (fd_res);
391 if (obj != NULL)
393 /* We have found a NIS+ server serving ndomain, now
394 let us search for "name" */
395 nis_free_directory (dir);
396 return rec_dirsearch (name, obj, status);
398 else
400 /* Ups, very bad. Are we already the root server ? */
401 nis_free_directory (dir);
402 return NULL;
405 break;
406 case LOWER_NAME:
408 directory_obj *obj;
409 size_t namelen = strlen (name);
410 char leaf [namelen + 3];
411 char domain [namelen + 3];
412 char ndomain [namelen + 3];
413 char *cp;
414 u_int run = 0;
416 strcpy (domain, name);
420 if (domain[0] == '\0')
422 nis_free_directory (dir);
423 return NULL;
425 nis_leaf_of_r (domain, leaf, sizeof (leaf));
426 nis_domain_of_r (domain, ndomain, sizeof (ndomain));
427 strcpy (domain, ndomain);
428 ++run;
430 while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
432 if (run == 1)
434 /* We have found the directory above. Use it. */
435 return dir;
438 cp = strchr (leaf, '\0');
439 *cp++ = '.';
440 strcpy (cp, domain);
442 fd_res = __nis_finddirectory (dir, leaf);
443 *status = fd_res->status;
444 if (fd_res->status != NIS_SUCCESS)
446 /* Try the current directory object, maybe it works */
447 __free_fdresult (fd_res);
448 return dir;
450 obj = calloc(1, sizeof(directory_obj));
451 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
452 fd_res->dir_data.dir_data_len, XDR_DECODE);
453 _xdr_directory_obj(&xdrs, obj);
454 xdr_destroy(&xdrs);
455 __free_fdresult (fd_res);
456 if (obj != NULL)
458 /* We have found a NIS+ server serving ndomain, now
459 let us search for "name" */
460 nis_free_directory (dir);
461 return rec_dirsearch (name, obj, status);
464 break;
465 case BAD_NAME:
466 nis_free_directory (dir);
467 *status = NIS_BADNAME;
468 return NULL;
470 nis_free_directory (dir);
471 *status = NIS_FAIL;
472 return NULL;
475 /* We try to query the current server for the searched object,
476 maybe he know about it ? */
477 static directory_obj *
478 first_shoot (const_nis_name name, directory_obj *dir)
480 directory_obj *obj;
481 fd_result *fd_res;
482 XDR xdrs;
483 char domain [strlen (name) + 3];
485 if (nis_dir_cmp (name, dir->do_name) == SAME_NAME)
486 return dir;
488 nis_domain_of_r (name, domain, sizeof (domain));
490 if (nis_dir_cmp (domain, dir->do_name) == SAME_NAME)
491 return dir;
493 fd_res = __nis_finddirectory (dir, domain);
494 if (fd_res->status != NIS_SUCCESS)
496 __free_fdresult (fd_res);
497 return NULL;
499 obj = calloc(1, sizeof(directory_obj));
500 if (obj == NULL)
501 return NULL;
502 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
503 fd_res->dir_data.dir_data_len, XDR_DECODE);
504 _xdr_directory_obj (&xdrs, obj);
505 xdr_destroy (&xdrs);
506 __free_fdresult (fd_res);
507 if (obj != NULL)
509 nis_free_directory (dir);
510 return obj;
512 return NULL;
515 nis_error
516 __nisfind_server (const_nis_name name, directory_obj **dir)
518 if (name == NULL)
519 return NIS_BADNAME;
521 #if 0
522 /* Search in local cache. In the moment, we ignore the fastest server */
523 if (!(flags & NO_CACHE))
524 dir = __nis_cache_search (name, flags, &cinfo);
525 #endif
527 if (*dir == NULL)
529 nis_error status;
530 directory_obj *obj;
532 *dir = readColdStartFile ();
533 if (*dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
534 return NIS_UNAVAIL;
536 /* Try at first, if servers in "dir" know our object */
537 obj = first_shoot (name, *dir);
538 if (obj == NULL)
540 *dir = rec_dirsearch (name, *dir, &status);
541 if (*dir == NULL)
542 return status;
544 else
545 *dir = obj;
548 return NIS_SUCCESS;
551 nis_error
552 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
553 caddr_t req, xdrproc_t xres, caddr_t resp, unsigned int flags,
554 nis_cb *cb)
556 nis_error retcode;
557 dir_binding bptr;
558 directory_obj *dir = NULL;
559 nis_server *server;
560 u_int server_len;
561 int saved_errno = errno;
563 retcode = __nisfind_server (name, &dir);
564 if (retcode != NIS_SUCCESS)
565 return retcode;
567 if (flags & MASTER_ONLY)
569 server = dir->do_servers.do_servers_val;
570 server_len = 1;
572 else
574 server = dir->do_servers.do_servers_val;
575 server_len = dir->do_servers.do_servers_len;
578 retcode = __nisbind_create (&bptr, server, server_len, flags);
579 if (retcode == NIS_SUCCESS)
581 while (__nisbind_connect (&bptr) != NIS_SUCCESS)
583 if (__nisbind_next (&bptr) != NIS_SUCCESS)
585 nis_free_directory (dir);
586 __nisbind_destroy (&bptr);
587 return NIS_NAMEUNREACHABLE;
590 retcode = __do_niscall3 (&bptr, prog, xargs, req, xres, resp, flags, cb);
592 __nisbind_destroy (&bptr);
595 nis_free_directory (dir);
597 __set_errno (saved_errno);
599 return retcode;